s4:dsdb/repl_meta_data: make sure objectGUID can't be deleted
[Samba.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blob4c5ced4230902cec08439ea6b8256bf842d69624
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;
70 struct ldb_dn *schema_dn;
73 struct la_entry {
74 struct la_entry *next, *prev;
75 struct drsuapi_DsReplicaLinkedAttribute *la;
78 struct replmd_replicated_request {
79 struct ldb_module *module;
80 struct ldb_request *req;
82 const struct dsdb_schema *schema;
84 /* the controls we pass down */
85 struct ldb_control **controls;
87 /* details for the mode where we apply a bunch of inbound replication meessages */
88 bool apply_mode;
89 uint32_t index_current;
90 struct dsdb_extended_replicated_objects *objs;
92 struct ldb_message *search_msg;
94 uint64_t seq_num;
95 bool is_urgent;
97 bool isDeleted;
100 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
101 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
103 enum urgent_situation {
104 REPL_URGENT_ON_CREATE = 1,
105 REPL_URGENT_ON_UPDATE = 2,
106 REPL_URGENT_ON_DELETE = 4
109 enum deletion_state {
110 OBJECT_NOT_DELETED=1,
111 OBJECT_DELETED=2,
112 OBJECT_RECYCLED=3,
113 OBJECT_TOMBSTONE=4,
114 OBJECT_REMOVED=5
117 static void replmd_deletion_state(struct ldb_module *module,
118 const struct ldb_message *msg,
119 enum deletion_state *current_state,
120 enum deletion_state *next_state)
122 int ret;
123 bool enabled = false;
125 if (msg == NULL) {
126 *current_state = OBJECT_REMOVED;
127 if (next_state != NULL) {
128 *next_state = OBJECT_REMOVED;
130 return;
133 ret = dsdb_recyclebin_enabled(module, &enabled);
134 if (ret != LDB_SUCCESS) {
135 enabled = false;
138 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
139 if (!enabled) {
140 *current_state = OBJECT_TOMBSTONE;
141 if (next_state != NULL) {
142 *next_state = OBJECT_REMOVED;
144 return;
147 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
148 *current_state = OBJECT_RECYCLED;
149 if (next_state != NULL) {
150 *next_state = OBJECT_REMOVED;
152 return;
155 *current_state = OBJECT_DELETED;
156 if (next_state != NULL) {
157 *next_state = OBJECT_RECYCLED;
159 return;
162 *current_state = OBJECT_NOT_DELETED;
163 if (next_state == NULL) {
164 return;
167 if (enabled) {
168 *next_state = OBJECT_DELETED;
169 } else {
170 *next_state = OBJECT_TOMBSTONE;
174 static const struct {
175 const char *update_name;
176 enum urgent_situation repl_situation;
177 } urgent_objects[] = {
178 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
179 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
180 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
181 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
182 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
183 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
184 {NULL, 0}
187 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
188 static const char *urgent_attrs[] = {
189 "lockoutTime",
190 "pwdLastSet",
191 "userAccountControl",
192 NULL
196 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
197 enum urgent_situation situation)
199 unsigned int i, j;
200 for (i=0; urgent_objects[i].update_name; i++) {
202 if ((situation & urgent_objects[i].repl_situation) == 0) {
203 continue;
206 for (j=0; j<objectclass_el->num_values; j++) {
207 const struct ldb_val *v = &objectclass_el->values[j];
208 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
209 return true;
213 return false;
216 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
218 if (ldb_attr_in_list(urgent_attrs, el->name)) {
219 return true;
221 return false;
225 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
228 initialise the module
229 allocate the private structure and build the list
230 of partition DNs for use by replmd_notify()
232 static int replmd_init(struct ldb_module *module)
234 struct replmd_private *replmd_private;
235 struct ldb_context *ldb = ldb_module_get_ctx(module);
237 replmd_private = talloc_zero(module, struct replmd_private);
238 if (replmd_private == NULL) {
239 ldb_oom(ldb);
240 return LDB_ERR_OPERATIONS_ERROR;
242 ldb_module_set_private(module, replmd_private);
244 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
246 return ldb_next_init(module);
250 cleanup our per-transaction contexts
252 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
254 talloc_free(replmd_private->la_ctx);
255 replmd_private->la_list = NULL;
256 replmd_private->la_ctx = NULL;
258 talloc_free(replmd_private->bl_ctx);
259 replmd_private->la_backlinks = NULL;
260 replmd_private->bl_ctx = NULL;
264 struct la_backlink {
265 struct la_backlink *next, *prev;
266 const char *attr_name;
267 struct GUID forward_guid, target_guid;
268 bool active;
272 process a backlinks we accumulated during a transaction, adding and
273 deleting the backlinks from the target objects
275 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
277 struct ldb_dn *target_dn, *source_dn;
278 int ret;
279 struct ldb_context *ldb = ldb_module_get_ctx(module);
280 struct ldb_message *msg;
281 TALLOC_CTX *tmp_ctx = talloc_new(bl);
282 char *dn_string;
285 - find DN of target
286 - find DN of source
287 - construct ldb_message
288 - either an add or a delete
290 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn, parent);
291 if (ret != LDB_SUCCESS) {
292 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
293 GUID_string(bl, &bl->target_guid)));
294 return LDB_SUCCESS;
297 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn, parent);
298 if (ret != LDB_SUCCESS) {
299 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
300 GUID_string(bl, &bl->forward_guid));
301 talloc_free(tmp_ctx);
302 return ret;
305 msg = ldb_msg_new(tmp_ctx);
306 if (msg == NULL) {
307 ldb_module_oom(module);
308 talloc_free(tmp_ctx);
309 return LDB_ERR_OPERATIONS_ERROR;
312 /* construct a ldb_message for adding/deleting the backlink */
313 msg->dn = target_dn;
314 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
315 if (!dn_string) {
316 ldb_module_oom(module);
317 talloc_free(tmp_ctx);
318 return LDB_ERR_OPERATIONS_ERROR;
320 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
321 if (ret != LDB_SUCCESS) {
322 talloc_free(tmp_ctx);
323 return ret;
325 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
327 /* a backlink should never be single valued. Unfortunately the
328 exchange schema has a attribute
329 msExchBridgeheadedLocalConnectorsDNBL which is single
330 valued and a backlink. We need to cope with that by
331 ignoring the single value flag */
332 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
334 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
335 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
336 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
337 cope with possible corruption where the backlink has
338 already been removed */
339 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
340 ldb_dn_get_linearized(target_dn),
341 ldb_dn_get_linearized(source_dn),
342 ldb_errstring(ldb)));
343 ret = LDB_SUCCESS;
344 } else if (ret != LDB_SUCCESS) {
345 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
346 bl->active?"add":"remove",
347 ldb_dn_get_linearized(source_dn),
348 ldb_dn_get_linearized(target_dn),
349 ldb_errstring(ldb));
350 talloc_free(tmp_ctx);
351 return ret;
353 talloc_free(tmp_ctx);
354 return ret;
358 add a backlink to the list of backlinks to add/delete in the prepare
359 commit
361 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
362 struct GUID *forward_guid, struct GUID *target_guid,
363 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
365 const struct dsdb_attribute *target_attr;
366 struct la_backlink *bl;
367 struct replmd_private *replmd_private =
368 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
370 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
371 if (!target_attr) {
373 * windows 2003 has a broken schema where the
374 * definition of msDS-IsDomainFor is missing (which is
375 * supposed to be the backlink of the
376 * msDS-HasDomainNCs attribute
378 return LDB_SUCCESS;
381 /* see if its already in the list */
382 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
383 if (GUID_equal(forward_guid, &bl->forward_guid) &&
384 GUID_equal(target_guid, &bl->target_guid) &&
385 (target_attr->lDAPDisplayName == bl->attr_name ||
386 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
387 break;
391 if (bl) {
392 /* we found an existing one */
393 if (bl->active == active) {
394 return LDB_SUCCESS;
396 DLIST_REMOVE(replmd_private->la_backlinks, bl);
397 talloc_free(bl);
398 return LDB_SUCCESS;
401 if (replmd_private->bl_ctx == NULL) {
402 replmd_private->bl_ctx = talloc_new(replmd_private);
403 if (replmd_private->bl_ctx == NULL) {
404 ldb_module_oom(module);
405 return LDB_ERR_OPERATIONS_ERROR;
409 /* its a new one */
410 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
411 if (bl == NULL) {
412 ldb_module_oom(module);
413 return LDB_ERR_OPERATIONS_ERROR;
416 /* Ensure the schema does not go away before the bl->attr_name is used */
417 if (!talloc_reference(bl, schema)) {
418 talloc_free(bl);
419 ldb_module_oom(module);
420 return LDB_ERR_OPERATIONS_ERROR;
423 bl->attr_name = target_attr->lDAPDisplayName;
424 bl->forward_guid = *forward_guid;
425 bl->target_guid = *target_guid;
426 bl->active = active;
428 /* the caller may ask for this backlink to be processed
429 immediately */
430 if (immediate) {
431 int ret = replmd_process_backlink(module, bl, NULL);
432 talloc_free(bl);
433 return ret;
436 DLIST_ADD(replmd_private->la_backlinks, bl);
438 return LDB_SUCCESS;
443 * Callback for most write operations in this module:
445 * notify the repl task that a object has changed. The notifies are
446 * gathered up in the replmd_private structure then written to the
447 * @REPLCHANGED object in each partition during the prepare_commit
449 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
451 int ret;
452 struct replmd_replicated_request *ac =
453 talloc_get_type_abort(req->context, struct replmd_replicated_request);
454 struct replmd_private *replmd_private =
455 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
456 struct nc_entry *modified_partition;
457 struct ldb_control *partition_ctrl;
458 const struct dsdb_control_current_partition *partition;
460 struct ldb_control **controls;
462 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
464 controls = ares->controls;
465 if (ldb_request_get_control(ac->req,
466 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
468 * Remove the current partition control from what we pass up
469 * the chain if it hasn't been requested manually.
471 controls = ldb_controls_except_specified(ares->controls, ares,
472 partition_ctrl);
475 if (ares->error != LDB_SUCCESS) {
476 DEBUG(5,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
477 return ldb_module_done(ac->req, controls,
478 ares->response, ares->error);
481 if (ares->type != LDB_REPLY_DONE) {
482 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
483 return ldb_module_done(ac->req, NULL,
484 NULL, LDB_ERR_OPERATIONS_ERROR);
487 if (!partition_ctrl) {
488 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
489 return ldb_module_done(ac->req, NULL,
490 NULL, LDB_ERR_OPERATIONS_ERROR);
493 partition = talloc_get_type_abort(partition_ctrl->data,
494 struct dsdb_control_current_partition);
496 if (ac->seq_num > 0) {
497 for (modified_partition = replmd_private->ncs; modified_partition;
498 modified_partition = modified_partition->next) {
499 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
500 break;
504 if (modified_partition == NULL) {
505 modified_partition = talloc_zero(replmd_private, struct nc_entry);
506 if (!modified_partition) {
507 ldb_oom(ldb_module_get_ctx(ac->module));
508 return ldb_module_done(ac->req, NULL,
509 NULL, LDB_ERR_OPERATIONS_ERROR);
511 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
512 if (!modified_partition->dn) {
513 ldb_oom(ldb_module_get_ctx(ac->module));
514 return ldb_module_done(ac->req, NULL,
515 NULL, LDB_ERR_OPERATIONS_ERROR);
517 DLIST_ADD(replmd_private->ncs, modified_partition);
520 if (ac->seq_num > modified_partition->mod_usn) {
521 modified_partition->mod_usn = ac->seq_num;
522 if (ac->is_urgent) {
523 modified_partition->mod_usn_urgent = ac->seq_num;
528 if (ac->apply_mode) {
529 ret = replmd_replicated_apply_isDeleted(ac);
530 if (ret != LDB_SUCCESS) {
531 return ldb_module_done(ac->req, NULL, NULL, ret);
533 return ret;
534 } else {
535 /* free the partition control container here, for the
536 * common path. Other cases will have it cleaned up
537 * eventually with the ares */
538 talloc_free(partition_ctrl);
539 return ldb_module_done(ac->req, controls,
540 ares->response, LDB_SUCCESS);
546 * update a @REPLCHANGED record in each partition if there have been
547 * any writes of replicated data in the partition
549 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
551 struct replmd_private *replmd_private =
552 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
554 while (replmd_private->ncs) {
555 int ret;
556 struct nc_entry *modified_partition = replmd_private->ncs;
558 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
559 modified_partition->mod_usn,
560 modified_partition->mod_usn_urgent, parent);
561 if (ret != LDB_SUCCESS) {
562 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
563 ldb_dn_get_linearized(modified_partition->dn)));
564 return ret;
566 DLIST_REMOVE(replmd_private->ncs, modified_partition);
567 talloc_free(modified_partition);
570 return LDB_SUCCESS;
575 created a replmd_replicated_request context
577 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
578 struct ldb_request *req)
580 struct ldb_context *ldb;
581 struct replmd_replicated_request *ac;
583 ldb = ldb_module_get_ctx(module);
585 ac = talloc_zero(req, struct replmd_replicated_request);
586 if (ac == NULL) {
587 ldb_oom(ldb);
588 return NULL;
591 ac->module = module;
592 ac->req = req;
594 ac->schema = dsdb_get_schema(ldb, ac);
595 if (!ac->schema) {
596 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
597 "replmd_modify: no dsdb_schema loaded");
598 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
599 return NULL;
602 return ac;
606 add a time element to a record
608 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
610 struct ldb_message_element *el;
611 char *s;
612 int ret;
614 if (ldb_msg_find_element(msg, attr) != NULL) {
615 return LDB_SUCCESS;
618 s = ldb_timestring(msg, t);
619 if (s == NULL) {
620 return LDB_ERR_OPERATIONS_ERROR;
623 ret = ldb_msg_add_string(msg, attr, s);
624 if (ret != LDB_SUCCESS) {
625 return ret;
628 el = ldb_msg_find_element(msg, attr);
629 /* always set as replace. This works because on add ops, the flag
630 is ignored */
631 el->flags = LDB_FLAG_MOD_REPLACE;
633 return LDB_SUCCESS;
637 add a uint64_t element to a record
639 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
640 const char *attr, uint64_t v)
642 struct ldb_message_element *el;
643 int ret;
645 if (ldb_msg_find_element(msg, attr) != NULL) {
646 return LDB_SUCCESS;
649 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
650 if (ret != LDB_SUCCESS) {
651 return ret;
654 el = ldb_msg_find_element(msg, attr);
655 /* always set as replace. This works because on add ops, the flag
656 is ignored */
657 el->flags = LDB_FLAG_MOD_REPLACE;
659 return LDB_SUCCESS;
662 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
663 const struct replPropertyMetaData1 *m2,
664 const uint32_t *rdn_attid)
667 * This assignment seems inoccous, but it is critical for the
668 * system, as we need to do the comparisons as a unsigned
669 * quantity, not signed (enums are signed integers)
671 uint32_t attid_1 = m1->attid;
672 uint32_t attid_2 = m2->attid;
674 if (attid_1 == attid_2) {
675 return 0;
679 * the rdn attribute should be at the end!
680 * so we need to return a value greater than zero
681 * which means m1 is greater than m2
683 if (attid_1 == *rdn_attid) {
684 return 1;
688 * the rdn attribute should be at the end!
689 * so we need to return a value less than zero
690 * which means m2 is greater than m1
692 if (attid_2 == *rdn_attid) {
693 return -1;
697 * See above regarding this being an unsigned comparison.
698 * Otherwise when the high bit is set on non-standard
699 * attributes, they would end up first, before objectClass
700 * (0).
702 return attid_1 > attid_2 ? 1 : -1;
705 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
706 struct replPropertyMetaDataCtr1 *ctr1,
707 const struct dsdb_attribute *rdn_sa,
708 struct ldb_dn *dn)
710 if (ctr1->count == 0) {
711 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
712 "No elements found in replPropertyMetaData for %s!\n",
713 ldb_dn_get_linearized(dn));
714 return LDB_ERR_CONSTRAINT_VIOLATION;
716 if (ctr1->array[ctr1->count - 1].attid != rdn_sa->attributeID_id) {
717 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
718 "No rDN found in replPropertyMetaData for %s!\n",
719 ldb_dn_get_linearized(dn));
720 return LDB_ERR_CONSTRAINT_VIOLATION;
723 /* the objectClass attribute is value 0x00000000, so must be first */
724 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
725 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
726 "No objectClass found in replPropertyMetaData for %s!\n",
727 ldb_dn_get_linearized(dn));
728 return LDB_ERR_OBJECT_CLASS_VIOLATION;
731 return LDB_SUCCESS;
734 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
735 struct replPropertyMetaDataCtr1 *ctr1,
736 const struct dsdb_schema *schema,
737 struct ldb_dn *dn)
739 const char *rdn_name;
740 const struct dsdb_attribute *rdn_sa;
742 rdn_name = ldb_dn_get_rdn_name(dn);
743 if (!rdn_name) {
744 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
745 __location__ ": No rDN for %s?\n",
746 ldb_dn_get_linearized(dn));
747 return LDB_ERR_INVALID_DN_SYNTAX;
750 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
751 if (rdn_sa == NULL) {
752 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
753 __location__ ": No sa found for rDN %s for %s\n",
754 rdn_name, ldb_dn_get_linearized(dn));
755 return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
758 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
759 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
761 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id,
762 replmd_replPropertyMetaData1_attid_sort);
763 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, rdn_sa, dn);
766 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
767 const struct ldb_message_element *e2,
768 const struct dsdb_schema *schema)
770 const struct dsdb_attribute *a1;
771 const struct dsdb_attribute *a2;
774 * TODO: make this faster by caching the dsdb_attribute pointer
775 * on the ldb_messag_element
778 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
779 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
782 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
783 * in the schema
785 if (!a1 || !a2) {
786 return strcasecmp(e1->name, e2->name);
788 if (a1->attributeID_id == a2->attributeID_id) {
789 return 0;
791 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
794 static void replmd_ldb_message_sort(struct ldb_message *msg,
795 const struct dsdb_schema *schema)
797 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
800 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
801 const struct GUID *invocation_id, uint64_t seq_num,
802 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
806 fix up linked attributes in replmd_add.
807 This involves setting up the right meta-data in extended DN
808 components, and creating backlinks to the object
810 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
811 uint64_t seq_num, const struct GUID *invocationId, time_t t,
812 struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent)
814 unsigned int i;
815 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
816 struct ldb_context *ldb = ldb_module_get_ctx(module);
818 /* We will take a reference to the schema in replmd_add_backlink */
819 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
820 NTTIME now;
822 unix_to_nt_time(&now, t);
824 for (i=0; i<el->num_values; i++) {
825 struct ldb_val *v = &el->values[i];
826 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
827 struct GUID target_guid;
828 NTSTATUS status;
829 int ret;
831 /* note that the DN already has the extended
832 components from the extended_dn_store module */
833 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
834 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
835 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid, parent);
836 if (ret != LDB_SUCCESS) {
837 talloc_free(tmp_ctx);
838 return ret;
840 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
841 if (ret != LDB_SUCCESS) {
842 talloc_free(tmp_ctx);
843 return ret;
847 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
848 seq_num, seq_num, now, 0, false);
849 if (ret != LDB_SUCCESS) {
850 talloc_free(tmp_ctx);
851 return ret;
854 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
855 if (ret != LDB_SUCCESS) {
856 talloc_free(tmp_ctx);
857 return ret;
861 talloc_free(tmp_ctx);
862 return LDB_SUCCESS;
867 intercept add requests
869 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
871 struct samldb_msds_intid_persistant *msds_intid_struct;
872 struct ldb_context *ldb;
873 struct ldb_control *control;
874 struct replmd_replicated_request *ac;
875 enum ndr_err_code ndr_err;
876 struct ldb_request *down_req;
877 struct ldb_message *msg;
878 const DATA_BLOB *guid_blob;
879 struct GUID guid;
880 struct replPropertyMetaDataBlob nmd;
881 struct ldb_val nmd_value;
882 const struct GUID *our_invocation_id;
883 time_t t = time(NULL);
884 NTTIME now;
885 char *time_str;
886 int ret;
887 unsigned int i;
888 unsigned int functional_level;
889 uint32_t ni=0;
890 bool allow_add_guid = false;
891 bool remove_current_guid = false;
892 bool is_urgent = false;
893 struct ldb_message_element *objectclass_el;
894 struct replmd_private *replmd_private =
895 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
897 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
898 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
899 if (control) {
900 allow_add_guid = true;
903 /* do not manipulate our control entries */
904 if (ldb_dn_is_special(req->op.add.message->dn)) {
905 return ldb_next_request(module, req);
908 ldb = ldb_module_get_ctx(module);
910 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
912 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
913 if (guid_blob != NULL) {
914 if (!allow_add_guid) {
915 ldb_set_errstring(ldb,
916 "replmd_add: it's not allowed to add an object with objectGUID!");
917 return LDB_ERR_UNWILLING_TO_PERFORM;
918 } else {
919 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
920 if (!NT_STATUS_IS_OK(status)) {
921 ldb_set_errstring(ldb,
922 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
923 return LDB_ERR_UNWILLING_TO_PERFORM;
925 /* we remove this attribute as it can be a string and
926 * will not be treated correctly and then we will re-add
927 * it later on in the good format */
928 remove_current_guid = true;
930 } else {
931 /* a new GUID */
932 guid = GUID_random();
935 ac = replmd_ctx_init(module, req);
936 if (ac == NULL) {
937 return ldb_module_oom(module);
940 functional_level = dsdb_functional_level(ldb);
942 /* Get a sequence number from the backend */
943 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
944 if (ret != LDB_SUCCESS) {
945 talloc_free(ac);
946 return ret;
949 /* get our invocationId */
950 our_invocation_id = samdb_ntds_invocation_id(ldb);
951 if (!our_invocation_id) {
952 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
953 "replmd_add: unable to find invocationId\n");
954 talloc_free(ac);
955 return LDB_ERR_OPERATIONS_ERROR;
958 /* we have to copy the message as the caller might have it as a const */
959 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
960 if (msg == NULL) {
961 ldb_oom(ldb);
962 talloc_free(ac);
963 return LDB_ERR_OPERATIONS_ERROR;
966 /* generated times */
967 unix_to_nt_time(&now, t);
968 time_str = ldb_timestring(msg, t);
969 if (!time_str) {
970 ldb_oom(ldb);
971 talloc_free(ac);
972 return LDB_ERR_OPERATIONS_ERROR;
974 if (remove_current_guid) {
975 ldb_msg_remove_attr(msg,"objectGUID");
979 * remove autogenerated attributes
981 ldb_msg_remove_attr(msg, "whenCreated");
982 ldb_msg_remove_attr(msg, "whenChanged");
983 ldb_msg_remove_attr(msg, "uSNCreated");
984 ldb_msg_remove_attr(msg, "uSNChanged");
985 ldb_msg_remove_attr(msg, "replPropertyMetaData");
988 * readd replicated attributes
990 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
991 if (ret != LDB_SUCCESS) {
992 ldb_oom(ldb);
993 talloc_free(ac);
994 return ret;
997 /* build the replication meta_data */
998 ZERO_STRUCT(nmd);
999 nmd.version = 1;
1000 nmd.ctr.ctr1.count = msg->num_elements;
1001 nmd.ctr.ctr1.array = talloc_array(msg,
1002 struct replPropertyMetaData1,
1003 nmd.ctr.ctr1.count);
1004 if (!nmd.ctr.ctr1.array) {
1005 ldb_oom(ldb);
1006 talloc_free(ac);
1007 return LDB_ERR_OPERATIONS_ERROR;
1010 for (i=0; i < msg->num_elements; i++) {
1011 struct ldb_message_element *e = &msg->elements[i];
1012 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1013 const struct dsdb_attribute *sa;
1015 if (e->name[0] == '@') continue;
1017 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1018 if (!sa) {
1019 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1020 "replmd_add: attribute '%s' not defined in schema\n",
1021 e->name);
1022 talloc_free(ac);
1023 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1026 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1027 /* if the attribute is not replicated (0x00000001)
1028 * or constructed (0x00000004) it has no metadata
1030 continue;
1033 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1034 ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa, req);
1035 if (ret != LDB_SUCCESS) {
1036 talloc_free(ac);
1037 return ret;
1039 /* linked attributes are not stored in
1040 replPropertyMetaData in FL above w2k */
1041 continue;
1044 m->attid = sa->attributeID_id;
1045 m->version = 1;
1046 if (m->attid == 0x20030) {
1047 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1048 const char* rdn;
1050 if (rdn_val == NULL) {
1051 ldb_oom(ldb);
1052 talloc_free(ac);
1053 return LDB_ERR_OPERATIONS_ERROR;
1056 rdn = (const char*)rdn_val->data;
1057 if (strcmp(rdn, "Deleted Objects") == 0) {
1059 * Set the originating_change_time to 29/12/9999 at 23:59:59
1060 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1062 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1063 } else {
1064 m->originating_change_time = now;
1066 } else {
1067 m->originating_change_time = now;
1069 m->originating_invocation_id = *our_invocation_id;
1070 m->originating_usn = ac->seq_num;
1071 m->local_usn = ac->seq_num;
1072 ni++;
1075 /* fix meta data count */
1076 nmd.ctr.ctr1.count = ni;
1079 * sort meta data array, and move the rdn attribute entry to the end
1081 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, ac->schema, msg->dn);
1082 if (ret != LDB_SUCCESS) {
1083 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1084 talloc_free(ac);
1085 return ret;
1088 /* generated NDR encoded values */
1089 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1090 &nmd,
1091 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1092 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1093 ldb_oom(ldb);
1094 talloc_free(ac);
1095 return LDB_ERR_OPERATIONS_ERROR;
1099 * add the autogenerated values
1101 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1102 if (ret != LDB_SUCCESS) {
1103 ldb_oom(ldb);
1104 talloc_free(ac);
1105 return ret;
1107 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1108 if (ret != LDB_SUCCESS) {
1109 ldb_oom(ldb);
1110 talloc_free(ac);
1111 return ret;
1113 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1114 if (ret != LDB_SUCCESS) {
1115 ldb_oom(ldb);
1116 talloc_free(ac);
1117 return ret;
1119 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1120 if (ret != LDB_SUCCESS) {
1121 ldb_oom(ldb);
1122 talloc_free(ac);
1123 return ret;
1125 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1126 if (ret != LDB_SUCCESS) {
1127 ldb_oom(ldb);
1128 talloc_free(ac);
1129 return ret;
1133 * sort the attributes by attid before storing the object
1135 replmd_ldb_message_sort(msg, ac->schema);
1138 * Assert that we do have an objectClass
1140 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1141 if (objectclass_el == NULL) {
1142 ldb_asprintf_errstring(ldb, __location__
1143 ": objectClass missing on %s\n",
1144 ldb_dn_get_linearized(msg->dn));
1145 talloc_free(ac);
1146 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1148 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1149 REPL_URGENT_ON_CREATE);
1151 ac->is_urgent = is_urgent;
1152 ret = ldb_build_add_req(&down_req, ldb, ac,
1153 msg,
1154 req->controls,
1155 ac, replmd_op_callback,
1156 req);
1158 LDB_REQ_SET_LOCATION(down_req);
1159 if (ret != LDB_SUCCESS) {
1160 talloc_free(ac);
1161 return ret;
1164 /* current partition control is needed by "replmd_op_callback" */
1165 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1166 ret = ldb_request_add_control(down_req,
1167 DSDB_CONTROL_CURRENT_PARTITION_OID,
1168 false, NULL);
1169 if (ret != LDB_SUCCESS) {
1170 talloc_free(ac);
1171 return ret;
1175 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1176 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1177 if (ret != LDB_SUCCESS) {
1178 talloc_free(ac);
1179 return ret;
1183 /* mark the control done */
1184 if (control) {
1185 control->critical = 0;
1187 if (ldb_dn_compare_base(replmd_private->schema_dn, req->op.add.message->dn) != 0) {
1189 /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
1190 msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
1191 if (msds_intid_struct) {
1192 msds_intid_struct->usn = ac->seq_num;
1195 /* go on with the call chain */
1196 return ldb_next_request(module, down_req);
1201 * update the replPropertyMetaData for one element
1203 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1204 struct ldb_message *msg,
1205 struct ldb_message_element *el,
1206 struct ldb_message_element *old_el,
1207 struct replPropertyMetaDataBlob *omd,
1208 const struct dsdb_schema *schema,
1209 uint64_t *seq_num,
1210 const struct GUID *our_invocation_id,
1211 NTTIME now,
1212 struct ldb_request *req)
1214 uint32_t i;
1215 const struct dsdb_attribute *a;
1216 struct replPropertyMetaData1 *md1;
1217 bool may_skip = false;
1219 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1220 if (a == NULL) {
1221 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1222 /* allow this to make it possible for dbcheck
1223 to remove bad attributes */
1224 return LDB_SUCCESS;
1227 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1228 el->name));
1229 return LDB_ERR_OPERATIONS_ERROR;
1232 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1233 return LDB_SUCCESS;
1237 * if the attribute's value haven't changed, and this isn't
1238 * just a delete of everything then return LDB_SUCCESS Unless
1239 * we have the provision control or if the attribute is
1240 * interSiteTopologyGenerator as this page explain:
1241 * http://support.microsoft.com/kb/224815 this attribute is
1242 * periodicaly written by the DC responsible for the intersite
1243 * generation in a given site
1245 * Unchanged could be deleting or replacing an already-gone
1246 * thing with an unconstrained delete/empty replace or a
1247 * replace with the same value, but not an add with the same
1248 * value because that could be about adding a duplicate (which
1249 * is for someone else to error out on).
1251 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1252 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1253 may_skip = true;
1255 } else if (old_el == NULL && el->num_values == 0) {
1256 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1257 may_skip = true;
1258 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1259 may_skip = true;
1263 if (may_skip) {
1264 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1265 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1267 * allow this to make it possible for dbcheck
1268 * to rebuild broken metadata
1270 return LDB_SUCCESS;
1274 for (i=0; i<omd->ctr.ctr1.count; i++) {
1275 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
1278 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1279 /* linked attributes are not stored in
1280 replPropertyMetaData in FL above w2k, but we do
1281 raise the seqnum for the object */
1282 if (*seq_num == 0 &&
1283 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1284 return LDB_ERR_OPERATIONS_ERROR;
1286 return LDB_SUCCESS;
1289 if (i == omd->ctr.ctr1.count) {
1290 /* we need to add a new one */
1291 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1292 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1293 if (omd->ctr.ctr1.array == NULL) {
1294 ldb_oom(ldb);
1295 return LDB_ERR_OPERATIONS_ERROR;
1297 omd->ctr.ctr1.count++;
1298 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1301 /* Get a new sequence number from the backend. We only do this
1302 * if we have a change that requires a new
1303 * replPropertyMetaData element
1305 if (*seq_num == 0) {
1306 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1307 if (ret != LDB_SUCCESS) {
1308 return LDB_ERR_OPERATIONS_ERROR;
1312 md1 = &omd->ctr.ctr1.array[i];
1313 md1->version++;
1314 md1->attid = a->attributeID_id;
1315 if (md1->attid == 0x20030) {
1316 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1317 const char* rdn;
1319 if (rdn_val == NULL) {
1320 ldb_oom(ldb);
1321 return LDB_ERR_OPERATIONS_ERROR;
1324 rdn = (const char*)rdn_val->data;
1325 if (strcmp(rdn, "Deleted Objects") == 0) {
1327 * Set the originating_change_time to 29/12/9999 at 23:59:59
1328 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1330 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1331 } else {
1332 md1->originating_change_time = now;
1334 } else {
1335 md1->originating_change_time = now;
1337 md1->originating_invocation_id = *our_invocation_id;
1338 md1->originating_usn = *seq_num;
1339 md1->local_usn = *seq_num;
1341 return LDB_SUCCESS;
1344 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1346 uint32_t count = omd.ctr.ctr1.count;
1347 uint64_t max = 0;
1348 uint32_t i;
1349 for (i=0; i < count; i++) {
1350 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1351 if (max < m.local_usn) {
1352 max = m.local_usn;
1355 return max;
1359 * update the replPropertyMetaData object each time we modify an
1360 * object. This is needed for DRS replication, as the merge on the
1361 * client is based on this object
1363 static int replmd_update_rpmd(struct ldb_module *module,
1364 const struct dsdb_schema *schema,
1365 struct ldb_request *req,
1366 const char * const *rename_attrs,
1367 struct ldb_message *msg, uint64_t *seq_num,
1368 time_t t,
1369 bool *is_urgent, bool *rodc)
1371 const struct ldb_val *omd_value;
1372 enum ndr_err_code ndr_err;
1373 struct replPropertyMetaDataBlob omd;
1374 unsigned int i;
1375 NTTIME now;
1376 const struct GUID *our_invocation_id;
1377 int ret;
1378 const char * const *attrs = NULL;
1379 const char * const attrs1[] = { "replPropertyMetaData", "*", NULL };
1380 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1381 struct ldb_result *res;
1382 struct ldb_context *ldb;
1383 struct ldb_message_element *objectclass_el;
1384 enum urgent_situation situation;
1385 bool rmd_is_provided;
1387 if (rename_attrs) {
1388 attrs = rename_attrs;
1389 } else {
1390 attrs = attrs1;
1393 ldb = ldb_module_get_ctx(module);
1395 our_invocation_id = samdb_ntds_invocation_id(ldb);
1396 if (!our_invocation_id) {
1397 /* this happens during an initial vampire while
1398 updating the schema */
1399 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1400 return LDB_SUCCESS;
1403 unix_to_nt_time(&now, t);
1405 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1406 rmd_is_provided = true;
1407 } else {
1408 rmd_is_provided = false;
1411 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1412 * otherwise we consider we are updating */
1413 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1414 situation = REPL_URGENT_ON_DELETE;
1415 } else if (rename_attrs) {
1416 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1417 } else {
1418 situation = REPL_URGENT_ON_UPDATE;
1421 if (rmd_is_provided) {
1422 /* In this case the change_replmetadata control was supplied */
1423 /* We check that it's the only attribute that is provided
1424 * (it's a rare case so it's better to keep the code simplier)
1425 * We also check that the highest local_usn is bigger than
1426 * uSNChanged. */
1427 uint64_t db_seq;
1428 if( msg->num_elements != 1 ||
1429 strncmp(msg->elements[0].name,
1430 "replPropertyMetaData", 20) ) {
1431 DEBUG(0,(__location__ ": changereplmetada control called without "\
1432 "a specified replPropertyMetaData attribute or with others\n"));
1433 return LDB_ERR_OPERATIONS_ERROR;
1435 if (situation != REPL_URGENT_ON_UPDATE) {
1436 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1437 return LDB_ERR_OPERATIONS_ERROR;
1439 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1440 if (!omd_value) {
1441 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1442 ldb_dn_get_linearized(msg->dn)));
1443 return LDB_ERR_OPERATIONS_ERROR;
1445 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1446 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1447 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1448 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1449 ldb_dn_get_linearized(msg->dn)));
1450 return LDB_ERR_OPERATIONS_ERROR;
1452 *seq_num = find_max_local_usn(omd);
1454 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1455 DSDB_FLAG_NEXT_MODULE |
1456 DSDB_SEARCH_SHOW_RECYCLED |
1457 DSDB_SEARCH_SHOW_EXTENDED_DN |
1458 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1459 DSDB_SEARCH_REVEAL_INTERNALS, req);
1461 if (ret != LDB_SUCCESS) {
1462 return ret;
1465 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1466 if (*seq_num <= db_seq) {
1467 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)"\
1468 " is less or equal to uSNChanged (max = %lld uSNChanged = %lld)\n",
1469 (long long)*seq_num, (long long)db_seq));
1470 return LDB_ERR_OPERATIONS_ERROR;
1473 } else {
1474 /* search for the existing replPropertyMetaDataBlob. We need
1475 * to use REVEAL and ask for DNs in storage format to support
1476 * the check for values being the same in
1477 * replmd_update_rpmd_element()
1479 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1480 DSDB_FLAG_NEXT_MODULE |
1481 DSDB_SEARCH_SHOW_RECYCLED |
1482 DSDB_SEARCH_SHOW_EXTENDED_DN |
1483 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1484 DSDB_SEARCH_REVEAL_INTERNALS, req);
1485 if (ret != LDB_SUCCESS) {
1486 return ret;
1489 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1490 if (!omd_value) {
1491 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1492 ldb_dn_get_linearized(msg->dn)));
1493 return LDB_ERR_OPERATIONS_ERROR;
1496 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1497 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1498 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1499 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1500 ldb_dn_get_linearized(msg->dn)));
1501 return LDB_ERR_OPERATIONS_ERROR;
1504 if (omd.version != 1) {
1505 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1506 omd.version, ldb_dn_get_linearized(msg->dn)));
1507 return LDB_ERR_OPERATIONS_ERROR;
1510 for (i=0; i<msg->num_elements; i++) {
1511 struct ldb_message_element *old_el;
1512 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1513 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1514 our_invocation_id, now, req);
1515 if (ret != LDB_SUCCESS) {
1516 return ret;
1519 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1520 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1527 * Assert that we have an objectClass attribute - this is major
1528 * corruption if we don't have this!
1530 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1531 if (objectclass_el != NULL) {
1533 * Now check if this objectClass means we need to do urgent replication
1535 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1536 situation)) {
1537 *is_urgent = true;
1539 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1540 ldb_asprintf_errstring(ldb, __location__
1541 ": objectClass missing on %s\n",
1542 ldb_dn_get_linearized(msg->dn));
1543 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1547 * replmd_update_rpmd_element has done an update if the
1548 * seq_num is set
1550 if (*seq_num != 0) {
1551 struct ldb_val *md_value;
1552 struct ldb_message_element *el;
1554 /*if we are RODC and this is a DRSR update then its ok*/
1555 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1556 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)) {
1557 unsigned instanceType;
1559 ret = samdb_rodc(ldb, rodc);
1560 if (ret != LDB_SUCCESS) {
1561 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1562 } else if (*rodc) {
1563 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1564 return LDB_ERR_REFERRAL;
1567 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1568 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1569 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1570 "cannot change replicated attribute on partial replica");
1574 md_value = talloc(msg, struct ldb_val);
1575 if (md_value == NULL) {
1576 ldb_oom(ldb);
1577 return LDB_ERR_OPERATIONS_ERROR;
1580 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, schema, msg->dn);
1581 if (ret != LDB_SUCCESS) {
1582 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
1583 return ret;
1586 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1587 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1588 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1589 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1590 ldb_dn_get_linearized(msg->dn)));
1591 return LDB_ERR_OPERATIONS_ERROR;
1594 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1595 if (ret != LDB_SUCCESS) {
1596 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1597 ldb_dn_get_linearized(msg->dn)));
1598 return ret;
1601 el->num_values = 1;
1602 el->values = md_value;
1605 return LDB_SUCCESS;
1608 struct parsed_dn {
1609 struct dsdb_dn *dsdb_dn;
1610 struct GUID *guid;
1611 struct ldb_val *v;
1614 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1616 return GUID_compare(pdn1->guid, pdn2->guid);
1619 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
1620 unsigned int count, struct GUID *guid,
1621 struct ldb_dn *dn)
1623 struct parsed_dn *ret;
1624 unsigned int i;
1625 if (dn && GUID_all_zero(guid)) {
1626 /* when updating a link using DRS, we sometimes get a
1627 NULL GUID. We then need to try and match by DN */
1628 for (i=0; i<count; i++) {
1629 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1630 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1631 return &pdn[i];
1634 return NULL;
1636 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1637 return ret;
1641 get a series of message element values as an array of DNs and GUIDs
1642 the result is sorted by GUID
1644 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1645 struct ldb_message_element *el, struct parsed_dn **pdn,
1646 const char *ldap_oid, struct ldb_request *parent)
1648 unsigned int i;
1649 struct ldb_context *ldb = ldb_module_get_ctx(module);
1651 if (el == NULL) {
1652 *pdn = NULL;
1653 return LDB_SUCCESS;
1656 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1657 if (!*pdn) {
1658 ldb_module_oom(module);
1659 return LDB_ERR_OPERATIONS_ERROR;
1662 for (i=0; i<el->num_values; i++) {
1663 struct ldb_val *v = &el->values[i];
1664 NTSTATUS status;
1665 struct ldb_dn *dn;
1666 struct parsed_dn *p;
1668 p = &(*pdn)[i];
1670 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1671 if (p->dsdb_dn == NULL) {
1672 return LDB_ERR_INVALID_DN_SYNTAX;
1675 dn = p->dsdb_dn->dn;
1677 p->guid = talloc(*pdn, struct GUID);
1678 if (p->guid == NULL) {
1679 ldb_module_oom(module);
1680 return LDB_ERR_OPERATIONS_ERROR;
1683 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1684 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1685 /* we got a DN without a GUID - go find the GUID */
1686 int ret = dsdb_module_guid_by_dn(module, dn, p->guid, parent);
1687 if (ret != LDB_SUCCESS) {
1688 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1689 ldb_dn_get_linearized(dn));
1690 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
1691 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1692 ldb_attr_cmp(el->name, "member") == 0) {
1693 return LDB_ERR_UNWILLING_TO_PERFORM;
1695 return ret;
1697 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1698 if (ret != LDB_SUCCESS) {
1699 return ret;
1701 } else if (!NT_STATUS_IS_OK(status)) {
1702 return LDB_ERR_OPERATIONS_ERROR;
1705 /* keep a pointer to the original ldb_val */
1706 p->v = v;
1709 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1711 return LDB_SUCCESS;
1715 build a new extended DN, including all meta data fields
1717 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1718 RMD_ADDTIME = originating_add_time
1719 RMD_INVOCID = originating_invocation_id
1720 RMD_CHANGETIME = originating_change_time
1721 RMD_ORIGINATING_USN = originating_usn
1722 RMD_LOCAL_USN = local_usn
1723 RMD_VERSION = version
1725 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1726 const struct GUID *invocation_id, uint64_t seq_num,
1727 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1729 struct ldb_dn *dn = dsdb_dn->dn;
1730 const char *tstring, *usn_string, *flags_string;
1731 struct ldb_val tval;
1732 struct ldb_val iid;
1733 struct ldb_val usnv, local_usnv;
1734 struct ldb_val vers, flagsv;
1735 NTSTATUS status;
1736 int ret;
1737 const char *dnstring;
1738 char *vstring;
1739 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1741 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1742 if (!tstring) {
1743 return LDB_ERR_OPERATIONS_ERROR;
1745 tval = data_blob_string_const(tstring);
1747 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1748 if (!usn_string) {
1749 return LDB_ERR_OPERATIONS_ERROR;
1751 usnv = data_blob_string_const(usn_string);
1753 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1754 if (!usn_string) {
1755 return LDB_ERR_OPERATIONS_ERROR;
1757 local_usnv = data_blob_string_const(usn_string);
1759 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1760 if (!vstring) {
1761 return LDB_ERR_OPERATIONS_ERROR;
1763 vers = data_blob_string_const(vstring);
1765 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1766 if (!NT_STATUS_IS_OK(status)) {
1767 return LDB_ERR_OPERATIONS_ERROR;
1770 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1771 if (!flags_string) {
1772 return LDB_ERR_OPERATIONS_ERROR;
1774 flagsv = data_blob_string_const(flags_string);
1776 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1777 if (ret != LDB_SUCCESS) return ret;
1778 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1779 if (ret != LDB_SUCCESS) return ret;
1780 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1781 if (ret != LDB_SUCCESS) return ret;
1782 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1783 if (ret != LDB_SUCCESS) return ret;
1784 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1785 if (ret != LDB_SUCCESS) return ret;
1786 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1787 if (ret != LDB_SUCCESS) return ret;
1788 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1789 if (ret != LDB_SUCCESS) return ret;
1791 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1792 if (dnstring == NULL) {
1793 return LDB_ERR_OPERATIONS_ERROR;
1795 *v = data_blob_string_const(dnstring);
1797 return LDB_SUCCESS;
1800 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1801 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1802 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1803 uint32_t version, bool deleted);
1806 check if any links need upgrading from w2k format
1808 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.
1810 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1812 uint32_t i;
1813 for (i=0; i<count; i++) {
1814 NTSTATUS status;
1815 uint32_t version;
1816 int ret;
1818 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1819 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1820 continue;
1823 /* it's an old one that needs upgrading */
1824 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1825 1, 1, 0, 0, false);
1826 if (ret != LDB_SUCCESS) {
1827 return ret;
1830 return LDB_SUCCESS;
1834 update an extended DN, including all meta data fields
1836 see replmd_build_la_val for value names
1838 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1839 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1840 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1841 uint32_t version, bool deleted)
1843 struct ldb_dn *dn = dsdb_dn->dn;
1844 const char *tstring, *usn_string, *flags_string;
1845 struct ldb_val tval;
1846 struct ldb_val iid;
1847 struct ldb_val usnv, local_usnv;
1848 struct ldb_val vers, flagsv;
1849 const struct ldb_val *old_addtime;
1850 uint32_t old_version;
1851 NTSTATUS status;
1852 int ret;
1853 const char *dnstring;
1854 char *vstring;
1855 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1857 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1858 if (!tstring) {
1859 return LDB_ERR_OPERATIONS_ERROR;
1861 tval = data_blob_string_const(tstring);
1863 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1864 if (!usn_string) {
1865 return LDB_ERR_OPERATIONS_ERROR;
1867 usnv = data_blob_string_const(usn_string);
1869 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1870 if (!usn_string) {
1871 return LDB_ERR_OPERATIONS_ERROR;
1873 local_usnv = data_blob_string_const(usn_string);
1875 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1876 if (!NT_STATUS_IS_OK(status)) {
1877 return LDB_ERR_OPERATIONS_ERROR;
1880 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1881 if (!flags_string) {
1882 return LDB_ERR_OPERATIONS_ERROR;
1884 flagsv = data_blob_string_const(flags_string);
1886 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1887 if (ret != LDB_SUCCESS) return ret;
1889 /* get the ADDTIME from the original */
1890 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1891 if (old_addtime == NULL) {
1892 old_addtime = &tval;
1894 if (dsdb_dn != old_dsdb_dn ||
1895 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
1896 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1897 if (ret != LDB_SUCCESS) return ret;
1900 /* use our invocation id */
1901 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1902 if (ret != LDB_SUCCESS) return ret;
1904 /* changetime is the current time */
1905 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1906 if (ret != LDB_SUCCESS) return ret;
1908 /* update the USN */
1909 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1910 if (ret != LDB_SUCCESS) return ret;
1912 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1913 if (ret != LDB_SUCCESS) return ret;
1915 /* increase the version by 1 */
1916 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1917 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1918 version = old_version+1;
1920 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1921 vers = data_blob_string_const(vstring);
1922 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1923 if (ret != LDB_SUCCESS) return ret;
1925 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1926 if (dnstring == NULL) {
1927 return LDB_ERR_OPERATIONS_ERROR;
1929 *v = data_blob_string_const(dnstring);
1931 return LDB_SUCCESS;
1935 handle adding a linked attribute
1937 static int replmd_modify_la_add(struct ldb_module *module,
1938 const struct dsdb_schema *schema,
1939 struct ldb_message *msg,
1940 struct ldb_message_element *el,
1941 struct ldb_message_element *old_el,
1942 const struct dsdb_attribute *schema_attr,
1943 uint64_t seq_num,
1944 time_t t,
1945 struct GUID *msg_guid,
1946 struct ldb_request *parent)
1948 unsigned int i;
1949 struct parsed_dn *dns, *old_dns;
1950 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1951 int ret;
1952 struct ldb_val *new_values = NULL;
1953 unsigned int num_new_values = 0;
1954 unsigned old_num_values = old_el?old_el->num_values:0;
1955 const struct GUID *invocation_id;
1956 struct ldb_context *ldb = ldb_module_get_ctx(module);
1957 NTTIME now;
1959 unix_to_nt_time(&now, t);
1961 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1962 if (ret != LDB_SUCCESS) {
1963 talloc_free(tmp_ctx);
1964 return ret;
1967 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1968 if (ret != LDB_SUCCESS) {
1969 talloc_free(tmp_ctx);
1970 return ret;
1973 invocation_id = samdb_ntds_invocation_id(ldb);
1974 if (!invocation_id) {
1975 talloc_free(tmp_ctx);
1976 return LDB_ERR_OPERATIONS_ERROR;
1979 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1980 if (ret != LDB_SUCCESS) {
1981 talloc_free(tmp_ctx);
1982 return ret;
1985 /* for each new value, see if it exists already with the same GUID */
1986 for (i=0; i<el->num_values; i++) {
1987 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1988 if (p == NULL) {
1989 /* this is a new linked attribute value */
1990 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1991 if (new_values == NULL) {
1992 ldb_module_oom(module);
1993 talloc_free(tmp_ctx);
1994 return LDB_ERR_OPERATIONS_ERROR;
1996 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1997 invocation_id, seq_num, seq_num, now, 0, false);
1998 if (ret != LDB_SUCCESS) {
1999 talloc_free(tmp_ctx);
2000 return ret;
2002 num_new_values++;
2003 } else {
2004 /* this is only allowed if the GUID was
2005 previously deleted. */
2006 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2008 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2009 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
2010 el->name, GUID_string(tmp_ctx, p->guid));
2011 talloc_free(tmp_ctx);
2012 /* error codes for 'member' need to be
2013 special cased */
2014 if (ldb_attr_cmp(el->name, "member") == 0) {
2015 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2016 } else {
2017 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2020 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
2021 invocation_id, seq_num, seq_num, now, 0, false);
2022 if (ret != LDB_SUCCESS) {
2023 talloc_free(tmp_ctx);
2024 return ret;
2028 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
2029 if (ret != LDB_SUCCESS) {
2030 talloc_free(tmp_ctx);
2031 return ret;
2035 /* add the new ones on to the end of the old values, constructing a new el->values */
2036 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2037 struct ldb_val,
2038 old_num_values+num_new_values);
2039 if (el->values == NULL) {
2040 ldb_module_oom(module);
2041 return LDB_ERR_OPERATIONS_ERROR;
2044 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
2045 el->num_values = old_num_values + num_new_values;
2047 talloc_steal(msg->elements, el->values);
2048 talloc_steal(el->values, new_values);
2050 talloc_free(tmp_ctx);
2052 /* we now tell the backend to replace all existing values
2053 with the one we have constructed */
2054 el->flags = LDB_FLAG_MOD_REPLACE;
2056 return LDB_SUCCESS;
2061 handle deleting all active linked attributes
2063 static int replmd_modify_la_delete(struct ldb_module *module,
2064 const struct dsdb_schema *schema,
2065 struct ldb_message *msg,
2066 struct ldb_message_element *el,
2067 struct ldb_message_element *old_el,
2068 const struct dsdb_attribute *schema_attr,
2069 uint64_t seq_num,
2070 time_t t,
2071 struct GUID *msg_guid,
2072 struct ldb_request *parent)
2074 unsigned int i;
2075 struct parsed_dn *dns, *old_dns;
2076 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2077 int ret;
2078 const struct GUID *invocation_id;
2079 struct ldb_context *ldb = ldb_module_get_ctx(module);
2080 NTTIME now;
2082 unix_to_nt_time(&now, t);
2084 /* check if there is nothing to delete */
2085 if ((!old_el || old_el->num_values == 0) &&
2086 el->num_values == 0) {
2087 return LDB_SUCCESS;
2090 if (!old_el || old_el->num_values == 0) {
2091 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2094 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2095 if (ret != LDB_SUCCESS) {
2096 talloc_free(tmp_ctx);
2097 return ret;
2100 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2101 if (ret != LDB_SUCCESS) {
2102 talloc_free(tmp_ctx);
2103 return ret;
2106 invocation_id = samdb_ntds_invocation_id(ldb);
2107 if (!invocation_id) {
2108 return LDB_ERR_OPERATIONS_ERROR;
2111 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
2112 if (ret != LDB_SUCCESS) {
2113 talloc_free(tmp_ctx);
2114 return ret;
2117 el->values = NULL;
2119 /* see if we are being asked to delete any links that
2120 don't exist or are already deleted */
2121 for (i=0; i<el->num_values; i++) {
2122 struct parsed_dn *p = &dns[i];
2123 struct parsed_dn *p2;
2124 uint32_t rmd_flags;
2126 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
2127 if (!p2) {
2128 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
2129 el->name, GUID_string(tmp_ctx, p->guid));
2130 if (ldb_attr_cmp(el->name, "member") == 0) {
2131 return LDB_ERR_UNWILLING_TO_PERFORM;
2132 } else {
2133 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2136 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
2137 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2138 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
2139 el->name, GUID_string(tmp_ctx, p->guid));
2140 if (ldb_attr_cmp(el->name, "member") == 0) {
2141 return LDB_ERR_UNWILLING_TO_PERFORM;
2142 } else {
2143 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2148 /* for each new value, see if it exists already with the same GUID
2149 if it is not already deleted and matches the delete list then delete it
2151 for (i=0; i<old_el->num_values; i++) {
2152 struct parsed_dn *p = &old_dns[i];
2153 uint32_t rmd_flags;
2155 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
2156 continue;
2159 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2160 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2162 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
2163 invocation_id, seq_num, seq_num, now, 0, true);
2164 if (ret != LDB_SUCCESS) {
2165 talloc_free(tmp_ctx);
2166 return ret;
2169 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
2170 if (ret != LDB_SUCCESS) {
2171 talloc_free(tmp_ctx);
2172 return ret;
2176 el->values = talloc_steal(msg->elements, old_el->values);
2177 el->num_values = old_el->num_values;
2179 talloc_free(tmp_ctx);
2181 /* we now tell the backend to replace all existing values
2182 with the one we have constructed */
2183 el->flags = LDB_FLAG_MOD_REPLACE;
2185 return LDB_SUCCESS;
2189 handle replacing a linked attribute
2191 static int replmd_modify_la_replace(struct ldb_module *module,
2192 const struct dsdb_schema *schema,
2193 struct ldb_message *msg,
2194 struct ldb_message_element *el,
2195 struct ldb_message_element *old_el,
2196 const struct dsdb_attribute *schema_attr,
2197 uint64_t seq_num,
2198 time_t t,
2199 struct GUID *msg_guid,
2200 struct ldb_request *parent)
2202 unsigned int i;
2203 struct parsed_dn *dns, *old_dns;
2204 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2205 int ret;
2206 const struct GUID *invocation_id;
2207 struct ldb_context *ldb = ldb_module_get_ctx(module);
2208 struct ldb_val *new_values = NULL;
2209 unsigned int num_new_values = 0;
2210 unsigned int old_num_values = old_el?old_el->num_values:0;
2211 NTTIME now;
2213 unix_to_nt_time(&now, t);
2215 /* check if there is nothing to replace */
2216 if ((!old_el || old_el->num_values == 0) &&
2217 el->num_values == 0) {
2218 return LDB_SUCCESS;
2221 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2222 if (ret != LDB_SUCCESS) {
2223 talloc_free(tmp_ctx);
2224 return ret;
2227 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2228 if (ret != LDB_SUCCESS) {
2229 talloc_free(tmp_ctx);
2230 return ret;
2233 invocation_id = samdb_ntds_invocation_id(ldb);
2234 if (!invocation_id) {
2235 return LDB_ERR_OPERATIONS_ERROR;
2238 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2239 if (ret != LDB_SUCCESS) {
2240 talloc_free(tmp_ctx);
2241 return ret;
2244 /* mark all the old ones as deleted */
2245 for (i=0; i<old_num_values; i++) {
2246 struct parsed_dn *old_p = &old_dns[i];
2247 struct parsed_dn *p;
2248 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2250 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2252 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
2253 if (ret != LDB_SUCCESS) {
2254 talloc_free(tmp_ctx);
2255 return ret;
2258 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
2259 if (p) {
2260 /* we don't delete it if we are re-adding it */
2261 continue;
2264 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
2265 invocation_id, seq_num, seq_num, now, 0, true);
2266 if (ret != LDB_SUCCESS) {
2267 talloc_free(tmp_ctx);
2268 return ret;
2272 /* for each new value, either update its meta-data, or add it
2273 * to old_el
2275 for (i=0; i<el->num_values; i++) {
2276 struct parsed_dn *p = &dns[i], *old_p;
2278 if (old_dns &&
2279 (old_p = parsed_dn_find(old_dns,
2280 old_num_values, p->guid, NULL)) != NULL) {
2281 /* update in place */
2282 ret = replmd_update_la_val(old_el->values, old_p->v, p->dsdb_dn,
2283 old_p->dsdb_dn, invocation_id,
2284 seq_num, seq_num, now, 0, false);
2285 if (ret != LDB_SUCCESS) {
2286 talloc_free(tmp_ctx);
2287 return ret;
2289 } else {
2290 /* add a new one */
2291 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
2292 num_new_values+1);
2293 if (new_values == NULL) {
2294 ldb_module_oom(module);
2295 talloc_free(tmp_ctx);
2296 return LDB_ERR_OPERATIONS_ERROR;
2298 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2299 invocation_id, seq_num, seq_num, now, 0, false);
2300 if (ret != LDB_SUCCESS) {
2301 talloc_free(tmp_ctx);
2302 return ret;
2304 num_new_values++;
2307 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
2308 if (ret != LDB_SUCCESS) {
2309 talloc_free(tmp_ctx);
2310 return ret;
2314 /* add the new values to the end of old_el */
2315 if (num_new_values != 0) {
2316 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2317 struct ldb_val, old_num_values+num_new_values);
2318 if (el->values == NULL) {
2319 ldb_module_oom(module);
2320 return LDB_ERR_OPERATIONS_ERROR;
2322 memcpy(&el->values[old_num_values], &new_values[0],
2323 sizeof(struct ldb_val)*num_new_values);
2324 el->num_values = old_num_values + num_new_values;
2325 talloc_steal(msg->elements, new_values);
2326 } else {
2327 el->values = old_el->values;
2328 el->num_values = old_el->num_values;
2329 talloc_steal(msg->elements, el->values);
2332 talloc_free(tmp_ctx);
2334 /* we now tell the backend to replace all existing values
2335 with the one we have constructed */
2336 el->flags = LDB_FLAG_MOD_REPLACE;
2338 return LDB_SUCCESS;
2343 handle linked attributes in modify requests
2345 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
2346 struct ldb_message *msg,
2347 uint64_t seq_num, time_t t,
2348 struct ldb_request *parent)
2350 struct ldb_result *res;
2351 unsigned int i;
2352 int ret;
2353 struct ldb_context *ldb = ldb_module_get_ctx(module);
2354 struct ldb_message *old_msg;
2356 const struct dsdb_schema *schema;
2357 struct GUID old_guid;
2359 if (seq_num == 0) {
2360 /* there the replmd_update_rpmd code has already
2361 * checked and saw that there are no linked
2362 * attributes */
2363 return LDB_SUCCESS;
2366 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
2367 /* don't do anything special for linked attributes */
2368 return LDB_SUCCESS;
2371 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
2372 DSDB_FLAG_NEXT_MODULE |
2373 DSDB_SEARCH_SHOW_RECYCLED |
2374 DSDB_SEARCH_REVEAL_INTERNALS |
2375 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
2376 parent);
2377 if (ret != LDB_SUCCESS) {
2378 return ret;
2380 schema = dsdb_get_schema(ldb, res);
2381 if (!schema) {
2382 return LDB_ERR_OPERATIONS_ERROR;
2385 old_msg = res->msgs[0];
2387 old_guid = samdb_result_guid(old_msg, "objectGUID");
2389 for (i=0; i<msg->num_elements; i++) {
2390 struct ldb_message_element *el = &msg->elements[i];
2391 struct ldb_message_element *old_el, *new_el;
2392 const struct dsdb_attribute *schema_attr
2393 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2394 if (!schema_attr) {
2395 ldb_asprintf_errstring(ldb,
2396 "%s: attribute %s is not a valid attribute in schema",
2397 __FUNCTION__, el->name);
2398 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2400 if (schema_attr->linkID == 0) {
2401 continue;
2403 if ((schema_attr->linkID & 1) == 1) {
2404 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
2405 continue;
2407 /* Odd is for the target. Illegal to modify */
2408 ldb_asprintf_errstring(ldb,
2409 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2410 return LDB_ERR_UNWILLING_TO_PERFORM;
2412 old_el = ldb_msg_find_element(old_msg, el->name);
2413 switch (el->flags & LDB_FLAG_MOD_MASK) {
2414 case LDB_FLAG_MOD_REPLACE:
2415 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2416 break;
2417 case LDB_FLAG_MOD_DELETE:
2418 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2419 break;
2420 case LDB_FLAG_MOD_ADD:
2421 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2422 break;
2423 default:
2424 ldb_asprintf_errstring(ldb,
2425 "invalid flags 0x%x for %s linked attribute",
2426 el->flags, el->name);
2427 return LDB_ERR_UNWILLING_TO_PERFORM;
2429 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
2430 ldb_asprintf_errstring(ldb,
2431 "Attribute %s is single valued but more than one value has been supplied",
2432 el->name);
2433 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2434 } else {
2435 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
2440 if (ret != LDB_SUCCESS) {
2441 return ret;
2443 if (old_el) {
2444 ldb_msg_remove_attr(old_msg, el->name);
2446 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2447 new_el->num_values = el->num_values;
2448 new_el->values = talloc_steal(msg->elements, el->values);
2450 /* TODO: this relises a bit too heavily on the exact
2451 behaviour of ldb_msg_find_element and
2452 ldb_msg_remove_element */
2453 old_el = ldb_msg_find_element(msg, el->name);
2454 if (old_el != el) {
2455 ldb_msg_remove_element(msg, old_el);
2456 i--;
2460 talloc_free(res);
2461 return ret;
2466 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2468 struct samldb_msds_intid_persistant *msds_intid_struct;
2469 struct ldb_context *ldb;
2470 struct replmd_replicated_request *ac;
2471 struct ldb_request *down_req;
2472 struct ldb_message *msg;
2473 time_t t = time(NULL);
2474 int ret;
2475 bool is_urgent = false, rodc = false;
2476 unsigned int functional_level;
2477 const struct ldb_message_element *guid_el = NULL;
2478 struct ldb_control *sd_propagation_control;
2479 struct replmd_private *replmd_private =
2480 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
2482 /* do not manipulate our control entries */
2483 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2484 return ldb_next_request(module, req);
2487 sd_propagation_control = ldb_request_get_control(req,
2488 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
2489 if (sd_propagation_control != NULL) {
2490 if (req->op.mod.message->num_elements != 1) {
2491 return ldb_module_operr(module);
2493 ret = strcmp(req->op.mod.message->elements[0].name,
2494 "nTSecurityDescriptor");
2495 if (ret != 0) {
2496 return ldb_module_operr(module);
2499 return ldb_next_request(module, req);
2502 ldb = ldb_module_get_ctx(module);
2504 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2506 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
2507 if (guid_el != NULL) {
2508 ldb_set_errstring(ldb,
2509 "replmd_modify: it's not allowed to change the objectGUID!");
2510 return LDB_ERR_CONSTRAINT_VIOLATION;
2513 ac = replmd_ctx_init(module, req);
2514 if (ac == NULL) {
2515 return ldb_module_oom(module);
2518 functional_level = dsdb_functional_level(ldb);
2520 /* we have to copy the message as the caller might have it as a const */
2521 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2522 if (msg == NULL) {
2523 ldb_oom(ldb);
2524 talloc_free(ac);
2525 return LDB_ERR_OPERATIONS_ERROR;
2528 ldb_msg_remove_attr(msg, "whenChanged");
2529 ldb_msg_remove_attr(msg, "uSNChanged");
2531 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
2532 msg, &ac->seq_num, t, &is_urgent, &rodc);
2533 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2534 struct loadparm_context *lp_ctx;
2535 char *referral;
2537 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2538 struct loadparm_context);
2540 referral = talloc_asprintf(req,
2541 "ldap://%s/%s",
2542 lpcfg_dnsdomain(lp_ctx),
2543 ldb_dn_get_linearized(msg->dn));
2544 ret = ldb_module_send_referral(req, referral);
2545 talloc_free(ac);
2546 return ret;
2549 if (ret != LDB_SUCCESS) {
2550 talloc_free(ac);
2551 return ret;
2554 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
2555 if (ret != LDB_SUCCESS) {
2556 talloc_free(ac);
2557 return ret;
2560 /* TODO:
2561 * - replace the old object with the newly constructed one
2564 ac->is_urgent = is_urgent;
2566 ret = ldb_build_mod_req(&down_req, ldb, ac,
2567 msg,
2568 req->controls,
2569 ac, replmd_op_callback,
2570 req);
2571 LDB_REQ_SET_LOCATION(down_req);
2572 if (ret != LDB_SUCCESS) {
2573 talloc_free(ac);
2574 return ret;
2577 /* current partition control is needed by "replmd_op_callback" */
2578 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2579 ret = ldb_request_add_control(down_req,
2580 DSDB_CONTROL_CURRENT_PARTITION_OID,
2581 false, NULL);
2582 if (ret != LDB_SUCCESS) {
2583 talloc_free(ac);
2584 return ret;
2588 /* If we are in functional level 2000, then
2589 * replmd_modify_handle_linked_attribs will have done
2590 * nothing */
2591 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2592 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2593 if (ret != LDB_SUCCESS) {
2594 talloc_free(ac);
2595 return ret;
2599 talloc_steal(down_req, msg);
2601 /* we only change whenChanged and uSNChanged if the seq_num
2602 has changed */
2603 if (ac->seq_num != 0) {
2604 ret = add_time_element(msg, "whenChanged", t);
2605 if (ret != LDB_SUCCESS) {
2606 talloc_free(ac);
2607 ldb_operr(ldb);
2608 return ret;
2611 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2612 if (ret != LDB_SUCCESS) {
2613 talloc_free(ac);
2614 ldb_operr(ldb);
2615 return ret;
2619 if (!ldb_dn_compare_base(replmd_private->schema_dn, msg->dn)) {
2620 /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
2621 msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
2622 if (msds_intid_struct) {
2623 msds_intid_struct->usn = ac->seq_num;
2627 /* go on with the call chain */
2628 return ldb_next_request(module, down_req);
2631 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2634 handle a rename request
2636 On a rename we need to do an extra ldb_modify which sets the
2637 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2639 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2641 struct ldb_context *ldb;
2642 struct replmd_replicated_request *ac;
2643 int ret;
2644 struct ldb_request *down_req;
2646 /* do not manipulate our control entries */
2647 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2648 return ldb_next_request(module, req);
2651 ldb = ldb_module_get_ctx(module);
2653 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2655 ac = replmd_ctx_init(module, req);
2656 if (ac == NULL) {
2657 return ldb_module_oom(module);
2660 ret = ldb_build_rename_req(&down_req, ldb, ac,
2661 ac->req->op.rename.olddn,
2662 ac->req->op.rename.newdn,
2663 ac->req->controls,
2664 ac, replmd_rename_callback,
2665 ac->req);
2666 LDB_REQ_SET_LOCATION(down_req);
2667 if (ret != LDB_SUCCESS) {
2668 talloc_free(ac);
2669 return ret;
2672 /* go on with the call chain */
2673 return ldb_next_request(module, down_req);
2676 /* After the rename is compleated, update the whenchanged etc */
2677 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2679 struct ldb_context *ldb;
2680 struct replmd_replicated_request *ac;
2681 struct ldb_request *down_req;
2682 struct ldb_message *msg;
2683 const struct dsdb_attribute *rdn_attr;
2684 const char *rdn_name;
2685 const struct ldb_val *rdn_val;
2686 const char *attrs[5] = { NULL, };
2687 time_t t = time(NULL);
2688 int ret;
2689 bool is_urgent = false, rodc = false;
2691 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2692 ldb = ldb_module_get_ctx(ac->module);
2694 if (ares->error != LDB_SUCCESS) {
2695 return ldb_module_done(ac->req, ares->controls,
2696 ares->response, ares->error);
2699 if (ares->type != LDB_REPLY_DONE) {
2700 ldb_set_errstring(ldb,
2701 "invalid ldb_reply_type in callback");
2702 talloc_free(ares);
2703 return ldb_module_done(ac->req, NULL, NULL,
2704 LDB_ERR_OPERATIONS_ERROR);
2707 /* TODO:
2708 * - replace the old object with the newly constructed one
2711 msg = ldb_msg_new(ac);
2712 if (msg == NULL) {
2713 ldb_oom(ldb);
2714 return LDB_ERR_OPERATIONS_ERROR;
2717 msg->dn = ac->req->op.rename.newdn;
2719 rdn_name = ldb_dn_get_rdn_name(msg->dn);
2720 if (rdn_name == NULL) {
2721 talloc_free(ares);
2722 return ldb_module_done(ac->req, NULL, NULL,
2723 ldb_operr(ldb));
2726 /* normalize the rdn attribute name */
2727 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
2728 if (rdn_attr == NULL) {
2729 talloc_free(ares);
2730 return ldb_module_done(ac->req, NULL, NULL,
2731 ldb_operr(ldb));
2733 rdn_name = rdn_attr->lDAPDisplayName;
2735 rdn_val = ldb_dn_get_rdn_val(msg->dn);
2736 if (rdn_val == NULL) {
2737 talloc_free(ares);
2738 return ldb_module_done(ac->req, NULL, NULL,
2739 ldb_operr(ldb));
2742 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2743 talloc_free(ares);
2744 return ldb_module_done(ac->req, NULL, NULL,
2745 ldb_oom(ldb));
2747 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
2748 talloc_free(ares);
2749 return ldb_module_done(ac->req, NULL, NULL,
2750 ldb_oom(ldb));
2752 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2753 talloc_free(ares);
2754 return ldb_module_done(ac->req, NULL, NULL,
2755 ldb_oom(ldb));
2757 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
2758 talloc_free(ares);
2759 return ldb_module_done(ac->req, NULL, NULL,
2760 ldb_oom(ldb));
2764 * here we let replmd_update_rpmd() only search for
2765 * the existing "replPropertyMetaData" and rdn_name attributes.
2767 * We do not want the existing "name" attribute as
2768 * the "name" attribute needs to get the version
2769 * updated on rename even if the rdn value hasn't changed.
2771 * This is the diff of the meta data, for a moved user
2772 * on a w2k8r2 server:
2774 * # record 1
2775 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
2776 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
2777 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
2778 * version : 0x00000001 (1)
2779 * reserved : 0x00000000 (0)
2780 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
2781 * local_usn : 0x00000000000037a5 (14245)
2782 * array: struct replPropertyMetaData1
2783 * attid : DRSUAPI_ATTID_name (0x90001)
2784 * - version : 0x00000001 (1)
2785 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
2786 * + version : 0x00000002 (2)
2787 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
2788 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
2789 * - originating_usn : 0x00000000000037a5 (14245)
2790 * - local_usn : 0x00000000000037a5 (14245)
2791 * + originating_usn : 0x0000000000003834 (14388)
2792 * + local_usn : 0x0000000000003834 (14388)
2793 * array: struct replPropertyMetaData1
2794 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
2795 * version : 0x00000004 (4)
2797 attrs[0] = "replPropertyMetaData";
2798 attrs[1] = "objectClass";
2799 attrs[2] = "instanceType";
2800 attrs[3] = rdn_name;
2801 attrs[4] = NULL;
2803 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
2804 msg, &ac->seq_num, t, &is_urgent, &rodc);
2805 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2806 struct ldb_dn *olddn = ac->req->op.rename.olddn;
2807 struct loadparm_context *lp_ctx;
2808 char *referral;
2810 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2811 struct loadparm_context);
2813 referral = talloc_asprintf(req,
2814 "ldap://%s/%s",
2815 lpcfg_dnsdomain(lp_ctx),
2816 ldb_dn_get_linearized(olddn));
2817 ret = ldb_module_send_referral(req, referral);
2818 talloc_free(ares);
2819 return ldb_module_done(req, NULL, NULL, ret);
2822 if (ret != LDB_SUCCESS) {
2823 talloc_free(ares);
2824 return ldb_module_done(ac->req, NULL, NULL, ret);
2827 if (ac->seq_num == 0) {
2828 talloc_free(ares);
2829 return ldb_module_done(ac->req, NULL, NULL,
2830 ldb_error(ldb, ret,
2831 "internal error seq_num == 0"));
2833 ac->is_urgent = is_urgent;
2835 ret = ldb_build_mod_req(&down_req, ldb, ac,
2836 msg,
2837 req->controls,
2838 ac, replmd_op_callback,
2839 req);
2840 LDB_REQ_SET_LOCATION(down_req);
2841 if (ret != LDB_SUCCESS) {
2842 talloc_free(ac);
2843 return ret;
2846 /* current partition control is needed by "replmd_op_callback" */
2847 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2848 ret = ldb_request_add_control(down_req,
2849 DSDB_CONTROL_CURRENT_PARTITION_OID,
2850 false, NULL);
2851 if (ret != LDB_SUCCESS) {
2852 talloc_free(ac);
2853 return ret;
2857 talloc_steal(down_req, msg);
2859 ret = add_time_element(msg, "whenChanged", t);
2860 if (ret != LDB_SUCCESS) {
2861 talloc_free(ac);
2862 ldb_operr(ldb);
2863 return ret;
2866 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2867 if (ret != LDB_SUCCESS) {
2868 talloc_free(ac);
2869 ldb_operr(ldb);
2870 return ret;
2873 /* go on with the call chain - do the modify after the rename */
2874 return ldb_next_request(ac->module, down_req);
2878 * remove links from objects that point at this object when an object
2879 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
2880 * RemoveObj which states that link removal due to the object being
2881 * deleted is NOT an originating update - they just go away!
2884 static int replmd_delete_remove_link(struct ldb_module *module,
2885 const struct dsdb_schema *schema,
2886 struct ldb_dn *dn,
2887 struct ldb_message_element *el,
2888 const struct dsdb_attribute *sa,
2889 struct ldb_request *parent)
2891 unsigned int i;
2892 TALLOC_CTX *tmp_ctx = talloc_new(module);
2893 struct ldb_context *ldb = ldb_module_get_ctx(module);
2895 for (i=0; i<el->num_values; i++) {
2896 struct dsdb_dn *dsdb_dn;
2897 NTSTATUS status;
2898 int ret;
2899 struct GUID guid2;
2900 struct ldb_message *msg;
2901 const struct dsdb_attribute *target_attr;
2902 struct ldb_message_element *el2;
2903 struct ldb_val dn_val;
2905 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2906 continue;
2909 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2910 if (!dsdb_dn) {
2911 talloc_free(tmp_ctx);
2912 return LDB_ERR_OPERATIONS_ERROR;
2915 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2916 if (!NT_STATUS_IS_OK(status)) {
2917 talloc_free(tmp_ctx);
2918 return LDB_ERR_OPERATIONS_ERROR;
2921 /* remove the link */
2922 msg = ldb_msg_new(tmp_ctx);
2923 if (!msg) {
2924 ldb_module_oom(module);
2925 talloc_free(tmp_ctx);
2926 return LDB_ERR_OPERATIONS_ERROR;
2930 msg->dn = dsdb_dn->dn;
2932 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2933 if (target_attr == NULL) {
2934 continue;
2937 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2938 if (ret != LDB_SUCCESS) {
2939 ldb_module_oom(module);
2940 talloc_free(tmp_ctx);
2941 return LDB_ERR_OPERATIONS_ERROR;
2943 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2944 el2->values = &dn_val;
2945 el2->num_values = 1;
2947 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, parent);
2948 if (ret != LDB_SUCCESS) {
2949 talloc_free(tmp_ctx);
2950 return ret;
2953 talloc_free(tmp_ctx);
2954 return LDB_SUCCESS;
2959 handle update of replication meta data for deletion of objects
2961 This also handles the mapping of delete to a rename operation
2962 to allow deletes to be replicated.
2964 It also handles the incoming deleted objects, to ensure they are
2965 fully deleted here. In that case re_delete is true, and we do not
2966 use this as a signal to change the deleted state, just reinforce it.
2969 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
2971 int ret = LDB_ERR_OTHER;
2972 bool retb, disallow_move_on_delete;
2973 struct ldb_dn *old_dn, *new_dn;
2974 const char *rdn_name;
2975 const struct ldb_val *rdn_value, *new_rdn_value;
2976 struct GUID guid;
2977 struct ldb_context *ldb = ldb_module_get_ctx(module);
2978 const struct dsdb_schema *schema;
2979 struct ldb_message *msg, *old_msg;
2980 struct ldb_message_element *el;
2981 TALLOC_CTX *tmp_ctx;
2982 struct ldb_result *res, *parent_res;
2983 const char *preserved_attrs[] = {
2984 /* yes, this really is a hard coded list. See MS-ADTS
2985 section 3.1.1.5.5.1.1 */
2986 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2987 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2988 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2989 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2990 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2991 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2992 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2993 "whenChanged", NULL};
2994 unsigned int i, el_count = 0;
2995 enum deletion_state deletion_state, next_deletion_state;
2997 if (ldb_dn_is_special(req->op.del.dn)) {
2998 return ldb_next_request(module, req);
3002 * We have to allow dbcheck to remove an object that
3003 * is beyond repair, and to do so totally. This could
3004 * mean we we can get a partial object from the other
3005 * DC, causing havoc, so dbcheck suggests
3006 * re-replication first. dbcheck sets both DBCHECK
3007 * and RELAX in this situation.
3009 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
3010 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
3011 /* really, really remove it */
3012 return ldb_next_request(module, req);
3015 tmp_ctx = talloc_new(ldb);
3016 if (!tmp_ctx) {
3017 ldb_oom(ldb);
3018 return LDB_ERR_OPERATIONS_ERROR;
3021 schema = dsdb_get_schema(ldb, tmp_ctx);
3022 if (!schema) {
3023 talloc_free(tmp_ctx);
3024 return LDB_ERR_OPERATIONS_ERROR;
3027 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
3029 /* we need the complete msg off disk, so we can work out which
3030 attributes need to be removed */
3031 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
3032 DSDB_FLAG_NEXT_MODULE |
3033 DSDB_SEARCH_SHOW_RECYCLED |
3034 DSDB_SEARCH_REVEAL_INTERNALS |
3035 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
3036 if (ret != LDB_SUCCESS) {
3037 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3038 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
3039 re_delete ? "re-delete" : "delete",
3040 ldb_dn_get_linearized(old_dn),
3041 ldb_errstring(ldb_module_get_ctx(module)));
3042 talloc_free(tmp_ctx);
3043 return ret;
3045 old_msg = res->msgs[0];
3047 replmd_deletion_state(module, old_msg,
3048 &deletion_state,
3049 &next_deletion_state);
3051 /* This supports us noticing an incoming isDeleted and acting on it */
3052 if (re_delete) {
3053 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
3054 next_deletion_state = deletion_state;
3057 if (next_deletion_state == OBJECT_REMOVED) {
3059 * We have to prevent objects being deleted, even if
3060 * the administrator really wants them gone, as
3061 * without the tombstone, we can get a partial object
3062 * from the other DC, causing havoc.
3064 * The only other valid case is when the 180 day
3065 * timeout has expired, when relax is specified.
3067 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
3068 /* it is already deleted - really remove it this time */
3069 talloc_free(tmp_ctx);
3070 return ldb_next_request(module, req);
3073 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
3074 "This check is to prevent corruption of the replicated state.",
3075 ldb_dn_get_linearized(old_msg->dn));
3076 return LDB_ERR_UNWILLING_TO_PERFORM;
3079 rdn_name = ldb_dn_get_rdn_name(old_dn);
3080 rdn_value = ldb_dn_get_rdn_val(old_dn);
3081 if ((rdn_name == NULL) || (rdn_value == NULL)) {
3082 talloc_free(tmp_ctx);
3083 return ldb_operr(ldb);
3086 msg = ldb_msg_new(tmp_ctx);
3087 if (msg == NULL) {
3088 ldb_module_oom(module);
3089 talloc_free(tmp_ctx);
3090 return LDB_ERR_OPERATIONS_ERROR;
3093 msg->dn = old_dn;
3095 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
3096 disallow_move_on_delete =
3097 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
3098 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
3100 /* work out where we will be renaming this object to */
3101 if (!disallow_move_on_delete) {
3102 struct ldb_dn *deleted_objects_dn;
3103 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
3104 &deleted_objects_dn);
3107 * We should not move objects if we can't find the
3108 * deleted objects DN. Not moving (or otherwise
3109 * harming) the Deleted Objects DN itself is handled
3110 * in the caller.
3112 if (re_delete && (ret != LDB_SUCCESS)) {
3113 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3114 if (new_dn == NULL) {
3115 ldb_module_oom(module);
3116 talloc_free(tmp_ctx);
3117 return LDB_ERR_OPERATIONS_ERROR;
3119 } else if (ret != LDB_SUCCESS) {
3120 /* this is probably an attempted delete on a partition
3121 * that doesn't allow delete operations, such as the
3122 * schema partition */
3123 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
3124 ldb_dn_get_linearized(old_dn));
3125 talloc_free(tmp_ctx);
3126 return LDB_ERR_UNWILLING_TO_PERFORM;
3127 } else {
3128 new_dn = deleted_objects_dn;
3130 } else {
3131 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3132 if (new_dn == NULL) {
3133 ldb_module_oom(module);
3134 talloc_free(tmp_ctx);
3135 return LDB_ERR_OPERATIONS_ERROR;
3139 if (deletion_state == OBJECT_NOT_DELETED) {
3140 /* get the objects GUID from the search we just did */
3141 guid = samdb_result_guid(old_msg, "objectGUID");
3143 /* Add a formatted child */
3144 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
3145 rdn_name,
3146 ldb_dn_escape_value(tmp_ctx, *rdn_value),
3147 GUID_string(tmp_ctx, &guid));
3148 if (!retb) {
3149 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
3150 ldb_dn_get_linearized(new_dn)));
3151 talloc_free(tmp_ctx);
3152 return LDB_ERR_OPERATIONS_ERROR;
3155 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
3156 if (ret != LDB_SUCCESS) {
3157 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
3158 ldb_module_oom(module);
3159 talloc_free(tmp_ctx);
3160 return ret;
3162 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3163 } else {
3165 * No matter what has happened with other renames etc, try again to
3166 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
3169 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
3170 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
3171 if (!retb) {
3172 DEBUG(0,(__location__ ": Unable to add a prepare rdn of %s",
3173 ldb_dn_get_linearized(rdn)));
3174 talloc_free(tmp_ctx);
3175 return LDB_ERR_OPERATIONS_ERROR;
3177 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
3179 retb = ldb_dn_add_child(new_dn, rdn);
3180 if (!retb) {
3181 DEBUG(0,(__location__ ": Unable to add rdn %s to base dn: %s",
3182 ldb_dn_get_linearized(rdn),
3183 ldb_dn_get_linearized(new_dn)));
3184 talloc_free(tmp_ctx);
3185 return LDB_ERR_OPERATIONS_ERROR;
3190 now we need to modify the object in the following ways:
3192 - add isDeleted=TRUE
3193 - update rDN and name, with new rDN
3194 - remove linked attributes
3195 - remove objectCategory and sAMAccountType
3196 - remove attribs not on the preserved list
3197 - preserved if in above list, or is rDN
3198 - remove all linked attribs from this object
3199 - remove all links from other objects to this object
3200 - add lastKnownParent
3201 - update replPropertyMetaData?
3203 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
3206 if (deletion_state == OBJECT_NOT_DELETED) {
3207 /* we need the storage form of the parent GUID */
3208 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
3209 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
3210 DSDB_FLAG_NEXT_MODULE |
3211 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3212 DSDB_SEARCH_REVEAL_INTERNALS|
3213 DSDB_SEARCH_SHOW_RECYCLED, req);
3214 if (ret != LDB_SUCCESS) {
3215 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3216 "repmd_delete: Failed to %s %s, because we failed to find it's parent (%s): %s",
3217 re_delete ? "re-delete" : "delete",
3218 ldb_dn_get_linearized(old_dn),
3219 ldb_dn_get_linearized(ldb_dn_get_parent(tmp_ctx, old_dn)),
3220 ldb_errstring(ldb_module_get_ctx(module)));
3221 talloc_free(tmp_ctx);
3222 return ret;
3225 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
3226 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
3227 if (ret != LDB_SUCCESS) {
3228 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
3229 ldb_module_oom(module);
3230 talloc_free(tmp_ctx);
3231 return ret;
3233 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3235 if (next_deletion_state == OBJECT_DELETED) {
3236 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
3237 if (ret != LDB_SUCCESS) {
3238 DEBUG(0,(__location__ ": Failed to add msDS-LastKnownRDN string to the msg\n"));
3239 ldb_module_oom(module);
3240 talloc_free(tmp_ctx);
3241 return ret;
3243 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
3247 switch (next_deletion_state) {
3249 case OBJECT_RECYCLED:
3250 case OBJECT_TOMBSTONE:
3253 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
3254 * describes what must be removed from a tombstone
3255 * object
3257 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
3258 * describes what must be removed from a recycled
3259 * object
3264 * we also mark it as recycled, meaning this object can't be
3265 * recovered (we are stripping its attributes).
3266 * This is done only if we have this schema object of course ...
3267 * This behavior is identical to the one of Windows 2008R2 which
3268 * always set the isRecycled attribute, even if the recycle-bin is
3269 * not activated and what ever the forest level is.
3271 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
3272 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
3273 if (ret != LDB_SUCCESS) {
3274 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
3275 ldb_module_oom(module);
3276 talloc_free(tmp_ctx);
3277 return ret;
3279 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3282 /* work out which of the old attributes we will be removing */
3283 for (i=0; i<old_msg->num_elements; i++) {
3284 const struct dsdb_attribute *sa;
3285 el = &old_msg->elements[i];
3286 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3287 if (!sa) {
3288 talloc_free(tmp_ctx);
3289 return LDB_ERR_OPERATIONS_ERROR;
3291 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
3292 /* don't remove the rDN */
3293 continue;
3295 if (sa->linkID && (sa->linkID & 1)) {
3297 we have a backlink in this object
3298 that needs to be removed. We're not
3299 allowed to remove it directly
3300 however, so we instead setup a
3301 modify to delete the corresponding
3302 forward link
3304 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
3305 if (ret != LDB_SUCCESS) {
3306 talloc_free(tmp_ctx);
3307 return LDB_ERR_OPERATIONS_ERROR;
3309 /* now we continue, which means we
3310 won't remove this backlink
3311 directly
3313 continue;
3315 if (!sa->linkID) {
3316 if (ldb_attr_in_list(preserved_attrs, el->name)) {
3317 continue;
3319 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
3320 continue;
3323 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
3324 if (ret != LDB_SUCCESS) {
3325 talloc_free(tmp_ctx);
3326 ldb_module_oom(module);
3327 return ret;
3331 /* Duplicate with the below - we remove the
3332 * samAccountType as an originating update, in case it
3333 * somehow came back. The objectCategory will have
3334 * gone in the above */
3335 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
3336 if (ret != LDB_SUCCESS) {
3337 talloc_free(tmp_ctx);
3338 ldb_module_oom(module);
3339 return ret;
3342 break;
3344 case OBJECT_DELETED:
3346 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
3347 * describes what must be removed from a deleted
3348 * object
3351 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
3352 if (ret != LDB_SUCCESS) {
3353 talloc_free(tmp_ctx);
3354 ldb_module_oom(module);
3355 return ret;
3358 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
3359 if (ret != LDB_SUCCESS) {
3360 talloc_free(tmp_ctx);
3361 ldb_module_oom(module);
3362 return ret;
3365 break;
3367 default:
3368 break;
3371 if (deletion_state == OBJECT_NOT_DELETED) {
3372 const struct dsdb_attribute *sa;
3374 /* work out what the new rdn value is, for updating the
3375 rDN and name fields */
3376 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
3377 if (new_rdn_value == NULL) {
3378 talloc_free(tmp_ctx);
3379 return ldb_operr(ldb);
3382 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
3383 if (!sa) {
3384 talloc_free(tmp_ctx);
3385 return LDB_ERR_OPERATIONS_ERROR;
3388 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
3389 &el);
3390 if (ret != LDB_SUCCESS) {
3391 talloc_free(tmp_ctx);
3392 return ret;
3394 el->flags = LDB_FLAG_MOD_REPLACE;
3396 el = ldb_msg_find_element(old_msg, "name");
3397 if (el) {
3398 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
3399 if (ret != LDB_SUCCESS) {
3400 talloc_free(tmp_ctx);
3401 return ret;
3403 el->flags = LDB_FLAG_MOD_REPLACE;
3408 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
3412 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req);
3413 if (ret != LDB_SUCCESS) {
3414 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
3415 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
3416 talloc_free(tmp_ctx);
3417 return ret;
3421 * No matter what has happned with other renames, try again to
3422 * get this to be under the deleted DN.
3424 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
3425 /* now rename onto the new DN */
3426 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
3427 if (ret != LDB_SUCCESS){
3428 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
3429 ldb_dn_get_linearized(old_dn),
3430 ldb_dn_get_linearized(new_dn),
3431 ldb_errstring(ldb)));
3432 talloc_free(tmp_ctx);
3433 return ret;
3437 talloc_free(tmp_ctx);
3439 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
3442 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
3444 return replmd_delete_internals(module, req, false);
3448 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
3450 return ret;
3453 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
3455 int ret = LDB_ERR_OTHER;
3456 /* TODO: do some error mapping */
3457 return ret;
3461 static struct replPropertyMetaData1 *
3462 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
3463 enum drsuapi_DsAttributeId attid)
3465 uint32_t i;
3466 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
3468 for (i = 0; i < rpmd_ctr->count; i++) {
3469 if (rpmd_ctr->array[i].attid == attid) {
3470 return &rpmd_ctr->array[i];
3473 return NULL;
3478 return true if an update is newer than an existing entry
3479 see section 5.11 of MS-ADTS
3481 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
3482 const struct GUID *update_invocation_id,
3483 uint32_t current_version,
3484 uint32_t update_version,
3485 NTTIME current_change_time,
3486 NTTIME update_change_time)
3488 if (update_version != current_version) {
3489 return update_version > current_version;
3491 if (update_change_time != current_change_time) {
3492 return update_change_time > current_change_time;
3494 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
3497 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
3498 struct replPropertyMetaData1 *new_m)
3500 return replmd_update_is_newer(&cur_m->originating_invocation_id,
3501 &new_m->originating_invocation_id,
3502 cur_m->version,
3503 new_m->version,
3504 cur_m->originating_change_time,
3505 new_m->originating_change_time);
3510 form a conflict DN
3512 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
3514 const struct ldb_val *rdn_val;
3515 const char *rdn_name;
3516 struct ldb_dn *new_dn;
3518 rdn_val = ldb_dn_get_rdn_val(dn);
3519 rdn_name = ldb_dn_get_rdn_name(dn);
3520 if (!rdn_val || !rdn_name) {
3521 return NULL;
3524 new_dn = ldb_dn_copy(mem_ctx, dn);
3525 if (!new_dn) {
3526 return NULL;
3529 if (!ldb_dn_remove_child_components(new_dn, 1)) {
3530 return NULL;
3533 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
3534 rdn_name,
3535 ldb_dn_escape_value(new_dn, *rdn_val),
3536 GUID_string(new_dn, guid))) {
3537 return NULL;
3540 return new_dn;
3545 perform a modify operation which sets the rDN and name attributes to
3546 their current values. This has the effect of changing these
3547 attributes to have been last updated by the current DC. This is
3548 needed to ensure that renames performed as part of conflict
3549 resolution are propogated to other DCs
3551 static int replmd_name_modify(struct replmd_replicated_request *ar,
3552 struct ldb_request *req, struct ldb_dn *dn)
3554 struct ldb_message *msg;
3555 const char *rdn_name;
3556 const struct ldb_val *rdn_val;
3557 const struct dsdb_attribute *rdn_attr;
3558 int ret;
3560 msg = ldb_msg_new(req);
3561 if (msg == NULL) {
3562 goto failed;
3564 msg->dn = dn;
3566 rdn_name = ldb_dn_get_rdn_name(dn);
3567 if (rdn_name == NULL) {
3568 goto failed;
3571 /* normalize the rdn attribute name */
3572 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3573 if (rdn_attr == NULL) {
3574 goto failed;
3576 rdn_name = rdn_attr->lDAPDisplayName;
3578 rdn_val = ldb_dn_get_rdn_val(dn);
3579 if (rdn_val == NULL) {
3580 goto failed;
3583 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3584 goto failed;
3586 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3587 goto failed;
3589 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3590 goto failed;
3592 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3593 goto failed;
3596 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3597 if (ret != LDB_SUCCESS) {
3598 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
3599 ldb_dn_get_linearized(dn),
3600 ldb_errstring(ldb_module_get_ctx(ar->module))));
3601 return ret;
3604 talloc_free(msg);
3606 return LDB_SUCCESS;
3608 failed:
3609 talloc_free(msg);
3610 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
3611 ldb_dn_get_linearized(dn)));
3612 return LDB_ERR_OPERATIONS_ERROR;
3617 callback for conflict DN handling where we have renamed the incoming
3618 record. After renaming it, we need to ensure the change of name and
3619 rDN for the incoming record is seen as an originating update by this DC.
3621 This also handles updating lastKnownParent for entries sent to lostAndFound
3623 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3625 struct replmd_replicated_request *ar =
3626 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3627 struct ldb_dn *conflict_dn;
3628 int ret;
3630 if (ares->error != LDB_SUCCESS) {
3631 /* call the normal callback for everything except success */
3632 return replmd_op_callback(req, ares);
3635 switch (req->operation) {
3636 case LDB_ADD:
3637 conflict_dn = req->op.add.message->dn;
3638 break;
3639 case LDB_MODIFY:
3640 conflict_dn = req->op.mod.message->dn;
3641 break;
3642 default:
3643 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
3646 /* perform a modify of the rDN and name of the record */
3647 ret = replmd_name_modify(ar, req, conflict_dn);
3648 if (ret != LDB_SUCCESS) {
3649 ares->error = ret;
3650 return replmd_op_callback(req, ares);
3653 if (ar->objs->objects[ar->index_current].last_known_parent) {
3654 struct ldb_message *msg = ldb_msg_new(req);
3655 if (msg == NULL) {
3656 ldb_module_oom(ar->module);
3657 return LDB_ERR_OPERATIONS_ERROR;
3660 msg->dn = req->op.add.message->dn;
3662 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
3663 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
3664 if (ret != LDB_SUCCESS) {
3665 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
3666 ldb_module_oom(ar->module);
3667 return ret;
3669 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
3671 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3672 if (ret != LDB_SUCCESS) {
3673 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
3674 ldb_dn_get_linearized(msg->dn),
3675 ldb_errstring(ldb_module_get_ctx(ar->module))));
3676 return ret;
3678 TALLOC_FREE(msg);
3681 return replmd_op_callback(req, ares);
3685 callback for replmd_replicated_apply_add() and replmd_replicated_handle_rename()
3686 This copes with the creation of conflict records in the case where
3687 the DN exists, but with a different objectGUID
3689 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))
3691 struct ldb_dn *conflict_dn;
3692 struct replmd_replicated_request *ar =
3693 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3694 struct ldb_result *res;
3695 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
3696 int ret;
3697 const struct ldb_val *omd_value;
3698 struct replPropertyMetaDataBlob omd, *rmd;
3699 enum ndr_err_code ndr_err;
3700 bool rename_incoming_record, rodc;
3701 struct replPropertyMetaData1 *rmd_name, *omd_name;
3702 struct ldb_message *msg;
3704 req->callback = callback;
3706 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
3707 /* call the normal callback for everything except
3708 conflicts */
3709 return ldb_module_done(req, ares->controls, ares->response, ares->error);
3712 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
3713 if (ret != LDB_SUCCESS) {
3714 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)));
3715 return ldb_module_done(req, ares->controls, ares->response, LDB_ERR_OPERATIONS_ERROR);
3718 * we have a conflict, and need to decide if we will keep the
3719 * new record or the old record
3722 msg = ar->objs->objects[ar->index_current].msg;
3724 switch (req->operation) {
3725 case LDB_ADD:
3726 conflict_dn = msg->dn;
3727 break;
3728 case LDB_RENAME:
3729 conflict_dn = req->op.rename.newdn;
3730 break;
3731 default:
3732 return ldb_module_done(req, ares->controls, ares->response, ldb_module_operr(ar->module));
3735 if (rodc) {
3737 * We are on an RODC, or were a GC for this
3738 * partition, so we have to fail this until
3739 * someone who owns the partition sorts it
3740 * out
3742 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3743 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
3744 " - We must fail the operation until a master for this partition resolves the conflict",
3745 ldb_dn_get_linearized(conflict_dn));
3746 goto failed;
3750 * first we need the replPropertyMetaData attribute from the
3751 * old record
3753 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
3754 attrs,
3755 DSDB_FLAG_NEXT_MODULE |
3756 DSDB_SEARCH_SHOW_DELETED |
3757 DSDB_SEARCH_SHOW_RECYCLED, req);
3758 if (ret != LDB_SUCCESS) {
3759 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
3760 ldb_dn_get_linearized(conflict_dn)));
3761 goto failed;
3764 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
3765 if (omd_value == NULL) {
3766 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
3767 ldb_dn_get_linearized(conflict_dn)));
3768 goto failed;
3771 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
3772 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3773 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3774 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
3775 ldb_dn_get_linearized(conflict_dn)));
3776 goto failed;
3779 rmd = ar->objs->objects[ar->index_current].meta_data;
3781 /* we decide which is newer based on the RPMD on the name
3782 attribute. See [MS-DRSR] ResolveNameConflict */
3783 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
3784 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
3785 if (!rmd_name || !omd_name) {
3786 DEBUG(0,(__location__ ": Failed to find name attribute in replPropertyMetaData for %s\n",
3787 ldb_dn_get_linearized(conflict_dn)));
3788 goto failed;
3791 rename_incoming_record = !(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) &&
3792 !replmd_replPropertyMetaData1_is_newer(omd_name, rmd_name);
3794 if (rename_incoming_record) {
3795 struct GUID guid;
3796 struct ldb_dn *new_dn;
3799 * We want to run the original callback here, which
3800 * will return LDB_ERR_ENTRY_ALREADY_EXISTS to the
3801 * caller, which will in turn know to rename the
3802 * incoming record. The error string is set in case
3803 * this isn't handled properly at some point in the
3804 * future.
3806 if (req->operation == LDB_RENAME) {
3807 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3808 "Unable to handle incoming renames where this would "
3809 "create a conflict. Incoming record is %s (caller to handle)\n",
3810 ldb_dn_get_extended_linearized(req, conflict_dn, 1));
3812 goto failed;
3815 guid = samdb_result_guid(msg, "objectGUID");
3816 if (GUID_all_zero(&guid)) {
3817 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
3818 ldb_dn_get_linearized(conflict_dn)));
3819 goto failed;
3821 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3822 if (new_dn == NULL) {
3823 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3824 ldb_dn_get_linearized(conflict_dn)));
3825 goto failed;
3828 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
3829 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3831 /* re-submit the request, but with a different
3832 callback, so we don't loop forever. */
3833 msg->dn = new_dn;
3834 req->callback = replmd_op_name_modify_callback;
3836 return ldb_next_request(ar->module, req);
3837 } else {
3838 /* we are renaming the existing record */
3839 struct GUID guid;
3840 struct ldb_dn *new_dn;
3842 guid = samdb_result_guid(res->msgs[0], "objectGUID");
3843 if (GUID_all_zero(&guid)) {
3844 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
3845 ldb_dn_get_linearized(conflict_dn)));
3846 goto failed;
3849 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3850 if (new_dn == NULL) {
3851 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3852 ldb_dn_get_linearized(conflict_dn)));
3853 goto failed;
3856 DEBUG(2,(__location__ ": Resolving conflict record via existing rename '%s' -> '%s'\n",
3857 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3859 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
3860 DSDB_FLAG_OWN_MODULE, req);
3861 if (ret != LDB_SUCCESS) {
3862 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
3863 ldb_dn_get_linearized(conflict_dn),
3864 ldb_dn_get_linearized(new_dn),
3865 ldb_errstring(ldb_module_get_ctx(ar->module))));
3866 goto failed;
3870 * now we need to ensure that the rename is seen as an
3871 * originating update. We do that with a modify.
3873 ret = replmd_name_modify(ar, req, new_dn);
3874 if (ret != LDB_SUCCESS) {
3875 goto failed;
3878 return ldb_next_request(ar->module, req);
3881 failed:
3882 /* on failure do the original callback. This means replication
3883 * will stop with an error, but there is not much else we can
3884 * do
3886 return ldb_module_done(req, ares->controls, ares->response, ares->error);
3890 callback for replmd_replicated_apply_add()
3891 This copes with the creation of conflict records in the case where
3892 the DN exists, but with a different objectGUID
3894 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
3896 struct replmd_replicated_request *ar =
3897 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3899 if (ar->objs->objects[ar->index_current].last_known_parent) {
3900 /* This is like a conflict DN, where we put the object in LostAndFound
3901 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
3902 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
3905 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
3909 callback for replmd_replicated_handle_rename()
3910 This copes with the creation of conflict records in the case where
3911 the DN exists, but with a different objectGUID
3913 static int replmd_op_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3915 return replmd_op_possible_conflict_callback(req, ares, ldb_modify_default_callback);
3919 this is called when a new object comes in over DRS
3921 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
3923 struct ldb_context *ldb;
3924 struct ldb_request *change_req;
3925 enum ndr_err_code ndr_err;
3926 struct ldb_message *msg;
3927 struct replPropertyMetaDataBlob *md;
3928 struct ldb_val md_value;
3929 unsigned int i;
3930 int ret;
3931 bool remote_isDeleted = false;
3932 const struct dsdb_attribute *rdn_sa;
3933 const char *rdn_name;
3935 ldb = ldb_module_get_ctx(ar->module);
3936 msg = ar->objs->objects[ar->index_current].msg;
3937 md = ar->objs->objects[ar->index_current].meta_data;
3939 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3940 if (ret != LDB_SUCCESS) {
3941 return replmd_replicated_request_error(ar, ret);
3944 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
3945 if (ret != LDB_SUCCESS) {
3946 return replmd_replicated_request_error(ar, ret);
3949 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
3950 if (ret != LDB_SUCCESS) {
3951 return replmd_replicated_request_error(ar, ret);
3954 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
3955 if (ret != LDB_SUCCESS) {
3956 return replmd_replicated_request_error(ar, ret);
3959 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
3960 if (ret != LDB_SUCCESS) {
3961 return replmd_replicated_request_error(ar, ret);
3964 /* remove any message elements that have zero values */
3965 for (i=0; i<msg->num_elements; i++) {
3966 struct ldb_message_element *el = &msg->elements[i];
3968 if (el->num_values == 0) {
3969 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
3970 ldb_asprintf_errstring(ldb, __location__
3971 ": empty objectClass sent on %s, aborting replication\n",
3972 ldb_dn_get_linearized(msg->dn));
3973 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
3976 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
3977 el->name));
3978 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
3979 msg->num_elements--;
3980 i--;
3981 continue;
3985 if (DEBUGLVL(4)) {
3986 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
3987 DEBUG(4, ("DRS replication add message:\n%s\n", s));
3988 talloc_free(s);
3991 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
3992 "isDeleted", false);
3995 * the meta data array is already sorted by the caller
3998 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3999 if (rdn_name == NULL) {
4000 ldb_asprintf_errstring(ldb, __location__ ": No rDN for %s?\n", ldb_dn_get_linearized(msg->dn));
4001 return replmd_replicated_request_error(ar, LDB_ERR_INVALID_DN_SYNTAX);
4004 rdn_sa = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
4005 if (rdn_sa == NULL) {
4006 ldb_asprintf_errstring(ldb, ": No schema attribute found for rDN %s for %s\n",
4007 rdn_name, ldb_dn_get_linearized(msg->dn));
4008 return replmd_replicated_request_error(ar, LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE);
4011 ret = replmd_replPropertyMetaDataCtr1_verify(ldb, &md->ctr.ctr1, rdn_sa, msg->dn);
4012 if (ret != LDB_SUCCESS) {
4013 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
4014 return replmd_replicated_request_error(ar, ret);
4017 for (i=0; i < md->ctr.ctr1.count; i++) {
4018 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
4020 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
4021 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
4022 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4023 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4024 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4026 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
4027 if (ret != LDB_SUCCESS) {
4028 return replmd_replicated_request_error(ar, ret);
4031 replmd_ldb_message_sort(msg, ar->schema);
4033 if (!remote_isDeleted) {
4034 ret = dsdb_module_schedule_sd_propagation(ar->module,
4035 ar->objs->partition_dn,
4036 msg->dn, true);
4037 if (ret != LDB_SUCCESS) {
4038 return replmd_replicated_request_error(ar, ret);
4042 ar->isDeleted = remote_isDeleted;
4044 ret = ldb_build_add_req(&change_req,
4045 ldb,
4047 msg,
4048 ar->controls,
4050 replmd_op_add_callback,
4051 ar->req);
4052 LDB_REQ_SET_LOCATION(change_req);
4053 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4055 /* current partition control needed by "repmd_op_callback" */
4056 ret = ldb_request_add_control(change_req,
4057 DSDB_CONTROL_CURRENT_PARTITION_OID,
4058 false, NULL);
4059 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4061 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4062 /* this tells the partition module to make it a
4063 partial replica if creating an NC */
4064 ret = ldb_request_add_control(change_req,
4065 DSDB_CONTROL_PARTIAL_REPLICA,
4066 false, NULL);
4067 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4070 return ldb_next_request(ar->module, change_req);
4073 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
4074 struct ldb_reply *ares)
4076 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4077 struct replmd_replicated_request);
4078 int ret;
4080 if (!ares) {
4081 return ldb_module_done(ar->req, NULL, NULL,
4082 LDB_ERR_OPERATIONS_ERROR);
4084 if (ares->error != LDB_SUCCESS &&
4085 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4087 * TODO: deal with the above error that the parent object doesn't exist
4090 return ldb_module_done(ar->req, ares->controls,
4091 ares->response, ares->error);
4094 switch (ares->type) {
4095 case LDB_REPLY_ENTRY:
4097 struct ldb_message *parent_msg = ares->message;
4098 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
4099 struct ldb_dn *parent_dn;
4100 int comp_num;
4102 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
4103 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
4104 /* Per MS-DRSR 4.1.10.6.10
4105 * FindBestParentObject we need to move this
4106 * new object under a deleted object to
4107 * lost-and-found */
4108 struct ldb_dn *nc_root;
4110 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
4111 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4112 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4113 "No suitable NC root found for %s. "
4114 "We need to move this object because parent object %s "
4115 "is deleted, but this object is not.",
4116 ldb_dn_get_linearized(msg->dn),
4117 ldb_dn_get_linearized(parent_msg->dn));
4118 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4119 } else if (ret != LDB_SUCCESS) {
4120 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4121 "Unable to find NC root for %s: %s. "
4122 "We need to move this object because parent object %s "
4123 "is deleted, but this object is not.",
4124 ldb_dn_get_linearized(msg->dn),
4125 ldb_errstring(ldb_module_get_ctx(ar->module)),
4126 ldb_dn_get_linearized(parent_msg->dn));
4127 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4130 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
4131 nc_root,
4132 DS_GUID_LOSTANDFOUND_CONTAINER,
4133 &parent_dn);
4134 if (ret != LDB_SUCCESS) {
4135 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4136 "Unable to find LostAndFound Container for %s "
4137 "in partition %s: %s. "
4138 "We need to move this object because parent object %s "
4139 "is deleted, but this object is not.",
4140 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
4141 ldb_errstring(ldb_module_get_ctx(ar->module)),
4142 ldb_dn_get_linearized(parent_msg->dn));
4143 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4145 ar->objs->objects[ar->index_current].last_known_parent
4146 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4147 } else {
4148 parent_dn = parent_msg->dn;
4151 comp_num = ldb_dn_get_comp_num(msg->dn);
4152 if (comp_num > 1) {
4153 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
4154 talloc_free(ares);
4155 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4158 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
4159 talloc_free(ares);
4160 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4162 break;
4164 case LDB_REPLY_REFERRAL:
4165 /* we ignore referrals */
4166 break;
4168 case LDB_REPLY_DONE:
4169 if (ar->search_msg != NULL) {
4170 ret = replmd_replicated_apply_merge(ar);
4171 } else {
4172 ret = replmd_replicated_apply_add(ar);
4174 if (ret != LDB_SUCCESS) {
4175 return ldb_module_done(ar->req, NULL, NULL, ret);
4179 talloc_free(ares);
4180 return LDB_SUCCESS;
4184 * Look for the parent object, so we put the new object in the right
4185 * place This is akin to NameObject in MS-DRSR - this routine and the
4186 * callbacks find the right parent name, and correct name for this
4187 * object
4190 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
4192 struct ldb_context *ldb;
4193 int ret;
4194 char *tmp_str;
4195 char *filter;
4196 struct ldb_request *search_req;
4197 static const char *attrs[] = {"isDeleted", NULL};
4199 ldb = ldb_module_get_ctx(ar->module);
4201 if (!ar->objs->objects[ar->index_current].parent_guid_value.data) {
4202 if (ar->search_msg != NULL) {
4203 return replmd_replicated_apply_merge(ar);
4204 } else {
4205 return replmd_replicated_apply_add(ar);
4209 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].parent_guid_value);
4210 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4212 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
4213 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4214 talloc_free(tmp_str);
4216 ret = ldb_build_search_req(&search_req,
4217 ldb,
4219 ar->objs->partition_dn,
4220 LDB_SCOPE_SUBTREE,
4221 filter,
4222 attrs,
4223 NULL,
4225 replmd_replicated_apply_search_for_parent_callback,
4226 ar->req);
4227 LDB_REQ_SET_LOCATION(search_req);
4229 ret = dsdb_request_add_controls(search_req,
4230 DSDB_SEARCH_SHOW_RECYCLED|
4231 DSDB_SEARCH_SHOW_DELETED|
4232 DSDB_SEARCH_SHOW_EXTENDED_DN);
4233 if (ret != LDB_SUCCESS) {
4234 return ret;
4237 return ldb_next_request(ar->module, search_req);
4241 handle renames that come in over DRS replication
4243 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
4244 struct ldb_message *msg,
4245 struct ldb_request *parent)
4247 struct ldb_request *req;
4248 int ret;
4249 TALLOC_CTX *tmp_ctx = talloc_new(msg);
4250 struct ldb_result *res;
4252 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
4253 ldb_dn_get_linearized(ar->search_msg->dn),
4254 ldb_dn_get_linearized(msg->dn)));
4257 res = talloc_zero(tmp_ctx, struct ldb_result);
4258 if (!res) {
4259 talloc_free(tmp_ctx);
4260 return ldb_oom(ldb_module_get_ctx(ar->module));
4263 /* pass rename to the next module
4264 * so it doesn't appear as an originating update */
4265 ret = ldb_build_rename_req(&req, ldb_module_get_ctx(ar->module), tmp_ctx,
4266 ar->search_msg->dn, msg->dn,
4267 NULL,
4269 replmd_op_rename_callback,
4270 parent);
4271 LDB_REQ_SET_LOCATION(req);
4272 if (ret != LDB_SUCCESS) {
4273 talloc_free(tmp_ctx);
4274 return ret;
4277 ret = dsdb_request_add_controls(req, DSDB_MODIFY_RELAX);
4278 if (ret != LDB_SUCCESS) {
4279 talloc_free(tmp_ctx);
4280 return ret;
4283 ret = ldb_next_request(ar->module, req);
4285 if (ret == LDB_SUCCESS) {
4286 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
4289 talloc_free(tmp_ctx);
4290 return ret;
4294 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
4296 struct ldb_context *ldb;
4297 struct ldb_request *change_req;
4298 enum ndr_err_code ndr_err;
4299 struct ldb_message *msg;
4300 struct replPropertyMetaDataBlob *rmd;
4301 struct replPropertyMetaDataBlob omd;
4302 const struct ldb_val *omd_value;
4303 struct replPropertyMetaDataBlob nmd;
4304 struct ldb_val nmd_value;
4305 unsigned int i;
4306 uint32_t j,ni=0;
4307 unsigned int removed_attrs = 0;
4308 int ret;
4309 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
4310 bool isDeleted = false;
4311 bool local_isDeleted = false;
4312 bool remote_isDeleted = false;
4313 bool take_remote_isDeleted = false;
4314 bool sd_updated = false;
4315 bool renamed = false;
4317 ldb = ldb_module_get_ctx(ar->module);
4318 msg = ar->objs->objects[ar->index_current].msg;
4320 rmd = ar->objs->objects[ar->index_current].meta_data;
4321 ZERO_STRUCT(omd);
4322 omd.version = 1;
4324 /* find existing meta data */
4325 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
4326 if (omd_value) {
4327 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
4328 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4329 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4330 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4331 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4334 if (omd.version != 1) {
4335 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4339 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
4340 "isDeleted", false);
4341 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4342 "isDeleted", false);
4344 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0) {
4345 ret = LDB_SUCCESS;
4346 } else {
4348 * handle renames, even just by case that come in over
4349 * DRS. Changes in the parent DN don't hit us here,
4350 * because the search for a parent will clean up those
4351 * components.
4353 * We also have already filtered out the case where
4354 * the peer has an older name to what we have (see
4355 * replmd_replicated_apply_search_callback())
4357 renamed = true;
4358 ret = replmd_replicated_handle_rename(ar, msg, ar->req);
4362 * This particular error code means that we already tried the
4363 * conflict algrorithm, and the existing record name was newer, so we
4364 * need to rename the incoming record
4366 if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
4367 struct GUID guid;
4368 NTSTATUS status;
4369 struct ldb_dn *new_dn;
4370 status = GUID_from_ndr_blob(&ar->objs->objects[ar->index_current].guid_value, &guid);
4371 /* This really, really can't fail */
4372 SMB_ASSERT(NT_STATUS_IS_OK(status));
4374 new_dn = replmd_conflict_dn(msg, msg->dn, &guid);
4375 if (new_dn == NULL) {
4376 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4377 "Failed to form conflict DN for %s\n",
4378 ldb_dn_get_linearized(msg->dn));
4380 return replmd_replicated_request_werror(ar, WERR_NOMEM);
4383 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
4384 DSDB_FLAG_NEXT_MODULE, ar->req);
4385 if (ret != LDB_SUCCESS) {
4386 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4387 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
4388 ldb_dn_get_linearized(msg->dn),
4389 ldb_dn_get_linearized(ar->search_msg->dn),
4390 ldb_dn_get_linearized(new_dn),
4391 ldb_errstring(ldb_module_get_ctx(ar->module)));
4392 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4395 /* Set the callback to one that will fix up the name to be a conflict DN */
4396 callback = replmd_op_name_modify_callback;
4397 msg->dn = new_dn;
4398 renamed = true;
4399 } else if (ret != LDB_SUCCESS) {
4400 ldb_debug(ldb, LDB_DEBUG_FATAL,
4401 "replmd_replicated_request rename %s => %s failed - %s\n",
4402 ldb_dn_get_linearized(ar->search_msg->dn),
4403 ldb_dn_get_linearized(msg->dn),
4404 ldb_errstring(ldb));
4405 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4408 ZERO_STRUCT(nmd);
4409 nmd.version = 1;
4410 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
4411 nmd.ctr.ctr1.array = talloc_array(ar,
4412 struct replPropertyMetaData1,
4413 nmd.ctr.ctr1.count);
4414 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4416 /* first copy the old meta data */
4417 for (i=0; i < omd.ctr.ctr1.count; i++) {
4418 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
4419 ni++;
4422 ar->seq_num = 0;
4423 /* now merge in the new meta data */
4424 for (i=0; i < rmd->ctr.ctr1.count; i++) {
4425 bool found = false;
4427 for (j=0; j < ni; j++) {
4428 bool cmp;
4430 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
4431 continue;
4434 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4436 * if we compare equal then do an
4437 * update. This is used when a client
4438 * asks for a FULL_SYNC, and can be
4439 * used to recover a corrupt
4440 * replica.
4442 * This call is a bit tricky, what we
4443 * are doing it turning the 'is_newer'
4444 * call into a 'not is older' by
4445 * swapping i and j, and negating the
4446 * outcome.
4448 cmp = !replmd_replPropertyMetaData1_is_newer(&rmd->ctr.ctr1.array[i],
4449 &nmd.ctr.ctr1.array[j]);
4450 } else {
4451 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
4452 &rmd->ctr.ctr1.array[i]);
4454 if (cmp) {
4455 /* replace the entry */
4456 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
4457 if (ar->seq_num == 0) {
4458 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4459 if (ret != LDB_SUCCESS) {
4460 return replmd_replicated_request_error(ar, ret);
4463 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
4464 switch (nmd.ctr.ctr1.array[j].attid) {
4465 case DRSUAPI_ATTID_ntSecurityDescriptor:
4466 sd_updated = true;
4467 break;
4468 case DRSUAPI_ATTID_isDeleted:
4469 take_remote_isDeleted = true;
4470 break;
4471 default:
4472 break;
4474 found = true;
4475 break;
4478 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
4479 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
4480 msg->elements[i-removed_attrs].name,
4481 ldb_dn_get_linearized(msg->dn),
4482 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
4485 /* we don't want to apply this change so remove the attribute */
4486 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
4487 removed_attrs++;
4489 found = true;
4490 break;
4493 if (found) continue;
4495 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
4496 if (ar->seq_num == 0) {
4497 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4498 if (ret != LDB_SUCCESS) {
4499 return replmd_replicated_request_error(ar, ret);
4502 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
4503 switch (nmd.ctr.ctr1.array[ni].attid) {
4504 case DRSUAPI_ATTID_ntSecurityDescriptor:
4505 sd_updated = true;
4506 break;
4507 case DRSUAPI_ATTID_isDeleted:
4508 take_remote_isDeleted = true;
4509 break;
4510 default:
4511 break;
4513 ni++;
4517 * finally correct the size of the meta_data array
4519 nmd.ctr.ctr1.count = ni;
4522 * the rdn attribute (the alias for the name attribute),
4523 * 'cn' for most objects is the last entry in the meta data array
4524 * we have stored
4526 * sort the new meta data array
4528 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, ar->schema, msg->dn);
4529 if (ret != LDB_SUCCESS) {
4530 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
4531 return ret;
4535 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
4536 * UpdateObject.
4538 * This also controls SD propagation below
4540 if (take_remote_isDeleted) {
4541 isDeleted = remote_isDeleted;
4542 } else {
4543 isDeleted = local_isDeleted;
4546 ar->isDeleted = isDeleted;
4549 * check if some replicated attributes left, otherwise skip the ldb_modify() call
4551 if (msg->num_elements == 0) {
4552 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
4553 ar->index_current);
4555 return replmd_replicated_apply_isDeleted(ar);
4558 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
4559 ar->index_current, msg->num_elements);
4561 if (renamed) {
4562 sd_updated = true;
4565 if (sd_updated && !isDeleted) {
4566 ret = dsdb_module_schedule_sd_propagation(ar->module,
4567 ar->objs->partition_dn,
4568 msg->dn, true);
4569 if (ret != LDB_SUCCESS) {
4570 return ldb_operr(ldb);
4574 /* create the meta data value */
4575 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
4576 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
4577 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4578 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4579 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4583 * when we know that we'll modify the record, add the whenChanged, uSNChanged
4584 * and replPopertyMetaData attributes
4586 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4587 if (ret != LDB_SUCCESS) {
4588 return replmd_replicated_request_error(ar, ret);
4590 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4591 if (ret != LDB_SUCCESS) {
4592 return replmd_replicated_request_error(ar, ret);
4594 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
4595 if (ret != LDB_SUCCESS) {
4596 return replmd_replicated_request_error(ar, ret);
4599 replmd_ldb_message_sort(msg, ar->schema);
4601 /* we want to replace the old values */
4602 for (i=0; i < msg->num_elements; i++) {
4603 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
4604 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
4605 if (msg->elements[i].num_values == 0) {
4606 ldb_asprintf_errstring(ldb, __location__
4607 ": objectClass removed on %s, aborting replication\n",
4608 ldb_dn_get_linearized(msg->dn));
4609 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
4614 if (DEBUGLVL(4)) {
4615 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4616 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
4617 talloc_free(s);
4620 ret = ldb_build_mod_req(&change_req,
4621 ldb,
4623 msg,
4624 ar->controls,
4626 callback,
4627 ar->req);
4628 LDB_REQ_SET_LOCATION(change_req);
4629 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4631 /* current partition control needed by "repmd_op_callback" */
4632 ret = ldb_request_add_control(change_req,
4633 DSDB_CONTROL_CURRENT_PARTITION_OID,
4634 false, NULL);
4635 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4637 return ldb_next_request(ar->module, change_req);
4640 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
4641 struct ldb_reply *ares)
4643 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4644 struct replmd_replicated_request);
4645 int ret;
4647 if (!ares) {
4648 return ldb_module_done(ar->req, NULL, NULL,
4649 LDB_ERR_OPERATIONS_ERROR);
4651 if (ares->error != LDB_SUCCESS &&
4652 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4653 return ldb_module_done(ar->req, ares->controls,
4654 ares->response, ares->error);
4657 switch (ares->type) {
4658 case LDB_REPLY_ENTRY:
4659 ar->search_msg = talloc_steal(ar, ares->message);
4660 break;
4662 case LDB_REPLY_REFERRAL:
4663 /* we ignore referrals */
4664 break;
4666 case LDB_REPLY_DONE:
4668 struct replPropertyMetaData1 *md_remote;
4669 struct replPropertyMetaData1 *md_local;
4671 struct replPropertyMetaDataBlob omd;
4672 const struct ldb_val *omd_value;
4673 struct replPropertyMetaDataBlob *rmd;
4674 struct ldb_message *msg;
4676 ar->objs->objects[ar->index_current].last_known_parent = NULL;
4679 * This is the ADD case, find the appropriate parent,
4680 * as this object doesn't exist locally:
4682 if (ar->search_msg == NULL) {
4683 ret = replmd_replicated_apply_search_for_parent(ar);
4684 if (ret != LDB_SUCCESS) {
4685 return ldb_module_done(ar->req, NULL, NULL, ret);
4687 talloc_free(ares);
4688 return LDB_SUCCESS;
4692 * Otherwise, in the MERGE case, work out if we are
4693 * attempting a rename, and if so find the parent the
4694 * newly renamed object wants to belong under (which
4695 * may not be the parent in it's attached string DN
4697 rmd = ar->objs->objects[ar->index_current].meta_data;
4698 ZERO_STRUCT(omd);
4699 omd.version = 1;
4701 /* find existing meta data */
4702 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
4703 if (omd_value) {
4704 enum ndr_err_code ndr_err;
4705 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
4706 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4707 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4708 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4709 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4712 if (omd.version != 1) {
4713 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4718 * now we need to check for double renames. We could have a
4719 * local rename pending which our replication partner hasn't
4720 * received yet. We choose which one wins by looking at the
4721 * attribute stamps on the two objects, the newer one wins
4723 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4724 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4725 /* if there is no name attribute then we have to assume the
4726 object we've received is in fact newer */
4727 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING ||
4728 !md_remote || !md_local ||
4729 replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
4730 ret = replmd_replicated_apply_search_for_parent(ar);
4731 } else {
4732 msg = ar->objs->objects[ar->index_current].msg;
4734 /* Otherwise, just merge on the existing object, force no rename */
4735 DEBUG(4,(__location__ ": Keeping object %s and rejecting older rename to %s\n",
4736 ldb_dn_get_linearized(ar->search_msg->dn),
4737 ldb_dn_get_linearized(msg->dn)));
4740 * This assignment ensures that the strcmp()
4741 * in replmd_replicated_apply_merge() avoids
4742 * the rename call
4744 msg->dn = ar->search_msg->dn;
4745 ret = replmd_replicated_apply_merge(ar);
4747 if (ret != LDB_SUCCESS) {
4748 return ldb_module_done(ar->req, NULL, NULL, ret);
4753 talloc_free(ares);
4754 return LDB_SUCCESS;
4757 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
4759 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
4761 struct ldb_context *ldb;
4762 int ret;
4763 char *tmp_str;
4764 char *filter;
4765 struct ldb_request *search_req;
4767 if (ar->index_current >= ar->objs->num_objects) {
4768 /* done with it, go to next stage */
4769 return replmd_replicated_uptodate_vector(ar);
4772 ldb = ldb_module_get_ctx(ar->module);
4773 ar->search_msg = NULL;
4774 ar->isDeleted = false;
4776 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
4777 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4779 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
4780 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4781 talloc_free(tmp_str);
4783 ret = ldb_build_search_req(&search_req,
4784 ldb,
4786 NULL,
4787 LDB_SCOPE_SUBTREE,
4788 filter,
4789 NULL,
4790 NULL,
4792 replmd_replicated_apply_search_callback,
4793 ar->req);
4794 LDB_REQ_SET_LOCATION(search_req);
4796 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SEARCH_ALL_PARTITIONS|DSDB_SEARCH_SHOW_RECYCLED);
4798 if (ret != LDB_SUCCESS) {
4799 return ret;
4802 return ldb_next_request(ar->module, search_req);
4806 * This is essentially a wrapper for replmd_replicated_apply_next()
4808 * This is needed to ensure that both codepaths call this handler.
4810 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
4812 struct ldb_dn *deleted_objects_dn;
4813 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
4814 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
4815 &deleted_objects_dn);
4816 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
4818 * Do a delete here again, so that if there is
4819 * anything local that conflicts with this
4820 * object being deleted, it is removed. This
4821 * includes links. See MS-DRSR 4.1.10.6.9
4822 * UpdateObject.
4824 * If the object is already deleted, and there
4825 * is no more work required, it doesn't do
4826 * anything.
4829 /* This has been updated to point to the DN we eventually did the modify on */
4831 struct ldb_request *del_req;
4832 struct ldb_result *res;
4834 TALLOC_CTX *tmp_ctx = talloc_new(ar);
4835 if (!tmp_ctx) {
4836 ret = ldb_oom(ldb_module_get_ctx(ar->module));
4837 return ret;
4840 res = talloc_zero(tmp_ctx, struct ldb_result);
4841 if (!res) {
4842 ret = ldb_oom(ldb_module_get_ctx(ar->module));
4843 talloc_free(tmp_ctx);
4844 return ret;
4847 /* Build a delete request, which hopefully will artually turn into nothing */
4848 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
4849 msg->dn,
4850 NULL,
4851 res,
4852 ldb_modify_default_callback,
4853 ar->req);
4854 LDB_REQ_SET_LOCATION(del_req);
4855 if (ret != LDB_SUCCESS) {
4856 talloc_free(tmp_ctx);
4857 return ret;
4861 * This is the guts of the call, call back
4862 * into our delete code, but setting the
4863 * re_delete flag so we delete anything that
4864 * shouldn't be there on a deleted or recycled
4865 * object
4867 ret = replmd_delete_internals(ar->module, del_req, true);
4868 if (ret == LDB_SUCCESS) {
4869 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
4872 talloc_free(tmp_ctx);
4873 if (ret != LDB_SUCCESS) {
4874 return ret;
4878 ar->index_current++;
4879 return replmd_replicated_apply_next(ar);
4882 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
4883 struct ldb_reply *ares)
4885 struct ldb_context *ldb;
4886 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4887 struct replmd_replicated_request);
4888 ldb = ldb_module_get_ctx(ar->module);
4890 if (!ares) {
4891 return ldb_module_done(ar->req, NULL, NULL,
4892 LDB_ERR_OPERATIONS_ERROR);
4894 if (ares->error != LDB_SUCCESS) {
4895 return ldb_module_done(ar->req, ares->controls,
4896 ares->response, ares->error);
4899 if (ares->type != LDB_REPLY_DONE) {
4900 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
4901 return ldb_module_done(ar->req, NULL, NULL,
4902 LDB_ERR_OPERATIONS_ERROR);
4905 talloc_free(ares);
4907 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4910 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
4912 struct ldb_context *ldb;
4913 struct ldb_request *change_req;
4914 enum ndr_err_code ndr_err;
4915 struct ldb_message *msg;
4916 struct replUpToDateVectorBlob ouv;
4917 const struct ldb_val *ouv_value;
4918 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
4919 struct replUpToDateVectorBlob nuv;
4920 struct ldb_val nuv_value;
4921 struct ldb_message_element *nuv_el = NULL;
4922 const struct GUID *our_invocation_id;
4923 struct ldb_message_element *orf_el = NULL;
4924 struct repsFromToBlob nrf;
4925 struct ldb_val *nrf_value = NULL;
4926 struct ldb_message_element *nrf_el = NULL;
4927 unsigned int i;
4928 uint32_t j,ni=0;
4929 bool found = false;
4930 time_t t = time(NULL);
4931 NTTIME now;
4932 int ret;
4933 uint32_t instanceType;
4935 ldb = ldb_module_get_ctx(ar->module);
4936 ruv = ar->objs->uptodateness_vector;
4937 ZERO_STRUCT(ouv);
4938 ouv.version = 2;
4939 ZERO_STRUCT(nuv);
4940 nuv.version = 2;
4942 unix_to_nt_time(&now, t);
4944 if (ar->search_msg == NULL) {
4945 /* this happens for a REPL_OBJ call where we are
4946 creating the target object by replicating it. The
4947 subdomain join code does this for the partition DN
4949 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
4950 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4953 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
4954 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
4955 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
4956 ldb_dn_get_linearized(ar->search_msg->dn)));
4957 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4961 * first create the new replUpToDateVector
4963 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
4964 if (ouv_value) {
4965 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
4966 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
4967 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4968 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4969 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4972 if (ouv.version != 2) {
4973 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4978 * the new uptodateness vector will at least
4979 * contain 1 entry, one for the source_dsa
4981 * plus optional values from our old vector and the one from the source_dsa
4983 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
4984 if (ruv) nuv.ctr.ctr2.count += ruv->count;
4985 nuv.ctr.ctr2.cursors = talloc_array(ar,
4986 struct drsuapi_DsReplicaCursor2,
4987 nuv.ctr.ctr2.count);
4988 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4990 /* first copy the old vector */
4991 for (i=0; i < ouv.ctr.ctr2.count; i++) {
4992 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
4993 ni++;
4996 /* get our invocation_id if we have one already attached to the ldb */
4997 our_invocation_id = samdb_ntds_invocation_id(ldb);
4998 if (our_invocation_id == NULL) {
4999 DEBUG(0, ("repl_meta_data: Could not find our own server's invocationID!\n"));
5000 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5003 /* merge in the source_dsa vector is available */
5004 for (i=0; (ruv && i < ruv->count); i++) {
5005 found = false;
5007 if (our_invocation_id &&
5008 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
5009 our_invocation_id)) {
5010 continue;
5013 for (j=0; j < ni; j++) {
5014 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
5015 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
5016 continue;
5019 found = true;
5021 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
5022 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
5024 break;
5027 if (found) continue;
5029 /* if it's not there yet, add it */
5030 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
5031 ni++;
5035 * finally correct the size of the cursors array
5037 nuv.ctr.ctr2.count = ni;
5040 * sort the cursors
5042 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
5045 * create the change ldb_message
5047 msg = ldb_msg_new(ar);
5048 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5049 msg->dn = ar->search_msg->dn;
5051 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
5052 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
5053 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5054 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5055 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5057 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
5058 if (ret != LDB_SUCCESS) {
5059 return replmd_replicated_request_error(ar, ret);
5061 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
5064 * now create the new repsFrom value from the given repsFromTo1 structure
5066 ZERO_STRUCT(nrf);
5067 nrf.version = 1;
5068 nrf.ctr.ctr1 = *ar->objs->source_dsa;
5069 nrf.ctr.ctr1.last_attempt = now;
5070 nrf.ctr.ctr1.last_success = now;
5071 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
5074 * first see if we already have a repsFrom value for the current source dsa
5075 * if so we'll later replace this value
5077 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
5078 if (orf_el) {
5079 for (i=0; i < orf_el->num_values; i++) {
5080 struct repsFromToBlob *trf;
5082 trf = talloc(ar, struct repsFromToBlob);
5083 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5085 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
5086 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
5087 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5088 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5089 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5092 if (trf->version != 1) {
5093 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5097 * we compare the source dsa objectGUID not the invocation_id
5098 * because we want only one repsFrom value per source dsa
5099 * and when the invocation_id of the source dsa has changed we don't need
5100 * the old repsFrom with the old invocation_id
5102 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
5103 &ar->objs->source_dsa->source_dsa_obj_guid)) {
5104 talloc_free(trf);
5105 continue;
5108 talloc_free(trf);
5109 nrf_value = &orf_el->values[i];
5110 break;
5114 * copy over all old values to the new ldb_message
5116 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
5117 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5118 *nrf_el = *orf_el;
5122 * if we haven't found an old repsFrom value for the current source dsa
5123 * we'll add a new value
5125 if (!nrf_value) {
5126 struct ldb_val zero_value;
5127 ZERO_STRUCT(zero_value);
5128 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
5129 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5131 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
5134 /* we now fill the value which is already attached to ldb_message */
5135 ndr_err = ndr_push_struct_blob(nrf_value, msg,
5136 &nrf,
5137 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
5138 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5139 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5140 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5144 * the ldb_message_element for the attribute, has all the old values and the new one
5145 * so we'll replace the whole attribute with all values
5147 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
5149 if (CHECK_DEBUGLVL(4)) {
5150 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5151 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
5152 talloc_free(s);
5155 /* prepare the ldb_modify() request */
5156 ret = ldb_build_mod_req(&change_req,
5157 ldb,
5159 msg,
5160 ar->controls,
5162 replmd_replicated_uptodate_modify_callback,
5163 ar->req);
5164 LDB_REQ_SET_LOCATION(change_req);
5165 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5167 return ldb_next_request(ar->module, change_req);
5170 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
5171 struct ldb_reply *ares)
5173 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5174 struct replmd_replicated_request);
5175 int ret;
5177 if (!ares) {
5178 return ldb_module_done(ar->req, NULL, NULL,
5179 LDB_ERR_OPERATIONS_ERROR);
5181 if (ares->error != LDB_SUCCESS &&
5182 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5183 return ldb_module_done(ar->req, ares->controls,
5184 ares->response, ares->error);
5187 switch (ares->type) {
5188 case LDB_REPLY_ENTRY:
5189 ar->search_msg = talloc_steal(ar, ares->message);
5190 break;
5192 case LDB_REPLY_REFERRAL:
5193 /* we ignore referrals */
5194 break;
5196 case LDB_REPLY_DONE:
5197 ret = replmd_replicated_uptodate_modify(ar);
5198 if (ret != LDB_SUCCESS) {
5199 return ldb_module_done(ar->req, NULL, NULL, ret);
5203 talloc_free(ares);
5204 return LDB_SUCCESS;
5208 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
5210 struct ldb_context *ldb;
5211 int ret;
5212 static const char *attrs[] = {
5213 "replUpToDateVector",
5214 "repsFrom",
5215 "instanceType",
5216 NULL
5218 struct ldb_request *search_req;
5220 ldb = ldb_module_get_ctx(ar->module);
5221 ar->search_msg = NULL;
5223 ret = ldb_build_search_req(&search_req,
5224 ldb,
5226 ar->objs->partition_dn,
5227 LDB_SCOPE_BASE,
5228 "(objectClass=*)",
5229 attrs,
5230 NULL,
5232 replmd_replicated_uptodate_search_callback,
5233 ar->req);
5234 LDB_REQ_SET_LOCATION(search_req);
5235 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5237 return ldb_next_request(ar->module, search_req);
5242 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
5244 struct ldb_context *ldb;
5245 struct dsdb_extended_replicated_objects *objs;
5246 struct replmd_replicated_request *ar;
5247 struct ldb_control **ctrls;
5248 int ret;
5249 uint32_t i;
5250 struct replmd_private *replmd_private =
5251 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5252 struct dsdb_control_replicated_update *rep_update;
5254 ldb = ldb_module_get_ctx(module);
5256 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
5258 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
5259 if (!objs) {
5260 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
5261 return LDB_ERR_PROTOCOL_ERROR;
5264 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
5265 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
5266 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
5267 return LDB_ERR_PROTOCOL_ERROR;
5270 ar = replmd_ctx_init(module, req);
5271 if (!ar)
5272 return LDB_ERR_OPERATIONS_ERROR;
5274 /* Set the flags to have the replmd_op_callback run over the full set of objects */
5275 ar->apply_mode = true;
5276 ar->objs = objs;
5277 ar->schema = dsdb_get_schema(ldb, ar);
5278 if (!ar->schema) {
5279 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
5280 talloc_free(ar);
5281 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
5282 return LDB_ERR_CONSTRAINT_VIOLATION;
5285 ctrls = req->controls;
5287 if (req->controls) {
5288 req->controls = talloc_memdup(ar, req->controls,
5289 talloc_get_size(req->controls));
5290 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5293 /* This allows layers further down to know if a change came in
5294 over replication and what the replication flags were */
5295 rep_update = talloc_zero(ar, struct dsdb_control_replicated_update);
5296 if (rep_update == NULL) {
5297 return ldb_module_oom(module);
5299 rep_update->dsdb_repl_flags = objs->dsdb_repl_flags;
5301 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, rep_update);
5302 if (ret != LDB_SUCCESS) {
5303 return ret;
5306 /* If this change contained linked attributes in the body
5307 * (rather than in the links section) we need to update
5308 * backlinks in linked_attributes */
5309 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
5310 if (ret != LDB_SUCCESS) {
5311 return ret;
5314 ar->controls = req->controls;
5315 req->controls = ctrls;
5317 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
5319 /* save away the linked attributes for the end of the
5320 transaction */
5321 for (i=0; i<ar->objs->linked_attributes_count; i++) {
5322 struct la_entry *la_entry;
5324 if (replmd_private->la_ctx == NULL) {
5325 replmd_private->la_ctx = talloc_new(replmd_private);
5327 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
5328 if (la_entry == NULL) {
5329 ldb_oom(ldb);
5330 return LDB_ERR_OPERATIONS_ERROR;
5332 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
5333 if (la_entry->la == NULL) {
5334 talloc_free(la_entry);
5335 ldb_oom(ldb);
5336 return LDB_ERR_OPERATIONS_ERROR;
5338 *la_entry->la = ar->objs->linked_attributes[i];
5340 /* we need to steal the non-scalars so they stay
5341 around until the end of the transaction */
5342 talloc_steal(la_entry->la, la_entry->la->identifier);
5343 talloc_steal(la_entry->la, la_entry->la->value.blob);
5345 DLIST_ADD(replmd_private->la_list, la_entry);
5348 return replmd_replicated_apply_next(ar);
5352 process one linked attribute structure
5354 static int replmd_process_linked_attribute(struct ldb_module *module,
5355 struct la_entry *la_entry,
5356 struct ldb_request *parent)
5358 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
5359 struct ldb_context *ldb = ldb_module_get_ctx(module);
5360 struct ldb_message *msg;
5361 struct ldb_message *target_msg = NULL;
5362 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
5363 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
5364 int ret;
5365 const struct dsdb_attribute *attr;
5366 struct dsdb_dn *dsdb_dn;
5367 uint64_t seq_num = 0;
5368 struct ldb_message_element *old_el;
5369 WERROR status;
5370 time_t t = time(NULL);
5371 struct ldb_result *res;
5372 struct ldb_result *target_res;
5373 const char *attrs[4];
5374 const char *attrs2[] = { "isDeleted", "isRecycled", NULL };
5375 struct parsed_dn *pdn_list, *pdn;
5376 struct GUID guid = GUID_zero();
5377 NTSTATUS ntstatus;
5378 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
5379 const struct GUID *our_invocation_id;
5381 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
5382 enum deletion_state target_deletion_state = OBJECT_NOT_DELETED;
5385 linked_attributes[0]:
5386 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
5387 identifier : *
5388 identifier: struct drsuapi_DsReplicaObjectIdentifier
5389 __ndr_size : 0x0000003a (58)
5390 __ndr_size_sid : 0x00000000 (0)
5391 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
5392 sid : S-0-0
5393 __ndr_size_dn : 0x00000000 (0)
5394 dn : ''
5395 attid : DRSUAPI_ATTID_member (0x1F)
5396 value: struct drsuapi_DsAttributeValue
5397 __ndr_size : 0x0000007e (126)
5398 blob : *
5399 blob : DATA_BLOB length=126
5400 flags : 0x00000001 (1)
5401 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
5402 originating_add_time : Wed Sep 2 22:20:01 2009 EST
5403 meta_data: struct drsuapi_DsReplicaMetaData
5404 version : 0x00000015 (21)
5405 originating_change_time : Wed Sep 2 23:39:07 2009 EST
5406 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
5407 originating_usn : 0x000000000001e19c (123292)
5409 (for cases where the link is to a normal DN)
5410 &target: struct drsuapi_DsReplicaObjectIdentifier3
5411 __ndr_size : 0x0000007e (126)
5412 __ndr_size_sid : 0x0000001c (28)
5413 guid : 7639e594-db75-4086-b0d4-67890ae46031
5414 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
5415 __ndr_size_dn : 0x00000022 (34)
5416 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
5419 /* find the attribute being modified */
5420 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
5421 if (attr == NULL) {
5422 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
5423 talloc_free(tmp_ctx);
5424 return LDB_ERR_OPERATIONS_ERROR;
5427 attrs[0] = attr->lDAPDisplayName;
5428 attrs[1] = "isDeleted";
5429 attrs[2] = "isRecycled";
5430 attrs[3] = NULL;
5432 /* get the existing message from the db for the object with
5433 this GUID, returning attribute being modified. We will then
5434 use this msg as the basis for a modify call */
5435 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
5436 DSDB_FLAG_NEXT_MODULE |
5437 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5438 DSDB_SEARCH_SHOW_RECYCLED |
5439 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
5440 DSDB_SEARCH_REVEAL_INTERNALS,
5441 parent,
5442 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
5443 if (ret != LDB_SUCCESS) {
5444 talloc_free(tmp_ctx);
5445 return ret;
5447 if (res->count != 1) {
5448 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
5449 GUID_string(tmp_ctx, &la->identifier->guid));
5450 talloc_free(tmp_ctx);
5451 return LDB_ERR_NO_SUCH_OBJECT;
5453 msg = res->msgs[0];
5456 * Check for deleted objects per MS-DRSR 4.1.10.6.13
5457 * ProcessLinkValue, because link updates are not applied to
5458 * recycled and tombstone objects. We don't have to delete
5459 * any existing link, that should have happened when the
5460 * object deletion was replicated or initiated.
5463 replmd_deletion_state(module, msg, &deletion_state, NULL);
5465 if (deletion_state >= OBJECT_RECYCLED) {
5466 talloc_free(tmp_ctx);
5467 return LDB_SUCCESS;
5470 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
5471 if (old_el == NULL) {
5472 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
5473 if (ret != LDB_SUCCESS) {
5474 ldb_module_oom(module);
5475 talloc_free(tmp_ctx);
5476 return LDB_ERR_OPERATIONS_ERROR;
5478 } else {
5479 old_el->flags = LDB_FLAG_MOD_REPLACE;
5482 /* parse the existing links */
5483 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
5484 if (ret != LDB_SUCCESS) {
5485 talloc_free(tmp_ctx);
5486 return ret;
5489 /* get our invocationId */
5490 our_invocation_id = samdb_ntds_invocation_id(ldb);
5491 if (!our_invocation_id) {
5492 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
5493 talloc_free(tmp_ctx);
5494 return LDB_ERR_OPERATIONS_ERROR;
5497 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
5498 if (ret != LDB_SUCCESS) {
5499 talloc_free(tmp_ctx);
5500 return ret;
5503 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
5504 if (!W_ERROR_IS_OK(status)) {
5505 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
5506 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
5507 talloc_free(tmp_ctx);
5508 return LDB_ERR_OPERATIONS_ERROR;
5511 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
5512 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
5514 * This strange behaviour (allowing a NULL/missing
5515 * GUID) originally comes from:
5517 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
5518 * Author: Andrew Tridgell <tridge@samba.org>
5519 * Date: Mon Dec 21 21:21:55 2009 +1100
5521 * s4-drs: cope better with NULL GUIDS from DRS
5523 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
5524 * need to match by DN if possible when seeing if we should update an
5525 * existing link.
5527 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
5530 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
5531 dsdb_dn->dn, attrs2,
5532 DSDB_FLAG_NEXT_MODULE |
5533 DSDB_SEARCH_SHOW_RECYCLED |
5534 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5535 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
5536 parent);
5537 } else if (!NT_STATUS_IS_OK(ntstatus)) {
5538 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
5539 old_el->name,
5540 ldb_dn_get_linearized(dsdb_dn->dn),
5541 ldb_dn_get_linearized(msg->dn));
5542 talloc_free(tmp_ctx);
5543 return LDB_ERR_OPERATIONS_ERROR;
5544 } else {
5545 ret = dsdb_module_search(module, tmp_ctx, &target_res,
5546 NULL, LDB_SCOPE_SUBTREE,
5547 attrs2,
5548 DSDB_FLAG_NEXT_MODULE |
5549 DSDB_SEARCH_SHOW_RECYCLED |
5550 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5551 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
5552 parent,
5553 "objectGUID=%s",
5554 GUID_string(tmp_ctx, &guid));
5557 if (ret != LDB_SUCCESS) {
5558 ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to re-resolve GUID %s: %s\n",
5559 GUID_string(tmp_ctx, &guid),
5560 ldb_errstring(ldb_module_get_ctx(module)));
5561 talloc_free(tmp_ctx);
5562 return ret;
5565 if (target_res->count == 0) {
5566 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
5567 GUID_string(tmp_ctx, &guid),
5568 ldb_dn_get_linearized(dsdb_dn->dn)));
5569 } else if (target_res->count != 1) {
5570 ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching objectGUID %s\n",
5571 GUID_string(tmp_ctx, &guid));
5572 talloc_free(tmp_ctx);
5573 return LDB_ERR_OPERATIONS_ERROR;
5574 } else {
5575 target_msg = target_res->msgs[0];
5576 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
5580 * Check for deleted objects per MS-DRSR 4.1.10.6.13
5581 * ProcessLinkValue, because link updates are not applied to
5582 * recycled and tombstone objects. We don't have to delete
5583 * any existing link, that should have happened when the
5584 * object deletion was replicated or initiated.
5586 replmd_deletion_state(module, target_msg,
5587 &target_deletion_state, NULL);
5589 if (target_deletion_state >= OBJECT_RECYCLED) {
5590 talloc_free(tmp_ctx);
5591 return LDB_SUCCESS;
5594 /* see if this link already exists */
5595 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
5596 if (pdn != NULL) {
5597 /* see if this update is newer than what we have already */
5598 struct GUID invocation_id = GUID_zero();
5599 uint32_t version = 0;
5600 uint32_t originating_usn = 0;
5601 NTTIME change_time = 0;
5602 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
5604 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
5605 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
5606 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
5607 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
5609 if (!replmd_update_is_newer(&invocation_id,
5610 &la->meta_data.originating_invocation_id,
5611 version,
5612 la->meta_data.version,
5613 change_time,
5614 la->meta_data.originating_change_time)) {
5615 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
5616 old_el->name, ldb_dn_get_linearized(msg->dn),
5617 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
5618 talloc_free(tmp_ctx);
5619 return LDB_SUCCESS;
5622 /* get a seq_num for this change */
5623 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
5624 if (ret != LDB_SUCCESS) {
5625 talloc_free(tmp_ctx);
5626 return ret;
5629 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
5630 /* remove the existing backlink */
5631 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
5632 if (ret != LDB_SUCCESS) {
5633 talloc_free(tmp_ctx);
5634 return ret;
5638 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
5639 &la->meta_data.originating_invocation_id,
5640 la->meta_data.originating_usn, seq_num,
5641 la->meta_data.originating_change_time,
5642 la->meta_data.version,
5643 !active);
5644 if (ret != LDB_SUCCESS) {
5645 talloc_free(tmp_ctx);
5646 return ret;
5649 if (active) {
5650 /* add the new backlink */
5651 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
5652 if (ret != LDB_SUCCESS) {
5653 talloc_free(tmp_ctx);
5654 return ret;
5657 } else {
5658 /* get a seq_num for this change */
5659 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
5660 if (ret != LDB_SUCCESS) {
5661 talloc_free(tmp_ctx);
5662 return ret;
5665 old_el->values = talloc_realloc(msg->elements, old_el->values,
5666 struct ldb_val, old_el->num_values+1);
5667 if (!old_el->values) {
5668 ldb_module_oom(module);
5669 return LDB_ERR_OPERATIONS_ERROR;
5671 old_el->num_values++;
5673 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
5674 &la->meta_data.originating_invocation_id,
5675 la->meta_data.originating_usn, seq_num,
5676 la->meta_data.originating_change_time,
5677 la->meta_data.version,
5678 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
5679 if (ret != LDB_SUCCESS) {
5680 talloc_free(tmp_ctx);
5681 return ret;
5684 if (active) {
5685 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
5686 true, attr, false);
5687 if (ret != LDB_SUCCESS) {
5688 talloc_free(tmp_ctx);
5689 return ret;
5694 /* we only change whenChanged and uSNChanged if the seq_num
5695 has changed */
5696 ret = add_time_element(msg, "whenChanged", t);
5697 if (ret != LDB_SUCCESS) {
5698 talloc_free(tmp_ctx);
5699 ldb_operr(ldb);
5700 return ret;
5703 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
5704 if (ret != LDB_SUCCESS) {
5705 talloc_free(tmp_ctx);
5706 ldb_operr(ldb);
5707 return ret;
5710 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
5711 if (old_el == NULL) {
5712 talloc_free(tmp_ctx);
5713 return ldb_operr(ldb);
5716 ret = dsdb_check_single_valued_link(attr, old_el);
5717 if (ret != LDB_SUCCESS) {
5718 talloc_free(tmp_ctx);
5719 return ret;
5722 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
5724 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
5725 if (ret != LDB_SUCCESS) {
5726 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
5727 ldb_errstring(ldb),
5728 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
5729 talloc_free(tmp_ctx);
5730 return ret;
5733 talloc_free(tmp_ctx);
5735 return ret;
5738 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
5740 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
5741 return replmd_extended_replicated_objects(module, req);
5744 return ldb_next_request(module, req);
5749 we hook into the transaction operations to allow us to
5750 perform the linked attribute updates at the end of the whole
5751 transaction. This allows a forward linked attribute to be created
5752 before the object is created. During a vampire, w2k8 sends us linked
5753 attributes before the objects they are part of.
5755 static int replmd_start_transaction(struct ldb_module *module)
5757 /* create our private structure for this transaction */
5758 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
5759 struct replmd_private);
5760 replmd_txn_cleanup(replmd_private);
5762 /* free any leftover mod_usn records from cancelled
5763 transactions */
5764 while (replmd_private->ncs) {
5765 struct nc_entry *e = replmd_private->ncs;
5766 DLIST_REMOVE(replmd_private->ncs, e);
5767 talloc_free(e);
5770 return ldb_next_start_trans(module);
5774 on prepare commit we loop over our queued la_context structures and
5775 apply each of them
5777 static int replmd_prepare_commit(struct ldb_module *module)
5779 struct replmd_private *replmd_private =
5780 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5781 struct la_entry *la, *prev;
5782 struct la_backlink *bl;
5783 int ret;
5785 /* walk the list backwards, to do the first entry first, as we
5786 * added the entries with DLIST_ADD() which puts them at the
5787 * start of the list */
5788 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
5789 prev = DLIST_PREV(la);
5790 DLIST_REMOVE(replmd_private->la_list, la);
5791 ret = replmd_process_linked_attribute(module, la, NULL);
5792 if (ret != LDB_SUCCESS) {
5793 replmd_txn_cleanup(replmd_private);
5794 return ret;
5798 /* process our backlink list, creating and deleting backlinks
5799 as necessary */
5800 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
5801 ret = replmd_process_backlink(module, bl, NULL);
5802 if (ret != LDB_SUCCESS) {
5803 replmd_txn_cleanup(replmd_private);
5804 return ret;
5808 replmd_txn_cleanup(replmd_private);
5810 /* possibly change @REPLCHANGED */
5811 ret = replmd_notify_store(module, NULL);
5812 if (ret != LDB_SUCCESS) {
5813 return ret;
5816 return ldb_next_prepare_commit(module);
5819 static int replmd_del_transaction(struct ldb_module *module)
5821 struct replmd_private *replmd_private =
5822 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5823 replmd_txn_cleanup(replmd_private);
5825 return ldb_next_del_trans(module);
5829 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
5830 .name = "repl_meta_data",
5831 .init_context = replmd_init,
5832 .add = replmd_add,
5833 .modify = replmd_modify,
5834 .rename = replmd_rename,
5835 .del = replmd_delete,
5836 .extended = replmd_extended,
5837 .start_transaction = replmd_start_transaction,
5838 .prepare_commit = replmd_prepare_commit,
5839 .del_transaction = replmd_del_transaction,
5842 int ldb_repl_meta_data_module_init(const char *version)
5844 LDB_MODULE_CHECK_VERSION(version);
5845 return ldb_register_module(&ldb_repl_meta_data_module_ops);