repl_meta_data: Remove the correct forward link for dn+binary attributes
[Samba.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blobe3bf0324d7cf5ef022dff5742009f55467bf1f31
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;
71 bool originating_updates;
72 bool sorted_links;
75 struct la_entry {
76 struct la_entry *next, *prev;
77 struct drsuapi_DsReplicaLinkedAttribute *la;
80 struct replmd_replicated_request {
81 struct ldb_module *module;
82 struct ldb_request *req;
84 const struct dsdb_schema *schema;
85 struct GUID our_invocation_id;
87 /* the controls we pass down */
88 struct ldb_control **controls;
90 /* details for the mode where we apply a bunch of inbound replication meessages */
91 bool apply_mode;
92 uint32_t index_current;
93 struct dsdb_extended_replicated_objects *objs;
95 struct ldb_message *search_msg;
96 struct GUID local_parent_guid;
98 uint64_t seq_num;
99 bool is_urgent;
101 bool isDeleted;
104 struct parsed_dn {
105 struct dsdb_dn *dsdb_dn;
106 struct GUID guid;
107 struct ldb_val *v;
110 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
111 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
112 static int replmd_check_upgrade_links(struct ldb_context *ldb,
113 struct parsed_dn *dns, uint32_t count,
114 struct ldb_message_element *el,
115 const char *ldap_oid);
117 enum urgent_situation {
118 REPL_URGENT_ON_CREATE = 1,
119 REPL_URGENT_ON_UPDATE = 2,
120 REPL_URGENT_ON_DELETE = 4
123 enum deletion_state {
124 OBJECT_NOT_DELETED=1,
125 OBJECT_DELETED=2,
126 OBJECT_RECYCLED=3,
127 OBJECT_TOMBSTONE=4,
128 OBJECT_REMOVED=5
131 static void replmd_deletion_state(struct ldb_module *module,
132 const struct ldb_message *msg,
133 enum deletion_state *current_state,
134 enum deletion_state *next_state)
136 int ret;
137 bool enabled = false;
139 if (msg == NULL) {
140 *current_state = OBJECT_REMOVED;
141 if (next_state != NULL) {
142 *next_state = OBJECT_REMOVED;
144 return;
147 ret = dsdb_recyclebin_enabled(module, &enabled);
148 if (ret != LDB_SUCCESS) {
149 enabled = false;
152 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
153 if (!enabled) {
154 *current_state = OBJECT_TOMBSTONE;
155 if (next_state != NULL) {
156 *next_state = OBJECT_REMOVED;
158 return;
161 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
162 *current_state = OBJECT_RECYCLED;
163 if (next_state != NULL) {
164 *next_state = OBJECT_REMOVED;
166 return;
169 *current_state = OBJECT_DELETED;
170 if (next_state != NULL) {
171 *next_state = OBJECT_RECYCLED;
173 return;
176 *current_state = OBJECT_NOT_DELETED;
177 if (next_state == NULL) {
178 return;
181 if (enabled) {
182 *next_state = OBJECT_DELETED;
183 } else {
184 *next_state = OBJECT_TOMBSTONE;
188 static const struct {
189 const char *update_name;
190 enum urgent_situation repl_situation;
191 } urgent_objects[] = {
192 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
193 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
194 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
195 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
196 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
197 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
198 {NULL, 0}
201 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
202 static const char *urgent_attrs[] = {
203 "lockoutTime",
204 "pwdLastSet",
205 "userAccountControl",
206 NULL
210 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
211 enum urgent_situation situation)
213 unsigned int i, j;
214 for (i=0; urgent_objects[i].update_name; i++) {
216 if ((situation & urgent_objects[i].repl_situation) == 0) {
217 continue;
220 for (j=0; j<objectclass_el->num_values; j++) {
221 const struct ldb_val *v = &objectclass_el->values[j];
222 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
223 return true;
227 return false;
230 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
232 if (ldb_attr_in_list(urgent_attrs, el->name)) {
233 return true;
235 return false;
239 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
242 initialise the module
243 allocate the private structure and build the list
244 of partition DNs for use by replmd_notify()
246 static int replmd_init(struct ldb_module *module)
248 struct replmd_private *replmd_private;
249 struct ldb_context *ldb = ldb_module_get_ctx(module);
250 static const char *samba_dsdb_attrs[] = { SAMBA_COMPATIBLE_FEATURES_ATTR, NULL };
251 struct ldb_dn *samba_dsdb_dn;
252 struct ldb_result *res;
253 int ret;
254 TALLOC_CTX *frame = talloc_stackframe();
255 replmd_private = talloc_zero(module, struct replmd_private);
256 if (replmd_private == NULL) {
257 ldb_oom(ldb);
258 TALLOC_FREE(frame);
259 return LDB_ERR_OPERATIONS_ERROR;
261 ldb_module_set_private(module, replmd_private);
263 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
265 samba_dsdb_dn = ldb_dn_new(frame, ldb, "@SAMBA_DSDB");
266 if (!samba_dsdb_dn) {
267 TALLOC_FREE(frame);
268 return ldb_oom(ldb);
271 ret = dsdb_module_search_dn(module, frame, &res, samba_dsdb_dn,
272 samba_dsdb_attrs, DSDB_FLAG_NEXT_MODULE, NULL);
273 if (ret == LDB_SUCCESS) {
274 replmd_private->sorted_links
275 = ldb_msg_check_string_attribute(res->msgs[0],
276 SAMBA_COMPATIBLE_FEATURES_ATTR,
277 SAMBA_SORTED_LINKS_FEATURE);
279 TALLOC_FREE(frame);
281 return ldb_next_init(module);
285 cleanup our per-transaction contexts
287 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
289 talloc_free(replmd_private->la_ctx);
290 replmd_private->la_list = NULL;
291 replmd_private->la_ctx = NULL;
293 talloc_free(replmd_private->bl_ctx);
294 replmd_private->la_backlinks = NULL;
295 replmd_private->bl_ctx = NULL;
299 struct la_backlink {
300 struct la_backlink *next, *prev;
301 const char *attr_name;
302 struct GUID forward_guid, target_guid;
303 bool active;
307 a ldb_modify request operating on modules below the
308 current module
310 static int linked_attr_modify(struct ldb_module *module,
311 const struct ldb_message *message,
312 struct ldb_request *parent)
314 struct ldb_request *mod_req;
315 int ret;
316 struct ldb_context *ldb = ldb_module_get_ctx(module);
317 TALLOC_CTX *tmp_ctx = talloc_new(module);
318 struct ldb_result *res;
320 res = talloc_zero(tmp_ctx, struct ldb_result);
321 if (!res) {
322 talloc_free(tmp_ctx);
323 return ldb_oom(ldb_module_get_ctx(module));
326 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
327 message,
328 NULL,
329 res,
330 ldb_modify_default_callback,
331 parent);
332 LDB_REQ_SET_LOCATION(mod_req);
333 if (ret != LDB_SUCCESS) {
334 talloc_free(tmp_ctx);
335 return ret;
338 ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
339 false, NULL);
340 if (ret != LDB_SUCCESS) {
341 return ret;
344 /* Run the new request */
345 ret = ldb_next_request(module, mod_req);
347 if (ret == LDB_SUCCESS) {
348 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
351 talloc_free(tmp_ctx);
352 return ret;
356 process a backlinks we accumulated during a transaction, adding and
357 deleting the backlinks from the target objects
359 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
361 struct ldb_dn *target_dn, *source_dn;
362 int ret;
363 struct ldb_context *ldb = ldb_module_get_ctx(module);
364 struct ldb_message *msg;
365 TALLOC_CTX *tmp_ctx = talloc_new(bl);
366 char *dn_string;
369 - find DN of target
370 - find DN of source
371 - construct ldb_message
372 - either an add or a delete
374 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn, parent);
375 if (ret != LDB_SUCCESS) {
376 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
377 GUID_string(bl, &bl->target_guid)));
378 return LDB_SUCCESS;
381 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn, parent);
382 if (ret != LDB_SUCCESS) {
383 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
384 GUID_string(bl, &bl->forward_guid));
385 talloc_free(tmp_ctx);
386 return ret;
389 msg = ldb_msg_new(tmp_ctx);
390 if (msg == NULL) {
391 ldb_module_oom(module);
392 talloc_free(tmp_ctx);
393 return LDB_ERR_OPERATIONS_ERROR;
396 /* construct a ldb_message for adding/deleting the backlink */
397 msg->dn = target_dn;
398 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
399 if (!dn_string) {
400 ldb_module_oom(module);
401 talloc_free(tmp_ctx);
402 return LDB_ERR_OPERATIONS_ERROR;
404 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
405 if (ret != LDB_SUCCESS) {
406 talloc_free(tmp_ctx);
407 return ret;
409 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
411 /* a backlink should never be single valued. Unfortunately the
412 exchange schema has a attribute
413 msExchBridgeheadedLocalConnectorsDNBL which is single
414 valued and a backlink. We need to cope with that by
415 ignoring the single value flag */
416 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
418 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
419 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
420 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
421 cope with possible corruption where the backlink has
422 already been removed */
423 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
424 ldb_dn_get_linearized(target_dn),
425 ldb_dn_get_linearized(source_dn),
426 ldb_errstring(ldb)));
427 ret = LDB_SUCCESS;
428 } else if (ret != LDB_SUCCESS) {
429 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
430 bl->active?"add":"remove",
431 ldb_dn_get_linearized(source_dn),
432 ldb_dn_get_linearized(target_dn),
433 ldb_errstring(ldb));
434 talloc_free(tmp_ctx);
435 return ret;
437 talloc_free(tmp_ctx);
438 return ret;
442 add a backlink to the list of backlinks to add/delete in the prepare
443 commit
445 static int replmd_add_backlink(struct ldb_module *module,
446 struct replmd_private *replmd_private,
447 const struct dsdb_schema *schema,
448 struct GUID *forward_guid,
449 struct GUID *target_guid, bool active,
450 const struct dsdb_attribute *schema_attr,
451 bool immediate)
453 const struct dsdb_attribute *target_attr;
454 struct la_backlink *bl;
456 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
457 if (!target_attr) {
459 * windows 2003 has a broken schema where the
460 * definition of msDS-IsDomainFor is missing (which is
461 * supposed to be the backlink of the
462 * msDS-HasDomainNCs attribute
464 return LDB_SUCCESS;
467 /* see if its already in the list */
468 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
469 if (GUID_equal(forward_guid, &bl->forward_guid) &&
470 GUID_equal(target_guid, &bl->target_guid) &&
471 (target_attr->lDAPDisplayName == bl->attr_name ||
472 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
473 break;
477 if (bl) {
478 /* we found an existing one */
479 if (bl->active == active) {
480 return LDB_SUCCESS;
482 DLIST_REMOVE(replmd_private->la_backlinks, bl);
483 talloc_free(bl);
484 return LDB_SUCCESS;
487 if (replmd_private->bl_ctx == NULL) {
488 replmd_private->bl_ctx = talloc_new(replmd_private);
489 if (replmd_private->bl_ctx == NULL) {
490 ldb_module_oom(module);
491 return LDB_ERR_OPERATIONS_ERROR;
495 /* its a new one */
496 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
497 if (bl == NULL) {
498 ldb_module_oom(module);
499 return LDB_ERR_OPERATIONS_ERROR;
502 /* Ensure the schema does not go away before the bl->attr_name is used */
503 if (!talloc_reference(bl, schema)) {
504 talloc_free(bl);
505 ldb_module_oom(module);
506 return LDB_ERR_OPERATIONS_ERROR;
509 bl->attr_name = target_attr->lDAPDisplayName;
510 bl->forward_guid = *forward_guid;
511 bl->target_guid = *target_guid;
512 bl->active = active;
514 /* the caller may ask for this backlink to be processed
515 immediately */
516 if (immediate) {
517 int ret = replmd_process_backlink(module, bl, NULL);
518 talloc_free(bl);
519 return ret;
522 DLIST_ADD(replmd_private->la_backlinks, bl);
524 return LDB_SUCCESS;
529 * Callback for most write operations in this module:
531 * notify the repl task that a object has changed. The notifies are
532 * gathered up in the replmd_private structure then written to the
533 * @REPLCHANGED object in each partition during the prepare_commit
535 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
537 int ret;
538 struct replmd_replicated_request *ac =
539 talloc_get_type_abort(req->context, struct replmd_replicated_request);
540 struct replmd_private *replmd_private =
541 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
542 struct nc_entry *modified_partition;
543 struct ldb_control *partition_ctrl;
544 const struct dsdb_control_current_partition *partition;
546 struct ldb_control **controls;
548 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
550 controls = ares->controls;
551 if (ldb_request_get_control(ac->req,
552 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
554 * Remove the current partition control from what we pass up
555 * the chain if it hasn't been requested manually.
557 controls = ldb_controls_except_specified(ares->controls, ares,
558 partition_ctrl);
561 if (ares->error != LDB_SUCCESS) {
562 DEBUG(5,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
563 return ldb_module_done(ac->req, controls,
564 ares->response, ares->error);
567 if (ares->type != LDB_REPLY_DONE) {
568 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
569 return ldb_module_done(ac->req, NULL,
570 NULL, LDB_ERR_OPERATIONS_ERROR);
573 if (!partition_ctrl) {
574 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
575 return ldb_module_done(ac->req, NULL,
576 NULL, LDB_ERR_OPERATIONS_ERROR);
579 partition = talloc_get_type_abort(partition_ctrl->data,
580 struct dsdb_control_current_partition);
582 if (ac->seq_num > 0) {
583 for (modified_partition = replmd_private->ncs; modified_partition;
584 modified_partition = modified_partition->next) {
585 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
586 break;
590 if (modified_partition == NULL) {
591 modified_partition = talloc_zero(replmd_private, struct nc_entry);
592 if (!modified_partition) {
593 ldb_oom(ldb_module_get_ctx(ac->module));
594 return ldb_module_done(ac->req, NULL,
595 NULL, LDB_ERR_OPERATIONS_ERROR);
597 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
598 if (!modified_partition->dn) {
599 ldb_oom(ldb_module_get_ctx(ac->module));
600 return ldb_module_done(ac->req, NULL,
601 NULL, LDB_ERR_OPERATIONS_ERROR);
603 DLIST_ADD(replmd_private->ncs, modified_partition);
606 if (ac->seq_num > modified_partition->mod_usn) {
607 modified_partition->mod_usn = ac->seq_num;
608 if (ac->is_urgent) {
609 modified_partition->mod_usn_urgent = ac->seq_num;
612 if (!ac->apply_mode) {
613 replmd_private->originating_updates = true;
617 if (ac->apply_mode) {
618 ret = replmd_replicated_apply_isDeleted(ac);
619 if (ret != LDB_SUCCESS) {
620 return ldb_module_done(ac->req, NULL, NULL, ret);
622 return ret;
623 } else {
624 /* free the partition control container here, for the
625 * common path. Other cases will have it cleaned up
626 * eventually with the ares */
627 talloc_free(partition_ctrl);
628 return ldb_module_done(ac->req, controls,
629 ares->response, LDB_SUCCESS);
635 * update a @REPLCHANGED record in each partition if there have been
636 * any writes of replicated data in the partition
638 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
640 struct replmd_private *replmd_private =
641 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
643 while (replmd_private->ncs) {
644 int ret;
645 struct nc_entry *modified_partition = replmd_private->ncs;
647 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
648 modified_partition->mod_usn,
649 modified_partition->mod_usn_urgent, parent);
650 if (ret != LDB_SUCCESS) {
651 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
652 ldb_dn_get_linearized(modified_partition->dn)));
653 return ret;
656 if (ldb_dn_compare(modified_partition->dn,
657 replmd_private->schema_dn) == 0) {
658 struct ldb_result *ext_res;
659 ret = dsdb_module_extended(module,
660 replmd_private->schema_dn,
661 &ext_res,
662 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
663 ext_res,
664 DSDB_FLAG_NEXT_MODULE,
665 parent);
666 if (ret != LDB_SUCCESS) {
667 return ret;
669 talloc_free(ext_res);
672 DLIST_REMOVE(replmd_private->ncs, modified_partition);
673 talloc_free(modified_partition);
676 return LDB_SUCCESS;
681 created a replmd_replicated_request context
683 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
684 struct ldb_request *req)
686 struct ldb_context *ldb;
687 struct replmd_replicated_request *ac;
688 const struct GUID *our_invocation_id;
690 ldb = ldb_module_get_ctx(module);
692 ac = talloc_zero(req, struct replmd_replicated_request);
693 if (ac == NULL) {
694 ldb_oom(ldb);
695 return NULL;
698 ac->module = module;
699 ac->req = req;
701 ac->schema = dsdb_get_schema(ldb, ac);
702 if (!ac->schema) {
703 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
704 "replmd_modify: no dsdb_schema loaded");
705 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
706 talloc_free(ac);
707 return NULL;
710 /* get our invocationId */
711 our_invocation_id = samdb_ntds_invocation_id(ldb);
712 if (!our_invocation_id) {
713 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
714 "replmd_add: unable to find invocationId\n");
715 talloc_free(ac);
716 return NULL;
718 ac->our_invocation_id = *our_invocation_id;
720 return ac;
724 add a time element to a record
726 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
728 struct ldb_message_element *el;
729 char *s;
730 int ret;
732 if (ldb_msg_find_element(msg, attr) != NULL) {
733 return LDB_SUCCESS;
736 s = ldb_timestring(msg, t);
737 if (s == NULL) {
738 return LDB_ERR_OPERATIONS_ERROR;
741 ret = ldb_msg_add_string(msg, attr, s);
742 if (ret != LDB_SUCCESS) {
743 return ret;
746 el = ldb_msg_find_element(msg, attr);
747 /* always set as replace. This works because on add ops, the flag
748 is ignored */
749 el->flags = LDB_FLAG_MOD_REPLACE;
751 return LDB_SUCCESS;
755 add a uint64_t element to a record
757 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
758 const char *attr, uint64_t v)
760 struct ldb_message_element *el;
761 int ret;
763 if (ldb_msg_find_element(msg, attr) != NULL) {
764 return LDB_SUCCESS;
767 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
768 if (ret != LDB_SUCCESS) {
769 return ret;
772 el = ldb_msg_find_element(msg, attr);
773 /* always set as replace. This works because on add ops, the flag
774 is ignored */
775 el->flags = LDB_FLAG_MOD_REPLACE;
777 return LDB_SUCCESS;
780 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
781 const struct replPropertyMetaData1 *m2,
782 const uint32_t *rdn_attid)
785 * This assignment seems inoccous, but it is critical for the
786 * system, as we need to do the comparisons as a unsigned
787 * quantity, not signed (enums are signed integers)
789 uint32_t attid_1 = m1->attid;
790 uint32_t attid_2 = m2->attid;
792 if (attid_1 == attid_2) {
793 return 0;
797 * See above regarding this being an unsigned comparison.
798 * Otherwise when the high bit is set on non-standard
799 * attributes, they would end up first, before objectClass
800 * (0).
802 return attid_1 > attid_2 ? 1 : -1;
805 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
806 struct replPropertyMetaDataCtr1 *ctr1,
807 struct ldb_dn *dn)
809 if (ctr1->count == 0) {
810 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
811 "No elements found in replPropertyMetaData for %s!\n",
812 ldb_dn_get_linearized(dn));
813 return LDB_ERR_CONSTRAINT_VIOLATION;
816 /* the objectClass attribute is value 0x00000000, so must be first */
817 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
818 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
819 "No objectClass found in replPropertyMetaData for %s!\n",
820 ldb_dn_get_linearized(dn));
821 return LDB_ERR_OBJECT_CLASS_VIOLATION;
824 return LDB_SUCCESS;
827 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
828 struct replPropertyMetaDataCtr1 *ctr1,
829 struct ldb_dn *dn)
831 /* Note this is O(n^2) for the almost-sorted case, which this is */
832 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, NULL,
833 replmd_replPropertyMetaData1_attid_sort);
834 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
837 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
838 const struct ldb_message_element *e2,
839 const struct dsdb_schema *schema)
841 const struct dsdb_attribute *a1;
842 const struct dsdb_attribute *a2;
845 * TODO: make this faster by caching the dsdb_attribute pointer
846 * on the ldb_messag_element
849 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
850 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
853 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
854 * in the schema
856 if (!a1 || !a2) {
857 return strcasecmp(e1->name, e2->name);
859 if (a1->attributeID_id == a2->attributeID_id) {
860 return 0;
862 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
865 static void replmd_ldb_message_sort(struct ldb_message *msg,
866 const struct dsdb_schema *schema)
868 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
871 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
872 const struct GUID *invocation_id, uint64_t seq_num,
873 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
875 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
876 struct ldb_message_element *el, struct parsed_dn **pdn,
877 const char *ldap_oid, struct ldb_request *parent);
880 fix up linked attributes in replmd_add.
881 This involves setting up the right meta-data in extended DN
882 components, and creating backlinks to the object
884 static int replmd_add_fix_la(struct ldb_module *module, TALLOC_CTX *mem_ctx,
885 struct replmd_private *replmd_private,
886 struct ldb_message_element *el,
887 uint64_t seq_num, const struct GUID *invocationId, NTTIME now,
888 struct GUID *guid, const struct dsdb_attribute *sa,
889 struct ldb_request *parent)
891 unsigned int i;
892 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
893 struct ldb_context *ldb = ldb_module_get_ctx(module);
894 struct parsed_dn *pdn;
895 /* We will take a reference to the schema in replmd_add_backlink */
896 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
897 struct ldb_val *new_values = NULL;
899 int ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
900 sa->syntax->ldap_oid, parent);
901 if (ret != LDB_SUCCESS) {
902 talloc_free(tmp_ctx);
903 return ret;
906 new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
907 if (new_values == NULL) {
908 ldb_module_oom(module);
909 talloc_free(tmp_ctx);
910 return LDB_ERR_OPERATIONS_ERROR;
913 for (i = 0; i < el->num_values; i++) {
914 struct parsed_dn *p = &pdn[i];
915 ret = replmd_build_la_val(el->values, p->v, p->dsdb_dn,
916 invocationId,
917 seq_num, seq_num, now, 0, false);
918 if (ret != LDB_SUCCESS) {
919 talloc_free(tmp_ctx);
920 return ret;
923 /* This is the only place we are doing deferred back-links */
924 ret = replmd_add_backlink(module, replmd_private,
925 schema, guid, &p->guid, true, sa,
926 false);
927 if (ret != LDB_SUCCESS) {
928 talloc_free(tmp_ctx);
929 return ret;
932 new_values[i] = *p->v;
934 el->values = talloc_steal(mem_ctx, new_values);
936 talloc_free(tmp_ctx);
937 return LDB_SUCCESS;
942 intercept add requests
944 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
946 struct ldb_context *ldb;
947 struct ldb_control *control;
948 struct replmd_replicated_request *ac;
949 enum ndr_err_code ndr_err;
950 struct ldb_request *down_req;
951 struct ldb_message *msg;
952 const DATA_BLOB *guid_blob;
953 struct GUID guid;
954 struct replPropertyMetaDataBlob nmd;
955 struct ldb_val nmd_value;
958 * The use of a time_t here seems odd, but as the NTTIME
959 * elements are actually declared as NTTIME_1sec in the IDL,
960 * getting a higher resolution timestamp is not required.
962 time_t t = time(NULL);
963 NTTIME now;
964 char *time_str;
965 int ret;
966 unsigned int i;
967 unsigned int functional_level;
968 uint32_t ni=0;
969 bool allow_add_guid = false;
970 bool remove_current_guid = false;
971 bool is_urgent = false;
972 bool is_schema_nc = false;
973 struct ldb_message_element *objectclass_el;
974 struct replmd_private *replmd_private =
975 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
977 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
978 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
979 if (control) {
980 allow_add_guid = true;
983 /* do not manipulate our control entries */
984 if (ldb_dn_is_special(req->op.add.message->dn)) {
985 return ldb_next_request(module, req);
988 ldb = ldb_module_get_ctx(module);
990 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
992 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
993 if (guid_blob != NULL) {
994 if (!allow_add_guid) {
995 ldb_set_errstring(ldb,
996 "replmd_add: it's not allowed to add an object with objectGUID!");
997 return LDB_ERR_UNWILLING_TO_PERFORM;
998 } else {
999 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
1000 if (!NT_STATUS_IS_OK(status)) {
1001 ldb_set_errstring(ldb,
1002 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1003 return LDB_ERR_UNWILLING_TO_PERFORM;
1005 /* we remove this attribute as it can be a string and
1006 * will not be treated correctly and then we will re-add
1007 * it later on in the good format */
1008 remove_current_guid = true;
1010 } else {
1011 /* a new GUID */
1012 guid = GUID_random();
1015 ac = replmd_ctx_init(module, req);
1016 if (ac == NULL) {
1017 return ldb_module_oom(module);
1020 functional_level = dsdb_functional_level(ldb);
1022 /* Get a sequence number from the backend */
1023 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
1024 if (ret != LDB_SUCCESS) {
1025 talloc_free(ac);
1026 return ret;
1029 /* we have to copy the message as the caller might have it as a const */
1030 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
1031 if (msg == NULL) {
1032 ldb_oom(ldb);
1033 talloc_free(ac);
1034 return LDB_ERR_OPERATIONS_ERROR;
1037 /* generated times */
1038 unix_to_nt_time(&now, t);
1039 time_str = ldb_timestring(msg, t);
1040 if (!time_str) {
1041 ldb_oom(ldb);
1042 talloc_free(ac);
1043 return LDB_ERR_OPERATIONS_ERROR;
1045 if (remove_current_guid) {
1046 ldb_msg_remove_attr(msg,"objectGUID");
1050 * remove autogenerated attributes
1052 ldb_msg_remove_attr(msg, "whenCreated");
1053 ldb_msg_remove_attr(msg, "whenChanged");
1054 ldb_msg_remove_attr(msg, "uSNCreated");
1055 ldb_msg_remove_attr(msg, "uSNChanged");
1056 ldb_msg_remove_attr(msg, "replPropertyMetaData");
1059 * readd replicated attributes
1061 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1062 if (ret != LDB_SUCCESS) {
1063 ldb_oom(ldb);
1064 talloc_free(ac);
1065 return ret;
1068 /* build the replication meta_data */
1069 ZERO_STRUCT(nmd);
1070 nmd.version = 1;
1071 nmd.ctr.ctr1.count = msg->num_elements;
1072 nmd.ctr.ctr1.array = talloc_array(msg,
1073 struct replPropertyMetaData1,
1074 nmd.ctr.ctr1.count);
1075 if (!nmd.ctr.ctr1.array) {
1076 ldb_oom(ldb);
1077 talloc_free(ac);
1078 return LDB_ERR_OPERATIONS_ERROR;
1081 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1083 for (i=0; i < msg->num_elements;) {
1084 struct ldb_message_element *e = &msg->elements[i];
1085 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1086 const struct dsdb_attribute *sa;
1088 if (e->name[0] == '@') {
1089 i++;
1090 continue;
1093 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1094 if (!sa) {
1095 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1096 "replmd_add: attribute '%s' not defined in schema\n",
1097 e->name);
1098 talloc_free(ac);
1099 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1102 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1103 /* if the attribute is not replicated (0x00000001)
1104 * or constructed (0x00000004) it has no metadata
1106 i++;
1107 continue;
1110 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1111 ret = replmd_add_fix_la(module, msg->elements,
1112 replmd_private, e,
1113 ac->seq_num,
1114 &ac->our_invocation_id, now,
1115 &guid, sa, req);
1116 if (ret != LDB_SUCCESS) {
1117 talloc_free(ac);
1118 return ret;
1120 /* linked attributes are not stored in
1121 replPropertyMetaData in FL above w2k */
1122 i++;
1123 continue;
1126 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1127 m->version = 1;
1128 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1129 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1130 const char* rdn;
1132 if (rdn_val == NULL) {
1133 ldb_oom(ldb);
1134 talloc_free(ac);
1135 return LDB_ERR_OPERATIONS_ERROR;
1138 rdn = (const char*)rdn_val->data;
1139 if (strcmp(rdn, "Deleted Objects") == 0) {
1141 * Set the originating_change_time to 29/12/9999 at 23:59:59
1142 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1144 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1145 } else {
1146 m->originating_change_time = now;
1148 } else {
1149 m->originating_change_time = now;
1151 m->originating_invocation_id = ac->our_invocation_id;
1152 m->originating_usn = ac->seq_num;
1153 m->local_usn = ac->seq_num;
1154 ni++;
1156 if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1157 i++;
1158 continue;
1161 e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1163 if (e->num_values != 0) {
1164 i++;
1165 continue;
1168 ldb_msg_remove_element(msg, e);
1171 /* fix meta data count */
1172 nmd.ctr.ctr1.count = ni;
1175 * sort meta data array
1177 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1178 if (ret != LDB_SUCCESS) {
1179 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1180 talloc_free(ac);
1181 return ret;
1184 /* generated NDR encoded values */
1185 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1186 &nmd,
1187 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1188 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1189 ldb_oom(ldb);
1190 talloc_free(ac);
1191 return LDB_ERR_OPERATIONS_ERROR;
1195 * add the autogenerated values
1197 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1198 if (ret != LDB_SUCCESS) {
1199 ldb_oom(ldb);
1200 talloc_free(ac);
1201 return ret;
1203 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1204 if (ret != LDB_SUCCESS) {
1205 ldb_oom(ldb);
1206 talloc_free(ac);
1207 return ret;
1209 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1210 if (ret != LDB_SUCCESS) {
1211 ldb_oom(ldb);
1212 talloc_free(ac);
1213 return ret;
1215 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1216 if (ret != LDB_SUCCESS) {
1217 ldb_oom(ldb);
1218 talloc_free(ac);
1219 return ret;
1221 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1222 if (ret != LDB_SUCCESS) {
1223 ldb_oom(ldb);
1224 talloc_free(ac);
1225 return ret;
1229 * sort the attributes by attid before storing the object
1231 replmd_ldb_message_sort(msg, ac->schema);
1234 * Assert that we do have an objectClass
1236 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1237 if (objectclass_el == NULL) {
1238 ldb_asprintf_errstring(ldb, __location__
1239 ": objectClass missing on %s\n",
1240 ldb_dn_get_linearized(msg->dn));
1241 talloc_free(ac);
1242 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1244 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1245 REPL_URGENT_ON_CREATE);
1247 ac->is_urgent = is_urgent;
1248 ret = ldb_build_add_req(&down_req, ldb, ac,
1249 msg,
1250 req->controls,
1251 ac, replmd_op_callback,
1252 req);
1254 LDB_REQ_SET_LOCATION(down_req);
1255 if (ret != LDB_SUCCESS) {
1256 talloc_free(ac);
1257 return ret;
1260 /* current partition control is needed by "replmd_op_callback" */
1261 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1262 ret = ldb_request_add_control(down_req,
1263 DSDB_CONTROL_CURRENT_PARTITION_OID,
1264 false, NULL);
1265 if (ret != LDB_SUCCESS) {
1266 talloc_free(ac);
1267 return ret;
1271 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1272 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1273 if (ret != LDB_SUCCESS) {
1274 talloc_free(ac);
1275 return ret;
1279 /* mark the control done */
1280 if (control) {
1281 control->critical = 0;
1283 /* go on with the call chain */
1284 return ldb_next_request(module, down_req);
1289 * update the replPropertyMetaData for one element
1291 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1292 struct ldb_message *msg,
1293 struct ldb_message_element *el,
1294 struct ldb_message_element *old_el,
1295 struct replPropertyMetaDataBlob *omd,
1296 const struct dsdb_schema *schema,
1297 uint64_t *seq_num,
1298 const struct GUID *our_invocation_id,
1299 NTTIME now,
1300 bool is_schema_nc,
1301 struct ldb_request *req)
1303 uint32_t i;
1304 const struct dsdb_attribute *a;
1305 struct replPropertyMetaData1 *md1;
1306 bool may_skip = false;
1307 uint32_t attid;
1309 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1310 if (a == NULL) {
1311 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1312 /* allow this to make it possible for dbcheck
1313 to remove bad attributes */
1314 return LDB_SUCCESS;
1317 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1318 el->name));
1319 return LDB_ERR_OPERATIONS_ERROR;
1322 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1324 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1325 return LDB_SUCCESS;
1329 * if the attribute's value haven't changed, and this isn't
1330 * just a delete of everything then return LDB_SUCCESS Unless
1331 * we have the provision control or if the attribute is
1332 * interSiteTopologyGenerator as this page explain:
1333 * http://support.microsoft.com/kb/224815 this attribute is
1334 * periodicaly written by the DC responsible for the intersite
1335 * generation in a given site
1337 * Unchanged could be deleting or replacing an already-gone
1338 * thing with an unconstrained delete/empty replace or a
1339 * replace with the same value, but not an add with the same
1340 * value because that could be about adding a duplicate (which
1341 * is for someone else to error out on).
1343 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1344 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1345 may_skip = true;
1347 } else if (old_el == NULL && el->num_values == 0) {
1348 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1349 may_skip = true;
1350 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1351 may_skip = true;
1353 } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1354 ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
1356 * We intentionally skip the version bump when attempting to
1357 * vanish links.
1359 * The control is set by dbcheck and expunge-tombstones which
1360 * both attempt to be non-replicating. Otherwise, making an
1361 * alteration to the replication state would trigger a
1362 * broadcast of all expunged objects.
1364 may_skip = true;
1367 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1368 may_skip = false;
1369 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1372 if (may_skip) {
1373 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1374 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1376 * allow this to make it possible for dbcheck
1377 * to rebuild broken metadata
1379 return LDB_SUCCESS;
1383 for (i=0; i<omd->ctr.ctr1.count; i++) {
1385 * First check if we find it under the msDS-IntID,
1386 * then check if we find it under the OID and
1387 * prefixMap ID.
1389 * This allows the administrator to simply re-write
1390 * the attributes and so restore replication, which is
1391 * likely what they will try to do.
1393 if (attid == omd->ctr.ctr1.array[i].attid) {
1394 break;
1397 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1398 break;
1402 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1403 /* linked attributes are not stored in
1404 replPropertyMetaData in FL above w2k, but we do
1405 raise the seqnum for the object */
1406 if (*seq_num == 0 &&
1407 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1408 return LDB_ERR_OPERATIONS_ERROR;
1410 return LDB_SUCCESS;
1413 if (i == omd->ctr.ctr1.count) {
1414 /* we need to add a new one */
1415 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1416 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1417 if (omd->ctr.ctr1.array == NULL) {
1418 ldb_oom(ldb);
1419 return LDB_ERR_OPERATIONS_ERROR;
1421 omd->ctr.ctr1.count++;
1422 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1425 /* Get a new sequence number from the backend. We only do this
1426 * if we have a change that requires a new
1427 * replPropertyMetaData element
1429 if (*seq_num == 0) {
1430 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1431 if (ret != LDB_SUCCESS) {
1432 return LDB_ERR_OPERATIONS_ERROR;
1436 md1 = &omd->ctr.ctr1.array[i];
1437 md1->version++;
1438 md1->attid = attid;
1439 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1440 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1441 const char* rdn;
1443 if (rdn_val == NULL) {
1444 ldb_oom(ldb);
1445 return LDB_ERR_OPERATIONS_ERROR;
1448 rdn = (const char*)rdn_val->data;
1449 if (strcmp(rdn, "Deleted Objects") == 0) {
1451 * Set the originating_change_time to 29/12/9999 at 23:59:59
1452 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1454 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1455 } else {
1456 md1->originating_change_time = now;
1458 } else {
1459 md1->originating_change_time = now;
1461 md1->originating_invocation_id = *our_invocation_id;
1462 md1->originating_usn = *seq_num;
1463 md1->local_usn = *seq_num;
1465 return LDB_SUCCESS;
1469 * Bump the replPropertyMetaData version on an attribute, and if it
1470 * has changed (or forced by leaving rdn_old NULL), update the value
1471 * in the entry.
1473 * This is important, as calling a modify operation may not change the
1474 * version number if the values appear unchanged, but a rename between
1475 * parents bumps this value.
1478 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1479 struct ldb_message *msg,
1480 const struct ldb_val *rdn_new,
1481 const struct ldb_val *rdn_old,
1482 struct replPropertyMetaDataBlob *omd,
1483 struct replmd_replicated_request *ar,
1484 NTTIME now,
1485 bool is_schema_nc)
1487 const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
1488 const struct dsdb_attribute *rdn_attr =
1489 dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
1490 const char *attr_name = rdn_attr != NULL ?
1491 rdn_attr->lDAPDisplayName :
1492 rdn_name;
1493 struct ldb_message_element new_el = {
1494 .flags = LDB_FLAG_MOD_REPLACE,
1495 .name = attr_name,
1496 .num_values = 1,
1497 .values = discard_const_p(struct ldb_val, rdn_new)
1499 struct ldb_message_element old_el = {
1500 .flags = LDB_FLAG_MOD_REPLACE,
1501 .name = attr_name,
1502 .num_values = rdn_old ? 1 : 0,
1503 .values = discard_const_p(struct ldb_val, rdn_old)
1506 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1507 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1508 if (ret != LDB_SUCCESS) {
1509 return ldb_oom(ldb);
1513 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1514 omd, ar->schema, &ar->seq_num,
1515 &ar->our_invocation_id,
1516 now, is_schema_nc, ar->req);
1520 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1522 uint32_t count = omd.ctr.ctr1.count;
1523 uint64_t max = 0;
1524 uint32_t i;
1525 for (i=0; i < count; i++) {
1526 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1527 if (max < m.local_usn) {
1528 max = m.local_usn;
1531 return max;
1535 * update the replPropertyMetaData object each time we modify an
1536 * object. This is needed for DRS replication, as the merge on the
1537 * client is based on this object
1539 static int replmd_update_rpmd(struct ldb_module *module,
1540 const struct dsdb_schema *schema,
1541 struct ldb_request *req,
1542 const char * const *rename_attrs,
1543 struct ldb_message *msg, uint64_t *seq_num,
1544 time_t t, bool is_schema_nc,
1545 bool *is_urgent, bool *rodc)
1547 const struct ldb_val *omd_value;
1548 enum ndr_err_code ndr_err;
1549 struct replPropertyMetaDataBlob omd;
1550 unsigned int i;
1551 NTTIME now;
1552 const struct GUID *our_invocation_id;
1553 int ret;
1554 const char * const *attrs = NULL;
1555 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1556 struct ldb_result *res;
1557 struct ldb_context *ldb;
1558 struct ldb_message_element *objectclass_el;
1559 enum urgent_situation situation;
1560 bool rmd_is_provided;
1561 bool rmd_is_just_resorted = false;
1562 const char *not_rename_attrs[4 + msg->num_elements];
1564 if (rename_attrs) {
1565 attrs = rename_attrs;
1566 } else {
1567 for (i = 0; i < msg->num_elements; i++) {
1568 not_rename_attrs[i] = msg->elements[i].name;
1570 not_rename_attrs[i] = "replPropertyMetaData";
1571 not_rename_attrs[i+1] = "objectClass";
1572 not_rename_attrs[i+2] = "instanceType";
1573 not_rename_attrs[i+3] = NULL;
1574 attrs = not_rename_attrs;
1577 ldb = ldb_module_get_ctx(module);
1579 our_invocation_id = samdb_ntds_invocation_id(ldb);
1580 if (!our_invocation_id) {
1581 /* this happens during an initial vampire while
1582 updating the schema */
1583 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1584 return LDB_SUCCESS;
1587 unix_to_nt_time(&now, t);
1589 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1590 rmd_is_provided = true;
1591 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1592 rmd_is_just_resorted = true;
1594 } else {
1595 rmd_is_provided = false;
1598 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1599 * otherwise we consider we are updating */
1600 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1601 situation = REPL_URGENT_ON_DELETE;
1602 } else if (rename_attrs) {
1603 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1604 } else {
1605 situation = REPL_URGENT_ON_UPDATE;
1608 if (rmd_is_provided) {
1609 /* In this case the change_replmetadata control was supplied */
1610 /* We check that it's the only attribute that is provided
1611 * (it's a rare case so it's better to keep the code simplier)
1612 * We also check that the highest local_usn is bigger or the same as
1613 * uSNChanged. */
1614 uint64_t db_seq;
1615 if( msg->num_elements != 1 ||
1616 strncmp(msg->elements[0].name,
1617 "replPropertyMetaData", 20) ) {
1618 DEBUG(0,(__location__ ": changereplmetada control called without "\
1619 "a specified replPropertyMetaData attribute or with others\n"));
1620 return LDB_ERR_OPERATIONS_ERROR;
1622 if (situation != REPL_URGENT_ON_UPDATE) {
1623 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1624 return LDB_ERR_OPERATIONS_ERROR;
1626 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1627 if (!omd_value) {
1628 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1629 ldb_dn_get_linearized(msg->dn)));
1630 return LDB_ERR_OPERATIONS_ERROR;
1632 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1633 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1634 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1635 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1636 ldb_dn_get_linearized(msg->dn)));
1637 return LDB_ERR_OPERATIONS_ERROR;
1640 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1641 DSDB_FLAG_NEXT_MODULE |
1642 DSDB_SEARCH_SHOW_RECYCLED |
1643 DSDB_SEARCH_SHOW_EXTENDED_DN |
1644 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1645 DSDB_SEARCH_REVEAL_INTERNALS, req);
1647 if (ret != LDB_SUCCESS) {
1648 return ret;
1651 if (rmd_is_just_resorted == false) {
1652 *seq_num = find_max_local_usn(omd);
1654 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1657 * The test here now allows for a new
1658 * replPropertyMetaData with no change, if was
1659 * just dbcheck re-sorting the values.
1661 if (*seq_num <= db_seq) {
1662 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1663 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1664 (long long)*seq_num, (long long)db_seq));
1665 return LDB_ERR_OPERATIONS_ERROR;
1669 } else {
1670 /* search for the existing replPropertyMetaDataBlob. We need
1671 * to use REVEAL and ask for DNs in storage format to support
1672 * the check for values being the same in
1673 * replmd_update_rpmd_element()
1675 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1676 DSDB_FLAG_NEXT_MODULE |
1677 DSDB_SEARCH_SHOW_RECYCLED |
1678 DSDB_SEARCH_SHOW_EXTENDED_DN |
1679 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1680 DSDB_SEARCH_REVEAL_INTERNALS, req);
1681 if (ret != LDB_SUCCESS) {
1682 return ret;
1685 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1686 if (!omd_value) {
1687 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1688 ldb_dn_get_linearized(msg->dn)));
1689 return LDB_ERR_OPERATIONS_ERROR;
1692 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1693 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1694 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1695 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1696 ldb_dn_get_linearized(msg->dn)));
1697 return LDB_ERR_OPERATIONS_ERROR;
1700 if (omd.version != 1) {
1701 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1702 omd.version, ldb_dn_get_linearized(msg->dn)));
1703 return LDB_ERR_OPERATIONS_ERROR;
1706 for (i=0; i<msg->num_elements;) {
1707 struct ldb_message_element *el = &msg->elements[i];
1708 struct ldb_message_element *old_el;
1710 old_el = ldb_msg_find_element(res->msgs[0], el->name);
1711 ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1712 &omd, schema, seq_num,
1713 our_invocation_id,
1714 now, is_schema_nc,
1715 req);
1716 if (ret != LDB_SUCCESS) {
1717 return ret;
1720 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1721 *is_urgent = replmd_check_urgent_attribute(el);
1724 if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1725 i++;
1726 continue;
1729 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1731 if (el->num_values != 0) {
1732 i++;
1733 continue;
1736 ldb_msg_remove_element(msg, el);
1741 * Assert that we have an objectClass attribute - this is major
1742 * corruption if we don't have this!
1744 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1745 if (objectclass_el != NULL) {
1747 * Now check if this objectClass means we need to do urgent replication
1749 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1750 situation)) {
1751 *is_urgent = true;
1753 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1754 ldb_asprintf_errstring(ldb, __location__
1755 ": objectClass missing on %s\n",
1756 ldb_dn_get_linearized(msg->dn));
1757 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1761 * replmd_update_rpmd_element has done an update if the
1762 * seq_num is set
1764 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1765 struct ldb_val *md_value;
1766 struct ldb_message_element *el;
1768 /*if we are RODC and this is a DRSR update then its ok*/
1769 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1770 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)) {
1771 unsigned instanceType;
1773 ret = samdb_rodc(ldb, rodc);
1774 if (ret != LDB_SUCCESS) {
1775 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1776 } else if (*rodc) {
1777 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1778 return LDB_ERR_REFERRAL;
1781 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1782 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1783 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1784 "cannot change replicated attribute on partial replica");
1788 md_value = talloc(msg, struct ldb_val);
1789 if (md_value == NULL) {
1790 ldb_oom(ldb);
1791 return LDB_ERR_OPERATIONS_ERROR;
1794 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
1795 if (ret != LDB_SUCCESS) {
1796 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
1797 return ret;
1800 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1801 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1802 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1803 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1804 ldb_dn_get_linearized(msg->dn)));
1805 return LDB_ERR_OPERATIONS_ERROR;
1808 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1809 if (ret != LDB_SUCCESS) {
1810 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1811 ldb_dn_get_linearized(msg->dn)));
1812 return ret;
1815 el->num_values = 1;
1816 el->values = md_value;
1819 return LDB_SUCCESS;
1822 struct compare_ctx {
1823 struct GUID *guid;
1824 struct ldb_context *ldb;
1825 TALLOC_CTX *mem_ctx;
1826 const char *ldap_oid;
1827 int err;
1828 const struct GUID *invocation_id;
1831 /* When a parsed_dn comes from the database, sometimes it is not really parsed. */
1833 static int really_parse_trusted_dn(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
1834 struct parsed_dn *pdn, const char *ldap_oid)
1836 NTSTATUS status;
1837 struct dsdb_dn *dsdb_dn = dsdb_dn_parse_trusted(mem_ctx, ldb, pdn->v,
1838 ldap_oid);
1839 if (dsdb_dn == NULL) {
1840 return LDB_ERR_INVALID_DN_SYNTAX;
1843 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &pdn->guid, "GUID");
1844 if (!NT_STATUS_IS_OK(status)) {
1845 return LDB_ERR_OPERATIONS_ERROR;
1847 pdn->dsdb_dn = dsdb_dn;
1848 return LDB_SUCCESS;
1852 * We choose, as the sort order, the same order as is used in DRS replication,
1853 * which is the memcmp() order of the NDR GUID, not that obtained from
1854 * GUID_compare().
1856 * This means that sorted links will be in the same order as a new DC would
1857 * see them.
1859 static int ndr_guid_compare(struct GUID *guid1, struct GUID *guid2)
1861 uint8_t v1_data[16];
1862 struct ldb_val v1 = data_blob_const(v1_data, sizeof(v1_data));
1863 uint8_t v2_data[16];
1864 struct ldb_val v2 = data_blob_const(v2_data, sizeof(v2_data));
1866 /* This can't fail */
1867 ndr_push_struct_into_fixed_blob(&v1, guid1,
1868 (ndr_push_flags_fn_t)ndr_push_GUID);
1869 /* This can't fail */
1870 ndr_push_struct_into_fixed_blob(&v2, guid2,
1871 (ndr_push_flags_fn_t)ndr_push_GUID);
1872 return data_blob_cmp(&v1, &v2);
1875 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1877 return ndr_guid_compare(&pdn1->guid, &pdn2->guid);
1880 static int la_guid_compare_with_trusted_dn(struct compare_ctx *ctx,
1881 struct parsed_dn *p)
1884 * This works like a standard compare function in its return values,
1885 * but has an extra trick to deal with errors: zero is returned and
1886 * ctx->err is set to the ldb error code.
1888 * That is, if (as is expected in most cases) you get a non-zero
1889 * result, you don't need to check for errors.
1891 * We assume the second argument refers to a DN is from the database
1892 * and has a GUID -- but this GUID might not have been parsed out yet.
1894 if (p->dsdb_dn == NULL) {
1895 int ret = really_parse_trusted_dn(ctx->mem_ctx, ctx->ldb, p,
1896 ctx->ldap_oid);
1897 if (ret != LDB_SUCCESS) {
1898 ctx->err = ret;
1899 return 0;
1902 return ndr_guid_compare(ctx->guid, &p->guid);
1907 static int parsed_dn_find(struct ldb_context *ldb, struct parsed_dn *pdn,
1908 unsigned int count,
1909 struct GUID *guid,
1910 struct ldb_dn *target_dn,
1911 struct parsed_dn **exact,
1912 struct parsed_dn **next,
1913 const char *ldap_oid)
1915 unsigned int i;
1916 struct compare_ctx ctx;
1917 if (pdn == NULL) {
1918 *exact = NULL;
1919 *next = NULL;
1920 return LDB_SUCCESS;
1923 if (unlikely(GUID_all_zero(guid))) {
1925 * When updating a link using DRS, we sometimes get a NULL
1926 * GUID when a forward link has been deleted and its GUID has
1927 * for some reason been forgotten. The best we can do is try
1928 * and match by DN via a linear search. Note that this
1929 * probably only happens in the ADD case, in which we only
1930 * allow modification of link if it is already deleted, so
1931 * this seems very close to an elaborate NO-OP, but we are not
1932 * quite prepared to declare it so.
1934 * If the DN is not in our list, we have to add it to the
1935 * beginning of the list, where it would naturally sort.
1937 struct parsed_dn *p;
1938 if (target_dn == NULL) {
1939 /* We don't know the target DN, so we can't search for DN */
1940 DEBUG(1, ("parsed_dn_find has a NULL GUID for a linked "
1941 "attribute but we don't have a DN to compare "
1942 "it with\n"));
1943 return LDB_ERR_OPERATIONS_ERROR;
1945 *exact = NULL;
1946 *next = NULL;
1948 DEBUG(3, ("parsed_dn_find has a NULL GUID for a link to DN "
1949 "%s; searching through links for it",
1950 ldb_dn_get_linearized(target_dn)));
1952 for (i = 0; i < count; i++) {
1953 int cmp;
1954 p = &pdn[i];
1955 if (p->dsdb_dn == NULL) {
1956 int ret = really_parse_trusted_dn(pdn, ldb, p, ldap_oid);
1957 if (ret != LDB_SUCCESS) {
1958 return LDB_ERR_OPERATIONS_ERROR;
1962 cmp = ldb_dn_compare(p->dsdb_dn->dn, target_dn);
1963 if (cmp == 0) {
1964 *exact = p;
1965 return LDB_SUCCESS;
1969 * Here we have a null guid which doesn't match any existing
1970 * link. This is a bit unexpected because null guids occur
1971 * when a forward link has been deleted and we are replicating
1972 * that deletion.
1974 * The best thing to do is weep into the logs and add the
1975 * offending link to the beginning of the list which is
1976 * at least the correct sort position.
1978 DEBUG(1, ("parsed_dn_find has been given a NULL GUID for a "
1979 "link to unknown DN %s\n",
1980 ldb_dn_get_linearized(target_dn)));
1981 *next = pdn;
1982 return LDB_SUCCESS;
1985 ctx.guid = guid;
1986 ctx.ldb = ldb;
1987 ctx.mem_ctx = pdn;
1988 ctx.ldap_oid = ldap_oid;
1989 ctx.err = 0;
1991 BINARY_ARRAY_SEARCH_GTE(pdn, count, &ctx, la_guid_compare_with_trusted_dn,
1992 *exact, *next);
1994 if (ctx.err != 0) {
1995 return ctx.err;
1997 return LDB_SUCCESS;
2001 get a series of message element values as an array of DNs and GUIDs
2002 the result is sorted by GUID
2004 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
2005 struct ldb_message_element *el, struct parsed_dn **pdn,
2006 const char *ldap_oid, struct ldb_request *parent)
2008 unsigned int i;
2009 bool values_are_sorted = true;
2010 struct ldb_context *ldb = ldb_module_get_ctx(module);
2012 if (el == NULL) {
2013 *pdn = NULL;
2014 return LDB_SUCCESS;
2017 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
2018 if (!*pdn) {
2019 ldb_module_oom(module);
2020 return LDB_ERR_OPERATIONS_ERROR;
2023 for (i=0; i<el->num_values; i++) {
2024 struct ldb_val *v = &el->values[i];
2025 NTSTATUS status;
2026 struct ldb_dn *dn;
2027 struct parsed_dn *p;
2029 p = &(*pdn)[i];
2031 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
2032 if (p->dsdb_dn == NULL) {
2033 return LDB_ERR_INVALID_DN_SYNTAX;
2036 dn = p->dsdb_dn->dn;
2038 status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
2039 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
2040 unlikely(GUID_all_zero(&p->guid))) {
2041 /* we got a DN without a GUID - go find the GUID */
2042 int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
2043 if (ret != LDB_SUCCESS) {
2044 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
2045 ldb_dn_get_linearized(dn));
2046 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
2047 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
2048 ldb_attr_cmp(el->name, "member") == 0) {
2049 return LDB_ERR_UNWILLING_TO_PERFORM;
2051 return ret;
2053 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2054 if (ret != LDB_SUCCESS) {
2055 return ret;
2057 } else if (!NT_STATUS_IS_OK(status)) {
2058 return LDB_ERR_OPERATIONS_ERROR;
2060 if (i > 0 && values_are_sorted) {
2061 int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
2062 if (cmp < 0) {
2063 values_are_sorted = false;
2066 /* keep a pointer to the original ldb_val */
2067 p->v = v;
2069 if (! values_are_sorted) {
2070 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
2072 return LDB_SUCCESS;
2076 * Get a series of trusted message element values. The result is sorted by
2077 * GUID, even though the GUIDs might not be known. That works because we trust
2078 * the database to give us the elements like that if the
2079 * replmd_private->sorted_links flag is set.
2081 * We also ensure that the links are in the Functional Level 2003
2082 * linked attributes format.
2084 static int get_parsed_dns_trusted(struct ldb_module *module,
2085 struct replmd_private *replmd_private,
2086 TALLOC_CTX *mem_ctx,
2087 struct ldb_message_element *el,
2088 struct parsed_dn **pdn,
2089 const char *ldap_oid,
2090 struct ldb_request *parent)
2092 unsigned int i;
2093 int ret;
2094 if (el == NULL) {
2095 *pdn = NULL;
2096 return LDB_SUCCESS;
2099 if (!replmd_private->sorted_links) {
2100 /* We need to sort the list. This is the slow old path we want
2101 to avoid.
2103 ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2104 parent);
2105 if (ret != LDB_SUCCESS) {
2106 return ret;
2108 } else {
2109 /* Here we get a list of 'struct parsed_dns' without the parsing */
2110 *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
2111 el->num_values);
2112 if (!*pdn) {
2113 ldb_module_oom(module);
2114 return LDB_ERR_OPERATIONS_ERROR;
2117 for (i = 0; i < el->num_values; i++) {
2118 (*pdn)[i].v = &el->values[i];
2123 * This upgrades links to FL2003 style, and sorts the result
2124 * if that was needed.
2126 * TODO: Add a database feature that asserts we have no FL2000
2127 * style links to avoid this check or add a feature that
2128 * uses a similar check to find sorted/unsorted links
2129 * for an on-the-fly upgrade.
2132 ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
2133 *pdn, el->num_values,
2135 ldap_oid);
2136 if (ret != LDB_SUCCESS) {
2137 return ret;
2140 return LDB_SUCCESS;
2144 build a new extended DN, including all meta data fields
2146 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2147 RMD_ADDTIME = originating_add_time
2148 RMD_INVOCID = originating_invocation_id
2149 RMD_CHANGETIME = originating_change_time
2150 RMD_ORIGINATING_USN = originating_usn
2151 RMD_LOCAL_USN = local_usn
2152 RMD_VERSION = version
2154 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2155 const struct GUID *invocation_id, uint64_t seq_num,
2156 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
2158 struct ldb_dn *dn = dsdb_dn->dn;
2159 const char *tstring, *usn_string, *flags_string;
2160 struct ldb_val tval;
2161 struct ldb_val iid;
2162 struct ldb_val usnv, local_usnv;
2163 struct ldb_val vers, flagsv;
2164 NTSTATUS status;
2165 int ret;
2166 const char *dnstring;
2167 char *vstring;
2168 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2170 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2171 if (!tstring) {
2172 return LDB_ERR_OPERATIONS_ERROR;
2174 tval = data_blob_string_const(tstring);
2176 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
2177 if (!usn_string) {
2178 return LDB_ERR_OPERATIONS_ERROR;
2180 usnv = data_blob_string_const(usn_string);
2182 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2183 if (!usn_string) {
2184 return LDB_ERR_OPERATIONS_ERROR;
2186 local_usnv = data_blob_string_const(usn_string);
2188 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2189 if (!vstring) {
2190 return LDB_ERR_OPERATIONS_ERROR;
2192 vers = data_blob_string_const(vstring);
2194 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2195 if (!NT_STATUS_IS_OK(status)) {
2196 return LDB_ERR_OPERATIONS_ERROR;
2199 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2200 if (!flags_string) {
2201 return LDB_ERR_OPERATIONS_ERROR;
2203 flagsv = data_blob_string_const(flags_string);
2205 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2206 if (ret != LDB_SUCCESS) return ret;
2207 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
2208 if (ret != LDB_SUCCESS) return ret;
2209 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2210 if (ret != LDB_SUCCESS) return ret;
2211 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2212 if (ret != LDB_SUCCESS) return ret;
2213 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2214 if (ret != LDB_SUCCESS) return ret;
2215 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2216 if (ret != LDB_SUCCESS) return ret;
2217 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2218 if (ret != LDB_SUCCESS) return ret;
2220 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2221 if (dnstring == NULL) {
2222 return LDB_ERR_OPERATIONS_ERROR;
2224 *v = data_blob_string_const(dnstring);
2226 return LDB_SUCCESS;
2229 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2230 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2231 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
2232 uint32_t version, bool deleted);
2235 check if any links need upgrading from w2k format
2237 static int replmd_check_upgrade_links(struct ldb_context *ldb,
2238 struct parsed_dn *dns, uint32_t count,
2239 struct ldb_message_element *el,
2240 const char *ldap_oid)
2242 uint32_t i;
2243 const struct GUID *invocation_id = NULL;
2244 for (i=0; i<count; i++) {
2245 NTSTATUS status;
2246 uint32_t version;
2247 int ret;
2248 if (dns[i].dsdb_dn == NULL) {
2249 ret = really_parse_trusted_dn(dns, ldb, &dns[i],
2250 ldap_oid);
2251 if (ret != LDB_SUCCESS) {
2252 return LDB_ERR_INVALID_DN_SYNTAX;
2256 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
2257 &version, "RMD_VERSION");
2258 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2260 * We optimistically assume they are all the same; if
2261 * the first one is fixed, they are all fixed.
2263 * If the first one was *not* fixed and we find a
2264 * later one that is, that is an occasion to shout
2265 * with DEBUG(0).
2267 if (i == 0) {
2268 return LDB_SUCCESS;
2270 DEBUG(0, ("Mixed w2k and fixed format "
2271 "linked attributes\n"));
2272 continue;
2275 if (invocation_id == NULL) {
2276 invocation_id = samdb_ntds_invocation_id(ldb);
2277 if (invocation_id == NULL) {
2278 return LDB_ERR_OPERATIONS_ERROR;
2283 /* it's an old one that needs upgrading */
2284 ret = replmd_update_la_val(el->values, dns[i].v,
2285 dns[i].dsdb_dn, dns[i].dsdb_dn,
2286 invocation_id, 1, 1, 0, 0, false);
2287 if (ret != LDB_SUCCESS) {
2288 return ret;
2293 * This sort() is critical for the operation of
2294 * get_parsed_dns_trusted() because callers of this function
2295 * expect a sorted list, and FL2000 style links are not
2296 * sorted. In particular, as well as the upgrade case,
2297 * get_parsed_dns_trusted() is called from
2298 * replmd_delete_remove_link() even in FL2000 mode
2300 * We do not normally pay the cost of the qsort() due to the
2301 * early return in the RMD_VERSION found case.
2303 TYPESAFE_QSORT(dns, count, parsed_dn_compare);
2304 return LDB_SUCCESS;
2308 update an extended DN, including all meta data fields
2310 see replmd_build_la_val for value names
2312 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2313 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2314 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2315 uint32_t version, bool deleted)
2317 struct ldb_dn *dn = dsdb_dn->dn;
2318 const char *tstring, *usn_string, *flags_string;
2319 struct ldb_val tval;
2320 struct ldb_val iid;
2321 struct ldb_val usnv, local_usnv;
2322 struct ldb_val vers, flagsv;
2323 const struct ldb_val *old_addtime;
2324 uint32_t old_version;
2325 NTSTATUS status;
2326 int ret;
2327 const char *dnstring;
2328 char *vstring;
2329 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2331 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2332 if (!tstring) {
2333 return LDB_ERR_OPERATIONS_ERROR;
2335 tval = data_blob_string_const(tstring);
2337 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2338 if (!usn_string) {
2339 return LDB_ERR_OPERATIONS_ERROR;
2341 usnv = data_blob_string_const(usn_string);
2343 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2344 if (!usn_string) {
2345 return LDB_ERR_OPERATIONS_ERROR;
2347 local_usnv = data_blob_string_const(usn_string);
2349 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2350 if (!NT_STATUS_IS_OK(status)) {
2351 return LDB_ERR_OPERATIONS_ERROR;
2354 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2355 if (!flags_string) {
2356 return LDB_ERR_OPERATIONS_ERROR;
2358 flagsv = data_blob_string_const(flags_string);
2360 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2361 if (ret != LDB_SUCCESS) return ret;
2363 /* get the ADDTIME from the original */
2364 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
2365 if (old_addtime == NULL) {
2366 old_addtime = &tval;
2368 if (dsdb_dn != old_dsdb_dn ||
2369 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2370 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2371 if (ret != LDB_SUCCESS) return ret;
2374 /* use our invocation id */
2375 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2376 if (ret != LDB_SUCCESS) return ret;
2378 /* changetime is the current time */
2379 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2380 if (ret != LDB_SUCCESS) return ret;
2382 /* update the USN */
2383 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2384 if (ret != LDB_SUCCESS) return ret;
2386 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2387 if (ret != LDB_SUCCESS) return ret;
2389 /* increase the version by 1 */
2390 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
2391 if (NT_STATUS_IS_OK(status) && old_version >= version) {
2392 version = old_version+1;
2394 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
2395 vers = data_blob_string_const(vstring);
2396 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2397 if (ret != LDB_SUCCESS) return ret;
2399 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2400 if (dnstring == NULL) {
2401 return LDB_ERR_OPERATIONS_ERROR;
2403 *v = data_blob_string_const(dnstring);
2405 return LDB_SUCCESS;
2409 handle adding a linked attribute
2411 static int replmd_modify_la_add(struct ldb_module *module,
2412 struct replmd_private *replmd_private,
2413 const struct dsdb_schema *schema,
2414 struct ldb_message *msg,
2415 struct ldb_message_element *el,
2416 struct ldb_message_element *old_el,
2417 const struct dsdb_attribute *schema_attr,
2418 uint64_t seq_num,
2419 time_t t,
2420 struct GUID *msg_guid,
2421 struct ldb_request *parent)
2423 unsigned int i, j;
2424 struct parsed_dn *dns, *old_dns;
2425 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2426 int ret;
2427 struct ldb_val *new_values = NULL;
2428 unsigned old_num_values = old_el ? old_el->num_values : 0;
2429 unsigned num_values = 0;
2430 unsigned max_num_values;
2431 const struct GUID *invocation_id;
2432 struct ldb_context *ldb = ldb_module_get_ctx(module);
2433 NTTIME now;
2434 unix_to_nt_time(&now, t);
2436 invocation_id = samdb_ntds_invocation_id(ldb);
2437 if (!invocation_id) {
2438 talloc_free(tmp_ctx);
2439 return LDB_ERR_OPERATIONS_ERROR;
2442 /* get the DNs to be added, fully parsed.
2444 * We need full parsing because they came off the wire and we don't
2445 * trust them, besides which we need their details to know where to put
2446 * them.
2448 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2449 schema_attr->syntax->ldap_oid, parent);
2450 if (ret != LDB_SUCCESS) {
2451 talloc_free(tmp_ctx);
2452 return ret;
2455 /* get the existing DNs, lazily parsed */
2456 ret = get_parsed_dns_trusted(module, replmd_private,
2457 tmp_ctx, old_el, &old_dns,
2458 schema_attr->syntax->ldap_oid, parent);
2460 if (ret != LDB_SUCCESS) {
2461 talloc_free(tmp_ctx);
2462 return ret;
2465 max_num_values = old_num_values + el->num_values;
2466 if (max_num_values < old_num_values) {
2467 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2468 "old values: %u, new values: %u, sum: %u",
2469 old_num_values, el->num_values, max_num_values));
2470 talloc_free(tmp_ctx);
2471 return LDB_ERR_OPERATIONS_ERROR;
2474 new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2476 if (new_values == NULL) {
2477 ldb_module_oom(module);
2478 talloc_free(tmp_ctx);
2479 return LDB_ERR_OPERATIONS_ERROR;
2483 * For each new value, find where it would go in the list. If there is
2484 * a matching GUID there, we update the existing value; otherwise we
2485 * put it in place.
2487 j = 0;
2488 for (i = 0; i < el->num_values; i++) {
2489 struct parsed_dn *exact;
2490 struct parsed_dn *next;
2491 unsigned offset;
2492 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2493 &dns[i].guid,
2494 dns[i].dsdb_dn->dn,
2495 &exact, &next,
2496 schema_attr->syntax->ldap_oid);
2497 if (err != LDB_SUCCESS) {
2498 talloc_free(tmp_ctx);
2499 return err;
2502 if (exact != NULL) {
2504 * We are trying to add one that exists, which is only
2505 * allowed if it was previously deleted.
2507 * When we do undelete a link we change it in place.
2508 * It will be copied across into the right spot in due
2509 * course.
2511 uint32_t rmd_flags;
2512 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2514 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2515 struct GUID_txt_buf guid_str;
2516 ldb_asprintf_errstring(ldb,
2517 "Attribute %s already "
2518 "exists for target GUID %s",
2519 el->name,
2520 GUID_buf_string(&exact->guid,
2521 &guid_str));
2522 talloc_free(tmp_ctx);
2523 /* error codes for 'member' need to be
2524 special cased */
2525 if (ldb_attr_cmp(el->name, "member") == 0) {
2526 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2527 } else {
2528 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2532 ret = replmd_update_la_val(new_values, exact->v,
2533 dns[i].dsdb_dn,
2534 exact->dsdb_dn,
2535 invocation_id, seq_num,
2536 seq_num, now, 0, false);
2537 if (ret != LDB_SUCCESS) {
2538 talloc_free(tmp_ctx);
2539 return ret;
2542 ret = replmd_add_backlink(module, replmd_private,
2543 schema, msg_guid,
2544 &dns[i].guid, true,
2545 schema_attr, true);
2546 if (ret != LDB_SUCCESS) {
2547 talloc_free(tmp_ctx);
2548 return ret;
2550 continue;
2553 * Here we don't have an exact match.
2555 * If next is NULL, this one goes beyond the end of the
2556 * existing list, so we need to add all of those ones first.
2558 * If next is not NULL, we need to add all the ones before
2559 * next.
2561 if (next == NULL) {
2562 offset = old_num_values;
2563 } else {
2564 /* next should have been parsed, but let's make sure */
2565 if (next->dsdb_dn == NULL) {
2566 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2567 schema_attr->syntax->ldap_oid);
2568 if (ret != LDB_SUCCESS) {
2569 return ret;
2572 offset = MIN(next - old_dns, old_num_values);
2575 /* put all the old ones before next on the list */
2576 for (; j < offset; j++) {
2577 new_values[num_values] = *old_dns[j].v;
2578 num_values++;
2581 ret = replmd_add_backlink(module, replmd_private,
2582 schema, msg_guid, &dns[i].guid,
2583 true, schema_attr, true);
2584 /* Make the new linked attribute ldb_val. */
2585 ret = replmd_build_la_val(new_values, &new_values[num_values],
2586 dns[i].dsdb_dn, invocation_id,
2587 seq_num, seq_num,
2588 now, 0, false);
2589 if (ret != LDB_SUCCESS) {
2590 talloc_free(tmp_ctx);
2591 return ret;
2593 num_values++;
2594 if (ret != LDB_SUCCESS) {
2595 talloc_free(tmp_ctx);
2596 return ret;
2599 /* copy the rest of the old ones (if any) */
2600 for (; j < old_num_values; j++) {
2601 new_values[num_values] = *old_dns[j].v;
2602 num_values++;
2605 talloc_steal(msg->elements, new_values);
2606 if (old_el != NULL) {
2607 talloc_steal(msg->elements, old_el->values);
2609 el->values = new_values;
2610 el->num_values = num_values;
2612 talloc_free(tmp_ctx);
2614 /* we now tell the backend to replace all existing values
2615 with the one we have constructed */
2616 el->flags = LDB_FLAG_MOD_REPLACE;
2618 return LDB_SUCCESS;
2623 handle deleting all active linked attributes
2625 static int replmd_modify_la_delete(struct ldb_module *module,
2626 struct replmd_private *replmd_private,
2627 const struct dsdb_schema *schema,
2628 struct ldb_message *msg,
2629 struct ldb_message_element *el,
2630 struct ldb_message_element *old_el,
2631 const struct dsdb_attribute *schema_attr,
2632 uint64_t seq_num,
2633 time_t t,
2634 struct GUID *msg_guid,
2635 struct ldb_request *parent)
2637 unsigned int i;
2638 struct parsed_dn *dns, *old_dns;
2639 TALLOC_CTX *tmp_ctx = NULL;
2640 int ret;
2641 struct ldb_context *ldb = ldb_module_get_ctx(module);
2642 struct ldb_control *vanish_links_ctrl = NULL;
2643 bool vanish_links = false;
2644 unsigned int num_to_delete = el->num_values;
2645 uint32_t rmd_flags;
2646 const struct GUID *invocation_id;
2647 NTTIME now;
2649 unix_to_nt_time(&now, t);
2651 invocation_id = samdb_ntds_invocation_id(ldb);
2652 if (!invocation_id) {
2653 return LDB_ERR_OPERATIONS_ERROR;
2656 if (old_el == NULL || old_el->num_values == 0) {
2657 /* there is nothing to delete... */
2658 if (num_to_delete == 0) {
2659 /* and we're deleting nothing, so that's OK */
2660 return LDB_SUCCESS;
2662 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2665 tmp_ctx = talloc_new(msg);
2666 if (tmp_ctx == NULL) {
2667 return LDB_ERR_OPERATIONS_ERROR;
2670 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2671 schema_attr->syntax->ldap_oid, parent);
2672 if (ret != LDB_SUCCESS) {
2673 talloc_free(tmp_ctx);
2674 return ret;
2677 ret = get_parsed_dns_trusted(module, replmd_private,
2678 tmp_ctx, old_el, &old_dns,
2679 schema_attr->syntax->ldap_oid, parent);
2681 if (ret != LDB_SUCCESS) {
2682 talloc_free(tmp_ctx);
2683 return ret;
2686 if (parent) {
2687 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2688 if (vanish_links_ctrl) {
2689 vanish_links = true;
2690 vanish_links_ctrl->critical = false;
2694 /* we empty out el->values here to avoid damage if we return early. */
2695 el->num_values = 0;
2696 el->values = NULL;
2699 * If vanish links is set, we are actually removing members of
2700 * old_el->values; otherwise we are just marking them deleted.
2702 * There is a special case when no values are given: we remove them
2703 * all. When we have the vanish_links control we just have to remove
2704 * the backlinks and change our element to replace the existing values
2705 * with the empty list.
2708 if (num_to_delete == 0) {
2709 for (i = 0; i < old_el->num_values; i++) {
2710 struct parsed_dn *p = &old_dns[i];
2711 if (p->dsdb_dn == NULL) {
2712 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2713 schema_attr->syntax->ldap_oid);
2714 if (ret != LDB_SUCCESS) {
2715 return ret;
2718 ret = replmd_add_backlink(module, replmd_private,
2719 schema, msg_guid, &p->guid,
2720 false, schema_attr, true);
2721 if (ret != LDB_SUCCESS) {
2722 talloc_free(tmp_ctx);
2723 return ret;
2725 if (vanish_links) {
2726 continue;
2729 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2730 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2731 continue;
2734 ret = replmd_update_la_val(old_el->values, p->v,
2735 p->dsdb_dn, p->dsdb_dn,
2736 invocation_id, seq_num,
2737 seq_num, now, 0, true);
2738 if (ret != LDB_SUCCESS) {
2739 talloc_free(tmp_ctx);
2740 return ret;
2744 if (vanish_links) {
2745 el->flags = LDB_FLAG_MOD_REPLACE;
2746 talloc_free(tmp_ctx);
2747 return LDB_SUCCESS;
2752 for (i = 0; i < num_to_delete; i++) {
2753 struct parsed_dn *p = &dns[i];
2754 struct parsed_dn *exact = NULL;
2755 struct parsed_dn *next = NULL;
2756 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2757 &p->guid,
2758 NULL,
2759 &exact, &next,
2760 schema_attr->syntax->ldap_oid);
2761 if (ret != LDB_SUCCESS) {
2762 talloc_free(tmp_ctx);
2763 return ret;
2765 if (exact == NULL) {
2766 struct GUID_txt_buf buf;
2767 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2768 "exist for target GUID %s",
2769 el->name,
2770 GUID_buf_string(&p->guid, &buf));
2771 if (ldb_attr_cmp(el->name, "member") == 0) {
2772 talloc_free(tmp_ctx);
2773 return LDB_ERR_UNWILLING_TO_PERFORM;
2774 } else {
2775 talloc_free(tmp_ctx);
2776 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2780 if (vanish_links) {
2781 if (CHECK_DEBUGLVL(5)) {
2782 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2783 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2784 struct GUID_txt_buf buf;
2785 const char *guid_str = \
2786 GUID_buf_string(&p->guid, &buf);
2787 DEBUG(5, ("Deleting deleted linked "
2788 "attribute %s to %s, because "
2789 "vanish_links control is set\n",
2790 el->name, guid_str));
2794 /* remove the backlink */
2795 ret = replmd_add_backlink(module,
2796 replmd_private,
2797 schema, msg_guid,
2798 &p->guid,
2799 false, schema_attr,
2800 true);
2801 if (ret != LDB_SUCCESS) {
2802 talloc_free(tmp_ctx);
2803 return ret;
2806 /* We flag the deletion and tidy it up later. */
2807 exact->v = NULL;
2808 continue;
2811 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2813 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2814 struct GUID_txt_buf buf;
2815 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2816 ldb_asprintf_errstring(ldb, "Attribute %s already "
2817 "deleted for target GUID %s",
2818 el->name, guid_str);
2819 if (ldb_attr_cmp(el->name, "member") == 0) {
2820 talloc_free(tmp_ctx);
2821 return LDB_ERR_UNWILLING_TO_PERFORM;
2822 } else {
2823 talloc_free(tmp_ctx);
2824 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2828 ret = replmd_update_la_val(old_el->values, exact->v,
2829 exact->dsdb_dn, exact->dsdb_dn,
2830 invocation_id, seq_num, seq_num,
2831 now, 0, true);
2832 if (ret != LDB_SUCCESS) {
2833 talloc_free(tmp_ctx);
2834 return ret;
2836 ret = replmd_add_backlink(module, replmd_private,
2837 schema, msg_guid, &p->guid,
2838 false, schema_attr, true);
2839 if (ret != LDB_SUCCESS) {
2840 talloc_free(tmp_ctx);
2841 return ret;
2845 if (vanish_links) {
2846 unsigned j = 0;
2847 for (i = 0; i < old_el->num_values; i++) {
2848 if (old_dns[i].v != NULL) {
2849 old_el->values[j] = *old_dns[i].v;
2850 j++;
2853 old_el->num_values = j;
2856 el->values = talloc_steal(msg->elements, old_el->values);
2857 el->num_values = old_el->num_values;
2859 talloc_free(tmp_ctx);
2861 /* we now tell the backend to replace all existing values
2862 with the one we have constructed */
2863 el->flags = LDB_FLAG_MOD_REPLACE;
2865 return LDB_SUCCESS;
2869 handle replacing a linked attribute
2871 static int replmd_modify_la_replace(struct ldb_module *module,
2872 struct replmd_private *replmd_private,
2873 const struct dsdb_schema *schema,
2874 struct ldb_message *msg,
2875 struct ldb_message_element *el,
2876 struct ldb_message_element *old_el,
2877 const struct dsdb_attribute *schema_attr,
2878 uint64_t seq_num,
2879 time_t t,
2880 struct GUID *msg_guid,
2881 struct ldb_request *parent)
2883 unsigned int i, old_i, new_i;
2884 struct parsed_dn *dns, *old_dns;
2885 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2886 int ret;
2887 const struct GUID *invocation_id;
2888 struct ldb_context *ldb = ldb_module_get_ctx(module);
2889 struct ldb_val *new_values = NULL;
2890 const char *ldap_oid = schema_attr->syntax->ldap_oid;
2891 unsigned int old_num_values;
2892 unsigned int repl_num_values;
2893 unsigned int max_num_values;
2894 NTTIME now;
2896 unix_to_nt_time(&now, t);
2898 invocation_id = samdb_ntds_invocation_id(ldb);
2899 if (!invocation_id) {
2900 return LDB_ERR_OPERATIONS_ERROR;
2904 * The replace operation is unlike the replace and delete cases in that
2905 * we need to look at every existing link to see whether it is being
2906 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
2908 * As we are trying to combine two sorted lists, the algorithm we use
2909 * is akin to the merge phase of a merge sort. We interleave the two
2910 * lists, doing different things depending on which side the current
2911 * item came from.
2913 * There are three main cases, with some sub-cases.
2915 * - a DN is in the old list but not the new one. It needs to be
2916 * marked as deleted (but left in the list).
2917 * - maybe it is already deleted, and we have less to do.
2919 * - a DN is in both lists. The old data gets replaced by the new,
2920 * and the list doesn't grow. The old link may have been marked as
2921 * deleted, in which case we undelete it.
2923 * - a DN is in the new list only. We add it in the right place.
2926 old_num_values = old_el ? old_el->num_values : 0;
2927 repl_num_values = el->num_values;
2928 max_num_values = old_num_values + repl_num_values;
2930 if (max_num_values == 0) {
2931 /* There is nothing to do! */
2932 return LDB_SUCCESS;
2935 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
2936 if (ret != LDB_SUCCESS) {
2937 talloc_free(tmp_ctx);
2938 return ret;
2941 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
2942 ldap_oid, parent);
2943 if (ret != LDB_SUCCESS) {
2944 talloc_free(tmp_ctx);
2945 return ret;
2948 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
2949 old_el, ldap_oid);
2950 if (ret != LDB_SUCCESS) {
2951 talloc_free(tmp_ctx);
2952 return ret;
2955 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
2956 if (new_values == NULL) {
2957 ldb_module_oom(module);
2958 talloc_free(tmp_ctx);
2959 return LDB_ERR_OPERATIONS_ERROR;
2962 old_i = 0;
2963 new_i = 0;
2964 for (i = 0; i < max_num_values; i++) {
2965 int cmp;
2966 struct parsed_dn *old_p, *new_p;
2967 if (old_i < old_num_values && new_i < repl_num_values) {
2968 old_p = &old_dns[old_i];
2969 new_p = &dns[new_i];
2970 cmp = parsed_dn_compare(old_p, new_p);
2971 } else if (old_i < old_num_values) {
2972 /* the new list is empty, read the old list */
2973 old_p = &old_dns[old_i];
2974 new_p = NULL;
2975 cmp = -1;
2976 } else if (new_i < repl_num_values) {
2977 /* the old list is empty, read new list */
2978 old_p = NULL;
2979 new_p = &dns[new_i];
2980 cmp = 1;
2981 } else {
2982 break;
2985 if (cmp < 0) {
2987 * An old ones that come before the next replacement
2988 * (if any). We mark it as deleted and add it to the
2989 * final list.
2991 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2992 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
2993 ret = replmd_update_la_val(new_values, old_p->v,
2994 old_p->dsdb_dn,
2995 old_p->dsdb_dn,
2996 invocation_id,
2997 seq_num, seq_num,
2998 now, 0, true);
2999 if (ret != LDB_SUCCESS) {
3000 talloc_free(tmp_ctx);
3001 return ret;
3004 ret = replmd_add_backlink(module, replmd_private,
3005 schema, msg_guid,
3006 &old_p->guid, false,
3007 schema_attr, true);
3008 if (ret != LDB_SUCCESS) {
3009 talloc_free(tmp_ctx);
3010 return ret;
3013 new_values[i] = *old_p->v;
3014 old_i++;
3015 } else if (cmp == 0) {
3017 * We are overwriting one. If it was previously
3018 * deleted, we need to add a backlink.
3020 * Note that if any RMD_FLAGs in an extended new DN
3021 * will be ignored.
3023 uint32_t rmd_flags;
3025 ret = replmd_update_la_val(new_values, old_p->v,
3026 new_p->dsdb_dn,
3027 old_p->dsdb_dn,
3028 invocation_id,
3029 seq_num, seq_num,
3030 now, 0, false);
3031 if (ret != LDB_SUCCESS) {
3032 talloc_free(tmp_ctx);
3033 return ret;
3036 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3037 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3038 ret = replmd_add_backlink(module, replmd_private,
3039 schema, msg_guid,
3040 &new_p->guid, true,
3041 schema_attr, true);
3042 if (ret != LDB_SUCCESS) {
3043 talloc_free(tmp_ctx);
3044 return ret;
3048 new_values[i] = *old_p->v;
3049 old_i++;
3050 new_i++;
3051 } else {
3053 * Replacements that don't match an existing one. We
3054 * just add them to the final list.
3056 ret = replmd_build_la_val(new_values,
3057 new_p->v,
3058 new_p->dsdb_dn,
3059 invocation_id,
3060 seq_num, seq_num,
3061 now, 0, false);
3062 if (ret != LDB_SUCCESS) {
3063 talloc_free(tmp_ctx);
3064 return ret;
3066 ret = replmd_add_backlink(module, replmd_private,
3067 schema, msg_guid,
3068 &new_p->guid, true,
3069 schema_attr, true);
3070 if (ret != LDB_SUCCESS) {
3071 talloc_free(tmp_ctx);
3072 return ret;
3074 new_values[i] = *new_p->v;
3075 new_i++;
3078 if (old_el != NULL) {
3079 talloc_steal(msg->elements, old_el->values);
3081 el->values = talloc_steal(msg->elements, new_values);
3082 el->num_values = i;
3083 talloc_free(tmp_ctx);
3085 el->flags = LDB_FLAG_MOD_REPLACE;
3087 return LDB_SUCCESS;
3092 handle linked attributes in modify requests
3094 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3095 struct replmd_private *replmd_private,
3096 struct ldb_message *msg,
3097 uint64_t seq_num, time_t t,
3098 struct ldb_request *parent)
3100 struct ldb_result *res;
3101 unsigned int i;
3102 int ret;
3103 struct ldb_context *ldb = ldb_module_get_ctx(module);
3104 struct ldb_message *old_msg;
3106 const struct dsdb_schema *schema;
3107 struct GUID old_guid;
3109 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3111 * Nothing special is required for modifying or vanishing links
3112 * in fl2000 since they are just strings in a multi-valued
3113 * attribute.
3115 struct ldb_control *ctrl = ldb_request_get_control(parent,
3116 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3117 if (ctrl) {
3118 ctrl->critical = false;
3120 return LDB_SUCCESS;
3124 * TODO:
3126 * We should restrict this to the intersection of the list of
3127 * linked attributes in the schema and the list of attributes
3128 * being modified.
3130 * This will help performance a little, as otherwise we have
3131 * to allocate the entire object value-by-value.
3133 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3134 DSDB_FLAG_NEXT_MODULE |
3135 DSDB_SEARCH_SHOW_RECYCLED |
3136 DSDB_SEARCH_REVEAL_INTERNALS |
3137 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3138 parent);
3139 if (ret != LDB_SUCCESS) {
3140 return ret;
3142 schema = dsdb_get_schema(ldb, res);
3143 if (!schema) {
3144 return LDB_ERR_OPERATIONS_ERROR;
3147 old_msg = res->msgs[0];
3149 old_guid = samdb_result_guid(old_msg, "objectGUID");
3151 for (i=0; i<msg->num_elements; i++) {
3152 struct ldb_message_element *el = &msg->elements[i];
3153 struct ldb_message_element *old_el, *new_el;
3154 const struct dsdb_attribute *schema_attr
3155 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3156 if (!schema_attr) {
3157 ldb_asprintf_errstring(ldb,
3158 "%s: attribute %s is not a valid attribute in schema",
3159 __FUNCTION__, el->name);
3160 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3162 if (schema_attr->linkID == 0) {
3163 continue;
3165 if ((schema_attr->linkID & 1) == 1) {
3166 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
3167 continue;
3169 /* Odd is for the target. Illegal to modify */
3170 ldb_asprintf_errstring(ldb,
3171 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3172 return LDB_ERR_UNWILLING_TO_PERFORM;
3174 old_el = ldb_msg_find_element(old_msg, el->name);
3175 switch (el->flags & LDB_FLAG_MOD_MASK) {
3176 case LDB_FLAG_MOD_REPLACE:
3177 ret = replmd_modify_la_replace(module, replmd_private,
3178 schema, msg, el, old_el,
3179 schema_attr, seq_num, t,
3180 &old_guid, parent);
3181 break;
3182 case LDB_FLAG_MOD_DELETE:
3183 ret = replmd_modify_la_delete(module, replmd_private,
3184 schema, msg, el, old_el,
3185 schema_attr, seq_num, t,
3186 &old_guid, parent);
3187 break;
3188 case LDB_FLAG_MOD_ADD:
3189 ret = replmd_modify_la_add(module, replmd_private,
3190 schema, msg, el, old_el,
3191 schema_attr, seq_num, t,
3192 &old_guid, parent);
3193 break;
3194 default:
3195 ldb_asprintf_errstring(ldb,
3196 "invalid flags 0x%x for %s linked attribute",
3197 el->flags, el->name);
3198 return LDB_ERR_UNWILLING_TO_PERFORM;
3200 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
3201 ldb_asprintf_errstring(ldb,
3202 "Attribute %s is single valued but more than one value has been supplied",
3203 el->name);
3204 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3205 } else {
3206 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3211 if (ret != LDB_SUCCESS) {
3212 return ret;
3214 if (old_el) {
3215 ldb_msg_remove_attr(old_msg, el->name);
3217 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3218 new_el->num_values = el->num_values;
3219 new_el->values = talloc_steal(msg->elements, el->values);
3221 /* TODO: this relises a bit too heavily on the exact
3222 behaviour of ldb_msg_find_element and
3223 ldb_msg_remove_element */
3224 old_el = ldb_msg_find_element(msg, el->name);
3225 if (old_el != el) {
3226 ldb_msg_remove_element(msg, old_el);
3227 i--;
3231 talloc_free(res);
3232 return ret;
3237 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3239 struct ldb_context *ldb;
3240 struct replmd_replicated_request *ac;
3241 struct ldb_request *down_req;
3242 struct ldb_message *msg;
3243 time_t t = time(NULL);
3244 int ret;
3245 bool is_urgent = false, rodc = false;
3246 bool is_schema_nc = false;
3247 unsigned int functional_level;
3248 const struct ldb_message_element *guid_el = NULL;
3249 struct ldb_control *sd_propagation_control;
3250 struct replmd_private *replmd_private =
3251 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3253 /* do not manipulate our control entries */
3254 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3255 return ldb_next_request(module, req);
3258 sd_propagation_control = ldb_request_get_control(req,
3259 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3260 if (sd_propagation_control != NULL) {
3261 if (req->op.mod.message->num_elements != 1) {
3262 return ldb_module_operr(module);
3264 ret = strcmp(req->op.mod.message->elements[0].name,
3265 "nTSecurityDescriptor");
3266 if (ret != 0) {
3267 return ldb_module_operr(module);
3270 return ldb_next_request(module, req);
3273 ldb = ldb_module_get_ctx(module);
3275 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3277 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3278 if (guid_el != NULL) {
3279 ldb_set_errstring(ldb,
3280 "replmd_modify: it's not allowed to change the objectGUID!");
3281 return LDB_ERR_CONSTRAINT_VIOLATION;
3284 ac = replmd_ctx_init(module, req);
3285 if (ac == NULL) {
3286 return ldb_module_oom(module);
3289 functional_level = dsdb_functional_level(ldb);
3291 /* we have to copy the message as the caller might have it as a const */
3292 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3293 if (msg == NULL) {
3294 ldb_oom(ldb);
3295 talloc_free(ac);
3296 return LDB_ERR_OPERATIONS_ERROR;
3299 ldb_msg_remove_attr(msg, "whenChanged");
3300 ldb_msg_remove_attr(msg, "uSNChanged");
3302 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3304 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3305 msg, &ac->seq_num, t, is_schema_nc,
3306 &is_urgent, &rodc);
3307 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3308 struct loadparm_context *lp_ctx;
3309 char *referral;
3311 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3312 struct loadparm_context);
3314 referral = talloc_asprintf(req,
3315 "ldap://%s/%s",
3316 lpcfg_dnsdomain(lp_ctx),
3317 ldb_dn_get_linearized(msg->dn));
3318 ret = ldb_module_send_referral(req, referral);
3319 talloc_free(ac);
3320 return ret;
3323 if (ret != LDB_SUCCESS) {
3324 talloc_free(ac);
3325 return ret;
3328 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3329 msg, ac->seq_num, t, req);
3330 if (ret != LDB_SUCCESS) {
3331 talloc_free(ac);
3332 return ret;
3335 /* TODO:
3336 * - replace the old object with the newly constructed one
3339 ac->is_urgent = is_urgent;
3341 ret = ldb_build_mod_req(&down_req, ldb, ac,
3342 msg,
3343 req->controls,
3344 ac, replmd_op_callback,
3345 req);
3346 LDB_REQ_SET_LOCATION(down_req);
3347 if (ret != LDB_SUCCESS) {
3348 talloc_free(ac);
3349 return ret;
3352 /* current partition control is needed by "replmd_op_callback" */
3353 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3354 ret = ldb_request_add_control(down_req,
3355 DSDB_CONTROL_CURRENT_PARTITION_OID,
3356 false, NULL);
3357 if (ret != LDB_SUCCESS) {
3358 talloc_free(ac);
3359 return ret;
3363 /* If we are in functional level 2000, then
3364 * replmd_modify_handle_linked_attribs will have done
3365 * nothing */
3366 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3367 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3368 if (ret != LDB_SUCCESS) {
3369 talloc_free(ac);
3370 return ret;
3374 talloc_steal(down_req, msg);
3376 /* we only change whenChanged and uSNChanged if the seq_num
3377 has changed */
3378 if (ac->seq_num != 0) {
3379 ret = add_time_element(msg, "whenChanged", t);
3380 if (ret != LDB_SUCCESS) {
3381 talloc_free(ac);
3382 ldb_operr(ldb);
3383 return ret;
3386 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3387 if (ret != LDB_SUCCESS) {
3388 talloc_free(ac);
3389 ldb_operr(ldb);
3390 return ret;
3394 /* go on with the call chain */
3395 return ldb_next_request(module, down_req);
3398 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3401 handle a rename request
3403 On a rename we need to do an extra ldb_modify which sets the
3404 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3406 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3408 struct ldb_context *ldb;
3409 struct replmd_replicated_request *ac;
3410 int ret;
3411 struct ldb_request *down_req;
3413 /* do not manipulate our control entries */
3414 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3415 return ldb_next_request(module, req);
3418 ldb = ldb_module_get_ctx(module);
3420 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3422 ac = replmd_ctx_init(module, req);
3423 if (ac == NULL) {
3424 return ldb_module_oom(module);
3427 ret = ldb_build_rename_req(&down_req, ldb, ac,
3428 ac->req->op.rename.olddn,
3429 ac->req->op.rename.newdn,
3430 ac->req->controls,
3431 ac, replmd_rename_callback,
3432 ac->req);
3433 LDB_REQ_SET_LOCATION(down_req);
3434 if (ret != LDB_SUCCESS) {
3435 talloc_free(ac);
3436 return ret;
3439 /* go on with the call chain */
3440 return ldb_next_request(module, down_req);
3443 /* After the rename is compleated, update the whenchanged etc */
3444 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3446 struct ldb_context *ldb;
3447 struct ldb_request *down_req;
3448 struct ldb_message *msg;
3449 const struct dsdb_attribute *rdn_attr;
3450 const char *rdn_name;
3451 const struct ldb_val *rdn_val;
3452 const char *attrs[5] = { NULL, };
3453 time_t t = time(NULL);
3454 int ret;
3455 bool is_urgent = false, rodc = false;
3456 bool is_schema_nc;
3457 struct replmd_replicated_request *ac =
3458 talloc_get_type(req->context, struct replmd_replicated_request);
3459 struct replmd_private *replmd_private =
3460 talloc_get_type(ldb_module_get_private(ac->module),
3461 struct replmd_private);
3463 ldb = ldb_module_get_ctx(ac->module);
3465 if (ares->error != LDB_SUCCESS) {
3466 return ldb_module_done(ac->req, ares->controls,
3467 ares->response, ares->error);
3470 if (ares->type != LDB_REPLY_DONE) {
3471 ldb_set_errstring(ldb,
3472 "invalid ldb_reply_type in callback");
3473 talloc_free(ares);
3474 return ldb_module_done(ac->req, NULL, NULL,
3475 LDB_ERR_OPERATIONS_ERROR);
3478 /* TODO:
3479 * - replace the old object with the newly constructed one
3482 msg = ldb_msg_new(ac);
3483 if (msg == NULL) {
3484 ldb_oom(ldb);
3485 return LDB_ERR_OPERATIONS_ERROR;
3488 msg->dn = ac->req->op.rename.newdn;
3490 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3492 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3493 if (rdn_name == NULL) {
3494 talloc_free(ares);
3495 return ldb_module_done(ac->req, NULL, NULL,
3496 ldb_operr(ldb));
3499 /* normalize the rdn attribute name */
3500 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3501 if (rdn_attr == NULL) {
3502 talloc_free(ares);
3503 return ldb_module_done(ac->req, NULL, NULL,
3504 ldb_operr(ldb));
3506 rdn_name = rdn_attr->lDAPDisplayName;
3508 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3509 if (rdn_val == NULL) {
3510 talloc_free(ares);
3511 return ldb_module_done(ac->req, NULL, NULL,
3512 ldb_operr(ldb));
3515 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3516 talloc_free(ares);
3517 return ldb_module_done(ac->req, NULL, NULL,
3518 ldb_oom(ldb));
3520 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3521 talloc_free(ares);
3522 return ldb_module_done(ac->req, NULL, NULL,
3523 ldb_oom(ldb));
3525 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3526 talloc_free(ares);
3527 return ldb_module_done(ac->req, NULL, NULL,
3528 ldb_oom(ldb));
3530 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3531 talloc_free(ares);
3532 return ldb_module_done(ac->req, NULL, NULL,
3533 ldb_oom(ldb));
3537 * here we let replmd_update_rpmd() only search for
3538 * the existing "replPropertyMetaData" and rdn_name attributes.
3540 * We do not want the existing "name" attribute as
3541 * the "name" attribute needs to get the version
3542 * updated on rename even if the rdn value hasn't changed.
3544 * This is the diff of the meta data, for a moved user
3545 * on a w2k8r2 server:
3547 * # record 1
3548 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3549 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3550 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3551 * version : 0x00000001 (1)
3552 * reserved : 0x00000000 (0)
3553 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3554 * local_usn : 0x00000000000037a5 (14245)
3555 * array: struct replPropertyMetaData1
3556 * attid : DRSUAPI_ATTID_name (0x90001)
3557 * - version : 0x00000001 (1)
3558 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3559 * + version : 0x00000002 (2)
3560 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3561 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3562 * - originating_usn : 0x00000000000037a5 (14245)
3563 * - local_usn : 0x00000000000037a5 (14245)
3564 * + originating_usn : 0x0000000000003834 (14388)
3565 * + local_usn : 0x0000000000003834 (14388)
3566 * array: struct replPropertyMetaData1
3567 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3568 * version : 0x00000004 (4)
3570 attrs[0] = "replPropertyMetaData";
3571 attrs[1] = "objectClass";
3572 attrs[2] = "instanceType";
3573 attrs[3] = rdn_name;
3574 attrs[4] = NULL;
3576 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3577 msg, &ac->seq_num, t,
3578 is_schema_nc, &is_urgent, &rodc);
3579 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3580 struct ldb_dn *olddn = ac->req->op.rename.olddn;
3581 struct loadparm_context *lp_ctx;
3582 char *referral;
3584 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3585 struct loadparm_context);
3587 referral = talloc_asprintf(req,
3588 "ldap://%s/%s",
3589 lpcfg_dnsdomain(lp_ctx),
3590 ldb_dn_get_linearized(olddn));
3591 ret = ldb_module_send_referral(req, referral);
3592 talloc_free(ares);
3593 return ldb_module_done(req, NULL, NULL, ret);
3596 if (ret != LDB_SUCCESS) {
3597 talloc_free(ares);
3598 return ldb_module_done(ac->req, NULL, NULL, ret);
3601 if (ac->seq_num == 0) {
3602 talloc_free(ares);
3603 return ldb_module_done(ac->req, NULL, NULL,
3604 ldb_error(ldb, ret,
3605 "internal error seq_num == 0"));
3607 ac->is_urgent = is_urgent;
3609 ret = ldb_build_mod_req(&down_req, ldb, ac,
3610 msg,
3611 req->controls,
3612 ac, replmd_op_callback,
3613 req);
3614 LDB_REQ_SET_LOCATION(down_req);
3615 if (ret != LDB_SUCCESS) {
3616 talloc_free(ac);
3617 return ret;
3620 /* current partition control is needed by "replmd_op_callback" */
3621 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3622 ret = ldb_request_add_control(down_req,
3623 DSDB_CONTROL_CURRENT_PARTITION_OID,
3624 false, NULL);
3625 if (ret != LDB_SUCCESS) {
3626 talloc_free(ac);
3627 return ret;
3631 talloc_steal(down_req, msg);
3633 ret = add_time_element(msg, "whenChanged", t);
3634 if (ret != LDB_SUCCESS) {
3635 talloc_free(ac);
3636 ldb_operr(ldb);
3637 return ret;
3640 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3641 if (ret != LDB_SUCCESS) {
3642 talloc_free(ac);
3643 ldb_operr(ldb);
3644 return ret;
3647 /* go on with the call chain - do the modify after the rename */
3648 return ldb_next_request(ac->module, down_req);
3652 * remove links from objects that point at this object when an object
3653 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3654 * RemoveObj which states that link removal due to the object being
3655 * deleted is NOT an originating update - they just go away!
3658 static int replmd_delete_remove_link(struct ldb_module *module,
3659 const struct dsdb_schema *schema,
3660 struct replmd_private *replmd_private,
3661 struct ldb_dn *dn,
3662 struct GUID *guid,
3663 struct ldb_message_element *el,
3664 const struct dsdb_attribute *sa,
3665 struct ldb_request *parent)
3667 unsigned int i;
3668 TALLOC_CTX *tmp_ctx = talloc_new(module);
3669 struct ldb_context *ldb = ldb_module_get_ctx(module);
3671 for (i=0; i<el->num_values; i++) {
3672 struct dsdb_dn *dsdb_dn;
3673 int ret;
3674 struct ldb_message *msg;
3675 const struct dsdb_attribute *target_attr;
3676 struct ldb_message_element *el2;
3677 const char *dn_str;
3678 struct ldb_val dn_val;
3679 uint32_t dsdb_flags = 0;
3680 const char *attrs[] = { NULL, NULL };
3681 struct ldb_result *link_res;
3682 struct ldb_message *link_msg;
3683 struct ldb_message_element *link_el;
3684 struct parsed_dn *link_dns;
3685 struct parsed_dn *p = NULL, *unused = NULL;
3687 if (dsdb_dn_is_deleted_val(&el->values[i])) {
3688 continue;
3691 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
3692 if (!dsdb_dn) {
3693 talloc_free(tmp_ctx);
3694 return LDB_ERR_OPERATIONS_ERROR;
3697 /* remove the link */
3698 msg = ldb_msg_new(tmp_ctx);
3699 if (!msg) {
3700 ldb_module_oom(module);
3701 talloc_free(tmp_ctx);
3702 return LDB_ERR_OPERATIONS_ERROR;
3706 msg->dn = dsdb_dn->dn;
3708 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
3709 if (target_attr == NULL) {
3710 continue;
3712 attrs[0] = target_attr->lDAPDisplayName;
3714 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
3715 LDB_FLAG_MOD_DELETE, &el2);
3716 if (ret != LDB_SUCCESS) {
3717 ldb_module_oom(module);
3718 talloc_free(tmp_ctx);
3719 return LDB_ERR_OPERATIONS_ERROR;
3722 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
3723 msg->dn, attrs,
3724 DSDB_FLAG_NEXT_MODULE |
3725 DSDB_SEARCH_SHOW_EXTENDED_DN,
3726 parent);
3728 if (ret != LDB_SUCCESS) {
3729 talloc_free(tmp_ctx);
3730 return ret;
3733 link_msg = link_res->msgs[0];
3734 link_el = ldb_msg_find_element(link_msg,
3735 target_attr->lDAPDisplayName);
3736 if (link_el == NULL) {
3737 talloc_free(tmp_ctx);
3738 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3742 * This call 'upgrades' the links in link_dns, but we
3743 * do not commit the result back into the database, so
3744 * this is safe to call in FL2000 or on databases that
3745 * have been run at that level in the past.
3747 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx,
3748 link_el, &link_dns,
3749 target_attr->syntax->ldap_oid, parent);
3750 if (ret != LDB_SUCCESS) {
3751 talloc_free(tmp_ctx);
3752 return ret;
3755 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
3756 guid, dn, &p, &unused,
3757 target_attr->syntax->ldap_oid);
3758 if (ret != LDB_SUCCESS) {
3759 talloc_free(tmp_ctx);
3760 return ret;
3763 if (p == NULL) {
3764 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3765 "Failed to find forward link on %s "
3766 "as %s to remove backlink %s on %s",
3767 ldb_dn_get_linearized(msg->dn),
3768 target_attr->lDAPDisplayName,
3769 sa->lDAPDisplayName,
3770 ldb_dn_get_linearized(dn));
3771 talloc_free(tmp_ctx);
3772 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3776 /* This needs to get the Binary DN, by first searching */
3777 dn_str = dsdb_dn_get_linearized(tmp_ctx,
3778 p->dsdb_dn);
3779 dn_val = data_blob_string_const(dn_str);
3780 el2->values = &dn_val;
3781 el2->num_values = 1;
3784 * Ensure that we tell the modification to vanish any linked
3785 * attributes (not simply mark them as isDeleted = TRUE)
3787 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
3789 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
3790 if (ret != LDB_SUCCESS) {
3791 talloc_free(tmp_ctx);
3792 return ret;
3795 talloc_free(tmp_ctx);
3796 return LDB_SUCCESS;
3801 handle update of replication meta data for deletion of objects
3803 This also handles the mapping of delete to a rename operation
3804 to allow deletes to be replicated.
3806 It also handles the incoming deleted objects, to ensure they are
3807 fully deleted here. In that case re_delete is true, and we do not
3808 use this as a signal to change the deleted state, just reinforce it.
3811 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
3813 int ret = LDB_ERR_OTHER;
3814 bool retb, disallow_move_on_delete;
3815 struct ldb_dn *old_dn, *new_dn;
3816 const char *rdn_name;
3817 const struct ldb_val *rdn_value, *new_rdn_value;
3818 struct GUID guid;
3819 struct ldb_context *ldb = ldb_module_get_ctx(module);
3820 const struct dsdb_schema *schema;
3821 struct ldb_message *msg, *old_msg;
3822 struct ldb_message_element *el;
3823 TALLOC_CTX *tmp_ctx;
3824 struct ldb_result *res, *parent_res;
3825 static const char * const preserved_attrs[] = {
3826 /* yes, this really is a hard coded list. See MS-ADTS
3827 section 3.1.1.5.5.1.1 */
3828 "attributeID",
3829 "attributeSyntax",
3830 "dNReferenceUpdate",
3831 "dNSHostName",
3832 "flatName",
3833 "governsID",
3834 "groupType",
3835 "instanceType",
3836 "lDAPDisplayName",
3837 "legacyExchangeDN",
3838 "isDeleted",
3839 "isRecycled",
3840 "lastKnownParent",
3841 "msDS-LastKnownRDN",
3842 "msDS-PortLDAP",
3843 "mS-DS-CreatorSID",
3844 "mSMQOwnerID",
3845 "nCName",
3846 "objectClass",
3847 "distinguishedName",
3848 "objectGUID",
3849 "objectSid",
3850 "oMSyntax",
3851 "proxiedObjectName",
3852 "name",
3853 "nTSecurityDescriptor",
3854 "replPropertyMetaData",
3855 "sAMAccountName",
3856 "securityIdentifier",
3857 "sIDHistory",
3858 "subClassOf",
3859 "systemFlags",
3860 "trustPartner",
3861 "trustDirection",
3862 "trustType",
3863 "trustAttributes",
3864 "userAccountControl",
3865 "uSNChanged",
3866 "uSNCreated",
3867 "whenCreated",
3868 "whenChanged",
3869 NULL
3871 static const char * const all_attrs[] = {
3872 DSDB_SECRET_ATTRIBUTES,
3873 "*",
3874 NULL
3876 unsigned int i, el_count = 0;
3877 uint32_t dsdb_flags = 0;
3878 struct replmd_private *replmd_private;
3879 enum deletion_state deletion_state, next_deletion_state;
3881 if (ldb_dn_is_special(req->op.del.dn)) {
3882 return ldb_next_request(module, req);
3886 * We have to allow dbcheck to remove an object that
3887 * is beyond repair, and to do so totally. This could
3888 * mean we we can get a partial object from the other
3889 * DC, causing havoc, so dbcheck suggests
3890 * re-replication first. dbcheck sets both DBCHECK
3891 * and RELAX in this situation.
3893 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
3894 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
3895 /* really, really remove it */
3896 return ldb_next_request(module, req);
3899 tmp_ctx = talloc_new(ldb);
3900 if (!tmp_ctx) {
3901 ldb_oom(ldb);
3902 return LDB_ERR_OPERATIONS_ERROR;
3905 schema = dsdb_get_schema(ldb, tmp_ctx);
3906 if (!schema) {
3907 talloc_free(tmp_ctx);
3908 return LDB_ERR_OPERATIONS_ERROR;
3911 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
3913 /* we need the complete msg off disk, so we can work out which
3914 attributes need to be removed */
3915 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
3916 DSDB_FLAG_NEXT_MODULE |
3917 DSDB_SEARCH_SHOW_RECYCLED |
3918 DSDB_SEARCH_REVEAL_INTERNALS |
3919 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
3920 if (ret != LDB_SUCCESS) {
3921 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3922 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
3923 re_delete ? "re-delete" : "delete",
3924 ldb_dn_get_linearized(old_dn),
3925 ldb_errstring(ldb_module_get_ctx(module)));
3926 talloc_free(tmp_ctx);
3927 return ret;
3929 old_msg = res->msgs[0];
3931 replmd_deletion_state(module, old_msg,
3932 &deletion_state,
3933 &next_deletion_state);
3935 /* This supports us noticing an incoming isDeleted and acting on it */
3936 if (re_delete) {
3937 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
3938 next_deletion_state = deletion_state;
3941 if (next_deletion_state == OBJECT_REMOVED) {
3943 * We have to prevent objects being deleted, even if
3944 * the administrator really wants them gone, as
3945 * without the tombstone, we can get a partial object
3946 * from the other DC, causing havoc.
3948 * The only other valid case is when the 180 day
3949 * timeout has expired, when relax is specified.
3951 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
3952 /* it is already deleted - really remove it this time */
3953 talloc_free(tmp_ctx);
3954 return ldb_next_request(module, req);
3957 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
3958 "This check is to prevent corruption of the replicated state.",
3959 ldb_dn_get_linearized(old_msg->dn));
3960 return LDB_ERR_UNWILLING_TO_PERFORM;
3963 rdn_name = ldb_dn_get_rdn_name(old_dn);
3964 rdn_value = ldb_dn_get_rdn_val(old_dn);
3965 if ((rdn_name == NULL) || (rdn_value == NULL)) {
3966 talloc_free(tmp_ctx);
3967 return ldb_operr(ldb);
3970 msg = ldb_msg_new(tmp_ctx);
3971 if (msg == NULL) {
3972 ldb_module_oom(module);
3973 talloc_free(tmp_ctx);
3974 return LDB_ERR_OPERATIONS_ERROR;
3977 msg->dn = old_dn;
3979 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
3980 disallow_move_on_delete =
3981 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
3982 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
3984 /* work out where we will be renaming this object to */
3985 if (!disallow_move_on_delete) {
3986 struct ldb_dn *deleted_objects_dn;
3987 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
3988 &deleted_objects_dn);
3991 * We should not move objects if we can't find the
3992 * deleted objects DN. Not moving (or otherwise
3993 * harming) the Deleted Objects DN itself is handled
3994 * in the caller.
3996 if (re_delete && (ret != LDB_SUCCESS)) {
3997 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3998 if (new_dn == NULL) {
3999 ldb_module_oom(module);
4000 talloc_free(tmp_ctx);
4001 return LDB_ERR_OPERATIONS_ERROR;
4003 } else if (ret != LDB_SUCCESS) {
4004 /* this is probably an attempted delete on a partition
4005 * that doesn't allow delete operations, such as the
4006 * schema partition */
4007 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4008 ldb_dn_get_linearized(old_dn));
4009 talloc_free(tmp_ctx);
4010 return LDB_ERR_UNWILLING_TO_PERFORM;
4011 } else {
4012 new_dn = deleted_objects_dn;
4014 } else {
4015 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4016 if (new_dn == NULL) {
4017 ldb_module_oom(module);
4018 talloc_free(tmp_ctx);
4019 return LDB_ERR_OPERATIONS_ERROR;
4023 /* get the objects GUID from the search we just did */
4024 guid = samdb_result_guid(old_msg, "objectGUID");
4026 if (deletion_state == OBJECT_NOT_DELETED) {
4027 /* Add a formatted child */
4028 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
4029 rdn_name,
4030 ldb_dn_escape_value(tmp_ctx, *rdn_value),
4031 GUID_string(tmp_ctx, &guid));
4032 if (!retb) {
4033 ldb_asprintf_errstring(ldb, __location__
4034 ": Unable to add a formatted child to dn: %s",
4035 ldb_dn_get_linearized(new_dn));
4036 talloc_free(tmp_ctx);
4037 return LDB_ERR_OPERATIONS_ERROR;
4040 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
4041 if (ret != LDB_SUCCESS) {
4042 ldb_asprintf_errstring(ldb, __location__
4043 ": Failed to add isDeleted string to the msg");
4044 talloc_free(tmp_ctx);
4045 return ret;
4047 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4048 } else {
4050 * No matter what has happened with other renames etc, try again to
4051 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4054 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4055 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4056 if (!retb) {
4057 ldb_asprintf_errstring(ldb, __location__
4058 ": Unable to add a prepare rdn of %s",
4059 ldb_dn_get_linearized(rdn));
4060 talloc_free(tmp_ctx);
4061 return LDB_ERR_OPERATIONS_ERROR;
4063 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4065 retb = ldb_dn_add_child(new_dn, rdn);
4066 if (!retb) {
4067 ldb_asprintf_errstring(ldb, __location__
4068 ": Unable to add rdn %s to base dn: %s",
4069 ldb_dn_get_linearized(rdn),
4070 ldb_dn_get_linearized(new_dn));
4071 talloc_free(tmp_ctx);
4072 return LDB_ERR_OPERATIONS_ERROR;
4077 now we need to modify the object in the following ways:
4079 - add isDeleted=TRUE
4080 - update rDN and name, with new rDN
4081 - remove linked attributes
4082 - remove objectCategory and sAMAccountType
4083 - remove attribs not on the preserved list
4084 - preserved if in above list, or is rDN
4085 - remove all linked attribs from this object
4086 - remove all links from other objects to this object
4087 - add lastKnownParent
4088 - update replPropertyMetaData?
4090 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4093 if (deletion_state == OBJECT_NOT_DELETED) {
4094 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4095 char *parent_dn_str = NULL;
4097 /* we need the storage form of the parent GUID */
4098 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4099 parent_dn, NULL,
4100 DSDB_FLAG_NEXT_MODULE |
4101 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4102 DSDB_SEARCH_REVEAL_INTERNALS|
4103 DSDB_SEARCH_SHOW_RECYCLED, req);
4104 if (ret != LDB_SUCCESS) {
4105 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4106 "repmd_delete: Failed to %s %s, "
4107 "because we failed to find it's parent (%s): %s",
4108 re_delete ? "re-delete" : "delete",
4109 ldb_dn_get_linearized(old_dn),
4110 ldb_dn_get_linearized(parent_dn),
4111 ldb_errstring(ldb_module_get_ctx(module)));
4112 talloc_free(tmp_ctx);
4113 return ret;
4117 * Now we can use the DB version,
4118 * it will have the extended DN info in it
4120 parent_dn = parent_res->msgs[0]->dn;
4121 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4122 parent_dn,
4124 if (parent_dn_str == NULL) {
4125 talloc_free(tmp_ctx);
4126 return ldb_module_oom(module);
4129 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4130 parent_dn_str);
4131 if (ret != LDB_SUCCESS) {
4132 ldb_asprintf_errstring(ldb, __location__
4133 ": Failed to add lastKnownParent "
4134 "string when deleting %s",
4135 ldb_dn_get_linearized(old_dn));
4136 talloc_free(tmp_ctx);
4137 return ret;
4139 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4141 if (next_deletion_state == OBJECT_DELETED) {
4142 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4143 if (ret != LDB_SUCCESS) {
4144 ldb_asprintf_errstring(ldb, __location__
4145 ": Failed to add msDS-LastKnownRDN "
4146 "string when deleting %s",
4147 ldb_dn_get_linearized(old_dn));
4148 talloc_free(tmp_ctx);
4149 return ret;
4151 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
4155 switch (next_deletion_state) {
4157 case OBJECT_RECYCLED:
4158 case OBJECT_TOMBSTONE:
4161 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4162 * describes what must be removed from a tombstone
4163 * object
4165 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4166 * describes what must be removed from a recycled
4167 * object
4172 * we also mark it as recycled, meaning this object can't be
4173 * recovered (we are stripping its attributes).
4174 * This is done only if we have this schema object of course ...
4175 * This behavior is identical to the one of Windows 2008R2 which
4176 * always set the isRecycled attribute, even if the recycle-bin is
4177 * not activated and what ever the forest level is.
4179 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4180 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
4181 if (ret != LDB_SUCCESS) {
4182 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4183 ldb_module_oom(module);
4184 talloc_free(tmp_ctx);
4185 return ret;
4187 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4190 replmd_private = talloc_get_type(ldb_module_get_private(module),
4191 struct replmd_private);
4192 /* work out which of the old attributes we will be removing */
4193 for (i=0; i<old_msg->num_elements; i++) {
4194 const struct dsdb_attribute *sa;
4195 el = &old_msg->elements[i];
4196 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4197 if (!sa) {
4198 talloc_free(tmp_ctx);
4199 return LDB_ERR_OPERATIONS_ERROR;
4201 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4202 /* don't remove the rDN */
4203 continue;
4205 if (sa->linkID & 1) {
4207 we have a backlink in this object
4208 that needs to be removed. We're not
4209 allowed to remove it directly
4210 however, so we instead setup a
4211 modify to delete the corresponding
4212 forward link
4214 ret = replmd_delete_remove_link(module, schema,
4215 replmd_private,
4216 old_dn, &guid,
4217 el, sa, req);
4218 if (ret != LDB_SUCCESS) {
4219 const char *old_dn_str
4220 = ldb_dn_get_linearized(old_dn);
4221 ldb_asprintf_errstring(ldb,
4222 __location__
4223 ": Failed to remove backlink of "
4224 "%s when deleting %s: %s",
4225 el->name,
4226 old_dn_str,
4227 ldb_errstring(ldb));
4228 talloc_free(tmp_ctx);
4229 return LDB_ERR_OPERATIONS_ERROR;
4231 /* now we continue, which means we
4232 won't remove this backlink
4233 directly
4235 continue;
4236 } else if (sa->linkID == 0) {
4237 if (ldb_attr_in_list(preserved_attrs, el->name)) {
4238 continue;
4240 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4241 continue;
4243 } else {
4245 * Ensure that we tell the modification to vanish any linked
4246 * attributes (not simply mark them as isDeleted = TRUE)
4248 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4250 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4251 if (ret != LDB_SUCCESS) {
4252 talloc_free(tmp_ctx);
4253 ldb_module_oom(module);
4254 return ret;
4258 break;
4260 case OBJECT_DELETED:
4262 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4263 * describes what must be removed from a deleted
4264 * object
4267 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4268 if (ret != LDB_SUCCESS) {
4269 talloc_free(tmp_ctx);
4270 ldb_module_oom(module);
4271 return ret;
4274 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4275 if (ret != LDB_SUCCESS) {
4276 talloc_free(tmp_ctx);
4277 ldb_module_oom(module);
4278 return ret;
4281 break;
4283 default:
4284 break;
4287 if (deletion_state == OBJECT_NOT_DELETED) {
4288 const struct dsdb_attribute *sa;
4290 /* work out what the new rdn value is, for updating the
4291 rDN and name fields */
4292 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4293 if (new_rdn_value == NULL) {
4294 talloc_free(tmp_ctx);
4295 return ldb_operr(ldb);
4298 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4299 if (!sa) {
4300 talloc_free(tmp_ctx);
4301 return LDB_ERR_OPERATIONS_ERROR;
4304 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4305 &el);
4306 if (ret != LDB_SUCCESS) {
4307 talloc_free(tmp_ctx);
4308 return ret;
4310 el->flags = LDB_FLAG_MOD_REPLACE;
4312 el = ldb_msg_find_element(old_msg, "name");
4313 if (el) {
4314 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4315 if (ret != LDB_SUCCESS) {
4316 talloc_free(tmp_ctx);
4317 return ret;
4319 el->flags = LDB_FLAG_MOD_REPLACE;
4324 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4329 * No matter what has happned with other renames, try again to
4330 * get this to be under the deleted DN.
4332 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4333 /* now rename onto the new DN */
4334 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4335 if (ret != LDB_SUCCESS){
4336 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4337 ldb_dn_get_linearized(old_dn),
4338 ldb_dn_get_linearized(new_dn),
4339 ldb_errstring(ldb)));
4340 talloc_free(tmp_ctx);
4341 return ret;
4343 msg->dn = new_dn;
4346 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4347 if (ret != LDB_SUCCESS) {
4348 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
4349 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
4350 talloc_free(tmp_ctx);
4351 return ret;
4354 talloc_free(tmp_ctx);
4356 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4359 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4361 return replmd_delete_internals(module, req, false);
4365 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4367 return ret;
4370 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4372 int ret = LDB_ERR_OTHER;
4373 /* TODO: do some error mapping */
4375 /* Let the caller know the full WERROR */
4376 ar->objs->error = status;
4378 return ret;
4382 static struct replPropertyMetaData1 *
4383 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4384 enum drsuapi_DsAttributeId attid)
4386 uint32_t i;
4387 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4389 for (i = 0; i < rpmd_ctr->count; i++) {
4390 if (rpmd_ctr->array[i].attid == attid) {
4391 return &rpmd_ctr->array[i];
4394 return NULL;
4399 return true if an update is newer than an existing entry
4400 see section 5.11 of MS-ADTS
4402 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4403 const struct GUID *update_invocation_id,
4404 uint32_t current_version,
4405 uint32_t update_version,
4406 NTTIME current_change_time,
4407 NTTIME update_change_time)
4409 if (update_version != current_version) {
4410 return update_version > current_version;
4412 if (update_change_time != current_change_time) {
4413 return update_change_time > current_change_time;
4415 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4418 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4419 struct replPropertyMetaData1 *new_m)
4421 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4422 &new_m->originating_invocation_id,
4423 cur_m->version,
4424 new_m->version,
4425 cur_m->originating_change_time,
4426 new_m->originating_change_time);
4429 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4430 struct replPropertyMetaData1 *cur_m,
4431 struct replPropertyMetaData1 *new_m)
4433 bool cmp;
4436 * If the new replPropertyMetaData entry for this attribute is
4437 * not provided (this happens in the case where we look for
4438 * ATTID_name, but the name was not changed), then the local
4439 * state is clearly still current, as the remote
4440 * server didn't send it due to being older the high watermark
4441 * USN we sent.
4443 if (new_m == NULL) {
4444 return false;
4447 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4449 * if we compare equal then do an
4450 * update. This is used when a client
4451 * asks for a FULL_SYNC, and can be
4452 * used to recover a corrupt
4453 * replica.
4455 * This call is a bit tricky, what we
4456 * are doing it turning the 'is_newer'
4457 * call into a 'not is older' by
4458 * swapping cur_m and new_m, and negating the
4459 * outcome.
4461 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4462 cur_m);
4463 } else {
4464 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4465 new_m);
4467 return cmp;
4472 form a conflict DN
4474 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
4476 const struct ldb_val *rdn_val;
4477 const char *rdn_name;
4478 struct ldb_dn *new_dn;
4480 rdn_val = ldb_dn_get_rdn_val(dn);
4481 rdn_name = ldb_dn_get_rdn_name(dn);
4482 if (!rdn_val || !rdn_name) {
4483 return NULL;
4486 new_dn = ldb_dn_copy(mem_ctx, dn);
4487 if (!new_dn) {
4488 return NULL;
4491 if (!ldb_dn_remove_child_components(new_dn, 1)) {
4492 return NULL;
4495 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
4496 rdn_name,
4497 ldb_dn_escape_value(new_dn, *rdn_val),
4498 GUID_string(new_dn, guid))) {
4499 return NULL;
4502 return new_dn;
4507 perform a modify operation which sets the rDN and name attributes to
4508 their current values. This has the effect of changing these
4509 attributes to have been last updated by the current DC. This is
4510 needed to ensure that renames performed as part of conflict
4511 resolution are propogated to other DCs
4513 static int replmd_name_modify(struct replmd_replicated_request *ar,
4514 struct ldb_request *req, struct ldb_dn *dn)
4516 struct ldb_message *msg;
4517 const char *rdn_name;
4518 const struct ldb_val *rdn_val;
4519 const struct dsdb_attribute *rdn_attr;
4520 int ret;
4522 msg = ldb_msg_new(req);
4523 if (msg == NULL) {
4524 goto failed;
4526 msg->dn = dn;
4528 rdn_name = ldb_dn_get_rdn_name(dn);
4529 if (rdn_name == NULL) {
4530 goto failed;
4533 /* normalize the rdn attribute name */
4534 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
4535 if (rdn_attr == NULL) {
4536 goto failed;
4538 rdn_name = rdn_attr->lDAPDisplayName;
4540 rdn_val = ldb_dn_get_rdn_val(dn);
4541 if (rdn_val == NULL) {
4542 goto failed;
4545 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4546 goto failed;
4548 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
4549 goto failed;
4551 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4552 goto failed;
4554 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
4555 goto failed;
4558 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
4559 if (ret != LDB_SUCCESS) {
4560 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
4561 ldb_dn_get_linearized(dn),
4562 ldb_errstring(ldb_module_get_ctx(ar->module))));
4563 return ret;
4566 talloc_free(msg);
4568 return LDB_SUCCESS;
4570 failed:
4571 talloc_free(msg);
4572 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
4573 ldb_dn_get_linearized(dn)));
4574 return LDB_ERR_OPERATIONS_ERROR;
4579 callback for conflict DN handling where we have renamed the incoming
4580 record. After renaming it, we need to ensure the change of name and
4581 rDN for the incoming record is seen as an originating update by this DC.
4583 This also handles updating lastKnownParent for entries sent to lostAndFound
4585 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
4587 struct replmd_replicated_request *ar =
4588 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4589 struct ldb_dn *conflict_dn = NULL;
4590 int ret;
4592 if (ares->error != LDB_SUCCESS) {
4593 /* call the normal callback for everything except success */
4594 return replmd_op_callback(req, ares);
4597 switch (req->operation) {
4598 case LDB_ADD:
4599 conflict_dn = req->op.add.message->dn;
4600 break;
4601 case LDB_MODIFY:
4602 conflict_dn = req->op.mod.message->dn;
4603 break;
4604 default:
4605 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
4608 /* perform a modify of the rDN and name of the record */
4609 ret = replmd_name_modify(ar, req, conflict_dn);
4610 if (ret != LDB_SUCCESS) {
4611 ares->error = ret;
4612 return replmd_op_callback(req, ares);
4615 if (ar->objs->objects[ar->index_current].last_known_parent) {
4616 struct ldb_message *msg = ldb_msg_new(req);
4617 if (msg == NULL) {
4618 ldb_module_oom(ar->module);
4619 return LDB_ERR_OPERATIONS_ERROR;
4622 msg->dn = req->op.add.message->dn;
4624 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4625 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
4626 if (ret != LDB_SUCCESS) {
4627 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
4628 ldb_module_oom(ar->module);
4629 return ret;
4631 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
4633 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
4634 if (ret != LDB_SUCCESS) {
4635 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
4636 ldb_dn_get_linearized(msg->dn),
4637 ldb_errstring(ldb_module_get_ctx(ar->module))));
4638 return ret;
4640 TALLOC_FREE(msg);
4643 return replmd_op_callback(req, ares);
4647 callback for replmd_replicated_apply_add()
4648 This copes with the creation of conflict records in the case where
4649 the DN exists, but with a different objectGUID
4651 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))
4653 struct ldb_dn *conflict_dn;
4654 struct replmd_replicated_request *ar =
4655 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4656 struct ldb_result *res;
4657 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4658 int ret;
4659 const struct ldb_val *omd_value;
4660 struct replPropertyMetaDataBlob omd, *rmd;
4661 enum ndr_err_code ndr_err;
4662 bool rename_incoming_record, rodc;
4663 struct replPropertyMetaData1 *rmd_name, *omd_name;
4664 struct ldb_message *msg;
4665 struct ldb_request *down_req = NULL;
4667 /* call the normal callback for success */
4668 if (ares->error == LDB_SUCCESS) {
4669 return callback(req, ares);
4673 * we have a conflict, and need to decide if we will keep the
4674 * new record or the old record
4677 msg = ar->objs->objects[ar->index_current].msg;
4678 conflict_dn = msg->dn;
4680 /* For failures other than conflicts, fail the whole operation here */
4681 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4682 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
4683 ldb_dn_get_linearized(conflict_dn),
4684 ldb_errstring(ldb_module_get_ctx(ar->module)));
4686 return ldb_module_done(ar->req, NULL, NULL,
4687 LDB_ERR_OPERATIONS_ERROR);
4690 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4691 if (ret != LDB_SUCCESS) {
4692 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)));
4693 return ldb_module_done(ar->req, NULL, NULL,
4694 LDB_ERR_OPERATIONS_ERROR);
4698 if (rodc) {
4700 * We are on an RODC, or were a GC for this
4701 * partition, so we have to fail this until
4702 * someone who owns the partition sorts it
4703 * out
4705 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4706 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
4707 " - We must fail the operation until a master for this partition resolves the conflict",
4708 ldb_dn_get_linearized(conflict_dn));
4709 goto failed;
4713 * first we need the replPropertyMetaData attribute from the
4714 * local, conflicting record
4716 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
4717 attrs,
4718 DSDB_FLAG_NEXT_MODULE |
4719 DSDB_SEARCH_SHOW_DELETED |
4720 DSDB_SEARCH_SHOW_RECYCLED, req);
4721 if (ret != LDB_SUCCESS) {
4722 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4723 ldb_dn_get_linearized(conflict_dn)));
4724 goto failed;
4727 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4728 if (omd_value == NULL) {
4729 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4730 ldb_dn_get_linearized(conflict_dn)));
4731 goto failed;
4734 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4735 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4736 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4737 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4738 ldb_dn_get_linearized(conflict_dn)));
4739 goto failed;
4742 rmd = ar->objs->objects[ar->index_current].meta_data;
4745 * we decide which is newer based on the RPMD on the name
4746 * attribute. See [MS-DRSR] ResolveNameConflict.
4748 * We expect omd_name to be present, as this is from a local
4749 * search, but while rmd_name should have been given to us by
4750 * the remote server, if it is missing we just prefer the
4751 * local name in
4752 * replmd_replPropertyMetaData1_new_should_be_taken()
4754 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4755 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4756 if (!omd_name) {
4757 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4758 ldb_dn_get_linearized(conflict_dn)));
4759 goto failed;
4763 * Should we preserve the current record, and so rename the
4764 * incoming record to be a conflict?
4766 rename_incoming_record
4767 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
4768 omd_name, rmd_name);
4770 if (rename_incoming_record) {
4771 struct GUID guid;
4772 struct ldb_dn *new_dn;
4774 guid = samdb_result_guid(msg, "objectGUID");
4775 if (GUID_all_zero(&guid)) {
4776 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
4777 ldb_dn_get_linearized(conflict_dn)));
4778 goto failed;
4780 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4781 if (new_dn == NULL) {
4782 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4783 ldb_dn_get_linearized(conflict_dn)));
4784 goto failed;
4787 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
4788 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4790 /* re-submit the request, but with the new DN */
4791 callback = replmd_op_name_modify_callback;
4792 msg->dn = new_dn;
4793 } else {
4794 /* we are renaming the existing record */
4795 struct GUID guid;
4796 struct ldb_dn *new_dn;
4798 guid = samdb_result_guid(res->msgs[0], "objectGUID");
4799 if (GUID_all_zero(&guid)) {
4800 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
4801 ldb_dn_get_linearized(conflict_dn)));
4802 goto failed;
4805 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4806 if (new_dn == NULL) {
4807 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4808 ldb_dn_get_linearized(conflict_dn)));
4809 goto failed;
4812 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4813 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4815 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
4816 DSDB_FLAG_OWN_MODULE, req);
4817 if (ret != LDB_SUCCESS) {
4818 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
4819 ldb_dn_get_linearized(conflict_dn),
4820 ldb_dn_get_linearized(new_dn),
4821 ldb_errstring(ldb_module_get_ctx(ar->module))));
4822 goto failed;
4826 * now we need to ensure that the rename is seen as an
4827 * originating update. We do that with a modify.
4829 ret = replmd_name_modify(ar, req, new_dn);
4830 if (ret != LDB_SUCCESS) {
4831 goto failed;
4834 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
4835 ldb_dn_get_linearized(req->op.add.message->dn)));
4838 ret = ldb_build_add_req(&down_req,
4839 ldb_module_get_ctx(ar->module),
4840 req,
4841 msg,
4842 ar->controls,
4844 callback,
4845 req);
4846 if (ret != LDB_SUCCESS) {
4847 goto failed;
4849 LDB_REQ_SET_LOCATION(down_req);
4851 /* current partition control needed by "repmd_op_callback" */
4852 ret = ldb_request_add_control(down_req,
4853 DSDB_CONTROL_CURRENT_PARTITION_OID,
4854 false, NULL);
4855 if (ret != LDB_SUCCESS) {
4856 return replmd_replicated_request_error(ar, ret);
4859 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4860 /* this tells the partition module to make it a
4861 partial replica if creating an NC */
4862 ret = ldb_request_add_control(down_req,
4863 DSDB_CONTROL_PARTIAL_REPLICA,
4864 false, NULL);
4865 if (ret != LDB_SUCCESS) {
4866 return replmd_replicated_request_error(ar, ret);
4871 * Finally we re-run the add, otherwise the new record won't
4872 * exist, as we are here because of that exact failure!
4874 return ldb_next_request(ar->module, down_req);
4875 failed:
4877 /* on failure make the caller get the error. This means
4878 * replication will stop with an error, but there is not much
4879 * else we can do.
4881 return ldb_module_done(ar->req, NULL, NULL,
4882 ret);
4886 callback for replmd_replicated_apply_add()
4887 This copes with the creation of conflict records in the case where
4888 the DN exists, but with a different objectGUID
4890 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
4892 struct replmd_replicated_request *ar =
4893 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4895 if (ar->objs->objects[ar->index_current].last_known_parent) {
4896 /* This is like a conflict DN, where we put the object in LostAndFound
4897 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
4898 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
4901 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
4905 this is called when a new object comes in over DRS
4907 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
4909 struct ldb_context *ldb;
4910 struct ldb_request *change_req;
4911 enum ndr_err_code ndr_err;
4912 struct ldb_message *msg;
4913 struct replPropertyMetaDataBlob *md;
4914 struct ldb_val md_value;
4915 unsigned int i;
4916 int ret;
4917 bool remote_isDeleted = false;
4918 bool is_schema_nc;
4919 NTTIME now;
4920 time_t t = time(NULL);
4921 const struct ldb_val *rdn_val;
4922 struct replmd_private *replmd_private =
4923 talloc_get_type(ldb_module_get_private(ar->module),
4924 struct replmd_private);
4925 unix_to_nt_time(&now, t);
4927 ldb = ldb_module_get_ctx(ar->module);
4928 msg = ar->objs->objects[ar->index_current].msg;
4929 md = ar->objs->objects[ar->index_current].meta_data;
4930 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
4932 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4933 if (ret != LDB_SUCCESS) {
4934 return replmd_replicated_request_error(ar, ret);
4937 ret = dsdb_msg_add_guid(msg,
4938 &ar->objs->objects[ar->index_current].object_guid,
4939 "objectGUID");
4940 if (ret != LDB_SUCCESS) {
4941 return replmd_replicated_request_error(ar, ret);
4944 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4945 if (ret != LDB_SUCCESS) {
4946 return replmd_replicated_request_error(ar, ret);
4949 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
4950 if (ret != LDB_SUCCESS) {
4951 return replmd_replicated_request_error(ar, ret);
4954 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4955 if (ret != LDB_SUCCESS) {
4956 return replmd_replicated_request_error(ar, ret);
4959 /* remove any message elements that have zero values */
4960 for (i=0; i<msg->num_elements; i++) {
4961 struct ldb_message_element *el = &msg->elements[i];
4963 if (el->num_values == 0) {
4964 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
4965 ldb_asprintf_errstring(ldb, __location__
4966 ": empty objectClass sent on %s, aborting replication\n",
4967 ldb_dn_get_linearized(msg->dn));
4968 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
4971 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
4972 el->name));
4973 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
4974 msg->num_elements--;
4975 i--;
4976 continue;
4980 if (DEBUGLVL(4)) {
4981 struct GUID_txt_buf guid_txt;
4983 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
4984 DEBUG(4, ("DRS replication add message of %s:\n%s\n",
4985 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
4986 s));
4987 talloc_free(s);
4990 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4991 "isDeleted", false);
4994 * the meta data array is already sorted by the caller, except
4995 * for the RDN, which needs to be added.
4999 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5000 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5001 md, ar, now, is_schema_nc);
5002 if (ret != LDB_SUCCESS) {
5003 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5004 return replmd_replicated_request_error(ar, ret);
5007 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5008 if (ret != LDB_SUCCESS) {
5009 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5010 return replmd_replicated_request_error(ar, ret);
5013 for (i=0; i < md->ctr.ctr1.count; i++) {
5014 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5016 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5017 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5018 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5019 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5020 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5022 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5023 if (ret != LDB_SUCCESS) {
5024 return replmd_replicated_request_error(ar, ret);
5027 replmd_ldb_message_sort(msg, ar->schema);
5029 if (!remote_isDeleted) {
5030 ret = dsdb_module_schedule_sd_propagation(ar->module,
5031 ar->objs->partition_dn,
5032 msg->dn, true);
5033 if (ret != LDB_SUCCESS) {
5034 return replmd_replicated_request_error(ar, ret);
5038 ar->isDeleted = remote_isDeleted;
5040 ret = ldb_build_add_req(&change_req,
5041 ldb,
5043 msg,
5044 ar->controls,
5046 replmd_op_add_callback,
5047 ar->req);
5048 LDB_REQ_SET_LOCATION(change_req);
5049 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5051 /* current partition control needed by "repmd_op_callback" */
5052 ret = ldb_request_add_control(change_req,
5053 DSDB_CONTROL_CURRENT_PARTITION_OID,
5054 false, NULL);
5055 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5057 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5058 /* this tells the partition module to make it a
5059 partial replica if creating an NC */
5060 ret = ldb_request_add_control(change_req,
5061 DSDB_CONTROL_PARTIAL_REPLICA,
5062 false, NULL);
5063 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5066 return ldb_next_request(ar->module, change_req);
5069 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5070 struct ldb_reply *ares)
5072 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5073 struct replmd_replicated_request);
5074 int ret;
5076 if (!ares) {
5077 return ldb_module_done(ar->req, NULL, NULL,
5078 LDB_ERR_OPERATIONS_ERROR);
5082 * The error NO_SUCH_OBJECT is not expected, unless the search
5083 * base is the partition DN, and that case doesn't happen here
5084 * because then we wouldn't get a parent_guid_value in any
5085 * case.
5087 if (ares->error != LDB_SUCCESS) {
5088 return ldb_module_done(ar->req, ares->controls,
5089 ares->response, ares->error);
5092 switch (ares->type) {
5093 case LDB_REPLY_ENTRY:
5095 struct ldb_message *parent_msg = ares->message;
5096 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5097 struct ldb_dn *parent_dn;
5098 int comp_num;
5100 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5101 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5102 /* Per MS-DRSR 4.1.10.6.10
5103 * FindBestParentObject we need to move this
5104 * new object under a deleted object to
5105 * lost-and-found */
5106 struct ldb_dn *nc_root;
5108 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5109 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5110 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5111 "No suitable NC root found for %s. "
5112 "We need to move this object because parent object %s "
5113 "is deleted, but this object is not.",
5114 ldb_dn_get_linearized(msg->dn),
5115 ldb_dn_get_linearized(parent_msg->dn));
5116 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5117 } else if (ret != LDB_SUCCESS) {
5118 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5119 "Unable to find NC root for %s: %s. "
5120 "We need to move this object because parent object %s "
5121 "is deleted, but this object is not.",
5122 ldb_dn_get_linearized(msg->dn),
5123 ldb_errstring(ldb_module_get_ctx(ar->module)),
5124 ldb_dn_get_linearized(parent_msg->dn));
5125 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5128 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5129 nc_root,
5130 DS_GUID_LOSTANDFOUND_CONTAINER,
5131 &parent_dn);
5132 if (ret != LDB_SUCCESS) {
5133 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5134 "Unable to find LostAndFound Container for %s "
5135 "in partition %s: %s. "
5136 "We need to move this object because parent object %s "
5137 "is deleted, but this object is not.",
5138 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5139 ldb_errstring(ldb_module_get_ctx(ar->module)),
5140 ldb_dn_get_linearized(parent_msg->dn));
5141 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5143 ar->objs->objects[ar->index_current].last_known_parent
5144 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5146 } else {
5147 parent_dn
5148 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5151 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5153 comp_num = ldb_dn_get_comp_num(msg->dn);
5154 if (comp_num > 1) {
5155 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5156 talloc_free(ares);
5157 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5160 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5161 talloc_free(ares);
5162 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5164 break;
5166 case LDB_REPLY_REFERRAL:
5167 /* we ignore referrals */
5168 break;
5170 case LDB_REPLY_DONE:
5172 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5173 struct GUID_txt_buf str_buf;
5174 if (ar->search_msg != NULL) {
5175 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5176 "No parent with GUID %s found for object locally known as %s",
5177 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5178 ldb_dn_get_linearized(ar->search_msg->dn));
5179 } else {
5180 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5181 "No parent with GUID %s found for object remotely known as %s",
5182 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5183 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5187 * This error code is really important, as it
5188 * is the flag back to the callers to retry
5189 * this with DRSUAPI_DRS_GET_ANC, and so get
5190 * the parent objects before the child
5191 * objects
5193 return ldb_module_done(ar->req, NULL, NULL,
5194 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5197 if (ar->search_msg != NULL) {
5198 ret = replmd_replicated_apply_merge(ar);
5199 } else {
5200 ret = replmd_replicated_apply_add(ar);
5202 if (ret != LDB_SUCCESS) {
5203 return ldb_module_done(ar->req, NULL, NULL, ret);
5207 talloc_free(ares);
5208 return LDB_SUCCESS;
5212 * Look for the parent object, so we put the new object in the right
5213 * place This is akin to NameObject in MS-DRSR - this routine and the
5214 * callbacks find the right parent name, and correct name for this
5215 * object
5218 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5220 struct ldb_context *ldb;
5221 int ret;
5222 char *tmp_str;
5223 char *filter;
5224 struct ldb_request *search_req;
5225 static const char *attrs[] = {"isDeleted", NULL};
5226 struct GUID_txt_buf guid_str_buf;
5228 ldb = ldb_module_get_ctx(ar->module);
5230 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5231 if (ar->search_msg != NULL) {
5232 return replmd_replicated_apply_merge(ar);
5233 } else {
5234 return replmd_replicated_apply_add(ar);
5238 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5239 &guid_str_buf);
5241 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5242 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5244 ret = ldb_build_search_req(&search_req,
5245 ldb,
5247 ar->objs->partition_dn,
5248 LDB_SCOPE_SUBTREE,
5249 filter,
5250 attrs,
5251 NULL,
5253 replmd_replicated_apply_search_for_parent_callback,
5254 ar->req);
5255 LDB_REQ_SET_LOCATION(search_req);
5257 ret = dsdb_request_add_controls(search_req,
5258 DSDB_SEARCH_SHOW_RECYCLED|
5259 DSDB_SEARCH_SHOW_DELETED|
5260 DSDB_SEARCH_SHOW_EXTENDED_DN);
5261 if (ret != LDB_SUCCESS) {
5262 return ret;
5265 return ldb_next_request(ar->module, search_req);
5269 handle renames that come in over DRS replication
5271 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5272 struct ldb_message *msg,
5273 struct ldb_request *parent,
5274 bool *renamed)
5276 int ret;
5277 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5278 struct ldb_result *res;
5279 struct ldb_dn *conflict_dn;
5280 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5281 const struct ldb_val *omd_value;
5282 struct replPropertyMetaDataBlob omd, *rmd;
5283 enum ndr_err_code ndr_err;
5284 bool rename_incoming_record, rodc;
5285 struct replPropertyMetaData1 *rmd_name, *omd_name;
5286 struct ldb_dn *new_dn;
5287 struct GUID guid;
5289 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5290 ldb_dn_get_linearized(ar->search_msg->dn),
5291 ldb_dn_get_linearized(msg->dn)));
5294 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5295 DSDB_FLAG_NEXT_MODULE, ar->req);
5296 if (ret == LDB_SUCCESS) {
5297 talloc_free(tmp_ctx);
5298 *renamed = true;
5299 return ret;
5302 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5303 talloc_free(tmp_ctx);
5304 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5305 ldb_dn_get_linearized(ar->search_msg->dn),
5306 ldb_dn_get_linearized(msg->dn),
5307 ldb_errstring(ldb_module_get_ctx(ar->module)));
5308 return ret;
5311 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5312 if (ret != LDB_SUCCESS) {
5313 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5314 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5315 ldb_errstring(ldb_module_get_ctx(ar->module)));
5316 return LDB_ERR_OPERATIONS_ERROR;
5319 * we have a conflict, and need to decide if we will keep the
5320 * new record or the old record
5323 conflict_dn = msg->dn;
5325 if (rodc) {
5327 * We are on an RODC, or were a GC for this
5328 * partition, so we have to fail this until
5329 * someone who owns the partition sorts it
5330 * out
5332 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5333 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5334 " - We must fail the operation until a master for this partition resolves the conflict",
5335 ldb_dn_get_linearized(conflict_dn));
5336 goto failed;
5340 * first we need the replPropertyMetaData attribute from the
5341 * old record
5343 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
5344 attrs,
5345 DSDB_FLAG_NEXT_MODULE |
5346 DSDB_SEARCH_SHOW_DELETED |
5347 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5348 if (ret != LDB_SUCCESS) {
5349 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5350 ldb_dn_get_linearized(conflict_dn)));
5351 goto failed;
5354 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5355 if (omd_value == NULL) {
5356 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5357 ldb_dn_get_linearized(conflict_dn)));
5358 goto failed;
5361 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5362 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5363 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5364 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5365 ldb_dn_get_linearized(conflict_dn)));
5366 goto failed;
5369 rmd = ar->objs->objects[ar->index_current].meta_data;
5372 * we decide which is newer based on the RPMD on the name
5373 * attribute. See [MS-DRSR] ResolveNameConflict.
5375 * We expect omd_name to be present, as this is from a local
5376 * search, but while rmd_name should have been given to us by
5377 * the remote server, if it is missing we just prefer the
5378 * local name in
5379 * replmd_replPropertyMetaData1_new_should_be_taken()
5381 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5382 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5383 if (!omd_name) {
5384 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5385 ldb_dn_get_linearized(conflict_dn)));
5386 goto failed;
5390 * Should we preserve the current record, and so rename the
5391 * incoming record to be a conflict?
5393 rename_incoming_record =
5394 !replmd_replPropertyMetaData1_new_should_be_taken(
5395 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5396 omd_name, rmd_name);
5398 if (rename_incoming_record) {
5400 new_dn = replmd_conflict_dn(msg, msg->dn,
5401 &ar->objs->objects[ar->index_current].object_guid);
5402 if (new_dn == NULL) {
5403 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5404 "Failed to form conflict DN for %s\n",
5405 ldb_dn_get_linearized(msg->dn));
5407 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5410 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
5411 DSDB_FLAG_NEXT_MODULE, ar->req);
5412 if (ret != LDB_SUCCESS) {
5413 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5414 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5415 ldb_dn_get_linearized(conflict_dn),
5416 ldb_dn_get_linearized(ar->search_msg->dn),
5417 ldb_dn_get_linearized(new_dn),
5418 ldb_errstring(ldb_module_get_ctx(ar->module)));
5419 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5422 msg->dn = new_dn;
5423 *renamed = true;
5424 return LDB_SUCCESS;
5427 /* we are renaming the existing record */
5429 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5430 if (GUID_all_zero(&guid)) {
5431 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5432 ldb_dn_get_linearized(conflict_dn)));
5433 goto failed;
5436 new_dn = replmd_conflict_dn(tmp_ctx, conflict_dn, &guid);
5437 if (new_dn == NULL) {
5438 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5439 ldb_dn_get_linearized(conflict_dn)));
5440 goto failed;
5443 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5444 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5446 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5447 DSDB_FLAG_OWN_MODULE, ar->req);
5448 if (ret != LDB_SUCCESS) {
5449 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5450 ldb_dn_get_linearized(conflict_dn),
5451 ldb_dn_get_linearized(new_dn),
5452 ldb_errstring(ldb_module_get_ctx(ar->module))));
5453 goto failed;
5457 * now we need to ensure that the rename is seen as an
5458 * originating update. We do that with a modify.
5460 ret = replmd_name_modify(ar, ar->req, new_dn);
5461 if (ret != LDB_SUCCESS) {
5462 goto failed;
5465 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
5466 ldb_dn_get_linearized(ar->search_msg->dn),
5467 ldb_dn_get_linearized(msg->dn)));
5470 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5471 DSDB_FLAG_NEXT_MODULE, ar->req);
5472 if (ret != LDB_SUCCESS) {
5473 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
5474 ldb_dn_get_linearized(ar->search_msg->dn),
5475 ldb_dn_get_linearized(msg->dn),
5476 ldb_errstring(ldb_module_get_ctx(ar->module))));
5477 goto failed;
5479 failed:
5482 * On failure make the caller get the error
5483 * This means replication will stop with an error,
5484 * but there is not much else we can do. In the
5485 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
5486 * needed.
5489 talloc_free(tmp_ctx);
5490 return ret;
5494 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
5496 struct ldb_context *ldb;
5497 struct ldb_request *change_req;
5498 enum ndr_err_code ndr_err;
5499 struct ldb_message *msg;
5500 struct replPropertyMetaDataBlob *rmd;
5501 struct replPropertyMetaDataBlob omd;
5502 const struct ldb_val *omd_value;
5503 struct replPropertyMetaDataBlob nmd;
5504 struct ldb_val nmd_value;
5505 struct GUID remote_parent_guid;
5506 unsigned int i;
5507 uint32_t j,ni=0;
5508 unsigned int removed_attrs = 0;
5509 int ret;
5510 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
5511 bool isDeleted = false;
5512 bool local_isDeleted = false;
5513 bool remote_isDeleted = false;
5514 bool take_remote_isDeleted = false;
5515 bool sd_updated = false;
5516 bool renamed = false;
5517 bool is_schema_nc = false;
5518 NTSTATUS nt_status;
5519 const struct ldb_val *old_rdn, *new_rdn;
5520 struct replmd_private *replmd_private =
5521 talloc_get_type(ldb_module_get_private(ar->module),
5522 struct replmd_private);
5523 NTTIME now;
5524 time_t t = time(NULL);
5525 unix_to_nt_time(&now, t);
5527 ldb = ldb_module_get_ctx(ar->module);
5528 msg = ar->objs->objects[ar->index_current].msg;
5530 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5532 rmd = ar->objs->objects[ar->index_current].meta_data;
5533 ZERO_STRUCT(omd);
5534 omd.version = 1;
5536 /* find existing meta data */
5537 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5538 if (omd_value) {
5539 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5540 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5541 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5542 nt_status = ndr_map_error2ntstatus(ndr_err);
5543 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5546 if (omd.version != 1) {
5547 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5551 if (DEBUGLVL(5)) {
5552 struct GUID_txt_buf guid_txt;
5554 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5555 DEBUG(5, ("Initial DRS replication modify message of %s is:\n%s\n"
5556 "%s\n"
5557 "%s\n",
5558 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5560 ndr_print_struct_string(s,
5561 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5562 "existing replPropertyMetaData",
5563 &omd),
5564 ndr_print_struct_string(s,
5565 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5566 "incoming replPropertyMetaData",
5567 rmd)));
5568 talloc_free(s);
5571 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
5572 "isDeleted", false);
5573 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5574 "isDeleted", false);
5577 * Fill in the remote_parent_guid with the GUID or an all-zero
5578 * GUID.
5580 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
5581 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
5582 } else {
5583 remote_parent_guid = GUID_zero();
5587 * To ensure we follow a complex rename chain around, we have
5588 * to confirm that the DN is the same (mostly to confirm the
5589 * RDN) and the parentGUID is the same.
5591 * This ensures we keep things under the correct parent, which
5592 * replmd_replicated_handle_rename() will do.
5595 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
5596 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
5597 ret = LDB_SUCCESS;
5598 } else {
5600 * handle renames, even just by case that come in over
5601 * DRS. Changes in the parent DN don't hit us here,
5602 * because the search for a parent will clean up those
5603 * components.
5605 * We also have already filtered out the case where
5606 * the peer has an older name to what we have (see
5607 * replmd_replicated_apply_search_callback())
5609 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
5612 if (ret != LDB_SUCCESS) {
5613 ldb_debug(ldb, LDB_DEBUG_FATAL,
5614 "replmd_replicated_request rename %s => %s failed - %s\n",
5615 ldb_dn_get_linearized(ar->search_msg->dn),
5616 ldb_dn_get_linearized(msg->dn),
5617 ldb_errstring(ldb));
5618 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5621 if (renamed == true) {
5623 * Set the callback to one that will fix up the name
5624 * metadata on the new conflict DN
5626 callback = replmd_op_name_modify_callback;
5629 ZERO_STRUCT(nmd);
5630 nmd.version = 1;
5631 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
5632 nmd.ctr.ctr1.array = talloc_array(ar,
5633 struct replPropertyMetaData1,
5634 nmd.ctr.ctr1.count);
5635 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5637 /* first copy the old meta data */
5638 for (i=0; i < omd.ctr.ctr1.count; i++) {
5639 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
5640 ni++;
5643 ar->seq_num = 0;
5644 /* now merge in the new meta data */
5645 for (i=0; i < rmd->ctr.ctr1.count; i++) {
5646 bool found = false;
5648 for (j=0; j < ni; j++) {
5649 bool cmp;
5651 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
5652 continue;
5655 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
5656 ar->objs->dsdb_repl_flags,
5657 &nmd.ctr.ctr1.array[j],
5658 &rmd->ctr.ctr1.array[i]);
5659 if (cmp) {
5660 /* replace the entry */
5661 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
5662 if (ar->seq_num == 0) {
5663 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5664 if (ret != LDB_SUCCESS) {
5665 return replmd_replicated_request_error(ar, ret);
5668 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
5669 switch (nmd.ctr.ctr1.array[j].attid) {
5670 case DRSUAPI_ATTID_ntSecurityDescriptor:
5671 sd_updated = true;
5672 break;
5673 case DRSUAPI_ATTID_isDeleted:
5674 take_remote_isDeleted = true;
5675 break;
5676 default:
5677 break;
5679 found = true;
5680 break;
5683 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
5684 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
5685 msg->elements[i-removed_attrs].name,
5686 ldb_dn_get_linearized(msg->dn),
5687 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
5690 /* we don't want to apply this change so remove the attribute */
5691 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
5692 removed_attrs++;
5694 found = true;
5695 break;
5698 if (found) continue;
5700 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
5701 if (ar->seq_num == 0) {
5702 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5703 if (ret != LDB_SUCCESS) {
5704 return replmd_replicated_request_error(ar, ret);
5707 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
5708 switch (nmd.ctr.ctr1.array[ni].attid) {
5709 case DRSUAPI_ATTID_ntSecurityDescriptor:
5710 sd_updated = true;
5711 break;
5712 case DRSUAPI_ATTID_isDeleted:
5713 take_remote_isDeleted = true;
5714 break;
5715 default:
5716 break;
5718 ni++;
5722 * finally correct the size of the meta_data array
5724 nmd.ctr.ctr1.count = ni;
5726 new_rdn = ldb_dn_get_rdn_val(msg->dn);
5727 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
5729 if (renamed) {
5730 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
5731 &nmd, ar, now, is_schema_nc);
5732 if (ret != LDB_SUCCESS) {
5733 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5734 return replmd_replicated_request_error(ar, ret);
5738 * sort the new meta data array
5740 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
5741 if (ret != LDB_SUCCESS) {
5742 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5743 return ret;
5747 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
5748 * UpdateObject.
5750 * This also controls SD propagation below
5752 if (take_remote_isDeleted) {
5753 isDeleted = remote_isDeleted;
5754 } else {
5755 isDeleted = local_isDeleted;
5758 ar->isDeleted = isDeleted;
5761 * check if some replicated attributes left, otherwise skip the ldb_modify() call
5763 if (msg->num_elements == 0) {
5764 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
5765 ar->index_current);
5767 return replmd_replicated_apply_isDeleted(ar);
5770 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
5771 ar->index_current, msg->num_elements);
5773 if (renamed) {
5774 sd_updated = true;
5777 if (sd_updated && !isDeleted) {
5778 ret = dsdb_module_schedule_sd_propagation(ar->module,
5779 ar->objs->partition_dn,
5780 msg->dn, true);
5781 if (ret != LDB_SUCCESS) {
5782 return ldb_operr(ldb);
5786 /* create the meta data value */
5787 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
5788 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5789 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5790 nt_status = ndr_map_error2ntstatus(ndr_err);
5791 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5795 * when we know that we'll modify the record, add the whenChanged, uSNChanged
5796 * and replPopertyMetaData attributes
5798 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5799 if (ret != LDB_SUCCESS) {
5800 return replmd_replicated_request_error(ar, ret);
5802 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5803 if (ret != LDB_SUCCESS) {
5804 return replmd_replicated_request_error(ar, ret);
5806 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
5807 if (ret != LDB_SUCCESS) {
5808 return replmd_replicated_request_error(ar, ret);
5811 replmd_ldb_message_sort(msg, ar->schema);
5813 /* we want to replace the old values */
5814 for (i=0; i < msg->num_elements; i++) {
5815 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
5816 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5817 if (msg->elements[i].num_values == 0) {
5818 ldb_asprintf_errstring(ldb, __location__
5819 ": objectClass removed on %s, aborting replication\n",
5820 ldb_dn_get_linearized(msg->dn));
5821 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5826 if (DEBUGLVL(4)) {
5827 struct GUID_txt_buf guid_txt;
5829 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5830 DEBUG(4, ("Final DRS replication modify message of %s:\n%s\n",
5831 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5832 s));
5833 talloc_free(s);
5836 ret = ldb_build_mod_req(&change_req,
5837 ldb,
5839 msg,
5840 ar->controls,
5842 callback,
5843 ar->req);
5844 LDB_REQ_SET_LOCATION(change_req);
5845 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5847 /* current partition control needed by "repmd_op_callback" */
5848 ret = ldb_request_add_control(change_req,
5849 DSDB_CONTROL_CURRENT_PARTITION_OID,
5850 false, NULL);
5851 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5853 return ldb_next_request(ar->module, change_req);
5856 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
5857 struct ldb_reply *ares)
5859 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5860 struct replmd_replicated_request);
5861 int ret;
5863 if (!ares) {
5864 return ldb_module_done(ar->req, NULL, NULL,
5865 LDB_ERR_OPERATIONS_ERROR);
5867 if (ares->error != LDB_SUCCESS &&
5868 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5869 return ldb_module_done(ar->req, ares->controls,
5870 ares->response, ares->error);
5873 switch (ares->type) {
5874 case LDB_REPLY_ENTRY:
5875 ar->search_msg = talloc_steal(ar, ares->message);
5876 break;
5878 case LDB_REPLY_REFERRAL:
5879 /* we ignore referrals */
5880 break;
5882 case LDB_REPLY_DONE:
5884 struct replPropertyMetaData1 *md_remote;
5885 struct replPropertyMetaData1 *md_local;
5887 struct replPropertyMetaDataBlob omd;
5888 const struct ldb_val *omd_value;
5889 struct replPropertyMetaDataBlob *rmd;
5890 struct ldb_message *msg;
5891 int instanceType;
5892 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
5893 ar->objs->objects[ar->index_current].last_known_parent = NULL;
5896 * This is the ADD case, find the appropriate parent,
5897 * as this object doesn't exist locally:
5899 if (ar->search_msg == NULL) {
5900 ret = replmd_replicated_apply_search_for_parent(ar);
5901 if (ret != LDB_SUCCESS) {
5902 return ldb_module_done(ar->req, NULL, NULL, ret);
5904 talloc_free(ares);
5905 return LDB_SUCCESS;
5909 * Otherwise, in the MERGE case, work out if we are
5910 * attempting a rename, and if so find the parent the
5911 * newly renamed object wants to belong under (which
5912 * may not be the parent in it's attached string DN
5914 rmd = ar->objs->objects[ar->index_current].meta_data;
5915 ZERO_STRUCT(omd);
5916 omd.version = 1;
5918 /* find existing meta data */
5919 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5920 if (omd_value) {
5921 enum ndr_err_code ndr_err;
5922 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5923 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5924 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5925 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5926 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5929 if (omd.version != 1) {
5930 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5934 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
5936 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
5937 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
5938 && GUID_all_zero(&ar->local_parent_guid)) {
5939 DEBUG(0, ("Refusing to replicate new version of %s "
5940 "as local object has an all-zero parentGUID attribute, "
5941 "despite not being an NC root\n",
5942 ldb_dn_get_linearized(ar->search_msg->dn)));
5943 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5947 * now we need to check for double renames. We could have a
5948 * local rename pending which our replication partner hasn't
5949 * received yet. We choose which one wins by looking at the
5950 * attribute stamps on the two objects, the newer one wins.
5952 * This also simply applies the correct algorithms for
5953 * determining if a change was made to name at all, or
5954 * if the object has just been renamed under the same
5955 * parent.
5957 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5958 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5959 if (!md_local) {
5960 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5961 ldb_dn_get_linearized(ar->search_msg->dn)));
5962 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5966 * if there is no name attribute given then we have to assume the
5967 * object we've received has the older name
5969 if (replmd_replPropertyMetaData1_new_should_be_taken(
5970 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5971 md_local, md_remote)) {
5972 struct GUID_txt_buf p_guid_local;
5973 struct GUID_txt_buf p_guid_remote;
5974 msg = ar->objs->objects[ar->index_current].msg;
5976 /* Merge on the existing object, with rename */
5978 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
5979 "as incoming object changing to %s under %s\n",
5980 ldb_dn_get_linearized(ar->search_msg->dn),
5981 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5982 ldb_dn_get_linearized(msg->dn),
5983 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5984 &p_guid_remote)));
5985 ret = replmd_replicated_apply_search_for_parent(ar);
5986 } else {
5987 struct GUID_txt_buf p_guid_local;
5988 struct GUID_txt_buf p_guid_remote;
5989 msg = ar->objs->objects[ar->index_current].msg;
5992 * Merge on the existing object, force no
5993 * rename (code below just to explain why in
5994 * the DEBUG() logs)
5997 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
5998 ldb_dn_get_linearized(msg->dn)) == 0) {
5999 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6000 GUID_equal(&ar->local_parent_guid,
6001 ar->objs->objects[ar->index_current].parent_guid)
6002 == false) {
6003 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6004 "despite incoming object changing parent to %s\n",
6005 ldb_dn_get_linearized(ar->search_msg->dn),
6006 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6007 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6008 &p_guid_remote)));
6010 } else {
6011 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6012 " and rejecting older rename to %s under %s\n",
6013 ldb_dn_get_linearized(ar->search_msg->dn),
6014 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6015 ldb_dn_get_linearized(msg->dn),
6016 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6017 &p_guid_remote)));
6020 * This assignment ensures that the strcmp()
6021 * and GUID_equal() calls in
6022 * replmd_replicated_apply_merge() avoids the
6023 * rename call
6025 ar->objs->objects[ar->index_current].parent_guid =
6026 &ar->local_parent_guid;
6028 msg->dn = ar->search_msg->dn;
6029 ret = replmd_replicated_apply_merge(ar);
6031 if (ret != LDB_SUCCESS) {
6032 return ldb_module_done(ar->req, NULL, NULL, ret);
6037 talloc_free(ares);
6038 return LDB_SUCCESS;
6041 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6043 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6045 struct ldb_context *ldb;
6046 int ret;
6047 char *tmp_str;
6048 char *filter;
6049 struct ldb_request *search_req;
6050 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6051 "parentGUID", "instanceType",
6052 "replPropertyMetaData", "nTSecurityDescriptor",
6053 "isDeleted", NULL };
6054 struct GUID_txt_buf guid_str_buf;
6056 if (ar->index_current >= ar->objs->num_objects) {
6057 /* done with it, go to next stage */
6058 return replmd_replicated_uptodate_vector(ar);
6061 ldb = ldb_module_get_ctx(ar->module);
6062 ar->search_msg = NULL;
6063 ar->isDeleted = false;
6065 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6066 &guid_str_buf);
6068 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6069 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6071 ret = ldb_build_search_req(&search_req,
6072 ldb,
6074 ar->objs->partition_dn,
6075 LDB_SCOPE_SUBTREE,
6076 filter,
6077 attrs,
6078 NULL,
6080 replmd_replicated_apply_search_callback,
6081 ar->req);
6082 LDB_REQ_SET_LOCATION(search_req);
6084 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
6086 if (ret != LDB_SUCCESS) {
6087 return ret;
6090 return ldb_next_request(ar->module, search_req);
6094 * This is essentially a wrapper for replmd_replicated_apply_next()
6096 * This is needed to ensure that both codepaths call this handler.
6098 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6100 struct ldb_dn *deleted_objects_dn;
6101 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6102 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
6103 &deleted_objects_dn);
6104 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
6106 * Do a delete here again, so that if there is
6107 * anything local that conflicts with this
6108 * object being deleted, it is removed. This
6109 * includes links. See MS-DRSR 4.1.10.6.9
6110 * UpdateObject.
6112 * If the object is already deleted, and there
6113 * is no more work required, it doesn't do
6114 * anything.
6117 /* This has been updated to point to the DN we eventually did the modify on */
6119 struct ldb_request *del_req;
6120 struct ldb_result *res;
6122 TALLOC_CTX *tmp_ctx = talloc_new(ar);
6123 if (!tmp_ctx) {
6124 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6125 return ret;
6128 res = talloc_zero(tmp_ctx, struct ldb_result);
6129 if (!res) {
6130 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6131 talloc_free(tmp_ctx);
6132 return ret;
6135 /* Build a delete request, which hopefully will artually turn into nothing */
6136 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
6137 msg->dn,
6138 NULL,
6139 res,
6140 ldb_modify_default_callback,
6141 ar->req);
6142 LDB_REQ_SET_LOCATION(del_req);
6143 if (ret != LDB_SUCCESS) {
6144 talloc_free(tmp_ctx);
6145 return ret;
6149 * This is the guts of the call, call back
6150 * into our delete code, but setting the
6151 * re_delete flag so we delete anything that
6152 * shouldn't be there on a deleted or recycled
6153 * object
6155 ret = replmd_delete_internals(ar->module, del_req, true);
6156 if (ret == LDB_SUCCESS) {
6157 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
6160 talloc_free(tmp_ctx);
6161 if (ret != LDB_SUCCESS) {
6162 return ret;
6166 ar->index_current++;
6167 return replmd_replicated_apply_next(ar);
6170 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
6171 struct ldb_reply *ares)
6173 struct ldb_context *ldb;
6174 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6175 struct replmd_replicated_request);
6176 ldb = ldb_module_get_ctx(ar->module);
6178 if (!ares) {
6179 return ldb_module_done(ar->req, NULL, NULL,
6180 LDB_ERR_OPERATIONS_ERROR);
6182 if (ares->error != LDB_SUCCESS) {
6183 return ldb_module_done(ar->req, ares->controls,
6184 ares->response, ares->error);
6187 if (ares->type != LDB_REPLY_DONE) {
6188 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
6189 return ldb_module_done(ar->req, NULL, NULL,
6190 LDB_ERR_OPERATIONS_ERROR);
6193 talloc_free(ares);
6195 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6198 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
6200 struct ldb_context *ldb;
6201 struct ldb_request *change_req;
6202 enum ndr_err_code ndr_err;
6203 struct ldb_message *msg;
6204 struct replUpToDateVectorBlob ouv;
6205 const struct ldb_val *ouv_value;
6206 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
6207 struct replUpToDateVectorBlob nuv;
6208 struct ldb_val nuv_value;
6209 struct ldb_message_element *nuv_el = NULL;
6210 struct ldb_message_element *orf_el = NULL;
6211 struct repsFromToBlob nrf;
6212 struct ldb_val *nrf_value = NULL;
6213 struct ldb_message_element *nrf_el = NULL;
6214 unsigned int i;
6215 uint32_t j,ni=0;
6216 bool found = false;
6217 time_t t = time(NULL);
6218 NTTIME now;
6219 int ret;
6220 uint32_t instanceType;
6222 ldb = ldb_module_get_ctx(ar->module);
6223 ruv = ar->objs->uptodateness_vector;
6224 ZERO_STRUCT(ouv);
6225 ouv.version = 2;
6226 ZERO_STRUCT(nuv);
6227 nuv.version = 2;
6229 unix_to_nt_time(&now, t);
6231 if (ar->search_msg == NULL) {
6232 /* this happens for a REPL_OBJ call where we are
6233 creating the target object by replicating it. The
6234 subdomain join code does this for the partition DN
6236 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
6237 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6240 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
6241 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
6242 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
6243 ldb_dn_get_linearized(ar->search_msg->dn)));
6244 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6248 * first create the new replUpToDateVector
6250 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
6251 if (ouv_value) {
6252 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
6253 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
6254 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6255 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6256 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6259 if (ouv.version != 2) {
6260 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6265 * the new uptodateness vector will at least
6266 * contain 1 entry, one for the source_dsa
6268 * plus optional values from our old vector and the one from the source_dsa
6270 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
6271 if (ruv) nuv.ctr.ctr2.count += ruv->count;
6272 nuv.ctr.ctr2.cursors = talloc_array(ar,
6273 struct drsuapi_DsReplicaCursor2,
6274 nuv.ctr.ctr2.count);
6275 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6277 /* first copy the old vector */
6278 for (i=0; i < ouv.ctr.ctr2.count; i++) {
6279 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
6280 ni++;
6283 /* merge in the source_dsa vector is available */
6284 for (i=0; (ruv && i < ruv->count); i++) {
6285 found = false;
6287 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6288 &ar->our_invocation_id)) {
6289 continue;
6292 for (j=0; j < ni; j++) {
6293 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6294 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
6295 continue;
6298 found = true;
6300 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
6301 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
6303 break;
6306 if (found) continue;
6308 /* if it's not there yet, add it */
6309 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
6310 ni++;
6314 * finally correct the size of the cursors array
6316 nuv.ctr.ctr2.count = ni;
6319 * sort the cursors
6321 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
6324 * create the change ldb_message
6326 msg = ldb_msg_new(ar);
6327 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6328 msg->dn = ar->search_msg->dn;
6330 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
6331 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
6332 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6333 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6334 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6336 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
6337 if (ret != LDB_SUCCESS) {
6338 return replmd_replicated_request_error(ar, ret);
6340 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
6343 * now create the new repsFrom value from the given repsFromTo1 structure
6345 ZERO_STRUCT(nrf);
6346 nrf.version = 1;
6347 nrf.ctr.ctr1 = *ar->objs->source_dsa;
6348 nrf.ctr.ctr1.last_attempt = now;
6349 nrf.ctr.ctr1.last_success = now;
6350 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
6353 * first see if we already have a repsFrom value for the current source dsa
6354 * if so we'll later replace this value
6356 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
6357 if (orf_el) {
6358 for (i=0; i < orf_el->num_values; i++) {
6359 struct repsFromToBlob *trf;
6361 trf = talloc(ar, struct repsFromToBlob);
6362 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6364 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
6365 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
6366 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6367 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6368 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6371 if (trf->version != 1) {
6372 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6376 * we compare the source dsa objectGUID not the invocation_id
6377 * because we want only one repsFrom value per source dsa
6378 * and when the invocation_id of the source dsa has changed we don't need
6379 * the old repsFrom with the old invocation_id
6381 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
6382 &ar->objs->source_dsa->source_dsa_obj_guid)) {
6383 talloc_free(trf);
6384 continue;
6387 talloc_free(trf);
6388 nrf_value = &orf_el->values[i];
6389 break;
6393 * copy over all old values to the new ldb_message
6395 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
6396 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6397 *nrf_el = *orf_el;
6401 * if we haven't found an old repsFrom value for the current source dsa
6402 * we'll add a new value
6404 if (!nrf_value) {
6405 struct ldb_val zero_value;
6406 ZERO_STRUCT(zero_value);
6407 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
6408 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6410 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
6413 /* we now fill the value which is already attached to ldb_message */
6414 ndr_err = ndr_push_struct_blob(nrf_value, msg,
6415 &nrf,
6416 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
6417 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6418 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6419 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6423 * the ldb_message_element for the attribute, has all the old values and the new one
6424 * so we'll replace the whole attribute with all values
6426 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
6428 if (CHECK_DEBUGLVL(4)) {
6429 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
6430 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
6431 talloc_free(s);
6434 /* prepare the ldb_modify() request */
6435 ret = ldb_build_mod_req(&change_req,
6436 ldb,
6438 msg,
6439 ar->controls,
6441 replmd_replicated_uptodate_modify_callback,
6442 ar->req);
6443 LDB_REQ_SET_LOCATION(change_req);
6444 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6446 return ldb_next_request(ar->module, change_req);
6449 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
6450 struct ldb_reply *ares)
6452 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6453 struct replmd_replicated_request);
6454 int ret;
6456 if (!ares) {
6457 return ldb_module_done(ar->req, NULL, NULL,
6458 LDB_ERR_OPERATIONS_ERROR);
6460 if (ares->error != LDB_SUCCESS &&
6461 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6462 return ldb_module_done(ar->req, ares->controls,
6463 ares->response, ares->error);
6466 switch (ares->type) {
6467 case LDB_REPLY_ENTRY:
6468 ar->search_msg = talloc_steal(ar, ares->message);
6469 break;
6471 case LDB_REPLY_REFERRAL:
6472 /* we ignore referrals */
6473 break;
6475 case LDB_REPLY_DONE:
6476 ret = replmd_replicated_uptodate_modify(ar);
6477 if (ret != LDB_SUCCESS) {
6478 return ldb_module_done(ar->req, NULL, NULL, ret);
6482 talloc_free(ares);
6483 return LDB_SUCCESS;
6487 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
6489 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
6490 struct replmd_private *replmd_private =
6491 talloc_get_type_abort(ldb_module_get_private(ar->module),
6492 struct replmd_private);
6493 int ret;
6494 static const char *attrs[] = {
6495 "replUpToDateVector",
6496 "repsFrom",
6497 "instanceType",
6498 NULL
6500 struct ldb_request *search_req;
6502 ar->search_msg = NULL;
6505 * Let the caller know that we did an originating updates
6507 ar->objs->originating_updates = replmd_private->originating_updates;
6509 ret = ldb_build_search_req(&search_req,
6510 ldb,
6512 ar->objs->partition_dn,
6513 LDB_SCOPE_BASE,
6514 "(objectClass=*)",
6515 attrs,
6516 NULL,
6518 replmd_replicated_uptodate_search_callback,
6519 ar->req);
6520 LDB_REQ_SET_LOCATION(search_req);
6521 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6523 return ldb_next_request(ar->module, search_req);
6528 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
6530 struct ldb_context *ldb;
6531 struct dsdb_extended_replicated_objects *objs;
6532 struct replmd_replicated_request *ar;
6533 struct ldb_control **ctrls;
6534 int ret;
6535 uint32_t i;
6536 struct replmd_private *replmd_private =
6537 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6539 ldb = ldb_module_get_ctx(module);
6541 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
6543 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
6544 if (!objs) {
6545 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
6546 return LDB_ERR_PROTOCOL_ERROR;
6549 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
6550 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
6551 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
6552 return LDB_ERR_PROTOCOL_ERROR;
6555 ar = replmd_ctx_init(module, req);
6556 if (!ar)
6557 return LDB_ERR_OPERATIONS_ERROR;
6559 /* Set the flags to have the replmd_op_callback run over the full set of objects */
6560 ar->apply_mode = true;
6561 ar->objs = objs;
6562 ar->schema = dsdb_get_schema(ldb, ar);
6563 if (!ar->schema) {
6564 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
6565 talloc_free(ar);
6566 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
6567 return LDB_ERR_CONSTRAINT_VIOLATION;
6570 ctrls = req->controls;
6572 if (req->controls) {
6573 req->controls = talloc_memdup(ar, req->controls,
6574 talloc_get_size(req->controls));
6575 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6578 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
6579 if (ret != LDB_SUCCESS) {
6580 return ret;
6583 /* If this change contained linked attributes in the body
6584 * (rather than in the links section) we need to update
6585 * backlinks in linked_attributes */
6586 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
6587 if (ret != LDB_SUCCESS) {
6588 return ret;
6591 ar->controls = req->controls;
6592 req->controls = ctrls;
6594 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
6596 /* save away the linked attributes for the end of the
6597 transaction */
6598 for (i=0; i<ar->objs->linked_attributes_count; i++) {
6599 struct la_entry *la_entry;
6601 if (replmd_private->la_ctx == NULL) {
6602 replmd_private->la_ctx = talloc_new(replmd_private);
6604 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6605 if (la_entry == NULL) {
6606 ldb_oom(ldb);
6607 return LDB_ERR_OPERATIONS_ERROR;
6609 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
6610 if (la_entry->la == NULL) {
6611 talloc_free(la_entry);
6612 ldb_oom(ldb);
6613 return LDB_ERR_OPERATIONS_ERROR;
6615 *la_entry->la = ar->objs->linked_attributes[i];
6617 /* we need to steal the non-scalars so they stay
6618 around until the end of the transaction */
6619 talloc_steal(la_entry->la, la_entry->la->identifier);
6620 talloc_steal(la_entry->la, la_entry->la->value.blob);
6622 DLIST_ADD(replmd_private->la_list, la_entry);
6625 return replmd_replicated_apply_next(ar);
6629 process one linked attribute structure
6631 static int replmd_process_linked_attribute(struct ldb_module *module,
6632 struct replmd_private *replmd_private,
6633 struct la_entry *la_entry,
6634 struct ldb_request *parent)
6636 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
6637 struct ldb_context *ldb = ldb_module_get_ctx(module);
6638 struct ldb_message *msg;
6639 struct ldb_message *target_msg = NULL;
6640 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
6641 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
6642 int ret;
6643 const struct dsdb_attribute *attr;
6644 struct dsdb_dn *dsdb_dn;
6645 uint64_t seq_num = 0;
6646 struct ldb_message_element *old_el;
6647 WERROR status;
6648 time_t t = time(NULL);
6649 struct ldb_result *res;
6650 struct ldb_result *target_res;
6651 const char *attrs[4];
6652 const char *attrs2[] = { "isDeleted", "isRecycled", NULL };
6653 struct parsed_dn *pdn_list, *pdn, *next;
6654 struct GUID guid = GUID_zero();
6655 NTSTATUS ntstatus;
6656 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
6658 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
6659 enum deletion_state target_deletion_state = OBJECT_NOT_DELETED;
6662 linked_attributes[0]:
6663 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
6664 identifier : *
6665 identifier: struct drsuapi_DsReplicaObjectIdentifier
6666 __ndr_size : 0x0000003a (58)
6667 __ndr_size_sid : 0x00000000 (0)
6668 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
6669 sid : S-0-0
6670 __ndr_size_dn : 0x00000000 (0)
6671 dn : ''
6672 attid : DRSUAPI_ATTID_member (0x1F)
6673 value: struct drsuapi_DsAttributeValue
6674 __ndr_size : 0x0000007e (126)
6675 blob : *
6676 blob : DATA_BLOB length=126
6677 flags : 0x00000001 (1)
6678 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
6679 originating_add_time : Wed Sep 2 22:20:01 2009 EST
6680 meta_data: struct drsuapi_DsReplicaMetaData
6681 version : 0x00000015 (21)
6682 originating_change_time : Wed Sep 2 23:39:07 2009 EST
6683 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
6684 originating_usn : 0x000000000001e19c (123292)
6686 (for cases where the link is to a normal DN)
6687 &target: struct drsuapi_DsReplicaObjectIdentifier3
6688 __ndr_size : 0x0000007e (126)
6689 __ndr_size_sid : 0x0000001c (28)
6690 guid : 7639e594-db75-4086-b0d4-67890ae46031
6691 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
6692 __ndr_size_dn : 0x00000022 (34)
6693 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
6696 /* find the attribute being modified */
6697 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
6698 if (attr == NULL) {
6699 struct GUID_txt_buf guid_str;
6700 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
6701 la->attid,
6702 GUID_buf_string(&la->identifier->guid,
6703 &guid_str));
6704 talloc_free(tmp_ctx);
6705 return LDB_ERR_OPERATIONS_ERROR;
6708 attrs[0] = attr->lDAPDisplayName;
6709 attrs[1] = "isDeleted";
6710 attrs[2] = "isRecycled";
6711 attrs[3] = NULL;
6713 /* get the existing message from the db for the object with
6714 this GUID, returning attribute being modified. We will then
6715 use this msg as the basis for a modify call */
6716 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
6717 DSDB_FLAG_NEXT_MODULE |
6718 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6719 DSDB_SEARCH_SHOW_RECYCLED |
6720 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
6721 DSDB_SEARCH_REVEAL_INTERNALS,
6722 parent,
6723 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
6724 if (ret != LDB_SUCCESS) {
6725 talloc_free(tmp_ctx);
6726 return ret;
6728 if (res->count != 1) {
6729 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
6730 GUID_string(tmp_ctx, &la->identifier->guid));
6731 talloc_free(tmp_ctx);
6732 return LDB_ERR_NO_SUCH_OBJECT;
6734 msg = res->msgs[0];
6737 * Check for deleted objects per MS-DRSR 4.1.10.6.13
6738 * ProcessLinkValue, because link updates are not applied to
6739 * recycled and tombstone objects. We don't have to delete
6740 * any existing link, that should have happened when the
6741 * object deletion was replicated or initiated.
6744 replmd_deletion_state(module, msg, &deletion_state, NULL);
6746 if (deletion_state >= OBJECT_RECYCLED) {
6747 talloc_free(tmp_ctx);
6748 return LDB_SUCCESS;
6751 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
6752 if (old_el == NULL) {
6753 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
6754 if (ret != LDB_SUCCESS) {
6755 ldb_module_oom(module);
6756 talloc_free(tmp_ctx);
6757 return LDB_ERR_OPERATIONS_ERROR;
6759 } else {
6760 old_el->flags = LDB_FLAG_MOD_REPLACE;
6763 /* parse the existing links */
6764 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx, old_el, &pdn_list,
6765 attr->syntax->ldap_oid, parent);
6767 if (ret != LDB_SUCCESS) {
6768 talloc_free(tmp_ctx);
6769 return ret;
6772 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
6773 if (!W_ERROR_IS_OK(status)) {
6774 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
6775 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
6776 talloc_free(tmp_ctx);
6777 return LDB_ERR_OPERATIONS_ERROR;
6780 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
6781 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
6783 * This strange behaviour (allowing a NULL/missing
6784 * GUID) originally comes from:
6786 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
6787 * Author: Andrew Tridgell <tridge@samba.org>
6788 * Date: Mon Dec 21 21:21:55 2009 +1100
6790 * s4-drs: cope better with NULL GUIDS from DRS
6792 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
6793 * need to match by DN if possible when seeing if we should update an
6794 * existing link.
6796 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
6799 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
6800 dsdb_dn->dn, attrs2,
6801 DSDB_FLAG_NEXT_MODULE |
6802 DSDB_SEARCH_SHOW_RECYCLED |
6803 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6804 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6805 parent);
6806 } else if (!NT_STATUS_IS_OK(ntstatus)) {
6807 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
6808 old_el->name,
6809 ldb_dn_get_linearized(dsdb_dn->dn),
6810 ldb_dn_get_linearized(msg->dn));
6811 talloc_free(tmp_ctx);
6812 return LDB_ERR_OPERATIONS_ERROR;
6813 } else {
6814 ret = dsdb_module_search(module, tmp_ctx, &target_res,
6815 NULL, LDB_SCOPE_SUBTREE,
6816 attrs2,
6817 DSDB_FLAG_NEXT_MODULE |
6818 DSDB_SEARCH_SHOW_RECYCLED |
6819 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6820 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6821 parent,
6822 "objectGUID=%s",
6823 GUID_string(tmp_ctx, &guid));
6826 if (ret != LDB_SUCCESS) {
6827 ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to re-resolve GUID %s: %s\n",
6828 GUID_string(tmp_ctx, &guid),
6829 ldb_errstring(ldb_module_get_ctx(module)));
6830 talloc_free(tmp_ctx);
6831 return ret;
6834 if (target_res->count == 0) {
6835 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
6836 GUID_string(tmp_ctx, &guid),
6837 ldb_dn_get_linearized(dsdb_dn->dn)));
6838 } else if (target_res->count != 1) {
6839 ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching objectGUID %s\n",
6840 GUID_string(tmp_ctx, &guid));
6841 talloc_free(tmp_ctx);
6842 return LDB_ERR_OPERATIONS_ERROR;
6843 } else {
6844 target_msg = target_res->msgs[0];
6845 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
6849 * Check for deleted objects per MS-DRSR 4.1.10.6.13
6850 * ProcessLinkValue, because link updates are not applied to
6851 * recycled and tombstone objects. We don't have to delete
6852 * any existing link, that should have happened when the
6853 * object deletion was replicated or initiated.
6855 replmd_deletion_state(module, target_msg,
6856 &target_deletion_state, NULL);
6858 if (target_deletion_state >= OBJECT_RECYCLED) {
6859 talloc_free(tmp_ctx);
6860 return LDB_SUCCESS;
6863 /* see if this link already exists */
6864 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
6865 &guid,
6866 dsdb_dn->dn,
6867 &pdn, &next,
6868 attr->syntax->ldap_oid);
6869 if (ret != LDB_SUCCESS) {
6870 talloc_free(tmp_ctx);
6871 return ret;
6875 if (pdn != NULL) {
6876 /* see if this update is newer than what we have already */
6877 struct GUID invocation_id = GUID_zero();
6878 uint32_t version = 0;
6879 uint32_t originating_usn = 0;
6880 NTTIME change_time = 0;
6881 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
6883 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
6884 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
6885 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
6886 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
6888 if (!replmd_update_is_newer(&invocation_id,
6889 &la->meta_data.originating_invocation_id,
6890 version,
6891 la->meta_data.version,
6892 change_time,
6893 la->meta_data.originating_change_time)) {
6894 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
6895 old_el->name, ldb_dn_get_linearized(msg->dn),
6896 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
6897 talloc_free(tmp_ctx);
6898 return LDB_SUCCESS;
6901 /* get a seq_num for this change */
6902 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
6903 if (ret != LDB_SUCCESS) {
6904 talloc_free(tmp_ctx);
6905 return ret;
6908 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
6909 /* remove the existing backlink */
6910 ret = replmd_add_backlink(module, replmd_private,
6911 schema, &la->identifier->guid,
6912 &guid, false, attr, true);
6913 if (ret != LDB_SUCCESS) {
6914 talloc_free(tmp_ctx);
6915 return ret;
6919 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
6920 &la->meta_data.originating_invocation_id,
6921 la->meta_data.originating_usn, seq_num,
6922 la->meta_data.originating_change_time,
6923 la->meta_data.version,
6924 !active);
6925 if (ret != LDB_SUCCESS) {
6926 talloc_free(tmp_ctx);
6927 return ret;
6930 if (active) {
6931 /* add the new backlink */
6932 ret = replmd_add_backlink(module, replmd_private,
6933 schema, &la->identifier->guid,
6934 &guid, true, attr, true);
6935 if (ret != LDB_SUCCESS) {
6936 talloc_free(tmp_ctx);
6937 return ret;
6940 } else {
6941 unsigned offset;
6942 /* get a seq_num for this change */
6943 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
6944 if (ret != LDB_SUCCESS) {
6945 talloc_free(tmp_ctx);
6946 return ret;
6949 * We know where the new one needs to be, from the *next
6950 * pointer into pdn_list.
6952 if (next == NULL) {
6953 offset = old_el->num_values;
6954 } else {
6955 if (next->dsdb_dn == NULL) {
6956 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
6957 attr->syntax->ldap_oid);
6958 if (ret != LDB_SUCCESS) {
6959 return ret;
6962 offset = next - pdn_list;
6963 if (offset > old_el->num_values) {
6964 talloc_free(tmp_ctx);
6965 return LDB_ERR_OPERATIONS_ERROR;
6969 old_el->values = talloc_realloc(msg->elements, old_el->values,
6970 struct ldb_val, old_el->num_values+1);
6971 if (!old_el->values) {
6972 ldb_module_oom(module);
6973 return LDB_ERR_OPERATIONS_ERROR;
6976 if (offset != old_el->num_values) {
6977 memmove(&old_el->values[offset + 1], &old_el->values[offset],
6978 (old_el->num_values - offset) * sizeof(old_el->values[0]));
6981 old_el->num_values++;
6983 ret = replmd_build_la_val(tmp_ctx, &old_el->values[offset], dsdb_dn,
6984 &la->meta_data.originating_invocation_id,
6985 la->meta_data.originating_usn, seq_num,
6986 la->meta_data.originating_change_time,
6987 la->meta_data.version,
6988 !active);
6989 if (ret != LDB_SUCCESS) {
6990 talloc_free(tmp_ctx);
6991 return ret;
6994 if (active) {
6995 ret = replmd_add_backlink(module, replmd_private,
6996 schema, &la->identifier->guid,
6997 &guid, true, attr, true);
6998 if (ret != LDB_SUCCESS) {
6999 talloc_free(tmp_ctx);
7000 return ret;
7005 /* we only change whenChanged and uSNChanged if the seq_num
7006 has changed */
7007 ret = add_time_element(msg, "whenChanged", t);
7008 if (ret != LDB_SUCCESS) {
7009 talloc_free(tmp_ctx);
7010 ldb_operr(ldb);
7011 return ret;
7014 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
7015 if (ret != LDB_SUCCESS) {
7016 talloc_free(tmp_ctx);
7017 ldb_operr(ldb);
7018 return ret;
7021 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7022 if (old_el == NULL) {
7023 talloc_free(tmp_ctx);
7024 return ldb_operr(ldb);
7027 ret = dsdb_check_single_valued_link(attr, old_el);
7028 if (ret != LDB_SUCCESS) {
7029 talloc_free(tmp_ctx);
7030 return ret;
7033 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
7035 ret = linked_attr_modify(module, msg, parent);
7036 if (ret != LDB_SUCCESS) {
7037 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
7038 ldb_errstring(ldb),
7039 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
7040 talloc_free(tmp_ctx);
7041 return ret;
7044 talloc_free(tmp_ctx);
7046 return ret;
7049 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
7051 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
7052 return replmd_extended_replicated_objects(module, req);
7055 return ldb_next_request(module, req);
7060 we hook into the transaction operations to allow us to
7061 perform the linked attribute updates at the end of the whole
7062 transaction. This allows a forward linked attribute to be created
7063 before the object is created. During a vampire, w2k8 sends us linked
7064 attributes before the objects they are part of.
7066 static int replmd_start_transaction(struct ldb_module *module)
7068 /* create our private structure for this transaction */
7069 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
7070 struct replmd_private);
7071 replmd_txn_cleanup(replmd_private);
7073 /* free any leftover mod_usn records from cancelled
7074 transactions */
7075 while (replmd_private->ncs) {
7076 struct nc_entry *e = replmd_private->ncs;
7077 DLIST_REMOVE(replmd_private->ncs, e);
7078 talloc_free(e);
7081 replmd_private->originating_updates = false;
7083 return ldb_next_start_trans(module);
7087 on prepare commit we loop over our queued la_context structures and
7088 apply each of them
7090 static int replmd_prepare_commit(struct ldb_module *module)
7092 struct replmd_private *replmd_private =
7093 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
7094 struct la_entry *la, *prev;
7095 struct la_backlink *bl;
7096 int ret;
7098 /* walk the list backwards, to do the first entry first, as we
7099 * added the entries with DLIST_ADD() which puts them at the
7100 * start of the list */
7101 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
7102 prev = DLIST_PREV(la);
7103 DLIST_REMOVE(replmd_private->la_list, la);
7104 ret = replmd_process_linked_attribute(module, replmd_private,
7105 la, NULL);
7106 if (ret != LDB_SUCCESS) {
7107 replmd_txn_cleanup(replmd_private);
7108 return ret;
7112 /* process our backlink list, creating and deleting backlinks
7113 as necessary */
7114 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
7115 ret = replmd_process_backlink(module, bl, NULL);
7116 if (ret != LDB_SUCCESS) {
7117 replmd_txn_cleanup(replmd_private);
7118 return ret;
7122 replmd_txn_cleanup(replmd_private);
7124 /* possibly change @REPLCHANGED */
7125 ret = replmd_notify_store(module, NULL);
7126 if (ret != LDB_SUCCESS) {
7127 return ret;
7130 return ldb_next_prepare_commit(module);
7133 static int replmd_del_transaction(struct ldb_module *module)
7135 struct replmd_private *replmd_private =
7136 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
7137 replmd_txn_cleanup(replmd_private);
7139 return ldb_next_del_trans(module);
7143 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
7144 .name = "repl_meta_data",
7145 .init_context = replmd_init,
7146 .add = replmd_add,
7147 .modify = replmd_modify,
7148 .rename = replmd_rename,
7149 .del = replmd_delete,
7150 .extended = replmd_extended,
7151 .start_transaction = replmd_start_transaction,
7152 .prepare_commit = replmd_prepare_commit,
7153 .del_transaction = replmd_del_transaction,
7156 int ldb_repl_meta_data_module_init(const char *version)
7158 LDB_MODULE_CHECK_VERSION(version);
7159 return ldb_register_module(&ldb_repl_meta_data_module_ops);