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 "../libds/common/flags.h"
43 #include "librpc/gen_ndr/ndr_misc.h"
44 #include "librpc/gen_ndr/ndr_drsuapi.h"
45 #include "librpc/gen_ndr/ndr_drsblobs.h"
46 #include "param/param.h"
47 #include "libcli/security/security.h"
48 #include "lib/util/dlinklist.h"
49 #include "dsdb/samdb/ldb_modules/util.h"
50 #include "lib/util/binsearch.h"
51 #include "lib/util/tsort.h"
54 * It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
55 * Deleted Objects Container
57 static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME
= 2650466015990000000ULL;
59 struct replmd_private
{
61 struct la_entry
*la_list
;
63 struct la_backlink
*la_backlinks
;
65 struct nc_entry
*prev
, *next
;
68 uint64_t mod_usn_urgent
;
70 struct ldb_dn
*schema_dn
;
71 bool originating_updates
;
76 struct la_entry
*next
, *prev
;
77 struct drsuapi_DsReplicaLinkedAttribute
*la
;
80 struct replmd_replicated_request
{
81 struct ldb_module
*module
;
82 struct ldb_request
*req
;
84 const struct dsdb_schema
*schema
;
85 struct GUID our_invocation_id
;
87 /* the controls we pass down */
88 struct ldb_control
**controls
;
90 /* details for the mode where we apply a bunch of inbound replication meessages */
92 uint32_t index_current
;
93 struct dsdb_extended_replicated_objects
*objs
;
95 struct ldb_message
*search_msg
;
96 struct GUID local_parent_guid
;
105 struct dsdb_dn
*dsdb_dn
;
110 static int replmd_replicated_apply_merge(struct replmd_replicated_request
*ar
);
111 static int replmd_delete_internals(struct ldb_module
*module
, struct ldb_request
*req
, bool re_delete
);
112 static int replmd_check_upgrade_links(struct ldb_context
*ldb
,
113 struct parsed_dn
*dns
, uint32_t count
,
114 struct ldb_message_element
*el
,
115 const char *ldap_oid
);
117 enum urgent_situation
{
118 REPL_URGENT_ON_CREATE
= 1,
119 REPL_URGENT_ON_UPDATE
= 2,
120 REPL_URGENT_ON_DELETE
= 4
123 enum deletion_state
{
124 OBJECT_NOT_DELETED
=1,
131 static void replmd_deletion_state(struct ldb_module
*module
,
132 const struct ldb_message
*msg
,
133 enum deletion_state
*current_state
,
134 enum deletion_state
*next_state
)
137 bool enabled
= false;
140 *current_state
= OBJECT_REMOVED
;
141 if (next_state
!= NULL
) {
142 *next_state
= OBJECT_REMOVED
;
147 ret
= dsdb_recyclebin_enabled(module
, &enabled
);
148 if (ret
!= LDB_SUCCESS
) {
152 if (ldb_msg_check_string_attribute(msg
, "isDeleted", "TRUE")) {
154 *current_state
= OBJECT_TOMBSTONE
;
155 if (next_state
!= NULL
) {
156 *next_state
= OBJECT_REMOVED
;
161 if (ldb_msg_check_string_attribute(msg
, "isRecycled", "TRUE")) {
162 *current_state
= OBJECT_RECYCLED
;
163 if (next_state
!= NULL
) {
164 *next_state
= OBJECT_REMOVED
;
169 *current_state
= OBJECT_DELETED
;
170 if (next_state
!= NULL
) {
171 *next_state
= OBJECT_RECYCLED
;
176 *current_state
= OBJECT_NOT_DELETED
;
177 if (next_state
== NULL
) {
182 *next_state
= OBJECT_DELETED
;
184 *next_state
= OBJECT_TOMBSTONE
;
188 static const struct {
189 const char *update_name
;
190 enum urgent_situation repl_situation
;
191 } urgent_objects
[] = {
192 {"nTDSDSA", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_DELETE
)},
193 {"crossRef", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_DELETE
)},
194 {"attributeSchema", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_UPDATE
)},
195 {"classSchema", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_UPDATE
)},
196 {"secret", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_UPDATE
)},
197 {"rIDManager", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_UPDATE
)},
201 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
202 static const char *urgent_attrs
[] = {
205 "userAccountControl",
210 static bool replmd_check_urgent_objectclass(const struct ldb_message_element
*objectclass_el
,
211 enum urgent_situation situation
)
214 for (i
=0; urgent_objects
[i
].update_name
; i
++) {
216 if ((situation
& urgent_objects
[i
].repl_situation
) == 0) {
220 for (j
=0; j
<objectclass_el
->num_values
; j
++) {
221 const struct ldb_val
*v
= &objectclass_el
->values
[j
];
222 if (ldb_attr_cmp((const char *)v
->data
, urgent_objects
[i
].update_name
) == 0) {
230 static bool replmd_check_urgent_attribute(const struct ldb_message_element
*el
)
232 if (ldb_attr_in_list(urgent_attrs
, el
->name
)) {
239 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request
*ar
);
242 initialise the module
243 allocate the private structure and build the list
244 of partition DNs for use by replmd_notify()
246 static int replmd_init(struct ldb_module
*module
)
248 struct replmd_private
*replmd_private
;
249 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
250 static const char *samba_dsdb_attrs
[] = { SAMBA_COMPATIBLE_FEATURES_ATTR
, NULL
};
251 struct ldb_dn
*samba_dsdb_dn
;
252 struct ldb_result
*res
;
254 TALLOC_CTX
*frame
= talloc_stackframe();
255 replmd_private
= talloc_zero(module
, struct replmd_private
);
256 if (replmd_private
== NULL
) {
259 return LDB_ERR_OPERATIONS_ERROR
;
261 ldb_module_set_private(module
, replmd_private
);
263 replmd_private
->schema_dn
= ldb_get_schema_basedn(ldb
);
265 samba_dsdb_dn
= ldb_dn_new(frame
, ldb
, "@SAMBA_DSDB");
266 if (!samba_dsdb_dn
) {
271 ret
= dsdb_module_search_dn(module
, frame
, &res
, samba_dsdb_dn
,
272 samba_dsdb_attrs
, DSDB_FLAG_NEXT_MODULE
, NULL
);
273 if (ret
== LDB_SUCCESS
) {
274 replmd_private
->sorted_links
275 = ldb_msg_check_string_attribute(res
->msgs
[0],
276 SAMBA_COMPATIBLE_FEATURES_ATTR
,
277 SAMBA_SORTED_LINKS_FEATURE
);
281 return ldb_next_init(module
);
285 cleanup our per-transaction contexts
287 static void replmd_txn_cleanup(struct replmd_private
*replmd_private
)
289 talloc_free(replmd_private
->la_ctx
);
290 replmd_private
->la_list
= NULL
;
291 replmd_private
->la_ctx
= NULL
;
293 talloc_free(replmd_private
->bl_ctx
);
294 replmd_private
->la_backlinks
= NULL
;
295 replmd_private
->bl_ctx
= NULL
;
300 struct la_backlink
*next
, *prev
;
301 const char *attr_name
;
302 struct GUID forward_guid
, target_guid
;
307 a ldb_modify request operating on modules below the
310 static int linked_attr_modify(struct ldb_module
*module
,
311 const struct ldb_message
*message
,
312 struct ldb_request
*parent
)
314 struct ldb_request
*mod_req
;
316 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
317 TALLOC_CTX
*tmp_ctx
= talloc_new(module
);
318 struct ldb_result
*res
;
320 res
= talloc_zero(tmp_ctx
, struct ldb_result
);
322 talloc_free(tmp_ctx
);
323 return ldb_oom(ldb_module_get_ctx(module
));
326 ret
= ldb_build_mod_req(&mod_req
, ldb
, tmp_ctx
,
330 ldb_modify_default_callback
,
332 LDB_REQ_SET_LOCATION(mod_req
);
333 if (ret
!= LDB_SUCCESS
) {
334 talloc_free(tmp_ctx
);
338 ret
= ldb_request_add_control(mod_req
, DSDB_CONTROL_REPLICATED_UPDATE_OID
,
340 if (ret
!= LDB_SUCCESS
) {
344 /* Run the new request */
345 ret
= ldb_next_request(module
, mod_req
);
347 if (ret
== LDB_SUCCESS
) {
348 ret
= ldb_wait(mod_req
->handle
, LDB_WAIT_ALL
);
351 talloc_free(tmp_ctx
);
356 process a backlinks we accumulated during a transaction, adding and
357 deleting the backlinks from the target objects
359 static int replmd_process_backlink(struct ldb_module
*module
, struct la_backlink
*bl
, struct ldb_request
*parent
)
361 struct ldb_dn
*target_dn
, *source_dn
;
363 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
364 struct ldb_message
*msg
;
365 TALLOC_CTX
*tmp_ctx
= talloc_new(bl
);
371 - construct ldb_message
372 - either an add or a delete
374 ret
= dsdb_module_dn_by_guid(module
, tmp_ctx
, &bl
->target_guid
, &target_dn
, parent
);
375 if (ret
!= LDB_SUCCESS
) {
376 DEBUG(2,(__location__
": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
377 GUID_string(bl
, &bl
->target_guid
)));
381 ret
= dsdb_module_dn_by_guid(module
, tmp_ctx
, &bl
->forward_guid
, &source_dn
, parent
);
382 if (ret
!= LDB_SUCCESS
) {
383 ldb_asprintf_errstring(ldb
, "Failed to find source DN for linked attribute with GUID %s\n",
384 GUID_string(bl
, &bl
->forward_guid
));
385 talloc_free(tmp_ctx
);
389 msg
= ldb_msg_new(tmp_ctx
);
391 ldb_module_oom(module
);
392 talloc_free(tmp_ctx
);
393 return LDB_ERR_OPERATIONS_ERROR
;
396 /* construct a ldb_message for adding/deleting the backlink */
398 dn_string
= ldb_dn_get_extended_linearized(tmp_ctx
, source_dn
, 1);
400 ldb_module_oom(module
);
401 talloc_free(tmp_ctx
);
402 return LDB_ERR_OPERATIONS_ERROR
;
404 ret
= ldb_msg_add_steal_string(msg
, bl
->attr_name
, dn_string
);
405 if (ret
!= LDB_SUCCESS
) {
406 talloc_free(tmp_ctx
);
409 msg
->elements
[0].flags
= bl
->active
?LDB_FLAG_MOD_ADD
:LDB_FLAG_MOD_DELETE
;
411 /* a backlink should never be single valued. Unfortunately the
412 exchange schema has a attribute
413 msExchBridgeheadedLocalConnectorsDNBL which is single
414 valued and a backlink. We need to cope with that by
415 ignoring the single value flag */
416 msg
->elements
[0].flags
|= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK
;
418 ret
= dsdb_module_modify(module
, msg
, DSDB_FLAG_NEXT_MODULE
, parent
);
419 if (ret
== LDB_ERR_NO_SUCH_ATTRIBUTE
&& !bl
->active
) {
420 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
421 cope with possible corruption where the backlink has
422 already been removed */
423 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
424 ldb_dn_get_linearized(target_dn
),
425 ldb_dn_get_linearized(source_dn
),
426 ldb_errstring(ldb
)));
428 } else if (ret
!= LDB_SUCCESS
) {
429 ldb_asprintf_errstring(ldb
, "Failed to %s backlink from %s to %s - %s",
430 bl
->active
?"add":"remove",
431 ldb_dn_get_linearized(source_dn
),
432 ldb_dn_get_linearized(target_dn
),
434 talloc_free(tmp_ctx
);
437 talloc_free(tmp_ctx
);
442 add a backlink to the list of backlinks to add/delete in the prepare
445 static int replmd_add_backlink(struct ldb_module
*module
,
446 struct replmd_private
*replmd_private
,
447 const struct dsdb_schema
*schema
,
448 struct GUID
*forward_guid
,
449 struct GUID
*target_guid
, bool active
,
450 const struct dsdb_attribute
*schema_attr
,
453 const struct dsdb_attribute
*target_attr
;
454 struct la_backlink
*bl
;
456 target_attr
= dsdb_attribute_by_linkID(schema
, schema_attr
->linkID
^ 1);
459 * windows 2003 has a broken schema where the
460 * definition of msDS-IsDomainFor is missing (which is
461 * supposed to be the backlink of the
462 * msDS-HasDomainNCs attribute
467 /* see if its already in the list */
468 for (bl
=replmd_private
->la_backlinks
; bl
; bl
=bl
->next
) {
469 if (GUID_equal(forward_guid
, &bl
->forward_guid
) &&
470 GUID_equal(target_guid
, &bl
->target_guid
) &&
471 (target_attr
->lDAPDisplayName
== bl
->attr_name
||
472 strcmp(target_attr
->lDAPDisplayName
, bl
->attr_name
) == 0)) {
478 /* we found an existing one */
479 if (bl
->active
== active
) {
482 DLIST_REMOVE(replmd_private
->la_backlinks
, bl
);
487 if (replmd_private
->bl_ctx
== NULL
) {
488 replmd_private
->bl_ctx
= talloc_new(replmd_private
);
489 if (replmd_private
->bl_ctx
== NULL
) {
490 ldb_module_oom(module
);
491 return LDB_ERR_OPERATIONS_ERROR
;
496 bl
= talloc(replmd_private
->bl_ctx
, struct la_backlink
);
498 ldb_module_oom(module
);
499 return LDB_ERR_OPERATIONS_ERROR
;
502 /* Ensure the schema does not go away before the bl->attr_name is used */
503 if (!talloc_reference(bl
, schema
)) {
505 ldb_module_oom(module
);
506 return LDB_ERR_OPERATIONS_ERROR
;
509 bl
->attr_name
= target_attr
->lDAPDisplayName
;
510 bl
->forward_guid
= *forward_guid
;
511 bl
->target_guid
= *target_guid
;
514 /* the caller may ask for this backlink to be processed
517 int ret
= replmd_process_backlink(module
, bl
, NULL
);
522 DLIST_ADD(replmd_private
->la_backlinks
, bl
);
529 * Callback for most write operations in this module:
531 * notify the repl task that a object has changed. The notifies are
532 * gathered up in the replmd_private structure then written to the
533 * @REPLCHANGED object in each partition during the prepare_commit
535 static int replmd_op_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
538 struct replmd_replicated_request
*ac
=
539 talloc_get_type_abort(req
->context
, struct replmd_replicated_request
);
540 struct replmd_private
*replmd_private
=
541 talloc_get_type_abort(ldb_module_get_private(ac
->module
), struct replmd_private
);
542 struct nc_entry
*modified_partition
;
543 struct ldb_control
*partition_ctrl
;
544 const struct dsdb_control_current_partition
*partition
;
546 struct ldb_control
**controls
;
548 partition_ctrl
= ldb_reply_get_control(ares
, DSDB_CONTROL_CURRENT_PARTITION_OID
);
550 controls
= ares
->controls
;
551 if (ldb_request_get_control(ac
->req
,
552 DSDB_CONTROL_CURRENT_PARTITION_OID
) == NULL
) {
554 * Remove the current partition control from what we pass up
555 * the chain if it hasn't been requested manually.
557 controls
= ldb_controls_except_specified(ares
->controls
, ares
,
561 if (ares
->error
!= LDB_SUCCESS
) {
562 DEBUG(5,("%s failure. Error is: %s\n", __FUNCTION__
, ldb_strerror(ares
->error
)));
563 return ldb_module_done(ac
->req
, controls
,
564 ares
->response
, ares
->error
);
567 if (ares
->type
!= LDB_REPLY_DONE
) {
568 ldb_set_errstring(ldb_module_get_ctx(ac
->module
), "Invalid reply type for notify\n!");
569 return ldb_module_done(ac
->req
, NULL
,
570 NULL
, LDB_ERR_OPERATIONS_ERROR
);
573 if (!partition_ctrl
) {
574 ldb_set_errstring(ldb_module_get_ctx(ac
->module
),"No partition control on reply");
575 return ldb_module_done(ac
->req
, NULL
,
576 NULL
, LDB_ERR_OPERATIONS_ERROR
);
579 partition
= talloc_get_type_abort(partition_ctrl
->data
,
580 struct dsdb_control_current_partition
);
582 if (ac
->seq_num
> 0) {
583 for (modified_partition
= replmd_private
->ncs
; modified_partition
;
584 modified_partition
= modified_partition
->next
) {
585 if (ldb_dn_compare(modified_partition
->dn
, partition
->dn
) == 0) {
590 if (modified_partition
== NULL
) {
591 modified_partition
= talloc_zero(replmd_private
, struct nc_entry
);
592 if (!modified_partition
) {
593 ldb_oom(ldb_module_get_ctx(ac
->module
));
594 return ldb_module_done(ac
->req
, NULL
,
595 NULL
, LDB_ERR_OPERATIONS_ERROR
);
597 modified_partition
->dn
= ldb_dn_copy(modified_partition
, partition
->dn
);
598 if (!modified_partition
->dn
) {
599 ldb_oom(ldb_module_get_ctx(ac
->module
));
600 return ldb_module_done(ac
->req
, NULL
,
601 NULL
, LDB_ERR_OPERATIONS_ERROR
);
603 DLIST_ADD(replmd_private
->ncs
, modified_partition
);
606 if (ac
->seq_num
> modified_partition
->mod_usn
) {
607 modified_partition
->mod_usn
= ac
->seq_num
;
609 modified_partition
->mod_usn_urgent
= ac
->seq_num
;
612 if (!ac
->apply_mode
) {
613 replmd_private
->originating_updates
= true;
617 if (ac
->apply_mode
) {
618 ret
= replmd_replicated_apply_isDeleted(ac
);
619 if (ret
!= LDB_SUCCESS
) {
620 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
624 /* free the partition control container here, for the
625 * common path. Other cases will have it cleaned up
626 * eventually with the ares */
627 talloc_free(partition_ctrl
);
628 return ldb_module_done(ac
->req
, controls
,
629 ares
->response
, LDB_SUCCESS
);
635 * update a @REPLCHANGED record in each partition if there have been
636 * any writes of replicated data in the partition
638 static int replmd_notify_store(struct ldb_module
*module
, struct ldb_request
*parent
)
640 struct replmd_private
*replmd_private
=
641 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
643 while (replmd_private
->ncs
) {
645 struct nc_entry
*modified_partition
= replmd_private
->ncs
;
647 ret
= dsdb_module_save_partition_usn(module
, modified_partition
->dn
,
648 modified_partition
->mod_usn
,
649 modified_partition
->mod_usn_urgent
, parent
);
650 if (ret
!= LDB_SUCCESS
) {
651 DEBUG(0,(__location__
": Failed to save partition uSN for %s\n",
652 ldb_dn_get_linearized(modified_partition
->dn
)));
656 if (ldb_dn_compare(modified_partition
->dn
,
657 replmd_private
->schema_dn
) == 0) {
658 struct ldb_result
*ext_res
;
659 ret
= dsdb_module_extended(module
,
660 replmd_private
->schema_dn
,
662 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID
,
664 DSDB_FLAG_NEXT_MODULE
,
666 if (ret
!= LDB_SUCCESS
) {
669 talloc_free(ext_res
);
672 DLIST_REMOVE(replmd_private
->ncs
, modified_partition
);
673 talloc_free(modified_partition
);
681 created a replmd_replicated_request context
683 static struct replmd_replicated_request
*replmd_ctx_init(struct ldb_module
*module
,
684 struct ldb_request
*req
)
686 struct ldb_context
*ldb
;
687 struct replmd_replicated_request
*ac
;
688 const struct GUID
*our_invocation_id
;
690 ldb
= ldb_module_get_ctx(module
);
692 ac
= talloc_zero(req
, struct replmd_replicated_request
);
701 ac
->schema
= dsdb_get_schema(ldb
, ac
);
703 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
704 "replmd_modify: no dsdb_schema loaded");
705 DEBUG(0,(__location__
": %s\n", ldb_errstring(ldb
)));
710 /* get our invocationId */
711 our_invocation_id
= samdb_ntds_invocation_id(ldb
);
712 if (!our_invocation_id
) {
713 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
714 "replmd_add: unable to find invocationId\n");
718 ac
->our_invocation_id
= *our_invocation_id
;
724 add a time element to a record
726 static int add_time_element(struct ldb_message
*msg
, const char *attr
, time_t t
)
728 struct ldb_message_element
*el
;
732 if (ldb_msg_find_element(msg
, attr
) != NULL
) {
736 s
= ldb_timestring(msg
, t
);
738 return LDB_ERR_OPERATIONS_ERROR
;
741 ret
= ldb_msg_add_string(msg
, attr
, s
);
742 if (ret
!= LDB_SUCCESS
) {
746 el
= ldb_msg_find_element(msg
, attr
);
747 /* always set as replace. This works because on add ops, the flag
749 el
->flags
= LDB_FLAG_MOD_REPLACE
;
755 add a uint64_t element to a record
757 static int add_uint64_element(struct ldb_context
*ldb
, struct ldb_message
*msg
,
758 const char *attr
, uint64_t v
)
760 struct ldb_message_element
*el
;
763 if (ldb_msg_find_element(msg
, attr
) != NULL
) {
767 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, attr
, v
);
768 if (ret
!= LDB_SUCCESS
) {
772 el
= ldb_msg_find_element(msg
, attr
);
773 /* always set as replace. This works because on add ops, the flag
775 el
->flags
= LDB_FLAG_MOD_REPLACE
;
780 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1
*m1
,
781 const struct replPropertyMetaData1
*m2
,
782 const uint32_t *rdn_attid
)
785 * This assignment seems inoccous, but it is critical for the
786 * system, as we need to do the comparisons as a unsigned
787 * quantity, not signed (enums are signed integers)
789 uint32_t attid_1
= m1
->attid
;
790 uint32_t attid_2
= m2
->attid
;
792 if (attid_1
== attid_2
) {
797 * See above regarding this being an unsigned comparison.
798 * Otherwise when the high bit is set on non-standard
799 * attributes, they would end up first, before objectClass
802 return attid_1
> attid_2
? 1 : -1;
805 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context
*ldb
,
806 struct replPropertyMetaDataCtr1
*ctr1
,
809 if (ctr1
->count
== 0) {
810 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
811 "No elements found in replPropertyMetaData for %s!\n",
812 ldb_dn_get_linearized(dn
));
813 return LDB_ERR_CONSTRAINT_VIOLATION
;
816 /* the objectClass attribute is value 0x00000000, so must be first */
817 if (ctr1
->array
[0].attid
!= DRSUAPI_ATTID_objectClass
) {
818 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
819 "No objectClass found in replPropertyMetaData for %s!\n",
820 ldb_dn_get_linearized(dn
));
821 return LDB_ERR_OBJECT_CLASS_VIOLATION
;
827 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context
*ldb
,
828 struct replPropertyMetaDataCtr1
*ctr1
,
831 /* Note this is O(n^2) for the almost-sorted case, which this is */
832 LDB_TYPESAFE_QSORT(ctr1
->array
, ctr1
->count
, NULL
,
833 replmd_replPropertyMetaData1_attid_sort
);
834 return replmd_replPropertyMetaDataCtr1_verify(ldb
, ctr1
, dn
);
837 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element
*e1
,
838 const struct ldb_message_element
*e2
,
839 const struct dsdb_schema
*schema
)
841 const struct dsdb_attribute
*a1
;
842 const struct dsdb_attribute
*a2
;
845 * TODO: make this faster by caching the dsdb_attribute pointer
846 * on the ldb_messag_element
849 a1
= dsdb_attribute_by_lDAPDisplayName(schema
, e1
->name
);
850 a2
= dsdb_attribute_by_lDAPDisplayName(schema
, e2
->name
);
853 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
857 return strcasecmp(e1
->name
, e2
->name
);
859 if (a1
->attributeID_id
== a2
->attributeID_id
) {
862 return a1
->attributeID_id
> a2
->attributeID_id
? 1 : -1;
865 static void replmd_ldb_message_sort(struct ldb_message
*msg
,
866 const struct dsdb_schema
*schema
)
868 LDB_TYPESAFE_QSORT(msg
->elements
, msg
->num_elements
, schema
, replmd_ldb_message_element_attid_sort
);
871 static int replmd_build_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
, struct dsdb_dn
*dsdb_dn
,
872 const struct GUID
*invocation_id
, uint64_t seq_num
,
873 uint64_t local_usn
, NTTIME nttime
, uint32_t version
, bool deleted
);
875 static int get_parsed_dns(struct ldb_module
*module
, TALLOC_CTX
*mem_ctx
,
876 struct ldb_message_element
*el
, struct parsed_dn
**pdn
,
877 const char *ldap_oid
, struct ldb_request
*parent
);
880 fix up linked attributes in replmd_add.
881 This involves setting up the right meta-data in extended DN
882 components, and creating backlinks to the object
884 static int replmd_add_fix_la(struct ldb_module
*module
, TALLOC_CTX
*mem_ctx
,
885 struct replmd_private
*replmd_private
,
886 struct ldb_message_element
*el
,
887 uint64_t seq_num
, const struct GUID
*invocationId
, NTTIME now
,
888 struct GUID
*guid
, const struct dsdb_attribute
*sa
,
889 struct ldb_request
*parent
)
892 TALLOC_CTX
*tmp_ctx
= talloc_new(mem_ctx
);
893 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
894 struct parsed_dn
*pdn
;
895 /* We will take a reference to the schema in replmd_add_backlink */
896 const struct dsdb_schema
*schema
= dsdb_get_schema(ldb
, NULL
);
897 struct ldb_val
*new_values
= NULL
;
899 int ret
= get_parsed_dns(module
, tmp_ctx
, el
, &pdn
,
900 sa
->syntax
->ldap_oid
, parent
);
901 if (ret
!= LDB_SUCCESS
) {
902 talloc_free(tmp_ctx
);
906 new_values
= talloc_array(tmp_ctx
, struct ldb_val
, el
->num_values
);
907 if (new_values
== NULL
) {
908 ldb_module_oom(module
);
909 talloc_free(tmp_ctx
);
910 return LDB_ERR_OPERATIONS_ERROR
;
913 for (i
= 0; i
< el
->num_values
; i
++) {
914 struct parsed_dn
*p
= &pdn
[i
];
915 ret
= replmd_build_la_val(el
->values
, p
->v
, p
->dsdb_dn
,
917 seq_num
, seq_num
, now
, 0, false);
918 if (ret
!= LDB_SUCCESS
) {
919 talloc_free(tmp_ctx
);
923 /* This is the only place we are doing deferred back-links */
924 ret
= replmd_add_backlink(module
, replmd_private
,
925 schema
, guid
, &p
->guid
, true, sa
,
927 if (ret
!= LDB_SUCCESS
) {
928 talloc_free(tmp_ctx
);
932 new_values
[i
] = *p
->v
;
934 el
->values
= talloc_steal(mem_ctx
, new_values
);
936 talloc_free(tmp_ctx
);
942 intercept add requests
944 static int replmd_add(struct ldb_module
*module
, struct ldb_request
*req
)
946 struct ldb_context
*ldb
;
947 struct ldb_control
*control
;
948 struct replmd_replicated_request
*ac
;
949 enum ndr_err_code ndr_err
;
950 struct ldb_request
*down_req
;
951 struct ldb_message
*msg
;
952 const DATA_BLOB
*guid_blob
;
954 struct replPropertyMetaDataBlob nmd
;
955 struct ldb_val nmd_value
;
958 * The use of a time_t here seems odd, but as the NTTIME
959 * elements are actually declared as NTTIME_1sec in the IDL,
960 * getting a higher resolution timestamp is not required.
962 time_t t
= time(NULL
);
967 unsigned int functional_level
;
969 bool allow_add_guid
= false;
970 bool remove_current_guid
= false;
971 bool is_urgent
= false;
972 bool is_schema_nc
= false;
973 struct ldb_message_element
*objectclass_el
;
974 struct replmd_private
*replmd_private
=
975 talloc_get_type_abort(ldb_module_get_private(module
), struct replmd_private
);
977 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
978 control
= ldb_request_get_control(req
, LDB_CONTROL_RELAX_OID
);
980 allow_add_guid
= true;
983 /* do not manipulate our control entries */
984 if (ldb_dn_is_special(req
->op
.add
.message
->dn
)) {
985 return ldb_next_request(module
, req
);
988 ldb
= ldb_module_get_ctx(module
);
990 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_add\n");
992 guid_blob
= ldb_msg_find_ldb_val(req
->op
.add
.message
, "objectGUID");
993 if (guid_blob
!= NULL
) {
994 if (!allow_add_guid
) {
995 ldb_set_errstring(ldb
,
996 "replmd_add: it's not allowed to add an object with objectGUID!");
997 return LDB_ERR_UNWILLING_TO_PERFORM
;
999 NTSTATUS status
= GUID_from_data_blob(guid_blob
,&guid
);
1000 if (!NT_STATUS_IS_OK(status
)) {
1001 ldb_set_errstring(ldb
,
1002 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1003 return LDB_ERR_UNWILLING_TO_PERFORM
;
1005 /* we remove this attribute as it can be a string and
1006 * will not be treated correctly and then we will re-add
1007 * it later on in the good format */
1008 remove_current_guid
= true;
1012 guid
= GUID_random();
1015 ac
= replmd_ctx_init(module
, req
);
1017 return ldb_module_oom(module
);
1020 functional_level
= dsdb_functional_level(ldb
);
1022 /* Get a sequence number from the backend */
1023 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &ac
->seq_num
);
1024 if (ret
!= LDB_SUCCESS
) {
1029 /* we have to copy the message as the caller might have it as a const */
1030 msg
= ldb_msg_copy_shallow(ac
, req
->op
.add
.message
);
1034 return LDB_ERR_OPERATIONS_ERROR
;
1037 /* generated times */
1038 unix_to_nt_time(&now
, t
);
1039 time_str
= ldb_timestring(msg
, t
);
1043 return LDB_ERR_OPERATIONS_ERROR
;
1045 if (remove_current_guid
) {
1046 ldb_msg_remove_attr(msg
,"objectGUID");
1050 * remove autogenerated attributes
1052 ldb_msg_remove_attr(msg
, "whenCreated");
1053 ldb_msg_remove_attr(msg
, "whenChanged");
1054 ldb_msg_remove_attr(msg
, "uSNCreated");
1055 ldb_msg_remove_attr(msg
, "uSNChanged");
1056 ldb_msg_remove_attr(msg
, "replPropertyMetaData");
1059 * readd replicated attributes
1061 ret
= ldb_msg_add_string(msg
, "whenCreated", time_str
);
1062 if (ret
!= LDB_SUCCESS
) {
1068 /* build the replication meta_data */
1071 nmd
.ctr
.ctr1
.count
= msg
->num_elements
;
1072 nmd
.ctr
.ctr1
.array
= talloc_array(msg
,
1073 struct replPropertyMetaData1
,
1074 nmd
.ctr
.ctr1
.count
);
1075 if (!nmd
.ctr
.ctr1
.array
) {
1078 return LDB_ERR_OPERATIONS_ERROR
;
1081 is_schema_nc
= ldb_dn_compare_base(replmd_private
->schema_dn
, msg
->dn
) == 0;
1083 for (i
=0; i
< msg
->num_elements
;) {
1084 struct ldb_message_element
*e
= &msg
->elements
[i
];
1085 struct replPropertyMetaData1
*m
= &nmd
.ctr
.ctr1
.array
[ni
];
1086 const struct dsdb_attribute
*sa
;
1088 if (e
->name
[0] == '@') {
1093 sa
= dsdb_attribute_by_lDAPDisplayName(ac
->schema
, e
->name
);
1095 ldb_debug_set(ldb
, LDB_DEBUG_ERROR
,
1096 "replmd_add: attribute '%s' not defined in schema\n",
1099 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
1102 if ((sa
->systemFlags
& DS_FLAG_ATTR_NOT_REPLICATED
) || (sa
->systemFlags
& DS_FLAG_ATTR_IS_CONSTRUCTED
)) {
1103 /* if the attribute is not replicated (0x00000001)
1104 * or constructed (0x00000004) it has no metadata
1110 if (sa
->linkID
!= 0 && functional_level
> DS_DOMAIN_FUNCTION_2000
) {
1111 ret
= replmd_add_fix_la(module
, msg
->elements
,
1114 &ac
->our_invocation_id
, now
,
1116 if (ret
!= LDB_SUCCESS
) {
1120 /* linked attributes are not stored in
1121 replPropertyMetaData in FL above w2k */
1126 m
->attid
= dsdb_attribute_get_attid(sa
, is_schema_nc
);
1128 if (m
->attid
== DRSUAPI_ATTID_isDeleted
) {
1129 const struct ldb_val
*rdn_val
= ldb_dn_get_rdn_val(msg
->dn
);
1132 if (rdn_val
== NULL
) {
1135 return LDB_ERR_OPERATIONS_ERROR
;
1138 rdn
= (const char*)rdn_val
->data
;
1139 if (strcmp(rdn
, "Deleted Objects") == 0) {
1141 * Set the originating_change_time to 29/12/9999 at 23:59:59
1142 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1144 m
->originating_change_time
= DELETED_OBJECT_CONTAINER_CHANGE_TIME
;
1146 m
->originating_change_time
= now
;
1149 m
->originating_change_time
= now
;
1151 m
->originating_invocation_id
= ac
->our_invocation_id
;
1152 m
->originating_usn
= ac
->seq_num
;
1153 m
->local_usn
= ac
->seq_num
;
1156 if (!(e
->flags
& DSDB_FLAG_INTERNAL_FORCE_META_DATA
)) {
1161 e
->flags
&= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA
;
1163 if (e
->num_values
!= 0) {
1168 ldb_msg_remove_element(msg
, e
);
1171 /* fix meta data count */
1172 nmd
.ctr
.ctr1
.count
= ni
;
1175 * sort meta data array
1177 ret
= replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb
, &nmd
.ctr
.ctr1
, msg
->dn
);
1178 if (ret
!= LDB_SUCCESS
) {
1179 ldb_asprintf_errstring(ldb
, "%s: error during direct ADD: %s", __func__
, ldb_errstring(ldb
));
1184 /* generated NDR encoded values */
1185 ndr_err
= ndr_push_struct_blob(&nmd_value
, msg
,
1187 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
1188 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1191 return LDB_ERR_OPERATIONS_ERROR
;
1195 * add the autogenerated values
1197 ret
= dsdb_msg_add_guid(msg
, &guid
, "objectGUID");
1198 if (ret
!= LDB_SUCCESS
) {
1203 ret
= ldb_msg_add_string(msg
, "whenChanged", time_str
);
1204 if (ret
!= LDB_SUCCESS
) {
1209 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNCreated", ac
->seq_num
);
1210 if (ret
!= LDB_SUCCESS
) {
1215 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNChanged", ac
->seq_num
);
1216 if (ret
!= LDB_SUCCESS
) {
1221 ret
= ldb_msg_add_value(msg
, "replPropertyMetaData", &nmd_value
, NULL
);
1222 if (ret
!= LDB_SUCCESS
) {
1229 * sort the attributes by attid before storing the object
1231 replmd_ldb_message_sort(msg
, ac
->schema
);
1234 * Assert that we do have an objectClass
1236 objectclass_el
= ldb_msg_find_element(msg
, "objectClass");
1237 if (objectclass_el
== NULL
) {
1238 ldb_asprintf_errstring(ldb
, __location__
1239 ": objectClass missing on %s\n",
1240 ldb_dn_get_linearized(msg
->dn
));
1242 return LDB_ERR_OBJECT_CLASS_VIOLATION
;
1244 is_urgent
= replmd_check_urgent_objectclass(objectclass_el
,
1245 REPL_URGENT_ON_CREATE
);
1247 ac
->is_urgent
= is_urgent
;
1248 ret
= ldb_build_add_req(&down_req
, ldb
, ac
,
1251 ac
, replmd_op_callback
,
1254 LDB_REQ_SET_LOCATION(down_req
);
1255 if (ret
!= LDB_SUCCESS
) {
1260 /* current partition control is needed by "replmd_op_callback" */
1261 if (ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
) == NULL
) {
1262 ret
= ldb_request_add_control(down_req
,
1263 DSDB_CONTROL_CURRENT_PARTITION_OID
,
1265 if (ret
!= LDB_SUCCESS
) {
1271 if (functional_level
== DS_DOMAIN_FUNCTION_2000
) {
1272 ret
= ldb_request_add_control(down_req
, DSDB_CONTROL_APPLY_LINKS
, false, NULL
);
1273 if (ret
!= LDB_SUCCESS
) {
1279 /* mark the control done */
1281 control
->critical
= 0;
1283 /* go on with the call chain */
1284 return ldb_next_request(module
, down_req
);
1289 * update the replPropertyMetaData for one element
1291 static int replmd_update_rpmd_element(struct ldb_context
*ldb
,
1292 struct ldb_message
*msg
,
1293 struct ldb_message_element
*el
,
1294 struct ldb_message_element
*old_el
,
1295 struct replPropertyMetaDataBlob
*omd
,
1296 const struct dsdb_schema
*schema
,
1298 const struct GUID
*our_invocation_id
,
1301 struct ldb_request
*req
)
1304 const struct dsdb_attribute
*a
;
1305 struct replPropertyMetaData1
*md1
;
1306 bool may_skip
= false;
1309 a
= dsdb_attribute_by_lDAPDisplayName(schema
, el
->name
);
1311 if (ldb_request_get_control(req
, LDB_CONTROL_RELAX_OID
)) {
1312 /* allow this to make it possible for dbcheck
1313 to remove bad attributes */
1317 DEBUG(0,(__location__
": Unable to find attribute %s in schema\n",
1319 return LDB_ERR_OPERATIONS_ERROR
;
1322 attid
= dsdb_attribute_get_attid(a
, is_schema_nc
);
1324 if ((a
->systemFlags
& DS_FLAG_ATTR_NOT_REPLICATED
) || (a
->systemFlags
& DS_FLAG_ATTR_IS_CONSTRUCTED
)) {
1329 * if the attribute's value haven't changed, and this isn't
1330 * just a delete of everything then return LDB_SUCCESS Unless
1331 * we have the provision control or if the attribute is
1332 * interSiteTopologyGenerator as this page explain:
1333 * http://support.microsoft.com/kb/224815 this attribute is
1334 * periodicaly written by the DC responsible for the intersite
1335 * generation in a given site
1337 * Unchanged could be deleting or replacing an already-gone
1338 * thing with an unconstrained delete/empty replace or a
1339 * replace with the same value, but not an add with the same
1340 * value because that could be about adding a duplicate (which
1341 * is for someone else to error out on).
1343 if (old_el
!= NULL
&& ldb_msg_element_equal_ordered(el
, old_el
)) {
1344 if (LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_REPLACE
) {
1347 } else if (old_el
== NULL
&& el
->num_values
== 0) {
1348 if (LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_REPLACE
) {
1350 } else if (LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_DELETE
) {
1353 } else if (a
->linkID
!= 0 && LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_DELETE
&&
1354 ldb_request_get_control(req
, DSDB_CONTROL_REPLMD_VANISH_LINKS
) != NULL
) {
1356 * We intentionally skip the version bump when attempting to
1359 * The control is set by dbcheck and expunge-tombstones which
1360 * both attempt to be non-replicating. Otherwise, making an
1361 * alteration to the replication state would trigger a
1362 * broadcast of all expunged objects.
1367 if (el
->flags
& DSDB_FLAG_INTERNAL_FORCE_META_DATA
) {
1369 el
->flags
&= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA
;
1373 if (strcmp(el
->name
, "interSiteTopologyGenerator") != 0 &&
1374 !ldb_request_get_control(req
, LDB_CONTROL_PROVISION_OID
)) {
1376 * allow this to make it possible for dbcheck
1377 * to rebuild broken metadata
1383 for (i
=0; i
<omd
->ctr
.ctr1
.count
; i
++) {
1385 * First check if we find it under the msDS-IntID,
1386 * then check if we find it under the OID and
1389 * This allows the administrator to simply re-write
1390 * the attributes and so restore replication, which is
1391 * likely what they will try to do.
1393 if (attid
== omd
->ctr
.ctr1
.array
[i
].attid
) {
1397 if (a
->attributeID_id
== omd
->ctr
.ctr1
.array
[i
].attid
) {
1402 if (a
->linkID
!= 0 && dsdb_functional_level(ldb
) > DS_DOMAIN_FUNCTION_2000
) {
1403 /* linked attributes are not stored in
1404 replPropertyMetaData in FL above w2k, but we do
1405 raise the seqnum for the object */
1406 if (*seq_num
== 0 &&
1407 ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, seq_num
) != LDB_SUCCESS
) {
1408 return LDB_ERR_OPERATIONS_ERROR
;
1413 if (i
== omd
->ctr
.ctr1
.count
) {
1414 /* we need to add a new one */
1415 omd
->ctr
.ctr1
.array
= talloc_realloc(msg
, omd
->ctr
.ctr1
.array
,
1416 struct replPropertyMetaData1
, omd
->ctr
.ctr1
.count
+1);
1417 if (omd
->ctr
.ctr1
.array
== NULL
) {
1419 return LDB_ERR_OPERATIONS_ERROR
;
1421 omd
->ctr
.ctr1
.count
++;
1422 ZERO_STRUCT(omd
->ctr
.ctr1
.array
[i
]);
1425 /* Get a new sequence number from the backend. We only do this
1426 * if we have a change that requires a new
1427 * replPropertyMetaData element
1429 if (*seq_num
== 0) {
1430 int ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, seq_num
);
1431 if (ret
!= LDB_SUCCESS
) {
1432 return LDB_ERR_OPERATIONS_ERROR
;
1436 md1
= &omd
->ctr
.ctr1
.array
[i
];
1439 if (md1
->attid
== DRSUAPI_ATTID_isDeleted
) {
1440 const struct ldb_val
*rdn_val
= ldb_dn_get_rdn_val(msg
->dn
);
1443 if (rdn_val
== NULL
) {
1445 return LDB_ERR_OPERATIONS_ERROR
;
1448 rdn
= (const char*)rdn_val
->data
;
1449 if (strcmp(rdn
, "Deleted Objects") == 0) {
1451 * Set the originating_change_time to 29/12/9999 at 23:59:59
1452 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1454 md1
->originating_change_time
= DELETED_OBJECT_CONTAINER_CHANGE_TIME
;
1456 md1
->originating_change_time
= now
;
1459 md1
->originating_change_time
= now
;
1461 md1
->originating_invocation_id
= *our_invocation_id
;
1462 md1
->originating_usn
= *seq_num
;
1463 md1
->local_usn
= *seq_num
;
1469 * Bump the replPropertyMetaData version on an attribute, and if it
1470 * has changed (or forced by leaving rdn_old NULL), update the value
1473 * This is important, as calling a modify operation may not change the
1474 * version number if the values appear unchanged, but a rename between
1475 * parents bumps this value.
1478 static int replmd_update_rpmd_rdn_attr(struct ldb_context
*ldb
,
1479 struct ldb_message
*msg
,
1480 const struct ldb_val
*rdn_new
,
1481 const struct ldb_val
*rdn_old
,
1482 struct replPropertyMetaDataBlob
*omd
,
1483 struct replmd_replicated_request
*ar
,
1487 const char *rdn_name
= ldb_dn_get_rdn_name(msg
->dn
);
1488 const struct dsdb_attribute
*rdn_attr
=
1489 dsdb_attribute_by_lDAPDisplayName(ar
->schema
, rdn_name
);
1490 const char *attr_name
= rdn_attr
!= NULL
?
1491 rdn_attr
->lDAPDisplayName
:
1493 struct ldb_message_element new_el
= {
1494 .flags
= LDB_FLAG_MOD_REPLACE
,
1497 .values
= discard_const_p(struct ldb_val
, rdn_new
)
1499 struct ldb_message_element old_el
= {
1500 .flags
= LDB_FLAG_MOD_REPLACE
,
1502 .num_values
= rdn_old
? 1 : 0,
1503 .values
= discard_const_p(struct ldb_val
, rdn_old
)
1506 if (ldb_msg_element_equal_ordered(&new_el
, &old_el
) == false) {
1507 int ret
= ldb_msg_add(msg
, &new_el
, LDB_FLAG_MOD_REPLACE
);
1508 if (ret
!= LDB_SUCCESS
) {
1509 return ldb_oom(ldb
);
1513 return replmd_update_rpmd_element(ldb
, msg
, &new_el
, NULL
,
1514 omd
, ar
->schema
, &ar
->seq_num
,
1515 &ar
->our_invocation_id
,
1516 now
, is_schema_nc
, ar
->req
);
1520 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd
)
1522 uint32_t count
= omd
.ctr
.ctr1
.count
;
1525 for (i
=0; i
< count
; i
++) {
1526 struct replPropertyMetaData1 m
= omd
.ctr
.ctr1
.array
[i
];
1527 if (max
< m
.local_usn
) {
1535 * update the replPropertyMetaData object each time we modify an
1536 * object. This is needed for DRS replication, as the merge on the
1537 * client is based on this object
1539 static int replmd_update_rpmd(struct ldb_module
*module
,
1540 const struct dsdb_schema
*schema
,
1541 struct ldb_request
*req
,
1542 const char * const *rename_attrs
,
1543 struct ldb_message
*msg
, uint64_t *seq_num
,
1544 time_t t
, bool is_schema_nc
,
1545 bool *is_urgent
, bool *rodc
)
1547 const struct ldb_val
*omd_value
;
1548 enum ndr_err_code ndr_err
;
1549 struct replPropertyMetaDataBlob omd
;
1552 const struct GUID
*our_invocation_id
;
1554 const char * const *attrs
= NULL
;
1555 const char * const attrs2
[] = { "uSNChanged", "objectClass", "instanceType", NULL
};
1556 struct ldb_result
*res
;
1557 struct ldb_context
*ldb
;
1558 struct ldb_message_element
*objectclass_el
;
1559 enum urgent_situation situation
;
1560 bool rmd_is_provided
;
1561 bool rmd_is_just_resorted
= false;
1562 const char *not_rename_attrs
[4 + msg
->num_elements
];
1565 attrs
= rename_attrs
;
1567 for (i
= 0; i
< msg
->num_elements
; i
++) {
1568 not_rename_attrs
[i
] = msg
->elements
[i
].name
;
1570 not_rename_attrs
[i
] = "replPropertyMetaData";
1571 not_rename_attrs
[i
+1] = "objectClass";
1572 not_rename_attrs
[i
+2] = "instanceType";
1573 not_rename_attrs
[i
+3] = NULL
;
1574 attrs
= not_rename_attrs
;
1577 ldb
= ldb_module_get_ctx(module
);
1579 our_invocation_id
= samdb_ntds_invocation_id(ldb
);
1580 if (!our_invocation_id
) {
1581 /* this happens during an initial vampire while
1582 updating the schema */
1583 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1587 unix_to_nt_time(&now
, t
);
1589 if (ldb_request_get_control(req
, DSDB_CONTROL_CHANGEREPLMETADATA_OID
)) {
1590 rmd_is_provided
= true;
1591 if (ldb_request_get_control(req
, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID
)) {
1592 rmd_is_just_resorted
= true;
1595 rmd_is_provided
= false;
1598 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1599 * otherwise we consider we are updating */
1600 if (ldb_msg_check_string_attribute(msg
, "isDeleted", "TRUE")) {
1601 situation
= REPL_URGENT_ON_DELETE
;
1602 } else if (rename_attrs
) {
1603 situation
= REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_DELETE
;
1605 situation
= REPL_URGENT_ON_UPDATE
;
1608 if (rmd_is_provided
) {
1609 /* In this case the change_replmetadata control was supplied */
1610 /* We check that it's the only attribute that is provided
1611 * (it's a rare case so it's better to keep the code simplier)
1612 * We also check that the highest local_usn is bigger or the same as
1615 if( msg
->num_elements
!= 1 ||
1616 strncmp(msg
->elements
[0].name
,
1617 "replPropertyMetaData", 20) ) {
1618 DEBUG(0,(__location__
": changereplmetada control called without "\
1619 "a specified replPropertyMetaData attribute or with others\n"));
1620 return LDB_ERR_OPERATIONS_ERROR
;
1622 if (situation
!= REPL_URGENT_ON_UPDATE
) {
1623 DEBUG(0,(__location__
": changereplmetada control can't be called when deleting an object\n"));
1624 return LDB_ERR_OPERATIONS_ERROR
;
1626 omd_value
= ldb_msg_find_ldb_val(msg
, "replPropertyMetaData");
1628 DEBUG(0,(__location__
": replPropertyMetaData was not specified for Object %s\n",
1629 ldb_dn_get_linearized(msg
->dn
)));
1630 return LDB_ERR_OPERATIONS_ERROR
;
1632 ndr_err
= ndr_pull_struct_blob(omd_value
, msg
, &omd
,
1633 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
1634 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1635 DEBUG(0,(__location__
": Failed to parse replPropertyMetaData for %s\n",
1636 ldb_dn_get_linearized(msg
->dn
)));
1637 return LDB_ERR_OPERATIONS_ERROR
;
1640 ret
= dsdb_module_search_dn(module
, msg
, &res
, msg
->dn
, attrs2
,
1641 DSDB_FLAG_NEXT_MODULE
|
1642 DSDB_SEARCH_SHOW_RECYCLED
|
1643 DSDB_SEARCH_SHOW_EXTENDED_DN
|
1644 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
|
1645 DSDB_SEARCH_REVEAL_INTERNALS
, req
);
1647 if (ret
!= LDB_SUCCESS
) {
1651 if (rmd_is_just_resorted
== false) {
1652 *seq_num
= find_max_local_usn(omd
);
1654 db_seq
= ldb_msg_find_attr_as_uint64(res
->msgs
[0], "uSNChanged", 0);
1657 * The test here now allows for a new
1658 * replPropertyMetaData with no change, if was
1659 * just dbcheck re-sorting the values.
1661 if (*seq_num
<= db_seq
) {
1662 DEBUG(0,(__location__
": changereplmetada control provided but max(local_usn)" \
1663 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1664 (long long)*seq_num
, (long long)db_seq
));
1665 return LDB_ERR_OPERATIONS_ERROR
;
1670 /* search for the existing replPropertyMetaDataBlob. We need
1671 * to use REVEAL and ask for DNs in storage format to support
1672 * the check for values being the same in
1673 * replmd_update_rpmd_element()
1675 ret
= dsdb_module_search_dn(module
, msg
, &res
, msg
->dn
, attrs
,
1676 DSDB_FLAG_NEXT_MODULE
|
1677 DSDB_SEARCH_SHOW_RECYCLED
|
1678 DSDB_SEARCH_SHOW_EXTENDED_DN
|
1679 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
|
1680 DSDB_SEARCH_REVEAL_INTERNALS
, req
);
1681 if (ret
!= LDB_SUCCESS
) {
1685 omd_value
= ldb_msg_find_ldb_val(res
->msgs
[0], "replPropertyMetaData");
1687 DEBUG(0,(__location__
": Object %s does not have a replPropertyMetaData attribute\n",
1688 ldb_dn_get_linearized(msg
->dn
)));
1689 return LDB_ERR_OPERATIONS_ERROR
;
1692 ndr_err
= ndr_pull_struct_blob(omd_value
, msg
, &omd
,
1693 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
1694 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1695 DEBUG(0,(__location__
": Failed to parse replPropertyMetaData for %s\n",
1696 ldb_dn_get_linearized(msg
->dn
)));
1697 return LDB_ERR_OPERATIONS_ERROR
;
1700 if (omd
.version
!= 1) {
1701 DEBUG(0,(__location__
": bad version %u in replPropertyMetaData for %s\n",
1702 omd
.version
, ldb_dn_get_linearized(msg
->dn
)));
1703 return LDB_ERR_OPERATIONS_ERROR
;
1706 for (i
=0; i
<msg
->num_elements
;) {
1707 struct ldb_message_element
*el
= &msg
->elements
[i
];
1708 struct ldb_message_element
*old_el
;
1710 old_el
= ldb_msg_find_element(res
->msgs
[0], el
->name
);
1711 ret
= replmd_update_rpmd_element(ldb
, msg
, el
, old_el
,
1712 &omd
, schema
, seq_num
,
1716 if (ret
!= LDB_SUCCESS
) {
1720 if (!*is_urgent
&& (situation
== REPL_URGENT_ON_UPDATE
)) {
1721 *is_urgent
= replmd_check_urgent_attribute(el
);
1724 if (!(el
->flags
& DSDB_FLAG_INTERNAL_FORCE_META_DATA
)) {
1729 el
->flags
&= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA
;
1731 if (el
->num_values
!= 0) {
1736 ldb_msg_remove_element(msg
, el
);
1741 * Assert that we have an objectClass attribute - this is major
1742 * corruption if we don't have this!
1744 objectclass_el
= ldb_msg_find_element(res
->msgs
[0], "objectClass");
1745 if (objectclass_el
!= NULL
) {
1747 * Now check if this objectClass means we need to do urgent replication
1749 if (!*is_urgent
&& replmd_check_urgent_objectclass(objectclass_el
,
1753 } else if (!ldb_request_get_control(req
, DSDB_CONTROL_DBCHECK
)) {
1754 ldb_asprintf_errstring(ldb
, __location__
1755 ": objectClass missing on %s\n",
1756 ldb_dn_get_linearized(msg
->dn
));
1757 return LDB_ERR_OBJECT_CLASS_VIOLATION
;
1761 * replmd_update_rpmd_element has done an update if the
1764 if (*seq_num
!= 0 || rmd_is_just_resorted
== true) {
1765 struct ldb_val
*md_value
;
1766 struct ldb_message_element
*el
;
1768 /*if we are RODC and this is a DRSR update then its ok*/
1769 if (!ldb_request_get_control(req
, DSDB_CONTROL_REPLICATED_UPDATE_OID
)
1770 && !ldb_request_get_control(req
, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA
)) {
1771 unsigned instanceType
;
1773 ret
= samdb_rodc(ldb
, rodc
);
1774 if (ret
!= LDB_SUCCESS
) {
1775 DEBUG(4, (__location__
": unable to tell if we are an RODC\n"));
1777 ldb_set_errstring(ldb
, "RODC modify is forbidden!");
1778 return LDB_ERR_REFERRAL
;
1781 instanceType
= ldb_msg_find_attr_as_uint(res
->msgs
[0], "instanceType", INSTANCE_TYPE_WRITE
);
1782 if (!(instanceType
& INSTANCE_TYPE_WRITE
)) {
1783 return ldb_error(ldb
, LDB_ERR_UNWILLING_TO_PERFORM
,
1784 "cannot change replicated attribute on partial replica");
1788 md_value
= talloc(msg
, struct ldb_val
);
1789 if (md_value
== NULL
) {
1791 return LDB_ERR_OPERATIONS_ERROR
;
1794 ret
= replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb
, &omd
.ctr
.ctr1
, msg
->dn
);
1795 if (ret
!= LDB_SUCCESS
) {
1796 ldb_asprintf_errstring(ldb
, "%s: %s", __func__
, ldb_errstring(ldb
));
1800 ndr_err
= ndr_push_struct_blob(md_value
, msg
, &omd
,
1801 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
1802 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1803 DEBUG(0,(__location__
": Failed to marshall replPropertyMetaData for %s\n",
1804 ldb_dn_get_linearized(msg
->dn
)));
1805 return LDB_ERR_OPERATIONS_ERROR
;
1808 ret
= ldb_msg_add_empty(msg
, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE
, &el
);
1809 if (ret
!= LDB_SUCCESS
) {
1810 DEBUG(0,(__location__
": Failed to add updated replPropertyMetaData %s\n",
1811 ldb_dn_get_linearized(msg
->dn
)));
1816 el
->values
= md_value
;
1822 struct compare_ctx
{
1824 struct ldb_context
*ldb
;
1825 TALLOC_CTX
*mem_ctx
;
1826 const char *ldap_oid
;
1828 const struct GUID
*invocation_id
;
1831 /* When a parsed_dn comes from the database, sometimes it is not really parsed. */
1833 static int really_parse_trusted_dn(TALLOC_CTX
*mem_ctx
, struct ldb_context
*ldb
,
1834 struct parsed_dn
*pdn
, const char *ldap_oid
)
1837 struct dsdb_dn
*dsdb_dn
= dsdb_dn_parse_trusted(mem_ctx
, ldb
, pdn
->v
,
1839 if (dsdb_dn
== NULL
) {
1840 return LDB_ERR_INVALID_DN_SYNTAX
;
1843 status
= dsdb_get_extended_dn_guid(dsdb_dn
->dn
, &pdn
->guid
, "GUID");
1844 if (!NT_STATUS_IS_OK(status
)) {
1845 return LDB_ERR_OPERATIONS_ERROR
;
1847 pdn
->dsdb_dn
= dsdb_dn
;
1852 * We choose, as the sort order, the same order as is used in DRS replication,
1853 * which is the memcmp() order of the NDR GUID, not that obtained from
1856 * This means that sorted links will be in the same order as a new DC would
1859 static int ndr_guid_compare(struct GUID
*guid1
, struct GUID
*guid2
)
1861 uint8_t v1_data
[16];
1862 struct ldb_val v1
= data_blob_const(v1_data
, sizeof(v1_data
));
1863 uint8_t v2_data
[16];
1864 struct ldb_val v2
= data_blob_const(v2_data
, sizeof(v2_data
));
1866 /* This can't fail */
1867 ndr_push_struct_into_fixed_blob(&v1
, guid1
,
1868 (ndr_push_flags_fn_t
)ndr_push_GUID
);
1869 /* This can't fail */
1870 ndr_push_struct_into_fixed_blob(&v2
, guid2
,
1871 (ndr_push_flags_fn_t
)ndr_push_GUID
);
1872 return data_blob_cmp(&v1
, &v2
);
1875 static int parsed_dn_compare(struct parsed_dn
*pdn1
, struct parsed_dn
*pdn2
)
1877 return ndr_guid_compare(&pdn1
->guid
, &pdn2
->guid
);
1880 static int la_guid_compare_with_trusted_dn(struct compare_ctx
*ctx
,
1881 struct parsed_dn
*p
)
1884 * This works like a standard compare function in its return values,
1885 * but has an extra trick to deal with errors: zero is returned and
1886 * ctx->err is set to the ldb error code.
1888 * That is, if (as is expected in most cases) you get a non-zero
1889 * result, you don't need to check for errors.
1891 * We assume the second argument refers to a DN is from the database
1892 * and has a GUID -- but this GUID might not have been parsed out yet.
1894 if (p
->dsdb_dn
== NULL
) {
1895 int ret
= really_parse_trusted_dn(ctx
->mem_ctx
, ctx
->ldb
, p
,
1897 if (ret
!= LDB_SUCCESS
) {
1902 return ndr_guid_compare(ctx
->guid
, &p
->guid
);
1907 static int parsed_dn_find(struct ldb_context
*ldb
, struct parsed_dn
*pdn
,
1910 struct ldb_dn
*target_dn
,
1911 struct parsed_dn
**exact
,
1912 struct parsed_dn
**next
,
1913 const char *ldap_oid
)
1916 struct compare_ctx ctx
;
1923 if (unlikely(GUID_all_zero(guid
))) {
1925 * When updating a link using DRS, we sometimes get a NULL
1926 * GUID when a forward link has been deleted and its GUID has
1927 * for some reason been forgotten. The best we can do is try
1928 * and match by DN via a linear search. Note that this
1929 * probably only happens in the ADD case, in which we only
1930 * allow modification of link if it is already deleted, so
1931 * this seems very close to an elaborate NO-OP, but we are not
1932 * quite prepared to declare it so.
1934 * If the DN is not in our list, we have to add it to the
1935 * beginning of the list, where it would naturally sort.
1937 struct parsed_dn
*p
;
1938 if (target_dn
== NULL
) {
1939 /* We don't know the target DN, so we can't search for DN */
1940 DEBUG(1, ("parsed_dn_find has a NULL GUID for a linked "
1941 "attribute but we don't have a DN to compare "
1943 return LDB_ERR_OPERATIONS_ERROR
;
1948 DEBUG(3, ("parsed_dn_find has a NULL GUID for a link to DN "
1949 "%s; searching through links for it",
1950 ldb_dn_get_linearized(target_dn
)));
1952 for (i
= 0; i
< count
; i
++) {
1955 if (p
->dsdb_dn
== NULL
) {
1956 int ret
= really_parse_trusted_dn(pdn
, ldb
, p
, ldap_oid
);
1957 if (ret
!= LDB_SUCCESS
) {
1958 return LDB_ERR_OPERATIONS_ERROR
;
1962 cmp
= ldb_dn_compare(p
->dsdb_dn
->dn
, target_dn
);
1969 * Here we have a null guid which doesn't match any existing
1970 * link. This is a bit unexpected because null guids occur
1971 * when a forward link has been deleted and we are replicating
1974 * The best thing to do is weep into the logs and add the
1975 * offending link to the beginning of the list which is
1976 * at least the correct sort position.
1978 DEBUG(1, ("parsed_dn_find has been given a NULL GUID for a "
1979 "link to unknown DN %s\n",
1980 ldb_dn_get_linearized(target_dn
)));
1988 ctx
.ldap_oid
= ldap_oid
;
1991 BINARY_ARRAY_SEARCH_GTE(pdn
, count
, &ctx
, la_guid_compare_with_trusted_dn
,
2001 get a series of message element values as an array of DNs and GUIDs
2002 the result is sorted by GUID
2004 static int get_parsed_dns(struct ldb_module
*module
, TALLOC_CTX
*mem_ctx
,
2005 struct ldb_message_element
*el
, struct parsed_dn
**pdn
,
2006 const char *ldap_oid
, struct ldb_request
*parent
)
2009 bool values_are_sorted
= true;
2010 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
2017 (*pdn
) = talloc_array(mem_ctx
, struct parsed_dn
, el
->num_values
);
2019 ldb_module_oom(module
);
2020 return LDB_ERR_OPERATIONS_ERROR
;
2023 for (i
=0; i
<el
->num_values
; i
++) {
2024 struct ldb_val
*v
= &el
->values
[i
];
2027 struct parsed_dn
*p
;
2031 p
->dsdb_dn
= dsdb_dn_parse(*pdn
, ldb
, v
, ldap_oid
);
2032 if (p
->dsdb_dn
== NULL
) {
2033 return LDB_ERR_INVALID_DN_SYNTAX
;
2036 dn
= p
->dsdb_dn
->dn
;
2038 status
= dsdb_get_extended_dn_guid(dn
, &p
->guid
, "GUID");
2039 if (NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
) ||
2040 unlikely(GUID_all_zero(&p
->guid
))) {
2041 /* we got a DN without a GUID - go find the GUID */
2042 int ret
= dsdb_module_guid_by_dn(module
, dn
, &p
->guid
, parent
);
2043 if (ret
!= LDB_SUCCESS
) {
2044 ldb_asprintf_errstring(ldb
, "Unable to find GUID for DN %s\n",
2045 ldb_dn_get_linearized(dn
));
2046 if (ret
== LDB_ERR_NO_SUCH_OBJECT
&&
2047 LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_DELETE
&&
2048 ldb_attr_cmp(el
->name
, "member") == 0) {
2049 return LDB_ERR_UNWILLING_TO_PERFORM
;
2053 ret
= dsdb_set_extended_dn_guid(dn
, &p
->guid
, "GUID");
2054 if (ret
!= LDB_SUCCESS
) {
2057 } else if (!NT_STATUS_IS_OK(status
)) {
2058 return LDB_ERR_OPERATIONS_ERROR
;
2060 if (i
> 0 && values_are_sorted
) {
2061 int cmp
= parsed_dn_compare(p
, &(*pdn
)[i
- 1]);
2063 values_are_sorted
= false;
2066 /* keep a pointer to the original ldb_val */
2069 if (! values_are_sorted
) {
2070 TYPESAFE_QSORT(*pdn
, el
->num_values
, parsed_dn_compare
);
2076 * Get a series of trusted message element values. The result is sorted by
2077 * GUID, even though the GUIDs might not be known. That works because we trust
2078 * the database to give us the elements like that if the
2079 * replmd_private->sorted_links flag is set.
2081 * We also ensure that the links are in the Functional Level 2003
2082 * linked attributes format.
2084 static int get_parsed_dns_trusted(struct ldb_module
*module
,
2085 struct replmd_private
*replmd_private
,
2086 TALLOC_CTX
*mem_ctx
,
2087 struct ldb_message_element
*el
,
2088 struct parsed_dn
**pdn
,
2089 const char *ldap_oid
,
2090 struct ldb_request
*parent
)
2099 if (!replmd_private
->sorted_links
) {
2100 /* We need to sort the list. This is the slow old path we want
2103 ret
= get_parsed_dns(module
, mem_ctx
, el
, pdn
, ldap_oid
,
2105 if (ret
!= LDB_SUCCESS
) {
2109 /* Here we get a list of 'struct parsed_dns' without the parsing */
2110 *pdn
= talloc_zero_array(mem_ctx
, struct parsed_dn
,
2113 ldb_module_oom(module
);
2114 return LDB_ERR_OPERATIONS_ERROR
;
2117 for (i
= 0; i
< el
->num_values
; i
++) {
2118 (*pdn
)[i
].v
= &el
->values
[i
];
2123 * This upgrades links to FL2003 style, and sorts the result
2124 * if that was needed.
2126 * TODO: Add a database feature that asserts we have no FL2000
2127 * style links to avoid this check or add a feature that
2128 * uses a similar check to find sorted/unsorted links
2129 * for an on-the-fly upgrade.
2132 ret
= replmd_check_upgrade_links(ldb_module_get_ctx(module
),
2133 *pdn
, el
->num_values
,
2136 if (ret
!= LDB_SUCCESS
) {
2144 build a new extended DN, including all meta data fields
2146 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2147 RMD_ADDTIME = originating_add_time
2148 RMD_INVOCID = originating_invocation_id
2149 RMD_CHANGETIME = originating_change_time
2150 RMD_ORIGINATING_USN = originating_usn
2151 RMD_LOCAL_USN = local_usn
2152 RMD_VERSION = version
2154 static int replmd_build_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
, struct dsdb_dn
*dsdb_dn
,
2155 const struct GUID
*invocation_id
, uint64_t seq_num
,
2156 uint64_t local_usn
, NTTIME nttime
, uint32_t version
, bool deleted
)
2158 struct ldb_dn
*dn
= dsdb_dn
->dn
;
2159 const char *tstring
, *usn_string
, *flags_string
;
2160 struct ldb_val tval
;
2162 struct ldb_val usnv
, local_usnv
;
2163 struct ldb_val vers
, flagsv
;
2166 const char *dnstring
;
2168 uint32_t rmd_flags
= deleted
?DSDB_RMD_FLAG_DELETED
:0;
2170 tstring
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)nttime
);
2172 return LDB_ERR_OPERATIONS_ERROR
;
2174 tval
= data_blob_string_const(tstring
);
2176 usn_string
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)seq_num
);
2178 return LDB_ERR_OPERATIONS_ERROR
;
2180 usnv
= data_blob_string_const(usn_string
);
2182 usn_string
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)local_usn
);
2184 return LDB_ERR_OPERATIONS_ERROR
;
2186 local_usnv
= data_blob_string_const(usn_string
);
2188 vstring
= talloc_asprintf(mem_ctx
, "%lu", (unsigned long)version
);
2190 return LDB_ERR_OPERATIONS_ERROR
;
2192 vers
= data_blob_string_const(vstring
);
2194 status
= GUID_to_ndr_blob(invocation_id
, dn
, &iid
);
2195 if (!NT_STATUS_IS_OK(status
)) {
2196 return LDB_ERR_OPERATIONS_ERROR
;
2199 flags_string
= talloc_asprintf(mem_ctx
, "%u", rmd_flags
);
2200 if (!flags_string
) {
2201 return LDB_ERR_OPERATIONS_ERROR
;
2203 flagsv
= data_blob_string_const(flags_string
);
2205 ret
= ldb_dn_set_extended_component(dn
, "RMD_FLAGS", &flagsv
);
2206 if (ret
!= LDB_SUCCESS
) return ret
;
2207 ret
= ldb_dn_set_extended_component(dn
, "RMD_ADDTIME", &tval
);
2208 if (ret
!= LDB_SUCCESS
) return ret
;
2209 ret
= ldb_dn_set_extended_component(dn
, "RMD_INVOCID", &iid
);
2210 if (ret
!= LDB_SUCCESS
) return ret
;
2211 ret
= ldb_dn_set_extended_component(dn
, "RMD_CHANGETIME", &tval
);
2212 if (ret
!= LDB_SUCCESS
) return ret
;
2213 ret
= ldb_dn_set_extended_component(dn
, "RMD_LOCAL_USN", &local_usnv
);
2214 if (ret
!= LDB_SUCCESS
) return ret
;
2215 ret
= ldb_dn_set_extended_component(dn
, "RMD_ORIGINATING_USN", &usnv
);
2216 if (ret
!= LDB_SUCCESS
) return ret
;
2217 ret
= ldb_dn_set_extended_component(dn
, "RMD_VERSION", &vers
);
2218 if (ret
!= LDB_SUCCESS
) return ret
;
2220 dnstring
= dsdb_dn_get_extended_linearized(mem_ctx
, dsdb_dn
, 1);
2221 if (dnstring
== NULL
) {
2222 return LDB_ERR_OPERATIONS_ERROR
;
2224 *v
= data_blob_string_const(dnstring
);
2229 static int replmd_update_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
, struct dsdb_dn
*dsdb_dn
,
2230 struct dsdb_dn
*old_dsdb_dn
, const struct GUID
*invocation_id
,
2231 uint64_t seq_num
, uint64_t local_usn
, NTTIME nttime
,
2232 uint32_t version
, bool deleted
);
2235 check if any links need upgrading from w2k format
2237 static int replmd_check_upgrade_links(struct ldb_context
*ldb
,
2238 struct parsed_dn
*dns
, uint32_t count
,
2239 struct ldb_message_element
*el
,
2240 const char *ldap_oid
)
2243 const struct GUID
*invocation_id
= NULL
;
2244 for (i
=0; i
<count
; i
++) {
2248 if (dns
[i
].dsdb_dn
== NULL
) {
2249 ret
= really_parse_trusted_dn(dns
, ldb
, &dns
[i
],
2251 if (ret
!= LDB_SUCCESS
) {
2252 return LDB_ERR_INVALID_DN_SYNTAX
;
2256 status
= dsdb_get_extended_dn_uint32(dns
[i
].dsdb_dn
->dn
,
2257 &version
, "RMD_VERSION");
2258 if (!NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)) {
2260 * We optimistically assume they are all the same; if
2261 * the first one is fixed, they are all fixed.
2263 * If the first one was *not* fixed and we find a
2264 * later one that is, that is an occasion to shout
2270 DEBUG(0, ("Mixed w2k and fixed format "
2271 "linked attributes\n"));
2275 if (invocation_id
== NULL
) {
2276 invocation_id
= samdb_ntds_invocation_id(ldb
);
2277 if (invocation_id
== NULL
) {
2278 return LDB_ERR_OPERATIONS_ERROR
;
2283 /* it's an old one that needs upgrading */
2284 ret
= replmd_update_la_val(el
->values
, dns
[i
].v
,
2285 dns
[i
].dsdb_dn
, dns
[i
].dsdb_dn
,
2286 invocation_id
, 1, 1, 0, 0, false);
2287 if (ret
!= LDB_SUCCESS
) {
2293 * This sort() is critical for the operation of
2294 * get_parsed_dns_trusted() because callers of this function
2295 * expect a sorted list, and FL2000 style links are not
2296 * sorted. In particular, as well as the upgrade case,
2297 * get_parsed_dns_trusted() is called from
2298 * replmd_delete_remove_link() even in FL2000 mode
2300 * We do not normally pay the cost of the qsort() due to the
2301 * early return in the RMD_VERSION found case.
2303 TYPESAFE_QSORT(dns
, count
, parsed_dn_compare
);
2308 update an extended DN, including all meta data fields
2310 see replmd_build_la_val for value names
2312 static int replmd_update_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
, struct dsdb_dn
*dsdb_dn
,
2313 struct dsdb_dn
*old_dsdb_dn
, const struct GUID
*invocation_id
,
2314 uint64_t usn
, uint64_t local_usn
, NTTIME nttime
,
2315 uint32_t version
, bool deleted
)
2317 struct ldb_dn
*dn
= dsdb_dn
->dn
;
2318 const char *tstring
, *usn_string
, *flags_string
;
2319 struct ldb_val tval
;
2321 struct ldb_val usnv
, local_usnv
;
2322 struct ldb_val vers
, flagsv
;
2323 const struct ldb_val
*old_addtime
;
2324 uint32_t old_version
;
2327 const char *dnstring
;
2329 uint32_t rmd_flags
= deleted
?DSDB_RMD_FLAG_DELETED
:0;
2331 tstring
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)nttime
);
2333 return LDB_ERR_OPERATIONS_ERROR
;
2335 tval
= data_blob_string_const(tstring
);
2337 usn_string
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)usn
);
2339 return LDB_ERR_OPERATIONS_ERROR
;
2341 usnv
= data_blob_string_const(usn_string
);
2343 usn_string
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)local_usn
);
2345 return LDB_ERR_OPERATIONS_ERROR
;
2347 local_usnv
= data_blob_string_const(usn_string
);
2349 status
= GUID_to_ndr_blob(invocation_id
, dn
, &iid
);
2350 if (!NT_STATUS_IS_OK(status
)) {
2351 return LDB_ERR_OPERATIONS_ERROR
;
2354 flags_string
= talloc_asprintf(mem_ctx
, "%u", rmd_flags
);
2355 if (!flags_string
) {
2356 return LDB_ERR_OPERATIONS_ERROR
;
2358 flagsv
= data_blob_string_const(flags_string
);
2360 ret
= ldb_dn_set_extended_component(dn
, "RMD_FLAGS", &flagsv
);
2361 if (ret
!= LDB_SUCCESS
) return ret
;
2363 /* get the ADDTIME from the original */
2364 old_addtime
= ldb_dn_get_extended_component(old_dsdb_dn
->dn
, "RMD_ADDTIME");
2365 if (old_addtime
== NULL
) {
2366 old_addtime
= &tval
;
2368 if (dsdb_dn
!= old_dsdb_dn
||
2369 ldb_dn_get_extended_component(dn
, "RMD_ADDTIME") == NULL
) {
2370 ret
= ldb_dn_set_extended_component(dn
, "RMD_ADDTIME", old_addtime
);
2371 if (ret
!= LDB_SUCCESS
) return ret
;
2374 /* use our invocation id */
2375 ret
= ldb_dn_set_extended_component(dn
, "RMD_INVOCID", &iid
);
2376 if (ret
!= LDB_SUCCESS
) return ret
;
2378 /* changetime is the current time */
2379 ret
= ldb_dn_set_extended_component(dn
, "RMD_CHANGETIME", &tval
);
2380 if (ret
!= LDB_SUCCESS
) return ret
;
2382 /* update the USN */
2383 ret
= ldb_dn_set_extended_component(dn
, "RMD_ORIGINATING_USN", &usnv
);
2384 if (ret
!= LDB_SUCCESS
) return ret
;
2386 ret
= ldb_dn_set_extended_component(dn
, "RMD_LOCAL_USN", &local_usnv
);
2387 if (ret
!= LDB_SUCCESS
) return ret
;
2389 /* increase the version by 1 */
2390 status
= dsdb_get_extended_dn_uint32(old_dsdb_dn
->dn
, &old_version
, "RMD_VERSION");
2391 if (NT_STATUS_IS_OK(status
) && old_version
>= version
) {
2392 version
= old_version
+1;
2394 vstring
= talloc_asprintf(dn
, "%lu", (unsigned long)version
);
2395 vers
= data_blob_string_const(vstring
);
2396 ret
= ldb_dn_set_extended_component(dn
, "RMD_VERSION", &vers
);
2397 if (ret
!= LDB_SUCCESS
) return ret
;
2399 dnstring
= dsdb_dn_get_extended_linearized(mem_ctx
, dsdb_dn
, 1);
2400 if (dnstring
== NULL
) {
2401 return LDB_ERR_OPERATIONS_ERROR
;
2403 *v
= data_blob_string_const(dnstring
);
2409 handle adding a linked attribute
2411 static int replmd_modify_la_add(struct ldb_module
*module
,
2412 struct replmd_private
*replmd_private
,
2413 const struct dsdb_schema
*schema
,
2414 struct ldb_message
*msg
,
2415 struct ldb_message_element
*el
,
2416 struct ldb_message_element
*old_el
,
2417 const struct dsdb_attribute
*schema_attr
,
2420 struct GUID
*msg_guid
,
2421 struct ldb_request
*parent
)
2424 struct parsed_dn
*dns
, *old_dns
;
2425 TALLOC_CTX
*tmp_ctx
= talloc_new(msg
);
2427 struct ldb_val
*new_values
= NULL
;
2428 unsigned old_num_values
= old_el
? old_el
->num_values
: 0;
2429 unsigned num_values
= 0;
2430 unsigned max_num_values
;
2431 const struct GUID
*invocation_id
;
2432 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
2434 unix_to_nt_time(&now
, t
);
2436 invocation_id
= samdb_ntds_invocation_id(ldb
);
2437 if (!invocation_id
) {
2438 talloc_free(tmp_ctx
);
2439 return LDB_ERR_OPERATIONS_ERROR
;
2442 /* get the DNs to be added, fully parsed.
2444 * We need full parsing because they came off the wire and we don't
2445 * trust them, besides which we need their details to know where to put
2448 ret
= get_parsed_dns(module
, tmp_ctx
, el
, &dns
,
2449 schema_attr
->syntax
->ldap_oid
, parent
);
2450 if (ret
!= LDB_SUCCESS
) {
2451 talloc_free(tmp_ctx
);
2455 /* get the existing DNs, lazily parsed */
2456 ret
= get_parsed_dns_trusted(module
, replmd_private
,
2457 tmp_ctx
, old_el
, &old_dns
,
2458 schema_attr
->syntax
->ldap_oid
, parent
);
2460 if (ret
!= LDB_SUCCESS
) {
2461 talloc_free(tmp_ctx
);
2465 max_num_values
= old_num_values
+ el
->num_values
;
2466 if (max_num_values
< old_num_values
) {
2467 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2468 "old values: %u, new values: %u, sum: %u",
2469 old_num_values
, el
->num_values
, max_num_values
));
2470 talloc_free(tmp_ctx
);
2471 return LDB_ERR_OPERATIONS_ERROR
;
2474 new_values
= talloc_zero_array(tmp_ctx
, struct ldb_val
, max_num_values
);
2476 if (new_values
== NULL
) {
2477 ldb_module_oom(module
);
2478 talloc_free(tmp_ctx
);
2479 return LDB_ERR_OPERATIONS_ERROR
;
2483 * For each new value, find where it would go in the list. If there is
2484 * a matching GUID there, we update the existing value; otherwise we
2488 for (i
= 0; i
< el
->num_values
; i
++) {
2489 struct parsed_dn
*exact
;
2490 struct parsed_dn
*next
;
2492 int err
= parsed_dn_find(ldb
, old_dns
, old_num_values
,
2496 schema_attr
->syntax
->ldap_oid
);
2497 if (err
!= LDB_SUCCESS
) {
2498 talloc_free(tmp_ctx
);
2502 if (exact
!= NULL
) {
2504 * We are trying to add one that exists, which is only
2505 * allowed if it was previously deleted.
2507 * When we do undelete a link we change it in place.
2508 * It will be copied across into the right spot in due
2512 rmd_flags
= dsdb_dn_rmd_flags(exact
->dsdb_dn
->dn
);
2514 if (!(rmd_flags
& DSDB_RMD_FLAG_DELETED
)) {
2515 struct GUID_txt_buf guid_str
;
2516 ldb_asprintf_errstring(ldb
,
2517 "Attribute %s already "
2518 "exists for target GUID %s",
2520 GUID_buf_string(&exact
->guid
,
2522 talloc_free(tmp_ctx
);
2523 /* error codes for 'member' need to be
2525 if (ldb_attr_cmp(el
->name
, "member") == 0) {
2526 return LDB_ERR_ENTRY_ALREADY_EXISTS
;
2528 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS
;
2532 ret
= replmd_update_la_val(new_values
, exact
->v
,
2535 invocation_id
, seq_num
,
2536 seq_num
, now
, 0, false);
2537 if (ret
!= LDB_SUCCESS
) {
2538 talloc_free(tmp_ctx
);
2542 ret
= replmd_add_backlink(module
, replmd_private
,
2546 if (ret
!= LDB_SUCCESS
) {
2547 talloc_free(tmp_ctx
);
2553 * Here we don't have an exact match.
2555 * If next is NULL, this one goes beyond the end of the
2556 * existing list, so we need to add all of those ones first.
2558 * If next is not NULL, we need to add all the ones before
2562 offset
= old_num_values
;
2564 /* next should have been parsed, but let's make sure */
2565 if (next
->dsdb_dn
== NULL
) {
2566 ret
= really_parse_trusted_dn(tmp_ctx
, ldb
, next
,
2567 schema_attr
->syntax
->ldap_oid
);
2568 if (ret
!= LDB_SUCCESS
) {
2572 offset
= MIN(next
- old_dns
, old_num_values
);
2575 /* put all the old ones before next on the list */
2576 for (; j
< offset
; j
++) {
2577 new_values
[num_values
] = *old_dns
[j
].v
;
2581 ret
= replmd_add_backlink(module
, replmd_private
,
2582 schema
, msg_guid
, &dns
[i
].guid
,
2583 true, schema_attr
, true);
2584 /* Make the new linked attribute ldb_val. */
2585 ret
= replmd_build_la_val(new_values
, &new_values
[num_values
],
2586 dns
[i
].dsdb_dn
, invocation_id
,
2589 if (ret
!= LDB_SUCCESS
) {
2590 talloc_free(tmp_ctx
);
2594 if (ret
!= LDB_SUCCESS
) {
2595 talloc_free(tmp_ctx
);
2599 /* copy the rest of the old ones (if any) */
2600 for (; j
< old_num_values
; j
++) {
2601 new_values
[num_values
] = *old_dns
[j
].v
;
2605 talloc_steal(msg
->elements
, new_values
);
2606 if (old_el
!= NULL
) {
2607 talloc_steal(msg
->elements
, old_el
->values
);
2609 el
->values
= new_values
;
2610 el
->num_values
= num_values
;
2612 talloc_free(tmp_ctx
);
2614 /* we now tell the backend to replace all existing values
2615 with the one we have constructed */
2616 el
->flags
= LDB_FLAG_MOD_REPLACE
;
2623 handle deleting all active linked attributes
2625 static int replmd_modify_la_delete(struct ldb_module
*module
,
2626 struct replmd_private
*replmd_private
,
2627 const struct dsdb_schema
*schema
,
2628 struct ldb_message
*msg
,
2629 struct ldb_message_element
*el
,
2630 struct ldb_message_element
*old_el
,
2631 const struct dsdb_attribute
*schema_attr
,
2634 struct GUID
*msg_guid
,
2635 struct ldb_request
*parent
)
2638 struct parsed_dn
*dns
, *old_dns
;
2639 TALLOC_CTX
*tmp_ctx
= NULL
;
2641 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
2642 struct ldb_control
*vanish_links_ctrl
= NULL
;
2643 bool vanish_links
= false;
2644 unsigned int num_to_delete
= el
->num_values
;
2646 const struct GUID
*invocation_id
;
2649 unix_to_nt_time(&now
, t
);
2651 invocation_id
= samdb_ntds_invocation_id(ldb
);
2652 if (!invocation_id
) {
2653 return LDB_ERR_OPERATIONS_ERROR
;
2656 if (old_el
== NULL
|| old_el
->num_values
== 0) {
2657 /* there is nothing to delete... */
2658 if (num_to_delete
== 0) {
2659 /* and we're deleting nothing, so that's OK */
2662 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
2665 tmp_ctx
= talloc_new(msg
);
2666 if (tmp_ctx
== NULL
) {
2667 return LDB_ERR_OPERATIONS_ERROR
;
2670 ret
= get_parsed_dns(module
, tmp_ctx
, el
, &dns
,
2671 schema_attr
->syntax
->ldap_oid
, parent
);
2672 if (ret
!= LDB_SUCCESS
) {
2673 talloc_free(tmp_ctx
);
2677 ret
= get_parsed_dns_trusted(module
, replmd_private
,
2678 tmp_ctx
, old_el
, &old_dns
,
2679 schema_attr
->syntax
->ldap_oid
, parent
);
2681 if (ret
!= LDB_SUCCESS
) {
2682 talloc_free(tmp_ctx
);
2687 vanish_links_ctrl
= ldb_request_get_control(parent
, DSDB_CONTROL_REPLMD_VANISH_LINKS
);
2688 if (vanish_links_ctrl
) {
2689 vanish_links
= true;
2690 vanish_links_ctrl
->critical
= false;
2694 /* we empty out el->values here to avoid damage if we return early. */
2699 * If vanish links is set, we are actually removing members of
2700 * old_el->values; otherwise we are just marking them deleted.
2702 * There is a special case when no values are given: we remove them
2703 * all. When we have the vanish_links control we just have to remove
2704 * the backlinks and change our element to replace the existing values
2705 * with the empty list.
2708 if (num_to_delete
== 0) {
2709 for (i
= 0; i
< old_el
->num_values
; i
++) {
2710 struct parsed_dn
*p
= &old_dns
[i
];
2711 if (p
->dsdb_dn
== NULL
) {
2712 ret
= really_parse_trusted_dn(tmp_ctx
, ldb
, p
,
2713 schema_attr
->syntax
->ldap_oid
);
2714 if (ret
!= LDB_SUCCESS
) {
2718 ret
= replmd_add_backlink(module
, replmd_private
,
2719 schema
, msg_guid
, &p
->guid
,
2720 false, schema_attr
, true);
2721 if (ret
!= LDB_SUCCESS
) {
2722 talloc_free(tmp_ctx
);
2729 rmd_flags
= dsdb_dn_rmd_flags(p
->dsdb_dn
->dn
);
2730 if (rmd_flags
& DSDB_RMD_FLAG_DELETED
) {
2734 ret
= replmd_update_la_val(old_el
->values
, p
->v
,
2735 p
->dsdb_dn
, p
->dsdb_dn
,
2736 invocation_id
, seq_num
,
2737 seq_num
, now
, 0, true);
2738 if (ret
!= LDB_SUCCESS
) {
2739 talloc_free(tmp_ctx
);
2745 el
->flags
= LDB_FLAG_MOD_REPLACE
;
2746 talloc_free(tmp_ctx
);
2752 for (i
= 0; i
< num_to_delete
; i
++) {
2753 struct parsed_dn
*p
= &dns
[i
];
2754 struct parsed_dn
*exact
= NULL
;
2755 struct parsed_dn
*next
= NULL
;
2756 ret
= parsed_dn_find(ldb
, old_dns
, old_el
->num_values
,
2760 schema_attr
->syntax
->ldap_oid
);
2761 if (ret
!= LDB_SUCCESS
) {
2762 talloc_free(tmp_ctx
);
2765 if (exact
== NULL
) {
2766 struct GUID_txt_buf buf
;
2767 ldb_asprintf_errstring(ldb
, "Attribute %s doesn't "
2768 "exist for target GUID %s",
2770 GUID_buf_string(&p
->guid
, &buf
));
2771 if (ldb_attr_cmp(el
->name
, "member") == 0) {
2772 talloc_free(tmp_ctx
);
2773 return LDB_ERR_UNWILLING_TO_PERFORM
;
2775 talloc_free(tmp_ctx
);
2776 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
2781 if (CHECK_DEBUGLVL(5)) {
2782 rmd_flags
= dsdb_dn_rmd_flags(exact
->dsdb_dn
->dn
);
2783 if ((rmd_flags
& DSDB_RMD_FLAG_DELETED
)) {
2784 struct GUID_txt_buf buf
;
2785 const char *guid_str
= \
2786 GUID_buf_string(&p
->guid
, &buf
);
2787 DEBUG(5, ("Deleting deleted linked "
2788 "attribute %s to %s, because "
2789 "vanish_links control is set\n",
2790 el
->name
, guid_str
));
2794 /* remove the backlink */
2795 ret
= replmd_add_backlink(module
,
2801 if (ret
!= LDB_SUCCESS
) {
2802 talloc_free(tmp_ctx
);
2806 /* We flag the deletion and tidy it up later. */
2811 rmd_flags
= dsdb_dn_rmd_flags(exact
->dsdb_dn
->dn
);
2813 if (rmd_flags
& DSDB_RMD_FLAG_DELETED
) {
2814 struct GUID_txt_buf buf
;
2815 const char *guid_str
= GUID_buf_string(&p
->guid
, &buf
);
2816 ldb_asprintf_errstring(ldb
, "Attribute %s already "
2817 "deleted for target GUID %s",
2818 el
->name
, guid_str
);
2819 if (ldb_attr_cmp(el
->name
, "member") == 0) {
2820 talloc_free(tmp_ctx
);
2821 return LDB_ERR_UNWILLING_TO_PERFORM
;
2823 talloc_free(tmp_ctx
);
2824 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
2828 ret
= replmd_update_la_val(old_el
->values
, exact
->v
,
2829 exact
->dsdb_dn
, exact
->dsdb_dn
,
2830 invocation_id
, seq_num
, seq_num
,
2832 if (ret
!= LDB_SUCCESS
) {
2833 talloc_free(tmp_ctx
);
2836 ret
= replmd_add_backlink(module
, replmd_private
,
2837 schema
, msg_guid
, &p
->guid
,
2838 false, schema_attr
, true);
2839 if (ret
!= LDB_SUCCESS
) {
2840 talloc_free(tmp_ctx
);
2847 for (i
= 0; i
< old_el
->num_values
; i
++) {
2848 if (old_dns
[i
].v
!= NULL
) {
2849 old_el
->values
[j
] = *old_dns
[i
].v
;
2853 old_el
->num_values
= j
;
2856 el
->values
= talloc_steal(msg
->elements
, old_el
->values
);
2857 el
->num_values
= old_el
->num_values
;
2859 talloc_free(tmp_ctx
);
2861 /* we now tell the backend to replace all existing values
2862 with the one we have constructed */
2863 el
->flags
= LDB_FLAG_MOD_REPLACE
;
2869 handle replacing a linked attribute
2871 static int replmd_modify_la_replace(struct ldb_module
*module
,
2872 struct replmd_private
*replmd_private
,
2873 const struct dsdb_schema
*schema
,
2874 struct ldb_message
*msg
,
2875 struct ldb_message_element
*el
,
2876 struct ldb_message_element
*old_el
,
2877 const struct dsdb_attribute
*schema_attr
,
2880 struct GUID
*msg_guid
,
2881 struct ldb_request
*parent
)
2883 unsigned int i
, old_i
, new_i
;
2884 struct parsed_dn
*dns
, *old_dns
;
2885 TALLOC_CTX
*tmp_ctx
= talloc_new(msg
);
2887 const struct GUID
*invocation_id
;
2888 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
2889 struct ldb_val
*new_values
= NULL
;
2890 const char *ldap_oid
= schema_attr
->syntax
->ldap_oid
;
2891 unsigned int old_num_values
;
2892 unsigned int repl_num_values
;
2893 unsigned int max_num_values
;
2896 unix_to_nt_time(&now
, t
);
2898 invocation_id
= samdb_ntds_invocation_id(ldb
);
2899 if (!invocation_id
) {
2900 return LDB_ERR_OPERATIONS_ERROR
;
2904 * The replace operation is unlike the replace and delete cases in that
2905 * we need to look at every existing link to see whether it is being
2906 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
2908 * As we are trying to combine two sorted lists, the algorithm we use
2909 * is akin to the merge phase of a merge sort. We interleave the two
2910 * lists, doing different things depending on which side the current
2913 * There are three main cases, with some sub-cases.
2915 * - a DN is in the old list but not the new one. It needs to be
2916 * marked as deleted (but left in the list).
2917 * - maybe it is already deleted, and we have less to do.
2919 * - a DN is in both lists. The old data gets replaced by the new,
2920 * and the list doesn't grow. The old link may have been marked as
2921 * deleted, in which case we undelete it.
2923 * - a DN is in the new list only. We add it in the right place.
2926 old_num_values
= old_el
? old_el
->num_values
: 0;
2927 repl_num_values
= el
->num_values
;
2928 max_num_values
= old_num_values
+ repl_num_values
;
2930 if (max_num_values
== 0) {
2931 /* There is nothing to do! */
2935 ret
= get_parsed_dns(module
, tmp_ctx
, el
, &dns
, ldap_oid
, parent
);
2936 if (ret
!= LDB_SUCCESS
) {
2937 talloc_free(tmp_ctx
);
2941 ret
= get_parsed_dns(module
, tmp_ctx
, old_el
, &old_dns
,
2943 if (ret
!= LDB_SUCCESS
) {
2944 talloc_free(tmp_ctx
);
2948 ret
= replmd_check_upgrade_links(ldb
, old_dns
, old_num_values
,
2950 if (ret
!= LDB_SUCCESS
) {
2951 talloc_free(tmp_ctx
);
2955 new_values
= talloc_array(tmp_ctx
, struct ldb_val
, max_num_values
);
2956 if (new_values
== NULL
) {
2957 ldb_module_oom(module
);
2958 talloc_free(tmp_ctx
);
2959 return LDB_ERR_OPERATIONS_ERROR
;
2964 for (i
= 0; i
< max_num_values
; i
++) {
2966 struct parsed_dn
*old_p
, *new_p
;
2967 if (old_i
< old_num_values
&& new_i
< repl_num_values
) {
2968 old_p
= &old_dns
[old_i
];
2969 new_p
= &dns
[new_i
];
2970 cmp
= parsed_dn_compare(old_p
, new_p
);
2971 } else if (old_i
< old_num_values
) {
2972 /* the new list is empty, read the old list */
2973 old_p
= &old_dns
[old_i
];
2976 } else if (new_i
< repl_num_values
) {
2977 /* the old list is empty, read new list */
2979 new_p
= &dns
[new_i
];
2987 * An old ones that come before the next replacement
2988 * (if any). We mark it as deleted and add it to the
2991 uint32_t rmd_flags
= dsdb_dn_rmd_flags(old_p
->dsdb_dn
->dn
);
2992 if ((rmd_flags
& DSDB_RMD_FLAG_DELETED
) == 0) {
2993 ret
= replmd_update_la_val(new_values
, old_p
->v
,
2999 if (ret
!= LDB_SUCCESS
) {
3000 talloc_free(tmp_ctx
);
3004 ret
= replmd_add_backlink(module
, replmd_private
,
3006 &old_p
->guid
, false,
3008 if (ret
!= LDB_SUCCESS
) {
3009 talloc_free(tmp_ctx
);
3013 new_values
[i
] = *old_p
->v
;
3015 } else if (cmp
== 0) {
3017 * We are overwriting one. If it was previously
3018 * deleted, we need to add a backlink.
3020 * Note that if any RMD_FLAGs in an extended new DN
3025 ret
= replmd_update_la_val(new_values
, old_p
->v
,
3031 if (ret
!= LDB_SUCCESS
) {
3032 talloc_free(tmp_ctx
);
3036 rmd_flags
= dsdb_dn_rmd_flags(old_p
->dsdb_dn
->dn
);
3037 if ((rmd_flags
& DSDB_RMD_FLAG_DELETED
) != 0) {
3038 ret
= replmd_add_backlink(module
, replmd_private
,
3042 if (ret
!= LDB_SUCCESS
) {
3043 talloc_free(tmp_ctx
);
3048 new_values
[i
] = *old_p
->v
;
3053 * Replacements that don't match an existing one. We
3054 * just add them to the final list.
3056 ret
= replmd_build_la_val(new_values
,
3062 if (ret
!= LDB_SUCCESS
) {
3063 talloc_free(tmp_ctx
);
3066 ret
= replmd_add_backlink(module
, replmd_private
,
3070 if (ret
!= LDB_SUCCESS
) {
3071 talloc_free(tmp_ctx
);
3074 new_values
[i
] = *new_p
->v
;
3078 if (old_el
!= NULL
) {
3079 talloc_steal(msg
->elements
, old_el
->values
);
3081 el
->values
= talloc_steal(msg
->elements
, new_values
);
3083 talloc_free(tmp_ctx
);
3085 el
->flags
= LDB_FLAG_MOD_REPLACE
;
3092 handle linked attributes in modify requests
3094 static int replmd_modify_handle_linked_attribs(struct ldb_module
*module
,
3095 struct replmd_private
*replmd_private
,
3096 struct ldb_message
*msg
,
3097 uint64_t seq_num
, time_t t
,
3098 struct ldb_request
*parent
)
3100 struct ldb_result
*res
;
3103 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
3104 struct ldb_message
*old_msg
;
3106 const struct dsdb_schema
*schema
;
3107 struct GUID old_guid
;
3109 if (dsdb_functional_level(ldb
) == DS_DOMAIN_FUNCTION_2000
) {
3111 * Nothing special is required for modifying or vanishing links
3112 * in fl2000 since they are just strings in a multi-valued
3115 struct ldb_control
*ctrl
= ldb_request_get_control(parent
,
3116 DSDB_CONTROL_REPLMD_VANISH_LINKS
);
3118 ctrl
->critical
= false;
3126 * We should restrict this to the intersection of the list of
3127 * linked attributes in the schema and the list of attributes
3130 * This will help performance a little, as otherwise we have
3131 * to allocate the entire object value-by-value.
3133 ret
= dsdb_module_search_dn(module
, msg
, &res
, msg
->dn
, NULL
,
3134 DSDB_FLAG_NEXT_MODULE
|
3135 DSDB_SEARCH_SHOW_RECYCLED
|
3136 DSDB_SEARCH_REVEAL_INTERNALS
|
3137 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
,
3139 if (ret
!= LDB_SUCCESS
) {
3142 schema
= dsdb_get_schema(ldb
, res
);
3144 return LDB_ERR_OPERATIONS_ERROR
;
3147 old_msg
= res
->msgs
[0];
3149 old_guid
= samdb_result_guid(old_msg
, "objectGUID");
3151 for (i
=0; i
<msg
->num_elements
; i
++) {
3152 struct ldb_message_element
*el
= &msg
->elements
[i
];
3153 struct ldb_message_element
*old_el
, *new_el
;
3154 const struct dsdb_attribute
*schema_attr
3155 = dsdb_attribute_by_lDAPDisplayName(schema
, el
->name
);
3157 ldb_asprintf_errstring(ldb
,
3158 "%s: attribute %s is not a valid attribute in schema",
3159 __FUNCTION__
, el
->name
);
3160 return LDB_ERR_OBJECT_CLASS_VIOLATION
;
3162 if (schema_attr
->linkID
== 0) {
3165 if ((schema_attr
->linkID
& 1) == 1) {
3166 if (parent
&& ldb_request_get_control(parent
, DSDB_CONTROL_DBCHECK
)) {
3169 /* Odd is for the target. Illegal to modify */
3170 ldb_asprintf_errstring(ldb
,
3171 "attribute %s must not be modified directly, it is a linked attribute", el
->name
);
3172 return LDB_ERR_UNWILLING_TO_PERFORM
;
3174 old_el
= ldb_msg_find_element(old_msg
, el
->name
);
3175 switch (el
->flags
& LDB_FLAG_MOD_MASK
) {
3176 case LDB_FLAG_MOD_REPLACE
:
3177 ret
= replmd_modify_la_replace(module
, replmd_private
,
3178 schema
, msg
, el
, old_el
,
3179 schema_attr
, seq_num
, t
,
3182 case LDB_FLAG_MOD_DELETE
:
3183 ret
= replmd_modify_la_delete(module
, replmd_private
,
3184 schema
, msg
, el
, old_el
,
3185 schema_attr
, seq_num
, t
,
3188 case LDB_FLAG_MOD_ADD
:
3189 ret
= replmd_modify_la_add(module
, replmd_private
,
3190 schema
, msg
, el
, old_el
,
3191 schema_attr
, seq_num
, t
,
3195 ldb_asprintf_errstring(ldb
,
3196 "invalid flags 0x%x for %s linked attribute",
3197 el
->flags
, el
->name
);
3198 return LDB_ERR_UNWILLING_TO_PERFORM
;
3200 if (dsdb_check_single_valued_link(schema_attr
, el
) != LDB_SUCCESS
) {
3201 ldb_asprintf_errstring(ldb
,
3202 "Attribute %s is single valued but more than one value has been supplied",
3204 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS
;
3206 el
->flags
|= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK
;
3211 if (ret
!= LDB_SUCCESS
) {
3215 ldb_msg_remove_attr(old_msg
, el
->name
);
3217 ldb_msg_add_empty(old_msg
, el
->name
, 0, &new_el
);
3218 new_el
->num_values
= el
->num_values
;
3219 new_el
->values
= talloc_steal(msg
->elements
, el
->values
);
3221 /* TODO: this relises a bit too heavily on the exact
3222 behaviour of ldb_msg_find_element and
3223 ldb_msg_remove_element */
3224 old_el
= ldb_msg_find_element(msg
, el
->name
);
3226 ldb_msg_remove_element(msg
, old_el
);
3237 static int replmd_modify(struct ldb_module
*module
, struct ldb_request
*req
)
3239 struct ldb_context
*ldb
;
3240 struct replmd_replicated_request
*ac
;
3241 struct ldb_request
*down_req
;
3242 struct ldb_message
*msg
;
3243 time_t t
= time(NULL
);
3245 bool is_urgent
= false, rodc
= false;
3246 bool is_schema_nc
= false;
3247 unsigned int functional_level
;
3248 const struct ldb_message_element
*guid_el
= NULL
;
3249 struct ldb_control
*sd_propagation_control
;
3250 struct replmd_private
*replmd_private
=
3251 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
3253 /* do not manipulate our control entries */
3254 if (ldb_dn_is_special(req
->op
.mod
.message
->dn
)) {
3255 return ldb_next_request(module
, req
);
3258 sd_propagation_control
= ldb_request_get_control(req
,
3259 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID
);
3260 if (sd_propagation_control
!= NULL
) {
3261 if (req
->op
.mod
.message
->num_elements
!= 1) {
3262 return ldb_module_operr(module
);
3264 ret
= strcmp(req
->op
.mod
.message
->elements
[0].name
,
3265 "nTSecurityDescriptor");
3267 return ldb_module_operr(module
);
3270 return ldb_next_request(module
, req
);
3273 ldb
= ldb_module_get_ctx(module
);
3275 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_modify\n");
3277 guid_el
= ldb_msg_find_element(req
->op
.mod
.message
, "objectGUID");
3278 if (guid_el
!= NULL
) {
3279 ldb_set_errstring(ldb
,
3280 "replmd_modify: it's not allowed to change the objectGUID!");
3281 return LDB_ERR_CONSTRAINT_VIOLATION
;
3284 ac
= replmd_ctx_init(module
, req
);
3286 return ldb_module_oom(module
);
3289 functional_level
= dsdb_functional_level(ldb
);
3291 /* we have to copy the message as the caller might have it as a const */
3292 msg
= ldb_msg_copy_shallow(ac
, req
->op
.mod
.message
);
3296 return LDB_ERR_OPERATIONS_ERROR
;
3299 ldb_msg_remove_attr(msg
, "whenChanged");
3300 ldb_msg_remove_attr(msg
, "uSNChanged");
3302 is_schema_nc
= ldb_dn_compare_base(replmd_private
->schema_dn
, msg
->dn
) == 0;
3304 ret
= replmd_update_rpmd(module
, ac
->schema
, req
, NULL
,
3305 msg
, &ac
->seq_num
, t
, is_schema_nc
,
3307 if (rodc
&& (ret
== LDB_ERR_REFERRAL
)) {
3308 struct loadparm_context
*lp_ctx
;
3311 lp_ctx
= talloc_get_type(ldb_get_opaque(ldb
, "loadparm"),
3312 struct loadparm_context
);
3314 referral
= talloc_asprintf(req
,
3316 lpcfg_dnsdomain(lp_ctx
),
3317 ldb_dn_get_linearized(msg
->dn
));
3318 ret
= ldb_module_send_referral(req
, referral
);
3323 if (ret
!= LDB_SUCCESS
) {
3328 ret
= replmd_modify_handle_linked_attribs(module
, replmd_private
,
3329 msg
, ac
->seq_num
, t
, req
);
3330 if (ret
!= LDB_SUCCESS
) {
3336 * - replace the old object with the newly constructed one
3339 ac
->is_urgent
= is_urgent
;
3341 ret
= ldb_build_mod_req(&down_req
, ldb
, ac
,
3344 ac
, replmd_op_callback
,
3346 LDB_REQ_SET_LOCATION(down_req
);
3347 if (ret
!= LDB_SUCCESS
) {
3352 /* current partition control is needed by "replmd_op_callback" */
3353 if (ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
) == NULL
) {
3354 ret
= ldb_request_add_control(down_req
,
3355 DSDB_CONTROL_CURRENT_PARTITION_OID
,
3357 if (ret
!= LDB_SUCCESS
) {
3363 /* If we are in functional level 2000, then
3364 * replmd_modify_handle_linked_attribs will have done
3366 if (functional_level
== DS_DOMAIN_FUNCTION_2000
) {
3367 ret
= ldb_request_add_control(down_req
, DSDB_CONTROL_APPLY_LINKS
, false, NULL
);
3368 if (ret
!= LDB_SUCCESS
) {
3374 talloc_steal(down_req
, msg
);
3376 /* we only change whenChanged and uSNChanged if the seq_num
3378 if (ac
->seq_num
!= 0) {
3379 ret
= add_time_element(msg
, "whenChanged", t
);
3380 if (ret
!= LDB_SUCCESS
) {
3386 ret
= add_uint64_element(ldb
, msg
, "uSNChanged", ac
->seq_num
);
3387 if (ret
!= LDB_SUCCESS
) {
3394 /* go on with the call chain */
3395 return ldb_next_request(module
, down_req
);
3398 static int replmd_rename_callback(struct ldb_request
*req
, struct ldb_reply
*ares
);
3401 handle a rename request
3403 On a rename we need to do an extra ldb_modify which sets the
3404 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3406 static int replmd_rename(struct ldb_module
*module
, struct ldb_request
*req
)
3408 struct ldb_context
*ldb
;
3409 struct replmd_replicated_request
*ac
;
3411 struct ldb_request
*down_req
;
3413 /* do not manipulate our control entries */
3414 if (ldb_dn_is_special(req
->op
.mod
.message
->dn
)) {
3415 return ldb_next_request(module
, req
);
3418 ldb
= ldb_module_get_ctx(module
);
3420 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_rename\n");
3422 ac
= replmd_ctx_init(module
, req
);
3424 return ldb_module_oom(module
);
3427 ret
= ldb_build_rename_req(&down_req
, ldb
, ac
,
3428 ac
->req
->op
.rename
.olddn
,
3429 ac
->req
->op
.rename
.newdn
,
3431 ac
, replmd_rename_callback
,
3433 LDB_REQ_SET_LOCATION(down_req
);
3434 if (ret
!= LDB_SUCCESS
) {
3439 /* go on with the call chain */
3440 return ldb_next_request(module
, down_req
);
3443 /* After the rename is compleated, update the whenchanged etc */
3444 static int replmd_rename_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
3446 struct ldb_context
*ldb
;
3447 struct ldb_request
*down_req
;
3448 struct ldb_message
*msg
;
3449 const struct dsdb_attribute
*rdn_attr
;
3450 const char *rdn_name
;
3451 const struct ldb_val
*rdn_val
;
3452 const char *attrs
[5] = { NULL
, };
3453 time_t t
= time(NULL
);
3455 bool is_urgent
= false, rodc
= false;
3457 struct replmd_replicated_request
*ac
=
3458 talloc_get_type(req
->context
, struct replmd_replicated_request
);
3459 struct replmd_private
*replmd_private
=
3460 talloc_get_type(ldb_module_get_private(ac
->module
),
3461 struct replmd_private
);
3463 ldb
= ldb_module_get_ctx(ac
->module
);
3465 if (ares
->error
!= LDB_SUCCESS
) {
3466 return ldb_module_done(ac
->req
, ares
->controls
,
3467 ares
->response
, ares
->error
);
3470 if (ares
->type
!= LDB_REPLY_DONE
) {
3471 ldb_set_errstring(ldb
,
3472 "invalid ldb_reply_type in callback");
3474 return ldb_module_done(ac
->req
, NULL
, NULL
,
3475 LDB_ERR_OPERATIONS_ERROR
);
3479 * - replace the old object with the newly constructed one
3482 msg
= ldb_msg_new(ac
);
3485 return LDB_ERR_OPERATIONS_ERROR
;
3488 msg
->dn
= ac
->req
->op
.rename
.newdn
;
3490 is_schema_nc
= ldb_dn_compare_base(replmd_private
->schema_dn
, msg
->dn
) == 0;
3492 rdn_name
= ldb_dn_get_rdn_name(msg
->dn
);
3493 if (rdn_name
== NULL
) {
3495 return ldb_module_done(ac
->req
, NULL
, NULL
,
3499 /* normalize the rdn attribute name */
3500 rdn_attr
= dsdb_attribute_by_lDAPDisplayName(ac
->schema
, rdn_name
);
3501 if (rdn_attr
== NULL
) {
3503 return ldb_module_done(ac
->req
, NULL
, NULL
,
3506 rdn_name
= rdn_attr
->lDAPDisplayName
;
3508 rdn_val
= ldb_dn_get_rdn_val(msg
->dn
);
3509 if (rdn_val
== NULL
) {
3511 return ldb_module_done(ac
->req
, NULL
, NULL
,
3515 if (ldb_msg_add_empty(msg
, rdn_name
, LDB_FLAG_MOD_REPLACE
, NULL
) != 0) {
3517 return ldb_module_done(ac
->req
, NULL
, NULL
,
3520 if (ldb_msg_add_value(msg
, rdn_name
, rdn_val
, NULL
) != 0) {
3522 return ldb_module_done(ac
->req
, NULL
, NULL
,
3525 if (ldb_msg_add_empty(msg
, "name", LDB_FLAG_MOD_REPLACE
, NULL
) != 0) {
3527 return ldb_module_done(ac
->req
, NULL
, NULL
,
3530 if (ldb_msg_add_value(msg
, "name", rdn_val
, NULL
) != 0) {
3532 return ldb_module_done(ac
->req
, NULL
, NULL
,
3537 * here we let replmd_update_rpmd() only search for
3538 * the existing "replPropertyMetaData" and rdn_name attributes.
3540 * We do not want the existing "name" attribute as
3541 * the "name" attribute needs to get the version
3542 * updated on rename even if the rdn value hasn't changed.
3544 * This is the diff of the meta data, for a moved user
3545 * on a w2k8r2 server:
3548 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3549 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3550 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3551 * version : 0x00000001 (1)
3552 * reserved : 0x00000000 (0)
3553 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3554 * local_usn : 0x00000000000037a5 (14245)
3555 * array: struct replPropertyMetaData1
3556 * attid : DRSUAPI_ATTID_name (0x90001)
3557 * - version : 0x00000001 (1)
3558 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3559 * + version : 0x00000002 (2)
3560 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3561 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3562 * - originating_usn : 0x00000000000037a5 (14245)
3563 * - local_usn : 0x00000000000037a5 (14245)
3564 * + originating_usn : 0x0000000000003834 (14388)
3565 * + local_usn : 0x0000000000003834 (14388)
3566 * array: struct replPropertyMetaData1
3567 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3568 * version : 0x00000004 (4)
3570 attrs
[0] = "replPropertyMetaData";
3571 attrs
[1] = "objectClass";
3572 attrs
[2] = "instanceType";
3573 attrs
[3] = rdn_name
;
3576 ret
= replmd_update_rpmd(ac
->module
, ac
->schema
, req
, attrs
,
3577 msg
, &ac
->seq_num
, t
,
3578 is_schema_nc
, &is_urgent
, &rodc
);
3579 if (rodc
&& (ret
== LDB_ERR_REFERRAL
)) {
3580 struct ldb_dn
*olddn
= ac
->req
->op
.rename
.olddn
;
3581 struct loadparm_context
*lp_ctx
;
3584 lp_ctx
= talloc_get_type(ldb_get_opaque(ldb
, "loadparm"),
3585 struct loadparm_context
);
3587 referral
= talloc_asprintf(req
,
3589 lpcfg_dnsdomain(lp_ctx
),
3590 ldb_dn_get_linearized(olddn
));
3591 ret
= ldb_module_send_referral(req
, referral
);
3593 return ldb_module_done(req
, NULL
, NULL
, ret
);
3596 if (ret
!= LDB_SUCCESS
) {
3598 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
3601 if (ac
->seq_num
== 0) {
3603 return ldb_module_done(ac
->req
, NULL
, NULL
,
3605 "internal error seq_num == 0"));
3607 ac
->is_urgent
= is_urgent
;
3609 ret
= ldb_build_mod_req(&down_req
, ldb
, ac
,
3612 ac
, replmd_op_callback
,
3614 LDB_REQ_SET_LOCATION(down_req
);
3615 if (ret
!= LDB_SUCCESS
) {
3620 /* current partition control is needed by "replmd_op_callback" */
3621 if (ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
) == NULL
) {
3622 ret
= ldb_request_add_control(down_req
,
3623 DSDB_CONTROL_CURRENT_PARTITION_OID
,
3625 if (ret
!= LDB_SUCCESS
) {
3631 talloc_steal(down_req
, msg
);
3633 ret
= add_time_element(msg
, "whenChanged", t
);
3634 if (ret
!= LDB_SUCCESS
) {
3640 ret
= add_uint64_element(ldb
, msg
, "uSNChanged", ac
->seq_num
);
3641 if (ret
!= LDB_SUCCESS
) {
3647 /* go on with the call chain - do the modify after the rename */
3648 return ldb_next_request(ac
->module
, down_req
);
3652 * remove links from objects that point at this object when an object
3653 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3654 * RemoveObj which states that link removal due to the object being
3655 * deleted is NOT an originating update - they just go away!
3658 static int replmd_delete_remove_link(struct ldb_module
*module
,
3659 const struct dsdb_schema
*schema
,
3660 struct replmd_private
*replmd_private
,
3663 struct ldb_message_element
*el
,
3664 const struct dsdb_attribute
*sa
,
3665 struct ldb_request
*parent
)
3668 TALLOC_CTX
*tmp_ctx
= talloc_new(module
);
3669 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
3671 for (i
=0; i
<el
->num_values
; i
++) {
3672 struct dsdb_dn
*dsdb_dn
;
3674 struct ldb_message
*msg
;
3675 const struct dsdb_attribute
*target_attr
;
3676 struct ldb_message_element
*el2
;
3678 struct ldb_val dn_val
;
3679 uint32_t dsdb_flags
= 0;
3680 const char *attrs
[] = { NULL
, NULL
};
3681 struct ldb_result
*link_res
;
3682 struct ldb_message
*link_msg
;
3683 struct ldb_message_element
*link_el
;
3684 struct parsed_dn
*link_dns
;
3685 struct parsed_dn
*p
= NULL
, *unused
= NULL
;
3687 if (dsdb_dn_is_deleted_val(&el
->values
[i
])) {
3691 dsdb_dn
= dsdb_dn_parse(tmp_ctx
, ldb
, &el
->values
[i
], sa
->syntax
->ldap_oid
);
3693 talloc_free(tmp_ctx
);
3694 return LDB_ERR_OPERATIONS_ERROR
;
3697 /* remove the link */
3698 msg
= ldb_msg_new(tmp_ctx
);
3700 ldb_module_oom(module
);
3701 talloc_free(tmp_ctx
);
3702 return LDB_ERR_OPERATIONS_ERROR
;
3706 msg
->dn
= dsdb_dn
->dn
;
3708 target_attr
= dsdb_attribute_by_linkID(schema
, sa
->linkID
^ 1);
3709 if (target_attr
== NULL
) {
3712 attrs
[0] = target_attr
->lDAPDisplayName
;
3714 ret
= ldb_msg_add_empty(msg
, target_attr
->lDAPDisplayName
,
3715 LDB_FLAG_MOD_DELETE
, &el2
);
3716 if (ret
!= LDB_SUCCESS
) {
3717 ldb_module_oom(module
);
3718 talloc_free(tmp_ctx
);
3719 return LDB_ERR_OPERATIONS_ERROR
;
3722 ret
= dsdb_module_search_dn(module
, tmp_ctx
, &link_res
,
3724 DSDB_FLAG_NEXT_MODULE
|
3725 DSDB_SEARCH_SHOW_EXTENDED_DN
,
3728 if (ret
!= LDB_SUCCESS
) {
3729 talloc_free(tmp_ctx
);
3733 link_msg
= link_res
->msgs
[0];
3734 link_el
= ldb_msg_find_element(link_msg
,
3735 target_attr
->lDAPDisplayName
);
3736 if (link_el
== NULL
) {
3737 talloc_free(tmp_ctx
);
3738 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
3742 * This call 'upgrades' the links in link_dns, but we
3743 * do not commit the result back into the database, so
3744 * this is safe to call in FL2000 or on databases that
3745 * have been run at that level in the past.
3747 ret
= get_parsed_dns_trusted(module
, replmd_private
, tmp_ctx
,
3749 target_attr
->syntax
->ldap_oid
, parent
);
3750 if (ret
!= LDB_SUCCESS
) {
3751 talloc_free(tmp_ctx
);
3755 ret
= parsed_dn_find(ldb
, link_dns
, link_el
->num_values
,
3756 guid
, dn
, &p
, &unused
,
3757 target_attr
->syntax
->ldap_oid
);
3758 if (ret
!= LDB_SUCCESS
) {
3759 talloc_free(tmp_ctx
);
3764 ldb_asprintf_errstring(ldb_module_get_ctx(module
),
3765 "Failed to find forward link on %s "
3766 "as %s to remove backlink %s on %s",
3767 ldb_dn_get_linearized(msg
->dn
),
3768 target_attr
->lDAPDisplayName
,
3769 sa
->lDAPDisplayName
,
3770 ldb_dn_get_linearized(dn
));
3771 talloc_free(tmp_ctx
);
3772 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
3776 /* This needs to get the Binary DN, by first searching */
3777 dn_str
= dsdb_dn_get_linearized(tmp_ctx
,
3779 dn_val
= data_blob_string_const(dn_str
);
3780 el2
->values
= &dn_val
;
3781 el2
->num_values
= 1;
3784 * Ensure that we tell the modification to vanish any linked
3785 * attributes (not simply mark them as isDeleted = TRUE)
3787 dsdb_flags
|= DSDB_REPLMD_VANISH_LINKS
;
3789 ret
= dsdb_module_modify(module
, msg
, dsdb_flags
|DSDB_FLAG_OWN_MODULE
, parent
);
3790 if (ret
!= LDB_SUCCESS
) {
3791 talloc_free(tmp_ctx
);
3795 talloc_free(tmp_ctx
);
3801 handle update of replication meta data for deletion of objects
3803 This also handles the mapping of delete to a rename operation
3804 to allow deletes to be replicated.
3806 It also handles the incoming deleted objects, to ensure they are
3807 fully deleted here. In that case re_delete is true, and we do not
3808 use this as a signal to change the deleted state, just reinforce it.
3811 static int replmd_delete_internals(struct ldb_module
*module
, struct ldb_request
*req
, bool re_delete
)
3813 int ret
= LDB_ERR_OTHER
;
3814 bool retb
, disallow_move_on_delete
;
3815 struct ldb_dn
*old_dn
, *new_dn
;
3816 const char *rdn_name
;
3817 const struct ldb_val
*rdn_value
, *new_rdn_value
;
3819 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
3820 const struct dsdb_schema
*schema
;
3821 struct ldb_message
*msg
, *old_msg
;
3822 struct ldb_message_element
*el
;
3823 TALLOC_CTX
*tmp_ctx
;
3824 struct ldb_result
*res
, *parent_res
;
3825 static const char * const preserved_attrs
[] = {
3826 /* yes, this really is a hard coded list. See MS-ADTS
3827 section 3.1.1.5.5.1.1 */
3830 "dNReferenceUpdate",
3841 "msDS-LastKnownRDN",
3847 "distinguishedName",
3851 "proxiedObjectName",
3853 "nTSecurityDescriptor",
3854 "replPropertyMetaData",
3856 "securityIdentifier",
3864 "userAccountControl",
3871 static const char * const all_attrs
[] = {
3872 DSDB_SECRET_ATTRIBUTES
,
3876 unsigned int i
, el_count
= 0;
3877 uint32_t dsdb_flags
= 0;
3878 struct replmd_private
*replmd_private
;
3879 enum deletion_state deletion_state
, next_deletion_state
;
3881 if (ldb_dn_is_special(req
->op
.del
.dn
)) {
3882 return ldb_next_request(module
, req
);
3886 * We have to allow dbcheck to remove an object that
3887 * is beyond repair, and to do so totally. This could
3888 * mean we we can get a partial object from the other
3889 * DC, causing havoc, so dbcheck suggests
3890 * re-replication first. dbcheck sets both DBCHECK
3891 * and RELAX in this situation.
3893 if (ldb_request_get_control(req
, LDB_CONTROL_RELAX_OID
)
3894 && ldb_request_get_control(req
, DSDB_CONTROL_DBCHECK
)) {
3895 /* really, really remove it */
3896 return ldb_next_request(module
, req
);
3899 tmp_ctx
= talloc_new(ldb
);
3902 return LDB_ERR_OPERATIONS_ERROR
;
3905 schema
= dsdb_get_schema(ldb
, tmp_ctx
);
3907 talloc_free(tmp_ctx
);
3908 return LDB_ERR_OPERATIONS_ERROR
;
3911 old_dn
= ldb_dn_copy(tmp_ctx
, req
->op
.del
.dn
);
3913 /* we need the complete msg off disk, so we can work out which
3914 attributes need to be removed */
3915 ret
= dsdb_module_search_dn(module
, tmp_ctx
, &res
, old_dn
, all_attrs
,
3916 DSDB_FLAG_NEXT_MODULE
|
3917 DSDB_SEARCH_SHOW_RECYCLED
|
3918 DSDB_SEARCH_REVEAL_INTERNALS
|
3919 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
, req
);
3920 if (ret
!= LDB_SUCCESS
) {
3921 ldb_asprintf_errstring(ldb_module_get_ctx(module
),
3922 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
3923 re_delete
? "re-delete" : "delete",
3924 ldb_dn_get_linearized(old_dn
),
3925 ldb_errstring(ldb_module_get_ctx(module
)));
3926 talloc_free(tmp_ctx
);
3929 old_msg
= res
->msgs
[0];
3931 replmd_deletion_state(module
, old_msg
,
3933 &next_deletion_state
);
3935 /* This supports us noticing an incoming isDeleted and acting on it */
3937 SMB_ASSERT(deletion_state
> OBJECT_NOT_DELETED
);
3938 next_deletion_state
= deletion_state
;
3941 if (next_deletion_state
== OBJECT_REMOVED
) {
3943 * We have to prevent objects being deleted, even if
3944 * the administrator really wants them gone, as
3945 * without the tombstone, we can get a partial object
3946 * from the other DC, causing havoc.
3948 * The only other valid case is when the 180 day
3949 * timeout has expired, when relax is specified.
3951 if (ldb_request_get_control(req
, LDB_CONTROL_RELAX_OID
)) {
3952 /* it is already deleted - really remove it this time */
3953 talloc_free(tmp_ctx
);
3954 return ldb_next_request(module
, req
);
3957 ldb_asprintf_errstring(ldb
, "Refusing to delete tombstone object %s. "
3958 "This check is to prevent corruption of the replicated state.",
3959 ldb_dn_get_linearized(old_msg
->dn
));
3960 return LDB_ERR_UNWILLING_TO_PERFORM
;
3963 rdn_name
= ldb_dn_get_rdn_name(old_dn
);
3964 rdn_value
= ldb_dn_get_rdn_val(old_dn
);
3965 if ((rdn_name
== NULL
) || (rdn_value
== NULL
)) {
3966 talloc_free(tmp_ctx
);
3967 return ldb_operr(ldb
);
3970 msg
= ldb_msg_new(tmp_ctx
);
3972 ldb_module_oom(module
);
3973 talloc_free(tmp_ctx
);
3974 return LDB_ERR_OPERATIONS_ERROR
;
3979 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
3980 disallow_move_on_delete
=
3981 (ldb_msg_find_attr_as_int(old_msg
, "systemFlags", 0)
3982 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE
);
3984 /* work out where we will be renaming this object to */
3985 if (!disallow_move_on_delete
) {
3986 struct ldb_dn
*deleted_objects_dn
;
3987 ret
= dsdb_get_deleted_objects_dn(ldb
, tmp_ctx
, old_dn
,
3988 &deleted_objects_dn
);
3991 * We should not move objects if we can't find the
3992 * deleted objects DN. Not moving (or otherwise
3993 * harming) the Deleted Objects DN itself is handled
3996 if (re_delete
&& (ret
!= LDB_SUCCESS
)) {
3997 new_dn
= ldb_dn_get_parent(tmp_ctx
, old_dn
);
3998 if (new_dn
== NULL
) {
3999 ldb_module_oom(module
);
4000 talloc_free(tmp_ctx
);
4001 return LDB_ERR_OPERATIONS_ERROR
;
4003 } else if (ret
!= LDB_SUCCESS
) {
4004 /* this is probably an attempted delete on a partition
4005 * that doesn't allow delete operations, such as the
4006 * schema partition */
4007 ldb_asprintf_errstring(ldb
, "No Deleted Objects container for DN %s",
4008 ldb_dn_get_linearized(old_dn
));
4009 talloc_free(tmp_ctx
);
4010 return LDB_ERR_UNWILLING_TO_PERFORM
;
4012 new_dn
= deleted_objects_dn
;
4015 new_dn
= ldb_dn_get_parent(tmp_ctx
, old_dn
);
4016 if (new_dn
== NULL
) {
4017 ldb_module_oom(module
);
4018 talloc_free(tmp_ctx
);
4019 return LDB_ERR_OPERATIONS_ERROR
;
4023 /* get the objects GUID from the search we just did */
4024 guid
= samdb_result_guid(old_msg
, "objectGUID");
4026 if (deletion_state
== OBJECT_NOT_DELETED
) {
4027 /* Add a formatted child */
4028 retb
= ldb_dn_add_child_fmt(new_dn
, "%s=%s\\0ADEL:%s",
4030 ldb_dn_escape_value(tmp_ctx
, *rdn_value
),
4031 GUID_string(tmp_ctx
, &guid
));
4033 ldb_asprintf_errstring(ldb
, __location__
4034 ": Unable to add a formatted child to dn: %s",
4035 ldb_dn_get_linearized(new_dn
));
4036 talloc_free(tmp_ctx
);
4037 return LDB_ERR_OPERATIONS_ERROR
;
4040 ret
= ldb_msg_add_string(msg
, "isDeleted", "TRUE");
4041 if (ret
!= LDB_SUCCESS
) {
4042 ldb_asprintf_errstring(ldb
, __location__
4043 ": Failed to add isDeleted string to the msg");
4044 talloc_free(tmp_ctx
);
4047 msg
->elements
[el_count
++].flags
= LDB_FLAG_MOD_REPLACE
;
4050 * No matter what has happened with other renames etc, try again to
4051 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4054 struct ldb_dn
*rdn
= ldb_dn_copy(tmp_ctx
, old_dn
);
4055 retb
= ldb_dn_remove_base_components(rdn
, ldb_dn_get_comp_num(rdn
) - 1);
4057 ldb_asprintf_errstring(ldb
, __location__
4058 ": Unable to add a prepare rdn of %s",
4059 ldb_dn_get_linearized(rdn
));
4060 talloc_free(tmp_ctx
);
4061 return LDB_ERR_OPERATIONS_ERROR
;
4063 SMB_ASSERT(ldb_dn_get_comp_num(rdn
) == 1);
4065 retb
= ldb_dn_add_child(new_dn
, rdn
);
4067 ldb_asprintf_errstring(ldb
, __location__
4068 ": Unable to add rdn %s to base dn: %s",
4069 ldb_dn_get_linearized(rdn
),
4070 ldb_dn_get_linearized(new_dn
));
4071 talloc_free(tmp_ctx
);
4072 return LDB_ERR_OPERATIONS_ERROR
;
4077 now we need to modify the object in the following ways:
4079 - add isDeleted=TRUE
4080 - update rDN and name, with new rDN
4081 - remove linked attributes
4082 - remove objectCategory and sAMAccountType
4083 - remove attribs not on the preserved list
4084 - preserved if in above list, or is rDN
4085 - remove all linked attribs from this object
4086 - remove all links from other objects to this object
4087 - add lastKnownParent
4088 - update replPropertyMetaData?
4090 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4093 if (deletion_state
== OBJECT_NOT_DELETED
) {
4094 struct ldb_dn
*parent_dn
= ldb_dn_get_parent(tmp_ctx
, old_dn
);
4095 char *parent_dn_str
= NULL
;
4097 /* we need the storage form of the parent GUID */
4098 ret
= dsdb_module_search_dn(module
, tmp_ctx
, &parent_res
,
4100 DSDB_FLAG_NEXT_MODULE
|
4101 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
|
4102 DSDB_SEARCH_REVEAL_INTERNALS
|
4103 DSDB_SEARCH_SHOW_RECYCLED
, req
);
4104 if (ret
!= LDB_SUCCESS
) {
4105 ldb_asprintf_errstring(ldb_module_get_ctx(module
),
4106 "repmd_delete: Failed to %s %s, "
4107 "because we failed to find it's parent (%s): %s",
4108 re_delete
? "re-delete" : "delete",
4109 ldb_dn_get_linearized(old_dn
),
4110 ldb_dn_get_linearized(parent_dn
),
4111 ldb_errstring(ldb_module_get_ctx(module
)));
4112 talloc_free(tmp_ctx
);
4117 * Now we can use the DB version,
4118 * it will have the extended DN info in it
4120 parent_dn
= parent_res
->msgs
[0]->dn
;
4121 parent_dn_str
= ldb_dn_get_extended_linearized(tmp_ctx
,
4124 if (parent_dn_str
== NULL
) {
4125 talloc_free(tmp_ctx
);
4126 return ldb_module_oom(module
);
4129 ret
= ldb_msg_add_steal_string(msg
, "lastKnownParent",
4131 if (ret
!= LDB_SUCCESS
) {
4132 ldb_asprintf_errstring(ldb
, __location__
4133 ": Failed to add lastKnownParent "
4134 "string when deleting %s",
4135 ldb_dn_get_linearized(old_dn
));
4136 talloc_free(tmp_ctx
);
4139 msg
->elements
[el_count
++].flags
= LDB_FLAG_MOD_REPLACE
;
4141 if (next_deletion_state
== OBJECT_DELETED
) {
4142 ret
= ldb_msg_add_value(msg
, "msDS-LastKnownRDN", rdn_value
, NULL
);
4143 if (ret
!= LDB_SUCCESS
) {
4144 ldb_asprintf_errstring(ldb
, __location__
4145 ": Failed to add msDS-LastKnownRDN "
4146 "string when deleting %s",
4147 ldb_dn_get_linearized(old_dn
));
4148 talloc_free(tmp_ctx
);
4151 msg
->elements
[el_count
++].flags
= LDB_FLAG_MOD_ADD
;
4155 switch (next_deletion_state
) {
4157 case OBJECT_RECYCLED
:
4158 case OBJECT_TOMBSTONE
:
4161 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4162 * describes what must be removed from a tombstone
4165 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4166 * describes what must be removed from a recycled
4172 * we also mark it as recycled, meaning this object can't be
4173 * recovered (we are stripping its attributes).
4174 * This is done only if we have this schema object of course ...
4175 * This behavior is identical to the one of Windows 2008R2 which
4176 * always set the isRecycled attribute, even if the recycle-bin is
4177 * not activated and what ever the forest level is.
4179 if (dsdb_attribute_by_lDAPDisplayName(schema
, "isRecycled") != NULL
) {
4180 ret
= ldb_msg_add_string(msg
, "isRecycled", "TRUE");
4181 if (ret
!= LDB_SUCCESS
) {
4182 DEBUG(0,(__location__
": Failed to add isRecycled string to the msg\n"));
4183 ldb_module_oom(module
);
4184 talloc_free(tmp_ctx
);
4187 msg
->elements
[el_count
++].flags
= LDB_FLAG_MOD_REPLACE
;
4190 replmd_private
= talloc_get_type(ldb_module_get_private(module
),
4191 struct replmd_private
);
4192 /* work out which of the old attributes we will be removing */
4193 for (i
=0; i
<old_msg
->num_elements
; i
++) {
4194 const struct dsdb_attribute
*sa
;
4195 el
= &old_msg
->elements
[i
];
4196 sa
= dsdb_attribute_by_lDAPDisplayName(schema
, el
->name
);
4198 talloc_free(tmp_ctx
);
4199 return LDB_ERR_OPERATIONS_ERROR
;
4201 if (ldb_attr_cmp(el
->name
, rdn_name
) == 0) {
4202 /* don't remove the rDN */
4205 if (sa
->linkID
& 1) {
4207 we have a backlink in this object
4208 that needs to be removed. We're not
4209 allowed to remove it directly
4210 however, so we instead setup a
4211 modify to delete the corresponding
4214 ret
= replmd_delete_remove_link(module
, schema
,
4218 if (ret
!= LDB_SUCCESS
) {
4219 const char *old_dn_str
4220 = ldb_dn_get_linearized(old_dn
);
4221 ldb_asprintf_errstring(ldb
,
4223 ": Failed to remove backlink of "
4224 "%s when deleting %s: %s",
4227 ldb_errstring(ldb
));
4228 talloc_free(tmp_ctx
);
4229 return LDB_ERR_OPERATIONS_ERROR
;
4231 /* now we continue, which means we
4232 won't remove this backlink
4236 } else if (sa
->linkID
== 0) {
4237 if (ldb_attr_in_list(preserved_attrs
, el
->name
)) {
4240 if (sa
->searchFlags
& SEARCH_FLAG_PRESERVEONDELETE
) {
4245 * Ensure that we tell the modification to vanish any linked
4246 * attributes (not simply mark them as isDeleted = TRUE)
4248 dsdb_flags
|= DSDB_REPLMD_VANISH_LINKS
;
4250 ret
= ldb_msg_add_empty(msg
, el
->name
, LDB_FLAG_MOD_DELETE
, &el
);
4251 if (ret
!= LDB_SUCCESS
) {
4252 talloc_free(tmp_ctx
);
4253 ldb_module_oom(module
);
4260 case OBJECT_DELETED
:
4262 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4263 * describes what must be removed from a deleted
4267 ret
= ldb_msg_add_empty(msg
, "objectCategory", LDB_FLAG_MOD_REPLACE
, NULL
);
4268 if (ret
!= LDB_SUCCESS
) {
4269 talloc_free(tmp_ctx
);
4270 ldb_module_oom(module
);
4274 ret
= ldb_msg_add_empty(msg
, "sAMAccountType", LDB_FLAG_MOD_REPLACE
, NULL
);
4275 if (ret
!= LDB_SUCCESS
) {
4276 talloc_free(tmp_ctx
);
4277 ldb_module_oom(module
);
4287 if (deletion_state
== OBJECT_NOT_DELETED
) {
4288 const struct dsdb_attribute
*sa
;
4290 /* work out what the new rdn value is, for updating the
4291 rDN and name fields */
4292 new_rdn_value
= ldb_dn_get_rdn_val(new_dn
);
4293 if (new_rdn_value
== NULL
) {
4294 talloc_free(tmp_ctx
);
4295 return ldb_operr(ldb
);
4298 sa
= dsdb_attribute_by_lDAPDisplayName(schema
, rdn_name
);
4300 talloc_free(tmp_ctx
);
4301 return LDB_ERR_OPERATIONS_ERROR
;
4304 ret
= ldb_msg_add_value(msg
, sa
->lDAPDisplayName
, new_rdn_value
,
4306 if (ret
!= LDB_SUCCESS
) {
4307 talloc_free(tmp_ctx
);
4310 el
->flags
= LDB_FLAG_MOD_REPLACE
;
4312 el
= ldb_msg_find_element(old_msg
, "name");
4314 ret
= ldb_msg_add_value(msg
, "name", new_rdn_value
, &el
);
4315 if (ret
!= LDB_SUCCESS
) {
4316 talloc_free(tmp_ctx
);
4319 el
->flags
= LDB_FLAG_MOD_REPLACE
;
4324 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4329 * No matter what has happned with other renames, try again to
4330 * get this to be under the deleted DN.
4332 if (strcmp(ldb_dn_get_linearized(old_dn
), ldb_dn_get_linearized(new_dn
)) != 0) {
4333 /* now rename onto the new DN */
4334 ret
= dsdb_module_rename(module
, old_dn
, new_dn
, DSDB_FLAG_NEXT_MODULE
, req
);
4335 if (ret
!= LDB_SUCCESS
){
4336 DEBUG(0,(__location__
": Failed to rename object from '%s' to '%s' - %s\n",
4337 ldb_dn_get_linearized(old_dn
),
4338 ldb_dn_get_linearized(new_dn
),
4339 ldb_errstring(ldb
)));
4340 talloc_free(tmp_ctx
);
4346 ret
= dsdb_module_modify(module
, msg
, dsdb_flags
|DSDB_FLAG_OWN_MODULE
, req
);
4347 if (ret
!= LDB_SUCCESS
) {
4348 ldb_asprintf_errstring(ldb
, "replmd_delete: Failed to modify object %s in delete - %s",
4349 ldb_dn_get_linearized(old_dn
), ldb_errstring(ldb
));
4350 talloc_free(tmp_ctx
);
4354 talloc_free(tmp_ctx
);
4356 return ldb_module_done(req
, NULL
, NULL
, LDB_SUCCESS
);
4359 static int replmd_delete(struct ldb_module
*module
, struct ldb_request
*req
)
4361 return replmd_delete_internals(module
, req
, false);
4365 static int replmd_replicated_request_error(struct replmd_replicated_request
*ar
, int ret
)
4370 static int replmd_replicated_request_werror(struct replmd_replicated_request
*ar
, WERROR status
)
4372 int ret
= LDB_ERR_OTHER
;
4373 /* TODO: do some error mapping */
4375 /* Let the caller know the full WERROR */
4376 ar
->objs
->error
= status
;
4382 static struct replPropertyMetaData1
*
4383 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob
*md_blob
,
4384 enum drsuapi_DsAttributeId attid
)
4387 struct replPropertyMetaDataCtr1
*rpmd_ctr
= &md_blob
->ctr
.ctr1
;
4389 for (i
= 0; i
< rpmd_ctr
->count
; i
++) {
4390 if (rpmd_ctr
->array
[i
].attid
== attid
) {
4391 return &rpmd_ctr
->array
[i
];
4399 return true if an update is newer than an existing entry
4400 see section 5.11 of MS-ADTS
4402 static bool replmd_update_is_newer(const struct GUID
*current_invocation_id
,
4403 const struct GUID
*update_invocation_id
,
4404 uint32_t current_version
,
4405 uint32_t update_version
,
4406 NTTIME current_change_time
,
4407 NTTIME update_change_time
)
4409 if (update_version
!= current_version
) {
4410 return update_version
> current_version
;
4412 if (update_change_time
!= current_change_time
) {
4413 return update_change_time
> current_change_time
;
4415 return GUID_compare(update_invocation_id
, current_invocation_id
) > 0;
4418 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1
*cur_m
,
4419 struct replPropertyMetaData1
*new_m
)
4421 return replmd_update_is_newer(&cur_m
->originating_invocation_id
,
4422 &new_m
->originating_invocation_id
,
4425 cur_m
->originating_change_time
,
4426 new_m
->originating_change_time
);
4429 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags
,
4430 struct replPropertyMetaData1
*cur_m
,
4431 struct replPropertyMetaData1
*new_m
)
4436 * If the new replPropertyMetaData entry for this attribute is
4437 * not provided (this happens in the case where we look for
4438 * ATTID_name, but the name was not changed), then the local
4439 * state is clearly still current, as the remote
4440 * server didn't send it due to being older the high watermark
4443 if (new_m
== NULL
) {
4447 if (dsdb_repl_flags
& DSDB_REPL_FLAG_PRIORITISE_INCOMING
) {
4449 * if we compare equal then do an
4450 * update. This is used when a client
4451 * asks for a FULL_SYNC, and can be
4452 * used to recover a corrupt
4455 * This call is a bit tricky, what we
4456 * are doing it turning the 'is_newer'
4457 * call into a 'not is older' by
4458 * swapping cur_m and new_m, and negating the
4461 cmp
= !replmd_replPropertyMetaData1_is_newer(new_m
,
4464 cmp
= replmd_replPropertyMetaData1_is_newer(cur_m
,
4474 static struct ldb_dn
*replmd_conflict_dn(TALLOC_CTX
*mem_ctx
, struct ldb_dn
*dn
, struct GUID
*guid
)
4476 const struct ldb_val
*rdn_val
;
4477 const char *rdn_name
;
4478 struct ldb_dn
*new_dn
;
4480 rdn_val
= ldb_dn_get_rdn_val(dn
);
4481 rdn_name
= ldb_dn_get_rdn_name(dn
);
4482 if (!rdn_val
|| !rdn_name
) {
4486 new_dn
= ldb_dn_copy(mem_ctx
, dn
);
4491 if (!ldb_dn_remove_child_components(new_dn
, 1)) {
4495 if (!ldb_dn_add_child_fmt(new_dn
, "%s=%s\\0ACNF:%s",
4497 ldb_dn_escape_value(new_dn
, *rdn_val
),
4498 GUID_string(new_dn
, guid
))) {
4507 perform a modify operation which sets the rDN and name attributes to
4508 their current values. This has the effect of changing these
4509 attributes to have been last updated by the current DC. This is
4510 needed to ensure that renames performed as part of conflict
4511 resolution are propogated to other DCs
4513 static int replmd_name_modify(struct replmd_replicated_request
*ar
,
4514 struct ldb_request
*req
, struct ldb_dn
*dn
)
4516 struct ldb_message
*msg
;
4517 const char *rdn_name
;
4518 const struct ldb_val
*rdn_val
;
4519 const struct dsdb_attribute
*rdn_attr
;
4522 msg
= ldb_msg_new(req
);
4528 rdn_name
= ldb_dn_get_rdn_name(dn
);
4529 if (rdn_name
== NULL
) {
4533 /* normalize the rdn attribute name */
4534 rdn_attr
= dsdb_attribute_by_lDAPDisplayName(ar
->schema
, rdn_name
);
4535 if (rdn_attr
== NULL
) {
4538 rdn_name
= rdn_attr
->lDAPDisplayName
;
4540 rdn_val
= ldb_dn_get_rdn_val(dn
);
4541 if (rdn_val
== NULL
) {
4545 if (ldb_msg_add_empty(msg
, rdn_name
, LDB_FLAG_MOD_REPLACE
, NULL
) != 0) {
4548 if (ldb_msg_add_value(msg
, rdn_name
, rdn_val
, NULL
) != 0) {
4551 if (ldb_msg_add_empty(msg
, "name", LDB_FLAG_MOD_REPLACE
, NULL
) != 0) {
4554 if (ldb_msg_add_value(msg
, "name", rdn_val
, NULL
) != 0) {
4558 ret
= dsdb_module_modify(ar
->module
, msg
, DSDB_FLAG_OWN_MODULE
, req
);
4559 if (ret
!= LDB_SUCCESS
) {
4560 DEBUG(0,(__location__
": Failed to modify rDN/name of conflict DN '%s' - %s",
4561 ldb_dn_get_linearized(dn
),
4562 ldb_errstring(ldb_module_get_ctx(ar
->module
))));
4572 DEBUG(0,(__location__
": Failed to setup modify rDN/name of conflict DN '%s'",
4573 ldb_dn_get_linearized(dn
)));
4574 return LDB_ERR_OPERATIONS_ERROR
;
4579 callback for conflict DN handling where we have renamed the incoming
4580 record. After renaming it, we need to ensure the change of name and
4581 rDN for the incoming record is seen as an originating update by this DC.
4583 This also handles updating lastKnownParent for entries sent to lostAndFound
4585 static int replmd_op_name_modify_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
4587 struct replmd_replicated_request
*ar
=
4588 talloc_get_type_abort(req
->context
, struct replmd_replicated_request
);
4589 struct ldb_dn
*conflict_dn
= NULL
;
4592 if (ares
->error
!= LDB_SUCCESS
) {
4593 /* call the normal callback for everything except success */
4594 return replmd_op_callback(req
, ares
);
4597 switch (req
->operation
) {
4599 conflict_dn
= req
->op
.add
.message
->dn
;
4602 conflict_dn
= req
->op
.mod
.message
->dn
;
4605 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
4608 /* perform a modify of the rDN and name of the record */
4609 ret
= replmd_name_modify(ar
, req
, conflict_dn
);
4610 if (ret
!= LDB_SUCCESS
) {
4612 return replmd_op_callback(req
, ares
);
4615 if (ar
->objs
->objects
[ar
->index_current
].last_known_parent
) {
4616 struct ldb_message
*msg
= ldb_msg_new(req
);
4618 ldb_module_oom(ar
->module
);
4619 return LDB_ERR_OPERATIONS_ERROR
;
4622 msg
->dn
= req
->op
.add
.message
->dn
;
4624 ret
= ldb_msg_add_steal_string(msg
, "lastKnownParent",
4625 ldb_dn_get_extended_linearized(msg
, ar
->objs
->objects
[ar
->index_current
].last_known_parent
, 1));
4626 if (ret
!= LDB_SUCCESS
) {
4627 DEBUG(0,(__location__
": Failed to add lastKnownParent string to the msg\n"));
4628 ldb_module_oom(ar
->module
);
4631 msg
->elements
[0].flags
= LDB_FLAG_MOD_REPLACE
;
4633 ret
= dsdb_module_modify(ar
->module
, msg
, DSDB_FLAG_OWN_MODULE
, req
);
4634 if (ret
!= LDB_SUCCESS
) {
4635 DEBUG(0,(__location__
": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
4636 ldb_dn_get_linearized(msg
->dn
),
4637 ldb_errstring(ldb_module_get_ctx(ar
->module
))));
4643 return replmd_op_callback(req
, ares
);
4647 callback for replmd_replicated_apply_add()
4648 This copes with the creation of conflict records in the case where
4649 the DN exists, but with a different objectGUID
4651 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
))
4653 struct ldb_dn
*conflict_dn
;
4654 struct replmd_replicated_request
*ar
=
4655 talloc_get_type_abort(req
->context
, struct replmd_replicated_request
);
4656 struct ldb_result
*res
;
4657 const char *attrs
[] = { "replPropertyMetaData", "objectGUID", NULL
};
4659 const struct ldb_val
*omd_value
;
4660 struct replPropertyMetaDataBlob omd
, *rmd
;
4661 enum ndr_err_code ndr_err
;
4662 bool rename_incoming_record
, rodc
;
4663 struct replPropertyMetaData1
*rmd_name
, *omd_name
;
4664 struct ldb_message
*msg
;
4665 struct ldb_request
*down_req
= NULL
;
4667 /* call the normal callback for success */
4668 if (ares
->error
== LDB_SUCCESS
) {
4669 return callback(req
, ares
);
4673 * we have a conflict, and need to decide if we will keep the
4674 * new record or the old record
4677 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
4678 conflict_dn
= msg
->dn
;
4680 /* For failures other than conflicts, fail the whole operation here */
4681 if (ares
->error
!= LDB_ERR_ENTRY_ALREADY_EXISTS
) {
4682 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
), "Failed to locally apply remote add of %s: %s",
4683 ldb_dn_get_linearized(conflict_dn
),
4684 ldb_errstring(ldb_module_get_ctx(ar
->module
)));
4686 return ldb_module_done(ar
->req
, NULL
, NULL
,
4687 LDB_ERR_OPERATIONS_ERROR
);
4690 ret
= samdb_rodc(ldb_module_get_ctx(ar
->module
), &rodc
);
4691 if (ret
!= LDB_SUCCESS
) {
4692 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
)));
4693 return ldb_module_done(ar
->req
, NULL
, NULL
,
4694 LDB_ERR_OPERATIONS_ERROR
);
4700 * We are on an RODC, or were a GC for this
4701 * partition, so we have to fail this until
4702 * someone who owns the partition sorts it
4705 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
4706 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
4707 " - We must fail the operation until a master for this partition resolves the conflict",
4708 ldb_dn_get_linearized(conflict_dn
));
4713 * first we need the replPropertyMetaData attribute from the
4714 * local, conflicting record
4716 ret
= dsdb_module_search_dn(ar
->module
, req
, &res
, conflict_dn
,
4718 DSDB_FLAG_NEXT_MODULE
|
4719 DSDB_SEARCH_SHOW_DELETED
|
4720 DSDB_SEARCH_SHOW_RECYCLED
, req
);
4721 if (ret
!= LDB_SUCCESS
) {
4722 DEBUG(0,(__location__
": Unable to find object for conflicting record '%s'\n",
4723 ldb_dn_get_linearized(conflict_dn
)));
4727 omd_value
= ldb_msg_find_ldb_val(res
->msgs
[0], "replPropertyMetaData");
4728 if (omd_value
== NULL
) {
4729 DEBUG(0,(__location__
": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4730 ldb_dn_get_linearized(conflict_dn
)));
4734 ndr_err
= ndr_pull_struct_blob(omd_value
, res
->msgs
[0], &omd
,
4735 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
4736 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
4737 DEBUG(0,(__location__
": Failed to parse old replPropertyMetaData for %s\n",
4738 ldb_dn_get_linearized(conflict_dn
)));
4742 rmd
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
4745 * we decide which is newer based on the RPMD on the name
4746 * attribute. See [MS-DRSR] ResolveNameConflict.
4748 * We expect omd_name to be present, as this is from a local
4749 * search, but while rmd_name should have been given to us by
4750 * the remote server, if it is missing we just prefer the
4752 * replmd_replPropertyMetaData1_new_should_be_taken()
4754 rmd_name
= replmd_replPropertyMetaData1_find_attid(rmd
, DRSUAPI_ATTID_name
);
4755 omd_name
= replmd_replPropertyMetaData1_find_attid(&omd
, DRSUAPI_ATTID_name
);
4757 DEBUG(0,(__location__
": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4758 ldb_dn_get_linearized(conflict_dn
)));
4763 * Should we preserve the current record, and so rename the
4764 * incoming record to be a conflict?
4766 rename_incoming_record
4767 = !replmd_replPropertyMetaData1_new_should_be_taken(ar
->objs
->dsdb_repl_flags
& DSDB_REPL_FLAG_PRIORITISE_INCOMING
,
4768 omd_name
, rmd_name
);
4770 if (rename_incoming_record
) {
4772 struct ldb_dn
*new_dn
;
4774 guid
= samdb_result_guid(msg
, "objectGUID");
4775 if (GUID_all_zero(&guid
)) {
4776 DEBUG(0,(__location__
": Failed to find objectGUID for conflicting incoming record %s\n",
4777 ldb_dn_get_linearized(conflict_dn
)));
4780 new_dn
= replmd_conflict_dn(req
, conflict_dn
, &guid
);
4781 if (new_dn
== NULL
) {
4782 DEBUG(0,(__location__
": Failed to form conflict DN for %s\n",
4783 ldb_dn_get_linearized(conflict_dn
)));
4787 DEBUG(2,(__location__
": Resolving conflict record via incoming rename '%s' -> '%s'\n",
4788 ldb_dn_get_linearized(conflict_dn
), ldb_dn_get_linearized(new_dn
)));
4790 /* re-submit the request, but with the new DN */
4791 callback
= replmd_op_name_modify_callback
;
4794 /* we are renaming the existing record */
4796 struct ldb_dn
*new_dn
;
4798 guid
= samdb_result_guid(res
->msgs
[0], "objectGUID");
4799 if (GUID_all_zero(&guid
)) {
4800 DEBUG(0,(__location__
": Failed to find objectGUID for existing conflict record %s\n",
4801 ldb_dn_get_linearized(conflict_dn
)));
4805 new_dn
= replmd_conflict_dn(req
, conflict_dn
, &guid
);
4806 if (new_dn
== NULL
) {
4807 DEBUG(0,(__location__
": Failed to form conflict DN for %s\n",
4808 ldb_dn_get_linearized(conflict_dn
)));
4812 DEBUG(2,(__location__
": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4813 ldb_dn_get_linearized(conflict_dn
), ldb_dn_get_linearized(new_dn
)));
4815 ret
= dsdb_module_rename(ar
->module
, conflict_dn
, new_dn
,
4816 DSDB_FLAG_OWN_MODULE
, req
);
4817 if (ret
!= LDB_SUCCESS
) {
4818 DEBUG(0,(__location__
": Failed to rename conflict dn '%s' to '%s' - %s\n",
4819 ldb_dn_get_linearized(conflict_dn
),
4820 ldb_dn_get_linearized(new_dn
),
4821 ldb_errstring(ldb_module_get_ctx(ar
->module
))));
4826 * now we need to ensure that the rename is seen as an
4827 * originating update. We do that with a modify.
4829 ret
= replmd_name_modify(ar
, req
, new_dn
);
4830 if (ret
!= LDB_SUCCESS
) {
4834 DEBUG(2,(__location__
": With conflicting record renamed, re-apply replicated creation of '%s'\n",
4835 ldb_dn_get_linearized(req
->op
.add
.message
->dn
)));
4838 ret
= ldb_build_add_req(&down_req
,
4839 ldb_module_get_ctx(ar
->module
),
4846 if (ret
!= LDB_SUCCESS
) {
4849 LDB_REQ_SET_LOCATION(down_req
);
4851 /* current partition control needed by "repmd_op_callback" */
4852 ret
= ldb_request_add_control(down_req
,
4853 DSDB_CONTROL_CURRENT_PARTITION_OID
,
4855 if (ret
!= LDB_SUCCESS
) {
4856 return replmd_replicated_request_error(ar
, ret
);
4859 if (ar
->objs
->dsdb_repl_flags
& DSDB_REPL_FLAG_PARTIAL_REPLICA
) {
4860 /* this tells the partition module to make it a
4861 partial replica if creating an NC */
4862 ret
= ldb_request_add_control(down_req
,
4863 DSDB_CONTROL_PARTIAL_REPLICA
,
4865 if (ret
!= LDB_SUCCESS
) {
4866 return replmd_replicated_request_error(ar
, ret
);
4871 * Finally we re-run the add, otherwise the new record won't
4872 * exist, as we are here because of that exact failure!
4874 return ldb_next_request(ar
->module
, down_req
);
4877 /* on failure make the caller get the error. This means
4878 * replication will stop with an error, but there is not much
4881 return ldb_module_done(ar
->req
, NULL
, NULL
,
4886 callback for replmd_replicated_apply_add()
4887 This copes with the creation of conflict records in the case where
4888 the DN exists, but with a different objectGUID
4890 static int replmd_op_add_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
4892 struct replmd_replicated_request
*ar
=
4893 talloc_get_type_abort(req
->context
, struct replmd_replicated_request
);
4895 if (ar
->objs
->objects
[ar
->index_current
].last_known_parent
) {
4896 /* This is like a conflict DN, where we put the object in LostAndFound
4897 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
4898 return replmd_op_possible_conflict_callback(req
, ares
, replmd_op_name_modify_callback
);
4901 return replmd_op_possible_conflict_callback(req
, ares
, replmd_op_callback
);
4905 this is called when a new object comes in over DRS
4907 static int replmd_replicated_apply_add(struct replmd_replicated_request
*ar
)
4909 struct ldb_context
*ldb
;
4910 struct ldb_request
*change_req
;
4911 enum ndr_err_code ndr_err
;
4912 struct ldb_message
*msg
;
4913 struct replPropertyMetaDataBlob
*md
;
4914 struct ldb_val md_value
;
4917 bool remote_isDeleted
= false;
4920 time_t t
= time(NULL
);
4921 const struct ldb_val
*rdn_val
;
4922 struct replmd_private
*replmd_private
=
4923 talloc_get_type(ldb_module_get_private(ar
->module
),
4924 struct replmd_private
);
4925 unix_to_nt_time(&now
, t
);
4927 ldb
= ldb_module_get_ctx(ar
->module
);
4928 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
4929 md
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
4930 is_schema_nc
= ldb_dn_compare_base(replmd_private
->schema_dn
, msg
->dn
) == 0;
4932 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &ar
->seq_num
);
4933 if (ret
!= LDB_SUCCESS
) {
4934 return replmd_replicated_request_error(ar
, ret
);
4937 ret
= dsdb_msg_add_guid(msg
,
4938 &ar
->objs
->objects
[ar
->index_current
].object_guid
,
4940 if (ret
!= LDB_SUCCESS
) {
4941 return replmd_replicated_request_error(ar
, ret
);
4944 ret
= ldb_msg_add_string(msg
, "whenChanged", ar
->objs
->objects
[ar
->index_current
].when_changed
);
4945 if (ret
!= LDB_SUCCESS
) {
4946 return replmd_replicated_request_error(ar
, ret
);
4949 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNCreated", ar
->seq_num
);
4950 if (ret
!= LDB_SUCCESS
) {
4951 return replmd_replicated_request_error(ar
, ret
);
4954 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNChanged", ar
->seq_num
);
4955 if (ret
!= LDB_SUCCESS
) {
4956 return replmd_replicated_request_error(ar
, ret
);
4959 /* remove any message elements that have zero values */
4960 for (i
=0; i
<msg
->num_elements
; i
++) {
4961 struct ldb_message_element
*el
= &msg
->elements
[i
];
4963 if (el
->num_values
== 0) {
4964 if (ldb_attr_cmp(msg
->elements
[i
].name
, "objectClass") == 0) {
4965 ldb_asprintf_errstring(ldb
, __location__
4966 ": empty objectClass sent on %s, aborting replication\n",
4967 ldb_dn_get_linearized(msg
->dn
));
4968 return replmd_replicated_request_error(ar
, LDB_ERR_OBJECT_CLASS_VIOLATION
);
4971 DEBUG(4,(__location__
": Removing attribute %s with num_values==0\n",
4973 memmove(el
, el
+1, sizeof(*el
)*(msg
->num_elements
- (i
+1)));
4974 msg
->num_elements
--;
4981 struct GUID_txt_buf guid_txt
;
4983 char *s
= ldb_ldif_message_string(ldb
, ar
, LDB_CHANGETYPE_ADD
, msg
);
4984 DEBUG(4, ("DRS replication add message of %s:\n%s\n",
4985 GUID_buf_string(&ar
->objs
->objects
[ar
->index_current
].object_guid
, &guid_txt
),
4990 remote_isDeleted
= ldb_msg_find_attr_as_bool(msg
,
4991 "isDeleted", false);
4994 * the meta data array is already sorted by the caller, except
4995 * for the RDN, which needs to be added.
4999 rdn_val
= ldb_dn_get_rdn_val(msg
->dn
);
5000 ret
= replmd_update_rpmd_rdn_attr(ldb
, msg
, rdn_val
, NULL
,
5001 md
, ar
, now
, is_schema_nc
);
5002 if (ret
!= LDB_SUCCESS
) {
5003 ldb_asprintf_errstring(ldb
, "%s: error during DRS repl ADD: %s", __func__
, ldb_errstring(ldb
));
5004 return replmd_replicated_request_error(ar
, ret
);
5007 ret
= replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb
, &md
->ctr
.ctr1
, msg
->dn
);
5008 if (ret
!= LDB_SUCCESS
) {
5009 ldb_asprintf_errstring(ldb
, "%s: error during DRS repl ADD: %s", __func__
, ldb_errstring(ldb
));
5010 return replmd_replicated_request_error(ar
, ret
);
5013 for (i
=0; i
< md
->ctr
.ctr1
.count
; i
++) {
5014 md
->ctr
.ctr1
.array
[i
].local_usn
= ar
->seq_num
;
5016 ndr_err
= ndr_push_struct_blob(&md_value
, msg
, md
,
5017 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
5018 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
5019 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
5020 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
5022 ret
= ldb_msg_add_value(msg
, "replPropertyMetaData", &md_value
, NULL
);
5023 if (ret
!= LDB_SUCCESS
) {
5024 return replmd_replicated_request_error(ar
, ret
);
5027 replmd_ldb_message_sort(msg
, ar
->schema
);
5029 if (!remote_isDeleted
) {
5030 ret
= dsdb_module_schedule_sd_propagation(ar
->module
,
5031 ar
->objs
->partition_dn
,
5033 if (ret
!= LDB_SUCCESS
) {
5034 return replmd_replicated_request_error(ar
, ret
);
5038 ar
->isDeleted
= remote_isDeleted
;
5040 ret
= ldb_build_add_req(&change_req
,
5046 replmd_op_add_callback
,
5048 LDB_REQ_SET_LOCATION(change_req
);
5049 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
5051 /* current partition control needed by "repmd_op_callback" */
5052 ret
= ldb_request_add_control(change_req
,
5053 DSDB_CONTROL_CURRENT_PARTITION_OID
,
5055 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
5057 if (ar
->objs
->dsdb_repl_flags
& DSDB_REPL_FLAG_PARTIAL_REPLICA
) {
5058 /* this tells the partition module to make it a
5059 partial replica if creating an NC */
5060 ret
= ldb_request_add_control(change_req
,
5061 DSDB_CONTROL_PARTIAL_REPLICA
,
5063 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
5066 return ldb_next_request(ar
->module
, change_req
);
5069 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request
*req
,
5070 struct ldb_reply
*ares
)
5072 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
5073 struct replmd_replicated_request
);
5077 return ldb_module_done(ar
->req
, NULL
, NULL
,
5078 LDB_ERR_OPERATIONS_ERROR
);
5082 * The error NO_SUCH_OBJECT is not expected, unless the search
5083 * base is the partition DN, and that case doesn't happen here
5084 * because then we wouldn't get a parent_guid_value in any
5087 if (ares
->error
!= LDB_SUCCESS
) {
5088 return ldb_module_done(ar
->req
, ares
->controls
,
5089 ares
->response
, ares
->error
);
5092 switch (ares
->type
) {
5093 case LDB_REPLY_ENTRY
:
5095 struct ldb_message
*parent_msg
= ares
->message
;
5096 struct ldb_message
*msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
5097 struct ldb_dn
*parent_dn
;
5100 if (!ldb_msg_check_string_attribute(msg
, "isDeleted", "TRUE")
5101 && ldb_msg_check_string_attribute(parent_msg
, "isDeleted", "TRUE")) {
5102 /* Per MS-DRSR 4.1.10.6.10
5103 * FindBestParentObject we need to move this
5104 * new object under a deleted object to
5106 struct ldb_dn
*nc_root
;
5108 ret
= dsdb_find_nc_root(ldb_module_get_ctx(ar
->module
), msg
, msg
->dn
, &nc_root
);
5109 if (ret
== LDB_ERR_NO_SUCH_OBJECT
) {
5110 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5111 "No suitable NC root found for %s. "
5112 "We need to move this object because parent object %s "
5113 "is deleted, but this object is not.",
5114 ldb_dn_get_linearized(msg
->dn
),
5115 ldb_dn_get_linearized(parent_msg
->dn
));
5116 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
5117 } else if (ret
!= LDB_SUCCESS
) {
5118 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5119 "Unable to find NC root for %s: %s. "
5120 "We need to move this object because parent object %s "
5121 "is deleted, but this object is not.",
5122 ldb_dn_get_linearized(msg
->dn
),
5123 ldb_errstring(ldb_module_get_ctx(ar
->module
)),
5124 ldb_dn_get_linearized(parent_msg
->dn
));
5125 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
5128 ret
= dsdb_wellknown_dn(ldb_module_get_ctx(ar
->module
), msg
,
5130 DS_GUID_LOSTANDFOUND_CONTAINER
,
5132 if (ret
!= LDB_SUCCESS
) {
5133 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5134 "Unable to find LostAndFound Container for %s "
5135 "in partition %s: %s. "
5136 "We need to move this object because parent object %s "
5137 "is deleted, but this object is not.",
5138 ldb_dn_get_linearized(msg
->dn
), ldb_dn_get_linearized(nc_root
),
5139 ldb_errstring(ldb_module_get_ctx(ar
->module
)),
5140 ldb_dn_get_linearized(parent_msg
->dn
));
5141 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
5143 ar
->objs
->objects
[ar
->index_current
].last_known_parent
5144 = talloc_steal(ar
->objs
->objects
[ar
->index_current
].msg
, parent_msg
->dn
);
5148 = talloc_steal(ar
->objs
->objects
[ar
->index_current
].msg
, parent_msg
->dn
);
5151 ar
->objs
->objects
[ar
->index_current
].local_parent_dn
= parent_dn
;
5153 comp_num
= ldb_dn_get_comp_num(msg
->dn
);
5155 if (!ldb_dn_remove_base_components(msg
->dn
, comp_num
- 1)) {
5157 return ldb_module_done(ar
->req
, NULL
, NULL
, ldb_module_operr(ar
->module
));
5160 if (!ldb_dn_add_base(msg
->dn
, parent_dn
)) {
5162 return ldb_module_done(ar
->req
, NULL
, NULL
, ldb_module_operr(ar
->module
));
5166 case LDB_REPLY_REFERRAL
:
5167 /* we ignore referrals */
5170 case LDB_REPLY_DONE
:
5172 if (ar
->objs
->objects
[ar
->index_current
].local_parent_dn
== NULL
) {
5173 struct GUID_txt_buf str_buf
;
5174 if (ar
->search_msg
!= NULL
) {
5175 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5176 "No parent with GUID %s found for object locally known as %s",
5177 GUID_buf_string(ar
->objs
->objects
[ar
->index_current
].parent_guid
, &str_buf
),
5178 ldb_dn_get_linearized(ar
->search_msg
->dn
));
5180 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5181 "No parent with GUID %s found for object remotely known as %s",
5182 GUID_buf_string(ar
->objs
->objects
[ar
->index_current
].parent_guid
, &str_buf
),
5183 ldb_dn_get_linearized(ar
->objs
->objects
[ar
->index_current
].msg
->dn
));
5187 * This error code is really important, as it
5188 * is the flag back to the callers to retry
5189 * this with DRSUAPI_DRS_GET_ANC, and so get
5190 * the parent objects before the child
5193 return ldb_module_done(ar
->req
, NULL
, NULL
,
5194 replmd_replicated_request_werror(ar
, WERR_DS_DRA_MISSING_PARENT
));
5197 if (ar
->search_msg
!= NULL
) {
5198 ret
= replmd_replicated_apply_merge(ar
);
5200 ret
= replmd_replicated_apply_add(ar
);
5202 if (ret
!= LDB_SUCCESS
) {
5203 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
5212 * Look for the parent object, so we put the new object in the right
5213 * place This is akin to NameObject in MS-DRSR - this routine and the
5214 * callbacks find the right parent name, and correct name for this
5218 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request
*ar
)
5220 struct ldb_context
*ldb
;
5224 struct ldb_request
*search_req
;
5225 static const char *attrs
[] = {"isDeleted", NULL
};
5226 struct GUID_txt_buf guid_str_buf
;
5228 ldb
= ldb_module_get_ctx(ar
->module
);
5230 if (ar
->objs
->objects
[ar
->index_current
].parent_guid
== NULL
) {
5231 if (ar
->search_msg
!= NULL
) {
5232 return replmd_replicated_apply_merge(ar
);
5234 return replmd_replicated_apply_add(ar
);
5238 tmp_str
= GUID_buf_string(ar
->objs
->objects
[ar
->index_current
].parent_guid
,
5241 filter
= talloc_asprintf(ar
, "(objectGUID=%s)", tmp_str
);
5242 if (!filter
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
5244 ret
= ldb_build_search_req(&search_req
,
5247 ar
->objs
->partition_dn
,
5253 replmd_replicated_apply_search_for_parent_callback
,
5255 LDB_REQ_SET_LOCATION(search_req
);
5257 ret
= dsdb_request_add_controls(search_req
,
5258 DSDB_SEARCH_SHOW_RECYCLED
|
5259 DSDB_SEARCH_SHOW_DELETED
|
5260 DSDB_SEARCH_SHOW_EXTENDED_DN
);
5261 if (ret
!= LDB_SUCCESS
) {
5265 return ldb_next_request(ar
->module
, search_req
);
5269 handle renames that come in over DRS replication
5271 static int replmd_replicated_handle_rename(struct replmd_replicated_request
*ar
,
5272 struct ldb_message
*msg
,
5273 struct ldb_request
*parent
,
5277 TALLOC_CTX
*tmp_ctx
= talloc_new(msg
);
5278 struct ldb_result
*res
;
5279 struct ldb_dn
*conflict_dn
;
5280 const char *attrs
[] = { "replPropertyMetaData", "objectGUID", NULL
};
5281 const struct ldb_val
*omd_value
;
5282 struct replPropertyMetaDataBlob omd
, *rmd
;
5283 enum ndr_err_code ndr_err
;
5284 bool rename_incoming_record
, rodc
;
5285 struct replPropertyMetaData1
*rmd_name
, *omd_name
;
5286 struct ldb_dn
*new_dn
;
5289 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5290 ldb_dn_get_linearized(ar
->search_msg
->dn
),
5291 ldb_dn_get_linearized(msg
->dn
)));
5294 ret
= dsdb_module_rename(ar
->module
, ar
->search_msg
->dn
, msg
->dn
,
5295 DSDB_FLAG_NEXT_MODULE
, ar
->req
);
5296 if (ret
== LDB_SUCCESS
) {
5297 talloc_free(tmp_ctx
);
5302 if (ret
!= LDB_ERR_ENTRY_ALREADY_EXISTS
) {
5303 talloc_free(tmp_ctx
);
5304 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
), "Failed to locally apply remote rename from %s to %s: %s",
5305 ldb_dn_get_linearized(ar
->search_msg
->dn
),
5306 ldb_dn_get_linearized(msg
->dn
),
5307 ldb_errstring(ldb_module_get_ctx(ar
->module
)));
5311 ret
= samdb_rodc(ldb_module_get_ctx(ar
->module
), &rodc
);
5312 if (ret
!= LDB_SUCCESS
) {
5313 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5314 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5315 ldb_errstring(ldb_module_get_ctx(ar
->module
)));
5316 return LDB_ERR_OPERATIONS_ERROR
;
5319 * we have a conflict, and need to decide if we will keep the
5320 * new record or the old record
5323 conflict_dn
= msg
->dn
;
5327 * We are on an RODC, or were a GC for this
5328 * partition, so we have to fail this until
5329 * someone who owns the partition sorts it
5332 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5333 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5334 " - We must fail the operation until a master for this partition resolves the conflict",
5335 ldb_dn_get_linearized(conflict_dn
));
5340 * first we need the replPropertyMetaData attribute from the
5343 ret
= dsdb_module_search_dn(ar
->module
, tmp_ctx
, &res
, conflict_dn
,
5345 DSDB_FLAG_NEXT_MODULE
|
5346 DSDB_SEARCH_SHOW_DELETED
|
5347 DSDB_SEARCH_SHOW_RECYCLED
, ar
->req
);
5348 if (ret
!= LDB_SUCCESS
) {
5349 DEBUG(0,(__location__
": Unable to find object for conflicting record '%s'\n",
5350 ldb_dn_get_linearized(conflict_dn
)));
5354 omd_value
= ldb_msg_find_ldb_val(res
->msgs
[0], "replPropertyMetaData");
5355 if (omd_value
== NULL
) {
5356 DEBUG(0,(__location__
": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5357 ldb_dn_get_linearized(conflict_dn
)));
5361 ndr_err
= ndr_pull_struct_blob(omd_value
, res
->msgs
[0], &omd
,
5362 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
5363 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
5364 DEBUG(0,(__location__
": Failed to parse old replPropertyMetaData for %s\n",
5365 ldb_dn_get_linearized(conflict_dn
)));
5369 rmd
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
5372 * we decide which is newer based on the RPMD on the name
5373 * attribute. See [MS-DRSR] ResolveNameConflict.
5375 * We expect omd_name to be present, as this is from a local
5376 * search, but while rmd_name should have been given to us by
5377 * the remote server, if it is missing we just prefer the
5379 * replmd_replPropertyMetaData1_new_should_be_taken()
5381 rmd_name
= replmd_replPropertyMetaData1_find_attid(rmd
, DRSUAPI_ATTID_name
);
5382 omd_name
= replmd_replPropertyMetaData1_find_attid(&omd
, DRSUAPI_ATTID_name
);
5384 DEBUG(0,(__location__
": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5385 ldb_dn_get_linearized(conflict_dn
)));
5390 * Should we preserve the current record, and so rename the
5391 * incoming record to be a conflict?
5393 rename_incoming_record
=
5394 !replmd_replPropertyMetaData1_new_should_be_taken(
5395 ar
->objs
->dsdb_repl_flags
& DSDB_REPL_FLAG_PRIORITISE_INCOMING
,
5396 omd_name
, rmd_name
);
5398 if (rename_incoming_record
) {
5400 new_dn
= replmd_conflict_dn(msg
, msg
->dn
,
5401 &ar
->objs
->objects
[ar
->index_current
].object_guid
);
5402 if (new_dn
== NULL
) {
5403 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5404 "Failed to form conflict DN for %s\n",
5405 ldb_dn_get_linearized(msg
->dn
));
5407 return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
5410 ret
= dsdb_module_rename(ar
->module
, ar
->search_msg
->dn
, new_dn
,
5411 DSDB_FLAG_NEXT_MODULE
, ar
->req
);
5412 if (ret
!= LDB_SUCCESS
) {
5413 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
5414 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5415 ldb_dn_get_linearized(conflict_dn
),
5416 ldb_dn_get_linearized(ar
->search_msg
->dn
),
5417 ldb_dn_get_linearized(new_dn
),
5418 ldb_errstring(ldb_module_get_ctx(ar
->module
)));
5419 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_DB_ERROR
);
5427 /* we are renaming the existing record */
5429 guid
= samdb_result_guid(res
->msgs
[0], "objectGUID");
5430 if (GUID_all_zero(&guid
)) {
5431 DEBUG(0,(__location__
": Failed to find objectGUID for existing conflict record %s\n",
5432 ldb_dn_get_linearized(conflict_dn
)));
5436 new_dn
= replmd_conflict_dn(tmp_ctx
, conflict_dn
, &guid
);
5437 if (new_dn
== NULL
) {
5438 DEBUG(0,(__location__
": Failed to form conflict DN for %s\n",
5439 ldb_dn_get_linearized(conflict_dn
)));
5443 DEBUG(2,(__location__
": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5444 ldb_dn_get_linearized(conflict_dn
), ldb_dn_get_linearized(new_dn
)));
5446 ret
= dsdb_module_rename(ar
->module
, conflict_dn
, new_dn
,
5447 DSDB_FLAG_OWN_MODULE
, ar
->req
);
5448 if (ret
!= LDB_SUCCESS
) {
5449 DEBUG(0,(__location__
": Failed to rename conflict dn '%s' to '%s' - %s\n",
5450 ldb_dn_get_linearized(conflict_dn
),
5451 ldb_dn_get_linearized(new_dn
),
5452 ldb_errstring(ldb_module_get_ctx(ar
->module
))));
5457 * now we need to ensure that the rename is seen as an
5458 * originating update. We do that with a modify.
5460 ret
= replmd_name_modify(ar
, ar
->req
, new_dn
);
5461 if (ret
!= LDB_SUCCESS
) {
5465 DEBUG(2,(__location__
": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
5466 ldb_dn_get_linearized(ar
->search_msg
->dn
),
5467 ldb_dn_get_linearized(msg
->dn
)));
5470 ret
= dsdb_module_rename(ar
->module
, ar
->search_msg
->dn
, msg
->dn
,
5471 DSDB_FLAG_NEXT_MODULE
, ar
->req
);
5472 if (ret
!= LDB_SUCCESS
) {
5473 DEBUG(0,(__location__
": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
5474 ldb_dn_get_linearized(ar
->search_msg
->dn
),
5475 ldb_dn_get_linearized(msg
->dn
),
5476 ldb_errstring(ldb_module_get_ctx(ar
->module
))));
5482 * On failure make the caller get the error
5483 * This means replication will stop with an error,
5484 * but there is not much else we can do. In the
5485 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
5489 talloc_free(tmp_ctx
);
5494 static int replmd_replicated_apply_merge(struct replmd_replicated_request
*ar
)
5496 struct ldb_context
*ldb
;
5497 struct ldb_request
*change_req
;
5498 enum ndr_err_code ndr_err
;
5499 struct ldb_message
*msg
;
5500 struct replPropertyMetaDataBlob
*rmd
;
5501 struct replPropertyMetaDataBlob omd
;
5502 const struct ldb_val
*omd_value
;
5503 struct replPropertyMetaDataBlob nmd
;
5504 struct ldb_val nmd_value
;
5505 struct GUID remote_parent_guid
;
5508 unsigned int removed_attrs
= 0;
5510 int (*callback
)(struct ldb_request
*req
, struct ldb_reply
*ares
) = replmd_op_callback
;
5511 bool isDeleted
= false;
5512 bool local_isDeleted
= false;
5513 bool remote_isDeleted
= false;
5514 bool take_remote_isDeleted
= false;
5515 bool sd_updated
= false;
5516 bool renamed
= false;
5517 bool is_schema_nc
= false;
5519 const struct ldb_val
*old_rdn
, *new_rdn
;
5520 struct replmd_private
*replmd_private
=
5521 talloc_get_type(ldb_module_get_private(ar
->module
),
5522 struct replmd_private
);
5524 time_t t
= time(NULL
);
5525 unix_to_nt_time(&now
, t
);
5527 ldb
= ldb_module_get_ctx(ar
->module
);
5528 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
5530 is_schema_nc
= ldb_dn_compare_base(replmd_private
->schema_dn
, msg
->dn
) == 0;
5532 rmd
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
5536 /* find existing meta data */
5537 omd_value
= ldb_msg_find_ldb_val(ar
->search_msg
, "replPropertyMetaData");
5539 ndr_err
= ndr_pull_struct_blob(omd_value
, ar
, &omd
,
5540 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
5541 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
5542 nt_status
= ndr_map_error2ntstatus(ndr_err
);
5543 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
5546 if (omd
.version
!= 1) {
5547 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
5552 struct GUID_txt_buf guid_txt
;
5554 char *s
= ldb_ldif_message_string(ldb
, ar
, LDB_CHANGETYPE_MODIFY
, msg
);
5555 DEBUG(5, ("Initial DRS replication modify message of %s is:\n%s\n"
5558 GUID_buf_string(&ar
->objs
->objects
[ar
->index_current
].object_guid
, &guid_txt
),
5560 ndr_print_struct_string(s
,
5561 (ndr_print_fn_t
)ndr_print_replPropertyMetaDataBlob
,
5562 "existing replPropertyMetaData",
5564 ndr_print_struct_string(s
,
5565 (ndr_print_fn_t
)ndr_print_replPropertyMetaDataBlob
,
5566 "incoming replPropertyMetaData",
5571 local_isDeleted
= ldb_msg_find_attr_as_bool(ar
->search_msg
,
5572 "isDeleted", false);
5573 remote_isDeleted
= ldb_msg_find_attr_as_bool(msg
,
5574 "isDeleted", false);
5577 * Fill in the remote_parent_guid with the GUID or an all-zero
5580 if (ar
->objs
->objects
[ar
->index_current
].parent_guid
!= NULL
) {
5581 remote_parent_guid
= *ar
->objs
->objects
[ar
->index_current
].parent_guid
;
5583 remote_parent_guid
= GUID_zero();
5587 * To ensure we follow a complex rename chain around, we have
5588 * to confirm that the DN is the same (mostly to confirm the
5589 * RDN) and the parentGUID is the same.
5591 * This ensures we keep things under the correct parent, which
5592 * replmd_replicated_handle_rename() will do.
5595 if (strcmp(ldb_dn_get_linearized(msg
->dn
), ldb_dn_get_linearized(ar
->search_msg
->dn
)) == 0
5596 && GUID_equal(&remote_parent_guid
, &ar
->local_parent_guid
)) {
5600 * handle renames, even just by case that come in over
5601 * DRS. Changes in the parent DN don't hit us here,
5602 * because the search for a parent will clean up those
5605 * We also have already filtered out the case where
5606 * the peer has an older name to what we have (see
5607 * replmd_replicated_apply_search_callback())
5609 ret
= replmd_replicated_handle_rename(ar
, msg
, ar
->req
, &renamed
);
5612 if (ret
!= LDB_SUCCESS
) {
5613 ldb_debug(ldb
, LDB_DEBUG_FATAL
,
5614 "replmd_replicated_request rename %s => %s failed - %s\n",
5615 ldb_dn_get_linearized(ar
->search_msg
->dn
),
5616 ldb_dn_get_linearized(msg
->dn
),
5617 ldb_errstring(ldb
));
5618 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_DB_ERROR
);
5621 if (renamed
== true) {
5623 * Set the callback to one that will fix up the name
5624 * metadata on the new conflict DN
5626 callback
= replmd_op_name_modify_callback
;
5631 nmd
.ctr
.ctr1
.count
= omd
.ctr
.ctr1
.count
+ rmd
->ctr
.ctr1
.count
;
5632 nmd
.ctr
.ctr1
.array
= talloc_array(ar
,
5633 struct replPropertyMetaData1
,
5634 nmd
.ctr
.ctr1
.count
);
5635 if (!nmd
.ctr
.ctr1
.array
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
5637 /* first copy the old meta data */
5638 for (i
=0; i
< omd
.ctr
.ctr1
.count
; i
++) {
5639 nmd
.ctr
.ctr1
.array
[ni
] = omd
.ctr
.ctr1
.array
[i
];
5644 /* now merge in the new meta data */
5645 for (i
=0; i
< rmd
->ctr
.ctr1
.count
; i
++) {
5648 for (j
=0; j
< ni
; j
++) {
5651 if (rmd
->ctr
.ctr1
.array
[i
].attid
!= nmd
.ctr
.ctr1
.array
[j
].attid
) {
5655 cmp
= replmd_replPropertyMetaData1_new_should_be_taken(
5656 ar
->objs
->dsdb_repl_flags
,
5657 &nmd
.ctr
.ctr1
.array
[j
],
5658 &rmd
->ctr
.ctr1
.array
[i
]);
5660 /* replace the entry */
5661 nmd
.ctr
.ctr1
.array
[j
] = rmd
->ctr
.ctr1
.array
[i
];
5662 if (ar
->seq_num
== 0) {
5663 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &ar
->seq_num
);
5664 if (ret
!= LDB_SUCCESS
) {
5665 return replmd_replicated_request_error(ar
, ret
);
5668 nmd
.ctr
.ctr1
.array
[j
].local_usn
= ar
->seq_num
;
5669 switch (nmd
.ctr
.ctr1
.array
[j
].attid
) {
5670 case DRSUAPI_ATTID_ntSecurityDescriptor
:
5673 case DRSUAPI_ATTID_isDeleted
:
5674 take_remote_isDeleted
= true;
5683 if (rmd
->ctr
.ctr1
.array
[i
].attid
!= DRSUAPI_ATTID_instanceType
) {
5684 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
5685 msg
->elements
[i
-removed_attrs
].name
,
5686 ldb_dn_get_linearized(msg
->dn
),
5687 GUID_string(ar
, &rmd
->ctr
.ctr1
.array
[i
].originating_invocation_id
)));
5690 /* we don't want to apply this change so remove the attribute */
5691 ldb_msg_remove_element(msg
, &msg
->elements
[i
-removed_attrs
]);
5698 if (found
) continue;
5700 nmd
.ctr
.ctr1
.array
[ni
] = rmd
->ctr
.ctr1
.array
[i
];
5701 if (ar
->seq_num
== 0) {
5702 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &ar
->seq_num
);
5703 if (ret
!= LDB_SUCCESS
) {
5704 return replmd_replicated_request_error(ar
, ret
);
5707 nmd
.ctr
.ctr1
.array
[ni
].local_usn
= ar
->seq_num
;
5708 switch (nmd
.ctr
.ctr1
.array
[ni
].attid
) {
5709 case DRSUAPI_ATTID_ntSecurityDescriptor
:
5712 case DRSUAPI_ATTID_isDeleted
:
5713 take_remote_isDeleted
= true;
5722 * finally correct the size of the meta_data array
5724 nmd
.ctr
.ctr1
.count
= ni
;
5726 new_rdn
= ldb_dn_get_rdn_val(msg
->dn
);
5727 old_rdn
= ldb_dn_get_rdn_val(ar
->search_msg
->dn
);
5730 ret
= replmd_update_rpmd_rdn_attr(ldb
, msg
, new_rdn
, old_rdn
,
5731 &nmd
, ar
, now
, is_schema_nc
);
5732 if (ret
!= LDB_SUCCESS
) {
5733 ldb_asprintf_errstring(ldb
, "%s: error during DRS repl merge: %s", __func__
, ldb_errstring(ldb
));
5734 return replmd_replicated_request_error(ar
, ret
);
5738 * sort the new meta data array
5740 ret
= replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb
, &nmd
.ctr
.ctr1
, msg
->dn
);
5741 if (ret
!= LDB_SUCCESS
) {
5742 ldb_asprintf_errstring(ldb
, "%s: error during DRS repl merge: %s", __func__
, ldb_errstring(ldb
));
5747 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
5750 * This also controls SD propagation below
5752 if (take_remote_isDeleted
) {
5753 isDeleted
= remote_isDeleted
;
5755 isDeleted
= local_isDeleted
;
5758 ar
->isDeleted
= isDeleted
;
5761 * check if some replicated attributes left, otherwise skip the ldb_modify() call
5763 if (msg
->num_elements
== 0) {
5764 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_replicated_apply_merge[%u]: skip replace\n",
5767 return replmd_replicated_apply_isDeleted(ar
);
5770 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
5771 ar
->index_current
, msg
->num_elements
);
5777 if (sd_updated
&& !isDeleted
) {
5778 ret
= dsdb_module_schedule_sd_propagation(ar
->module
,
5779 ar
->objs
->partition_dn
,
5781 if (ret
!= LDB_SUCCESS
) {
5782 return ldb_operr(ldb
);
5786 /* create the meta data value */
5787 ndr_err
= ndr_push_struct_blob(&nmd_value
, msg
, &nmd
,
5788 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
5789 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
5790 nt_status
= ndr_map_error2ntstatus(ndr_err
);
5791 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
5795 * when we know that we'll modify the record, add the whenChanged, uSNChanged
5796 * and replPopertyMetaData attributes
5798 ret
= ldb_msg_add_string(msg
, "whenChanged", ar
->objs
->objects
[ar
->index_current
].when_changed
);
5799 if (ret
!= LDB_SUCCESS
) {
5800 return replmd_replicated_request_error(ar
, ret
);
5802 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNChanged", ar
->seq_num
);
5803 if (ret
!= LDB_SUCCESS
) {
5804 return replmd_replicated_request_error(ar
, ret
);
5806 ret
= ldb_msg_add_value(msg
, "replPropertyMetaData", &nmd_value
, NULL
);
5807 if (ret
!= LDB_SUCCESS
) {
5808 return replmd_replicated_request_error(ar
, ret
);
5811 replmd_ldb_message_sort(msg
, ar
->schema
);
5813 /* we want to replace the old values */
5814 for (i
=0; i
< msg
->num_elements
; i
++) {
5815 msg
->elements
[i
].flags
= LDB_FLAG_MOD_REPLACE
;
5816 if (ldb_attr_cmp(msg
->elements
[i
].name
, "objectClass") == 0) {
5817 if (msg
->elements
[i
].num_values
== 0) {
5818 ldb_asprintf_errstring(ldb
, __location__
5819 ": objectClass removed on %s, aborting replication\n",
5820 ldb_dn_get_linearized(msg
->dn
));
5821 return replmd_replicated_request_error(ar
, LDB_ERR_OBJECT_CLASS_VIOLATION
);
5827 struct GUID_txt_buf guid_txt
;
5829 char *s
= ldb_ldif_message_string(ldb
, ar
, LDB_CHANGETYPE_MODIFY
, msg
);
5830 DEBUG(4, ("Final DRS replication modify message of %s:\n%s\n",
5831 GUID_buf_string(&ar
->objs
->objects
[ar
->index_current
].object_guid
, &guid_txt
),
5836 ret
= ldb_build_mod_req(&change_req
,
5844 LDB_REQ_SET_LOCATION(change_req
);
5845 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
5847 /* current partition control needed by "repmd_op_callback" */
5848 ret
= ldb_request_add_control(change_req
,
5849 DSDB_CONTROL_CURRENT_PARTITION_OID
,
5851 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
5853 return ldb_next_request(ar
->module
, change_req
);
5856 static int replmd_replicated_apply_search_callback(struct ldb_request
*req
,
5857 struct ldb_reply
*ares
)
5859 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
5860 struct replmd_replicated_request
);
5864 return ldb_module_done(ar
->req
, NULL
, NULL
,
5865 LDB_ERR_OPERATIONS_ERROR
);
5867 if (ares
->error
!= LDB_SUCCESS
&&
5868 ares
->error
!= LDB_ERR_NO_SUCH_OBJECT
) {
5869 return ldb_module_done(ar
->req
, ares
->controls
,
5870 ares
->response
, ares
->error
);
5873 switch (ares
->type
) {
5874 case LDB_REPLY_ENTRY
:
5875 ar
->search_msg
= talloc_steal(ar
, ares
->message
);
5878 case LDB_REPLY_REFERRAL
:
5879 /* we ignore referrals */
5882 case LDB_REPLY_DONE
:
5884 struct replPropertyMetaData1
*md_remote
;
5885 struct replPropertyMetaData1
*md_local
;
5887 struct replPropertyMetaDataBlob omd
;
5888 const struct ldb_val
*omd_value
;
5889 struct replPropertyMetaDataBlob
*rmd
;
5890 struct ldb_message
*msg
;
5892 ar
->objs
->objects
[ar
->index_current
].local_parent_dn
= NULL
;
5893 ar
->objs
->objects
[ar
->index_current
].last_known_parent
= NULL
;
5896 * This is the ADD case, find the appropriate parent,
5897 * as this object doesn't exist locally:
5899 if (ar
->search_msg
== NULL
) {
5900 ret
= replmd_replicated_apply_search_for_parent(ar
);
5901 if (ret
!= LDB_SUCCESS
) {
5902 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
5909 * Otherwise, in the MERGE case, work out if we are
5910 * attempting a rename, and if so find the parent the
5911 * newly renamed object wants to belong under (which
5912 * may not be the parent in it's attached string DN
5914 rmd
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
5918 /* find existing meta data */
5919 omd_value
= ldb_msg_find_ldb_val(ar
->search_msg
, "replPropertyMetaData");
5921 enum ndr_err_code ndr_err
;
5922 ndr_err
= ndr_pull_struct_blob(omd_value
, ar
, &omd
,
5923 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
5924 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
5925 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
5926 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
5929 if (omd
.version
!= 1) {
5930 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
5934 ar
->local_parent_guid
= samdb_result_guid(ar
->search_msg
, "parentGUID");
5936 instanceType
= ldb_msg_find_attr_as_int(ar
->search_msg
, "instanceType", 0);
5937 if (((instanceType
& INSTANCE_TYPE_IS_NC_HEAD
) == 0)
5938 && GUID_all_zero(&ar
->local_parent_guid
)) {
5939 DEBUG(0, ("Refusing to replicate new version of %s "
5940 "as local object has an all-zero parentGUID attribute, "
5941 "despite not being an NC root\n",
5942 ldb_dn_get_linearized(ar
->search_msg
->dn
)));
5943 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
5947 * now we need to check for double renames. We could have a
5948 * local rename pending which our replication partner hasn't
5949 * received yet. We choose which one wins by looking at the
5950 * attribute stamps on the two objects, the newer one wins.
5952 * This also simply applies the correct algorithms for
5953 * determining if a change was made to name at all, or
5954 * if the object has just been renamed under the same
5957 md_remote
= replmd_replPropertyMetaData1_find_attid(rmd
, DRSUAPI_ATTID_name
);
5958 md_local
= replmd_replPropertyMetaData1_find_attid(&omd
, DRSUAPI_ATTID_name
);
5960 DEBUG(0,(__location__
": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5961 ldb_dn_get_linearized(ar
->search_msg
->dn
)));
5962 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_DB_ERROR
);
5966 * if there is no name attribute given then we have to assume the
5967 * object we've received has the older name
5969 if (replmd_replPropertyMetaData1_new_should_be_taken(
5970 ar
->objs
->dsdb_repl_flags
& DSDB_REPL_FLAG_PRIORITISE_INCOMING
,
5971 md_local
, md_remote
)) {
5972 struct GUID_txt_buf p_guid_local
;
5973 struct GUID_txt_buf p_guid_remote
;
5974 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
5976 /* Merge on the existing object, with rename */
5978 DEBUG(4,(__location__
": Looking for new parent for object %s currently under %s "
5979 "as incoming object changing to %s under %s\n",
5980 ldb_dn_get_linearized(ar
->search_msg
->dn
),
5981 GUID_buf_string(&ar
->local_parent_guid
, &p_guid_local
),
5982 ldb_dn_get_linearized(msg
->dn
),
5983 GUID_buf_string(ar
->objs
->objects
[ar
->index_current
].parent_guid
,
5985 ret
= replmd_replicated_apply_search_for_parent(ar
);
5987 struct GUID_txt_buf p_guid_local
;
5988 struct GUID_txt_buf p_guid_remote
;
5989 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
5992 * Merge on the existing object, force no
5993 * rename (code below just to explain why in
5997 if (strcmp(ldb_dn_get_linearized(ar
->search_msg
->dn
),
5998 ldb_dn_get_linearized(msg
->dn
)) == 0) {
5999 if (ar
->objs
->objects
[ar
->index_current
].parent_guid
!= NULL
&&
6000 GUID_equal(&ar
->local_parent_guid
,
6001 ar
->objs
->objects
[ar
->index_current
].parent_guid
)
6003 DEBUG(4,(__location__
": Keeping object %s at under %s "
6004 "despite incoming object changing parent to %s\n",
6005 ldb_dn_get_linearized(ar
->search_msg
->dn
),
6006 GUID_buf_string(&ar
->local_parent_guid
, &p_guid_local
),
6007 GUID_buf_string(ar
->objs
->objects
[ar
->index_current
].parent_guid
,
6011 DEBUG(4,(__location__
": Keeping object %s at under %s "
6012 " and rejecting older rename to %s under %s\n",
6013 ldb_dn_get_linearized(ar
->search_msg
->dn
),
6014 GUID_buf_string(&ar
->local_parent_guid
, &p_guid_local
),
6015 ldb_dn_get_linearized(msg
->dn
),
6016 GUID_buf_string(ar
->objs
->objects
[ar
->index_current
].parent_guid
,
6020 * This assignment ensures that the strcmp()
6021 * and GUID_equal() calls in
6022 * replmd_replicated_apply_merge() avoids the
6025 ar
->objs
->objects
[ar
->index_current
].parent_guid
=
6026 &ar
->local_parent_guid
;
6028 msg
->dn
= ar
->search_msg
->dn
;
6029 ret
= replmd_replicated_apply_merge(ar
);
6031 if (ret
!= LDB_SUCCESS
) {
6032 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
6041 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request
*ar
);
6043 static int replmd_replicated_apply_next(struct replmd_replicated_request
*ar
)
6045 struct ldb_context
*ldb
;
6049 struct ldb_request
*search_req
;
6050 static const char *attrs
[] = { "repsFrom", "replUpToDateVector",
6051 "parentGUID", "instanceType",
6052 "replPropertyMetaData", "nTSecurityDescriptor",
6053 "isDeleted", NULL
};
6054 struct GUID_txt_buf guid_str_buf
;
6056 if (ar
->index_current
>= ar
->objs
->num_objects
) {
6057 /* done with it, go to next stage */
6058 return replmd_replicated_uptodate_vector(ar
);
6061 ldb
= ldb_module_get_ctx(ar
->module
);
6062 ar
->search_msg
= NULL
;
6063 ar
->isDeleted
= false;
6065 tmp_str
= GUID_buf_string(&ar
->objs
->objects
[ar
->index_current
].object_guid
,
6068 filter
= talloc_asprintf(ar
, "(objectGUID=%s)", tmp_str
);
6069 if (!filter
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
6071 ret
= ldb_build_search_req(&search_req
,
6074 ar
->objs
->partition_dn
,
6080 replmd_replicated_apply_search_callback
,
6082 LDB_REQ_SET_LOCATION(search_req
);
6084 ret
= dsdb_request_add_controls(search_req
, DSDB_SEARCH_SHOW_RECYCLED
);
6086 if (ret
!= LDB_SUCCESS
) {
6090 return ldb_next_request(ar
->module
, search_req
);
6094 * This is essentially a wrapper for replmd_replicated_apply_next()
6096 * This is needed to ensure that both codepaths call this handler.
6098 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request
*ar
)
6100 struct ldb_dn
*deleted_objects_dn
;
6101 struct ldb_message
*msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
6102 int ret
= dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar
->module
), msg
, msg
->dn
,
6103 &deleted_objects_dn
);
6104 if (ar
->isDeleted
&& (ret
!= LDB_SUCCESS
|| ldb_dn_compare(msg
->dn
, deleted_objects_dn
) != 0)) {
6106 * Do a delete here again, so that if there is
6107 * anything local that conflicts with this
6108 * object being deleted, it is removed. This
6109 * includes links. See MS-DRSR 4.1.10.6.9
6112 * If the object is already deleted, and there
6113 * is no more work required, it doesn't do
6117 /* This has been updated to point to the DN we eventually did the modify on */
6119 struct ldb_request
*del_req
;
6120 struct ldb_result
*res
;
6122 TALLOC_CTX
*tmp_ctx
= talloc_new(ar
);
6124 ret
= ldb_oom(ldb_module_get_ctx(ar
->module
));
6128 res
= talloc_zero(tmp_ctx
, struct ldb_result
);
6130 ret
= ldb_oom(ldb_module_get_ctx(ar
->module
));
6131 talloc_free(tmp_ctx
);
6135 /* Build a delete request, which hopefully will artually turn into nothing */
6136 ret
= ldb_build_del_req(&del_req
, ldb_module_get_ctx(ar
->module
), tmp_ctx
,
6140 ldb_modify_default_callback
,
6142 LDB_REQ_SET_LOCATION(del_req
);
6143 if (ret
!= LDB_SUCCESS
) {
6144 talloc_free(tmp_ctx
);
6149 * This is the guts of the call, call back
6150 * into our delete code, but setting the
6151 * re_delete flag so we delete anything that
6152 * shouldn't be there on a deleted or recycled
6155 ret
= replmd_delete_internals(ar
->module
, del_req
, true);
6156 if (ret
== LDB_SUCCESS
) {
6157 ret
= ldb_wait(del_req
->handle
, LDB_WAIT_ALL
);
6160 talloc_free(tmp_ctx
);
6161 if (ret
!= LDB_SUCCESS
) {
6166 ar
->index_current
++;
6167 return replmd_replicated_apply_next(ar
);
6170 static int replmd_replicated_uptodate_modify_callback(struct ldb_request
*req
,
6171 struct ldb_reply
*ares
)
6173 struct ldb_context
*ldb
;
6174 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
6175 struct replmd_replicated_request
);
6176 ldb
= ldb_module_get_ctx(ar
->module
);
6179 return ldb_module_done(ar
->req
, NULL
, NULL
,
6180 LDB_ERR_OPERATIONS_ERROR
);
6182 if (ares
->error
!= LDB_SUCCESS
) {
6183 return ldb_module_done(ar
->req
, ares
->controls
,
6184 ares
->response
, ares
->error
);
6187 if (ares
->type
!= LDB_REPLY_DONE
) {
6188 ldb_asprintf_errstring(ldb
, "Invalid LDB reply type %d", ares
->type
);
6189 return ldb_module_done(ar
->req
, NULL
, NULL
,
6190 LDB_ERR_OPERATIONS_ERROR
);
6195 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_SUCCESS
);
6198 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request
*ar
)
6200 struct ldb_context
*ldb
;
6201 struct ldb_request
*change_req
;
6202 enum ndr_err_code ndr_err
;
6203 struct ldb_message
*msg
;
6204 struct replUpToDateVectorBlob ouv
;
6205 const struct ldb_val
*ouv_value
;
6206 const struct drsuapi_DsReplicaCursor2CtrEx
*ruv
;
6207 struct replUpToDateVectorBlob nuv
;
6208 struct ldb_val nuv_value
;
6209 struct ldb_message_element
*nuv_el
= NULL
;
6210 struct ldb_message_element
*orf_el
= NULL
;
6211 struct repsFromToBlob nrf
;
6212 struct ldb_val
*nrf_value
= NULL
;
6213 struct ldb_message_element
*nrf_el
= NULL
;
6217 time_t t
= time(NULL
);
6220 uint32_t instanceType
;
6222 ldb
= ldb_module_get_ctx(ar
->module
);
6223 ruv
= ar
->objs
->uptodateness_vector
;
6229 unix_to_nt_time(&now
, t
);
6231 if (ar
->search_msg
== NULL
) {
6232 /* this happens for a REPL_OBJ call where we are
6233 creating the target object by replicating it. The
6234 subdomain join code does this for the partition DN
6236 DEBUG(4,(__location__
": Skipping UDV and repsFrom update as no target DN\n"));
6237 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_SUCCESS
);
6240 instanceType
= ldb_msg_find_attr_as_uint(ar
->search_msg
, "instanceType", 0);
6241 if (! (instanceType
& INSTANCE_TYPE_IS_NC_HEAD
)) {
6242 DEBUG(4,(__location__
": Skipping UDV and repsFrom update as not NC root: %s\n",
6243 ldb_dn_get_linearized(ar
->search_msg
->dn
)));
6244 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_SUCCESS
);
6248 * first create the new replUpToDateVector
6250 ouv_value
= ldb_msg_find_ldb_val(ar
->search_msg
, "replUpToDateVector");
6252 ndr_err
= ndr_pull_struct_blob(ouv_value
, ar
, &ouv
,
6253 (ndr_pull_flags_fn_t
)ndr_pull_replUpToDateVectorBlob
);
6254 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
6255 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
6256 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
6259 if (ouv
.version
!= 2) {
6260 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
6265 * the new uptodateness vector will at least
6266 * contain 1 entry, one for the source_dsa
6268 * plus optional values from our old vector and the one from the source_dsa
6270 nuv
.ctr
.ctr2
.count
= ouv
.ctr
.ctr2
.count
;
6271 if (ruv
) nuv
.ctr
.ctr2
.count
+= ruv
->count
;
6272 nuv
.ctr
.ctr2
.cursors
= talloc_array(ar
,
6273 struct drsuapi_DsReplicaCursor2
,
6274 nuv
.ctr
.ctr2
.count
);
6275 if (!nuv
.ctr
.ctr2
.cursors
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
6277 /* first copy the old vector */
6278 for (i
=0; i
< ouv
.ctr
.ctr2
.count
; i
++) {
6279 nuv
.ctr
.ctr2
.cursors
[ni
] = ouv
.ctr
.ctr2
.cursors
[i
];
6283 /* merge in the source_dsa vector is available */
6284 for (i
=0; (ruv
&& i
< ruv
->count
); i
++) {
6287 if (GUID_equal(&ruv
->cursors
[i
].source_dsa_invocation_id
,
6288 &ar
->our_invocation_id
)) {
6292 for (j
=0; j
< ni
; j
++) {
6293 if (!GUID_equal(&ruv
->cursors
[i
].source_dsa_invocation_id
,
6294 &nuv
.ctr
.ctr2
.cursors
[j
].source_dsa_invocation_id
)) {
6300 if (ruv
->cursors
[i
].highest_usn
> nuv
.ctr
.ctr2
.cursors
[j
].highest_usn
) {
6301 nuv
.ctr
.ctr2
.cursors
[j
] = ruv
->cursors
[i
];
6306 if (found
) continue;
6308 /* if it's not there yet, add it */
6309 nuv
.ctr
.ctr2
.cursors
[ni
] = ruv
->cursors
[i
];
6314 * finally correct the size of the cursors array
6316 nuv
.ctr
.ctr2
.count
= ni
;
6321 TYPESAFE_QSORT(nuv
.ctr
.ctr2
.cursors
, nuv
.ctr
.ctr2
.count
, drsuapi_DsReplicaCursor2_compare
);
6324 * create the change ldb_message
6326 msg
= ldb_msg_new(ar
);
6327 if (!msg
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
6328 msg
->dn
= ar
->search_msg
->dn
;
6330 ndr_err
= ndr_push_struct_blob(&nuv_value
, msg
, &nuv
,
6331 (ndr_push_flags_fn_t
)ndr_push_replUpToDateVectorBlob
);
6332 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
6333 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
6334 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
6336 ret
= ldb_msg_add_value(msg
, "replUpToDateVector", &nuv_value
, &nuv_el
);
6337 if (ret
!= LDB_SUCCESS
) {
6338 return replmd_replicated_request_error(ar
, ret
);
6340 nuv_el
->flags
= LDB_FLAG_MOD_REPLACE
;
6343 * now create the new repsFrom value from the given repsFromTo1 structure
6347 nrf
.ctr
.ctr1
= *ar
->objs
->source_dsa
;
6348 nrf
.ctr
.ctr1
.last_attempt
= now
;
6349 nrf
.ctr
.ctr1
.last_success
= now
;
6350 nrf
.ctr
.ctr1
.result_last_attempt
= WERR_OK
;
6353 * first see if we already have a repsFrom value for the current source dsa
6354 * if so we'll later replace this value
6356 orf_el
= ldb_msg_find_element(ar
->search_msg
, "repsFrom");
6358 for (i
=0; i
< orf_el
->num_values
; i
++) {
6359 struct repsFromToBlob
*trf
;
6361 trf
= talloc(ar
, struct repsFromToBlob
);
6362 if (!trf
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
6364 ndr_err
= ndr_pull_struct_blob(&orf_el
->values
[i
], trf
, trf
,
6365 (ndr_pull_flags_fn_t
)ndr_pull_repsFromToBlob
);
6366 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
6367 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
6368 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
6371 if (trf
->version
!= 1) {
6372 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
6376 * we compare the source dsa objectGUID not the invocation_id
6377 * because we want only one repsFrom value per source dsa
6378 * and when the invocation_id of the source dsa has changed we don't need
6379 * the old repsFrom with the old invocation_id
6381 if (!GUID_equal(&trf
->ctr
.ctr1
.source_dsa_obj_guid
,
6382 &ar
->objs
->source_dsa
->source_dsa_obj_guid
)) {
6388 nrf_value
= &orf_el
->values
[i
];
6393 * copy over all old values to the new ldb_message
6395 ret
= ldb_msg_add_empty(msg
, "repsFrom", 0, &nrf_el
);
6396 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
6401 * if we haven't found an old repsFrom value for the current source dsa
6402 * we'll add a new value
6405 struct ldb_val zero_value
;
6406 ZERO_STRUCT(zero_value
);
6407 ret
= ldb_msg_add_value(msg
, "repsFrom", &zero_value
, &nrf_el
);
6408 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
6410 nrf_value
= &nrf_el
->values
[nrf_el
->num_values
- 1];
6413 /* we now fill the value which is already attached to ldb_message */
6414 ndr_err
= ndr_push_struct_blob(nrf_value
, msg
,
6416 (ndr_push_flags_fn_t
)ndr_push_repsFromToBlob
);
6417 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
6418 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
6419 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
6423 * the ldb_message_element for the attribute, has all the old values and the new one
6424 * so we'll replace the whole attribute with all values
6426 nrf_el
->flags
= LDB_FLAG_MOD_REPLACE
;
6428 if (CHECK_DEBUGLVL(4)) {
6429 char *s
= ldb_ldif_message_string(ldb
, ar
, LDB_CHANGETYPE_MODIFY
, msg
);
6430 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s
));
6434 /* prepare the ldb_modify() request */
6435 ret
= ldb_build_mod_req(&change_req
,
6441 replmd_replicated_uptodate_modify_callback
,
6443 LDB_REQ_SET_LOCATION(change_req
);
6444 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
6446 return ldb_next_request(ar
->module
, change_req
);
6449 static int replmd_replicated_uptodate_search_callback(struct ldb_request
*req
,
6450 struct ldb_reply
*ares
)
6452 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
6453 struct replmd_replicated_request
);
6457 return ldb_module_done(ar
->req
, NULL
, NULL
,
6458 LDB_ERR_OPERATIONS_ERROR
);
6460 if (ares
->error
!= LDB_SUCCESS
&&
6461 ares
->error
!= LDB_ERR_NO_SUCH_OBJECT
) {
6462 return ldb_module_done(ar
->req
, ares
->controls
,
6463 ares
->response
, ares
->error
);
6466 switch (ares
->type
) {
6467 case LDB_REPLY_ENTRY
:
6468 ar
->search_msg
= talloc_steal(ar
, ares
->message
);
6471 case LDB_REPLY_REFERRAL
:
6472 /* we ignore referrals */
6475 case LDB_REPLY_DONE
:
6476 ret
= replmd_replicated_uptodate_modify(ar
);
6477 if (ret
!= LDB_SUCCESS
) {
6478 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
6487 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request
*ar
)
6489 struct ldb_context
*ldb
= ldb_module_get_ctx(ar
->module
);
6490 struct replmd_private
*replmd_private
=
6491 talloc_get_type_abort(ldb_module_get_private(ar
->module
),
6492 struct replmd_private
);
6494 static const char *attrs
[] = {
6495 "replUpToDateVector",
6500 struct ldb_request
*search_req
;
6502 ar
->search_msg
= NULL
;
6505 * Let the caller know that we did an originating updates
6507 ar
->objs
->originating_updates
= replmd_private
->originating_updates
;
6509 ret
= ldb_build_search_req(&search_req
,
6512 ar
->objs
->partition_dn
,
6518 replmd_replicated_uptodate_search_callback
,
6520 LDB_REQ_SET_LOCATION(search_req
);
6521 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
6523 return ldb_next_request(ar
->module
, search_req
);
6528 static int replmd_extended_replicated_objects(struct ldb_module
*module
, struct ldb_request
*req
)
6530 struct ldb_context
*ldb
;
6531 struct dsdb_extended_replicated_objects
*objs
;
6532 struct replmd_replicated_request
*ar
;
6533 struct ldb_control
**ctrls
;
6536 struct replmd_private
*replmd_private
=
6537 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
6539 ldb
= ldb_module_get_ctx(module
);
6541 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_extended_replicated_objects\n");
6543 objs
= talloc_get_type(req
->op
.extended
.data
, struct dsdb_extended_replicated_objects
);
6545 ldb_debug(ldb
, LDB_DEBUG_FATAL
, "replmd_extended_replicated_objects: invalid extended data\n");
6546 return LDB_ERR_PROTOCOL_ERROR
;
6549 if (objs
->version
!= DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION
) {
6550 ldb_debug(ldb
, LDB_DEBUG_FATAL
, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
6551 objs
->version
, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION
);
6552 return LDB_ERR_PROTOCOL_ERROR
;
6555 ar
= replmd_ctx_init(module
, req
);
6557 return LDB_ERR_OPERATIONS_ERROR
;
6559 /* Set the flags to have the replmd_op_callback run over the full set of objects */
6560 ar
->apply_mode
= true;
6562 ar
->schema
= dsdb_get_schema(ldb
, ar
);
6564 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
, "replmd_ctx_init: no loaded schema found\n");
6566 DEBUG(0,(__location__
": %s\n", ldb_errstring(ldb
)));
6567 return LDB_ERR_CONSTRAINT_VIOLATION
;
6570 ctrls
= req
->controls
;
6572 if (req
->controls
) {
6573 req
->controls
= talloc_memdup(ar
, req
->controls
,
6574 talloc_get_size(req
->controls
));
6575 if (!req
->controls
) return replmd_replicated_request_werror(ar
, WERR_NOT_ENOUGH_MEMORY
);
6578 ret
= ldb_request_add_control(req
, DSDB_CONTROL_REPLICATED_UPDATE_OID
, false, NULL
);
6579 if (ret
!= LDB_SUCCESS
) {
6583 /* If this change contained linked attributes in the body
6584 * (rather than in the links section) we need to update
6585 * backlinks in linked_attributes */
6586 ret
= ldb_request_add_control(req
, DSDB_CONTROL_APPLY_LINKS
, false, NULL
);
6587 if (ret
!= LDB_SUCCESS
) {
6591 ar
->controls
= req
->controls
;
6592 req
->controls
= ctrls
;
6594 DEBUG(4,("linked_attributes_count=%u\n", objs
->linked_attributes_count
));
6596 /* save away the linked attributes for the end of the
6598 for (i
=0; i
<ar
->objs
->linked_attributes_count
; i
++) {
6599 struct la_entry
*la_entry
;
6601 if (replmd_private
->la_ctx
== NULL
) {
6602 replmd_private
->la_ctx
= talloc_new(replmd_private
);
6604 la_entry
= talloc(replmd_private
->la_ctx
, struct la_entry
);
6605 if (la_entry
== NULL
) {
6607 return LDB_ERR_OPERATIONS_ERROR
;
6609 la_entry
->la
= talloc(la_entry
, struct drsuapi_DsReplicaLinkedAttribute
);
6610 if (la_entry
->la
== NULL
) {
6611 talloc_free(la_entry
);
6613 return LDB_ERR_OPERATIONS_ERROR
;
6615 *la_entry
->la
= ar
->objs
->linked_attributes
[i
];
6617 /* we need to steal the non-scalars so they stay
6618 around until the end of the transaction */
6619 talloc_steal(la_entry
->la
, la_entry
->la
->identifier
);
6620 talloc_steal(la_entry
->la
, la_entry
->la
->value
.blob
);
6622 DLIST_ADD(replmd_private
->la_list
, la_entry
);
6625 return replmd_replicated_apply_next(ar
);
6629 process one linked attribute structure
6631 static int replmd_process_linked_attribute(struct ldb_module
*module
,
6632 struct replmd_private
*replmd_private
,
6633 struct la_entry
*la_entry
,
6634 struct ldb_request
*parent
)
6636 struct drsuapi_DsReplicaLinkedAttribute
*la
= la_entry
->la
;
6637 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
6638 struct ldb_message
*msg
;
6639 struct ldb_message
*target_msg
= NULL
;
6640 TALLOC_CTX
*tmp_ctx
= talloc_new(la_entry
);
6641 const struct dsdb_schema
*schema
= dsdb_get_schema(ldb
, tmp_ctx
);
6643 const struct dsdb_attribute
*attr
;
6644 struct dsdb_dn
*dsdb_dn
;
6645 uint64_t seq_num
= 0;
6646 struct ldb_message_element
*old_el
;
6648 time_t t
= time(NULL
);
6649 struct ldb_result
*res
;
6650 struct ldb_result
*target_res
;
6651 const char *attrs
[4];
6652 const char *attrs2
[] = { "isDeleted", "isRecycled", NULL
};
6653 struct parsed_dn
*pdn_list
, *pdn
, *next
;
6654 struct GUID guid
= GUID_zero();
6656 bool active
= (la
->flags
& DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
)?true:false;
6658 enum deletion_state deletion_state
= OBJECT_NOT_DELETED
;
6659 enum deletion_state target_deletion_state
= OBJECT_NOT_DELETED
;
6662 linked_attributes[0]:
6663 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
6665 identifier: struct drsuapi_DsReplicaObjectIdentifier
6666 __ndr_size : 0x0000003a (58)
6667 __ndr_size_sid : 0x00000000 (0)
6668 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
6670 __ndr_size_dn : 0x00000000 (0)
6672 attid : DRSUAPI_ATTID_member (0x1F)
6673 value: struct drsuapi_DsAttributeValue
6674 __ndr_size : 0x0000007e (126)
6676 blob : DATA_BLOB length=126
6677 flags : 0x00000001 (1)
6678 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
6679 originating_add_time : Wed Sep 2 22:20:01 2009 EST
6680 meta_data: struct drsuapi_DsReplicaMetaData
6681 version : 0x00000015 (21)
6682 originating_change_time : Wed Sep 2 23:39:07 2009 EST
6683 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
6684 originating_usn : 0x000000000001e19c (123292)
6686 (for cases where the link is to a normal DN)
6687 &target: struct drsuapi_DsReplicaObjectIdentifier3
6688 __ndr_size : 0x0000007e (126)
6689 __ndr_size_sid : 0x0000001c (28)
6690 guid : 7639e594-db75-4086-b0d4-67890ae46031
6691 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
6692 __ndr_size_dn : 0x00000022 (34)
6693 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
6696 /* find the attribute being modified */
6697 attr
= dsdb_attribute_by_attributeID_id(schema
, la
->attid
);
6699 struct GUID_txt_buf guid_str
;
6700 ldb_asprintf_errstring(ldb
, "Unable to find attributeID 0x%x for link on <GUID=%s>",
6702 GUID_buf_string(&la
->identifier
->guid
,
6704 talloc_free(tmp_ctx
);
6705 return LDB_ERR_OPERATIONS_ERROR
;
6708 attrs
[0] = attr
->lDAPDisplayName
;
6709 attrs
[1] = "isDeleted";
6710 attrs
[2] = "isRecycled";
6713 /* get the existing message from the db for the object with
6714 this GUID, returning attribute being modified. We will then
6715 use this msg as the basis for a modify call */
6716 ret
= dsdb_module_search(module
, tmp_ctx
, &res
, NULL
, LDB_SCOPE_SUBTREE
, attrs
,
6717 DSDB_FLAG_NEXT_MODULE
|
6718 DSDB_SEARCH_SEARCH_ALL_PARTITIONS
|
6719 DSDB_SEARCH_SHOW_RECYCLED
|
6720 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
|
6721 DSDB_SEARCH_REVEAL_INTERNALS
,
6723 "objectGUID=%s", GUID_string(tmp_ctx
, &la
->identifier
->guid
));
6724 if (ret
!= LDB_SUCCESS
) {
6725 talloc_free(tmp_ctx
);
6728 if (res
->count
!= 1) {
6729 ldb_asprintf_errstring(ldb
, "DRS linked attribute for GUID %s - DN not found",
6730 GUID_string(tmp_ctx
, &la
->identifier
->guid
));
6731 talloc_free(tmp_ctx
);
6732 return LDB_ERR_NO_SUCH_OBJECT
;
6737 * Check for deleted objects per MS-DRSR 4.1.10.6.13
6738 * ProcessLinkValue, because link updates are not applied to
6739 * recycled and tombstone objects. We don't have to delete
6740 * any existing link, that should have happened when the
6741 * object deletion was replicated or initiated.
6744 replmd_deletion_state(module
, msg
, &deletion_state
, NULL
);
6746 if (deletion_state
>= OBJECT_RECYCLED
) {
6747 talloc_free(tmp_ctx
);
6751 old_el
= ldb_msg_find_element(msg
, attr
->lDAPDisplayName
);
6752 if (old_el
== NULL
) {
6753 ret
= ldb_msg_add_empty(msg
, attr
->lDAPDisplayName
, LDB_FLAG_MOD_REPLACE
, &old_el
);
6754 if (ret
!= LDB_SUCCESS
) {
6755 ldb_module_oom(module
);
6756 talloc_free(tmp_ctx
);
6757 return LDB_ERR_OPERATIONS_ERROR
;
6760 old_el
->flags
= LDB_FLAG_MOD_REPLACE
;
6763 /* parse the existing links */
6764 ret
= get_parsed_dns_trusted(module
, replmd_private
, tmp_ctx
, old_el
, &pdn_list
,
6765 attr
->syntax
->ldap_oid
, parent
);
6767 if (ret
!= LDB_SUCCESS
) {
6768 talloc_free(tmp_ctx
);
6772 status
= dsdb_dn_la_from_blob(ldb
, attr
, schema
, tmp_ctx
, la
->value
.blob
, &dsdb_dn
);
6773 if (!W_ERROR_IS_OK(status
)) {
6774 ldb_asprintf_errstring(ldb
, "Failed to parsed linked attribute blob for %s on %s - %s\n",
6775 old_el
->name
, ldb_dn_get_linearized(msg
->dn
), win_errstr(status
));
6776 talloc_free(tmp_ctx
);
6777 return LDB_ERR_OPERATIONS_ERROR
;
6780 ntstatus
= dsdb_get_extended_dn_guid(dsdb_dn
->dn
, &guid
, "GUID");
6781 if (!NT_STATUS_IS_OK(ntstatus
) && !active
) {
6783 * This strange behaviour (allowing a NULL/missing
6784 * GUID) originally comes from:
6786 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
6787 * Author: Andrew Tridgell <tridge@samba.org>
6788 * Date: Mon Dec 21 21:21:55 2009 +1100
6790 * s4-drs: cope better with NULL GUIDS from DRS
6792 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
6793 * need to match by DN if possible when seeing if we should update an
6796 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
6799 ret
= dsdb_module_search_dn(module
, tmp_ctx
, &target_res
,
6800 dsdb_dn
->dn
, attrs2
,
6801 DSDB_FLAG_NEXT_MODULE
|
6802 DSDB_SEARCH_SHOW_RECYCLED
|
6803 DSDB_SEARCH_SEARCH_ALL_PARTITIONS
|
6804 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
,
6806 } else if (!NT_STATUS_IS_OK(ntstatus
)) {
6807 ldb_asprintf_errstring(ldb
, "Failed to find GUID in linked attribute blob for %s on %s from %s",
6809 ldb_dn_get_linearized(dsdb_dn
->dn
),
6810 ldb_dn_get_linearized(msg
->dn
));
6811 talloc_free(tmp_ctx
);
6812 return LDB_ERR_OPERATIONS_ERROR
;
6814 ret
= dsdb_module_search(module
, tmp_ctx
, &target_res
,
6815 NULL
, LDB_SCOPE_SUBTREE
,
6817 DSDB_FLAG_NEXT_MODULE
|
6818 DSDB_SEARCH_SHOW_RECYCLED
|
6819 DSDB_SEARCH_SEARCH_ALL_PARTITIONS
|
6820 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
,
6823 GUID_string(tmp_ctx
, &guid
));
6826 if (ret
!= LDB_SUCCESS
) {
6827 ldb_asprintf_errstring(ldb_module_get_ctx(module
), "Failed to re-resolve GUID %s: %s\n",
6828 GUID_string(tmp_ctx
, &guid
),
6829 ldb_errstring(ldb_module_get_ctx(module
)));
6830 talloc_free(tmp_ctx
);
6834 if (target_res
->count
== 0) {
6835 DEBUG(2,(__location__
": WARNING: Failed to re-resolve GUID %s - using %s\n",
6836 GUID_string(tmp_ctx
, &guid
),
6837 ldb_dn_get_linearized(dsdb_dn
->dn
)));
6838 } else if (target_res
->count
!= 1) {
6839 ldb_asprintf_errstring(ldb_module_get_ctx(module
), "More than one object found matching objectGUID %s\n",
6840 GUID_string(tmp_ctx
, &guid
));
6841 talloc_free(tmp_ctx
);
6842 return LDB_ERR_OPERATIONS_ERROR
;
6844 target_msg
= target_res
->msgs
[0];
6845 dsdb_dn
->dn
= talloc_steal(dsdb_dn
, target_msg
->dn
);
6849 * Check for deleted objects per MS-DRSR 4.1.10.6.13
6850 * ProcessLinkValue, because link updates are not applied to
6851 * recycled and tombstone objects. We don't have to delete
6852 * any existing link, that should have happened when the
6853 * object deletion was replicated or initiated.
6855 replmd_deletion_state(module
, target_msg
,
6856 &target_deletion_state
, NULL
);
6858 if (target_deletion_state
>= OBJECT_RECYCLED
) {
6859 talloc_free(tmp_ctx
);
6863 /* see if this link already exists */
6864 ret
= parsed_dn_find(ldb
, pdn_list
, old_el
->num_values
,
6868 attr
->syntax
->ldap_oid
);
6869 if (ret
!= LDB_SUCCESS
) {
6870 talloc_free(tmp_ctx
);
6876 /* see if this update is newer than what we have already */
6877 struct GUID invocation_id
= GUID_zero();
6878 uint32_t version
= 0;
6879 uint32_t originating_usn
= 0;
6880 NTTIME change_time
= 0;
6881 uint32_t rmd_flags
= dsdb_dn_rmd_flags(pdn
->dsdb_dn
->dn
);
6883 dsdb_get_extended_dn_guid(pdn
->dsdb_dn
->dn
, &invocation_id
, "RMD_INVOCID");
6884 dsdb_get_extended_dn_uint32(pdn
->dsdb_dn
->dn
, &version
, "RMD_VERSION");
6885 dsdb_get_extended_dn_uint32(pdn
->dsdb_dn
->dn
, &originating_usn
, "RMD_ORIGINATING_USN");
6886 dsdb_get_extended_dn_nttime(pdn
->dsdb_dn
->dn
, &change_time
, "RMD_CHANGETIME");
6888 if (!replmd_update_is_newer(&invocation_id
,
6889 &la
->meta_data
.originating_invocation_id
,
6891 la
->meta_data
.version
,
6893 la
->meta_data
.originating_change_time
)) {
6894 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
6895 old_el
->name
, ldb_dn_get_linearized(msg
->dn
),
6896 GUID_string(tmp_ctx
, &la
->meta_data
.originating_invocation_id
)));
6897 talloc_free(tmp_ctx
);
6901 /* get a seq_num for this change */
6902 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &seq_num
);
6903 if (ret
!= LDB_SUCCESS
) {
6904 talloc_free(tmp_ctx
);
6908 if (!(rmd_flags
& DSDB_RMD_FLAG_DELETED
)) {
6909 /* remove the existing backlink */
6910 ret
= replmd_add_backlink(module
, replmd_private
,
6911 schema
, &la
->identifier
->guid
,
6912 &guid
, false, attr
, true);
6913 if (ret
!= LDB_SUCCESS
) {
6914 talloc_free(tmp_ctx
);
6919 ret
= replmd_update_la_val(tmp_ctx
, pdn
->v
, dsdb_dn
, pdn
->dsdb_dn
,
6920 &la
->meta_data
.originating_invocation_id
,
6921 la
->meta_data
.originating_usn
, seq_num
,
6922 la
->meta_data
.originating_change_time
,
6923 la
->meta_data
.version
,
6925 if (ret
!= LDB_SUCCESS
) {
6926 talloc_free(tmp_ctx
);
6931 /* add the new backlink */
6932 ret
= replmd_add_backlink(module
, replmd_private
,
6933 schema
, &la
->identifier
->guid
,
6934 &guid
, true, attr
, true);
6935 if (ret
!= LDB_SUCCESS
) {
6936 talloc_free(tmp_ctx
);
6942 /* get a seq_num for this change */
6943 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &seq_num
);
6944 if (ret
!= LDB_SUCCESS
) {
6945 talloc_free(tmp_ctx
);
6949 * We know where the new one needs to be, from the *next
6950 * pointer into pdn_list.
6953 offset
= old_el
->num_values
;
6955 if (next
->dsdb_dn
== NULL
) {
6956 ret
= really_parse_trusted_dn(tmp_ctx
, ldb
, next
,
6957 attr
->syntax
->ldap_oid
);
6958 if (ret
!= LDB_SUCCESS
) {
6962 offset
= next
- pdn_list
;
6963 if (offset
> old_el
->num_values
) {
6964 talloc_free(tmp_ctx
);
6965 return LDB_ERR_OPERATIONS_ERROR
;
6969 old_el
->values
= talloc_realloc(msg
->elements
, old_el
->values
,
6970 struct ldb_val
, old_el
->num_values
+1);
6971 if (!old_el
->values
) {
6972 ldb_module_oom(module
);
6973 return LDB_ERR_OPERATIONS_ERROR
;
6976 if (offset
!= old_el
->num_values
) {
6977 memmove(&old_el
->values
[offset
+ 1], &old_el
->values
[offset
],
6978 (old_el
->num_values
- offset
) * sizeof(old_el
->values
[0]));
6981 old_el
->num_values
++;
6983 ret
= replmd_build_la_val(tmp_ctx
, &old_el
->values
[offset
], dsdb_dn
,
6984 &la
->meta_data
.originating_invocation_id
,
6985 la
->meta_data
.originating_usn
, seq_num
,
6986 la
->meta_data
.originating_change_time
,
6987 la
->meta_data
.version
,
6989 if (ret
!= LDB_SUCCESS
) {
6990 talloc_free(tmp_ctx
);
6995 ret
= replmd_add_backlink(module
, replmd_private
,
6996 schema
, &la
->identifier
->guid
,
6997 &guid
, true, attr
, true);
6998 if (ret
!= LDB_SUCCESS
) {
6999 talloc_free(tmp_ctx
);
7005 /* we only change whenChanged and uSNChanged if the seq_num
7007 ret
= add_time_element(msg
, "whenChanged", t
);
7008 if (ret
!= LDB_SUCCESS
) {
7009 talloc_free(tmp_ctx
);
7014 ret
= add_uint64_element(ldb
, msg
, "uSNChanged", seq_num
);
7015 if (ret
!= LDB_SUCCESS
) {
7016 talloc_free(tmp_ctx
);
7021 old_el
= ldb_msg_find_element(msg
, attr
->lDAPDisplayName
);
7022 if (old_el
== NULL
) {
7023 talloc_free(tmp_ctx
);
7024 return ldb_operr(ldb
);
7027 ret
= dsdb_check_single_valued_link(attr
, old_el
);
7028 if (ret
!= LDB_SUCCESS
) {
7029 talloc_free(tmp_ctx
);
7033 old_el
->flags
|= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK
;
7035 ret
= linked_attr_modify(module
, msg
, parent
);
7036 if (ret
!= LDB_SUCCESS
) {
7037 ldb_debug(ldb
, LDB_DEBUG_WARNING
, "Failed to apply linked attribute change '%s'\n%s\n",
7039 ldb_ldif_message_string(ldb
, tmp_ctx
, LDB_CHANGETYPE_MODIFY
, msg
));
7040 talloc_free(tmp_ctx
);
7044 talloc_free(tmp_ctx
);
7049 static int replmd_extended(struct ldb_module
*module
, struct ldb_request
*req
)
7051 if (strcmp(req
->op
.extended
.oid
, DSDB_EXTENDED_REPLICATED_OBJECTS_OID
) == 0) {
7052 return replmd_extended_replicated_objects(module
, req
);
7055 return ldb_next_request(module
, req
);
7060 we hook into the transaction operations to allow us to
7061 perform the linked attribute updates at the end of the whole
7062 transaction. This allows a forward linked attribute to be created
7063 before the object is created. During a vampire, w2k8 sends us linked
7064 attributes before the objects they are part of.
7066 static int replmd_start_transaction(struct ldb_module
*module
)
7068 /* create our private structure for this transaction */
7069 struct replmd_private
*replmd_private
= talloc_get_type(ldb_module_get_private(module
),
7070 struct replmd_private
);
7071 replmd_txn_cleanup(replmd_private
);
7073 /* free any leftover mod_usn records from cancelled
7075 while (replmd_private
->ncs
) {
7076 struct nc_entry
*e
= replmd_private
->ncs
;
7077 DLIST_REMOVE(replmd_private
->ncs
, e
);
7081 replmd_private
->originating_updates
= false;
7083 return ldb_next_start_trans(module
);
7087 on prepare commit we loop over our queued la_context structures and
7090 static int replmd_prepare_commit(struct ldb_module
*module
)
7092 struct replmd_private
*replmd_private
=
7093 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
7094 struct la_entry
*la
, *prev
;
7095 struct la_backlink
*bl
;
7098 /* walk the list backwards, to do the first entry first, as we
7099 * added the entries with DLIST_ADD() which puts them at the
7100 * start of the list */
7101 for (la
= DLIST_TAIL(replmd_private
->la_list
); la
; la
=prev
) {
7102 prev
= DLIST_PREV(la
);
7103 DLIST_REMOVE(replmd_private
->la_list
, la
);
7104 ret
= replmd_process_linked_attribute(module
, replmd_private
,
7106 if (ret
!= LDB_SUCCESS
) {
7107 replmd_txn_cleanup(replmd_private
);
7112 /* process our backlink list, creating and deleting backlinks
7114 for (bl
=replmd_private
->la_backlinks
; bl
; bl
=bl
->next
) {
7115 ret
= replmd_process_backlink(module
, bl
, NULL
);
7116 if (ret
!= LDB_SUCCESS
) {
7117 replmd_txn_cleanup(replmd_private
);
7122 replmd_txn_cleanup(replmd_private
);
7124 /* possibly change @REPLCHANGED */
7125 ret
= replmd_notify_store(module
, NULL
);
7126 if (ret
!= LDB_SUCCESS
) {
7130 return ldb_next_prepare_commit(module
);
7133 static int replmd_del_transaction(struct ldb_module
*module
)
7135 struct replmd_private
*replmd_private
=
7136 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
7137 replmd_txn_cleanup(replmd_private
);
7139 return ldb_next_del_trans(module
);
7143 static const struct ldb_module_ops ldb_repl_meta_data_module_ops
= {
7144 .name
= "repl_meta_data",
7145 .init_context
= replmd_init
,
7147 .modify
= replmd_modify
,
7148 .rename
= replmd_rename
,
7149 .del
= replmd_delete
,
7150 .extended
= replmd_extended
,
7151 .start_transaction
= replmd_start_transaction
,
7152 .prepare_commit
= replmd_prepare_commit
,
7153 .del_transaction
= replmd_del_transaction
,
7156 int ldb_repl_meta_data_module_init(const char *version
)
7158 LDB_MODULE_CHECK_VERSION(version
);
7159 return ldb_register_module(&ldb_repl_meta_data_module_ops
);