dsdb: Further assert that we always have an objectClass and an rDN
[Samba.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blob05ef176ba498f86f6795e2ec2fc724a9c129d420
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_OPERATIONS_ERROR;
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) {
1527 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
1528 __location__ ": objectClass missing on %s\n",
1529 ldb_dn_get_linearized(msg->dn));
1530 return LDB_ERR_OPERATIONS_ERROR;
1534 * Now check if this objectClass means we need to do urgent replication
1536 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1537 situation)) {
1538 *is_urgent = true;
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 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
3941 el->name));
3942 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
3943 msg->num_elements--;
3944 i--;
3945 continue;
3949 if (DEBUGLVL(4)) {
3950 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
3951 DEBUG(4, ("DRS replication add message:\n%s\n", s));
3952 talloc_free(s);
3955 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
3956 "isDeleted", false);
3959 * the meta data array is already sorted by the caller
3962 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3963 if (rdn_name == NULL) {
3964 ldb_asprintf_errstring(ldb, __location__ ": No rDN for %s?\n", ldb_dn_get_linearized(msg->dn));
3965 return replmd_replicated_request_error(ar, LDB_ERR_INVALID_DN_SYNTAX);
3968 rdn_sa = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3969 if (rdn_sa == NULL) {
3970 ldb_asprintf_errstring(ldb, ": No schema attribute found for rDN %s for %s\n",
3971 rdn_name, ldb_dn_get_linearized(msg->dn));
3972 return replmd_replicated_request_error(ar, LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE);
3975 ret = replmd_replPropertyMetaDataCtr1_verify(ldb, &md->ctr.ctr1, rdn_sa, msg->dn);
3976 if (ret != LDB_SUCCESS) {
3977 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
3978 return replmd_replicated_request_error(ar, ret);
3981 for (i=0; i < md->ctr.ctr1.count; i++) {
3982 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
3984 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
3985 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
3986 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3987 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3988 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3990 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
3991 if (ret != LDB_SUCCESS) {
3992 return replmd_replicated_request_error(ar, ret);
3995 replmd_ldb_message_sort(msg, ar->schema);
3997 if (!remote_isDeleted) {
3998 ret = dsdb_module_schedule_sd_propagation(ar->module,
3999 ar->objs->partition_dn,
4000 msg->dn, true);
4001 if (ret != LDB_SUCCESS) {
4002 return replmd_replicated_request_error(ar, ret);
4006 ar->isDeleted = remote_isDeleted;
4008 ret = ldb_build_add_req(&change_req,
4009 ldb,
4011 msg,
4012 ar->controls,
4014 replmd_op_add_callback,
4015 ar->req);
4016 LDB_REQ_SET_LOCATION(change_req);
4017 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4019 /* current partition control needed by "repmd_op_callback" */
4020 ret = ldb_request_add_control(change_req,
4021 DSDB_CONTROL_CURRENT_PARTITION_OID,
4022 false, NULL);
4023 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4025 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4026 /* this tells the partition module to make it a
4027 partial replica if creating an NC */
4028 ret = ldb_request_add_control(change_req,
4029 DSDB_CONTROL_PARTIAL_REPLICA,
4030 false, NULL);
4031 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4034 return ldb_next_request(ar->module, change_req);
4037 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
4038 struct ldb_reply *ares)
4040 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4041 struct replmd_replicated_request);
4042 int ret;
4044 if (!ares) {
4045 return ldb_module_done(ar->req, NULL, NULL,
4046 LDB_ERR_OPERATIONS_ERROR);
4048 if (ares->error != LDB_SUCCESS &&
4049 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4051 * TODO: deal with the above error that the parent object doesn't exist
4054 return ldb_module_done(ar->req, ares->controls,
4055 ares->response, ares->error);
4058 switch (ares->type) {
4059 case LDB_REPLY_ENTRY:
4061 struct ldb_message *parent_msg = ares->message;
4062 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
4063 struct ldb_dn *parent_dn;
4064 int comp_num;
4066 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
4067 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
4068 /* Per MS-DRSR 4.1.10.6.10
4069 * FindBestParentObject we need to move this
4070 * new object under a deleted object to
4071 * lost-and-found */
4072 struct ldb_dn *nc_root;
4074 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
4075 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4076 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4077 "No suitable NC root found for %s. "
4078 "We need to move this object because parent object %s "
4079 "is deleted, but this object is not.",
4080 ldb_dn_get_linearized(msg->dn),
4081 ldb_dn_get_linearized(parent_msg->dn));
4082 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4083 } else if (ret != LDB_SUCCESS) {
4084 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4085 "Unable to find NC root for %s: %s. "
4086 "We need to move this object because parent object %s "
4087 "is deleted, but this object is not.",
4088 ldb_dn_get_linearized(msg->dn),
4089 ldb_errstring(ldb_module_get_ctx(ar->module)),
4090 ldb_dn_get_linearized(parent_msg->dn));
4091 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4094 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
4095 nc_root,
4096 DS_GUID_LOSTANDFOUND_CONTAINER,
4097 &parent_dn);
4098 if (ret != LDB_SUCCESS) {
4099 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4100 "Unable to find LostAndFound Container for %s "
4101 "in partition %s: %s. "
4102 "We need to move this object because parent object %s "
4103 "is deleted, but this object is not.",
4104 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
4105 ldb_errstring(ldb_module_get_ctx(ar->module)),
4106 ldb_dn_get_linearized(parent_msg->dn));
4107 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4109 ar->objs->objects[ar->index_current].last_known_parent
4110 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4111 } else {
4112 parent_dn = parent_msg->dn;
4115 comp_num = ldb_dn_get_comp_num(msg->dn);
4116 if (comp_num > 1) {
4117 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
4118 talloc_free(ares);
4119 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4122 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
4123 talloc_free(ares);
4124 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4126 break;
4128 case LDB_REPLY_REFERRAL:
4129 /* we ignore referrals */
4130 break;
4132 case LDB_REPLY_DONE:
4133 if (ar->search_msg != NULL) {
4134 ret = replmd_replicated_apply_merge(ar);
4135 } else {
4136 ret = replmd_replicated_apply_add(ar);
4138 if (ret != LDB_SUCCESS) {
4139 return ldb_module_done(ar->req, NULL, NULL, ret);
4143 talloc_free(ares);
4144 return LDB_SUCCESS;
4148 * Look for the parent object, so we put the new object in the right
4149 * place This is akin to NameObject in MS-DRSR - this routine and the
4150 * callbacks find the right parent name, and correct name for this
4151 * object
4154 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
4156 struct ldb_context *ldb;
4157 int ret;
4158 char *tmp_str;
4159 char *filter;
4160 struct ldb_request *search_req;
4161 static const char *attrs[] = {"isDeleted", NULL};
4163 ldb = ldb_module_get_ctx(ar->module);
4165 if (!ar->objs->objects[ar->index_current].parent_guid_value.data) {
4166 if (ar->search_msg != NULL) {
4167 return replmd_replicated_apply_merge(ar);
4168 } else {
4169 return replmd_replicated_apply_add(ar);
4173 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].parent_guid_value);
4174 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4176 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
4177 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4178 talloc_free(tmp_str);
4180 ret = ldb_build_search_req(&search_req,
4181 ldb,
4183 ar->objs->partition_dn,
4184 LDB_SCOPE_SUBTREE,
4185 filter,
4186 attrs,
4187 NULL,
4189 replmd_replicated_apply_search_for_parent_callback,
4190 ar->req);
4191 LDB_REQ_SET_LOCATION(search_req);
4193 ret = dsdb_request_add_controls(search_req,
4194 DSDB_SEARCH_SHOW_RECYCLED|
4195 DSDB_SEARCH_SHOW_DELETED|
4196 DSDB_SEARCH_SHOW_EXTENDED_DN);
4197 if (ret != LDB_SUCCESS) {
4198 return ret;
4201 return ldb_next_request(ar->module, search_req);
4205 handle renames that come in over DRS replication
4207 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
4208 struct ldb_message *msg,
4209 struct ldb_request *parent)
4211 struct ldb_request *req;
4212 int ret;
4213 TALLOC_CTX *tmp_ctx = talloc_new(msg);
4214 struct ldb_result *res;
4216 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
4217 ldb_dn_get_linearized(ar->search_msg->dn),
4218 ldb_dn_get_linearized(msg->dn)));
4221 res = talloc_zero(tmp_ctx, struct ldb_result);
4222 if (!res) {
4223 talloc_free(tmp_ctx);
4224 return ldb_oom(ldb_module_get_ctx(ar->module));
4227 /* pass rename to the next module
4228 * so it doesn't appear as an originating update */
4229 ret = ldb_build_rename_req(&req, ldb_module_get_ctx(ar->module), tmp_ctx,
4230 ar->search_msg->dn, msg->dn,
4231 NULL,
4233 replmd_op_rename_callback,
4234 parent);
4235 LDB_REQ_SET_LOCATION(req);
4236 if (ret != LDB_SUCCESS) {
4237 talloc_free(tmp_ctx);
4238 return ret;
4241 ret = dsdb_request_add_controls(req, DSDB_MODIFY_RELAX);
4242 if (ret != LDB_SUCCESS) {
4243 talloc_free(tmp_ctx);
4244 return ret;
4247 ret = ldb_next_request(ar->module, req);
4249 if (ret == LDB_SUCCESS) {
4250 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
4253 talloc_free(tmp_ctx);
4254 return ret;
4258 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
4260 struct ldb_context *ldb;
4261 struct ldb_request *change_req;
4262 enum ndr_err_code ndr_err;
4263 struct ldb_message *msg;
4264 struct replPropertyMetaDataBlob *rmd;
4265 struct replPropertyMetaDataBlob omd;
4266 const struct ldb_val *omd_value;
4267 struct replPropertyMetaDataBlob nmd;
4268 struct ldb_val nmd_value;
4269 unsigned int i;
4270 uint32_t j,ni=0;
4271 unsigned int removed_attrs = 0;
4272 int ret;
4273 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
4274 bool isDeleted = false;
4275 bool local_isDeleted = false;
4276 bool remote_isDeleted = false;
4277 bool take_remote_isDeleted = false;
4278 bool sd_updated = false;
4279 bool renamed = false;
4281 ldb = ldb_module_get_ctx(ar->module);
4282 msg = ar->objs->objects[ar->index_current].msg;
4284 rmd = ar->objs->objects[ar->index_current].meta_data;
4285 ZERO_STRUCT(omd);
4286 omd.version = 1;
4288 /* find existing meta data */
4289 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
4290 if (omd_value) {
4291 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
4292 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4293 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4294 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4295 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4298 if (omd.version != 1) {
4299 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4303 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
4304 "isDeleted", false);
4305 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4306 "isDeleted", false);
4308 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0) {
4309 ret = LDB_SUCCESS;
4310 } else {
4312 * handle renames, even just by case that come in over
4313 * DRS. Changes in the parent DN don't hit us here,
4314 * because the search for a parent will clean up those
4315 * components.
4317 * We also have already filtered out the case where
4318 * the peer has an older name to what we have (see
4319 * replmd_replicated_apply_search_callback())
4321 renamed = true;
4322 ret = replmd_replicated_handle_rename(ar, msg, ar->req);
4326 * This particular error code means that we already tried the
4327 * conflict algrorithm, and the existing record name was newer, so we
4328 * need to rename the incoming record
4330 if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
4331 struct GUID guid;
4332 NTSTATUS status;
4333 struct ldb_dn *new_dn;
4334 status = GUID_from_ndr_blob(&ar->objs->objects[ar->index_current].guid_value, &guid);
4335 /* This really, really can't fail */
4336 SMB_ASSERT(NT_STATUS_IS_OK(status));
4338 new_dn = replmd_conflict_dn(msg, msg->dn, &guid);
4339 if (new_dn == NULL) {
4340 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4341 "Failed to form conflict DN for %s\n",
4342 ldb_dn_get_linearized(msg->dn));
4344 return replmd_replicated_request_werror(ar, WERR_NOMEM);
4347 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
4348 DSDB_FLAG_NEXT_MODULE, ar->req);
4349 if (ret != LDB_SUCCESS) {
4350 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4351 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
4352 ldb_dn_get_linearized(msg->dn),
4353 ldb_dn_get_linearized(ar->search_msg->dn),
4354 ldb_dn_get_linearized(new_dn),
4355 ldb_errstring(ldb_module_get_ctx(ar->module)));
4356 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4359 /* Set the callback to one that will fix up the name to be a conflict DN */
4360 callback = replmd_op_name_modify_callback;
4361 msg->dn = new_dn;
4362 renamed = true;
4363 } else if (ret != LDB_SUCCESS) {
4364 ldb_debug(ldb, LDB_DEBUG_FATAL,
4365 "replmd_replicated_request rename %s => %s failed - %s\n",
4366 ldb_dn_get_linearized(ar->search_msg->dn),
4367 ldb_dn_get_linearized(msg->dn),
4368 ldb_errstring(ldb));
4369 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4372 ZERO_STRUCT(nmd);
4373 nmd.version = 1;
4374 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
4375 nmd.ctr.ctr1.array = talloc_array(ar,
4376 struct replPropertyMetaData1,
4377 nmd.ctr.ctr1.count);
4378 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4380 /* first copy the old meta data */
4381 for (i=0; i < omd.ctr.ctr1.count; i++) {
4382 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
4383 ni++;
4386 ar->seq_num = 0;
4387 /* now merge in the new meta data */
4388 for (i=0; i < rmd->ctr.ctr1.count; i++) {
4389 bool found = false;
4391 for (j=0; j < ni; j++) {
4392 bool cmp;
4394 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
4395 continue;
4398 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4399 /* if we compare equal then do an
4400 update. This is used when a client
4401 asks for a FULL_SYNC, and can be
4402 used to recover a corrupt
4403 replica */
4404 cmp = !replmd_replPropertyMetaData1_is_newer(&rmd->ctr.ctr1.array[i],
4405 &nmd.ctr.ctr1.array[j]);
4406 } else {
4407 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
4408 &rmd->ctr.ctr1.array[i]);
4410 if (cmp) {
4411 /* replace the entry */
4412 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
4413 if (ar->seq_num == 0) {
4414 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4415 if (ret != LDB_SUCCESS) {
4416 return replmd_replicated_request_error(ar, ret);
4419 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
4420 switch (nmd.ctr.ctr1.array[j].attid) {
4421 case DRSUAPI_ATTID_ntSecurityDescriptor:
4422 sd_updated = true;
4423 break;
4424 case DRSUAPI_ATTID_isDeleted:
4425 take_remote_isDeleted = true;
4426 break;
4427 default:
4428 break;
4430 found = true;
4431 break;
4434 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
4435 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
4436 msg->elements[i-removed_attrs].name,
4437 ldb_dn_get_linearized(msg->dn),
4438 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
4441 /* we don't want to apply this change so remove the attribute */
4442 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
4443 removed_attrs++;
4445 found = true;
4446 break;
4449 if (found) continue;
4451 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
4452 if (ar->seq_num == 0) {
4453 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4454 if (ret != LDB_SUCCESS) {
4455 return replmd_replicated_request_error(ar, ret);
4458 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
4459 switch (nmd.ctr.ctr1.array[ni].attid) {
4460 case DRSUAPI_ATTID_ntSecurityDescriptor:
4461 sd_updated = true;
4462 break;
4463 case DRSUAPI_ATTID_isDeleted:
4464 take_remote_isDeleted = true;
4465 break;
4466 default:
4467 break;
4469 ni++;
4473 * finally correct the size of the meta_data array
4475 nmd.ctr.ctr1.count = ni;
4478 * the rdn attribute (the alias for the name attribute),
4479 * 'cn' for most objects is the last entry in the meta data array
4480 * we have stored
4482 * sort the new meta data array
4484 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, ar->schema, msg->dn);
4485 if (ret != LDB_SUCCESS) {
4486 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
4487 return ret;
4491 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
4492 * UpdateObject.
4494 * This also controls SD propagation below
4496 if (take_remote_isDeleted) {
4497 isDeleted = remote_isDeleted;
4498 } else {
4499 isDeleted = local_isDeleted;
4502 ar->isDeleted = isDeleted;
4505 * check if some replicated attributes left, otherwise skip the ldb_modify() call
4507 if (msg->num_elements == 0) {
4508 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
4509 ar->index_current);
4511 return replmd_replicated_apply_isDeleted(ar);
4514 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
4515 ar->index_current, msg->num_elements);
4517 if (renamed) {
4518 sd_updated = true;
4521 if (sd_updated && !isDeleted) {
4522 ret = dsdb_module_schedule_sd_propagation(ar->module,
4523 ar->objs->partition_dn,
4524 msg->dn, true);
4525 if (ret != LDB_SUCCESS) {
4526 return ldb_operr(ldb);
4530 /* create the meta data value */
4531 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
4532 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
4533 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4534 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4535 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4539 * when we know that we'll modify the record, add the whenChanged, uSNChanged
4540 * and replPopertyMetaData attributes
4542 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4543 if (ret != LDB_SUCCESS) {
4544 return replmd_replicated_request_error(ar, ret);
4546 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4547 if (ret != LDB_SUCCESS) {
4548 return replmd_replicated_request_error(ar, ret);
4550 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
4551 if (ret != LDB_SUCCESS) {
4552 return replmd_replicated_request_error(ar, ret);
4555 replmd_ldb_message_sort(msg, ar->schema);
4557 /* we want to replace the old values */
4558 for (i=0; i < msg->num_elements; i++) {
4559 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
4560 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
4561 if (msg->elements[i].num_values == 0) {
4562 ldb_asprintf_errstring(ldb, __location__
4563 ": objectClass removed on %s, aborting replication\n",
4564 ldb_dn_get_linearized(msg->dn));
4565 return replmd_replicated_request_error(ar, LDB_ERR_OPERATIONS_ERROR);
4570 if (DEBUGLVL(4)) {
4571 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4572 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
4573 talloc_free(s);
4576 ret = ldb_build_mod_req(&change_req,
4577 ldb,
4579 msg,
4580 ar->controls,
4582 callback,
4583 ar->req);
4584 LDB_REQ_SET_LOCATION(change_req);
4585 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4587 /* current partition control needed by "repmd_op_callback" */
4588 ret = ldb_request_add_control(change_req,
4589 DSDB_CONTROL_CURRENT_PARTITION_OID,
4590 false, NULL);
4591 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4593 return ldb_next_request(ar->module, change_req);
4596 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
4597 struct ldb_reply *ares)
4599 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4600 struct replmd_replicated_request);
4601 int ret;
4603 if (!ares) {
4604 return ldb_module_done(ar->req, NULL, NULL,
4605 LDB_ERR_OPERATIONS_ERROR);
4607 if (ares->error != LDB_SUCCESS &&
4608 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4609 return ldb_module_done(ar->req, ares->controls,
4610 ares->response, ares->error);
4613 switch (ares->type) {
4614 case LDB_REPLY_ENTRY:
4615 ar->search_msg = talloc_steal(ar, ares->message);
4616 break;
4618 case LDB_REPLY_REFERRAL:
4619 /* we ignore referrals */
4620 break;
4622 case LDB_REPLY_DONE:
4624 struct replPropertyMetaData1 *md_remote;
4625 struct replPropertyMetaData1 *md_local;
4627 struct replPropertyMetaDataBlob omd;
4628 const struct ldb_val *omd_value;
4629 struct replPropertyMetaDataBlob *rmd;
4630 struct ldb_message *msg;
4632 ar->objs->objects[ar->index_current].last_known_parent = NULL;
4635 * This is the ADD case, find the appropriate parent,
4636 * as this object doesn't exist locally:
4638 if (ar->search_msg == NULL) {
4639 ret = replmd_replicated_apply_search_for_parent(ar);
4640 if (ret != LDB_SUCCESS) {
4641 return ldb_module_done(ar->req, NULL, NULL, ret);
4643 talloc_free(ares);
4644 return LDB_SUCCESS;
4648 * Otherwise, in the MERGE case, work out if we are
4649 * attempting a rename, and if so find the parent the
4650 * newly renamed object wants to belong under (which
4651 * may not be the parent in it's attached string DN
4653 rmd = ar->objs->objects[ar->index_current].meta_data;
4654 ZERO_STRUCT(omd);
4655 omd.version = 1;
4657 /* find existing meta data */
4658 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
4659 if (omd_value) {
4660 enum ndr_err_code ndr_err;
4661 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
4662 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4663 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4664 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4665 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4668 if (omd.version != 1) {
4669 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4674 * now we need to check for double renames. We could have a
4675 * local rename pending which our replication partner hasn't
4676 * received yet. We choose which one wins by looking at the
4677 * attribute stamps on the two objects, the newer one wins
4679 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4680 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4681 /* if there is no name attribute then we have to assume the
4682 object we've received is in fact newer */
4683 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING ||
4684 !md_remote || !md_local ||
4685 replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
4686 ret = replmd_replicated_apply_search_for_parent(ar);
4687 } else {
4688 msg = ar->objs->objects[ar->index_current].msg;
4690 /* Otherwise, just merge on the existing object, force no rename */
4691 DEBUG(4,(__location__ ": Keeping object %s and rejecting older rename to %s\n",
4692 ldb_dn_get_linearized(ar->search_msg->dn),
4693 ldb_dn_get_linearized(msg->dn)));
4696 * This assignment ensures that the strcmp()
4697 * in replmd_replicated_apply_merge() avoids
4698 * the rename call
4700 msg->dn = ar->search_msg->dn;
4701 ret = replmd_replicated_apply_merge(ar);
4703 if (ret != LDB_SUCCESS) {
4704 return ldb_module_done(ar->req, NULL, NULL, ret);
4709 talloc_free(ares);
4710 return LDB_SUCCESS;
4713 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
4715 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
4717 struct ldb_context *ldb;
4718 int ret;
4719 char *tmp_str;
4720 char *filter;
4721 struct ldb_request *search_req;
4723 if (ar->index_current >= ar->objs->num_objects) {
4724 /* done with it, go to next stage */
4725 return replmd_replicated_uptodate_vector(ar);
4728 ldb = ldb_module_get_ctx(ar->module);
4729 ar->search_msg = NULL;
4730 ar->isDeleted = false;
4732 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
4733 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4735 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
4736 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4737 talloc_free(tmp_str);
4739 ret = ldb_build_search_req(&search_req,
4740 ldb,
4742 NULL,
4743 LDB_SCOPE_SUBTREE,
4744 filter,
4745 NULL,
4746 NULL,
4748 replmd_replicated_apply_search_callback,
4749 ar->req);
4750 LDB_REQ_SET_LOCATION(search_req);
4752 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SEARCH_ALL_PARTITIONS|DSDB_SEARCH_SHOW_RECYCLED);
4754 if (ret != LDB_SUCCESS) {
4755 return ret;
4758 return ldb_next_request(ar->module, search_req);
4762 * This is essentially a wrapper for replmd_replicated_apply_next()
4764 * This is needed to ensure that both codepaths call this handler.
4766 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
4768 struct ldb_dn *deleted_objects_dn;
4769 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
4770 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
4771 &deleted_objects_dn);
4772 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
4774 * Do a delete here again, so that if there is
4775 * anything local that conflicts with this
4776 * object being deleted, it is removed. This
4777 * includes links. See MS-DRSR 4.1.10.6.9
4778 * UpdateObject.
4780 * If the object is already deleted, and there
4781 * is no more work required, it doesn't do
4782 * anything.
4785 /* This has been updated to point to the DN we eventually did the modify on */
4787 struct ldb_request *del_req;
4788 struct ldb_result *res;
4790 TALLOC_CTX *tmp_ctx = talloc_new(ar);
4791 if (!tmp_ctx) {
4792 ret = ldb_oom(ldb_module_get_ctx(ar->module));
4793 return ret;
4796 res = talloc_zero(tmp_ctx, struct ldb_result);
4797 if (!res) {
4798 ret = ldb_oom(ldb_module_get_ctx(ar->module));
4799 talloc_free(tmp_ctx);
4800 return ret;
4803 /* Build a delete request, which hopefully will artually turn into nothing */
4804 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
4805 msg->dn,
4806 NULL,
4807 res,
4808 ldb_modify_default_callback,
4809 ar->req);
4810 LDB_REQ_SET_LOCATION(del_req);
4811 if (ret != LDB_SUCCESS) {
4812 talloc_free(tmp_ctx);
4813 return ret;
4817 * This is the guts of the call, call back
4818 * into our delete code, but setting the
4819 * re_delete flag so we delete anything that
4820 * shouldn't be there on a deleted or recycled
4821 * object
4823 ret = replmd_delete_internals(ar->module, del_req, true);
4824 if (ret == LDB_SUCCESS) {
4825 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
4828 talloc_free(tmp_ctx);
4829 if (ret != LDB_SUCCESS) {
4830 return ret;
4834 ar->index_current++;
4835 return replmd_replicated_apply_next(ar);
4838 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
4839 struct ldb_reply *ares)
4841 struct ldb_context *ldb;
4842 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4843 struct replmd_replicated_request);
4844 ldb = ldb_module_get_ctx(ar->module);
4846 if (!ares) {
4847 return ldb_module_done(ar->req, NULL, NULL,
4848 LDB_ERR_OPERATIONS_ERROR);
4850 if (ares->error != LDB_SUCCESS) {
4851 return ldb_module_done(ar->req, ares->controls,
4852 ares->response, ares->error);
4855 if (ares->type != LDB_REPLY_DONE) {
4856 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
4857 return ldb_module_done(ar->req, NULL, NULL,
4858 LDB_ERR_OPERATIONS_ERROR);
4861 talloc_free(ares);
4863 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4866 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
4868 struct ldb_context *ldb;
4869 struct ldb_request *change_req;
4870 enum ndr_err_code ndr_err;
4871 struct ldb_message *msg;
4872 struct replUpToDateVectorBlob ouv;
4873 const struct ldb_val *ouv_value;
4874 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
4875 struct replUpToDateVectorBlob nuv;
4876 struct ldb_val nuv_value;
4877 struct ldb_message_element *nuv_el = NULL;
4878 const struct GUID *our_invocation_id;
4879 struct ldb_message_element *orf_el = NULL;
4880 struct repsFromToBlob nrf;
4881 struct ldb_val *nrf_value = NULL;
4882 struct ldb_message_element *nrf_el = NULL;
4883 unsigned int i;
4884 uint32_t j,ni=0;
4885 bool found = false;
4886 time_t t = time(NULL);
4887 NTTIME now;
4888 int ret;
4889 uint32_t instanceType;
4891 ldb = ldb_module_get_ctx(ar->module);
4892 ruv = ar->objs->uptodateness_vector;
4893 ZERO_STRUCT(ouv);
4894 ouv.version = 2;
4895 ZERO_STRUCT(nuv);
4896 nuv.version = 2;
4898 unix_to_nt_time(&now, t);
4900 if (ar->search_msg == NULL) {
4901 /* this happens for a REPL_OBJ call where we are
4902 creating the target object by replicating it. The
4903 subdomain join code does this for the partition DN
4905 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
4906 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4909 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
4910 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
4911 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
4912 ldb_dn_get_linearized(ar->search_msg->dn)));
4913 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4917 * first create the new replUpToDateVector
4919 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
4920 if (ouv_value) {
4921 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
4922 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
4923 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4924 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4925 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4928 if (ouv.version != 2) {
4929 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4934 * the new uptodateness vector will at least
4935 * contain 1 entry, one for the source_dsa
4937 * plus optional values from our old vector and the one from the source_dsa
4939 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
4940 if (ruv) nuv.ctr.ctr2.count += ruv->count;
4941 nuv.ctr.ctr2.cursors = talloc_array(ar,
4942 struct drsuapi_DsReplicaCursor2,
4943 nuv.ctr.ctr2.count);
4944 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4946 /* first copy the old vector */
4947 for (i=0; i < ouv.ctr.ctr2.count; i++) {
4948 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
4949 ni++;
4952 /* get our invocation_id if we have one already attached to the ldb */
4953 our_invocation_id = samdb_ntds_invocation_id(ldb);
4954 if (our_invocation_id == NULL) {
4955 DEBUG(0, ("repl_meta_data: Could not find our own server's invocationID!\n"));
4956 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4959 /* merge in the source_dsa vector is available */
4960 for (i=0; (ruv && i < ruv->count); i++) {
4961 found = false;
4963 if (our_invocation_id &&
4964 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
4965 our_invocation_id)) {
4966 continue;
4969 for (j=0; j < ni; j++) {
4970 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
4971 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
4972 continue;
4975 found = true;
4977 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
4978 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
4980 break;
4983 if (found) continue;
4985 /* if it's not there yet, add it */
4986 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
4987 ni++;
4991 * finally correct the size of the cursors array
4993 nuv.ctr.ctr2.count = ni;
4996 * sort the cursors
4998 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
5001 * create the change ldb_message
5003 msg = ldb_msg_new(ar);
5004 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5005 msg->dn = ar->search_msg->dn;
5007 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
5008 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
5009 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5010 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5011 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5013 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
5014 if (ret != LDB_SUCCESS) {
5015 return replmd_replicated_request_error(ar, ret);
5017 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
5020 * now create the new repsFrom value from the given repsFromTo1 structure
5022 ZERO_STRUCT(nrf);
5023 nrf.version = 1;
5024 nrf.ctr.ctr1 = *ar->objs->source_dsa;
5025 nrf.ctr.ctr1.last_attempt = now;
5026 nrf.ctr.ctr1.last_success = now;
5027 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
5030 * first see if we already have a repsFrom value for the current source dsa
5031 * if so we'll later replace this value
5033 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
5034 if (orf_el) {
5035 for (i=0; i < orf_el->num_values; i++) {
5036 struct repsFromToBlob *trf;
5038 trf = talloc(ar, struct repsFromToBlob);
5039 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5041 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
5042 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
5043 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5044 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5045 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5048 if (trf->version != 1) {
5049 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5053 * we compare the source dsa objectGUID not the invocation_id
5054 * because we want only one repsFrom value per source dsa
5055 * and when the invocation_id of the source dsa has changed we don't need
5056 * the old repsFrom with the old invocation_id
5058 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
5059 &ar->objs->source_dsa->source_dsa_obj_guid)) {
5060 talloc_free(trf);
5061 continue;
5064 talloc_free(trf);
5065 nrf_value = &orf_el->values[i];
5066 break;
5070 * copy over all old values to the new ldb_message
5072 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
5073 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5074 *nrf_el = *orf_el;
5078 * if we haven't found an old repsFrom value for the current source dsa
5079 * we'll add a new value
5081 if (!nrf_value) {
5082 struct ldb_val zero_value;
5083 ZERO_STRUCT(zero_value);
5084 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
5085 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5087 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
5090 /* we now fill the value which is already attached to ldb_message */
5091 ndr_err = ndr_push_struct_blob(nrf_value, msg,
5092 &nrf,
5093 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
5094 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5095 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5096 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5100 * the ldb_message_element for the attribute, has all the old values and the new one
5101 * so we'll replace the whole attribute with all values
5103 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
5105 if (CHECK_DEBUGLVL(4)) {
5106 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5107 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
5108 talloc_free(s);
5111 /* prepare the ldb_modify() request */
5112 ret = ldb_build_mod_req(&change_req,
5113 ldb,
5115 msg,
5116 ar->controls,
5118 replmd_replicated_uptodate_modify_callback,
5119 ar->req);
5120 LDB_REQ_SET_LOCATION(change_req);
5121 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5123 return ldb_next_request(ar->module, change_req);
5126 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
5127 struct ldb_reply *ares)
5129 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5130 struct replmd_replicated_request);
5131 int ret;
5133 if (!ares) {
5134 return ldb_module_done(ar->req, NULL, NULL,
5135 LDB_ERR_OPERATIONS_ERROR);
5137 if (ares->error != LDB_SUCCESS &&
5138 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5139 return ldb_module_done(ar->req, ares->controls,
5140 ares->response, ares->error);
5143 switch (ares->type) {
5144 case LDB_REPLY_ENTRY:
5145 ar->search_msg = talloc_steal(ar, ares->message);
5146 break;
5148 case LDB_REPLY_REFERRAL:
5149 /* we ignore referrals */
5150 break;
5152 case LDB_REPLY_DONE:
5153 ret = replmd_replicated_uptodate_modify(ar);
5154 if (ret != LDB_SUCCESS) {
5155 return ldb_module_done(ar->req, NULL, NULL, ret);
5159 talloc_free(ares);
5160 return LDB_SUCCESS;
5164 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
5166 struct ldb_context *ldb;
5167 int ret;
5168 static const char *attrs[] = {
5169 "replUpToDateVector",
5170 "repsFrom",
5171 "instanceType",
5172 NULL
5174 struct ldb_request *search_req;
5176 ldb = ldb_module_get_ctx(ar->module);
5177 ar->search_msg = NULL;
5179 ret = ldb_build_search_req(&search_req,
5180 ldb,
5182 ar->objs->partition_dn,
5183 LDB_SCOPE_BASE,
5184 "(objectClass=*)",
5185 attrs,
5186 NULL,
5188 replmd_replicated_uptodate_search_callback,
5189 ar->req);
5190 LDB_REQ_SET_LOCATION(search_req);
5191 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5193 return ldb_next_request(ar->module, search_req);
5198 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
5200 struct ldb_context *ldb;
5201 struct dsdb_extended_replicated_objects *objs;
5202 struct replmd_replicated_request *ar;
5203 struct ldb_control **ctrls;
5204 int ret;
5205 uint32_t i;
5206 struct replmd_private *replmd_private =
5207 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5208 struct dsdb_control_replicated_update *rep_update;
5210 ldb = ldb_module_get_ctx(module);
5212 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
5214 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
5215 if (!objs) {
5216 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
5217 return LDB_ERR_PROTOCOL_ERROR;
5220 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
5221 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
5222 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
5223 return LDB_ERR_PROTOCOL_ERROR;
5226 ar = replmd_ctx_init(module, req);
5227 if (!ar)
5228 return LDB_ERR_OPERATIONS_ERROR;
5230 /* Set the flags to have the replmd_op_callback run over the full set of objects */
5231 ar->apply_mode = true;
5232 ar->objs = objs;
5233 ar->schema = dsdb_get_schema(ldb, ar);
5234 if (!ar->schema) {
5235 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
5236 talloc_free(ar);
5237 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
5238 return LDB_ERR_CONSTRAINT_VIOLATION;
5241 ctrls = req->controls;
5243 if (req->controls) {
5244 req->controls = talloc_memdup(ar, req->controls,
5245 talloc_get_size(req->controls));
5246 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5249 /* This allows layers further down to know if a change came in
5250 over replication and what the replication flags were */
5251 rep_update = talloc_zero(ar, struct dsdb_control_replicated_update);
5252 if (rep_update == NULL) {
5253 return ldb_module_oom(module);
5255 rep_update->dsdb_repl_flags = objs->dsdb_repl_flags;
5257 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, rep_update);
5258 if (ret != LDB_SUCCESS) {
5259 return ret;
5262 /* If this change contained linked attributes in the body
5263 * (rather than in the links section) we need to update
5264 * backlinks in linked_attributes */
5265 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
5266 if (ret != LDB_SUCCESS) {
5267 return ret;
5270 ar->controls = req->controls;
5271 req->controls = ctrls;
5273 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
5275 /* save away the linked attributes for the end of the
5276 transaction */
5277 for (i=0; i<ar->objs->linked_attributes_count; i++) {
5278 struct la_entry *la_entry;
5280 if (replmd_private->la_ctx == NULL) {
5281 replmd_private->la_ctx = talloc_new(replmd_private);
5283 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
5284 if (la_entry == NULL) {
5285 ldb_oom(ldb);
5286 return LDB_ERR_OPERATIONS_ERROR;
5288 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
5289 if (la_entry->la == NULL) {
5290 talloc_free(la_entry);
5291 ldb_oom(ldb);
5292 return LDB_ERR_OPERATIONS_ERROR;
5294 *la_entry->la = ar->objs->linked_attributes[i];
5296 /* we need to steal the non-scalars so they stay
5297 around until the end of the transaction */
5298 talloc_steal(la_entry->la, la_entry->la->identifier);
5299 talloc_steal(la_entry->la, la_entry->la->value.blob);
5301 DLIST_ADD(replmd_private->la_list, la_entry);
5304 return replmd_replicated_apply_next(ar);
5308 process one linked attribute structure
5310 static int replmd_process_linked_attribute(struct ldb_module *module,
5311 struct la_entry *la_entry,
5312 struct ldb_request *parent)
5314 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
5315 struct ldb_context *ldb = ldb_module_get_ctx(module);
5316 struct ldb_message *msg;
5317 struct ldb_message *target_msg = NULL;
5318 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
5319 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
5320 int ret;
5321 const struct dsdb_attribute *attr;
5322 struct dsdb_dn *dsdb_dn;
5323 uint64_t seq_num = 0;
5324 struct ldb_message_element *old_el;
5325 WERROR status;
5326 time_t t = time(NULL);
5327 struct ldb_result *res;
5328 struct ldb_result *target_res;
5329 const char *attrs[4];
5330 const char *attrs2[] = { "isDeleted", "isRecycled", NULL };
5331 struct parsed_dn *pdn_list, *pdn;
5332 struct GUID guid = GUID_zero();
5333 NTSTATUS ntstatus;
5334 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
5335 const struct GUID *our_invocation_id;
5337 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
5338 enum deletion_state target_deletion_state = OBJECT_NOT_DELETED;
5341 linked_attributes[0]:
5342 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
5343 identifier : *
5344 identifier: struct drsuapi_DsReplicaObjectIdentifier
5345 __ndr_size : 0x0000003a (58)
5346 __ndr_size_sid : 0x00000000 (0)
5347 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
5348 sid : S-0-0
5349 __ndr_size_dn : 0x00000000 (0)
5350 dn : ''
5351 attid : DRSUAPI_ATTID_member (0x1F)
5352 value: struct drsuapi_DsAttributeValue
5353 __ndr_size : 0x0000007e (126)
5354 blob : *
5355 blob : DATA_BLOB length=126
5356 flags : 0x00000001 (1)
5357 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
5358 originating_add_time : Wed Sep 2 22:20:01 2009 EST
5359 meta_data: struct drsuapi_DsReplicaMetaData
5360 version : 0x00000015 (21)
5361 originating_change_time : Wed Sep 2 23:39:07 2009 EST
5362 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
5363 originating_usn : 0x000000000001e19c (123292)
5365 (for cases where the link is to a normal DN)
5366 &target: struct drsuapi_DsReplicaObjectIdentifier3
5367 __ndr_size : 0x0000007e (126)
5368 __ndr_size_sid : 0x0000001c (28)
5369 guid : 7639e594-db75-4086-b0d4-67890ae46031
5370 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
5371 __ndr_size_dn : 0x00000022 (34)
5372 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
5375 /* find the attribute being modified */
5376 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
5377 if (attr == NULL) {
5378 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
5379 talloc_free(tmp_ctx);
5380 return LDB_ERR_OPERATIONS_ERROR;
5383 attrs[0] = attr->lDAPDisplayName;
5384 attrs[1] = "isDeleted";
5385 attrs[1] = "isRecycled";
5386 attrs[2] = NULL;
5388 /* get the existing message from the db for the object with
5389 this GUID, returning attribute being modified. We will then
5390 use this msg as the basis for a modify call */
5391 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
5392 DSDB_FLAG_NEXT_MODULE |
5393 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5394 DSDB_SEARCH_SHOW_RECYCLED |
5395 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
5396 DSDB_SEARCH_REVEAL_INTERNALS,
5397 parent,
5398 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
5399 if (ret != LDB_SUCCESS) {
5400 talloc_free(tmp_ctx);
5401 return ret;
5403 if (res->count != 1) {
5404 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
5405 GUID_string(tmp_ctx, &la->identifier->guid));
5406 talloc_free(tmp_ctx);
5407 return LDB_ERR_NO_SUCH_OBJECT;
5409 msg = res->msgs[0];
5412 * Check for deleted objects per MS-DRSR 4.1.10.6.13
5413 * ProcessLinkValue, because link updates are not applied to
5414 * recycled and tombstone objects. We don't have to delete
5415 * any existing link, that should have happened when the
5416 * object deletion was replicated or initiated.
5419 replmd_deletion_state(module, msg, &deletion_state, NULL);
5421 if (deletion_state >= OBJECT_RECYCLED) {
5422 talloc_free(tmp_ctx);
5423 return LDB_SUCCESS;
5426 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
5427 if (old_el == NULL) {
5428 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
5429 if (ret != LDB_SUCCESS) {
5430 ldb_module_oom(module);
5431 talloc_free(tmp_ctx);
5432 return LDB_ERR_OPERATIONS_ERROR;
5434 } else {
5435 old_el->flags = LDB_FLAG_MOD_REPLACE;
5438 /* parse the existing links */
5439 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
5440 if (ret != LDB_SUCCESS) {
5441 talloc_free(tmp_ctx);
5442 return ret;
5445 /* get our invocationId */
5446 our_invocation_id = samdb_ntds_invocation_id(ldb);
5447 if (!our_invocation_id) {
5448 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
5449 talloc_free(tmp_ctx);
5450 return LDB_ERR_OPERATIONS_ERROR;
5453 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
5454 if (ret != LDB_SUCCESS) {
5455 talloc_free(tmp_ctx);
5456 return ret;
5459 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
5460 if (!W_ERROR_IS_OK(status)) {
5461 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
5462 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
5463 talloc_free(tmp_ctx);
5464 return LDB_ERR_OPERATIONS_ERROR;
5467 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
5468 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
5470 * This strange behaviour (allowing a NULL/missing
5471 * GUID) originally comes from:
5473 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
5474 * Author: Andrew Tridgell <tridge@samba.org>
5475 * Date: Mon Dec 21 21:21:55 2009 +1100
5477 * s4-drs: cope better with NULL GUIDS from DRS
5479 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
5480 * need to match by DN if possible when seeing if we should update an
5481 * existing link.
5483 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
5486 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
5487 dsdb_dn->dn, attrs2,
5488 DSDB_FLAG_NEXT_MODULE |
5489 DSDB_SEARCH_SHOW_RECYCLED |
5490 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5491 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
5492 parent);
5493 } else if (!NT_STATUS_IS_OK(ntstatus)) {
5494 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
5495 old_el->name,
5496 ldb_dn_get_linearized(dsdb_dn->dn),
5497 ldb_dn_get_linearized(msg->dn));
5498 talloc_free(tmp_ctx);
5499 return LDB_ERR_OPERATIONS_ERROR;
5500 } else {
5501 ret = dsdb_module_search(module, tmp_ctx, &target_res,
5502 NULL, LDB_SCOPE_SUBTREE,
5503 attrs2,
5504 DSDB_FLAG_NEXT_MODULE |
5505 DSDB_SEARCH_SHOW_RECYCLED |
5506 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5507 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
5508 parent,
5509 "objectGUID=%s",
5510 GUID_string(tmp_ctx, &guid));
5513 if (ret != LDB_SUCCESS) {
5514 ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to re-resolve GUID %s: %s\n",
5515 GUID_string(tmp_ctx, &guid),
5516 ldb_errstring(ldb_module_get_ctx(module)));
5517 talloc_free(tmp_ctx);
5518 return ret;
5521 if (target_res->count == 0) {
5522 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
5523 GUID_string(tmp_ctx, &guid),
5524 ldb_dn_get_linearized(dsdb_dn->dn)));
5525 } else if (target_res->count != 1) {
5526 ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching objectGUID %s\n",
5527 GUID_string(tmp_ctx, &guid));
5528 talloc_free(tmp_ctx);
5529 return LDB_ERR_OPERATIONS_ERROR;
5530 } else {
5531 target_msg = target_res->msgs[0];
5532 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
5536 * Check for deleted objects per MS-DRSR 4.1.10.6.13
5537 * ProcessLinkValue, because link updates are not applied to
5538 * recycled and tombstone objects. We don't have to delete
5539 * any existing link, that should have happened when the
5540 * object deletion was replicated or initiated.
5542 replmd_deletion_state(module, target_msg,
5543 &target_deletion_state, NULL);
5545 if (target_deletion_state >= OBJECT_RECYCLED) {
5546 talloc_free(tmp_ctx);
5547 return LDB_SUCCESS;
5550 /* see if this link already exists */
5551 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
5552 if (pdn != NULL) {
5553 /* see if this update is newer than what we have already */
5554 struct GUID invocation_id = GUID_zero();
5555 uint32_t version = 0;
5556 uint32_t originating_usn = 0;
5557 NTTIME change_time = 0;
5558 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
5560 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
5561 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
5562 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
5563 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
5565 if (!replmd_update_is_newer(&invocation_id,
5566 &la->meta_data.originating_invocation_id,
5567 version,
5568 la->meta_data.version,
5569 change_time,
5570 la->meta_data.originating_change_time)) {
5571 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
5572 old_el->name, ldb_dn_get_linearized(msg->dn),
5573 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
5574 talloc_free(tmp_ctx);
5575 return LDB_SUCCESS;
5578 /* get a seq_num for this change */
5579 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
5580 if (ret != LDB_SUCCESS) {
5581 talloc_free(tmp_ctx);
5582 return ret;
5585 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
5586 /* remove the existing backlink */
5587 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
5588 if (ret != LDB_SUCCESS) {
5589 talloc_free(tmp_ctx);
5590 return ret;
5594 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
5595 &la->meta_data.originating_invocation_id,
5596 la->meta_data.originating_usn, seq_num,
5597 la->meta_data.originating_change_time,
5598 la->meta_data.version,
5599 !active);
5600 if (ret != LDB_SUCCESS) {
5601 talloc_free(tmp_ctx);
5602 return ret;
5605 if (active) {
5606 /* add the new backlink */
5607 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
5608 if (ret != LDB_SUCCESS) {
5609 talloc_free(tmp_ctx);
5610 return ret;
5613 } else {
5614 /* get a seq_num for this change */
5615 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
5616 if (ret != LDB_SUCCESS) {
5617 talloc_free(tmp_ctx);
5618 return ret;
5621 old_el->values = talloc_realloc(msg->elements, old_el->values,
5622 struct ldb_val, old_el->num_values+1);
5623 if (!old_el->values) {
5624 ldb_module_oom(module);
5625 return LDB_ERR_OPERATIONS_ERROR;
5627 old_el->num_values++;
5629 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
5630 &la->meta_data.originating_invocation_id,
5631 la->meta_data.originating_usn, seq_num,
5632 la->meta_data.originating_change_time,
5633 la->meta_data.version,
5634 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
5635 if (ret != LDB_SUCCESS) {
5636 talloc_free(tmp_ctx);
5637 return ret;
5640 if (active) {
5641 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
5642 true, attr, false);
5643 if (ret != LDB_SUCCESS) {
5644 talloc_free(tmp_ctx);
5645 return ret;
5650 /* we only change whenChanged and uSNChanged if the seq_num
5651 has changed */
5652 ret = add_time_element(msg, "whenChanged", t);
5653 if (ret != LDB_SUCCESS) {
5654 talloc_free(tmp_ctx);
5655 ldb_operr(ldb);
5656 return ret;
5659 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
5660 if (ret != LDB_SUCCESS) {
5661 talloc_free(tmp_ctx);
5662 ldb_operr(ldb);
5663 return ret;
5666 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
5667 if (old_el == NULL) {
5668 talloc_free(tmp_ctx);
5669 return ldb_operr(ldb);
5672 ret = dsdb_check_single_valued_link(attr, old_el);
5673 if (ret != LDB_SUCCESS) {
5674 talloc_free(tmp_ctx);
5675 return ret;
5678 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
5680 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
5681 if (ret != LDB_SUCCESS) {
5682 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
5683 ldb_errstring(ldb),
5684 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
5685 talloc_free(tmp_ctx);
5686 return ret;
5689 talloc_free(tmp_ctx);
5691 return ret;
5694 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
5696 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
5697 return replmd_extended_replicated_objects(module, req);
5700 return ldb_next_request(module, req);
5705 we hook into the transaction operations to allow us to
5706 perform the linked attribute updates at the end of the whole
5707 transaction. This allows a forward linked attribute to be created
5708 before the object is created. During a vampire, w2k8 sends us linked
5709 attributes before the objects they are part of.
5711 static int replmd_start_transaction(struct ldb_module *module)
5713 /* create our private structure for this transaction */
5714 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
5715 struct replmd_private);
5716 replmd_txn_cleanup(replmd_private);
5718 /* free any leftover mod_usn records from cancelled
5719 transactions */
5720 while (replmd_private->ncs) {
5721 struct nc_entry *e = replmd_private->ncs;
5722 DLIST_REMOVE(replmd_private->ncs, e);
5723 talloc_free(e);
5726 return ldb_next_start_trans(module);
5730 on prepare commit we loop over our queued la_context structures and
5731 apply each of them
5733 static int replmd_prepare_commit(struct ldb_module *module)
5735 struct replmd_private *replmd_private =
5736 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5737 struct la_entry *la, *prev;
5738 struct la_backlink *bl;
5739 int ret;
5741 /* walk the list backwards, to do the first entry first, as we
5742 * added the entries with DLIST_ADD() which puts them at the
5743 * start of the list */
5744 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
5745 prev = DLIST_PREV(la);
5746 DLIST_REMOVE(replmd_private->la_list, la);
5747 ret = replmd_process_linked_attribute(module, la, NULL);
5748 if (ret != LDB_SUCCESS) {
5749 replmd_txn_cleanup(replmd_private);
5750 return ret;
5754 /* process our backlink list, creating and deleting backlinks
5755 as necessary */
5756 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
5757 ret = replmd_process_backlink(module, bl, NULL);
5758 if (ret != LDB_SUCCESS) {
5759 replmd_txn_cleanup(replmd_private);
5760 return ret;
5764 replmd_txn_cleanup(replmd_private);
5766 /* possibly change @REPLCHANGED */
5767 ret = replmd_notify_store(module, NULL);
5768 if (ret != LDB_SUCCESS) {
5769 return ret;
5772 return ldb_next_prepare_commit(module);
5775 static int replmd_del_transaction(struct ldb_module *module)
5777 struct replmd_private *replmd_private =
5778 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5779 replmd_txn_cleanup(replmd_private);
5781 return ldb_next_del_trans(module);
5785 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
5786 .name = "repl_meta_data",
5787 .init_context = replmd_init,
5788 .add = replmd_add,
5789 .modify = replmd_modify,
5790 .rename = replmd_rename,
5791 .del = replmd_delete,
5792 .extended = replmd_extended,
5793 .start_transaction = replmd_start_transaction,
5794 .prepare_commit = replmd_prepare_commit,
5795 .del_transaction = replmd_del_transaction,
5798 int ldb_repl_meta_data_module_init(const char *version)
5800 LDB_MODULE_CHECK_VERSION(version);
5801 return ldb_register_module(&ldb_repl_meta_data_module_ops);