s4:repl_meta_data: add support for DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID
[Samba.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blob5bdc57366da6e5f58e10787f39818b33893b839f
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;
116 bool fix_link_sid;
119 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
120 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
121 static int replmd_check_upgrade_links(struct ldb_context *ldb,
122 struct parsed_dn *dns, uint32_t count,
123 struct ldb_message_element *el,
124 const char *ldap_oid);
125 static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
126 struct la_entry *la);
127 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
128 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
129 uint64_t usn, uint64_t local_usn, NTTIME nttime,
130 uint32_t version, bool deleted);
132 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
133 struct ldb_context *ldb,
134 struct ldb_dn *dn,
135 const char *rdn_name,
136 const struct ldb_val *rdn_value,
137 struct GUID guid);
139 enum urgent_situation {
140 REPL_URGENT_ON_CREATE = 1,
141 REPL_URGENT_ON_UPDATE = 2,
142 REPL_URGENT_ON_DELETE = 4
145 enum deletion_state {
146 OBJECT_NOT_DELETED=1,
147 OBJECT_DELETED=2,
148 OBJECT_RECYCLED=3,
149 OBJECT_TOMBSTONE=4,
150 OBJECT_REMOVED=5
153 static void replmd_deletion_state(struct ldb_module *module,
154 const struct ldb_message *msg,
155 enum deletion_state *current_state,
156 enum deletion_state *next_state)
158 int ret;
159 bool enabled = false;
161 if (msg == NULL) {
162 *current_state = OBJECT_REMOVED;
163 if (next_state != NULL) {
164 *next_state = OBJECT_REMOVED;
166 return;
169 ret = dsdb_recyclebin_enabled(module, &enabled);
170 if (ret != LDB_SUCCESS) {
171 enabled = false;
174 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
175 if (!enabled) {
176 *current_state = OBJECT_TOMBSTONE;
177 if (next_state != NULL) {
178 *next_state = OBJECT_REMOVED;
180 return;
183 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
184 *current_state = OBJECT_RECYCLED;
185 if (next_state != NULL) {
186 *next_state = OBJECT_REMOVED;
188 return;
191 *current_state = OBJECT_DELETED;
192 if (next_state != NULL) {
193 *next_state = OBJECT_RECYCLED;
195 return;
198 *current_state = OBJECT_NOT_DELETED;
199 if (next_state == NULL) {
200 return;
203 if (enabled) {
204 *next_state = OBJECT_DELETED;
205 } else {
206 *next_state = OBJECT_TOMBSTONE;
210 static const struct {
211 const char *update_name;
212 enum urgent_situation repl_situation;
213 } urgent_objects[] = {
214 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
215 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
216 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
217 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
218 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
219 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
220 {NULL, 0}
223 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
224 static const char *urgent_attrs[] = {
225 "lockoutTime",
226 "pwdLastSet",
227 "userAccountControl",
228 NULL
232 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
233 enum urgent_situation situation)
235 unsigned int i, j;
236 for (i=0; urgent_objects[i].update_name; i++) {
238 if ((situation & urgent_objects[i].repl_situation) == 0) {
239 continue;
242 for (j=0; j<objectclass_el->num_values; j++) {
243 const struct ldb_val *v = &objectclass_el->values[j];
244 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
245 return true;
249 return false;
252 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
254 if (ldb_attr_in_list(urgent_attrs, el->name)) {
255 return true;
257 return false;
260 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
263 initialise the module
264 allocate the private structure and build the list
265 of partition DNs for use by replmd_notify()
267 static int replmd_init(struct ldb_module *module)
269 struct replmd_private *replmd_private;
270 struct ldb_context *ldb = ldb_module_get_ctx(module);
271 static const char *samba_dsdb_attrs[] = { SAMBA_COMPATIBLE_FEATURES_ATTR, NULL };
272 struct ldb_dn *samba_dsdb_dn;
273 struct ldb_result *res;
274 int ret;
275 TALLOC_CTX *frame = talloc_stackframe();
276 replmd_private = talloc_zero(module, struct replmd_private);
277 if (replmd_private == NULL) {
278 ldb_oom(ldb);
279 TALLOC_FREE(frame);
280 return LDB_ERR_OPERATIONS_ERROR;
282 ldb_module_set_private(module, replmd_private);
284 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
286 samba_dsdb_dn = ldb_dn_new(frame, ldb, "@SAMBA_DSDB");
287 if (!samba_dsdb_dn) {
288 TALLOC_FREE(frame);
289 return ldb_oom(ldb);
292 ret = dsdb_module_search_dn(module, frame, &res, samba_dsdb_dn,
293 samba_dsdb_attrs, DSDB_FLAG_NEXT_MODULE, NULL);
294 if (ret == LDB_SUCCESS) {
295 replmd_private->sorted_links
296 = ldb_msg_check_string_attribute(res->msgs[0],
297 SAMBA_COMPATIBLE_FEATURES_ATTR,
298 SAMBA_SORTED_LINKS_FEATURE);
300 TALLOC_FREE(frame);
302 return ldb_next_init(module);
306 cleanup our per-transaction contexts
308 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
310 talloc_free(replmd_private->la_ctx);
311 replmd_private->la_list = NULL;
312 replmd_private->la_ctx = NULL;
317 struct la_backlink {
318 struct la_backlink *next, *prev;
319 const char *attr_name;
320 struct ldb_dn *forward_dn;
321 struct GUID target_guid;
322 bool active;
326 a ldb_modify request operating on modules below the
327 current module
329 static int linked_attr_modify(struct ldb_module *module,
330 const struct ldb_message *message,
331 struct ldb_request *parent)
333 struct ldb_request *mod_req;
334 int ret;
335 struct ldb_context *ldb = ldb_module_get_ctx(module);
336 TALLOC_CTX *tmp_ctx = talloc_new(module);
337 struct ldb_result *res;
339 res = talloc_zero(tmp_ctx, struct ldb_result);
340 if (!res) {
341 talloc_free(tmp_ctx);
342 return ldb_oom(ldb_module_get_ctx(module));
345 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
346 message,
347 NULL,
348 res,
349 ldb_modify_default_callback,
350 parent);
351 LDB_REQ_SET_LOCATION(mod_req);
352 if (ret != LDB_SUCCESS) {
353 talloc_free(tmp_ctx);
354 return ret;
357 ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
358 false, NULL);
359 if (ret != LDB_SUCCESS) {
360 return ret;
363 /* Run the new request */
364 ret = ldb_next_request(module, mod_req);
366 if (ret == LDB_SUCCESS) {
367 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
370 talloc_free(tmp_ctx);
371 return ret;
375 process a backlinks we accumulated during a transaction, adding and
376 deleting the backlinks from the target objects
378 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
380 struct ldb_dn *target_dn, *source_dn;
381 int ret;
382 struct ldb_context *ldb = ldb_module_get_ctx(module);
383 struct ldb_message *msg;
384 TALLOC_CTX *frame = talloc_stackframe();
385 char *dn_string;
388 - find DN of target
389 - find DN of source
390 - construct ldb_message
391 - either an add or a delete
393 ret = dsdb_module_dn_by_guid(module, frame, &bl->target_guid, &target_dn, parent);
394 if (ret != LDB_SUCCESS) {
395 struct GUID_txt_buf guid_str;
396 DBG_WARNING("Failed to find target DN for linked attribute with GUID %s\n",
397 GUID_buf_string(&bl->target_guid, &guid_str));
398 DBG_WARNING("Please run 'samba-tool dbcheck' to resolve any missing backlinks.\n");
399 talloc_free(frame);
400 return LDB_SUCCESS;
403 msg = ldb_msg_new(frame);
404 if (msg == NULL) {
405 ldb_module_oom(module);
406 talloc_free(frame);
407 return LDB_ERR_OPERATIONS_ERROR;
410 source_dn = ldb_dn_copy(frame, bl->forward_dn);
411 if (!source_dn) {
412 ldb_module_oom(module);
413 talloc_free(frame);
414 return LDB_ERR_OPERATIONS_ERROR;
415 } else {
416 /* Filter down to the attributes we want in the backlink */
417 const char *accept[] = { "GUID", "SID", NULL };
418 ldb_dn_extended_filter(source_dn, accept);
421 /* construct a ldb_message for adding/deleting the backlink */
422 msg->dn = target_dn;
423 dn_string = ldb_dn_get_extended_linearized(frame, bl->forward_dn, 1);
424 if (!dn_string) {
425 ldb_module_oom(module);
426 talloc_free(frame);
427 return LDB_ERR_OPERATIONS_ERROR;
429 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
430 if (ret != LDB_SUCCESS) {
431 talloc_free(frame);
432 return ret;
434 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
436 /* a backlink should never be single valued. Unfortunately the
437 exchange schema has a attribute
438 msExchBridgeheadedLocalConnectorsDNBL which is single
439 valued and a backlink. We need to cope with that by
440 ignoring the single value flag */
441 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
443 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
444 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
445 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
446 cope with possible corruption where the backlink has
447 already been removed */
448 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
449 ldb_dn_get_linearized(target_dn),
450 ldb_dn_get_linearized(source_dn),
451 ldb_errstring(ldb)));
452 ret = LDB_SUCCESS;
453 } else if (ret != LDB_SUCCESS) {
454 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
455 bl->active?"add":"remove",
456 ldb_dn_get_linearized(source_dn),
457 ldb_dn_get_linearized(target_dn),
458 ldb_errstring(ldb));
459 talloc_free(frame);
460 return ret;
462 talloc_free(frame);
463 return ret;
467 add a backlink to the list of backlinks to add/delete in the prepare
468 commit
470 forward_dn is stolen onto the defereed context
472 static int replmd_defer_add_backlink(struct ldb_module *module,
473 struct replmd_private *replmd_private,
474 const struct dsdb_schema *schema,
475 struct replmd_replicated_request *ac,
476 struct ldb_dn *forward_dn,
477 struct GUID *target_guid, bool active,
478 const struct dsdb_attribute *schema_attr,
479 struct ldb_request *parent)
481 const struct dsdb_attribute *target_attr;
482 struct la_backlink *bl;
484 bl = talloc(ac, struct la_backlink);
485 if (bl == NULL) {
486 ldb_module_oom(module);
487 return LDB_ERR_OPERATIONS_ERROR;
490 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
491 if (!target_attr) {
493 * windows 2003 has a broken schema where the
494 * definition of msDS-IsDomainFor is missing (which is
495 * supposed to be the backlink of the
496 * msDS-HasDomainNCs attribute
498 return LDB_SUCCESS;
501 bl->attr_name = target_attr->lDAPDisplayName;
502 bl->forward_dn = talloc_steal(bl, forward_dn);
503 bl->target_guid = *target_guid;
504 bl->active = active;
506 DLIST_ADD(ac->la_backlinks, bl);
508 return LDB_SUCCESS;
512 add a backlink to the list of backlinks to add/delete in the prepare
513 commit
515 static int replmd_add_backlink(struct ldb_module *module,
516 struct replmd_private *replmd_private,
517 const struct dsdb_schema *schema,
518 struct ldb_dn *forward_dn,
519 struct GUID *target_guid, bool active,
520 const struct dsdb_attribute *schema_attr,
521 struct ldb_request *parent)
523 const struct dsdb_attribute *target_attr;
524 struct la_backlink bl;
525 int ret;
527 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
528 if (!target_attr) {
530 * windows 2003 has a broken schema where the
531 * definition of msDS-IsDomainFor is missing (which is
532 * supposed to be the backlink of the
533 * msDS-HasDomainNCs attribute
535 return LDB_SUCCESS;
538 bl.attr_name = target_attr->lDAPDisplayName;
539 bl.forward_dn = forward_dn;
540 bl.target_guid = *target_guid;
541 bl.active = active;
543 ret = replmd_process_backlink(module, &bl, parent);
544 return ret;
549 * Callback for most write operations in this module:
551 * notify the repl task that a object has changed. The notifies are
552 * gathered up in the replmd_private structure then written to the
553 * @REPLCHANGED object in each partition during the prepare_commit
555 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
557 int ret;
558 struct replmd_replicated_request *ac =
559 talloc_get_type_abort(req->context, struct replmd_replicated_request);
560 struct replmd_private *replmd_private =
561 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
562 struct nc_entry *modified_partition;
563 struct ldb_control *partition_ctrl;
564 const struct dsdb_control_current_partition *partition;
566 struct ldb_control **controls;
568 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
570 controls = ares->controls;
571 if (ldb_request_get_control(ac->req,
572 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
574 * Remove the current partition control from what we pass up
575 * the chain if it hasn't been requested manually.
577 controls = ldb_controls_except_specified(ares->controls, ares,
578 partition_ctrl);
581 if (ares->error != LDB_SUCCESS) {
582 struct GUID_txt_buf guid_txt;
583 struct ldb_message *msg = NULL;
584 char *s = NULL;
586 if (ac->apply_mode == false) {
587 DBG_NOTICE("Originating update failure. Error is: %s\n",
588 ldb_strerror(ares->error));
589 return ldb_module_done(ac->req, controls,
590 ares->response, ares->error);
593 msg = ac->objs->objects[ac->index_current].msg;
595 * Set at DBG_NOTICE as once these start to happe, they
596 * will happen a lot until resolved, due to repeated
597 * replication. The caller will probably print the
598 * ldb error string anyway.
600 DBG_NOTICE("DRS replication apply failure for %s. Error is: %s\n",
601 ldb_dn_get_linearized(msg->dn),
602 ldb_strerror(ares->error));
604 s = ldb_ldif_message_redacted_string(ldb_module_get_ctx(ac->module),
606 LDB_CHANGETYPE_ADD,
607 msg);
609 DBG_INFO("Failing DRS %s replication message was %s:\n%s\n",
610 ac->search_msg == NULL ? "ADD" : "MODIFY",
611 GUID_buf_string(&ac->objs->objects[ac->index_current].object_guid,
612 &guid_txt),
614 talloc_free(s);
615 return ldb_module_done(ac->req, controls,
616 ares->response, ares->error);
619 if (ares->type != LDB_REPLY_DONE) {
620 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
621 return ldb_module_done(ac->req, NULL,
622 NULL, LDB_ERR_OPERATIONS_ERROR);
625 if (ac->apply_mode == false) {
626 struct la_backlink *bl;
628 * process our backlink list after an replmd_add(),
629 * creating and deleting backlinks as necessary (this
630 * code is sync). The other cases are handled inline
631 * with the modify.
633 for (bl=ac->la_backlinks; bl; bl=bl->next) {
634 ret = replmd_process_backlink(ac->module, bl, ac->req);
635 if (ret != LDB_SUCCESS) {
636 return ldb_module_done(ac->req, NULL,
637 NULL, ret);
642 if (!partition_ctrl) {
643 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
644 return ldb_module_done(ac->req, NULL,
645 NULL, LDB_ERR_OPERATIONS_ERROR);
648 partition = talloc_get_type_abort(partition_ctrl->data,
649 struct dsdb_control_current_partition);
651 if (ac->seq_num > 0) {
652 for (modified_partition = replmd_private->ncs; modified_partition;
653 modified_partition = modified_partition->next) {
654 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
655 break;
659 if (modified_partition == NULL) {
660 modified_partition = talloc_zero(replmd_private, struct nc_entry);
661 if (!modified_partition) {
662 ldb_oom(ldb_module_get_ctx(ac->module));
663 return ldb_module_done(ac->req, NULL,
664 NULL, LDB_ERR_OPERATIONS_ERROR);
666 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
667 if (!modified_partition->dn) {
668 ldb_oom(ldb_module_get_ctx(ac->module));
669 return ldb_module_done(ac->req, NULL,
670 NULL, LDB_ERR_OPERATIONS_ERROR);
672 DLIST_ADD(replmd_private->ncs, modified_partition);
675 if (ac->seq_num > modified_partition->mod_usn) {
676 modified_partition->mod_usn = ac->seq_num;
677 if (ac->is_urgent) {
678 modified_partition->mod_usn_urgent = ac->seq_num;
681 if (!ac->apply_mode) {
682 replmd_private->originating_updates = true;
686 if (ac->apply_mode) {
687 ret = replmd_replicated_apply_isDeleted(ac);
688 if (ret != LDB_SUCCESS) {
689 return ldb_module_done(ac->req, NULL, NULL, ret);
691 return ret;
692 } else {
693 /* free the partition control container here, for the
694 * common path. Other cases will have it cleaned up
695 * eventually with the ares */
696 talloc_free(partition_ctrl);
697 return ldb_module_done(ac->req, controls,
698 ares->response, LDB_SUCCESS);
704 * update a @REPLCHANGED record in each partition if there have been
705 * any writes of replicated data in the partition
707 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
709 struct replmd_private *replmd_private =
710 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
712 while (replmd_private->ncs) {
713 int ret;
714 struct nc_entry *modified_partition = replmd_private->ncs;
716 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
717 modified_partition->mod_usn,
718 modified_partition->mod_usn_urgent, parent);
719 if (ret != LDB_SUCCESS) {
720 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
721 ldb_dn_get_linearized(modified_partition->dn)));
722 return ret;
725 if (ldb_dn_compare(modified_partition->dn,
726 replmd_private->schema_dn) == 0) {
727 struct ldb_result *ext_res;
728 ret = dsdb_module_extended(module,
729 replmd_private->schema_dn,
730 &ext_res,
731 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
732 ext_res,
733 DSDB_FLAG_NEXT_MODULE,
734 parent);
735 if (ret != LDB_SUCCESS) {
736 return ret;
738 talloc_free(ext_res);
741 DLIST_REMOVE(replmd_private->ncs, modified_partition);
742 talloc_free(modified_partition);
745 return LDB_SUCCESS;
750 created a replmd_replicated_request context
752 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
753 struct ldb_request *req)
755 struct ldb_context *ldb;
756 struct replmd_replicated_request *ac;
757 const struct GUID *our_invocation_id;
759 ldb = ldb_module_get_ctx(module);
761 ac = talloc_zero(req, struct replmd_replicated_request);
762 if (ac == NULL) {
763 ldb_oom(ldb);
764 return NULL;
767 ac->module = module;
768 ac->req = req;
770 ac->schema = dsdb_get_schema(ldb, ac);
771 if (!ac->schema) {
772 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
773 "replmd_modify: no dsdb_schema loaded");
774 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
775 talloc_free(ac);
776 return NULL;
779 /* get our invocationId */
780 our_invocation_id = samdb_ntds_invocation_id(ldb);
781 if (!our_invocation_id) {
782 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
783 "replmd_add: unable to find invocationId\n");
784 talloc_free(ac);
785 return NULL;
787 ac->our_invocation_id = *our_invocation_id;
789 return ac;
793 add a time element to a record
795 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
797 struct ldb_message_element *el;
798 char *s;
799 int ret;
801 if (ldb_msg_find_element(msg, attr) != NULL) {
802 return LDB_SUCCESS;
805 s = ldb_timestring(msg, t);
806 if (s == NULL) {
807 return LDB_ERR_OPERATIONS_ERROR;
810 ret = ldb_msg_add_string(msg, attr, s);
811 if (ret != LDB_SUCCESS) {
812 return ret;
815 el = ldb_msg_find_element(msg, attr);
816 /* always set as replace. This works because on add ops, the flag
817 is ignored */
818 el->flags = LDB_FLAG_MOD_REPLACE;
820 return LDB_SUCCESS;
824 add a uint64_t element to a record
826 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
827 const char *attr, uint64_t v)
829 struct ldb_message_element *el;
830 int ret;
832 if (ldb_msg_find_element(msg, attr) != NULL) {
833 return LDB_SUCCESS;
836 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
837 if (ret != LDB_SUCCESS) {
838 return ret;
841 el = ldb_msg_find_element(msg, attr);
842 /* always set as replace. This works because on add ops, the flag
843 is ignored */
844 el->flags = LDB_FLAG_MOD_REPLACE;
846 return LDB_SUCCESS;
849 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
850 const struct replPropertyMetaData1 *m2,
851 const uint32_t *rdn_attid)
854 * This assignment seems inoccous, but it is critical for the
855 * system, as we need to do the comparisons as a unsigned
856 * quantity, not signed (enums are signed integers)
858 uint32_t attid_1 = m1->attid;
859 uint32_t attid_2 = m2->attid;
861 if (attid_1 == attid_2) {
862 return 0;
866 * See above regarding this being an unsigned comparison.
867 * Otherwise when the high bit is set on non-standard
868 * attributes, they would end up first, before objectClass
869 * (0).
871 return attid_1 > attid_2 ? 1 : -1;
874 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
875 struct replPropertyMetaDataCtr1 *ctr1,
876 struct ldb_dn *dn)
878 if (ctr1->count == 0) {
879 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
880 "No elements found in replPropertyMetaData for %s!\n",
881 ldb_dn_get_linearized(dn));
882 return LDB_ERR_CONSTRAINT_VIOLATION;
885 /* the objectClass attribute is value 0x00000000, so must be first */
886 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
887 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
888 "No objectClass found in replPropertyMetaData for %s!\n",
889 ldb_dn_get_linearized(dn));
890 return LDB_ERR_OBJECT_CLASS_VIOLATION;
893 return LDB_SUCCESS;
896 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
897 struct replPropertyMetaDataCtr1 *ctr1,
898 struct ldb_dn *dn)
900 /* Note this is O(n^2) for the almost-sorted case, which this is */
901 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, NULL,
902 replmd_replPropertyMetaData1_attid_sort);
903 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
906 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
907 const struct ldb_message_element *e2,
908 const struct dsdb_schema *schema)
910 const struct dsdb_attribute *a1;
911 const struct dsdb_attribute *a2;
914 * TODO: make this faster by caching the dsdb_attribute pointer
915 * on the ldb_messag_element
918 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
919 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
922 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
923 * in the schema
925 if (!a1 || !a2) {
926 return strcasecmp(e1->name, e2->name);
928 if (a1->attributeID_id == a2->attributeID_id) {
929 return 0;
931 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
934 static void replmd_ldb_message_sort(struct ldb_message *msg,
935 const struct dsdb_schema *schema)
937 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
940 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
941 const struct GUID *invocation_id,
942 uint64_t local_usn, NTTIME nttime);
944 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2);
946 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
947 struct ldb_message_element *el, struct parsed_dn **pdn,
948 const char *ldap_oid, struct ldb_request *parent);
950 static int check_parsed_dn_duplicates(struct ldb_module *module,
951 struct ldb_message_element *el,
952 struct parsed_dn *pdn);
955 fix up linked attributes in replmd_add.
956 This involves setting up the right meta-data in extended DN
957 components, and creating backlinks to the object
959 static int replmd_add_fix_la(struct ldb_module *module, TALLOC_CTX *mem_ctx,
960 struct replmd_private *replmd_private,
961 struct ldb_message_element *el,
962 struct replmd_replicated_request *ac,
963 NTTIME now,
964 struct ldb_dn *forward_dn,
965 const struct dsdb_attribute *sa,
966 struct ldb_request *parent)
968 unsigned int i;
969 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
970 struct ldb_context *ldb = ldb_module_get_ctx(module);
971 struct parsed_dn *pdn;
972 /* We will take a reference to the schema in replmd_add_backlink */
973 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
974 struct ldb_val *new_values = NULL;
975 int ret;
977 if (dsdb_check_single_valued_link(sa, el) == LDB_SUCCESS) {
978 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
979 } else {
980 ldb_asprintf_errstring(ldb,
981 "Attribute %s is single valued but "
982 "more than one value has been supplied",
983 el->name);
984 talloc_free(tmp_ctx);
985 return LDB_ERR_CONSTRAINT_VIOLATION;
988 ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
989 sa->syntax->ldap_oid, parent);
990 if (ret != LDB_SUCCESS) {
991 talloc_free(tmp_ctx);
992 return ret;
995 ret = check_parsed_dn_duplicates(module, el, pdn);
996 if (ret != LDB_SUCCESS) {
997 talloc_free(tmp_ctx);
998 return ret;
1001 new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
1002 if (new_values == NULL) {
1003 ldb_module_oom(module);
1004 talloc_free(tmp_ctx);
1005 return LDB_ERR_OPERATIONS_ERROR;
1008 for (i = 0; i < el->num_values; i++) {
1009 struct parsed_dn *p = &pdn[i];
1010 ret = replmd_build_la_val(el->values, p->v, p->dsdb_dn,
1011 &ac->our_invocation_id,
1012 ac->seq_num, now);
1013 if (ret != LDB_SUCCESS) {
1014 talloc_free(tmp_ctx);
1015 return ret;
1018 ret = replmd_defer_add_backlink(module, replmd_private,
1019 schema, ac,
1020 forward_dn, &p->guid, true, sa,
1021 parent);
1022 if (ret != LDB_SUCCESS) {
1023 talloc_free(tmp_ctx);
1024 return ret;
1027 new_values[i] = *p->v;
1029 el->values = talloc_steal(mem_ctx, new_values);
1031 talloc_free(tmp_ctx);
1032 return LDB_SUCCESS;
1035 static int replmd_add_make_extended_dn(struct ldb_request *req,
1036 const DATA_BLOB *guid_blob,
1037 struct ldb_dn **_extended_dn)
1039 int ret;
1040 const DATA_BLOB *sid_blob;
1041 /* Calculate an extended DN for any linked attributes */
1042 struct ldb_dn *extended_dn = ldb_dn_copy(req, req->op.add.message->dn);
1043 if (!extended_dn) {
1044 return LDB_ERR_OPERATIONS_ERROR;
1046 ret = ldb_dn_set_extended_component(extended_dn, "GUID", guid_blob);
1047 if (ret != LDB_SUCCESS) {
1048 return ret;
1051 sid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectSID");
1052 if (sid_blob != NULL) {
1053 ret = ldb_dn_set_extended_component(extended_dn, "SID", sid_blob);
1054 if (ret != LDB_SUCCESS) {
1055 return ret;
1058 *_extended_dn = extended_dn;
1059 return LDB_SUCCESS;
1063 intercept add requests
1065 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
1067 struct ldb_context *ldb;
1068 struct ldb_control *control;
1069 struct replmd_replicated_request *ac;
1070 enum ndr_err_code ndr_err;
1071 struct ldb_request *down_req;
1072 struct ldb_message *msg;
1073 const DATA_BLOB *guid_blob;
1074 DATA_BLOB guid_blob_stack;
1075 struct GUID guid;
1076 uint8_t guid_data[16];
1077 struct replPropertyMetaDataBlob nmd;
1078 struct ldb_val nmd_value;
1079 struct ldb_dn *extended_dn = NULL;
1082 * The use of a time_t here seems odd, but as the NTTIME
1083 * elements are actually declared as NTTIME_1sec in the IDL,
1084 * getting a higher resolution timestamp is not required.
1086 time_t t = time(NULL);
1087 NTTIME now;
1088 char *time_str;
1089 int ret;
1090 unsigned int i;
1091 unsigned int functional_level;
1092 uint32_t ni=0;
1093 bool allow_add_guid = false;
1094 bool remove_current_guid = false;
1095 bool is_urgent = false;
1096 bool is_schema_nc = false;
1097 struct ldb_message_element *objectclass_el;
1098 struct replmd_private *replmd_private =
1099 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
1101 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
1102 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
1103 if (control) {
1104 allow_add_guid = true;
1107 /* do not manipulate our control entries */
1108 if (ldb_dn_is_special(req->op.add.message->dn)) {
1109 return ldb_next_request(module, req);
1112 ldb = ldb_module_get_ctx(module);
1114 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
1116 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
1117 if (guid_blob != NULL) {
1118 if (!allow_add_guid) {
1119 ldb_set_errstring(ldb,
1120 "replmd_add: it's not allowed to add an object with objectGUID!");
1121 return LDB_ERR_UNWILLING_TO_PERFORM;
1122 } else {
1123 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
1124 if (!NT_STATUS_IS_OK(status)) {
1125 ldb_set_errstring(ldb,
1126 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1127 return LDB_ERR_UNWILLING_TO_PERFORM;
1129 /* we remove this attribute as it can be a string and
1130 * will not be treated correctly and then we will re-add
1131 * it later on in the good format */
1132 remove_current_guid = true;
1134 } else {
1135 /* a new GUID */
1136 guid = GUID_random();
1138 guid_blob_stack = data_blob_const(guid_data, sizeof(guid_data));
1140 /* This can't fail */
1141 ndr_push_struct_into_fixed_blob(&guid_blob_stack, &guid,
1142 (ndr_push_flags_fn_t)ndr_push_GUID);
1143 guid_blob = &guid_blob_stack;
1146 ac = replmd_ctx_init(module, req);
1147 if (ac == NULL) {
1148 return ldb_module_oom(module);
1151 functional_level = dsdb_functional_level(ldb);
1153 /* Get a sequence number from the backend */
1154 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
1155 if (ret != LDB_SUCCESS) {
1156 talloc_free(ac);
1157 return ret;
1160 /* we have to copy the message as the caller might have it as a const */
1161 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
1162 if (msg == NULL) {
1163 ldb_oom(ldb);
1164 talloc_free(ac);
1165 return LDB_ERR_OPERATIONS_ERROR;
1168 /* generated times */
1169 unix_to_nt_time(&now, t);
1170 time_str = ldb_timestring(msg, t);
1171 if (!time_str) {
1172 ldb_oom(ldb);
1173 talloc_free(ac);
1174 return LDB_ERR_OPERATIONS_ERROR;
1176 if (remove_current_guid) {
1177 ldb_msg_remove_attr(msg,"objectGUID");
1181 * remove autogenerated attributes
1183 ldb_msg_remove_attr(msg, "whenCreated");
1184 ldb_msg_remove_attr(msg, "whenChanged");
1185 ldb_msg_remove_attr(msg, "uSNCreated");
1186 ldb_msg_remove_attr(msg, "uSNChanged");
1187 ldb_msg_remove_attr(msg, "replPropertyMetaData");
1190 * readd replicated attributes
1192 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1193 if (ret != LDB_SUCCESS) {
1194 ldb_oom(ldb);
1195 talloc_free(ac);
1196 return ret;
1199 /* build the replication meta_data */
1200 ZERO_STRUCT(nmd);
1201 nmd.version = 1;
1202 nmd.ctr.ctr1.count = msg->num_elements;
1203 nmd.ctr.ctr1.array = talloc_array(msg,
1204 struct replPropertyMetaData1,
1205 nmd.ctr.ctr1.count);
1206 if (!nmd.ctr.ctr1.array) {
1207 ldb_oom(ldb);
1208 talloc_free(ac);
1209 return LDB_ERR_OPERATIONS_ERROR;
1212 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1214 for (i=0; i < msg->num_elements;) {
1215 struct ldb_message_element *e = &msg->elements[i];
1216 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1217 const struct dsdb_attribute *sa;
1219 if (e->name[0] == '@') {
1220 i++;
1221 continue;
1224 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1225 if (!sa) {
1226 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1227 "replmd_add: attribute '%s' not defined in schema\n",
1228 e->name);
1229 talloc_free(ac);
1230 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1233 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1234 /* if the attribute is not replicated (0x00000001)
1235 * or constructed (0x00000004) it has no metadata
1237 i++;
1238 continue;
1241 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1242 if (extended_dn == NULL) {
1243 ret = replmd_add_make_extended_dn(req,
1244 guid_blob,
1245 &extended_dn);
1246 if (ret != LDB_SUCCESS) {
1247 talloc_free(ac);
1248 return ret;
1253 * Prepare the context for the backlinks and
1254 * create metadata for the forward links. The
1255 * backlinks are created in
1256 * replmd_op_callback() after the successful
1257 * ADD of the object.
1259 ret = replmd_add_fix_la(module, msg->elements,
1260 replmd_private, e,
1261 ac, now,
1262 extended_dn,
1263 sa, req);
1264 if (ret != LDB_SUCCESS) {
1265 talloc_free(ac);
1266 return ret;
1268 /* linked attributes are not stored in
1269 replPropertyMetaData in FL above w2k */
1270 i++;
1271 continue;
1274 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1275 m->version = 1;
1276 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1277 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1278 const char* rdn;
1280 if (rdn_val == NULL) {
1281 ldb_oom(ldb);
1282 talloc_free(ac);
1283 return LDB_ERR_OPERATIONS_ERROR;
1286 rdn = (const char*)rdn_val->data;
1287 if (strcmp(rdn, "Deleted Objects") == 0) {
1289 * Set the originating_change_time to 29/12/9999 at 23:59:59
1290 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1292 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1293 } else {
1294 m->originating_change_time = now;
1296 } else {
1297 m->originating_change_time = now;
1299 m->originating_invocation_id = ac->our_invocation_id;
1300 m->originating_usn = ac->seq_num;
1301 m->local_usn = ac->seq_num;
1302 ni++;
1304 if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1305 i++;
1306 continue;
1309 e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1311 if (e->num_values != 0) {
1312 i++;
1313 continue;
1316 ldb_msg_remove_element(msg, e);
1319 /* fix meta data count */
1320 nmd.ctr.ctr1.count = ni;
1323 * sort meta data array
1325 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1326 if (ret != LDB_SUCCESS) {
1327 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1328 talloc_free(ac);
1329 return ret;
1332 /* generated NDR encoded values */
1333 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1334 &nmd,
1335 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1336 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1337 ldb_oom(ldb);
1338 talloc_free(ac);
1339 return LDB_ERR_OPERATIONS_ERROR;
1343 * add the autogenerated values
1345 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1346 if (ret != LDB_SUCCESS) {
1347 ldb_oom(ldb);
1348 talloc_free(ac);
1349 return ret;
1351 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1352 if (ret != LDB_SUCCESS) {
1353 ldb_oom(ldb);
1354 talloc_free(ac);
1355 return ret;
1357 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1358 if (ret != LDB_SUCCESS) {
1359 ldb_oom(ldb);
1360 talloc_free(ac);
1361 return ret;
1363 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1364 if (ret != LDB_SUCCESS) {
1365 ldb_oom(ldb);
1366 talloc_free(ac);
1367 return ret;
1369 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1370 if (ret != LDB_SUCCESS) {
1371 ldb_oom(ldb);
1372 talloc_free(ac);
1373 return ret;
1377 * sort the attributes by attid before storing the object
1379 replmd_ldb_message_sort(msg, ac->schema);
1382 * Assert that we do have an objectClass
1384 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1385 if (objectclass_el == NULL) {
1386 ldb_asprintf_errstring(ldb, __location__
1387 ": objectClass missing on %s\n",
1388 ldb_dn_get_linearized(msg->dn));
1389 talloc_free(ac);
1390 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1392 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1393 REPL_URGENT_ON_CREATE);
1395 ac->is_urgent = is_urgent;
1396 ret = ldb_build_add_req(&down_req, ldb, ac,
1397 msg,
1398 req->controls,
1399 ac, replmd_op_callback,
1400 req);
1402 LDB_REQ_SET_LOCATION(down_req);
1403 if (ret != LDB_SUCCESS) {
1404 talloc_free(ac);
1405 return ret;
1408 /* current partition control is needed by "replmd_op_callback" */
1409 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1410 ret = ldb_request_add_control(down_req,
1411 DSDB_CONTROL_CURRENT_PARTITION_OID,
1412 false, NULL);
1413 if (ret != LDB_SUCCESS) {
1414 talloc_free(ac);
1415 return ret;
1419 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1420 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1421 if (ret != LDB_SUCCESS) {
1422 talloc_free(ac);
1423 return ret;
1427 /* mark the control done */
1428 if (control) {
1429 control->critical = 0;
1431 /* go on with the call chain */
1432 return ldb_next_request(module, down_req);
1437 * update the replPropertyMetaData for one element
1439 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1440 struct ldb_message *msg,
1441 struct ldb_message_element *el,
1442 struct ldb_message_element *old_el,
1443 struct replPropertyMetaDataBlob *omd,
1444 const struct dsdb_schema *schema,
1445 uint64_t *seq_num,
1446 const struct GUID *our_invocation_id,
1447 NTTIME now,
1448 bool is_schema_nc,
1449 bool is_forced_rodc,
1450 struct ldb_request *req)
1452 uint32_t i;
1453 const struct dsdb_attribute *a;
1454 struct replPropertyMetaData1 *md1;
1455 bool may_skip = false;
1456 uint32_t attid;
1458 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1459 if (a == NULL) {
1460 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1461 /* allow this to make it possible for dbcheck
1462 to remove bad attributes */
1463 return LDB_SUCCESS;
1466 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1467 el->name));
1468 return LDB_ERR_OPERATIONS_ERROR;
1471 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1473 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1474 return LDB_SUCCESS;
1478 * if the attribute's value haven't changed, and this isn't
1479 * just a delete of everything then return LDB_SUCCESS Unless
1480 * we have the provision control or if the attribute is
1481 * interSiteTopologyGenerator as this page explain:
1482 * http://support.microsoft.com/kb/224815 this attribute is
1483 * periodicaly written by the DC responsible for the intersite
1484 * generation in a given site
1486 * Unchanged could be deleting or replacing an already-gone
1487 * thing with an unconstrained delete/empty replace or a
1488 * replace with the same value, but not an add with the same
1489 * value because that could be about adding a duplicate (which
1490 * is for someone else to error out on).
1492 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1493 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1494 may_skip = true;
1496 } else if (old_el == NULL && el->num_values == 0) {
1497 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1498 may_skip = true;
1499 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1500 may_skip = true;
1502 } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1503 ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
1505 * We intentionally skip the version bump when attempting to
1506 * vanish links.
1508 * The control is set by dbcheck and expunge-tombstones which
1509 * both attempt to be non-replicating. Otherwise, making an
1510 * alteration to the replication state would trigger a
1511 * broadcast of all expunged objects.
1513 may_skip = true;
1516 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1517 may_skip = false;
1518 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1521 if (may_skip) {
1522 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1523 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1525 * allow this to make it possible for dbcheck
1526 * to rebuild broken metadata
1528 return LDB_SUCCESS;
1532 for (i=0; i<omd->ctr.ctr1.count; i++) {
1534 * First check if we find it under the msDS-IntID,
1535 * then check if we find it under the OID and
1536 * prefixMap ID.
1538 * This allows the administrator to simply re-write
1539 * the attributes and so restore replication, which is
1540 * likely what they will try to do.
1542 if (attid == omd->ctr.ctr1.array[i].attid) {
1543 break;
1546 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1547 break;
1551 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1552 /* linked attributes are not stored in
1553 replPropertyMetaData in FL above w2k, but we do
1554 raise the seqnum for the object */
1555 if (*seq_num == 0 &&
1556 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1557 return LDB_ERR_OPERATIONS_ERROR;
1559 return LDB_SUCCESS;
1562 if (i == omd->ctr.ctr1.count) {
1563 /* we need to add a new one */
1564 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1565 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1566 if (omd->ctr.ctr1.array == NULL) {
1567 ldb_oom(ldb);
1568 return LDB_ERR_OPERATIONS_ERROR;
1570 omd->ctr.ctr1.count++;
1571 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1574 /* Get a new sequence number from the backend. We only do this
1575 * if we have a change that requires a new
1576 * replPropertyMetaData element
1578 if (*seq_num == 0) {
1579 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1580 if (ret != LDB_SUCCESS) {
1581 return LDB_ERR_OPERATIONS_ERROR;
1585 md1 = &omd->ctr.ctr1.array[i];
1586 md1->version++;
1587 md1->attid = attid;
1589 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1590 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1591 const char* rdn;
1593 if (rdn_val == NULL) {
1594 ldb_oom(ldb);
1595 return LDB_ERR_OPERATIONS_ERROR;
1598 rdn = (const char*)rdn_val->data;
1599 if (strcmp(rdn, "Deleted Objects") == 0) {
1601 * Set the originating_change_time to 29/12/9999 at 23:59:59
1602 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1604 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1605 } else {
1606 md1->originating_change_time = now;
1608 } else {
1609 md1->originating_change_time = now;
1611 md1->originating_invocation_id = *our_invocation_id;
1612 md1->originating_usn = *seq_num;
1613 md1->local_usn = *seq_num;
1615 if (is_forced_rodc) {
1616 /* Force version to 0 to be overriden later via replication */
1617 md1->version = 0;
1620 return LDB_SUCCESS;
1624 * Bump the replPropertyMetaData version on an attribute, and if it
1625 * has changed (or forced by leaving rdn_old NULL), update the value
1626 * in the entry.
1628 * This is important, as calling a modify operation may not change the
1629 * version number if the values appear unchanged, but a rename between
1630 * parents bumps this value.
1633 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1634 struct ldb_message *msg,
1635 const struct ldb_val *rdn_new,
1636 const struct ldb_val *rdn_old,
1637 struct replPropertyMetaDataBlob *omd,
1638 struct replmd_replicated_request *ar,
1639 NTTIME now,
1640 bool is_schema_nc,
1641 bool is_forced_rodc)
1643 const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
1644 const struct dsdb_attribute *rdn_attr =
1645 dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
1646 const char *attr_name = rdn_attr != NULL ?
1647 rdn_attr->lDAPDisplayName :
1648 rdn_name;
1649 struct ldb_message_element new_el = {
1650 .flags = LDB_FLAG_MOD_REPLACE,
1651 .name = attr_name,
1652 .num_values = 1,
1653 .values = discard_const_p(struct ldb_val, rdn_new)
1655 struct ldb_message_element old_el = {
1656 .flags = LDB_FLAG_MOD_REPLACE,
1657 .name = attr_name,
1658 .num_values = rdn_old ? 1 : 0,
1659 .values = discard_const_p(struct ldb_val, rdn_old)
1662 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1663 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1664 if (ret != LDB_SUCCESS) {
1665 return ldb_oom(ldb);
1669 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1670 omd, ar->schema, &ar->seq_num,
1671 &ar->our_invocation_id,
1672 now, is_schema_nc, is_forced_rodc,
1673 ar->req);
1677 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1679 uint32_t count = omd.ctr.ctr1.count;
1680 uint64_t max = 0;
1681 uint32_t i;
1682 for (i=0; i < count; i++) {
1683 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1684 if (max < m.local_usn) {
1685 max = m.local_usn;
1688 return max;
1692 * update the replPropertyMetaData object each time we modify an
1693 * object. This is needed for DRS replication, as the merge on the
1694 * client is based on this object
1696 static int replmd_update_rpmd(struct ldb_module *module,
1697 const struct dsdb_schema *schema,
1698 struct ldb_request *req,
1699 const char * const *rename_attrs,
1700 struct ldb_message *msg, uint64_t *seq_num,
1701 time_t t, bool is_schema_nc,
1702 bool *is_urgent, bool *rodc)
1704 const struct ldb_val *omd_value;
1705 enum ndr_err_code ndr_err;
1706 struct replPropertyMetaDataBlob omd;
1707 unsigned int i;
1708 NTTIME now;
1709 const struct GUID *our_invocation_id;
1710 int ret;
1711 const char * const *attrs = NULL;
1712 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1713 struct ldb_result *res;
1714 struct ldb_context *ldb;
1715 struct ldb_message_element *objectclass_el;
1716 enum urgent_situation situation;
1717 bool rmd_is_provided;
1718 bool rmd_is_just_resorted = false;
1719 const char *not_rename_attrs[4 + msg->num_elements];
1720 bool is_forced_rodc = false;
1722 if (rename_attrs) {
1723 attrs = rename_attrs;
1724 } else {
1725 for (i = 0; i < msg->num_elements; i++) {
1726 not_rename_attrs[i] = msg->elements[i].name;
1728 not_rename_attrs[i] = "replPropertyMetaData";
1729 not_rename_attrs[i+1] = "objectClass";
1730 not_rename_attrs[i+2] = "instanceType";
1731 not_rename_attrs[i+3] = NULL;
1732 attrs = not_rename_attrs;
1735 ldb = ldb_module_get_ctx(module);
1737 ret = samdb_rodc(ldb, rodc);
1738 if (ret != LDB_SUCCESS) {
1739 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1740 *rodc = false;
1743 if (*rodc &&
1744 ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
1745 is_forced_rodc = true;
1748 our_invocation_id = samdb_ntds_invocation_id(ldb);
1749 if (!our_invocation_id) {
1750 /* this happens during an initial vampire while
1751 updating the schema */
1752 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1753 return LDB_SUCCESS;
1756 unix_to_nt_time(&now, t);
1758 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1759 rmd_is_provided = true;
1760 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1761 rmd_is_just_resorted = true;
1763 } else {
1764 rmd_is_provided = false;
1767 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1768 * otherwise we consider we are updating */
1769 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1770 situation = REPL_URGENT_ON_DELETE;
1771 } else if (rename_attrs) {
1772 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1773 } else {
1774 situation = REPL_URGENT_ON_UPDATE;
1777 if (rmd_is_provided) {
1778 /* In this case the change_replmetadata control was supplied */
1779 /* We check that it's the only attribute that is provided
1780 * (it's a rare case so it's better to keep the code simplier)
1781 * We also check that the highest local_usn is bigger or the same as
1782 * uSNChanged. */
1783 uint64_t db_seq;
1784 if( msg->num_elements != 1 ||
1785 strncmp(msg->elements[0].name,
1786 "replPropertyMetaData", 20) ) {
1787 DEBUG(0,(__location__ ": changereplmetada control called without "\
1788 "a specified replPropertyMetaData attribute or with others\n"));
1789 return LDB_ERR_OPERATIONS_ERROR;
1791 if (situation != REPL_URGENT_ON_UPDATE) {
1792 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1793 return LDB_ERR_OPERATIONS_ERROR;
1795 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1796 if (!omd_value) {
1797 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1798 ldb_dn_get_linearized(msg->dn)));
1799 return LDB_ERR_OPERATIONS_ERROR;
1801 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1802 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1803 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1804 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1805 ldb_dn_get_linearized(msg->dn)));
1806 return LDB_ERR_OPERATIONS_ERROR;
1809 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1810 DSDB_FLAG_NEXT_MODULE |
1811 DSDB_SEARCH_SHOW_RECYCLED |
1812 DSDB_SEARCH_SHOW_EXTENDED_DN |
1813 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1814 DSDB_SEARCH_REVEAL_INTERNALS, req);
1816 if (ret != LDB_SUCCESS) {
1817 return ret;
1820 if (rmd_is_just_resorted == false) {
1821 *seq_num = find_max_local_usn(omd);
1823 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1826 * The test here now allows for a new
1827 * replPropertyMetaData with no change, if was
1828 * just dbcheck re-sorting the values.
1830 if (*seq_num <= db_seq) {
1831 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1832 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1833 (long long)*seq_num, (long long)db_seq));
1834 return LDB_ERR_OPERATIONS_ERROR;
1838 } else {
1839 /* search for the existing replPropertyMetaDataBlob. We need
1840 * to use REVEAL and ask for DNs in storage format to support
1841 * the check for values being the same in
1842 * replmd_update_rpmd_element()
1844 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1845 DSDB_FLAG_NEXT_MODULE |
1846 DSDB_SEARCH_SHOW_RECYCLED |
1847 DSDB_SEARCH_SHOW_EXTENDED_DN |
1848 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1849 DSDB_SEARCH_REVEAL_INTERNALS, req);
1850 if (ret != LDB_SUCCESS) {
1851 return ret;
1854 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1855 if (!omd_value) {
1856 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1857 ldb_dn_get_linearized(msg->dn)));
1858 return LDB_ERR_OPERATIONS_ERROR;
1861 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1862 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1863 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1864 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1865 ldb_dn_get_linearized(msg->dn)));
1866 return LDB_ERR_OPERATIONS_ERROR;
1869 if (omd.version != 1) {
1870 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1871 omd.version, ldb_dn_get_linearized(msg->dn)));
1872 return LDB_ERR_OPERATIONS_ERROR;
1875 for (i=0; i<msg->num_elements;) {
1876 struct ldb_message_element *el = &msg->elements[i];
1877 struct ldb_message_element *old_el;
1879 old_el = ldb_msg_find_element(res->msgs[0], el->name);
1880 ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1881 &omd, schema, seq_num,
1882 our_invocation_id,
1883 now, is_schema_nc,
1884 is_forced_rodc,
1885 req);
1886 if (ret != LDB_SUCCESS) {
1887 return ret;
1890 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1891 *is_urgent = replmd_check_urgent_attribute(el);
1894 if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1895 i++;
1896 continue;
1899 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1901 if (el->num_values != 0) {
1902 i++;
1903 continue;
1906 ldb_msg_remove_element(msg, el);
1911 * Assert that we have an objectClass attribute - this is major
1912 * corruption if we don't have this!
1914 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1915 if (objectclass_el != NULL) {
1917 * Now check if this objectClass means we need to do urgent replication
1919 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1920 situation)) {
1921 *is_urgent = true;
1923 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1924 ldb_asprintf_errstring(ldb, __location__
1925 ": objectClass missing on %s\n",
1926 ldb_dn_get_linearized(msg->dn));
1927 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1931 * replmd_update_rpmd_element has done an update if the
1932 * seq_num is set
1934 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1935 struct ldb_val *md_value;
1936 struct ldb_message_element *el;
1938 /*if we are RODC and this is a DRSR update then its ok*/
1939 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1940 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
1941 && !is_forced_rodc) {
1942 unsigned instanceType;
1944 if (*rodc) {
1945 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1946 return LDB_ERR_REFERRAL;
1949 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1950 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1951 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1952 "cannot change replicated attribute on partial replica");
1956 md_value = talloc(msg, struct ldb_val);
1957 if (md_value == NULL) {
1958 ldb_oom(ldb);
1959 return LDB_ERR_OPERATIONS_ERROR;
1962 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
1963 if (ret != LDB_SUCCESS) {
1964 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
1965 return ret;
1968 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1969 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1970 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1971 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1972 ldb_dn_get_linearized(msg->dn)));
1973 return LDB_ERR_OPERATIONS_ERROR;
1976 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1977 if (ret != LDB_SUCCESS) {
1978 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1979 ldb_dn_get_linearized(msg->dn)));
1980 return ret;
1983 el->num_values = 1;
1984 el->values = md_value;
1987 return LDB_SUCCESS;
1990 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1992 int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
1993 if (ret == 0) {
1994 return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
1995 &pdn2->dsdb_dn->extra_part);
1997 return ret;
2001 get a series of message element values as an array of DNs and GUIDs
2002 the result is sorted by GUID
2004 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
2005 struct ldb_message_element *el, struct parsed_dn **pdn,
2006 const char *ldap_oid, struct ldb_request *parent)
2008 unsigned int i;
2009 bool values_are_sorted = true;
2010 struct ldb_context *ldb = ldb_module_get_ctx(module);
2012 if (el == NULL) {
2013 *pdn = NULL;
2014 return LDB_SUCCESS;
2017 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
2018 if (!*pdn) {
2019 ldb_module_oom(module);
2020 return LDB_ERR_OPERATIONS_ERROR;
2023 for (i=0; i<el->num_values; i++) {
2024 struct ldb_val *v = &el->values[i];
2025 NTSTATUS status;
2026 struct ldb_dn *dn;
2027 struct parsed_dn *p;
2029 p = &(*pdn)[i];
2031 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
2032 if (p->dsdb_dn == NULL) {
2033 return LDB_ERR_INVALID_DN_SYNTAX;
2036 dn = p->dsdb_dn->dn;
2038 status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
2039 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
2040 unlikely(GUID_all_zero(&p->guid))) {
2041 /* we got a DN without a GUID - go find the GUID */
2042 int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
2043 if (ret != LDB_SUCCESS) {
2044 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
2045 ldb_dn_get_linearized(dn));
2046 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
2047 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
2048 ldb_attr_cmp(el->name, "member") == 0) {
2049 return LDB_ERR_UNWILLING_TO_PERFORM;
2051 return ret;
2053 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2054 if (ret != LDB_SUCCESS) {
2055 return ret;
2057 } else if (!NT_STATUS_IS_OK(status)) {
2058 return LDB_ERR_OPERATIONS_ERROR;
2060 if (i > 0 && values_are_sorted) {
2061 int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
2062 if (cmp < 0) {
2063 values_are_sorted = false;
2066 /* keep a pointer to the original ldb_val */
2067 p->v = v;
2069 if (! values_are_sorted) {
2070 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
2072 return LDB_SUCCESS;
2076 * Get a series of trusted message element values. The result is sorted by
2077 * GUID, even though the GUIDs might not be known. That works because we trust
2078 * the database to give us the elements like that if the
2079 * replmd_private->sorted_links flag is set.
2081 * We also ensure that the links are in the Functional Level 2003
2082 * linked attributes format.
2084 static int get_parsed_dns_trusted(struct ldb_module *module,
2085 struct replmd_private *replmd_private,
2086 TALLOC_CTX *mem_ctx,
2087 struct ldb_message_element *el,
2088 struct parsed_dn **pdn,
2089 const char *ldap_oid,
2090 struct ldb_request *parent)
2092 unsigned int i;
2093 int ret;
2094 if (el == NULL) {
2095 *pdn = NULL;
2096 return LDB_SUCCESS;
2099 if (!replmd_private->sorted_links) {
2100 /* We need to sort the list. This is the slow old path we want
2101 to avoid.
2103 ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2104 parent);
2105 if (ret != LDB_SUCCESS) {
2106 return ret;
2108 } else {
2109 /* Here we get a list of 'struct parsed_dns' without the parsing */
2110 *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
2111 el->num_values);
2112 if (!*pdn) {
2113 ldb_module_oom(module);
2114 return LDB_ERR_OPERATIONS_ERROR;
2117 for (i = 0; i < el->num_values; i++) {
2118 (*pdn)[i].v = &el->values[i];
2123 * This upgrades links to FL2003 style, and sorts the result
2124 * if that was needed.
2126 * TODO: Add a database feature that asserts we have no FL2000
2127 * style links to avoid this check or add a feature that
2128 * uses a similar check to find sorted/unsorted links
2129 * for an on-the-fly upgrade.
2132 ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
2133 *pdn, el->num_values,
2135 ldap_oid);
2136 if (ret != LDB_SUCCESS) {
2137 return ret;
2140 return LDB_SUCCESS;
2144 Return LDB_SUCCESS if a parsed_dn list contains no duplicate values,
2145 otherwise an error code. For compatibility the error code differs depending
2146 on whether or not the attribute is "member".
2148 As always, the parsed_dn list is assumed to be sorted.
2150 static int check_parsed_dn_duplicates(struct ldb_module *module,
2151 struct ldb_message_element *el,
2152 struct parsed_dn *pdn)
2154 unsigned int i;
2155 struct ldb_context *ldb = ldb_module_get_ctx(module);
2157 for (i = 1; i < el->num_values; i++) {
2158 struct parsed_dn *p = &pdn[i];
2159 if (parsed_dn_compare(p, &pdn[i - 1]) == 0) {
2160 ldb_asprintf_errstring(ldb,
2161 "Linked attribute %s has "
2162 "multiple identical values",
2163 el->name);
2164 if (ldb_attr_cmp(el->name, "member") == 0) {
2165 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2166 } else {
2167 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2171 return LDB_SUCCESS;
2175 build a new extended DN, including all meta data fields
2177 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2178 RMD_ADDTIME = originating_add_time
2179 RMD_INVOCID = originating_invocation_id
2180 RMD_CHANGETIME = originating_change_time
2181 RMD_ORIGINATING_USN = originating_usn
2182 RMD_LOCAL_USN = local_usn
2183 RMD_VERSION = version
2185 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v,
2186 struct dsdb_dn *dsdb_dn,
2187 const struct GUID *invocation_id,
2188 uint64_t local_usn, NTTIME nttime)
2190 return replmd_set_la_val(mem_ctx, v, dsdb_dn, NULL, invocation_id,
2191 local_usn, local_usn, nttime,
2192 RMD_VERSION_INITIAL, false);
2195 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2196 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2197 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
2198 bool deleted);
2201 check if any links need upgrading from w2k format
2203 static int replmd_check_upgrade_links(struct ldb_context *ldb,
2204 struct parsed_dn *dns, uint32_t count,
2205 struct ldb_message_element *el,
2206 const char *ldap_oid)
2208 uint32_t i;
2209 const struct GUID *invocation_id = NULL;
2210 for (i=0; i<count; i++) {
2211 NTSTATUS status;
2212 uint32_t version;
2213 int ret;
2214 if (dns[i].dsdb_dn == NULL) {
2215 ret = really_parse_trusted_dn(dns, ldb, &dns[i],
2216 ldap_oid);
2217 if (ret != LDB_SUCCESS) {
2218 return LDB_ERR_INVALID_DN_SYNTAX;
2222 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
2223 &version, "RMD_VERSION");
2224 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2226 * We optimistically assume they are all the same; if
2227 * the first one is fixed, they are all fixed.
2229 * If the first one was *not* fixed and we find a
2230 * later one that is, that is an occasion to shout
2231 * with DEBUG(0).
2233 if (i == 0) {
2234 return LDB_SUCCESS;
2236 DEBUG(0, ("Mixed w2k and fixed format "
2237 "linked attributes\n"));
2238 continue;
2241 if (invocation_id == NULL) {
2242 invocation_id = samdb_ntds_invocation_id(ldb);
2243 if (invocation_id == NULL) {
2244 return LDB_ERR_OPERATIONS_ERROR;
2249 /* it's an old one that needs upgrading */
2250 ret = replmd_update_la_val(el->values, dns[i].v,
2251 dns[i].dsdb_dn, dns[i].dsdb_dn,
2252 invocation_id, 1, 1, 0, false);
2253 if (ret != LDB_SUCCESS) {
2254 return ret;
2259 * This sort() is critical for the operation of
2260 * get_parsed_dns_trusted() because callers of this function
2261 * expect a sorted list, and FL2000 style links are not
2262 * sorted. In particular, as well as the upgrade case,
2263 * get_parsed_dns_trusted() is called from
2264 * replmd_delete_remove_link() even in FL2000 mode
2266 * We do not normally pay the cost of the qsort() due to the
2267 * early return in the RMD_VERSION found case.
2269 TYPESAFE_QSORT(dns, count, parsed_dn_compare);
2270 return LDB_SUCCESS;
2274 Sets the value for a linked attribute, including all meta data fields
2276 see replmd_build_la_val for value names
2278 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2279 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2280 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2281 uint32_t version, bool deleted)
2283 struct ldb_dn *dn = dsdb_dn->dn;
2284 const char *tstring, *usn_string, *flags_string;
2285 struct ldb_val tval;
2286 struct ldb_val iid;
2287 struct ldb_val usnv, local_usnv;
2288 struct ldb_val vers, flagsv;
2289 const struct ldb_val *old_addtime = NULL;
2290 NTSTATUS status;
2291 int ret;
2292 const char *dnstring;
2293 char *vstring;
2294 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2296 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2297 if (!tstring) {
2298 return LDB_ERR_OPERATIONS_ERROR;
2300 tval = data_blob_string_const(tstring);
2302 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2303 if (!usn_string) {
2304 return LDB_ERR_OPERATIONS_ERROR;
2306 usnv = data_blob_string_const(usn_string);
2308 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2309 if (!usn_string) {
2310 return LDB_ERR_OPERATIONS_ERROR;
2312 local_usnv = data_blob_string_const(usn_string);
2314 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2315 if (!NT_STATUS_IS_OK(status)) {
2316 return LDB_ERR_OPERATIONS_ERROR;
2319 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2320 if (!flags_string) {
2321 return LDB_ERR_OPERATIONS_ERROR;
2323 flagsv = data_blob_string_const(flags_string);
2325 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2326 if (ret != LDB_SUCCESS) return ret;
2328 /* get the ADDTIME from the original */
2329 if (old_dsdb_dn != NULL) {
2330 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn,
2331 "RMD_ADDTIME");
2333 if (old_addtime == NULL) {
2334 old_addtime = &tval;
2336 if (dsdb_dn != old_dsdb_dn ||
2337 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2338 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2339 if (ret != LDB_SUCCESS) return ret;
2342 /* use our invocation id */
2343 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2344 if (ret != LDB_SUCCESS) return ret;
2346 /* changetime is the current time */
2347 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2348 if (ret != LDB_SUCCESS) return ret;
2350 /* update the USN */
2351 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2352 if (ret != LDB_SUCCESS) return ret;
2354 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2355 if (ret != LDB_SUCCESS) return ret;
2357 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2358 vers = data_blob_string_const(vstring);
2359 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2360 if (ret != LDB_SUCCESS) return ret;
2362 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2363 if (dnstring == NULL) {
2364 return LDB_ERR_OPERATIONS_ERROR;
2366 *v = data_blob_string_const(dnstring);
2368 return LDB_SUCCESS;
2372 * Updates the value for a linked attribute, including all meta data fields
2374 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2375 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2376 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2377 bool deleted)
2379 uint32_t old_version;
2380 uint32_t version = RMD_VERSION_INITIAL;
2381 NTSTATUS status;
2384 * We're updating the linked attribute locally, so increase the version
2385 * by 1 so that other DCs will see the change when it gets replicated out
2387 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version,
2388 "RMD_VERSION");
2390 if (NT_STATUS_IS_OK(status)) {
2391 version = old_version + 1;
2394 return replmd_set_la_val(mem_ctx, v, dsdb_dn, old_dsdb_dn, invocation_id,
2395 usn, local_usn, nttime, version, deleted);
2399 handle adding a linked attribute
2401 static int replmd_modify_la_add(struct ldb_module *module,
2402 struct replmd_private *replmd_private,
2403 struct replmd_replicated_request *ac,
2404 struct ldb_message *msg,
2405 struct ldb_message_element *el,
2406 struct ldb_message_element *old_el,
2407 const struct dsdb_attribute *schema_attr,
2408 time_t t,
2409 struct ldb_dn *msg_dn,
2410 struct ldb_request *parent)
2412 unsigned int i, j;
2413 struct parsed_dn *dns, *old_dns;
2414 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2415 int ret;
2416 struct ldb_val *new_values = NULL;
2417 unsigned old_num_values = old_el ? old_el->num_values : 0;
2418 unsigned num_values = 0;
2419 unsigned max_num_values;
2420 struct ldb_context *ldb = ldb_module_get_ctx(module);
2421 NTTIME now;
2422 unix_to_nt_time(&now, t);
2424 /* get the DNs to be added, fully parsed.
2426 * We need full parsing because they came off the wire and we don't
2427 * trust them, besides which we need their details to know where to put
2428 * them.
2430 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2431 schema_attr->syntax->ldap_oid, parent);
2432 if (ret != LDB_SUCCESS) {
2433 talloc_free(tmp_ctx);
2434 return ret;
2437 /* get the existing DNs, lazily parsed */
2438 ret = get_parsed_dns_trusted(module, replmd_private,
2439 tmp_ctx, old_el, &old_dns,
2440 schema_attr->syntax->ldap_oid, parent);
2442 if (ret != LDB_SUCCESS) {
2443 talloc_free(tmp_ctx);
2444 return ret;
2447 max_num_values = old_num_values + el->num_values;
2448 if (max_num_values < old_num_values) {
2449 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2450 "old values: %u, new values: %u, sum: %u\n",
2451 old_num_values, el->num_values, max_num_values));
2452 talloc_free(tmp_ctx);
2453 return LDB_ERR_OPERATIONS_ERROR;
2456 new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2458 if (new_values == NULL) {
2459 ldb_module_oom(module);
2460 talloc_free(tmp_ctx);
2461 return LDB_ERR_OPERATIONS_ERROR;
2465 * For each new value, find where it would go in the list. If there is
2466 * a matching GUID there, we update the existing value; otherwise we
2467 * put it in place.
2469 j = 0;
2470 for (i = 0; i < el->num_values; i++) {
2471 struct parsed_dn *exact;
2472 struct parsed_dn *next;
2473 unsigned offset;
2474 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2475 &dns[i].guid,
2476 dns[i].dsdb_dn->dn,
2477 dns[i].dsdb_dn->extra_part, 0,
2478 &exact, &next,
2479 schema_attr->syntax->ldap_oid,
2480 true);
2481 if (err != LDB_SUCCESS) {
2482 talloc_free(tmp_ctx);
2483 return err;
2486 if (ac->fix_link_sid) {
2487 char *fixed_dnstring = NULL;
2488 struct dom_sid tmp_sid = { 0, };
2489 DATA_BLOB sid_blob = data_blob_null;
2490 enum ndr_err_code ndr_err;
2491 NTSTATUS status;
2492 int num;
2494 if (exact == NULL) {
2495 talloc_free(tmp_ctx);
2496 return ldb_operr(ldb);
2499 if (dns[i].dsdb_dn->dn_format != DSDB_NORMAL_DN) {
2500 talloc_free(tmp_ctx);
2501 return ldb_operr(ldb);
2505 * Only "<GUID=...><SID=...>" is allowed.
2507 * We get the GUID to just to find the old
2508 * value and the SID in order to add it
2509 * to the found value.
2512 num = ldb_dn_get_comp_num(dns[i].dsdb_dn->dn);
2513 if (num != 0) {
2514 talloc_free(tmp_ctx);
2515 return ldb_operr(ldb);
2518 num = ldb_dn_get_extended_comp_num(dns[i].dsdb_dn->dn);
2519 if (num != 2) {
2520 talloc_free(tmp_ctx);
2521 return ldb_operr(ldb);
2524 status = dsdb_get_extended_dn_sid(exact->dsdb_dn->dn,
2525 &tmp_sid, "SID");
2526 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2527 /* this is what we expect */
2528 } else if (NT_STATUS_IS_OK(status)) {
2529 struct GUID_txt_buf guid_str;
2530 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
2531 "i[%u] SID NOT MISSING... Attribute %s already "
2532 "exists for target GUID %s, SID %s, DN: %s",
2533 i, el->name,
2534 GUID_buf_string(&exact->guid,
2535 &guid_str),
2536 dom_sid_string(tmp_ctx, &tmp_sid),
2537 dsdb_dn_get_extended_linearized(tmp_ctx,
2538 exact->dsdb_dn, 1));
2539 talloc_free(tmp_ctx);
2540 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2541 } else {
2542 talloc_free(tmp_ctx);
2543 return ldb_operr(ldb);
2546 status = dsdb_get_extended_dn_sid(dns[i].dsdb_dn->dn,
2547 &tmp_sid, "SID");
2548 if (!NT_STATUS_IS_OK(status)) {
2549 struct GUID_txt_buf guid_str;
2550 ldb_asprintf_errstring(ldb,
2551 "NO SID PROVIDED... Attribute %s already "
2552 "exists for target GUID %s",
2553 el->name,
2554 GUID_buf_string(&exact->guid,
2555 &guid_str));
2556 talloc_free(tmp_ctx);
2557 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2560 ndr_err = ndr_push_struct_blob(&sid_blob, tmp_ctx, &tmp_sid,
2561 (ndr_push_flags_fn_t)ndr_push_dom_sid);
2562 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2563 talloc_free(tmp_ctx);
2564 return ldb_operr(ldb);
2567 ret = ldb_dn_set_extended_component(exact->dsdb_dn->dn, "SID", &sid_blob);
2568 data_blob_free(&sid_blob);
2569 if (ret != LDB_SUCCESS) {
2570 talloc_free(tmp_ctx);
2571 return ret;
2574 fixed_dnstring = dsdb_dn_get_extended_linearized(
2575 new_values, exact->dsdb_dn, 1);
2576 if (fixed_dnstring == NULL) {
2577 talloc_free(tmp_ctx);
2578 return ldb_operr(ldb);
2582 * We just replace the existing value...
2584 *exact->v = data_blob_string_const(fixed_dnstring);
2586 continue;
2589 if (exact != NULL) {
2591 * We are trying to add one that exists, which is only
2592 * allowed if it was previously deleted.
2594 * When we do undelete a link we change it in place.
2595 * It will be copied across into the right spot in due
2596 * course.
2598 uint32_t rmd_flags;
2599 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2601 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2602 struct GUID_txt_buf guid_str;
2603 ldb_asprintf_errstring(ldb,
2604 "Attribute %s already "
2605 "exists for target GUID %s",
2606 el->name,
2607 GUID_buf_string(&exact->guid,
2608 &guid_str));
2609 talloc_free(tmp_ctx);
2610 /* error codes for 'member' need to be
2611 special cased */
2612 if (ldb_attr_cmp(el->name, "member") == 0) {
2613 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2614 } else {
2615 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2619 ret = replmd_update_la_val(new_values, exact->v,
2620 dns[i].dsdb_dn,
2621 exact->dsdb_dn,
2622 &ac->our_invocation_id,
2623 ac->seq_num, ac->seq_num,
2624 now, false);
2625 if (ret != LDB_SUCCESS) {
2626 talloc_free(tmp_ctx);
2627 return ret;
2630 ret = replmd_add_backlink(module, replmd_private,
2631 ac->schema,
2632 msg_dn,
2633 &dns[i].guid,
2634 true,
2635 schema_attr,
2636 parent);
2637 if (ret != LDB_SUCCESS) {
2638 talloc_free(tmp_ctx);
2639 return ret;
2641 continue;
2644 * Here we don't have an exact match.
2646 * If next is NULL, this one goes beyond the end of the
2647 * existing list, so we need to add all of those ones first.
2649 * If next is not NULL, we need to add all the ones before
2650 * next.
2652 if (next == NULL) {
2653 offset = old_num_values;
2654 } else {
2655 /* next should have been parsed, but let's make sure */
2656 if (next->dsdb_dn == NULL) {
2657 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2658 schema_attr->syntax->ldap_oid);
2659 if (ret != LDB_SUCCESS) {
2660 return ret;
2663 offset = MIN(next - old_dns, old_num_values);
2666 /* put all the old ones before next on the list */
2667 for (; j < offset; j++) {
2668 new_values[num_values] = *old_dns[j].v;
2669 num_values++;
2672 ret = replmd_add_backlink(module, replmd_private,
2673 ac->schema, msg_dn,
2674 &dns[i].guid,
2675 true, schema_attr,
2676 parent);
2677 /* Make the new linked attribute ldb_val. */
2678 ret = replmd_build_la_val(new_values, &new_values[num_values],
2679 dns[i].dsdb_dn, &ac->our_invocation_id,
2680 ac->seq_num, now);
2681 if (ret != LDB_SUCCESS) {
2682 talloc_free(tmp_ctx);
2683 return ret;
2685 num_values++;
2686 if (ret != LDB_SUCCESS) {
2687 talloc_free(tmp_ctx);
2688 return ret;
2691 /* copy the rest of the old ones (if any) */
2692 for (; j < old_num_values; j++) {
2693 new_values[num_values] = *old_dns[j].v;
2694 num_values++;
2697 talloc_steal(msg->elements, new_values);
2698 if (old_el != NULL) {
2699 talloc_steal(msg->elements, old_el->values);
2701 el->values = new_values;
2702 el->num_values = num_values;
2704 talloc_free(tmp_ctx);
2706 /* we now tell the backend to replace all existing values
2707 with the one we have constructed */
2708 el->flags = LDB_FLAG_MOD_REPLACE;
2710 return LDB_SUCCESS;
2715 handle deleting all active linked attributes
2717 static int replmd_modify_la_delete(struct ldb_module *module,
2718 struct replmd_private *replmd_private,
2719 struct replmd_replicated_request *ac,
2720 struct ldb_message *msg,
2721 struct ldb_message_element *el,
2722 struct ldb_message_element *old_el,
2723 const struct dsdb_attribute *schema_attr,
2724 time_t t,
2725 struct ldb_dn *msg_dn,
2726 struct ldb_request *parent)
2728 unsigned int i;
2729 struct parsed_dn *dns, *old_dns;
2730 TALLOC_CTX *tmp_ctx = NULL;
2731 int ret;
2732 struct ldb_context *ldb = ldb_module_get_ctx(module);
2733 struct ldb_control *vanish_links_ctrl = NULL;
2734 bool vanish_links = false;
2735 unsigned int num_to_delete = el->num_values;
2736 uint32_t rmd_flags;
2737 NTTIME now;
2739 unix_to_nt_time(&now, t);
2741 if (old_el == NULL || old_el->num_values == 0) {
2742 /* there is nothing to delete... */
2743 if (num_to_delete == 0) {
2744 /* and we're deleting nothing, so that's OK */
2745 return LDB_SUCCESS;
2747 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2750 tmp_ctx = talloc_new(msg);
2751 if (tmp_ctx == NULL) {
2752 return LDB_ERR_OPERATIONS_ERROR;
2755 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2756 schema_attr->syntax->ldap_oid, parent);
2757 if (ret != LDB_SUCCESS) {
2758 talloc_free(tmp_ctx);
2759 return ret;
2762 ret = get_parsed_dns_trusted(module, replmd_private,
2763 tmp_ctx, old_el, &old_dns,
2764 schema_attr->syntax->ldap_oid, parent);
2766 if (ret != LDB_SUCCESS) {
2767 talloc_free(tmp_ctx);
2768 return ret;
2771 if (parent) {
2772 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2773 if (vanish_links_ctrl) {
2774 vanish_links = true;
2775 vanish_links_ctrl->critical = false;
2779 /* we empty out el->values here to avoid damage if we return early. */
2780 el->num_values = 0;
2781 el->values = NULL;
2784 * If vanish links is set, we are actually removing members of
2785 * old_el->values; otherwise we are just marking them deleted.
2787 * There is a special case when no values are given: we remove them
2788 * all. When we have the vanish_links control we just have to remove
2789 * the backlinks and change our element to replace the existing values
2790 * with the empty list.
2793 if (num_to_delete == 0) {
2794 for (i = 0; i < old_el->num_values; i++) {
2795 struct parsed_dn *p = &old_dns[i];
2796 if (p->dsdb_dn == NULL) {
2797 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2798 schema_attr->syntax->ldap_oid);
2799 if (ret != LDB_SUCCESS) {
2800 return ret;
2803 ret = replmd_add_backlink(module, replmd_private,
2804 ac->schema, msg_dn, &p->guid,
2805 false, schema_attr,
2806 parent);
2807 if (ret != LDB_SUCCESS) {
2808 talloc_free(tmp_ctx);
2809 return ret;
2811 if (vanish_links) {
2812 continue;
2815 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2816 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2817 continue;
2820 ret = replmd_update_la_val(old_el->values, p->v,
2821 p->dsdb_dn, p->dsdb_dn,
2822 &ac->our_invocation_id,
2823 ac->seq_num, ac->seq_num,
2824 now, true);
2825 if (ret != LDB_SUCCESS) {
2826 talloc_free(tmp_ctx);
2827 return ret;
2831 if (vanish_links) {
2832 el->flags = LDB_FLAG_MOD_REPLACE;
2833 talloc_free(tmp_ctx);
2834 return LDB_SUCCESS;
2839 for (i = 0; i < num_to_delete; i++) {
2840 struct parsed_dn *p = &dns[i];
2841 struct parsed_dn *exact = NULL;
2842 struct parsed_dn *next = NULL;
2843 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2844 &p->guid,
2845 NULL,
2846 p->dsdb_dn->extra_part, 0,
2847 &exact, &next,
2848 schema_attr->syntax->ldap_oid,
2849 true);
2850 if (ret != LDB_SUCCESS) {
2851 talloc_free(tmp_ctx);
2852 return ret;
2854 if (exact == NULL) {
2855 struct GUID_txt_buf buf;
2856 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2857 "exist for target GUID %s",
2858 el->name,
2859 GUID_buf_string(&p->guid, &buf));
2860 if (ldb_attr_cmp(el->name, "member") == 0) {
2861 talloc_free(tmp_ctx);
2862 return LDB_ERR_UNWILLING_TO_PERFORM;
2863 } else {
2864 talloc_free(tmp_ctx);
2865 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2869 if (vanish_links) {
2870 if (CHECK_DEBUGLVL(5)) {
2871 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2872 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2873 struct GUID_txt_buf buf;
2874 const char *guid_str = \
2875 GUID_buf_string(&p->guid, &buf);
2876 DEBUG(5, ("Deleting deleted linked "
2877 "attribute %s to %s, because "
2878 "vanish_links control is set\n",
2879 el->name, guid_str));
2883 /* remove the backlink */
2884 ret = replmd_add_backlink(module,
2885 replmd_private,
2886 ac->schema,
2887 msg_dn,
2888 &p->guid,
2889 false, schema_attr,
2890 parent);
2891 if (ret != LDB_SUCCESS) {
2892 talloc_free(tmp_ctx);
2893 return ret;
2896 /* We flag the deletion and tidy it up later. */
2897 exact->v = NULL;
2898 continue;
2901 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2903 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2904 struct GUID_txt_buf buf;
2905 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2906 ldb_asprintf_errstring(ldb, "Attribute %s already "
2907 "deleted for target GUID %s",
2908 el->name, guid_str);
2909 if (ldb_attr_cmp(el->name, "member") == 0) {
2910 talloc_free(tmp_ctx);
2911 return LDB_ERR_UNWILLING_TO_PERFORM;
2912 } else {
2913 talloc_free(tmp_ctx);
2914 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2918 ret = replmd_update_la_val(old_el->values, exact->v,
2919 exact->dsdb_dn, exact->dsdb_dn,
2920 &ac->our_invocation_id,
2921 ac->seq_num, ac->seq_num,
2922 now, true);
2923 if (ret != LDB_SUCCESS) {
2924 talloc_free(tmp_ctx);
2925 return ret;
2927 ret = replmd_add_backlink(module, replmd_private,
2928 ac->schema, msg_dn,
2929 &p->guid,
2930 false, schema_attr,
2931 parent);
2932 if (ret != LDB_SUCCESS) {
2933 talloc_free(tmp_ctx);
2934 return ret;
2938 if (vanish_links) {
2939 unsigned j = 0;
2940 struct ldb_val *tmp_vals = NULL;
2942 tmp_vals = talloc_array(tmp_ctx, struct ldb_val,
2943 old_el->num_values);
2944 if (tmp_vals == NULL) {
2945 talloc_free(tmp_ctx);
2946 return ldb_module_oom(module);
2948 for (i = 0; i < old_el->num_values; i++) {
2949 if (old_dns[i].v == NULL) {
2950 continue;
2952 tmp_vals[j] = *old_dns[i].v;
2953 j++;
2955 for (i = 0; i < j; i++) {
2956 old_el->values[i] = tmp_vals[i];
2958 old_el->num_values = j;
2961 el->values = talloc_steal(msg->elements, old_el->values);
2962 el->num_values = old_el->num_values;
2964 talloc_free(tmp_ctx);
2966 /* we now tell the backend to replace all existing values
2967 with the one we have constructed */
2968 el->flags = LDB_FLAG_MOD_REPLACE;
2970 return LDB_SUCCESS;
2974 handle replacing a linked attribute
2976 static int replmd_modify_la_replace(struct ldb_module *module,
2977 struct replmd_private *replmd_private,
2978 struct replmd_replicated_request *ac,
2979 struct ldb_message *msg,
2980 struct ldb_message_element *el,
2981 struct ldb_message_element *old_el,
2982 const struct dsdb_attribute *schema_attr,
2983 time_t t,
2984 struct ldb_dn *msg_dn,
2985 struct ldb_request *parent)
2987 unsigned int i, old_i, new_i;
2988 struct parsed_dn *dns, *old_dns;
2989 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2990 int ret;
2991 struct ldb_context *ldb = ldb_module_get_ctx(module);
2992 struct ldb_val *new_values = NULL;
2993 const char *ldap_oid = schema_attr->syntax->ldap_oid;
2994 unsigned int old_num_values;
2995 unsigned int repl_num_values;
2996 unsigned int max_num_values;
2997 NTTIME now;
2999 unix_to_nt_time(&now, t);
3002 * The replace operation is unlike the replace and delete cases in that
3003 * we need to look at every existing link to see whether it is being
3004 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
3006 * As we are trying to combine two sorted lists, the algorithm we use
3007 * is akin to the merge phase of a merge sort. We interleave the two
3008 * lists, doing different things depending on which side the current
3009 * item came from.
3011 * There are three main cases, with some sub-cases.
3013 * - a DN is in the old list but not the new one. It needs to be
3014 * marked as deleted (but left in the list).
3015 * - maybe it is already deleted, and we have less to do.
3017 * - a DN is in both lists. The old data gets replaced by the new,
3018 * and the list doesn't grow. The old link may have been marked as
3019 * deleted, in which case we undelete it.
3021 * - a DN is in the new list only. We add it in the right place.
3024 old_num_values = old_el ? old_el->num_values : 0;
3025 repl_num_values = el->num_values;
3026 max_num_values = old_num_values + repl_num_values;
3028 if (max_num_values == 0) {
3029 /* There is nothing to do! */
3030 return LDB_SUCCESS;
3033 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
3034 if (ret != LDB_SUCCESS) {
3035 talloc_free(tmp_ctx);
3036 return ret;
3039 ret = check_parsed_dn_duplicates(module, el, dns);
3040 if (ret != LDB_SUCCESS) {
3041 talloc_free(tmp_ctx);
3042 return ret;
3045 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
3046 ldap_oid, parent);
3047 if (ret != LDB_SUCCESS) {
3048 talloc_free(tmp_ctx);
3049 return ret;
3052 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
3053 old_el, ldap_oid);
3054 if (ret != LDB_SUCCESS) {
3055 talloc_free(tmp_ctx);
3056 return ret;
3059 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
3060 if (new_values == NULL) {
3061 ldb_module_oom(module);
3062 talloc_free(tmp_ctx);
3063 return LDB_ERR_OPERATIONS_ERROR;
3066 old_i = 0;
3067 new_i = 0;
3068 for (i = 0; i < max_num_values; i++) {
3069 int cmp;
3070 struct parsed_dn *old_p, *new_p;
3071 if (old_i < old_num_values && new_i < repl_num_values) {
3072 old_p = &old_dns[old_i];
3073 new_p = &dns[new_i];
3074 cmp = parsed_dn_compare(old_p, new_p);
3075 } else if (old_i < old_num_values) {
3076 /* the new list is empty, read the old list */
3077 old_p = &old_dns[old_i];
3078 new_p = NULL;
3079 cmp = -1;
3080 } else if (new_i < repl_num_values) {
3081 /* the old list is empty, read new list */
3082 old_p = NULL;
3083 new_p = &dns[new_i];
3084 cmp = 1;
3085 } else {
3086 break;
3089 if (cmp < 0) {
3091 * An old ones that come before the next replacement
3092 * (if any). We mark it as deleted and add it to the
3093 * final list.
3095 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3096 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
3097 ret = replmd_update_la_val(new_values, old_p->v,
3098 old_p->dsdb_dn,
3099 old_p->dsdb_dn,
3100 &ac->our_invocation_id,
3101 ac->seq_num, ac->seq_num,
3102 now, true);
3103 if (ret != LDB_SUCCESS) {
3104 talloc_free(tmp_ctx);
3105 return ret;
3108 ret = replmd_add_backlink(module, replmd_private,
3109 ac->schema,
3110 msg_dn,
3111 &old_p->guid, false,
3112 schema_attr,
3113 parent);
3114 if (ret != LDB_SUCCESS) {
3115 talloc_free(tmp_ctx);
3116 return ret;
3119 new_values[i] = *old_p->v;
3120 old_i++;
3121 } else if (cmp == 0) {
3123 * We are overwriting one. If it was previously
3124 * deleted, we need to add a backlink.
3126 * Note that if any RMD_FLAGs in an extended new DN
3127 * will be ignored.
3129 uint32_t rmd_flags;
3131 ret = replmd_update_la_val(new_values, old_p->v,
3132 new_p->dsdb_dn,
3133 old_p->dsdb_dn,
3134 &ac->our_invocation_id,
3135 ac->seq_num, ac->seq_num,
3136 now, false);
3137 if (ret != LDB_SUCCESS) {
3138 talloc_free(tmp_ctx);
3139 return ret;
3142 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3143 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3144 ret = replmd_add_backlink(module, replmd_private,
3145 ac->schema,
3146 msg_dn,
3147 &new_p->guid, true,
3148 schema_attr,
3149 parent);
3150 if (ret != LDB_SUCCESS) {
3151 talloc_free(tmp_ctx);
3152 return ret;
3156 new_values[i] = *old_p->v;
3157 old_i++;
3158 new_i++;
3159 } else {
3161 * Replacements that don't match an existing one. We
3162 * just add them to the final list.
3164 ret = replmd_build_la_val(new_values,
3165 new_p->v,
3166 new_p->dsdb_dn,
3167 &ac->our_invocation_id,
3168 ac->seq_num, now);
3169 if (ret != LDB_SUCCESS) {
3170 talloc_free(tmp_ctx);
3171 return ret;
3173 ret = replmd_add_backlink(module, replmd_private,
3174 ac->schema,
3175 msg_dn,
3176 &new_p->guid, true,
3177 schema_attr,
3178 parent);
3179 if (ret != LDB_SUCCESS) {
3180 talloc_free(tmp_ctx);
3181 return ret;
3183 new_values[i] = *new_p->v;
3184 new_i++;
3187 if (old_el != NULL) {
3188 talloc_steal(msg->elements, old_el->values);
3190 el->values = talloc_steal(msg->elements, new_values);
3191 el->num_values = i;
3192 talloc_free(tmp_ctx);
3194 el->flags = LDB_FLAG_MOD_REPLACE;
3196 return LDB_SUCCESS;
3201 handle linked attributes in modify requests
3203 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3204 struct replmd_private *replmd_private,
3205 struct replmd_replicated_request *ac,
3206 struct ldb_message *msg,
3207 time_t t,
3208 struct ldb_request *parent)
3210 struct ldb_result *res;
3211 unsigned int i;
3212 int ret;
3213 struct ldb_context *ldb = ldb_module_get_ctx(module);
3214 struct ldb_message *old_msg;
3216 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3218 * Nothing special is required for modifying or vanishing links
3219 * in fl2000 since they are just strings in a multi-valued
3220 * attribute.
3222 struct ldb_control *ctrl = ldb_request_get_control(parent,
3223 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3224 if (ctrl) {
3225 ctrl->critical = false;
3227 return LDB_SUCCESS;
3231 * TODO:
3233 * We should restrict this to the intersection of the list of
3234 * linked attributes in the schema and the list of attributes
3235 * being modified.
3237 * This will help performance a little, as otherwise we have
3238 * to allocate the entire object value-by-value.
3240 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3241 DSDB_FLAG_NEXT_MODULE |
3242 DSDB_SEARCH_SHOW_RECYCLED |
3243 DSDB_SEARCH_REVEAL_INTERNALS |
3244 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3245 parent);
3246 if (ret != LDB_SUCCESS) {
3247 return ret;
3250 old_msg = res->msgs[0];
3252 for (i=0; i<msg->num_elements; i++) {
3253 struct ldb_message_element *el = &msg->elements[i];
3254 struct ldb_message_element *old_el, *new_el;
3255 unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3256 const struct dsdb_attribute *schema_attr
3257 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
3258 if (!schema_attr) {
3259 ldb_asprintf_errstring(ldb,
3260 "%s: attribute %s is not a valid attribute in schema",
3261 __FUNCTION__, el->name);
3262 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3264 if (schema_attr->linkID == 0) {
3265 continue;
3267 if ((schema_attr->linkID & 1) == 1) {
3268 if (parent) {
3269 struct ldb_control *ctrl;
3271 ctrl = ldb_request_get_control(parent,
3272 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3273 if (ctrl != NULL) {
3274 ctrl->critical = false;
3275 continue;
3277 ctrl = ldb_request_get_control(parent,
3278 DSDB_CONTROL_DBCHECK);
3279 if (ctrl != NULL) {
3280 continue;
3284 /* Odd is for the target. Illegal to modify */
3285 ldb_asprintf_errstring(ldb,
3286 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3287 return LDB_ERR_UNWILLING_TO_PERFORM;
3289 old_el = ldb_msg_find_element(old_msg, el->name);
3290 switch (mod_type) {
3291 case LDB_FLAG_MOD_REPLACE:
3292 ret = replmd_modify_la_replace(module, replmd_private,
3293 ac, msg, el, old_el,
3294 schema_attr, t,
3295 old_msg->dn,
3296 parent);
3297 break;
3298 case LDB_FLAG_MOD_DELETE:
3299 ret = replmd_modify_la_delete(module, replmd_private,
3300 ac, msg, el, old_el,
3301 schema_attr, t,
3302 old_msg->dn,
3303 parent);
3304 break;
3305 case LDB_FLAG_MOD_ADD:
3306 ret = replmd_modify_la_add(module, replmd_private,
3307 ac, msg, el, old_el,
3308 schema_attr, t,
3309 old_msg->dn,
3310 parent);
3311 break;
3312 default:
3313 ldb_asprintf_errstring(ldb,
3314 "invalid flags 0x%x for %s linked attribute",
3315 el->flags, el->name);
3316 return LDB_ERR_UNWILLING_TO_PERFORM;
3318 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
3319 ldb_asprintf_errstring(ldb,
3320 "Attribute %s is single valued but more than one value has been supplied",
3321 el->name);
3322 /* Return codes as found on Windows 2012r2 */
3323 if (mod_type == LDB_FLAG_MOD_REPLACE) {
3324 return LDB_ERR_CONSTRAINT_VIOLATION;
3325 } else {
3326 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3328 } else {
3329 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3332 if (ret != LDB_SUCCESS) {
3333 return ret;
3335 if (old_el) {
3336 ldb_msg_remove_attr(old_msg, el->name);
3338 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3339 new_el->num_values = el->num_values;
3340 new_el->values = talloc_steal(msg->elements, el->values);
3342 /* TODO: this relises a bit too heavily on the exact
3343 behaviour of ldb_msg_find_element and
3344 ldb_msg_remove_element */
3345 old_el = ldb_msg_find_element(msg, el->name);
3346 if (old_el != el) {
3347 ldb_msg_remove_element(msg, old_el);
3348 i--;
3352 talloc_free(res);
3353 return ret;
3357 static int send_rodc_referral(struct ldb_request *req,
3358 struct ldb_context *ldb,
3359 struct ldb_dn *dn)
3361 char *referral = NULL;
3362 struct loadparm_context *lp_ctx = NULL;
3363 struct ldb_dn *fsmo_role_dn = NULL;
3364 struct ldb_dn *role_owner_dn = NULL;
3365 const char *domain = NULL;
3366 WERROR werr;
3368 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3369 struct loadparm_context);
3371 werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3372 &fsmo_role_dn, &role_owner_dn);
3374 if (W_ERROR_IS_OK(werr)) {
3375 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3376 if (server_dn != NULL) {
3377 ldb_dn_remove_child_components(server_dn, 1);
3378 domain = samdb_dn_to_dnshostname(ldb, req,
3379 server_dn);
3383 if (domain == NULL) {
3384 domain = lpcfg_dnsdomain(lp_ctx);
3387 referral = talloc_asprintf(req, "ldap://%s/%s",
3388 domain,
3389 ldb_dn_get_linearized(dn));
3390 if (referral == NULL) {
3391 ldb_oom(ldb);
3392 return LDB_ERR_OPERATIONS_ERROR;
3395 return ldb_module_send_referral(req, referral);
3399 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3401 struct ldb_context *ldb;
3402 struct replmd_replicated_request *ac;
3403 struct ldb_request *down_req;
3404 struct ldb_message *msg;
3405 time_t t = time(NULL);
3406 int ret;
3407 bool is_urgent = false, rodc = false;
3408 bool is_schema_nc = false;
3409 unsigned int functional_level;
3410 const struct ldb_message_element *guid_el = NULL;
3411 struct ldb_control *sd_propagation_control;
3412 struct ldb_control *fix_links_control = NULL;
3413 struct ldb_control *fix_dn_name_control = NULL;
3414 struct ldb_control *fix_dn_sid_control = NULL;
3415 struct replmd_private *replmd_private =
3416 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3418 /* do not manipulate our control entries */
3419 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3420 return ldb_next_request(module, req);
3423 sd_propagation_control = ldb_request_get_control(req,
3424 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3425 if (sd_propagation_control != NULL) {
3426 if (req->op.mod.message->num_elements != 1) {
3427 return ldb_module_operr(module);
3429 ret = strcmp(req->op.mod.message->elements[0].name,
3430 "nTSecurityDescriptor");
3431 if (ret != 0) {
3432 return ldb_module_operr(module);
3435 return ldb_next_request(module, req);
3438 ldb = ldb_module_get_ctx(module);
3440 fix_links_control = ldb_request_get_control(req,
3441 DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
3442 if (fix_links_control != NULL) {
3443 struct dsdb_schema *schema = NULL;
3444 const struct dsdb_attribute *sa = NULL;
3446 if (req->op.mod.message->num_elements != 1) {
3447 return ldb_module_operr(module);
3450 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_REPLACE) {
3451 return ldb_module_operr(module);
3454 schema = dsdb_get_schema(ldb, req);
3455 if (schema == NULL) {
3456 return ldb_module_operr(module);
3459 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3460 req->op.mod.message->elements[0].name);
3461 if (sa == NULL) {
3462 return ldb_module_operr(module);
3465 if (sa->linkID == 0) {
3466 return ldb_module_operr(module);
3469 fix_links_control->critical = false;
3470 return ldb_next_request(module, req);
3473 fix_dn_name_control = ldb_request_get_control(req,
3474 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3475 if (fix_dn_name_control != NULL) {
3476 struct dsdb_schema *schema = NULL;
3477 const struct dsdb_attribute *sa = NULL;
3479 if (req->op.mod.message->num_elements != 2) {
3480 return ldb_module_operr(module);
3483 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_DELETE) {
3484 return ldb_module_operr(module);
3487 if (req->op.mod.message->elements[1].flags != LDB_FLAG_MOD_ADD) {
3488 return ldb_module_operr(module);
3491 if (req->op.mod.message->elements[0].num_values != 1) {
3492 return ldb_module_operr(module);
3495 if (req->op.mod.message->elements[1].num_values != 1) {
3496 return ldb_module_operr(module);
3499 schema = dsdb_get_schema(ldb, req);
3500 if (schema == NULL) {
3501 return ldb_module_operr(module);
3504 if (ldb_attr_cmp(req->op.mod.message->elements[0].name,
3505 req->op.mod.message->elements[1].name) != 0) {
3506 return ldb_module_operr(module);
3509 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3510 req->op.mod.message->elements[0].name);
3511 if (sa == NULL) {
3512 return ldb_module_operr(module);
3515 if (sa->dn_format == DSDB_INVALID_DN) {
3516 return ldb_module_operr(module);
3519 if (sa->linkID != 0) {
3520 return ldb_module_operr(module);
3524 * If we are run from dbcheck and we are not updating
3525 * a link (as these would need to be sorted and so
3526 * can't go via such a simple update, then do not
3527 * trigger replicated updates and a new USN from this
3528 * change, it wasn't a real change, just a new
3529 * (correct) string DN
3532 fix_dn_name_control->critical = false;
3533 return ldb_next_request(module, req);
3536 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3538 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3539 if (guid_el != NULL) {
3540 ldb_set_errstring(ldb,
3541 "replmd_modify: it's not allowed to change the objectGUID!");
3542 return LDB_ERR_CONSTRAINT_VIOLATION;
3545 ac = replmd_ctx_init(module, req);
3546 if (ac == NULL) {
3547 return ldb_module_oom(module);
3550 functional_level = dsdb_functional_level(ldb);
3552 /* we have to copy the message as the caller might have it as a const */
3553 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3554 if (msg == NULL) {
3555 ldb_oom(ldb);
3556 talloc_free(ac);
3557 return LDB_ERR_OPERATIONS_ERROR;
3560 fix_dn_sid_control = ldb_request_get_control(req,
3561 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
3562 if (fix_dn_sid_control != NULL) {
3563 const struct dsdb_attribute *sa = NULL;
3565 if (msg->num_elements != 1) {
3566 talloc_free(ac);
3567 return ldb_module_operr(module);
3570 if (msg->elements[0].flags != LDB_FLAG_MOD_ADD) {
3571 talloc_free(ac);
3572 return ldb_module_operr(module);
3575 if (msg->elements[0].num_values != 1) {
3576 talloc_free(ac);
3577 return ldb_module_operr(module);
3580 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema,
3581 msg->elements[0].name);
3582 if (sa == NULL) {
3583 talloc_free(ac);
3584 return ldb_module_operr(module);
3587 if (sa->dn_format != DSDB_NORMAL_DN) {
3588 talloc_free(ac);
3589 return ldb_module_operr(module);
3592 fix_dn_sid_control->critical = false;
3593 ac->fix_link_sid = true;
3595 goto handle_linked_attribs;
3598 ldb_msg_remove_attr(msg, "whenChanged");
3599 ldb_msg_remove_attr(msg, "uSNChanged");
3601 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3603 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3604 msg, &ac->seq_num, t, is_schema_nc,
3605 &is_urgent, &rodc);
3606 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3607 ret = send_rodc_referral(req, ldb, msg->dn);
3608 talloc_free(ac);
3609 return ret;
3613 if (ret != LDB_SUCCESS) {
3614 talloc_free(ac);
3615 return ret;
3618 handle_linked_attribs:
3619 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3620 ac, msg, t, req);
3621 if (ret != LDB_SUCCESS) {
3622 talloc_free(ac);
3623 return ret;
3626 /* TODO:
3627 * - replace the old object with the newly constructed one
3630 ac->is_urgent = is_urgent;
3632 ret = ldb_build_mod_req(&down_req, ldb, ac,
3633 msg,
3634 req->controls,
3635 ac, replmd_op_callback,
3636 req);
3637 LDB_REQ_SET_LOCATION(down_req);
3638 if (ret != LDB_SUCCESS) {
3639 talloc_free(ac);
3640 return ret;
3643 /* current partition control is needed by "replmd_op_callback" */
3644 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3645 ret = ldb_request_add_control(down_req,
3646 DSDB_CONTROL_CURRENT_PARTITION_OID,
3647 false, NULL);
3648 if (ret != LDB_SUCCESS) {
3649 talloc_free(ac);
3650 return ret;
3654 /* If we are in functional level 2000, then
3655 * replmd_modify_handle_linked_attribs will have done
3656 * nothing */
3657 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3658 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3659 if (ret != LDB_SUCCESS) {
3660 talloc_free(ac);
3661 return ret;
3665 talloc_steal(down_req, msg);
3667 /* we only change whenChanged and uSNChanged if the seq_num
3668 has changed */
3669 if (ac->seq_num != 0) {
3670 ret = add_time_element(msg, "whenChanged", t);
3671 if (ret != LDB_SUCCESS) {
3672 talloc_free(ac);
3673 ldb_operr(ldb);
3674 return ret;
3677 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3678 if (ret != LDB_SUCCESS) {
3679 talloc_free(ac);
3680 ldb_operr(ldb);
3681 return ret;
3685 /* go on with the call chain */
3686 return ldb_next_request(module, down_req);
3689 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3692 handle a rename request
3694 On a rename we need to do an extra ldb_modify which sets the
3695 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3697 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3699 struct ldb_context *ldb;
3700 struct replmd_replicated_request *ac;
3701 int ret;
3702 struct ldb_request *down_req;
3704 /* do not manipulate our control entries */
3705 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3706 return ldb_next_request(module, req);
3709 ldb = ldb_module_get_ctx(module);
3711 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3713 ac = replmd_ctx_init(module, req);
3714 if (ac == NULL) {
3715 return ldb_module_oom(module);
3718 ret = ldb_build_rename_req(&down_req, ldb, ac,
3719 ac->req->op.rename.olddn,
3720 ac->req->op.rename.newdn,
3721 ac->req->controls,
3722 ac, replmd_rename_callback,
3723 ac->req);
3724 LDB_REQ_SET_LOCATION(down_req);
3725 if (ret != LDB_SUCCESS) {
3726 talloc_free(ac);
3727 return ret;
3730 /* go on with the call chain */
3731 return ldb_next_request(module, down_req);
3734 /* After the rename is compleated, update the whenchanged etc */
3735 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3737 struct ldb_context *ldb;
3738 struct ldb_request *down_req;
3739 struct ldb_message *msg;
3740 const struct dsdb_attribute *rdn_attr;
3741 const char *rdn_name;
3742 const struct ldb_val *rdn_val;
3743 const char *attrs[5] = { NULL, };
3744 time_t t = time(NULL);
3745 int ret;
3746 bool is_urgent = false, rodc = false;
3747 bool is_schema_nc;
3748 struct replmd_replicated_request *ac =
3749 talloc_get_type(req->context, struct replmd_replicated_request);
3750 struct replmd_private *replmd_private =
3751 talloc_get_type(ldb_module_get_private(ac->module),
3752 struct replmd_private);
3754 ldb = ldb_module_get_ctx(ac->module);
3756 if (ares->error != LDB_SUCCESS) {
3757 return ldb_module_done(ac->req, ares->controls,
3758 ares->response, ares->error);
3761 if (ares->type != LDB_REPLY_DONE) {
3762 ldb_set_errstring(ldb,
3763 "invalid ldb_reply_type in callback");
3764 talloc_free(ares);
3765 return ldb_module_done(ac->req, NULL, NULL,
3766 LDB_ERR_OPERATIONS_ERROR);
3769 /* TODO:
3770 * - replace the old object with the newly constructed one
3773 msg = ldb_msg_new(ac);
3774 if (msg == NULL) {
3775 ldb_oom(ldb);
3776 return LDB_ERR_OPERATIONS_ERROR;
3779 msg->dn = ac->req->op.rename.newdn;
3781 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3783 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3784 if (rdn_name == NULL) {
3785 talloc_free(ares);
3786 return ldb_module_done(ac->req, NULL, NULL,
3787 ldb_operr(ldb));
3790 /* normalize the rdn attribute name */
3791 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3792 if (rdn_attr == NULL) {
3793 talloc_free(ares);
3794 return ldb_module_done(ac->req, NULL, NULL,
3795 ldb_operr(ldb));
3797 rdn_name = rdn_attr->lDAPDisplayName;
3799 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3800 if (rdn_val == NULL) {
3801 talloc_free(ares);
3802 return ldb_module_done(ac->req, NULL, NULL,
3803 ldb_operr(ldb));
3806 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3807 talloc_free(ares);
3808 return ldb_module_done(ac->req, NULL, NULL,
3809 ldb_oom(ldb));
3811 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3812 talloc_free(ares);
3813 return ldb_module_done(ac->req, NULL, NULL,
3814 ldb_oom(ldb));
3816 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3817 talloc_free(ares);
3818 return ldb_module_done(ac->req, NULL, NULL,
3819 ldb_oom(ldb));
3821 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3822 talloc_free(ares);
3823 return ldb_module_done(ac->req, NULL, NULL,
3824 ldb_oom(ldb));
3828 * here we let replmd_update_rpmd() only search for
3829 * the existing "replPropertyMetaData" and rdn_name attributes.
3831 * We do not want the existing "name" attribute as
3832 * the "name" attribute needs to get the version
3833 * updated on rename even if the rdn value hasn't changed.
3835 * This is the diff of the meta data, for a moved user
3836 * on a w2k8r2 server:
3838 * # record 1
3839 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3840 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3841 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3842 * version : 0x00000001 (1)
3843 * reserved : 0x00000000 (0)
3844 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3845 * local_usn : 0x00000000000037a5 (14245)
3846 * array: struct replPropertyMetaData1
3847 * attid : DRSUAPI_ATTID_name (0x90001)
3848 * - version : 0x00000001 (1)
3849 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3850 * + version : 0x00000002 (2)
3851 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3852 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3853 * - originating_usn : 0x00000000000037a5 (14245)
3854 * - local_usn : 0x00000000000037a5 (14245)
3855 * + originating_usn : 0x0000000000003834 (14388)
3856 * + local_usn : 0x0000000000003834 (14388)
3857 * array: struct replPropertyMetaData1
3858 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3859 * version : 0x00000004 (4)
3861 attrs[0] = "replPropertyMetaData";
3862 attrs[1] = "objectClass";
3863 attrs[2] = "instanceType";
3864 attrs[3] = rdn_name;
3865 attrs[4] = NULL;
3867 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3868 msg, &ac->seq_num, t,
3869 is_schema_nc, &is_urgent, &rodc);
3870 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3871 ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
3872 talloc_free(ares);
3873 return ldb_module_done(req, NULL, NULL, ret);
3876 if (ret != LDB_SUCCESS) {
3877 talloc_free(ares);
3878 return ldb_module_done(ac->req, NULL, NULL, ret);
3881 if (ac->seq_num == 0) {
3882 talloc_free(ares);
3883 return ldb_module_done(ac->req, NULL, NULL,
3884 ldb_error(ldb, ret,
3885 "internal error seq_num == 0"));
3887 ac->is_urgent = is_urgent;
3889 ret = ldb_build_mod_req(&down_req, ldb, ac,
3890 msg,
3891 req->controls,
3892 ac, replmd_op_callback,
3893 req);
3894 LDB_REQ_SET_LOCATION(down_req);
3895 if (ret != LDB_SUCCESS) {
3896 talloc_free(ac);
3897 return ret;
3900 /* current partition control is needed by "replmd_op_callback" */
3901 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3902 ret = ldb_request_add_control(down_req,
3903 DSDB_CONTROL_CURRENT_PARTITION_OID,
3904 false, NULL);
3905 if (ret != LDB_SUCCESS) {
3906 talloc_free(ac);
3907 return ret;
3911 talloc_steal(down_req, msg);
3913 ret = add_time_element(msg, "whenChanged", t);
3914 if (ret != LDB_SUCCESS) {
3915 talloc_free(ac);
3916 ldb_operr(ldb);
3917 return ret;
3920 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3921 if (ret != LDB_SUCCESS) {
3922 talloc_free(ac);
3923 ldb_operr(ldb);
3924 return ret;
3927 /* go on with the call chain - do the modify after the rename */
3928 return ldb_next_request(ac->module, down_req);
3932 * remove links from objects that point at this object when an object
3933 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3934 * RemoveObj which states that link removal due to the object being
3935 * deleted is NOT an originating update - they just go away!
3938 static int replmd_delete_remove_link(struct ldb_module *module,
3939 const struct dsdb_schema *schema,
3940 struct replmd_private *replmd_private,
3941 struct ldb_dn *dn,
3942 struct GUID *guid,
3943 struct ldb_message_element *el,
3944 const struct dsdb_attribute *sa,
3945 struct ldb_request *parent)
3947 unsigned int i;
3948 TALLOC_CTX *tmp_ctx = talloc_new(module);
3949 struct ldb_context *ldb = ldb_module_get_ctx(module);
3951 for (i=0; i<el->num_values; i++) {
3952 struct dsdb_dn *dsdb_dn;
3953 int ret;
3954 struct ldb_message *msg;
3955 const struct dsdb_attribute *target_attr;
3956 struct ldb_message_element *el2;
3957 const char *dn_str;
3958 struct ldb_val dn_val;
3959 uint32_t dsdb_flags = 0;
3960 const char *attrs[] = { NULL, NULL };
3961 struct ldb_result *link_res;
3962 struct ldb_message *link_msg;
3963 struct ldb_message_element *link_el;
3964 struct parsed_dn *link_dns;
3965 struct parsed_dn *p = NULL, *unused = NULL;
3967 if (dsdb_dn_is_deleted_val(&el->values[i])) {
3968 continue;
3971 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
3972 if (!dsdb_dn) {
3973 talloc_free(tmp_ctx);
3974 return LDB_ERR_OPERATIONS_ERROR;
3977 /* remove the link */
3978 msg = ldb_msg_new(tmp_ctx);
3979 if (!msg) {
3980 ldb_module_oom(module);
3981 talloc_free(tmp_ctx);
3982 return LDB_ERR_OPERATIONS_ERROR;
3986 msg->dn = dsdb_dn->dn;
3988 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
3989 if (target_attr == NULL) {
3990 continue;
3992 attrs[0] = target_attr->lDAPDisplayName;
3994 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
3995 LDB_FLAG_MOD_DELETE, &el2);
3996 if (ret != LDB_SUCCESS) {
3997 ldb_module_oom(module);
3998 talloc_free(tmp_ctx);
3999 return LDB_ERR_OPERATIONS_ERROR;
4002 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
4003 msg->dn, attrs,
4004 DSDB_FLAG_NEXT_MODULE |
4005 DSDB_SEARCH_SHOW_EXTENDED_DN |
4006 DSDB_SEARCH_SHOW_RECYCLED,
4007 parent);
4009 if (ret != LDB_SUCCESS) {
4010 talloc_free(tmp_ctx);
4011 return ret;
4014 link_msg = link_res->msgs[0];
4015 link_el = ldb_msg_find_element(link_msg,
4016 target_attr->lDAPDisplayName);
4017 if (link_el == NULL) {
4018 talloc_free(tmp_ctx);
4019 return LDB_ERR_NO_SUCH_ATTRIBUTE;
4023 * This call 'upgrades' the links in link_dns, but we
4024 * do not commit the result back into the database, so
4025 * this is safe to call in FL2000 or on databases that
4026 * have been run at that level in the past.
4028 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx,
4029 link_el, &link_dns,
4030 target_attr->syntax->ldap_oid, parent);
4031 if (ret != LDB_SUCCESS) {
4032 talloc_free(tmp_ctx);
4033 return ret;
4036 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
4037 guid, dn,
4038 data_blob_null, 0,
4039 &p, &unused,
4040 target_attr->syntax->ldap_oid, false);
4041 if (ret != LDB_SUCCESS) {
4042 talloc_free(tmp_ctx);
4043 return ret;
4046 if (p == NULL) {
4047 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4048 "Failed to find forward link on %s "
4049 "as %s to remove backlink %s on %s",
4050 ldb_dn_get_linearized(msg->dn),
4051 target_attr->lDAPDisplayName,
4052 sa->lDAPDisplayName,
4053 ldb_dn_get_linearized(dn));
4054 talloc_free(tmp_ctx);
4055 return LDB_ERR_NO_SUCH_ATTRIBUTE;
4059 /* This needs to get the Binary DN, by first searching */
4060 dn_str = dsdb_dn_get_linearized(tmp_ctx,
4061 p->dsdb_dn);
4063 dn_val = data_blob_string_const(dn_str);
4064 el2->values = &dn_val;
4065 el2->num_values = 1;
4068 * Ensure that we tell the modification to vanish any linked
4069 * attributes (not simply mark them as isDeleted = TRUE)
4071 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4073 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
4074 if (ret != LDB_SUCCESS) {
4075 talloc_free(tmp_ctx);
4076 return ret;
4079 talloc_free(tmp_ctx);
4080 return LDB_SUCCESS;
4085 handle update of replication meta data for deletion of objects
4087 This also handles the mapping of delete to a rename operation
4088 to allow deletes to be replicated.
4090 It also handles the incoming deleted objects, to ensure they are
4091 fully deleted here. In that case re_delete is true, and we do not
4092 use this as a signal to change the deleted state, just reinforce it.
4095 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
4097 int ret = LDB_ERR_OTHER;
4098 bool retb, disallow_move_on_delete;
4099 struct ldb_dn *old_dn = NULL, *new_dn = NULL;
4100 const char *rdn_name;
4101 const struct ldb_val *rdn_value, *new_rdn_value;
4102 struct GUID guid;
4103 struct ldb_context *ldb = ldb_module_get_ctx(module);
4104 const struct dsdb_schema *schema;
4105 struct ldb_message *msg, *old_msg;
4106 struct ldb_message_element *el;
4107 TALLOC_CTX *tmp_ctx;
4108 struct ldb_result *res, *parent_res;
4109 static const char * const preserved_attrs[] = {
4110 /* yes, this really is a hard coded list. See MS-ADTS
4111 section 3.1.1.5.5.1.1 */
4112 "attributeID",
4113 "attributeSyntax",
4114 "dNReferenceUpdate",
4115 "dNSHostName",
4116 "flatName",
4117 "governsID",
4118 "groupType",
4119 "instanceType",
4120 "lDAPDisplayName",
4121 "legacyExchangeDN",
4122 "isDeleted",
4123 "isRecycled",
4124 "lastKnownParent",
4125 "msDS-LastKnownRDN",
4126 "msDS-PortLDAP",
4127 "mS-DS-CreatorSID",
4128 "mSMQOwnerID",
4129 "nCName",
4130 "objectClass",
4131 "distinguishedName",
4132 "objectGUID",
4133 "objectSid",
4134 "oMSyntax",
4135 "proxiedObjectName",
4136 "name",
4137 "nTSecurityDescriptor",
4138 "replPropertyMetaData",
4139 "sAMAccountName",
4140 "securityIdentifier",
4141 "sIDHistory",
4142 "subClassOf",
4143 "systemFlags",
4144 "trustPartner",
4145 "trustDirection",
4146 "trustType",
4147 "trustAttributes",
4148 "userAccountControl",
4149 "uSNChanged",
4150 "uSNCreated",
4151 "whenCreated",
4152 "whenChanged",
4153 NULL
4155 static const char * const all_attrs[] = {
4156 DSDB_SECRET_ATTRIBUTES,
4157 "*",
4158 NULL
4160 unsigned int i, el_count = 0;
4161 uint32_t dsdb_flags = 0;
4162 struct replmd_private *replmd_private;
4163 enum deletion_state deletion_state, next_deletion_state;
4165 if (ldb_dn_is_special(req->op.del.dn)) {
4166 return ldb_next_request(module, req);
4170 * We have to allow dbcheck to remove an object that
4171 * is beyond repair, and to do so totally. This could
4172 * mean we we can get a partial object from the other
4173 * DC, causing havoc, so dbcheck suggests
4174 * re-replication first. dbcheck sets both DBCHECK
4175 * and RELAX in this situation.
4177 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
4178 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
4179 /* really, really remove it */
4180 return ldb_next_request(module, req);
4183 tmp_ctx = talloc_new(ldb);
4184 if (!tmp_ctx) {
4185 ldb_oom(ldb);
4186 return LDB_ERR_OPERATIONS_ERROR;
4189 schema = dsdb_get_schema(ldb, tmp_ctx);
4190 if (!schema) {
4191 talloc_free(tmp_ctx);
4192 return LDB_ERR_OPERATIONS_ERROR;
4195 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
4197 /* we need the complete msg off disk, so we can work out which
4198 attributes need to be removed */
4199 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
4200 DSDB_FLAG_NEXT_MODULE |
4201 DSDB_SEARCH_SHOW_RECYCLED |
4202 DSDB_SEARCH_REVEAL_INTERNALS |
4203 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
4204 if (ret != LDB_SUCCESS) {
4205 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4206 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
4207 re_delete ? "re-delete" : "delete",
4208 ldb_dn_get_linearized(old_dn),
4209 ldb_errstring(ldb_module_get_ctx(module)));
4210 talloc_free(tmp_ctx);
4211 return ret;
4213 old_msg = res->msgs[0];
4215 replmd_deletion_state(module, old_msg,
4216 &deletion_state,
4217 &next_deletion_state);
4219 /* This supports us noticing an incoming isDeleted and acting on it */
4220 if (re_delete) {
4221 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
4222 next_deletion_state = deletion_state;
4225 if (next_deletion_state == OBJECT_REMOVED) {
4227 * We have to prevent objects being deleted, even if
4228 * the administrator really wants them gone, as
4229 * without the tombstone, we can get a partial object
4230 * from the other DC, causing havoc.
4232 * The only other valid case is when the 180 day
4233 * timeout has expired, when relax is specified.
4235 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
4236 /* it is already deleted - really remove it this time */
4237 talloc_free(tmp_ctx);
4238 return ldb_next_request(module, req);
4241 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
4242 "This check is to prevent corruption of the replicated state.",
4243 ldb_dn_get_linearized(old_msg->dn));
4244 return LDB_ERR_UNWILLING_TO_PERFORM;
4247 rdn_name = ldb_dn_get_rdn_name(old_dn);
4248 rdn_value = ldb_dn_get_rdn_val(old_dn);
4249 if ((rdn_name == NULL) || (rdn_value == NULL)) {
4250 talloc_free(tmp_ctx);
4251 return ldb_operr(ldb);
4254 msg = ldb_msg_new(tmp_ctx);
4255 if (msg == NULL) {
4256 ldb_module_oom(module);
4257 talloc_free(tmp_ctx);
4258 return LDB_ERR_OPERATIONS_ERROR;
4261 msg->dn = old_dn;
4263 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4264 disallow_move_on_delete =
4265 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4266 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4268 /* work out where we will be renaming this object to */
4269 if (!disallow_move_on_delete) {
4270 struct ldb_dn *deleted_objects_dn;
4271 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4272 &deleted_objects_dn);
4275 * We should not move objects if we can't find the
4276 * deleted objects DN. Not moving (or otherwise
4277 * harming) the Deleted Objects DN itself is handled
4278 * in the caller.
4280 if (re_delete && (ret != LDB_SUCCESS)) {
4281 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4282 if (new_dn == NULL) {
4283 ldb_module_oom(module);
4284 talloc_free(tmp_ctx);
4285 return LDB_ERR_OPERATIONS_ERROR;
4287 } else if (ret != LDB_SUCCESS) {
4288 /* this is probably an attempted delete on a partition
4289 * that doesn't allow delete operations, such as the
4290 * schema partition */
4291 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4292 ldb_dn_get_linearized(old_dn));
4293 talloc_free(tmp_ctx);
4294 return LDB_ERR_UNWILLING_TO_PERFORM;
4295 } else {
4296 new_dn = deleted_objects_dn;
4298 } else {
4299 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4300 if (new_dn == NULL) {
4301 ldb_module_oom(module);
4302 talloc_free(tmp_ctx);
4303 return LDB_ERR_OPERATIONS_ERROR;
4307 /* get the objects GUID from the search we just did */
4308 guid = samdb_result_guid(old_msg, "objectGUID");
4310 if (deletion_state == OBJECT_NOT_DELETED) {
4312 ret = replmd_make_deleted_child_dn(tmp_ctx,
4313 ldb,
4314 new_dn,
4315 rdn_name, rdn_value,
4316 guid);
4318 if (ret != LDB_SUCCESS) {
4319 talloc_free(tmp_ctx);
4320 return ret;
4323 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
4324 if (ret != LDB_SUCCESS) {
4325 ldb_asprintf_errstring(ldb, __location__
4326 ": Failed to add isDeleted string to the msg");
4327 talloc_free(tmp_ctx);
4328 return ret;
4330 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4331 } else {
4333 * No matter what has happened with other renames etc, try again to
4334 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4337 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4338 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4339 if (!retb) {
4340 ldb_asprintf_errstring(ldb, __location__
4341 ": Unable to add a prepare rdn of %s",
4342 ldb_dn_get_linearized(rdn));
4343 talloc_free(tmp_ctx);
4344 return LDB_ERR_OPERATIONS_ERROR;
4346 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4348 retb = ldb_dn_add_child(new_dn, rdn);
4349 if (!retb) {
4350 ldb_asprintf_errstring(ldb, __location__
4351 ": Unable to add rdn %s to base dn: %s",
4352 ldb_dn_get_linearized(rdn),
4353 ldb_dn_get_linearized(new_dn));
4354 talloc_free(tmp_ctx);
4355 return LDB_ERR_OPERATIONS_ERROR;
4360 now we need to modify the object in the following ways:
4362 - add isDeleted=TRUE
4363 - update rDN and name, with new rDN
4364 - remove linked attributes
4365 - remove objectCategory and sAMAccountType
4366 - remove attribs not on the preserved list
4367 - preserved if in above list, or is rDN
4368 - remove all linked attribs from this object
4369 - remove all links from other objects to this object
4370 - add lastKnownParent
4371 - update replPropertyMetaData?
4373 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4376 if (deletion_state == OBJECT_NOT_DELETED) {
4377 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4378 char *parent_dn_str = NULL;
4380 /* we need the storage form of the parent GUID */
4381 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4382 parent_dn, NULL,
4383 DSDB_FLAG_NEXT_MODULE |
4384 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4385 DSDB_SEARCH_REVEAL_INTERNALS|
4386 DSDB_SEARCH_SHOW_RECYCLED, req);
4387 if (ret != LDB_SUCCESS) {
4388 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4389 "repmd_delete: Failed to %s %s, "
4390 "because we failed to find it's parent (%s): %s",
4391 re_delete ? "re-delete" : "delete",
4392 ldb_dn_get_linearized(old_dn),
4393 ldb_dn_get_linearized(parent_dn),
4394 ldb_errstring(ldb_module_get_ctx(module)));
4395 talloc_free(tmp_ctx);
4396 return ret;
4400 * Now we can use the DB version,
4401 * it will have the extended DN info in it
4403 parent_dn = parent_res->msgs[0]->dn;
4404 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4405 parent_dn,
4407 if (parent_dn_str == NULL) {
4408 talloc_free(tmp_ctx);
4409 return ldb_module_oom(module);
4412 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4413 parent_dn_str);
4414 if (ret != LDB_SUCCESS) {
4415 ldb_asprintf_errstring(ldb, __location__
4416 ": Failed to add lastKnownParent "
4417 "string when deleting %s",
4418 ldb_dn_get_linearized(old_dn));
4419 talloc_free(tmp_ctx);
4420 return ret;
4422 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4424 if (next_deletion_state == OBJECT_DELETED) {
4425 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4426 if (ret != LDB_SUCCESS) {
4427 ldb_asprintf_errstring(ldb, __location__
4428 ": Failed to add msDS-LastKnownRDN "
4429 "string when deleting %s",
4430 ldb_dn_get_linearized(old_dn));
4431 talloc_free(tmp_ctx);
4432 return ret;
4434 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
4438 switch (next_deletion_state) {
4440 case OBJECT_RECYCLED:
4441 case OBJECT_TOMBSTONE:
4444 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4445 * describes what must be removed from a tombstone
4446 * object
4448 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4449 * describes what must be removed from a recycled
4450 * object
4455 * we also mark it as recycled, meaning this object can't be
4456 * recovered (we are stripping its attributes).
4457 * This is done only if we have this schema object of course ...
4458 * This behavior is identical to the one of Windows 2008R2 which
4459 * always set the isRecycled attribute, even if the recycle-bin is
4460 * not activated and what ever the forest level is.
4462 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4463 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
4464 if (ret != LDB_SUCCESS) {
4465 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4466 ldb_module_oom(module);
4467 talloc_free(tmp_ctx);
4468 return ret;
4470 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4473 replmd_private = talloc_get_type(ldb_module_get_private(module),
4474 struct replmd_private);
4475 /* work out which of the old attributes we will be removing */
4476 for (i=0; i<old_msg->num_elements; i++) {
4477 const struct dsdb_attribute *sa;
4478 el = &old_msg->elements[i];
4479 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4480 if (!sa) {
4481 talloc_free(tmp_ctx);
4482 return LDB_ERR_OPERATIONS_ERROR;
4484 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4485 /* don't remove the rDN */
4486 continue;
4489 if (sa->linkID & 1) {
4491 we have a backlink in this object
4492 that needs to be removed. We're not
4493 allowed to remove it directly
4494 however, so we instead setup a
4495 modify to delete the corresponding
4496 forward link
4498 ret = replmd_delete_remove_link(module, schema,
4499 replmd_private,
4500 old_dn, &guid,
4501 el, sa, req);
4502 if (ret == LDB_SUCCESS) {
4504 * now we continue, which means we
4505 * won't remove this backlink
4506 * directly
4508 continue;
4511 if (ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
4512 const char *old_dn_str
4513 = ldb_dn_get_linearized(old_dn);
4514 ldb_asprintf_errstring(ldb,
4515 __location__
4516 ": Failed to remove backlink of "
4517 "%s when deleting %s: %s",
4518 el->name,
4519 old_dn_str,
4520 ldb_errstring(ldb));
4521 talloc_free(tmp_ctx);
4522 return LDB_ERR_OPERATIONS_ERROR;
4526 * Otherwise vanish the link, we are
4527 * out of sync and the controlling
4528 * object does not have the source
4529 * link any more
4532 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4534 } else if (sa->linkID == 0) {
4535 if (ldb_attr_in_list(preserved_attrs, el->name)) {
4536 continue;
4538 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4539 continue;
4541 } else {
4543 * Ensure that we tell the modification to vanish any linked
4544 * attributes (not simply mark them as isDeleted = TRUE)
4546 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4548 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4549 if (ret != LDB_SUCCESS) {
4550 talloc_free(tmp_ctx);
4551 ldb_module_oom(module);
4552 return ret;
4556 break;
4558 case OBJECT_DELETED:
4560 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4561 * describes what must be removed from a deleted
4562 * object
4565 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4566 if (ret != LDB_SUCCESS) {
4567 talloc_free(tmp_ctx);
4568 ldb_module_oom(module);
4569 return ret;
4572 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4573 if (ret != LDB_SUCCESS) {
4574 talloc_free(tmp_ctx);
4575 ldb_module_oom(module);
4576 return ret;
4579 break;
4581 default:
4582 break;
4585 if (deletion_state == OBJECT_NOT_DELETED) {
4586 const struct dsdb_attribute *sa;
4588 /* work out what the new rdn value is, for updating the
4589 rDN and name fields */
4590 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4591 if (new_rdn_value == NULL) {
4592 talloc_free(tmp_ctx);
4593 return ldb_operr(ldb);
4596 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4597 if (!sa) {
4598 talloc_free(tmp_ctx);
4599 return LDB_ERR_OPERATIONS_ERROR;
4602 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4603 &el);
4604 if (ret != LDB_SUCCESS) {
4605 talloc_free(tmp_ctx);
4606 return ret;
4608 el->flags = LDB_FLAG_MOD_REPLACE;
4610 el = ldb_msg_find_element(old_msg, "name");
4611 if (el) {
4612 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4613 if (ret != LDB_SUCCESS) {
4614 talloc_free(tmp_ctx);
4615 return ret;
4617 el->flags = LDB_FLAG_MOD_REPLACE;
4622 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4627 * No matter what has happned with other renames, try again to
4628 * get this to be under the deleted DN.
4630 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4631 /* now rename onto the new DN */
4632 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4633 if (ret != LDB_SUCCESS){
4634 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4635 ldb_dn_get_linearized(old_dn),
4636 ldb_dn_get_linearized(new_dn),
4637 ldb_errstring(ldb)));
4638 talloc_free(tmp_ctx);
4639 return ret;
4641 msg->dn = new_dn;
4644 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4645 if (ret != LDB_SUCCESS) {
4646 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
4647 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
4648 talloc_free(tmp_ctx);
4649 return ret;
4652 talloc_free(tmp_ctx);
4654 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4657 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4659 return replmd_delete_internals(module, req, false);
4663 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4665 return ret;
4668 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4670 int ret = LDB_ERR_OTHER;
4671 /* TODO: do some error mapping */
4673 /* Let the caller know the full WERROR */
4674 ar->objs->error = status;
4676 return ret;
4680 static struct replPropertyMetaData1 *
4681 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4682 enum drsuapi_DsAttributeId attid)
4684 uint32_t i;
4685 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4687 for (i = 0; i < rpmd_ctr->count; i++) {
4688 if (rpmd_ctr->array[i].attid == attid) {
4689 return &rpmd_ctr->array[i];
4692 return NULL;
4697 return true if an update is newer than an existing entry
4698 see section 5.11 of MS-ADTS
4700 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4701 const struct GUID *update_invocation_id,
4702 uint32_t current_version,
4703 uint32_t update_version,
4704 NTTIME current_change_time,
4705 NTTIME update_change_time)
4707 if (update_version != current_version) {
4708 return update_version > current_version;
4710 if (update_change_time != current_change_time) {
4711 return update_change_time > current_change_time;
4713 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4716 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4717 struct replPropertyMetaData1 *new_m)
4719 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4720 &new_m->originating_invocation_id,
4721 cur_m->version,
4722 new_m->version,
4723 cur_m->originating_change_time,
4724 new_m->originating_change_time);
4727 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4728 struct replPropertyMetaData1 *cur_m,
4729 struct replPropertyMetaData1 *new_m)
4731 bool cmp;
4734 * If the new replPropertyMetaData entry for this attribute is
4735 * not provided (this happens in the case where we look for
4736 * ATTID_name, but the name was not changed), then the local
4737 * state is clearly still current, as the remote
4738 * server didn't send it due to being older the high watermark
4739 * USN we sent.
4741 if (new_m == NULL) {
4742 return false;
4745 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4747 * if we compare equal then do an
4748 * update. This is used when a client
4749 * asks for a FULL_SYNC, and can be
4750 * used to recover a corrupt
4751 * replica.
4753 * This call is a bit tricky, what we
4754 * are doing it turning the 'is_newer'
4755 * call into a 'not is older' by
4756 * swapping cur_m and new_m, and negating the
4757 * outcome.
4759 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4760 cur_m);
4761 } else {
4762 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4763 new_m);
4765 return cmp;
4770 form a DN for a deleted (DEL:) or conflict (CNF:) DN
4772 static int replmd_make_prefix_child_dn(TALLOC_CTX *tmp_ctx,
4773 struct ldb_context *ldb,
4774 struct ldb_dn *dn,
4775 const char *four_char_prefix,
4776 const char *rdn_name,
4777 const struct ldb_val *rdn_value,
4778 struct GUID guid)
4780 struct ldb_val deleted_child_rdn_val;
4781 struct GUID_txt_buf guid_str;
4782 bool retb;
4784 GUID_buf_string(&guid, &guid_str);
4786 retb = ldb_dn_add_child_fmt(dn, "X=Y");
4787 if (!retb) {
4788 ldb_asprintf_errstring(ldb, __location__
4789 ": Unable to add a formatted child to dn: %s",
4790 ldb_dn_get_linearized(dn));
4791 return LDB_ERR_OPERATIONS_ERROR;
4795 * TODO: Per MS-ADTS 3.1.1.5.5 Delete Operation
4796 * we should truncate this value to ensure the RDN is not more than 255 chars.
4798 * However we MS-ADTS 3.1.1.5.1.2 Naming Constraints indicates that:
4800 * "Naming constraints are not enforced for replicated
4801 * updates." so this is safe and we don't have to work out not
4802 * splitting a UTF8 char right now.
4804 deleted_child_rdn_val = ldb_val_dup(tmp_ctx, rdn_value);
4807 * sizeof(guid_str.buf) will always be longer than
4808 * strlen(guid_str.buf) but we allocate using this and
4809 * waste the trailing bytes to avoid scaring folks
4810 * with memcpy() using strlen() below
4813 deleted_child_rdn_val.data
4814 = talloc_realloc(tmp_ctx, deleted_child_rdn_val.data,
4815 uint8_t,
4816 rdn_value->length + 5
4817 + sizeof(guid_str.buf));
4818 if (!deleted_child_rdn_val.data) {
4819 ldb_asprintf_errstring(ldb, __location__
4820 ": Unable to add a formatted child to dn: %s",
4821 ldb_dn_get_linearized(dn));
4822 return LDB_ERR_OPERATIONS_ERROR;
4825 deleted_child_rdn_val.length =
4826 rdn_value->length + 5
4827 + strlen(guid_str.buf);
4829 SMB_ASSERT(deleted_child_rdn_val.length <
4830 talloc_get_size(deleted_child_rdn_val.data));
4833 * talloc won't allocate more than 256MB so we can't
4834 * overflow but just to be sure
4836 if (deleted_child_rdn_val.length < rdn_value->length) {
4837 return LDB_ERR_OPERATIONS_ERROR;
4840 deleted_child_rdn_val.data[rdn_value->length] = 0x0a;
4841 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 1],
4842 four_char_prefix, 4);
4843 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 5],
4844 guid_str.buf,
4845 sizeof(guid_str.buf));
4847 /* Now set the value into the RDN, without parsing it */
4848 ldb_dn_set_component(dn, 0, rdn_name,
4849 deleted_child_rdn_val);
4851 return LDB_SUCCESS;
4856 form a conflict DN
4858 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx,
4859 struct ldb_context *ldb,
4860 struct ldb_dn *dn,
4861 struct GUID *guid)
4863 const struct ldb_val *rdn_val;
4864 const char *rdn_name;
4865 struct ldb_dn *new_dn;
4866 int ret;
4868 rdn_val = ldb_dn_get_rdn_val(dn);
4869 rdn_name = ldb_dn_get_rdn_name(dn);
4870 if (!rdn_val || !rdn_name) {
4871 return NULL;
4874 new_dn = ldb_dn_get_parent(mem_ctx, dn);
4875 if (!new_dn) {
4876 return NULL;
4879 ret = replmd_make_prefix_child_dn(mem_ctx,
4880 ldb, new_dn,
4881 "CNF:",
4882 rdn_name,
4883 rdn_val,
4884 *guid);
4885 if (ret != LDB_SUCCESS) {
4886 return NULL;
4888 return new_dn;
4892 form a deleted DN
4894 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
4895 struct ldb_context *ldb,
4896 struct ldb_dn *dn,
4897 const char *rdn_name,
4898 const struct ldb_val *rdn_value,
4899 struct GUID guid)
4901 return replmd_make_prefix_child_dn(tmp_ctx,
4902 ldb, dn,
4903 "DEL:",
4904 rdn_name,
4905 rdn_value,
4906 guid);
4911 perform a modify operation which sets the rDN and name attributes to
4912 their current values. This has the effect of changing these
4913 attributes to have been last updated by the current DC. This is
4914 needed to ensure that renames performed as part of conflict
4915 resolution are propogated to other DCs
4917 static int replmd_name_modify(struct replmd_replicated_request *ar,
4918 struct ldb_request *req, struct ldb_dn *dn)
4920 struct ldb_message *msg;
4921 const char *rdn_name;
4922 const struct ldb_val *rdn_val;
4923 const struct dsdb_attribute *rdn_attr;
4924 int ret;
4926 msg = ldb_msg_new(req);
4927 if (msg == NULL) {
4928 goto failed;
4930 msg->dn = dn;
4932 rdn_name = ldb_dn_get_rdn_name(dn);
4933 if (rdn_name == NULL) {
4934 goto failed;
4937 /* normalize the rdn attribute name */
4938 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
4939 if (rdn_attr == NULL) {
4940 goto failed;
4942 rdn_name = rdn_attr->lDAPDisplayName;
4944 rdn_val = ldb_dn_get_rdn_val(dn);
4945 if (rdn_val == NULL) {
4946 goto failed;
4949 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4950 goto failed;
4952 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
4953 goto failed;
4955 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4956 goto failed;
4958 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
4959 goto failed;
4963 * We have to mark this as a replicated update otherwise
4964 * schema_data may reject a rename in the schema partition
4967 ret = dsdb_module_modify(ar->module, msg,
4968 DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
4969 req);
4970 if (ret != LDB_SUCCESS) {
4971 DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
4972 ldb_dn_get_linearized(dn),
4973 ldb_errstring(ldb_module_get_ctx(ar->module))));
4974 return ret;
4977 talloc_free(msg);
4979 return LDB_SUCCESS;
4981 failed:
4982 talloc_free(msg);
4983 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
4984 ldb_dn_get_linearized(dn)));
4985 return LDB_ERR_OPERATIONS_ERROR;
4990 callback for conflict DN handling where we have renamed the incoming
4991 record. After renaming it, we need to ensure the change of name and
4992 rDN for the incoming record is seen as an originating update by this DC.
4994 This also handles updating lastKnownParent for entries sent to lostAndFound
4996 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
4998 struct replmd_replicated_request *ar =
4999 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5000 struct ldb_dn *conflict_dn = NULL;
5001 int ret;
5003 if (ares->error != LDB_SUCCESS) {
5004 /* call the normal callback for everything except success */
5005 return replmd_op_callback(req, ares);
5008 switch (req->operation) {
5009 case LDB_ADD:
5010 conflict_dn = req->op.add.message->dn;
5011 break;
5012 case LDB_MODIFY:
5013 conflict_dn = req->op.mod.message->dn;
5014 break;
5015 default:
5016 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
5019 /* perform a modify of the rDN and name of the record */
5020 ret = replmd_name_modify(ar, req, conflict_dn);
5021 if (ret != LDB_SUCCESS) {
5022 ares->error = ret;
5023 return replmd_op_callback(req, ares);
5026 if (ar->objs->objects[ar->index_current].last_known_parent) {
5027 struct ldb_message *msg = ldb_msg_new(req);
5028 if (msg == NULL) {
5029 ldb_module_oom(ar->module);
5030 return LDB_ERR_OPERATIONS_ERROR;
5033 msg->dn = req->op.add.message->dn;
5035 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
5036 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
5037 if (ret != LDB_SUCCESS) {
5038 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
5039 ldb_module_oom(ar->module);
5040 return ret;
5042 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
5044 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
5045 if (ret != LDB_SUCCESS) {
5046 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
5047 ldb_dn_get_linearized(msg->dn),
5048 ldb_errstring(ldb_module_get_ctx(ar->module))));
5049 return ret;
5051 TALLOC_FREE(msg);
5054 return replmd_op_callback(req, ares);
5058 callback for replmd_replicated_apply_add()
5059 This copes with the creation of conflict records in the case where
5060 the DN exists, but with a different objectGUID
5062 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))
5064 struct ldb_dn *conflict_dn;
5065 struct replmd_replicated_request *ar =
5066 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5067 struct ldb_result *res;
5068 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5069 int ret;
5070 const struct ldb_val *omd_value;
5071 struct replPropertyMetaDataBlob omd, *rmd;
5072 enum ndr_err_code ndr_err;
5073 bool rename_incoming_record, rodc;
5074 struct replPropertyMetaData1 *rmd_name, *omd_name;
5075 struct ldb_message *msg;
5076 struct ldb_request *down_req = NULL;
5078 /* call the normal callback for success */
5079 if (ares->error == LDB_SUCCESS) {
5080 return callback(req, ares);
5084 * we have a conflict, and need to decide if we will keep the
5085 * new record or the old record
5088 msg = ar->objs->objects[ar->index_current].msg;
5089 conflict_dn = msg->dn;
5091 /* For failures other than conflicts, fail the whole operation here */
5092 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5093 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
5094 ldb_dn_get_linearized(conflict_dn),
5095 ldb_errstring(ldb_module_get_ctx(ar->module)));
5097 return ldb_module_done(ar->req, NULL, NULL,
5098 LDB_ERR_OPERATIONS_ERROR);
5101 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5102 if (ret != LDB_SUCCESS) {
5103 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)));
5104 return ldb_module_done(ar->req, NULL, NULL,
5105 LDB_ERR_OPERATIONS_ERROR);
5109 if (rodc) {
5111 * We are on an RODC, or were a GC for this
5112 * partition, so we have to fail this until
5113 * someone who owns the partition sorts it
5114 * out
5116 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5117 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
5118 " - We must fail the operation until a master for this partition resolves the conflict",
5119 ldb_dn_get_linearized(conflict_dn));
5120 ret = LDB_ERR_OPERATIONS_ERROR;
5121 goto failed;
5125 * first we need the replPropertyMetaData attribute from the
5126 * local, conflicting record
5128 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
5129 attrs,
5130 DSDB_FLAG_NEXT_MODULE |
5131 DSDB_SEARCH_SHOW_DELETED |
5132 DSDB_SEARCH_SHOW_RECYCLED, req);
5133 if (ret != LDB_SUCCESS) {
5134 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5135 ldb_dn_get_linearized(conflict_dn)));
5136 goto failed;
5139 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5140 if (omd_value == NULL) {
5141 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5142 ldb_dn_get_linearized(conflict_dn)));
5143 goto failed;
5146 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5147 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5148 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5149 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5150 ldb_dn_get_linearized(conflict_dn)));
5151 goto failed;
5154 rmd = ar->objs->objects[ar->index_current].meta_data;
5157 * we decide which is newer based on the RPMD on the name
5158 * attribute. See [MS-DRSR] ResolveNameConflict.
5160 * We expect omd_name to be present, as this is from a local
5161 * search, but while rmd_name should have been given to us by
5162 * the remote server, if it is missing we just prefer the
5163 * local name in
5164 * replmd_replPropertyMetaData1_new_should_be_taken()
5166 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5167 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5168 if (!omd_name) {
5169 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5170 ldb_dn_get_linearized(conflict_dn)));
5171 goto failed;
5175 * Should we preserve the current record, and so rename the
5176 * incoming record to be a conflict?
5178 rename_incoming_record
5179 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5180 omd_name, rmd_name);
5182 if (rename_incoming_record) {
5183 struct GUID guid;
5184 struct ldb_dn *new_dn;
5186 guid = samdb_result_guid(msg, "objectGUID");
5187 if (GUID_all_zero(&guid)) {
5188 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
5189 ldb_dn_get_linearized(conflict_dn)));
5190 goto failed;
5192 new_dn = replmd_conflict_dn(req,
5193 ldb_module_get_ctx(ar->module),
5194 conflict_dn, &guid);
5195 if (new_dn == NULL) {
5196 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5197 ldb_dn_get_linearized(conflict_dn)));
5198 goto failed;
5201 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
5202 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5204 /* re-submit the request, but with the new DN */
5205 callback = replmd_op_name_modify_callback;
5206 msg->dn = new_dn;
5207 } else {
5208 /* we are renaming the existing record */
5209 struct GUID guid;
5210 struct ldb_dn *new_dn;
5212 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5213 if (GUID_all_zero(&guid)) {
5214 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5215 ldb_dn_get_linearized(conflict_dn)));
5216 goto failed;
5219 new_dn = replmd_conflict_dn(req,
5220 ldb_module_get_ctx(ar->module),
5221 conflict_dn, &guid);
5222 if (new_dn == NULL) {
5223 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5224 ldb_dn_get_linearized(conflict_dn)));
5225 goto failed;
5228 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5229 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5231 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5232 DSDB_FLAG_OWN_MODULE, req);
5233 if (ret != LDB_SUCCESS) {
5234 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5235 ldb_dn_get_linearized(conflict_dn),
5236 ldb_dn_get_linearized(new_dn),
5237 ldb_errstring(ldb_module_get_ctx(ar->module))));
5238 goto failed;
5242 * now we need to ensure that the rename is seen as an
5243 * originating update. We do that with a modify.
5245 ret = replmd_name_modify(ar, req, new_dn);
5246 if (ret != LDB_SUCCESS) {
5247 goto failed;
5250 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
5251 ldb_dn_get_linearized(req->op.add.message->dn)));
5254 ret = ldb_build_add_req(&down_req,
5255 ldb_module_get_ctx(ar->module),
5256 req,
5257 msg,
5258 ar->controls,
5260 callback,
5261 req);
5262 if (ret != LDB_SUCCESS) {
5263 goto failed;
5265 LDB_REQ_SET_LOCATION(down_req);
5267 /* current partition control needed by "repmd_op_callback" */
5268 ret = ldb_request_add_control(down_req,
5269 DSDB_CONTROL_CURRENT_PARTITION_OID,
5270 false, NULL);
5271 if (ret != LDB_SUCCESS) {
5272 return replmd_replicated_request_error(ar, ret);
5275 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5276 /* this tells the partition module to make it a
5277 partial replica if creating an NC */
5278 ret = ldb_request_add_control(down_req,
5279 DSDB_CONTROL_PARTIAL_REPLICA,
5280 false, NULL);
5281 if (ret != LDB_SUCCESS) {
5282 return replmd_replicated_request_error(ar, ret);
5287 * Finally we re-run the add, otherwise the new record won't
5288 * exist, as we are here because of that exact failure!
5290 return ldb_next_request(ar->module, down_req);
5291 failed:
5293 /* on failure make the caller get the error. This means
5294 * replication will stop with an error, but there is not much
5295 * else we can do.
5297 if (ret == LDB_SUCCESS) {
5298 ret = LDB_ERR_OPERATIONS_ERROR;
5300 return ldb_module_done(ar->req, NULL, NULL,
5301 ret);
5305 callback for replmd_replicated_apply_add()
5306 This copes with the creation of conflict records in the case where
5307 the DN exists, but with a different objectGUID
5309 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
5311 struct replmd_replicated_request *ar =
5312 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5314 if (ar->objs->objects[ar->index_current].last_known_parent) {
5315 /* This is like a conflict DN, where we put the object in LostAndFound
5316 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
5317 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
5320 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
5324 this is called when a new object comes in over DRS
5326 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
5328 struct ldb_context *ldb;
5329 struct ldb_request *change_req;
5330 enum ndr_err_code ndr_err;
5331 struct ldb_message *msg;
5332 struct replPropertyMetaDataBlob *md;
5333 struct ldb_val md_value;
5334 unsigned int i;
5335 int ret;
5336 bool remote_isDeleted = false;
5337 bool is_schema_nc;
5338 NTTIME now;
5339 time_t t = time(NULL);
5340 const struct ldb_val *rdn_val;
5341 struct replmd_private *replmd_private =
5342 talloc_get_type(ldb_module_get_private(ar->module),
5343 struct replmd_private);
5344 unix_to_nt_time(&now, t);
5346 ldb = ldb_module_get_ctx(ar->module);
5347 msg = ar->objs->objects[ar->index_current].msg;
5348 md = ar->objs->objects[ar->index_current].meta_data;
5349 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5351 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5352 if (ret != LDB_SUCCESS) {
5353 return replmd_replicated_request_error(ar, ret);
5356 ret = dsdb_msg_add_guid(msg,
5357 &ar->objs->objects[ar->index_current].object_guid,
5358 "objectGUID");
5359 if (ret != LDB_SUCCESS) {
5360 return replmd_replicated_request_error(ar, ret);
5363 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5364 if (ret != LDB_SUCCESS) {
5365 return replmd_replicated_request_error(ar, ret);
5368 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
5369 if (ret != LDB_SUCCESS) {
5370 return replmd_replicated_request_error(ar, ret);
5373 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5374 if (ret != LDB_SUCCESS) {
5375 return replmd_replicated_request_error(ar, ret);
5378 /* remove any message elements that have zero values */
5379 for (i=0; i<msg->num_elements; i++) {
5380 struct ldb_message_element *el = &msg->elements[i];
5382 if (el->num_values == 0) {
5383 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5384 ldb_asprintf_errstring(ldb, __location__
5385 ": empty objectClass sent on %s, aborting replication\n",
5386 ldb_dn_get_linearized(msg->dn));
5387 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5390 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5391 el->name));
5392 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
5393 msg->num_elements--;
5394 i--;
5395 continue;
5399 if (DEBUGLVL(8)) {
5400 struct GUID_txt_buf guid_txt;
5402 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5403 LDB_CHANGETYPE_ADD,
5404 msg);
5405 DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5406 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5407 s));
5408 talloc_free(s);
5409 } else if (DEBUGLVL(4)) {
5410 struct GUID_txt_buf guid_txt;
5411 DEBUG(4, ("DRS replication add DN of %s is %s\n",
5412 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5413 ldb_dn_get_linearized(msg->dn)));
5415 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5416 "isDeleted", false);
5419 * the meta data array is already sorted by the caller, except
5420 * for the RDN, which needs to be added.
5424 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5425 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5426 md, ar, now, is_schema_nc,
5427 false);
5428 if (ret != LDB_SUCCESS) {
5429 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5430 return replmd_replicated_request_error(ar, ret);
5433 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5434 if (ret != LDB_SUCCESS) {
5435 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5436 return replmd_replicated_request_error(ar, ret);
5439 for (i=0; i < md->ctr.ctr1.count; i++) {
5440 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5442 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5443 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5444 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5445 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5446 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5448 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5449 if (ret != LDB_SUCCESS) {
5450 return replmd_replicated_request_error(ar, ret);
5453 replmd_ldb_message_sort(msg, ar->schema);
5455 if (!remote_isDeleted) {
5456 ret = dsdb_module_schedule_sd_propagation(ar->module,
5457 ar->objs->partition_dn,
5458 msg->dn, true);
5459 if (ret != LDB_SUCCESS) {
5460 return replmd_replicated_request_error(ar, ret);
5464 ar->isDeleted = remote_isDeleted;
5466 ret = ldb_build_add_req(&change_req,
5467 ldb,
5469 msg,
5470 ar->controls,
5472 replmd_op_add_callback,
5473 ar->req);
5474 LDB_REQ_SET_LOCATION(change_req);
5475 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5477 /* current partition control needed by "repmd_op_callback" */
5478 ret = ldb_request_add_control(change_req,
5479 DSDB_CONTROL_CURRENT_PARTITION_OID,
5480 false, NULL);
5481 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5483 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5484 /* this tells the partition module to make it a
5485 partial replica if creating an NC */
5486 ret = ldb_request_add_control(change_req,
5487 DSDB_CONTROL_PARTIAL_REPLICA,
5488 false, NULL);
5489 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5492 return ldb_next_request(ar->module, change_req);
5495 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5496 struct ldb_reply *ares)
5498 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5499 struct replmd_replicated_request);
5500 int ret;
5502 if (!ares) {
5503 return ldb_module_done(ar->req, NULL, NULL,
5504 LDB_ERR_OPERATIONS_ERROR);
5508 * The error NO_SUCH_OBJECT is not expected, unless the search
5509 * base is the partition DN, and that case doesn't happen here
5510 * because then we wouldn't get a parent_guid_value in any
5511 * case.
5513 if (ares->error != LDB_SUCCESS) {
5514 return ldb_module_done(ar->req, ares->controls,
5515 ares->response, ares->error);
5518 switch (ares->type) {
5519 case LDB_REPLY_ENTRY:
5521 struct ldb_message *parent_msg = ares->message;
5522 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5523 struct ldb_dn *parent_dn = NULL;
5524 int comp_num;
5526 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5527 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5528 /* Per MS-DRSR 4.1.10.6.10
5529 * FindBestParentObject we need to move this
5530 * new object under a deleted object to
5531 * lost-and-found */
5532 struct ldb_dn *nc_root;
5534 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5535 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5536 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5537 "No suitable NC root found for %s. "
5538 "We need to move this object because parent object %s "
5539 "is deleted, but this object is not.",
5540 ldb_dn_get_linearized(msg->dn),
5541 ldb_dn_get_linearized(parent_msg->dn));
5542 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5543 } else if (ret != LDB_SUCCESS) {
5544 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5545 "Unable to find NC root for %s: %s. "
5546 "We need to move this object because parent object %s "
5547 "is deleted, but this object is not.",
5548 ldb_dn_get_linearized(msg->dn),
5549 ldb_errstring(ldb_module_get_ctx(ar->module)),
5550 ldb_dn_get_linearized(parent_msg->dn));
5551 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5554 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5555 nc_root,
5556 DS_GUID_LOSTANDFOUND_CONTAINER,
5557 &parent_dn);
5558 if (ret != LDB_SUCCESS) {
5559 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5560 "Unable to find LostAndFound Container for %s "
5561 "in partition %s: %s. "
5562 "We need to move this object because parent object %s "
5563 "is deleted, but this object is not.",
5564 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5565 ldb_errstring(ldb_module_get_ctx(ar->module)),
5566 ldb_dn_get_linearized(parent_msg->dn));
5567 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5569 ar->objs->objects[ar->index_current].last_known_parent
5570 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5572 } else {
5573 parent_dn
5574 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5577 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5579 comp_num = ldb_dn_get_comp_num(msg->dn);
5580 if (comp_num > 1) {
5581 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5582 talloc_free(ares);
5583 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5586 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5587 talloc_free(ares);
5588 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5590 break;
5592 case LDB_REPLY_REFERRAL:
5593 /* we ignore referrals */
5594 break;
5596 case LDB_REPLY_DONE:
5598 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5599 struct GUID_txt_buf str_buf;
5600 if (ar->search_msg != NULL) {
5601 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5602 "No parent with GUID %s found for object locally known as %s",
5603 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5604 ldb_dn_get_linearized(ar->search_msg->dn));
5605 } else {
5606 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5607 "No parent with GUID %s found for object remotely known as %s",
5608 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5609 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5613 * This error code is really important, as it
5614 * is the flag back to the callers to retry
5615 * this with DRSUAPI_DRS_GET_ANC, and so get
5616 * the parent objects before the child
5617 * objects
5619 return ldb_module_done(ar->req, NULL, NULL,
5620 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5623 if (ar->search_msg != NULL) {
5624 ret = replmd_replicated_apply_merge(ar);
5625 } else {
5626 ret = replmd_replicated_apply_add(ar);
5628 if (ret != LDB_SUCCESS) {
5629 return ldb_module_done(ar->req, NULL, NULL, ret);
5633 talloc_free(ares);
5634 return LDB_SUCCESS;
5638 * Look for the parent object, so we put the new object in the right
5639 * place This is akin to NameObject in MS-DRSR - this routine and the
5640 * callbacks find the right parent name, and correct name for this
5641 * object
5644 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5646 struct ldb_context *ldb;
5647 int ret;
5648 char *tmp_str;
5649 char *filter;
5650 struct ldb_request *search_req;
5651 static const char *attrs[] = {"isDeleted", NULL};
5652 struct GUID_txt_buf guid_str_buf;
5654 ldb = ldb_module_get_ctx(ar->module);
5656 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5657 if (ar->search_msg != NULL) {
5658 return replmd_replicated_apply_merge(ar);
5659 } else {
5660 return replmd_replicated_apply_add(ar);
5664 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5665 &guid_str_buf);
5667 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5668 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5670 ret = ldb_build_search_req(&search_req,
5671 ldb,
5673 ar->objs->partition_dn,
5674 LDB_SCOPE_SUBTREE,
5675 filter,
5676 attrs,
5677 NULL,
5679 replmd_replicated_apply_search_for_parent_callback,
5680 ar->req);
5681 LDB_REQ_SET_LOCATION(search_req);
5683 ret = dsdb_request_add_controls(search_req,
5684 DSDB_SEARCH_SHOW_RECYCLED|
5685 DSDB_SEARCH_SHOW_DELETED|
5686 DSDB_SEARCH_SHOW_EXTENDED_DN);
5687 if (ret != LDB_SUCCESS) {
5688 return ret;
5691 return ldb_next_request(ar->module, search_req);
5695 handle renames that come in over DRS replication
5697 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5698 struct ldb_message *msg,
5699 struct ldb_request *parent,
5700 bool *renamed)
5702 int ret;
5703 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5704 struct ldb_result *res;
5705 struct ldb_dn *conflict_dn;
5706 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5707 const struct ldb_val *omd_value;
5708 struct replPropertyMetaDataBlob omd, *rmd;
5709 enum ndr_err_code ndr_err;
5710 bool rename_incoming_record, rodc;
5711 struct replPropertyMetaData1 *rmd_name, *omd_name;
5712 struct ldb_dn *new_dn;
5713 struct GUID guid;
5715 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5716 ldb_dn_get_linearized(ar->search_msg->dn),
5717 ldb_dn_get_linearized(msg->dn)));
5720 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5721 DSDB_FLAG_NEXT_MODULE, ar->req);
5722 if (ret == LDB_SUCCESS) {
5723 talloc_free(tmp_ctx);
5724 *renamed = true;
5725 return ret;
5728 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5729 talloc_free(tmp_ctx);
5730 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5731 ldb_dn_get_linearized(ar->search_msg->dn),
5732 ldb_dn_get_linearized(msg->dn),
5733 ldb_errstring(ldb_module_get_ctx(ar->module)));
5734 return ret;
5737 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5738 if (ret != LDB_SUCCESS) {
5739 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5740 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5741 ldb_errstring(ldb_module_get_ctx(ar->module)));
5742 return LDB_ERR_OPERATIONS_ERROR;
5745 * we have a conflict, and need to decide if we will keep the
5746 * new record or the old record
5749 conflict_dn = msg->dn;
5751 if (rodc) {
5753 * We are on an RODC, or were a GC for this
5754 * partition, so we have to fail this until
5755 * someone who owns the partition sorts it
5756 * out
5758 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5759 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5760 " - We must fail the operation until a master for this partition resolves the conflict",
5761 ldb_dn_get_linearized(conflict_dn));
5762 ret = LDB_ERR_OPERATIONS_ERROR;
5763 goto failed;
5767 * first we need the replPropertyMetaData attribute from the
5768 * old record
5770 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
5771 attrs,
5772 DSDB_FLAG_NEXT_MODULE |
5773 DSDB_SEARCH_SHOW_DELETED |
5774 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5775 if (ret != LDB_SUCCESS) {
5776 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5777 ldb_dn_get_linearized(conflict_dn)));
5778 goto failed;
5781 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5782 if (omd_value == NULL) {
5783 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5784 ldb_dn_get_linearized(conflict_dn)));
5785 goto failed;
5788 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5789 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5790 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5791 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5792 ldb_dn_get_linearized(conflict_dn)));
5793 goto failed;
5796 rmd = ar->objs->objects[ar->index_current].meta_data;
5799 * we decide which is newer based on the RPMD on the name
5800 * attribute. See [MS-DRSR] ResolveNameConflict.
5802 * We expect omd_name to be present, as this is from a local
5803 * search, but while rmd_name should have been given to us by
5804 * the remote server, if it is missing we just prefer the
5805 * local name in
5806 * replmd_replPropertyMetaData1_new_should_be_taken()
5808 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5809 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5810 if (!omd_name) {
5811 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5812 ldb_dn_get_linearized(conflict_dn)));
5813 goto failed;
5817 * Should we preserve the current record, and so rename the
5818 * incoming record to be a conflict?
5820 rename_incoming_record =
5821 !replmd_replPropertyMetaData1_new_should_be_taken(
5822 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5823 omd_name, rmd_name);
5825 if (rename_incoming_record) {
5827 new_dn = replmd_conflict_dn(msg,
5828 ldb_module_get_ctx(ar->module),
5829 msg->dn,
5830 &ar->objs->objects[ar->index_current].object_guid);
5831 if (new_dn == NULL) {
5832 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5833 "Failed to form conflict DN for %s\n",
5834 ldb_dn_get_linearized(msg->dn));
5836 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5839 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
5840 DSDB_FLAG_NEXT_MODULE, ar->req);
5841 if (ret != LDB_SUCCESS) {
5842 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5843 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5844 ldb_dn_get_linearized(conflict_dn),
5845 ldb_dn_get_linearized(ar->search_msg->dn),
5846 ldb_dn_get_linearized(new_dn),
5847 ldb_errstring(ldb_module_get_ctx(ar->module)));
5848 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5851 msg->dn = new_dn;
5852 *renamed = true;
5853 return LDB_SUCCESS;
5856 /* we are renaming the existing record */
5858 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5859 if (GUID_all_zero(&guid)) {
5860 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5861 ldb_dn_get_linearized(conflict_dn)));
5862 goto failed;
5865 new_dn = replmd_conflict_dn(tmp_ctx,
5866 ldb_module_get_ctx(ar->module),
5867 conflict_dn, &guid);
5868 if (new_dn == NULL) {
5869 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5870 ldb_dn_get_linearized(conflict_dn)));
5871 goto failed;
5874 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5875 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5877 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5878 DSDB_FLAG_OWN_MODULE, ar->req);
5879 if (ret != LDB_SUCCESS) {
5880 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5881 ldb_dn_get_linearized(conflict_dn),
5882 ldb_dn_get_linearized(new_dn),
5883 ldb_errstring(ldb_module_get_ctx(ar->module))));
5884 goto failed;
5888 * now we need to ensure that the rename is seen as an
5889 * originating update. We do that with a modify.
5891 ret = replmd_name_modify(ar, ar->req, new_dn);
5892 if (ret != LDB_SUCCESS) {
5893 goto failed;
5896 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
5897 ldb_dn_get_linearized(ar->search_msg->dn),
5898 ldb_dn_get_linearized(msg->dn)));
5901 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5902 DSDB_FLAG_NEXT_MODULE, ar->req);
5903 if (ret != LDB_SUCCESS) {
5904 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
5905 ldb_dn_get_linearized(ar->search_msg->dn),
5906 ldb_dn_get_linearized(msg->dn),
5907 ldb_errstring(ldb_module_get_ctx(ar->module))));
5908 goto failed;
5911 talloc_free(tmp_ctx);
5912 return ret;
5913 failed:
5915 * On failure make the caller get the error
5916 * This means replication will stop with an error,
5917 * but there is not much else we can do. In the
5918 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
5919 * needed.
5921 if (ret == LDB_SUCCESS) {
5922 ret = LDB_ERR_OPERATIONS_ERROR;
5925 talloc_free(tmp_ctx);
5926 return ret;
5930 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
5932 struct ldb_context *ldb;
5933 struct ldb_request *change_req;
5934 enum ndr_err_code ndr_err;
5935 struct ldb_message *msg;
5936 struct replPropertyMetaDataBlob *rmd;
5937 struct replPropertyMetaDataBlob omd;
5938 const struct ldb_val *omd_value;
5939 struct replPropertyMetaDataBlob nmd;
5940 struct ldb_val nmd_value;
5941 struct GUID remote_parent_guid;
5942 unsigned int i;
5943 uint32_t j,ni=0;
5944 unsigned int removed_attrs = 0;
5945 int ret;
5946 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
5947 bool isDeleted = false;
5948 bool local_isDeleted = false;
5949 bool remote_isDeleted = false;
5950 bool take_remote_isDeleted = false;
5951 bool sd_updated = false;
5952 bool renamed = false;
5953 bool is_schema_nc = false;
5954 NTSTATUS nt_status;
5955 const struct ldb_val *old_rdn, *new_rdn;
5956 struct replmd_private *replmd_private =
5957 talloc_get_type(ldb_module_get_private(ar->module),
5958 struct replmd_private);
5959 NTTIME now;
5960 time_t t = time(NULL);
5961 unix_to_nt_time(&now, t);
5963 ldb = ldb_module_get_ctx(ar->module);
5964 msg = ar->objs->objects[ar->index_current].msg;
5966 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5968 rmd = ar->objs->objects[ar->index_current].meta_data;
5969 ZERO_STRUCT(omd);
5970 omd.version = 1;
5972 /* find existing meta data */
5973 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5974 if (omd_value) {
5975 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5976 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5977 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5978 nt_status = ndr_map_error2ntstatus(ndr_err);
5979 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5982 if (omd.version != 1) {
5983 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5987 if (DEBUGLVL(8)) {
5988 struct GUID_txt_buf guid_txt;
5990 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5991 LDB_CHANGETYPE_MODIFY, msg);
5992 DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
5993 "%s\n"
5994 "%s\n",
5995 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5997 ndr_print_struct_string(s,
5998 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5999 "existing replPropertyMetaData",
6000 &omd),
6001 ndr_print_struct_string(s,
6002 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6003 "incoming replPropertyMetaData",
6004 rmd)));
6005 talloc_free(s);
6006 } else if (DEBUGLVL(4)) {
6007 struct GUID_txt_buf guid_txt;
6009 DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
6010 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6011 &guid_txt),
6012 ldb_dn_get_linearized(msg->dn)));
6015 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
6016 "isDeleted", false);
6017 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
6018 "isDeleted", false);
6021 * Fill in the remote_parent_guid with the GUID or an all-zero
6022 * GUID.
6024 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
6025 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
6026 } else {
6027 remote_parent_guid = GUID_zero();
6031 * To ensure we follow a complex rename chain around, we have
6032 * to confirm that the DN is the same (mostly to confirm the
6033 * RDN) and the parentGUID is the same.
6035 * This ensures we keep things under the correct parent, which
6036 * replmd_replicated_handle_rename() will do.
6039 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
6040 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
6041 ret = LDB_SUCCESS;
6042 } else {
6044 * handle renames, even just by case that come in over
6045 * DRS. Changes in the parent DN don't hit us here,
6046 * because the search for a parent will clean up those
6047 * components.
6049 * We also have already filtered out the case where
6050 * the peer has an older name to what we have (see
6051 * replmd_replicated_apply_search_callback())
6053 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
6056 if (ret != LDB_SUCCESS) {
6057 ldb_debug(ldb, LDB_DEBUG_FATAL,
6058 "replmd_replicated_request rename %s => %s failed - %s\n",
6059 ldb_dn_get_linearized(ar->search_msg->dn),
6060 ldb_dn_get_linearized(msg->dn),
6061 ldb_errstring(ldb));
6062 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6065 if (renamed == true) {
6067 * Set the callback to one that will fix up the name
6068 * metadata on the new conflict DN
6070 callback = replmd_op_name_modify_callback;
6073 ZERO_STRUCT(nmd);
6074 nmd.version = 1;
6075 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
6076 nmd.ctr.ctr1.array = talloc_array(ar,
6077 struct replPropertyMetaData1,
6078 nmd.ctr.ctr1.count);
6079 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6081 /* first copy the old meta data */
6082 for (i=0; i < omd.ctr.ctr1.count; i++) {
6083 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
6084 ni++;
6087 ar->seq_num = 0;
6088 /* now merge in the new meta data */
6089 for (i=0; i < rmd->ctr.ctr1.count; i++) {
6090 bool found = false;
6092 for (j=0; j < ni; j++) {
6093 bool cmp;
6095 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
6096 continue;
6099 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
6100 ar->objs->dsdb_repl_flags,
6101 &nmd.ctr.ctr1.array[j],
6102 &rmd->ctr.ctr1.array[i]);
6103 if (cmp) {
6104 /* replace the entry */
6105 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
6106 if (ar->seq_num == 0) {
6107 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6108 if (ret != LDB_SUCCESS) {
6109 return replmd_replicated_request_error(ar, ret);
6112 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
6113 switch (nmd.ctr.ctr1.array[j].attid) {
6114 case DRSUAPI_ATTID_ntSecurityDescriptor:
6115 sd_updated = true;
6116 break;
6117 case DRSUAPI_ATTID_isDeleted:
6118 take_remote_isDeleted = true;
6119 break;
6120 default:
6121 break;
6123 found = true;
6124 break;
6127 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
6128 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
6129 msg->elements[i-removed_attrs].name,
6130 ldb_dn_get_linearized(msg->dn),
6131 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
6134 /* we don't want to apply this change so remove the attribute */
6135 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
6136 removed_attrs++;
6138 found = true;
6139 break;
6142 if (found) continue;
6144 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
6145 if (ar->seq_num == 0) {
6146 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6147 if (ret != LDB_SUCCESS) {
6148 return replmd_replicated_request_error(ar, ret);
6151 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
6152 switch (nmd.ctr.ctr1.array[ni].attid) {
6153 case DRSUAPI_ATTID_ntSecurityDescriptor:
6154 sd_updated = true;
6155 break;
6156 case DRSUAPI_ATTID_isDeleted:
6157 take_remote_isDeleted = true;
6158 break;
6159 default:
6160 break;
6162 ni++;
6166 * finally correct the size of the meta_data array
6168 nmd.ctr.ctr1.count = ni;
6170 new_rdn = ldb_dn_get_rdn_val(msg->dn);
6171 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
6173 if (renamed) {
6174 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
6175 &nmd, ar, now, is_schema_nc,
6176 false);
6177 if (ret != LDB_SUCCESS) {
6178 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6179 return replmd_replicated_request_error(ar, ret);
6183 * sort the new meta data array
6185 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
6186 if (ret != LDB_SUCCESS) {
6187 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6188 return ret;
6192 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
6193 * UpdateObject.
6195 * This also controls SD propagation below
6197 if (take_remote_isDeleted) {
6198 isDeleted = remote_isDeleted;
6199 } else {
6200 isDeleted = local_isDeleted;
6203 ar->isDeleted = isDeleted;
6206 * check if some replicated attributes left, otherwise skip the ldb_modify() call
6208 if (msg->num_elements == 0) {
6209 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
6210 ar->index_current);
6212 return replmd_replicated_apply_isDeleted(ar);
6215 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
6216 ar->index_current, msg->num_elements);
6218 if (renamed) {
6219 sd_updated = true;
6222 if (sd_updated && !isDeleted) {
6223 ret = dsdb_module_schedule_sd_propagation(ar->module,
6224 ar->objs->partition_dn,
6225 msg->dn, true);
6226 if (ret != LDB_SUCCESS) {
6227 return ldb_operr(ldb);
6231 /* create the meta data value */
6232 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
6233 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
6234 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6235 nt_status = ndr_map_error2ntstatus(ndr_err);
6236 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6240 * when we know that we'll modify the record, add the whenChanged, uSNChanged
6241 * and replPopertyMetaData attributes
6243 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
6244 if (ret != LDB_SUCCESS) {
6245 return replmd_replicated_request_error(ar, ret);
6247 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
6248 if (ret != LDB_SUCCESS) {
6249 return replmd_replicated_request_error(ar, ret);
6251 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
6252 if (ret != LDB_SUCCESS) {
6253 return replmd_replicated_request_error(ar, ret);
6256 replmd_ldb_message_sort(msg, ar->schema);
6258 /* we want to replace the old values */
6259 for (i=0; i < msg->num_elements; i++) {
6260 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
6261 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
6262 if (msg->elements[i].num_values == 0) {
6263 ldb_asprintf_errstring(ldb, __location__
6264 ": objectClass removed on %s, aborting replication\n",
6265 ldb_dn_get_linearized(msg->dn));
6266 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
6271 if (DEBUGLVL(8)) {
6272 struct GUID_txt_buf guid_txt;
6274 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6275 LDB_CHANGETYPE_MODIFY,
6276 msg);
6277 DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
6278 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6279 &guid_txt),
6280 s));
6281 talloc_free(s);
6282 } else if (DEBUGLVL(4)) {
6283 struct GUID_txt_buf guid_txt;
6285 DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
6286 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6287 &guid_txt),
6288 ldb_dn_get_linearized(msg->dn)));
6291 ret = ldb_build_mod_req(&change_req,
6292 ldb,
6294 msg,
6295 ar->controls,
6297 callback,
6298 ar->req);
6299 LDB_REQ_SET_LOCATION(change_req);
6300 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6302 /* current partition control needed by "repmd_op_callback" */
6303 ret = ldb_request_add_control(change_req,
6304 DSDB_CONTROL_CURRENT_PARTITION_OID,
6305 false, NULL);
6306 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6308 return ldb_next_request(ar->module, change_req);
6311 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
6312 struct ldb_reply *ares)
6314 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6315 struct replmd_replicated_request);
6316 int ret;
6318 if (!ares) {
6319 return ldb_module_done(ar->req, NULL, NULL,
6320 LDB_ERR_OPERATIONS_ERROR);
6322 if (ares->error != LDB_SUCCESS &&
6323 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6324 return ldb_module_done(ar->req, ares->controls,
6325 ares->response, ares->error);
6328 switch (ares->type) {
6329 case LDB_REPLY_ENTRY:
6330 ar->search_msg = talloc_steal(ar, ares->message);
6331 break;
6333 case LDB_REPLY_REFERRAL:
6334 /* we ignore referrals */
6335 break;
6337 case LDB_REPLY_DONE:
6339 struct replPropertyMetaData1 *md_remote;
6340 struct replPropertyMetaData1 *md_local;
6342 struct replPropertyMetaDataBlob omd;
6343 const struct ldb_val *omd_value;
6344 struct replPropertyMetaDataBlob *rmd;
6345 struct ldb_message *msg;
6346 int instanceType;
6347 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
6348 ar->objs->objects[ar->index_current].last_known_parent = NULL;
6351 * This is the ADD case, find the appropriate parent,
6352 * as this object doesn't exist locally:
6354 if (ar->search_msg == NULL) {
6355 ret = replmd_replicated_apply_search_for_parent(ar);
6356 if (ret != LDB_SUCCESS) {
6357 return ldb_module_done(ar->req, NULL, NULL, ret);
6359 talloc_free(ares);
6360 return LDB_SUCCESS;
6364 * Otherwise, in the MERGE case, work out if we are
6365 * attempting a rename, and if so find the parent the
6366 * newly renamed object wants to belong under (which
6367 * may not be the parent in it's attached string DN
6369 rmd = ar->objs->objects[ar->index_current].meta_data;
6370 ZERO_STRUCT(omd);
6371 omd.version = 1;
6373 /* find existing meta data */
6374 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6375 if (omd_value) {
6376 enum ndr_err_code ndr_err;
6377 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6378 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6379 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6380 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6381 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6384 if (omd.version != 1) {
6385 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6389 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6391 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6392 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6393 && GUID_all_zero(&ar->local_parent_guid)) {
6394 DEBUG(0, ("Refusing to replicate new version of %s "
6395 "as local object has an all-zero parentGUID attribute, "
6396 "despite not being an NC root\n",
6397 ldb_dn_get_linearized(ar->search_msg->dn)));
6398 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6402 * now we need to check for double renames. We could have a
6403 * local rename pending which our replication partner hasn't
6404 * received yet. We choose which one wins by looking at the
6405 * attribute stamps on the two objects, the newer one wins.
6407 * This also simply applies the correct algorithms for
6408 * determining if a change was made to name at all, or
6409 * if the object has just been renamed under the same
6410 * parent.
6412 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6413 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6414 if (!md_local) {
6415 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6416 ldb_dn_get_linearized(ar->search_msg->dn)));
6417 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6421 * if there is no name attribute given then we have to assume the
6422 * object we've received has the older name
6424 if (replmd_replPropertyMetaData1_new_should_be_taken(
6425 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6426 md_local, md_remote)) {
6427 struct GUID_txt_buf p_guid_local;
6428 struct GUID_txt_buf p_guid_remote;
6429 msg = ar->objs->objects[ar->index_current].msg;
6431 /* Merge on the existing object, with rename */
6433 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6434 "as incoming object changing to %s under %s\n",
6435 ldb_dn_get_linearized(ar->search_msg->dn),
6436 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6437 ldb_dn_get_linearized(msg->dn),
6438 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6439 &p_guid_remote)));
6440 ret = replmd_replicated_apply_search_for_parent(ar);
6441 } else {
6442 struct GUID_txt_buf p_guid_local;
6443 struct GUID_txt_buf p_guid_remote;
6444 msg = ar->objs->objects[ar->index_current].msg;
6447 * Merge on the existing object, force no
6448 * rename (code below just to explain why in
6449 * the DEBUG() logs)
6452 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6453 ldb_dn_get_linearized(msg->dn)) == 0) {
6454 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6455 GUID_equal(&ar->local_parent_guid,
6456 ar->objs->objects[ar->index_current].parent_guid)
6457 == false) {
6458 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6459 "despite incoming object changing parent to %s\n",
6460 ldb_dn_get_linearized(ar->search_msg->dn),
6461 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6462 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6463 &p_guid_remote)));
6465 } else {
6466 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6467 " and rejecting older rename to %s under %s\n",
6468 ldb_dn_get_linearized(ar->search_msg->dn),
6469 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6470 ldb_dn_get_linearized(msg->dn),
6471 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6472 &p_guid_remote)));
6475 * This assignment ensures that the strcmp()
6476 * and GUID_equal() calls in
6477 * replmd_replicated_apply_merge() avoids the
6478 * rename call
6480 ar->objs->objects[ar->index_current].parent_guid =
6481 &ar->local_parent_guid;
6483 msg->dn = ar->search_msg->dn;
6484 ret = replmd_replicated_apply_merge(ar);
6486 if (ret != LDB_SUCCESS) {
6487 return ldb_module_done(ar->req, NULL, NULL, ret);
6492 talloc_free(ares);
6493 return LDB_SUCCESS;
6497 * Stores the linked attributes received in the replication chunk - these get
6498 * applied at the end of the transaction. We also check that each linked
6499 * attribute is valid, i.e. source and target objects are known.
6501 static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
6503 int ret = LDB_SUCCESS;
6504 uint32_t i;
6505 struct ldb_module *module = ar->module;
6506 struct replmd_private *replmd_private =
6507 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6508 struct ldb_context *ldb;
6510 ldb = ldb_module_get_ctx(module);
6512 DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
6514 /* save away the linked attributes for the end of the transaction */
6515 for (i = 0; i < ar->objs->linked_attributes_count; i++) {
6516 struct la_entry *la_entry;
6518 if (replmd_private->la_ctx == NULL) {
6519 replmd_private->la_ctx = talloc_new(replmd_private);
6521 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6522 if (la_entry == NULL) {
6523 ldb_oom(ldb);
6524 return LDB_ERR_OPERATIONS_ERROR;
6526 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
6527 if (la_entry->la == NULL) {
6528 talloc_free(la_entry);
6529 ldb_oom(ldb);
6530 return LDB_ERR_OPERATIONS_ERROR;
6532 *la_entry->la = ar->objs->linked_attributes[i];
6533 la_entry->dsdb_repl_flags = ar->objs->dsdb_repl_flags;
6535 /* we need to steal the non-scalars so they stay
6536 around until the end of the transaction */
6537 talloc_steal(la_entry->la, la_entry->la->identifier);
6538 talloc_steal(la_entry->la, la_entry->la->value.blob);
6540 ret = replmd_verify_linked_attribute(ar, la_entry);
6542 if (ret != LDB_SUCCESS) {
6543 break;
6546 DLIST_ADD(replmd_private->la_list, la_entry);
6549 return ret;
6552 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6554 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6556 struct ldb_context *ldb;
6557 int ret;
6558 char *tmp_str;
6559 char *filter;
6560 struct ldb_request *search_req;
6561 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6562 "parentGUID", "instanceType",
6563 "replPropertyMetaData", "nTSecurityDescriptor",
6564 "isDeleted", NULL };
6565 struct GUID_txt_buf guid_str_buf;
6567 if (ar->index_current >= ar->objs->num_objects) {
6570 * Now that we've applied all the objects, check the new linked
6571 * attributes and store them (we apply them in .prepare_commit)
6573 ret = replmd_store_linked_attributes(ar);
6575 if (ret != LDB_SUCCESS) {
6576 return ret;
6579 /* done applying objects, move on to the next stage */
6580 return replmd_replicated_uptodate_vector(ar);
6583 ldb = ldb_module_get_ctx(ar->module);
6584 ar->search_msg = NULL;
6585 ar->isDeleted = false;
6587 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6588 &guid_str_buf);
6590 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6591 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6593 ret = ldb_build_search_req(&search_req,
6594 ldb,
6596 ar->objs->partition_dn,
6597 LDB_SCOPE_SUBTREE,
6598 filter,
6599 attrs,
6600 NULL,
6602 replmd_replicated_apply_search_callback,
6603 ar->req);
6604 LDB_REQ_SET_LOCATION(search_req);
6606 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
6608 if (ret != LDB_SUCCESS) {
6609 return ret;
6612 return ldb_next_request(ar->module, search_req);
6616 * This is essentially a wrapper for replmd_replicated_apply_next()
6618 * This is needed to ensure that both codepaths call this handler.
6620 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6622 struct ldb_dn *deleted_objects_dn;
6623 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6624 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
6625 &deleted_objects_dn);
6626 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
6628 * Do a delete here again, so that if there is
6629 * anything local that conflicts with this
6630 * object being deleted, it is removed. This
6631 * includes links. See MS-DRSR 4.1.10.6.9
6632 * UpdateObject.
6634 * If the object is already deleted, and there
6635 * is no more work required, it doesn't do
6636 * anything.
6639 /* This has been updated to point to the DN we eventually did the modify on */
6641 struct ldb_request *del_req;
6642 struct ldb_result *res;
6644 TALLOC_CTX *tmp_ctx = talloc_new(ar);
6645 if (!tmp_ctx) {
6646 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6647 return ret;
6650 res = talloc_zero(tmp_ctx, struct ldb_result);
6651 if (!res) {
6652 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6653 talloc_free(tmp_ctx);
6654 return ret;
6657 /* Build a delete request, which hopefully will artually turn into nothing */
6658 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
6659 msg->dn,
6660 NULL,
6661 res,
6662 ldb_modify_default_callback,
6663 ar->req);
6664 LDB_REQ_SET_LOCATION(del_req);
6665 if (ret != LDB_SUCCESS) {
6666 talloc_free(tmp_ctx);
6667 return ret;
6671 * This is the guts of the call, call back
6672 * into our delete code, but setting the
6673 * re_delete flag so we delete anything that
6674 * shouldn't be there on a deleted or recycled
6675 * object
6677 ret = replmd_delete_internals(ar->module, del_req, true);
6678 if (ret == LDB_SUCCESS) {
6679 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
6682 talloc_free(tmp_ctx);
6683 if (ret != LDB_SUCCESS) {
6684 return ret;
6688 ar->index_current++;
6689 return replmd_replicated_apply_next(ar);
6692 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
6693 struct ldb_reply *ares)
6695 struct ldb_context *ldb;
6696 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6697 struct replmd_replicated_request);
6698 ldb = ldb_module_get_ctx(ar->module);
6700 if (!ares) {
6701 return ldb_module_done(ar->req, NULL, NULL,
6702 LDB_ERR_OPERATIONS_ERROR);
6704 if (ares->error != LDB_SUCCESS) {
6705 return ldb_module_done(ar->req, ares->controls,
6706 ares->response, ares->error);
6709 if (ares->type != LDB_REPLY_DONE) {
6710 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
6711 return ldb_module_done(ar->req, NULL, NULL,
6712 LDB_ERR_OPERATIONS_ERROR);
6715 talloc_free(ares);
6717 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6720 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
6722 struct ldb_context *ldb;
6723 struct ldb_request *change_req;
6724 enum ndr_err_code ndr_err;
6725 struct ldb_message *msg;
6726 struct replUpToDateVectorBlob ouv;
6727 const struct ldb_val *ouv_value;
6728 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
6729 struct replUpToDateVectorBlob nuv;
6730 struct ldb_val nuv_value;
6731 struct ldb_message_element *nuv_el = NULL;
6732 struct ldb_message_element *orf_el = NULL;
6733 struct repsFromToBlob nrf;
6734 struct ldb_val *nrf_value = NULL;
6735 struct ldb_message_element *nrf_el = NULL;
6736 unsigned int i;
6737 uint32_t j,ni=0;
6738 bool found = false;
6739 time_t t = time(NULL);
6740 NTTIME now;
6741 int ret;
6742 uint32_t instanceType;
6744 ldb = ldb_module_get_ctx(ar->module);
6745 ruv = ar->objs->uptodateness_vector;
6746 ZERO_STRUCT(ouv);
6747 ouv.version = 2;
6748 ZERO_STRUCT(nuv);
6749 nuv.version = 2;
6751 unix_to_nt_time(&now, t);
6753 if (ar->search_msg == NULL) {
6754 /* this happens for a REPL_OBJ call where we are
6755 creating the target object by replicating it. The
6756 subdomain join code does this for the partition DN
6758 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
6759 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6762 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
6763 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
6764 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
6765 ldb_dn_get_linearized(ar->search_msg->dn)));
6766 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6770 * first create the new replUpToDateVector
6772 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
6773 if (ouv_value) {
6774 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
6775 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
6776 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6777 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6778 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6781 if (ouv.version != 2) {
6782 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6787 * the new uptodateness vector will at least
6788 * contain 1 entry, one for the source_dsa
6790 * plus optional values from our old vector and the one from the source_dsa
6792 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
6793 if (ruv) nuv.ctr.ctr2.count += ruv->count;
6794 nuv.ctr.ctr2.cursors = talloc_array(ar,
6795 struct drsuapi_DsReplicaCursor2,
6796 nuv.ctr.ctr2.count);
6797 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6799 /* first copy the old vector */
6800 for (i=0; i < ouv.ctr.ctr2.count; i++) {
6801 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
6802 ni++;
6805 /* merge in the source_dsa vector is available */
6806 for (i=0; (ruv && i < ruv->count); i++) {
6807 found = false;
6809 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6810 &ar->our_invocation_id)) {
6811 continue;
6814 for (j=0; j < ni; j++) {
6815 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6816 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
6817 continue;
6820 found = true;
6822 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
6823 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
6825 break;
6828 if (found) continue;
6830 /* if it's not there yet, add it */
6831 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
6832 ni++;
6836 * finally correct the size of the cursors array
6838 nuv.ctr.ctr2.count = ni;
6841 * sort the cursors
6843 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
6846 * create the change ldb_message
6848 msg = ldb_msg_new(ar);
6849 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6850 msg->dn = ar->search_msg->dn;
6852 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
6853 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
6854 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6855 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6856 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6858 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
6859 if (ret != LDB_SUCCESS) {
6860 return replmd_replicated_request_error(ar, ret);
6862 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
6865 * now create the new repsFrom value from the given repsFromTo1 structure
6867 ZERO_STRUCT(nrf);
6868 nrf.version = 1;
6869 nrf.ctr.ctr1 = *ar->objs->source_dsa;
6870 nrf.ctr.ctr1.last_attempt = now;
6871 nrf.ctr.ctr1.last_success = now;
6872 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
6875 * first see if we already have a repsFrom value for the current source dsa
6876 * if so we'll later replace this value
6878 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
6879 if (orf_el) {
6880 for (i=0; i < orf_el->num_values; i++) {
6881 struct repsFromToBlob *trf;
6883 trf = talloc(ar, struct repsFromToBlob);
6884 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6886 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
6887 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
6888 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6889 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6890 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6893 if (trf->version != 1) {
6894 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6898 * we compare the source dsa objectGUID not the invocation_id
6899 * because we want only one repsFrom value per source dsa
6900 * and when the invocation_id of the source dsa has changed we don't need
6901 * the old repsFrom with the old invocation_id
6903 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
6904 &ar->objs->source_dsa->source_dsa_obj_guid)) {
6905 talloc_free(trf);
6906 continue;
6909 talloc_free(trf);
6910 nrf_value = &orf_el->values[i];
6911 break;
6915 * copy over all old values to the new ldb_message
6917 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
6918 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6919 *nrf_el = *orf_el;
6923 * if we haven't found an old repsFrom value for the current source dsa
6924 * we'll add a new value
6926 if (!nrf_value) {
6927 struct ldb_val zero_value;
6928 ZERO_STRUCT(zero_value);
6929 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
6930 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6932 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
6935 /* we now fill the value which is already attached to ldb_message */
6936 ndr_err = ndr_push_struct_blob(nrf_value, msg,
6937 &nrf,
6938 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
6939 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6940 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6941 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6945 * the ldb_message_element for the attribute, has all the old values and the new one
6946 * so we'll replace the whole attribute with all values
6948 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
6950 if (CHECK_DEBUGLVL(4)) {
6951 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6952 LDB_CHANGETYPE_MODIFY,
6953 msg);
6954 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
6955 talloc_free(s);
6958 /* prepare the ldb_modify() request */
6959 ret = ldb_build_mod_req(&change_req,
6960 ldb,
6962 msg,
6963 ar->controls,
6965 replmd_replicated_uptodate_modify_callback,
6966 ar->req);
6967 LDB_REQ_SET_LOCATION(change_req);
6968 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6970 return ldb_next_request(ar->module, change_req);
6973 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
6974 struct ldb_reply *ares)
6976 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6977 struct replmd_replicated_request);
6978 int ret;
6980 if (!ares) {
6981 return ldb_module_done(ar->req, NULL, NULL,
6982 LDB_ERR_OPERATIONS_ERROR);
6984 if (ares->error != LDB_SUCCESS &&
6985 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6986 return ldb_module_done(ar->req, ares->controls,
6987 ares->response, ares->error);
6990 switch (ares->type) {
6991 case LDB_REPLY_ENTRY:
6992 ar->search_msg = talloc_steal(ar, ares->message);
6993 break;
6995 case LDB_REPLY_REFERRAL:
6996 /* we ignore referrals */
6997 break;
6999 case LDB_REPLY_DONE:
7000 ret = replmd_replicated_uptodate_modify(ar);
7001 if (ret != LDB_SUCCESS) {
7002 return ldb_module_done(ar->req, NULL, NULL, ret);
7006 talloc_free(ares);
7007 return LDB_SUCCESS;
7011 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
7013 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
7014 struct replmd_private *replmd_private =
7015 talloc_get_type_abort(ldb_module_get_private(ar->module),
7016 struct replmd_private);
7017 int ret;
7018 static const char *attrs[] = {
7019 "replUpToDateVector",
7020 "repsFrom",
7021 "instanceType",
7022 NULL
7024 struct ldb_request *search_req;
7026 ar->search_msg = NULL;
7029 * Let the caller know that we did an originating updates
7031 ar->objs->originating_updates = replmd_private->originating_updates;
7033 ret = ldb_build_search_req(&search_req,
7034 ldb,
7036 ar->objs->partition_dn,
7037 LDB_SCOPE_BASE,
7038 "(objectClass=*)",
7039 attrs,
7040 NULL,
7042 replmd_replicated_uptodate_search_callback,
7043 ar->req);
7044 LDB_REQ_SET_LOCATION(search_req);
7045 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7047 return ldb_next_request(ar->module, search_req);
7052 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
7054 struct ldb_context *ldb;
7055 struct dsdb_extended_replicated_objects *objs;
7056 struct replmd_replicated_request *ar;
7057 struct ldb_control **ctrls;
7058 int ret;
7060 ldb = ldb_module_get_ctx(module);
7062 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
7064 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
7065 if (!objs) {
7066 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
7067 return LDB_ERR_PROTOCOL_ERROR;
7070 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
7071 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
7072 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
7073 return LDB_ERR_PROTOCOL_ERROR;
7076 ar = replmd_ctx_init(module, req);
7077 if (!ar)
7078 return LDB_ERR_OPERATIONS_ERROR;
7080 /* Set the flags to have the replmd_op_callback run over the full set of objects */
7081 ar->apply_mode = true;
7082 ar->objs = objs;
7083 ar->schema = dsdb_get_schema(ldb, ar);
7084 if (!ar->schema) {
7085 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
7086 talloc_free(ar);
7087 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
7088 return LDB_ERR_CONSTRAINT_VIOLATION;
7091 ctrls = req->controls;
7093 if (req->controls) {
7094 req->controls = talloc_memdup(ar, req->controls,
7095 talloc_get_size(req->controls));
7096 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7099 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
7100 if (ret != LDB_SUCCESS) {
7101 return ret;
7104 /* If this change contained linked attributes in the body
7105 * (rather than in the links section) we need to update
7106 * backlinks in linked_attributes */
7107 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
7108 if (ret != LDB_SUCCESS) {
7109 return ret;
7112 ar->controls = req->controls;
7113 req->controls = ctrls;
7115 return replmd_replicated_apply_next(ar);
7119 * Checks how to handle an missing target - either we need to fail the
7120 * replication and retry with GET_TGT, ignore the link and continue, or try to
7121 * add a partial link to an unknown target.
7123 static int replmd_allow_missing_target(struct ldb_module *module,
7124 TALLOC_CTX *mem_ctx,
7125 struct ldb_dn *target_dn,
7126 struct ldb_dn *source_dn,
7127 bool is_obj_commit,
7128 struct GUID *guid,
7129 uint32_t dsdb_repl_flags,
7130 bool *ignore_link,
7131 const char * missing_str)
7133 struct ldb_context *ldb = ldb_module_get_ctx(module);
7134 bool is_in_same_nc;
7137 * we may not be able to resolve link targets properly when
7138 * dealing with subsets of objects, e.g. the source is a
7139 * critical object and the target isn't
7141 * TODO:
7142 * When we implement Trusted Domains we need to consider
7143 * whether they get treated as an incomplete replica here or not
7145 if (dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
7148 * Ignore the link. We don't increase the highwater-mark in
7149 * the object subset cases, so subsequent replications should
7150 * resolve any missing links
7152 DEBUG(2, ("%s target %s linked from %s\n", missing_str,
7153 ldb_dn_get_linearized(target_dn),
7154 ldb_dn_get_linearized(source_dn)));
7155 *ignore_link = true;
7156 return LDB_SUCCESS;
7159 if (dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
7162 * target should already be up-to-date so there's no point in
7163 * retrying. This could be due to bad timing, or if a target
7164 * on a one-way link was deleted. We ignore the link rather
7165 * than failing the replication cycle completely
7167 *ignore_link = true;
7168 DBG_WARNING("%s is %s but up to date. Ignoring link from %s\n",
7169 ldb_dn_get_linearized(target_dn), missing_str,
7170 ldb_dn_get_linearized(source_dn));
7171 return LDB_SUCCESS;
7174 is_in_same_nc = dsdb_objects_have_same_nc(ldb,
7175 mem_ctx,
7176 source_dn,
7177 target_dn);
7178 if (is_in_same_nc) {
7179 /* fail the replication and retry with GET_TGT */
7180 ldb_asprintf_errstring(ldb, "%s target %s GUID %s linked from %s\n",
7181 missing_str,
7182 ldb_dn_get_linearized(target_dn),
7183 GUID_string(mem_ctx, guid),
7184 ldb_dn_get_linearized(source_dn));
7185 return LDB_ERR_NO_SUCH_OBJECT;
7189 * The target of the cross-partition link is missing. Continue
7190 * and try to at least add the forward-link. This isn't great,
7191 * but a partial link can be fixed by dbcheck, so it's better
7192 * than dropping the link completely.
7194 *ignore_link = false;
7196 if (is_obj_commit) {
7199 * Only log this when we're actually committing the objects.
7200 * This avoids spurious logs, i.e. if we're just verifying the
7201 * received link during a join.
7203 DBG_WARNING("%s cross-partition target %s linked from %s\n",
7204 missing_str, ldb_dn_get_linearized(target_dn),
7205 ldb_dn_get_linearized(source_dn));
7208 return LDB_SUCCESS;
7212 * Checks that the target object for a linked attribute exists.
7213 * @param guid returns the target object's GUID (is returned)if it exists)
7214 * @param ignore_link set to true if the linked attribute should be ignored
7215 * (i.e. the target doesn't exist, but that it's OK to skip the link)
7217 static int replmd_check_target_exists(struct ldb_module *module,
7218 struct dsdb_dn *dsdb_dn,
7219 struct la_entry *la_entry,
7220 struct ldb_dn *source_dn,
7221 bool is_obj_commit,
7222 struct GUID *guid,
7223 bool *ignore_link)
7225 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7226 struct ldb_context *ldb = ldb_module_get_ctx(module);
7227 struct ldb_result *target_res;
7228 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7229 const char *attrs[] = { "isDeleted", "isRecycled", NULL };
7230 NTSTATUS ntstatus;
7231 int ret;
7232 enum deletion_state target_deletion_state = OBJECT_REMOVED;
7233 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
7235 *ignore_link = false;
7236 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
7238 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
7241 * This strange behaviour (allowing a NULL/missing
7242 * GUID) originally comes from:
7244 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
7245 * Author: Andrew Tridgell <tridge@samba.org>
7246 * Date: Mon Dec 21 21:21:55 2009 +1100
7248 * s4-drs: cope better with NULL GUIDS from DRS
7250 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
7251 * need to match by DN if possible when seeing if we should update an
7252 * existing link.
7254 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
7256 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
7257 dsdb_dn->dn, attrs,
7258 DSDB_FLAG_NEXT_MODULE |
7259 DSDB_SEARCH_SHOW_RECYCLED |
7260 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7261 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7262 NULL);
7263 } else if (!NT_STATUS_IS_OK(ntstatus)) {
7264 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
7265 la->attid,
7266 ldb_dn_get_linearized(dsdb_dn->dn),
7267 ldb_dn_get_linearized(source_dn));
7268 talloc_free(tmp_ctx);
7269 return LDB_ERR_OPERATIONS_ERROR;
7270 } else {
7271 ret = dsdb_module_search(module, tmp_ctx, &target_res,
7272 NULL, LDB_SCOPE_SUBTREE,
7273 attrs,
7274 DSDB_FLAG_NEXT_MODULE |
7275 DSDB_SEARCH_SHOW_RECYCLED |
7276 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7277 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7278 NULL,
7279 "objectGUID=%s",
7280 GUID_string(tmp_ctx, guid));
7283 if (ret != LDB_SUCCESS) {
7284 ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
7285 GUID_string(tmp_ctx, guid),
7286 ldb_errstring(ldb));
7287 talloc_free(tmp_ctx);
7288 return ret;
7291 if (target_res->count == 0) {
7294 * target object is unknown. Check whether to ignore the link,
7295 * fail the replication, or add a partial link
7297 ret = replmd_allow_missing_target(module, tmp_ctx, dsdb_dn->dn,
7298 source_dn, is_obj_commit, guid,
7299 la_entry->dsdb_repl_flags,
7300 ignore_link, "Unknown");
7302 } else if (target_res->count != 1) {
7303 ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
7304 GUID_string(tmp_ctx, guid));
7305 ret = LDB_ERR_OPERATIONS_ERROR;
7306 } else {
7307 struct ldb_message *target_msg = target_res->msgs[0];
7309 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
7311 /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
7312 replmd_deletion_state(module, target_msg,
7313 &target_deletion_state, NULL);
7316 * Check for deleted objects as per MS-DRSR 4.1.10.6.14
7317 * ProcessLinkValue(). Link updates should not be sent for
7318 * recycled and tombstone objects (deleting the links should
7319 * happen when we delete the object). This probably means our
7320 * copy of the target object isn't up to date.
7322 if (target_deletion_state >= OBJECT_RECYCLED) {
7325 * target object is deleted. Check whether to ignore the
7326 * link, fail the replication, or add a partial link
7328 ret = replmd_allow_missing_target(module, tmp_ctx,
7329 dsdb_dn->dn, source_dn,
7330 is_obj_commit, guid,
7331 la_entry->dsdb_repl_flags,
7332 ignore_link, "Deleted");
7336 talloc_free(tmp_ctx);
7337 return ret;
7341 * Extracts the key details about the source/target object for a
7342 * linked-attribute entry.
7343 * This returns the following details:
7344 * @param ret_attr the schema details for the linked attribute
7345 * @param source_msg the search result for the source object
7346 * @param target_dsdb_dn the unpacked DN info for the target object
7348 static int replmd_extract_la_entry_details(struct ldb_module *module,
7349 struct la_entry *la_entry,
7350 TALLOC_CTX *mem_ctx,
7351 const struct dsdb_attribute **ret_attr,
7352 struct ldb_message **source_msg,
7353 struct dsdb_dn **target_dsdb_dn)
7355 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7356 struct ldb_context *ldb = ldb_module_get_ctx(module);
7357 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7358 int ret;
7359 const struct dsdb_attribute *attr;
7360 WERROR status;
7361 struct ldb_result *res;
7362 const char *attrs[4];
7365 linked_attributes[0]:
7366 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
7367 identifier : *
7368 identifier: struct drsuapi_DsReplicaObjectIdentifier
7369 __ndr_size : 0x0000003a (58)
7370 __ndr_size_sid : 0x00000000 (0)
7371 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7372 sid : S-0-0
7373 __ndr_size_dn : 0x00000000 (0)
7374 dn : ''
7375 attid : DRSUAPI_ATTID_member (0x1F)
7376 value: struct drsuapi_DsAttributeValue
7377 __ndr_size : 0x0000007e (126)
7378 blob : *
7379 blob : DATA_BLOB length=126
7380 flags : 0x00000001 (1)
7381 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7382 originating_add_time : Wed Sep 2 22:20:01 2009 EST
7383 meta_data: struct drsuapi_DsReplicaMetaData
7384 version : 0x00000015 (21)
7385 originating_change_time : Wed Sep 2 23:39:07 2009 EST
7386 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7387 originating_usn : 0x000000000001e19c (123292)
7389 (for cases where the link is to a normal DN)
7390 &target: struct drsuapi_DsReplicaObjectIdentifier3
7391 __ndr_size : 0x0000007e (126)
7392 __ndr_size_sid : 0x0000001c (28)
7393 guid : 7639e594-db75-4086-b0d4-67890ae46031
7394 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7395 __ndr_size_dn : 0x00000022 (34)
7396 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7399 /* find the attribute being modified */
7400 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
7401 if (attr == NULL) {
7402 struct GUID_txt_buf guid_str;
7403 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7404 la->attid,
7405 GUID_buf_string(&la->identifier->guid,
7406 &guid_str));
7407 return LDB_ERR_OPERATIONS_ERROR;
7410 attrs[0] = attr->lDAPDisplayName;
7411 attrs[1] = "isDeleted";
7412 attrs[2] = "isRecycled";
7413 attrs[3] = NULL;
7416 * get the existing message from the db for the object with
7417 * this GUID, returning attribute being modified. We will then
7418 * use this msg as the basis for a modify call
7420 ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
7421 DSDB_FLAG_NEXT_MODULE |
7422 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7423 DSDB_SEARCH_SHOW_RECYCLED |
7424 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
7425 DSDB_SEARCH_REVEAL_INTERNALS,
7426 NULL,
7427 "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
7428 if (ret != LDB_SUCCESS) {
7429 return ret;
7431 if (res->count != 1) {
7432 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
7433 GUID_string(mem_ctx, &la->identifier->guid));
7434 return LDB_ERR_NO_SUCH_OBJECT;
7437 *source_msg = res->msgs[0];
7439 /* the value blob for the attribute holds the target object DN */
7440 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx, la->value.blob, target_dsdb_dn);
7441 if (!W_ERROR_IS_OK(status)) {
7442 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7443 attr->lDAPDisplayName,
7444 ldb_dn_get_linearized(res->msgs[0]->dn),
7445 win_errstr(status));
7446 return LDB_ERR_OPERATIONS_ERROR;
7449 *ret_attr = attr;
7451 return LDB_SUCCESS;
7455 * Verifies the source and target objects are known for a linked attribute
7457 static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
7458 struct la_entry *la)
7460 int ret = LDB_SUCCESS;
7461 TALLOC_CTX *tmp_ctx = talloc_new(la);
7462 struct ldb_module *module = ar->module;
7463 struct ldb_message *src_msg;
7464 const struct dsdb_attribute *attr;
7465 struct dsdb_dn *tgt_dsdb_dn;
7466 struct GUID guid = GUID_zero();
7467 bool dummy;
7469 ret = replmd_extract_la_entry_details(module, la, tmp_ctx, &attr,
7470 &src_msg, &tgt_dsdb_dn);
7473 * When we fail to find the source object, the error code we pass
7474 * back here is really important. It flags back to the callers to
7475 * retry this request with DRSUAPI_DRS_GET_ANC. This case should
7476 * never happen if we're replicating from a Samba DC, but it is
7477 * needed to talk to a Windows DC
7479 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7480 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT);
7483 if (ret != LDB_SUCCESS) {
7484 talloc_free(tmp_ctx);
7485 return ret;
7489 * We can skip the target object checks if we're only syncing critical
7490 * objects, or we know the target is up-to-date. If either case, we
7491 * still continue even if the target doesn't exist
7493 if ((la->dsdb_repl_flags & (DSDB_REPL_FLAG_OBJECT_SUBSET |
7494 DSDB_REPL_FLAG_TARGETS_UPTODATE)) == 0) {
7496 ret = replmd_check_target_exists(module, tgt_dsdb_dn, la,
7497 src_msg->dn, false, &guid,
7498 &dummy);
7502 * When we fail to find the target object, the error code we pass
7503 * back here is really important. It flags back to the callers to
7504 * retry this request with DRSUAPI_DRS_GET_TGT
7506 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7507 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
7510 talloc_free(tmp_ctx);
7511 return ret;
7515 * Finds the current active Parsed-DN value for a single-valued linked
7516 * attribute, if one exists.
7517 * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
7518 * @returns LDB_SUCCESS (regardless of whether a match was found), unless
7519 * an error occurred
7521 static int replmd_get_active_singleval_link(struct ldb_module *module,
7522 TALLOC_CTX *mem_ctx,
7523 struct parsed_dn pdn_list[],
7524 unsigned int count,
7525 const struct dsdb_attribute *attr,
7526 struct parsed_dn **ret_pdn)
7528 unsigned int i;
7530 *ret_pdn = NULL;
7532 if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE)) {
7534 /* nothing to do for multi-valued linked attributes */
7535 return LDB_SUCCESS;
7538 for (i = 0; i < count; i++) {
7539 int ret = LDB_SUCCESS;
7540 struct parsed_dn *pdn = &pdn_list[i];
7542 /* skip any inactive links */
7543 if (dsdb_dn_is_deleted_val(pdn->v)) {
7544 continue;
7547 /* we've found an active value for this attribute */
7548 *ret_pdn = pdn;
7550 if (pdn->dsdb_dn == NULL) {
7551 struct ldb_context *ldb = ldb_module_get_ctx(module);
7553 ret = really_parse_trusted_dn(mem_ctx, ldb, pdn,
7554 attr->syntax->ldap_oid);
7557 return ret;
7560 /* no active link found */
7561 return LDB_SUCCESS;
7565 * @returns true if the replication linked attribute info is newer than we
7566 * already have in our DB
7567 * @param pdn the existing linked attribute info in our DB
7568 * @param la the new linked attribute info received during replication
7570 static bool replmd_link_update_is_newer(struct parsed_dn *pdn,
7571 struct drsuapi_DsReplicaLinkedAttribute *la)
7573 /* see if this update is newer than what we have already */
7574 struct GUID invocation_id = GUID_zero();
7575 uint32_t version = 0;
7576 NTTIME change_time = 0;
7578 if (pdn == NULL) {
7580 /* no existing info so update is newer */
7581 return true;
7584 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
7585 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
7586 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
7588 return replmd_update_is_newer(&invocation_id,
7589 &la->meta_data.originating_invocation_id,
7590 version,
7591 la->meta_data.version,
7592 change_time,
7593 la->meta_data.originating_change_time);
7597 * Marks an existing linked attribute value as deleted in the DB
7598 * @param pdn the parsed-DN of the target-value to delete
7600 static int replmd_delete_link_value(struct ldb_module *module,
7601 struct replmd_private *replmd_private,
7602 TALLOC_CTX *mem_ctx,
7603 struct ldb_dn *src_obj_dn,
7604 const struct dsdb_schema *schema,
7605 const struct dsdb_attribute *attr,
7606 uint64_t seq_num,
7607 bool is_active,
7608 struct GUID *target_guid,
7609 struct dsdb_dn *target_dsdb_dn,
7610 struct ldb_val *output_val)
7612 struct ldb_context *ldb = ldb_module_get_ctx(module);
7613 time_t t;
7614 NTTIME now;
7615 const struct GUID *invocation_id = NULL;
7616 int ret;
7618 t = time(NULL);
7619 unix_to_nt_time(&now, t);
7621 invocation_id = samdb_ntds_invocation_id(ldb);
7622 if (invocation_id == NULL) {
7623 return LDB_ERR_OPERATIONS_ERROR;
7626 /* if the existing link is active, remove its backlink */
7627 if (is_active) {
7629 ret = replmd_add_backlink(module, replmd_private, schema,
7630 src_obj_dn, target_guid, false,
7631 attr, NULL);
7632 if (ret != LDB_SUCCESS) {
7633 return ret;
7637 /* mark the existing value as deleted */
7638 ret = replmd_update_la_val(mem_ctx, output_val, target_dsdb_dn,
7639 target_dsdb_dn, invocation_id, seq_num,
7640 seq_num, now, true);
7641 return ret;
7645 * Checks for a conflict in single-valued link attributes, and tries to
7646 * resolve the problem if possible.
7648 * Single-valued links should only ever have one active value. If we already
7649 * have an active link value, and during replication we receive an active link
7650 * value for a different target DN, then we need to resolve this inconsistency
7651 * and determine which value should be active. If the received info is better/
7652 * newer than the existing link attribute, then we need to set our existing
7653 * link as deleted. If the received info is worse/older, then we should continue
7654 * to add it, but set it as an inactive link.
7656 * Note that this is a corner-case that is unlikely to happen (but if it does
7657 * happen, we don't want it to break replication completely).
7659 * @param pdn_being_modified the parsed DN corresponding to the received link
7660 * target (note this is NULL if the link does not already exist in our DB)
7661 * @param pdn_list all the source object's Parsed-DNs for this attribute, i.e.
7662 * any existing active or inactive values for the attribute in our DB.
7663 * @param dsdb_dn the target DN for the received link attribute
7664 * @param add_as_inactive gets set to true if the received link is worse than
7665 * the existing link - it should still be added, but as an inactive link.
7667 static int replmd_check_singleval_la_conflict(struct ldb_module *module,
7668 struct replmd_private *replmd_private,
7669 TALLOC_CTX *mem_ctx,
7670 struct ldb_dn *src_obj_dn,
7671 struct drsuapi_DsReplicaLinkedAttribute *la,
7672 struct dsdb_dn *dsdb_dn,
7673 struct parsed_dn *pdn_being_modified,
7674 struct parsed_dn *pdn_list,
7675 struct ldb_message_element *old_el,
7676 const struct dsdb_schema *schema,
7677 const struct dsdb_attribute *attr,
7678 uint64_t seq_num,
7679 bool *add_as_inactive)
7681 struct parsed_dn *active_pdn = NULL;
7682 bool update_is_newer = false;
7683 int ret;
7686 * check if there's a conflict for single-valued links, i.e. an active
7687 * linked attribute already exists, but it has a different target value
7689 ret = replmd_get_active_singleval_link(module, mem_ctx, pdn_list,
7690 old_el->num_values, attr,
7691 &active_pdn);
7693 if (ret != LDB_SUCCESS) {
7694 return ret;
7698 * If no active value exists (or the received info is for the currently
7699 * active value), then no conflict exists
7701 if (active_pdn == NULL || active_pdn == pdn_being_modified) {
7702 return LDB_SUCCESS;
7705 DBG_WARNING("Link conflict for %s attribute on %s\n",
7706 attr->lDAPDisplayName, ldb_dn_get_linearized(src_obj_dn));
7708 /* Work out how to resolve the conflict based on which info is better */
7709 update_is_newer = replmd_link_update_is_newer(active_pdn, la);
7711 if (update_is_newer) {
7712 DBG_WARNING("Using received value %s, over existing target %s\n",
7713 ldb_dn_get_linearized(dsdb_dn->dn),
7714 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn));
7717 * Delete our existing active link. The received info will then
7718 * be added (through normal link processing) as the active value
7720 ret = replmd_delete_link_value(module, replmd_private, old_el,
7721 src_obj_dn, schema, attr,
7722 seq_num, true, &active_pdn->guid,
7723 active_pdn->dsdb_dn,
7724 active_pdn->v);
7726 if (ret != LDB_SUCCESS) {
7727 return ret;
7729 } else {
7730 DBG_WARNING("Using existing target %s, over received value %s\n",
7731 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn),
7732 ldb_dn_get_linearized(dsdb_dn->dn));
7735 * we want to keep our existing active link and add the
7736 * received link as inactive
7738 *add_as_inactive = true;
7741 return LDB_SUCCESS;
7745 process one linked attribute structure
7747 static int replmd_process_linked_attribute(struct ldb_module *module,
7748 struct replmd_private *replmd_private,
7749 struct la_entry *la_entry,
7750 struct ldb_request *parent)
7752 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7753 struct ldb_context *ldb = ldb_module_get_ctx(module);
7754 struct ldb_message *msg;
7755 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7756 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
7757 int ret;
7758 const struct dsdb_attribute *attr;
7759 struct dsdb_dn *dsdb_dn;
7760 uint64_t seq_num = 0;
7761 struct ldb_message_element *old_el;
7762 time_t t = time(NULL);
7763 struct parsed_dn *pdn_list, *pdn, *next;
7764 struct GUID guid = GUID_zero();
7765 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
7766 bool ignore_link;
7767 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
7768 struct dsdb_dn *old_dsdb_dn = NULL;
7769 struct ldb_val *val_to_update = NULL;
7770 bool add_as_inactive = false;
7773 * get the attribute being modified, the search result for the source object,
7774 * and the target object's DN details
7776 ret = replmd_extract_la_entry_details(module, la_entry, tmp_ctx, &attr,
7777 &msg, &dsdb_dn);
7779 if (ret != LDB_SUCCESS) {
7780 talloc_free(tmp_ctx);
7781 return ret;
7785 * Check for deleted objects per MS-DRSR 4.1.10.6.14
7786 * ProcessLinkValue, because link updates are not applied to
7787 * recycled and tombstone objects. We don't have to delete
7788 * any existing link, that should have happened when the
7789 * object deletion was replicated or initiated.
7791 replmd_deletion_state(module, msg, &deletion_state, NULL);
7793 if (deletion_state >= OBJECT_RECYCLED) {
7794 talloc_free(tmp_ctx);
7795 return LDB_SUCCESS;
7798 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7799 if (old_el == NULL) {
7800 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
7801 if (ret != LDB_SUCCESS) {
7802 ldb_module_oom(module);
7803 talloc_free(tmp_ctx);
7804 return LDB_ERR_OPERATIONS_ERROR;
7806 } else {
7807 old_el->flags = LDB_FLAG_MOD_REPLACE;
7810 /* parse the existing links */
7811 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx, old_el, &pdn_list,
7812 attr->syntax->ldap_oid, parent);
7814 if (ret != LDB_SUCCESS) {
7815 talloc_free(tmp_ctx);
7816 return ret;
7819 ret = replmd_check_target_exists(module, dsdb_dn, la_entry, msg->dn,
7820 true, &guid, &ignore_link);
7822 if (ret != LDB_SUCCESS) {
7823 talloc_free(tmp_ctx);
7824 return ret;
7828 * there are some cases where the target object doesn't exist, but it's
7829 * OK to ignore the linked attribute
7831 if (ignore_link) {
7832 talloc_free(tmp_ctx);
7833 return ret;
7836 /* see if this link already exists */
7837 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
7838 &guid,
7839 dsdb_dn->dn,
7840 dsdb_dn->extra_part, 0,
7841 &pdn, &next,
7842 attr->syntax->ldap_oid,
7843 true);
7844 if (ret != LDB_SUCCESS) {
7845 talloc_free(tmp_ctx);
7846 return ret;
7849 if (!replmd_link_update_is_newer(pdn, la)) {
7850 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
7851 old_el->name, ldb_dn_get_linearized(msg->dn),
7852 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
7853 talloc_free(tmp_ctx);
7854 return LDB_SUCCESS;
7857 /* get a seq_num for this change */
7858 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
7859 if (ret != LDB_SUCCESS) {
7860 talloc_free(tmp_ctx);
7861 return ret;
7865 * check for single-valued link conflicts, i.e. an active linked
7866 * attribute already exists, but it has a different target value
7868 if (active) {
7869 ret = replmd_check_singleval_la_conflict(module, replmd_private,
7870 tmp_ctx, msg->dn, la,
7871 dsdb_dn, pdn, pdn_list,
7872 old_el, schema, attr,
7873 seq_num,
7874 &add_as_inactive);
7875 if (ret != LDB_SUCCESS) {
7876 talloc_free(tmp_ctx);
7877 return ret;
7881 if (pdn != NULL) {
7882 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
7884 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
7885 /* remove the existing backlink */
7886 ret = replmd_add_backlink(module, replmd_private,
7887 schema,
7888 msg->dn,
7889 &pdn->guid, false, attr,
7890 parent);
7891 if (ret != LDB_SUCCESS) {
7892 talloc_free(tmp_ctx);
7893 return ret;
7897 val_to_update = pdn->v;
7898 old_dsdb_dn = pdn->dsdb_dn;
7900 } else {
7901 unsigned offset;
7904 * We know where the new one needs to be, from the *next
7905 * pointer into pdn_list.
7907 if (next == NULL) {
7908 offset = old_el->num_values;
7909 } else {
7910 if (next->dsdb_dn == NULL) {
7911 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
7912 attr->syntax->ldap_oid);
7913 if (ret != LDB_SUCCESS) {
7914 return ret;
7917 offset = next - pdn_list;
7918 if (offset > old_el->num_values) {
7919 talloc_free(tmp_ctx);
7920 return LDB_ERR_OPERATIONS_ERROR;
7924 old_el->values = talloc_realloc(msg->elements, old_el->values,
7925 struct ldb_val, old_el->num_values+1);
7926 if (!old_el->values) {
7927 ldb_module_oom(module);
7928 return LDB_ERR_OPERATIONS_ERROR;
7931 if (offset != old_el->num_values) {
7932 memmove(&old_el->values[offset + 1], &old_el->values[offset],
7933 (old_el->num_values - offset) * sizeof(old_el->values[0]));
7936 old_el->num_values++;
7938 val_to_update = &old_el->values[offset];
7939 old_dsdb_dn = NULL;
7942 /* set the link attribute's value to the info that was received */
7943 ret = replmd_set_la_val(tmp_ctx, val_to_update, dsdb_dn, old_dsdb_dn,
7944 &la->meta_data.originating_invocation_id,
7945 la->meta_data.originating_usn, seq_num,
7946 la->meta_data.originating_change_time,
7947 la->meta_data.version,
7948 !active);
7949 if (ret != LDB_SUCCESS) {
7950 talloc_free(tmp_ctx);
7951 return ret;
7954 if (add_as_inactive) {
7956 /* Set the new link as inactive/deleted to avoid conflicts */
7957 ret = replmd_delete_link_value(module, replmd_private, old_el,
7958 msg->dn, schema, attr, seq_num,
7959 false, &guid, dsdb_dn,
7960 val_to_update);
7962 if (ret != LDB_SUCCESS) {
7963 talloc_free(tmp_ctx);
7964 return ret;
7967 } else if (active) {
7969 /* if the new link is active, then add the new backlink */
7970 ret = replmd_add_backlink(module, replmd_private,
7971 schema,
7972 msg->dn,
7973 &guid, true, attr,
7974 parent);
7975 if (ret != LDB_SUCCESS) {
7976 talloc_free(tmp_ctx);
7977 return ret;
7981 /* we only change whenChanged and uSNChanged if the seq_num
7982 has changed */
7983 ret = add_time_element(msg, "whenChanged", t);
7984 if (ret != LDB_SUCCESS) {
7985 talloc_free(tmp_ctx);
7986 ldb_operr(ldb);
7987 return ret;
7990 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
7991 if (ret != LDB_SUCCESS) {
7992 talloc_free(tmp_ctx);
7993 ldb_operr(ldb);
7994 return ret;
7997 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7998 if (old_el == NULL) {
7999 talloc_free(tmp_ctx);
8000 return ldb_operr(ldb);
8003 ret = dsdb_check_single_valued_link(attr, old_el);
8004 if (ret != LDB_SUCCESS) {
8005 talloc_free(tmp_ctx);
8006 return ret;
8009 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
8011 ret = linked_attr_modify(module, msg, parent);
8012 if (ret != LDB_SUCCESS) {
8013 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
8014 ldb_errstring(ldb),
8015 ldb_ldif_message_redacted_string(ldb,
8016 tmp_ctx,
8017 LDB_CHANGETYPE_MODIFY,
8018 msg));
8019 talloc_free(tmp_ctx);
8020 return ret;
8023 talloc_free(tmp_ctx);
8025 return ret;
8028 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
8030 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
8031 return replmd_extended_replicated_objects(module, req);
8034 return ldb_next_request(module, req);
8039 we hook into the transaction operations to allow us to
8040 perform the linked attribute updates at the end of the whole
8041 transaction. This allows a forward linked attribute to be created
8042 before the object is created. During a vampire, w2k8 sends us linked
8043 attributes before the objects they are part of.
8045 static int replmd_start_transaction(struct ldb_module *module)
8047 /* create our private structure for this transaction */
8048 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
8049 struct replmd_private);
8050 replmd_txn_cleanup(replmd_private);
8052 /* free any leftover mod_usn records from cancelled
8053 transactions */
8054 while (replmd_private->ncs) {
8055 struct nc_entry *e = replmd_private->ncs;
8056 DLIST_REMOVE(replmd_private->ncs, e);
8057 talloc_free(e);
8060 replmd_private->originating_updates = false;
8062 return ldb_next_start_trans(module);
8066 on prepare commit we loop over our queued la_context structures and
8067 apply each of them
8069 static int replmd_prepare_commit(struct ldb_module *module)
8071 struct replmd_private *replmd_private =
8072 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8073 struct la_entry *la, *prev;
8074 int ret;
8077 * Walk the list of linked attributes from DRS replication.
8079 * We walk backwards, to do the first entry first, as we
8080 * added the entries with DLIST_ADD() which puts them at the
8081 * start of the list
8083 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
8084 prev = DLIST_PREV(la);
8085 DLIST_REMOVE(replmd_private->la_list, la);
8086 ret = replmd_process_linked_attribute(module, replmd_private,
8087 la, NULL);
8088 if (ret != LDB_SUCCESS) {
8089 replmd_txn_cleanup(replmd_private);
8090 return ret;
8094 replmd_txn_cleanup(replmd_private);
8096 /* possibly change @REPLCHANGED */
8097 ret = replmd_notify_store(module, NULL);
8098 if (ret != LDB_SUCCESS) {
8099 return ret;
8102 return ldb_next_prepare_commit(module);
8105 static int replmd_del_transaction(struct ldb_module *module)
8107 struct replmd_private *replmd_private =
8108 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8109 replmd_txn_cleanup(replmd_private);
8111 return ldb_next_del_trans(module);
8115 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
8116 .name = "repl_meta_data",
8117 .init_context = replmd_init,
8118 .add = replmd_add,
8119 .modify = replmd_modify,
8120 .rename = replmd_rename,
8121 .del = replmd_delete,
8122 .extended = replmd_extended,
8123 .start_transaction = replmd_start_transaction,
8124 .prepare_commit = replmd_prepare_commit,
8125 .del_transaction = replmd_del_transaction,
8128 int ldb_repl_meta_data_module_init(const char *version)
8130 LDB_MODULE_CHECK_VERSION(version);
8131 return ldb_register_module(&ldb_repl_meta_data_module_ops);