dsdb:repl_meta_data: implement DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS control
[Samba.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blobc7aacd83615c5a3d26eda63a68e0dcab38334724
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
57 /* the RMD_VERSION for linked attributes starts from 1 */
58 #define RMD_VERSION_INITIAL 1
61 * It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
62 * Deleted Objects Container
64 static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME = 2650466015990000000ULL;
66 struct replmd_private {
67 TALLOC_CTX *la_ctx;
68 struct la_entry *la_list;
69 struct nc_entry {
70 struct nc_entry *prev, *next;
71 struct ldb_dn *dn;
72 uint64_t mod_usn;
73 uint64_t mod_usn_urgent;
74 } *ncs;
75 struct ldb_dn *schema_dn;
76 bool originating_updates;
77 bool sorted_links;
80 struct la_entry {
81 struct la_entry *next, *prev;
82 struct drsuapi_DsReplicaLinkedAttribute *la;
83 uint32_t dsdb_repl_flags;
86 struct replmd_replicated_request {
87 struct ldb_module *module;
88 struct ldb_request *req;
90 const struct dsdb_schema *schema;
91 struct GUID our_invocation_id;
93 /* the controls we pass down */
94 struct ldb_control **controls;
97 * Backlinks for the replmd_add() case (we want to create
98 * backlinks after creating the user, but before the end of
99 * the ADD request)
101 struct la_backlink *la_backlinks;
103 /* details for the mode where we apply a bunch of inbound replication meessages */
104 bool apply_mode;
105 uint32_t index_current;
106 struct dsdb_extended_replicated_objects *objs;
108 struct ldb_message *search_msg;
109 struct GUID local_parent_guid;
111 uint64_t seq_num;
112 bool is_urgent;
114 bool isDeleted;
117 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
118 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
119 static int replmd_check_upgrade_links(struct ldb_context *ldb,
120 struct parsed_dn *dns, uint32_t count,
121 struct ldb_message_element *el,
122 const char *ldap_oid);
123 static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
124 struct la_entry *la);
125 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
126 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
127 uint64_t usn, uint64_t local_usn, NTTIME nttime,
128 uint32_t version, bool deleted);
130 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
131 struct ldb_context *ldb,
132 struct ldb_dn *dn,
133 const char *rdn_name,
134 const struct ldb_val *rdn_value,
135 struct GUID guid);
137 enum urgent_situation {
138 REPL_URGENT_ON_CREATE = 1,
139 REPL_URGENT_ON_UPDATE = 2,
140 REPL_URGENT_ON_DELETE = 4
143 enum deletion_state {
144 OBJECT_NOT_DELETED=1,
145 OBJECT_DELETED=2,
146 OBJECT_RECYCLED=3,
147 OBJECT_TOMBSTONE=4,
148 OBJECT_REMOVED=5
151 static void replmd_deletion_state(struct ldb_module *module,
152 const struct ldb_message *msg,
153 enum deletion_state *current_state,
154 enum deletion_state *next_state)
156 int ret;
157 bool enabled = false;
159 if (msg == NULL) {
160 *current_state = OBJECT_REMOVED;
161 if (next_state != NULL) {
162 *next_state = OBJECT_REMOVED;
164 return;
167 ret = dsdb_recyclebin_enabled(module, &enabled);
168 if (ret != LDB_SUCCESS) {
169 enabled = false;
172 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
173 if (!enabled) {
174 *current_state = OBJECT_TOMBSTONE;
175 if (next_state != NULL) {
176 *next_state = OBJECT_REMOVED;
178 return;
181 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
182 *current_state = OBJECT_RECYCLED;
183 if (next_state != NULL) {
184 *next_state = OBJECT_REMOVED;
186 return;
189 *current_state = OBJECT_DELETED;
190 if (next_state != NULL) {
191 *next_state = OBJECT_RECYCLED;
193 return;
196 *current_state = OBJECT_NOT_DELETED;
197 if (next_state == NULL) {
198 return;
201 if (enabled) {
202 *next_state = OBJECT_DELETED;
203 } else {
204 *next_state = OBJECT_TOMBSTONE;
208 static const struct {
209 const char *update_name;
210 enum urgent_situation repl_situation;
211 } urgent_objects[] = {
212 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
213 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
214 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
215 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
216 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
217 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
218 {NULL, 0}
221 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
222 static const char *urgent_attrs[] = {
223 "lockoutTime",
224 "pwdLastSet",
225 "userAccountControl",
226 NULL
230 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
231 enum urgent_situation situation)
233 unsigned int i, j;
234 for (i=0; urgent_objects[i].update_name; i++) {
236 if ((situation & urgent_objects[i].repl_situation) == 0) {
237 continue;
240 for (j=0; j<objectclass_el->num_values; j++) {
241 const struct ldb_val *v = &objectclass_el->values[j];
242 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
243 return true;
247 return false;
250 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
252 if (ldb_attr_in_list(urgent_attrs, el->name)) {
253 return true;
255 return false;
258 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
261 initialise the module
262 allocate the private structure and build the list
263 of partition DNs for use by replmd_notify()
265 static int replmd_init(struct ldb_module *module)
267 struct replmd_private *replmd_private;
268 struct ldb_context *ldb = ldb_module_get_ctx(module);
269 static const char *samba_dsdb_attrs[] = { SAMBA_COMPATIBLE_FEATURES_ATTR, NULL };
270 struct ldb_dn *samba_dsdb_dn;
271 struct ldb_result *res;
272 int ret;
273 TALLOC_CTX *frame = talloc_stackframe();
274 replmd_private = talloc_zero(module, struct replmd_private);
275 if (replmd_private == NULL) {
276 ldb_oom(ldb);
277 TALLOC_FREE(frame);
278 return LDB_ERR_OPERATIONS_ERROR;
280 ldb_module_set_private(module, replmd_private);
282 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
284 samba_dsdb_dn = ldb_dn_new(frame, ldb, "@SAMBA_DSDB");
285 if (!samba_dsdb_dn) {
286 TALLOC_FREE(frame);
287 return ldb_oom(ldb);
290 ret = dsdb_module_search_dn(module, frame, &res, samba_dsdb_dn,
291 samba_dsdb_attrs, DSDB_FLAG_NEXT_MODULE, NULL);
292 if (ret == LDB_SUCCESS) {
293 replmd_private->sorted_links
294 = ldb_msg_check_string_attribute(res->msgs[0],
295 SAMBA_COMPATIBLE_FEATURES_ATTR,
296 SAMBA_SORTED_LINKS_FEATURE);
298 TALLOC_FREE(frame);
300 return ldb_next_init(module);
304 cleanup our per-transaction contexts
306 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
308 talloc_free(replmd_private->la_ctx);
309 replmd_private->la_list = NULL;
310 replmd_private->la_ctx = NULL;
315 struct la_backlink {
316 struct la_backlink *next, *prev;
317 const char *attr_name;
318 struct ldb_dn *forward_dn;
319 struct GUID target_guid;
320 bool active;
324 a ldb_modify request operating on modules below the
325 current module
327 static int linked_attr_modify(struct ldb_module *module,
328 const struct ldb_message *message,
329 struct ldb_request *parent)
331 struct ldb_request *mod_req;
332 int ret;
333 struct ldb_context *ldb = ldb_module_get_ctx(module);
334 TALLOC_CTX *tmp_ctx = talloc_new(module);
335 struct ldb_result *res;
337 res = talloc_zero(tmp_ctx, struct ldb_result);
338 if (!res) {
339 talloc_free(tmp_ctx);
340 return ldb_oom(ldb_module_get_ctx(module));
343 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
344 message,
345 NULL,
346 res,
347 ldb_modify_default_callback,
348 parent);
349 LDB_REQ_SET_LOCATION(mod_req);
350 if (ret != LDB_SUCCESS) {
351 talloc_free(tmp_ctx);
352 return ret;
355 ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
356 false, NULL);
357 if (ret != LDB_SUCCESS) {
358 return ret;
361 /* Run the new request */
362 ret = ldb_next_request(module, mod_req);
364 if (ret == LDB_SUCCESS) {
365 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
368 talloc_free(tmp_ctx);
369 return ret;
373 process a backlinks we accumulated during a transaction, adding and
374 deleting the backlinks from the target objects
376 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
378 struct ldb_dn *target_dn, *source_dn;
379 int ret;
380 struct ldb_context *ldb = ldb_module_get_ctx(module);
381 struct ldb_message *msg;
382 TALLOC_CTX *frame = talloc_stackframe();
383 char *dn_string;
386 - find DN of target
387 - find DN of source
388 - construct ldb_message
389 - either an add or a delete
391 ret = dsdb_module_dn_by_guid(module, frame, &bl->target_guid, &target_dn, parent);
392 if (ret != LDB_SUCCESS) {
393 struct GUID_txt_buf guid_str;
394 DBG_WARNING("Failed to find target DN for linked attribute with GUID %s\n",
395 GUID_buf_string(&bl->target_guid, &guid_str));
396 DBG_WARNING("Please run 'samba-tool dbcheck' to resolve any missing backlinks.\n");
397 talloc_free(frame);
398 return LDB_SUCCESS;
401 msg = ldb_msg_new(frame);
402 if (msg == NULL) {
403 ldb_module_oom(module);
404 talloc_free(frame);
405 return LDB_ERR_OPERATIONS_ERROR;
408 source_dn = ldb_dn_copy(frame, bl->forward_dn);
409 if (!source_dn) {
410 ldb_module_oom(module);
411 talloc_free(frame);
412 return LDB_ERR_OPERATIONS_ERROR;
413 } else {
414 /* Filter down to the attributes we want in the backlink */
415 const char *accept[] = { "GUID", "SID", NULL };
416 ldb_dn_extended_filter(source_dn, accept);
419 /* construct a ldb_message for adding/deleting the backlink */
420 msg->dn = target_dn;
421 dn_string = ldb_dn_get_extended_linearized(frame, bl->forward_dn, 1);
422 if (!dn_string) {
423 ldb_module_oom(module);
424 talloc_free(frame);
425 return LDB_ERR_OPERATIONS_ERROR;
427 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
428 if (ret != LDB_SUCCESS) {
429 talloc_free(frame);
430 return ret;
432 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
434 /* a backlink should never be single valued. Unfortunately the
435 exchange schema has a attribute
436 msExchBridgeheadedLocalConnectorsDNBL which is single
437 valued and a backlink. We need to cope with that by
438 ignoring the single value flag */
439 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
441 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
442 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
443 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
444 cope with possible corruption where the backlink has
445 already been removed */
446 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
447 ldb_dn_get_linearized(target_dn),
448 ldb_dn_get_linearized(source_dn),
449 ldb_errstring(ldb)));
450 ret = LDB_SUCCESS;
451 } else if (ret != LDB_SUCCESS) {
452 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
453 bl->active?"add":"remove",
454 ldb_dn_get_linearized(source_dn),
455 ldb_dn_get_linearized(target_dn),
456 ldb_errstring(ldb));
457 talloc_free(frame);
458 return ret;
460 talloc_free(frame);
461 return ret;
465 add a backlink to the list of backlinks to add/delete in the prepare
466 commit
468 forward_dn is stolen onto the defereed context
470 static int replmd_defer_add_backlink(struct ldb_module *module,
471 struct replmd_private *replmd_private,
472 const struct dsdb_schema *schema,
473 struct replmd_replicated_request *ac,
474 struct ldb_dn *forward_dn,
475 struct GUID *target_guid, bool active,
476 const struct dsdb_attribute *schema_attr,
477 struct ldb_request *parent)
479 const struct dsdb_attribute *target_attr;
480 struct la_backlink *bl;
482 bl = talloc(ac, struct la_backlink);
483 if (bl == NULL) {
484 ldb_module_oom(module);
485 return LDB_ERR_OPERATIONS_ERROR;
488 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
489 if (!target_attr) {
491 * windows 2003 has a broken schema where the
492 * definition of msDS-IsDomainFor is missing (which is
493 * supposed to be the backlink of the
494 * msDS-HasDomainNCs attribute
496 return LDB_SUCCESS;
499 bl->attr_name = target_attr->lDAPDisplayName;
500 bl->forward_dn = talloc_steal(bl, forward_dn);
501 bl->target_guid = *target_guid;
502 bl->active = active;
504 DLIST_ADD(ac->la_backlinks, bl);
506 return LDB_SUCCESS;
510 add a backlink to the list of backlinks to add/delete in the prepare
511 commit
513 static int replmd_add_backlink(struct ldb_module *module,
514 struct replmd_private *replmd_private,
515 const struct dsdb_schema *schema,
516 struct ldb_dn *forward_dn,
517 struct GUID *target_guid, bool active,
518 const struct dsdb_attribute *schema_attr,
519 struct ldb_request *parent)
521 const struct dsdb_attribute *target_attr;
522 struct la_backlink bl;
523 int ret;
525 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
526 if (!target_attr) {
528 * windows 2003 has a broken schema where the
529 * definition of msDS-IsDomainFor is missing (which is
530 * supposed to be the backlink of the
531 * msDS-HasDomainNCs attribute
533 return LDB_SUCCESS;
536 bl.attr_name = target_attr->lDAPDisplayName;
537 bl.forward_dn = forward_dn;
538 bl.target_guid = *target_guid;
539 bl.active = active;
541 ret = replmd_process_backlink(module, &bl, parent);
542 return ret;
547 * Callback for most write operations in this module:
549 * notify the repl task that a object has changed. The notifies are
550 * gathered up in the replmd_private structure then written to the
551 * @REPLCHANGED object in each partition during the prepare_commit
553 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
555 int ret;
556 struct replmd_replicated_request *ac =
557 talloc_get_type_abort(req->context, struct replmd_replicated_request);
558 struct replmd_private *replmd_private =
559 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
560 struct nc_entry *modified_partition;
561 struct ldb_control *partition_ctrl;
562 const struct dsdb_control_current_partition *partition;
564 struct ldb_control **controls;
566 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
568 controls = ares->controls;
569 if (ldb_request_get_control(ac->req,
570 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
572 * Remove the current partition control from what we pass up
573 * the chain if it hasn't been requested manually.
575 controls = ldb_controls_except_specified(ares->controls, ares,
576 partition_ctrl);
579 if (ares->error != LDB_SUCCESS) {
580 struct GUID_txt_buf guid_txt;
581 struct ldb_message *msg = NULL;
582 char *s = NULL;
584 if (ac->apply_mode == false) {
585 DBG_NOTICE("Originating update failure. Error is: %s\n",
586 ldb_strerror(ares->error));
587 return ldb_module_done(ac->req, controls,
588 ares->response, ares->error);
591 msg = ac->objs->objects[ac->index_current].msg;
593 * Set at DBG_NOTICE as once these start to happe, they
594 * will happen a lot until resolved, due to repeated
595 * replication. The caller will probably print the
596 * ldb error string anyway.
598 DBG_NOTICE("DRS replication apply failure for %s. Error is: %s\n",
599 ldb_dn_get_linearized(msg->dn),
600 ldb_strerror(ares->error));
602 s = ldb_ldif_message_redacted_string(ldb_module_get_ctx(ac->module),
604 LDB_CHANGETYPE_ADD,
605 msg);
607 DBG_INFO("Failing DRS %s replication message was %s:\n%s\n",
608 ac->search_msg == NULL ? "ADD" : "MODIFY",
609 GUID_buf_string(&ac->objs->objects[ac->index_current].object_guid,
610 &guid_txt),
612 talloc_free(s);
613 return ldb_module_done(ac->req, controls,
614 ares->response, ares->error);
617 if (ares->type != LDB_REPLY_DONE) {
618 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
619 return ldb_module_done(ac->req, NULL,
620 NULL, LDB_ERR_OPERATIONS_ERROR);
623 if (ac->apply_mode == false) {
624 struct la_backlink *bl;
626 * process our backlink list after an replmd_add(),
627 * creating and deleting backlinks as necessary (this
628 * code is sync). The other cases are handled inline
629 * with the modify.
631 for (bl=ac->la_backlinks; bl; bl=bl->next) {
632 ret = replmd_process_backlink(ac->module, bl, ac->req);
633 if (ret != LDB_SUCCESS) {
634 return ldb_module_done(ac->req, NULL,
635 NULL, ret);
640 if (!partition_ctrl) {
641 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
642 return ldb_module_done(ac->req, NULL,
643 NULL, LDB_ERR_OPERATIONS_ERROR);
646 partition = talloc_get_type_abort(partition_ctrl->data,
647 struct dsdb_control_current_partition);
649 if (ac->seq_num > 0) {
650 for (modified_partition = replmd_private->ncs; modified_partition;
651 modified_partition = modified_partition->next) {
652 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
653 break;
657 if (modified_partition == NULL) {
658 modified_partition = talloc_zero(replmd_private, struct nc_entry);
659 if (!modified_partition) {
660 ldb_oom(ldb_module_get_ctx(ac->module));
661 return ldb_module_done(ac->req, NULL,
662 NULL, LDB_ERR_OPERATIONS_ERROR);
664 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
665 if (!modified_partition->dn) {
666 ldb_oom(ldb_module_get_ctx(ac->module));
667 return ldb_module_done(ac->req, NULL,
668 NULL, LDB_ERR_OPERATIONS_ERROR);
670 DLIST_ADD(replmd_private->ncs, modified_partition);
673 if (ac->seq_num > modified_partition->mod_usn) {
674 modified_partition->mod_usn = ac->seq_num;
675 if (ac->is_urgent) {
676 modified_partition->mod_usn_urgent = ac->seq_num;
679 if (!ac->apply_mode) {
680 replmd_private->originating_updates = true;
684 if (ac->apply_mode) {
685 ret = replmd_replicated_apply_isDeleted(ac);
686 if (ret != LDB_SUCCESS) {
687 return ldb_module_done(ac->req, NULL, NULL, ret);
689 return ret;
690 } else {
691 /* free the partition control container here, for the
692 * common path. Other cases will have it cleaned up
693 * eventually with the ares */
694 talloc_free(partition_ctrl);
695 return ldb_module_done(ac->req, controls,
696 ares->response, LDB_SUCCESS);
702 * update a @REPLCHANGED record in each partition if there have been
703 * any writes of replicated data in the partition
705 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
707 struct replmd_private *replmd_private =
708 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
710 while (replmd_private->ncs) {
711 int ret;
712 struct nc_entry *modified_partition = replmd_private->ncs;
714 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
715 modified_partition->mod_usn,
716 modified_partition->mod_usn_urgent, parent);
717 if (ret != LDB_SUCCESS) {
718 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
719 ldb_dn_get_linearized(modified_partition->dn)));
720 return ret;
723 if (ldb_dn_compare(modified_partition->dn,
724 replmd_private->schema_dn) == 0) {
725 struct ldb_result *ext_res;
726 ret = dsdb_module_extended(module,
727 replmd_private->schema_dn,
728 &ext_res,
729 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
730 ext_res,
731 DSDB_FLAG_NEXT_MODULE,
732 parent);
733 if (ret != LDB_SUCCESS) {
734 return ret;
736 talloc_free(ext_res);
739 DLIST_REMOVE(replmd_private->ncs, modified_partition);
740 talloc_free(modified_partition);
743 return LDB_SUCCESS;
748 created a replmd_replicated_request context
750 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
751 struct ldb_request *req)
753 struct ldb_context *ldb;
754 struct replmd_replicated_request *ac;
755 const struct GUID *our_invocation_id;
757 ldb = ldb_module_get_ctx(module);
759 ac = talloc_zero(req, struct replmd_replicated_request);
760 if (ac == NULL) {
761 ldb_oom(ldb);
762 return NULL;
765 ac->module = module;
766 ac->req = req;
768 ac->schema = dsdb_get_schema(ldb, ac);
769 if (!ac->schema) {
770 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
771 "replmd_modify: no dsdb_schema loaded");
772 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
773 talloc_free(ac);
774 return NULL;
777 /* get our invocationId */
778 our_invocation_id = samdb_ntds_invocation_id(ldb);
779 if (!our_invocation_id) {
780 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
781 "replmd_add: unable to find invocationId\n");
782 talloc_free(ac);
783 return NULL;
785 ac->our_invocation_id = *our_invocation_id;
787 return ac;
791 add a time element to a record
793 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
795 struct ldb_message_element *el;
796 char *s;
797 int ret;
799 if (ldb_msg_find_element(msg, attr) != NULL) {
800 return LDB_SUCCESS;
803 s = ldb_timestring(msg, t);
804 if (s == NULL) {
805 return LDB_ERR_OPERATIONS_ERROR;
808 ret = ldb_msg_add_string(msg, attr, s);
809 if (ret != LDB_SUCCESS) {
810 return ret;
813 el = ldb_msg_find_element(msg, attr);
814 /* always set as replace. This works because on add ops, the flag
815 is ignored */
816 el->flags = LDB_FLAG_MOD_REPLACE;
818 return LDB_SUCCESS;
822 add a uint64_t element to a record
824 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
825 const char *attr, uint64_t v)
827 struct ldb_message_element *el;
828 int ret;
830 if (ldb_msg_find_element(msg, attr) != NULL) {
831 return LDB_SUCCESS;
834 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
835 if (ret != LDB_SUCCESS) {
836 return ret;
839 el = ldb_msg_find_element(msg, attr);
840 /* always set as replace. This works because on add ops, the flag
841 is ignored */
842 el->flags = LDB_FLAG_MOD_REPLACE;
844 return LDB_SUCCESS;
847 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
848 const struct replPropertyMetaData1 *m2,
849 const uint32_t *rdn_attid)
852 * This assignment seems inoccous, but it is critical for the
853 * system, as we need to do the comparisons as a unsigned
854 * quantity, not signed (enums are signed integers)
856 uint32_t attid_1 = m1->attid;
857 uint32_t attid_2 = m2->attid;
859 if (attid_1 == attid_2) {
860 return 0;
864 * See above regarding this being an unsigned comparison.
865 * Otherwise when the high bit is set on non-standard
866 * attributes, they would end up first, before objectClass
867 * (0).
869 return attid_1 > attid_2 ? 1 : -1;
872 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
873 struct replPropertyMetaDataCtr1 *ctr1,
874 struct ldb_dn *dn)
876 if (ctr1->count == 0) {
877 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
878 "No elements found in replPropertyMetaData for %s!\n",
879 ldb_dn_get_linearized(dn));
880 return LDB_ERR_CONSTRAINT_VIOLATION;
883 /* the objectClass attribute is value 0x00000000, so must be first */
884 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
885 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
886 "No objectClass found in replPropertyMetaData for %s!\n",
887 ldb_dn_get_linearized(dn));
888 return LDB_ERR_OBJECT_CLASS_VIOLATION;
891 return LDB_SUCCESS;
894 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
895 struct replPropertyMetaDataCtr1 *ctr1,
896 struct ldb_dn *dn)
898 /* Note this is O(n^2) for the almost-sorted case, which this is */
899 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, NULL,
900 replmd_replPropertyMetaData1_attid_sort);
901 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
904 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
905 const struct ldb_message_element *e2,
906 const struct dsdb_schema *schema)
908 const struct dsdb_attribute *a1;
909 const struct dsdb_attribute *a2;
912 * TODO: make this faster by caching the dsdb_attribute pointer
913 * on the ldb_messag_element
916 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
917 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
920 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
921 * in the schema
923 if (!a1 || !a2) {
924 return strcasecmp(e1->name, e2->name);
926 if (a1->attributeID_id == a2->attributeID_id) {
927 return 0;
929 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
932 static void replmd_ldb_message_sort(struct ldb_message *msg,
933 const struct dsdb_schema *schema)
935 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
938 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
939 const struct GUID *invocation_id,
940 uint64_t local_usn, NTTIME nttime);
942 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2);
944 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
945 struct ldb_message_element *el, struct parsed_dn **pdn,
946 const char *ldap_oid, struct ldb_request *parent);
948 static int check_parsed_dn_duplicates(struct ldb_module *module,
949 struct ldb_message_element *el,
950 struct parsed_dn *pdn);
953 fix up linked attributes in replmd_add.
954 This involves setting up the right meta-data in extended DN
955 components, and creating backlinks to the object
957 static int replmd_add_fix_la(struct ldb_module *module, TALLOC_CTX *mem_ctx,
958 struct replmd_private *replmd_private,
959 struct ldb_message_element *el,
960 struct replmd_replicated_request *ac,
961 NTTIME now,
962 struct ldb_dn *forward_dn,
963 const struct dsdb_attribute *sa,
964 struct ldb_request *parent)
966 unsigned int i;
967 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
968 struct ldb_context *ldb = ldb_module_get_ctx(module);
969 struct parsed_dn *pdn;
970 /* We will take a reference to the schema in replmd_add_backlink */
971 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
972 struct ldb_val *new_values = NULL;
973 int ret;
975 if (dsdb_check_single_valued_link(sa, el) == LDB_SUCCESS) {
976 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
977 } else {
978 ldb_asprintf_errstring(ldb,
979 "Attribute %s is single valued but "
980 "more than one value has been supplied",
981 el->name);
982 talloc_free(tmp_ctx);
983 return LDB_ERR_CONSTRAINT_VIOLATION;
986 ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
987 sa->syntax->ldap_oid, parent);
988 if (ret != LDB_SUCCESS) {
989 talloc_free(tmp_ctx);
990 return ret;
993 ret = check_parsed_dn_duplicates(module, el, pdn);
994 if (ret != LDB_SUCCESS) {
995 talloc_free(tmp_ctx);
996 return ret;
999 new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
1000 if (new_values == NULL) {
1001 ldb_module_oom(module);
1002 talloc_free(tmp_ctx);
1003 return LDB_ERR_OPERATIONS_ERROR;
1006 for (i = 0; i < el->num_values; i++) {
1007 struct parsed_dn *p = &pdn[i];
1008 ret = replmd_build_la_val(el->values, p->v, p->dsdb_dn,
1009 &ac->our_invocation_id,
1010 ac->seq_num, now);
1011 if (ret != LDB_SUCCESS) {
1012 talloc_free(tmp_ctx);
1013 return ret;
1016 ret = replmd_defer_add_backlink(module, replmd_private,
1017 schema, ac,
1018 forward_dn, &p->guid, true, sa,
1019 parent);
1020 if (ret != LDB_SUCCESS) {
1021 talloc_free(tmp_ctx);
1022 return ret;
1025 new_values[i] = *p->v;
1027 el->values = talloc_steal(mem_ctx, new_values);
1029 talloc_free(tmp_ctx);
1030 return LDB_SUCCESS;
1033 static int replmd_add_make_extended_dn(struct ldb_request *req,
1034 const DATA_BLOB *guid_blob,
1035 struct ldb_dn **_extended_dn)
1037 int ret;
1038 const DATA_BLOB *sid_blob;
1039 /* Calculate an extended DN for any linked attributes */
1040 struct ldb_dn *extended_dn = ldb_dn_copy(req, req->op.add.message->dn);
1041 if (!extended_dn) {
1042 return LDB_ERR_OPERATIONS_ERROR;
1044 ret = ldb_dn_set_extended_component(extended_dn, "GUID", guid_blob);
1045 if (ret != LDB_SUCCESS) {
1046 return ret;
1049 sid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectSID");
1050 if (sid_blob != NULL) {
1051 ret = ldb_dn_set_extended_component(extended_dn, "SID", sid_blob);
1052 if (ret != LDB_SUCCESS) {
1053 return ret;
1056 *_extended_dn = extended_dn;
1057 return LDB_SUCCESS;
1061 intercept add requests
1063 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
1065 struct ldb_context *ldb;
1066 struct ldb_control *control;
1067 struct replmd_replicated_request *ac;
1068 enum ndr_err_code ndr_err;
1069 struct ldb_request *down_req;
1070 struct ldb_message *msg;
1071 const DATA_BLOB *guid_blob;
1072 DATA_BLOB guid_blob_stack;
1073 struct GUID guid;
1074 uint8_t guid_data[16];
1075 struct replPropertyMetaDataBlob nmd;
1076 struct ldb_val nmd_value;
1077 struct ldb_dn *extended_dn = NULL;
1080 * The use of a time_t here seems odd, but as the NTTIME
1081 * elements are actually declared as NTTIME_1sec in the IDL,
1082 * getting a higher resolution timestamp is not required.
1084 time_t t = time(NULL);
1085 NTTIME now;
1086 char *time_str;
1087 int ret;
1088 unsigned int i;
1089 unsigned int functional_level;
1090 uint32_t ni=0;
1091 bool allow_add_guid = false;
1092 bool remove_current_guid = false;
1093 bool is_urgent = false;
1094 bool is_schema_nc = false;
1095 struct ldb_message_element *objectclass_el;
1096 struct replmd_private *replmd_private =
1097 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
1099 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
1100 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
1101 if (control) {
1102 allow_add_guid = true;
1105 /* do not manipulate our control entries */
1106 if (ldb_dn_is_special(req->op.add.message->dn)) {
1107 return ldb_next_request(module, req);
1110 ldb = ldb_module_get_ctx(module);
1112 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
1114 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
1115 if (guid_blob != NULL) {
1116 if (!allow_add_guid) {
1117 ldb_set_errstring(ldb,
1118 "replmd_add: it's not allowed to add an object with objectGUID!");
1119 return LDB_ERR_UNWILLING_TO_PERFORM;
1120 } else {
1121 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
1122 if (!NT_STATUS_IS_OK(status)) {
1123 ldb_set_errstring(ldb,
1124 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1125 return LDB_ERR_UNWILLING_TO_PERFORM;
1127 /* we remove this attribute as it can be a string and
1128 * will not be treated correctly and then we will re-add
1129 * it later on in the good format */
1130 remove_current_guid = true;
1132 } else {
1133 /* a new GUID */
1134 guid = GUID_random();
1136 guid_blob_stack = data_blob_const(guid_data, sizeof(guid_data));
1138 /* This can't fail */
1139 ndr_push_struct_into_fixed_blob(&guid_blob_stack, &guid,
1140 (ndr_push_flags_fn_t)ndr_push_GUID);
1141 guid_blob = &guid_blob_stack;
1144 ac = replmd_ctx_init(module, req);
1145 if (ac == NULL) {
1146 return ldb_module_oom(module);
1149 functional_level = dsdb_functional_level(ldb);
1151 /* Get a sequence number from the backend */
1152 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
1153 if (ret != LDB_SUCCESS) {
1154 talloc_free(ac);
1155 return ret;
1158 /* we have to copy the message as the caller might have it as a const */
1159 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
1160 if (msg == NULL) {
1161 ldb_oom(ldb);
1162 talloc_free(ac);
1163 return LDB_ERR_OPERATIONS_ERROR;
1166 /* generated times */
1167 unix_to_nt_time(&now, t);
1168 time_str = ldb_timestring(msg, t);
1169 if (!time_str) {
1170 ldb_oom(ldb);
1171 talloc_free(ac);
1172 return LDB_ERR_OPERATIONS_ERROR;
1174 if (remove_current_guid) {
1175 ldb_msg_remove_attr(msg,"objectGUID");
1179 * remove autogenerated attributes
1181 ldb_msg_remove_attr(msg, "whenCreated");
1182 ldb_msg_remove_attr(msg, "whenChanged");
1183 ldb_msg_remove_attr(msg, "uSNCreated");
1184 ldb_msg_remove_attr(msg, "uSNChanged");
1185 ldb_msg_remove_attr(msg, "replPropertyMetaData");
1188 * readd replicated attributes
1190 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1191 if (ret != LDB_SUCCESS) {
1192 ldb_oom(ldb);
1193 talloc_free(ac);
1194 return ret;
1197 /* build the replication meta_data */
1198 ZERO_STRUCT(nmd);
1199 nmd.version = 1;
1200 nmd.ctr.ctr1.count = msg->num_elements;
1201 nmd.ctr.ctr1.array = talloc_array(msg,
1202 struct replPropertyMetaData1,
1203 nmd.ctr.ctr1.count);
1204 if (!nmd.ctr.ctr1.array) {
1205 ldb_oom(ldb);
1206 talloc_free(ac);
1207 return LDB_ERR_OPERATIONS_ERROR;
1210 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1212 for (i=0; i < msg->num_elements;) {
1213 struct ldb_message_element *e = &msg->elements[i];
1214 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1215 const struct dsdb_attribute *sa;
1217 if (e->name[0] == '@') {
1218 i++;
1219 continue;
1222 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1223 if (!sa) {
1224 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1225 "replmd_add: attribute '%s' not defined in schema\n",
1226 e->name);
1227 talloc_free(ac);
1228 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1231 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1232 /* if the attribute is not replicated (0x00000001)
1233 * or constructed (0x00000004) it has no metadata
1235 i++;
1236 continue;
1239 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1240 if (extended_dn == NULL) {
1241 ret = replmd_add_make_extended_dn(req,
1242 guid_blob,
1243 &extended_dn);
1244 if (ret != LDB_SUCCESS) {
1245 talloc_free(ac);
1246 return ret;
1251 * Prepare the context for the backlinks and
1252 * create metadata for the forward links. The
1253 * backlinks are created in
1254 * replmd_op_callback() after the successful
1255 * ADD of the object.
1257 ret = replmd_add_fix_la(module, msg->elements,
1258 replmd_private, e,
1259 ac, now,
1260 extended_dn,
1261 sa, req);
1262 if (ret != LDB_SUCCESS) {
1263 talloc_free(ac);
1264 return ret;
1266 /* linked attributes are not stored in
1267 replPropertyMetaData in FL above w2k */
1268 i++;
1269 continue;
1272 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1273 m->version = 1;
1274 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1275 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1276 const char* rdn;
1278 if (rdn_val == NULL) {
1279 ldb_oom(ldb);
1280 talloc_free(ac);
1281 return LDB_ERR_OPERATIONS_ERROR;
1284 rdn = (const char*)rdn_val->data;
1285 if (strcmp(rdn, "Deleted Objects") == 0) {
1287 * Set the originating_change_time to 29/12/9999 at 23:59:59
1288 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1290 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1291 } else {
1292 m->originating_change_time = now;
1294 } else {
1295 m->originating_change_time = now;
1297 m->originating_invocation_id = ac->our_invocation_id;
1298 m->originating_usn = ac->seq_num;
1299 m->local_usn = ac->seq_num;
1300 ni++;
1302 if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1303 i++;
1304 continue;
1307 e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1309 if (e->num_values != 0) {
1310 i++;
1311 continue;
1314 ldb_msg_remove_element(msg, e);
1317 /* fix meta data count */
1318 nmd.ctr.ctr1.count = ni;
1321 * sort meta data array
1323 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1324 if (ret != LDB_SUCCESS) {
1325 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1326 talloc_free(ac);
1327 return ret;
1330 /* generated NDR encoded values */
1331 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1332 &nmd,
1333 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1334 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1335 ldb_oom(ldb);
1336 talloc_free(ac);
1337 return LDB_ERR_OPERATIONS_ERROR;
1341 * add the autogenerated values
1343 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1344 if (ret != LDB_SUCCESS) {
1345 ldb_oom(ldb);
1346 talloc_free(ac);
1347 return ret;
1349 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1350 if (ret != LDB_SUCCESS) {
1351 ldb_oom(ldb);
1352 talloc_free(ac);
1353 return ret;
1355 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1356 if (ret != LDB_SUCCESS) {
1357 ldb_oom(ldb);
1358 talloc_free(ac);
1359 return ret;
1361 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1362 if (ret != LDB_SUCCESS) {
1363 ldb_oom(ldb);
1364 talloc_free(ac);
1365 return ret;
1367 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1368 if (ret != LDB_SUCCESS) {
1369 ldb_oom(ldb);
1370 talloc_free(ac);
1371 return ret;
1375 * sort the attributes by attid before storing the object
1377 replmd_ldb_message_sort(msg, ac->schema);
1380 * Assert that we do have an objectClass
1382 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1383 if (objectclass_el == NULL) {
1384 ldb_asprintf_errstring(ldb, __location__
1385 ": objectClass missing on %s\n",
1386 ldb_dn_get_linearized(msg->dn));
1387 talloc_free(ac);
1388 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1390 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1391 REPL_URGENT_ON_CREATE);
1393 ac->is_urgent = is_urgent;
1394 ret = ldb_build_add_req(&down_req, ldb, ac,
1395 msg,
1396 req->controls,
1397 ac, replmd_op_callback,
1398 req);
1400 LDB_REQ_SET_LOCATION(down_req);
1401 if (ret != LDB_SUCCESS) {
1402 talloc_free(ac);
1403 return ret;
1406 /* current partition control is needed by "replmd_op_callback" */
1407 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1408 ret = ldb_request_add_control(down_req,
1409 DSDB_CONTROL_CURRENT_PARTITION_OID,
1410 false, NULL);
1411 if (ret != LDB_SUCCESS) {
1412 talloc_free(ac);
1413 return ret;
1417 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1418 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1419 if (ret != LDB_SUCCESS) {
1420 talloc_free(ac);
1421 return ret;
1425 /* mark the control done */
1426 if (control) {
1427 control->critical = 0;
1429 /* go on with the call chain */
1430 return ldb_next_request(module, down_req);
1435 * update the replPropertyMetaData for one element
1437 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1438 struct ldb_message *msg,
1439 struct ldb_message_element *el,
1440 struct ldb_message_element *old_el,
1441 struct replPropertyMetaDataBlob *omd,
1442 const struct dsdb_schema *schema,
1443 uint64_t *seq_num,
1444 const struct GUID *our_invocation_id,
1445 NTTIME now,
1446 bool is_schema_nc,
1447 bool is_forced_rodc,
1448 struct ldb_request *req)
1450 uint32_t i;
1451 const struct dsdb_attribute *a;
1452 struct replPropertyMetaData1 *md1;
1453 bool may_skip = false;
1454 uint32_t attid;
1456 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1457 if (a == NULL) {
1458 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1459 /* allow this to make it possible for dbcheck
1460 to remove bad attributes */
1461 return LDB_SUCCESS;
1464 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1465 el->name));
1466 return LDB_ERR_OPERATIONS_ERROR;
1469 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1471 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1472 return LDB_SUCCESS;
1476 * if the attribute's value haven't changed, and this isn't
1477 * just a delete of everything then return LDB_SUCCESS Unless
1478 * we have the provision control or if the attribute is
1479 * interSiteTopologyGenerator as this page explain:
1480 * http://support.microsoft.com/kb/224815 this attribute is
1481 * periodicaly written by the DC responsible for the intersite
1482 * generation in a given site
1484 * Unchanged could be deleting or replacing an already-gone
1485 * thing with an unconstrained delete/empty replace or a
1486 * replace with the same value, but not an add with the same
1487 * value because that could be about adding a duplicate (which
1488 * is for someone else to error out on).
1490 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1491 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1492 may_skip = true;
1494 } else if (old_el == NULL && el->num_values == 0) {
1495 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1496 may_skip = true;
1497 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1498 may_skip = true;
1500 } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1501 ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
1503 * We intentionally skip the version bump when attempting to
1504 * vanish links.
1506 * The control is set by dbcheck and expunge-tombstones which
1507 * both attempt to be non-replicating. Otherwise, making an
1508 * alteration to the replication state would trigger a
1509 * broadcast of all expunged objects.
1511 may_skip = true;
1514 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1515 may_skip = false;
1516 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1519 if (may_skip) {
1520 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1521 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1523 * allow this to make it possible for dbcheck
1524 * to rebuild broken metadata
1526 return LDB_SUCCESS;
1530 for (i=0; i<omd->ctr.ctr1.count; i++) {
1532 * First check if we find it under the msDS-IntID,
1533 * then check if we find it under the OID and
1534 * prefixMap ID.
1536 * This allows the administrator to simply re-write
1537 * the attributes and so restore replication, which is
1538 * likely what they will try to do.
1540 if (attid == omd->ctr.ctr1.array[i].attid) {
1541 break;
1544 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1545 break;
1549 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1550 /* linked attributes are not stored in
1551 replPropertyMetaData in FL above w2k, but we do
1552 raise the seqnum for the object */
1553 if (*seq_num == 0 &&
1554 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1555 return LDB_ERR_OPERATIONS_ERROR;
1557 return LDB_SUCCESS;
1560 if (i == omd->ctr.ctr1.count) {
1561 /* we need to add a new one */
1562 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1563 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1564 if (omd->ctr.ctr1.array == NULL) {
1565 ldb_oom(ldb);
1566 return LDB_ERR_OPERATIONS_ERROR;
1568 omd->ctr.ctr1.count++;
1569 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1572 /* Get a new sequence number from the backend. We only do this
1573 * if we have a change that requires a new
1574 * replPropertyMetaData element
1576 if (*seq_num == 0) {
1577 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1578 if (ret != LDB_SUCCESS) {
1579 return LDB_ERR_OPERATIONS_ERROR;
1583 md1 = &omd->ctr.ctr1.array[i];
1584 md1->version++;
1585 md1->attid = attid;
1587 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1588 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1589 const char* rdn;
1591 if (rdn_val == NULL) {
1592 ldb_oom(ldb);
1593 return LDB_ERR_OPERATIONS_ERROR;
1596 rdn = (const char*)rdn_val->data;
1597 if (strcmp(rdn, "Deleted Objects") == 0) {
1599 * Set the originating_change_time to 29/12/9999 at 23:59:59
1600 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1602 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1603 } else {
1604 md1->originating_change_time = now;
1606 } else {
1607 md1->originating_change_time = now;
1609 md1->originating_invocation_id = *our_invocation_id;
1610 md1->originating_usn = *seq_num;
1611 md1->local_usn = *seq_num;
1613 if (is_forced_rodc) {
1614 /* Force version to 0 to be overriden later via replication */
1615 md1->version = 0;
1618 return LDB_SUCCESS;
1622 * Bump the replPropertyMetaData version on an attribute, and if it
1623 * has changed (or forced by leaving rdn_old NULL), update the value
1624 * in the entry.
1626 * This is important, as calling a modify operation may not change the
1627 * version number if the values appear unchanged, but a rename between
1628 * parents bumps this value.
1631 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1632 struct ldb_message *msg,
1633 const struct ldb_val *rdn_new,
1634 const struct ldb_val *rdn_old,
1635 struct replPropertyMetaDataBlob *omd,
1636 struct replmd_replicated_request *ar,
1637 NTTIME now,
1638 bool is_schema_nc,
1639 bool is_forced_rodc)
1641 const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
1642 const struct dsdb_attribute *rdn_attr =
1643 dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
1644 const char *attr_name = rdn_attr != NULL ?
1645 rdn_attr->lDAPDisplayName :
1646 rdn_name;
1647 struct ldb_message_element new_el = {
1648 .flags = LDB_FLAG_MOD_REPLACE,
1649 .name = attr_name,
1650 .num_values = 1,
1651 .values = discard_const_p(struct ldb_val, rdn_new)
1653 struct ldb_message_element old_el = {
1654 .flags = LDB_FLAG_MOD_REPLACE,
1655 .name = attr_name,
1656 .num_values = rdn_old ? 1 : 0,
1657 .values = discard_const_p(struct ldb_val, rdn_old)
1660 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1661 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1662 if (ret != LDB_SUCCESS) {
1663 return ldb_oom(ldb);
1667 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1668 omd, ar->schema, &ar->seq_num,
1669 &ar->our_invocation_id,
1670 now, is_schema_nc, is_forced_rodc,
1671 ar->req);
1675 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1677 uint32_t count = omd.ctr.ctr1.count;
1678 uint64_t max = 0;
1679 uint32_t i;
1680 for (i=0; i < count; i++) {
1681 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1682 if (max < m.local_usn) {
1683 max = m.local_usn;
1686 return max;
1690 * update the replPropertyMetaData object each time we modify an
1691 * object. This is needed for DRS replication, as the merge on the
1692 * client is based on this object
1694 static int replmd_update_rpmd(struct ldb_module *module,
1695 const struct dsdb_schema *schema,
1696 struct ldb_request *req,
1697 const char * const *rename_attrs,
1698 struct ldb_message *msg, uint64_t *seq_num,
1699 time_t t, bool is_schema_nc,
1700 bool *is_urgent, bool *rodc)
1702 const struct ldb_val *omd_value;
1703 enum ndr_err_code ndr_err;
1704 struct replPropertyMetaDataBlob omd;
1705 unsigned int i;
1706 NTTIME now;
1707 const struct GUID *our_invocation_id;
1708 int ret;
1709 const char * const *attrs = NULL;
1710 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1711 struct ldb_result *res;
1712 struct ldb_context *ldb;
1713 struct ldb_message_element *objectclass_el;
1714 enum urgent_situation situation;
1715 bool rmd_is_provided;
1716 bool rmd_is_just_resorted = false;
1717 const char *not_rename_attrs[4 + msg->num_elements];
1718 bool is_forced_rodc = false;
1720 if (rename_attrs) {
1721 attrs = rename_attrs;
1722 } else {
1723 for (i = 0; i < msg->num_elements; i++) {
1724 not_rename_attrs[i] = msg->elements[i].name;
1726 not_rename_attrs[i] = "replPropertyMetaData";
1727 not_rename_attrs[i+1] = "objectClass";
1728 not_rename_attrs[i+2] = "instanceType";
1729 not_rename_attrs[i+3] = NULL;
1730 attrs = not_rename_attrs;
1733 ldb = ldb_module_get_ctx(module);
1735 ret = samdb_rodc(ldb, rodc);
1736 if (ret != LDB_SUCCESS) {
1737 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1738 *rodc = false;
1741 if (*rodc &&
1742 ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
1743 is_forced_rodc = true;
1746 our_invocation_id = samdb_ntds_invocation_id(ldb);
1747 if (!our_invocation_id) {
1748 /* this happens during an initial vampire while
1749 updating the schema */
1750 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1751 return LDB_SUCCESS;
1754 unix_to_nt_time(&now, t);
1756 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1757 rmd_is_provided = true;
1758 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1759 rmd_is_just_resorted = true;
1761 } else {
1762 rmd_is_provided = false;
1765 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1766 * otherwise we consider we are updating */
1767 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1768 situation = REPL_URGENT_ON_DELETE;
1769 } else if (rename_attrs) {
1770 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1771 } else {
1772 situation = REPL_URGENT_ON_UPDATE;
1775 if (rmd_is_provided) {
1776 /* In this case the change_replmetadata control was supplied */
1777 /* We check that it's the only attribute that is provided
1778 * (it's a rare case so it's better to keep the code simplier)
1779 * We also check that the highest local_usn is bigger or the same as
1780 * uSNChanged. */
1781 uint64_t db_seq;
1782 if( msg->num_elements != 1 ||
1783 strncmp(msg->elements[0].name,
1784 "replPropertyMetaData", 20) ) {
1785 DEBUG(0,(__location__ ": changereplmetada control called without "\
1786 "a specified replPropertyMetaData attribute or with others\n"));
1787 return LDB_ERR_OPERATIONS_ERROR;
1789 if (situation != REPL_URGENT_ON_UPDATE) {
1790 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1791 return LDB_ERR_OPERATIONS_ERROR;
1793 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1794 if (!omd_value) {
1795 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1796 ldb_dn_get_linearized(msg->dn)));
1797 return LDB_ERR_OPERATIONS_ERROR;
1799 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1800 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1801 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1802 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1803 ldb_dn_get_linearized(msg->dn)));
1804 return LDB_ERR_OPERATIONS_ERROR;
1807 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1808 DSDB_FLAG_NEXT_MODULE |
1809 DSDB_SEARCH_SHOW_RECYCLED |
1810 DSDB_SEARCH_SHOW_EXTENDED_DN |
1811 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1812 DSDB_SEARCH_REVEAL_INTERNALS, req);
1814 if (ret != LDB_SUCCESS) {
1815 return ret;
1818 if (rmd_is_just_resorted == false) {
1819 *seq_num = find_max_local_usn(omd);
1821 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1824 * The test here now allows for a new
1825 * replPropertyMetaData with no change, if was
1826 * just dbcheck re-sorting the values.
1828 if (*seq_num <= db_seq) {
1829 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1830 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1831 (long long)*seq_num, (long long)db_seq));
1832 return LDB_ERR_OPERATIONS_ERROR;
1836 } else {
1837 /* search for the existing replPropertyMetaDataBlob. We need
1838 * to use REVEAL and ask for DNs in storage format to support
1839 * the check for values being the same in
1840 * replmd_update_rpmd_element()
1842 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1843 DSDB_FLAG_NEXT_MODULE |
1844 DSDB_SEARCH_SHOW_RECYCLED |
1845 DSDB_SEARCH_SHOW_EXTENDED_DN |
1846 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1847 DSDB_SEARCH_REVEAL_INTERNALS, req);
1848 if (ret != LDB_SUCCESS) {
1849 return ret;
1852 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1853 if (!omd_value) {
1854 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1855 ldb_dn_get_linearized(msg->dn)));
1856 return LDB_ERR_OPERATIONS_ERROR;
1859 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1860 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1861 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1862 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1863 ldb_dn_get_linearized(msg->dn)));
1864 return LDB_ERR_OPERATIONS_ERROR;
1867 if (omd.version != 1) {
1868 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1869 omd.version, ldb_dn_get_linearized(msg->dn)));
1870 return LDB_ERR_OPERATIONS_ERROR;
1873 for (i=0; i<msg->num_elements;) {
1874 struct ldb_message_element *el = &msg->elements[i];
1875 struct ldb_message_element *old_el;
1877 old_el = ldb_msg_find_element(res->msgs[0], el->name);
1878 ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1879 &omd, schema, seq_num,
1880 our_invocation_id,
1881 now, is_schema_nc,
1882 is_forced_rodc,
1883 req);
1884 if (ret != LDB_SUCCESS) {
1885 return ret;
1888 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1889 *is_urgent = replmd_check_urgent_attribute(el);
1892 if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1893 i++;
1894 continue;
1897 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1899 if (el->num_values != 0) {
1900 i++;
1901 continue;
1904 ldb_msg_remove_element(msg, el);
1909 * Assert that we have an objectClass attribute - this is major
1910 * corruption if we don't have this!
1912 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1913 if (objectclass_el != NULL) {
1915 * Now check if this objectClass means we need to do urgent replication
1917 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1918 situation)) {
1919 *is_urgent = true;
1921 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1922 ldb_asprintf_errstring(ldb, __location__
1923 ": objectClass missing on %s\n",
1924 ldb_dn_get_linearized(msg->dn));
1925 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1929 * replmd_update_rpmd_element has done an update if the
1930 * seq_num is set
1932 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1933 struct ldb_val *md_value;
1934 struct ldb_message_element *el;
1936 /*if we are RODC and this is a DRSR update then its ok*/
1937 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1938 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
1939 && !is_forced_rodc) {
1940 unsigned instanceType;
1942 if (*rodc) {
1943 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1944 return LDB_ERR_REFERRAL;
1947 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1948 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1949 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1950 "cannot change replicated attribute on partial replica");
1954 md_value = talloc(msg, struct ldb_val);
1955 if (md_value == NULL) {
1956 ldb_oom(ldb);
1957 return LDB_ERR_OPERATIONS_ERROR;
1960 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
1961 if (ret != LDB_SUCCESS) {
1962 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
1963 return ret;
1966 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1967 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1968 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1969 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1970 ldb_dn_get_linearized(msg->dn)));
1971 return LDB_ERR_OPERATIONS_ERROR;
1974 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1975 if (ret != LDB_SUCCESS) {
1976 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1977 ldb_dn_get_linearized(msg->dn)));
1978 return ret;
1981 el->num_values = 1;
1982 el->values = md_value;
1985 return LDB_SUCCESS;
1988 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1990 int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
1991 if (ret == 0) {
1992 return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
1993 &pdn2->dsdb_dn->extra_part);
1995 return ret;
1999 get a series of message element values as an array of DNs and GUIDs
2000 the result is sorted by GUID
2002 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
2003 struct ldb_message_element *el, struct parsed_dn **pdn,
2004 const char *ldap_oid, struct ldb_request *parent)
2006 unsigned int i;
2007 bool values_are_sorted = true;
2008 struct ldb_context *ldb = ldb_module_get_ctx(module);
2010 if (el == NULL) {
2011 *pdn = NULL;
2012 return LDB_SUCCESS;
2015 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
2016 if (!*pdn) {
2017 ldb_module_oom(module);
2018 return LDB_ERR_OPERATIONS_ERROR;
2021 for (i=0; i<el->num_values; i++) {
2022 struct ldb_val *v = &el->values[i];
2023 NTSTATUS status;
2024 struct ldb_dn *dn;
2025 struct parsed_dn *p;
2027 p = &(*pdn)[i];
2029 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
2030 if (p->dsdb_dn == NULL) {
2031 return LDB_ERR_INVALID_DN_SYNTAX;
2034 dn = p->dsdb_dn->dn;
2036 status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
2037 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
2038 unlikely(GUID_all_zero(&p->guid))) {
2039 /* we got a DN without a GUID - go find the GUID */
2040 int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
2041 if (ret != LDB_SUCCESS) {
2042 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
2043 ldb_dn_get_linearized(dn));
2044 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
2045 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
2046 ldb_attr_cmp(el->name, "member") == 0) {
2047 return LDB_ERR_UNWILLING_TO_PERFORM;
2049 return ret;
2051 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2052 if (ret != LDB_SUCCESS) {
2053 return ret;
2055 } else if (!NT_STATUS_IS_OK(status)) {
2056 return LDB_ERR_OPERATIONS_ERROR;
2058 if (i > 0 && values_are_sorted) {
2059 int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
2060 if (cmp < 0) {
2061 values_are_sorted = false;
2064 /* keep a pointer to the original ldb_val */
2065 p->v = v;
2067 if (! values_are_sorted) {
2068 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
2070 return LDB_SUCCESS;
2074 * Get a series of trusted message element values. The result is sorted by
2075 * GUID, even though the GUIDs might not be known. That works because we trust
2076 * the database to give us the elements like that if the
2077 * replmd_private->sorted_links flag is set.
2079 * We also ensure that the links are in the Functional Level 2003
2080 * linked attributes format.
2082 static int get_parsed_dns_trusted(struct ldb_module *module,
2083 struct replmd_private *replmd_private,
2084 TALLOC_CTX *mem_ctx,
2085 struct ldb_message_element *el,
2086 struct parsed_dn **pdn,
2087 const char *ldap_oid,
2088 struct ldb_request *parent)
2090 unsigned int i;
2091 int ret;
2092 if (el == NULL) {
2093 *pdn = NULL;
2094 return LDB_SUCCESS;
2097 if (!replmd_private->sorted_links) {
2098 /* We need to sort the list. This is the slow old path we want
2099 to avoid.
2101 ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2102 parent);
2103 if (ret != LDB_SUCCESS) {
2104 return ret;
2106 } else {
2107 /* Here we get a list of 'struct parsed_dns' without the parsing */
2108 *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
2109 el->num_values);
2110 if (!*pdn) {
2111 ldb_module_oom(module);
2112 return LDB_ERR_OPERATIONS_ERROR;
2115 for (i = 0; i < el->num_values; i++) {
2116 (*pdn)[i].v = &el->values[i];
2121 * This upgrades links to FL2003 style, and sorts the result
2122 * if that was needed.
2124 * TODO: Add a database feature that asserts we have no FL2000
2125 * style links to avoid this check or add a feature that
2126 * uses a similar check to find sorted/unsorted links
2127 * for an on-the-fly upgrade.
2130 ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
2131 *pdn, el->num_values,
2133 ldap_oid);
2134 if (ret != LDB_SUCCESS) {
2135 return ret;
2138 return LDB_SUCCESS;
2142 Return LDB_SUCCESS if a parsed_dn list contains no duplicate values,
2143 otherwise an error code. For compatibility the error code differs depending
2144 on whether or not the attribute is "member".
2146 As always, the parsed_dn list is assumed to be sorted.
2148 static int check_parsed_dn_duplicates(struct ldb_module *module,
2149 struct ldb_message_element *el,
2150 struct parsed_dn *pdn)
2152 unsigned int i;
2153 struct ldb_context *ldb = ldb_module_get_ctx(module);
2155 for (i = 1; i < el->num_values; i++) {
2156 struct parsed_dn *p = &pdn[i];
2157 if (parsed_dn_compare(p, &pdn[i - 1]) == 0) {
2158 ldb_asprintf_errstring(ldb,
2159 "Linked attribute %s has "
2160 "multiple identical values",
2161 el->name);
2162 if (ldb_attr_cmp(el->name, "member") == 0) {
2163 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2164 } else {
2165 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2169 return LDB_SUCCESS;
2173 build a new extended DN, including all meta data fields
2175 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2176 RMD_ADDTIME = originating_add_time
2177 RMD_INVOCID = originating_invocation_id
2178 RMD_CHANGETIME = originating_change_time
2179 RMD_ORIGINATING_USN = originating_usn
2180 RMD_LOCAL_USN = local_usn
2181 RMD_VERSION = version
2183 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v,
2184 struct dsdb_dn *dsdb_dn,
2185 const struct GUID *invocation_id,
2186 uint64_t local_usn, NTTIME nttime)
2188 return replmd_set_la_val(mem_ctx, v, dsdb_dn, NULL, invocation_id,
2189 local_usn, local_usn, nttime,
2190 RMD_VERSION_INITIAL, false);
2193 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2194 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2195 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
2196 bool deleted);
2199 check if any links need upgrading from w2k format
2201 static int replmd_check_upgrade_links(struct ldb_context *ldb,
2202 struct parsed_dn *dns, uint32_t count,
2203 struct ldb_message_element *el,
2204 const char *ldap_oid)
2206 uint32_t i;
2207 const struct GUID *invocation_id = NULL;
2208 for (i=0; i<count; i++) {
2209 NTSTATUS status;
2210 uint32_t version;
2211 int ret;
2212 if (dns[i].dsdb_dn == NULL) {
2213 ret = really_parse_trusted_dn(dns, ldb, &dns[i],
2214 ldap_oid);
2215 if (ret != LDB_SUCCESS) {
2216 return LDB_ERR_INVALID_DN_SYNTAX;
2220 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
2221 &version, "RMD_VERSION");
2222 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2224 * We optimistically assume they are all the same; if
2225 * the first one is fixed, they are all fixed.
2227 * If the first one was *not* fixed and we find a
2228 * later one that is, that is an occasion to shout
2229 * with DEBUG(0).
2231 if (i == 0) {
2232 return LDB_SUCCESS;
2234 DEBUG(0, ("Mixed w2k and fixed format "
2235 "linked attributes\n"));
2236 continue;
2239 if (invocation_id == NULL) {
2240 invocation_id = samdb_ntds_invocation_id(ldb);
2241 if (invocation_id == NULL) {
2242 return LDB_ERR_OPERATIONS_ERROR;
2247 /* it's an old one that needs upgrading */
2248 ret = replmd_update_la_val(el->values, dns[i].v,
2249 dns[i].dsdb_dn, dns[i].dsdb_dn,
2250 invocation_id, 1, 1, 0, false);
2251 if (ret != LDB_SUCCESS) {
2252 return ret;
2257 * This sort() is critical for the operation of
2258 * get_parsed_dns_trusted() because callers of this function
2259 * expect a sorted list, and FL2000 style links are not
2260 * sorted. In particular, as well as the upgrade case,
2261 * get_parsed_dns_trusted() is called from
2262 * replmd_delete_remove_link() even in FL2000 mode
2264 * We do not normally pay the cost of the qsort() due to the
2265 * early return in the RMD_VERSION found case.
2267 TYPESAFE_QSORT(dns, count, parsed_dn_compare);
2268 return LDB_SUCCESS;
2272 Sets the value for a linked attribute, including all meta data fields
2274 see replmd_build_la_val for value names
2276 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2277 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2278 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2279 uint32_t version, bool deleted)
2281 struct ldb_dn *dn = dsdb_dn->dn;
2282 const char *tstring, *usn_string, *flags_string;
2283 struct ldb_val tval;
2284 struct ldb_val iid;
2285 struct ldb_val usnv, local_usnv;
2286 struct ldb_val vers, flagsv;
2287 const struct ldb_val *old_addtime = NULL;
2288 NTSTATUS status;
2289 int ret;
2290 const char *dnstring;
2291 char *vstring;
2292 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2294 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2295 if (!tstring) {
2296 return LDB_ERR_OPERATIONS_ERROR;
2298 tval = data_blob_string_const(tstring);
2300 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2301 if (!usn_string) {
2302 return LDB_ERR_OPERATIONS_ERROR;
2304 usnv = data_blob_string_const(usn_string);
2306 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2307 if (!usn_string) {
2308 return LDB_ERR_OPERATIONS_ERROR;
2310 local_usnv = data_blob_string_const(usn_string);
2312 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2313 if (!NT_STATUS_IS_OK(status)) {
2314 return LDB_ERR_OPERATIONS_ERROR;
2317 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2318 if (!flags_string) {
2319 return LDB_ERR_OPERATIONS_ERROR;
2321 flagsv = data_blob_string_const(flags_string);
2323 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2324 if (ret != LDB_SUCCESS) return ret;
2326 /* get the ADDTIME from the original */
2327 if (old_dsdb_dn != NULL) {
2328 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn,
2329 "RMD_ADDTIME");
2331 if (old_addtime == NULL) {
2332 old_addtime = &tval;
2334 if (dsdb_dn != old_dsdb_dn ||
2335 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2336 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2337 if (ret != LDB_SUCCESS) return ret;
2340 /* use our invocation id */
2341 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2342 if (ret != LDB_SUCCESS) return ret;
2344 /* changetime is the current time */
2345 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2346 if (ret != LDB_SUCCESS) return ret;
2348 /* update the USN */
2349 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2350 if (ret != LDB_SUCCESS) return ret;
2352 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2353 if (ret != LDB_SUCCESS) return ret;
2355 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2356 vers = data_blob_string_const(vstring);
2357 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2358 if (ret != LDB_SUCCESS) return ret;
2360 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2361 if (dnstring == NULL) {
2362 return LDB_ERR_OPERATIONS_ERROR;
2364 *v = data_blob_string_const(dnstring);
2366 return LDB_SUCCESS;
2370 * Updates the value for a linked attribute, including all meta data fields
2372 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2373 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2374 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2375 bool deleted)
2377 uint32_t old_version;
2378 uint32_t version = RMD_VERSION_INITIAL;
2379 NTSTATUS status;
2382 * We're updating the linked attribute locally, so increase the version
2383 * by 1 so that other DCs will see the change when it gets replicated out
2385 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version,
2386 "RMD_VERSION");
2388 if (NT_STATUS_IS_OK(status)) {
2389 version = old_version + 1;
2392 return replmd_set_la_val(mem_ctx, v, dsdb_dn, old_dsdb_dn, invocation_id,
2393 usn, local_usn, nttime, version, deleted);
2397 handle adding a linked attribute
2399 static int replmd_modify_la_add(struct ldb_module *module,
2400 struct replmd_private *replmd_private,
2401 const struct dsdb_schema *schema,
2402 struct ldb_message *msg,
2403 struct ldb_message_element *el,
2404 struct ldb_message_element *old_el,
2405 const struct dsdb_attribute *schema_attr,
2406 uint64_t seq_num,
2407 time_t t,
2408 struct ldb_dn *msg_dn,
2409 struct ldb_request *parent)
2411 unsigned int i, j;
2412 struct parsed_dn *dns, *old_dns;
2413 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2414 int ret;
2415 struct ldb_val *new_values = NULL;
2416 unsigned old_num_values = old_el ? old_el->num_values : 0;
2417 unsigned num_values = 0;
2418 unsigned max_num_values;
2419 const struct GUID *invocation_id;
2420 struct ldb_context *ldb = ldb_module_get_ctx(module);
2421 NTTIME now;
2422 unix_to_nt_time(&now, t);
2424 invocation_id = samdb_ntds_invocation_id(ldb);
2425 if (!invocation_id) {
2426 talloc_free(tmp_ctx);
2427 return LDB_ERR_OPERATIONS_ERROR;
2430 /* get the DNs to be added, fully parsed.
2432 * We need full parsing because they came off the wire and we don't
2433 * trust them, besides which we need their details to know where to put
2434 * them.
2436 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2437 schema_attr->syntax->ldap_oid, parent);
2438 if (ret != LDB_SUCCESS) {
2439 talloc_free(tmp_ctx);
2440 return ret;
2443 /* get the existing DNs, lazily parsed */
2444 ret = get_parsed_dns_trusted(module, replmd_private,
2445 tmp_ctx, old_el, &old_dns,
2446 schema_attr->syntax->ldap_oid, parent);
2448 if (ret != LDB_SUCCESS) {
2449 talloc_free(tmp_ctx);
2450 return ret;
2453 max_num_values = old_num_values + el->num_values;
2454 if (max_num_values < old_num_values) {
2455 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2456 "old values: %u, new values: %u, sum: %u",
2457 old_num_values, el->num_values, max_num_values));
2458 talloc_free(tmp_ctx);
2459 return LDB_ERR_OPERATIONS_ERROR;
2462 new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2464 if (new_values == NULL) {
2465 ldb_module_oom(module);
2466 talloc_free(tmp_ctx);
2467 return LDB_ERR_OPERATIONS_ERROR;
2471 * For each new value, find where it would go in the list. If there is
2472 * a matching GUID there, we update the existing value; otherwise we
2473 * put it in place.
2475 j = 0;
2476 for (i = 0; i < el->num_values; i++) {
2477 struct parsed_dn *exact;
2478 struct parsed_dn *next;
2479 unsigned offset;
2480 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2481 &dns[i].guid,
2482 dns[i].dsdb_dn->dn,
2483 dns[i].dsdb_dn->extra_part, 0,
2484 &exact, &next,
2485 schema_attr->syntax->ldap_oid,
2486 true);
2487 if (err != LDB_SUCCESS) {
2488 talloc_free(tmp_ctx);
2489 return err;
2492 if (exact != NULL) {
2494 * We are trying to add one that exists, which is only
2495 * allowed if it was previously deleted.
2497 * When we do undelete a link we change it in place.
2498 * It will be copied across into the right spot in due
2499 * course.
2501 uint32_t rmd_flags;
2502 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2504 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2505 struct GUID_txt_buf guid_str;
2506 ldb_asprintf_errstring(ldb,
2507 "Attribute %s already "
2508 "exists for target GUID %s",
2509 el->name,
2510 GUID_buf_string(&exact->guid,
2511 &guid_str));
2512 talloc_free(tmp_ctx);
2513 /* error codes for 'member' need to be
2514 special cased */
2515 if (ldb_attr_cmp(el->name, "member") == 0) {
2516 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2517 } else {
2518 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2522 ret = replmd_update_la_val(new_values, exact->v,
2523 dns[i].dsdb_dn,
2524 exact->dsdb_dn,
2525 invocation_id, seq_num,
2526 seq_num, now, false);
2527 if (ret != LDB_SUCCESS) {
2528 talloc_free(tmp_ctx);
2529 return ret;
2532 ret = replmd_add_backlink(module, replmd_private,
2533 schema,
2534 msg_dn,
2535 &dns[i].guid,
2536 true,
2537 schema_attr,
2538 parent);
2539 if (ret != LDB_SUCCESS) {
2540 talloc_free(tmp_ctx);
2541 return ret;
2543 continue;
2546 * Here we don't have an exact match.
2548 * If next is NULL, this one goes beyond the end of the
2549 * existing list, so we need to add all of those ones first.
2551 * If next is not NULL, we need to add all the ones before
2552 * next.
2554 if (next == NULL) {
2555 offset = old_num_values;
2556 } else {
2557 /* next should have been parsed, but let's make sure */
2558 if (next->dsdb_dn == NULL) {
2559 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2560 schema_attr->syntax->ldap_oid);
2561 if (ret != LDB_SUCCESS) {
2562 return ret;
2565 offset = MIN(next - old_dns, old_num_values);
2568 /* put all the old ones before next on the list */
2569 for (; j < offset; j++) {
2570 new_values[num_values] = *old_dns[j].v;
2571 num_values++;
2574 ret = replmd_add_backlink(module, replmd_private,
2575 schema, msg_dn,
2576 &dns[i].guid,
2577 true, schema_attr,
2578 parent);
2579 /* Make the new linked attribute ldb_val. */
2580 ret = replmd_build_la_val(new_values, &new_values[num_values],
2581 dns[i].dsdb_dn, invocation_id,
2582 seq_num, now);
2583 if (ret != LDB_SUCCESS) {
2584 talloc_free(tmp_ctx);
2585 return ret;
2587 num_values++;
2588 if (ret != LDB_SUCCESS) {
2589 talloc_free(tmp_ctx);
2590 return ret;
2593 /* copy the rest of the old ones (if any) */
2594 for (; j < old_num_values; j++) {
2595 new_values[num_values] = *old_dns[j].v;
2596 num_values++;
2599 talloc_steal(msg->elements, new_values);
2600 if (old_el != NULL) {
2601 talloc_steal(msg->elements, old_el->values);
2603 el->values = new_values;
2604 el->num_values = num_values;
2606 talloc_free(tmp_ctx);
2608 /* we now tell the backend to replace all existing values
2609 with the one we have constructed */
2610 el->flags = LDB_FLAG_MOD_REPLACE;
2612 return LDB_SUCCESS;
2617 handle deleting all active linked attributes
2619 static int replmd_modify_la_delete(struct ldb_module *module,
2620 struct replmd_private *replmd_private,
2621 const struct dsdb_schema *schema,
2622 struct ldb_message *msg,
2623 struct ldb_message_element *el,
2624 struct ldb_message_element *old_el,
2625 const struct dsdb_attribute *schema_attr,
2626 uint64_t seq_num,
2627 time_t t,
2628 struct ldb_dn *msg_dn,
2629 struct ldb_request *parent)
2631 unsigned int i;
2632 struct parsed_dn *dns, *old_dns;
2633 TALLOC_CTX *tmp_ctx = NULL;
2634 int ret;
2635 struct ldb_context *ldb = ldb_module_get_ctx(module);
2636 struct ldb_control *vanish_links_ctrl = NULL;
2637 bool vanish_links = false;
2638 unsigned int num_to_delete = el->num_values;
2639 uint32_t rmd_flags;
2640 const struct GUID *invocation_id;
2641 NTTIME now;
2643 unix_to_nt_time(&now, t);
2645 invocation_id = samdb_ntds_invocation_id(ldb);
2646 if (!invocation_id) {
2647 return LDB_ERR_OPERATIONS_ERROR;
2650 if (old_el == NULL || old_el->num_values == 0) {
2651 /* there is nothing to delete... */
2652 if (num_to_delete == 0) {
2653 /* and we're deleting nothing, so that's OK */
2654 return LDB_SUCCESS;
2656 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2659 tmp_ctx = talloc_new(msg);
2660 if (tmp_ctx == NULL) {
2661 return LDB_ERR_OPERATIONS_ERROR;
2664 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2665 schema_attr->syntax->ldap_oid, parent);
2666 if (ret != LDB_SUCCESS) {
2667 talloc_free(tmp_ctx);
2668 return ret;
2671 ret = get_parsed_dns_trusted(module, replmd_private,
2672 tmp_ctx, old_el, &old_dns,
2673 schema_attr->syntax->ldap_oid, parent);
2675 if (ret != LDB_SUCCESS) {
2676 talloc_free(tmp_ctx);
2677 return ret;
2680 if (parent) {
2681 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2682 if (vanish_links_ctrl) {
2683 vanish_links = true;
2684 vanish_links_ctrl->critical = false;
2688 /* we empty out el->values here to avoid damage if we return early. */
2689 el->num_values = 0;
2690 el->values = NULL;
2693 * If vanish links is set, we are actually removing members of
2694 * old_el->values; otherwise we are just marking them deleted.
2696 * There is a special case when no values are given: we remove them
2697 * all. When we have the vanish_links control we just have to remove
2698 * the backlinks and change our element to replace the existing values
2699 * with the empty list.
2702 if (num_to_delete == 0) {
2703 for (i = 0; i < old_el->num_values; i++) {
2704 struct parsed_dn *p = &old_dns[i];
2705 if (p->dsdb_dn == NULL) {
2706 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2707 schema_attr->syntax->ldap_oid);
2708 if (ret != LDB_SUCCESS) {
2709 return ret;
2712 ret = replmd_add_backlink(module, replmd_private,
2713 schema, msg_dn, &p->guid,
2714 false, schema_attr,
2715 parent);
2716 if (ret != LDB_SUCCESS) {
2717 talloc_free(tmp_ctx);
2718 return ret;
2720 if (vanish_links) {
2721 continue;
2724 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2725 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2726 continue;
2729 ret = replmd_update_la_val(old_el->values, p->v,
2730 p->dsdb_dn, p->dsdb_dn,
2731 invocation_id, seq_num,
2732 seq_num, now, true);
2733 if (ret != LDB_SUCCESS) {
2734 talloc_free(tmp_ctx);
2735 return ret;
2739 if (vanish_links) {
2740 el->flags = LDB_FLAG_MOD_REPLACE;
2741 talloc_free(tmp_ctx);
2742 return LDB_SUCCESS;
2747 for (i = 0; i < num_to_delete; i++) {
2748 struct parsed_dn *p = &dns[i];
2749 struct parsed_dn *exact = NULL;
2750 struct parsed_dn *next = NULL;
2751 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2752 &p->guid,
2753 NULL,
2754 p->dsdb_dn->extra_part, 0,
2755 &exact, &next,
2756 schema_attr->syntax->ldap_oid,
2757 true);
2758 if (ret != LDB_SUCCESS) {
2759 talloc_free(tmp_ctx);
2760 return ret;
2762 if (exact == NULL) {
2763 struct GUID_txt_buf buf;
2764 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2765 "exist for target GUID %s",
2766 el->name,
2767 GUID_buf_string(&p->guid, &buf));
2768 if (ldb_attr_cmp(el->name, "member") == 0) {
2769 talloc_free(tmp_ctx);
2770 return LDB_ERR_UNWILLING_TO_PERFORM;
2771 } else {
2772 talloc_free(tmp_ctx);
2773 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2777 if (vanish_links) {
2778 if (CHECK_DEBUGLVL(5)) {
2779 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2780 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2781 struct GUID_txt_buf buf;
2782 const char *guid_str = \
2783 GUID_buf_string(&p->guid, &buf);
2784 DEBUG(5, ("Deleting deleted linked "
2785 "attribute %s to %s, because "
2786 "vanish_links control is set\n",
2787 el->name, guid_str));
2791 /* remove the backlink */
2792 ret = replmd_add_backlink(module,
2793 replmd_private,
2794 schema,
2795 msg_dn,
2796 &p->guid,
2797 false, schema_attr,
2798 parent);
2799 if (ret != LDB_SUCCESS) {
2800 talloc_free(tmp_ctx);
2801 return ret;
2804 /* We flag the deletion and tidy it up later. */
2805 exact->v = NULL;
2806 continue;
2809 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2811 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2812 struct GUID_txt_buf buf;
2813 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2814 ldb_asprintf_errstring(ldb, "Attribute %s already "
2815 "deleted for target GUID %s",
2816 el->name, guid_str);
2817 if (ldb_attr_cmp(el->name, "member") == 0) {
2818 talloc_free(tmp_ctx);
2819 return LDB_ERR_UNWILLING_TO_PERFORM;
2820 } else {
2821 talloc_free(tmp_ctx);
2822 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2826 ret = replmd_update_la_val(old_el->values, exact->v,
2827 exact->dsdb_dn, exact->dsdb_dn,
2828 invocation_id, seq_num, seq_num,
2829 now, true);
2830 if (ret != LDB_SUCCESS) {
2831 talloc_free(tmp_ctx);
2832 return ret;
2834 ret = replmd_add_backlink(module, replmd_private,
2835 schema, msg_dn,
2836 &p->guid,
2837 false, schema_attr,
2838 parent);
2839 if (ret != LDB_SUCCESS) {
2840 talloc_free(tmp_ctx);
2841 return ret;
2845 if (vanish_links) {
2846 unsigned j = 0;
2847 for (i = 0; i < old_el->num_values; i++) {
2848 if (old_dns[i].v != NULL) {
2849 old_el->values[j] = *old_dns[i].v;
2850 j++;
2853 old_el->num_values = j;
2856 el->values = talloc_steal(msg->elements, old_el->values);
2857 el->num_values = old_el->num_values;
2859 talloc_free(tmp_ctx);
2861 /* we now tell the backend to replace all existing values
2862 with the one we have constructed */
2863 el->flags = LDB_FLAG_MOD_REPLACE;
2865 return LDB_SUCCESS;
2869 handle replacing a linked attribute
2871 static int replmd_modify_la_replace(struct ldb_module *module,
2872 struct replmd_private *replmd_private,
2873 const struct dsdb_schema *schema,
2874 struct ldb_message *msg,
2875 struct ldb_message_element *el,
2876 struct ldb_message_element *old_el,
2877 const struct dsdb_attribute *schema_attr,
2878 uint64_t seq_num,
2879 time_t t,
2880 struct ldb_dn *msg_dn,
2881 struct ldb_request *parent)
2883 unsigned int i, old_i, new_i;
2884 struct parsed_dn *dns, *old_dns;
2885 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2886 int ret;
2887 const struct GUID *invocation_id;
2888 struct ldb_context *ldb = ldb_module_get_ctx(module);
2889 struct ldb_val *new_values = NULL;
2890 const char *ldap_oid = schema_attr->syntax->ldap_oid;
2891 unsigned int old_num_values;
2892 unsigned int repl_num_values;
2893 unsigned int max_num_values;
2894 NTTIME now;
2896 unix_to_nt_time(&now, t);
2898 invocation_id = samdb_ntds_invocation_id(ldb);
2899 if (!invocation_id) {
2900 return LDB_ERR_OPERATIONS_ERROR;
2904 * The replace operation is unlike the replace and delete cases in that
2905 * we need to look at every existing link to see whether it is being
2906 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
2908 * As we are trying to combine two sorted lists, the algorithm we use
2909 * is akin to the merge phase of a merge sort. We interleave the two
2910 * lists, doing different things depending on which side the current
2911 * item came from.
2913 * There are three main cases, with some sub-cases.
2915 * - a DN is in the old list but not the new one. It needs to be
2916 * marked as deleted (but left in the list).
2917 * - maybe it is already deleted, and we have less to do.
2919 * - a DN is in both lists. The old data gets replaced by the new,
2920 * and the list doesn't grow. The old link may have been marked as
2921 * deleted, in which case we undelete it.
2923 * - a DN is in the new list only. We add it in the right place.
2926 old_num_values = old_el ? old_el->num_values : 0;
2927 repl_num_values = el->num_values;
2928 max_num_values = old_num_values + repl_num_values;
2930 if (max_num_values == 0) {
2931 /* There is nothing to do! */
2932 return LDB_SUCCESS;
2935 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
2936 if (ret != LDB_SUCCESS) {
2937 talloc_free(tmp_ctx);
2938 return ret;
2941 ret = check_parsed_dn_duplicates(module, el, dns);
2942 if (ret != LDB_SUCCESS) {
2943 talloc_free(tmp_ctx);
2944 return ret;
2947 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
2948 ldap_oid, parent);
2949 if (ret != LDB_SUCCESS) {
2950 talloc_free(tmp_ctx);
2951 return ret;
2954 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
2955 old_el, ldap_oid);
2956 if (ret != LDB_SUCCESS) {
2957 talloc_free(tmp_ctx);
2958 return ret;
2961 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
2962 if (new_values == NULL) {
2963 ldb_module_oom(module);
2964 talloc_free(tmp_ctx);
2965 return LDB_ERR_OPERATIONS_ERROR;
2968 old_i = 0;
2969 new_i = 0;
2970 for (i = 0; i < max_num_values; i++) {
2971 int cmp;
2972 struct parsed_dn *old_p, *new_p;
2973 if (old_i < old_num_values && new_i < repl_num_values) {
2974 old_p = &old_dns[old_i];
2975 new_p = &dns[new_i];
2976 cmp = parsed_dn_compare(old_p, new_p);
2977 } else if (old_i < old_num_values) {
2978 /* the new list is empty, read the old list */
2979 old_p = &old_dns[old_i];
2980 new_p = NULL;
2981 cmp = -1;
2982 } else if (new_i < repl_num_values) {
2983 /* the old list is empty, read new list */
2984 old_p = NULL;
2985 new_p = &dns[new_i];
2986 cmp = 1;
2987 } else {
2988 break;
2991 if (cmp < 0) {
2993 * An old ones that come before the next replacement
2994 * (if any). We mark it as deleted and add it to the
2995 * final list.
2997 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2998 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
2999 ret = replmd_update_la_val(new_values, old_p->v,
3000 old_p->dsdb_dn,
3001 old_p->dsdb_dn,
3002 invocation_id,
3003 seq_num, seq_num,
3004 now, true);
3005 if (ret != LDB_SUCCESS) {
3006 talloc_free(tmp_ctx);
3007 return ret;
3010 ret = replmd_add_backlink(module, replmd_private,
3011 schema,
3012 msg_dn,
3013 &old_p->guid, false,
3014 schema_attr,
3015 parent);
3016 if (ret != LDB_SUCCESS) {
3017 talloc_free(tmp_ctx);
3018 return ret;
3021 new_values[i] = *old_p->v;
3022 old_i++;
3023 } else if (cmp == 0) {
3025 * We are overwriting one. If it was previously
3026 * deleted, we need to add a backlink.
3028 * Note that if any RMD_FLAGs in an extended new DN
3029 * will be ignored.
3031 uint32_t rmd_flags;
3033 ret = replmd_update_la_val(new_values, old_p->v,
3034 new_p->dsdb_dn,
3035 old_p->dsdb_dn,
3036 invocation_id,
3037 seq_num, seq_num,
3038 now, false);
3039 if (ret != LDB_SUCCESS) {
3040 talloc_free(tmp_ctx);
3041 return ret;
3044 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3045 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3046 ret = replmd_add_backlink(module, replmd_private,
3047 schema,
3048 msg_dn,
3049 &new_p->guid, true,
3050 schema_attr,
3051 parent);
3052 if (ret != LDB_SUCCESS) {
3053 talloc_free(tmp_ctx);
3054 return ret;
3058 new_values[i] = *old_p->v;
3059 old_i++;
3060 new_i++;
3061 } else {
3063 * Replacements that don't match an existing one. We
3064 * just add them to the final list.
3066 ret = replmd_build_la_val(new_values,
3067 new_p->v,
3068 new_p->dsdb_dn,
3069 invocation_id,
3070 seq_num, now);
3071 if (ret != LDB_SUCCESS) {
3072 talloc_free(tmp_ctx);
3073 return ret;
3075 ret = replmd_add_backlink(module, replmd_private,
3076 schema,
3077 msg_dn,
3078 &new_p->guid, true,
3079 schema_attr,
3080 parent);
3081 if (ret != LDB_SUCCESS) {
3082 talloc_free(tmp_ctx);
3083 return ret;
3085 new_values[i] = *new_p->v;
3086 new_i++;
3089 if (old_el != NULL) {
3090 talloc_steal(msg->elements, old_el->values);
3092 el->values = talloc_steal(msg->elements, new_values);
3093 el->num_values = i;
3094 talloc_free(tmp_ctx);
3096 el->flags = LDB_FLAG_MOD_REPLACE;
3098 return LDB_SUCCESS;
3103 handle linked attributes in modify requests
3105 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3106 struct replmd_private *replmd_private,
3107 struct ldb_message *msg,
3108 uint64_t seq_num, time_t t,
3109 struct ldb_request *parent)
3111 struct ldb_result *res;
3112 unsigned int i;
3113 int ret;
3114 struct ldb_context *ldb = ldb_module_get_ctx(module);
3115 struct ldb_message *old_msg;
3117 const struct dsdb_schema *schema;
3119 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3121 * Nothing special is required for modifying or vanishing links
3122 * in fl2000 since they are just strings in a multi-valued
3123 * attribute.
3125 struct ldb_control *ctrl = ldb_request_get_control(parent,
3126 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3127 if (ctrl) {
3128 ctrl->critical = false;
3130 return LDB_SUCCESS;
3134 * TODO:
3136 * We should restrict this to the intersection of the list of
3137 * linked attributes in the schema and the list of attributes
3138 * being modified.
3140 * This will help performance a little, as otherwise we have
3141 * to allocate the entire object value-by-value.
3143 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3144 DSDB_FLAG_NEXT_MODULE |
3145 DSDB_SEARCH_SHOW_RECYCLED |
3146 DSDB_SEARCH_REVEAL_INTERNALS |
3147 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3148 parent);
3149 if (ret != LDB_SUCCESS) {
3150 return ret;
3152 schema = dsdb_get_schema(ldb, res);
3153 if (!schema) {
3154 return LDB_ERR_OPERATIONS_ERROR;
3157 old_msg = res->msgs[0];
3159 for (i=0; i<msg->num_elements; i++) {
3160 struct ldb_message_element *el = &msg->elements[i];
3161 struct ldb_message_element *old_el, *new_el;
3162 unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3163 const struct dsdb_attribute *schema_attr
3164 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3165 if (!schema_attr) {
3166 ldb_asprintf_errstring(ldb,
3167 "%s: attribute %s is not a valid attribute in schema",
3168 __FUNCTION__, el->name);
3169 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3171 if (schema_attr->linkID == 0) {
3172 continue;
3174 if ((schema_attr->linkID & 1) == 1) {
3175 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
3176 continue;
3178 /* Odd is for the target. Illegal to modify */
3179 ldb_asprintf_errstring(ldb,
3180 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3181 return LDB_ERR_UNWILLING_TO_PERFORM;
3183 old_el = ldb_msg_find_element(old_msg, el->name);
3184 switch (mod_type) {
3185 case LDB_FLAG_MOD_REPLACE:
3186 ret = replmd_modify_la_replace(module, replmd_private,
3187 schema, msg, el, old_el,
3188 schema_attr, seq_num, t,
3189 old_msg->dn,
3190 parent);
3191 break;
3192 case LDB_FLAG_MOD_DELETE:
3193 ret = replmd_modify_la_delete(module, replmd_private,
3194 schema, msg, el, old_el,
3195 schema_attr, seq_num, t,
3196 old_msg->dn,
3197 parent);
3198 break;
3199 case LDB_FLAG_MOD_ADD:
3200 ret = replmd_modify_la_add(module, replmd_private,
3201 schema, msg, el, old_el,
3202 schema_attr, seq_num, t,
3203 old_msg->dn,
3204 parent);
3205 break;
3206 default:
3207 ldb_asprintf_errstring(ldb,
3208 "invalid flags 0x%x for %s linked attribute",
3209 el->flags, el->name);
3210 return LDB_ERR_UNWILLING_TO_PERFORM;
3212 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
3213 ldb_asprintf_errstring(ldb,
3214 "Attribute %s is single valued but more than one value has been supplied",
3215 el->name);
3216 /* Return codes as found on Windows 2012r2 */
3217 if (mod_type == LDB_FLAG_MOD_REPLACE) {
3218 return LDB_ERR_CONSTRAINT_VIOLATION;
3219 } else {
3220 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3222 } else {
3223 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3226 if (ret != LDB_SUCCESS) {
3227 return ret;
3229 if (old_el) {
3230 ldb_msg_remove_attr(old_msg, el->name);
3232 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3233 new_el->num_values = el->num_values;
3234 new_el->values = talloc_steal(msg->elements, el->values);
3236 /* TODO: this relises a bit too heavily on the exact
3237 behaviour of ldb_msg_find_element and
3238 ldb_msg_remove_element */
3239 old_el = ldb_msg_find_element(msg, el->name);
3240 if (old_el != el) {
3241 ldb_msg_remove_element(msg, old_el);
3242 i--;
3246 talloc_free(res);
3247 return ret;
3251 static int send_rodc_referral(struct ldb_request *req,
3252 struct ldb_context *ldb,
3253 struct ldb_dn *dn)
3255 char *referral = NULL;
3256 struct loadparm_context *lp_ctx = NULL;
3257 struct ldb_dn *fsmo_role_dn = NULL;
3258 struct ldb_dn *role_owner_dn = NULL;
3259 const char *domain = NULL;
3260 WERROR werr;
3262 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3263 struct loadparm_context);
3265 werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3266 &fsmo_role_dn, &role_owner_dn);
3268 if (W_ERROR_IS_OK(werr)) {
3269 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3270 if (server_dn != NULL) {
3271 ldb_dn_remove_child_components(server_dn, 1);
3272 domain = samdb_dn_to_dnshostname(ldb, req,
3273 server_dn);
3277 if (domain == NULL) {
3278 domain = lpcfg_dnsdomain(lp_ctx);
3281 referral = talloc_asprintf(req, "ldap://%s/%s",
3282 domain,
3283 ldb_dn_get_linearized(dn));
3284 if (referral == NULL) {
3285 ldb_oom(ldb);
3286 return LDB_ERR_OPERATIONS_ERROR;
3289 return ldb_module_send_referral(req, referral);
3293 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3295 struct ldb_context *ldb;
3296 struct replmd_replicated_request *ac;
3297 struct ldb_request *down_req;
3298 struct ldb_message *msg;
3299 time_t t = time(NULL);
3300 int ret;
3301 bool is_urgent = false, rodc = false;
3302 bool is_schema_nc = false;
3303 unsigned int functional_level;
3304 const struct ldb_message_element *guid_el = NULL;
3305 struct ldb_control *sd_propagation_control;
3306 struct ldb_control *fix_links_control = NULL;
3307 struct replmd_private *replmd_private =
3308 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3310 /* do not manipulate our control entries */
3311 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3312 return ldb_next_request(module, req);
3315 sd_propagation_control = ldb_request_get_control(req,
3316 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3317 if (sd_propagation_control != NULL) {
3318 if (req->op.mod.message->num_elements != 1) {
3319 return ldb_module_operr(module);
3321 ret = strcmp(req->op.mod.message->elements[0].name,
3322 "nTSecurityDescriptor");
3323 if (ret != 0) {
3324 return ldb_module_operr(module);
3327 return ldb_next_request(module, req);
3330 ldb = ldb_module_get_ctx(module);
3332 fix_links_control = ldb_request_get_control(req,
3333 DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
3334 if (fix_links_control != NULL) {
3335 struct dsdb_schema *schema = NULL;
3336 const struct dsdb_attribute *sa = NULL;
3338 if (req->op.mod.message->num_elements != 1) {
3339 return ldb_module_operr(module);
3342 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_REPLACE) {
3343 return ldb_module_operr(module);
3346 schema = dsdb_get_schema(ldb, req);
3347 if (schema == NULL) {
3348 return ldb_module_operr(module);
3351 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3352 req->op.mod.message->elements[0].name);
3353 if (sa == NULL) {
3354 return ldb_module_operr(module);
3357 if (sa->linkID == 0) {
3358 return ldb_module_operr(module);
3361 fix_links_control->critical = false;
3362 return ldb_next_request(module, req);
3365 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3367 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3368 if (guid_el != NULL) {
3369 ldb_set_errstring(ldb,
3370 "replmd_modify: it's not allowed to change the objectGUID!");
3371 return LDB_ERR_CONSTRAINT_VIOLATION;
3374 ac = replmd_ctx_init(module, req);
3375 if (ac == NULL) {
3376 return ldb_module_oom(module);
3379 functional_level = dsdb_functional_level(ldb);
3381 /* we have to copy the message as the caller might have it as a const */
3382 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3383 if (msg == NULL) {
3384 ldb_oom(ldb);
3385 talloc_free(ac);
3386 return LDB_ERR_OPERATIONS_ERROR;
3389 ldb_msg_remove_attr(msg, "whenChanged");
3390 ldb_msg_remove_attr(msg, "uSNChanged");
3392 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3394 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3395 msg, &ac->seq_num, t, is_schema_nc,
3396 &is_urgent, &rodc);
3397 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3398 ret = send_rodc_referral(req, ldb, msg->dn);
3399 talloc_free(ac);
3400 return ret;
3404 if (ret != LDB_SUCCESS) {
3405 talloc_free(ac);
3406 return ret;
3409 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3410 msg, ac->seq_num, t, req);
3411 if (ret != LDB_SUCCESS) {
3412 talloc_free(ac);
3413 return ret;
3416 /* TODO:
3417 * - replace the old object with the newly constructed one
3420 ac->is_urgent = is_urgent;
3422 ret = ldb_build_mod_req(&down_req, ldb, ac,
3423 msg,
3424 req->controls,
3425 ac, replmd_op_callback,
3426 req);
3427 LDB_REQ_SET_LOCATION(down_req);
3428 if (ret != LDB_SUCCESS) {
3429 talloc_free(ac);
3430 return ret;
3433 /* current partition control is needed by "replmd_op_callback" */
3434 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3435 ret = ldb_request_add_control(down_req,
3436 DSDB_CONTROL_CURRENT_PARTITION_OID,
3437 false, NULL);
3438 if (ret != LDB_SUCCESS) {
3439 talloc_free(ac);
3440 return ret;
3444 /* If we are in functional level 2000, then
3445 * replmd_modify_handle_linked_attribs will have done
3446 * nothing */
3447 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3448 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3449 if (ret != LDB_SUCCESS) {
3450 talloc_free(ac);
3451 return ret;
3455 talloc_steal(down_req, msg);
3457 /* we only change whenChanged and uSNChanged if the seq_num
3458 has changed */
3459 if (ac->seq_num != 0) {
3460 ret = add_time_element(msg, "whenChanged", t);
3461 if (ret != LDB_SUCCESS) {
3462 talloc_free(ac);
3463 ldb_operr(ldb);
3464 return ret;
3467 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3468 if (ret != LDB_SUCCESS) {
3469 talloc_free(ac);
3470 ldb_operr(ldb);
3471 return ret;
3475 /* go on with the call chain */
3476 return ldb_next_request(module, down_req);
3479 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3482 handle a rename request
3484 On a rename we need to do an extra ldb_modify which sets the
3485 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3487 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3489 struct ldb_context *ldb;
3490 struct replmd_replicated_request *ac;
3491 int ret;
3492 struct ldb_request *down_req;
3494 /* do not manipulate our control entries */
3495 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3496 return ldb_next_request(module, req);
3499 ldb = ldb_module_get_ctx(module);
3501 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3503 ac = replmd_ctx_init(module, req);
3504 if (ac == NULL) {
3505 return ldb_module_oom(module);
3508 ret = ldb_build_rename_req(&down_req, ldb, ac,
3509 ac->req->op.rename.olddn,
3510 ac->req->op.rename.newdn,
3511 ac->req->controls,
3512 ac, replmd_rename_callback,
3513 ac->req);
3514 LDB_REQ_SET_LOCATION(down_req);
3515 if (ret != LDB_SUCCESS) {
3516 talloc_free(ac);
3517 return ret;
3520 /* go on with the call chain */
3521 return ldb_next_request(module, down_req);
3524 /* After the rename is compleated, update the whenchanged etc */
3525 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3527 struct ldb_context *ldb;
3528 struct ldb_request *down_req;
3529 struct ldb_message *msg;
3530 const struct dsdb_attribute *rdn_attr;
3531 const char *rdn_name;
3532 const struct ldb_val *rdn_val;
3533 const char *attrs[5] = { NULL, };
3534 time_t t = time(NULL);
3535 int ret;
3536 bool is_urgent = false, rodc = false;
3537 bool is_schema_nc;
3538 struct replmd_replicated_request *ac =
3539 talloc_get_type(req->context, struct replmd_replicated_request);
3540 struct replmd_private *replmd_private =
3541 talloc_get_type(ldb_module_get_private(ac->module),
3542 struct replmd_private);
3544 ldb = ldb_module_get_ctx(ac->module);
3546 if (ares->error != LDB_SUCCESS) {
3547 return ldb_module_done(ac->req, ares->controls,
3548 ares->response, ares->error);
3551 if (ares->type != LDB_REPLY_DONE) {
3552 ldb_set_errstring(ldb,
3553 "invalid ldb_reply_type in callback");
3554 talloc_free(ares);
3555 return ldb_module_done(ac->req, NULL, NULL,
3556 LDB_ERR_OPERATIONS_ERROR);
3559 /* TODO:
3560 * - replace the old object with the newly constructed one
3563 msg = ldb_msg_new(ac);
3564 if (msg == NULL) {
3565 ldb_oom(ldb);
3566 return LDB_ERR_OPERATIONS_ERROR;
3569 msg->dn = ac->req->op.rename.newdn;
3571 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3573 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3574 if (rdn_name == NULL) {
3575 talloc_free(ares);
3576 return ldb_module_done(ac->req, NULL, NULL,
3577 ldb_operr(ldb));
3580 /* normalize the rdn attribute name */
3581 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3582 if (rdn_attr == NULL) {
3583 talloc_free(ares);
3584 return ldb_module_done(ac->req, NULL, NULL,
3585 ldb_operr(ldb));
3587 rdn_name = rdn_attr->lDAPDisplayName;
3589 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3590 if (rdn_val == NULL) {
3591 talloc_free(ares);
3592 return ldb_module_done(ac->req, NULL, NULL,
3593 ldb_operr(ldb));
3596 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3597 talloc_free(ares);
3598 return ldb_module_done(ac->req, NULL, NULL,
3599 ldb_oom(ldb));
3601 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3602 talloc_free(ares);
3603 return ldb_module_done(ac->req, NULL, NULL,
3604 ldb_oom(ldb));
3606 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3607 talloc_free(ares);
3608 return ldb_module_done(ac->req, NULL, NULL,
3609 ldb_oom(ldb));
3611 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3612 talloc_free(ares);
3613 return ldb_module_done(ac->req, NULL, NULL,
3614 ldb_oom(ldb));
3618 * here we let replmd_update_rpmd() only search for
3619 * the existing "replPropertyMetaData" and rdn_name attributes.
3621 * We do not want the existing "name" attribute as
3622 * the "name" attribute needs to get the version
3623 * updated on rename even if the rdn value hasn't changed.
3625 * This is the diff of the meta data, for a moved user
3626 * on a w2k8r2 server:
3628 * # record 1
3629 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3630 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3631 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3632 * version : 0x00000001 (1)
3633 * reserved : 0x00000000 (0)
3634 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3635 * local_usn : 0x00000000000037a5 (14245)
3636 * array: struct replPropertyMetaData1
3637 * attid : DRSUAPI_ATTID_name (0x90001)
3638 * - version : 0x00000001 (1)
3639 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3640 * + version : 0x00000002 (2)
3641 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3642 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3643 * - originating_usn : 0x00000000000037a5 (14245)
3644 * - local_usn : 0x00000000000037a5 (14245)
3645 * + originating_usn : 0x0000000000003834 (14388)
3646 * + local_usn : 0x0000000000003834 (14388)
3647 * array: struct replPropertyMetaData1
3648 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3649 * version : 0x00000004 (4)
3651 attrs[0] = "replPropertyMetaData";
3652 attrs[1] = "objectClass";
3653 attrs[2] = "instanceType";
3654 attrs[3] = rdn_name;
3655 attrs[4] = NULL;
3657 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3658 msg, &ac->seq_num, t,
3659 is_schema_nc, &is_urgent, &rodc);
3660 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3661 ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
3662 talloc_free(ares);
3663 return ldb_module_done(req, NULL, NULL, ret);
3666 if (ret != LDB_SUCCESS) {
3667 talloc_free(ares);
3668 return ldb_module_done(ac->req, NULL, NULL, ret);
3671 if (ac->seq_num == 0) {
3672 talloc_free(ares);
3673 return ldb_module_done(ac->req, NULL, NULL,
3674 ldb_error(ldb, ret,
3675 "internal error seq_num == 0"));
3677 ac->is_urgent = is_urgent;
3679 ret = ldb_build_mod_req(&down_req, ldb, ac,
3680 msg,
3681 req->controls,
3682 ac, replmd_op_callback,
3683 req);
3684 LDB_REQ_SET_LOCATION(down_req);
3685 if (ret != LDB_SUCCESS) {
3686 talloc_free(ac);
3687 return ret;
3690 /* current partition control is needed by "replmd_op_callback" */
3691 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3692 ret = ldb_request_add_control(down_req,
3693 DSDB_CONTROL_CURRENT_PARTITION_OID,
3694 false, NULL);
3695 if (ret != LDB_SUCCESS) {
3696 talloc_free(ac);
3697 return ret;
3701 talloc_steal(down_req, msg);
3703 ret = add_time_element(msg, "whenChanged", t);
3704 if (ret != LDB_SUCCESS) {
3705 talloc_free(ac);
3706 ldb_operr(ldb);
3707 return ret;
3710 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3711 if (ret != LDB_SUCCESS) {
3712 talloc_free(ac);
3713 ldb_operr(ldb);
3714 return ret;
3717 /* go on with the call chain - do the modify after the rename */
3718 return ldb_next_request(ac->module, down_req);
3722 * remove links from objects that point at this object when an object
3723 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3724 * RemoveObj which states that link removal due to the object being
3725 * deleted is NOT an originating update - they just go away!
3728 static int replmd_delete_remove_link(struct ldb_module *module,
3729 const struct dsdb_schema *schema,
3730 struct replmd_private *replmd_private,
3731 struct ldb_dn *dn,
3732 struct GUID *guid,
3733 struct ldb_message_element *el,
3734 const struct dsdb_attribute *sa,
3735 struct ldb_request *parent)
3737 unsigned int i;
3738 TALLOC_CTX *tmp_ctx = talloc_new(module);
3739 struct ldb_context *ldb = ldb_module_get_ctx(module);
3741 for (i=0; i<el->num_values; i++) {
3742 struct dsdb_dn *dsdb_dn;
3743 int ret;
3744 struct ldb_message *msg;
3745 const struct dsdb_attribute *target_attr;
3746 struct ldb_message_element *el2;
3747 const char *dn_str;
3748 struct ldb_val dn_val;
3749 uint32_t dsdb_flags = 0;
3750 const char *attrs[] = { NULL, NULL };
3751 struct ldb_result *link_res;
3752 struct ldb_message *link_msg;
3753 struct ldb_message_element *link_el;
3754 struct parsed_dn *link_dns;
3755 struct parsed_dn *p = NULL, *unused = NULL;
3757 if (dsdb_dn_is_deleted_val(&el->values[i])) {
3758 continue;
3761 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
3762 if (!dsdb_dn) {
3763 talloc_free(tmp_ctx);
3764 return LDB_ERR_OPERATIONS_ERROR;
3767 /* remove the link */
3768 msg = ldb_msg_new(tmp_ctx);
3769 if (!msg) {
3770 ldb_module_oom(module);
3771 talloc_free(tmp_ctx);
3772 return LDB_ERR_OPERATIONS_ERROR;
3776 msg->dn = dsdb_dn->dn;
3778 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
3779 if (target_attr == NULL) {
3780 continue;
3782 attrs[0] = target_attr->lDAPDisplayName;
3784 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
3785 LDB_FLAG_MOD_DELETE, &el2);
3786 if (ret != LDB_SUCCESS) {
3787 ldb_module_oom(module);
3788 talloc_free(tmp_ctx);
3789 return LDB_ERR_OPERATIONS_ERROR;
3792 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
3793 msg->dn, attrs,
3794 DSDB_FLAG_NEXT_MODULE |
3795 DSDB_SEARCH_SHOW_EXTENDED_DN,
3796 parent);
3798 if (ret != LDB_SUCCESS) {
3799 talloc_free(tmp_ctx);
3800 return ret;
3803 link_msg = link_res->msgs[0];
3804 link_el = ldb_msg_find_element(link_msg,
3805 target_attr->lDAPDisplayName);
3806 if (link_el == NULL) {
3807 talloc_free(tmp_ctx);
3808 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3812 * This call 'upgrades' the links in link_dns, but we
3813 * do not commit the result back into the database, so
3814 * this is safe to call in FL2000 or on databases that
3815 * have been run at that level in the past.
3817 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx,
3818 link_el, &link_dns,
3819 target_attr->syntax->ldap_oid, parent);
3820 if (ret != LDB_SUCCESS) {
3821 talloc_free(tmp_ctx);
3822 return ret;
3825 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
3826 guid, dn,
3827 data_blob_null, 0,
3828 &p, &unused,
3829 target_attr->syntax->ldap_oid, false);
3830 if (ret != LDB_SUCCESS) {
3831 talloc_free(tmp_ctx);
3832 return ret;
3835 if (p == NULL) {
3836 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3837 "Failed to find forward link on %s "
3838 "as %s to remove backlink %s on %s",
3839 ldb_dn_get_linearized(msg->dn),
3840 target_attr->lDAPDisplayName,
3841 sa->lDAPDisplayName,
3842 ldb_dn_get_linearized(dn));
3843 talloc_free(tmp_ctx);
3844 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3848 /* This needs to get the Binary DN, by first searching */
3849 dn_str = dsdb_dn_get_linearized(tmp_ctx,
3850 p->dsdb_dn);
3852 dn_val = data_blob_string_const(dn_str);
3853 el2->values = &dn_val;
3854 el2->num_values = 1;
3857 * Ensure that we tell the modification to vanish any linked
3858 * attributes (not simply mark them as isDeleted = TRUE)
3860 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
3862 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
3863 if (ret != LDB_SUCCESS) {
3864 talloc_free(tmp_ctx);
3865 return ret;
3868 talloc_free(tmp_ctx);
3869 return LDB_SUCCESS;
3874 handle update of replication meta data for deletion of objects
3876 This also handles the mapping of delete to a rename operation
3877 to allow deletes to be replicated.
3879 It also handles the incoming deleted objects, to ensure they are
3880 fully deleted here. In that case re_delete is true, and we do not
3881 use this as a signal to change the deleted state, just reinforce it.
3884 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
3886 int ret = LDB_ERR_OTHER;
3887 bool retb, disallow_move_on_delete;
3888 struct ldb_dn *old_dn = NULL, *new_dn = NULL;
3889 const char *rdn_name;
3890 const struct ldb_val *rdn_value, *new_rdn_value;
3891 struct GUID guid;
3892 struct ldb_context *ldb = ldb_module_get_ctx(module);
3893 const struct dsdb_schema *schema;
3894 struct ldb_message *msg, *old_msg;
3895 struct ldb_message_element *el;
3896 TALLOC_CTX *tmp_ctx;
3897 struct ldb_result *res, *parent_res;
3898 static const char * const preserved_attrs[] = {
3899 /* yes, this really is a hard coded list. See MS-ADTS
3900 section 3.1.1.5.5.1.1 */
3901 "attributeID",
3902 "attributeSyntax",
3903 "dNReferenceUpdate",
3904 "dNSHostName",
3905 "flatName",
3906 "governsID",
3907 "groupType",
3908 "instanceType",
3909 "lDAPDisplayName",
3910 "legacyExchangeDN",
3911 "isDeleted",
3912 "isRecycled",
3913 "lastKnownParent",
3914 "msDS-LastKnownRDN",
3915 "msDS-PortLDAP",
3916 "mS-DS-CreatorSID",
3917 "mSMQOwnerID",
3918 "nCName",
3919 "objectClass",
3920 "distinguishedName",
3921 "objectGUID",
3922 "objectSid",
3923 "oMSyntax",
3924 "proxiedObjectName",
3925 "name",
3926 "nTSecurityDescriptor",
3927 "replPropertyMetaData",
3928 "sAMAccountName",
3929 "securityIdentifier",
3930 "sIDHistory",
3931 "subClassOf",
3932 "systemFlags",
3933 "trustPartner",
3934 "trustDirection",
3935 "trustType",
3936 "trustAttributes",
3937 "userAccountControl",
3938 "uSNChanged",
3939 "uSNCreated",
3940 "whenCreated",
3941 "whenChanged",
3942 NULL
3944 static const char * const all_attrs[] = {
3945 DSDB_SECRET_ATTRIBUTES,
3946 "*",
3947 NULL
3949 unsigned int i, el_count = 0;
3950 uint32_t dsdb_flags = 0;
3951 struct replmd_private *replmd_private;
3952 enum deletion_state deletion_state, next_deletion_state;
3954 if (ldb_dn_is_special(req->op.del.dn)) {
3955 return ldb_next_request(module, req);
3959 * We have to allow dbcheck to remove an object that
3960 * is beyond repair, and to do so totally. This could
3961 * mean we we can get a partial object from the other
3962 * DC, causing havoc, so dbcheck suggests
3963 * re-replication first. dbcheck sets both DBCHECK
3964 * and RELAX in this situation.
3966 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
3967 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
3968 /* really, really remove it */
3969 return ldb_next_request(module, req);
3972 tmp_ctx = talloc_new(ldb);
3973 if (!tmp_ctx) {
3974 ldb_oom(ldb);
3975 return LDB_ERR_OPERATIONS_ERROR;
3978 schema = dsdb_get_schema(ldb, tmp_ctx);
3979 if (!schema) {
3980 talloc_free(tmp_ctx);
3981 return LDB_ERR_OPERATIONS_ERROR;
3984 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
3986 /* we need the complete msg off disk, so we can work out which
3987 attributes need to be removed */
3988 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
3989 DSDB_FLAG_NEXT_MODULE |
3990 DSDB_SEARCH_SHOW_RECYCLED |
3991 DSDB_SEARCH_REVEAL_INTERNALS |
3992 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
3993 if (ret != LDB_SUCCESS) {
3994 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3995 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
3996 re_delete ? "re-delete" : "delete",
3997 ldb_dn_get_linearized(old_dn),
3998 ldb_errstring(ldb_module_get_ctx(module)));
3999 talloc_free(tmp_ctx);
4000 return ret;
4002 old_msg = res->msgs[0];
4004 replmd_deletion_state(module, old_msg,
4005 &deletion_state,
4006 &next_deletion_state);
4008 /* This supports us noticing an incoming isDeleted and acting on it */
4009 if (re_delete) {
4010 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
4011 next_deletion_state = deletion_state;
4014 if (next_deletion_state == OBJECT_REMOVED) {
4016 * We have to prevent objects being deleted, even if
4017 * the administrator really wants them gone, as
4018 * without the tombstone, we can get a partial object
4019 * from the other DC, causing havoc.
4021 * The only other valid case is when the 180 day
4022 * timeout has expired, when relax is specified.
4024 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
4025 /* it is already deleted - really remove it this time */
4026 talloc_free(tmp_ctx);
4027 return ldb_next_request(module, req);
4030 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
4031 "This check is to prevent corruption of the replicated state.",
4032 ldb_dn_get_linearized(old_msg->dn));
4033 return LDB_ERR_UNWILLING_TO_PERFORM;
4036 rdn_name = ldb_dn_get_rdn_name(old_dn);
4037 rdn_value = ldb_dn_get_rdn_val(old_dn);
4038 if ((rdn_name == NULL) || (rdn_value == NULL)) {
4039 talloc_free(tmp_ctx);
4040 return ldb_operr(ldb);
4043 msg = ldb_msg_new(tmp_ctx);
4044 if (msg == NULL) {
4045 ldb_module_oom(module);
4046 talloc_free(tmp_ctx);
4047 return LDB_ERR_OPERATIONS_ERROR;
4050 msg->dn = old_dn;
4052 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4053 disallow_move_on_delete =
4054 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4055 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4057 /* work out where we will be renaming this object to */
4058 if (!disallow_move_on_delete) {
4059 struct ldb_dn *deleted_objects_dn;
4060 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4061 &deleted_objects_dn);
4064 * We should not move objects if we can't find the
4065 * deleted objects DN. Not moving (or otherwise
4066 * harming) the Deleted Objects DN itself is handled
4067 * in the caller.
4069 if (re_delete && (ret != LDB_SUCCESS)) {
4070 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4071 if (new_dn == NULL) {
4072 ldb_module_oom(module);
4073 talloc_free(tmp_ctx);
4074 return LDB_ERR_OPERATIONS_ERROR;
4076 } else if (ret != LDB_SUCCESS) {
4077 /* this is probably an attempted delete on a partition
4078 * that doesn't allow delete operations, such as the
4079 * schema partition */
4080 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4081 ldb_dn_get_linearized(old_dn));
4082 talloc_free(tmp_ctx);
4083 return LDB_ERR_UNWILLING_TO_PERFORM;
4084 } else {
4085 new_dn = deleted_objects_dn;
4087 } else {
4088 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4089 if (new_dn == NULL) {
4090 ldb_module_oom(module);
4091 talloc_free(tmp_ctx);
4092 return LDB_ERR_OPERATIONS_ERROR;
4096 /* get the objects GUID from the search we just did */
4097 guid = samdb_result_guid(old_msg, "objectGUID");
4099 if (deletion_state == OBJECT_NOT_DELETED) {
4101 ret = replmd_make_deleted_child_dn(tmp_ctx,
4102 ldb,
4103 new_dn,
4104 rdn_name, rdn_value,
4105 guid);
4107 if (ret != LDB_SUCCESS) {
4108 talloc_free(tmp_ctx);
4109 return ret;
4112 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
4113 if (ret != LDB_SUCCESS) {
4114 ldb_asprintf_errstring(ldb, __location__
4115 ": Failed to add isDeleted string to the msg");
4116 talloc_free(tmp_ctx);
4117 return ret;
4119 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4120 } else {
4122 * No matter what has happened with other renames etc, try again to
4123 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4126 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4127 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4128 if (!retb) {
4129 ldb_asprintf_errstring(ldb, __location__
4130 ": Unable to add a prepare rdn of %s",
4131 ldb_dn_get_linearized(rdn));
4132 talloc_free(tmp_ctx);
4133 return LDB_ERR_OPERATIONS_ERROR;
4135 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4137 retb = ldb_dn_add_child(new_dn, rdn);
4138 if (!retb) {
4139 ldb_asprintf_errstring(ldb, __location__
4140 ": Unable to add rdn %s to base dn: %s",
4141 ldb_dn_get_linearized(rdn),
4142 ldb_dn_get_linearized(new_dn));
4143 talloc_free(tmp_ctx);
4144 return LDB_ERR_OPERATIONS_ERROR;
4149 now we need to modify the object in the following ways:
4151 - add isDeleted=TRUE
4152 - update rDN and name, with new rDN
4153 - remove linked attributes
4154 - remove objectCategory and sAMAccountType
4155 - remove attribs not on the preserved list
4156 - preserved if in above list, or is rDN
4157 - remove all linked attribs from this object
4158 - remove all links from other objects to this object
4159 - add lastKnownParent
4160 - update replPropertyMetaData?
4162 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4165 if (deletion_state == OBJECT_NOT_DELETED) {
4166 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4167 char *parent_dn_str = NULL;
4169 /* we need the storage form of the parent GUID */
4170 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4171 parent_dn, NULL,
4172 DSDB_FLAG_NEXT_MODULE |
4173 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4174 DSDB_SEARCH_REVEAL_INTERNALS|
4175 DSDB_SEARCH_SHOW_RECYCLED, req);
4176 if (ret != LDB_SUCCESS) {
4177 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4178 "repmd_delete: Failed to %s %s, "
4179 "because we failed to find it's parent (%s): %s",
4180 re_delete ? "re-delete" : "delete",
4181 ldb_dn_get_linearized(old_dn),
4182 ldb_dn_get_linearized(parent_dn),
4183 ldb_errstring(ldb_module_get_ctx(module)));
4184 talloc_free(tmp_ctx);
4185 return ret;
4189 * Now we can use the DB version,
4190 * it will have the extended DN info in it
4192 parent_dn = parent_res->msgs[0]->dn;
4193 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4194 parent_dn,
4196 if (parent_dn_str == NULL) {
4197 talloc_free(tmp_ctx);
4198 return ldb_module_oom(module);
4201 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4202 parent_dn_str);
4203 if (ret != LDB_SUCCESS) {
4204 ldb_asprintf_errstring(ldb, __location__
4205 ": Failed to add lastKnownParent "
4206 "string when deleting %s",
4207 ldb_dn_get_linearized(old_dn));
4208 talloc_free(tmp_ctx);
4209 return ret;
4211 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4213 if (next_deletion_state == OBJECT_DELETED) {
4214 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4215 if (ret != LDB_SUCCESS) {
4216 ldb_asprintf_errstring(ldb, __location__
4217 ": Failed to add msDS-LastKnownRDN "
4218 "string when deleting %s",
4219 ldb_dn_get_linearized(old_dn));
4220 talloc_free(tmp_ctx);
4221 return ret;
4223 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
4227 switch (next_deletion_state) {
4229 case OBJECT_RECYCLED:
4230 case OBJECT_TOMBSTONE:
4233 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4234 * describes what must be removed from a tombstone
4235 * object
4237 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4238 * describes what must be removed from a recycled
4239 * object
4244 * we also mark it as recycled, meaning this object can't be
4245 * recovered (we are stripping its attributes).
4246 * This is done only if we have this schema object of course ...
4247 * This behavior is identical to the one of Windows 2008R2 which
4248 * always set the isRecycled attribute, even if the recycle-bin is
4249 * not activated and what ever the forest level is.
4251 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4252 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
4253 if (ret != LDB_SUCCESS) {
4254 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4255 ldb_module_oom(module);
4256 talloc_free(tmp_ctx);
4257 return ret;
4259 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4262 replmd_private = talloc_get_type(ldb_module_get_private(module),
4263 struct replmd_private);
4264 /* work out which of the old attributes we will be removing */
4265 for (i=0; i<old_msg->num_elements; i++) {
4266 const struct dsdb_attribute *sa;
4267 el = &old_msg->elements[i];
4268 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4269 if (!sa) {
4270 talloc_free(tmp_ctx);
4271 return LDB_ERR_OPERATIONS_ERROR;
4273 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4274 /* don't remove the rDN */
4275 continue;
4277 if (sa->linkID & 1) {
4279 we have a backlink in this object
4280 that needs to be removed. We're not
4281 allowed to remove it directly
4282 however, so we instead setup a
4283 modify to delete the corresponding
4284 forward link
4286 ret = replmd_delete_remove_link(module, schema,
4287 replmd_private,
4288 old_dn, &guid,
4289 el, sa, req);
4290 if (ret != LDB_SUCCESS) {
4291 const char *old_dn_str
4292 = ldb_dn_get_linearized(old_dn);
4293 ldb_asprintf_errstring(ldb,
4294 __location__
4295 ": Failed to remove backlink of "
4296 "%s when deleting %s: %s",
4297 el->name,
4298 old_dn_str,
4299 ldb_errstring(ldb));
4300 talloc_free(tmp_ctx);
4301 return LDB_ERR_OPERATIONS_ERROR;
4303 /* now we continue, which means we
4304 won't remove this backlink
4305 directly
4307 continue;
4308 } else if (sa->linkID == 0) {
4309 if (ldb_attr_in_list(preserved_attrs, el->name)) {
4310 continue;
4312 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4313 continue;
4315 } else {
4317 * Ensure that we tell the modification to vanish any linked
4318 * attributes (not simply mark them as isDeleted = TRUE)
4320 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4322 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4323 if (ret != LDB_SUCCESS) {
4324 talloc_free(tmp_ctx);
4325 ldb_module_oom(module);
4326 return ret;
4330 break;
4332 case OBJECT_DELETED:
4334 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4335 * describes what must be removed from a deleted
4336 * object
4339 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4340 if (ret != LDB_SUCCESS) {
4341 talloc_free(tmp_ctx);
4342 ldb_module_oom(module);
4343 return ret;
4346 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4347 if (ret != LDB_SUCCESS) {
4348 talloc_free(tmp_ctx);
4349 ldb_module_oom(module);
4350 return ret;
4353 break;
4355 default:
4356 break;
4359 if (deletion_state == OBJECT_NOT_DELETED) {
4360 const struct dsdb_attribute *sa;
4362 /* work out what the new rdn value is, for updating the
4363 rDN and name fields */
4364 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4365 if (new_rdn_value == NULL) {
4366 talloc_free(tmp_ctx);
4367 return ldb_operr(ldb);
4370 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4371 if (!sa) {
4372 talloc_free(tmp_ctx);
4373 return LDB_ERR_OPERATIONS_ERROR;
4376 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4377 &el);
4378 if (ret != LDB_SUCCESS) {
4379 talloc_free(tmp_ctx);
4380 return ret;
4382 el->flags = LDB_FLAG_MOD_REPLACE;
4384 el = ldb_msg_find_element(old_msg, "name");
4385 if (el) {
4386 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4387 if (ret != LDB_SUCCESS) {
4388 talloc_free(tmp_ctx);
4389 return ret;
4391 el->flags = LDB_FLAG_MOD_REPLACE;
4396 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4401 * No matter what has happned with other renames, try again to
4402 * get this to be under the deleted DN.
4404 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4405 /* now rename onto the new DN */
4406 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4407 if (ret != LDB_SUCCESS){
4408 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4409 ldb_dn_get_linearized(old_dn),
4410 ldb_dn_get_linearized(new_dn),
4411 ldb_errstring(ldb)));
4412 talloc_free(tmp_ctx);
4413 return ret;
4415 msg->dn = new_dn;
4418 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4419 if (ret != LDB_SUCCESS) {
4420 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
4421 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
4422 talloc_free(tmp_ctx);
4423 return ret;
4426 talloc_free(tmp_ctx);
4428 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4431 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4433 return replmd_delete_internals(module, req, false);
4437 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4439 return ret;
4442 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4444 int ret = LDB_ERR_OTHER;
4445 /* TODO: do some error mapping */
4447 /* Let the caller know the full WERROR */
4448 ar->objs->error = status;
4450 return ret;
4454 static struct replPropertyMetaData1 *
4455 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4456 enum drsuapi_DsAttributeId attid)
4458 uint32_t i;
4459 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4461 for (i = 0; i < rpmd_ctr->count; i++) {
4462 if (rpmd_ctr->array[i].attid == attid) {
4463 return &rpmd_ctr->array[i];
4466 return NULL;
4471 return true if an update is newer than an existing entry
4472 see section 5.11 of MS-ADTS
4474 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4475 const struct GUID *update_invocation_id,
4476 uint32_t current_version,
4477 uint32_t update_version,
4478 NTTIME current_change_time,
4479 NTTIME update_change_time)
4481 if (update_version != current_version) {
4482 return update_version > current_version;
4484 if (update_change_time != current_change_time) {
4485 return update_change_time > current_change_time;
4487 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4490 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4491 struct replPropertyMetaData1 *new_m)
4493 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4494 &new_m->originating_invocation_id,
4495 cur_m->version,
4496 new_m->version,
4497 cur_m->originating_change_time,
4498 new_m->originating_change_time);
4501 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4502 struct replPropertyMetaData1 *cur_m,
4503 struct replPropertyMetaData1 *new_m)
4505 bool cmp;
4508 * If the new replPropertyMetaData entry for this attribute is
4509 * not provided (this happens in the case where we look for
4510 * ATTID_name, but the name was not changed), then the local
4511 * state is clearly still current, as the remote
4512 * server didn't send it due to being older the high watermark
4513 * USN we sent.
4515 if (new_m == NULL) {
4516 return false;
4519 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4521 * if we compare equal then do an
4522 * update. This is used when a client
4523 * asks for a FULL_SYNC, and can be
4524 * used to recover a corrupt
4525 * replica.
4527 * This call is a bit tricky, what we
4528 * are doing it turning the 'is_newer'
4529 * call into a 'not is older' by
4530 * swapping cur_m and new_m, and negating the
4531 * outcome.
4533 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4534 cur_m);
4535 } else {
4536 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4537 new_m);
4539 return cmp;
4544 form a DN for a deleted (DEL:) or conflict (CNF:) DN
4546 static int replmd_make_prefix_child_dn(TALLOC_CTX *tmp_ctx,
4547 struct ldb_context *ldb,
4548 struct ldb_dn *dn,
4549 const char *four_char_prefix,
4550 const char *rdn_name,
4551 const struct ldb_val *rdn_value,
4552 struct GUID guid)
4554 struct ldb_val deleted_child_rdn_val;
4555 struct GUID_txt_buf guid_str;
4556 bool retb;
4558 GUID_buf_string(&guid, &guid_str);
4560 retb = ldb_dn_add_child_fmt(dn, "X=Y");
4561 if (!retb) {
4562 ldb_asprintf_errstring(ldb, __location__
4563 ": Unable to add a formatted child to dn: %s",
4564 ldb_dn_get_linearized(dn));
4565 return LDB_ERR_OPERATIONS_ERROR;
4569 * TODO: Per MS-ADTS 3.1.1.5.5 Delete Operation
4570 * we should truncate this value to ensure the RDN is not more than 255 chars.
4572 * However we MS-ADTS 3.1.1.5.1.2 Naming Constraints indicates that:
4574 * "Naming constraints are not enforced for replicated
4575 * updates." so this is safe and we don't have to work out not
4576 * splitting a UTF8 char right now.
4578 deleted_child_rdn_val = ldb_val_dup(tmp_ctx, rdn_value);
4581 * sizeof(guid_str.buf) will always be longer than
4582 * strlen(guid_str.buf) but we allocate using this and
4583 * waste the trailing bytes to avoid scaring folks
4584 * with memcpy() using strlen() below
4587 deleted_child_rdn_val.data
4588 = talloc_realloc(tmp_ctx, deleted_child_rdn_val.data,
4589 uint8_t,
4590 rdn_value->length + 5
4591 + sizeof(guid_str.buf));
4592 if (!deleted_child_rdn_val.data) {
4593 ldb_asprintf_errstring(ldb, __location__
4594 ": Unable to add a formatted child to dn: %s",
4595 ldb_dn_get_linearized(dn));
4596 return LDB_ERR_OPERATIONS_ERROR;
4599 deleted_child_rdn_val.length =
4600 rdn_value->length + 5
4601 + strlen(guid_str.buf);
4603 SMB_ASSERT(deleted_child_rdn_val.length <
4604 talloc_get_size(deleted_child_rdn_val.data));
4607 * talloc won't allocate more than 256MB so we can't
4608 * overflow but just to be sure
4610 if (deleted_child_rdn_val.length < rdn_value->length) {
4611 return LDB_ERR_OPERATIONS_ERROR;
4614 deleted_child_rdn_val.data[rdn_value->length] = 0x0a;
4615 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 1],
4616 four_char_prefix, 4);
4617 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 5],
4618 guid_str.buf,
4619 sizeof(guid_str.buf));
4621 /* Now set the value into the RDN, without parsing it */
4622 ldb_dn_set_component(dn, 0, rdn_name,
4623 deleted_child_rdn_val);
4625 return LDB_SUCCESS;
4630 form a conflict DN
4632 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx,
4633 struct ldb_context *ldb,
4634 struct ldb_dn *dn,
4635 struct GUID *guid)
4637 const struct ldb_val *rdn_val;
4638 const char *rdn_name;
4639 struct ldb_dn *new_dn;
4640 int ret;
4642 rdn_val = ldb_dn_get_rdn_val(dn);
4643 rdn_name = ldb_dn_get_rdn_name(dn);
4644 if (!rdn_val || !rdn_name) {
4645 return NULL;
4648 new_dn = ldb_dn_get_parent(mem_ctx, dn);
4649 if (!new_dn) {
4650 return NULL;
4653 ret = replmd_make_prefix_child_dn(mem_ctx,
4654 ldb, new_dn,
4655 "CNF:",
4656 rdn_name,
4657 rdn_val,
4658 *guid);
4659 if (ret != LDB_SUCCESS) {
4660 return NULL;
4662 return new_dn;
4666 form a deleted DN
4668 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
4669 struct ldb_context *ldb,
4670 struct ldb_dn *dn,
4671 const char *rdn_name,
4672 const struct ldb_val *rdn_value,
4673 struct GUID guid)
4675 return replmd_make_prefix_child_dn(tmp_ctx,
4676 ldb, dn,
4677 "DEL:",
4678 rdn_name,
4679 rdn_value,
4680 guid);
4685 perform a modify operation which sets the rDN and name attributes to
4686 their current values. This has the effect of changing these
4687 attributes to have been last updated by the current DC. This is
4688 needed to ensure that renames performed as part of conflict
4689 resolution are propogated to other DCs
4691 static int replmd_name_modify(struct replmd_replicated_request *ar,
4692 struct ldb_request *req, struct ldb_dn *dn)
4694 struct ldb_message *msg;
4695 const char *rdn_name;
4696 const struct ldb_val *rdn_val;
4697 const struct dsdb_attribute *rdn_attr;
4698 int ret;
4700 msg = ldb_msg_new(req);
4701 if (msg == NULL) {
4702 goto failed;
4704 msg->dn = dn;
4706 rdn_name = ldb_dn_get_rdn_name(dn);
4707 if (rdn_name == NULL) {
4708 goto failed;
4711 /* normalize the rdn attribute name */
4712 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
4713 if (rdn_attr == NULL) {
4714 goto failed;
4716 rdn_name = rdn_attr->lDAPDisplayName;
4718 rdn_val = ldb_dn_get_rdn_val(dn);
4719 if (rdn_val == NULL) {
4720 goto failed;
4723 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4724 goto failed;
4726 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
4727 goto failed;
4729 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4730 goto failed;
4732 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
4733 goto failed;
4737 * We have to mark this as a replicated update otherwise
4738 * schema_data may reject a rename in the schema partition
4741 ret = dsdb_module_modify(ar->module, msg,
4742 DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
4743 req);
4744 if (ret != LDB_SUCCESS) {
4745 DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
4746 ldb_dn_get_linearized(dn),
4747 ldb_errstring(ldb_module_get_ctx(ar->module))));
4748 return ret;
4751 talloc_free(msg);
4753 return LDB_SUCCESS;
4755 failed:
4756 talloc_free(msg);
4757 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
4758 ldb_dn_get_linearized(dn)));
4759 return LDB_ERR_OPERATIONS_ERROR;
4764 callback for conflict DN handling where we have renamed the incoming
4765 record. After renaming it, we need to ensure the change of name and
4766 rDN for the incoming record is seen as an originating update by this DC.
4768 This also handles updating lastKnownParent for entries sent to lostAndFound
4770 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
4772 struct replmd_replicated_request *ar =
4773 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4774 struct ldb_dn *conflict_dn = NULL;
4775 int ret;
4777 if (ares->error != LDB_SUCCESS) {
4778 /* call the normal callback for everything except success */
4779 return replmd_op_callback(req, ares);
4782 switch (req->operation) {
4783 case LDB_ADD:
4784 conflict_dn = req->op.add.message->dn;
4785 break;
4786 case LDB_MODIFY:
4787 conflict_dn = req->op.mod.message->dn;
4788 break;
4789 default:
4790 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
4793 /* perform a modify of the rDN and name of the record */
4794 ret = replmd_name_modify(ar, req, conflict_dn);
4795 if (ret != LDB_SUCCESS) {
4796 ares->error = ret;
4797 return replmd_op_callback(req, ares);
4800 if (ar->objs->objects[ar->index_current].last_known_parent) {
4801 struct ldb_message *msg = ldb_msg_new(req);
4802 if (msg == NULL) {
4803 ldb_module_oom(ar->module);
4804 return LDB_ERR_OPERATIONS_ERROR;
4807 msg->dn = req->op.add.message->dn;
4809 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4810 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
4811 if (ret != LDB_SUCCESS) {
4812 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
4813 ldb_module_oom(ar->module);
4814 return ret;
4816 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
4818 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
4819 if (ret != LDB_SUCCESS) {
4820 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
4821 ldb_dn_get_linearized(msg->dn),
4822 ldb_errstring(ldb_module_get_ctx(ar->module))));
4823 return ret;
4825 TALLOC_FREE(msg);
4828 return replmd_op_callback(req, ares);
4832 callback for replmd_replicated_apply_add()
4833 This copes with the creation of conflict records in the case where
4834 the DN exists, but with a different objectGUID
4836 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))
4838 struct ldb_dn *conflict_dn;
4839 struct replmd_replicated_request *ar =
4840 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4841 struct ldb_result *res;
4842 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4843 int ret;
4844 const struct ldb_val *omd_value;
4845 struct replPropertyMetaDataBlob omd, *rmd;
4846 enum ndr_err_code ndr_err;
4847 bool rename_incoming_record, rodc;
4848 struct replPropertyMetaData1 *rmd_name, *omd_name;
4849 struct ldb_message *msg;
4850 struct ldb_request *down_req = NULL;
4852 /* call the normal callback for success */
4853 if (ares->error == LDB_SUCCESS) {
4854 return callback(req, ares);
4858 * we have a conflict, and need to decide if we will keep the
4859 * new record or the old record
4862 msg = ar->objs->objects[ar->index_current].msg;
4863 conflict_dn = msg->dn;
4865 /* For failures other than conflicts, fail the whole operation here */
4866 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4867 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
4868 ldb_dn_get_linearized(conflict_dn),
4869 ldb_errstring(ldb_module_get_ctx(ar->module)));
4871 return ldb_module_done(ar->req, NULL, NULL,
4872 LDB_ERR_OPERATIONS_ERROR);
4875 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4876 if (ret != LDB_SUCCESS) {
4877 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)));
4878 return ldb_module_done(ar->req, NULL, NULL,
4879 LDB_ERR_OPERATIONS_ERROR);
4883 if (rodc) {
4885 * We are on an RODC, or were a GC for this
4886 * partition, so we have to fail this until
4887 * someone who owns the partition sorts it
4888 * out
4890 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4891 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
4892 " - We must fail the operation until a master for this partition resolves the conflict",
4893 ldb_dn_get_linearized(conflict_dn));
4894 goto failed;
4898 * first we need the replPropertyMetaData attribute from the
4899 * local, conflicting record
4901 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
4902 attrs,
4903 DSDB_FLAG_NEXT_MODULE |
4904 DSDB_SEARCH_SHOW_DELETED |
4905 DSDB_SEARCH_SHOW_RECYCLED, req);
4906 if (ret != LDB_SUCCESS) {
4907 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4908 ldb_dn_get_linearized(conflict_dn)));
4909 goto failed;
4912 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4913 if (omd_value == NULL) {
4914 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4915 ldb_dn_get_linearized(conflict_dn)));
4916 goto failed;
4919 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4920 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4921 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4922 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4923 ldb_dn_get_linearized(conflict_dn)));
4924 goto failed;
4927 rmd = ar->objs->objects[ar->index_current].meta_data;
4930 * we decide which is newer based on the RPMD on the name
4931 * attribute. See [MS-DRSR] ResolveNameConflict.
4933 * We expect omd_name to be present, as this is from a local
4934 * search, but while rmd_name should have been given to us by
4935 * the remote server, if it is missing we just prefer the
4936 * local name in
4937 * replmd_replPropertyMetaData1_new_should_be_taken()
4939 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4940 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4941 if (!omd_name) {
4942 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4943 ldb_dn_get_linearized(conflict_dn)));
4944 goto failed;
4948 * Should we preserve the current record, and so rename the
4949 * incoming record to be a conflict?
4951 rename_incoming_record
4952 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
4953 omd_name, rmd_name);
4955 if (rename_incoming_record) {
4956 struct GUID guid;
4957 struct ldb_dn *new_dn;
4959 guid = samdb_result_guid(msg, "objectGUID");
4960 if (GUID_all_zero(&guid)) {
4961 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
4962 ldb_dn_get_linearized(conflict_dn)));
4963 goto failed;
4965 new_dn = replmd_conflict_dn(req,
4966 ldb_module_get_ctx(ar->module),
4967 conflict_dn, &guid);
4968 if (new_dn == NULL) {
4969 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4970 ldb_dn_get_linearized(conflict_dn)));
4971 goto failed;
4974 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
4975 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4977 /* re-submit the request, but with the new DN */
4978 callback = replmd_op_name_modify_callback;
4979 msg->dn = new_dn;
4980 } else {
4981 /* we are renaming the existing record */
4982 struct GUID guid;
4983 struct ldb_dn *new_dn;
4985 guid = samdb_result_guid(res->msgs[0], "objectGUID");
4986 if (GUID_all_zero(&guid)) {
4987 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
4988 ldb_dn_get_linearized(conflict_dn)));
4989 goto failed;
4992 new_dn = replmd_conflict_dn(req,
4993 ldb_module_get_ctx(ar->module),
4994 conflict_dn, &guid);
4995 if (new_dn == NULL) {
4996 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4997 ldb_dn_get_linearized(conflict_dn)));
4998 goto failed;
5001 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5002 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5004 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5005 DSDB_FLAG_OWN_MODULE, req);
5006 if (ret != LDB_SUCCESS) {
5007 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5008 ldb_dn_get_linearized(conflict_dn),
5009 ldb_dn_get_linearized(new_dn),
5010 ldb_errstring(ldb_module_get_ctx(ar->module))));
5011 goto failed;
5015 * now we need to ensure that the rename is seen as an
5016 * originating update. We do that with a modify.
5018 ret = replmd_name_modify(ar, req, new_dn);
5019 if (ret != LDB_SUCCESS) {
5020 goto failed;
5023 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
5024 ldb_dn_get_linearized(req->op.add.message->dn)));
5027 ret = ldb_build_add_req(&down_req,
5028 ldb_module_get_ctx(ar->module),
5029 req,
5030 msg,
5031 ar->controls,
5033 callback,
5034 req);
5035 if (ret != LDB_SUCCESS) {
5036 goto failed;
5038 LDB_REQ_SET_LOCATION(down_req);
5040 /* current partition control needed by "repmd_op_callback" */
5041 ret = ldb_request_add_control(down_req,
5042 DSDB_CONTROL_CURRENT_PARTITION_OID,
5043 false, NULL);
5044 if (ret != LDB_SUCCESS) {
5045 return replmd_replicated_request_error(ar, ret);
5048 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5049 /* this tells the partition module to make it a
5050 partial replica if creating an NC */
5051 ret = ldb_request_add_control(down_req,
5052 DSDB_CONTROL_PARTIAL_REPLICA,
5053 false, NULL);
5054 if (ret != LDB_SUCCESS) {
5055 return replmd_replicated_request_error(ar, ret);
5060 * Finally we re-run the add, otherwise the new record won't
5061 * exist, as we are here because of that exact failure!
5063 return ldb_next_request(ar->module, down_req);
5064 failed:
5066 /* on failure make the caller get the error. This means
5067 * replication will stop with an error, but there is not much
5068 * else we can do.
5070 return ldb_module_done(ar->req, NULL, NULL,
5071 ret);
5075 callback for replmd_replicated_apply_add()
5076 This copes with the creation of conflict records in the case where
5077 the DN exists, but with a different objectGUID
5079 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
5081 struct replmd_replicated_request *ar =
5082 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5084 if (ar->objs->objects[ar->index_current].last_known_parent) {
5085 /* This is like a conflict DN, where we put the object in LostAndFound
5086 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
5087 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
5090 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
5094 this is called when a new object comes in over DRS
5096 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
5098 struct ldb_context *ldb;
5099 struct ldb_request *change_req;
5100 enum ndr_err_code ndr_err;
5101 struct ldb_message *msg;
5102 struct replPropertyMetaDataBlob *md;
5103 struct ldb_val md_value;
5104 unsigned int i;
5105 int ret;
5106 bool remote_isDeleted = false;
5107 bool is_schema_nc;
5108 NTTIME now;
5109 time_t t = time(NULL);
5110 const struct ldb_val *rdn_val;
5111 struct replmd_private *replmd_private =
5112 talloc_get_type(ldb_module_get_private(ar->module),
5113 struct replmd_private);
5114 unix_to_nt_time(&now, t);
5116 ldb = ldb_module_get_ctx(ar->module);
5117 msg = ar->objs->objects[ar->index_current].msg;
5118 md = ar->objs->objects[ar->index_current].meta_data;
5119 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5121 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5122 if (ret != LDB_SUCCESS) {
5123 return replmd_replicated_request_error(ar, ret);
5126 ret = dsdb_msg_add_guid(msg,
5127 &ar->objs->objects[ar->index_current].object_guid,
5128 "objectGUID");
5129 if (ret != LDB_SUCCESS) {
5130 return replmd_replicated_request_error(ar, ret);
5133 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5134 if (ret != LDB_SUCCESS) {
5135 return replmd_replicated_request_error(ar, ret);
5138 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
5139 if (ret != LDB_SUCCESS) {
5140 return replmd_replicated_request_error(ar, ret);
5143 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5144 if (ret != LDB_SUCCESS) {
5145 return replmd_replicated_request_error(ar, ret);
5148 /* remove any message elements that have zero values */
5149 for (i=0; i<msg->num_elements; i++) {
5150 struct ldb_message_element *el = &msg->elements[i];
5152 if (el->num_values == 0) {
5153 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5154 ldb_asprintf_errstring(ldb, __location__
5155 ": empty objectClass sent on %s, aborting replication\n",
5156 ldb_dn_get_linearized(msg->dn));
5157 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5160 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5161 el->name));
5162 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
5163 msg->num_elements--;
5164 i--;
5165 continue;
5169 if (DEBUGLVL(8)) {
5170 struct GUID_txt_buf guid_txt;
5172 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5173 LDB_CHANGETYPE_ADD,
5174 msg);
5175 DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5176 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5177 s));
5178 talloc_free(s);
5179 } else if (DEBUGLVL(4)) {
5180 struct GUID_txt_buf guid_txt;
5181 DEBUG(4, ("DRS replication add DN of %s is %s\n",
5182 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5183 ldb_dn_get_linearized(msg->dn)));
5185 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5186 "isDeleted", false);
5189 * the meta data array is already sorted by the caller, except
5190 * for the RDN, which needs to be added.
5194 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5195 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5196 md, ar, now, is_schema_nc,
5197 false);
5198 if (ret != LDB_SUCCESS) {
5199 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5200 return replmd_replicated_request_error(ar, ret);
5203 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5204 if (ret != LDB_SUCCESS) {
5205 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5206 return replmd_replicated_request_error(ar, ret);
5209 for (i=0; i < md->ctr.ctr1.count; i++) {
5210 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5212 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5213 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5214 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5215 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5216 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5218 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5219 if (ret != LDB_SUCCESS) {
5220 return replmd_replicated_request_error(ar, ret);
5223 replmd_ldb_message_sort(msg, ar->schema);
5225 if (!remote_isDeleted) {
5226 ret = dsdb_module_schedule_sd_propagation(ar->module,
5227 ar->objs->partition_dn,
5228 msg->dn, true);
5229 if (ret != LDB_SUCCESS) {
5230 return replmd_replicated_request_error(ar, ret);
5234 ar->isDeleted = remote_isDeleted;
5236 ret = ldb_build_add_req(&change_req,
5237 ldb,
5239 msg,
5240 ar->controls,
5242 replmd_op_add_callback,
5243 ar->req);
5244 LDB_REQ_SET_LOCATION(change_req);
5245 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5247 /* current partition control needed by "repmd_op_callback" */
5248 ret = ldb_request_add_control(change_req,
5249 DSDB_CONTROL_CURRENT_PARTITION_OID,
5250 false, NULL);
5251 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5253 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5254 /* this tells the partition module to make it a
5255 partial replica if creating an NC */
5256 ret = ldb_request_add_control(change_req,
5257 DSDB_CONTROL_PARTIAL_REPLICA,
5258 false, NULL);
5259 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5262 return ldb_next_request(ar->module, change_req);
5265 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5266 struct ldb_reply *ares)
5268 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5269 struct replmd_replicated_request);
5270 int ret;
5272 if (!ares) {
5273 return ldb_module_done(ar->req, NULL, NULL,
5274 LDB_ERR_OPERATIONS_ERROR);
5278 * The error NO_SUCH_OBJECT is not expected, unless the search
5279 * base is the partition DN, and that case doesn't happen here
5280 * because then we wouldn't get a parent_guid_value in any
5281 * case.
5283 if (ares->error != LDB_SUCCESS) {
5284 return ldb_module_done(ar->req, ares->controls,
5285 ares->response, ares->error);
5288 switch (ares->type) {
5289 case LDB_REPLY_ENTRY:
5291 struct ldb_message *parent_msg = ares->message;
5292 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5293 struct ldb_dn *parent_dn = NULL;
5294 int comp_num;
5296 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5297 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5298 /* Per MS-DRSR 4.1.10.6.10
5299 * FindBestParentObject we need to move this
5300 * new object under a deleted object to
5301 * lost-and-found */
5302 struct ldb_dn *nc_root;
5304 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5305 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5306 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5307 "No suitable NC root found for %s. "
5308 "We need to move this object because parent object %s "
5309 "is deleted, but this object is not.",
5310 ldb_dn_get_linearized(msg->dn),
5311 ldb_dn_get_linearized(parent_msg->dn));
5312 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5313 } else if (ret != LDB_SUCCESS) {
5314 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5315 "Unable to find NC root for %s: %s. "
5316 "We need to move this object because parent object %s "
5317 "is deleted, but this object is not.",
5318 ldb_dn_get_linearized(msg->dn),
5319 ldb_errstring(ldb_module_get_ctx(ar->module)),
5320 ldb_dn_get_linearized(parent_msg->dn));
5321 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5324 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5325 nc_root,
5326 DS_GUID_LOSTANDFOUND_CONTAINER,
5327 &parent_dn);
5328 if (ret != LDB_SUCCESS) {
5329 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5330 "Unable to find LostAndFound Container for %s "
5331 "in partition %s: %s. "
5332 "We need to move this object because parent object %s "
5333 "is deleted, but this object is not.",
5334 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5335 ldb_errstring(ldb_module_get_ctx(ar->module)),
5336 ldb_dn_get_linearized(parent_msg->dn));
5337 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5339 ar->objs->objects[ar->index_current].last_known_parent
5340 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5342 } else {
5343 parent_dn
5344 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5347 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5349 comp_num = ldb_dn_get_comp_num(msg->dn);
5350 if (comp_num > 1) {
5351 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5352 talloc_free(ares);
5353 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5356 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5357 talloc_free(ares);
5358 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5360 break;
5362 case LDB_REPLY_REFERRAL:
5363 /* we ignore referrals */
5364 break;
5366 case LDB_REPLY_DONE:
5368 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5369 struct GUID_txt_buf str_buf;
5370 if (ar->search_msg != NULL) {
5371 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5372 "No parent with GUID %s found for object locally known as %s",
5373 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5374 ldb_dn_get_linearized(ar->search_msg->dn));
5375 } else {
5376 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5377 "No parent with GUID %s found for object remotely known as %s",
5378 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5379 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5383 * This error code is really important, as it
5384 * is the flag back to the callers to retry
5385 * this with DRSUAPI_DRS_GET_ANC, and so get
5386 * the parent objects before the child
5387 * objects
5389 return ldb_module_done(ar->req, NULL, NULL,
5390 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5393 if (ar->search_msg != NULL) {
5394 ret = replmd_replicated_apply_merge(ar);
5395 } else {
5396 ret = replmd_replicated_apply_add(ar);
5398 if (ret != LDB_SUCCESS) {
5399 return ldb_module_done(ar->req, NULL, NULL, ret);
5403 talloc_free(ares);
5404 return LDB_SUCCESS;
5408 * Look for the parent object, so we put the new object in the right
5409 * place This is akin to NameObject in MS-DRSR - this routine and the
5410 * callbacks find the right parent name, and correct name for this
5411 * object
5414 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5416 struct ldb_context *ldb;
5417 int ret;
5418 char *tmp_str;
5419 char *filter;
5420 struct ldb_request *search_req;
5421 static const char *attrs[] = {"isDeleted", NULL};
5422 struct GUID_txt_buf guid_str_buf;
5424 ldb = ldb_module_get_ctx(ar->module);
5426 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5427 if (ar->search_msg != NULL) {
5428 return replmd_replicated_apply_merge(ar);
5429 } else {
5430 return replmd_replicated_apply_add(ar);
5434 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5435 &guid_str_buf);
5437 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5438 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5440 ret = ldb_build_search_req(&search_req,
5441 ldb,
5443 ar->objs->partition_dn,
5444 LDB_SCOPE_SUBTREE,
5445 filter,
5446 attrs,
5447 NULL,
5449 replmd_replicated_apply_search_for_parent_callback,
5450 ar->req);
5451 LDB_REQ_SET_LOCATION(search_req);
5453 ret = dsdb_request_add_controls(search_req,
5454 DSDB_SEARCH_SHOW_RECYCLED|
5455 DSDB_SEARCH_SHOW_DELETED|
5456 DSDB_SEARCH_SHOW_EXTENDED_DN);
5457 if (ret != LDB_SUCCESS) {
5458 return ret;
5461 return ldb_next_request(ar->module, search_req);
5465 handle renames that come in over DRS replication
5467 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5468 struct ldb_message *msg,
5469 struct ldb_request *parent,
5470 bool *renamed)
5472 int ret;
5473 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5474 struct ldb_result *res;
5475 struct ldb_dn *conflict_dn;
5476 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5477 const struct ldb_val *omd_value;
5478 struct replPropertyMetaDataBlob omd, *rmd;
5479 enum ndr_err_code ndr_err;
5480 bool rename_incoming_record, rodc;
5481 struct replPropertyMetaData1 *rmd_name, *omd_name;
5482 struct ldb_dn *new_dn;
5483 struct GUID guid;
5485 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5486 ldb_dn_get_linearized(ar->search_msg->dn),
5487 ldb_dn_get_linearized(msg->dn)));
5490 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5491 DSDB_FLAG_NEXT_MODULE, ar->req);
5492 if (ret == LDB_SUCCESS) {
5493 talloc_free(tmp_ctx);
5494 *renamed = true;
5495 return ret;
5498 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5499 talloc_free(tmp_ctx);
5500 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5501 ldb_dn_get_linearized(ar->search_msg->dn),
5502 ldb_dn_get_linearized(msg->dn),
5503 ldb_errstring(ldb_module_get_ctx(ar->module)));
5504 return ret;
5507 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5508 if (ret != LDB_SUCCESS) {
5509 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5510 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5511 ldb_errstring(ldb_module_get_ctx(ar->module)));
5512 return LDB_ERR_OPERATIONS_ERROR;
5515 * we have a conflict, and need to decide if we will keep the
5516 * new record or the old record
5519 conflict_dn = msg->dn;
5521 if (rodc) {
5523 * We are on an RODC, or were a GC for this
5524 * partition, so we have to fail this until
5525 * someone who owns the partition sorts it
5526 * out
5528 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5529 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5530 " - We must fail the operation until a master for this partition resolves the conflict",
5531 ldb_dn_get_linearized(conflict_dn));
5532 goto failed;
5536 * first we need the replPropertyMetaData attribute from the
5537 * old record
5539 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
5540 attrs,
5541 DSDB_FLAG_NEXT_MODULE |
5542 DSDB_SEARCH_SHOW_DELETED |
5543 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5544 if (ret != LDB_SUCCESS) {
5545 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5546 ldb_dn_get_linearized(conflict_dn)));
5547 goto failed;
5550 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5551 if (omd_value == NULL) {
5552 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5553 ldb_dn_get_linearized(conflict_dn)));
5554 goto failed;
5557 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5558 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5559 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5560 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5561 ldb_dn_get_linearized(conflict_dn)));
5562 goto failed;
5565 rmd = ar->objs->objects[ar->index_current].meta_data;
5568 * we decide which is newer based on the RPMD on the name
5569 * attribute. See [MS-DRSR] ResolveNameConflict.
5571 * We expect omd_name to be present, as this is from a local
5572 * search, but while rmd_name should have been given to us by
5573 * the remote server, if it is missing we just prefer the
5574 * local name in
5575 * replmd_replPropertyMetaData1_new_should_be_taken()
5577 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5578 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5579 if (!omd_name) {
5580 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5581 ldb_dn_get_linearized(conflict_dn)));
5582 goto failed;
5586 * Should we preserve the current record, and so rename the
5587 * incoming record to be a conflict?
5589 rename_incoming_record =
5590 !replmd_replPropertyMetaData1_new_should_be_taken(
5591 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5592 omd_name, rmd_name);
5594 if (rename_incoming_record) {
5596 new_dn = replmd_conflict_dn(msg,
5597 ldb_module_get_ctx(ar->module),
5598 msg->dn,
5599 &ar->objs->objects[ar->index_current].object_guid);
5600 if (new_dn == NULL) {
5601 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5602 "Failed to form conflict DN for %s\n",
5603 ldb_dn_get_linearized(msg->dn));
5605 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5608 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
5609 DSDB_FLAG_NEXT_MODULE, ar->req);
5610 if (ret != LDB_SUCCESS) {
5611 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5612 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5613 ldb_dn_get_linearized(conflict_dn),
5614 ldb_dn_get_linearized(ar->search_msg->dn),
5615 ldb_dn_get_linearized(new_dn),
5616 ldb_errstring(ldb_module_get_ctx(ar->module)));
5617 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5620 msg->dn = new_dn;
5621 *renamed = true;
5622 return LDB_SUCCESS;
5625 /* we are renaming the existing record */
5627 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5628 if (GUID_all_zero(&guid)) {
5629 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5630 ldb_dn_get_linearized(conflict_dn)));
5631 goto failed;
5634 new_dn = replmd_conflict_dn(tmp_ctx,
5635 ldb_module_get_ctx(ar->module),
5636 conflict_dn, &guid);
5637 if (new_dn == NULL) {
5638 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5639 ldb_dn_get_linearized(conflict_dn)));
5640 goto failed;
5643 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5644 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5646 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5647 DSDB_FLAG_OWN_MODULE, ar->req);
5648 if (ret != LDB_SUCCESS) {
5649 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5650 ldb_dn_get_linearized(conflict_dn),
5651 ldb_dn_get_linearized(new_dn),
5652 ldb_errstring(ldb_module_get_ctx(ar->module))));
5653 goto failed;
5657 * now we need to ensure that the rename is seen as an
5658 * originating update. We do that with a modify.
5660 ret = replmd_name_modify(ar, ar->req, new_dn);
5661 if (ret != LDB_SUCCESS) {
5662 goto failed;
5665 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
5666 ldb_dn_get_linearized(ar->search_msg->dn),
5667 ldb_dn_get_linearized(msg->dn)));
5670 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5671 DSDB_FLAG_NEXT_MODULE, ar->req);
5672 if (ret != LDB_SUCCESS) {
5673 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
5674 ldb_dn_get_linearized(ar->search_msg->dn),
5675 ldb_dn_get_linearized(msg->dn),
5676 ldb_errstring(ldb_module_get_ctx(ar->module))));
5677 goto failed;
5679 failed:
5682 * On failure make the caller get the error
5683 * This means replication will stop with an error,
5684 * but there is not much else we can do. In the
5685 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
5686 * needed.
5689 talloc_free(tmp_ctx);
5690 return ret;
5694 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
5696 struct ldb_context *ldb;
5697 struct ldb_request *change_req;
5698 enum ndr_err_code ndr_err;
5699 struct ldb_message *msg;
5700 struct replPropertyMetaDataBlob *rmd;
5701 struct replPropertyMetaDataBlob omd;
5702 const struct ldb_val *omd_value;
5703 struct replPropertyMetaDataBlob nmd;
5704 struct ldb_val nmd_value;
5705 struct GUID remote_parent_guid;
5706 unsigned int i;
5707 uint32_t j,ni=0;
5708 unsigned int removed_attrs = 0;
5709 int ret;
5710 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
5711 bool isDeleted = false;
5712 bool local_isDeleted = false;
5713 bool remote_isDeleted = false;
5714 bool take_remote_isDeleted = false;
5715 bool sd_updated = false;
5716 bool renamed = false;
5717 bool is_schema_nc = false;
5718 NTSTATUS nt_status;
5719 const struct ldb_val *old_rdn, *new_rdn;
5720 struct replmd_private *replmd_private =
5721 talloc_get_type(ldb_module_get_private(ar->module),
5722 struct replmd_private);
5723 NTTIME now;
5724 time_t t = time(NULL);
5725 unix_to_nt_time(&now, t);
5727 ldb = ldb_module_get_ctx(ar->module);
5728 msg = ar->objs->objects[ar->index_current].msg;
5730 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5732 rmd = ar->objs->objects[ar->index_current].meta_data;
5733 ZERO_STRUCT(omd);
5734 omd.version = 1;
5736 /* find existing meta data */
5737 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5738 if (omd_value) {
5739 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5740 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5741 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5742 nt_status = ndr_map_error2ntstatus(ndr_err);
5743 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5746 if (omd.version != 1) {
5747 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5751 if (DEBUGLVL(8)) {
5752 struct GUID_txt_buf guid_txt;
5754 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5755 LDB_CHANGETYPE_MODIFY, msg);
5756 DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
5757 "%s\n"
5758 "%s\n",
5759 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5761 ndr_print_struct_string(s,
5762 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5763 "existing replPropertyMetaData",
5764 &omd),
5765 ndr_print_struct_string(s,
5766 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5767 "incoming replPropertyMetaData",
5768 rmd)));
5769 talloc_free(s);
5770 } else if (DEBUGLVL(4)) {
5771 struct GUID_txt_buf guid_txt;
5773 DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
5774 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5775 &guid_txt),
5776 ldb_dn_get_linearized(msg->dn)));
5779 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
5780 "isDeleted", false);
5781 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5782 "isDeleted", false);
5785 * Fill in the remote_parent_guid with the GUID or an all-zero
5786 * GUID.
5788 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
5789 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
5790 } else {
5791 remote_parent_guid = GUID_zero();
5795 * To ensure we follow a complex rename chain around, we have
5796 * to confirm that the DN is the same (mostly to confirm the
5797 * RDN) and the parentGUID is the same.
5799 * This ensures we keep things under the correct parent, which
5800 * replmd_replicated_handle_rename() will do.
5803 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
5804 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
5805 ret = LDB_SUCCESS;
5806 } else {
5808 * handle renames, even just by case that come in over
5809 * DRS. Changes in the parent DN don't hit us here,
5810 * because the search for a parent will clean up those
5811 * components.
5813 * We also have already filtered out the case where
5814 * the peer has an older name to what we have (see
5815 * replmd_replicated_apply_search_callback())
5817 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
5820 if (ret != LDB_SUCCESS) {
5821 ldb_debug(ldb, LDB_DEBUG_FATAL,
5822 "replmd_replicated_request rename %s => %s failed - %s\n",
5823 ldb_dn_get_linearized(ar->search_msg->dn),
5824 ldb_dn_get_linearized(msg->dn),
5825 ldb_errstring(ldb));
5826 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5829 if (renamed == true) {
5831 * Set the callback to one that will fix up the name
5832 * metadata on the new conflict DN
5834 callback = replmd_op_name_modify_callback;
5837 ZERO_STRUCT(nmd);
5838 nmd.version = 1;
5839 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
5840 nmd.ctr.ctr1.array = talloc_array(ar,
5841 struct replPropertyMetaData1,
5842 nmd.ctr.ctr1.count);
5843 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5845 /* first copy the old meta data */
5846 for (i=0; i < omd.ctr.ctr1.count; i++) {
5847 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
5848 ni++;
5851 ar->seq_num = 0;
5852 /* now merge in the new meta data */
5853 for (i=0; i < rmd->ctr.ctr1.count; i++) {
5854 bool found = false;
5856 for (j=0; j < ni; j++) {
5857 bool cmp;
5859 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
5860 continue;
5863 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
5864 ar->objs->dsdb_repl_flags,
5865 &nmd.ctr.ctr1.array[j],
5866 &rmd->ctr.ctr1.array[i]);
5867 if (cmp) {
5868 /* replace the entry */
5869 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
5870 if (ar->seq_num == 0) {
5871 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5872 if (ret != LDB_SUCCESS) {
5873 return replmd_replicated_request_error(ar, ret);
5876 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
5877 switch (nmd.ctr.ctr1.array[j].attid) {
5878 case DRSUAPI_ATTID_ntSecurityDescriptor:
5879 sd_updated = true;
5880 break;
5881 case DRSUAPI_ATTID_isDeleted:
5882 take_remote_isDeleted = true;
5883 break;
5884 default:
5885 break;
5887 found = true;
5888 break;
5891 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
5892 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
5893 msg->elements[i-removed_attrs].name,
5894 ldb_dn_get_linearized(msg->dn),
5895 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
5898 /* we don't want to apply this change so remove the attribute */
5899 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
5900 removed_attrs++;
5902 found = true;
5903 break;
5906 if (found) continue;
5908 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
5909 if (ar->seq_num == 0) {
5910 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5911 if (ret != LDB_SUCCESS) {
5912 return replmd_replicated_request_error(ar, ret);
5915 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
5916 switch (nmd.ctr.ctr1.array[ni].attid) {
5917 case DRSUAPI_ATTID_ntSecurityDescriptor:
5918 sd_updated = true;
5919 break;
5920 case DRSUAPI_ATTID_isDeleted:
5921 take_remote_isDeleted = true;
5922 break;
5923 default:
5924 break;
5926 ni++;
5930 * finally correct the size of the meta_data array
5932 nmd.ctr.ctr1.count = ni;
5934 new_rdn = ldb_dn_get_rdn_val(msg->dn);
5935 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
5937 if (renamed) {
5938 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
5939 &nmd, ar, now, is_schema_nc,
5940 false);
5941 if (ret != LDB_SUCCESS) {
5942 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5943 return replmd_replicated_request_error(ar, ret);
5947 * sort the new meta data array
5949 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
5950 if (ret != LDB_SUCCESS) {
5951 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5952 return ret;
5956 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
5957 * UpdateObject.
5959 * This also controls SD propagation below
5961 if (take_remote_isDeleted) {
5962 isDeleted = remote_isDeleted;
5963 } else {
5964 isDeleted = local_isDeleted;
5967 ar->isDeleted = isDeleted;
5970 * check if some replicated attributes left, otherwise skip the ldb_modify() call
5972 if (msg->num_elements == 0) {
5973 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
5974 ar->index_current);
5976 return replmd_replicated_apply_isDeleted(ar);
5979 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
5980 ar->index_current, msg->num_elements);
5982 if (renamed) {
5983 sd_updated = true;
5986 if (sd_updated && !isDeleted) {
5987 ret = dsdb_module_schedule_sd_propagation(ar->module,
5988 ar->objs->partition_dn,
5989 msg->dn, true);
5990 if (ret != LDB_SUCCESS) {
5991 return ldb_operr(ldb);
5995 /* create the meta data value */
5996 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
5997 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5998 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5999 nt_status = ndr_map_error2ntstatus(ndr_err);
6000 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6004 * when we know that we'll modify the record, add the whenChanged, uSNChanged
6005 * and replPopertyMetaData attributes
6007 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
6008 if (ret != LDB_SUCCESS) {
6009 return replmd_replicated_request_error(ar, ret);
6011 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
6012 if (ret != LDB_SUCCESS) {
6013 return replmd_replicated_request_error(ar, ret);
6015 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
6016 if (ret != LDB_SUCCESS) {
6017 return replmd_replicated_request_error(ar, ret);
6020 replmd_ldb_message_sort(msg, ar->schema);
6022 /* we want to replace the old values */
6023 for (i=0; i < msg->num_elements; i++) {
6024 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
6025 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
6026 if (msg->elements[i].num_values == 0) {
6027 ldb_asprintf_errstring(ldb, __location__
6028 ": objectClass removed on %s, aborting replication\n",
6029 ldb_dn_get_linearized(msg->dn));
6030 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
6035 if (DEBUGLVL(8)) {
6036 struct GUID_txt_buf guid_txt;
6038 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6039 LDB_CHANGETYPE_MODIFY,
6040 msg);
6041 DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
6042 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6043 &guid_txt),
6044 s));
6045 talloc_free(s);
6046 } else if (DEBUGLVL(4)) {
6047 struct GUID_txt_buf guid_txt;
6049 DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
6050 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6051 &guid_txt),
6052 ldb_dn_get_linearized(msg->dn)));
6055 ret = ldb_build_mod_req(&change_req,
6056 ldb,
6058 msg,
6059 ar->controls,
6061 callback,
6062 ar->req);
6063 LDB_REQ_SET_LOCATION(change_req);
6064 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6066 /* current partition control needed by "repmd_op_callback" */
6067 ret = ldb_request_add_control(change_req,
6068 DSDB_CONTROL_CURRENT_PARTITION_OID,
6069 false, NULL);
6070 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6072 return ldb_next_request(ar->module, change_req);
6075 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
6076 struct ldb_reply *ares)
6078 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6079 struct replmd_replicated_request);
6080 int ret;
6082 if (!ares) {
6083 return ldb_module_done(ar->req, NULL, NULL,
6084 LDB_ERR_OPERATIONS_ERROR);
6086 if (ares->error != LDB_SUCCESS &&
6087 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6088 return ldb_module_done(ar->req, ares->controls,
6089 ares->response, ares->error);
6092 switch (ares->type) {
6093 case LDB_REPLY_ENTRY:
6094 ar->search_msg = talloc_steal(ar, ares->message);
6095 break;
6097 case LDB_REPLY_REFERRAL:
6098 /* we ignore referrals */
6099 break;
6101 case LDB_REPLY_DONE:
6103 struct replPropertyMetaData1 *md_remote;
6104 struct replPropertyMetaData1 *md_local;
6106 struct replPropertyMetaDataBlob omd;
6107 const struct ldb_val *omd_value;
6108 struct replPropertyMetaDataBlob *rmd;
6109 struct ldb_message *msg;
6110 int instanceType;
6111 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
6112 ar->objs->objects[ar->index_current].last_known_parent = NULL;
6115 * This is the ADD case, find the appropriate parent,
6116 * as this object doesn't exist locally:
6118 if (ar->search_msg == NULL) {
6119 ret = replmd_replicated_apply_search_for_parent(ar);
6120 if (ret != LDB_SUCCESS) {
6121 return ldb_module_done(ar->req, NULL, NULL, ret);
6123 talloc_free(ares);
6124 return LDB_SUCCESS;
6128 * Otherwise, in the MERGE case, work out if we are
6129 * attempting a rename, and if so find the parent the
6130 * newly renamed object wants to belong under (which
6131 * may not be the parent in it's attached string DN
6133 rmd = ar->objs->objects[ar->index_current].meta_data;
6134 ZERO_STRUCT(omd);
6135 omd.version = 1;
6137 /* find existing meta data */
6138 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6139 if (omd_value) {
6140 enum ndr_err_code ndr_err;
6141 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6142 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6143 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6144 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6145 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6148 if (omd.version != 1) {
6149 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6153 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6155 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6156 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6157 && GUID_all_zero(&ar->local_parent_guid)) {
6158 DEBUG(0, ("Refusing to replicate new version of %s "
6159 "as local object has an all-zero parentGUID attribute, "
6160 "despite not being an NC root\n",
6161 ldb_dn_get_linearized(ar->search_msg->dn)));
6162 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6166 * now we need to check for double renames. We could have a
6167 * local rename pending which our replication partner hasn't
6168 * received yet. We choose which one wins by looking at the
6169 * attribute stamps on the two objects, the newer one wins.
6171 * This also simply applies the correct algorithms for
6172 * determining if a change was made to name at all, or
6173 * if the object has just been renamed under the same
6174 * parent.
6176 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6177 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6178 if (!md_local) {
6179 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6180 ldb_dn_get_linearized(ar->search_msg->dn)));
6181 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6185 * if there is no name attribute given then we have to assume the
6186 * object we've received has the older name
6188 if (replmd_replPropertyMetaData1_new_should_be_taken(
6189 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6190 md_local, md_remote)) {
6191 struct GUID_txt_buf p_guid_local;
6192 struct GUID_txt_buf p_guid_remote;
6193 msg = ar->objs->objects[ar->index_current].msg;
6195 /* Merge on the existing object, with rename */
6197 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6198 "as incoming object changing to %s under %s\n",
6199 ldb_dn_get_linearized(ar->search_msg->dn),
6200 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6201 ldb_dn_get_linearized(msg->dn),
6202 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6203 &p_guid_remote)));
6204 ret = replmd_replicated_apply_search_for_parent(ar);
6205 } else {
6206 struct GUID_txt_buf p_guid_local;
6207 struct GUID_txt_buf p_guid_remote;
6208 msg = ar->objs->objects[ar->index_current].msg;
6211 * Merge on the existing object, force no
6212 * rename (code below just to explain why in
6213 * the DEBUG() logs)
6216 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6217 ldb_dn_get_linearized(msg->dn)) == 0) {
6218 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6219 GUID_equal(&ar->local_parent_guid,
6220 ar->objs->objects[ar->index_current].parent_guid)
6221 == false) {
6222 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6223 "despite incoming object changing parent to %s\n",
6224 ldb_dn_get_linearized(ar->search_msg->dn),
6225 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6226 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6227 &p_guid_remote)));
6229 } else {
6230 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6231 " and rejecting older rename to %s under %s\n",
6232 ldb_dn_get_linearized(ar->search_msg->dn),
6233 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6234 ldb_dn_get_linearized(msg->dn),
6235 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6236 &p_guid_remote)));
6239 * This assignment ensures that the strcmp()
6240 * and GUID_equal() calls in
6241 * replmd_replicated_apply_merge() avoids the
6242 * rename call
6244 ar->objs->objects[ar->index_current].parent_guid =
6245 &ar->local_parent_guid;
6247 msg->dn = ar->search_msg->dn;
6248 ret = replmd_replicated_apply_merge(ar);
6250 if (ret != LDB_SUCCESS) {
6251 return ldb_module_done(ar->req, NULL, NULL, ret);
6256 talloc_free(ares);
6257 return LDB_SUCCESS;
6261 * Stores the linked attributes received in the replication chunk - these get
6262 * applied at the end of the transaction. We also check that each linked
6263 * attribute is valid, i.e. source and target objects are known.
6265 static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
6267 int ret = LDB_SUCCESS;
6268 uint32_t i;
6269 struct ldb_module *module = ar->module;
6270 struct replmd_private *replmd_private =
6271 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6272 struct ldb_context *ldb;
6274 ldb = ldb_module_get_ctx(module);
6276 DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
6278 /* save away the linked attributes for the end of the transaction */
6279 for (i = 0; i < ar->objs->linked_attributes_count; i++) {
6280 struct la_entry *la_entry;
6282 if (replmd_private->la_ctx == NULL) {
6283 replmd_private->la_ctx = talloc_new(replmd_private);
6285 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6286 if (la_entry == NULL) {
6287 ldb_oom(ldb);
6288 return LDB_ERR_OPERATIONS_ERROR;
6290 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
6291 if (la_entry->la == NULL) {
6292 talloc_free(la_entry);
6293 ldb_oom(ldb);
6294 return LDB_ERR_OPERATIONS_ERROR;
6296 *la_entry->la = ar->objs->linked_attributes[i];
6297 la_entry->dsdb_repl_flags = ar->objs->dsdb_repl_flags;
6299 /* we need to steal the non-scalars so they stay
6300 around until the end of the transaction */
6301 talloc_steal(la_entry->la, la_entry->la->identifier);
6302 talloc_steal(la_entry->la, la_entry->la->value.blob);
6304 ret = replmd_verify_linked_attribute(ar, la_entry);
6306 if (ret != LDB_SUCCESS) {
6307 break;
6310 DLIST_ADD(replmd_private->la_list, la_entry);
6313 return ret;
6316 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6318 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6320 struct ldb_context *ldb;
6321 int ret;
6322 char *tmp_str;
6323 char *filter;
6324 struct ldb_request *search_req;
6325 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6326 "parentGUID", "instanceType",
6327 "replPropertyMetaData", "nTSecurityDescriptor",
6328 "isDeleted", NULL };
6329 struct GUID_txt_buf guid_str_buf;
6331 if (ar->index_current >= ar->objs->num_objects) {
6334 * Now that we've applied all the objects, check the new linked
6335 * attributes and store them (we apply them in .prepare_commit)
6337 ret = replmd_store_linked_attributes(ar);
6339 if (ret != LDB_SUCCESS) {
6340 return ret;
6343 /* done applying objects, move on to the next stage */
6344 return replmd_replicated_uptodate_vector(ar);
6347 ldb = ldb_module_get_ctx(ar->module);
6348 ar->search_msg = NULL;
6349 ar->isDeleted = false;
6351 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6352 &guid_str_buf);
6354 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6355 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6357 ret = ldb_build_search_req(&search_req,
6358 ldb,
6360 ar->objs->partition_dn,
6361 LDB_SCOPE_SUBTREE,
6362 filter,
6363 attrs,
6364 NULL,
6366 replmd_replicated_apply_search_callback,
6367 ar->req);
6368 LDB_REQ_SET_LOCATION(search_req);
6370 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
6372 if (ret != LDB_SUCCESS) {
6373 return ret;
6376 return ldb_next_request(ar->module, search_req);
6380 * This is essentially a wrapper for replmd_replicated_apply_next()
6382 * This is needed to ensure that both codepaths call this handler.
6384 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6386 struct ldb_dn *deleted_objects_dn;
6387 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6388 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
6389 &deleted_objects_dn);
6390 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
6392 * Do a delete here again, so that if there is
6393 * anything local that conflicts with this
6394 * object being deleted, it is removed. This
6395 * includes links. See MS-DRSR 4.1.10.6.9
6396 * UpdateObject.
6398 * If the object is already deleted, and there
6399 * is no more work required, it doesn't do
6400 * anything.
6403 /* This has been updated to point to the DN we eventually did the modify on */
6405 struct ldb_request *del_req;
6406 struct ldb_result *res;
6408 TALLOC_CTX *tmp_ctx = talloc_new(ar);
6409 if (!tmp_ctx) {
6410 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6411 return ret;
6414 res = talloc_zero(tmp_ctx, struct ldb_result);
6415 if (!res) {
6416 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6417 talloc_free(tmp_ctx);
6418 return ret;
6421 /* Build a delete request, which hopefully will artually turn into nothing */
6422 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
6423 msg->dn,
6424 NULL,
6425 res,
6426 ldb_modify_default_callback,
6427 ar->req);
6428 LDB_REQ_SET_LOCATION(del_req);
6429 if (ret != LDB_SUCCESS) {
6430 talloc_free(tmp_ctx);
6431 return ret;
6435 * This is the guts of the call, call back
6436 * into our delete code, but setting the
6437 * re_delete flag so we delete anything that
6438 * shouldn't be there on a deleted or recycled
6439 * object
6441 ret = replmd_delete_internals(ar->module, del_req, true);
6442 if (ret == LDB_SUCCESS) {
6443 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
6446 talloc_free(tmp_ctx);
6447 if (ret != LDB_SUCCESS) {
6448 return ret;
6452 ar->index_current++;
6453 return replmd_replicated_apply_next(ar);
6456 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
6457 struct ldb_reply *ares)
6459 struct ldb_context *ldb;
6460 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6461 struct replmd_replicated_request);
6462 ldb = ldb_module_get_ctx(ar->module);
6464 if (!ares) {
6465 return ldb_module_done(ar->req, NULL, NULL,
6466 LDB_ERR_OPERATIONS_ERROR);
6468 if (ares->error != LDB_SUCCESS) {
6469 return ldb_module_done(ar->req, ares->controls,
6470 ares->response, ares->error);
6473 if (ares->type != LDB_REPLY_DONE) {
6474 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
6475 return ldb_module_done(ar->req, NULL, NULL,
6476 LDB_ERR_OPERATIONS_ERROR);
6479 talloc_free(ares);
6481 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6484 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
6486 struct ldb_context *ldb;
6487 struct ldb_request *change_req;
6488 enum ndr_err_code ndr_err;
6489 struct ldb_message *msg;
6490 struct replUpToDateVectorBlob ouv;
6491 const struct ldb_val *ouv_value;
6492 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
6493 struct replUpToDateVectorBlob nuv;
6494 struct ldb_val nuv_value;
6495 struct ldb_message_element *nuv_el = NULL;
6496 struct ldb_message_element *orf_el = NULL;
6497 struct repsFromToBlob nrf;
6498 struct ldb_val *nrf_value = NULL;
6499 struct ldb_message_element *nrf_el = NULL;
6500 unsigned int i;
6501 uint32_t j,ni=0;
6502 bool found = false;
6503 time_t t = time(NULL);
6504 NTTIME now;
6505 int ret;
6506 uint32_t instanceType;
6508 ldb = ldb_module_get_ctx(ar->module);
6509 ruv = ar->objs->uptodateness_vector;
6510 ZERO_STRUCT(ouv);
6511 ouv.version = 2;
6512 ZERO_STRUCT(nuv);
6513 nuv.version = 2;
6515 unix_to_nt_time(&now, t);
6517 if (ar->search_msg == NULL) {
6518 /* this happens for a REPL_OBJ call where we are
6519 creating the target object by replicating it. The
6520 subdomain join code does this for the partition DN
6522 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
6523 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6526 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
6527 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
6528 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
6529 ldb_dn_get_linearized(ar->search_msg->dn)));
6530 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6534 * first create the new replUpToDateVector
6536 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
6537 if (ouv_value) {
6538 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
6539 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
6540 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6541 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6542 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6545 if (ouv.version != 2) {
6546 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6551 * the new uptodateness vector will at least
6552 * contain 1 entry, one for the source_dsa
6554 * plus optional values from our old vector and the one from the source_dsa
6556 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
6557 if (ruv) nuv.ctr.ctr2.count += ruv->count;
6558 nuv.ctr.ctr2.cursors = talloc_array(ar,
6559 struct drsuapi_DsReplicaCursor2,
6560 nuv.ctr.ctr2.count);
6561 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6563 /* first copy the old vector */
6564 for (i=0; i < ouv.ctr.ctr2.count; i++) {
6565 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
6566 ni++;
6569 /* merge in the source_dsa vector is available */
6570 for (i=0; (ruv && i < ruv->count); i++) {
6571 found = false;
6573 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6574 &ar->our_invocation_id)) {
6575 continue;
6578 for (j=0; j < ni; j++) {
6579 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6580 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
6581 continue;
6584 found = true;
6586 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
6587 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
6589 break;
6592 if (found) continue;
6594 /* if it's not there yet, add it */
6595 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
6596 ni++;
6600 * finally correct the size of the cursors array
6602 nuv.ctr.ctr2.count = ni;
6605 * sort the cursors
6607 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
6610 * create the change ldb_message
6612 msg = ldb_msg_new(ar);
6613 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6614 msg->dn = ar->search_msg->dn;
6616 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
6617 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
6618 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6619 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6620 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6622 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
6623 if (ret != LDB_SUCCESS) {
6624 return replmd_replicated_request_error(ar, ret);
6626 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
6629 * now create the new repsFrom value from the given repsFromTo1 structure
6631 ZERO_STRUCT(nrf);
6632 nrf.version = 1;
6633 nrf.ctr.ctr1 = *ar->objs->source_dsa;
6634 nrf.ctr.ctr1.last_attempt = now;
6635 nrf.ctr.ctr1.last_success = now;
6636 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
6639 * first see if we already have a repsFrom value for the current source dsa
6640 * if so we'll later replace this value
6642 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
6643 if (orf_el) {
6644 for (i=0; i < orf_el->num_values; i++) {
6645 struct repsFromToBlob *trf;
6647 trf = talloc(ar, struct repsFromToBlob);
6648 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6650 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
6651 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
6652 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6653 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6654 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6657 if (trf->version != 1) {
6658 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6662 * we compare the source dsa objectGUID not the invocation_id
6663 * because we want only one repsFrom value per source dsa
6664 * and when the invocation_id of the source dsa has changed we don't need
6665 * the old repsFrom with the old invocation_id
6667 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
6668 &ar->objs->source_dsa->source_dsa_obj_guid)) {
6669 talloc_free(trf);
6670 continue;
6673 talloc_free(trf);
6674 nrf_value = &orf_el->values[i];
6675 break;
6679 * copy over all old values to the new ldb_message
6681 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
6682 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6683 *nrf_el = *orf_el;
6687 * if we haven't found an old repsFrom value for the current source dsa
6688 * we'll add a new value
6690 if (!nrf_value) {
6691 struct ldb_val zero_value;
6692 ZERO_STRUCT(zero_value);
6693 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
6694 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6696 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
6699 /* we now fill the value which is already attached to ldb_message */
6700 ndr_err = ndr_push_struct_blob(nrf_value, msg,
6701 &nrf,
6702 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
6703 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6704 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6705 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6709 * the ldb_message_element for the attribute, has all the old values and the new one
6710 * so we'll replace the whole attribute with all values
6712 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
6714 if (CHECK_DEBUGLVL(4)) {
6715 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6716 LDB_CHANGETYPE_MODIFY,
6717 msg);
6718 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
6719 talloc_free(s);
6722 /* prepare the ldb_modify() request */
6723 ret = ldb_build_mod_req(&change_req,
6724 ldb,
6726 msg,
6727 ar->controls,
6729 replmd_replicated_uptodate_modify_callback,
6730 ar->req);
6731 LDB_REQ_SET_LOCATION(change_req);
6732 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6734 return ldb_next_request(ar->module, change_req);
6737 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
6738 struct ldb_reply *ares)
6740 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6741 struct replmd_replicated_request);
6742 int ret;
6744 if (!ares) {
6745 return ldb_module_done(ar->req, NULL, NULL,
6746 LDB_ERR_OPERATIONS_ERROR);
6748 if (ares->error != LDB_SUCCESS &&
6749 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6750 return ldb_module_done(ar->req, ares->controls,
6751 ares->response, ares->error);
6754 switch (ares->type) {
6755 case LDB_REPLY_ENTRY:
6756 ar->search_msg = talloc_steal(ar, ares->message);
6757 break;
6759 case LDB_REPLY_REFERRAL:
6760 /* we ignore referrals */
6761 break;
6763 case LDB_REPLY_DONE:
6764 ret = replmd_replicated_uptodate_modify(ar);
6765 if (ret != LDB_SUCCESS) {
6766 return ldb_module_done(ar->req, NULL, NULL, ret);
6770 talloc_free(ares);
6771 return LDB_SUCCESS;
6775 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
6777 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
6778 struct replmd_private *replmd_private =
6779 talloc_get_type_abort(ldb_module_get_private(ar->module),
6780 struct replmd_private);
6781 int ret;
6782 static const char *attrs[] = {
6783 "replUpToDateVector",
6784 "repsFrom",
6785 "instanceType",
6786 NULL
6788 struct ldb_request *search_req;
6790 ar->search_msg = NULL;
6793 * Let the caller know that we did an originating updates
6795 ar->objs->originating_updates = replmd_private->originating_updates;
6797 ret = ldb_build_search_req(&search_req,
6798 ldb,
6800 ar->objs->partition_dn,
6801 LDB_SCOPE_BASE,
6802 "(objectClass=*)",
6803 attrs,
6804 NULL,
6806 replmd_replicated_uptodate_search_callback,
6807 ar->req);
6808 LDB_REQ_SET_LOCATION(search_req);
6809 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6811 return ldb_next_request(ar->module, search_req);
6816 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
6818 struct ldb_context *ldb;
6819 struct dsdb_extended_replicated_objects *objs;
6820 struct replmd_replicated_request *ar;
6821 struct ldb_control **ctrls;
6822 int ret;
6824 ldb = ldb_module_get_ctx(module);
6826 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
6828 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
6829 if (!objs) {
6830 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
6831 return LDB_ERR_PROTOCOL_ERROR;
6834 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
6835 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
6836 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
6837 return LDB_ERR_PROTOCOL_ERROR;
6840 ar = replmd_ctx_init(module, req);
6841 if (!ar)
6842 return LDB_ERR_OPERATIONS_ERROR;
6844 /* Set the flags to have the replmd_op_callback run over the full set of objects */
6845 ar->apply_mode = true;
6846 ar->objs = objs;
6847 ar->schema = dsdb_get_schema(ldb, ar);
6848 if (!ar->schema) {
6849 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
6850 talloc_free(ar);
6851 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
6852 return LDB_ERR_CONSTRAINT_VIOLATION;
6855 ctrls = req->controls;
6857 if (req->controls) {
6858 req->controls = talloc_memdup(ar, req->controls,
6859 talloc_get_size(req->controls));
6860 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6863 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
6864 if (ret != LDB_SUCCESS) {
6865 return ret;
6868 /* If this change contained linked attributes in the body
6869 * (rather than in the links section) we need to update
6870 * backlinks in linked_attributes */
6871 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
6872 if (ret != LDB_SUCCESS) {
6873 return ret;
6876 ar->controls = req->controls;
6877 req->controls = ctrls;
6879 return replmd_replicated_apply_next(ar);
6883 * Checks how to handle an missing target - either we need to fail the
6884 * replication and retry with GET_TGT, ignore the link and continue, or try to
6885 * add a partial link to an unknown target.
6887 static int replmd_allow_missing_target(struct ldb_module *module,
6888 TALLOC_CTX *mem_ctx,
6889 struct ldb_dn *target_dn,
6890 struct ldb_dn *source_dn,
6891 bool is_obj_commit,
6892 struct GUID *guid,
6893 uint32_t dsdb_repl_flags,
6894 bool *ignore_link,
6895 const char * missing_str)
6897 struct ldb_context *ldb = ldb_module_get_ctx(module);
6898 bool is_in_same_nc;
6901 * we may not be able to resolve link targets properly when
6902 * dealing with subsets of objects, e.g. the source is a
6903 * critical object and the target isn't
6905 * TODO:
6906 * When we implement Trusted Domains we need to consider
6907 * whether they get treated as an incomplete replica here or not
6909 if (dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
6912 * Ignore the link. We don't increase the highwater-mark in
6913 * the object subset cases, so subsequent replications should
6914 * resolve any missing links
6916 DEBUG(2, ("%s target %s linked from %s\n", missing_str,
6917 ldb_dn_get_linearized(target_dn),
6918 ldb_dn_get_linearized(source_dn)));
6919 *ignore_link = true;
6920 return LDB_SUCCESS;
6923 if (dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
6926 * target should already be up-to-date so there's no point in
6927 * retrying. This could be due to bad timing, or if a target
6928 * on a one-way link was deleted. We ignore the link rather
6929 * than failing the replication cycle completely
6931 *ignore_link = true;
6932 DBG_WARNING("%s is %s but up to date. Ignoring link from %s\n",
6933 ldb_dn_get_linearized(target_dn), missing_str,
6934 ldb_dn_get_linearized(source_dn));
6935 return LDB_SUCCESS;
6938 is_in_same_nc = dsdb_objects_have_same_nc(ldb,
6939 mem_ctx,
6940 source_dn,
6941 target_dn);
6942 if (is_in_same_nc) {
6943 /* fail the replication and retry with GET_TGT */
6944 ldb_asprintf_errstring(ldb, "%s target %s GUID %s linked from %s\n",
6945 missing_str,
6946 ldb_dn_get_linearized(target_dn),
6947 GUID_string(mem_ctx, guid),
6948 ldb_dn_get_linearized(source_dn));
6949 return LDB_ERR_NO_SUCH_OBJECT;
6953 * The target of the cross-partition link is missing. Continue
6954 * and try to at least add the forward-link. This isn't great,
6955 * but a partial link can be fixed by dbcheck, so it's better
6956 * than dropping the link completely.
6958 *ignore_link = false;
6960 if (is_obj_commit) {
6963 * Only log this when we're actually committing the objects.
6964 * This avoids spurious logs, i.e. if we're just verifying the
6965 * received link during a join.
6967 DBG_WARNING("%s cross-partition target %s linked from %s\n",
6968 missing_str, ldb_dn_get_linearized(target_dn),
6969 ldb_dn_get_linearized(source_dn));
6972 return LDB_SUCCESS;
6976 * Checks that the target object for a linked attribute exists.
6977 * @param guid returns the target object's GUID (is returned)if it exists)
6978 * @param ignore_link set to true if the linked attribute should be ignored
6979 * (i.e. the target doesn't exist, but that it's OK to skip the link)
6981 static int replmd_check_target_exists(struct ldb_module *module,
6982 struct dsdb_dn *dsdb_dn,
6983 struct la_entry *la_entry,
6984 struct ldb_dn *source_dn,
6985 bool is_obj_commit,
6986 struct GUID *guid,
6987 bool *ignore_link)
6989 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
6990 struct ldb_context *ldb = ldb_module_get_ctx(module);
6991 struct ldb_result *target_res;
6992 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
6993 const char *attrs[] = { "isDeleted", "isRecycled", NULL };
6994 NTSTATUS ntstatus;
6995 int ret;
6996 enum deletion_state target_deletion_state = OBJECT_REMOVED;
6997 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
6999 *ignore_link = false;
7000 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
7002 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
7005 * This strange behaviour (allowing a NULL/missing
7006 * GUID) originally comes from:
7008 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
7009 * Author: Andrew Tridgell <tridge@samba.org>
7010 * Date: Mon Dec 21 21:21:55 2009 +1100
7012 * s4-drs: cope better with NULL GUIDS from DRS
7014 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
7015 * need to match by DN if possible when seeing if we should update an
7016 * existing link.
7018 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
7020 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
7021 dsdb_dn->dn, attrs,
7022 DSDB_FLAG_NEXT_MODULE |
7023 DSDB_SEARCH_SHOW_RECYCLED |
7024 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7025 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7026 NULL);
7027 } else if (!NT_STATUS_IS_OK(ntstatus)) {
7028 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
7029 la->attid,
7030 ldb_dn_get_linearized(dsdb_dn->dn),
7031 ldb_dn_get_linearized(source_dn));
7032 talloc_free(tmp_ctx);
7033 return LDB_ERR_OPERATIONS_ERROR;
7034 } else {
7035 ret = dsdb_module_search(module, tmp_ctx, &target_res,
7036 NULL, LDB_SCOPE_SUBTREE,
7037 attrs,
7038 DSDB_FLAG_NEXT_MODULE |
7039 DSDB_SEARCH_SHOW_RECYCLED |
7040 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7041 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7042 NULL,
7043 "objectGUID=%s",
7044 GUID_string(tmp_ctx, guid));
7047 if (ret != LDB_SUCCESS) {
7048 ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
7049 GUID_string(tmp_ctx, guid),
7050 ldb_errstring(ldb));
7051 talloc_free(tmp_ctx);
7052 return ret;
7055 if (target_res->count == 0) {
7058 * target object is unknown. Check whether to ignore the link,
7059 * fail the replication, or add a partial link
7061 ret = replmd_allow_missing_target(module, tmp_ctx, dsdb_dn->dn,
7062 source_dn, is_obj_commit, guid,
7063 la_entry->dsdb_repl_flags,
7064 ignore_link, "Unknown");
7066 } else if (target_res->count != 1) {
7067 ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
7068 GUID_string(tmp_ctx, guid));
7069 ret = LDB_ERR_OPERATIONS_ERROR;
7070 } else {
7071 struct ldb_message *target_msg = target_res->msgs[0];
7073 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
7075 /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
7076 replmd_deletion_state(module, target_msg,
7077 &target_deletion_state, NULL);
7080 * Check for deleted objects as per MS-DRSR 4.1.10.6.14
7081 * ProcessLinkValue(). Link updates should not be sent for
7082 * recycled and tombstone objects (deleting the links should
7083 * happen when we delete the object). This probably means our
7084 * copy of the target object isn't up to date.
7086 if (target_deletion_state >= OBJECT_RECYCLED) {
7089 * target object is deleted. Check whether to ignore the
7090 * link, fail the replication, or add a partial link
7092 ret = replmd_allow_missing_target(module, tmp_ctx,
7093 dsdb_dn->dn, source_dn,
7094 is_obj_commit, guid,
7095 la_entry->dsdb_repl_flags,
7096 ignore_link, "Deleted");
7100 talloc_free(tmp_ctx);
7101 return ret;
7105 * Extracts the key details about the source/target object for a
7106 * linked-attribute entry.
7107 * This returns the following details:
7108 * @param ret_attr the schema details for the linked attribute
7109 * @param source_msg the search result for the source object
7110 * @param target_dsdb_dn the unpacked DN info for the target object
7112 static int replmd_extract_la_entry_details(struct ldb_module *module,
7113 struct la_entry *la_entry,
7114 TALLOC_CTX *mem_ctx,
7115 const struct dsdb_attribute **ret_attr,
7116 struct ldb_message **source_msg,
7117 struct dsdb_dn **target_dsdb_dn)
7119 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7120 struct ldb_context *ldb = ldb_module_get_ctx(module);
7121 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7122 int ret;
7123 const struct dsdb_attribute *attr;
7124 WERROR status;
7125 struct ldb_result *res;
7126 const char *attrs[4];
7129 linked_attributes[0]:
7130 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
7131 identifier : *
7132 identifier: struct drsuapi_DsReplicaObjectIdentifier
7133 __ndr_size : 0x0000003a (58)
7134 __ndr_size_sid : 0x00000000 (0)
7135 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7136 sid : S-0-0
7137 __ndr_size_dn : 0x00000000 (0)
7138 dn : ''
7139 attid : DRSUAPI_ATTID_member (0x1F)
7140 value: struct drsuapi_DsAttributeValue
7141 __ndr_size : 0x0000007e (126)
7142 blob : *
7143 blob : DATA_BLOB length=126
7144 flags : 0x00000001 (1)
7145 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7146 originating_add_time : Wed Sep 2 22:20:01 2009 EST
7147 meta_data: struct drsuapi_DsReplicaMetaData
7148 version : 0x00000015 (21)
7149 originating_change_time : Wed Sep 2 23:39:07 2009 EST
7150 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7151 originating_usn : 0x000000000001e19c (123292)
7153 (for cases where the link is to a normal DN)
7154 &target: struct drsuapi_DsReplicaObjectIdentifier3
7155 __ndr_size : 0x0000007e (126)
7156 __ndr_size_sid : 0x0000001c (28)
7157 guid : 7639e594-db75-4086-b0d4-67890ae46031
7158 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7159 __ndr_size_dn : 0x00000022 (34)
7160 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7163 /* find the attribute being modified */
7164 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
7165 if (attr == NULL) {
7166 struct GUID_txt_buf guid_str;
7167 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7168 la->attid,
7169 GUID_buf_string(&la->identifier->guid,
7170 &guid_str));
7171 return LDB_ERR_OPERATIONS_ERROR;
7174 attrs[0] = attr->lDAPDisplayName;
7175 attrs[1] = "isDeleted";
7176 attrs[2] = "isRecycled";
7177 attrs[3] = NULL;
7180 * get the existing message from the db for the object with
7181 * this GUID, returning attribute being modified. We will then
7182 * use this msg as the basis for a modify call
7184 ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
7185 DSDB_FLAG_NEXT_MODULE |
7186 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7187 DSDB_SEARCH_SHOW_RECYCLED |
7188 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
7189 DSDB_SEARCH_REVEAL_INTERNALS,
7190 NULL,
7191 "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
7192 if (ret != LDB_SUCCESS) {
7193 return ret;
7195 if (res->count != 1) {
7196 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
7197 GUID_string(mem_ctx, &la->identifier->guid));
7198 return LDB_ERR_NO_SUCH_OBJECT;
7201 *source_msg = res->msgs[0];
7203 /* the value blob for the attribute holds the target object DN */
7204 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx, la->value.blob, target_dsdb_dn);
7205 if (!W_ERROR_IS_OK(status)) {
7206 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7207 attr->lDAPDisplayName,
7208 ldb_dn_get_linearized(res->msgs[0]->dn),
7209 win_errstr(status));
7210 return LDB_ERR_OPERATIONS_ERROR;
7213 *ret_attr = attr;
7215 return LDB_SUCCESS;
7219 * Verifies the source and target objects are known for a linked attribute
7221 static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
7222 struct la_entry *la)
7224 int ret = LDB_SUCCESS;
7225 TALLOC_CTX *tmp_ctx = talloc_new(la);
7226 struct ldb_module *module = ar->module;
7227 struct ldb_message *src_msg;
7228 const struct dsdb_attribute *attr;
7229 struct dsdb_dn *tgt_dsdb_dn;
7230 struct GUID guid = GUID_zero();
7231 bool dummy;
7233 ret = replmd_extract_la_entry_details(module, la, tmp_ctx, &attr,
7234 &src_msg, &tgt_dsdb_dn);
7237 * When we fail to find the source object, the error code we pass
7238 * back here is really important. It flags back to the callers to
7239 * retry this request with DRSUAPI_DRS_GET_ANC. This case should
7240 * never happen if we're replicating from a Samba DC, but it is
7241 * needed to talk to a Windows DC
7243 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7244 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT);
7247 if (ret != LDB_SUCCESS) {
7248 talloc_free(tmp_ctx);
7249 return ret;
7253 * We can skip the target object checks if we're only syncing critical
7254 * objects, or we know the target is up-to-date. If either case, we
7255 * still continue even if the target doesn't exist
7257 if ((la->dsdb_repl_flags & (DSDB_REPL_FLAG_OBJECT_SUBSET |
7258 DSDB_REPL_FLAG_TARGETS_UPTODATE)) == 0) {
7260 ret = replmd_check_target_exists(module, tgt_dsdb_dn, la,
7261 src_msg->dn, false, &guid,
7262 &dummy);
7266 * When we fail to find the target object, the error code we pass
7267 * back here is really important. It flags back to the callers to
7268 * retry this request with DRSUAPI_DRS_GET_TGT
7270 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7271 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
7274 talloc_free(tmp_ctx);
7275 return ret;
7279 * Finds the current active Parsed-DN value for a single-valued linked
7280 * attribute, if one exists.
7281 * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
7282 * @returns LDB_SUCCESS (regardless of whether a match was found), unless
7283 * an error occurred
7285 static int replmd_get_active_singleval_link(struct ldb_module *module,
7286 TALLOC_CTX *mem_ctx,
7287 struct parsed_dn pdn_list[],
7288 unsigned int count,
7289 const struct dsdb_attribute *attr,
7290 struct parsed_dn **ret_pdn)
7292 unsigned int i;
7294 *ret_pdn = NULL;
7296 if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE)) {
7298 /* nothing to do for multi-valued linked attributes */
7299 return LDB_SUCCESS;
7302 for (i = 0; i < count; i++) {
7303 int ret = LDB_SUCCESS;
7304 struct parsed_dn *pdn = &pdn_list[i];
7306 /* skip any inactive links */
7307 if (dsdb_dn_is_deleted_val(pdn->v)) {
7308 continue;
7311 /* we've found an active value for this attribute */
7312 *ret_pdn = pdn;
7314 if (pdn->dsdb_dn == NULL) {
7315 struct ldb_context *ldb = ldb_module_get_ctx(module);
7317 ret = really_parse_trusted_dn(mem_ctx, ldb, pdn,
7318 attr->syntax->ldap_oid);
7321 return ret;
7324 /* no active link found */
7325 return LDB_SUCCESS;
7329 * @returns true if the replication linked attribute info is newer than we
7330 * already have in our DB
7331 * @param pdn the existing linked attribute info in our DB
7332 * @param la the new linked attribute info received during replication
7334 static bool replmd_link_update_is_newer(struct parsed_dn *pdn,
7335 struct drsuapi_DsReplicaLinkedAttribute *la)
7337 /* see if this update is newer than what we have already */
7338 struct GUID invocation_id = GUID_zero();
7339 uint32_t version = 0;
7340 NTTIME change_time = 0;
7342 if (pdn == NULL) {
7344 /* no existing info so update is newer */
7345 return true;
7348 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
7349 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
7350 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
7352 return replmd_update_is_newer(&invocation_id,
7353 &la->meta_data.originating_invocation_id,
7354 version,
7355 la->meta_data.version,
7356 change_time,
7357 la->meta_data.originating_change_time);
7361 * Marks an existing linked attribute value as deleted in the DB
7362 * @param pdn the parsed-DN of the target-value to delete
7364 static int replmd_delete_link_value(struct ldb_module *module,
7365 struct replmd_private *replmd_private,
7366 TALLOC_CTX *mem_ctx,
7367 struct ldb_dn *src_obj_dn,
7368 const struct dsdb_schema *schema,
7369 const struct dsdb_attribute *attr,
7370 uint64_t seq_num,
7371 bool is_active,
7372 struct GUID *target_guid,
7373 struct dsdb_dn *target_dsdb_dn,
7374 struct ldb_val *output_val)
7376 struct ldb_context *ldb = ldb_module_get_ctx(module);
7377 time_t t;
7378 NTTIME now;
7379 const struct GUID *invocation_id = NULL;
7380 int ret;
7382 t = time(NULL);
7383 unix_to_nt_time(&now, t);
7385 invocation_id = samdb_ntds_invocation_id(ldb);
7386 if (invocation_id == NULL) {
7387 return LDB_ERR_OPERATIONS_ERROR;
7390 /* if the existing link is active, remove its backlink */
7391 if (is_active) {
7393 ret = replmd_add_backlink(module, replmd_private, schema,
7394 src_obj_dn, target_guid, false,
7395 attr, NULL);
7396 if (ret != LDB_SUCCESS) {
7397 return ret;
7401 /* mark the existing value as deleted */
7402 ret = replmd_update_la_val(mem_ctx, output_val, target_dsdb_dn,
7403 target_dsdb_dn, invocation_id, seq_num,
7404 seq_num, now, true);
7405 return ret;
7409 * Checks for a conflict in single-valued link attributes, and tries to
7410 * resolve the problem if possible.
7412 * Single-valued links should only ever have one active value. If we already
7413 * have an active link value, and during replication we receive an active link
7414 * value for a different target DN, then we need to resolve this inconsistency
7415 * and determine which value should be active. If the received info is better/
7416 * newer than the existing link attribute, then we need to set our existing
7417 * link as deleted. If the received info is worse/older, then we should continue
7418 * to add it, but set it as an inactive link.
7420 * Note that this is a corner-case that is unlikely to happen (but if it does
7421 * happen, we don't want it to break replication completely).
7423 * @param pdn_being_modified the parsed DN corresponding to the received link
7424 * target (note this is NULL if the link does not already exist in our DB)
7425 * @param pdn_list all the source object's Parsed-DNs for this attribute, i.e.
7426 * any existing active or inactive values for the attribute in our DB.
7427 * @param dsdb_dn the target DN for the received link attribute
7428 * @param add_as_inactive gets set to true if the received link is worse than
7429 * the existing link - it should still be added, but as an inactive link.
7431 static int replmd_check_singleval_la_conflict(struct ldb_module *module,
7432 struct replmd_private *replmd_private,
7433 TALLOC_CTX *mem_ctx,
7434 struct ldb_dn *src_obj_dn,
7435 struct drsuapi_DsReplicaLinkedAttribute *la,
7436 struct dsdb_dn *dsdb_dn,
7437 struct parsed_dn *pdn_being_modified,
7438 struct parsed_dn *pdn_list,
7439 struct ldb_message_element *old_el,
7440 const struct dsdb_schema *schema,
7441 const struct dsdb_attribute *attr,
7442 uint64_t seq_num,
7443 bool *add_as_inactive)
7445 struct parsed_dn *active_pdn = NULL;
7446 bool update_is_newer = false;
7447 int ret;
7450 * check if there's a conflict for single-valued links, i.e. an active
7451 * linked attribute already exists, but it has a different target value
7453 ret = replmd_get_active_singleval_link(module, mem_ctx, pdn_list,
7454 old_el->num_values, attr,
7455 &active_pdn);
7457 if (ret != LDB_SUCCESS) {
7458 return ret;
7462 * If no active value exists (or the received info is for the currently
7463 * active value), then no conflict exists
7465 if (active_pdn == NULL || active_pdn == pdn_being_modified) {
7466 return LDB_SUCCESS;
7469 DBG_WARNING("Link conflict for %s attribute on %s\n",
7470 attr->lDAPDisplayName, ldb_dn_get_linearized(src_obj_dn));
7472 /* Work out how to resolve the conflict based on which info is better */
7473 update_is_newer = replmd_link_update_is_newer(active_pdn, la);
7475 if (update_is_newer) {
7476 DBG_WARNING("Using received value %s, over existing target %s\n",
7477 ldb_dn_get_linearized(dsdb_dn->dn),
7478 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn));
7481 * Delete our existing active link. The received info will then
7482 * be added (through normal link processing) as the active value
7484 ret = replmd_delete_link_value(module, replmd_private, old_el,
7485 src_obj_dn, schema, attr,
7486 seq_num, true, &active_pdn->guid,
7487 active_pdn->dsdb_dn,
7488 active_pdn->v);
7490 if (ret != LDB_SUCCESS) {
7491 return ret;
7493 } else {
7494 DBG_WARNING("Using existing target %s, over received value %s\n",
7495 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn),
7496 ldb_dn_get_linearized(dsdb_dn->dn));
7499 * we want to keep our existing active link and add the
7500 * received link as inactive
7502 *add_as_inactive = true;
7505 return LDB_SUCCESS;
7509 process one linked attribute structure
7511 static int replmd_process_linked_attribute(struct ldb_module *module,
7512 struct replmd_private *replmd_private,
7513 struct la_entry *la_entry,
7514 struct ldb_request *parent)
7516 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7517 struct ldb_context *ldb = ldb_module_get_ctx(module);
7518 struct ldb_message *msg;
7519 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7520 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
7521 int ret;
7522 const struct dsdb_attribute *attr;
7523 struct dsdb_dn *dsdb_dn;
7524 uint64_t seq_num = 0;
7525 struct ldb_message_element *old_el;
7526 time_t t = time(NULL);
7527 struct parsed_dn *pdn_list, *pdn, *next;
7528 struct GUID guid = GUID_zero();
7529 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
7530 bool ignore_link;
7531 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
7532 struct dsdb_dn *old_dsdb_dn = NULL;
7533 struct ldb_val *val_to_update = NULL;
7534 bool add_as_inactive = false;
7537 * get the attribute being modified, the search result for the source object,
7538 * and the target object's DN details
7540 ret = replmd_extract_la_entry_details(module, la_entry, tmp_ctx, &attr,
7541 &msg, &dsdb_dn);
7543 if (ret != LDB_SUCCESS) {
7544 talloc_free(tmp_ctx);
7545 return ret;
7549 * Check for deleted objects per MS-DRSR 4.1.10.6.14
7550 * ProcessLinkValue, because link updates are not applied to
7551 * recycled and tombstone objects. We don't have to delete
7552 * any existing link, that should have happened when the
7553 * object deletion was replicated or initiated.
7555 replmd_deletion_state(module, msg, &deletion_state, NULL);
7557 if (deletion_state >= OBJECT_RECYCLED) {
7558 talloc_free(tmp_ctx);
7559 return LDB_SUCCESS;
7562 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7563 if (old_el == NULL) {
7564 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
7565 if (ret != LDB_SUCCESS) {
7566 ldb_module_oom(module);
7567 talloc_free(tmp_ctx);
7568 return LDB_ERR_OPERATIONS_ERROR;
7570 } else {
7571 old_el->flags = LDB_FLAG_MOD_REPLACE;
7574 /* parse the existing links */
7575 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx, old_el, &pdn_list,
7576 attr->syntax->ldap_oid, parent);
7578 if (ret != LDB_SUCCESS) {
7579 talloc_free(tmp_ctx);
7580 return ret;
7583 ret = replmd_check_target_exists(module, dsdb_dn, la_entry, msg->dn,
7584 true, &guid, &ignore_link);
7586 if (ret != LDB_SUCCESS) {
7587 talloc_free(tmp_ctx);
7588 return ret;
7592 * there are some cases where the target object doesn't exist, but it's
7593 * OK to ignore the linked attribute
7595 if (ignore_link) {
7596 talloc_free(tmp_ctx);
7597 return ret;
7600 /* see if this link already exists */
7601 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
7602 &guid,
7603 dsdb_dn->dn,
7604 dsdb_dn->extra_part, 0,
7605 &pdn, &next,
7606 attr->syntax->ldap_oid,
7607 true);
7608 if (ret != LDB_SUCCESS) {
7609 talloc_free(tmp_ctx);
7610 return ret;
7613 if (!replmd_link_update_is_newer(pdn, la)) {
7614 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
7615 old_el->name, ldb_dn_get_linearized(msg->dn),
7616 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
7617 talloc_free(tmp_ctx);
7618 return LDB_SUCCESS;
7621 /* get a seq_num for this change */
7622 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
7623 if (ret != LDB_SUCCESS) {
7624 talloc_free(tmp_ctx);
7625 return ret;
7629 * check for single-valued link conflicts, i.e. an active linked
7630 * attribute already exists, but it has a different target value
7632 if (active) {
7633 ret = replmd_check_singleval_la_conflict(module, replmd_private,
7634 tmp_ctx, msg->dn, la,
7635 dsdb_dn, pdn, pdn_list,
7636 old_el, schema, attr,
7637 seq_num,
7638 &add_as_inactive);
7639 if (ret != LDB_SUCCESS) {
7640 talloc_free(tmp_ctx);
7641 return ret;
7645 if (pdn != NULL) {
7646 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
7648 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
7649 /* remove the existing backlink */
7650 ret = replmd_add_backlink(module, replmd_private,
7651 schema,
7652 msg->dn,
7653 &pdn->guid, false, attr,
7654 parent);
7655 if (ret != LDB_SUCCESS) {
7656 talloc_free(tmp_ctx);
7657 return ret;
7661 val_to_update = pdn->v;
7662 old_dsdb_dn = pdn->dsdb_dn;
7664 } else {
7665 unsigned offset;
7668 * We know where the new one needs to be, from the *next
7669 * pointer into pdn_list.
7671 if (next == NULL) {
7672 offset = old_el->num_values;
7673 } else {
7674 if (next->dsdb_dn == NULL) {
7675 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
7676 attr->syntax->ldap_oid);
7677 if (ret != LDB_SUCCESS) {
7678 return ret;
7681 offset = next - pdn_list;
7682 if (offset > old_el->num_values) {
7683 talloc_free(tmp_ctx);
7684 return LDB_ERR_OPERATIONS_ERROR;
7688 old_el->values = talloc_realloc(msg->elements, old_el->values,
7689 struct ldb_val, old_el->num_values+1);
7690 if (!old_el->values) {
7691 ldb_module_oom(module);
7692 return LDB_ERR_OPERATIONS_ERROR;
7695 if (offset != old_el->num_values) {
7696 memmove(&old_el->values[offset + 1], &old_el->values[offset],
7697 (old_el->num_values - offset) * sizeof(old_el->values[0]));
7700 old_el->num_values++;
7702 val_to_update = &old_el->values[offset];
7703 old_dsdb_dn = NULL;
7706 /* set the link attribute's value to the info that was received */
7707 ret = replmd_set_la_val(tmp_ctx, val_to_update, dsdb_dn, old_dsdb_dn,
7708 &la->meta_data.originating_invocation_id,
7709 la->meta_data.originating_usn, seq_num,
7710 la->meta_data.originating_change_time,
7711 la->meta_data.version,
7712 !active);
7713 if (ret != LDB_SUCCESS) {
7714 talloc_free(tmp_ctx);
7715 return ret;
7718 if (add_as_inactive) {
7720 /* Set the new link as inactive/deleted to avoid conflicts */
7721 ret = replmd_delete_link_value(module, replmd_private, old_el,
7722 msg->dn, schema, attr, seq_num,
7723 false, &guid, dsdb_dn,
7724 val_to_update);
7726 if (ret != LDB_SUCCESS) {
7727 talloc_free(tmp_ctx);
7728 return ret;
7731 } else if (active) {
7733 /* if the new link is active, then add the new backlink */
7734 ret = replmd_add_backlink(module, replmd_private,
7735 schema,
7736 msg->dn,
7737 &guid, true, attr,
7738 parent);
7739 if (ret != LDB_SUCCESS) {
7740 talloc_free(tmp_ctx);
7741 return ret;
7745 /* we only change whenChanged and uSNChanged if the seq_num
7746 has changed */
7747 ret = add_time_element(msg, "whenChanged", t);
7748 if (ret != LDB_SUCCESS) {
7749 talloc_free(tmp_ctx);
7750 ldb_operr(ldb);
7751 return ret;
7754 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
7755 if (ret != LDB_SUCCESS) {
7756 talloc_free(tmp_ctx);
7757 ldb_operr(ldb);
7758 return ret;
7761 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7762 if (old_el == NULL) {
7763 talloc_free(tmp_ctx);
7764 return ldb_operr(ldb);
7767 ret = dsdb_check_single_valued_link(attr, old_el);
7768 if (ret != LDB_SUCCESS) {
7769 talloc_free(tmp_ctx);
7770 return ret;
7773 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
7775 ret = linked_attr_modify(module, msg, parent);
7776 if (ret != LDB_SUCCESS) {
7777 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
7778 ldb_errstring(ldb),
7779 ldb_ldif_message_redacted_string(ldb,
7780 tmp_ctx,
7781 LDB_CHANGETYPE_MODIFY,
7782 msg));
7783 talloc_free(tmp_ctx);
7784 return ret;
7787 talloc_free(tmp_ctx);
7789 return ret;
7792 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
7794 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
7795 return replmd_extended_replicated_objects(module, req);
7798 return ldb_next_request(module, req);
7803 we hook into the transaction operations to allow us to
7804 perform the linked attribute updates at the end of the whole
7805 transaction. This allows a forward linked attribute to be created
7806 before the object is created. During a vampire, w2k8 sends us linked
7807 attributes before the objects they are part of.
7809 static int replmd_start_transaction(struct ldb_module *module)
7811 /* create our private structure for this transaction */
7812 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
7813 struct replmd_private);
7814 replmd_txn_cleanup(replmd_private);
7816 /* free any leftover mod_usn records from cancelled
7817 transactions */
7818 while (replmd_private->ncs) {
7819 struct nc_entry *e = replmd_private->ncs;
7820 DLIST_REMOVE(replmd_private->ncs, e);
7821 talloc_free(e);
7824 replmd_private->originating_updates = false;
7826 return ldb_next_start_trans(module);
7830 on prepare commit we loop over our queued la_context structures and
7831 apply each of them
7833 static int replmd_prepare_commit(struct ldb_module *module)
7835 struct replmd_private *replmd_private =
7836 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
7837 struct la_entry *la, *prev;
7838 int ret;
7841 * Walk the list of linked attributes from DRS replication.
7843 * We walk backwards, to do the first entry first, as we
7844 * added the entries with DLIST_ADD() which puts them at the
7845 * start of the list
7847 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
7848 prev = DLIST_PREV(la);
7849 DLIST_REMOVE(replmd_private->la_list, la);
7850 ret = replmd_process_linked_attribute(module, replmd_private,
7851 la, NULL);
7852 if (ret != LDB_SUCCESS) {
7853 replmd_txn_cleanup(replmd_private);
7854 return ret;
7858 replmd_txn_cleanup(replmd_private);
7860 /* possibly change @REPLCHANGED */
7861 ret = replmd_notify_store(module, NULL);
7862 if (ret != LDB_SUCCESS) {
7863 return ret;
7866 return ldb_next_prepare_commit(module);
7869 static int replmd_del_transaction(struct ldb_module *module)
7871 struct replmd_private *replmd_private =
7872 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
7873 replmd_txn_cleanup(replmd_private);
7875 return ldb_next_del_trans(module);
7879 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
7880 .name = "repl_meta_data",
7881 .init_context = replmd_init,
7882 .add = replmd_add,
7883 .modify = replmd_modify,
7884 .rename = replmd_rename,
7885 .del = replmd_delete,
7886 .extended = replmd_extended,
7887 .start_transaction = replmd_start_transaction,
7888 .prepare_commit = replmd_prepare_commit,
7889 .del_transaction = replmd_del_transaction,
7892 int ldb_repl_meta_data_module_init(const char *version)
7894 LDB_MODULE_CHECK_VERSION(version);
7895 return ldb_register_module(&ldb_repl_meta_data_module_ops);