replmd: Small refactor to replmd_check_singleval_la_conflict()
[Samba.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blobd21ae97a0ebd545b90de92a66873ffdcfc53a25b
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 "dsdb/common/util.h"
43 #include "../libds/common/flags.h"
44 #include "librpc/gen_ndr/irpc.h"
45 #include "librpc/gen_ndr/ndr_misc.h"
46 #include "librpc/gen_ndr/ndr_drsuapi.h"
47 #include "librpc/gen_ndr/ndr_drsblobs.h"
48 #include "param/param.h"
49 #include "libcli/security/security.h"
50 #include "lib/util/dlinklist.h"
51 #include "dsdb/samdb/ldb_modules/util.h"
52 #include "lib/util/tsort.h"
54 #undef DBGC_CLASS
55 #define DBGC_CLASS DBGC_DRS_REPL
58 * It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
59 * Deleted Objects Container
61 static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME = 2650466015990000000ULL;
63 struct replmd_private {
64 TALLOC_CTX *la_ctx;
65 struct la_entry *la_list;
66 struct nc_entry {
67 struct nc_entry *prev, *next;
68 struct ldb_dn *dn;
69 uint64_t mod_usn;
70 uint64_t mod_usn_urgent;
71 } *ncs;
72 struct ldb_dn *schema_dn;
73 bool originating_updates;
74 bool sorted_links;
77 struct la_entry {
78 struct la_entry *next, *prev;
79 struct drsuapi_DsReplicaLinkedAttribute *la;
80 uint32_t dsdb_repl_flags;
83 struct replmd_replicated_request {
84 struct ldb_module *module;
85 struct ldb_request *req;
87 const struct dsdb_schema *schema;
88 struct GUID our_invocation_id;
90 /* the controls we pass down */
91 struct ldb_control **controls;
94 * Backlinks for the replmd_add() case (we want to create
95 * backlinks after creating the user, but before the end of
96 * the ADD request)
98 struct la_backlink *la_backlinks;
100 /* details for the mode where we apply a bunch of inbound replication meessages */
101 bool apply_mode;
102 uint32_t index_current;
103 struct dsdb_extended_replicated_objects *objs;
105 struct ldb_message *search_msg;
106 struct GUID local_parent_guid;
108 uint64_t seq_num;
109 bool is_urgent;
111 bool isDeleted;
114 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
115 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
116 static int replmd_check_upgrade_links(struct ldb_context *ldb,
117 struct parsed_dn *dns, uint32_t count,
118 struct ldb_message_element *el,
119 const char *ldap_oid);
120 static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
121 struct la_entry *la);
123 enum urgent_situation {
124 REPL_URGENT_ON_CREATE = 1,
125 REPL_URGENT_ON_UPDATE = 2,
126 REPL_URGENT_ON_DELETE = 4
129 enum deletion_state {
130 OBJECT_NOT_DELETED=1,
131 OBJECT_DELETED=2,
132 OBJECT_RECYCLED=3,
133 OBJECT_TOMBSTONE=4,
134 OBJECT_REMOVED=5
137 static void replmd_deletion_state(struct ldb_module *module,
138 const struct ldb_message *msg,
139 enum deletion_state *current_state,
140 enum deletion_state *next_state)
142 int ret;
143 bool enabled = false;
145 if (msg == NULL) {
146 *current_state = OBJECT_REMOVED;
147 if (next_state != NULL) {
148 *next_state = OBJECT_REMOVED;
150 return;
153 ret = dsdb_recyclebin_enabled(module, &enabled);
154 if (ret != LDB_SUCCESS) {
155 enabled = false;
158 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
159 if (!enabled) {
160 *current_state = OBJECT_TOMBSTONE;
161 if (next_state != NULL) {
162 *next_state = OBJECT_REMOVED;
164 return;
167 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
168 *current_state = OBJECT_RECYCLED;
169 if (next_state != NULL) {
170 *next_state = OBJECT_REMOVED;
172 return;
175 *current_state = OBJECT_DELETED;
176 if (next_state != NULL) {
177 *next_state = OBJECT_RECYCLED;
179 return;
182 *current_state = OBJECT_NOT_DELETED;
183 if (next_state == NULL) {
184 return;
187 if (enabled) {
188 *next_state = OBJECT_DELETED;
189 } else {
190 *next_state = OBJECT_TOMBSTONE;
194 static const struct {
195 const char *update_name;
196 enum urgent_situation repl_situation;
197 } urgent_objects[] = {
198 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
199 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
200 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
201 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
202 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
203 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
204 {NULL, 0}
207 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
208 static const char *urgent_attrs[] = {
209 "lockoutTime",
210 "pwdLastSet",
211 "userAccountControl",
212 NULL
216 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
217 enum urgent_situation situation)
219 unsigned int i, j;
220 for (i=0; urgent_objects[i].update_name; i++) {
222 if ((situation & urgent_objects[i].repl_situation) == 0) {
223 continue;
226 for (j=0; j<objectclass_el->num_values; j++) {
227 const struct ldb_val *v = &objectclass_el->values[j];
228 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
229 return true;
233 return false;
236 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
238 if (ldb_attr_in_list(urgent_attrs, el->name)) {
239 return true;
241 return false;
244 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
247 initialise the module
248 allocate the private structure and build the list
249 of partition DNs for use by replmd_notify()
251 static int replmd_init(struct ldb_module *module)
253 struct replmd_private *replmd_private;
254 struct ldb_context *ldb = ldb_module_get_ctx(module);
255 static const char *samba_dsdb_attrs[] = { SAMBA_COMPATIBLE_FEATURES_ATTR, NULL };
256 struct ldb_dn *samba_dsdb_dn;
257 struct ldb_result *res;
258 int ret;
259 TALLOC_CTX *frame = talloc_stackframe();
260 replmd_private = talloc_zero(module, struct replmd_private);
261 if (replmd_private == NULL) {
262 ldb_oom(ldb);
263 TALLOC_FREE(frame);
264 return LDB_ERR_OPERATIONS_ERROR;
266 ldb_module_set_private(module, replmd_private);
268 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
270 samba_dsdb_dn = ldb_dn_new(frame, ldb, "@SAMBA_DSDB");
271 if (!samba_dsdb_dn) {
272 TALLOC_FREE(frame);
273 return ldb_oom(ldb);
276 ret = dsdb_module_search_dn(module, frame, &res, samba_dsdb_dn,
277 samba_dsdb_attrs, DSDB_FLAG_NEXT_MODULE, NULL);
278 if (ret == LDB_SUCCESS) {
279 replmd_private->sorted_links
280 = ldb_msg_check_string_attribute(res->msgs[0],
281 SAMBA_COMPATIBLE_FEATURES_ATTR,
282 SAMBA_SORTED_LINKS_FEATURE);
284 TALLOC_FREE(frame);
286 return ldb_next_init(module);
290 cleanup our per-transaction contexts
292 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
294 talloc_free(replmd_private->la_ctx);
295 replmd_private->la_list = NULL;
296 replmd_private->la_ctx = NULL;
301 struct la_backlink {
302 struct la_backlink *next, *prev;
303 const char *attr_name;
304 struct ldb_dn *forward_dn;
305 struct GUID target_guid;
306 bool active;
310 a ldb_modify request operating on modules below the
311 current module
313 static int linked_attr_modify(struct ldb_module *module,
314 const struct ldb_message *message,
315 struct ldb_request *parent)
317 struct ldb_request *mod_req;
318 int ret;
319 struct ldb_context *ldb = ldb_module_get_ctx(module);
320 TALLOC_CTX *tmp_ctx = talloc_new(module);
321 struct ldb_result *res;
323 res = talloc_zero(tmp_ctx, struct ldb_result);
324 if (!res) {
325 talloc_free(tmp_ctx);
326 return ldb_oom(ldb_module_get_ctx(module));
329 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
330 message,
331 NULL,
332 res,
333 ldb_modify_default_callback,
334 parent);
335 LDB_REQ_SET_LOCATION(mod_req);
336 if (ret != LDB_SUCCESS) {
337 talloc_free(tmp_ctx);
338 return ret;
341 ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
342 false, NULL);
343 if (ret != LDB_SUCCESS) {
344 return ret;
347 /* Run the new request */
348 ret = ldb_next_request(module, mod_req);
350 if (ret == LDB_SUCCESS) {
351 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
354 talloc_free(tmp_ctx);
355 return ret;
359 process a backlinks we accumulated during a transaction, adding and
360 deleting the backlinks from the target objects
362 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
364 struct ldb_dn *target_dn, *source_dn;
365 int ret;
366 struct ldb_context *ldb = ldb_module_get_ctx(module);
367 struct ldb_message *msg;
368 TALLOC_CTX *frame = talloc_stackframe();
369 char *dn_string;
372 - find DN of target
373 - find DN of source
374 - construct ldb_message
375 - either an add or a delete
377 ret = dsdb_module_dn_by_guid(module, frame, &bl->target_guid, &target_dn, parent);
378 if (ret != LDB_SUCCESS) {
379 struct GUID_txt_buf guid_str;
380 DBG_WARNING("Failed to find target DN for linked attribute with GUID %s\n",
381 GUID_buf_string(&bl->target_guid, &guid_str));
382 DBG_WARNING("Please run 'samba-tool dbcheck' to resolve any missing backlinks.\n");
383 talloc_free(frame);
384 return LDB_SUCCESS;
387 msg = ldb_msg_new(frame);
388 if (msg == NULL) {
389 ldb_module_oom(module);
390 talloc_free(frame);
391 return LDB_ERR_OPERATIONS_ERROR;
394 source_dn = ldb_dn_copy(frame, bl->forward_dn);
395 if (!source_dn) {
396 ldb_module_oom(module);
397 talloc_free(frame);
398 return LDB_ERR_OPERATIONS_ERROR;
399 } else {
400 /* Filter down to the attributes we want in the backlink */
401 const char *accept[] = { "GUID", "SID", NULL };
402 ldb_dn_extended_filter(source_dn, accept);
405 /* construct a ldb_message for adding/deleting the backlink */
406 msg->dn = target_dn;
407 dn_string = ldb_dn_get_extended_linearized(frame, bl->forward_dn, 1);
408 if (!dn_string) {
409 ldb_module_oom(module);
410 talloc_free(frame);
411 return LDB_ERR_OPERATIONS_ERROR;
413 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
414 if (ret != LDB_SUCCESS) {
415 talloc_free(frame);
416 return ret;
418 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
420 /* a backlink should never be single valued. Unfortunately the
421 exchange schema has a attribute
422 msExchBridgeheadedLocalConnectorsDNBL which is single
423 valued and a backlink. We need to cope with that by
424 ignoring the single value flag */
425 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
427 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
428 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
429 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
430 cope with possible corruption where the backlink has
431 already been removed */
432 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
433 ldb_dn_get_linearized(target_dn),
434 ldb_dn_get_linearized(source_dn),
435 ldb_errstring(ldb)));
436 ret = LDB_SUCCESS;
437 } else if (ret != LDB_SUCCESS) {
438 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
439 bl->active?"add":"remove",
440 ldb_dn_get_linearized(source_dn),
441 ldb_dn_get_linearized(target_dn),
442 ldb_errstring(ldb));
443 talloc_free(frame);
444 return ret;
446 talloc_free(frame);
447 return ret;
451 add a backlink to the list of backlinks to add/delete in the prepare
452 commit
454 forward_dn is stolen onto the defereed context
456 static int replmd_defer_add_backlink(struct ldb_module *module,
457 struct replmd_private *replmd_private,
458 const struct dsdb_schema *schema,
459 struct replmd_replicated_request *ac,
460 struct ldb_dn *forward_dn,
461 struct GUID *target_guid, bool active,
462 const struct dsdb_attribute *schema_attr,
463 struct ldb_request *parent)
465 const struct dsdb_attribute *target_attr;
466 struct la_backlink *bl;
468 bl = talloc(ac, struct la_backlink);
469 if (bl == NULL) {
470 ldb_module_oom(module);
471 return LDB_ERR_OPERATIONS_ERROR;
474 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
475 if (!target_attr) {
477 * windows 2003 has a broken schema where the
478 * definition of msDS-IsDomainFor is missing (which is
479 * supposed to be the backlink of the
480 * msDS-HasDomainNCs attribute
482 return LDB_SUCCESS;
485 bl->attr_name = target_attr->lDAPDisplayName;
486 bl->forward_dn = talloc_steal(bl, forward_dn);
487 bl->target_guid = *target_guid;
488 bl->active = active;
490 DLIST_ADD(ac->la_backlinks, bl);
492 return LDB_SUCCESS;
496 add a backlink to the list of backlinks to add/delete in the prepare
497 commit
499 static int replmd_add_backlink(struct ldb_module *module,
500 struct replmd_private *replmd_private,
501 const struct dsdb_schema *schema,
502 struct ldb_dn *forward_dn,
503 struct GUID *target_guid, bool active,
504 const struct dsdb_attribute *schema_attr,
505 struct ldb_request *parent)
507 const struct dsdb_attribute *target_attr;
508 struct la_backlink bl;
509 int ret;
511 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
512 if (!target_attr) {
514 * windows 2003 has a broken schema where the
515 * definition of msDS-IsDomainFor is missing (which is
516 * supposed to be the backlink of the
517 * msDS-HasDomainNCs attribute
519 return LDB_SUCCESS;
522 bl.attr_name = target_attr->lDAPDisplayName;
523 bl.forward_dn = forward_dn;
524 bl.target_guid = *target_guid;
525 bl.active = active;
527 ret = replmd_process_backlink(module, &bl, parent);
528 return ret;
533 * Callback for most write operations in this module:
535 * notify the repl task that a object has changed. The notifies are
536 * gathered up in the replmd_private structure then written to the
537 * @REPLCHANGED object in each partition during the prepare_commit
539 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
541 int ret;
542 struct replmd_replicated_request *ac =
543 talloc_get_type_abort(req->context, struct replmd_replicated_request);
544 struct replmd_private *replmd_private =
545 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
546 struct nc_entry *modified_partition;
547 struct ldb_control *partition_ctrl;
548 const struct dsdb_control_current_partition *partition;
550 struct ldb_control **controls;
552 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
554 controls = ares->controls;
555 if (ldb_request_get_control(ac->req,
556 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
558 * Remove the current partition control from what we pass up
559 * the chain if it hasn't been requested manually.
561 controls = ldb_controls_except_specified(ares->controls, ares,
562 partition_ctrl);
565 if (ares->error != LDB_SUCCESS) {
566 struct GUID_txt_buf guid_txt;
567 struct ldb_message *msg = NULL;
568 char *s = NULL;
570 if (ac->apply_mode == false) {
571 DBG_NOTICE("Originating update failure. Error is: %s\n",
572 ldb_strerror(ares->error));
573 return ldb_module_done(ac->req, controls,
574 ares->response, ares->error);
577 msg = ac->objs->objects[ac->index_current].msg;
579 * Set at DBG_NOTICE as once these start to happe, they
580 * will happen a lot until resolved, due to repeated
581 * replication. The caller will probably print the
582 * ldb error string anyway.
584 DBG_NOTICE("DRS replication apply failure for %s. Error is: %s\n",
585 ldb_dn_get_linearized(msg->dn),
586 ldb_strerror(ares->error));
588 s = ldb_ldif_message_redacted_string(ldb_module_get_ctx(ac->module),
590 LDB_CHANGETYPE_ADD,
591 msg);
593 DBG_INFO("Failing DRS %s replication message was %s:\n%s\n",
594 ac->search_msg == NULL ? "ADD" : "MODIFY",
595 GUID_buf_string(&ac->objs->objects[ac->index_current].object_guid,
596 &guid_txt),
598 talloc_free(s);
599 return ldb_module_done(ac->req, controls,
600 ares->response, ares->error);
603 if (ares->type != LDB_REPLY_DONE) {
604 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
605 return ldb_module_done(ac->req, NULL,
606 NULL, LDB_ERR_OPERATIONS_ERROR);
609 if (ac->apply_mode == false) {
610 struct la_backlink *bl;
612 * process our backlink list after an replmd_add(),
613 * creating and deleting backlinks as necessary (this
614 * code is sync). The other cases are handled inline
615 * with the modify.
617 for (bl=ac->la_backlinks; bl; bl=bl->next) {
618 ret = replmd_process_backlink(ac->module, bl, ac->req);
619 if (ret != LDB_SUCCESS) {
620 return ldb_module_done(ac->req, NULL,
621 NULL, ret);
626 if (!partition_ctrl) {
627 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
628 return ldb_module_done(ac->req, NULL,
629 NULL, LDB_ERR_OPERATIONS_ERROR);
632 partition = talloc_get_type_abort(partition_ctrl->data,
633 struct dsdb_control_current_partition);
635 if (ac->seq_num > 0) {
636 for (modified_partition = replmd_private->ncs; modified_partition;
637 modified_partition = modified_partition->next) {
638 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
639 break;
643 if (modified_partition == NULL) {
644 modified_partition = talloc_zero(replmd_private, struct nc_entry);
645 if (!modified_partition) {
646 ldb_oom(ldb_module_get_ctx(ac->module));
647 return ldb_module_done(ac->req, NULL,
648 NULL, LDB_ERR_OPERATIONS_ERROR);
650 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
651 if (!modified_partition->dn) {
652 ldb_oom(ldb_module_get_ctx(ac->module));
653 return ldb_module_done(ac->req, NULL,
654 NULL, LDB_ERR_OPERATIONS_ERROR);
656 DLIST_ADD(replmd_private->ncs, modified_partition);
659 if (ac->seq_num > modified_partition->mod_usn) {
660 modified_partition->mod_usn = ac->seq_num;
661 if (ac->is_urgent) {
662 modified_partition->mod_usn_urgent = ac->seq_num;
665 if (!ac->apply_mode) {
666 replmd_private->originating_updates = true;
670 if (ac->apply_mode) {
671 ret = replmd_replicated_apply_isDeleted(ac);
672 if (ret != LDB_SUCCESS) {
673 return ldb_module_done(ac->req, NULL, NULL, ret);
675 return ret;
676 } else {
677 /* free the partition control container here, for the
678 * common path. Other cases will have it cleaned up
679 * eventually with the ares */
680 talloc_free(partition_ctrl);
681 return ldb_module_done(ac->req, controls,
682 ares->response, LDB_SUCCESS);
688 * update a @REPLCHANGED record in each partition if there have been
689 * any writes of replicated data in the partition
691 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
693 struct replmd_private *replmd_private =
694 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
696 while (replmd_private->ncs) {
697 int ret;
698 struct nc_entry *modified_partition = replmd_private->ncs;
700 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
701 modified_partition->mod_usn,
702 modified_partition->mod_usn_urgent, parent);
703 if (ret != LDB_SUCCESS) {
704 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
705 ldb_dn_get_linearized(modified_partition->dn)));
706 return ret;
709 if (ldb_dn_compare(modified_partition->dn,
710 replmd_private->schema_dn) == 0) {
711 struct ldb_result *ext_res;
712 ret = dsdb_module_extended(module,
713 replmd_private->schema_dn,
714 &ext_res,
715 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
716 ext_res,
717 DSDB_FLAG_NEXT_MODULE,
718 parent);
719 if (ret != LDB_SUCCESS) {
720 return ret;
722 talloc_free(ext_res);
725 DLIST_REMOVE(replmd_private->ncs, modified_partition);
726 talloc_free(modified_partition);
729 return LDB_SUCCESS;
734 created a replmd_replicated_request context
736 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
737 struct ldb_request *req)
739 struct ldb_context *ldb;
740 struct replmd_replicated_request *ac;
741 const struct GUID *our_invocation_id;
743 ldb = ldb_module_get_ctx(module);
745 ac = talloc_zero(req, struct replmd_replicated_request);
746 if (ac == NULL) {
747 ldb_oom(ldb);
748 return NULL;
751 ac->module = module;
752 ac->req = req;
754 ac->schema = dsdb_get_schema(ldb, ac);
755 if (!ac->schema) {
756 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
757 "replmd_modify: no dsdb_schema loaded");
758 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
759 talloc_free(ac);
760 return NULL;
763 /* get our invocationId */
764 our_invocation_id = samdb_ntds_invocation_id(ldb);
765 if (!our_invocation_id) {
766 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
767 "replmd_add: unable to find invocationId\n");
768 talloc_free(ac);
769 return NULL;
771 ac->our_invocation_id = *our_invocation_id;
773 return ac;
777 add a time element to a record
779 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
781 struct ldb_message_element *el;
782 char *s;
783 int ret;
785 if (ldb_msg_find_element(msg, attr) != NULL) {
786 return LDB_SUCCESS;
789 s = ldb_timestring(msg, t);
790 if (s == NULL) {
791 return LDB_ERR_OPERATIONS_ERROR;
794 ret = ldb_msg_add_string(msg, attr, s);
795 if (ret != LDB_SUCCESS) {
796 return ret;
799 el = ldb_msg_find_element(msg, attr);
800 /* always set as replace. This works because on add ops, the flag
801 is ignored */
802 el->flags = LDB_FLAG_MOD_REPLACE;
804 return LDB_SUCCESS;
808 add a uint64_t element to a record
810 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
811 const char *attr, uint64_t v)
813 struct ldb_message_element *el;
814 int ret;
816 if (ldb_msg_find_element(msg, attr) != NULL) {
817 return LDB_SUCCESS;
820 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
821 if (ret != LDB_SUCCESS) {
822 return ret;
825 el = ldb_msg_find_element(msg, attr);
826 /* always set as replace. This works because on add ops, the flag
827 is ignored */
828 el->flags = LDB_FLAG_MOD_REPLACE;
830 return LDB_SUCCESS;
833 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
834 const struct replPropertyMetaData1 *m2,
835 const uint32_t *rdn_attid)
838 * This assignment seems inoccous, but it is critical for the
839 * system, as we need to do the comparisons as a unsigned
840 * quantity, not signed (enums are signed integers)
842 uint32_t attid_1 = m1->attid;
843 uint32_t attid_2 = m2->attid;
845 if (attid_1 == attid_2) {
846 return 0;
850 * See above regarding this being an unsigned comparison.
851 * Otherwise when the high bit is set on non-standard
852 * attributes, they would end up first, before objectClass
853 * (0).
855 return attid_1 > attid_2 ? 1 : -1;
858 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
859 struct replPropertyMetaDataCtr1 *ctr1,
860 struct ldb_dn *dn)
862 if (ctr1->count == 0) {
863 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
864 "No elements found in replPropertyMetaData for %s!\n",
865 ldb_dn_get_linearized(dn));
866 return LDB_ERR_CONSTRAINT_VIOLATION;
869 /* the objectClass attribute is value 0x00000000, so must be first */
870 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
871 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
872 "No objectClass found in replPropertyMetaData for %s!\n",
873 ldb_dn_get_linearized(dn));
874 return LDB_ERR_OBJECT_CLASS_VIOLATION;
877 return LDB_SUCCESS;
880 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
881 struct replPropertyMetaDataCtr1 *ctr1,
882 struct ldb_dn *dn)
884 /* Note this is O(n^2) for the almost-sorted case, which this is */
885 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, NULL,
886 replmd_replPropertyMetaData1_attid_sort);
887 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
890 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
891 const struct ldb_message_element *e2,
892 const struct dsdb_schema *schema)
894 const struct dsdb_attribute *a1;
895 const struct dsdb_attribute *a2;
898 * TODO: make this faster by caching the dsdb_attribute pointer
899 * on the ldb_messag_element
902 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
903 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
906 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
907 * in the schema
909 if (!a1 || !a2) {
910 return strcasecmp(e1->name, e2->name);
912 if (a1->attributeID_id == a2->attributeID_id) {
913 return 0;
915 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
918 static void replmd_ldb_message_sort(struct ldb_message *msg,
919 const struct dsdb_schema *schema)
921 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
924 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
925 const struct GUID *invocation_id, uint64_t seq_num,
926 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
928 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2);
930 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
931 struct ldb_message_element *el, struct parsed_dn **pdn,
932 const char *ldap_oid, struct ldb_request *parent);
935 fix up linked attributes in replmd_add.
936 This involves setting up the right meta-data in extended DN
937 components, and creating backlinks to the object
939 static int replmd_add_fix_la(struct ldb_module *module, TALLOC_CTX *mem_ctx,
940 struct replmd_private *replmd_private,
941 struct ldb_message_element *el,
942 struct replmd_replicated_request *ac,
943 NTTIME now,
944 struct ldb_dn *forward_dn,
945 const struct dsdb_attribute *sa,
946 struct ldb_request *parent)
948 unsigned int i;
949 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
950 struct ldb_context *ldb = ldb_module_get_ctx(module);
951 struct parsed_dn *pdn;
952 /* We will take a reference to the schema in replmd_add_backlink */
953 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
954 struct ldb_val *new_values = NULL;
955 int ret;
957 if (dsdb_check_single_valued_link(sa, el) == LDB_SUCCESS) {
958 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
959 } else {
960 ldb_asprintf_errstring(ldb,
961 "Attribute %s is single valued but "
962 "more than one value has been supplied",
963 el->name);
964 talloc_free(tmp_ctx);
965 return LDB_ERR_CONSTRAINT_VIOLATION;
968 ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
969 sa->syntax->ldap_oid, parent);
970 if (ret != LDB_SUCCESS) {
971 talloc_free(tmp_ctx);
972 return ret;
975 new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
976 if (new_values == NULL) {
977 ldb_module_oom(module);
978 talloc_free(tmp_ctx);
979 return LDB_ERR_OPERATIONS_ERROR;
982 for (i = 0; i < el->num_values; i++) {
983 struct parsed_dn *p = &pdn[i];
984 if (i > 0 && parsed_dn_compare(p, &pdn[i - 1]) == 0) {
985 ldb_asprintf_errstring(ldb,
986 "Linked attribute %s has "
987 "multiple identical values", el->name);
988 talloc_free(tmp_ctx);
989 if (ldb_attr_cmp(el->name, "member") == 0) {
990 return LDB_ERR_ENTRY_ALREADY_EXISTS;
991 } else {
992 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
995 ret = replmd_build_la_val(el->values, p->v, p->dsdb_dn,
996 &ac->our_invocation_id,
997 ac->seq_num, ac->seq_num, now, 0, false);
998 if (ret != LDB_SUCCESS) {
999 talloc_free(tmp_ctx);
1000 return ret;
1003 ret = replmd_defer_add_backlink(module, replmd_private,
1004 schema, ac,
1005 forward_dn, &p->guid, true, sa,
1006 parent);
1007 if (ret != LDB_SUCCESS) {
1008 talloc_free(tmp_ctx);
1009 return ret;
1012 new_values[i] = *p->v;
1014 el->values = talloc_steal(mem_ctx, new_values);
1016 talloc_free(tmp_ctx);
1017 return LDB_SUCCESS;
1020 static int replmd_add_make_extended_dn(struct ldb_request *req,
1021 const DATA_BLOB *guid_blob,
1022 struct ldb_dn **_extended_dn)
1024 int ret;
1025 const DATA_BLOB *sid_blob;
1026 /* Calculate an extended DN for any linked attributes */
1027 struct ldb_dn *extended_dn = ldb_dn_copy(req, req->op.add.message->dn);
1028 if (!extended_dn) {
1029 return LDB_ERR_OPERATIONS_ERROR;
1031 ret = ldb_dn_set_extended_component(extended_dn, "GUID", guid_blob);
1032 if (ret != LDB_SUCCESS) {
1033 return ret;
1036 sid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectSID");
1037 if (sid_blob != NULL) {
1038 ret = ldb_dn_set_extended_component(extended_dn, "SID", sid_blob);
1039 if (ret != LDB_SUCCESS) {
1040 return ret;
1043 *_extended_dn = extended_dn;
1044 return LDB_SUCCESS;
1048 intercept add requests
1050 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
1052 struct ldb_context *ldb;
1053 struct ldb_control *control;
1054 struct replmd_replicated_request *ac;
1055 enum ndr_err_code ndr_err;
1056 struct ldb_request *down_req;
1057 struct ldb_message *msg;
1058 const DATA_BLOB *guid_blob;
1059 DATA_BLOB guid_blob_stack;
1060 struct GUID guid;
1061 uint8_t guid_data[16];
1062 struct replPropertyMetaDataBlob nmd;
1063 struct ldb_val nmd_value;
1064 struct ldb_dn *extended_dn = NULL;
1067 * The use of a time_t here seems odd, but as the NTTIME
1068 * elements are actually declared as NTTIME_1sec in the IDL,
1069 * getting a higher resolution timestamp is not required.
1071 time_t t = time(NULL);
1072 NTTIME now;
1073 char *time_str;
1074 int ret;
1075 unsigned int i;
1076 unsigned int functional_level;
1077 uint32_t ni=0;
1078 bool allow_add_guid = false;
1079 bool remove_current_guid = false;
1080 bool is_urgent = false;
1081 bool is_schema_nc = false;
1082 struct ldb_message_element *objectclass_el;
1083 struct replmd_private *replmd_private =
1084 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
1086 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
1087 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
1088 if (control) {
1089 allow_add_guid = true;
1092 /* do not manipulate our control entries */
1093 if (ldb_dn_is_special(req->op.add.message->dn)) {
1094 return ldb_next_request(module, req);
1097 ldb = ldb_module_get_ctx(module);
1099 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
1101 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
1102 if (guid_blob != NULL) {
1103 if (!allow_add_guid) {
1104 ldb_set_errstring(ldb,
1105 "replmd_add: it's not allowed to add an object with objectGUID!");
1106 return LDB_ERR_UNWILLING_TO_PERFORM;
1107 } else {
1108 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
1109 if (!NT_STATUS_IS_OK(status)) {
1110 ldb_set_errstring(ldb,
1111 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1112 return LDB_ERR_UNWILLING_TO_PERFORM;
1114 /* we remove this attribute as it can be a string and
1115 * will not be treated correctly and then we will re-add
1116 * it later on in the good format */
1117 remove_current_guid = true;
1119 } else {
1120 /* a new GUID */
1121 guid = GUID_random();
1123 guid_blob_stack = data_blob_const(guid_data, sizeof(guid_data));
1125 /* This can't fail */
1126 ndr_push_struct_into_fixed_blob(&guid_blob_stack, &guid,
1127 (ndr_push_flags_fn_t)ndr_push_GUID);
1128 guid_blob = &guid_blob_stack;
1131 ac = replmd_ctx_init(module, req);
1132 if (ac == NULL) {
1133 return ldb_module_oom(module);
1136 functional_level = dsdb_functional_level(ldb);
1138 /* Get a sequence number from the backend */
1139 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
1140 if (ret != LDB_SUCCESS) {
1141 talloc_free(ac);
1142 return ret;
1145 /* we have to copy the message as the caller might have it as a const */
1146 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
1147 if (msg == NULL) {
1148 ldb_oom(ldb);
1149 talloc_free(ac);
1150 return LDB_ERR_OPERATIONS_ERROR;
1153 /* generated times */
1154 unix_to_nt_time(&now, t);
1155 time_str = ldb_timestring(msg, t);
1156 if (!time_str) {
1157 ldb_oom(ldb);
1158 talloc_free(ac);
1159 return LDB_ERR_OPERATIONS_ERROR;
1161 if (remove_current_guid) {
1162 ldb_msg_remove_attr(msg,"objectGUID");
1166 * remove autogenerated attributes
1168 ldb_msg_remove_attr(msg, "whenCreated");
1169 ldb_msg_remove_attr(msg, "whenChanged");
1170 ldb_msg_remove_attr(msg, "uSNCreated");
1171 ldb_msg_remove_attr(msg, "uSNChanged");
1172 ldb_msg_remove_attr(msg, "replPropertyMetaData");
1175 * readd replicated attributes
1177 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1178 if (ret != LDB_SUCCESS) {
1179 ldb_oom(ldb);
1180 talloc_free(ac);
1181 return ret;
1184 /* build the replication meta_data */
1185 ZERO_STRUCT(nmd);
1186 nmd.version = 1;
1187 nmd.ctr.ctr1.count = msg->num_elements;
1188 nmd.ctr.ctr1.array = talloc_array(msg,
1189 struct replPropertyMetaData1,
1190 nmd.ctr.ctr1.count);
1191 if (!nmd.ctr.ctr1.array) {
1192 ldb_oom(ldb);
1193 talloc_free(ac);
1194 return LDB_ERR_OPERATIONS_ERROR;
1197 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1199 for (i=0; i < msg->num_elements;) {
1200 struct ldb_message_element *e = &msg->elements[i];
1201 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1202 const struct dsdb_attribute *sa;
1204 if (e->name[0] == '@') {
1205 i++;
1206 continue;
1209 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1210 if (!sa) {
1211 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1212 "replmd_add: attribute '%s' not defined in schema\n",
1213 e->name);
1214 talloc_free(ac);
1215 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1218 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1219 /* if the attribute is not replicated (0x00000001)
1220 * or constructed (0x00000004) it has no metadata
1222 i++;
1223 continue;
1226 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1227 if (extended_dn == NULL) {
1228 ret = replmd_add_make_extended_dn(req,
1229 guid_blob,
1230 &extended_dn);
1231 if (ret != LDB_SUCCESS) {
1232 talloc_free(ac);
1233 return ret;
1238 * Prepare the context for the backlinks and
1239 * create metadata for the forward links. The
1240 * backlinks are created in
1241 * replmd_op_callback() after the successful
1242 * ADD of the object.
1244 ret = replmd_add_fix_la(module, msg->elements,
1245 replmd_private, e,
1246 ac, now,
1247 extended_dn,
1248 sa, req);
1249 if (ret != LDB_SUCCESS) {
1250 talloc_free(ac);
1251 return ret;
1253 /* linked attributes are not stored in
1254 replPropertyMetaData in FL above w2k */
1255 i++;
1256 continue;
1259 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1260 m->version = 1;
1261 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1262 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1263 const char* rdn;
1265 if (rdn_val == NULL) {
1266 ldb_oom(ldb);
1267 talloc_free(ac);
1268 return LDB_ERR_OPERATIONS_ERROR;
1271 rdn = (const char*)rdn_val->data;
1272 if (strcmp(rdn, "Deleted Objects") == 0) {
1274 * Set the originating_change_time to 29/12/9999 at 23:59:59
1275 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1277 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1278 } else {
1279 m->originating_change_time = now;
1281 } else {
1282 m->originating_change_time = now;
1284 m->originating_invocation_id = ac->our_invocation_id;
1285 m->originating_usn = ac->seq_num;
1286 m->local_usn = ac->seq_num;
1287 ni++;
1289 if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1290 i++;
1291 continue;
1294 e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1296 if (e->num_values != 0) {
1297 i++;
1298 continue;
1301 ldb_msg_remove_element(msg, e);
1304 /* fix meta data count */
1305 nmd.ctr.ctr1.count = ni;
1308 * sort meta data array
1310 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1311 if (ret != LDB_SUCCESS) {
1312 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1313 talloc_free(ac);
1314 return ret;
1317 /* generated NDR encoded values */
1318 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1319 &nmd,
1320 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1321 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1322 ldb_oom(ldb);
1323 talloc_free(ac);
1324 return LDB_ERR_OPERATIONS_ERROR;
1328 * add the autogenerated values
1330 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1331 if (ret != LDB_SUCCESS) {
1332 ldb_oom(ldb);
1333 talloc_free(ac);
1334 return ret;
1336 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1337 if (ret != LDB_SUCCESS) {
1338 ldb_oom(ldb);
1339 talloc_free(ac);
1340 return ret;
1342 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1343 if (ret != LDB_SUCCESS) {
1344 ldb_oom(ldb);
1345 talloc_free(ac);
1346 return ret;
1348 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1349 if (ret != LDB_SUCCESS) {
1350 ldb_oom(ldb);
1351 talloc_free(ac);
1352 return ret;
1354 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1355 if (ret != LDB_SUCCESS) {
1356 ldb_oom(ldb);
1357 talloc_free(ac);
1358 return ret;
1362 * sort the attributes by attid before storing the object
1364 replmd_ldb_message_sort(msg, ac->schema);
1367 * Assert that we do have an objectClass
1369 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1370 if (objectclass_el == NULL) {
1371 ldb_asprintf_errstring(ldb, __location__
1372 ": objectClass missing on %s\n",
1373 ldb_dn_get_linearized(msg->dn));
1374 talloc_free(ac);
1375 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1377 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1378 REPL_URGENT_ON_CREATE);
1380 ac->is_urgent = is_urgent;
1381 ret = ldb_build_add_req(&down_req, ldb, ac,
1382 msg,
1383 req->controls,
1384 ac, replmd_op_callback,
1385 req);
1387 LDB_REQ_SET_LOCATION(down_req);
1388 if (ret != LDB_SUCCESS) {
1389 talloc_free(ac);
1390 return ret;
1393 /* current partition control is needed by "replmd_op_callback" */
1394 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1395 ret = ldb_request_add_control(down_req,
1396 DSDB_CONTROL_CURRENT_PARTITION_OID,
1397 false, NULL);
1398 if (ret != LDB_SUCCESS) {
1399 talloc_free(ac);
1400 return ret;
1404 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1405 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1406 if (ret != LDB_SUCCESS) {
1407 talloc_free(ac);
1408 return ret;
1412 /* mark the control done */
1413 if (control) {
1414 control->critical = 0;
1416 /* go on with the call chain */
1417 return ldb_next_request(module, down_req);
1422 * update the replPropertyMetaData for one element
1424 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1425 struct ldb_message *msg,
1426 struct ldb_message_element *el,
1427 struct ldb_message_element *old_el,
1428 struct replPropertyMetaDataBlob *omd,
1429 const struct dsdb_schema *schema,
1430 uint64_t *seq_num,
1431 const struct GUID *our_invocation_id,
1432 NTTIME now,
1433 bool is_schema_nc,
1434 bool is_forced_rodc,
1435 struct ldb_request *req)
1437 uint32_t i;
1438 const struct dsdb_attribute *a;
1439 struct replPropertyMetaData1 *md1;
1440 bool may_skip = false;
1441 uint32_t attid;
1443 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1444 if (a == NULL) {
1445 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1446 /* allow this to make it possible for dbcheck
1447 to remove bad attributes */
1448 return LDB_SUCCESS;
1451 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1452 el->name));
1453 return LDB_ERR_OPERATIONS_ERROR;
1456 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1458 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1459 return LDB_SUCCESS;
1463 * if the attribute's value haven't changed, and this isn't
1464 * just a delete of everything then return LDB_SUCCESS Unless
1465 * we have the provision control or if the attribute is
1466 * interSiteTopologyGenerator as this page explain:
1467 * http://support.microsoft.com/kb/224815 this attribute is
1468 * periodicaly written by the DC responsible for the intersite
1469 * generation in a given site
1471 * Unchanged could be deleting or replacing an already-gone
1472 * thing with an unconstrained delete/empty replace or a
1473 * replace with the same value, but not an add with the same
1474 * value because that could be about adding a duplicate (which
1475 * is for someone else to error out on).
1477 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1478 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1479 may_skip = true;
1481 } else if (old_el == NULL && el->num_values == 0) {
1482 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1483 may_skip = true;
1484 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1485 may_skip = true;
1487 } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1488 ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
1490 * We intentionally skip the version bump when attempting to
1491 * vanish links.
1493 * The control is set by dbcheck and expunge-tombstones which
1494 * both attempt to be non-replicating. Otherwise, making an
1495 * alteration to the replication state would trigger a
1496 * broadcast of all expunged objects.
1498 may_skip = true;
1501 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1502 may_skip = false;
1503 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1506 if (may_skip) {
1507 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1508 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1510 * allow this to make it possible for dbcheck
1511 * to rebuild broken metadata
1513 return LDB_SUCCESS;
1517 for (i=0; i<omd->ctr.ctr1.count; i++) {
1519 * First check if we find it under the msDS-IntID,
1520 * then check if we find it under the OID and
1521 * prefixMap ID.
1523 * This allows the administrator to simply re-write
1524 * the attributes and so restore replication, which is
1525 * likely what they will try to do.
1527 if (attid == omd->ctr.ctr1.array[i].attid) {
1528 break;
1531 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1532 break;
1536 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1537 /* linked attributes are not stored in
1538 replPropertyMetaData in FL above w2k, but we do
1539 raise the seqnum for the object */
1540 if (*seq_num == 0 &&
1541 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1542 return LDB_ERR_OPERATIONS_ERROR;
1544 return LDB_SUCCESS;
1547 if (i == omd->ctr.ctr1.count) {
1548 /* we need to add a new one */
1549 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1550 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1551 if (omd->ctr.ctr1.array == NULL) {
1552 ldb_oom(ldb);
1553 return LDB_ERR_OPERATIONS_ERROR;
1555 omd->ctr.ctr1.count++;
1556 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1559 /* Get a new sequence number from the backend. We only do this
1560 * if we have a change that requires a new
1561 * replPropertyMetaData element
1563 if (*seq_num == 0) {
1564 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1565 if (ret != LDB_SUCCESS) {
1566 return LDB_ERR_OPERATIONS_ERROR;
1570 md1 = &omd->ctr.ctr1.array[i];
1571 md1->version++;
1572 md1->attid = attid;
1574 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1575 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1576 const char* rdn;
1578 if (rdn_val == NULL) {
1579 ldb_oom(ldb);
1580 return LDB_ERR_OPERATIONS_ERROR;
1583 rdn = (const char*)rdn_val->data;
1584 if (strcmp(rdn, "Deleted Objects") == 0) {
1586 * Set the originating_change_time to 29/12/9999 at 23:59:59
1587 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1589 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1590 } else {
1591 md1->originating_change_time = now;
1593 } else {
1594 md1->originating_change_time = now;
1596 md1->originating_invocation_id = *our_invocation_id;
1597 md1->originating_usn = *seq_num;
1598 md1->local_usn = *seq_num;
1600 if (is_forced_rodc) {
1601 /* Force version to 0 to be overriden later via replication */
1602 md1->version = 0;
1605 return LDB_SUCCESS;
1609 * Bump the replPropertyMetaData version on an attribute, and if it
1610 * has changed (or forced by leaving rdn_old NULL), update the value
1611 * in the entry.
1613 * This is important, as calling a modify operation may not change the
1614 * version number if the values appear unchanged, but a rename between
1615 * parents bumps this value.
1618 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1619 struct ldb_message *msg,
1620 const struct ldb_val *rdn_new,
1621 const struct ldb_val *rdn_old,
1622 struct replPropertyMetaDataBlob *omd,
1623 struct replmd_replicated_request *ar,
1624 NTTIME now,
1625 bool is_schema_nc,
1626 bool is_forced_rodc)
1628 const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
1629 const struct dsdb_attribute *rdn_attr =
1630 dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
1631 const char *attr_name = rdn_attr != NULL ?
1632 rdn_attr->lDAPDisplayName :
1633 rdn_name;
1634 struct ldb_message_element new_el = {
1635 .flags = LDB_FLAG_MOD_REPLACE,
1636 .name = attr_name,
1637 .num_values = 1,
1638 .values = discard_const_p(struct ldb_val, rdn_new)
1640 struct ldb_message_element old_el = {
1641 .flags = LDB_FLAG_MOD_REPLACE,
1642 .name = attr_name,
1643 .num_values = rdn_old ? 1 : 0,
1644 .values = discard_const_p(struct ldb_val, rdn_old)
1647 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1648 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1649 if (ret != LDB_SUCCESS) {
1650 return ldb_oom(ldb);
1654 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1655 omd, ar->schema, &ar->seq_num,
1656 &ar->our_invocation_id,
1657 now, is_schema_nc, is_forced_rodc,
1658 ar->req);
1662 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1664 uint32_t count = omd.ctr.ctr1.count;
1665 uint64_t max = 0;
1666 uint32_t i;
1667 for (i=0; i < count; i++) {
1668 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1669 if (max < m.local_usn) {
1670 max = m.local_usn;
1673 return max;
1677 * update the replPropertyMetaData object each time we modify an
1678 * object. This is needed for DRS replication, as the merge on the
1679 * client is based on this object
1681 static int replmd_update_rpmd(struct ldb_module *module,
1682 const struct dsdb_schema *schema,
1683 struct ldb_request *req,
1684 const char * const *rename_attrs,
1685 struct ldb_message *msg, uint64_t *seq_num,
1686 time_t t, bool is_schema_nc,
1687 bool *is_urgent, bool *rodc)
1689 const struct ldb_val *omd_value;
1690 enum ndr_err_code ndr_err;
1691 struct replPropertyMetaDataBlob omd;
1692 unsigned int i;
1693 NTTIME now;
1694 const struct GUID *our_invocation_id;
1695 int ret;
1696 const char * const *attrs = NULL;
1697 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1698 struct ldb_result *res;
1699 struct ldb_context *ldb;
1700 struct ldb_message_element *objectclass_el;
1701 enum urgent_situation situation;
1702 bool rmd_is_provided;
1703 bool rmd_is_just_resorted = false;
1704 const char *not_rename_attrs[4 + msg->num_elements];
1705 bool is_forced_rodc = false;
1707 if (rename_attrs) {
1708 attrs = rename_attrs;
1709 } else {
1710 for (i = 0; i < msg->num_elements; i++) {
1711 not_rename_attrs[i] = msg->elements[i].name;
1713 not_rename_attrs[i] = "replPropertyMetaData";
1714 not_rename_attrs[i+1] = "objectClass";
1715 not_rename_attrs[i+2] = "instanceType";
1716 not_rename_attrs[i+3] = NULL;
1717 attrs = not_rename_attrs;
1720 ldb = ldb_module_get_ctx(module);
1722 ret = samdb_rodc(ldb, rodc);
1723 if (ret != LDB_SUCCESS) {
1724 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1725 *rodc = false;
1728 if (*rodc &&
1729 ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
1730 is_forced_rodc = true;
1733 our_invocation_id = samdb_ntds_invocation_id(ldb);
1734 if (!our_invocation_id) {
1735 /* this happens during an initial vampire while
1736 updating the schema */
1737 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1738 return LDB_SUCCESS;
1741 unix_to_nt_time(&now, t);
1743 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1744 rmd_is_provided = true;
1745 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1746 rmd_is_just_resorted = true;
1748 } else {
1749 rmd_is_provided = false;
1752 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1753 * otherwise we consider we are updating */
1754 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1755 situation = REPL_URGENT_ON_DELETE;
1756 } else if (rename_attrs) {
1757 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1758 } else {
1759 situation = REPL_URGENT_ON_UPDATE;
1762 if (rmd_is_provided) {
1763 /* In this case the change_replmetadata control was supplied */
1764 /* We check that it's the only attribute that is provided
1765 * (it's a rare case so it's better to keep the code simplier)
1766 * We also check that the highest local_usn is bigger or the same as
1767 * uSNChanged. */
1768 uint64_t db_seq;
1769 if( msg->num_elements != 1 ||
1770 strncmp(msg->elements[0].name,
1771 "replPropertyMetaData", 20) ) {
1772 DEBUG(0,(__location__ ": changereplmetada control called without "\
1773 "a specified replPropertyMetaData attribute or with others\n"));
1774 return LDB_ERR_OPERATIONS_ERROR;
1776 if (situation != REPL_URGENT_ON_UPDATE) {
1777 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1778 return LDB_ERR_OPERATIONS_ERROR;
1780 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1781 if (!omd_value) {
1782 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1783 ldb_dn_get_linearized(msg->dn)));
1784 return LDB_ERR_OPERATIONS_ERROR;
1786 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1787 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1788 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1789 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1790 ldb_dn_get_linearized(msg->dn)));
1791 return LDB_ERR_OPERATIONS_ERROR;
1794 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1795 DSDB_FLAG_NEXT_MODULE |
1796 DSDB_SEARCH_SHOW_RECYCLED |
1797 DSDB_SEARCH_SHOW_EXTENDED_DN |
1798 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1799 DSDB_SEARCH_REVEAL_INTERNALS, req);
1801 if (ret != LDB_SUCCESS) {
1802 return ret;
1805 if (rmd_is_just_resorted == false) {
1806 *seq_num = find_max_local_usn(omd);
1808 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1811 * The test here now allows for a new
1812 * replPropertyMetaData with no change, if was
1813 * just dbcheck re-sorting the values.
1815 if (*seq_num <= db_seq) {
1816 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1817 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1818 (long long)*seq_num, (long long)db_seq));
1819 return LDB_ERR_OPERATIONS_ERROR;
1823 } else {
1824 /* search for the existing replPropertyMetaDataBlob. We need
1825 * to use REVEAL and ask for DNs in storage format to support
1826 * the check for values being the same in
1827 * replmd_update_rpmd_element()
1829 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1830 DSDB_FLAG_NEXT_MODULE |
1831 DSDB_SEARCH_SHOW_RECYCLED |
1832 DSDB_SEARCH_SHOW_EXTENDED_DN |
1833 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1834 DSDB_SEARCH_REVEAL_INTERNALS, req);
1835 if (ret != LDB_SUCCESS) {
1836 return ret;
1839 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1840 if (!omd_value) {
1841 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1842 ldb_dn_get_linearized(msg->dn)));
1843 return LDB_ERR_OPERATIONS_ERROR;
1846 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1847 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1848 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1849 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1850 ldb_dn_get_linearized(msg->dn)));
1851 return LDB_ERR_OPERATIONS_ERROR;
1854 if (omd.version != 1) {
1855 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1856 omd.version, ldb_dn_get_linearized(msg->dn)));
1857 return LDB_ERR_OPERATIONS_ERROR;
1860 for (i=0; i<msg->num_elements;) {
1861 struct ldb_message_element *el = &msg->elements[i];
1862 struct ldb_message_element *old_el;
1864 old_el = ldb_msg_find_element(res->msgs[0], el->name);
1865 ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1866 &omd, schema, seq_num,
1867 our_invocation_id,
1868 now, is_schema_nc,
1869 is_forced_rodc,
1870 req);
1871 if (ret != LDB_SUCCESS) {
1872 return ret;
1875 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1876 *is_urgent = replmd_check_urgent_attribute(el);
1879 if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1880 i++;
1881 continue;
1884 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1886 if (el->num_values != 0) {
1887 i++;
1888 continue;
1891 ldb_msg_remove_element(msg, el);
1896 * Assert that we have an objectClass attribute - this is major
1897 * corruption if we don't have this!
1899 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1900 if (objectclass_el != NULL) {
1902 * Now check if this objectClass means we need to do urgent replication
1904 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1905 situation)) {
1906 *is_urgent = true;
1908 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1909 ldb_asprintf_errstring(ldb, __location__
1910 ": objectClass missing on %s\n",
1911 ldb_dn_get_linearized(msg->dn));
1912 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1916 * replmd_update_rpmd_element has done an update if the
1917 * seq_num is set
1919 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1920 struct ldb_val *md_value;
1921 struct ldb_message_element *el;
1923 /*if we are RODC and this is a DRSR update then its ok*/
1924 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1925 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
1926 && !is_forced_rodc) {
1927 unsigned instanceType;
1929 if (*rodc) {
1930 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1931 return LDB_ERR_REFERRAL;
1934 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1935 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1936 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1937 "cannot change replicated attribute on partial replica");
1941 md_value = talloc(msg, struct ldb_val);
1942 if (md_value == NULL) {
1943 ldb_oom(ldb);
1944 return LDB_ERR_OPERATIONS_ERROR;
1947 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
1948 if (ret != LDB_SUCCESS) {
1949 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
1950 return ret;
1953 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1954 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1955 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1956 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1957 ldb_dn_get_linearized(msg->dn)));
1958 return LDB_ERR_OPERATIONS_ERROR;
1961 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1962 if (ret != LDB_SUCCESS) {
1963 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1964 ldb_dn_get_linearized(msg->dn)));
1965 return ret;
1968 el->num_values = 1;
1969 el->values = md_value;
1972 return LDB_SUCCESS;
1975 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1977 int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
1978 if (ret == 0) {
1979 return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
1980 &pdn2->dsdb_dn->extra_part);
1982 return ret;
1986 get a series of message element values as an array of DNs and GUIDs
1987 the result is sorted by GUID
1989 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1990 struct ldb_message_element *el, struct parsed_dn **pdn,
1991 const char *ldap_oid, struct ldb_request *parent)
1993 unsigned int i;
1994 bool values_are_sorted = true;
1995 struct ldb_context *ldb = ldb_module_get_ctx(module);
1997 if (el == NULL) {
1998 *pdn = NULL;
1999 return LDB_SUCCESS;
2002 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
2003 if (!*pdn) {
2004 ldb_module_oom(module);
2005 return LDB_ERR_OPERATIONS_ERROR;
2008 for (i=0; i<el->num_values; i++) {
2009 struct ldb_val *v = &el->values[i];
2010 NTSTATUS status;
2011 struct ldb_dn *dn;
2012 struct parsed_dn *p;
2014 p = &(*pdn)[i];
2016 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
2017 if (p->dsdb_dn == NULL) {
2018 return LDB_ERR_INVALID_DN_SYNTAX;
2021 dn = p->dsdb_dn->dn;
2023 status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
2024 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
2025 unlikely(GUID_all_zero(&p->guid))) {
2026 /* we got a DN without a GUID - go find the GUID */
2027 int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
2028 if (ret != LDB_SUCCESS) {
2029 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
2030 ldb_dn_get_linearized(dn));
2031 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
2032 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
2033 ldb_attr_cmp(el->name, "member") == 0) {
2034 return LDB_ERR_UNWILLING_TO_PERFORM;
2036 return ret;
2038 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2039 if (ret != LDB_SUCCESS) {
2040 return ret;
2042 } else if (!NT_STATUS_IS_OK(status)) {
2043 return LDB_ERR_OPERATIONS_ERROR;
2045 if (i > 0 && values_are_sorted) {
2046 int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
2047 if (cmp < 0) {
2048 values_are_sorted = false;
2051 /* keep a pointer to the original ldb_val */
2052 p->v = v;
2054 if (! values_are_sorted) {
2055 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
2057 return LDB_SUCCESS;
2061 * Get a series of trusted message element values. The result is sorted by
2062 * GUID, even though the GUIDs might not be known. That works because we trust
2063 * the database to give us the elements like that if the
2064 * replmd_private->sorted_links flag is set.
2066 * We also ensure that the links are in the Functional Level 2003
2067 * linked attributes format.
2069 static int get_parsed_dns_trusted(struct ldb_module *module,
2070 struct replmd_private *replmd_private,
2071 TALLOC_CTX *mem_ctx,
2072 struct ldb_message_element *el,
2073 struct parsed_dn **pdn,
2074 const char *ldap_oid,
2075 struct ldb_request *parent)
2077 unsigned int i;
2078 int ret;
2079 if (el == NULL) {
2080 *pdn = NULL;
2081 return LDB_SUCCESS;
2084 if (!replmd_private->sorted_links) {
2085 /* We need to sort the list. This is the slow old path we want
2086 to avoid.
2088 ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2089 parent);
2090 if (ret != LDB_SUCCESS) {
2091 return ret;
2093 } else {
2094 /* Here we get a list of 'struct parsed_dns' without the parsing */
2095 *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
2096 el->num_values);
2097 if (!*pdn) {
2098 ldb_module_oom(module);
2099 return LDB_ERR_OPERATIONS_ERROR;
2102 for (i = 0; i < el->num_values; i++) {
2103 (*pdn)[i].v = &el->values[i];
2108 * This upgrades links to FL2003 style, and sorts the result
2109 * if that was needed.
2111 * TODO: Add a database feature that asserts we have no FL2000
2112 * style links to avoid this check or add a feature that
2113 * uses a similar check to find sorted/unsorted links
2114 * for an on-the-fly upgrade.
2117 ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
2118 *pdn, el->num_values,
2120 ldap_oid);
2121 if (ret != LDB_SUCCESS) {
2122 return ret;
2125 return LDB_SUCCESS;
2129 build a new extended DN, including all meta data fields
2131 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2132 RMD_ADDTIME = originating_add_time
2133 RMD_INVOCID = originating_invocation_id
2134 RMD_CHANGETIME = originating_change_time
2135 RMD_ORIGINATING_USN = originating_usn
2136 RMD_LOCAL_USN = local_usn
2137 RMD_VERSION = version
2139 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2140 const struct GUID *invocation_id, uint64_t seq_num,
2141 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
2143 struct ldb_dn *dn = dsdb_dn->dn;
2144 const char *tstring, *usn_string, *flags_string;
2145 struct ldb_val tval;
2146 struct ldb_val iid;
2147 struct ldb_val usnv, local_usnv;
2148 struct ldb_val vers, flagsv;
2149 NTSTATUS status;
2150 int ret;
2151 const char *dnstring;
2152 char *vstring;
2153 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2155 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2156 if (!tstring) {
2157 return LDB_ERR_OPERATIONS_ERROR;
2159 tval = data_blob_string_const(tstring);
2161 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
2162 if (!usn_string) {
2163 return LDB_ERR_OPERATIONS_ERROR;
2165 usnv = data_blob_string_const(usn_string);
2167 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2168 if (!usn_string) {
2169 return LDB_ERR_OPERATIONS_ERROR;
2171 local_usnv = data_blob_string_const(usn_string);
2173 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2174 if (!vstring) {
2175 return LDB_ERR_OPERATIONS_ERROR;
2177 vers = data_blob_string_const(vstring);
2179 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2180 if (!NT_STATUS_IS_OK(status)) {
2181 return LDB_ERR_OPERATIONS_ERROR;
2184 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2185 if (!flags_string) {
2186 return LDB_ERR_OPERATIONS_ERROR;
2188 flagsv = data_blob_string_const(flags_string);
2190 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2191 if (ret != LDB_SUCCESS) return ret;
2192 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
2193 if (ret != LDB_SUCCESS) return ret;
2194 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2195 if (ret != LDB_SUCCESS) return ret;
2196 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2197 if (ret != LDB_SUCCESS) return ret;
2198 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2199 if (ret != LDB_SUCCESS) return ret;
2200 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2201 if (ret != LDB_SUCCESS) return ret;
2202 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2203 if (ret != LDB_SUCCESS) return ret;
2205 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2206 if (dnstring == NULL) {
2207 return LDB_ERR_OPERATIONS_ERROR;
2209 *v = data_blob_string_const(dnstring);
2211 return LDB_SUCCESS;
2214 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2215 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2216 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
2217 bool deleted);
2220 check if any links need upgrading from w2k format
2222 static int replmd_check_upgrade_links(struct ldb_context *ldb,
2223 struct parsed_dn *dns, uint32_t count,
2224 struct ldb_message_element *el,
2225 const char *ldap_oid)
2227 uint32_t i;
2228 const struct GUID *invocation_id = NULL;
2229 for (i=0; i<count; i++) {
2230 NTSTATUS status;
2231 uint32_t version;
2232 int ret;
2233 if (dns[i].dsdb_dn == NULL) {
2234 ret = really_parse_trusted_dn(dns, ldb, &dns[i],
2235 ldap_oid);
2236 if (ret != LDB_SUCCESS) {
2237 return LDB_ERR_INVALID_DN_SYNTAX;
2241 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
2242 &version, "RMD_VERSION");
2243 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2245 * We optimistically assume they are all the same; if
2246 * the first one is fixed, they are all fixed.
2248 * If the first one was *not* fixed and we find a
2249 * later one that is, that is an occasion to shout
2250 * with DEBUG(0).
2252 if (i == 0) {
2253 return LDB_SUCCESS;
2255 DEBUG(0, ("Mixed w2k and fixed format "
2256 "linked attributes\n"));
2257 continue;
2260 if (invocation_id == NULL) {
2261 invocation_id = samdb_ntds_invocation_id(ldb);
2262 if (invocation_id == NULL) {
2263 return LDB_ERR_OPERATIONS_ERROR;
2268 /* it's an old one that needs upgrading */
2269 ret = replmd_update_la_val(el->values, dns[i].v,
2270 dns[i].dsdb_dn, dns[i].dsdb_dn,
2271 invocation_id, 1, 1, 0, false);
2272 if (ret != LDB_SUCCESS) {
2273 return ret;
2278 * This sort() is critical for the operation of
2279 * get_parsed_dns_trusted() because callers of this function
2280 * expect a sorted list, and FL2000 style links are not
2281 * sorted. In particular, as well as the upgrade case,
2282 * get_parsed_dns_trusted() is called from
2283 * replmd_delete_remove_link() even in FL2000 mode
2285 * We do not normally pay the cost of the qsort() due to the
2286 * early return in the RMD_VERSION found case.
2288 TYPESAFE_QSORT(dns, count, parsed_dn_compare);
2289 return LDB_SUCCESS;
2293 Sets the value for a linked attribute, including all meta data fields
2295 see replmd_build_la_val for value names
2297 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2298 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2299 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2300 uint32_t version, bool deleted)
2302 struct ldb_dn *dn = dsdb_dn->dn;
2303 const char *tstring, *usn_string, *flags_string;
2304 struct ldb_val tval;
2305 struct ldb_val iid;
2306 struct ldb_val usnv, local_usnv;
2307 struct ldb_val vers, flagsv;
2308 const struct ldb_val *old_addtime = NULL;
2309 NTSTATUS status;
2310 int ret;
2311 const char *dnstring;
2312 char *vstring;
2313 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2315 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2316 if (!tstring) {
2317 return LDB_ERR_OPERATIONS_ERROR;
2319 tval = data_blob_string_const(tstring);
2321 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2322 if (!usn_string) {
2323 return LDB_ERR_OPERATIONS_ERROR;
2325 usnv = data_blob_string_const(usn_string);
2327 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2328 if (!usn_string) {
2329 return LDB_ERR_OPERATIONS_ERROR;
2331 local_usnv = data_blob_string_const(usn_string);
2333 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2334 if (!NT_STATUS_IS_OK(status)) {
2335 return LDB_ERR_OPERATIONS_ERROR;
2338 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2339 if (!flags_string) {
2340 return LDB_ERR_OPERATIONS_ERROR;
2342 flagsv = data_blob_string_const(flags_string);
2344 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2345 if (ret != LDB_SUCCESS) return ret;
2347 /* get the ADDTIME from the original */
2348 if (old_dsdb_dn != NULL) {
2349 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn,
2350 "RMD_ADDTIME");
2352 if (old_addtime == NULL) {
2353 old_addtime = &tval;
2355 if (dsdb_dn != old_dsdb_dn ||
2356 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2357 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2358 if (ret != LDB_SUCCESS) return ret;
2361 /* use our invocation id */
2362 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2363 if (ret != LDB_SUCCESS) return ret;
2365 /* changetime is the current time */
2366 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2367 if (ret != LDB_SUCCESS) return ret;
2369 /* update the USN */
2370 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2371 if (ret != LDB_SUCCESS) return ret;
2373 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2374 if (ret != LDB_SUCCESS) return ret;
2376 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2377 vers = data_blob_string_const(vstring);
2378 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2379 if (ret != LDB_SUCCESS) return ret;
2381 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2382 if (dnstring == NULL) {
2383 return LDB_ERR_OPERATIONS_ERROR;
2385 *v = data_blob_string_const(dnstring);
2387 return LDB_SUCCESS;
2391 * Updates the value for a linked attribute, including all meta data fields
2393 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2394 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2395 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2396 bool deleted)
2398 uint32_t old_version;
2399 uint32_t version = 0;
2400 NTSTATUS status;
2403 * We're updating the linked attribute locally, so increase the version
2404 * by 1 so that other DCs will see the change when it gets replicated out
2406 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version,
2407 "RMD_VERSION");
2409 if (NT_STATUS_IS_OK(status)) {
2410 version = old_version + 1;
2413 return replmd_set_la_val(mem_ctx, v, dsdb_dn, old_dsdb_dn, invocation_id,
2414 usn, local_usn, nttime, version, deleted);
2418 handle adding a linked attribute
2420 static int replmd_modify_la_add(struct ldb_module *module,
2421 struct replmd_private *replmd_private,
2422 const struct dsdb_schema *schema,
2423 struct ldb_message *msg,
2424 struct ldb_message_element *el,
2425 struct ldb_message_element *old_el,
2426 const struct dsdb_attribute *schema_attr,
2427 uint64_t seq_num,
2428 time_t t,
2429 struct ldb_dn *msg_dn,
2430 struct ldb_request *parent)
2432 unsigned int i, j;
2433 struct parsed_dn *dns, *old_dns;
2434 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2435 int ret;
2436 struct ldb_val *new_values = NULL;
2437 unsigned old_num_values = old_el ? old_el->num_values : 0;
2438 unsigned num_values = 0;
2439 unsigned max_num_values;
2440 const struct GUID *invocation_id;
2441 struct ldb_context *ldb = ldb_module_get_ctx(module);
2442 NTTIME now;
2443 unix_to_nt_time(&now, t);
2445 invocation_id = samdb_ntds_invocation_id(ldb);
2446 if (!invocation_id) {
2447 talloc_free(tmp_ctx);
2448 return LDB_ERR_OPERATIONS_ERROR;
2451 /* get the DNs to be added, fully parsed.
2453 * We need full parsing because they came off the wire and we don't
2454 * trust them, besides which we need their details to know where to put
2455 * them.
2457 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2458 schema_attr->syntax->ldap_oid, parent);
2459 if (ret != LDB_SUCCESS) {
2460 talloc_free(tmp_ctx);
2461 return ret;
2464 /* get the existing DNs, lazily parsed */
2465 ret = get_parsed_dns_trusted(module, replmd_private,
2466 tmp_ctx, old_el, &old_dns,
2467 schema_attr->syntax->ldap_oid, parent);
2469 if (ret != LDB_SUCCESS) {
2470 talloc_free(tmp_ctx);
2471 return ret;
2474 max_num_values = old_num_values + el->num_values;
2475 if (max_num_values < old_num_values) {
2476 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2477 "old values: %u, new values: %u, sum: %u",
2478 old_num_values, el->num_values, max_num_values));
2479 talloc_free(tmp_ctx);
2480 return LDB_ERR_OPERATIONS_ERROR;
2483 new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2485 if (new_values == NULL) {
2486 ldb_module_oom(module);
2487 talloc_free(tmp_ctx);
2488 return LDB_ERR_OPERATIONS_ERROR;
2492 * For each new value, find where it would go in the list. If there is
2493 * a matching GUID there, we update the existing value; otherwise we
2494 * put it in place.
2496 j = 0;
2497 for (i = 0; i < el->num_values; i++) {
2498 struct parsed_dn *exact;
2499 struct parsed_dn *next;
2500 unsigned offset;
2501 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2502 &dns[i].guid,
2503 dns[i].dsdb_dn->dn,
2504 dns[i].dsdb_dn->extra_part, 0,
2505 &exact, &next,
2506 schema_attr->syntax->ldap_oid,
2507 true);
2508 if (err != LDB_SUCCESS) {
2509 talloc_free(tmp_ctx);
2510 return err;
2513 if (exact != NULL) {
2515 * We are trying to add one that exists, which is only
2516 * allowed if it was previously deleted.
2518 * When we do undelete a link we change it in place.
2519 * It will be copied across into the right spot in due
2520 * course.
2522 uint32_t rmd_flags;
2523 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2525 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2526 struct GUID_txt_buf guid_str;
2527 ldb_asprintf_errstring(ldb,
2528 "Attribute %s already "
2529 "exists for target GUID %s",
2530 el->name,
2531 GUID_buf_string(&exact->guid,
2532 &guid_str));
2533 talloc_free(tmp_ctx);
2534 /* error codes for 'member' need to be
2535 special cased */
2536 if (ldb_attr_cmp(el->name, "member") == 0) {
2537 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2538 } else {
2539 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2543 ret = replmd_update_la_val(new_values, exact->v,
2544 dns[i].dsdb_dn,
2545 exact->dsdb_dn,
2546 invocation_id, seq_num,
2547 seq_num, now, false);
2548 if (ret != LDB_SUCCESS) {
2549 talloc_free(tmp_ctx);
2550 return ret;
2553 ret = replmd_add_backlink(module, replmd_private,
2554 schema,
2555 msg_dn,
2556 &dns[i].guid,
2557 true,
2558 schema_attr,
2559 parent);
2560 if (ret != LDB_SUCCESS) {
2561 talloc_free(tmp_ctx);
2562 return ret;
2564 continue;
2567 * Here we don't have an exact match.
2569 * If next is NULL, this one goes beyond the end of the
2570 * existing list, so we need to add all of those ones first.
2572 * If next is not NULL, we need to add all the ones before
2573 * next.
2575 if (next == NULL) {
2576 offset = old_num_values;
2577 } else {
2578 /* next should have been parsed, but let's make sure */
2579 if (next->dsdb_dn == NULL) {
2580 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2581 schema_attr->syntax->ldap_oid);
2582 if (ret != LDB_SUCCESS) {
2583 return ret;
2586 offset = MIN(next - old_dns, old_num_values);
2589 /* put all the old ones before next on the list */
2590 for (; j < offset; j++) {
2591 new_values[num_values] = *old_dns[j].v;
2592 num_values++;
2595 ret = replmd_add_backlink(module, replmd_private,
2596 schema, msg_dn,
2597 &dns[i].guid,
2598 true, schema_attr,
2599 parent);
2600 /* Make the new linked attribute ldb_val. */
2601 ret = replmd_build_la_val(new_values, &new_values[num_values],
2602 dns[i].dsdb_dn, invocation_id,
2603 seq_num, seq_num,
2604 now, 0, false);
2605 if (ret != LDB_SUCCESS) {
2606 talloc_free(tmp_ctx);
2607 return ret;
2609 num_values++;
2610 if (ret != LDB_SUCCESS) {
2611 talloc_free(tmp_ctx);
2612 return ret;
2615 /* copy the rest of the old ones (if any) */
2616 for (; j < old_num_values; j++) {
2617 new_values[num_values] = *old_dns[j].v;
2618 num_values++;
2621 talloc_steal(msg->elements, new_values);
2622 if (old_el != NULL) {
2623 talloc_steal(msg->elements, old_el->values);
2625 el->values = new_values;
2626 el->num_values = num_values;
2628 talloc_free(tmp_ctx);
2630 /* we now tell the backend to replace all existing values
2631 with the one we have constructed */
2632 el->flags = LDB_FLAG_MOD_REPLACE;
2634 return LDB_SUCCESS;
2639 handle deleting all active linked attributes
2641 static int replmd_modify_la_delete(struct ldb_module *module,
2642 struct replmd_private *replmd_private,
2643 const struct dsdb_schema *schema,
2644 struct ldb_message *msg,
2645 struct ldb_message_element *el,
2646 struct ldb_message_element *old_el,
2647 const struct dsdb_attribute *schema_attr,
2648 uint64_t seq_num,
2649 time_t t,
2650 struct ldb_dn *msg_dn,
2651 struct ldb_request *parent)
2653 unsigned int i;
2654 struct parsed_dn *dns, *old_dns;
2655 TALLOC_CTX *tmp_ctx = NULL;
2656 int ret;
2657 struct ldb_context *ldb = ldb_module_get_ctx(module);
2658 struct ldb_control *vanish_links_ctrl = NULL;
2659 bool vanish_links = false;
2660 unsigned int num_to_delete = el->num_values;
2661 uint32_t rmd_flags;
2662 const struct GUID *invocation_id;
2663 NTTIME now;
2665 unix_to_nt_time(&now, t);
2667 invocation_id = samdb_ntds_invocation_id(ldb);
2668 if (!invocation_id) {
2669 return LDB_ERR_OPERATIONS_ERROR;
2672 if (old_el == NULL || old_el->num_values == 0) {
2673 /* there is nothing to delete... */
2674 if (num_to_delete == 0) {
2675 /* and we're deleting nothing, so that's OK */
2676 return LDB_SUCCESS;
2678 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2681 tmp_ctx = talloc_new(msg);
2682 if (tmp_ctx == NULL) {
2683 return LDB_ERR_OPERATIONS_ERROR;
2686 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2687 schema_attr->syntax->ldap_oid, parent);
2688 if (ret != LDB_SUCCESS) {
2689 talloc_free(tmp_ctx);
2690 return ret;
2693 ret = get_parsed_dns_trusted(module, replmd_private,
2694 tmp_ctx, old_el, &old_dns,
2695 schema_attr->syntax->ldap_oid, parent);
2697 if (ret != LDB_SUCCESS) {
2698 talloc_free(tmp_ctx);
2699 return ret;
2702 if (parent) {
2703 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2704 if (vanish_links_ctrl) {
2705 vanish_links = true;
2706 vanish_links_ctrl->critical = false;
2710 /* we empty out el->values here to avoid damage if we return early. */
2711 el->num_values = 0;
2712 el->values = NULL;
2715 * If vanish links is set, we are actually removing members of
2716 * old_el->values; otherwise we are just marking them deleted.
2718 * There is a special case when no values are given: we remove them
2719 * all. When we have the vanish_links control we just have to remove
2720 * the backlinks and change our element to replace the existing values
2721 * with the empty list.
2724 if (num_to_delete == 0) {
2725 for (i = 0; i < old_el->num_values; i++) {
2726 struct parsed_dn *p = &old_dns[i];
2727 if (p->dsdb_dn == NULL) {
2728 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2729 schema_attr->syntax->ldap_oid);
2730 if (ret != LDB_SUCCESS) {
2731 return ret;
2734 ret = replmd_add_backlink(module, replmd_private,
2735 schema, msg_dn, &p->guid,
2736 false, schema_attr,
2737 parent);
2738 if (ret != LDB_SUCCESS) {
2739 talloc_free(tmp_ctx);
2740 return ret;
2742 if (vanish_links) {
2743 continue;
2746 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2747 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2748 continue;
2751 ret = replmd_update_la_val(old_el->values, p->v,
2752 p->dsdb_dn, p->dsdb_dn,
2753 invocation_id, seq_num,
2754 seq_num, now, true);
2755 if (ret != LDB_SUCCESS) {
2756 talloc_free(tmp_ctx);
2757 return ret;
2761 if (vanish_links) {
2762 el->flags = LDB_FLAG_MOD_REPLACE;
2763 talloc_free(tmp_ctx);
2764 return LDB_SUCCESS;
2769 for (i = 0; i < num_to_delete; i++) {
2770 struct parsed_dn *p = &dns[i];
2771 struct parsed_dn *exact = NULL;
2772 struct parsed_dn *next = NULL;
2773 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2774 &p->guid,
2775 NULL,
2776 p->dsdb_dn->extra_part, 0,
2777 &exact, &next,
2778 schema_attr->syntax->ldap_oid,
2779 true);
2780 if (ret != LDB_SUCCESS) {
2781 talloc_free(tmp_ctx);
2782 return ret;
2784 if (exact == NULL) {
2785 struct GUID_txt_buf buf;
2786 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2787 "exist for target GUID %s",
2788 el->name,
2789 GUID_buf_string(&p->guid, &buf));
2790 if (ldb_attr_cmp(el->name, "member") == 0) {
2791 talloc_free(tmp_ctx);
2792 return LDB_ERR_UNWILLING_TO_PERFORM;
2793 } else {
2794 talloc_free(tmp_ctx);
2795 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2799 if (vanish_links) {
2800 if (CHECK_DEBUGLVL(5)) {
2801 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2802 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2803 struct GUID_txt_buf buf;
2804 const char *guid_str = \
2805 GUID_buf_string(&p->guid, &buf);
2806 DEBUG(5, ("Deleting deleted linked "
2807 "attribute %s to %s, because "
2808 "vanish_links control is set\n",
2809 el->name, guid_str));
2813 /* remove the backlink */
2814 ret = replmd_add_backlink(module,
2815 replmd_private,
2816 schema,
2817 msg_dn,
2818 &p->guid,
2819 false, schema_attr,
2820 parent);
2821 if (ret != LDB_SUCCESS) {
2822 talloc_free(tmp_ctx);
2823 return ret;
2826 /* We flag the deletion and tidy it up later. */
2827 exact->v = NULL;
2828 continue;
2831 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2833 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2834 struct GUID_txt_buf buf;
2835 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2836 ldb_asprintf_errstring(ldb, "Attribute %s already "
2837 "deleted for target GUID %s",
2838 el->name, guid_str);
2839 if (ldb_attr_cmp(el->name, "member") == 0) {
2840 talloc_free(tmp_ctx);
2841 return LDB_ERR_UNWILLING_TO_PERFORM;
2842 } else {
2843 talloc_free(tmp_ctx);
2844 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2848 ret = replmd_update_la_val(old_el->values, exact->v,
2849 exact->dsdb_dn, exact->dsdb_dn,
2850 invocation_id, seq_num, seq_num,
2851 now, true);
2852 if (ret != LDB_SUCCESS) {
2853 talloc_free(tmp_ctx);
2854 return ret;
2856 ret = replmd_add_backlink(module, replmd_private,
2857 schema, msg_dn,
2858 &p->guid,
2859 false, schema_attr,
2860 parent);
2861 if (ret != LDB_SUCCESS) {
2862 talloc_free(tmp_ctx);
2863 return ret;
2867 if (vanish_links) {
2868 unsigned j = 0;
2869 for (i = 0; i < old_el->num_values; i++) {
2870 if (old_dns[i].v != NULL) {
2871 old_el->values[j] = *old_dns[i].v;
2872 j++;
2875 old_el->num_values = j;
2878 el->values = talloc_steal(msg->elements, old_el->values);
2879 el->num_values = old_el->num_values;
2881 talloc_free(tmp_ctx);
2883 /* we now tell the backend to replace all existing values
2884 with the one we have constructed */
2885 el->flags = LDB_FLAG_MOD_REPLACE;
2887 return LDB_SUCCESS;
2891 handle replacing a linked attribute
2893 static int replmd_modify_la_replace(struct ldb_module *module,
2894 struct replmd_private *replmd_private,
2895 const struct dsdb_schema *schema,
2896 struct ldb_message *msg,
2897 struct ldb_message_element *el,
2898 struct ldb_message_element *old_el,
2899 const struct dsdb_attribute *schema_attr,
2900 uint64_t seq_num,
2901 time_t t,
2902 struct ldb_dn *msg_dn,
2903 struct ldb_request *parent)
2905 unsigned int i, old_i, new_i;
2906 struct parsed_dn *dns, *old_dns;
2907 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2908 int ret;
2909 const struct GUID *invocation_id;
2910 struct ldb_context *ldb = ldb_module_get_ctx(module);
2911 struct ldb_val *new_values = NULL;
2912 const char *ldap_oid = schema_attr->syntax->ldap_oid;
2913 unsigned int old_num_values;
2914 unsigned int repl_num_values;
2915 unsigned int max_num_values;
2916 NTTIME now;
2918 unix_to_nt_time(&now, t);
2920 invocation_id = samdb_ntds_invocation_id(ldb);
2921 if (!invocation_id) {
2922 return LDB_ERR_OPERATIONS_ERROR;
2926 * The replace operation is unlike the replace and delete cases in that
2927 * we need to look at every existing link to see whether it is being
2928 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
2930 * As we are trying to combine two sorted lists, the algorithm we use
2931 * is akin to the merge phase of a merge sort. We interleave the two
2932 * lists, doing different things depending on which side the current
2933 * item came from.
2935 * There are three main cases, with some sub-cases.
2937 * - a DN is in the old list but not the new one. It needs to be
2938 * marked as deleted (but left in the list).
2939 * - maybe it is already deleted, and we have less to do.
2941 * - a DN is in both lists. The old data gets replaced by the new,
2942 * and the list doesn't grow. The old link may have been marked as
2943 * deleted, in which case we undelete it.
2945 * - a DN is in the new list only. We add it in the right place.
2948 old_num_values = old_el ? old_el->num_values : 0;
2949 repl_num_values = el->num_values;
2950 max_num_values = old_num_values + repl_num_values;
2952 if (max_num_values == 0) {
2953 /* There is nothing to do! */
2954 return LDB_SUCCESS;
2957 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
2958 if (ret != LDB_SUCCESS) {
2959 talloc_free(tmp_ctx);
2960 return ret;
2963 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
2964 ldap_oid, parent);
2965 if (ret != LDB_SUCCESS) {
2966 talloc_free(tmp_ctx);
2967 return ret;
2970 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
2971 old_el, ldap_oid);
2972 if (ret != LDB_SUCCESS) {
2973 talloc_free(tmp_ctx);
2974 return ret;
2977 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
2978 if (new_values == NULL) {
2979 ldb_module_oom(module);
2980 talloc_free(tmp_ctx);
2981 return LDB_ERR_OPERATIONS_ERROR;
2984 old_i = 0;
2985 new_i = 0;
2986 for (i = 0; i < max_num_values; i++) {
2987 int cmp;
2988 struct parsed_dn *old_p, *new_p;
2989 if (old_i < old_num_values && new_i < repl_num_values) {
2990 old_p = &old_dns[old_i];
2991 new_p = &dns[new_i];
2992 cmp = parsed_dn_compare(old_p, new_p);
2993 } else if (old_i < old_num_values) {
2994 /* the new list is empty, read the old list */
2995 old_p = &old_dns[old_i];
2996 new_p = NULL;
2997 cmp = -1;
2998 } else if (new_i < repl_num_values) {
2999 /* the old list is empty, read new list */
3000 old_p = NULL;
3001 new_p = &dns[new_i];
3002 cmp = 1;
3003 } else {
3004 break;
3007 if (cmp < 0) {
3009 * An old ones that come before the next replacement
3010 * (if any). We mark it as deleted and add it to the
3011 * final list.
3013 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3014 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
3015 ret = replmd_update_la_val(new_values, old_p->v,
3016 old_p->dsdb_dn,
3017 old_p->dsdb_dn,
3018 invocation_id,
3019 seq_num, seq_num,
3020 now, true);
3021 if (ret != LDB_SUCCESS) {
3022 talloc_free(tmp_ctx);
3023 return ret;
3026 ret = replmd_add_backlink(module, replmd_private,
3027 schema,
3028 msg_dn,
3029 &old_p->guid, false,
3030 schema_attr,
3031 parent);
3032 if (ret != LDB_SUCCESS) {
3033 talloc_free(tmp_ctx);
3034 return ret;
3037 new_values[i] = *old_p->v;
3038 old_i++;
3039 } else if (cmp == 0) {
3041 * We are overwriting one. If it was previously
3042 * deleted, we need to add a backlink.
3044 * Note that if any RMD_FLAGs in an extended new DN
3045 * will be ignored.
3047 uint32_t rmd_flags;
3049 ret = replmd_update_la_val(new_values, old_p->v,
3050 new_p->dsdb_dn,
3051 old_p->dsdb_dn,
3052 invocation_id,
3053 seq_num, seq_num,
3054 now, false);
3055 if (ret != LDB_SUCCESS) {
3056 talloc_free(tmp_ctx);
3057 return ret;
3060 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3061 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3062 ret = replmd_add_backlink(module, replmd_private,
3063 schema,
3064 msg_dn,
3065 &new_p->guid, true,
3066 schema_attr,
3067 parent);
3068 if (ret != LDB_SUCCESS) {
3069 talloc_free(tmp_ctx);
3070 return ret;
3074 new_values[i] = *old_p->v;
3075 old_i++;
3076 new_i++;
3077 } else {
3079 * Replacements that don't match an existing one. We
3080 * just add them to the final list.
3082 ret = replmd_build_la_val(new_values,
3083 new_p->v,
3084 new_p->dsdb_dn,
3085 invocation_id,
3086 seq_num, seq_num,
3087 now, 0, false);
3088 if (ret != LDB_SUCCESS) {
3089 talloc_free(tmp_ctx);
3090 return ret;
3092 ret = replmd_add_backlink(module, replmd_private,
3093 schema,
3094 msg_dn,
3095 &new_p->guid, true,
3096 schema_attr,
3097 parent);
3098 if (ret != LDB_SUCCESS) {
3099 talloc_free(tmp_ctx);
3100 return ret;
3102 new_values[i] = *new_p->v;
3103 new_i++;
3106 if (old_el != NULL) {
3107 talloc_steal(msg->elements, old_el->values);
3109 el->values = talloc_steal(msg->elements, new_values);
3110 el->num_values = i;
3111 talloc_free(tmp_ctx);
3113 el->flags = LDB_FLAG_MOD_REPLACE;
3115 return LDB_SUCCESS;
3120 handle linked attributes in modify requests
3122 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3123 struct replmd_private *replmd_private,
3124 struct ldb_message *msg,
3125 uint64_t seq_num, time_t t,
3126 struct ldb_request *parent)
3128 struct ldb_result *res;
3129 unsigned int i;
3130 int ret;
3131 struct ldb_context *ldb = ldb_module_get_ctx(module);
3132 struct ldb_message *old_msg;
3134 const struct dsdb_schema *schema;
3136 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3138 * Nothing special is required for modifying or vanishing links
3139 * in fl2000 since they are just strings in a multi-valued
3140 * attribute.
3142 struct ldb_control *ctrl = ldb_request_get_control(parent,
3143 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3144 if (ctrl) {
3145 ctrl->critical = false;
3147 return LDB_SUCCESS;
3151 * TODO:
3153 * We should restrict this to the intersection of the list of
3154 * linked attributes in the schema and the list of attributes
3155 * being modified.
3157 * This will help performance a little, as otherwise we have
3158 * to allocate the entire object value-by-value.
3160 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3161 DSDB_FLAG_NEXT_MODULE |
3162 DSDB_SEARCH_SHOW_RECYCLED |
3163 DSDB_SEARCH_REVEAL_INTERNALS |
3164 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3165 parent);
3166 if (ret != LDB_SUCCESS) {
3167 return ret;
3169 schema = dsdb_get_schema(ldb, res);
3170 if (!schema) {
3171 return LDB_ERR_OPERATIONS_ERROR;
3174 old_msg = res->msgs[0];
3176 for (i=0; i<msg->num_elements; i++) {
3177 struct ldb_message_element *el = &msg->elements[i];
3178 struct ldb_message_element *old_el, *new_el;
3179 unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3180 const struct dsdb_attribute *schema_attr
3181 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3182 if (!schema_attr) {
3183 ldb_asprintf_errstring(ldb,
3184 "%s: attribute %s is not a valid attribute in schema",
3185 __FUNCTION__, el->name);
3186 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3188 if (schema_attr->linkID == 0) {
3189 continue;
3191 if ((schema_attr->linkID & 1) == 1) {
3192 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
3193 continue;
3195 /* Odd is for the target. Illegal to modify */
3196 ldb_asprintf_errstring(ldb,
3197 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3198 return LDB_ERR_UNWILLING_TO_PERFORM;
3200 old_el = ldb_msg_find_element(old_msg, el->name);
3201 switch (mod_type) {
3202 case LDB_FLAG_MOD_REPLACE:
3203 ret = replmd_modify_la_replace(module, replmd_private,
3204 schema, msg, el, old_el,
3205 schema_attr, seq_num, t,
3206 old_msg->dn,
3207 parent);
3208 break;
3209 case LDB_FLAG_MOD_DELETE:
3210 ret = replmd_modify_la_delete(module, replmd_private,
3211 schema, msg, el, old_el,
3212 schema_attr, seq_num, t,
3213 old_msg->dn,
3214 parent);
3215 break;
3216 case LDB_FLAG_MOD_ADD:
3217 ret = replmd_modify_la_add(module, replmd_private,
3218 schema, msg, el, old_el,
3219 schema_attr, seq_num, t,
3220 old_msg->dn,
3221 parent);
3222 break;
3223 default:
3224 ldb_asprintf_errstring(ldb,
3225 "invalid flags 0x%x for %s linked attribute",
3226 el->flags, el->name);
3227 return LDB_ERR_UNWILLING_TO_PERFORM;
3229 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
3230 ldb_asprintf_errstring(ldb,
3231 "Attribute %s is single valued but more than one value has been supplied",
3232 el->name);
3233 /* Return codes as found on Windows 2012r2 */
3234 if (mod_type == LDB_FLAG_MOD_REPLACE) {
3235 return LDB_ERR_CONSTRAINT_VIOLATION;
3236 } else {
3237 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3239 } else {
3240 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3243 if (ret != LDB_SUCCESS) {
3244 return ret;
3246 if (old_el) {
3247 ldb_msg_remove_attr(old_msg, el->name);
3249 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3250 new_el->num_values = el->num_values;
3251 new_el->values = talloc_steal(msg->elements, el->values);
3253 /* TODO: this relises a bit too heavily on the exact
3254 behaviour of ldb_msg_find_element and
3255 ldb_msg_remove_element */
3256 old_el = ldb_msg_find_element(msg, el->name);
3257 if (old_el != el) {
3258 ldb_msg_remove_element(msg, old_el);
3259 i--;
3263 talloc_free(res);
3264 return ret;
3268 static int send_rodc_referral(struct ldb_request *req,
3269 struct ldb_context *ldb,
3270 struct ldb_dn *dn)
3272 char *referral = NULL;
3273 struct loadparm_context *lp_ctx = NULL;
3274 struct ldb_dn *fsmo_role_dn = NULL;
3275 struct ldb_dn *role_owner_dn = NULL;
3276 const char *domain = NULL;
3277 WERROR werr;
3279 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3280 struct loadparm_context);
3282 werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3283 &fsmo_role_dn, &role_owner_dn);
3285 if (W_ERROR_IS_OK(werr)) {
3286 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3287 if (server_dn != NULL) {
3288 ldb_dn_remove_child_components(server_dn, 1);
3289 domain = samdb_dn_to_dnshostname(ldb, req,
3290 server_dn);
3294 if (domain == NULL) {
3295 domain = lpcfg_dnsdomain(lp_ctx);
3298 referral = talloc_asprintf(req, "ldap://%s/%s",
3299 domain,
3300 ldb_dn_get_linearized(dn));
3301 if (referral == NULL) {
3302 ldb_oom(ldb);
3303 return LDB_ERR_OPERATIONS_ERROR;
3306 return ldb_module_send_referral(req, referral);
3310 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3312 struct ldb_context *ldb;
3313 struct replmd_replicated_request *ac;
3314 struct ldb_request *down_req;
3315 struct ldb_message *msg;
3316 time_t t = time(NULL);
3317 int ret;
3318 bool is_urgent = false, rodc = false;
3319 bool is_schema_nc = false;
3320 unsigned int functional_level;
3321 const struct ldb_message_element *guid_el = NULL;
3322 struct ldb_control *sd_propagation_control;
3323 struct replmd_private *replmd_private =
3324 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3326 /* do not manipulate our control entries */
3327 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3328 return ldb_next_request(module, req);
3331 sd_propagation_control = ldb_request_get_control(req,
3332 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3333 if (sd_propagation_control != NULL) {
3334 if (req->op.mod.message->num_elements != 1) {
3335 return ldb_module_operr(module);
3337 ret = strcmp(req->op.mod.message->elements[0].name,
3338 "nTSecurityDescriptor");
3339 if (ret != 0) {
3340 return ldb_module_operr(module);
3343 return ldb_next_request(module, req);
3346 ldb = ldb_module_get_ctx(module);
3348 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3350 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3351 if (guid_el != NULL) {
3352 ldb_set_errstring(ldb,
3353 "replmd_modify: it's not allowed to change the objectGUID!");
3354 return LDB_ERR_CONSTRAINT_VIOLATION;
3357 ac = replmd_ctx_init(module, req);
3358 if (ac == NULL) {
3359 return ldb_module_oom(module);
3362 functional_level = dsdb_functional_level(ldb);
3364 /* we have to copy the message as the caller might have it as a const */
3365 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3366 if (msg == NULL) {
3367 ldb_oom(ldb);
3368 talloc_free(ac);
3369 return LDB_ERR_OPERATIONS_ERROR;
3372 ldb_msg_remove_attr(msg, "whenChanged");
3373 ldb_msg_remove_attr(msg, "uSNChanged");
3375 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3377 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3378 msg, &ac->seq_num, t, is_schema_nc,
3379 &is_urgent, &rodc);
3380 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3381 ret = send_rodc_referral(req, ldb, msg->dn);
3382 talloc_free(ac);
3383 return ret;
3387 if (ret != LDB_SUCCESS) {
3388 talloc_free(ac);
3389 return ret;
3392 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3393 msg, ac->seq_num, t, req);
3394 if (ret != LDB_SUCCESS) {
3395 talloc_free(ac);
3396 return ret;
3399 /* TODO:
3400 * - replace the old object with the newly constructed one
3403 ac->is_urgent = is_urgent;
3405 ret = ldb_build_mod_req(&down_req, ldb, ac,
3406 msg,
3407 req->controls,
3408 ac, replmd_op_callback,
3409 req);
3410 LDB_REQ_SET_LOCATION(down_req);
3411 if (ret != LDB_SUCCESS) {
3412 talloc_free(ac);
3413 return ret;
3416 /* current partition control is needed by "replmd_op_callback" */
3417 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3418 ret = ldb_request_add_control(down_req,
3419 DSDB_CONTROL_CURRENT_PARTITION_OID,
3420 false, NULL);
3421 if (ret != LDB_SUCCESS) {
3422 talloc_free(ac);
3423 return ret;
3427 /* If we are in functional level 2000, then
3428 * replmd_modify_handle_linked_attribs will have done
3429 * nothing */
3430 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3431 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3432 if (ret != LDB_SUCCESS) {
3433 talloc_free(ac);
3434 return ret;
3438 talloc_steal(down_req, msg);
3440 /* we only change whenChanged and uSNChanged if the seq_num
3441 has changed */
3442 if (ac->seq_num != 0) {
3443 ret = add_time_element(msg, "whenChanged", t);
3444 if (ret != LDB_SUCCESS) {
3445 talloc_free(ac);
3446 ldb_operr(ldb);
3447 return ret;
3450 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3451 if (ret != LDB_SUCCESS) {
3452 talloc_free(ac);
3453 ldb_operr(ldb);
3454 return ret;
3458 /* go on with the call chain */
3459 return ldb_next_request(module, down_req);
3462 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3465 handle a rename request
3467 On a rename we need to do an extra ldb_modify which sets the
3468 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3470 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3472 struct ldb_context *ldb;
3473 struct replmd_replicated_request *ac;
3474 int ret;
3475 struct ldb_request *down_req;
3477 /* do not manipulate our control entries */
3478 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3479 return ldb_next_request(module, req);
3482 ldb = ldb_module_get_ctx(module);
3484 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3486 ac = replmd_ctx_init(module, req);
3487 if (ac == NULL) {
3488 return ldb_module_oom(module);
3491 ret = ldb_build_rename_req(&down_req, ldb, ac,
3492 ac->req->op.rename.olddn,
3493 ac->req->op.rename.newdn,
3494 ac->req->controls,
3495 ac, replmd_rename_callback,
3496 ac->req);
3497 LDB_REQ_SET_LOCATION(down_req);
3498 if (ret != LDB_SUCCESS) {
3499 talloc_free(ac);
3500 return ret;
3503 /* go on with the call chain */
3504 return ldb_next_request(module, down_req);
3507 /* After the rename is compleated, update the whenchanged etc */
3508 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3510 struct ldb_context *ldb;
3511 struct ldb_request *down_req;
3512 struct ldb_message *msg;
3513 const struct dsdb_attribute *rdn_attr;
3514 const char *rdn_name;
3515 const struct ldb_val *rdn_val;
3516 const char *attrs[5] = { NULL, };
3517 time_t t = time(NULL);
3518 int ret;
3519 bool is_urgent = false, rodc = false;
3520 bool is_schema_nc;
3521 struct replmd_replicated_request *ac =
3522 talloc_get_type(req->context, struct replmd_replicated_request);
3523 struct replmd_private *replmd_private =
3524 talloc_get_type(ldb_module_get_private(ac->module),
3525 struct replmd_private);
3527 ldb = ldb_module_get_ctx(ac->module);
3529 if (ares->error != LDB_SUCCESS) {
3530 return ldb_module_done(ac->req, ares->controls,
3531 ares->response, ares->error);
3534 if (ares->type != LDB_REPLY_DONE) {
3535 ldb_set_errstring(ldb,
3536 "invalid ldb_reply_type in callback");
3537 talloc_free(ares);
3538 return ldb_module_done(ac->req, NULL, NULL,
3539 LDB_ERR_OPERATIONS_ERROR);
3542 /* TODO:
3543 * - replace the old object with the newly constructed one
3546 msg = ldb_msg_new(ac);
3547 if (msg == NULL) {
3548 ldb_oom(ldb);
3549 return LDB_ERR_OPERATIONS_ERROR;
3552 msg->dn = ac->req->op.rename.newdn;
3554 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3556 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3557 if (rdn_name == NULL) {
3558 talloc_free(ares);
3559 return ldb_module_done(ac->req, NULL, NULL,
3560 ldb_operr(ldb));
3563 /* normalize the rdn attribute name */
3564 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3565 if (rdn_attr == NULL) {
3566 talloc_free(ares);
3567 return ldb_module_done(ac->req, NULL, NULL,
3568 ldb_operr(ldb));
3570 rdn_name = rdn_attr->lDAPDisplayName;
3572 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3573 if (rdn_val == NULL) {
3574 talloc_free(ares);
3575 return ldb_module_done(ac->req, NULL, NULL,
3576 ldb_operr(ldb));
3579 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3580 talloc_free(ares);
3581 return ldb_module_done(ac->req, NULL, NULL,
3582 ldb_oom(ldb));
3584 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3585 talloc_free(ares);
3586 return ldb_module_done(ac->req, NULL, NULL,
3587 ldb_oom(ldb));
3589 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3590 talloc_free(ares);
3591 return ldb_module_done(ac->req, NULL, NULL,
3592 ldb_oom(ldb));
3594 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3595 talloc_free(ares);
3596 return ldb_module_done(ac->req, NULL, NULL,
3597 ldb_oom(ldb));
3601 * here we let replmd_update_rpmd() only search for
3602 * the existing "replPropertyMetaData" and rdn_name attributes.
3604 * We do not want the existing "name" attribute as
3605 * the "name" attribute needs to get the version
3606 * updated on rename even if the rdn value hasn't changed.
3608 * This is the diff of the meta data, for a moved user
3609 * on a w2k8r2 server:
3611 * # record 1
3612 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3613 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3614 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3615 * version : 0x00000001 (1)
3616 * reserved : 0x00000000 (0)
3617 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3618 * local_usn : 0x00000000000037a5 (14245)
3619 * array: struct replPropertyMetaData1
3620 * attid : DRSUAPI_ATTID_name (0x90001)
3621 * - version : 0x00000001 (1)
3622 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3623 * + version : 0x00000002 (2)
3624 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3625 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3626 * - originating_usn : 0x00000000000037a5 (14245)
3627 * - local_usn : 0x00000000000037a5 (14245)
3628 * + originating_usn : 0x0000000000003834 (14388)
3629 * + local_usn : 0x0000000000003834 (14388)
3630 * array: struct replPropertyMetaData1
3631 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3632 * version : 0x00000004 (4)
3634 attrs[0] = "replPropertyMetaData";
3635 attrs[1] = "objectClass";
3636 attrs[2] = "instanceType";
3637 attrs[3] = rdn_name;
3638 attrs[4] = NULL;
3640 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3641 msg, &ac->seq_num, t,
3642 is_schema_nc, &is_urgent, &rodc);
3643 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3644 ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
3645 talloc_free(ares);
3646 return ldb_module_done(req, NULL, NULL, ret);
3649 if (ret != LDB_SUCCESS) {
3650 talloc_free(ares);
3651 return ldb_module_done(ac->req, NULL, NULL, ret);
3654 if (ac->seq_num == 0) {
3655 talloc_free(ares);
3656 return ldb_module_done(ac->req, NULL, NULL,
3657 ldb_error(ldb, ret,
3658 "internal error seq_num == 0"));
3660 ac->is_urgent = is_urgent;
3662 ret = ldb_build_mod_req(&down_req, ldb, ac,
3663 msg,
3664 req->controls,
3665 ac, replmd_op_callback,
3666 req);
3667 LDB_REQ_SET_LOCATION(down_req);
3668 if (ret != LDB_SUCCESS) {
3669 talloc_free(ac);
3670 return ret;
3673 /* current partition control is needed by "replmd_op_callback" */
3674 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3675 ret = ldb_request_add_control(down_req,
3676 DSDB_CONTROL_CURRENT_PARTITION_OID,
3677 false, NULL);
3678 if (ret != LDB_SUCCESS) {
3679 talloc_free(ac);
3680 return ret;
3684 talloc_steal(down_req, msg);
3686 ret = add_time_element(msg, "whenChanged", t);
3687 if (ret != LDB_SUCCESS) {
3688 talloc_free(ac);
3689 ldb_operr(ldb);
3690 return ret;
3693 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3694 if (ret != LDB_SUCCESS) {
3695 talloc_free(ac);
3696 ldb_operr(ldb);
3697 return ret;
3700 /* go on with the call chain - do the modify after the rename */
3701 return ldb_next_request(ac->module, down_req);
3705 * remove links from objects that point at this object when an object
3706 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3707 * RemoveObj which states that link removal due to the object being
3708 * deleted is NOT an originating update - they just go away!
3711 static int replmd_delete_remove_link(struct ldb_module *module,
3712 const struct dsdb_schema *schema,
3713 struct replmd_private *replmd_private,
3714 struct ldb_dn *dn,
3715 struct GUID *guid,
3716 struct ldb_message_element *el,
3717 const struct dsdb_attribute *sa,
3718 struct ldb_request *parent)
3720 unsigned int i;
3721 TALLOC_CTX *tmp_ctx = talloc_new(module);
3722 struct ldb_context *ldb = ldb_module_get_ctx(module);
3724 for (i=0; i<el->num_values; i++) {
3725 struct dsdb_dn *dsdb_dn;
3726 int ret;
3727 struct ldb_message *msg;
3728 const struct dsdb_attribute *target_attr;
3729 struct ldb_message_element *el2;
3730 const char *dn_str;
3731 struct ldb_val dn_val;
3732 uint32_t dsdb_flags = 0;
3733 const char *attrs[] = { NULL, NULL };
3734 struct ldb_result *link_res;
3735 struct ldb_message *link_msg;
3736 struct ldb_message_element *link_el;
3737 struct parsed_dn *link_dns;
3738 struct parsed_dn *p = NULL, *unused = NULL;
3740 if (dsdb_dn_is_deleted_val(&el->values[i])) {
3741 continue;
3744 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
3745 if (!dsdb_dn) {
3746 talloc_free(tmp_ctx);
3747 return LDB_ERR_OPERATIONS_ERROR;
3750 /* remove the link */
3751 msg = ldb_msg_new(tmp_ctx);
3752 if (!msg) {
3753 ldb_module_oom(module);
3754 talloc_free(tmp_ctx);
3755 return LDB_ERR_OPERATIONS_ERROR;
3759 msg->dn = dsdb_dn->dn;
3761 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
3762 if (target_attr == NULL) {
3763 continue;
3765 attrs[0] = target_attr->lDAPDisplayName;
3767 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
3768 LDB_FLAG_MOD_DELETE, &el2);
3769 if (ret != LDB_SUCCESS) {
3770 ldb_module_oom(module);
3771 talloc_free(tmp_ctx);
3772 return LDB_ERR_OPERATIONS_ERROR;
3775 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
3776 msg->dn, attrs,
3777 DSDB_FLAG_NEXT_MODULE |
3778 DSDB_SEARCH_SHOW_EXTENDED_DN,
3779 parent);
3781 if (ret != LDB_SUCCESS) {
3782 talloc_free(tmp_ctx);
3783 return ret;
3786 link_msg = link_res->msgs[0];
3787 link_el = ldb_msg_find_element(link_msg,
3788 target_attr->lDAPDisplayName);
3789 if (link_el == NULL) {
3790 talloc_free(tmp_ctx);
3791 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3795 * This call 'upgrades' the links in link_dns, but we
3796 * do not commit the result back into the database, so
3797 * this is safe to call in FL2000 or on databases that
3798 * have been run at that level in the past.
3800 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx,
3801 link_el, &link_dns,
3802 target_attr->syntax->ldap_oid, parent);
3803 if (ret != LDB_SUCCESS) {
3804 talloc_free(tmp_ctx);
3805 return ret;
3808 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
3809 guid, dn,
3810 data_blob_null, 0,
3811 &p, &unused,
3812 target_attr->syntax->ldap_oid, false);
3813 if (ret != LDB_SUCCESS) {
3814 talloc_free(tmp_ctx);
3815 return ret;
3818 if (p == NULL) {
3819 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3820 "Failed to find forward link on %s "
3821 "as %s to remove backlink %s on %s",
3822 ldb_dn_get_linearized(msg->dn),
3823 target_attr->lDAPDisplayName,
3824 sa->lDAPDisplayName,
3825 ldb_dn_get_linearized(dn));
3826 talloc_free(tmp_ctx);
3827 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3831 /* This needs to get the Binary DN, by first searching */
3832 dn_str = dsdb_dn_get_linearized(tmp_ctx,
3833 p->dsdb_dn);
3835 dn_val = data_blob_string_const(dn_str);
3836 el2->values = &dn_val;
3837 el2->num_values = 1;
3840 * Ensure that we tell the modification to vanish any linked
3841 * attributes (not simply mark them as isDeleted = TRUE)
3843 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
3845 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
3846 if (ret != LDB_SUCCESS) {
3847 talloc_free(tmp_ctx);
3848 return ret;
3851 talloc_free(tmp_ctx);
3852 return LDB_SUCCESS;
3857 handle update of replication meta data for deletion of objects
3859 This also handles the mapping of delete to a rename operation
3860 to allow deletes to be replicated.
3862 It also handles the incoming deleted objects, to ensure they are
3863 fully deleted here. In that case re_delete is true, and we do not
3864 use this as a signal to change the deleted state, just reinforce it.
3867 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
3869 int ret = LDB_ERR_OTHER;
3870 bool retb, disallow_move_on_delete;
3871 struct ldb_dn *old_dn, *new_dn;
3872 const char *rdn_name;
3873 const struct ldb_val *rdn_value, *new_rdn_value;
3874 struct GUID guid;
3875 struct ldb_context *ldb = ldb_module_get_ctx(module);
3876 const struct dsdb_schema *schema;
3877 struct ldb_message *msg, *old_msg;
3878 struct ldb_message_element *el;
3879 TALLOC_CTX *tmp_ctx;
3880 struct ldb_result *res, *parent_res;
3881 static const char * const preserved_attrs[] = {
3882 /* yes, this really is a hard coded list. See MS-ADTS
3883 section 3.1.1.5.5.1.1 */
3884 "attributeID",
3885 "attributeSyntax",
3886 "dNReferenceUpdate",
3887 "dNSHostName",
3888 "flatName",
3889 "governsID",
3890 "groupType",
3891 "instanceType",
3892 "lDAPDisplayName",
3893 "legacyExchangeDN",
3894 "isDeleted",
3895 "isRecycled",
3896 "lastKnownParent",
3897 "msDS-LastKnownRDN",
3898 "msDS-PortLDAP",
3899 "mS-DS-CreatorSID",
3900 "mSMQOwnerID",
3901 "nCName",
3902 "objectClass",
3903 "distinguishedName",
3904 "objectGUID",
3905 "objectSid",
3906 "oMSyntax",
3907 "proxiedObjectName",
3908 "name",
3909 "nTSecurityDescriptor",
3910 "replPropertyMetaData",
3911 "sAMAccountName",
3912 "securityIdentifier",
3913 "sIDHistory",
3914 "subClassOf",
3915 "systemFlags",
3916 "trustPartner",
3917 "trustDirection",
3918 "trustType",
3919 "trustAttributes",
3920 "userAccountControl",
3921 "uSNChanged",
3922 "uSNCreated",
3923 "whenCreated",
3924 "whenChanged",
3925 NULL
3927 static const char * const all_attrs[] = {
3928 DSDB_SECRET_ATTRIBUTES,
3929 "*",
3930 NULL
3932 unsigned int i, el_count = 0;
3933 uint32_t dsdb_flags = 0;
3934 struct replmd_private *replmd_private;
3935 enum deletion_state deletion_state, next_deletion_state;
3937 if (ldb_dn_is_special(req->op.del.dn)) {
3938 return ldb_next_request(module, req);
3942 * We have to allow dbcheck to remove an object that
3943 * is beyond repair, and to do so totally. This could
3944 * mean we we can get a partial object from the other
3945 * DC, causing havoc, so dbcheck suggests
3946 * re-replication first. dbcheck sets both DBCHECK
3947 * and RELAX in this situation.
3949 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
3950 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
3951 /* really, really remove it */
3952 return ldb_next_request(module, req);
3955 tmp_ctx = talloc_new(ldb);
3956 if (!tmp_ctx) {
3957 ldb_oom(ldb);
3958 return LDB_ERR_OPERATIONS_ERROR;
3961 schema = dsdb_get_schema(ldb, tmp_ctx);
3962 if (!schema) {
3963 talloc_free(tmp_ctx);
3964 return LDB_ERR_OPERATIONS_ERROR;
3967 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
3969 /* we need the complete msg off disk, so we can work out which
3970 attributes need to be removed */
3971 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
3972 DSDB_FLAG_NEXT_MODULE |
3973 DSDB_SEARCH_SHOW_RECYCLED |
3974 DSDB_SEARCH_REVEAL_INTERNALS |
3975 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
3976 if (ret != LDB_SUCCESS) {
3977 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3978 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
3979 re_delete ? "re-delete" : "delete",
3980 ldb_dn_get_linearized(old_dn),
3981 ldb_errstring(ldb_module_get_ctx(module)));
3982 talloc_free(tmp_ctx);
3983 return ret;
3985 old_msg = res->msgs[0];
3987 replmd_deletion_state(module, old_msg,
3988 &deletion_state,
3989 &next_deletion_state);
3991 /* This supports us noticing an incoming isDeleted and acting on it */
3992 if (re_delete) {
3993 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
3994 next_deletion_state = deletion_state;
3997 if (next_deletion_state == OBJECT_REMOVED) {
3999 * We have to prevent objects being deleted, even if
4000 * the administrator really wants them gone, as
4001 * without the tombstone, we can get a partial object
4002 * from the other DC, causing havoc.
4004 * The only other valid case is when the 180 day
4005 * timeout has expired, when relax is specified.
4007 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
4008 /* it is already deleted - really remove it this time */
4009 talloc_free(tmp_ctx);
4010 return ldb_next_request(module, req);
4013 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
4014 "This check is to prevent corruption of the replicated state.",
4015 ldb_dn_get_linearized(old_msg->dn));
4016 return LDB_ERR_UNWILLING_TO_PERFORM;
4019 rdn_name = ldb_dn_get_rdn_name(old_dn);
4020 rdn_value = ldb_dn_get_rdn_val(old_dn);
4021 if ((rdn_name == NULL) || (rdn_value == NULL)) {
4022 talloc_free(tmp_ctx);
4023 return ldb_operr(ldb);
4026 msg = ldb_msg_new(tmp_ctx);
4027 if (msg == NULL) {
4028 ldb_module_oom(module);
4029 talloc_free(tmp_ctx);
4030 return LDB_ERR_OPERATIONS_ERROR;
4033 msg->dn = old_dn;
4035 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4036 disallow_move_on_delete =
4037 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4038 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4040 /* work out where we will be renaming this object to */
4041 if (!disallow_move_on_delete) {
4042 struct ldb_dn *deleted_objects_dn;
4043 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4044 &deleted_objects_dn);
4047 * We should not move objects if we can't find the
4048 * deleted objects DN. Not moving (or otherwise
4049 * harming) the Deleted Objects DN itself is handled
4050 * in the caller.
4052 if (re_delete && (ret != LDB_SUCCESS)) {
4053 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4054 if (new_dn == NULL) {
4055 ldb_module_oom(module);
4056 talloc_free(tmp_ctx);
4057 return LDB_ERR_OPERATIONS_ERROR;
4059 } else if (ret != LDB_SUCCESS) {
4060 /* this is probably an attempted delete on a partition
4061 * that doesn't allow delete operations, such as the
4062 * schema partition */
4063 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4064 ldb_dn_get_linearized(old_dn));
4065 talloc_free(tmp_ctx);
4066 return LDB_ERR_UNWILLING_TO_PERFORM;
4067 } else {
4068 new_dn = deleted_objects_dn;
4070 } else {
4071 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4072 if (new_dn == NULL) {
4073 ldb_module_oom(module);
4074 talloc_free(tmp_ctx);
4075 return LDB_ERR_OPERATIONS_ERROR;
4079 /* get the objects GUID from the search we just did */
4080 guid = samdb_result_guid(old_msg, "objectGUID");
4082 if (deletion_state == OBJECT_NOT_DELETED) {
4083 /* Add a formatted child */
4084 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
4085 rdn_name,
4086 ldb_dn_escape_value(tmp_ctx, *rdn_value),
4087 GUID_string(tmp_ctx, &guid));
4088 if (!retb) {
4089 ldb_asprintf_errstring(ldb, __location__
4090 ": Unable to add a formatted child to dn: %s",
4091 ldb_dn_get_linearized(new_dn));
4092 talloc_free(tmp_ctx);
4093 return LDB_ERR_OPERATIONS_ERROR;
4096 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
4097 if (ret != LDB_SUCCESS) {
4098 ldb_asprintf_errstring(ldb, __location__
4099 ": Failed to add isDeleted string to the msg");
4100 talloc_free(tmp_ctx);
4101 return ret;
4103 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4104 } else {
4106 * No matter what has happened with other renames etc, try again to
4107 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4110 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4111 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4112 if (!retb) {
4113 ldb_asprintf_errstring(ldb, __location__
4114 ": Unable to add a prepare rdn of %s",
4115 ldb_dn_get_linearized(rdn));
4116 talloc_free(tmp_ctx);
4117 return LDB_ERR_OPERATIONS_ERROR;
4119 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4121 retb = ldb_dn_add_child(new_dn, rdn);
4122 if (!retb) {
4123 ldb_asprintf_errstring(ldb, __location__
4124 ": Unable to add rdn %s to base dn: %s",
4125 ldb_dn_get_linearized(rdn),
4126 ldb_dn_get_linearized(new_dn));
4127 talloc_free(tmp_ctx);
4128 return LDB_ERR_OPERATIONS_ERROR;
4133 now we need to modify the object in the following ways:
4135 - add isDeleted=TRUE
4136 - update rDN and name, with new rDN
4137 - remove linked attributes
4138 - remove objectCategory and sAMAccountType
4139 - remove attribs not on the preserved list
4140 - preserved if in above list, or is rDN
4141 - remove all linked attribs from this object
4142 - remove all links from other objects to this object
4143 - add lastKnownParent
4144 - update replPropertyMetaData?
4146 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4149 if (deletion_state == OBJECT_NOT_DELETED) {
4150 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4151 char *parent_dn_str = NULL;
4153 /* we need the storage form of the parent GUID */
4154 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4155 parent_dn, NULL,
4156 DSDB_FLAG_NEXT_MODULE |
4157 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4158 DSDB_SEARCH_REVEAL_INTERNALS|
4159 DSDB_SEARCH_SHOW_RECYCLED, req);
4160 if (ret != LDB_SUCCESS) {
4161 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4162 "repmd_delete: Failed to %s %s, "
4163 "because we failed to find it's parent (%s): %s",
4164 re_delete ? "re-delete" : "delete",
4165 ldb_dn_get_linearized(old_dn),
4166 ldb_dn_get_linearized(parent_dn),
4167 ldb_errstring(ldb_module_get_ctx(module)));
4168 talloc_free(tmp_ctx);
4169 return ret;
4173 * Now we can use the DB version,
4174 * it will have the extended DN info in it
4176 parent_dn = parent_res->msgs[0]->dn;
4177 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4178 parent_dn,
4180 if (parent_dn_str == NULL) {
4181 talloc_free(tmp_ctx);
4182 return ldb_module_oom(module);
4185 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4186 parent_dn_str);
4187 if (ret != LDB_SUCCESS) {
4188 ldb_asprintf_errstring(ldb, __location__
4189 ": Failed to add lastKnownParent "
4190 "string when deleting %s",
4191 ldb_dn_get_linearized(old_dn));
4192 talloc_free(tmp_ctx);
4193 return ret;
4195 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4197 if (next_deletion_state == OBJECT_DELETED) {
4198 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4199 if (ret != LDB_SUCCESS) {
4200 ldb_asprintf_errstring(ldb, __location__
4201 ": Failed to add msDS-LastKnownRDN "
4202 "string when deleting %s",
4203 ldb_dn_get_linearized(old_dn));
4204 talloc_free(tmp_ctx);
4205 return ret;
4207 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
4211 switch (next_deletion_state) {
4213 case OBJECT_RECYCLED:
4214 case OBJECT_TOMBSTONE:
4217 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4218 * describes what must be removed from a tombstone
4219 * object
4221 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4222 * describes what must be removed from a recycled
4223 * object
4228 * we also mark it as recycled, meaning this object can't be
4229 * recovered (we are stripping its attributes).
4230 * This is done only if we have this schema object of course ...
4231 * This behavior is identical to the one of Windows 2008R2 which
4232 * always set the isRecycled attribute, even if the recycle-bin is
4233 * not activated and what ever the forest level is.
4235 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4236 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
4237 if (ret != LDB_SUCCESS) {
4238 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4239 ldb_module_oom(module);
4240 talloc_free(tmp_ctx);
4241 return ret;
4243 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4246 replmd_private = talloc_get_type(ldb_module_get_private(module),
4247 struct replmd_private);
4248 /* work out which of the old attributes we will be removing */
4249 for (i=0; i<old_msg->num_elements; i++) {
4250 const struct dsdb_attribute *sa;
4251 el = &old_msg->elements[i];
4252 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4253 if (!sa) {
4254 talloc_free(tmp_ctx);
4255 return LDB_ERR_OPERATIONS_ERROR;
4257 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4258 /* don't remove the rDN */
4259 continue;
4261 if (sa->linkID & 1) {
4263 we have a backlink in this object
4264 that needs to be removed. We're not
4265 allowed to remove it directly
4266 however, so we instead setup a
4267 modify to delete the corresponding
4268 forward link
4270 ret = replmd_delete_remove_link(module, schema,
4271 replmd_private,
4272 old_dn, &guid,
4273 el, sa, req);
4274 if (ret != LDB_SUCCESS) {
4275 const char *old_dn_str
4276 = ldb_dn_get_linearized(old_dn);
4277 ldb_asprintf_errstring(ldb,
4278 __location__
4279 ": Failed to remove backlink of "
4280 "%s when deleting %s: %s",
4281 el->name,
4282 old_dn_str,
4283 ldb_errstring(ldb));
4284 talloc_free(tmp_ctx);
4285 return LDB_ERR_OPERATIONS_ERROR;
4287 /* now we continue, which means we
4288 won't remove this backlink
4289 directly
4291 continue;
4292 } else if (sa->linkID == 0) {
4293 if (ldb_attr_in_list(preserved_attrs, el->name)) {
4294 continue;
4296 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4297 continue;
4299 } else {
4301 * Ensure that we tell the modification to vanish any linked
4302 * attributes (not simply mark them as isDeleted = TRUE)
4304 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4306 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4307 if (ret != LDB_SUCCESS) {
4308 talloc_free(tmp_ctx);
4309 ldb_module_oom(module);
4310 return ret;
4314 break;
4316 case OBJECT_DELETED:
4318 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4319 * describes what must be removed from a deleted
4320 * object
4323 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4324 if (ret != LDB_SUCCESS) {
4325 talloc_free(tmp_ctx);
4326 ldb_module_oom(module);
4327 return ret;
4330 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4331 if (ret != LDB_SUCCESS) {
4332 talloc_free(tmp_ctx);
4333 ldb_module_oom(module);
4334 return ret;
4337 break;
4339 default:
4340 break;
4343 if (deletion_state == OBJECT_NOT_DELETED) {
4344 const struct dsdb_attribute *sa;
4346 /* work out what the new rdn value is, for updating the
4347 rDN and name fields */
4348 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4349 if (new_rdn_value == NULL) {
4350 talloc_free(tmp_ctx);
4351 return ldb_operr(ldb);
4354 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4355 if (!sa) {
4356 talloc_free(tmp_ctx);
4357 return LDB_ERR_OPERATIONS_ERROR;
4360 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4361 &el);
4362 if (ret != LDB_SUCCESS) {
4363 talloc_free(tmp_ctx);
4364 return ret;
4366 el->flags = LDB_FLAG_MOD_REPLACE;
4368 el = ldb_msg_find_element(old_msg, "name");
4369 if (el) {
4370 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4371 if (ret != LDB_SUCCESS) {
4372 talloc_free(tmp_ctx);
4373 return ret;
4375 el->flags = LDB_FLAG_MOD_REPLACE;
4380 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4385 * No matter what has happned with other renames, try again to
4386 * get this to be under the deleted DN.
4388 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4389 /* now rename onto the new DN */
4390 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4391 if (ret != LDB_SUCCESS){
4392 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4393 ldb_dn_get_linearized(old_dn),
4394 ldb_dn_get_linearized(new_dn),
4395 ldb_errstring(ldb)));
4396 talloc_free(tmp_ctx);
4397 return ret;
4399 msg->dn = new_dn;
4402 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4403 if (ret != LDB_SUCCESS) {
4404 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
4405 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
4406 talloc_free(tmp_ctx);
4407 return ret;
4410 talloc_free(tmp_ctx);
4412 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4415 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4417 return replmd_delete_internals(module, req, false);
4421 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4423 return ret;
4426 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4428 int ret = LDB_ERR_OTHER;
4429 /* TODO: do some error mapping */
4431 /* Let the caller know the full WERROR */
4432 ar->objs->error = status;
4434 return ret;
4438 static struct replPropertyMetaData1 *
4439 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4440 enum drsuapi_DsAttributeId attid)
4442 uint32_t i;
4443 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4445 for (i = 0; i < rpmd_ctr->count; i++) {
4446 if (rpmd_ctr->array[i].attid == attid) {
4447 return &rpmd_ctr->array[i];
4450 return NULL;
4455 return true if an update is newer than an existing entry
4456 see section 5.11 of MS-ADTS
4458 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4459 const struct GUID *update_invocation_id,
4460 uint32_t current_version,
4461 uint32_t update_version,
4462 NTTIME current_change_time,
4463 NTTIME update_change_time)
4465 if (update_version != current_version) {
4466 return update_version > current_version;
4468 if (update_change_time != current_change_time) {
4469 return update_change_time > current_change_time;
4471 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4474 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4475 struct replPropertyMetaData1 *new_m)
4477 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4478 &new_m->originating_invocation_id,
4479 cur_m->version,
4480 new_m->version,
4481 cur_m->originating_change_time,
4482 new_m->originating_change_time);
4485 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4486 struct replPropertyMetaData1 *cur_m,
4487 struct replPropertyMetaData1 *new_m)
4489 bool cmp;
4492 * If the new replPropertyMetaData entry for this attribute is
4493 * not provided (this happens in the case where we look for
4494 * ATTID_name, but the name was not changed), then the local
4495 * state is clearly still current, as the remote
4496 * server didn't send it due to being older the high watermark
4497 * USN we sent.
4499 if (new_m == NULL) {
4500 return false;
4503 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4505 * if we compare equal then do an
4506 * update. This is used when a client
4507 * asks for a FULL_SYNC, and can be
4508 * used to recover a corrupt
4509 * replica.
4511 * This call is a bit tricky, what we
4512 * are doing it turning the 'is_newer'
4513 * call into a 'not is older' by
4514 * swapping cur_m and new_m, and negating the
4515 * outcome.
4517 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4518 cur_m);
4519 } else {
4520 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4521 new_m);
4523 return cmp;
4528 form a conflict DN
4530 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
4532 const struct ldb_val *rdn_val;
4533 const char *rdn_name;
4534 struct ldb_dn *new_dn;
4536 rdn_val = ldb_dn_get_rdn_val(dn);
4537 rdn_name = ldb_dn_get_rdn_name(dn);
4538 if (!rdn_val || !rdn_name) {
4539 return NULL;
4542 new_dn = ldb_dn_copy(mem_ctx, dn);
4543 if (!new_dn) {
4544 return NULL;
4547 if (!ldb_dn_remove_child_components(new_dn, 1)) {
4548 return NULL;
4551 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
4552 rdn_name,
4553 ldb_dn_escape_value(new_dn, *rdn_val),
4554 GUID_string(new_dn, guid))) {
4555 return NULL;
4558 return new_dn;
4563 perform a modify operation which sets the rDN and name attributes to
4564 their current values. This has the effect of changing these
4565 attributes to have been last updated by the current DC. This is
4566 needed to ensure that renames performed as part of conflict
4567 resolution are propogated to other DCs
4569 static int replmd_name_modify(struct replmd_replicated_request *ar,
4570 struct ldb_request *req, struct ldb_dn *dn)
4572 struct ldb_message *msg;
4573 const char *rdn_name;
4574 const struct ldb_val *rdn_val;
4575 const struct dsdb_attribute *rdn_attr;
4576 int ret;
4578 msg = ldb_msg_new(req);
4579 if (msg == NULL) {
4580 goto failed;
4582 msg->dn = dn;
4584 rdn_name = ldb_dn_get_rdn_name(dn);
4585 if (rdn_name == NULL) {
4586 goto failed;
4589 /* normalize the rdn attribute name */
4590 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
4591 if (rdn_attr == NULL) {
4592 goto failed;
4594 rdn_name = rdn_attr->lDAPDisplayName;
4596 rdn_val = ldb_dn_get_rdn_val(dn);
4597 if (rdn_val == NULL) {
4598 goto failed;
4601 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4602 goto failed;
4604 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
4605 goto failed;
4607 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4608 goto failed;
4610 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
4611 goto failed;
4615 * We have to mark this as a replicated update otherwise
4616 * schema_data may reject a rename in the schema partition
4619 ret = dsdb_module_modify(ar->module, msg,
4620 DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
4621 req);
4622 if (ret != LDB_SUCCESS) {
4623 DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
4624 ldb_dn_get_linearized(dn),
4625 ldb_errstring(ldb_module_get_ctx(ar->module))));
4626 return ret;
4629 talloc_free(msg);
4631 return LDB_SUCCESS;
4633 failed:
4634 talloc_free(msg);
4635 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
4636 ldb_dn_get_linearized(dn)));
4637 return LDB_ERR_OPERATIONS_ERROR;
4642 callback for conflict DN handling where we have renamed the incoming
4643 record. After renaming it, we need to ensure the change of name and
4644 rDN for the incoming record is seen as an originating update by this DC.
4646 This also handles updating lastKnownParent for entries sent to lostAndFound
4648 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
4650 struct replmd_replicated_request *ar =
4651 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4652 struct ldb_dn *conflict_dn = NULL;
4653 int ret;
4655 if (ares->error != LDB_SUCCESS) {
4656 /* call the normal callback for everything except success */
4657 return replmd_op_callback(req, ares);
4660 switch (req->operation) {
4661 case LDB_ADD:
4662 conflict_dn = req->op.add.message->dn;
4663 break;
4664 case LDB_MODIFY:
4665 conflict_dn = req->op.mod.message->dn;
4666 break;
4667 default:
4668 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
4671 /* perform a modify of the rDN and name of the record */
4672 ret = replmd_name_modify(ar, req, conflict_dn);
4673 if (ret != LDB_SUCCESS) {
4674 ares->error = ret;
4675 return replmd_op_callback(req, ares);
4678 if (ar->objs->objects[ar->index_current].last_known_parent) {
4679 struct ldb_message *msg = ldb_msg_new(req);
4680 if (msg == NULL) {
4681 ldb_module_oom(ar->module);
4682 return LDB_ERR_OPERATIONS_ERROR;
4685 msg->dn = req->op.add.message->dn;
4687 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4688 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
4689 if (ret != LDB_SUCCESS) {
4690 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
4691 ldb_module_oom(ar->module);
4692 return ret;
4694 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
4696 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
4697 if (ret != LDB_SUCCESS) {
4698 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
4699 ldb_dn_get_linearized(msg->dn),
4700 ldb_errstring(ldb_module_get_ctx(ar->module))));
4701 return ret;
4703 TALLOC_FREE(msg);
4706 return replmd_op_callback(req, ares);
4710 callback for replmd_replicated_apply_add()
4711 This copes with the creation of conflict records in the case where
4712 the DN exists, but with a different objectGUID
4714 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))
4716 struct ldb_dn *conflict_dn;
4717 struct replmd_replicated_request *ar =
4718 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4719 struct ldb_result *res;
4720 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4721 int ret;
4722 const struct ldb_val *omd_value;
4723 struct replPropertyMetaDataBlob omd, *rmd;
4724 enum ndr_err_code ndr_err;
4725 bool rename_incoming_record, rodc;
4726 struct replPropertyMetaData1 *rmd_name, *omd_name;
4727 struct ldb_message *msg;
4728 struct ldb_request *down_req = NULL;
4730 /* call the normal callback for success */
4731 if (ares->error == LDB_SUCCESS) {
4732 return callback(req, ares);
4736 * we have a conflict, and need to decide if we will keep the
4737 * new record or the old record
4740 msg = ar->objs->objects[ar->index_current].msg;
4741 conflict_dn = msg->dn;
4743 /* For failures other than conflicts, fail the whole operation here */
4744 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4745 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
4746 ldb_dn_get_linearized(conflict_dn),
4747 ldb_errstring(ldb_module_get_ctx(ar->module)));
4749 return ldb_module_done(ar->req, NULL, NULL,
4750 LDB_ERR_OPERATIONS_ERROR);
4753 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4754 if (ret != LDB_SUCCESS) {
4755 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)));
4756 return ldb_module_done(ar->req, NULL, NULL,
4757 LDB_ERR_OPERATIONS_ERROR);
4761 if (rodc) {
4763 * We are on an RODC, or were a GC for this
4764 * partition, so we have to fail this until
4765 * someone who owns the partition sorts it
4766 * out
4768 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4769 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
4770 " - We must fail the operation until a master for this partition resolves the conflict",
4771 ldb_dn_get_linearized(conflict_dn));
4772 goto failed;
4776 * first we need the replPropertyMetaData attribute from the
4777 * local, conflicting record
4779 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
4780 attrs,
4781 DSDB_FLAG_NEXT_MODULE |
4782 DSDB_SEARCH_SHOW_DELETED |
4783 DSDB_SEARCH_SHOW_RECYCLED, req);
4784 if (ret != LDB_SUCCESS) {
4785 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4786 ldb_dn_get_linearized(conflict_dn)));
4787 goto failed;
4790 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4791 if (omd_value == NULL) {
4792 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4793 ldb_dn_get_linearized(conflict_dn)));
4794 goto failed;
4797 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4798 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4799 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4800 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4801 ldb_dn_get_linearized(conflict_dn)));
4802 goto failed;
4805 rmd = ar->objs->objects[ar->index_current].meta_data;
4808 * we decide which is newer based on the RPMD on the name
4809 * attribute. See [MS-DRSR] ResolveNameConflict.
4811 * We expect omd_name to be present, as this is from a local
4812 * search, but while rmd_name should have been given to us by
4813 * the remote server, if it is missing we just prefer the
4814 * local name in
4815 * replmd_replPropertyMetaData1_new_should_be_taken()
4817 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4818 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4819 if (!omd_name) {
4820 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4821 ldb_dn_get_linearized(conflict_dn)));
4822 goto failed;
4826 * Should we preserve the current record, and so rename the
4827 * incoming record to be a conflict?
4829 rename_incoming_record
4830 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
4831 omd_name, rmd_name);
4833 if (rename_incoming_record) {
4834 struct GUID guid;
4835 struct ldb_dn *new_dn;
4837 guid = samdb_result_guid(msg, "objectGUID");
4838 if (GUID_all_zero(&guid)) {
4839 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
4840 ldb_dn_get_linearized(conflict_dn)));
4841 goto failed;
4843 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4844 if (new_dn == NULL) {
4845 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4846 ldb_dn_get_linearized(conflict_dn)));
4847 goto failed;
4850 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
4851 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4853 /* re-submit the request, but with the new DN */
4854 callback = replmd_op_name_modify_callback;
4855 msg->dn = new_dn;
4856 } else {
4857 /* we are renaming the existing record */
4858 struct GUID guid;
4859 struct ldb_dn *new_dn;
4861 guid = samdb_result_guid(res->msgs[0], "objectGUID");
4862 if (GUID_all_zero(&guid)) {
4863 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
4864 ldb_dn_get_linearized(conflict_dn)));
4865 goto failed;
4868 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4869 if (new_dn == NULL) {
4870 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4871 ldb_dn_get_linearized(conflict_dn)));
4872 goto failed;
4875 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4876 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4878 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
4879 DSDB_FLAG_OWN_MODULE, req);
4880 if (ret != LDB_SUCCESS) {
4881 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
4882 ldb_dn_get_linearized(conflict_dn),
4883 ldb_dn_get_linearized(new_dn),
4884 ldb_errstring(ldb_module_get_ctx(ar->module))));
4885 goto failed;
4889 * now we need to ensure that the rename is seen as an
4890 * originating update. We do that with a modify.
4892 ret = replmd_name_modify(ar, req, new_dn);
4893 if (ret != LDB_SUCCESS) {
4894 goto failed;
4897 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
4898 ldb_dn_get_linearized(req->op.add.message->dn)));
4901 ret = ldb_build_add_req(&down_req,
4902 ldb_module_get_ctx(ar->module),
4903 req,
4904 msg,
4905 ar->controls,
4907 callback,
4908 req);
4909 if (ret != LDB_SUCCESS) {
4910 goto failed;
4912 LDB_REQ_SET_LOCATION(down_req);
4914 /* current partition control needed by "repmd_op_callback" */
4915 ret = ldb_request_add_control(down_req,
4916 DSDB_CONTROL_CURRENT_PARTITION_OID,
4917 false, NULL);
4918 if (ret != LDB_SUCCESS) {
4919 return replmd_replicated_request_error(ar, ret);
4922 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4923 /* this tells the partition module to make it a
4924 partial replica if creating an NC */
4925 ret = ldb_request_add_control(down_req,
4926 DSDB_CONTROL_PARTIAL_REPLICA,
4927 false, NULL);
4928 if (ret != LDB_SUCCESS) {
4929 return replmd_replicated_request_error(ar, ret);
4934 * Finally we re-run the add, otherwise the new record won't
4935 * exist, as we are here because of that exact failure!
4937 return ldb_next_request(ar->module, down_req);
4938 failed:
4940 /* on failure make the caller get the error. This means
4941 * replication will stop with an error, but there is not much
4942 * else we can do.
4944 return ldb_module_done(ar->req, NULL, NULL,
4945 ret);
4949 callback for replmd_replicated_apply_add()
4950 This copes with the creation of conflict records in the case where
4951 the DN exists, but with a different objectGUID
4953 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
4955 struct replmd_replicated_request *ar =
4956 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4958 if (ar->objs->objects[ar->index_current].last_known_parent) {
4959 /* This is like a conflict DN, where we put the object in LostAndFound
4960 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
4961 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
4964 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
4968 this is called when a new object comes in over DRS
4970 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
4972 struct ldb_context *ldb;
4973 struct ldb_request *change_req;
4974 enum ndr_err_code ndr_err;
4975 struct ldb_message *msg;
4976 struct replPropertyMetaDataBlob *md;
4977 struct ldb_val md_value;
4978 unsigned int i;
4979 int ret;
4980 bool remote_isDeleted = false;
4981 bool is_schema_nc;
4982 NTTIME now;
4983 time_t t = time(NULL);
4984 const struct ldb_val *rdn_val;
4985 struct replmd_private *replmd_private =
4986 talloc_get_type(ldb_module_get_private(ar->module),
4987 struct replmd_private);
4988 unix_to_nt_time(&now, t);
4990 ldb = ldb_module_get_ctx(ar->module);
4991 msg = ar->objs->objects[ar->index_current].msg;
4992 md = ar->objs->objects[ar->index_current].meta_data;
4993 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
4995 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4996 if (ret != LDB_SUCCESS) {
4997 return replmd_replicated_request_error(ar, ret);
5000 ret = dsdb_msg_add_guid(msg,
5001 &ar->objs->objects[ar->index_current].object_guid,
5002 "objectGUID");
5003 if (ret != LDB_SUCCESS) {
5004 return replmd_replicated_request_error(ar, ret);
5007 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5008 if (ret != LDB_SUCCESS) {
5009 return replmd_replicated_request_error(ar, ret);
5012 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
5013 if (ret != LDB_SUCCESS) {
5014 return replmd_replicated_request_error(ar, ret);
5017 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5018 if (ret != LDB_SUCCESS) {
5019 return replmd_replicated_request_error(ar, ret);
5022 /* remove any message elements that have zero values */
5023 for (i=0; i<msg->num_elements; i++) {
5024 struct ldb_message_element *el = &msg->elements[i];
5026 if (el->num_values == 0) {
5027 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5028 ldb_asprintf_errstring(ldb, __location__
5029 ": empty objectClass sent on %s, aborting replication\n",
5030 ldb_dn_get_linearized(msg->dn));
5031 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5034 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5035 el->name));
5036 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
5037 msg->num_elements--;
5038 i--;
5039 continue;
5043 if (DEBUGLVL(8)) {
5044 struct GUID_txt_buf guid_txt;
5046 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5047 LDB_CHANGETYPE_ADD,
5048 msg);
5049 DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5050 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5051 s));
5052 talloc_free(s);
5053 } else if (DEBUGLVL(4)) {
5054 struct GUID_txt_buf guid_txt;
5055 DEBUG(4, ("DRS replication add DN of %s is %s\n",
5056 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5057 ldb_dn_get_linearized(msg->dn)));
5059 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5060 "isDeleted", false);
5063 * the meta data array is already sorted by the caller, except
5064 * for the RDN, which needs to be added.
5068 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5069 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5070 md, ar, now, is_schema_nc,
5071 false);
5072 if (ret != LDB_SUCCESS) {
5073 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5074 return replmd_replicated_request_error(ar, ret);
5077 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5078 if (ret != LDB_SUCCESS) {
5079 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5080 return replmd_replicated_request_error(ar, ret);
5083 for (i=0; i < md->ctr.ctr1.count; i++) {
5084 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5086 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5087 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5088 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5089 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5090 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5092 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5093 if (ret != LDB_SUCCESS) {
5094 return replmd_replicated_request_error(ar, ret);
5097 replmd_ldb_message_sort(msg, ar->schema);
5099 if (!remote_isDeleted) {
5100 ret = dsdb_module_schedule_sd_propagation(ar->module,
5101 ar->objs->partition_dn,
5102 msg->dn, true);
5103 if (ret != LDB_SUCCESS) {
5104 return replmd_replicated_request_error(ar, ret);
5108 ar->isDeleted = remote_isDeleted;
5110 ret = ldb_build_add_req(&change_req,
5111 ldb,
5113 msg,
5114 ar->controls,
5116 replmd_op_add_callback,
5117 ar->req);
5118 LDB_REQ_SET_LOCATION(change_req);
5119 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5121 /* current partition control needed by "repmd_op_callback" */
5122 ret = ldb_request_add_control(change_req,
5123 DSDB_CONTROL_CURRENT_PARTITION_OID,
5124 false, NULL);
5125 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5127 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5128 /* this tells the partition module to make it a
5129 partial replica if creating an NC */
5130 ret = ldb_request_add_control(change_req,
5131 DSDB_CONTROL_PARTIAL_REPLICA,
5132 false, NULL);
5133 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5136 return ldb_next_request(ar->module, change_req);
5139 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5140 struct ldb_reply *ares)
5142 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5143 struct replmd_replicated_request);
5144 int ret;
5146 if (!ares) {
5147 return ldb_module_done(ar->req, NULL, NULL,
5148 LDB_ERR_OPERATIONS_ERROR);
5152 * The error NO_SUCH_OBJECT is not expected, unless the search
5153 * base is the partition DN, and that case doesn't happen here
5154 * because then we wouldn't get a parent_guid_value in any
5155 * case.
5157 if (ares->error != LDB_SUCCESS) {
5158 return ldb_module_done(ar->req, ares->controls,
5159 ares->response, ares->error);
5162 switch (ares->type) {
5163 case LDB_REPLY_ENTRY:
5165 struct ldb_message *parent_msg = ares->message;
5166 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5167 struct ldb_dn *parent_dn;
5168 int comp_num;
5170 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5171 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5172 /* Per MS-DRSR 4.1.10.6.10
5173 * FindBestParentObject we need to move this
5174 * new object under a deleted object to
5175 * lost-and-found */
5176 struct ldb_dn *nc_root;
5178 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5179 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5180 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5181 "No suitable NC root found for %s. "
5182 "We need to move this object because parent object %s "
5183 "is deleted, but this object is not.",
5184 ldb_dn_get_linearized(msg->dn),
5185 ldb_dn_get_linearized(parent_msg->dn));
5186 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5187 } else if (ret != LDB_SUCCESS) {
5188 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5189 "Unable to find NC root for %s: %s. "
5190 "We need to move this object because parent object %s "
5191 "is deleted, but this object is not.",
5192 ldb_dn_get_linearized(msg->dn),
5193 ldb_errstring(ldb_module_get_ctx(ar->module)),
5194 ldb_dn_get_linearized(parent_msg->dn));
5195 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5198 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5199 nc_root,
5200 DS_GUID_LOSTANDFOUND_CONTAINER,
5201 &parent_dn);
5202 if (ret != LDB_SUCCESS) {
5203 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5204 "Unable to find LostAndFound Container for %s "
5205 "in partition %s: %s. "
5206 "We need to move this object because parent object %s "
5207 "is deleted, but this object is not.",
5208 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5209 ldb_errstring(ldb_module_get_ctx(ar->module)),
5210 ldb_dn_get_linearized(parent_msg->dn));
5211 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5213 ar->objs->objects[ar->index_current].last_known_parent
5214 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5216 } else {
5217 parent_dn
5218 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5221 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5223 comp_num = ldb_dn_get_comp_num(msg->dn);
5224 if (comp_num > 1) {
5225 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5226 talloc_free(ares);
5227 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5230 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5231 talloc_free(ares);
5232 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5234 break;
5236 case LDB_REPLY_REFERRAL:
5237 /* we ignore referrals */
5238 break;
5240 case LDB_REPLY_DONE:
5242 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5243 struct GUID_txt_buf str_buf;
5244 if (ar->search_msg != NULL) {
5245 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5246 "No parent with GUID %s found for object locally known as %s",
5247 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5248 ldb_dn_get_linearized(ar->search_msg->dn));
5249 } else {
5250 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5251 "No parent with GUID %s found for object remotely known as %s",
5252 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5253 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5257 * This error code is really important, as it
5258 * is the flag back to the callers to retry
5259 * this with DRSUAPI_DRS_GET_ANC, and so get
5260 * the parent objects before the child
5261 * objects
5263 return ldb_module_done(ar->req, NULL, NULL,
5264 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5267 if (ar->search_msg != NULL) {
5268 ret = replmd_replicated_apply_merge(ar);
5269 } else {
5270 ret = replmd_replicated_apply_add(ar);
5272 if (ret != LDB_SUCCESS) {
5273 return ldb_module_done(ar->req, NULL, NULL, ret);
5277 talloc_free(ares);
5278 return LDB_SUCCESS;
5282 * Look for the parent object, so we put the new object in the right
5283 * place This is akin to NameObject in MS-DRSR - this routine and the
5284 * callbacks find the right parent name, and correct name for this
5285 * object
5288 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5290 struct ldb_context *ldb;
5291 int ret;
5292 char *tmp_str;
5293 char *filter;
5294 struct ldb_request *search_req;
5295 static const char *attrs[] = {"isDeleted", NULL};
5296 struct GUID_txt_buf guid_str_buf;
5298 ldb = ldb_module_get_ctx(ar->module);
5300 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5301 if (ar->search_msg != NULL) {
5302 return replmd_replicated_apply_merge(ar);
5303 } else {
5304 return replmd_replicated_apply_add(ar);
5308 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5309 &guid_str_buf);
5311 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5312 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5314 ret = ldb_build_search_req(&search_req,
5315 ldb,
5317 ar->objs->partition_dn,
5318 LDB_SCOPE_SUBTREE,
5319 filter,
5320 attrs,
5321 NULL,
5323 replmd_replicated_apply_search_for_parent_callback,
5324 ar->req);
5325 LDB_REQ_SET_LOCATION(search_req);
5327 ret = dsdb_request_add_controls(search_req,
5328 DSDB_SEARCH_SHOW_RECYCLED|
5329 DSDB_SEARCH_SHOW_DELETED|
5330 DSDB_SEARCH_SHOW_EXTENDED_DN);
5331 if (ret != LDB_SUCCESS) {
5332 return ret;
5335 return ldb_next_request(ar->module, search_req);
5339 handle renames that come in over DRS replication
5341 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5342 struct ldb_message *msg,
5343 struct ldb_request *parent,
5344 bool *renamed)
5346 int ret;
5347 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5348 struct ldb_result *res;
5349 struct ldb_dn *conflict_dn;
5350 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5351 const struct ldb_val *omd_value;
5352 struct replPropertyMetaDataBlob omd, *rmd;
5353 enum ndr_err_code ndr_err;
5354 bool rename_incoming_record, rodc;
5355 struct replPropertyMetaData1 *rmd_name, *omd_name;
5356 struct ldb_dn *new_dn;
5357 struct GUID guid;
5359 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5360 ldb_dn_get_linearized(ar->search_msg->dn),
5361 ldb_dn_get_linearized(msg->dn)));
5364 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5365 DSDB_FLAG_NEXT_MODULE, ar->req);
5366 if (ret == LDB_SUCCESS) {
5367 talloc_free(tmp_ctx);
5368 *renamed = true;
5369 return ret;
5372 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5373 talloc_free(tmp_ctx);
5374 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5375 ldb_dn_get_linearized(ar->search_msg->dn),
5376 ldb_dn_get_linearized(msg->dn),
5377 ldb_errstring(ldb_module_get_ctx(ar->module)));
5378 return ret;
5381 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5382 if (ret != LDB_SUCCESS) {
5383 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5384 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5385 ldb_errstring(ldb_module_get_ctx(ar->module)));
5386 return LDB_ERR_OPERATIONS_ERROR;
5389 * we have a conflict, and need to decide if we will keep the
5390 * new record or the old record
5393 conflict_dn = msg->dn;
5395 if (rodc) {
5397 * We are on an RODC, or were a GC for this
5398 * partition, so we have to fail this until
5399 * someone who owns the partition sorts it
5400 * out
5402 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5403 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5404 " - We must fail the operation until a master for this partition resolves the conflict",
5405 ldb_dn_get_linearized(conflict_dn));
5406 goto failed;
5410 * first we need the replPropertyMetaData attribute from the
5411 * old record
5413 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
5414 attrs,
5415 DSDB_FLAG_NEXT_MODULE |
5416 DSDB_SEARCH_SHOW_DELETED |
5417 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5418 if (ret != LDB_SUCCESS) {
5419 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5420 ldb_dn_get_linearized(conflict_dn)));
5421 goto failed;
5424 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5425 if (omd_value == NULL) {
5426 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5427 ldb_dn_get_linearized(conflict_dn)));
5428 goto failed;
5431 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5432 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5433 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5434 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5435 ldb_dn_get_linearized(conflict_dn)));
5436 goto failed;
5439 rmd = ar->objs->objects[ar->index_current].meta_data;
5442 * we decide which is newer based on the RPMD on the name
5443 * attribute. See [MS-DRSR] ResolveNameConflict.
5445 * We expect omd_name to be present, as this is from a local
5446 * search, but while rmd_name should have been given to us by
5447 * the remote server, if it is missing we just prefer the
5448 * local name in
5449 * replmd_replPropertyMetaData1_new_should_be_taken()
5451 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5452 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5453 if (!omd_name) {
5454 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5455 ldb_dn_get_linearized(conflict_dn)));
5456 goto failed;
5460 * Should we preserve the current record, and so rename the
5461 * incoming record to be a conflict?
5463 rename_incoming_record =
5464 !replmd_replPropertyMetaData1_new_should_be_taken(
5465 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5466 omd_name, rmd_name);
5468 if (rename_incoming_record) {
5470 new_dn = replmd_conflict_dn(msg, msg->dn,
5471 &ar->objs->objects[ar->index_current].object_guid);
5472 if (new_dn == NULL) {
5473 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5474 "Failed to form conflict DN for %s\n",
5475 ldb_dn_get_linearized(msg->dn));
5477 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5480 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
5481 DSDB_FLAG_NEXT_MODULE, ar->req);
5482 if (ret != LDB_SUCCESS) {
5483 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5484 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5485 ldb_dn_get_linearized(conflict_dn),
5486 ldb_dn_get_linearized(ar->search_msg->dn),
5487 ldb_dn_get_linearized(new_dn),
5488 ldb_errstring(ldb_module_get_ctx(ar->module)));
5489 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5492 msg->dn = new_dn;
5493 *renamed = true;
5494 return LDB_SUCCESS;
5497 /* we are renaming the existing record */
5499 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5500 if (GUID_all_zero(&guid)) {
5501 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5502 ldb_dn_get_linearized(conflict_dn)));
5503 goto failed;
5506 new_dn = replmd_conflict_dn(tmp_ctx, conflict_dn, &guid);
5507 if (new_dn == NULL) {
5508 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5509 ldb_dn_get_linearized(conflict_dn)));
5510 goto failed;
5513 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5514 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5516 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5517 DSDB_FLAG_OWN_MODULE, ar->req);
5518 if (ret != LDB_SUCCESS) {
5519 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5520 ldb_dn_get_linearized(conflict_dn),
5521 ldb_dn_get_linearized(new_dn),
5522 ldb_errstring(ldb_module_get_ctx(ar->module))));
5523 goto failed;
5527 * now we need to ensure that the rename is seen as an
5528 * originating update. We do that with a modify.
5530 ret = replmd_name_modify(ar, ar->req, new_dn);
5531 if (ret != LDB_SUCCESS) {
5532 goto failed;
5535 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
5536 ldb_dn_get_linearized(ar->search_msg->dn),
5537 ldb_dn_get_linearized(msg->dn)));
5540 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5541 DSDB_FLAG_NEXT_MODULE, ar->req);
5542 if (ret != LDB_SUCCESS) {
5543 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
5544 ldb_dn_get_linearized(ar->search_msg->dn),
5545 ldb_dn_get_linearized(msg->dn),
5546 ldb_errstring(ldb_module_get_ctx(ar->module))));
5547 goto failed;
5549 failed:
5552 * On failure make the caller get the error
5553 * This means replication will stop with an error,
5554 * but there is not much else we can do. In the
5555 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
5556 * needed.
5559 talloc_free(tmp_ctx);
5560 return ret;
5564 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
5566 struct ldb_context *ldb;
5567 struct ldb_request *change_req;
5568 enum ndr_err_code ndr_err;
5569 struct ldb_message *msg;
5570 struct replPropertyMetaDataBlob *rmd;
5571 struct replPropertyMetaDataBlob omd;
5572 const struct ldb_val *omd_value;
5573 struct replPropertyMetaDataBlob nmd;
5574 struct ldb_val nmd_value;
5575 struct GUID remote_parent_guid;
5576 unsigned int i;
5577 uint32_t j,ni=0;
5578 unsigned int removed_attrs = 0;
5579 int ret;
5580 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
5581 bool isDeleted = false;
5582 bool local_isDeleted = false;
5583 bool remote_isDeleted = false;
5584 bool take_remote_isDeleted = false;
5585 bool sd_updated = false;
5586 bool renamed = false;
5587 bool is_schema_nc = false;
5588 NTSTATUS nt_status;
5589 const struct ldb_val *old_rdn, *new_rdn;
5590 struct replmd_private *replmd_private =
5591 talloc_get_type(ldb_module_get_private(ar->module),
5592 struct replmd_private);
5593 NTTIME now;
5594 time_t t = time(NULL);
5595 unix_to_nt_time(&now, t);
5597 ldb = ldb_module_get_ctx(ar->module);
5598 msg = ar->objs->objects[ar->index_current].msg;
5600 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5602 rmd = ar->objs->objects[ar->index_current].meta_data;
5603 ZERO_STRUCT(omd);
5604 omd.version = 1;
5606 /* find existing meta data */
5607 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5608 if (omd_value) {
5609 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5610 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5611 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5612 nt_status = ndr_map_error2ntstatus(ndr_err);
5613 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5616 if (omd.version != 1) {
5617 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5621 if (DEBUGLVL(8)) {
5622 struct GUID_txt_buf guid_txt;
5624 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5625 LDB_CHANGETYPE_MODIFY, msg);
5626 DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
5627 "%s\n"
5628 "%s\n",
5629 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5631 ndr_print_struct_string(s,
5632 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5633 "existing replPropertyMetaData",
5634 &omd),
5635 ndr_print_struct_string(s,
5636 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5637 "incoming replPropertyMetaData",
5638 rmd)));
5639 talloc_free(s);
5640 } else if (DEBUGLVL(4)) {
5641 struct GUID_txt_buf guid_txt;
5643 DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
5644 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5645 &guid_txt),
5646 ldb_dn_get_linearized(msg->dn)));
5649 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
5650 "isDeleted", false);
5651 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5652 "isDeleted", false);
5655 * Fill in the remote_parent_guid with the GUID or an all-zero
5656 * GUID.
5658 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
5659 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
5660 } else {
5661 remote_parent_guid = GUID_zero();
5665 * To ensure we follow a complex rename chain around, we have
5666 * to confirm that the DN is the same (mostly to confirm the
5667 * RDN) and the parentGUID is the same.
5669 * This ensures we keep things under the correct parent, which
5670 * replmd_replicated_handle_rename() will do.
5673 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
5674 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
5675 ret = LDB_SUCCESS;
5676 } else {
5678 * handle renames, even just by case that come in over
5679 * DRS. Changes in the parent DN don't hit us here,
5680 * because the search for a parent will clean up those
5681 * components.
5683 * We also have already filtered out the case where
5684 * the peer has an older name to what we have (see
5685 * replmd_replicated_apply_search_callback())
5687 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
5690 if (ret != LDB_SUCCESS) {
5691 ldb_debug(ldb, LDB_DEBUG_FATAL,
5692 "replmd_replicated_request rename %s => %s failed - %s\n",
5693 ldb_dn_get_linearized(ar->search_msg->dn),
5694 ldb_dn_get_linearized(msg->dn),
5695 ldb_errstring(ldb));
5696 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5699 if (renamed == true) {
5701 * Set the callback to one that will fix up the name
5702 * metadata on the new conflict DN
5704 callback = replmd_op_name_modify_callback;
5707 ZERO_STRUCT(nmd);
5708 nmd.version = 1;
5709 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
5710 nmd.ctr.ctr1.array = talloc_array(ar,
5711 struct replPropertyMetaData1,
5712 nmd.ctr.ctr1.count);
5713 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5715 /* first copy the old meta data */
5716 for (i=0; i < omd.ctr.ctr1.count; i++) {
5717 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
5718 ni++;
5721 ar->seq_num = 0;
5722 /* now merge in the new meta data */
5723 for (i=0; i < rmd->ctr.ctr1.count; i++) {
5724 bool found = false;
5726 for (j=0; j < ni; j++) {
5727 bool cmp;
5729 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
5730 continue;
5733 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
5734 ar->objs->dsdb_repl_flags,
5735 &nmd.ctr.ctr1.array[j],
5736 &rmd->ctr.ctr1.array[i]);
5737 if (cmp) {
5738 /* replace the entry */
5739 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
5740 if (ar->seq_num == 0) {
5741 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5742 if (ret != LDB_SUCCESS) {
5743 return replmd_replicated_request_error(ar, ret);
5746 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
5747 switch (nmd.ctr.ctr1.array[j].attid) {
5748 case DRSUAPI_ATTID_ntSecurityDescriptor:
5749 sd_updated = true;
5750 break;
5751 case DRSUAPI_ATTID_isDeleted:
5752 take_remote_isDeleted = true;
5753 break;
5754 default:
5755 break;
5757 found = true;
5758 break;
5761 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
5762 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
5763 msg->elements[i-removed_attrs].name,
5764 ldb_dn_get_linearized(msg->dn),
5765 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
5768 /* we don't want to apply this change so remove the attribute */
5769 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
5770 removed_attrs++;
5772 found = true;
5773 break;
5776 if (found) continue;
5778 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
5779 if (ar->seq_num == 0) {
5780 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5781 if (ret != LDB_SUCCESS) {
5782 return replmd_replicated_request_error(ar, ret);
5785 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
5786 switch (nmd.ctr.ctr1.array[ni].attid) {
5787 case DRSUAPI_ATTID_ntSecurityDescriptor:
5788 sd_updated = true;
5789 break;
5790 case DRSUAPI_ATTID_isDeleted:
5791 take_remote_isDeleted = true;
5792 break;
5793 default:
5794 break;
5796 ni++;
5800 * finally correct the size of the meta_data array
5802 nmd.ctr.ctr1.count = ni;
5804 new_rdn = ldb_dn_get_rdn_val(msg->dn);
5805 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
5807 if (renamed) {
5808 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
5809 &nmd, ar, now, is_schema_nc,
5810 false);
5811 if (ret != LDB_SUCCESS) {
5812 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5813 return replmd_replicated_request_error(ar, ret);
5817 * sort the new meta data array
5819 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
5820 if (ret != LDB_SUCCESS) {
5821 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5822 return ret;
5826 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
5827 * UpdateObject.
5829 * This also controls SD propagation below
5831 if (take_remote_isDeleted) {
5832 isDeleted = remote_isDeleted;
5833 } else {
5834 isDeleted = local_isDeleted;
5837 ar->isDeleted = isDeleted;
5840 * check if some replicated attributes left, otherwise skip the ldb_modify() call
5842 if (msg->num_elements == 0) {
5843 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
5844 ar->index_current);
5846 return replmd_replicated_apply_isDeleted(ar);
5849 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
5850 ar->index_current, msg->num_elements);
5852 if (renamed) {
5853 sd_updated = true;
5856 if (sd_updated && !isDeleted) {
5857 ret = dsdb_module_schedule_sd_propagation(ar->module,
5858 ar->objs->partition_dn,
5859 msg->dn, true);
5860 if (ret != LDB_SUCCESS) {
5861 return ldb_operr(ldb);
5865 /* create the meta data value */
5866 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
5867 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5868 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5869 nt_status = ndr_map_error2ntstatus(ndr_err);
5870 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5874 * when we know that we'll modify the record, add the whenChanged, uSNChanged
5875 * and replPopertyMetaData attributes
5877 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5878 if (ret != LDB_SUCCESS) {
5879 return replmd_replicated_request_error(ar, ret);
5881 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5882 if (ret != LDB_SUCCESS) {
5883 return replmd_replicated_request_error(ar, ret);
5885 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
5886 if (ret != LDB_SUCCESS) {
5887 return replmd_replicated_request_error(ar, ret);
5890 replmd_ldb_message_sort(msg, ar->schema);
5892 /* we want to replace the old values */
5893 for (i=0; i < msg->num_elements; i++) {
5894 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
5895 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5896 if (msg->elements[i].num_values == 0) {
5897 ldb_asprintf_errstring(ldb, __location__
5898 ": objectClass removed on %s, aborting replication\n",
5899 ldb_dn_get_linearized(msg->dn));
5900 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5905 if (DEBUGLVL(8)) {
5906 struct GUID_txt_buf guid_txt;
5908 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5909 LDB_CHANGETYPE_MODIFY,
5910 msg);
5911 DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
5912 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5913 &guid_txt),
5914 s));
5915 talloc_free(s);
5916 } else if (DEBUGLVL(4)) {
5917 struct GUID_txt_buf guid_txt;
5919 DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
5920 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5921 &guid_txt),
5922 ldb_dn_get_linearized(msg->dn)));
5925 ret = ldb_build_mod_req(&change_req,
5926 ldb,
5928 msg,
5929 ar->controls,
5931 callback,
5932 ar->req);
5933 LDB_REQ_SET_LOCATION(change_req);
5934 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5936 /* current partition control needed by "repmd_op_callback" */
5937 ret = ldb_request_add_control(change_req,
5938 DSDB_CONTROL_CURRENT_PARTITION_OID,
5939 false, NULL);
5940 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5942 return ldb_next_request(ar->module, change_req);
5945 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
5946 struct ldb_reply *ares)
5948 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5949 struct replmd_replicated_request);
5950 int ret;
5952 if (!ares) {
5953 return ldb_module_done(ar->req, NULL, NULL,
5954 LDB_ERR_OPERATIONS_ERROR);
5956 if (ares->error != LDB_SUCCESS &&
5957 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5958 return ldb_module_done(ar->req, ares->controls,
5959 ares->response, ares->error);
5962 switch (ares->type) {
5963 case LDB_REPLY_ENTRY:
5964 ar->search_msg = talloc_steal(ar, ares->message);
5965 break;
5967 case LDB_REPLY_REFERRAL:
5968 /* we ignore referrals */
5969 break;
5971 case LDB_REPLY_DONE:
5973 struct replPropertyMetaData1 *md_remote;
5974 struct replPropertyMetaData1 *md_local;
5976 struct replPropertyMetaDataBlob omd;
5977 const struct ldb_val *omd_value;
5978 struct replPropertyMetaDataBlob *rmd;
5979 struct ldb_message *msg;
5980 int instanceType;
5981 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
5982 ar->objs->objects[ar->index_current].last_known_parent = NULL;
5985 * This is the ADD case, find the appropriate parent,
5986 * as this object doesn't exist locally:
5988 if (ar->search_msg == NULL) {
5989 ret = replmd_replicated_apply_search_for_parent(ar);
5990 if (ret != LDB_SUCCESS) {
5991 return ldb_module_done(ar->req, NULL, NULL, ret);
5993 talloc_free(ares);
5994 return LDB_SUCCESS;
5998 * Otherwise, in the MERGE case, work out if we are
5999 * attempting a rename, and if so find the parent the
6000 * newly renamed object wants to belong under (which
6001 * may not be the parent in it's attached string DN
6003 rmd = ar->objs->objects[ar->index_current].meta_data;
6004 ZERO_STRUCT(omd);
6005 omd.version = 1;
6007 /* find existing meta data */
6008 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6009 if (omd_value) {
6010 enum ndr_err_code ndr_err;
6011 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6012 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6013 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6014 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6015 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6018 if (omd.version != 1) {
6019 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6023 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6025 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6026 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6027 && GUID_all_zero(&ar->local_parent_guid)) {
6028 DEBUG(0, ("Refusing to replicate new version of %s "
6029 "as local object has an all-zero parentGUID attribute, "
6030 "despite not being an NC root\n",
6031 ldb_dn_get_linearized(ar->search_msg->dn)));
6032 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6036 * now we need to check for double renames. We could have a
6037 * local rename pending which our replication partner hasn't
6038 * received yet. We choose which one wins by looking at the
6039 * attribute stamps on the two objects, the newer one wins.
6041 * This also simply applies the correct algorithms for
6042 * determining if a change was made to name at all, or
6043 * if the object has just been renamed under the same
6044 * parent.
6046 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6047 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6048 if (!md_local) {
6049 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6050 ldb_dn_get_linearized(ar->search_msg->dn)));
6051 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6055 * if there is no name attribute given then we have to assume the
6056 * object we've received has the older name
6058 if (replmd_replPropertyMetaData1_new_should_be_taken(
6059 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6060 md_local, md_remote)) {
6061 struct GUID_txt_buf p_guid_local;
6062 struct GUID_txt_buf p_guid_remote;
6063 msg = ar->objs->objects[ar->index_current].msg;
6065 /* Merge on the existing object, with rename */
6067 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6068 "as incoming object changing to %s under %s\n",
6069 ldb_dn_get_linearized(ar->search_msg->dn),
6070 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6071 ldb_dn_get_linearized(msg->dn),
6072 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6073 &p_guid_remote)));
6074 ret = replmd_replicated_apply_search_for_parent(ar);
6075 } else {
6076 struct GUID_txt_buf p_guid_local;
6077 struct GUID_txt_buf p_guid_remote;
6078 msg = ar->objs->objects[ar->index_current].msg;
6081 * Merge on the existing object, force no
6082 * rename (code below just to explain why in
6083 * the DEBUG() logs)
6086 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6087 ldb_dn_get_linearized(msg->dn)) == 0) {
6088 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6089 GUID_equal(&ar->local_parent_guid,
6090 ar->objs->objects[ar->index_current].parent_guid)
6091 == false) {
6092 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6093 "despite incoming object changing parent to %s\n",
6094 ldb_dn_get_linearized(ar->search_msg->dn),
6095 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6096 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6097 &p_guid_remote)));
6099 } else {
6100 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6101 " and rejecting older rename to %s under %s\n",
6102 ldb_dn_get_linearized(ar->search_msg->dn),
6103 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6104 ldb_dn_get_linearized(msg->dn),
6105 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6106 &p_guid_remote)));
6109 * This assignment ensures that the strcmp()
6110 * and GUID_equal() calls in
6111 * replmd_replicated_apply_merge() avoids the
6112 * rename call
6114 ar->objs->objects[ar->index_current].parent_guid =
6115 &ar->local_parent_guid;
6117 msg->dn = ar->search_msg->dn;
6118 ret = replmd_replicated_apply_merge(ar);
6120 if (ret != LDB_SUCCESS) {
6121 return ldb_module_done(ar->req, NULL, NULL, ret);
6126 talloc_free(ares);
6127 return LDB_SUCCESS;
6131 * Stores the linked attributes received in the replication chunk - these get
6132 * applied at the end of the transaction. We also check that each linked
6133 * attribute is valid, i.e. source and target objects are known.
6135 static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
6137 int ret = LDB_SUCCESS;
6138 uint32_t i;
6139 struct ldb_module *module = ar->module;
6140 struct replmd_private *replmd_private =
6141 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6142 struct ldb_context *ldb;
6144 ldb = ldb_module_get_ctx(module);
6146 DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
6148 /* save away the linked attributes for the end of the transaction */
6149 for (i = 0; i < ar->objs->linked_attributes_count; i++) {
6150 struct la_entry *la_entry;
6152 if (replmd_private->la_ctx == NULL) {
6153 replmd_private->la_ctx = talloc_new(replmd_private);
6155 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6156 if (la_entry == NULL) {
6157 ldb_oom(ldb);
6158 return LDB_ERR_OPERATIONS_ERROR;
6160 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
6161 if (la_entry->la == NULL) {
6162 talloc_free(la_entry);
6163 ldb_oom(ldb);
6164 return LDB_ERR_OPERATIONS_ERROR;
6166 *la_entry->la = ar->objs->linked_attributes[i];
6167 la_entry->dsdb_repl_flags = ar->objs->dsdb_repl_flags;
6169 /* we need to steal the non-scalars so they stay
6170 around until the end of the transaction */
6171 talloc_steal(la_entry->la, la_entry->la->identifier);
6172 talloc_steal(la_entry->la, la_entry->la->value.blob);
6174 ret = replmd_verify_linked_attribute(ar, la_entry);
6176 if (ret != LDB_SUCCESS) {
6177 break;
6180 DLIST_ADD(replmd_private->la_list, la_entry);
6183 return ret;
6186 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6188 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6190 struct ldb_context *ldb;
6191 int ret;
6192 char *tmp_str;
6193 char *filter;
6194 struct ldb_request *search_req;
6195 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6196 "parentGUID", "instanceType",
6197 "replPropertyMetaData", "nTSecurityDescriptor",
6198 "isDeleted", NULL };
6199 struct GUID_txt_buf guid_str_buf;
6201 if (ar->index_current >= ar->objs->num_objects) {
6204 * Now that we've applied all the objects, check the new linked
6205 * attributes and store them (we apply them in .prepare_commit)
6207 ret = replmd_store_linked_attributes(ar);
6209 if (ret != LDB_SUCCESS) {
6210 return ret;
6213 /* done applying objects, move on to the next stage */
6214 return replmd_replicated_uptodate_vector(ar);
6217 ldb = ldb_module_get_ctx(ar->module);
6218 ar->search_msg = NULL;
6219 ar->isDeleted = false;
6221 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6222 &guid_str_buf);
6224 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6225 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6227 ret = ldb_build_search_req(&search_req,
6228 ldb,
6230 ar->objs->partition_dn,
6231 LDB_SCOPE_SUBTREE,
6232 filter,
6233 attrs,
6234 NULL,
6236 replmd_replicated_apply_search_callback,
6237 ar->req);
6238 LDB_REQ_SET_LOCATION(search_req);
6240 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
6242 if (ret != LDB_SUCCESS) {
6243 return ret;
6246 return ldb_next_request(ar->module, search_req);
6250 * This is essentially a wrapper for replmd_replicated_apply_next()
6252 * This is needed to ensure that both codepaths call this handler.
6254 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6256 struct ldb_dn *deleted_objects_dn;
6257 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6258 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
6259 &deleted_objects_dn);
6260 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
6262 * Do a delete here again, so that if there is
6263 * anything local that conflicts with this
6264 * object being deleted, it is removed. This
6265 * includes links. See MS-DRSR 4.1.10.6.9
6266 * UpdateObject.
6268 * If the object is already deleted, and there
6269 * is no more work required, it doesn't do
6270 * anything.
6273 /* This has been updated to point to the DN we eventually did the modify on */
6275 struct ldb_request *del_req;
6276 struct ldb_result *res;
6278 TALLOC_CTX *tmp_ctx = talloc_new(ar);
6279 if (!tmp_ctx) {
6280 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6281 return ret;
6284 res = talloc_zero(tmp_ctx, struct ldb_result);
6285 if (!res) {
6286 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6287 talloc_free(tmp_ctx);
6288 return ret;
6291 /* Build a delete request, which hopefully will artually turn into nothing */
6292 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
6293 msg->dn,
6294 NULL,
6295 res,
6296 ldb_modify_default_callback,
6297 ar->req);
6298 LDB_REQ_SET_LOCATION(del_req);
6299 if (ret != LDB_SUCCESS) {
6300 talloc_free(tmp_ctx);
6301 return ret;
6305 * This is the guts of the call, call back
6306 * into our delete code, but setting the
6307 * re_delete flag so we delete anything that
6308 * shouldn't be there on a deleted or recycled
6309 * object
6311 ret = replmd_delete_internals(ar->module, del_req, true);
6312 if (ret == LDB_SUCCESS) {
6313 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
6316 talloc_free(tmp_ctx);
6317 if (ret != LDB_SUCCESS) {
6318 return ret;
6322 ar->index_current++;
6323 return replmd_replicated_apply_next(ar);
6326 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
6327 struct ldb_reply *ares)
6329 struct ldb_context *ldb;
6330 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6331 struct replmd_replicated_request);
6332 ldb = ldb_module_get_ctx(ar->module);
6334 if (!ares) {
6335 return ldb_module_done(ar->req, NULL, NULL,
6336 LDB_ERR_OPERATIONS_ERROR);
6338 if (ares->error != LDB_SUCCESS) {
6339 return ldb_module_done(ar->req, ares->controls,
6340 ares->response, ares->error);
6343 if (ares->type != LDB_REPLY_DONE) {
6344 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
6345 return ldb_module_done(ar->req, NULL, NULL,
6346 LDB_ERR_OPERATIONS_ERROR);
6349 talloc_free(ares);
6351 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6354 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
6356 struct ldb_context *ldb;
6357 struct ldb_request *change_req;
6358 enum ndr_err_code ndr_err;
6359 struct ldb_message *msg;
6360 struct replUpToDateVectorBlob ouv;
6361 const struct ldb_val *ouv_value;
6362 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
6363 struct replUpToDateVectorBlob nuv;
6364 struct ldb_val nuv_value;
6365 struct ldb_message_element *nuv_el = NULL;
6366 struct ldb_message_element *orf_el = NULL;
6367 struct repsFromToBlob nrf;
6368 struct ldb_val *nrf_value = NULL;
6369 struct ldb_message_element *nrf_el = NULL;
6370 unsigned int i;
6371 uint32_t j,ni=0;
6372 bool found = false;
6373 time_t t = time(NULL);
6374 NTTIME now;
6375 int ret;
6376 uint32_t instanceType;
6378 ldb = ldb_module_get_ctx(ar->module);
6379 ruv = ar->objs->uptodateness_vector;
6380 ZERO_STRUCT(ouv);
6381 ouv.version = 2;
6382 ZERO_STRUCT(nuv);
6383 nuv.version = 2;
6385 unix_to_nt_time(&now, t);
6387 if (ar->search_msg == NULL) {
6388 /* this happens for a REPL_OBJ call where we are
6389 creating the target object by replicating it. The
6390 subdomain join code does this for the partition DN
6392 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
6393 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6396 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
6397 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
6398 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
6399 ldb_dn_get_linearized(ar->search_msg->dn)));
6400 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6404 * first create the new replUpToDateVector
6406 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
6407 if (ouv_value) {
6408 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
6409 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
6410 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6411 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6412 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6415 if (ouv.version != 2) {
6416 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6421 * the new uptodateness vector will at least
6422 * contain 1 entry, one for the source_dsa
6424 * plus optional values from our old vector and the one from the source_dsa
6426 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
6427 if (ruv) nuv.ctr.ctr2.count += ruv->count;
6428 nuv.ctr.ctr2.cursors = talloc_array(ar,
6429 struct drsuapi_DsReplicaCursor2,
6430 nuv.ctr.ctr2.count);
6431 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6433 /* first copy the old vector */
6434 for (i=0; i < ouv.ctr.ctr2.count; i++) {
6435 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
6436 ni++;
6439 /* merge in the source_dsa vector is available */
6440 for (i=0; (ruv && i < ruv->count); i++) {
6441 found = false;
6443 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6444 &ar->our_invocation_id)) {
6445 continue;
6448 for (j=0; j < ni; j++) {
6449 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6450 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
6451 continue;
6454 found = true;
6456 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
6457 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
6459 break;
6462 if (found) continue;
6464 /* if it's not there yet, add it */
6465 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
6466 ni++;
6470 * finally correct the size of the cursors array
6472 nuv.ctr.ctr2.count = ni;
6475 * sort the cursors
6477 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
6480 * create the change ldb_message
6482 msg = ldb_msg_new(ar);
6483 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6484 msg->dn = ar->search_msg->dn;
6486 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
6487 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
6488 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6489 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6490 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6492 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
6493 if (ret != LDB_SUCCESS) {
6494 return replmd_replicated_request_error(ar, ret);
6496 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
6499 * now create the new repsFrom value from the given repsFromTo1 structure
6501 ZERO_STRUCT(nrf);
6502 nrf.version = 1;
6503 nrf.ctr.ctr1 = *ar->objs->source_dsa;
6504 nrf.ctr.ctr1.last_attempt = now;
6505 nrf.ctr.ctr1.last_success = now;
6506 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
6509 * first see if we already have a repsFrom value for the current source dsa
6510 * if so we'll later replace this value
6512 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
6513 if (orf_el) {
6514 for (i=0; i < orf_el->num_values; i++) {
6515 struct repsFromToBlob *trf;
6517 trf = talloc(ar, struct repsFromToBlob);
6518 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6520 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
6521 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
6522 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6523 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6524 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6527 if (trf->version != 1) {
6528 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6532 * we compare the source dsa objectGUID not the invocation_id
6533 * because we want only one repsFrom value per source dsa
6534 * and when the invocation_id of the source dsa has changed we don't need
6535 * the old repsFrom with the old invocation_id
6537 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
6538 &ar->objs->source_dsa->source_dsa_obj_guid)) {
6539 talloc_free(trf);
6540 continue;
6543 talloc_free(trf);
6544 nrf_value = &orf_el->values[i];
6545 break;
6549 * copy over all old values to the new ldb_message
6551 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
6552 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6553 *nrf_el = *orf_el;
6557 * if we haven't found an old repsFrom value for the current source dsa
6558 * we'll add a new value
6560 if (!nrf_value) {
6561 struct ldb_val zero_value;
6562 ZERO_STRUCT(zero_value);
6563 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
6564 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6566 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
6569 /* we now fill the value which is already attached to ldb_message */
6570 ndr_err = ndr_push_struct_blob(nrf_value, msg,
6571 &nrf,
6572 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
6573 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6574 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6575 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6579 * the ldb_message_element for the attribute, has all the old values and the new one
6580 * so we'll replace the whole attribute with all values
6582 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
6584 if (CHECK_DEBUGLVL(4)) {
6585 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6586 LDB_CHANGETYPE_MODIFY,
6587 msg);
6588 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
6589 talloc_free(s);
6592 /* prepare the ldb_modify() request */
6593 ret = ldb_build_mod_req(&change_req,
6594 ldb,
6596 msg,
6597 ar->controls,
6599 replmd_replicated_uptodate_modify_callback,
6600 ar->req);
6601 LDB_REQ_SET_LOCATION(change_req);
6602 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6604 return ldb_next_request(ar->module, change_req);
6607 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
6608 struct ldb_reply *ares)
6610 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6611 struct replmd_replicated_request);
6612 int ret;
6614 if (!ares) {
6615 return ldb_module_done(ar->req, NULL, NULL,
6616 LDB_ERR_OPERATIONS_ERROR);
6618 if (ares->error != LDB_SUCCESS &&
6619 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6620 return ldb_module_done(ar->req, ares->controls,
6621 ares->response, ares->error);
6624 switch (ares->type) {
6625 case LDB_REPLY_ENTRY:
6626 ar->search_msg = talloc_steal(ar, ares->message);
6627 break;
6629 case LDB_REPLY_REFERRAL:
6630 /* we ignore referrals */
6631 break;
6633 case LDB_REPLY_DONE:
6634 ret = replmd_replicated_uptodate_modify(ar);
6635 if (ret != LDB_SUCCESS) {
6636 return ldb_module_done(ar->req, NULL, NULL, ret);
6640 talloc_free(ares);
6641 return LDB_SUCCESS;
6645 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
6647 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
6648 struct replmd_private *replmd_private =
6649 talloc_get_type_abort(ldb_module_get_private(ar->module),
6650 struct replmd_private);
6651 int ret;
6652 static const char *attrs[] = {
6653 "replUpToDateVector",
6654 "repsFrom",
6655 "instanceType",
6656 NULL
6658 struct ldb_request *search_req;
6660 ar->search_msg = NULL;
6663 * Let the caller know that we did an originating updates
6665 ar->objs->originating_updates = replmd_private->originating_updates;
6667 ret = ldb_build_search_req(&search_req,
6668 ldb,
6670 ar->objs->partition_dn,
6671 LDB_SCOPE_BASE,
6672 "(objectClass=*)",
6673 attrs,
6674 NULL,
6676 replmd_replicated_uptodate_search_callback,
6677 ar->req);
6678 LDB_REQ_SET_LOCATION(search_req);
6679 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6681 return ldb_next_request(ar->module, search_req);
6686 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
6688 struct ldb_context *ldb;
6689 struct dsdb_extended_replicated_objects *objs;
6690 struct replmd_replicated_request *ar;
6691 struct ldb_control **ctrls;
6692 int ret;
6694 ldb = ldb_module_get_ctx(module);
6696 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
6698 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
6699 if (!objs) {
6700 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
6701 return LDB_ERR_PROTOCOL_ERROR;
6704 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
6705 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
6706 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
6707 return LDB_ERR_PROTOCOL_ERROR;
6710 ar = replmd_ctx_init(module, req);
6711 if (!ar)
6712 return LDB_ERR_OPERATIONS_ERROR;
6714 /* Set the flags to have the replmd_op_callback run over the full set of objects */
6715 ar->apply_mode = true;
6716 ar->objs = objs;
6717 ar->schema = dsdb_get_schema(ldb, ar);
6718 if (!ar->schema) {
6719 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
6720 talloc_free(ar);
6721 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
6722 return LDB_ERR_CONSTRAINT_VIOLATION;
6725 ctrls = req->controls;
6727 if (req->controls) {
6728 req->controls = talloc_memdup(ar, req->controls,
6729 talloc_get_size(req->controls));
6730 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6733 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
6734 if (ret != LDB_SUCCESS) {
6735 return ret;
6738 /* If this change contained linked attributes in the body
6739 * (rather than in the links section) we need to update
6740 * backlinks in linked_attributes */
6741 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
6742 if (ret != LDB_SUCCESS) {
6743 return ret;
6746 ar->controls = req->controls;
6747 req->controls = ctrls;
6749 return replmd_replicated_apply_next(ar);
6753 * Checks how to handle an missing target - either we need to fail the
6754 * replication and retry with GET_TGT, ignore the link and continue, or try to
6755 * add a partial link to an unknown target.
6757 static int replmd_allow_missing_target(struct ldb_module *module,
6758 TALLOC_CTX *mem_ctx,
6759 struct ldb_dn *target_dn,
6760 struct ldb_dn *source_dn,
6761 bool is_obj_commit,
6762 struct GUID *guid,
6763 uint32_t dsdb_repl_flags,
6764 bool *ignore_link,
6765 const char * missing_str)
6767 struct ldb_context *ldb = ldb_module_get_ctx(module);
6768 bool is_in_same_nc;
6771 * we may not be able to resolve link targets properly when
6772 * dealing with subsets of objects, e.g. the source is a
6773 * critical object and the target isn't
6775 * TODO:
6776 * When we implement Trusted Domains we need to consider
6777 * whether they get treated as an incomplete replica here or not
6779 if (dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
6782 * Ignore the link. We don't increase the highwater-mark in
6783 * the object subset cases, so subsequent replications should
6784 * resolve any missing links
6786 DEBUG(2, ("%s target %s linked from %s\n", missing_str,
6787 ldb_dn_get_linearized(target_dn),
6788 ldb_dn_get_linearized(source_dn)));
6789 *ignore_link = true;
6790 return LDB_SUCCESS;
6793 if (dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
6796 * target should already be up-to-date so there's no point in
6797 * retrying. This could be due to bad timing, or if a target
6798 * on a one-way link was deleted. We ignore the link rather
6799 * than failing the replication cycle completely
6801 *ignore_link = true;
6802 DBG_WARNING("%s is %s but up to date. Ignoring link from %s\n",
6803 ldb_dn_get_linearized(target_dn), missing_str,
6804 ldb_dn_get_linearized(source_dn));
6805 return LDB_SUCCESS;
6808 is_in_same_nc = dsdb_objects_have_same_nc(ldb,
6809 mem_ctx,
6810 source_dn,
6811 target_dn);
6812 if (is_in_same_nc) {
6813 /* fail the replication and retry with GET_TGT */
6814 ldb_asprintf_errstring(ldb, "%s target %s GUID %s linked from %s\n",
6815 missing_str,
6816 ldb_dn_get_linearized(target_dn),
6817 GUID_string(mem_ctx, guid),
6818 ldb_dn_get_linearized(source_dn));
6819 return LDB_ERR_NO_SUCH_OBJECT;
6823 * The target of the cross-partition link is missing. Continue
6824 * and try to at least add the forward-link. This isn't great,
6825 * but a partial link can be fixed by dbcheck, so it's better
6826 * than dropping the link completely.
6828 *ignore_link = false;
6830 if (is_obj_commit) {
6833 * Only log this when we're actually committing the objects.
6834 * This avoids spurious logs, i.e. if we're just verifying the
6835 * received link during a join.
6837 DBG_WARNING("%s cross-partition target %s linked from %s\n",
6838 missing_str, ldb_dn_get_linearized(target_dn),
6839 ldb_dn_get_linearized(source_dn));
6842 return LDB_SUCCESS;
6846 * Checks that the target object for a linked attribute exists.
6847 * @param guid returns the target object's GUID (is returned)if it exists)
6848 * @param ignore_link set to true if the linked attribute should be ignored
6849 * (i.e. the target doesn't exist, but that it's OK to skip the link)
6851 static int replmd_check_target_exists(struct ldb_module *module,
6852 struct dsdb_dn *dsdb_dn,
6853 struct la_entry *la_entry,
6854 struct ldb_dn *source_dn,
6855 bool is_obj_commit,
6856 struct GUID *guid,
6857 bool *ignore_link)
6859 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
6860 struct ldb_context *ldb = ldb_module_get_ctx(module);
6861 struct ldb_result *target_res;
6862 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
6863 const char *attrs[] = { "isDeleted", "isRecycled", NULL };
6864 NTSTATUS ntstatus;
6865 int ret;
6866 enum deletion_state target_deletion_state = OBJECT_REMOVED;
6867 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
6869 *ignore_link = false;
6870 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
6872 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
6875 * This strange behaviour (allowing a NULL/missing
6876 * GUID) originally comes from:
6878 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
6879 * Author: Andrew Tridgell <tridge@samba.org>
6880 * Date: Mon Dec 21 21:21:55 2009 +1100
6882 * s4-drs: cope better with NULL GUIDS from DRS
6884 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
6885 * need to match by DN if possible when seeing if we should update an
6886 * existing link.
6888 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
6890 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
6891 dsdb_dn->dn, attrs,
6892 DSDB_FLAG_NEXT_MODULE |
6893 DSDB_SEARCH_SHOW_RECYCLED |
6894 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6895 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6896 NULL);
6897 } else if (!NT_STATUS_IS_OK(ntstatus)) {
6898 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
6899 la->attid,
6900 ldb_dn_get_linearized(dsdb_dn->dn),
6901 ldb_dn_get_linearized(source_dn));
6902 talloc_free(tmp_ctx);
6903 return LDB_ERR_OPERATIONS_ERROR;
6904 } else {
6905 ret = dsdb_module_search(module, tmp_ctx, &target_res,
6906 NULL, LDB_SCOPE_SUBTREE,
6907 attrs,
6908 DSDB_FLAG_NEXT_MODULE |
6909 DSDB_SEARCH_SHOW_RECYCLED |
6910 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6911 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6912 NULL,
6913 "objectGUID=%s",
6914 GUID_string(tmp_ctx, guid));
6917 if (ret != LDB_SUCCESS) {
6918 ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
6919 GUID_string(tmp_ctx, guid),
6920 ldb_errstring(ldb));
6921 talloc_free(tmp_ctx);
6922 return ret;
6925 if (target_res->count == 0) {
6928 * target object is unknown. Check whether to ignore the link,
6929 * fail the replication, or add a partial link
6931 ret = replmd_allow_missing_target(module, tmp_ctx, dsdb_dn->dn,
6932 source_dn, is_obj_commit, guid,
6933 la_entry->dsdb_repl_flags,
6934 ignore_link, "Unknown");
6936 } else if (target_res->count != 1) {
6937 ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
6938 GUID_string(tmp_ctx, guid));
6939 ret = LDB_ERR_OPERATIONS_ERROR;
6940 } else {
6941 struct ldb_message *target_msg = target_res->msgs[0];
6943 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
6945 /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
6946 replmd_deletion_state(module, target_msg,
6947 &target_deletion_state, NULL);
6950 * Check for deleted objects as per MS-DRSR 4.1.10.6.14
6951 * ProcessLinkValue(). Link updates should not be sent for
6952 * recycled and tombstone objects (deleting the links should
6953 * happen when we delete the object). This probably means our
6954 * copy of the target object isn't up to date.
6956 if (target_deletion_state >= OBJECT_RECYCLED) {
6959 * target object is deleted. Check whether to ignore the
6960 * link, fail the replication, or add a partial link
6962 ret = replmd_allow_missing_target(module, tmp_ctx,
6963 dsdb_dn->dn, source_dn,
6964 is_obj_commit, guid,
6965 la_entry->dsdb_repl_flags,
6966 ignore_link, "Deleted");
6970 talloc_free(tmp_ctx);
6971 return ret;
6975 * Extracts the key details about the source/target object for a
6976 * linked-attribute entry.
6977 * This returns the following details:
6978 * @param ret_attr the schema details for the linked attribute
6979 * @param source_msg the search result for the source object
6980 * @param target_dsdb_dn the unpacked DN info for the target object
6982 static int replmd_extract_la_entry_details(struct ldb_module *module,
6983 struct la_entry *la_entry,
6984 TALLOC_CTX *mem_ctx,
6985 const struct dsdb_attribute **ret_attr,
6986 struct ldb_message **source_msg,
6987 struct dsdb_dn **target_dsdb_dn)
6989 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
6990 struct ldb_context *ldb = ldb_module_get_ctx(module);
6991 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
6992 int ret;
6993 const struct dsdb_attribute *attr;
6994 WERROR status;
6995 struct ldb_result *res;
6996 const char *attrs[4];
6999 linked_attributes[0]:
7000 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
7001 identifier : *
7002 identifier: struct drsuapi_DsReplicaObjectIdentifier
7003 __ndr_size : 0x0000003a (58)
7004 __ndr_size_sid : 0x00000000 (0)
7005 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7006 sid : S-0-0
7007 __ndr_size_dn : 0x00000000 (0)
7008 dn : ''
7009 attid : DRSUAPI_ATTID_member (0x1F)
7010 value: struct drsuapi_DsAttributeValue
7011 __ndr_size : 0x0000007e (126)
7012 blob : *
7013 blob : DATA_BLOB length=126
7014 flags : 0x00000001 (1)
7015 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7016 originating_add_time : Wed Sep 2 22:20:01 2009 EST
7017 meta_data: struct drsuapi_DsReplicaMetaData
7018 version : 0x00000015 (21)
7019 originating_change_time : Wed Sep 2 23:39:07 2009 EST
7020 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7021 originating_usn : 0x000000000001e19c (123292)
7023 (for cases where the link is to a normal DN)
7024 &target: struct drsuapi_DsReplicaObjectIdentifier3
7025 __ndr_size : 0x0000007e (126)
7026 __ndr_size_sid : 0x0000001c (28)
7027 guid : 7639e594-db75-4086-b0d4-67890ae46031
7028 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7029 __ndr_size_dn : 0x00000022 (34)
7030 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7033 /* find the attribute being modified */
7034 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
7035 if (attr == NULL) {
7036 struct GUID_txt_buf guid_str;
7037 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7038 la->attid,
7039 GUID_buf_string(&la->identifier->guid,
7040 &guid_str));
7041 return LDB_ERR_OPERATIONS_ERROR;
7044 attrs[0] = attr->lDAPDisplayName;
7045 attrs[1] = "isDeleted";
7046 attrs[2] = "isRecycled";
7047 attrs[3] = NULL;
7050 * get the existing message from the db for the object with
7051 * this GUID, returning attribute being modified. We will then
7052 * use this msg as the basis for a modify call
7054 ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
7055 DSDB_FLAG_NEXT_MODULE |
7056 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7057 DSDB_SEARCH_SHOW_RECYCLED |
7058 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
7059 DSDB_SEARCH_REVEAL_INTERNALS,
7060 NULL,
7061 "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
7062 if (ret != LDB_SUCCESS) {
7063 return ret;
7065 if (res->count != 1) {
7066 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
7067 GUID_string(mem_ctx, &la->identifier->guid));
7068 return LDB_ERR_NO_SUCH_OBJECT;
7071 *source_msg = res->msgs[0];
7073 /* the value blob for the attribute holds the target object DN */
7074 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx, la->value.blob, target_dsdb_dn);
7075 if (!W_ERROR_IS_OK(status)) {
7076 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7077 attr->lDAPDisplayName,
7078 ldb_dn_get_linearized(res->msgs[0]->dn),
7079 win_errstr(status));
7080 return LDB_ERR_OPERATIONS_ERROR;
7083 *ret_attr = attr;
7085 return LDB_SUCCESS;
7089 * Verifies the source and target objects are known for a linked attribute
7091 static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
7092 struct la_entry *la)
7094 int ret = LDB_SUCCESS;
7095 TALLOC_CTX *tmp_ctx = talloc_new(la);
7096 struct ldb_module *module = ar->module;
7097 struct ldb_message *src_msg;
7098 const struct dsdb_attribute *attr;
7099 struct dsdb_dn *tgt_dsdb_dn;
7100 struct GUID guid = GUID_zero();
7101 bool dummy;
7103 ret = replmd_extract_la_entry_details(module, la, tmp_ctx, &attr,
7104 &src_msg, &tgt_dsdb_dn);
7107 * When we fail to find the source object, the error code we pass
7108 * back here is really important. It flags back to the callers to
7109 * retry this request with DRSUAPI_DRS_GET_ANC. This case should
7110 * never happen if we're replicating from a Samba DC, but it is
7111 * needed to talk to a Windows DC
7113 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7114 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT);
7117 if (ret != LDB_SUCCESS) {
7118 talloc_free(tmp_ctx);
7119 return ret;
7123 * We can skip the target object checks if we're only syncing critical
7124 * objects, or we know the target is up-to-date. If either case, we
7125 * still continue even if the target doesn't exist
7127 if ((la->dsdb_repl_flags & (DSDB_REPL_FLAG_OBJECT_SUBSET |
7128 DSDB_REPL_FLAG_TARGETS_UPTODATE)) == 0) {
7130 ret = replmd_check_target_exists(module, tgt_dsdb_dn, la,
7131 src_msg->dn, false, &guid,
7132 &dummy);
7136 * When we fail to find the target object, the error code we pass
7137 * back here is really important. It flags back to the callers to
7138 * retry this request with DRSUAPI_DRS_GET_TGT
7140 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7141 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
7144 talloc_free(tmp_ctx);
7145 return ret;
7149 * Finds the current active Parsed-DN value for a single-valued linked
7150 * attribute, if one exists.
7151 * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
7152 * @returns LDB_SUCCESS (regardless of whether a match was found), unless
7153 * an error occurred
7155 static int replmd_get_active_singleval_link(struct ldb_module *module,
7156 TALLOC_CTX *mem_ctx,
7157 struct parsed_dn pdn_list[],
7158 unsigned int count,
7159 const struct dsdb_attribute *attr,
7160 struct parsed_dn **ret_pdn)
7162 unsigned int i;
7164 *ret_pdn = NULL;
7166 if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE)) {
7168 /* nothing to do for multi-valued linked attributes */
7169 return LDB_SUCCESS;
7172 for (i = 0; i < count; i++) {
7173 int ret = LDB_SUCCESS;
7174 struct parsed_dn *pdn = &pdn_list[i];
7176 /* skip any inactive links */
7177 if (dsdb_dn_is_deleted_val(pdn->v)) {
7178 continue;
7181 /* we've found an active value for this attribute */
7182 *ret_pdn = pdn;
7184 if (pdn->dsdb_dn == NULL) {
7185 struct ldb_context *ldb = ldb_module_get_ctx(module);
7187 ret = really_parse_trusted_dn(mem_ctx, ldb, pdn,
7188 attr->syntax->ldap_oid);
7191 return ret;
7194 /* no active link found */
7195 return LDB_SUCCESS;
7199 * @returns true if the replication linked attribute info is newer than we
7200 * already have in our DB
7201 * @param pdn the existing linked attribute info in our DB
7202 * @param la the new linked attribute info received during replication
7204 static bool replmd_link_update_is_newer(struct parsed_dn *pdn,
7205 struct drsuapi_DsReplicaLinkedAttribute *la)
7207 /* see if this update is newer than what we have already */
7208 struct GUID invocation_id = GUID_zero();
7209 uint32_t version = 0;
7210 NTTIME change_time = 0;
7212 if (pdn == NULL) {
7214 /* no existing info so update is newer */
7215 return true;
7218 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
7219 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
7220 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
7222 return replmd_update_is_newer(&invocation_id,
7223 &la->meta_data.originating_invocation_id,
7224 version,
7225 la->meta_data.version,
7226 change_time,
7227 la->meta_data.originating_change_time);
7231 * Marks an existing linked attribute value as deleted in the DB
7232 * @param pdn the parsed-DN of the target-value to delete
7234 static int replmd_delete_link_value(struct ldb_module *module,
7235 struct replmd_private *replmd_private,
7236 TALLOC_CTX *mem_ctx,
7237 struct ldb_dn *src_obj_dn,
7238 const struct dsdb_schema *schema,
7239 const struct dsdb_attribute *attr,
7240 uint64_t seq_num,
7241 bool is_active,
7242 struct GUID *target_guid,
7243 struct dsdb_dn *target_dsdb_dn,
7244 struct ldb_val *output_val)
7246 struct ldb_context *ldb = ldb_module_get_ctx(module);
7247 time_t t;
7248 NTTIME now;
7249 const struct GUID *invocation_id = NULL;
7250 int ret;
7252 t = time(NULL);
7253 unix_to_nt_time(&now, t);
7255 invocation_id = samdb_ntds_invocation_id(ldb);
7256 if (invocation_id == NULL) {
7257 return LDB_ERR_OPERATIONS_ERROR;
7260 /* if the existing link is active, remove its backlink */
7261 if (is_active) {
7263 ret = replmd_add_backlink(module, replmd_private, schema,
7264 src_obj_dn, target_guid, false,
7265 attr, NULL);
7266 if (ret != LDB_SUCCESS) {
7267 return ret;
7271 /* mark the existing value as deleted */
7272 ret = replmd_update_la_val(mem_ctx, output_val, target_dsdb_dn,
7273 target_dsdb_dn, invocation_id, seq_num,
7274 seq_num, now, true);
7275 return ret;
7279 * Checks for a conflict in single-valued link attributes, and tries to
7280 * resolve the problem if possible.
7282 * Single-valued links should only ever have one active value. If we already
7283 * have an active link value, and during replication we receive an active link
7284 * value for a different target DN, then we need to resolve this inconsistency
7285 * and determine which value should be active. If the received info is better/
7286 * newer than the existing link attribute, then we need to set our existing
7287 * link as deleted. If the received info is worse/older, then we should continue
7288 * to add it, but set it as an inactive link.
7290 * Note that this is a corner-case that is unlikely to happen (but if it does
7291 * happen, we don't want it to break replication completely).
7293 * @param pdn_being_modified the parsed DN corresponding to the received link
7294 * target (note this is NULL if the link does not already exist in our DB)
7295 * @param pdn_list all the source object's Parsed-DNs for this attribute, i.e.
7296 * any existing active or inactive values for the attribute in our DB.
7297 * @param dsdb_dn the target DN for the received link attribute
7298 * @param add_as_inactive gets set to true if the received link is worse than
7299 * the existing link - it should still be added, but as an inactive link.
7301 static int replmd_check_singleval_la_conflict(struct ldb_module *module,
7302 struct replmd_private *replmd_private,
7303 TALLOC_CTX *mem_ctx,
7304 struct ldb_dn *src_obj_dn,
7305 struct drsuapi_DsReplicaLinkedAttribute *la,
7306 struct dsdb_dn *dsdb_dn,
7307 struct parsed_dn *pdn_being_modified,
7308 struct parsed_dn *pdn_list,
7309 struct ldb_message_element *old_el,
7310 const struct dsdb_schema *schema,
7311 const struct dsdb_attribute *attr,
7312 uint64_t seq_num,
7313 bool *add_as_inactive)
7315 struct parsed_dn *active_pdn = NULL;
7316 bool update_is_newer = false;
7317 int ret;
7320 * check if there's a conflict for single-valued links, i.e. an active
7321 * linked attribute already exists, but it has a different target value
7323 ret = replmd_get_active_singleval_link(module, mem_ctx, pdn_list,
7324 old_el->num_values, attr,
7325 &active_pdn);
7327 if (ret != LDB_SUCCESS) {
7328 return ret;
7332 * If no active value exists (or the received info is for the currently
7333 * active value), then no conflict exists
7335 if (active_pdn == NULL || active_pdn == pdn_being_modified) {
7336 return LDB_SUCCESS;
7339 DBG_WARNING("Link conflict for %s attribute on %s\n",
7340 attr->lDAPDisplayName, ldb_dn_get_linearized(src_obj_dn));
7342 /* Work out how to resolve the conflict based on which info is better */
7343 update_is_newer = replmd_link_update_is_newer(active_pdn, la);
7345 if (update_is_newer) {
7346 DBG_WARNING("Using received value %s, over existing target %s\n",
7347 ldb_dn_get_linearized(dsdb_dn->dn),
7348 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn));
7351 * Delete our existing active link. The received info will then
7352 * be added (through normal link processing) as the active value
7354 ret = replmd_delete_link_value(module, replmd_private, old_el,
7355 src_obj_dn, schema, attr,
7356 seq_num, true, &active_pdn->guid,
7357 active_pdn->dsdb_dn,
7358 active_pdn->v);
7360 if (ret != LDB_SUCCESS) {
7361 return ret;
7363 } else {
7364 DBG_WARNING("Using existing target %s, over received value %s\n",
7365 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn),
7366 ldb_dn_get_linearized(dsdb_dn->dn));
7369 * we want to keep our existing active link and add the
7370 * received link as inactive
7372 *add_as_inactive = true;
7375 return LDB_SUCCESS;
7379 process one linked attribute structure
7381 static int replmd_process_linked_attribute(struct ldb_module *module,
7382 struct replmd_private *replmd_private,
7383 struct la_entry *la_entry,
7384 struct ldb_request *parent)
7386 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7387 struct ldb_context *ldb = ldb_module_get_ctx(module);
7388 struct ldb_message *msg;
7389 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7390 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
7391 int ret;
7392 const struct dsdb_attribute *attr;
7393 struct dsdb_dn *dsdb_dn;
7394 uint64_t seq_num = 0;
7395 struct ldb_message_element *old_el;
7396 time_t t = time(NULL);
7397 struct parsed_dn *pdn_list, *pdn, *next;
7398 struct GUID guid = GUID_zero();
7399 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
7400 bool ignore_link;
7401 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
7402 struct dsdb_dn *old_dsdb_dn = NULL;
7403 struct ldb_val *val_to_update = NULL;
7404 bool add_as_inactive = false;
7407 * get the attribute being modified, the search result for the source object,
7408 * and the target object's DN details
7410 ret = replmd_extract_la_entry_details(module, la_entry, tmp_ctx, &attr,
7411 &msg, &dsdb_dn);
7413 if (ret != LDB_SUCCESS) {
7414 talloc_free(tmp_ctx);
7415 return ret;
7419 * Check for deleted objects per MS-DRSR 4.1.10.6.14
7420 * ProcessLinkValue, because link updates are not applied to
7421 * recycled and tombstone objects. We don't have to delete
7422 * any existing link, that should have happened when the
7423 * object deletion was replicated or initiated.
7425 replmd_deletion_state(module, msg, &deletion_state, NULL);
7427 if (deletion_state >= OBJECT_RECYCLED) {
7428 talloc_free(tmp_ctx);
7429 return LDB_SUCCESS;
7432 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7433 if (old_el == NULL) {
7434 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
7435 if (ret != LDB_SUCCESS) {
7436 ldb_module_oom(module);
7437 talloc_free(tmp_ctx);
7438 return LDB_ERR_OPERATIONS_ERROR;
7440 } else {
7441 old_el->flags = LDB_FLAG_MOD_REPLACE;
7444 /* parse the existing links */
7445 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx, old_el, &pdn_list,
7446 attr->syntax->ldap_oid, parent);
7448 if (ret != LDB_SUCCESS) {
7449 talloc_free(tmp_ctx);
7450 return ret;
7453 ret = replmd_check_target_exists(module, dsdb_dn, la_entry, msg->dn,
7454 true, &guid, &ignore_link);
7456 if (ret != LDB_SUCCESS) {
7457 talloc_free(tmp_ctx);
7458 return ret;
7462 * there are some cases where the target object doesn't exist, but it's
7463 * OK to ignore the linked attribute
7465 if (ignore_link) {
7466 talloc_free(tmp_ctx);
7467 return ret;
7470 /* see if this link already exists */
7471 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
7472 &guid,
7473 dsdb_dn->dn,
7474 dsdb_dn->extra_part, 0,
7475 &pdn, &next,
7476 attr->syntax->ldap_oid,
7477 true);
7478 if (ret != LDB_SUCCESS) {
7479 talloc_free(tmp_ctx);
7480 return ret;
7483 if (!replmd_link_update_is_newer(pdn, la)) {
7484 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
7485 old_el->name, ldb_dn_get_linearized(msg->dn),
7486 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
7487 talloc_free(tmp_ctx);
7488 return LDB_SUCCESS;
7491 /* get a seq_num for this change */
7492 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
7493 if (ret != LDB_SUCCESS) {
7494 talloc_free(tmp_ctx);
7495 return ret;
7499 * check for single-valued link conflicts, i.e. an active linked
7500 * attribute already exists, but it has a different target value
7502 if (active) {
7503 ret = replmd_check_singleval_la_conflict(module, replmd_private,
7504 tmp_ctx, msg->dn, la,
7505 dsdb_dn, pdn, pdn_list,
7506 old_el, schema, attr,
7507 seq_num,
7508 &add_as_inactive);
7509 if (ret != LDB_SUCCESS) {
7510 talloc_free(tmp_ctx);
7511 return ret;
7515 if (pdn != NULL) {
7516 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
7518 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
7519 /* remove the existing backlink */
7520 ret = replmd_add_backlink(module, replmd_private,
7521 schema,
7522 msg->dn,
7523 &pdn->guid, false, attr,
7524 parent);
7525 if (ret != LDB_SUCCESS) {
7526 talloc_free(tmp_ctx);
7527 return ret;
7531 val_to_update = pdn->v;
7532 old_dsdb_dn = pdn->dsdb_dn;
7534 } else {
7535 unsigned offset;
7538 * We know where the new one needs to be, from the *next
7539 * pointer into pdn_list.
7541 if (next == NULL) {
7542 offset = old_el->num_values;
7543 } else {
7544 if (next->dsdb_dn == NULL) {
7545 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
7546 attr->syntax->ldap_oid);
7547 if (ret != LDB_SUCCESS) {
7548 return ret;
7551 offset = next - pdn_list;
7552 if (offset > old_el->num_values) {
7553 talloc_free(tmp_ctx);
7554 return LDB_ERR_OPERATIONS_ERROR;
7558 old_el->values = talloc_realloc(msg->elements, old_el->values,
7559 struct ldb_val, old_el->num_values+1);
7560 if (!old_el->values) {
7561 ldb_module_oom(module);
7562 return LDB_ERR_OPERATIONS_ERROR;
7565 if (offset != old_el->num_values) {
7566 memmove(&old_el->values[offset + 1], &old_el->values[offset],
7567 (old_el->num_values - offset) * sizeof(old_el->values[0]));
7570 old_el->num_values++;
7572 val_to_update = &old_el->values[offset];
7573 old_dsdb_dn = NULL;
7576 /* set the link attribute's value to the info that was received */
7577 ret = replmd_set_la_val(tmp_ctx, val_to_update, dsdb_dn, old_dsdb_dn,
7578 &la->meta_data.originating_invocation_id,
7579 la->meta_data.originating_usn, seq_num,
7580 la->meta_data.originating_change_time,
7581 la->meta_data.version,
7582 !active);
7583 if (ret != LDB_SUCCESS) {
7584 talloc_free(tmp_ctx);
7585 return ret;
7588 if (add_as_inactive) {
7590 /* Set the new link as inactive/deleted to avoid conflicts */
7591 ret = replmd_delete_link_value(module, replmd_private, old_el,
7592 msg->dn, schema, attr, seq_num,
7593 false, &guid, dsdb_dn,
7594 val_to_update);
7596 if (ret != LDB_SUCCESS) {
7597 talloc_free(tmp_ctx);
7598 return ret;
7601 } else if (active) {
7603 /* if the new link is active, then add the new backlink */
7604 ret = replmd_add_backlink(module, replmd_private,
7605 schema,
7606 msg->dn,
7607 &guid, true, attr,
7608 parent);
7609 if (ret != LDB_SUCCESS) {
7610 talloc_free(tmp_ctx);
7611 return ret;
7615 /* we only change whenChanged and uSNChanged if the seq_num
7616 has changed */
7617 ret = add_time_element(msg, "whenChanged", t);
7618 if (ret != LDB_SUCCESS) {
7619 talloc_free(tmp_ctx);
7620 ldb_operr(ldb);
7621 return ret;
7624 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
7625 if (ret != LDB_SUCCESS) {
7626 talloc_free(tmp_ctx);
7627 ldb_operr(ldb);
7628 return ret;
7631 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7632 if (old_el == NULL) {
7633 talloc_free(tmp_ctx);
7634 return ldb_operr(ldb);
7637 ret = dsdb_check_single_valued_link(attr, old_el);
7638 if (ret != LDB_SUCCESS) {
7639 talloc_free(tmp_ctx);
7640 return ret;
7643 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
7645 ret = linked_attr_modify(module, msg, parent);
7646 if (ret != LDB_SUCCESS) {
7647 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
7648 ldb_errstring(ldb),
7649 ldb_ldif_message_redacted_string(ldb,
7650 tmp_ctx,
7651 LDB_CHANGETYPE_MODIFY,
7652 msg));
7653 talloc_free(tmp_ctx);
7654 return ret;
7657 talloc_free(tmp_ctx);
7659 return ret;
7662 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
7664 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
7665 return replmd_extended_replicated_objects(module, req);
7668 return ldb_next_request(module, req);
7673 we hook into the transaction operations to allow us to
7674 perform the linked attribute updates at the end of the whole
7675 transaction. This allows a forward linked attribute to be created
7676 before the object is created. During a vampire, w2k8 sends us linked
7677 attributes before the objects they are part of.
7679 static int replmd_start_transaction(struct ldb_module *module)
7681 /* create our private structure for this transaction */
7682 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
7683 struct replmd_private);
7684 replmd_txn_cleanup(replmd_private);
7686 /* free any leftover mod_usn records from cancelled
7687 transactions */
7688 while (replmd_private->ncs) {
7689 struct nc_entry *e = replmd_private->ncs;
7690 DLIST_REMOVE(replmd_private->ncs, e);
7691 talloc_free(e);
7694 replmd_private->originating_updates = false;
7696 return ldb_next_start_trans(module);
7700 on prepare commit we loop over our queued la_context structures and
7701 apply each of them
7703 static int replmd_prepare_commit(struct ldb_module *module)
7705 struct replmd_private *replmd_private =
7706 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
7707 struct la_entry *la, *prev;
7708 int ret;
7711 * Walk the list of linked attributes from DRS replication.
7713 * We walk backwards, to do the first entry first, as we
7714 * added the entries with DLIST_ADD() which puts them at the
7715 * start of the list
7717 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
7718 prev = DLIST_PREV(la);
7719 DLIST_REMOVE(replmd_private->la_list, la);
7720 ret = replmd_process_linked_attribute(module, replmd_private,
7721 la, NULL);
7722 if (ret != LDB_SUCCESS) {
7723 replmd_txn_cleanup(replmd_private);
7724 return ret;
7728 replmd_txn_cleanup(replmd_private);
7730 /* possibly change @REPLCHANGED */
7731 ret = replmd_notify_store(module, NULL);
7732 if (ret != LDB_SUCCESS) {
7733 return ret;
7736 return ldb_next_prepare_commit(module);
7739 static int replmd_del_transaction(struct ldb_module *module)
7741 struct replmd_private *replmd_private =
7742 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
7743 replmd_txn_cleanup(replmd_private);
7745 return ldb_next_del_trans(module);
7749 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
7750 .name = "repl_meta_data",
7751 .init_context = replmd_init,
7752 .add = replmd_add,
7753 .modify = replmd_modify,
7754 .rename = replmd_rename,
7755 .del = replmd_delete,
7756 .extended = replmd_extended,
7757 .start_transaction = replmd_start_transaction,
7758 .prepare_commit = replmd_prepare_commit,
7759 .del_transaction = replmd_del_transaction,
7762 int ldb_repl_meta_data_module_init(const char *version)
7764 LDB_MODULE_CHECK_VERSION(version);
7765 return ldb_register_module(&ldb_repl_meta_data_module_ops);