4 Copyright (C) Simo Sorce 2004-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2013
6 Copyright (C) Andrew Tridgell 2005-2009
7 Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
8 Copyright (C) Matthieu Patou <mat@samba.org> 2010-2011
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 * Component: ldb repl_meta_data module
29 * Description: - add a unique objectGUID onto every new record,
30 * - handle whenCreated, whenChanged timestamps
31 * - handle uSNCreated, uSNChanged numbers
32 * - handle replPropertyMetaData attribute
35 * Author: Stefan Metzmacher
39 #include "ldb_module.h"
40 #include "dsdb/samdb/samdb.h"
41 #include "dsdb/common/proto.h"
42 #include "../libds/common/flags.h"
43 #include "librpc/gen_ndr/ndr_misc.h"
44 #include "librpc/gen_ndr/ndr_drsuapi.h"
45 #include "librpc/gen_ndr/ndr_drsblobs.h"
46 #include "param/param.h"
47 #include "libcli/security/security.h"
48 #include "lib/util/dlinklist.h"
49 #include "dsdb/samdb/ldb_modules/util.h"
50 #include "lib/util/binsearch.h"
51 #include "lib/util/tsort.h"
54 * It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
55 * Deleted Objects Container
57 static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME = 2650466015990000000ULL;
59 struct replmd_private {
61 struct la_entry *la_list;
63 struct la_backlink *la_backlinks;
65 struct nc_entry *prev, *next;
68 uint64_t mod_usn_urgent;
70 struct ldb_dn *schema_dn;
74 struct la_entry *next, *prev;
75 struct drsuapi_DsReplicaLinkedAttribute *la;
78 struct replmd_replicated_request {
79 struct ldb_module *module;
80 struct ldb_request *req;
82 const struct dsdb_schema *schema;
83 struct GUID our_invocation_id;
85 /* the controls we pass down */
86 struct ldb_control **controls;
88 /* details for the mode where we apply a bunch of inbound replication meessages */
90 uint32_t index_current;
91 struct dsdb_extended_replicated_objects *objs;
93 struct ldb_message *search_msg;
94 struct GUID local_parent_guid;
102 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
103 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
105 enum urgent_situation {
106 REPL_URGENT_ON_CREATE = 1,
107 REPL_URGENT_ON_UPDATE = 2,
108 REPL_URGENT_ON_DELETE = 4
111 enum deletion_state {
112 OBJECT_NOT_DELETED=1,
119 static void replmd_deletion_state(struct ldb_module *module,
120 const struct ldb_message *msg,
121 enum deletion_state *current_state,
122 enum deletion_state *next_state)
125 bool enabled = false;
128 *current_state = OBJECT_REMOVED;
129 if (next_state != NULL) {
130 *next_state = OBJECT_REMOVED;
135 ret = dsdb_recyclebin_enabled(module, &enabled);
136 if (ret != LDB_SUCCESS) {
140 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
142 *current_state = OBJECT_TOMBSTONE;
143 if (next_state != NULL) {
144 *next_state = OBJECT_REMOVED;
149 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
150 *current_state = OBJECT_RECYCLED;
151 if (next_state != NULL) {
152 *next_state = OBJECT_REMOVED;
157 *current_state = OBJECT_DELETED;
158 if (next_state != NULL) {
159 *next_state = OBJECT_RECYCLED;
164 *current_state = OBJECT_NOT_DELETED;
165 if (next_state == NULL) {
170 *next_state = OBJECT_DELETED;
172 *next_state = OBJECT_TOMBSTONE;
176 static const struct {
177 const char *update_name;
178 enum urgent_situation repl_situation;
179 } urgent_objects[] = {
180 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
181 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
182 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
183 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
184 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
185 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
189 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
190 static const char *urgent_attrs[] = {
193 "userAccountControl",
198 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
199 enum urgent_situation situation)
202 for (i=0; urgent_objects[i].update_name; i++) {
204 if ((situation & urgent_objects[i].repl_situation) == 0) {
208 for (j=0; j<objectclass_el->num_values; j++) {
209 const struct ldb_val *v = &objectclass_el->values[j];
210 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
218 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
220 if (ldb_attr_in_list(urgent_attrs, el->name)) {
227 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
230 initialise the module
231 allocate the private structure and build the list
232 of partition DNs for use by replmd_notify()
234 static int replmd_init(struct ldb_module *module)
236 struct replmd_private *replmd_private;
237 struct ldb_context *ldb = ldb_module_get_ctx(module);
239 replmd_private = talloc_zero(module, struct replmd_private);
240 if (replmd_private == NULL) {
242 return LDB_ERR_OPERATIONS_ERROR;
244 ldb_module_set_private(module, replmd_private);
246 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
248 return ldb_next_init(module);
252 cleanup our per-transaction contexts
254 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
256 talloc_free(replmd_private->la_ctx);
257 replmd_private->la_list = NULL;
258 replmd_private->la_ctx = NULL;
260 talloc_free(replmd_private->bl_ctx);
261 replmd_private->la_backlinks = NULL;
262 replmd_private->bl_ctx = NULL;
267 struct la_backlink *next, *prev;
268 const char *attr_name;
269 struct GUID forward_guid, target_guid;
274 process a backlinks we accumulated during a transaction, adding and
275 deleting the backlinks from the target objects
277 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
279 struct ldb_dn *target_dn, *source_dn;
281 struct ldb_context *ldb = ldb_module_get_ctx(module);
282 struct ldb_message *msg;
283 TALLOC_CTX *tmp_ctx = talloc_new(bl);
289 - construct ldb_message
290 - either an add or a delete
292 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn, parent);
293 if (ret != LDB_SUCCESS) {
294 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
295 GUID_string(bl, &bl->target_guid)));
299 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn, parent);
300 if (ret != LDB_SUCCESS) {
301 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
302 GUID_string(bl, &bl->forward_guid));
303 talloc_free(tmp_ctx);
307 msg = ldb_msg_new(tmp_ctx);
309 ldb_module_oom(module);
310 talloc_free(tmp_ctx);
311 return LDB_ERR_OPERATIONS_ERROR;
314 /* construct a ldb_message for adding/deleting the backlink */
316 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
318 ldb_module_oom(module);
319 talloc_free(tmp_ctx);
320 return LDB_ERR_OPERATIONS_ERROR;
322 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
323 if (ret != LDB_SUCCESS) {
324 talloc_free(tmp_ctx);
327 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
329 /* a backlink should never be single valued. Unfortunately the
330 exchange schema has a attribute
331 msExchBridgeheadedLocalConnectorsDNBL which is single
332 valued and a backlink. We need to cope with that by
333 ignoring the single value flag */
334 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
336 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
337 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
338 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
339 cope with possible corruption where the backlink has
340 already been removed */
341 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
342 ldb_dn_get_linearized(target_dn),
343 ldb_dn_get_linearized(source_dn),
344 ldb_errstring(ldb)));
346 } else if (ret != LDB_SUCCESS) {
347 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
348 bl->active?"add":"remove",
349 ldb_dn_get_linearized(source_dn),
350 ldb_dn_get_linearized(target_dn),
352 talloc_free(tmp_ctx);
355 talloc_free(tmp_ctx);
360 add a backlink to the list of backlinks to add/delete in the prepare
363 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
364 struct GUID *forward_guid, struct GUID *target_guid,
365 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
367 const struct dsdb_attribute *target_attr;
368 struct la_backlink *bl;
369 struct replmd_private *replmd_private =
370 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
372 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
375 * windows 2003 has a broken schema where the
376 * definition of msDS-IsDomainFor is missing (which is
377 * supposed to be the backlink of the
378 * msDS-HasDomainNCs attribute
383 /* see if its already in the list */
384 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
385 if (GUID_equal(forward_guid, &bl->forward_guid) &&
386 GUID_equal(target_guid, &bl->target_guid) &&
387 (target_attr->lDAPDisplayName == bl->attr_name ||
388 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
394 /* we found an existing one */
395 if (bl->active == active) {
398 DLIST_REMOVE(replmd_private->la_backlinks, bl);
403 if (replmd_private->bl_ctx == NULL) {
404 replmd_private->bl_ctx = talloc_new(replmd_private);
405 if (replmd_private->bl_ctx == NULL) {
406 ldb_module_oom(module);
407 return LDB_ERR_OPERATIONS_ERROR;
412 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
414 ldb_module_oom(module);
415 return LDB_ERR_OPERATIONS_ERROR;
418 /* Ensure the schema does not go away before the bl->attr_name is used */
419 if (!talloc_reference(bl, schema)) {
421 ldb_module_oom(module);
422 return LDB_ERR_OPERATIONS_ERROR;
425 bl->attr_name = target_attr->lDAPDisplayName;
426 bl->forward_guid = *forward_guid;
427 bl->target_guid = *target_guid;
430 /* the caller may ask for this backlink to be processed
433 int ret = replmd_process_backlink(module, bl, NULL);
438 DLIST_ADD(replmd_private->la_backlinks, bl);
445 * Callback for most write operations in this module:
447 * notify the repl task that a object has changed. The notifies are
448 * gathered up in the replmd_private structure then written to the
449 * @REPLCHANGED object in each partition during the prepare_commit
451 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
454 struct replmd_replicated_request *ac =
455 talloc_get_type_abort(req->context, struct replmd_replicated_request);
456 struct replmd_private *replmd_private =
457 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
458 struct nc_entry *modified_partition;
459 struct ldb_control *partition_ctrl;
460 const struct dsdb_control_current_partition *partition;
462 struct ldb_control **controls;
464 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
466 controls = ares->controls;
467 if (ldb_request_get_control(ac->req,
468 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
470 * Remove the current partition control from what we pass up
471 * the chain if it hasn't been requested manually.
473 controls = ldb_controls_except_specified(ares->controls, ares,
477 if (ares->error != LDB_SUCCESS) {
478 DEBUG(5,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
479 return ldb_module_done(ac->req, controls,
480 ares->response, ares->error);
483 if (ares->type != LDB_REPLY_DONE) {
484 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
485 return ldb_module_done(ac->req, NULL,
486 NULL, LDB_ERR_OPERATIONS_ERROR);
489 if (!partition_ctrl) {
490 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
491 return ldb_module_done(ac->req, NULL,
492 NULL, LDB_ERR_OPERATIONS_ERROR);
495 partition = talloc_get_type_abort(partition_ctrl->data,
496 struct dsdb_control_current_partition);
498 if (ac->seq_num > 0) {
499 for (modified_partition = replmd_private->ncs; modified_partition;
500 modified_partition = modified_partition->next) {
501 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
506 if (modified_partition == NULL) {
507 modified_partition = talloc_zero(replmd_private, struct nc_entry);
508 if (!modified_partition) {
509 ldb_oom(ldb_module_get_ctx(ac->module));
510 return ldb_module_done(ac->req, NULL,
511 NULL, LDB_ERR_OPERATIONS_ERROR);
513 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
514 if (!modified_partition->dn) {
515 ldb_oom(ldb_module_get_ctx(ac->module));
516 return ldb_module_done(ac->req, NULL,
517 NULL, LDB_ERR_OPERATIONS_ERROR);
519 DLIST_ADD(replmd_private->ncs, modified_partition);
522 if (ac->seq_num > modified_partition->mod_usn) {
523 modified_partition->mod_usn = ac->seq_num;
525 modified_partition->mod_usn_urgent = ac->seq_num;
530 if (ac->apply_mode) {
531 ret = replmd_replicated_apply_isDeleted(ac);
532 if (ret != LDB_SUCCESS) {
533 return ldb_module_done(ac->req, NULL, NULL, ret);
537 /* free the partition control container here, for the
538 * common path. Other cases will have it cleaned up
539 * eventually with the ares */
540 talloc_free(partition_ctrl);
541 return ldb_module_done(ac->req, controls,
542 ares->response, LDB_SUCCESS);
548 * update a @REPLCHANGED record in each partition if there have been
549 * any writes of replicated data in the partition
551 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
553 struct replmd_private *replmd_private =
554 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
556 while (replmd_private->ncs) {
558 struct nc_entry *modified_partition = replmd_private->ncs;
560 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
561 modified_partition->mod_usn,
562 modified_partition->mod_usn_urgent, parent);
563 if (ret != LDB_SUCCESS) {
564 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
565 ldb_dn_get_linearized(modified_partition->dn)));
568 DLIST_REMOVE(replmd_private->ncs, modified_partition);
569 talloc_free(modified_partition);
577 created a replmd_replicated_request context
579 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
580 struct ldb_request *req)
582 struct ldb_context *ldb;
583 struct replmd_replicated_request *ac;
584 const struct GUID *our_invocation_id;
586 ldb = ldb_module_get_ctx(module);
588 ac = talloc_zero(req, struct replmd_replicated_request);
597 ac->schema = dsdb_get_schema(ldb, ac);
599 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
600 "replmd_modify: no dsdb_schema loaded");
601 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
606 /* get our invocationId */
607 our_invocation_id = samdb_ntds_invocation_id(ldb);
608 if (!our_invocation_id) {
609 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
610 "replmd_add: unable to find invocationId\n");
614 ac->our_invocation_id = *our_invocation_id;
620 add a time element to a record
622 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
624 struct ldb_message_element *el;
628 if (ldb_msg_find_element(msg, attr) != NULL) {
632 s = ldb_timestring(msg, t);
634 return LDB_ERR_OPERATIONS_ERROR;
637 ret = ldb_msg_add_string(msg, attr, s);
638 if (ret != LDB_SUCCESS) {
642 el = ldb_msg_find_element(msg, attr);
643 /* always set as replace. This works because on add ops, the flag
645 el->flags = LDB_FLAG_MOD_REPLACE;
651 add a uint64_t element to a record
653 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
654 const char *attr, uint64_t v)
656 struct ldb_message_element *el;
659 if (ldb_msg_find_element(msg, attr) != NULL) {
663 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
664 if (ret != LDB_SUCCESS) {
668 el = ldb_msg_find_element(msg, attr);
669 /* always set as replace. This works because on add ops, the flag
671 el->flags = LDB_FLAG_MOD_REPLACE;
676 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
677 const struct replPropertyMetaData1 *m2,
678 const uint32_t *rdn_attid)
681 * This assignment seems inoccous, but it is critical for the
682 * system, as we need to do the comparisons as a unsigned
683 * quantity, not signed (enums are signed integers)
685 uint32_t attid_1 = m1->attid;
686 uint32_t attid_2 = m2->attid;
688 if (attid_1 == attid_2) {
693 * the rdn attribute should be at the end!
694 * so we need to return a value greater than zero
695 * which means m1 is greater than m2
697 if (attid_1 == *rdn_attid) {
702 * the rdn attribute should be at the end!
703 * so we need to return a value less than zero
704 * which means m2 is greater than m1
706 if (attid_2 == *rdn_attid) {
711 * See above regarding this being an unsigned comparison.
712 * Otherwise when the high bit is set on non-standard
713 * attributes, they would end up first, before objectClass
716 return attid_1 > attid_2 ? 1 : -1;
719 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
720 struct replPropertyMetaDataCtr1 *ctr1,
721 const struct dsdb_attribute *rdn_sa,
724 if (ctr1->count == 0) {
725 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
726 "No elements found in replPropertyMetaData for %s!\n",
727 ldb_dn_get_linearized(dn));
728 return LDB_ERR_CONSTRAINT_VIOLATION;
731 /* the objectClass attribute is value 0x00000000, so must be first */
732 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
733 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
734 "No objectClass found in replPropertyMetaData for %s!\n",
735 ldb_dn_get_linearized(dn));
736 return LDB_ERR_OBJECT_CLASS_VIOLATION;
742 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
743 struct replPropertyMetaDataCtr1 *ctr1,
744 const struct dsdb_schema *schema,
747 const char *rdn_name;
748 const struct dsdb_attribute *rdn_sa;
750 rdn_name = ldb_dn_get_rdn_name(dn);
752 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
753 __location__ ": No rDN for %s?\n",
754 ldb_dn_get_linearized(dn));
755 return LDB_ERR_INVALID_DN_SYNTAX;
758 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
759 if (rdn_sa == NULL) {
760 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
761 __location__ ": No sa found for rDN %s for %s\n",
762 rdn_name, ldb_dn_get_linearized(dn));
763 return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
766 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
767 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
769 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id,
770 replmd_replPropertyMetaData1_attid_sort);
771 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, rdn_sa, dn);
774 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
775 const struct ldb_message_element *e2,
776 const struct dsdb_schema *schema)
778 const struct dsdb_attribute *a1;
779 const struct dsdb_attribute *a2;
782 * TODO: make this faster by caching the dsdb_attribute pointer
783 * on the ldb_messag_element
786 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
787 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
790 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
794 return strcasecmp(e1->name, e2->name);
796 if (a1->attributeID_id == a2->attributeID_id) {
799 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
802 static void replmd_ldb_message_sort(struct ldb_message *msg,
803 const struct dsdb_schema *schema)
805 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
808 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
809 const struct GUID *invocation_id, uint64_t seq_num,
810 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
814 fix up linked attributes in replmd_add.
815 This involves setting up the right meta-data in extended DN
816 components, and creating backlinks to the object
818 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
819 uint64_t seq_num, const struct GUID *invocationId, time_t t,
820 struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent)
823 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
824 struct ldb_context *ldb = ldb_module_get_ctx(module);
826 /* We will take a reference to the schema in replmd_add_backlink */
827 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
830 unix_to_nt_time(&now, t);
832 for (i=0; i<el->num_values; i++) {
833 struct ldb_val *v = &el->values[i];
834 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
835 struct GUID target_guid;
839 /* note that the DN already has the extended
840 components from the extended_dn_store module */
841 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
842 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
843 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid, parent);
844 if (ret != LDB_SUCCESS) {
845 talloc_free(tmp_ctx);
848 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
849 if (ret != LDB_SUCCESS) {
850 talloc_free(tmp_ctx);
855 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
856 seq_num, seq_num, now, 0, false);
857 if (ret != LDB_SUCCESS) {
858 talloc_free(tmp_ctx);
862 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
863 if (ret != LDB_SUCCESS) {
864 talloc_free(tmp_ctx);
869 talloc_free(tmp_ctx);
875 intercept add requests
877 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
879 struct samldb_msds_intid_persistant *msds_intid_struct;
880 struct ldb_context *ldb;
881 struct ldb_control *control;
882 struct replmd_replicated_request *ac;
883 enum ndr_err_code ndr_err;
884 struct ldb_request *down_req;
885 struct ldb_message *msg;
886 const DATA_BLOB *guid_blob;
888 struct replPropertyMetaDataBlob nmd;
889 struct ldb_val nmd_value;
892 * The use of a time_t here seems odd, but as the NTTIME
893 * elements are actually declared as NTTIME_1sec in the IDL,
894 * getting a higher resolution timestamp is not required.
896 time_t t = time(NULL);
901 unsigned int functional_level;
903 bool allow_add_guid = false;
904 bool remove_current_guid = false;
905 bool is_urgent = false;
906 bool is_schema_nc = false;
907 struct ldb_message_element *objectclass_el;
908 struct replmd_private *replmd_private =
909 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
911 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
912 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
914 allow_add_guid = true;
917 /* do not manipulate our control entries */
918 if (ldb_dn_is_special(req->op.add.message->dn)) {
919 return ldb_next_request(module, req);
922 ldb = ldb_module_get_ctx(module);
924 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
926 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
927 if (guid_blob != NULL) {
928 if (!allow_add_guid) {
929 ldb_set_errstring(ldb,
930 "replmd_add: it's not allowed to add an object with objectGUID!");
931 return LDB_ERR_UNWILLING_TO_PERFORM;
933 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
934 if (!NT_STATUS_IS_OK(status)) {
935 ldb_set_errstring(ldb,
936 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
937 return LDB_ERR_UNWILLING_TO_PERFORM;
939 /* we remove this attribute as it can be a string and
940 * will not be treated correctly and then we will re-add
941 * it later on in the good format */
942 remove_current_guid = true;
946 guid = GUID_random();
949 ac = replmd_ctx_init(module, req);
951 return ldb_module_oom(module);
954 functional_level = dsdb_functional_level(ldb);
956 /* Get a sequence number from the backend */
957 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
958 if (ret != LDB_SUCCESS) {
963 /* we have to copy the message as the caller might have it as a const */
964 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
968 return LDB_ERR_OPERATIONS_ERROR;
971 /* generated times */
972 unix_to_nt_time(&now, t);
973 time_str = ldb_timestring(msg, t);
977 return LDB_ERR_OPERATIONS_ERROR;
979 if (remove_current_guid) {
980 ldb_msg_remove_attr(msg,"objectGUID");
984 * remove autogenerated attributes
986 ldb_msg_remove_attr(msg, "whenCreated");
987 ldb_msg_remove_attr(msg, "whenChanged");
988 ldb_msg_remove_attr(msg, "uSNCreated");
989 ldb_msg_remove_attr(msg, "uSNChanged");
990 ldb_msg_remove_attr(msg, "replPropertyMetaData");
993 * readd replicated attributes
995 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
996 if (ret != LDB_SUCCESS) {
1002 /* build the replication meta_data */
1005 nmd.ctr.ctr1.count = msg->num_elements;
1006 nmd.ctr.ctr1.array = talloc_array(msg,
1007 struct replPropertyMetaData1,
1008 nmd.ctr.ctr1.count);
1009 if (!nmd.ctr.ctr1.array) {
1012 return LDB_ERR_OPERATIONS_ERROR;
1015 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1017 for (i=0; i < msg->num_elements; i++) {
1018 struct ldb_message_element *e = &msg->elements[i];
1019 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1020 const struct dsdb_attribute *sa;
1022 if (e->name[0] == '@') continue;
1024 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1026 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1027 "replmd_add: attribute '%s' not defined in schema\n",
1030 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1033 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1034 /* if the attribute is not replicated (0x00000001)
1035 * or constructed (0x00000004) it has no metadata
1040 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1041 ret = replmd_add_fix_la(module, e, ac->seq_num, &ac->our_invocation_id, t, &guid, sa, req);
1042 if (ret != LDB_SUCCESS) {
1046 /* linked attributes are not stored in
1047 replPropertyMetaData in FL above w2k */
1051 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1053 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1054 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1057 if (rdn_val == NULL) {
1060 return LDB_ERR_OPERATIONS_ERROR;
1063 rdn = (const char*)rdn_val->data;
1064 if (strcmp(rdn, "Deleted Objects") == 0) {
1066 * Set the originating_change_time to 29/12/9999 at 23:59:59
1067 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1069 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1071 m->originating_change_time = now;
1074 m->originating_change_time = now;
1076 m->originating_invocation_id = ac->our_invocation_id;
1077 m->originating_usn = ac->seq_num;
1078 m->local_usn = ac->seq_num;
1082 /* fix meta data count */
1083 nmd.ctr.ctr1.count = ni;
1086 * sort meta data array, and move the rdn attribute entry to the end
1088 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, ac->schema, msg->dn);
1089 if (ret != LDB_SUCCESS) {
1090 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1095 /* generated NDR encoded values */
1096 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1098 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1099 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1102 return LDB_ERR_OPERATIONS_ERROR;
1106 * add the autogenerated values
1108 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1109 if (ret != LDB_SUCCESS) {
1114 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1115 if (ret != LDB_SUCCESS) {
1120 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1121 if (ret != LDB_SUCCESS) {
1126 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1127 if (ret != LDB_SUCCESS) {
1132 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1133 if (ret != LDB_SUCCESS) {
1140 * sort the attributes by attid before storing the object
1142 replmd_ldb_message_sort(msg, ac->schema);
1145 * Assert that we do have an objectClass
1147 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1148 if (objectclass_el == NULL) {
1149 ldb_asprintf_errstring(ldb, __location__
1150 ": objectClass missing on %s\n",
1151 ldb_dn_get_linearized(msg->dn));
1153 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1155 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1156 REPL_URGENT_ON_CREATE);
1158 ac->is_urgent = is_urgent;
1159 ret = ldb_build_add_req(&down_req, ldb, ac,
1162 ac, replmd_op_callback,
1165 LDB_REQ_SET_LOCATION(down_req);
1166 if (ret != LDB_SUCCESS) {
1171 /* current partition control is needed by "replmd_op_callback" */
1172 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1173 ret = ldb_request_add_control(down_req,
1174 DSDB_CONTROL_CURRENT_PARTITION_OID,
1176 if (ret != LDB_SUCCESS) {
1182 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1183 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1184 if (ret != LDB_SUCCESS) {
1190 /* mark the control done */
1192 control->critical = 0;
1194 if (ldb_dn_compare_base(replmd_private->schema_dn, req->op.add.message->dn) != 0) {
1196 /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
1197 msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
1198 if (msds_intid_struct) {
1199 msds_intid_struct->usn = ac->seq_num;
1202 /* go on with the call chain */
1203 return ldb_next_request(module, down_req);
1208 * update the replPropertyMetaData for one element
1210 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1211 struct ldb_message *msg,
1212 struct ldb_message_element *el,
1213 struct ldb_message_element *old_el,
1214 struct replPropertyMetaDataBlob *omd,
1215 const struct dsdb_schema *schema,
1217 const struct GUID *our_invocation_id,
1220 struct ldb_request *req)
1223 const struct dsdb_attribute *a;
1224 struct replPropertyMetaData1 *md1;
1225 bool may_skip = false;
1228 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1230 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1231 /* allow this to make it possible for dbcheck
1232 to remove bad attributes */
1236 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1238 return LDB_ERR_OPERATIONS_ERROR;
1241 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1243 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1248 * if the attribute's value haven't changed, and this isn't
1249 * just a delete of everything then return LDB_SUCCESS Unless
1250 * we have the provision control or if the attribute is
1251 * interSiteTopologyGenerator as this page explain:
1252 * http://support.microsoft.com/kb/224815 this attribute is
1253 * periodicaly written by the DC responsible for the intersite
1254 * generation in a given site
1256 * Unchanged could be deleting or replacing an already-gone
1257 * thing with an unconstrained delete/empty replace or a
1258 * replace with the same value, but not an add with the same
1259 * value because that could be about adding a duplicate (which
1260 * is for someone else to error out on).
1262 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1263 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1266 } else if (old_el == NULL && el->num_values == 0) {
1267 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1269 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1275 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1276 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1278 * allow this to make it possible for dbcheck
1279 * to rebuild broken metadata
1285 for (i=0; i<omd->ctr.ctr1.count; i++) {
1287 * First check if we find it under the msDS-IntID,
1288 * then check if we find it under the OID and
1291 * This allows the administrator to simply re-write
1292 * the attributes and so restore replication, which is
1293 * likely what they will try to do.
1295 if (attid == omd->ctr.ctr1.array[i].attid) {
1299 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1304 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1305 /* linked attributes are not stored in
1306 replPropertyMetaData in FL above w2k, but we do
1307 raise the seqnum for the object */
1308 if (*seq_num == 0 &&
1309 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1310 return LDB_ERR_OPERATIONS_ERROR;
1315 if (i == omd->ctr.ctr1.count) {
1316 /* we need to add a new one */
1317 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1318 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1319 if (omd->ctr.ctr1.array == NULL) {
1321 return LDB_ERR_OPERATIONS_ERROR;
1323 omd->ctr.ctr1.count++;
1324 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1327 /* Get a new sequence number from the backend. We only do this
1328 * if we have a change that requires a new
1329 * replPropertyMetaData element
1331 if (*seq_num == 0) {
1332 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1333 if (ret != LDB_SUCCESS) {
1334 return LDB_ERR_OPERATIONS_ERROR;
1338 md1 = &omd->ctr.ctr1.array[i];
1341 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1342 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1345 if (rdn_val == NULL) {
1347 return LDB_ERR_OPERATIONS_ERROR;
1350 rdn = (const char*)rdn_val->data;
1351 if (strcmp(rdn, "Deleted Objects") == 0) {
1353 * Set the originating_change_time to 29/12/9999 at 23:59:59
1354 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1356 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1358 md1->originating_change_time = now;
1361 md1->originating_change_time = now;
1363 md1->originating_invocation_id = *our_invocation_id;
1364 md1->originating_usn = *seq_num;
1365 md1->local_usn = *seq_num;
1370 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1372 uint32_t count = omd.ctr.ctr1.count;
1375 for (i=0; i < count; i++) {
1376 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1377 if (max < m.local_usn) {
1385 * update the replPropertyMetaData object each time we modify an
1386 * object. This is needed for DRS replication, as the merge on the
1387 * client is based on this object
1389 static int replmd_update_rpmd(struct ldb_module *module,
1390 const struct dsdb_schema *schema,
1391 struct ldb_request *req,
1392 const char * const *rename_attrs,
1393 struct ldb_message *msg, uint64_t *seq_num,
1394 time_t t, bool is_schema_nc,
1395 bool *is_urgent, bool *rodc)
1397 const struct ldb_val *omd_value;
1398 enum ndr_err_code ndr_err;
1399 struct replPropertyMetaDataBlob omd;
1402 const struct GUID *our_invocation_id;
1404 const char * const *attrs = NULL;
1405 const char * const attrs1[] = { "replPropertyMetaData", "*", NULL };
1406 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1407 struct ldb_result *res;
1408 struct ldb_context *ldb;
1409 struct ldb_message_element *objectclass_el;
1410 enum urgent_situation situation;
1411 bool rmd_is_provided;
1412 bool rmd_is_just_resorted = false;
1415 attrs = rename_attrs;
1420 ldb = ldb_module_get_ctx(module);
1422 our_invocation_id = samdb_ntds_invocation_id(ldb);
1423 if (!our_invocation_id) {
1424 /* this happens during an initial vampire while
1425 updating the schema */
1426 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1430 unix_to_nt_time(&now, t);
1432 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1433 rmd_is_provided = true;
1434 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1435 rmd_is_just_resorted = true;
1438 rmd_is_provided = false;
1441 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1442 * otherwise we consider we are updating */
1443 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1444 situation = REPL_URGENT_ON_DELETE;
1445 } else if (rename_attrs) {
1446 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1448 situation = REPL_URGENT_ON_UPDATE;
1451 if (rmd_is_provided) {
1452 /* In this case the change_replmetadata control was supplied */
1453 /* We check that it's the only attribute that is provided
1454 * (it's a rare case so it's better to keep the code simplier)
1455 * We also check that the highest local_usn is bigger or the same as
1458 if( msg->num_elements != 1 ||
1459 strncmp(msg->elements[0].name,
1460 "replPropertyMetaData", 20) ) {
1461 DEBUG(0,(__location__ ": changereplmetada control called without "\
1462 "a specified replPropertyMetaData attribute or with others\n"));
1463 return LDB_ERR_OPERATIONS_ERROR;
1465 if (situation != REPL_URGENT_ON_UPDATE) {
1466 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1467 return LDB_ERR_OPERATIONS_ERROR;
1469 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1471 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1472 ldb_dn_get_linearized(msg->dn)));
1473 return LDB_ERR_OPERATIONS_ERROR;
1475 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1476 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1477 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1478 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1479 ldb_dn_get_linearized(msg->dn)));
1480 return LDB_ERR_OPERATIONS_ERROR;
1483 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1484 DSDB_FLAG_NEXT_MODULE |
1485 DSDB_SEARCH_SHOW_RECYCLED |
1486 DSDB_SEARCH_SHOW_EXTENDED_DN |
1487 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1488 DSDB_SEARCH_REVEAL_INTERNALS, req);
1490 if (ret != LDB_SUCCESS) {
1494 if (rmd_is_just_resorted == false) {
1495 *seq_num = find_max_local_usn(omd);
1497 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1500 * The test here now allows for a new
1501 * replPropertyMetaData with no change, if was
1502 * just dbcheck re-sorting the values.
1504 if (*seq_num <= db_seq) {
1505 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1506 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1507 (long long)*seq_num, (long long)db_seq));
1508 return LDB_ERR_OPERATIONS_ERROR;
1513 /* search for the existing replPropertyMetaDataBlob. We need
1514 * to use REVEAL and ask for DNs in storage format to support
1515 * the check for values being the same in
1516 * replmd_update_rpmd_element()
1518 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1519 DSDB_FLAG_NEXT_MODULE |
1520 DSDB_SEARCH_SHOW_RECYCLED |
1521 DSDB_SEARCH_SHOW_EXTENDED_DN |
1522 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1523 DSDB_SEARCH_REVEAL_INTERNALS, req);
1524 if (ret != LDB_SUCCESS) {
1528 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1530 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1531 ldb_dn_get_linearized(msg->dn)));
1532 return LDB_ERR_OPERATIONS_ERROR;
1535 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1536 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1537 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1538 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1539 ldb_dn_get_linearized(msg->dn)));
1540 return LDB_ERR_OPERATIONS_ERROR;
1543 if (omd.version != 1) {
1544 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1545 omd.version, ldb_dn_get_linearized(msg->dn)));
1546 return LDB_ERR_OPERATIONS_ERROR;
1549 for (i=0; i<msg->num_elements; i++) {
1550 struct ldb_message_element *old_el;
1551 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1552 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1556 if (ret != LDB_SUCCESS) {
1560 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1561 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1568 * Assert that we have an objectClass attribute - this is major
1569 * corruption if we don't have this!
1571 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1572 if (objectclass_el != NULL) {
1574 * Now check if this objectClass means we need to do urgent replication
1576 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1580 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1581 ldb_asprintf_errstring(ldb, __location__
1582 ": objectClass missing on %s\n",
1583 ldb_dn_get_linearized(msg->dn));
1584 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1588 * replmd_update_rpmd_element has done an update if the
1591 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1592 struct ldb_val *md_value;
1593 struct ldb_message_element *el;
1595 /*if we are RODC and this is a DRSR update then its ok*/
1596 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1597 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)) {
1598 unsigned instanceType;
1600 ret = samdb_rodc(ldb, rodc);
1601 if (ret != LDB_SUCCESS) {
1602 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1604 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1605 return LDB_ERR_REFERRAL;
1608 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1609 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1610 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1611 "cannot change replicated attribute on partial replica");
1615 md_value = talloc(msg, struct ldb_val);
1616 if (md_value == NULL) {
1618 return LDB_ERR_OPERATIONS_ERROR;
1621 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, schema, msg->dn);
1622 if (ret != LDB_SUCCESS) {
1623 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
1627 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1628 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1629 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1630 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1631 ldb_dn_get_linearized(msg->dn)));
1632 return LDB_ERR_OPERATIONS_ERROR;
1635 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1636 if (ret != LDB_SUCCESS) {
1637 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1638 ldb_dn_get_linearized(msg->dn)));
1643 el->values = md_value;
1650 struct dsdb_dn *dsdb_dn;
1655 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1657 return GUID_compare(pdn1->guid, pdn2->guid);
1660 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
1661 unsigned int count, struct GUID *guid,
1664 struct parsed_dn *ret;
1666 if (dn && GUID_all_zero(guid)) {
1667 /* when updating a link using DRS, we sometimes get a
1668 NULL GUID. We then need to try and match by DN */
1669 for (i=0; i<count; i++) {
1670 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1671 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1677 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1682 get a series of message element values as an array of DNs and GUIDs
1683 the result is sorted by GUID
1685 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1686 struct ldb_message_element *el, struct parsed_dn **pdn,
1687 const char *ldap_oid, struct ldb_request *parent)
1690 struct ldb_context *ldb = ldb_module_get_ctx(module);
1697 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1699 ldb_module_oom(module);
1700 return LDB_ERR_OPERATIONS_ERROR;
1703 for (i=0; i<el->num_values; i++) {
1704 struct ldb_val *v = &el->values[i];
1707 struct parsed_dn *p;
1711 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1712 if (p->dsdb_dn == NULL) {
1713 return LDB_ERR_INVALID_DN_SYNTAX;
1716 dn = p->dsdb_dn->dn;
1718 p->guid = talloc(*pdn, struct GUID);
1719 if (p->guid == NULL) {
1720 ldb_module_oom(module);
1721 return LDB_ERR_OPERATIONS_ERROR;
1724 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1725 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1726 /* we got a DN without a GUID - go find the GUID */
1727 int ret = dsdb_module_guid_by_dn(module, dn, p->guid, parent);
1728 if (ret != LDB_SUCCESS) {
1729 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1730 ldb_dn_get_linearized(dn));
1731 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
1732 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1733 ldb_attr_cmp(el->name, "member") == 0) {
1734 return LDB_ERR_UNWILLING_TO_PERFORM;
1738 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1739 if (ret != LDB_SUCCESS) {
1742 } else if (!NT_STATUS_IS_OK(status)) {
1743 return LDB_ERR_OPERATIONS_ERROR;
1746 /* keep a pointer to the original ldb_val */
1750 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1756 build a new extended DN, including all meta data fields
1758 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1759 RMD_ADDTIME = originating_add_time
1760 RMD_INVOCID = originating_invocation_id
1761 RMD_CHANGETIME = originating_change_time
1762 RMD_ORIGINATING_USN = originating_usn
1763 RMD_LOCAL_USN = local_usn
1764 RMD_VERSION = version
1766 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1767 const struct GUID *invocation_id, uint64_t seq_num,
1768 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1770 struct ldb_dn *dn = dsdb_dn->dn;
1771 const char *tstring, *usn_string, *flags_string;
1772 struct ldb_val tval;
1774 struct ldb_val usnv, local_usnv;
1775 struct ldb_val vers, flagsv;
1778 const char *dnstring;
1780 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1782 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1784 return LDB_ERR_OPERATIONS_ERROR;
1786 tval = data_blob_string_const(tstring);
1788 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1790 return LDB_ERR_OPERATIONS_ERROR;
1792 usnv = data_blob_string_const(usn_string);
1794 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1796 return LDB_ERR_OPERATIONS_ERROR;
1798 local_usnv = data_blob_string_const(usn_string);
1800 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1802 return LDB_ERR_OPERATIONS_ERROR;
1804 vers = data_blob_string_const(vstring);
1806 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1807 if (!NT_STATUS_IS_OK(status)) {
1808 return LDB_ERR_OPERATIONS_ERROR;
1811 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1812 if (!flags_string) {
1813 return LDB_ERR_OPERATIONS_ERROR;
1815 flagsv = data_blob_string_const(flags_string);
1817 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1818 if (ret != LDB_SUCCESS) return ret;
1819 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1820 if (ret != LDB_SUCCESS) return ret;
1821 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1822 if (ret != LDB_SUCCESS) return ret;
1823 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1824 if (ret != LDB_SUCCESS) return ret;
1825 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1826 if (ret != LDB_SUCCESS) return ret;
1827 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1828 if (ret != LDB_SUCCESS) return ret;
1829 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1830 if (ret != LDB_SUCCESS) return ret;
1832 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1833 if (dnstring == NULL) {
1834 return LDB_ERR_OPERATIONS_ERROR;
1836 *v = data_blob_string_const(dnstring);
1841 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1842 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1843 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1844 uint32_t version, bool deleted);
1847 check if any links need upgrading from w2k format
1849 The parent_ctx is the ldb_message_element which contains the values array that dns[i].v points at, and which should be used for allocating any new value.
1851 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1854 for (i=0; i<count; i++) {
1859 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1860 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1864 /* it's an old one that needs upgrading */
1865 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1867 if (ret != LDB_SUCCESS) {
1875 update an extended DN, including all meta data fields
1877 see replmd_build_la_val for value names
1879 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1880 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1881 uint64_t usn, uint64_t local_usn, NTTIME nttime,
1882 uint32_t version, bool deleted)
1884 struct ldb_dn *dn = dsdb_dn->dn;
1885 const char *tstring, *usn_string, *flags_string;
1886 struct ldb_val tval;
1888 struct ldb_val usnv, local_usnv;
1889 struct ldb_val vers, flagsv;
1890 const struct ldb_val *old_addtime;
1891 uint32_t old_version;
1894 const char *dnstring;
1896 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1898 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1900 return LDB_ERR_OPERATIONS_ERROR;
1902 tval = data_blob_string_const(tstring);
1904 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
1906 return LDB_ERR_OPERATIONS_ERROR;
1908 usnv = data_blob_string_const(usn_string);
1910 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1912 return LDB_ERR_OPERATIONS_ERROR;
1914 local_usnv = data_blob_string_const(usn_string);
1916 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1917 if (!NT_STATUS_IS_OK(status)) {
1918 return LDB_ERR_OPERATIONS_ERROR;
1921 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1922 if (!flags_string) {
1923 return LDB_ERR_OPERATIONS_ERROR;
1925 flagsv = data_blob_string_const(flags_string);
1927 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1928 if (ret != LDB_SUCCESS) return ret;
1930 /* get the ADDTIME from the original */
1931 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1932 if (old_addtime == NULL) {
1933 old_addtime = &tval;
1935 if (dsdb_dn != old_dsdb_dn ||
1936 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
1937 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1938 if (ret != LDB_SUCCESS) return ret;
1941 /* use our invocation id */
1942 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1943 if (ret != LDB_SUCCESS) return ret;
1945 /* changetime is the current time */
1946 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1947 if (ret != LDB_SUCCESS) return ret;
1949 /* update the USN */
1950 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1951 if (ret != LDB_SUCCESS) return ret;
1953 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1954 if (ret != LDB_SUCCESS) return ret;
1956 /* increase the version by 1 */
1957 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1958 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1959 version = old_version+1;
1961 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1962 vers = data_blob_string_const(vstring);
1963 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1964 if (ret != LDB_SUCCESS) return ret;
1966 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1967 if (dnstring == NULL) {
1968 return LDB_ERR_OPERATIONS_ERROR;
1970 *v = data_blob_string_const(dnstring);
1976 handle adding a linked attribute
1978 static int replmd_modify_la_add(struct ldb_module *module,
1979 const struct dsdb_schema *schema,
1980 struct ldb_message *msg,
1981 struct ldb_message_element *el,
1982 struct ldb_message_element *old_el,
1983 const struct dsdb_attribute *schema_attr,
1986 struct GUID *msg_guid,
1987 struct ldb_request *parent)
1990 struct parsed_dn *dns, *old_dns;
1991 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1993 struct ldb_val *new_values = NULL;
1994 unsigned int num_new_values = 0;
1995 unsigned old_num_values = old_el?old_el->num_values:0;
1996 const struct GUID *invocation_id;
1997 struct ldb_context *ldb = ldb_module_get_ctx(module);
2000 unix_to_nt_time(&now, t);
2002 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2003 if (ret != LDB_SUCCESS) {
2004 talloc_free(tmp_ctx);
2008 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2009 if (ret != LDB_SUCCESS) {
2010 talloc_free(tmp_ctx);
2014 invocation_id = samdb_ntds_invocation_id(ldb);
2015 if (!invocation_id) {
2016 talloc_free(tmp_ctx);
2017 return LDB_ERR_OPERATIONS_ERROR;
2020 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2021 if (ret != LDB_SUCCESS) {
2022 talloc_free(tmp_ctx);
2026 /* for each new value, see if it exists already with the same GUID */
2027 for (i=0; i<el->num_values; i++) {
2028 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
2030 /* this is a new linked attribute value */
2031 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
2032 if (new_values == NULL) {
2033 ldb_module_oom(module);
2034 talloc_free(tmp_ctx);
2035 return LDB_ERR_OPERATIONS_ERROR;
2037 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2038 invocation_id, seq_num, seq_num, now, 0, false);
2039 if (ret != LDB_SUCCESS) {
2040 talloc_free(tmp_ctx);
2045 /* this is only allowed if the GUID was
2046 previously deleted. */
2047 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2049 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2050 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
2051 el->name, GUID_string(tmp_ctx, p->guid));
2052 talloc_free(tmp_ctx);
2053 /* error codes for 'member' need to be
2055 if (ldb_attr_cmp(el->name, "member") == 0) {
2056 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2058 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2061 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
2062 invocation_id, seq_num, seq_num, now, 0, false);
2063 if (ret != LDB_SUCCESS) {
2064 talloc_free(tmp_ctx);
2069 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
2070 if (ret != LDB_SUCCESS) {
2071 talloc_free(tmp_ctx);
2076 /* add the new ones on to the end of the old values, constructing a new el->values */
2077 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2079 old_num_values+num_new_values);
2080 if (el->values == NULL) {
2081 ldb_module_oom(module);
2082 return LDB_ERR_OPERATIONS_ERROR;
2085 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
2086 el->num_values = old_num_values + num_new_values;
2088 talloc_steal(msg->elements, el->values);
2089 talloc_steal(el->values, new_values);
2091 talloc_free(tmp_ctx);
2093 /* we now tell the backend to replace all existing values
2094 with the one we have constructed */
2095 el->flags = LDB_FLAG_MOD_REPLACE;
2102 handle deleting all active linked attributes
2104 static int replmd_modify_la_delete(struct ldb_module *module,
2105 const struct dsdb_schema *schema,
2106 struct ldb_message *msg,
2107 struct ldb_message_element *el,
2108 struct ldb_message_element *old_el,
2109 const struct dsdb_attribute *schema_attr,
2112 struct GUID *msg_guid,
2113 struct ldb_request *parent)
2116 struct parsed_dn *dns, *old_dns;
2117 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2119 const struct GUID *invocation_id;
2120 struct ldb_context *ldb = ldb_module_get_ctx(module);
2123 unix_to_nt_time(&now, t);
2125 /* check if there is nothing to delete */
2126 if ((!old_el || old_el->num_values == 0) &&
2127 el->num_values == 0) {
2131 if (!old_el || old_el->num_values == 0) {
2132 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2135 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2136 if (ret != LDB_SUCCESS) {
2137 talloc_free(tmp_ctx);
2141 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2142 if (ret != LDB_SUCCESS) {
2143 talloc_free(tmp_ctx);
2147 invocation_id = samdb_ntds_invocation_id(ldb);
2148 if (!invocation_id) {
2149 return LDB_ERR_OPERATIONS_ERROR;
2152 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
2153 if (ret != LDB_SUCCESS) {
2154 talloc_free(tmp_ctx);
2160 /* see if we are being asked to delete any links that
2161 don't exist or are already deleted */
2162 for (i=0; i<el->num_values; i++) {
2163 struct parsed_dn *p = &dns[i];
2164 struct parsed_dn *p2;
2167 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
2169 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
2170 el->name, GUID_string(tmp_ctx, p->guid));
2171 if (ldb_attr_cmp(el->name, "member") == 0) {
2172 return LDB_ERR_UNWILLING_TO_PERFORM;
2174 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2177 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
2178 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2179 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
2180 el->name, GUID_string(tmp_ctx, p->guid));
2181 if (ldb_attr_cmp(el->name, "member") == 0) {
2182 return LDB_ERR_UNWILLING_TO_PERFORM;
2184 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2189 /* for each new value, see if it exists already with the same GUID
2190 if it is not already deleted and matches the delete list then delete it
2192 for (i=0; i<old_el->num_values; i++) {
2193 struct parsed_dn *p = &old_dns[i];
2196 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
2200 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2201 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2203 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
2204 invocation_id, seq_num, seq_num, now, 0, true);
2205 if (ret != LDB_SUCCESS) {
2206 talloc_free(tmp_ctx);
2210 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
2211 if (ret != LDB_SUCCESS) {
2212 talloc_free(tmp_ctx);
2217 el->values = talloc_steal(msg->elements, old_el->values);
2218 el->num_values = old_el->num_values;
2220 talloc_free(tmp_ctx);
2222 /* we now tell the backend to replace all existing values
2223 with the one we have constructed */
2224 el->flags = LDB_FLAG_MOD_REPLACE;
2230 handle replacing a linked attribute
2232 static int replmd_modify_la_replace(struct ldb_module *module,
2233 const struct dsdb_schema *schema,
2234 struct ldb_message *msg,
2235 struct ldb_message_element *el,
2236 struct ldb_message_element *old_el,
2237 const struct dsdb_attribute *schema_attr,
2240 struct GUID *msg_guid,
2241 struct ldb_request *parent)
2244 struct parsed_dn *dns, *old_dns;
2245 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2247 const struct GUID *invocation_id;
2248 struct ldb_context *ldb = ldb_module_get_ctx(module);
2249 struct ldb_val *new_values = NULL;
2250 unsigned int num_new_values = 0;
2251 unsigned int old_num_values = old_el?old_el->num_values:0;
2254 unix_to_nt_time(&now, t);
2256 /* check if there is nothing to replace */
2257 if ((!old_el || old_el->num_values == 0) &&
2258 el->num_values == 0) {
2262 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2263 if (ret != LDB_SUCCESS) {
2264 talloc_free(tmp_ctx);
2268 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2269 if (ret != LDB_SUCCESS) {
2270 talloc_free(tmp_ctx);
2274 invocation_id = samdb_ntds_invocation_id(ldb);
2275 if (!invocation_id) {
2276 return LDB_ERR_OPERATIONS_ERROR;
2279 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2280 if (ret != LDB_SUCCESS) {
2281 talloc_free(tmp_ctx);
2285 /* mark all the old ones as deleted */
2286 for (i=0; i<old_num_values; i++) {
2287 struct parsed_dn *old_p = &old_dns[i];
2288 struct parsed_dn *p;
2289 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2291 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2293 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
2294 if (ret != LDB_SUCCESS) {
2295 talloc_free(tmp_ctx);
2299 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
2301 /* we don't delete it if we are re-adding it */
2305 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
2306 invocation_id, seq_num, seq_num, now, 0, true);
2307 if (ret != LDB_SUCCESS) {
2308 talloc_free(tmp_ctx);
2313 /* for each new value, either update its meta-data, or add it
2316 for (i=0; i<el->num_values; i++) {
2317 struct parsed_dn *p = &dns[i], *old_p;
2320 (old_p = parsed_dn_find(old_dns,
2321 old_num_values, p->guid, NULL)) != NULL) {
2322 /* update in place */
2323 ret = replmd_update_la_val(old_el->values, old_p->v, p->dsdb_dn,
2324 old_p->dsdb_dn, invocation_id,
2325 seq_num, seq_num, now, 0, false);
2326 if (ret != LDB_SUCCESS) {
2327 talloc_free(tmp_ctx);
2332 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
2334 if (new_values == NULL) {
2335 ldb_module_oom(module);
2336 talloc_free(tmp_ctx);
2337 return LDB_ERR_OPERATIONS_ERROR;
2339 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2340 invocation_id, seq_num, seq_num, now, 0, false);
2341 if (ret != LDB_SUCCESS) {
2342 talloc_free(tmp_ctx);
2348 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
2349 if (ret != LDB_SUCCESS) {
2350 talloc_free(tmp_ctx);
2355 /* add the new values to the end of old_el */
2356 if (num_new_values != 0) {
2357 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2358 struct ldb_val, old_num_values+num_new_values);
2359 if (el->values == NULL) {
2360 ldb_module_oom(module);
2361 return LDB_ERR_OPERATIONS_ERROR;
2363 memcpy(&el->values[old_num_values], &new_values[0],
2364 sizeof(struct ldb_val)*num_new_values);
2365 el->num_values = old_num_values + num_new_values;
2366 talloc_steal(msg->elements, new_values);
2368 el->values = old_el->values;
2369 el->num_values = old_el->num_values;
2370 talloc_steal(msg->elements, el->values);
2373 talloc_free(tmp_ctx);
2375 /* we now tell the backend to replace all existing values
2376 with the one we have constructed */
2377 el->flags = LDB_FLAG_MOD_REPLACE;
2384 handle linked attributes in modify requests
2386 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
2387 struct ldb_message *msg,
2388 uint64_t seq_num, time_t t,
2389 struct ldb_request *parent)
2391 struct ldb_result *res;
2394 struct ldb_context *ldb = ldb_module_get_ctx(module);
2395 struct ldb_message *old_msg;
2397 const struct dsdb_schema *schema;
2398 struct GUID old_guid;
2401 /* there the replmd_update_rpmd code has already
2402 * checked and saw that there are no linked
2407 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
2408 /* don't do anything special for linked attributes */
2412 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
2413 DSDB_FLAG_NEXT_MODULE |
2414 DSDB_SEARCH_SHOW_RECYCLED |
2415 DSDB_SEARCH_REVEAL_INTERNALS |
2416 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
2418 if (ret != LDB_SUCCESS) {
2421 schema = dsdb_get_schema(ldb, res);
2423 return LDB_ERR_OPERATIONS_ERROR;
2426 old_msg = res->msgs[0];
2428 old_guid = samdb_result_guid(old_msg, "objectGUID");
2430 for (i=0; i<msg->num_elements; i++) {
2431 struct ldb_message_element *el = &msg->elements[i];
2432 struct ldb_message_element *old_el, *new_el;
2433 const struct dsdb_attribute *schema_attr
2434 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2436 ldb_asprintf_errstring(ldb,
2437 "%s: attribute %s is not a valid attribute in schema",
2438 __FUNCTION__, el->name);
2439 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2441 if (schema_attr->linkID == 0) {
2444 if ((schema_attr->linkID & 1) == 1) {
2445 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
2448 /* Odd is for the target. Illegal to modify */
2449 ldb_asprintf_errstring(ldb,
2450 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2451 return LDB_ERR_UNWILLING_TO_PERFORM;
2453 old_el = ldb_msg_find_element(old_msg, el->name);
2454 switch (el->flags & LDB_FLAG_MOD_MASK) {
2455 case LDB_FLAG_MOD_REPLACE:
2456 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2458 case LDB_FLAG_MOD_DELETE:
2459 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2461 case LDB_FLAG_MOD_ADD:
2462 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2465 ldb_asprintf_errstring(ldb,
2466 "invalid flags 0x%x for %s linked attribute",
2467 el->flags, el->name);
2468 return LDB_ERR_UNWILLING_TO_PERFORM;
2470 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
2471 ldb_asprintf_errstring(ldb,
2472 "Attribute %s is single valued but more than one value has been supplied",
2474 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2476 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
2481 if (ret != LDB_SUCCESS) {
2485 ldb_msg_remove_attr(old_msg, el->name);
2487 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2488 new_el->num_values = el->num_values;
2489 new_el->values = talloc_steal(msg->elements, el->values);
2491 /* TODO: this relises a bit too heavily on the exact
2492 behaviour of ldb_msg_find_element and
2493 ldb_msg_remove_element */
2494 old_el = ldb_msg_find_element(msg, el->name);
2496 ldb_msg_remove_element(msg, old_el);
2507 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2509 struct samldb_msds_intid_persistant *msds_intid_struct;
2510 struct ldb_context *ldb;
2511 struct replmd_replicated_request *ac;
2512 struct ldb_request *down_req;
2513 struct ldb_message *msg;
2514 time_t t = time(NULL);
2516 bool is_urgent = false, rodc = false;
2517 bool is_schema_nc = false;
2518 unsigned int functional_level;
2519 const struct ldb_message_element *guid_el = NULL;
2520 struct ldb_control *sd_propagation_control;
2521 struct replmd_private *replmd_private =
2522 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
2524 /* do not manipulate our control entries */
2525 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2526 return ldb_next_request(module, req);
2529 sd_propagation_control = ldb_request_get_control(req,
2530 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
2531 if (sd_propagation_control != NULL) {
2532 if (req->op.mod.message->num_elements != 1) {
2533 return ldb_module_operr(module);
2535 ret = strcmp(req->op.mod.message->elements[0].name,
2536 "nTSecurityDescriptor");
2538 return ldb_module_operr(module);
2541 return ldb_next_request(module, req);
2544 ldb = ldb_module_get_ctx(module);
2546 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2548 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
2549 if (guid_el != NULL) {
2550 ldb_set_errstring(ldb,
2551 "replmd_modify: it's not allowed to change the objectGUID!");
2552 return LDB_ERR_CONSTRAINT_VIOLATION;
2555 ac = replmd_ctx_init(module, req);
2557 return ldb_module_oom(module);
2560 functional_level = dsdb_functional_level(ldb);
2562 /* we have to copy the message as the caller might have it as a const */
2563 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2567 return LDB_ERR_OPERATIONS_ERROR;
2570 ldb_msg_remove_attr(msg, "whenChanged");
2571 ldb_msg_remove_attr(msg, "uSNChanged");
2573 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
2575 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
2576 msg, &ac->seq_num, t, is_schema_nc,
2578 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2579 struct loadparm_context *lp_ctx;
2582 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2583 struct loadparm_context);
2585 referral = talloc_asprintf(req,
2587 lpcfg_dnsdomain(lp_ctx),
2588 ldb_dn_get_linearized(msg->dn));
2589 ret = ldb_module_send_referral(req, referral);
2594 if (ret != LDB_SUCCESS) {
2599 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
2600 if (ret != LDB_SUCCESS) {
2606 * - replace the old object with the newly constructed one
2609 ac->is_urgent = is_urgent;
2611 ret = ldb_build_mod_req(&down_req, ldb, ac,
2614 ac, replmd_op_callback,
2616 LDB_REQ_SET_LOCATION(down_req);
2617 if (ret != LDB_SUCCESS) {
2622 /* current partition control is needed by "replmd_op_callback" */
2623 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2624 ret = ldb_request_add_control(down_req,
2625 DSDB_CONTROL_CURRENT_PARTITION_OID,
2627 if (ret != LDB_SUCCESS) {
2633 /* If we are in functional level 2000, then
2634 * replmd_modify_handle_linked_attribs will have done
2636 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2637 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2638 if (ret != LDB_SUCCESS) {
2644 talloc_steal(down_req, msg);
2646 /* we only change whenChanged and uSNChanged if the seq_num
2648 if (ac->seq_num != 0) {
2649 ret = add_time_element(msg, "whenChanged", t);
2650 if (ret != LDB_SUCCESS) {
2656 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2657 if (ret != LDB_SUCCESS) {
2664 if (!ldb_dn_compare_base(replmd_private->schema_dn, msg->dn)) {
2665 /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
2666 msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
2667 if (msds_intid_struct) {
2668 msds_intid_struct->usn = ac->seq_num;
2672 /* go on with the call chain */
2673 return ldb_next_request(module, down_req);
2676 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2679 handle a rename request
2681 On a rename we need to do an extra ldb_modify which sets the
2682 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2684 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2686 struct ldb_context *ldb;
2687 struct replmd_replicated_request *ac;
2689 struct ldb_request *down_req;
2691 /* do not manipulate our control entries */
2692 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2693 return ldb_next_request(module, req);
2696 ldb = ldb_module_get_ctx(module);
2698 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2700 ac = replmd_ctx_init(module, req);
2702 return ldb_module_oom(module);
2705 ret = ldb_build_rename_req(&down_req, ldb, ac,
2706 ac->req->op.rename.olddn,
2707 ac->req->op.rename.newdn,
2709 ac, replmd_rename_callback,
2711 LDB_REQ_SET_LOCATION(down_req);
2712 if (ret != LDB_SUCCESS) {
2717 /* go on with the call chain */
2718 return ldb_next_request(module, down_req);
2721 /* After the rename is compleated, update the whenchanged etc */
2722 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2724 struct ldb_context *ldb;
2725 struct ldb_request *down_req;
2726 struct ldb_message *msg;
2727 const struct dsdb_attribute *rdn_attr;
2728 const char *rdn_name;
2729 const struct ldb_val *rdn_val;
2730 const char *attrs[5] = { NULL, };
2731 time_t t = time(NULL);
2733 bool is_urgent = false, rodc = false;
2735 struct replmd_replicated_request *ac =
2736 talloc_get_type(req->context, struct replmd_replicated_request);
2737 struct replmd_private *replmd_private =
2738 talloc_get_type(ldb_module_get_private(ac->module),
2739 struct replmd_private);
2741 ldb = ldb_module_get_ctx(ac->module);
2743 if (ares->error != LDB_SUCCESS) {
2744 return ldb_module_done(ac->req, ares->controls,
2745 ares->response, ares->error);
2748 if (ares->type != LDB_REPLY_DONE) {
2749 ldb_set_errstring(ldb,
2750 "invalid ldb_reply_type in callback");
2752 return ldb_module_done(ac->req, NULL, NULL,
2753 LDB_ERR_OPERATIONS_ERROR);
2757 * - replace the old object with the newly constructed one
2760 msg = ldb_msg_new(ac);
2763 return LDB_ERR_OPERATIONS_ERROR;
2766 msg->dn = ac->req->op.rename.newdn;
2768 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
2770 rdn_name = ldb_dn_get_rdn_name(msg->dn);
2771 if (rdn_name == NULL) {
2773 return ldb_module_done(ac->req, NULL, NULL,
2777 /* normalize the rdn attribute name */
2778 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
2779 if (rdn_attr == NULL) {
2781 return ldb_module_done(ac->req, NULL, NULL,
2784 rdn_name = rdn_attr->lDAPDisplayName;
2786 rdn_val = ldb_dn_get_rdn_val(msg->dn);
2787 if (rdn_val == NULL) {
2789 return ldb_module_done(ac->req, NULL, NULL,
2793 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2795 return ldb_module_done(ac->req, NULL, NULL,
2798 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
2800 return ldb_module_done(ac->req, NULL, NULL,
2803 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2805 return ldb_module_done(ac->req, NULL, NULL,
2808 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
2810 return ldb_module_done(ac->req, NULL, NULL,
2815 * here we let replmd_update_rpmd() only search for
2816 * the existing "replPropertyMetaData" and rdn_name attributes.
2818 * We do not want the existing "name" attribute as
2819 * the "name" attribute needs to get the version
2820 * updated on rename even if the rdn value hasn't changed.
2822 * This is the diff of the meta data, for a moved user
2823 * on a w2k8r2 server:
2826 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
2827 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
2828 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
2829 * version : 0x00000001 (1)
2830 * reserved : 0x00000000 (0)
2831 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
2832 * local_usn : 0x00000000000037a5 (14245)
2833 * array: struct replPropertyMetaData1
2834 * attid : DRSUAPI_ATTID_name (0x90001)
2835 * - version : 0x00000001 (1)
2836 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
2837 * + version : 0x00000002 (2)
2838 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
2839 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
2840 * - originating_usn : 0x00000000000037a5 (14245)
2841 * - local_usn : 0x00000000000037a5 (14245)
2842 * + originating_usn : 0x0000000000003834 (14388)
2843 * + local_usn : 0x0000000000003834 (14388)
2844 * array: struct replPropertyMetaData1
2845 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
2846 * version : 0x00000004 (4)
2848 attrs[0] = "replPropertyMetaData";
2849 attrs[1] = "objectClass";
2850 attrs[2] = "instanceType";
2851 attrs[3] = rdn_name;
2854 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
2855 msg, &ac->seq_num, t,
2856 is_schema_nc, &is_urgent, &rodc);
2857 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2858 struct ldb_dn *olddn = ac->req->op.rename.olddn;
2859 struct loadparm_context *lp_ctx;
2862 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2863 struct loadparm_context);
2865 referral = talloc_asprintf(req,
2867 lpcfg_dnsdomain(lp_ctx),
2868 ldb_dn_get_linearized(olddn));
2869 ret = ldb_module_send_referral(req, referral);
2871 return ldb_module_done(req, NULL, NULL, ret);
2874 if (ret != LDB_SUCCESS) {
2876 return ldb_module_done(ac->req, NULL, NULL, ret);
2879 if (ac->seq_num == 0) {
2881 return ldb_module_done(ac->req, NULL, NULL,
2883 "internal error seq_num == 0"));
2885 ac->is_urgent = is_urgent;
2887 ret = ldb_build_mod_req(&down_req, ldb, ac,
2890 ac, replmd_op_callback,
2892 LDB_REQ_SET_LOCATION(down_req);
2893 if (ret != LDB_SUCCESS) {
2898 /* current partition control is needed by "replmd_op_callback" */
2899 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2900 ret = ldb_request_add_control(down_req,
2901 DSDB_CONTROL_CURRENT_PARTITION_OID,
2903 if (ret != LDB_SUCCESS) {
2909 talloc_steal(down_req, msg);
2911 ret = add_time_element(msg, "whenChanged", t);
2912 if (ret != LDB_SUCCESS) {
2918 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2919 if (ret != LDB_SUCCESS) {
2925 /* go on with the call chain - do the modify after the rename */
2926 return ldb_next_request(ac->module, down_req);
2930 * remove links from objects that point at this object when an object
2931 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
2932 * RemoveObj which states that link removal due to the object being
2933 * deleted is NOT an originating update - they just go away!
2936 static int replmd_delete_remove_link(struct ldb_module *module,
2937 const struct dsdb_schema *schema,
2939 struct ldb_message_element *el,
2940 const struct dsdb_attribute *sa,
2941 struct ldb_request *parent)
2944 TALLOC_CTX *tmp_ctx = talloc_new(module);
2945 struct ldb_context *ldb = ldb_module_get_ctx(module);
2947 for (i=0; i<el->num_values; i++) {
2948 struct dsdb_dn *dsdb_dn;
2952 struct ldb_message *msg;
2953 const struct dsdb_attribute *target_attr;
2954 struct ldb_message_element *el2;
2955 struct ldb_val dn_val;
2957 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2961 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2963 talloc_free(tmp_ctx);
2964 return LDB_ERR_OPERATIONS_ERROR;
2967 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2968 if (!NT_STATUS_IS_OK(status)) {
2969 talloc_free(tmp_ctx);
2970 return LDB_ERR_OPERATIONS_ERROR;
2973 /* remove the link */
2974 msg = ldb_msg_new(tmp_ctx);
2976 ldb_module_oom(module);
2977 talloc_free(tmp_ctx);
2978 return LDB_ERR_OPERATIONS_ERROR;
2982 msg->dn = dsdb_dn->dn;
2984 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2985 if (target_attr == NULL) {
2989 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2990 if (ret != LDB_SUCCESS) {
2991 ldb_module_oom(module);
2992 talloc_free(tmp_ctx);
2993 return LDB_ERR_OPERATIONS_ERROR;
2995 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2996 el2->values = &dn_val;
2997 el2->num_values = 1;
2999 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, parent);
3000 if (ret != LDB_SUCCESS) {
3001 talloc_free(tmp_ctx);
3005 talloc_free(tmp_ctx);
3011 handle update of replication meta data for deletion of objects
3013 This also handles the mapping of delete to a rename operation
3014 to allow deletes to be replicated.
3016 It also handles the incoming deleted objects, to ensure they are
3017 fully deleted here. In that case re_delete is true, and we do not
3018 use this as a signal to change the deleted state, just reinforce it.
3021 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
3023 int ret = LDB_ERR_OTHER;
3024 bool retb, disallow_move_on_delete;
3025 struct ldb_dn *old_dn, *new_dn;
3026 const char *rdn_name;
3027 const struct ldb_val *rdn_value, *new_rdn_value;
3029 struct ldb_context *ldb = ldb_module_get_ctx(module);
3030 const struct dsdb_schema *schema;
3031 struct ldb_message *msg, *old_msg;
3032 struct ldb_message_element *el;
3033 TALLOC_CTX *tmp_ctx;
3034 struct ldb_result *res, *parent_res;
3035 const char *preserved_attrs[] = {
3036 /* yes, this really is a hard coded list. See MS-ADTS
3037 section 3.1.1.5.5.1.1 */
3038 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
3039 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
3040 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
3041 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
3042 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
3043 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
3044 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
3045 "whenChanged", NULL};
3046 unsigned int i, el_count = 0;
3047 enum deletion_state deletion_state, next_deletion_state;
3049 if (ldb_dn_is_special(req->op.del.dn)) {
3050 return ldb_next_request(module, req);
3054 * We have to allow dbcheck to remove an object that
3055 * is beyond repair, and to do so totally. This could
3056 * mean we we can get a partial object from the other
3057 * DC, causing havoc, so dbcheck suggests
3058 * re-replication first. dbcheck sets both DBCHECK
3059 * and RELAX in this situation.
3061 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
3062 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
3063 /* really, really remove it */
3064 return ldb_next_request(module, req);
3067 tmp_ctx = talloc_new(ldb);
3070 return LDB_ERR_OPERATIONS_ERROR;
3073 schema = dsdb_get_schema(ldb, tmp_ctx);
3075 talloc_free(tmp_ctx);
3076 return LDB_ERR_OPERATIONS_ERROR;
3079 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
3081 /* we need the complete msg off disk, so we can work out which
3082 attributes need to be removed */
3083 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
3084 DSDB_FLAG_NEXT_MODULE |
3085 DSDB_SEARCH_SHOW_RECYCLED |
3086 DSDB_SEARCH_REVEAL_INTERNALS |
3087 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
3088 if (ret != LDB_SUCCESS) {
3089 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3090 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
3091 re_delete ? "re-delete" : "delete",
3092 ldb_dn_get_linearized(old_dn),
3093 ldb_errstring(ldb_module_get_ctx(module)));
3094 talloc_free(tmp_ctx);
3097 old_msg = res->msgs[0];
3099 replmd_deletion_state(module, old_msg,
3101 &next_deletion_state);
3103 /* This supports us noticing an incoming isDeleted and acting on it */
3105 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
3106 next_deletion_state = deletion_state;
3109 if (next_deletion_state == OBJECT_REMOVED) {
3111 * We have to prevent objects being deleted, even if
3112 * the administrator really wants them gone, as
3113 * without the tombstone, we can get a partial object
3114 * from the other DC, causing havoc.
3116 * The only other valid case is when the 180 day
3117 * timeout has expired, when relax is specified.
3119 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
3120 /* it is already deleted - really remove it this time */
3121 talloc_free(tmp_ctx);
3122 return ldb_next_request(module, req);
3125 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
3126 "This check is to prevent corruption of the replicated state.",
3127 ldb_dn_get_linearized(old_msg->dn));
3128 return LDB_ERR_UNWILLING_TO_PERFORM;
3131 rdn_name = ldb_dn_get_rdn_name(old_dn);
3132 rdn_value = ldb_dn_get_rdn_val(old_dn);
3133 if ((rdn_name == NULL) || (rdn_value == NULL)) {
3134 talloc_free(tmp_ctx);
3135 return ldb_operr(ldb);
3138 msg = ldb_msg_new(tmp_ctx);
3140 ldb_module_oom(module);
3141 talloc_free(tmp_ctx);
3142 return LDB_ERR_OPERATIONS_ERROR;
3147 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
3148 disallow_move_on_delete =
3149 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
3150 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
3152 /* work out where we will be renaming this object to */
3153 if (!disallow_move_on_delete) {
3154 struct ldb_dn *deleted_objects_dn;
3155 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
3156 &deleted_objects_dn);
3159 * We should not move objects if we can't find the
3160 * deleted objects DN. Not moving (or otherwise
3161 * harming) the Deleted Objects DN itself is handled
3164 if (re_delete && (ret != LDB_SUCCESS)) {
3165 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3166 if (new_dn == NULL) {
3167 ldb_module_oom(module);
3168 talloc_free(tmp_ctx);
3169 return LDB_ERR_OPERATIONS_ERROR;
3171 } else if (ret != LDB_SUCCESS) {
3172 /* this is probably an attempted delete on a partition
3173 * that doesn't allow delete operations, such as the
3174 * schema partition */
3175 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
3176 ldb_dn_get_linearized(old_dn));
3177 talloc_free(tmp_ctx);
3178 return LDB_ERR_UNWILLING_TO_PERFORM;
3180 new_dn = deleted_objects_dn;
3183 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3184 if (new_dn == NULL) {
3185 ldb_module_oom(module);
3186 talloc_free(tmp_ctx);
3187 return LDB_ERR_OPERATIONS_ERROR;
3191 if (deletion_state == OBJECT_NOT_DELETED) {
3192 /* get the objects GUID from the search we just did */
3193 guid = samdb_result_guid(old_msg, "objectGUID");
3195 /* Add a formatted child */
3196 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
3198 ldb_dn_escape_value(tmp_ctx, *rdn_value),
3199 GUID_string(tmp_ctx, &guid));
3201 ldb_asprintf_errstring(ldb, __location__
3202 ": Unable to add a formatted child to dn: %s",
3203 ldb_dn_get_linearized(new_dn));
3204 talloc_free(tmp_ctx);
3205 return LDB_ERR_OPERATIONS_ERROR;
3208 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
3209 if (ret != LDB_SUCCESS) {
3210 ldb_asprintf_errstring(ldb, __location__
3211 ": Failed to add isDeleted string to the msg");
3212 talloc_free(tmp_ctx);
3215 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3218 * No matter what has happened with other renames etc, try again to
3219 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
3222 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
3223 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
3225 ldb_asprintf_errstring(ldb, __location__
3226 ": Unable to add a prepare rdn of %s",
3227 ldb_dn_get_linearized(rdn));
3228 talloc_free(tmp_ctx);
3229 return LDB_ERR_OPERATIONS_ERROR;
3231 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
3233 retb = ldb_dn_add_child(new_dn, rdn);
3235 ldb_asprintf_errstring(ldb, __location__
3236 ": Unable to add rdn %s to base dn: %s",
3237 ldb_dn_get_linearized(rdn),
3238 ldb_dn_get_linearized(new_dn));
3239 talloc_free(tmp_ctx);
3240 return LDB_ERR_OPERATIONS_ERROR;
3245 now we need to modify the object in the following ways:
3247 - add isDeleted=TRUE
3248 - update rDN and name, with new rDN
3249 - remove linked attributes
3250 - remove objectCategory and sAMAccountType
3251 - remove attribs not on the preserved list
3252 - preserved if in above list, or is rDN
3253 - remove all linked attribs from this object
3254 - remove all links from other objects to this object
3255 - add lastKnownParent
3256 - update replPropertyMetaData?
3258 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
3261 if (deletion_state == OBJECT_NOT_DELETED) {
3262 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3263 char *parent_dn_str = NULL;
3265 /* we need the storage form of the parent GUID */
3266 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
3268 DSDB_FLAG_NEXT_MODULE |
3269 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3270 DSDB_SEARCH_REVEAL_INTERNALS|
3271 DSDB_SEARCH_SHOW_RECYCLED, req);
3272 if (ret != LDB_SUCCESS) {
3273 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3274 "repmd_delete: Failed to %s %s, "
3275 "because we failed to find it's parent (%s): %s",
3276 re_delete ? "re-delete" : "delete",
3277 ldb_dn_get_linearized(old_dn),
3278 ldb_dn_get_linearized(parent_dn),
3279 ldb_errstring(ldb_module_get_ctx(module)));
3280 talloc_free(tmp_ctx);
3285 * Now we can use the DB version,
3286 * it will have the extended DN info in it
3288 parent_dn = parent_res->msgs[0]->dn;
3289 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
3292 if (parent_dn_str == NULL) {
3293 talloc_free(tmp_ctx);
3294 return ldb_module_oom(module);
3297 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
3299 if (ret != LDB_SUCCESS) {
3300 ldb_asprintf_errstring(ldb, __location__
3301 ": Failed to add lastKnownParent "
3302 "string when deleting %s",
3303 ldb_dn_get_linearized(old_dn));
3304 talloc_free(tmp_ctx);
3307 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3309 if (next_deletion_state == OBJECT_DELETED) {
3310 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
3311 if (ret != LDB_SUCCESS) {
3312 ldb_asprintf_errstring(ldb, __location__
3313 ": Failed to add msDS-LastKnownRDN "
3314 "string when deleting %s",
3315 ldb_dn_get_linearized(old_dn));
3316 talloc_free(tmp_ctx);
3319 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
3323 switch (next_deletion_state) {
3325 case OBJECT_RECYCLED:
3326 case OBJECT_TOMBSTONE:
3329 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
3330 * describes what must be removed from a tombstone
3333 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
3334 * describes what must be removed from a recycled
3340 * we also mark it as recycled, meaning this object can't be
3341 * recovered (we are stripping its attributes).
3342 * This is done only if we have this schema object of course ...
3343 * This behavior is identical to the one of Windows 2008R2 which
3344 * always set the isRecycled attribute, even if the recycle-bin is
3345 * not activated and what ever the forest level is.
3347 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
3348 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
3349 if (ret != LDB_SUCCESS) {
3350 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
3351 ldb_module_oom(module);
3352 talloc_free(tmp_ctx);
3355 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3358 /* work out which of the old attributes we will be removing */
3359 for (i=0; i<old_msg->num_elements; i++) {
3360 const struct dsdb_attribute *sa;
3361 el = &old_msg->elements[i];
3362 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3364 talloc_free(tmp_ctx);
3365 return LDB_ERR_OPERATIONS_ERROR;
3367 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
3368 /* don't remove the rDN */
3371 if (sa->linkID && (sa->linkID & 1)) {
3373 we have a backlink in this object
3374 that needs to be removed. We're not
3375 allowed to remove it directly
3376 however, so we instead setup a
3377 modify to delete the corresponding
3380 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
3381 if (ret != LDB_SUCCESS) {
3382 const char *old_dn_str
3383 = ldb_dn_get_linearized(old_dn);
3384 ldb_asprintf_errstring(ldb,
3386 ": Failed to remove backlink of "
3387 "%s when deleting %s",
3390 talloc_free(tmp_ctx);
3391 return LDB_ERR_OPERATIONS_ERROR;
3393 /* now we continue, which means we
3394 won't remove this backlink
3400 if (ldb_attr_in_list(preserved_attrs, el->name)) {
3403 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
3407 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
3408 if (ret != LDB_SUCCESS) {
3409 talloc_free(tmp_ctx);
3410 ldb_module_oom(module);
3417 case OBJECT_DELETED:
3419 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
3420 * describes what must be removed from a deleted
3424 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
3425 if (ret != LDB_SUCCESS) {
3426 talloc_free(tmp_ctx);
3427 ldb_module_oom(module);
3431 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
3432 if (ret != LDB_SUCCESS) {
3433 talloc_free(tmp_ctx);
3434 ldb_module_oom(module);
3444 if (deletion_state == OBJECT_NOT_DELETED) {
3445 const struct dsdb_attribute *sa;
3447 /* work out what the new rdn value is, for updating the
3448 rDN and name fields */
3449 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
3450 if (new_rdn_value == NULL) {
3451 talloc_free(tmp_ctx);
3452 return ldb_operr(ldb);
3455 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
3457 talloc_free(tmp_ctx);
3458 return LDB_ERR_OPERATIONS_ERROR;
3461 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
3463 if (ret != LDB_SUCCESS) {
3464 talloc_free(tmp_ctx);
3467 el->flags = LDB_FLAG_MOD_REPLACE;
3469 el = ldb_msg_find_element(old_msg, "name");
3471 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
3472 if (ret != LDB_SUCCESS) {
3473 talloc_free(tmp_ctx);
3476 el->flags = LDB_FLAG_MOD_REPLACE;
3481 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
3486 * No matter what has happned with other renames, try again to
3487 * get this to be under the deleted DN.
3489 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
3490 /* now rename onto the new DN */
3491 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
3492 if (ret != LDB_SUCCESS){
3493 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
3494 ldb_dn_get_linearized(old_dn),
3495 ldb_dn_get_linearized(new_dn),
3496 ldb_errstring(ldb)));
3497 talloc_free(tmp_ctx);
3503 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req);
3504 if (ret != LDB_SUCCESS) {
3505 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
3506 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
3507 talloc_free(tmp_ctx);
3511 talloc_free(tmp_ctx);
3513 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
3516 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
3518 return replmd_delete_internals(module, req, false);
3522 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
3527 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
3529 int ret = LDB_ERR_OTHER;
3530 /* TODO: do some error mapping */
3532 /* Let the caller know the full WERROR */
3533 ar->objs->error = status;
3539 static struct replPropertyMetaData1 *
3540 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
3541 enum drsuapi_DsAttributeId attid)
3544 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
3546 for (i = 0; i < rpmd_ctr->count; i++) {
3547 if (rpmd_ctr->array[i].attid == attid) {
3548 return &rpmd_ctr->array[i];
3556 return true if an update is newer than an existing entry
3557 see section 5.11 of MS-ADTS
3559 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
3560 const struct GUID *update_invocation_id,
3561 uint32_t current_version,
3562 uint32_t update_version,
3563 NTTIME current_change_time,
3564 NTTIME update_change_time)
3566 if (update_version != current_version) {
3567 return update_version > current_version;
3569 if (update_change_time != current_change_time) {
3570 return update_change_time > current_change_time;
3572 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
3575 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
3576 struct replPropertyMetaData1 *new_m)
3578 return replmd_update_is_newer(&cur_m->originating_invocation_id,
3579 &new_m->originating_invocation_id,
3582 cur_m->originating_change_time,
3583 new_m->originating_change_time);
3590 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
3592 const struct ldb_val *rdn_val;
3593 const char *rdn_name;
3594 struct ldb_dn *new_dn;
3596 rdn_val = ldb_dn_get_rdn_val(dn);
3597 rdn_name = ldb_dn_get_rdn_name(dn);
3598 if (!rdn_val || !rdn_name) {
3602 new_dn = ldb_dn_copy(mem_ctx, dn);
3607 if (!ldb_dn_remove_child_components(new_dn, 1)) {
3611 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
3613 ldb_dn_escape_value(new_dn, *rdn_val),
3614 GUID_string(new_dn, guid))) {
3623 perform a modify operation which sets the rDN and name attributes to
3624 their current values. This has the effect of changing these
3625 attributes to have been last updated by the current DC. This is
3626 needed to ensure that renames performed as part of conflict
3627 resolution are propogated to other DCs
3629 static int replmd_name_modify(struct replmd_replicated_request *ar,
3630 struct ldb_request *req, struct ldb_dn *dn)
3632 struct ldb_message *msg;
3633 const char *rdn_name;
3634 const struct ldb_val *rdn_val;
3635 const struct dsdb_attribute *rdn_attr;
3638 msg = ldb_msg_new(req);
3644 rdn_name = ldb_dn_get_rdn_name(dn);
3645 if (rdn_name == NULL) {
3649 /* normalize the rdn attribute name */
3650 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3651 if (rdn_attr == NULL) {
3654 rdn_name = rdn_attr->lDAPDisplayName;
3656 rdn_val = ldb_dn_get_rdn_val(dn);
3657 if (rdn_val == NULL) {
3661 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3664 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3667 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3670 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3674 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3675 if (ret != LDB_SUCCESS) {
3676 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
3677 ldb_dn_get_linearized(dn),
3678 ldb_errstring(ldb_module_get_ctx(ar->module))));
3688 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
3689 ldb_dn_get_linearized(dn)));
3690 return LDB_ERR_OPERATIONS_ERROR;
3695 callback for conflict DN handling where we have renamed the incoming
3696 record. After renaming it, we need to ensure the change of name and
3697 rDN for the incoming record is seen as an originating update by this DC.
3699 This also handles updating lastKnownParent for entries sent to lostAndFound
3701 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3703 struct replmd_replicated_request *ar =
3704 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3705 struct ldb_dn *conflict_dn = NULL;
3708 if (ares->error != LDB_SUCCESS) {
3709 /* call the normal callback for everything except success */
3710 return replmd_op_callback(req, ares);
3713 switch (req->operation) {
3715 conflict_dn = req->op.add.message->dn;
3718 conflict_dn = req->op.mod.message->dn;
3721 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
3724 /* perform a modify of the rDN and name of the record */
3725 ret = replmd_name_modify(ar, req, conflict_dn);
3726 if (ret != LDB_SUCCESS) {
3728 return replmd_op_callback(req, ares);
3731 if (ar->objs->objects[ar->index_current].last_known_parent) {
3732 struct ldb_message *msg = ldb_msg_new(req);
3734 ldb_module_oom(ar->module);
3735 return LDB_ERR_OPERATIONS_ERROR;
3738 msg->dn = req->op.add.message->dn;
3740 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
3741 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
3742 if (ret != LDB_SUCCESS) {
3743 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
3744 ldb_module_oom(ar->module);
3747 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
3749 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3750 if (ret != LDB_SUCCESS) {
3751 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
3752 ldb_dn_get_linearized(msg->dn),
3753 ldb_errstring(ldb_module_get_ctx(ar->module))));
3759 return replmd_op_callback(req, ares);
3763 callback for replmd_replicated_apply_add()
3764 This copes with the creation of conflict records in the case where
3765 the DN exists, but with a different objectGUID
3767 static int replmd_op_possible_conflict_callback(struct ldb_request *req, struct ldb_reply *ares, int (*callback)(struct ldb_request *req, struct ldb_reply *ares))
3769 struct ldb_dn *conflict_dn;
3770 struct replmd_replicated_request *ar =
3771 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3772 struct ldb_result *res;
3773 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
3775 const struct ldb_val *omd_value;
3776 struct replPropertyMetaDataBlob omd, *rmd;
3777 enum ndr_err_code ndr_err;
3778 bool rename_incoming_record, rodc;
3779 struct replPropertyMetaData1 *rmd_name, *omd_name;
3780 struct ldb_message *msg;
3781 struct ldb_request *down_req = NULL;
3783 /* call the normal callback for success */
3784 if (ares->error == LDB_SUCCESS) {
3785 return callback(req, ares);
3789 * we have a conflict, and need to decide if we will keep the
3790 * new record or the old record
3793 msg = ar->objs->objects[ar->index_current].msg;
3794 conflict_dn = msg->dn;
3796 /* For failures other than conflicts, fail the whole operation here */
3797 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
3798 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
3799 ldb_dn_get_linearized(conflict_dn),
3800 ldb_errstring(ldb_module_get_ctx(ar->module)));
3802 return ldb_module_done(ar->req, NULL, NULL,
3803 LDB_ERR_OPERATIONS_ERROR);
3806 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
3807 if (ret != LDB_SUCCESS) {
3808 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)));
3809 return ldb_module_done(ar->req, NULL, NULL,
3810 LDB_ERR_OPERATIONS_ERROR);
3816 * We are on an RODC, or were a GC for this
3817 * partition, so we have to fail this until
3818 * someone who owns the partition sorts it
3821 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3822 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
3823 " - We must fail the operation until a master for this partition resolves the conflict",
3824 ldb_dn_get_linearized(conflict_dn));
3829 * first we need the replPropertyMetaData attribute from the
3830 * local, conflicting record
3832 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
3834 DSDB_FLAG_NEXT_MODULE |
3835 DSDB_SEARCH_SHOW_DELETED |
3836 DSDB_SEARCH_SHOW_RECYCLED, req);
3837 if (ret != LDB_SUCCESS) {
3838 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
3839 ldb_dn_get_linearized(conflict_dn)));
3843 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
3844 if (omd_value == NULL) {
3845 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
3846 ldb_dn_get_linearized(conflict_dn)));
3850 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
3851 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3852 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3853 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
3854 ldb_dn_get_linearized(conflict_dn)));
3858 rmd = ar->objs->objects[ar->index_current].meta_data;
3860 /* we decide which is newer based on the RPMD on the name
3861 attribute. See [MS-DRSR] ResolveNameConflict */
3862 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
3863 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
3864 if (!rmd_name || !omd_name) {
3865 DEBUG(0,(__location__ ": Failed to find name attribute in replPropertyMetaData for %s\n",
3866 ldb_dn_get_linearized(conflict_dn)));
3870 rename_incoming_record = !(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) &&
3871 !replmd_replPropertyMetaData1_is_newer(omd_name, rmd_name);
3873 if (rename_incoming_record) {
3875 struct ldb_dn *new_dn;
3877 guid = samdb_result_guid(msg, "objectGUID");
3878 if (GUID_all_zero(&guid)) {
3879 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
3880 ldb_dn_get_linearized(conflict_dn)));
3883 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3884 if (new_dn == NULL) {
3885 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3886 ldb_dn_get_linearized(conflict_dn)));
3890 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
3891 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3893 /* re-submit the request, but with the new DN */
3894 callback = replmd_op_name_modify_callback;
3897 /* we are renaming the existing record */
3899 struct ldb_dn *new_dn;
3901 guid = samdb_result_guid(res->msgs[0], "objectGUID");
3902 if (GUID_all_zero(&guid)) {
3903 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
3904 ldb_dn_get_linearized(conflict_dn)));
3908 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3909 if (new_dn == NULL) {
3910 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3911 ldb_dn_get_linearized(conflict_dn)));
3915 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
3916 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3918 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
3919 DSDB_FLAG_OWN_MODULE, req);
3920 if (ret != LDB_SUCCESS) {
3921 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
3922 ldb_dn_get_linearized(conflict_dn),
3923 ldb_dn_get_linearized(new_dn),
3924 ldb_errstring(ldb_module_get_ctx(ar->module))));
3929 * now we need to ensure that the rename is seen as an
3930 * originating update. We do that with a modify.
3932 ret = replmd_name_modify(ar, req, new_dn);
3933 if (ret != LDB_SUCCESS) {
3937 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
3938 ldb_dn_get_linearized(req->op.add.message->dn)));
3941 ret = ldb_build_add_req(&down_req,
3942 ldb_module_get_ctx(ar->module),
3949 if (ret != LDB_SUCCESS) {
3952 LDB_REQ_SET_LOCATION(down_req);
3954 /* current partition control needed by "repmd_op_callback" */
3955 ret = ldb_request_add_control(down_req,
3956 DSDB_CONTROL_CURRENT_PARTITION_OID,
3958 if (ret != LDB_SUCCESS) {
3959 return replmd_replicated_request_error(ar, ret);
3962 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
3963 /* this tells the partition module to make it a
3964 partial replica if creating an NC */
3965 ret = ldb_request_add_control(down_req,
3966 DSDB_CONTROL_PARTIAL_REPLICA,
3968 if (ret != LDB_SUCCESS) {
3969 return replmd_replicated_request_error(ar, ret);
3974 * Finally we re-run the add, otherwise the new record won't
3975 * exist, as we are here because of that exact failure!
3977 return ldb_next_request(ar->module, down_req);
3980 /* on failure make the caller get the error. This means
3981 * replication will stop with an error, but there is not much
3984 return ldb_module_done(ar->req, NULL, NULL,
3989 callback for replmd_replicated_apply_add()
3990 This copes with the creation of conflict records in the case where
3991 the DN exists, but with a different objectGUID
3993 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
3995 struct replmd_replicated_request *ar =
3996 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3998 if (ar->objs->objects[ar->index_current].last_known_parent) {
3999 /* This is like a conflict DN, where we put the object in LostAndFound
4000 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
4001 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
4004 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
4008 this is called when a new object comes in over DRS
4010 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
4012 struct ldb_context *ldb;
4013 struct ldb_request *change_req;
4014 enum ndr_err_code ndr_err;
4015 struct ldb_message *msg;
4016 struct replPropertyMetaDataBlob *md;
4017 struct ldb_val md_value;
4020 bool remote_isDeleted = false;
4021 const struct dsdb_attribute *rdn_sa;
4022 const char *rdn_name;
4024 ldb = ldb_module_get_ctx(ar->module);
4025 msg = ar->objs->objects[ar->index_current].msg;
4026 md = ar->objs->objects[ar->index_current].meta_data;
4028 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4029 if (ret != LDB_SUCCESS) {
4030 return replmd_replicated_request_error(ar, ret);
4033 ret = dsdb_msg_add_guid(msg,
4034 &ar->objs->objects[ar->index_current].object_guid,
4036 if (ret != LDB_SUCCESS) {
4037 return replmd_replicated_request_error(ar, ret);
4040 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4041 if (ret != LDB_SUCCESS) {
4042 return replmd_replicated_request_error(ar, ret);
4045 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
4046 if (ret != LDB_SUCCESS) {
4047 return replmd_replicated_request_error(ar, ret);
4050 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4051 if (ret != LDB_SUCCESS) {
4052 return replmd_replicated_request_error(ar, ret);
4055 /* remove any message elements that have zero values */
4056 for (i=0; i<msg->num_elements; i++) {
4057 struct ldb_message_element *el = &msg->elements[i];
4059 if (el->num_values == 0) {
4060 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
4061 ldb_asprintf_errstring(ldb, __location__
4062 ": empty objectClass sent on %s, aborting replication\n",
4063 ldb_dn_get_linearized(msg->dn));
4064 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
4067 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
4069 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
4070 msg->num_elements--;
4077 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
4078 DEBUG(4, ("DRS replication add message:\n%s\n", s));
4082 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4083 "isDeleted", false);
4086 * the meta data array is already sorted by the caller
4089 rdn_name = ldb_dn_get_rdn_name(msg->dn);
4090 if (rdn_name == NULL) {
4091 ldb_asprintf_errstring(ldb, __location__ ": No rDN for %s?\n", ldb_dn_get_linearized(msg->dn));
4092 return replmd_replicated_request_error(ar, LDB_ERR_INVALID_DN_SYNTAX);
4095 rdn_sa = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
4096 if (rdn_sa == NULL) {
4097 ldb_asprintf_errstring(ldb, ": No schema attribute found for rDN %s for %s\n",
4098 rdn_name, ldb_dn_get_linearized(msg->dn));
4099 return replmd_replicated_request_error(ar, LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE);
4102 ret = replmd_replPropertyMetaDataCtr1_verify(ldb, &md->ctr.ctr1, rdn_sa, msg->dn);
4103 if (ret != LDB_SUCCESS) {
4104 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
4105 return replmd_replicated_request_error(ar, ret);
4108 for (i=0; i < md->ctr.ctr1.count; i++) {
4109 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
4111 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
4112 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
4113 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4114 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4115 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4117 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
4118 if (ret != LDB_SUCCESS) {
4119 return replmd_replicated_request_error(ar, ret);
4122 replmd_ldb_message_sort(msg, ar->schema);
4124 if (!remote_isDeleted) {
4125 ret = dsdb_module_schedule_sd_propagation(ar->module,
4126 ar->objs->partition_dn,
4128 if (ret != LDB_SUCCESS) {
4129 return replmd_replicated_request_error(ar, ret);
4133 ar->isDeleted = remote_isDeleted;
4135 ret = ldb_build_add_req(&change_req,
4141 replmd_op_add_callback,
4143 LDB_REQ_SET_LOCATION(change_req);
4144 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4146 /* current partition control needed by "repmd_op_callback" */
4147 ret = ldb_request_add_control(change_req,
4148 DSDB_CONTROL_CURRENT_PARTITION_OID,
4150 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4152 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4153 /* this tells the partition module to make it a
4154 partial replica if creating an NC */
4155 ret = ldb_request_add_control(change_req,
4156 DSDB_CONTROL_PARTIAL_REPLICA,
4158 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4161 return ldb_next_request(ar->module, change_req);
4164 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
4165 struct ldb_reply *ares)
4167 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4168 struct replmd_replicated_request);
4172 return ldb_module_done(ar->req, NULL, NULL,
4173 LDB_ERR_OPERATIONS_ERROR);
4177 * The error NO_SUCH_OBJECT is not expected, unless the search
4178 * base is the partition DN, and that case doesn't happen here
4179 * because then we wouldn't get a parent_guid_value in any
4182 if (ares->error != LDB_SUCCESS) {
4183 return ldb_module_done(ar->req, ares->controls,
4184 ares->response, ares->error);
4187 switch (ares->type) {
4188 case LDB_REPLY_ENTRY:
4190 struct ldb_message *parent_msg = ares->message;
4191 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
4192 struct ldb_dn *parent_dn;
4195 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
4196 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
4197 /* Per MS-DRSR 4.1.10.6.10
4198 * FindBestParentObject we need to move this
4199 * new object under a deleted object to
4201 struct ldb_dn *nc_root;
4203 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
4204 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4205 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4206 "No suitable NC root found for %s. "
4207 "We need to move this object because parent object %s "
4208 "is deleted, but this object is not.",
4209 ldb_dn_get_linearized(msg->dn),
4210 ldb_dn_get_linearized(parent_msg->dn));
4211 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4212 } else if (ret != LDB_SUCCESS) {
4213 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4214 "Unable to find NC root for %s: %s. "
4215 "We need to move this object because parent object %s "
4216 "is deleted, but this object is not.",
4217 ldb_dn_get_linearized(msg->dn),
4218 ldb_errstring(ldb_module_get_ctx(ar->module)),
4219 ldb_dn_get_linearized(parent_msg->dn));
4220 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4223 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
4225 DS_GUID_LOSTANDFOUND_CONTAINER,
4227 if (ret != LDB_SUCCESS) {
4228 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4229 "Unable to find LostAndFound Container for %s "
4230 "in partition %s: %s. "
4231 "We need to move this object because parent object %s "
4232 "is deleted, but this object is not.",
4233 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
4234 ldb_errstring(ldb_module_get_ctx(ar->module)),
4235 ldb_dn_get_linearized(parent_msg->dn));
4236 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4238 ar->objs->objects[ar->index_current].last_known_parent
4239 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4243 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4246 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
4248 comp_num = ldb_dn_get_comp_num(msg->dn);
4250 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
4252 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4255 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
4257 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4261 case LDB_REPLY_REFERRAL:
4262 /* we ignore referrals */
4265 case LDB_REPLY_DONE:
4267 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
4268 struct GUID_txt_buf str_buf;
4269 if (ar->search_msg != NULL) {
4270 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4271 "No parent with GUID %s found for object locally known as %s",
4272 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
4273 ldb_dn_get_linearized(ar->search_msg->dn));
4275 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4276 "No parent with GUID %s found for object remotely known as %s",
4277 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
4278 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
4282 * This error code is really important, as it
4283 * is the flag back to the callers to retry
4284 * this with DRSUAPI_DRS_GET_ANC, and so get
4285 * the parent objects before the child
4288 return ldb_module_done(ar->req, NULL, NULL,
4289 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
4292 if (ar->search_msg != NULL) {
4293 ret = replmd_replicated_apply_merge(ar);
4295 ret = replmd_replicated_apply_add(ar);
4297 if (ret != LDB_SUCCESS) {
4298 return ldb_module_done(ar->req, NULL, NULL, ret);
4307 * Look for the parent object, so we put the new object in the right
4308 * place This is akin to NameObject in MS-DRSR - this routine and the
4309 * callbacks find the right parent name, and correct name for this
4313 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
4315 struct ldb_context *ldb;
4319 struct ldb_request *search_req;
4320 static const char *attrs[] = {"isDeleted", NULL};
4321 struct GUID_txt_buf guid_str_buf;
4323 ldb = ldb_module_get_ctx(ar->module);
4325 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
4326 if (ar->search_msg != NULL) {
4327 return replmd_replicated_apply_merge(ar);
4329 return replmd_replicated_apply_add(ar);
4333 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
4336 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
4337 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4339 ret = ldb_build_search_req(&search_req,
4342 ar->objs->partition_dn,
4348 replmd_replicated_apply_search_for_parent_callback,
4350 LDB_REQ_SET_LOCATION(search_req);
4352 ret = dsdb_request_add_controls(search_req,
4353 DSDB_SEARCH_SHOW_RECYCLED|
4354 DSDB_SEARCH_SHOW_DELETED|
4355 DSDB_SEARCH_SHOW_EXTENDED_DN);
4356 if (ret != LDB_SUCCESS) {
4360 return ldb_next_request(ar->module, search_req);
4364 handle renames that come in over DRS replication
4366 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
4367 struct ldb_message *msg,
4368 struct ldb_request *parent,
4372 TALLOC_CTX *tmp_ctx = talloc_new(msg);
4373 struct ldb_result *res;
4374 struct ldb_dn *conflict_dn;
4375 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4376 const struct ldb_val *omd_value;
4377 struct replPropertyMetaDataBlob omd, *rmd;
4378 enum ndr_err_code ndr_err;
4379 bool rename_incoming_record, rodc;
4380 struct replPropertyMetaData1 *rmd_name, *omd_name;
4381 struct ldb_dn *new_dn;
4384 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
4385 ldb_dn_get_linearized(ar->search_msg->dn),
4386 ldb_dn_get_linearized(msg->dn)));
4389 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
4390 DSDB_FLAG_NEXT_MODULE, ar->req);
4392 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4393 talloc_free(tmp_ctx);
4394 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
4395 ldb_dn_get_linearized(ar->search_msg->dn),
4396 ldb_dn_get_linearized(msg->dn),
4397 ldb_errstring(ldb_module_get_ctx(ar->module)));
4401 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4402 if (ret != LDB_SUCCESS) {
4403 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4404 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
4405 ldb_errstring(ldb_module_get_ctx(ar->module)));
4406 return LDB_ERR_OPERATIONS_ERROR;
4409 * we have a conflict, and need to decide if we will keep the
4410 * new record or the old record
4413 conflict_dn = msg->dn;
4417 * We are on an RODC, or were a GC for this
4418 * partition, so we have to fail this until
4419 * someone who owns the partition sorts it
4422 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4423 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
4424 " - We must fail the operation until a master for this partition resolves the conflict",
4425 ldb_dn_get_linearized(conflict_dn));
4430 * first we need the replPropertyMetaData attribute from the
4433 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
4435 DSDB_FLAG_NEXT_MODULE |
4436 DSDB_SEARCH_SHOW_DELETED |
4437 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
4438 if (ret != LDB_SUCCESS) {
4439 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4440 ldb_dn_get_linearized(conflict_dn)));
4444 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4445 if (omd_value == NULL) {
4446 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4447 ldb_dn_get_linearized(conflict_dn)));
4451 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4452 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4453 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4454 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4455 ldb_dn_get_linearized(conflict_dn)));
4459 rmd = ar->objs->objects[ar->index_current].meta_data;
4461 /* we decide which is newer based on the RPMD on the name
4462 attribute. See [MS-DRSR] ResolveNameConflict */
4463 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4464 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4465 if (!rmd_name || !omd_name) {
4466 DEBUG(0,(__location__ ": Failed to find name attribute in replPropertyMetaData for %s\n",
4467 ldb_dn_get_linearized(conflict_dn)));
4471 rename_incoming_record = !(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) &&
4472 !replmd_replPropertyMetaData1_is_newer(omd_name, rmd_name);
4474 if (rename_incoming_record) {
4476 new_dn = replmd_conflict_dn(msg, msg->dn,
4477 &ar->objs->objects[ar->index_current].object_guid);
4478 if (new_dn == NULL) {
4479 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4480 "Failed to form conflict DN for %s\n",
4481 ldb_dn_get_linearized(msg->dn));
4483 return replmd_replicated_request_werror(ar, WERR_NOMEM);
4486 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
4487 DSDB_FLAG_NEXT_MODULE, ar->req);
4488 if (ret != LDB_SUCCESS) {
4489 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4490 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
4491 ldb_dn_get_linearized(conflict_dn),
4492 ldb_dn_get_linearized(ar->search_msg->dn),
4493 ldb_dn_get_linearized(new_dn),
4494 ldb_errstring(ldb_module_get_ctx(ar->module)));
4495 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4503 /* we are renaming the existing record */
4505 guid = samdb_result_guid(res->msgs[0], "objectGUID");
4506 if (GUID_all_zero(&guid)) {
4507 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
4508 ldb_dn_get_linearized(conflict_dn)));
4512 new_dn = replmd_conflict_dn(tmp_ctx, conflict_dn, &guid);
4513 if (new_dn == NULL) {
4514 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4515 ldb_dn_get_linearized(conflict_dn)));
4519 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4520 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4522 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
4523 DSDB_FLAG_OWN_MODULE, ar->req);
4524 if (ret != LDB_SUCCESS) {
4525 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
4526 ldb_dn_get_linearized(conflict_dn),
4527 ldb_dn_get_linearized(new_dn),
4528 ldb_errstring(ldb_module_get_ctx(ar->module))));
4533 * now we need to ensure that the rename is seen as an
4534 * originating update. We do that with a modify.
4536 ret = replmd_name_modify(ar, ar->req, new_dn);
4537 if (ret != LDB_SUCCESS) {
4541 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
4542 ldb_dn_get_linearized(ar->search_msg->dn),
4543 ldb_dn_get_linearized(msg->dn)));
4546 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
4547 DSDB_FLAG_NEXT_MODULE, ar->req);
4548 if (ret != LDB_SUCCESS) {
4549 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
4550 ldb_dn_get_linearized(ar->search_msg->dn),
4551 ldb_dn_get_linearized(msg->dn),
4552 ldb_errstring(ldb_module_get_ctx(ar->module))));
4558 * On failure make the caller get the error
4559 * This means replication will stop with an error,
4560 * but there is not much else we can do. In the
4561 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
4565 talloc_free(tmp_ctx);
4570 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
4572 struct ldb_context *ldb;
4573 struct ldb_request *change_req;
4574 enum ndr_err_code ndr_err;
4575 struct ldb_message *msg;
4576 struct replPropertyMetaDataBlob *rmd;
4577 struct replPropertyMetaDataBlob omd;
4578 const struct ldb_val *omd_value;
4579 struct replPropertyMetaDataBlob nmd;
4580 struct ldb_val nmd_value;
4581 struct GUID remote_parent_guid;
4584 unsigned int removed_attrs = 0;
4586 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
4587 bool isDeleted = false;
4588 bool local_isDeleted = false;
4589 bool remote_isDeleted = false;
4590 bool take_remote_isDeleted = false;
4591 bool sd_updated = false;
4592 bool renamed = false;
4595 ldb = ldb_module_get_ctx(ar->module);
4596 msg = ar->objs->objects[ar->index_current].msg;
4598 rmd = ar->objs->objects[ar->index_current].meta_data;
4602 /* find existing meta data */
4603 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
4605 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
4606 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4607 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4608 nt_status = ndr_map_error2ntstatus(ndr_err);
4609 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4612 if (omd.version != 1) {
4613 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4617 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
4618 "isDeleted", false);
4619 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4620 "isDeleted", false);
4623 * Fill in the remote_parent_guid with the GUID or an all-zero
4626 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
4627 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
4629 remote_parent_guid = GUID_zero();
4633 * To ensure we follow a complex rename chain around, we have
4634 * to confirm that the DN is the same (mostly to confirm the
4635 * RDN) and the parentGUID is the same.
4637 * This ensures we keep things under the correct parent, which
4638 * replmd_replicated_handle_rename() will do.
4641 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
4642 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
4646 * handle renames, even just by case that come in over
4647 * DRS. Changes in the parent DN don't hit us here,
4648 * because the search for a parent will clean up those
4651 * We also have already filtered out the case where
4652 * the peer has an older name to what we have (see
4653 * replmd_replicated_apply_search_callback())
4656 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
4659 if (ret != LDB_SUCCESS) {
4660 ldb_debug(ldb, LDB_DEBUG_FATAL,
4661 "replmd_replicated_request rename %s => %s failed - %s\n",
4662 ldb_dn_get_linearized(ar->search_msg->dn),
4663 ldb_dn_get_linearized(msg->dn),
4664 ldb_errstring(ldb));
4665 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4668 if (renamed == true) {
4670 * Set the callback to one that will fix up the name
4671 * metadata on the new conflict DN
4673 callback = replmd_op_name_modify_callback;
4678 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
4679 nmd.ctr.ctr1.array = talloc_array(ar,
4680 struct replPropertyMetaData1,
4681 nmd.ctr.ctr1.count);
4682 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4684 /* first copy the old meta data */
4685 for (i=0; i < omd.ctr.ctr1.count; i++) {
4686 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
4691 /* now merge in the new meta data */
4692 for (i=0; i < rmd->ctr.ctr1.count; i++) {
4695 for (j=0; j < ni; j++) {
4698 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
4702 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4704 * if we compare equal then do an
4705 * update. This is used when a client
4706 * asks for a FULL_SYNC, and can be
4707 * used to recover a corrupt
4710 * This call is a bit tricky, what we
4711 * are doing it turning the 'is_newer'
4712 * call into a 'not is older' by
4713 * swapping i and j, and negating the
4716 cmp = !replmd_replPropertyMetaData1_is_newer(&rmd->ctr.ctr1.array[i],
4717 &nmd.ctr.ctr1.array[j]);
4719 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
4720 &rmd->ctr.ctr1.array[i]);
4723 /* replace the entry */
4724 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
4725 if (ar->seq_num == 0) {
4726 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4727 if (ret != LDB_SUCCESS) {
4728 return replmd_replicated_request_error(ar, ret);
4731 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
4732 switch (nmd.ctr.ctr1.array[j].attid) {
4733 case DRSUAPI_ATTID_ntSecurityDescriptor:
4736 case DRSUAPI_ATTID_isDeleted:
4737 take_remote_isDeleted = true;
4746 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
4747 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
4748 msg->elements[i-removed_attrs].name,
4749 ldb_dn_get_linearized(msg->dn),
4750 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
4753 /* we don't want to apply this change so remove the attribute */
4754 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
4761 if (found) continue;
4763 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
4764 if (ar->seq_num == 0) {
4765 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4766 if (ret != LDB_SUCCESS) {
4767 return replmd_replicated_request_error(ar, ret);
4770 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
4771 switch (nmd.ctr.ctr1.array[ni].attid) {
4772 case DRSUAPI_ATTID_ntSecurityDescriptor:
4775 case DRSUAPI_ATTID_isDeleted:
4776 take_remote_isDeleted = true;
4785 * finally correct the size of the meta_data array
4787 nmd.ctr.ctr1.count = ni;
4790 * the rdn attribute (the alias for the name attribute),
4791 * 'cn' for most objects is the last entry in the meta data array
4794 * sort the new meta data array
4796 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, ar->schema, msg->dn);
4797 if (ret != LDB_SUCCESS) {
4798 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
4803 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
4806 * This also controls SD propagation below
4808 if (take_remote_isDeleted) {
4809 isDeleted = remote_isDeleted;
4811 isDeleted = local_isDeleted;
4814 ar->isDeleted = isDeleted;
4817 * check if some replicated attributes left, otherwise skip the ldb_modify() call
4819 if (msg->num_elements == 0) {
4820 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
4823 return replmd_replicated_apply_isDeleted(ar);
4826 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
4827 ar->index_current, msg->num_elements);
4833 if (sd_updated && !isDeleted) {
4834 ret = dsdb_module_schedule_sd_propagation(ar->module,
4835 ar->objs->partition_dn,
4837 if (ret != LDB_SUCCESS) {
4838 return ldb_operr(ldb);
4842 /* create the meta data value */
4843 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
4844 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
4845 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4846 nt_status = ndr_map_error2ntstatus(ndr_err);
4847 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4851 * when we know that we'll modify the record, add the whenChanged, uSNChanged
4852 * and replPopertyMetaData attributes
4854 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4855 if (ret != LDB_SUCCESS) {
4856 return replmd_replicated_request_error(ar, ret);
4858 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4859 if (ret != LDB_SUCCESS) {
4860 return replmd_replicated_request_error(ar, ret);
4862 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
4863 if (ret != LDB_SUCCESS) {
4864 return replmd_replicated_request_error(ar, ret);
4867 replmd_ldb_message_sort(msg, ar->schema);
4869 /* we want to replace the old values */
4870 for (i=0; i < msg->num_elements; i++) {
4871 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
4872 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
4873 if (msg->elements[i].num_values == 0) {
4874 ldb_asprintf_errstring(ldb, __location__
4875 ": objectClass removed on %s, aborting replication\n",
4876 ldb_dn_get_linearized(msg->dn));
4877 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
4883 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4884 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
4888 ret = ldb_build_mod_req(&change_req,
4896 LDB_REQ_SET_LOCATION(change_req);
4897 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4899 /* current partition control needed by "repmd_op_callback" */
4900 ret = ldb_request_add_control(change_req,
4901 DSDB_CONTROL_CURRENT_PARTITION_OID,
4903 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4905 return ldb_next_request(ar->module, change_req);
4908 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
4909 struct ldb_reply *ares)
4911 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4912 struct replmd_replicated_request);
4916 return ldb_module_done(ar->req, NULL, NULL,
4917 LDB_ERR_OPERATIONS_ERROR);
4919 if (ares->error != LDB_SUCCESS &&
4920 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4921 return ldb_module_done(ar->req, ares->controls,
4922 ares->response, ares->error);
4925 switch (ares->type) {
4926 case LDB_REPLY_ENTRY:
4927 ar->search_msg = talloc_steal(ar, ares->message);
4930 case LDB_REPLY_REFERRAL:
4931 /* we ignore referrals */
4934 case LDB_REPLY_DONE:
4936 struct replPropertyMetaData1 *md_remote;
4937 struct replPropertyMetaData1 *md_local;
4939 struct replPropertyMetaDataBlob omd;
4940 const struct ldb_val *omd_value;
4941 struct replPropertyMetaDataBlob *rmd;
4942 struct ldb_message *msg;
4944 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
4945 ar->objs->objects[ar->index_current].last_known_parent = NULL;
4948 * This is the ADD case, find the appropriate parent,
4949 * as this object doesn't exist locally:
4951 if (ar->search_msg == NULL) {
4952 ret = replmd_replicated_apply_search_for_parent(ar);
4953 if (ret != LDB_SUCCESS) {
4954 return ldb_module_done(ar->req, NULL, NULL, ret);
4961 * Otherwise, in the MERGE case, work out if we are
4962 * attempting a rename, and if so find the parent the
4963 * newly renamed object wants to belong under (which
4964 * may not be the parent in it's attached string DN
4966 rmd = ar->objs->objects[ar->index_current].meta_data;
4970 /* find existing meta data */
4971 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
4973 enum ndr_err_code ndr_err;
4974 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
4975 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4976 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4977 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4978 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4981 if (omd.version != 1) {
4982 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4986 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
4988 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
4989 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
4990 && GUID_all_zero(&ar->local_parent_guid)) {
4991 DEBUG(0, ("Refusing to replicate new version of %s "
4992 "as local object has an all-zero parentGUID attribute, "
4993 "despite not being an NC root\n",
4994 ldb_dn_get_linearized(ar->search_msg->dn)));
4995 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4999 * now we need to check for double renames. We could have a
5000 * local rename pending which our replication partner hasn't
5001 * received yet. We choose which one wins by looking at the
5002 * attribute stamps on the two objects, the newer one wins.
5004 * This also simply applies the correct algorithms for
5005 * determining if a change was made to name at all, or
5006 * if the object has just been renamed under the same
5009 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5010 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5011 /* if there is no name attribute then we have to assume the
5012 object we've received is in fact newer */
5013 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING ||
5014 !md_remote || !md_local ||
5015 replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
5016 struct GUID_txt_buf p_guid_local;
5017 struct GUID_txt_buf p_guid_remote;
5018 msg = ar->objs->objects[ar->index_current].msg;
5020 /* Otherwise, just merge on the existing object, force no rename */
5022 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
5023 "as incoming object changing to %s under %s\n",
5024 ldb_dn_get_linearized(ar->search_msg->dn),
5025 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5026 ldb_dn_get_linearized(msg->dn),
5027 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5029 ret = replmd_replicated_apply_search_for_parent(ar);
5031 struct GUID_txt_buf p_guid_local;
5032 struct GUID_txt_buf p_guid_remote;
5033 msg = ar->objs->objects[ar->index_current].msg;
5035 /* Otherwise, just merge on the existing object, force no rename */
5037 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
5038 ldb_dn_get_linearized(msg->dn)) == 0) {
5039 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
5040 GUID_equal(&ar->local_parent_guid,
5041 ar->objs->objects[ar->index_current].parent_guid)
5043 DEBUG(4,(__location__ ": Keeping object %s at under %s "
5044 "despite incoming object changing parent to %s\n",
5045 ldb_dn_get_linearized(ar->search_msg->dn),
5046 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5047 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5051 DEBUG(4,(__location__ ": Keeping object %s at under %s "
5052 " and rejecting older rename to %s under %s\n",
5053 ldb_dn_get_linearized(ar->search_msg->dn),
5054 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5055 ldb_dn_get_linearized(msg->dn),
5056 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5060 * This assignment ensures that the strcmp()
5061 * and GUID_equal() calls in
5062 * replmd_replicated_apply_merge() avoids the
5065 ar->objs->objects[ar->index_current].parent_guid =
5066 &ar->local_parent_guid;
5068 msg->dn = ar->search_msg->dn;
5069 ret = replmd_replicated_apply_merge(ar);
5071 if (ret != LDB_SUCCESS) {
5072 return ldb_module_done(ar->req, NULL, NULL, ret);
5081 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
5083 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
5085 struct ldb_context *ldb;
5089 struct ldb_request *search_req;
5090 static const char *attrs[] = { "*", "parentGUID", "instanceType",
5091 "replPropertyMetaData", "nTSecurityDescriptor",
5093 struct GUID_txt_buf guid_str_buf;
5095 if (ar->index_current >= ar->objs->num_objects) {
5096 /* done with it, go to next stage */
5097 return replmd_replicated_uptodate_vector(ar);
5100 ldb = ldb_module_get_ctx(ar->module);
5101 ar->search_msg = NULL;
5102 ar->isDeleted = false;
5104 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5107 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5108 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5110 ret = ldb_build_search_req(&search_req,
5113 ar->objs->partition_dn,
5119 replmd_replicated_apply_search_callback,
5121 LDB_REQ_SET_LOCATION(search_req);
5123 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
5125 if (ret != LDB_SUCCESS) {
5129 return ldb_next_request(ar->module, search_req);
5133 * This is essentially a wrapper for replmd_replicated_apply_next()
5135 * This is needed to ensure that both codepaths call this handler.
5137 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
5139 struct ldb_dn *deleted_objects_dn;
5140 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5141 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
5142 &deleted_objects_dn);
5143 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
5145 * Do a delete here again, so that if there is
5146 * anything local that conflicts with this
5147 * object being deleted, it is removed. This
5148 * includes links. See MS-DRSR 4.1.10.6.9
5151 * If the object is already deleted, and there
5152 * is no more work required, it doesn't do
5156 /* This has been updated to point to the DN we eventually did the modify on */
5158 struct ldb_request *del_req;
5159 struct ldb_result *res;
5161 TALLOC_CTX *tmp_ctx = talloc_new(ar);
5163 ret = ldb_oom(ldb_module_get_ctx(ar->module));
5167 res = talloc_zero(tmp_ctx, struct ldb_result);
5169 ret = ldb_oom(ldb_module_get_ctx(ar->module));
5170 talloc_free(tmp_ctx);
5174 /* Build a delete request, which hopefully will artually turn into nothing */
5175 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
5179 ldb_modify_default_callback,
5181 LDB_REQ_SET_LOCATION(del_req);
5182 if (ret != LDB_SUCCESS) {
5183 talloc_free(tmp_ctx);
5188 * This is the guts of the call, call back
5189 * into our delete code, but setting the
5190 * re_delete flag so we delete anything that
5191 * shouldn't be there on a deleted or recycled
5194 ret = replmd_delete_internals(ar->module, del_req, true);
5195 if (ret == LDB_SUCCESS) {
5196 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
5199 talloc_free(tmp_ctx);
5200 if (ret != LDB_SUCCESS) {
5205 ar->index_current++;
5206 return replmd_replicated_apply_next(ar);
5209 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
5210 struct ldb_reply *ares)
5212 struct ldb_context *ldb;
5213 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5214 struct replmd_replicated_request);
5215 ldb = ldb_module_get_ctx(ar->module);
5218 return ldb_module_done(ar->req, NULL, NULL,
5219 LDB_ERR_OPERATIONS_ERROR);
5221 if (ares->error != LDB_SUCCESS) {
5222 return ldb_module_done(ar->req, ares->controls,
5223 ares->response, ares->error);
5226 if (ares->type != LDB_REPLY_DONE) {
5227 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
5228 return ldb_module_done(ar->req, NULL, NULL,
5229 LDB_ERR_OPERATIONS_ERROR);
5234 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5237 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
5239 struct ldb_context *ldb;
5240 struct ldb_request *change_req;
5241 enum ndr_err_code ndr_err;
5242 struct ldb_message *msg;
5243 struct replUpToDateVectorBlob ouv;
5244 const struct ldb_val *ouv_value;
5245 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
5246 struct replUpToDateVectorBlob nuv;
5247 struct ldb_val nuv_value;
5248 struct ldb_message_element *nuv_el = NULL;
5249 struct ldb_message_element *orf_el = NULL;
5250 struct repsFromToBlob nrf;
5251 struct ldb_val *nrf_value = NULL;
5252 struct ldb_message_element *nrf_el = NULL;
5256 time_t t = time(NULL);
5259 uint32_t instanceType;
5261 ldb = ldb_module_get_ctx(ar->module);
5262 ruv = ar->objs->uptodateness_vector;
5268 unix_to_nt_time(&now, t);
5270 if (ar->search_msg == NULL) {
5271 /* this happens for a REPL_OBJ call where we are
5272 creating the target object by replicating it. The
5273 subdomain join code does this for the partition DN
5275 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
5276 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5279 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
5280 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
5281 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
5282 ldb_dn_get_linearized(ar->search_msg->dn)));
5283 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5287 * first create the new replUpToDateVector
5289 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
5291 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
5292 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
5293 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5294 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5295 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5298 if (ouv.version != 2) {
5299 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5304 * the new uptodateness vector will at least
5305 * contain 1 entry, one for the source_dsa
5307 * plus optional values from our old vector and the one from the source_dsa
5309 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
5310 if (ruv) nuv.ctr.ctr2.count += ruv->count;
5311 nuv.ctr.ctr2.cursors = talloc_array(ar,
5312 struct drsuapi_DsReplicaCursor2,
5313 nuv.ctr.ctr2.count);
5314 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5316 /* first copy the old vector */
5317 for (i=0; i < ouv.ctr.ctr2.count; i++) {
5318 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
5322 /* merge in the source_dsa vector is available */
5323 for (i=0; (ruv && i < ruv->count); i++) {
5326 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
5327 &ar->our_invocation_id)) {
5331 for (j=0; j < ni; j++) {
5332 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
5333 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
5339 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
5340 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
5345 if (found) continue;
5347 /* if it's not there yet, add it */
5348 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
5353 * finally correct the size of the cursors array
5355 nuv.ctr.ctr2.count = ni;
5360 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
5363 * create the change ldb_message
5365 msg = ldb_msg_new(ar);
5366 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5367 msg->dn = ar->search_msg->dn;
5369 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
5370 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
5371 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5372 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5373 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5375 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
5376 if (ret != LDB_SUCCESS) {
5377 return replmd_replicated_request_error(ar, ret);
5379 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
5382 * now create the new repsFrom value from the given repsFromTo1 structure
5386 nrf.ctr.ctr1 = *ar->objs->source_dsa;
5387 nrf.ctr.ctr1.last_attempt = now;
5388 nrf.ctr.ctr1.last_success = now;
5389 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
5392 * first see if we already have a repsFrom value for the current source dsa
5393 * if so we'll later replace this value
5395 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
5397 for (i=0; i < orf_el->num_values; i++) {
5398 struct repsFromToBlob *trf;
5400 trf = talloc(ar, struct repsFromToBlob);
5401 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5403 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
5404 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
5405 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5406 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5407 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5410 if (trf->version != 1) {
5411 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5415 * we compare the source dsa objectGUID not the invocation_id
5416 * because we want only one repsFrom value per source dsa
5417 * and when the invocation_id of the source dsa has changed we don't need
5418 * the old repsFrom with the old invocation_id
5420 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
5421 &ar->objs->source_dsa->source_dsa_obj_guid)) {
5427 nrf_value = &orf_el->values[i];
5432 * copy over all old values to the new ldb_message
5434 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
5435 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5440 * if we haven't found an old repsFrom value for the current source dsa
5441 * we'll add a new value
5444 struct ldb_val zero_value;
5445 ZERO_STRUCT(zero_value);
5446 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
5447 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5449 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
5452 /* we now fill the value which is already attached to ldb_message */
5453 ndr_err = ndr_push_struct_blob(nrf_value, msg,
5455 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
5456 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5457 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5458 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5462 * the ldb_message_element for the attribute, has all the old values and the new one
5463 * so we'll replace the whole attribute with all values
5465 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
5467 if (CHECK_DEBUGLVL(4)) {
5468 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5469 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
5473 /* prepare the ldb_modify() request */
5474 ret = ldb_build_mod_req(&change_req,
5480 replmd_replicated_uptodate_modify_callback,
5482 LDB_REQ_SET_LOCATION(change_req);
5483 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5485 return ldb_next_request(ar->module, change_req);
5488 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
5489 struct ldb_reply *ares)
5491 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5492 struct replmd_replicated_request);
5496 return ldb_module_done(ar->req, NULL, NULL,
5497 LDB_ERR_OPERATIONS_ERROR);
5499 if (ares->error != LDB_SUCCESS &&
5500 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5501 return ldb_module_done(ar->req, ares->controls,
5502 ares->response, ares->error);
5505 switch (ares->type) {
5506 case LDB_REPLY_ENTRY:
5507 ar->search_msg = talloc_steal(ar, ares->message);
5510 case LDB_REPLY_REFERRAL:
5511 /* we ignore referrals */
5514 case LDB_REPLY_DONE:
5515 ret = replmd_replicated_uptodate_modify(ar);
5516 if (ret != LDB_SUCCESS) {
5517 return ldb_module_done(ar->req, NULL, NULL, ret);
5526 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
5528 struct ldb_context *ldb;
5530 static const char *attrs[] = {
5531 "replUpToDateVector",
5536 struct ldb_request *search_req;
5538 ldb = ldb_module_get_ctx(ar->module);
5539 ar->search_msg = NULL;
5541 ret = ldb_build_search_req(&search_req,
5544 ar->objs->partition_dn,
5550 replmd_replicated_uptodate_search_callback,
5552 LDB_REQ_SET_LOCATION(search_req);
5553 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5555 return ldb_next_request(ar->module, search_req);
5560 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
5562 struct ldb_context *ldb;
5563 struct dsdb_extended_replicated_objects *objs;
5564 struct replmd_replicated_request *ar;
5565 struct ldb_control **ctrls;
5568 struct replmd_private *replmd_private =
5569 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5570 struct dsdb_control_replicated_update *rep_update;
5572 ldb = ldb_module_get_ctx(module);
5574 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
5576 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
5578 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
5579 return LDB_ERR_PROTOCOL_ERROR;
5582 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
5583 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
5584 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
5585 return LDB_ERR_PROTOCOL_ERROR;
5588 ar = replmd_ctx_init(module, req);
5590 return LDB_ERR_OPERATIONS_ERROR;
5592 /* Set the flags to have the replmd_op_callback run over the full set of objects */
5593 ar->apply_mode = true;
5595 ar->schema = dsdb_get_schema(ldb, ar);
5597 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
5599 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
5600 return LDB_ERR_CONSTRAINT_VIOLATION;
5603 ctrls = req->controls;
5605 if (req->controls) {
5606 req->controls = talloc_memdup(ar, req->controls,
5607 talloc_get_size(req->controls));
5608 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5611 /* This allows layers further down to know if a change came in
5612 over replication and what the replication flags were */
5613 rep_update = talloc_zero(ar, struct dsdb_control_replicated_update);
5614 if (rep_update == NULL) {
5615 return ldb_module_oom(module);
5617 rep_update->dsdb_repl_flags = objs->dsdb_repl_flags;
5619 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, rep_update);
5620 if (ret != LDB_SUCCESS) {
5624 /* If this change contained linked attributes in the body
5625 * (rather than in the links section) we need to update
5626 * backlinks in linked_attributes */
5627 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
5628 if (ret != LDB_SUCCESS) {
5632 ar->controls = req->controls;
5633 req->controls = ctrls;
5635 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
5637 /* save away the linked attributes for the end of the
5639 for (i=0; i<ar->objs->linked_attributes_count; i++) {
5640 struct la_entry *la_entry;
5642 if (replmd_private->la_ctx == NULL) {
5643 replmd_private->la_ctx = talloc_new(replmd_private);
5645 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
5646 if (la_entry == NULL) {
5648 return LDB_ERR_OPERATIONS_ERROR;
5650 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
5651 if (la_entry->la == NULL) {
5652 talloc_free(la_entry);
5654 return LDB_ERR_OPERATIONS_ERROR;
5656 *la_entry->la = ar->objs->linked_attributes[i];
5658 /* we need to steal the non-scalars so they stay
5659 around until the end of the transaction */
5660 talloc_steal(la_entry->la, la_entry->la->identifier);
5661 talloc_steal(la_entry->la, la_entry->la->value.blob);
5663 DLIST_ADD(replmd_private->la_list, la_entry);
5666 return replmd_replicated_apply_next(ar);
5670 process one linked attribute structure
5672 static int replmd_process_linked_attribute(struct ldb_module *module,
5673 struct la_entry *la_entry,
5674 struct ldb_request *parent)
5676 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
5677 struct ldb_context *ldb = ldb_module_get_ctx(module);
5678 struct ldb_message *msg;
5679 struct ldb_message *target_msg = NULL;
5680 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
5681 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
5683 const struct dsdb_attribute *attr;
5684 struct dsdb_dn *dsdb_dn;
5685 uint64_t seq_num = 0;
5686 struct ldb_message_element *old_el;
5688 time_t t = time(NULL);
5689 struct ldb_result *res;
5690 struct ldb_result *target_res;
5691 const char *attrs[4];
5692 const char *attrs2[] = { "isDeleted", "isRecycled", NULL };
5693 struct parsed_dn *pdn_list, *pdn;
5694 struct GUID guid = GUID_zero();
5696 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
5697 const struct GUID *our_invocation_id;
5699 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
5700 enum deletion_state target_deletion_state = OBJECT_NOT_DELETED;
5703 linked_attributes[0]:
5704 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
5706 identifier: struct drsuapi_DsReplicaObjectIdentifier
5707 __ndr_size : 0x0000003a (58)
5708 __ndr_size_sid : 0x00000000 (0)
5709 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
5711 __ndr_size_dn : 0x00000000 (0)
5713 attid : DRSUAPI_ATTID_member (0x1F)
5714 value: struct drsuapi_DsAttributeValue
5715 __ndr_size : 0x0000007e (126)
5717 blob : DATA_BLOB length=126
5718 flags : 0x00000001 (1)
5719 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
5720 originating_add_time : Wed Sep 2 22:20:01 2009 EST
5721 meta_data: struct drsuapi_DsReplicaMetaData
5722 version : 0x00000015 (21)
5723 originating_change_time : Wed Sep 2 23:39:07 2009 EST
5724 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
5725 originating_usn : 0x000000000001e19c (123292)
5727 (for cases where the link is to a normal DN)
5728 &target: struct drsuapi_DsReplicaObjectIdentifier3
5729 __ndr_size : 0x0000007e (126)
5730 __ndr_size_sid : 0x0000001c (28)
5731 guid : 7639e594-db75-4086-b0d4-67890ae46031
5732 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
5733 __ndr_size_dn : 0x00000022 (34)
5734 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
5737 /* find the attribute being modified */
5738 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
5740 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
5741 talloc_free(tmp_ctx);
5742 return LDB_ERR_OPERATIONS_ERROR;
5745 attrs[0] = attr->lDAPDisplayName;
5746 attrs[1] = "isDeleted";
5747 attrs[2] = "isRecycled";
5750 /* get the existing message from the db for the object with
5751 this GUID, returning attribute being modified. We will then
5752 use this msg as the basis for a modify call */
5753 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
5754 DSDB_FLAG_NEXT_MODULE |
5755 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5756 DSDB_SEARCH_SHOW_RECYCLED |
5757 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
5758 DSDB_SEARCH_REVEAL_INTERNALS,
5760 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
5761 if (ret != LDB_SUCCESS) {
5762 talloc_free(tmp_ctx);
5765 if (res->count != 1) {
5766 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
5767 GUID_string(tmp_ctx, &la->identifier->guid));
5768 talloc_free(tmp_ctx);
5769 return LDB_ERR_NO_SUCH_OBJECT;
5774 * Check for deleted objects per MS-DRSR 4.1.10.6.13
5775 * ProcessLinkValue, because link updates are not applied to
5776 * recycled and tombstone objects. We don't have to delete
5777 * any existing link, that should have happened when the
5778 * object deletion was replicated or initiated.
5781 replmd_deletion_state(module, msg, &deletion_state, NULL);
5783 if (deletion_state >= OBJECT_RECYCLED) {
5784 talloc_free(tmp_ctx);
5788 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
5789 if (old_el == NULL) {
5790 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
5791 if (ret != LDB_SUCCESS) {
5792 ldb_module_oom(module);
5793 talloc_free(tmp_ctx);
5794 return LDB_ERR_OPERATIONS_ERROR;
5797 old_el->flags = LDB_FLAG_MOD_REPLACE;
5800 /* parse the existing links */
5801 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
5802 if (ret != LDB_SUCCESS) {
5803 talloc_free(tmp_ctx);
5807 /* get our invocationId */
5808 our_invocation_id = samdb_ntds_invocation_id(ldb);
5809 if (!our_invocation_id) {
5810 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
5811 talloc_free(tmp_ctx);
5812 return LDB_ERR_OPERATIONS_ERROR;
5815 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
5816 if (ret != LDB_SUCCESS) {
5817 talloc_free(tmp_ctx);
5821 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
5822 if (!W_ERROR_IS_OK(status)) {
5823 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
5824 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
5825 talloc_free(tmp_ctx);
5826 return LDB_ERR_OPERATIONS_ERROR;
5829 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
5830 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
5832 * This strange behaviour (allowing a NULL/missing
5833 * GUID) originally comes from:
5835 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
5836 * Author: Andrew Tridgell <tridge@samba.org>
5837 * Date: Mon Dec 21 21:21:55 2009 +1100
5839 * s4-drs: cope better with NULL GUIDS from DRS
5841 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
5842 * need to match by DN if possible when seeing if we should update an
5845 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
5848 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
5849 dsdb_dn->dn, attrs2,
5850 DSDB_FLAG_NEXT_MODULE |
5851 DSDB_SEARCH_SHOW_RECYCLED |
5852 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5853 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
5855 } else if (!NT_STATUS_IS_OK(ntstatus)) {
5856 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
5858 ldb_dn_get_linearized(dsdb_dn->dn),
5859 ldb_dn_get_linearized(msg->dn));
5860 talloc_free(tmp_ctx);
5861 return LDB_ERR_OPERATIONS_ERROR;
5863 ret = dsdb_module_search(module, tmp_ctx, &target_res,
5864 NULL, LDB_SCOPE_SUBTREE,
5866 DSDB_FLAG_NEXT_MODULE |
5867 DSDB_SEARCH_SHOW_RECYCLED |
5868 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5869 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
5872 GUID_string(tmp_ctx, &guid));
5875 if (ret != LDB_SUCCESS) {
5876 ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to re-resolve GUID %s: %s\n",
5877 GUID_string(tmp_ctx, &guid),
5878 ldb_errstring(ldb_module_get_ctx(module)));
5879 talloc_free(tmp_ctx);
5883 if (target_res->count == 0) {
5884 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
5885 GUID_string(tmp_ctx, &guid),
5886 ldb_dn_get_linearized(dsdb_dn->dn)));
5887 } else if (target_res->count != 1) {
5888 ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching objectGUID %s\n",
5889 GUID_string(tmp_ctx, &guid));
5890 talloc_free(tmp_ctx);
5891 return LDB_ERR_OPERATIONS_ERROR;
5893 target_msg = target_res->msgs[0];
5894 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
5898 * Check for deleted objects per MS-DRSR 4.1.10.6.13
5899 * ProcessLinkValue, because link updates are not applied to
5900 * recycled and tombstone objects. We don't have to delete
5901 * any existing link, that should have happened when the
5902 * object deletion was replicated or initiated.
5904 replmd_deletion_state(module, target_msg,
5905 &target_deletion_state, NULL);
5907 if (target_deletion_state >= OBJECT_RECYCLED) {
5908 talloc_free(tmp_ctx);
5912 /* see if this link already exists */
5913 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
5915 /* see if this update is newer than what we have already */
5916 struct GUID invocation_id = GUID_zero();
5917 uint32_t version = 0;
5918 uint32_t originating_usn = 0;
5919 NTTIME change_time = 0;
5920 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
5922 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
5923 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
5924 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
5925 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
5927 if (!replmd_update_is_newer(&invocation_id,
5928 &la->meta_data.originating_invocation_id,
5930 la->meta_data.version,
5932 la->meta_data.originating_change_time)) {
5933 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
5934 old_el->name, ldb_dn_get_linearized(msg->dn),
5935 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
5936 talloc_free(tmp_ctx);
5940 /* get a seq_num for this change */
5941 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
5942 if (ret != LDB_SUCCESS) {
5943 talloc_free(tmp_ctx);
5947 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
5948 /* remove the existing backlink */
5949 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
5950 if (ret != LDB_SUCCESS) {
5951 talloc_free(tmp_ctx);
5956 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
5957 &la->meta_data.originating_invocation_id,
5958 la->meta_data.originating_usn, seq_num,
5959 la->meta_data.originating_change_time,
5960 la->meta_data.version,
5962 if (ret != LDB_SUCCESS) {
5963 talloc_free(tmp_ctx);
5968 /* add the new backlink */
5969 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
5970 if (ret != LDB_SUCCESS) {
5971 talloc_free(tmp_ctx);
5976 /* get a seq_num for this change */
5977 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
5978 if (ret != LDB_SUCCESS) {
5979 talloc_free(tmp_ctx);
5983 old_el->values = talloc_realloc(msg->elements, old_el->values,
5984 struct ldb_val, old_el->num_values+1);
5985 if (!old_el->values) {
5986 ldb_module_oom(module);
5987 return LDB_ERR_OPERATIONS_ERROR;
5989 old_el->num_values++;
5991 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
5992 &la->meta_data.originating_invocation_id,
5993 la->meta_data.originating_usn, seq_num,
5994 la->meta_data.originating_change_time,
5995 la->meta_data.version,
5996 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
5997 if (ret != LDB_SUCCESS) {
5998 talloc_free(tmp_ctx);
6003 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
6005 if (ret != LDB_SUCCESS) {
6006 talloc_free(tmp_ctx);
6012 /* we only change whenChanged and uSNChanged if the seq_num
6014 ret = add_time_element(msg, "whenChanged", t);
6015 if (ret != LDB_SUCCESS) {
6016 talloc_free(tmp_ctx);
6021 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
6022 if (ret != LDB_SUCCESS) {
6023 talloc_free(tmp_ctx);
6028 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
6029 if (old_el == NULL) {
6030 talloc_free(tmp_ctx);
6031 return ldb_operr(ldb);
6034 ret = dsdb_check_single_valued_link(attr, old_el);
6035 if (ret != LDB_SUCCESS) {
6036 talloc_free(tmp_ctx);
6040 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
6042 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
6043 if (ret != LDB_SUCCESS) {
6044 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
6046 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
6047 talloc_free(tmp_ctx);
6051 talloc_free(tmp_ctx);
6056 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
6058 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
6059 return replmd_extended_replicated_objects(module, req);
6062 return ldb_next_request(module, req);
6067 we hook into the transaction operations to allow us to
6068 perform the linked attribute updates at the end of the whole
6069 transaction. This allows a forward linked attribute to be created
6070 before the object is created. During a vampire, w2k8 sends us linked
6071 attributes before the objects they are part of.
6073 static int replmd_start_transaction(struct ldb_module *module)
6075 /* create our private structure for this transaction */
6076 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
6077 struct replmd_private);
6078 replmd_txn_cleanup(replmd_private);
6080 /* free any leftover mod_usn records from cancelled
6082 while (replmd_private->ncs) {
6083 struct nc_entry *e = replmd_private->ncs;
6084 DLIST_REMOVE(replmd_private->ncs, e);
6088 return ldb_next_start_trans(module);
6092 on prepare commit we loop over our queued la_context structures and
6095 static int replmd_prepare_commit(struct ldb_module *module)
6097 struct replmd_private *replmd_private =
6098 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6099 struct la_entry *la, *prev;
6100 struct la_backlink *bl;
6103 /* walk the list backwards, to do the first entry first, as we
6104 * added the entries with DLIST_ADD() which puts them at the
6105 * start of the list */
6106 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
6107 prev = DLIST_PREV(la);
6108 DLIST_REMOVE(replmd_private->la_list, la);
6109 ret = replmd_process_linked_attribute(module, la, NULL);
6110 if (ret != LDB_SUCCESS) {
6111 replmd_txn_cleanup(replmd_private);
6116 /* process our backlink list, creating and deleting backlinks
6118 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
6119 ret = replmd_process_backlink(module, bl, NULL);
6120 if (ret != LDB_SUCCESS) {
6121 replmd_txn_cleanup(replmd_private);
6126 replmd_txn_cleanup(replmd_private);
6128 /* possibly change @REPLCHANGED */
6129 ret = replmd_notify_store(module, NULL);
6130 if (ret != LDB_SUCCESS) {
6134 return ldb_next_prepare_commit(module);
6137 static int replmd_del_transaction(struct ldb_module *module)
6139 struct replmd_private *replmd_private =
6140 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6141 replmd_txn_cleanup(replmd_private);
6143 return ldb_next_del_trans(module);
6147 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
6148 .name = "repl_meta_data",
6149 .init_context = replmd_init,
6151 .modify = replmd_modify,
6152 .rename = replmd_rename,
6153 .del = replmd_delete,
6154 .extended = replmd_extended,
6155 .start_transaction = replmd_start_transaction,
6156 .prepare_commit = replmd_prepare_commit,
6157 .del_transaction = replmd_del_transaction,
6160 int ldb_repl_meta_data_module_init(const char *version)
6162 LDB_MODULE_CHECK_VERSION(version);
6163 return ldb_register_module(&ldb_repl_meta_data_module_ops);