s4:repl_meta_data: pass down struct replmd_replicated_request to replmd_modify_la_del...
[Samba.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blob3ce69e06920e4dbfbca7af95b424a8d71ea1d729
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 struct replmd_replicated_request *ac,
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 time_t t,
2411 struct ldb_dn *msg_dn,
2412 struct ldb_request *parent)
2414 unsigned int i, j;
2415 struct parsed_dn *dns, *old_dns;
2416 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2417 int ret;
2418 struct ldb_val *new_values = NULL;
2419 unsigned old_num_values = old_el ? old_el->num_values : 0;
2420 unsigned num_values = 0;
2421 unsigned max_num_values;
2422 struct ldb_context *ldb = ldb_module_get_ctx(module);
2423 NTTIME now;
2424 unix_to_nt_time(&now, t);
2426 /* get the DNs to be added, fully parsed.
2428 * We need full parsing because they came off the wire and we don't
2429 * trust them, besides which we need their details to know where to put
2430 * them.
2432 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2433 schema_attr->syntax->ldap_oid, parent);
2434 if (ret != LDB_SUCCESS) {
2435 talloc_free(tmp_ctx);
2436 return ret;
2439 /* get the existing DNs, lazily parsed */
2440 ret = get_parsed_dns_trusted(module, replmd_private,
2441 tmp_ctx, old_el, &old_dns,
2442 schema_attr->syntax->ldap_oid, parent);
2444 if (ret != LDB_SUCCESS) {
2445 talloc_free(tmp_ctx);
2446 return ret;
2449 max_num_values = old_num_values + el->num_values;
2450 if (max_num_values < old_num_values) {
2451 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2452 "old values: %u, new values: %u, sum: %u\n",
2453 old_num_values, el->num_values, max_num_values));
2454 talloc_free(tmp_ctx);
2455 return LDB_ERR_OPERATIONS_ERROR;
2458 new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2460 if (new_values == NULL) {
2461 ldb_module_oom(module);
2462 talloc_free(tmp_ctx);
2463 return LDB_ERR_OPERATIONS_ERROR;
2467 * For each new value, find where it would go in the list. If there is
2468 * a matching GUID there, we update the existing value; otherwise we
2469 * put it in place.
2471 j = 0;
2472 for (i = 0; i < el->num_values; i++) {
2473 struct parsed_dn *exact;
2474 struct parsed_dn *next;
2475 unsigned offset;
2476 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2477 &dns[i].guid,
2478 dns[i].dsdb_dn->dn,
2479 dns[i].dsdb_dn->extra_part, 0,
2480 &exact, &next,
2481 schema_attr->syntax->ldap_oid,
2482 true);
2483 if (err != LDB_SUCCESS) {
2484 talloc_free(tmp_ctx);
2485 return err;
2488 if (exact != NULL) {
2490 * We are trying to add one that exists, which is only
2491 * allowed if it was previously deleted.
2493 * When we do undelete a link we change it in place.
2494 * It will be copied across into the right spot in due
2495 * course.
2497 uint32_t rmd_flags;
2498 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2500 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2501 struct GUID_txt_buf guid_str;
2502 ldb_asprintf_errstring(ldb,
2503 "Attribute %s already "
2504 "exists for target GUID %s",
2505 el->name,
2506 GUID_buf_string(&exact->guid,
2507 &guid_str));
2508 talloc_free(tmp_ctx);
2509 /* error codes for 'member' need to be
2510 special cased */
2511 if (ldb_attr_cmp(el->name, "member") == 0) {
2512 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2513 } else {
2514 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2518 ret = replmd_update_la_val(new_values, exact->v,
2519 dns[i].dsdb_dn,
2520 exact->dsdb_dn,
2521 &ac->our_invocation_id,
2522 ac->seq_num, ac->seq_num,
2523 now, false);
2524 if (ret != LDB_SUCCESS) {
2525 talloc_free(tmp_ctx);
2526 return ret;
2529 ret = replmd_add_backlink(module, replmd_private,
2530 ac->schema,
2531 msg_dn,
2532 &dns[i].guid,
2533 true,
2534 schema_attr,
2535 parent);
2536 if (ret != LDB_SUCCESS) {
2537 talloc_free(tmp_ctx);
2538 return ret;
2540 continue;
2543 * Here we don't have an exact match.
2545 * If next is NULL, this one goes beyond the end of the
2546 * existing list, so we need to add all of those ones first.
2548 * If next is not NULL, we need to add all the ones before
2549 * next.
2551 if (next == NULL) {
2552 offset = old_num_values;
2553 } else {
2554 /* next should have been parsed, but let's make sure */
2555 if (next->dsdb_dn == NULL) {
2556 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2557 schema_attr->syntax->ldap_oid);
2558 if (ret != LDB_SUCCESS) {
2559 return ret;
2562 offset = MIN(next - old_dns, old_num_values);
2565 /* put all the old ones before next on the list */
2566 for (; j < offset; j++) {
2567 new_values[num_values] = *old_dns[j].v;
2568 num_values++;
2571 ret = replmd_add_backlink(module, replmd_private,
2572 ac->schema, msg_dn,
2573 &dns[i].guid,
2574 true, schema_attr,
2575 parent);
2576 /* Make the new linked attribute ldb_val. */
2577 ret = replmd_build_la_val(new_values, &new_values[num_values],
2578 dns[i].dsdb_dn, &ac->our_invocation_id,
2579 ac->seq_num, now);
2580 if (ret != LDB_SUCCESS) {
2581 talloc_free(tmp_ctx);
2582 return ret;
2584 num_values++;
2585 if (ret != LDB_SUCCESS) {
2586 talloc_free(tmp_ctx);
2587 return ret;
2590 /* copy the rest of the old ones (if any) */
2591 for (; j < old_num_values; j++) {
2592 new_values[num_values] = *old_dns[j].v;
2593 num_values++;
2596 talloc_steal(msg->elements, new_values);
2597 if (old_el != NULL) {
2598 talloc_steal(msg->elements, old_el->values);
2600 el->values = new_values;
2601 el->num_values = num_values;
2603 talloc_free(tmp_ctx);
2605 /* we now tell the backend to replace all existing values
2606 with the one we have constructed */
2607 el->flags = LDB_FLAG_MOD_REPLACE;
2609 return LDB_SUCCESS;
2614 handle deleting all active linked attributes
2616 static int replmd_modify_la_delete(struct ldb_module *module,
2617 struct replmd_private *replmd_private,
2618 struct replmd_replicated_request *ac,
2619 struct ldb_message *msg,
2620 struct ldb_message_element *el,
2621 struct ldb_message_element *old_el,
2622 const struct dsdb_attribute *schema_attr,
2623 time_t t,
2624 struct ldb_dn *msg_dn,
2625 struct ldb_request *parent)
2627 unsigned int i;
2628 struct parsed_dn *dns, *old_dns;
2629 TALLOC_CTX *tmp_ctx = NULL;
2630 int ret;
2631 struct ldb_context *ldb = ldb_module_get_ctx(module);
2632 struct ldb_control *vanish_links_ctrl = NULL;
2633 bool vanish_links = false;
2634 unsigned int num_to_delete = el->num_values;
2635 uint32_t rmd_flags;
2636 NTTIME now;
2638 unix_to_nt_time(&now, t);
2640 if (old_el == NULL || old_el->num_values == 0) {
2641 /* there is nothing to delete... */
2642 if (num_to_delete == 0) {
2643 /* and we're deleting nothing, so that's OK */
2644 return LDB_SUCCESS;
2646 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2649 tmp_ctx = talloc_new(msg);
2650 if (tmp_ctx == NULL) {
2651 return LDB_ERR_OPERATIONS_ERROR;
2654 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2655 schema_attr->syntax->ldap_oid, parent);
2656 if (ret != LDB_SUCCESS) {
2657 talloc_free(tmp_ctx);
2658 return ret;
2661 ret = get_parsed_dns_trusted(module, replmd_private,
2662 tmp_ctx, old_el, &old_dns,
2663 schema_attr->syntax->ldap_oid, parent);
2665 if (ret != LDB_SUCCESS) {
2666 talloc_free(tmp_ctx);
2667 return ret;
2670 if (parent) {
2671 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2672 if (vanish_links_ctrl) {
2673 vanish_links = true;
2674 vanish_links_ctrl->critical = false;
2678 /* we empty out el->values here to avoid damage if we return early. */
2679 el->num_values = 0;
2680 el->values = NULL;
2683 * If vanish links is set, we are actually removing members of
2684 * old_el->values; otherwise we are just marking them deleted.
2686 * There is a special case when no values are given: we remove them
2687 * all. When we have the vanish_links control we just have to remove
2688 * the backlinks and change our element to replace the existing values
2689 * with the empty list.
2692 if (num_to_delete == 0) {
2693 for (i = 0; i < old_el->num_values; i++) {
2694 struct parsed_dn *p = &old_dns[i];
2695 if (p->dsdb_dn == NULL) {
2696 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2697 schema_attr->syntax->ldap_oid);
2698 if (ret != LDB_SUCCESS) {
2699 return ret;
2702 ret = replmd_add_backlink(module, replmd_private,
2703 ac->schema, msg_dn, &p->guid,
2704 false, schema_attr,
2705 parent);
2706 if (ret != LDB_SUCCESS) {
2707 talloc_free(tmp_ctx);
2708 return ret;
2710 if (vanish_links) {
2711 continue;
2714 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2715 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2716 continue;
2719 ret = replmd_update_la_val(old_el->values, p->v,
2720 p->dsdb_dn, p->dsdb_dn,
2721 &ac->our_invocation_id,
2722 ac->seq_num, ac->seq_num,
2723 now, true);
2724 if (ret != LDB_SUCCESS) {
2725 talloc_free(tmp_ctx);
2726 return ret;
2730 if (vanish_links) {
2731 el->flags = LDB_FLAG_MOD_REPLACE;
2732 talloc_free(tmp_ctx);
2733 return LDB_SUCCESS;
2738 for (i = 0; i < num_to_delete; i++) {
2739 struct parsed_dn *p = &dns[i];
2740 struct parsed_dn *exact = NULL;
2741 struct parsed_dn *next = NULL;
2742 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2743 &p->guid,
2744 NULL,
2745 p->dsdb_dn->extra_part, 0,
2746 &exact, &next,
2747 schema_attr->syntax->ldap_oid,
2748 true);
2749 if (ret != LDB_SUCCESS) {
2750 talloc_free(tmp_ctx);
2751 return ret;
2753 if (exact == NULL) {
2754 struct GUID_txt_buf buf;
2755 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2756 "exist for target GUID %s",
2757 el->name,
2758 GUID_buf_string(&p->guid, &buf));
2759 if (ldb_attr_cmp(el->name, "member") == 0) {
2760 talloc_free(tmp_ctx);
2761 return LDB_ERR_UNWILLING_TO_PERFORM;
2762 } else {
2763 talloc_free(tmp_ctx);
2764 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2768 if (vanish_links) {
2769 if (CHECK_DEBUGLVL(5)) {
2770 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2771 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2772 struct GUID_txt_buf buf;
2773 const char *guid_str = \
2774 GUID_buf_string(&p->guid, &buf);
2775 DEBUG(5, ("Deleting deleted linked "
2776 "attribute %s to %s, because "
2777 "vanish_links control is set\n",
2778 el->name, guid_str));
2782 /* remove the backlink */
2783 ret = replmd_add_backlink(module,
2784 replmd_private,
2785 ac->schema,
2786 msg_dn,
2787 &p->guid,
2788 false, schema_attr,
2789 parent);
2790 if (ret != LDB_SUCCESS) {
2791 talloc_free(tmp_ctx);
2792 return ret;
2795 /* We flag the deletion and tidy it up later. */
2796 exact->v = NULL;
2797 continue;
2800 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2802 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2803 struct GUID_txt_buf buf;
2804 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2805 ldb_asprintf_errstring(ldb, "Attribute %s already "
2806 "deleted for target GUID %s",
2807 el->name, guid_str);
2808 if (ldb_attr_cmp(el->name, "member") == 0) {
2809 talloc_free(tmp_ctx);
2810 return LDB_ERR_UNWILLING_TO_PERFORM;
2811 } else {
2812 talloc_free(tmp_ctx);
2813 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2817 ret = replmd_update_la_val(old_el->values, exact->v,
2818 exact->dsdb_dn, exact->dsdb_dn,
2819 &ac->our_invocation_id,
2820 ac->seq_num, ac->seq_num,
2821 now, true);
2822 if (ret != LDB_SUCCESS) {
2823 talloc_free(tmp_ctx);
2824 return ret;
2826 ret = replmd_add_backlink(module, replmd_private,
2827 ac->schema, msg_dn,
2828 &p->guid,
2829 false, schema_attr,
2830 parent);
2831 if (ret != LDB_SUCCESS) {
2832 talloc_free(tmp_ctx);
2833 return ret;
2837 if (vanish_links) {
2838 unsigned j = 0;
2839 struct ldb_val *tmp_vals = NULL;
2841 tmp_vals = talloc_array(tmp_ctx, struct ldb_val,
2842 old_el->num_values);
2843 if (tmp_vals == NULL) {
2844 talloc_free(tmp_ctx);
2845 return ldb_module_oom(module);
2847 for (i = 0; i < old_el->num_values; i++) {
2848 if (old_dns[i].v == NULL) {
2849 continue;
2851 tmp_vals[j] = *old_dns[i].v;
2852 j++;
2854 for (i = 0; i < j; i++) {
2855 old_el->values[i] = tmp_vals[i];
2857 old_el->num_values = j;
2860 el->values = talloc_steal(msg->elements, old_el->values);
2861 el->num_values = old_el->num_values;
2863 talloc_free(tmp_ctx);
2865 /* we now tell the backend to replace all existing values
2866 with the one we have constructed */
2867 el->flags = LDB_FLAG_MOD_REPLACE;
2869 return LDB_SUCCESS;
2873 handle replacing a linked attribute
2875 static int replmd_modify_la_replace(struct ldb_module *module,
2876 struct replmd_private *replmd_private,
2877 const struct dsdb_schema *schema,
2878 struct ldb_message *msg,
2879 struct ldb_message_element *el,
2880 struct ldb_message_element *old_el,
2881 const struct dsdb_attribute *schema_attr,
2882 uint64_t seq_num,
2883 time_t t,
2884 struct ldb_dn *msg_dn,
2885 struct ldb_request *parent)
2887 unsigned int i, old_i, new_i;
2888 struct parsed_dn *dns, *old_dns;
2889 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2890 int ret;
2891 const struct GUID *invocation_id;
2892 struct ldb_context *ldb = ldb_module_get_ctx(module);
2893 struct ldb_val *new_values = NULL;
2894 const char *ldap_oid = schema_attr->syntax->ldap_oid;
2895 unsigned int old_num_values;
2896 unsigned int repl_num_values;
2897 unsigned int max_num_values;
2898 NTTIME now;
2900 unix_to_nt_time(&now, t);
2902 invocation_id = samdb_ntds_invocation_id(ldb);
2903 if (!invocation_id) {
2904 return LDB_ERR_OPERATIONS_ERROR;
2908 * The replace operation is unlike the replace and delete cases in that
2909 * we need to look at every existing link to see whether it is being
2910 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
2912 * As we are trying to combine two sorted lists, the algorithm we use
2913 * is akin to the merge phase of a merge sort. We interleave the two
2914 * lists, doing different things depending on which side the current
2915 * item came from.
2917 * There are three main cases, with some sub-cases.
2919 * - a DN is in the old list but not the new one. It needs to be
2920 * marked as deleted (but left in the list).
2921 * - maybe it is already deleted, and we have less to do.
2923 * - a DN is in both lists. The old data gets replaced by the new,
2924 * and the list doesn't grow. The old link may have been marked as
2925 * deleted, in which case we undelete it.
2927 * - a DN is in the new list only. We add it in the right place.
2930 old_num_values = old_el ? old_el->num_values : 0;
2931 repl_num_values = el->num_values;
2932 max_num_values = old_num_values + repl_num_values;
2934 if (max_num_values == 0) {
2935 /* There is nothing to do! */
2936 return LDB_SUCCESS;
2939 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
2940 if (ret != LDB_SUCCESS) {
2941 talloc_free(tmp_ctx);
2942 return ret;
2945 ret = check_parsed_dn_duplicates(module, el, dns);
2946 if (ret != LDB_SUCCESS) {
2947 talloc_free(tmp_ctx);
2948 return ret;
2951 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
2952 ldap_oid, parent);
2953 if (ret != LDB_SUCCESS) {
2954 talloc_free(tmp_ctx);
2955 return ret;
2958 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
2959 old_el, ldap_oid);
2960 if (ret != LDB_SUCCESS) {
2961 talloc_free(tmp_ctx);
2962 return ret;
2965 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
2966 if (new_values == NULL) {
2967 ldb_module_oom(module);
2968 talloc_free(tmp_ctx);
2969 return LDB_ERR_OPERATIONS_ERROR;
2972 old_i = 0;
2973 new_i = 0;
2974 for (i = 0; i < max_num_values; i++) {
2975 int cmp;
2976 struct parsed_dn *old_p, *new_p;
2977 if (old_i < old_num_values && new_i < repl_num_values) {
2978 old_p = &old_dns[old_i];
2979 new_p = &dns[new_i];
2980 cmp = parsed_dn_compare(old_p, new_p);
2981 } else if (old_i < old_num_values) {
2982 /* the new list is empty, read the old list */
2983 old_p = &old_dns[old_i];
2984 new_p = NULL;
2985 cmp = -1;
2986 } else if (new_i < repl_num_values) {
2987 /* the old list is empty, read new list */
2988 old_p = NULL;
2989 new_p = &dns[new_i];
2990 cmp = 1;
2991 } else {
2992 break;
2995 if (cmp < 0) {
2997 * An old ones that come before the next replacement
2998 * (if any). We mark it as deleted and add it to the
2999 * final list.
3001 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3002 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
3003 ret = replmd_update_la_val(new_values, old_p->v,
3004 old_p->dsdb_dn,
3005 old_p->dsdb_dn,
3006 invocation_id,
3007 seq_num, seq_num,
3008 now, true);
3009 if (ret != LDB_SUCCESS) {
3010 talloc_free(tmp_ctx);
3011 return ret;
3014 ret = replmd_add_backlink(module, replmd_private,
3015 schema,
3016 msg_dn,
3017 &old_p->guid, false,
3018 schema_attr,
3019 parent);
3020 if (ret != LDB_SUCCESS) {
3021 talloc_free(tmp_ctx);
3022 return ret;
3025 new_values[i] = *old_p->v;
3026 old_i++;
3027 } else if (cmp == 0) {
3029 * We are overwriting one. If it was previously
3030 * deleted, we need to add a backlink.
3032 * Note that if any RMD_FLAGs in an extended new DN
3033 * will be ignored.
3035 uint32_t rmd_flags;
3037 ret = replmd_update_la_val(new_values, old_p->v,
3038 new_p->dsdb_dn,
3039 old_p->dsdb_dn,
3040 invocation_id,
3041 seq_num, seq_num,
3042 now, false);
3043 if (ret != LDB_SUCCESS) {
3044 talloc_free(tmp_ctx);
3045 return ret;
3048 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3049 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3050 ret = replmd_add_backlink(module, replmd_private,
3051 schema,
3052 msg_dn,
3053 &new_p->guid, true,
3054 schema_attr,
3055 parent);
3056 if (ret != LDB_SUCCESS) {
3057 talloc_free(tmp_ctx);
3058 return ret;
3062 new_values[i] = *old_p->v;
3063 old_i++;
3064 new_i++;
3065 } else {
3067 * Replacements that don't match an existing one. We
3068 * just add them to the final list.
3070 ret = replmd_build_la_val(new_values,
3071 new_p->v,
3072 new_p->dsdb_dn,
3073 invocation_id,
3074 seq_num, now);
3075 if (ret != LDB_SUCCESS) {
3076 talloc_free(tmp_ctx);
3077 return ret;
3079 ret = replmd_add_backlink(module, replmd_private,
3080 schema,
3081 msg_dn,
3082 &new_p->guid, true,
3083 schema_attr,
3084 parent);
3085 if (ret != LDB_SUCCESS) {
3086 talloc_free(tmp_ctx);
3087 return ret;
3089 new_values[i] = *new_p->v;
3090 new_i++;
3093 if (old_el != NULL) {
3094 talloc_steal(msg->elements, old_el->values);
3096 el->values = talloc_steal(msg->elements, new_values);
3097 el->num_values = i;
3098 talloc_free(tmp_ctx);
3100 el->flags = LDB_FLAG_MOD_REPLACE;
3102 return LDB_SUCCESS;
3107 handle linked attributes in modify requests
3109 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3110 struct replmd_private *replmd_private,
3111 struct replmd_replicated_request *ac,
3112 struct ldb_message *msg,
3113 time_t t,
3114 struct ldb_request *parent)
3116 struct ldb_result *res;
3117 unsigned int i;
3118 int ret;
3119 struct ldb_context *ldb = ldb_module_get_ctx(module);
3120 struct ldb_message *old_msg;
3122 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3124 * Nothing special is required for modifying or vanishing links
3125 * in fl2000 since they are just strings in a multi-valued
3126 * attribute.
3128 struct ldb_control *ctrl = ldb_request_get_control(parent,
3129 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3130 if (ctrl) {
3131 ctrl->critical = false;
3133 return LDB_SUCCESS;
3137 * TODO:
3139 * We should restrict this to the intersection of the list of
3140 * linked attributes in the schema and the list of attributes
3141 * being modified.
3143 * This will help performance a little, as otherwise we have
3144 * to allocate the entire object value-by-value.
3146 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3147 DSDB_FLAG_NEXT_MODULE |
3148 DSDB_SEARCH_SHOW_RECYCLED |
3149 DSDB_SEARCH_REVEAL_INTERNALS |
3150 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3151 parent);
3152 if (ret != LDB_SUCCESS) {
3153 return ret;
3156 old_msg = res->msgs[0];
3158 for (i=0; i<msg->num_elements; i++) {
3159 struct ldb_message_element *el = &msg->elements[i];
3160 struct ldb_message_element *old_el, *new_el;
3161 unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3162 const struct dsdb_attribute *schema_attr
3163 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
3164 if (!schema_attr) {
3165 ldb_asprintf_errstring(ldb,
3166 "%s: attribute %s is not a valid attribute in schema",
3167 __FUNCTION__, el->name);
3168 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3170 if (schema_attr->linkID == 0) {
3171 continue;
3173 if ((schema_attr->linkID & 1) == 1) {
3174 if (parent) {
3175 struct ldb_control *ctrl;
3177 ctrl = ldb_request_get_control(parent,
3178 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3179 if (ctrl != NULL) {
3180 ctrl->critical = false;
3181 continue;
3183 ctrl = ldb_request_get_control(parent,
3184 DSDB_CONTROL_DBCHECK);
3185 if (ctrl != NULL) {
3186 continue;
3190 /* Odd is for the target. Illegal to modify */
3191 ldb_asprintf_errstring(ldb,
3192 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3193 return LDB_ERR_UNWILLING_TO_PERFORM;
3195 old_el = ldb_msg_find_element(old_msg, el->name);
3196 switch (mod_type) {
3197 case LDB_FLAG_MOD_REPLACE:
3198 ret = replmd_modify_la_replace(module, replmd_private,
3199 ac->schema, msg, el, old_el,
3200 schema_attr, ac->seq_num, t,
3201 old_msg->dn,
3202 parent);
3203 break;
3204 case LDB_FLAG_MOD_DELETE:
3205 ret = replmd_modify_la_delete(module, replmd_private,
3206 ac, msg, el, old_el,
3207 schema_attr, t,
3208 old_msg->dn,
3209 parent);
3210 break;
3211 case LDB_FLAG_MOD_ADD:
3212 ret = replmd_modify_la_add(module, replmd_private,
3213 ac, msg, el, old_el,
3214 schema_attr, t,
3215 old_msg->dn,
3216 parent);
3217 break;
3218 default:
3219 ldb_asprintf_errstring(ldb,
3220 "invalid flags 0x%x for %s linked attribute",
3221 el->flags, el->name);
3222 return LDB_ERR_UNWILLING_TO_PERFORM;
3224 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
3225 ldb_asprintf_errstring(ldb,
3226 "Attribute %s is single valued but more than one value has been supplied",
3227 el->name);
3228 /* Return codes as found on Windows 2012r2 */
3229 if (mod_type == LDB_FLAG_MOD_REPLACE) {
3230 return LDB_ERR_CONSTRAINT_VIOLATION;
3231 } else {
3232 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3234 } else {
3235 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3238 if (ret != LDB_SUCCESS) {
3239 return ret;
3241 if (old_el) {
3242 ldb_msg_remove_attr(old_msg, el->name);
3244 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3245 new_el->num_values = el->num_values;
3246 new_el->values = talloc_steal(msg->elements, el->values);
3248 /* TODO: this relises a bit too heavily on the exact
3249 behaviour of ldb_msg_find_element and
3250 ldb_msg_remove_element */
3251 old_el = ldb_msg_find_element(msg, el->name);
3252 if (old_el != el) {
3253 ldb_msg_remove_element(msg, old_el);
3254 i--;
3258 talloc_free(res);
3259 return ret;
3263 static int send_rodc_referral(struct ldb_request *req,
3264 struct ldb_context *ldb,
3265 struct ldb_dn *dn)
3267 char *referral = NULL;
3268 struct loadparm_context *lp_ctx = NULL;
3269 struct ldb_dn *fsmo_role_dn = NULL;
3270 struct ldb_dn *role_owner_dn = NULL;
3271 const char *domain = NULL;
3272 WERROR werr;
3274 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3275 struct loadparm_context);
3277 werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3278 &fsmo_role_dn, &role_owner_dn);
3280 if (W_ERROR_IS_OK(werr)) {
3281 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3282 if (server_dn != NULL) {
3283 ldb_dn_remove_child_components(server_dn, 1);
3284 domain = samdb_dn_to_dnshostname(ldb, req,
3285 server_dn);
3289 if (domain == NULL) {
3290 domain = lpcfg_dnsdomain(lp_ctx);
3293 referral = talloc_asprintf(req, "ldap://%s/%s",
3294 domain,
3295 ldb_dn_get_linearized(dn));
3296 if (referral == NULL) {
3297 ldb_oom(ldb);
3298 return LDB_ERR_OPERATIONS_ERROR;
3301 return ldb_module_send_referral(req, referral);
3305 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3307 struct ldb_context *ldb;
3308 struct replmd_replicated_request *ac;
3309 struct ldb_request *down_req;
3310 struct ldb_message *msg;
3311 time_t t = time(NULL);
3312 int ret;
3313 bool is_urgent = false, rodc = false;
3314 bool is_schema_nc = false;
3315 unsigned int functional_level;
3316 const struct ldb_message_element *guid_el = NULL;
3317 struct ldb_control *sd_propagation_control;
3318 struct ldb_control *fix_links_control = NULL;
3319 struct ldb_control *fix_dn_name_control = NULL;
3320 struct replmd_private *replmd_private =
3321 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3323 /* do not manipulate our control entries */
3324 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3325 return ldb_next_request(module, req);
3328 sd_propagation_control = ldb_request_get_control(req,
3329 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3330 if (sd_propagation_control != NULL) {
3331 if (req->op.mod.message->num_elements != 1) {
3332 return ldb_module_operr(module);
3334 ret = strcmp(req->op.mod.message->elements[0].name,
3335 "nTSecurityDescriptor");
3336 if (ret != 0) {
3337 return ldb_module_operr(module);
3340 return ldb_next_request(module, req);
3343 ldb = ldb_module_get_ctx(module);
3345 fix_links_control = ldb_request_get_control(req,
3346 DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
3347 if (fix_links_control != NULL) {
3348 struct dsdb_schema *schema = NULL;
3349 const struct dsdb_attribute *sa = NULL;
3351 if (req->op.mod.message->num_elements != 1) {
3352 return ldb_module_operr(module);
3355 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_REPLACE) {
3356 return ldb_module_operr(module);
3359 schema = dsdb_get_schema(ldb, req);
3360 if (schema == NULL) {
3361 return ldb_module_operr(module);
3364 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3365 req->op.mod.message->elements[0].name);
3366 if (sa == NULL) {
3367 return ldb_module_operr(module);
3370 if (sa->linkID == 0) {
3371 return ldb_module_operr(module);
3374 fix_links_control->critical = false;
3375 return ldb_next_request(module, req);
3378 fix_dn_name_control = ldb_request_get_control(req,
3379 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3380 if (fix_dn_name_control != NULL) {
3381 struct dsdb_schema *schema = NULL;
3382 const struct dsdb_attribute *sa = NULL;
3384 if (req->op.mod.message->num_elements != 2) {
3385 return ldb_module_operr(module);
3388 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_DELETE) {
3389 return ldb_module_operr(module);
3392 if (req->op.mod.message->elements[1].flags != LDB_FLAG_MOD_ADD) {
3393 return ldb_module_operr(module);
3396 if (req->op.mod.message->elements[0].num_values != 1) {
3397 return ldb_module_operr(module);
3400 if (req->op.mod.message->elements[1].num_values != 1) {
3401 return ldb_module_operr(module);
3404 schema = dsdb_get_schema(ldb, req);
3405 if (schema == NULL) {
3406 return ldb_module_operr(module);
3409 if (ldb_attr_cmp(req->op.mod.message->elements[0].name,
3410 req->op.mod.message->elements[1].name) != 0) {
3411 return ldb_module_operr(module);
3414 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3415 req->op.mod.message->elements[0].name);
3416 if (sa == NULL) {
3417 return ldb_module_operr(module);
3420 if (sa->dn_format == DSDB_INVALID_DN) {
3421 return ldb_module_operr(module);
3424 if (sa->linkID != 0) {
3425 return ldb_module_operr(module);
3429 * If we are run from dbcheck and we are not updating
3430 * a link (as these would need to be sorted and so
3431 * can't go via such a simple update, then do not
3432 * trigger replicated updates and a new USN from this
3433 * change, it wasn't a real change, just a new
3434 * (correct) string DN
3437 fix_dn_name_control->critical = false;
3438 return ldb_next_request(module, req);
3441 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3443 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3444 if (guid_el != NULL) {
3445 ldb_set_errstring(ldb,
3446 "replmd_modify: it's not allowed to change the objectGUID!");
3447 return LDB_ERR_CONSTRAINT_VIOLATION;
3450 ac = replmd_ctx_init(module, req);
3451 if (ac == NULL) {
3452 return ldb_module_oom(module);
3455 functional_level = dsdb_functional_level(ldb);
3457 /* we have to copy the message as the caller might have it as a const */
3458 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3459 if (msg == NULL) {
3460 ldb_oom(ldb);
3461 talloc_free(ac);
3462 return LDB_ERR_OPERATIONS_ERROR;
3465 ldb_msg_remove_attr(msg, "whenChanged");
3466 ldb_msg_remove_attr(msg, "uSNChanged");
3468 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3470 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3471 msg, &ac->seq_num, t, is_schema_nc,
3472 &is_urgent, &rodc);
3473 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3474 ret = send_rodc_referral(req, ldb, msg->dn);
3475 talloc_free(ac);
3476 return ret;
3480 if (ret != LDB_SUCCESS) {
3481 talloc_free(ac);
3482 return ret;
3485 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3486 ac, msg, t, req);
3487 if (ret != LDB_SUCCESS) {
3488 talloc_free(ac);
3489 return ret;
3492 /* TODO:
3493 * - replace the old object with the newly constructed one
3496 ac->is_urgent = is_urgent;
3498 ret = ldb_build_mod_req(&down_req, ldb, ac,
3499 msg,
3500 req->controls,
3501 ac, replmd_op_callback,
3502 req);
3503 LDB_REQ_SET_LOCATION(down_req);
3504 if (ret != LDB_SUCCESS) {
3505 talloc_free(ac);
3506 return ret;
3509 /* current partition control is needed by "replmd_op_callback" */
3510 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3511 ret = ldb_request_add_control(down_req,
3512 DSDB_CONTROL_CURRENT_PARTITION_OID,
3513 false, NULL);
3514 if (ret != LDB_SUCCESS) {
3515 talloc_free(ac);
3516 return ret;
3520 /* If we are in functional level 2000, then
3521 * replmd_modify_handle_linked_attribs will have done
3522 * nothing */
3523 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3524 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3525 if (ret != LDB_SUCCESS) {
3526 talloc_free(ac);
3527 return ret;
3531 talloc_steal(down_req, msg);
3533 /* we only change whenChanged and uSNChanged if the seq_num
3534 has changed */
3535 if (ac->seq_num != 0) {
3536 ret = add_time_element(msg, "whenChanged", t);
3537 if (ret != LDB_SUCCESS) {
3538 talloc_free(ac);
3539 ldb_operr(ldb);
3540 return ret;
3543 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3544 if (ret != LDB_SUCCESS) {
3545 talloc_free(ac);
3546 ldb_operr(ldb);
3547 return ret;
3551 /* go on with the call chain */
3552 return ldb_next_request(module, down_req);
3555 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3558 handle a rename request
3560 On a rename we need to do an extra ldb_modify which sets the
3561 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3563 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3565 struct ldb_context *ldb;
3566 struct replmd_replicated_request *ac;
3567 int ret;
3568 struct ldb_request *down_req;
3570 /* do not manipulate our control entries */
3571 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3572 return ldb_next_request(module, req);
3575 ldb = ldb_module_get_ctx(module);
3577 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3579 ac = replmd_ctx_init(module, req);
3580 if (ac == NULL) {
3581 return ldb_module_oom(module);
3584 ret = ldb_build_rename_req(&down_req, ldb, ac,
3585 ac->req->op.rename.olddn,
3586 ac->req->op.rename.newdn,
3587 ac->req->controls,
3588 ac, replmd_rename_callback,
3589 ac->req);
3590 LDB_REQ_SET_LOCATION(down_req);
3591 if (ret != LDB_SUCCESS) {
3592 talloc_free(ac);
3593 return ret;
3596 /* go on with the call chain */
3597 return ldb_next_request(module, down_req);
3600 /* After the rename is compleated, update the whenchanged etc */
3601 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3603 struct ldb_context *ldb;
3604 struct ldb_request *down_req;
3605 struct ldb_message *msg;
3606 const struct dsdb_attribute *rdn_attr;
3607 const char *rdn_name;
3608 const struct ldb_val *rdn_val;
3609 const char *attrs[5] = { NULL, };
3610 time_t t = time(NULL);
3611 int ret;
3612 bool is_urgent = false, rodc = false;
3613 bool is_schema_nc;
3614 struct replmd_replicated_request *ac =
3615 talloc_get_type(req->context, struct replmd_replicated_request);
3616 struct replmd_private *replmd_private =
3617 talloc_get_type(ldb_module_get_private(ac->module),
3618 struct replmd_private);
3620 ldb = ldb_module_get_ctx(ac->module);
3622 if (ares->error != LDB_SUCCESS) {
3623 return ldb_module_done(ac->req, ares->controls,
3624 ares->response, ares->error);
3627 if (ares->type != LDB_REPLY_DONE) {
3628 ldb_set_errstring(ldb,
3629 "invalid ldb_reply_type in callback");
3630 talloc_free(ares);
3631 return ldb_module_done(ac->req, NULL, NULL,
3632 LDB_ERR_OPERATIONS_ERROR);
3635 /* TODO:
3636 * - replace the old object with the newly constructed one
3639 msg = ldb_msg_new(ac);
3640 if (msg == NULL) {
3641 ldb_oom(ldb);
3642 return LDB_ERR_OPERATIONS_ERROR;
3645 msg->dn = ac->req->op.rename.newdn;
3647 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3649 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3650 if (rdn_name == NULL) {
3651 talloc_free(ares);
3652 return ldb_module_done(ac->req, NULL, NULL,
3653 ldb_operr(ldb));
3656 /* normalize the rdn attribute name */
3657 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3658 if (rdn_attr == NULL) {
3659 talloc_free(ares);
3660 return ldb_module_done(ac->req, NULL, NULL,
3661 ldb_operr(ldb));
3663 rdn_name = rdn_attr->lDAPDisplayName;
3665 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3666 if (rdn_val == NULL) {
3667 talloc_free(ares);
3668 return ldb_module_done(ac->req, NULL, NULL,
3669 ldb_operr(ldb));
3672 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3673 talloc_free(ares);
3674 return ldb_module_done(ac->req, NULL, NULL,
3675 ldb_oom(ldb));
3677 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3678 talloc_free(ares);
3679 return ldb_module_done(ac->req, NULL, NULL,
3680 ldb_oom(ldb));
3682 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3683 talloc_free(ares);
3684 return ldb_module_done(ac->req, NULL, NULL,
3685 ldb_oom(ldb));
3687 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3688 talloc_free(ares);
3689 return ldb_module_done(ac->req, NULL, NULL,
3690 ldb_oom(ldb));
3694 * here we let replmd_update_rpmd() only search for
3695 * the existing "replPropertyMetaData" and rdn_name attributes.
3697 * We do not want the existing "name" attribute as
3698 * the "name" attribute needs to get the version
3699 * updated on rename even if the rdn value hasn't changed.
3701 * This is the diff of the meta data, for a moved user
3702 * on a w2k8r2 server:
3704 * # record 1
3705 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3706 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3707 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3708 * version : 0x00000001 (1)
3709 * reserved : 0x00000000 (0)
3710 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3711 * local_usn : 0x00000000000037a5 (14245)
3712 * array: struct replPropertyMetaData1
3713 * attid : DRSUAPI_ATTID_name (0x90001)
3714 * - version : 0x00000001 (1)
3715 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3716 * + version : 0x00000002 (2)
3717 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3718 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3719 * - originating_usn : 0x00000000000037a5 (14245)
3720 * - local_usn : 0x00000000000037a5 (14245)
3721 * + originating_usn : 0x0000000000003834 (14388)
3722 * + local_usn : 0x0000000000003834 (14388)
3723 * array: struct replPropertyMetaData1
3724 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3725 * version : 0x00000004 (4)
3727 attrs[0] = "replPropertyMetaData";
3728 attrs[1] = "objectClass";
3729 attrs[2] = "instanceType";
3730 attrs[3] = rdn_name;
3731 attrs[4] = NULL;
3733 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3734 msg, &ac->seq_num, t,
3735 is_schema_nc, &is_urgent, &rodc);
3736 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3737 ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
3738 talloc_free(ares);
3739 return ldb_module_done(req, NULL, NULL, ret);
3742 if (ret != LDB_SUCCESS) {
3743 talloc_free(ares);
3744 return ldb_module_done(ac->req, NULL, NULL, ret);
3747 if (ac->seq_num == 0) {
3748 talloc_free(ares);
3749 return ldb_module_done(ac->req, NULL, NULL,
3750 ldb_error(ldb, ret,
3751 "internal error seq_num == 0"));
3753 ac->is_urgent = is_urgent;
3755 ret = ldb_build_mod_req(&down_req, ldb, ac,
3756 msg,
3757 req->controls,
3758 ac, replmd_op_callback,
3759 req);
3760 LDB_REQ_SET_LOCATION(down_req);
3761 if (ret != LDB_SUCCESS) {
3762 talloc_free(ac);
3763 return ret;
3766 /* current partition control is needed by "replmd_op_callback" */
3767 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3768 ret = ldb_request_add_control(down_req,
3769 DSDB_CONTROL_CURRENT_PARTITION_OID,
3770 false, NULL);
3771 if (ret != LDB_SUCCESS) {
3772 talloc_free(ac);
3773 return ret;
3777 talloc_steal(down_req, msg);
3779 ret = add_time_element(msg, "whenChanged", t);
3780 if (ret != LDB_SUCCESS) {
3781 talloc_free(ac);
3782 ldb_operr(ldb);
3783 return ret;
3786 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3787 if (ret != LDB_SUCCESS) {
3788 talloc_free(ac);
3789 ldb_operr(ldb);
3790 return ret;
3793 /* go on with the call chain - do the modify after the rename */
3794 return ldb_next_request(ac->module, down_req);
3798 * remove links from objects that point at this object when an object
3799 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3800 * RemoveObj which states that link removal due to the object being
3801 * deleted is NOT an originating update - they just go away!
3804 static int replmd_delete_remove_link(struct ldb_module *module,
3805 const struct dsdb_schema *schema,
3806 struct replmd_private *replmd_private,
3807 struct ldb_dn *dn,
3808 struct GUID *guid,
3809 struct ldb_message_element *el,
3810 const struct dsdb_attribute *sa,
3811 struct ldb_request *parent)
3813 unsigned int i;
3814 TALLOC_CTX *tmp_ctx = talloc_new(module);
3815 struct ldb_context *ldb = ldb_module_get_ctx(module);
3817 for (i=0; i<el->num_values; i++) {
3818 struct dsdb_dn *dsdb_dn;
3819 int ret;
3820 struct ldb_message *msg;
3821 const struct dsdb_attribute *target_attr;
3822 struct ldb_message_element *el2;
3823 const char *dn_str;
3824 struct ldb_val dn_val;
3825 uint32_t dsdb_flags = 0;
3826 const char *attrs[] = { NULL, NULL };
3827 struct ldb_result *link_res;
3828 struct ldb_message *link_msg;
3829 struct ldb_message_element *link_el;
3830 struct parsed_dn *link_dns;
3831 struct parsed_dn *p = NULL, *unused = NULL;
3833 if (dsdb_dn_is_deleted_val(&el->values[i])) {
3834 continue;
3837 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
3838 if (!dsdb_dn) {
3839 talloc_free(tmp_ctx);
3840 return LDB_ERR_OPERATIONS_ERROR;
3843 /* remove the link */
3844 msg = ldb_msg_new(tmp_ctx);
3845 if (!msg) {
3846 ldb_module_oom(module);
3847 talloc_free(tmp_ctx);
3848 return LDB_ERR_OPERATIONS_ERROR;
3852 msg->dn = dsdb_dn->dn;
3854 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
3855 if (target_attr == NULL) {
3856 continue;
3858 attrs[0] = target_attr->lDAPDisplayName;
3860 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
3861 LDB_FLAG_MOD_DELETE, &el2);
3862 if (ret != LDB_SUCCESS) {
3863 ldb_module_oom(module);
3864 talloc_free(tmp_ctx);
3865 return LDB_ERR_OPERATIONS_ERROR;
3868 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
3869 msg->dn, attrs,
3870 DSDB_FLAG_NEXT_MODULE |
3871 DSDB_SEARCH_SHOW_EXTENDED_DN |
3872 DSDB_SEARCH_SHOW_RECYCLED,
3873 parent);
3875 if (ret != LDB_SUCCESS) {
3876 talloc_free(tmp_ctx);
3877 return ret;
3880 link_msg = link_res->msgs[0];
3881 link_el = ldb_msg_find_element(link_msg,
3882 target_attr->lDAPDisplayName);
3883 if (link_el == NULL) {
3884 talloc_free(tmp_ctx);
3885 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3889 * This call 'upgrades' the links in link_dns, but we
3890 * do not commit the result back into the database, so
3891 * this is safe to call in FL2000 or on databases that
3892 * have been run at that level in the past.
3894 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx,
3895 link_el, &link_dns,
3896 target_attr->syntax->ldap_oid, parent);
3897 if (ret != LDB_SUCCESS) {
3898 talloc_free(tmp_ctx);
3899 return ret;
3902 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
3903 guid, dn,
3904 data_blob_null, 0,
3905 &p, &unused,
3906 target_attr->syntax->ldap_oid, false);
3907 if (ret != LDB_SUCCESS) {
3908 talloc_free(tmp_ctx);
3909 return ret;
3912 if (p == NULL) {
3913 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3914 "Failed to find forward link on %s "
3915 "as %s to remove backlink %s on %s",
3916 ldb_dn_get_linearized(msg->dn),
3917 target_attr->lDAPDisplayName,
3918 sa->lDAPDisplayName,
3919 ldb_dn_get_linearized(dn));
3920 talloc_free(tmp_ctx);
3921 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3925 /* This needs to get the Binary DN, by first searching */
3926 dn_str = dsdb_dn_get_linearized(tmp_ctx,
3927 p->dsdb_dn);
3929 dn_val = data_blob_string_const(dn_str);
3930 el2->values = &dn_val;
3931 el2->num_values = 1;
3934 * Ensure that we tell the modification to vanish any linked
3935 * attributes (not simply mark them as isDeleted = TRUE)
3937 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
3939 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
3940 if (ret != LDB_SUCCESS) {
3941 talloc_free(tmp_ctx);
3942 return ret;
3945 talloc_free(tmp_ctx);
3946 return LDB_SUCCESS;
3951 handle update of replication meta data for deletion of objects
3953 This also handles the mapping of delete to a rename operation
3954 to allow deletes to be replicated.
3956 It also handles the incoming deleted objects, to ensure they are
3957 fully deleted here. In that case re_delete is true, and we do not
3958 use this as a signal to change the deleted state, just reinforce it.
3961 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
3963 int ret = LDB_ERR_OTHER;
3964 bool retb, disallow_move_on_delete;
3965 struct ldb_dn *old_dn = NULL, *new_dn = NULL;
3966 const char *rdn_name;
3967 const struct ldb_val *rdn_value, *new_rdn_value;
3968 struct GUID guid;
3969 struct ldb_context *ldb = ldb_module_get_ctx(module);
3970 const struct dsdb_schema *schema;
3971 struct ldb_message *msg, *old_msg;
3972 struct ldb_message_element *el;
3973 TALLOC_CTX *tmp_ctx;
3974 struct ldb_result *res, *parent_res;
3975 static const char * const preserved_attrs[] = {
3976 /* yes, this really is a hard coded list. See MS-ADTS
3977 section 3.1.1.5.5.1.1 */
3978 "attributeID",
3979 "attributeSyntax",
3980 "dNReferenceUpdate",
3981 "dNSHostName",
3982 "flatName",
3983 "governsID",
3984 "groupType",
3985 "instanceType",
3986 "lDAPDisplayName",
3987 "legacyExchangeDN",
3988 "isDeleted",
3989 "isRecycled",
3990 "lastKnownParent",
3991 "msDS-LastKnownRDN",
3992 "msDS-PortLDAP",
3993 "mS-DS-CreatorSID",
3994 "mSMQOwnerID",
3995 "nCName",
3996 "objectClass",
3997 "distinguishedName",
3998 "objectGUID",
3999 "objectSid",
4000 "oMSyntax",
4001 "proxiedObjectName",
4002 "name",
4003 "nTSecurityDescriptor",
4004 "replPropertyMetaData",
4005 "sAMAccountName",
4006 "securityIdentifier",
4007 "sIDHistory",
4008 "subClassOf",
4009 "systemFlags",
4010 "trustPartner",
4011 "trustDirection",
4012 "trustType",
4013 "trustAttributes",
4014 "userAccountControl",
4015 "uSNChanged",
4016 "uSNCreated",
4017 "whenCreated",
4018 "whenChanged",
4019 NULL
4021 static const char * const all_attrs[] = {
4022 DSDB_SECRET_ATTRIBUTES,
4023 "*",
4024 NULL
4026 static const struct ldb_val true_val = {
4027 .data = discard_const_p(uint8_t, "TRUE"),
4028 .length = 4
4031 unsigned int i;
4032 uint32_t dsdb_flags = 0;
4033 struct replmd_private *replmd_private;
4034 enum deletion_state deletion_state, next_deletion_state;
4036 if (ldb_dn_is_special(req->op.del.dn)) {
4037 return ldb_next_request(module, req);
4041 * We have to allow dbcheck to remove an object that
4042 * is beyond repair, and to do so totally. This could
4043 * mean we we can get a partial object from the other
4044 * DC, causing havoc, so dbcheck suggests
4045 * re-replication first. dbcheck sets both DBCHECK
4046 * and RELAX in this situation.
4048 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
4049 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
4050 /* really, really remove it */
4051 return ldb_next_request(module, req);
4054 tmp_ctx = talloc_new(ldb);
4055 if (!tmp_ctx) {
4056 ldb_oom(ldb);
4057 return LDB_ERR_OPERATIONS_ERROR;
4060 schema = dsdb_get_schema(ldb, tmp_ctx);
4061 if (!schema) {
4062 talloc_free(tmp_ctx);
4063 return LDB_ERR_OPERATIONS_ERROR;
4066 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
4068 /* we need the complete msg off disk, so we can work out which
4069 attributes need to be removed */
4070 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
4071 DSDB_FLAG_NEXT_MODULE |
4072 DSDB_SEARCH_SHOW_RECYCLED |
4073 DSDB_SEARCH_REVEAL_INTERNALS |
4074 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
4075 if (ret != LDB_SUCCESS) {
4076 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4077 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
4078 re_delete ? "re-delete" : "delete",
4079 ldb_dn_get_linearized(old_dn),
4080 ldb_errstring(ldb_module_get_ctx(module)));
4081 talloc_free(tmp_ctx);
4082 return ret;
4084 old_msg = res->msgs[0];
4086 replmd_deletion_state(module, old_msg,
4087 &deletion_state,
4088 &next_deletion_state);
4090 /* This supports us noticing an incoming isDeleted and acting on it */
4091 if (re_delete) {
4092 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
4093 next_deletion_state = deletion_state;
4096 if (next_deletion_state == OBJECT_REMOVED) {
4098 * We have to prevent objects being deleted, even if
4099 * the administrator really wants them gone, as
4100 * without the tombstone, we can get a partial object
4101 * from the other DC, causing havoc.
4103 * The only other valid case is when the 180 day
4104 * timeout has expired, when relax is specified.
4106 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
4107 /* it is already deleted - really remove it this time */
4108 talloc_free(tmp_ctx);
4109 return ldb_next_request(module, req);
4112 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
4113 "This check is to prevent corruption of the replicated state.",
4114 ldb_dn_get_linearized(old_msg->dn));
4115 return LDB_ERR_UNWILLING_TO_PERFORM;
4118 rdn_name = ldb_dn_get_rdn_name(old_dn);
4119 rdn_value = ldb_dn_get_rdn_val(old_dn);
4120 if ((rdn_name == NULL) || (rdn_value == NULL)) {
4121 talloc_free(tmp_ctx);
4122 return ldb_operr(ldb);
4125 msg = ldb_msg_new(tmp_ctx);
4126 if (msg == NULL) {
4127 ldb_module_oom(module);
4128 talloc_free(tmp_ctx);
4129 return LDB_ERR_OPERATIONS_ERROR;
4132 msg->dn = old_dn;
4134 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4135 disallow_move_on_delete =
4136 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4137 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4139 /* work out where we will be renaming this object to */
4140 if (!disallow_move_on_delete) {
4141 struct ldb_dn *deleted_objects_dn;
4142 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4143 &deleted_objects_dn);
4146 * We should not move objects if we can't find the
4147 * deleted objects DN. Not moving (or otherwise
4148 * harming) the Deleted Objects DN itself is handled
4149 * in the caller.
4151 if (re_delete && (ret != LDB_SUCCESS)) {
4152 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4153 if (new_dn == NULL) {
4154 ldb_module_oom(module);
4155 talloc_free(tmp_ctx);
4156 return LDB_ERR_OPERATIONS_ERROR;
4158 } else if (ret != LDB_SUCCESS) {
4159 /* this is probably an attempted delete on a partition
4160 * that doesn't allow delete operations, such as the
4161 * schema partition */
4162 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4163 ldb_dn_get_linearized(old_dn));
4164 talloc_free(tmp_ctx);
4165 return LDB_ERR_UNWILLING_TO_PERFORM;
4166 } else {
4167 new_dn = deleted_objects_dn;
4169 } else {
4170 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4171 if (new_dn == NULL) {
4172 ldb_module_oom(module);
4173 talloc_free(tmp_ctx);
4174 return LDB_ERR_OPERATIONS_ERROR;
4178 /* get the objects GUID from the search we just did */
4179 guid = samdb_result_guid(old_msg, "objectGUID");
4181 if (deletion_state == OBJECT_NOT_DELETED) {
4182 struct ldb_message_element *is_deleted_el;
4184 ret = replmd_make_deleted_child_dn(tmp_ctx,
4185 ldb,
4186 new_dn,
4187 rdn_name, rdn_value,
4188 guid);
4190 if (ret != LDB_SUCCESS) {
4191 talloc_free(tmp_ctx);
4192 return ret;
4195 ret = ldb_msg_add_value(msg, "isDeleted", &true_val,
4196 &is_deleted_el);
4197 if (ret != LDB_SUCCESS) {
4198 ldb_asprintf_errstring(ldb, __location__
4199 ": Failed to add isDeleted string to the msg");
4200 talloc_free(tmp_ctx);
4201 return ret;
4203 is_deleted_el->flags = LDB_FLAG_MOD_REPLACE;
4204 } else {
4206 * No matter what has happened with other renames etc, try again to
4207 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4210 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4211 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4212 if (!retb) {
4213 ldb_asprintf_errstring(ldb, __location__
4214 ": Unable to add a prepare rdn of %s",
4215 ldb_dn_get_linearized(rdn));
4216 talloc_free(tmp_ctx);
4217 return LDB_ERR_OPERATIONS_ERROR;
4219 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4221 retb = ldb_dn_add_child(new_dn, rdn);
4222 if (!retb) {
4223 ldb_asprintf_errstring(ldb, __location__
4224 ": Unable to add rdn %s to base dn: %s",
4225 ldb_dn_get_linearized(rdn),
4226 ldb_dn_get_linearized(new_dn));
4227 talloc_free(tmp_ctx);
4228 return LDB_ERR_OPERATIONS_ERROR;
4233 now we need to modify the object in the following ways:
4235 - add isDeleted=TRUE
4236 - update rDN and name, with new rDN
4237 - remove linked attributes
4238 - remove objectCategory and sAMAccountType
4239 - remove attribs not on the preserved list
4240 - preserved if in above list, or is rDN
4241 - remove all linked attribs from this object
4242 - remove all links from other objects to this object
4243 - add lastKnownParent
4244 - update replPropertyMetaData?
4246 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4249 if (deletion_state == OBJECT_NOT_DELETED) {
4250 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4251 char *parent_dn_str = NULL;
4252 struct ldb_message_element *p_el;
4254 /* we need the storage form of the parent GUID */
4255 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4256 parent_dn, NULL,
4257 DSDB_FLAG_NEXT_MODULE |
4258 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4259 DSDB_SEARCH_REVEAL_INTERNALS|
4260 DSDB_SEARCH_SHOW_RECYCLED, req);
4261 if (ret != LDB_SUCCESS) {
4262 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4263 "repmd_delete: Failed to %s %s, "
4264 "because we failed to find it's parent (%s): %s",
4265 re_delete ? "re-delete" : "delete",
4266 ldb_dn_get_linearized(old_dn),
4267 ldb_dn_get_linearized(parent_dn),
4268 ldb_errstring(ldb_module_get_ctx(module)));
4269 talloc_free(tmp_ctx);
4270 return ret;
4274 * Now we can use the DB version,
4275 * it will have the extended DN info in it
4277 parent_dn = parent_res->msgs[0]->dn;
4278 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4279 parent_dn,
4281 if (parent_dn_str == NULL) {
4282 talloc_free(tmp_ctx);
4283 return ldb_module_oom(module);
4286 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4287 parent_dn_str);
4288 if (ret != LDB_SUCCESS) {
4289 ldb_asprintf_errstring(ldb, __location__
4290 ": Failed to add lastKnownParent "
4291 "string when deleting %s",
4292 ldb_dn_get_linearized(old_dn));
4293 talloc_free(tmp_ctx);
4294 return ret;
4296 p_el = ldb_msg_find_element(msg,
4297 "lastKnownParent");
4298 if (p_el == NULL) {
4299 talloc_free(tmp_ctx);
4300 return ldb_module_operr(module);
4302 p_el->flags = LDB_FLAG_MOD_REPLACE;
4304 if (next_deletion_state == OBJECT_DELETED) {
4305 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4306 if (ret != LDB_SUCCESS) {
4307 ldb_asprintf_errstring(ldb, __location__
4308 ": Failed to add msDS-LastKnownRDN "
4309 "string when deleting %s",
4310 ldb_dn_get_linearized(old_dn));
4311 talloc_free(tmp_ctx);
4312 return ret;
4314 p_el = ldb_msg_find_element(msg,
4315 "msDS-LastKnownRDN");
4316 if (p_el == NULL) {
4317 talloc_free(tmp_ctx);
4318 return ldb_module_operr(module);
4320 p_el->flags = LDB_FLAG_MOD_ADD;
4324 switch (next_deletion_state) {
4326 case OBJECT_RECYCLED:
4327 case OBJECT_TOMBSTONE:
4330 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4331 * describes what must be removed from a tombstone
4332 * object
4334 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4335 * describes what must be removed from a recycled
4336 * object
4341 * we also mark it as recycled, meaning this object can't be
4342 * recovered (we are stripping its attributes).
4343 * This is done only if we have this schema object of course ...
4344 * This behavior is identical to the one of Windows 2008R2 which
4345 * always set the isRecycled attribute, even if the recycle-bin is
4346 * not activated and what ever the forest level is.
4348 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4349 struct ldb_message_element *is_recycled_el;
4351 ret = ldb_msg_add_value(msg, "isRecycled", &true_val,
4352 &is_recycled_el);
4353 if (ret != LDB_SUCCESS) {
4354 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4355 ldb_module_oom(module);
4356 talloc_free(tmp_ctx);
4357 return ret;
4359 is_recycled_el->flags = LDB_FLAG_MOD_REPLACE;
4362 replmd_private = talloc_get_type(ldb_module_get_private(module),
4363 struct replmd_private);
4364 /* work out which of the old attributes we will be removing */
4365 for (i=0; i<old_msg->num_elements; i++) {
4366 const struct dsdb_attribute *sa;
4367 el = &old_msg->elements[i];
4368 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4369 if (!sa) {
4370 talloc_free(tmp_ctx);
4371 return LDB_ERR_OPERATIONS_ERROR;
4373 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4374 /* don't remove the rDN */
4375 continue;
4378 if (sa->linkID & 1) {
4380 we have a backlink in this object
4381 that needs to be removed. We're not
4382 allowed to remove it directly
4383 however, so we instead setup a
4384 modify to delete the corresponding
4385 forward link
4387 ret = replmd_delete_remove_link(module, schema,
4388 replmd_private,
4389 old_dn, &guid,
4390 el, sa, req);
4391 if (ret == LDB_SUCCESS) {
4393 * now we continue, which means we
4394 * won't remove this backlink
4395 * directly
4397 continue;
4400 if (ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
4401 const char *old_dn_str
4402 = ldb_dn_get_linearized(old_dn);
4403 ldb_asprintf_errstring(ldb,
4404 __location__
4405 ": Failed to remove backlink of "
4406 "%s when deleting %s: %s",
4407 el->name,
4408 old_dn_str,
4409 ldb_errstring(ldb));
4410 talloc_free(tmp_ctx);
4411 return LDB_ERR_OPERATIONS_ERROR;
4415 * Otherwise vanish the link, we are
4416 * out of sync and the controlling
4417 * object does not have the source
4418 * link any more
4421 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4423 } else if (sa->linkID == 0) {
4424 if (ldb_attr_in_list(preserved_attrs, el->name)) {
4425 continue;
4427 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4428 continue;
4430 } else {
4432 * Ensure that we tell the modification to vanish any linked
4433 * attributes (not simply mark them as isDeleted = TRUE)
4435 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4437 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4438 if (ret != LDB_SUCCESS) {
4439 talloc_free(tmp_ctx);
4440 ldb_module_oom(module);
4441 return ret;
4445 break;
4447 case OBJECT_DELETED:
4449 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4450 * describes what must be removed from a deleted
4451 * object
4454 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4455 if (ret != LDB_SUCCESS) {
4456 talloc_free(tmp_ctx);
4457 ldb_module_oom(module);
4458 return ret;
4461 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4462 if (ret != LDB_SUCCESS) {
4463 talloc_free(tmp_ctx);
4464 ldb_module_oom(module);
4465 return ret;
4468 break;
4470 default:
4471 break;
4474 if (deletion_state == OBJECT_NOT_DELETED) {
4475 const struct dsdb_attribute *sa;
4477 /* work out what the new rdn value is, for updating the
4478 rDN and name fields */
4479 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4480 if (new_rdn_value == NULL) {
4481 talloc_free(tmp_ctx);
4482 return ldb_operr(ldb);
4485 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4486 if (!sa) {
4487 talloc_free(tmp_ctx);
4488 return LDB_ERR_OPERATIONS_ERROR;
4491 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4492 &el);
4493 if (ret != LDB_SUCCESS) {
4494 talloc_free(tmp_ctx);
4495 return ret;
4497 el->flags = LDB_FLAG_MOD_REPLACE;
4499 el = ldb_msg_find_element(old_msg, "name");
4500 if (el) {
4501 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4502 if (ret != LDB_SUCCESS) {
4503 talloc_free(tmp_ctx);
4504 return ret;
4506 el->flags = LDB_FLAG_MOD_REPLACE;
4511 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4516 * No matter what has happned with other renames, try again to
4517 * get this to be under the deleted DN.
4519 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4520 /* now rename onto the new DN */
4521 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4522 if (ret != LDB_SUCCESS){
4523 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4524 ldb_dn_get_linearized(old_dn),
4525 ldb_dn_get_linearized(new_dn),
4526 ldb_errstring(ldb)));
4527 talloc_free(tmp_ctx);
4528 return ret;
4530 msg->dn = new_dn;
4533 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4534 if (ret != LDB_SUCCESS) {
4535 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
4536 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
4537 talloc_free(tmp_ctx);
4538 return ret;
4541 talloc_free(tmp_ctx);
4543 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4546 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4548 return replmd_delete_internals(module, req, false);
4552 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4554 return ret;
4557 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4559 int ret = LDB_ERR_OTHER;
4560 /* TODO: do some error mapping */
4562 /* Let the caller know the full WERROR */
4563 ar->objs->error = status;
4565 return ret;
4569 static struct replPropertyMetaData1 *
4570 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4571 enum drsuapi_DsAttributeId attid)
4573 uint32_t i;
4574 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4576 for (i = 0; i < rpmd_ctr->count; i++) {
4577 if (rpmd_ctr->array[i].attid == attid) {
4578 return &rpmd_ctr->array[i];
4581 return NULL;
4586 return true if an update is newer than an existing entry
4587 see section 5.11 of MS-ADTS
4589 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4590 const struct GUID *update_invocation_id,
4591 uint32_t current_version,
4592 uint32_t update_version,
4593 NTTIME current_change_time,
4594 NTTIME update_change_time)
4596 if (update_version != current_version) {
4597 return update_version > current_version;
4599 if (update_change_time != current_change_time) {
4600 return update_change_time > current_change_time;
4602 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4605 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4606 struct replPropertyMetaData1 *new_m)
4608 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4609 &new_m->originating_invocation_id,
4610 cur_m->version,
4611 new_m->version,
4612 cur_m->originating_change_time,
4613 new_m->originating_change_time);
4616 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4617 struct replPropertyMetaData1 *cur_m,
4618 struct replPropertyMetaData1 *new_m)
4620 bool cmp;
4623 * If the new replPropertyMetaData entry for this attribute is
4624 * not provided (this happens in the case where we look for
4625 * ATTID_name, but the name was not changed), then the local
4626 * state is clearly still current, as the remote
4627 * server didn't send it due to being older the high watermark
4628 * USN we sent.
4630 if (new_m == NULL) {
4631 return false;
4634 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4636 * if we compare equal then do an
4637 * update. This is used when a client
4638 * asks for a FULL_SYNC, and can be
4639 * used to recover a corrupt
4640 * replica.
4642 * This call is a bit tricky, what we
4643 * are doing it turning the 'is_newer'
4644 * call into a 'not is older' by
4645 * swapping cur_m and new_m, and negating the
4646 * outcome.
4648 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4649 cur_m);
4650 } else {
4651 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4652 new_m);
4654 return cmp;
4659 form a DN for a deleted (DEL:) or conflict (CNF:) DN
4661 static int replmd_make_prefix_child_dn(TALLOC_CTX *tmp_ctx,
4662 struct ldb_context *ldb,
4663 struct ldb_dn *dn,
4664 const char *four_char_prefix,
4665 const char *rdn_name,
4666 const struct ldb_val *rdn_value,
4667 struct GUID guid)
4669 struct ldb_val deleted_child_rdn_val;
4670 struct GUID_txt_buf guid_str;
4671 int ret;
4672 bool retb;
4674 GUID_buf_string(&guid, &guid_str);
4676 retb = ldb_dn_add_child_fmt(dn, "X=Y");
4677 if (!retb) {
4678 ldb_asprintf_errstring(ldb, __location__
4679 ": Unable to add a formatted child to dn: %s",
4680 ldb_dn_get_linearized(dn));
4681 return LDB_ERR_OPERATIONS_ERROR;
4685 * TODO: Per MS-ADTS 3.1.1.5.5 Delete Operation
4686 * we should truncate this value to ensure the RDN is not more than 255 chars.
4688 * However we MS-ADTS 3.1.1.5.1.2 Naming Constraints indicates that:
4690 * "Naming constraints are not enforced for replicated
4691 * updates." so this is safe and we don't have to work out not
4692 * splitting a UTF8 char right now.
4694 deleted_child_rdn_val = ldb_val_dup(tmp_ctx, rdn_value);
4697 * sizeof(guid_str.buf) will always be longer than
4698 * strlen(guid_str.buf) but we allocate using this and
4699 * waste the trailing bytes to avoid scaring folks
4700 * with memcpy() using strlen() below
4703 deleted_child_rdn_val.data
4704 = talloc_realloc(tmp_ctx, deleted_child_rdn_val.data,
4705 uint8_t,
4706 rdn_value->length + 5
4707 + sizeof(guid_str.buf));
4708 if (!deleted_child_rdn_val.data) {
4709 ldb_asprintf_errstring(ldb, __location__
4710 ": Unable to add a formatted child to dn: %s",
4711 ldb_dn_get_linearized(dn));
4712 return LDB_ERR_OPERATIONS_ERROR;
4715 deleted_child_rdn_val.length =
4716 rdn_value->length + 5
4717 + strlen(guid_str.buf);
4719 SMB_ASSERT(deleted_child_rdn_val.length <
4720 talloc_get_size(deleted_child_rdn_val.data));
4723 * talloc won't allocate more than 256MB so we can't
4724 * overflow but just to be sure
4726 if (deleted_child_rdn_val.length < rdn_value->length) {
4727 return LDB_ERR_OPERATIONS_ERROR;
4730 deleted_child_rdn_val.data[rdn_value->length] = 0x0a;
4731 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 1],
4732 four_char_prefix, 4);
4733 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 5],
4734 guid_str.buf,
4735 sizeof(guid_str.buf));
4737 /* Now set the value into the RDN, without parsing it */
4738 ret = ldb_dn_set_component(
4741 rdn_name,
4742 deleted_child_rdn_val);
4744 return ret;
4749 form a conflict DN
4751 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx,
4752 struct ldb_context *ldb,
4753 struct ldb_dn *dn,
4754 struct GUID *guid)
4756 const struct ldb_val *rdn_val;
4757 const char *rdn_name;
4758 struct ldb_dn *new_dn;
4759 int ret;
4761 rdn_val = ldb_dn_get_rdn_val(dn);
4762 rdn_name = ldb_dn_get_rdn_name(dn);
4763 if (!rdn_val || !rdn_name) {
4764 return NULL;
4767 new_dn = ldb_dn_get_parent(mem_ctx, dn);
4768 if (!new_dn) {
4769 return NULL;
4772 ret = replmd_make_prefix_child_dn(mem_ctx,
4773 ldb, new_dn,
4774 "CNF:",
4775 rdn_name,
4776 rdn_val,
4777 *guid);
4778 if (ret != LDB_SUCCESS) {
4779 return NULL;
4781 return new_dn;
4785 form a deleted DN
4787 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
4788 struct ldb_context *ldb,
4789 struct ldb_dn *dn,
4790 const char *rdn_name,
4791 const struct ldb_val *rdn_value,
4792 struct GUID guid)
4794 return replmd_make_prefix_child_dn(tmp_ctx,
4795 ldb, dn,
4796 "DEL:",
4797 rdn_name,
4798 rdn_value,
4799 guid);
4804 perform a modify operation which sets the rDN and name attributes to
4805 their current values. This has the effect of changing these
4806 attributes to have been last updated by the current DC. This is
4807 needed to ensure that renames performed as part of conflict
4808 resolution are propagated to other DCs
4810 static int replmd_name_modify(struct replmd_replicated_request *ar,
4811 struct ldb_request *req, struct ldb_dn *dn)
4813 struct ldb_message *msg;
4814 const char *rdn_name;
4815 const struct ldb_val *rdn_val;
4816 const struct dsdb_attribute *rdn_attr;
4817 int ret;
4819 msg = ldb_msg_new(req);
4820 if (msg == NULL) {
4821 goto failed;
4823 msg->dn = dn;
4825 rdn_name = ldb_dn_get_rdn_name(dn);
4826 if (rdn_name == NULL) {
4827 goto failed;
4830 /* normalize the rdn attribute name */
4831 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
4832 if (rdn_attr == NULL) {
4833 goto failed;
4835 rdn_name = rdn_attr->lDAPDisplayName;
4837 rdn_val = ldb_dn_get_rdn_val(dn);
4838 if (rdn_val == NULL) {
4839 goto failed;
4842 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4843 goto failed;
4845 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
4846 goto failed;
4848 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4849 goto failed;
4851 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
4852 goto failed;
4856 * We have to mark this as a replicated update otherwise
4857 * schema_data may reject a rename in the schema partition
4860 ret = dsdb_module_modify(ar->module, msg,
4861 DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
4862 req);
4863 if (ret != LDB_SUCCESS) {
4864 DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
4865 ldb_dn_get_linearized(dn),
4866 ldb_errstring(ldb_module_get_ctx(ar->module))));
4867 return ret;
4870 talloc_free(msg);
4872 return LDB_SUCCESS;
4874 failed:
4875 talloc_free(msg);
4876 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
4877 ldb_dn_get_linearized(dn)));
4878 return LDB_ERR_OPERATIONS_ERROR;
4883 callback for conflict DN handling where we have renamed the incoming
4884 record. After renaming it, we need to ensure the change of name and
4885 rDN for the incoming record is seen as an originating update by this DC.
4887 This also handles updating lastKnownParent for entries sent to lostAndFound
4889 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
4891 struct replmd_replicated_request *ar =
4892 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4893 struct ldb_dn *conflict_dn = NULL;
4894 int ret;
4896 if (ares->error != LDB_SUCCESS) {
4897 /* call the normal callback for everything except success */
4898 return replmd_op_callback(req, ares);
4901 switch (req->operation) {
4902 case LDB_ADD:
4903 conflict_dn = req->op.add.message->dn;
4904 break;
4905 case LDB_MODIFY:
4906 conflict_dn = req->op.mod.message->dn;
4907 break;
4908 default:
4909 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
4912 /* perform a modify of the rDN and name of the record */
4913 ret = replmd_name_modify(ar, req, conflict_dn);
4914 if (ret != LDB_SUCCESS) {
4915 ares->error = ret;
4916 return replmd_op_callback(req, ares);
4919 if (ar->objs->objects[ar->index_current].last_known_parent) {
4920 struct ldb_message *msg = ldb_msg_new(req);
4921 if (msg == NULL) {
4922 ldb_module_oom(ar->module);
4923 return LDB_ERR_OPERATIONS_ERROR;
4926 msg->dn = req->op.add.message->dn;
4928 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4929 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
4930 if (ret != LDB_SUCCESS) {
4931 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
4932 ldb_module_oom(ar->module);
4933 return ret;
4935 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
4937 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
4938 if (ret != LDB_SUCCESS) {
4939 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
4940 ldb_dn_get_linearized(msg->dn),
4941 ldb_errstring(ldb_module_get_ctx(ar->module))));
4942 return ret;
4944 TALLOC_FREE(msg);
4947 return replmd_op_callback(req, ares);
4951 callback for replmd_replicated_apply_add()
4952 This copes with the creation of conflict records in the case where
4953 the DN exists, but with a different objectGUID
4955 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))
4957 struct ldb_dn *conflict_dn;
4958 struct replmd_replicated_request *ar =
4959 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4960 struct ldb_result *res;
4961 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4962 int ret;
4963 const struct ldb_val *omd_value;
4964 struct replPropertyMetaDataBlob omd, *rmd;
4965 enum ndr_err_code ndr_err;
4966 bool rename_incoming_record, rodc;
4967 struct replPropertyMetaData1 *rmd_name, *omd_name;
4968 struct ldb_message *msg;
4969 struct ldb_request *down_req = NULL;
4971 /* call the normal callback for success */
4972 if (ares->error == LDB_SUCCESS) {
4973 return callback(req, ares);
4977 * we have a conflict, and need to decide if we will keep the
4978 * new record or the old record
4981 msg = ar->objs->objects[ar->index_current].msg;
4982 conflict_dn = msg->dn;
4984 /* For failures other than conflicts, fail the whole operation here */
4985 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4986 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
4987 ldb_dn_get_linearized(conflict_dn),
4988 ldb_errstring(ldb_module_get_ctx(ar->module)));
4990 return ldb_module_done(ar->req, NULL, NULL,
4991 LDB_ERR_OPERATIONS_ERROR);
4994 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4995 if (ret != LDB_SUCCESS) {
4996 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)));
4997 return ldb_module_done(ar->req, NULL, NULL,
4998 LDB_ERR_OPERATIONS_ERROR);
5002 if (rodc) {
5004 * We are on an RODC, or were a GC for this
5005 * partition, so we have to fail this until
5006 * someone who owns the partition sorts it
5007 * out
5009 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5010 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
5011 " - We must fail the operation until a master for this partition resolves the conflict",
5012 ldb_dn_get_linearized(conflict_dn));
5013 ret = LDB_ERR_OPERATIONS_ERROR;
5014 goto failed;
5018 * first we need the replPropertyMetaData attribute from the
5019 * local, conflicting record
5021 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
5022 attrs,
5023 DSDB_FLAG_NEXT_MODULE |
5024 DSDB_SEARCH_SHOW_DELETED |
5025 DSDB_SEARCH_SHOW_RECYCLED, req);
5026 if (ret != LDB_SUCCESS) {
5027 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5028 ldb_dn_get_linearized(conflict_dn)));
5029 goto failed;
5032 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5033 if (omd_value == NULL) {
5034 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5035 ldb_dn_get_linearized(conflict_dn)));
5036 goto failed;
5039 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5040 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5041 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5042 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5043 ldb_dn_get_linearized(conflict_dn)));
5044 goto failed;
5047 rmd = ar->objs->objects[ar->index_current].meta_data;
5050 * we decide which is newer based on the RPMD on the name
5051 * attribute. See [MS-DRSR] ResolveNameConflict.
5053 * We expect omd_name to be present, as this is from a local
5054 * search, but while rmd_name should have been given to us by
5055 * the remote server, if it is missing we just prefer the
5056 * local name in
5057 * replmd_replPropertyMetaData1_new_should_be_taken()
5059 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5060 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5061 if (!omd_name) {
5062 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5063 ldb_dn_get_linearized(conflict_dn)));
5064 goto failed;
5068 * Should we preserve the current record, and so rename the
5069 * incoming record to be a conflict?
5071 rename_incoming_record
5072 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5073 omd_name, rmd_name);
5075 if (rename_incoming_record) {
5076 struct GUID guid;
5077 struct ldb_dn *new_dn;
5079 guid = samdb_result_guid(msg, "objectGUID");
5080 if (GUID_all_zero(&guid)) {
5081 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
5082 ldb_dn_get_linearized(conflict_dn)));
5083 goto failed;
5085 new_dn = replmd_conflict_dn(req,
5086 ldb_module_get_ctx(ar->module),
5087 conflict_dn, &guid);
5088 if (new_dn == NULL) {
5089 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5090 ldb_dn_get_linearized(conflict_dn)));
5091 goto failed;
5094 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
5095 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5097 /* re-submit the request, but with the new DN */
5098 callback = replmd_op_name_modify_callback;
5099 msg->dn = new_dn;
5100 } else {
5101 /* we are renaming the existing record */
5102 struct GUID guid;
5103 struct ldb_dn *new_dn;
5105 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5106 if (GUID_all_zero(&guid)) {
5107 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5108 ldb_dn_get_linearized(conflict_dn)));
5109 goto failed;
5112 new_dn = replmd_conflict_dn(req,
5113 ldb_module_get_ctx(ar->module),
5114 conflict_dn, &guid);
5115 if (new_dn == NULL) {
5116 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5117 ldb_dn_get_linearized(conflict_dn)));
5118 goto failed;
5121 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5122 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5124 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5125 DSDB_FLAG_OWN_MODULE, req);
5126 if (ret != LDB_SUCCESS) {
5127 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5128 ldb_dn_get_linearized(conflict_dn),
5129 ldb_dn_get_linearized(new_dn),
5130 ldb_errstring(ldb_module_get_ctx(ar->module))));
5131 goto failed;
5135 * now we need to ensure that the rename is seen as an
5136 * originating update. We do that with a modify.
5138 ret = replmd_name_modify(ar, req, new_dn);
5139 if (ret != LDB_SUCCESS) {
5140 goto failed;
5143 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
5144 ldb_dn_get_linearized(req->op.add.message->dn)));
5147 ret = ldb_build_add_req(&down_req,
5148 ldb_module_get_ctx(ar->module),
5149 req,
5150 msg,
5151 ar->controls,
5153 callback,
5154 req);
5155 if (ret != LDB_SUCCESS) {
5156 goto failed;
5158 LDB_REQ_SET_LOCATION(down_req);
5160 /* current partition control needed by "repmd_op_callback" */
5161 ret = ldb_request_add_control(down_req,
5162 DSDB_CONTROL_CURRENT_PARTITION_OID,
5163 false, NULL);
5164 if (ret != LDB_SUCCESS) {
5165 return replmd_replicated_request_error(ar, ret);
5168 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5169 /* this tells the partition module to make it a
5170 partial replica if creating an NC */
5171 ret = ldb_request_add_control(down_req,
5172 DSDB_CONTROL_PARTIAL_REPLICA,
5173 false, NULL);
5174 if (ret != LDB_SUCCESS) {
5175 return replmd_replicated_request_error(ar, ret);
5180 * Finally we re-run the add, otherwise the new record won't
5181 * exist, as we are here because of that exact failure!
5183 return ldb_next_request(ar->module, down_req);
5184 failed:
5186 /* on failure make the caller get the error. This means
5187 * replication will stop with an error, but there is not much
5188 * else we can do.
5190 if (ret == LDB_SUCCESS) {
5191 ret = LDB_ERR_OPERATIONS_ERROR;
5193 return ldb_module_done(ar->req, NULL, NULL,
5194 ret);
5198 callback for replmd_replicated_apply_add()
5199 This copes with the creation of conflict records in the case where
5200 the DN exists, but with a different objectGUID
5202 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
5204 struct replmd_replicated_request *ar =
5205 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5207 if (ar->objs->objects[ar->index_current].last_known_parent) {
5208 /* This is like a conflict DN, where we put the object in LostAndFound
5209 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
5210 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
5213 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
5217 this is called when a new object comes in over DRS
5219 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
5221 struct ldb_context *ldb;
5222 struct ldb_request *change_req;
5223 enum ndr_err_code ndr_err;
5224 struct ldb_message *msg;
5225 struct replPropertyMetaDataBlob *md;
5226 struct ldb_val md_value;
5227 unsigned int i;
5228 int ret;
5229 bool remote_isDeleted = false;
5230 bool is_schema_nc;
5231 NTTIME now;
5232 time_t t = time(NULL);
5233 const struct ldb_val *rdn_val;
5234 struct replmd_private *replmd_private =
5235 talloc_get_type(ldb_module_get_private(ar->module),
5236 struct replmd_private);
5237 unix_to_nt_time(&now, t);
5239 ldb = ldb_module_get_ctx(ar->module);
5240 msg = ar->objs->objects[ar->index_current].msg;
5241 md = ar->objs->objects[ar->index_current].meta_data;
5242 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5244 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5245 if (ret != LDB_SUCCESS) {
5246 return replmd_replicated_request_error(ar, ret);
5249 ret = dsdb_msg_add_guid(msg,
5250 &ar->objs->objects[ar->index_current].object_guid,
5251 "objectGUID");
5252 if (ret != LDB_SUCCESS) {
5253 return replmd_replicated_request_error(ar, ret);
5256 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5257 if (ret != LDB_SUCCESS) {
5258 return replmd_replicated_request_error(ar, ret);
5261 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
5262 if (ret != LDB_SUCCESS) {
5263 return replmd_replicated_request_error(ar, ret);
5266 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5267 if (ret != LDB_SUCCESS) {
5268 return replmd_replicated_request_error(ar, ret);
5271 /* remove any message elements that have zero values */
5272 for (i=0; i<msg->num_elements; i++) {
5273 struct ldb_message_element *el = &msg->elements[i];
5275 if (el->num_values == 0) {
5276 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5277 ldb_asprintf_errstring(ldb, __location__
5278 ": empty objectClass sent on %s, aborting replication\n",
5279 ldb_dn_get_linearized(msg->dn));
5280 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5283 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5284 el->name));
5285 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
5286 msg->num_elements--;
5287 i--;
5288 continue;
5292 if (DEBUGLVL(8)) {
5293 struct GUID_txt_buf guid_txt;
5295 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5296 LDB_CHANGETYPE_ADD,
5297 msg);
5298 DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5299 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5300 s));
5301 talloc_free(s);
5302 } else if (DEBUGLVL(4)) {
5303 struct GUID_txt_buf guid_txt;
5304 DEBUG(4, ("DRS replication add DN of %s is %s\n",
5305 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5306 ldb_dn_get_linearized(msg->dn)));
5308 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5309 "isDeleted", false);
5312 * the meta data array is already sorted by the caller, except
5313 * for the RDN, which needs to be added.
5317 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5318 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5319 md, ar, now, is_schema_nc,
5320 false);
5321 if (ret != LDB_SUCCESS) {
5322 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5323 return replmd_replicated_request_error(ar, ret);
5326 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5327 if (ret != LDB_SUCCESS) {
5328 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5329 return replmd_replicated_request_error(ar, ret);
5332 for (i=0; i < md->ctr.ctr1.count; i++) {
5333 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5335 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5336 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5337 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5338 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5339 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5341 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5342 if (ret != LDB_SUCCESS) {
5343 return replmd_replicated_request_error(ar, ret);
5346 replmd_ldb_message_sort(msg, ar->schema);
5348 if (!remote_isDeleted) {
5349 ret = dsdb_module_schedule_sd_propagation(ar->module,
5350 ar->objs->partition_dn,
5351 msg->dn, true);
5352 if (ret != LDB_SUCCESS) {
5353 return replmd_replicated_request_error(ar, ret);
5357 ar->isDeleted = remote_isDeleted;
5359 ret = ldb_build_add_req(&change_req,
5360 ldb,
5362 msg,
5363 ar->controls,
5365 replmd_op_add_callback,
5366 ar->req);
5367 LDB_REQ_SET_LOCATION(change_req);
5368 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5370 /* current partition control needed by "repmd_op_callback" */
5371 ret = ldb_request_add_control(change_req,
5372 DSDB_CONTROL_CURRENT_PARTITION_OID,
5373 false, NULL);
5374 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5376 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5377 /* this tells the partition module to make it a
5378 partial replica if creating an NC */
5379 ret = ldb_request_add_control(change_req,
5380 DSDB_CONTROL_PARTIAL_REPLICA,
5381 false, NULL);
5382 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5385 return ldb_next_request(ar->module, change_req);
5388 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5389 struct ldb_reply *ares)
5391 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5392 struct replmd_replicated_request);
5393 int ret;
5395 if (!ares) {
5396 return ldb_module_done(ar->req, NULL, NULL,
5397 LDB_ERR_OPERATIONS_ERROR);
5401 * The error NO_SUCH_OBJECT is not expected, unless the search
5402 * base is the partition DN, and that case doesn't happen here
5403 * because then we wouldn't get a parent_guid_value in any
5404 * case.
5406 if (ares->error != LDB_SUCCESS) {
5407 return ldb_module_done(ar->req, ares->controls,
5408 ares->response, ares->error);
5411 switch (ares->type) {
5412 case LDB_REPLY_ENTRY:
5414 struct ldb_message *parent_msg = ares->message;
5415 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5416 struct ldb_dn *parent_dn = NULL;
5417 int comp_num;
5419 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5420 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5421 /* Per MS-DRSR 4.1.10.6.10
5422 * FindBestParentObject we need to move this
5423 * new object under a deleted object to
5424 * lost-and-found */
5425 struct ldb_dn *nc_root;
5427 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5428 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5429 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5430 "No suitable NC root found for %s. "
5431 "We need to move this object because parent object %s "
5432 "is deleted, but this object is not.",
5433 ldb_dn_get_linearized(msg->dn),
5434 ldb_dn_get_linearized(parent_msg->dn));
5435 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5436 } else if (ret != LDB_SUCCESS) {
5437 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5438 "Unable to find NC root for %s: %s. "
5439 "We need to move this object because parent object %s "
5440 "is deleted, but this object is not.",
5441 ldb_dn_get_linearized(msg->dn),
5442 ldb_errstring(ldb_module_get_ctx(ar->module)),
5443 ldb_dn_get_linearized(parent_msg->dn));
5444 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5447 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5448 nc_root,
5449 DS_GUID_LOSTANDFOUND_CONTAINER,
5450 &parent_dn);
5451 if (ret != LDB_SUCCESS) {
5452 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5453 "Unable to find LostAndFound Container for %s "
5454 "in partition %s: %s. "
5455 "We need to move this object because parent object %s "
5456 "is deleted, but this object is not.",
5457 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5458 ldb_errstring(ldb_module_get_ctx(ar->module)),
5459 ldb_dn_get_linearized(parent_msg->dn));
5460 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5462 ar->objs->objects[ar->index_current].last_known_parent
5463 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5465 } else {
5466 parent_dn
5467 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5470 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5472 comp_num = ldb_dn_get_comp_num(msg->dn);
5473 if (comp_num > 1) {
5474 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5475 talloc_free(ares);
5476 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5479 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5480 talloc_free(ares);
5481 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5483 break;
5485 case LDB_REPLY_REFERRAL:
5486 /* we ignore referrals */
5487 break;
5489 case LDB_REPLY_DONE:
5491 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5492 struct GUID_txt_buf str_buf;
5493 if (ar->search_msg != NULL) {
5494 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5495 "No parent with GUID %s found for object locally known as %s",
5496 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5497 ldb_dn_get_linearized(ar->search_msg->dn));
5498 } else {
5499 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5500 "No parent with GUID %s found for object remotely known as %s",
5501 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5502 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5506 * This error code is really important, as it
5507 * is the flag back to the callers to retry
5508 * this with DRSUAPI_DRS_GET_ANC, and so get
5509 * the parent objects before the child
5510 * objects
5512 return ldb_module_done(ar->req, NULL, NULL,
5513 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5516 if (ar->search_msg != NULL) {
5517 ret = replmd_replicated_apply_merge(ar);
5518 } else {
5519 ret = replmd_replicated_apply_add(ar);
5521 if (ret != LDB_SUCCESS) {
5522 return ldb_module_done(ar->req, NULL, NULL, ret);
5526 talloc_free(ares);
5527 return LDB_SUCCESS;
5531 * Look for the parent object, so we put the new object in the right
5532 * place This is akin to NameObject in MS-DRSR - this routine and the
5533 * callbacks find the right parent name, and correct name for this
5534 * object
5537 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5539 struct ldb_context *ldb;
5540 int ret;
5541 char *tmp_str;
5542 char *filter;
5543 struct ldb_request *search_req;
5544 static const char *attrs[] = {"isDeleted", NULL};
5545 struct GUID_txt_buf guid_str_buf;
5547 ldb = ldb_module_get_ctx(ar->module);
5549 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5550 if (ar->search_msg != NULL) {
5551 return replmd_replicated_apply_merge(ar);
5552 } else {
5553 return replmd_replicated_apply_add(ar);
5557 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5558 &guid_str_buf);
5560 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5561 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5563 ret = ldb_build_search_req(&search_req,
5564 ldb,
5566 ar->objs->partition_dn,
5567 LDB_SCOPE_SUBTREE,
5568 filter,
5569 attrs,
5570 NULL,
5572 replmd_replicated_apply_search_for_parent_callback,
5573 ar->req);
5574 LDB_REQ_SET_LOCATION(search_req);
5576 ret = dsdb_request_add_controls(search_req,
5577 DSDB_SEARCH_SHOW_RECYCLED|
5578 DSDB_SEARCH_SHOW_DELETED|
5579 DSDB_SEARCH_SHOW_EXTENDED_DN);
5580 if (ret != LDB_SUCCESS) {
5581 return ret;
5584 return ldb_next_request(ar->module, search_req);
5588 handle renames that come in over DRS replication
5590 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5591 struct ldb_message *msg,
5592 struct ldb_request *parent,
5593 bool *renamed)
5595 int ret;
5596 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5597 struct ldb_result *res;
5598 struct ldb_dn *conflict_dn;
5599 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5600 const struct ldb_val *omd_value;
5601 struct replPropertyMetaDataBlob omd, *rmd;
5602 enum ndr_err_code ndr_err;
5603 bool rename_incoming_record, rodc;
5604 struct replPropertyMetaData1 *rmd_name, *omd_name;
5605 struct ldb_dn *new_dn;
5606 struct GUID guid;
5608 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5609 ldb_dn_get_linearized(ar->search_msg->dn),
5610 ldb_dn_get_linearized(msg->dn)));
5613 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5614 DSDB_FLAG_NEXT_MODULE, ar->req);
5615 if (ret == LDB_SUCCESS) {
5616 talloc_free(tmp_ctx);
5617 *renamed = true;
5618 return ret;
5621 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5622 talloc_free(tmp_ctx);
5623 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5624 ldb_dn_get_linearized(ar->search_msg->dn),
5625 ldb_dn_get_linearized(msg->dn),
5626 ldb_errstring(ldb_module_get_ctx(ar->module)));
5627 return ret;
5630 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5631 if (ret != LDB_SUCCESS) {
5632 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5633 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5634 ldb_errstring(ldb_module_get_ctx(ar->module)));
5635 return LDB_ERR_OPERATIONS_ERROR;
5638 * we have a conflict, and need to decide if we will keep the
5639 * new record or the old record
5642 conflict_dn = msg->dn;
5644 if (rodc) {
5646 * We are on an RODC, or were a GC for this
5647 * partition, so we have to fail this until
5648 * someone who owns the partition sorts it
5649 * out
5651 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5652 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5653 " - We must fail the operation until a master for this partition resolves the conflict",
5654 ldb_dn_get_linearized(conflict_dn));
5655 ret = LDB_ERR_OPERATIONS_ERROR;
5656 goto failed;
5660 * first we need the replPropertyMetaData attribute from the
5661 * old record
5663 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
5664 attrs,
5665 DSDB_FLAG_NEXT_MODULE |
5666 DSDB_SEARCH_SHOW_DELETED |
5667 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5668 if (ret != LDB_SUCCESS) {
5669 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5670 ldb_dn_get_linearized(conflict_dn)));
5671 goto failed;
5674 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5675 if (omd_value == NULL) {
5676 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5677 ldb_dn_get_linearized(conflict_dn)));
5678 goto failed;
5681 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5682 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5683 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5684 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5685 ldb_dn_get_linearized(conflict_dn)));
5686 goto failed;
5689 rmd = ar->objs->objects[ar->index_current].meta_data;
5692 * we decide which is newer based on the RPMD on the name
5693 * attribute. See [MS-DRSR] ResolveNameConflict.
5695 * We expect omd_name to be present, as this is from a local
5696 * search, but while rmd_name should have been given to us by
5697 * the remote server, if it is missing we just prefer the
5698 * local name in
5699 * replmd_replPropertyMetaData1_new_should_be_taken()
5701 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5702 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5703 if (!omd_name) {
5704 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5705 ldb_dn_get_linearized(conflict_dn)));
5706 goto failed;
5710 * Should we preserve the current record, and so rename the
5711 * incoming record to be a conflict?
5713 rename_incoming_record =
5714 !replmd_replPropertyMetaData1_new_should_be_taken(
5715 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5716 omd_name, rmd_name);
5718 if (rename_incoming_record) {
5720 new_dn = replmd_conflict_dn(msg,
5721 ldb_module_get_ctx(ar->module),
5722 msg->dn,
5723 &ar->objs->objects[ar->index_current].object_guid);
5724 if (new_dn == NULL) {
5725 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5726 "Failed to form conflict DN for %s\n",
5727 ldb_dn_get_linearized(msg->dn));
5729 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5732 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
5733 DSDB_FLAG_NEXT_MODULE, ar->req);
5734 if (ret != LDB_SUCCESS) {
5735 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5736 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5737 ldb_dn_get_linearized(conflict_dn),
5738 ldb_dn_get_linearized(ar->search_msg->dn),
5739 ldb_dn_get_linearized(new_dn),
5740 ldb_errstring(ldb_module_get_ctx(ar->module)));
5741 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5744 msg->dn = new_dn;
5745 *renamed = true;
5746 return LDB_SUCCESS;
5749 /* we are renaming the existing record */
5751 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5752 if (GUID_all_zero(&guid)) {
5753 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5754 ldb_dn_get_linearized(conflict_dn)));
5755 goto failed;
5758 new_dn = replmd_conflict_dn(tmp_ctx,
5759 ldb_module_get_ctx(ar->module),
5760 conflict_dn, &guid);
5761 if (new_dn == NULL) {
5762 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5763 ldb_dn_get_linearized(conflict_dn)));
5764 goto failed;
5767 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5768 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5770 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5771 DSDB_FLAG_OWN_MODULE, ar->req);
5772 if (ret != LDB_SUCCESS) {
5773 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5774 ldb_dn_get_linearized(conflict_dn),
5775 ldb_dn_get_linearized(new_dn),
5776 ldb_errstring(ldb_module_get_ctx(ar->module))));
5777 goto failed;
5781 * now we need to ensure that the rename is seen as an
5782 * originating update. We do that with a modify.
5784 ret = replmd_name_modify(ar, ar->req, new_dn);
5785 if (ret != LDB_SUCCESS) {
5786 goto failed;
5789 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
5790 ldb_dn_get_linearized(ar->search_msg->dn),
5791 ldb_dn_get_linearized(msg->dn)));
5794 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5795 DSDB_FLAG_NEXT_MODULE, ar->req);
5796 if (ret != LDB_SUCCESS) {
5797 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
5798 ldb_dn_get_linearized(ar->search_msg->dn),
5799 ldb_dn_get_linearized(msg->dn),
5800 ldb_errstring(ldb_module_get_ctx(ar->module))));
5801 goto failed;
5804 talloc_free(tmp_ctx);
5805 return ret;
5806 failed:
5808 * On failure make the caller get the error
5809 * This means replication will stop with an error,
5810 * but there is not much else we can do. In the
5811 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
5812 * needed.
5814 if (ret == LDB_SUCCESS) {
5815 ret = LDB_ERR_OPERATIONS_ERROR;
5818 talloc_free(tmp_ctx);
5819 return ret;
5823 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
5825 struct ldb_context *ldb;
5826 struct ldb_request *change_req;
5827 enum ndr_err_code ndr_err;
5828 struct ldb_message *msg;
5829 struct replPropertyMetaDataBlob *rmd;
5830 struct replPropertyMetaDataBlob omd;
5831 const struct ldb_val *omd_value;
5832 struct replPropertyMetaDataBlob nmd;
5833 struct ldb_val nmd_value;
5834 struct GUID remote_parent_guid;
5835 unsigned int i;
5836 uint32_t j,ni=0;
5837 unsigned int removed_attrs = 0;
5838 int ret;
5839 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
5840 bool isDeleted = false;
5841 bool local_isDeleted = false;
5842 bool remote_isDeleted = false;
5843 bool take_remote_isDeleted = false;
5844 bool sd_updated = false;
5845 bool renamed = false;
5846 bool is_schema_nc = false;
5847 NTSTATUS nt_status;
5848 const struct ldb_val *old_rdn, *new_rdn;
5849 struct replmd_private *replmd_private =
5850 talloc_get_type(ldb_module_get_private(ar->module),
5851 struct replmd_private);
5852 NTTIME now;
5853 time_t t = time(NULL);
5854 unix_to_nt_time(&now, t);
5856 ldb = ldb_module_get_ctx(ar->module);
5857 msg = ar->objs->objects[ar->index_current].msg;
5859 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5861 rmd = ar->objs->objects[ar->index_current].meta_data;
5862 ZERO_STRUCT(omd);
5863 omd.version = 1;
5865 /* find existing meta data */
5866 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5867 if (omd_value) {
5868 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5869 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5870 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5871 nt_status = ndr_map_error2ntstatus(ndr_err);
5872 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5875 if (omd.version != 1) {
5876 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5880 if (DEBUGLVL(8)) {
5881 struct GUID_txt_buf guid_txt;
5883 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5884 LDB_CHANGETYPE_MODIFY, msg);
5885 DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
5886 "%s\n"
5887 "%s\n",
5888 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5890 ndr_print_struct_string(s,
5891 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5892 "existing replPropertyMetaData",
5893 &omd),
5894 ndr_print_struct_string(s,
5895 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5896 "incoming replPropertyMetaData",
5897 rmd)));
5898 talloc_free(s);
5899 } else if (DEBUGLVL(4)) {
5900 struct GUID_txt_buf guid_txt;
5902 DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
5903 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5904 &guid_txt),
5905 ldb_dn_get_linearized(msg->dn)));
5908 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
5909 "isDeleted", false);
5910 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5911 "isDeleted", false);
5914 * Fill in the remote_parent_guid with the GUID or an all-zero
5915 * GUID.
5917 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
5918 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
5919 } else {
5920 remote_parent_guid = GUID_zero();
5924 * To ensure we follow a complex rename chain around, we have
5925 * to confirm that the DN is the same (mostly to confirm the
5926 * RDN) and the parentGUID is the same.
5928 * This ensures we keep things under the correct parent, which
5929 * replmd_replicated_handle_rename() will do.
5932 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
5933 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
5934 ret = LDB_SUCCESS;
5935 } else {
5937 * handle renames, even just by case that come in over
5938 * DRS. Changes in the parent DN don't hit us here,
5939 * because the search for a parent will clean up those
5940 * components.
5942 * We also have already filtered out the case where
5943 * the peer has an older name to what we have (see
5944 * replmd_replicated_apply_search_callback())
5946 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
5949 if (ret != LDB_SUCCESS) {
5950 ldb_debug(ldb, LDB_DEBUG_FATAL,
5951 "replmd_replicated_request rename %s => %s failed - %s\n",
5952 ldb_dn_get_linearized(ar->search_msg->dn),
5953 ldb_dn_get_linearized(msg->dn),
5954 ldb_errstring(ldb));
5955 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5958 if (renamed == true) {
5960 * Set the callback to one that will fix up the name
5961 * metadata on the new conflict DN
5963 callback = replmd_op_name_modify_callback;
5966 ZERO_STRUCT(nmd);
5967 nmd.version = 1;
5968 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
5969 nmd.ctr.ctr1.array = talloc_array(ar,
5970 struct replPropertyMetaData1,
5971 nmd.ctr.ctr1.count);
5972 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5974 /* first copy the old meta data */
5975 for (i=0; i < omd.ctr.ctr1.count; i++) {
5976 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
5977 ni++;
5980 ar->seq_num = 0;
5981 /* now merge in the new meta data */
5982 for (i=0; i < rmd->ctr.ctr1.count; i++) {
5983 bool found = false;
5985 for (j=0; j < ni; j++) {
5986 bool cmp;
5988 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
5989 continue;
5992 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
5993 ar->objs->dsdb_repl_flags,
5994 &nmd.ctr.ctr1.array[j],
5995 &rmd->ctr.ctr1.array[i]);
5996 if (cmp) {
5997 /* replace the entry */
5998 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
5999 if (ar->seq_num == 0) {
6000 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6001 if (ret != LDB_SUCCESS) {
6002 return replmd_replicated_request_error(ar, ret);
6005 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
6006 switch (nmd.ctr.ctr1.array[j].attid) {
6007 case DRSUAPI_ATTID_ntSecurityDescriptor:
6008 sd_updated = true;
6009 break;
6010 case DRSUAPI_ATTID_isDeleted:
6011 take_remote_isDeleted = true;
6012 break;
6013 default:
6014 break;
6016 found = true;
6017 break;
6020 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
6021 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
6022 msg->elements[i-removed_attrs].name,
6023 ldb_dn_get_linearized(msg->dn),
6024 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
6027 /* we don't want to apply this change so remove the attribute */
6028 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
6029 removed_attrs++;
6031 found = true;
6032 break;
6035 if (found) continue;
6037 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
6038 if (ar->seq_num == 0) {
6039 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6040 if (ret != LDB_SUCCESS) {
6041 return replmd_replicated_request_error(ar, ret);
6044 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
6045 switch (nmd.ctr.ctr1.array[ni].attid) {
6046 case DRSUAPI_ATTID_ntSecurityDescriptor:
6047 sd_updated = true;
6048 break;
6049 case DRSUAPI_ATTID_isDeleted:
6050 take_remote_isDeleted = true;
6051 break;
6052 default:
6053 break;
6055 ni++;
6059 * finally correct the size of the meta_data array
6061 nmd.ctr.ctr1.count = ni;
6063 new_rdn = ldb_dn_get_rdn_val(msg->dn);
6064 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
6066 if (renamed) {
6067 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
6068 &nmd, ar, now, is_schema_nc,
6069 false);
6070 if (ret != LDB_SUCCESS) {
6071 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6072 return replmd_replicated_request_error(ar, ret);
6076 * sort the new meta data array
6078 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
6079 if (ret != LDB_SUCCESS) {
6080 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6081 return ret;
6085 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
6086 * UpdateObject.
6088 * This also controls SD propagation below
6090 if (take_remote_isDeleted) {
6091 isDeleted = remote_isDeleted;
6092 } else {
6093 isDeleted = local_isDeleted;
6096 ar->isDeleted = isDeleted;
6099 * check if some replicated attributes left, otherwise skip the ldb_modify() call
6101 if (msg->num_elements == 0) {
6102 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
6103 ar->index_current);
6105 return replmd_replicated_apply_isDeleted(ar);
6108 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
6109 ar->index_current, msg->num_elements);
6111 if (renamed) {
6112 sd_updated = true;
6115 if (sd_updated && !isDeleted) {
6116 ret = dsdb_module_schedule_sd_propagation(ar->module,
6117 ar->objs->partition_dn,
6118 msg->dn, true);
6119 if (ret != LDB_SUCCESS) {
6120 return ldb_operr(ldb);
6124 /* create the meta data value */
6125 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
6126 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
6127 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6128 nt_status = ndr_map_error2ntstatus(ndr_err);
6129 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6133 * when we know that we'll modify the record, add the whenChanged, uSNChanged
6134 * and replPopertyMetaData attributes
6136 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
6137 if (ret != LDB_SUCCESS) {
6138 return replmd_replicated_request_error(ar, ret);
6140 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
6141 if (ret != LDB_SUCCESS) {
6142 return replmd_replicated_request_error(ar, ret);
6144 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
6145 if (ret != LDB_SUCCESS) {
6146 return replmd_replicated_request_error(ar, ret);
6149 replmd_ldb_message_sort(msg, ar->schema);
6151 /* we want to replace the old values */
6152 for (i=0; i < msg->num_elements; i++) {
6153 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
6154 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
6155 if (msg->elements[i].num_values == 0) {
6156 ldb_asprintf_errstring(ldb, __location__
6157 ": objectClass removed on %s, aborting replication\n",
6158 ldb_dn_get_linearized(msg->dn));
6159 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
6164 if (DEBUGLVL(8)) {
6165 struct GUID_txt_buf guid_txt;
6167 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6168 LDB_CHANGETYPE_MODIFY,
6169 msg);
6170 DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
6171 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6172 &guid_txt),
6173 s));
6174 talloc_free(s);
6175 } else if (DEBUGLVL(4)) {
6176 struct GUID_txt_buf guid_txt;
6178 DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
6179 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6180 &guid_txt),
6181 ldb_dn_get_linearized(msg->dn)));
6184 ret = ldb_build_mod_req(&change_req,
6185 ldb,
6187 msg,
6188 ar->controls,
6190 callback,
6191 ar->req);
6192 LDB_REQ_SET_LOCATION(change_req);
6193 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6195 /* current partition control needed by "repmd_op_callback" */
6196 ret = ldb_request_add_control(change_req,
6197 DSDB_CONTROL_CURRENT_PARTITION_OID,
6198 false, NULL);
6199 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6201 return ldb_next_request(ar->module, change_req);
6204 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
6205 struct ldb_reply *ares)
6207 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6208 struct replmd_replicated_request);
6209 int ret;
6211 if (!ares) {
6212 return ldb_module_done(ar->req, NULL, NULL,
6213 LDB_ERR_OPERATIONS_ERROR);
6215 if (ares->error != LDB_SUCCESS &&
6216 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6217 return ldb_module_done(ar->req, ares->controls,
6218 ares->response, ares->error);
6221 switch (ares->type) {
6222 case LDB_REPLY_ENTRY:
6223 ar->search_msg = talloc_steal(ar, ares->message);
6224 break;
6226 case LDB_REPLY_REFERRAL:
6227 /* we ignore referrals */
6228 break;
6230 case LDB_REPLY_DONE:
6232 struct replPropertyMetaData1 *md_remote;
6233 struct replPropertyMetaData1 *md_local;
6235 struct replPropertyMetaDataBlob omd;
6236 const struct ldb_val *omd_value;
6237 struct replPropertyMetaDataBlob *rmd;
6238 struct ldb_message *msg;
6239 int instanceType;
6240 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
6241 ar->objs->objects[ar->index_current].last_known_parent = NULL;
6244 * This is the ADD case, find the appropriate parent,
6245 * as this object doesn't exist locally:
6247 if (ar->search_msg == NULL) {
6248 ret = replmd_replicated_apply_search_for_parent(ar);
6249 if (ret != LDB_SUCCESS) {
6250 return ldb_module_done(ar->req, NULL, NULL, ret);
6252 talloc_free(ares);
6253 return LDB_SUCCESS;
6257 * Otherwise, in the MERGE case, work out if we are
6258 * attempting a rename, and if so find the parent the
6259 * newly renamed object wants to belong under (which
6260 * may not be the parent in it's attached string DN
6262 rmd = ar->objs->objects[ar->index_current].meta_data;
6263 ZERO_STRUCT(omd);
6264 omd.version = 1;
6266 /* find existing meta data */
6267 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6268 if (omd_value) {
6269 enum ndr_err_code ndr_err;
6270 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6271 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6272 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6273 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6274 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6277 if (omd.version != 1) {
6278 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6282 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6284 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6285 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6286 && GUID_all_zero(&ar->local_parent_guid)) {
6287 DEBUG(0, ("Refusing to replicate new version of %s "
6288 "as local object has an all-zero parentGUID attribute, "
6289 "despite not being an NC root\n",
6290 ldb_dn_get_linearized(ar->search_msg->dn)));
6291 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6295 * now we need to check for double renames. We could have a
6296 * local rename pending which our replication partner hasn't
6297 * received yet. We choose which one wins by looking at the
6298 * attribute stamps on the two objects, the newer one wins.
6300 * This also simply applies the correct algorithms for
6301 * determining if a change was made to name at all, or
6302 * if the object has just been renamed under the same
6303 * parent.
6305 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6306 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6307 if (!md_local) {
6308 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6309 ldb_dn_get_linearized(ar->search_msg->dn)));
6310 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6314 * if there is no name attribute given then we have to assume the
6315 * object we've received has the older name
6317 if (replmd_replPropertyMetaData1_new_should_be_taken(
6318 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6319 md_local, md_remote)) {
6320 struct GUID_txt_buf p_guid_local;
6321 struct GUID_txt_buf p_guid_remote;
6322 msg = ar->objs->objects[ar->index_current].msg;
6324 /* Merge on the existing object, with rename */
6326 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6327 "as incoming object changing to %s under %s\n",
6328 ldb_dn_get_linearized(ar->search_msg->dn),
6329 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6330 ldb_dn_get_linearized(msg->dn),
6331 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6332 &p_guid_remote)));
6333 ret = replmd_replicated_apply_search_for_parent(ar);
6334 } else {
6335 struct GUID_txt_buf p_guid_local;
6336 struct GUID_txt_buf p_guid_remote;
6337 msg = ar->objs->objects[ar->index_current].msg;
6340 * Merge on the existing object, force no
6341 * rename (code below just to explain why in
6342 * the DEBUG() logs)
6345 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6346 ldb_dn_get_linearized(msg->dn)) == 0) {
6347 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6348 GUID_equal(&ar->local_parent_guid,
6349 ar->objs->objects[ar->index_current].parent_guid)
6350 == false) {
6351 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6352 "despite incoming object changing parent to %s\n",
6353 ldb_dn_get_linearized(ar->search_msg->dn),
6354 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6355 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6356 &p_guid_remote)));
6358 } else {
6359 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6360 " and rejecting older rename to %s under %s\n",
6361 ldb_dn_get_linearized(ar->search_msg->dn),
6362 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6363 ldb_dn_get_linearized(msg->dn),
6364 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6365 &p_guid_remote)));
6368 * This assignment ensures that the strcmp()
6369 * and GUID_equal() calls in
6370 * replmd_replicated_apply_merge() avoids the
6371 * rename call
6373 ar->objs->objects[ar->index_current].parent_guid =
6374 &ar->local_parent_guid;
6376 msg->dn = ar->search_msg->dn;
6377 ret = replmd_replicated_apply_merge(ar);
6379 if (ret != LDB_SUCCESS) {
6380 return ldb_module_done(ar->req, NULL, NULL, ret);
6385 talloc_free(ares);
6386 return LDB_SUCCESS;
6390 * Stores the linked attributes received in the replication chunk - these get
6391 * applied at the end of the transaction. We also check that each linked
6392 * attribute is valid, i.e. source and target objects are known.
6394 static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
6396 int ret = LDB_SUCCESS;
6397 uint32_t i;
6398 struct ldb_module *module = ar->module;
6399 struct replmd_private *replmd_private =
6400 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6401 struct ldb_context *ldb;
6403 ldb = ldb_module_get_ctx(module);
6405 DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
6407 /* save away the linked attributes for the end of the transaction */
6408 for (i = 0; i < ar->objs->linked_attributes_count; i++) {
6409 struct la_entry *la_entry;
6411 if (replmd_private->la_ctx == NULL) {
6412 replmd_private->la_ctx = talloc_new(replmd_private);
6414 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6415 if (la_entry == NULL) {
6416 ldb_oom(ldb);
6417 return LDB_ERR_OPERATIONS_ERROR;
6419 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
6420 if (la_entry->la == NULL) {
6421 talloc_free(la_entry);
6422 ldb_oom(ldb);
6423 return LDB_ERR_OPERATIONS_ERROR;
6425 *la_entry->la = ar->objs->linked_attributes[i];
6426 la_entry->dsdb_repl_flags = ar->objs->dsdb_repl_flags;
6428 /* we need to steal the non-scalars so they stay
6429 around until the end of the transaction */
6430 talloc_steal(la_entry->la, la_entry->la->identifier);
6431 talloc_steal(la_entry->la, la_entry->la->value.blob);
6433 ret = replmd_verify_linked_attribute(ar, la_entry);
6435 if (ret != LDB_SUCCESS) {
6436 break;
6439 DLIST_ADD(replmd_private->la_list, la_entry);
6442 return ret;
6445 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6447 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6449 struct ldb_context *ldb;
6450 int ret;
6451 char *tmp_str;
6452 char *filter;
6453 struct ldb_request *search_req;
6454 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6455 "parentGUID", "instanceType",
6456 "replPropertyMetaData", "nTSecurityDescriptor",
6457 "isDeleted", NULL };
6458 struct GUID_txt_buf guid_str_buf;
6460 if (ar->index_current >= ar->objs->num_objects) {
6463 * Now that we've applied all the objects, check the new linked
6464 * attributes and store them (we apply them in .prepare_commit)
6466 ret = replmd_store_linked_attributes(ar);
6468 if (ret != LDB_SUCCESS) {
6469 return ret;
6472 /* done applying objects, move on to the next stage */
6473 return replmd_replicated_uptodate_vector(ar);
6476 ldb = ldb_module_get_ctx(ar->module);
6477 ar->search_msg = NULL;
6478 ar->isDeleted = false;
6480 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6481 &guid_str_buf);
6483 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6484 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6486 ret = ldb_build_search_req(&search_req,
6487 ldb,
6489 ar->objs->partition_dn,
6490 LDB_SCOPE_SUBTREE,
6491 filter,
6492 attrs,
6493 NULL,
6495 replmd_replicated_apply_search_callback,
6496 ar->req);
6497 LDB_REQ_SET_LOCATION(search_req);
6499 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
6501 if (ret != LDB_SUCCESS) {
6502 return ret;
6505 return ldb_next_request(ar->module, search_req);
6509 * This is essentially a wrapper for replmd_replicated_apply_next()
6511 * This is needed to ensure that both codepaths call this handler.
6513 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6515 struct ldb_dn *deleted_objects_dn;
6516 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6517 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
6518 &deleted_objects_dn);
6519 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
6521 * Do a delete here again, so that if there is
6522 * anything local that conflicts with this
6523 * object being deleted, it is removed. This
6524 * includes links. See MS-DRSR 4.1.10.6.9
6525 * UpdateObject.
6527 * If the object is already deleted, and there
6528 * is no more work required, it doesn't do
6529 * anything.
6532 /* This has been updated to point to the DN we eventually did the modify on */
6534 struct ldb_request *del_req;
6535 struct ldb_result *res;
6537 TALLOC_CTX *tmp_ctx = talloc_new(ar);
6538 if (!tmp_ctx) {
6539 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6540 return ret;
6543 res = talloc_zero(tmp_ctx, struct ldb_result);
6544 if (!res) {
6545 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6546 talloc_free(tmp_ctx);
6547 return ret;
6550 /* Build a delete request, which hopefully will artually turn into nothing */
6551 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
6552 msg->dn,
6553 NULL,
6554 res,
6555 ldb_modify_default_callback,
6556 ar->req);
6557 LDB_REQ_SET_LOCATION(del_req);
6558 if (ret != LDB_SUCCESS) {
6559 talloc_free(tmp_ctx);
6560 return ret;
6564 * This is the guts of the call, call back
6565 * into our delete code, but setting the
6566 * re_delete flag so we delete anything that
6567 * shouldn't be there on a deleted or recycled
6568 * object
6570 ret = replmd_delete_internals(ar->module, del_req, true);
6571 if (ret == LDB_SUCCESS) {
6572 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
6575 talloc_free(tmp_ctx);
6576 if (ret != LDB_SUCCESS) {
6577 return ret;
6581 ar->index_current++;
6582 return replmd_replicated_apply_next(ar);
6585 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
6586 struct ldb_reply *ares)
6588 struct ldb_context *ldb;
6589 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6590 struct replmd_replicated_request);
6591 ldb = ldb_module_get_ctx(ar->module);
6593 if (!ares) {
6594 return ldb_module_done(ar->req, NULL, NULL,
6595 LDB_ERR_OPERATIONS_ERROR);
6597 if (ares->error != LDB_SUCCESS) {
6598 return ldb_module_done(ar->req, ares->controls,
6599 ares->response, ares->error);
6602 if (ares->type != LDB_REPLY_DONE) {
6603 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
6604 return ldb_module_done(ar->req, NULL, NULL,
6605 LDB_ERR_OPERATIONS_ERROR);
6608 talloc_free(ares);
6610 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6613 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
6615 struct ldb_context *ldb;
6616 struct ldb_request *change_req;
6617 enum ndr_err_code ndr_err;
6618 struct ldb_message *msg;
6619 struct replUpToDateVectorBlob ouv;
6620 const struct ldb_val *ouv_value;
6621 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
6622 struct replUpToDateVectorBlob nuv;
6623 struct ldb_val nuv_value;
6624 struct ldb_message_element *nuv_el = NULL;
6625 struct ldb_message_element *orf_el = NULL;
6626 struct repsFromToBlob nrf;
6627 struct ldb_val *nrf_value = NULL;
6628 struct ldb_message_element *nrf_el = NULL;
6629 unsigned int i;
6630 uint32_t j,ni=0;
6631 bool found = false;
6632 time_t t = time(NULL);
6633 NTTIME now;
6634 int ret;
6635 uint32_t instanceType;
6637 ldb = ldb_module_get_ctx(ar->module);
6638 ruv = ar->objs->uptodateness_vector;
6639 ZERO_STRUCT(ouv);
6640 ouv.version = 2;
6641 ZERO_STRUCT(nuv);
6642 nuv.version = 2;
6644 unix_to_nt_time(&now, t);
6646 if (ar->search_msg == NULL) {
6647 /* this happens for a REPL_OBJ call where we are
6648 creating the target object by replicating it. The
6649 subdomain join code does this for the partition DN
6651 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
6652 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6655 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
6656 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
6657 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
6658 ldb_dn_get_linearized(ar->search_msg->dn)));
6659 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6663 * first create the new replUpToDateVector
6665 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
6666 if (ouv_value) {
6667 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
6668 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
6669 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6670 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6671 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6674 if (ouv.version != 2) {
6675 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6680 * the new uptodateness vector will at least
6681 * contain 1 entry, one for the source_dsa
6683 * plus optional values from our old vector and the one from the source_dsa
6685 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
6686 if (ruv) nuv.ctr.ctr2.count += ruv->count;
6687 nuv.ctr.ctr2.cursors = talloc_array(ar,
6688 struct drsuapi_DsReplicaCursor2,
6689 nuv.ctr.ctr2.count);
6690 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6692 /* first copy the old vector */
6693 for (i=0; i < ouv.ctr.ctr2.count; i++) {
6694 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
6695 ni++;
6698 /* merge in the source_dsa vector is available */
6699 for (i=0; (ruv && i < ruv->count); i++) {
6700 found = false;
6702 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6703 &ar->our_invocation_id)) {
6704 continue;
6707 for (j=0; j < ni; j++) {
6708 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6709 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
6710 continue;
6713 found = true;
6715 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
6716 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
6718 break;
6721 if (found) continue;
6723 /* if it's not there yet, add it */
6724 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
6725 ni++;
6729 * finally correct the size of the cursors array
6731 nuv.ctr.ctr2.count = ni;
6734 * sort the cursors
6736 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
6739 * create the change ldb_message
6741 msg = ldb_msg_new(ar);
6742 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6743 msg->dn = ar->search_msg->dn;
6745 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
6746 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
6747 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6748 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6749 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6751 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
6752 if (ret != LDB_SUCCESS) {
6753 return replmd_replicated_request_error(ar, ret);
6755 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
6758 * now create the new repsFrom value from the given repsFromTo1 structure
6760 ZERO_STRUCT(nrf);
6761 nrf.version = 1;
6762 nrf.ctr.ctr1 = *ar->objs->source_dsa;
6763 nrf.ctr.ctr1.last_attempt = now;
6764 nrf.ctr.ctr1.last_success = now;
6765 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
6768 * first see if we already have a repsFrom value for the current source dsa
6769 * if so we'll later replace this value
6771 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
6772 if (orf_el) {
6773 for (i=0; i < orf_el->num_values; i++) {
6774 struct repsFromToBlob *trf;
6776 trf = talloc(ar, struct repsFromToBlob);
6777 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6779 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
6780 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
6781 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6782 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6783 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6786 if (trf->version != 1) {
6787 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6791 * we compare the source dsa objectGUID not the invocation_id
6792 * because we want only one repsFrom value per source dsa
6793 * and when the invocation_id of the source dsa has changed we don't need
6794 * the old repsFrom with the old invocation_id
6796 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
6797 &ar->objs->source_dsa->source_dsa_obj_guid)) {
6798 talloc_free(trf);
6799 continue;
6802 talloc_free(trf);
6803 nrf_value = &orf_el->values[i];
6804 break;
6808 * copy over all old values to the new ldb_message
6810 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
6811 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6812 *nrf_el = *orf_el;
6816 * if we haven't found an old repsFrom value for the current source dsa
6817 * we'll add a new value
6819 if (!nrf_value) {
6820 struct ldb_val zero_value;
6821 ZERO_STRUCT(zero_value);
6822 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
6823 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6825 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
6828 /* we now fill the value which is already attached to ldb_message */
6829 ndr_err = ndr_push_struct_blob(nrf_value, msg,
6830 &nrf,
6831 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
6832 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6833 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6834 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6838 * the ldb_message_element for the attribute, has all the old values and the new one
6839 * so we'll replace the whole attribute with all values
6841 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
6843 if (CHECK_DEBUGLVL(4)) {
6844 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6845 LDB_CHANGETYPE_MODIFY,
6846 msg);
6847 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
6848 talloc_free(s);
6851 /* prepare the ldb_modify() request */
6852 ret = ldb_build_mod_req(&change_req,
6853 ldb,
6855 msg,
6856 ar->controls,
6858 replmd_replicated_uptodate_modify_callback,
6859 ar->req);
6860 LDB_REQ_SET_LOCATION(change_req);
6861 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6863 return ldb_next_request(ar->module, change_req);
6866 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
6867 struct ldb_reply *ares)
6869 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6870 struct replmd_replicated_request);
6871 int ret;
6873 if (!ares) {
6874 return ldb_module_done(ar->req, NULL, NULL,
6875 LDB_ERR_OPERATIONS_ERROR);
6877 if (ares->error != LDB_SUCCESS &&
6878 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6879 return ldb_module_done(ar->req, ares->controls,
6880 ares->response, ares->error);
6883 switch (ares->type) {
6884 case LDB_REPLY_ENTRY:
6885 ar->search_msg = talloc_steal(ar, ares->message);
6886 break;
6888 case LDB_REPLY_REFERRAL:
6889 /* we ignore referrals */
6890 break;
6892 case LDB_REPLY_DONE:
6893 ret = replmd_replicated_uptodate_modify(ar);
6894 if (ret != LDB_SUCCESS) {
6895 return ldb_module_done(ar->req, NULL, NULL, ret);
6899 talloc_free(ares);
6900 return LDB_SUCCESS;
6904 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
6906 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
6907 struct replmd_private *replmd_private =
6908 talloc_get_type_abort(ldb_module_get_private(ar->module),
6909 struct replmd_private);
6910 int ret;
6911 static const char *attrs[] = {
6912 "replUpToDateVector",
6913 "repsFrom",
6914 "instanceType",
6915 NULL
6917 struct ldb_request *search_req;
6919 ar->search_msg = NULL;
6922 * Let the caller know that we did an originating updates
6924 ar->objs->originating_updates = replmd_private->originating_updates;
6926 ret = ldb_build_search_req(&search_req,
6927 ldb,
6929 ar->objs->partition_dn,
6930 LDB_SCOPE_BASE,
6931 "(objectClass=*)",
6932 attrs,
6933 NULL,
6935 replmd_replicated_uptodate_search_callback,
6936 ar->req);
6937 LDB_REQ_SET_LOCATION(search_req);
6938 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6940 return ldb_next_request(ar->module, search_req);
6945 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
6947 struct ldb_context *ldb;
6948 struct dsdb_extended_replicated_objects *objs;
6949 struct replmd_replicated_request *ar;
6950 struct ldb_control **ctrls;
6951 int ret;
6953 ldb = ldb_module_get_ctx(module);
6955 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
6957 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
6958 if (!objs) {
6959 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
6960 return LDB_ERR_PROTOCOL_ERROR;
6963 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
6964 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
6965 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
6966 return LDB_ERR_PROTOCOL_ERROR;
6969 ar = replmd_ctx_init(module, req);
6970 if (!ar)
6971 return LDB_ERR_OPERATIONS_ERROR;
6973 /* Set the flags to have the replmd_op_callback run over the full set of objects */
6974 ar->apply_mode = true;
6975 ar->objs = objs;
6976 ar->schema = dsdb_get_schema(ldb, ar);
6977 if (!ar->schema) {
6978 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
6979 talloc_free(ar);
6980 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
6981 return LDB_ERR_CONSTRAINT_VIOLATION;
6984 ctrls = req->controls;
6986 if (req->controls) {
6987 req->controls = talloc_memdup(ar, req->controls,
6988 talloc_get_size(req->controls));
6989 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6992 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
6993 if (ret != LDB_SUCCESS) {
6994 return ret;
6997 /* If this change contained linked attributes in the body
6998 * (rather than in the links section) we need to update
6999 * backlinks in linked_attributes */
7000 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
7001 if (ret != LDB_SUCCESS) {
7002 return ret;
7005 ar->controls = req->controls;
7006 req->controls = ctrls;
7008 return replmd_replicated_apply_next(ar);
7012 * Checks how to handle an missing target - either we need to fail the
7013 * replication and retry with GET_TGT, ignore the link and continue, or try to
7014 * add a partial link to an unknown target.
7016 static int replmd_allow_missing_target(struct ldb_module *module,
7017 TALLOC_CTX *mem_ctx,
7018 struct ldb_dn *target_dn,
7019 struct ldb_dn *source_dn,
7020 bool is_obj_commit,
7021 struct GUID *guid,
7022 uint32_t dsdb_repl_flags,
7023 bool *ignore_link,
7024 const char * missing_str)
7026 struct ldb_context *ldb = ldb_module_get_ctx(module);
7027 bool is_in_same_nc;
7030 * we may not be able to resolve link targets properly when
7031 * dealing with subsets of objects, e.g. the source is a
7032 * critical object and the target isn't
7034 * TODO:
7035 * When we implement Trusted Domains we need to consider
7036 * whether they get treated as an incomplete replica here or not
7038 if (dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
7041 * Ignore the link. We don't increase the highwater-mark in
7042 * the object subset cases, so subsequent replications should
7043 * resolve any missing links
7045 DEBUG(2, ("%s target %s linked from %s\n", missing_str,
7046 ldb_dn_get_linearized(target_dn),
7047 ldb_dn_get_linearized(source_dn)));
7048 *ignore_link = true;
7049 return LDB_SUCCESS;
7052 if (dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
7055 * target should already be up-to-date so there's no point in
7056 * retrying. This could be due to bad timing, or if a target
7057 * on a one-way link was deleted. We ignore the link rather
7058 * than failing the replication cycle completely
7060 *ignore_link = true;
7061 DBG_WARNING("%s is %s but up to date. Ignoring link from %s\n",
7062 ldb_dn_get_linearized(target_dn), missing_str,
7063 ldb_dn_get_linearized(source_dn));
7064 return LDB_SUCCESS;
7067 is_in_same_nc = dsdb_objects_have_same_nc(ldb,
7068 mem_ctx,
7069 source_dn,
7070 target_dn);
7071 if (is_in_same_nc) {
7072 /* fail the replication and retry with GET_TGT */
7073 ldb_asprintf_errstring(ldb, "%s target %s GUID %s linked from %s\n",
7074 missing_str,
7075 ldb_dn_get_linearized(target_dn),
7076 GUID_string(mem_ctx, guid),
7077 ldb_dn_get_linearized(source_dn));
7078 return LDB_ERR_NO_SUCH_OBJECT;
7082 * The target of the cross-partition link is missing. Continue
7083 * and try to at least add the forward-link. This isn't great,
7084 * but a partial link can be fixed by dbcheck, so it's better
7085 * than dropping the link completely.
7087 *ignore_link = false;
7089 if (is_obj_commit) {
7092 * Only log this when we're actually committing the objects.
7093 * This avoids spurious logs, i.e. if we're just verifying the
7094 * received link during a join.
7096 DBG_WARNING("%s cross-partition target %s linked from %s\n",
7097 missing_str, ldb_dn_get_linearized(target_dn),
7098 ldb_dn_get_linearized(source_dn));
7101 return LDB_SUCCESS;
7105 * Checks that the target object for a linked attribute exists.
7106 * @param guid returns the target object's GUID (is returned)if it exists)
7107 * @param ignore_link set to true if the linked attribute should be ignored
7108 * (i.e. the target doesn't exist, but that it's OK to skip the link)
7110 static int replmd_check_target_exists(struct ldb_module *module,
7111 struct dsdb_dn *dsdb_dn,
7112 struct la_entry *la_entry,
7113 struct ldb_dn *source_dn,
7114 bool is_obj_commit,
7115 struct GUID *guid,
7116 bool *ignore_link)
7118 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7119 struct ldb_context *ldb = ldb_module_get_ctx(module);
7120 struct ldb_result *target_res;
7121 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7122 const char *attrs[] = { "isDeleted", "isRecycled", NULL };
7123 NTSTATUS ntstatus;
7124 int ret;
7125 enum deletion_state target_deletion_state = OBJECT_REMOVED;
7126 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
7128 *ignore_link = false;
7129 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
7131 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
7134 * This strange behaviour (allowing a NULL/missing
7135 * GUID) originally comes from:
7137 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
7138 * Author: Andrew Tridgell <tridge@samba.org>
7139 * Date: Mon Dec 21 21:21:55 2009 +1100
7141 * s4-drs: cope better with NULL GUIDS from DRS
7143 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
7144 * need to match by DN if possible when seeing if we should update an
7145 * existing link.
7147 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
7149 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
7150 dsdb_dn->dn, attrs,
7151 DSDB_FLAG_NEXT_MODULE |
7152 DSDB_SEARCH_SHOW_RECYCLED |
7153 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7154 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7155 NULL);
7156 } else if (!NT_STATUS_IS_OK(ntstatus)) {
7157 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
7158 la->attid,
7159 ldb_dn_get_linearized(dsdb_dn->dn),
7160 ldb_dn_get_linearized(source_dn));
7161 talloc_free(tmp_ctx);
7162 return LDB_ERR_OPERATIONS_ERROR;
7163 } else {
7164 ret = dsdb_module_search(module, tmp_ctx, &target_res,
7165 NULL, LDB_SCOPE_SUBTREE,
7166 attrs,
7167 DSDB_FLAG_NEXT_MODULE |
7168 DSDB_SEARCH_SHOW_RECYCLED |
7169 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7170 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7171 NULL,
7172 "objectGUID=%s",
7173 GUID_string(tmp_ctx, guid));
7176 if (ret != LDB_SUCCESS) {
7177 ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
7178 GUID_string(tmp_ctx, guid),
7179 ldb_errstring(ldb));
7180 talloc_free(tmp_ctx);
7181 return ret;
7184 if (target_res->count == 0) {
7187 * target object is unknown. Check whether to ignore the link,
7188 * fail the replication, or add a partial link
7190 ret = replmd_allow_missing_target(module, tmp_ctx, dsdb_dn->dn,
7191 source_dn, is_obj_commit, guid,
7192 la_entry->dsdb_repl_flags,
7193 ignore_link, "Unknown");
7195 } else if (target_res->count != 1) {
7196 ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
7197 GUID_string(tmp_ctx, guid));
7198 ret = LDB_ERR_OPERATIONS_ERROR;
7199 } else {
7200 struct ldb_message *target_msg = target_res->msgs[0];
7202 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
7204 /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
7205 replmd_deletion_state(module, target_msg,
7206 &target_deletion_state, NULL);
7209 * Check for deleted objects as per MS-DRSR 4.1.10.6.14
7210 * ProcessLinkValue(). Link updates should not be sent for
7211 * recycled and tombstone objects (deleting the links should
7212 * happen when we delete the object). This probably means our
7213 * copy of the target object isn't up to date.
7215 if (target_deletion_state >= OBJECT_RECYCLED) {
7218 * target object is deleted. Check whether to ignore the
7219 * link, fail the replication, or add a partial link
7221 ret = replmd_allow_missing_target(module, tmp_ctx,
7222 dsdb_dn->dn, source_dn,
7223 is_obj_commit, guid,
7224 la_entry->dsdb_repl_flags,
7225 ignore_link, "Deleted");
7229 talloc_free(tmp_ctx);
7230 return ret;
7234 * Extracts the key details about the source/target object for a
7235 * linked-attribute entry.
7236 * This returns the following details:
7237 * @param ret_attr the schema details for the linked attribute
7238 * @param source_msg the search result for the source object
7239 * @param target_dsdb_dn the unpacked DN info for the target object
7241 static int replmd_extract_la_entry_details(struct ldb_module *module,
7242 struct la_entry *la_entry,
7243 TALLOC_CTX *mem_ctx,
7244 const struct dsdb_attribute **ret_attr,
7245 struct ldb_message **source_msg,
7246 struct dsdb_dn **target_dsdb_dn)
7248 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7249 struct ldb_context *ldb = ldb_module_get_ctx(module);
7250 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7251 int ret;
7252 const struct dsdb_attribute *attr;
7253 WERROR status;
7254 struct ldb_result *res;
7255 const char *attrs[4];
7258 linked_attributes[0]:
7259 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
7260 identifier : *
7261 identifier: struct drsuapi_DsReplicaObjectIdentifier
7262 __ndr_size : 0x0000003a (58)
7263 __ndr_size_sid : 0x00000000 (0)
7264 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7265 sid : S-0-0
7266 __ndr_size_dn : 0x00000000 (0)
7267 dn : ''
7268 attid : DRSUAPI_ATTID_member (0x1F)
7269 value: struct drsuapi_DsAttributeValue
7270 __ndr_size : 0x0000007e (126)
7271 blob : *
7272 blob : DATA_BLOB length=126
7273 flags : 0x00000001 (1)
7274 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7275 originating_add_time : Wed Sep 2 22:20:01 2009 EST
7276 meta_data: struct drsuapi_DsReplicaMetaData
7277 version : 0x00000015 (21)
7278 originating_change_time : Wed Sep 2 23:39:07 2009 EST
7279 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7280 originating_usn : 0x000000000001e19c (123292)
7282 (for cases where the link is to a normal DN)
7283 &target: struct drsuapi_DsReplicaObjectIdentifier3
7284 __ndr_size : 0x0000007e (126)
7285 __ndr_size_sid : 0x0000001c (28)
7286 guid : 7639e594-db75-4086-b0d4-67890ae46031
7287 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7288 __ndr_size_dn : 0x00000022 (34)
7289 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7292 /* find the attribute being modified */
7293 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
7294 if (attr == NULL) {
7295 struct GUID_txt_buf guid_str;
7296 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7297 la->attid,
7298 GUID_buf_string(&la->identifier->guid,
7299 &guid_str));
7300 return LDB_ERR_OPERATIONS_ERROR;
7304 * All attributes listed here must be dealt with in some way
7305 * by replmd_process_linked_attribute() otherwise in the case
7306 * of isDeleted: FALSE the modify will fail with:
7308 * Failed to apply linked attribute change 'attribute 'isDeleted':
7309 * invalid modify flags on
7310 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
7311 * 0x0'
7313 * This is becaue isDeleted is a Boolean, so FALSE is a
7314 * legitimate value (set by Samba's deletetest.py)
7316 attrs[0] = attr->lDAPDisplayName;
7317 attrs[1] = "isDeleted";
7318 attrs[2] = "isRecycled";
7319 attrs[3] = NULL;
7322 * get the existing message from the db for the object with
7323 * this GUID, returning attribute being modified. We will then
7324 * use this msg as the basis for a modify call
7326 ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
7327 DSDB_FLAG_NEXT_MODULE |
7328 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7329 DSDB_SEARCH_SHOW_RECYCLED |
7330 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
7331 DSDB_SEARCH_REVEAL_INTERNALS,
7332 NULL,
7333 "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
7334 if (ret != LDB_SUCCESS) {
7335 return ret;
7337 if (res->count != 1) {
7338 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
7339 GUID_string(mem_ctx, &la->identifier->guid));
7340 return LDB_ERR_NO_SUCH_OBJECT;
7343 *source_msg = res->msgs[0];
7345 /* the value blob for the attribute holds the target object DN */
7346 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx, la->value.blob, target_dsdb_dn);
7347 if (!W_ERROR_IS_OK(status)) {
7348 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7349 attr->lDAPDisplayName,
7350 ldb_dn_get_linearized(res->msgs[0]->dn),
7351 win_errstr(status));
7352 return LDB_ERR_OPERATIONS_ERROR;
7355 *ret_attr = attr;
7357 return LDB_SUCCESS;
7361 * Verifies the source and target objects are known for a linked attribute
7363 static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
7364 struct la_entry *la)
7366 int ret = LDB_SUCCESS;
7367 TALLOC_CTX *tmp_ctx = talloc_new(la);
7368 struct ldb_module *module = ar->module;
7369 struct ldb_message *src_msg;
7370 const struct dsdb_attribute *attr;
7371 struct dsdb_dn *tgt_dsdb_dn;
7372 struct GUID guid = GUID_zero();
7373 bool dummy;
7375 ret = replmd_extract_la_entry_details(module, la, tmp_ctx, &attr,
7376 &src_msg, &tgt_dsdb_dn);
7379 * When we fail to find the source object, the error code we pass
7380 * back here is really important. It flags back to the callers to
7381 * retry this request with DRSUAPI_DRS_GET_ANC. This case should
7382 * never happen if we're replicating from a Samba DC, but it is
7383 * needed to talk to a Windows DC
7385 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7386 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT);
7389 if (ret != LDB_SUCCESS) {
7390 talloc_free(tmp_ctx);
7391 return ret;
7395 * We can skip the target object checks if we're only syncing critical
7396 * objects, or we know the target is up-to-date. If either case, we
7397 * still continue even if the target doesn't exist
7399 if ((la->dsdb_repl_flags & (DSDB_REPL_FLAG_OBJECT_SUBSET |
7400 DSDB_REPL_FLAG_TARGETS_UPTODATE)) == 0) {
7402 ret = replmd_check_target_exists(module, tgt_dsdb_dn, la,
7403 src_msg->dn, false, &guid,
7404 &dummy);
7408 * When we fail to find the target object, the error code we pass
7409 * back here is really important. It flags back to the callers to
7410 * retry this request with DRSUAPI_DRS_GET_TGT
7412 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7413 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
7416 talloc_free(tmp_ctx);
7417 return ret;
7421 * Finds the current active Parsed-DN value for a single-valued linked
7422 * attribute, if one exists.
7423 * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
7424 * @returns LDB_SUCCESS (regardless of whether a match was found), unless
7425 * an error occurred
7427 static int replmd_get_active_singleval_link(struct ldb_module *module,
7428 TALLOC_CTX *mem_ctx,
7429 struct parsed_dn pdn_list[],
7430 unsigned int count,
7431 const struct dsdb_attribute *attr,
7432 struct parsed_dn **ret_pdn)
7434 unsigned int i;
7436 *ret_pdn = NULL;
7438 if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE)) {
7440 /* nothing to do for multi-valued linked attributes */
7441 return LDB_SUCCESS;
7444 for (i = 0; i < count; i++) {
7445 int ret = LDB_SUCCESS;
7446 struct parsed_dn *pdn = &pdn_list[i];
7448 /* skip any inactive links */
7449 if (dsdb_dn_is_deleted_val(pdn->v)) {
7450 continue;
7453 /* we've found an active value for this attribute */
7454 *ret_pdn = pdn;
7456 if (pdn->dsdb_dn == NULL) {
7457 struct ldb_context *ldb = ldb_module_get_ctx(module);
7459 ret = really_parse_trusted_dn(mem_ctx, ldb, pdn,
7460 attr->syntax->ldap_oid);
7463 return ret;
7466 /* no active link found */
7467 return LDB_SUCCESS;
7471 * @returns true if the replication linked attribute info is newer than we
7472 * already have in our DB
7473 * @param pdn the existing linked attribute info in our DB
7474 * @param la the new linked attribute info received during replication
7476 static bool replmd_link_update_is_newer(struct parsed_dn *pdn,
7477 struct drsuapi_DsReplicaLinkedAttribute *la)
7479 /* see if this update is newer than what we have already */
7480 struct GUID invocation_id = GUID_zero();
7481 uint32_t version = 0;
7482 NTTIME change_time = 0;
7484 if (pdn == NULL) {
7486 /* no existing info so update is newer */
7487 return true;
7490 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
7491 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
7492 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
7494 return replmd_update_is_newer(&invocation_id,
7495 &la->meta_data.originating_invocation_id,
7496 version,
7497 la->meta_data.version,
7498 change_time,
7499 la->meta_data.originating_change_time);
7503 * Marks an existing linked attribute value as deleted in the DB
7504 * @param pdn the parsed-DN of the target-value to delete
7506 static int replmd_delete_link_value(struct ldb_module *module,
7507 struct replmd_private *replmd_private,
7508 TALLOC_CTX *mem_ctx,
7509 struct ldb_dn *src_obj_dn,
7510 const struct dsdb_schema *schema,
7511 const struct dsdb_attribute *attr,
7512 uint64_t seq_num,
7513 bool is_active,
7514 struct GUID *target_guid,
7515 struct dsdb_dn *target_dsdb_dn,
7516 struct ldb_val *output_val)
7518 struct ldb_context *ldb = ldb_module_get_ctx(module);
7519 time_t t;
7520 NTTIME now;
7521 const struct GUID *invocation_id = NULL;
7522 int ret;
7524 t = time(NULL);
7525 unix_to_nt_time(&now, t);
7527 invocation_id = samdb_ntds_invocation_id(ldb);
7528 if (invocation_id == NULL) {
7529 return LDB_ERR_OPERATIONS_ERROR;
7532 /* if the existing link is active, remove its backlink */
7533 if (is_active) {
7535 ret = replmd_add_backlink(module, replmd_private, schema,
7536 src_obj_dn, target_guid, false,
7537 attr, NULL);
7538 if (ret != LDB_SUCCESS) {
7539 return ret;
7543 /* mark the existing value as deleted */
7544 ret = replmd_update_la_val(mem_ctx, output_val, target_dsdb_dn,
7545 target_dsdb_dn, invocation_id, seq_num,
7546 seq_num, now, true);
7547 return ret;
7551 * Checks for a conflict in single-valued link attributes, and tries to
7552 * resolve the problem if possible.
7554 * Single-valued links should only ever have one active value. If we already
7555 * have an active link value, and during replication we receive an active link
7556 * value for a different target DN, then we need to resolve this inconsistency
7557 * and determine which value should be active. If the received info is better/
7558 * newer than the existing link attribute, then we need to set our existing
7559 * link as deleted. If the received info is worse/older, then we should continue
7560 * to add it, but set it as an inactive link.
7562 * Note that this is a corner-case that is unlikely to happen (but if it does
7563 * happen, we don't want it to break replication completely).
7565 * @param pdn_being_modified the parsed DN corresponding to the received link
7566 * target (note this is NULL if the link does not already exist in our DB)
7567 * @param pdn_list all the source object's Parsed-DNs for this attribute, i.e.
7568 * any existing active or inactive values for the attribute in our DB.
7569 * @param dsdb_dn the target DN for the received link attribute
7570 * @param add_as_inactive gets set to true if the received link is worse than
7571 * the existing link - it should still be added, but as an inactive link.
7573 static int replmd_check_singleval_la_conflict(struct ldb_module *module,
7574 struct replmd_private *replmd_private,
7575 TALLOC_CTX *mem_ctx,
7576 struct ldb_dn *src_obj_dn,
7577 struct drsuapi_DsReplicaLinkedAttribute *la,
7578 struct dsdb_dn *dsdb_dn,
7579 struct parsed_dn *pdn_being_modified,
7580 struct parsed_dn *pdn_list,
7581 struct ldb_message_element *old_el,
7582 const struct dsdb_schema *schema,
7583 const struct dsdb_attribute *attr,
7584 uint64_t seq_num,
7585 bool *add_as_inactive)
7587 struct parsed_dn *active_pdn = NULL;
7588 bool update_is_newer = false;
7589 int ret;
7592 * check if there's a conflict for single-valued links, i.e. an active
7593 * linked attribute already exists, but it has a different target value
7595 ret = replmd_get_active_singleval_link(module, mem_ctx, pdn_list,
7596 old_el->num_values, attr,
7597 &active_pdn);
7599 if (ret != LDB_SUCCESS) {
7600 return ret;
7604 * If no active value exists (or the received info is for the currently
7605 * active value), then no conflict exists
7607 if (active_pdn == NULL || active_pdn == pdn_being_modified) {
7608 return LDB_SUCCESS;
7611 DBG_WARNING("Link conflict for %s attribute on %s\n",
7612 attr->lDAPDisplayName, ldb_dn_get_linearized(src_obj_dn));
7614 /* Work out how to resolve the conflict based on which info is better */
7615 update_is_newer = replmd_link_update_is_newer(active_pdn, la);
7617 if (update_is_newer) {
7618 DBG_WARNING("Using received value %s, over existing target %s\n",
7619 ldb_dn_get_linearized(dsdb_dn->dn),
7620 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn));
7623 * Delete our existing active link. The received info will then
7624 * be added (through normal link processing) as the active value
7626 ret = replmd_delete_link_value(module, replmd_private, old_el,
7627 src_obj_dn, schema, attr,
7628 seq_num, true, &active_pdn->guid,
7629 active_pdn->dsdb_dn,
7630 active_pdn->v);
7632 if (ret != LDB_SUCCESS) {
7633 return ret;
7635 } else {
7636 DBG_WARNING("Using existing target %s, over received value %s\n",
7637 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn),
7638 ldb_dn_get_linearized(dsdb_dn->dn));
7641 * we want to keep our existing active link and add the
7642 * received link as inactive
7644 *add_as_inactive = true;
7647 return LDB_SUCCESS;
7651 process one linked attribute structure
7653 static int replmd_process_linked_attribute(struct ldb_module *module,
7654 struct replmd_private *replmd_private,
7655 struct la_entry *la_entry,
7656 struct ldb_request *parent)
7658 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7659 struct ldb_context *ldb = ldb_module_get_ctx(module);
7660 struct ldb_message *msg;
7661 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7662 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
7663 int ret;
7664 const struct dsdb_attribute *attr;
7665 struct dsdb_dn *dsdb_dn;
7666 uint64_t seq_num = 0;
7667 struct ldb_message_element *old_el;
7668 time_t t = time(NULL);
7669 struct parsed_dn *pdn_list, *pdn, *next;
7670 struct GUID guid = GUID_zero();
7671 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
7672 bool ignore_link;
7673 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
7674 struct dsdb_dn *old_dsdb_dn = NULL;
7675 struct ldb_val *val_to_update = NULL;
7676 bool add_as_inactive = false;
7679 * get the attribute being modified, the search result for the source object,
7680 * and the target object's DN details
7682 ret = replmd_extract_la_entry_details(module, la_entry, tmp_ctx, &attr,
7683 &msg, &dsdb_dn);
7685 if (ret != LDB_SUCCESS) {
7686 talloc_free(tmp_ctx);
7687 return ret;
7691 * Check for deleted objects per MS-DRSR 4.1.10.6.14
7692 * ProcessLinkValue, because link updates are not applied to
7693 * recycled and tombstone objects. We don't have to delete
7694 * any existing link, that should have happened when the
7695 * object deletion was replicated or initiated.
7697 * This needs isDeleted and isRecycled to be included as
7698 * attributes in the search and so in msg if set.
7700 replmd_deletion_state(module, msg, &deletion_state, NULL);
7702 if (deletion_state >= OBJECT_RECYCLED) {
7703 talloc_free(tmp_ctx);
7704 return LDB_SUCCESS;
7708 * Now that we know the deletion_state, remove the extra
7709 * attributes added for that purpose. We need to do this
7710 * otherwise in the case of isDeleted: FALSE the modify will
7711 * fail with:
7713 * Failed to apply linked attribute change 'attribute 'isDeleted':
7714 * invalid modify flags on
7715 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
7716 * 0x0'
7718 * This is becaue isDeleted is a Boolean, so FALSE is a
7719 * legitimate value (set by Samba's deletetest.py)
7722 ldb_msg_remove_attr(msg, "isDeleted");
7723 ldb_msg_remove_attr(msg, "isRecycled");
7725 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7726 if (old_el == NULL) {
7727 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
7728 if (ret != LDB_SUCCESS) {
7729 ldb_module_oom(module);
7730 talloc_free(tmp_ctx);
7731 return LDB_ERR_OPERATIONS_ERROR;
7733 } else {
7734 old_el->flags = LDB_FLAG_MOD_REPLACE;
7737 /* parse the existing links */
7738 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx, old_el, &pdn_list,
7739 attr->syntax->ldap_oid, parent);
7741 if (ret != LDB_SUCCESS) {
7742 talloc_free(tmp_ctx);
7743 return ret;
7746 ret = replmd_check_target_exists(module, dsdb_dn, la_entry, msg->dn,
7747 true, &guid, &ignore_link);
7749 if (ret != LDB_SUCCESS) {
7750 talloc_free(tmp_ctx);
7751 return ret;
7755 * there are some cases where the target object doesn't exist, but it's
7756 * OK to ignore the linked attribute
7758 if (ignore_link) {
7759 talloc_free(tmp_ctx);
7760 return ret;
7763 /* see if this link already exists */
7764 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
7765 &guid,
7766 dsdb_dn->dn,
7767 dsdb_dn->extra_part, 0,
7768 &pdn, &next,
7769 attr->syntax->ldap_oid,
7770 true);
7771 if (ret != LDB_SUCCESS) {
7772 talloc_free(tmp_ctx);
7773 return ret;
7776 if (!replmd_link_update_is_newer(pdn, la)) {
7777 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
7778 old_el->name, ldb_dn_get_linearized(msg->dn),
7779 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
7780 talloc_free(tmp_ctx);
7781 return LDB_SUCCESS;
7784 /* get a seq_num for this change */
7785 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
7786 if (ret != LDB_SUCCESS) {
7787 talloc_free(tmp_ctx);
7788 return ret;
7792 * check for single-valued link conflicts, i.e. an active linked
7793 * attribute already exists, but it has a different target value
7795 if (active) {
7796 ret = replmd_check_singleval_la_conflict(module, replmd_private,
7797 tmp_ctx, msg->dn, la,
7798 dsdb_dn, pdn, pdn_list,
7799 old_el, schema, attr,
7800 seq_num,
7801 &add_as_inactive);
7802 if (ret != LDB_SUCCESS) {
7803 talloc_free(tmp_ctx);
7804 return ret;
7808 if (pdn != NULL) {
7809 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
7811 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
7812 /* remove the existing backlink */
7813 ret = replmd_add_backlink(module, replmd_private,
7814 schema,
7815 msg->dn,
7816 &pdn->guid, false, attr,
7817 parent);
7818 if (ret != LDB_SUCCESS) {
7819 talloc_free(tmp_ctx);
7820 return ret;
7824 val_to_update = pdn->v;
7825 old_dsdb_dn = pdn->dsdb_dn;
7827 } else {
7828 unsigned offset;
7831 * We know where the new one needs to be, from the *next
7832 * pointer into pdn_list.
7834 if (next == NULL) {
7835 offset = old_el->num_values;
7836 } else {
7837 if (next->dsdb_dn == NULL) {
7838 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
7839 attr->syntax->ldap_oid);
7840 if (ret != LDB_SUCCESS) {
7841 return ret;
7844 offset = next - pdn_list;
7845 if (offset > old_el->num_values) {
7846 talloc_free(tmp_ctx);
7847 return LDB_ERR_OPERATIONS_ERROR;
7851 old_el->values = talloc_realloc(msg->elements, old_el->values,
7852 struct ldb_val, old_el->num_values+1);
7853 if (!old_el->values) {
7854 ldb_module_oom(module);
7855 return LDB_ERR_OPERATIONS_ERROR;
7858 if (offset != old_el->num_values) {
7859 memmove(&old_el->values[offset + 1], &old_el->values[offset],
7860 (old_el->num_values - offset) * sizeof(old_el->values[0]));
7863 old_el->num_values++;
7865 val_to_update = &old_el->values[offset];
7866 old_dsdb_dn = NULL;
7869 /* set the link attribute's value to the info that was received */
7870 ret = replmd_set_la_val(tmp_ctx, val_to_update, dsdb_dn, old_dsdb_dn,
7871 &la->meta_data.originating_invocation_id,
7872 la->meta_data.originating_usn, seq_num,
7873 la->meta_data.originating_change_time,
7874 la->meta_data.version,
7875 !active);
7876 if (ret != LDB_SUCCESS) {
7877 talloc_free(tmp_ctx);
7878 return ret;
7881 if (add_as_inactive) {
7883 /* Set the new link as inactive/deleted to avoid conflicts */
7884 ret = replmd_delete_link_value(module, replmd_private, old_el,
7885 msg->dn, schema, attr, seq_num,
7886 false, &guid, dsdb_dn,
7887 val_to_update);
7889 if (ret != LDB_SUCCESS) {
7890 talloc_free(tmp_ctx);
7891 return ret;
7894 } else if (active) {
7896 /* if the new link is active, then add the new backlink */
7897 ret = replmd_add_backlink(module, replmd_private,
7898 schema,
7899 msg->dn,
7900 &guid, true, attr,
7901 parent);
7902 if (ret != LDB_SUCCESS) {
7903 talloc_free(tmp_ctx);
7904 return ret;
7908 /* we only change whenChanged and uSNChanged if the seq_num
7909 has changed */
7910 ret = add_time_element(msg, "whenChanged", t);
7911 if (ret != LDB_SUCCESS) {
7912 talloc_free(tmp_ctx);
7913 ldb_operr(ldb);
7914 return ret;
7917 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
7918 if (ret != LDB_SUCCESS) {
7919 talloc_free(tmp_ctx);
7920 ldb_operr(ldb);
7921 return ret;
7924 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7925 if (old_el == NULL) {
7926 talloc_free(tmp_ctx);
7927 return ldb_operr(ldb);
7930 ret = dsdb_check_single_valued_link(attr, old_el);
7931 if (ret != LDB_SUCCESS) {
7932 talloc_free(tmp_ctx);
7933 return ret;
7936 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
7938 ret = linked_attr_modify(module, msg, parent);
7939 if (ret != LDB_SUCCESS) {
7940 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
7941 ldb_errstring(ldb),
7942 ldb_ldif_message_redacted_string(ldb,
7943 tmp_ctx,
7944 LDB_CHANGETYPE_MODIFY,
7945 msg));
7946 talloc_free(tmp_ctx);
7947 return ret;
7950 talloc_free(tmp_ctx);
7952 return ret;
7955 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
7957 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
7958 return replmd_extended_replicated_objects(module, req);
7961 return ldb_next_request(module, req);
7966 we hook into the transaction operations to allow us to
7967 perform the linked attribute updates at the end of the whole
7968 transaction. This allows a forward linked attribute to be created
7969 before the object is created. During a vampire, w2k8 sends us linked
7970 attributes before the objects they are part of.
7972 static int replmd_start_transaction(struct ldb_module *module)
7974 /* create our private structure for this transaction */
7975 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
7976 struct replmd_private);
7977 replmd_txn_cleanup(replmd_private);
7979 /* free any leftover mod_usn records from cancelled
7980 transactions */
7981 while (replmd_private->ncs) {
7982 struct nc_entry *e = replmd_private->ncs;
7983 DLIST_REMOVE(replmd_private->ncs, e);
7984 talloc_free(e);
7987 replmd_private->originating_updates = false;
7989 return ldb_next_start_trans(module);
7993 on prepare commit we loop over our queued la_context structures and
7994 apply each of them
7996 static int replmd_prepare_commit(struct ldb_module *module)
7998 struct replmd_private *replmd_private =
7999 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8000 struct la_entry *la, *prev;
8001 int ret;
8004 * Walk the list of linked attributes from DRS replication.
8006 * We walk backwards, to do the first entry first, as we
8007 * added the entries with DLIST_ADD() which puts them at the
8008 * start of the list
8010 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
8011 prev = DLIST_PREV(la);
8012 DLIST_REMOVE(replmd_private->la_list, la);
8013 ret = replmd_process_linked_attribute(module, replmd_private,
8014 la, NULL);
8015 if (ret != LDB_SUCCESS) {
8016 replmd_txn_cleanup(replmd_private);
8017 return ret;
8021 replmd_txn_cleanup(replmd_private);
8023 /* possibly change @REPLCHANGED */
8024 ret = replmd_notify_store(module, NULL);
8025 if (ret != LDB_SUCCESS) {
8026 return ret;
8029 return ldb_next_prepare_commit(module);
8032 static int replmd_del_transaction(struct ldb_module *module)
8034 struct replmd_private *replmd_private =
8035 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8036 replmd_txn_cleanup(replmd_private);
8038 return ldb_next_del_trans(module);
8042 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
8043 .name = "repl_meta_data",
8044 .init_context = replmd_init,
8045 .add = replmd_add,
8046 .modify = replmd_modify,
8047 .rename = replmd_rename,
8048 .del = replmd_delete,
8049 .extended = replmd_extended,
8050 .start_transaction = replmd_start_transaction,
8051 .prepare_commit = replmd_prepare_commit,
8052 .del_transaction = replmd_del_transaction,
8055 int ldb_repl_meta_data_module_init(const char *version)
8057 LDB_MODULE_CHECK_VERSION(version);
8058 return ldb_register_module(&ldb_repl_meta_data_module_ops);