s4:repl_meta_data: pass down struct replmd_replicated_request to replmd_modify_handle...
[Samba.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blobe5baefc4cb431a59d0035322075295269c567a68
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 char *dn_str = NULL;
2043 dn_str = ldb_dn_get_extended_linearized(mem_ctx,
2044 (dn), 1);
2045 ldb_asprintf_errstring(ldb,
2046 "Unable to find GUID for DN %s\n",
2047 dn_str);
2048 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
2049 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
2050 ldb_attr_cmp(el->name, "member") == 0) {
2051 return LDB_ERR_UNWILLING_TO_PERFORM;
2053 return ret;
2055 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2056 if (ret != LDB_SUCCESS) {
2057 return ret;
2059 } else if (!NT_STATUS_IS_OK(status)) {
2060 return LDB_ERR_OPERATIONS_ERROR;
2062 if (i > 0 && values_are_sorted) {
2063 int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
2064 if (cmp < 0) {
2065 values_are_sorted = false;
2068 /* keep a pointer to the original ldb_val */
2069 p->v = v;
2071 if (! values_are_sorted) {
2072 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
2074 return LDB_SUCCESS;
2078 * Get a series of trusted message element values. The result is sorted by
2079 * GUID, even though the GUIDs might not be known. That works because we trust
2080 * the database to give us the elements like that if the
2081 * replmd_private->sorted_links flag is set.
2083 * We also ensure that the links are in the Functional Level 2003
2084 * linked attributes format.
2086 static int get_parsed_dns_trusted(struct ldb_module *module,
2087 struct replmd_private *replmd_private,
2088 TALLOC_CTX *mem_ctx,
2089 struct ldb_message_element *el,
2090 struct parsed_dn **pdn,
2091 const char *ldap_oid,
2092 struct ldb_request *parent)
2094 unsigned int i;
2095 int ret;
2096 if (el == NULL) {
2097 *pdn = NULL;
2098 return LDB_SUCCESS;
2101 if (!replmd_private->sorted_links) {
2102 /* We need to sort the list. This is the slow old path we want
2103 to avoid.
2105 ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2106 parent);
2107 if (ret != LDB_SUCCESS) {
2108 return ret;
2110 } else {
2111 /* Here we get a list of 'struct parsed_dns' without the parsing */
2112 *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
2113 el->num_values);
2114 if (!*pdn) {
2115 ldb_module_oom(module);
2116 return LDB_ERR_OPERATIONS_ERROR;
2119 for (i = 0; i < el->num_values; i++) {
2120 (*pdn)[i].v = &el->values[i];
2125 * This upgrades links to FL2003 style, and sorts the result
2126 * if that was needed.
2128 * TODO: Add a database feature that asserts we have no FL2000
2129 * style links to avoid this check or add a feature that
2130 * uses a similar check to find sorted/unsorted links
2131 * for an on-the-fly upgrade.
2134 ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
2135 *pdn, el->num_values,
2137 ldap_oid);
2138 if (ret != LDB_SUCCESS) {
2139 return ret;
2142 return LDB_SUCCESS;
2146 Return LDB_SUCCESS if a parsed_dn list contains no duplicate values,
2147 otherwise an error code. For compatibility the error code differs depending
2148 on whether or not the attribute is "member".
2150 As always, the parsed_dn list is assumed to be sorted.
2152 static int check_parsed_dn_duplicates(struct ldb_module *module,
2153 struct ldb_message_element *el,
2154 struct parsed_dn *pdn)
2156 unsigned int i;
2157 struct ldb_context *ldb = ldb_module_get_ctx(module);
2159 for (i = 1; i < el->num_values; i++) {
2160 struct parsed_dn *p = &pdn[i];
2161 if (parsed_dn_compare(p, &pdn[i - 1]) == 0) {
2162 ldb_asprintf_errstring(ldb,
2163 "Linked attribute %s has "
2164 "multiple identical values",
2165 el->name);
2166 if (ldb_attr_cmp(el->name, "member") == 0) {
2167 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2168 } else {
2169 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2173 return LDB_SUCCESS;
2177 build a new extended DN, including all meta data fields
2179 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2180 RMD_ADDTIME = originating_add_time
2181 RMD_INVOCID = originating_invocation_id
2182 RMD_CHANGETIME = originating_change_time
2183 RMD_ORIGINATING_USN = originating_usn
2184 RMD_LOCAL_USN = local_usn
2185 RMD_VERSION = version
2187 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v,
2188 struct dsdb_dn *dsdb_dn,
2189 const struct GUID *invocation_id,
2190 uint64_t local_usn, NTTIME nttime)
2192 return replmd_set_la_val(mem_ctx, v, dsdb_dn, NULL, invocation_id,
2193 local_usn, local_usn, nttime,
2194 RMD_VERSION_INITIAL, false);
2197 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2198 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2199 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
2200 bool deleted);
2203 check if any links need upgrading from w2k format
2205 static int replmd_check_upgrade_links(struct ldb_context *ldb,
2206 struct parsed_dn *dns, uint32_t count,
2207 struct ldb_message_element *el,
2208 const char *ldap_oid)
2210 uint32_t i;
2211 const struct GUID *invocation_id = NULL;
2212 for (i=0; i<count; i++) {
2213 NTSTATUS status;
2214 uint32_t version;
2215 int ret;
2216 if (dns[i].dsdb_dn == NULL) {
2217 ret = really_parse_trusted_dn(dns, ldb, &dns[i],
2218 ldap_oid);
2219 if (ret != LDB_SUCCESS) {
2220 return LDB_ERR_INVALID_DN_SYNTAX;
2224 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
2225 &version, "RMD_VERSION");
2226 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2228 * We optimistically assume they are all the same; if
2229 * the first one is fixed, they are all fixed.
2231 * If the first one was *not* fixed and we find a
2232 * later one that is, that is an occasion to shout
2233 * with DEBUG(0).
2235 if (i == 0) {
2236 return LDB_SUCCESS;
2238 DEBUG(0, ("Mixed w2k and fixed format "
2239 "linked attributes\n"));
2240 continue;
2243 if (invocation_id == NULL) {
2244 invocation_id = samdb_ntds_invocation_id(ldb);
2245 if (invocation_id == NULL) {
2246 return LDB_ERR_OPERATIONS_ERROR;
2251 /* it's an old one that needs upgrading */
2252 ret = replmd_update_la_val(el->values, dns[i].v,
2253 dns[i].dsdb_dn, dns[i].dsdb_dn,
2254 invocation_id, 1, 1, 0, false);
2255 if (ret != LDB_SUCCESS) {
2256 return ret;
2261 * This sort() is critical for the operation of
2262 * get_parsed_dns_trusted() because callers of this function
2263 * expect a sorted list, and FL2000 style links are not
2264 * sorted. In particular, as well as the upgrade case,
2265 * get_parsed_dns_trusted() is called from
2266 * replmd_delete_remove_link() even in FL2000 mode
2268 * We do not normally pay the cost of the qsort() due to the
2269 * early return in the RMD_VERSION found case.
2271 TYPESAFE_QSORT(dns, count, parsed_dn_compare);
2272 return LDB_SUCCESS;
2276 Sets the value for a linked attribute, including all meta data fields
2278 see replmd_build_la_val for value names
2280 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2281 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2282 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2283 uint32_t version, bool deleted)
2285 struct ldb_dn *dn = dsdb_dn->dn;
2286 const char *tstring, *usn_string, *flags_string;
2287 struct ldb_val tval;
2288 struct ldb_val iid;
2289 struct ldb_val usnv, local_usnv;
2290 struct ldb_val vers, flagsv;
2291 const struct ldb_val *old_addtime = NULL;
2292 NTSTATUS status;
2293 int ret;
2294 const char *dnstring;
2295 char *vstring;
2296 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2298 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2299 if (!tstring) {
2300 return LDB_ERR_OPERATIONS_ERROR;
2302 tval = data_blob_string_const(tstring);
2304 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2305 if (!usn_string) {
2306 return LDB_ERR_OPERATIONS_ERROR;
2308 usnv = data_blob_string_const(usn_string);
2310 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2311 if (!usn_string) {
2312 return LDB_ERR_OPERATIONS_ERROR;
2314 local_usnv = data_blob_string_const(usn_string);
2316 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2317 if (!NT_STATUS_IS_OK(status)) {
2318 return LDB_ERR_OPERATIONS_ERROR;
2321 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2322 if (!flags_string) {
2323 return LDB_ERR_OPERATIONS_ERROR;
2325 flagsv = data_blob_string_const(flags_string);
2327 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2328 if (ret != LDB_SUCCESS) return ret;
2330 /* get the ADDTIME from the original */
2331 if (old_dsdb_dn != NULL) {
2332 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn,
2333 "RMD_ADDTIME");
2335 if (old_addtime == NULL) {
2336 old_addtime = &tval;
2338 if (dsdb_dn != old_dsdb_dn ||
2339 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2340 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2341 if (ret != LDB_SUCCESS) return ret;
2344 /* use our invocation id */
2345 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2346 if (ret != LDB_SUCCESS) return ret;
2348 /* changetime is the current time */
2349 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2350 if (ret != LDB_SUCCESS) return ret;
2352 /* update the USN */
2353 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2354 if (ret != LDB_SUCCESS) return ret;
2356 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2357 if (ret != LDB_SUCCESS) return ret;
2359 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2360 vers = data_blob_string_const(vstring);
2361 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2362 if (ret != LDB_SUCCESS) return ret;
2364 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2365 if (dnstring == NULL) {
2366 return LDB_ERR_OPERATIONS_ERROR;
2368 *v = data_blob_string_const(dnstring);
2370 return LDB_SUCCESS;
2374 * Updates the value for a linked attribute, including all meta data fields
2376 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2377 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2378 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2379 bool deleted)
2381 uint32_t old_version;
2382 uint32_t version = RMD_VERSION_INITIAL;
2383 NTSTATUS status;
2386 * We're updating the linked attribute locally, so increase the version
2387 * by 1 so that other DCs will see the change when it gets replicated out
2389 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version,
2390 "RMD_VERSION");
2392 if (NT_STATUS_IS_OK(status)) {
2393 version = old_version + 1;
2396 return replmd_set_la_val(mem_ctx, v, dsdb_dn, old_dsdb_dn, invocation_id,
2397 usn, local_usn, nttime, version, deleted);
2401 handle adding a linked attribute
2403 static int replmd_modify_la_add(struct ldb_module *module,
2404 struct replmd_private *replmd_private,
2405 const struct dsdb_schema *schema,
2406 struct ldb_message *msg,
2407 struct ldb_message_element *el,
2408 struct ldb_message_element *old_el,
2409 const struct dsdb_attribute *schema_attr,
2410 uint64_t seq_num,
2411 time_t t,
2412 struct ldb_dn *msg_dn,
2413 struct ldb_request *parent)
2415 unsigned int i, j;
2416 struct parsed_dn *dns, *old_dns;
2417 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2418 int ret;
2419 struct ldb_val *new_values = NULL;
2420 unsigned old_num_values = old_el ? old_el->num_values : 0;
2421 unsigned num_values = 0;
2422 unsigned max_num_values;
2423 const struct GUID *invocation_id;
2424 struct ldb_context *ldb = ldb_module_get_ctx(module);
2425 NTTIME now;
2426 unix_to_nt_time(&now, t);
2428 invocation_id = samdb_ntds_invocation_id(ldb);
2429 if (!invocation_id) {
2430 talloc_free(tmp_ctx);
2431 return LDB_ERR_OPERATIONS_ERROR;
2434 /* get the DNs to be added, fully parsed.
2436 * We need full parsing because they came off the wire and we don't
2437 * trust them, besides which we need their details to know where to put
2438 * them.
2440 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2441 schema_attr->syntax->ldap_oid, parent);
2442 if (ret != LDB_SUCCESS) {
2443 talloc_free(tmp_ctx);
2444 return ret;
2447 /* get the existing DNs, lazily parsed */
2448 ret = get_parsed_dns_trusted(module, replmd_private,
2449 tmp_ctx, old_el, &old_dns,
2450 schema_attr->syntax->ldap_oid, parent);
2452 if (ret != LDB_SUCCESS) {
2453 talloc_free(tmp_ctx);
2454 return ret;
2457 max_num_values = old_num_values + el->num_values;
2458 if (max_num_values < old_num_values) {
2459 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2460 "old values: %u, new values: %u, sum: %u",
2461 old_num_values, el->num_values, max_num_values));
2462 talloc_free(tmp_ctx);
2463 return LDB_ERR_OPERATIONS_ERROR;
2466 new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2468 if (new_values == NULL) {
2469 ldb_module_oom(module);
2470 talloc_free(tmp_ctx);
2471 return LDB_ERR_OPERATIONS_ERROR;
2475 * For each new value, find where it would go in the list. If there is
2476 * a matching GUID there, we update the existing value; otherwise we
2477 * put it in place.
2479 j = 0;
2480 for (i = 0; i < el->num_values; i++) {
2481 struct parsed_dn *exact;
2482 struct parsed_dn *next;
2483 unsigned offset;
2484 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2485 &dns[i].guid,
2486 dns[i].dsdb_dn->dn,
2487 dns[i].dsdb_dn->extra_part, 0,
2488 &exact, &next,
2489 schema_attr->syntax->ldap_oid,
2490 true);
2491 if (err != LDB_SUCCESS) {
2492 talloc_free(tmp_ctx);
2493 return err;
2496 if (exact != NULL) {
2498 * We are trying to add one that exists, which is only
2499 * allowed if it was previously deleted.
2501 * When we do undelete a link we change it in place.
2502 * It will be copied across into the right spot in due
2503 * course.
2505 uint32_t rmd_flags;
2506 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2508 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2509 struct GUID_txt_buf guid_str;
2510 ldb_asprintf_errstring(ldb,
2511 "Attribute %s already "
2512 "exists for target GUID %s",
2513 el->name,
2514 GUID_buf_string(&exact->guid,
2515 &guid_str));
2516 talloc_free(tmp_ctx);
2517 /* error codes for 'member' need to be
2518 special cased */
2519 if (ldb_attr_cmp(el->name, "member") == 0) {
2520 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2521 } else {
2522 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2526 ret = replmd_update_la_val(new_values, exact->v,
2527 dns[i].dsdb_dn,
2528 exact->dsdb_dn,
2529 invocation_id, seq_num,
2530 seq_num, now, false);
2531 if (ret != LDB_SUCCESS) {
2532 talloc_free(tmp_ctx);
2533 return ret;
2536 ret = replmd_add_backlink(module, replmd_private,
2537 schema,
2538 msg_dn,
2539 &dns[i].guid,
2540 true,
2541 schema_attr,
2542 parent);
2543 if (ret != LDB_SUCCESS) {
2544 talloc_free(tmp_ctx);
2545 return ret;
2547 continue;
2550 * Here we don't have an exact match.
2552 * If next is NULL, this one goes beyond the end of the
2553 * existing list, so we need to add all of those ones first.
2555 * If next is not NULL, we need to add all the ones before
2556 * next.
2558 if (next == NULL) {
2559 offset = old_num_values;
2560 } else {
2561 /* next should have been parsed, but let's make sure */
2562 if (next->dsdb_dn == NULL) {
2563 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2564 schema_attr->syntax->ldap_oid);
2565 if (ret != LDB_SUCCESS) {
2566 return ret;
2569 offset = MIN(next - old_dns, old_num_values);
2572 /* put all the old ones before next on the list */
2573 for (; j < offset; j++) {
2574 new_values[num_values] = *old_dns[j].v;
2575 num_values++;
2578 ret = replmd_add_backlink(module, replmd_private,
2579 schema, msg_dn,
2580 &dns[i].guid,
2581 true, schema_attr,
2582 parent);
2583 /* Make the new linked attribute ldb_val. */
2584 ret = replmd_build_la_val(new_values, &new_values[num_values],
2585 dns[i].dsdb_dn, invocation_id,
2586 seq_num, now);
2587 if (ret != LDB_SUCCESS) {
2588 talloc_free(tmp_ctx);
2589 return ret;
2591 num_values++;
2592 if (ret != LDB_SUCCESS) {
2593 talloc_free(tmp_ctx);
2594 return ret;
2597 /* copy the rest of the old ones (if any) */
2598 for (; j < old_num_values; j++) {
2599 new_values[num_values] = *old_dns[j].v;
2600 num_values++;
2603 talloc_steal(msg->elements, new_values);
2604 if (old_el != NULL) {
2605 talloc_steal(msg->elements, old_el->values);
2607 el->values = new_values;
2608 el->num_values = num_values;
2610 talloc_free(tmp_ctx);
2612 /* we now tell the backend to replace all existing values
2613 with the one we have constructed */
2614 el->flags = LDB_FLAG_MOD_REPLACE;
2616 return LDB_SUCCESS;
2621 handle deleting all active linked attributes
2623 static int replmd_modify_la_delete(struct ldb_module *module,
2624 struct replmd_private *replmd_private,
2625 const struct dsdb_schema *schema,
2626 struct ldb_message *msg,
2627 struct ldb_message_element *el,
2628 struct ldb_message_element *old_el,
2629 const struct dsdb_attribute *schema_attr,
2630 uint64_t seq_num,
2631 time_t t,
2632 struct ldb_dn *msg_dn,
2633 struct ldb_request *parent)
2635 unsigned int i;
2636 struct parsed_dn *dns, *old_dns;
2637 TALLOC_CTX *tmp_ctx = NULL;
2638 int ret;
2639 struct ldb_context *ldb = ldb_module_get_ctx(module);
2640 struct ldb_control *vanish_links_ctrl = NULL;
2641 bool vanish_links = false;
2642 unsigned int num_to_delete = el->num_values;
2643 uint32_t rmd_flags;
2644 const struct GUID *invocation_id;
2645 NTTIME now;
2647 unix_to_nt_time(&now, t);
2649 invocation_id = samdb_ntds_invocation_id(ldb);
2650 if (!invocation_id) {
2651 return LDB_ERR_OPERATIONS_ERROR;
2654 if (old_el == NULL || old_el->num_values == 0) {
2655 /* there is nothing to delete... */
2656 if (num_to_delete == 0) {
2657 /* and we're deleting nothing, so that's OK */
2658 return LDB_SUCCESS;
2660 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2663 tmp_ctx = talloc_new(msg);
2664 if (tmp_ctx == NULL) {
2665 return LDB_ERR_OPERATIONS_ERROR;
2668 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2669 schema_attr->syntax->ldap_oid, parent);
2670 if (ret != LDB_SUCCESS) {
2671 talloc_free(tmp_ctx);
2672 return ret;
2675 ret = get_parsed_dns_trusted(module, replmd_private,
2676 tmp_ctx, old_el, &old_dns,
2677 schema_attr->syntax->ldap_oid, parent);
2679 if (ret != LDB_SUCCESS) {
2680 talloc_free(tmp_ctx);
2681 return ret;
2684 if (parent) {
2685 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2686 if (vanish_links_ctrl) {
2687 vanish_links = true;
2688 vanish_links_ctrl->critical = false;
2692 /* we empty out el->values here to avoid damage if we return early. */
2693 el->num_values = 0;
2694 el->values = NULL;
2697 * If vanish links is set, we are actually removing members of
2698 * old_el->values; otherwise we are just marking them deleted.
2700 * There is a special case when no values are given: we remove them
2701 * all. When we have the vanish_links control we just have to remove
2702 * the backlinks and change our element to replace the existing values
2703 * with the empty list.
2706 if (num_to_delete == 0) {
2707 for (i = 0; i < old_el->num_values; i++) {
2708 struct parsed_dn *p = &old_dns[i];
2709 if (p->dsdb_dn == NULL) {
2710 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2711 schema_attr->syntax->ldap_oid);
2712 if (ret != LDB_SUCCESS) {
2713 return ret;
2716 ret = replmd_add_backlink(module, replmd_private,
2717 schema, msg_dn, &p->guid,
2718 false, schema_attr,
2719 parent);
2720 if (ret != LDB_SUCCESS) {
2721 talloc_free(tmp_ctx);
2722 return ret;
2724 if (vanish_links) {
2725 continue;
2728 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2729 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2730 continue;
2733 ret = replmd_update_la_val(old_el->values, p->v,
2734 p->dsdb_dn, p->dsdb_dn,
2735 invocation_id, seq_num,
2736 seq_num, now, true);
2737 if (ret != LDB_SUCCESS) {
2738 talloc_free(tmp_ctx);
2739 return ret;
2743 if (vanish_links) {
2744 el->flags = LDB_FLAG_MOD_REPLACE;
2745 talloc_free(tmp_ctx);
2746 return LDB_SUCCESS;
2751 for (i = 0; i < num_to_delete; i++) {
2752 struct parsed_dn *p = &dns[i];
2753 struct parsed_dn *exact = NULL;
2754 struct parsed_dn *next = NULL;
2755 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2756 &p->guid,
2757 NULL,
2758 p->dsdb_dn->extra_part, 0,
2759 &exact, &next,
2760 schema_attr->syntax->ldap_oid,
2761 true);
2762 if (ret != LDB_SUCCESS) {
2763 talloc_free(tmp_ctx);
2764 return ret;
2766 if (exact == NULL) {
2767 struct GUID_txt_buf buf;
2768 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2769 "exist for target GUID %s",
2770 el->name,
2771 GUID_buf_string(&p->guid, &buf));
2772 if (ldb_attr_cmp(el->name, "member") == 0) {
2773 talloc_free(tmp_ctx);
2774 return LDB_ERR_UNWILLING_TO_PERFORM;
2775 } else {
2776 talloc_free(tmp_ctx);
2777 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2781 if (vanish_links) {
2782 if (CHECK_DEBUGLVL(5)) {
2783 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2784 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2785 struct GUID_txt_buf buf;
2786 const char *guid_str = \
2787 GUID_buf_string(&p->guid, &buf);
2788 DEBUG(5, ("Deleting deleted linked "
2789 "attribute %s to %s, because "
2790 "vanish_links control is set\n",
2791 el->name, guid_str));
2795 /* remove the backlink */
2796 ret = replmd_add_backlink(module,
2797 replmd_private,
2798 schema,
2799 msg_dn,
2800 &p->guid,
2801 false, schema_attr,
2802 parent);
2803 if (ret != LDB_SUCCESS) {
2804 talloc_free(tmp_ctx);
2805 return ret;
2808 /* We flag the deletion and tidy it up later. */
2809 exact->v = NULL;
2810 continue;
2813 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2815 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2816 struct GUID_txt_buf buf;
2817 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2818 ldb_asprintf_errstring(ldb, "Attribute %s already "
2819 "deleted for target GUID %s",
2820 el->name, guid_str);
2821 if (ldb_attr_cmp(el->name, "member") == 0) {
2822 talloc_free(tmp_ctx);
2823 return LDB_ERR_UNWILLING_TO_PERFORM;
2824 } else {
2825 talloc_free(tmp_ctx);
2826 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2830 ret = replmd_update_la_val(old_el->values, exact->v,
2831 exact->dsdb_dn, exact->dsdb_dn,
2832 invocation_id, seq_num, seq_num,
2833 now, true);
2834 if (ret != LDB_SUCCESS) {
2835 talloc_free(tmp_ctx);
2836 return ret;
2838 ret = replmd_add_backlink(module, replmd_private,
2839 schema, msg_dn,
2840 &p->guid,
2841 false, schema_attr,
2842 parent);
2843 if (ret != LDB_SUCCESS) {
2844 talloc_free(tmp_ctx);
2845 return ret;
2849 if (vanish_links) {
2850 unsigned j = 0;
2851 struct ldb_val *tmp_vals = NULL;
2853 tmp_vals = talloc_array(tmp_ctx, struct ldb_val,
2854 old_el->num_values);
2855 if (tmp_vals == NULL) {
2856 talloc_free(tmp_ctx);
2857 return ldb_module_oom(module);
2859 for (i = 0; i < old_el->num_values; i++) {
2860 if (old_dns[i].v == NULL) {
2861 continue;
2863 tmp_vals[j] = *old_dns[i].v;
2864 j++;
2866 for (i = 0; i < j; i++) {
2867 old_el->values[i] = tmp_vals[i];
2869 old_el->num_values = j;
2872 el->values = talloc_steal(msg->elements, old_el->values);
2873 el->num_values = old_el->num_values;
2875 talloc_free(tmp_ctx);
2877 /* we now tell the backend to replace all existing values
2878 with the one we have constructed */
2879 el->flags = LDB_FLAG_MOD_REPLACE;
2881 return LDB_SUCCESS;
2885 handle replacing a linked attribute
2887 static int replmd_modify_la_replace(struct ldb_module *module,
2888 struct replmd_private *replmd_private,
2889 const struct dsdb_schema *schema,
2890 struct ldb_message *msg,
2891 struct ldb_message_element *el,
2892 struct ldb_message_element *old_el,
2893 const struct dsdb_attribute *schema_attr,
2894 uint64_t seq_num,
2895 time_t t,
2896 struct ldb_dn *msg_dn,
2897 struct ldb_request *parent)
2899 unsigned int i, old_i, new_i;
2900 struct parsed_dn *dns, *old_dns;
2901 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2902 int ret;
2903 const struct GUID *invocation_id;
2904 struct ldb_context *ldb = ldb_module_get_ctx(module);
2905 struct ldb_val *new_values = NULL;
2906 const char *ldap_oid = schema_attr->syntax->ldap_oid;
2907 unsigned int old_num_values;
2908 unsigned int repl_num_values;
2909 unsigned int max_num_values;
2910 NTTIME now;
2912 unix_to_nt_time(&now, t);
2914 invocation_id = samdb_ntds_invocation_id(ldb);
2915 if (!invocation_id) {
2916 return LDB_ERR_OPERATIONS_ERROR;
2920 * The replace operation is unlike the replace and delete cases in that
2921 * we need to look at every existing link to see whether it is being
2922 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
2924 * As we are trying to combine two sorted lists, the algorithm we use
2925 * is akin to the merge phase of a merge sort. We interleave the two
2926 * lists, doing different things depending on which side the current
2927 * item came from.
2929 * There are three main cases, with some sub-cases.
2931 * - a DN is in the old list but not the new one. It needs to be
2932 * marked as deleted (but left in the list).
2933 * - maybe it is already deleted, and we have less to do.
2935 * - a DN is in both lists. The old data gets replaced by the new,
2936 * and the list doesn't grow. The old link may have been marked as
2937 * deleted, in which case we undelete it.
2939 * - a DN is in the new list only. We add it in the right place.
2942 old_num_values = old_el ? old_el->num_values : 0;
2943 repl_num_values = el->num_values;
2944 max_num_values = old_num_values + repl_num_values;
2946 if (max_num_values == 0) {
2947 /* There is nothing to do! */
2948 return LDB_SUCCESS;
2951 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
2952 if (ret != LDB_SUCCESS) {
2953 talloc_free(tmp_ctx);
2954 return ret;
2957 ret = check_parsed_dn_duplicates(module, el, dns);
2958 if (ret != LDB_SUCCESS) {
2959 talloc_free(tmp_ctx);
2960 return ret;
2963 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
2964 ldap_oid, parent);
2965 if (ret != LDB_SUCCESS) {
2966 talloc_free(tmp_ctx);
2967 return ret;
2970 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
2971 old_el, ldap_oid);
2972 if (ret != LDB_SUCCESS) {
2973 talloc_free(tmp_ctx);
2974 return ret;
2977 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
2978 if (new_values == NULL) {
2979 ldb_module_oom(module);
2980 talloc_free(tmp_ctx);
2981 return LDB_ERR_OPERATIONS_ERROR;
2984 old_i = 0;
2985 new_i = 0;
2986 for (i = 0; i < max_num_values; i++) {
2987 int cmp;
2988 struct parsed_dn *old_p, *new_p;
2989 if (old_i < old_num_values && new_i < repl_num_values) {
2990 old_p = &old_dns[old_i];
2991 new_p = &dns[new_i];
2992 cmp = parsed_dn_compare(old_p, new_p);
2993 } else if (old_i < old_num_values) {
2994 /* the new list is empty, read the old list */
2995 old_p = &old_dns[old_i];
2996 new_p = NULL;
2997 cmp = -1;
2998 } else if (new_i < repl_num_values) {
2999 /* the old list is empty, read new list */
3000 old_p = NULL;
3001 new_p = &dns[new_i];
3002 cmp = 1;
3003 } else {
3004 break;
3007 if (cmp < 0) {
3009 * An old ones that come before the next replacement
3010 * (if any). We mark it as deleted and add it to the
3011 * final list.
3013 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3014 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
3015 ret = replmd_update_la_val(new_values, old_p->v,
3016 old_p->dsdb_dn,
3017 old_p->dsdb_dn,
3018 invocation_id,
3019 seq_num, seq_num,
3020 now, true);
3021 if (ret != LDB_SUCCESS) {
3022 talloc_free(tmp_ctx);
3023 return ret;
3026 ret = replmd_add_backlink(module, replmd_private,
3027 schema,
3028 msg_dn,
3029 &old_p->guid, false,
3030 schema_attr,
3031 parent);
3032 if (ret != LDB_SUCCESS) {
3033 talloc_free(tmp_ctx);
3034 return ret;
3037 new_values[i] = *old_p->v;
3038 old_i++;
3039 } else if (cmp == 0) {
3041 * We are overwriting one. If it was previously
3042 * deleted, we need to add a backlink.
3044 * Note that if any RMD_FLAGs in an extended new DN
3045 * will be ignored.
3047 uint32_t rmd_flags;
3049 ret = replmd_update_la_val(new_values, old_p->v,
3050 new_p->dsdb_dn,
3051 old_p->dsdb_dn,
3052 invocation_id,
3053 seq_num, seq_num,
3054 now, false);
3055 if (ret != LDB_SUCCESS) {
3056 talloc_free(tmp_ctx);
3057 return ret;
3060 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3061 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3062 ret = replmd_add_backlink(module, replmd_private,
3063 schema,
3064 msg_dn,
3065 &new_p->guid, true,
3066 schema_attr,
3067 parent);
3068 if (ret != LDB_SUCCESS) {
3069 talloc_free(tmp_ctx);
3070 return ret;
3074 new_values[i] = *old_p->v;
3075 old_i++;
3076 new_i++;
3077 } else {
3079 * Replacements that don't match an existing one. We
3080 * just add them to the final list.
3082 ret = replmd_build_la_val(new_values,
3083 new_p->v,
3084 new_p->dsdb_dn,
3085 invocation_id,
3086 seq_num, now);
3087 if (ret != LDB_SUCCESS) {
3088 talloc_free(tmp_ctx);
3089 return ret;
3091 ret = replmd_add_backlink(module, replmd_private,
3092 schema,
3093 msg_dn,
3094 &new_p->guid, true,
3095 schema_attr,
3096 parent);
3097 if (ret != LDB_SUCCESS) {
3098 talloc_free(tmp_ctx);
3099 return ret;
3101 new_values[i] = *new_p->v;
3102 new_i++;
3105 if (old_el != NULL) {
3106 talloc_steal(msg->elements, old_el->values);
3108 el->values = talloc_steal(msg->elements, new_values);
3109 el->num_values = i;
3110 talloc_free(tmp_ctx);
3112 el->flags = LDB_FLAG_MOD_REPLACE;
3114 return LDB_SUCCESS;
3119 handle linked attributes in modify requests
3121 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3122 struct replmd_private *replmd_private,
3123 struct replmd_replicated_request *ac,
3124 struct ldb_message *msg,
3125 time_t t,
3126 struct ldb_request *parent)
3128 struct ldb_result *res;
3129 unsigned int i;
3130 int ret;
3131 struct ldb_context *ldb = ldb_module_get_ctx(module);
3132 struct ldb_message *old_msg;
3134 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3136 * Nothing special is required for modifying or vanishing links
3137 * in fl2000 since they are just strings in a multi-valued
3138 * attribute.
3140 struct ldb_control *ctrl = ldb_request_get_control(parent,
3141 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3142 if (ctrl) {
3143 ctrl->critical = false;
3145 return LDB_SUCCESS;
3149 * TODO:
3151 * We should restrict this to the intersection of the list of
3152 * linked attributes in the schema and the list of attributes
3153 * being modified.
3155 * This will help performance a little, as otherwise we have
3156 * to allocate the entire object value-by-value.
3158 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3159 DSDB_FLAG_NEXT_MODULE |
3160 DSDB_SEARCH_SHOW_RECYCLED |
3161 DSDB_SEARCH_REVEAL_INTERNALS |
3162 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3163 parent);
3164 if (ret != LDB_SUCCESS) {
3165 return ret;
3168 old_msg = res->msgs[0];
3170 for (i=0; i<msg->num_elements; i++) {
3171 struct ldb_message_element *el = &msg->elements[i];
3172 struct ldb_message_element *old_el, *new_el;
3173 unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3174 const struct dsdb_attribute *schema_attr
3175 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
3176 if (!schema_attr) {
3177 ldb_asprintf_errstring(ldb,
3178 "%s: attribute %s is not a valid attribute in schema",
3179 __FUNCTION__, el->name);
3180 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3182 if (schema_attr->linkID == 0) {
3183 continue;
3185 if ((schema_attr->linkID & 1) == 1) {
3186 if (parent) {
3187 struct ldb_control *ctrl;
3189 ctrl = ldb_request_get_control(parent,
3190 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3191 if (ctrl != NULL) {
3192 ctrl->critical = false;
3193 continue;
3195 ctrl = ldb_request_get_control(parent,
3196 DSDB_CONTROL_DBCHECK);
3197 if (ctrl != NULL) {
3198 continue;
3202 /* Odd is for the target. Illegal to modify */
3203 ldb_asprintf_errstring(ldb,
3204 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3205 return LDB_ERR_UNWILLING_TO_PERFORM;
3207 old_el = ldb_msg_find_element(old_msg, el->name);
3208 switch (mod_type) {
3209 case LDB_FLAG_MOD_REPLACE:
3210 ret = replmd_modify_la_replace(module, replmd_private,
3211 ac->schema, msg, el, old_el,
3212 schema_attr, ac->seq_num, t,
3213 old_msg->dn,
3214 parent);
3215 break;
3216 case LDB_FLAG_MOD_DELETE:
3217 ret = replmd_modify_la_delete(module, replmd_private,
3218 ac->schema, msg, el, old_el,
3219 schema_attr, ac->seq_num, t,
3220 old_msg->dn,
3221 parent);
3222 break;
3223 case LDB_FLAG_MOD_ADD:
3224 ret = replmd_modify_la_add(module, replmd_private,
3225 ac->schema, msg, el, old_el,
3226 schema_attr, ac->seq_num, t,
3227 old_msg->dn,
3228 parent);
3229 break;
3230 default:
3231 ldb_asprintf_errstring(ldb,
3232 "invalid flags 0x%x for %s linked attribute",
3233 el->flags, el->name);
3234 return LDB_ERR_UNWILLING_TO_PERFORM;
3236 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
3237 ldb_asprintf_errstring(ldb,
3238 "Attribute %s is single valued but more than one value has been supplied",
3239 el->name);
3240 /* Return codes as found on Windows 2012r2 */
3241 if (mod_type == LDB_FLAG_MOD_REPLACE) {
3242 return LDB_ERR_CONSTRAINT_VIOLATION;
3243 } else {
3244 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3246 } else {
3247 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3250 if (ret != LDB_SUCCESS) {
3251 return ret;
3253 if (old_el) {
3254 ldb_msg_remove_attr(old_msg, el->name);
3256 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3257 new_el->num_values = el->num_values;
3258 new_el->values = talloc_steal(msg->elements, el->values);
3260 /* TODO: this relises a bit too heavily on the exact
3261 behaviour of ldb_msg_find_element and
3262 ldb_msg_remove_element */
3263 old_el = ldb_msg_find_element(msg, el->name);
3264 if (old_el != el) {
3265 ldb_msg_remove_element(msg, old_el);
3266 i--;
3270 talloc_free(res);
3271 return ret;
3275 static int send_rodc_referral(struct ldb_request *req,
3276 struct ldb_context *ldb,
3277 struct ldb_dn *dn)
3279 char *referral = NULL;
3280 struct loadparm_context *lp_ctx = NULL;
3281 struct ldb_dn *fsmo_role_dn = NULL;
3282 struct ldb_dn *role_owner_dn = NULL;
3283 const char *domain = NULL;
3284 WERROR werr;
3286 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3287 struct loadparm_context);
3289 werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3290 &fsmo_role_dn, &role_owner_dn);
3292 if (W_ERROR_IS_OK(werr)) {
3293 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3294 if (server_dn != NULL) {
3295 ldb_dn_remove_child_components(server_dn, 1);
3296 domain = samdb_dn_to_dnshostname(ldb, req,
3297 server_dn);
3301 if (domain == NULL) {
3302 domain = lpcfg_dnsdomain(lp_ctx);
3305 referral = talloc_asprintf(req, "ldap://%s/%s",
3306 domain,
3307 ldb_dn_get_linearized(dn));
3308 if (referral == NULL) {
3309 ldb_oom(ldb);
3310 return LDB_ERR_OPERATIONS_ERROR;
3313 return ldb_module_send_referral(req, referral);
3317 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3319 struct ldb_context *ldb;
3320 struct replmd_replicated_request *ac;
3321 struct ldb_request *down_req;
3322 struct ldb_message *msg;
3323 time_t t = time(NULL);
3324 int ret;
3325 bool is_urgent = false, rodc = false;
3326 bool is_schema_nc = false;
3327 unsigned int functional_level;
3328 const struct ldb_message_element *guid_el = NULL;
3329 struct ldb_control *sd_propagation_control;
3330 struct ldb_control *fix_links_control = NULL;
3331 struct ldb_control *fix_dn_name_control = NULL;
3332 struct replmd_private *replmd_private =
3333 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3335 /* do not manipulate our control entries */
3336 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3337 return ldb_next_request(module, req);
3340 sd_propagation_control = ldb_request_get_control(req,
3341 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3342 if (sd_propagation_control != NULL) {
3343 if (req->op.mod.message->num_elements != 1) {
3344 return ldb_module_operr(module);
3346 ret = strcmp(req->op.mod.message->elements[0].name,
3347 "nTSecurityDescriptor");
3348 if (ret != 0) {
3349 return ldb_module_operr(module);
3352 return ldb_next_request(module, req);
3355 ldb = ldb_module_get_ctx(module);
3357 fix_links_control = ldb_request_get_control(req,
3358 DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
3359 if (fix_links_control != NULL) {
3360 struct dsdb_schema *schema = NULL;
3361 const struct dsdb_attribute *sa = NULL;
3363 if (req->op.mod.message->num_elements != 1) {
3364 return ldb_module_operr(module);
3367 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_REPLACE) {
3368 return ldb_module_operr(module);
3371 schema = dsdb_get_schema(ldb, req);
3372 if (schema == NULL) {
3373 return ldb_module_operr(module);
3376 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3377 req->op.mod.message->elements[0].name);
3378 if (sa == NULL) {
3379 return ldb_module_operr(module);
3382 if (sa->linkID == 0) {
3383 return ldb_module_operr(module);
3386 fix_links_control->critical = false;
3387 return ldb_next_request(module, req);
3390 fix_dn_name_control = ldb_request_get_control(req,
3391 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3392 if (fix_dn_name_control != NULL) {
3393 struct dsdb_schema *schema = NULL;
3394 const struct dsdb_attribute *sa = NULL;
3396 if (req->op.mod.message->num_elements != 2) {
3397 return ldb_module_operr(module);
3400 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_DELETE) {
3401 return ldb_module_operr(module);
3404 if (req->op.mod.message->elements[1].flags != LDB_FLAG_MOD_ADD) {
3405 return ldb_module_operr(module);
3408 if (req->op.mod.message->elements[0].num_values != 1) {
3409 return ldb_module_operr(module);
3412 if (req->op.mod.message->elements[1].num_values != 1) {
3413 return ldb_module_operr(module);
3416 schema = dsdb_get_schema(ldb, req);
3417 if (schema == NULL) {
3418 return ldb_module_operr(module);
3421 if (ldb_attr_cmp(req->op.mod.message->elements[0].name,
3422 req->op.mod.message->elements[1].name) != 0) {
3423 return ldb_module_operr(module);
3426 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3427 req->op.mod.message->elements[0].name);
3428 if (sa == NULL) {
3429 return ldb_module_operr(module);
3432 if (sa->dn_format == DSDB_INVALID_DN) {
3433 return ldb_module_operr(module);
3436 if (sa->linkID != 0) {
3437 return ldb_module_operr(module);
3441 * If we are run from dbcheck and we are not updating
3442 * a link (as these would need to be sorted and so
3443 * can't go via such a simple update, then do not
3444 * trigger replicated updates and a new USN from this
3445 * change, it wasn't a real change, just a new
3446 * (correct) string DN
3449 fix_dn_name_control->critical = false;
3450 return ldb_next_request(module, req);
3453 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3455 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3456 if (guid_el != NULL) {
3457 ldb_set_errstring(ldb,
3458 "replmd_modify: it's not allowed to change the objectGUID!");
3459 return LDB_ERR_CONSTRAINT_VIOLATION;
3462 ac = replmd_ctx_init(module, req);
3463 if (ac == NULL) {
3464 return ldb_module_oom(module);
3467 functional_level = dsdb_functional_level(ldb);
3469 /* we have to copy the message as the caller might have it as a const */
3470 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3471 if (msg == NULL) {
3472 ldb_oom(ldb);
3473 talloc_free(ac);
3474 return LDB_ERR_OPERATIONS_ERROR;
3477 ldb_msg_remove_attr(msg, "whenChanged");
3478 ldb_msg_remove_attr(msg, "uSNChanged");
3480 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3482 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3483 msg, &ac->seq_num, t, is_schema_nc,
3484 &is_urgent, &rodc);
3485 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3486 ret = send_rodc_referral(req, ldb, msg->dn);
3487 talloc_free(ac);
3488 return ret;
3492 if (ret != LDB_SUCCESS) {
3493 talloc_free(ac);
3494 return ret;
3497 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3498 ac, msg, t, req);
3499 if (ret != LDB_SUCCESS) {
3500 talloc_free(ac);
3501 return ret;
3504 /* TODO:
3505 * - replace the old object with the newly constructed one
3508 ac->is_urgent = is_urgent;
3510 ret = ldb_build_mod_req(&down_req, ldb, ac,
3511 msg,
3512 req->controls,
3513 ac, replmd_op_callback,
3514 req);
3515 LDB_REQ_SET_LOCATION(down_req);
3516 if (ret != LDB_SUCCESS) {
3517 talloc_free(ac);
3518 return ret;
3521 /* current partition control is needed by "replmd_op_callback" */
3522 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3523 ret = ldb_request_add_control(down_req,
3524 DSDB_CONTROL_CURRENT_PARTITION_OID,
3525 false, NULL);
3526 if (ret != LDB_SUCCESS) {
3527 talloc_free(ac);
3528 return ret;
3532 /* If we are in functional level 2000, then
3533 * replmd_modify_handle_linked_attribs will have done
3534 * nothing */
3535 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3536 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3537 if (ret != LDB_SUCCESS) {
3538 talloc_free(ac);
3539 return ret;
3543 talloc_steal(down_req, msg);
3545 /* we only change whenChanged and uSNChanged if the seq_num
3546 has changed */
3547 if (ac->seq_num != 0) {
3548 ret = add_time_element(msg, "whenChanged", t);
3549 if (ret != LDB_SUCCESS) {
3550 talloc_free(ac);
3551 ldb_operr(ldb);
3552 return ret;
3555 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3556 if (ret != LDB_SUCCESS) {
3557 talloc_free(ac);
3558 ldb_operr(ldb);
3559 return ret;
3563 /* go on with the call chain */
3564 return ldb_next_request(module, down_req);
3567 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3570 handle a rename request
3572 On a rename we need to do an extra ldb_modify which sets the
3573 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3575 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3577 struct ldb_context *ldb;
3578 struct replmd_replicated_request *ac;
3579 int ret;
3580 struct ldb_request *down_req;
3582 /* do not manipulate our control entries */
3583 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3584 return ldb_next_request(module, req);
3587 ldb = ldb_module_get_ctx(module);
3589 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3591 ac = replmd_ctx_init(module, req);
3592 if (ac == NULL) {
3593 return ldb_module_oom(module);
3596 ret = ldb_build_rename_req(&down_req, ldb, ac,
3597 ac->req->op.rename.olddn,
3598 ac->req->op.rename.newdn,
3599 ac->req->controls,
3600 ac, replmd_rename_callback,
3601 ac->req);
3602 LDB_REQ_SET_LOCATION(down_req);
3603 if (ret != LDB_SUCCESS) {
3604 talloc_free(ac);
3605 return ret;
3608 /* go on with the call chain */
3609 return ldb_next_request(module, down_req);
3612 /* After the rename is compleated, update the whenchanged etc */
3613 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3615 struct ldb_context *ldb;
3616 struct ldb_request *down_req;
3617 struct ldb_message *msg;
3618 const struct dsdb_attribute *rdn_attr;
3619 const char *rdn_name;
3620 const struct ldb_val *rdn_val;
3621 const char *attrs[5] = { NULL, };
3622 time_t t = time(NULL);
3623 int ret;
3624 bool is_urgent = false, rodc = false;
3625 bool is_schema_nc;
3626 struct replmd_replicated_request *ac =
3627 talloc_get_type(req->context, struct replmd_replicated_request);
3628 struct replmd_private *replmd_private =
3629 talloc_get_type(ldb_module_get_private(ac->module),
3630 struct replmd_private);
3632 ldb = ldb_module_get_ctx(ac->module);
3634 if (ares->error != LDB_SUCCESS) {
3635 return ldb_module_done(ac->req, ares->controls,
3636 ares->response, ares->error);
3639 if (ares->type != LDB_REPLY_DONE) {
3640 ldb_set_errstring(ldb,
3641 "invalid ldb_reply_type in callback");
3642 talloc_free(ares);
3643 return ldb_module_done(ac->req, NULL, NULL,
3644 LDB_ERR_OPERATIONS_ERROR);
3647 /* TODO:
3648 * - replace the old object with the newly constructed one
3651 msg = ldb_msg_new(ac);
3652 if (msg == NULL) {
3653 ldb_oom(ldb);
3654 return LDB_ERR_OPERATIONS_ERROR;
3657 msg->dn = ac->req->op.rename.newdn;
3659 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3661 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3662 if (rdn_name == NULL) {
3663 talloc_free(ares);
3664 return ldb_module_done(ac->req, NULL, NULL,
3665 ldb_operr(ldb));
3668 /* normalize the rdn attribute name */
3669 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3670 if (rdn_attr == NULL) {
3671 talloc_free(ares);
3672 return ldb_module_done(ac->req, NULL, NULL,
3673 ldb_operr(ldb));
3675 rdn_name = rdn_attr->lDAPDisplayName;
3677 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3678 if (rdn_val == NULL) {
3679 talloc_free(ares);
3680 return ldb_module_done(ac->req, NULL, NULL,
3681 ldb_operr(ldb));
3684 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3685 talloc_free(ares);
3686 return ldb_module_done(ac->req, NULL, NULL,
3687 ldb_oom(ldb));
3689 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3690 talloc_free(ares);
3691 return ldb_module_done(ac->req, NULL, NULL,
3692 ldb_oom(ldb));
3694 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3695 talloc_free(ares);
3696 return ldb_module_done(ac->req, NULL, NULL,
3697 ldb_oom(ldb));
3699 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3700 talloc_free(ares);
3701 return ldb_module_done(ac->req, NULL, NULL,
3702 ldb_oom(ldb));
3706 * here we let replmd_update_rpmd() only search for
3707 * the existing "replPropertyMetaData" and rdn_name attributes.
3709 * We do not want the existing "name" attribute as
3710 * the "name" attribute needs to get the version
3711 * updated on rename even if the rdn value hasn't changed.
3713 * This is the diff of the meta data, for a moved user
3714 * on a w2k8r2 server:
3716 * # record 1
3717 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3718 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3719 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3720 * version : 0x00000001 (1)
3721 * reserved : 0x00000000 (0)
3722 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3723 * local_usn : 0x00000000000037a5 (14245)
3724 * array: struct replPropertyMetaData1
3725 * attid : DRSUAPI_ATTID_name (0x90001)
3726 * - version : 0x00000001 (1)
3727 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3728 * + version : 0x00000002 (2)
3729 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3730 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3731 * - originating_usn : 0x00000000000037a5 (14245)
3732 * - local_usn : 0x00000000000037a5 (14245)
3733 * + originating_usn : 0x0000000000003834 (14388)
3734 * + local_usn : 0x0000000000003834 (14388)
3735 * array: struct replPropertyMetaData1
3736 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3737 * version : 0x00000004 (4)
3739 attrs[0] = "replPropertyMetaData";
3740 attrs[1] = "objectClass";
3741 attrs[2] = "instanceType";
3742 attrs[3] = rdn_name;
3743 attrs[4] = NULL;
3745 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3746 msg, &ac->seq_num, t,
3747 is_schema_nc, &is_urgent, &rodc);
3748 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3749 ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
3750 talloc_free(ares);
3751 return ldb_module_done(req, NULL, NULL, ret);
3754 if (ret != LDB_SUCCESS) {
3755 talloc_free(ares);
3756 return ldb_module_done(ac->req, NULL, NULL, ret);
3759 if (ac->seq_num == 0) {
3760 talloc_free(ares);
3761 return ldb_module_done(ac->req, NULL, NULL,
3762 ldb_error(ldb, ret,
3763 "internal error seq_num == 0"));
3765 ac->is_urgent = is_urgent;
3767 ret = ldb_build_mod_req(&down_req, ldb, ac,
3768 msg,
3769 req->controls,
3770 ac, replmd_op_callback,
3771 req);
3772 LDB_REQ_SET_LOCATION(down_req);
3773 if (ret != LDB_SUCCESS) {
3774 talloc_free(ac);
3775 return ret;
3778 /* current partition control is needed by "replmd_op_callback" */
3779 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3780 ret = ldb_request_add_control(down_req,
3781 DSDB_CONTROL_CURRENT_PARTITION_OID,
3782 false, NULL);
3783 if (ret != LDB_SUCCESS) {
3784 talloc_free(ac);
3785 return ret;
3789 talloc_steal(down_req, msg);
3791 ret = add_time_element(msg, "whenChanged", t);
3792 if (ret != LDB_SUCCESS) {
3793 talloc_free(ac);
3794 ldb_operr(ldb);
3795 return ret;
3798 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3799 if (ret != LDB_SUCCESS) {
3800 talloc_free(ac);
3801 ldb_operr(ldb);
3802 return ret;
3805 /* go on with the call chain - do the modify after the rename */
3806 return ldb_next_request(ac->module, down_req);
3810 * remove links from objects that point at this object when an object
3811 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3812 * RemoveObj which states that link removal due to the object being
3813 * deleted is NOT an originating update - they just go away!
3816 static int replmd_delete_remove_link(struct ldb_module *module,
3817 const struct dsdb_schema *schema,
3818 struct replmd_private *replmd_private,
3819 struct ldb_dn *dn,
3820 struct GUID *guid,
3821 struct ldb_message_element *el,
3822 const struct dsdb_attribute *sa,
3823 struct ldb_request *parent)
3825 unsigned int i;
3826 TALLOC_CTX *tmp_ctx = talloc_new(module);
3827 struct ldb_context *ldb = ldb_module_get_ctx(module);
3829 for (i=0; i<el->num_values; i++) {
3830 struct dsdb_dn *dsdb_dn;
3831 int ret;
3832 struct ldb_message *msg;
3833 const struct dsdb_attribute *target_attr;
3834 struct ldb_message_element *el2;
3835 const char *dn_str;
3836 struct ldb_val dn_val;
3837 uint32_t dsdb_flags = 0;
3838 const char *attrs[] = { NULL, NULL };
3839 struct ldb_result *link_res;
3840 struct ldb_message *link_msg;
3841 struct ldb_message_element *link_el;
3842 struct parsed_dn *link_dns;
3843 struct parsed_dn *p = NULL, *unused = NULL;
3845 if (dsdb_dn_is_deleted_val(&el->values[i])) {
3846 continue;
3849 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
3850 if (!dsdb_dn) {
3851 talloc_free(tmp_ctx);
3852 return LDB_ERR_OPERATIONS_ERROR;
3855 /* remove the link */
3856 msg = ldb_msg_new(tmp_ctx);
3857 if (!msg) {
3858 ldb_module_oom(module);
3859 talloc_free(tmp_ctx);
3860 return LDB_ERR_OPERATIONS_ERROR;
3864 msg->dn = dsdb_dn->dn;
3866 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
3867 if (target_attr == NULL) {
3868 continue;
3870 attrs[0] = target_attr->lDAPDisplayName;
3872 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
3873 LDB_FLAG_MOD_DELETE, &el2);
3874 if (ret != LDB_SUCCESS) {
3875 ldb_module_oom(module);
3876 talloc_free(tmp_ctx);
3877 return LDB_ERR_OPERATIONS_ERROR;
3880 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
3881 msg->dn, attrs,
3882 DSDB_FLAG_NEXT_MODULE |
3883 DSDB_SEARCH_SHOW_EXTENDED_DN |
3884 DSDB_SEARCH_SHOW_RECYCLED,
3885 parent);
3887 if (ret != LDB_SUCCESS) {
3888 talloc_free(tmp_ctx);
3889 return ret;
3892 link_msg = link_res->msgs[0];
3893 link_el = ldb_msg_find_element(link_msg,
3894 target_attr->lDAPDisplayName);
3895 if (link_el == NULL) {
3896 talloc_free(tmp_ctx);
3897 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3901 * This call 'upgrades' the links in link_dns, but we
3902 * do not commit the result back into the database, so
3903 * this is safe to call in FL2000 or on databases that
3904 * have been run at that level in the past.
3906 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx,
3907 link_el, &link_dns,
3908 target_attr->syntax->ldap_oid, parent);
3909 if (ret != LDB_SUCCESS) {
3910 talloc_free(tmp_ctx);
3911 return ret;
3914 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
3915 guid, dn,
3916 data_blob_null, 0,
3917 &p, &unused,
3918 target_attr->syntax->ldap_oid, false);
3919 if (ret != LDB_SUCCESS) {
3920 talloc_free(tmp_ctx);
3921 return ret;
3924 if (p == NULL) {
3925 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3926 "Failed to find forward link on %s "
3927 "as %s to remove backlink %s on %s",
3928 ldb_dn_get_linearized(msg->dn),
3929 target_attr->lDAPDisplayName,
3930 sa->lDAPDisplayName,
3931 ldb_dn_get_linearized(dn));
3932 talloc_free(tmp_ctx);
3933 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3937 /* This needs to get the Binary DN, by first searching */
3938 dn_str = dsdb_dn_get_linearized(tmp_ctx,
3939 p->dsdb_dn);
3941 dn_val = data_blob_string_const(dn_str);
3942 el2->values = &dn_val;
3943 el2->num_values = 1;
3946 * Ensure that we tell the modification to vanish any linked
3947 * attributes (not simply mark them as isDeleted = TRUE)
3949 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
3951 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
3952 if (ret != LDB_SUCCESS) {
3953 talloc_free(tmp_ctx);
3954 return ret;
3957 talloc_free(tmp_ctx);
3958 return LDB_SUCCESS;
3963 handle update of replication meta data for deletion of objects
3965 This also handles the mapping of delete to a rename operation
3966 to allow deletes to be replicated.
3968 It also handles the incoming deleted objects, to ensure they are
3969 fully deleted here. In that case re_delete is true, and we do not
3970 use this as a signal to change the deleted state, just reinforce it.
3973 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
3975 int ret = LDB_ERR_OTHER;
3976 bool retb, disallow_move_on_delete;
3977 struct ldb_dn *old_dn = NULL, *new_dn = NULL;
3978 const char *rdn_name;
3979 const struct ldb_val *rdn_value, *new_rdn_value;
3980 struct GUID guid;
3981 struct ldb_context *ldb = ldb_module_get_ctx(module);
3982 const struct dsdb_schema *schema;
3983 struct ldb_message *msg, *old_msg;
3984 struct ldb_message_element *el;
3985 TALLOC_CTX *tmp_ctx;
3986 struct ldb_result *res, *parent_res;
3987 static const char * const preserved_attrs[] = {
3988 /* yes, this really is a hard coded list. See MS-ADTS
3989 section 3.1.1.5.5.1.1 */
3990 "attributeID",
3991 "attributeSyntax",
3992 "dNReferenceUpdate",
3993 "dNSHostName",
3994 "flatName",
3995 "governsID",
3996 "groupType",
3997 "instanceType",
3998 "lDAPDisplayName",
3999 "legacyExchangeDN",
4000 "isDeleted",
4001 "isRecycled",
4002 "lastKnownParent",
4003 "msDS-LastKnownRDN",
4004 "msDS-PortLDAP",
4005 "mS-DS-CreatorSID",
4006 "mSMQOwnerID",
4007 "nCName",
4008 "objectClass",
4009 "distinguishedName",
4010 "objectGUID",
4011 "objectSid",
4012 "oMSyntax",
4013 "proxiedObjectName",
4014 "name",
4015 "nTSecurityDescriptor",
4016 "replPropertyMetaData",
4017 "sAMAccountName",
4018 "securityIdentifier",
4019 "sIDHistory",
4020 "subClassOf",
4021 "systemFlags",
4022 "trustPartner",
4023 "trustDirection",
4024 "trustType",
4025 "trustAttributes",
4026 "userAccountControl",
4027 "uSNChanged",
4028 "uSNCreated",
4029 "whenCreated",
4030 "whenChanged",
4031 NULL
4033 static const char * const all_attrs[] = {
4034 DSDB_SECRET_ATTRIBUTES,
4035 "*",
4036 NULL
4038 static const struct ldb_val true_val = {
4039 .data = discard_const_p(uint8_t, "TRUE"),
4040 .length = 4
4043 unsigned int i;
4044 uint32_t dsdb_flags = 0;
4045 struct replmd_private *replmd_private;
4046 enum deletion_state deletion_state, next_deletion_state;
4048 if (ldb_dn_is_special(req->op.del.dn)) {
4049 return ldb_next_request(module, req);
4053 * We have to allow dbcheck to remove an object that
4054 * is beyond repair, and to do so totally. This could
4055 * mean we we can get a partial object from the other
4056 * DC, causing havoc, so dbcheck suggests
4057 * re-replication first. dbcheck sets both DBCHECK
4058 * and RELAX in this situation.
4060 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
4061 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
4062 /* really, really remove it */
4063 return ldb_next_request(module, req);
4066 tmp_ctx = talloc_new(ldb);
4067 if (!tmp_ctx) {
4068 ldb_oom(ldb);
4069 return LDB_ERR_OPERATIONS_ERROR;
4072 schema = dsdb_get_schema(ldb, tmp_ctx);
4073 if (!schema) {
4074 talloc_free(tmp_ctx);
4075 return LDB_ERR_OPERATIONS_ERROR;
4078 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
4080 /* we need the complete msg off disk, so we can work out which
4081 attributes need to be removed */
4082 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
4083 DSDB_FLAG_NEXT_MODULE |
4084 DSDB_SEARCH_SHOW_RECYCLED |
4085 DSDB_SEARCH_REVEAL_INTERNALS |
4086 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
4087 if (ret != LDB_SUCCESS) {
4088 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4089 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
4090 re_delete ? "re-delete" : "delete",
4091 ldb_dn_get_linearized(old_dn),
4092 ldb_errstring(ldb_module_get_ctx(module)));
4093 talloc_free(tmp_ctx);
4094 return ret;
4096 old_msg = res->msgs[0];
4098 replmd_deletion_state(module, old_msg,
4099 &deletion_state,
4100 &next_deletion_state);
4102 /* This supports us noticing an incoming isDeleted and acting on it */
4103 if (re_delete) {
4104 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
4105 next_deletion_state = deletion_state;
4108 if (next_deletion_state == OBJECT_REMOVED) {
4110 * We have to prevent objects being deleted, even if
4111 * the administrator really wants them gone, as
4112 * without the tombstone, we can get a partial object
4113 * from the other DC, causing havoc.
4115 * The only other valid case is when the 180 day
4116 * timeout has expired, when relax is specified.
4118 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
4119 /* it is already deleted - really remove it this time */
4120 talloc_free(tmp_ctx);
4121 return ldb_next_request(module, req);
4124 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
4125 "This check is to prevent corruption of the replicated state.",
4126 ldb_dn_get_linearized(old_msg->dn));
4127 return LDB_ERR_UNWILLING_TO_PERFORM;
4130 rdn_name = ldb_dn_get_rdn_name(old_dn);
4131 rdn_value = ldb_dn_get_rdn_val(old_dn);
4132 if ((rdn_name == NULL) || (rdn_value == NULL)) {
4133 talloc_free(tmp_ctx);
4134 return ldb_operr(ldb);
4137 msg = ldb_msg_new(tmp_ctx);
4138 if (msg == NULL) {
4139 ldb_module_oom(module);
4140 talloc_free(tmp_ctx);
4141 return LDB_ERR_OPERATIONS_ERROR;
4144 msg->dn = old_dn;
4146 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4147 disallow_move_on_delete =
4148 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4149 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4151 /* work out where we will be renaming this object to */
4152 if (!disallow_move_on_delete) {
4153 struct ldb_dn *deleted_objects_dn;
4154 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4155 &deleted_objects_dn);
4158 * We should not move objects if we can't find the
4159 * deleted objects DN. Not moving (or otherwise
4160 * harming) the Deleted Objects DN itself is handled
4161 * in the caller.
4163 if (re_delete && (ret != LDB_SUCCESS)) {
4164 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4165 if (new_dn == NULL) {
4166 ldb_module_oom(module);
4167 talloc_free(tmp_ctx);
4168 return LDB_ERR_OPERATIONS_ERROR;
4170 } else if (ret != LDB_SUCCESS) {
4171 /* this is probably an attempted delete on a partition
4172 * that doesn't allow delete operations, such as the
4173 * schema partition */
4174 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4175 ldb_dn_get_linearized(old_dn));
4176 talloc_free(tmp_ctx);
4177 return LDB_ERR_UNWILLING_TO_PERFORM;
4178 } else {
4179 new_dn = deleted_objects_dn;
4181 } else {
4182 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4183 if (new_dn == NULL) {
4184 ldb_module_oom(module);
4185 talloc_free(tmp_ctx);
4186 return LDB_ERR_OPERATIONS_ERROR;
4190 /* get the objects GUID from the search we just did */
4191 guid = samdb_result_guid(old_msg, "objectGUID");
4193 if (deletion_state == OBJECT_NOT_DELETED) {
4194 struct ldb_message_element *is_deleted_el;
4196 ret = replmd_make_deleted_child_dn(tmp_ctx,
4197 ldb,
4198 new_dn,
4199 rdn_name, rdn_value,
4200 guid);
4202 if (ret != LDB_SUCCESS) {
4203 talloc_free(tmp_ctx);
4204 return ret;
4207 ret = ldb_msg_add_value(msg, "isDeleted", &true_val,
4208 &is_deleted_el);
4209 if (ret != LDB_SUCCESS) {
4210 ldb_asprintf_errstring(ldb, __location__
4211 ": Failed to add isDeleted string to the msg");
4212 talloc_free(tmp_ctx);
4213 return ret;
4215 is_deleted_el->flags = LDB_FLAG_MOD_REPLACE;
4216 } else {
4218 * No matter what has happened with other renames etc, try again to
4219 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4222 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4223 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4224 if (!retb) {
4225 ldb_asprintf_errstring(ldb, __location__
4226 ": Unable to add a prepare rdn of %s",
4227 ldb_dn_get_linearized(rdn));
4228 talloc_free(tmp_ctx);
4229 return LDB_ERR_OPERATIONS_ERROR;
4231 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4233 retb = ldb_dn_add_child(new_dn, rdn);
4234 if (!retb) {
4235 ldb_asprintf_errstring(ldb, __location__
4236 ": Unable to add rdn %s to base dn: %s",
4237 ldb_dn_get_linearized(rdn),
4238 ldb_dn_get_linearized(new_dn));
4239 talloc_free(tmp_ctx);
4240 return LDB_ERR_OPERATIONS_ERROR;
4245 now we need to modify the object in the following ways:
4247 - add isDeleted=TRUE
4248 - update rDN and name, with new rDN
4249 - remove linked attributes
4250 - remove objectCategory and sAMAccountType
4251 - remove attribs not on the preserved list
4252 - preserved if in above list, or is rDN
4253 - remove all linked attribs from this object
4254 - remove all links from other objects to this object
4255 - add lastKnownParent
4256 - update replPropertyMetaData?
4258 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4261 if (deletion_state == OBJECT_NOT_DELETED) {
4262 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4263 char *parent_dn_str = NULL;
4264 struct ldb_message_element *p_el;
4266 /* we need the storage form of the parent GUID */
4267 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4268 parent_dn, NULL,
4269 DSDB_FLAG_NEXT_MODULE |
4270 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4271 DSDB_SEARCH_REVEAL_INTERNALS|
4272 DSDB_SEARCH_SHOW_RECYCLED, req);
4273 if (ret != LDB_SUCCESS) {
4274 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4275 "repmd_delete: Failed to %s %s, "
4276 "because we failed to find it's parent (%s): %s",
4277 re_delete ? "re-delete" : "delete",
4278 ldb_dn_get_linearized(old_dn),
4279 ldb_dn_get_linearized(parent_dn),
4280 ldb_errstring(ldb_module_get_ctx(module)));
4281 talloc_free(tmp_ctx);
4282 return ret;
4286 * Now we can use the DB version,
4287 * it will have the extended DN info in it
4289 parent_dn = parent_res->msgs[0]->dn;
4290 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4291 parent_dn,
4293 if (parent_dn_str == NULL) {
4294 talloc_free(tmp_ctx);
4295 return ldb_module_oom(module);
4298 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4299 parent_dn_str);
4300 if (ret != LDB_SUCCESS) {
4301 ldb_asprintf_errstring(ldb, __location__
4302 ": Failed to add lastKnownParent "
4303 "string when deleting %s",
4304 ldb_dn_get_linearized(old_dn));
4305 talloc_free(tmp_ctx);
4306 return ret;
4308 p_el = ldb_msg_find_element(msg,
4309 "lastKnownParent");
4310 if (p_el == NULL) {
4311 talloc_free(tmp_ctx);
4312 return ldb_module_operr(module);
4314 p_el->flags = LDB_FLAG_MOD_REPLACE;
4316 if (next_deletion_state == OBJECT_DELETED) {
4317 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4318 if (ret != LDB_SUCCESS) {
4319 ldb_asprintf_errstring(ldb, __location__
4320 ": Failed to add msDS-LastKnownRDN "
4321 "string when deleting %s",
4322 ldb_dn_get_linearized(old_dn));
4323 talloc_free(tmp_ctx);
4324 return ret;
4326 p_el = ldb_msg_find_element(msg,
4327 "msDS-LastKnownRDN");
4328 if (p_el == NULL) {
4329 talloc_free(tmp_ctx);
4330 return ldb_module_operr(module);
4332 p_el->flags = LDB_FLAG_MOD_ADD;
4336 switch (next_deletion_state) {
4338 case OBJECT_RECYCLED:
4339 case OBJECT_TOMBSTONE:
4342 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4343 * describes what must be removed from a tombstone
4344 * object
4346 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4347 * describes what must be removed from a recycled
4348 * object
4353 * we also mark it as recycled, meaning this object can't be
4354 * recovered (we are stripping its attributes).
4355 * This is done only if we have this schema object of course ...
4356 * This behavior is identical to the one of Windows 2008R2 which
4357 * always set the isRecycled attribute, even if the recycle-bin is
4358 * not activated and what ever the forest level is.
4360 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4361 struct ldb_message_element *is_recycled_el;
4363 ret = ldb_msg_add_value(msg, "isRecycled", &true_val,
4364 &is_recycled_el);
4365 if (ret != LDB_SUCCESS) {
4366 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4367 ldb_module_oom(module);
4368 talloc_free(tmp_ctx);
4369 return ret;
4371 is_recycled_el->flags = LDB_FLAG_MOD_REPLACE;
4374 replmd_private = talloc_get_type(ldb_module_get_private(module),
4375 struct replmd_private);
4376 /* work out which of the old attributes we will be removing */
4377 for (i=0; i<old_msg->num_elements; i++) {
4378 const struct dsdb_attribute *sa;
4379 el = &old_msg->elements[i];
4380 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4381 if (!sa) {
4382 talloc_free(tmp_ctx);
4383 return LDB_ERR_OPERATIONS_ERROR;
4385 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4386 /* don't remove the rDN */
4387 continue;
4390 if (sa->linkID & 1) {
4392 we have a backlink in this object
4393 that needs to be removed. We're not
4394 allowed to remove it directly
4395 however, so we instead setup a
4396 modify to delete the corresponding
4397 forward link
4399 ret = replmd_delete_remove_link(module, schema,
4400 replmd_private,
4401 old_dn, &guid,
4402 el, sa, req);
4403 if (ret == LDB_SUCCESS) {
4405 * now we continue, which means we
4406 * won't remove this backlink
4407 * directly
4409 continue;
4412 if (ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
4413 const char *old_dn_str
4414 = ldb_dn_get_linearized(old_dn);
4415 ldb_asprintf_errstring(ldb,
4416 __location__
4417 ": Failed to remove backlink of "
4418 "%s when deleting %s: %s",
4419 el->name,
4420 old_dn_str,
4421 ldb_errstring(ldb));
4422 talloc_free(tmp_ctx);
4423 return LDB_ERR_OPERATIONS_ERROR;
4427 * Otherwise vanish the link, we are
4428 * out of sync and the controlling
4429 * object does not have the source
4430 * link any more
4433 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4435 } else if (sa->linkID == 0) {
4436 if (ldb_attr_in_list(preserved_attrs, el->name)) {
4437 continue;
4439 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4440 continue;
4442 } else {
4444 * Ensure that we tell the modification to vanish any linked
4445 * attributes (not simply mark them as isDeleted = TRUE)
4447 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4449 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4450 if (ret != LDB_SUCCESS) {
4451 talloc_free(tmp_ctx);
4452 ldb_module_oom(module);
4453 return ret;
4457 break;
4459 case OBJECT_DELETED:
4461 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4462 * describes what must be removed from a deleted
4463 * object
4466 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4467 if (ret != LDB_SUCCESS) {
4468 talloc_free(tmp_ctx);
4469 ldb_module_oom(module);
4470 return ret;
4473 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4474 if (ret != LDB_SUCCESS) {
4475 talloc_free(tmp_ctx);
4476 ldb_module_oom(module);
4477 return ret;
4480 break;
4482 default:
4483 break;
4486 if (deletion_state == OBJECT_NOT_DELETED) {
4487 const struct dsdb_attribute *sa;
4489 /* work out what the new rdn value is, for updating the
4490 rDN and name fields */
4491 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4492 if (new_rdn_value == NULL) {
4493 talloc_free(tmp_ctx);
4494 return ldb_operr(ldb);
4497 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4498 if (!sa) {
4499 talloc_free(tmp_ctx);
4500 return LDB_ERR_OPERATIONS_ERROR;
4503 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4504 &el);
4505 if (ret != LDB_SUCCESS) {
4506 talloc_free(tmp_ctx);
4507 return ret;
4509 el->flags = LDB_FLAG_MOD_REPLACE;
4511 el = ldb_msg_find_element(old_msg, "name");
4512 if (el) {
4513 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4514 if (ret != LDB_SUCCESS) {
4515 talloc_free(tmp_ctx);
4516 return ret;
4518 el->flags = LDB_FLAG_MOD_REPLACE;
4523 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4528 * No matter what has happned with other renames, try again to
4529 * get this to be under the deleted DN.
4531 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4532 /* now rename onto the new DN */
4533 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4534 if (ret != LDB_SUCCESS){
4535 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4536 ldb_dn_get_linearized(old_dn),
4537 ldb_dn_get_linearized(new_dn),
4538 ldb_errstring(ldb)));
4539 talloc_free(tmp_ctx);
4540 return ret;
4542 msg->dn = new_dn;
4545 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4546 if (ret != LDB_SUCCESS) {
4547 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
4548 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
4549 talloc_free(tmp_ctx);
4550 return ret;
4553 talloc_free(tmp_ctx);
4555 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4558 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4560 return replmd_delete_internals(module, req, false);
4564 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4566 return ret;
4569 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4571 int ret = LDB_ERR_OTHER;
4572 /* TODO: do some error mapping */
4574 /* Let the caller know the full WERROR */
4575 ar->objs->error = status;
4577 return ret;
4581 static struct replPropertyMetaData1 *
4582 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4583 enum drsuapi_DsAttributeId attid)
4585 uint32_t i;
4586 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4588 for (i = 0; i < rpmd_ctr->count; i++) {
4589 if (rpmd_ctr->array[i].attid == attid) {
4590 return &rpmd_ctr->array[i];
4593 return NULL;
4598 return true if an update is newer than an existing entry
4599 see section 5.11 of MS-ADTS
4601 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4602 const struct GUID *update_invocation_id,
4603 uint32_t current_version,
4604 uint32_t update_version,
4605 NTTIME current_change_time,
4606 NTTIME update_change_time)
4608 if (update_version != current_version) {
4609 return update_version > current_version;
4611 if (update_change_time != current_change_time) {
4612 return update_change_time > current_change_time;
4614 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4617 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4618 struct replPropertyMetaData1 *new_m)
4620 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4621 &new_m->originating_invocation_id,
4622 cur_m->version,
4623 new_m->version,
4624 cur_m->originating_change_time,
4625 new_m->originating_change_time);
4628 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4629 struct replPropertyMetaData1 *cur_m,
4630 struct replPropertyMetaData1 *new_m)
4632 bool cmp;
4635 * If the new replPropertyMetaData entry for this attribute is
4636 * not provided (this happens in the case where we look for
4637 * ATTID_name, but the name was not changed), then the local
4638 * state is clearly still current, as the remote
4639 * server didn't send it due to being older the high watermark
4640 * USN we sent.
4642 if (new_m == NULL) {
4643 return false;
4646 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4648 * if we compare equal then do an
4649 * update. This is used when a client
4650 * asks for a FULL_SYNC, and can be
4651 * used to recover a corrupt
4652 * replica.
4654 * This call is a bit tricky, what we
4655 * are doing it turning the 'is_newer'
4656 * call into a 'not is older' by
4657 * swapping cur_m and new_m, and negating the
4658 * outcome.
4660 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4661 cur_m);
4662 } else {
4663 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4664 new_m);
4666 return cmp;
4671 form a DN for a deleted (DEL:) or conflict (CNF:) DN
4673 static int replmd_make_prefix_child_dn(TALLOC_CTX *tmp_ctx,
4674 struct ldb_context *ldb,
4675 struct ldb_dn *dn,
4676 const char *four_char_prefix,
4677 const char *rdn_name,
4678 const struct ldb_val *rdn_value,
4679 struct GUID guid)
4681 struct ldb_val deleted_child_rdn_val;
4682 struct GUID_txt_buf guid_str;
4683 int ret;
4684 bool retb;
4686 GUID_buf_string(&guid, &guid_str);
4688 retb = ldb_dn_add_child_fmt(dn, "X=Y");
4689 if (!retb) {
4690 ldb_asprintf_errstring(ldb, __location__
4691 ": Unable to add a formatted child to dn: %s",
4692 ldb_dn_get_linearized(dn));
4693 return LDB_ERR_OPERATIONS_ERROR;
4697 * TODO: Per MS-ADTS 3.1.1.5.5 Delete Operation
4698 * we should truncate this value to ensure the RDN is not more than 255 chars.
4700 * However we MS-ADTS 3.1.1.5.1.2 Naming Constraints indicates that:
4702 * "Naming constraints are not enforced for replicated
4703 * updates." so this is safe and we don't have to work out not
4704 * splitting a UTF8 char right now.
4706 deleted_child_rdn_val = ldb_val_dup(tmp_ctx, rdn_value);
4709 * sizeof(guid_str.buf) will always be longer than
4710 * strlen(guid_str.buf) but we allocate using this and
4711 * waste the trailing bytes to avoid scaring folks
4712 * with memcpy() using strlen() below
4715 deleted_child_rdn_val.data
4716 = talloc_realloc(tmp_ctx, deleted_child_rdn_val.data,
4717 uint8_t,
4718 rdn_value->length + 5
4719 + sizeof(guid_str.buf));
4720 if (!deleted_child_rdn_val.data) {
4721 ldb_asprintf_errstring(ldb, __location__
4722 ": Unable to add a formatted child to dn: %s",
4723 ldb_dn_get_linearized(dn));
4724 return LDB_ERR_OPERATIONS_ERROR;
4727 deleted_child_rdn_val.length =
4728 rdn_value->length + 5
4729 + strlen(guid_str.buf);
4731 SMB_ASSERT(deleted_child_rdn_val.length <
4732 talloc_get_size(deleted_child_rdn_val.data));
4735 * talloc won't allocate more than 256MB so we can't
4736 * overflow but just to be sure
4738 if (deleted_child_rdn_val.length < rdn_value->length) {
4739 return LDB_ERR_OPERATIONS_ERROR;
4742 deleted_child_rdn_val.data[rdn_value->length] = 0x0a;
4743 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 1],
4744 four_char_prefix, 4);
4745 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 5],
4746 guid_str.buf,
4747 sizeof(guid_str.buf));
4749 /* Now set the value into the RDN, without parsing it */
4750 ret = ldb_dn_set_component(
4753 rdn_name,
4754 deleted_child_rdn_val);
4756 return ret;
4761 form a conflict DN
4763 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx,
4764 struct ldb_context *ldb,
4765 struct ldb_dn *dn,
4766 struct GUID *guid)
4768 const struct ldb_val *rdn_val;
4769 const char *rdn_name;
4770 struct ldb_dn *new_dn;
4771 int ret;
4773 rdn_val = ldb_dn_get_rdn_val(dn);
4774 rdn_name = ldb_dn_get_rdn_name(dn);
4775 if (!rdn_val || !rdn_name) {
4776 return NULL;
4779 new_dn = ldb_dn_get_parent(mem_ctx, dn);
4780 if (!new_dn) {
4781 return NULL;
4784 ret = replmd_make_prefix_child_dn(mem_ctx,
4785 ldb, new_dn,
4786 "CNF:",
4787 rdn_name,
4788 rdn_val,
4789 *guid);
4790 if (ret != LDB_SUCCESS) {
4791 return NULL;
4793 return new_dn;
4797 form a deleted DN
4799 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
4800 struct ldb_context *ldb,
4801 struct ldb_dn *dn,
4802 const char *rdn_name,
4803 const struct ldb_val *rdn_value,
4804 struct GUID guid)
4806 return replmd_make_prefix_child_dn(tmp_ctx,
4807 ldb, dn,
4808 "DEL:",
4809 rdn_name,
4810 rdn_value,
4811 guid);
4816 perform a modify operation which sets the rDN and name attributes to
4817 their current values. This has the effect of changing these
4818 attributes to have been last updated by the current DC. This is
4819 needed to ensure that renames performed as part of conflict
4820 resolution are propagated to other DCs
4822 static int replmd_name_modify(struct replmd_replicated_request *ar,
4823 struct ldb_request *req, struct ldb_dn *dn)
4825 struct ldb_message *msg;
4826 const char *rdn_name;
4827 const struct ldb_val *rdn_val;
4828 const struct dsdb_attribute *rdn_attr;
4829 int ret;
4831 msg = ldb_msg_new(req);
4832 if (msg == NULL) {
4833 goto failed;
4835 msg->dn = dn;
4837 rdn_name = ldb_dn_get_rdn_name(dn);
4838 if (rdn_name == NULL) {
4839 goto failed;
4842 /* normalize the rdn attribute name */
4843 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
4844 if (rdn_attr == NULL) {
4845 goto failed;
4847 rdn_name = rdn_attr->lDAPDisplayName;
4849 rdn_val = ldb_dn_get_rdn_val(dn);
4850 if (rdn_val == NULL) {
4851 goto failed;
4854 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4855 goto failed;
4857 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
4858 goto failed;
4860 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4861 goto failed;
4863 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
4864 goto failed;
4868 * We have to mark this as a replicated update otherwise
4869 * schema_data may reject a rename in the schema partition
4872 ret = dsdb_module_modify(ar->module, msg,
4873 DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
4874 req);
4875 if (ret != LDB_SUCCESS) {
4876 DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
4877 ldb_dn_get_linearized(dn),
4878 ldb_errstring(ldb_module_get_ctx(ar->module))));
4879 return ret;
4882 talloc_free(msg);
4884 return LDB_SUCCESS;
4886 failed:
4887 talloc_free(msg);
4888 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
4889 ldb_dn_get_linearized(dn)));
4890 return LDB_ERR_OPERATIONS_ERROR;
4895 callback for conflict DN handling where we have renamed the incoming
4896 record. After renaming it, we need to ensure the change of name and
4897 rDN for the incoming record is seen as an originating update by this DC.
4899 This also handles updating lastKnownParent for entries sent to lostAndFound
4901 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
4903 struct replmd_replicated_request *ar =
4904 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4905 struct ldb_dn *conflict_dn = NULL;
4906 int ret;
4908 if (ares->error != LDB_SUCCESS) {
4909 /* call the normal callback for everything except success */
4910 return replmd_op_callback(req, ares);
4913 switch (req->operation) {
4914 case LDB_ADD:
4915 conflict_dn = req->op.add.message->dn;
4916 break;
4917 case LDB_MODIFY:
4918 conflict_dn = req->op.mod.message->dn;
4919 break;
4920 default:
4921 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
4924 /* perform a modify of the rDN and name of the record */
4925 ret = replmd_name_modify(ar, req, conflict_dn);
4926 if (ret != LDB_SUCCESS) {
4927 ares->error = ret;
4928 return replmd_op_callback(req, ares);
4931 if (ar->objs->objects[ar->index_current].last_known_parent) {
4932 struct ldb_message *msg = ldb_msg_new(req);
4933 if (msg == NULL) {
4934 ldb_module_oom(ar->module);
4935 return LDB_ERR_OPERATIONS_ERROR;
4938 msg->dn = req->op.add.message->dn;
4940 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4941 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
4942 if (ret != LDB_SUCCESS) {
4943 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
4944 ldb_module_oom(ar->module);
4945 return ret;
4947 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
4949 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
4950 if (ret != LDB_SUCCESS) {
4951 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
4952 ldb_dn_get_linearized(msg->dn),
4953 ldb_errstring(ldb_module_get_ctx(ar->module))));
4954 return ret;
4956 TALLOC_FREE(msg);
4959 return replmd_op_callback(req, ares);
4963 callback for replmd_replicated_apply_add()
4964 This copes with the creation of conflict records in the case where
4965 the DN exists, but with a different objectGUID
4967 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))
4969 struct ldb_dn *conflict_dn;
4970 struct replmd_replicated_request *ar =
4971 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4972 struct ldb_result *res;
4973 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4974 int ret;
4975 const struct ldb_val *omd_value;
4976 struct replPropertyMetaDataBlob omd, *rmd;
4977 enum ndr_err_code ndr_err;
4978 bool rename_incoming_record, rodc;
4979 struct replPropertyMetaData1 *rmd_name, *omd_name;
4980 struct ldb_message *msg;
4981 struct ldb_request *down_req = NULL;
4983 /* call the normal callback for success */
4984 if (ares->error == LDB_SUCCESS) {
4985 return callback(req, ares);
4989 * we have a conflict, and need to decide if we will keep the
4990 * new record or the old record
4993 msg = ar->objs->objects[ar->index_current].msg;
4994 conflict_dn = msg->dn;
4996 /* For failures other than conflicts, fail the whole operation here */
4997 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4998 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
4999 ldb_dn_get_linearized(conflict_dn),
5000 ldb_errstring(ldb_module_get_ctx(ar->module)));
5002 return ldb_module_done(ar->req, NULL, NULL,
5003 LDB_ERR_OPERATIONS_ERROR);
5006 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5007 if (ret != LDB_SUCCESS) {
5008 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)));
5009 return ldb_module_done(ar->req, NULL, NULL,
5010 LDB_ERR_OPERATIONS_ERROR);
5014 if (rodc) {
5016 * We are on an RODC, or were a GC for this
5017 * partition, so we have to fail this until
5018 * someone who owns the partition sorts it
5019 * out
5021 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5022 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
5023 " - We must fail the operation until a master for this partition resolves the conflict",
5024 ldb_dn_get_linearized(conflict_dn));
5025 ret = LDB_ERR_OPERATIONS_ERROR;
5026 goto failed;
5030 * first we need the replPropertyMetaData attribute from the
5031 * local, conflicting record
5033 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
5034 attrs,
5035 DSDB_FLAG_NEXT_MODULE |
5036 DSDB_SEARCH_SHOW_DELETED |
5037 DSDB_SEARCH_SHOW_RECYCLED, req);
5038 if (ret != LDB_SUCCESS) {
5039 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5040 ldb_dn_get_linearized(conflict_dn)));
5041 goto failed;
5044 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5045 if (omd_value == NULL) {
5046 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5047 ldb_dn_get_linearized(conflict_dn)));
5048 goto failed;
5051 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5052 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5053 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5054 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5055 ldb_dn_get_linearized(conflict_dn)));
5056 goto failed;
5059 rmd = ar->objs->objects[ar->index_current].meta_data;
5062 * we decide which is newer based on the RPMD on the name
5063 * attribute. See [MS-DRSR] ResolveNameConflict.
5065 * We expect omd_name to be present, as this is from a local
5066 * search, but while rmd_name should have been given to us by
5067 * the remote server, if it is missing we just prefer the
5068 * local name in
5069 * replmd_replPropertyMetaData1_new_should_be_taken()
5071 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5072 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5073 if (!omd_name) {
5074 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5075 ldb_dn_get_linearized(conflict_dn)));
5076 goto failed;
5080 * Should we preserve the current record, and so rename the
5081 * incoming record to be a conflict?
5083 rename_incoming_record
5084 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5085 omd_name, rmd_name);
5087 if (rename_incoming_record) {
5088 struct GUID guid;
5089 struct ldb_dn *new_dn;
5091 guid = samdb_result_guid(msg, "objectGUID");
5092 if (GUID_all_zero(&guid)) {
5093 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
5094 ldb_dn_get_linearized(conflict_dn)));
5095 goto failed;
5097 new_dn = replmd_conflict_dn(req,
5098 ldb_module_get_ctx(ar->module),
5099 conflict_dn, &guid);
5100 if (new_dn == NULL) {
5101 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5102 ldb_dn_get_linearized(conflict_dn)));
5103 goto failed;
5106 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
5107 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5109 /* re-submit the request, but with the new DN */
5110 callback = replmd_op_name_modify_callback;
5111 msg->dn = new_dn;
5112 } else {
5113 /* we are renaming the existing record */
5114 struct GUID guid;
5115 struct ldb_dn *new_dn;
5117 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5118 if (GUID_all_zero(&guid)) {
5119 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5120 ldb_dn_get_linearized(conflict_dn)));
5121 goto failed;
5124 new_dn = replmd_conflict_dn(req,
5125 ldb_module_get_ctx(ar->module),
5126 conflict_dn, &guid);
5127 if (new_dn == NULL) {
5128 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5129 ldb_dn_get_linearized(conflict_dn)));
5130 goto failed;
5133 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5134 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5136 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5137 DSDB_FLAG_OWN_MODULE, req);
5138 if (ret != LDB_SUCCESS) {
5139 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5140 ldb_dn_get_linearized(conflict_dn),
5141 ldb_dn_get_linearized(new_dn),
5142 ldb_errstring(ldb_module_get_ctx(ar->module))));
5143 goto failed;
5147 * now we need to ensure that the rename is seen as an
5148 * originating update. We do that with a modify.
5150 ret = replmd_name_modify(ar, req, new_dn);
5151 if (ret != LDB_SUCCESS) {
5152 goto failed;
5155 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
5156 ldb_dn_get_linearized(req->op.add.message->dn)));
5159 ret = ldb_build_add_req(&down_req,
5160 ldb_module_get_ctx(ar->module),
5161 req,
5162 msg,
5163 ar->controls,
5165 callback,
5166 req);
5167 if (ret != LDB_SUCCESS) {
5168 goto failed;
5170 LDB_REQ_SET_LOCATION(down_req);
5172 /* current partition control needed by "repmd_op_callback" */
5173 ret = ldb_request_add_control(down_req,
5174 DSDB_CONTROL_CURRENT_PARTITION_OID,
5175 false, NULL);
5176 if (ret != LDB_SUCCESS) {
5177 return replmd_replicated_request_error(ar, ret);
5180 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5181 /* this tells the partition module to make it a
5182 partial replica if creating an NC */
5183 ret = ldb_request_add_control(down_req,
5184 DSDB_CONTROL_PARTIAL_REPLICA,
5185 false, NULL);
5186 if (ret != LDB_SUCCESS) {
5187 return replmd_replicated_request_error(ar, ret);
5192 * Finally we re-run the add, otherwise the new record won't
5193 * exist, as we are here because of that exact failure!
5195 return ldb_next_request(ar->module, down_req);
5196 failed:
5198 /* on failure make the caller get the error. This means
5199 * replication will stop with an error, but there is not much
5200 * else we can do.
5202 if (ret == LDB_SUCCESS) {
5203 ret = LDB_ERR_OPERATIONS_ERROR;
5205 return ldb_module_done(ar->req, NULL, NULL,
5206 ret);
5210 callback for replmd_replicated_apply_add()
5211 This copes with the creation of conflict records in the case where
5212 the DN exists, but with a different objectGUID
5214 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
5216 struct replmd_replicated_request *ar =
5217 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5219 if (ar->objs->objects[ar->index_current].last_known_parent) {
5220 /* This is like a conflict DN, where we put the object in LostAndFound
5221 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
5222 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
5225 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
5229 this is called when a new object comes in over DRS
5231 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
5233 struct ldb_context *ldb;
5234 struct ldb_request *change_req;
5235 enum ndr_err_code ndr_err;
5236 struct ldb_message *msg;
5237 struct replPropertyMetaDataBlob *md;
5238 struct ldb_val md_value;
5239 unsigned int i;
5240 int ret;
5241 bool remote_isDeleted = false;
5242 bool is_schema_nc;
5243 NTTIME now;
5244 time_t t = time(NULL);
5245 const struct ldb_val *rdn_val;
5246 struct replmd_private *replmd_private =
5247 talloc_get_type(ldb_module_get_private(ar->module),
5248 struct replmd_private);
5249 unix_to_nt_time(&now, t);
5251 ldb = ldb_module_get_ctx(ar->module);
5252 msg = ar->objs->objects[ar->index_current].msg;
5253 md = ar->objs->objects[ar->index_current].meta_data;
5254 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5256 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5257 if (ret != LDB_SUCCESS) {
5258 return replmd_replicated_request_error(ar, ret);
5261 ret = dsdb_msg_add_guid(msg,
5262 &ar->objs->objects[ar->index_current].object_guid,
5263 "objectGUID");
5264 if (ret != LDB_SUCCESS) {
5265 return replmd_replicated_request_error(ar, ret);
5268 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5269 if (ret != LDB_SUCCESS) {
5270 return replmd_replicated_request_error(ar, ret);
5273 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
5274 if (ret != LDB_SUCCESS) {
5275 return replmd_replicated_request_error(ar, ret);
5278 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5279 if (ret != LDB_SUCCESS) {
5280 return replmd_replicated_request_error(ar, ret);
5283 /* remove any message elements that have zero values */
5284 for (i=0; i<msg->num_elements; i++) {
5285 struct ldb_message_element *el = &msg->elements[i];
5287 if (el->num_values == 0) {
5288 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5289 ldb_asprintf_errstring(ldb, __location__
5290 ": empty objectClass sent on %s, aborting replication\n",
5291 ldb_dn_get_linearized(msg->dn));
5292 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5295 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5296 el->name));
5297 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
5298 msg->num_elements--;
5299 i--;
5300 continue;
5304 if (DEBUGLVL(8)) {
5305 struct GUID_txt_buf guid_txt;
5307 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5308 LDB_CHANGETYPE_ADD,
5309 msg);
5310 DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5311 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5312 s));
5313 talloc_free(s);
5314 } else if (DEBUGLVL(4)) {
5315 struct GUID_txt_buf guid_txt;
5316 DEBUG(4, ("DRS replication add DN of %s is %s\n",
5317 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5318 ldb_dn_get_linearized(msg->dn)));
5320 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5321 "isDeleted", false);
5324 * the meta data array is already sorted by the caller, except
5325 * for the RDN, which needs to be added.
5329 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5330 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5331 md, ar, now, is_schema_nc,
5332 false);
5333 if (ret != LDB_SUCCESS) {
5334 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5335 return replmd_replicated_request_error(ar, ret);
5338 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5339 if (ret != LDB_SUCCESS) {
5340 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5341 return replmd_replicated_request_error(ar, ret);
5344 for (i=0; i < md->ctr.ctr1.count; i++) {
5345 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5347 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5348 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5349 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5350 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5351 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5353 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5354 if (ret != LDB_SUCCESS) {
5355 return replmd_replicated_request_error(ar, ret);
5358 replmd_ldb_message_sort(msg, ar->schema);
5360 if (!remote_isDeleted) {
5361 ret = dsdb_module_schedule_sd_propagation(ar->module,
5362 ar->objs->partition_dn,
5363 msg->dn, true);
5364 if (ret != LDB_SUCCESS) {
5365 return replmd_replicated_request_error(ar, ret);
5369 ar->isDeleted = remote_isDeleted;
5371 ret = ldb_build_add_req(&change_req,
5372 ldb,
5374 msg,
5375 ar->controls,
5377 replmd_op_add_callback,
5378 ar->req);
5379 LDB_REQ_SET_LOCATION(change_req);
5380 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5382 /* current partition control needed by "repmd_op_callback" */
5383 ret = ldb_request_add_control(change_req,
5384 DSDB_CONTROL_CURRENT_PARTITION_OID,
5385 false, NULL);
5386 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5388 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5389 /* this tells the partition module to make it a
5390 partial replica if creating an NC */
5391 ret = ldb_request_add_control(change_req,
5392 DSDB_CONTROL_PARTIAL_REPLICA,
5393 false, NULL);
5394 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5397 return ldb_next_request(ar->module, change_req);
5400 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5401 struct ldb_reply *ares)
5403 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5404 struct replmd_replicated_request);
5405 int ret;
5407 if (!ares) {
5408 return ldb_module_done(ar->req, NULL, NULL,
5409 LDB_ERR_OPERATIONS_ERROR);
5413 * The error NO_SUCH_OBJECT is not expected, unless the search
5414 * base is the partition DN, and that case doesn't happen here
5415 * because then we wouldn't get a parent_guid_value in any
5416 * case.
5418 if (ares->error != LDB_SUCCESS) {
5419 return ldb_module_done(ar->req, ares->controls,
5420 ares->response, ares->error);
5423 switch (ares->type) {
5424 case LDB_REPLY_ENTRY:
5426 struct ldb_message *parent_msg = ares->message;
5427 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5428 struct ldb_dn *parent_dn = NULL;
5429 int comp_num;
5431 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5432 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5433 /* Per MS-DRSR 4.1.10.6.10
5434 * FindBestParentObject we need to move this
5435 * new object under a deleted object to
5436 * lost-and-found */
5437 struct ldb_dn *nc_root;
5439 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5440 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5441 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5442 "No suitable NC root found for %s. "
5443 "We need to move this object because parent object %s "
5444 "is deleted, but this object is not.",
5445 ldb_dn_get_linearized(msg->dn),
5446 ldb_dn_get_linearized(parent_msg->dn));
5447 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5448 } else if (ret != LDB_SUCCESS) {
5449 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5450 "Unable to find NC root for %s: %s. "
5451 "We need to move this object because parent object %s "
5452 "is deleted, but this object is not.",
5453 ldb_dn_get_linearized(msg->dn),
5454 ldb_errstring(ldb_module_get_ctx(ar->module)),
5455 ldb_dn_get_linearized(parent_msg->dn));
5456 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5459 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5460 nc_root,
5461 DS_GUID_LOSTANDFOUND_CONTAINER,
5462 &parent_dn);
5463 if (ret != LDB_SUCCESS) {
5464 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5465 "Unable to find LostAndFound Container for %s "
5466 "in partition %s: %s. "
5467 "We need to move this object because parent object %s "
5468 "is deleted, but this object is not.",
5469 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5470 ldb_errstring(ldb_module_get_ctx(ar->module)),
5471 ldb_dn_get_linearized(parent_msg->dn));
5472 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5474 ar->objs->objects[ar->index_current].last_known_parent
5475 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5477 } else {
5478 parent_dn
5479 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5482 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5484 comp_num = ldb_dn_get_comp_num(msg->dn);
5485 if (comp_num > 1) {
5486 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5487 talloc_free(ares);
5488 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5491 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5492 talloc_free(ares);
5493 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5495 break;
5497 case LDB_REPLY_REFERRAL:
5498 /* we ignore referrals */
5499 break;
5501 case LDB_REPLY_DONE:
5503 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5504 struct GUID_txt_buf str_buf;
5505 if (ar->search_msg != NULL) {
5506 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5507 "No parent with GUID %s found for object locally known as %s",
5508 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5509 ldb_dn_get_linearized(ar->search_msg->dn));
5510 } else {
5511 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5512 "No parent with GUID %s found for object remotely known as %s",
5513 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5514 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5518 * This error code is really important, as it
5519 * is the flag back to the callers to retry
5520 * this with DRSUAPI_DRS_GET_ANC, and so get
5521 * the parent objects before the child
5522 * objects
5524 return ldb_module_done(ar->req, NULL, NULL,
5525 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5528 if (ar->search_msg != NULL) {
5529 ret = replmd_replicated_apply_merge(ar);
5530 } else {
5531 ret = replmd_replicated_apply_add(ar);
5533 if (ret != LDB_SUCCESS) {
5534 return ldb_module_done(ar->req, NULL, NULL, ret);
5538 talloc_free(ares);
5539 return LDB_SUCCESS;
5543 * Look for the parent object, so we put the new object in the right
5544 * place This is akin to NameObject in MS-DRSR - this routine and the
5545 * callbacks find the right parent name, and correct name for this
5546 * object
5549 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5551 struct ldb_context *ldb;
5552 int ret;
5553 char *tmp_str;
5554 char *filter;
5555 struct ldb_request *search_req;
5556 static const char *attrs[] = {"isDeleted", NULL};
5557 struct GUID_txt_buf guid_str_buf;
5559 ldb = ldb_module_get_ctx(ar->module);
5561 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5562 if (ar->search_msg != NULL) {
5563 return replmd_replicated_apply_merge(ar);
5564 } else {
5565 return replmd_replicated_apply_add(ar);
5569 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5570 &guid_str_buf);
5572 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5573 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5575 ret = ldb_build_search_req(&search_req,
5576 ldb,
5578 ar->objs->partition_dn,
5579 LDB_SCOPE_SUBTREE,
5580 filter,
5581 attrs,
5582 NULL,
5584 replmd_replicated_apply_search_for_parent_callback,
5585 ar->req);
5586 LDB_REQ_SET_LOCATION(search_req);
5588 ret = dsdb_request_add_controls(search_req,
5589 DSDB_SEARCH_SHOW_RECYCLED|
5590 DSDB_SEARCH_SHOW_DELETED|
5591 DSDB_SEARCH_SHOW_EXTENDED_DN);
5592 if (ret != LDB_SUCCESS) {
5593 return ret;
5596 return ldb_next_request(ar->module, search_req);
5600 handle renames that come in over DRS replication
5602 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5603 struct ldb_message *msg,
5604 struct ldb_request *parent,
5605 bool *renamed)
5607 int ret;
5608 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5609 struct ldb_result *res;
5610 struct ldb_dn *conflict_dn;
5611 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5612 const struct ldb_val *omd_value;
5613 struct replPropertyMetaDataBlob omd, *rmd;
5614 enum ndr_err_code ndr_err;
5615 bool rename_incoming_record, rodc;
5616 struct replPropertyMetaData1 *rmd_name, *omd_name;
5617 struct ldb_dn *new_dn;
5618 struct GUID guid;
5620 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5621 ldb_dn_get_linearized(ar->search_msg->dn),
5622 ldb_dn_get_linearized(msg->dn)));
5625 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5626 DSDB_FLAG_NEXT_MODULE, ar->req);
5627 if (ret == LDB_SUCCESS) {
5628 talloc_free(tmp_ctx);
5629 *renamed = true;
5630 return ret;
5633 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5634 talloc_free(tmp_ctx);
5635 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5636 ldb_dn_get_linearized(ar->search_msg->dn),
5637 ldb_dn_get_linearized(msg->dn),
5638 ldb_errstring(ldb_module_get_ctx(ar->module)));
5639 return ret;
5642 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5643 if (ret != LDB_SUCCESS) {
5644 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5645 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5646 ldb_errstring(ldb_module_get_ctx(ar->module)));
5647 return LDB_ERR_OPERATIONS_ERROR;
5650 * we have a conflict, and need to decide if we will keep the
5651 * new record or the old record
5654 conflict_dn = msg->dn;
5656 if (rodc) {
5658 * We are on an RODC, or were a GC for this
5659 * partition, so we have to fail this until
5660 * someone who owns the partition sorts it
5661 * out
5663 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5664 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5665 " - We must fail the operation until a master for this partition resolves the conflict",
5666 ldb_dn_get_linearized(conflict_dn));
5667 ret = LDB_ERR_OPERATIONS_ERROR;
5668 goto failed;
5672 * first we need the replPropertyMetaData attribute from the
5673 * old record
5675 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
5676 attrs,
5677 DSDB_FLAG_NEXT_MODULE |
5678 DSDB_SEARCH_SHOW_DELETED |
5679 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5680 if (ret != LDB_SUCCESS) {
5681 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5682 ldb_dn_get_linearized(conflict_dn)));
5683 goto failed;
5686 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5687 if (omd_value == NULL) {
5688 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5689 ldb_dn_get_linearized(conflict_dn)));
5690 goto failed;
5693 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5694 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5695 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5696 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5697 ldb_dn_get_linearized(conflict_dn)));
5698 goto failed;
5701 rmd = ar->objs->objects[ar->index_current].meta_data;
5704 * we decide which is newer based on the RPMD on the name
5705 * attribute. See [MS-DRSR] ResolveNameConflict.
5707 * We expect omd_name to be present, as this is from a local
5708 * search, but while rmd_name should have been given to us by
5709 * the remote server, if it is missing we just prefer the
5710 * local name in
5711 * replmd_replPropertyMetaData1_new_should_be_taken()
5713 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5714 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5715 if (!omd_name) {
5716 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5717 ldb_dn_get_linearized(conflict_dn)));
5718 goto failed;
5722 * Should we preserve the current record, and so rename the
5723 * incoming record to be a conflict?
5725 rename_incoming_record =
5726 !replmd_replPropertyMetaData1_new_should_be_taken(
5727 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5728 omd_name, rmd_name);
5730 if (rename_incoming_record) {
5732 new_dn = replmd_conflict_dn(msg,
5733 ldb_module_get_ctx(ar->module),
5734 msg->dn,
5735 &ar->objs->objects[ar->index_current].object_guid);
5736 if (new_dn == NULL) {
5737 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5738 "Failed to form conflict DN for %s\n",
5739 ldb_dn_get_linearized(msg->dn));
5741 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5744 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
5745 DSDB_FLAG_NEXT_MODULE, ar->req);
5746 if (ret != LDB_SUCCESS) {
5747 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5748 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5749 ldb_dn_get_linearized(conflict_dn),
5750 ldb_dn_get_linearized(ar->search_msg->dn),
5751 ldb_dn_get_linearized(new_dn),
5752 ldb_errstring(ldb_module_get_ctx(ar->module)));
5753 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5756 msg->dn = new_dn;
5757 *renamed = true;
5758 return LDB_SUCCESS;
5761 /* we are renaming the existing record */
5763 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5764 if (GUID_all_zero(&guid)) {
5765 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5766 ldb_dn_get_linearized(conflict_dn)));
5767 goto failed;
5770 new_dn = replmd_conflict_dn(tmp_ctx,
5771 ldb_module_get_ctx(ar->module),
5772 conflict_dn, &guid);
5773 if (new_dn == NULL) {
5774 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5775 ldb_dn_get_linearized(conflict_dn)));
5776 goto failed;
5779 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5780 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5782 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5783 DSDB_FLAG_OWN_MODULE, ar->req);
5784 if (ret != LDB_SUCCESS) {
5785 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5786 ldb_dn_get_linearized(conflict_dn),
5787 ldb_dn_get_linearized(new_dn),
5788 ldb_errstring(ldb_module_get_ctx(ar->module))));
5789 goto failed;
5793 * now we need to ensure that the rename is seen as an
5794 * originating update. We do that with a modify.
5796 ret = replmd_name_modify(ar, ar->req, new_dn);
5797 if (ret != LDB_SUCCESS) {
5798 goto failed;
5801 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
5802 ldb_dn_get_linearized(ar->search_msg->dn),
5803 ldb_dn_get_linearized(msg->dn)));
5806 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5807 DSDB_FLAG_NEXT_MODULE, ar->req);
5808 if (ret != LDB_SUCCESS) {
5809 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
5810 ldb_dn_get_linearized(ar->search_msg->dn),
5811 ldb_dn_get_linearized(msg->dn),
5812 ldb_errstring(ldb_module_get_ctx(ar->module))));
5813 goto failed;
5816 talloc_free(tmp_ctx);
5817 return ret;
5818 failed:
5820 * On failure make the caller get the error
5821 * This means replication will stop with an error,
5822 * but there is not much else we can do. In the
5823 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
5824 * needed.
5826 if (ret == LDB_SUCCESS) {
5827 ret = LDB_ERR_OPERATIONS_ERROR;
5830 talloc_free(tmp_ctx);
5831 return ret;
5835 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
5837 struct ldb_context *ldb;
5838 struct ldb_request *change_req;
5839 enum ndr_err_code ndr_err;
5840 struct ldb_message *msg;
5841 struct replPropertyMetaDataBlob *rmd;
5842 struct replPropertyMetaDataBlob omd;
5843 const struct ldb_val *omd_value;
5844 struct replPropertyMetaDataBlob nmd;
5845 struct ldb_val nmd_value;
5846 struct GUID remote_parent_guid;
5847 unsigned int i;
5848 uint32_t j,ni=0;
5849 unsigned int removed_attrs = 0;
5850 int ret;
5851 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
5852 bool isDeleted = false;
5853 bool local_isDeleted = false;
5854 bool remote_isDeleted = false;
5855 bool take_remote_isDeleted = false;
5856 bool sd_updated = false;
5857 bool renamed = false;
5858 bool is_schema_nc = false;
5859 NTSTATUS nt_status;
5860 const struct ldb_val *old_rdn, *new_rdn;
5861 struct replmd_private *replmd_private =
5862 talloc_get_type(ldb_module_get_private(ar->module),
5863 struct replmd_private);
5864 NTTIME now;
5865 time_t t = time(NULL);
5866 unix_to_nt_time(&now, t);
5868 ldb = ldb_module_get_ctx(ar->module);
5869 msg = ar->objs->objects[ar->index_current].msg;
5871 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5873 rmd = ar->objs->objects[ar->index_current].meta_data;
5874 ZERO_STRUCT(omd);
5875 omd.version = 1;
5877 /* find existing meta data */
5878 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5879 if (omd_value) {
5880 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5881 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5882 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5883 nt_status = ndr_map_error2ntstatus(ndr_err);
5884 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5887 if (omd.version != 1) {
5888 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5892 if (DEBUGLVL(8)) {
5893 struct GUID_txt_buf guid_txt;
5895 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5896 LDB_CHANGETYPE_MODIFY, msg);
5897 DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
5898 "%s\n"
5899 "%s\n",
5900 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5902 ndr_print_struct_string(s,
5903 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5904 "existing replPropertyMetaData",
5905 &omd),
5906 ndr_print_struct_string(s,
5907 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5908 "incoming replPropertyMetaData",
5909 rmd)));
5910 talloc_free(s);
5911 } else if (DEBUGLVL(4)) {
5912 struct GUID_txt_buf guid_txt;
5914 DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
5915 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5916 &guid_txt),
5917 ldb_dn_get_linearized(msg->dn)));
5920 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
5921 "isDeleted", false);
5922 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5923 "isDeleted", false);
5926 * Fill in the remote_parent_guid with the GUID or an all-zero
5927 * GUID.
5929 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
5930 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
5931 } else {
5932 remote_parent_guid = GUID_zero();
5936 * To ensure we follow a complex rename chain around, we have
5937 * to confirm that the DN is the same (mostly to confirm the
5938 * RDN) and the parentGUID is the same.
5940 * This ensures we keep things under the correct parent, which
5941 * replmd_replicated_handle_rename() will do.
5944 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
5945 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
5946 ret = LDB_SUCCESS;
5947 } else {
5949 * handle renames, even just by case that come in over
5950 * DRS. Changes in the parent DN don't hit us here,
5951 * because the search for a parent will clean up those
5952 * components.
5954 * We also have already filtered out the case where
5955 * the peer has an older name to what we have (see
5956 * replmd_replicated_apply_search_callback())
5958 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
5961 if (ret != LDB_SUCCESS) {
5962 ldb_debug(ldb, LDB_DEBUG_FATAL,
5963 "replmd_replicated_request rename %s => %s failed - %s\n",
5964 ldb_dn_get_linearized(ar->search_msg->dn),
5965 ldb_dn_get_linearized(msg->dn),
5966 ldb_errstring(ldb));
5967 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5970 if (renamed == true) {
5972 * Set the callback to one that will fix up the name
5973 * metadata on the new conflict DN
5975 callback = replmd_op_name_modify_callback;
5978 ZERO_STRUCT(nmd);
5979 nmd.version = 1;
5980 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
5981 nmd.ctr.ctr1.array = talloc_array(ar,
5982 struct replPropertyMetaData1,
5983 nmd.ctr.ctr1.count);
5984 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5986 /* first copy the old meta data */
5987 for (i=0; i < omd.ctr.ctr1.count; i++) {
5988 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
5989 ni++;
5992 ar->seq_num = 0;
5993 /* now merge in the new meta data */
5994 for (i=0; i < rmd->ctr.ctr1.count; i++) {
5995 bool found = false;
5997 for (j=0; j < ni; j++) {
5998 bool cmp;
6000 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
6001 continue;
6004 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
6005 ar->objs->dsdb_repl_flags,
6006 &nmd.ctr.ctr1.array[j],
6007 &rmd->ctr.ctr1.array[i]);
6008 if (cmp) {
6009 /* replace the entry */
6010 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
6011 if (ar->seq_num == 0) {
6012 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6013 if (ret != LDB_SUCCESS) {
6014 return replmd_replicated_request_error(ar, ret);
6017 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
6018 switch (nmd.ctr.ctr1.array[j].attid) {
6019 case DRSUAPI_ATTID_ntSecurityDescriptor:
6020 sd_updated = true;
6021 break;
6022 case DRSUAPI_ATTID_isDeleted:
6023 take_remote_isDeleted = true;
6024 break;
6025 default:
6026 break;
6028 found = true;
6029 break;
6032 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
6033 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
6034 msg->elements[i-removed_attrs].name,
6035 ldb_dn_get_linearized(msg->dn),
6036 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
6039 /* we don't want to apply this change so remove the attribute */
6040 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
6041 removed_attrs++;
6043 found = true;
6044 break;
6047 if (found) continue;
6049 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
6050 if (ar->seq_num == 0) {
6051 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6052 if (ret != LDB_SUCCESS) {
6053 return replmd_replicated_request_error(ar, ret);
6056 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
6057 switch (nmd.ctr.ctr1.array[ni].attid) {
6058 case DRSUAPI_ATTID_ntSecurityDescriptor:
6059 sd_updated = true;
6060 break;
6061 case DRSUAPI_ATTID_isDeleted:
6062 take_remote_isDeleted = true;
6063 break;
6064 default:
6065 break;
6067 ni++;
6071 * finally correct the size of the meta_data array
6073 nmd.ctr.ctr1.count = ni;
6075 new_rdn = ldb_dn_get_rdn_val(msg->dn);
6076 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
6078 if (renamed) {
6079 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
6080 &nmd, ar, now, is_schema_nc,
6081 false);
6082 if (ret != LDB_SUCCESS) {
6083 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6084 return replmd_replicated_request_error(ar, ret);
6088 * sort the new meta data array
6090 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
6091 if (ret != LDB_SUCCESS) {
6092 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6093 return ret;
6097 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
6098 * UpdateObject.
6100 * This also controls SD propagation below
6102 if (take_remote_isDeleted) {
6103 isDeleted = remote_isDeleted;
6104 } else {
6105 isDeleted = local_isDeleted;
6108 ar->isDeleted = isDeleted;
6111 * check if some replicated attributes left, otherwise skip the ldb_modify() call
6113 if (msg->num_elements == 0) {
6114 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
6115 ar->index_current);
6117 return replmd_replicated_apply_isDeleted(ar);
6120 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
6121 ar->index_current, msg->num_elements);
6123 if (renamed) {
6124 sd_updated = true;
6127 if (sd_updated && !isDeleted) {
6128 ret = dsdb_module_schedule_sd_propagation(ar->module,
6129 ar->objs->partition_dn,
6130 msg->dn, true);
6131 if (ret != LDB_SUCCESS) {
6132 return ldb_operr(ldb);
6136 /* create the meta data value */
6137 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
6138 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
6139 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6140 nt_status = ndr_map_error2ntstatus(ndr_err);
6141 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6145 * when we know that we'll modify the record, add the whenChanged, uSNChanged
6146 * and replPopertyMetaData attributes
6148 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
6149 if (ret != LDB_SUCCESS) {
6150 return replmd_replicated_request_error(ar, ret);
6152 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
6153 if (ret != LDB_SUCCESS) {
6154 return replmd_replicated_request_error(ar, ret);
6156 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
6157 if (ret != LDB_SUCCESS) {
6158 return replmd_replicated_request_error(ar, ret);
6161 replmd_ldb_message_sort(msg, ar->schema);
6163 /* we want to replace the old values */
6164 for (i=0; i < msg->num_elements; i++) {
6165 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
6166 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
6167 if (msg->elements[i].num_values == 0) {
6168 ldb_asprintf_errstring(ldb, __location__
6169 ": objectClass removed on %s, aborting replication\n",
6170 ldb_dn_get_linearized(msg->dn));
6171 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
6176 if (DEBUGLVL(8)) {
6177 struct GUID_txt_buf guid_txt;
6179 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6180 LDB_CHANGETYPE_MODIFY,
6181 msg);
6182 DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
6183 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6184 &guid_txt),
6185 s));
6186 talloc_free(s);
6187 } else if (DEBUGLVL(4)) {
6188 struct GUID_txt_buf guid_txt;
6190 DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
6191 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6192 &guid_txt),
6193 ldb_dn_get_linearized(msg->dn)));
6196 ret = ldb_build_mod_req(&change_req,
6197 ldb,
6199 msg,
6200 ar->controls,
6202 callback,
6203 ar->req);
6204 LDB_REQ_SET_LOCATION(change_req);
6205 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6207 /* current partition control needed by "repmd_op_callback" */
6208 ret = ldb_request_add_control(change_req,
6209 DSDB_CONTROL_CURRENT_PARTITION_OID,
6210 false, NULL);
6211 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6213 return ldb_next_request(ar->module, change_req);
6216 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
6217 struct ldb_reply *ares)
6219 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6220 struct replmd_replicated_request);
6221 int ret;
6223 if (!ares) {
6224 return ldb_module_done(ar->req, NULL, NULL,
6225 LDB_ERR_OPERATIONS_ERROR);
6227 if (ares->error != LDB_SUCCESS &&
6228 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6229 return ldb_module_done(ar->req, ares->controls,
6230 ares->response, ares->error);
6233 switch (ares->type) {
6234 case LDB_REPLY_ENTRY:
6235 ar->search_msg = talloc_steal(ar, ares->message);
6236 break;
6238 case LDB_REPLY_REFERRAL:
6239 /* we ignore referrals */
6240 break;
6242 case LDB_REPLY_DONE:
6244 struct replPropertyMetaData1 *md_remote;
6245 struct replPropertyMetaData1 *md_local;
6247 struct replPropertyMetaDataBlob omd;
6248 const struct ldb_val *omd_value;
6249 struct replPropertyMetaDataBlob *rmd;
6250 struct ldb_message *msg;
6251 int instanceType;
6252 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
6253 ar->objs->objects[ar->index_current].last_known_parent = NULL;
6256 * This is the ADD case, find the appropriate parent,
6257 * as this object doesn't exist locally:
6259 if (ar->search_msg == NULL) {
6260 ret = replmd_replicated_apply_search_for_parent(ar);
6261 if (ret != LDB_SUCCESS) {
6262 return ldb_module_done(ar->req, NULL, NULL, ret);
6264 talloc_free(ares);
6265 return LDB_SUCCESS;
6269 * Otherwise, in the MERGE case, work out if we are
6270 * attempting a rename, and if so find the parent the
6271 * newly renamed object wants to belong under (which
6272 * may not be the parent in it's attached string DN
6274 rmd = ar->objs->objects[ar->index_current].meta_data;
6275 ZERO_STRUCT(omd);
6276 omd.version = 1;
6278 /* find existing meta data */
6279 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6280 if (omd_value) {
6281 enum ndr_err_code ndr_err;
6282 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6283 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6284 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6285 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6286 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6289 if (omd.version != 1) {
6290 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6294 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6296 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6297 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6298 && GUID_all_zero(&ar->local_parent_guid)) {
6299 DEBUG(0, ("Refusing to replicate new version of %s "
6300 "as local object has an all-zero parentGUID attribute, "
6301 "despite not being an NC root\n",
6302 ldb_dn_get_linearized(ar->search_msg->dn)));
6303 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6307 * now we need to check for double renames. We could have a
6308 * local rename pending which our replication partner hasn't
6309 * received yet. We choose which one wins by looking at the
6310 * attribute stamps on the two objects, the newer one wins.
6312 * This also simply applies the correct algorithms for
6313 * determining if a change was made to name at all, or
6314 * if the object has just been renamed under the same
6315 * parent.
6317 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6318 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6319 if (!md_local) {
6320 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6321 ldb_dn_get_linearized(ar->search_msg->dn)));
6322 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6326 * if there is no name attribute given then we have to assume the
6327 * object we've received has the older name
6329 if (replmd_replPropertyMetaData1_new_should_be_taken(
6330 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6331 md_local, md_remote)) {
6332 struct GUID_txt_buf p_guid_local;
6333 struct GUID_txt_buf p_guid_remote;
6334 msg = ar->objs->objects[ar->index_current].msg;
6336 /* Merge on the existing object, with rename */
6338 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6339 "as incoming object changing to %s under %s\n",
6340 ldb_dn_get_linearized(ar->search_msg->dn),
6341 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6342 ldb_dn_get_linearized(msg->dn),
6343 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6344 &p_guid_remote)));
6345 ret = replmd_replicated_apply_search_for_parent(ar);
6346 } else {
6347 struct GUID_txt_buf p_guid_local;
6348 struct GUID_txt_buf p_guid_remote;
6349 msg = ar->objs->objects[ar->index_current].msg;
6352 * Merge on the existing object, force no
6353 * rename (code below just to explain why in
6354 * the DEBUG() logs)
6357 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6358 ldb_dn_get_linearized(msg->dn)) == 0) {
6359 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6360 GUID_equal(&ar->local_parent_guid,
6361 ar->objs->objects[ar->index_current].parent_guid)
6362 == false) {
6363 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6364 "despite incoming object changing parent to %s\n",
6365 ldb_dn_get_linearized(ar->search_msg->dn),
6366 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6367 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6368 &p_guid_remote)));
6370 } else {
6371 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6372 " and rejecting older rename to %s under %s\n",
6373 ldb_dn_get_linearized(ar->search_msg->dn),
6374 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6375 ldb_dn_get_linearized(msg->dn),
6376 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6377 &p_guid_remote)));
6380 * This assignment ensures that the strcmp()
6381 * and GUID_equal() calls in
6382 * replmd_replicated_apply_merge() avoids the
6383 * rename call
6385 ar->objs->objects[ar->index_current].parent_guid =
6386 &ar->local_parent_guid;
6388 msg->dn = ar->search_msg->dn;
6389 ret = replmd_replicated_apply_merge(ar);
6391 if (ret != LDB_SUCCESS) {
6392 return ldb_module_done(ar->req, NULL, NULL, ret);
6397 talloc_free(ares);
6398 return LDB_SUCCESS;
6402 * Stores the linked attributes received in the replication chunk - these get
6403 * applied at the end of the transaction. We also check that each linked
6404 * attribute is valid, i.e. source and target objects are known.
6406 static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
6408 int ret = LDB_SUCCESS;
6409 uint32_t i;
6410 struct ldb_module *module = ar->module;
6411 struct replmd_private *replmd_private =
6412 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6413 struct ldb_context *ldb;
6415 ldb = ldb_module_get_ctx(module);
6417 DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
6419 /* save away the linked attributes for the end of the transaction */
6420 for (i = 0; i < ar->objs->linked_attributes_count; i++) {
6421 struct la_entry *la_entry;
6423 if (replmd_private->la_ctx == NULL) {
6424 replmd_private->la_ctx = talloc_new(replmd_private);
6426 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6427 if (la_entry == NULL) {
6428 ldb_oom(ldb);
6429 return LDB_ERR_OPERATIONS_ERROR;
6431 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
6432 if (la_entry->la == NULL) {
6433 talloc_free(la_entry);
6434 ldb_oom(ldb);
6435 return LDB_ERR_OPERATIONS_ERROR;
6437 *la_entry->la = ar->objs->linked_attributes[i];
6438 la_entry->dsdb_repl_flags = ar->objs->dsdb_repl_flags;
6440 /* we need to steal the non-scalars so they stay
6441 around until the end of the transaction */
6442 talloc_steal(la_entry->la, la_entry->la->identifier);
6443 talloc_steal(la_entry->la, la_entry->la->value.blob);
6445 ret = replmd_verify_linked_attribute(ar, la_entry);
6447 if (ret != LDB_SUCCESS) {
6448 break;
6451 DLIST_ADD(replmd_private->la_list, la_entry);
6454 return ret;
6457 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6459 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6461 struct ldb_context *ldb;
6462 int ret;
6463 char *tmp_str;
6464 char *filter;
6465 struct ldb_request *search_req;
6466 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6467 "parentGUID", "instanceType",
6468 "replPropertyMetaData", "nTSecurityDescriptor",
6469 "isDeleted", NULL };
6470 struct GUID_txt_buf guid_str_buf;
6472 if (ar->index_current >= ar->objs->num_objects) {
6475 * Now that we've applied all the objects, check the new linked
6476 * attributes and store them (we apply them in .prepare_commit)
6478 ret = replmd_store_linked_attributes(ar);
6480 if (ret != LDB_SUCCESS) {
6481 return ret;
6484 /* done applying objects, move on to the next stage */
6485 return replmd_replicated_uptodate_vector(ar);
6488 ldb = ldb_module_get_ctx(ar->module);
6489 ar->search_msg = NULL;
6490 ar->isDeleted = false;
6492 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6493 &guid_str_buf);
6495 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6496 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6498 ret = ldb_build_search_req(&search_req,
6499 ldb,
6501 ar->objs->partition_dn,
6502 LDB_SCOPE_SUBTREE,
6503 filter,
6504 attrs,
6505 NULL,
6507 replmd_replicated_apply_search_callback,
6508 ar->req);
6509 LDB_REQ_SET_LOCATION(search_req);
6511 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
6513 if (ret != LDB_SUCCESS) {
6514 return ret;
6517 return ldb_next_request(ar->module, search_req);
6521 * This is essentially a wrapper for replmd_replicated_apply_next()
6523 * This is needed to ensure that both codepaths call this handler.
6525 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6527 struct ldb_dn *deleted_objects_dn;
6528 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6529 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
6530 &deleted_objects_dn);
6531 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
6533 * Do a delete here again, so that if there is
6534 * anything local that conflicts with this
6535 * object being deleted, it is removed. This
6536 * includes links. See MS-DRSR 4.1.10.6.9
6537 * UpdateObject.
6539 * If the object is already deleted, and there
6540 * is no more work required, it doesn't do
6541 * anything.
6544 /* This has been updated to point to the DN we eventually did the modify on */
6546 struct ldb_request *del_req;
6547 struct ldb_result *res;
6549 TALLOC_CTX *tmp_ctx = talloc_new(ar);
6550 if (!tmp_ctx) {
6551 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6552 return ret;
6555 res = talloc_zero(tmp_ctx, struct ldb_result);
6556 if (!res) {
6557 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6558 talloc_free(tmp_ctx);
6559 return ret;
6562 /* Build a delete request, which hopefully will artually turn into nothing */
6563 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
6564 msg->dn,
6565 NULL,
6566 res,
6567 ldb_modify_default_callback,
6568 ar->req);
6569 LDB_REQ_SET_LOCATION(del_req);
6570 if (ret != LDB_SUCCESS) {
6571 talloc_free(tmp_ctx);
6572 return ret;
6576 * This is the guts of the call, call back
6577 * into our delete code, but setting the
6578 * re_delete flag so we delete anything that
6579 * shouldn't be there on a deleted or recycled
6580 * object
6582 ret = replmd_delete_internals(ar->module, del_req, true);
6583 if (ret == LDB_SUCCESS) {
6584 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
6587 talloc_free(tmp_ctx);
6588 if (ret != LDB_SUCCESS) {
6589 return ret;
6593 ar->index_current++;
6594 return replmd_replicated_apply_next(ar);
6597 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
6598 struct ldb_reply *ares)
6600 struct ldb_context *ldb;
6601 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6602 struct replmd_replicated_request);
6603 ldb = ldb_module_get_ctx(ar->module);
6605 if (!ares) {
6606 return ldb_module_done(ar->req, NULL, NULL,
6607 LDB_ERR_OPERATIONS_ERROR);
6609 if (ares->error != LDB_SUCCESS) {
6610 return ldb_module_done(ar->req, ares->controls,
6611 ares->response, ares->error);
6614 if (ares->type != LDB_REPLY_DONE) {
6615 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
6616 return ldb_module_done(ar->req, NULL, NULL,
6617 LDB_ERR_OPERATIONS_ERROR);
6620 talloc_free(ares);
6622 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6625 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
6627 struct ldb_context *ldb;
6628 struct ldb_request *change_req;
6629 enum ndr_err_code ndr_err;
6630 struct ldb_message *msg;
6631 struct replUpToDateVectorBlob ouv;
6632 const struct ldb_val *ouv_value;
6633 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
6634 struct replUpToDateVectorBlob nuv;
6635 struct ldb_val nuv_value;
6636 struct ldb_message_element *nuv_el = NULL;
6637 struct ldb_message_element *orf_el = NULL;
6638 struct repsFromToBlob nrf;
6639 struct ldb_val *nrf_value = NULL;
6640 struct ldb_message_element *nrf_el = NULL;
6641 unsigned int i;
6642 uint32_t j,ni=0;
6643 bool found = false;
6644 time_t t = time(NULL);
6645 NTTIME now;
6646 int ret;
6647 uint32_t instanceType;
6649 ldb = ldb_module_get_ctx(ar->module);
6650 ruv = ar->objs->uptodateness_vector;
6651 ZERO_STRUCT(ouv);
6652 ouv.version = 2;
6653 ZERO_STRUCT(nuv);
6654 nuv.version = 2;
6656 unix_to_nt_time(&now, t);
6658 if (ar->search_msg == NULL) {
6659 /* this happens for a REPL_OBJ call where we are
6660 creating the target object by replicating it. The
6661 subdomain join code does this for the partition DN
6663 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
6664 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6667 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
6668 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
6669 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
6670 ldb_dn_get_linearized(ar->search_msg->dn)));
6671 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6675 * first create the new replUpToDateVector
6677 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
6678 if (ouv_value) {
6679 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
6680 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
6681 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6682 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6683 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6686 if (ouv.version != 2) {
6687 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6692 * the new uptodateness vector will at least
6693 * contain 1 entry, one for the source_dsa
6695 * plus optional values from our old vector and the one from the source_dsa
6697 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
6698 if (ruv) nuv.ctr.ctr2.count += ruv->count;
6699 nuv.ctr.ctr2.cursors = talloc_array(ar,
6700 struct drsuapi_DsReplicaCursor2,
6701 nuv.ctr.ctr2.count);
6702 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6704 /* first copy the old vector */
6705 for (i=0; i < ouv.ctr.ctr2.count; i++) {
6706 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
6707 ni++;
6710 /* merge in the source_dsa vector is available */
6711 for (i=0; (ruv && i < ruv->count); i++) {
6712 found = false;
6714 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6715 &ar->our_invocation_id)) {
6716 continue;
6719 for (j=0; j < ni; j++) {
6720 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6721 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
6722 continue;
6725 found = true;
6727 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
6728 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
6730 break;
6733 if (found) continue;
6735 /* if it's not there yet, add it */
6736 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
6737 ni++;
6741 * finally correct the size of the cursors array
6743 nuv.ctr.ctr2.count = ni;
6746 * sort the cursors
6748 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
6751 * create the change ldb_message
6753 msg = ldb_msg_new(ar);
6754 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6755 msg->dn = ar->search_msg->dn;
6757 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
6758 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
6759 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6760 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6761 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6763 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
6764 if (ret != LDB_SUCCESS) {
6765 return replmd_replicated_request_error(ar, ret);
6767 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
6770 * now create the new repsFrom value from the given repsFromTo1 structure
6772 ZERO_STRUCT(nrf);
6773 nrf.version = 1;
6774 nrf.ctr.ctr1 = *ar->objs->source_dsa;
6775 nrf.ctr.ctr1.last_attempt = now;
6776 nrf.ctr.ctr1.last_success = now;
6777 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
6780 * first see if we already have a repsFrom value for the current source dsa
6781 * if so we'll later replace this value
6783 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
6784 if (orf_el) {
6785 for (i=0; i < orf_el->num_values; i++) {
6786 struct repsFromToBlob *trf;
6788 trf = talloc(ar, struct repsFromToBlob);
6789 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6791 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
6792 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
6793 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6794 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6795 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6798 if (trf->version != 1) {
6799 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6803 * we compare the source dsa objectGUID not the invocation_id
6804 * because we want only one repsFrom value per source dsa
6805 * and when the invocation_id of the source dsa has changed we don't need
6806 * the old repsFrom with the old invocation_id
6808 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
6809 &ar->objs->source_dsa->source_dsa_obj_guid)) {
6810 talloc_free(trf);
6811 continue;
6814 talloc_free(trf);
6815 nrf_value = &orf_el->values[i];
6816 break;
6820 * copy over all old values to the new ldb_message
6822 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
6823 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6824 *nrf_el = *orf_el;
6828 * if we haven't found an old repsFrom value for the current source dsa
6829 * we'll add a new value
6831 if (!nrf_value) {
6832 struct ldb_val zero_value;
6833 ZERO_STRUCT(zero_value);
6834 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
6835 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6837 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
6840 /* we now fill the value which is already attached to ldb_message */
6841 ndr_err = ndr_push_struct_blob(nrf_value, msg,
6842 &nrf,
6843 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
6844 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6845 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6846 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6850 * the ldb_message_element for the attribute, has all the old values and the new one
6851 * so we'll replace the whole attribute with all values
6853 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
6855 if (CHECK_DEBUGLVL(4)) {
6856 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6857 LDB_CHANGETYPE_MODIFY,
6858 msg);
6859 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
6860 talloc_free(s);
6863 /* prepare the ldb_modify() request */
6864 ret = ldb_build_mod_req(&change_req,
6865 ldb,
6867 msg,
6868 ar->controls,
6870 replmd_replicated_uptodate_modify_callback,
6871 ar->req);
6872 LDB_REQ_SET_LOCATION(change_req);
6873 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6875 return ldb_next_request(ar->module, change_req);
6878 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
6879 struct ldb_reply *ares)
6881 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6882 struct replmd_replicated_request);
6883 int ret;
6885 if (!ares) {
6886 return ldb_module_done(ar->req, NULL, NULL,
6887 LDB_ERR_OPERATIONS_ERROR);
6889 if (ares->error != LDB_SUCCESS &&
6890 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6891 return ldb_module_done(ar->req, ares->controls,
6892 ares->response, ares->error);
6895 switch (ares->type) {
6896 case LDB_REPLY_ENTRY:
6897 ar->search_msg = talloc_steal(ar, ares->message);
6898 break;
6900 case LDB_REPLY_REFERRAL:
6901 /* we ignore referrals */
6902 break;
6904 case LDB_REPLY_DONE:
6905 ret = replmd_replicated_uptodate_modify(ar);
6906 if (ret != LDB_SUCCESS) {
6907 return ldb_module_done(ar->req, NULL, NULL, ret);
6911 talloc_free(ares);
6912 return LDB_SUCCESS;
6916 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
6918 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
6919 struct replmd_private *replmd_private =
6920 talloc_get_type_abort(ldb_module_get_private(ar->module),
6921 struct replmd_private);
6922 int ret;
6923 static const char *attrs[] = {
6924 "replUpToDateVector",
6925 "repsFrom",
6926 "instanceType",
6927 NULL
6929 struct ldb_request *search_req;
6931 ar->search_msg = NULL;
6934 * Let the caller know that we did an originating updates
6936 ar->objs->originating_updates = replmd_private->originating_updates;
6938 ret = ldb_build_search_req(&search_req,
6939 ldb,
6941 ar->objs->partition_dn,
6942 LDB_SCOPE_BASE,
6943 "(objectClass=*)",
6944 attrs,
6945 NULL,
6947 replmd_replicated_uptodate_search_callback,
6948 ar->req);
6949 LDB_REQ_SET_LOCATION(search_req);
6950 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6952 return ldb_next_request(ar->module, search_req);
6957 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
6959 struct ldb_context *ldb;
6960 struct dsdb_extended_replicated_objects *objs;
6961 struct replmd_replicated_request *ar;
6962 struct ldb_control **ctrls;
6963 int ret;
6965 ldb = ldb_module_get_ctx(module);
6967 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
6969 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
6970 if (!objs) {
6971 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
6972 return LDB_ERR_PROTOCOL_ERROR;
6975 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
6976 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
6977 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
6978 return LDB_ERR_PROTOCOL_ERROR;
6981 ar = replmd_ctx_init(module, req);
6982 if (!ar)
6983 return LDB_ERR_OPERATIONS_ERROR;
6985 /* Set the flags to have the replmd_op_callback run over the full set of objects */
6986 ar->apply_mode = true;
6987 ar->objs = objs;
6988 ar->schema = dsdb_get_schema(ldb, ar);
6989 if (!ar->schema) {
6990 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
6991 talloc_free(ar);
6992 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
6993 return LDB_ERR_CONSTRAINT_VIOLATION;
6996 ctrls = req->controls;
6998 if (req->controls) {
6999 req->controls = talloc_memdup(ar, req->controls,
7000 talloc_get_size(req->controls));
7001 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7004 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
7005 if (ret != LDB_SUCCESS) {
7006 return ret;
7009 /* If this change contained linked attributes in the body
7010 * (rather than in the links section) we need to update
7011 * backlinks in linked_attributes */
7012 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
7013 if (ret != LDB_SUCCESS) {
7014 return ret;
7017 ar->controls = req->controls;
7018 req->controls = ctrls;
7020 return replmd_replicated_apply_next(ar);
7024 * Checks how to handle an missing target - either we need to fail the
7025 * replication and retry with GET_TGT, ignore the link and continue, or try to
7026 * add a partial link to an unknown target.
7028 static int replmd_allow_missing_target(struct ldb_module *module,
7029 TALLOC_CTX *mem_ctx,
7030 struct ldb_dn *target_dn,
7031 struct ldb_dn *source_dn,
7032 bool is_obj_commit,
7033 struct GUID *guid,
7034 uint32_t dsdb_repl_flags,
7035 bool *ignore_link,
7036 const char * missing_str)
7038 struct ldb_context *ldb = ldb_module_get_ctx(module);
7039 bool is_in_same_nc;
7042 * we may not be able to resolve link targets properly when
7043 * dealing with subsets of objects, e.g. the source is a
7044 * critical object and the target isn't
7046 * TODO:
7047 * When we implement Trusted Domains we need to consider
7048 * whether they get treated as an incomplete replica here or not
7050 if (dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
7053 * Ignore the link. We don't increase the highwater-mark in
7054 * the object subset cases, so subsequent replications should
7055 * resolve any missing links
7057 DEBUG(2, ("%s target %s linked from %s\n", missing_str,
7058 ldb_dn_get_linearized(target_dn),
7059 ldb_dn_get_linearized(source_dn)));
7060 *ignore_link = true;
7061 return LDB_SUCCESS;
7064 if (dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
7067 * target should already be up-to-date so there's no point in
7068 * retrying. This could be due to bad timing, or if a target
7069 * on a one-way link was deleted. We ignore the link rather
7070 * than failing the replication cycle completely
7072 *ignore_link = true;
7073 DBG_WARNING("%s is %s but up to date. Ignoring link from %s\n",
7074 ldb_dn_get_linearized(target_dn), missing_str,
7075 ldb_dn_get_linearized(source_dn));
7076 return LDB_SUCCESS;
7079 is_in_same_nc = dsdb_objects_have_same_nc(ldb,
7080 mem_ctx,
7081 source_dn,
7082 target_dn);
7083 if (is_in_same_nc) {
7084 /* fail the replication and retry with GET_TGT */
7085 ldb_asprintf_errstring(ldb, "%s target %s GUID %s linked from %s\n",
7086 missing_str,
7087 ldb_dn_get_linearized(target_dn),
7088 GUID_string(mem_ctx, guid),
7089 ldb_dn_get_linearized(source_dn));
7090 return LDB_ERR_NO_SUCH_OBJECT;
7094 * The target of the cross-partition link is missing. Continue
7095 * and try to at least add the forward-link. This isn't great,
7096 * but a partial link can be fixed by dbcheck, so it's better
7097 * than dropping the link completely.
7099 *ignore_link = false;
7101 if (is_obj_commit) {
7104 * Only log this when we're actually committing the objects.
7105 * This avoids spurious logs, i.e. if we're just verifying the
7106 * received link during a join.
7108 DBG_WARNING("%s cross-partition target %s linked from %s\n",
7109 missing_str, ldb_dn_get_linearized(target_dn),
7110 ldb_dn_get_linearized(source_dn));
7113 return LDB_SUCCESS;
7117 * Checks that the target object for a linked attribute exists.
7118 * @param guid returns the target object's GUID (is returned)if it exists)
7119 * @param ignore_link set to true if the linked attribute should be ignored
7120 * (i.e. the target doesn't exist, but that it's OK to skip the link)
7122 static int replmd_check_target_exists(struct ldb_module *module,
7123 struct dsdb_dn *dsdb_dn,
7124 struct la_entry *la_entry,
7125 struct ldb_dn *source_dn,
7126 bool is_obj_commit,
7127 struct GUID *guid,
7128 bool *ignore_link)
7130 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7131 struct ldb_context *ldb = ldb_module_get_ctx(module);
7132 struct ldb_result *target_res;
7133 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7134 const char *attrs[] = { "isDeleted", "isRecycled", NULL };
7135 NTSTATUS ntstatus;
7136 int ret;
7137 enum deletion_state target_deletion_state = OBJECT_REMOVED;
7138 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
7140 *ignore_link = false;
7141 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
7143 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
7146 * This strange behaviour (allowing a NULL/missing
7147 * GUID) originally comes from:
7149 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
7150 * Author: Andrew Tridgell <tridge@samba.org>
7151 * Date: Mon Dec 21 21:21:55 2009 +1100
7153 * s4-drs: cope better with NULL GUIDS from DRS
7155 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
7156 * need to match by DN if possible when seeing if we should update an
7157 * existing link.
7159 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
7161 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
7162 dsdb_dn->dn, attrs,
7163 DSDB_FLAG_NEXT_MODULE |
7164 DSDB_SEARCH_SHOW_RECYCLED |
7165 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7166 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7167 NULL);
7168 } else if (!NT_STATUS_IS_OK(ntstatus)) {
7169 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
7170 la->attid,
7171 ldb_dn_get_linearized(dsdb_dn->dn),
7172 ldb_dn_get_linearized(source_dn));
7173 talloc_free(tmp_ctx);
7174 return LDB_ERR_OPERATIONS_ERROR;
7175 } else {
7176 ret = dsdb_module_search(module, tmp_ctx, &target_res,
7177 NULL, LDB_SCOPE_SUBTREE,
7178 attrs,
7179 DSDB_FLAG_NEXT_MODULE |
7180 DSDB_SEARCH_SHOW_RECYCLED |
7181 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7182 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7183 NULL,
7184 "objectGUID=%s",
7185 GUID_string(tmp_ctx, guid));
7188 if (ret != LDB_SUCCESS) {
7189 ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
7190 GUID_string(tmp_ctx, guid),
7191 ldb_errstring(ldb));
7192 talloc_free(tmp_ctx);
7193 return ret;
7196 if (target_res->count == 0) {
7199 * target object is unknown. Check whether to ignore the link,
7200 * fail the replication, or add a partial link
7202 ret = replmd_allow_missing_target(module, tmp_ctx, dsdb_dn->dn,
7203 source_dn, is_obj_commit, guid,
7204 la_entry->dsdb_repl_flags,
7205 ignore_link, "Unknown");
7207 } else if (target_res->count != 1) {
7208 ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
7209 GUID_string(tmp_ctx, guid));
7210 ret = LDB_ERR_OPERATIONS_ERROR;
7211 } else {
7212 struct ldb_message *target_msg = target_res->msgs[0];
7214 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
7216 /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
7217 replmd_deletion_state(module, target_msg,
7218 &target_deletion_state, NULL);
7221 * Check for deleted objects as per MS-DRSR 4.1.10.6.14
7222 * ProcessLinkValue(). Link updates should not be sent for
7223 * recycled and tombstone objects (deleting the links should
7224 * happen when we delete the object). This probably means our
7225 * copy of the target object isn't up to date.
7227 if (target_deletion_state >= OBJECT_RECYCLED) {
7230 * target object is deleted. Check whether to ignore the
7231 * link, fail the replication, or add a partial link
7233 ret = replmd_allow_missing_target(module, tmp_ctx,
7234 dsdb_dn->dn, source_dn,
7235 is_obj_commit, guid,
7236 la_entry->dsdb_repl_flags,
7237 ignore_link, "Deleted");
7241 talloc_free(tmp_ctx);
7242 return ret;
7246 * Extracts the key details about the source/target object for a
7247 * linked-attribute entry.
7248 * This returns the following details:
7249 * @param ret_attr the schema details for the linked attribute
7250 * @param source_msg the search result for the source object
7251 * @param target_dsdb_dn the unpacked DN info for the target object
7253 static int replmd_extract_la_entry_details(struct ldb_module *module,
7254 struct la_entry *la_entry,
7255 TALLOC_CTX *mem_ctx,
7256 const struct dsdb_attribute **ret_attr,
7257 struct ldb_message **source_msg,
7258 struct dsdb_dn **target_dsdb_dn)
7260 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7261 struct ldb_context *ldb = ldb_module_get_ctx(module);
7262 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7263 int ret;
7264 const struct dsdb_attribute *attr;
7265 WERROR status;
7266 struct ldb_result *res;
7267 const char *attrs[4];
7270 linked_attributes[0]:
7271 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
7272 identifier : *
7273 identifier: struct drsuapi_DsReplicaObjectIdentifier
7274 __ndr_size : 0x0000003a (58)
7275 __ndr_size_sid : 0x00000000 (0)
7276 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7277 sid : S-0-0
7278 __ndr_size_dn : 0x00000000 (0)
7279 dn : ''
7280 attid : DRSUAPI_ATTID_member (0x1F)
7281 value: struct drsuapi_DsAttributeValue
7282 __ndr_size : 0x0000007e (126)
7283 blob : *
7284 blob : DATA_BLOB length=126
7285 flags : 0x00000001 (1)
7286 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7287 originating_add_time : Wed Sep 2 22:20:01 2009 EST
7288 meta_data: struct drsuapi_DsReplicaMetaData
7289 version : 0x00000015 (21)
7290 originating_change_time : Wed Sep 2 23:39:07 2009 EST
7291 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7292 originating_usn : 0x000000000001e19c (123292)
7294 (for cases where the link is to a normal DN)
7295 &target: struct drsuapi_DsReplicaObjectIdentifier3
7296 __ndr_size : 0x0000007e (126)
7297 __ndr_size_sid : 0x0000001c (28)
7298 guid : 7639e594-db75-4086-b0d4-67890ae46031
7299 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7300 __ndr_size_dn : 0x00000022 (34)
7301 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7304 /* find the attribute being modified */
7305 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
7306 if (attr == NULL) {
7307 struct GUID_txt_buf guid_str;
7308 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7309 la->attid,
7310 GUID_buf_string(&la->identifier->guid,
7311 &guid_str));
7312 return LDB_ERR_OPERATIONS_ERROR;
7316 * All attributes listed here must be dealt with in some way
7317 * by replmd_process_linked_attribute() otherwise in the case
7318 * of isDeleted: FALSE the modify will fail with:
7320 * Failed to apply linked attribute change 'attribute 'isDeleted':
7321 * invalid modify flags on
7322 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
7323 * 0x0'
7325 * This is becaue isDeleted is a Boolean, so FALSE is a
7326 * legitimate value (set by Samba's deletetest.py)
7328 attrs[0] = attr->lDAPDisplayName;
7329 attrs[1] = "isDeleted";
7330 attrs[2] = "isRecycled";
7331 attrs[3] = NULL;
7334 * get the existing message from the db for the object with
7335 * this GUID, returning attribute being modified. We will then
7336 * use this msg as the basis for a modify call
7338 ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
7339 DSDB_FLAG_NEXT_MODULE |
7340 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7341 DSDB_SEARCH_SHOW_RECYCLED |
7342 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
7343 DSDB_SEARCH_REVEAL_INTERNALS,
7344 NULL,
7345 "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
7346 if (ret != LDB_SUCCESS) {
7347 return ret;
7349 if (res->count != 1) {
7350 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
7351 GUID_string(mem_ctx, &la->identifier->guid));
7352 return LDB_ERR_NO_SUCH_OBJECT;
7355 *source_msg = res->msgs[0];
7357 /* the value blob for the attribute holds the target object DN */
7358 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx, la->value.blob, target_dsdb_dn);
7359 if (!W_ERROR_IS_OK(status)) {
7360 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7361 attr->lDAPDisplayName,
7362 ldb_dn_get_linearized(res->msgs[0]->dn),
7363 win_errstr(status));
7364 return LDB_ERR_OPERATIONS_ERROR;
7367 *ret_attr = attr;
7369 return LDB_SUCCESS;
7373 * Verifies the source and target objects are known for a linked attribute
7375 static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
7376 struct la_entry *la)
7378 int ret = LDB_SUCCESS;
7379 TALLOC_CTX *tmp_ctx = talloc_new(la);
7380 struct ldb_module *module = ar->module;
7381 struct ldb_message *src_msg;
7382 const struct dsdb_attribute *attr;
7383 struct dsdb_dn *tgt_dsdb_dn;
7384 struct GUID guid = GUID_zero();
7385 bool dummy;
7387 ret = replmd_extract_la_entry_details(module, la, tmp_ctx, &attr,
7388 &src_msg, &tgt_dsdb_dn);
7391 * When we fail to find the source object, the error code we pass
7392 * back here is really important. It flags back to the callers to
7393 * retry this request with DRSUAPI_DRS_GET_ANC. This case should
7394 * never happen if we're replicating from a Samba DC, but it is
7395 * needed to talk to a Windows DC
7397 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7398 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT);
7401 if (ret != LDB_SUCCESS) {
7402 talloc_free(tmp_ctx);
7403 return ret;
7407 * We can skip the target object checks if we're only syncing critical
7408 * objects, or we know the target is up-to-date. If either case, we
7409 * still continue even if the target doesn't exist
7411 if ((la->dsdb_repl_flags & (DSDB_REPL_FLAG_OBJECT_SUBSET |
7412 DSDB_REPL_FLAG_TARGETS_UPTODATE)) == 0) {
7414 ret = replmd_check_target_exists(module, tgt_dsdb_dn, la,
7415 src_msg->dn, false, &guid,
7416 &dummy);
7420 * When we fail to find the target object, the error code we pass
7421 * back here is really important. It flags back to the callers to
7422 * retry this request with DRSUAPI_DRS_GET_TGT
7424 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7425 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
7428 talloc_free(tmp_ctx);
7429 return ret;
7433 * Finds the current active Parsed-DN value for a single-valued linked
7434 * attribute, if one exists.
7435 * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
7436 * @returns LDB_SUCCESS (regardless of whether a match was found), unless
7437 * an error occurred
7439 static int replmd_get_active_singleval_link(struct ldb_module *module,
7440 TALLOC_CTX *mem_ctx,
7441 struct parsed_dn pdn_list[],
7442 unsigned int count,
7443 const struct dsdb_attribute *attr,
7444 struct parsed_dn **ret_pdn)
7446 unsigned int i;
7448 *ret_pdn = NULL;
7450 if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE)) {
7452 /* nothing to do for multi-valued linked attributes */
7453 return LDB_SUCCESS;
7456 for (i = 0; i < count; i++) {
7457 int ret = LDB_SUCCESS;
7458 struct parsed_dn *pdn = &pdn_list[i];
7460 /* skip any inactive links */
7461 if (dsdb_dn_is_deleted_val(pdn->v)) {
7462 continue;
7465 /* we've found an active value for this attribute */
7466 *ret_pdn = pdn;
7468 if (pdn->dsdb_dn == NULL) {
7469 struct ldb_context *ldb = ldb_module_get_ctx(module);
7471 ret = really_parse_trusted_dn(mem_ctx, ldb, pdn,
7472 attr->syntax->ldap_oid);
7475 return ret;
7478 /* no active link found */
7479 return LDB_SUCCESS;
7483 * @returns true if the replication linked attribute info is newer than we
7484 * already have in our DB
7485 * @param pdn the existing linked attribute info in our DB
7486 * @param la the new linked attribute info received during replication
7488 static bool replmd_link_update_is_newer(struct parsed_dn *pdn,
7489 struct drsuapi_DsReplicaLinkedAttribute *la)
7491 /* see if this update is newer than what we have already */
7492 struct GUID invocation_id = GUID_zero();
7493 uint32_t version = 0;
7494 NTTIME change_time = 0;
7496 if (pdn == NULL) {
7498 /* no existing info so update is newer */
7499 return true;
7502 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
7503 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
7504 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
7506 return replmd_update_is_newer(&invocation_id,
7507 &la->meta_data.originating_invocation_id,
7508 version,
7509 la->meta_data.version,
7510 change_time,
7511 la->meta_data.originating_change_time);
7515 * Marks an existing linked attribute value as deleted in the DB
7516 * @param pdn the parsed-DN of the target-value to delete
7518 static int replmd_delete_link_value(struct ldb_module *module,
7519 struct replmd_private *replmd_private,
7520 TALLOC_CTX *mem_ctx,
7521 struct ldb_dn *src_obj_dn,
7522 const struct dsdb_schema *schema,
7523 const struct dsdb_attribute *attr,
7524 uint64_t seq_num,
7525 bool is_active,
7526 struct GUID *target_guid,
7527 struct dsdb_dn *target_dsdb_dn,
7528 struct ldb_val *output_val)
7530 struct ldb_context *ldb = ldb_module_get_ctx(module);
7531 time_t t;
7532 NTTIME now;
7533 const struct GUID *invocation_id = NULL;
7534 int ret;
7536 t = time(NULL);
7537 unix_to_nt_time(&now, t);
7539 invocation_id = samdb_ntds_invocation_id(ldb);
7540 if (invocation_id == NULL) {
7541 return LDB_ERR_OPERATIONS_ERROR;
7544 /* if the existing link is active, remove its backlink */
7545 if (is_active) {
7547 ret = replmd_add_backlink(module, replmd_private, schema,
7548 src_obj_dn, target_guid, false,
7549 attr, NULL);
7550 if (ret != LDB_SUCCESS) {
7551 return ret;
7555 /* mark the existing value as deleted */
7556 ret = replmd_update_la_val(mem_ctx, output_val, target_dsdb_dn,
7557 target_dsdb_dn, invocation_id, seq_num,
7558 seq_num, now, true);
7559 return ret;
7563 * Checks for a conflict in single-valued link attributes, and tries to
7564 * resolve the problem if possible.
7566 * Single-valued links should only ever have one active value. If we already
7567 * have an active link value, and during replication we receive an active link
7568 * value for a different target DN, then we need to resolve this inconsistency
7569 * and determine which value should be active. If the received info is better/
7570 * newer than the existing link attribute, then we need to set our existing
7571 * link as deleted. If the received info is worse/older, then we should continue
7572 * to add it, but set it as an inactive link.
7574 * Note that this is a corner-case that is unlikely to happen (but if it does
7575 * happen, we don't want it to break replication completely).
7577 * @param pdn_being_modified the parsed DN corresponding to the received link
7578 * target (note this is NULL if the link does not already exist in our DB)
7579 * @param pdn_list all the source object's Parsed-DNs for this attribute, i.e.
7580 * any existing active or inactive values for the attribute in our DB.
7581 * @param dsdb_dn the target DN for the received link attribute
7582 * @param add_as_inactive gets set to true if the received link is worse than
7583 * the existing link - it should still be added, but as an inactive link.
7585 static int replmd_check_singleval_la_conflict(struct ldb_module *module,
7586 struct replmd_private *replmd_private,
7587 TALLOC_CTX *mem_ctx,
7588 struct ldb_dn *src_obj_dn,
7589 struct drsuapi_DsReplicaLinkedAttribute *la,
7590 struct dsdb_dn *dsdb_dn,
7591 struct parsed_dn *pdn_being_modified,
7592 struct parsed_dn *pdn_list,
7593 struct ldb_message_element *old_el,
7594 const struct dsdb_schema *schema,
7595 const struct dsdb_attribute *attr,
7596 uint64_t seq_num,
7597 bool *add_as_inactive)
7599 struct parsed_dn *active_pdn = NULL;
7600 bool update_is_newer = false;
7601 int ret;
7604 * check if there's a conflict for single-valued links, i.e. an active
7605 * linked attribute already exists, but it has a different target value
7607 ret = replmd_get_active_singleval_link(module, mem_ctx, pdn_list,
7608 old_el->num_values, attr,
7609 &active_pdn);
7611 if (ret != LDB_SUCCESS) {
7612 return ret;
7616 * If no active value exists (or the received info is for the currently
7617 * active value), then no conflict exists
7619 if (active_pdn == NULL || active_pdn == pdn_being_modified) {
7620 return LDB_SUCCESS;
7623 DBG_WARNING("Link conflict for %s attribute on %s\n",
7624 attr->lDAPDisplayName, ldb_dn_get_linearized(src_obj_dn));
7626 /* Work out how to resolve the conflict based on which info is better */
7627 update_is_newer = replmd_link_update_is_newer(active_pdn, la);
7629 if (update_is_newer) {
7630 DBG_WARNING("Using received value %s, over existing target %s\n",
7631 ldb_dn_get_linearized(dsdb_dn->dn),
7632 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn));
7635 * Delete our existing active link. The received info will then
7636 * be added (through normal link processing) as the active value
7638 ret = replmd_delete_link_value(module, replmd_private, old_el,
7639 src_obj_dn, schema, attr,
7640 seq_num, true, &active_pdn->guid,
7641 active_pdn->dsdb_dn,
7642 active_pdn->v);
7644 if (ret != LDB_SUCCESS) {
7645 return ret;
7647 } else {
7648 DBG_WARNING("Using existing target %s, over received value %s\n",
7649 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn),
7650 ldb_dn_get_linearized(dsdb_dn->dn));
7653 * we want to keep our existing active link and add the
7654 * received link as inactive
7656 *add_as_inactive = true;
7659 return LDB_SUCCESS;
7663 process one linked attribute structure
7665 static int replmd_process_linked_attribute(struct ldb_module *module,
7666 struct replmd_private *replmd_private,
7667 struct la_entry *la_entry,
7668 struct ldb_request *parent)
7670 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7671 struct ldb_context *ldb = ldb_module_get_ctx(module);
7672 struct ldb_message *msg;
7673 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7674 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
7675 int ret;
7676 const struct dsdb_attribute *attr;
7677 struct dsdb_dn *dsdb_dn;
7678 uint64_t seq_num = 0;
7679 struct ldb_message_element *old_el;
7680 time_t t = time(NULL);
7681 struct parsed_dn *pdn_list, *pdn, *next;
7682 struct GUID guid = GUID_zero();
7683 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
7684 bool ignore_link;
7685 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
7686 struct dsdb_dn *old_dsdb_dn = NULL;
7687 struct ldb_val *val_to_update = NULL;
7688 bool add_as_inactive = false;
7691 * get the attribute being modified, the search result for the source object,
7692 * and the target object's DN details
7694 ret = replmd_extract_la_entry_details(module, la_entry, tmp_ctx, &attr,
7695 &msg, &dsdb_dn);
7697 if (ret != LDB_SUCCESS) {
7698 talloc_free(tmp_ctx);
7699 return ret;
7703 * Check for deleted objects per MS-DRSR 4.1.10.6.14
7704 * ProcessLinkValue, because link updates are not applied to
7705 * recycled and tombstone objects. We don't have to delete
7706 * any existing link, that should have happened when the
7707 * object deletion was replicated or initiated.
7709 * This needs isDeleted and isRecycled to be included as
7710 * attributes in the search and so in msg if set.
7712 replmd_deletion_state(module, msg, &deletion_state, NULL);
7714 if (deletion_state >= OBJECT_RECYCLED) {
7715 talloc_free(tmp_ctx);
7716 return LDB_SUCCESS;
7720 * Now that we know the deletion_state, remove the extra
7721 * attributes added for that purpose. We need to do this
7722 * otherwise in the case of isDeleted: FALSE the modify will
7723 * fail with:
7725 * Failed to apply linked attribute change 'attribute 'isDeleted':
7726 * invalid modify flags on
7727 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
7728 * 0x0'
7730 * This is becaue isDeleted is a Boolean, so FALSE is a
7731 * legitimate value (set by Samba's deletetest.py)
7734 ldb_msg_remove_attr(msg, "isDeleted");
7735 ldb_msg_remove_attr(msg, "isRecycled");
7737 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7738 if (old_el == NULL) {
7739 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
7740 if (ret != LDB_SUCCESS) {
7741 ldb_module_oom(module);
7742 talloc_free(tmp_ctx);
7743 return LDB_ERR_OPERATIONS_ERROR;
7745 } else {
7746 old_el->flags = LDB_FLAG_MOD_REPLACE;
7749 /* parse the existing links */
7750 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx, old_el, &pdn_list,
7751 attr->syntax->ldap_oid, parent);
7753 if (ret != LDB_SUCCESS) {
7754 talloc_free(tmp_ctx);
7755 return ret;
7758 ret = replmd_check_target_exists(module, dsdb_dn, la_entry, msg->dn,
7759 true, &guid, &ignore_link);
7761 if (ret != LDB_SUCCESS) {
7762 talloc_free(tmp_ctx);
7763 return ret;
7767 * there are some cases where the target object doesn't exist, but it's
7768 * OK to ignore the linked attribute
7770 if (ignore_link) {
7771 talloc_free(tmp_ctx);
7772 return ret;
7775 /* see if this link already exists */
7776 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
7777 &guid,
7778 dsdb_dn->dn,
7779 dsdb_dn->extra_part, 0,
7780 &pdn, &next,
7781 attr->syntax->ldap_oid,
7782 true);
7783 if (ret != LDB_SUCCESS) {
7784 talloc_free(tmp_ctx);
7785 return ret;
7788 if (!replmd_link_update_is_newer(pdn, la)) {
7789 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
7790 old_el->name, ldb_dn_get_linearized(msg->dn),
7791 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
7792 talloc_free(tmp_ctx);
7793 return LDB_SUCCESS;
7796 /* get a seq_num for this change */
7797 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
7798 if (ret != LDB_SUCCESS) {
7799 talloc_free(tmp_ctx);
7800 return ret;
7804 * check for single-valued link conflicts, i.e. an active linked
7805 * attribute already exists, but it has a different target value
7807 if (active) {
7808 ret = replmd_check_singleval_la_conflict(module, replmd_private,
7809 tmp_ctx, msg->dn, la,
7810 dsdb_dn, pdn, pdn_list,
7811 old_el, schema, attr,
7812 seq_num,
7813 &add_as_inactive);
7814 if (ret != LDB_SUCCESS) {
7815 talloc_free(tmp_ctx);
7816 return ret;
7820 if (pdn != NULL) {
7821 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
7823 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
7824 /* remove the existing backlink */
7825 ret = replmd_add_backlink(module, replmd_private,
7826 schema,
7827 msg->dn,
7828 &pdn->guid, false, attr,
7829 parent);
7830 if (ret != LDB_SUCCESS) {
7831 talloc_free(tmp_ctx);
7832 return ret;
7836 val_to_update = pdn->v;
7837 old_dsdb_dn = pdn->dsdb_dn;
7839 } else {
7840 unsigned offset;
7843 * We know where the new one needs to be, from the *next
7844 * pointer into pdn_list.
7846 if (next == NULL) {
7847 offset = old_el->num_values;
7848 } else {
7849 if (next->dsdb_dn == NULL) {
7850 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
7851 attr->syntax->ldap_oid);
7852 if (ret != LDB_SUCCESS) {
7853 return ret;
7856 offset = next - pdn_list;
7857 if (offset > old_el->num_values) {
7858 talloc_free(tmp_ctx);
7859 return LDB_ERR_OPERATIONS_ERROR;
7863 old_el->values = talloc_realloc(msg->elements, old_el->values,
7864 struct ldb_val, old_el->num_values+1);
7865 if (!old_el->values) {
7866 ldb_module_oom(module);
7867 return LDB_ERR_OPERATIONS_ERROR;
7870 if (offset != old_el->num_values) {
7871 memmove(&old_el->values[offset + 1], &old_el->values[offset],
7872 (old_el->num_values - offset) * sizeof(old_el->values[0]));
7875 old_el->num_values++;
7877 val_to_update = &old_el->values[offset];
7878 old_dsdb_dn = NULL;
7881 /* set the link attribute's value to the info that was received */
7882 ret = replmd_set_la_val(tmp_ctx, val_to_update, dsdb_dn, old_dsdb_dn,
7883 &la->meta_data.originating_invocation_id,
7884 la->meta_data.originating_usn, seq_num,
7885 la->meta_data.originating_change_time,
7886 la->meta_data.version,
7887 !active);
7888 if (ret != LDB_SUCCESS) {
7889 talloc_free(tmp_ctx);
7890 return ret;
7893 if (add_as_inactive) {
7895 /* Set the new link as inactive/deleted to avoid conflicts */
7896 ret = replmd_delete_link_value(module, replmd_private, old_el,
7897 msg->dn, schema, attr, seq_num,
7898 false, &guid, dsdb_dn,
7899 val_to_update);
7901 if (ret != LDB_SUCCESS) {
7902 talloc_free(tmp_ctx);
7903 return ret;
7906 } else if (active) {
7908 /* if the new link is active, then add the new backlink */
7909 ret = replmd_add_backlink(module, replmd_private,
7910 schema,
7911 msg->dn,
7912 &guid, true, attr,
7913 parent);
7914 if (ret != LDB_SUCCESS) {
7915 talloc_free(tmp_ctx);
7916 return ret;
7920 /* we only change whenChanged and uSNChanged if the seq_num
7921 has changed */
7922 ret = add_time_element(msg, "whenChanged", t);
7923 if (ret != LDB_SUCCESS) {
7924 talloc_free(tmp_ctx);
7925 ldb_operr(ldb);
7926 return ret;
7929 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
7930 if (ret != LDB_SUCCESS) {
7931 talloc_free(tmp_ctx);
7932 ldb_operr(ldb);
7933 return ret;
7936 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7937 if (old_el == NULL) {
7938 talloc_free(tmp_ctx);
7939 return ldb_operr(ldb);
7942 ret = dsdb_check_single_valued_link(attr, old_el);
7943 if (ret != LDB_SUCCESS) {
7944 talloc_free(tmp_ctx);
7945 return ret;
7948 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
7950 ret = linked_attr_modify(module, msg, parent);
7951 if (ret != LDB_SUCCESS) {
7952 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
7953 ldb_errstring(ldb),
7954 ldb_ldif_message_redacted_string(ldb,
7955 tmp_ctx,
7956 LDB_CHANGETYPE_MODIFY,
7957 msg));
7958 talloc_free(tmp_ctx);
7959 return ret;
7962 talloc_free(tmp_ctx);
7964 return ret;
7967 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
7969 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
7970 return replmd_extended_replicated_objects(module, req);
7973 return ldb_next_request(module, req);
7978 we hook into the transaction operations to allow us to
7979 perform the linked attribute updates at the end of the whole
7980 transaction. This allows a forward linked attribute to be created
7981 before the object is created. During a vampire, w2k8 sends us linked
7982 attributes before the objects they are part of.
7984 static int replmd_start_transaction(struct ldb_module *module)
7986 /* create our private structure for this transaction */
7987 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
7988 struct replmd_private);
7989 replmd_txn_cleanup(replmd_private);
7991 /* free any leftover mod_usn records from cancelled
7992 transactions */
7993 while (replmd_private->ncs) {
7994 struct nc_entry *e = replmd_private->ncs;
7995 DLIST_REMOVE(replmd_private->ncs, e);
7996 talloc_free(e);
7999 replmd_private->originating_updates = false;
8001 return ldb_next_start_trans(module);
8005 on prepare commit we loop over our queued la_context structures and
8006 apply each of them
8008 static int replmd_prepare_commit(struct ldb_module *module)
8010 struct replmd_private *replmd_private =
8011 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8012 struct la_entry *la, *prev;
8013 int ret;
8016 * Walk the list of linked attributes from DRS replication.
8018 * We walk backwards, to do the first entry first, as we
8019 * added the entries with DLIST_ADD() which puts them at the
8020 * start of the list
8022 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
8023 prev = DLIST_PREV(la);
8024 DLIST_REMOVE(replmd_private->la_list, la);
8025 ret = replmd_process_linked_attribute(module, replmd_private,
8026 la, NULL);
8027 if (ret != LDB_SUCCESS) {
8028 replmd_txn_cleanup(replmd_private);
8029 return ret;
8033 replmd_txn_cleanup(replmd_private);
8035 /* possibly change @REPLCHANGED */
8036 ret = replmd_notify_store(module, NULL);
8037 if (ret != LDB_SUCCESS) {
8038 return ret;
8041 return ldb_next_prepare_commit(module);
8044 static int replmd_del_transaction(struct ldb_module *module)
8046 struct replmd_private *replmd_private =
8047 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8048 replmd_txn_cleanup(replmd_private);
8050 return ldb_next_del_trans(module);
8054 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
8055 .name = "repl_meta_data",
8056 .init_context = replmd_init,
8057 .add = replmd_add,
8058 .modify = replmd_modify,
8059 .rename = replmd_rename,
8060 .del = replmd_delete,
8061 .extended = replmd_extended,
8062 .start_transaction = replmd_start_transaction,
8063 .prepare_commit = replmd_prepare_commit,
8064 .del_transaction = replmd_del_transaction,
8067 int ldb_repl_meta_data_module_init(const char *version)
8069 LDB_MODULE_CHECK_VERSION(version);
8070 return ldb_register_module(&ldb_repl_meta_data_module_ops);