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);
3586 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
3587 struct replPropertyMetaData1 *cur_m,
3588 struct replPropertyMetaData1 *new_m)
3593 * If the new replPropertyMetaData entry for this attribute is
3594 * not provided (this happens in the case where we look for
3595 * ATTID_name, but the name was not changed), then the local
3596 * state is clearly still current, as the remote
3597 * server didn't send it due to being older the high watermark
3600 if (new_m == NULL) {
3604 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
3606 * if we compare equal then do an
3607 * update. This is used when a client
3608 * asks for a FULL_SYNC, and can be
3609 * used to recover a corrupt
3612 * This call is a bit tricky, what we
3613 * are doing it turning the 'is_newer'
3614 * call into a 'not is older' by
3615 * swapping cur_m and new_m, and negating the
3618 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
3621 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
3631 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
3633 const struct ldb_val *rdn_val;
3634 const char *rdn_name;
3635 struct ldb_dn *new_dn;
3637 rdn_val = ldb_dn_get_rdn_val(dn);
3638 rdn_name = ldb_dn_get_rdn_name(dn);
3639 if (!rdn_val || !rdn_name) {
3643 new_dn = ldb_dn_copy(mem_ctx, dn);
3648 if (!ldb_dn_remove_child_components(new_dn, 1)) {
3652 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
3654 ldb_dn_escape_value(new_dn, *rdn_val),
3655 GUID_string(new_dn, guid))) {
3664 perform a modify operation which sets the rDN and name attributes to
3665 their current values. This has the effect of changing these
3666 attributes to have been last updated by the current DC. This is
3667 needed to ensure that renames performed as part of conflict
3668 resolution are propogated to other DCs
3670 static int replmd_name_modify(struct replmd_replicated_request *ar,
3671 struct ldb_request *req, struct ldb_dn *dn)
3673 struct ldb_message *msg;
3674 const char *rdn_name;
3675 const struct ldb_val *rdn_val;
3676 const struct dsdb_attribute *rdn_attr;
3679 msg = ldb_msg_new(req);
3685 rdn_name = ldb_dn_get_rdn_name(dn);
3686 if (rdn_name == NULL) {
3690 /* normalize the rdn attribute name */
3691 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3692 if (rdn_attr == NULL) {
3695 rdn_name = rdn_attr->lDAPDisplayName;
3697 rdn_val = ldb_dn_get_rdn_val(dn);
3698 if (rdn_val == NULL) {
3702 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3705 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3708 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3711 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3715 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3716 if (ret != LDB_SUCCESS) {
3717 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
3718 ldb_dn_get_linearized(dn),
3719 ldb_errstring(ldb_module_get_ctx(ar->module))));
3729 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
3730 ldb_dn_get_linearized(dn)));
3731 return LDB_ERR_OPERATIONS_ERROR;
3736 callback for conflict DN handling where we have renamed the incoming
3737 record. After renaming it, we need to ensure the change of name and
3738 rDN for the incoming record is seen as an originating update by this DC.
3740 This also handles updating lastKnownParent for entries sent to lostAndFound
3742 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3744 struct replmd_replicated_request *ar =
3745 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3746 struct ldb_dn *conflict_dn = NULL;
3749 if (ares->error != LDB_SUCCESS) {
3750 /* call the normal callback for everything except success */
3751 return replmd_op_callback(req, ares);
3754 switch (req->operation) {
3756 conflict_dn = req->op.add.message->dn;
3759 conflict_dn = req->op.mod.message->dn;
3762 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
3765 /* perform a modify of the rDN and name of the record */
3766 ret = replmd_name_modify(ar, req, conflict_dn);
3767 if (ret != LDB_SUCCESS) {
3769 return replmd_op_callback(req, ares);
3772 if (ar->objs->objects[ar->index_current].last_known_parent) {
3773 struct ldb_message *msg = ldb_msg_new(req);
3775 ldb_module_oom(ar->module);
3776 return LDB_ERR_OPERATIONS_ERROR;
3779 msg->dn = req->op.add.message->dn;
3781 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
3782 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
3783 if (ret != LDB_SUCCESS) {
3784 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
3785 ldb_module_oom(ar->module);
3788 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
3790 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3791 if (ret != LDB_SUCCESS) {
3792 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
3793 ldb_dn_get_linearized(msg->dn),
3794 ldb_errstring(ldb_module_get_ctx(ar->module))));
3800 return replmd_op_callback(req, ares);
3804 callback for replmd_replicated_apply_add()
3805 This copes with the creation of conflict records in the case where
3806 the DN exists, but with a different objectGUID
3808 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))
3810 struct ldb_dn *conflict_dn;
3811 struct replmd_replicated_request *ar =
3812 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3813 struct ldb_result *res;
3814 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
3816 const struct ldb_val *omd_value;
3817 struct replPropertyMetaDataBlob omd, *rmd;
3818 enum ndr_err_code ndr_err;
3819 bool rename_incoming_record, rodc;
3820 struct replPropertyMetaData1 *rmd_name, *omd_name;
3821 struct ldb_message *msg;
3822 struct ldb_request *down_req = NULL;
3824 /* call the normal callback for success */
3825 if (ares->error == LDB_SUCCESS) {
3826 return callback(req, ares);
3830 * we have a conflict, and need to decide if we will keep the
3831 * new record or the old record
3834 msg = ar->objs->objects[ar->index_current].msg;
3835 conflict_dn = msg->dn;
3837 /* For failures other than conflicts, fail the whole operation here */
3838 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
3839 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
3840 ldb_dn_get_linearized(conflict_dn),
3841 ldb_errstring(ldb_module_get_ctx(ar->module)));
3843 return ldb_module_done(ar->req, NULL, NULL,
3844 LDB_ERR_OPERATIONS_ERROR);
3847 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
3848 if (ret != LDB_SUCCESS) {
3849 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)));
3850 return ldb_module_done(ar->req, NULL, NULL,
3851 LDB_ERR_OPERATIONS_ERROR);
3857 * We are on an RODC, or were a GC for this
3858 * partition, so we have to fail this until
3859 * someone who owns the partition sorts it
3862 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3863 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
3864 " - We must fail the operation until a master for this partition resolves the conflict",
3865 ldb_dn_get_linearized(conflict_dn));
3870 * first we need the replPropertyMetaData attribute from the
3871 * local, conflicting record
3873 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
3875 DSDB_FLAG_NEXT_MODULE |
3876 DSDB_SEARCH_SHOW_DELETED |
3877 DSDB_SEARCH_SHOW_RECYCLED, req);
3878 if (ret != LDB_SUCCESS) {
3879 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
3880 ldb_dn_get_linearized(conflict_dn)));
3884 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
3885 if (omd_value == NULL) {
3886 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
3887 ldb_dn_get_linearized(conflict_dn)));
3891 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
3892 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3893 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3894 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
3895 ldb_dn_get_linearized(conflict_dn)));
3899 rmd = ar->objs->objects[ar->index_current].meta_data;
3902 * we decide which is newer based on the RPMD on the name
3903 * attribute. See [MS-DRSR] ResolveNameConflict.
3905 * We expect omd_name to be present, as this is from a local
3906 * search, but while rmd_name should have been given to us by
3907 * the remote server, if it is missing we just prefer the
3909 * replmd_replPropertyMetaData1_new_should_be_taken()
3911 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
3912 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
3914 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
3915 ldb_dn_get_linearized(conflict_dn)));
3920 * Should we preserve the current record, and so rename the
3921 * incoming record to be a conflict?
3923 rename_incoming_record
3924 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
3925 omd_name, rmd_name);
3927 if (rename_incoming_record) {
3929 struct ldb_dn *new_dn;
3931 guid = samdb_result_guid(msg, "objectGUID");
3932 if (GUID_all_zero(&guid)) {
3933 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
3934 ldb_dn_get_linearized(conflict_dn)));
3937 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3938 if (new_dn == NULL) {
3939 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3940 ldb_dn_get_linearized(conflict_dn)));
3944 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
3945 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3947 /* re-submit the request, but with the new DN */
3948 callback = replmd_op_name_modify_callback;
3951 /* we are renaming the existing record */
3953 struct ldb_dn *new_dn;
3955 guid = samdb_result_guid(res->msgs[0], "objectGUID");
3956 if (GUID_all_zero(&guid)) {
3957 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
3958 ldb_dn_get_linearized(conflict_dn)));
3962 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3963 if (new_dn == NULL) {
3964 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3965 ldb_dn_get_linearized(conflict_dn)));
3969 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
3970 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3972 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
3973 DSDB_FLAG_OWN_MODULE, req);
3974 if (ret != LDB_SUCCESS) {
3975 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
3976 ldb_dn_get_linearized(conflict_dn),
3977 ldb_dn_get_linearized(new_dn),
3978 ldb_errstring(ldb_module_get_ctx(ar->module))));
3983 * now we need to ensure that the rename is seen as an
3984 * originating update. We do that with a modify.
3986 ret = replmd_name_modify(ar, req, new_dn);
3987 if (ret != LDB_SUCCESS) {
3991 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
3992 ldb_dn_get_linearized(req->op.add.message->dn)));
3995 ret = ldb_build_add_req(&down_req,
3996 ldb_module_get_ctx(ar->module),
4003 if (ret != LDB_SUCCESS) {
4006 LDB_REQ_SET_LOCATION(down_req);
4008 /* current partition control needed by "repmd_op_callback" */
4009 ret = ldb_request_add_control(down_req,
4010 DSDB_CONTROL_CURRENT_PARTITION_OID,
4012 if (ret != LDB_SUCCESS) {
4013 return replmd_replicated_request_error(ar, ret);
4016 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4017 /* this tells the partition module to make it a
4018 partial replica if creating an NC */
4019 ret = ldb_request_add_control(down_req,
4020 DSDB_CONTROL_PARTIAL_REPLICA,
4022 if (ret != LDB_SUCCESS) {
4023 return replmd_replicated_request_error(ar, ret);
4028 * Finally we re-run the add, otherwise the new record won't
4029 * exist, as we are here because of that exact failure!
4031 return ldb_next_request(ar->module, down_req);
4034 /* on failure make the caller get the error. This means
4035 * replication will stop with an error, but there is not much
4038 return ldb_module_done(ar->req, NULL, NULL,
4043 callback for replmd_replicated_apply_add()
4044 This copes with the creation of conflict records in the case where
4045 the DN exists, but with a different objectGUID
4047 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
4049 struct replmd_replicated_request *ar =
4050 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4052 if (ar->objs->objects[ar->index_current].last_known_parent) {
4053 /* This is like a conflict DN, where we put the object in LostAndFound
4054 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
4055 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
4058 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
4062 this is called when a new object comes in over DRS
4064 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
4066 struct ldb_context *ldb;
4067 struct ldb_request *change_req;
4068 enum ndr_err_code ndr_err;
4069 struct ldb_message *msg;
4070 struct replPropertyMetaDataBlob *md;
4071 struct ldb_val md_value;
4074 bool remote_isDeleted = false;
4075 const struct dsdb_attribute *rdn_sa;
4076 const char *rdn_name;
4078 ldb = ldb_module_get_ctx(ar->module);
4079 msg = ar->objs->objects[ar->index_current].msg;
4080 md = ar->objs->objects[ar->index_current].meta_data;
4082 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4083 if (ret != LDB_SUCCESS) {
4084 return replmd_replicated_request_error(ar, ret);
4087 ret = dsdb_msg_add_guid(msg,
4088 &ar->objs->objects[ar->index_current].object_guid,
4090 if (ret != LDB_SUCCESS) {
4091 return replmd_replicated_request_error(ar, ret);
4094 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4095 if (ret != LDB_SUCCESS) {
4096 return replmd_replicated_request_error(ar, ret);
4099 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
4100 if (ret != LDB_SUCCESS) {
4101 return replmd_replicated_request_error(ar, ret);
4104 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4105 if (ret != LDB_SUCCESS) {
4106 return replmd_replicated_request_error(ar, ret);
4109 /* remove any message elements that have zero values */
4110 for (i=0; i<msg->num_elements; i++) {
4111 struct ldb_message_element *el = &msg->elements[i];
4113 if (el->num_values == 0) {
4114 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
4115 ldb_asprintf_errstring(ldb, __location__
4116 ": empty objectClass sent on %s, aborting replication\n",
4117 ldb_dn_get_linearized(msg->dn));
4118 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
4121 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
4123 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
4124 msg->num_elements--;
4131 struct GUID_txt_buf guid_txt;
4133 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
4134 DEBUG(4, ("DRS replication add message of %s:\n%s\n",
4135 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
4140 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4141 "isDeleted", false);
4144 * the meta data array is already sorted by the caller
4147 rdn_name = ldb_dn_get_rdn_name(msg->dn);
4148 if (rdn_name == NULL) {
4149 ldb_asprintf_errstring(ldb, __location__ ": No rDN for %s?\n", ldb_dn_get_linearized(msg->dn));
4150 return replmd_replicated_request_error(ar, LDB_ERR_INVALID_DN_SYNTAX);
4153 rdn_sa = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
4154 if (rdn_sa == NULL) {
4155 ldb_asprintf_errstring(ldb, ": No schema attribute found for rDN %s for %s\n",
4156 rdn_name, ldb_dn_get_linearized(msg->dn));
4157 return replmd_replicated_request_error(ar, LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE);
4160 ret = replmd_replPropertyMetaDataCtr1_verify(ldb, &md->ctr.ctr1, rdn_sa, msg->dn);
4161 if (ret != LDB_SUCCESS) {
4162 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
4163 return replmd_replicated_request_error(ar, ret);
4166 for (i=0; i < md->ctr.ctr1.count; i++) {
4167 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
4169 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
4170 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
4171 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4172 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4173 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4175 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
4176 if (ret != LDB_SUCCESS) {
4177 return replmd_replicated_request_error(ar, ret);
4180 replmd_ldb_message_sort(msg, ar->schema);
4182 if (!remote_isDeleted) {
4183 ret = dsdb_module_schedule_sd_propagation(ar->module,
4184 ar->objs->partition_dn,
4186 if (ret != LDB_SUCCESS) {
4187 return replmd_replicated_request_error(ar, ret);
4191 ar->isDeleted = remote_isDeleted;
4193 ret = ldb_build_add_req(&change_req,
4199 replmd_op_add_callback,
4201 LDB_REQ_SET_LOCATION(change_req);
4202 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4204 /* current partition control needed by "repmd_op_callback" */
4205 ret = ldb_request_add_control(change_req,
4206 DSDB_CONTROL_CURRENT_PARTITION_OID,
4208 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4210 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4211 /* this tells the partition module to make it a
4212 partial replica if creating an NC */
4213 ret = ldb_request_add_control(change_req,
4214 DSDB_CONTROL_PARTIAL_REPLICA,
4216 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4219 return ldb_next_request(ar->module, change_req);
4222 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
4223 struct ldb_reply *ares)
4225 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4226 struct replmd_replicated_request);
4230 return ldb_module_done(ar->req, NULL, NULL,
4231 LDB_ERR_OPERATIONS_ERROR);
4235 * The error NO_SUCH_OBJECT is not expected, unless the search
4236 * base is the partition DN, and that case doesn't happen here
4237 * because then we wouldn't get a parent_guid_value in any
4240 if (ares->error != LDB_SUCCESS) {
4241 return ldb_module_done(ar->req, ares->controls,
4242 ares->response, ares->error);
4245 switch (ares->type) {
4246 case LDB_REPLY_ENTRY:
4248 struct ldb_message *parent_msg = ares->message;
4249 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
4250 struct ldb_dn *parent_dn;
4253 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
4254 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
4255 /* Per MS-DRSR 4.1.10.6.10
4256 * FindBestParentObject we need to move this
4257 * new object under a deleted object to
4259 struct ldb_dn *nc_root;
4261 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
4262 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4263 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4264 "No suitable NC root found for %s. "
4265 "We need to move this object because parent object %s "
4266 "is deleted, but this object is not.",
4267 ldb_dn_get_linearized(msg->dn),
4268 ldb_dn_get_linearized(parent_msg->dn));
4269 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4270 } else if (ret != LDB_SUCCESS) {
4271 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4272 "Unable to find NC root for %s: %s. "
4273 "We need to move this object because parent object %s "
4274 "is deleted, but this object is not.",
4275 ldb_dn_get_linearized(msg->dn),
4276 ldb_errstring(ldb_module_get_ctx(ar->module)),
4277 ldb_dn_get_linearized(parent_msg->dn));
4278 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4281 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
4283 DS_GUID_LOSTANDFOUND_CONTAINER,
4285 if (ret != LDB_SUCCESS) {
4286 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4287 "Unable to find LostAndFound Container for %s "
4288 "in partition %s: %s. "
4289 "We need to move this object because parent object %s "
4290 "is deleted, but this object is not.",
4291 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
4292 ldb_errstring(ldb_module_get_ctx(ar->module)),
4293 ldb_dn_get_linearized(parent_msg->dn));
4294 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4296 ar->objs->objects[ar->index_current].last_known_parent
4297 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4301 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4304 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
4306 comp_num = ldb_dn_get_comp_num(msg->dn);
4308 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
4310 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4313 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
4315 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4319 case LDB_REPLY_REFERRAL:
4320 /* we ignore referrals */
4323 case LDB_REPLY_DONE:
4325 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
4326 struct GUID_txt_buf str_buf;
4327 if (ar->search_msg != NULL) {
4328 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4329 "No parent with GUID %s found for object locally known as %s",
4330 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
4331 ldb_dn_get_linearized(ar->search_msg->dn));
4333 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4334 "No parent with GUID %s found for object remotely known as %s",
4335 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
4336 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
4340 * This error code is really important, as it
4341 * is the flag back to the callers to retry
4342 * this with DRSUAPI_DRS_GET_ANC, and so get
4343 * the parent objects before the child
4346 return ldb_module_done(ar->req, NULL, NULL,
4347 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
4350 if (ar->search_msg != NULL) {
4351 ret = replmd_replicated_apply_merge(ar);
4353 ret = replmd_replicated_apply_add(ar);
4355 if (ret != LDB_SUCCESS) {
4356 return ldb_module_done(ar->req, NULL, NULL, ret);
4365 * Look for the parent object, so we put the new object in the right
4366 * place This is akin to NameObject in MS-DRSR - this routine and the
4367 * callbacks find the right parent name, and correct name for this
4371 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
4373 struct ldb_context *ldb;
4377 struct ldb_request *search_req;
4378 static const char *attrs[] = {"isDeleted", NULL};
4379 struct GUID_txt_buf guid_str_buf;
4381 ldb = ldb_module_get_ctx(ar->module);
4383 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
4384 if (ar->search_msg != NULL) {
4385 return replmd_replicated_apply_merge(ar);
4387 return replmd_replicated_apply_add(ar);
4391 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
4394 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
4395 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4397 ret = ldb_build_search_req(&search_req,
4400 ar->objs->partition_dn,
4406 replmd_replicated_apply_search_for_parent_callback,
4408 LDB_REQ_SET_LOCATION(search_req);
4410 ret = dsdb_request_add_controls(search_req,
4411 DSDB_SEARCH_SHOW_RECYCLED|
4412 DSDB_SEARCH_SHOW_DELETED|
4413 DSDB_SEARCH_SHOW_EXTENDED_DN);
4414 if (ret != LDB_SUCCESS) {
4418 return ldb_next_request(ar->module, search_req);
4422 handle renames that come in over DRS replication
4424 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
4425 struct ldb_message *msg,
4426 struct ldb_request *parent,
4430 TALLOC_CTX *tmp_ctx = talloc_new(msg);
4431 struct ldb_result *res;
4432 struct ldb_dn *conflict_dn;
4433 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4434 const struct ldb_val *omd_value;
4435 struct replPropertyMetaDataBlob omd, *rmd;
4436 enum ndr_err_code ndr_err;
4437 bool rename_incoming_record, rodc;
4438 struct replPropertyMetaData1 *rmd_name, *omd_name;
4439 struct ldb_dn *new_dn;
4442 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
4443 ldb_dn_get_linearized(ar->search_msg->dn),
4444 ldb_dn_get_linearized(msg->dn)));
4447 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
4448 DSDB_FLAG_NEXT_MODULE, ar->req);
4450 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4451 talloc_free(tmp_ctx);
4452 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
4453 ldb_dn_get_linearized(ar->search_msg->dn),
4454 ldb_dn_get_linearized(msg->dn),
4455 ldb_errstring(ldb_module_get_ctx(ar->module)));
4459 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4460 if (ret != LDB_SUCCESS) {
4461 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4462 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
4463 ldb_errstring(ldb_module_get_ctx(ar->module)));
4464 return LDB_ERR_OPERATIONS_ERROR;
4467 * we have a conflict, and need to decide if we will keep the
4468 * new record or the old record
4471 conflict_dn = msg->dn;
4475 * We are on an RODC, or were a GC for this
4476 * partition, so we have to fail this until
4477 * someone who owns the partition sorts it
4480 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4481 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
4482 " - We must fail the operation until a master for this partition resolves the conflict",
4483 ldb_dn_get_linearized(conflict_dn));
4488 * first we need the replPropertyMetaData attribute from the
4491 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
4493 DSDB_FLAG_NEXT_MODULE |
4494 DSDB_SEARCH_SHOW_DELETED |
4495 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
4496 if (ret != LDB_SUCCESS) {
4497 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4498 ldb_dn_get_linearized(conflict_dn)));
4502 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4503 if (omd_value == NULL) {
4504 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4505 ldb_dn_get_linearized(conflict_dn)));
4509 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4510 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4511 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4512 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4513 ldb_dn_get_linearized(conflict_dn)));
4517 rmd = ar->objs->objects[ar->index_current].meta_data;
4520 * we decide which is newer based on the RPMD on the name
4521 * attribute. See [MS-DRSR] ResolveNameConflict.
4523 * We expect omd_name to be present, as this is from a local
4524 * search, but while rmd_name should have been given to us by
4525 * the remote server, if it is missing we just prefer the
4527 * replmd_replPropertyMetaData1_new_should_be_taken()
4529 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4530 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4532 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4533 ldb_dn_get_linearized(conflict_dn)));
4538 * Should we preserve the current record, and so rename the
4539 * incoming record to be a conflict?
4541 rename_incoming_record =
4542 !replmd_replPropertyMetaData1_new_should_be_taken(
4543 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
4544 omd_name, rmd_name);
4546 if (rename_incoming_record) {
4548 new_dn = replmd_conflict_dn(msg, msg->dn,
4549 &ar->objs->objects[ar->index_current].object_guid);
4550 if (new_dn == NULL) {
4551 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4552 "Failed to form conflict DN for %s\n",
4553 ldb_dn_get_linearized(msg->dn));
4555 return replmd_replicated_request_werror(ar, WERR_NOMEM);
4558 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
4559 DSDB_FLAG_NEXT_MODULE, ar->req);
4560 if (ret != LDB_SUCCESS) {
4561 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4562 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
4563 ldb_dn_get_linearized(conflict_dn),
4564 ldb_dn_get_linearized(ar->search_msg->dn),
4565 ldb_dn_get_linearized(new_dn),
4566 ldb_errstring(ldb_module_get_ctx(ar->module)));
4567 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4575 /* we are renaming the existing record */
4577 guid = samdb_result_guid(res->msgs[0], "objectGUID");
4578 if (GUID_all_zero(&guid)) {
4579 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
4580 ldb_dn_get_linearized(conflict_dn)));
4584 new_dn = replmd_conflict_dn(tmp_ctx, conflict_dn, &guid);
4585 if (new_dn == NULL) {
4586 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4587 ldb_dn_get_linearized(conflict_dn)));
4591 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4592 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4594 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
4595 DSDB_FLAG_OWN_MODULE, ar->req);
4596 if (ret != LDB_SUCCESS) {
4597 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
4598 ldb_dn_get_linearized(conflict_dn),
4599 ldb_dn_get_linearized(new_dn),
4600 ldb_errstring(ldb_module_get_ctx(ar->module))));
4605 * now we need to ensure that the rename is seen as an
4606 * originating update. We do that with a modify.
4608 ret = replmd_name_modify(ar, ar->req, new_dn);
4609 if (ret != LDB_SUCCESS) {
4613 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
4614 ldb_dn_get_linearized(ar->search_msg->dn),
4615 ldb_dn_get_linearized(msg->dn)));
4618 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
4619 DSDB_FLAG_NEXT_MODULE, ar->req);
4620 if (ret != LDB_SUCCESS) {
4621 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
4622 ldb_dn_get_linearized(ar->search_msg->dn),
4623 ldb_dn_get_linearized(msg->dn),
4624 ldb_errstring(ldb_module_get_ctx(ar->module))));
4630 * On failure make the caller get the error
4631 * This means replication will stop with an error,
4632 * but there is not much else we can do. In the
4633 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
4637 talloc_free(tmp_ctx);
4642 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
4644 struct ldb_context *ldb;
4645 struct ldb_request *change_req;
4646 enum ndr_err_code ndr_err;
4647 struct ldb_message *msg;
4648 struct replPropertyMetaDataBlob *rmd;
4649 struct replPropertyMetaDataBlob omd;
4650 const struct ldb_val *omd_value;
4651 struct replPropertyMetaDataBlob nmd;
4652 struct ldb_val nmd_value;
4653 struct GUID remote_parent_guid;
4656 unsigned int removed_attrs = 0;
4658 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
4659 bool isDeleted = false;
4660 bool local_isDeleted = false;
4661 bool remote_isDeleted = false;
4662 bool take_remote_isDeleted = false;
4663 bool sd_updated = false;
4664 bool renamed = false;
4667 ldb = ldb_module_get_ctx(ar->module);
4668 msg = ar->objs->objects[ar->index_current].msg;
4670 rmd = ar->objs->objects[ar->index_current].meta_data;
4674 /* find existing meta data */
4675 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
4677 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
4678 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4679 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4680 nt_status = ndr_map_error2ntstatus(ndr_err);
4681 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4684 if (omd.version != 1) {
4685 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4689 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
4690 "isDeleted", false);
4691 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4692 "isDeleted", false);
4695 * Fill in the remote_parent_guid with the GUID or an all-zero
4698 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
4699 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
4701 remote_parent_guid = GUID_zero();
4705 * To ensure we follow a complex rename chain around, we have
4706 * to confirm that the DN is the same (mostly to confirm the
4707 * RDN) and the parentGUID is the same.
4709 * This ensures we keep things under the correct parent, which
4710 * replmd_replicated_handle_rename() will do.
4713 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
4714 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
4718 * handle renames, even just by case that come in over
4719 * DRS. Changes in the parent DN don't hit us here,
4720 * because the search for a parent will clean up those
4723 * We also have already filtered out the case where
4724 * the peer has an older name to what we have (see
4725 * replmd_replicated_apply_search_callback())
4728 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
4731 if (ret != LDB_SUCCESS) {
4732 ldb_debug(ldb, LDB_DEBUG_FATAL,
4733 "replmd_replicated_request rename %s => %s failed - %s\n",
4734 ldb_dn_get_linearized(ar->search_msg->dn),
4735 ldb_dn_get_linearized(msg->dn),
4736 ldb_errstring(ldb));
4737 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4740 if (renamed == true) {
4742 * Set the callback to one that will fix up the name
4743 * metadata on the new conflict DN
4745 callback = replmd_op_name_modify_callback;
4750 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
4751 nmd.ctr.ctr1.array = talloc_array(ar,
4752 struct replPropertyMetaData1,
4753 nmd.ctr.ctr1.count);
4754 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4756 /* first copy the old meta data */
4757 for (i=0; i < omd.ctr.ctr1.count; i++) {
4758 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
4763 /* now merge in the new meta data */
4764 for (i=0; i < rmd->ctr.ctr1.count; i++) {
4767 for (j=0; j < ni; j++) {
4770 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
4774 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
4775 ar->objs->dsdb_repl_flags,
4776 &nmd.ctr.ctr1.array[j],
4777 &rmd->ctr.ctr1.array[i]);
4779 /* replace the entry */
4780 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
4781 if (ar->seq_num == 0) {
4782 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4783 if (ret != LDB_SUCCESS) {
4784 return replmd_replicated_request_error(ar, ret);
4787 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
4788 switch (nmd.ctr.ctr1.array[j].attid) {
4789 case DRSUAPI_ATTID_ntSecurityDescriptor:
4792 case DRSUAPI_ATTID_isDeleted:
4793 take_remote_isDeleted = true;
4802 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
4803 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
4804 msg->elements[i-removed_attrs].name,
4805 ldb_dn_get_linearized(msg->dn),
4806 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
4809 /* we don't want to apply this change so remove the attribute */
4810 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
4817 if (found) continue;
4819 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
4820 if (ar->seq_num == 0) {
4821 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4822 if (ret != LDB_SUCCESS) {
4823 return replmd_replicated_request_error(ar, ret);
4826 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
4827 switch (nmd.ctr.ctr1.array[ni].attid) {
4828 case DRSUAPI_ATTID_ntSecurityDescriptor:
4831 case DRSUAPI_ATTID_isDeleted:
4832 take_remote_isDeleted = true;
4841 * finally correct the size of the meta_data array
4843 nmd.ctr.ctr1.count = ni;
4846 * the rdn attribute (the alias for the name attribute),
4847 * 'cn' for most objects is the last entry in the meta data array
4850 * sort the new meta data array
4852 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, ar->schema, msg->dn);
4853 if (ret != LDB_SUCCESS) {
4854 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
4859 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
4862 * This also controls SD propagation below
4864 if (take_remote_isDeleted) {
4865 isDeleted = remote_isDeleted;
4867 isDeleted = local_isDeleted;
4870 ar->isDeleted = isDeleted;
4873 * check if some replicated attributes left, otherwise skip the ldb_modify() call
4875 if (msg->num_elements == 0) {
4876 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
4879 return replmd_replicated_apply_isDeleted(ar);
4882 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
4883 ar->index_current, msg->num_elements);
4889 if (sd_updated && !isDeleted) {
4890 ret = dsdb_module_schedule_sd_propagation(ar->module,
4891 ar->objs->partition_dn,
4893 if (ret != LDB_SUCCESS) {
4894 return ldb_operr(ldb);
4898 /* create the meta data value */
4899 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
4900 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
4901 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4902 nt_status = ndr_map_error2ntstatus(ndr_err);
4903 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4907 * when we know that we'll modify the record, add the whenChanged, uSNChanged
4908 * and replPopertyMetaData attributes
4910 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4911 if (ret != LDB_SUCCESS) {
4912 return replmd_replicated_request_error(ar, ret);
4914 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4915 if (ret != LDB_SUCCESS) {
4916 return replmd_replicated_request_error(ar, ret);
4918 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
4919 if (ret != LDB_SUCCESS) {
4920 return replmd_replicated_request_error(ar, ret);
4923 replmd_ldb_message_sort(msg, ar->schema);
4925 /* we want to replace the old values */
4926 for (i=0; i < msg->num_elements; i++) {
4927 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
4928 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
4929 if (msg->elements[i].num_values == 0) {
4930 ldb_asprintf_errstring(ldb, __location__
4931 ": objectClass removed on %s, aborting replication\n",
4932 ldb_dn_get_linearized(msg->dn));
4933 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
4939 struct GUID_txt_buf guid_txt;
4941 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4942 DEBUG(4, ("DRS replication modify message of %s:\n%s\n",
4943 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
4948 ret = ldb_build_mod_req(&change_req,
4956 LDB_REQ_SET_LOCATION(change_req);
4957 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4959 /* current partition control needed by "repmd_op_callback" */
4960 ret = ldb_request_add_control(change_req,
4961 DSDB_CONTROL_CURRENT_PARTITION_OID,
4963 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4965 return ldb_next_request(ar->module, change_req);
4968 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
4969 struct ldb_reply *ares)
4971 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4972 struct replmd_replicated_request);
4976 return ldb_module_done(ar->req, NULL, NULL,
4977 LDB_ERR_OPERATIONS_ERROR);
4979 if (ares->error != LDB_SUCCESS &&
4980 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4981 return ldb_module_done(ar->req, ares->controls,
4982 ares->response, ares->error);
4985 switch (ares->type) {
4986 case LDB_REPLY_ENTRY:
4987 ar->search_msg = talloc_steal(ar, ares->message);
4990 case LDB_REPLY_REFERRAL:
4991 /* we ignore referrals */
4994 case LDB_REPLY_DONE:
4996 struct replPropertyMetaData1 *md_remote;
4997 struct replPropertyMetaData1 *md_local;
4999 struct replPropertyMetaDataBlob omd;
5000 const struct ldb_val *omd_value;
5001 struct replPropertyMetaDataBlob *rmd;
5002 struct ldb_message *msg;
5004 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
5005 ar->objs->objects[ar->index_current].last_known_parent = NULL;
5008 * This is the ADD case, find the appropriate parent,
5009 * as this object doesn't exist locally:
5011 if (ar->search_msg == NULL) {
5012 ret = replmd_replicated_apply_search_for_parent(ar);
5013 if (ret != LDB_SUCCESS) {
5014 return ldb_module_done(ar->req, NULL, NULL, ret);
5021 * Otherwise, in the MERGE case, work out if we are
5022 * attempting a rename, and if so find the parent the
5023 * newly renamed object wants to belong under (which
5024 * may not be the parent in it's attached string DN
5026 rmd = ar->objs->objects[ar->index_current].meta_data;
5030 /* find existing meta data */
5031 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5033 enum ndr_err_code ndr_err;
5034 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5035 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5036 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5037 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5038 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5041 if (omd.version != 1) {
5042 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5046 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
5048 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
5049 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
5050 && GUID_all_zero(&ar->local_parent_guid)) {
5051 DEBUG(0, ("Refusing to replicate new version of %s "
5052 "as local object has an all-zero parentGUID attribute, "
5053 "despite not being an NC root\n",
5054 ldb_dn_get_linearized(ar->search_msg->dn)));
5055 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5059 * now we need to check for double renames. We could have a
5060 * local rename pending which our replication partner hasn't
5061 * received yet. We choose which one wins by looking at the
5062 * attribute stamps on the two objects, the newer one wins.
5064 * This also simply applies the correct algorithms for
5065 * determining if a change was made to name at all, or
5066 * if the object has just been renamed under the same
5069 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5070 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5072 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5073 ldb_dn_get_linearized(ar->search_msg->dn)));
5074 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5078 * if there is no name attribute given then we have to assume the
5079 * object we've received has the older name
5081 if (replmd_replPropertyMetaData1_new_should_be_taken(
5082 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5083 md_local, md_remote)) {
5084 struct GUID_txt_buf p_guid_local;
5085 struct GUID_txt_buf p_guid_remote;
5086 msg = ar->objs->objects[ar->index_current].msg;
5088 /* Merge on the existing object, with rename */
5090 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
5091 "as incoming object changing to %s under %s\n",
5092 ldb_dn_get_linearized(ar->search_msg->dn),
5093 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5094 ldb_dn_get_linearized(msg->dn),
5095 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5097 ret = replmd_replicated_apply_search_for_parent(ar);
5099 struct GUID_txt_buf p_guid_local;
5100 struct GUID_txt_buf p_guid_remote;
5101 msg = ar->objs->objects[ar->index_current].msg;
5104 * Merge on the existing object, force no
5105 * rename (code below just to explain why in
5109 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
5110 ldb_dn_get_linearized(msg->dn)) == 0) {
5111 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
5112 GUID_equal(&ar->local_parent_guid,
5113 ar->objs->objects[ar->index_current].parent_guid)
5115 DEBUG(4,(__location__ ": Keeping object %s at under %s "
5116 "despite incoming object changing parent to %s\n",
5117 ldb_dn_get_linearized(ar->search_msg->dn),
5118 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5119 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5123 DEBUG(4,(__location__ ": Keeping object %s at under %s "
5124 " and rejecting older rename to %s under %s\n",
5125 ldb_dn_get_linearized(ar->search_msg->dn),
5126 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5127 ldb_dn_get_linearized(msg->dn),
5128 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5132 * This assignment ensures that the strcmp()
5133 * and GUID_equal() calls in
5134 * replmd_replicated_apply_merge() avoids the
5137 ar->objs->objects[ar->index_current].parent_guid =
5138 &ar->local_parent_guid;
5140 msg->dn = ar->search_msg->dn;
5141 ret = replmd_replicated_apply_merge(ar);
5143 if (ret != LDB_SUCCESS) {
5144 return ldb_module_done(ar->req, NULL, NULL, ret);
5153 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
5155 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
5157 struct ldb_context *ldb;
5161 struct ldb_request *search_req;
5162 static const char *attrs[] = { "*", "parentGUID", "instanceType",
5163 "replPropertyMetaData", "nTSecurityDescriptor",
5165 struct GUID_txt_buf guid_str_buf;
5167 if (ar->index_current >= ar->objs->num_objects) {
5168 /* done with it, go to next stage */
5169 return replmd_replicated_uptodate_vector(ar);
5172 ldb = ldb_module_get_ctx(ar->module);
5173 ar->search_msg = NULL;
5174 ar->isDeleted = false;
5176 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5179 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5180 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5182 ret = ldb_build_search_req(&search_req,
5185 ar->objs->partition_dn,
5191 replmd_replicated_apply_search_callback,
5193 LDB_REQ_SET_LOCATION(search_req);
5195 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
5197 if (ret != LDB_SUCCESS) {
5201 return ldb_next_request(ar->module, search_req);
5205 * This is essentially a wrapper for replmd_replicated_apply_next()
5207 * This is needed to ensure that both codepaths call this handler.
5209 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
5211 struct ldb_dn *deleted_objects_dn;
5212 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5213 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
5214 &deleted_objects_dn);
5215 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
5217 * Do a delete here again, so that if there is
5218 * anything local that conflicts with this
5219 * object being deleted, it is removed. This
5220 * includes links. See MS-DRSR 4.1.10.6.9
5223 * If the object is already deleted, and there
5224 * is no more work required, it doesn't do
5228 /* This has been updated to point to the DN we eventually did the modify on */
5230 struct ldb_request *del_req;
5231 struct ldb_result *res;
5233 TALLOC_CTX *tmp_ctx = talloc_new(ar);
5235 ret = ldb_oom(ldb_module_get_ctx(ar->module));
5239 res = talloc_zero(tmp_ctx, struct ldb_result);
5241 ret = ldb_oom(ldb_module_get_ctx(ar->module));
5242 talloc_free(tmp_ctx);
5246 /* Build a delete request, which hopefully will artually turn into nothing */
5247 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
5251 ldb_modify_default_callback,
5253 LDB_REQ_SET_LOCATION(del_req);
5254 if (ret != LDB_SUCCESS) {
5255 talloc_free(tmp_ctx);
5260 * This is the guts of the call, call back
5261 * into our delete code, but setting the
5262 * re_delete flag so we delete anything that
5263 * shouldn't be there on a deleted or recycled
5266 ret = replmd_delete_internals(ar->module, del_req, true);
5267 if (ret == LDB_SUCCESS) {
5268 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
5271 talloc_free(tmp_ctx);
5272 if (ret != LDB_SUCCESS) {
5277 ar->index_current++;
5278 return replmd_replicated_apply_next(ar);
5281 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
5282 struct ldb_reply *ares)
5284 struct ldb_context *ldb;
5285 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5286 struct replmd_replicated_request);
5287 ldb = ldb_module_get_ctx(ar->module);
5290 return ldb_module_done(ar->req, NULL, NULL,
5291 LDB_ERR_OPERATIONS_ERROR);
5293 if (ares->error != LDB_SUCCESS) {
5294 return ldb_module_done(ar->req, ares->controls,
5295 ares->response, ares->error);
5298 if (ares->type != LDB_REPLY_DONE) {
5299 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
5300 return ldb_module_done(ar->req, NULL, NULL,
5301 LDB_ERR_OPERATIONS_ERROR);
5306 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5309 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
5311 struct ldb_context *ldb;
5312 struct ldb_request *change_req;
5313 enum ndr_err_code ndr_err;
5314 struct ldb_message *msg;
5315 struct replUpToDateVectorBlob ouv;
5316 const struct ldb_val *ouv_value;
5317 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
5318 struct replUpToDateVectorBlob nuv;
5319 struct ldb_val nuv_value;
5320 struct ldb_message_element *nuv_el = NULL;
5321 struct ldb_message_element *orf_el = NULL;
5322 struct repsFromToBlob nrf;
5323 struct ldb_val *nrf_value = NULL;
5324 struct ldb_message_element *nrf_el = NULL;
5328 time_t t = time(NULL);
5331 uint32_t instanceType;
5333 ldb = ldb_module_get_ctx(ar->module);
5334 ruv = ar->objs->uptodateness_vector;
5340 unix_to_nt_time(&now, t);
5342 if (ar->search_msg == NULL) {
5343 /* this happens for a REPL_OBJ call where we are
5344 creating the target object by replicating it. The
5345 subdomain join code does this for the partition DN
5347 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
5348 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5351 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
5352 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
5353 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
5354 ldb_dn_get_linearized(ar->search_msg->dn)));
5355 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5359 * first create the new replUpToDateVector
5361 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
5363 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
5364 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
5365 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5366 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5367 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5370 if (ouv.version != 2) {
5371 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5376 * the new uptodateness vector will at least
5377 * contain 1 entry, one for the source_dsa
5379 * plus optional values from our old vector and the one from the source_dsa
5381 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
5382 if (ruv) nuv.ctr.ctr2.count += ruv->count;
5383 nuv.ctr.ctr2.cursors = talloc_array(ar,
5384 struct drsuapi_DsReplicaCursor2,
5385 nuv.ctr.ctr2.count);
5386 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5388 /* first copy the old vector */
5389 for (i=0; i < ouv.ctr.ctr2.count; i++) {
5390 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
5394 /* merge in the source_dsa vector is available */
5395 for (i=0; (ruv && i < ruv->count); i++) {
5398 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
5399 &ar->our_invocation_id)) {
5403 for (j=0; j < ni; j++) {
5404 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
5405 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
5411 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
5412 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
5417 if (found) continue;
5419 /* if it's not there yet, add it */
5420 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
5425 * finally correct the size of the cursors array
5427 nuv.ctr.ctr2.count = ni;
5432 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
5435 * create the change ldb_message
5437 msg = ldb_msg_new(ar);
5438 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5439 msg->dn = ar->search_msg->dn;
5441 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
5442 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
5443 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5444 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5445 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5447 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
5448 if (ret != LDB_SUCCESS) {
5449 return replmd_replicated_request_error(ar, ret);
5451 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
5454 * now create the new repsFrom value from the given repsFromTo1 structure
5458 nrf.ctr.ctr1 = *ar->objs->source_dsa;
5459 nrf.ctr.ctr1.last_attempt = now;
5460 nrf.ctr.ctr1.last_success = now;
5461 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
5464 * first see if we already have a repsFrom value for the current source dsa
5465 * if so we'll later replace this value
5467 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
5469 for (i=0; i < orf_el->num_values; i++) {
5470 struct repsFromToBlob *trf;
5472 trf = talloc(ar, struct repsFromToBlob);
5473 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5475 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
5476 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
5477 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5478 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5479 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5482 if (trf->version != 1) {
5483 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5487 * we compare the source dsa objectGUID not the invocation_id
5488 * because we want only one repsFrom value per source dsa
5489 * and when the invocation_id of the source dsa has changed we don't need
5490 * the old repsFrom with the old invocation_id
5492 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
5493 &ar->objs->source_dsa->source_dsa_obj_guid)) {
5499 nrf_value = &orf_el->values[i];
5504 * copy over all old values to the new ldb_message
5506 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
5507 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5512 * if we haven't found an old repsFrom value for the current source dsa
5513 * we'll add a new value
5516 struct ldb_val zero_value;
5517 ZERO_STRUCT(zero_value);
5518 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
5519 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5521 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
5524 /* we now fill the value which is already attached to ldb_message */
5525 ndr_err = ndr_push_struct_blob(nrf_value, msg,
5527 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
5528 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5529 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5530 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5534 * the ldb_message_element for the attribute, has all the old values and the new one
5535 * so we'll replace the whole attribute with all values
5537 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
5539 if (CHECK_DEBUGLVL(4)) {
5540 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5541 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
5545 /* prepare the ldb_modify() request */
5546 ret = ldb_build_mod_req(&change_req,
5552 replmd_replicated_uptodate_modify_callback,
5554 LDB_REQ_SET_LOCATION(change_req);
5555 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5557 return ldb_next_request(ar->module, change_req);
5560 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
5561 struct ldb_reply *ares)
5563 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5564 struct replmd_replicated_request);
5568 return ldb_module_done(ar->req, NULL, NULL,
5569 LDB_ERR_OPERATIONS_ERROR);
5571 if (ares->error != LDB_SUCCESS &&
5572 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5573 return ldb_module_done(ar->req, ares->controls,
5574 ares->response, ares->error);
5577 switch (ares->type) {
5578 case LDB_REPLY_ENTRY:
5579 ar->search_msg = talloc_steal(ar, ares->message);
5582 case LDB_REPLY_REFERRAL:
5583 /* we ignore referrals */
5586 case LDB_REPLY_DONE:
5587 ret = replmd_replicated_uptodate_modify(ar);
5588 if (ret != LDB_SUCCESS) {
5589 return ldb_module_done(ar->req, NULL, NULL, ret);
5598 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
5600 struct ldb_context *ldb;
5602 static const char *attrs[] = {
5603 "replUpToDateVector",
5608 struct ldb_request *search_req;
5610 ldb = ldb_module_get_ctx(ar->module);
5611 ar->search_msg = NULL;
5613 ret = ldb_build_search_req(&search_req,
5616 ar->objs->partition_dn,
5622 replmd_replicated_uptodate_search_callback,
5624 LDB_REQ_SET_LOCATION(search_req);
5625 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5627 return ldb_next_request(ar->module, search_req);
5632 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
5634 struct ldb_context *ldb;
5635 struct dsdb_extended_replicated_objects *objs;
5636 struct replmd_replicated_request *ar;
5637 struct ldb_control **ctrls;
5640 struct replmd_private *replmd_private =
5641 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5642 struct dsdb_control_replicated_update *rep_update;
5644 ldb = ldb_module_get_ctx(module);
5646 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
5648 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
5650 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
5651 return LDB_ERR_PROTOCOL_ERROR;
5654 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
5655 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
5656 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
5657 return LDB_ERR_PROTOCOL_ERROR;
5660 ar = replmd_ctx_init(module, req);
5662 return LDB_ERR_OPERATIONS_ERROR;
5664 /* Set the flags to have the replmd_op_callback run over the full set of objects */
5665 ar->apply_mode = true;
5667 ar->schema = dsdb_get_schema(ldb, ar);
5669 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
5671 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
5672 return LDB_ERR_CONSTRAINT_VIOLATION;
5675 ctrls = req->controls;
5677 if (req->controls) {
5678 req->controls = talloc_memdup(ar, req->controls,
5679 talloc_get_size(req->controls));
5680 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5683 /* This allows layers further down to know if a change came in
5684 over replication and what the replication flags were */
5685 rep_update = talloc_zero(ar, struct dsdb_control_replicated_update);
5686 if (rep_update == NULL) {
5687 return ldb_module_oom(module);
5689 rep_update->dsdb_repl_flags = objs->dsdb_repl_flags;
5691 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, rep_update);
5692 if (ret != LDB_SUCCESS) {
5696 /* If this change contained linked attributes in the body
5697 * (rather than in the links section) we need to update
5698 * backlinks in linked_attributes */
5699 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
5700 if (ret != LDB_SUCCESS) {
5704 ar->controls = req->controls;
5705 req->controls = ctrls;
5707 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
5709 /* save away the linked attributes for the end of the
5711 for (i=0; i<ar->objs->linked_attributes_count; i++) {
5712 struct la_entry *la_entry;
5714 if (replmd_private->la_ctx == NULL) {
5715 replmd_private->la_ctx = talloc_new(replmd_private);
5717 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
5718 if (la_entry == NULL) {
5720 return LDB_ERR_OPERATIONS_ERROR;
5722 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
5723 if (la_entry->la == NULL) {
5724 talloc_free(la_entry);
5726 return LDB_ERR_OPERATIONS_ERROR;
5728 *la_entry->la = ar->objs->linked_attributes[i];
5730 /* we need to steal the non-scalars so they stay
5731 around until the end of the transaction */
5732 talloc_steal(la_entry->la, la_entry->la->identifier);
5733 talloc_steal(la_entry->la, la_entry->la->value.blob);
5735 DLIST_ADD(replmd_private->la_list, la_entry);
5738 return replmd_replicated_apply_next(ar);
5742 process one linked attribute structure
5744 static int replmd_process_linked_attribute(struct ldb_module *module,
5745 struct la_entry *la_entry,
5746 struct ldb_request *parent)
5748 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
5749 struct ldb_context *ldb = ldb_module_get_ctx(module);
5750 struct ldb_message *msg;
5751 struct ldb_message *target_msg = NULL;
5752 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
5753 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
5755 const struct dsdb_attribute *attr;
5756 struct dsdb_dn *dsdb_dn;
5757 uint64_t seq_num = 0;
5758 struct ldb_message_element *old_el;
5760 time_t t = time(NULL);
5761 struct ldb_result *res;
5762 struct ldb_result *target_res;
5763 const char *attrs[4];
5764 const char *attrs2[] = { "isDeleted", "isRecycled", NULL };
5765 struct parsed_dn *pdn_list, *pdn;
5766 struct GUID guid = GUID_zero();
5768 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
5769 const struct GUID *our_invocation_id;
5771 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
5772 enum deletion_state target_deletion_state = OBJECT_NOT_DELETED;
5775 linked_attributes[0]:
5776 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
5778 identifier: struct drsuapi_DsReplicaObjectIdentifier
5779 __ndr_size : 0x0000003a (58)
5780 __ndr_size_sid : 0x00000000 (0)
5781 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
5783 __ndr_size_dn : 0x00000000 (0)
5785 attid : DRSUAPI_ATTID_member (0x1F)
5786 value: struct drsuapi_DsAttributeValue
5787 __ndr_size : 0x0000007e (126)
5789 blob : DATA_BLOB length=126
5790 flags : 0x00000001 (1)
5791 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
5792 originating_add_time : Wed Sep 2 22:20:01 2009 EST
5793 meta_data: struct drsuapi_DsReplicaMetaData
5794 version : 0x00000015 (21)
5795 originating_change_time : Wed Sep 2 23:39:07 2009 EST
5796 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
5797 originating_usn : 0x000000000001e19c (123292)
5799 (for cases where the link is to a normal DN)
5800 &target: struct drsuapi_DsReplicaObjectIdentifier3
5801 __ndr_size : 0x0000007e (126)
5802 __ndr_size_sid : 0x0000001c (28)
5803 guid : 7639e594-db75-4086-b0d4-67890ae46031
5804 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
5805 __ndr_size_dn : 0x00000022 (34)
5806 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
5809 /* find the attribute being modified */
5810 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
5812 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
5813 talloc_free(tmp_ctx);
5814 return LDB_ERR_OPERATIONS_ERROR;
5817 attrs[0] = attr->lDAPDisplayName;
5818 attrs[1] = "isDeleted";
5819 attrs[2] = "isRecycled";
5822 /* get the existing message from the db for the object with
5823 this GUID, returning attribute being modified. We will then
5824 use this msg as the basis for a modify call */
5825 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
5826 DSDB_FLAG_NEXT_MODULE |
5827 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5828 DSDB_SEARCH_SHOW_RECYCLED |
5829 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
5830 DSDB_SEARCH_REVEAL_INTERNALS,
5832 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
5833 if (ret != LDB_SUCCESS) {
5834 talloc_free(tmp_ctx);
5837 if (res->count != 1) {
5838 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
5839 GUID_string(tmp_ctx, &la->identifier->guid));
5840 talloc_free(tmp_ctx);
5841 return LDB_ERR_NO_SUCH_OBJECT;
5846 * Check for deleted objects per MS-DRSR 4.1.10.6.13
5847 * ProcessLinkValue, because link updates are not applied to
5848 * recycled and tombstone objects. We don't have to delete
5849 * any existing link, that should have happened when the
5850 * object deletion was replicated or initiated.
5853 replmd_deletion_state(module, msg, &deletion_state, NULL);
5855 if (deletion_state >= OBJECT_RECYCLED) {
5856 talloc_free(tmp_ctx);
5860 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
5861 if (old_el == NULL) {
5862 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
5863 if (ret != LDB_SUCCESS) {
5864 ldb_module_oom(module);
5865 talloc_free(tmp_ctx);
5866 return LDB_ERR_OPERATIONS_ERROR;
5869 old_el->flags = LDB_FLAG_MOD_REPLACE;
5872 /* parse the existing links */
5873 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
5874 if (ret != LDB_SUCCESS) {
5875 talloc_free(tmp_ctx);
5879 /* get our invocationId */
5880 our_invocation_id = samdb_ntds_invocation_id(ldb);
5881 if (!our_invocation_id) {
5882 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
5883 talloc_free(tmp_ctx);
5884 return LDB_ERR_OPERATIONS_ERROR;
5887 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
5888 if (ret != LDB_SUCCESS) {
5889 talloc_free(tmp_ctx);
5893 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
5894 if (!W_ERROR_IS_OK(status)) {
5895 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
5896 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
5897 talloc_free(tmp_ctx);
5898 return LDB_ERR_OPERATIONS_ERROR;
5901 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
5902 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
5904 * This strange behaviour (allowing a NULL/missing
5905 * GUID) originally comes from:
5907 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
5908 * Author: Andrew Tridgell <tridge@samba.org>
5909 * Date: Mon Dec 21 21:21:55 2009 +1100
5911 * s4-drs: cope better with NULL GUIDS from DRS
5913 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
5914 * need to match by DN if possible when seeing if we should update an
5917 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
5920 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
5921 dsdb_dn->dn, attrs2,
5922 DSDB_FLAG_NEXT_MODULE |
5923 DSDB_SEARCH_SHOW_RECYCLED |
5924 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5925 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
5927 } else if (!NT_STATUS_IS_OK(ntstatus)) {
5928 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
5930 ldb_dn_get_linearized(dsdb_dn->dn),
5931 ldb_dn_get_linearized(msg->dn));
5932 talloc_free(tmp_ctx);
5933 return LDB_ERR_OPERATIONS_ERROR;
5935 ret = dsdb_module_search(module, tmp_ctx, &target_res,
5936 NULL, LDB_SCOPE_SUBTREE,
5938 DSDB_FLAG_NEXT_MODULE |
5939 DSDB_SEARCH_SHOW_RECYCLED |
5940 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5941 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
5944 GUID_string(tmp_ctx, &guid));
5947 if (ret != LDB_SUCCESS) {
5948 ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to re-resolve GUID %s: %s\n",
5949 GUID_string(tmp_ctx, &guid),
5950 ldb_errstring(ldb_module_get_ctx(module)));
5951 talloc_free(tmp_ctx);
5955 if (target_res->count == 0) {
5956 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
5957 GUID_string(tmp_ctx, &guid),
5958 ldb_dn_get_linearized(dsdb_dn->dn)));
5959 } else if (target_res->count != 1) {
5960 ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching objectGUID %s\n",
5961 GUID_string(tmp_ctx, &guid));
5962 talloc_free(tmp_ctx);
5963 return LDB_ERR_OPERATIONS_ERROR;
5965 target_msg = target_res->msgs[0];
5966 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
5970 * Check for deleted objects per MS-DRSR 4.1.10.6.13
5971 * ProcessLinkValue, because link updates are not applied to
5972 * recycled and tombstone objects. We don't have to delete
5973 * any existing link, that should have happened when the
5974 * object deletion was replicated or initiated.
5976 replmd_deletion_state(module, target_msg,
5977 &target_deletion_state, NULL);
5979 if (target_deletion_state >= OBJECT_RECYCLED) {
5980 talloc_free(tmp_ctx);
5984 /* see if this link already exists */
5985 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
5987 /* see if this update is newer than what we have already */
5988 struct GUID invocation_id = GUID_zero();
5989 uint32_t version = 0;
5990 uint32_t originating_usn = 0;
5991 NTTIME change_time = 0;
5992 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
5994 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
5995 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
5996 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
5997 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
5999 if (!replmd_update_is_newer(&invocation_id,
6000 &la->meta_data.originating_invocation_id,
6002 la->meta_data.version,
6004 la->meta_data.originating_change_time)) {
6005 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
6006 old_el->name, ldb_dn_get_linearized(msg->dn),
6007 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
6008 talloc_free(tmp_ctx);
6012 /* get a seq_num for this change */
6013 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
6014 if (ret != LDB_SUCCESS) {
6015 talloc_free(tmp_ctx);
6019 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
6020 /* remove the existing backlink */
6021 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
6022 if (ret != LDB_SUCCESS) {
6023 talloc_free(tmp_ctx);
6028 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
6029 &la->meta_data.originating_invocation_id,
6030 la->meta_data.originating_usn, seq_num,
6031 la->meta_data.originating_change_time,
6032 la->meta_data.version,
6034 if (ret != LDB_SUCCESS) {
6035 talloc_free(tmp_ctx);
6040 /* add the new backlink */
6041 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
6042 if (ret != LDB_SUCCESS) {
6043 talloc_free(tmp_ctx);
6048 /* get a seq_num for this change */
6049 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
6050 if (ret != LDB_SUCCESS) {
6051 talloc_free(tmp_ctx);
6055 old_el->values = talloc_realloc(msg->elements, old_el->values,
6056 struct ldb_val, old_el->num_values+1);
6057 if (!old_el->values) {
6058 ldb_module_oom(module);
6059 return LDB_ERR_OPERATIONS_ERROR;
6061 old_el->num_values++;
6063 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
6064 &la->meta_data.originating_invocation_id,
6065 la->meta_data.originating_usn, seq_num,
6066 la->meta_data.originating_change_time,
6067 la->meta_data.version,
6068 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
6069 if (ret != LDB_SUCCESS) {
6070 talloc_free(tmp_ctx);
6075 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
6077 if (ret != LDB_SUCCESS) {
6078 talloc_free(tmp_ctx);
6084 /* we only change whenChanged and uSNChanged if the seq_num
6086 ret = add_time_element(msg, "whenChanged", t);
6087 if (ret != LDB_SUCCESS) {
6088 talloc_free(tmp_ctx);
6093 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
6094 if (ret != LDB_SUCCESS) {
6095 talloc_free(tmp_ctx);
6100 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
6101 if (old_el == NULL) {
6102 talloc_free(tmp_ctx);
6103 return ldb_operr(ldb);
6106 ret = dsdb_check_single_valued_link(attr, old_el);
6107 if (ret != LDB_SUCCESS) {
6108 talloc_free(tmp_ctx);
6112 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
6114 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
6115 if (ret != LDB_SUCCESS) {
6116 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
6118 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
6119 talloc_free(tmp_ctx);
6123 talloc_free(tmp_ctx);
6128 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
6130 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
6131 return replmd_extended_replicated_objects(module, req);
6134 return ldb_next_request(module, req);
6139 we hook into the transaction operations to allow us to
6140 perform the linked attribute updates at the end of the whole
6141 transaction. This allows a forward linked attribute to be created
6142 before the object is created. During a vampire, w2k8 sends us linked
6143 attributes before the objects they are part of.
6145 static int replmd_start_transaction(struct ldb_module *module)
6147 /* create our private structure for this transaction */
6148 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
6149 struct replmd_private);
6150 replmd_txn_cleanup(replmd_private);
6152 /* free any leftover mod_usn records from cancelled
6154 while (replmd_private->ncs) {
6155 struct nc_entry *e = replmd_private->ncs;
6156 DLIST_REMOVE(replmd_private->ncs, e);
6160 return ldb_next_start_trans(module);
6164 on prepare commit we loop over our queued la_context structures and
6167 static int replmd_prepare_commit(struct ldb_module *module)
6169 struct replmd_private *replmd_private =
6170 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6171 struct la_entry *la, *prev;
6172 struct la_backlink *bl;
6175 /* walk the list backwards, to do the first entry first, as we
6176 * added the entries with DLIST_ADD() which puts them at the
6177 * start of the list */
6178 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
6179 prev = DLIST_PREV(la);
6180 DLIST_REMOVE(replmd_private->la_list, la);
6181 ret = replmd_process_linked_attribute(module, la, NULL);
6182 if (ret != LDB_SUCCESS) {
6183 replmd_txn_cleanup(replmd_private);
6188 /* process our backlink list, creating and deleting backlinks
6190 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
6191 ret = replmd_process_backlink(module, bl, NULL);
6192 if (ret != LDB_SUCCESS) {
6193 replmd_txn_cleanup(replmd_private);
6198 replmd_txn_cleanup(replmd_private);
6200 /* possibly change @REPLCHANGED */
6201 ret = replmd_notify_store(module, NULL);
6202 if (ret != LDB_SUCCESS) {
6206 return ldb_next_prepare_commit(module);
6209 static int replmd_del_transaction(struct ldb_module *module)
6211 struct replmd_private *replmd_private =
6212 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6213 replmd_txn_cleanup(replmd_private);
6215 return ldb_next_del_trans(module);
6219 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
6220 .name = "repl_meta_data",
6221 .init_context = replmd_init,
6223 .modify = replmd_modify,
6224 .rename = replmd_rename,
6225 .del = replmd_delete,
6226 .extended = replmd_extended,
6227 .start_transaction = replmd_start_transaction,
6228 .prepare_commit = replmd_prepare_commit,
6229 .del_transaction = replmd_del_transaction,
6232 int ldb_repl_meta_data_module_init(const char *version)
6234 LDB_MODULE_CHECK_VERSION(version);
6235 return ldb_register_module(&ldb_repl_meta_data_module_ops);