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"
55 #define DBGC_CLASS DBGC_DRS_REPL
58 * It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
59 * Deleted Objects Container
61 static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME
= 2650466015990000000ULL;
63 struct replmd_private
{
65 struct la_entry
*la_list
;
67 struct nc_entry
*prev
, *next
;
70 uint64_t mod_usn_urgent
;
72 struct ldb_dn
*schema_dn
;
73 bool originating_updates
;
78 struct la_entry
*next
, *prev
;
79 struct drsuapi_DsReplicaLinkedAttribute
*la
;
80 uint32_t dsdb_repl_flags
;
83 struct replmd_replicated_request
{
84 struct ldb_module
*module
;
85 struct ldb_request
*req
;
87 const struct dsdb_schema
*schema
;
88 struct GUID our_invocation_id
;
90 /* the controls we pass down */
91 struct ldb_control
**controls
;
94 * Backlinks for the replmd_add() case (we want to create
95 * backlinks after creating the user, but before the end of
98 struct la_backlink
*la_backlinks
;
100 /* details for the mode where we apply a bunch of inbound replication meessages */
102 uint32_t index_current
;
103 struct dsdb_extended_replicated_objects
*objs
;
105 struct ldb_message
*search_msg
;
106 struct GUID local_parent_guid
;
114 static int replmd_replicated_apply_merge(struct replmd_replicated_request
*ar
);
115 static int replmd_delete_internals(struct ldb_module
*module
, struct ldb_request
*req
, bool re_delete
);
116 static int replmd_check_upgrade_links(struct ldb_context
*ldb
,
117 struct parsed_dn
*dns
, uint32_t count
,
118 struct ldb_message_element
*el
,
119 const char *ldap_oid
);
120 static int replmd_verify_linked_attribute(struct replmd_replicated_request
*ar
,
121 struct la_entry
*la
);
123 enum urgent_situation
{
124 REPL_URGENT_ON_CREATE
= 1,
125 REPL_URGENT_ON_UPDATE
= 2,
126 REPL_URGENT_ON_DELETE
= 4
129 enum deletion_state
{
130 OBJECT_NOT_DELETED
=1,
137 static void replmd_deletion_state(struct ldb_module
*module
,
138 const struct ldb_message
*msg
,
139 enum deletion_state
*current_state
,
140 enum deletion_state
*next_state
)
143 bool enabled
= false;
146 *current_state
= OBJECT_REMOVED
;
147 if (next_state
!= NULL
) {
148 *next_state
= OBJECT_REMOVED
;
153 ret
= dsdb_recyclebin_enabled(module
, &enabled
);
154 if (ret
!= LDB_SUCCESS
) {
158 if (ldb_msg_check_string_attribute(msg
, "isDeleted", "TRUE")) {
160 *current_state
= OBJECT_TOMBSTONE
;
161 if (next_state
!= NULL
) {
162 *next_state
= OBJECT_REMOVED
;
167 if (ldb_msg_check_string_attribute(msg
, "isRecycled", "TRUE")) {
168 *current_state
= OBJECT_RECYCLED
;
169 if (next_state
!= NULL
) {
170 *next_state
= OBJECT_REMOVED
;
175 *current_state
= OBJECT_DELETED
;
176 if (next_state
!= NULL
) {
177 *next_state
= OBJECT_RECYCLED
;
182 *current_state
= OBJECT_NOT_DELETED
;
183 if (next_state
== NULL
) {
188 *next_state
= OBJECT_DELETED
;
190 *next_state
= OBJECT_TOMBSTONE
;
194 static const struct {
195 const char *update_name
;
196 enum urgent_situation repl_situation
;
197 } urgent_objects
[] = {
198 {"nTDSDSA", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_DELETE
)},
199 {"crossRef", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_DELETE
)},
200 {"attributeSchema", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_UPDATE
)},
201 {"classSchema", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_UPDATE
)},
202 {"secret", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_UPDATE
)},
203 {"rIDManager", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_UPDATE
)},
207 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
208 static const char *urgent_attrs
[] = {
211 "userAccountControl",
216 static bool replmd_check_urgent_objectclass(const struct ldb_message_element
*objectclass_el
,
217 enum urgent_situation situation
)
220 for (i
=0; urgent_objects
[i
].update_name
; i
++) {
222 if ((situation
& urgent_objects
[i
].repl_situation
) == 0) {
226 for (j
=0; j
<objectclass_el
->num_values
; j
++) {
227 const struct ldb_val
*v
= &objectclass_el
->values
[j
];
228 if (ldb_attr_cmp((const char *)v
->data
, urgent_objects
[i
].update_name
) == 0) {
236 static bool replmd_check_urgent_attribute(const struct ldb_message_element
*el
)
238 if (ldb_attr_in_list(urgent_attrs
, el
->name
)) {
244 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request
*ar
);
247 initialise the module
248 allocate the private structure and build the list
249 of partition DNs for use by replmd_notify()
251 static int replmd_init(struct ldb_module
*module
)
253 struct replmd_private
*replmd_private
;
254 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
255 static const char *samba_dsdb_attrs
[] = { SAMBA_COMPATIBLE_FEATURES_ATTR
, NULL
};
256 struct ldb_dn
*samba_dsdb_dn
;
257 struct ldb_result
*res
;
259 TALLOC_CTX
*frame
= talloc_stackframe();
260 replmd_private
= talloc_zero(module
, struct replmd_private
);
261 if (replmd_private
== NULL
) {
264 return LDB_ERR_OPERATIONS_ERROR
;
266 ldb_module_set_private(module
, replmd_private
);
268 replmd_private
->schema_dn
= ldb_get_schema_basedn(ldb
);
270 samba_dsdb_dn
= ldb_dn_new(frame
, ldb
, "@SAMBA_DSDB");
271 if (!samba_dsdb_dn
) {
276 ret
= dsdb_module_search_dn(module
, frame
, &res
, samba_dsdb_dn
,
277 samba_dsdb_attrs
, DSDB_FLAG_NEXT_MODULE
, NULL
);
278 if (ret
== LDB_SUCCESS
) {
279 replmd_private
->sorted_links
280 = ldb_msg_check_string_attribute(res
->msgs
[0],
281 SAMBA_COMPATIBLE_FEATURES_ATTR
,
282 SAMBA_SORTED_LINKS_FEATURE
);
286 return ldb_next_init(module
);
290 cleanup our per-transaction contexts
292 static void replmd_txn_cleanup(struct replmd_private
*replmd_private
)
294 talloc_free(replmd_private
->la_ctx
);
295 replmd_private
->la_list
= NULL
;
296 replmd_private
->la_ctx
= NULL
;
302 struct la_backlink
*next
, *prev
;
303 const char *attr_name
;
304 struct ldb_dn
*forward_dn
;
305 struct GUID target_guid
;
310 a ldb_modify request operating on modules below the
313 static int linked_attr_modify(struct ldb_module
*module
,
314 const struct ldb_message
*message
,
315 struct ldb_request
*parent
)
317 struct ldb_request
*mod_req
;
319 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
320 TALLOC_CTX
*tmp_ctx
= talloc_new(module
);
321 struct ldb_result
*res
;
323 res
= talloc_zero(tmp_ctx
, struct ldb_result
);
325 talloc_free(tmp_ctx
);
326 return ldb_oom(ldb_module_get_ctx(module
));
329 ret
= ldb_build_mod_req(&mod_req
, ldb
, tmp_ctx
,
333 ldb_modify_default_callback
,
335 LDB_REQ_SET_LOCATION(mod_req
);
336 if (ret
!= LDB_SUCCESS
) {
337 talloc_free(tmp_ctx
);
341 ret
= ldb_request_add_control(mod_req
, DSDB_CONTROL_REPLICATED_UPDATE_OID
,
343 if (ret
!= LDB_SUCCESS
) {
347 /* Run the new request */
348 ret
= ldb_next_request(module
, mod_req
);
350 if (ret
== LDB_SUCCESS
) {
351 ret
= ldb_wait(mod_req
->handle
, LDB_WAIT_ALL
);
354 talloc_free(tmp_ctx
);
359 process a backlinks we accumulated during a transaction, adding and
360 deleting the backlinks from the target objects
362 static int replmd_process_backlink(struct ldb_module
*module
, struct la_backlink
*bl
, struct ldb_request
*parent
)
364 struct ldb_dn
*target_dn
, *source_dn
;
366 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
367 struct ldb_message
*msg
;
368 TALLOC_CTX
*frame
= talloc_stackframe();
374 - construct ldb_message
375 - either an add or a delete
377 ret
= dsdb_module_dn_by_guid(module
, frame
, &bl
->target_guid
, &target_dn
, parent
);
378 if (ret
!= LDB_SUCCESS
) {
379 struct GUID_txt_buf guid_str
;
380 DBG_WARNING("Failed to find target DN for linked attribute with GUID %s\n",
381 GUID_buf_string(&bl
->target_guid
, &guid_str
));
382 DBG_WARNING("Please run 'samba-tool dbcheck' to resolve any missing backlinks.\n");
387 msg
= ldb_msg_new(frame
);
389 ldb_module_oom(module
);
391 return LDB_ERR_OPERATIONS_ERROR
;
394 source_dn
= ldb_dn_copy(frame
, bl
->forward_dn
);
396 ldb_module_oom(module
);
398 return LDB_ERR_OPERATIONS_ERROR
;
400 /* Filter down to the attributes we want in the backlink */
401 const char *accept
[] = { "GUID", "SID", NULL
};
402 ldb_dn_extended_filter(source_dn
, accept
);
405 /* construct a ldb_message for adding/deleting the backlink */
407 dn_string
= ldb_dn_get_extended_linearized(frame
, bl
->forward_dn
, 1);
409 ldb_module_oom(module
);
411 return LDB_ERR_OPERATIONS_ERROR
;
413 ret
= ldb_msg_add_steal_string(msg
, bl
->attr_name
, dn_string
);
414 if (ret
!= LDB_SUCCESS
) {
418 msg
->elements
[0].flags
= bl
->active
?LDB_FLAG_MOD_ADD
:LDB_FLAG_MOD_DELETE
;
420 /* a backlink should never be single valued. Unfortunately the
421 exchange schema has a attribute
422 msExchBridgeheadedLocalConnectorsDNBL which is single
423 valued and a backlink. We need to cope with that by
424 ignoring the single value flag */
425 msg
->elements
[0].flags
|= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK
;
427 ret
= dsdb_module_modify(module
, msg
, DSDB_FLAG_NEXT_MODULE
, parent
);
428 if (ret
== LDB_ERR_NO_SUCH_ATTRIBUTE
&& !bl
->active
) {
429 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
430 cope with possible corruption where the backlink has
431 already been removed */
432 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
433 ldb_dn_get_linearized(target_dn
),
434 ldb_dn_get_linearized(source_dn
),
435 ldb_errstring(ldb
)));
437 } else if (ret
!= LDB_SUCCESS
) {
438 ldb_asprintf_errstring(ldb
, "Failed to %s backlink from %s to %s - %s",
439 bl
->active
?"add":"remove",
440 ldb_dn_get_linearized(source_dn
),
441 ldb_dn_get_linearized(target_dn
),
451 add a backlink to the list of backlinks to add/delete in the prepare
454 forward_dn is stolen onto the defereed context
456 static int replmd_defer_add_backlink(struct ldb_module
*module
,
457 struct replmd_private
*replmd_private
,
458 const struct dsdb_schema
*schema
,
459 struct replmd_replicated_request
*ac
,
460 struct ldb_dn
*forward_dn
,
461 struct GUID
*target_guid
, bool active
,
462 const struct dsdb_attribute
*schema_attr
,
463 struct ldb_request
*parent
)
465 const struct dsdb_attribute
*target_attr
;
466 struct la_backlink
*bl
;
468 bl
= talloc(ac
, struct la_backlink
);
470 ldb_module_oom(module
);
471 return LDB_ERR_OPERATIONS_ERROR
;
474 target_attr
= dsdb_attribute_by_linkID(schema
, schema_attr
->linkID
^ 1);
477 * windows 2003 has a broken schema where the
478 * definition of msDS-IsDomainFor is missing (which is
479 * supposed to be the backlink of the
480 * msDS-HasDomainNCs attribute
485 bl
->attr_name
= target_attr
->lDAPDisplayName
;
486 bl
->forward_dn
= talloc_steal(bl
, forward_dn
);
487 bl
->target_guid
= *target_guid
;
490 DLIST_ADD(ac
->la_backlinks
, bl
);
496 add a backlink to the list of backlinks to add/delete in the prepare
499 static int replmd_add_backlink(struct ldb_module
*module
,
500 struct replmd_private
*replmd_private
,
501 const struct dsdb_schema
*schema
,
502 struct ldb_dn
*forward_dn
,
503 struct GUID
*target_guid
, bool active
,
504 const struct dsdb_attribute
*schema_attr
,
505 struct ldb_request
*parent
)
507 const struct dsdb_attribute
*target_attr
;
508 struct la_backlink bl
;
511 target_attr
= dsdb_attribute_by_linkID(schema
, schema_attr
->linkID
^ 1);
514 * windows 2003 has a broken schema where the
515 * definition of msDS-IsDomainFor is missing (which is
516 * supposed to be the backlink of the
517 * msDS-HasDomainNCs attribute
522 bl
.attr_name
= target_attr
->lDAPDisplayName
;
523 bl
.forward_dn
= forward_dn
;
524 bl
.target_guid
= *target_guid
;
527 ret
= replmd_process_backlink(module
, &bl
, parent
);
533 * Callback for most write operations in this module:
535 * notify the repl task that a object has changed. The notifies are
536 * gathered up in the replmd_private structure then written to the
537 * @REPLCHANGED object in each partition during the prepare_commit
539 static int replmd_op_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
542 struct replmd_replicated_request
*ac
=
543 talloc_get_type_abort(req
->context
, struct replmd_replicated_request
);
544 struct replmd_private
*replmd_private
=
545 talloc_get_type_abort(ldb_module_get_private(ac
->module
), struct replmd_private
);
546 struct nc_entry
*modified_partition
;
547 struct ldb_control
*partition_ctrl
;
548 const struct dsdb_control_current_partition
*partition
;
550 struct ldb_control
**controls
;
552 partition_ctrl
= ldb_reply_get_control(ares
, DSDB_CONTROL_CURRENT_PARTITION_OID
);
554 controls
= ares
->controls
;
555 if (ldb_request_get_control(ac
->req
,
556 DSDB_CONTROL_CURRENT_PARTITION_OID
) == NULL
) {
558 * Remove the current partition control from what we pass up
559 * the chain if it hasn't been requested manually.
561 controls
= ldb_controls_except_specified(ares
->controls
, ares
,
565 if (ares
->error
!= LDB_SUCCESS
) {
566 struct GUID_txt_buf guid_txt
;
567 struct ldb_message
*msg
= NULL
;
570 if (ac
->apply_mode
== false) {
571 DBG_NOTICE("Originating update failure. Error is: %s\n",
572 ldb_strerror(ares
->error
));
573 return ldb_module_done(ac
->req
, controls
,
574 ares
->response
, ares
->error
);
577 msg
= ac
->objs
->objects
[ac
->index_current
].msg
;
579 * Set at DBG_NOTICE as once these start to happe, they
580 * will happen a lot until resolved, due to repeated
581 * replication. The caller will probably print the
582 * ldb error string anyway.
584 DBG_NOTICE("DRS replication apply failure for %s. Error is: %s\n",
585 ldb_dn_get_linearized(msg
->dn
),
586 ldb_strerror(ares
->error
));
588 s
= ldb_ldif_message_redacted_string(ldb_module_get_ctx(ac
->module
),
593 DBG_INFO("Failing DRS %s replication message was %s:\n%s\n",
594 ac
->search_msg
== NULL
? "ADD" : "MODIFY",
595 GUID_buf_string(&ac
->objs
->objects
[ac
->index_current
].object_guid
,
599 return ldb_module_done(ac
->req
, controls
,
600 ares
->response
, ares
->error
);
603 if (ares
->type
!= LDB_REPLY_DONE
) {
604 ldb_set_errstring(ldb_module_get_ctx(ac
->module
), "Invalid reply type for notify\n!");
605 return ldb_module_done(ac
->req
, NULL
,
606 NULL
, LDB_ERR_OPERATIONS_ERROR
);
609 if (ac
->apply_mode
== false) {
610 struct la_backlink
*bl
;
612 * process our backlink list after an replmd_add(),
613 * creating and deleting backlinks as necessary (this
614 * code is sync). The other cases are handled inline
617 for (bl
=ac
->la_backlinks
; bl
; bl
=bl
->next
) {
618 ret
= replmd_process_backlink(ac
->module
, bl
, ac
->req
);
619 if (ret
!= LDB_SUCCESS
) {
620 return ldb_module_done(ac
->req
, NULL
,
626 if (!partition_ctrl
) {
627 ldb_set_errstring(ldb_module_get_ctx(ac
->module
),"No partition control on reply");
628 return ldb_module_done(ac
->req
, NULL
,
629 NULL
, LDB_ERR_OPERATIONS_ERROR
);
632 partition
= talloc_get_type_abort(partition_ctrl
->data
,
633 struct dsdb_control_current_partition
);
635 if (ac
->seq_num
> 0) {
636 for (modified_partition
= replmd_private
->ncs
; modified_partition
;
637 modified_partition
= modified_partition
->next
) {
638 if (ldb_dn_compare(modified_partition
->dn
, partition
->dn
) == 0) {
643 if (modified_partition
== NULL
) {
644 modified_partition
= talloc_zero(replmd_private
, struct nc_entry
);
645 if (!modified_partition
) {
646 ldb_oom(ldb_module_get_ctx(ac
->module
));
647 return ldb_module_done(ac
->req
, NULL
,
648 NULL
, LDB_ERR_OPERATIONS_ERROR
);
650 modified_partition
->dn
= ldb_dn_copy(modified_partition
, partition
->dn
);
651 if (!modified_partition
->dn
) {
652 ldb_oom(ldb_module_get_ctx(ac
->module
));
653 return ldb_module_done(ac
->req
, NULL
,
654 NULL
, LDB_ERR_OPERATIONS_ERROR
);
656 DLIST_ADD(replmd_private
->ncs
, modified_partition
);
659 if (ac
->seq_num
> modified_partition
->mod_usn
) {
660 modified_partition
->mod_usn
= ac
->seq_num
;
662 modified_partition
->mod_usn_urgent
= ac
->seq_num
;
665 if (!ac
->apply_mode
) {
666 replmd_private
->originating_updates
= true;
670 if (ac
->apply_mode
) {
671 ret
= replmd_replicated_apply_isDeleted(ac
);
672 if (ret
!= LDB_SUCCESS
) {
673 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
677 /* free the partition control container here, for the
678 * common path. Other cases will have it cleaned up
679 * eventually with the ares */
680 talloc_free(partition_ctrl
);
681 return ldb_module_done(ac
->req
, controls
,
682 ares
->response
, LDB_SUCCESS
);
688 * update a @REPLCHANGED record in each partition if there have been
689 * any writes of replicated data in the partition
691 static int replmd_notify_store(struct ldb_module
*module
, struct ldb_request
*parent
)
693 struct replmd_private
*replmd_private
=
694 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
696 while (replmd_private
->ncs
) {
698 struct nc_entry
*modified_partition
= replmd_private
->ncs
;
700 ret
= dsdb_module_save_partition_usn(module
, modified_partition
->dn
,
701 modified_partition
->mod_usn
,
702 modified_partition
->mod_usn_urgent
, parent
);
703 if (ret
!= LDB_SUCCESS
) {
704 DEBUG(0,(__location__
": Failed to save partition uSN for %s\n",
705 ldb_dn_get_linearized(modified_partition
->dn
)));
709 if (ldb_dn_compare(modified_partition
->dn
,
710 replmd_private
->schema_dn
) == 0) {
711 struct ldb_result
*ext_res
;
712 ret
= dsdb_module_extended(module
,
713 replmd_private
->schema_dn
,
715 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID
,
717 DSDB_FLAG_NEXT_MODULE
,
719 if (ret
!= LDB_SUCCESS
) {
722 talloc_free(ext_res
);
725 DLIST_REMOVE(replmd_private
->ncs
, modified_partition
);
726 talloc_free(modified_partition
);
734 created a replmd_replicated_request context
736 static struct replmd_replicated_request
*replmd_ctx_init(struct ldb_module
*module
,
737 struct ldb_request
*req
)
739 struct ldb_context
*ldb
;
740 struct replmd_replicated_request
*ac
;
741 const struct GUID
*our_invocation_id
;
743 ldb
= ldb_module_get_ctx(module
);
745 ac
= talloc_zero(req
, struct replmd_replicated_request
);
754 ac
->schema
= dsdb_get_schema(ldb
, ac
);
756 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
757 "replmd_modify: no dsdb_schema loaded");
758 DEBUG(0,(__location__
": %s\n", ldb_errstring(ldb
)));
763 /* get our invocationId */
764 our_invocation_id
= samdb_ntds_invocation_id(ldb
);
765 if (!our_invocation_id
) {
766 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
767 "replmd_add: unable to find invocationId\n");
771 ac
->our_invocation_id
= *our_invocation_id
;
777 add a time element to a record
779 static int add_time_element(struct ldb_message
*msg
, const char *attr
, time_t t
)
781 struct ldb_message_element
*el
;
785 if (ldb_msg_find_element(msg
, attr
) != NULL
) {
789 s
= ldb_timestring(msg
, t
);
791 return LDB_ERR_OPERATIONS_ERROR
;
794 ret
= ldb_msg_add_string(msg
, attr
, s
);
795 if (ret
!= LDB_SUCCESS
) {
799 el
= ldb_msg_find_element(msg
, attr
);
800 /* always set as replace. This works because on add ops, the flag
802 el
->flags
= LDB_FLAG_MOD_REPLACE
;
808 add a uint64_t element to a record
810 static int add_uint64_element(struct ldb_context
*ldb
, struct ldb_message
*msg
,
811 const char *attr
, uint64_t v
)
813 struct ldb_message_element
*el
;
816 if (ldb_msg_find_element(msg
, attr
) != NULL
) {
820 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, attr
, v
);
821 if (ret
!= LDB_SUCCESS
) {
825 el
= ldb_msg_find_element(msg
, attr
);
826 /* always set as replace. This works because on add ops, the flag
828 el
->flags
= LDB_FLAG_MOD_REPLACE
;
833 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1
*m1
,
834 const struct replPropertyMetaData1
*m2
,
835 const uint32_t *rdn_attid
)
838 * This assignment seems inoccous, but it is critical for the
839 * system, as we need to do the comparisons as a unsigned
840 * quantity, not signed (enums are signed integers)
842 uint32_t attid_1
= m1
->attid
;
843 uint32_t attid_2
= m2
->attid
;
845 if (attid_1
== attid_2
) {
850 * See above regarding this being an unsigned comparison.
851 * Otherwise when the high bit is set on non-standard
852 * attributes, they would end up first, before objectClass
855 return attid_1
> attid_2
? 1 : -1;
858 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context
*ldb
,
859 struct replPropertyMetaDataCtr1
*ctr1
,
862 if (ctr1
->count
== 0) {
863 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
864 "No elements found in replPropertyMetaData for %s!\n",
865 ldb_dn_get_linearized(dn
));
866 return LDB_ERR_CONSTRAINT_VIOLATION
;
869 /* the objectClass attribute is value 0x00000000, so must be first */
870 if (ctr1
->array
[0].attid
!= DRSUAPI_ATTID_objectClass
) {
871 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
872 "No objectClass found in replPropertyMetaData for %s!\n",
873 ldb_dn_get_linearized(dn
));
874 return LDB_ERR_OBJECT_CLASS_VIOLATION
;
880 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context
*ldb
,
881 struct replPropertyMetaDataCtr1
*ctr1
,
884 /* Note this is O(n^2) for the almost-sorted case, which this is */
885 LDB_TYPESAFE_QSORT(ctr1
->array
, ctr1
->count
, NULL
,
886 replmd_replPropertyMetaData1_attid_sort
);
887 return replmd_replPropertyMetaDataCtr1_verify(ldb
, ctr1
, dn
);
890 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element
*e1
,
891 const struct ldb_message_element
*e2
,
892 const struct dsdb_schema
*schema
)
894 const struct dsdb_attribute
*a1
;
895 const struct dsdb_attribute
*a2
;
898 * TODO: make this faster by caching the dsdb_attribute pointer
899 * on the ldb_messag_element
902 a1
= dsdb_attribute_by_lDAPDisplayName(schema
, e1
->name
);
903 a2
= dsdb_attribute_by_lDAPDisplayName(schema
, e2
->name
);
906 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
910 return strcasecmp(e1
->name
, e2
->name
);
912 if (a1
->attributeID_id
== a2
->attributeID_id
) {
915 return a1
->attributeID_id
> a2
->attributeID_id
? 1 : -1;
918 static void replmd_ldb_message_sort(struct ldb_message
*msg
,
919 const struct dsdb_schema
*schema
)
921 LDB_TYPESAFE_QSORT(msg
->elements
, msg
->num_elements
, schema
, replmd_ldb_message_element_attid_sort
);
924 static int replmd_build_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
, struct dsdb_dn
*dsdb_dn
,
925 const struct GUID
*invocation_id
, uint64_t seq_num
,
926 uint64_t local_usn
, NTTIME nttime
, uint32_t version
, bool deleted
);
928 static int parsed_dn_compare(struct parsed_dn
*pdn1
, struct parsed_dn
*pdn2
);
930 static int get_parsed_dns(struct ldb_module
*module
, TALLOC_CTX
*mem_ctx
,
931 struct ldb_message_element
*el
, struct parsed_dn
**pdn
,
932 const char *ldap_oid
, struct ldb_request
*parent
);
935 fix up linked attributes in replmd_add.
936 This involves setting up the right meta-data in extended DN
937 components, and creating backlinks to the object
939 static int replmd_add_fix_la(struct ldb_module
*module
, TALLOC_CTX
*mem_ctx
,
940 struct replmd_private
*replmd_private
,
941 struct ldb_message_element
*el
,
942 struct replmd_replicated_request
*ac
,
944 struct ldb_dn
*forward_dn
,
945 const struct dsdb_attribute
*sa
,
946 struct ldb_request
*parent
)
949 TALLOC_CTX
*tmp_ctx
= talloc_new(mem_ctx
);
950 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
951 struct parsed_dn
*pdn
;
952 /* We will take a reference to the schema in replmd_add_backlink */
953 const struct dsdb_schema
*schema
= dsdb_get_schema(ldb
, NULL
);
954 struct ldb_val
*new_values
= NULL
;
957 if (dsdb_check_single_valued_link(sa
, el
) == LDB_SUCCESS
) {
958 el
->flags
|= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK
;
960 ldb_asprintf_errstring(ldb
,
961 "Attribute %s is single valued but "
962 "more than one value has been supplied",
964 talloc_free(tmp_ctx
);
965 return LDB_ERR_CONSTRAINT_VIOLATION
;
968 ret
= get_parsed_dns(module
, tmp_ctx
, el
, &pdn
,
969 sa
->syntax
->ldap_oid
, parent
);
970 if (ret
!= LDB_SUCCESS
) {
971 talloc_free(tmp_ctx
);
975 new_values
= talloc_array(tmp_ctx
, struct ldb_val
, el
->num_values
);
976 if (new_values
== NULL
) {
977 ldb_module_oom(module
);
978 talloc_free(tmp_ctx
);
979 return LDB_ERR_OPERATIONS_ERROR
;
982 for (i
= 0; i
< el
->num_values
; i
++) {
983 struct parsed_dn
*p
= &pdn
[i
];
984 if (i
> 0 && parsed_dn_compare(p
, &pdn
[i
- 1]) == 0) {
985 ldb_asprintf_errstring(ldb
,
986 "Linked attribute %s has "
987 "multiple identical values", el
->name
);
988 talloc_free(tmp_ctx
);
989 if (ldb_attr_cmp(el
->name
, "member") == 0) {
990 return LDB_ERR_ENTRY_ALREADY_EXISTS
;
992 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS
;
995 ret
= replmd_build_la_val(el
->values
, p
->v
, p
->dsdb_dn
,
996 &ac
->our_invocation_id
,
997 ac
->seq_num
, ac
->seq_num
, now
, 0, false);
998 if (ret
!= LDB_SUCCESS
) {
999 talloc_free(tmp_ctx
);
1003 ret
= replmd_defer_add_backlink(module
, replmd_private
,
1005 forward_dn
, &p
->guid
, true, sa
,
1007 if (ret
!= LDB_SUCCESS
) {
1008 talloc_free(tmp_ctx
);
1012 new_values
[i
] = *p
->v
;
1014 el
->values
= talloc_steal(mem_ctx
, new_values
);
1016 talloc_free(tmp_ctx
);
1020 static int replmd_add_make_extended_dn(struct ldb_request
*req
,
1021 const DATA_BLOB
*guid_blob
,
1022 struct ldb_dn
**_extended_dn
)
1025 const DATA_BLOB
*sid_blob
;
1026 /* Calculate an extended DN for any linked attributes */
1027 struct ldb_dn
*extended_dn
= ldb_dn_copy(req
, req
->op
.add
.message
->dn
);
1029 return LDB_ERR_OPERATIONS_ERROR
;
1031 ret
= ldb_dn_set_extended_component(extended_dn
, "GUID", guid_blob
);
1032 if (ret
!= LDB_SUCCESS
) {
1036 sid_blob
= ldb_msg_find_ldb_val(req
->op
.add
.message
, "objectSID");
1037 if (sid_blob
!= NULL
) {
1038 ret
= ldb_dn_set_extended_component(extended_dn
, "SID", sid_blob
);
1039 if (ret
!= LDB_SUCCESS
) {
1043 *_extended_dn
= extended_dn
;
1048 intercept add requests
1050 static int replmd_add(struct ldb_module
*module
, struct ldb_request
*req
)
1052 struct ldb_context
*ldb
;
1053 struct ldb_control
*control
;
1054 struct replmd_replicated_request
*ac
;
1055 enum ndr_err_code ndr_err
;
1056 struct ldb_request
*down_req
;
1057 struct ldb_message
*msg
;
1058 const DATA_BLOB
*guid_blob
;
1059 DATA_BLOB guid_blob_stack
;
1061 uint8_t guid_data
[16];
1062 struct replPropertyMetaDataBlob nmd
;
1063 struct ldb_val nmd_value
;
1064 struct ldb_dn
*extended_dn
= NULL
;
1067 * The use of a time_t here seems odd, but as the NTTIME
1068 * elements are actually declared as NTTIME_1sec in the IDL,
1069 * getting a higher resolution timestamp is not required.
1071 time_t t
= time(NULL
);
1076 unsigned int functional_level
;
1078 bool allow_add_guid
= false;
1079 bool remove_current_guid
= false;
1080 bool is_urgent
= false;
1081 bool is_schema_nc
= false;
1082 struct ldb_message_element
*objectclass_el
;
1083 struct replmd_private
*replmd_private
=
1084 talloc_get_type_abort(ldb_module_get_private(module
), struct replmd_private
);
1086 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
1087 control
= ldb_request_get_control(req
, LDB_CONTROL_RELAX_OID
);
1089 allow_add_guid
= true;
1092 /* do not manipulate our control entries */
1093 if (ldb_dn_is_special(req
->op
.add
.message
->dn
)) {
1094 return ldb_next_request(module
, req
);
1097 ldb
= ldb_module_get_ctx(module
);
1099 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_add\n");
1101 guid_blob
= ldb_msg_find_ldb_val(req
->op
.add
.message
, "objectGUID");
1102 if (guid_blob
!= NULL
) {
1103 if (!allow_add_guid
) {
1104 ldb_set_errstring(ldb
,
1105 "replmd_add: it's not allowed to add an object with objectGUID!");
1106 return LDB_ERR_UNWILLING_TO_PERFORM
;
1108 NTSTATUS status
= GUID_from_data_blob(guid_blob
,&guid
);
1109 if (!NT_STATUS_IS_OK(status
)) {
1110 ldb_set_errstring(ldb
,
1111 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1112 return LDB_ERR_UNWILLING_TO_PERFORM
;
1114 /* we remove this attribute as it can be a string and
1115 * will not be treated correctly and then we will re-add
1116 * it later on in the good format */
1117 remove_current_guid
= true;
1121 guid
= GUID_random();
1123 guid_blob_stack
= data_blob_const(guid_data
, sizeof(guid_data
));
1125 /* This can't fail */
1126 ndr_push_struct_into_fixed_blob(&guid_blob_stack
, &guid
,
1127 (ndr_push_flags_fn_t
)ndr_push_GUID
);
1128 guid_blob
= &guid_blob_stack
;
1131 ac
= replmd_ctx_init(module
, req
);
1133 return ldb_module_oom(module
);
1136 functional_level
= dsdb_functional_level(ldb
);
1138 /* Get a sequence number from the backend */
1139 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &ac
->seq_num
);
1140 if (ret
!= LDB_SUCCESS
) {
1145 /* we have to copy the message as the caller might have it as a const */
1146 msg
= ldb_msg_copy_shallow(ac
, req
->op
.add
.message
);
1150 return LDB_ERR_OPERATIONS_ERROR
;
1153 /* generated times */
1154 unix_to_nt_time(&now
, t
);
1155 time_str
= ldb_timestring(msg
, t
);
1159 return LDB_ERR_OPERATIONS_ERROR
;
1161 if (remove_current_guid
) {
1162 ldb_msg_remove_attr(msg
,"objectGUID");
1166 * remove autogenerated attributes
1168 ldb_msg_remove_attr(msg
, "whenCreated");
1169 ldb_msg_remove_attr(msg
, "whenChanged");
1170 ldb_msg_remove_attr(msg
, "uSNCreated");
1171 ldb_msg_remove_attr(msg
, "uSNChanged");
1172 ldb_msg_remove_attr(msg
, "replPropertyMetaData");
1175 * readd replicated attributes
1177 ret
= ldb_msg_add_string(msg
, "whenCreated", time_str
);
1178 if (ret
!= LDB_SUCCESS
) {
1184 /* build the replication meta_data */
1187 nmd
.ctr
.ctr1
.count
= msg
->num_elements
;
1188 nmd
.ctr
.ctr1
.array
= talloc_array(msg
,
1189 struct replPropertyMetaData1
,
1190 nmd
.ctr
.ctr1
.count
);
1191 if (!nmd
.ctr
.ctr1
.array
) {
1194 return LDB_ERR_OPERATIONS_ERROR
;
1197 is_schema_nc
= ldb_dn_compare_base(replmd_private
->schema_dn
, msg
->dn
) == 0;
1199 for (i
=0; i
< msg
->num_elements
;) {
1200 struct ldb_message_element
*e
= &msg
->elements
[i
];
1201 struct replPropertyMetaData1
*m
= &nmd
.ctr
.ctr1
.array
[ni
];
1202 const struct dsdb_attribute
*sa
;
1204 if (e
->name
[0] == '@') {
1209 sa
= dsdb_attribute_by_lDAPDisplayName(ac
->schema
, e
->name
);
1211 ldb_debug_set(ldb
, LDB_DEBUG_ERROR
,
1212 "replmd_add: attribute '%s' not defined in schema\n",
1215 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
1218 if ((sa
->systemFlags
& DS_FLAG_ATTR_NOT_REPLICATED
) || (sa
->systemFlags
& DS_FLAG_ATTR_IS_CONSTRUCTED
)) {
1219 /* if the attribute is not replicated (0x00000001)
1220 * or constructed (0x00000004) it has no metadata
1226 if (sa
->linkID
!= 0 && functional_level
> DS_DOMAIN_FUNCTION_2000
) {
1227 if (extended_dn
== NULL
) {
1228 ret
= replmd_add_make_extended_dn(req
,
1231 if (ret
!= LDB_SUCCESS
) {
1238 * Prepare the context for the backlinks and
1239 * create metadata for the forward links. The
1240 * backlinks are created in
1241 * replmd_op_callback() after the successful
1242 * ADD of the object.
1244 ret
= replmd_add_fix_la(module
, msg
->elements
,
1249 if (ret
!= LDB_SUCCESS
) {
1253 /* linked attributes are not stored in
1254 replPropertyMetaData in FL above w2k */
1259 m
->attid
= dsdb_attribute_get_attid(sa
, is_schema_nc
);
1261 if (m
->attid
== DRSUAPI_ATTID_isDeleted
) {
1262 const struct ldb_val
*rdn_val
= ldb_dn_get_rdn_val(msg
->dn
);
1265 if (rdn_val
== NULL
) {
1268 return LDB_ERR_OPERATIONS_ERROR
;
1271 rdn
= (const char*)rdn_val
->data
;
1272 if (strcmp(rdn
, "Deleted Objects") == 0) {
1274 * Set the originating_change_time to 29/12/9999 at 23:59:59
1275 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1277 m
->originating_change_time
= DELETED_OBJECT_CONTAINER_CHANGE_TIME
;
1279 m
->originating_change_time
= now
;
1282 m
->originating_change_time
= now
;
1284 m
->originating_invocation_id
= ac
->our_invocation_id
;
1285 m
->originating_usn
= ac
->seq_num
;
1286 m
->local_usn
= ac
->seq_num
;
1289 if (!(e
->flags
& DSDB_FLAG_INTERNAL_FORCE_META_DATA
)) {
1294 e
->flags
&= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA
;
1296 if (e
->num_values
!= 0) {
1301 ldb_msg_remove_element(msg
, e
);
1304 /* fix meta data count */
1305 nmd
.ctr
.ctr1
.count
= ni
;
1308 * sort meta data array
1310 ret
= replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb
, &nmd
.ctr
.ctr1
, msg
->dn
);
1311 if (ret
!= LDB_SUCCESS
) {
1312 ldb_asprintf_errstring(ldb
, "%s: error during direct ADD: %s", __func__
, ldb_errstring(ldb
));
1317 /* generated NDR encoded values */
1318 ndr_err
= ndr_push_struct_blob(&nmd_value
, msg
,
1320 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
1321 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1324 return LDB_ERR_OPERATIONS_ERROR
;
1328 * add the autogenerated values
1330 ret
= dsdb_msg_add_guid(msg
, &guid
, "objectGUID");
1331 if (ret
!= LDB_SUCCESS
) {
1336 ret
= ldb_msg_add_string(msg
, "whenChanged", time_str
);
1337 if (ret
!= LDB_SUCCESS
) {
1342 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNCreated", ac
->seq_num
);
1343 if (ret
!= LDB_SUCCESS
) {
1348 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNChanged", ac
->seq_num
);
1349 if (ret
!= LDB_SUCCESS
) {
1354 ret
= ldb_msg_add_value(msg
, "replPropertyMetaData", &nmd_value
, NULL
);
1355 if (ret
!= LDB_SUCCESS
) {
1362 * sort the attributes by attid before storing the object
1364 replmd_ldb_message_sort(msg
, ac
->schema
);
1367 * Assert that we do have an objectClass
1369 objectclass_el
= ldb_msg_find_element(msg
, "objectClass");
1370 if (objectclass_el
== NULL
) {
1371 ldb_asprintf_errstring(ldb
, __location__
1372 ": objectClass missing on %s\n",
1373 ldb_dn_get_linearized(msg
->dn
));
1375 return LDB_ERR_OBJECT_CLASS_VIOLATION
;
1377 is_urgent
= replmd_check_urgent_objectclass(objectclass_el
,
1378 REPL_URGENT_ON_CREATE
);
1380 ac
->is_urgent
= is_urgent
;
1381 ret
= ldb_build_add_req(&down_req
, ldb
, ac
,
1384 ac
, replmd_op_callback
,
1387 LDB_REQ_SET_LOCATION(down_req
);
1388 if (ret
!= LDB_SUCCESS
) {
1393 /* current partition control is needed by "replmd_op_callback" */
1394 if (ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
) == NULL
) {
1395 ret
= ldb_request_add_control(down_req
,
1396 DSDB_CONTROL_CURRENT_PARTITION_OID
,
1398 if (ret
!= LDB_SUCCESS
) {
1404 if (functional_level
== DS_DOMAIN_FUNCTION_2000
) {
1405 ret
= ldb_request_add_control(down_req
, DSDB_CONTROL_APPLY_LINKS
, false, NULL
);
1406 if (ret
!= LDB_SUCCESS
) {
1412 /* mark the control done */
1414 control
->critical
= 0;
1416 /* go on with the call chain */
1417 return ldb_next_request(module
, down_req
);
1422 * update the replPropertyMetaData for one element
1424 static int replmd_update_rpmd_element(struct ldb_context
*ldb
,
1425 struct ldb_message
*msg
,
1426 struct ldb_message_element
*el
,
1427 struct ldb_message_element
*old_el
,
1428 struct replPropertyMetaDataBlob
*omd
,
1429 const struct dsdb_schema
*schema
,
1431 const struct GUID
*our_invocation_id
,
1434 bool is_forced_rodc
,
1435 struct ldb_request
*req
)
1438 const struct dsdb_attribute
*a
;
1439 struct replPropertyMetaData1
*md1
;
1440 bool may_skip
= false;
1443 a
= dsdb_attribute_by_lDAPDisplayName(schema
, el
->name
);
1445 if (ldb_request_get_control(req
, LDB_CONTROL_RELAX_OID
)) {
1446 /* allow this to make it possible for dbcheck
1447 to remove bad attributes */
1451 DEBUG(0,(__location__
": Unable to find attribute %s in schema\n",
1453 return LDB_ERR_OPERATIONS_ERROR
;
1456 attid
= dsdb_attribute_get_attid(a
, is_schema_nc
);
1458 if ((a
->systemFlags
& DS_FLAG_ATTR_NOT_REPLICATED
) || (a
->systemFlags
& DS_FLAG_ATTR_IS_CONSTRUCTED
)) {
1463 * if the attribute's value haven't changed, and this isn't
1464 * just a delete of everything then return LDB_SUCCESS Unless
1465 * we have the provision control or if the attribute is
1466 * interSiteTopologyGenerator as this page explain:
1467 * http://support.microsoft.com/kb/224815 this attribute is
1468 * periodicaly written by the DC responsible for the intersite
1469 * generation in a given site
1471 * Unchanged could be deleting or replacing an already-gone
1472 * thing with an unconstrained delete/empty replace or a
1473 * replace with the same value, but not an add with the same
1474 * value because that could be about adding a duplicate (which
1475 * is for someone else to error out on).
1477 if (old_el
!= NULL
&& ldb_msg_element_equal_ordered(el
, old_el
)) {
1478 if (LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_REPLACE
) {
1481 } else if (old_el
== NULL
&& el
->num_values
== 0) {
1482 if (LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_REPLACE
) {
1484 } else if (LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_DELETE
) {
1487 } else if (a
->linkID
!= 0 && LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_DELETE
&&
1488 ldb_request_get_control(req
, DSDB_CONTROL_REPLMD_VANISH_LINKS
) != NULL
) {
1490 * We intentionally skip the version bump when attempting to
1493 * The control is set by dbcheck and expunge-tombstones which
1494 * both attempt to be non-replicating. Otherwise, making an
1495 * alteration to the replication state would trigger a
1496 * broadcast of all expunged objects.
1501 if (el
->flags
& DSDB_FLAG_INTERNAL_FORCE_META_DATA
) {
1503 el
->flags
&= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA
;
1507 if (strcmp(el
->name
, "interSiteTopologyGenerator") != 0 &&
1508 !ldb_request_get_control(req
, LDB_CONTROL_PROVISION_OID
)) {
1510 * allow this to make it possible for dbcheck
1511 * to rebuild broken metadata
1517 for (i
=0; i
<omd
->ctr
.ctr1
.count
; i
++) {
1519 * First check if we find it under the msDS-IntID,
1520 * then check if we find it under the OID and
1523 * This allows the administrator to simply re-write
1524 * the attributes and so restore replication, which is
1525 * likely what they will try to do.
1527 if (attid
== omd
->ctr
.ctr1
.array
[i
].attid
) {
1531 if (a
->attributeID_id
== omd
->ctr
.ctr1
.array
[i
].attid
) {
1536 if (a
->linkID
!= 0 && dsdb_functional_level(ldb
) > DS_DOMAIN_FUNCTION_2000
) {
1537 /* linked attributes are not stored in
1538 replPropertyMetaData in FL above w2k, but we do
1539 raise the seqnum for the object */
1540 if (*seq_num
== 0 &&
1541 ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, seq_num
) != LDB_SUCCESS
) {
1542 return LDB_ERR_OPERATIONS_ERROR
;
1547 if (i
== omd
->ctr
.ctr1
.count
) {
1548 /* we need to add a new one */
1549 omd
->ctr
.ctr1
.array
= talloc_realloc(msg
, omd
->ctr
.ctr1
.array
,
1550 struct replPropertyMetaData1
, omd
->ctr
.ctr1
.count
+1);
1551 if (omd
->ctr
.ctr1
.array
== NULL
) {
1553 return LDB_ERR_OPERATIONS_ERROR
;
1555 omd
->ctr
.ctr1
.count
++;
1556 ZERO_STRUCT(omd
->ctr
.ctr1
.array
[i
]);
1559 /* Get a new sequence number from the backend. We only do this
1560 * if we have a change that requires a new
1561 * replPropertyMetaData element
1563 if (*seq_num
== 0) {
1564 int ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, seq_num
);
1565 if (ret
!= LDB_SUCCESS
) {
1566 return LDB_ERR_OPERATIONS_ERROR
;
1570 md1
= &omd
->ctr
.ctr1
.array
[i
];
1574 if (md1
->attid
== DRSUAPI_ATTID_isDeleted
) {
1575 const struct ldb_val
*rdn_val
= ldb_dn_get_rdn_val(msg
->dn
);
1578 if (rdn_val
== NULL
) {
1580 return LDB_ERR_OPERATIONS_ERROR
;
1583 rdn
= (const char*)rdn_val
->data
;
1584 if (strcmp(rdn
, "Deleted Objects") == 0) {
1586 * Set the originating_change_time to 29/12/9999 at 23:59:59
1587 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1589 md1
->originating_change_time
= DELETED_OBJECT_CONTAINER_CHANGE_TIME
;
1591 md1
->originating_change_time
= now
;
1594 md1
->originating_change_time
= now
;
1596 md1
->originating_invocation_id
= *our_invocation_id
;
1597 md1
->originating_usn
= *seq_num
;
1598 md1
->local_usn
= *seq_num
;
1600 if (is_forced_rodc
) {
1601 /* Force version to 0 to be overriden later via replication */
1609 * Bump the replPropertyMetaData version on an attribute, and if it
1610 * has changed (or forced by leaving rdn_old NULL), update the value
1613 * This is important, as calling a modify operation may not change the
1614 * version number if the values appear unchanged, but a rename between
1615 * parents bumps this value.
1618 static int replmd_update_rpmd_rdn_attr(struct ldb_context
*ldb
,
1619 struct ldb_message
*msg
,
1620 const struct ldb_val
*rdn_new
,
1621 const struct ldb_val
*rdn_old
,
1622 struct replPropertyMetaDataBlob
*omd
,
1623 struct replmd_replicated_request
*ar
,
1626 bool is_forced_rodc
)
1628 const char *rdn_name
= ldb_dn_get_rdn_name(msg
->dn
);
1629 const struct dsdb_attribute
*rdn_attr
=
1630 dsdb_attribute_by_lDAPDisplayName(ar
->schema
, rdn_name
);
1631 const char *attr_name
= rdn_attr
!= NULL
?
1632 rdn_attr
->lDAPDisplayName
:
1634 struct ldb_message_element new_el
= {
1635 .flags
= LDB_FLAG_MOD_REPLACE
,
1638 .values
= discard_const_p(struct ldb_val
, rdn_new
)
1640 struct ldb_message_element old_el
= {
1641 .flags
= LDB_FLAG_MOD_REPLACE
,
1643 .num_values
= rdn_old
? 1 : 0,
1644 .values
= discard_const_p(struct ldb_val
, rdn_old
)
1647 if (ldb_msg_element_equal_ordered(&new_el
, &old_el
) == false) {
1648 int ret
= ldb_msg_add(msg
, &new_el
, LDB_FLAG_MOD_REPLACE
);
1649 if (ret
!= LDB_SUCCESS
) {
1650 return ldb_oom(ldb
);
1654 return replmd_update_rpmd_element(ldb
, msg
, &new_el
, NULL
,
1655 omd
, ar
->schema
, &ar
->seq_num
,
1656 &ar
->our_invocation_id
,
1657 now
, is_schema_nc
, is_forced_rodc
,
1662 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd
)
1664 uint32_t count
= omd
.ctr
.ctr1
.count
;
1667 for (i
=0; i
< count
; i
++) {
1668 struct replPropertyMetaData1 m
= omd
.ctr
.ctr1
.array
[i
];
1669 if (max
< m
.local_usn
) {
1677 * update the replPropertyMetaData object each time we modify an
1678 * object. This is needed for DRS replication, as the merge on the
1679 * client is based on this object
1681 static int replmd_update_rpmd(struct ldb_module
*module
,
1682 const struct dsdb_schema
*schema
,
1683 struct ldb_request
*req
,
1684 const char * const *rename_attrs
,
1685 struct ldb_message
*msg
, uint64_t *seq_num
,
1686 time_t t
, bool is_schema_nc
,
1687 bool *is_urgent
, bool *rodc
)
1689 const struct ldb_val
*omd_value
;
1690 enum ndr_err_code ndr_err
;
1691 struct replPropertyMetaDataBlob omd
;
1694 const struct GUID
*our_invocation_id
;
1696 const char * const *attrs
= NULL
;
1697 const char * const attrs2
[] = { "uSNChanged", "objectClass", "instanceType", NULL
};
1698 struct ldb_result
*res
;
1699 struct ldb_context
*ldb
;
1700 struct ldb_message_element
*objectclass_el
;
1701 enum urgent_situation situation
;
1702 bool rmd_is_provided
;
1703 bool rmd_is_just_resorted
= false;
1704 const char *not_rename_attrs
[4 + msg
->num_elements
];
1705 bool is_forced_rodc
= false;
1708 attrs
= rename_attrs
;
1710 for (i
= 0; i
< msg
->num_elements
; i
++) {
1711 not_rename_attrs
[i
] = msg
->elements
[i
].name
;
1713 not_rename_attrs
[i
] = "replPropertyMetaData";
1714 not_rename_attrs
[i
+1] = "objectClass";
1715 not_rename_attrs
[i
+2] = "instanceType";
1716 not_rename_attrs
[i
+3] = NULL
;
1717 attrs
= not_rename_attrs
;
1720 ldb
= ldb_module_get_ctx(module
);
1722 ret
= samdb_rodc(ldb
, rodc
);
1723 if (ret
!= LDB_SUCCESS
) {
1724 DEBUG(4, (__location__
": unable to tell if we are an RODC\n"));
1729 ldb_request_get_control(req
, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE
)) {
1730 is_forced_rodc
= true;
1733 our_invocation_id
= samdb_ntds_invocation_id(ldb
);
1734 if (!our_invocation_id
) {
1735 /* this happens during an initial vampire while
1736 updating the schema */
1737 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1741 unix_to_nt_time(&now
, t
);
1743 if (ldb_request_get_control(req
, DSDB_CONTROL_CHANGEREPLMETADATA_OID
)) {
1744 rmd_is_provided
= true;
1745 if (ldb_request_get_control(req
, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID
)) {
1746 rmd_is_just_resorted
= true;
1749 rmd_is_provided
= false;
1752 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1753 * otherwise we consider we are updating */
1754 if (ldb_msg_check_string_attribute(msg
, "isDeleted", "TRUE")) {
1755 situation
= REPL_URGENT_ON_DELETE
;
1756 } else if (rename_attrs
) {
1757 situation
= REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_DELETE
;
1759 situation
= REPL_URGENT_ON_UPDATE
;
1762 if (rmd_is_provided
) {
1763 /* In this case the change_replmetadata control was supplied */
1764 /* We check that it's the only attribute that is provided
1765 * (it's a rare case so it's better to keep the code simplier)
1766 * We also check that the highest local_usn is bigger or the same as
1769 if( msg
->num_elements
!= 1 ||
1770 strncmp(msg
->elements
[0].name
,
1771 "replPropertyMetaData", 20) ) {
1772 DEBUG(0,(__location__
": changereplmetada control called without "\
1773 "a specified replPropertyMetaData attribute or with others\n"));
1774 return LDB_ERR_OPERATIONS_ERROR
;
1776 if (situation
!= REPL_URGENT_ON_UPDATE
) {
1777 DEBUG(0,(__location__
": changereplmetada control can't be called when deleting an object\n"));
1778 return LDB_ERR_OPERATIONS_ERROR
;
1780 omd_value
= ldb_msg_find_ldb_val(msg
, "replPropertyMetaData");
1782 DEBUG(0,(__location__
": replPropertyMetaData was not specified for Object %s\n",
1783 ldb_dn_get_linearized(msg
->dn
)));
1784 return LDB_ERR_OPERATIONS_ERROR
;
1786 ndr_err
= ndr_pull_struct_blob(omd_value
, msg
, &omd
,
1787 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
1788 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1789 DEBUG(0,(__location__
": Failed to parse replPropertyMetaData for %s\n",
1790 ldb_dn_get_linearized(msg
->dn
)));
1791 return LDB_ERR_OPERATIONS_ERROR
;
1794 ret
= dsdb_module_search_dn(module
, msg
, &res
, msg
->dn
, attrs2
,
1795 DSDB_FLAG_NEXT_MODULE
|
1796 DSDB_SEARCH_SHOW_RECYCLED
|
1797 DSDB_SEARCH_SHOW_EXTENDED_DN
|
1798 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
|
1799 DSDB_SEARCH_REVEAL_INTERNALS
, req
);
1801 if (ret
!= LDB_SUCCESS
) {
1805 if (rmd_is_just_resorted
== false) {
1806 *seq_num
= find_max_local_usn(omd
);
1808 db_seq
= ldb_msg_find_attr_as_uint64(res
->msgs
[0], "uSNChanged", 0);
1811 * The test here now allows for a new
1812 * replPropertyMetaData with no change, if was
1813 * just dbcheck re-sorting the values.
1815 if (*seq_num
<= db_seq
) {
1816 DEBUG(0,(__location__
": changereplmetada control provided but max(local_usn)" \
1817 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1818 (long long)*seq_num
, (long long)db_seq
));
1819 return LDB_ERR_OPERATIONS_ERROR
;
1824 /* search for the existing replPropertyMetaDataBlob. We need
1825 * to use REVEAL and ask for DNs in storage format to support
1826 * the check for values being the same in
1827 * replmd_update_rpmd_element()
1829 ret
= dsdb_module_search_dn(module
, msg
, &res
, msg
->dn
, attrs
,
1830 DSDB_FLAG_NEXT_MODULE
|
1831 DSDB_SEARCH_SHOW_RECYCLED
|
1832 DSDB_SEARCH_SHOW_EXTENDED_DN
|
1833 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
|
1834 DSDB_SEARCH_REVEAL_INTERNALS
, req
);
1835 if (ret
!= LDB_SUCCESS
) {
1839 omd_value
= ldb_msg_find_ldb_val(res
->msgs
[0], "replPropertyMetaData");
1841 DEBUG(0,(__location__
": Object %s does not have a replPropertyMetaData attribute\n",
1842 ldb_dn_get_linearized(msg
->dn
)));
1843 return LDB_ERR_OPERATIONS_ERROR
;
1846 ndr_err
= ndr_pull_struct_blob(omd_value
, msg
, &omd
,
1847 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
1848 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1849 DEBUG(0,(__location__
": Failed to parse replPropertyMetaData for %s\n",
1850 ldb_dn_get_linearized(msg
->dn
)));
1851 return LDB_ERR_OPERATIONS_ERROR
;
1854 if (omd
.version
!= 1) {
1855 DEBUG(0,(__location__
": bad version %u in replPropertyMetaData for %s\n",
1856 omd
.version
, ldb_dn_get_linearized(msg
->dn
)));
1857 return LDB_ERR_OPERATIONS_ERROR
;
1860 for (i
=0; i
<msg
->num_elements
;) {
1861 struct ldb_message_element
*el
= &msg
->elements
[i
];
1862 struct ldb_message_element
*old_el
;
1864 old_el
= ldb_msg_find_element(res
->msgs
[0], el
->name
);
1865 ret
= replmd_update_rpmd_element(ldb
, msg
, el
, old_el
,
1866 &omd
, schema
, seq_num
,
1871 if (ret
!= LDB_SUCCESS
) {
1875 if (!*is_urgent
&& (situation
== REPL_URGENT_ON_UPDATE
)) {
1876 *is_urgent
= replmd_check_urgent_attribute(el
);
1879 if (!(el
->flags
& DSDB_FLAG_INTERNAL_FORCE_META_DATA
)) {
1884 el
->flags
&= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA
;
1886 if (el
->num_values
!= 0) {
1891 ldb_msg_remove_element(msg
, el
);
1896 * Assert that we have an objectClass attribute - this is major
1897 * corruption if we don't have this!
1899 objectclass_el
= ldb_msg_find_element(res
->msgs
[0], "objectClass");
1900 if (objectclass_el
!= NULL
) {
1902 * Now check if this objectClass means we need to do urgent replication
1904 if (!*is_urgent
&& replmd_check_urgent_objectclass(objectclass_el
,
1908 } else if (!ldb_request_get_control(req
, DSDB_CONTROL_DBCHECK
)) {
1909 ldb_asprintf_errstring(ldb
, __location__
1910 ": objectClass missing on %s\n",
1911 ldb_dn_get_linearized(msg
->dn
));
1912 return LDB_ERR_OBJECT_CLASS_VIOLATION
;
1916 * replmd_update_rpmd_element has done an update if the
1919 if (*seq_num
!= 0 || rmd_is_just_resorted
== true) {
1920 struct ldb_val
*md_value
;
1921 struct ldb_message_element
*el
;
1923 /*if we are RODC and this is a DRSR update then its ok*/
1924 if (!ldb_request_get_control(req
, DSDB_CONTROL_REPLICATED_UPDATE_OID
)
1925 && !ldb_request_get_control(req
, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA
)
1926 && !is_forced_rodc
) {
1927 unsigned instanceType
;
1930 ldb_set_errstring(ldb
, "RODC modify is forbidden!");
1931 return LDB_ERR_REFERRAL
;
1934 instanceType
= ldb_msg_find_attr_as_uint(res
->msgs
[0], "instanceType", INSTANCE_TYPE_WRITE
);
1935 if (!(instanceType
& INSTANCE_TYPE_WRITE
)) {
1936 return ldb_error(ldb
, LDB_ERR_UNWILLING_TO_PERFORM
,
1937 "cannot change replicated attribute on partial replica");
1941 md_value
= talloc(msg
, struct ldb_val
);
1942 if (md_value
== NULL
) {
1944 return LDB_ERR_OPERATIONS_ERROR
;
1947 ret
= replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb
, &omd
.ctr
.ctr1
, msg
->dn
);
1948 if (ret
!= LDB_SUCCESS
) {
1949 ldb_asprintf_errstring(ldb
, "%s: %s", __func__
, ldb_errstring(ldb
));
1953 ndr_err
= ndr_push_struct_blob(md_value
, msg
, &omd
,
1954 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
1955 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1956 DEBUG(0,(__location__
": Failed to marshall replPropertyMetaData for %s\n",
1957 ldb_dn_get_linearized(msg
->dn
)));
1958 return LDB_ERR_OPERATIONS_ERROR
;
1961 ret
= ldb_msg_add_empty(msg
, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE
, &el
);
1962 if (ret
!= LDB_SUCCESS
) {
1963 DEBUG(0,(__location__
": Failed to add updated replPropertyMetaData %s\n",
1964 ldb_dn_get_linearized(msg
->dn
)));
1969 el
->values
= md_value
;
1975 static int parsed_dn_compare(struct parsed_dn
*pdn1
, struct parsed_dn
*pdn2
)
1977 int ret
= ndr_guid_compare(&pdn1
->guid
, &pdn2
->guid
);
1979 return data_blob_cmp(&pdn1
->dsdb_dn
->extra_part
,
1980 &pdn2
->dsdb_dn
->extra_part
);
1986 get a series of message element values as an array of DNs and GUIDs
1987 the result is sorted by GUID
1989 static int get_parsed_dns(struct ldb_module
*module
, TALLOC_CTX
*mem_ctx
,
1990 struct ldb_message_element
*el
, struct parsed_dn
**pdn
,
1991 const char *ldap_oid
, struct ldb_request
*parent
)
1994 bool values_are_sorted
= true;
1995 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
2002 (*pdn
) = talloc_array(mem_ctx
, struct parsed_dn
, el
->num_values
);
2004 ldb_module_oom(module
);
2005 return LDB_ERR_OPERATIONS_ERROR
;
2008 for (i
=0; i
<el
->num_values
; i
++) {
2009 struct ldb_val
*v
= &el
->values
[i
];
2012 struct parsed_dn
*p
;
2016 p
->dsdb_dn
= dsdb_dn_parse(*pdn
, ldb
, v
, ldap_oid
);
2017 if (p
->dsdb_dn
== NULL
) {
2018 return LDB_ERR_INVALID_DN_SYNTAX
;
2021 dn
= p
->dsdb_dn
->dn
;
2023 status
= dsdb_get_extended_dn_guid(dn
, &p
->guid
, "GUID");
2024 if (NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
) ||
2025 unlikely(GUID_all_zero(&p
->guid
))) {
2026 /* we got a DN without a GUID - go find the GUID */
2027 int ret
= dsdb_module_guid_by_dn(module
, dn
, &p
->guid
, parent
);
2028 if (ret
!= LDB_SUCCESS
) {
2029 ldb_asprintf_errstring(ldb
, "Unable to find GUID for DN %s\n",
2030 ldb_dn_get_linearized(dn
));
2031 if (ret
== LDB_ERR_NO_SUCH_OBJECT
&&
2032 LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_DELETE
&&
2033 ldb_attr_cmp(el
->name
, "member") == 0) {
2034 return LDB_ERR_UNWILLING_TO_PERFORM
;
2038 ret
= dsdb_set_extended_dn_guid(dn
, &p
->guid
, "GUID");
2039 if (ret
!= LDB_SUCCESS
) {
2042 } else if (!NT_STATUS_IS_OK(status
)) {
2043 return LDB_ERR_OPERATIONS_ERROR
;
2045 if (i
> 0 && values_are_sorted
) {
2046 int cmp
= parsed_dn_compare(p
, &(*pdn
)[i
- 1]);
2048 values_are_sorted
= false;
2051 /* keep a pointer to the original ldb_val */
2054 if (! values_are_sorted
) {
2055 TYPESAFE_QSORT(*pdn
, el
->num_values
, parsed_dn_compare
);
2061 * Get a series of trusted message element values. The result is sorted by
2062 * GUID, even though the GUIDs might not be known. That works because we trust
2063 * the database to give us the elements like that if the
2064 * replmd_private->sorted_links flag is set.
2066 * We also ensure that the links are in the Functional Level 2003
2067 * linked attributes format.
2069 static int get_parsed_dns_trusted(struct ldb_module
*module
,
2070 struct replmd_private
*replmd_private
,
2071 TALLOC_CTX
*mem_ctx
,
2072 struct ldb_message_element
*el
,
2073 struct parsed_dn
**pdn
,
2074 const char *ldap_oid
,
2075 struct ldb_request
*parent
)
2084 if (!replmd_private
->sorted_links
) {
2085 /* We need to sort the list. This is the slow old path we want
2088 ret
= get_parsed_dns(module
, mem_ctx
, el
, pdn
, ldap_oid
,
2090 if (ret
!= LDB_SUCCESS
) {
2094 /* Here we get a list of 'struct parsed_dns' without the parsing */
2095 *pdn
= talloc_zero_array(mem_ctx
, struct parsed_dn
,
2098 ldb_module_oom(module
);
2099 return LDB_ERR_OPERATIONS_ERROR
;
2102 for (i
= 0; i
< el
->num_values
; i
++) {
2103 (*pdn
)[i
].v
= &el
->values
[i
];
2108 * This upgrades links to FL2003 style, and sorts the result
2109 * if that was needed.
2111 * TODO: Add a database feature that asserts we have no FL2000
2112 * style links to avoid this check or add a feature that
2113 * uses a similar check to find sorted/unsorted links
2114 * for an on-the-fly upgrade.
2117 ret
= replmd_check_upgrade_links(ldb_module_get_ctx(module
),
2118 *pdn
, el
->num_values
,
2121 if (ret
!= LDB_SUCCESS
) {
2129 build a new extended DN, including all meta data fields
2131 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2132 RMD_ADDTIME = originating_add_time
2133 RMD_INVOCID = originating_invocation_id
2134 RMD_CHANGETIME = originating_change_time
2135 RMD_ORIGINATING_USN = originating_usn
2136 RMD_LOCAL_USN = local_usn
2137 RMD_VERSION = version
2139 static int replmd_build_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
, struct dsdb_dn
*dsdb_dn
,
2140 const struct GUID
*invocation_id
, uint64_t seq_num
,
2141 uint64_t local_usn
, NTTIME nttime
, uint32_t version
, bool deleted
)
2143 struct ldb_dn
*dn
= dsdb_dn
->dn
;
2144 const char *tstring
, *usn_string
, *flags_string
;
2145 struct ldb_val tval
;
2147 struct ldb_val usnv
, local_usnv
;
2148 struct ldb_val vers
, flagsv
;
2151 const char *dnstring
;
2153 uint32_t rmd_flags
= deleted
?DSDB_RMD_FLAG_DELETED
:0;
2155 tstring
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)nttime
);
2157 return LDB_ERR_OPERATIONS_ERROR
;
2159 tval
= data_blob_string_const(tstring
);
2161 usn_string
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)seq_num
);
2163 return LDB_ERR_OPERATIONS_ERROR
;
2165 usnv
= data_blob_string_const(usn_string
);
2167 usn_string
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)local_usn
);
2169 return LDB_ERR_OPERATIONS_ERROR
;
2171 local_usnv
= data_blob_string_const(usn_string
);
2173 vstring
= talloc_asprintf(mem_ctx
, "%lu", (unsigned long)version
);
2175 return LDB_ERR_OPERATIONS_ERROR
;
2177 vers
= data_blob_string_const(vstring
);
2179 status
= GUID_to_ndr_blob(invocation_id
, dn
, &iid
);
2180 if (!NT_STATUS_IS_OK(status
)) {
2181 return LDB_ERR_OPERATIONS_ERROR
;
2184 flags_string
= talloc_asprintf(mem_ctx
, "%u", rmd_flags
);
2185 if (!flags_string
) {
2186 return LDB_ERR_OPERATIONS_ERROR
;
2188 flagsv
= data_blob_string_const(flags_string
);
2190 ret
= ldb_dn_set_extended_component(dn
, "RMD_FLAGS", &flagsv
);
2191 if (ret
!= LDB_SUCCESS
) return ret
;
2192 ret
= ldb_dn_set_extended_component(dn
, "RMD_ADDTIME", &tval
);
2193 if (ret
!= LDB_SUCCESS
) return ret
;
2194 ret
= ldb_dn_set_extended_component(dn
, "RMD_INVOCID", &iid
);
2195 if (ret
!= LDB_SUCCESS
) return ret
;
2196 ret
= ldb_dn_set_extended_component(dn
, "RMD_CHANGETIME", &tval
);
2197 if (ret
!= LDB_SUCCESS
) return ret
;
2198 ret
= ldb_dn_set_extended_component(dn
, "RMD_LOCAL_USN", &local_usnv
);
2199 if (ret
!= LDB_SUCCESS
) return ret
;
2200 ret
= ldb_dn_set_extended_component(dn
, "RMD_ORIGINATING_USN", &usnv
);
2201 if (ret
!= LDB_SUCCESS
) return ret
;
2202 ret
= ldb_dn_set_extended_component(dn
, "RMD_VERSION", &vers
);
2203 if (ret
!= LDB_SUCCESS
) return ret
;
2205 dnstring
= dsdb_dn_get_extended_linearized(mem_ctx
, dsdb_dn
, 1);
2206 if (dnstring
== NULL
) {
2207 return LDB_ERR_OPERATIONS_ERROR
;
2209 *v
= data_blob_string_const(dnstring
);
2214 static int replmd_update_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
, struct dsdb_dn
*dsdb_dn
,
2215 struct dsdb_dn
*old_dsdb_dn
, const struct GUID
*invocation_id
,
2216 uint64_t seq_num
, uint64_t local_usn
, NTTIME nttime
,
2220 check if any links need upgrading from w2k format
2222 static int replmd_check_upgrade_links(struct ldb_context
*ldb
,
2223 struct parsed_dn
*dns
, uint32_t count
,
2224 struct ldb_message_element
*el
,
2225 const char *ldap_oid
)
2228 const struct GUID
*invocation_id
= NULL
;
2229 for (i
=0; i
<count
; i
++) {
2233 if (dns
[i
].dsdb_dn
== NULL
) {
2234 ret
= really_parse_trusted_dn(dns
, ldb
, &dns
[i
],
2236 if (ret
!= LDB_SUCCESS
) {
2237 return LDB_ERR_INVALID_DN_SYNTAX
;
2241 status
= dsdb_get_extended_dn_uint32(dns
[i
].dsdb_dn
->dn
,
2242 &version
, "RMD_VERSION");
2243 if (!NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)) {
2245 * We optimistically assume they are all the same; if
2246 * the first one is fixed, they are all fixed.
2248 * If the first one was *not* fixed and we find a
2249 * later one that is, that is an occasion to shout
2255 DEBUG(0, ("Mixed w2k and fixed format "
2256 "linked attributes\n"));
2260 if (invocation_id
== NULL
) {
2261 invocation_id
= samdb_ntds_invocation_id(ldb
);
2262 if (invocation_id
== NULL
) {
2263 return LDB_ERR_OPERATIONS_ERROR
;
2268 /* it's an old one that needs upgrading */
2269 ret
= replmd_update_la_val(el
->values
, dns
[i
].v
,
2270 dns
[i
].dsdb_dn
, dns
[i
].dsdb_dn
,
2271 invocation_id
, 1, 1, 0, false);
2272 if (ret
!= LDB_SUCCESS
) {
2278 * This sort() is critical for the operation of
2279 * get_parsed_dns_trusted() because callers of this function
2280 * expect a sorted list, and FL2000 style links are not
2281 * sorted. In particular, as well as the upgrade case,
2282 * get_parsed_dns_trusted() is called from
2283 * replmd_delete_remove_link() even in FL2000 mode
2285 * We do not normally pay the cost of the qsort() due to the
2286 * early return in the RMD_VERSION found case.
2288 TYPESAFE_QSORT(dns
, count
, parsed_dn_compare
);
2293 Sets the value for a linked attribute, including all meta data fields
2295 see replmd_build_la_val for value names
2297 static int replmd_set_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
, struct dsdb_dn
*dsdb_dn
,
2298 struct dsdb_dn
*old_dsdb_dn
, const struct GUID
*invocation_id
,
2299 uint64_t usn
, uint64_t local_usn
, NTTIME nttime
,
2300 uint32_t version
, bool deleted
)
2302 struct ldb_dn
*dn
= dsdb_dn
->dn
;
2303 const char *tstring
, *usn_string
, *flags_string
;
2304 struct ldb_val tval
;
2306 struct ldb_val usnv
, local_usnv
;
2307 struct ldb_val vers
, flagsv
;
2308 const struct ldb_val
*old_addtime
= NULL
;
2311 const char *dnstring
;
2313 uint32_t rmd_flags
= deleted
?DSDB_RMD_FLAG_DELETED
:0;
2315 tstring
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)nttime
);
2317 return LDB_ERR_OPERATIONS_ERROR
;
2319 tval
= data_blob_string_const(tstring
);
2321 usn_string
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)usn
);
2323 return LDB_ERR_OPERATIONS_ERROR
;
2325 usnv
= data_blob_string_const(usn_string
);
2327 usn_string
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)local_usn
);
2329 return LDB_ERR_OPERATIONS_ERROR
;
2331 local_usnv
= data_blob_string_const(usn_string
);
2333 status
= GUID_to_ndr_blob(invocation_id
, dn
, &iid
);
2334 if (!NT_STATUS_IS_OK(status
)) {
2335 return LDB_ERR_OPERATIONS_ERROR
;
2338 flags_string
= talloc_asprintf(mem_ctx
, "%u", rmd_flags
);
2339 if (!flags_string
) {
2340 return LDB_ERR_OPERATIONS_ERROR
;
2342 flagsv
= data_blob_string_const(flags_string
);
2344 ret
= ldb_dn_set_extended_component(dn
, "RMD_FLAGS", &flagsv
);
2345 if (ret
!= LDB_SUCCESS
) return ret
;
2347 /* get the ADDTIME from the original */
2348 if (old_dsdb_dn
!= NULL
) {
2349 old_addtime
= ldb_dn_get_extended_component(old_dsdb_dn
->dn
,
2352 if (old_addtime
== NULL
) {
2353 old_addtime
= &tval
;
2355 if (dsdb_dn
!= old_dsdb_dn
||
2356 ldb_dn_get_extended_component(dn
, "RMD_ADDTIME") == NULL
) {
2357 ret
= ldb_dn_set_extended_component(dn
, "RMD_ADDTIME", old_addtime
);
2358 if (ret
!= LDB_SUCCESS
) return ret
;
2361 /* use our invocation id */
2362 ret
= ldb_dn_set_extended_component(dn
, "RMD_INVOCID", &iid
);
2363 if (ret
!= LDB_SUCCESS
) return ret
;
2365 /* changetime is the current time */
2366 ret
= ldb_dn_set_extended_component(dn
, "RMD_CHANGETIME", &tval
);
2367 if (ret
!= LDB_SUCCESS
) return ret
;
2369 /* update the USN */
2370 ret
= ldb_dn_set_extended_component(dn
, "RMD_ORIGINATING_USN", &usnv
);
2371 if (ret
!= LDB_SUCCESS
) return ret
;
2373 ret
= ldb_dn_set_extended_component(dn
, "RMD_LOCAL_USN", &local_usnv
);
2374 if (ret
!= LDB_SUCCESS
) return ret
;
2376 vstring
= talloc_asprintf(mem_ctx
, "%lu", (unsigned long)version
);
2377 vers
= data_blob_string_const(vstring
);
2378 ret
= ldb_dn_set_extended_component(dn
, "RMD_VERSION", &vers
);
2379 if (ret
!= LDB_SUCCESS
) return ret
;
2381 dnstring
= dsdb_dn_get_extended_linearized(mem_ctx
, dsdb_dn
, 1);
2382 if (dnstring
== NULL
) {
2383 return LDB_ERR_OPERATIONS_ERROR
;
2385 *v
= data_blob_string_const(dnstring
);
2391 * Updates the value for a linked attribute, including all meta data fields
2393 static int replmd_update_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
, struct dsdb_dn
*dsdb_dn
,
2394 struct dsdb_dn
*old_dsdb_dn
, const struct GUID
*invocation_id
,
2395 uint64_t usn
, uint64_t local_usn
, NTTIME nttime
,
2398 uint32_t old_version
;
2399 uint32_t version
= 0;
2403 * We're updating the linked attribute locally, so increase the version
2404 * by 1 so that other DCs will see the change when it gets replicated out
2406 status
= dsdb_get_extended_dn_uint32(old_dsdb_dn
->dn
, &old_version
,
2409 if (NT_STATUS_IS_OK(status
)) {
2410 version
= old_version
+ 1;
2413 return replmd_set_la_val(mem_ctx
, v
, dsdb_dn
, old_dsdb_dn
, invocation_id
,
2414 usn
, local_usn
, nttime
, version
, deleted
);
2418 handle adding a linked attribute
2420 static int replmd_modify_la_add(struct ldb_module
*module
,
2421 struct replmd_private
*replmd_private
,
2422 const struct dsdb_schema
*schema
,
2423 struct ldb_message
*msg
,
2424 struct ldb_message_element
*el
,
2425 struct ldb_message_element
*old_el
,
2426 const struct dsdb_attribute
*schema_attr
,
2429 struct ldb_dn
*msg_dn
,
2430 struct ldb_request
*parent
)
2433 struct parsed_dn
*dns
, *old_dns
;
2434 TALLOC_CTX
*tmp_ctx
= talloc_new(msg
);
2436 struct ldb_val
*new_values
= NULL
;
2437 unsigned old_num_values
= old_el
? old_el
->num_values
: 0;
2438 unsigned num_values
= 0;
2439 unsigned max_num_values
;
2440 const struct GUID
*invocation_id
;
2441 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
2443 unix_to_nt_time(&now
, t
);
2445 invocation_id
= samdb_ntds_invocation_id(ldb
);
2446 if (!invocation_id
) {
2447 talloc_free(tmp_ctx
);
2448 return LDB_ERR_OPERATIONS_ERROR
;
2451 /* get the DNs to be added, fully parsed.
2453 * We need full parsing because they came off the wire and we don't
2454 * trust them, besides which we need their details to know where to put
2457 ret
= get_parsed_dns(module
, tmp_ctx
, el
, &dns
,
2458 schema_attr
->syntax
->ldap_oid
, parent
);
2459 if (ret
!= LDB_SUCCESS
) {
2460 talloc_free(tmp_ctx
);
2464 /* get the existing DNs, lazily parsed */
2465 ret
= get_parsed_dns_trusted(module
, replmd_private
,
2466 tmp_ctx
, old_el
, &old_dns
,
2467 schema_attr
->syntax
->ldap_oid
, parent
);
2469 if (ret
!= LDB_SUCCESS
) {
2470 talloc_free(tmp_ctx
);
2474 max_num_values
= old_num_values
+ el
->num_values
;
2475 if (max_num_values
< old_num_values
) {
2476 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2477 "old values: %u, new values: %u, sum: %u",
2478 old_num_values
, el
->num_values
, max_num_values
));
2479 talloc_free(tmp_ctx
);
2480 return LDB_ERR_OPERATIONS_ERROR
;
2483 new_values
= talloc_zero_array(tmp_ctx
, struct ldb_val
, max_num_values
);
2485 if (new_values
== NULL
) {
2486 ldb_module_oom(module
);
2487 talloc_free(tmp_ctx
);
2488 return LDB_ERR_OPERATIONS_ERROR
;
2492 * For each new value, find where it would go in the list. If there is
2493 * a matching GUID there, we update the existing value; otherwise we
2497 for (i
= 0; i
< el
->num_values
; i
++) {
2498 struct parsed_dn
*exact
;
2499 struct parsed_dn
*next
;
2501 int err
= parsed_dn_find(ldb
, old_dns
, old_num_values
,
2504 dns
[i
].dsdb_dn
->extra_part
, 0,
2506 schema_attr
->syntax
->ldap_oid
,
2508 if (err
!= LDB_SUCCESS
) {
2509 talloc_free(tmp_ctx
);
2513 if (exact
!= NULL
) {
2515 * We are trying to add one that exists, which is only
2516 * allowed if it was previously deleted.
2518 * When we do undelete a link we change it in place.
2519 * It will be copied across into the right spot in due
2523 rmd_flags
= dsdb_dn_rmd_flags(exact
->dsdb_dn
->dn
);
2525 if (!(rmd_flags
& DSDB_RMD_FLAG_DELETED
)) {
2526 struct GUID_txt_buf guid_str
;
2527 ldb_asprintf_errstring(ldb
,
2528 "Attribute %s already "
2529 "exists for target GUID %s",
2531 GUID_buf_string(&exact
->guid
,
2533 talloc_free(tmp_ctx
);
2534 /* error codes for 'member' need to be
2536 if (ldb_attr_cmp(el
->name
, "member") == 0) {
2537 return LDB_ERR_ENTRY_ALREADY_EXISTS
;
2539 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS
;
2543 ret
= replmd_update_la_val(new_values
, exact
->v
,
2546 invocation_id
, seq_num
,
2547 seq_num
, now
, false);
2548 if (ret
!= LDB_SUCCESS
) {
2549 talloc_free(tmp_ctx
);
2553 ret
= replmd_add_backlink(module
, replmd_private
,
2560 if (ret
!= LDB_SUCCESS
) {
2561 talloc_free(tmp_ctx
);
2567 * Here we don't have an exact match.
2569 * If next is NULL, this one goes beyond the end of the
2570 * existing list, so we need to add all of those ones first.
2572 * If next is not NULL, we need to add all the ones before
2576 offset
= old_num_values
;
2578 /* next should have been parsed, but let's make sure */
2579 if (next
->dsdb_dn
== NULL
) {
2580 ret
= really_parse_trusted_dn(tmp_ctx
, ldb
, next
,
2581 schema_attr
->syntax
->ldap_oid
);
2582 if (ret
!= LDB_SUCCESS
) {
2586 offset
= MIN(next
- old_dns
, old_num_values
);
2589 /* put all the old ones before next on the list */
2590 for (; j
< offset
; j
++) {
2591 new_values
[num_values
] = *old_dns
[j
].v
;
2595 ret
= replmd_add_backlink(module
, replmd_private
,
2600 /* Make the new linked attribute ldb_val. */
2601 ret
= replmd_build_la_val(new_values
, &new_values
[num_values
],
2602 dns
[i
].dsdb_dn
, invocation_id
,
2605 if (ret
!= LDB_SUCCESS
) {
2606 talloc_free(tmp_ctx
);
2610 if (ret
!= LDB_SUCCESS
) {
2611 talloc_free(tmp_ctx
);
2615 /* copy the rest of the old ones (if any) */
2616 for (; j
< old_num_values
; j
++) {
2617 new_values
[num_values
] = *old_dns
[j
].v
;
2621 talloc_steal(msg
->elements
, new_values
);
2622 if (old_el
!= NULL
) {
2623 talloc_steal(msg
->elements
, old_el
->values
);
2625 el
->values
= new_values
;
2626 el
->num_values
= num_values
;
2628 talloc_free(tmp_ctx
);
2630 /* we now tell the backend to replace all existing values
2631 with the one we have constructed */
2632 el
->flags
= LDB_FLAG_MOD_REPLACE
;
2639 handle deleting all active linked attributes
2641 static int replmd_modify_la_delete(struct ldb_module
*module
,
2642 struct replmd_private
*replmd_private
,
2643 const struct dsdb_schema
*schema
,
2644 struct ldb_message
*msg
,
2645 struct ldb_message_element
*el
,
2646 struct ldb_message_element
*old_el
,
2647 const struct dsdb_attribute
*schema_attr
,
2650 struct ldb_dn
*msg_dn
,
2651 struct ldb_request
*parent
)
2654 struct parsed_dn
*dns
, *old_dns
;
2655 TALLOC_CTX
*tmp_ctx
= NULL
;
2657 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
2658 struct ldb_control
*vanish_links_ctrl
= NULL
;
2659 bool vanish_links
= false;
2660 unsigned int num_to_delete
= el
->num_values
;
2662 const struct GUID
*invocation_id
;
2665 unix_to_nt_time(&now
, t
);
2667 invocation_id
= samdb_ntds_invocation_id(ldb
);
2668 if (!invocation_id
) {
2669 return LDB_ERR_OPERATIONS_ERROR
;
2672 if (old_el
== NULL
|| old_el
->num_values
== 0) {
2673 /* there is nothing to delete... */
2674 if (num_to_delete
== 0) {
2675 /* and we're deleting nothing, so that's OK */
2678 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
2681 tmp_ctx
= talloc_new(msg
);
2682 if (tmp_ctx
== NULL
) {
2683 return LDB_ERR_OPERATIONS_ERROR
;
2686 ret
= get_parsed_dns(module
, tmp_ctx
, el
, &dns
,
2687 schema_attr
->syntax
->ldap_oid
, parent
);
2688 if (ret
!= LDB_SUCCESS
) {
2689 talloc_free(tmp_ctx
);
2693 ret
= get_parsed_dns_trusted(module
, replmd_private
,
2694 tmp_ctx
, old_el
, &old_dns
,
2695 schema_attr
->syntax
->ldap_oid
, parent
);
2697 if (ret
!= LDB_SUCCESS
) {
2698 talloc_free(tmp_ctx
);
2703 vanish_links_ctrl
= ldb_request_get_control(parent
, DSDB_CONTROL_REPLMD_VANISH_LINKS
);
2704 if (vanish_links_ctrl
) {
2705 vanish_links
= true;
2706 vanish_links_ctrl
->critical
= false;
2710 /* we empty out el->values here to avoid damage if we return early. */
2715 * If vanish links is set, we are actually removing members of
2716 * old_el->values; otherwise we are just marking them deleted.
2718 * There is a special case when no values are given: we remove them
2719 * all. When we have the vanish_links control we just have to remove
2720 * the backlinks and change our element to replace the existing values
2721 * with the empty list.
2724 if (num_to_delete
== 0) {
2725 for (i
= 0; i
< old_el
->num_values
; i
++) {
2726 struct parsed_dn
*p
= &old_dns
[i
];
2727 if (p
->dsdb_dn
== NULL
) {
2728 ret
= really_parse_trusted_dn(tmp_ctx
, ldb
, p
,
2729 schema_attr
->syntax
->ldap_oid
);
2730 if (ret
!= LDB_SUCCESS
) {
2734 ret
= replmd_add_backlink(module
, replmd_private
,
2735 schema
, msg_dn
, &p
->guid
,
2738 if (ret
!= LDB_SUCCESS
) {
2739 talloc_free(tmp_ctx
);
2746 rmd_flags
= dsdb_dn_rmd_flags(p
->dsdb_dn
->dn
);
2747 if (rmd_flags
& DSDB_RMD_FLAG_DELETED
) {
2751 ret
= replmd_update_la_val(old_el
->values
, p
->v
,
2752 p
->dsdb_dn
, p
->dsdb_dn
,
2753 invocation_id
, seq_num
,
2754 seq_num
, now
, true);
2755 if (ret
!= LDB_SUCCESS
) {
2756 talloc_free(tmp_ctx
);
2762 el
->flags
= LDB_FLAG_MOD_REPLACE
;
2763 talloc_free(tmp_ctx
);
2769 for (i
= 0; i
< num_to_delete
; i
++) {
2770 struct parsed_dn
*p
= &dns
[i
];
2771 struct parsed_dn
*exact
= NULL
;
2772 struct parsed_dn
*next
= NULL
;
2773 ret
= parsed_dn_find(ldb
, old_dns
, old_el
->num_values
,
2776 p
->dsdb_dn
->extra_part
, 0,
2778 schema_attr
->syntax
->ldap_oid
,
2780 if (ret
!= LDB_SUCCESS
) {
2781 talloc_free(tmp_ctx
);
2784 if (exact
== NULL
) {
2785 struct GUID_txt_buf buf
;
2786 ldb_asprintf_errstring(ldb
, "Attribute %s doesn't "
2787 "exist for target GUID %s",
2789 GUID_buf_string(&p
->guid
, &buf
));
2790 if (ldb_attr_cmp(el
->name
, "member") == 0) {
2791 talloc_free(tmp_ctx
);
2792 return LDB_ERR_UNWILLING_TO_PERFORM
;
2794 talloc_free(tmp_ctx
);
2795 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
2800 if (CHECK_DEBUGLVL(5)) {
2801 rmd_flags
= dsdb_dn_rmd_flags(exact
->dsdb_dn
->dn
);
2802 if ((rmd_flags
& DSDB_RMD_FLAG_DELETED
)) {
2803 struct GUID_txt_buf buf
;
2804 const char *guid_str
= \
2805 GUID_buf_string(&p
->guid
, &buf
);
2806 DEBUG(5, ("Deleting deleted linked "
2807 "attribute %s to %s, because "
2808 "vanish_links control is set\n",
2809 el
->name
, guid_str
));
2813 /* remove the backlink */
2814 ret
= replmd_add_backlink(module
,
2821 if (ret
!= LDB_SUCCESS
) {
2822 talloc_free(tmp_ctx
);
2826 /* We flag the deletion and tidy it up later. */
2831 rmd_flags
= dsdb_dn_rmd_flags(exact
->dsdb_dn
->dn
);
2833 if (rmd_flags
& DSDB_RMD_FLAG_DELETED
) {
2834 struct GUID_txt_buf buf
;
2835 const char *guid_str
= GUID_buf_string(&p
->guid
, &buf
);
2836 ldb_asprintf_errstring(ldb
, "Attribute %s already "
2837 "deleted for target GUID %s",
2838 el
->name
, guid_str
);
2839 if (ldb_attr_cmp(el
->name
, "member") == 0) {
2840 talloc_free(tmp_ctx
);
2841 return LDB_ERR_UNWILLING_TO_PERFORM
;
2843 talloc_free(tmp_ctx
);
2844 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
2848 ret
= replmd_update_la_val(old_el
->values
, exact
->v
,
2849 exact
->dsdb_dn
, exact
->dsdb_dn
,
2850 invocation_id
, seq_num
, seq_num
,
2852 if (ret
!= LDB_SUCCESS
) {
2853 talloc_free(tmp_ctx
);
2856 ret
= replmd_add_backlink(module
, replmd_private
,
2861 if (ret
!= LDB_SUCCESS
) {
2862 talloc_free(tmp_ctx
);
2869 for (i
= 0; i
< old_el
->num_values
; i
++) {
2870 if (old_dns
[i
].v
!= NULL
) {
2871 old_el
->values
[j
] = *old_dns
[i
].v
;
2875 old_el
->num_values
= j
;
2878 el
->values
= talloc_steal(msg
->elements
, old_el
->values
);
2879 el
->num_values
= old_el
->num_values
;
2881 talloc_free(tmp_ctx
);
2883 /* we now tell the backend to replace all existing values
2884 with the one we have constructed */
2885 el
->flags
= LDB_FLAG_MOD_REPLACE
;
2891 handle replacing a linked attribute
2893 static int replmd_modify_la_replace(struct ldb_module
*module
,
2894 struct replmd_private
*replmd_private
,
2895 const struct dsdb_schema
*schema
,
2896 struct ldb_message
*msg
,
2897 struct ldb_message_element
*el
,
2898 struct ldb_message_element
*old_el
,
2899 const struct dsdb_attribute
*schema_attr
,
2902 struct ldb_dn
*msg_dn
,
2903 struct ldb_request
*parent
)
2905 unsigned int i
, old_i
, new_i
;
2906 struct parsed_dn
*dns
, *old_dns
;
2907 TALLOC_CTX
*tmp_ctx
= talloc_new(msg
);
2909 const struct GUID
*invocation_id
;
2910 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
2911 struct ldb_val
*new_values
= NULL
;
2912 const char *ldap_oid
= schema_attr
->syntax
->ldap_oid
;
2913 unsigned int old_num_values
;
2914 unsigned int repl_num_values
;
2915 unsigned int max_num_values
;
2918 unix_to_nt_time(&now
, t
);
2920 invocation_id
= samdb_ntds_invocation_id(ldb
);
2921 if (!invocation_id
) {
2922 return LDB_ERR_OPERATIONS_ERROR
;
2926 * The replace operation is unlike the replace and delete cases in that
2927 * we need to look at every existing link to see whether it is being
2928 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
2930 * As we are trying to combine two sorted lists, the algorithm we use
2931 * is akin to the merge phase of a merge sort. We interleave the two
2932 * lists, doing different things depending on which side the current
2935 * There are three main cases, with some sub-cases.
2937 * - a DN is in the old list but not the new one. It needs to be
2938 * marked as deleted (but left in the list).
2939 * - maybe it is already deleted, and we have less to do.
2941 * - a DN is in both lists. The old data gets replaced by the new,
2942 * and the list doesn't grow. The old link may have been marked as
2943 * deleted, in which case we undelete it.
2945 * - a DN is in the new list only. We add it in the right place.
2948 old_num_values
= old_el
? old_el
->num_values
: 0;
2949 repl_num_values
= el
->num_values
;
2950 max_num_values
= old_num_values
+ repl_num_values
;
2952 if (max_num_values
== 0) {
2953 /* There is nothing to do! */
2957 ret
= get_parsed_dns(module
, tmp_ctx
, el
, &dns
, ldap_oid
, parent
);
2958 if (ret
!= LDB_SUCCESS
) {
2959 talloc_free(tmp_ctx
);
2963 ret
= get_parsed_dns(module
, tmp_ctx
, old_el
, &old_dns
,
2965 if (ret
!= LDB_SUCCESS
) {
2966 talloc_free(tmp_ctx
);
2970 ret
= replmd_check_upgrade_links(ldb
, old_dns
, old_num_values
,
2972 if (ret
!= LDB_SUCCESS
) {
2973 talloc_free(tmp_ctx
);
2977 new_values
= talloc_array(tmp_ctx
, struct ldb_val
, max_num_values
);
2978 if (new_values
== NULL
) {
2979 ldb_module_oom(module
);
2980 talloc_free(tmp_ctx
);
2981 return LDB_ERR_OPERATIONS_ERROR
;
2986 for (i
= 0; i
< max_num_values
; i
++) {
2988 struct parsed_dn
*old_p
, *new_p
;
2989 if (old_i
< old_num_values
&& new_i
< repl_num_values
) {
2990 old_p
= &old_dns
[old_i
];
2991 new_p
= &dns
[new_i
];
2992 cmp
= parsed_dn_compare(old_p
, new_p
);
2993 } else if (old_i
< old_num_values
) {
2994 /* the new list is empty, read the old list */
2995 old_p
= &old_dns
[old_i
];
2998 } else if (new_i
< repl_num_values
) {
2999 /* the old list is empty, read new list */
3001 new_p
= &dns
[new_i
];
3009 * An old ones that come before the next replacement
3010 * (if any). We mark it as deleted and add it to the
3013 uint32_t rmd_flags
= dsdb_dn_rmd_flags(old_p
->dsdb_dn
->dn
);
3014 if ((rmd_flags
& DSDB_RMD_FLAG_DELETED
) == 0) {
3015 ret
= replmd_update_la_val(new_values
, old_p
->v
,
3021 if (ret
!= LDB_SUCCESS
) {
3022 talloc_free(tmp_ctx
);
3026 ret
= replmd_add_backlink(module
, replmd_private
,
3029 &old_p
->guid
, false,
3032 if (ret
!= LDB_SUCCESS
) {
3033 talloc_free(tmp_ctx
);
3037 new_values
[i
] = *old_p
->v
;
3039 } else if (cmp
== 0) {
3041 * We are overwriting one. If it was previously
3042 * deleted, we need to add a backlink.
3044 * Note that if any RMD_FLAGs in an extended new DN
3049 ret
= replmd_update_la_val(new_values
, old_p
->v
,
3055 if (ret
!= LDB_SUCCESS
) {
3056 talloc_free(tmp_ctx
);
3060 rmd_flags
= dsdb_dn_rmd_flags(old_p
->dsdb_dn
->dn
);
3061 if ((rmd_flags
& DSDB_RMD_FLAG_DELETED
) != 0) {
3062 ret
= replmd_add_backlink(module
, replmd_private
,
3068 if (ret
!= LDB_SUCCESS
) {
3069 talloc_free(tmp_ctx
);
3074 new_values
[i
] = *old_p
->v
;
3079 * Replacements that don't match an existing one. We
3080 * just add them to the final list.
3082 ret
= replmd_build_la_val(new_values
,
3088 if (ret
!= LDB_SUCCESS
) {
3089 talloc_free(tmp_ctx
);
3092 ret
= replmd_add_backlink(module
, replmd_private
,
3098 if (ret
!= LDB_SUCCESS
) {
3099 talloc_free(tmp_ctx
);
3102 new_values
[i
] = *new_p
->v
;
3106 if (old_el
!= NULL
) {
3107 talloc_steal(msg
->elements
, old_el
->values
);
3109 el
->values
= talloc_steal(msg
->elements
, new_values
);
3111 talloc_free(tmp_ctx
);
3113 el
->flags
= LDB_FLAG_MOD_REPLACE
;
3120 handle linked attributes in modify requests
3122 static int replmd_modify_handle_linked_attribs(struct ldb_module
*module
,
3123 struct replmd_private
*replmd_private
,
3124 struct ldb_message
*msg
,
3125 uint64_t seq_num
, time_t t
,
3126 struct ldb_request
*parent
)
3128 struct ldb_result
*res
;
3131 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
3132 struct ldb_message
*old_msg
;
3134 const struct dsdb_schema
*schema
;
3136 if (dsdb_functional_level(ldb
) == DS_DOMAIN_FUNCTION_2000
) {
3138 * Nothing special is required for modifying or vanishing links
3139 * in fl2000 since they are just strings in a multi-valued
3142 struct ldb_control
*ctrl
= ldb_request_get_control(parent
,
3143 DSDB_CONTROL_REPLMD_VANISH_LINKS
);
3145 ctrl
->critical
= false;
3153 * We should restrict this to the intersection of the list of
3154 * linked attributes in the schema and the list of attributes
3157 * This will help performance a little, as otherwise we have
3158 * to allocate the entire object value-by-value.
3160 ret
= dsdb_module_search_dn(module
, msg
, &res
, msg
->dn
, NULL
,
3161 DSDB_FLAG_NEXT_MODULE
|
3162 DSDB_SEARCH_SHOW_RECYCLED
|
3163 DSDB_SEARCH_REVEAL_INTERNALS
|
3164 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
,
3166 if (ret
!= LDB_SUCCESS
) {
3169 schema
= dsdb_get_schema(ldb
, res
);
3171 return LDB_ERR_OPERATIONS_ERROR
;
3174 old_msg
= res
->msgs
[0];
3176 for (i
=0; i
<msg
->num_elements
; i
++) {
3177 struct ldb_message_element
*el
= &msg
->elements
[i
];
3178 struct ldb_message_element
*old_el
, *new_el
;
3179 unsigned int mod_type
= LDB_FLAG_MOD_TYPE(el
->flags
);
3180 const struct dsdb_attribute
*schema_attr
3181 = dsdb_attribute_by_lDAPDisplayName(schema
, el
->name
);
3183 ldb_asprintf_errstring(ldb
,
3184 "%s: attribute %s is not a valid attribute in schema",
3185 __FUNCTION__
, el
->name
);
3186 return LDB_ERR_OBJECT_CLASS_VIOLATION
;
3188 if (schema_attr
->linkID
== 0) {
3191 if ((schema_attr
->linkID
& 1) == 1) {
3192 if (parent
&& ldb_request_get_control(parent
, DSDB_CONTROL_DBCHECK
)) {
3195 /* Odd is for the target. Illegal to modify */
3196 ldb_asprintf_errstring(ldb
,
3197 "attribute %s must not be modified directly, it is a linked attribute", el
->name
);
3198 return LDB_ERR_UNWILLING_TO_PERFORM
;
3200 old_el
= ldb_msg_find_element(old_msg
, el
->name
);
3202 case LDB_FLAG_MOD_REPLACE
:
3203 ret
= replmd_modify_la_replace(module
, replmd_private
,
3204 schema
, msg
, el
, old_el
,
3205 schema_attr
, seq_num
, t
,
3209 case LDB_FLAG_MOD_DELETE
:
3210 ret
= replmd_modify_la_delete(module
, replmd_private
,
3211 schema
, msg
, el
, old_el
,
3212 schema_attr
, seq_num
, t
,
3216 case LDB_FLAG_MOD_ADD
:
3217 ret
= replmd_modify_la_add(module
, replmd_private
,
3218 schema
, msg
, el
, old_el
,
3219 schema_attr
, seq_num
, t
,
3224 ldb_asprintf_errstring(ldb
,
3225 "invalid flags 0x%x for %s linked attribute",
3226 el
->flags
, el
->name
);
3227 return LDB_ERR_UNWILLING_TO_PERFORM
;
3229 if (dsdb_check_single_valued_link(schema_attr
, el
) != LDB_SUCCESS
) {
3230 ldb_asprintf_errstring(ldb
,
3231 "Attribute %s is single valued but more than one value has been supplied",
3233 /* Return codes as found on Windows 2012r2 */
3234 if (mod_type
== LDB_FLAG_MOD_REPLACE
) {
3235 return LDB_ERR_CONSTRAINT_VIOLATION
;
3237 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS
;
3240 el
->flags
|= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK
;
3243 if (ret
!= LDB_SUCCESS
) {
3247 ldb_msg_remove_attr(old_msg
, el
->name
);
3249 ldb_msg_add_empty(old_msg
, el
->name
, 0, &new_el
);
3250 new_el
->num_values
= el
->num_values
;
3251 new_el
->values
= talloc_steal(msg
->elements
, el
->values
);
3253 /* TODO: this relises a bit too heavily on the exact
3254 behaviour of ldb_msg_find_element and
3255 ldb_msg_remove_element */
3256 old_el
= ldb_msg_find_element(msg
, el
->name
);
3258 ldb_msg_remove_element(msg
, old_el
);
3268 static int send_rodc_referral(struct ldb_request
*req
,
3269 struct ldb_context
*ldb
,
3272 char *referral
= NULL
;
3273 struct loadparm_context
*lp_ctx
= NULL
;
3274 struct ldb_dn
*fsmo_role_dn
= NULL
;
3275 struct ldb_dn
*role_owner_dn
= NULL
;
3276 const char *domain
= NULL
;
3279 lp_ctx
= talloc_get_type(ldb_get_opaque(ldb
, "loadparm"),
3280 struct loadparm_context
);
3282 werr
= dsdb_get_fsmo_role_info(req
, ldb
, DREPL_PDC_MASTER
,
3283 &fsmo_role_dn
, &role_owner_dn
);
3285 if (W_ERROR_IS_OK(werr
)) {
3286 struct ldb_dn
*server_dn
= ldb_dn_copy(req
, role_owner_dn
);
3287 if (server_dn
!= NULL
) {
3288 ldb_dn_remove_child_components(server_dn
, 1);
3289 domain
= samdb_dn_to_dnshostname(ldb
, req
,
3294 if (domain
== NULL
) {
3295 domain
= lpcfg_dnsdomain(lp_ctx
);
3298 referral
= talloc_asprintf(req
, "ldap://%s/%s",
3300 ldb_dn_get_linearized(dn
));
3301 if (referral
== NULL
) {
3303 return LDB_ERR_OPERATIONS_ERROR
;
3306 return ldb_module_send_referral(req
, referral
);
3310 static int replmd_modify(struct ldb_module
*module
, struct ldb_request
*req
)
3312 struct ldb_context
*ldb
;
3313 struct replmd_replicated_request
*ac
;
3314 struct ldb_request
*down_req
;
3315 struct ldb_message
*msg
;
3316 time_t t
= time(NULL
);
3318 bool is_urgent
= false, rodc
= false;
3319 bool is_schema_nc
= false;
3320 unsigned int functional_level
;
3321 const struct ldb_message_element
*guid_el
= NULL
;
3322 struct ldb_control
*sd_propagation_control
;
3323 struct replmd_private
*replmd_private
=
3324 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
3326 /* do not manipulate our control entries */
3327 if (ldb_dn_is_special(req
->op
.mod
.message
->dn
)) {
3328 return ldb_next_request(module
, req
);
3331 sd_propagation_control
= ldb_request_get_control(req
,
3332 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID
);
3333 if (sd_propagation_control
!= NULL
) {
3334 if (req
->op
.mod
.message
->num_elements
!= 1) {
3335 return ldb_module_operr(module
);
3337 ret
= strcmp(req
->op
.mod
.message
->elements
[0].name
,
3338 "nTSecurityDescriptor");
3340 return ldb_module_operr(module
);
3343 return ldb_next_request(module
, req
);
3346 ldb
= ldb_module_get_ctx(module
);
3348 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_modify\n");
3350 guid_el
= ldb_msg_find_element(req
->op
.mod
.message
, "objectGUID");
3351 if (guid_el
!= NULL
) {
3352 ldb_set_errstring(ldb
,
3353 "replmd_modify: it's not allowed to change the objectGUID!");
3354 return LDB_ERR_CONSTRAINT_VIOLATION
;
3357 ac
= replmd_ctx_init(module
, req
);
3359 return ldb_module_oom(module
);
3362 functional_level
= dsdb_functional_level(ldb
);
3364 /* we have to copy the message as the caller might have it as a const */
3365 msg
= ldb_msg_copy_shallow(ac
, req
->op
.mod
.message
);
3369 return LDB_ERR_OPERATIONS_ERROR
;
3372 ldb_msg_remove_attr(msg
, "whenChanged");
3373 ldb_msg_remove_attr(msg
, "uSNChanged");
3375 is_schema_nc
= ldb_dn_compare_base(replmd_private
->schema_dn
, msg
->dn
) == 0;
3377 ret
= replmd_update_rpmd(module
, ac
->schema
, req
, NULL
,
3378 msg
, &ac
->seq_num
, t
, is_schema_nc
,
3380 if (rodc
&& (ret
== LDB_ERR_REFERRAL
)) {
3381 ret
= send_rodc_referral(req
, ldb
, msg
->dn
);
3387 if (ret
!= LDB_SUCCESS
) {
3392 ret
= replmd_modify_handle_linked_attribs(module
, replmd_private
,
3393 msg
, ac
->seq_num
, t
, req
);
3394 if (ret
!= LDB_SUCCESS
) {
3400 * - replace the old object with the newly constructed one
3403 ac
->is_urgent
= is_urgent
;
3405 ret
= ldb_build_mod_req(&down_req
, ldb
, ac
,
3408 ac
, replmd_op_callback
,
3410 LDB_REQ_SET_LOCATION(down_req
);
3411 if (ret
!= LDB_SUCCESS
) {
3416 /* current partition control is needed by "replmd_op_callback" */
3417 if (ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
) == NULL
) {
3418 ret
= ldb_request_add_control(down_req
,
3419 DSDB_CONTROL_CURRENT_PARTITION_OID
,
3421 if (ret
!= LDB_SUCCESS
) {
3427 /* If we are in functional level 2000, then
3428 * replmd_modify_handle_linked_attribs will have done
3430 if (functional_level
== DS_DOMAIN_FUNCTION_2000
) {
3431 ret
= ldb_request_add_control(down_req
, DSDB_CONTROL_APPLY_LINKS
, false, NULL
);
3432 if (ret
!= LDB_SUCCESS
) {
3438 talloc_steal(down_req
, msg
);
3440 /* we only change whenChanged and uSNChanged if the seq_num
3442 if (ac
->seq_num
!= 0) {
3443 ret
= add_time_element(msg
, "whenChanged", t
);
3444 if (ret
!= LDB_SUCCESS
) {
3450 ret
= add_uint64_element(ldb
, msg
, "uSNChanged", ac
->seq_num
);
3451 if (ret
!= LDB_SUCCESS
) {
3458 /* go on with the call chain */
3459 return ldb_next_request(module
, down_req
);
3462 static int replmd_rename_callback(struct ldb_request
*req
, struct ldb_reply
*ares
);
3465 handle a rename request
3467 On a rename we need to do an extra ldb_modify which sets the
3468 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3470 static int replmd_rename(struct ldb_module
*module
, struct ldb_request
*req
)
3472 struct ldb_context
*ldb
;
3473 struct replmd_replicated_request
*ac
;
3475 struct ldb_request
*down_req
;
3477 /* do not manipulate our control entries */
3478 if (ldb_dn_is_special(req
->op
.mod
.message
->dn
)) {
3479 return ldb_next_request(module
, req
);
3482 ldb
= ldb_module_get_ctx(module
);
3484 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_rename\n");
3486 ac
= replmd_ctx_init(module
, req
);
3488 return ldb_module_oom(module
);
3491 ret
= ldb_build_rename_req(&down_req
, ldb
, ac
,
3492 ac
->req
->op
.rename
.olddn
,
3493 ac
->req
->op
.rename
.newdn
,
3495 ac
, replmd_rename_callback
,
3497 LDB_REQ_SET_LOCATION(down_req
);
3498 if (ret
!= LDB_SUCCESS
) {
3503 /* go on with the call chain */
3504 return ldb_next_request(module
, down_req
);
3507 /* After the rename is compleated, update the whenchanged etc */
3508 static int replmd_rename_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
3510 struct ldb_context
*ldb
;
3511 struct ldb_request
*down_req
;
3512 struct ldb_message
*msg
;
3513 const struct dsdb_attribute
*rdn_attr
;
3514 const char *rdn_name
;
3515 const struct ldb_val
*rdn_val
;
3516 const char *attrs
[5] = { NULL
, };
3517 time_t t
= time(NULL
);
3519 bool is_urgent
= false, rodc
= false;
3521 struct replmd_replicated_request
*ac
=
3522 talloc_get_type(req
->context
, struct replmd_replicated_request
);
3523 struct replmd_private
*replmd_private
=
3524 talloc_get_type(ldb_module_get_private(ac
->module
),
3525 struct replmd_private
);
3527 ldb
= ldb_module_get_ctx(ac
->module
);
3529 if (ares
->error
!= LDB_SUCCESS
) {
3530 return ldb_module_done(ac
->req
, ares
->controls
,
3531 ares
->response
, ares
->error
);
3534 if (ares
->type
!= LDB_REPLY_DONE
) {
3535 ldb_set_errstring(ldb
,
3536 "invalid ldb_reply_type in callback");
3538 return ldb_module_done(ac
->req
, NULL
, NULL
,
3539 LDB_ERR_OPERATIONS_ERROR
);
3543 * - replace the old object with the newly constructed one
3546 msg
= ldb_msg_new(ac
);
3549 return LDB_ERR_OPERATIONS_ERROR
;
3552 msg
->dn
= ac
->req
->op
.rename
.newdn
;
3554 is_schema_nc
= ldb_dn_compare_base(replmd_private
->schema_dn
, msg
->dn
) == 0;
3556 rdn_name
= ldb_dn_get_rdn_name(msg
->dn
);
3557 if (rdn_name
== NULL
) {
3559 return ldb_module_done(ac
->req
, NULL
, NULL
,
3563 /* normalize the rdn attribute name */
3564 rdn_attr
= dsdb_attribute_by_lDAPDisplayName(ac
->schema
, rdn_name
);
3565 if (rdn_attr
== NULL
) {
3567 return ldb_module_done(ac
->req
, NULL
, NULL
,
3570 rdn_name
= rdn_attr
->lDAPDisplayName
;
3572 rdn_val
= ldb_dn_get_rdn_val(msg
->dn
);
3573 if (rdn_val
== NULL
) {
3575 return ldb_module_done(ac
->req
, NULL
, NULL
,
3579 if (ldb_msg_add_empty(msg
, rdn_name
, LDB_FLAG_MOD_REPLACE
, NULL
) != 0) {
3581 return ldb_module_done(ac
->req
, NULL
, NULL
,
3584 if (ldb_msg_add_value(msg
, rdn_name
, rdn_val
, NULL
) != 0) {
3586 return ldb_module_done(ac
->req
, NULL
, NULL
,
3589 if (ldb_msg_add_empty(msg
, "name", LDB_FLAG_MOD_REPLACE
, NULL
) != 0) {
3591 return ldb_module_done(ac
->req
, NULL
, NULL
,
3594 if (ldb_msg_add_value(msg
, "name", rdn_val
, NULL
) != 0) {
3596 return ldb_module_done(ac
->req
, NULL
, NULL
,
3601 * here we let replmd_update_rpmd() only search for
3602 * the existing "replPropertyMetaData" and rdn_name attributes.
3604 * We do not want the existing "name" attribute as
3605 * the "name" attribute needs to get the version
3606 * updated on rename even if the rdn value hasn't changed.
3608 * This is the diff of the meta data, for a moved user
3609 * on a w2k8r2 server:
3612 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3613 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3614 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3615 * version : 0x00000001 (1)
3616 * reserved : 0x00000000 (0)
3617 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3618 * local_usn : 0x00000000000037a5 (14245)
3619 * array: struct replPropertyMetaData1
3620 * attid : DRSUAPI_ATTID_name (0x90001)
3621 * - version : 0x00000001 (1)
3622 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3623 * + version : 0x00000002 (2)
3624 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3625 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3626 * - originating_usn : 0x00000000000037a5 (14245)
3627 * - local_usn : 0x00000000000037a5 (14245)
3628 * + originating_usn : 0x0000000000003834 (14388)
3629 * + local_usn : 0x0000000000003834 (14388)
3630 * array: struct replPropertyMetaData1
3631 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3632 * version : 0x00000004 (4)
3634 attrs
[0] = "replPropertyMetaData";
3635 attrs
[1] = "objectClass";
3636 attrs
[2] = "instanceType";
3637 attrs
[3] = rdn_name
;
3640 ret
= replmd_update_rpmd(ac
->module
, ac
->schema
, req
, attrs
,
3641 msg
, &ac
->seq_num
, t
,
3642 is_schema_nc
, &is_urgent
, &rodc
);
3643 if (rodc
&& (ret
== LDB_ERR_REFERRAL
)) {
3644 ret
= send_rodc_referral(req
, ldb
, ac
->req
->op
.rename
.olddn
);
3646 return ldb_module_done(req
, NULL
, NULL
, ret
);
3649 if (ret
!= LDB_SUCCESS
) {
3651 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
3654 if (ac
->seq_num
== 0) {
3656 return ldb_module_done(ac
->req
, NULL
, NULL
,
3658 "internal error seq_num == 0"));
3660 ac
->is_urgent
= is_urgent
;
3662 ret
= ldb_build_mod_req(&down_req
, ldb
, ac
,
3665 ac
, replmd_op_callback
,
3667 LDB_REQ_SET_LOCATION(down_req
);
3668 if (ret
!= LDB_SUCCESS
) {
3673 /* current partition control is needed by "replmd_op_callback" */
3674 if (ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
) == NULL
) {
3675 ret
= ldb_request_add_control(down_req
,
3676 DSDB_CONTROL_CURRENT_PARTITION_OID
,
3678 if (ret
!= LDB_SUCCESS
) {
3684 talloc_steal(down_req
, msg
);
3686 ret
= add_time_element(msg
, "whenChanged", t
);
3687 if (ret
!= LDB_SUCCESS
) {
3693 ret
= add_uint64_element(ldb
, msg
, "uSNChanged", ac
->seq_num
);
3694 if (ret
!= LDB_SUCCESS
) {
3700 /* go on with the call chain - do the modify after the rename */
3701 return ldb_next_request(ac
->module
, down_req
);
3705 * remove links from objects that point at this object when an object
3706 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3707 * RemoveObj which states that link removal due to the object being
3708 * deleted is NOT an originating update - they just go away!
3711 static int replmd_delete_remove_link(struct ldb_module
*module
,
3712 const struct dsdb_schema
*schema
,
3713 struct replmd_private
*replmd_private
,
3716 struct ldb_message_element
*el
,
3717 const struct dsdb_attribute
*sa
,
3718 struct ldb_request
*parent
)
3721 TALLOC_CTX
*tmp_ctx
= talloc_new(module
);
3722 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
3724 for (i
=0; i
<el
->num_values
; i
++) {
3725 struct dsdb_dn
*dsdb_dn
;
3727 struct ldb_message
*msg
;
3728 const struct dsdb_attribute
*target_attr
;
3729 struct ldb_message_element
*el2
;
3731 struct ldb_val dn_val
;
3732 uint32_t dsdb_flags
= 0;
3733 const char *attrs
[] = { NULL
, NULL
};
3734 struct ldb_result
*link_res
;
3735 struct ldb_message
*link_msg
;
3736 struct ldb_message_element
*link_el
;
3737 struct parsed_dn
*link_dns
;
3738 struct parsed_dn
*p
= NULL
, *unused
= NULL
;
3740 if (dsdb_dn_is_deleted_val(&el
->values
[i
])) {
3744 dsdb_dn
= dsdb_dn_parse(tmp_ctx
, ldb
, &el
->values
[i
], sa
->syntax
->ldap_oid
);
3746 talloc_free(tmp_ctx
);
3747 return LDB_ERR_OPERATIONS_ERROR
;
3750 /* remove the link */
3751 msg
= ldb_msg_new(tmp_ctx
);
3753 ldb_module_oom(module
);
3754 talloc_free(tmp_ctx
);
3755 return LDB_ERR_OPERATIONS_ERROR
;
3759 msg
->dn
= dsdb_dn
->dn
;
3761 target_attr
= dsdb_attribute_by_linkID(schema
, sa
->linkID
^ 1);
3762 if (target_attr
== NULL
) {
3765 attrs
[0] = target_attr
->lDAPDisplayName
;
3767 ret
= ldb_msg_add_empty(msg
, target_attr
->lDAPDisplayName
,
3768 LDB_FLAG_MOD_DELETE
, &el2
);
3769 if (ret
!= LDB_SUCCESS
) {
3770 ldb_module_oom(module
);
3771 talloc_free(tmp_ctx
);
3772 return LDB_ERR_OPERATIONS_ERROR
;
3775 ret
= dsdb_module_search_dn(module
, tmp_ctx
, &link_res
,
3777 DSDB_FLAG_NEXT_MODULE
|
3778 DSDB_SEARCH_SHOW_EXTENDED_DN
,
3781 if (ret
!= LDB_SUCCESS
) {
3782 talloc_free(tmp_ctx
);
3786 link_msg
= link_res
->msgs
[0];
3787 link_el
= ldb_msg_find_element(link_msg
,
3788 target_attr
->lDAPDisplayName
);
3789 if (link_el
== NULL
) {
3790 talloc_free(tmp_ctx
);
3791 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
3795 * This call 'upgrades' the links in link_dns, but we
3796 * do not commit the result back into the database, so
3797 * this is safe to call in FL2000 or on databases that
3798 * have been run at that level in the past.
3800 ret
= get_parsed_dns_trusted(module
, replmd_private
, tmp_ctx
,
3802 target_attr
->syntax
->ldap_oid
, parent
);
3803 if (ret
!= LDB_SUCCESS
) {
3804 talloc_free(tmp_ctx
);
3808 ret
= parsed_dn_find(ldb
, link_dns
, link_el
->num_values
,
3812 target_attr
->syntax
->ldap_oid
, false);
3813 if (ret
!= LDB_SUCCESS
) {
3814 talloc_free(tmp_ctx
);
3819 ldb_asprintf_errstring(ldb_module_get_ctx(module
),
3820 "Failed to find forward link on %s "
3821 "as %s to remove backlink %s on %s",
3822 ldb_dn_get_linearized(msg
->dn
),
3823 target_attr
->lDAPDisplayName
,
3824 sa
->lDAPDisplayName
,
3825 ldb_dn_get_linearized(dn
));
3826 talloc_free(tmp_ctx
);
3827 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
3831 /* This needs to get the Binary DN, by first searching */
3832 dn_str
= dsdb_dn_get_linearized(tmp_ctx
,
3835 dn_val
= data_blob_string_const(dn_str
);
3836 el2
->values
= &dn_val
;
3837 el2
->num_values
= 1;
3840 * Ensure that we tell the modification to vanish any linked
3841 * attributes (not simply mark them as isDeleted = TRUE)
3843 dsdb_flags
|= DSDB_REPLMD_VANISH_LINKS
;
3845 ret
= dsdb_module_modify(module
, msg
, dsdb_flags
|DSDB_FLAG_OWN_MODULE
, parent
);
3846 if (ret
!= LDB_SUCCESS
) {
3847 talloc_free(tmp_ctx
);
3851 talloc_free(tmp_ctx
);
3857 handle update of replication meta data for deletion of objects
3859 This also handles the mapping of delete to a rename operation
3860 to allow deletes to be replicated.
3862 It also handles the incoming deleted objects, to ensure they are
3863 fully deleted here. In that case re_delete is true, and we do not
3864 use this as a signal to change the deleted state, just reinforce it.
3867 static int replmd_delete_internals(struct ldb_module
*module
, struct ldb_request
*req
, bool re_delete
)
3869 int ret
= LDB_ERR_OTHER
;
3870 bool retb
, disallow_move_on_delete
;
3871 struct ldb_dn
*old_dn
, *new_dn
;
3872 const char *rdn_name
;
3873 const struct ldb_val
*rdn_value
, *new_rdn_value
;
3875 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
3876 const struct dsdb_schema
*schema
;
3877 struct ldb_message
*msg
, *old_msg
;
3878 struct ldb_message_element
*el
;
3879 TALLOC_CTX
*tmp_ctx
;
3880 struct ldb_result
*res
, *parent_res
;
3881 static const char * const preserved_attrs
[] = {
3882 /* yes, this really is a hard coded list. See MS-ADTS
3883 section 3.1.1.5.5.1.1 */
3886 "dNReferenceUpdate",
3897 "msDS-LastKnownRDN",
3903 "distinguishedName",
3907 "proxiedObjectName",
3909 "nTSecurityDescriptor",
3910 "replPropertyMetaData",
3912 "securityIdentifier",
3920 "userAccountControl",
3927 static const char * const all_attrs
[] = {
3928 DSDB_SECRET_ATTRIBUTES
,
3932 unsigned int i
, el_count
= 0;
3933 uint32_t dsdb_flags
= 0;
3934 struct replmd_private
*replmd_private
;
3935 enum deletion_state deletion_state
, next_deletion_state
;
3937 if (ldb_dn_is_special(req
->op
.del
.dn
)) {
3938 return ldb_next_request(module
, req
);
3942 * We have to allow dbcheck to remove an object that
3943 * is beyond repair, and to do so totally. This could
3944 * mean we we can get a partial object from the other
3945 * DC, causing havoc, so dbcheck suggests
3946 * re-replication first. dbcheck sets both DBCHECK
3947 * and RELAX in this situation.
3949 if (ldb_request_get_control(req
, LDB_CONTROL_RELAX_OID
)
3950 && ldb_request_get_control(req
, DSDB_CONTROL_DBCHECK
)) {
3951 /* really, really remove it */
3952 return ldb_next_request(module
, req
);
3955 tmp_ctx
= talloc_new(ldb
);
3958 return LDB_ERR_OPERATIONS_ERROR
;
3961 schema
= dsdb_get_schema(ldb
, tmp_ctx
);
3963 talloc_free(tmp_ctx
);
3964 return LDB_ERR_OPERATIONS_ERROR
;
3967 old_dn
= ldb_dn_copy(tmp_ctx
, req
->op
.del
.dn
);
3969 /* we need the complete msg off disk, so we can work out which
3970 attributes need to be removed */
3971 ret
= dsdb_module_search_dn(module
, tmp_ctx
, &res
, old_dn
, all_attrs
,
3972 DSDB_FLAG_NEXT_MODULE
|
3973 DSDB_SEARCH_SHOW_RECYCLED
|
3974 DSDB_SEARCH_REVEAL_INTERNALS
|
3975 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
, req
);
3976 if (ret
!= LDB_SUCCESS
) {
3977 ldb_asprintf_errstring(ldb_module_get_ctx(module
),
3978 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
3979 re_delete
? "re-delete" : "delete",
3980 ldb_dn_get_linearized(old_dn
),
3981 ldb_errstring(ldb_module_get_ctx(module
)));
3982 talloc_free(tmp_ctx
);
3985 old_msg
= res
->msgs
[0];
3987 replmd_deletion_state(module
, old_msg
,
3989 &next_deletion_state
);
3991 /* This supports us noticing an incoming isDeleted and acting on it */
3993 SMB_ASSERT(deletion_state
> OBJECT_NOT_DELETED
);
3994 next_deletion_state
= deletion_state
;
3997 if (next_deletion_state
== OBJECT_REMOVED
) {
3999 * We have to prevent objects being deleted, even if
4000 * the administrator really wants them gone, as
4001 * without the tombstone, we can get a partial object
4002 * from the other DC, causing havoc.
4004 * The only other valid case is when the 180 day
4005 * timeout has expired, when relax is specified.
4007 if (ldb_request_get_control(req
, LDB_CONTROL_RELAX_OID
)) {
4008 /* it is already deleted - really remove it this time */
4009 talloc_free(tmp_ctx
);
4010 return ldb_next_request(module
, req
);
4013 ldb_asprintf_errstring(ldb
, "Refusing to delete tombstone object %s. "
4014 "This check is to prevent corruption of the replicated state.",
4015 ldb_dn_get_linearized(old_msg
->dn
));
4016 return LDB_ERR_UNWILLING_TO_PERFORM
;
4019 rdn_name
= ldb_dn_get_rdn_name(old_dn
);
4020 rdn_value
= ldb_dn_get_rdn_val(old_dn
);
4021 if ((rdn_name
== NULL
) || (rdn_value
== NULL
)) {
4022 talloc_free(tmp_ctx
);
4023 return ldb_operr(ldb
);
4026 msg
= ldb_msg_new(tmp_ctx
);
4028 ldb_module_oom(module
);
4029 talloc_free(tmp_ctx
);
4030 return LDB_ERR_OPERATIONS_ERROR
;
4035 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4036 disallow_move_on_delete
=
4037 (ldb_msg_find_attr_as_int(old_msg
, "systemFlags", 0)
4038 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE
);
4040 /* work out where we will be renaming this object to */
4041 if (!disallow_move_on_delete
) {
4042 struct ldb_dn
*deleted_objects_dn
;
4043 ret
= dsdb_get_deleted_objects_dn(ldb
, tmp_ctx
, old_dn
,
4044 &deleted_objects_dn
);
4047 * We should not move objects if we can't find the
4048 * deleted objects DN. Not moving (or otherwise
4049 * harming) the Deleted Objects DN itself is handled
4052 if (re_delete
&& (ret
!= LDB_SUCCESS
)) {
4053 new_dn
= ldb_dn_get_parent(tmp_ctx
, old_dn
);
4054 if (new_dn
== NULL
) {
4055 ldb_module_oom(module
);
4056 talloc_free(tmp_ctx
);
4057 return LDB_ERR_OPERATIONS_ERROR
;
4059 } else if (ret
!= LDB_SUCCESS
) {
4060 /* this is probably an attempted delete on a partition
4061 * that doesn't allow delete operations, such as the
4062 * schema partition */
4063 ldb_asprintf_errstring(ldb
, "No Deleted Objects container for DN %s",
4064 ldb_dn_get_linearized(old_dn
));
4065 talloc_free(tmp_ctx
);
4066 return LDB_ERR_UNWILLING_TO_PERFORM
;
4068 new_dn
= deleted_objects_dn
;
4071 new_dn
= ldb_dn_get_parent(tmp_ctx
, old_dn
);
4072 if (new_dn
== NULL
) {
4073 ldb_module_oom(module
);
4074 talloc_free(tmp_ctx
);
4075 return LDB_ERR_OPERATIONS_ERROR
;
4079 /* get the objects GUID from the search we just did */
4080 guid
= samdb_result_guid(old_msg
, "objectGUID");
4082 if (deletion_state
== OBJECT_NOT_DELETED
) {
4083 /* Add a formatted child */
4084 retb
= ldb_dn_add_child_fmt(new_dn
, "%s=%s\\0ADEL:%s",
4086 ldb_dn_escape_value(tmp_ctx
, *rdn_value
),
4087 GUID_string(tmp_ctx
, &guid
));
4089 ldb_asprintf_errstring(ldb
, __location__
4090 ": Unable to add a formatted child to dn: %s",
4091 ldb_dn_get_linearized(new_dn
));
4092 talloc_free(tmp_ctx
);
4093 return LDB_ERR_OPERATIONS_ERROR
;
4096 ret
= ldb_msg_add_string(msg
, "isDeleted", "TRUE");
4097 if (ret
!= LDB_SUCCESS
) {
4098 ldb_asprintf_errstring(ldb
, __location__
4099 ": Failed to add isDeleted string to the msg");
4100 talloc_free(tmp_ctx
);
4103 msg
->elements
[el_count
++].flags
= LDB_FLAG_MOD_REPLACE
;
4106 * No matter what has happened with other renames etc, try again to
4107 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4110 struct ldb_dn
*rdn
= ldb_dn_copy(tmp_ctx
, old_dn
);
4111 retb
= ldb_dn_remove_base_components(rdn
, ldb_dn_get_comp_num(rdn
) - 1);
4113 ldb_asprintf_errstring(ldb
, __location__
4114 ": Unable to add a prepare rdn of %s",
4115 ldb_dn_get_linearized(rdn
));
4116 talloc_free(tmp_ctx
);
4117 return LDB_ERR_OPERATIONS_ERROR
;
4119 SMB_ASSERT(ldb_dn_get_comp_num(rdn
) == 1);
4121 retb
= ldb_dn_add_child(new_dn
, rdn
);
4123 ldb_asprintf_errstring(ldb
, __location__
4124 ": Unable to add rdn %s to base dn: %s",
4125 ldb_dn_get_linearized(rdn
),
4126 ldb_dn_get_linearized(new_dn
));
4127 talloc_free(tmp_ctx
);
4128 return LDB_ERR_OPERATIONS_ERROR
;
4133 now we need to modify the object in the following ways:
4135 - add isDeleted=TRUE
4136 - update rDN and name, with new rDN
4137 - remove linked attributes
4138 - remove objectCategory and sAMAccountType
4139 - remove attribs not on the preserved list
4140 - preserved if in above list, or is rDN
4141 - remove all linked attribs from this object
4142 - remove all links from other objects to this object
4143 - add lastKnownParent
4144 - update replPropertyMetaData?
4146 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4149 if (deletion_state
== OBJECT_NOT_DELETED
) {
4150 struct ldb_dn
*parent_dn
= ldb_dn_get_parent(tmp_ctx
, old_dn
);
4151 char *parent_dn_str
= NULL
;
4153 /* we need the storage form of the parent GUID */
4154 ret
= dsdb_module_search_dn(module
, tmp_ctx
, &parent_res
,
4156 DSDB_FLAG_NEXT_MODULE
|
4157 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
|
4158 DSDB_SEARCH_REVEAL_INTERNALS
|
4159 DSDB_SEARCH_SHOW_RECYCLED
, req
);
4160 if (ret
!= LDB_SUCCESS
) {
4161 ldb_asprintf_errstring(ldb_module_get_ctx(module
),
4162 "repmd_delete: Failed to %s %s, "
4163 "because we failed to find it's parent (%s): %s",
4164 re_delete
? "re-delete" : "delete",
4165 ldb_dn_get_linearized(old_dn
),
4166 ldb_dn_get_linearized(parent_dn
),
4167 ldb_errstring(ldb_module_get_ctx(module
)));
4168 talloc_free(tmp_ctx
);
4173 * Now we can use the DB version,
4174 * it will have the extended DN info in it
4176 parent_dn
= parent_res
->msgs
[0]->dn
;
4177 parent_dn_str
= ldb_dn_get_extended_linearized(tmp_ctx
,
4180 if (parent_dn_str
== NULL
) {
4181 talloc_free(tmp_ctx
);
4182 return ldb_module_oom(module
);
4185 ret
= ldb_msg_add_steal_string(msg
, "lastKnownParent",
4187 if (ret
!= LDB_SUCCESS
) {
4188 ldb_asprintf_errstring(ldb
, __location__
4189 ": Failed to add lastKnownParent "
4190 "string when deleting %s",
4191 ldb_dn_get_linearized(old_dn
));
4192 talloc_free(tmp_ctx
);
4195 msg
->elements
[el_count
++].flags
= LDB_FLAG_MOD_REPLACE
;
4197 if (next_deletion_state
== OBJECT_DELETED
) {
4198 ret
= ldb_msg_add_value(msg
, "msDS-LastKnownRDN", rdn_value
, NULL
);
4199 if (ret
!= LDB_SUCCESS
) {
4200 ldb_asprintf_errstring(ldb
, __location__
4201 ": Failed to add msDS-LastKnownRDN "
4202 "string when deleting %s",
4203 ldb_dn_get_linearized(old_dn
));
4204 talloc_free(tmp_ctx
);
4207 msg
->elements
[el_count
++].flags
= LDB_FLAG_MOD_ADD
;
4211 switch (next_deletion_state
) {
4213 case OBJECT_RECYCLED
:
4214 case OBJECT_TOMBSTONE
:
4217 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4218 * describes what must be removed from a tombstone
4221 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4222 * describes what must be removed from a recycled
4228 * we also mark it as recycled, meaning this object can't be
4229 * recovered (we are stripping its attributes).
4230 * This is done only if we have this schema object of course ...
4231 * This behavior is identical to the one of Windows 2008R2 which
4232 * always set the isRecycled attribute, even if the recycle-bin is
4233 * not activated and what ever the forest level is.
4235 if (dsdb_attribute_by_lDAPDisplayName(schema
, "isRecycled") != NULL
) {
4236 ret
= ldb_msg_add_string(msg
, "isRecycled", "TRUE");
4237 if (ret
!= LDB_SUCCESS
) {
4238 DEBUG(0,(__location__
": Failed to add isRecycled string to the msg\n"));
4239 ldb_module_oom(module
);
4240 talloc_free(tmp_ctx
);
4243 msg
->elements
[el_count
++].flags
= LDB_FLAG_MOD_REPLACE
;
4246 replmd_private
= talloc_get_type(ldb_module_get_private(module
),
4247 struct replmd_private
);
4248 /* work out which of the old attributes we will be removing */
4249 for (i
=0; i
<old_msg
->num_elements
; i
++) {
4250 const struct dsdb_attribute
*sa
;
4251 el
= &old_msg
->elements
[i
];
4252 sa
= dsdb_attribute_by_lDAPDisplayName(schema
, el
->name
);
4254 talloc_free(tmp_ctx
);
4255 return LDB_ERR_OPERATIONS_ERROR
;
4257 if (ldb_attr_cmp(el
->name
, rdn_name
) == 0) {
4258 /* don't remove the rDN */
4261 if (sa
->linkID
& 1) {
4263 we have a backlink in this object
4264 that needs to be removed. We're not
4265 allowed to remove it directly
4266 however, so we instead setup a
4267 modify to delete the corresponding
4270 ret
= replmd_delete_remove_link(module
, schema
,
4274 if (ret
!= LDB_SUCCESS
) {
4275 const char *old_dn_str
4276 = ldb_dn_get_linearized(old_dn
);
4277 ldb_asprintf_errstring(ldb
,
4279 ": Failed to remove backlink of "
4280 "%s when deleting %s: %s",
4283 ldb_errstring(ldb
));
4284 talloc_free(tmp_ctx
);
4285 return LDB_ERR_OPERATIONS_ERROR
;
4287 /* now we continue, which means we
4288 won't remove this backlink
4292 } else if (sa
->linkID
== 0) {
4293 if (ldb_attr_in_list(preserved_attrs
, el
->name
)) {
4296 if (sa
->searchFlags
& SEARCH_FLAG_PRESERVEONDELETE
) {
4301 * Ensure that we tell the modification to vanish any linked
4302 * attributes (not simply mark them as isDeleted = TRUE)
4304 dsdb_flags
|= DSDB_REPLMD_VANISH_LINKS
;
4306 ret
= ldb_msg_add_empty(msg
, el
->name
, LDB_FLAG_MOD_DELETE
, &el
);
4307 if (ret
!= LDB_SUCCESS
) {
4308 talloc_free(tmp_ctx
);
4309 ldb_module_oom(module
);
4316 case OBJECT_DELETED
:
4318 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4319 * describes what must be removed from a deleted
4323 ret
= ldb_msg_add_empty(msg
, "objectCategory", LDB_FLAG_MOD_REPLACE
, NULL
);
4324 if (ret
!= LDB_SUCCESS
) {
4325 talloc_free(tmp_ctx
);
4326 ldb_module_oom(module
);
4330 ret
= ldb_msg_add_empty(msg
, "sAMAccountType", LDB_FLAG_MOD_REPLACE
, NULL
);
4331 if (ret
!= LDB_SUCCESS
) {
4332 talloc_free(tmp_ctx
);
4333 ldb_module_oom(module
);
4343 if (deletion_state
== OBJECT_NOT_DELETED
) {
4344 const struct dsdb_attribute
*sa
;
4346 /* work out what the new rdn value is, for updating the
4347 rDN and name fields */
4348 new_rdn_value
= ldb_dn_get_rdn_val(new_dn
);
4349 if (new_rdn_value
== NULL
) {
4350 talloc_free(tmp_ctx
);
4351 return ldb_operr(ldb
);
4354 sa
= dsdb_attribute_by_lDAPDisplayName(schema
, rdn_name
);
4356 talloc_free(tmp_ctx
);
4357 return LDB_ERR_OPERATIONS_ERROR
;
4360 ret
= ldb_msg_add_value(msg
, sa
->lDAPDisplayName
, new_rdn_value
,
4362 if (ret
!= LDB_SUCCESS
) {
4363 talloc_free(tmp_ctx
);
4366 el
->flags
= LDB_FLAG_MOD_REPLACE
;
4368 el
= ldb_msg_find_element(old_msg
, "name");
4370 ret
= ldb_msg_add_value(msg
, "name", new_rdn_value
, &el
);
4371 if (ret
!= LDB_SUCCESS
) {
4372 talloc_free(tmp_ctx
);
4375 el
->flags
= LDB_FLAG_MOD_REPLACE
;
4380 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4385 * No matter what has happned with other renames, try again to
4386 * get this to be under the deleted DN.
4388 if (strcmp(ldb_dn_get_linearized(old_dn
), ldb_dn_get_linearized(new_dn
)) != 0) {
4389 /* now rename onto the new DN */
4390 ret
= dsdb_module_rename(module
, old_dn
, new_dn
, DSDB_FLAG_NEXT_MODULE
, req
);
4391 if (ret
!= LDB_SUCCESS
){
4392 DEBUG(0,(__location__
": Failed to rename object from '%s' to '%s' - %s\n",
4393 ldb_dn_get_linearized(old_dn
),
4394 ldb_dn_get_linearized(new_dn
),
4395 ldb_errstring(ldb
)));
4396 talloc_free(tmp_ctx
);
4402 ret
= dsdb_module_modify(module
, msg
, dsdb_flags
|DSDB_FLAG_OWN_MODULE
, req
);
4403 if (ret
!= LDB_SUCCESS
) {
4404 ldb_asprintf_errstring(ldb
, "replmd_delete: Failed to modify object %s in delete - %s",
4405 ldb_dn_get_linearized(old_dn
), ldb_errstring(ldb
));
4406 talloc_free(tmp_ctx
);
4410 talloc_free(tmp_ctx
);
4412 return ldb_module_done(req
, NULL
, NULL
, LDB_SUCCESS
);
4415 static int replmd_delete(struct ldb_module
*module
, struct ldb_request
*req
)
4417 return replmd_delete_internals(module
, req
, false);
4421 static int replmd_replicated_request_error(struct replmd_replicated_request
*ar
, int ret
)
4426 static int replmd_replicated_request_werror(struct replmd_replicated_request
*ar
, WERROR status
)
4428 int ret
= LDB_ERR_OTHER
;
4429 /* TODO: do some error mapping */
4431 /* Let the caller know the full WERROR */
4432 ar
->objs
->error
= status
;
4438 static struct replPropertyMetaData1
*
4439 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob
*md_blob
,
4440 enum drsuapi_DsAttributeId attid
)
4443 struct replPropertyMetaDataCtr1
*rpmd_ctr
= &md_blob
->ctr
.ctr1
;
4445 for (i
= 0; i
< rpmd_ctr
->count
; i
++) {
4446 if (rpmd_ctr
->array
[i
].attid
== attid
) {
4447 return &rpmd_ctr
->array
[i
];
4455 return true if an update is newer than an existing entry
4456 see section 5.11 of MS-ADTS
4458 static bool replmd_update_is_newer(const struct GUID
*current_invocation_id
,
4459 const struct GUID
*update_invocation_id
,
4460 uint32_t current_version
,
4461 uint32_t update_version
,
4462 NTTIME current_change_time
,
4463 NTTIME update_change_time
)
4465 if (update_version
!= current_version
) {
4466 return update_version
> current_version
;
4468 if (update_change_time
!= current_change_time
) {
4469 return update_change_time
> current_change_time
;
4471 return GUID_compare(update_invocation_id
, current_invocation_id
) > 0;
4474 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1
*cur_m
,
4475 struct replPropertyMetaData1
*new_m
)
4477 return replmd_update_is_newer(&cur_m
->originating_invocation_id
,
4478 &new_m
->originating_invocation_id
,
4481 cur_m
->originating_change_time
,
4482 new_m
->originating_change_time
);
4485 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags
,
4486 struct replPropertyMetaData1
*cur_m
,
4487 struct replPropertyMetaData1
*new_m
)
4492 * If the new replPropertyMetaData entry for this attribute is
4493 * not provided (this happens in the case where we look for
4494 * ATTID_name, but the name was not changed), then the local
4495 * state is clearly still current, as the remote
4496 * server didn't send it due to being older the high watermark
4499 if (new_m
== NULL
) {
4503 if (dsdb_repl_flags
& DSDB_REPL_FLAG_PRIORITISE_INCOMING
) {
4505 * if we compare equal then do an
4506 * update. This is used when a client
4507 * asks for a FULL_SYNC, and can be
4508 * used to recover a corrupt
4511 * This call is a bit tricky, what we
4512 * are doing it turning the 'is_newer'
4513 * call into a 'not is older' by
4514 * swapping cur_m and new_m, and negating the
4517 cmp
= !replmd_replPropertyMetaData1_is_newer(new_m
,
4520 cmp
= replmd_replPropertyMetaData1_is_newer(cur_m
,
4530 static struct ldb_dn
*replmd_conflict_dn(TALLOC_CTX
*mem_ctx
, struct ldb_dn
*dn
, struct GUID
*guid
)
4532 const struct ldb_val
*rdn_val
;
4533 const char *rdn_name
;
4534 struct ldb_dn
*new_dn
;
4536 rdn_val
= ldb_dn_get_rdn_val(dn
);
4537 rdn_name
= ldb_dn_get_rdn_name(dn
);
4538 if (!rdn_val
|| !rdn_name
) {
4542 new_dn
= ldb_dn_copy(mem_ctx
, dn
);
4547 if (!ldb_dn_remove_child_components(new_dn
, 1)) {
4551 if (!ldb_dn_add_child_fmt(new_dn
, "%s=%s\\0ACNF:%s",
4553 ldb_dn_escape_value(new_dn
, *rdn_val
),
4554 GUID_string(new_dn
, guid
))) {
4563 perform a modify operation which sets the rDN and name attributes to
4564 their current values. This has the effect of changing these
4565 attributes to have been last updated by the current DC. This is
4566 needed to ensure that renames performed as part of conflict
4567 resolution are propogated to other DCs
4569 static int replmd_name_modify(struct replmd_replicated_request
*ar
,
4570 struct ldb_request
*req
, struct ldb_dn
*dn
)
4572 struct ldb_message
*msg
;
4573 const char *rdn_name
;
4574 const struct ldb_val
*rdn_val
;
4575 const struct dsdb_attribute
*rdn_attr
;
4578 msg
= ldb_msg_new(req
);
4584 rdn_name
= ldb_dn_get_rdn_name(dn
);
4585 if (rdn_name
== NULL
) {
4589 /* normalize the rdn attribute name */
4590 rdn_attr
= dsdb_attribute_by_lDAPDisplayName(ar
->schema
, rdn_name
);
4591 if (rdn_attr
== NULL
) {
4594 rdn_name
= rdn_attr
->lDAPDisplayName
;
4596 rdn_val
= ldb_dn_get_rdn_val(dn
);
4597 if (rdn_val
== NULL
) {
4601 if (ldb_msg_add_empty(msg
, rdn_name
, LDB_FLAG_MOD_REPLACE
, NULL
) != 0) {
4604 if (ldb_msg_add_value(msg
, rdn_name
, rdn_val
, NULL
) != 0) {
4607 if (ldb_msg_add_empty(msg
, "name", LDB_FLAG_MOD_REPLACE
, NULL
) != 0) {
4610 if (ldb_msg_add_value(msg
, "name", rdn_val
, NULL
) != 0) {
4615 * We have to mark this as a replicated update otherwise
4616 * schema_data may reject a rename in the schema partition
4619 ret
= dsdb_module_modify(ar
->module
, msg
,
4620 DSDB_FLAG_OWN_MODULE
|DSDB_FLAG_REPLICATED_UPDATE
,
4622 if (ret
!= LDB_SUCCESS
) {
4623 DEBUG(0,(__location__
": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
4624 ldb_dn_get_linearized(dn
),
4625 ldb_errstring(ldb_module_get_ctx(ar
->module
))));
4635 DEBUG(0,(__location__
": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
4636 ldb_dn_get_linearized(dn
)));
4637 return LDB_ERR_OPERATIONS_ERROR
;
4642 callback for conflict DN handling where we have renamed the incoming
4643 record. After renaming it, we need to ensure the change of name and
4644 rDN for the incoming record is seen as an originating update by this DC.
4646 This also handles updating lastKnownParent for entries sent to lostAndFound
4648 static int replmd_op_name_modify_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
4650 struct replmd_replicated_request
*ar
=
4651 talloc_get_type_abort(req
->context
, struct replmd_replicated_request
);
4652 struct ldb_dn
*conflict_dn
= NULL
;
4655 if (ares
->error
!= LDB_SUCCESS
) {
4656 /* call the normal callback for everything except success */
4657 return replmd_op_callback(req
, ares
);
4660 switch (req
->operation
) {
4662 conflict_dn
= req
->op
.add
.message
->dn
;
4665 conflict_dn
= req
->op
.mod
.message
->dn
;
4668 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
4671 /* perform a modify of the rDN and name of the record */
4672 ret
= replmd_name_modify(ar
, req
, conflict_dn
);
4673 if (ret
!= LDB_SUCCESS
) {
4675 return replmd_op_callback(req
, ares
);
4678 if (ar
->objs
->objects
[ar
->index_current
].last_known_parent
) {
4679 struct ldb_message
*msg
= ldb_msg_new(req
);
4681 ldb_module_oom(ar
->module
);
4682 return LDB_ERR_OPERATIONS_ERROR
;
4685 msg
->dn
= req
->op
.add
.message
->dn
;
4687 ret
= ldb_msg_add_steal_string(msg
, "lastKnownParent",
4688 ldb_dn_get_extended_linearized(msg
, ar
->objs
->objects
[ar
->index_current
].last_known_parent
, 1));
4689 if (ret
!= LDB_SUCCESS
) {
4690 DEBUG(0,(__location__
": Failed to add lastKnownParent string to the msg\n"));
4691 ldb_module_oom(ar
->module
);
4694 msg
->elements
[0].flags
= LDB_FLAG_MOD_REPLACE
;
4696 ret
= dsdb_module_modify(ar
->module
, msg
, DSDB_FLAG_OWN_MODULE
, req
);
4697 if (ret
!= LDB_SUCCESS
) {
4698 DEBUG(0,(__location__
": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
4699 ldb_dn_get_linearized(msg
->dn
),
4700 ldb_errstring(ldb_module_get_ctx(ar
->module
))));
4706 return replmd_op_callback(req
, ares
);
4710 callback for replmd_replicated_apply_add()
4711 This copes with the creation of conflict records in the case where
4712 the DN exists, but with a different objectGUID
4714 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
))
4716 struct ldb_dn
*conflict_dn
;
4717 struct replmd_replicated_request
*ar
=
4718 talloc_get_type_abort(req
->context
, struct replmd_replicated_request
);
4719 struct ldb_result
*res
;
4720 const char *attrs
[] = { "replPropertyMetaData", "objectGUID", NULL
};
4722 const struct ldb_val
*omd_value
;
4723 struct replPropertyMetaDataBlob omd
, *rmd
;
4724 enum ndr_err_code ndr_err
;
4725 bool rename_incoming_record
, rodc
;
4726 struct replPropertyMetaData1
*rmd_name
, *omd_name
;
4727 struct ldb_message
*msg
;
4728 struct ldb_request
*down_req
= NULL
;
4730 /* call the normal callback for success */
4731 if (ares
->error
== LDB_SUCCESS
) {
4732 return callback(req
, ares
);
4736 * we have a conflict, and need to decide if we will keep the
4737 * new record or the old record
4740 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
4741 conflict_dn
= msg
->dn
;
4743 /* For failures other than conflicts, fail the whole operation here */
4744 if (ares
->error
!= LDB_ERR_ENTRY_ALREADY_EXISTS
) {
4745 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
), "Failed to locally apply remote add of %s: %s",
4746 ldb_dn_get_linearized(conflict_dn
),
4747 ldb_errstring(ldb_module_get_ctx(ar
->module
)));
4749 return ldb_module_done(ar
->req
, NULL
, NULL
,
4750 LDB_ERR_OPERATIONS_ERROR
);
4753 ret
= samdb_rodc(ldb_module_get_ctx(ar
->module
), &rodc
);
4754 if (ret
!= LDB_SUCCESS
) {
4755 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
), "Failed to determine if we are an RODC when attempting to form conflict DN: %s", ldb_errstring(ldb_module_get_ctx(ar
->module
)));
4756 return ldb_module_done(ar
->req
, NULL
, NULL
,
4757 LDB_ERR_OPERATIONS_ERROR
);
4763 * We are on an RODC, or were a GC for this
4764 * partition, so we have to fail this until
4765 * someone who owns the partition sorts it
4768 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
4769 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
4770 " - We must fail the operation until a master for this partition resolves the conflict",
4771 ldb_dn_get_linearized(conflict_dn
));
4776 * first we need the replPropertyMetaData attribute from the
4777 * local, conflicting record
4779 ret
= dsdb_module_search_dn(ar
->module
, req
, &res
, conflict_dn
,
4781 DSDB_FLAG_NEXT_MODULE
|
4782 DSDB_SEARCH_SHOW_DELETED
|
4783 DSDB_SEARCH_SHOW_RECYCLED
, req
);
4784 if (ret
!= LDB_SUCCESS
) {
4785 DEBUG(0,(__location__
": Unable to find object for conflicting record '%s'\n",
4786 ldb_dn_get_linearized(conflict_dn
)));
4790 omd_value
= ldb_msg_find_ldb_val(res
->msgs
[0], "replPropertyMetaData");
4791 if (omd_value
== NULL
) {
4792 DEBUG(0,(__location__
": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4793 ldb_dn_get_linearized(conflict_dn
)));
4797 ndr_err
= ndr_pull_struct_blob(omd_value
, res
->msgs
[0], &omd
,
4798 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
4799 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
4800 DEBUG(0,(__location__
": Failed to parse old replPropertyMetaData for %s\n",
4801 ldb_dn_get_linearized(conflict_dn
)));
4805 rmd
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
4808 * we decide which is newer based on the RPMD on the name
4809 * attribute. See [MS-DRSR] ResolveNameConflict.
4811 * We expect omd_name to be present, as this is from a local
4812 * search, but while rmd_name should have been given to us by
4813 * the remote server, if it is missing we just prefer the
4815 * replmd_replPropertyMetaData1_new_should_be_taken()
4817 rmd_name
= replmd_replPropertyMetaData1_find_attid(rmd
, DRSUAPI_ATTID_name
);
4818 omd_name
= replmd_replPropertyMetaData1_find_attid(&omd
, DRSUAPI_ATTID_name
);
4820 DEBUG(0,(__location__
": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4821 ldb_dn_get_linearized(conflict_dn
)));
4826 * Should we preserve the current record, and so rename the
4827 * incoming record to be a conflict?
4829 rename_incoming_record
4830 = !replmd_replPropertyMetaData1_new_should_be_taken(ar
->objs
->dsdb_repl_flags
& DSDB_REPL_FLAG_PRIORITISE_INCOMING
,
4831 omd_name
, rmd_name
);
4833 if (rename_incoming_record
) {
4835 struct ldb_dn
*new_dn
;
4837 guid
= samdb_result_guid(msg
, "objectGUID");
4838 if (GUID_all_zero(&guid
)) {
4839 DEBUG(0,(__location__
": Failed to find objectGUID for conflicting incoming record %s\n",
4840 ldb_dn_get_linearized(conflict_dn
)));
4843 new_dn
= replmd_conflict_dn(req
, conflict_dn
, &guid
);
4844 if (new_dn
== NULL
) {
4845 DEBUG(0,(__location__
": Failed to form conflict DN for %s\n",
4846 ldb_dn_get_linearized(conflict_dn
)));
4850 DEBUG(2,(__location__
": Resolving conflict record via incoming rename '%s' -> '%s'\n",
4851 ldb_dn_get_linearized(conflict_dn
), ldb_dn_get_linearized(new_dn
)));
4853 /* re-submit the request, but with the new DN */
4854 callback
= replmd_op_name_modify_callback
;
4857 /* we are renaming the existing record */
4859 struct ldb_dn
*new_dn
;
4861 guid
= samdb_result_guid(res
->msgs
[0], "objectGUID");
4862 if (GUID_all_zero(&guid
)) {
4863 DEBUG(0,(__location__
": Failed to find objectGUID for existing conflict record %s\n",
4864 ldb_dn_get_linearized(conflict_dn
)));
4868 new_dn
= replmd_conflict_dn(req
, conflict_dn
, &guid
);
4869 if (new_dn
== NULL
) {
4870 DEBUG(0,(__location__
": Failed to form conflict DN for %s\n",
4871 ldb_dn_get_linearized(conflict_dn
)));
4875 DEBUG(2,(__location__
": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4876 ldb_dn_get_linearized(conflict_dn
), ldb_dn_get_linearized(new_dn
)));
4878 ret
= dsdb_module_rename(ar
->module
, conflict_dn
, new_dn
,
4879 DSDB_FLAG_OWN_MODULE
, req
);
4880 if (ret
!= LDB_SUCCESS
) {
4881 DEBUG(0,(__location__
": Failed to rename conflict dn '%s' to '%s' - %s\n",
4882 ldb_dn_get_linearized(conflict_dn
),
4883 ldb_dn_get_linearized(new_dn
),
4884 ldb_errstring(ldb_module_get_ctx(ar
->module
))));
4889 * now we need to ensure that the rename is seen as an
4890 * originating update. We do that with a modify.
4892 ret
= replmd_name_modify(ar
, req
, new_dn
);
4893 if (ret
!= LDB_SUCCESS
) {
4897 DEBUG(2,(__location__
": With conflicting record renamed, re-apply replicated creation of '%s'\n",
4898 ldb_dn_get_linearized(req
->op
.add
.message
->dn
)));
4901 ret
= ldb_build_add_req(&down_req
,
4902 ldb_module_get_ctx(ar
->module
),
4909 if (ret
!= LDB_SUCCESS
) {
4912 LDB_REQ_SET_LOCATION(down_req
);
4914 /* current partition control needed by "repmd_op_callback" */
4915 ret
= ldb_request_add_control(down_req
,
4916 DSDB_CONTROL_CURRENT_PARTITION_OID
,
4918 if (ret
!= LDB_SUCCESS
) {
4919 return replmd_replicated_request_error(ar
, ret
);
4922 if (ar
->objs
->dsdb_repl_flags
& DSDB_REPL_FLAG_PARTIAL_REPLICA
) {
4923 /* this tells the partition module to make it a
4924 partial replica if creating an NC */
4925 ret
= ldb_request_add_control(down_req
,
4926 DSDB_CONTROL_PARTIAL_REPLICA
,
4928 if (ret
!= LDB_SUCCESS
) {
4929 return replmd_replicated_request_error(ar
, ret
);
4934 * Finally we re-run the add, otherwise the new record won't
4935 * exist, as we are here because of that exact failure!
4937 return ldb_next_request(ar
->module
, down_req
);
4940 /* on failure make the caller get the error. This means
4941 * replication will stop with an error, but there is not much
4944 return ldb_module_done(ar
->req
, NULL
, NULL
,
4949 callback for replmd_replicated_apply_add()
4950 This copes with the creation of conflict records in the case where
4951 the DN exists, but with a different objectGUID
4953 static int replmd_op_add_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
4955 struct replmd_replicated_request
*ar
=
4956 talloc_get_type_abort(req
->context
, struct replmd_replicated_request
);
4958 if (ar
->objs
->objects
[ar
->index_current
].last_known_parent
) {
4959 /* This is like a conflict DN, where we put the object in LostAndFound
4960 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
4961 return replmd_op_possible_conflict_callback(req
, ares
, replmd_op_name_modify_callback
);
4964 return replmd_op_possible_conflict_callback(req
, ares
, replmd_op_callback
);
4968 this is called when a new object comes in over DRS
4970 static int replmd_replicated_apply_add(struct replmd_replicated_request
*ar
)
4972 struct ldb_context
*ldb
;
4973 struct ldb_request
*change_req
;
4974 enum ndr_err_code ndr_err
;
4975 struct ldb_message
*msg
;
4976 struct replPropertyMetaDataBlob
*md
;
4977 struct ldb_val md_value
;
4980 bool remote_isDeleted
= false;
4983 time_t t
= time(NULL
);
4984 const struct ldb_val
*rdn_val
;
4985 struct replmd_private
*replmd_private
=
4986 talloc_get_type(ldb_module_get_private(ar
->module
),
4987 struct replmd_private
);
4988 unix_to_nt_time(&now
, t
);
4990 ldb
= ldb_module_get_ctx(ar
->module
);
4991 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
4992 md
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
4993 is_schema_nc
= ldb_dn_compare_base(replmd_private
->schema_dn
, msg
->dn
) == 0;
4995 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &ar
->seq_num
);
4996 if (ret
!= LDB_SUCCESS
) {
4997 return replmd_replicated_request_error(ar
, ret
);
5000 ret
= dsdb_msg_add_guid(msg
,
5001 &ar
->objs
->objects
[ar
->index_current
].object_guid
,
5003 if (ret
!= LDB_SUCCESS
) {
5004 return replmd_replicated_request_error(ar
, ret
);
5007 ret
= ldb_msg_add_string(msg
, "whenChanged", ar
->objs
->objects
[ar
->index_current
].when_changed
);
5008 if (ret
!= LDB_SUCCESS
) {
5009 return replmd_replicated_request_error(ar
, ret
);
5012 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNCreated", ar
->seq_num
);
5013 if (ret
!= LDB_SUCCESS
) {
5014 return replmd_replicated_request_error(ar
, ret
);
5017 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNChanged", ar
->seq_num
);
5018 if (ret
!= LDB_SUCCESS
) {
5019 return replmd_replicated_request_error(ar
, ret
);
5022 /* remove any message elements that have zero values */
5023 for (i
=0; i
<msg
->num_elements
; i
++) {
5024 struct ldb_message_element
*el
= &msg
->elements
[i
];
5026 if (el
->num_values
== 0) {
5027 if (ldb_attr_cmp(msg
->elements
[i
].name
, "objectClass") == 0) {
5028 ldb_asprintf_errstring(ldb
, __location__
5029 ": empty objectClass sent on %s, aborting replication\n",
5030 ldb_dn_get_linearized(msg
->dn
));
5031 return replmd_replicated_request_error(ar
, LDB_ERR_OBJECT_CLASS_VIOLATION
);
5034 DEBUG(4,(__location__
": Removing attribute %s with num_values==0\n",
5036 memmove(el
, el
+1, sizeof(*el
)*(msg
->num_elements
- (i
+1)));
5037 msg
->num_elements
--;
5044 struct GUID_txt_buf guid_txt
;
5046 char *s
= ldb_ldif_message_redacted_string(ldb
, ar
,
5049 DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5050 GUID_buf_string(&ar
->objs
->objects
[ar
->index_current
].object_guid
, &guid_txt
),
5053 } else if (DEBUGLVL(4)) {
5054 struct GUID_txt_buf guid_txt
;
5055 DEBUG(4, ("DRS replication add DN of %s is %s\n",
5056 GUID_buf_string(&ar
->objs
->objects
[ar
->index_current
].object_guid
, &guid_txt
),
5057 ldb_dn_get_linearized(msg
->dn
)));
5059 remote_isDeleted
= ldb_msg_find_attr_as_bool(msg
,
5060 "isDeleted", false);
5063 * the meta data array is already sorted by the caller, except
5064 * for the RDN, which needs to be added.
5068 rdn_val
= ldb_dn_get_rdn_val(msg
->dn
);
5069 ret
= replmd_update_rpmd_rdn_attr(ldb
, msg
, rdn_val
, NULL
,
5070 md
, ar
, now
, is_schema_nc
,
5072 if (ret
!= LDB_SUCCESS
) {
5073 ldb_asprintf_errstring(ldb
, "%s: error during DRS repl ADD: %s", __func__
, ldb_errstring(ldb
));
5074 return replmd_replicated_request_error(ar
, ret
);
5077 ret
= replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb
, &md
->ctr
.ctr1
, msg
->dn
);
5078 if (ret
!= LDB_SUCCESS
) {
5079 ldb_asprintf_errstring(ldb
, "%s: error during DRS repl ADD: %s", __func__
, ldb_errstring(ldb
));
5080 return replmd_replicated_request_error(ar
, ret
);
5083 for (i
=0; i
< md
->ctr
.ctr1
.count
; i
++) {
5084 md
->ctr
.ctr1
.array
[i
].local_usn
= ar
->seq_num
;
5086 ndr_err
= ndr_push_struct_blob(&md_value
, msg
, md
,
5087 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
5088 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
5089 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
5090 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
5092 ret
= ldb_msg_add_value(msg
, "replPropertyMetaData", &md_value
, NULL
);
5093 if (ret
!= LDB_SUCCESS
) {
5094 return replmd_replicated_request_error(ar
, ret
);
5097 replmd_ldb_message_sort(msg
, ar
->schema
);
5099 if (!remote_isDeleted
) {
5100 ret
= dsdb_module_schedule_sd_propagation(ar
->module
,
5101 ar
->objs
->partition_dn
,
5103 if (ret
!= LDB_SUCCESS
) {
5104 return replmd_replicated_request_error(ar
, ret
);
5108 ar
->isDeleted
= remote_isDeleted
;
5110 ret
= ldb_build_add_req(&change_req
,
5116 replmd_op_add_callback
,
5118 LDB_REQ_SET_LOCATION(change_req
);
5119 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
5121 /* current partition control needed by "repmd_op_callback" */
5122 ret
= ldb_request_add_control(change_req
,
5123 DSDB_CONTROL_CURRENT_PARTITION_OID
,
5125 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
5127 if (ar
->objs
->dsdb_repl_flags
& DSDB_REPL_FLAG_PARTIAL_REPLICA
) {
5128 /* this tells the partition module to make it a
5129 partial replica if creating an NC */
5130 ret
= ldb_request_add_control(change_req
,
5131 DSDB_CONTROL_PARTIAL_REPLICA
,
5133 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
5136 return ldb_next_request(ar
->module
, change_req
);
5139 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request
*req
,
5140 struct ldb_reply
*ares
)
5142 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
5143 struct replmd_replicated_request
);
5147 return ldb_module_done(ar
->req
, NULL
, NULL
,
5148 LDB_ERR_OPERATIONS_ERROR
);
5152 * The error NO_SUCH_OBJECT is not expected, unless the search
5153 * base is the partition DN, and that case doesn't happen here
5154 * because then we wouldn't get a parent_guid_value in any
5157 if (ares
->error
!= LDB_SUCCESS
) {
5158 return ldb_module_done(ar
->req
, ares
->controls
,
5159 ares
->response
, ares
->error
);
5162 switch (ares
->type
) {
5163 case LDB_REPLY_ENTRY
:
5165 struct ldb_message
*parent_msg
= ares
->message
;
5166 struct ldb_message
*msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
5167 struct ldb_dn
*parent_dn
;
5170 if (!ldb_msg_check_string_attribute(msg
, "isDeleted", "TRUE")
5171 && ldb_msg_check_string_attribute(parent_msg
, "isDeleted", "TRUE")) {
5172 /* Per MS-DRSR 4.1.10.6.10
5173 * FindBestParentObject we need to move this
5174 * new object under a deleted object to
5176 struct ldb_dn
*nc_root
;
5178 ret
= dsdb_find_nc_root(ldb_module_get_ctx(ar
->module
), msg
, msg
->dn
, &nc_root
);
5179 if (ret
== LDB_ERR_NO_SUCH_OBJECT
) {
5180 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5181 "No suitable NC root found for %s. "
5182 "We need to move this object because parent object %s "
5183 "is deleted, but this object is not.",
5184 ldb_dn_get_linearized(msg
->dn
),
5185 ldb_dn_get_linearized(parent_msg
->dn
));
5186 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
5187 } else if (ret
!= LDB_SUCCESS
) {
5188 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5189 "Unable to find NC root for %s: %s. "
5190 "We need to move this object because parent object %s "
5191 "is deleted, but this object is not.",
5192 ldb_dn_get_linearized(msg
->dn
),
5193 ldb_errstring(ldb_module_get_ctx(ar
->module
)),
5194 ldb_dn_get_linearized(parent_msg
->dn
));
5195 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
5198 ret
= dsdb_wellknown_dn(ldb_module_get_ctx(ar
->module
), msg
,
5200 DS_GUID_LOSTANDFOUND_CONTAINER
,
5202 if (ret
!= LDB_SUCCESS
) {
5203 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5204 "Unable to find LostAndFound Container for %s "
5205 "in partition %s: %s. "
5206 "We need to move this object because parent object %s "
5207 "is deleted, but this object is not.",
5208 ldb_dn_get_linearized(msg
->dn
), ldb_dn_get_linearized(nc_root
),
5209 ldb_errstring(ldb_module_get_ctx(ar
->module
)),
5210 ldb_dn_get_linearized(parent_msg
->dn
));
5211 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
5213 ar
->objs
->objects
[ar
->index_current
].last_known_parent
5214 = talloc_steal(ar
->objs
->objects
[ar
->index_current
].msg
, parent_msg
->dn
);
5218 = talloc_steal(ar
->objs
->objects
[ar
->index_current
].msg
, parent_msg
->dn
);
5221 ar
->objs
->objects
[ar
->index_current
].local_parent_dn
= parent_dn
;
5223 comp_num
= ldb_dn_get_comp_num(msg
->dn
);
5225 if (!ldb_dn_remove_base_components(msg
->dn
, comp_num
- 1)) {
5227 return ldb_module_done(ar
->req
, NULL
, NULL
, ldb_module_operr(ar
->module
));
5230 if (!ldb_dn_add_base(msg
->dn
, parent_dn
)) {
5232 return ldb_module_done(ar
->req
, NULL
, NULL
, ldb_module_operr(ar
->module
));
5236 case LDB_REPLY_REFERRAL
:
5237 /* we ignore referrals */
5240 case LDB_REPLY_DONE
:
5242 if (ar
->objs
->objects
[ar
->index_current
].local_parent_dn
== NULL
) {
5243 struct GUID_txt_buf str_buf
;
5244 if (ar
->search_msg
!= NULL
) {
5245 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5246 "No parent with GUID %s found for object locally known as %s",
5247 GUID_buf_string(ar
->objs
->objects
[ar
->index_current
].parent_guid
, &str_buf
),
5248 ldb_dn_get_linearized(ar
->search_msg
->dn
));
5250 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5251 "No parent with GUID %s found for object remotely known as %s",
5252 GUID_buf_string(ar
->objs
->objects
[ar
->index_current
].parent_guid
, &str_buf
),
5253 ldb_dn_get_linearized(ar
->objs
->objects
[ar
->index_current
].msg
->dn
));
5257 * This error code is really important, as it
5258 * is the flag back to the callers to retry
5259 * this with DRSUAPI_DRS_GET_ANC, and so get
5260 * the parent objects before the child
5263 return ldb_module_done(ar
->req
, NULL
, NULL
,
5264 replmd_replicated_request_werror(ar
, WERR_DS_DRA_MISSING_PARENT
));
5267 if (ar
->search_msg
!= NULL
) {
5268 ret
= replmd_replicated_apply_merge(ar
);
5270 ret
= replmd_replicated_apply_add(ar
);
5272 if (ret
!= LDB_SUCCESS
) {
5273 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
5282 * Look for the parent object, so we put the new object in the right
5283 * place This is akin to NameObject in MS-DRSR - this routine and the
5284 * callbacks find the right parent name, and correct name for this
5288 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request
*ar
)
5290 struct ldb_context
*ldb
;
5294 struct ldb_request
*search_req
;
5295 static const char *attrs
[] = {"isDeleted", NULL
};
5296 struct GUID_txt_buf guid_str_buf
;
5298 ldb
= ldb_module_get_ctx(ar
->module
);
5300 if (ar
->objs
->objects
[ar
->index_current
].parent_guid
== NULL
) {
5301 if (ar
->search_msg
!= NULL
) {
5302 return replmd_replicated_apply_merge(ar
);
5304 return replmd_replicated_apply_add(ar
);
5308 tmp_str
= GUID_buf_string(ar
->objs
->objects
[ar
->index_current
].parent_guid
,
5311 filter
= talloc_asprintf(ar
, "(objectGUID=%s)", tmp_str
);
5312 if (!filter
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
5314 ret
= ldb_build_search_req(&search_req
,
5317 ar
->objs
->partition_dn
,
5323 replmd_replicated_apply_search_for_parent_callback
,
5325 LDB_REQ_SET_LOCATION(search_req
);
5327 ret
= dsdb_request_add_controls(search_req
,
5328 DSDB_SEARCH_SHOW_RECYCLED
|
5329 DSDB_SEARCH_SHOW_DELETED
|
5330 DSDB_SEARCH_SHOW_EXTENDED_DN
);
5331 if (ret
!= LDB_SUCCESS
) {
5335 return ldb_next_request(ar
->module
, search_req
);
5339 handle renames that come in over DRS replication
5341 static int replmd_replicated_handle_rename(struct replmd_replicated_request
*ar
,
5342 struct ldb_message
*msg
,
5343 struct ldb_request
*parent
,
5347 TALLOC_CTX
*tmp_ctx
= talloc_new(msg
);
5348 struct ldb_result
*res
;
5349 struct ldb_dn
*conflict_dn
;
5350 const char *attrs
[] = { "replPropertyMetaData", "objectGUID", NULL
};
5351 const struct ldb_val
*omd_value
;
5352 struct replPropertyMetaDataBlob omd
, *rmd
;
5353 enum ndr_err_code ndr_err
;
5354 bool rename_incoming_record
, rodc
;
5355 struct replPropertyMetaData1
*rmd_name
, *omd_name
;
5356 struct ldb_dn
*new_dn
;
5359 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5360 ldb_dn_get_linearized(ar
->search_msg
->dn
),
5361 ldb_dn_get_linearized(msg
->dn
)));
5364 ret
= dsdb_module_rename(ar
->module
, ar
->search_msg
->dn
, msg
->dn
,
5365 DSDB_FLAG_NEXT_MODULE
, ar
->req
);
5366 if (ret
== LDB_SUCCESS
) {
5367 talloc_free(tmp_ctx
);
5372 if (ret
!= LDB_ERR_ENTRY_ALREADY_EXISTS
) {
5373 talloc_free(tmp_ctx
);
5374 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
), "Failed to locally apply remote rename from %s to %s: %s",
5375 ldb_dn_get_linearized(ar
->search_msg
->dn
),
5376 ldb_dn_get_linearized(msg
->dn
),
5377 ldb_errstring(ldb_module_get_ctx(ar
->module
)));
5381 ret
= samdb_rodc(ldb_module_get_ctx(ar
->module
), &rodc
);
5382 if (ret
!= LDB_SUCCESS
) {
5383 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5384 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5385 ldb_errstring(ldb_module_get_ctx(ar
->module
)));
5386 return LDB_ERR_OPERATIONS_ERROR
;
5389 * we have a conflict, and need to decide if we will keep the
5390 * new record or the old record
5393 conflict_dn
= msg
->dn
;
5397 * We are on an RODC, or were a GC for this
5398 * partition, so we have to fail this until
5399 * someone who owns the partition sorts it
5402 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5403 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5404 " - We must fail the operation until a master for this partition resolves the conflict",
5405 ldb_dn_get_linearized(conflict_dn
));
5410 * first we need the replPropertyMetaData attribute from the
5413 ret
= dsdb_module_search_dn(ar
->module
, tmp_ctx
, &res
, conflict_dn
,
5415 DSDB_FLAG_NEXT_MODULE
|
5416 DSDB_SEARCH_SHOW_DELETED
|
5417 DSDB_SEARCH_SHOW_RECYCLED
, ar
->req
);
5418 if (ret
!= LDB_SUCCESS
) {
5419 DEBUG(0,(__location__
": Unable to find object for conflicting record '%s'\n",
5420 ldb_dn_get_linearized(conflict_dn
)));
5424 omd_value
= ldb_msg_find_ldb_val(res
->msgs
[0], "replPropertyMetaData");
5425 if (omd_value
== NULL
) {
5426 DEBUG(0,(__location__
": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5427 ldb_dn_get_linearized(conflict_dn
)));
5431 ndr_err
= ndr_pull_struct_blob(omd_value
, res
->msgs
[0], &omd
,
5432 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
5433 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
5434 DEBUG(0,(__location__
": Failed to parse old replPropertyMetaData for %s\n",
5435 ldb_dn_get_linearized(conflict_dn
)));
5439 rmd
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
5442 * we decide which is newer based on the RPMD on the name
5443 * attribute. See [MS-DRSR] ResolveNameConflict.
5445 * We expect omd_name to be present, as this is from a local
5446 * search, but while rmd_name should have been given to us by
5447 * the remote server, if it is missing we just prefer the
5449 * replmd_replPropertyMetaData1_new_should_be_taken()
5451 rmd_name
= replmd_replPropertyMetaData1_find_attid(rmd
, DRSUAPI_ATTID_name
);
5452 omd_name
= replmd_replPropertyMetaData1_find_attid(&omd
, DRSUAPI_ATTID_name
);
5454 DEBUG(0,(__location__
": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5455 ldb_dn_get_linearized(conflict_dn
)));
5460 * Should we preserve the current record, and so rename the
5461 * incoming record to be a conflict?
5463 rename_incoming_record
=
5464 !replmd_replPropertyMetaData1_new_should_be_taken(
5465 ar
->objs
->dsdb_repl_flags
& DSDB_REPL_FLAG_PRIORITISE_INCOMING
,
5466 omd_name
, rmd_name
);
5468 if (rename_incoming_record
) {
5470 new_dn
= replmd_conflict_dn(msg
, msg
->dn
,
5471 &ar
->objs
->objects
[ar
->index_current
].object_guid
);
5472 if (new_dn
== NULL
) {
5473 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5474 "Failed to form conflict DN for %s\n",
5475 ldb_dn_get_linearized(msg
->dn
));
5477 return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
5480 ret
= dsdb_module_rename(ar
->module
, ar
->search_msg
->dn
, new_dn
,
5481 DSDB_FLAG_NEXT_MODULE
, ar
->req
);
5482 if (ret
!= LDB_SUCCESS
) {
5483 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5484 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5485 ldb_dn_get_linearized(conflict_dn
),
5486 ldb_dn_get_linearized(ar
->search_msg
->dn
),
5487 ldb_dn_get_linearized(new_dn
),
5488 ldb_errstring(ldb_module_get_ctx(ar
->module
)));
5489 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_DB_ERROR
);
5497 /* we are renaming the existing record */
5499 guid
= samdb_result_guid(res
->msgs
[0], "objectGUID");
5500 if (GUID_all_zero(&guid
)) {
5501 DEBUG(0,(__location__
": Failed to find objectGUID for existing conflict record %s\n",
5502 ldb_dn_get_linearized(conflict_dn
)));
5506 new_dn
= replmd_conflict_dn(tmp_ctx
, conflict_dn
, &guid
);
5507 if (new_dn
== NULL
) {
5508 DEBUG(0,(__location__
": Failed to form conflict DN for %s\n",
5509 ldb_dn_get_linearized(conflict_dn
)));
5513 DEBUG(2,(__location__
": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5514 ldb_dn_get_linearized(conflict_dn
), ldb_dn_get_linearized(new_dn
)));
5516 ret
= dsdb_module_rename(ar
->module
, conflict_dn
, new_dn
,
5517 DSDB_FLAG_OWN_MODULE
, ar
->req
);
5518 if (ret
!= LDB_SUCCESS
) {
5519 DEBUG(0,(__location__
": Failed to rename conflict dn '%s' to '%s' - %s\n",
5520 ldb_dn_get_linearized(conflict_dn
),
5521 ldb_dn_get_linearized(new_dn
),
5522 ldb_errstring(ldb_module_get_ctx(ar
->module
))));
5527 * now we need to ensure that the rename is seen as an
5528 * originating update. We do that with a modify.
5530 ret
= replmd_name_modify(ar
, ar
->req
, new_dn
);
5531 if (ret
!= LDB_SUCCESS
) {
5535 DEBUG(2,(__location__
": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
5536 ldb_dn_get_linearized(ar
->search_msg
->dn
),
5537 ldb_dn_get_linearized(msg
->dn
)));
5540 ret
= dsdb_module_rename(ar
->module
, ar
->search_msg
->dn
, msg
->dn
,
5541 DSDB_FLAG_NEXT_MODULE
, ar
->req
);
5542 if (ret
!= LDB_SUCCESS
) {
5543 DEBUG(0,(__location__
": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
5544 ldb_dn_get_linearized(ar
->search_msg
->dn
),
5545 ldb_dn_get_linearized(msg
->dn
),
5546 ldb_errstring(ldb_module_get_ctx(ar
->module
))));
5552 * On failure make the caller get the error
5553 * This means replication will stop with an error,
5554 * but there is not much else we can do. In the
5555 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
5559 talloc_free(tmp_ctx
);
5564 static int replmd_replicated_apply_merge(struct replmd_replicated_request
*ar
)
5566 struct ldb_context
*ldb
;
5567 struct ldb_request
*change_req
;
5568 enum ndr_err_code ndr_err
;
5569 struct ldb_message
*msg
;
5570 struct replPropertyMetaDataBlob
*rmd
;
5571 struct replPropertyMetaDataBlob omd
;
5572 const struct ldb_val
*omd_value
;
5573 struct replPropertyMetaDataBlob nmd
;
5574 struct ldb_val nmd_value
;
5575 struct GUID remote_parent_guid
;
5578 unsigned int removed_attrs
= 0;
5580 int (*callback
)(struct ldb_request
*req
, struct ldb_reply
*ares
) = replmd_op_callback
;
5581 bool isDeleted
= false;
5582 bool local_isDeleted
= false;
5583 bool remote_isDeleted
= false;
5584 bool take_remote_isDeleted
= false;
5585 bool sd_updated
= false;
5586 bool renamed
= false;
5587 bool is_schema_nc
= false;
5589 const struct ldb_val
*old_rdn
, *new_rdn
;
5590 struct replmd_private
*replmd_private
=
5591 talloc_get_type(ldb_module_get_private(ar
->module
),
5592 struct replmd_private
);
5594 time_t t
= time(NULL
);
5595 unix_to_nt_time(&now
, t
);
5597 ldb
= ldb_module_get_ctx(ar
->module
);
5598 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
5600 is_schema_nc
= ldb_dn_compare_base(replmd_private
->schema_dn
, msg
->dn
) == 0;
5602 rmd
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
5606 /* find existing meta data */
5607 omd_value
= ldb_msg_find_ldb_val(ar
->search_msg
, "replPropertyMetaData");
5609 ndr_err
= ndr_pull_struct_blob(omd_value
, ar
, &omd
,
5610 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
5611 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
5612 nt_status
= ndr_map_error2ntstatus(ndr_err
);
5613 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
5616 if (omd
.version
!= 1) {
5617 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
5622 struct GUID_txt_buf guid_txt
;
5624 char *s
= ldb_ldif_message_redacted_string(ldb
, ar
,
5625 LDB_CHANGETYPE_MODIFY
, msg
);
5626 DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
5629 GUID_buf_string(&ar
->objs
->objects
[ar
->index_current
].object_guid
, &guid_txt
),
5631 ndr_print_struct_string(s
,
5632 (ndr_print_fn_t
)ndr_print_replPropertyMetaDataBlob
,
5633 "existing replPropertyMetaData",
5635 ndr_print_struct_string(s
,
5636 (ndr_print_fn_t
)ndr_print_replPropertyMetaDataBlob
,
5637 "incoming replPropertyMetaData",
5640 } else if (DEBUGLVL(4)) {
5641 struct GUID_txt_buf guid_txt
;
5643 DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
5644 GUID_buf_string(&ar
->objs
->objects
[ar
->index_current
].object_guid
,
5646 ldb_dn_get_linearized(msg
->dn
)));
5649 local_isDeleted
= ldb_msg_find_attr_as_bool(ar
->search_msg
,
5650 "isDeleted", false);
5651 remote_isDeleted
= ldb_msg_find_attr_as_bool(msg
,
5652 "isDeleted", false);
5655 * Fill in the remote_parent_guid with the GUID or an all-zero
5658 if (ar
->objs
->objects
[ar
->index_current
].parent_guid
!= NULL
) {
5659 remote_parent_guid
= *ar
->objs
->objects
[ar
->index_current
].parent_guid
;
5661 remote_parent_guid
= GUID_zero();
5665 * To ensure we follow a complex rename chain around, we have
5666 * to confirm that the DN is the same (mostly to confirm the
5667 * RDN) and the parentGUID is the same.
5669 * This ensures we keep things under the correct parent, which
5670 * replmd_replicated_handle_rename() will do.
5673 if (strcmp(ldb_dn_get_linearized(msg
->dn
), ldb_dn_get_linearized(ar
->search_msg
->dn
)) == 0
5674 && GUID_equal(&remote_parent_guid
, &ar
->local_parent_guid
)) {
5678 * handle renames, even just by case that come in over
5679 * DRS. Changes in the parent DN don't hit us here,
5680 * because the search for a parent will clean up those
5683 * We also have already filtered out the case where
5684 * the peer has an older name to what we have (see
5685 * replmd_replicated_apply_search_callback())
5687 ret
= replmd_replicated_handle_rename(ar
, msg
, ar
->req
, &renamed
);
5690 if (ret
!= LDB_SUCCESS
) {
5691 ldb_debug(ldb
, LDB_DEBUG_FATAL
,
5692 "replmd_replicated_request rename %s => %s failed - %s\n",
5693 ldb_dn_get_linearized(ar
->search_msg
->dn
),
5694 ldb_dn_get_linearized(msg
->dn
),
5695 ldb_errstring(ldb
));
5696 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_DB_ERROR
);
5699 if (renamed
== true) {
5701 * Set the callback to one that will fix up the name
5702 * metadata on the new conflict DN
5704 callback
= replmd_op_name_modify_callback
;
5709 nmd
.ctr
.ctr1
.count
= omd
.ctr
.ctr1
.count
+ rmd
->ctr
.ctr1
.count
;
5710 nmd
.ctr
.ctr1
.array
= talloc_array(ar
,
5711 struct replPropertyMetaData1
,
5712 nmd
.ctr
.ctr1
.count
);
5713 if (!nmd
.ctr
.ctr1
.array
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
5715 /* first copy the old meta data */
5716 for (i
=0; i
< omd
.ctr
.ctr1
.count
; i
++) {
5717 nmd
.ctr
.ctr1
.array
[ni
] = omd
.ctr
.ctr1
.array
[i
];
5722 /* now merge in the new meta data */
5723 for (i
=0; i
< rmd
->ctr
.ctr1
.count
; i
++) {
5726 for (j
=0; j
< ni
; j
++) {
5729 if (rmd
->ctr
.ctr1
.array
[i
].attid
!= nmd
.ctr
.ctr1
.array
[j
].attid
) {
5733 cmp
= replmd_replPropertyMetaData1_new_should_be_taken(
5734 ar
->objs
->dsdb_repl_flags
,
5735 &nmd
.ctr
.ctr1
.array
[j
],
5736 &rmd
->ctr
.ctr1
.array
[i
]);
5738 /* replace the entry */
5739 nmd
.ctr
.ctr1
.array
[j
] = rmd
->ctr
.ctr1
.array
[i
];
5740 if (ar
->seq_num
== 0) {
5741 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &ar
->seq_num
);
5742 if (ret
!= LDB_SUCCESS
) {
5743 return replmd_replicated_request_error(ar
, ret
);
5746 nmd
.ctr
.ctr1
.array
[j
].local_usn
= ar
->seq_num
;
5747 switch (nmd
.ctr
.ctr1
.array
[j
].attid
) {
5748 case DRSUAPI_ATTID_ntSecurityDescriptor
:
5751 case DRSUAPI_ATTID_isDeleted
:
5752 take_remote_isDeleted
= true;
5761 if (rmd
->ctr
.ctr1
.array
[i
].attid
!= DRSUAPI_ATTID_instanceType
) {
5762 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
5763 msg
->elements
[i
-removed_attrs
].name
,
5764 ldb_dn_get_linearized(msg
->dn
),
5765 GUID_string(ar
, &rmd
->ctr
.ctr1
.array
[i
].originating_invocation_id
)));
5768 /* we don't want to apply this change so remove the attribute */
5769 ldb_msg_remove_element(msg
, &msg
->elements
[i
-removed_attrs
]);
5776 if (found
) continue;
5778 nmd
.ctr
.ctr1
.array
[ni
] = rmd
->ctr
.ctr1
.array
[i
];
5779 if (ar
->seq_num
== 0) {
5780 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &ar
->seq_num
);
5781 if (ret
!= LDB_SUCCESS
) {
5782 return replmd_replicated_request_error(ar
, ret
);
5785 nmd
.ctr
.ctr1
.array
[ni
].local_usn
= ar
->seq_num
;
5786 switch (nmd
.ctr
.ctr1
.array
[ni
].attid
) {
5787 case DRSUAPI_ATTID_ntSecurityDescriptor
:
5790 case DRSUAPI_ATTID_isDeleted
:
5791 take_remote_isDeleted
= true;
5800 * finally correct the size of the meta_data array
5802 nmd
.ctr
.ctr1
.count
= ni
;
5804 new_rdn
= ldb_dn_get_rdn_val(msg
->dn
);
5805 old_rdn
= ldb_dn_get_rdn_val(ar
->search_msg
->dn
);
5808 ret
= replmd_update_rpmd_rdn_attr(ldb
, msg
, new_rdn
, old_rdn
,
5809 &nmd
, ar
, now
, is_schema_nc
,
5811 if (ret
!= LDB_SUCCESS
) {
5812 ldb_asprintf_errstring(ldb
, "%s: error during DRS repl merge: %s", __func__
, ldb_errstring(ldb
));
5813 return replmd_replicated_request_error(ar
, ret
);
5817 * sort the new meta data array
5819 ret
= replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb
, &nmd
.ctr
.ctr1
, msg
->dn
);
5820 if (ret
!= LDB_SUCCESS
) {
5821 ldb_asprintf_errstring(ldb
, "%s: error during DRS repl merge: %s", __func__
, ldb_errstring(ldb
));
5826 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
5829 * This also controls SD propagation below
5831 if (take_remote_isDeleted
) {
5832 isDeleted
= remote_isDeleted
;
5834 isDeleted
= local_isDeleted
;
5837 ar
->isDeleted
= isDeleted
;
5840 * check if some replicated attributes left, otherwise skip the ldb_modify() call
5842 if (msg
->num_elements
== 0) {
5843 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_replicated_apply_merge[%u]: skip replace\n",
5846 return replmd_replicated_apply_isDeleted(ar
);
5849 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
5850 ar
->index_current
, msg
->num_elements
);
5856 if (sd_updated
&& !isDeleted
) {
5857 ret
= dsdb_module_schedule_sd_propagation(ar
->module
,
5858 ar
->objs
->partition_dn
,
5860 if (ret
!= LDB_SUCCESS
) {
5861 return ldb_operr(ldb
);
5865 /* create the meta data value */
5866 ndr_err
= ndr_push_struct_blob(&nmd_value
, msg
, &nmd
,
5867 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
5868 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
5869 nt_status
= ndr_map_error2ntstatus(ndr_err
);
5870 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
5874 * when we know that we'll modify the record, add the whenChanged, uSNChanged
5875 * and replPopertyMetaData attributes
5877 ret
= ldb_msg_add_string(msg
, "whenChanged", ar
->objs
->objects
[ar
->index_current
].when_changed
);
5878 if (ret
!= LDB_SUCCESS
) {
5879 return replmd_replicated_request_error(ar
, ret
);
5881 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNChanged", ar
->seq_num
);
5882 if (ret
!= LDB_SUCCESS
) {
5883 return replmd_replicated_request_error(ar
, ret
);
5885 ret
= ldb_msg_add_value(msg
, "replPropertyMetaData", &nmd_value
, NULL
);
5886 if (ret
!= LDB_SUCCESS
) {
5887 return replmd_replicated_request_error(ar
, ret
);
5890 replmd_ldb_message_sort(msg
, ar
->schema
);
5892 /* we want to replace the old values */
5893 for (i
=0; i
< msg
->num_elements
; i
++) {
5894 msg
->elements
[i
].flags
= LDB_FLAG_MOD_REPLACE
;
5895 if (ldb_attr_cmp(msg
->elements
[i
].name
, "objectClass") == 0) {
5896 if (msg
->elements
[i
].num_values
== 0) {
5897 ldb_asprintf_errstring(ldb
, __location__
5898 ": objectClass removed on %s, aborting replication\n",
5899 ldb_dn_get_linearized(msg
->dn
));
5900 return replmd_replicated_request_error(ar
, LDB_ERR_OBJECT_CLASS_VIOLATION
);
5906 struct GUID_txt_buf guid_txt
;
5908 char *s
= ldb_ldif_message_redacted_string(ldb
, ar
,
5909 LDB_CHANGETYPE_MODIFY
,
5911 DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
5912 GUID_buf_string(&ar
->objs
->objects
[ar
->index_current
].object_guid
,
5916 } else if (DEBUGLVL(4)) {
5917 struct GUID_txt_buf guid_txt
;
5919 DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
5920 GUID_buf_string(&ar
->objs
->objects
[ar
->index_current
].object_guid
,
5922 ldb_dn_get_linearized(msg
->dn
)));
5925 ret
= ldb_build_mod_req(&change_req
,
5933 LDB_REQ_SET_LOCATION(change_req
);
5934 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
5936 /* current partition control needed by "repmd_op_callback" */
5937 ret
= ldb_request_add_control(change_req
,
5938 DSDB_CONTROL_CURRENT_PARTITION_OID
,
5940 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
5942 return ldb_next_request(ar
->module
, change_req
);
5945 static int replmd_replicated_apply_search_callback(struct ldb_request
*req
,
5946 struct ldb_reply
*ares
)
5948 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
5949 struct replmd_replicated_request
);
5953 return ldb_module_done(ar
->req
, NULL
, NULL
,
5954 LDB_ERR_OPERATIONS_ERROR
);
5956 if (ares
->error
!= LDB_SUCCESS
&&
5957 ares
->error
!= LDB_ERR_NO_SUCH_OBJECT
) {
5958 return ldb_module_done(ar
->req
, ares
->controls
,
5959 ares
->response
, ares
->error
);
5962 switch (ares
->type
) {
5963 case LDB_REPLY_ENTRY
:
5964 ar
->search_msg
= talloc_steal(ar
, ares
->message
);
5967 case LDB_REPLY_REFERRAL
:
5968 /* we ignore referrals */
5971 case LDB_REPLY_DONE
:
5973 struct replPropertyMetaData1
*md_remote
;
5974 struct replPropertyMetaData1
*md_local
;
5976 struct replPropertyMetaDataBlob omd
;
5977 const struct ldb_val
*omd_value
;
5978 struct replPropertyMetaDataBlob
*rmd
;
5979 struct ldb_message
*msg
;
5981 ar
->objs
->objects
[ar
->index_current
].local_parent_dn
= NULL
;
5982 ar
->objs
->objects
[ar
->index_current
].last_known_parent
= NULL
;
5985 * This is the ADD case, find the appropriate parent,
5986 * as this object doesn't exist locally:
5988 if (ar
->search_msg
== NULL
) {
5989 ret
= replmd_replicated_apply_search_for_parent(ar
);
5990 if (ret
!= LDB_SUCCESS
) {
5991 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
5998 * Otherwise, in the MERGE case, work out if we are
5999 * attempting a rename, and if so find the parent the
6000 * newly renamed object wants to belong under (which
6001 * may not be the parent in it's attached string DN
6003 rmd
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
6007 /* find existing meta data */
6008 omd_value
= ldb_msg_find_ldb_val(ar
->search_msg
, "replPropertyMetaData");
6010 enum ndr_err_code ndr_err
;
6011 ndr_err
= ndr_pull_struct_blob(omd_value
, ar
, &omd
,
6012 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
6013 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
6014 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
6015 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
6018 if (omd
.version
!= 1) {
6019 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
6023 ar
->local_parent_guid
= samdb_result_guid(ar
->search_msg
, "parentGUID");
6025 instanceType
= ldb_msg_find_attr_as_int(ar
->search_msg
, "instanceType", 0);
6026 if (((instanceType
& INSTANCE_TYPE_IS_NC_HEAD
) == 0)
6027 && GUID_all_zero(&ar
->local_parent_guid
)) {
6028 DEBUG(0, ("Refusing to replicate new version of %s "
6029 "as local object has an all-zero parentGUID attribute, "
6030 "despite not being an NC root\n",
6031 ldb_dn_get_linearized(ar
->search_msg
->dn
)));
6032 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
6036 * now we need to check for double renames. We could have a
6037 * local rename pending which our replication partner hasn't
6038 * received yet. We choose which one wins by looking at the
6039 * attribute stamps on the two objects, the newer one wins.
6041 * This also simply applies the correct algorithms for
6042 * determining if a change was made to name at all, or
6043 * if the object has just been renamed under the same
6046 md_remote
= replmd_replPropertyMetaData1_find_attid(rmd
, DRSUAPI_ATTID_name
);
6047 md_local
= replmd_replPropertyMetaData1_find_attid(&omd
, DRSUAPI_ATTID_name
);
6049 DEBUG(0,(__location__
": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6050 ldb_dn_get_linearized(ar
->search_msg
->dn
)));
6051 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_DB_ERROR
);
6055 * if there is no name attribute given then we have to assume the
6056 * object we've received has the older name
6058 if (replmd_replPropertyMetaData1_new_should_be_taken(
6059 ar
->objs
->dsdb_repl_flags
& DSDB_REPL_FLAG_PRIORITISE_INCOMING
,
6060 md_local
, md_remote
)) {
6061 struct GUID_txt_buf p_guid_local
;
6062 struct GUID_txt_buf p_guid_remote
;
6063 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
6065 /* Merge on the existing object, with rename */
6067 DEBUG(4,(__location__
": Looking for new parent for object %s currently under %s "
6068 "as incoming object changing to %s under %s\n",
6069 ldb_dn_get_linearized(ar
->search_msg
->dn
),
6070 GUID_buf_string(&ar
->local_parent_guid
, &p_guid_local
),
6071 ldb_dn_get_linearized(msg
->dn
),
6072 GUID_buf_string(ar
->objs
->objects
[ar
->index_current
].parent_guid
,
6074 ret
= replmd_replicated_apply_search_for_parent(ar
);
6076 struct GUID_txt_buf p_guid_local
;
6077 struct GUID_txt_buf p_guid_remote
;
6078 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
6081 * Merge on the existing object, force no
6082 * rename (code below just to explain why in
6086 if (strcmp(ldb_dn_get_linearized(ar
->search_msg
->dn
),
6087 ldb_dn_get_linearized(msg
->dn
)) == 0) {
6088 if (ar
->objs
->objects
[ar
->index_current
].parent_guid
!= NULL
&&
6089 GUID_equal(&ar
->local_parent_guid
,
6090 ar
->objs
->objects
[ar
->index_current
].parent_guid
)
6092 DEBUG(4,(__location__
": Keeping object %s at under %s "
6093 "despite incoming object changing parent to %s\n",
6094 ldb_dn_get_linearized(ar
->search_msg
->dn
),
6095 GUID_buf_string(&ar
->local_parent_guid
, &p_guid_local
),
6096 GUID_buf_string(ar
->objs
->objects
[ar
->index_current
].parent_guid
,
6100 DEBUG(4,(__location__
": Keeping object %s at under %s "
6101 " and rejecting older rename to %s under %s\n",
6102 ldb_dn_get_linearized(ar
->search_msg
->dn
),
6103 GUID_buf_string(&ar
->local_parent_guid
, &p_guid_local
),
6104 ldb_dn_get_linearized(msg
->dn
),
6105 GUID_buf_string(ar
->objs
->objects
[ar
->index_current
].parent_guid
,
6109 * This assignment ensures that the strcmp()
6110 * and GUID_equal() calls in
6111 * replmd_replicated_apply_merge() avoids the
6114 ar
->objs
->objects
[ar
->index_current
].parent_guid
=
6115 &ar
->local_parent_guid
;
6117 msg
->dn
= ar
->search_msg
->dn
;
6118 ret
= replmd_replicated_apply_merge(ar
);
6120 if (ret
!= LDB_SUCCESS
) {
6121 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
6131 * Stores the linked attributes received in the replication chunk - these get
6132 * applied at the end of the transaction. We also check that each linked
6133 * attribute is valid, i.e. source and target objects are known.
6135 static int replmd_store_linked_attributes(struct replmd_replicated_request
*ar
)
6137 int ret
= LDB_SUCCESS
;
6139 struct ldb_module
*module
= ar
->module
;
6140 struct replmd_private
*replmd_private
=
6141 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
6142 struct ldb_context
*ldb
;
6144 ldb
= ldb_module_get_ctx(module
);
6146 DEBUG(4,("linked_attributes_count=%u\n", ar
->objs
->linked_attributes_count
));
6148 /* save away the linked attributes for the end of the transaction */
6149 for (i
= 0; i
< ar
->objs
->linked_attributes_count
; i
++) {
6150 struct la_entry
*la_entry
;
6152 if (replmd_private
->la_ctx
== NULL
) {
6153 replmd_private
->la_ctx
= talloc_new(replmd_private
);
6155 la_entry
= talloc(replmd_private
->la_ctx
, struct la_entry
);
6156 if (la_entry
== NULL
) {
6158 return LDB_ERR_OPERATIONS_ERROR
;
6160 la_entry
->la
= talloc(la_entry
, struct drsuapi_DsReplicaLinkedAttribute
);
6161 if (la_entry
->la
== NULL
) {
6162 talloc_free(la_entry
);
6164 return LDB_ERR_OPERATIONS_ERROR
;
6166 *la_entry
->la
= ar
->objs
->linked_attributes
[i
];
6167 la_entry
->dsdb_repl_flags
= ar
->objs
->dsdb_repl_flags
;
6169 /* we need to steal the non-scalars so they stay
6170 around until the end of the transaction */
6171 talloc_steal(la_entry
->la
, la_entry
->la
->identifier
);
6172 talloc_steal(la_entry
->la
, la_entry
->la
->value
.blob
);
6174 ret
= replmd_verify_linked_attribute(ar
, la_entry
);
6176 if (ret
!= LDB_SUCCESS
) {
6180 DLIST_ADD(replmd_private
->la_list
, la_entry
);
6186 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request
*ar
);
6188 static int replmd_replicated_apply_next(struct replmd_replicated_request
*ar
)
6190 struct ldb_context
*ldb
;
6194 struct ldb_request
*search_req
;
6195 static const char *attrs
[] = { "repsFrom", "replUpToDateVector",
6196 "parentGUID", "instanceType",
6197 "replPropertyMetaData", "nTSecurityDescriptor",
6198 "isDeleted", NULL
};
6199 struct GUID_txt_buf guid_str_buf
;
6201 if (ar
->index_current
>= ar
->objs
->num_objects
) {
6204 * Now that we've applied all the objects, check the new linked
6205 * attributes and store them (we apply them in .prepare_commit)
6207 ret
= replmd_store_linked_attributes(ar
);
6209 if (ret
!= LDB_SUCCESS
) {
6213 /* done applying objects, move on to the next stage */
6214 return replmd_replicated_uptodate_vector(ar
);
6217 ldb
= ldb_module_get_ctx(ar
->module
);
6218 ar
->search_msg
= NULL
;
6219 ar
->isDeleted
= false;
6221 tmp_str
= GUID_buf_string(&ar
->objs
->objects
[ar
->index_current
].object_guid
,
6224 filter
= talloc_asprintf(ar
, "(objectGUID=%s)", tmp_str
);
6225 if (!filter
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
6227 ret
= ldb_build_search_req(&search_req
,
6230 ar
->objs
->partition_dn
,
6236 replmd_replicated_apply_search_callback
,
6238 LDB_REQ_SET_LOCATION(search_req
);
6240 ret
= dsdb_request_add_controls(search_req
, DSDB_SEARCH_SHOW_RECYCLED
);
6242 if (ret
!= LDB_SUCCESS
) {
6246 return ldb_next_request(ar
->module
, search_req
);
6250 * This is essentially a wrapper for replmd_replicated_apply_next()
6252 * This is needed to ensure that both codepaths call this handler.
6254 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request
*ar
)
6256 struct ldb_dn
*deleted_objects_dn
;
6257 struct ldb_message
*msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
6258 int ret
= dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar
->module
), msg
, msg
->dn
,
6259 &deleted_objects_dn
);
6260 if (ar
->isDeleted
&& (ret
!= LDB_SUCCESS
|| ldb_dn_compare(msg
->dn
, deleted_objects_dn
) != 0)) {
6262 * Do a delete here again, so that if there is
6263 * anything local that conflicts with this
6264 * object being deleted, it is removed. This
6265 * includes links. See MS-DRSR 4.1.10.6.9
6268 * If the object is already deleted, and there
6269 * is no more work required, it doesn't do
6273 /* This has been updated to point to the DN we eventually did the modify on */
6275 struct ldb_request
*del_req
;
6276 struct ldb_result
*res
;
6278 TALLOC_CTX
*tmp_ctx
= talloc_new(ar
);
6280 ret
= ldb_oom(ldb_module_get_ctx(ar
->module
));
6284 res
= talloc_zero(tmp_ctx
, struct ldb_result
);
6286 ret
= ldb_oom(ldb_module_get_ctx(ar
->module
));
6287 talloc_free(tmp_ctx
);
6291 /* Build a delete request, which hopefully will artually turn into nothing */
6292 ret
= ldb_build_del_req(&del_req
, ldb_module_get_ctx(ar
->module
), tmp_ctx
,
6296 ldb_modify_default_callback
,
6298 LDB_REQ_SET_LOCATION(del_req
);
6299 if (ret
!= LDB_SUCCESS
) {
6300 talloc_free(tmp_ctx
);
6305 * This is the guts of the call, call back
6306 * into our delete code, but setting the
6307 * re_delete flag so we delete anything that
6308 * shouldn't be there on a deleted or recycled
6311 ret
= replmd_delete_internals(ar
->module
, del_req
, true);
6312 if (ret
== LDB_SUCCESS
) {
6313 ret
= ldb_wait(del_req
->handle
, LDB_WAIT_ALL
);
6316 talloc_free(tmp_ctx
);
6317 if (ret
!= LDB_SUCCESS
) {
6322 ar
->index_current
++;
6323 return replmd_replicated_apply_next(ar
);
6326 static int replmd_replicated_uptodate_modify_callback(struct ldb_request
*req
,
6327 struct ldb_reply
*ares
)
6329 struct ldb_context
*ldb
;
6330 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
6331 struct replmd_replicated_request
);
6332 ldb
= ldb_module_get_ctx(ar
->module
);
6335 return ldb_module_done(ar
->req
, NULL
, NULL
,
6336 LDB_ERR_OPERATIONS_ERROR
);
6338 if (ares
->error
!= LDB_SUCCESS
) {
6339 return ldb_module_done(ar
->req
, ares
->controls
,
6340 ares
->response
, ares
->error
);
6343 if (ares
->type
!= LDB_REPLY_DONE
) {
6344 ldb_asprintf_errstring(ldb
, "Invalid LDB reply type %d", ares
->type
);
6345 return ldb_module_done(ar
->req
, NULL
, NULL
,
6346 LDB_ERR_OPERATIONS_ERROR
);
6351 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_SUCCESS
);
6354 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request
*ar
)
6356 struct ldb_context
*ldb
;
6357 struct ldb_request
*change_req
;
6358 enum ndr_err_code ndr_err
;
6359 struct ldb_message
*msg
;
6360 struct replUpToDateVectorBlob ouv
;
6361 const struct ldb_val
*ouv_value
;
6362 const struct drsuapi_DsReplicaCursor2CtrEx
*ruv
;
6363 struct replUpToDateVectorBlob nuv
;
6364 struct ldb_val nuv_value
;
6365 struct ldb_message_element
*nuv_el
= NULL
;
6366 struct ldb_message_element
*orf_el
= NULL
;
6367 struct repsFromToBlob nrf
;
6368 struct ldb_val
*nrf_value
= NULL
;
6369 struct ldb_message_element
*nrf_el
= NULL
;
6373 time_t t
= time(NULL
);
6376 uint32_t instanceType
;
6378 ldb
= ldb_module_get_ctx(ar
->module
);
6379 ruv
= ar
->objs
->uptodateness_vector
;
6385 unix_to_nt_time(&now
, t
);
6387 if (ar
->search_msg
== NULL
) {
6388 /* this happens for a REPL_OBJ call where we are
6389 creating the target object by replicating it. The
6390 subdomain join code does this for the partition DN
6392 DEBUG(4,(__location__
": Skipping UDV and repsFrom update as no target DN\n"));
6393 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_SUCCESS
);
6396 instanceType
= ldb_msg_find_attr_as_uint(ar
->search_msg
, "instanceType", 0);
6397 if (! (instanceType
& INSTANCE_TYPE_IS_NC_HEAD
)) {
6398 DEBUG(4,(__location__
": Skipping UDV and repsFrom update as not NC root: %s\n",
6399 ldb_dn_get_linearized(ar
->search_msg
->dn
)));
6400 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_SUCCESS
);
6404 * first create the new replUpToDateVector
6406 ouv_value
= ldb_msg_find_ldb_val(ar
->search_msg
, "replUpToDateVector");
6408 ndr_err
= ndr_pull_struct_blob(ouv_value
, ar
, &ouv
,
6409 (ndr_pull_flags_fn_t
)ndr_pull_replUpToDateVectorBlob
);
6410 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
6411 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
6412 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
6415 if (ouv
.version
!= 2) {
6416 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
6421 * the new uptodateness vector will at least
6422 * contain 1 entry, one for the source_dsa
6424 * plus optional values from our old vector and the one from the source_dsa
6426 nuv
.ctr
.ctr2
.count
= ouv
.ctr
.ctr2
.count
;
6427 if (ruv
) nuv
.ctr
.ctr2
.count
+= ruv
->count
;
6428 nuv
.ctr
.ctr2
.cursors
= talloc_array(ar
,
6429 struct drsuapi_DsReplicaCursor2
,
6430 nuv
.ctr
.ctr2
.count
);
6431 if (!nuv
.ctr
.ctr2
.cursors
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
6433 /* first copy the old vector */
6434 for (i
=0; i
< ouv
.ctr
.ctr2
.count
; i
++) {
6435 nuv
.ctr
.ctr2
.cursors
[ni
] = ouv
.ctr
.ctr2
.cursors
[i
];
6439 /* merge in the source_dsa vector is available */
6440 for (i
=0; (ruv
&& i
< ruv
->count
); i
++) {
6443 if (GUID_equal(&ruv
->cursors
[i
].source_dsa_invocation_id
,
6444 &ar
->our_invocation_id
)) {
6448 for (j
=0; j
< ni
; j
++) {
6449 if (!GUID_equal(&ruv
->cursors
[i
].source_dsa_invocation_id
,
6450 &nuv
.ctr
.ctr2
.cursors
[j
].source_dsa_invocation_id
)) {
6456 if (ruv
->cursors
[i
].highest_usn
> nuv
.ctr
.ctr2
.cursors
[j
].highest_usn
) {
6457 nuv
.ctr
.ctr2
.cursors
[j
] = ruv
->cursors
[i
];
6462 if (found
) continue;
6464 /* if it's not there yet, add it */
6465 nuv
.ctr
.ctr2
.cursors
[ni
] = ruv
->cursors
[i
];
6470 * finally correct the size of the cursors array
6472 nuv
.ctr
.ctr2
.count
= ni
;
6477 TYPESAFE_QSORT(nuv
.ctr
.ctr2
.cursors
, nuv
.ctr
.ctr2
.count
, drsuapi_DsReplicaCursor2_compare
);
6480 * create the change ldb_message
6482 msg
= ldb_msg_new(ar
);
6483 if (!msg
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
6484 msg
->dn
= ar
->search_msg
->dn
;
6486 ndr_err
= ndr_push_struct_blob(&nuv_value
, msg
, &nuv
,
6487 (ndr_push_flags_fn_t
)ndr_push_replUpToDateVectorBlob
);
6488 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
6489 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
6490 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
6492 ret
= ldb_msg_add_value(msg
, "replUpToDateVector", &nuv_value
, &nuv_el
);
6493 if (ret
!= LDB_SUCCESS
) {
6494 return replmd_replicated_request_error(ar
, ret
);
6496 nuv_el
->flags
= LDB_FLAG_MOD_REPLACE
;
6499 * now create the new repsFrom value from the given repsFromTo1 structure
6503 nrf
.ctr
.ctr1
= *ar
->objs
->source_dsa
;
6504 nrf
.ctr
.ctr1
.last_attempt
= now
;
6505 nrf
.ctr
.ctr1
.last_success
= now
;
6506 nrf
.ctr
.ctr1
.result_last_attempt
= WERR_OK
;
6509 * first see if we already have a repsFrom value for the current source dsa
6510 * if so we'll later replace this value
6512 orf_el
= ldb_msg_find_element(ar
->search_msg
, "repsFrom");
6514 for (i
=0; i
< orf_el
->num_values
; i
++) {
6515 struct repsFromToBlob
*trf
;
6517 trf
= talloc(ar
, struct repsFromToBlob
);
6518 if (!trf
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
6520 ndr_err
= ndr_pull_struct_blob(&orf_el
->values
[i
], trf
, trf
,
6521 (ndr_pull_flags_fn_t
)ndr_pull_repsFromToBlob
);
6522 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
6523 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
6524 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
6527 if (trf
->version
!= 1) {
6528 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
6532 * we compare the source dsa objectGUID not the invocation_id
6533 * because we want only one repsFrom value per source dsa
6534 * and when the invocation_id of the source dsa has changed we don't need
6535 * the old repsFrom with the old invocation_id
6537 if (!GUID_equal(&trf
->ctr
.ctr1
.source_dsa_obj_guid
,
6538 &ar
->objs
->source_dsa
->source_dsa_obj_guid
)) {
6544 nrf_value
= &orf_el
->values
[i
];
6549 * copy over all old values to the new ldb_message
6551 ret
= ldb_msg_add_empty(msg
, "repsFrom", 0, &nrf_el
);
6552 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
6557 * if we haven't found an old repsFrom value for the current source dsa
6558 * we'll add a new value
6561 struct ldb_val zero_value
;
6562 ZERO_STRUCT(zero_value
);
6563 ret
= ldb_msg_add_value(msg
, "repsFrom", &zero_value
, &nrf_el
);
6564 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
6566 nrf_value
= &nrf_el
->values
[nrf_el
->num_values
- 1];
6569 /* we now fill the value which is already attached to ldb_message */
6570 ndr_err
= ndr_push_struct_blob(nrf_value
, msg
,
6572 (ndr_push_flags_fn_t
)ndr_push_repsFromToBlob
);
6573 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
6574 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
6575 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
6579 * the ldb_message_element for the attribute, has all the old values and the new one
6580 * so we'll replace the whole attribute with all values
6582 nrf_el
->flags
= LDB_FLAG_MOD_REPLACE
;
6584 if (CHECK_DEBUGLVL(4)) {
6585 char *s
= ldb_ldif_message_redacted_string(ldb
, ar
,
6586 LDB_CHANGETYPE_MODIFY
,
6588 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s
));
6592 /* prepare the ldb_modify() request */
6593 ret
= ldb_build_mod_req(&change_req
,
6599 replmd_replicated_uptodate_modify_callback
,
6601 LDB_REQ_SET_LOCATION(change_req
);
6602 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
6604 return ldb_next_request(ar
->module
, change_req
);
6607 static int replmd_replicated_uptodate_search_callback(struct ldb_request
*req
,
6608 struct ldb_reply
*ares
)
6610 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
6611 struct replmd_replicated_request
);
6615 return ldb_module_done(ar
->req
, NULL
, NULL
,
6616 LDB_ERR_OPERATIONS_ERROR
);
6618 if (ares
->error
!= LDB_SUCCESS
&&
6619 ares
->error
!= LDB_ERR_NO_SUCH_OBJECT
) {
6620 return ldb_module_done(ar
->req
, ares
->controls
,
6621 ares
->response
, ares
->error
);
6624 switch (ares
->type
) {
6625 case LDB_REPLY_ENTRY
:
6626 ar
->search_msg
= talloc_steal(ar
, ares
->message
);
6629 case LDB_REPLY_REFERRAL
:
6630 /* we ignore referrals */
6633 case LDB_REPLY_DONE
:
6634 ret
= replmd_replicated_uptodate_modify(ar
);
6635 if (ret
!= LDB_SUCCESS
) {
6636 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
6645 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request
*ar
)
6647 struct ldb_context
*ldb
= ldb_module_get_ctx(ar
->module
);
6648 struct replmd_private
*replmd_private
=
6649 talloc_get_type_abort(ldb_module_get_private(ar
->module
),
6650 struct replmd_private
);
6652 static const char *attrs
[] = {
6653 "replUpToDateVector",
6658 struct ldb_request
*search_req
;
6660 ar
->search_msg
= NULL
;
6663 * Let the caller know that we did an originating updates
6665 ar
->objs
->originating_updates
= replmd_private
->originating_updates
;
6667 ret
= ldb_build_search_req(&search_req
,
6670 ar
->objs
->partition_dn
,
6676 replmd_replicated_uptodate_search_callback
,
6678 LDB_REQ_SET_LOCATION(search_req
);
6679 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
6681 return ldb_next_request(ar
->module
, search_req
);
6686 static int replmd_extended_replicated_objects(struct ldb_module
*module
, struct ldb_request
*req
)
6688 struct ldb_context
*ldb
;
6689 struct dsdb_extended_replicated_objects
*objs
;
6690 struct replmd_replicated_request
*ar
;
6691 struct ldb_control
**ctrls
;
6694 ldb
= ldb_module_get_ctx(module
);
6696 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_extended_replicated_objects\n");
6698 objs
= talloc_get_type(req
->op
.extended
.data
, struct dsdb_extended_replicated_objects
);
6700 ldb_debug(ldb
, LDB_DEBUG_FATAL
, "replmd_extended_replicated_objects: invalid extended data\n");
6701 return LDB_ERR_PROTOCOL_ERROR
;
6704 if (objs
->version
!= DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION
) {
6705 ldb_debug(ldb
, LDB_DEBUG_FATAL
, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
6706 objs
->version
, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION
);
6707 return LDB_ERR_PROTOCOL_ERROR
;
6710 ar
= replmd_ctx_init(module
, req
);
6712 return LDB_ERR_OPERATIONS_ERROR
;
6714 /* Set the flags to have the replmd_op_callback run over the full set of objects */
6715 ar
->apply_mode
= true;
6717 ar
->schema
= dsdb_get_schema(ldb
, ar
);
6719 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
, "replmd_ctx_init: no loaded schema found\n");
6721 DEBUG(0,(__location__
": %s\n", ldb_errstring(ldb
)));
6722 return LDB_ERR_CONSTRAINT_VIOLATION
;
6725 ctrls
= req
->controls
;
6727 if (req
->controls
) {
6728 req
->controls
= talloc_memdup(ar
, req
->controls
,
6729 talloc_get_size(req
->controls
));
6730 if (!req
->controls
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
6733 ret
= ldb_request_add_control(req
, DSDB_CONTROL_REPLICATED_UPDATE_OID
, false, NULL
);
6734 if (ret
!= LDB_SUCCESS
) {
6738 /* If this change contained linked attributes in the body
6739 * (rather than in the links section) we need to update
6740 * backlinks in linked_attributes */
6741 ret
= ldb_request_add_control(req
, DSDB_CONTROL_APPLY_LINKS
, false, NULL
);
6742 if (ret
!= LDB_SUCCESS
) {
6746 ar
->controls
= req
->controls
;
6747 req
->controls
= ctrls
;
6749 return replmd_replicated_apply_next(ar
);
6753 * Checks how to handle an missing target - either we need to fail the
6754 * replication and retry with GET_TGT, ignore the link and continue, or try to
6755 * add a partial link to an unknown target.
6757 static int replmd_allow_missing_target(struct ldb_module
*module
,
6758 TALLOC_CTX
*mem_ctx
,
6759 struct ldb_dn
*target_dn
,
6760 struct ldb_dn
*source_dn
,
6763 uint32_t dsdb_repl_flags
,
6765 const char * missing_str
)
6767 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
6771 * we may not be able to resolve link targets properly when
6772 * dealing with subsets of objects, e.g. the source is a
6773 * critical object and the target isn't
6776 * When we implement Trusted Domains we need to consider
6777 * whether they get treated as an incomplete replica here or not
6779 if (dsdb_repl_flags
& DSDB_REPL_FLAG_OBJECT_SUBSET
) {
6782 * Ignore the link. We don't increase the highwater-mark in
6783 * the object subset cases, so subsequent replications should
6784 * resolve any missing links
6786 DEBUG(2, ("%s target %s linked from %s\n", missing_str
,
6787 ldb_dn_get_linearized(target_dn
),
6788 ldb_dn_get_linearized(source_dn
)));
6789 *ignore_link
= true;
6793 if (dsdb_repl_flags
& DSDB_REPL_FLAG_TARGETS_UPTODATE
) {
6796 * target should already be up-to-date so there's no point in
6797 * retrying. This could be due to bad timing, or if a target
6798 * on a one-way link was deleted. We ignore the link rather
6799 * than failing the replication cycle completely
6801 *ignore_link
= true;
6802 DBG_WARNING("%s is %s but up to date. Ignoring link from %s\n",
6803 ldb_dn_get_linearized(target_dn
), missing_str
,
6804 ldb_dn_get_linearized(source_dn
));
6808 is_in_same_nc
= dsdb_objects_have_same_nc(ldb
,
6812 if (is_in_same_nc
) {
6813 /* fail the replication and retry with GET_TGT */
6814 ldb_asprintf_errstring(ldb
, "%s target %s GUID %s linked from %s\n",
6816 ldb_dn_get_linearized(target_dn
),
6817 GUID_string(mem_ctx
, guid
),
6818 ldb_dn_get_linearized(source_dn
));
6819 return LDB_ERR_NO_SUCH_OBJECT
;
6823 * The target of the cross-partition link is missing. Continue
6824 * and try to at least add the forward-link. This isn't great,
6825 * but a partial link can be fixed by dbcheck, so it's better
6826 * than dropping the link completely.
6828 *ignore_link
= false;
6830 if (is_obj_commit
) {
6833 * Only log this when we're actually committing the objects.
6834 * This avoids spurious logs, i.e. if we're just verifying the
6835 * received link during a join.
6837 DBG_WARNING("%s cross-partition target %s linked from %s\n",
6838 missing_str
, ldb_dn_get_linearized(target_dn
),
6839 ldb_dn_get_linearized(source_dn
));
6846 * Checks that the target object for a linked attribute exists.
6847 * @param guid returns the target object's GUID (is returned)if it exists)
6848 * @param ignore_link set to true if the linked attribute should be ignored
6849 * (i.e. the target doesn't exist, but that it's OK to skip the link)
6851 static int replmd_check_target_exists(struct ldb_module
*module
,
6852 struct dsdb_dn
*dsdb_dn
,
6853 struct la_entry
*la_entry
,
6854 struct ldb_dn
*source_dn
,
6859 struct drsuapi_DsReplicaLinkedAttribute
*la
= la_entry
->la
;
6860 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
6861 struct ldb_result
*target_res
;
6862 TALLOC_CTX
*tmp_ctx
= talloc_new(la_entry
);
6863 const char *attrs
[] = { "isDeleted", "isRecycled", NULL
};
6866 enum deletion_state target_deletion_state
= OBJECT_REMOVED
;
6867 bool active
= (la
->flags
& DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
) ? true : false;
6869 *ignore_link
= false;
6870 ntstatus
= dsdb_get_extended_dn_guid(dsdb_dn
->dn
, guid
, "GUID");
6872 if (!NT_STATUS_IS_OK(ntstatus
) && !active
) {
6875 * This strange behaviour (allowing a NULL/missing
6876 * GUID) originally comes from:
6878 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
6879 * Author: Andrew Tridgell <tridge@samba.org>
6880 * Date: Mon Dec 21 21:21:55 2009 +1100
6882 * s4-drs: cope better with NULL GUIDS from DRS
6884 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
6885 * need to match by DN if possible when seeing if we should update an
6888 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
6890 ret
= dsdb_module_search_dn(module
, tmp_ctx
, &target_res
,
6892 DSDB_FLAG_NEXT_MODULE
|
6893 DSDB_SEARCH_SHOW_RECYCLED
|
6894 DSDB_SEARCH_SEARCH_ALL_PARTITIONS
|
6895 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
,
6897 } else if (!NT_STATUS_IS_OK(ntstatus
)) {
6898 ldb_asprintf_errstring(ldb
, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
6900 ldb_dn_get_linearized(dsdb_dn
->dn
),
6901 ldb_dn_get_linearized(source_dn
));
6902 talloc_free(tmp_ctx
);
6903 return LDB_ERR_OPERATIONS_ERROR
;
6905 ret
= dsdb_module_search(module
, tmp_ctx
, &target_res
,
6906 NULL
, LDB_SCOPE_SUBTREE
,
6908 DSDB_FLAG_NEXT_MODULE
|
6909 DSDB_SEARCH_SHOW_RECYCLED
|
6910 DSDB_SEARCH_SEARCH_ALL_PARTITIONS
|
6911 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
,
6914 GUID_string(tmp_ctx
, guid
));
6917 if (ret
!= LDB_SUCCESS
) {
6918 ldb_asprintf_errstring(ldb
, "Failed to re-resolve GUID %s: %s\n",
6919 GUID_string(tmp_ctx
, guid
),
6920 ldb_errstring(ldb
));
6921 talloc_free(tmp_ctx
);
6925 if (target_res
->count
== 0) {
6928 * target object is unknown. Check whether to ignore the link,
6929 * fail the replication, or add a partial link
6931 ret
= replmd_allow_missing_target(module
, tmp_ctx
, dsdb_dn
->dn
,
6932 source_dn
, is_obj_commit
, guid
,
6933 la_entry
->dsdb_repl_flags
,
6934 ignore_link
, "Unknown");
6936 } else if (target_res
->count
!= 1) {
6937 ldb_asprintf_errstring(ldb
, "More than one object found matching objectGUID %s\n",
6938 GUID_string(tmp_ctx
, guid
));
6939 ret
= LDB_ERR_OPERATIONS_ERROR
;
6941 struct ldb_message
*target_msg
= target_res
->msgs
[0];
6943 dsdb_dn
->dn
= talloc_steal(dsdb_dn
, target_msg
->dn
);
6945 /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
6946 replmd_deletion_state(module
, target_msg
,
6947 &target_deletion_state
, NULL
);
6950 * Check for deleted objects as per MS-DRSR 4.1.10.6.14
6951 * ProcessLinkValue(). Link updates should not be sent for
6952 * recycled and tombstone objects (deleting the links should
6953 * happen when we delete the object). This probably means our
6954 * copy of the target object isn't up to date.
6956 if (target_deletion_state
>= OBJECT_RECYCLED
) {
6959 * target object is deleted. Check whether to ignore the
6960 * link, fail the replication, or add a partial link
6962 ret
= replmd_allow_missing_target(module
, tmp_ctx
,
6963 dsdb_dn
->dn
, source_dn
,
6964 is_obj_commit
, guid
,
6965 la_entry
->dsdb_repl_flags
,
6966 ignore_link
, "Deleted");
6970 talloc_free(tmp_ctx
);
6975 * Extracts the key details about the source/target object for a
6976 * linked-attribute entry.
6977 * This returns the following details:
6978 * @param ret_attr the schema details for the linked attribute
6979 * @param source_msg the search result for the source object
6980 * @param target_dsdb_dn the unpacked DN info for the target object
6982 static int replmd_extract_la_entry_details(struct ldb_module
*module
,
6983 struct la_entry
*la_entry
,
6984 TALLOC_CTX
*mem_ctx
,
6985 const struct dsdb_attribute
**ret_attr
,
6986 struct ldb_message
**source_msg
,
6987 struct dsdb_dn
**target_dsdb_dn
)
6989 struct drsuapi_DsReplicaLinkedAttribute
*la
= la_entry
->la
;
6990 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
6991 const struct dsdb_schema
*schema
= dsdb_get_schema(ldb
, mem_ctx
);
6993 const struct dsdb_attribute
*attr
;
6995 struct ldb_result
*res
;
6996 const char *attrs
[4];
6999 linked_attributes[0]:
7000 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
7002 identifier: struct drsuapi_DsReplicaObjectIdentifier
7003 __ndr_size : 0x0000003a (58)
7004 __ndr_size_sid : 0x00000000 (0)
7005 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7007 __ndr_size_dn : 0x00000000 (0)
7009 attid : DRSUAPI_ATTID_member (0x1F)
7010 value: struct drsuapi_DsAttributeValue
7011 __ndr_size : 0x0000007e (126)
7013 blob : DATA_BLOB length=126
7014 flags : 0x00000001 (1)
7015 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7016 originating_add_time : Wed Sep 2 22:20:01 2009 EST
7017 meta_data: struct drsuapi_DsReplicaMetaData
7018 version : 0x00000015 (21)
7019 originating_change_time : Wed Sep 2 23:39:07 2009 EST
7020 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7021 originating_usn : 0x000000000001e19c (123292)
7023 (for cases where the link is to a normal DN)
7024 &target: struct drsuapi_DsReplicaObjectIdentifier3
7025 __ndr_size : 0x0000007e (126)
7026 __ndr_size_sid : 0x0000001c (28)
7027 guid : 7639e594-db75-4086-b0d4-67890ae46031
7028 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7029 __ndr_size_dn : 0x00000022 (34)
7030 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7033 /* find the attribute being modified */
7034 attr
= dsdb_attribute_by_attributeID_id(schema
, la
->attid
);
7036 struct GUID_txt_buf guid_str
;
7037 ldb_asprintf_errstring(ldb
, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7039 GUID_buf_string(&la
->identifier
->guid
,
7041 return LDB_ERR_OPERATIONS_ERROR
;
7044 attrs
[0] = attr
->lDAPDisplayName
;
7045 attrs
[1] = "isDeleted";
7046 attrs
[2] = "isRecycled";
7050 * get the existing message from the db for the object with
7051 * this GUID, returning attribute being modified. We will then
7052 * use this msg as the basis for a modify call
7054 ret
= dsdb_module_search(module
, mem_ctx
, &res
, NULL
, LDB_SCOPE_SUBTREE
, attrs
,
7055 DSDB_FLAG_NEXT_MODULE
|
7056 DSDB_SEARCH_SEARCH_ALL_PARTITIONS
|
7057 DSDB_SEARCH_SHOW_RECYCLED
|
7058 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
|
7059 DSDB_SEARCH_REVEAL_INTERNALS
,
7061 "objectGUID=%s", GUID_string(mem_ctx
, &la
->identifier
->guid
));
7062 if (ret
!= LDB_SUCCESS
) {
7065 if (res
->count
!= 1) {
7066 ldb_asprintf_errstring(ldb
, "DRS linked attribute for GUID %s - DN not found",
7067 GUID_string(mem_ctx
, &la
->identifier
->guid
));
7068 return LDB_ERR_NO_SUCH_OBJECT
;
7071 *source_msg
= res
->msgs
[0];
7073 /* the value blob for the attribute holds the target object DN */
7074 status
= dsdb_dn_la_from_blob(ldb
, attr
, schema
, mem_ctx
, la
->value
.blob
, target_dsdb_dn
);
7075 if (!W_ERROR_IS_OK(status
)) {
7076 ldb_asprintf_errstring(ldb
, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7077 attr
->lDAPDisplayName
,
7078 ldb_dn_get_linearized(res
->msgs
[0]->dn
),
7079 win_errstr(status
));
7080 return LDB_ERR_OPERATIONS_ERROR
;
7089 * Verifies the source and target objects are known for a linked attribute
7091 static int replmd_verify_linked_attribute(struct replmd_replicated_request
*ar
,
7092 struct la_entry
*la
)
7094 int ret
= LDB_SUCCESS
;
7095 TALLOC_CTX
*tmp_ctx
= talloc_new(la
);
7096 struct ldb_module
*module
= ar
->module
;
7097 struct ldb_message
*src_msg
;
7098 const struct dsdb_attribute
*attr
;
7099 struct dsdb_dn
*tgt_dsdb_dn
;
7100 struct GUID guid
= GUID_zero();
7103 ret
= replmd_extract_la_entry_details(module
, la
, tmp_ctx
, &attr
,
7104 &src_msg
, &tgt_dsdb_dn
);
7107 * When we fail to find the source object, the error code we pass
7108 * back here is really important. It flags back to the callers to
7109 * retry this request with DRSUAPI_DRS_GET_ANC. This case should
7110 * never happen if we're replicating from a Samba DC, but it is
7111 * needed to talk to a Windows DC
7113 if (ret
== LDB_ERR_NO_SUCH_OBJECT
) {
7114 ret
= replmd_replicated_request_werror(ar
, WERR_DS_DRA_MISSING_PARENT
);
7117 if (ret
!= LDB_SUCCESS
) {
7118 talloc_free(tmp_ctx
);
7123 * We can skip the target object checks if we're only syncing critical
7124 * objects, or we know the target is up-to-date. If either case, we
7125 * still continue even if the target doesn't exist
7127 if ((la
->dsdb_repl_flags
& (DSDB_REPL_FLAG_OBJECT_SUBSET
|
7128 DSDB_REPL_FLAG_TARGETS_UPTODATE
)) == 0) {
7130 ret
= replmd_check_target_exists(module
, tgt_dsdb_dn
, la
,
7131 src_msg
->dn
, false, &guid
,
7136 * When we fail to find the target object, the error code we pass
7137 * back here is really important. It flags back to the callers to
7138 * retry this request with DRSUAPI_DRS_GET_TGT
7140 if (ret
== LDB_ERR_NO_SUCH_OBJECT
) {
7141 ret
= replmd_replicated_request_werror(ar
, WERR_DS_DRA_RECYCLED_TARGET
);
7144 talloc_free(tmp_ctx
);
7149 * Finds the current active Parsed-DN value for a single-valued linked
7150 * attribute, if one exists.
7151 * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
7152 * @returns LDB_SUCCESS (regardless of whether a match was found), unless
7155 static int replmd_get_active_singleval_link(struct ldb_module
*module
,
7156 TALLOC_CTX
*mem_ctx
,
7157 struct parsed_dn pdn_list
[],
7159 const struct dsdb_attribute
*attr
,
7160 struct parsed_dn
**ret_pdn
)
7166 if (!(attr
->ldb_schema_attribute
->flags
& LDB_ATTR_FLAG_SINGLE_VALUE
)) {
7168 /* nothing to do for multi-valued linked attributes */
7172 for (i
= 0; i
< count
; i
++) {
7173 int ret
= LDB_SUCCESS
;
7174 struct parsed_dn
*pdn
= &pdn_list
[i
];
7176 /* skip any inactive links */
7177 if (dsdb_dn_is_deleted_val(pdn
->v
)) {
7181 /* we've found an active value for this attribute */
7184 if (pdn
->dsdb_dn
== NULL
) {
7185 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
7187 ret
= really_parse_trusted_dn(mem_ctx
, ldb
, pdn
,
7188 attr
->syntax
->ldap_oid
);
7194 /* no active link found */
7199 * @returns true if the replication linked attribute info is newer than we
7200 * already have in our DB
7201 * @param pdn the existing linked attribute info in our DB
7202 * @param la the new linked attribute info received during replication
7204 static bool replmd_link_update_is_newer(struct parsed_dn
*pdn
,
7205 struct drsuapi_DsReplicaLinkedAttribute
*la
)
7207 /* see if this update is newer than what we have already */
7208 struct GUID invocation_id
= GUID_zero();
7209 uint32_t version
= 0;
7210 NTTIME change_time
= 0;
7214 /* no existing info so update is newer */
7218 dsdb_get_extended_dn_guid(pdn
->dsdb_dn
->dn
, &invocation_id
, "RMD_INVOCID");
7219 dsdb_get_extended_dn_uint32(pdn
->dsdb_dn
->dn
, &version
, "RMD_VERSION");
7220 dsdb_get_extended_dn_nttime(pdn
->dsdb_dn
->dn
, &change_time
, "RMD_CHANGETIME");
7222 return replmd_update_is_newer(&invocation_id
,
7223 &la
->meta_data
.originating_invocation_id
,
7225 la
->meta_data
.version
,
7227 la
->meta_data
.originating_change_time
);
7231 * Marks an existing linked attribute value as deleted in the DB
7232 * @param pdn the parsed-DN of the target-value to delete
7234 static int replmd_delete_link_value(struct ldb_module
*module
,
7235 struct replmd_private
*replmd_private
,
7236 TALLOC_CTX
*mem_ctx
,
7237 struct ldb_dn
*src_obj_dn
,
7238 const struct dsdb_schema
*schema
,
7239 const struct dsdb_attribute
*attr
,
7242 struct GUID
*target_guid
,
7243 struct dsdb_dn
*target_dsdb_dn
,
7244 struct ldb_val
*output_val
)
7246 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
7249 const struct GUID
*invocation_id
= NULL
;
7253 unix_to_nt_time(&now
, t
);
7255 invocation_id
= samdb_ntds_invocation_id(ldb
);
7256 if (invocation_id
== NULL
) {
7257 return LDB_ERR_OPERATIONS_ERROR
;
7260 /* if the existing link is active, remove its backlink */
7263 ret
= replmd_add_backlink(module
, replmd_private
, schema
,
7264 src_obj_dn
, target_guid
, false,
7266 if (ret
!= LDB_SUCCESS
) {
7271 /* mark the existing value as deleted */
7272 ret
= replmd_update_la_val(mem_ctx
, output_val
, target_dsdb_dn
,
7273 target_dsdb_dn
, invocation_id
, seq_num
,
7274 seq_num
, now
, true);
7279 * Checks for a conflict in single-valued link attributes, and tries to
7280 * resolve the problem if possible.
7282 * Single-valued links should only ever have one active value. If we already
7283 * have an active link value, and during replication we receive an active link
7284 * value for a different target DN, then we need to resolve this inconsistency
7285 * and determine which value should be active. If the received info is better/
7286 * newer than the existing link attribute, then we need to set our existing
7287 * link as deleted. If the received info is worse/older, then we should continue
7288 * to add it, but set it as an inactive link.
7290 * Note that this is a corner-case that is unlikely to happen (but if it does
7291 * happen, we don't want it to break replication completely).
7293 * @param pdn_being_modified the parsed DN corresponding to the received link
7294 * target (note this is NULL if the link does not already exist in our DB)
7295 * @param pdn_list all the source object's Parsed-DNs for this attribute, i.e.
7296 * any existing active or inactive values for the attribute in our DB.
7297 * @param dsdb_dn the target DN for the received link attribute
7298 * @param add_as_inactive gets set to true if the received link is worse than
7299 * the existing link - it should still be added, but as an inactive link.
7301 static int replmd_check_singleval_la_conflict(struct ldb_module
*module
,
7302 struct replmd_private
*replmd_private
,
7303 TALLOC_CTX
*mem_ctx
,
7304 struct ldb_dn
*src_obj_dn
,
7305 struct drsuapi_DsReplicaLinkedAttribute
*la
,
7306 struct dsdb_dn
*dsdb_dn
,
7307 struct parsed_dn
*pdn_being_modified
,
7308 struct parsed_dn
*pdn_list
,
7309 struct ldb_message_element
*old_el
,
7310 const struct dsdb_schema
*schema
,
7311 const struct dsdb_attribute
*attr
,
7313 bool *add_as_inactive
)
7315 struct parsed_dn
*active_pdn
= NULL
;
7316 bool update_is_newer
= false;
7320 * check if there's a conflict for single-valued links, i.e. an active
7321 * linked attribute already exists, but it has a different target value
7323 ret
= replmd_get_active_singleval_link(module
, mem_ctx
, pdn_list
,
7324 old_el
->num_values
, attr
,
7327 if (ret
!= LDB_SUCCESS
) {
7332 * If no active value exists (or the received info is for the currently
7333 * active value), then no conflict exists
7335 if (active_pdn
== NULL
|| active_pdn
== pdn_being_modified
) {
7339 DBG_WARNING("Link conflict for %s attribute on %s\n",
7340 attr
->lDAPDisplayName
, ldb_dn_get_linearized(src_obj_dn
));
7342 /* Work out how to resolve the conflict based on which info is better */
7343 update_is_newer
= replmd_link_update_is_newer(active_pdn
, la
);
7345 if (update_is_newer
) {
7346 DBG_WARNING("Using received value %s, over existing target %s\n",
7347 ldb_dn_get_linearized(dsdb_dn
->dn
),
7348 ldb_dn_get_linearized(active_pdn
->dsdb_dn
->dn
));
7351 * Delete our existing active link. The received info will then
7352 * be added (through normal link processing) as the active value
7354 ret
= replmd_delete_link_value(module
, replmd_private
, old_el
,
7355 src_obj_dn
, schema
, attr
,
7356 seq_num
, true, &active_pdn
->guid
,
7357 active_pdn
->dsdb_dn
,
7360 if (ret
!= LDB_SUCCESS
) {
7364 DBG_WARNING("Using existing target %s, over received value %s\n",
7365 ldb_dn_get_linearized(active_pdn
->dsdb_dn
->dn
),
7366 ldb_dn_get_linearized(dsdb_dn
->dn
));
7369 * we want to keep our existing active link and add the
7370 * received link as inactive
7372 *add_as_inactive
= true;
7379 process one linked attribute structure
7381 static int replmd_process_linked_attribute(struct ldb_module
*module
,
7382 struct replmd_private
*replmd_private
,
7383 struct la_entry
*la_entry
,
7384 struct ldb_request
*parent
)
7386 struct drsuapi_DsReplicaLinkedAttribute
*la
= la_entry
->la
;
7387 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
7388 struct ldb_message
*msg
;
7389 TALLOC_CTX
*tmp_ctx
= talloc_new(la_entry
);
7390 const struct dsdb_schema
*schema
= dsdb_get_schema(ldb
, tmp_ctx
);
7392 const struct dsdb_attribute
*attr
;
7393 struct dsdb_dn
*dsdb_dn
;
7394 uint64_t seq_num
= 0;
7395 struct ldb_message_element
*old_el
;
7396 time_t t
= time(NULL
);
7397 struct parsed_dn
*pdn_list
, *pdn
, *next
;
7398 struct GUID guid
= GUID_zero();
7399 bool active
= (la
->flags
& DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
)?true:false;
7401 enum deletion_state deletion_state
= OBJECT_NOT_DELETED
;
7402 struct dsdb_dn
*old_dsdb_dn
= NULL
;
7403 struct ldb_val
*val_to_update
= NULL
;
7404 bool add_as_inactive
= false;
7407 * get the attribute being modified, the search result for the source object,
7408 * and the target object's DN details
7410 ret
= replmd_extract_la_entry_details(module
, la_entry
, tmp_ctx
, &attr
,
7413 if (ret
!= LDB_SUCCESS
) {
7414 talloc_free(tmp_ctx
);
7419 * Check for deleted objects per MS-DRSR 4.1.10.6.14
7420 * ProcessLinkValue, because link updates are not applied to
7421 * recycled and tombstone objects. We don't have to delete
7422 * any existing link, that should have happened when the
7423 * object deletion was replicated or initiated.
7425 replmd_deletion_state(module
, msg
, &deletion_state
, NULL
);
7427 if (deletion_state
>= OBJECT_RECYCLED
) {
7428 talloc_free(tmp_ctx
);
7432 old_el
= ldb_msg_find_element(msg
, attr
->lDAPDisplayName
);
7433 if (old_el
== NULL
) {
7434 ret
= ldb_msg_add_empty(msg
, attr
->lDAPDisplayName
, LDB_FLAG_MOD_REPLACE
, &old_el
);
7435 if (ret
!= LDB_SUCCESS
) {
7436 ldb_module_oom(module
);
7437 talloc_free(tmp_ctx
);
7438 return LDB_ERR_OPERATIONS_ERROR
;
7441 old_el
->flags
= LDB_FLAG_MOD_REPLACE
;
7444 /* parse the existing links */
7445 ret
= get_parsed_dns_trusted(module
, replmd_private
, tmp_ctx
, old_el
, &pdn_list
,
7446 attr
->syntax
->ldap_oid
, parent
);
7448 if (ret
!= LDB_SUCCESS
) {
7449 talloc_free(tmp_ctx
);
7453 ret
= replmd_check_target_exists(module
, dsdb_dn
, la_entry
, msg
->dn
,
7454 true, &guid
, &ignore_link
);
7456 if (ret
!= LDB_SUCCESS
) {
7457 talloc_free(tmp_ctx
);
7462 * there are some cases where the target object doesn't exist, but it's
7463 * OK to ignore the linked attribute
7466 talloc_free(tmp_ctx
);
7470 /* see if this link already exists */
7471 ret
= parsed_dn_find(ldb
, pdn_list
, old_el
->num_values
,
7474 dsdb_dn
->extra_part
, 0,
7476 attr
->syntax
->ldap_oid
,
7478 if (ret
!= LDB_SUCCESS
) {
7479 talloc_free(tmp_ctx
);
7483 if (!replmd_link_update_is_newer(pdn
, la
)) {
7484 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
7485 old_el
->name
, ldb_dn_get_linearized(msg
->dn
),
7486 GUID_string(tmp_ctx
, &la
->meta_data
.originating_invocation_id
)));
7487 talloc_free(tmp_ctx
);
7491 /* get a seq_num for this change */
7492 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &seq_num
);
7493 if (ret
!= LDB_SUCCESS
) {
7494 talloc_free(tmp_ctx
);
7499 * check for single-valued link conflicts, i.e. an active linked
7500 * attribute already exists, but it has a different target value
7503 ret
= replmd_check_singleval_la_conflict(module
, replmd_private
,
7504 tmp_ctx
, msg
->dn
, la
,
7505 dsdb_dn
, pdn
, pdn_list
,
7506 old_el
, schema
, attr
,
7509 if (ret
!= LDB_SUCCESS
) {
7510 talloc_free(tmp_ctx
);
7516 uint32_t rmd_flags
= dsdb_dn_rmd_flags(pdn
->dsdb_dn
->dn
);
7518 if (!(rmd_flags
& DSDB_RMD_FLAG_DELETED
)) {
7519 /* remove the existing backlink */
7520 ret
= replmd_add_backlink(module
, replmd_private
,
7523 &pdn
->guid
, false, attr
,
7525 if (ret
!= LDB_SUCCESS
) {
7526 talloc_free(tmp_ctx
);
7531 val_to_update
= pdn
->v
;
7532 old_dsdb_dn
= pdn
->dsdb_dn
;
7538 * We know where the new one needs to be, from the *next
7539 * pointer into pdn_list.
7542 offset
= old_el
->num_values
;
7544 if (next
->dsdb_dn
== NULL
) {
7545 ret
= really_parse_trusted_dn(tmp_ctx
, ldb
, next
,
7546 attr
->syntax
->ldap_oid
);
7547 if (ret
!= LDB_SUCCESS
) {
7551 offset
= next
- pdn_list
;
7552 if (offset
> old_el
->num_values
) {
7553 talloc_free(tmp_ctx
);
7554 return LDB_ERR_OPERATIONS_ERROR
;
7558 old_el
->values
= talloc_realloc(msg
->elements
, old_el
->values
,
7559 struct ldb_val
, old_el
->num_values
+1);
7560 if (!old_el
->values
) {
7561 ldb_module_oom(module
);
7562 return LDB_ERR_OPERATIONS_ERROR
;
7565 if (offset
!= old_el
->num_values
) {
7566 memmove(&old_el
->values
[offset
+ 1], &old_el
->values
[offset
],
7567 (old_el
->num_values
- offset
) * sizeof(old_el
->values
[0]));
7570 old_el
->num_values
++;
7572 val_to_update
= &old_el
->values
[offset
];
7576 /* set the link attribute's value to the info that was received */
7577 ret
= replmd_set_la_val(tmp_ctx
, val_to_update
, dsdb_dn
, old_dsdb_dn
,
7578 &la
->meta_data
.originating_invocation_id
,
7579 la
->meta_data
.originating_usn
, seq_num
,
7580 la
->meta_data
.originating_change_time
,
7581 la
->meta_data
.version
,
7583 if (ret
!= LDB_SUCCESS
) {
7584 talloc_free(tmp_ctx
);
7588 if (add_as_inactive
) {
7590 /* Set the new link as inactive/deleted to avoid conflicts */
7591 ret
= replmd_delete_link_value(module
, replmd_private
, old_el
,
7592 msg
->dn
, schema
, attr
, seq_num
,
7593 false, &guid
, dsdb_dn
,
7596 if (ret
!= LDB_SUCCESS
) {
7597 talloc_free(tmp_ctx
);
7601 } else if (active
) {
7603 /* if the new link is active, then add the new backlink */
7604 ret
= replmd_add_backlink(module
, replmd_private
,
7609 if (ret
!= LDB_SUCCESS
) {
7610 talloc_free(tmp_ctx
);
7615 /* we only change whenChanged and uSNChanged if the seq_num
7617 ret
= add_time_element(msg
, "whenChanged", t
);
7618 if (ret
!= LDB_SUCCESS
) {
7619 talloc_free(tmp_ctx
);
7624 ret
= add_uint64_element(ldb
, msg
, "uSNChanged", seq_num
);
7625 if (ret
!= LDB_SUCCESS
) {
7626 talloc_free(tmp_ctx
);
7631 old_el
= ldb_msg_find_element(msg
, attr
->lDAPDisplayName
);
7632 if (old_el
== NULL
) {
7633 talloc_free(tmp_ctx
);
7634 return ldb_operr(ldb
);
7637 ret
= dsdb_check_single_valued_link(attr
, old_el
);
7638 if (ret
!= LDB_SUCCESS
) {
7639 talloc_free(tmp_ctx
);
7643 old_el
->flags
|= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK
;
7645 ret
= linked_attr_modify(module
, msg
, parent
);
7646 if (ret
!= LDB_SUCCESS
) {
7647 ldb_debug(ldb
, LDB_DEBUG_WARNING
, "Failed to apply linked attribute change '%s'\n%s\n",
7649 ldb_ldif_message_redacted_string(ldb
,
7651 LDB_CHANGETYPE_MODIFY
,
7653 talloc_free(tmp_ctx
);
7657 talloc_free(tmp_ctx
);
7662 static int replmd_extended(struct ldb_module
*module
, struct ldb_request
*req
)
7664 if (strcmp(req
->op
.extended
.oid
, DSDB_EXTENDED_REPLICATED_OBJECTS_OID
) == 0) {
7665 return replmd_extended_replicated_objects(module
, req
);
7668 return ldb_next_request(module
, req
);
7673 we hook into the transaction operations to allow us to
7674 perform the linked attribute updates at the end of the whole
7675 transaction. This allows a forward linked attribute to be created
7676 before the object is created. During a vampire, w2k8 sends us linked
7677 attributes before the objects they are part of.
7679 static int replmd_start_transaction(struct ldb_module
*module
)
7681 /* create our private structure for this transaction */
7682 struct replmd_private
*replmd_private
= talloc_get_type(ldb_module_get_private(module
),
7683 struct replmd_private
);
7684 replmd_txn_cleanup(replmd_private
);
7686 /* free any leftover mod_usn records from cancelled
7688 while (replmd_private
->ncs
) {
7689 struct nc_entry
*e
= replmd_private
->ncs
;
7690 DLIST_REMOVE(replmd_private
->ncs
, e
);
7694 replmd_private
->originating_updates
= false;
7696 return ldb_next_start_trans(module
);
7700 on prepare commit we loop over our queued la_context structures and
7703 static int replmd_prepare_commit(struct ldb_module
*module
)
7705 struct replmd_private
*replmd_private
=
7706 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
7707 struct la_entry
*la
, *prev
;
7711 * Walk the list of linked attributes from DRS replication.
7713 * We walk backwards, to do the first entry first, as we
7714 * added the entries with DLIST_ADD() which puts them at the
7717 for (la
= DLIST_TAIL(replmd_private
->la_list
); la
; la
=prev
) {
7718 prev
= DLIST_PREV(la
);
7719 DLIST_REMOVE(replmd_private
->la_list
, la
);
7720 ret
= replmd_process_linked_attribute(module
, replmd_private
,
7722 if (ret
!= LDB_SUCCESS
) {
7723 replmd_txn_cleanup(replmd_private
);
7728 replmd_txn_cleanup(replmd_private
);
7730 /* possibly change @REPLCHANGED */
7731 ret
= replmd_notify_store(module
, NULL
);
7732 if (ret
!= LDB_SUCCESS
) {
7736 return ldb_next_prepare_commit(module
);
7739 static int replmd_del_transaction(struct ldb_module
*module
)
7741 struct replmd_private
*replmd_private
=
7742 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
7743 replmd_txn_cleanup(replmd_private
);
7745 return ldb_next_del_trans(module
);
7749 static const struct ldb_module_ops ldb_repl_meta_data_module_ops
= {
7750 .name
= "repl_meta_data",
7751 .init_context
= replmd_init
,
7753 .modify
= replmd_modify
,
7754 .rename
= replmd_rename
,
7755 .del
= replmd_delete
,
7756 .extended
= replmd_extended
,
7757 .start_transaction
= replmd_start_transaction
,
7758 .prepare_commit
= replmd_prepare_commit
,
7759 .del_transaction
= replmd_del_transaction
,
7762 int ldb_repl_meta_data_module_init(const char *version
)
7764 LDB_MODULE_CHECK_VERSION(version
);
7765 return ldb_register_module(&ldb_repl_meta_data_module_ops
);