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 nc_entry *prev, *next;
66 uint64_t mod_usn_urgent;
68 struct ldb_dn *schema_dn;
69 bool originating_updates;
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;
89 * Backlinks for the replmd_add() case (we want to create
90 * backlinks after creating the user, but before the end of
93 struct la_backlink *la_backlinks;
95 /* details for the mode where we apply a bunch of inbound replication meessages */
97 uint32_t index_current;
98 struct dsdb_extended_replicated_objects *objs;
100 struct ldb_message *search_msg;
101 struct GUID local_parent_guid;
110 struct dsdb_dn *dsdb_dn;
115 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
116 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
117 static int replmd_check_upgrade_links(struct ldb_context *ldb,
118 struct parsed_dn *dns, uint32_t count,
119 struct ldb_message_element *el,
120 const char *ldap_oid);
122 enum urgent_situation {
123 REPL_URGENT_ON_CREATE = 1,
124 REPL_URGENT_ON_UPDATE = 2,
125 REPL_URGENT_ON_DELETE = 4
128 enum deletion_state {
129 OBJECT_NOT_DELETED=1,
136 static void replmd_deletion_state(struct ldb_module *module,
137 const struct ldb_message *msg,
138 enum deletion_state *current_state,
139 enum deletion_state *next_state)
142 bool enabled = false;
145 *current_state = OBJECT_REMOVED;
146 if (next_state != NULL) {
147 *next_state = OBJECT_REMOVED;
152 ret = dsdb_recyclebin_enabled(module, &enabled);
153 if (ret != LDB_SUCCESS) {
157 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
159 *current_state = OBJECT_TOMBSTONE;
160 if (next_state != NULL) {
161 *next_state = OBJECT_REMOVED;
166 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
167 *current_state = OBJECT_RECYCLED;
168 if (next_state != NULL) {
169 *next_state = OBJECT_REMOVED;
174 *current_state = OBJECT_DELETED;
175 if (next_state != NULL) {
176 *next_state = OBJECT_RECYCLED;
181 *current_state = OBJECT_NOT_DELETED;
182 if (next_state == NULL) {
187 *next_state = OBJECT_DELETED;
189 *next_state = OBJECT_TOMBSTONE;
193 static const struct {
194 const char *update_name;
195 enum urgent_situation repl_situation;
196 } urgent_objects[] = {
197 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
198 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
199 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
200 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
201 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
202 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
206 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
207 static const char *urgent_attrs[] = {
210 "userAccountControl",
215 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
216 enum urgent_situation situation)
219 for (i=0; urgent_objects[i].update_name; i++) {
221 if ((situation & urgent_objects[i].repl_situation) == 0) {
225 for (j=0; j<objectclass_el->num_values; j++) {
226 const struct ldb_val *v = &objectclass_el->values[j];
227 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
235 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
237 if (ldb_attr_in_list(urgent_attrs, el->name)) {
244 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
247 initialise the module
248 allocate the private structure and build the list
249 of partition DNs for use by replmd_notify()
251 static int replmd_init(struct ldb_module *module)
253 struct replmd_private *replmd_private;
254 struct ldb_context *ldb = ldb_module_get_ctx(module);
255 static const char *samba_dsdb_attrs[] = { SAMBA_COMPATIBLE_FEATURES_ATTR, NULL };
256 struct ldb_dn *samba_dsdb_dn;
257 struct ldb_result *res;
259 TALLOC_CTX *frame = talloc_stackframe();
260 replmd_private = talloc_zero(module, struct replmd_private);
261 if (replmd_private == NULL) {
264 return LDB_ERR_OPERATIONS_ERROR;
266 ldb_module_set_private(module, replmd_private);
268 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
270 samba_dsdb_dn = ldb_dn_new(frame, ldb, "@SAMBA_DSDB");
271 if (!samba_dsdb_dn) {
276 ret = dsdb_module_search_dn(module, frame, &res, samba_dsdb_dn,
277 samba_dsdb_attrs, DSDB_FLAG_NEXT_MODULE, NULL);
278 if (ret == LDB_SUCCESS) {
279 replmd_private->sorted_links
280 = ldb_msg_check_string_attribute(res->msgs[0],
281 SAMBA_COMPATIBLE_FEATURES_ATTR,
282 SAMBA_SORTED_LINKS_FEATURE);
286 return ldb_next_init(module);
290 cleanup our per-transaction contexts
292 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
294 talloc_free(replmd_private->la_ctx);
295 replmd_private->la_list = NULL;
296 replmd_private->la_ctx = NULL;
302 struct la_backlink *next, *prev;
303 const char *attr_name;
304 struct ldb_dn *forward_dn;
305 struct GUID target_guid;
310 a ldb_modify request operating on modules below the
313 static int linked_attr_modify(struct ldb_module *module,
314 const struct ldb_message *message,
315 struct ldb_request *parent)
317 struct ldb_request *mod_req;
319 struct ldb_context *ldb = ldb_module_get_ctx(module);
320 TALLOC_CTX *tmp_ctx = talloc_new(module);
321 struct ldb_result *res;
323 res = talloc_zero(tmp_ctx, struct ldb_result);
325 talloc_free(tmp_ctx);
326 return ldb_oom(ldb_module_get_ctx(module));
329 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
333 ldb_modify_default_callback,
335 LDB_REQ_SET_LOCATION(mod_req);
336 if (ret != LDB_SUCCESS) {
337 talloc_free(tmp_ctx);
341 ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
343 if (ret != LDB_SUCCESS) {
347 /* Run the new request */
348 ret = ldb_next_request(module, mod_req);
350 if (ret == LDB_SUCCESS) {
351 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
354 talloc_free(tmp_ctx);
359 process a backlinks we accumulated during a transaction, adding and
360 deleting the backlinks from the target objects
362 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
364 struct ldb_dn *target_dn, *source_dn;
366 struct ldb_context *ldb = ldb_module_get_ctx(module);
367 struct ldb_message *msg;
368 TALLOC_CTX *frame = talloc_stackframe();
374 - construct ldb_message
375 - either an add or a delete
377 ret = dsdb_module_dn_by_guid(module, frame, &bl->target_guid, &target_dn, parent);
378 if (ret != LDB_SUCCESS) {
379 struct GUID_txt_buf guid_str;
380 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
381 GUID_buf_string(&bl->target_guid, &guid_str)));
386 msg = ldb_msg_new(frame);
388 ldb_module_oom(module);
390 return LDB_ERR_OPERATIONS_ERROR;
393 source_dn = ldb_dn_copy(frame, bl->forward_dn);
395 ldb_module_oom(module);
397 return LDB_ERR_OPERATIONS_ERROR;
399 /* Filter down to the attributes we want in the backlink */
400 const char *accept[] = { "GUID", "SID", NULL };
401 ldb_dn_extended_filter(source_dn, accept);
404 /* construct a ldb_message for adding/deleting the backlink */
406 dn_string = ldb_dn_get_extended_linearized(frame, bl->forward_dn, 1);
408 ldb_module_oom(module);
410 return LDB_ERR_OPERATIONS_ERROR;
412 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
413 if (ret != LDB_SUCCESS) {
417 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
419 /* a backlink should never be single valued. Unfortunately the
420 exchange schema has a attribute
421 msExchBridgeheadedLocalConnectorsDNBL which is single
422 valued and a backlink. We need to cope with that by
423 ignoring the single value flag */
424 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
426 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
427 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
428 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
429 cope with possible corruption where the backlink has
430 already been removed */
431 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
432 ldb_dn_get_linearized(target_dn),
433 ldb_dn_get_linearized(source_dn),
434 ldb_errstring(ldb)));
436 } else if (ret != LDB_SUCCESS) {
437 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
438 bl->active?"add":"remove",
439 ldb_dn_get_linearized(source_dn),
440 ldb_dn_get_linearized(target_dn),
450 add a backlink to the list of backlinks to add/delete in the prepare
453 forward_dn is stolen onto the defereed context
455 static int replmd_defer_add_backlink(struct ldb_module *module,
456 struct replmd_private *replmd_private,
457 const struct dsdb_schema *schema,
458 struct replmd_replicated_request *ac,
459 struct ldb_dn *forward_dn,
460 struct GUID *target_guid, bool active,
461 const struct dsdb_attribute *schema_attr,
462 struct ldb_request *parent)
464 const struct dsdb_attribute *target_attr;
465 struct la_backlink *bl;
467 bl = talloc(ac, struct la_backlink);
469 ldb_module_oom(module);
470 return LDB_ERR_OPERATIONS_ERROR;
473 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
476 * windows 2003 has a broken schema where the
477 * definition of msDS-IsDomainFor is missing (which is
478 * supposed to be the backlink of the
479 * msDS-HasDomainNCs attribute
484 bl->attr_name = target_attr->lDAPDisplayName;
485 bl->forward_dn = talloc_steal(bl, forward_dn);
486 bl->target_guid = *target_guid;
489 DLIST_ADD(ac->la_backlinks, bl);
495 add a backlink to the list of backlinks to add/delete in the prepare
498 static int replmd_add_backlink(struct ldb_module *module,
499 struct replmd_private *replmd_private,
500 const struct dsdb_schema *schema,
501 struct ldb_dn *forward_dn,
502 struct GUID *target_guid, bool active,
503 const struct dsdb_attribute *schema_attr,
504 struct ldb_request *parent)
506 const struct dsdb_attribute *target_attr;
507 struct la_backlink bl;
510 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
513 * windows 2003 has a broken schema where the
514 * definition of msDS-IsDomainFor is missing (which is
515 * supposed to be the backlink of the
516 * msDS-HasDomainNCs attribute
521 bl.attr_name = target_attr->lDAPDisplayName;
522 bl.forward_dn = forward_dn;
523 bl.target_guid = *target_guid;
526 ret = replmd_process_backlink(module, &bl, parent);
532 * Callback for most write operations in this module:
534 * notify the repl task that a object has changed. The notifies are
535 * gathered up in the replmd_private structure then written to the
536 * @REPLCHANGED object in each partition during the prepare_commit
538 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
541 struct replmd_replicated_request *ac =
542 talloc_get_type_abort(req->context, struct replmd_replicated_request);
543 struct replmd_private *replmd_private =
544 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
545 struct nc_entry *modified_partition;
546 struct ldb_control *partition_ctrl;
547 const struct dsdb_control_current_partition *partition;
549 struct ldb_control **controls;
551 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
553 controls = ares->controls;
554 if (ldb_request_get_control(ac->req,
555 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
557 * Remove the current partition control from what we pass up
558 * the chain if it hasn't been requested manually.
560 controls = ldb_controls_except_specified(ares->controls, ares,
564 if (ares->error != LDB_SUCCESS) {
565 DEBUG(5,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
566 return ldb_module_done(ac->req, controls,
567 ares->response, ares->error);
570 if (ares->type != LDB_REPLY_DONE) {
571 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
572 return ldb_module_done(ac->req, NULL,
573 NULL, LDB_ERR_OPERATIONS_ERROR);
576 if (ac->apply_mode == false) {
577 struct la_backlink *bl;
579 * process our backlink list after an replmd_add(),
580 * creating and deleting backlinks as necessary (this
581 * code is sync). The other cases are handled inline
584 for (bl=ac->la_backlinks; bl; bl=bl->next) {
585 ret = replmd_process_backlink(ac->module, bl, ac->req);
586 if (ret != LDB_SUCCESS) {
587 return ldb_module_done(ac->req, NULL,
593 if (!partition_ctrl) {
594 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
595 return ldb_module_done(ac->req, NULL,
596 NULL, LDB_ERR_OPERATIONS_ERROR);
599 partition = talloc_get_type_abort(partition_ctrl->data,
600 struct dsdb_control_current_partition);
602 if (ac->seq_num > 0) {
603 for (modified_partition = replmd_private->ncs; modified_partition;
604 modified_partition = modified_partition->next) {
605 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
610 if (modified_partition == NULL) {
611 modified_partition = talloc_zero(replmd_private, struct nc_entry);
612 if (!modified_partition) {
613 ldb_oom(ldb_module_get_ctx(ac->module));
614 return ldb_module_done(ac->req, NULL,
615 NULL, LDB_ERR_OPERATIONS_ERROR);
617 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
618 if (!modified_partition->dn) {
619 ldb_oom(ldb_module_get_ctx(ac->module));
620 return ldb_module_done(ac->req, NULL,
621 NULL, LDB_ERR_OPERATIONS_ERROR);
623 DLIST_ADD(replmd_private->ncs, modified_partition);
626 if (ac->seq_num > modified_partition->mod_usn) {
627 modified_partition->mod_usn = ac->seq_num;
629 modified_partition->mod_usn_urgent = ac->seq_num;
632 if (!ac->apply_mode) {
633 replmd_private->originating_updates = true;
637 if (ac->apply_mode) {
638 ret = replmd_replicated_apply_isDeleted(ac);
639 if (ret != LDB_SUCCESS) {
640 return ldb_module_done(ac->req, NULL, NULL, ret);
644 /* free the partition control container here, for the
645 * common path. Other cases will have it cleaned up
646 * eventually with the ares */
647 talloc_free(partition_ctrl);
648 return ldb_module_done(ac->req, controls,
649 ares->response, LDB_SUCCESS);
655 * update a @REPLCHANGED record in each partition if there have been
656 * any writes of replicated data in the partition
658 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
660 struct replmd_private *replmd_private =
661 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
663 while (replmd_private->ncs) {
665 struct nc_entry *modified_partition = replmd_private->ncs;
667 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
668 modified_partition->mod_usn,
669 modified_partition->mod_usn_urgent, parent);
670 if (ret != LDB_SUCCESS) {
671 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
672 ldb_dn_get_linearized(modified_partition->dn)));
676 if (ldb_dn_compare(modified_partition->dn,
677 replmd_private->schema_dn) == 0) {
678 struct ldb_result *ext_res;
679 ret = dsdb_module_extended(module,
680 replmd_private->schema_dn,
682 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
684 DSDB_FLAG_NEXT_MODULE,
686 if (ret != LDB_SUCCESS) {
689 talloc_free(ext_res);
692 DLIST_REMOVE(replmd_private->ncs, modified_partition);
693 talloc_free(modified_partition);
701 created a replmd_replicated_request context
703 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
704 struct ldb_request *req)
706 struct ldb_context *ldb;
707 struct replmd_replicated_request *ac;
708 const struct GUID *our_invocation_id;
710 ldb = ldb_module_get_ctx(module);
712 ac = talloc_zero(req, struct replmd_replicated_request);
721 ac->schema = dsdb_get_schema(ldb, ac);
723 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
724 "replmd_modify: no dsdb_schema loaded");
725 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
730 /* get our invocationId */
731 our_invocation_id = samdb_ntds_invocation_id(ldb);
732 if (!our_invocation_id) {
733 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
734 "replmd_add: unable to find invocationId\n");
738 ac->our_invocation_id = *our_invocation_id;
744 add a time element to a record
746 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
748 struct ldb_message_element *el;
752 if (ldb_msg_find_element(msg, attr) != NULL) {
756 s = ldb_timestring(msg, t);
758 return LDB_ERR_OPERATIONS_ERROR;
761 ret = ldb_msg_add_string(msg, attr, s);
762 if (ret != LDB_SUCCESS) {
766 el = ldb_msg_find_element(msg, attr);
767 /* always set as replace. This works because on add ops, the flag
769 el->flags = LDB_FLAG_MOD_REPLACE;
775 add a uint64_t element to a record
777 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
778 const char *attr, uint64_t v)
780 struct ldb_message_element *el;
783 if (ldb_msg_find_element(msg, attr) != NULL) {
787 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
788 if (ret != LDB_SUCCESS) {
792 el = ldb_msg_find_element(msg, attr);
793 /* always set as replace. This works because on add ops, the flag
795 el->flags = LDB_FLAG_MOD_REPLACE;
800 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
801 const struct replPropertyMetaData1 *m2,
802 const uint32_t *rdn_attid)
805 * This assignment seems inoccous, but it is critical for the
806 * system, as we need to do the comparisons as a unsigned
807 * quantity, not signed (enums are signed integers)
809 uint32_t attid_1 = m1->attid;
810 uint32_t attid_2 = m2->attid;
812 if (attid_1 == attid_2) {
817 * See above regarding this being an unsigned comparison.
818 * Otherwise when the high bit is set on non-standard
819 * attributes, they would end up first, before objectClass
822 return attid_1 > attid_2 ? 1 : -1;
825 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
826 struct replPropertyMetaDataCtr1 *ctr1,
829 if (ctr1->count == 0) {
830 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
831 "No elements found in replPropertyMetaData for %s!\n",
832 ldb_dn_get_linearized(dn));
833 return LDB_ERR_CONSTRAINT_VIOLATION;
836 /* the objectClass attribute is value 0x00000000, so must be first */
837 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
838 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
839 "No objectClass found in replPropertyMetaData for %s!\n",
840 ldb_dn_get_linearized(dn));
841 return LDB_ERR_OBJECT_CLASS_VIOLATION;
847 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
848 struct replPropertyMetaDataCtr1 *ctr1,
851 /* Note this is O(n^2) for the almost-sorted case, which this is */
852 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, NULL,
853 replmd_replPropertyMetaData1_attid_sort);
854 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
857 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
858 const struct ldb_message_element *e2,
859 const struct dsdb_schema *schema)
861 const struct dsdb_attribute *a1;
862 const struct dsdb_attribute *a2;
865 * TODO: make this faster by caching the dsdb_attribute pointer
866 * on the ldb_messag_element
869 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
870 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
873 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
877 return strcasecmp(e1->name, e2->name);
879 if (a1->attributeID_id == a2->attributeID_id) {
882 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
885 static void replmd_ldb_message_sort(struct ldb_message *msg,
886 const struct dsdb_schema *schema)
888 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
891 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
892 const struct GUID *invocation_id, uint64_t seq_num,
893 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
895 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
896 struct ldb_message_element *el, struct parsed_dn **pdn,
897 const char *ldap_oid, struct ldb_request *parent);
900 fix up linked attributes in replmd_add.
901 This involves setting up the right meta-data in extended DN
902 components, and creating backlinks to the object
904 static int replmd_add_fix_la(struct ldb_module *module, TALLOC_CTX *mem_ctx,
905 struct replmd_private *replmd_private,
906 struct ldb_message_element *el,
907 struct replmd_replicated_request *ac,
909 struct ldb_dn *forward_dn,
910 const struct dsdb_attribute *sa,
911 struct ldb_request *parent)
914 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
915 struct ldb_context *ldb = ldb_module_get_ctx(module);
916 struct parsed_dn *pdn;
917 /* We will take a reference to the schema in replmd_add_backlink */
918 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
919 struct ldb_val *new_values = NULL;
921 int ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
922 sa->syntax->ldap_oid, parent);
923 if (ret != LDB_SUCCESS) {
924 talloc_free(tmp_ctx);
928 new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
929 if (new_values == NULL) {
930 ldb_module_oom(module);
931 talloc_free(tmp_ctx);
932 return LDB_ERR_OPERATIONS_ERROR;
935 for (i = 0; i < el->num_values; i++) {
936 struct parsed_dn *p = &pdn[i];
937 ret = replmd_build_la_val(el->values, p->v, p->dsdb_dn,
938 &ac->our_invocation_id,
939 ac->seq_num, ac->seq_num, now, 0, false);
940 if (ret != LDB_SUCCESS) {
941 talloc_free(tmp_ctx);
945 ret = replmd_defer_add_backlink(module, replmd_private,
947 forward_dn, &p->guid, true, sa,
949 if (ret != LDB_SUCCESS) {
950 talloc_free(tmp_ctx);
954 new_values[i] = *p->v;
956 el->values = talloc_steal(mem_ctx, new_values);
958 talloc_free(tmp_ctx);
962 static int replmd_add_make_extended_dn(struct ldb_request *req,
963 const DATA_BLOB *guid_blob,
964 struct ldb_dn **_extended_dn)
967 const DATA_BLOB *sid_blob;
968 /* Calculate an extended DN for any linked attributes */
969 struct ldb_dn *extended_dn = ldb_dn_copy(req, req->op.add.message->dn);
971 return LDB_ERR_OPERATIONS_ERROR;
973 ret = ldb_dn_set_extended_component(extended_dn, "GUID", guid_blob);
974 if (ret != LDB_SUCCESS) {
978 sid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectSID");
979 if (sid_blob != NULL) {
980 ret = ldb_dn_set_extended_component(extended_dn, "SID", sid_blob);
981 if (ret != LDB_SUCCESS) {
985 *_extended_dn = extended_dn;
990 intercept add requests
992 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
994 struct ldb_context *ldb;
995 struct ldb_control *control;
996 struct replmd_replicated_request *ac;
997 enum ndr_err_code ndr_err;
998 struct ldb_request *down_req;
999 struct ldb_message *msg;
1000 const DATA_BLOB *guid_blob;
1001 DATA_BLOB guid_blob_stack;
1003 uint8_t guid_data[16];
1004 struct replPropertyMetaDataBlob nmd;
1005 struct ldb_val nmd_value;
1006 struct ldb_dn *extended_dn = NULL;
1009 * The use of a time_t here seems odd, but as the NTTIME
1010 * elements are actually declared as NTTIME_1sec in the IDL,
1011 * getting a higher resolution timestamp is not required.
1013 time_t t = time(NULL);
1018 unsigned int functional_level;
1020 bool allow_add_guid = false;
1021 bool remove_current_guid = false;
1022 bool is_urgent = false;
1023 bool is_schema_nc = false;
1024 struct ldb_message_element *objectclass_el;
1025 struct replmd_private *replmd_private =
1026 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
1028 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
1029 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
1031 allow_add_guid = true;
1034 /* do not manipulate our control entries */
1035 if (ldb_dn_is_special(req->op.add.message->dn)) {
1036 return ldb_next_request(module, req);
1039 ldb = ldb_module_get_ctx(module);
1041 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
1043 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
1044 if (guid_blob != NULL) {
1045 if (!allow_add_guid) {
1046 ldb_set_errstring(ldb,
1047 "replmd_add: it's not allowed to add an object with objectGUID!");
1048 return LDB_ERR_UNWILLING_TO_PERFORM;
1050 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
1051 if (!NT_STATUS_IS_OK(status)) {
1052 ldb_set_errstring(ldb,
1053 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1054 return LDB_ERR_UNWILLING_TO_PERFORM;
1056 /* we remove this attribute as it can be a string and
1057 * will not be treated correctly and then we will re-add
1058 * it later on in the good format */
1059 remove_current_guid = true;
1063 guid = GUID_random();
1065 guid_blob_stack = data_blob_const(guid_data, sizeof(guid_data));
1067 /* This can't fail */
1068 ndr_push_struct_into_fixed_blob(&guid_blob_stack, &guid,
1069 (ndr_push_flags_fn_t)ndr_push_GUID);
1070 guid_blob = &guid_blob_stack;
1073 ac = replmd_ctx_init(module, req);
1075 return ldb_module_oom(module);
1078 functional_level = dsdb_functional_level(ldb);
1080 /* Get a sequence number from the backend */
1081 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
1082 if (ret != LDB_SUCCESS) {
1087 /* we have to copy the message as the caller might have it as a const */
1088 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
1092 return LDB_ERR_OPERATIONS_ERROR;
1095 /* generated times */
1096 unix_to_nt_time(&now, t);
1097 time_str = ldb_timestring(msg, t);
1101 return LDB_ERR_OPERATIONS_ERROR;
1103 if (remove_current_guid) {
1104 ldb_msg_remove_attr(msg,"objectGUID");
1108 * remove autogenerated attributes
1110 ldb_msg_remove_attr(msg, "whenCreated");
1111 ldb_msg_remove_attr(msg, "whenChanged");
1112 ldb_msg_remove_attr(msg, "uSNCreated");
1113 ldb_msg_remove_attr(msg, "uSNChanged");
1114 ldb_msg_remove_attr(msg, "replPropertyMetaData");
1117 * readd replicated attributes
1119 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1120 if (ret != LDB_SUCCESS) {
1126 /* build the replication meta_data */
1129 nmd.ctr.ctr1.count = msg->num_elements;
1130 nmd.ctr.ctr1.array = talloc_array(msg,
1131 struct replPropertyMetaData1,
1132 nmd.ctr.ctr1.count);
1133 if (!nmd.ctr.ctr1.array) {
1136 return LDB_ERR_OPERATIONS_ERROR;
1139 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1141 for (i=0; i < msg->num_elements;) {
1142 struct ldb_message_element *e = &msg->elements[i];
1143 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1144 const struct dsdb_attribute *sa;
1146 if (e->name[0] == '@') {
1151 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1153 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1154 "replmd_add: attribute '%s' not defined in schema\n",
1157 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1160 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1161 /* if the attribute is not replicated (0x00000001)
1162 * or constructed (0x00000004) it has no metadata
1168 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1169 if (extended_dn == NULL) {
1170 ret = replmd_add_make_extended_dn(req,
1173 if (ret != LDB_SUCCESS) {
1180 * Prepare the context for the backlinks and
1181 * create metadata for the forward links. The
1182 * backlinks are created in
1183 * replmd_op_callback() after the successful
1184 * ADD of the object.
1186 ret = replmd_add_fix_la(module, msg->elements,
1191 if (ret != LDB_SUCCESS) {
1195 /* linked attributes are not stored in
1196 replPropertyMetaData in FL above w2k */
1201 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1203 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1204 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1207 if (rdn_val == NULL) {
1210 return LDB_ERR_OPERATIONS_ERROR;
1213 rdn = (const char*)rdn_val->data;
1214 if (strcmp(rdn, "Deleted Objects") == 0) {
1216 * Set the originating_change_time to 29/12/9999 at 23:59:59
1217 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1219 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1221 m->originating_change_time = now;
1224 m->originating_change_time = now;
1226 m->originating_invocation_id = ac->our_invocation_id;
1227 m->originating_usn = ac->seq_num;
1228 m->local_usn = ac->seq_num;
1231 if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1236 e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1238 if (e->num_values != 0) {
1243 ldb_msg_remove_element(msg, e);
1246 /* fix meta data count */
1247 nmd.ctr.ctr1.count = ni;
1250 * sort meta data array
1252 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1253 if (ret != LDB_SUCCESS) {
1254 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1259 /* generated NDR encoded values */
1260 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1262 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1263 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1266 return LDB_ERR_OPERATIONS_ERROR;
1270 * add the autogenerated values
1272 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1273 if (ret != LDB_SUCCESS) {
1278 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1279 if (ret != LDB_SUCCESS) {
1284 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1285 if (ret != LDB_SUCCESS) {
1290 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1291 if (ret != LDB_SUCCESS) {
1296 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1297 if (ret != LDB_SUCCESS) {
1304 * sort the attributes by attid before storing the object
1306 replmd_ldb_message_sort(msg, ac->schema);
1309 * Assert that we do have an objectClass
1311 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1312 if (objectclass_el == NULL) {
1313 ldb_asprintf_errstring(ldb, __location__
1314 ": objectClass missing on %s\n",
1315 ldb_dn_get_linearized(msg->dn));
1317 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1319 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1320 REPL_URGENT_ON_CREATE);
1322 ac->is_urgent = is_urgent;
1323 ret = ldb_build_add_req(&down_req, ldb, ac,
1326 ac, replmd_op_callback,
1329 LDB_REQ_SET_LOCATION(down_req);
1330 if (ret != LDB_SUCCESS) {
1335 /* current partition control is needed by "replmd_op_callback" */
1336 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1337 ret = ldb_request_add_control(down_req,
1338 DSDB_CONTROL_CURRENT_PARTITION_OID,
1340 if (ret != LDB_SUCCESS) {
1346 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1347 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1348 if (ret != LDB_SUCCESS) {
1354 /* mark the control done */
1356 control->critical = 0;
1358 /* go on with the call chain */
1359 return ldb_next_request(module, down_req);
1364 * update the replPropertyMetaData for one element
1366 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1367 struct ldb_message *msg,
1368 struct ldb_message_element *el,
1369 struct ldb_message_element *old_el,
1370 struct replPropertyMetaDataBlob *omd,
1371 const struct dsdb_schema *schema,
1373 const struct GUID *our_invocation_id,
1376 struct ldb_request *req)
1379 const struct dsdb_attribute *a;
1380 struct replPropertyMetaData1 *md1;
1381 bool may_skip = false;
1384 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1386 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1387 /* allow this to make it possible for dbcheck
1388 to remove bad attributes */
1392 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1394 return LDB_ERR_OPERATIONS_ERROR;
1397 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1399 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1404 * if the attribute's value haven't changed, and this isn't
1405 * just a delete of everything then return LDB_SUCCESS Unless
1406 * we have the provision control or if the attribute is
1407 * interSiteTopologyGenerator as this page explain:
1408 * http://support.microsoft.com/kb/224815 this attribute is
1409 * periodicaly written by the DC responsible for the intersite
1410 * generation in a given site
1412 * Unchanged could be deleting or replacing an already-gone
1413 * thing with an unconstrained delete/empty replace or a
1414 * replace with the same value, but not an add with the same
1415 * value because that could be about adding a duplicate (which
1416 * is for someone else to error out on).
1418 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1419 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1422 } else if (old_el == NULL && el->num_values == 0) {
1423 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1425 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1428 } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1429 ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
1431 * We intentionally skip the version bump when attempting to
1434 * The control is set by dbcheck and expunge-tombstones which
1435 * both attempt to be non-replicating. Otherwise, making an
1436 * alteration to the replication state would trigger a
1437 * broadcast of all expunged objects.
1442 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1444 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1448 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1449 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1451 * allow this to make it possible for dbcheck
1452 * to rebuild broken metadata
1458 for (i=0; i<omd->ctr.ctr1.count; i++) {
1460 * First check if we find it under the msDS-IntID,
1461 * then check if we find it under the OID and
1464 * This allows the administrator to simply re-write
1465 * the attributes and so restore replication, which is
1466 * likely what they will try to do.
1468 if (attid == omd->ctr.ctr1.array[i].attid) {
1472 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1477 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1478 /* linked attributes are not stored in
1479 replPropertyMetaData in FL above w2k, but we do
1480 raise the seqnum for the object */
1481 if (*seq_num == 0 &&
1482 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1483 return LDB_ERR_OPERATIONS_ERROR;
1488 if (i == omd->ctr.ctr1.count) {
1489 /* we need to add a new one */
1490 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1491 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1492 if (omd->ctr.ctr1.array == NULL) {
1494 return LDB_ERR_OPERATIONS_ERROR;
1496 omd->ctr.ctr1.count++;
1497 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1500 /* Get a new sequence number from the backend. We only do this
1501 * if we have a change that requires a new
1502 * replPropertyMetaData element
1504 if (*seq_num == 0) {
1505 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1506 if (ret != LDB_SUCCESS) {
1507 return LDB_ERR_OPERATIONS_ERROR;
1511 md1 = &omd->ctr.ctr1.array[i];
1514 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1515 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1518 if (rdn_val == NULL) {
1520 return LDB_ERR_OPERATIONS_ERROR;
1523 rdn = (const char*)rdn_val->data;
1524 if (strcmp(rdn, "Deleted Objects") == 0) {
1526 * Set the originating_change_time to 29/12/9999 at 23:59:59
1527 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1529 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1531 md1->originating_change_time = now;
1534 md1->originating_change_time = now;
1536 md1->originating_invocation_id = *our_invocation_id;
1537 md1->originating_usn = *seq_num;
1538 md1->local_usn = *seq_num;
1544 * Bump the replPropertyMetaData version on an attribute, and if it
1545 * has changed (or forced by leaving rdn_old NULL), update the value
1548 * This is important, as calling a modify operation may not change the
1549 * version number if the values appear unchanged, but a rename between
1550 * parents bumps this value.
1553 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1554 struct ldb_message *msg,
1555 const struct ldb_val *rdn_new,
1556 const struct ldb_val *rdn_old,
1557 struct replPropertyMetaDataBlob *omd,
1558 struct replmd_replicated_request *ar,
1562 const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
1563 const struct dsdb_attribute *rdn_attr =
1564 dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
1565 const char *attr_name = rdn_attr != NULL ?
1566 rdn_attr->lDAPDisplayName :
1568 struct ldb_message_element new_el = {
1569 .flags = LDB_FLAG_MOD_REPLACE,
1572 .values = discard_const_p(struct ldb_val, rdn_new)
1574 struct ldb_message_element old_el = {
1575 .flags = LDB_FLAG_MOD_REPLACE,
1577 .num_values = rdn_old ? 1 : 0,
1578 .values = discard_const_p(struct ldb_val, rdn_old)
1581 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1582 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1583 if (ret != LDB_SUCCESS) {
1584 return ldb_oom(ldb);
1588 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1589 omd, ar->schema, &ar->seq_num,
1590 &ar->our_invocation_id,
1591 now, is_schema_nc, ar->req);
1595 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1597 uint32_t count = omd.ctr.ctr1.count;
1600 for (i=0; i < count; i++) {
1601 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1602 if (max < m.local_usn) {
1610 * update the replPropertyMetaData object each time we modify an
1611 * object. This is needed for DRS replication, as the merge on the
1612 * client is based on this object
1614 static int replmd_update_rpmd(struct ldb_module *module,
1615 const struct dsdb_schema *schema,
1616 struct ldb_request *req,
1617 const char * const *rename_attrs,
1618 struct ldb_message *msg, uint64_t *seq_num,
1619 time_t t, bool is_schema_nc,
1620 bool *is_urgent, bool *rodc)
1622 const struct ldb_val *omd_value;
1623 enum ndr_err_code ndr_err;
1624 struct replPropertyMetaDataBlob omd;
1627 const struct GUID *our_invocation_id;
1629 const char * const *attrs = NULL;
1630 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1631 struct ldb_result *res;
1632 struct ldb_context *ldb;
1633 struct ldb_message_element *objectclass_el;
1634 enum urgent_situation situation;
1635 bool rmd_is_provided;
1636 bool rmd_is_just_resorted = false;
1637 const char *not_rename_attrs[4 + msg->num_elements];
1640 attrs = rename_attrs;
1642 for (i = 0; i < msg->num_elements; i++) {
1643 not_rename_attrs[i] = msg->elements[i].name;
1645 not_rename_attrs[i] = "replPropertyMetaData";
1646 not_rename_attrs[i+1] = "objectClass";
1647 not_rename_attrs[i+2] = "instanceType";
1648 not_rename_attrs[i+3] = NULL;
1649 attrs = not_rename_attrs;
1652 ldb = ldb_module_get_ctx(module);
1654 our_invocation_id = samdb_ntds_invocation_id(ldb);
1655 if (!our_invocation_id) {
1656 /* this happens during an initial vampire while
1657 updating the schema */
1658 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1662 unix_to_nt_time(&now, t);
1664 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1665 rmd_is_provided = true;
1666 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1667 rmd_is_just_resorted = true;
1670 rmd_is_provided = false;
1673 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1674 * otherwise we consider we are updating */
1675 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1676 situation = REPL_URGENT_ON_DELETE;
1677 } else if (rename_attrs) {
1678 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1680 situation = REPL_URGENT_ON_UPDATE;
1683 if (rmd_is_provided) {
1684 /* In this case the change_replmetadata control was supplied */
1685 /* We check that it's the only attribute that is provided
1686 * (it's a rare case so it's better to keep the code simplier)
1687 * We also check that the highest local_usn is bigger or the same as
1690 if( msg->num_elements != 1 ||
1691 strncmp(msg->elements[0].name,
1692 "replPropertyMetaData", 20) ) {
1693 DEBUG(0,(__location__ ": changereplmetada control called without "\
1694 "a specified replPropertyMetaData attribute or with others\n"));
1695 return LDB_ERR_OPERATIONS_ERROR;
1697 if (situation != REPL_URGENT_ON_UPDATE) {
1698 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1699 return LDB_ERR_OPERATIONS_ERROR;
1701 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1703 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1704 ldb_dn_get_linearized(msg->dn)));
1705 return LDB_ERR_OPERATIONS_ERROR;
1707 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1708 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1709 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1710 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1711 ldb_dn_get_linearized(msg->dn)));
1712 return LDB_ERR_OPERATIONS_ERROR;
1715 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1716 DSDB_FLAG_NEXT_MODULE |
1717 DSDB_SEARCH_SHOW_RECYCLED |
1718 DSDB_SEARCH_SHOW_EXTENDED_DN |
1719 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1720 DSDB_SEARCH_REVEAL_INTERNALS, req);
1722 if (ret != LDB_SUCCESS) {
1726 if (rmd_is_just_resorted == false) {
1727 *seq_num = find_max_local_usn(omd);
1729 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1732 * The test here now allows for a new
1733 * replPropertyMetaData with no change, if was
1734 * just dbcheck re-sorting the values.
1736 if (*seq_num <= db_seq) {
1737 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1738 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1739 (long long)*seq_num, (long long)db_seq));
1740 return LDB_ERR_OPERATIONS_ERROR;
1745 /* search for the existing replPropertyMetaDataBlob. We need
1746 * to use REVEAL and ask for DNs in storage format to support
1747 * the check for values being the same in
1748 * replmd_update_rpmd_element()
1750 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1751 DSDB_FLAG_NEXT_MODULE |
1752 DSDB_SEARCH_SHOW_RECYCLED |
1753 DSDB_SEARCH_SHOW_EXTENDED_DN |
1754 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1755 DSDB_SEARCH_REVEAL_INTERNALS, req);
1756 if (ret != LDB_SUCCESS) {
1760 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1762 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1763 ldb_dn_get_linearized(msg->dn)));
1764 return LDB_ERR_OPERATIONS_ERROR;
1767 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1768 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1769 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1770 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1771 ldb_dn_get_linearized(msg->dn)));
1772 return LDB_ERR_OPERATIONS_ERROR;
1775 if (omd.version != 1) {
1776 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1777 omd.version, ldb_dn_get_linearized(msg->dn)));
1778 return LDB_ERR_OPERATIONS_ERROR;
1781 for (i=0; i<msg->num_elements;) {
1782 struct ldb_message_element *el = &msg->elements[i];
1783 struct ldb_message_element *old_el;
1785 old_el = ldb_msg_find_element(res->msgs[0], el->name);
1786 ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1787 &omd, schema, seq_num,
1791 if (ret != LDB_SUCCESS) {
1795 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1796 *is_urgent = replmd_check_urgent_attribute(el);
1799 if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1804 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1806 if (el->num_values != 0) {
1811 ldb_msg_remove_element(msg, el);
1816 * Assert that we have an objectClass attribute - this is major
1817 * corruption if we don't have this!
1819 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1820 if (objectclass_el != NULL) {
1822 * Now check if this objectClass means we need to do urgent replication
1824 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1828 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1829 ldb_asprintf_errstring(ldb, __location__
1830 ": objectClass missing on %s\n",
1831 ldb_dn_get_linearized(msg->dn));
1832 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1836 * replmd_update_rpmd_element has done an update if the
1839 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1840 struct ldb_val *md_value;
1841 struct ldb_message_element *el;
1843 /*if we are RODC and this is a DRSR update then its ok*/
1844 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1845 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)) {
1846 unsigned instanceType;
1848 ret = samdb_rodc(ldb, rodc);
1849 if (ret != LDB_SUCCESS) {
1850 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1852 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1853 return LDB_ERR_REFERRAL;
1856 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1857 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1858 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1859 "cannot change replicated attribute on partial replica");
1863 md_value = talloc(msg, struct ldb_val);
1864 if (md_value == NULL) {
1866 return LDB_ERR_OPERATIONS_ERROR;
1869 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
1870 if (ret != LDB_SUCCESS) {
1871 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
1875 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1876 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1877 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1878 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1879 ldb_dn_get_linearized(msg->dn)));
1880 return LDB_ERR_OPERATIONS_ERROR;
1883 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1884 if (ret != LDB_SUCCESS) {
1885 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1886 ldb_dn_get_linearized(msg->dn)));
1891 el->values = md_value;
1897 struct compare_ctx {
1899 struct ldb_context *ldb;
1900 TALLOC_CTX *mem_ctx;
1901 const char *ldap_oid;
1903 const struct GUID *invocation_id;
1906 /* When a parsed_dn comes from the database, sometimes it is not really parsed. */
1908 static int really_parse_trusted_dn(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
1909 struct parsed_dn *pdn, const char *ldap_oid)
1912 struct dsdb_dn *dsdb_dn = dsdb_dn_parse_trusted(mem_ctx, ldb, pdn->v,
1914 if (dsdb_dn == NULL) {
1915 return LDB_ERR_INVALID_DN_SYNTAX;
1918 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &pdn->guid, "GUID");
1919 if (!NT_STATUS_IS_OK(status)) {
1920 return LDB_ERR_OPERATIONS_ERROR;
1922 pdn->dsdb_dn = dsdb_dn;
1927 * We choose, as the sort order, the same order as is used in DRS replication,
1928 * which is the memcmp() order of the NDR GUID, not that obtained from
1931 * This means that sorted links will be in the same order as a new DC would
1934 static int ndr_guid_compare(struct GUID *guid1, struct GUID *guid2)
1936 uint8_t v1_data[16];
1937 struct ldb_val v1 = data_blob_const(v1_data, sizeof(v1_data));
1938 uint8_t v2_data[16];
1939 struct ldb_val v2 = data_blob_const(v2_data, sizeof(v2_data));
1941 /* This can't fail */
1942 ndr_push_struct_into_fixed_blob(&v1, guid1,
1943 (ndr_push_flags_fn_t)ndr_push_GUID);
1944 /* This can't fail */
1945 ndr_push_struct_into_fixed_blob(&v2, guid2,
1946 (ndr_push_flags_fn_t)ndr_push_GUID);
1947 return data_blob_cmp(&v1, &v2);
1950 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1952 int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
1954 return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
1955 &pdn2->dsdb_dn->extra_part);
1960 static int la_guid_compare_with_trusted_dn(struct compare_ctx *ctx,
1961 struct parsed_dn *p)
1964 * This works like a standard compare function in its return values,
1965 * but has an extra trick to deal with errors: zero is returned and
1966 * ctx->err is set to the ldb error code.
1968 * That is, if (as is expected in most cases) you get a non-zero
1969 * result, you don't need to check for errors.
1971 * We assume the second argument refers to a DN is from the database
1972 * and has a GUID -- but this GUID might not have been parsed out yet.
1974 if (p->dsdb_dn == NULL) {
1975 int ret = really_parse_trusted_dn(ctx->mem_ctx, ctx->ldb, p,
1977 if (ret != LDB_SUCCESS) {
1982 return ndr_guid_compare(ctx->guid, &p->guid);
1987 static int parsed_dn_find(struct ldb_context *ldb, struct parsed_dn *pdn,
1990 struct ldb_dn *target_dn,
1991 struct parsed_dn **exact,
1992 struct parsed_dn **next,
1993 const char *ldap_oid)
1996 struct compare_ctx ctx;
2003 if (unlikely(GUID_all_zero(guid))) {
2005 * When updating a link using DRS, we sometimes get a NULL
2006 * GUID when a forward link has been deleted and its GUID has
2007 * for some reason been forgotten. The best we can do is try
2008 * and match by DN via a linear search. Note that this
2009 * probably only happens in the ADD case, in which we only
2010 * allow modification of link if it is already deleted, so
2011 * this seems very close to an elaborate NO-OP, but we are not
2012 * quite prepared to declare it so.
2014 * If the DN is not in our list, we have to add it to the
2015 * beginning of the list, where it would naturally sort.
2017 struct parsed_dn *p;
2018 if (target_dn == NULL) {
2019 /* We don't know the target DN, so we can't search for DN */
2020 DEBUG(1, ("parsed_dn_find has a NULL GUID for a linked "
2021 "attribute but we don't have a DN to compare "
2023 return LDB_ERR_OPERATIONS_ERROR;
2028 DEBUG(3, ("parsed_dn_find has a NULL GUID for a link to DN "
2029 "%s; searching through links for it",
2030 ldb_dn_get_linearized(target_dn)));
2032 for (i = 0; i < count; i++) {
2035 if (p->dsdb_dn == NULL) {
2036 int ret = really_parse_trusted_dn(pdn, ldb, p, ldap_oid);
2037 if (ret != LDB_SUCCESS) {
2038 return LDB_ERR_OPERATIONS_ERROR;
2042 cmp = ldb_dn_compare(p->dsdb_dn->dn, target_dn);
2049 * Here we have a null guid which doesn't match any existing
2050 * link. This is a bit unexpected because null guids occur
2051 * when a forward link has been deleted and we are replicating
2054 * The best thing to do is weep into the logs and add the
2055 * offending link to the beginning of the list which is
2056 * at least the correct sort position.
2058 DEBUG(1, ("parsed_dn_find has been given a NULL GUID for a "
2059 "link to unknown DN %s\n",
2060 ldb_dn_get_linearized(target_dn)));
2068 ctx.ldap_oid = ldap_oid;
2071 BINARY_ARRAY_SEARCH_GTE(pdn, count, &ctx, la_guid_compare_with_trusted_dn,
2081 get a series of message element values as an array of DNs and GUIDs
2082 the result is sorted by GUID
2084 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
2085 struct ldb_message_element *el, struct parsed_dn **pdn,
2086 const char *ldap_oid, struct ldb_request *parent)
2089 bool values_are_sorted = true;
2090 struct ldb_context *ldb = ldb_module_get_ctx(module);
2097 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
2099 ldb_module_oom(module);
2100 return LDB_ERR_OPERATIONS_ERROR;
2103 for (i=0; i<el->num_values; i++) {
2104 struct ldb_val *v = &el->values[i];
2107 struct parsed_dn *p;
2111 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
2112 if (p->dsdb_dn == NULL) {
2113 return LDB_ERR_INVALID_DN_SYNTAX;
2116 dn = p->dsdb_dn->dn;
2118 status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
2119 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
2120 unlikely(GUID_all_zero(&p->guid))) {
2121 /* we got a DN without a GUID - go find the GUID */
2122 int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
2123 if (ret != LDB_SUCCESS) {
2124 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
2125 ldb_dn_get_linearized(dn));
2126 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
2127 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
2128 ldb_attr_cmp(el->name, "member") == 0) {
2129 return LDB_ERR_UNWILLING_TO_PERFORM;
2133 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2134 if (ret != LDB_SUCCESS) {
2137 } else if (!NT_STATUS_IS_OK(status)) {
2138 return LDB_ERR_OPERATIONS_ERROR;
2140 if (i > 0 && values_are_sorted) {
2141 int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
2143 values_are_sorted = false;
2146 /* keep a pointer to the original ldb_val */
2149 if (! values_are_sorted) {
2150 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
2156 * Get a series of trusted message element values. The result is sorted by
2157 * GUID, even though the GUIDs might not be known. That works because we trust
2158 * the database to give us the elements like that if the
2159 * replmd_private->sorted_links flag is set.
2161 * We also ensure that the links are in the Functional Level 2003
2162 * linked attributes format.
2164 static int get_parsed_dns_trusted(struct ldb_module *module,
2165 struct replmd_private *replmd_private,
2166 TALLOC_CTX *mem_ctx,
2167 struct ldb_message_element *el,
2168 struct parsed_dn **pdn,
2169 const char *ldap_oid,
2170 struct ldb_request *parent)
2179 if (!replmd_private->sorted_links) {
2180 /* We need to sort the list. This is the slow old path we want
2183 ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2185 if (ret != LDB_SUCCESS) {
2189 /* Here we get a list of 'struct parsed_dns' without the parsing */
2190 *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
2193 ldb_module_oom(module);
2194 return LDB_ERR_OPERATIONS_ERROR;
2197 for (i = 0; i < el->num_values; i++) {
2198 (*pdn)[i].v = &el->values[i];
2203 * This upgrades links to FL2003 style, and sorts the result
2204 * if that was needed.
2206 * TODO: Add a database feature that asserts we have no FL2000
2207 * style links to avoid this check or add a feature that
2208 * uses a similar check to find sorted/unsorted links
2209 * for an on-the-fly upgrade.
2212 ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
2213 *pdn, el->num_values,
2216 if (ret != LDB_SUCCESS) {
2224 build a new extended DN, including all meta data fields
2226 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2227 RMD_ADDTIME = originating_add_time
2228 RMD_INVOCID = originating_invocation_id
2229 RMD_CHANGETIME = originating_change_time
2230 RMD_ORIGINATING_USN = originating_usn
2231 RMD_LOCAL_USN = local_usn
2232 RMD_VERSION = version
2234 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2235 const struct GUID *invocation_id, uint64_t seq_num,
2236 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
2238 struct ldb_dn *dn = dsdb_dn->dn;
2239 const char *tstring, *usn_string, *flags_string;
2240 struct ldb_val tval;
2242 struct ldb_val usnv, local_usnv;
2243 struct ldb_val vers, flagsv;
2246 const char *dnstring;
2248 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2250 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2252 return LDB_ERR_OPERATIONS_ERROR;
2254 tval = data_blob_string_const(tstring);
2256 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
2258 return LDB_ERR_OPERATIONS_ERROR;
2260 usnv = data_blob_string_const(usn_string);
2262 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2264 return LDB_ERR_OPERATIONS_ERROR;
2266 local_usnv = data_blob_string_const(usn_string);
2268 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2270 return LDB_ERR_OPERATIONS_ERROR;
2272 vers = data_blob_string_const(vstring);
2274 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2275 if (!NT_STATUS_IS_OK(status)) {
2276 return LDB_ERR_OPERATIONS_ERROR;
2279 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2280 if (!flags_string) {
2281 return LDB_ERR_OPERATIONS_ERROR;
2283 flagsv = data_blob_string_const(flags_string);
2285 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2286 if (ret != LDB_SUCCESS) return ret;
2287 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
2288 if (ret != LDB_SUCCESS) return ret;
2289 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2290 if (ret != LDB_SUCCESS) return ret;
2291 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2292 if (ret != LDB_SUCCESS) return ret;
2293 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2294 if (ret != LDB_SUCCESS) return ret;
2295 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2296 if (ret != LDB_SUCCESS) return ret;
2297 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2298 if (ret != LDB_SUCCESS) return ret;
2300 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2301 if (dnstring == NULL) {
2302 return LDB_ERR_OPERATIONS_ERROR;
2304 *v = data_blob_string_const(dnstring);
2309 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2310 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2311 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
2312 uint32_t version, bool deleted);
2315 check if any links need upgrading from w2k format
2317 static int replmd_check_upgrade_links(struct ldb_context *ldb,
2318 struct parsed_dn *dns, uint32_t count,
2319 struct ldb_message_element *el,
2320 const char *ldap_oid)
2323 const struct GUID *invocation_id = NULL;
2324 for (i=0; i<count; i++) {
2328 if (dns[i].dsdb_dn == NULL) {
2329 ret = really_parse_trusted_dn(dns, ldb, &dns[i],
2331 if (ret != LDB_SUCCESS) {
2332 return LDB_ERR_INVALID_DN_SYNTAX;
2336 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
2337 &version, "RMD_VERSION");
2338 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2340 * We optimistically assume they are all the same; if
2341 * the first one is fixed, they are all fixed.
2343 * If the first one was *not* fixed and we find a
2344 * later one that is, that is an occasion to shout
2350 DEBUG(0, ("Mixed w2k and fixed format "
2351 "linked attributes\n"));
2355 if (invocation_id == NULL) {
2356 invocation_id = samdb_ntds_invocation_id(ldb);
2357 if (invocation_id == NULL) {
2358 return LDB_ERR_OPERATIONS_ERROR;
2363 /* it's an old one that needs upgrading */
2364 ret = replmd_update_la_val(el->values, dns[i].v,
2365 dns[i].dsdb_dn, dns[i].dsdb_dn,
2366 invocation_id, 1, 1, 0, 0, false);
2367 if (ret != LDB_SUCCESS) {
2373 * This sort() is critical for the operation of
2374 * get_parsed_dns_trusted() because callers of this function
2375 * expect a sorted list, and FL2000 style links are not
2376 * sorted. In particular, as well as the upgrade case,
2377 * get_parsed_dns_trusted() is called from
2378 * replmd_delete_remove_link() even in FL2000 mode
2380 * We do not normally pay the cost of the qsort() due to the
2381 * early return in the RMD_VERSION found case.
2383 TYPESAFE_QSORT(dns, count, parsed_dn_compare);
2388 update an extended DN, including all meta data fields
2390 see replmd_build_la_val for value names
2392 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2393 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2394 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2395 uint32_t version, bool deleted)
2397 struct ldb_dn *dn = dsdb_dn->dn;
2398 const char *tstring, *usn_string, *flags_string;
2399 struct ldb_val tval;
2401 struct ldb_val usnv, local_usnv;
2402 struct ldb_val vers, flagsv;
2403 const struct ldb_val *old_addtime;
2404 uint32_t old_version;
2407 const char *dnstring;
2409 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2411 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2413 return LDB_ERR_OPERATIONS_ERROR;
2415 tval = data_blob_string_const(tstring);
2417 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2419 return LDB_ERR_OPERATIONS_ERROR;
2421 usnv = data_blob_string_const(usn_string);
2423 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2425 return LDB_ERR_OPERATIONS_ERROR;
2427 local_usnv = data_blob_string_const(usn_string);
2429 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2430 if (!NT_STATUS_IS_OK(status)) {
2431 return LDB_ERR_OPERATIONS_ERROR;
2434 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2435 if (!flags_string) {
2436 return LDB_ERR_OPERATIONS_ERROR;
2438 flagsv = data_blob_string_const(flags_string);
2440 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2441 if (ret != LDB_SUCCESS) return ret;
2443 /* get the ADDTIME from the original */
2444 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
2445 if (old_addtime == NULL) {
2446 old_addtime = &tval;
2448 if (dsdb_dn != old_dsdb_dn ||
2449 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2450 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2451 if (ret != LDB_SUCCESS) return ret;
2454 /* use our invocation id */
2455 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2456 if (ret != LDB_SUCCESS) return ret;
2458 /* changetime is the current time */
2459 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2460 if (ret != LDB_SUCCESS) return ret;
2462 /* update the USN */
2463 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2464 if (ret != LDB_SUCCESS) return ret;
2466 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2467 if (ret != LDB_SUCCESS) return ret;
2469 /* increase the version by 1 */
2470 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
2471 if (NT_STATUS_IS_OK(status) && old_version >= version) {
2472 version = old_version+1;
2474 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
2475 vers = data_blob_string_const(vstring);
2476 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2477 if (ret != LDB_SUCCESS) return ret;
2479 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2480 if (dnstring == NULL) {
2481 return LDB_ERR_OPERATIONS_ERROR;
2483 *v = data_blob_string_const(dnstring);
2489 handle adding a linked attribute
2491 static int replmd_modify_la_add(struct ldb_module *module,
2492 struct replmd_private *replmd_private,
2493 const struct dsdb_schema *schema,
2494 struct ldb_message *msg,
2495 struct ldb_message_element *el,
2496 struct ldb_message_element *old_el,
2497 const struct dsdb_attribute *schema_attr,
2500 struct ldb_dn *msg_dn,
2501 struct ldb_request *parent)
2504 struct parsed_dn *dns, *old_dns;
2505 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2507 struct ldb_val *new_values = NULL;
2508 unsigned old_num_values = old_el ? old_el->num_values : 0;
2509 unsigned num_values = 0;
2510 unsigned max_num_values;
2511 const struct GUID *invocation_id;
2512 struct ldb_context *ldb = ldb_module_get_ctx(module);
2514 unix_to_nt_time(&now, t);
2516 invocation_id = samdb_ntds_invocation_id(ldb);
2517 if (!invocation_id) {
2518 talloc_free(tmp_ctx);
2519 return LDB_ERR_OPERATIONS_ERROR;
2522 /* get the DNs to be added, fully parsed.
2524 * We need full parsing because they came off the wire and we don't
2525 * trust them, besides which we need their details to know where to put
2528 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2529 schema_attr->syntax->ldap_oid, parent);
2530 if (ret != LDB_SUCCESS) {
2531 talloc_free(tmp_ctx);
2535 /* get the existing DNs, lazily parsed */
2536 ret = get_parsed_dns_trusted(module, replmd_private,
2537 tmp_ctx, old_el, &old_dns,
2538 schema_attr->syntax->ldap_oid, parent);
2540 if (ret != LDB_SUCCESS) {
2541 talloc_free(tmp_ctx);
2545 max_num_values = old_num_values + el->num_values;
2546 if (max_num_values < old_num_values) {
2547 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2548 "old values: %u, new values: %u, sum: %u",
2549 old_num_values, el->num_values, max_num_values));
2550 talloc_free(tmp_ctx);
2551 return LDB_ERR_OPERATIONS_ERROR;
2554 new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2556 if (new_values == NULL) {
2557 ldb_module_oom(module);
2558 talloc_free(tmp_ctx);
2559 return LDB_ERR_OPERATIONS_ERROR;
2563 * For each new value, find where it would go in the list. If there is
2564 * a matching GUID there, we update the existing value; otherwise we
2568 for (i = 0; i < el->num_values; i++) {
2569 struct parsed_dn *exact;
2570 struct parsed_dn *next;
2572 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2576 schema_attr->syntax->ldap_oid);
2577 if (err != LDB_SUCCESS) {
2578 talloc_free(tmp_ctx);
2582 if (exact != NULL) {
2584 * We are trying to add one that exists, which is only
2585 * allowed if it was previously deleted.
2587 * When we do undelete a link we change it in place.
2588 * It will be copied across into the right spot in due
2592 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2594 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2595 struct GUID_txt_buf guid_str;
2596 ldb_asprintf_errstring(ldb,
2597 "Attribute %s already "
2598 "exists for target GUID %s",
2600 GUID_buf_string(&exact->guid,
2602 talloc_free(tmp_ctx);
2603 /* error codes for 'member' need to be
2605 if (ldb_attr_cmp(el->name, "member") == 0) {
2606 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2608 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2612 ret = replmd_update_la_val(new_values, exact->v,
2615 invocation_id, seq_num,
2616 seq_num, now, 0, false);
2617 if (ret != LDB_SUCCESS) {
2618 talloc_free(tmp_ctx);
2622 ret = replmd_add_backlink(module, replmd_private,
2629 if (ret != LDB_SUCCESS) {
2630 talloc_free(tmp_ctx);
2636 * Here we don't have an exact match.
2638 * If next is NULL, this one goes beyond the end of the
2639 * existing list, so we need to add all of those ones first.
2641 * If next is not NULL, we need to add all the ones before
2645 offset = old_num_values;
2647 /* next should have been parsed, but let's make sure */
2648 if (next->dsdb_dn == NULL) {
2649 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2650 schema_attr->syntax->ldap_oid);
2651 if (ret != LDB_SUCCESS) {
2655 offset = MIN(next - old_dns, old_num_values);
2658 /* put all the old ones before next on the list */
2659 for (; j < offset; j++) {
2660 new_values[num_values] = *old_dns[j].v;
2664 ret = replmd_add_backlink(module, replmd_private,
2669 /* Make the new linked attribute ldb_val. */
2670 ret = replmd_build_la_val(new_values, &new_values[num_values],
2671 dns[i].dsdb_dn, invocation_id,
2674 if (ret != LDB_SUCCESS) {
2675 talloc_free(tmp_ctx);
2679 if (ret != LDB_SUCCESS) {
2680 talloc_free(tmp_ctx);
2684 /* copy the rest of the old ones (if any) */
2685 for (; j < old_num_values; j++) {
2686 new_values[num_values] = *old_dns[j].v;
2690 talloc_steal(msg->elements, new_values);
2691 if (old_el != NULL) {
2692 talloc_steal(msg->elements, old_el->values);
2694 el->values = new_values;
2695 el->num_values = num_values;
2697 talloc_free(tmp_ctx);
2699 /* we now tell the backend to replace all existing values
2700 with the one we have constructed */
2701 el->flags = LDB_FLAG_MOD_REPLACE;
2708 handle deleting all active linked attributes
2710 static int replmd_modify_la_delete(struct ldb_module *module,
2711 struct replmd_private *replmd_private,
2712 const struct dsdb_schema *schema,
2713 struct ldb_message *msg,
2714 struct ldb_message_element *el,
2715 struct ldb_message_element *old_el,
2716 const struct dsdb_attribute *schema_attr,
2719 struct ldb_dn *msg_dn,
2720 struct ldb_request *parent)
2723 struct parsed_dn *dns, *old_dns;
2724 TALLOC_CTX *tmp_ctx = NULL;
2726 struct ldb_context *ldb = ldb_module_get_ctx(module);
2727 struct ldb_control *vanish_links_ctrl = NULL;
2728 bool vanish_links = false;
2729 unsigned int num_to_delete = el->num_values;
2731 const struct GUID *invocation_id;
2734 unix_to_nt_time(&now, t);
2736 invocation_id = samdb_ntds_invocation_id(ldb);
2737 if (!invocation_id) {
2738 return LDB_ERR_OPERATIONS_ERROR;
2741 if (old_el == NULL || old_el->num_values == 0) {
2742 /* there is nothing to delete... */
2743 if (num_to_delete == 0) {
2744 /* and we're deleting nothing, so that's OK */
2747 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2750 tmp_ctx = talloc_new(msg);
2751 if (tmp_ctx == NULL) {
2752 return LDB_ERR_OPERATIONS_ERROR;
2755 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2756 schema_attr->syntax->ldap_oid, parent);
2757 if (ret != LDB_SUCCESS) {
2758 talloc_free(tmp_ctx);
2762 ret = get_parsed_dns_trusted(module, replmd_private,
2763 tmp_ctx, old_el, &old_dns,
2764 schema_attr->syntax->ldap_oid, parent);
2766 if (ret != LDB_SUCCESS) {
2767 talloc_free(tmp_ctx);
2772 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2773 if (vanish_links_ctrl) {
2774 vanish_links = true;
2775 vanish_links_ctrl->critical = false;
2779 /* we empty out el->values here to avoid damage if we return early. */
2784 * If vanish links is set, we are actually removing members of
2785 * old_el->values; otherwise we are just marking them deleted.
2787 * There is a special case when no values are given: we remove them
2788 * all. When we have the vanish_links control we just have to remove
2789 * the backlinks and change our element to replace the existing values
2790 * with the empty list.
2793 if (num_to_delete == 0) {
2794 for (i = 0; i < old_el->num_values; i++) {
2795 struct parsed_dn *p = &old_dns[i];
2796 if (p->dsdb_dn == NULL) {
2797 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2798 schema_attr->syntax->ldap_oid);
2799 if (ret != LDB_SUCCESS) {
2803 ret = replmd_add_backlink(module, replmd_private,
2804 schema, msg_dn, &p->guid,
2807 if (ret != LDB_SUCCESS) {
2808 talloc_free(tmp_ctx);
2815 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2816 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2820 ret = replmd_update_la_val(old_el->values, p->v,
2821 p->dsdb_dn, p->dsdb_dn,
2822 invocation_id, seq_num,
2823 seq_num, now, 0, true);
2824 if (ret != LDB_SUCCESS) {
2825 talloc_free(tmp_ctx);
2831 el->flags = LDB_FLAG_MOD_REPLACE;
2832 talloc_free(tmp_ctx);
2838 for (i = 0; i < num_to_delete; i++) {
2839 struct parsed_dn *p = &dns[i];
2840 struct parsed_dn *exact = NULL;
2841 struct parsed_dn *next = NULL;
2842 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2846 schema_attr->syntax->ldap_oid);
2847 if (ret != LDB_SUCCESS) {
2848 talloc_free(tmp_ctx);
2851 if (exact == NULL) {
2852 struct GUID_txt_buf buf;
2853 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2854 "exist for target GUID %s",
2856 GUID_buf_string(&p->guid, &buf));
2857 if (ldb_attr_cmp(el->name, "member") == 0) {
2858 talloc_free(tmp_ctx);
2859 return LDB_ERR_UNWILLING_TO_PERFORM;
2861 talloc_free(tmp_ctx);
2862 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2867 if (CHECK_DEBUGLVL(5)) {
2868 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2869 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2870 struct GUID_txt_buf buf;
2871 const char *guid_str = \
2872 GUID_buf_string(&p->guid, &buf);
2873 DEBUG(5, ("Deleting deleted linked "
2874 "attribute %s to %s, because "
2875 "vanish_links control is set\n",
2876 el->name, guid_str));
2880 /* remove the backlink */
2881 ret = replmd_add_backlink(module,
2888 if (ret != LDB_SUCCESS) {
2889 talloc_free(tmp_ctx);
2893 /* We flag the deletion and tidy it up later. */
2898 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2900 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2901 struct GUID_txt_buf buf;
2902 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2903 ldb_asprintf_errstring(ldb, "Attribute %s already "
2904 "deleted for target GUID %s",
2905 el->name, guid_str);
2906 if (ldb_attr_cmp(el->name, "member") == 0) {
2907 talloc_free(tmp_ctx);
2908 return LDB_ERR_UNWILLING_TO_PERFORM;
2910 talloc_free(tmp_ctx);
2911 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2915 ret = replmd_update_la_val(old_el->values, exact->v,
2916 exact->dsdb_dn, exact->dsdb_dn,
2917 invocation_id, seq_num, seq_num,
2919 if (ret != LDB_SUCCESS) {
2920 talloc_free(tmp_ctx);
2923 ret = replmd_add_backlink(module, replmd_private,
2928 if (ret != LDB_SUCCESS) {
2929 talloc_free(tmp_ctx);
2936 for (i = 0; i < old_el->num_values; i++) {
2937 if (old_dns[i].v != NULL) {
2938 old_el->values[j] = *old_dns[i].v;
2942 old_el->num_values = j;
2945 el->values = talloc_steal(msg->elements, old_el->values);
2946 el->num_values = old_el->num_values;
2948 talloc_free(tmp_ctx);
2950 /* we now tell the backend to replace all existing values
2951 with the one we have constructed */
2952 el->flags = LDB_FLAG_MOD_REPLACE;
2958 handle replacing a linked attribute
2960 static int replmd_modify_la_replace(struct ldb_module *module,
2961 struct replmd_private *replmd_private,
2962 const struct dsdb_schema *schema,
2963 struct ldb_message *msg,
2964 struct ldb_message_element *el,
2965 struct ldb_message_element *old_el,
2966 const struct dsdb_attribute *schema_attr,
2969 struct ldb_dn *msg_dn,
2970 struct ldb_request *parent)
2972 unsigned int i, old_i, new_i;
2973 struct parsed_dn *dns, *old_dns;
2974 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2976 const struct GUID *invocation_id;
2977 struct ldb_context *ldb = ldb_module_get_ctx(module);
2978 struct ldb_val *new_values = NULL;
2979 const char *ldap_oid = schema_attr->syntax->ldap_oid;
2980 unsigned int old_num_values;
2981 unsigned int repl_num_values;
2982 unsigned int max_num_values;
2985 unix_to_nt_time(&now, t);
2987 invocation_id = samdb_ntds_invocation_id(ldb);
2988 if (!invocation_id) {
2989 return LDB_ERR_OPERATIONS_ERROR;
2993 * The replace operation is unlike the replace and delete cases in that
2994 * we need to look at every existing link to see whether it is being
2995 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
2997 * As we are trying to combine two sorted lists, the algorithm we use
2998 * is akin to the merge phase of a merge sort. We interleave the two
2999 * lists, doing different things depending on which side the current
3002 * There are three main cases, with some sub-cases.
3004 * - a DN is in the old list but not the new one. It needs to be
3005 * marked as deleted (but left in the list).
3006 * - maybe it is already deleted, and we have less to do.
3008 * - a DN is in both lists. The old data gets replaced by the new,
3009 * and the list doesn't grow. The old link may have been marked as
3010 * deleted, in which case we undelete it.
3012 * - a DN is in the new list only. We add it in the right place.
3015 old_num_values = old_el ? old_el->num_values : 0;
3016 repl_num_values = el->num_values;
3017 max_num_values = old_num_values + repl_num_values;
3019 if (max_num_values == 0) {
3020 /* There is nothing to do! */
3024 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
3025 if (ret != LDB_SUCCESS) {
3026 talloc_free(tmp_ctx);
3030 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
3032 if (ret != LDB_SUCCESS) {
3033 talloc_free(tmp_ctx);
3037 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
3039 if (ret != LDB_SUCCESS) {
3040 talloc_free(tmp_ctx);
3044 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
3045 if (new_values == NULL) {
3046 ldb_module_oom(module);
3047 talloc_free(tmp_ctx);
3048 return LDB_ERR_OPERATIONS_ERROR;
3053 for (i = 0; i < max_num_values; i++) {
3055 struct parsed_dn *old_p, *new_p;
3056 if (old_i < old_num_values && new_i < repl_num_values) {
3057 old_p = &old_dns[old_i];
3058 new_p = &dns[new_i];
3059 cmp = parsed_dn_compare(old_p, new_p);
3060 } else if (old_i < old_num_values) {
3061 /* the new list is empty, read the old list */
3062 old_p = &old_dns[old_i];
3065 } else if (new_i < repl_num_values) {
3066 /* the old list is empty, read new list */
3068 new_p = &dns[new_i];
3076 * An old ones that come before the next replacement
3077 * (if any). We mark it as deleted and add it to the
3080 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3081 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
3082 ret = replmd_update_la_val(new_values, old_p->v,
3088 if (ret != LDB_SUCCESS) {
3089 talloc_free(tmp_ctx);
3093 ret = replmd_add_backlink(module, replmd_private,
3096 &old_p->guid, false,
3099 if (ret != LDB_SUCCESS) {
3100 talloc_free(tmp_ctx);
3104 new_values[i] = *old_p->v;
3106 } else if (cmp == 0) {
3108 * We are overwriting one. If it was previously
3109 * deleted, we need to add a backlink.
3111 * Note that if any RMD_FLAGs in an extended new DN
3116 ret = replmd_update_la_val(new_values, old_p->v,
3122 if (ret != LDB_SUCCESS) {
3123 talloc_free(tmp_ctx);
3127 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3128 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3129 ret = replmd_add_backlink(module, replmd_private,
3135 if (ret != LDB_SUCCESS) {
3136 talloc_free(tmp_ctx);
3141 new_values[i] = *old_p->v;
3146 * Replacements that don't match an existing one. We
3147 * just add them to the final list.
3149 ret = replmd_build_la_val(new_values,
3155 if (ret != LDB_SUCCESS) {
3156 talloc_free(tmp_ctx);
3159 ret = replmd_add_backlink(module, replmd_private,
3165 if (ret != LDB_SUCCESS) {
3166 talloc_free(tmp_ctx);
3169 new_values[i] = *new_p->v;
3173 if (old_el != NULL) {
3174 talloc_steal(msg->elements, old_el->values);
3176 el->values = talloc_steal(msg->elements, new_values);
3178 talloc_free(tmp_ctx);
3180 el->flags = LDB_FLAG_MOD_REPLACE;
3187 handle linked attributes in modify requests
3189 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3190 struct replmd_private *replmd_private,
3191 struct ldb_message *msg,
3192 uint64_t seq_num, time_t t,
3193 struct ldb_request *parent)
3195 struct ldb_result *res;
3198 struct ldb_context *ldb = ldb_module_get_ctx(module);
3199 struct ldb_message *old_msg;
3201 const struct dsdb_schema *schema;
3203 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3205 * Nothing special is required for modifying or vanishing links
3206 * in fl2000 since they are just strings in a multi-valued
3209 struct ldb_control *ctrl = ldb_request_get_control(parent,
3210 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3212 ctrl->critical = false;
3220 * We should restrict this to the intersection of the list of
3221 * linked attributes in the schema and the list of attributes
3224 * This will help performance a little, as otherwise we have
3225 * to allocate the entire object value-by-value.
3227 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3228 DSDB_FLAG_NEXT_MODULE |
3229 DSDB_SEARCH_SHOW_RECYCLED |
3230 DSDB_SEARCH_REVEAL_INTERNALS |
3231 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3233 if (ret != LDB_SUCCESS) {
3236 schema = dsdb_get_schema(ldb, res);
3238 return LDB_ERR_OPERATIONS_ERROR;
3241 old_msg = res->msgs[0];
3243 for (i=0; i<msg->num_elements; i++) {
3244 struct ldb_message_element *el = &msg->elements[i];
3245 struct ldb_message_element *old_el, *new_el;
3246 const struct dsdb_attribute *schema_attr
3247 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3249 ldb_asprintf_errstring(ldb,
3250 "%s: attribute %s is not a valid attribute in schema",
3251 __FUNCTION__, el->name);
3252 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3254 if (schema_attr->linkID == 0) {
3257 if ((schema_attr->linkID & 1) == 1) {
3258 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
3261 /* Odd is for the target. Illegal to modify */
3262 ldb_asprintf_errstring(ldb,
3263 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3264 return LDB_ERR_UNWILLING_TO_PERFORM;
3266 old_el = ldb_msg_find_element(old_msg, el->name);
3267 switch (el->flags & LDB_FLAG_MOD_MASK) {
3268 case LDB_FLAG_MOD_REPLACE:
3269 ret = replmd_modify_la_replace(module, replmd_private,
3270 schema, msg, el, old_el,
3271 schema_attr, seq_num, t,
3275 case LDB_FLAG_MOD_DELETE:
3276 ret = replmd_modify_la_delete(module, replmd_private,
3277 schema, msg, el, old_el,
3278 schema_attr, seq_num, t,
3282 case LDB_FLAG_MOD_ADD:
3283 ret = replmd_modify_la_add(module, replmd_private,
3284 schema, msg, el, old_el,
3285 schema_attr, seq_num, t,
3290 ldb_asprintf_errstring(ldb,
3291 "invalid flags 0x%x for %s linked attribute",
3292 el->flags, el->name);
3293 return LDB_ERR_UNWILLING_TO_PERFORM;
3295 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
3296 ldb_asprintf_errstring(ldb,
3297 "Attribute %s is single valued but more than one value has been supplied",
3299 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3301 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3306 if (ret != LDB_SUCCESS) {
3310 ldb_msg_remove_attr(old_msg, el->name);
3312 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3313 new_el->num_values = el->num_values;
3314 new_el->values = talloc_steal(msg->elements, el->values);
3316 /* TODO: this relises a bit too heavily on the exact
3317 behaviour of ldb_msg_find_element and
3318 ldb_msg_remove_element */
3319 old_el = ldb_msg_find_element(msg, el->name);
3321 ldb_msg_remove_element(msg, old_el);
3332 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3334 struct ldb_context *ldb;
3335 struct replmd_replicated_request *ac;
3336 struct ldb_request *down_req;
3337 struct ldb_message *msg;
3338 time_t t = time(NULL);
3340 bool is_urgent = false, rodc = false;
3341 bool is_schema_nc = false;
3342 unsigned int functional_level;
3343 const struct ldb_message_element *guid_el = NULL;
3344 struct ldb_control *sd_propagation_control;
3345 struct replmd_private *replmd_private =
3346 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3348 /* do not manipulate our control entries */
3349 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3350 return ldb_next_request(module, req);
3353 sd_propagation_control = ldb_request_get_control(req,
3354 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3355 if (sd_propagation_control != NULL) {
3356 if (req->op.mod.message->num_elements != 1) {
3357 return ldb_module_operr(module);
3359 ret = strcmp(req->op.mod.message->elements[0].name,
3360 "nTSecurityDescriptor");
3362 return ldb_module_operr(module);
3365 return ldb_next_request(module, req);
3368 ldb = ldb_module_get_ctx(module);
3370 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3372 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3373 if (guid_el != NULL) {
3374 ldb_set_errstring(ldb,
3375 "replmd_modify: it's not allowed to change the objectGUID!");
3376 return LDB_ERR_CONSTRAINT_VIOLATION;
3379 ac = replmd_ctx_init(module, req);
3381 return ldb_module_oom(module);
3384 functional_level = dsdb_functional_level(ldb);
3386 /* we have to copy the message as the caller might have it as a const */
3387 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3391 return LDB_ERR_OPERATIONS_ERROR;
3394 ldb_msg_remove_attr(msg, "whenChanged");
3395 ldb_msg_remove_attr(msg, "uSNChanged");
3397 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3399 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3400 msg, &ac->seq_num, t, is_schema_nc,
3402 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3403 struct loadparm_context *lp_ctx;
3406 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3407 struct loadparm_context);
3409 referral = talloc_asprintf(req,
3411 lpcfg_dnsdomain(lp_ctx),
3412 ldb_dn_get_linearized(msg->dn));
3413 ret = ldb_module_send_referral(req, referral);
3418 if (ret != LDB_SUCCESS) {
3423 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3424 msg, ac->seq_num, t, req);
3425 if (ret != LDB_SUCCESS) {
3431 * - replace the old object with the newly constructed one
3434 ac->is_urgent = is_urgent;
3436 ret = ldb_build_mod_req(&down_req, ldb, ac,
3439 ac, replmd_op_callback,
3441 LDB_REQ_SET_LOCATION(down_req);
3442 if (ret != LDB_SUCCESS) {
3447 /* current partition control is needed by "replmd_op_callback" */
3448 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3449 ret = ldb_request_add_control(down_req,
3450 DSDB_CONTROL_CURRENT_PARTITION_OID,
3452 if (ret != LDB_SUCCESS) {
3458 /* If we are in functional level 2000, then
3459 * replmd_modify_handle_linked_attribs will have done
3461 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3462 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3463 if (ret != LDB_SUCCESS) {
3469 talloc_steal(down_req, msg);
3471 /* we only change whenChanged and uSNChanged if the seq_num
3473 if (ac->seq_num != 0) {
3474 ret = add_time_element(msg, "whenChanged", t);
3475 if (ret != LDB_SUCCESS) {
3481 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3482 if (ret != LDB_SUCCESS) {
3489 /* go on with the call chain */
3490 return ldb_next_request(module, down_req);
3493 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3496 handle a rename request
3498 On a rename we need to do an extra ldb_modify which sets the
3499 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3501 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3503 struct ldb_context *ldb;
3504 struct replmd_replicated_request *ac;
3506 struct ldb_request *down_req;
3508 /* do not manipulate our control entries */
3509 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3510 return ldb_next_request(module, req);
3513 ldb = ldb_module_get_ctx(module);
3515 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3517 ac = replmd_ctx_init(module, req);
3519 return ldb_module_oom(module);
3522 ret = ldb_build_rename_req(&down_req, ldb, ac,
3523 ac->req->op.rename.olddn,
3524 ac->req->op.rename.newdn,
3526 ac, replmd_rename_callback,
3528 LDB_REQ_SET_LOCATION(down_req);
3529 if (ret != LDB_SUCCESS) {
3534 /* go on with the call chain */
3535 return ldb_next_request(module, down_req);
3538 /* After the rename is compleated, update the whenchanged etc */
3539 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3541 struct ldb_context *ldb;
3542 struct ldb_request *down_req;
3543 struct ldb_message *msg;
3544 const struct dsdb_attribute *rdn_attr;
3545 const char *rdn_name;
3546 const struct ldb_val *rdn_val;
3547 const char *attrs[5] = { NULL, };
3548 time_t t = time(NULL);
3550 bool is_urgent = false, rodc = false;
3552 struct replmd_replicated_request *ac =
3553 talloc_get_type(req->context, struct replmd_replicated_request);
3554 struct replmd_private *replmd_private =
3555 talloc_get_type(ldb_module_get_private(ac->module),
3556 struct replmd_private);
3558 ldb = ldb_module_get_ctx(ac->module);
3560 if (ares->error != LDB_SUCCESS) {
3561 return ldb_module_done(ac->req, ares->controls,
3562 ares->response, ares->error);
3565 if (ares->type != LDB_REPLY_DONE) {
3566 ldb_set_errstring(ldb,
3567 "invalid ldb_reply_type in callback");
3569 return ldb_module_done(ac->req, NULL, NULL,
3570 LDB_ERR_OPERATIONS_ERROR);
3574 * - replace the old object with the newly constructed one
3577 msg = ldb_msg_new(ac);
3580 return LDB_ERR_OPERATIONS_ERROR;
3583 msg->dn = ac->req->op.rename.newdn;
3585 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3587 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3588 if (rdn_name == NULL) {
3590 return ldb_module_done(ac->req, NULL, NULL,
3594 /* normalize the rdn attribute name */
3595 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3596 if (rdn_attr == NULL) {
3598 return ldb_module_done(ac->req, NULL, NULL,
3601 rdn_name = rdn_attr->lDAPDisplayName;
3603 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3604 if (rdn_val == NULL) {
3606 return ldb_module_done(ac->req, NULL, NULL,
3610 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3612 return ldb_module_done(ac->req, NULL, NULL,
3615 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3617 return ldb_module_done(ac->req, NULL, NULL,
3620 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3622 return ldb_module_done(ac->req, NULL, NULL,
3625 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3627 return ldb_module_done(ac->req, NULL, NULL,
3632 * here we let replmd_update_rpmd() only search for
3633 * the existing "replPropertyMetaData" and rdn_name attributes.
3635 * We do not want the existing "name" attribute as
3636 * the "name" attribute needs to get the version
3637 * updated on rename even if the rdn value hasn't changed.
3639 * This is the diff of the meta data, for a moved user
3640 * on a w2k8r2 server:
3643 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3644 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3645 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3646 * version : 0x00000001 (1)
3647 * reserved : 0x00000000 (0)
3648 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3649 * local_usn : 0x00000000000037a5 (14245)
3650 * array: struct replPropertyMetaData1
3651 * attid : DRSUAPI_ATTID_name (0x90001)
3652 * - version : 0x00000001 (1)
3653 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3654 * + version : 0x00000002 (2)
3655 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3656 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3657 * - originating_usn : 0x00000000000037a5 (14245)
3658 * - local_usn : 0x00000000000037a5 (14245)
3659 * + originating_usn : 0x0000000000003834 (14388)
3660 * + local_usn : 0x0000000000003834 (14388)
3661 * array: struct replPropertyMetaData1
3662 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3663 * version : 0x00000004 (4)
3665 attrs[0] = "replPropertyMetaData";
3666 attrs[1] = "objectClass";
3667 attrs[2] = "instanceType";
3668 attrs[3] = rdn_name;
3671 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3672 msg, &ac->seq_num, t,
3673 is_schema_nc, &is_urgent, &rodc);
3674 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3675 struct ldb_dn *olddn = ac->req->op.rename.olddn;
3676 struct loadparm_context *lp_ctx;
3679 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3680 struct loadparm_context);
3682 referral = talloc_asprintf(req,
3684 lpcfg_dnsdomain(lp_ctx),
3685 ldb_dn_get_linearized(olddn));
3686 ret = ldb_module_send_referral(req, referral);
3688 return ldb_module_done(req, NULL, NULL, ret);
3691 if (ret != LDB_SUCCESS) {
3693 return ldb_module_done(ac->req, NULL, NULL, ret);
3696 if (ac->seq_num == 0) {
3698 return ldb_module_done(ac->req, NULL, NULL,
3700 "internal error seq_num == 0"));
3702 ac->is_urgent = is_urgent;
3704 ret = ldb_build_mod_req(&down_req, ldb, ac,
3707 ac, replmd_op_callback,
3709 LDB_REQ_SET_LOCATION(down_req);
3710 if (ret != LDB_SUCCESS) {
3715 /* current partition control is needed by "replmd_op_callback" */
3716 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3717 ret = ldb_request_add_control(down_req,
3718 DSDB_CONTROL_CURRENT_PARTITION_OID,
3720 if (ret != LDB_SUCCESS) {
3726 talloc_steal(down_req, msg);
3728 ret = add_time_element(msg, "whenChanged", t);
3729 if (ret != LDB_SUCCESS) {
3735 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3736 if (ret != LDB_SUCCESS) {
3742 /* go on with the call chain - do the modify after the rename */
3743 return ldb_next_request(ac->module, down_req);
3747 * remove links from objects that point at this object when an object
3748 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3749 * RemoveObj which states that link removal due to the object being
3750 * deleted is NOT an originating update - they just go away!
3753 static int replmd_delete_remove_link(struct ldb_module *module,
3754 const struct dsdb_schema *schema,
3755 struct replmd_private *replmd_private,
3758 struct ldb_message_element *el,
3759 const struct dsdb_attribute *sa,
3760 struct ldb_request *parent)
3763 TALLOC_CTX *tmp_ctx = talloc_new(module);
3764 struct ldb_context *ldb = ldb_module_get_ctx(module);
3766 for (i=0; i<el->num_values; i++) {
3767 struct dsdb_dn *dsdb_dn;
3769 struct ldb_message *msg;
3770 const struct dsdb_attribute *target_attr;
3771 struct ldb_message_element *el2;
3773 struct ldb_val dn_val;
3774 uint32_t dsdb_flags = 0;
3775 const char *attrs[] = { NULL, NULL };
3776 struct ldb_result *link_res;
3777 struct ldb_message *link_msg;
3778 struct ldb_message_element *link_el;
3779 struct parsed_dn *link_dns;
3780 struct parsed_dn *p = NULL, *unused = NULL;
3782 if (dsdb_dn_is_deleted_val(&el->values[i])) {
3786 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
3788 talloc_free(tmp_ctx);
3789 return LDB_ERR_OPERATIONS_ERROR;
3792 /* remove the link */
3793 msg = ldb_msg_new(tmp_ctx);
3795 ldb_module_oom(module);
3796 talloc_free(tmp_ctx);
3797 return LDB_ERR_OPERATIONS_ERROR;
3801 msg->dn = dsdb_dn->dn;
3803 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
3804 if (target_attr == NULL) {
3807 attrs[0] = target_attr->lDAPDisplayName;
3809 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
3810 LDB_FLAG_MOD_DELETE, &el2);
3811 if (ret != LDB_SUCCESS) {
3812 ldb_module_oom(module);
3813 talloc_free(tmp_ctx);
3814 return LDB_ERR_OPERATIONS_ERROR;
3817 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
3819 DSDB_FLAG_NEXT_MODULE |
3820 DSDB_SEARCH_SHOW_EXTENDED_DN,
3823 if (ret != LDB_SUCCESS) {
3824 talloc_free(tmp_ctx);
3828 link_msg = link_res->msgs[0];
3829 link_el = ldb_msg_find_element(link_msg,
3830 target_attr->lDAPDisplayName);
3831 if (link_el == NULL) {
3832 talloc_free(tmp_ctx);
3833 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3837 * This call 'upgrades' the links in link_dns, but we
3838 * do not commit the result back into the database, so
3839 * this is safe to call in FL2000 or on databases that
3840 * have been run at that level in the past.
3842 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx,
3844 target_attr->syntax->ldap_oid, parent);
3845 if (ret != LDB_SUCCESS) {
3846 talloc_free(tmp_ctx);
3850 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
3851 guid, dn, &p, &unused,
3852 target_attr->syntax->ldap_oid);
3853 if (ret != LDB_SUCCESS) {
3854 talloc_free(tmp_ctx);
3859 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3860 "Failed to find forward link on %s "
3861 "as %s to remove backlink %s on %s",
3862 ldb_dn_get_linearized(msg->dn),
3863 target_attr->lDAPDisplayName,
3864 sa->lDAPDisplayName,
3865 ldb_dn_get_linearized(dn));
3866 talloc_free(tmp_ctx);
3867 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3871 /* This needs to get the Binary DN, by first searching */
3872 dn_str = dsdb_dn_get_linearized(tmp_ctx,
3874 dn_val = data_blob_string_const(dn_str);
3875 el2->values = &dn_val;
3876 el2->num_values = 1;
3879 * Ensure that we tell the modification to vanish any linked
3880 * attributes (not simply mark them as isDeleted = TRUE)
3882 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
3884 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
3885 if (ret != LDB_SUCCESS) {
3886 talloc_free(tmp_ctx);
3890 talloc_free(tmp_ctx);
3896 handle update of replication meta data for deletion of objects
3898 This also handles the mapping of delete to a rename operation
3899 to allow deletes to be replicated.
3901 It also handles the incoming deleted objects, to ensure they are
3902 fully deleted here. In that case re_delete is true, and we do not
3903 use this as a signal to change the deleted state, just reinforce it.
3906 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
3908 int ret = LDB_ERR_OTHER;
3909 bool retb, disallow_move_on_delete;
3910 struct ldb_dn *old_dn, *new_dn;
3911 const char *rdn_name;
3912 const struct ldb_val *rdn_value, *new_rdn_value;
3914 struct ldb_context *ldb = ldb_module_get_ctx(module);
3915 const struct dsdb_schema *schema;
3916 struct ldb_message *msg, *old_msg;
3917 struct ldb_message_element *el;
3918 TALLOC_CTX *tmp_ctx;
3919 struct ldb_result *res, *parent_res;
3920 static const char * const preserved_attrs[] = {
3921 /* yes, this really is a hard coded list. See MS-ADTS
3922 section 3.1.1.5.5.1.1 */
3925 "dNReferenceUpdate",
3936 "msDS-LastKnownRDN",
3942 "distinguishedName",
3946 "proxiedObjectName",
3948 "nTSecurityDescriptor",
3949 "replPropertyMetaData",
3951 "securityIdentifier",
3959 "userAccountControl",
3966 static const char * const all_attrs[] = {
3967 DSDB_SECRET_ATTRIBUTES,
3971 unsigned int i, el_count = 0;
3972 uint32_t dsdb_flags = 0;
3973 struct replmd_private *replmd_private;
3974 enum deletion_state deletion_state, next_deletion_state;
3976 if (ldb_dn_is_special(req->op.del.dn)) {
3977 return ldb_next_request(module, req);
3981 * We have to allow dbcheck to remove an object that
3982 * is beyond repair, and to do so totally. This could
3983 * mean we we can get a partial object from the other
3984 * DC, causing havoc, so dbcheck suggests
3985 * re-replication first. dbcheck sets both DBCHECK
3986 * and RELAX in this situation.
3988 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
3989 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
3990 /* really, really remove it */
3991 return ldb_next_request(module, req);
3994 tmp_ctx = talloc_new(ldb);
3997 return LDB_ERR_OPERATIONS_ERROR;
4000 schema = dsdb_get_schema(ldb, tmp_ctx);
4002 talloc_free(tmp_ctx);
4003 return LDB_ERR_OPERATIONS_ERROR;
4006 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
4008 /* we need the complete msg off disk, so we can work out which
4009 attributes need to be removed */
4010 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
4011 DSDB_FLAG_NEXT_MODULE |
4012 DSDB_SEARCH_SHOW_RECYCLED |
4013 DSDB_SEARCH_REVEAL_INTERNALS |
4014 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
4015 if (ret != LDB_SUCCESS) {
4016 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4017 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
4018 re_delete ? "re-delete" : "delete",
4019 ldb_dn_get_linearized(old_dn),
4020 ldb_errstring(ldb_module_get_ctx(module)));
4021 talloc_free(tmp_ctx);
4024 old_msg = res->msgs[0];
4026 replmd_deletion_state(module, old_msg,
4028 &next_deletion_state);
4030 /* This supports us noticing an incoming isDeleted and acting on it */
4032 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
4033 next_deletion_state = deletion_state;
4036 if (next_deletion_state == OBJECT_REMOVED) {
4038 * We have to prevent objects being deleted, even if
4039 * the administrator really wants them gone, as
4040 * without the tombstone, we can get a partial object
4041 * from the other DC, causing havoc.
4043 * The only other valid case is when the 180 day
4044 * timeout has expired, when relax is specified.
4046 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
4047 /* it is already deleted - really remove it this time */
4048 talloc_free(tmp_ctx);
4049 return ldb_next_request(module, req);
4052 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
4053 "This check is to prevent corruption of the replicated state.",
4054 ldb_dn_get_linearized(old_msg->dn));
4055 return LDB_ERR_UNWILLING_TO_PERFORM;
4058 rdn_name = ldb_dn_get_rdn_name(old_dn);
4059 rdn_value = ldb_dn_get_rdn_val(old_dn);
4060 if ((rdn_name == NULL) || (rdn_value == NULL)) {
4061 talloc_free(tmp_ctx);
4062 return ldb_operr(ldb);
4065 msg = ldb_msg_new(tmp_ctx);
4067 ldb_module_oom(module);
4068 talloc_free(tmp_ctx);
4069 return LDB_ERR_OPERATIONS_ERROR;
4074 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4075 disallow_move_on_delete =
4076 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4077 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4079 /* work out where we will be renaming this object to */
4080 if (!disallow_move_on_delete) {
4081 struct ldb_dn *deleted_objects_dn;
4082 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4083 &deleted_objects_dn);
4086 * We should not move objects if we can't find the
4087 * deleted objects DN. Not moving (or otherwise
4088 * harming) the Deleted Objects DN itself is handled
4091 if (re_delete && (ret != LDB_SUCCESS)) {
4092 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4093 if (new_dn == NULL) {
4094 ldb_module_oom(module);
4095 talloc_free(tmp_ctx);
4096 return LDB_ERR_OPERATIONS_ERROR;
4098 } else if (ret != LDB_SUCCESS) {
4099 /* this is probably an attempted delete on a partition
4100 * that doesn't allow delete operations, such as the
4101 * schema partition */
4102 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4103 ldb_dn_get_linearized(old_dn));
4104 talloc_free(tmp_ctx);
4105 return LDB_ERR_UNWILLING_TO_PERFORM;
4107 new_dn = deleted_objects_dn;
4110 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4111 if (new_dn == NULL) {
4112 ldb_module_oom(module);
4113 talloc_free(tmp_ctx);
4114 return LDB_ERR_OPERATIONS_ERROR;
4118 /* get the objects GUID from the search we just did */
4119 guid = samdb_result_guid(old_msg, "objectGUID");
4121 if (deletion_state == OBJECT_NOT_DELETED) {
4122 /* Add a formatted child */
4123 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
4125 ldb_dn_escape_value(tmp_ctx, *rdn_value),
4126 GUID_string(tmp_ctx, &guid));
4128 ldb_asprintf_errstring(ldb, __location__
4129 ": Unable to add a formatted child to dn: %s",
4130 ldb_dn_get_linearized(new_dn));
4131 talloc_free(tmp_ctx);
4132 return LDB_ERR_OPERATIONS_ERROR;
4135 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
4136 if (ret != LDB_SUCCESS) {
4137 ldb_asprintf_errstring(ldb, __location__
4138 ": Failed to add isDeleted string to the msg");
4139 talloc_free(tmp_ctx);
4142 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4145 * No matter what has happened with other renames etc, try again to
4146 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4149 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4150 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4152 ldb_asprintf_errstring(ldb, __location__
4153 ": Unable to add a prepare rdn of %s",
4154 ldb_dn_get_linearized(rdn));
4155 talloc_free(tmp_ctx);
4156 return LDB_ERR_OPERATIONS_ERROR;
4158 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4160 retb = ldb_dn_add_child(new_dn, rdn);
4162 ldb_asprintf_errstring(ldb, __location__
4163 ": Unable to add rdn %s to base dn: %s",
4164 ldb_dn_get_linearized(rdn),
4165 ldb_dn_get_linearized(new_dn));
4166 talloc_free(tmp_ctx);
4167 return LDB_ERR_OPERATIONS_ERROR;
4172 now we need to modify the object in the following ways:
4174 - add isDeleted=TRUE
4175 - update rDN and name, with new rDN
4176 - remove linked attributes
4177 - remove objectCategory and sAMAccountType
4178 - remove attribs not on the preserved list
4179 - preserved if in above list, or is rDN
4180 - remove all linked attribs from this object
4181 - remove all links from other objects to this object
4182 - add lastKnownParent
4183 - update replPropertyMetaData?
4185 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4188 if (deletion_state == OBJECT_NOT_DELETED) {
4189 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4190 char *parent_dn_str = NULL;
4192 /* we need the storage form of the parent GUID */
4193 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4195 DSDB_FLAG_NEXT_MODULE |
4196 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4197 DSDB_SEARCH_REVEAL_INTERNALS|
4198 DSDB_SEARCH_SHOW_RECYCLED, req);
4199 if (ret != LDB_SUCCESS) {
4200 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4201 "repmd_delete: Failed to %s %s, "
4202 "because we failed to find it's parent (%s): %s",
4203 re_delete ? "re-delete" : "delete",
4204 ldb_dn_get_linearized(old_dn),
4205 ldb_dn_get_linearized(parent_dn),
4206 ldb_errstring(ldb_module_get_ctx(module)));
4207 talloc_free(tmp_ctx);
4212 * Now we can use the DB version,
4213 * it will have the extended DN info in it
4215 parent_dn = parent_res->msgs[0]->dn;
4216 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4219 if (parent_dn_str == NULL) {
4220 talloc_free(tmp_ctx);
4221 return ldb_module_oom(module);
4224 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4226 if (ret != LDB_SUCCESS) {
4227 ldb_asprintf_errstring(ldb, __location__
4228 ": Failed to add lastKnownParent "
4229 "string when deleting %s",
4230 ldb_dn_get_linearized(old_dn));
4231 talloc_free(tmp_ctx);
4234 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4236 if (next_deletion_state == OBJECT_DELETED) {
4237 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4238 if (ret != LDB_SUCCESS) {
4239 ldb_asprintf_errstring(ldb, __location__
4240 ": Failed to add msDS-LastKnownRDN "
4241 "string when deleting %s",
4242 ldb_dn_get_linearized(old_dn));
4243 talloc_free(tmp_ctx);
4246 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
4250 switch (next_deletion_state) {
4252 case OBJECT_RECYCLED:
4253 case OBJECT_TOMBSTONE:
4256 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4257 * describes what must be removed from a tombstone
4260 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4261 * describes what must be removed from a recycled
4267 * we also mark it as recycled, meaning this object can't be
4268 * recovered (we are stripping its attributes).
4269 * This is done only if we have this schema object of course ...
4270 * This behavior is identical to the one of Windows 2008R2 which
4271 * always set the isRecycled attribute, even if the recycle-bin is
4272 * not activated and what ever the forest level is.
4274 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4275 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
4276 if (ret != LDB_SUCCESS) {
4277 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4278 ldb_module_oom(module);
4279 talloc_free(tmp_ctx);
4282 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4285 replmd_private = talloc_get_type(ldb_module_get_private(module),
4286 struct replmd_private);
4287 /* work out which of the old attributes we will be removing */
4288 for (i=0; i<old_msg->num_elements; i++) {
4289 const struct dsdb_attribute *sa;
4290 el = &old_msg->elements[i];
4291 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4293 talloc_free(tmp_ctx);
4294 return LDB_ERR_OPERATIONS_ERROR;
4296 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4297 /* don't remove the rDN */
4300 if (sa->linkID & 1) {
4302 we have a backlink in this object
4303 that needs to be removed. We're not
4304 allowed to remove it directly
4305 however, so we instead setup a
4306 modify to delete the corresponding
4309 ret = replmd_delete_remove_link(module, schema,
4313 if (ret != LDB_SUCCESS) {
4314 const char *old_dn_str
4315 = ldb_dn_get_linearized(old_dn);
4316 ldb_asprintf_errstring(ldb,
4318 ": Failed to remove backlink of "
4319 "%s when deleting %s: %s",
4322 ldb_errstring(ldb));
4323 talloc_free(tmp_ctx);
4324 return LDB_ERR_OPERATIONS_ERROR;
4326 /* now we continue, which means we
4327 won't remove this backlink
4331 } else if (sa->linkID == 0) {
4332 if (ldb_attr_in_list(preserved_attrs, el->name)) {
4335 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4340 * Ensure that we tell the modification to vanish any linked
4341 * attributes (not simply mark them as isDeleted = TRUE)
4343 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4345 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4346 if (ret != LDB_SUCCESS) {
4347 talloc_free(tmp_ctx);
4348 ldb_module_oom(module);
4355 case OBJECT_DELETED:
4357 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4358 * describes what must be removed from a deleted
4362 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4363 if (ret != LDB_SUCCESS) {
4364 talloc_free(tmp_ctx);
4365 ldb_module_oom(module);
4369 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4370 if (ret != LDB_SUCCESS) {
4371 talloc_free(tmp_ctx);
4372 ldb_module_oom(module);
4382 if (deletion_state == OBJECT_NOT_DELETED) {
4383 const struct dsdb_attribute *sa;
4385 /* work out what the new rdn value is, for updating the
4386 rDN and name fields */
4387 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4388 if (new_rdn_value == NULL) {
4389 talloc_free(tmp_ctx);
4390 return ldb_operr(ldb);
4393 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4395 talloc_free(tmp_ctx);
4396 return LDB_ERR_OPERATIONS_ERROR;
4399 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4401 if (ret != LDB_SUCCESS) {
4402 talloc_free(tmp_ctx);
4405 el->flags = LDB_FLAG_MOD_REPLACE;
4407 el = ldb_msg_find_element(old_msg, "name");
4409 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4410 if (ret != LDB_SUCCESS) {
4411 talloc_free(tmp_ctx);
4414 el->flags = LDB_FLAG_MOD_REPLACE;
4419 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4424 * No matter what has happned with other renames, try again to
4425 * get this to be under the deleted DN.
4427 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4428 /* now rename onto the new DN */
4429 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4430 if (ret != LDB_SUCCESS){
4431 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4432 ldb_dn_get_linearized(old_dn),
4433 ldb_dn_get_linearized(new_dn),
4434 ldb_errstring(ldb)));
4435 talloc_free(tmp_ctx);
4441 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4442 if (ret != LDB_SUCCESS) {
4443 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
4444 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
4445 talloc_free(tmp_ctx);
4449 talloc_free(tmp_ctx);
4451 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4454 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4456 return replmd_delete_internals(module, req, false);
4460 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4465 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4467 int ret = LDB_ERR_OTHER;
4468 /* TODO: do some error mapping */
4470 /* Let the caller know the full WERROR */
4471 ar->objs->error = status;
4477 static struct replPropertyMetaData1 *
4478 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4479 enum drsuapi_DsAttributeId attid)
4482 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4484 for (i = 0; i < rpmd_ctr->count; i++) {
4485 if (rpmd_ctr->array[i].attid == attid) {
4486 return &rpmd_ctr->array[i];
4494 return true if an update is newer than an existing entry
4495 see section 5.11 of MS-ADTS
4497 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4498 const struct GUID *update_invocation_id,
4499 uint32_t current_version,
4500 uint32_t update_version,
4501 NTTIME current_change_time,
4502 NTTIME update_change_time)
4504 if (update_version != current_version) {
4505 return update_version > current_version;
4507 if (update_change_time != current_change_time) {
4508 return update_change_time > current_change_time;
4510 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4513 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4514 struct replPropertyMetaData1 *new_m)
4516 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4517 &new_m->originating_invocation_id,
4520 cur_m->originating_change_time,
4521 new_m->originating_change_time);
4524 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4525 struct replPropertyMetaData1 *cur_m,
4526 struct replPropertyMetaData1 *new_m)
4531 * If the new replPropertyMetaData entry for this attribute is
4532 * not provided (this happens in the case where we look for
4533 * ATTID_name, but the name was not changed), then the local
4534 * state is clearly still current, as the remote
4535 * server didn't send it due to being older the high watermark
4538 if (new_m == NULL) {
4542 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4544 * if we compare equal then do an
4545 * update. This is used when a client
4546 * asks for a FULL_SYNC, and can be
4547 * used to recover a corrupt
4550 * This call is a bit tricky, what we
4551 * are doing it turning the 'is_newer'
4552 * call into a 'not is older' by
4553 * swapping cur_m and new_m, and negating the
4556 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4559 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4569 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
4571 const struct ldb_val *rdn_val;
4572 const char *rdn_name;
4573 struct ldb_dn *new_dn;
4575 rdn_val = ldb_dn_get_rdn_val(dn);
4576 rdn_name = ldb_dn_get_rdn_name(dn);
4577 if (!rdn_val || !rdn_name) {
4581 new_dn = ldb_dn_copy(mem_ctx, dn);
4586 if (!ldb_dn_remove_child_components(new_dn, 1)) {
4590 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
4592 ldb_dn_escape_value(new_dn, *rdn_val),
4593 GUID_string(new_dn, guid))) {
4602 perform a modify operation which sets the rDN and name attributes to
4603 their current values. This has the effect of changing these
4604 attributes to have been last updated by the current DC. This is
4605 needed to ensure that renames performed as part of conflict
4606 resolution are propogated to other DCs
4608 static int replmd_name_modify(struct replmd_replicated_request *ar,
4609 struct ldb_request *req, struct ldb_dn *dn)
4611 struct ldb_message *msg;
4612 const char *rdn_name;
4613 const struct ldb_val *rdn_val;
4614 const struct dsdb_attribute *rdn_attr;
4617 msg = ldb_msg_new(req);
4623 rdn_name = ldb_dn_get_rdn_name(dn);
4624 if (rdn_name == NULL) {
4628 /* normalize the rdn attribute name */
4629 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
4630 if (rdn_attr == NULL) {
4633 rdn_name = rdn_attr->lDAPDisplayName;
4635 rdn_val = ldb_dn_get_rdn_val(dn);
4636 if (rdn_val == NULL) {
4640 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4643 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
4646 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4649 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
4653 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
4654 if (ret != LDB_SUCCESS) {
4655 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
4656 ldb_dn_get_linearized(dn),
4657 ldb_errstring(ldb_module_get_ctx(ar->module))));
4667 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
4668 ldb_dn_get_linearized(dn)));
4669 return LDB_ERR_OPERATIONS_ERROR;
4674 callback for conflict DN handling where we have renamed the incoming
4675 record. After renaming it, we need to ensure the change of name and
4676 rDN for the incoming record is seen as an originating update by this DC.
4678 This also handles updating lastKnownParent for entries sent to lostAndFound
4680 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
4682 struct replmd_replicated_request *ar =
4683 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4684 struct ldb_dn *conflict_dn = NULL;
4687 if (ares->error != LDB_SUCCESS) {
4688 /* call the normal callback for everything except success */
4689 return replmd_op_callback(req, ares);
4692 switch (req->operation) {
4694 conflict_dn = req->op.add.message->dn;
4697 conflict_dn = req->op.mod.message->dn;
4700 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
4703 /* perform a modify of the rDN and name of the record */
4704 ret = replmd_name_modify(ar, req, conflict_dn);
4705 if (ret != LDB_SUCCESS) {
4707 return replmd_op_callback(req, ares);
4710 if (ar->objs->objects[ar->index_current].last_known_parent) {
4711 struct ldb_message *msg = ldb_msg_new(req);
4713 ldb_module_oom(ar->module);
4714 return LDB_ERR_OPERATIONS_ERROR;
4717 msg->dn = req->op.add.message->dn;
4719 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4720 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
4721 if (ret != LDB_SUCCESS) {
4722 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
4723 ldb_module_oom(ar->module);
4726 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
4728 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
4729 if (ret != LDB_SUCCESS) {
4730 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
4731 ldb_dn_get_linearized(msg->dn),
4732 ldb_errstring(ldb_module_get_ctx(ar->module))));
4738 return replmd_op_callback(req, ares);
4742 callback for replmd_replicated_apply_add()
4743 This copes with the creation of conflict records in the case where
4744 the DN exists, but with a different objectGUID
4746 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))
4748 struct ldb_dn *conflict_dn;
4749 struct replmd_replicated_request *ar =
4750 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4751 struct ldb_result *res;
4752 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4754 const struct ldb_val *omd_value;
4755 struct replPropertyMetaDataBlob omd, *rmd;
4756 enum ndr_err_code ndr_err;
4757 bool rename_incoming_record, rodc;
4758 struct replPropertyMetaData1 *rmd_name, *omd_name;
4759 struct ldb_message *msg;
4760 struct ldb_request *down_req = NULL;
4762 /* call the normal callback for success */
4763 if (ares->error == LDB_SUCCESS) {
4764 return callback(req, ares);
4768 * we have a conflict, and need to decide if we will keep the
4769 * new record or the old record
4772 msg = ar->objs->objects[ar->index_current].msg;
4773 conflict_dn = msg->dn;
4775 /* For failures other than conflicts, fail the whole operation here */
4776 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4777 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
4778 ldb_dn_get_linearized(conflict_dn),
4779 ldb_errstring(ldb_module_get_ctx(ar->module)));
4781 return ldb_module_done(ar->req, NULL, NULL,
4782 LDB_ERR_OPERATIONS_ERROR);
4785 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4786 if (ret != LDB_SUCCESS) {
4787 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)));
4788 return ldb_module_done(ar->req, NULL, NULL,
4789 LDB_ERR_OPERATIONS_ERROR);
4795 * We are on an RODC, or were a GC for this
4796 * partition, so we have to fail this until
4797 * someone who owns the partition sorts it
4800 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4801 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
4802 " - We must fail the operation until a master for this partition resolves the conflict",
4803 ldb_dn_get_linearized(conflict_dn));
4808 * first we need the replPropertyMetaData attribute from the
4809 * local, conflicting record
4811 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
4813 DSDB_FLAG_NEXT_MODULE |
4814 DSDB_SEARCH_SHOW_DELETED |
4815 DSDB_SEARCH_SHOW_RECYCLED, req);
4816 if (ret != LDB_SUCCESS) {
4817 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4818 ldb_dn_get_linearized(conflict_dn)));
4822 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4823 if (omd_value == NULL) {
4824 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4825 ldb_dn_get_linearized(conflict_dn)));
4829 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4830 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4831 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4832 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4833 ldb_dn_get_linearized(conflict_dn)));
4837 rmd = ar->objs->objects[ar->index_current].meta_data;
4840 * we decide which is newer based on the RPMD on the name
4841 * attribute. See [MS-DRSR] ResolveNameConflict.
4843 * We expect omd_name to be present, as this is from a local
4844 * search, but while rmd_name should have been given to us by
4845 * the remote server, if it is missing we just prefer the
4847 * replmd_replPropertyMetaData1_new_should_be_taken()
4849 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4850 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4852 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4853 ldb_dn_get_linearized(conflict_dn)));
4858 * Should we preserve the current record, and so rename the
4859 * incoming record to be a conflict?
4861 rename_incoming_record
4862 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
4863 omd_name, rmd_name);
4865 if (rename_incoming_record) {
4867 struct ldb_dn *new_dn;
4869 guid = samdb_result_guid(msg, "objectGUID");
4870 if (GUID_all_zero(&guid)) {
4871 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
4872 ldb_dn_get_linearized(conflict_dn)));
4875 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4876 if (new_dn == NULL) {
4877 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4878 ldb_dn_get_linearized(conflict_dn)));
4882 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
4883 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4885 /* re-submit the request, but with the new DN */
4886 callback = replmd_op_name_modify_callback;
4889 /* we are renaming the existing record */
4891 struct ldb_dn *new_dn;
4893 guid = samdb_result_guid(res->msgs[0], "objectGUID");
4894 if (GUID_all_zero(&guid)) {
4895 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
4896 ldb_dn_get_linearized(conflict_dn)));
4900 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4901 if (new_dn == NULL) {
4902 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4903 ldb_dn_get_linearized(conflict_dn)));
4907 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4908 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4910 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
4911 DSDB_FLAG_OWN_MODULE, req);
4912 if (ret != LDB_SUCCESS) {
4913 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
4914 ldb_dn_get_linearized(conflict_dn),
4915 ldb_dn_get_linearized(new_dn),
4916 ldb_errstring(ldb_module_get_ctx(ar->module))));
4921 * now we need to ensure that the rename is seen as an
4922 * originating update. We do that with a modify.
4924 ret = replmd_name_modify(ar, req, new_dn);
4925 if (ret != LDB_SUCCESS) {
4929 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
4930 ldb_dn_get_linearized(req->op.add.message->dn)));
4933 ret = ldb_build_add_req(&down_req,
4934 ldb_module_get_ctx(ar->module),
4941 if (ret != LDB_SUCCESS) {
4944 LDB_REQ_SET_LOCATION(down_req);
4946 /* current partition control needed by "repmd_op_callback" */
4947 ret = ldb_request_add_control(down_req,
4948 DSDB_CONTROL_CURRENT_PARTITION_OID,
4950 if (ret != LDB_SUCCESS) {
4951 return replmd_replicated_request_error(ar, ret);
4954 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4955 /* this tells the partition module to make it a
4956 partial replica if creating an NC */
4957 ret = ldb_request_add_control(down_req,
4958 DSDB_CONTROL_PARTIAL_REPLICA,
4960 if (ret != LDB_SUCCESS) {
4961 return replmd_replicated_request_error(ar, ret);
4966 * Finally we re-run the add, otherwise the new record won't
4967 * exist, as we are here because of that exact failure!
4969 return ldb_next_request(ar->module, down_req);
4972 /* on failure make the caller get the error. This means
4973 * replication will stop with an error, but there is not much
4976 return ldb_module_done(ar->req, NULL, NULL,
4981 callback for replmd_replicated_apply_add()
4982 This copes with the creation of conflict records in the case where
4983 the DN exists, but with a different objectGUID
4985 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
4987 struct replmd_replicated_request *ar =
4988 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4990 if (ar->objs->objects[ar->index_current].last_known_parent) {
4991 /* This is like a conflict DN, where we put the object in LostAndFound
4992 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
4993 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
4996 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
5000 this is called when a new object comes in over DRS
5002 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
5004 struct ldb_context *ldb;
5005 struct ldb_request *change_req;
5006 enum ndr_err_code ndr_err;
5007 struct ldb_message *msg;
5008 struct replPropertyMetaDataBlob *md;
5009 struct ldb_val md_value;
5012 bool remote_isDeleted = false;
5015 time_t t = time(NULL);
5016 const struct ldb_val *rdn_val;
5017 struct replmd_private *replmd_private =
5018 talloc_get_type(ldb_module_get_private(ar->module),
5019 struct replmd_private);
5020 unix_to_nt_time(&now, t);
5022 ldb = ldb_module_get_ctx(ar->module);
5023 msg = ar->objs->objects[ar->index_current].msg;
5024 md = ar->objs->objects[ar->index_current].meta_data;
5025 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5027 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5028 if (ret != LDB_SUCCESS) {
5029 return replmd_replicated_request_error(ar, ret);
5032 ret = dsdb_msg_add_guid(msg,
5033 &ar->objs->objects[ar->index_current].object_guid,
5035 if (ret != LDB_SUCCESS) {
5036 return replmd_replicated_request_error(ar, ret);
5039 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5040 if (ret != LDB_SUCCESS) {
5041 return replmd_replicated_request_error(ar, ret);
5044 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
5045 if (ret != LDB_SUCCESS) {
5046 return replmd_replicated_request_error(ar, ret);
5049 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5050 if (ret != LDB_SUCCESS) {
5051 return replmd_replicated_request_error(ar, ret);
5054 /* remove any message elements that have zero values */
5055 for (i=0; i<msg->num_elements; i++) {
5056 struct ldb_message_element *el = &msg->elements[i];
5058 if (el->num_values == 0) {
5059 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5060 ldb_asprintf_errstring(ldb, __location__
5061 ": empty objectClass sent on %s, aborting replication\n",
5062 ldb_dn_get_linearized(msg->dn));
5063 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5066 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5068 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
5069 msg->num_elements--;
5076 struct GUID_txt_buf guid_txt;
5078 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
5079 DEBUG(4, ("DRS replication add message of %s:\n%s\n",
5080 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5085 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5086 "isDeleted", false);
5089 * the meta data array is already sorted by the caller, except
5090 * for the RDN, which needs to be added.
5094 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5095 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5096 md, ar, now, is_schema_nc);
5097 if (ret != LDB_SUCCESS) {
5098 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5099 return replmd_replicated_request_error(ar, ret);
5102 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5103 if (ret != LDB_SUCCESS) {
5104 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5105 return replmd_replicated_request_error(ar, ret);
5108 for (i=0; i < md->ctr.ctr1.count; i++) {
5109 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5111 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5112 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5113 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5114 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5115 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5117 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5118 if (ret != LDB_SUCCESS) {
5119 return replmd_replicated_request_error(ar, ret);
5122 replmd_ldb_message_sort(msg, ar->schema);
5124 if (!remote_isDeleted) {
5125 ret = dsdb_module_schedule_sd_propagation(ar->module,
5126 ar->objs->partition_dn,
5128 if (ret != LDB_SUCCESS) {
5129 return replmd_replicated_request_error(ar, ret);
5133 ar->isDeleted = remote_isDeleted;
5135 ret = ldb_build_add_req(&change_req,
5141 replmd_op_add_callback,
5143 LDB_REQ_SET_LOCATION(change_req);
5144 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5146 /* current partition control needed by "repmd_op_callback" */
5147 ret = ldb_request_add_control(change_req,
5148 DSDB_CONTROL_CURRENT_PARTITION_OID,
5150 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5152 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5153 /* this tells the partition module to make it a
5154 partial replica if creating an NC */
5155 ret = ldb_request_add_control(change_req,
5156 DSDB_CONTROL_PARTIAL_REPLICA,
5158 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5161 return ldb_next_request(ar->module, change_req);
5164 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5165 struct ldb_reply *ares)
5167 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5168 struct replmd_replicated_request);
5172 return ldb_module_done(ar->req, NULL, NULL,
5173 LDB_ERR_OPERATIONS_ERROR);
5177 * The error NO_SUCH_OBJECT is not expected, unless the search
5178 * base is the partition DN, and that case doesn't happen here
5179 * because then we wouldn't get a parent_guid_value in any
5182 if (ares->error != LDB_SUCCESS) {
5183 return ldb_module_done(ar->req, ares->controls,
5184 ares->response, ares->error);
5187 switch (ares->type) {
5188 case LDB_REPLY_ENTRY:
5190 struct ldb_message *parent_msg = ares->message;
5191 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5192 struct ldb_dn *parent_dn;
5195 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5196 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5197 /* Per MS-DRSR 4.1.10.6.10
5198 * FindBestParentObject we need to move this
5199 * new object under a deleted object to
5201 struct ldb_dn *nc_root;
5203 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5204 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5205 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5206 "No suitable NC root found for %s. "
5207 "We need to move this object because parent object %s "
5208 "is deleted, but this object is not.",
5209 ldb_dn_get_linearized(msg->dn),
5210 ldb_dn_get_linearized(parent_msg->dn));
5211 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5212 } else if (ret != LDB_SUCCESS) {
5213 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5214 "Unable to find NC root for %s: %s. "
5215 "We need to move this object because parent object %s "
5216 "is deleted, but this object is not.",
5217 ldb_dn_get_linearized(msg->dn),
5218 ldb_errstring(ldb_module_get_ctx(ar->module)),
5219 ldb_dn_get_linearized(parent_msg->dn));
5220 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5223 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5225 DS_GUID_LOSTANDFOUND_CONTAINER,
5227 if (ret != LDB_SUCCESS) {
5228 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5229 "Unable to find LostAndFound Container for %s "
5230 "in partition %s: %s. "
5231 "We need to move this object because parent object %s "
5232 "is deleted, but this object is not.",
5233 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5234 ldb_errstring(ldb_module_get_ctx(ar->module)),
5235 ldb_dn_get_linearized(parent_msg->dn));
5236 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5238 ar->objs->objects[ar->index_current].last_known_parent
5239 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5243 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5246 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5248 comp_num = ldb_dn_get_comp_num(msg->dn);
5250 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5252 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5255 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5257 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5261 case LDB_REPLY_REFERRAL:
5262 /* we ignore referrals */
5265 case LDB_REPLY_DONE:
5267 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5268 struct GUID_txt_buf str_buf;
5269 if (ar->search_msg != NULL) {
5270 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5271 "No parent with GUID %s found for object locally known as %s",
5272 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5273 ldb_dn_get_linearized(ar->search_msg->dn));
5275 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5276 "No parent with GUID %s found for object remotely known as %s",
5277 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5278 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5282 * This error code is really important, as it
5283 * is the flag back to the callers to retry
5284 * this with DRSUAPI_DRS_GET_ANC, and so get
5285 * the parent objects before the child
5288 return ldb_module_done(ar->req, NULL, NULL,
5289 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5292 if (ar->search_msg != NULL) {
5293 ret = replmd_replicated_apply_merge(ar);
5295 ret = replmd_replicated_apply_add(ar);
5297 if (ret != LDB_SUCCESS) {
5298 return ldb_module_done(ar->req, NULL, NULL, ret);
5307 * Look for the parent object, so we put the new object in the right
5308 * place This is akin to NameObject in MS-DRSR - this routine and the
5309 * callbacks find the right parent name, and correct name for this
5313 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5315 struct ldb_context *ldb;
5319 struct ldb_request *search_req;
5320 static const char *attrs[] = {"isDeleted", NULL};
5321 struct GUID_txt_buf guid_str_buf;
5323 ldb = ldb_module_get_ctx(ar->module);
5325 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5326 if (ar->search_msg != NULL) {
5327 return replmd_replicated_apply_merge(ar);
5329 return replmd_replicated_apply_add(ar);
5333 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5336 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5337 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5339 ret = ldb_build_search_req(&search_req,
5342 ar->objs->partition_dn,
5348 replmd_replicated_apply_search_for_parent_callback,
5350 LDB_REQ_SET_LOCATION(search_req);
5352 ret = dsdb_request_add_controls(search_req,
5353 DSDB_SEARCH_SHOW_RECYCLED|
5354 DSDB_SEARCH_SHOW_DELETED|
5355 DSDB_SEARCH_SHOW_EXTENDED_DN);
5356 if (ret != LDB_SUCCESS) {
5360 return ldb_next_request(ar->module, search_req);
5364 handle renames that come in over DRS replication
5366 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5367 struct ldb_message *msg,
5368 struct ldb_request *parent,
5372 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5373 struct ldb_result *res;
5374 struct ldb_dn *conflict_dn;
5375 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5376 const struct ldb_val *omd_value;
5377 struct replPropertyMetaDataBlob omd, *rmd;
5378 enum ndr_err_code ndr_err;
5379 bool rename_incoming_record, rodc;
5380 struct replPropertyMetaData1 *rmd_name, *omd_name;
5381 struct ldb_dn *new_dn;
5384 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5385 ldb_dn_get_linearized(ar->search_msg->dn),
5386 ldb_dn_get_linearized(msg->dn)));
5389 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5390 DSDB_FLAG_NEXT_MODULE, ar->req);
5391 if (ret == LDB_SUCCESS) {
5392 talloc_free(tmp_ctx);
5397 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5398 talloc_free(tmp_ctx);
5399 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5400 ldb_dn_get_linearized(ar->search_msg->dn),
5401 ldb_dn_get_linearized(msg->dn),
5402 ldb_errstring(ldb_module_get_ctx(ar->module)));
5406 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5407 if (ret != LDB_SUCCESS) {
5408 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5409 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5410 ldb_errstring(ldb_module_get_ctx(ar->module)));
5411 return LDB_ERR_OPERATIONS_ERROR;
5414 * we have a conflict, and need to decide if we will keep the
5415 * new record or the old record
5418 conflict_dn = msg->dn;
5422 * We are on an RODC, or were a GC for this
5423 * partition, so we have to fail this until
5424 * someone who owns the partition sorts it
5427 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5428 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5429 " - We must fail the operation until a master for this partition resolves the conflict",
5430 ldb_dn_get_linearized(conflict_dn));
5435 * first we need the replPropertyMetaData attribute from the
5438 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
5440 DSDB_FLAG_NEXT_MODULE |
5441 DSDB_SEARCH_SHOW_DELETED |
5442 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5443 if (ret != LDB_SUCCESS) {
5444 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5445 ldb_dn_get_linearized(conflict_dn)));
5449 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5450 if (omd_value == NULL) {
5451 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5452 ldb_dn_get_linearized(conflict_dn)));
5456 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5457 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5458 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5459 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5460 ldb_dn_get_linearized(conflict_dn)));
5464 rmd = ar->objs->objects[ar->index_current].meta_data;
5467 * we decide which is newer based on the RPMD on the name
5468 * attribute. See [MS-DRSR] ResolveNameConflict.
5470 * We expect omd_name to be present, as this is from a local
5471 * search, but while rmd_name should have been given to us by
5472 * the remote server, if it is missing we just prefer the
5474 * replmd_replPropertyMetaData1_new_should_be_taken()
5476 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5477 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5479 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5480 ldb_dn_get_linearized(conflict_dn)));
5485 * Should we preserve the current record, and so rename the
5486 * incoming record to be a conflict?
5488 rename_incoming_record =
5489 !replmd_replPropertyMetaData1_new_should_be_taken(
5490 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5491 omd_name, rmd_name);
5493 if (rename_incoming_record) {
5495 new_dn = replmd_conflict_dn(msg, msg->dn,
5496 &ar->objs->objects[ar->index_current].object_guid);
5497 if (new_dn == NULL) {
5498 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5499 "Failed to form conflict DN for %s\n",
5500 ldb_dn_get_linearized(msg->dn));
5502 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5505 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
5506 DSDB_FLAG_NEXT_MODULE, ar->req);
5507 if (ret != LDB_SUCCESS) {
5508 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5509 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5510 ldb_dn_get_linearized(conflict_dn),
5511 ldb_dn_get_linearized(ar->search_msg->dn),
5512 ldb_dn_get_linearized(new_dn),
5513 ldb_errstring(ldb_module_get_ctx(ar->module)));
5514 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5522 /* we are renaming the existing record */
5524 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5525 if (GUID_all_zero(&guid)) {
5526 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5527 ldb_dn_get_linearized(conflict_dn)));
5531 new_dn = replmd_conflict_dn(tmp_ctx, conflict_dn, &guid);
5532 if (new_dn == NULL) {
5533 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5534 ldb_dn_get_linearized(conflict_dn)));
5538 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5539 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5541 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5542 DSDB_FLAG_OWN_MODULE, ar->req);
5543 if (ret != LDB_SUCCESS) {
5544 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5545 ldb_dn_get_linearized(conflict_dn),
5546 ldb_dn_get_linearized(new_dn),
5547 ldb_errstring(ldb_module_get_ctx(ar->module))));
5552 * now we need to ensure that the rename is seen as an
5553 * originating update. We do that with a modify.
5555 ret = replmd_name_modify(ar, ar->req, new_dn);
5556 if (ret != LDB_SUCCESS) {
5560 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
5561 ldb_dn_get_linearized(ar->search_msg->dn),
5562 ldb_dn_get_linearized(msg->dn)));
5565 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5566 DSDB_FLAG_NEXT_MODULE, ar->req);
5567 if (ret != LDB_SUCCESS) {
5568 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
5569 ldb_dn_get_linearized(ar->search_msg->dn),
5570 ldb_dn_get_linearized(msg->dn),
5571 ldb_errstring(ldb_module_get_ctx(ar->module))));
5577 * On failure make the caller get the error
5578 * This means replication will stop with an error,
5579 * but there is not much else we can do. In the
5580 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
5584 talloc_free(tmp_ctx);
5589 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
5591 struct ldb_context *ldb;
5592 struct ldb_request *change_req;
5593 enum ndr_err_code ndr_err;
5594 struct ldb_message *msg;
5595 struct replPropertyMetaDataBlob *rmd;
5596 struct replPropertyMetaDataBlob omd;
5597 const struct ldb_val *omd_value;
5598 struct replPropertyMetaDataBlob nmd;
5599 struct ldb_val nmd_value;
5600 struct GUID remote_parent_guid;
5603 unsigned int removed_attrs = 0;
5605 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
5606 bool isDeleted = false;
5607 bool local_isDeleted = false;
5608 bool remote_isDeleted = false;
5609 bool take_remote_isDeleted = false;
5610 bool sd_updated = false;
5611 bool renamed = false;
5612 bool is_schema_nc = false;
5614 const struct ldb_val *old_rdn, *new_rdn;
5615 struct replmd_private *replmd_private =
5616 talloc_get_type(ldb_module_get_private(ar->module),
5617 struct replmd_private);
5619 time_t t = time(NULL);
5620 unix_to_nt_time(&now, t);
5622 ldb = ldb_module_get_ctx(ar->module);
5623 msg = ar->objs->objects[ar->index_current].msg;
5625 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5627 rmd = ar->objs->objects[ar->index_current].meta_data;
5631 /* find existing meta data */
5632 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5634 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5635 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5636 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5637 nt_status = ndr_map_error2ntstatus(ndr_err);
5638 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5641 if (omd.version != 1) {
5642 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5647 struct GUID_txt_buf guid_txt;
5649 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5650 DEBUG(5, ("Initial DRS replication modify message of %s is:\n%s\n"
5653 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5655 ndr_print_struct_string(s,
5656 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5657 "existing replPropertyMetaData",
5659 ndr_print_struct_string(s,
5660 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5661 "incoming replPropertyMetaData",
5666 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
5667 "isDeleted", false);
5668 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5669 "isDeleted", false);
5672 * Fill in the remote_parent_guid with the GUID or an all-zero
5675 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
5676 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
5678 remote_parent_guid = GUID_zero();
5682 * To ensure we follow a complex rename chain around, we have
5683 * to confirm that the DN is the same (mostly to confirm the
5684 * RDN) and the parentGUID is the same.
5686 * This ensures we keep things under the correct parent, which
5687 * replmd_replicated_handle_rename() will do.
5690 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
5691 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
5695 * handle renames, even just by case that come in over
5696 * DRS. Changes in the parent DN don't hit us here,
5697 * because the search for a parent will clean up those
5700 * We also have already filtered out the case where
5701 * the peer has an older name to what we have (see
5702 * replmd_replicated_apply_search_callback())
5704 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
5707 if (ret != LDB_SUCCESS) {
5708 ldb_debug(ldb, LDB_DEBUG_FATAL,
5709 "replmd_replicated_request rename %s => %s failed - %s\n",
5710 ldb_dn_get_linearized(ar->search_msg->dn),
5711 ldb_dn_get_linearized(msg->dn),
5712 ldb_errstring(ldb));
5713 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5716 if (renamed == true) {
5718 * Set the callback to one that will fix up the name
5719 * metadata on the new conflict DN
5721 callback = replmd_op_name_modify_callback;
5726 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
5727 nmd.ctr.ctr1.array = talloc_array(ar,
5728 struct replPropertyMetaData1,
5729 nmd.ctr.ctr1.count);
5730 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5732 /* first copy the old meta data */
5733 for (i=0; i < omd.ctr.ctr1.count; i++) {
5734 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
5739 /* now merge in the new meta data */
5740 for (i=0; i < rmd->ctr.ctr1.count; i++) {
5743 for (j=0; j < ni; j++) {
5746 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
5750 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
5751 ar->objs->dsdb_repl_flags,
5752 &nmd.ctr.ctr1.array[j],
5753 &rmd->ctr.ctr1.array[i]);
5755 /* replace the entry */
5756 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
5757 if (ar->seq_num == 0) {
5758 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5759 if (ret != LDB_SUCCESS) {
5760 return replmd_replicated_request_error(ar, ret);
5763 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
5764 switch (nmd.ctr.ctr1.array[j].attid) {
5765 case DRSUAPI_ATTID_ntSecurityDescriptor:
5768 case DRSUAPI_ATTID_isDeleted:
5769 take_remote_isDeleted = true;
5778 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
5779 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
5780 msg->elements[i-removed_attrs].name,
5781 ldb_dn_get_linearized(msg->dn),
5782 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
5785 /* we don't want to apply this change so remove the attribute */
5786 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
5793 if (found) continue;
5795 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
5796 if (ar->seq_num == 0) {
5797 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5798 if (ret != LDB_SUCCESS) {
5799 return replmd_replicated_request_error(ar, ret);
5802 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
5803 switch (nmd.ctr.ctr1.array[ni].attid) {
5804 case DRSUAPI_ATTID_ntSecurityDescriptor:
5807 case DRSUAPI_ATTID_isDeleted:
5808 take_remote_isDeleted = true;
5817 * finally correct the size of the meta_data array
5819 nmd.ctr.ctr1.count = ni;
5821 new_rdn = ldb_dn_get_rdn_val(msg->dn);
5822 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
5825 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
5826 &nmd, ar, now, is_schema_nc);
5827 if (ret != LDB_SUCCESS) {
5828 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5829 return replmd_replicated_request_error(ar, ret);
5833 * sort the new meta data array
5835 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
5836 if (ret != LDB_SUCCESS) {
5837 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5842 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
5845 * This also controls SD propagation below
5847 if (take_remote_isDeleted) {
5848 isDeleted = remote_isDeleted;
5850 isDeleted = local_isDeleted;
5853 ar->isDeleted = isDeleted;
5856 * check if some replicated attributes left, otherwise skip the ldb_modify() call
5858 if (msg->num_elements == 0) {
5859 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
5862 return replmd_replicated_apply_isDeleted(ar);
5865 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
5866 ar->index_current, msg->num_elements);
5872 if (sd_updated && !isDeleted) {
5873 ret = dsdb_module_schedule_sd_propagation(ar->module,
5874 ar->objs->partition_dn,
5876 if (ret != LDB_SUCCESS) {
5877 return ldb_operr(ldb);
5881 /* create the meta data value */
5882 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
5883 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5884 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5885 nt_status = ndr_map_error2ntstatus(ndr_err);
5886 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5890 * when we know that we'll modify the record, add the whenChanged, uSNChanged
5891 * and replPopertyMetaData attributes
5893 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5894 if (ret != LDB_SUCCESS) {
5895 return replmd_replicated_request_error(ar, ret);
5897 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5898 if (ret != LDB_SUCCESS) {
5899 return replmd_replicated_request_error(ar, ret);
5901 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
5902 if (ret != LDB_SUCCESS) {
5903 return replmd_replicated_request_error(ar, ret);
5906 replmd_ldb_message_sort(msg, ar->schema);
5908 /* we want to replace the old values */
5909 for (i=0; i < msg->num_elements; i++) {
5910 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
5911 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5912 if (msg->elements[i].num_values == 0) {
5913 ldb_asprintf_errstring(ldb, __location__
5914 ": objectClass removed on %s, aborting replication\n",
5915 ldb_dn_get_linearized(msg->dn));
5916 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5922 struct GUID_txt_buf guid_txt;
5924 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5925 DEBUG(4, ("Final DRS replication modify message of %s:\n%s\n",
5926 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5931 ret = ldb_build_mod_req(&change_req,
5939 LDB_REQ_SET_LOCATION(change_req);
5940 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5942 /* current partition control needed by "repmd_op_callback" */
5943 ret = ldb_request_add_control(change_req,
5944 DSDB_CONTROL_CURRENT_PARTITION_OID,
5946 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5948 return ldb_next_request(ar->module, change_req);
5951 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
5952 struct ldb_reply *ares)
5954 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5955 struct replmd_replicated_request);
5959 return ldb_module_done(ar->req, NULL, NULL,
5960 LDB_ERR_OPERATIONS_ERROR);
5962 if (ares->error != LDB_SUCCESS &&
5963 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5964 return ldb_module_done(ar->req, ares->controls,
5965 ares->response, ares->error);
5968 switch (ares->type) {
5969 case LDB_REPLY_ENTRY:
5970 ar->search_msg = talloc_steal(ar, ares->message);
5973 case LDB_REPLY_REFERRAL:
5974 /* we ignore referrals */
5977 case LDB_REPLY_DONE:
5979 struct replPropertyMetaData1 *md_remote;
5980 struct replPropertyMetaData1 *md_local;
5982 struct replPropertyMetaDataBlob omd;
5983 const struct ldb_val *omd_value;
5984 struct replPropertyMetaDataBlob *rmd;
5985 struct ldb_message *msg;
5987 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
5988 ar->objs->objects[ar->index_current].last_known_parent = NULL;
5991 * This is the ADD case, find the appropriate parent,
5992 * as this object doesn't exist locally:
5994 if (ar->search_msg == NULL) {
5995 ret = replmd_replicated_apply_search_for_parent(ar);
5996 if (ret != LDB_SUCCESS) {
5997 return ldb_module_done(ar->req, NULL, NULL, ret);
6004 * Otherwise, in the MERGE case, work out if we are
6005 * attempting a rename, and if so find the parent the
6006 * newly renamed object wants to belong under (which
6007 * may not be the parent in it's attached string DN
6009 rmd = ar->objs->objects[ar->index_current].meta_data;
6013 /* find existing meta data */
6014 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6016 enum ndr_err_code ndr_err;
6017 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6018 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6019 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6020 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6021 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6024 if (omd.version != 1) {
6025 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6029 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6031 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6032 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6033 && GUID_all_zero(&ar->local_parent_guid)) {
6034 DEBUG(0, ("Refusing to replicate new version of %s "
6035 "as local object has an all-zero parentGUID attribute, "
6036 "despite not being an NC root\n",
6037 ldb_dn_get_linearized(ar->search_msg->dn)));
6038 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6042 * now we need to check for double renames. We could have a
6043 * local rename pending which our replication partner hasn't
6044 * received yet. We choose which one wins by looking at the
6045 * attribute stamps on the two objects, the newer one wins.
6047 * This also simply applies the correct algorithms for
6048 * determining if a change was made to name at all, or
6049 * if the object has just been renamed under the same
6052 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6053 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6055 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6056 ldb_dn_get_linearized(ar->search_msg->dn)));
6057 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6061 * if there is no name attribute given then we have to assume the
6062 * object we've received has the older name
6064 if (replmd_replPropertyMetaData1_new_should_be_taken(
6065 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6066 md_local, md_remote)) {
6067 struct GUID_txt_buf p_guid_local;
6068 struct GUID_txt_buf p_guid_remote;
6069 msg = ar->objs->objects[ar->index_current].msg;
6071 /* Merge on the existing object, with rename */
6073 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6074 "as incoming object changing to %s under %s\n",
6075 ldb_dn_get_linearized(ar->search_msg->dn),
6076 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6077 ldb_dn_get_linearized(msg->dn),
6078 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6080 ret = replmd_replicated_apply_search_for_parent(ar);
6082 struct GUID_txt_buf p_guid_local;
6083 struct GUID_txt_buf p_guid_remote;
6084 msg = ar->objs->objects[ar->index_current].msg;
6087 * Merge on the existing object, force no
6088 * rename (code below just to explain why in
6092 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6093 ldb_dn_get_linearized(msg->dn)) == 0) {
6094 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6095 GUID_equal(&ar->local_parent_guid,
6096 ar->objs->objects[ar->index_current].parent_guid)
6098 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6099 "despite incoming object changing parent to %s\n",
6100 ldb_dn_get_linearized(ar->search_msg->dn),
6101 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6102 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6106 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6107 " and rejecting older rename to %s under %s\n",
6108 ldb_dn_get_linearized(ar->search_msg->dn),
6109 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6110 ldb_dn_get_linearized(msg->dn),
6111 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6115 * This assignment ensures that the strcmp()
6116 * and GUID_equal() calls in
6117 * replmd_replicated_apply_merge() avoids the
6120 ar->objs->objects[ar->index_current].parent_guid =
6121 &ar->local_parent_guid;
6123 msg->dn = ar->search_msg->dn;
6124 ret = replmd_replicated_apply_merge(ar);
6126 if (ret != LDB_SUCCESS) {
6127 return ldb_module_done(ar->req, NULL, NULL, ret);
6136 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6138 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6140 struct ldb_context *ldb;
6144 struct ldb_request *search_req;
6145 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6146 "parentGUID", "instanceType",
6147 "replPropertyMetaData", "nTSecurityDescriptor",
6148 "isDeleted", NULL };
6149 struct GUID_txt_buf guid_str_buf;
6151 if (ar->index_current >= ar->objs->num_objects) {
6152 /* done with it, go to next stage */
6153 return replmd_replicated_uptodate_vector(ar);
6156 ldb = ldb_module_get_ctx(ar->module);
6157 ar->search_msg = NULL;
6158 ar->isDeleted = false;
6160 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6163 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6164 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6166 ret = ldb_build_search_req(&search_req,
6169 ar->objs->partition_dn,
6175 replmd_replicated_apply_search_callback,
6177 LDB_REQ_SET_LOCATION(search_req);
6179 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
6181 if (ret != LDB_SUCCESS) {
6185 return ldb_next_request(ar->module, search_req);
6189 * This is essentially a wrapper for replmd_replicated_apply_next()
6191 * This is needed to ensure that both codepaths call this handler.
6193 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6195 struct ldb_dn *deleted_objects_dn;
6196 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6197 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
6198 &deleted_objects_dn);
6199 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
6201 * Do a delete here again, so that if there is
6202 * anything local that conflicts with this
6203 * object being deleted, it is removed. This
6204 * includes links. See MS-DRSR 4.1.10.6.9
6207 * If the object is already deleted, and there
6208 * is no more work required, it doesn't do
6212 /* This has been updated to point to the DN we eventually did the modify on */
6214 struct ldb_request *del_req;
6215 struct ldb_result *res;
6217 TALLOC_CTX *tmp_ctx = talloc_new(ar);
6219 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6223 res = talloc_zero(tmp_ctx, struct ldb_result);
6225 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6226 talloc_free(tmp_ctx);
6230 /* Build a delete request, which hopefully will artually turn into nothing */
6231 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
6235 ldb_modify_default_callback,
6237 LDB_REQ_SET_LOCATION(del_req);
6238 if (ret != LDB_SUCCESS) {
6239 talloc_free(tmp_ctx);
6244 * This is the guts of the call, call back
6245 * into our delete code, but setting the
6246 * re_delete flag so we delete anything that
6247 * shouldn't be there on a deleted or recycled
6250 ret = replmd_delete_internals(ar->module, del_req, true);
6251 if (ret == LDB_SUCCESS) {
6252 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
6255 talloc_free(tmp_ctx);
6256 if (ret != LDB_SUCCESS) {
6261 ar->index_current++;
6262 return replmd_replicated_apply_next(ar);
6265 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
6266 struct ldb_reply *ares)
6268 struct ldb_context *ldb;
6269 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6270 struct replmd_replicated_request);
6271 ldb = ldb_module_get_ctx(ar->module);
6274 return ldb_module_done(ar->req, NULL, NULL,
6275 LDB_ERR_OPERATIONS_ERROR);
6277 if (ares->error != LDB_SUCCESS) {
6278 return ldb_module_done(ar->req, ares->controls,
6279 ares->response, ares->error);
6282 if (ares->type != LDB_REPLY_DONE) {
6283 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
6284 return ldb_module_done(ar->req, NULL, NULL,
6285 LDB_ERR_OPERATIONS_ERROR);
6290 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6293 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
6295 struct ldb_context *ldb;
6296 struct ldb_request *change_req;
6297 enum ndr_err_code ndr_err;
6298 struct ldb_message *msg;
6299 struct replUpToDateVectorBlob ouv;
6300 const struct ldb_val *ouv_value;
6301 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
6302 struct replUpToDateVectorBlob nuv;
6303 struct ldb_val nuv_value;
6304 struct ldb_message_element *nuv_el = NULL;
6305 struct ldb_message_element *orf_el = NULL;
6306 struct repsFromToBlob nrf;
6307 struct ldb_val *nrf_value = NULL;
6308 struct ldb_message_element *nrf_el = NULL;
6312 time_t t = time(NULL);
6315 uint32_t instanceType;
6317 ldb = ldb_module_get_ctx(ar->module);
6318 ruv = ar->objs->uptodateness_vector;
6324 unix_to_nt_time(&now, t);
6326 if (ar->search_msg == NULL) {
6327 /* this happens for a REPL_OBJ call where we are
6328 creating the target object by replicating it. The
6329 subdomain join code does this for the partition DN
6331 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
6332 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6335 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
6336 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
6337 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
6338 ldb_dn_get_linearized(ar->search_msg->dn)));
6339 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6343 * first create the new replUpToDateVector
6345 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
6347 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
6348 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
6349 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6350 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6351 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6354 if (ouv.version != 2) {
6355 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6360 * the new uptodateness vector will at least
6361 * contain 1 entry, one for the source_dsa
6363 * plus optional values from our old vector and the one from the source_dsa
6365 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
6366 if (ruv) nuv.ctr.ctr2.count += ruv->count;
6367 nuv.ctr.ctr2.cursors = talloc_array(ar,
6368 struct drsuapi_DsReplicaCursor2,
6369 nuv.ctr.ctr2.count);
6370 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6372 /* first copy the old vector */
6373 for (i=0; i < ouv.ctr.ctr2.count; i++) {
6374 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
6378 /* merge in the source_dsa vector is available */
6379 for (i=0; (ruv && i < ruv->count); i++) {
6382 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6383 &ar->our_invocation_id)) {
6387 for (j=0; j < ni; j++) {
6388 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6389 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
6395 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
6396 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
6401 if (found) continue;
6403 /* if it's not there yet, add it */
6404 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
6409 * finally correct the size of the cursors array
6411 nuv.ctr.ctr2.count = ni;
6416 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
6419 * create the change ldb_message
6421 msg = ldb_msg_new(ar);
6422 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6423 msg->dn = ar->search_msg->dn;
6425 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
6426 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
6427 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6428 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6429 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6431 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
6432 if (ret != LDB_SUCCESS) {
6433 return replmd_replicated_request_error(ar, ret);
6435 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
6438 * now create the new repsFrom value from the given repsFromTo1 structure
6442 nrf.ctr.ctr1 = *ar->objs->source_dsa;
6443 nrf.ctr.ctr1.last_attempt = now;
6444 nrf.ctr.ctr1.last_success = now;
6445 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
6448 * first see if we already have a repsFrom value for the current source dsa
6449 * if so we'll later replace this value
6451 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
6453 for (i=0; i < orf_el->num_values; i++) {
6454 struct repsFromToBlob *trf;
6456 trf = talloc(ar, struct repsFromToBlob);
6457 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6459 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
6460 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
6461 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6462 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6463 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6466 if (trf->version != 1) {
6467 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6471 * we compare the source dsa objectGUID not the invocation_id
6472 * because we want only one repsFrom value per source dsa
6473 * and when the invocation_id of the source dsa has changed we don't need
6474 * the old repsFrom with the old invocation_id
6476 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
6477 &ar->objs->source_dsa->source_dsa_obj_guid)) {
6483 nrf_value = &orf_el->values[i];
6488 * copy over all old values to the new ldb_message
6490 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
6491 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6496 * if we haven't found an old repsFrom value for the current source dsa
6497 * we'll add a new value
6500 struct ldb_val zero_value;
6501 ZERO_STRUCT(zero_value);
6502 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
6503 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6505 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
6508 /* we now fill the value which is already attached to ldb_message */
6509 ndr_err = ndr_push_struct_blob(nrf_value, msg,
6511 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
6512 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6513 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6514 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6518 * the ldb_message_element for the attribute, has all the old values and the new one
6519 * so we'll replace the whole attribute with all values
6521 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
6523 if (CHECK_DEBUGLVL(4)) {
6524 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
6525 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
6529 /* prepare the ldb_modify() request */
6530 ret = ldb_build_mod_req(&change_req,
6536 replmd_replicated_uptodate_modify_callback,
6538 LDB_REQ_SET_LOCATION(change_req);
6539 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6541 return ldb_next_request(ar->module, change_req);
6544 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
6545 struct ldb_reply *ares)
6547 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6548 struct replmd_replicated_request);
6552 return ldb_module_done(ar->req, NULL, NULL,
6553 LDB_ERR_OPERATIONS_ERROR);
6555 if (ares->error != LDB_SUCCESS &&
6556 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6557 return ldb_module_done(ar->req, ares->controls,
6558 ares->response, ares->error);
6561 switch (ares->type) {
6562 case LDB_REPLY_ENTRY:
6563 ar->search_msg = talloc_steal(ar, ares->message);
6566 case LDB_REPLY_REFERRAL:
6567 /* we ignore referrals */
6570 case LDB_REPLY_DONE:
6571 ret = replmd_replicated_uptodate_modify(ar);
6572 if (ret != LDB_SUCCESS) {
6573 return ldb_module_done(ar->req, NULL, NULL, ret);
6582 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
6584 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
6585 struct replmd_private *replmd_private =
6586 talloc_get_type_abort(ldb_module_get_private(ar->module),
6587 struct replmd_private);
6589 static const char *attrs[] = {
6590 "replUpToDateVector",
6595 struct ldb_request *search_req;
6597 ar->search_msg = NULL;
6600 * Let the caller know that we did an originating updates
6602 ar->objs->originating_updates = replmd_private->originating_updates;
6604 ret = ldb_build_search_req(&search_req,
6607 ar->objs->partition_dn,
6613 replmd_replicated_uptodate_search_callback,
6615 LDB_REQ_SET_LOCATION(search_req);
6616 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6618 return ldb_next_request(ar->module, search_req);
6623 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
6625 struct ldb_context *ldb;
6626 struct dsdb_extended_replicated_objects *objs;
6627 struct replmd_replicated_request *ar;
6628 struct ldb_control **ctrls;
6631 struct replmd_private *replmd_private =
6632 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6634 ldb = ldb_module_get_ctx(module);
6636 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
6638 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
6640 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
6641 return LDB_ERR_PROTOCOL_ERROR;
6644 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
6645 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
6646 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
6647 return LDB_ERR_PROTOCOL_ERROR;
6650 ar = replmd_ctx_init(module, req);
6652 return LDB_ERR_OPERATIONS_ERROR;
6654 /* Set the flags to have the replmd_op_callback run over the full set of objects */
6655 ar->apply_mode = true;
6657 ar->schema = dsdb_get_schema(ldb, ar);
6659 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
6661 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
6662 return LDB_ERR_CONSTRAINT_VIOLATION;
6665 ctrls = req->controls;
6667 if (req->controls) {
6668 req->controls = talloc_memdup(ar, req->controls,
6669 talloc_get_size(req->controls));
6670 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6673 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
6674 if (ret != LDB_SUCCESS) {
6678 /* If this change contained linked attributes in the body
6679 * (rather than in the links section) we need to update
6680 * backlinks in linked_attributes */
6681 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
6682 if (ret != LDB_SUCCESS) {
6686 ar->controls = req->controls;
6687 req->controls = ctrls;
6689 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
6691 /* save away the linked attributes for the end of the
6693 for (i=0; i<ar->objs->linked_attributes_count; i++) {
6694 struct la_entry *la_entry;
6696 if (replmd_private->la_ctx == NULL) {
6697 replmd_private->la_ctx = talloc_new(replmd_private);
6699 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6700 if (la_entry == NULL) {
6702 return LDB_ERR_OPERATIONS_ERROR;
6704 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
6705 if (la_entry->la == NULL) {
6706 talloc_free(la_entry);
6708 return LDB_ERR_OPERATIONS_ERROR;
6710 *la_entry->la = ar->objs->linked_attributes[i];
6712 /* we need to steal the non-scalars so they stay
6713 around until the end of the transaction */
6714 talloc_steal(la_entry->la, la_entry->la->identifier);
6715 talloc_steal(la_entry->la, la_entry->la->value.blob);
6717 DLIST_ADD(replmd_private->la_list, la_entry);
6720 return replmd_replicated_apply_next(ar);
6724 process one linked attribute structure
6726 static int replmd_process_linked_attribute(struct ldb_module *module,
6727 struct replmd_private *replmd_private,
6728 struct la_entry *la_entry,
6729 struct ldb_request *parent)
6731 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
6732 struct ldb_context *ldb = ldb_module_get_ctx(module);
6733 struct ldb_message *msg;
6734 struct ldb_message *target_msg = NULL;
6735 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
6736 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
6738 const struct dsdb_attribute *attr;
6739 struct dsdb_dn *dsdb_dn;
6740 uint64_t seq_num = 0;
6741 struct ldb_message_element *old_el;
6743 time_t t = time(NULL);
6744 struct ldb_result *res;
6745 struct ldb_result *target_res;
6746 const char *attrs[4];
6747 const char *attrs2[] = { "isDeleted", "isRecycled", NULL };
6748 struct parsed_dn *pdn_list, *pdn, *next;
6749 struct GUID guid = GUID_zero();
6751 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
6753 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
6754 enum deletion_state target_deletion_state = OBJECT_NOT_DELETED;
6757 linked_attributes[0]:
6758 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
6760 identifier: struct drsuapi_DsReplicaObjectIdentifier
6761 __ndr_size : 0x0000003a (58)
6762 __ndr_size_sid : 0x00000000 (0)
6763 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
6765 __ndr_size_dn : 0x00000000 (0)
6767 attid : DRSUAPI_ATTID_member (0x1F)
6768 value: struct drsuapi_DsAttributeValue
6769 __ndr_size : 0x0000007e (126)
6771 blob : DATA_BLOB length=126
6772 flags : 0x00000001 (1)
6773 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
6774 originating_add_time : Wed Sep 2 22:20:01 2009 EST
6775 meta_data: struct drsuapi_DsReplicaMetaData
6776 version : 0x00000015 (21)
6777 originating_change_time : Wed Sep 2 23:39:07 2009 EST
6778 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
6779 originating_usn : 0x000000000001e19c (123292)
6781 (for cases where the link is to a normal DN)
6782 &target: struct drsuapi_DsReplicaObjectIdentifier3
6783 __ndr_size : 0x0000007e (126)
6784 __ndr_size_sid : 0x0000001c (28)
6785 guid : 7639e594-db75-4086-b0d4-67890ae46031
6786 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
6787 __ndr_size_dn : 0x00000022 (34)
6788 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
6791 /* find the attribute being modified */
6792 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
6794 struct GUID_txt_buf guid_str;
6795 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
6797 GUID_buf_string(&la->identifier->guid,
6799 talloc_free(tmp_ctx);
6800 return LDB_ERR_OPERATIONS_ERROR;
6803 attrs[0] = attr->lDAPDisplayName;
6804 attrs[1] = "isDeleted";
6805 attrs[2] = "isRecycled";
6808 /* get the existing message from the db for the object with
6809 this GUID, returning attribute being modified. We will then
6810 use this msg as the basis for a modify call */
6811 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
6812 DSDB_FLAG_NEXT_MODULE |
6813 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6814 DSDB_SEARCH_SHOW_RECYCLED |
6815 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
6816 DSDB_SEARCH_REVEAL_INTERNALS,
6818 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
6819 if (ret != LDB_SUCCESS) {
6820 talloc_free(tmp_ctx);
6823 if (res->count != 1) {
6824 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
6825 GUID_string(tmp_ctx, &la->identifier->guid));
6826 talloc_free(tmp_ctx);
6827 return LDB_ERR_NO_SUCH_OBJECT;
6832 * Check for deleted objects per MS-DRSR 4.1.10.6.13
6833 * ProcessLinkValue, because link updates are not applied to
6834 * recycled and tombstone objects. We don't have to delete
6835 * any existing link, that should have happened when the
6836 * object deletion was replicated or initiated.
6839 replmd_deletion_state(module, msg, &deletion_state, NULL);
6841 if (deletion_state >= OBJECT_RECYCLED) {
6842 talloc_free(tmp_ctx);
6846 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
6847 if (old_el == NULL) {
6848 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
6849 if (ret != LDB_SUCCESS) {
6850 ldb_module_oom(module);
6851 talloc_free(tmp_ctx);
6852 return LDB_ERR_OPERATIONS_ERROR;
6855 old_el->flags = LDB_FLAG_MOD_REPLACE;
6858 /* parse the existing links */
6859 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx, old_el, &pdn_list,
6860 attr->syntax->ldap_oid, parent);
6862 if (ret != LDB_SUCCESS) {
6863 talloc_free(tmp_ctx);
6867 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
6868 if (!W_ERROR_IS_OK(status)) {
6869 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
6870 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
6871 talloc_free(tmp_ctx);
6872 return LDB_ERR_OPERATIONS_ERROR;
6875 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
6876 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
6878 * This strange behaviour (allowing a NULL/missing
6879 * GUID) originally comes from:
6881 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
6882 * Author: Andrew Tridgell <tridge@samba.org>
6883 * Date: Mon Dec 21 21:21:55 2009 +1100
6885 * s4-drs: cope better with NULL GUIDS from DRS
6887 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
6888 * need to match by DN if possible when seeing if we should update an
6891 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
6894 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
6895 dsdb_dn->dn, attrs2,
6896 DSDB_FLAG_NEXT_MODULE |
6897 DSDB_SEARCH_SHOW_RECYCLED |
6898 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6899 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6901 } else if (!NT_STATUS_IS_OK(ntstatus)) {
6902 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
6904 ldb_dn_get_linearized(dsdb_dn->dn),
6905 ldb_dn_get_linearized(msg->dn));
6906 talloc_free(tmp_ctx);
6907 return LDB_ERR_OPERATIONS_ERROR;
6909 ret = dsdb_module_search(module, tmp_ctx, &target_res,
6910 NULL, LDB_SCOPE_SUBTREE,
6912 DSDB_FLAG_NEXT_MODULE |
6913 DSDB_SEARCH_SHOW_RECYCLED |
6914 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6915 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6918 GUID_string(tmp_ctx, &guid));
6921 if (ret != LDB_SUCCESS) {
6922 ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to re-resolve GUID %s: %s\n",
6923 GUID_string(tmp_ctx, &guid),
6924 ldb_errstring(ldb_module_get_ctx(module)));
6925 talloc_free(tmp_ctx);
6929 if (target_res->count == 0) {
6930 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
6931 GUID_string(tmp_ctx, &guid),
6932 ldb_dn_get_linearized(dsdb_dn->dn)));
6933 } else if (target_res->count != 1) {
6934 ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching objectGUID %s\n",
6935 GUID_string(tmp_ctx, &guid));
6936 talloc_free(tmp_ctx);
6937 return LDB_ERR_OPERATIONS_ERROR;
6939 target_msg = target_res->msgs[0];
6940 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
6944 * Check for deleted objects per MS-DRSR 4.1.10.6.13
6945 * ProcessLinkValue, because link updates are not applied to
6946 * recycled and tombstone objects. We don't have to delete
6947 * any existing link, that should have happened when the
6948 * object deletion was replicated or initiated.
6950 replmd_deletion_state(module, target_msg,
6951 &target_deletion_state, NULL);
6953 if (target_deletion_state >= OBJECT_RECYCLED) {
6954 talloc_free(tmp_ctx);
6958 /* see if this link already exists */
6959 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
6963 attr->syntax->ldap_oid);
6964 if (ret != LDB_SUCCESS) {
6965 talloc_free(tmp_ctx);
6971 /* see if this update is newer than what we have already */
6972 struct GUID invocation_id = GUID_zero();
6973 uint32_t version = 0;
6974 uint32_t originating_usn = 0;
6975 NTTIME change_time = 0;
6976 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
6978 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
6979 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
6980 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
6981 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
6983 if (!replmd_update_is_newer(&invocation_id,
6984 &la->meta_data.originating_invocation_id,
6986 la->meta_data.version,
6988 la->meta_data.originating_change_time)) {
6989 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
6990 old_el->name, ldb_dn_get_linearized(msg->dn),
6991 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
6992 talloc_free(tmp_ctx);
6996 /* get a seq_num for this change */
6997 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
6998 if (ret != LDB_SUCCESS) {
6999 talloc_free(tmp_ctx);
7003 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
7004 /* remove the existing backlink */
7005 ret = replmd_add_backlink(module, replmd_private,
7010 if (ret != LDB_SUCCESS) {
7011 talloc_free(tmp_ctx);
7016 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
7017 &la->meta_data.originating_invocation_id,
7018 la->meta_data.originating_usn, seq_num,
7019 la->meta_data.originating_change_time,
7020 la->meta_data.version,
7022 if (ret != LDB_SUCCESS) {
7023 talloc_free(tmp_ctx);
7028 /* add the new backlink */
7029 ret = replmd_add_backlink(module, replmd_private,
7034 if (ret != LDB_SUCCESS) {
7035 talloc_free(tmp_ctx);
7041 /* get a seq_num for this change */
7042 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
7043 if (ret != LDB_SUCCESS) {
7044 talloc_free(tmp_ctx);
7048 * We know where the new one needs to be, from the *next
7049 * pointer into pdn_list.
7052 offset = old_el->num_values;
7054 if (next->dsdb_dn == NULL) {
7055 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
7056 attr->syntax->ldap_oid);
7057 if (ret != LDB_SUCCESS) {
7061 offset = next - pdn_list;
7062 if (offset > old_el->num_values) {
7063 talloc_free(tmp_ctx);
7064 return LDB_ERR_OPERATIONS_ERROR;
7068 old_el->values = talloc_realloc(msg->elements, old_el->values,
7069 struct ldb_val, old_el->num_values+1);
7070 if (!old_el->values) {
7071 ldb_module_oom(module);
7072 return LDB_ERR_OPERATIONS_ERROR;
7075 if (offset != old_el->num_values) {
7076 memmove(&old_el->values[offset + 1], &old_el->values[offset],
7077 (old_el->num_values - offset) * sizeof(old_el->values[0]));
7080 old_el->num_values++;
7082 ret = replmd_build_la_val(tmp_ctx, &old_el->values[offset], dsdb_dn,
7083 &la->meta_data.originating_invocation_id,
7084 la->meta_data.originating_usn, seq_num,
7085 la->meta_data.originating_change_time,
7086 la->meta_data.version,
7088 if (ret != LDB_SUCCESS) {
7089 talloc_free(tmp_ctx);
7094 ret = replmd_add_backlink(module, replmd_private,
7099 if (ret != LDB_SUCCESS) {
7100 talloc_free(tmp_ctx);
7106 /* we only change whenChanged and uSNChanged if the seq_num
7108 ret = add_time_element(msg, "whenChanged", t);
7109 if (ret != LDB_SUCCESS) {
7110 talloc_free(tmp_ctx);
7115 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
7116 if (ret != LDB_SUCCESS) {
7117 talloc_free(tmp_ctx);
7122 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7123 if (old_el == NULL) {
7124 talloc_free(tmp_ctx);
7125 return ldb_operr(ldb);
7128 ret = dsdb_check_single_valued_link(attr, old_el);
7129 if (ret != LDB_SUCCESS) {
7130 talloc_free(tmp_ctx);
7134 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
7136 ret = linked_attr_modify(module, msg, parent);
7137 if (ret != LDB_SUCCESS) {
7138 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
7140 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
7141 talloc_free(tmp_ctx);
7145 talloc_free(tmp_ctx);
7150 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
7152 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
7153 return replmd_extended_replicated_objects(module, req);
7156 return ldb_next_request(module, req);
7161 we hook into the transaction operations to allow us to
7162 perform the linked attribute updates at the end of the whole
7163 transaction. This allows a forward linked attribute to be created
7164 before the object is created. During a vampire, w2k8 sends us linked
7165 attributes before the objects they are part of.
7167 static int replmd_start_transaction(struct ldb_module *module)
7169 /* create our private structure for this transaction */
7170 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
7171 struct replmd_private);
7172 replmd_txn_cleanup(replmd_private);
7174 /* free any leftover mod_usn records from cancelled
7176 while (replmd_private->ncs) {
7177 struct nc_entry *e = replmd_private->ncs;
7178 DLIST_REMOVE(replmd_private->ncs, e);
7182 replmd_private->originating_updates = false;
7184 return ldb_next_start_trans(module);
7188 on prepare commit we loop over our queued la_context structures and
7191 static int replmd_prepare_commit(struct ldb_module *module)
7193 struct replmd_private *replmd_private =
7194 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
7195 struct la_entry *la, *prev;
7199 * Walk the list of linked attributes from DRS replication.
7201 * We walk backwards, to do the first entry first, as we
7202 * added the entries with DLIST_ADD() which puts them at the
7205 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
7206 prev = DLIST_PREV(la);
7207 DLIST_REMOVE(replmd_private->la_list, la);
7208 ret = replmd_process_linked_attribute(module, replmd_private,
7210 if (ret != LDB_SUCCESS) {
7211 replmd_txn_cleanup(replmd_private);
7216 replmd_txn_cleanup(replmd_private);
7218 /* possibly change @REPLCHANGED */
7219 ret = replmd_notify_store(module, NULL);
7220 if (ret != LDB_SUCCESS) {
7224 return ldb_next_prepare_commit(module);
7227 static int replmd_del_transaction(struct ldb_module *module)
7229 struct replmd_private *replmd_private =
7230 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
7231 replmd_txn_cleanup(replmd_private);
7233 return ldb_next_del_trans(module);
7237 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
7238 .name = "repl_meta_data",
7239 .init_context = replmd_init,
7241 .modify = replmd_modify,
7242 .rename = replmd_rename,
7243 .del = replmd_delete,
7244 .extended = replmd_extended,
7245 .start_transaction = replmd_start_transaction,
7246 .prepare_commit = replmd_prepare_commit,
7247 .del_transaction = replmd_del_transaction,
7250 int ldb_repl_meta_data_module_init(const char *version)
7252 LDB_MODULE_CHECK_VERSION(version);
7253 return ldb_register_module(&ldb_repl_meta_data_module_ops);