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/>.
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
35 * Author: Stefan Metzmacher
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"
53 #include "lib/util/binsearch.h"
58 #define DBGC_CLASS DBGC_DRS_REPL
60 /* the RMD_VERSION for linked attributes starts from 1 */
61 #define RMD_VERSION_INITIAL 1
64 * It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
65 * Deleted Objects Container
67 static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME
= 2650466015990000000ULL;
69 struct replmd_private
{
71 struct la_group
*la_list
;
73 struct nc_entry
*prev
, *next
;
76 uint64_t mod_usn_urgent
;
78 struct ldb_dn
*schema_dn
;
79 bool originating_updates
;
82 uint32_t num_processed
;
83 bool recyclebin_enabled
;
84 bool recyclebin_state_known
;
88 * groups link attributes together by source-object and attribute-ID,
89 * to improve processing efficiency (i.e. for 'member' attribute, which
90 * could have 100s or 1000s of links).
91 * Note this grouping is best effort - the same source object could still
92 * correspond to several la_groups (a lot depends on the order DRS sends
93 * the links in). The groups currently don't span replication chunks (which
94 * caps the size to ~1500 links by default).
97 struct la_group
*next
, *prev
;
98 struct la_entry
*la_entries
;
102 struct la_entry
*next
, *prev
;
103 struct drsuapi_DsReplicaLinkedAttribute
*la
;
104 uint32_t dsdb_repl_flags
;
107 struct replmd_replicated_request
{
108 struct ldb_module
*module
;
109 struct ldb_request
*req
;
111 const struct dsdb_schema
*schema
;
112 struct GUID our_invocation_id
;
114 /* the controls we pass down */
115 struct ldb_control
**controls
;
118 * Backlinks for the replmd_add() case (we want to create
119 * backlinks after creating the user, but before the end of
122 struct la_backlink
*la_backlinks
;
124 /* details for the mode where we apply a bunch of inbound replication meessages */
126 uint32_t index_current
;
127 struct dsdb_extended_replicated_objects
*objs
;
129 struct ldb_message
*search_msg
;
130 struct GUID local_parent_guid
;
141 * the result of replmd_process_linked_attribute(): either there was no change
142 * (update was ignored), a new link was added (either inactive or active), or
143 * an existing link was modified (active/inactive status may have changed).
148 LINK_CHANGE_MODIFIED
,
149 } replmd_link_changed
;
151 static int replmd_replicated_apply_merge(struct replmd_replicated_request
*ar
);
152 static int replmd_delete_internals(struct ldb_module
*module
, struct ldb_request
*req
, bool re_delete
);
153 static int replmd_check_upgrade_links(struct ldb_context
*ldb
,
154 struct parsed_dn
*dns
, uint32_t count
,
155 struct ldb_message_element
*el
,
156 const char *ldap_oid
);
157 static int replmd_verify_link_target(struct replmd_replicated_request
*ar
,
159 struct la_entry
*la_entry
,
160 struct ldb_dn
*src_dn
,
161 const struct dsdb_attribute
*attr
);
162 static int replmd_get_la_entry_source(struct ldb_module
*module
,
163 struct la_entry
*la_entry
,
165 const struct dsdb_attribute
**ret_attr
,
166 struct ldb_message
**source_msg
);
167 static int replmd_set_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
, struct dsdb_dn
*dsdb_dn
,
168 struct dsdb_dn
*old_dsdb_dn
, const struct GUID
*invocation_id
,
169 uint64_t usn
, uint64_t local_usn
, NTTIME nttime
,
170 uint32_t version
, bool deleted
);
172 static int replmd_make_deleted_child_dn(TALLOC_CTX
*tmp_ctx
,
173 struct ldb_context
*ldb
,
175 const char *rdn_name
,
176 const struct ldb_val
*rdn_value
,
179 enum urgent_situation
{
180 REPL_URGENT_ON_CREATE
= 1,
181 REPL_URGENT_ON_UPDATE
= 2,
182 REPL_URGENT_ON_DELETE
= 4
185 enum deletion_state
{
186 OBJECT_NOT_DELETED
=1,
193 static bool replmd_recyclebin_enabled(struct ldb_module
*module
)
195 bool enabled
= false;
196 struct replmd_private
*replmd_private
=
197 talloc_get_type_abort(ldb_module_get_private(module
),
198 struct replmd_private
);
201 * only lookup the recycle-bin state once per replication, then cache
202 * the result. This can save us 1000s of DB searches
204 if (!replmd_private
->recyclebin_state_known
) {
205 int ret
= dsdb_recyclebin_enabled(module
, &enabled
);
206 if (ret
!= LDB_SUCCESS
) {
210 replmd_private
->recyclebin_enabled
= enabled
;
211 replmd_private
->recyclebin_state_known
= true;
214 return replmd_private
->recyclebin_enabled
;
217 static void replmd_deletion_state(struct ldb_module
*module
,
218 const struct ldb_message
*msg
,
219 enum deletion_state
*current_state
,
220 enum deletion_state
*next_state
)
222 bool enabled
= false;
225 *current_state
= OBJECT_REMOVED
;
226 if (next_state
!= NULL
) {
227 *next_state
= OBJECT_REMOVED
;
232 enabled
= replmd_recyclebin_enabled(module
);
234 if (ldb_msg_check_string_attribute(msg
, "isDeleted", "TRUE")) {
236 *current_state
= OBJECT_TOMBSTONE
;
237 if (next_state
!= NULL
) {
238 *next_state
= OBJECT_REMOVED
;
243 if (ldb_msg_check_string_attribute(msg
, "isRecycled", "TRUE")) {
244 *current_state
= OBJECT_RECYCLED
;
245 if (next_state
!= NULL
) {
246 *next_state
= OBJECT_REMOVED
;
251 *current_state
= OBJECT_DELETED
;
252 if (next_state
!= NULL
) {
253 *next_state
= OBJECT_RECYCLED
;
258 *current_state
= OBJECT_NOT_DELETED
;
259 if (next_state
== NULL
) {
264 *next_state
= OBJECT_DELETED
;
266 *next_state
= OBJECT_TOMBSTONE
;
270 static const struct {
271 const char *update_name
;
272 enum urgent_situation repl_situation
;
273 } urgent_objects
[] = {
274 {"nTDSDSA", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_DELETE
)},
275 {"crossRef", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_DELETE
)},
276 {"attributeSchema", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_UPDATE
)},
277 {"classSchema", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_UPDATE
)},
278 {"secret", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_UPDATE
)},
279 {"rIDManager", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_UPDATE
)},
283 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
284 static const char *urgent_attrs
[] = {
287 "userAccountControl",
292 static bool replmd_check_urgent_objectclass(const struct ldb_message_element
*objectclass_el
,
293 enum urgent_situation situation
)
296 for (i
=0; urgent_objects
[i
].update_name
; i
++) {
298 if ((situation
& urgent_objects
[i
].repl_situation
) == 0) {
302 for (j
=0; j
<objectclass_el
->num_values
; j
++) {
303 const struct ldb_val
*v
= &objectclass_el
->values
[j
];
304 if (ldb_attr_cmp((const char *)v
->data
, urgent_objects
[i
].update_name
) == 0) {
312 static bool replmd_check_urgent_attribute(const struct ldb_message_element
*el
)
314 if (ldb_attr_in_list(urgent_attrs
, el
->name
)) {
320 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request
*ar
);
323 initialise the module
324 allocate the private structure and build the list
325 of partition DNs for use by replmd_notify()
327 static int replmd_init(struct ldb_module
*module
)
329 struct replmd_private
*replmd_private
;
330 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
333 replmd_private
= talloc_zero(module
, struct replmd_private
);
334 if (replmd_private
== NULL
) {
336 return LDB_ERR_OPERATIONS_ERROR
;
339 ret
= dsdb_check_samba_compatible_feature(module
,
340 SAMBA_SORTED_LINKS_FEATURE
,
341 &replmd_private
->sorted_links
);
342 if (ret
!= LDB_SUCCESS
) {
343 talloc_free(replmd_private
);
347 replmd_private
->schema_dn
= ldb_get_schema_basedn(ldb
);
348 ldb_module_set_private(module
, replmd_private
);
349 return ldb_next_init(module
);
353 cleanup our per-transaction contexts
355 static void replmd_txn_cleanup(struct replmd_private
*replmd_private
)
357 talloc_free(replmd_private
->la_ctx
);
358 replmd_private
->la_list
= NULL
;
359 replmd_private
->la_ctx
= NULL
;
360 replmd_private
->recyclebin_state_known
= false;
365 struct la_backlink
*next
, *prev
;
366 const char *attr_name
;
367 struct ldb_dn
*forward_dn
;
368 struct GUID target_guid
;
373 a ldb_modify request operating on modules below the
376 static int linked_attr_modify(struct ldb_module
*module
,
377 const struct ldb_message
*message
,
378 struct ldb_request
*parent
)
380 struct ldb_request
*mod_req
;
382 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
383 TALLOC_CTX
*tmp_ctx
= talloc_new(module
);
384 struct ldb_result
*res
;
386 res
= talloc_zero(tmp_ctx
, struct ldb_result
);
388 talloc_free(tmp_ctx
);
389 return ldb_oom(ldb_module_get_ctx(module
));
392 ret
= ldb_build_mod_req(&mod_req
, ldb
, tmp_ctx
,
396 ldb_modify_default_callback
,
398 LDB_REQ_SET_LOCATION(mod_req
);
399 if (ret
!= LDB_SUCCESS
) {
400 talloc_free(tmp_ctx
);
404 ret
= ldb_request_add_control(mod_req
, DSDB_CONTROL_REPLICATED_UPDATE_OID
,
406 if (ret
!= LDB_SUCCESS
) {
410 /* Run the new request */
411 ret
= ldb_next_request(module
, mod_req
);
413 if (ret
== LDB_SUCCESS
) {
414 ret
= ldb_wait(mod_req
->handle
, LDB_WAIT_ALL
);
417 talloc_free(tmp_ctx
);
422 process a backlinks we accumulated during a transaction, adding and
423 deleting the backlinks from the target objects
425 static int replmd_process_backlink(struct ldb_module
*module
, struct la_backlink
*bl
, struct ldb_request
*parent
)
427 struct ldb_dn
*target_dn
, *source_dn
;
429 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
430 struct ldb_message
*msg
;
431 TALLOC_CTX
*frame
= talloc_stackframe();
437 - construct ldb_message
438 - either an add or a delete
440 ret
= dsdb_module_dn_by_guid(module
, frame
, &bl
->target_guid
, &target_dn
, parent
);
441 if (ret
!= LDB_SUCCESS
) {
442 struct GUID_txt_buf guid_str
;
443 DBG_WARNING("Failed to find target DN for linked attribute with GUID %s\n",
444 GUID_buf_string(&bl
->target_guid
, &guid_str
));
445 DBG_WARNING("Please run 'samba-tool dbcheck' to resolve any missing backlinks.\n");
450 msg
= ldb_msg_new(frame
);
452 ldb_module_oom(module
);
454 return LDB_ERR_OPERATIONS_ERROR
;
457 source_dn
= ldb_dn_copy(frame
, bl
->forward_dn
);
459 ldb_module_oom(module
);
461 return LDB_ERR_OPERATIONS_ERROR
;
463 /* Filter down to the attributes we want in the backlink */
464 const char *accept
[] = { "GUID", "SID", NULL
};
465 ldb_dn_extended_filter(source_dn
, accept
);
468 /* construct a ldb_message for adding/deleting the backlink */
470 dn_string
= ldb_dn_get_extended_linearized(frame
, bl
->forward_dn
, 1);
472 ldb_module_oom(module
);
474 return LDB_ERR_OPERATIONS_ERROR
;
476 ret
= ldb_msg_add_steal_string(msg
, bl
->attr_name
, dn_string
);
477 if (ret
!= LDB_SUCCESS
) {
481 msg
->elements
[0].flags
= bl
->active
?LDB_FLAG_MOD_ADD
:LDB_FLAG_MOD_DELETE
;
483 /* a backlink should never be single valued. Unfortunately the
484 exchange schema has a attribute
485 msExchBridgeheadedLocalConnectorsDNBL which is single
486 valued and a backlink. We need to cope with that by
487 ignoring the single value flag */
488 msg
->elements
[0].flags
|= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK
;
490 ret
= dsdb_module_modify(module
, msg
, DSDB_FLAG_NEXT_MODULE
, parent
);
491 if (ret
== LDB_ERR_NO_SUCH_ATTRIBUTE
&& !bl
->active
) {
492 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
493 cope with possible corruption where the backlink has
494 already been removed */
495 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
496 ldb_dn_get_linearized(target_dn
),
497 ldb_dn_get_linearized(source_dn
),
498 ldb_errstring(ldb
)));
500 } else if (ret
!= LDB_SUCCESS
) {
501 ldb_asprintf_errstring(ldb
, "Failed to %s backlink from %s to %s - %s",
502 bl
->active
?"add":"remove",
503 ldb_dn_get_linearized(source_dn
),
504 ldb_dn_get_linearized(target_dn
),
514 add a backlink to the list of backlinks to add/delete in the prepare
517 forward_dn is stolen onto the defereed context
519 static int replmd_defer_add_backlink(struct ldb_module
*module
,
520 struct replmd_private
*replmd_private
,
521 const struct dsdb_schema
*schema
,
522 struct replmd_replicated_request
*ac
,
523 struct ldb_dn
*forward_dn
,
524 struct GUID
*target_guid
, bool active
,
525 const struct dsdb_attribute
*schema_attr
,
526 struct ldb_request
*parent
)
528 const struct dsdb_attribute
*target_attr
;
529 struct la_backlink
*bl
;
531 bl
= talloc(ac
, struct la_backlink
);
533 ldb_module_oom(module
);
534 return LDB_ERR_OPERATIONS_ERROR
;
537 target_attr
= dsdb_attribute_by_linkID(schema
, schema_attr
->linkID
^ 1);
540 * windows 2003 has a broken schema where the
541 * definition of msDS-IsDomainFor is missing (which is
542 * supposed to be the backlink of the
543 * msDS-HasDomainNCs attribute
548 bl
->attr_name
= target_attr
->lDAPDisplayName
;
549 bl
->forward_dn
= talloc_steal(bl
, forward_dn
);
550 bl
->target_guid
= *target_guid
;
553 DLIST_ADD(ac
->la_backlinks
, bl
);
559 add a backlink to the list of backlinks to add/delete in the prepare
562 static int replmd_add_backlink(struct ldb_module
*module
,
563 struct replmd_private
*replmd_private
,
564 const struct dsdb_schema
*schema
,
565 struct ldb_dn
*forward_dn
,
566 struct GUID
*target_guid
, bool active
,
567 const struct dsdb_attribute
*schema_attr
,
568 struct ldb_request
*parent
)
570 const struct dsdb_attribute
*target_attr
;
571 struct la_backlink bl
;
574 target_attr
= dsdb_attribute_by_linkID(schema
, schema_attr
->linkID
^ 1);
577 * windows 2003 has a broken schema where the
578 * definition of msDS-IsDomainFor is missing (which is
579 * supposed to be the backlink of the
580 * msDS-HasDomainNCs attribute
585 bl
.attr_name
= target_attr
->lDAPDisplayName
;
586 bl
.forward_dn
= forward_dn
;
587 bl
.target_guid
= *target_guid
;
590 ret
= replmd_process_backlink(module
, &bl
, parent
);
596 * Callback for most write operations in this module:
598 * notify the repl task that a object has changed. The notifies are
599 * gathered up in the replmd_private structure then written to the
600 * @REPLCHANGED object in each partition during the prepare_commit
602 static int replmd_op_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
605 struct replmd_replicated_request
*ac
=
606 talloc_get_type_abort(req
->context
, struct replmd_replicated_request
);
607 struct replmd_private
*replmd_private
=
608 talloc_get_type_abort(ldb_module_get_private(ac
->module
), struct replmd_private
);
609 struct nc_entry
*modified_partition
;
610 struct ldb_control
*partition_ctrl
;
611 const struct dsdb_control_current_partition
*partition
;
613 struct ldb_control
**controls
;
615 partition_ctrl
= ldb_reply_get_control(ares
, DSDB_CONTROL_CURRENT_PARTITION_OID
);
617 controls
= ares
->controls
;
618 if (ldb_request_get_control(ac
->req
,
619 DSDB_CONTROL_CURRENT_PARTITION_OID
) == NULL
) {
621 * Remove the current partition control from what we pass up
622 * the chain if it hasn't been requested manually.
624 controls
= ldb_controls_except_specified(ares
->controls
, ares
,
628 if (ares
->error
!= LDB_SUCCESS
) {
629 struct GUID_txt_buf guid_txt
;
630 struct ldb_message
*msg
= NULL
;
633 if (ac
->apply_mode
== false) {
634 DBG_NOTICE("Originating update failure. Error is: %s\n",
635 ldb_strerror(ares
->error
));
636 return ldb_module_done(ac
->req
, controls
,
637 ares
->response
, ares
->error
);
640 msg
= ac
->objs
->objects
[ac
->index_current
].msg
;
642 * Set at DBG_NOTICE as once these start to happe, they
643 * will happen a lot until resolved, due to repeated
644 * replication. The caller will probably print the
645 * ldb error string anyway.
647 DBG_NOTICE("DRS replication apply failure for %s. Error is: %s\n",
648 ldb_dn_get_linearized(msg
->dn
),
649 ldb_strerror(ares
->error
));
651 s
= ldb_ldif_message_redacted_string(ldb_module_get_ctx(ac
->module
),
656 DBG_INFO("Failing DRS %s replication message was %s:\n%s\n",
657 ac
->search_msg
== NULL
? "ADD" : "MODIFY",
658 GUID_buf_string(&ac
->objs
->objects
[ac
->index_current
].object_guid
,
662 return ldb_module_done(ac
->req
, controls
,
663 ares
->response
, ares
->error
);
666 if (ares
->type
!= LDB_REPLY_DONE
) {
667 ldb_set_errstring(ldb_module_get_ctx(ac
->module
), "Invalid reply type for notify\n!");
668 return ldb_module_done(ac
->req
, NULL
,
669 NULL
, LDB_ERR_OPERATIONS_ERROR
);
672 if (ac
->apply_mode
== false) {
673 struct la_backlink
*bl
;
675 * process our backlink list after an replmd_add(),
676 * creating and deleting backlinks as necessary (this
677 * code is sync). The other cases are handled inline
680 for (bl
=ac
->la_backlinks
; bl
; bl
=bl
->next
) {
681 ret
= replmd_process_backlink(ac
->module
, bl
, ac
->req
);
682 if (ret
!= LDB_SUCCESS
) {
683 return ldb_module_done(ac
->req
, NULL
,
689 if (!partition_ctrl
) {
690 ldb_set_errstring(ldb_module_get_ctx(ac
->module
),"No partition control on reply");
691 return ldb_module_done(ac
->req
, NULL
,
692 NULL
, LDB_ERR_OPERATIONS_ERROR
);
695 partition
= talloc_get_type_abort(partition_ctrl
->data
,
696 struct dsdb_control_current_partition
);
698 if (ac
->seq_num
> 0) {
699 for (modified_partition
= replmd_private
->ncs
; modified_partition
;
700 modified_partition
= modified_partition
->next
) {
701 if (ldb_dn_compare(modified_partition
->dn
, partition
->dn
) == 0) {
706 if (modified_partition
== NULL
) {
707 modified_partition
= talloc_zero(replmd_private
, struct nc_entry
);
708 if (!modified_partition
) {
709 ldb_oom(ldb_module_get_ctx(ac
->module
));
710 return ldb_module_done(ac
->req
, NULL
,
711 NULL
, LDB_ERR_OPERATIONS_ERROR
);
713 modified_partition
->dn
= ldb_dn_copy(modified_partition
, partition
->dn
);
714 if (!modified_partition
->dn
) {
715 ldb_oom(ldb_module_get_ctx(ac
->module
));
716 return ldb_module_done(ac
->req
, NULL
,
717 NULL
, LDB_ERR_OPERATIONS_ERROR
);
719 DLIST_ADD(replmd_private
->ncs
, modified_partition
);
722 if (ac
->seq_num
> modified_partition
->mod_usn
) {
723 modified_partition
->mod_usn
= ac
->seq_num
;
725 modified_partition
->mod_usn_urgent
= ac
->seq_num
;
728 if (!ac
->apply_mode
) {
729 replmd_private
->originating_updates
= true;
733 if (ac
->apply_mode
) {
734 ret
= replmd_replicated_apply_isDeleted(ac
);
735 if (ret
!= LDB_SUCCESS
) {
736 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
740 /* free the partition control container here, for the
741 * common path. Other cases will have it cleaned up
742 * eventually with the ares */
743 talloc_free(partition_ctrl
);
744 return ldb_module_done(ac
->req
, controls
,
745 ares
->response
, LDB_SUCCESS
);
751 * update a @REPLCHANGED record in each partition if there have been
752 * any writes of replicated data in the partition
754 static int replmd_notify_store(struct ldb_module
*module
, struct ldb_request
*parent
)
756 struct replmd_private
*replmd_private
=
757 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
759 while (replmd_private
->ncs
) {
761 struct nc_entry
*modified_partition
= replmd_private
->ncs
;
763 ret
= dsdb_module_save_partition_usn(module
, modified_partition
->dn
,
764 modified_partition
->mod_usn
,
765 modified_partition
->mod_usn_urgent
, parent
);
766 if (ret
!= LDB_SUCCESS
) {
767 DEBUG(0,(__location__
": Failed to save partition uSN for %s\n",
768 ldb_dn_get_linearized(modified_partition
->dn
)));
772 if (ldb_dn_compare(modified_partition
->dn
,
773 replmd_private
->schema_dn
) == 0) {
774 struct ldb_result
*ext_res
;
775 ret
= dsdb_module_extended(module
,
776 replmd_private
->schema_dn
,
778 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID
,
780 DSDB_FLAG_NEXT_MODULE
,
782 if (ret
!= LDB_SUCCESS
) {
785 talloc_free(ext_res
);
788 DLIST_REMOVE(replmd_private
->ncs
, modified_partition
);
789 talloc_free(modified_partition
);
797 created a replmd_replicated_request context
799 static struct replmd_replicated_request
*replmd_ctx_init(struct ldb_module
*module
,
800 struct ldb_request
*req
)
802 struct ldb_context
*ldb
;
803 struct replmd_replicated_request
*ac
;
804 const struct GUID
*our_invocation_id
;
806 ldb
= ldb_module_get_ctx(module
);
808 ac
= talloc_zero(req
, struct replmd_replicated_request
);
817 ac
->schema
= dsdb_get_schema(ldb
, ac
);
819 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
820 "replmd_modify: no dsdb_schema loaded");
821 DEBUG(0,(__location__
": %s\n", ldb_errstring(ldb
)));
826 /* get our invocationId */
827 our_invocation_id
= samdb_ntds_invocation_id(ldb
);
828 if (!our_invocation_id
) {
829 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
830 "replmd_add: unable to find invocationId\n");
834 ac
->our_invocation_id
= *our_invocation_id
;
840 add a time element to a record
842 static int add_time_element(struct ldb_message
*msg
, const char *attr
, time_t t
)
844 struct ldb_message_element
*el
;
848 if (ldb_msg_find_element(msg
, attr
) != NULL
) {
852 s
= ldb_timestring(msg
, t
);
854 return LDB_ERR_OPERATIONS_ERROR
;
857 ret
= ldb_msg_add_string(msg
, attr
, s
);
858 if (ret
!= LDB_SUCCESS
) {
862 el
= ldb_msg_find_element(msg
, attr
);
863 /* always set as replace. This works because on add ops, the flag
865 el
->flags
= LDB_FLAG_MOD_REPLACE
;
871 add a uint64_t element to a record
873 static int add_uint64_element(struct ldb_context
*ldb
, struct ldb_message
*msg
,
874 const char *attr
, uint64_t v
)
876 struct ldb_message_element
*el
;
879 if (ldb_msg_find_element(msg
, attr
) != NULL
) {
883 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, attr
, v
);
884 if (ret
!= LDB_SUCCESS
) {
888 el
= ldb_msg_find_element(msg
, attr
);
889 /* always set as replace. This works because on add ops, the flag
891 el
->flags
= LDB_FLAG_MOD_REPLACE
;
896 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1
*m1
,
897 const struct replPropertyMetaData1
*m2
)
900 * This assignment seems inoccous, but it is critical for the
901 * system, as we need to do the comparisons as a unsigned
902 * quantity, not signed (enums are signed integers)
904 uint32_t attid_1
= m1
->attid
;
905 uint32_t attid_2
= m2
->attid
;
907 if (attid_1
== attid_2
) {
912 * See above regarding this being an unsigned comparison.
913 * Otherwise when the high bit is set on non-standard
914 * attributes, they would end up first, before objectClass
917 return attid_1
> attid_2
? 1 : -1;
920 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context
*ldb
,
921 struct replPropertyMetaDataCtr1
*ctr1
,
924 if (ctr1
->count
== 0) {
925 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
926 "No elements found in replPropertyMetaData for %s!\n",
927 ldb_dn_get_linearized(dn
));
928 return LDB_ERR_CONSTRAINT_VIOLATION
;
931 /* the objectClass attribute is value 0x00000000, so must be first */
932 if (ctr1
->array
[0].attid
!= DRSUAPI_ATTID_objectClass
) {
933 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
934 "No objectClass found in replPropertyMetaData for %s!\n",
935 ldb_dn_get_linearized(dn
));
936 return LDB_ERR_OBJECT_CLASS_VIOLATION
;
942 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context
*ldb
,
943 struct replPropertyMetaDataCtr1
*ctr1
,
946 /* Note this is O(n^2) for the almost-sorted case, which this is */
947 TYPESAFE_QSORT(ctr1
->array
, ctr1
->count
,
948 replmd_replPropertyMetaData1_attid_sort
);
949 return replmd_replPropertyMetaDataCtr1_verify(ldb
, ctr1
, dn
);
952 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element
*e1
,
953 const struct ldb_message_element
*e2
,
954 const struct dsdb_schema
*schema
)
956 const struct dsdb_attribute
*a1
;
957 const struct dsdb_attribute
*a2
;
960 * TODO: make this faster by caching the dsdb_attribute pointer
961 * on the ldb_messag_element
964 a1
= dsdb_attribute_by_lDAPDisplayName(schema
, e1
->name
);
965 a2
= dsdb_attribute_by_lDAPDisplayName(schema
, e2
->name
);
968 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
972 return strcasecmp(e1
->name
, e2
->name
);
974 if (a1
->attributeID_id
== a2
->attributeID_id
) {
977 return a1
->attributeID_id
> a2
->attributeID_id
? 1 : -1;
980 static void replmd_ldb_message_sort(struct ldb_message
*msg
,
981 const struct dsdb_schema
*schema
)
983 LDB_TYPESAFE_QSORT(msg
->elements
, msg
->num_elements
, schema
, replmd_ldb_message_element_attid_sort
);
986 static int replmd_build_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
, struct dsdb_dn
*dsdb_dn
,
987 const struct GUID
*invocation_id
,
988 uint64_t local_usn
, NTTIME nttime
);
990 static int parsed_dn_compare(struct parsed_dn
*pdn1
, struct parsed_dn
*pdn2
);
992 static int get_parsed_dns(struct ldb_module
*module
, TALLOC_CTX
*mem_ctx
,
993 struct ldb_message_element
*el
, struct parsed_dn
**pdn
,
994 const char *ldap_oid
, struct ldb_request
*parent
);
996 static int check_parsed_dn_duplicates(struct ldb_module
*module
,
997 struct ldb_message_element
*el
,
998 struct parsed_dn
*pdn
);
1001 fix up linked attributes in replmd_add.
1002 This involves setting up the right meta-data in extended DN
1003 components, and creating backlinks to the object
1005 static int replmd_add_fix_la(struct ldb_module
*module
, TALLOC_CTX
*mem_ctx
,
1006 struct replmd_private
*replmd_private
,
1007 struct ldb_message_element
*el
,
1008 struct replmd_replicated_request
*ac
,
1010 struct ldb_dn
*forward_dn
,
1011 const struct dsdb_attribute
*sa
,
1012 struct ldb_request
*parent
)
1015 TALLOC_CTX
*tmp_ctx
= talloc_new(mem_ctx
);
1016 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
1017 struct parsed_dn
*pdn
;
1018 /* We will take a reference to the schema in replmd_add_backlink */
1019 const struct dsdb_schema
*schema
= dsdb_get_schema(ldb
, NULL
);
1020 struct ldb_val
*new_values
= NULL
;
1023 if (dsdb_check_single_valued_link(sa
, el
) == LDB_SUCCESS
) {
1024 el
->flags
|= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK
;
1026 ldb_asprintf_errstring(ldb
,
1027 "Attribute %s is single valued but "
1028 "more than one value has been supplied",
1030 talloc_free(tmp_ctx
);
1031 return LDB_ERR_CONSTRAINT_VIOLATION
;
1035 * At the successful end of these functions el->values is
1036 * overwritten with new_values. However get_parsed_dns()
1037 * points p->v at the supplied el and it effectively gets used
1038 * as a working area by replmd_build_la_val(). So we must
1039 * duplicate it because our caller only called
1040 * ldb_msg_copy_shallow().
1043 el
->values
= talloc_memdup(tmp_ctx
,
1045 sizeof(el
->values
[0]) * el
->num_values
);
1046 if (el
->values
== NULL
) {
1047 ldb_module_oom(module
);
1048 talloc_free(tmp_ctx
);
1049 return LDB_ERR_OPERATIONS_ERROR
;
1052 ret
= get_parsed_dns(module
, tmp_ctx
, el
, &pdn
,
1053 sa
->syntax
->ldap_oid
, parent
);
1054 if (ret
!= LDB_SUCCESS
) {
1055 talloc_free(tmp_ctx
);
1059 ret
= check_parsed_dn_duplicates(module
, el
, pdn
);
1060 if (ret
!= LDB_SUCCESS
) {
1061 talloc_free(tmp_ctx
);
1065 new_values
= talloc_array(tmp_ctx
, struct ldb_val
, el
->num_values
);
1066 if (new_values
== NULL
) {
1067 ldb_module_oom(module
);
1068 talloc_free(tmp_ctx
);
1069 return LDB_ERR_OPERATIONS_ERROR
;
1072 for (i
= 0; i
< el
->num_values
; i
++) {
1073 struct parsed_dn
*p
= &pdn
[i
];
1074 ret
= replmd_build_la_val(new_values
, p
->v
, p
->dsdb_dn
,
1075 &ac
->our_invocation_id
,
1077 if (ret
!= LDB_SUCCESS
) {
1078 talloc_free(tmp_ctx
);
1082 ret
= replmd_defer_add_backlink(module
, replmd_private
,
1084 forward_dn
, &p
->guid
, true, sa
,
1086 if (ret
!= LDB_SUCCESS
) {
1087 talloc_free(tmp_ctx
);
1091 new_values
[i
] = *p
->v
;
1093 el
->values
= talloc_steal(mem_ctx
, new_values
);
1095 talloc_free(tmp_ctx
);
1099 static int replmd_add_make_extended_dn(struct ldb_request
*req
,
1100 const DATA_BLOB
*guid_blob
,
1101 struct ldb_dn
**_extended_dn
)
1104 const DATA_BLOB
*sid_blob
;
1105 /* Calculate an extended DN for any linked attributes */
1106 struct ldb_dn
*extended_dn
= ldb_dn_copy(req
, req
->op
.add
.message
->dn
);
1108 return LDB_ERR_OPERATIONS_ERROR
;
1110 ret
= ldb_dn_set_extended_component(extended_dn
, "GUID", guid_blob
);
1111 if (ret
!= LDB_SUCCESS
) {
1115 sid_blob
= ldb_msg_find_ldb_val(req
->op
.add
.message
, "objectSID");
1116 if (sid_blob
!= NULL
) {
1117 ret
= ldb_dn_set_extended_component(extended_dn
, "SID", sid_blob
);
1118 if (ret
!= LDB_SUCCESS
) {
1122 *_extended_dn
= extended_dn
;
1127 intercept add requests
1129 static int replmd_add(struct ldb_module
*module
, struct ldb_request
*req
)
1131 struct ldb_context
*ldb
;
1132 struct ldb_control
*control
;
1133 struct replmd_replicated_request
*ac
;
1134 enum ndr_err_code ndr_err
;
1135 struct ldb_request
*down_req
;
1136 struct ldb_message
*msg
;
1137 const DATA_BLOB
*guid_blob
;
1138 DATA_BLOB guid_blob_stack
;
1140 uint8_t guid_data
[16];
1141 struct replPropertyMetaDataBlob nmd
;
1142 struct ldb_val nmd_value
;
1143 struct ldb_dn
*extended_dn
= NULL
;
1146 * The use of a time_t here seems odd, but as the NTTIME
1147 * elements are actually declared as NTTIME_1sec in the IDL,
1148 * getting a higher resolution timestamp is not required.
1150 time_t t
= time(NULL
);
1155 unsigned int functional_level
;
1157 bool allow_add_guid
= false;
1158 bool remove_current_guid
= false;
1159 bool is_urgent
= false;
1160 bool is_schema_nc
= false;
1161 struct ldb_message_element
*objectclass_el
;
1162 struct replmd_private
*replmd_private
=
1163 talloc_get_type_abort(ldb_module_get_private(module
), struct replmd_private
);
1165 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
1166 control
= ldb_request_get_control(req
, LDB_CONTROL_RELAX_OID
);
1168 allow_add_guid
= true;
1171 /* do not manipulate our control entries */
1172 if (ldb_dn_is_special(req
->op
.add
.message
->dn
)) {
1173 return ldb_next_request(module
, req
);
1176 ldb
= ldb_module_get_ctx(module
);
1178 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_add\n");
1180 guid_blob
= ldb_msg_find_ldb_val(req
->op
.add
.message
, "objectGUID");
1181 if (guid_blob
!= NULL
) {
1182 if (!allow_add_guid
) {
1183 ldb_set_errstring(ldb
,
1184 "replmd_add: it's not allowed to add an object with objectGUID!");
1185 return LDB_ERR_UNWILLING_TO_PERFORM
;
1187 NTSTATUS status
= GUID_from_data_blob(guid_blob
,&guid
);
1188 if (!NT_STATUS_IS_OK(status
)) {
1189 ldb_set_errstring(ldb
,
1190 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1191 return LDB_ERR_UNWILLING_TO_PERFORM
;
1193 /* we remove this attribute as it can be a string and
1194 * will not be treated correctly and then we will re-add
1195 * it later on in the good format */
1196 remove_current_guid
= true;
1200 guid
= GUID_random();
1202 guid_blob_stack
= data_blob_const(guid_data
, sizeof(guid_data
));
1204 /* This can't fail */
1205 ndr_push_struct_into_fixed_blob(&guid_blob_stack
, &guid
,
1206 (ndr_push_flags_fn_t
)ndr_push_GUID
);
1207 guid_blob
= &guid_blob_stack
;
1210 ac
= replmd_ctx_init(module
, req
);
1212 return ldb_module_oom(module
);
1215 functional_level
= dsdb_functional_level(ldb
);
1217 /* Get a sequence number from the backend */
1218 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &ac
->seq_num
);
1219 if (ret
!= LDB_SUCCESS
) {
1224 /* we have to copy the message as the caller might have it as a const */
1225 msg
= ldb_msg_copy_shallow(ac
, req
->op
.add
.message
);
1229 return LDB_ERR_OPERATIONS_ERROR
;
1232 /* generated times */
1233 unix_to_nt_time(&now
, t
);
1234 time_str
= ldb_timestring(msg
, t
);
1238 return LDB_ERR_OPERATIONS_ERROR
;
1240 if (remove_current_guid
) {
1241 ldb_msg_remove_attr(msg
,"objectGUID");
1245 * remove autogenerated attributes
1247 ldb_msg_remove_attr(msg
, "whenCreated");
1248 ldb_msg_remove_attr(msg
, "whenChanged");
1249 ldb_msg_remove_attr(msg
, "uSNCreated");
1250 ldb_msg_remove_attr(msg
, "uSNChanged");
1251 ldb_msg_remove_attr(msg
, "replPropertyMetaData");
1254 * readd replicated attributes
1256 ret
= ldb_msg_add_string(msg
, "whenCreated", time_str
);
1257 if (ret
!= LDB_SUCCESS
) {
1263 /* build the replication meta_data */
1266 nmd
.ctr
.ctr1
.count
= msg
->num_elements
;
1267 nmd
.ctr
.ctr1
.array
= talloc_array(msg
,
1268 struct replPropertyMetaData1
,
1269 nmd
.ctr
.ctr1
.count
);
1270 if (!nmd
.ctr
.ctr1
.array
) {
1273 return LDB_ERR_OPERATIONS_ERROR
;
1276 is_schema_nc
= ldb_dn_compare_base(replmd_private
->schema_dn
, msg
->dn
) == 0;
1278 for (i
=0; i
< msg
->num_elements
;) {
1279 struct ldb_message_element
*e
= &msg
->elements
[i
];
1280 struct replPropertyMetaData1
*m
= &nmd
.ctr
.ctr1
.array
[ni
];
1281 const struct dsdb_attribute
*sa
;
1283 if (e
->name
[0] == '@') {
1288 sa
= dsdb_attribute_by_lDAPDisplayName(ac
->schema
, e
->name
);
1290 ldb_debug_set(ldb
, LDB_DEBUG_ERROR
,
1291 "replmd_add: attribute '%s' not defined in schema\n",
1294 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
1297 if ((sa
->systemFlags
& DS_FLAG_ATTR_NOT_REPLICATED
) || (sa
->systemFlags
& DS_FLAG_ATTR_IS_CONSTRUCTED
)) {
1298 /* if the attribute is not replicated (0x00000001)
1299 * or constructed (0x00000004) it has no metadata
1305 if (sa
->linkID
!= 0 && functional_level
> DS_DOMAIN_FUNCTION_2000
) {
1306 if (extended_dn
== NULL
) {
1307 ret
= replmd_add_make_extended_dn(req
,
1310 if (ret
!= LDB_SUCCESS
) {
1317 * Prepare the context for the backlinks and
1318 * create metadata for the forward links. The
1319 * backlinks are created in
1320 * replmd_op_callback() after the successful
1321 * ADD of the object.
1323 ret
= replmd_add_fix_la(module
, msg
->elements
,
1328 if (ret
!= LDB_SUCCESS
) {
1332 /* linked attributes are not stored in
1333 replPropertyMetaData in FL above w2k */
1338 m
->attid
= dsdb_attribute_get_attid(sa
, is_schema_nc
);
1340 if (m
->attid
== DRSUAPI_ATTID_isDeleted
) {
1341 const struct ldb_val
*rdn_val
= ldb_dn_get_rdn_val(msg
->dn
);
1344 if (rdn_val
== NULL
) {
1347 return LDB_ERR_OPERATIONS_ERROR
;
1350 rdn
= (const char*)rdn_val
->data
;
1351 if (strcmp(rdn
, "Deleted Objects") == 0) {
1353 * Set the originating_change_time to 29/12/9999 at 23:59:59
1354 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1356 m
->originating_change_time
= DELETED_OBJECT_CONTAINER_CHANGE_TIME
;
1358 m
->originating_change_time
= now
;
1361 m
->originating_change_time
= now
;
1363 m
->originating_invocation_id
= ac
->our_invocation_id
;
1364 m
->originating_usn
= ac
->seq_num
;
1365 m
->local_usn
= ac
->seq_num
;
1368 if (!(e
->flags
& DSDB_FLAG_INTERNAL_FORCE_META_DATA
)) {
1373 e
->flags
&= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA
;
1375 if (e
->num_values
!= 0) {
1380 ldb_msg_remove_element(msg
, e
);
1383 /* fix meta data count */
1384 nmd
.ctr
.ctr1
.count
= ni
;
1387 * sort meta data array
1389 ret
= replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb
, &nmd
.ctr
.ctr1
, msg
->dn
);
1390 if (ret
!= LDB_SUCCESS
) {
1391 ldb_asprintf_errstring(ldb
, "%s: error during direct ADD: %s", __func__
, ldb_errstring(ldb
));
1396 /* generated NDR encoded values */
1397 ndr_err
= ndr_push_struct_blob(&nmd_value
, msg
,
1399 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
1400 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1403 return LDB_ERR_OPERATIONS_ERROR
;
1407 * add the autogenerated values
1409 ret
= dsdb_msg_add_guid(msg
, &guid
, "objectGUID");
1410 if (ret
!= LDB_SUCCESS
) {
1415 ret
= ldb_msg_add_string(msg
, "whenChanged", time_str
);
1416 if (ret
!= LDB_SUCCESS
) {
1421 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNCreated", ac
->seq_num
);
1422 if (ret
!= LDB_SUCCESS
) {
1427 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNChanged", ac
->seq_num
);
1428 if (ret
!= LDB_SUCCESS
) {
1433 ret
= ldb_msg_add_value(msg
, "replPropertyMetaData", &nmd_value
, NULL
);
1434 if (ret
!= LDB_SUCCESS
) {
1441 * sort the attributes by attid before storing the object
1443 replmd_ldb_message_sort(msg
, ac
->schema
);
1446 * Assert that we do have an objectClass
1448 objectclass_el
= ldb_msg_find_element(msg
, "objectClass");
1449 if (objectclass_el
== NULL
) {
1450 ldb_asprintf_errstring(ldb
, __location__
1451 ": objectClass missing on %s\n",
1452 ldb_dn_get_linearized(msg
->dn
));
1454 return LDB_ERR_OBJECT_CLASS_VIOLATION
;
1456 is_urgent
= replmd_check_urgent_objectclass(objectclass_el
,
1457 REPL_URGENT_ON_CREATE
);
1459 ac
->is_urgent
= is_urgent
;
1460 ret
= ldb_build_add_req(&down_req
, ldb
, ac
,
1463 ac
, replmd_op_callback
,
1466 LDB_REQ_SET_LOCATION(down_req
);
1467 if (ret
!= LDB_SUCCESS
) {
1472 /* current partition control is needed by "replmd_op_callback" */
1473 if (ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
) == NULL
) {
1474 ret
= ldb_request_add_control(down_req
,
1475 DSDB_CONTROL_CURRENT_PARTITION_OID
,
1477 if (ret
!= LDB_SUCCESS
) {
1483 if (functional_level
== DS_DOMAIN_FUNCTION_2000
) {
1484 ret
= ldb_request_add_control(down_req
, DSDB_CONTROL_APPLY_LINKS
, false, NULL
);
1485 if (ret
!= LDB_SUCCESS
) {
1491 /* mark the relax control done */
1493 control
->critical
= 0;
1495 /* go on with the call chain */
1496 return ldb_next_request(module
, down_req
);
1501 * update the replPropertyMetaData for one element
1503 static int replmd_update_rpmd_element(struct ldb_context
*ldb
,
1504 struct ldb_message
*msg
,
1505 struct ldb_message_element
*el
,
1506 struct ldb_message_element
*old_el
,
1507 struct replPropertyMetaDataBlob
*omd
,
1508 const struct dsdb_schema
*schema
,
1510 const struct GUID
*our_invocation_id
,
1513 bool is_forced_rodc
,
1514 struct ldb_request
*req
)
1517 const struct dsdb_attribute
*a
;
1518 struct replPropertyMetaData1
*md1
;
1519 bool may_skip
= false;
1522 a
= dsdb_attribute_by_lDAPDisplayName(schema
, el
->name
);
1524 if (ldb_request_get_control(req
, LDB_CONTROL_RELAX_OID
)) {
1525 /* allow this to make it possible for dbcheck
1526 to remove bad attributes */
1530 DEBUG(0,(__location__
": Unable to find attribute %s in schema\n",
1532 return LDB_ERR_OPERATIONS_ERROR
;
1535 attid
= dsdb_attribute_get_attid(a
, is_schema_nc
);
1537 if ((a
->systemFlags
& DS_FLAG_ATTR_NOT_REPLICATED
) || (a
->systemFlags
& DS_FLAG_ATTR_IS_CONSTRUCTED
)) {
1542 * if the attribute's value haven't changed, and this isn't
1543 * just a delete of everything then return LDB_SUCCESS Unless
1544 * we have the provision control or if the attribute is
1545 * interSiteTopologyGenerator as this page explain:
1546 * http://support.microsoft.com/kb/224815 this attribute is
1547 * periodicaly written by the DC responsible for the intersite
1548 * generation in a given site
1550 * Unchanged could be deleting or replacing an already-gone
1551 * thing with an unconstrained delete/empty replace or a
1552 * replace with the same value, but not an add with the same
1553 * value because that could be about adding a duplicate (which
1554 * is for someone else to error out on).
1556 if (old_el
!= NULL
&& ldb_msg_element_equal_ordered(el
, old_el
)) {
1557 if (LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_REPLACE
) {
1560 } else if (old_el
== NULL
&& el
->num_values
== 0) {
1561 if (LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_REPLACE
) {
1563 } else if (LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_DELETE
) {
1566 } else if (a
->linkID
!= 0 && LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_DELETE
&&
1567 ldb_request_get_control(req
, DSDB_CONTROL_REPLMD_VANISH_LINKS
) != NULL
) {
1569 * We intentionally skip the version bump when attempting to
1572 * The control is set by dbcheck and expunge-tombstones which
1573 * both attempt to be non-replicating. Otherwise, making an
1574 * alteration to the replication state would trigger a
1575 * broadcast of all expunged objects.
1580 if (el
->flags
& DSDB_FLAG_INTERNAL_FORCE_META_DATA
) {
1582 el
->flags
&= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA
;
1586 if (strcmp(el
->name
, "interSiteTopologyGenerator") != 0 &&
1587 !ldb_request_get_control(req
, LDB_CONTROL_PROVISION_OID
)) {
1589 * allow this to make it possible for dbcheck
1590 * to rebuild broken metadata
1596 for (i
=0; i
<omd
->ctr
.ctr1
.count
; i
++) {
1598 * First check if we find it under the msDS-IntID,
1599 * then check if we find it under the OID and
1602 * This allows the administrator to simply re-write
1603 * the attributes and so restore replication, which is
1604 * likely what they will try to do.
1606 if (attid
== omd
->ctr
.ctr1
.array
[i
].attid
) {
1610 if (a
->attributeID_id
== omd
->ctr
.ctr1
.array
[i
].attid
) {
1615 if (a
->linkID
!= 0 && dsdb_functional_level(ldb
) > DS_DOMAIN_FUNCTION_2000
) {
1616 /* linked attributes are not stored in
1617 replPropertyMetaData in FL above w2k, but we do
1618 raise the seqnum for the object */
1619 if (*seq_num
== 0 &&
1620 ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, seq_num
) != LDB_SUCCESS
) {
1621 return LDB_ERR_OPERATIONS_ERROR
;
1626 if (i
== omd
->ctr
.ctr1
.count
) {
1627 /* we need to add a new one */
1628 omd
->ctr
.ctr1
.array
= talloc_realloc(msg
, omd
->ctr
.ctr1
.array
,
1629 struct replPropertyMetaData1
, omd
->ctr
.ctr1
.count
+1);
1630 if (omd
->ctr
.ctr1
.array
== NULL
) {
1632 return LDB_ERR_OPERATIONS_ERROR
;
1634 omd
->ctr
.ctr1
.count
++;
1635 ZERO_STRUCT(omd
->ctr
.ctr1
.array
[i
]);
1638 /* Get a new sequence number from the backend. We only do this
1639 * if we have a change that requires a new
1640 * replPropertyMetaData element
1642 if (*seq_num
== 0) {
1643 int ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, seq_num
);
1644 if (ret
!= LDB_SUCCESS
) {
1645 return LDB_ERR_OPERATIONS_ERROR
;
1649 md1
= &omd
->ctr
.ctr1
.array
[i
];
1653 if (md1
->attid
== DRSUAPI_ATTID_isDeleted
) {
1654 const struct ldb_val
*rdn_val
= ldb_dn_get_rdn_val(msg
->dn
);
1657 if (rdn_val
== NULL
) {
1659 return LDB_ERR_OPERATIONS_ERROR
;
1662 rdn
= (const char*)rdn_val
->data
;
1663 if (strcmp(rdn
, "Deleted Objects") == 0) {
1665 * Set the originating_change_time to 29/12/9999 at 23:59:59
1666 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1668 md1
->originating_change_time
= DELETED_OBJECT_CONTAINER_CHANGE_TIME
;
1670 md1
->originating_change_time
= now
;
1673 md1
->originating_change_time
= now
;
1675 md1
->originating_invocation_id
= *our_invocation_id
;
1676 md1
->originating_usn
= *seq_num
;
1677 md1
->local_usn
= *seq_num
;
1679 if (is_forced_rodc
) {
1680 /* Force version to 0 to be overridden later via replication */
1688 * Bump the replPropertyMetaData version on an attribute, and if it
1689 * has changed (or forced by leaving rdn_old NULL), update the value
1692 * This is important, as calling a modify operation may not change the
1693 * version number if the values appear unchanged, but a rename between
1694 * parents bumps this value.
1697 static int replmd_update_rpmd_rdn_attr(struct ldb_context
*ldb
,
1698 struct ldb_message
*msg
,
1699 const struct ldb_val
*rdn_new
,
1700 const struct ldb_val
*rdn_old
,
1701 struct replPropertyMetaDataBlob
*omd
,
1702 struct replmd_replicated_request
*ar
,
1705 bool is_forced_rodc
)
1707 const char *rdn_name
= ldb_dn_get_rdn_name(msg
->dn
);
1708 const struct dsdb_attribute
*rdn_attr
=
1709 dsdb_attribute_by_lDAPDisplayName(ar
->schema
, rdn_name
);
1710 const char *attr_name
= rdn_attr
!= NULL
?
1711 rdn_attr
->lDAPDisplayName
:
1713 struct ldb_message_element new_el
= {
1714 .flags
= LDB_FLAG_MOD_REPLACE
,
1717 .values
= discard_const_p(struct ldb_val
, rdn_new
)
1719 struct ldb_message_element old_el
= {
1720 .flags
= LDB_FLAG_MOD_REPLACE
,
1722 .num_values
= rdn_old
? 1 : 0,
1723 .values
= discard_const_p(struct ldb_val
, rdn_old
)
1726 if (ldb_msg_element_equal_ordered(&new_el
, &old_el
) == false) {
1727 int ret
= ldb_msg_add(msg
, &new_el
, LDB_FLAG_MOD_REPLACE
);
1728 if (ret
!= LDB_SUCCESS
) {
1729 return ldb_oom(ldb
);
1733 return replmd_update_rpmd_element(ldb
, msg
, &new_el
, NULL
,
1734 omd
, ar
->schema
, &ar
->seq_num
,
1735 &ar
->our_invocation_id
,
1736 now
, is_schema_nc
, is_forced_rodc
,
1741 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd
)
1743 uint32_t count
= omd
.ctr
.ctr1
.count
;
1746 for (i
=0; i
< count
; i
++) {
1747 struct replPropertyMetaData1 m
= omd
.ctr
.ctr1
.array
[i
];
1748 if (max
< m
.local_usn
) {
1756 * update the replPropertyMetaData object each time we modify an
1757 * object. This is needed for DRS replication, as the merge on the
1758 * client is based on this object
1760 static int replmd_update_rpmd(struct ldb_module
*module
,
1761 const struct dsdb_schema
*schema
,
1762 struct ldb_request
*req
,
1763 const char * const *rename_attrs
,
1764 struct ldb_message
*msg
, uint64_t *seq_num
,
1765 time_t t
, bool is_schema_nc
,
1766 bool *is_urgent
, bool *rodc
)
1768 const struct ldb_val
*omd_value
;
1769 enum ndr_err_code ndr_err
;
1770 struct replPropertyMetaDataBlob omd
;
1773 const struct GUID
*our_invocation_id
;
1775 const char * const *attrs
= NULL
;
1776 const char * const attrs2
[] = { "uSNChanged", "objectClass", "instanceType", NULL
};
1777 struct ldb_result
*res
;
1778 struct ldb_context
*ldb
;
1779 struct ldb_message_element
*objectclass_el
;
1780 enum urgent_situation situation
;
1781 bool rmd_is_provided
;
1782 bool rmd_is_just_resorted
= false;
1783 const char *not_rename_attrs
[4 + msg
->num_elements
];
1784 bool is_forced_rodc
= false;
1787 attrs
= rename_attrs
;
1789 for (i
= 0; i
< msg
->num_elements
; i
++) {
1790 not_rename_attrs
[i
] = msg
->elements
[i
].name
;
1792 not_rename_attrs
[i
] = "replPropertyMetaData";
1793 not_rename_attrs
[i
+1] = "objectClass";
1794 not_rename_attrs
[i
+2] = "instanceType";
1795 not_rename_attrs
[i
+3] = NULL
;
1796 attrs
= not_rename_attrs
;
1799 ldb
= ldb_module_get_ctx(module
);
1801 ret
= samdb_rodc(ldb
, rodc
);
1802 if (ret
!= LDB_SUCCESS
) {
1803 DEBUG(4, (__location__
": unable to tell if we are an RODC\n"));
1808 ldb_request_get_control(req
, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE
)) {
1809 is_forced_rodc
= true;
1812 our_invocation_id
= samdb_ntds_invocation_id(ldb
);
1813 if (!our_invocation_id
) {
1814 /* this happens during an initial vampire while
1815 updating the schema */
1816 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1820 unix_to_nt_time(&now
, t
);
1822 if (ldb_request_get_control(req
, DSDB_CONTROL_CHANGEREPLMETADATA_OID
)) {
1823 rmd_is_provided
= true;
1824 if (ldb_request_get_control(req
, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID
)) {
1825 rmd_is_just_resorted
= true;
1828 rmd_is_provided
= false;
1831 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1832 * otherwise we consider we are updating */
1833 if (ldb_msg_check_string_attribute(msg
, "isDeleted", "TRUE")) {
1834 situation
= REPL_URGENT_ON_DELETE
;
1835 } else if (rename_attrs
) {
1836 situation
= REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_DELETE
;
1838 situation
= REPL_URGENT_ON_UPDATE
;
1841 if (rmd_is_provided
) {
1842 /* In this case the change_replmetadata control was supplied */
1843 /* We check that it's the only attribute that is provided
1844 * (it's a rare case so it's better to keep the code simplier)
1845 * We also check that the highest local_usn is bigger or the same as
1848 if( msg
->num_elements
!= 1 ||
1849 strncmp(msg
->elements
[0].name
,
1850 "replPropertyMetaData", 20) ) {
1851 DEBUG(0,(__location__
": changereplmetada control called without "\
1852 "a specified replPropertyMetaData attribute or with others\n"));
1853 return LDB_ERR_OPERATIONS_ERROR
;
1855 if (situation
!= REPL_URGENT_ON_UPDATE
) {
1856 DEBUG(0,(__location__
": changereplmetada control can't be called when deleting an object\n"));
1857 return LDB_ERR_OPERATIONS_ERROR
;
1859 omd_value
= ldb_msg_find_ldb_val(msg
, "replPropertyMetaData");
1861 DEBUG(0,(__location__
": replPropertyMetaData was not specified for Object %s\n",
1862 ldb_dn_get_linearized(msg
->dn
)));
1863 return LDB_ERR_OPERATIONS_ERROR
;
1865 ndr_err
= ndr_pull_struct_blob(omd_value
, msg
, &omd
,
1866 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
1867 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1868 DEBUG(0,(__location__
": Failed to parse replPropertyMetaData for %s\n",
1869 ldb_dn_get_linearized(msg
->dn
)));
1870 return LDB_ERR_OPERATIONS_ERROR
;
1873 ret
= dsdb_module_search_dn(module
, msg
, &res
, msg
->dn
, attrs2
,
1874 DSDB_FLAG_NEXT_MODULE
|
1875 DSDB_SEARCH_SHOW_RECYCLED
|
1876 DSDB_SEARCH_SHOW_EXTENDED_DN
|
1877 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
|
1878 DSDB_SEARCH_REVEAL_INTERNALS
, req
);
1880 if (ret
!= LDB_SUCCESS
) {
1884 if (rmd_is_just_resorted
== false) {
1885 *seq_num
= find_max_local_usn(omd
);
1887 db_seq
= ldb_msg_find_attr_as_uint64(res
->msgs
[0], "uSNChanged", 0);
1890 * The test here now allows for a new
1891 * replPropertyMetaData with no change, if was
1892 * just dbcheck re-sorting the values.
1894 if (*seq_num
<= db_seq
) {
1895 DEBUG(0,(__location__
": changereplmetada control provided but max(local_usn)" \
1896 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1897 (long long)*seq_num
, (long long)db_seq
));
1898 return LDB_ERR_OPERATIONS_ERROR
;
1903 /* search for the existing replPropertyMetaDataBlob. We need
1904 * to use REVEAL and ask for DNs in storage format to support
1905 * the check for values being the same in
1906 * replmd_update_rpmd_element()
1908 ret
= dsdb_module_search_dn(module
, msg
, &res
, msg
->dn
, attrs
,
1909 DSDB_FLAG_NEXT_MODULE
|
1910 DSDB_SEARCH_SHOW_RECYCLED
|
1911 DSDB_SEARCH_SHOW_EXTENDED_DN
|
1912 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
|
1913 DSDB_SEARCH_REVEAL_INTERNALS
, req
);
1914 if (ret
!= LDB_SUCCESS
) {
1918 omd_value
= ldb_msg_find_ldb_val(res
->msgs
[0], "replPropertyMetaData");
1920 DEBUG(0,(__location__
": Object %s does not have a replPropertyMetaData attribute\n",
1921 ldb_dn_get_linearized(msg
->dn
)));
1922 return LDB_ERR_OPERATIONS_ERROR
;
1925 ndr_err
= ndr_pull_struct_blob(omd_value
, msg
, &omd
,
1926 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
1927 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1928 DEBUG(0,(__location__
": Failed to parse replPropertyMetaData for %s\n",
1929 ldb_dn_get_linearized(msg
->dn
)));
1930 return LDB_ERR_OPERATIONS_ERROR
;
1933 if (omd
.version
!= 1) {
1934 DEBUG(0,(__location__
": bad version %u in replPropertyMetaData for %s\n",
1935 omd
.version
, ldb_dn_get_linearized(msg
->dn
)));
1936 return LDB_ERR_OPERATIONS_ERROR
;
1939 for (i
=0; i
<msg
->num_elements
;) {
1940 struct ldb_message_element
*el
= &msg
->elements
[i
];
1941 struct ldb_message_element
*old_el
;
1943 old_el
= ldb_msg_find_element(res
->msgs
[0], el
->name
);
1944 ret
= replmd_update_rpmd_element(ldb
, msg
, el
, old_el
,
1945 &omd
, schema
, seq_num
,
1950 if (ret
!= LDB_SUCCESS
) {
1954 if (!*is_urgent
&& (situation
== REPL_URGENT_ON_UPDATE
)) {
1955 *is_urgent
= replmd_check_urgent_attribute(el
);
1958 if (!(el
->flags
& DSDB_FLAG_INTERNAL_FORCE_META_DATA
)) {
1963 el
->flags
&= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA
;
1965 if (el
->num_values
!= 0) {
1970 ldb_msg_remove_element(msg
, el
);
1975 * Assert that we have an objectClass attribute - this is major
1976 * corruption if we don't have this!
1978 objectclass_el
= ldb_msg_find_element(res
->msgs
[0], "objectClass");
1979 if (objectclass_el
!= NULL
) {
1981 * Now check if this objectClass means we need to do urgent replication
1983 if (!*is_urgent
&& replmd_check_urgent_objectclass(objectclass_el
,
1987 } else if (!ldb_request_get_control(req
, DSDB_CONTROL_DBCHECK
)) {
1988 ldb_asprintf_errstring(ldb
, __location__
1989 ": objectClass missing on %s\n",
1990 ldb_dn_get_linearized(msg
->dn
));
1991 return LDB_ERR_OBJECT_CLASS_VIOLATION
;
1995 * replmd_update_rpmd_element has done an update if the
1998 if (*seq_num
!= 0 || rmd_is_just_resorted
== true) {
1999 struct ldb_val
*md_value
;
2000 struct ldb_message_element
*el
;
2002 /*if we are RODC and this is a DRSR update then its ok*/
2003 if (!ldb_request_get_control(req
, DSDB_CONTROL_REPLICATED_UPDATE_OID
)
2004 && !ldb_request_get_control(req
, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA
)
2005 && !is_forced_rodc
) {
2006 unsigned instanceType
;
2009 ldb_set_errstring(ldb
, "RODC modify is forbidden!");
2010 return LDB_ERR_REFERRAL
;
2013 instanceType
= ldb_msg_find_attr_as_uint(res
->msgs
[0], "instanceType", INSTANCE_TYPE_WRITE
);
2014 if (!(instanceType
& INSTANCE_TYPE_WRITE
)) {
2015 return ldb_error(ldb
, LDB_ERR_UNWILLING_TO_PERFORM
,
2016 "cannot change replicated attribute on partial replica");
2020 md_value
= talloc(msg
, struct ldb_val
);
2021 if (md_value
== NULL
) {
2023 return LDB_ERR_OPERATIONS_ERROR
;
2026 ret
= replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb
, &omd
.ctr
.ctr1
, msg
->dn
);
2027 if (ret
!= LDB_SUCCESS
) {
2028 ldb_asprintf_errstring(ldb
, "%s: %s", __func__
, ldb_errstring(ldb
));
2032 ndr_err
= ndr_push_struct_blob(md_value
, msg
, &omd
,
2033 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
2034 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
2035 DEBUG(0,(__location__
": Failed to marshall replPropertyMetaData for %s\n",
2036 ldb_dn_get_linearized(msg
->dn
)));
2037 return LDB_ERR_OPERATIONS_ERROR
;
2040 ret
= ldb_msg_add_empty(msg
, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE
, &el
);
2041 if (ret
!= LDB_SUCCESS
) {
2042 DEBUG(0,(__location__
": Failed to add updated replPropertyMetaData %s\n",
2043 ldb_dn_get_linearized(msg
->dn
)));
2048 el
->values
= md_value
;
2054 static int parsed_dn_compare(struct parsed_dn
*pdn1
, struct parsed_dn
*pdn2
)
2056 int ret
= ndr_guid_compare(&pdn1
->guid
, &pdn2
->guid
);
2058 return data_blob_cmp(&pdn1
->dsdb_dn
->extra_part
,
2059 &pdn2
->dsdb_dn
->extra_part
);
2065 get a series of message element values as an array of DNs and GUIDs
2066 the result is sorted by GUID
2068 static int get_parsed_dns(struct ldb_module
*module
, TALLOC_CTX
*mem_ctx
,
2069 struct ldb_message_element
*el
, struct parsed_dn
**pdn
,
2070 const char *ldap_oid
, struct ldb_request
*parent
)
2073 bool values_are_sorted
= true;
2074 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
2081 (*pdn
) = talloc_array(mem_ctx
, struct parsed_dn
, el
->num_values
);
2083 ldb_module_oom(module
);
2084 return LDB_ERR_OPERATIONS_ERROR
;
2087 for (i
=0; i
<el
->num_values
; i
++) {
2088 struct ldb_val
*v
= &el
->values
[i
];
2091 struct parsed_dn
*p
;
2095 p
->dsdb_dn
= dsdb_dn_parse(*pdn
, ldb
, v
, ldap_oid
);
2096 if (p
->dsdb_dn
== NULL
) {
2097 return LDB_ERR_INVALID_DN_SYNTAX
;
2100 dn
= p
->dsdb_dn
->dn
;
2102 status
= dsdb_get_extended_dn_guid(dn
, &p
->guid
, "GUID");
2103 if (NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
) ||
2104 unlikely(GUID_all_zero(&p
->guid
))) {
2105 /* we got a DN without a GUID - go find the GUID */
2106 int ret
= dsdb_module_guid_by_dn(module
, dn
, &p
->guid
, parent
);
2107 if (ret
!= LDB_SUCCESS
) {
2108 char *dn_str
= NULL
;
2109 dn_str
= ldb_dn_get_extended_linearized(mem_ctx
,
2111 ldb_asprintf_errstring(ldb
,
2112 "Unable to find GUID for DN %s\n",
2114 if (ret
== LDB_ERR_NO_SUCH_OBJECT
&&
2115 LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_DELETE
&&
2116 ldb_attr_cmp(el
->name
, "member") == 0) {
2117 return LDB_ERR_UNWILLING_TO_PERFORM
;
2121 ret
= dsdb_set_extended_dn_guid(dn
, &p
->guid
, "GUID");
2122 if (ret
!= LDB_SUCCESS
) {
2125 } else if (!NT_STATUS_IS_OK(status
)) {
2126 return LDB_ERR_OPERATIONS_ERROR
;
2128 if (i
> 0 && values_are_sorted
) {
2129 int cmp
= parsed_dn_compare(p
, &(*pdn
)[i
- 1]);
2131 values_are_sorted
= false;
2134 /* keep a pointer to the original ldb_val */
2137 if (! values_are_sorted
) {
2138 TYPESAFE_QSORT(*pdn
, el
->num_values
, parsed_dn_compare
);
2144 * Get a series of trusted message element values. The result is sorted by
2145 * GUID, even though the GUIDs might not be known. That works because we trust
2146 * the database to give us the elements like that if the
2147 * replmd_private->sorted_links flag is set.
2149 * We also ensure that the links are in the Functional Level 2003
2150 * linked attributes format.
2152 static int get_parsed_dns_trusted_fallback(struct ldb_module
*module
,
2153 struct replmd_private
*replmd_private
,
2154 TALLOC_CTX
*mem_ctx
,
2155 struct ldb_message_element
*el
,
2156 struct parsed_dn
**pdn
,
2157 const char *ldap_oid
,
2158 struct ldb_request
*parent
)
2166 if (!replmd_private
->sorted_links
) {
2167 /* We need to sort the list. This is the slow old path we want
2170 ret
= get_parsed_dns(module
, mem_ctx
, el
, pdn
, ldap_oid
,
2172 if (ret
!= LDB_SUCCESS
) {
2176 ret
= get_parsed_dns_trusted(mem_ctx
, el
, pdn
);
2177 if (ret
!= LDB_SUCCESS
) {
2178 ldb_module_oom(module
);
2179 return LDB_ERR_OPERATIONS_ERROR
;
2184 * This upgrades links to FL2003 style, and sorts the result
2185 * if that was needed.
2187 * TODO: Add a database feature that asserts we have no FL2000
2188 * style links to avoid this check or add a feature that
2189 * uses a similar check to find sorted/unsorted links
2190 * for an on-the-fly upgrade.
2193 ret
= replmd_check_upgrade_links(ldb_module_get_ctx(module
),
2194 *pdn
, el
->num_values
,
2197 if (ret
!= LDB_SUCCESS
) {
2205 Return LDB_SUCCESS if a parsed_dn list contains no duplicate values,
2206 otherwise an error code. For compatibility the error code differs depending
2207 on whether or not the attribute is "member".
2209 As always, the parsed_dn list is assumed to be sorted.
2211 static int check_parsed_dn_duplicates(struct ldb_module
*module
,
2212 struct ldb_message_element
*el
,
2213 struct parsed_dn
*pdn
)
2216 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
2218 for (i
= 1; i
< el
->num_values
; i
++) {
2219 struct parsed_dn
*p
= &pdn
[i
];
2220 if (parsed_dn_compare(p
, &pdn
[i
- 1]) == 0) {
2221 ldb_asprintf_errstring(ldb
,
2222 "Linked attribute %s has "
2223 "multiple identical values",
2225 if (ldb_attr_cmp(el
->name
, "member") == 0) {
2226 return LDB_ERR_ENTRY_ALREADY_EXISTS
;
2228 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS
;
2236 build a new extended DN, including all meta data fields
2238 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2239 RMD_ADDTIME = originating_add_time
2240 RMD_INVOCID = originating_invocation_id
2241 RMD_CHANGETIME = originating_change_time
2242 RMD_ORIGINATING_USN = originating_usn
2243 RMD_LOCAL_USN = local_usn
2244 RMD_VERSION = version
2246 static int replmd_build_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
,
2247 struct dsdb_dn
*dsdb_dn
,
2248 const struct GUID
*invocation_id
,
2249 uint64_t local_usn
, NTTIME nttime
)
2251 return replmd_set_la_val(mem_ctx
, v
, dsdb_dn
, NULL
, invocation_id
,
2252 local_usn
, local_usn
, nttime
,
2253 RMD_VERSION_INITIAL
, false);
2256 static int replmd_update_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
, struct dsdb_dn
*dsdb_dn
,
2257 struct dsdb_dn
*old_dsdb_dn
, const struct GUID
*invocation_id
,
2258 uint64_t seq_num
, uint64_t local_usn
, NTTIME nttime
,
2262 check if any links need upgrading from w2k format
2264 static int replmd_check_upgrade_links(struct ldb_context
*ldb
,
2265 struct parsed_dn
*dns
, uint32_t count
,
2266 struct ldb_message_element
*el
,
2267 const char *ldap_oid
)
2270 const struct GUID
*invocation_id
= NULL
;
2271 for (i
=0; i
<count
; i
++) {
2275 if (dns
[i
].dsdb_dn
== NULL
) {
2276 ret
= really_parse_trusted_dn(dns
, ldb
, &dns
[i
],
2278 if (ret
!= LDB_SUCCESS
) {
2279 return LDB_ERR_INVALID_DN_SYNTAX
;
2283 status
= dsdb_get_extended_dn_uint32(dns
[i
].dsdb_dn
->dn
,
2284 &version
, "RMD_VERSION");
2285 if (!NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)) {
2287 * We optimistically assume they are all the same; if
2288 * the first one is fixed, they are all fixed.
2290 * If the first one was *not* fixed and we find a
2291 * later one that is, that is an occasion to shout
2297 DEBUG(0, ("Mixed w2k and fixed format "
2298 "linked attributes\n"));
2302 if (invocation_id
== NULL
) {
2303 invocation_id
= samdb_ntds_invocation_id(ldb
);
2304 if (invocation_id
== NULL
) {
2305 return LDB_ERR_OPERATIONS_ERROR
;
2310 /* it's an old one that needs upgrading */
2311 ret
= replmd_update_la_val(el
->values
, dns
[i
].v
,
2312 dns
[i
].dsdb_dn
, dns
[i
].dsdb_dn
,
2313 invocation_id
, 1, 1, 0, false);
2314 if (ret
!= LDB_SUCCESS
) {
2320 * This sort() is critical for the operation of
2321 * get_parsed_dns_trusted_fallback() because callers of this function
2322 * expect a sorted list, and FL2000 style links are not
2323 * sorted. In particular, as well as the upgrade case,
2324 * get_parsed_dns_trusted_fallback() is called from
2325 * replmd_delete_remove_link() even in FL2000 mode
2327 * We do not normally pay the cost of the qsort() due to the
2328 * early return in the RMD_VERSION found case.
2330 TYPESAFE_QSORT(dns
, count
, parsed_dn_compare
);
2335 Sets the value for a linked attribute, including all meta data fields
2337 see replmd_build_la_val for value names
2339 static int replmd_set_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
, struct dsdb_dn
*dsdb_dn
,
2340 struct dsdb_dn
*old_dsdb_dn
, const struct GUID
*invocation_id
,
2341 uint64_t usn
, uint64_t local_usn
, NTTIME nttime
,
2342 uint32_t version
, bool deleted
)
2344 struct ldb_dn
*dn
= dsdb_dn
->dn
;
2345 const char *tstring
, *usn_string
, *flags_string
;
2346 struct ldb_val tval
;
2348 struct ldb_val usnv
, local_usnv
;
2349 struct ldb_val vers
, flagsv
;
2350 const struct ldb_val
*old_addtime
= NULL
;
2353 const char *dnstring
;
2355 uint32_t rmd_flags
= deleted
?DSDB_RMD_FLAG_DELETED
:0;
2357 tstring
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)nttime
);
2359 return LDB_ERR_OPERATIONS_ERROR
;
2361 tval
= data_blob_string_const(tstring
);
2363 usn_string
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)usn
);
2365 return LDB_ERR_OPERATIONS_ERROR
;
2367 usnv
= data_blob_string_const(usn_string
);
2369 usn_string
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)local_usn
);
2371 return LDB_ERR_OPERATIONS_ERROR
;
2373 local_usnv
= data_blob_string_const(usn_string
);
2375 status
= GUID_to_ndr_blob(invocation_id
, dn
, &iid
);
2376 if (!NT_STATUS_IS_OK(status
)) {
2377 return LDB_ERR_OPERATIONS_ERROR
;
2380 flags_string
= talloc_asprintf(mem_ctx
, "%u", rmd_flags
);
2381 if (!flags_string
) {
2382 return LDB_ERR_OPERATIONS_ERROR
;
2384 flagsv
= data_blob_string_const(flags_string
);
2386 ret
= ldb_dn_set_extended_component(dn
, "RMD_FLAGS", &flagsv
);
2387 if (ret
!= LDB_SUCCESS
) return ret
;
2389 /* get the ADDTIME from the original */
2390 if (old_dsdb_dn
!= NULL
) {
2391 old_addtime
= ldb_dn_get_extended_component(old_dsdb_dn
->dn
,
2394 if (old_addtime
== NULL
) {
2395 old_addtime
= &tval
;
2397 if (dsdb_dn
!= old_dsdb_dn
||
2398 ldb_dn_get_extended_component(dn
, "RMD_ADDTIME") == NULL
) {
2399 ret
= ldb_dn_set_extended_component(dn
, "RMD_ADDTIME", old_addtime
);
2400 if (ret
!= LDB_SUCCESS
) return ret
;
2403 /* use our invocation id */
2404 ret
= ldb_dn_set_extended_component(dn
, "RMD_INVOCID", &iid
);
2405 if (ret
!= LDB_SUCCESS
) return ret
;
2407 /* changetime is the current time */
2408 ret
= ldb_dn_set_extended_component(dn
, "RMD_CHANGETIME", &tval
);
2409 if (ret
!= LDB_SUCCESS
) return ret
;
2411 /* update the USN */
2412 ret
= ldb_dn_set_extended_component(dn
, "RMD_ORIGINATING_USN", &usnv
);
2413 if (ret
!= LDB_SUCCESS
) return ret
;
2415 ret
= ldb_dn_set_extended_component(dn
, "RMD_LOCAL_USN", &local_usnv
);
2416 if (ret
!= LDB_SUCCESS
) return ret
;
2418 vstring
= talloc_asprintf(mem_ctx
, "%lu", (unsigned long)version
);
2419 vers
= data_blob_string_const(vstring
);
2420 ret
= ldb_dn_set_extended_component(dn
, "RMD_VERSION", &vers
);
2421 if (ret
!= LDB_SUCCESS
) return ret
;
2423 dnstring
= dsdb_dn_get_extended_linearized(mem_ctx
, dsdb_dn
, 1);
2424 if (dnstring
== NULL
) {
2425 return LDB_ERR_OPERATIONS_ERROR
;
2427 *v
= data_blob_string_const(dnstring
);
2433 * Updates the value for a linked attribute, including all meta data fields
2435 static int replmd_update_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
, struct dsdb_dn
*dsdb_dn
,
2436 struct dsdb_dn
*old_dsdb_dn
, const struct GUID
*invocation_id
,
2437 uint64_t usn
, uint64_t local_usn
, NTTIME nttime
,
2440 uint32_t old_version
;
2441 uint32_t version
= RMD_VERSION_INITIAL
;
2445 * We're updating the linked attribute locally, so increase the version
2446 * by 1 so that other DCs will see the change when it gets replicated out
2448 status
= dsdb_get_extended_dn_uint32(old_dsdb_dn
->dn
, &old_version
,
2451 if (NT_STATUS_IS_OK(status
)) {
2452 version
= old_version
+ 1;
2455 return replmd_set_la_val(mem_ctx
, v
, dsdb_dn
, old_dsdb_dn
, invocation_id
,
2456 usn
, local_usn
, nttime
, version
, deleted
);
2460 handle adding a linked attribute
2462 static int replmd_modify_la_add(struct ldb_module
*module
,
2463 struct replmd_private
*replmd_private
,
2464 struct replmd_replicated_request
*ac
,
2465 struct ldb_message
*msg
,
2466 struct ldb_message_element
*el
,
2467 struct ldb_message_element
*old_el
,
2468 const struct dsdb_attribute
*schema_attr
,
2470 struct ldb_dn
*msg_dn
,
2471 struct ldb_request
*parent
)
2474 struct parsed_dn
*dns
, *old_dns
;
2475 TALLOC_CTX
*tmp_ctx
= talloc_new(msg
);
2477 struct ldb_val
*new_values
= NULL
;
2478 unsigned old_num_values
= old_el
? old_el
->num_values
: 0;
2479 unsigned num_values
= 0;
2480 unsigned max_num_values
;
2481 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
2483 unix_to_nt_time(&now
, t
);
2485 /* get the DNs to be added, fully parsed.
2487 * We need full parsing because they came off the wire and we don't
2488 * trust them, besides which we need their details to know where to put
2491 ret
= get_parsed_dns(module
, tmp_ctx
, el
, &dns
,
2492 schema_attr
->syntax
->ldap_oid
, parent
);
2493 if (ret
!= LDB_SUCCESS
) {
2494 talloc_free(tmp_ctx
);
2498 /* get the existing DNs, lazily parsed */
2499 ret
= get_parsed_dns_trusted_fallback(module
, replmd_private
,
2500 tmp_ctx
, old_el
, &old_dns
,
2501 schema_attr
->syntax
->ldap_oid
,
2504 if (ret
!= LDB_SUCCESS
) {
2505 talloc_free(tmp_ctx
);
2509 max_num_values
= old_num_values
+ el
->num_values
;
2510 if (max_num_values
< old_num_values
) {
2511 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2512 "old values: %u, new values: %u, sum: %u\n",
2513 old_num_values
, el
->num_values
, max_num_values
));
2514 talloc_free(tmp_ctx
);
2515 return LDB_ERR_OPERATIONS_ERROR
;
2518 new_values
= talloc_zero_array(tmp_ctx
, struct ldb_val
, max_num_values
);
2520 if (new_values
== NULL
) {
2521 ldb_module_oom(module
);
2522 talloc_free(tmp_ctx
);
2523 return LDB_ERR_OPERATIONS_ERROR
;
2527 * For each new value, find where it would go in the list. If there is
2528 * a matching GUID there, we update the existing value; otherwise we
2532 for (i
= 0; i
< el
->num_values
; i
++) {
2533 struct parsed_dn
*exact
;
2534 struct parsed_dn
*next
;
2536 int err
= parsed_dn_find(ldb
, old_dns
, old_num_values
,
2539 dns
[i
].dsdb_dn
->extra_part
, 0,
2541 schema_attr
->syntax
->ldap_oid
,
2543 if (err
!= LDB_SUCCESS
) {
2544 talloc_free(tmp_ctx
);
2548 if (ac
->fix_link_sid
) {
2549 char *fixed_dnstring
= NULL
;
2550 struct dom_sid tmp_sid
= { 0, };
2551 DATA_BLOB sid_blob
= data_blob_null
;
2552 enum ndr_err_code ndr_err
;
2556 if (exact
== NULL
) {
2557 talloc_free(tmp_ctx
);
2558 return ldb_operr(ldb
);
2561 if (dns
[i
].dsdb_dn
->dn_format
!= DSDB_NORMAL_DN
) {
2562 talloc_free(tmp_ctx
);
2563 return ldb_operr(ldb
);
2567 * Only "<GUID=...><SID=...>" is allowed.
2569 * We get the GUID to just to find the old
2570 * value and the SID in order to add it
2571 * to the found value.
2574 num
= ldb_dn_get_comp_num(dns
[i
].dsdb_dn
->dn
);
2576 talloc_free(tmp_ctx
);
2577 return ldb_operr(ldb
);
2580 num
= ldb_dn_get_extended_comp_num(dns
[i
].dsdb_dn
->dn
);
2582 talloc_free(tmp_ctx
);
2583 return ldb_operr(ldb
);
2586 status
= dsdb_get_extended_dn_sid(exact
->dsdb_dn
->dn
,
2588 if (NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)) {
2589 /* this is what we expect */
2590 } else if (NT_STATUS_IS_OK(status
)) {
2591 struct GUID_txt_buf guid_str
;
2592 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
2593 "i[%u] SID NOT MISSING... Attribute %s already "
2594 "exists for target GUID %s, SID %s, DN: %s",
2596 GUID_buf_string(&exact
->guid
,
2598 dom_sid_string(tmp_ctx
, &tmp_sid
),
2599 dsdb_dn_get_extended_linearized(tmp_ctx
,
2600 exact
->dsdb_dn
, 1));
2601 talloc_free(tmp_ctx
);
2602 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS
;
2604 talloc_free(tmp_ctx
);
2605 return ldb_operr(ldb
);
2608 status
= dsdb_get_extended_dn_sid(dns
[i
].dsdb_dn
->dn
,
2610 if (!NT_STATUS_IS_OK(status
)) {
2611 struct GUID_txt_buf guid_str
;
2612 ldb_asprintf_errstring(ldb
,
2613 "NO SID PROVIDED... Attribute %s already "
2614 "exists for target GUID %s",
2616 GUID_buf_string(&exact
->guid
,
2618 talloc_free(tmp_ctx
);
2619 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS
;
2622 ndr_err
= ndr_push_struct_blob(&sid_blob
, tmp_ctx
, &tmp_sid
,
2623 (ndr_push_flags_fn_t
)ndr_push_dom_sid
);
2624 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
2625 talloc_free(tmp_ctx
);
2626 return ldb_operr(ldb
);
2629 ret
= ldb_dn_set_extended_component(exact
->dsdb_dn
->dn
, "SID", &sid_blob
);
2630 data_blob_free(&sid_blob
);
2631 if (ret
!= LDB_SUCCESS
) {
2632 talloc_free(tmp_ctx
);
2636 fixed_dnstring
= dsdb_dn_get_extended_linearized(
2637 new_values
, exact
->dsdb_dn
, 1);
2638 if (fixed_dnstring
== NULL
) {
2639 talloc_free(tmp_ctx
);
2640 return ldb_operr(ldb
);
2644 * We just replace the existing value...
2646 *exact
->v
= data_blob_string_const(fixed_dnstring
);
2651 if (exact
!= NULL
) {
2653 * We are trying to add one that exists, which is only
2654 * allowed if it was previously deleted.
2656 * When we do undelete a link we change it in place.
2657 * It will be copied across into the right spot in due
2661 rmd_flags
= dsdb_dn_rmd_flags(exact
->dsdb_dn
->dn
);
2663 if (!(rmd_flags
& DSDB_RMD_FLAG_DELETED
)) {
2664 struct GUID_txt_buf guid_str
;
2665 ldb_asprintf_errstring(ldb
,
2666 "Attribute %s already "
2667 "exists for target GUID %s",
2669 GUID_buf_string(&exact
->guid
,
2671 talloc_free(tmp_ctx
);
2672 /* error codes for 'member' need to be
2674 if (ldb_attr_cmp(el
->name
, "member") == 0) {
2675 return LDB_ERR_ENTRY_ALREADY_EXISTS
;
2677 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS
;
2681 ret
= replmd_update_la_val(new_values
, exact
->v
,
2684 &ac
->our_invocation_id
,
2685 ac
->seq_num
, ac
->seq_num
,
2687 if (ret
!= LDB_SUCCESS
) {
2688 talloc_free(tmp_ctx
);
2692 ret
= replmd_add_backlink(module
, replmd_private
,
2699 if (ret
!= LDB_SUCCESS
) {
2700 talloc_free(tmp_ctx
);
2706 * Here we don't have an exact match.
2708 * If next is NULL, this one goes beyond the end of the
2709 * existing list, so we need to add all of those ones first.
2711 * If next is not NULL, we need to add all the ones before
2715 offset
= old_num_values
;
2717 /* next should have been parsed, but let's make sure */
2718 if (next
->dsdb_dn
== NULL
) {
2719 ret
= really_parse_trusted_dn(tmp_ctx
, ldb
, next
,
2720 schema_attr
->syntax
->ldap_oid
);
2721 if (ret
!= LDB_SUCCESS
) {
2722 talloc_free(tmp_ctx
);
2726 offset
= MIN(next
- old_dns
, old_num_values
);
2729 /* put all the old ones before next on the list */
2730 for (; j
< offset
; j
++) {
2731 new_values
[num_values
] = *old_dns
[j
].v
;
2735 ret
= replmd_add_backlink(module
, replmd_private
,
2740 if (ret
!= LDB_SUCCESS
) {
2741 talloc_free(tmp_ctx
);
2744 /* Make the new linked attribute ldb_val. */
2745 ret
= replmd_build_la_val(new_values
, &new_values
[num_values
],
2746 dns
[i
].dsdb_dn
, &ac
->our_invocation_id
,
2748 if (ret
!= LDB_SUCCESS
) {
2749 talloc_free(tmp_ctx
);
2753 if (ret
!= LDB_SUCCESS
) {
2754 talloc_free(tmp_ctx
);
2758 /* copy the rest of the old ones (if any) */
2759 for (; j
< old_num_values
; j
++) {
2760 new_values
[num_values
] = *old_dns
[j
].v
;
2764 talloc_steal(msg
->elements
, new_values
);
2765 if (old_el
!= NULL
) {
2766 talloc_steal(msg
->elements
, old_el
->values
);
2768 el
->values
= new_values
;
2769 el
->num_values
= num_values
;
2771 talloc_free(tmp_ctx
);
2773 /* we now tell the backend to replace all existing values
2774 with the one we have constructed */
2775 el
->flags
= LDB_FLAG_MOD_REPLACE
;
2782 handle deleting all active linked attributes
2784 static int replmd_modify_la_delete(struct ldb_module
*module
,
2785 struct replmd_private
*replmd_private
,
2786 struct replmd_replicated_request
*ac
,
2787 struct ldb_message
*msg
,
2788 struct ldb_message_element
*el
,
2789 struct ldb_message_element
*old_el
,
2790 const struct dsdb_attribute
*schema_attr
,
2792 struct ldb_dn
*msg_dn
,
2793 struct ldb_request
*parent
)
2796 struct parsed_dn
*dns
, *old_dns
;
2797 TALLOC_CTX
*tmp_ctx
= NULL
;
2799 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
2800 struct ldb_control
*vanish_links_ctrl
= NULL
;
2801 bool vanish_links
= false;
2802 unsigned int num_to_delete
= el
->num_values
;
2806 unix_to_nt_time(&now
, t
);
2808 if (old_el
== NULL
|| old_el
->num_values
== 0) {
2809 /* there is nothing to delete... */
2810 if (num_to_delete
== 0) {
2811 /* and we're deleting nothing, so that's OK */
2814 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
2817 tmp_ctx
= talloc_new(msg
);
2818 if (tmp_ctx
== NULL
) {
2819 return LDB_ERR_OPERATIONS_ERROR
;
2822 ret
= get_parsed_dns(module
, tmp_ctx
, el
, &dns
,
2823 schema_attr
->syntax
->ldap_oid
, parent
);
2824 if (ret
!= LDB_SUCCESS
) {
2825 talloc_free(tmp_ctx
);
2829 ret
= get_parsed_dns_trusted_fallback(module
, replmd_private
,
2830 tmp_ctx
, old_el
, &old_dns
,
2831 schema_attr
->syntax
->ldap_oid
,
2834 if (ret
!= LDB_SUCCESS
) {
2835 talloc_free(tmp_ctx
);
2839 vanish_links_ctrl
= ldb_request_get_control(parent
, DSDB_CONTROL_REPLMD_VANISH_LINKS
);
2840 if (vanish_links_ctrl
) {
2841 vanish_links
= true;
2842 vanish_links_ctrl
->critical
= false;
2845 /* we empty out el->values here to avoid damage if we return early. */
2850 * If vanish links is set, we are actually removing members of
2851 * old_el->values; otherwise we are just marking them deleted.
2853 * There is a special case when no values are given: we remove them
2854 * all. When we have the vanish_links control we just have to remove
2855 * the backlinks and change our element to replace the existing values
2856 * with the empty list.
2859 if (num_to_delete
== 0) {
2860 for (i
= 0; i
< old_el
->num_values
; i
++) {
2861 struct parsed_dn
*p
= &old_dns
[i
];
2862 if (p
->dsdb_dn
== NULL
) {
2863 ret
= really_parse_trusted_dn(tmp_ctx
, ldb
, p
,
2864 schema_attr
->syntax
->ldap_oid
);
2865 if (ret
!= LDB_SUCCESS
) {
2869 ret
= replmd_add_backlink(module
, replmd_private
,
2870 ac
->schema
, msg_dn
, &p
->guid
,
2873 if (ret
!= LDB_SUCCESS
) {
2874 talloc_free(tmp_ctx
);
2881 rmd_flags
= dsdb_dn_rmd_flags(p
->dsdb_dn
->dn
);
2882 if (rmd_flags
& DSDB_RMD_FLAG_DELETED
) {
2886 ret
= replmd_update_la_val(old_el
->values
, p
->v
,
2887 p
->dsdb_dn
, p
->dsdb_dn
,
2888 &ac
->our_invocation_id
,
2889 ac
->seq_num
, ac
->seq_num
,
2891 if (ret
!= LDB_SUCCESS
) {
2892 talloc_free(tmp_ctx
);
2898 el
->flags
= LDB_FLAG_MOD_REPLACE
;
2899 talloc_free(tmp_ctx
);
2905 for (i
= 0; i
< num_to_delete
; i
++) {
2906 struct parsed_dn
*p
= &dns
[i
];
2907 struct parsed_dn
*exact
= NULL
;
2908 struct parsed_dn
*next
= NULL
;
2909 ret
= parsed_dn_find(ldb
, old_dns
, old_el
->num_values
,
2912 p
->dsdb_dn
->extra_part
, 0,
2914 schema_attr
->syntax
->ldap_oid
,
2916 if (ret
!= LDB_SUCCESS
) {
2917 talloc_free(tmp_ctx
);
2920 if (exact
== NULL
) {
2921 struct GUID_txt_buf buf
;
2922 ldb_asprintf_errstring(ldb
, "Attribute %s doesn't "
2923 "exist for target GUID %s",
2925 GUID_buf_string(&p
->guid
, &buf
));
2926 if (ldb_attr_cmp(el
->name
, "member") == 0) {
2927 talloc_free(tmp_ctx
);
2928 return LDB_ERR_UNWILLING_TO_PERFORM
;
2930 talloc_free(tmp_ctx
);
2931 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
2936 if (CHECK_DEBUGLVL(5)) {
2937 rmd_flags
= dsdb_dn_rmd_flags(exact
->dsdb_dn
->dn
);
2938 if ((rmd_flags
& DSDB_RMD_FLAG_DELETED
)) {
2939 struct GUID_txt_buf buf
;
2940 const char *guid_str
= \
2941 GUID_buf_string(&p
->guid
, &buf
);
2942 DEBUG(5, ("Deleting deleted linked "
2943 "attribute %s to %s, because "
2944 "vanish_links control is set\n",
2945 el
->name
, guid_str
));
2949 /* remove the backlink */
2950 ret
= replmd_add_backlink(module
,
2957 if (ret
!= LDB_SUCCESS
) {
2958 talloc_free(tmp_ctx
);
2962 /* We flag the deletion and tidy it up later. */
2967 rmd_flags
= dsdb_dn_rmd_flags(exact
->dsdb_dn
->dn
);
2969 if (rmd_flags
& DSDB_RMD_FLAG_DELETED
) {
2970 struct GUID_txt_buf buf
;
2971 const char *guid_str
= GUID_buf_string(&p
->guid
, &buf
);
2972 ldb_asprintf_errstring(ldb
, "Attribute %s already "
2973 "deleted for target GUID %s",
2974 el
->name
, guid_str
);
2975 if (ldb_attr_cmp(el
->name
, "member") == 0) {
2976 talloc_free(tmp_ctx
);
2977 return LDB_ERR_UNWILLING_TO_PERFORM
;
2979 talloc_free(tmp_ctx
);
2980 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
2984 ret
= replmd_update_la_val(old_el
->values
, exact
->v
,
2985 exact
->dsdb_dn
, exact
->dsdb_dn
,
2986 &ac
->our_invocation_id
,
2987 ac
->seq_num
, ac
->seq_num
,
2989 if (ret
!= LDB_SUCCESS
) {
2990 talloc_free(tmp_ctx
);
2993 ret
= replmd_add_backlink(module
, replmd_private
,
2998 if (ret
!= LDB_SUCCESS
) {
2999 talloc_free(tmp_ctx
);
3006 struct ldb_val
*tmp_vals
= NULL
;
3008 tmp_vals
= talloc_array(tmp_ctx
, struct ldb_val
,
3009 old_el
->num_values
);
3010 if (tmp_vals
== NULL
) {
3011 talloc_free(tmp_ctx
);
3012 return ldb_module_oom(module
);
3014 for (i
= 0; i
< old_el
->num_values
; i
++) {
3015 if (old_dns
[i
].v
== NULL
) {
3018 tmp_vals
[j
] = *old_dns
[i
].v
;
3021 for (i
= 0; i
< j
; i
++) {
3022 old_el
->values
[i
] = tmp_vals
[i
];
3024 old_el
->num_values
= j
;
3027 el
->values
= talloc_steal(msg
->elements
, old_el
->values
);
3028 el
->num_values
= old_el
->num_values
;
3030 talloc_free(tmp_ctx
);
3032 /* we now tell the backend to replace all existing values
3033 with the one we have constructed */
3034 el
->flags
= LDB_FLAG_MOD_REPLACE
;
3040 handle replacing a linked attribute
3042 static int replmd_modify_la_replace(struct ldb_module
*module
,
3043 struct replmd_private
*replmd_private
,
3044 struct replmd_replicated_request
*ac
,
3045 struct ldb_message
*msg
,
3046 struct ldb_message_element
*el
,
3047 struct ldb_message_element
*old_el
,
3048 const struct dsdb_attribute
*schema_attr
,
3050 struct ldb_dn
*msg_dn
,
3051 struct ldb_request
*parent
)
3053 unsigned int i
, old_i
, new_i
;
3054 struct parsed_dn
*dns
, *old_dns
;
3055 TALLOC_CTX
*tmp_ctx
= talloc_new(msg
);
3057 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
3058 struct ldb_val
*new_values
= NULL
;
3059 const char *ldap_oid
= schema_attr
->syntax
->ldap_oid
;
3060 unsigned int old_num_values
;
3061 unsigned int repl_num_values
;
3062 unsigned int max_num_values
;
3065 unix_to_nt_time(&now
, t
);
3068 * The replace operation is unlike the replace and delete cases in that
3069 * we need to look at every existing link to see whether it is being
3070 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
3072 * As we are trying to combine two sorted lists, the algorithm we use
3073 * is akin to the merge phase of a merge sort. We interleave the two
3074 * lists, doing different things depending on which side the current
3077 * There are three main cases, with some sub-cases.
3079 * - a DN is in the old list but not the new one. It needs to be
3080 * marked as deleted (but left in the list).
3081 * - maybe it is already deleted, and we have less to do.
3083 * - a DN is in both lists. The old data gets replaced by the new,
3084 * and the list doesn't grow. The old link may have been marked as
3085 * deleted, in which case we undelete it.
3087 * - a DN is in the new list only. We add it in the right place.
3090 old_num_values
= old_el
? old_el
->num_values
: 0;
3091 repl_num_values
= el
->num_values
;
3092 max_num_values
= old_num_values
+ repl_num_values
;
3094 if (max_num_values
== 0) {
3095 /* There is nothing to do! */
3100 * At the successful end of these functions el->values is
3101 * overwritten with new_values. However get_parsed_dns()
3102 * points p->v at the supplied el and it effectively gets used
3103 * as a working area by replmd_build_la_val(). So we must
3104 * duplicate it because our caller only called
3105 * ldb_msg_copy_shallow().
3108 el
->values
= talloc_memdup(tmp_ctx
,
3110 sizeof(el
->values
[0]) * el
->num_values
);
3111 if (el
->values
== NULL
) {
3112 ldb_module_oom(module
);
3113 talloc_free(tmp_ctx
);
3114 return LDB_ERR_OPERATIONS_ERROR
;
3117 ret
= get_parsed_dns(module
, tmp_ctx
, el
, &dns
, ldap_oid
, parent
);
3118 if (ret
!= LDB_SUCCESS
) {
3119 talloc_free(tmp_ctx
);
3123 ret
= check_parsed_dn_duplicates(module
, el
, dns
);
3124 if (ret
!= LDB_SUCCESS
) {
3125 talloc_free(tmp_ctx
);
3129 ret
= get_parsed_dns(module
, tmp_ctx
, old_el
, &old_dns
,
3131 if (ret
!= LDB_SUCCESS
) {
3132 talloc_free(tmp_ctx
);
3136 ret
= replmd_check_upgrade_links(ldb
, old_dns
, old_num_values
,
3138 if (ret
!= LDB_SUCCESS
) {
3139 talloc_free(tmp_ctx
);
3143 new_values
= talloc_array(tmp_ctx
, struct ldb_val
, max_num_values
);
3144 if (new_values
== NULL
) {
3145 ldb_module_oom(module
);
3146 talloc_free(tmp_ctx
);
3147 return LDB_ERR_OPERATIONS_ERROR
;
3152 for (i
= 0; i
< max_num_values
; i
++) {
3154 struct parsed_dn
*old_p
, *new_p
;
3155 if (old_i
< old_num_values
&& new_i
< repl_num_values
) {
3156 old_p
= &old_dns
[old_i
];
3157 new_p
= &dns
[new_i
];
3158 cmp
= parsed_dn_compare(old_p
, new_p
);
3159 } else if (old_i
< old_num_values
) {
3160 /* the new list is empty, read the old list */
3161 old_p
= &old_dns
[old_i
];
3164 } else if (new_i
< repl_num_values
) {
3165 /* the old list is empty, read new list */
3167 new_p
= &dns
[new_i
];
3175 * An old ones that come before the next replacement
3176 * (if any). We mark it as deleted and add it to the
3179 uint32_t rmd_flags
= dsdb_dn_rmd_flags(old_p
->dsdb_dn
->dn
);
3180 if ((rmd_flags
& DSDB_RMD_FLAG_DELETED
) == 0) {
3181 ret
= replmd_update_la_val(new_values
, old_p
->v
,
3184 &ac
->our_invocation_id
,
3185 ac
->seq_num
, ac
->seq_num
,
3187 if (ret
!= LDB_SUCCESS
) {
3188 talloc_free(tmp_ctx
);
3192 ret
= replmd_add_backlink(module
, replmd_private
,
3195 &old_p
->guid
, false,
3198 if (ret
!= LDB_SUCCESS
) {
3199 talloc_free(tmp_ctx
);
3203 new_values
[i
] = *old_p
->v
;
3205 } else if (cmp
== 0) {
3207 * We are overwriting one. If it was previously
3208 * deleted, we need to add a backlink.
3210 * Note that if any RMD_FLAGs in an extended new DN
3215 ret
= replmd_update_la_val(new_values
, old_p
->v
,
3218 &ac
->our_invocation_id
,
3219 ac
->seq_num
, ac
->seq_num
,
3221 if (ret
!= LDB_SUCCESS
) {
3222 talloc_free(tmp_ctx
);
3226 rmd_flags
= dsdb_dn_rmd_flags(old_p
->dsdb_dn
->dn
);
3227 if ((rmd_flags
& DSDB_RMD_FLAG_DELETED
) != 0) {
3228 ret
= replmd_add_backlink(module
, replmd_private
,
3234 if (ret
!= LDB_SUCCESS
) {
3235 talloc_free(tmp_ctx
);
3240 new_values
[i
] = *old_p
->v
;
3245 * Replacements that don't match an existing one. We
3246 * just add them to the final list.
3248 ret
= replmd_build_la_val(new_values
,
3251 &ac
->our_invocation_id
,
3253 if (ret
!= LDB_SUCCESS
) {
3254 talloc_free(tmp_ctx
);
3257 ret
= replmd_add_backlink(module
, replmd_private
,
3263 if (ret
!= LDB_SUCCESS
) {
3264 talloc_free(tmp_ctx
);
3267 new_values
[i
] = *new_p
->v
;
3271 if (old_el
!= NULL
) {
3272 talloc_steal(msg
->elements
, old_el
->values
);
3274 el
->values
= talloc_steal(msg
->elements
, new_values
);
3276 talloc_free(tmp_ctx
);
3278 el
->flags
= LDB_FLAG_MOD_REPLACE
;
3285 handle linked attributes in modify requests
3287 static int replmd_modify_handle_linked_attribs(struct ldb_module
*module
,
3288 struct replmd_private
*replmd_private
,
3289 struct replmd_replicated_request
*ac
,
3290 struct ldb_message
*msg
,
3292 struct ldb_request
*parent
)
3294 struct ldb_result
*res
;
3297 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
3298 struct ldb_message
*old_msg
;
3300 if (dsdb_functional_level(ldb
) == DS_DOMAIN_FUNCTION_2000
) {
3302 * Nothing special is required for modifying or vanishing links
3303 * in fl2000 since they are just strings in a multi-valued
3306 struct ldb_control
*ctrl
= ldb_request_get_control(parent
,
3307 DSDB_CONTROL_REPLMD_VANISH_LINKS
);
3309 ctrl
->critical
= false;
3317 * We should restrict this to the intersection of the list of
3318 * linked attributes in the schema and the list of attributes
3321 * This will help performance a little, as otherwise we have
3322 * to allocate the entire object value-by-value.
3324 ret
= dsdb_module_search_dn(module
, msg
, &res
, msg
->dn
, NULL
,
3325 DSDB_FLAG_NEXT_MODULE
|
3326 DSDB_SEARCH_SHOW_RECYCLED
|
3327 DSDB_SEARCH_REVEAL_INTERNALS
|
3328 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
,
3330 if (ret
!= LDB_SUCCESS
) {
3334 old_msg
= res
->msgs
[0];
3336 for (i
=0; i
<msg
->num_elements
; i
++) {
3337 struct ldb_message_element
*el
= &msg
->elements
[i
];
3338 struct ldb_message_element
*old_el
, *new_el
;
3339 unsigned int mod_type
= LDB_FLAG_MOD_TYPE(el
->flags
);
3340 const struct dsdb_attribute
*schema_attr
3341 = dsdb_attribute_by_lDAPDisplayName(ac
->schema
, el
->name
);
3343 ldb_asprintf_errstring(ldb
,
3344 "%s: attribute %s is not a valid attribute in schema",
3345 __FUNCTION__
, el
->name
);
3346 return LDB_ERR_OBJECT_CLASS_VIOLATION
;
3348 if (schema_attr
->linkID
== 0) {
3351 if ((schema_attr
->linkID
& 1) == 1) {
3352 struct ldb_control
*ctrl
;
3354 ctrl
= ldb_request_get_control(parent
,
3355 DSDB_CONTROL_REPLMD_VANISH_LINKS
);
3357 ctrl
->critical
= false;
3360 ctrl
= ldb_request_get_control(parent
,
3361 DSDB_CONTROL_DBCHECK
);
3366 /* Odd is for the target. Illegal to modify */
3367 ldb_asprintf_errstring(ldb
,
3368 "attribute %s must not be modified directly, it is a linked attribute", el
->name
);
3369 return LDB_ERR_UNWILLING_TO_PERFORM
;
3371 old_el
= ldb_msg_find_element(old_msg
, el
->name
);
3373 case LDB_FLAG_MOD_REPLACE
:
3374 ret
= replmd_modify_la_replace(module
, replmd_private
,
3375 ac
, msg
, el
, old_el
,
3380 case LDB_FLAG_MOD_DELETE
:
3381 ret
= replmd_modify_la_delete(module
, replmd_private
,
3382 ac
, msg
, el
, old_el
,
3387 case LDB_FLAG_MOD_ADD
:
3388 ret
= replmd_modify_la_add(module
, replmd_private
,
3389 ac
, msg
, el
, old_el
,
3395 ldb_asprintf_errstring(ldb
,
3396 "invalid flags 0x%x for %s linked attribute",
3397 el
->flags
, el
->name
);
3398 return LDB_ERR_UNWILLING_TO_PERFORM
;
3400 if (ret
!= LDB_SUCCESS
) {
3403 ret
= dsdb_check_single_valued_link(schema_attr
, el
);
3404 if (ret
!= LDB_SUCCESS
) {
3405 ldb_asprintf_errstring(ldb
,
3406 "Attribute %s is single valued but more than one value has been supplied",
3408 /* Return codes as found on Windows 2012r2 */
3409 if (mod_type
== LDB_FLAG_MOD_REPLACE
) {
3410 return LDB_ERR_CONSTRAINT_VIOLATION
;
3412 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS
;
3415 el
->flags
|= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK
;
3419 ldb_msg_remove_attr(old_msg
, el
->name
);
3421 ldb_msg_add_empty(old_msg
, el
->name
, 0, &new_el
);
3422 new_el
->num_values
= el
->num_values
;
3423 new_el
->values
= talloc_steal(msg
->elements
, el
->values
);
3425 /* TODO: this relises a bit too heavily on the exact
3426 behaviour of ldb_msg_find_element and
3427 ldb_msg_remove_element */
3428 old_el
= ldb_msg_find_element(msg
, el
->name
);
3430 ldb_msg_remove_element(msg
, old_el
);
3440 static int send_rodc_referral(struct ldb_request
*req
,
3441 struct ldb_context
*ldb
,
3444 char *referral
= NULL
;
3445 struct loadparm_context
*lp_ctx
= NULL
;
3446 struct ldb_dn
*fsmo_role_dn
= NULL
;
3447 struct ldb_dn
*role_owner_dn
= NULL
;
3448 const char *domain
= NULL
;
3451 lp_ctx
= talloc_get_type(ldb_get_opaque(ldb
, "loadparm"),
3452 struct loadparm_context
);
3454 werr
= dsdb_get_fsmo_role_info(req
, ldb
, DREPL_PDC_MASTER
,
3455 &fsmo_role_dn
, &role_owner_dn
);
3457 if (W_ERROR_IS_OK(werr
)) {
3458 struct ldb_dn
*server_dn
= ldb_dn_copy(req
, role_owner_dn
);
3459 if (server_dn
!= NULL
) {
3460 ldb_dn_remove_child_components(server_dn
, 1);
3461 domain
= samdb_dn_to_dnshostname(ldb
, req
,
3466 if (domain
== NULL
) {
3467 domain
= lpcfg_dnsdomain(lp_ctx
);
3470 referral
= talloc_asprintf(req
, "ldap://%s/%s",
3472 ldb_dn_get_linearized(dn
));
3473 if (referral
== NULL
) {
3475 return LDB_ERR_OPERATIONS_ERROR
;
3478 return ldb_module_send_referral(req
, referral
);
3482 static int replmd_modify(struct ldb_module
*module
, struct ldb_request
*req
)
3484 struct ldb_context
*ldb
;
3485 struct replmd_replicated_request
*ac
;
3486 struct ldb_request
*down_req
;
3487 struct ldb_message
*msg
;
3488 time_t t
= time(NULL
);
3490 bool is_urgent
= false, rodc
= false;
3491 bool is_schema_nc
= false;
3492 unsigned int functional_level
;
3493 const struct ldb_message_element
*guid_el
= NULL
;
3494 struct ldb_control
*sd_propagation_control
;
3495 struct ldb_control
*fix_links_control
= NULL
;
3496 struct ldb_control
*fix_dn_name_control
= NULL
;
3497 struct ldb_control
*fix_dn_sid_control
= NULL
;
3498 struct replmd_private
*replmd_private
=
3499 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
3501 /* do not manipulate our control entries */
3502 if (ldb_dn_is_special(req
->op
.mod
.message
->dn
)) {
3503 return ldb_next_request(module
, req
);
3506 sd_propagation_control
= ldb_request_get_control(req
,
3507 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID
);
3508 if (sd_propagation_control
!= NULL
) {
3509 if (req
->op
.mod
.message
->num_elements
!= 1) {
3510 return ldb_module_operr(module
);
3512 ret
= strcmp(req
->op
.mod
.message
->elements
[0].name
,
3513 "nTSecurityDescriptor");
3515 return ldb_module_operr(module
);
3518 return ldb_next_request(module
, req
);
3521 ldb
= ldb_module_get_ctx(module
);
3523 fix_links_control
= ldb_request_get_control(req
,
3524 DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS
);
3525 if (fix_links_control
!= NULL
) {
3526 struct dsdb_schema
*schema
= NULL
;
3527 const struct dsdb_attribute
*sa
= NULL
;
3529 if (req
->op
.mod
.message
->num_elements
!= 1) {
3530 return ldb_module_operr(module
);
3533 if (LDB_FLAG_MOD_TYPE(req
->op
.mod
.message
->elements
[0].flags
) != LDB_FLAG_MOD_REPLACE
) {
3534 return ldb_module_operr(module
);
3537 schema
= dsdb_get_schema(ldb
, req
);
3538 if (schema
== NULL
) {
3539 return ldb_module_operr(module
);
3542 sa
= dsdb_attribute_by_lDAPDisplayName(schema
,
3543 req
->op
.mod
.message
->elements
[0].name
);
3545 return ldb_module_operr(module
);
3548 if (sa
->linkID
== 0) {
3549 return ldb_module_operr(module
);
3552 fix_links_control
->critical
= false;
3553 return ldb_next_request(module
, req
);
3556 fix_dn_name_control
= ldb_request_get_control(req
,
3557 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME
);
3558 if (fix_dn_name_control
!= NULL
) {
3559 struct dsdb_schema
*schema
= NULL
;
3560 const struct dsdb_attribute
*sa
= NULL
;
3562 if (req
->op
.mod
.message
->num_elements
!= 2) {
3563 return ldb_module_operr(module
);
3566 if (LDB_FLAG_MOD_TYPE(req
->op
.mod
.message
->elements
[0].flags
) != LDB_FLAG_MOD_DELETE
) {
3567 return ldb_module_operr(module
);
3570 if (LDB_FLAG_MOD_TYPE(req
->op
.mod
.message
->elements
[1].flags
) != LDB_FLAG_MOD_ADD
) {
3571 return ldb_module_operr(module
);
3574 if (req
->op
.mod
.message
->elements
[0].num_values
!= 1) {
3575 return ldb_module_operr(module
);
3578 if (req
->op
.mod
.message
->elements
[1].num_values
!= 1) {
3579 return ldb_module_operr(module
);
3582 schema
= dsdb_get_schema(ldb
, req
);
3583 if (schema
== NULL
) {
3584 return ldb_module_operr(module
);
3587 if (ldb_attr_cmp(req
->op
.mod
.message
->elements
[0].name
,
3588 req
->op
.mod
.message
->elements
[1].name
) != 0) {
3589 return ldb_module_operr(module
);
3592 sa
= dsdb_attribute_by_lDAPDisplayName(schema
,
3593 req
->op
.mod
.message
->elements
[0].name
);
3595 return ldb_module_operr(module
);
3598 if (sa
->dn_format
== DSDB_INVALID_DN
) {
3599 return ldb_module_operr(module
);
3602 if (sa
->linkID
!= 0) {
3603 return ldb_module_operr(module
);
3607 * If we are run from dbcheck and we are not updating
3608 * a link (as these would need to be sorted and so
3609 * can't go via such a simple update, then do not
3610 * trigger replicated updates and a new USN from this
3611 * change, it wasn't a real change, just a new
3612 * (correct) string DN
3615 fix_dn_name_control
->critical
= false;
3616 return ldb_next_request(module
, req
);
3619 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_modify\n");
3621 guid_el
= ldb_msg_find_element(req
->op
.mod
.message
, "objectGUID");
3622 if (guid_el
!= NULL
) {
3623 ldb_set_errstring(ldb
,
3624 "replmd_modify: it's not allowed to change the objectGUID!");
3625 return LDB_ERR_CONSTRAINT_VIOLATION
;
3628 ac
= replmd_ctx_init(module
, req
);
3630 return ldb_module_oom(module
);
3633 functional_level
= dsdb_functional_level(ldb
);
3635 /* we have to copy the message as the caller might have it as a const */
3636 msg
= ldb_msg_copy_shallow(ac
, req
->op
.mod
.message
);
3640 return LDB_ERR_OPERATIONS_ERROR
;
3643 fix_dn_sid_control
= ldb_request_get_control(req
,
3644 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID
);
3645 if (fix_dn_sid_control
!= NULL
) {
3646 const struct dsdb_attribute
*sa
= NULL
;
3648 if (msg
->num_elements
!= 1) {
3650 return ldb_module_operr(module
);
3653 if (LDB_FLAG_MOD_TYPE(msg
->elements
[0].flags
) != LDB_FLAG_MOD_ADD
) {
3655 return ldb_module_operr(module
);
3658 if (msg
->elements
[0].num_values
!= 1) {
3660 return ldb_module_operr(module
);
3663 sa
= dsdb_attribute_by_lDAPDisplayName(ac
->schema
,
3664 msg
->elements
[0].name
);
3667 return ldb_module_operr(module
);
3670 if (sa
->dn_format
!= DSDB_NORMAL_DN
) {
3672 return ldb_module_operr(module
);
3675 fix_dn_sid_control
->critical
= false;
3676 ac
->fix_link_sid
= true;
3678 goto handle_linked_attribs
;
3681 ldb_msg_remove_attr(msg
, "whenChanged");
3682 ldb_msg_remove_attr(msg
, "uSNChanged");
3684 is_schema_nc
= ldb_dn_compare_base(replmd_private
->schema_dn
, msg
->dn
) == 0;
3686 ret
= replmd_update_rpmd(module
, ac
->schema
, req
, NULL
,
3687 msg
, &ac
->seq_num
, t
, is_schema_nc
,
3689 if (rodc
&& (ret
== LDB_ERR_REFERRAL
)) {
3690 ret
= send_rodc_referral(req
, ldb
, msg
->dn
);
3696 if (ret
!= LDB_SUCCESS
) {
3701 handle_linked_attribs
:
3702 ret
= replmd_modify_handle_linked_attribs(module
, replmd_private
,
3704 if (ret
!= LDB_SUCCESS
) {
3710 * - replace the old object with the newly constructed one
3713 ac
->is_urgent
= is_urgent
;
3715 ret
= ldb_build_mod_req(&down_req
, ldb
, ac
,
3718 ac
, replmd_op_callback
,
3720 LDB_REQ_SET_LOCATION(down_req
);
3721 if (ret
!= LDB_SUCCESS
) {
3726 /* current partition control is needed by "replmd_op_callback" */
3727 if (ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
) == NULL
) {
3728 ret
= ldb_request_add_control(down_req
,
3729 DSDB_CONTROL_CURRENT_PARTITION_OID
,
3731 if (ret
!= LDB_SUCCESS
) {
3737 /* If we are in functional level 2000, then
3738 * replmd_modify_handle_linked_attribs will have done
3740 if (functional_level
== DS_DOMAIN_FUNCTION_2000
) {
3741 ret
= ldb_request_add_control(down_req
, DSDB_CONTROL_APPLY_LINKS
, false, NULL
);
3742 if (ret
!= LDB_SUCCESS
) {
3748 talloc_steal(down_req
, msg
);
3750 /* we only change whenChanged and uSNChanged if the seq_num
3752 if (ac
->seq_num
!= 0) {
3753 ret
= add_time_element(msg
, "whenChanged", t
);
3754 if (ret
!= LDB_SUCCESS
) {
3760 ret
= add_uint64_element(ldb
, msg
, "uSNChanged", ac
->seq_num
);
3761 if (ret
!= LDB_SUCCESS
) {
3768 /* go on with the call chain */
3769 return ldb_next_request(module
, down_req
);
3772 static int replmd_rename_callback(struct ldb_request
*req
, struct ldb_reply
*ares
);
3775 handle a rename request
3777 On a rename we need to do an extra ldb_modify which sets the
3778 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3780 static int replmd_rename(struct ldb_module
*module
, struct ldb_request
*req
)
3782 struct ldb_context
*ldb
;
3783 struct ldb_control
*fix_dn_name_control
= NULL
;
3784 struct replmd_replicated_request
*ac
;
3786 struct ldb_request
*down_req
;
3788 /* do not manipulate our control entries */
3789 if (ldb_dn_is_special(req
->op
.rename
.olddn
)) {
3790 return ldb_next_request(module
, req
);
3793 fix_dn_name_control
= ldb_request_get_control(req
,
3794 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME
);
3795 if (fix_dn_name_control
!= NULL
) {
3796 return ldb_next_request(module
, req
);
3799 ldb
= ldb_module_get_ctx(module
);
3801 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_rename\n");
3803 ac
= replmd_ctx_init(module
, req
);
3805 return ldb_module_oom(module
);
3808 ret
= ldb_build_rename_req(&down_req
, ldb
, ac
,
3809 ac
->req
->op
.rename
.olddn
,
3810 ac
->req
->op
.rename
.newdn
,
3812 ac
, replmd_rename_callback
,
3814 LDB_REQ_SET_LOCATION(down_req
);
3815 if (ret
!= LDB_SUCCESS
) {
3820 /* go on with the call chain */
3821 return ldb_next_request(module
, down_req
);
3824 /* After the rename is compleated, update the whenchanged etc */
3825 static int replmd_rename_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
3827 struct ldb_context
*ldb
;
3828 struct ldb_request
*down_req
;
3829 struct ldb_message
*msg
;
3830 const struct dsdb_attribute
*rdn_attr
;
3831 const char *rdn_name
;
3832 const struct ldb_val
*rdn_val
;
3833 const char *attrs
[5] = { NULL
, };
3834 time_t t
= time(NULL
);
3836 bool is_urgent
= false, rodc
= false;
3838 struct replmd_replicated_request
*ac
=
3839 talloc_get_type(req
->context
, struct replmd_replicated_request
);
3840 struct replmd_private
*replmd_private
=
3841 talloc_get_type(ldb_module_get_private(ac
->module
),
3842 struct replmd_private
);
3844 ldb
= ldb_module_get_ctx(ac
->module
);
3846 if (ares
->error
!= LDB_SUCCESS
) {
3847 return ldb_module_done(ac
->req
, ares
->controls
,
3848 ares
->response
, ares
->error
);
3851 if (ares
->type
!= LDB_REPLY_DONE
) {
3852 ldb_set_errstring(ldb
,
3853 "invalid reply type in repl_meta_data rename callback");
3855 return ldb_module_done(ac
->req
, NULL
, NULL
,
3856 LDB_ERR_OPERATIONS_ERROR
);
3860 * - replace the old object with the newly constructed one
3863 msg
= ldb_msg_new(ac
);
3866 return LDB_ERR_OPERATIONS_ERROR
;
3869 msg
->dn
= ac
->req
->op
.rename
.newdn
;
3871 is_schema_nc
= ldb_dn_compare_base(replmd_private
->schema_dn
, msg
->dn
) == 0;
3873 rdn_name
= ldb_dn_get_rdn_name(msg
->dn
);
3874 if (rdn_name
== NULL
) {
3876 return ldb_module_done(ac
->req
, NULL
, NULL
,
3880 /* normalize the rdn attribute name */
3881 rdn_attr
= dsdb_attribute_by_lDAPDisplayName(ac
->schema
, rdn_name
);
3882 if (rdn_attr
== NULL
) {
3884 return ldb_module_done(ac
->req
, NULL
, NULL
,
3887 rdn_name
= rdn_attr
->lDAPDisplayName
;
3889 rdn_val
= ldb_dn_get_rdn_val(msg
->dn
);
3890 if (rdn_val
== NULL
) {
3892 return ldb_module_done(ac
->req
, NULL
, NULL
,
3896 if (ldb_msg_append_value(msg
, rdn_name
, rdn_val
, LDB_FLAG_MOD_REPLACE
) != 0) {
3898 return ldb_module_done(ac
->req
, NULL
, NULL
,
3901 if (ldb_msg_append_value(msg
, "name", rdn_val
, LDB_FLAG_MOD_REPLACE
) != 0) {
3903 return ldb_module_done(ac
->req
, NULL
, NULL
,
3908 * here we let replmd_update_rpmd() only search for
3909 * the existing "replPropertyMetaData" and rdn_name attributes.
3911 * We do not want the existing "name" attribute as
3912 * the "name" attribute needs to get the version
3913 * updated on rename even if the rdn value hasn't changed.
3915 * This is the diff of the meta data, for a moved user
3916 * on a w2k8r2 server:
3919 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3920 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3921 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3922 * version : 0x00000001 (1)
3923 * reserved : 0x00000000 (0)
3924 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3925 * local_usn : 0x00000000000037a5 (14245)
3926 * array: struct replPropertyMetaData1
3927 * attid : DRSUAPI_ATTID_name (0x90001)
3928 * - version : 0x00000001 (1)
3929 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3930 * + version : 0x00000002 (2)
3931 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3932 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3933 * - originating_usn : 0x00000000000037a5 (14245)
3934 * - local_usn : 0x00000000000037a5 (14245)
3935 * + originating_usn : 0x0000000000003834 (14388)
3936 * + local_usn : 0x0000000000003834 (14388)
3937 * array: struct replPropertyMetaData1
3938 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3939 * version : 0x00000004 (4)
3941 attrs
[0] = "replPropertyMetaData";
3942 attrs
[1] = "objectClass";
3943 attrs
[2] = "instanceType";
3944 attrs
[3] = rdn_name
;
3947 ret
= replmd_update_rpmd(ac
->module
, ac
->schema
, req
, attrs
,
3948 msg
, &ac
->seq_num
, t
,
3949 is_schema_nc
, &is_urgent
, &rodc
);
3950 if (rodc
&& (ret
== LDB_ERR_REFERRAL
)) {
3951 ret
= send_rodc_referral(req
, ldb
, ac
->req
->op
.rename
.olddn
);
3953 return ldb_module_done(req
, NULL
, NULL
, ret
);
3956 if (ret
!= LDB_SUCCESS
) {
3958 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
3961 if (ac
->seq_num
== 0) {
3963 return ldb_module_done(ac
->req
, NULL
, NULL
,
3965 "internal error seq_num == 0"));
3967 ac
->is_urgent
= is_urgent
;
3969 ret
= ldb_build_mod_req(&down_req
, ldb
, ac
,
3972 ac
, replmd_op_callback
,
3974 LDB_REQ_SET_LOCATION(down_req
);
3975 if (ret
!= LDB_SUCCESS
) {
3980 /* current partition control is needed by "replmd_op_callback" */
3981 if (ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
) == NULL
) {
3982 ret
= ldb_request_add_control(down_req
,
3983 DSDB_CONTROL_CURRENT_PARTITION_OID
,
3985 if (ret
!= LDB_SUCCESS
) {
3991 talloc_steal(down_req
, msg
);
3993 ret
= add_time_element(msg
, "whenChanged", t
);
3994 if (ret
!= LDB_SUCCESS
) {
4000 ret
= add_uint64_element(ldb
, msg
, "uSNChanged", ac
->seq_num
);
4001 if (ret
!= LDB_SUCCESS
) {
4007 /* go on with the call chain - do the modify after the rename */
4008 return ldb_next_request(ac
->module
, down_req
);
4012 * remove links from objects that point at this object when an object
4013 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
4014 * RemoveObj which states that link removal due to the object being
4015 * deleted is NOT an originating update - they just go away!
4018 static int replmd_delete_remove_link(struct ldb_module
*module
,
4019 const struct dsdb_schema
*schema
,
4020 struct replmd_private
*replmd_private
,
4023 struct ldb_message_element
*el
,
4024 const struct dsdb_attribute
*sa
,
4025 struct ldb_request
*parent
,
4026 bool *caller_should_vanish
)
4029 TALLOC_CTX
*tmp_ctx
= talloc_new(module
);
4030 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
4032 for (i
=0; i
<el
->num_values
; i
++) {
4033 struct dsdb_dn
*dsdb_dn
;
4035 struct ldb_message
*msg
;
4036 const struct dsdb_attribute
*target_attr
;
4037 struct ldb_message_element
*el2
;
4039 struct ldb_val dn_val
;
4040 uint32_t dsdb_flags
= 0;
4041 const char *attrs
[] = { NULL
, NULL
};
4042 struct ldb_result
*link_res
;
4043 struct ldb_message
*link_msg
;
4044 struct ldb_message_element
*link_el
;
4045 struct parsed_dn
*link_dns
;
4046 struct parsed_dn
*p
= NULL
, *unused
= NULL
;
4048 if (dsdb_dn_is_deleted_val(&el
->values
[i
])) {
4052 dsdb_dn
= dsdb_dn_parse(tmp_ctx
, ldb
, &el
->values
[i
], sa
->syntax
->ldap_oid
);
4054 talloc_free(tmp_ctx
);
4055 return LDB_ERR_OPERATIONS_ERROR
;
4058 /* remove the link */
4059 msg
= ldb_msg_new(tmp_ctx
);
4061 ldb_module_oom(module
);
4062 talloc_free(tmp_ctx
);
4063 return LDB_ERR_OPERATIONS_ERROR
;
4066 msg
->dn
= dsdb_dn
->dn
;
4068 target_attr
= dsdb_attribute_by_linkID(schema
, sa
->linkID
^ 1);
4069 if (target_attr
== NULL
) {
4072 attrs
[0] = target_attr
->lDAPDisplayName
;
4074 ret
= ldb_msg_add_empty(msg
, target_attr
->lDAPDisplayName
,
4075 LDB_FLAG_MOD_DELETE
, &el2
);
4076 if (ret
!= LDB_SUCCESS
) {
4077 ldb_module_oom(module
);
4078 talloc_free(tmp_ctx
);
4079 return LDB_ERR_OPERATIONS_ERROR
;
4082 ret
= dsdb_module_search_dn(module
, tmp_ctx
, &link_res
,
4084 DSDB_FLAG_NEXT_MODULE
|
4085 DSDB_SEARCH_SHOW_EXTENDED_DN
|
4086 DSDB_SEARCH_SHOW_RECYCLED
,
4089 if (ret
== LDB_ERR_NO_SUCH_OBJECT
) {
4090 DBG_WARNING("Failed to find forward link object %s "
4091 "to remove backlink %s on %s",
4092 ldb_dn_get_linearized(msg
->dn
),
4093 sa
->lDAPDisplayName
,
4094 ldb_dn_get_linearized(dn
));
4095 *caller_should_vanish
= true;
4099 if (ret
!= LDB_SUCCESS
) {
4100 talloc_free(tmp_ctx
);
4104 link_msg
= link_res
->msgs
[0];
4105 link_el
= ldb_msg_find_element(link_msg
,
4106 target_attr
->lDAPDisplayName
);
4107 if (link_el
== NULL
) {
4108 DBG_WARNING("Failed to find forward link on %s "
4109 "as %s to remove backlink %s on %s",
4110 ldb_dn_get_linearized(msg
->dn
),
4111 target_attr
->lDAPDisplayName
,
4112 sa
->lDAPDisplayName
,
4113 ldb_dn_get_linearized(dn
));
4114 *caller_should_vanish
= true;
4119 * This call 'upgrades' the links in link_dns, but we
4120 * do not commit the result back into the database, so
4121 * this is safe to call in FL2000 or on databases that
4122 * have been run at that level in the past.
4124 ret
= get_parsed_dns_trusted_fallback(module
, replmd_private
,
4127 target_attr
->syntax
->ldap_oid
,
4129 if (ret
!= LDB_SUCCESS
) {
4130 talloc_free(tmp_ctx
);
4134 ret
= parsed_dn_find(ldb
, link_dns
, link_el
->num_values
,
4138 target_attr
->syntax
->ldap_oid
, false);
4139 if (ret
!= LDB_SUCCESS
) {
4140 talloc_free(tmp_ctx
);
4145 DBG_WARNING("Failed to find forward link on %s "
4146 "as %s to remove backlink %s on %s",
4147 ldb_dn_get_linearized(msg
->dn
),
4148 target_attr
->lDAPDisplayName
,
4149 sa
->lDAPDisplayName
,
4150 ldb_dn_get_linearized(dn
));
4151 *caller_should_vanish
= true;
4156 * If we find a backlink to ourself, we will delete
4157 * the forward link before we get to process that
4158 * properly, so just let the caller process this via
4161 * We do this once we are sure we have the forward
4162 * link (to ourself) in case something is very wrong
4163 * and they are out of sync.
4165 if (ldb_dn_compare(dsdb_dn
->dn
, dn
) == 0) {
4169 /* This needs to get the Binary DN, by first searching */
4170 dn_str
= dsdb_dn_get_linearized(tmp_ctx
,
4173 dn_val
= data_blob_string_const(dn_str
);
4174 el2
->values
= &dn_val
;
4175 el2
->num_values
= 1;
4178 * Ensure that we tell the modification to vanish any linked
4179 * attributes (not simply mark them as isDeleted = TRUE)
4181 dsdb_flags
|= DSDB_REPLMD_VANISH_LINKS
;
4183 ret
= dsdb_module_modify(module
, msg
, dsdb_flags
|DSDB_FLAG_OWN_MODULE
, parent
);
4184 if (ret
!= LDB_SUCCESS
) {
4185 talloc_free(tmp_ctx
);
4189 talloc_free(tmp_ctx
);
4195 handle update of replication meta data for deletion of objects
4197 This also handles the mapping of delete to a rename operation
4198 to allow deletes to be replicated.
4200 It also handles the incoming deleted objects, to ensure they are
4201 fully deleted here. In that case re_delete is true, and we do not
4202 use this as a signal to change the deleted state, just reinforce it.
4205 static int replmd_delete_internals(struct ldb_module
*module
, struct ldb_request
*req
, bool re_delete
)
4207 int ret
= LDB_ERR_OTHER
;
4208 bool retb
, disallow_move_on_delete
;
4209 struct ldb_dn
*old_dn
= NULL
, *new_dn
= NULL
;
4210 const char *rdn_name
;
4211 const struct ldb_val
*rdn_value
, *new_rdn_value
;
4213 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
4214 const struct dsdb_schema
*schema
;
4215 struct ldb_message
*msg
, *old_msg
;
4216 struct ldb_message_element
*el
;
4217 TALLOC_CTX
*tmp_ctx
;
4218 struct ldb_result
*res
, *parent_res
;
4219 static const char * const preserved_attrs
[] = {
4221 * This list MUST be kept in case-insensitive sorted order,
4222 * as we use it in a binary search with ldb_attr_cmp().
4224 * We get this hard-coded list from
4225 * MS-ADTS section 3.1.1.5.5.1.1 "Tombstone Requirements".
4229 "distinguishedName",
4230 "dNReferenceUpdate",
4242 "msDS-LastKnownRDN",
4247 "nTSecurityDescriptor",
4252 "proxiedObjectName",
4253 "replPropertyMetaData",
4255 "securityIdentifier",
4263 "userAccountControl",
4269 * DO NOT JUST APPEND TO THIS LIST.
4271 * In case you missed the note at the top, this list is kept
4272 * in case-insensitive sorted order. In the unlikely event you
4273 * need to add an attrbute, please add it in the RIGHT PLACE.
4276 static const char * const all_attrs
[] = {
4277 DSDB_SECRET_ATTRIBUTES
,
4281 static const struct ldb_val true_val
= {
4282 .data
= discard_const_p(uint8_t, "TRUE"),
4287 uint32_t dsdb_flags
= 0;
4288 struct replmd_private
*replmd_private
;
4289 enum deletion_state deletion_state
, next_deletion_state
;
4291 if (ldb_dn_is_special(req
->op
.del
.dn
)) {
4292 return ldb_next_request(module
, req
);
4296 * We have to allow dbcheck to remove an object that
4297 * is beyond repair, and to do so totally. This could
4298 * mean we we can get a partial object from the other
4299 * DC, causing havoc, so dbcheck suggests
4300 * re-replication first. dbcheck sets both DBCHECK
4301 * and RELAX in this situation.
4303 if (ldb_request_get_control(req
, LDB_CONTROL_RELAX_OID
)
4304 && ldb_request_get_control(req
, DSDB_CONTROL_DBCHECK
)) {
4305 /* really, really remove it */
4306 return ldb_next_request(module
, req
);
4309 tmp_ctx
= talloc_new(ldb
);
4312 return LDB_ERR_OPERATIONS_ERROR
;
4315 schema
= dsdb_get_schema(ldb
, tmp_ctx
);
4317 talloc_free(tmp_ctx
);
4318 return LDB_ERR_OPERATIONS_ERROR
;
4321 old_dn
= ldb_dn_copy(tmp_ctx
, req
->op
.del
.dn
);
4323 /* we need the complete msg off disk, so we can work out which
4324 attributes need to be removed */
4325 ret
= dsdb_module_search_dn(module
, tmp_ctx
, &res
, old_dn
, all_attrs
,
4326 DSDB_FLAG_NEXT_MODULE
|
4327 DSDB_SEARCH_SHOW_RECYCLED
|
4328 DSDB_SEARCH_REVEAL_INTERNALS
|
4329 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
, req
);
4330 if (ret
!= LDB_SUCCESS
) {
4331 ldb_asprintf_errstring(ldb_module_get_ctx(module
),
4332 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
4333 re_delete
? "re-delete" : "delete",
4334 ldb_dn_get_linearized(old_dn
),
4335 ldb_errstring(ldb_module_get_ctx(module
)));
4336 talloc_free(tmp_ctx
);
4339 old_msg
= res
->msgs
[0];
4341 replmd_deletion_state(module
, old_msg
,
4343 &next_deletion_state
);
4345 /* This supports us noticing an incoming isDeleted and acting on it */
4347 SMB_ASSERT(deletion_state
> OBJECT_NOT_DELETED
);
4348 next_deletion_state
= deletion_state
;
4351 if (next_deletion_state
== OBJECT_REMOVED
) {
4353 * We have to prevent objects being deleted, even if
4354 * the administrator really wants them gone, as
4355 * without the tombstone, we can get a partial object
4356 * from the other DC, causing havoc.
4358 * The only other valid case is when the 180 day
4359 * timeout has expired, when relax is specified.
4361 if (ldb_request_get_control(req
, LDB_CONTROL_RELAX_OID
)) {
4362 /* it is already deleted - really remove it this time */
4363 talloc_free(tmp_ctx
);
4364 return ldb_next_request(module
, req
);
4367 ldb_asprintf_errstring(ldb
, "Refusing to delete tombstone object %s. "
4368 "This check is to prevent corruption of the replicated state.",
4369 ldb_dn_get_linearized(old_msg
->dn
));
4370 return LDB_ERR_UNWILLING_TO_PERFORM
;
4373 rdn_name
= ldb_dn_get_rdn_name(old_dn
);
4374 rdn_value
= ldb_dn_get_rdn_val(old_dn
);
4375 if ((rdn_name
== NULL
) || (rdn_value
== NULL
)) {
4376 talloc_free(tmp_ctx
);
4377 return ldb_operr(ldb
);
4380 msg
= ldb_msg_new(tmp_ctx
);
4382 ldb_module_oom(module
);
4383 talloc_free(tmp_ctx
);
4384 return LDB_ERR_OPERATIONS_ERROR
;
4389 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4390 disallow_move_on_delete
=
4391 (ldb_msg_find_attr_as_int(old_msg
, "systemFlags", 0)
4392 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE
);
4394 /* work out where we will be renaming this object to */
4395 if (!disallow_move_on_delete
) {
4396 struct ldb_dn
*deleted_objects_dn
;
4397 ret
= dsdb_get_deleted_objects_dn(ldb
, tmp_ctx
, old_dn
,
4398 &deleted_objects_dn
);
4401 * We should not move objects if we can't find the
4402 * deleted objects DN. Not moving (or otherwise
4403 * harming) the Deleted Objects DN itself is handled
4406 if (re_delete
&& (ret
!= LDB_SUCCESS
)) {
4407 new_dn
= ldb_dn_get_parent(tmp_ctx
, old_dn
);
4408 if (new_dn
== NULL
) {
4409 ldb_module_oom(module
);
4410 talloc_free(tmp_ctx
);
4411 return LDB_ERR_OPERATIONS_ERROR
;
4413 } else if (ret
!= LDB_SUCCESS
) {
4414 /* this is probably an attempted delete on a partition
4415 * that doesn't allow delete operations, such as the
4416 * schema partition */
4417 ldb_asprintf_errstring(ldb
, "No Deleted Objects container for DN %s",
4418 ldb_dn_get_linearized(old_dn
));
4419 talloc_free(tmp_ctx
);
4420 return LDB_ERR_UNWILLING_TO_PERFORM
;
4422 new_dn
= deleted_objects_dn
;
4425 new_dn
= ldb_dn_get_parent(tmp_ctx
, old_dn
);
4426 if (new_dn
== NULL
) {
4427 ldb_module_oom(module
);
4428 talloc_free(tmp_ctx
);
4429 return LDB_ERR_OPERATIONS_ERROR
;
4433 /* get the objects GUID from the search we just did */
4434 guid
= samdb_result_guid(old_msg
, "objectGUID");
4436 if (deletion_state
== OBJECT_NOT_DELETED
) {
4437 struct ldb_message_element
*is_deleted_el
;
4439 ret
= replmd_make_deleted_child_dn(tmp_ctx
,
4442 rdn_name
, rdn_value
,
4445 if (ret
!= LDB_SUCCESS
) {
4446 talloc_free(tmp_ctx
);
4450 ret
= ldb_msg_add_value(msg
, "isDeleted", &true_val
,
4452 if (ret
!= LDB_SUCCESS
) {
4453 ldb_asprintf_errstring(ldb
, __location__
4454 ": Failed to add isDeleted string to the msg");
4455 talloc_free(tmp_ctx
);
4458 is_deleted_el
->flags
= LDB_FLAG_MOD_REPLACE
;
4461 * No matter what has happened with other renames etc, try again to
4462 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4465 struct ldb_dn
*rdn
= ldb_dn_copy(tmp_ctx
, old_dn
);
4466 retb
= ldb_dn_remove_base_components(rdn
, ldb_dn_get_comp_num(rdn
) - 1);
4468 ldb_asprintf_errstring(ldb
, __location__
4469 ": Unable to add a prepare rdn of %s",
4470 ldb_dn_get_linearized(rdn
));
4471 talloc_free(tmp_ctx
);
4472 return LDB_ERR_OPERATIONS_ERROR
;
4474 SMB_ASSERT(ldb_dn_get_comp_num(rdn
) == 1);
4476 retb
= ldb_dn_add_child(new_dn
, rdn
);
4478 ldb_asprintf_errstring(ldb
, __location__
4479 ": Unable to add rdn %s to base dn: %s",
4480 ldb_dn_get_linearized(rdn
),
4481 ldb_dn_get_linearized(new_dn
));
4482 talloc_free(tmp_ctx
);
4483 return LDB_ERR_OPERATIONS_ERROR
;
4488 now we need to modify the object in the following ways:
4490 - add isDeleted=TRUE
4491 - update rDN and name, with new rDN
4492 - remove linked attributes
4493 - remove objectCategory and sAMAccountType
4494 - remove attribs not on the preserved list
4495 - preserved if in above list, or is rDN
4496 - remove all linked attribs from this object
4497 - remove all links from other objects to this object
4498 (note we use the backlinks to do this, so we won't find one-way
4499 links that still point to this object, or deactivated two-way
4500 links, i.e. 'member' after the user has been removed from the
4502 - add lastKnownParent
4503 - update replPropertyMetaData?
4505 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4508 if (deletion_state
== OBJECT_NOT_DELETED
) {
4509 struct ldb_dn
*parent_dn
= ldb_dn_get_parent(tmp_ctx
, old_dn
);
4510 char *parent_dn_str
= NULL
;
4511 struct ldb_message_element
*p_el
;
4513 /* we need the storage form of the parent GUID */
4514 ret
= dsdb_module_search_dn(module
, tmp_ctx
, &parent_res
,
4516 DSDB_FLAG_NEXT_MODULE
|
4517 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
|
4518 DSDB_SEARCH_REVEAL_INTERNALS
|
4519 DSDB_SEARCH_SHOW_RECYCLED
, req
);
4520 if (ret
!= LDB_SUCCESS
) {
4521 ldb_asprintf_errstring(ldb_module_get_ctx(module
),
4522 "repmd_delete: Failed to %s %s, "
4523 "because we failed to find it's parent (%s): %s",
4524 re_delete
? "re-delete" : "delete",
4525 ldb_dn_get_linearized(old_dn
),
4526 ldb_dn_get_linearized(parent_dn
),
4527 ldb_errstring(ldb_module_get_ctx(module
)));
4528 talloc_free(tmp_ctx
);
4533 * Now we can use the DB version,
4534 * it will have the extended DN info in it
4536 parent_dn
= parent_res
->msgs
[0]->dn
;
4537 parent_dn_str
= ldb_dn_get_extended_linearized(tmp_ctx
,
4540 if (parent_dn_str
== NULL
) {
4541 talloc_free(tmp_ctx
);
4542 return ldb_module_oom(module
);
4545 ret
= ldb_msg_add_steal_string(msg
, "lastKnownParent",
4547 if (ret
!= LDB_SUCCESS
) {
4548 ldb_asprintf_errstring(ldb
, __location__
4549 ": Failed to add lastKnownParent "
4550 "string when deleting %s",
4551 ldb_dn_get_linearized(old_dn
));
4552 talloc_free(tmp_ctx
);
4555 p_el
= ldb_msg_find_element(msg
,
4558 talloc_free(tmp_ctx
);
4559 return ldb_module_operr(module
);
4561 p_el
->flags
= LDB_FLAG_MOD_REPLACE
;
4563 if (next_deletion_state
== OBJECT_DELETED
) {
4564 ret
= ldb_msg_add_value(msg
, "msDS-LastKnownRDN", rdn_value
, NULL
);
4565 if (ret
!= LDB_SUCCESS
) {
4566 ldb_asprintf_errstring(ldb
, __location__
4567 ": Failed to add msDS-LastKnownRDN "
4568 "string when deleting %s",
4569 ldb_dn_get_linearized(old_dn
));
4570 talloc_free(tmp_ctx
);
4573 p_el
= ldb_msg_find_element(msg
,
4574 "msDS-LastKnownRDN");
4576 talloc_free(tmp_ctx
);
4577 return ldb_module_operr(module
);
4579 p_el
->flags
= LDB_FLAG_MOD_ADD
;
4583 switch (next_deletion_state
) {
4585 case OBJECT_RECYCLED
:
4586 case OBJECT_TOMBSTONE
:
4589 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4590 * describes what must be removed from a tombstone
4593 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4594 * describes what must be removed from a recycled
4600 * we also mark it as recycled, meaning this object can't be
4601 * recovered (we are stripping its attributes).
4602 * This is done only if we have this schema object of course ...
4603 * This behavior is identical to the one of Windows 2008R2 which
4604 * always set the isRecycled attribute, even if the recycle-bin is
4605 * not activated and what ever the forest level is.
4607 if (dsdb_attribute_by_lDAPDisplayName(schema
, "isRecycled") != NULL
) {
4608 struct ldb_message_element
*is_recycled_el
;
4610 ret
= ldb_msg_add_value(msg
, "isRecycled", &true_val
,
4612 if (ret
!= LDB_SUCCESS
) {
4613 DEBUG(0,(__location__
": Failed to add isRecycled string to the msg\n"));
4614 ldb_module_oom(module
);
4615 talloc_free(tmp_ctx
);
4618 is_recycled_el
->flags
= LDB_FLAG_MOD_REPLACE
;
4621 replmd_private
= talloc_get_type(ldb_module_get_private(module
),
4622 struct replmd_private
);
4623 /* work out which of the old attributes we will be removing */
4624 for (i
=0; i
<old_msg
->num_elements
; i
++) {
4625 const struct dsdb_attribute
*sa
;
4626 el
= &old_msg
->elements
[i
];
4627 sa
= dsdb_attribute_by_lDAPDisplayName(schema
, el
->name
);
4629 const char *old_dn_str
4630 = ldb_dn_get_linearized(old_dn
);
4632 ldb_asprintf_errstring(ldb
,
4635 "not found in schema "
4636 "when deleting %s. "
4637 "Existing record is invalid",
4640 talloc_free(tmp_ctx
);
4641 return LDB_ERR_OPERATIONS_ERROR
;
4643 if (ldb_attr_cmp(el
->name
, rdn_name
) == 0) {
4644 /* don't remove the rDN */
4648 if (sa
->linkID
& 1) {
4649 bool caller_should_vanish
= false;
4651 * we have a backlink in this object
4652 * that needs to be removed. We're not
4653 * allowed to remove it directly
4654 * however, so we instead setup a
4655 * modify to delete the corresponding
4658 ret
= replmd_delete_remove_link(module
, schema
,
4662 &caller_should_vanish
);
4663 if (ret
!= LDB_SUCCESS
) {
4664 const char *old_dn_str
4665 = ldb_dn_get_linearized(old_dn
);
4666 ldb_asprintf_errstring(ldb
,
4668 ": Failed to remove backlink of "
4669 "%s when deleting %s: %s",
4672 ldb_errstring(ldb
));
4673 talloc_free(tmp_ctx
);
4674 return LDB_ERR_OPERATIONS_ERROR
;
4677 if (caller_should_vanish
== false) {
4679 * now we continue, which means we
4680 * won't remove this backlink
4687 * Otherwise vanish the link, we are
4688 * out of sync and the controlling
4689 * object does not have the source
4693 dsdb_flags
|= DSDB_REPLMD_VANISH_LINKS
;
4695 } else if (sa
->linkID
== 0) {
4696 const char * const *attr
= NULL
;
4697 if (sa
->searchFlags
& SEARCH_FLAG_PRESERVEONDELETE
) {
4700 BINARY_ARRAY_SEARCH_V(preserved_attrs
,
4701 ARRAY_SIZE(preserved_attrs
),
4706 * If we are preserving, do not do the
4707 * ldb_msg_add_empty() below, continue
4708 * to the next element
4715 * Ensure that we tell the modification to vanish any linked
4716 * attributes (not simply mark them as isDeleted = TRUE)
4718 dsdb_flags
|= DSDB_REPLMD_VANISH_LINKS
;
4720 ret
= ldb_msg_add_empty(msg
, el
->name
, LDB_FLAG_MOD_DELETE
, NULL
);
4721 if (ret
!= LDB_SUCCESS
) {
4722 talloc_free(tmp_ctx
);
4723 ldb_module_oom(module
);
4730 case OBJECT_DELETED
:
4732 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4733 * describes what must be removed from a deleted
4737 ret
= ldb_msg_add_empty(msg
, "objectCategory", LDB_FLAG_MOD_REPLACE
, NULL
);
4738 if (ret
!= LDB_SUCCESS
) {
4739 talloc_free(tmp_ctx
);
4740 ldb_module_oom(module
);
4744 ret
= ldb_msg_add_empty(msg
, "sAMAccountType", LDB_FLAG_MOD_REPLACE
, NULL
);
4745 if (ret
!= LDB_SUCCESS
) {
4746 talloc_free(tmp_ctx
);
4747 ldb_module_oom(module
);
4757 if (deletion_state
== OBJECT_NOT_DELETED
) {
4758 const struct dsdb_attribute
*sa
;
4760 /* work out what the new rdn value is, for updating the
4761 rDN and name fields */
4762 new_rdn_value
= ldb_dn_get_rdn_val(new_dn
);
4763 if (new_rdn_value
== NULL
) {
4764 talloc_free(tmp_ctx
);
4765 return ldb_operr(ldb
);
4768 sa
= dsdb_attribute_by_lDAPDisplayName(schema
, rdn_name
);
4770 talloc_free(tmp_ctx
);
4771 return LDB_ERR_OPERATIONS_ERROR
;
4774 ret
= ldb_msg_add_value(msg
, sa
->lDAPDisplayName
, new_rdn_value
,
4776 if (ret
!= LDB_SUCCESS
) {
4777 talloc_free(tmp_ctx
);
4780 el
->flags
= LDB_FLAG_MOD_REPLACE
;
4782 el
= ldb_msg_find_element(old_msg
, "name");
4784 ret
= ldb_msg_add_value(msg
, "name", new_rdn_value
, &el
);
4785 if (ret
!= LDB_SUCCESS
) {
4786 talloc_free(tmp_ctx
);
4789 el
->flags
= LDB_FLAG_MOD_REPLACE
;
4794 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4799 * No matter what has happned with other renames, try again to
4800 * get this to be under the deleted DN.
4802 if (strcmp(ldb_dn_get_linearized(old_dn
), ldb_dn_get_linearized(new_dn
)) != 0) {
4803 /* now rename onto the new DN */
4804 ret
= dsdb_module_rename(module
, old_dn
, new_dn
, DSDB_FLAG_NEXT_MODULE
, req
);
4805 if (ret
!= LDB_SUCCESS
){
4806 DEBUG(0,(__location__
": Failed to rename object from '%s' to '%s' - %s\n",
4807 ldb_dn_get_linearized(old_dn
),
4808 ldb_dn_get_linearized(new_dn
),
4809 ldb_errstring(ldb
)));
4810 talloc_free(tmp_ctx
);
4816 ret
= dsdb_module_modify(module
, msg
, dsdb_flags
|DSDB_FLAG_OWN_MODULE
, req
);
4817 if (ret
!= LDB_SUCCESS
) {
4820 * This should not fail, so be quite verbose in the
4821 * error handling if it fails
4823 if (strcmp(ldb_dn_get_linearized(old_dn
),
4824 ldb_dn_get_linearized(new_dn
)) != 0) {
4825 DBG_NOTICE("Failure to handle '%s' of object %s "
4826 "after successful rename to %s. "
4827 "Error during tombstone modificaton was: %s\n",
4828 re_delete
? "re-delete" : "delete",
4829 ldb_dn_get_linearized(new_dn
),
4830 ldb_dn_get_linearized(old_dn
),
4831 ldb_errstring(ldb
));
4833 DBG_NOTICE("Failure to handle '%s' of object %s. "
4834 "Error during tombstone modificaton was: %s\n",
4835 re_delete
? "re-delete" : "delete",
4836 ldb_dn_get_linearized(new_dn
),
4837 ldb_errstring(ldb
));
4839 s
= ldb_ldif_message_redacted_string(ldb_module_get_ctx(module
),
4841 LDB_CHANGETYPE_MODIFY
,
4844 DBG_INFO("Failed tombstone modify%s was:\n%s\n",
4845 (dsdb_flags
& DSDB_REPLMD_VANISH_LINKS
) ?
4846 " with VANISH_LINKS" : "",
4848 ldb_asprintf_errstring(ldb
,
4849 "replmd_delete: Failed to modify"
4850 " object %s in '%s' - %s",
4851 ldb_dn_get_linearized(old_dn
),
4852 re_delete
? "re-delete" : "delete",
4853 ldb_errstring(ldb
));
4854 talloc_free(tmp_ctx
);
4858 talloc_free(tmp_ctx
);
4860 return ldb_module_done(req
, NULL
, NULL
, LDB_SUCCESS
);
4863 static int replmd_delete(struct ldb_module
*module
, struct ldb_request
*req
)
4865 return replmd_delete_internals(module
, req
, false);
4869 static int replmd_replicated_request_error(struct replmd_replicated_request
*ar
, int ret
)
4874 static int replmd_replicated_request_werror(struct replmd_replicated_request
*ar
, WERROR status
)
4876 int ret
= LDB_ERR_OTHER
;
4877 /* TODO: do some error mapping */
4879 /* Let the caller know the full WERROR */
4880 ar
->objs
->error
= status
;
4886 static struct replPropertyMetaData1
*
4887 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob
*md_blob
,
4888 enum drsuapi_DsAttributeId attid
)
4891 struct replPropertyMetaDataCtr1
*rpmd_ctr
= &md_blob
->ctr
.ctr1
;
4893 for (i
= 0; i
< rpmd_ctr
->count
; i
++) {
4894 if (rpmd_ctr
->array
[i
].attid
== attid
) {
4895 return &rpmd_ctr
->array
[i
];
4903 return true if an update is newer than an existing entry
4904 see section 5.11 of MS-ADTS
4906 static bool replmd_update_is_newer(const struct GUID
*current_invocation_id
,
4907 const struct GUID
*update_invocation_id
,
4908 uint32_t current_version
,
4909 uint32_t update_version
,
4910 NTTIME current_change_time
,
4911 NTTIME update_change_time
)
4913 if (update_version
!= current_version
) {
4914 return update_version
> current_version
;
4916 if (update_change_time
!= current_change_time
) {
4917 return update_change_time
> current_change_time
;
4919 return GUID_compare(update_invocation_id
, current_invocation_id
) > 0;
4922 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1
*cur_m
,
4923 struct replPropertyMetaData1
*new_m
)
4925 return replmd_update_is_newer(&cur_m
->originating_invocation_id
,
4926 &new_m
->originating_invocation_id
,
4929 cur_m
->originating_change_time
,
4930 new_m
->originating_change_time
);
4933 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags
,
4934 struct replPropertyMetaData1
*cur_m
,
4935 struct replPropertyMetaData1
*new_m
)
4940 * If the new replPropertyMetaData entry for this attribute is
4941 * not provided (this happens in the case where we look for
4942 * ATTID_name, but the name was not changed), then the local
4943 * state is clearly still current, as the remote
4944 * server didn't send it due to being older the high watermark
4947 if (new_m
== NULL
) {
4951 if (dsdb_repl_flags
& DSDB_REPL_FLAG_PRIORITISE_INCOMING
) {
4953 * if we compare equal then do an
4954 * update. This is used when a client
4955 * asks for a FULL_SYNC, and can be
4956 * used to recover a corrupt
4959 * This call is a bit tricky, what we
4960 * are doing it turning the 'is_newer'
4961 * call into a 'not is older' by
4962 * swapping cur_m and new_m, and negating the
4965 cmp
= !replmd_replPropertyMetaData1_is_newer(new_m
,
4968 cmp
= replmd_replPropertyMetaData1_is_newer(cur_m
,
4976 form a DN for a deleted (DEL:) or conflict (CNF:) DN
4978 static int replmd_make_prefix_child_dn(TALLOC_CTX
*tmp_ctx
,
4979 struct ldb_context
*ldb
,
4981 const char *four_char_prefix
,
4982 const char *rdn_name
,
4983 const struct ldb_val
*rdn_value
,
4986 struct ldb_val deleted_child_rdn_val
;
4987 struct GUID_txt_buf guid_str
;
4991 GUID_buf_string(&guid
, &guid_str
);
4993 retb
= ldb_dn_add_child_fmt(dn
, "X=Y");
4995 ldb_asprintf_errstring(ldb
, __location__
4996 ": Unable to add a formatted child to dn: %s",
4997 ldb_dn_get_linearized(dn
));
4998 return LDB_ERR_OPERATIONS_ERROR
;
5002 * TODO: Per MS-ADTS 3.1.1.5.5 Delete Operation
5003 * we should truncate this value to ensure the RDN is not more than 255 chars.
5005 * However we MS-ADTS 3.1.1.5.1.2 Naming Constraints indicates that:
5007 * "Naming constraints are not enforced for replicated
5008 * updates." so this is safe and we don't have to work out not
5009 * splitting a UTF8 char right now.
5011 deleted_child_rdn_val
= ldb_val_dup(tmp_ctx
, rdn_value
);
5014 * sizeof(guid_str.buf) will always be longer than
5015 * strlen(guid_str.buf) but we allocate using this and
5016 * waste the trailing bytes to avoid scaring folks
5017 * with memcpy() using strlen() below
5020 deleted_child_rdn_val
.data
5021 = talloc_realloc(tmp_ctx
, deleted_child_rdn_val
.data
,
5023 rdn_value
->length
+ 5
5024 + sizeof(guid_str
.buf
));
5025 if (!deleted_child_rdn_val
.data
) {
5026 ldb_asprintf_errstring(ldb
, __location__
5027 ": Unable to add a formatted child to dn: %s",
5028 ldb_dn_get_linearized(dn
));
5029 return LDB_ERR_OPERATIONS_ERROR
;
5032 deleted_child_rdn_val
.length
=
5033 rdn_value
->length
+ 5
5034 + strlen(guid_str
.buf
);
5036 SMB_ASSERT(deleted_child_rdn_val
.length
<
5037 talloc_get_size(deleted_child_rdn_val
.data
));
5040 * talloc won't allocate more than 256MB so we can't
5041 * overflow but just to be sure
5043 if (deleted_child_rdn_val
.length
< rdn_value
->length
) {
5044 return LDB_ERR_OPERATIONS_ERROR
;
5047 deleted_child_rdn_val
.data
[rdn_value
->length
] = 0x0a;
5048 memcpy(&deleted_child_rdn_val
.data
[rdn_value
->length
+ 1],
5049 four_char_prefix
, 4);
5050 memcpy(&deleted_child_rdn_val
.data
[rdn_value
->length
+ 5],
5052 sizeof(guid_str
.buf
));
5054 /* Now set the value into the RDN, without parsing it */
5055 ret
= ldb_dn_set_component(
5059 deleted_child_rdn_val
);
5068 static struct ldb_dn
*replmd_conflict_dn(TALLOC_CTX
*mem_ctx
,
5069 struct ldb_context
*ldb
,
5073 const struct ldb_val
*rdn_val
;
5074 const char *rdn_name
;
5075 struct ldb_dn
*new_dn
;
5078 rdn_val
= ldb_dn_get_rdn_val(dn
);
5079 rdn_name
= ldb_dn_get_rdn_name(dn
);
5080 if (!rdn_val
|| !rdn_name
) {
5084 new_dn
= ldb_dn_get_parent(mem_ctx
, dn
);
5089 ret
= replmd_make_prefix_child_dn(mem_ctx
,
5095 if (ret
!= LDB_SUCCESS
) {
5104 static int replmd_make_deleted_child_dn(TALLOC_CTX
*tmp_ctx
,
5105 struct ldb_context
*ldb
,
5107 const char *rdn_name
,
5108 const struct ldb_val
*rdn_value
,
5111 return replmd_make_prefix_child_dn(tmp_ctx
,
5121 perform a modify operation which sets the rDN and name attributes to
5122 their current values. This has the effect of changing these
5123 attributes to have been last updated by the current DC. This is
5124 needed to ensure that renames performed as part of conflict
5125 resolution are propagated to other DCs
5127 static int replmd_name_modify(struct replmd_replicated_request
*ar
,
5128 struct ldb_request
*req
, struct ldb_dn
*dn
)
5130 struct ldb_message
*msg
;
5131 const char *rdn_name
;
5132 const struct ldb_val
*rdn_val
;
5133 const struct dsdb_attribute
*rdn_attr
;
5136 msg
= ldb_msg_new(req
);
5142 rdn_name
= ldb_dn_get_rdn_name(dn
);
5143 if (rdn_name
== NULL
) {
5147 /* normalize the rdn attribute name */
5148 rdn_attr
= dsdb_attribute_by_lDAPDisplayName(ar
->schema
, rdn_name
);
5149 if (rdn_attr
== NULL
) {
5152 rdn_name
= rdn_attr
->lDAPDisplayName
;
5154 rdn_val
= ldb_dn_get_rdn_val(dn
);
5155 if (rdn_val
== NULL
) {
5159 if (ldb_msg_append_value(msg
, rdn_name
, rdn_val
, LDB_FLAG_MOD_REPLACE
) != 0) {
5162 if (ldb_msg_append_value(msg
, "name", rdn_val
, LDB_FLAG_MOD_REPLACE
) != 0) {
5167 * We have to mark this as a replicated update otherwise
5168 * schema_data may reject a rename in the schema partition
5171 ret
= dsdb_module_modify(ar
->module
, msg
,
5172 DSDB_FLAG_OWN_MODULE
|DSDB_FLAG_REPLICATED_UPDATE
,
5174 if (ret
!= LDB_SUCCESS
) {
5175 DEBUG(0,(__location__
": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
5176 ldb_dn_get_linearized(dn
),
5177 ldb_errstring(ldb_module_get_ctx(ar
->module
))));
5187 DEBUG(0,(__location__
": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
5188 ldb_dn_get_linearized(dn
)));
5189 return LDB_ERR_OPERATIONS_ERROR
;
5194 callback for conflict DN handling where we have renamed the incoming
5195 record. After renaming it, we need to ensure the change of name and
5196 rDN for the incoming record is seen as an originating update by this DC.
5198 This also handles updating lastKnownParent for entries sent to lostAndFound
5200 static int replmd_op_name_modify_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
5202 struct replmd_replicated_request
*ar
=
5203 talloc_get_type_abort(req
->context
, struct replmd_replicated_request
);
5204 struct ldb_dn
*conflict_dn
= NULL
;
5207 if (ares
->error
!= LDB_SUCCESS
) {
5208 /* call the normal callback for everything except success */
5209 return replmd_op_callback(req
, ares
);
5212 switch (req
->operation
) {
5214 conflict_dn
= req
->op
.add
.message
->dn
;
5217 conflict_dn
= req
->op
.mod
.message
->dn
;
5220 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
5223 /* perform a modify of the rDN and name of the record */
5224 ret
= replmd_name_modify(ar
, req
, conflict_dn
);
5225 if (ret
!= LDB_SUCCESS
) {
5227 return replmd_op_callback(req
, ares
);
5230 if (ar
->objs
->objects
[ar
->index_current
].last_known_parent
) {
5231 struct ldb_message
*msg
= ldb_msg_new(req
);
5233 ldb_module_oom(ar
->module
);
5234 return LDB_ERR_OPERATIONS_ERROR
;
5237 msg
->dn
= req
->op
.add
.message
->dn
;
5239 ret
= ldb_msg_add_steal_string(msg
, "lastKnownParent",
5240 ldb_dn_get_extended_linearized(msg
, ar
->objs
->objects
[ar
->index_current
].last_known_parent
, 1));
5241 if (ret
!= LDB_SUCCESS
) {
5242 DEBUG(0,(__location__
": Failed to add lastKnownParent string to the msg\n"));
5243 ldb_module_oom(ar
->module
);
5246 msg
->elements
[0].flags
= LDB_FLAG_MOD_REPLACE
;
5248 ret
= dsdb_module_modify(ar
->module
, msg
, DSDB_FLAG_OWN_MODULE
, req
);
5249 if (ret
!= LDB_SUCCESS
) {
5250 DEBUG(0,(__location__
": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
5251 ldb_dn_get_linearized(msg
->dn
),
5252 ldb_errstring(ldb_module_get_ctx(ar
->module
))));
5258 return replmd_op_callback(req
, ares
);
5264 * A helper for replmd_op_possible_conflict_callback() and
5265 * replmd_replicated_handle_rename()
5267 static int incoming_dn_should_be_renamed(TALLOC_CTX
*mem_ctx
,
5268 struct replmd_replicated_request
*ar
,
5269 struct ldb_dn
*conflict_dn
,
5270 struct ldb_result
**res
,
5271 bool *rename_incoming_record
)
5275 enum ndr_err_code ndr_err
;
5276 const struct ldb_val
*omd_value
= NULL
;
5277 struct replPropertyMetaDataBlob omd
, *rmd
= NULL
;
5278 struct ldb_context
*ldb
= ldb_module_get_ctx(ar
->module
);
5279 const char *attrs
[] = { "replPropertyMetaData", "objectGUID", NULL
};
5280 struct replPropertyMetaData1
*omd_name
= NULL
;
5281 struct replPropertyMetaData1
*rmd_name
= NULL
;
5282 struct ldb_message
*msg
= NULL
;
5284 ret
= samdb_rodc(ldb
, &rodc
);
5285 if (ret
!= LDB_SUCCESS
) {
5286 ldb_asprintf_errstring(
5288 "Failed to determine if we are an RODC when attempting "
5289 "to form conflict DN: %s",
5290 ldb_errstring(ldb
));
5291 return LDB_ERR_OPERATIONS_ERROR
;
5296 * We are on an RODC, or were a GC for this
5297 * partition, so we have to fail this until
5298 * someone who owns the partition sorts it
5301 ldb_asprintf_errstring(
5303 "Conflict adding object '%s' from incoming replication "
5304 "but we are read only for the partition. \n"
5305 " - We must fail the operation until a master for this "
5306 "partition resolves the conflict",
5307 ldb_dn_get_linearized(conflict_dn
));
5308 return LDB_ERR_OPERATIONS_ERROR
;
5312 * first we need the replPropertyMetaData attribute from the
5315 ret
= dsdb_module_search_dn(ar
->module
, mem_ctx
, res
, conflict_dn
,
5317 DSDB_FLAG_NEXT_MODULE
|
5318 DSDB_SEARCH_SHOW_DELETED
|
5319 DSDB_SEARCH_SHOW_RECYCLED
, ar
->req
);
5320 if (ret
!= LDB_SUCCESS
) {
5321 DBG_ERR(__location__
5322 ": Unable to find object for conflicting record '%s'\n",
5323 ldb_dn_get_linearized(conflict_dn
));
5324 return LDB_ERR_OPERATIONS_ERROR
;
5327 msg
= (*res
)->msgs
[0];
5328 omd_value
= ldb_msg_find_ldb_val(msg
, "replPropertyMetaData");
5329 if (omd_value
== NULL
) {
5330 DBG_ERR(__location__
5331 ": Unable to find replPropertyMetaData for conflicting "
5333 ldb_dn_get_linearized(conflict_dn
));
5334 return LDB_ERR_OPERATIONS_ERROR
;
5337 ndr_err
= ndr_pull_struct_blob(
5338 omd_value
, msg
, &omd
,
5339 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
5340 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
5341 DBG_ERR(__location__
5342 ": Failed to parse old replPropertyMetaData for %s\n",
5343 ldb_dn_get_linearized(conflict_dn
));
5344 return LDB_ERR_OPERATIONS_ERROR
;
5347 rmd
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
5350 * we decide which is newer based on the RPMD on the name
5351 * attribute. See [MS-DRSR] ResolveNameConflict.
5353 * We expect omd_name to be present, as this is from a local
5354 * search, but while rmd_name should have been given to us by
5355 * the remote server, if it is missing we just prefer the
5357 * replmd_replPropertyMetaData1_new_should_be_taken()
5359 rmd_name
= replmd_replPropertyMetaData1_find_attid(rmd
,
5360 DRSUAPI_ATTID_name
);
5361 omd_name
= replmd_replPropertyMetaData1_find_attid(&omd
,
5362 DRSUAPI_ATTID_name
);
5364 DBG_ERR(__location__
5365 ": Failed to find name attribute in "
5366 "local LDB replPropertyMetaData for %s\n",
5367 ldb_dn_get_linearized(conflict_dn
));
5368 return LDB_ERR_OPERATIONS_ERROR
;
5372 * Should we preserve the current record, and so rename the
5373 * incoming record to be a conflict?
5375 *rename_incoming_record
=
5376 !replmd_replPropertyMetaData1_new_should_be_taken(
5377 (ar
->objs
->dsdb_repl_flags
&
5378 DSDB_REPL_FLAG_PRIORITISE_INCOMING
),
5379 omd_name
, rmd_name
);
5386 callback for replmd_replicated_apply_add()
5387 This copes with the creation of conflict records in the case where
5388 the DN exists, but with a different objectGUID
5390 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
))
5392 struct ldb_dn
*conflict_dn
;
5393 struct replmd_replicated_request
*ar
=
5394 talloc_get_type_abort(req
->context
, struct replmd_replicated_request
);
5395 struct ldb_result
*res
;
5397 bool rename_incoming_record
;
5398 struct ldb_message
*msg
;
5399 struct ldb_request
*down_req
= NULL
;
5401 /* call the normal callback for success */
5402 if (ares
->error
== LDB_SUCCESS
) {
5403 return callback(req
, ares
);
5407 * we have a conflict, and need to decide if we will keep the
5408 * new record or the old record
5411 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
5412 conflict_dn
= msg
->dn
;
5414 /* For failures other than conflicts, fail the whole operation here */
5415 if (ares
->error
!= LDB_ERR_ENTRY_ALREADY_EXISTS
) {
5416 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
), "Failed to locally apply remote add of %s: %s",
5417 ldb_dn_get_linearized(conflict_dn
),
5418 ldb_errstring(ldb_module_get_ctx(ar
->module
)));
5420 return ldb_module_done(ar
->req
, NULL
, NULL
,
5421 LDB_ERR_OPERATIONS_ERROR
);
5425 ret
= incoming_dn_should_be_renamed(req
, ar
, conflict_dn
, &res
,
5426 &rename_incoming_record
);
5427 if (ret
!= LDB_SUCCESS
) {
5431 if (rename_incoming_record
) {
5433 struct ldb_dn
*new_dn
;
5435 guid
= samdb_result_guid(msg
, "objectGUID");
5436 if (GUID_all_zero(&guid
)) {
5437 DEBUG(0,(__location__
": Failed to find objectGUID for conflicting incoming record %s\n",
5438 ldb_dn_get_linearized(conflict_dn
)));
5441 new_dn
= replmd_conflict_dn(req
,
5442 ldb_module_get_ctx(ar
->module
),
5443 conflict_dn
, &guid
);
5444 if (new_dn
== NULL
) {
5445 DEBUG(0,(__location__
": Failed to form conflict DN for %s\n",
5446 ldb_dn_get_linearized(conflict_dn
)));
5450 DEBUG(2,(__location__
": Resolving conflict record via incoming rename '%s' -> '%s'\n",
5451 ldb_dn_get_linearized(conflict_dn
), ldb_dn_get_linearized(new_dn
)));
5453 /* re-submit the request, but with the new DN */
5454 callback
= replmd_op_name_modify_callback
;
5457 /* we are renaming the existing record */
5459 struct ldb_dn
*new_dn
;
5461 guid
= samdb_result_guid(res
->msgs
[0], "objectGUID");
5462 if (GUID_all_zero(&guid
)) {
5463 DEBUG(0,(__location__
": Failed to find objectGUID for existing conflict record %s\n",
5464 ldb_dn_get_linearized(conflict_dn
)));
5468 new_dn
= replmd_conflict_dn(req
,
5469 ldb_module_get_ctx(ar
->module
),
5470 conflict_dn
, &guid
);
5471 if (new_dn
== NULL
) {
5472 DEBUG(0,(__location__
": Failed to form conflict DN for %s\n",
5473 ldb_dn_get_linearized(conflict_dn
)));
5477 DEBUG(2,(__location__
": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5478 ldb_dn_get_linearized(conflict_dn
), ldb_dn_get_linearized(new_dn
)));
5480 ret
= dsdb_module_rename(ar
->module
, conflict_dn
, new_dn
,
5481 DSDB_FLAG_OWN_MODULE
, req
);
5482 if (ret
!= LDB_SUCCESS
) {
5483 DEBUG(0,(__location__
": Failed to rename conflict dn '%s' to '%s' - %s\n",
5484 ldb_dn_get_linearized(conflict_dn
),
5485 ldb_dn_get_linearized(new_dn
),
5486 ldb_errstring(ldb_module_get_ctx(ar
->module
))));
5491 * now we need to ensure that the rename is seen as an
5492 * originating update. We do that with a modify.
5494 ret
= replmd_name_modify(ar
, req
, new_dn
);
5495 if (ret
!= LDB_SUCCESS
) {
5499 DEBUG(2,(__location__
": With conflicting record renamed, re-apply replicated creation of '%s'\n",
5500 ldb_dn_get_linearized(req
->op
.add
.message
->dn
)));
5503 ret
= ldb_build_add_req(&down_req
,
5504 ldb_module_get_ctx(ar
->module
),
5511 if (ret
!= LDB_SUCCESS
) {
5514 LDB_REQ_SET_LOCATION(down_req
);
5516 /* current partition control needed by "repmd_op_callback" */
5517 ret
= ldb_request_add_control(down_req
,
5518 DSDB_CONTROL_CURRENT_PARTITION_OID
,
5520 if (ret
!= LDB_SUCCESS
) {
5521 return replmd_replicated_request_error(ar
, ret
);
5524 if (ar
->objs
->dsdb_repl_flags
& DSDB_REPL_FLAG_PARTIAL_REPLICA
) {
5525 /* this tells the partition module to make it a
5526 partial replica if creating an NC */
5527 ret
= ldb_request_add_control(down_req
,
5528 DSDB_CONTROL_PARTIAL_REPLICA
,
5530 if (ret
!= LDB_SUCCESS
) {
5531 return replmd_replicated_request_error(ar
, ret
);
5536 * Finally we re-run the add, otherwise the new record won't
5537 * exist, as we are here because of that exact failure!
5539 return ldb_next_request(ar
->module
, down_req
);
5542 /* on failure make the caller get the error. This means
5543 * replication will stop with an error, but there is not much
5546 if (ret
== LDB_SUCCESS
) {
5547 ret
= LDB_ERR_OPERATIONS_ERROR
;
5549 return ldb_module_done(ar
->req
, NULL
, NULL
,
5554 callback for replmd_replicated_apply_add()
5555 This copes with the creation of conflict records in the case where
5556 the DN exists, but with a different objectGUID
5558 static int replmd_op_add_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
5560 struct replmd_replicated_request
*ar
=
5561 talloc_get_type_abort(req
->context
, struct replmd_replicated_request
);
5563 if (ar
->objs
->objects
[ar
->index_current
].last_known_parent
) {
5564 /* This is like a conflict DN, where we put the object in LostAndFound
5565 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
5566 return replmd_op_possible_conflict_callback(req
, ares
, replmd_op_name_modify_callback
);
5569 return replmd_op_possible_conflict_callback(req
, ares
, replmd_op_callback
);
5573 this is called when a new object comes in over DRS
5575 static int replmd_replicated_apply_add(struct replmd_replicated_request
*ar
)
5577 struct ldb_context
*ldb
;
5578 struct ldb_request
*change_req
;
5579 enum ndr_err_code ndr_err
;
5580 struct ldb_message
*msg
;
5581 struct replPropertyMetaDataBlob
*md
;
5582 struct ldb_val md_value
;
5585 bool remote_isDeleted
= false;
5588 time_t t
= time(NULL
);
5589 const struct ldb_val
*rdn_val
;
5590 struct replmd_private
*replmd_private
=
5591 talloc_get_type(ldb_module_get_private(ar
->module
),
5592 struct replmd_private
);
5593 unix_to_nt_time(&now
, t
);
5595 ldb
= ldb_module_get_ctx(ar
->module
);
5596 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
5597 md
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
5598 is_schema_nc
= ldb_dn_compare_base(replmd_private
->schema_dn
, msg
->dn
) == 0;
5600 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &ar
->seq_num
);
5601 if (ret
!= LDB_SUCCESS
) {
5602 return replmd_replicated_request_error(ar
, ret
);
5605 ret
= dsdb_msg_add_guid(msg
,
5606 &ar
->objs
->objects
[ar
->index_current
].object_guid
,
5608 if (ret
!= LDB_SUCCESS
) {
5609 return replmd_replicated_request_error(ar
, ret
);
5612 ret
= ldb_msg_add_string(msg
, "whenChanged", ar
->objs
->objects
[ar
->index_current
].when_changed
);
5613 if (ret
!= LDB_SUCCESS
) {
5614 return replmd_replicated_request_error(ar
, ret
);
5617 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNCreated", ar
->seq_num
);
5618 if (ret
!= LDB_SUCCESS
) {
5619 return replmd_replicated_request_error(ar
, ret
);
5622 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNChanged", ar
->seq_num
);
5623 if (ret
!= LDB_SUCCESS
) {
5624 return replmd_replicated_request_error(ar
, ret
);
5627 /* remove any message elements that have zero values */
5628 for (i
=0; i
<msg
->num_elements
; i
++) {
5629 struct ldb_message_element
*el
= &msg
->elements
[i
];
5631 if (el
->num_values
== 0) {
5632 if (ldb_attr_cmp(msg
->elements
[i
].name
, "objectClass") == 0) {
5633 ldb_asprintf_errstring(ldb
, __location__
5634 ": empty objectClass sent on %s, aborting replication\n",
5635 ldb_dn_get_linearized(msg
->dn
));
5636 return replmd_replicated_request_error(ar
, LDB_ERR_OBJECT_CLASS_VIOLATION
);
5639 DEBUG(4,(__location__
": Removing attribute %s with num_values==0\n",
5641 ldb_msg_remove_element(msg
, &msg
->elements
[i
]);
5648 struct GUID_txt_buf guid_txt
;
5650 char *s
= ldb_ldif_message_redacted_string(ldb
, ar
,
5653 DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5654 GUID_buf_string(&ar
->objs
->objects
[ar
->index_current
].object_guid
, &guid_txt
),
5657 } else if (DEBUGLVL(4)) {
5658 struct GUID_txt_buf guid_txt
;
5659 DEBUG(4, ("DRS replication add DN of %s is %s\n",
5660 GUID_buf_string(&ar
->objs
->objects
[ar
->index_current
].object_guid
, &guid_txt
),
5661 ldb_dn_get_linearized(msg
->dn
)));
5663 remote_isDeleted
= ldb_msg_find_attr_as_bool(msg
,
5664 "isDeleted", false);
5667 * the meta data array is already sorted by the caller, except
5668 * for the RDN, which needs to be added.
5672 rdn_val
= ldb_dn_get_rdn_val(msg
->dn
);
5673 ret
= replmd_update_rpmd_rdn_attr(ldb
, msg
, rdn_val
, NULL
,
5674 md
, ar
, now
, is_schema_nc
,
5676 if (ret
!= LDB_SUCCESS
) {
5677 ldb_asprintf_errstring(ldb
, "%s: error during DRS repl ADD: %s", __func__
, ldb_errstring(ldb
));
5678 return replmd_replicated_request_error(ar
, ret
);
5681 ret
= replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb
, &md
->ctr
.ctr1
, msg
->dn
);
5682 if (ret
!= LDB_SUCCESS
) {
5683 ldb_asprintf_errstring(ldb
, "%s: error during DRS repl ADD: %s", __func__
, ldb_errstring(ldb
));
5684 return replmd_replicated_request_error(ar
, ret
);
5687 for (i
=0; i
< md
->ctr
.ctr1
.count
; i
++) {
5688 md
->ctr
.ctr1
.array
[i
].local_usn
= ar
->seq_num
;
5690 ndr_err
= ndr_push_struct_blob(&md_value
, msg
, md
,
5691 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
5692 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
5693 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
5694 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
5696 ret
= ldb_msg_add_value(msg
, "replPropertyMetaData", &md_value
, NULL
);
5697 if (ret
!= LDB_SUCCESS
) {
5698 return replmd_replicated_request_error(ar
, ret
);
5701 replmd_ldb_message_sort(msg
, ar
->schema
);
5703 if (!remote_isDeleted
) {
5705 * Ensure any local ACL inheritence is applied from
5706 * the parent object.
5708 * This is needed because descriptor is above
5709 * repl_meta_data in the module stack, so this will
5710 * not be trigered 'naturally' by the flow of
5713 ret
= dsdb_module_schedule_sd_propagation(ar
->module
,
5714 ar
->objs
->partition_dn
,
5715 ar
->objs
->objects
[ar
->index_current
].object_guid
,
5716 ar
->objs
->objects
[ar
->index_current
].parent_guid
?
5717 *ar
->objs
->objects
[ar
->index_current
].parent_guid
:
5720 if (ret
!= LDB_SUCCESS
) {
5721 return replmd_replicated_request_error(ar
, ret
);
5725 ar
->isDeleted
= remote_isDeleted
;
5727 ret
= ldb_build_add_req(&change_req
,
5733 replmd_op_add_callback
,
5735 LDB_REQ_SET_LOCATION(change_req
);
5736 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
5738 /* current partition control needed by "repmd_op_callback" */
5739 ret
= ldb_request_add_control(change_req
,
5740 DSDB_CONTROL_CURRENT_PARTITION_OID
,
5742 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
5744 if (ar
->objs
->dsdb_repl_flags
& DSDB_REPL_FLAG_PARTIAL_REPLICA
) {
5745 /* this tells the partition module to make it a
5746 partial replica if creating an NC */
5747 ret
= ldb_request_add_control(change_req
,
5748 DSDB_CONTROL_PARTIAL_REPLICA
,
5750 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
5753 return ldb_next_request(ar
->module
, change_req
);
5756 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request
*req
,
5757 struct ldb_reply
*ares
)
5759 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
5760 struct replmd_replicated_request
);
5764 return ldb_module_done(ar
->req
, NULL
, NULL
,
5765 LDB_ERR_OPERATIONS_ERROR
);
5769 * The error NO_SUCH_OBJECT is not expected, unless the search
5770 * base is the partition DN, and that case doesn't happen here
5771 * because then we wouldn't get a parent_guid_value in any
5774 if (ares
->error
!= LDB_SUCCESS
) {
5775 return ldb_module_done(ar
->req
, ares
->controls
,
5776 ares
->response
, ares
->error
);
5779 switch (ares
->type
) {
5780 case LDB_REPLY_ENTRY
:
5782 struct ldb_message
*parent_msg
= ares
->message
;
5783 struct ldb_message
*msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
5784 struct ldb_dn
*parent_dn
= NULL
;
5787 if (!ldb_msg_check_string_attribute(msg
, "isDeleted", "TRUE")
5788 && ldb_msg_check_string_attribute(parent_msg
, "isDeleted", "TRUE")) {
5789 /* Per MS-DRSR 4.1.10.6.10
5790 * FindBestParentObject we need to move this
5791 * new object under a deleted object to
5793 struct ldb_dn
*nc_root
;
5795 ret
= dsdb_find_nc_root(ldb_module_get_ctx(ar
->module
), msg
, msg
->dn
, &nc_root
);
5796 if (ret
== LDB_ERR_NO_SUCH_OBJECT
) {
5797 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5798 "No suitable NC root found for %s. "
5799 "We need to move this object because parent object %s "
5800 "is deleted, but this object is not.",
5801 ldb_dn_get_linearized(msg
->dn
),
5802 ldb_dn_get_linearized(parent_msg
->dn
));
5803 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
5804 } else if (ret
!= LDB_SUCCESS
) {
5805 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5806 "Unable to find NC root for %s: %s. "
5807 "We need to move this object because parent object %s "
5808 "is deleted, but this object is not.",
5809 ldb_dn_get_linearized(msg
->dn
),
5810 ldb_errstring(ldb_module_get_ctx(ar
->module
)),
5811 ldb_dn_get_linearized(parent_msg
->dn
));
5812 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
5815 ret
= dsdb_wellknown_dn(ldb_module_get_ctx(ar
->module
), msg
,
5817 DS_GUID_LOSTANDFOUND_CONTAINER
,
5819 if (ret
!= LDB_SUCCESS
) {
5820 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5821 "Unable to find LostAndFound Container for %s "
5822 "in partition %s: %s. "
5823 "We need to move this object because parent object %s "
5824 "is deleted, but this object is not.",
5825 ldb_dn_get_linearized(msg
->dn
), ldb_dn_get_linearized(nc_root
),
5826 ldb_errstring(ldb_module_get_ctx(ar
->module
)),
5827 ldb_dn_get_linearized(parent_msg
->dn
));
5828 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
5830 ar
->objs
->objects
[ar
->index_current
].last_known_parent
5831 = talloc_steal(ar
->objs
->objects
[ar
->index_current
].msg
, parent_msg
->dn
);
5835 = talloc_steal(ar
->objs
->objects
[ar
->index_current
].msg
, parent_msg
->dn
);
5838 ar
->objs
->objects
[ar
->index_current
].local_parent_dn
= parent_dn
;
5840 comp_num
= ldb_dn_get_comp_num(msg
->dn
);
5842 if (!ldb_dn_remove_base_components(msg
->dn
, comp_num
- 1)) {
5844 return ldb_module_done(ar
->req
, NULL
, NULL
, ldb_module_operr(ar
->module
));
5847 if (!ldb_dn_add_base(msg
->dn
, parent_dn
)) {
5849 return ldb_module_done(ar
->req
, NULL
, NULL
, ldb_module_operr(ar
->module
));
5853 case LDB_REPLY_REFERRAL
:
5854 /* we ignore referrals */
5857 case LDB_REPLY_DONE
:
5859 if (ar
->objs
->objects
[ar
->index_current
].local_parent_dn
== NULL
) {
5860 struct GUID_txt_buf str_buf
;
5861 if (ar
->search_msg
!= NULL
) {
5862 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5863 "No parent with GUID %s found for object locally known as %s",
5864 GUID_buf_string(ar
->objs
->objects
[ar
->index_current
].parent_guid
, &str_buf
),
5865 ldb_dn_get_linearized(ar
->search_msg
->dn
));
5867 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5868 "No parent with GUID %s found for object remotely known as %s",
5869 GUID_buf_string(ar
->objs
->objects
[ar
->index_current
].parent_guid
, &str_buf
),
5870 ldb_dn_get_linearized(ar
->objs
->objects
[ar
->index_current
].msg
->dn
));
5874 * This error code is really important, as it
5875 * is the flag back to the callers to retry
5876 * this with DRSUAPI_DRS_GET_ANC, and so get
5877 * the parent objects before the child
5880 return ldb_module_done(ar
->req
, NULL
, NULL
,
5881 replmd_replicated_request_werror(ar
, WERR_DS_DRA_MISSING_PARENT
));
5884 if (ar
->search_msg
!= NULL
) {
5885 ret
= replmd_replicated_apply_merge(ar
);
5887 ret
= replmd_replicated_apply_add(ar
);
5889 if (ret
!= LDB_SUCCESS
) {
5890 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
5899 * Look for the parent object, so we put the new object in the right
5900 * place This is akin to NameObject in MS-DRSR - this routine and the
5901 * callbacks find the right parent name, and correct name for this
5905 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request
*ar
)
5907 struct ldb_context
*ldb
;
5911 struct ldb_request
*search_req
;
5912 static const char *attrs
[] = {"isDeleted", NULL
};
5913 struct GUID_txt_buf guid_str_buf
;
5915 ldb
= ldb_module_get_ctx(ar
->module
);
5917 if (ar
->objs
->objects
[ar
->index_current
].parent_guid
== NULL
) {
5918 if (ar
->search_msg
!= NULL
) {
5919 return replmd_replicated_apply_merge(ar
);
5921 return replmd_replicated_apply_add(ar
);
5925 tmp_str
= GUID_buf_string(ar
->objs
->objects
[ar
->index_current
].parent_guid
,
5928 filter
= talloc_asprintf(ar
, "(objectGUID=%s)", tmp_str
);
5929 if (!filter
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
5931 ret
= ldb_build_search_req(&search_req
,
5934 ar
->objs
->partition_dn
,
5940 replmd_replicated_apply_search_for_parent_callback
,
5942 LDB_REQ_SET_LOCATION(search_req
);
5944 ret
= dsdb_request_add_controls(search_req
,
5945 DSDB_SEARCH_SHOW_RECYCLED
|
5946 DSDB_SEARCH_SHOW_DELETED
|
5947 DSDB_SEARCH_SHOW_EXTENDED_DN
);
5948 if (ret
!= LDB_SUCCESS
) {
5952 return ldb_next_request(ar
->module
, search_req
);
5956 handle renames that come in over DRS replication
5958 static int replmd_replicated_handle_rename(struct replmd_replicated_request
*ar
,
5959 struct ldb_message
*msg
,
5960 struct ldb_request
*parent
,
5961 bool *renamed_to_conflict
)
5964 TALLOC_CTX
*tmp_ctx
= talloc_new(msg
);
5965 struct ldb_result
*res
;
5966 struct ldb_dn
*conflict_dn
;
5967 bool rename_incoming_record
;
5968 struct ldb_dn
*new_dn
;
5971 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5972 ldb_dn_get_linearized(ar
->search_msg
->dn
),
5973 ldb_dn_get_linearized(msg
->dn
)));
5976 ret
= dsdb_module_rename(ar
->module
, ar
->search_msg
->dn
, msg
->dn
,
5977 DSDB_FLAG_NEXT_MODULE
, ar
->req
);
5978 if (ret
== LDB_SUCCESS
) {
5979 talloc_free(tmp_ctx
);
5983 if (ret
!= LDB_ERR_ENTRY_ALREADY_EXISTS
) {
5984 talloc_free(tmp_ctx
);
5985 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
), "Failed to locally apply remote rename from %s to %s: %s",
5986 ldb_dn_get_linearized(ar
->search_msg
->dn
),
5987 ldb_dn_get_linearized(msg
->dn
),
5988 ldb_errstring(ldb_module_get_ctx(ar
->module
)));
5992 conflict_dn
= msg
->dn
;
5995 ret
= incoming_dn_should_be_renamed(tmp_ctx
, ar
, conflict_dn
, &res
,
5996 &rename_incoming_record
);
5997 if (ret
!= LDB_SUCCESS
) {
6001 if (rename_incoming_record
) {
6003 new_dn
= replmd_conflict_dn(msg
,
6004 ldb_module_get_ctx(ar
->module
),
6006 &ar
->objs
->objects
[ar
->index_current
].object_guid
);
6007 if (new_dn
== NULL
) {
6008 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
6009 "Failed to form conflict DN for %s\n",
6010 ldb_dn_get_linearized(msg
->dn
));
6012 talloc_free(tmp_ctx
);
6013 return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
6016 ret
= dsdb_module_rename(ar
->module
, ar
->search_msg
->dn
, new_dn
,
6017 DSDB_FLAG_NEXT_MODULE
, ar
->req
);
6018 if (ret
!= LDB_SUCCESS
) {
6019 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
6020 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
6021 ldb_dn_get_linearized(conflict_dn
),
6022 ldb_dn_get_linearized(ar
->search_msg
->dn
),
6023 ldb_dn_get_linearized(new_dn
),
6024 ldb_errstring(ldb_module_get_ctx(ar
->module
)));
6025 talloc_free(tmp_ctx
);
6026 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_DB_ERROR
);
6030 *renamed_to_conflict
= true;
6031 talloc_free(tmp_ctx
);
6035 /* we are renaming the existing record */
6037 guid
= samdb_result_guid(res
->msgs
[0], "objectGUID");
6038 if (GUID_all_zero(&guid
)) {
6039 DEBUG(0,(__location__
": Failed to find objectGUID for existing conflict record %s\n",
6040 ldb_dn_get_linearized(conflict_dn
)));
6044 new_dn
= replmd_conflict_dn(tmp_ctx
,
6045 ldb_module_get_ctx(ar
->module
),
6046 conflict_dn
, &guid
);
6047 if (new_dn
== NULL
) {
6048 DEBUG(0,(__location__
": Failed to form conflict DN for %s\n",
6049 ldb_dn_get_linearized(conflict_dn
)));
6053 DEBUG(2,(__location__
": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
6054 ldb_dn_get_linearized(conflict_dn
), ldb_dn_get_linearized(new_dn
)));
6056 ret
= dsdb_module_rename(ar
->module
, conflict_dn
, new_dn
,
6057 DSDB_FLAG_OWN_MODULE
, ar
->req
);
6058 if (ret
!= LDB_SUCCESS
) {
6059 DEBUG(0,(__location__
": Failed to rename conflict dn '%s' to '%s' - %s\n",
6060 ldb_dn_get_linearized(conflict_dn
),
6061 ldb_dn_get_linearized(new_dn
),
6062 ldb_errstring(ldb_module_get_ctx(ar
->module
))));
6067 * now we need to ensure that the rename is seen as an
6068 * originating update. We do that with a modify.
6070 ret
= replmd_name_modify(ar
, ar
->req
, new_dn
);
6071 if (ret
!= LDB_SUCCESS
) {
6075 DEBUG(2,(__location__
": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
6076 ldb_dn_get_linearized(ar
->search_msg
->dn
),
6077 ldb_dn_get_linearized(msg
->dn
)));
6080 * With the other record out of the way, do the rename we had
6083 ret
= dsdb_module_rename(ar
->module
, ar
->search_msg
->dn
, msg
->dn
,
6084 DSDB_FLAG_NEXT_MODULE
, ar
->req
);
6085 if (ret
!= LDB_SUCCESS
) {
6086 DEBUG(0,(__location__
": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
6087 ldb_dn_get_linearized(ar
->search_msg
->dn
),
6088 ldb_dn_get_linearized(msg
->dn
),
6089 ldb_errstring(ldb_module_get_ctx(ar
->module
))));
6093 talloc_free(tmp_ctx
);
6097 * On failure make the caller get the error
6098 * This means replication will stop with an error,
6099 * but there is not much else we can do. In the
6100 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
6103 if (ret
== LDB_SUCCESS
) {
6104 ret
= LDB_ERR_OPERATIONS_ERROR
;
6107 talloc_free(tmp_ctx
);
6112 static int replmd_replicated_apply_merge(struct replmd_replicated_request
*ar
)
6114 struct ldb_context
*ldb
;
6115 struct ldb_request
*change_req
;
6116 enum ndr_err_code ndr_err
;
6117 struct ldb_message
*msg
;
6118 struct replPropertyMetaDataBlob
*rmd
;
6119 struct replPropertyMetaDataBlob omd
;
6120 const struct ldb_val
*omd_value
;
6121 struct replPropertyMetaDataBlob nmd
;
6122 struct ldb_val nmd_value
;
6123 struct GUID remote_parent_guid
;
6126 unsigned int removed_attrs
= 0;
6128 int (*callback
)(struct ldb_request
*req
, struct ldb_reply
*ares
) = replmd_op_callback
;
6129 bool isDeleted
= false;
6130 bool local_isDeleted
= false;
6131 bool remote_isDeleted
= false;
6132 bool take_remote_isDeleted
= false;
6133 bool sd_updated
= false;
6134 bool renamed
= false;
6135 bool renamed_to_conflict
= false;
6136 bool is_schema_nc
= false;
6138 const struct ldb_val
*old_rdn
, *new_rdn
;
6139 struct replmd_private
*replmd_private
=
6140 talloc_get_type(ldb_module_get_private(ar
->module
),
6141 struct replmd_private
);
6143 time_t t
= time(NULL
);
6144 unix_to_nt_time(&now
, t
);
6146 ldb
= ldb_module_get_ctx(ar
->module
);
6147 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
6149 is_schema_nc
= ldb_dn_compare_base(replmd_private
->schema_dn
, msg
->dn
) == 0;
6151 rmd
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
6155 /* find existing meta data */
6156 omd_value
= ldb_msg_find_ldb_val(ar
->search_msg
, "replPropertyMetaData");
6158 ndr_err
= ndr_pull_struct_blob(omd_value
, ar
, &omd
,
6159 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
6160 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
6161 nt_status
= ndr_map_error2ntstatus(ndr_err
);
6162 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
6165 if (omd
.version
!= 1) {
6166 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
6171 struct GUID_txt_buf guid_txt
;
6173 char *s
= ldb_ldif_message_redacted_string(ldb
, ar
,
6174 LDB_CHANGETYPE_MODIFY
, msg
);
6175 DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
6178 GUID_buf_string(&ar
->objs
->objects
[ar
->index_current
].object_guid
, &guid_txt
),
6180 ndr_print_struct_string(s
,
6181 (ndr_print_fn_t
)ndr_print_replPropertyMetaDataBlob
,
6182 "existing replPropertyMetaData",
6184 ndr_print_struct_string(s
,
6185 (ndr_print_fn_t
)ndr_print_replPropertyMetaDataBlob
,
6186 "incoming replPropertyMetaData",
6189 } else if (DEBUGLVL(4)) {
6190 struct GUID_txt_buf guid_txt
;
6192 DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
6193 GUID_buf_string(&ar
->objs
->objects
[ar
->index_current
].object_guid
,
6195 ldb_dn_get_linearized(msg
->dn
)));
6198 local_isDeleted
= ldb_msg_find_attr_as_bool(ar
->search_msg
,
6199 "isDeleted", false);
6200 remote_isDeleted
= ldb_msg_find_attr_as_bool(msg
,
6201 "isDeleted", false);
6204 * Fill in the remote_parent_guid with the GUID or an all-zero
6207 if (ar
->objs
->objects
[ar
->index_current
].parent_guid
!= NULL
) {
6208 remote_parent_guid
= *ar
->objs
->objects
[ar
->index_current
].parent_guid
;
6210 remote_parent_guid
= GUID_zero();
6214 * To ensure we follow a complex rename chain around, we have
6215 * to confirm that the DN is the same (mostly to confirm the
6216 * RDN) and the parentGUID is the same.
6218 * This ensures we keep things under the correct parent, which
6219 * replmd_replicated_handle_rename() will do.
6222 if (strcmp(ldb_dn_get_linearized(msg
->dn
), ldb_dn_get_linearized(ar
->search_msg
->dn
)) == 0
6223 && GUID_equal(&remote_parent_guid
, &ar
->local_parent_guid
)) {
6227 * handle renames, even just by case that come in over
6228 * DRS. Changes in the parent DN don't hit us here,
6229 * because the search for a parent will clean up those
6232 * We also have already filtered out the case where
6233 * the peer has an older name to what we have (see
6234 * replmd_replicated_apply_search_callback())
6236 ret
= replmd_replicated_handle_rename(ar
, msg
, ar
->req
, &renamed_to_conflict
);
6239 * This looks strange, but we must set this after any
6240 * rename, otherwise the SD propegation will not
6241 * happen (which might matter if we have a new parent)
6243 * The additional case of calling
6244 * replmd_op_name_modify_callback (below) is
6245 * controlled by renamed_to_conflict.
6250 if (ret
!= LDB_SUCCESS
) {
6251 ldb_debug(ldb
, LDB_DEBUG_FATAL
,
6252 "replmd_replicated_request rename %s => %s failed - %s\n",
6253 ldb_dn_get_linearized(ar
->search_msg
->dn
),
6254 ldb_dn_get_linearized(msg
->dn
),
6255 ldb_errstring(ldb
));
6256 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_DB_ERROR
);
6259 if (renamed_to_conflict
== true) {
6261 * Set the callback to one that will fix up the name
6262 * metadata on the new conflict DN
6264 callback
= replmd_op_name_modify_callback
;
6269 nmd
.ctr
.ctr1
.count
= omd
.ctr
.ctr1
.count
+ rmd
->ctr
.ctr1
.count
;
6270 nmd
.ctr
.ctr1
.array
= talloc_array(ar
,
6271 struct replPropertyMetaData1
,
6272 nmd
.ctr
.ctr1
.count
);
6273 if (!nmd
.ctr
.ctr1
.array
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
6275 /* first copy the old meta data */
6276 for (i
=0; i
< omd
.ctr
.ctr1
.count
; i
++) {
6277 nmd
.ctr
.ctr1
.array
[ni
] = omd
.ctr
.ctr1
.array
[i
];
6282 /* now merge in the new meta data */
6283 for (i
=0; i
< rmd
->ctr
.ctr1
.count
; i
++) {
6286 for (j
=0; j
< ni
; j
++) {
6289 if (rmd
->ctr
.ctr1
.array
[i
].attid
!= nmd
.ctr
.ctr1
.array
[j
].attid
) {
6293 cmp
= replmd_replPropertyMetaData1_new_should_be_taken(
6294 ar
->objs
->dsdb_repl_flags
,
6295 &nmd
.ctr
.ctr1
.array
[j
],
6296 &rmd
->ctr
.ctr1
.array
[i
]);
6298 /* replace the entry */
6299 nmd
.ctr
.ctr1
.array
[j
] = rmd
->ctr
.ctr1
.array
[i
];
6300 if (ar
->seq_num
== 0) {
6301 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &ar
->seq_num
);
6302 if (ret
!= LDB_SUCCESS
) {
6303 return replmd_replicated_request_error(ar
, ret
);
6306 nmd
.ctr
.ctr1
.array
[j
].local_usn
= ar
->seq_num
;
6307 switch (nmd
.ctr
.ctr1
.array
[j
].attid
) {
6308 case DRSUAPI_ATTID_ntSecurityDescriptor
:
6311 case DRSUAPI_ATTID_isDeleted
:
6312 take_remote_isDeleted
= true;
6321 if (rmd
->ctr
.ctr1
.array
[i
].attid
!= DRSUAPI_ATTID_instanceType
) {
6322 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
6323 msg
->elements
[i
-removed_attrs
].name
,
6324 ldb_dn_get_linearized(msg
->dn
),
6325 GUID_string(ar
, &rmd
->ctr
.ctr1
.array
[i
].originating_invocation_id
)));
6328 /* we don't want to apply this change so remove the attribute */
6329 ldb_msg_remove_element(msg
, &msg
->elements
[i
-removed_attrs
]);
6336 if (found
) continue;
6338 nmd
.ctr
.ctr1
.array
[ni
] = rmd
->ctr
.ctr1
.array
[i
];
6339 if (ar
->seq_num
== 0) {
6340 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &ar
->seq_num
);
6341 if (ret
!= LDB_SUCCESS
) {
6342 return replmd_replicated_request_error(ar
, ret
);
6345 nmd
.ctr
.ctr1
.array
[ni
].local_usn
= ar
->seq_num
;
6346 switch (nmd
.ctr
.ctr1
.array
[ni
].attid
) {
6347 case DRSUAPI_ATTID_ntSecurityDescriptor
:
6350 case DRSUAPI_ATTID_isDeleted
:
6351 take_remote_isDeleted
= true;
6360 * finally correct the size of the meta_data array
6362 nmd
.ctr
.ctr1
.count
= ni
;
6364 new_rdn
= ldb_dn_get_rdn_val(msg
->dn
);
6365 old_rdn
= ldb_dn_get_rdn_val(ar
->search_msg
->dn
);
6368 ret
= replmd_update_rpmd_rdn_attr(ldb
, msg
, new_rdn
, old_rdn
,
6369 &nmd
, ar
, now
, is_schema_nc
,
6371 if (ret
!= LDB_SUCCESS
) {
6372 ldb_asprintf_errstring(ldb
, "%s: error during DRS repl merge: %s", __func__
, ldb_errstring(ldb
));
6373 return replmd_replicated_request_error(ar
, ret
);
6377 * sort the new meta data array
6379 ret
= replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb
, &nmd
.ctr
.ctr1
, msg
->dn
);
6380 if (ret
!= LDB_SUCCESS
) {
6381 ldb_asprintf_errstring(ldb
, "%s: error during DRS repl merge: %s", __func__
, ldb_errstring(ldb
));
6386 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
6389 * This also controls SD propagation below
6391 if (take_remote_isDeleted
) {
6392 isDeleted
= remote_isDeleted
;
6394 isDeleted
= local_isDeleted
;
6397 ar
->isDeleted
= isDeleted
;
6400 * check if some replicated attributes left, otherwise skip the ldb_modify() call
6402 if (msg
->num_elements
== 0) {
6403 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_replicated_apply_merge[%u]: skip replace\n",
6406 return replmd_replicated_apply_isDeleted(ar
);
6409 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
6410 ar
->index_current
, msg
->num_elements
);
6414 * This is an new name for this object, so we must
6415 * inherit from the parent
6417 * This is needed because descriptor is above
6418 * repl_meta_data in the module stack, so this will
6419 * not be trigered 'naturally' by the flow of
6422 ret
= dsdb_module_schedule_sd_propagation(ar
->module
,
6423 ar
->objs
->partition_dn
,
6424 ar
->objs
->objects
[ar
->index_current
].object_guid
,
6425 ar
->objs
->objects
[ar
->index_current
].parent_guid
?
6426 *ar
->objs
->objects
[ar
->index_current
].parent_guid
:
6429 if (ret
!= LDB_SUCCESS
) {
6430 return ldb_operr(ldb
);
6434 if (sd_updated
&& !isDeleted
) {
6436 * This is an existing object, so there is no need to
6437 * inherit from the parent, but we must inherit any
6438 * incoming changes to our child objects.
6440 * This is needed because descriptor is above
6441 * repl_meta_data in the module stack, so this will
6442 * not be trigered 'naturally' by the flow of
6445 ret
= dsdb_module_schedule_sd_propagation(ar
->module
,
6446 ar
->objs
->partition_dn
,
6447 ar
->objs
->objects
[ar
->index_current
].object_guid
,
6448 ar
->objs
->objects
[ar
->index_current
].parent_guid
?
6449 *ar
->objs
->objects
[ar
->index_current
].parent_guid
:
6452 if (ret
!= LDB_SUCCESS
) {
6453 return ldb_operr(ldb
);
6457 /* create the meta data value */
6458 ndr_err
= ndr_push_struct_blob(&nmd_value
, msg
, &nmd
,
6459 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
6460 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
6461 nt_status
= ndr_map_error2ntstatus(ndr_err
);
6462 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
6466 * when we know that we'll modify the record, add the whenChanged, uSNChanged
6467 * and replPopertyMetaData attributes
6469 ret
= ldb_msg_add_string(msg
, "whenChanged", ar
->objs
->objects
[ar
->index_current
].when_changed
);
6470 if (ret
!= LDB_SUCCESS
) {
6471 return replmd_replicated_request_error(ar
, ret
);
6473 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNChanged", ar
->seq_num
);
6474 if (ret
!= LDB_SUCCESS
) {
6475 return replmd_replicated_request_error(ar
, ret
);
6477 ret
= ldb_msg_add_value(msg
, "replPropertyMetaData", &nmd_value
, NULL
);
6478 if (ret
!= LDB_SUCCESS
) {
6479 return replmd_replicated_request_error(ar
, ret
);
6482 replmd_ldb_message_sort(msg
, ar
->schema
);
6484 /* we want to replace the old values */
6485 for (i
=0; i
< msg
->num_elements
; i
++) {
6486 msg
->elements
[i
].flags
= LDB_FLAG_MOD_REPLACE
;
6487 if (ldb_attr_cmp(msg
->elements
[i
].name
, "objectClass") == 0) {
6488 if (msg
->elements
[i
].num_values
== 0) {
6489 ldb_asprintf_errstring(ldb
, __location__
6490 ": objectClass removed on %s, aborting replication\n",
6491 ldb_dn_get_linearized(msg
->dn
));
6492 return replmd_replicated_request_error(ar
, LDB_ERR_OBJECT_CLASS_VIOLATION
);
6498 struct GUID_txt_buf guid_txt
;
6500 char *s
= ldb_ldif_message_redacted_string(ldb
, ar
,
6501 LDB_CHANGETYPE_MODIFY
,
6503 DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
6504 GUID_buf_string(&ar
->objs
->objects
[ar
->index_current
].object_guid
,
6508 } else if (DEBUGLVL(4)) {
6509 struct GUID_txt_buf guid_txt
;
6511 DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
6512 GUID_buf_string(&ar
->objs
->objects
[ar
->index_current
].object_guid
,
6514 ldb_dn_get_linearized(msg
->dn
)));
6517 ret
= ldb_build_mod_req(&change_req
,
6525 LDB_REQ_SET_LOCATION(change_req
);
6526 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
6528 /* current partition control needed by "repmd_op_callback" */
6529 ret
= ldb_request_add_control(change_req
,
6530 DSDB_CONTROL_CURRENT_PARTITION_OID
,
6532 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
6534 return ldb_next_request(ar
->module
, change_req
);
6537 static int replmd_replicated_apply_search_callback(struct ldb_request
*req
,
6538 struct ldb_reply
*ares
)
6540 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
6541 struct replmd_replicated_request
);
6545 return ldb_module_done(ar
->req
, NULL
, NULL
,
6546 LDB_ERR_OPERATIONS_ERROR
);
6548 if (ares
->error
!= LDB_SUCCESS
&&
6549 ares
->error
!= LDB_ERR_NO_SUCH_OBJECT
) {
6550 return ldb_module_done(ar
->req
, ares
->controls
,
6551 ares
->response
, ares
->error
);
6554 switch (ares
->type
) {
6555 case LDB_REPLY_ENTRY
:
6556 ar
->search_msg
= talloc_steal(ar
, ares
->message
);
6559 case LDB_REPLY_REFERRAL
:
6560 /* we ignore referrals */
6563 case LDB_REPLY_DONE
:
6565 struct replPropertyMetaData1
*md_remote
;
6566 struct replPropertyMetaData1
*md_local
;
6568 struct replPropertyMetaDataBlob omd
;
6569 const struct ldb_val
*omd_value
;
6570 struct replPropertyMetaDataBlob
*rmd
;
6571 struct ldb_message
*msg
;
6573 ar
->objs
->objects
[ar
->index_current
].local_parent_dn
= NULL
;
6574 ar
->objs
->objects
[ar
->index_current
].last_known_parent
= NULL
;
6577 * This is the ADD case, find the appropriate parent,
6578 * as this object doesn't exist locally:
6580 if (ar
->search_msg
== NULL
) {
6581 ret
= replmd_replicated_apply_search_for_parent(ar
);
6582 if (ret
!= LDB_SUCCESS
) {
6583 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
6590 * Otherwise, in the MERGE case, work out if we are
6591 * attempting a rename, and if so find the parent the
6592 * newly renamed object wants to belong under (which
6593 * may not be the parent in it's attached string DN
6595 rmd
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
6599 /* find existing meta data */
6600 omd_value
= ldb_msg_find_ldb_val(ar
->search_msg
, "replPropertyMetaData");
6602 enum ndr_err_code ndr_err
;
6603 ndr_err
= ndr_pull_struct_blob(omd_value
, ar
, &omd
,
6604 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
6605 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
6606 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
6607 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
6610 if (omd
.version
!= 1) {
6611 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
6615 ar
->local_parent_guid
= samdb_result_guid(ar
->search_msg
, "parentGUID");
6617 instanceType
= ldb_msg_find_attr_as_int(ar
->search_msg
, "instanceType", 0);
6618 if (((instanceType
& INSTANCE_TYPE_IS_NC_HEAD
) == 0)
6619 && GUID_all_zero(&ar
->local_parent_guid
)) {
6620 DEBUG(0, ("Refusing to replicate new version of %s "
6621 "as local object has an all-zero parentGUID attribute, "
6622 "despite not being an NC root\n",
6623 ldb_dn_get_linearized(ar
->search_msg
->dn
)));
6624 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
6628 * now we need to check for double renames. We could have a
6629 * local rename pending which our replication partner hasn't
6630 * received yet. We choose which one wins by looking at the
6631 * attribute stamps on the two objects, the newer one wins.
6633 * This also simply applies the correct algorithms for
6634 * determining if a change was made to name at all, or
6635 * if the object has just been renamed under the same
6638 md_remote
= replmd_replPropertyMetaData1_find_attid(rmd
, DRSUAPI_ATTID_name
);
6639 md_local
= replmd_replPropertyMetaData1_find_attid(&omd
, DRSUAPI_ATTID_name
);
6641 DEBUG(0,(__location__
": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6642 ldb_dn_get_linearized(ar
->search_msg
->dn
)));
6643 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_DB_ERROR
);
6647 * if there is no name attribute given then we have to assume the
6648 * object we've received has the older name
6650 if (replmd_replPropertyMetaData1_new_should_be_taken(
6651 ar
->objs
->dsdb_repl_flags
& DSDB_REPL_FLAG_PRIORITISE_INCOMING
,
6652 md_local
, md_remote
)) {
6653 struct GUID_txt_buf p_guid_local
;
6654 struct GUID_txt_buf p_guid_remote
;
6655 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
6657 /* Merge on the existing object, with rename */
6659 DEBUG(4,(__location__
": Looking for new parent for object %s currently under %s "
6660 "as incoming object changing to %s under %s\n",
6661 ldb_dn_get_linearized(ar
->search_msg
->dn
),
6662 GUID_buf_string(&ar
->local_parent_guid
, &p_guid_local
),
6663 ldb_dn_get_linearized(msg
->dn
),
6664 GUID_buf_string(ar
->objs
->objects
[ar
->index_current
].parent_guid
,
6666 ret
= replmd_replicated_apply_search_for_parent(ar
);
6668 struct GUID_txt_buf p_guid_local
;
6669 struct GUID_txt_buf p_guid_remote
;
6670 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
6673 * Merge on the existing object, force no
6674 * rename (code below just to explain why in
6678 if (strcmp(ldb_dn_get_linearized(ar
->search_msg
->dn
),
6679 ldb_dn_get_linearized(msg
->dn
)) == 0) {
6680 if (ar
->objs
->objects
[ar
->index_current
].parent_guid
!= NULL
&&
6681 GUID_equal(&ar
->local_parent_guid
,
6682 ar
->objs
->objects
[ar
->index_current
].parent_guid
)
6684 DEBUG(4,(__location__
": Keeping object %s at under %s "
6685 "despite incoming object changing parent to %s\n",
6686 ldb_dn_get_linearized(ar
->search_msg
->dn
),
6687 GUID_buf_string(&ar
->local_parent_guid
, &p_guid_local
),
6688 GUID_buf_string(ar
->objs
->objects
[ar
->index_current
].parent_guid
,
6692 DEBUG(4,(__location__
": Keeping object %s at under %s "
6693 " and rejecting older rename to %s under %s\n",
6694 ldb_dn_get_linearized(ar
->search_msg
->dn
),
6695 GUID_buf_string(&ar
->local_parent_guid
, &p_guid_local
),
6696 ldb_dn_get_linearized(msg
->dn
),
6697 GUID_buf_string(ar
->objs
->objects
[ar
->index_current
].parent_guid
,
6701 * This assignment ensures that the strcmp()
6702 * and GUID_equal() calls in
6703 * replmd_replicated_apply_merge() avoids the
6706 ar
->objs
->objects
[ar
->index_current
].parent_guid
=
6707 &ar
->local_parent_guid
;
6709 msg
->dn
= ar
->search_msg
->dn
;
6710 ret
= replmd_replicated_apply_merge(ar
);
6712 if (ret
!= LDB_SUCCESS
) {
6713 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
6723 * Returns true if we can group together processing this link attribute,
6724 * i.e. it has the same source-object and attribute ID as other links
6725 * already in the group
6727 static bool la_entry_matches_group(struct la_entry
*la_entry
,
6728 struct la_group
*la_group
)
6730 struct la_entry
*prev
= la_group
->la_entries
;
6732 return (la_entry
->la
->attid
== prev
->la
->attid
&&
6733 GUID_equal(&la_entry
->la
->identifier
->guid
,
6734 &prev
->la
->identifier
->guid
));
6738 * Creates a new la_entry to store replication info for a single
6741 static struct la_entry
*
6742 create_la_entry(struct replmd_private
*replmd_private
,
6743 struct drsuapi_DsReplicaLinkedAttribute
*la
,
6744 uint32_t dsdb_repl_flags
)
6746 struct la_entry
*la_entry
;
6748 if (replmd_private
->la_ctx
== NULL
) {
6749 replmd_private
->la_ctx
= talloc_new(replmd_private
);
6751 la_entry
= talloc(replmd_private
->la_ctx
, struct la_entry
);
6752 if (la_entry
== NULL
) {
6755 la_entry
->la
= talloc(la_entry
,
6756 struct drsuapi_DsReplicaLinkedAttribute
);
6757 if (la_entry
->la
== NULL
) {
6758 talloc_free(la_entry
);
6761 *la_entry
->la
= *la
;
6762 la_entry
->dsdb_repl_flags
= dsdb_repl_flags
;
6765 * we need to steal the non-scalars so they stay
6766 * around until the end of the transaction
6768 talloc_steal(la_entry
->la
, la_entry
->la
->identifier
);
6769 talloc_steal(la_entry
->la
, la_entry
->la
->value
.blob
);
6775 * Stores the linked attributes received in the replication chunk - these get
6776 * applied at the end of the transaction. We also check that each linked
6777 * attribute is valid, i.e. source and target objects are known.
6779 static int replmd_store_linked_attributes(struct replmd_replicated_request
*ar
)
6781 int ret
= LDB_SUCCESS
;
6783 struct ldb_module
*module
= ar
->module
;
6784 struct replmd_private
*replmd_private
=
6785 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
6786 struct la_group
*la_group
= NULL
;
6787 struct ldb_context
*ldb
;
6788 TALLOC_CTX
*tmp_ctx
= NULL
;
6789 struct ldb_message
*src_msg
= NULL
;
6790 const struct dsdb_attribute
*attr
= NULL
;
6792 ldb
= ldb_module_get_ctx(module
);
6794 DEBUG(4,("linked_attributes_count=%u\n", ar
->objs
->linked_attributes_count
));
6796 /* save away the linked attributes for the end of the transaction */
6797 for (i
= 0; i
< ar
->objs
->linked_attributes_count
; i
++) {
6798 struct la_entry
*la_entry
;
6801 /* create an entry to store the received link attribute info */
6802 la_entry
= create_la_entry(replmd_private
,
6803 &ar
->objs
->linked_attributes
[i
],
6804 ar
->objs
->dsdb_repl_flags
);
6805 if (la_entry
== NULL
) {
6807 return LDB_ERR_OPERATIONS_ERROR
;
6811 * check if we're still dealing with the same source object
6814 new_srcobj
= (la_group
== NULL
||
6815 !la_entry_matches_group(la_entry
, la_group
));
6819 /* get a new mem_ctx to lookup the source object */
6820 TALLOC_FREE(tmp_ctx
);
6821 tmp_ctx
= talloc_new(ar
);
6822 if (tmp_ctx
== NULL
) {
6824 return LDB_ERR_OPERATIONS_ERROR
;
6827 /* verify the link source exists */
6828 ret
= replmd_get_la_entry_source(module
, la_entry
,
6833 * When we fail to find the source object, the error
6834 * code we pass back here is really important. It flags
6835 * back to the callers to retry this request with
6836 * DRSUAPI_DRS_GET_ANC. This case should never happen
6837 * if we're replicating from a Samba DC, but it is
6838 * needed to talk to a Windows DC
6840 if (ret
== LDB_ERR_NO_SUCH_OBJECT
) {
6841 WERROR err
= WERR_DS_DRA_MISSING_PARENT
;
6842 ret
= replmd_replicated_request_werror(ar
,
6848 ret
= replmd_verify_link_target(ar
, tmp_ctx
, la_entry
,
6850 if (ret
!= LDB_SUCCESS
) {
6854 /* group the links together by source-object for efficiency */
6856 la_group
= talloc_zero(replmd_private
->la_ctx
,
6858 if (la_group
== NULL
) {
6860 return LDB_ERR_OPERATIONS_ERROR
;
6862 DLIST_ADD(replmd_private
->la_list
, la_group
);
6864 DLIST_ADD(la_group
->la_entries
, la_entry
);
6865 replmd_private
->total_links
++;
6868 TALLOC_FREE(tmp_ctx
);
6872 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request
*ar
);
6874 static int replmd_replicated_apply_next(struct replmd_replicated_request
*ar
)
6876 struct ldb_context
*ldb
;
6880 struct ldb_request
*search_req
;
6881 static const char *attrs
[] = { "repsFrom", "replUpToDateVector",
6882 "parentGUID", "instanceType",
6883 "replPropertyMetaData", "nTSecurityDescriptor",
6884 "isDeleted", NULL
};
6885 struct GUID_txt_buf guid_str_buf
;
6887 if (ar
->index_current
>= ar
->objs
->num_objects
) {
6890 * Now that we've applied all the objects, check the new linked
6891 * attributes and store them (we apply them in .prepare_commit)
6893 ret
= replmd_store_linked_attributes(ar
);
6895 if (ret
!= LDB_SUCCESS
) {
6899 /* done applying objects, move on to the next stage */
6900 return replmd_replicated_uptodate_vector(ar
);
6903 ldb
= ldb_module_get_ctx(ar
->module
);
6904 ar
->search_msg
= NULL
;
6905 ar
->isDeleted
= false;
6907 tmp_str
= GUID_buf_string(&ar
->objs
->objects
[ar
->index_current
].object_guid
,
6910 filter
= talloc_asprintf(ar
, "(objectGUID=%s)", tmp_str
);
6911 if (!filter
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
6913 ret
= ldb_build_search_req(&search_req
,
6916 ar
->objs
->partition_dn
,
6922 replmd_replicated_apply_search_callback
,
6924 LDB_REQ_SET_LOCATION(search_req
);
6927 * We set DSDB_SEARCH_SHOW_EXTENDED_DN to get the GUID on the
6928 * DN. This in turn helps our operational module find the
6929 * record by GUID, not DN lookup which is more error prone if
6930 * DN indexing changes. We prefer to keep chasing GUIDs
6931 * around if possible, even within a transaction.
6933 * The aim here is to keep replication moving and allow a
6936 ret
= dsdb_request_add_controls(search_req
, DSDB_SEARCH_SHOW_RECYCLED
6937 |DSDB_SEARCH_SHOW_EXTENDED_DN
);
6939 if (ret
!= LDB_SUCCESS
) {
6943 return ldb_next_request(ar
->module
, search_req
);
6947 * Returns true if we need to do extra processing to handle deleted object
6948 * changes received via replication
6950 static bool replmd_should_apply_isDeleted(struct replmd_replicated_request
*ar
,
6951 struct ldb_message
*msg
)
6953 struct ldb_dn
*deleted_objects_dn
;
6956 if (!ar
->isDeleted
) {
6958 /* not a deleted object, so don't set isDeleted */
6962 ret
= dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar
->module
),
6964 &deleted_objects_dn
);
6967 * if the Deleted Object container lookup failed, then just apply
6968 * isDeleted (note that it doesn't exist for the Schema partition)
6970 if (ret
!= LDB_SUCCESS
) {
6975 * the Deleted Objects container has isDeleted set but is not entirely
6976 * a deleted object, so DON'T re-apply isDeleted to it
6978 if (ldb_dn_compare(msg
->dn
, deleted_objects_dn
) == 0) {
6986 * This is essentially a wrapper for replmd_replicated_apply_next()
6988 * This is needed to ensure that both codepaths call this handler.
6990 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request
*ar
)
6992 struct ldb_message
*msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
6994 bool apply_isDeleted
;
6995 struct ldb_request
*del_req
= NULL
;
6996 struct ldb_result
*res
= NULL
;
6997 TALLOC_CTX
*tmp_ctx
= NULL
;
6999 apply_isDeleted
= replmd_should_apply_isDeleted(ar
, msg
);
7001 if (!apply_isDeleted
) {
7004 ar
->index_current
++;
7005 return replmd_replicated_apply_next(ar
);
7009 * Do a delete here again, so that if there is
7010 * anything local that conflicts with this
7011 * object being deleted, it is removed. This
7012 * includes links. See MS-DRSR 4.1.10.6.9
7015 * If the object is already deleted, and there
7016 * is no more work required, it doesn't do
7020 /* This has been updated to point to the DN we eventually did the modify on */
7022 tmp_ctx
= talloc_new(ar
);
7024 ret
= ldb_oom(ldb_module_get_ctx(ar
->module
));
7028 res
= talloc_zero(tmp_ctx
, struct ldb_result
);
7030 ret
= ldb_oom(ldb_module_get_ctx(ar
->module
));
7031 talloc_free(tmp_ctx
);
7035 /* Build a delete request, which hopefully will artually turn into nothing */
7036 ret
= ldb_build_del_req(&del_req
, ldb_module_get_ctx(ar
->module
), tmp_ctx
,
7040 ldb_modify_default_callback
,
7042 LDB_REQ_SET_LOCATION(del_req
);
7043 if (ret
!= LDB_SUCCESS
) {
7044 talloc_free(tmp_ctx
);
7049 * This is the guts of the call, call back
7050 * into our delete code, but setting the
7051 * re_delete flag so we delete anything that
7052 * shouldn't be there on a deleted or recycled
7055 ret
= replmd_delete_internals(ar
->module
, del_req
, true);
7056 if (ret
== LDB_SUCCESS
) {
7057 ret
= ldb_wait(del_req
->handle
, LDB_WAIT_ALL
);
7060 talloc_free(tmp_ctx
);
7061 if (ret
!= LDB_SUCCESS
) {
7065 ar
->index_current
++;
7066 return replmd_replicated_apply_next(ar
);
7069 static int replmd_replicated_uptodate_modify_callback(struct ldb_request
*req
,
7070 struct ldb_reply
*ares
)
7072 struct ldb_context
*ldb
;
7073 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
7074 struct replmd_replicated_request
);
7075 ldb
= ldb_module_get_ctx(ar
->module
);
7078 return ldb_module_done(ar
->req
, NULL
, NULL
,
7079 LDB_ERR_OPERATIONS_ERROR
);
7081 if (ares
->error
!= LDB_SUCCESS
) {
7082 return ldb_module_done(ar
->req
, ares
->controls
,
7083 ares
->response
, ares
->error
);
7086 if (ares
->type
!= LDB_REPLY_DONE
) {
7087 ldb_asprintf_errstring(ldb
, "Invalid LDB reply type %d", ares
->type
);
7088 return ldb_module_done(ar
->req
, NULL
, NULL
,
7089 LDB_ERR_OPERATIONS_ERROR
);
7094 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_SUCCESS
);
7097 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request
*ar
)
7099 struct ldb_context
*ldb
;
7100 struct ldb_request
*change_req
;
7101 enum ndr_err_code ndr_err
;
7102 struct ldb_message
*msg
;
7103 struct replUpToDateVectorBlob ouv
;
7104 const struct ldb_val
*ouv_value
;
7105 const struct drsuapi_DsReplicaCursor2CtrEx
*ruv
;
7106 struct replUpToDateVectorBlob nuv
;
7107 struct ldb_val nuv_value
;
7108 struct ldb_message_element
*nuv_el
= NULL
;
7109 struct ldb_message_element
*orf_el
= NULL
;
7110 struct repsFromToBlob nrf
;
7111 struct ldb_val
*nrf_value
= NULL
;
7112 struct ldb_message_element
*nrf_el
= NULL
;
7116 time_t t
= time(NULL
);
7119 uint32_t instanceType
;
7121 ldb
= ldb_module_get_ctx(ar
->module
);
7122 ruv
= ar
->objs
->uptodateness_vector
;
7128 unix_to_nt_time(&now
, t
);
7130 if (ar
->search_msg
== NULL
) {
7131 /* this happens for a REPL_OBJ call where we are
7132 creating the target object by replicating it. The
7133 subdomain join code does this for the partition DN
7135 DEBUG(4,(__location__
": Skipping UDV and repsFrom update as no target DN\n"));
7136 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_SUCCESS
);
7139 instanceType
= ldb_msg_find_attr_as_uint(ar
->search_msg
, "instanceType", 0);
7140 if (! (instanceType
& INSTANCE_TYPE_IS_NC_HEAD
)) {
7141 DEBUG(4,(__location__
": Skipping UDV and repsFrom update as not NC root: %s\n",
7142 ldb_dn_get_linearized(ar
->search_msg
->dn
)));
7143 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_SUCCESS
);
7147 * first create the new replUpToDateVector
7149 ouv_value
= ldb_msg_find_ldb_val(ar
->search_msg
, "replUpToDateVector");
7151 ndr_err
= ndr_pull_struct_blob(ouv_value
, ar
, &ouv
,
7152 (ndr_pull_flags_fn_t
)ndr_pull_replUpToDateVectorBlob
);
7153 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
7154 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
7155 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
7158 if (ouv
.version
!= 2) {
7159 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
7164 * the new uptodateness vector will at least
7165 * contain 1 entry, one for the source_dsa
7167 * plus optional values from our old vector and the one from the source_dsa
7169 nuv
.ctr
.ctr2
.count
= ouv
.ctr
.ctr2
.count
;
7170 if (ruv
) nuv
.ctr
.ctr2
.count
+= ruv
->count
;
7171 nuv
.ctr
.ctr2
.cursors
= talloc_array(ar
,
7172 struct drsuapi_DsReplicaCursor2
,
7173 nuv
.ctr
.ctr2
.count
);
7174 if (!nuv
.ctr
.ctr2
.cursors
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
7176 /* first copy the old vector */
7177 for (i
=0; i
< ouv
.ctr
.ctr2
.count
; i
++) {
7178 nuv
.ctr
.ctr2
.cursors
[ni
] = ouv
.ctr
.ctr2
.cursors
[i
];
7182 /* merge in the source_dsa vector is available */
7183 for (i
=0; (ruv
&& i
< ruv
->count
); i
++) {
7186 if (GUID_equal(&ruv
->cursors
[i
].source_dsa_invocation_id
,
7187 &ar
->our_invocation_id
)) {
7191 for (j
=0; j
< ni
; j
++) {
7192 if (!GUID_equal(&ruv
->cursors
[i
].source_dsa_invocation_id
,
7193 &nuv
.ctr
.ctr2
.cursors
[j
].source_dsa_invocation_id
)) {
7199 if (ruv
->cursors
[i
].highest_usn
> nuv
.ctr
.ctr2
.cursors
[j
].highest_usn
) {
7200 nuv
.ctr
.ctr2
.cursors
[j
] = ruv
->cursors
[i
];
7205 if (found
) continue;
7207 /* if it's not there yet, add it */
7208 nuv
.ctr
.ctr2
.cursors
[ni
] = ruv
->cursors
[i
];
7213 * finally correct the size of the cursors array
7215 nuv
.ctr
.ctr2
.count
= ni
;
7220 TYPESAFE_QSORT(nuv
.ctr
.ctr2
.cursors
, nuv
.ctr
.ctr2
.count
, drsuapi_DsReplicaCursor2_compare
);
7223 * create the change ldb_message
7225 msg
= ldb_msg_new(ar
);
7226 if (!msg
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
7227 msg
->dn
= ar
->search_msg
->dn
;
7229 ndr_err
= ndr_push_struct_blob(&nuv_value
, msg
, &nuv
,
7230 (ndr_push_flags_fn_t
)ndr_push_replUpToDateVectorBlob
);
7231 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
7232 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
7233 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
7235 ret
= ldb_msg_add_value(msg
, "replUpToDateVector", &nuv_value
, &nuv_el
);
7236 if (ret
!= LDB_SUCCESS
) {
7237 return replmd_replicated_request_error(ar
, ret
);
7239 nuv_el
->flags
= LDB_FLAG_MOD_REPLACE
;
7242 * now create the new repsFrom value from the given repsFromTo1 structure
7246 nrf
.ctr
.ctr1
= *ar
->objs
->source_dsa
;
7247 nrf
.ctr
.ctr1
.last_attempt
= now
;
7248 nrf
.ctr
.ctr1
.last_success
= now
;
7249 nrf
.ctr
.ctr1
.result_last_attempt
= WERR_OK
;
7252 * first see if we already have a repsFrom value for the current source dsa
7253 * if so we'll later replace this value
7255 orf_el
= ldb_msg_find_element(ar
->search_msg
, "repsFrom");
7257 for (i
=0; i
< orf_el
->num_values
; i
++) {
7258 struct repsFromToBlob
*trf
;
7260 trf
= talloc(ar
, struct repsFromToBlob
);
7261 if (!trf
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
7263 ndr_err
= ndr_pull_struct_blob(&orf_el
->values
[i
], trf
, trf
,
7264 (ndr_pull_flags_fn_t
)ndr_pull_repsFromToBlob
);
7265 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
7266 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
7267 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
7270 if (trf
->version
!= 1) {
7271 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
7275 * we compare the source dsa objectGUID not the invocation_id
7276 * because we want only one repsFrom value per source dsa
7277 * and when the invocation_id of the source dsa has changed we don't need
7278 * the old repsFrom with the old invocation_id
7280 if (!GUID_equal(&trf
->ctr
.ctr1
.source_dsa_obj_guid
,
7281 &ar
->objs
->source_dsa
->source_dsa_obj_guid
)) {
7287 nrf_value
= &orf_el
->values
[i
];
7292 * copy over all old values to the new ldb_message
7294 ret
= ldb_msg_add_empty(msg
, "repsFrom", 0, &nrf_el
);
7295 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
7300 * if we haven't found an old repsFrom value for the current source dsa
7301 * we'll add a new value
7304 struct ldb_val zero_value
;
7305 ZERO_STRUCT(zero_value
);
7306 ret
= ldb_msg_add_value(msg
, "repsFrom", &zero_value
, &nrf_el
);
7307 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
7309 nrf_value
= &nrf_el
->values
[nrf_el
->num_values
- 1];
7312 /* we now fill the value which is already attached to ldb_message */
7313 ndr_err
= ndr_push_struct_blob(nrf_value
, msg
,
7315 (ndr_push_flags_fn_t
)ndr_push_repsFromToBlob
);
7316 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
7317 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
7318 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
7322 * the ldb_message_element for the attribute, has all the old values and the new one
7323 * so we'll replace the whole attribute with all values
7325 nrf_el
->flags
= LDB_FLAG_MOD_REPLACE
;
7327 if (CHECK_DEBUGLVL(4)) {
7328 char *s
= ldb_ldif_message_redacted_string(ldb
, ar
,
7329 LDB_CHANGETYPE_MODIFY
,
7331 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s
));
7335 /* prepare the ldb_modify() request */
7336 ret
= ldb_build_mod_req(&change_req
,
7342 replmd_replicated_uptodate_modify_callback
,
7344 LDB_REQ_SET_LOCATION(change_req
);
7345 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
7347 return ldb_next_request(ar
->module
, change_req
);
7350 static int replmd_replicated_uptodate_search_callback(struct ldb_request
*req
,
7351 struct ldb_reply
*ares
)
7353 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
7354 struct replmd_replicated_request
);
7358 return ldb_module_done(ar
->req
, NULL
, NULL
,
7359 LDB_ERR_OPERATIONS_ERROR
);
7361 if (ares
->error
!= LDB_SUCCESS
&&
7362 ares
->error
!= LDB_ERR_NO_SUCH_OBJECT
) {
7363 return ldb_module_done(ar
->req
, ares
->controls
,
7364 ares
->response
, ares
->error
);
7367 switch (ares
->type
) {
7368 case LDB_REPLY_ENTRY
:
7369 ar
->search_msg
= talloc_steal(ar
, ares
->message
);
7372 case LDB_REPLY_REFERRAL
:
7373 /* we ignore referrals */
7376 case LDB_REPLY_DONE
:
7377 ret
= replmd_replicated_uptodate_modify(ar
);
7378 if (ret
!= LDB_SUCCESS
) {
7379 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
7388 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request
*ar
)
7390 struct ldb_context
*ldb
= ldb_module_get_ctx(ar
->module
);
7391 struct replmd_private
*replmd_private
=
7392 talloc_get_type_abort(ldb_module_get_private(ar
->module
),
7393 struct replmd_private
);
7395 static const char *attrs
[] = {
7396 "replUpToDateVector",
7401 struct ldb_request
*search_req
;
7403 ar
->search_msg
= NULL
;
7406 * Let the caller know that we did an originating updates
7408 ar
->objs
->originating_updates
= replmd_private
->originating_updates
;
7410 ret
= ldb_build_search_req(&search_req
,
7413 ar
->objs
->partition_dn
,
7419 replmd_replicated_uptodate_search_callback
,
7421 LDB_REQ_SET_LOCATION(search_req
);
7422 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
7424 return ldb_next_request(ar
->module
, search_req
);
7429 static int replmd_extended_replicated_objects(struct ldb_module
*module
, struct ldb_request
*req
)
7431 struct ldb_context
*ldb
;
7432 struct dsdb_extended_replicated_objects
*objs
;
7433 struct replmd_replicated_request
*ar
;
7434 struct ldb_control
**ctrls
;
7437 ldb
= ldb_module_get_ctx(module
);
7439 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_extended_replicated_objects\n");
7441 objs
= talloc_get_type(req
->op
.extended
.data
, struct dsdb_extended_replicated_objects
);
7443 ldb_debug(ldb
, LDB_DEBUG_FATAL
, "replmd_extended_replicated_objects: invalid extended data\n");
7444 return LDB_ERR_PROTOCOL_ERROR
;
7447 if (objs
->version
!= DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION
) {
7448 ldb_debug(ldb
, LDB_DEBUG_FATAL
, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
7449 objs
->version
, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION
);
7450 return LDB_ERR_PROTOCOL_ERROR
;
7453 ar
= replmd_ctx_init(module
, req
);
7455 return LDB_ERR_OPERATIONS_ERROR
;
7457 /* Set the flags to have the replmd_op_callback run over the full set of objects */
7458 ar
->apply_mode
= true;
7460 ar
->schema
= dsdb_get_schema(ldb
, ar
);
7462 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
, "replmd_ctx_init: no loaded schema found\n");
7464 DEBUG(0,(__location__
": %s\n", ldb_errstring(ldb
)));
7465 return LDB_ERR_CONSTRAINT_VIOLATION
;
7468 ctrls
= req
->controls
;
7470 if (req
->controls
) {
7471 req
->controls
= talloc_memdup(ar
, req
->controls
,
7472 talloc_get_size(req
->controls
));
7473 if (!req
->controls
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
7476 ret
= ldb_request_add_control(req
, DSDB_CONTROL_REPLICATED_UPDATE_OID
, false, NULL
);
7477 if (ret
!= LDB_SUCCESS
) {
7481 /* If this change contained linked attributes in the body
7482 * (rather than in the links section) we need to update
7483 * backlinks in linked_attributes */
7484 ret
= ldb_request_add_control(req
, DSDB_CONTROL_APPLY_LINKS
, false, NULL
);
7485 if (ret
!= LDB_SUCCESS
) {
7489 ar
->controls
= req
->controls
;
7490 req
->controls
= ctrls
;
7492 return replmd_replicated_apply_next(ar
);
7496 * Checks how to handle an missing target - either we need to fail the
7497 * replication and retry with GET_TGT, ignore the link and continue, or try to
7498 * add a partial link to an unknown target.
7500 static int replmd_allow_missing_target(struct ldb_module
*module
,
7501 TALLOC_CTX
*mem_ctx
,
7502 struct ldb_dn
*target_dn
,
7503 struct ldb_dn
*source_dn
,
7506 uint32_t dsdb_repl_flags
,
7508 const char * missing_str
)
7510 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
7514 * we may not be able to resolve link targets properly when
7515 * dealing with subsets of objects, e.g. the source is a
7516 * critical object and the target isn't
7519 * When we implement Trusted Domains we need to consider
7520 * whether they get treated as an incomplete replica here or not
7522 if (dsdb_repl_flags
& DSDB_REPL_FLAG_OBJECT_SUBSET
) {
7525 * Ignore the link. We don't increase the highwater-mark in
7526 * the object subset cases, so subsequent replications should
7527 * resolve any missing links
7529 DEBUG(2, ("%s target %s linked from %s\n", missing_str
,
7530 ldb_dn_get_linearized(target_dn
),
7531 ldb_dn_get_linearized(source_dn
)));
7532 *ignore_link
= true;
7536 is_in_same_nc
= dsdb_objects_have_same_nc(ldb
,
7540 if (is_in_same_nc
) {
7542 * We allow the join.py code to point out that all
7543 * replication is completed, so failing now would just
7544 * trigger errors, rather than trigger a GET_TGT
7546 int *finished_full_join_ptr
=
7547 talloc_get_type(ldb_get_opaque(ldb
,
7548 DSDB_FULL_JOIN_REPLICATION_COMPLETED_OPAQUE_NAME
),
7550 bool finished_full_join
= finished_full_join_ptr
&& *finished_full_join_ptr
;
7553 * if the target is already be up-to-date there's no point in
7554 * retrying. This could be due to bad timing, or if a target
7555 * on a one-way link was deleted. We ignore the link rather
7556 * than failing the replication cycle completely
7558 if (finished_full_join
7559 || dsdb_repl_flags
& DSDB_REPL_FLAG_TARGETS_UPTODATE
) {
7560 *ignore_link
= true;
7561 DBG_WARNING("%s is %s "
7562 "but up to date. Ignoring link from %s\n",
7563 ldb_dn_get_linearized(target_dn
), missing_str
,
7564 ldb_dn_get_linearized(source_dn
));
7568 /* otherwise fail the replication and retry with GET_TGT */
7569 ldb_asprintf_errstring(ldb
, "%s target %s GUID %s linked from %s\n",
7571 ldb_dn_get_linearized(target_dn
),
7572 GUID_string(mem_ctx
, guid
),
7573 ldb_dn_get_linearized(source_dn
));
7574 return LDB_ERR_NO_SUCH_OBJECT
;
7578 * The target of the cross-partition link is missing. Continue
7579 * and try to at least add the forward-link. This isn't great,
7580 * but a partial link can be fixed by dbcheck, so it's better
7581 * than dropping the link completely.
7583 *ignore_link
= false;
7585 if (is_obj_commit
) {
7588 * Only log this when we're actually committing the objects.
7589 * This avoids spurious logs, i.e. if we're just verifying the
7590 * received link during a join.
7592 DBG_WARNING("%s cross-partition target %s linked from %s\n",
7593 missing_str
, ldb_dn_get_linearized(target_dn
),
7594 ldb_dn_get_linearized(source_dn
));
7601 * Checks that the target object for a linked attribute exists.
7602 * @param guid returns the target object's GUID (is returned)if it exists)
7603 * @param ignore_link set to true if the linked attribute should be ignored
7604 * (i.e. the target doesn't exist, but that it's OK to skip the link)
7606 static int replmd_check_target_exists(struct ldb_module
*module
,
7607 struct dsdb_dn
*dsdb_dn
,
7608 struct la_entry
*la_entry
,
7609 struct ldb_dn
*source_dn
,
7614 struct drsuapi_DsReplicaLinkedAttribute
*la
= la_entry
->la
;
7615 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
7616 struct ldb_result
*target_res
;
7617 TALLOC_CTX
*tmp_ctx
= talloc_new(la_entry
);
7618 const char *attrs
[] = { "isDeleted", "isRecycled", NULL
};
7621 enum deletion_state target_deletion_state
= OBJECT_REMOVED
;
7622 bool active
= (la
->flags
& DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
) ? true : false;
7624 *ignore_link
= false;
7625 ntstatus
= dsdb_get_extended_dn_guid(dsdb_dn
->dn
, guid
, "GUID");
7627 if (!NT_STATUS_IS_OK(ntstatus
) && !active
) {
7630 * This strange behaviour (allowing a NULL/missing
7631 * GUID) originally comes from:
7633 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
7634 * Author: Andrew Tridgell <tridge@samba.org>
7635 * Date: Mon Dec 21 21:21:55 2009 +1100
7637 * s4-drs: cope better with NULL GUIDS from DRS
7639 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
7640 * need to match by DN if possible when seeing if we should update an
7643 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
7645 ret
= dsdb_module_search_dn(module
, tmp_ctx
, &target_res
,
7647 DSDB_FLAG_NEXT_MODULE
|
7648 DSDB_SEARCH_SHOW_RECYCLED
|
7649 DSDB_SEARCH_SEARCH_ALL_PARTITIONS
|
7650 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
,
7652 } else if (!NT_STATUS_IS_OK(ntstatus
)) {
7653 ldb_asprintf_errstring(ldb
, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
7655 ldb_dn_get_linearized(dsdb_dn
->dn
),
7656 ldb_dn_get_linearized(source_dn
));
7657 talloc_free(tmp_ctx
);
7658 return LDB_ERR_OPERATIONS_ERROR
;
7660 ret
= dsdb_module_search(module
, tmp_ctx
, &target_res
,
7661 NULL
, LDB_SCOPE_SUBTREE
,
7663 DSDB_FLAG_NEXT_MODULE
|
7664 DSDB_SEARCH_SHOW_RECYCLED
|
7665 DSDB_SEARCH_SEARCH_ALL_PARTITIONS
|
7666 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
,
7669 GUID_string(tmp_ctx
, guid
));
7672 if (ret
!= LDB_SUCCESS
) {
7673 ldb_asprintf_errstring(ldb
, "Failed to re-resolve GUID %s: %s\n",
7674 GUID_string(tmp_ctx
, guid
),
7675 ldb_errstring(ldb
));
7676 talloc_free(tmp_ctx
);
7680 if (target_res
->count
== 0) {
7683 * target object is unknown. Check whether to ignore the link,
7684 * fail the replication, or add a partial link
7686 ret
= replmd_allow_missing_target(module
, tmp_ctx
, dsdb_dn
->dn
,
7687 source_dn
, is_obj_commit
, guid
,
7688 la_entry
->dsdb_repl_flags
,
7689 ignore_link
, "Unknown");
7691 } else if (target_res
->count
!= 1) {
7692 ldb_asprintf_errstring(ldb
, "More than one object found matching objectGUID %s\n",
7693 GUID_string(tmp_ctx
, guid
));
7694 ret
= LDB_ERR_OPERATIONS_ERROR
;
7696 struct ldb_message
*target_msg
= target_res
->msgs
[0];
7698 dsdb_dn
->dn
= talloc_steal(dsdb_dn
, target_msg
->dn
);
7700 /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
7701 replmd_deletion_state(module
, target_msg
,
7702 &target_deletion_state
, NULL
);
7705 * Check for deleted objects as per MS-DRSR 4.1.10.6.14
7706 * ProcessLinkValue(). Link updates should not be sent for
7707 * recycled and tombstone objects (deleting the links should
7708 * happen when we delete the object). This probably means our
7709 * copy of the target object isn't up to date.
7711 if (target_deletion_state
>= OBJECT_RECYCLED
) {
7714 * target object is deleted. Check whether to ignore the
7715 * link, fail the replication, or add a partial link
7717 ret
= replmd_allow_missing_target(module
, tmp_ctx
,
7718 dsdb_dn
->dn
, source_dn
,
7719 is_obj_commit
, guid
,
7720 la_entry
->dsdb_repl_flags
,
7721 ignore_link
, "Deleted");
7725 talloc_free(tmp_ctx
);
7730 * Extracts the key details about the source object for a
7731 * linked-attribute entry.
7732 * This returns the following details:
7733 * @param ret_attr the schema details for the linked attribute
7734 * @param source_msg the search result for the source object
7736 static int replmd_get_la_entry_source(struct ldb_module
*module
,
7737 struct la_entry
*la_entry
,
7738 TALLOC_CTX
*mem_ctx
,
7739 const struct dsdb_attribute
**ret_attr
,
7740 struct ldb_message
**source_msg
)
7742 struct drsuapi_DsReplicaLinkedAttribute
*la
= la_entry
->la
;
7743 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
7744 const struct dsdb_schema
*schema
= dsdb_get_schema(ldb
, mem_ctx
);
7746 const struct dsdb_attribute
*attr
;
7747 struct ldb_result
*res
;
7748 const char *attrs
[4];
7751 linked_attributes[0]:
7752 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
7754 identifier: struct drsuapi_DsReplicaObjectIdentifier
7755 __ndr_size : 0x0000003a (58)
7756 __ndr_size_sid : 0x00000000 (0)
7757 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7759 __ndr_size_dn : 0x00000000 (0)
7761 attid : DRSUAPI_ATTID_member (0x1F)
7762 value: struct drsuapi_DsAttributeValue
7763 __ndr_size : 0x0000007e (126)
7765 blob : DATA_BLOB length=126
7766 flags : 0x00000001 (1)
7767 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7768 originating_add_time : Wed Sep 2 22:20:01 2009 EST
7769 meta_data: struct drsuapi_DsReplicaMetaData
7770 version : 0x00000015 (21)
7771 originating_change_time : Wed Sep 2 23:39:07 2009 EST
7772 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7773 originating_usn : 0x000000000001e19c (123292)
7775 (for cases where the link is to a normal DN)
7776 &target: struct drsuapi_DsReplicaObjectIdentifier3
7777 __ndr_size : 0x0000007e (126)
7778 __ndr_size_sid : 0x0000001c (28)
7779 guid : 7639e594-db75-4086-b0d4-67890ae46031
7780 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7781 __ndr_size_dn : 0x00000022 (34)
7782 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7785 /* find the attribute being modified */
7786 attr
= dsdb_attribute_by_attributeID_id(schema
, la
->attid
);
7788 struct GUID_txt_buf guid_str
;
7789 ldb_asprintf_errstring(ldb
, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7791 GUID_buf_string(&la
->identifier
->guid
,
7793 return LDB_ERR_OPERATIONS_ERROR
;
7797 * All attributes listed here must be dealt with in some way
7798 * by replmd_process_linked_attribute() otherwise in the case
7799 * of isDeleted: FALSE the modify will fail with:
7801 * Failed to apply linked attribute change 'attribute 'isDeleted':
7802 * invalid modify flags on
7803 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
7806 * This is becaue isDeleted is a Boolean, so FALSE is a
7807 * legitimate value (set by Samba's deletetest.py)
7809 attrs
[0] = attr
->lDAPDisplayName
;
7810 attrs
[1] = "isDeleted";
7811 attrs
[2] = "isRecycled";
7815 * get the existing message from the db for the object with
7816 * this GUID, returning attribute being modified. We will then
7817 * use this msg as the basis for a modify call
7819 ret
= dsdb_module_search(module
, mem_ctx
, &res
, NULL
, LDB_SCOPE_SUBTREE
, attrs
,
7820 DSDB_FLAG_NEXT_MODULE
|
7821 DSDB_SEARCH_SEARCH_ALL_PARTITIONS
|
7822 DSDB_SEARCH_SHOW_RECYCLED
|
7823 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
|
7824 DSDB_SEARCH_REVEAL_INTERNALS
,
7826 "objectGUID=%s", GUID_string(mem_ctx
, &la
->identifier
->guid
));
7827 if (ret
!= LDB_SUCCESS
) {
7830 if (res
->count
!= 1) {
7831 ldb_asprintf_errstring(ldb
, "DRS linked attribute for GUID %s - DN not found",
7832 GUID_string(mem_ctx
, &la
->identifier
->guid
));
7833 return LDB_ERR_NO_SUCH_OBJECT
;
7836 *source_msg
= res
->msgs
[0];
7843 * Verifies the target object is known for a linked attribute
7845 static int replmd_verify_link_target(struct replmd_replicated_request
*ar
,
7846 TALLOC_CTX
*mem_ctx
,
7847 struct la_entry
*la_entry
,
7848 struct ldb_dn
*src_dn
,
7849 const struct dsdb_attribute
*attr
)
7851 int ret
= LDB_SUCCESS
;
7852 struct ldb_module
*module
= ar
->module
;
7853 struct dsdb_dn
*tgt_dsdb_dn
= NULL
;
7854 struct GUID guid
= GUID_zero();
7857 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
7858 struct drsuapi_DsReplicaLinkedAttribute
*la
= la_entry
->la
;
7859 const struct dsdb_schema
*schema
= dsdb_get_schema(ldb
, mem_ctx
);
7861 /* the value blob for the attribute holds the target object DN */
7862 status
= dsdb_dn_la_from_blob(ldb
, attr
, schema
, mem_ctx
,
7863 la
->value
.blob
, &tgt_dsdb_dn
);
7864 if (!W_ERROR_IS_OK(status
)) {
7865 ldb_asprintf_errstring(ldb
, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7866 attr
->lDAPDisplayName
,
7867 ldb_dn_get_linearized(src_dn
),
7868 win_errstr(status
));
7869 return LDB_ERR_OPERATIONS_ERROR
;
7873 * We can skip the target object checks if we're only syncing critical
7874 * objects, or we know the target is up-to-date. If either case, we
7875 * still continue even if the target doesn't exist
7877 if ((la_entry
->dsdb_repl_flags
& (DSDB_REPL_FLAG_OBJECT_SUBSET
|
7878 DSDB_REPL_FLAG_TARGETS_UPTODATE
)) == 0) {
7880 ret
= replmd_check_target_exists(module
, tgt_dsdb_dn
, la_entry
,
7881 src_dn
, false, &guid
, &dummy
);
7885 * When we fail to find the target object, the error code we pass
7886 * back here is really important. It flags back to the callers to
7887 * retry this request with DRSUAPI_DRS_GET_TGT
7889 if (ret
== LDB_ERR_NO_SUCH_OBJECT
) {
7890 ret
= replmd_replicated_request_werror(ar
, WERR_DS_DRA_RECYCLED_TARGET
);
7897 * Finds the current active Parsed-DN value for a single-valued linked
7898 * attribute, if one exists.
7899 * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
7900 * @returns LDB_SUCCESS (regardless of whether a match was found), unless
7903 static int replmd_get_active_singleval_link(struct ldb_module
*module
,
7904 TALLOC_CTX
*mem_ctx
,
7905 struct parsed_dn pdn_list
[],
7907 const struct dsdb_attribute
*attr
,
7908 struct parsed_dn
**ret_pdn
)
7914 if (!(attr
->ldb_schema_attribute
->flags
& LDB_ATTR_FLAG_SINGLE_VALUE
)) {
7916 /* nothing to do for multi-valued linked attributes */
7920 for (i
= 0; i
< count
; i
++) {
7921 int ret
= LDB_SUCCESS
;
7922 struct parsed_dn
*pdn
= &pdn_list
[i
];
7924 /* skip any inactive links */
7925 if (dsdb_dn_is_deleted_val(pdn
->v
)) {
7929 /* we've found an active value for this attribute */
7932 if (pdn
->dsdb_dn
== NULL
) {
7933 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
7935 ret
= really_parse_trusted_dn(mem_ctx
, ldb
, pdn
,
7936 attr
->syntax
->ldap_oid
);
7942 /* no active link found */
7947 * @returns true if the replication linked attribute info is newer than we
7948 * already have in our DB
7949 * @param pdn the existing linked attribute info in our DB
7950 * @param la the new linked attribute info received during replication
7952 static bool replmd_link_update_is_newer(struct parsed_dn
*pdn
,
7953 struct drsuapi_DsReplicaLinkedAttribute
*la
)
7955 /* see if this update is newer than what we have already */
7956 struct GUID invocation_id
= GUID_zero();
7957 uint32_t version
= 0;
7958 NTTIME change_time
= 0;
7962 /* no existing info so update is newer */
7966 dsdb_get_extended_dn_guid(pdn
->dsdb_dn
->dn
, &invocation_id
, "RMD_INVOCID");
7967 dsdb_get_extended_dn_uint32(pdn
->dsdb_dn
->dn
, &version
, "RMD_VERSION");
7968 dsdb_get_extended_dn_nttime(pdn
->dsdb_dn
->dn
, &change_time
, "RMD_CHANGETIME");
7970 return replmd_update_is_newer(&invocation_id
,
7971 &la
->meta_data
.originating_invocation_id
,
7973 la
->meta_data
.version
,
7975 la
->meta_data
.originating_change_time
);
7979 * Marks an existing linked attribute value as deleted in the DB
7980 * @param pdn the parsed-DN of the target-value to delete
7982 static int replmd_delete_link_value(struct ldb_module
*module
,
7983 struct replmd_private
*replmd_private
,
7984 TALLOC_CTX
*mem_ctx
,
7985 struct ldb_dn
*src_obj_dn
,
7986 const struct dsdb_schema
*schema
,
7987 const struct dsdb_attribute
*attr
,
7990 struct GUID
*target_guid
,
7991 struct dsdb_dn
*target_dsdb_dn
,
7992 struct ldb_val
*output_val
)
7994 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
7997 const struct GUID
*invocation_id
= NULL
;
8001 unix_to_nt_time(&now
, t
);
8003 invocation_id
= samdb_ntds_invocation_id(ldb
);
8004 if (invocation_id
== NULL
) {
8005 return LDB_ERR_OPERATIONS_ERROR
;
8008 /* if the existing link is active, remove its backlink */
8012 * NOTE WELL: After this we will never (at runtime) be
8013 * able to find this forward link (for instant
8014 * removal) if/when the link target is deleted.
8016 * We have dbcheck rules to cover this and cope otherwise
8017 * by filtering at runtime (i.e. in the extended_dn module).
8019 ret
= replmd_add_backlink(module
, replmd_private
, schema
,
8020 src_obj_dn
, target_guid
, false,
8022 if (ret
!= LDB_SUCCESS
) {
8027 /* mark the existing value as deleted */
8028 ret
= replmd_update_la_val(mem_ctx
, output_val
, target_dsdb_dn
,
8029 target_dsdb_dn
, invocation_id
, seq_num
,
8030 seq_num
, now
, true);
8035 * Checks for a conflict in single-valued link attributes, and tries to
8036 * resolve the problem if possible.
8038 * Single-valued links should only ever have one active value. If we already
8039 * have an active link value, and during replication we receive an active link
8040 * value for a different target DN, then we need to resolve this inconsistency
8041 * and determine which value should be active. If the received info is better/
8042 * newer than the existing link attribute, then we need to set our existing
8043 * link as deleted. If the received info is worse/older, then we should continue
8044 * to add it, but set it as an inactive link.
8046 * Note that this is a corner-case that is unlikely to happen (but if it does
8047 * happen, we don't want it to break replication completely).
8049 * @param pdn_being_modified the parsed DN corresponding to the received link
8050 * target (note this is NULL if the link does not already exist in our DB)
8051 * @param pdn_list all the source object's Parsed-DNs for this attribute, i.e.
8052 * any existing active or inactive values for the attribute in our DB.
8053 * @param dsdb_dn the target DN for the received link attribute
8054 * @param add_as_inactive gets set to true if the received link is worse than
8055 * the existing link - it should still be added, but as an inactive link.
8057 static int replmd_check_singleval_la_conflict(struct ldb_module
*module
,
8058 struct replmd_private
*replmd_private
,
8059 TALLOC_CTX
*mem_ctx
,
8060 struct ldb_dn
*src_obj_dn
,
8061 struct drsuapi_DsReplicaLinkedAttribute
*la
,
8062 struct dsdb_dn
*dsdb_dn
,
8063 struct parsed_dn
*pdn_being_modified
,
8064 struct parsed_dn
*pdn_list
,
8065 struct ldb_message_element
*old_el
,
8066 const struct dsdb_schema
*schema
,
8067 const struct dsdb_attribute
*attr
,
8069 bool *add_as_inactive
)
8071 struct parsed_dn
*active_pdn
= NULL
;
8072 bool update_is_newer
= false;
8076 * check if there's a conflict for single-valued links, i.e. an active
8077 * linked attribute already exists, but it has a different target value
8079 ret
= replmd_get_active_singleval_link(module
, mem_ctx
, pdn_list
,
8080 old_el
->num_values
, attr
,
8083 if (ret
!= LDB_SUCCESS
) {
8088 * If no active value exists (or the received info is for the currently
8089 * active value), then no conflict exists
8091 if (active_pdn
== NULL
|| active_pdn
== pdn_being_modified
) {
8095 DBG_WARNING("Link conflict for %s attribute on %s\n",
8096 attr
->lDAPDisplayName
, ldb_dn_get_linearized(src_obj_dn
));
8098 /* Work out how to resolve the conflict based on which info is better */
8099 update_is_newer
= replmd_link_update_is_newer(active_pdn
, la
);
8101 if (update_is_newer
) {
8102 DBG_WARNING("Using received value %s, over existing target %s\n",
8103 ldb_dn_get_linearized(dsdb_dn
->dn
),
8104 ldb_dn_get_linearized(active_pdn
->dsdb_dn
->dn
));
8107 * Delete our existing active link. The received info will then
8108 * be added (through normal link processing) as the active value
8110 ret
= replmd_delete_link_value(module
, replmd_private
, old_el
,
8111 src_obj_dn
, schema
, attr
,
8112 seq_num
, true, &active_pdn
->guid
,
8113 active_pdn
->dsdb_dn
,
8116 if (ret
!= LDB_SUCCESS
) {
8120 DBG_WARNING("Using existing target %s, over received value %s\n",
8121 ldb_dn_get_linearized(active_pdn
->dsdb_dn
->dn
),
8122 ldb_dn_get_linearized(dsdb_dn
->dn
));
8125 * we want to keep our existing active link and add the
8126 * received link as inactive
8128 *add_as_inactive
= true;
8135 * Processes one linked attribute received via replication.
8136 * @param src_dn the DN of the source object for the link
8137 * @param attr schema info for the linked attribute
8138 * @param la_entry the linked attribute info received via DRS
8139 * @param element_ctx mem context for msg->element[] (when adding a new value
8140 * we need to realloc old_el->values)
8141 * @param old_el the corresponding msg->element[] for the linked attribute
8142 * @param pdn_list a (binary-searchable) parsed DN array for the existing link
8143 * values in the msg. E.g. for a group, this is the existing members.
8144 * @param change what got modified: either nothing, an existing link value was
8145 * modified, or a new link value was added.
8146 * @returns LDB_SUCCESS if OK, an error otherwise
8148 static int replmd_process_linked_attribute(struct ldb_module
*module
,
8149 TALLOC_CTX
*mem_ctx
,
8150 struct replmd_private
*replmd_private
,
8151 struct ldb_dn
*src_dn
,
8152 const struct dsdb_attribute
*attr
,
8153 struct la_entry
*la_entry
,
8154 struct ldb_request
*parent
,
8155 TALLOC_CTX
*element_ctx
,
8156 struct ldb_message_element
*old_el
,
8157 struct parsed_dn
*pdn_list
,
8158 replmd_link_changed
*change
)
8160 struct drsuapi_DsReplicaLinkedAttribute
*la
= la_entry
->la
;
8161 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
8162 const struct dsdb_schema
*schema
= dsdb_get_schema(ldb
, mem_ctx
);
8164 struct dsdb_dn
*dsdb_dn
= NULL
;
8165 uint64_t seq_num
= 0;
8166 struct parsed_dn
*pdn
, *next
;
8167 struct GUID guid
= GUID_zero();
8168 bool active
= (la
->flags
& DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
)?true:false;
8170 struct dsdb_dn
*old_dsdb_dn
= NULL
;
8171 struct ldb_val
*val_to_update
= NULL
;
8172 bool add_as_inactive
= false;
8175 *change
= LINK_CHANGE_NONE
;
8177 /* the value blob for the attribute holds the target object DN */
8178 status
= dsdb_dn_la_from_blob(ldb
, attr
, schema
, mem_ctx
,
8179 la
->value
.blob
, &dsdb_dn
);
8180 if (!W_ERROR_IS_OK(status
)) {
8181 ldb_asprintf_errstring(ldb
, "Failed to parsed linked attribute blob for %s on %s - %s\n",
8182 attr
->lDAPDisplayName
,
8183 ldb_dn_get_linearized(src_dn
),
8184 win_errstr(status
));
8185 return LDB_ERR_OPERATIONS_ERROR
;
8188 ret
= replmd_check_target_exists(module
, dsdb_dn
, la_entry
, src_dn
,
8189 true, &guid
, &ignore_link
);
8191 if (ret
!= LDB_SUCCESS
) {
8196 * there are some cases where the target object doesn't exist, but it's
8197 * OK to ignore the linked attribute
8203 /* see if this link already exists */
8204 ret
= parsed_dn_find(ldb
, pdn_list
, old_el
->num_values
,
8207 dsdb_dn
->extra_part
, 0,
8209 attr
->syntax
->ldap_oid
,
8211 if (ret
!= LDB_SUCCESS
) {
8215 if (!replmd_link_update_is_newer(pdn
, la
)) {
8216 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
8217 old_el
->name
, ldb_dn_get_linearized(src_dn
),
8218 GUID_string(mem_ctx
, &la
->meta_data
.originating_invocation_id
)));
8222 /* get a seq_num for this change */
8223 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &seq_num
);
8224 if (ret
!= LDB_SUCCESS
) {
8229 * check for single-valued link conflicts, i.e. an active linked
8230 * attribute already exists, but it has a different target value
8233 ret
= replmd_check_singleval_la_conflict(module
, replmd_private
,
8234 mem_ctx
, src_dn
, la
,
8235 dsdb_dn
, pdn
, pdn_list
,
8236 old_el
, schema
, attr
,
8239 if (ret
!= LDB_SUCCESS
) {
8245 uint32_t rmd_flags
= dsdb_dn_rmd_flags(pdn
->dsdb_dn
->dn
);
8247 if (!(rmd_flags
& DSDB_RMD_FLAG_DELETED
)) {
8248 /* remove the existing backlink */
8249 ret
= replmd_add_backlink(module
, replmd_private
,
8252 &pdn
->guid
, false, attr
,
8254 if (ret
!= LDB_SUCCESS
) {
8259 val_to_update
= pdn
->v
;
8260 old_dsdb_dn
= pdn
->dsdb_dn
;
8261 *change
= LINK_CHANGE_MODIFIED
;
8267 * We know where the new one needs to be, from the *next
8268 * pointer into pdn_list.
8271 offset
= old_el
->num_values
;
8273 if (next
->dsdb_dn
== NULL
) {
8274 ret
= really_parse_trusted_dn(mem_ctx
, ldb
, next
,
8275 attr
->syntax
->ldap_oid
);
8276 if (ret
!= LDB_SUCCESS
) {
8280 offset
= next
- pdn_list
;
8281 if (offset
> old_el
->num_values
) {
8282 return LDB_ERR_OPERATIONS_ERROR
;
8286 old_el
->values
= talloc_realloc(element_ctx
, old_el
->values
,
8287 struct ldb_val
, old_el
->num_values
+1);
8288 if (!old_el
->values
) {
8289 ldb_module_oom(module
);
8290 return LDB_ERR_OPERATIONS_ERROR
;
8293 if (offset
!= old_el
->num_values
) {
8294 memmove(&old_el
->values
[offset
+ 1], &old_el
->values
[offset
],
8295 (old_el
->num_values
- offset
) * sizeof(old_el
->values
[0]));
8298 old_el
->num_values
++;
8300 val_to_update
= &old_el
->values
[offset
];
8302 *change
= LINK_CHANGE_ADDED
;
8305 /* set the link attribute's value to the info that was received */
8306 ret
= replmd_set_la_val(mem_ctx
, val_to_update
, dsdb_dn
, old_dsdb_dn
,
8307 &la
->meta_data
.originating_invocation_id
,
8308 la
->meta_data
.originating_usn
, seq_num
,
8309 la
->meta_data
.originating_change_time
,
8310 la
->meta_data
.version
,
8312 if (ret
!= LDB_SUCCESS
) {
8316 if (add_as_inactive
) {
8318 /* Set the new link as inactive/deleted to avoid conflicts */
8319 ret
= replmd_delete_link_value(module
, replmd_private
, old_el
,
8320 src_dn
, schema
, attr
, seq_num
,
8321 false, &guid
, dsdb_dn
,
8324 if (ret
!= LDB_SUCCESS
) {
8328 } else if (active
) {
8330 /* if the new link is active, then add the new backlink */
8331 ret
= replmd_add_backlink(module
, replmd_private
,
8336 if (ret
!= LDB_SUCCESS
) {
8341 ret
= dsdb_check_single_valued_link(attr
, old_el
);
8342 if (ret
!= LDB_SUCCESS
) {
8346 old_el
->flags
|= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK
;
8351 static int replmd_extended(struct ldb_module
*module
, struct ldb_request
*req
)
8353 if (strcmp(req
->op
.extended
.oid
, DSDB_EXTENDED_REPLICATED_OBJECTS_OID
) == 0) {
8354 return replmd_extended_replicated_objects(module
, req
);
8357 return ldb_next_request(module
, req
);
8362 we hook into the transaction operations to allow us to
8363 perform the linked attribute updates at the end of the whole
8364 transaction. This allows a forward linked attribute to be created
8365 before the object is created. During a vampire, w2k8 sends us linked
8366 attributes before the objects they are part of.
8368 static int replmd_start_transaction(struct ldb_module
*module
)
8370 /* create our private structure for this transaction */
8371 struct replmd_private
*replmd_private
= talloc_get_type(ldb_module_get_private(module
),
8372 struct replmd_private
);
8373 replmd_txn_cleanup(replmd_private
);
8375 /* free any leftover mod_usn records from cancelled
8377 while (replmd_private
->ncs
) {
8378 struct nc_entry
*e
= replmd_private
->ncs
;
8379 DLIST_REMOVE(replmd_private
->ncs
, e
);
8383 replmd_private
->originating_updates
= false;
8385 return ldb_next_start_trans(module
);
8389 * Processes a group of linked attributes that apply to the same source-object
8390 * and attribute-ID (and were received in the same replication chunk).
8392 static int replmd_process_la_group(struct ldb_module
*module
,
8393 struct replmd_private
*replmd_private
,
8394 struct la_group
*la_group
)
8396 struct la_entry
*la
= NULL
;
8397 struct la_entry
*prev
= NULL
;
8399 TALLOC_CTX
*tmp_ctx
= NULL
;
8400 struct la_entry
*first_la
= DLIST_TAIL(la_group
->la_entries
);
8401 struct ldb_message
*msg
= NULL
;
8402 enum deletion_state deletion_state
= OBJECT_NOT_DELETED
;
8403 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
8404 const struct dsdb_attribute
*attr
= NULL
;
8405 struct ldb_message_element
*old_el
= NULL
;
8406 struct parsed_dn
*pdn_list
= NULL
;
8407 replmd_link_changed change_type
;
8408 uint32_t num_changes
= 0;
8410 uint64_t seq_num
= 0;
8412 tmp_ctx
= talloc_new(la_group
);
8413 if (tmp_ctx
== NULL
) {
8414 return ldb_oom(ldb
);
8418 * get the attribute being modified and the search result for the
8421 ret
= replmd_get_la_entry_source(module
, first_la
, tmp_ctx
, &attr
,
8424 if (ret
!= LDB_SUCCESS
) {
8429 * Check for deleted objects per MS-DRSR 4.1.10.6.14
8430 * ProcessLinkValue, because link updates are not applied to
8431 * recycled and tombstone objects. We don't have to delete
8432 * any existing link, that should have happened when the
8433 * object deletion was replicated or initiated.
8435 * This needs isDeleted and isRecycled to be included as
8436 * attributes in the search and so in msg if set.
8438 replmd_deletion_state(module
, msg
, &deletion_state
, NULL
);
8440 if (deletion_state
>= OBJECT_RECYCLED
) {
8441 TALLOC_FREE(tmp_ctx
);
8446 * Now that we know the deletion_state, remove the extra
8447 * attributes added for that purpose. We need to do this
8448 * otherwise in the case of isDeleted: FALSE the modify will
8451 * Failed to apply linked attribute change 'attribute 'isDeleted':
8452 * invalid modify flags on
8453 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
8456 * This is becaue isDeleted is a Boolean, so FALSE is a
8457 * legitimate value (set by Samba's deletetest.py)
8459 ldb_msg_remove_attr(msg
, "isDeleted");
8460 ldb_msg_remove_attr(msg
, "isRecycled");
8462 /* get the msg->element[] for the link attribute being processed */
8463 old_el
= ldb_msg_find_element(msg
, attr
->lDAPDisplayName
);
8464 if (old_el
== NULL
) {
8465 ret
= ldb_msg_add_empty(msg
, attr
->lDAPDisplayName
,
8466 LDB_FLAG_MOD_REPLACE
, &old_el
);
8467 if (ret
!= LDB_SUCCESS
) {
8468 ldb_module_oom(module
);
8469 return LDB_ERR_OPERATIONS_ERROR
;
8472 old_el
->flags
= LDB_FLAG_MOD_REPLACE
;
8476 * go through and process the link target value(s) for this particular
8477 * source object and attribute. For optimization, the same msg is used
8478 * across multiple calls to replmd_process_linked_attribute().
8479 * Note that we should not add or remove any msg attributes inside the
8480 * loop (we should only add/modify *values* for the attribute being
8481 * processed). Otherwise msg->elements is realloc'd and old_el/pdn_list
8482 * pointers will be invalidated
8484 for (la
= DLIST_TAIL(la_group
->la_entries
); la
; la
=prev
) {
8485 prev
= DLIST_PREV(la
);
8486 DLIST_REMOVE(la_group
->la_entries
, la
);
8489 * parse the existing links (this can be costly for a large
8490 * group, so we try to minimize the times we do it)
8492 if (pdn_list
== NULL
) {
8493 ret
= get_parsed_dns_trusted_fallback(module
,
8497 attr
->syntax
->ldap_oid
,
8500 if (ret
!= LDB_SUCCESS
) {
8504 ret
= replmd_process_linked_attribute(module
, tmp_ctx
,
8506 msg
->dn
, attr
, la
, NULL
,
8507 msg
->elements
, old_el
,
8508 pdn_list
, &change_type
);
8509 if (ret
!= LDB_SUCCESS
) {
8510 replmd_txn_cleanup(replmd_private
);
8515 * Adding a link reallocs memory, and so invalidates all the
8516 * pointers in pdn_list. Reparse the PDNs on the next loop
8518 if (change_type
== LINK_CHANGE_ADDED
) {
8519 TALLOC_FREE(pdn_list
);
8522 if (change_type
!= LINK_CHANGE_NONE
) {
8526 if ((++replmd_private
->num_processed
% 8192) == 0) {
8527 DBG_NOTICE("Processed %u/%u linked attributes\n",
8528 replmd_private
->num_processed
,
8529 replmd_private
->total_links
);
8534 * it's possible we're already up-to-date and so don't need to modify
8535 * the object at all (e.g. doing a 'drs replicate --full-sync')
8537 if (num_changes
== 0) {
8538 TALLOC_FREE(tmp_ctx
);
8543 * Note that adding the whenChanged/etc attributes below will realloc
8544 * msg->elements, invalidating the existing element/parsed-DN pointers
8547 TALLOC_FREE(pdn_list
);
8549 /* update whenChanged/uSNChanged as the object has changed */
8551 ret
= ldb_sequence_number(ldb
, LDB_SEQ_HIGHEST_SEQ
,
8553 if (ret
!= LDB_SUCCESS
) {
8557 ret
= add_time_element(msg
, "whenChanged", t
);
8558 if (ret
!= LDB_SUCCESS
) {
8563 ret
= add_uint64_element(ldb
, msg
, "uSNChanged", seq_num
);
8564 if (ret
!= LDB_SUCCESS
) {
8569 /* apply the link changes to the source object */
8570 ret
= linked_attr_modify(module
, msg
, NULL
);
8571 if (ret
!= LDB_SUCCESS
) {
8572 ldb_debug(ldb
, LDB_DEBUG_WARNING
,
8573 "Failed to apply linked attribute change "
8574 "Error: '%s' DN: '%s' Attribute: '%s'\n",
8576 ldb_dn_get_linearized(msg
->dn
),
8577 attr
->lDAPDisplayName
);
8578 TALLOC_FREE(tmp_ctx
);
8582 TALLOC_FREE(tmp_ctx
);
8587 on prepare commit we loop over our queued la_context structures and
8590 static int replmd_prepare_commit(struct ldb_module
*module
)
8592 struct replmd_private
*replmd_private
=
8593 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
8594 struct la_group
*la_group
, *prev
;
8597 if (replmd_private
->la_list
!= NULL
) {
8598 DBG_NOTICE("Processing linked attributes\n");
8602 * Walk the list of linked attributes from DRS replication.
8604 * We walk backwards, to do the first entry first, as we
8605 * added the entries with DLIST_ADD() which puts them at the
8608 * Links are grouped together so we process links for the same
8609 * source object in one go.
8611 for (la_group
= DLIST_TAIL(replmd_private
->la_list
);
8615 prev
= DLIST_PREV(la_group
);
8616 DLIST_REMOVE(replmd_private
->la_list
, la_group
);
8617 ret
= replmd_process_la_group(module
, replmd_private
,
8619 if (ret
!= LDB_SUCCESS
) {
8620 replmd_txn_cleanup(replmd_private
);
8625 replmd_txn_cleanup(replmd_private
);
8627 /* possibly change @REPLCHANGED */
8628 ret
= replmd_notify_store(module
, NULL
);
8629 if (ret
!= LDB_SUCCESS
) {
8633 return ldb_next_prepare_commit(module
);
8636 static int replmd_del_transaction(struct ldb_module
*module
)
8638 struct replmd_private
*replmd_private
=
8639 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
8640 replmd_txn_cleanup(replmd_private
);
8642 return ldb_next_del_trans(module
);
8646 static const struct ldb_module_ops ldb_repl_meta_data_module_ops
= {
8647 .name
= "repl_meta_data",
8648 .init_context
= replmd_init
,
8650 .modify
= replmd_modify
,
8651 .rename
= replmd_rename
,
8652 .del
= replmd_delete
,
8653 .extended
= replmd_extended
,
8654 .start_transaction
= replmd_start_transaction
,
8655 .prepare_commit
= replmd_prepare_commit
,
8656 .del_transaction
= replmd_del_transaction
,
8659 int ldb_repl_meta_data_module_init(const char *version
)
8661 LDB_MODULE_CHECK_VERSION(version
);
8662 return ldb_register_module(&ldb_repl_meta_data_module_ops
);