dsdb: Improve missing objectClass handling
[Samba.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blob4e5d8f0597cf61fc3ba0c27c51f3cfbc415249bc
1 /*
2 ldb database library
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/>.
25 * Name: ldb
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
34 * Author: Simo Sorce
35 * Author: Stefan Metzmacher
38 #include "includes.h"
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 {
60 TALLOC_CTX *la_ctx;
61 struct la_entry *la_list;
62 TALLOC_CTX *bl_ctx;
63 struct la_backlink *la_backlinks;
64 struct nc_entry {
65 struct nc_entry *prev, *next;
66 struct ldb_dn *dn;
67 uint64_t mod_usn;
68 uint64_t mod_usn_urgent;
69 } *ncs;
72 struct la_entry {
73 struct la_entry *next, *prev;
74 struct drsuapi_DsReplicaLinkedAttribute *la;
77 struct replmd_replicated_request {
78 struct ldb_module *module;
79 struct ldb_request *req;
81 const struct dsdb_schema *schema;
83 /* the controls we pass down */
84 struct ldb_control **controls;
86 /* details for the mode where we apply a bunch of inbound replication meessages */
87 bool apply_mode;
88 uint32_t index_current;
89 struct dsdb_extended_replicated_objects *objs;
91 struct ldb_message *search_msg;
93 uint64_t seq_num;
94 bool is_urgent;
96 bool isDeleted;
99 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
100 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
102 enum urgent_situation {
103 REPL_URGENT_ON_CREATE = 1,
104 REPL_URGENT_ON_UPDATE = 2,
105 REPL_URGENT_ON_DELETE = 4
108 enum deletion_state {
109 OBJECT_NOT_DELETED=1,
110 OBJECT_DELETED=2,
111 OBJECT_RECYCLED=3,
112 OBJECT_TOMBSTONE=4,
113 OBJECT_REMOVED=5
116 static void replmd_deletion_state(struct ldb_module *module,
117 const struct ldb_message *msg,
118 enum deletion_state *current_state,
119 enum deletion_state *next_state)
121 int ret;
122 bool enabled = false;
124 if (msg == NULL) {
125 *current_state = OBJECT_REMOVED;
126 if (next_state != NULL) {
127 *next_state = OBJECT_REMOVED;
129 return;
132 ret = dsdb_recyclebin_enabled(module, &enabled);
133 if (ret != LDB_SUCCESS) {
134 enabled = false;
137 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
138 if (!enabled) {
139 *current_state = OBJECT_TOMBSTONE;
140 if (next_state != NULL) {
141 *next_state = OBJECT_REMOVED;
143 return;
146 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
147 *current_state = OBJECT_RECYCLED;
148 if (next_state != NULL) {
149 *next_state = OBJECT_REMOVED;
151 return;
154 *current_state = OBJECT_DELETED;
155 if (next_state != NULL) {
156 *next_state = OBJECT_RECYCLED;
158 return;
161 *current_state = OBJECT_NOT_DELETED;
162 if (next_state == NULL) {
163 return;
166 if (enabled) {
167 *next_state = OBJECT_DELETED;
168 } else {
169 *next_state = OBJECT_TOMBSTONE;
173 static const struct {
174 const char *update_name;
175 enum urgent_situation repl_situation;
176 } urgent_objects[] = {
177 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
178 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
179 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
180 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
181 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
182 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
183 {NULL, 0}
186 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
187 static const char *urgent_attrs[] = {
188 "lockoutTime",
189 "pwdLastSet",
190 "userAccountControl",
191 NULL
195 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
196 enum urgent_situation situation)
198 unsigned int i, j;
199 for (i=0; urgent_objects[i].update_name; i++) {
201 if ((situation & urgent_objects[i].repl_situation) == 0) {
202 continue;
205 for (j=0; j<objectclass_el->num_values; j++) {
206 const struct ldb_val *v = &objectclass_el->values[j];
207 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
208 return true;
212 return false;
215 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
217 if (ldb_attr_in_list(urgent_attrs, el->name)) {
218 return true;
220 return false;
224 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
227 initialise the module
228 allocate the private structure and build the list
229 of partition DNs for use by replmd_notify()
231 static int replmd_init(struct ldb_module *module)
233 struct replmd_private *replmd_private;
234 struct ldb_context *ldb = ldb_module_get_ctx(module);
236 replmd_private = talloc_zero(module, struct replmd_private);
237 if (replmd_private == NULL) {
238 ldb_oom(ldb);
239 return LDB_ERR_OPERATIONS_ERROR;
241 ldb_module_set_private(module, replmd_private);
243 return ldb_next_init(module);
247 cleanup our per-transaction contexts
249 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
251 talloc_free(replmd_private->la_ctx);
252 replmd_private->la_list = NULL;
253 replmd_private->la_ctx = NULL;
255 talloc_free(replmd_private->bl_ctx);
256 replmd_private->la_backlinks = NULL;
257 replmd_private->bl_ctx = NULL;
261 struct la_backlink {
262 struct la_backlink *next, *prev;
263 const char *attr_name;
264 struct GUID forward_guid, target_guid;
265 bool active;
269 process a backlinks we accumulated during a transaction, adding and
270 deleting the backlinks from the target objects
272 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
274 struct ldb_dn *target_dn, *source_dn;
275 int ret;
276 struct ldb_context *ldb = ldb_module_get_ctx(module);
277 struct ldb_message *msg;
278 TALLOC_CTX *tmp_ctx = talloc_new(bl);
279 char *dn_string;
282 - find DN of target
283 - find DN of source
284 - construct ldb_message
285 - either an add or a delete
287 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn, parent);
288 if (ret != LDB_SUCCESS) {
289 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
290 GUID_string(bl, &bl->target_guid)));
291 return LDB_SUCCESS;
294 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn, parent);
295 if (ret != LDB_SUCCESS) {
296 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
297 GUID_string(bl, &bl->forward_guid));
298 talloc_free(tmp_ctx);
299 return ret;
302 msg = ldb_msg_new(tmp_ctx);
303 if (msg == NULL) {
304 ldb_module_oom(module);
305 talloc_free(tmp_ctx);
306 return LDB_ERR_OPERATIONS_ERROR;
309 /* construct a ldb_message for adding/deleting the backlink */
310 msg->dn = target_dn;
311 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
312 if (!dn_string) {
313 ldb_module_oom(module);
314 talloc_free(tmp_ctx);
315 return LDB_ERR_OPERATIONS_ERROR;
317 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
318 if (ret != LDB_SUCCESS) {
319 talloc_free(tmp_ctx);
320 return ret;
322 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
324 /* a backlink should never be single valued. Unfortunately the
325 exchange schema has a attribute
326 msExchBridgeheadedLocalConnectorsDNBL which is single
327 valued and a backlink. We need to cope with that by
328 ignoring the single value flag */
329 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
331 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
332 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
333 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
334 cope with possible corruption where the backlink has
335 already been removed */
336 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
337 ldb_dn_get_linearized(target_dn),
338 ldb_dn_get_linearized(source_dn),
339 ldb_errstring(ldb)));
340 ret = LDB_SUCCESS;
341 } else if (ret != LDB_SUCCESS) {
342 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
343 bl->active?"add":"remove",
344 ldb_dn_get_linearized(source_dn),
345 ldb_dn_get_linearized(target_dn),
346 ldb_errstring(ldb));
347 talloc_free(tmp_ctx);
348 return ret;
350 talloc_free(tmp_ctx);
351 return ret;
355 add a backlink to the list of backlinks to add/delete in the prepare
356 commit
358 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
359 struct GUID *forward_guid, struct GUID *target_guid,
360 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
362 const struct dsdb_attribute *target_attr;
363 struct la_backlink *bl;
364 struct replmd_private *replmd_private =
365 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
367 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
368 if (!target_attr) {
370 * windows 2003 has a broken schema where the
371 * definition of msDS-IsDomainFor is missing (which is
372 * supposed to be the backlink of the
373 * msDS-HasDomainNCs attribute
375 return LDB_SUCCESS;
378 /* see if its already in the list */
379 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
380 if (GUID_equal(forward_guid, &bl->forward_guid) &&
381 GUID_equal(target_guid, &bl->target_guid) &&
382 (target_attr->lDAPDisplayName == bl->attr_name ||
383 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
384 break;
388 if (bl) {
389 /* we found an existing one */
390 if (bl->active == active) {
391 return LDB_SUCCESS;
393 DLIST_REMOVE(replmd_private->la_backlinks, bl);
394 talloc_free(bl);
395 return LDB_SUCCESS;
398 if (replmd_private->bl_ctx == NULL) {
399 replmd_private->bl_ctx = talloc_new(replmd_private);
400 if (replmd_private->bl_ctx == NULL) {
401 ldb_module_oom(module);
402 return LDB_ERR_OPERATIONS_ERROR;
406 /* its a new one */
407 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
408 if (bl == NULL) {
409 ldb_module_oom(module);
410 return LDB_ERR_OPERATIONS_ERROR;
413 /* Ensure the schema does not go away before the bl->attr_name is used */
414 if (!talloc_reference(bl, schema)) {
415 talloc_free(bl);
416 ldb_module_oom(module);
417 return LDB_ERR_OPERATIONS_ERROR;
420 bl->attr_name = target_attr->lDAPDisplayName;
421 bl->forward_guid = *forward_guid;
422 bl->target_guid = *target_guid;
423 bl->active = active;
425 /* the caller may ask for this backlink to be processed
426 immediately */
427 if (immediate) {
428 int ret = replmd_process_backlink(module, bl, NULL);
429 talloc_free(bl);
430 return ret;
433 DLIST_ADD(replmd_private->la_backlinks, bl);
435 return LDB_SUCCESS;
440 * Callback for most write operations in this module:
442 * notify the repl task that a object has changed. The notifies are
443 * gathered up in the replmd_private structure then written to the
444 * @REPLCHANGED object in each partition during the prepare_commit
446 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
448 int ret;
449 struct replmd_replicated_request *ac =
450 talloc_get_type_abort(req->context, struct replmd_replicated_request);
451 struct replmd_private *replmd_private =
452 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
453 struct nc_entry *modified_partition;
454 struct ldb_control *partition_ctrl;
455 const struct dsdb_control_current_partition *partition;
457 struct ldb_control **controls;
459 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
461 controls = ares->controls;
462 if (ldb_request_get_control(ac->req,
463 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
465 * Remove the current partition control from what we pass up
466 * the chain if it hasn't been requested manually.
468 controls = ldb_controls_except_specified(ares->controls, ares,
469 partition_ctrl);
472 if (ares->error != LDB_SUCCESS) {
473 DEBUG(5,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
474 return ldb_module_done(ac->req, controls,
475 ares->response, ares->error);
478 if (ares->type != LDB_REPLY_DONE) {
479 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
480 return ldb_module_done(ac->req, NULL,
481 NULL, LDB_ERR_OPERATIONS_ERROR);
484 if (!partition_ctrl) {
485 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
486 return ldb_module_done(ac->req, NULL,
487 NULL, LDB_ERR_OPERATIONS_ERROR);
490 partition = talloc_get_type_abort(partition_ctrl->data,
491 struct dsdb_control_current_partition);
493 if (ac->seq_num > 0) {
494 for (modified_partition = replmd_private->ncs; modified_partition;
495 modified_partition = modified_partition->next) {
496 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
497 break;
501 if (modified_partition == NULL) {
502 modified_partition = talloc_zero(replmd_private, struct nc_entry);
503 if (!modified_partition) {
504 ldb_oom(ldb_module_get_ctx(ac->module));
505 return ldb_module_done(ac->req, NULL,
506 NULL, LDB_ERR_OPERATIONS_ERROR);
508 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
509 if (!modified_partition->dn) {
510 ldb_oom(ldb_module_get_ctx(ac->module));
511 return ldb_module_done(ac->req, NULL,
512 NULL, LDB_ERR_OPERATIONS_ERROR);
514 DLIST_ADD(replmd_private->ncs, modified_partition);
517 if (ac->seq_num > modified_partition->mod_usn) {
518 modified_partition->mod_usn = ac->seq_num;
519 if (ac->is_urgent) {
520 modified_partition->mod_usn_urgent = ac->seq_num;
525 if (ac->apply_mode) {
526 ret = replmd_replicated_apply_isDeleted(ac);
527 if (ret != LDB_SUCCESS) {
528 return ldb_module_done(ac->req, NULL, NULL, ret);
530 return ret;
531 } else {
532 /* free the partition control container here, for the
533 * common path. Other cases will have it cleaned up
534 * eventually with the ares */
535 talloc_free(partition_ctrl);
536 return ldb_module_done(ac->req, controls,
537 ares->response, LDB_SUCCESS);
543 * update a @REPLCHANGED record in each partition if there have been
544 * any writes of replicated data in the partition
546 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
548 struct replmd_private *replmd_private =
549 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
551 while (replmd_private->ncs) {
552 int ret;
553 struct nc_entry *modified_partition = replmd_private->ncs;
555 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
556 modified_partition->mod_usn,
557 modified_partition->mod_usn_urgent, parent);
558 if (ret != LDB_SUCCESS) {
559 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
560 ldb_dn_get_linearized(modified_partition->dn)));
561 return ret;
563 DLIST_REMOVE(replmd_private->ncs, modified_partition);
564 talloc_free(modified_partition);
567 return LDB_SUCCESS;
572 created a replmd_replicated_request context
574 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
575 struct ldb_request *req)
577 struct ldb_context *ldb;
578 struct replmd_replicated_request *ac;
580 ldb = ldb_module_get_ctx(module);
582 ac = talloc_zero(req, struct replmd_replicated_request);
583 if (ac == NULL) {
584 ldb_oom(ldb);
585 return NULL;
588 ac->module = module;
589 ac->req = req;
591 ac->schema = dsdb_get_schema(ldb, ac);
592 if (!ac->schema) {
593 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
594 "replmd_modify: no dsdb_schema loaded");
595 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
596 return NULL;
599 return ac;
603 add a time element to a record
605 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
607 struct ldb_message_element *el;
608 char *s;
609 int ret;
611 if (ldb_msg_find_element(msg, attr) != NULL) {
612 return LDB_SUCCESS;
615 s = ldb_timestring(msg, t);
616 if (s == NULL) {
617 return LDB_ERR_OPERATIONS_ERROR;
620 ret = ldb_msg_add_string(msg, attr, s);
621 if (ret != LDB_SUCCESS) {
622 return ret;
625 el = ldb_msg_find_element(msg, attr);
626 /* always set as replace. This works because on add ops, the flag
627 is ignored */
628 el->flags = LDB_FLAG_MOD_REPLACE;
630 return LDB_SUCCESS;
634 add a uint64_t element to a record
636 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
637 const char *attr, uint64_t v)
639 struct ldb_message_element *el;
640 int ret;
642 if (ldb_msg_find_element(msg, attr) != NULL) {
643 return LDB_SUCCESS;
646 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
647 if (ret != LDB_SUCCESS) {
648 return ret;
651 el = ldb_msg_find_element(msg, attr);
652 /* always set as replace. This works because on add ops, the flag
653 is ignored */
654 el->flags = LDB_FLAG_MOD_REPLACE;
656 return LDB_SUCCESS;
659 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
660 const struct replPropertyMetaData1 *m2,
661 const uint32_t *rdn_attid)
664 * This assignment seems inoccous, but it is critical for the
665 * system, as we need to do the comparisons as a unsigned
666 * quantity, not signed (enums are signed integers)
668 uint32_t attid_1 = m1->attid;
669 uint32_t attid_2 = m2->attid;
671 if (attid_1 == attid_2) {
672 return 0;
676 * the rdn attribute should be at the end!
677 * so we need to return a value greater than zero
678 * which means m1 is greater than m2
680 if (attid_1 == *rdn_attid) {
681 return 1;
685 * the rdn attribute should be at the end!
686 * so we need to return a value less than zero
687 * which means m2 is greater than m1
689 if (attid_2 == *rdn_attid) {
690 return -1;
694 * See above regarding this being an unsigned comparison.
695 * Otherwise when the high bit is set on non-standard
696 * attributes, they would end up first, before objectClass
697 * (0).
699 return attid_1 > attid_2 ? 1 : -1;
702 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
703 struct replPropertyMetaDataCtr1 *ctr1,
704 const struct dsdb_attribute *rdn_sa,
705 struct ldb_dn *dn)
707 if (ctr1->count == 0) {
708 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
709 "No elements found in replPropertyMetaData for %s!\n",
710 ldb_dn_get_linearized(dn));
711 return LDB_ERR_CONSTRAINT_VIOLATION;
713 if (ctr1->array[ctr1->count - 1].attid != rdn_sa->attributeID_id) {
714 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
715 "No rDN found in replPropertyMetaData for %s!\n",
716 ldb_dn_get_linearized(dn));
717 return LDB_ERR_CONSTRAINT_VIOLATION;
720 /* the objectClass attribute is value 0x00000000, so must be first */
721 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
722 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
723 "No objectClass found in replPropertyMetaData for %s!\n",
724 ldb_dn_get_linearized(dn));
725 return LDB_ERR_OBJECT_CLASS_VIOLATION;
728 return LDB_SUCCESS;
731 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
732 struct replPropertyMetaDataCtr1 *ctr1,
733 const struct dsdb_schema *schema,
734 struct ldb_dn *dn)
736 const char *rdn_name;
737 const struct dsdb_attribute *rdn_sa;
739 rdn_name = ldb_dn_get_rdn_name(dn);
740 if (!rdn_name) {
741 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
742 __location__ ": No rDN for %s?\n",
743 ldb_dn_get_linearized(dn));
744 return LDB_ERR_INVALID_DN_SYNTAX;
747 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
748 if (rdn_sa == NULL) {
749 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
750 __location__ ": No sa found for rDN %s for %s\n",
751 rdn_name, ldb_dn_get_linearized(dn));
752 return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
755 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
756 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
758 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id,
759 replmd_replPropertyMetaData1_attid_sort);
760 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, rdn_sa, dn);
763 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
764 const struct ldb_message_element *e2,
765 const struct dsdb_schema *schema)
767 const struct dsdb_attribute *a1;
768 const struct dsdb_attribute *a2;
771 * TODO: make this faster by caching the dsdb_attribute pointer
772 * on the ldb_messag_element
775 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
776 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
779 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
780 * in the schema
782 if (!a1 || !a2) {
783 return strcasecmp(e1->name, e2->name);
785 if (a1->attributeID_id == a2->attributeID_id) {
786 return 0;
788 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
791 static void replmd_ldb_message_sort(struct ldb_message *msg,
792 const struct dsdb_schema *schema)
794 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
797 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
798 const struct GUID *invocation_id, uint64_t seq_num,
799 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
803 fix up linked attributes in replmd_add.
804 This involves setting up the right meta-data in extended DN
805 components, and creating backlinks to the object
807 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
808 uint64_t seq_num, const struct GUID *invocationId, time_t t,
809 struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent)
811 unsigned int i;
812 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
813 struct ldb_context *ldb = ldb_module_get_ctx(module);
815 /* We will take a reference to the schema in replmd_add_backlink */
816 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
817 NTTIME now;
819 unix_to_nt_time(&now, t);
821 for (i=0; i<el->num_values; i++) {
822 struct ldb_val *v = &el->values[i];
823 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
824 struct GUID target_guid;
825 NTSTATUS status;
826 int ret;
828 /* note that the DN already has the extended
829 components from the extended_dn_store module */
830 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
831 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
832 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid, parent);
833 if (ret != LDB_SUCCESS) {
834 talloc_free(tmp_ctx);
835 return ret;
837 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
838 if (ret != LDB_SUCCESS) {
839 talloc_free(tmp_ctx);
840 return ret;
844 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
845 seq_num, seq_num, now, 0, false);
846 if (ret != LDB_SUCCESS) {
847 talloc_free(tmp_ctx);
848 return ret;
851 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
852 if (ret != LDB_SUCCESS) {
853 talloc_free(tmp_ctx);
854 return ret;
858 talloc_free(tmp_ctx);
859 return LDB_SUCCESS;
864 intercept add requests
866 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
868 struct samldb_msds_intid_persistant *msds_intid_struct;
869 struct ldb_context *ldb;
870 struct ldb_control *control;
871 struct replmd_replicated_request *ac;
872 enum ndr_err_code ndr_err;
873 struct ldb_request *down_req;
874 struct ldb_message *msg;
875 const DATA_BLOB *guid_blob;
876 struct GUID guid;
877 struct replPropertyMetaDataBlob nmd;
878 struct ldb_val nmd_value;
879 const struct GUID *our_invocation_id;
880 time_t t = time(NULL);
881 NTTIME now;
882 char *time_str;
883 int ret;
884 unsigned int i;
885 unsigned int functional_level;
886 uint32_t ni=0;
887 bool allow_add_guid = false;
888 bool remove_current_guid = false;
889 bool is_urgent = false;
890 struct ldb_message_element *objectclass_el;
892 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
893 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
894 if (control) {
895 allow_add_guid = true;
898 /* do not manipulate our control entries */
899 if (ldb_dn_is_special(req->op.add.message->dn)) {
900 return ldb_next_request(module, req);
903 ldb = ldb_module_get_ctx(module);
905 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
907 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
908 if (guid_blob != NULL) {
909 if (!allow_add_guid) {
910 ldb_set_errstring(ldb,
911 "replmd_add: it's not allowed to add an object with objectGUID!");
912 return LDB_ERR_UNWILLING_TO_PERFORM;
913 } else {
914 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
915 if (!NT_STATUS_IS_OK(status)) {
916 ldb_set_errstring(ldb,
917 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
918 return LDB_ERR_UNWILLING_TO_PERFORM;
920 /* we remove this attribute as it can be a string and
921 * will not be treated correctly and then we will re-add
922 * it later on in the good format */
923 remove_current_guid = true;
925 } else {
926 /* a new GUID */
927 guid = GUID_random();
930 ac = replmd_ctx_init(module, req);
931 if (ac == NULL) {
932 return ldb_module_oom(module);
935 functional_level = dsdb_functional_level(ldb);
937 /* Get a sequence number from the backend */
938 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
939 if (ret != LDB_SUCCESS) {
940 talloc_free(ac);
941 return ret;
944 /* get our invocationId */
945 our_invocation_id = samdb_ntds_invocation_id(ldb);
946 if (!our_invocation_id) {
947 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
948 "replmd_add: unable to find invocationId\n");
949 talloc_free(ac);
950 return LDB_ERR_OPERATIONS_ERROR;
953 /* we have to copy the message as the caller might have it as a const */
954 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
955 if (msg == NULL) {
956 ldb_oom(ldb);
957 talloc_free(ac);
958 return LDB_ERR_OPERATIONS_ERROR;
961 /* generated times */
962 unix_to_nt_time(&now, t);
963 time_str = ldb_timestring(msg, t);
964 if (!time_str) {
965 ldb_oom(ldb);
966 talloc_free(ac);
967 return LDB_ERR_OPERATIONS_ERROR;
969 if (remove_current_guid) {
970 ldb_msg_remove_attr(msg,"objectGUID");
974 * remove autogenerated attributes
976 ldb_msg_remove_attr(msg, "whenCreated");
977 ldb_msg_remove_attr(msg, "whenChanged");
978 ldb_msg_remove_attr(msg, "uSNCreated");
979 ldb_msg_remove_attr(msg, "uSNChanged");
980 ldb_msg_remove_attr(msg, "replPropertyMetaData");
983 * readd replicated attributes
985 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
986 if (ret != LDB_SUCCESS) {
987 ldb_oom(ldb);
988 talloc_free(ac);
989 return ret;
992 /* build the replication meta_data */
993 ZERO_STRUCT(nmd);
994 nmd.version = 1;
995 nmd.ctr.ctr1.count = msg->num_elements;
996 nmd.ctr.ctr1.array = talloc_array(msg,
997 struct replPropertyMetaData1,
998 nmd.ctr.ctr1.count);
999 if (!nmd.ctr.ctr1.array) {
1000 ldb_oom(ldb);
1001 talloc_free(ac);
1002 return LDB_ERR_OPERATIONS_ERROR;
1005 for (i=0; i < msg->num_elements; i++) {
1006 struct ldb_message_element *e = &msg->elements[i];
1007 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1008 const struct dsdb_attribute *sa;
1010 if (e->name[0] == '@') continue;
1012 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1013 if (!sa) {
1014 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1015 "replmd_add: attribute '%s' not defined in schema\n",
1016 e->name);
1017 talloc_free(ac);
1018 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1021 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1022 /* if the attribute is not replicated (0x00000001)
1023 * or constructed (0x00000004) it has no metadata
1025 continue;
1028 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1029 ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa, req);
1030 if (ret != LDB_SUCCESS) {
1031 talloc_free(ac);
1032 return ret;
1034 /* linked attributes are not stored in
1035 replPropertyMetaData in FL above w2k */
1036 continue;
1039 m->attid = sa->attributeID_id;
1040 m->version = 1;
1041 if (m->attid == 0x20030) {
1042 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1043 const char* rdn;
1045 if (rdn_val == NULL) {
1046 ldb_oom(ldb);
1047 talloc_free(ac);
1048 return LDB_ERR_OPERATIONS_ERROR;
1051 rdn = (const char*)rdn_val->data;
1052 if (strcmp(rdn, "Deleted Objects") == 0) {
1054 * Set the originating_change_time to 29/12/9999 at 23:59:59
1055 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1057 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1058 } else {
1059 m->originating_change_time = now;
1061 } else {
1062 m->originating_change_time = now;
1064 m->originating_invocation_id = *our_invocation_id;
1065 m->originating_usn = ac->seq_num;
1066 m->local_usn = ac->seq_num;
1067 ni++;
1070 /* fix meta data count */
1071 nmd.ctr.ctr1.count = ni;
1074 * sort meta data array, and move the rdn attribute entry to the end
1076 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, ac->schema, msg->dn);
1077 if (ret != LDB_SUCCESS) {
1078 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1079 talloc_free(ac);
1080 return ret;
1083 /* generated NDR encoded values */
1084 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1085 &nmd,
1086 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1087 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1088 ldb_oom(ldb);
1089 talloc_free(ac);
1090 return LDB_ERR_OPERATIONS_ERROR;
1094 * add the autogenerated values
1096 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1097 if (ret != LDB_SUCCESS) {
1098 ldb_oom(ldb);
1099 talloc_free(ac);
1100 return ret;
1102 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1103 if (ret != LDB_SUCCESS) {
1104 ldb_oom(ldb);
1105 talloc_free(ac);
1106 return ret;
1108 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1109 if (ret != LDB_SUCCESS) {
1110 ldb_oom(ldb);
1111 talloc_free(ac);
1112 return ret;
1114 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1115 if (ret != LDB_SUCCESS) {
1116 ldb_oom(ldb);
1117 talloc_free(ac);
1118 return ret;
1120 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1121 if (ret != LDB_SUCCESS) {
1122 ldb_oom(ldb);
1123 talloc_free(ac);
1124 return ret;
1128 * sort the attributes by attid before storing the object
1130 replmd_ldb_message_sort(msg, ac->schema);
1133 * Assert that we do have an objectClass
1135 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1136 if (objectclass_el == NULL) {
1137 ldb_asprintf_errstring(ldb, __location__
1138 ": objectClass missing on %s\n",
1139 ldb_dn_get_linearized(msg->dn));
1140 talloc_free(ac);
1141 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1143 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1144 REPL_URGENT_ON_CREATE);
1146 ac->is_urgent = is_urgent;
1147 ret = ldb_build_add_req(&down_req, ldb, ac,
1148 msg,
1149 req->controls,
1150 ac, replmd_op_callback,
1151 req);
1153 LDB_REQ_SET_LOCATION(down_req);
1154 if (ret != LDB_SUCCESS) {
1155 talloc_free(ac);
1156 return ret;
1159 /* current partition control is needed by "replmd_op_callback" */
1160 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1161 ret = ldb_request_add_control(down_req,
1162 DSDB_CONTROL_CURRENT_PARTITION_OID,
1163 false, NULL);
1164 if (ret != LDB_SUCCESS) {
1165 talloc_free(ac);
1166 return ret;
1170 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1171 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1172 if (ret != LDB_SUCCESS) {
1173 talloc_free(ac);
1174 return ret;
1178 /* mark the control done */
1179 if (control) {
1180 control->critical = 0;
1182 if (ldb_dn_compare_base(ac->schema->base_dn, req->op.add.message->dn) != 0) {
1184 /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
1185 msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
1186 if (msds_intid_struct) {
1187 msds_intid_struct->usn = ac->seq_num;
1190 /* go on with the call chain */
1191 return ldb_next_request(module, down_req);
1196 * update the replPropertyMetaData for one element
1198 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1199 struct ldb_message *msg,
1200 struct ldb_message_element *el,
1201 struct ldb_message_element *old_el,
1202 struct replPropertyMetaDataBlob *omd,
1203 const struct dsdb_schema *schema,
1204 uint64_t *seq_num,
1205 const struct GUID *our_invocation_id,
1206 NTTIME now,
1207 struct ldb_request *req)
1209 uint32_t i;
1210 const struct dsdb_attribute *a;
1211 struct replPropertyMetaData1 *md1;
1212 bool may_skip = false;
1214 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1215 if (a == NULL) {
1216 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1217 /* allow this to make it possible for dbcheck
1218 to remove bad attributes */
1219 return LDB_SUCCESS;
1222 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1223 el->name));
1224 return LDB_ERR_OPERATIONS_ERROR;
1227 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1228 return LDB_SUCCESS;
1232 * if the attribute's value haven't changed, and this isn't
1233 * just a delete of everything then return LDB_SUCCESS Unless
1234 * we have the provision control or if the attribute is
1235 * interSiteTopologyGenerator as this page explain:
1236 * http://support.microsoft.com/kb/224815 this attribute is
1237 * periodicaly written by the DC responsible for the intersite
1238 * generation in a given site
1240 * Unchanged could be deleting or replacing an already-gone
1241 * thing with an unconstrained delete/empty replace or a
1242 * replace with the same value, but not an add with the same
1243 * value because that could be about adding a duplicate (which
1244 * is for someone else to error out on).
1246 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1247 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1248 may_skip = true;
1250 } else if (old_el == NULL && el->num_values == 0) {
1251 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1252 may_skip = true;
1253 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1254 may_skip = true;
1258 if (may_skip) {
1259 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1260 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1262 * allow this to make it possible for dbcheck
1263 * to rebuild broken metadata
1265 return LDB_SUCCESS;
1269 for (i=0; i<omd->ctr.ctr1.count; i++) {
1270 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
1273 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1274 /* linked attributes are not stored in
1275 replPropertyMetaData in FL above w2k, but we do
1276 raise the seqnum for the object */
1277 if (*seq_num == 0 &&
1278 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1279 return LDB_ERR_OPERATIONS_ERROR;
1281 return LDB_SUCCESS;
1284 if (i == omd->ctr.ctr1.count) {
1285 /* we need to add a new one */
1286 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1287 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1288 if (omd->ctr.ctr1.array == NULL) {
1289 ldb_oom(ldb);
1290 return LDB_ERR_OPERATIONS_ERROR;
1292 omd->ctr.ctr1.count++;
1293 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1296 /* Get a new sequence number from the backend. We only do this
1297 * if we have a change that requires a new
1298 * replPropertyMetaData element
1300 if (*seq_num == 0) {
1301 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1302 if (ret != LDB_SUCCESS) {
1303 return LDB_ERR_OPERATIONS_ERROR;
1307 md1 = &omd->ctr.ctr1.array[i];
1308 md1->version++;
1309 md1->attid = a->attributeID_id;
1310 if (md1->attid == 0x20030) {
1311 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1312 const char* rdn;
1314 if (rdn_val == NULL) {
1315 ldb_oom(ldb);
1316 return LDB_ERR_OPERATIONS_ERROR;
1319 rdn = (const char*)rdn_val->data;
1320 if (strcmp(rdn, "Deleted Objects") == 0) {
1322 * Set the originating_change_time to 29/12/9999 at 23:59:59
1323 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1325 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1326 } else {
1327 md1->originating_change_time = now;
1329 } else {
1330 md1->originating_change_time = now;
1332 md1->originating_invocation_id = *our_invocation_id;
1333 md1->originating_usn = *seq_num;
1334 md1->local_usn = *seq_num;
1336 return LDB_SUCCESS;
1339 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1341 uint32_t count = omd.ctr.ctr1.count;
1342 uint64_t max = 0;
1343 uint32_t i;
1344 for (i=0; i < count; i++) {
1345 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1346 if (max < m.local_usn) {
1347 max = m.local_usn;
1350 return max;
1354 * update the replPropertyMetaData object each time we modify an
1355 * object. This is needed for DRS replication, as the merge on the
1356 * client is based on this object
1358 static int replmd_update_rpmd(struct ldb_module *module,
1359 const struct dsdb_schema *schema,
1360 struct ldb_request *req,
1361 const char * const *rename_attrs,
1362 struct ldb_message *msg, uint64_t *seq_num,
1363 time_t t,
1364 bool *is_urgent, bool *rodc)
1366 const struct ldb_val *omd_value;
1367 enum ndr_err_code ndr_err;
1368 struct replPropertyMetaDataBlob omd;
1369 unsigned int i;
1370 NTTIME now;
1371 const struct GUID *our_invocation_id;
1372 int ret;
1373 const char * const *attrs = NULL;
1374 const char * const attrs1[] = { "replPropertyMetaData", "*", NULL };
1375 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1376 struct ldb_result *res;
1377 struct ldb_context *ldb;
1378 struct ldb_message_element *objectclass_el;
1379 enum urgent_situation situation;
1380 bool rmd_is_provided;
1382 if (rename_attrs) {
1383 attrs = rename_attrs;
1384 } else {
1385 attrs = attrs1;
1388 ldb = ldb_module_get_ctx(module);
1390 our_invocation_id = samdb_ntds_invocation_id(ldb);
1391 if (!our_invocation_id) {
1392 /* this happens during an initial vampire while
1393 updating the schema */
1394 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1395 return LDB_SUCCESS;
1398 unix_to_nt_time(&now, t);
1400 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1401 rmd_is_provided = true;
1402 } else {
1403 rmd_is_provided = false;
1406 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1407 * otherwise we consider we are updating */
1408 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1409 situation = REPL_URGENT_ON_DELETE;
1410 } else if (rename_attrs) {
1411 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1412 } else {
1413 situation = REPL_URGENT_ON_UPDATE;
1416 if (rmd_is_provided) {
1417 /* In this case the change_replmetadata control was supplied */
1418 /* We check that it's the only attribute that is provided
1419 * (it's a rare case so it's better to keep the code simplier)
1420 * We also check that the highest local_usn is bigger than
1421 * uSNChanged. */
1422 uint64_t db_seq;
1423 if( msg->num_elements != 1 ||
1424 strncmp(msg->elements[0].name,
1425 "replPropertyMetaData", 20) ) {
1426 DEBUG(0,(__location__ ": changereplmetada control called without "\
1427 "a specified replPropertyMetaData attribute or with others\n"));
1428 return LDB_ERR_OPERATIONS_ERROR;
1430 if (situation != REPL_URGENT_ON_UPDATE) {
1431 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1432 return LDB_ERR_OPERATIONS_ERROR;
1434 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1435 if (!omd_value) {
1436 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1437 ldb_dn_get_linearized(msg->dn)));
1438 return LDB_ERR_OPERATIONS_ERROR;
1440 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1441 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1442 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1443 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1444 ldb_dn_get_linearized(msg->dn)));
1445 return LDB_ERR_OPERATIONS_ERROR;
1447 *seq_num = find_max_local_usn(omd);
1449 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1450 DSDB_FLAG_NEXT_MODULE |
1451 DSDB_SEARCH_SHOW_RECYCLED |
1452 DSDB_SEARCH_SHOW_EXTENDED_DN |
1453 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1454 DSDB_SEARCH_REVEAL_INTERNALS, req);
1456 if (ret != LDB_SUCCESS) {
1457 return ret;
1460 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1461 if (*seq_num <= db_seq) {
1462 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)"\
1463 " is less or equal to uSNChanged (max = %lld uSNChanged = %lld)\n",
1464 (long long)*seq_num, (long long)db_seq));
1465 return LDB_ERR_OPERATIONS_ERROR;
1468 } else {
1469 /* search for the existing replPropertyMetaDataBlob. We need
1470 * to use REVEAL and ask for DNs in storage format to support
1471 * the check for values being the same in
1472 * replmd_update_rpmd_element()
1474 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1475 DSDB_FLAG_NEXT_MODULE |
1476 DSDB_SEARCH_SHOW_RECYCLED |
1477 DSDB_SEARCH_SHOW_EXTENDED_DN |
1478 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1479 DSDB_SEARCH_REVEAL_INTERNALS, req);
1480 if (ret != LDB_SUCCESS) {
1481 return ret;
1484 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1485 if (!omd_value) {
1486 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1487 ldb_dn_get_linearized(msg->dn)));
1488 return LDB_ERR_OPERATIONS_ERROR;
1491 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1492 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1493 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1494 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1495 ldb_dn_get_linearized(msg->dn)));
1496 return LDB_ERR_OPERATIONS_ERROR;
1499 if (omd.version != 1) {
1500 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1501 omd.version, ldb_dn_get_linearized(msg->dn)));
1502 return LDB_ERR_OPERATIONS_ERROR;
1505 for (i=0; i<msg->num_elements; i++) {
1506 struct ldb_message_element *old_el;
1507 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1508 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1509 our_invocation_id, now, req);
1510 if (ret != LDB_SUCCESS) {
1511 return ret;
1514 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1515 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1522 * Assert that we have an objectClass attribute - this is major
1523 * corruption if we don't have this!
1525 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1526 if (objectclass_el != NULL) {
1528 * Now check if this objectClass means we need to do urgent replication
1530 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1531 situation)) {
1532 *is_urgent = true;
1534 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1535 ldb_asprintf_errstring(ldb, __location__
1536 ": objectClass missing on %s\n",
1537 ldb_dn_get_linearized(msg->dn));
1538 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1542 * replmd_update_rpmd_element has done an update if the
1543 * seq_num is set
1545 if (*seq_num != 0) {
1546 struct ldb_val *md_value;
1547 struct ldb_message_element *el;
1549 /*if we are RODC and this is a DRSR update then its ok*/
1550 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1551 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)) {
1552 unsigned instanceType;
1554 ret = samdb_rodc(ldb, rodc);
1555 if (ret != LDB_SUCCESS) {
1556 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1557 } else if (*rodc) {
1558 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1559 return LDB_ERR_REFERRAL;
1562 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1563 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1564 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1565 "cannot change replicated attribute on partial replica");
1569 md_value = talloc(msg, struct ldb_val);
1570 if (md_value == NULL) {
1571 ldb_oom(ldb);
1572 return LDB_ERR_OPERATIONS_ERROR;
1575 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, schema, msg->dn);
1576 if (ret != LDB_SUCCESS) {
1577 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
1578 return ret;
1581 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1582 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1583 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1584 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1585 ldb_dn_get_linearized(msg->dn)));
1586 return LDB_ERR_OPERATIONS_ERROR;
1589 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1590 if (ret != LDB_SUCCESS) {
1591 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1592 ldb_dn_get_linearized(msg->dn)));
1593 return ret;
1596 el->num_values = 1;
1597 el->values = md_value;
1600 return LDB_SUCCESS;
1603 struct parsed_dn {
1604 struct dsdb_dn *dsdb_dn;
1605 struct GUID *guid;
1606 struct ldb_val *v;
1609 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1611 return GUID_compare(pdn1->guid, pdn2->guid);
1614 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
1615 unsigned int count, struct GUID *guid,
1616 struct ldb_dn *dn)
1618 struct parsed_dn *ret;
1619 unsigned int i;
1620 if (dn && GUID_all_zero(guid)) {
1621 /* when updating a link using DRS, we sometimes get a
1622 NULL GUID. We then need to try and match by DN */
1623 for (i=0; i<count; i++) {
1624 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1625 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1626 return &pdn[i];
1629 return NULL;
1631 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1632 return ret;
1636 get a series of message element values as an array of DNs and GUIDs
1637 the result is sorted by GUID
1639 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1640 struct ldb_message_element *el, struct parsed_dn **pdn,
1641 const char *ldap_oid, struct ldb_request *parent)
1643 unsigned int i;
1644 struct ldb_context *ldb = ldb_module_get_ctx(module);
1646 if (el == NULL) {
1647 *pdn = NULL;
1648 return LDB_SUCCESS;
1651 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1652 if (!*pdn) {
1653 ldb_module_oom(module);
1654 return LDB_ERR_OPERATIONS_ERROR;
1657 for (i=0; i<el->num_values; i++) {
1658 struct ldb_val *v = &el->values[i];
1659 NTSTATUS status;
1660 struct ldb_dn *dn;
1661 struct parsed_dn *p;
1663 p = &(*pdn)[i];
1665 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1666 if (p->dsdb_dn == NULL) {
1667 return LDB_ERR_INVALID_DN_SYNTAX;
1670 dn = p->dsdb_dn->dn;
1672 p->guid = talloc(*pdn, struct GUID);
1673 if (p->guid == NULL) {
1674 ldb_module_oom(module);
1675 return LDB_ERR_OPERATIONS_ERROR;
1678 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1679 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1680 /* we got a DN without a GUID - go find the GUID */
1681 int ret = dsdb_module_guid_by_dn(module, dn, p->guid, parent);
1682 if (ret != LDB_SUCCESS) {
1683 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1684 ldb_dn_get_linearized(dn));
1685 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
1686 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1687 ldb_attr_cmp(el->name, "member") == 0) {
1688 return LDB_ERR_UNWILLING_TO_PERFORM;
1690 return ret;
1692 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1693 if (ret != LDB_SUCCESS) {
1694 return ret;
1696 } else if (!NT_STATUS_IS_OK(status)) {
1697 return LDB_ERR_OPERATIONS_ERROR;
1700 /* keep a pointer to the original ldb_val */
1701 p->v = v;
1704 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1706 return LDB_SUCCESS;
1710 build a new extended DN, including all meta data fields
1712 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1713 RMD_ADDTIME = originating_add_time
1714 RMD_INVOCID = originating_invocation_id
1715 RMD_CHANGETIME = originating_change_time
1716 RMD_ORIGINATING_USN = originating_usn
1717 RMD_LOCAL_USN = local_usn
1718 RMD_VERSION = version
1720 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1721 const struct GUID *invocation_id, uint64_t seq_num,
1722 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1724 struct ldb_dn *dn = dsdb_dn->dn;
1725 const char *tstring, *usn_string, *flags_string;
1726 struct ldb_val tval;
1727 struct ldb_val iid;
1728 struct ldb_val usnv, local_usnv;
1729 struct ldb_val vers, flagsv;
1730 NTSTATUS status;
1731 int ret;
1732 const char *dnstring;
1733 char *vstring;
1734 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1736 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1737 if (!tstring) {
1738 return LDB_ERR_OPERATIONS_ERROR;
1740 tval = data_blob_string_const(tstring);
1742 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1743 if (!usn_string) {
1744 return LDB_ERR_OPERATIONS_ERROR;
1746 usnv = data_blob_string_const(usn_string);
1748 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1749 if (!usn_string) {
1750 return LDB_ERR_OPERATIONS_ERROR;
1752 local_usnv = data_blob_string_const(usn_string);
1754 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1755 if (!vstring) {
1756 return LDB_ERR_OPERATIONS_ERROR;
1758 vers = data_blob_string_const(vstring);
1760 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1761 if (!NT_STATUS_IS_OK(status)) {
1762 return LDB_ERR_OPERATIONS_ERROR;
1765 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1766 if (!flags_string) {
1767 return LDB_ERR_OPERATIONS_ERROR;
1769 flagsv = data_blob_string_const(flags_string);
1771 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1772 if (ret != LDB_SUCCESS) return ret;
1773 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1774 if (ret != LDB_SUCCESS) return ret;
1775 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1776 if (ret != LDB_SUCCESS) return ret;
1777 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1778 if (ret != LDB_SUCCESS) return ret;
1779 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1780 if (ret != LDB_SUCCESS) return ret;
1781 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1782 if (ret != LDB_SUCCESS) return ret;
1783 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1784 if (ret != LDB_SUCCESS) return ret;
1786 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1787 if (dnstring == NULL) {
1788 return LDB_ERR_OPERATIONS_ERROR;
1790 *v = data_blob_string_const(dnstring);
1792 return LDB_SUCCESS;
1795 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1796 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1797 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1798 uint32_t version, bool deleted);
1801 check if any links need upgrading from w2k format
1803 The parent_ctx is the ldb_message_element which contains the values array that dns[i].v points at, and which should be used for allocating any new value.
1805 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1807 uint32_t i;
1808 for (i=0; i<count; i++) {
1809 NTSTATUS status;
1810 uint32_t version;
1811 int ret;
1813 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1814 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1815 continue;
1818 /* it's an old one that needs upgrading */
1819 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1820 1, 1, 0, 0, false);
1821 if (ret != LDB_SUCCESS) {
1822 return ret;
1825 return LDB_SUCCESS;
1829 update an extended DN, including all meta data fields
1831 see replmd_build_la_val for value names
1833 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1834 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1835 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1836 uint32_t version, bool deleted)
1838 struct ldb_dn *dn = dsdb_dn->dn;
1839 const char *tstring, *usn_string, *flags_string;
1840 struct ldb_val tval;
1841 struct ldb_val iid;
1842 struct ldb_val usnv, local_usnv;
1843 struct ldb_val vers, flagsv;
1844 const struct ldb_val *old_addtime;
1845 uint32_t old_version;
1846 NTSTATUS status;
1847 int ret;
1848 const char *dnstring;
1849 char *vstring;
1850 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1852 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1853 if (!tstring) {
1854 return LDB_ERR_OPERATIONS_ERROR;
1856 tval = data_blob_string_const(tstring);
1858 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1859 if (!usn_string) {
1860 return LDB_ERR_OPERATIONS_ERROR;
1862 usnv = data_blob_string_const(usn_string);
1864 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1865 if (!usn_string) {
1866 return LDB_ERR_OPERATIONS_ERROR;
1868 local_usnv = data_blob_string_const(usn_string);
1870 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1871 if (!NT_STATUS_IS_OK(status)) {
1872 return LDB_ERR_OPERATIONS_ERROR;
1875 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1876 if (!flags_string) {
1877 return LDB_ERR_OPERATIONS_ERROR;
1879 flagsv = data_blob_string_const(flags_string);
1881 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1882 if (ret != LDB_SUCCESS) return ret;
1884 /* get the ADDTIME from the original */
1885 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1886 if (old_addtime == NULL) {
1887 old_addtime = &tval;
1889 if (dsdb_dn != old_dsdb_dn ||
1890 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
1891 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1892 if (ret != LDB_SUCCESS) return ret;
1895 /* use our invocation id */
1896 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1897 if (ret != LDB_SUCCESS) return ret;
1899 /* changetime is the current time */
1900 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1901 if (ret != LDB_SUCCESS) return ret;
1903 /* update the USN */
1904 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1905 if (ret != LDB_SUCCESS) return ret;
1907 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1908 if (ret != LDB_SUCCESS) return ret;
1910 /* increase the version by 1 */
1911 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1912 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1913 version = old_version+1;
1915 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1916 vers = data_blob_string_const(vstring);
1917 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1918 if (ret != LDB_SUCCESS) return ret;
1920 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1921 if (dnstring == NULL) {
1922 return LDB_ERR_OPERATIONS_ERROR;
1924 *v = data_blob_string_const(dnstring);
1926 return LDB_SUCCESS;
1930 handle adding a linked attribute
1932 static int replmd_modify_la_add(struct ldb_module *module,
1933 const struct dsdb_schema *schema,
1934 struct ldb_message *msg,
1935 struct ldb_message_element *el,
1936 struct ldb_message_element *old_el,
1937 const struct dsdb_attribute *schema_attr,
1938 uint64_t seq_num,
1939 time_t t,
1940 struct GUID *msg_guid,
1941 struct ldb_request *parent)
1943 unsigned int i;
1944 struct parsed_dn *dns, *old_dns;
1945 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1946 int ret;
1947 struct ldb_val *new_values = NULL;
1948 unsigned int num_new_values = 0;
1949 unsigned old_num_values = old_el?old_el->num_values:0;
1950 const struct GUID *invocation_id;
1951 struct ldb_context *ldb = ldb_module_get_ctx(module);
1952 NTTIME now;
1954 unix_to_nt_time(&now, t);
1956 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1957 if (ret != LDB_SUCCESS) {
1958 talloc_free(tmp_ctx);
1959 return ret;
1962 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1963 if (ret != LDB_SUCCESS) {
1964 talloc_free(tmp_ctx);
1965 return ret;
1968 invocation_id = samdb_ntds_invocation_id(ldb);
1969 if (!invocation_id) {
1970 talloc_free(tmp_ctx);
1971 return LDB_ERR_OPERATIONS_ERROR;
1974 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1975 if (ret != LDB_SUCCESS) {
1976 talloc_free(tmp_ctx);
1977 return ret;
1980 /* for each new value, see if it exists already with the same GUID */
1981 for (i=0; i<el->num_values; i++) {
1982 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1983 if (p == NULL) {
1984 /* this is a new linked attribute value */
1985 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1986 if (new_values == NULL) {
1987 ldb_module_oom(module);
1988 talloc_free(tmp_ctx);
1989 return LDB_ERR_OPERATIONS_ERROR;
1991 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1992 invocation_id, seq_num, seq_num, now, 0, false);
1993 if (ret != LDB_SUCCESS) {
1994 talloc_free(tmp_ctx);
1995 return ret;
1997 num_new_values++;
1998 } else {
1999 /* this is only allowed if the GUID was
2000 previously deleted. */
2001 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2003 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2004 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
2005 el->name, GUID_string(tmp_ctx, p->guid));
2006 talloc_free(tmp_ctx);
2007 /* error codes for 'member' need to be
2008 special cased */
2009 if (ldb_attr_cmp(el->name, "member") == 0) {
2010 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2011 } else {
2012 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2015 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
2016 invocation_id, seq_num, seq_num, now, 0, false);
2017 if (ret != LDB_SUCCESS) {
2018 talloc_free(tmp_ctx);
2019 return ret;
2023 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
2024 if (ret != LDB_SUCCESS) {
2025 talloc_free(tmp_ctx);
2026 return ret;
2030 /* add the new ones on to the end of the old values, constructing a new el->values */
2031 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2032 struct ldb_val,
2033 old_num_values+num_new_values);
2034 if (el->values == NULL) {
2035 ldb_module_oom(module);
2036 return LDB_ERR_OPERATIONS_ERROR;
2039 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
2040 el->num_values = old_num_values + num_new_values;
2042 talloc_steal(msg->elements, el->values);
2043 talloc_steal(el->values, new_values);
2045 talloc_free(tmp_ctx);
2047 /* we now tell the backend to replace all existing values
2048 with the one we have constructed */
2049 el->flags = LDB_FLAG_MOD_REPLACE;
2051 return LDB_SUCCESS;
2056 handle deleting all active linked attributes
2058 static int replmd_modify_la_delete(struct ldb_module *module,
2059 const struct dsdb_schema *schema,
2060 struct ldb_message *msg,
2061 struct ldb_message_element *el,
2062 struct ldb_message_element *old_el,
2063 const struct dsdb_attribute *schema_attr,
2064 uint64_t seq_num,
2065 time_t t,
2066 struct GUID *msg_guid,
2067 struct ldb_request *parent)
2069 unsigned int i;
2070 struct parsed_dn *dns, *old_dns;
2071 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2072 int ret;
2073 const struct GUID *invocation_id;
2074 struct ldb_context *ldb = ldb_module_get_ctx(module);
2075 NTTIME now;
2077 unix_to_nt_time(&now, t);
2079 /* check if there is nothing to delete */
2080 if ((!old_el || old_el->num_values == 0) &&
2081 el->num_values == 0) {
2082 return LDB_SUCCESS;
2085 if (!old_el || old_el->num_values == 0) {
2086 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2089 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2090 if (ret != LDB_SUCCESS) {
2091 talloc_free(tmp_ctx);
2092 return ret;
2095 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2096 if (ret != LDB_SUCCESS) {
2097 talloc_free(tmp_ctx);
2098 return ret;
2101 invocation_id = samdb_ntds_invocation_id(ldb);
2102 if (!invocation_id) {
2103 return LDB_ERR_OPERATIONS_ERROR;
2106 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
2107 if (ret != LDB_SUCCESS) {
2108 talloc_free(tmp_ctx);
2109 return ret;
2112 el->values = NULL;
2114 /* see if we are being asked to delete any links that
2115 don't exist or are already deleted */
2116 for (i=0; i<el->num_values; i++) {
2117 struct parsed_dn *p = &dns[i];
2118 struct parsed_dn *p2;
2119 uint32_t rmd_flags;
2121 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
2122 if (!p2) {
2123 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
2124 el->name, GUID_string(tmp_ctx, p->guid));
2125 if (ldb_attr_cmp(el->name, "member") == 0) {
2126 return LDB_ERR_UNWILLING_TO_PERFORM;
2127 } else {
2128 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2131 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
2132 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2133 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
2134 el->name, GUID_string(tmp_ctx, p->guid));
2135 if (ldb_attr_cmp(el->name, "member") == 0) {
2136 return LDB_ERR_UNWILLING_TO_PERFORM;
2137 } else {
2138 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2143 /* for each new value, see if it exists already with the same GUID
2144 if it is not already deleted and matches the delete list then delete it
2146 for (i=0; i<old_el->num_values; i++) {
2147 struct parsed_dn *p = &old_dns[i];
2148 uint32_t rmd_flags;
2150 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
2151 continue;
2154 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2155 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2157 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
2158 invocation_id, seq_num, seq_num, now, 0, true);
2159 if (ret != LDB_SUCCESS) {
2160 talloc_free(tmp_ctx);
2161 return ret;
2164 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
2165 if (ret != LDB_SUCCESS) {
2166 talloc_free(tmp_ctx);
2167 return ret;
2171 el->values = talloc_steal(msg->elements, old_el->values);
2172 el->num_values = old_el->num_values;
2174 talloc_free(tmp_ctx);
2176 /* we now tell the backend to replace all existing values
2177 with the one we have constructed */
2178 el->flags = LDB_FLAG_MOD_REPLACE;
2180 return LDB_SUCCESS;
2184 handle replacing a linked attribute
2186 static int replmd_modify_la_replace(struct ldb_module *module,
2187 const struct dsdb_schema *schema,
2188 struct ldb_message *msg,
2189 struct ldb_message_element *el,
2190 struct ldb_message_element *old_el,
2191 const struct dsdb_attribute *schema_attr,
2192 uint64_t seq_num,
2193 time_t t,
2194 struct GUID *msg_guid,
2195 struct ldb_request *parent)
2197 unsigned int i;
2198 struct parsed_dn *dns, *old_dns;
2199 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2200 int ret;
2201 const struct GUID *invocation_id;
2202 struct ldb_context *ldb = ldb_module_get_ctx(module);
2203 struct ldb_val *new_values = NULL;
2204 unsigned int num_new_values = 0;
2205 unsigned int old_num_values = old_el?old_el->num_values:0;
2206 NTTIME now;
2208 unix_to_nt_time(&now, t);
2210 /* check if there is nothing to replace */
2211 if ((!old_el || old_el->num_values == 0) &&
2212 el->num_values == 0) {
2213 return LDB_SUCCESS;
2216 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2217 if (ret != LDB_SUCCESS) {
2218 talloc_free(tmp_ctx);
2219 return ret;
2222 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2223 if (ret != LDB_SUCCESS) {
2224 talloc_free(tmp_ctx);
2225 return ret;
2228 invocation_id = samdb_ntds_invocation_id(ldb);
2229 if (!invocation_id) {
2230 return LDB_ERR_OPERATIONS_ERROR;
2233 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2234 if (ret != LDB_SUCCESS) {
2235 talloc_free(tmp_ctx);
2236 return ret;
2239 /* mark all the old ones as deleted */
2240 for (i=0; i<old_num_values; i++) {
2241 struct parsed_dn *old_p = &old_dns[i];
2242 struct parsed_dn *p;
2243 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2245 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2247 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
2248 if (ret != LDB_SUCCESS) {
2249 talloc_free(tmp_ctx);
2250 return ret;
2253 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
2254 if (p) {
2255 /* we don't delete it if we are re-adding it */
2256 continue;
2259 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
2260 invocation_id, seq_num, seq_num, now, 0, true);
2261 if (ret != LDB_SUCCESS) {
2262 talloc_free(tmp_ctx);
2263 return ret;
2267 /* for each new value, either update its meta-data, or add it
2268 * to old_el
2270 for (i=0; i<el->num_values; i++) {
2271 struct parsed_dn *p = &dns[i], *old_p;
2273 if (old_dns &&
2274 (old_p = parsed_dn_find(old_dns,
2275 old_num_values, p->guid, NULL)) != NULL) {
2276 /* update in place */
2277 ret = replmd_update_la_val(old_el->values, old_p->v, p->dsdb_dn,
2278 old_p->dsdb_dn, invocation_id,
2279 seq_num, seq_num, now, 0, false);
2280 if (ret != LDB_SUCCESS) {
2281 talloc_free(tmp_ctx);
2282 return ret;
2284 } else {
2285 /* add a new one */
2286 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
2287 num_new_values+1);
2288 if (new_values == NULL) {
2289 ldb_module_oom(module);
2290 talloc_free(tmp_ctx);
2291 return LDB_ERR_OPERATIONS_ERROR;
2293 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2294 invocation_id, seq_num, seq_num, now, 0, false);
2295 if (ret != LDB_SUCCESS) {
2296 talloc_free(tmp_ctx);
2297 return ret;
2299 num_new_values++;
2302 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
2303 if (ret != LDB_SUCCESS) {
2304 talloc_free(tmp_ctx);
2305 return ret;
2309 /* add the new values to the end of old_el */
2310 if (num_new_values != 0) {
2311 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2312 struct ldb_val, old_num_values+num_new_values);
2313 if (el->values == NULL) {
2314 ldb_module_oom(module);
2315 return LDB_ERR_OPERATIONS_ERROR;
2317 memcpy(&el->values[old_num_values], &new_values[0],
2318 sizeof(struct ldb_val)*num_new_values);
2319 el->num_values = old_num_values + num_new_values;
2320 talloc_steal(msg->elements, new_values);
2321 } else {
2322 el->values = old_el->values;
2323 el->num_values = old_el->num_values;
2324 talloc_steal(msg->elements, el->values);
2327 talloc_free(tmp_ctx);
2329 /* we now tell the backend to replace all existing values
2330 with the one we have constructed */
2331 el->flags = LDB_FLAG_MOD_REPLACE;
2333 return LDB_SUCCESS;
2338 handle linked attributes in modify requests
2340 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
2341 struct ldb_message *msg,
2342 uint64_t seq_num, time_t t,
2343 struct ldb_request *parent)
2345 struct ldb_result *res;
2346 unsigned int i;
2347 int ret;
2348 struct ldb_context *ldb = ldb_module_get_ctx(module);
2349 struct ldb_message *old_msg;
2351 const struct dsdb_schema *schema;
2352 struct GUID old_guid;
2354 if (seq_num == 0) {
2355 /* there the replmd_update_rpmd code has already
2356 * checked and saw that there are no linked
2357 * attributes */
2358 return LDB_SUCCESS;
2361 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
2362 /* don't do anything special for linked attributes */
2363 return LDB_SUCCESS;
2366 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
2367 DSDB_FLAG_NEXT_MODULE |
2368 DSDB_SEARCH_SHOW_RECYCLED |
2369 DSDB_SEARCH_REVEAL_INTERNALS |
2370 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
2371 parent);
2372 if (ret != LDB_SUCCESS) {
2373 return ret;
2375 schema = dsdb_get_schema(ldb, res);
2376 if (!schema) {
2377 return LDB_ERR_OPERATIONS_ERROR;
2380 old_msg = res->msgs[0];
2382 old_guid = samdb_result_guid(old_msg, "objectGUID");
2384 for (i=0; i<msg->num_elements; i++) {
2385 struct ldb_message_element *el = &msg->elements[i];
2386 struct ldb_message_element *old_el, *new_el;
2387 const struct dsdb_attribute *schema_attr
2388 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2389 if (!schema_attr) {
2390 ldb_asprintf_errstring(ldb,
2391 "%s: attribute %s is not a valid attribute in schema",
2392 __FUNCTION__, el->name);
2393 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2395 if (schema_attr->linkID == 0) {
2396 continue;
2398 if ((schema_attr->linkID & 1) == 1) {
2399 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
2400 continue;
2402 /* Odd is for the target. Illegal to modify */
2403 ldb_asprintf_errstring(ldb,
2404 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2405 return LDB_ERR_UNWILLING_TO_PERFORM;
2407 old_el = ldb_msg_find_element(old_msg, el->name);
2408 switch (el->flags & LDB_FLAG_MOD_MASK) {
2409 case LDB_FLAG_MOD_REPLACE:
2410 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2411 break;
2412 case LDB_FLAG_MOD_DELETE:
2413 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2414 break;
2415 case LDB_FLAG_MOD_ADD:
2416 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2417 break;
2418 default:
2419 ldb_asprintf_errstring(ldb,
2420 "invalid flags 0x%x for %s linked attribute",
2421 el->flags, el->name);
2422 return LDB_ERR_UNWILLING_TO_PERFORM;
2424 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
2425 ldb_asprintf_errstring(ldb,
2426 "Attribute %s is single valued but more than one value has been supplied",
2427 el->name);
2428 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2429 } else {
2430 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
2435 if (ret != LDB_SUCCESS) {
2436 return ret;
2438 if (old_el) {
2439 ldb_msg_remove_attr(old_msg, el->name);
2441 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2442 new_el->num_values = el->num_values;
2443 new_el->values = talloc_steal(msg->elements, el->values);
2445 /* TODO: this relises a bit too heavily on the exact
2446 behaviour of ldb_msg_find_element and
2447 ldb_msg_remove_element */
2448 old_el = ldb_msg_find_element(msg, el->name);
2449 if (old_el != el) {
2450 ldb_msg_remove_element(msg, old_el);
2451 i--;
2455 talloc_free(res);
2456 return ret;
2461 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2463 struct samldb_msds_intid_persistant *msds_intid_struct;
2464 struct ldb_context *ldb;
2465 struct replmd_replicated_request *ac;
2466 struct ldb_request *down_req;
2467 struct ldb_message *msg;
2468 time_t t = time(NULL);
2469 int ret;
2470 bool is_urgent = false, rodc = false;
2471 unsigned int functional_level;
2472 const DATA_BLOB *guid_blob;
2473 struct ldb_control *sd_propagation_control;
2475 /* do not manipulate our control entries */
2476 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2477 return ldb_next_request(module, req);
2480 sd_propagation_control = ldb_request_get_control(req,
2481 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
2482 if (sd_propagation_control != NULL) {
2483 if (req->op.mod.message->num_elements != 1) {
2484 return ldb_module_operr(module);
2486 ret = strcmp(req->op.mod.message->elements[0].name,
2487 "nTSecurityDescriptor");
2488 if (ret != 0) {
2489 return ldb_module_operr(module);
2492 return ldb_next_request(module, req);
2495 ldb = ldb_module_get_ctx(module);
2497 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2499 guid_blob = ldb_msg_find_ldb_val(req->op.mod.message, "objectGUID");
2500 if ( guid_blob != NULL ) {
2501 ldb_set_errstring(ldb,
2502 "replmd_modify: it's not allowed to change the objectGUID!");
2503 return LDB_ERR_CONSTRAINT_VIOLATION;
2506 ac = replmd_ctx_init(module, req);
2507 if (ac == NULL) {
2508 return ldb_module_oom(module);
2511 functional_level = dsdb_functional_level(ldb);
2513 /* we have to copy the message as the caller might have it as a const */
2514 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2515 if (msg == NULL) {
2516 ldb_oom(ldb);
2517 talloc_free(ac);
2518 return LDB_ERR_OPERATIONS_ERROR;
2521 ldb_msg_remove_attr(msg, "whenChanged");
2522 ldb_msg_remove_attr(msg, "uSNChanged");
2524 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
2525 msg, &ac->seq_num, t, &is_urgent, &rodc);
2526 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2527 struct loadparm_context *lp_ctx;
2528 char *referral;
2530 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2531 struct loadparm_context);
2533 referral = talloc_asprintf(req,
2534 "ldap://%s/%s",
2535 lpcfg_dnsdomain(lp_ctx),
2536 ldb_dn_get_linearized(msg->dn));
2537 ret = ldb_module_send_referral(req, referral);
2538 talloc_free(ac);
2539 return ret;
2542 if (ret != LDB_SUCCESS) {
2543 talloc_free(ac);
2544 return ret;
2547 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
2548 if (ret != LDB_SUCCESS) {
2549 talloc_free(ac);
2550 return ret;
2553 /* TODO:
2554 * - replace the old object with the newly constructed one
2557 ac->is_urgent = is_urgent;
2559 ret = ldb_build_mod_req(&down_req, ldb, ac,
2560 msg,
2561 req->controls,
2562 ac, replmd_op_callback,
2563 req);
2564 LDB_REQ_SET_LOCATION(down_req);
2565 if (ret != LDB_SUCCESS) {
2566 talloc_free(ac);
2567 return ret;
2570 /* current partition control is needed by "replmd_op_callback" */
2571 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2572 ret = ldb_request_add_control(down_req,
2573 DSDB_CONTROL_CURRENT_PARTITION_OID,
2574 false, NULL);
2575 if (ret != LDB_SUCCESS) {
2576 talloc_free(ac);
2577 return ret;
2581 /* If we are in functional level 2000, then
2582 * replmd_modify_handle_linked_attribs will have done
2583 * nothing */
2584 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2585 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2586 if (ret != LDB_SUCCESS) {
2587 talloc_free(ac);
2588 return ret;
2592 talloc_steal(down_req, msg);
2594 /* we only change whenChanged and uSNChanged if the seq_num
2595 has changed */
2596 if (ac->seq_num != 0) {
2597 ret = add_time_element(msg, "whenChanged", t);
2598 if (ret != LDB_SUCCESS) {
2599 talloc_free(ac);
2600 ldb_operr(ldb);
2601 return ret;
2604 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2605 if (ret != LDB_SUCCESS) {
2606 talloc_free(ac);
2607 ldb_operr(ldb);
2608 return ret;
2612 if (!ldb_dn_compare_base(ac->schema->base_dn, msg->dn)) {
2613 /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
2614 msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
2615 if (msds_intid_struct) {
2616 msds_intid_struct->usn = ac->seq_num;
2620 /* go on with the call chain */
2621 return ldb_next_request(module, down_req);
2624 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2627 handle a rename request
2629 On a rename we need to do an extra ldb_modify which sets the
2630 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2632 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2634 struct ldb_context *ldb;
2635 struct replmd_replicated_request *ac;
2636 int ret;
2637 struct ldb_request *down_req;
2639 /* do not manipulate our control entries */
2640 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2641 return ldb_next_request(module, req);
2644 ldb = ldb_module_get_ctx(module);
2646 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2648 ac = replmd_ctx_init(module, req);
2649 if (ac == NULL) {
2650 return ldb_module_oom(module);
2653 ret = ldb_build_rename_req(&down_req, ldb, ac,
2654 ac->req->op.rename.olddn,
2655 ac->req->op.rename.newdn,
2656 ac->req->controls,
2657 ac, replmd_rename_callback,
2658 ac->req);
2659 LDB_REQ_SET_LOCATION(down_req);
2660 if (ret != LDB_SUCCESS) {
2661 talloc_free(ac);
2662 return ret;
2665 /* go on with the call chain */
2666 return ldb_next_request(module, down_req);
2669 /* After the rename is compleated, update the whenchanged etc */
2670 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2672 struct ldb_context *ldb;
2673 struct replmd_replicated_request *ac;
2674 struct ldb_request *down_req;
2675 struct ldb_message *msg;
2676 const struct dsdb_attribute *rdn_attr;
2677 const char *rdn_name;
2678 const struct ldb_val *rdn_val;
2679 const char *attrs[5] = { NULL, };
2680 time_t t = time(NULL);
2681 int ret;
2682 bool is_urgent = false, rodc = false;
2684 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2685 ldb = ldb_module_get_ctx(ac->module);
2687 if (ares->error != LDB_SUCCESS) {
2688 return ldb_module_done(ac->req, ares->controls,
2689 ares->response, ares->error);
2692 if (ares->type != LDB_REPLY_DONE) {
2693 ldb_set_errstring(ldb,
2694 "invalid ldb_reply_type in callback");
2695 talloc_free(ares);
2696 return ldb_module_done(ac->req, NULL, NULL,
2697 LDB_ERR_OPERATIONS_ERROR);
2700 /* TODO:
2701 * - replace the old object with the newly constructed one
2704 msg = ldb_msg_new(ac);
2705 if (msg == NULL) {
2706 ldb_oom(ldb);
2707 return LDB_ERR_OPERATIONS_ERROR;
2710 msg->dn = ac->req->op.rename.newdn;
2712 rdn_name = ldb_dn_get_rdn_name(msg->dn);
2713 if (rdn_name == NULL) {
2714 talloc_free(ares);
2715 return ldb_module_done(ac->req, NULL, NULL,
2716 ldb_operr(ldb));
2719 /* normalize the rdn attribute name */
2720 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
2721 if (rdn_attr == NULL) {
2722 talloc_free(ares);
2723 return ldb_module_done(ac->req, NULL, NULL,
2724 ldb_operr(ldb));
2726 rdn_name = rdn_attr->lDAPDisplayName;
2728 rdn_val = ldb_dn_get_rdn_val(msg->dn);
2729 if (rdn_val == NULL) {
2730 talloc_free(ares);
2731 return ldb_module_done(ac->req, NULL, NULL,
2732 ldb_operr(ldb));
2735 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2736 talloc_free(ares);
2737 return ldb_module_done(ac->req, NULL, NULL,
2738 ldb_oom(ldb));
2740 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
2741 talloc_free(ares);
2742 return ldb_module_done(ac->req, NULL, NULL,
2743 ldb_oom(ldb));
2745 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2746 talloc_free(ares);
2747 return ldb_module_done(ac->req, NULL, NULL,
2748 ldb_oom(ldb));
2750 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
2751 talloc_free(ares);
2752 return ldb_module_done(ac->req, NULL, NULL,
2753 ldb_oom(ldb));
2757 * here we let replmd_update_rpmd() only search for
2758 * the existing "replPropertyMetaData" and rdn_name attributes.
2760 * We do not want the existing "name" attribute as
2761 * the "name" attribute needs to get the version
2762 * updated on rename even if the rdn value hasn't changed.
2764 * This is the diff of the meta data, for a moved user
2765 * on a w2k8r2 server:
2767 * # record 1
2768 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
2769 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
2770 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
2771 * version : 0x00000001 (1)
2772 * reserved : 0x00000000 (0)
2773 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
2774 * local_usn : 0x00000000000037a5 (14245)
2775 * array: struct replPropertyMetaData1
2776 * attid : DRSUAPI_ATTID_name (0x90001)
2777 * - version : 0x00000001 (1)
2778 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
2779 * + version : 0x00000002 (2)
2780 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
2781 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
2782 * - originating_usn : 0x00000000000037a5 (14245)
2783 * - local_usn : 0x00000000000037a5 (14245)
2784 * + originating_usn : 0x0000000000003834 (14388)
2785 * + local_usn : 0x0000000000003834 (14388)
2786 * array: struct replPropertyMetaData1
2787 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
2788 * version : 0x00000004 (4)
2790 attrs[0] = "replPropertyMetaData";
2791 attrs[1] = "objectClass";
2792 attrs[2] = "instanceType";
2793 attrs[3] = rdn_name;
2794 attrs[4] = NULL;
2796 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
2797 msg, &ac->seq_num, t, &is_urgent, &rodc);
2798 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2799 struct ldb_dn *olddn = ac->req->op.rename.olddn;
2800 struct loadparm_context *lp_ctx;
2801 char *referral;
2803 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2804 struct loadparm_context);
2806 referral = talloc_asprintf(req,
2807 "ldap://%s/%s",
2808 lpcfg_dnsdomain(lp_ctx),
2809 ldb_dn_get_linearized(olddn));
2810 ret = ldb_module_send_referral(req, referral);
2811 talloc_free(ares);
2812 return ldb_module_done(req, NULL, NULL, ret);
2815 if (ret != LDB_SUCCESS) {
2816 talloc_free(ares);
2817 return ldb_module_done(ac->req, NULL, NULL, ret);
2820 if (ac->seq_num == 0) {
2821 talloc_free(ares);
2822 return ldb_module_done(ac->req, NULL, NULL,
2823 ldb_error(ldb, ret,
2824 "internal error seq_num == 0"));
2826 ac->is_urgent = is_urgent;
2828 ret = ldb_build_mod_req(&down_req, ldb, ac,
2829 msg,
2830 req->controls,
2831 ac, replmd_op_callback,
2832 req);
2833 LDB_REQ_SET_LOCATION(down_req);
2834 if (ret != LDB_SUCCESS) {
2835 talloc_free(ac);
2836 return ret;
2839 /* current partition control is needed by "replmd_op_callback" */
2840 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2841 ret = ldb_request_add_control(down_req,
2842 DSDB_CONTROL_CURRENT_PARTITION_OID,
2843 false, NULL);
2844 if (ret != LDB_SUCCESS) {
2845 talloc_free(ac);
2846 return ret;
2850 talloc_steal(down_req, msg);
2852 ret = add_time_element(msg, "whenChanged", t);
2853 if (ret != LDB_SUCCESS) {
2854 talloc_free(ac);
2855 ldb_operr(ldb);
2856 return ret;
2859 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2860 if (ret != LDB_SUCCESS) {
2861 talloc_free(ac);
2862 ldb_operr(ldb);
2863 return ret;
2866 /* go on with the call chain - do the modify after the rename */
2867 return ldb_next_request(ac->module, down_req);
2871 * remove links from objects that point at this object when an object
2872 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
2873 * RemoveObj which states that link removal due to the object being
2874 * deleted is NOT an originating update - they just go away!
2877 static int replmd_delete_remove_link(struct ldb_module *module,
2878 const struct dsdb_schema *schema,
2879 struct ldb_dn *dn,
2880 struct ldb_message_element *el,
2881 const struct dsdb_attribute *sa,
2882 struct ldb_request *parent)
2884 unsigned int i;
2885 TALLOC_CTX *tmp_ctx = talloc_new(module);
2886 struct ldb_context *ldb = ldb_module_get_ctx(module);
2888 for (i=0; i<el->num_values; i++) {
2889 struct dsdb_dn *dsdb_dn;
2890 NTSTATUS status;
2891 int ret;
2892 struct GUID guid2;
2893 struct ldb_message *msg;
2894 const struct dsdb_attribute *target_attr;
2895 struct ldb_message_element *el2;
2896 struct ldb_val dn_val;
2898 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2899 continue;
2902 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2903 if (!dsdb_dn) {
2904 talloc_free(tmp_ctx);
2905 return LDB_ERR_OPERATIONS_ERROR;
2908 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2909 if (!NT_STATUS_IS_OK(status)) {
2910 talloc_free(tmp_ctx);
2911 return LDB_ERR_OPERATIONS_ERROR;
2914 /* remove the link */
2915 msg = ldb_msg_new(tmp_ctx);
2916 if (!msg) {
2917 ldb_module_oom(module);
2918 talloc_free(tmp_ctx);
2919 return LDB_ERR_OPERATIONS_ERROR;
2923 msg->dn = dsdb_dn->dn;
2925 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2926 if (target_attr == NULL) {
2927 continue;
2930 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2931 if (ret != LDB_SUCCESS) {
2932 ldb_module_oom(module);
2933 talloc_free(tmp_ctx);
2934 return LDB_ERR_OPERATIONS_ERROR;
2936 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2937 el2->values = &dn_val;
2938 el2->num_values = 1;
2940 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, parent);
2941 if (ret != LDB_SUCCESS) {
2942 talloc_free(tmp_ctx);
2943 return ret;
2946 talloc_free(tmp_ctx);
2947 return LDB_SUCCESS;
2952 handle update of replication meta data for deletion of objects
2954 This also handles the mapping of delete to a rename operation
2955 to allow deletes to be replicated.
2957 It also handles the incoming deleted objects, to ensure they are
2958 fully deleted here. In that case re_delete is true, and we do not
2959 use this as a signal to change the deleted state, just reinforce it.
2962 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
2964 int ret = LDB_ERR_OTHER;
2965 bool retb, disallow_move_on_delete;
2966 struct ldb_dn *old_dn, *new_dn;
2967 const char *rdn_name;
2968 const struct ldb_val *rdn_value, *new_rdn_value;
2969 struct GUID guid;
2970 struct ldb_context *ldb = ldb_module_get_ctx(module);
2971 const struct dsdb_schema *schema;
2972 struct ldb_message *msg, *old_msg;
2973 struct ldb_message_element *el;
2974 TALLOC_CTX *tmp_ctx;
2975 struct ldb_result *res, *parent_res;
2976 const char *preserved_attrs[] = {
2977 /* yes, this really is a hard coded list. See MS-ADTS
2978 section 3.1.1.5.5.1.1 */
2979 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2980 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2981 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2982 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2983 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2984 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2985 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2986 "whenChanged", NULL};
2987 unsigned int i, el_count = 0;
2988 enum deletion_state deletion_state, next_deletion_state;
2990 if (ldb_dn_is_special(req->op.del.dn)) {
2991 return ldb_next_request(module, req);
2994 tmp_ctx = talloc_new(ldb);
2995 if (!tmp_ctx) {
2996 ldb_oom(ldb);
2997 return LDB_ERR_OPERATIONS_ERROR;
3000 schema = dsdb_get_schema(ldb, tmp_ctx);
3001 if (!schema) {
3002 talloc_free(tmp_ctx);
3003 return LDB_ERR_OPERATIONS_ERROR;
3006 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
3008 /* we need the complete msg off disk, so we can work out which
3009 attributes need to be removed */
3010 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
3011 DSDB_FLAG_NEXT_MODULE |
3012 DSDB_SEARCH_SHOW_RECYCLED |
3013 DSDB_SEARCH_REVEAL_INTERNALS |
3014 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
3015 if (ret != LDB_SUCCESS) {
3016 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3017 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
3018 re_delete ? "re-delete" : "delete",
3019 ldb_dn_get_linearized(old_dn),
3020 ldb_errstring(ldb_module_get_ctx(module)));
3021 talloc_free(tmp_ctx);
3022 return ret;
3024 old_msg = res->msgs[0];
3026 replmd_deletion_state(module, old_msg,
3027 &deletion_state,
3028 &next_deletion_state);
3030 /* This supports us noticing an incoming isDeleted and acting on it */
3031 if (re_delete) {
3032 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
3033 next_deletion_state = deletion_state;
3036 if (next_deletion_state == OBJECT_REMOVED) {
3037 struct auth_session_info *session_info =
3038 (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
3039 if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
3040 ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
3041 ldb_dn_get_linearized(old_msg->dn));
3042 return LDB_ERR_UNWILLING_TO_PERFORM;
3045 /* it is already deleted - really remove it this time */
3046 talloc_free(tmp_ctx);
3047 return ldb_next_request(module, req);
3050 rdn_name = ldb_dn_get_rdn_name(old_dn);
3051 rdn_value = ldb_dn_get_rdn_val(old_dn);
3052 if ((rdn_name == NULL) || (rdn_value == NULL)) {
3053 talloc_free(tmp_ctx);
3054 return ldb_operr(ldb);
3057 msg = ldb_msg_new(tmp_ctx);
3058 if (msg == NULL) {
3059 ldb_module_oom(module);
3060 talloc_free(tmp_ctx);
3061 return LDB_ERR_OPERATIONS_ERROR;
3064 msg->dn = old_dn;
3066 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
3067 disallow_move_on_delete =
3068 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
3069 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
3071 /* work out where we will be renaming this object to */
3072 if (!disallow_move_on_delete) {
3073 struct ldb_dn *deleted_objects_dn;
3074 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
3075 &deleted_objects_dn);
3078 * We should not move objects if we can't find the
3079 * deleted objects DN. Not moving (or otherwise
3080 * harming) the Deleted Objects DN itself is handled
3081 * in the caller.
3083 if (re_delete && (ret != LDB_SUCCESS)) {
3084 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3085 if (new_dn == NULL) {
3086 ldb_module_oom(module);
3087 talloc_free(tmp_ctx);
3088 return LDB_ERR_OPERATIONS_ERROR;
3090 } else if (ret != LDB_SUCCESS) {
3091 /* this is probably an attempted delete on a partition
3092 * that doesn't allow delete operations, such as the
3093 * schema partition */
3094 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
3095 ldb_dn_get_linearized(old_dn));
3096 talloc_free(tmp_ctx);
3097 return LDB_ERR_UNWILLING_TO_PERFORM;
3098 } else {
3099 new_dn = deleted_objects_dn;
3101 } else {
3102 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3103 if (new_dn == NULL) {
3104 ldb_module_oom(module);
3105 talloc_free(tmp_ctx);
3106 return LDB_ERR_OPERATIONS_ERROR;
3110 if (deletion_state == OBJECT_NOT_DELETED) {
3111 /* get the objects GUID from the search we just did */
3112 guid = samdb_result_guid(old_msg, "objectGUID");
3114 /* Add a formatted child */
3115 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
3116 rdn_name,
3117 ldb_dn_escape_value(tmp_ctx, *rdn_value),
3118 GUID_string(tmp_ctx, &guid));
3119 if (!retb) {
3120 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
3121 ldb_dn_get_linearized(new_dn)));
3122 talloc_free(tmp_ctx);
3123 return LDB_ERR_OPERATIONS_ERROR;
3126 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
3127 if (ret != LDB_SUCCESS) {
3128 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
3129 ldb_module_oom(module);
3130 talloc_free(tmp_ctx);
3131 return ret;
3133 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3134 } else {
3136 * No matter what has happened with other renames etc, try again to
3137 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
3140 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
3141 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
3142 if (!retb) {
3143 DEBUG(0,(__location__ ": Unable to add a prepare rdn of %s",
3144 ldb_dn_get_linearized(rdn)));
3145 talloc_free(tmp_ctx);
3146 return LDB_ERR_OPERATIONS_ERROR;
3148 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
3150 retb = ldb_dn_add_child(new_dn, rdn);
3151 if (!retb) {
3152 DEBUG(0,(__location__ ": Unable to add rdn %s to base dn: %s",
3153 ldb_dn_get_linearized(rdn),
3154 ldb_dn_get_linearized(new_dn)));
3155 talloc_free(tmp_ctx);
3156 return LDB_ERR_OPERATIONS_ERROR;
3161 now we need to modify the object in the following ways:
3163 - add isDeleted=TRUE
3164 - update rDN and name, with new rDN
3165 - remove linked attributes
3166 - remove objectCategory and sAMAccountType
3167 - remove attribs not on the preserved list
3168 - preserved if in above list, or is rDN
3169 - remove all linked attribs from this object
3170 - remove all links from other objects to this object
3171 - add lastKnownParent
3172 - update replPropertyMetaData?
3174 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
3177 if (deletion_state == OBJECT_NOT_DELETED) {
3178 /* we need the storage form of the parent GUID */
3179 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
3180 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
3181 DSDB_FLAG_NEXT_MODULE |
3182 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3183 DSDB_SEARCH_REVEAL_INTERNALS|
3184 DSDB_SEARCH_SHOW_RECYCLED, req);
3185 if (ret != LDB_SUCCESS) {
3186 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3187 "repmd_delete: Failed to %s %s, because we failed to find it's parent (%s): %s",
3188 re_delete ? "re-delete" : "delete",
3189 ldb_dn_get_linearized(old_dn),
3190 ldb_dn_get_linearized(ldb_dn_get_parent(tmp_ctx, old_dn)),
3191 ldb_errstring(ldb_module_get_ctx(module)));
3192 talloc_free(tmp_ctx);
3193 return ret;
3196 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
3197 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
3198 if (ret != LDB_SUCCESS) {
3199 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
3200 ldb_module_oom(module);
3201 talloc_free(tmp_ctx);
3202 return ret;
3204 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3206 if (next_deletion_state == OBJECT_DELETED) {
3207 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
3208 if (ret != LDB_SUCCESS) {
3209 DEBUG(0,(__location__ ": Failed to add msDS-LastKnownRDN string to the msg\n"));
3210 ldb_module_oom(module);
3211 talloc_free(tmp_ctx);
3212 return ret;
3214 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
3218 switch (next_deletion_state) {
3220 case OBJECT_RECYCLED:
3221 case OBJECT_TOMBSTONE:
3224 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
3225 * describes what must be removed from a tombstone
3226 * object
3228 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
3229 * describes what must be removed from a recycled
3230 * object
3235 * we also mark it as recycled, meaning this object can't be
3236 * recovered (we are stripping its attributes).
3237 * This is done only if we have this schema object of course ...
3238 * This behavior is identical to the one of Windows 2008R2 which
3239 * always set the isRecycled attribute, even if the recycle-bin is
3240 * not activated and what ever the forest level is.
3242 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
3243 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
3244 if (ret != LDB_SUCCESS) {
3245 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
3246 ldb_module_oom(module);
3247 talloc_free(tmp_ctx);
3248 return ret;
3250 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3253 /* work out which of the old attributes we will be removing */
3254 for (i=0; i<old_msg->num_elements; i++) {
3255 const struct dsdb_attribute *sa;
3256 el = &old_msg->elements[i];
3257 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3258 if (!sa) {
3259 talloc_free(tmp_ctx);
3260 return LDB_ERR_OPERATIONS_ERROR;
3262 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
3263 /* don't remove the rDN */
3264 continue;
3266 if (sa->linkID && (sa->linkID & 1)) {
3268 we have a backlink in this object
3269 that needs to be removed. We're not
3270 allowed to remove it directly
3271 however, so we instead setup a
3272 modify to delete the corresponding
3273 forward link
3275 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
3276 if (ret != LDB_SUCCESS) {
3277 talloc_free(tmp_ctx);
3278 return LDB_ERR_OPERATIONS_ERROR;
3280 /* now we continue, which means we
3281 won't remove this backlink
3282 directly
3284 continue;
3286 if (!sa->linkID) {
3287 if (ldb_attr_in_list(preserved_attrs, el->name)) {
3288 continue;
3290 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
3291 continue;
3294 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
3295 if (ret != LDB_SUCCESS) {
3296 talloc_free(tmp_ctx);
3297 ldb_module_oom(module);
3298 return ret;
3302 /* Duplicate with the below - we remove the
3303 * samAccountType as an originating update, in case it
3304 * somehow came back. The objectCategory will have
3305 * gone in the above */
3306 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
3307 if (ret != LDB_SUCCESS) {
3308 talloc_free(tmp_ctx);
3309 ldb_module_oom(module);
3310 return ret;
3313 break;
3315 case OBJECT_DELETED:
3317 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
3318 * describes what must be removed from a deleted
3319 * object
3322 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
3323 if (ret != LDB_SUCCESS) {
3324 talloc_free(tmp_ctx);
3325 ldb_module_oom(module);
3326 return ret;
3329 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
3330 if (ret != LDB_SUCCESS) {
3331 talloc_free(tmp_ctx);
3332 ldb_module_oom(module);
3333 return ret;
3336 break;
3338 default:
3339 break;
3342 if (deletion_state == OBJECT_NOT_DELETED) {
3343 const struct dsdb_attribute *sa;
3345 /* work out what the new rdn value is, for updating the
3346 rDN and name fields */
3347 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
3348 if (new_rdn_value == NULL) {
3349 talloc_free(tmp_ctx);
3350 return ldb_operr(ldb);
3353 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
3354 if (!sa) {
3355 talloc_free(tmp_ctx);
3356 return LDB_ERR_OPERATIONS_ERROR;
3359 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
3360 &el);
3361 if (ret != LDB_SUCCESS) {
3362 talloc_free(tmp_ctx);
3363 return ret;
3365 el->flags = LDB_FLAG_MOD_REPLACE;
3367 el = ldb_msg_find_element(old_msg, "name");
3368 if (el) {
3369 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
3370 if (ret != LDB_SUCCESS) {
3371 talloc_free(tmp_ctx);
3372 return ret;
3374 el->flags = LDB_FLAG_MOD_REPLACE;
3379 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
3383 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req);
3384 if (ret != LDB_SUCCESS) {
3385 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
3386 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
3387 talloc_free(tmp_ctx);
3388 return ret;
3392 * No matter what has happned with other renames, try again to
3393 * get this to be under the deleted DN.
3395 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
3396 /* now rename onto the new DN */
3397 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
3398 if (ret != LDB_SUCCESS){
3399 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
3400 ldb_dn_get_linearized(old_dn),
3401 ldb_dn_get_linearized(new_dn),
3402 ldb_errstring(ldb)));
3403 talloc_free(tmp_ctx);
3404 return ret;
3408 talloc_free(tmp_ctx);
3410 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
3413 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
3415 return replmd_delete_internals(module, req, false);
3419 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
3421 return ret;
3424 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
3426 int ret = LDB_ERR_OTHER;
3427 /* TODO: do some error mapping */
3428 return ret;
3432 static struct replPropertyMetaData1 *
3433 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
3434 enum drsuapi_DsAttributeId attid)
3436 uint32_t i;
3437 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
3439 for (i = 0; i < rpmd_ctr->count; i++) {
3440 if (rpmd_ctr->array[i].attid == attid) {
3441 return &rpmd_ctr->array[i];
3444 return NULL;
3449 return true if an update is newer than an existing entry
3450 see section 5.11 of MS-ADTS
3452 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
3453 const struct GUID *update_invocation_id,
3454 uint32_t current_version,
3455 uint32_t update_version,
3456 NTTIME current_change_time,
3457 NTTIME update_change_time)
3459 if (update_version != current_version) {
3460 return update_version > current_version;
3462 if (update_change_time != current_change_time) {
3463 return update_change_time > current_change_time;
3465 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
3468 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
3469 struct replPropertyMetaData1 *new_m)
3471 return replmd_update_is_newer(&cur_m->originating_invocation_id,
3472 &new_m->originating_invocation_id,
3473 cur_m->version,
3474 new_m->version,
3475 cur_m->originating_change_time,
3476 new_m->originating_change_time);
3481 form a conflict DN
3483 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
3485 const struct ldb_val *rdn_val;
3486 const char *rdn_name;
3487 struct ldb_dn *new_dn;
3489 rdn_val = ldb_dn_get_rdn_val(dn);
3490 rdn_name = ldb_dn_get_rdn_name(dn);
3491 if (!rdn_val || !rdn_name) {
3492 return NULL;
3495 new_dn = ldb_dn_copy(mem_ctx, dn);
3496 if (!new_dn) {
3497 return NULL;
3500 if (!ldb_dn_remove_child_components(new_dn, 1)) {
3501 return NULL;
3504 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
3505 rdn_name,
3506 ldb_dn_escape_value(new_dn, *rdn_val),
3507 GUID_string(new_dn, guid))) {
3508 return NULL;
3511 return new_dn;
3516 perform a modify operation which sets the rDN and name attributes to
3517 their current values. This has the effect of changing these
3518 attributes to have been last updated by the current DC. This is
3519 needed to ensure that renames performed as part of conflict
3520 resolution are propogated to other DCs
3522 static int replmd_name_modify(struct replmd_replicated_request *ar,
3523 struct ldb_request *req, struct ldb_dn *dn)
3525 struct ldb_message *msg;
3526 const char *rdn_name;
3527 const struct ldb_val *rdn_val;
3528 const struct dsdb_attribute *rdn_attr;
3529 int ret;
3531 msg = ldb_msg_new(req);
3532 if (msg == NULL) {
3533 goto failed;
3535 msg->dn = dn;
3537 rdn_name = ldb_dn_get_rdn_name(dn);
3538 if (rdn_name == NULL) {
3539 goto failed;
3542 /* normalize the rdn attribute name */
3543 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3544 if (rdn_attr == NULL) {
3545 goto failed;
3547 rdn_name = rdn_attr->lDAPDisplayName;
3549 rdn_val = ldb_dn_get_rdn_val(dn);
3550 if (rdn_val == NULL) {
3551 goto failed;
3554 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3555 goto failed;
3557 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3558 goto failed;
3560 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3561 goto failed;
3563 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3564 goto failed;
3567 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3568 if (ret != LDB_SUCCESS) {
3569 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
3570 ldb_dn_get_linearized(dn),
3571 ldb_errstring(ldb_module_get_ctx(ar->module))));
3572 return ret;
3575 talloc_free(msg);
3577 return LDB_SUCCESS;
3579 failed:
3580 talloc_free(msg);
3581 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
3582 ldb_dn_get_linearized(dn)));
3583 return LDB_ERR_OPERATIONS_ERROR;
3588 callback for conflict DN handling where we have renamed the incoming
3589 record. After renaming it, we need to ensure the change of name and
3590 rDN for the incoming record is seen as an originating update by this DC.
3592 This also handles updating lastKnownParent for entries sent to lostAndFound
3594 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3596 struct replmd_replicated_request *ar =
3597 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3598 struct ldb_dn *conflict_dn;
3599 int ret;
3601 if (ares->error != LDB_SUCCESS) {
3602 /* call the normal callback for everything except success */
3603 return replmd_op_callback(req, ares);
3606 switch (req->operation) {
3607 case LDB_ADD:
3608 conflict_dn = req->op.add.message->dn;
3609 break;
3610 case LDB_MODIFY:
3611 conflict_dn = req->op.mod.message->dn;
3612 break;
3613 default:
3614 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
3617 /* perform a modify of the rDN and name of the record */
3618 ret = replmd_name_modify(ar, req, conflict_dn);
3619 if (ret != LDB_SUCCESS) {
3620 ares->error = ret;
3621 return replmd_op_callback(req, ares);
3624 if (ar->objs->objects[ar->index_current].last_known_parent) {
3625 struct ldb_message *msg = ldb_msg_new(req);
3626 if (msg == NULL) {
3627 ldb_module_oom(ar->module);
3628 return LDB_ERR_OPERATIONS_ERROR;
3631 msg->dn = req->op.add.message->dn;
3633 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
3634 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
3635 if (ret != LDB_SUCCESS) {
3636 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
3637 ldb_module_oom(ar->module);
3638 return ret;
3640 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
3642 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3643 if (ret != LDB_SUCCESS) {
3644 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
3645 ldb_dn_get_linearized(msg->dn),
3646 ldb_errstring(ldb_module_get_ctx(ar->module))));
3647 return ret;
3649 TALLOC_FREE(msg);
3652 return replmd_op_callback(req, ares);
3656 callback for replmd_replicated_apply_add() and replmd_replicated_handle_rename()
3657 This copes with the creation of conflict records in the case where
3658 the DN exists, but with a different objectGUID
3660 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))
3662 struct ldb_dn *conflict_dn;
3663 struct replmd_replicated_request *ar =
3664 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3665 struct ldb_result *res;
3666 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
3667 int ret;
3668 const struct ldb_val *omd_value;
3669 struct replPropertyMetaDataBlob omd, *rmd;
3670 enum ndr_err_code ndr_err;
3671 bool rename_incoming_record, rodc;
3672 struct replPropertyMetaData1 *rmd_name, *omd_name;
3673 struct ldb_message *msg;
3675 req->callback = callback;
3677 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
3678 /* call the normal callback for everything except
3679 conflicts */
3680 return ldb_module_done(req, ares->controls, ares->response, ares->error);
3683 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
3684 if (ret != LDB_SUCCESS) {
3685 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)));
3686 return ldb_module_done(req, ares->controls, ares->response, LDB_ERR_OPERATIONS_ERROR);
3689 * we have a conflict, and need to decide if we will keep the
3690 * new record or the old record
3693 msg = ar->objs->objects[ar->index_current].msg;
3695 switch (req->operation) {
3696 case LDB_ADD:
3697 conflict_dn = msg->dn;
3698 break;
3699 case LDB_RENAME:
3700 conflict_dn = req->op.rename.newdn;
3701 break;
3702 default:
3703 return ldb_module_done(req, ares->controls, ares->response, ldb_module_operr(ar->module));
3706 if (rodc) {
3708 * We are on an RODC, or were a GC for this
3709 * partition, so we have to fail this until
3710 * someone who owns the partition sorts it
3711 * out
3713 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3714 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
3715 " - We must fail the operation until a master for this partition resolves the conflict",
3716 ldb_dn_get_linearized(conflict_dn));
3717 goto failed;
3721 * first we need the replPropertyMetaData attribute from the
3722 * old record
3724 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
3725 attrs,
3726 DSDB_FLAG_NEXT_MODULE |
3727 DSDB_SEARCH_SHOW_DELETED |
3728 DSDB_SEARCH_SHOW_RECYCLED, req);
3729 if (ret != LDB_SUCCESS) {
3730 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
3731 ldb_dn_get_linearized(conflict_dn)));
3732 goto failed;
3735 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
3736 if (omd_value == NULL) {
3737 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
3738 ldb_dn_get_linearized(conflict_dn)));
3739 goto failed;
3742 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
3743 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3744 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3745 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
3746 ldb_dn_get_linearized(conflict_dn)));
3747 goto failed;
3750 rmd = ar->objs->objects[ar->index_current].meta_data;
3752 /* we decide which is newer based on the RPMD on the name
3753 attribute. See [MS-DRSR] ResolveNameConflict */
3754 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
3755 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
3756 if (!rmd_name || !omd_name) {
3757 DEBUG(0,(__location__ ": Failed to find name attribute in replPropertyMetaData for %s\n",
3758 ldb_dn_get_linearized(conflict_dn)));
3759 goto failed;
3762 rename_incoming_record = !(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) &&
3763 !replmd_replPropertyMetaData1_is_newer(omd_name, rmd_name);
3765 if (rename_incoming_record) {
3766 struct GUID guid;
3767 struct ldb_dn *new_dn;
3770 * We want to run the original callback here, which
3771 * will return LDB_ERR_ENTRY_ALREADY_EXISTS to the
3772 * caller, which will in turn know to rename the
3773 * incoming record. The error string is set in case
3774 * this isn't handled properly at some point in the
3775 * future.
3777 if (req->operation == LDB_RENAME) {
3778 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3779 "Unable to handle incoming renames where this would "
3780 "create a conflict. Incoming record is %s (caller to handle)\n",
3781 ldb_dn_get_extended_linearized(req, conflict_dn, 1));
3783 goto failed;
3786 guid = samdb_result_guid(msg, "objectGUID");
3787 if (GUID_all_zero(&guid)) {
3788 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
3789 ldb_dn_get_linearized(conflict_dn)));
3790 goto failed;
3792 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3793 if (new_dn == NULL) {
3794 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3795 ldb_dn_get_linearized(conflict_dn)));
3796 goto failed;
3799 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
3800 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3802 /* re-submit the request, but with a different
3803 callback, so we don't loop forever. */
3804 msg->dn = new_dn;
3805 req->callback = replmd_op_name_modify_callback;
3807 return ldb_next_request(ar->module, req);
3808 } else {
3809 /* we are renaming the existing record */
3810 struct GUID guid;
3811 struct ldb_dn *new_dn;
3813 guid = samdb_result_guid(res->msgs[0], "objectGUID");
3814 if (GUID_all_zero(&guid)) {
3815 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
3816 ldb_dn_get_linearized(conflict_dn)));
3817 goto failed;
3820 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3821 if (new_dn == NULL) {
3822 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3823 ldb_dn_get_linearized(conflict_dn)));
3824 goto failed;
3827 DEBUG(2,(__location__ ": Resolving conflict record via existing rename '%s' -> '%s'\n",
3828 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3830 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
3831 DSDB_FLAG_OWN_MODULE, req);
3832 if (ret != LDB_SUCCESS) {
3833 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
3834 ldb_dn_get_linearized(conflict_dn),
3835 ldb_dn_get_linearized(new_dn),
3836 ldb_errstring(ldb_module_get_ctx(ar->module))));
3837 goto failed;
3841 * now we need to ensure that the rename is seen as an
3842 * originating update. We do that with a modify.
3844 ret = replmd_name_modify(ar, req, new_dn);
3845 if (ret != LDB_SUCCESS) {
3846 goto failed;
3849 return ldb_next_request(ar->module, req);
3852 failed:
3853 /* on failure do the original callback. This means replication
3854 * will stop with an error, but there is not much else we can
3855 * do
3857 return ldb_module_done(req, ares->controls, ares->response, ares->error);
3861 callback for replmd_replicated_apply_add()
3862 This copes with the creation of conflict records in the case where
3863 the DN exists, but with a different objectGUID
3865 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
3867 struct replmd_replicated_request *ar =
3868 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3870 if (ar->objs->objects[ar->index_current].last_known_parent) {
3871 /* This is like a conflict DN, where we put the object in LostAndFound
3872 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
3873 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
3876 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
3880 callback for replmd_replicated_handle_rename()
3881 This copes with the creation of conflict records in the case where
3882 the DN exists, but with a different objectGUID
3884 static int replmd_op_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3886 return replmd_op_possible_conflict_callback(req, ares, ldb_modify_default_callback);
3890 this is called when a new object comes in over DRS
3892 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
3894 struct ldb_context *ldb;
3895 struct ldb_request *change_req;
3896 enum ndr_err_code ndr_err;
3897 struct ldb_message *msg;
3898 struct replPropertyMetaDataBlob *md;
3899 struct ldb_val md_value;
3900 unsigned int i;
3901 int ret;
3902 bool remote_isDeleted = false;
3903 const struct dsdb_attribute *rdn_sa;
3904 const char *rdn_name;
3906 ldb = ldb_module_get_ctx(ar->module);
3907 msg = ar->objs->objects[ar->index_current].msg;
3908 md = ar->objs->objects[ar->index_current].meta_data;
3910 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3911 if (ret != LDB_SUCCESS) {
3912 return replmd_replicated_request_error(ar, ret);
3915 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
3916 if (ret != LDB_SUCCESS) {
3917 return replmd_replicated_request_error(ar, ret);
3920 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
3921 if (ret != LDB_SUCCESS) {
3922 return replmd_replicated_request_error(ar, ret);
3925 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
3926 if (ret != LDB_SUCCESS) {
3927 return replmd_replicated_request_error(ar, ret);
3930 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
3931 if (ret != LDB_SUCCESS) {
3932 return replmd_replicated_request_error(ar, ret);
3935 /* remove any message elements that have zero values */
3936 for (i=0; i<msg->num_elements; i++) {
3937 struct ldb_message_element *el = &msg->elements[i];
3939 if (el->num_values == 0) {
3940 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
3941 ldb_asprintf_errstring(ldb, __location__
3942 ": empty objectClass sent on %s, aborting replication\n",
3943 ldb_dn_get_linearized(msg->dn));
3944 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
3947 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
3948 el->name));
3949 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
3950 msg->num_elements--;
3951 i--;
3952 continue;
3956 if (DEBUGLVL(4)) {
3957 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
3958 DEBUG(4, ("DRS replication add message:\n%s\n", s));
3959 talloc_free(s);
3962 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
3963 "isDeleted", false);
3966 * the meta data array is already sorted by the caller
3969 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3970 if (rdn_name == NULL) {
3971 ldb_asprintf_errstring(ldb, __location__ ": No rDN for %s?\n", ldb_dn_get_linearized(msg->dn));
3972 return replmd_replicated_request_error(ar, LDB_ERR_INVALID_DN_SYNTAX);
3975 rdn_sa = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3976 if (rdn_sa == NULL) {
3977 ldb_asprintf_errstring(ldb, ": No schema attribute found for rDN %s for %s\n",
3978 rdn_name, ldb_dn_get_linearized(msg->dn));
3979 return replmd_replicated_request_error(ar, LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE);
3982 ret = replmd_replPropertyMetaDataCtr1_verify(ldb, &md->ctr.ctr1, rdn_sa, msg->dn);
3983 if (ret != LDB_SUCCESS) {
3984 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
3985 return replmd_replicated_request_error(ar, ret);
3988 for (i=0; i < md->ctr.ctr1.count; i++) {
3989 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
3991 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
3992 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
3993 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3994 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3995 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3997 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
3998 if (ret != LDB_SUCCESS) {
3999 return replmd_replicated_request_error(ar, ret);
4002 replmd_ldb_message_sort(msg, ar->schema);
4004 if (!remote_isDeleted) {
4005 ret = dsdb_module_schedule_sd_propagation(ar->module,
4006 ar->objs->partition_dn,
4007 msg->dn, true);
4008 if (ret != LDB_SUCCESS) {
4009 return replmd_replicated_request_error(ar, ret);
4013 ar->isDeleted = remote_isDeleted;
4015 ret = ldb_build_add_req(&change_req,
4016 ldb,
4018 msg,
4019 ar->controls,
4021 replmd_op_add_callback,
4022 ar->req);
4023 LDB_REQ_SET_LOCATION(change_req);
4024 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4026 /* current partition control needed by "repmd_op_callback" */
4027 ret = ldb_request_add_control(change_req,
4028 DSDB_CONTROL_CURRENT_PARTITION_OID,
4029 false, NULL);
4030 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4032 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4033 /* this tells the partition module to make it a
4034 partial replica if creating an NC */
4035 ret = ldb_request_add_control(change_req,
4036 DSDB_CONTROL_PARTIAL_REPLICA,
4037 false, NULL);
4038 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4041 return ldb_next_request(ar->module, change_req);
4044 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
4045 struct ldb_reply *ares)
4047 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4048 struct replmd_replicated_request);
4049 int ret;
4051 if (!ares) {
4052 return ldb_module_done(ar->req, NULL, NULL,
4053 LDB_ERR_OPERATIONS_ERROR);
4055 if (ares->error != LDB_SUCCESS &&
4056 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4058 * TODO: deal with the above error that the parent object doesn't exist
4061 return ldb_module_done(ar->req, ares->controls,
4062 ares->response, ares->error);
4065 switch (ares->type) {
4066 case LDB_REPLY_ENTRY:
4068 struct ldb_message *parent_msg = ares->message;
4069 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
4070 struct ldb_dn *parent_dn;
4071 int comp_num;
4073 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
4074 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
4075 /* Per MS-DRSR 4.1.10.6.10
4076 * FindBestParentObject we need to move this
4077 * new object under a deleted object to
4078 * lost-and-found */
4079 struct ldb_dn *nc_root;
4081 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
4082 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4083 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4084 "No suitable NC root found for %s. "
4085 "We need to move this object because parent object %s "
4086 "is deleted, but this object is not.",
4087 ldb_dn_get_linearized(msg->dn),
4088 ldb_dn_get_linearized(parent_msg->dn));
4089 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4090 } else if (ret != LDB_SUCCESS) {
4091 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4092 "Unable to find NC root for %s: %s. "
4093 "We need to move this object because parent object %s "
4094 "is deleted, but this object is not.",
4095 ldb_dn_get_linearized(msg->dn),
4096 ldb_errstring(ldb_module_get_ctx(ar->module)),
4097 ldb_dn_get_linearized(parent_msg->dn));
4098 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4101 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
4102 nc_root,
4103 DS_GUID_LOSTANDFOUND_CONTAINER,
4104 &parent_dn);
4105 if (ret != LDB_SUCCESS) {
4106 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4107 "Unable to find LostAndFound Container for %s "
4108 "in partition %s: %s. "
4109 "We need to move this object because parent object %s "
4110 "is deleted, but this object is not.",
4111 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
4112 ldb_errstring(ldb_module_get_ctx(ar->module)),
4113 ldb_dn_get_linearized(parent_msg->dn));
4114 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4116 ar->objs->objects[ar->index_current].last_known_parent
4117 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4118 } else {
4119 parent_dn = parent_msg->dn;
4122 comp_num = ldb_dn_get_comp_num(msg->dn);
4123 if (comp_num > 1) {
4124 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
4125 talloc_free(ares);
4126 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4129 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
4130 talloc_free(ares);
4131 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4133 break;
4135 case LDB_REPLY_REFERRAL:
4136 /* we ignore referrals */
4137 break;
4139 case LDB_REPLY_DONE:
4140 if (ar->search_msg != NULL) {
4141 ret = replmd_replicated_apply_merge(ar);
4142 } else {
4143 ret = replmd_replicated_apply_add(ar);
4145 if (ret != LDB_SUCCESS) {
4146 return ldb_module_done(ar->req, NULL, NULL, ret);
4150 talloc_free(ares);
4151 return LDB_SUCCESS;
4155 * Look for the parent object, so we put the new object in the right
4156 * place This is akin to NameObject in MS-DRSR - this routine and the
4157 * callbacks find the right parent name, and correct name for this
4158 * object
4161 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
4163 struct ldb_context *ldb;
4164 int ret;
4165 char *tmp_str;
4166 char *filter;
4167 struct ldb_request *search_req;
4168 static const char *attrs[] = {"isDeleted", NULL};
4170 ldb = ldb_module_get_ctx(ar->module);
4172 if (!ar->objs->objects[ar->index_current].parent_guid_value.data) {
4173 if (ar->search_msg != NULL) {
4174 return replmd_replicated_apply_merge(ar);
4175 } else {
4176 return replmd_replicated_apply_add(ar);
4180 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].parent_guid_value);
4181 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4183 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
4184 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4185 talloc_free(tmp_str);
4187 ret = ldb_build_search_req(&search_req,
4188 ldb,
4190 ar->objs->partition_dn,
4191 LDB_SCOPE_SUBTREE,
4192 filter,
4193 attrs,
4194 NULL,
4196 replmd_replicated_apply_search_for_parent_callback,
4197 ar->req);
4198 LDB_REQ_SET_LOCATION(search_req);
4200 ret = dsdb_request_add_controls(search_req,
4201 DSDB_SEARCH_SHOW_RECYCLED|
4202 DSDB_SEARCH_SHOW_DELETED|
4203 DSDB_SEARCH_SHOW_EXTENDED_DN);
4204 if (ret != LDB_SUCCESS) {
4205 return ret;
4208 return ldb_next_request(ar->module, search_req);
4212 handle renames that come in over DRS replication
4214 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
4215 struct ldb_message *msg,
4216 struct ldb_request *parent)
4218 struct ldb_request *req;
4219 int ret;
4220 TALLOC_CTX *tmp_ctx = talloc_new(msg);
4221 struct ldb_result *res;
4223 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
4224 ldb_dn_get_linearized(ar->search_msg->dn),
4225 ldb_dn_get_linearized(msg->dn)));
4228 res = talloc_zero(tmp_ctx, struct ldb_result);
4229 if (!res) {
4230 talloc_free(tmp_ctx);
4231 return ldb_oom(ldb_module_get_ctx(ar->module));
4234 /* pass rename to the next module
4235 * so it doesn't appear as an originating update */
4236 ret = ldb_build_rename_req(&req, ldb_module_get_ctx(ar->module), tmp_ctx,
4237 ar->search_msg->dn, msg->dn,
4238 NULL,
4240 replmd_op_rename_callback,
4241 parent);
4242 LDB_REQ_SET_LOCATION(req);
4243 if (ret != LDB_SUCCESS) {
4244 talloc_free(tmp_ctx);
4245 return ret;
4248 ret = dsdb_request_add_controls(req, DSDB_MODIFY_RELAX);
4249 if (ret != LDB_SUCCESS) {
4250 talloc_free(tmp_ctx);
4251 return ret;
4254 ret = ldb_next_request(ar->module, req);
4256 if (ret == LDB_SUCCESS) {
4257 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
4260 talloc_free(tmp_ctx);
4261 return ret;
4265 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
4267 struct ldb_context *ldb;
4268 struct ldb_request *change_req;
4269 enum ndr_err_code ndr_err;
4270 struct ldb_message *msg;
4271 struct replPropertyMetaDataBlob *rmd;
4272 struct replPropertyMetaDataBlob omd;
4273 const struct ldb_val *omd_value;
4274 struct replPropertyMetaDataBlob nmd;
4275 struct ldb_val nmd_value;
4276 unsigned int i;
4277 uint32_t j,ni=0;
4278 unsigned int removed_attrs = 0;
4279 int ret;
4280 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
4281 bool isDeleted = false;
4282 bool local_isDeleted = false;
4283 bool remote_isDeleted = false;
4284 bool take_remote_isDeleted = false;
4285 bool sd_updated = false;
4286 bool renamed = false;
4288 ldb = ldb_module_get_ctx(ar->module);
4289 msg = ar->objs->objects[ar->index_current].msg;
4291 rmd = ar->objs->objects[ar->index_current].meta_data;
4292 ZERO_STRUCT(omd);
4293 omd.version = 1;
4295 /* find existing meta data */
4296 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
4297 if (omd_value) {
4298 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
4299 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4300 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4301 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4302 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4305 if (omd.version != 1) {
4306 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4310 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
4311 "isDeleted", false);
4312 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4313 "isDeleted", false);
4315 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0) {
4316 ret = LDB_SUCCESS;
4317 } else {
4319 * handle renames, even just by case that come in over
4320 * DRS. Changes in the parent DN don't hit us here,
4321 * because the search for a parent will clean up those
4322 * components.
4324 * We also have already filtered out the case where
4325 * the peer has an older name to what we have (see
4326 * replmd_replicated_apply_search_callback())
4328 renamed = true;
4329 ret = replmd_replicated_handle_rename(ar, msg, ar->req);
4333 * This particular error code means that we already tried the
4334 * conflict algrorithm, and the existing record name was newer, so we
4335 * need to rename the incoming record
4337 if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
4338 struct GUID guid;
4339 NTSTATUS status;
4340 struct ldb_dn *new_dn;
4341 status = GUID_from_ndr_blob(&ar->objs->objects[ar->index_current].guid_value, &guid);
4342 /* This really, really can't fail */
4343 SMB_ASSERT(NT_STATUS_IS_OK(status));
4345 new_dn = replmd_conflict_dn(msg, msg->dn, &guid);
4346 if (new_dn == NULL) {
4347 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4348 "Failed to form conflict DN for %s\n",
4349 ldb_dn_get_linearized(msg->dn));
4351 return replmd_replicated_request_werror(ar, WERR_NOMEM);
4354 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
4355 DSDB_FLAG_NEXT_MODULE, ar->req);
4356 if (ret != LDB_SUCCESS) {
4357 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4358 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
4359 ldb_dn_get_linearized(msg->dn),
4360 ldb_dn_get_linearized(ar->search_msg->dn),
4361 ldb_dn_get_linearized(new_dn),
4362 ldb_errstring(ldb_module_get_ctx(ar->module)));
4363 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4366 /* Set the callback to one that will fix up the name to be a conflict DN */
4367 callback = replmd_op_name_modify_callback;
4368 msg->dn = new_dn;
4369 renamed = true;
4370 } else if (ret != LDB_SUCCESS) {
4371 ldb_debug(ldb, LDB_DEBUG_FATAL,
4372 "replmd_replicated_request rename %s => %s failed - %s\n",
4373 ldb_dn_get_linearized(ar->search_msg->dn),
4374 ldb_dn_get_linearized(msg->dn),
4375 ldb_errstring(ldb));
4376 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4379 ZERO_STRUCT(nmd);
4380 nmd.version = 1;
4381 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
4382 nmd.ctr.ctr1.array = talloc_array(ar,
4383 struct replPropertyMetaData1,
4384 nmd.ctr.ctr1.count);
4385 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4387 /* first copy the old meta data */
4388 for (i=0; i < omd.ctr.ctr1.count; i++) {
4389 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
4390 ni++;
4393 ar->seq_num = 0;
4394 /* now merge in the new meta data */
4395 for (i=0; i < rmd->ctr.ctr1.count; i++) {
4396 bool found = false;
4398 for (j=0; j < ni; j++) {
4399 bool cmp;
4401 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
4402 continue;
4405 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4407 * if we compare equal then do an
4408 * update. This is used when a client
4409 * asks for a FULL_SYNC, and can be
4410 * used to recover a corrupt
4411 * replica.
4413 * This call is a bit tricky, what we
4414 * are doing it turning the 'is_newer'
4415 * call into a 'not is older' by
4416 * swapping i and j, and negating the
4417 * outcome.
4419 cmp = !replmd_replPropertyMetaData1_is_newer(&rmd->ctr.ctr1.array[i],
4420 &nmd.ctr.ctr1.array[j]);
4421 } else {
4422 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
4423 &rmd->ctr.ctr1.array[i]);
4425 if (cmp) {
4426 /* replace the entry */
4427 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
4428 if (ar->seq_num == 0) {
4429 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4430 if (ret != LDB_SUCCESS) {
4431 return replmd_replicated_request_error(ar, ret);
4434 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
4435 switch (nmd.ctr.ctr1.array[j].attid) {
4436 case DRSUAPI_ATTID_ntSecurityDescriptor:
4437 sd_updated = true;
4438 break;
4439 case DRSUAPI_ATTID_isDeleted:
4440 take_remote_isDeleted = true;
4441 break;
4442 default:
4443 break;
4445 found = true;
4446 break;
4449 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
4450 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
4451 msg->elements[i-removed_attrs].name,
4452 ldb_dn_get_linearized(msg->dn),
4453 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
4456 /* we don't want to apply this change so remove the attribute */
4457 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
4458 removed_attrs++;
4460 found = true;
4461 break;
4464 if (found) continue;
4466 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
4467 if (ar->seq_num == 0) {
4468 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4469 if (ret != LDB_SUCCESS) {
4470 return replmd_replicated_request_error(ar, ret);
4473 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
4474 switch (nmd.ctr.ctr1.array[ni].attid) {
4475 case DRSUAPI_ATTID_ntSecurityDescriptor:
4476 sd_updated = true;
4477 break;
4478 case DRSUAPI_ATTID_isDeleted:
4479 take_remote_isDeleted = true;
4480 break;
4481 default:
4482 break;
4484 ni++;
4488 * finally correct the size of the meta_data array
4490 nmd.ctr.ctr1.count = ni;
4493 * the rdn attribute (the alias for the name attribute),
4494 * 'cn' for most objects is the last entry in the meta data array
4495 * we have stored
4497 * sort the new meta data array
4499 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, ar->schema, msg->dn);
4500 if (ret != LDB_SUCCESS) {
4501 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
4502 return ret;
4506 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
4507 * UpdateObject.
4509 * This also controls SD propagation below
4511 if (take_remote_isDeleted) {
4512 isDeleted = remote_isDeleted;
4513 } else {
4514 isDeleted = local_isDeleted;
4517 ar->isDeleted = isDeleted;
4520 * check if some replicated attributes left, otherwise skip the ldb_modify() call
4522 if (msg->num_elements == 0) {
4523 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
4524 ar->index_current);
4526 return replmd_replicated_apply_isDeleted(ar);
4529 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
4530 ar->index_current, msg->num_elements);
4532 if (renamed) {
4533 sd_updated = true;
4536 if (sd_updated && !isDeleted) {
4537 ret = dsdb_module_schedule_sd_propagation(ar->module,
4538 ar->objs->partition_dn,
4539 msg->dn, true);
4540 if (ret != LDB_SUCCESS) {
4541 return ldb_operr(ldb);
4545 /* create the meta data value */
4546 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
4547 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
4548 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4549 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4550 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4554 * when we know that we'll modify the record, add the whenChanged, uSNChanged
4555 * and replPopertyMetaData attributes
4557 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4558 if (ret != LDB_SUCCESS) {
4559 return replmd_replicated_request_error(ar, ret);
4561 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4562 if (ret != LDB_SUCCESS) {
4563 return replmd_replicated_request_error(ar, ret);
4565 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
4566 if (ret != LDB_SUCCESS) {
4567 return replmd_replicated_request_error(ar, ret);
4570 replmd_ldb_message_sort(msg, ar->schema);
4572 /* we want to replace the old values */
4573 for (i=0; i < msg->num_elements; i++) {
4574 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
4575 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
4576 if (msg->elements[i].num_values == 0) {
4577 ldb_asprintf_errstring(ldb, __location__
4578 ": objectClass removed on %s, aborting replication\n",
4579 ldb_dn_get_linearized(msg->dn));
4580 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
4585 if (DEBUGLVL(4)) {
4586 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4587 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
4588 talloc_free(s);
4591 ret = ldb_build_mod_req(&change_req,
4592 ldb,
4594 msg,
4595 ar->controls,
4597 callback,
4598 ar->req);
4599 LDB_REQ_SET_LOCATION(change_req);
4600 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4602 /* current partition control needed by "repmd_op_callback" */
4603 ret = ldb_request_add_control(change_req,
4604 DSDB_CONTROL_CURRENT_PARTITION_OID,
4605 false, NULL);
4606 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4608 return ldb_next_request(ar->module, change_req);
4611 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
4612 struct ldb_reply *ares)
4614 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4615 struct replmd_replicated_request);
4616 int ret;
4618 if (!ares) {
4619 return ldb_module_done(ar->req, NULL, NULL,
4620 LDB_ERR_OPERATIONS_ERROR);
4622 if (ares->error != LDB_SUCCESS &&
4623 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4624 return ldb_module_done(ar->req, ares->controls,
4625 ares->response, ares->error);
4628 switch (ares->type) {
4629 case LDB_REPLY_ENTRY:
4630 ar->search_msg = talloc_steal(ar, ares->message);
4631 break;
4633 case LDB_REPLY_REFERRAL:
4634 /* we ignore referrals */
4635 break;
4637 case LDB_REPLY_DONE:
4639 struct replPropertyMetaData1 *md_remote;
4640 struct replPropertyMetaData1 *md_local;
4642 struct replPropertyMetaDataBlob omd;
4643 const struct ldb_val *omd_value;
4644 struct replPropertyMetaDataBlob *rmd;
4645 struct ldb_message *msg;
4647 ar->objs->objects[ar->index_current].last_known_parent = NULL;
4650 * This is the ADD case, find the appropriate parent,
4651 * as this object doesn't exist locally:
4653 if (ar->search_msg == NULL) {
4654 ret = replmd_replicated_apply_search_for_parent(ar);
4655 if (ret != LDB_SUCCESS) {
4656 return ldb_module_done(ar->req, NULL, NULL, ret);
4658 talloc_free(ares);
4659 return LDB_SUCCESS;
4663 * Otherwise, in the MERGE case, work out if we are
4664 * attempting a rename, and if so find the parent the
4665 * newly renamed object wants to belong under (which
4666 * may not be the parent in it's attached string DN
4668 rmd = ar->objs->objects[ar->index_current].meta_data;
4669 ZERO_STRUCT(omd);
4670 omd.version = 1;
4672 /* find existing meta data */
4673 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
4674 if (omd_value) {
4675 enum ndr_err_code ndr_err;
4676 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
4677 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4678 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4679 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4680 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4683 if (omd.version != 1) {
4684 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4689 * now we need to check for double renames. We could have a
4690 * local rename pending which our replication partner hasn't
4691 * received yet. We choose which one wins by looking at the
4692 * attribute stamps on the two objects, the newer one wins
4694 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4695 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4696 /* if there is no name attribute then we have to assume the
4697 object we've received is in fact newer */
4698 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING ||
4699 !md_remote || !md_local ||
4700 replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
4701 ret = replmd_replicated_apply_search_for_parent(ar);
4702 } else {
4703 msg = ar->objs->objects[ar->index_current].msg;
4705 /* Otherwise, just merge on the existing object, force no rename */
4706 DEBUG(4,(__location__ ": Keeping object %s and rejecting older rename to %s\n",
4707 ldb_dn_get_linearized(ar->search_msg->dn),
4708 ldb_dn_get_linearized(msg->dn)));
4711 * This assignment ensures that the strcmp()
4712 * in replmd_replicated_apply_merge() avoids
4713 * the rename call
4715 msg->dn = ar->search_msg->dn;
4716 ret = replmd_replicated_apply_merge(ar);
4718 if (ret != LDB_SUCCESS) {
4719 return ldb_module_done(ar->req, NULL, NULL, ret);
4724 talloc_free(ares);
4725 return LDB_SUCCESS;
4728 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
4730 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
4732 struct ldb_context *ldb;
4733 int ret;
4734 char *tmp_str;
4735 char *filter;
4736 struct ldb_request *search_req;
4738 if (ar->index_current >= ar->objs->num_objects) {
4739 /* done with it, go to next stage */
4740 return replmd_replicated_uptodate_vector(ar);
4743 ldb = ldb_module_get_ctx(ar->module);
4744 ar->search_msg = NULL;
4745 ar->isDeleted = false;
4747 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
4748 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4750 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
4751 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4752 talloc_free(tmp_str);
4754 ret = ldb_build_search_req(&search_req,
4755 ldb,
4757 NULL,
4758 LDB_SCOPE_SUBTREE,
4759 filter,
4760 NULL,
4761 NULL,
4763 replmd_replicated_apply_search_callback,
4764 ar->req);
4765 LDB_REQ_SET_LOCATION(search_req);
4767 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SEARCH_ALL_PARTITIONS|DSDB_SEARCH_SHOW_RECYCLED);
4769 if (ret != LDB_SUCCESS) {
4770 return ret;
4773 return ldb_next_request(ar->module, search_req);
4777 * This is essentially a wrapper for replmd_replicated_apply_next()
4779 * This is needed to ensure that both codepaths call this handler.
4781 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
4783 struct ldb_dn *deleted_objects_dn;
4784 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
4785 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
4786 &deleted_objects_dn);
4787 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
4789 * Do a delete here again, so that if there is
4790 * anything local that conflicts with this
4791 * object being deleted, it is removed. This
4792 * includes links. See MS-DRSR 4.1.10.6.9
4793 * UpdateObject.
4795 * If the object is already deleted, and there
4796 * is no more work required, it doesn't do
4797 * anything.
4800 /* This has been updated to point to the DN we eventually did the modify on */
4802 struct ldb_request *del_req;
4803 struct ldb_result *res;
4805 TALLOC_CTX *tmp_ctx = talloc_new(ar);
4806 if (!tmp_ctx) {
4807 ret = ldb_oom(ldb_module_get_ctx(ar->module));
4808 return ret;
4811 res = talloc_zero(tmp_ctx, struct ldb_result);
4812 if (!res) {
4813 ret = ldb_oom(ldb_module_get_ctx(ar->module));
4814 talloc_free(tmp_ctx);
4815 return ret;
4818 /* Build a delete request, which hopefully will artually turn into nothing */
4819 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
4820 msg->dn,
4821 NULL,
4822 res,
4823 ldb_modify_default_callback,
4824 ar->req);
4825 LDB_REQ_SET_LOCATION(del_req);
4826 if (ret != LDB_SUCCESS) {
4827 talloc_free(tmp_ctx);
4828 return ret;
4832 * This is the guts of the call, call back
4833 * into our delete code, but setting the
4834 * re_delete flag so we delete anything that
4835 * shouldn't be there on a deleted or recycled
4836 * object
4838 ret = replmd_delete_internals(ar->module, del_req, true);
4839 if (ret == LDB_SUCCESS) {
4840 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
4843 talloc_free(tmp_ctx);
4844 if (ret != LDB_SUCCESS) {
4845 return ret;
4849 ar->index_current++;
4850 return replmd_replicated_apply_next(ar);
4853 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
4854 struct ldb_reply *ares)
4856 struct ldb_context *ldb;
4857 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4858 struct replmd_replicated_request);
4859 ldb = ldb_module_get_ctx(ar->module);
4861 if (!ares) {
4862 return ldb_module_done(ar->req, NULL, NULL,
4863 LDB_ERR_OPERATIONS_ERROR);
4865 if (ares->error != LDB_SUCCESS) {
4866 return ldb_module_done(ar->req, ares->controls,
4867 ares->response, ares->error);
4870 if (ares->type != LDB_REPLY_DONE) {
4871 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
4872 return ldb_module_done(ar->req, NULL, NULL,
4873 LDB_ERR_OPERATIONS_ERROR);
4876 talloc_free(ares);
4878 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4881 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
4883 struct ldb_context *ldb;
4884 struct ldb_request *change_req;
4885 enum ndr_err_code ndr_err;
4886 struct ldb_message *msg;
4887 struct replUpToDateVectorBlob ouv;
4888 const struct ldb_val *ouv_value;
4889 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
4890 struct replUpToDateVectorBlob nuv;
4891 struct ldb_val nuv_value;
4892 struct ldb_message_element *nuv_el = NULL;
4893 const struct GUID *our_invocation_id;
4894 struct ldb_message_element *orf_el = NULL;
4895 struct repsFromToBlob nrf;
4896 struct ldb_val *nrf_value = NULL;
4897 struct ldb_message_element *nrf_el = NULL;
4898 unsigned int i;
4899 uint32_t j,ni=0;
4900 bool found = false;
4901 time_t t = time(NULL);
4902 NTTIME now;
4903 int ret;
4904 uint32_t instanceType;
4906 ldb = ldb_module_get_ctx(ar->module);
4907 ruv = ar->objs->uptodateness_vector;
4908 ZERO_STRUCT(ouv);
4909 ouv.version = 2;
4910 ZERO_STRUCT(nuv);
4911 nuv.version = 2;
4913 unix_to_nt_time(&now, t);
4915 if (ar->search_msg == NULL) {
4916 /* this happens for a REPL_OBJ call where we are
4917 creating the target object by replicating it. The
4918 subdomain join code does this for the partition DN
4920 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
4921 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4924 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
4925 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
4926 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
4927 ldb_dn_get_linearized(ar->search_msg->dn)));
4928 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4932 * first create the new replUpToDateVector
4934 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
4935 if (ouv_value) {
4936 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
4937 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
4938 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4939 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4940 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4943 if (ouv.version != 2) {
4944 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4949 * the new uptodateness vector will at least
4950 * contain 1 entry, one for the source_dsa
4952 * plus optional values from our old vector and the one from the source_dsa
4954 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
4955 if (ruv) nuv.ctr.ctr2.count += ruv->count;
4956 nuv.ctr.ctr2.cursors = talloc_array(ar,
4957 struct drsuapi_DsReplicaCursor2,
4958 nuv.ctr.ctr2.count);
4959 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4961 /* first copy the old vector */
4962 for (i=0; i < ouv.ctr.ctr2.count; i++) {
4963 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
4964 ni++;
4967 /* get our invocation_id if we have one already attached to the ldb */
4968 our_invocation_id = samdb_ntds_invocation_id(ldb);
4969 if (our_invocation_id == NULL) {
4970 DEBUG(0, ("repl_meta_data: Could not find our own server's invocationID!\n"));
4971 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4974 /* merge in the source_dsa vector is available */
4975 for (i=0; (ruv && i < ruv->count); i++) {
4976 found = false;
4978 if (our_invocation_id &&
4979 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
4980 our_invocation_id)) {
4981 continue;
4984 for (j=0; j < ni; j++) {
4985 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
4986 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
4987 continue;
4990 found = true;
4992 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
4993 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
4995 break;
4998 if (found) continue;
5000 /* if it's not there yet, add it */
5001 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
5002 ni++;
5006 * finally correct the size of the cursors array
5008 nuv.ctr.ctr2.count = ni;
5011 * sort the cursors
5013 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
5016 * create the change ldb_message
5018 msg = ldb_msg_new(ar);
5019 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5020 msg->dn = ar->search_msg->dn;
5022 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
5023 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
5024 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5025 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5026 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5028 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
5029 if (ret != LDB_SUCCESS) {
5030 return replmd_replicated_request_error(ar, ret);
5032 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
5035 * now create the new repsFrom value from the given repsFromTo1 structure
5037 ZERO_STRUCT(nrf);
5038 nrf.version = 1;
5039 nrf.ctr.ctr1 = *ar->objs->source_dsa;
5040 nrf.ctr.ctr1.last_attempt = now;
5041 nrf.ctr.ctr1.last_success = now;
5042 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
5045 * first see if we already have a repsFrom value for the current source dsa
5046 * if so we'll later replace this value
5048 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
5049 if (orf_el) {
5050 for (i=0; i < orf_el->num_values; i++) {
5051 struct repsFromToBlob *trf;
5053 trf = talloc(ar, struct repsFromToBlob);
5054 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5056 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
5057 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
5058 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5059 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5060 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5063 if (trf->version != 1) {
5064 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5068 * we compare the source dsa objectGUID not the invocation_id
5069 * because we want only one repsFrom value per source dsa
5070 * and when the invocation_id of the source dsa has changed we don't need
5071 * the old repsFrom with the old invocation_id
5073 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
5074 &ar->objs->source_dsa->source_dsa_obj_guid)) {
5075 talloc_free(trf);
5076 continue;
5079 talloc_free(trf);
5080 nrf_value = &orf_el->values[i];
5081 break;
5085 * copy over all old values to the new ldb_message
5087 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
5088 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5089 *nrf_el = *orf_el;
5093 * if we haven't found an old repsFrom value for the current source dsa
5094 * we'll add a new value
5096 if (!nrf_value) {
5097 struct ldb_val zero_value;
5098 ZERO_STRUCT(zero_value);
5099 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
5100 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5102 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
5105 /* we now fill the value which is already attached to ldb_message */
5106 ndr_err = ndr_push_struct_blob(nrf_value, msg,
5107 &nrf,
5108 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
5109 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5110 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5111 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5115 * the ldb_message_element for the attribute, has all the old values and the new one
5116 * so we'll replace the whole attribute with all values
5118 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
5120 if (CHECK_DEBUGLVL(4)) {
5121 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5122 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
5123 talloc_free(s);
5126 /* prepare the ldb_modify() request */
5127 ret = ldb_build_mod_req(&change_req,
5128 ldb,
5130 msg,
5131 ar->controls,
5133 replmd_replicated_uptodate_modify_callback,
5134 ar->req);
5135 LDB_REQ_SET_LOCATION(change_req);
5136 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5138 return ldb_next_request(ar->module, change_req);
5141 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
5142 struct ldb_reply *ares)
5144 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5145 struct replmd_replicated_request);
5146 int ret;
5148 if (!ares) {
5149 return ldb_module_done(ar->req, NULL, NULL,
5150 LDB_ERR_OPERATIONS_ERROR);
5152 if (ares->error != LDB_SUCCESS &&
5153 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5154 return ldb_module_done(ar->req, ares->controls,
5155 ares->response, ares->error);
5158 switch (ares->type) {
5159 case LDB_REPLY_ENTRY:
5160 ar->search_msg = talloc_steal(ar, ares->message);
5161 break;
5163 case LDB_REPLY_REFERRAL:
5164 /* we ignore referrals */
5165 break;
5167 case LDB_REPLY_DONE:
5168 ret = replmd_replicated_uptodate_modify(ar);
5169 if (ret != LDB_SUCCESS) {
5170 return ldb_module_done(ar->req, NULL, NULL, ret);
5174 talloc_free(ares);
5175 return LDB_SUCCESS;
5179 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
5181 struct ldb_context *ldb;
5182 int ret;
5183 static const char *attrs[] = {
5184 "replUpToDateVector",
5185 "repsFrom",
5186 "instanceType",
5187 NULL
5189 struct ldb_request *search_req;
5191 ldb = ldb_module_get_ctx(ar->module);
5192 ar->search_msg = NULL;
5194 ret = ldb_build_search_req(&search_req,
5195 ldb,
5197 ar->objs->partition_dn,
5198 LDB_SCOPE_BASE,
5199 "(objectClass=*)",
5200 attrs,
5201 NULL,
5203 replmd_replicated_uptodate_search_callback,
5204 ar->req);
5205 LDB_REQ_SET_LOCATION(search_req);
5206 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5208 return ldb_next_request(ar->module, search_req);
5213 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
5215 struct ldb_context *ldb;
5216 struct dsdb_extended_replicated_objects *objs;
5217 struct replmd_replicated_request *ar;
5218 struct ldb_control **ctrls;
5219 int ret;
5220 uint32_t i;
5221 struct replmd_private *replmd_private =
5222 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5223 struct dsdb_control_replicated_update *rep_update;
5225 ldb = ldb_module_get_ctx(module);
5227 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
5229 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
5230 if (!objs) {
5231 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
5232 return LDB_ERR_PROTOCOL_ERROR;
5235 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
5236 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
5237 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
5238 return LDB_ERR_PROTOCOL_ERROR;
5241 ar = replmd_ctx_init(module, req);
5242 if (!ar)
5243 return LDB_ERR_OPERATIONS_ERROR;
5245 /* Set the flags to have the replmd_op_callback run over the full set of objects */
5246 ar->apply_mode = true;
5247 ar->objs = objs;
5248 ar->schema = dsdb_get_schema(ldb, ar);
5249 if (!ar->schema) {
5250 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
5251 talloc_free(ar);
5252 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
5253 return LDB_ERR_CONSTRAINT_VIOLATION;
5256 ctrls = req->controls;
5258 if (req->controls) {
5259 req->controls = talloc_memdup(ar, req->controls,
5260 talloc_get_size(req->controls));
5261 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5264 /* This allows layers further down to know if a change came in
5265 over replication and what the replication flags were */
5266 rep_update = talloc_zero(ar, struct dsdb_control_replicated_update);
5267 if (rep_update == NULL) {
5268 return ldb_module_oom(module);
5270 rep_update->dsdb_repl_flags = objs->dsdb_repl_flags;
5272 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, rep_update);
5273 if (ret != LDB_SUCCESS) {
5274 return ret;
5277 /* If this change contained linked attributes in the body
5278 * (rather than in the links section) we need to update
5279 * backlinks in linked_attributes */
5280 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
5281 if (ret != LDB_SUCCESS) {
5282 return ret;
5285 ar->controls = req->controls;
5286 req->controls = ctrls;
5288 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
5290 /* save away the linked attributes for the end of the
5291 transaction */
5292 for (i=0; i<ar->objs->linked_attributes_count; i++) {
5293 struct la_entry *la_entry;
5295 if (replmd_private->la_ctx == NULL) {
5296 replmd_private->la_ctx = talloc_new(replmd_private);
5298 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
5299 if (la_entry == NULL) {
5300 ldb_oom(ldb);
5301 return LDB_ERR_OPERATIONS_ERROR;
5303 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
5304 if (la_entry->la == NULL) {
5305 talloc_free(la_entry);
5306 ldb_oom(ldb);
5307 return LDB_ERR_OPERATIONS_ERROR;
5309 *la_entry->la = ar->objs->linked_attributes[i];
5311 /* we need to steal the non-scalars so they stay
5312 around until the end of the transaction */
5313 talloc_steal(la_entry->la, la_entry->la->identifier);
5314 talloc_steal(la_entry->la, la_entry->la->value.blob);
5316 DLIST_ADD(replmd_private->la_list, la_entry);
5319 return replmd_replicated_apply_next(ar);
5323 process one linked attribute structure
5325 static int replmd_process_linked_attribute(struct ldb_module *module,
5326 struct la_entry *la_entry,
5327 struct ldb_request *parent)
5329 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
5330 struct ldb_context *ldb = ldb_module_get_ctx(module);
5331 struct ldb_message *msg;
5332 struct ldb_message *target_msg = NULL;
5333 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
5334 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
5335 int ret;
5336 const struct dsdb_attribute *attr;
5337 struct dsdb_dn *dsdb_dn;
5338 uint64_t seq_num = 0;
5339 struct ldb_message_element *old_el;
5340 WERROR status;
5341 time_t t = time(NULL);
5342 struct ldb_result *res;
5343 struct ldb_result *target_res;
5344 const char *attrs[4];
5345 const char *attrs2[] = { "isDeleted", "isRecycled", NULL };
5346 struct parsed_dn *pdn_list, *pdn;
5347 struct GUID guid = GUID_zero();
5348 NTSTATUS ntstatus;
5349 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
5350 const struct GUID *our_invocation_id;
5352 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
5353 enum deletion_state target_deletion_state = OBJECT_NOT_DELETED;
5356 linked_attributes[0]:
5357 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
5358 identifier : *
5359 identifier: struct drsuapi_DsReplicaObjectIdentifier
5360 __ndr_size : 0x0000003a (58)
5361 __ndr_size_sid : 0x00000000 (0)
5362 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
5363 sid : S-0-0
5364 __ndr_size_dn : 0x00000000 (0)
5365 dn : ''
5366 attid : DRSUAPI_ATTID_member (0x1F)
5367 value: struct drsuapi_DsAttributeValue
5368 __ndr_size : 0x0000007e (126)
5369 blob : *
5370 blob : DATA_BLOB length=126
5371 flags : 0x00000001 (1)
5372 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
5373 originating_add_time : Wed Sep 2 22:20:01 2009 EST
5374 meta_data: struct drsuapi_DsReplicaMetaData
5375 version : 0x00000015 (21)
5376 originating_change_time : Wed Sep 2 23:39:07 2009 EST
5377 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
5378 originating_usn : 0x000000000001e19c (123292)
5380 (for cases where the link is to a normal DN)
5381 &target: struct drsuapi_DsReplicaObjectIdentifier3
5382 __ndr_size : 0x0000007e (126)
5383 __ndr_size_sid : 0x0000001c (28)
5384 guid : 7639e594-db75-4086-b0d4-67890ae46031
5385 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
5386 __ndr_size_dn : 0x00000022 (34)
5387 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
5390 /* find the attribute being modified */
5391 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
5392 if (attr == NULL) {
5393 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
5394 talloc_free(tmp_ctx);
5395 return LDB_ERR_OPERATIONS_ERROR;
5398 attrs[0] = attr->lDAPDisplayName;
5399 attrs[1] = "isDeleted";
5400 attrs[1] = "isRecycled";
5401 attrs[2] = NULL;
5403 /* get the existing message from the db for the object with
5404 this GUID, returning attribute being modified. We will then
5405 use this msg as the basis for a modify call */
5406 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
5407 DSDB_FLAG_NEXT_MODULE |
5408 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5409 DSDB_SEARCH_SHOW_RECYCLED |
5410 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
5411 DSDB_SEARCH_REVEAL_INTERNALS,
5412 parent,
5413 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
5414 if (ret != LDB_SUCCESS) {
5415 talloc_free(tmp_ctx);
5416 return ret;
5418 if (res->count != 1) {
5419 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
5420 GUID_string(tmp_ctx, &la->identifier->guid));
5421 talloc_free(tmp_ctx);
5422 return LDB_ERR_NO_SUCH_OBJECT;
5424 msg = res->msgs[0];
5427 * Check for deleted objects per MS-DRSR 4.1.10.6.13
5428 * ProcessLinkValue, because link updates are not applied to
5429 * recycled and tombstone objects. We don't have to delete
5430 * any existing link, that should have happened when the
5431 * object deletion was replicated or initiated.
5434 replmd_deletion_state(module, msg, &deletion_state, NULL);
5436 if (deletion_state >= OBJECT_RECYCLED) {
5437 talloc_free(tmp_ctx);
5438 return LDB_SUCCESS;
5441 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
5442 if (old_el == NULL) {
5443 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
5444 if (ret != LDB_SUCCESS) {
5445 ldb_module_oom(module);
5446 talloc_free(tmp_ctx);
5447 return LDB_ERR_OPERATIONS_ERROR;
5449 } else {
5450 old_el->flags = LDB_FLAG_MOD_REPLACE;
5453 /* parse the existing links */
5454 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
5455 if (ret != LDB_SUCCESS) {
5456 talloc_free(tmp_ctx);
5457 return ret;
5460 /* get our invocationId */
5461 our_invocation_id = samdb_ntds_invocation_id(ldb);
5462 if (!our_invocation_id) {
5463 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
5464 talloc_free(tmp_ctx);
5465 return LDB_ERR_OPERATIONS_ERROR;
5468 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
5469 if (ret != LDB_SUCCESS) {
5470 talloc_free(tmp_ctx);
5471 return ret;
5474 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
5475 if (!W_ERROR_IS_OK(status)) {
5476 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
5477 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
5478 talloc_free(tmp_ctx);
5479 return LDB_ERR_OPERATIONS_ERROR;
5482 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
5483 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
5485 * This strange behaviour (allowing a NULL/missing
5486 * GUID) originally comes from:
5488 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
5489 * Author: Andrew Tridgell <tridge@samba.org>
5490 * Date: Mon Dec 21 21:21:55 2009 +1100
5492 * s4-drs: cope better with NULL GUIDS from DRS
5494 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
5495 * need to match by DN if possible when seeing if we should update an
5496 * existing link.
5498 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
5501 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
5502 dsdb_dn->dn, attrs2,
5503 DSDB_FLAG_NEXT_MODULE |
5504 DSDB_SEARCH_SHOW_RECYCLED |
5505 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5506 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
5507 parent);
5508 } else if (!NT_STATUS_IS_OK(ntstatus)) {
5509 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
5510 old_el->name,
5511 ldb_dn_get_linearized(dsdb_dn->dn),
5512 ldb_dn_get_linearized(msg->dn));
5513 talloc_free(tmp_ctx);
5514 return LDB_ERR_OPERATIONS_ERROR;
5515 } else {
5516 ret = dsdb_module_search(module, tmp_ctx, &target_res,
5517 NULL, LDB_SCOPE_SUBTREE,
5518 attrs2,
5519 DSDB_FLAG_NEXT_MODULE |
5520 DSDB_SEARCH_SHOW_RECYCLED |
5521 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5522 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
5523 parent,
5524 "objectGUID=%s",
5525 GUID_string(tmp_ctx, &guid));
5528 if (ret != LDB_SUCCESS) {
5529 ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to re-resolve GUID %s: %s\n",
5530 GUID_string(tmp_ctx, &guid),
5531 ldb_errstring(ldb_module_get_ctx(module)));
5532 talloc_free(tmp_ctx);
5533 return ret;
5536 if (target_res->count == 0) {
5537 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
5538 GUID_string(tmp_ctx, &guid),
5539 ldb_dn_get_linearized(dsdb_dn->dn)));
5540 } else if (target_res->count != 1) {
5541 ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching objectGUID %s\n",
5542 GUID_string(tmp_ctx, &guid));
5543 talloc_free(tmp_ctx);
5544 return LDB_ERR_OPERATIONS_ERROR;
5545 } else {
5546 target_msg = target_res->msgs[0];
5547 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
5551 * Check for deleted objects per MS-DRSR 4.1.10.6.13
5552 * ProcessLinkValue, because link updates are not applied to
5553 * recycled and tombstone objects. We don't have to delete
5554 * any existing link, that should have happened when the
5555 * object deletion was replicated or initiated.
5557 replmd_deletion_state(module, target_msg,
5558 &target_deletion_state, NULL);
5560 if (target_deletion_state >= OBJECT_RECYCLED) {
5561 talloc_free(tmp_ctx);
5562 return LDB_SUCCESS;
5565 /* see if this link already exists */
5566 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
5567 if (pdn != NULL) {
5568 /* see if this update is newer than what we have already */
5569 struct GUID invocation_id = GUID_zero();
5570 uint32_t version = 0;
5571 uint32_t originating_usn = 0;
5572 NTTIME change_time = 0;
5573 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
5575 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
5576 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
5577 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
5578 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
5580 if (!replmd_update_is_newer(&invocation_id,
5581 &la->meta_data.originating_invocation_id,
5582 version,
5583 la->meta_data.version,
5584 change_time,
5585 la->meta_data.originating_change_time)) {
5586 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
5587 old_el->name, ldb_dn_get_linearized(msg->dn),
5588 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
5589 talloc_free(tmp_ctx);
5590 return LDB_SUCCESS;
5593 /* get a seq_num for this change */
5594 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
5595 if (ret != LDB_SUCCESS) {
5596 talloc_free(tmp_ctx);
5597 return ret;
5600 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
5601 /* remove the existing backlink */
5602 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
5603 if (ret != LDB_SUCCESS) {
5604 talloc_free(tmp_ctx);
5605 return ret;
5609 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
5610 &la->meta_data.originating_invocation_id,
5611 la->meta_data.originating_usn, seq_num,
5612 la->meta_data.originating_change_time,
5613 la->meta_data.version,
5614 !active);
5615 if (ret != LDB_SUCCESS) {
5616 talloc_free(tmp_ctx);
5617 return ret;
5620 if (active) {
5621 /* add the new backlink */
5622 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
5623 if (ret != LDB_SUCCESS) {
5624 talloc_free(tmp_ctx);
5625 return ret;
5628 } else {
5629 /* get a seq_num for this change */
5630 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
5631 if (ret != LDB_SUCCESS) {
5632 talloc_free(tmp_ctx);
5633 return ret;
5636 old_el->values = talloc_realloc(msg->elements, old_el->values,
5637 struct ldb_val, old_el->num_values+1);
5638 if (!old_el->values) {
5639 ldb_module_oom(module);
5640 return LDB_ERR_OPERATIONS_ERROR;
5642 old_el->num_values++;
5644 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
5645 &la->meta_data.originating_invocation_id,
5646 la->meta_data.originating_usn, seq_num,
5647 la->meta_data.originating_change_time,
5648 la->meta_data.version,
5649 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
5650 if (ret != LDB_SUCCESS) {
5651 talloc_free(tmp_ctx);
5652 return ret;
5655 if (active) {
5656 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
5657 true, attr, false);
5658 if (ret != LDB_SUCCESS) {
5659 talloc_free(tmp_ctx);
5660 return ret;
5665 /* we only change whenChanged and uSNChanged if the seq_num
5666 has changed */
5667 ret = add_time_element(msg, "whenChanged", t);
5668 if (ret != LDB_SUCCESS) {
5669 talloc_free(tmp_ctx);
5670 ldb_operr(ldb);
5671 return ret;
5674 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
5675 if (ret != LDB_SUCCESS) {
5676 talloc_free(tmp_ctx);
5677 ldb_operr(ldb);
5678 return ret;
5681 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
5682 if (old_el == NULL) {
5683 talloc_free(tmp_ctx);
5684 return ldb_operr(ldb);
5687 ret = dsdb_check_single_valued_link(attr, old_el);
5688 if (ret != LDB_SUCCESS) {
5689 talloc_free(tmp_ctx);
5690 return ret;
5693 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
5695 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
5696 if (ret != LDB_SUCCESS) {
5697 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
5698 ldb_errstring(ldb),
5699 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
5700 talloc_free(tmp_ctx);
5701 return ret;
5704 talloc_free(tmp_ctx);
5706 return ret;
5709 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
5711 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
5712 return replmd_extended_replicated_objects(module, req);
5715 return ldb_next_request(module, req);
5720 we hook into the transaction operations to allow us to
5721 perform the linked attribute updates at the end of the whole
5722 transaction. This allows a forward linked attribute to be created
5723 before the object is created. During a vampire, w2k8 sends us linked
5724 attributes before the objects they are part of.
5726 static int replmd_start_transaction(struct ldb_module *module)
5728 /* create our private structure for this transaction */
5729 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
5730 struct replmd_private);
5731 replmd_txn_cleanup(replmd_private);
5733 /* free any leftover mod_usn records from cancelled
5734 transactions */
5735 while (replmd_private->ncs) {
5736 struct nc_entry *e = replmd_private->ncs;
5737 DLIST_REMOVE(replmd_private->ncs, e);
5738 talloc_free(e);
5741 return ldb_next_start_trans(module);
5745 on prepare commit we loop over our queued la_context structures and
5746 apply each of them
5748 static int replmd_prepare_commit(struct ldb_module *module)
5750 struct replmd_private *replmd_private =
5751 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5752 struct la_entry *la, *prev;
5753 struct la_backlink *bl;
5754 int ret;
5756 /* walk the list backwards, to do the first entry first, as we
5757 * added the entries with DLIST_ADD() which puts them at the
5758 * start of the list */
5759 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
5760 prev = DLIST_PREV(la);
5761 DLIST_REMOVE(replmd_private->la_list, la);
5762 ret = replmd_process_linked_attribute(module, la, NULL);
5763 if (ret != LDB_SUCCESS) {
5764 replmd_txn_cleanup(replmd_private);
5765 return ret;
5769 /* process our backlink list, creating and deleting backlinks
5770 as necessary */
5771 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
5772 ret = replmd_process_backlink(module, bl, NULL);
5773 if (ret != LDB_SUCCESS) {
5774 replmd_txn_cleanup(replmd_private);
5775 return ret;
5779 replmd_txn_cleanup(replmd_private);
5781 /* possibly change @REPLCHANGED */
5782 ret = replmd_notify_store(module, NULL);
5783 if (ret != LDB_SUCCESS) {
5784 return ret;
5787 return ldb_next_prepare_commit(module);
5790 static int replmd_del_transaction(struct ldb_module *module)
5792 struct replmd_private *replmd_private =
5793 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5794 replmd_txn_cleanup(replmd_private);
5796 return ldb_next_del_trans(module);
5800 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
5801 .name = "repl_meta_data",
5802 .init_context = replmd_init,
5803 .add = replmd_add,
5804 .modify = replmd_modify,
5805 .rename = replmd_rename,
5806 .del = replmd_delete,
5807 .extended = replmd_extended,
5808 .start_transaction = replmd_start_transaction,
5809 .prepare_commit = replmd_prepare_commit,
5810 .del_transaction = replmd_del_transaction,
5813 int ldb_repl_meta_data_module_init(const char *version)
5815 LDB_MODULE_CHECK_VERSION(version);
5816 return ldb_register_module(&ldb_repl_meta_data_module_ops);