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
;
73 struct la_entry
*next
, *prev
;
74 struct drsuapi_DsReplicaLinkedAttribute
*la
;
77 struct replmd_replicated_request
{
78 struct ldb_module
*module
;
79 struct ldb_request
*req
;
81 const struct dsdb_schema
*schema
;
83 /* the controls we pass down */
84 struct ldb_control
**controls
;
86 /* details for the mode where we apply a bunch of inbound replication meessages */
88 uint32_t index_current
;
89 struct dsdb_extended_replicated_objects
*objs
;
91 struct ldb_message
*search_msg
;
99 static int replmd_replicated_apply_merge(struct replmd_replicated_request
*ar
);
100 static int replmd_delete_internals(struct ldb_module
*module
, struct ldb_request
*req
, bool re_delete
);
102 enum urgent_situation
{
103 REPL_URGENT_ON_CREATE
= 1,
104 REPL_URGENT_ON_UPDATE
= 2,
105 REPL_URGENT_ON_DELETE
= 4
108 enum deletion_state
{
109 OBJECT_NOT_DELETED
=1,
116 static void replmd_deletion_state(struct ldb_module
*module
,
117 const struct ldb_message
*msg
,
118 enum deletion_state
*current_state
,
119 enum deletion_state
*next_state
)
122 bool enabled
= false;
125 *current_state
= OBJECT_REMOVED
;
126 if (next_state
!= NULL
) {
127 *next_state
= OBJECT_REMOVED
;
132 ret
= dsdb_recyclebin_enabled(module
, &enabled
);
133 if (ret
!= LDB_SUCCESS
) {
137 if (ldb_msg_check_string_attribute(msg
, "isDeleted", "TRUE")) {
139 *current_state
= OBJECT_TOMBSTONE
;
140 if (next_state
!= NULL
) {
141 *next_state
= OBJECT_REMOVED
;
146 if (ldb_msg_check_string_attribute(msg
, "isRecycled", "TRUE")) {
147 *current_state
= OBJECT_RECYCLED
;
148 if (next_state
!= NULL
) {
149 *next_state
= OBJECT_REMOVED
;
154 *current_state
= OBJECT_DELETED
;
155 if (next_state
!= NULL
) {
156 *next_state
= OBJECT_RECYCLED
;
161 *current_state
= OBJECT_NOT_DELETED
;
162 if (next_state
== NULL
) {
167 *next_state
= OBJECT_DELETED
;
169 *next_state
= OBJECT_TOMBSTONE
;
173 static const struct {
174 const char *update_name
;
175 enum urgent_situation repl_situation
;
176 } urgent_objects
[] = {
177 {"nTDSDSA", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_DELETE
)},
178 {"crossRef", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_DELETE
)},
179 {"attributeSchema", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_UPDATE
)},
180 {"classSchema", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_UPDATE
)},
181 {"secret", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_UPDATE
)},
182 {"rIDManager", (REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_UPDATE
)},
186 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
187 static const char *urgent_attrs
[] = {
190 "userAccountControl",
195 static bool replmd_check_urgent_objectclass(const struct ldb_message_element
*objectclass_el
,
196 enum urgent_situation situation
)
199 for (i
=0; urgent_objects
[i
].update_name
; i
++) {
201 if ((situation
& urgent_objects
[i
].repl_situation
) == 0) {
205 for (j
=0; j
<objectclass_el
->num_values
; j
++) {
206 const struct ldb_val
*v
= &objectclass_el
->values
[j
];
207 if (ldb_attr_cmp((const char *)v
->data
, urgent_objects
[i
].update_name
) == 0) {
215 static bool replmd_check_urgent_attribute(const struct ldb_message_element
*el
)
217 if (ldb_attr_in_list(urgent_attrs
, el
->name
)) {
224 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request
*ar
);
227 initialise the module
228 allocate the private structure and build the list
229 of partition DNs for use by replmd_notify()
231 static int replmd_init(struct ldb_module
*module
)
233 struct replmd_private
*replmd_private
;
234 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
236 replmd_private
= talloc_zero(module
, struct replmd_private
);
237 if (replmd_private
== NULL
) {
239 return LDB_ERR_OPERATIONS_ERROR
;
241 ldb_module_set_private(module
, replmd_private
);
243 return ldb_next_init(module
);
247 cleanup our per-transaction contexts
249 static void replmd_txn_cleanup(struct replmd_private
*replmd_private
)
251 talloc_free(replmd_private
->la_ctx
);
252 replmd_private
->la_list
= NULL
;
253 replmd_private
->la_ctx
= NULL
;
255 talloc_free(replmd_private
->bl_ctx
);
256 replmd_private
->la_backlinks
= NULL
;
257 replmd_private
->bl_ctx
= NULL
;
262 struct la_backlink
*next
, *prev
;
263 const char *attr_name
;
264 struct GUID forward_guid
, target_guid
;
269 process a backlinks we accumulated during a transaction, adding and
270 deleting the backlinks from the target objects
272 static int replmd_process_backlink(struct ldb_module
*module
, struct la_backlink
*bl
, struct ldb_request
*parent
)
274 struct ldb_dn
*target_dn
, *source_dn
;
276 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
277 struct ldb_message
*msg
;
278 TALLOC_CTX
*tmp_ctx
= talloc_new(bl
);
284 - construct ldb_message
285 - either an add or a delete
287 ret
= dsdb_module_dn_by_guid(module
, tmp_ctx
, &bl
->target_guid
, &target_dn
, parent
);
288 if (ret
!= LDB_SUCCESS
) {
289 DEBUG(2,(__location__
": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
290 GUID_string(bl
, &bl
->target_guid
)));
294 ret
= dsdb_module_dn_by_guid(module
, tmp_ctx
, &bl
->forward_guid
, &source_dn
, parent
);
295 if (ret
!= LDB_SUCCESS
) {
296 ldb_asprintf_errstring(ldb
, "Failed to find source DN for linked attribute with GUID %s\n",
297 GUID_string(bl
, &bl
->forward_guid
));
298 talloc_free(tmp_ctx
);
302 msg
= ldb_msg_new(tmp_ctx
);
304 ldb_module_oom(module
);
305 talloc_free(tmp_ctx
);
306 return LDB_ERR_OPERATIONS_ERROR
;
309 /* construct a ldb_message for adding/deleting the backlink */
311 dn_string
= ldb_dn_get_extended_linearized(tmp_ctx
, source_dn
, 1);
313 ldb_module_oom(module
);
314 talloc_free(tmp_ctx
);
315 return LDB_ERR_OPERATIONS_ERROR
;
317 ret
= ldb_msg_add_steal_string(msg
, bl
->attr_name
, dn_string
);
318 if (ret
!= LDB_SUCCESS
) {
319 talloc_free(tmp_ctx
);
322 msg
->elements
[0].flags
= bl
->active
?LDB_FLAG_MOD_ADD
:LDB_FLAG_MOD_DELETE
;
324 /* a backlink should never be single valued. Unfortunately the
325 exchange schema has a attribute
326 msExchBridgeheadedLocalConnectorsDNBL which is single
327 valued and a backlink. We need to cope with that by
328 ignoring the single value flag */
329 msg
->elements
[0].flags
|= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK
;
331 ret
= dsdb_module_modify(module
, msg
, DSDB_FLAG_NEXT_MODULE
, parent
);
332 if (ret
== LDB_ERR_NO_SUCH_ATTRIBUTE
&& !bl
->active
) {
333 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
334 cope with possible corruption where the backlink has
335 already been removed */
336 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
337 ldb_dn_get_linearized(target_dn
),
338 ldb_dn_get_linearized(source_dn
),
339 ldb_errstring(ldb
)));
341 } else if (ret
!= LDB_SUCCESS
) {
342 ldb_asprintf_errstring(ldb
, "Failed to %s backlink from %s to %s - %s",
343 bl
->active
?"add":"remove",
344 ldb_dn_get_linearized(source_dn
),
345 ldb_dn_get_linearized(target_dn
),
347 talloc_free(tmp_ctx
);
350 talloc_free(tmp_ctx
);
355 add a backlink to the list of backlinks to add/delete in the prepare
358 static int replmd_add_backlink(struct ldb_module
*module
, const struct dsdb_schema
*schema
,
359 struct GUID
*forward_guid
, struct GUID
*target_guid
,
360 bool active
, const struct dsdb_attribute
*schema_attr
, bool immediate
)
362 const struct dsdb_attribute
*target_attr
;
363 struct la_backlink
*bl
;
364 struct replmd_private
*replmd_private
=
365 talloc_get_type_abort(ldb_module_get_private(module
), struct replmd_private
);
367 target_attr
= dsdb_attribute_by_linkID(schema
, schema_attr
->linkID
^ 1);
370 * windows 2003 has a broken schema where the
371 * definition of msDS-IsDomainFor is missing (which is
372 * supposed to be the backlink of the
373 * msDS-HasDomainNCs attribute
378 /* see if its already in the list */
379 for (bl
=replmd_private
->la_backlinks
; bl
; bl
=bl
->next
) {
380 if (GUID_equal(forward_guid
, &bl
->forward_guid
) &&
381 GUID_equal(target_guid
, &bl
->target_guid
) &&
382 (target_attr
->lDAPDisplayName
== bl
->attr_name
||
383 strcmp(target_attr
->lDAPDisplayName
, bl
->attr_name
) == 0)) {
389 /* we found an existing one */
390 if (bl
->active
== active
) {
393 DLIST_REMOVE(replmd_private
->la_backlinks
, bl
);
398 if (replmd_private
->bl_ctx
== NULL
) {
399 replmd_private
->bl_ctx
= talloc_new(replmd_private
);
400 if (replmd_private
->bl_ctx
== NULL
) {
401 ldb_module_oom(module
);
402 return LDB_ERR_OPERATIONS_ERROR
;
407 bl
= talloc(replmd_private
->bl_ctx
, struct la_backlink
);
409 ldb_module_oom(module
);
410 return LDB_ERR_OPERATIONS_ERROR
;
413 /* Ensure the schema does not go away before the bl->attr_name is used */
414 if (!talloc_reference(bl
, schema
)) {
416 ldb_module_oom(module
);
417 return LDB_ERR_OPERATIONS_ERROR
;
420 bl
->attr_name
= target_attr
->lDAPDisplayName
;
421 bl
->forward_guid
= *forward_guid
;
422 bl
->target_guid
= *target_guid
;
425 /* the caller may ask for this backlink to be processed
428 int ret
= replmd_process_backlink(module
, bl
, NULL
);
433 DLIST_ADD(replmd_private
->la_backlinks
, bl
);
440 * Callback for most write operations in this module:
442 * notify the repl task that a object has changed. The notifies are
443 * gathered up in the replmd_private structure then written to the
444 * @REPLCHANGED object in each partition during the prepare_commit
446 static int replmd_op_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
449 struct replmd_replicated_request
*ac
=
450 talloc_get_type_abort(req
->context
, struct replmd_replicated_request
);
451 struct replmd_private
*replmd_private
=
452 talloc_get_type_abort(ldb_module_get_private(ac
->module
), struct replmd_private
);
453 struct nc_entry
*modified_partition
;
454 struct ldb_control
*partition_ctrl
;
455 const struct dsdb_control_current_partition
*partition
;
457 struct ldb_control
**controls
;
459 partition_ctrl
= ldb_reply_get_control(ares
, DSDB_CONTROL_CURRENT_PARTITION_OID
);
461 controls
= ares
->controls
;
462 if (ldb_request_get_control(ac
->req
,
463 DSDB_CONTROL_CURRENT_PARTITION_OID
) == NULL
) {
465 * Remove the current partition control from what we pass up
466 * the chain if it hasn't been requested manually.
468 controls
= ldb_controls_except_specified(ares
->controls
, ares
,
472 if (ares
->error
!= LDB_SUCCESS
) {
473 DEBUG(5,("%s failure. Error is: %s\n", __FUNCTION__
, ldb_strerror(ares
->error
)));
474 return ldb_module_done(ac
->req
, controls
,
475 ares
->response
, ares
->error
);
478 if (ares
->type
!= LDB_REPLY_DONE
) {
479 ldb_set_errstring(ldb_module_get_ctx(ac
->module
), "Invalid reply type for notify\n!");
480 return ldb_module_done(ac
->req
, NULL
,
481 NULL
, LDB_ERR_OPERATIONS_ERROR
);
484 if (!partition_ctrl
) {
485 ldb_set_errstring(ldb_module_get_ctx(ac
->module
),"No partition control on reply");
486 return ldb_module_done(ac
->req
, NULL
,
487 NULL
, LDB_ERR_OPERATIONS_ERROR
);
490 partition
= talloc_get_type_abort(partition_ctrl
->data
,
491 struct dsdb_control_current_partition
);
493 if (ac
->seq_num
> 0) {
494 for (modified_partition
= replmd_private
->ncs
; modified_partition
;
495 modified_partition
= modified_partition
->next
) {
496 if (ldb_dn_compare(modified_partition
->dn
, partition
->dn
) == 0) {
501 if (modified_partition
== NULL
) {
502 modified_partition
= talloc_zero(replmd_private
, struct nc_entry
);
503 if (!modified_partition
) {
504 ldb_oom(ldb_module_get_ctx(ac
->module
));
505 return ldb_module_done(ac
->req
, NULL
,
506 NULL
, LDB_ERR_OPERATIONS_ERROR
);
508 modified_partition
->dn
= ldb_dn_copy(modified_partition
, partition
->dn
);
509 if (!modified_partition
->dn
) {
510 ldb_oom(ldb_module_get_ctx(ac
->module
));
511 return ldb_module_done(ac
->req
, NULL
,
512 NULL
, LDB_ERR_OPERATIONS_ERROR
);
514 DLIST_ADD(replmd_private
->ncs
, modified_partition
);
517 if (ac
->seq_num
> modified_partition
->mod_usn
) {
518 modified_partition
->mod_usn
= ac
->seq_num
;
520 modified_partition
->mod_usn_urgent
= ac
->seq_num
;
525 if (ac
->apply_mode
) {
526 ret
= replmd_replicated_apply_isDeleted(ac
);
527 if (ret
!= LDB_SUCCESS
) {
528 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
532 /* free the partition control container here, for the
533 * common path. Other cases will have it cleaned up
534 * eventually with the ares */
535 talloc_free(partition_ctrl
);
536 return ldb_module_done(ac
->req
, controls
,
537 ares
->response
, LDB_SUCCESS
);
543 * update a @REPLCHANGED record in each partition if there have been
544 * any writes of replicated data in the partition
546 static int replmd_notify_store(struct ldb_module
*module
, struct ldb_request
*parent
)
548 struct replmd_private
*replmd_private
=
549 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
551 while (replmd_private
->ncs
) {
553 struct nc_entry
*modified_partition
= replmd_private
->ncs
;
555 ret
= dsdb_module_save_partition_usn(module
, modified_partition
->dn
,
556 modified_partition
->mod_usn
,
557 modified_partition
->mod_usn_urgent
, parent
);
558 if (ret
!= LDB_SUCCESS
) {
559 DEBUG(0,(__location__
": Failed to save partition uSN for %s\n",
560 ldb_dn_get_linearized(modified_partition
->dn
)));
563 DLIST_REMOVE(replmd_private
->ncs
, modified_partition
);
564 talloc_free(modified_partition
);
572 created a replmd_replicated_request context
574 static struct replmd_replicated_request
*replmd_ctx_init(struct ldb_module
*module
,
575 struct ldb_request
*req
)
577 struct ldb_context
*ldb
;
578 struct replmd_replicated_request
*ac
;
580 ldb
= ldb_module_get_ctx(module
);
582 ac
= talloc_zero(req
, struct replmd_replicated_request
);
591 ac
->schema
= dsdb_get_schema(ldb
, ac
);
593 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
594 "replmd_modify: no dsdb_schema loaded");
595 DEBUG(0,(__location__
": %s\n", ldb_errstring(ldb
)));
603 add a time element to a record
605 static int add_time_element(struct ldb_message
*msg
, const char *attr
, time_t t
)
607 struct ldb_message_element
*el
;
611 if (ldb_msg_find_element(msg
, attr
) != NULL
) {
615 s
= ldb_timestring(msg
, t
);
617 return LDB_ERR_OPERATIONS_ERROR
;
620 ret
= ldb_msg_add_string(msg
, attr
, s
);
621 if (ret
!= LDB_SUCCESS
) {
625 el
= ldb_msg_find_element(msg
, attr
);
626 /* always set as replace. This works because on add ops, the flag
628 el
->flags
= LDB_FLAG_MOD_REPLACE
;
634 add a uint64_t element to a record
636 static int add_uint64_element(struct ldb_context
*ldb
, struct ldb_message
*msg
,
637 const char *attr
, uint64_t v
)
639 struct ldb_message_element
*el
;
642 if (ldb_msg_find_element(msg
, attr
) != NULL
) {
646 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, attr
, v
);
647 if (ret
!= LDB_SUCCESS
) {
651 el
= ldb_msg_find_element(msg
, attr
);
652 /* always set as replace. This works because on add ops, the flag
654 el
->flags
= LDB_FLAG_MOD_REPLACE
;
659 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1
*m1
,
660 const struct replPropertyMetaData1
*m2
,
661 const uint32_t *rdn_attid
)
664 * This assignment seems inoccous, but it is critical for the
665 * system, as we need to do the comparisons as a unsigned
666 * quantity, not signed (enums are signed integers)
668 uint32_t attid_1
= m1
->attid
;
669 uint32_t attid_2
= m2
->attid
;
671 if (attid_1
== attid_2
) {
676 * the rdn attribute should be at the end!
677 * so we need to return a value greater than zero
678 * which means m1 is greater than m2
680 if (attid_1
== *rdn_attid
) {
685 * the rdn attribute should be at the end!
686 * so we need to return a value less than zero
687 * which means m2 is greater than m1
689 if (attid_2
== *rdn_attid
) {
694 * See above regarding this being an unsigned comparison.
695 * Otherwise when the high bit is set on non-standard
696 * attributes, they would end up first, before objectClass
699 return attid_1
> attid_2
? 1 : -1;
702 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context
*ldb
,
703 struct replPropertyMetaDataCtr1
*ctr1
,
704 const struct dsdb_attribute
*rdn_sa
,
707 if (ctr1
->count
== 0) {
708 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
709 "No elements found in replPropertyMetaData for %s!\n",
710 ldb_dn_get_linearized(dn
));
711 return LDB_ERR_CONSTRAINT_VIOLATION
;
713 if (ctr1
->array
[ctr1
->count
- 1].attid
!= rdn_sa
->attributeID_id
) {
714 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
715 "No rDN found in replPropertyMetaData for %s!\n",
716 ldb_dn_get_linearized(dn
));
717 return LDB_ERR_CONSTRAINT_VIOLATION
;
720 /* the objectClass attribute is value 0x00000000, so must be first */
721 if (ctr1
->array
[0].attid
!= DRSUAPI_ATTID_objectClass
) {
722 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
723 "No objectClass found in replPropertyMetaData for %s!\n",
724 ldb_dn_get_linearized(dn
));
725 return LDB_ERR_OBJECT_CLASS_VIOLATION
;
731 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context
*ldb
,
732 struct replPropertyMetaDataCtr1
*ctr1
,
733 const struct dsdb_schema
*schema
,
736 const char *rdn_name
;
737 const struct dsdb_attribute
*rdn_sa
;
739 rdn_name
= ldb_dn_get_rdn_name(dn
);
741 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
742 __location__
": No rDN for %s?\n",
743 ldb_dn_get_linearized(dn
));
744 return LDB_ERR_INVALID_DN_SYNTAX
;
747 rdn_sa
= dsdb_attribute_by_lDAPDisplayName(schema
, rdn_name
);
748 if (rdn_sa
== NULL
) {
749 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
750 __location__
": No sa found for rDN %s for %s\n",
751 rdn_name
, ldb_dn_get_linearized(dn
));
752 return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE
;
755 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
756 rdn_sa
->attributeID_id
, rdn_name
, ldb_dn_get_linearized(dn
)));
758 LDB_TYPESAFE_QSORT(ctr1
->array
, ctr1
->count
, &rdn_sa
->attributeID_id
,
759 replmd_replPropertyMetaData1_attid_sort
);
760 return replmd_replPropertyMetaDataCtr1_verify(ldb
, ctr1
, rdn_sa
, dn
);
763 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element
*e1
,
764 const struct ldb_message_element
*e2
,
765 const struct dsdb_schema
*schema
)
767 const struct dsdb_attribute
*a1
;
768 const struct dsdb_attribute
*a2
;
771 * TODO: make this faster by caching the dsdb_attribute pointer
772 * on the ldb_messag_element
775 a1
= dsdb_attribute_by_lDAPDisplayName(schema
, e1
->name
);
776 a2
= dsdb_attribute_by_lDAPDisplayName(schema
, e2
->name
);
779 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
783 return strcasecmp(e1
->name
, e2
->name
);
785 if (a1
->attributeID_id
== a2
->attributeID_id
) {
788 return a1
->attributeID_id
> a2
->attributeID_id
? 1 : -1;
791 static void replmd_ldb_message_sort(struct ldb_message
*msg
,
792 const struct dsdb_schema
*schema
)
794 LDB_TYPESAFE_QSORT(msg
->elements
, msg
->num_elements
, schema
, replmd_ldb_message_element_attid_sort
);
797 static int replmd_build_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
, struct dsdb_dn
*dsdb_dn
,
798 const struct GUID
*invocation_id
, uint64_t seq_num
,
799 uint64_t local_usn
, NTTIME nttime
, uint32_t version
, bool deleted
);
803 fix up linked attributes in replmd_add.
804 This involves setting up the right meta-data in extended DN
805 components, and creating backlinks to the object
807 static int replmd_add_fix_la(struct ldb_module
*module
, struct ldb_message_element
*el
,
808 uint64_t seq_num
, const struct GUID
*invocationId
, time_t t
,
809 struct GUID
*guid
, const struct dsdb_attribute
*sa
, struct ldb_request
*parent
)
812 TALLOC_CTX
*tmp_ctx
= talloc_new(el
->values
);
813 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
815 /* We will take a reference to the schema in replmd_add_backlink */
816 const struct dsdb_schema
*schema
= dsdb_get_schema(ldb
, NULL
);
819 unix_to_nt_time(&now
, t
);
821 for (i
=0; i
<el
->num_values
; i
++) {
822 struct ldb_val
*v
= &el
->values
[i
];
823 struct dsdb_dn
*dsdb_dn
= dsdb_dn_parse(tmp_ctx
, ldb
, v
, sa
->syntax
->ldap_oid
);
824 struct GUID target_guid
;
828 /* note that the DN already has the extended
829 components from the extended_dn_store module */
830 status
= dsdb_get_extended_dn_guid(dsdb_dn
->dn
, &target_guid
, "GUID");
831 if (!NT_STATUS_IS_OK(status
) || GUID_all_zero(&target_guid
)) {
832 ret
= dsdb_module_guid_by_dn(module
, dsdb_dn
->dn
, &target_guid
, parent
);
833 if (ret
!= LDB_SUCCESS
) {
834 talloc_free(tmp_ctx
);
837 ret
= dsdb_set_extended_dn_guid(dsdb_dn
->dn
, &target_guid
, "GUID");
838 if (ret
!= LDB_SUCCESS
) {
839 talloc_free(tmp_ctx
);
844 ret
= replmd_build_la_val(el
->values
, v
, dsdb_dn
, invocationId
,
845 seq_num
, seq_num
, now
, 0, false);
846 if (ret
!= LDB_SUCCESS
) {
847 talloc_free(tmp_ctx
);
851 ret
= replmd_add_backlink(module
, schema
, guid
, &target_guid
, true, sa
, false);
852 if (ret
!= LDB_SUCCESS
) {
853 talloc_free(tmp_ctx
);
858 talloc_free(tmp_ctx
);
864 intercept add requests
866 static int replmd_add(struct ldb_module
*module
, struct ldb_request
*req
)
868 struct samldb_msds_intid_persistant
*msds_intid_struct
;
869 struct ldb_context
*ldb
;
870 struct ldb_control
*control
;
871 struct replmd_replicated_request
*ac
;
872 enum ndr_err_code ndr_err
;
873 struct ldb_request
*down_req
;
874 struct ldb_message
*msg
;
875 const DATA_BLOB
*guid_blob
;
877 struct replPropertyMetaDataBlob nmd
;
878 struct ldb_val nmd_value
;
879 const struct GUID
*our_invocation_id
;
880 time_t t
= time(NULL
);
885 unsigned int functional_level
;
887 bool allow_add_guid
= false;
888 bool remove_current_guid
= false;
889 bool is_urgent
= false;
890 struct ldb_message_element
*objectclass_el
;
892 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
893 control
= ldb_request_get_control(req
, LDB_CONTROL_RELAX_OID
);
895 allow_add_guid
= true;
898 /* do not manipulate our control entries */
899 if (ldb_dn_is_special(req
->op
.add
.message
->dn
)) {
900 return ldb_next_request(module
, req
);
903 ldb
= ldb_module_get_ctx(module
);
905 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_add\n");
907 guid_blob
= ldb_msg_find_ldb_val(req
->op
.add
.message
, "objectGUID");
908 if (guid_blob
!= NULL
) {
909 if (!allow_add_guid
) {
910 ldb_set_errstring(ldb
,
911 "replmd_add: it's not allowed to add an object with objectGUID!");
912 return LDB_ERR_UNWILLING_TO_PERFORM
;
914 NTSTATUS status
= GUID_from_data_blob(guid_blob
,&guid
);
915 if (!NT_STATUS_IS_OK(status
)) {
916 ldb_set_errstring(ldb
,
917 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
918 return LDB_ERR_UNWILLING_TO_PERFORM
;
920 /* we remove this attribute as it can be a string and
921 * will not be treated correctly and then we will re-add
922 * it later on in the good format */
923 remove_current_guid
= true;
927 guid
= GUID_random();
930 ac
= replmd_ctx_init(module
, req
);
932 return ldb_module_oom(module
);
935 functional_level
= dsdb_functional_level(ldb
);
937 /* Get a sequence number from the backend */
938 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &ac
->seq_num
);
939 if (ret
!= LDB_SUCCESS
) {
944 /* get our invocationId */
945 our_invocation_id
= samdb_ntds_invocation_id(ldb
);
946 if (!our_invocation_id
) {
947 ldb_debug_set(ldb
, LDB_DEBUG_ERROR
,
948 "replmd_add: unable to find invocationId\n");
950 return LDB_ERR_OPERATIONS_ERROR
;
953 /* we have to copy the message as the caller might have it as a const */
954 msg
= ldb_msg_copy_shallow(ac
, req
->op
.add
.message
);
958 return LDB_ERR_OPERATIONS_ERROR
;
961 /* generated times */
962 unix_to_nt_time(&now
, t
);
963 time_str
= ldb_timestring(msg
, t
);
967 return LDB_ERR_OPERATIONS_ERROR
;
969 if (remove_current_guid
) {
970 ldb_msg_remove_attr(msg
,"objectGUID");
974 * remove autogenerated attributes
976 ldb_msg_remove_attr(msg
, "whenCreated");
977 ldb_msg_remove_attr(msg
, "whenChanged");
978 ldb_msg_remove_attr(msg
, "uSNCreated");
979 ldb_msg_remove_attr(msg
, "uSNChanged");
980 ldb_msg_remove_attr(msg
, "replPropertyMetaData");
983 * readd replicated attributes
985 ret
= ldb_msg_add_string(msg
, "whenCreated", time_str
);
986 if (ret
!= LDB_SUCCESS
) {
992 /* build the replication meta_data */
995 nmd
.ctr
.ctr1
.count
= msg
->num_elements
;
996 nmd
.ctr
.ctr1
.array
= talloc_array(msg
,
997 struct replPropertyMetaData1
,
999 if (!nmd
.ctr
.ctr1
.array
) {
1002 return LDB_ERR_OPERATIONS_ERROR
;
1005 for (i
=0; i
< msg
->num_elements
; i
++) {
1006 struct ldb_message_element
*e
= &msg
->elements
[i
];
1007 struct replPropertyMetaData1
*m
= &nmd
.ctr
.ctr1
.array
[ni
];
1008 const struct dsdb_attribute
*sa
;
1010 if (e
->name
[0] == '@') continue;
1012 sa
= dsdb_attribute_by_lDAPDisplayName(ac
->schema
, e
->name
);
1014 ldb_debug_set(ldb
, LDB_DEBUG_ERROR
,
1015 "replmd_add: attribute '%s' not defined in schema\n",
1018 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
1021 if ((sa
->systemFlags
& DS_FLAG_ATTR_NOT_REPLICATED
) || (sa
->systemFlags
& DS_FLAG_ATTR_IS_CONSTRUCTED
)) {
1022 /* if the attribute is not replicated (0x00000001)
1023 * or constructed (0x00000004) it has no metadata
1028 if (sa
->linkID
!= 0 && functional_level
> DS_DOMAIN_FUNCTION_2000
) {
1029 ret
= replmd_add_fix_la(module
, e
, ac
->seq_num
, our_invocation_id
, t
, &guid
, sa
, req
);
1030 if (ret
!= LDB_SUCCESS
) {
1034 /* linked attributes are not stored in
1035 replPropertyMetaData in FL above w2k */
1039 m
->attid
= sa
->attributeID_id
;
1041 if (m
->attid
== 0x20030) {
1042 const struct ldb_val
*rdn_val
= ldb_dn_get_rdn_val(msg
->dn
);
1045 if (rdn_val
== NULL
) {
1048 return LDB_ERR_OPERATIONS_ERROR
;
1051 rdn
= (const char*)rdn_val
->data
;
1052 if (strcmp(rdn
, "Deleted Objects") == 0) {
1054 * Set the originating_change_time to 29/12/9999 at 23:59:59
1055 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1057 m
->originating_change_time
= DELETED_OBJECT_CONTAINER_CHANGE_TIME
;
1059 m
->originating_change_time
= now
;
1062 m
->originating_change_time
= now
;
1064 m
->originating_invocation_id
= *our_invocation_id
;
1065 m
->originating_usn
= ac
->seq_num
;
1066 m
->local_usn
= ac
->seq_num
;
1070 /* fix meta data count */
1071 nmd
.ctr
.ctr1
.count
= ni
;
1074 * sort meta data array, and move the rdn attribute entry to the end
1076 ret
= replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb
, &nmd
.ctr
.ctr1
, ac
->schema
, msg
->dn
);
1077 if (ret
!= LDB_SUCCESS
) {
1078 ldb_asprintf_errstring(ldb
, "%s: error during direct ADD: %s", __func__
, ldb_errstring(ldb
));
1083 /* generated NDR encoded values */
1084 ndr_err
= ndr_push_struct_blob(&nmd_value
, msg
,
1086 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
1087 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1090 return LDB_ERR_OPERATIONS_ERROR
;
1094 * add the autogenerated values
1096 ret
= dsdb_msg_add_guid(msg
, &guid
, "objectGUID");
1097 if (ret
!= LDB_SUCCESS
) {
1102 ret
= ldb_msg_add_string(msg
, "whenChanged", time_str
);
1103 if (ret
!= LDB_SUCCESS
) {
1108 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNCreated", ac
->seq_num
);
1109 if (ret
!= LDB_SUCCESS
) {
1114 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNChanged", ac
->seq_num
);
1115 if (ret
!= LDB_SUCCESS
) {
1120 ret
= ldb_msg_add_value(msg
, "replPropertyMetaData", &nmd_value
, NULL
);
1121 if (ret
!= LDB_SUCCESS
) {
1128 * sort the attributes by attid before storing the object
1130 replmd_ldb_message_sort(msg
, ac
->schema
);
1133 * Assert that we do have an objectClass
1135 objectclass_el
= ldb_msg_find_element(msg
, "objectClass");
1136 if (objectclass_el
== NULL
) {
1137 ldb_asprintf_errstring(ldb
, __location__
1138 ": objectClass missing on %s\n",
1139 ldb_dn_get_linearized(msg
->dn
));
1141 return LDB_ERR_OPERATIONS_ERROR
;
1143 is_urgent
= replmd_check_urgent_objectclass(objectclass_el
,
1144 REPL_URGENT_ON_CREATE
);
1146 ac
->is_urgent
= is_urgent
;
1147 ret
= ldb_build_add_req(&down_req
, ldb
, ac
,
1150 ac
, replmd_op_callback
,
1153 LDB_REQ_SET_LOCATION(down_req
);
1154 if (ret
!= LDB_SUCCESS
) {
1159 /* current partition control is needed by "replmd_op_callback" */
1160 if (ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
) == NULL
) {
1161 ret
= ldb_request_add_control(down_req
,
1162 DSDB_CONTROL_CURRENT_PARTITION_OID
,
1164 if (ret
!= LDB_SUCCESS
) {
1170 if (functional_level
== DS_DOMAIN_FUNCTION_2000
) {
1171 ret
= ldb_request_add_control(down_req
, DSDB_CONTROL_APPLY_LINKS
, false, NULL
);
1172 if (ret
!= LDB_SUCCESS
) {
1178 /* mark the control done */
1180 control
->critical
= 0;
1182 if (ldb_dn_compare_base(ac
->schema
->base_dn
, req
->op
.add
.message
->dn
) != 0) {
1184 /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
1185 msds_intid_struct
= (struct samldb_msds_intid_persistant
*) ldb_get_opaque(ldb
, SAMLDB_MSDS_INTID_OPAQUE
);
1186 if (msds_intid_struct
) {
1187 msds_intid_struct
->usn
= ac
->seq_num
;
1190 /* go on with the call chain */
1191 return ldb_next_request(module
, down_req
);
1196 * update the replPropertyMetaData for one element
1198 static int replmd_update_rpmd_element(struct ldb_context
*ldb
,
1199 struct ldb_message
*msg
,
1200 struct ldb_message_element
*el
,
1201 struct ldb_message_element
*old_el
,
1202 struct replPropertyMetaDataBlob
*omd
,
1203 const struct dsdb_schema
*schema
,
1205 const struct GUID
*our_invocation_id
,
1207 struct ldb_request
*req
)
1210 const struct dsdb_attribute
*a
;
1211 struct replPropertyMetaData1
*md1
;
1212 bool may_skip
= false;
1214 a
= dsdb_attribute_by_lDAPDisplayName(schema
, el
->name
);
1216 if (ldb_request_get_control(req
, LDB_CONTROL_RELAX_OID
)) {
1217 /* allow this to make it possible for dbcheck
1218 to remove bad attributes */
1222 DEBUG(0,(__location__
": Unable to find attribute %s in schema\n",
1224 return LDB_ERR_OPERATIONS_ERROR
;
1227 if ((a
->systemFlags
& DS_FLAG_ATTR_NOT_REPLICATED
) || (a
->systemFlags
& DS_FLAG_ATTR_IS_CONSTRUCTED
)) {
1232 * if the attribute's value haven't changed, and this isn't
1233 * just a delete of everything then return LDB_SUCCESS Unless
1234 * we have the provision control or if the attribute is
1235 * interSiteTopologyGenerator as this page explain:
1236 * http://support.microsoft.com/kb/224815 this attribute is
1237 * periodicaly written by the DC responsible for the intersite
1238 * generation in a given site
1240 * Unchanged could be deleting or replacing an already-gone
1241 * thing with an unconstrained delete/empty replace or a
1242 * replace with the same value, but not an add with the same
1243 * value because that could be about adding a duplicate (which
1244 * is for someone else to error out on).
1246 if (old_el
!= NULL
&& ldb_msg_element_equal_ordered(el
, old_el
)) {
1247 if (LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_REPLACE
) {
1250 } else if (old_el
== NULL
&& el
->num_values
== 0) {
1251 if (LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_REPLACE
) {
1253 } else if (LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_DELETE
) {
1259 if (strcmp(el
->name
, "interSiteTopologyGenerator") != 0 &&
1260 !ldb_request_get_control(req
, LDB_CONTROL_PROVISION_OID
)) {
1262 * allow this to make it possible for dbcheck
1263 * to rebuild broken metadata
1269 for (i
=0; i
<omd
->ctr
.ctr1
.count
; i
++) {
1270 if (a
->attributeID_id
== omd
->ctr
.ctr1
.array
[i
].attid
) break;
1273 if (a
->linkID
!= 0 && dsdb_functional_level(ldb
) > DS_DOMAIN_FUNCTION_2000
) {
1274 /* linked attributes are not stored in
1275 replPropertyMetaData in FL above w2k, but we do
1276 raise the seqnum for the object */
1277 if (*seq_num
== 0 &&
1278 ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, seq_num
) != LDB_SUCCESS
) {
1279 return LDB_ERR_OPERATIONS_ERROR
;
1284 if (i
== omd
->ctr
.ctr1
.count
) {
1285 /* we need to add a new one */
1286 omd
->ctr
.ctr1
.array
= talloc_realloc(msg
, omd
->ctr
.ctr1
.array
,
1287 struct replPropertyMetaData1
, omd
->ctr
.ctr1
.count
+1);
1288 if (omd
->ctr
.ctr1
.array
== NULL
) {
1290 return LDB_ERR_OPERATIONS_ERROR
;
1292 omd
->ctr
.ctr1
.count
++;
1293 ZERO_STRUCT(omd
->ctr
.ctr1
.array
[i
]);
1296 /* Get a new sequence number from the backend. We only do this
1297 * if we have a change that requires a new
1298 * replPropertyMetaData element
1300 if (*seq_num
== 0) {
1301 int ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, seq_num
);
1302 if (ret
!= LDB_SUCCESS
) {
1303 return LDB_ERR_OPERATIONS_ERROR
;
1307 md1
= &omd
->ctr
.ctr1
.array
[i
];
1309 md1
->attid
= a
->attributeID_id
;
1310 if (md1
->attid
== 0x20030) {
1311 const struct ldb_val
*rdn_val
= ldb_dn_get_rdn_val(msg
->dn
);
1314 if (rdn_val
== NULL
) {
1316 return LDB_ERR_OPERATIONS_ERROR
;
1319 rdn
= (const char*)rdn_val
->data
;
1320 if (strcmp(rdn
, "Deleted Objects") == 0) {
1322 * Set the originating_change_time to 29/12/9999 at 23:59:59
1323 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1325 md1
->originating_change_time
= DELETED_OBJECT_CONTAINER_CHANGE_TIME
;
1327 md1
->originating_change_time
= now
;
1330 md1
->originating_change_time
= now
;
1332 md1
->originating_invocation_id
= *our_invocation_id
;
1333 md1
->originating_usn
= *seq_num
;
1334 md1
->local_usn
= *seq_num
;
1339 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd
)
1341 uint32_t count
= omd
.ctr
.ctr1
.count
;
1344 for (i
=0; i
< count
; i
++) {
1345 struct replPropertyMetaData1 m
= omd
.ctr
.ctr1
.array
[i
];
1346 if (max
< m
.local_usn
) {
1354 * update the replPropertyMetaData object each time we modify an
1355 * object. This is needed for DRS replication, as the merge on the
1356 * client is based on this object
1358 static int replmd_update_rpmd(struct ldb_module
*module
,
1359 const struct dsdb_schema
*schema
,
1360 struct ldb_request
*req
,
1361 const char * const *rename_attrs
,
1362 struct ldb_message
*msg
, uint64_t *seq_num
,
1364 bool *is_urgent
, bool *rodc
)
1366 const struct ldb_val
*omd_value
;
1367 enum ndr_err_code ndr_err
;
1368 struct replPropertyMetaDataBlob omd
;
1371 const struct GUID
*our_invocation_id
;
1373 const char * const *attrs
= NULL
;
1374 const char * const attrs1
[] = { "replPropertyMetaData", "*", NULL
};
1375 const char * const attrs2
[] = { "uSNChanged", "objectClass", "instanceType", NULL
};
1376 struct ldb_result
*res
;
1377 struct ldb_context
*ldb
;
1378 struct ldb_message_element
*objectclass_el
;
1379 enum urgent_situation situation
;
1380 bool rmd_is_provided
;
1383 attrs
= rename_attrs
;
1388 ldb
= ldb_module_get_ctx(module
);
1390 our_invocation_id
= samdb_ntds_invocation_id(ldb
);
1391 if (!our_invocation_id
) {
1392 /* this happens during an initial vampire while
1393 updating the schema */
1394 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1398 unix_to_nt_time(&now
, t
);
1400 if (ldb_request_get_control(req
, DSDB_CONTROL_CHANGEREPLMETADATA_OID
)) {
1401 rmd_is_provided
= true;
1403 rmd_is_provided
= false;
1406 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1407 * otherwise we consider we are updating */
1408 if (ldb_msg_check_string_attribute(msg
, "isDeleted", "TRUE")) {
1409 situation
= REPL_URGENT_ON_DELETE
;
1410 } else if (rename_attrs
) {
1411 situation
= REPL_URGENT_ON_CREATE
| REPL_URGENT_ON_DELETE
;
1413 situation
= REPL_URGENT_ON_UPDATE
;
1416 if (rmd_is_provided
) {
1417 /* In this case the change_replmetadata control was supplied */
1418 /* We check that it's the only attribute that is provided
1419 * (it's a rare case so it's better to keep the code simplier)
1420 * We also check that the highest local_usn is bigger than
1423 if( msg
->num_elements
!= 1 ||
1424 strncmp(msg
->elements
[0].name
,
1425 "replPropertyMetaData", 20) ) {
1426 DEBUG(0,(__location__
": changereplmetada control called without "\
1427 "a specified replPropertyMetaData attribute or with others\n"));
1428 return LDB_ERR_OPERATIONS_ERROR
;
1430 if (situation
!= REPL_URGENT_ON_UPDATE
) {
1431 DEBUG(0,(__location__
": changereplmetada control can't be called when deleting an object\n"));
1432 return LDB_ERR_OPERATIONS_ERROR
;
1434 omd_value
= ldb_msg_find_ldb_val(msg
, "replPropertyMetaData");
1436 DEBUG(0,(__location__
": replPropertyMetaData was not specified for Object %s\n",
1437 ldb_dn_get_linearized(msg
->dn
)));
1438 return LDB_ERR_OPERATIONS_ERROR
;
1440 ndr_err
= ndr_pull_struct_blob(omd_value
, msg
, &omd
,
1441 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
1442 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1443 DEBUG(0,(__location__
": Failed to parse replPropertyMetaData for %s\n",
1444 ldb_dn_get_linearized(msg
->dn
)));
1445 return LDB_ERR_OPERATIONS_ERROR
;
1447 *seq_num
= find_max_local_usn(omd
);
1449 ret
= dsdb_module_search_dn(module
, msg
, &res
, msg
->dn
, attrs2
,
1450 DSDB_FLAG_NEXT_MODULE
|
1451 DSDB_SEARCH_SHOW_RECYCLED
|
1452 DSDB_SEARCH_SHOW_EXTENDED_DN
|
1453 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
|
1454 DSDB_SEARCH_REVEAL_INTERNALS
, req
);
1456 if (ret
!= LDB_SUCCESS
) {
1460 db_seq
= ldb_msg_find_attr_as_uint64(res
->msgs
[0], "uSNChanged", 0);
1461 if (*seq_num
<= db_seq
) {
1462 DEBUG(0,(__location__
": changereplmetada control provided but max(local_usn)"\
1463 " is less or equal to uSNChanged (max = %lld uSNChanged = %lld)\n",
1464 (long long)*seq_num
, (long long)db_seq
));
1465 return LDB_ERR_OPERATIONS_ERROR
;
1469 /* search for the existing replPropertyMetaDataBlob. We need
1470 * to use REVEAL and ask for DNs in storage format to support
1471 * the check for values being the same in
1472 * replmd_update_rpmd_element()
1474 ret
= dsdb_module_search_dn(module
, msg
, &res
, msg
->dn
, attrs
,
1475 DSDB_FLAG_NEXT_MODULE
|
1476 DSDB_SEARCH_SHOW_RECYCLED
|
1477 DSDB_SEARCH_SHOW_EXTENDED_DN
|
1478 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
|
1479 DSDB_SEARCH_REVEAL_INTERNALS
, req
);
1480 if (ret
!= LDB_SUCCESS
) {
1484 omd_value
= ldb_msg_find_ldb_val(res
->msgs
[0], "replPropertyMetaData");
1486 DEBUG(0,(__location__
": Object %s does not have a replPropertyMetaData attribute\n",
1487 ldb_dn_get_linearized(msg
->dn
)));
1488 return LDB_ERR_OPERATIONS_ERROR
;
1491 ndr_err
= ndr_pull_struct_blob(omd_value
, msg
, &omd
,
1492 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
1493 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1494 DEBUG(0,(__location__
": Failed to parse replPropertyMetaData for %s\n",
1495 ldb_dn_get_linearized(msg
->dn
)));
1496 return LDB_ERR_OPERATIONS_ERROR
;
1499 if (omd
.version
!= 1) {
1500 DEBUG(0,(__location__
": bad version %u in replPropertyMetaData for %s\n",
1501 omd
.version
, ldb_dn_get_linearized(msg
->dn
)));
1502 return LDB_ERR_OPERATIONS_ERROR
;
1505 for (i
=0; i
<msg
->num_elements
; i
++) {
1506 struct ldb_message_element
*old_el
;
1507 old_el
= ldb_msg_find_element(res
->msgs
[0], msg
->elements
[i
].name
);
1508 ret
= replmd_update_rpmd_element(ldb
, msg
, &msg
->elements
[i
], old_el
, &omd
, schema
, seq_num
,
1509 our_invocation_id
, now
, req
);
1510 if (ret
!= LDB_SUCCESS
) {
1514 if (!*is_urgent
&& (situation
== REPL_URGENT_ON_UPDATE
)) {
1515 *is_urgent
= replmd_check_urgent_attribute(&msg
->elements
[i
]);
1522 * Assert that we have an objectClass attribute - this is major
1523 * corruption if we don't have this!
1525 objectclass_el
= ldb_msg_find_element(res
->msgs
[0], "objectClass");
1526 if (objectclass_el
== NULL
) {
1527 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
1528 __location__
": objectClass missing on %s\n",
1529 ldb_dn_get_linearized(msg
->dn
));
1530 return LDB_ERR_OPERATIONS_ERROR
;
1534 * Now check if this objectClass means we need to do urgent replication
1536 if (!*is_urgent
&& replmd_check_urgent_objectclass(objectclass_el
,
1542 * replmd_update_rpmd_element has done an update if the
1545 if (*seq_num
!= 0) {
1546 struct ldb_val
*md_value
;
1547 struct ldb_message_element
*el
;
1549 /*if we are RODC and this is a DRSR update then its ok*/
1550 if (!ldb_request_get_control(req
, DSDB_CONTROL_REPLICATED_UPDATE_OID
)
1551 && !ldb_request_get_control(req
, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA
)) {
1552 unsigned instanceType
;
1554 ret
= samdb_rodc(ldb
, rodc
);
1555 if (ret
!= LDB_SUCCESS
) {
1556 DEBUG(4, (__location__
": unable to tell if we are an RODC\n"));
1558 ldb_set_errstring(ldb
, "RODC modify is forbidden!");
1559 return LDB_ERR_REFERRAL
;
1562 instanceType
= ldb_msg_find_attr_as_uint(res
->msgs
[0], "instanceType", INSTANCE_TYPE_WRITE
);
1563 if (!(instanceType
& INSTANCE_TYPE_WRITE
)) {
1564 return ldb_error(ldb
, LDB_ERR_UNWILLING_TO_PERFORM
,
1565 "cannot change replicated attribute on partial replica");
1569 md_value
= talloc(msg
, struct ldb_val
);
1570 if (md_value
== NULL
) {
1572 return LDB_ERR_OPERATIONS_ERROR
;
1575 ret
= replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb
, &omd
.ctr
.ctr1
, schema
, msg
->dn
);
1576 if (ret
!= LDB_SUCCESS
) {
1577 ldb_asprintf_errstring(ldb
, "%s: %s", __func__
, ldb_errstring(ldb
));
1581 ndr_err
= ndr_push_struct_blob(md_value
, msg
, &omd
,
1582 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
1583 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1584 DEBUG(0,(__location__
": Failed to marshall replPropertyMetaData for %s\n",
1585 ldb_dn_get_linearized(msg
->dn
)));
1586 return LDB_ERR_OPERATIONS_ERROR
;
1589 ret
= ldb_msg_add_empty(msg
, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE
, &el
);
1590 if (ret
!= LDB_SUCCESS
) {
1591 DEBUG(0,(__location__
": Failed to add updated replPropertyMetaData %s\n",
1592 ldb_dn_get_linearized(msg
->dn
)));
1597 el
->values
= md_value
;
1604 struct dsdb_dn
*dsdb_dn
;
1609 static int parsed_dn_compare(struct parsed_dn
*pdn1
, struct parsed_dn
*pdn2
)
1611 return GUID_compare(pdn1
->guid
, pdn2
->guid
);
1614 static struct parsed_dn
*parsed_dn_find(struct parsed_dn
*pdn
,
1615 unsigned int count
, struct GUID
*guid
,
1618 struct parsed_dn
*ret
;
1620 if (dn
&& GUID_all_zero(guid
)) {
1621 /* when updating a link using DRS, we sometimes get a
1622 NULL GUID. We then need to try and match by DN */
1623 for (i
=0; i
<count
; i
++) {
1624 if (ldb_dn_compare(pdn
[i
].dsdb_dn
->dn
, dn
) == 0) {
1625 dsdb_get_extended_dn_guid(pdn
[i
].dsdb_dn
->dn
, guid
, "GUID");
1631 BINARY_ARRAY_SEARCH(pdn
, count
, guid
, guid
, GUID_compare
, ret
);
1636 get a series of message element values as an array of DNs and GUIDs
1637 the result is sorted by GUID
1639 static int get_parsed_dns(struct ldb_module
*module
, TALLOC_CTX
*mem_ctx
,
1640 struct ldb_message_element
*el
, struct parsed_dn
**pdn
,
1641 const char *ldap_oid
, struct ldb_request
*parent
)
1644 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
1651 (*pdn
) = talloc_array(mem_ctx
, struct parsed_dn
, el
->num_values
);
1653 ldb_module_oom(module
);
1654 return LDB_ERR_OPERATIONS_ERROR
;
1657 for (i
=0; i
<el
->num_values
; i
++) {
1658 struct ldb_val
*v
= &el
->values
[i
];
1661 struct parsed_dn
*p
;
1665 p
->dsdb_dn
= dsdb_dn_parse(*pdn
, ldb
, v
, ldap_oid
);
1666 if (p
->dsdb_dn
== NULL
) {
1667 return LDB_ERR_INVALID_DN_SYNTAX
;
1670 dn
= p
->dsdb_dn
->dn
;
1672 p
->guid
= talloc(*pdn
, struct GUID
);
1673 if (p
->guid
== NULL
) {
1674 ldb_module_oom(module
);
1675 return LDB_ERR_OPERATIONS_ERROR
;
1678 status
= dsdb_get_extended_dn_guid(dn
, p
->guid
, "GUID");
1679 if (NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)) {
1680 /* we got a DN without a GUID - go find the GUID */
1681 int ret
= dsdb_module_guid_by_dn(module
, dn
, p
->guid
, parent
);
1682 if (ret
!= LDB_SUCCESS
) {
1683 ldb_asprintf_errstring(ldb
, "Unable to find GUID for DN %s\n",
1684 ldb_dn_get_linearized(dn
));
1685 if (ret
== LDB_ERR_NO_SUCH_OBJECT
&&
1686 LDB_FLAG_MOD_TYPE(el
->flags
) == LDB_FLAG_MOD_DELETE
&&
1687 ldb_attr_cmp(el
->name
, "member") == 0) {
1688 return LDB_ERR_UNWILLING_TO_PERFORM
;
1692 ret
= dsdb_set_extended_dn_guid(dn
, p
->guid
, "GUID");
1693 if (ret
!= LDB_SUCCESS
) {
1696 } else if (!NT_STATUS_IS_OK(status
)) {
1697 return LDB_ERR_OPERATIONS_ERROR
;
1700 /* keep a pointer to the original ldb_val */
1704 TYPESAFE_QSORT(*pdn
, el
->num_values
, parsed_dn_compare
);
1710 build a new extended DN, including all meta data fields
1712 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1713 RMD_ADDTIME = originating_add_time
1714 RMD_INVOCID = originating_invocation_id
1715 RMD_CHANGETIME = originating_change_time
1716 RMD_ORIGINATING_USN = originating_usn
1717 RMD_LOCAL_USN = local_usn
1718 RMD_VERSION = version
1720 static int replmd_build_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
, struct dsdb_dn
*dsdb_dn
,
1721 const struct GUID
*invocation_id
, uint64_t seq_num
,
1722 uint64_t local_usn
, NTTIME nttime
, uint32_t version
, bool deleted
)
1724 struct ldb_dn
*dn
= dsdb_dn
->dn
;
1725 const char *tstring
, *usn_string
, *flags_string
;
1726 struct ldb_val tval
;
1728 struct ldb_val usnv
, local_usnv
;
1729 struct ldb_val vers
, flagsv
;
1732 const char *dnstring
;
1734 uint32_t rmd_flags
= deleted
?DSDB_RMD_FLAG_DELETED
:0;
1736 tstring
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)nttime
);
1738 return LDB_ERR_OPERATIONS_ERROR
;
1740 tval
= data_blob_string_const(tstring
);
1742 usn_string
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)seq_num
);
1744 return LDB_ERR_OPERATIONS_ERROR
;
1746 usnv
= data_blob_string_const(usn_string
);
1748 usn_string
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)local_usn
);
1750 return LDB_ERR_OPERATIONS_ERROR
;
1752 local_usnv
= data_blob_string_const(usn_string
);
1754 vstring
= talloc_asprintf(mem_ctx
, "%lu", (unsigned long)version
);
1756 return LDB_ERR_OPERATIONS_ERROR
;
1758 vers
= data_blob_string_const(vstring
);
1760 status
= GUID_to_ndr_blob(invocation_id
, dn
, &iid
);
1761 if (!NT_STATUS_IS_OK(status
)) {
1762 return LDB_ERR_OPERATIONS_ERROR
;
1765 flags_string
= talloc_asprintf(mem_ctx
, "%u", rmd_flags
);
1766 if (!flags_string
) {
1767 return LDB_ERR_OPERATIONS_ERROR
;
1769 flagsv
= data_blob_string_const(flags_string
);
1771 ret
= ldb_dn_set_extended_component(dn
, "RMD_FLAGS", &flagsv
);
1772 if (ret
!= LDB_SUCCESS
) return ret
;
1773 ret
= ldb_dn_set_extended_component(dn
, "RMD_ADDTIME", &tval
);
1774 if (ret
!= LDB_SUCCESS
) return ret
;
1775 ret
= ldb_dn_set_extended_component(dn
, "RMD_INVOCID", &iid
);
1776 if (ret
!= LDB_SUCCESS
) return ret
;
1777 ret
= ldb_dn_set_extended_component(dn
, "RMD_CHANGETIME", &tval
);
1778 if (ret
!= LDB_SUCCESS
) return ret
;
1779 ret
= ldb_dn_set_extended_component(dn
, "RMD_LOCAL_USN", &local_usnv
);
1780 if (ret
!= LDB_SUCCESS
) return ret
;
1781 ret
= ldb_dn_set_extended_component(dn
, "RMD_ORIGINATING_USN", &usnv
);
1782 if (ret
!= LDB_SUCCESS
) return ret
;
1783 ret
= ldb_dn_set_extended_component(dn
, "RMD_VERSION", &vers
);
1784 if (ret
!= LDB_SUCCESS
) return ret
;
1786 dnstring
= dsdb_dn_get_extended_linearized(mem_ctx
, dsdb_dn
, 1);
1787 if (dnstring
== NULL
) {
1788 return LDB_ERR_OPERATIONS_ERROR
;
1790 *v
= data_blob_string_const(dnstring
);
1795 static int replmd_update_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
, struct dsdb_dn
*dsdb_dn
,
1796 struct dsdb_dn
*old_dsdb_dn
, const struct GUID
*invocation_id
,
1797 uint64_t seq_num
, uint64_t local_usn
, NTTIME nttime
,
1798 uint32_t version
, bool deleted
);
1801 check if any links need upgrading from w2k format
1803 The parent_ctx is the ldb_message_element which contains the values array that dns[i].v points at, and which should be used for allocating any new value.
1805 static int replmd_check_upgrade_links(struct parsed_dn
*dns
, uint32_t count
, struct ldb_message_element
*parent_ctx
, const struct GUID
*invocation_id
)
1808 for (i
=0; i
<count
; i
++) {
1813 status
= dsdb_get_extended_dn_uint32(dns
[i
].dsdb_dn
->dn
, &version
, "RMD_VERSION");
1814 if (!NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)) {
1818 /* it's an old one that needs upgrading */
1819 ret
= replmd_update_la_val(parent_ctx
->values
, dns
[i
].v
, dns
[i
].dsdb_dn
, dns
[i
].dsdb_dn
, invocation_id
,
1821 if (ret
!= LDB_SUCCESS
) {
1829 update an extended DN, including all meta data fields
1831 see replmd_build_la_val for value names
1833 static int replmd_update_la_val(TALLOC_CTX
*mem_ctx
, struct ldb_val
*v
, struct dsdb_dn
*dsdb_dn
,
1834 struct dsdb_dn
*old_dsdb_dn
, const struct GUID
*invocation_id
,
1835 uint64_t seq_num
, uint64_t local_usn
, NTTIME nttime
,
1836 uint32_t version
, bool deleted
)
1838 struct ldb_dn
*dn
= dsdb_dn
->dn
;
1839 const char *tstring
, *usn_string
, *flags_string
;
1840 struct ldb_val tval
;
1842 struct ldb_val usnv
, local_usnv
;
1843 struct ldb_val vers
, flagsv
;
1844 const struct ldb_val
*old_addtime
;
1845 uint32_t old_version
;
1848 const char *dnstring
;
1850 uint32_t rmd_flags
= deleted
?DSDB_RMD_FLAG_DELETED
:0;
1852 tstring
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)nttime
);
1854 return LDB_ERR_OPERATIONS_ERROR
;
1856 tval
= data_blob_string_const(tstring
);
1858 usn_string
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)seq_num
);
1860 return LDB_ERR_OPERATIONS_ERROR
;
1862 usnv
= data_blob_string_const(usn_string
);
1864 usn_string
= talloc_asprintf(mem_ctx
, "%llu", (unsigned long long)local_usn
);
1866 return LDB_ERR_OPERATIONS_ERROR
;
1868 local_usnv
= data_blob_string_const(usn_string
);
1870 status
= GUID_to_ndr_blob(invocation_id
, dn
, &iid
);
1871 if (!NT_STATUS_IS_OK(status
)) {
1872 return LDB_ERR_OPERATIONS_ERROR
;
1875 flags_string
= talloc_asprintf(mem_ctx
, "%u", rmd_flags
);
1876 if (!flags_string
) {
1877 return LDB_ERR_OPERATIONS_ERROR
;
1879 flagsv
= data_blob_string_const(flags_string
);
1881 ret
= ldb_dn_set_extended_component(dn
, "RMD_FLAGS", &flagsv
);
1882 if (ret
!= LDB_SUCCESS
) return ret
;
1884 /* get the ADDTIME from the original */
1885 old_addtime
= ldb_dn_get_extended_component(old_dsdb_dn
->dn
, "RMD_ADDTIME");
1886 if (old_addtime
== NULL
) {
1887 old_addtime
= &tval
;
1889 if (dsdb_dn
!= old_dsdb_dn
||
1890 ldb_dn_get_extended_component(dn
, "RMD_ADDTIME") == NULL
) {
1891 ret
= ldb_dn_set_extended_component(dn
, "RMD_ADDTIME", old_addtime
);
1892 if (ret
!= LDB_SUCCESS
) return ret
;
1895 /* use our invocation id */
1896 ret
= ldb_dn_set_extended_component(dn
, "RMD_INVOCID", &iid
);
1897 if (ret
!= LDB_SUCCESS
) return ret
;
1899 /* changetime is the current time */
1900 ret
= ldb_dn_set_extended_component(dn
, "RMD_CHANGETIME", &tval
);
1901 if (ret
!= LDB_SUCCESS
) return ret
;
1903 /* update the USN */
1904 ret
= ldb_dn_set_extended_component(dn
, "RMD_ORIGINATING_USN", &usnv
);
1905 if (ret
!= LDB_SUCCESS
) return ret
;
1907 ret
= ldb_dn_set_extended_component(dn
, "RMD_LOCAL_USN", &local_usnv
);
1908 if (ret
!= LDB_SUCCESS
) return ret
;
1910 /* increase the version by 1 */
1911 status
= dsdb_get_extended_dn_uint32(old_dsdb_dn
->dn
, &old_version
, "RMD_VERSION");
1912 if (NT_STATUS_IS_OK(status
) && old_version
>= version
) {
1913 version
= old_version
+1;
1915 vstring
= talloc_asprintf(dn
, "%lu", (unsigned long)version
);
1916 vers
= data_blob_string_const(vstring
);
1917 ret
= ldb_dn_set_extended_component(dn
, "RMD_VERSION", &vers
);
1918 if (ret
!= LDB_SUCCESS
) return ret
;
1920 dnstring
= dsdb_dn_get_extended_linearized(mem_ctx
, dsdb_dn
, 1);
1921 if (dnstring
== NULL
) {
1922 return LDB_ERR_OPERATIONS_ERROR
;
1924 *v
= data_blob_string_const(dnstring
);
1930 handle adding a linked attribute
1932 static int replmd_modify_la_add(struct ldb_module
*module
,
1933 const struct dsdb_schema
*schema
,
1934 struct ldb_message
*msg
,
1935 struct ldb_message_element
*el
,
1936 struct ldb_message_element
*old_el
,
1937 const struct dsdb_attribute
*schema_attr
,
1940 struct GUID
*msg_guid
,
1941 struct ldb_request
*parent
)
1944 struct parsed_dn
*dns
, *old_dns
;
1945 TALLOC_CTX
*tmp_ctx
= talloc_new(msg
);
1947 struct ldb_val
*new_values
= NULL
;
1948 unsigned int num_new_values
= 0;
1949 unsigned old_num_values
= old_el
?old_el
->num_values
:0;
1950 const struct GUID
*invocation_id
;
1951 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
1954 unix_to_nt_time(&now
, t
);
1956 ret
= get_parsed_dns(module
, tmp_ctx
, el
, &dns
, schema_attr
->syntax
->ldap_oid
, parent
);
1957 if (ret
!= LDB_SUCCESS
) {
1958 talloc_free(tmp_ctx
);
1962 ret
= get_parsed_dns(module
, tmp_ctx
, old_el
, &old_dns
, schema_attr
->syntax
->ldap_oid
, parent
);
1963 if (ret
!= LDB_SUCCESS
) {
1964 talloc_free(tmp_ctx
);
1968 invocation_id
= samdb_ntds_invocation_id(ldb
);
1969 if (!invocation_id
) {
1970 talloc_free(tmp_ctx
);
1971 return LDB_ERR_OPERATIONS_ERROR
;
1974 ret
= replmd_check_upgrade_links(old_dns
, old_num_values
, old_el
, invocation_id
);
1975 if (ret
!= LDB_SUCCESS
) {
1976 talloc_free(tmp_ctx
);
1980 /* for each new value, see if it exists already with the same GUID */
1981 for (i
=0; i
<el
->num_values
; i
++) {
1982 struct parsed_dn
*p
= parsed_dn_find(old_dns
, old_num_values
, dns
[i
].guid
, NULL
);
1984 /* this is a new linked attribute value */
1985 new_values
= talloc_realloc(tmp_ctx
, new_values
, struct ldb_val
, num_new_values
+1);
1986 if (new_values
== NULL
) {
1987 ldb_module_oom(module
);
1988 talloc_free(tmp_ctx
);
1989 return LDB_ERR_OPERATIONS_ERROR
;
1991 ret
= replmd_build_la_val(new_values
, &new_values
[num_new_values
], dns
[i
].dsdb_dn
,
1992 invocation_id
, seq_num
, seq_num
, now
, 0, false);
1993 if (ret
!= LDB_SUCCESS
) {
1994 talloc_free(tmp_ctx
);
1999 /* this is only allowed if the GUID was
2000 previously deleted. */
2001 uint32_t rmd_flags
= dsdb_dn_rmd_flags(p
->dsdb_dn
->dn
);
2003 if (!(rmd_flags
& DSDB_RMD_FLAG_DELETED
)) {
2004 ldb_asprintf_errstring(ldb
, "Attribute %s already exists for target GUID %s",
2005 el
->name
, GUID_string(tmp_ctx
, p
->guid
));
2006 talloc_free(tmp_ctx
);
2007 /* error codes for 'member' need to be
2009 if (ldb_attr_cmp(el
->name
, "member") == 0) {
2010 return LDB_ERR_ENTRY_ALREADY_EXISTS
;
2012 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS
;
2015 ret
= replmd_update_la_val(old_el
->values
, p
->v
, dns
[i
].dsdb_dn
, p
->dsdb_dn
,
2016 invocation_id
, seq_num
, seq_num
, now
, 0, false);
2017 if (ret
!= LDB_SUCCESS
) {
2018 talloc_free(tmp_ctx
);
2023 ret
= replmd_add_backlink(module
, schema
, msg_guid
, dns
[i
].guid
, true, schema_attr
, true);
2024 if (ret
!= LDB_SUCCESS
) {
2025 talloc_free(tmp_ctx
);
2030 /* add the new ones on to the end of the old values, constructing a new el->values */
2031 el
->values
= talloc_realloc(msg
->elements
, old_el
?old_el
->values
:NULL
,
2033 old_num_values
+num_new_values
);
2034 if (el
->values
== NULL
) {
2035 ldb_module_oom(module
);
2036 return LDB_ERR_OPERATIONS_ERROR
;
2039 memcpy(&el
->values
[old_num_values
], new_values
, num_new_values
*sizeof(struct ldb_val
));
2040 el
->num_values
= old_num_values
+ num_new_values
;
2042 talloc_steal(msg
->elements
, el
->values
);
2043 talloc_steal(el
->values
, new_values
);
2045 talloc_free(tmp_ctx
);
2047 /* we now tell the backend to replace all existing values
2048 with the one we have constructed */
2049 el
->flags
= LDB_FLAG_MOD_REPLACE
;
2056 handle deleting all active linked attributes
2058 static int replmd_modify_la_delete(struct ldb_module
*module
,
2059 const struct dsdb_schema
*schema
,
2060 struct ldb_message
*msg
,
2061 struct ldb_message_element
*el
,
2062 struct ldb_message_element
*old_el
,
2063 const struct dsdb_attribute
*schema_attr
,
2066 struct GUID
*msg_guid
,
2067 struct ldb_request
*parent
)
2070 struct parsed_dn
*dns
, *old_dns
;
2071 TALLOC_CTX
*tmp_ctx
= talloc_new(msg
);
2073 const struct GUID
*invocation_id
;
2074 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
2077 unix_to_nt_time(&now
, t
);
2079 /* check if there is nothing to delete */
2080 if ((!old_el
|| old_el
->num_values
== 0) &&
2081 el
->num_values
== 0) {
2085 if (!old_el
|| old_el
->num_values
== 0) {
2086 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
2089 ret
= get_parsed_dns(module
, tmp_ctx
, el
, &dns
, schema_attr
->syntax
->ldap_oid
, parent
);
2090 if (ret
!= LDB_SUCCESS
) {
2091 talloc_free(tmp_ctx
);
2095 ret
= get_parsed_dns(module
, tmp_ctx
, old_el
, &old_dns
, schema_attr
->syntax
->ldap_oid
, parent
);
2096 if (ret
!= LDB_SUCCESS
) {
2097 talloc_free(tmp_ctx
);
2101 invocation_id
= samdb_ntds_invocation_id(ldb
);
2102 if (!invocation_id
) {
2103 return LDB_ERR_OPERATIONS_ERROR
;
2106 ret
= replmd_check_upgrade_links(old_dns
, old_el
->num_values
, old_el
, invocation_id
);
2107 if (ret
!= LDB_SUCCESS
) {
2108 talloc_free(tmp_ctx
);
2114 /* see if we are being asked to delete any links that
2115 don't exist or are already deleted */
2116 for (i
=0; i
<el
->num_values
; i
++) {
2117 struct parsed_dn
*p
= &dns
[i
];
2118 struct parsed_dn
*p2
;
2121 p2
= parsed_dn_find(old_dns
, old_el
->num_values
, p
->guid
, NULL
);
2123 ldb_asprintf_errstring(ldb
, "Attribute %s doesn't exist for target GUID %s",
2124 el
->name
, GUID_string(tmp_ctx
, p
->guid
));
2125 if (ldb_attr_cmp(el
->name
, "member") == 0) {
2126 return LDB_ERR_UNWILLING_TO_PERFORM
;
2128 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
2131 rmd_flags
= dsdb_dn_rmd_flags(p2
->dsdb_dn
->dn
);
2132 if (rmd_flags
& DSDB_RMD_FLAG_DELETED
) {
2133 ldb_asprintf_errstring(ldb
, "Attribute %s already deleted for target GUID %s",
2134 el
->name
, GUID_string(tmp_ctx
, p
->guid
));
2135 if (ldb_attr_cmp(el
->name
, "member") == 0) {
2136 return LDB_ERR_UNWILLING_TO_PERFORM
;
2138 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
2143 /* for each new value, see if it exists already with the same GUID
2144 if it is not already deleted and matches the delete list then delete it
2146 for (i
=0; i
<old_el
->num_values
; i
++) {
2147 struct parsed_dn
*p
= &old_dns
[i
];
2150 if (el
->num_values
&& parsed_dn_find(dns
, el
->num_values
, p
->guid
, NULL
) == NULL
) {
2154 rmd_flags
= dsdb_dn_rmd_flags(p
->dsdb_dn
->dn
);
2155 if (rmd_flags
& DSDB_RMD_FLAG_DELETED
) continue;
2157 ret
= replmd_update_la_val(old_el
->values
, p
->v
, p
->dsdb_dn
, p
->dsdb_dn
,
2158 invocation_id
, seq_num
, seq_num
, now
, 0, true);
2159 if (ret
!= LDB_SUCCESS
) {
2160 talloc_free(tmp_ctx
);
2164 ret
= replmd_add_backlink(module
, schema
, msg_guid
, old_dns
[i
].guid
, false, schema_attr
, true);
2165 if (ret
!= LDB_SUCCESS
) {
2166 talloc_free(tmp_ctx
);
2171 el
->values
= talloc_steal(msg
->elements
, old_el
->values
);
2172 el
->num_values
= old_el
->num_values
;
2174 talloc_free(tmp_ctx
);
2176 /* we now tell the backend to replace all existing values
2177 with the one we have constructed */
2178 el
->flags
= LDB_FLAG_MOD_REPLACE
;
2184 handle replacing a linked attribute
2186 static int replmd_modify_la_replace(struct ldb_module
*module
,
2187 const struct dsdb_schema
*schema
,
2188 struct ldb_message
*msg
,
2189 struct ldb_message_element
*el
,
2190 struct ldb_message_element
*old_el
,
2191 const struct dsdb_attribute
*schema_attr
,
2194 struct GUID
*msg_guid
,
2195 struct ldb_request
*parent
)
2198 struct parsed_dn
*dns
, *old_dns
;
2199 TALLOC_CTX
*tmp_ctx
= talloc_new(msg
);
2201 const struct GUID
*invocation_id
;
2202 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
2203 struct ldb_val
*new_values
= NULL
;
2204 unsigned int num_new_values
= 0;
2205 unsigned int old_num_values
= old_el
?old_el
->num_values
:0;
2208 unix_to_nt_time(&now
, t
);
2210 /* check if there is nothing to replace */
2211 if ((!old_el
|| old_el
->num_values
== 0) &&
2212 el
->num_values
== 0) {
2216 ret
= get_parsed_dns(module
, tmp_ctx
, el
, &dns
, schema_attr
->syntax
->ldap_oid
, parent
);
2217 if (ret
!= LDB_SUCCESS
) {
2218 talloc_free(tmp_ctx
);
2222 ret
= get_parsed_dns(module
, tmp_ctx
, old_el
, &old_dns
, schema_attr
->syntax
->ldap_oid
, parent
);
2223 if (ret
!= LDB_SUCCESS
) {
2224 talloc_free(tmp_ctx
);
2228 invocation_id
= samdb_ntds_invocation_id(ldb
);
2229 if (!invocation_id
) {
2230 return LDB_ERR_OPERATIONS_ERROR
;
2233 ret
= replmd_check_upgrade_links(old_dns
, old_num_values
, old_el
, invocation_id
);
2234 if (ret
!= LDB_SUCCESS
) {
2235 talloc_free(tmp_ctx
);
2239 /* mark all the old ones as deleted */
2240 for (i
=0; i
<old_num_values
; i
++) {
2241 struct parsed_dn
*old_p
= &old_dns
[i
];
2242 struct parsed_dn
*p
;
2243 uint32_t rmd_flags
= dsdb_dn_rmd_flags(old_p
->dsdb_dn
->dn
);
2245 if (rmd_flags
& DSDB_RMD_FLAG_DELETED
) continue;
2247 ret
= replmd_add_backlink(module
, schema
, msg_guid
, old_dns
[i
].guid
, false, schema_attr
, false);
2248 if (ret
!= LDB_SUCCESS
) {
2249 talloc_free(tmp_ctx
);
2253 p
= parsed_dn_find(dns
, el
->num_values
, old_p
->guid
, NULL
);
2255 /* we don't delete it if we are re-adding it */
2259 ret
= replmd_update_la_val(old_el
->values
, old_p
->v
, old_p
->dsdb_dn
, old_p
->dsdb_dn
,
2260 invocation_id
, seq_num
, seq_num
, now
, 0, true);
2261 if (ret
!= LDB_SUCCESS
) {
2262 talloc_free(tmp_ctx
);
2267 /* for each new value, either update its meta-data, or add it
2270 for (i
=0; i
<el
->num_values
; i
++) {
2271 struct parsed_dn
*p
= &dns
[i
], *old_p
;
2274 (old_p
= parsed_dn_find(old_dns
,
2275 old_num_values
, p
->guid
, NULL
)) != NULL
) {
2276 /* update in place */
2277 ret
= replmd_update_la_val(old_el
->values
, old_p
->v
, p
->dsdb_dn
,
2278 old_p
->dsdb_dn
, invocation_id
,
2279 seq_num
, seq_num
, now
, 0, false);
2280 if (ret
!= LDB_SUCCESS
) {
2281 talloc_free(tmp_ctx
);
2286 new_values
= talloc_realloc(tmp_ctx
, new_values
, struct ldb_val
,
2288 if (new_values
== NULL
) {
2289 ldb_module_oom(module
);
2290 talloc_free(tmp_ctx
);
2291 return LDB_ERR_OPERATIONS_ERROR
;
2293 ret
= replmd_build_la_val(new_values
, &new_values
[num_new_values
], dns
[i
].dsdb_dn
,
2294 invocation_id
, seq_num
, seq_num
, now
, 0, false);
2295 if (ret
!= LDB_SUCCESS
) {
2296 talloc_free(tmp_ctx
);
2302 ret
= replmd_add_backlink(module
, schema
, msg_guid
, dns
[i
].guid
, true, schema_attr
, false);
2303 if (ret
!= LDB_SUCCESS
) {
2304 talloc_free(tmp_ctx
);
2309 /* add the new values to the end of old_el */
2310 if (num_new_values
!= 0) {
2311 el
->values
= talloc_realloc(msg
->elements
, old_el
?old_el
->values
:NULL
,
2312 struct ldb_val
, old_num_values
+num_new_values
);
2313 if (el
->values
== NULL
) {
2314 ldb_module_oom(module
);
2315 return LDB_ERR_OPERATIONS_ERROR
;
2317 memcpy(&el
->values
[old_num_values
], &new_values
[0],
2318 sizeof(struct ldb_val
)*num_new_values
);
2319 el
->num_values
= old_num_values
+ num_new_values
;
2320 talloc_steal(msg
->elements
, new_values
);
2322 el
->values
= old_el
->values
;
2323 el
->num_values
= old_el
->num_values
;
2324 talloc_steal(msg
->elements
, el
->values
);
2327 talloc_free(tmp_ctx
);
2329 /* we now tell the backend to replace all existing values
2330 with the one we have constructed */
2331 el
->flags
= LDB_FLAG_MOD_REPLACE
;
2338 handle linked attributes in modify requests
2340 static int replmd_modify_handle_linked_attribs(struct ldb_module
*module
,
2341 struct ldb_message
*msg
,
2342 uint64_t seq_num
, time_t t
,
2343 struct ldb_request
*parent
)
2345 struct ldb_result
*res
;
2348 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
2349 struct ldb_message
*old_msg
;
2351 const struct dsdb_schema
*schema
;
2352 struct GUID old_guid
;
2355 /* there the replmd_update_rpmd code has already
2356 * checked and saw that there are no linked
2361 if (dsdb_functional_level(ldb
) == DS_DOMAIN_FUNCTION_2000
) {
2362 /* don't do anything special for linked attributes */
2366 ret
= dsdb_module_search_dn(module
, msg
, &res
, msg
->dn
, NULL
,
2367 DSDB_FLAG_NEXT_MODULE
|
2368 DSDB_SEARCH_SHOW_RECYCLED
|
2369 DSDB_SEARCH_REVEAL_INTERNALS
|
2370 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
,
2372 if (ret
!= LDB_SUCCESS
) {
2375 schema
= dsdb_get_schema(ldb
, res
);
2377 return LDB_ERR_OPERATIONS_ERROR
;
2380 old_msg
= res
->msgs
[0];
2382 old_guid
= samdb_result_guid(old_msg
, "objectGUID");
2384 for (i
=0; i
<msg
->num_elements
; i
++) {
2385 struct ldb_message_element
*el
= &msg
->elements
[i
];
2386 struct ldb_message_element
*old_el
, *new_el
;
2387 const struct dsdb_attribute
*schema_attr
2388 = dsdb_attribute_by_lDAPDisplayName(schema
, el
->name
);
2390 ldb_asprintf_errstring(ldb
,
2391 "%s: attribute %s is not a valid attribute in schema",
2392 __FUNCTION__
, el
->name
);
2393 return LDB_ERR_OBJECT_CLASS_VIOLATION
;
2395 if (schema_attr
->linkID
== 0) {
2398 if ((schema_attr
->linkID
& 1) == 1) {
2399 if (parent
&& ldb_request_get_control(parent
, DSDB_CONTROL_DBCHECK
)) {
2402 /* Odd is for the target. Illegal to modify */
2403 ldb_asprintf_errstring(ldb
,
2404 "attribute %s must not be modified directly, it is a linked attribute", el
->name
);
2405 return LDB_ERR_UNWILLING_TO_PERFORM
;
2407 old_el
= ldb_msg_find_element(old_msg
, el
->name
);
2408 switch (el
->flags
& LDB_FLAG_MOD_MASK
) {
2409 case LDB_FLAG_MOD_REPLACE
:
2410 ret
= replmd_modify_la_replace(module
, schema
, msg
, el
, old_el
, schema_attr
, seq_num
, t
, &old_guid
, parent
);
2412 case LDB_FLAG_MOD_DELETE
:
2413 ret
= replmd_modify_la_delete(module
, schema
, msg
, el
, old_el
, schema_attr
, seq_num
, t
, &old_guid
, parent
);
2415 case LDB_FLAG_MOD_ADD
:
2416 ret
= replmd_modify_la_add(module
, schema
, msg
, el
, old_el
, schema_attr
, seq_num
, t
, &old_guid
, parent
);
2419 ldb_asprintf_errstring(ldb
,
2420 "invalid flags 0x%x for %s linked attribute",
2421 el
->flags
, el
->name
);
2422 return LDB_ERR_UNWILLING_TO_PERFORM
;
2424 if (dsdb_check_single_valued_link(schema_attr
, el
) != LDB_SUCCESS
) {
2425 ldb_asprintf_errstring(ldb
,
2426 "Attribute %s is single valued but more than one value has been supplied",
2428 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS
;
2430 el
->flags
|= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK
;
2435 if (ret
!= LDB_SUCCESS
) {
2439 ldb_msg_remove_attr(old_msg
, el
->name
);
2441 ldb_msg_add_empty(old_msg
, el
->name
, 0, &new_el
);
2442 new_el
->num_values
= el
->num_values
;
2443 new_el
->values
= talloc_steal(msg
->elements
, el
->values
);
2445 /* TODO: this relises a bit too heavily on the exact
2446 behaviour of ldb_msg_find_element and
2447 ldb_msg_remove_element */
2448 old_el
= ldb_msg_find_element(msg
, el
->name
);
2450 ldb_msg_remove_element(msg
, old_el
);
2461 static int replmd_modify(struct ldb_module
*module
, struct ldb_request
*req
)
2463 struct samldb_msds_intid_persistant
*msds_intid_struct
;
2464 struct ldb_context
*ldb
;
2465 struct replmd_replicated_request
*ac
;
2466 struct ldb_request
*down_req
;
2467 struct ldb_message
*msg
;
2468 time_t t
= time(NULL
);
2470 bool is_urgent
= false, rodc
= false;
2471 unsigned int functional_level
;
2472 const DATA_BLOB
*guid_blob
;
2473 struct ldb_control
*sd_propagation_control
;
2475 /* do not manipulate our control entries */
2476 if (ldb_dn_is_special(req
->op
.mod
.message
->dn
)) {
2477 return ldb_next_request(module
, req
);
2480 sd_propagation_control
= ldb_request_get_control(req
,
2481 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID
);
2482 if (sd_propagation_control
!= NULL
) {
2483 if (req
->op
.mod
.message
->num_elements
!= 1) {
2484 return ldb_module_operr(module
);
2486 ret
= strcmp(req
->op
.mod
.message
->elements
[0].name
,
2487 "nTSecurityDescriptor");
2489 return ldb_module_operr(module
);
2492 return ldb_next_request(module
, req
);
2495 ldb
= ldb_module_get_ctx(module
);
2497 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_modify\n");
2499 guid_blob
= ldb_msg_find_ldb_val(req
->op
.mod
.message
, "objectGUID");
2500 if ( guid_blob
!= NULL
) {
2501 ldb_set_errstring(ldb
,
2502 "replmd_modify: it's not allowed to change the objectGUID!");
2503 return LDB_ERR_CONSTRAINT_VIOLATION
;
2506 ac
= replmd_ctx_init(module
, req
);
2508 return ldb_module_oom(module
);
2511 functional_level
= dsdb_functional_level(ldb
);
2513 /* we have to copy the message as the caller might have it as a const */
2514 msg
= ldb_msg_copy_shallow(ac
, req
->op
.mod
.message
);
2518 return LDB_ERR_OPERATIONS_ERROR
;
2521 ldb_msg_remove_attr(msg
, "whenChanged");
2522 ldb_msg_remove_attr(msg
, "uSNChanged");
2524 ret
= replmd_update_rpmd(module
, ac
->schema
, req
, NULL
,
2525 msg
, &ac
->seq_num
, t
, &is_urgent
, &rodc
);
2526 if (rodc
&& (ret
== LDB_ERR_REFERRAL
)) {
2527 struct loadparm_context
*lp_ctx
;
2530 lp_ctx
= talloc_get_type(ldb_get_opaque(ldb
, "loadparm"),
2531 struct loadparm_context
);
2533 referral
= talloc_asprintf(req
,
2535 lpcfg_dnsdomain(lp_ctx
),
2536 ldb_dn_get_linearized(msg
->dn
));
2537 ret
= ldb_module_send_referral(req
, referral
);
2542 if (ret
!= LDB_SUCCESS
) {
2547 ret
= replmd_modify_handle_linked_attribs(module
, msg
, ac
->seq_num
, t
, req
);
2548 if (ret
!= LDB_SUCCESS
) {
2554 * - replace the old object with the newly constructed one
2557 ac
->is_urgent
= is_urgent
;
2559 ret
= ldb_build_mod_req(&down_req
, ldb
, ac
,
2562 ac
, replmd_op_callback
,
2564 LDB_REQ_SET_LOCATION(down_req
);
2565 if (ret
!= LDB_SUCCESS
) {
2570 /* current partition control is needed by "replmd_op_callback" */
2571 if (ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
) == NULL
) {
2572 ret
= ldb_request_add_control(down_req
,
2573 DSDB_CONTROL_CURRENT_PARTITION_OID
,
2575 if (ret
!= LDB_SUCCESS
) {
2581 /* If we are in functional level 2000, then
2582 * replmd_modify_handle_linked_attribs will have done
2584 if (functional_level
== DS_DOMAIN_FUNCTION_2000
) {
2585 ret
= ldb_request_add_control(down_req
, DSDB_CONTROL_APPLY_LINKS
, false, NULL
);
2586 if (ret
!= LDB_SUCCESS
) {
2592 talloc_steal(down_req
, msg
);
2594 /* we only change whenChanged and uSNChanged if the seq_num
2596 if (ac
->seq_num
!= 0) {
2597 ret
= add_time_element(msg
, "whenChanged", t
);
2598 if (ret
!= LDB_SUCCESS
) {
2604 ret
= add_uint64_element(ldb
, msg
, "uSNChanged", ac
->seq_num
);
2605 if (ret
!= LDB_SUCCESS
) {
2612 if (!ldb_dn_compare_base(ac
->schema
->base_dn
, msg
->dn
)) {
2613 /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
2614 msds_intid_struct
= (struct samldb_msds_intid_persistant
*) ldb_get_opaque(ldb
, SAMLDB_MSDS_INTID_OPAQUE
);
2615 if (msds_intid_struct
) {
2616 msds_intid_struct
->usn
= ac
->seq_num
;
2620 /* go on with the call chain */
2621 return ldb_next_request(module
, down_req
);
2624 static int replmd_rename_callback(struct ldb_request
*req
, struct ldb_reply
*ares
);
2627 handle a rename request
2629 On a rename we need to do an extra ldb_modify which sets the
2630 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2632 static int replmd_rename(struct ldb_module
*module
, struct ldb_request
*req
)
2634 struct ldb_context
*ldb
;
2635 struct replmd_replicated_request
*ac
;
2637 struct ldb_request
*down_req
;
2639 /* do not manipulate our control entries */
2640 if (ldb_dn_is_special(req
->op
.mod
.message
->dn
)) {
2641 return ldb_next_request(module
, req
);
2644 ldb
= ldb_module_get_ctx(module
);
2646 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_rename\n");
2648 ac
= replmd_ctx_init(module
, req
);
2650 return ldb_module_oom(module
);
2653 ret
= ldb_build_rename_req(&down_req
, ldb
, ac
,
2654 ac
->req
->op
.rename
.olddn
,
2655 ac
->req
->op
.rename
.newdn
,
2657 ac
, replmd_rename_callback
,
2659 LDB_REQ_SET_LOCATION(down_req
);
2660 if (ret
!= LDB_SUCCESS
) {
2665 /* go on with the call chain */
2666 return ldb_next_request(module
, down_req
);
2669 /* After the rename is compleated, update the whenchanged etc */
2670 static int replmd_rename_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
2672 struct ldb_context
*ldb
;
2673 struct replmd_replicated_request
*ac
;
2674 struct ldb_request
*down_req
;
2675 struct ldb_message
*msg
;
2676 const struct dsdb_attribute
*rdn_attr
;
2677 const char *rdn_name
;
2678 const struct ldb_val
*rdn_val
;
2679 const char *attrs
[5] = { NULL
, };
2680 time_t t
= time(NULL
);
2682 bool is_urgent
= false, rodc
= false;
2684 ac
= talloc_get_type(req
->context
, struct replmd_replicated_request
);
2685 ldb
= ldb_module_get_ctx(ac
->module
);
2687 if (ares
->error
!= LDB_SUCCESS
) {
2688 return ldb_module_done(ac
->req
, ares
->controls
,
2689 ares
->response
, ares
->error
);
2692 if (ares
->type
!= LDB_REPLY_DONE
) {
2693 ldb_set_errstring(ldb
,
2694 "invalid ldb_reply_type in callback");
2696 return ldb_module_done(ac
->req
, NULL
, NULL
,
2697 LDB_ERR_OPERATIONS_ERROR
);
2701 * - replace the old object with the newly constructed one
2704 msg
= ldb_msg_new(ac
);
2707 return LDB_ERR_OPERATIONS_ERROR
;
2710 msg
->dn
= ac
->req
->op
.rename
.newdn
;
2712 rdn_name
= ldb_dn_get_rdn_name(msg
->dn
);
2713 if (rdn_name
== NULL
) {
2715 return ldb_module_done(ac
->req
, NULL
, NULL
,
2719 /* normalize the rdn attribute name */
2720 rdn_attr
= dsdb_attribute_by_lDAPDisplayName(ac
->schema
, rdn_name
);
2721 if (rdn_attr
== NULL
) {
2723 return ldb_module_done(ac
->req
, NULL
, NULL
,
2726 rdn_name
= rdn_attr
->lDAPDisplayName
;
2728 rdn_val
= ldb_dn_get_rdn_val(msg
->dn
);
2729 if (rdn_val
== NULL
) {
2731 return ldb_module_done(ac
->req
, NULL
, NULL
,
2735 if (ldb_msg_add_empty(msg
, rdn_name
, LDB_FLAG_MOD_REPLACE
, NULL
) != 0) {
2737 return ldb_module_done(ac
->req
, NULL
, NULL
,
2740 if (ldb_msg_add_value(msg
, rdn_name
, rdn_val
, NULL
) != 0) {
2742 return ldb_module_done(ac
->req
, NULL
, NULL
,
2745 if (ldb_msg_add_empty(msg
, "name", LDB_FLAG_MOD_REPLACE
, NULL
) != 0) {
2747 return ldb_module_done(ac
->req
, NULL
, NULL
,
2750 if (ldb_msg_add_value(msg
, "name", rdn_val
, NULL
) != 0) {
2752 return ldb_module_done(ac
->req
, NULL
, NULL
,
2757 * here we let replmd_update_rpmd() only search for
2758 * the existing "replPropertyMetaData" and rdn_name attributes.
2760 * We do not want the existing "name" attribute as
2761 * the "name" attribute needs to get the version
2762 * updated on rename even if the rdn value hasn't changed.
2764 * This is the diff of the meta data, for a moved user
2765 * on a w2k8r2 server:
2768 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
2769 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
2770 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
2771 * version : 0x00000001 (1)
2772 * reserved : 0x00000000 (0)
2773 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
2774 * local_usn : 0x00000000000037a5 (14245)
2775 * array: struct replPropertyMetaData1
2776 * attid : DRSUAPI_ATTID_name (0x90001)
2777 * - version : 0x00000001 (1)
2778 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
2779 * + version : 0x00000002 (2)
2780 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
2781 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
2782 * - originating_usn : 0x00000000000037a5 (14245)
2783 * - local_usn : 0x00000000000037a5 (14245)
2784 * + originating_usn : 0x0000000000003834 (14388)
2785 * + local_usn : 0x0000000000003834 (14388)
2786 * array: struct replPropertyMetaData1
2787 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
2788 * version : 0x00000004 (4)
2790 attrs
[0] = "replPropertyMetaData";
2791 attrs
[1] = "objectClass";
2792 attrs
[2] = "instanceType";
2793 attrs
[3] = rdn_name
;
2796 ret
= replmd_update_rpmd(ac
->module
, ac
->schema
, req
, attrs
,
2797 msg
, &ac
->seq_num
, t
, &is_urgent
, &rodc
);
2798 if (rodc
&& (ret
== LDB_ERR_REFERRAL
)) {
2799 struct ldb_dn
*olddn
= ac
->req
->op
.rename
.olddn
;
2800 struct loadparm_context
*lp_ctx
;
2803 lp_ctx
= talloc_get_type(ldb_get_opaque(ldb
, "loadparm"),
2804 struct loadparm_context
);
2806 referral
= talloc_asprintf(req
,
2808 lpcfg_dnsdomain(lp_ctx
),
2809 ldb_dn_get_linearized(olddn
));
2810 ret
= ldb_module_send_referral(req
, referral
);
2812 return ldb_module_done(req
, NULL
, NULL
, ret
);
2815 if (ret
!= LDB_SUCCESS
) {
2817 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
2820 if (ac
->seq_num
== 0) {
2822 return ldb_module_done(ac
->req
, NULL
, NULL
,
2824 "internal error seq_num == 0"));
2826 ac
->is_urgent
= is_urgent
;
2828 ret
= ldb_build_mod_req(&down_req
, ldb
, ac
,
2831 ac
, replmd_op_callback
,
2833 LDB_REQ_SET_LOCATION(down_req
);
2834 if (ret
!= LDB_SUCCESS
) {
2839 /* current partition control is needed by "replmd_op_callback" */
2840 if (ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
) == NULL
) {
2841 ret
= ldb_request_add_control(down_req
,
2842 DSDB_CONTROL_CURRENT_PARTITION_OID
,
2844 if (ret
!= LDB_SUCCESS
) {
2850 talloc_steal(down_req
, msg
);
2852 ret
= add_time_element(msg
, "whenChanged", t
);
2853 if (ret
!= LDB_SUCCESS
) {
2859 ret
= add_uint64_element(ldb
, msg
, "uSNChanged", ac
->seq_num
);
2860 if (ret
!= LDB_SUCCESS
) {
2866 /* go on with the call chain - do the modify after the rename */
2867 return ldb_next_request(ac
->module
, down_req
);
2871 * remove links from objects that point at this object when an object
2872 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
2873 * RemoveObj which states that link removal due to the object being
2874 * deleted is NOT an originating update - they just go away!
2877 static int replmd_delete_remove_link(struct ldb_module
*module
,
2878 const struct dsdb_schema
*schema
,
2880 struct ldb_message_element
*el
,
2881 const struct dsdb_attribute
*sa
,
2882 struct ldb_request
*parent
)
2885 TALLOC_CTX
*tmp_ctx
= talloc_new(module
);
2886 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
2888 for (i
=0; i
<el
->num_values
; i
++) {
2889 struct dsdb_dn
*dsdb_dn
;
2893 struct ldb_message
*msg
;
2894 const struct dsdb_attribute
*target_attr
;
2895 struct ldb_message_element
*el2
;
2896 struct ldb_val dn_val
;
2898 if (dsdb_dn_is_deleted_val(&el
->values
[i
])) {
2902 dsdb_dn
= dsdb_dn_parse(tmp_ctx
, ldb
, &el
->values
[i
], sa
->syntax
->ldap_oid
);
2904 talloc_free(tmp_ctx
);
2905 return LDB_ERR_OPERATIONS_ERROR
;
2908 status
= dsdb_get_extended_dn_guid(dsdb_dn
->dn
, &guid2
, "GUID");
2909 if (!NT_STATUS_IS_OK(status
)) {
2910 talloc_free(tmp_ctx
);
2911 return LDB_ERR_OPERATIONS_ERROR
;
2914 /* remove the link */
2915 msg
= ldb_msg_new(tmp_ctx
);
2917 ldb_module_oom(module
);
2918 talloc_free(tmp_ctx
);
2919 return LDB_ERR_OPERATIONS_ERROR
;
2923 msg
->dn
= dsdb_dn
->dn
;
2925 target_attr
= dsdb_attribute_by_linkID(schema
, sa
->linkID
^ 1);
2926 if (target_attr
== NULL
) {
2930 ret
= ldb_msg_add_empty(msg
, target_attr
->lDAPDisplayName
, LDB_FLAG_MOD_DELETE
, &el2
);
2931 if (ret
!= LDB_SUCCESS
) {
2932 ldb_module_oom(module
);
2933 talloc_free(tmp_ctx
);
2934 return LDB_ERR_OPERATIONS_ERROR
;
2936 dn_val
= data_blob_string_const(ldb_dn_get_linearized(dn
));
2937 el2
->values
= &dn_val
;
2938 el2
->num_values
= 1;
2940 ret
= dsdb_module_modify(module
, msg
, DSDB_FLAG_OWN_MODULE
, parent
);
2941 if (ret
!= LDB_SUCCESS
) {
2942 talloc_free(tmp_ctx
);
2946 talloc_free(tmp_ctx
);
2952 handle update of replication meta data for deletion of objects
2954 This also handles the mapping of delete to a rename operation
2955 to allow deletes to be replicated.
2957 It also handles the incoming deleted objects, to ensure they are
2958 fully deleted here. In that case re_delete is true, and we do not
2959 use this as a signal to change the deleted state, just reinforce it.
2962 static int replmd_delete_internals(struct ldb_module
*module
, struct ldb_request
*req
, bool re_delete
)
2964 int ret
= LDB_ERR_OTHER
;
2965 bool retb
, disallow_move_on_delete
;
2966 struct ldb_dn
*old_dn
, *new_dn
;
2967 const char *rdn_name
;
2968 const struct ldb_val
*rdn_value
, *new_rdn_value
;
2970 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
2971 const struct dsdb_schema
*schema
;
2972 struct ldb_message
*msg
, *old_msg
;
2973 struct ldb_message_element
*el
;
2974 TALLOC_CTX
*tmp_ctx
;
2975 struct ldb_result
*res
, *parent_res
;
2976 const char *preserved_attrs
[] = {
2977 /* yes, this really is a hard coded list. See MS-ADTS
2978 section 3.1.1.5.5.1.1 */
2979 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2980 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2981 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2982 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2983 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2984 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2985 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2986 "whenChanged", NULL
};
2987 unsigned int i
, el_count
= 0;
2988 enum deletion_state deletion_state
, next_deletion_state
;
2990 if (ldb_dn_is_special(req
->op
.del
.dn
)) {
2991 return ldb_next_request(module
, req
);
2994 tmp_ctx
= talloc_new(ldb
);
2997 return LDB_ERR_OPERATIONS_ERROR
;
3000 schema
= dsdb_get_schema(ldb
, tmp_ctx
);
3002 talloc_free(tmp_ctx
);
3003 return LDB_ERR_OPERATIONS_ERROR
;
3006 old_dn
= ldb_dn_copy(tmp_ctx
, req
->op
.del
.dn
);
3008 /* we need the complete msg off disk, so we can work out which
3009 attributes need to be removed */
3010 ret
= dsdb_module_search_dn(module
, tmp_ctx
, &res
, old_dn
, NULL
,
3011 DSDB_FLAG_NEXT_MODULE
|
3012 DSDB_SEARCH_SHOW_RECYCLED
|
3013 DSDB_SEARCH_REVEAL_INTERNALS
|
3014 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
, req
);
3015 if (ret
!= LDB_SUCCESS
) {
3016 ldb_asprintf_errstring(ldb_module_get_ctx(module
),
3017 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
3018 re_delete
? "re-delete" : "delete",
3019 ldb_dn_get_linearized(old_dn
),
3020 ldb_errstring(ldb_module_get_ctx(module
)));
3021 talloc_free(tmp_ctx
);
3024 old_msg
= res
->msgs
[0];
3026 replmd_deletion_state(module
, old_msg
,
3028 &next_deletion_state
);
3030 /* This supports us noticing an incoming isDeleted and acting on it */
3032 SMB_ASSERT(deletion_state
> OBJECT_NOT_DELETED
);
3033 next_deletion_state
= deletion_state
;
3036 if (next_deletion_state
== OBJECT_REMOVED
) {
3037 struct auth_session_info
*session_info
=
3038 (struct auth_session_info
*)ldb_get_opaque(ldb
, "sessionInfo");
3039 if (security_session_user_level(session_info
, NULL
) != SECURITY_SYSTEM
) {
3040 ldb_asprintf_errstring(ldb
, "Refusing to delete deleted object %s",
3041 ldb_dn_get_linearized(old_msg
->dn
));
3042 return LDB_ERR_UNWILLING_TO_PERFORM
;
3045 /* it is already deleted - really remove it this time */
3046 talloc_free(tmp_ctx
);
3047 return ldb_next_request(module
, req
);
3050 rdn_name
= ldb_dn_get_rdn_name(old_dn
);
3051 rdn_value
= ldb_dn_get_rdn_val(old_dn
);
3052 if ((rdn_name
== NULL
) || (rdn_value
== NULL
)) {
3053 talloc_free(tmp_ctx
);
3054 return ldb_operr(ldb
);
3057 msg
= ldb_msg_new(tmp_ctx
);
3059 ldb_module_oom(module
);
3060 talloc_free(tmp_ctx
);
3061 return LDB_ERR_OPERATIONS_ERROR
;
3066 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
3067 disallow_move_on_delete
=
3068 (ldb_msg_find_attr_as_int(old_msg
, "systemFlags", 0)
3069 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE
);
3071 /* work out where we will be renaming this object to */
3072 if (!disallow_move_on_delete
) {
3073 struct ldb_dn
*deleted_objects_dn
;
3074 ret
= dsdb_get_deleted_objects_dn(ldb
, tmp_ctx
, old_dn
,
3075 &deleted_objects_dn
);
3078 * We should not move objects if we can't find the
3079 * deleted objects DN. Not moving (or otherwise
3080 * harming) the Deleted Objects DN itself is handled
3083 if (re_delete
&& (ret
!= LDB_SUCCESS
)) {
3084 new_dn
= ldb_dn_get_parent(tmp_ctx
, old_dn
);
3085 if (new_dn
== NULL
) {
3086 ldb_module_oom(module
);
3087 talloc_free(tmp_ctx
);
3088 return LDB_ERR_OPERATIONS_ERROR
;
3090 } else if (ret
!= LDB_SUCCESS
) {
3091 /* this is probably an attempted delete on a partition
3092 * that doesn't allow delete operations, such as the
3093 * schema partition */
3094 ldb_asprintf_errstring(ldb
, "No Deleted Objects container for DN %s",
3095 ldb_dn_get_linearized(old_dn
));
3096 talloc_free(tmp_ctx
);
3097 return LDB_ERR_UNWILLING_TO_PERFORM
;
3099 new_dn
= deleted_objects_dn
;
3102 new_dn
= ldb_dn_get_parent(tmp_ctx
, old_dn
);
3103 if (new_dn
== NULL
) {
3104 ldb_module_oom(module
);
3105 talloc_free(tmp_ctx
);
3106 return LDB_ERR_OPERATIONS_ERROR
;
3110 if (deletion_state
== OBJECT_NOT_DELETED
) {
3111 /* get the objects GUID from the search we just did */
3112 guid
= samdb_result_guid(old_msg
, "objectGUID");
3114 /* Add a formatted child */
3115 retb
= ldb_dn_add_child_fmt(new_dn
, "%s=%s\\0ADEL:%s",
3117 ldb_dn_escape_value(tmp_ctx
, *rdn_value
),
3118 GUID_string(tmp_ctx
, &guid
));
3120 DEBUG(0,(__location__
": Unable to add a formatted child to dn: %s",
3121 ldb_dn_get_linearized(new_dn
)));
3122 talloc_free(tmp_ctx
);
3123 return LDB_ERR_OPERATIONS_ERROR
;
3126 ret
= ldb_msg_add_string(msg
, "isDeleted", "TRUE");
3127 if (ret
!= LDB_SUCCESS
) {
3128 DEBUG(0,(__location__
": Failed to add isDeleted string to the msg\n"));
3129 ldb_module_oom(module
);
3130 talloc_free(tmp_ctx
);
3133 msg
->elements
[el_count
++].flags
= LDB_FLAG_MOD_REPLACE
;
3136 * No matter what has happened with other renames etc, try again to
3137 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
3140 struct ldb_dn
*rdn
= ldb_dn_copy(tmp_ctx
, old_dn
);
3141 retb
= ldb_dn_remove_base_components(rdn
, ldb_dn_get_comp_num(rdn
) - 1);
3143 DEBUG(0,(__location__
": Unable to add a prepare rdn of %s",
3144 ldb_dn_get_linearized(rdn
)));
3145 talloc_free(tmp_ctx
);
3146 return LDB_ERR_OPERATIONS_ERROR
;
3148 SMB_ASSERT(ldb_dn_get_comp_num(rdn
) == 1);
3150 retb
= ldb_dn_add_child(new_dn
, rdn
);
3152 DEBUG(0,(__location__
": Unable to add rdn %s to base dn: %s",
3153 ldb_dn_get_linearized(rdn
),
3154 ldb_dn_get_linearized(new_dn
)));
3155 talloc_free(tmp_ctx
);
3156 return LDB_ERR_OPERATIONS_ERROR
;
3161 now we need to modify the object in the following ways:
3163 - add isDeleted=TRUE
3164 - update rDN and name, with new rDN
3165 - remove linked attributes
3166 - remove objectCategory and sAMAccountType
3167 - remove attribs not on the preserved list
3168 - preserved if in above list, or is rDN
3169 - remove all linked attribs from this object
3170 - remove all links from other objects to this object
3171 - add lastKnownParent
3172 - update replPropertyMetaData?
3174 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
3177 if (deletion_state
== OBJECT_NOT_DELETED
) {
3178 /* we need the storage form of the parent GUID */
3179 ret
= dsdb_module_search_dn(module
, tmp_ctx
, &parent_res
,
3180 ldb_dn_get_parent(tmp_ctx
, old_dn
), NULL
,
3181 DSDB_FLAG_NEXT_MODULE
|
3182 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
|
3183 DSDB_SEARCH_REVEAL_INTERNALS
|
3184 DSDB_SEARCH_SHOW_RECYCLED
, req
);
3185 if (ret
!= LDB_SUCCESS
) {
3186 ldb_asprintf_errstring(ldb_module_get_ctx(module
),
3187 "repmd_delete: Failed to %s %s, because we failed to find it's parent (%s): %s",
3188 re_delete
? "re-delete" : "delete",
3189 ldb_dn_get_linearized(old_dn
),
3190 ldb_dn_get_linearized(ldb_dn_get_parent(tmp_ctx
, old_dn
)),
3191 ldb_errstring(ldb_module_get_ctx(module
)));
3192 talloc_free(tmp_ctx
);
3196 ret
= ldb_msg_add_steal_string(msg
, "lastKnownParent",
3197 ldb_dn_get_extended_linearized(tmp_ctx
, parent_res
->msgs
[0]->dn
, 1));
3198 if (ret
!= LDB_SUCCESS
) {
3199 DEBUG(0,(__location__
": Failed to add lastKnownParent string to the msg\n"));
3200 ldb_module_oom(module
);
3201 talloc_free(tmp_ctx
);
3204 msg
->elements
[el_count
++].flags
= LDB_FLAG_MOD_REPLACE
;
3206 if (next_deletion_state
== OBJECT_DELETED
) {
3207 ret
= ldb_msg_add_value(msg
, "msDS-LastKnownRDN", rdn_value
, NULL
);
3208 if (ret
!= LDB_SUCCESS
) {
3209 DEBUG(0,(__location__
": Failed to add msDS-LastKnownRDN string to the msg\n"));
3210 ldb_module_oom(module
);
3211 talloc_free(tmp_ctx
);
3214 msg
->elements
[el_count
++].flags
= LDB_FLAG_MOD_ADD
;
3218 switch (next_deletion_state
) {
3220 case OBJECT_RECYCLED
:
3221 case OBJECT_TOMBSTONE
:
3224 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
3225 * describes what must be removed from a tombstone
3228 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
3229 * describes what must be removed from a recycled
3235 * we also mark it as recycled, meaning this object can't be
3236 * recovered (we are stripping its attributes).
3237 * This is done only if we have this schema object of course ...
3238 * This behavior is identical to the one of Windows 2008R2 which
3239 * always set the isRecycled attribute, even if the recycle-bin is
3240 * not activated and what ever the forest level is.
3242 if (dsdb_attribute_by_lDAPDisplayName(schema
, "isRecycled") != NULL
) {
3243 ret
= ldb_msg_add_string(msg
, "isRecycled", "TRUE");
3244 if (ret
!= LDB_SUCCESS
) {
3245 DEBUG(0,(__location__
": Failed to add isRecycled string to the msg\n"));
3246 ldb_module_oom(module
);
3247 talloc_free(tmp_ctx
);
3250 msg
->elements
[el_count
++].flags
= LDB_FLAG_MOD_REPLACE
;
3253 /* work out which of the old attributes we will be removing */
3254 for (i
=0; i
<old_msg
->num_elements
; i
++) {
3255 const struct dsdb_attribute
*sa
;
3256 el
= &old_msg
->elements
[i
];
3257 sa
= dsdb_attribute_by_lDAPDisplayName(schema
, el
->name
);
3259 talloc_free(tmp_ctx
);
3260 return LDB_ERR_OPERATIONS_ERROR
;
3262 if (ldb_attr_cmp(el
->name
, rdn_name
) == 0) {
3263 /* don't remove the rDN */
3266 if (sa
->linkID
&& (sa
->linkID
& 1)) {
3268 we have a backlink in this object
3269 that needs to be removed. We're not
3270 allowed to remove it directly
3271 however, so we instead setup a
3272 modify to delete the corresponding
3275 ret
= replmd_delete_remove_link(module
, schema
, old_dn
, el
, sa
, req
);
3276 if (ret
!= LDB_SUCCESS
) {
3277 talloc_free(tmp_ctx
);
3278 return LDB_ERR_OPERATIONS_ERROR
;
3280 /* now we continue, which means we
3281 won't remove this backlink
3287 if (ldb_attr_in_list(preserved_attrs
, el
->name
)) {
3290 if (sa
->searchFlags
& SEARCH_FLAG_PRESERVEONDELETE
) {
3294 ret
= ldb_msg_add_empty(msg
, el
->name
, LDB_FLAG_MOD_DELETE
, &el
);
3295 if (ret
!= LDB_SUCCESS
) {
3296 talloc_free(tmp_ctx
);
3297 ldb_module_oom(module
);
3302 /* Duplicate with the below - we remove the
3303 * samAccountType as an originating update, in case it
3304 * somehow came back. The objectCategory will have
3305 * gone in the above */
3306 ret
= ldb_msg_add_empty(msg
, "sAMAccountType", LDB_FLAG_MOD_REPLACE
, NULL
);
3307 if (ret
!= LDB_SUCCESS
) {
3308 talloc_free(tmp_ctx
);
3309 ldb_module_oom(module
);
3315 case OBJECT_DELETED
:
3317 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
3318 * describes what must be removed from a deleted
3322 ret
= ldb_msg_add_empty(msg
, "objectCategory", LDB_FLAG_MOD_REPLACE
, NULL
);
3323 if (ret
!= LDB_SUCCESS
) {
3324 talloc_free(tmp_ctx
);
3325 ldb_module_oom(module
);
3329 ret
= ldb_msg_add_empty(msg
, "sAMAccountType", LDB_FLAG_MOD_REPLACE
, NULL
);
3330 if (ret
!= LDB_SUCCESS
) {
3331 talloc_free(tmp_ctx
);
3332 ldb_module_oom(module
);
3342 if (deletion_state
== OBJECT_NOT_DELETED
) {
3343 const struct dsdb_attribute
*sa
;
3345 /* work out what the new rdn value is, for updating the
3346 rDN and name fields */
3347 new_rdn_value
= ldb_dn_get_rdn_val(new_dn
);
3348 if (new_rdn_value
== NULL
) {
3349 talloc_free(tmp_ctx
);
3350 return ldb_operr(ldb
);
3353 sa
= dsdb_attribute_by_lDAPDisplayName(schema
, rdn_name
);
3355 talloc_free(tmp_ctx
);
3356 return LDB_ERR_OPERATIONS_ERROR
;
3359 ret
= ldb_msg_add_value(msg
, sa
->lDAPDisplayName
, new_rdn_value
,
3361 if (ret
!= LDB_SUCCESS
) {
3362 talloc_free(tmp_ctx
);
3365 el
->flags
= LDB_FLAG_MOD_REPLACE
;
3367 el
= ldb_msg_find_element(old_msg
, "name");
3369 ret
= ldb_msg_add_value(msg
, "name", new_rdn_value
, &el
);
3370 if (ret
!= LDB_SUCCESS
) {
3371 talloc_free(tmp_ctx
);
3374 el
->flags
= LDB_FLAG_MOD_REPLACE
;
3379 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
3383 ret
= dsdb_module_modify(module
, msg
, DSDB_FLAG_OWN_MODULE
, req
);
3384 if (ret
!= LDB_SUCCESS
) {
3385 ldb_asprintf_errstring(ldb
, "replmd_delete: Failed to modify object %s in delete - %s",
3386 ldb_dn_get_linearized(old_dn
), ldb_errstring(ldb
));
3387 talloc_free(tmp_ctx
);
3392 * No matter what has happned with other renames, try again to
3393 * get this to be under the deleted DN.
3395 if (strcmp(ldb_dn_get_linearized(old_dn
), ldb_dn_get_linearized(new_dn
)) != 0) {
3396 /* now rename onto the new DN */
3397 ret
= dsdb_module_rename(module
, old_dn
, new_dn
, DSDB_FLAG_NEXT_MODULE
, req
);
3398 if (ret
!= LDB_SUCCESS
){
3399 DEBUG(0,(__location__
": Failed to rename object from '%s' to '%s' - %s\n",
3400 ldb_dn_get_linearized(old_dn
),
3401 ldb_dn_get_linearized(new_dn
),
3402 ldb_errstring(ldb
)));
3403 talloc_free(tmp_ctx
);
3408 talloc_free(tmp_ctx
);
3410 return ldb_module_done(req
, NULL
, NULL
, LDB_SUCCESS
);
3413 static int replmd_delete(struct ldb_module
*module
, struct ldb_request
*req
)
3415 return replmd_delete_internals(module
, req
, false);
3419 static int replmd_replicated_request_error(struct replmd_replicated_request
*ar
, int ret
)
3424 static int replmd_replicated_request_werror(struct replmd_replicated_request
*ar
, WERROR status
)
3426 int ret
= LDB_ERR_OTHER
;
3427 /* TODO: do some error mapping */
3432 static struct replPropertyMetaData1
*
3433 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob
*md_blob
,
3434 enum drsuapi_DsAttributeId attid
)
3437 struct replPropertyMetaDataCtr1
*rpmd_ctr
= &md_blob
->ctr
.ctr1
;
3439 for (i
= 0; i
< rpmd_ctr
->count
; i
++) {
3440 if (rpmd_ctr
->array
[i
].attid
== attid
) {
3441 return &rpmd_ctr
->array
[i
];
3449 return true if an update is newer than an existing entry
3450 see section 5.11 of MS-ADTS
3452 static bool replmd_update_is_newer(const struct GUID
*current_invocation_id
,
3453 const struct GUID
*update_invocation_id
,
3454 uint32_t current_version
,
3455 uint32_t update_version
,
3456 NTTIME current_change_time
,
3457 NTTIME update_change_time
)
3459 if (update_version
!= current_version
) {
3460 return update_version
> current_version
;
3462 if (update_change_time
!= current_change_time
) {
3463 return update_change_time
> current_change_time
;
3465 return GUID_compare(update_invocation_id
, current_invocation_id
) > 0;
3468 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1
*cur_m
,
3469 struct replPropertyMetaData1
*new_m
)
3471 return replmd_update_is_newer(&cur_m
->originating_invocation_id
,
3472 &new_m
->originating_invocation_id
,
3475 cur_m
->originating_change_time
,
3476 new_m
->originating_change_time
);
3483 static struct ldb_dn
*replmd_conflict_dn(TALLOC_CTX
*mem_ctx
, struct ldb_dn
*dn
, struct GUID
*guid
)
3485 const struct ldb_val
*rdn_val
;
3486 const char *rdn_name
;
3487 struct ldb_dn
*new_dn
;
3489 rdn_val
= ldb_dn_get_rdn_val(dn
);
3490 rdn_name
= ldb_dn_get_rdn_name(dn
);
3491 if (!rdn_val
|| !rdn_name
) {
3495 new_dn
= ldb_dn_copy(mem_ctx
, dn
);
3500 if (!ldb_dn_remove_child_components(new_dn
, 1)) {
3504 if (!ldb_dn_add_child_fmt(new_dn
, "%s=%s\\0ACNF:%s",
3506 ldb_dn_escape_value(new_dn
, *rdn_val
),
3507 GUID_string(new_dn
, guid
))) {
3516 perform a modify operation which sets the rDN and name attributes to
3517 their current values. This has the effect of changing these
3518 attributes to have been last updated by the current DC. This is
3519 needed to ensure that renames performed as part of conflict
3520 resolution are propogated to other DCs
3522 static int replmd_name_modify(struct replmd_replicated_request
*ar
,
3523 struct ldb_request
*req
, struct ldb_dn
*dn
)
3525 struct ldb_message
*msg
;
3526 const char *rdn_name
;
3527 const struct ldb_val
*rdn_val
;
3528 const struct dsdb_attribute
*rdn_attr
;
3531 msg
= ldb_msg_new(req
);
3537 rdn_name
= ldb_dn_get_rdn_name(dn
);
3538 if (rdn_name
== NULL
) {
3542 /* normalize the rdn attribute name */
3543 rdn_attr
= dsdb_attribute_by_lDAPDisplayName(ar
->schema
, rdn_name
);
3544 if (rdn_attr
== NULL
) {
3547 rdn_name
= rdn_attr
->lDAPDisplayName
;
3549 rdn_val
= ldb_dn_get_rdn_val(dn
);
3550 if (rdn_val
== NULL
) {
3554 if (ldb_msg_add_empty(msg
, rdn_name
, LDB_FLAG_MOD_REPLACE
, NULL
) != 0) {
3557 if (ldb_msg_add_value(msg
, rdn_name
, rdn_val
, NULL
) != 0) {
3560 if (ldb_msg_add_empty(msg
, "name", LDB_FLAG_MOD_REPLACE
, NULL
) != 0) {
3563 if (ldb_msg_add_value(msg
, "name", rdn_val
, NULL
) != 0) {
3567 ret
= dsdb_module_modify(ar
->module
, msg
, DSDB_FLAG_OWN_MODULE
, req
);
3568 if (ret
!= LDB_SUCCESS
) {
3569 DEBUG(0,(__location__
": Failed to modify rDN/name of conflict DN '%s' - %s",
3570 ldb_dn_get_linearized(dn
),
3571 ldb_errstring(ldb_module_get_ctx(ar
->module
))));
3581 DEBUG(0,(__location__
": Failed to setup modify rDN/name of conflict DN '%s'",
3582 ldb_dn_get_linearized(dn
)));
3583 return LDB_ERR_OPERATIONS_ERROR
;
3588 callback for conflict DN handling where we have renamed the incoming
3589 record. After renaming it, we need to ensure the change of name and
3590 rDN for the incoming record is seen as an originating update by this DC.
3592 This also handles updating lastKnownParent for entries sent to lostAndFound
3594 static int replmd_op_name_modify_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
3596 struct replmd_replicated_request
*ar
=
3597 talloc_get_type_abort(req
->context
, struct replmd_replicated_request
);
3598 struct ldb_dn
*conflict_dn
;
3601 if (ares
->error
!= LDB_SUCCESS
) {
3602 /* call the normal callback for everything except success */
3603 return replmd_op_callback(req
, ares
);
3606 switch (req
->operation
) {
3608 conflict_dn
= req
->op
.add
.message
->dn
;
3611 conflict_dn
= req
->op
.mod
.message
->dn
;
3614 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
3617 /* perform a modify of the rDN and name of the record */
3618 ret
= replmd_name_modify(ar
, req
, conflict_dn
);
3619 if (ret
!= LDB_SUCCESS
) {
3621 return replmd_op_callback(req
, ares
);
3624 if (ar
->objs
->objects
[ar
->index_current
].last_known_parent
) {
3625 struct ldb_message
*msg
= ldb_msg_new(req
);
3627 ldb_module_oom(ar
->module
);
3628 return LDB_ERR_OPERATIONS_ERROR
;
3631 msg
->dn
= req
->op
.add
.message
->dn
;
3633 ret
= ldb_msg_add_steal_string(msg
, "lastKnownParent",
3634 ldb_dn_get_extended_linearized(msg
, ar
->objs
->objects
[ar
->index_current
].last_known_parent
, 1));
3635 if (ret
!= LDB_SUCCESS
) {
3636 DEBUG(0,(__location__
": Failed to add lastKnownParent string to the msg\n"));
3637 ldb_module_oom(ar
->module
);
3640 msg
->elements
[0].flags
= LDB_FLAG_MOD_REPLACE
;
3642 ret
= dsdb_module_modify(ar
->module
, msg
, DSDB_FLAG_OWN_MODULE
, req
);
3643 if (ret
!= LDB_SUCCESS
) {
3644 DEBUG(0,(__location__
": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
3645 ldb_dn_get_linearized(msg
->dn
),
3646 ldb_errstring(ldb_module_get_ctx(ar
->module
))));
3652 return replmd_op_callback(req
, ares
);
3656 callback for replmd_replicated_apply_add() and replmd_replicated_handle_rename()
3657 This copes with the creation of conflict records in the case where
3658 the DN exists, but with a different objectGUID
3660 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
))
3662 struct ldb_dn
*conflict_dn
;
3663 struct replmd_replicated_request
*ar
=
3664 talloc_get_type_abort(req
->context
, struct replmd_replicated_request
);
3665 struct ldb_result
*res
;
3666 const char *attrs
[] = { "replPropertyMetaData", "objectGUID", NULL
};
3668 const struct ldb_val
*omd_value
;
3669 struct replPropertyMetaDataBlob omd
, *rmd
;
3670 enum ndr_err_code ndr_err
;
3671 bool rename_incoming_record
, rodc
;
3672 struct replPropertyMetaData1
*rmd_name
, *omd_name
;
3673 struct ldb_message
*msg
;
3675 req
->callback
= callback
;
3677 if (ares
->error
!= LDB_ERR_ENTRY_ALREADY_EXISTS
) {
3678 /* call the normal callback for everything except
3680 return ldb_module_done(req
, ares
->controls
, ares
->response
, ares
->error
);
3683 ret
= samdb_rodc(ldb_module_get_ctx(ar
->module
), &rodc
);
3684 if (ret
!= LDB_SUCCESS
) {
3685 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
)));
3686 return ldb_module_done(req
, ares
->controls
, ares
->response
, LDB_ERR_OPERATIONS_ERROR
);
3689 * we have a conflict, and need to decide if we will keep the
3690 * new record or the old record
3693 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
3695 switch (req
->operation
) {
3697 conflict_dn
= msg
->dn
;
3700 conflict_dn
= req
->op
.rename
.newdn
;
3703 return ldb_module_done(req
, ares
->controls
, ares
->response
, ldb_module_operr(ar
->module
));
3708 * We are on an RODC, or were a GC for this
3709 * partition, so we have to fail this until
3710 * someone who owns the partition sorts it
3713 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
3714 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
3715 " - We must fail the operation until a master for this partition resolves the conflict",
3716 ldb_dn_get_linearized(conflict_dn
));
3721 * first we need the replPropertyMetaData attribute from the
3724 ret
= dsdb_module_search_dn(ar
->module
, req
, &res
, conflict_dn
,
3726 DSDB_FLAG_NEXT_MODULE
|
3727 DSDB_SEARCH_SHOW_DELETED
|
3728 DSDB_SEARCH_SHOW_RECYCLED
, req
);
3729 if (ret
!= LDB_SUCCESS
) {
3730 DEBUG(0,(__location__
": Unable to find object for conflicting record '%s'\n",
3731 ldb_dn_get_linearized(conflict_dn
)));
3735 omd_value
= ldb_msg_find_ldb_val(res
->msgs
[0], "replPropertyMetaData");
3736 if (omd_value
== NULL
) {
3737 DEBUG(0,(__location__
": Unable to find replPropertyMetaData for conflicting record '%s'\n",
3738 ldb_dn_get_linearized(conflict_dn
)));
3742 ndr_err
= ndr_pull_struct_blob(omd_value
, res
->msgs
[0], &omd
,
3743 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
3744 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
3745 DEBUG(0,(__location__
": Failed to parse old replPropertyMetaData for %s\n",
3746 ldb_dn_get_linearized(conflict_dn
)));
3750 rmd
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
3752 /* we decide which is newer based on the RPMD on the name
3753 attribute. See [MS-DRSR] ResolveNameConflict */
3754 rmd_name
= replmd_replPropertyMetaData1_find_attid(rmd
, DRSUAPI_ATTID_name
);
3755 omd_name
= replmd_replPropertyMetaData1_find_attid(&omd
, DRSUAPI_ATTID_name
);
3756 if (!rmd_name
|| !omd_name
) {
3757 DEBUG(0,(__location__
": Failed to find name attribute in replPropertyMetaData for %s\n",
3758 ldb_dn_get_linearized(conflict_dn
)));
3762 rename_incoming_record
= !(ar
->objs
->dsdb_repl_flags
& DSDB_REPL_FLAG_PRIORITISE_INCOMING
) &&
3763 !replmd_replPropertyMetaData1_is_newer(omd_name
, rmd_name
);
3765 if (rename_incoming_record
) {
3767 struct ldb_dn
*new_dn
;
3770 * We want to run the original callback here, which
3771 * will return LDB_ERR_ENTRY_ALREADY_EXISTS to the
3772 * caller, which will in turn know to rename the
3773 * incoming record. The error string is set in case
3774 * this isn't handled properly at some point in the
3777 if (req
->operation
== LDB_RENAME
) {
3778 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
3779 "Unable to handle incoming renames where this would "
3780 "create a conflict. Incoming record is %s (caller to handle)\n",
3781 ldb_dn_get_extended_linearized(req
, conflict_dn
, 1));
3786 guid
= samdb_result_guid(msg
, "objectGUID");
3787 if (GUID_all_zero(&guid
)) {
3788 DEBUG(0,(__location__
": Failed to find objectGUID for conflicting incoming record %s\n",
3789 ldb_dn_get_linearized(conflict_dn
)));
3792 new_dn
= replmd_conflict_dn(req
, conflict_dn
, &guid
);
3793 if (new_dn
== NULL
) {
3794 DEBUG(0,(__location__
": Failed to form conflict DN for %s\n",
3795 ldb_dn_get_linearized(conflict_dn
)));
3799 DEBUG(2,(__location__
": Resolving conflict record via incoming rename '%s' -> '%s'\n",
3800 ldb_dn_get_linearized(conflict_dn
), ldb_dn_get_linearized(new_dn
)));
3802 /* re-submit the request, but with a different
3803 callback, so we don't loop forever. */
3805 req
->callback
= replmd_op_name_modify_callback
;
3807 return ldb_next_request(ar
->module
, req
);
3809 /* we are renaming the existing record */
3811 struct ldb_dn
*new_dn
;
3813 guid
= samdb_result_guid(res
->msgs
[0], "objectGUID");
3814 if (GUID_all_zero(&guid
)) {
3815 DEBUG(0,(__location__
": Failed to find objectGUID for existing conflict record %s\n",
3816 ldb_dn_get_linearized(conflict_dn
)));
3820 new_dn
= replmd_conflict_dn(req
, conflict_dn
, &guid
);
3821 if (new_dn
== NULL
) {
3822 DEBUG(0,(__location__
": Failed to form conflict DN for %s\n",
3823 ldb_dn_get_linearized(conflict_dn
)));
3827 DEBUG(2,(__location__
": Resolving conflict record via existing rename '%s' -> '%s'\n",
3828 ldb_dn_get_linearized(conflict_dn
), ldb_dn_get_linearized(new_dn
)));
3830 ret
= dsdb_module_rename(ar
->module
, conflict_dn
, new_dn
,
3831 DSDB_FLAG_OWN_MODULE
, req
);
3832 if (ret
!= LDB_SUCCESS
) {
3833 DEBUG(0,(__location__
": Failed to rename conflict dn '%s' to '%s' - %s\n",
3834 ldb_dn_get_linearized(conflict_dn
),
3835 ldb_dn_get_linearized(new_dn
),
3836 ldb_errstring(ldb_module_get_ctx(ar
->module
))));
3841 * now we need to ensure that the rename is seen as an
3842 * originating update. We do that with a modify.
3844 ret
= replmd_name_modify(ar
, req
, new_dn
);
3845 if (ret
!= LDB_SUCCESS
) {
3849 return ldb_next_request(ar
->module
, req
);
3853 /* on failure do the original callback. This means replication
3854 * will stop with an error, but there is not much else we can
3857 return ldb_module_done(req
, ares
->controls
, ares
->response
, ares
->error
);
3861 callback for replmd_replicated_apply_add()
3862 This copes with the creation of conflict records in the case where
3863 the DN exists, but with a different objectGUID
3865 static int replmd_op_add_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
3867 struct replmd_replicated_request
*ar
=
3868 talloc_get_type_abort(req
->context
, struct replmd_replicated_request
);
3870 if (ar
->objs
->objects
[ar
->index_current
].last_known_parent
) {
3871 /* This is like a conflict DN, where we put the object in LostAndFound
3872 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
3873 return replmd_op_possible_conflict_callback(req
, ares
, replmd_op_name_modify_callback
);
3876 return replmd_op_possible_conflict_callback(req
, ares
, replmd_op_callback
);
3880 callback for replmd_replicated_handle_rename()
3881 This copes with the creation of conflict records in the case where
3882 the DN exists, but with a different objectGUID
3884 static int replmd_op_rename_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
3886 return replmd_op_possible_conflict_callback(req
, ares
, ldb_modify_default_callback
);
3890 this is called when a new object comes in over DRS
3892 static int replmd_replicated_apply_add(struct replmd_replicated_request
*ar
)
3894 struct ldb_context
*ldb
;
3895 struct ldb_request
*change_req
;
3896 enum ndr_err_code ndr_err
;
3897 struct ldb_message
*msg
;
3898 struct replPropertyMetaDataBlob
*md
;
3899 struct ldb_val md_value
;
3902 bool remote_isDeleted
= false;
3903 const struct dsdb_attribute
*rdn_sa
;
3904 const char *rdn_name
;
3906 ldb
= ldb_module_get_ctx(ar
->module
);
3907 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
3908 md
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
3910 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &ar
->seq_num
);
3911 if (ret
!= LDB_SUCCESS
) {
3912 return replmd_replicated_request_error(ar
, ret
);
3915 ret
= ldb_msg_add_value(msg
, "objectGUID", &ar
->objs
->objects
[ar
->index_current
].guid_value
, NULL
);
3916 if (ret
!= LDB_SUCCESS
) {
3917 return replmd_replicated_request_error(ar
, ret
);
3920 ret
= ldb_msg_add_string(msg
, "whenChanged", ar
->objs
->objects
[ar
->index_current
].when_changed
);
3921 if (ret
!= LDB_SUCCESS
) {
3922 return replmd_replicated_request_error(ar
, ret
);
3925 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNCreated", ar
->seq_num
);
3926 if (ret
!= LDB_SUCCESS
) {
3927 return replmd_replicated_request_error(ar
, ret
);
3930 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNChanged", ar
->seq_num
);
3931 if (ret
!= LDB_SUCCESS
) {
3932 return replmd_replicated_request_error(ar
, ret
);
3935 /* remove any message elements that have zero values */
3936 for (i
=0; i
<msg
->num_elements
; i
++) {
3937 struct ldb_message_element
*el
= &msg
->elements
[i
];
3939 if (el
->num_values
== 0) {
3940 DEBUG(4,(__location__
": Removing attribute %s with num_values==0\n",
3942 memmove(el
, el
+1, sizeof(*el
)*(msg
->num_elements
- (i
+1)));
3943 msg
->num_elements
--;
3950 char *s
= ldb_ldif_message_string(ldb
, ar
, LDB_CHANGETYPE_ADD
, msg
);
3951 DEBUG(4, ("DRS replication add message:\n%s\n", s
));
3955 remote_isDeleted
= ldb_msg_find_attr_as_bool(msg
,
3956 "isDeleted", false);
3959 * the meta data array is already sorted by the caller
3962 rdn_name
= ldb_dn_get_rdn_name(msg
->dn
);
3963 if (rdn_name
== NULL
) {
3964 ldb_asprintf_errstring(ldb
, __location__
": No rDN for %s?\n", ldb_dn_get_linearized(msg
->dn
));
3965 return replmd_replicated_request_error(ar
, LDB_ERR_INVALID_DN_SYNTAX
);
3968 rdn_sa
= dsdb_attribute_by_lDAPDisplayName(ar
->schema
, rdn_name
);
3969 if (rdn_sa
== NULL
) {
3970 ldb_asprintf_errstring(ldb
, ": No schema attribute found for rDN %s for %s\n",
3971 rdn_name
, ldb_dn_get_linearized(msg
->dn
));
3972 return replmd_replicated_request_error(ar
, LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE
);
3975 ret
= replmd_replPropertyMetaDataCtr1_verify(ldb
, &md
->ctr
.ctr1
, rdn_sa
, msg
->dn
);
3976 if (ret
!= LDB_SUCCESS
) {
3977 ldb_asprintf_errstring(ldb
, "%s: error during DRS repl ADD: %s", __func__
, ldb_errstring(ldb
));
3978 return replmd_replicated_request_error(ar
, ret
);
3981 for (i
=0; i
< md
->ctr
.ctr1
.count
; i
++) {
3982 md
->ctr
.ctr1
.array
[i
].local_usn
= ar
->seq_num
;
3984 ndr_err
= ndr_push_struct_blob(&md_value
, msg
, md
,
3985 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
3986 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
3987 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
3988 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
3990 ret
= ldb_msg_add_value(msg
, "replPropertyMetaData", &md_value
, NULL
);
3991 if (ret
!= LDB_SUCCESS
) {
3992 return replmd_replicated_request_error(ar
, ret
);
3995 replmd_ldb_message_sort(msg
, ar
->schema
);
3997 if (!remote_isDeleted
) {
3998 ret
= dsdb_module_schedule_sd_propagation(ar
->module
,
3999 ar
->objs
->partition_dn
,
4001 if (ret
!= LDB_SUCCESS
) {
4002 return replmd_replicated_request_error(ar
, ret
);
4006 ar
->isDeleted
= remote_isDeleted
;
4008 ret
= ldb_build_add_req(&change_req
,
4014 replmd_op_add_callback
,
4016 LDB_REQ_SET_LOCATION(change_req
);
4017 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
4019 /* current partition control needed by "repmd_op_callback" */
4020 ret
= ldb_request_add_control(change_req
,
4021 DSDB_CONTROL_CURRENT_PARTITION_OID
,
4023 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
4025 if (ar
->objs
->dsdb_repl_flags
& DSDB_REPL_FLAG_PARTIAL_REPLICA
) {
4026 /* this tells the partition module to make it a
4027 partial replica if creating an NC */
4028 ret
= ldb_request_add_control(change_req
,
4029 DSDB_CONTROL_PARTIAL_REPLICA
,
4031 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
4034 return ldb_next_request(ar
->module
, change_req
);
4037 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request
*req
,
4038 struct ldb_reply
*ares
)
4040 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
4041 struct replmd_replicated_request
);
4045 return ldb_module_done(ar
->req
, NULL
, NULL
,
4046 LDB_ERR_OPERATIONS_ERROR
);
4048 if (ares
->error
!= LDB_SUCCESS
&&
4049 ares
->error
!= LDB_ERR_NO_SUCH_OBJECT
) {
4051 * TODO: deal with the above error that the parent object doesn't exist
4054 return ldb_module_done(ar
->req
, ares
->controls
,
4055 ares
->response
, ares
->error
);
4058 switch (ares
->type
) {
4059 case LDB_REPLY_ENTRY
:
4061 struct ldb_message
*parent_msg
= ares
->message
;
4062 struct ldb_message
*msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
4063 struct ldb_dn
*parent_dn
;
4066 if (!ldb_msg_check_string_attribute(msg
, "isDeleted", "TRUE")
4067 && ldb_msg_check_string_attribute(parent_msg
, "isDeleted", "TRUE")) {
4068 /* Per MS-DRSR 4.1.10.6.10
4069 * FindBestParentObject we need to move this
4070 * new object under a deleted object to
4072 struct ldb_dn
*nc_root
;
4074 ret
= dsdb_find_nc_root(ldb_module_get_ctx(ar
->module
), msg
, msg
->dn
, &nc_root
);
4075 if (ret
== LDB_ERR_NO_SUCH_OBJECT
) {
4076 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
4077 "No suitable NC root found for %s. "
4078 "We need to move this object because parent object %s "
4079 "is deleted, but this object is not.",
4080 ldb_dn_get_linearized(msg
->dn
),
4081 ldb_dn_get_linearized(parent_msg
->dn
));
4082 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
4083 } else if (ret
!= LDB_SUCCESS
) {
4084 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
4085 "Unable to find NC root for %s: %s. "
4086 "We need to move this object because parent object %s "
4087 "is deleted, but this object is not.",
4088 ldb_dn_get_linearized(msg
->dn
),
4089 ldb_errstring(ldb_module_get_ctx(ar
->module
)),
4090 ldb_dn_get_linearized(parent_msg
->dn
));
4091 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
4094 ret
= dsdb_wellknown_dn(ldb_module_get_ctx(ar
->module
), msg
,
4096 DS_GUID_LOSTANDFOUND_CONTAINER
,
4098 if (ret
!= LDB_SUCCESS
) {
4099 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
4100 "Unable to find LostAndFound Container for %s "
4101 "in partition %s: %s. "
4102 "We need to move this object because parent object %s "
4103 "is deleted, but this object is not.",
4104 ldb_dn_get_linearized(msg
->dn
), ldb_dn_get_linearized(nc_root
),
4105 ldb_errstring(ldb_module_get_ctx(ar
->module
)),
4106 ldb_dn_get_linearized(parent_msg
->dn
));
4107 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
4109 ar
->objs
->objects
[ar
->index_current
].last_known_parent
4110 = talloc_steal(ar
->objs
->objects
[ar
->index_current
].msg
, parent_msg
->dn
);
4112 parent_dn
= parent_msg
->dn
;
4115 comp_num
= ldb_dn_get_comp_num(msg
->dn
);
4117 if (!ldb_dn_remove_base_components(msg
->dn
, comp_num
- 1)) {
4119 return ldb_module_done(ar
->req
, NULL
, NULL
, ldb_module_operr(ar
->module
));
4122 if (!ldb_dn_add_base(msg
->dn
, parent_dn
)) {
4124 return ldb_module_done(ar
->req
, NULL
, NULL
, ldb_module_operr(ar
->module
));
4128 case LDB_REPLY_REFERRAL
:
4129 /* we ignore referrals */
4132 case LDB_REPLY_DONE
:
4133 if (ar
->search_msg
!= NULL
) {
4134 ret
= replmd_replicated_apply_merge(ar
);
4136 ret
= replmd_replicated_apply_add(ar
);
4138 if (ret
!= LDB_SUCCESS
) {
4139 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
4148 * Look for the parent object, so we put the new object in the right
4149 * place This is akin to NameObject in MS-DRSR - this routine and the
4150 * callbacks find the right parent name, and correct name for this
4154 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request
*ar
)
4156 struct ldb_context
*ldb
;
4160 struct ldb_request
*search_req
;
4161 static const char *attrs
[] = {"isDeleted", NULL
};
4163 ldb
= ldb_module_get_ctx(ar
->module
);
4165 if (!ar
->objs
->objects
[ar
->index_current
].parent_guid_value
.data
) {
4166 if (ar
->search_msg
!= NULL
) {
4167 return replmd_replicated_apply_merge(ar
);
4169 return replmd_replicated_apply_add(ar
);
4173 tmp_str
= ldb_binary_encode(ar
, ar
->objs
->objects
[ar
->index_current
].parent_guid_value
);
4174 if (!tmp_str
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
4176 filter
= talloc_asprintf(ar
, "(objectGUID=%s)", tmp_str
);
4177 if (!filter
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
4178 talloc_free(tmp_str
);
4180 ret
= ldb_build_search_req(&search_req
,
4183 ar
->objs
->partition_dn
,
4189 replmd_replicated_apply_search_for_parent_callback
,
4191 LDB_REQ_SET_LOCATION(search_req
);
4193 ret
= dsdb_request_add_controls(search_req
,
4194 DSDB_SEARCH_SHOW_RECYCLED
|
4195 DSDB_SEARCH_SHOW_DELETED
|
4196 DSDB_SEARCH_SHOW_EXTENDED_DN
);
4197 if (ret
!= LDB_SUCCESS
) {
4201 return ldb_next_request(ar
->module
, search_req
);
4205 handle renames that come in over DRS replication
4207 static int replmd_replicated_handle_rename(struct replmd_replicated_request
*ar
,
4208 struct ldb_message
*msg
,
4209 struct ldb_request
*parent
)
4211 struct ldb_request
*req
;
4213 TALLOC_CTX
*tmp_ctx
= talloc_new(msg
);
4214 struct ldb_result
*res
;
4216 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
4217 ldb_dn_get_linearized(ar
->search_msg
->dn
),
4218 ldb_dn_get_linearized(msg
->dn
)));
4221 res
= talloc_zero(tmp_ctx
, struct ldb_result
);
4223 talloc_free(tmp_ctx
);
4224 return ldb_oom(ldb_module_get_ctx(ar
->module
));
4227 /* pass rename to the next module
4228 * so it doesn't appear as an originating update */
4229 ret
= ldb_build_rename_req(&req
, ldb_module_get_ctx(ar
->module
), tmp_ctx
,
4230 ar
->search_msg
->dn
, msg
->dn
,
4233 replmd_op_rename_callback
,
4235 LDB_REQ_SET_LOCATION(req
);
4236 if (ret
!= LDB_SUCCESS
) {
4237 talloc_free(tmp_ctx
);
4241 ret
= dsdb_request_add_controls(req
, DSDB_MODIFY_RELAX
);
4242 if (ret
!= LDB_SUCCESS
) {
4243 talloc_free(tmp_ctx
);
4247 ret
= ldb_next_request(ar
->module
, req
);
4249 if (ret
== LDB_SUCCESS
) {
4250 ret
= ldb_wait(req
->handle
, LDB_WAIT_ALL
);
4253 talloc_free(tmp_ctx
);
4258 static int replmd_replicated_apply_merge(struct replmd_replicated_request
*ar
)
4260 struct ldb_context
*ldb
;
4261 struct ldb_request
*change_req
;
4262 enum ndr_err_code ndr_err
;
4263 struct ldb_message
*msg
;
4264 struct replPropertyMetaDataBlob
*rmd
;
4265 struct replPropertyMetaDataBlob omd
;
4266 const struct ldb_val
*omd_value
;
4267 struct replPropertyMetaDataBlob nmd
;
4268 struct ldb_val nmd_value
;
4271 unsigned int removed_attrs
= 0;
4273 int (*callback
)(struct ldb_request
*req
, struct ldb_reply
*ares
) = replmd_op_callback
;
4274 bool isDeleted
= false;
4275 bool local_isDeleted
= false;
4276 bool remote_isDeleted
= false;
4277 bool take_remote_isDeleted
= false;
4278 bool sd_updated
= false;
4279 bool renamed
= false;
4281 ldb
= ldb_module_get_ctx(ar
->module
);
4282 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
4284 rmd
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
4288 /* find existing meta data */
4289 omd_value
= ldb_msg_find_ldb_val(ar
->search_msg
, "replPropertyMetaData");
4291 ndr_err
= ndr_pull_struct_blob(omd_value
, ar
, &omd
,
4292 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
4293 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
4294 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
4295 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
4298 if (omd
.version
!= 1) {
4299 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
4303 local_isDeleted
= ldb_msg_find_attr_as_bool(ar
->search_msg
,
4304 "isDeleted", false);
4305 remote_isDeleted
= ldb_msg_find_attr_as_bool(msg
,
4306 "isDeleted", false);
4308 if (strcmp(ldb_dn_get_linearized(msg
->dn
), ldb_dn_get_linearized(ar
->search_msg
->dn
)) == 0) {
4312 * handle renames, even just by case that come in over
4313 * DRS. Changes in the parent DN don't hit us here,
4314 * because the search for a parent will clean up those
4317 * We also have already filtered out the case where
4318 * the peer has an older name to what we have (see
4319 * replmd_replicated_apply_search_callback())
4322 ret
= replmd_replicated_handle_rename(ar
, msg
, ar
->req
);
4326 * This particular error code means that we already tried the
4327 * conflict algrorithm, and the existing record name was newer, so we
4328 * need to rename the incoming record
4330 if (ret
== LDB_ERR_ENTRY_ALREADY_EXISTS
) {
4333 struct ldb_dn
*new_dn
;
4334 status
= GUID_from_ndr_blob(&ar
->objs
->objects
[ar
->index_current
].guid_value
, &guid
);
4335 /* This really, really can't fail */
4336 SMB_ASSERT(NT_STATUS_IS_OK(status
));
4338 new_dn
= replmd_conflict_dn(msg
, msg
->dn
, &guid
);
4339 if (new_dn
== NULL
) {
4340 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
4341 "Failed to form conflict DN for %s\n",
4342 ldb_dn_get_linearized(msg
->dn
));
4344 return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
4347 ret
= dsdb_module_rename(ar
->module
, ar
->search_msg
->dn
, new_dn
,
4348 DSDB_FLAG_NEXT_MODULE
, ar
->req
);
4349 if (ret
!= LDB_SUCCESS
) {
4350 ldb_asprintf_errstring(ldb_module_get_ctx(ar
->module
),
4351 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
4352 ldb_dn_get_linearized(msg
->dn
),
4353 ldb_dn_get_linearized(ar
->search_msg
->dn
),
4354 ldb_dn_get_linearized(new_dn
),
4355 ldb_errstring(ldb_module_get_ctx(ar
->module
)));
4356 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_DB_ERROR
);
4359 /* Set the callback to one that will fix up the name to be a conflict DN */
4360 callback
= replmd_op_name_modify_callback
;
4363 } else if (ret
!= LDB_SUCCESS
) {
4364 ldb_debug(ldb
, LDB_DEBUG_FATAL
,
4365 "replmd_replicated_request rename %s => %s failed - %s\n",
4366 ldb_dn_get_linearized(ar
->search_msg
->dn
),
4367 ldb_dn_get_linearized(msg
->dn
),
4368 ldb_errstring(ldb
));
4369 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_DB_ERROR
);
4374 nmd
.ctr
.ctr1
.count
= omd
.ctr
.ctr1
.count
+ rmd
->ctr
.ctr1
.count
;
4375 nmd
.ctr
.ctr1
.array
= talloc_array(ar
,
4376 struct replPropertyMetaData1
,
4377 nmd
.ctr
.ctr1
.count
);
4378 if (!nmd
.ctr
.ctr1
.array
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
4380 /* first copy the old meta data */
4381 for (i
=0; i
< omd
.ctr
.ctr1
.count
; i
++) {
4382 nmd
.ctr
.ctr1
.array
[ni
] = omd
.ctr
.ctr1
.array
[i
];
4387 /* now merge in the new meta data */
4388 for (i
=0; i
< rmd
->ctr
.ctr1
.count
; i
++) {
4391 for (j
=0; j
< ni
; j
++) {
4394 if (rmd
->ctr
.ctr1
.array
[i
].attid
!= nmd
.ctr
.ctr1
.array
[j
].attid
) {
4398 if (ar
->objs
->dsdb_repl_flags
& DSDB_REPL_FLAG_PRIORITISE_INCOMING
) {
4399 /* if we compare equal then do an
4400 update. This is used when a client
4401 asks for a FULL_SYNC, and can be
4402 used to recover a corrupt
4404 cmp
= !replmd_replPropertyMetaData1_is_newer(&rmd
->ctr
.ctr1
.array
[i
],
4405 &nmd
.ctr
.ctr1
.array
[j
]);
4407 cmp
= replmd_replPropertyMetaData1_is_newer(&nmd
.ctr
.ctr1
.array
[j
],
4408 &rmd
->ctr
.ctr1
.array
[i
]);
4411 /* replace the entry */
4412 nmd
.ctr
.ctr1
.array
[j
] = rmd
->ctr
.ctr1
.array
[i
];
4413 if (ar
->seq_num
== 0) {
4414 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &ar
->seq_num
);
4415 if (ret
!= LDB_SUCCESS
) {
4416 return replmd_replicated_request_error(ar
, ret
);
4419 nmd
.ctr
.ctr1
.array
[j
].local_usn
= ar
->seq_num
;
4420 switch (nmd
.ctr
.ctr1
.array
[j
].attid
) {
4421 case DRSUAPI_ATTID_ntSecurityDescriptor
:
4424 case DRSUAPI_ATTID_isDeleted
:
4425 take_remote_isDeleted
= true;
4434 if (rmd
->ctr
.ctr1
.array
[i
].attid
!= DRSUAPI_ATTID_instanceType
) {
4435 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
4436 msg
->elements
[i
-removed_attrs
].name
,
4437 ldb_dn_get_linearized(msg
->dn
),
4438 GUID_string(ar
, &rmd
->ctr
.ctr1
.array
[i
].originating_invocation_id
)));
4441 /* we don't want to apply this change so remove the attribute */
4442 ldb_msg_remove_element(msg
, &msg
->elements
[i
-removed_attrs
]);
4449 if (found
) continue;
4451 nmd
.ctr
.ctr1
.array
[ni
] = rmd
->ctr
.ctr1
.array
[i
];
4452 if (ar
->seq_num
== 0) {
4453 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &ar
->seq_num
);
4454 if (ret
!= LDB_SUCCESS
) {
4455 return replmd_replicated_request_error(ar
, ret
);
4458 nmd
.ctr
.ctr1
.array
[ni
].local_usn
= ar
->seq_num
;
4459 switch (nmd
.ctr
.ctr1
.array
[ni
].attid
) {
4460 case DRSUAPI_ATTID_ntSecurityDescriptor
:
4463 case DRSUAPI_ATTID_isDeleted
:
4464 take_remote_isDeleted
= true;
4473 * finally correct the size of the meta_data array
4475 nmd
.ctr
.ctr1
.count
= ni
;
4478 * the rdn attribute (the alias for the name attribute),
4479 * 'cn' for most objects is the last entry in the meta data array
4482 * sort the new meta data array
4484 ret
= replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb
, &nmd
.ctr
.ctr1
, ar
->schema
, msg
->dn
);
4485 if (ret
!= LDB_SUCCESS
) {
4486 ldb_asprintf_errstring(ldb
, "%s: error during DRS repl merge: %s", __func__
, ldb_errstring(ldb
));
4491 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
4494 * This also controls SD propagation below
4496 if (take_remote_isDeleted
) {
4497 isDeleted
= remote_isDeleted
;
4499 isDeleted
= local_isDeleted
;
4502 ar
->isDeleted
= isDeleted
;
4505 * check if some replicated attributes left, otherwise skip the ldb_modify() call
4507 if (msg
->num_elements
== 0) {
4508 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_replicated_apply_merge[%u]: skip replace\n",
4511 return replmd_replicated_apply_isDeleted(ar
);
4514 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
4515 ar
->index_current
, msg
->num_elements
);
4521 if (sd_updated
&& !isDeleted
) {
4522 ret
= dsdb_module_schedule_sd_propagation(ar
->module
,
4523 ar
->objs
->partition_dn
,
4525 if (ret
!= LDB_SUCCESS
) {
4526 return ldb_operr(ldb
);
4530 /* create the meta data value */
4531 ndr_err
= ndr_push_struct_blob(&nmd_value
, msg
, &nmd
,
4532 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
4533 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
4534 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
4535 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
4539 * when we know that we'll modify the record, add the whenChanged, uSNChanged
4540 * and replPopertyMetaData attributes
4542 ret
= ldb_msg_add_string(msg
, "whenChanged", ar
->objs
->objects
[ar
->index_current
].when_changed
);
4543 if (ret
!= LDB_SUCCESS
) {
4544 return replmd_replicated_request_error(ar
, ret
);
4546 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNChanged", ar
->seq_num
);
4547 if (ret
!= LDB_SUCCESS
) {
4548 return replmd_replicated_request_error(ar
, ret
);
4550 ret
= ldb_msg_add_value(msg
, "replPropertyMetaData", &nmd_value
, NULL
);
4551 if (ret
!= LDB_SUCCESS
) {
4552 return replmd_replicated_request_error(ar
, ret
);
4555 replmd_ldb_message_sort(msg
, ar
->schema
);
4557 /* we want to replace the old values */
4558 for (i
=0; i
< msg
->num_elements
; i
++) {
4559 msg
->elements
[i
].flags
= LDB_FLAG_MOD_REPLACE
;
4560 if (ldb_attr_cmp(msg
->elements
[i
].name
, "objectClass") == 0) {
4561 if (msg
->elements
[i
].num_values
== 0) {
4562 ldb_asprintf_errstring(ldb
, __location__
4563 ": objectClass removed on %s, aborting replication\n",
4564 ldb_dn_get_linearized(msg
->dn
));
4565 return replmd_replicated_request_error(ar
, LDB_ERR_OPERATIONS_ERROR
);
4571 char *s
= ldb_ldif_message_string(ldb
, ar
, LDB_CHANGETYPE_MODIFY
, msg
);
4572 DEBUG(4, ("DRS replication modify message:\n%s\n", s
));
4576 ret
= ldb_build_mod_req(&change_req
,
4584 LDB_REQ_SET_LOCATION(change_req
);
4585 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
4587 /* current partition control needed by "repmd_op_callback" */
4588 ret
= ldb_request_add_control(change_req
,
4589 DSDB_CONTROL_CURRENT_PARTITION_OID
,
4591 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
4593 return ldb_next_request(ar
->module
, change_req
);
4596 static int replmd_replicated_apply_search_callback(struct ldb_request
*req
,
4597 struct ldb_reply
*ares
)
4599 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
4600 struct replmd_replicated_request
);
4604 return ldb_module_done(ar
->req
, NULL
, NULL
,
4605 LDB_ERR_OPERATIONS_ERROR
);
4607 if (ares
->error
!= LDB_SUCCESS
&&
4608 ares
->error
!= LDB_ERR_NO_SUCH_OBJECT
) {
4609 return ldb_module_done(ar
->req
, ares
->controls
,
4610 ares
->response
, ares
->error
);
4613 switch (ares
->type
) {
4614 case LDB_REPLY_ENTRY
:
4615 ar
->search_msg
= talloc_steal(ar
, ares
->message
);
4618 case LDB_REPLY_REFERRAL
:
4619 /* we ignore referrals */
4622 case LDB_REPLY_DONE
:
4624 struct replPropertyMetaData1
*md_remote
;
4625 struct replPropertyMetaData1
*md_local
;
4627 struct replPropertyMetaDataBlob omd
;
4628 const struct ldb_val
*omd_value
;
4629 struct replPropertyMetaDataBlob
*rmd
;
4630 struct ldb_message
*msg
;
4632 ar
->objs
->objects
[ar
->index_current
].last_known_parent
= NULL
;
4635 * This is the ADD case, find the appropriate parent,
4636 * as this object doesn't exist locally:
4638 if (ar
->search_msg
== NULL
) {
4639 ret
= replmd_replicated_apply_search_for_parent(ar
);
4640 if (ret
!= LDB_SUCCESS
) {
4641 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
4648 * Otherwise, in the MERGE case, work out if we are
4649 * attempting a rename, and if so find the parent the
4650 * newly renamed object wants to belong under (which
4651 * may not be the parent in it's attached string DN
4653 rmd
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
4657 /* find existing meta data */
4658 omd_value
= ldb_msg_find_ldb_val(ar
->search_msg
, "replPropertyMetaData");
4660 enum ndr_err_code ndr_err
;
4661 ndr_err
= ndr_pull_struct_blob(omd_value
, ar
, &omd
,
4662 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
4663 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
4664 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
4665 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
4668 if (omd
.version
!= 1) {
4669 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
4674 * now we need to check for double renames. We could have a
4675 * local rename pending which our replication partner hasn't
4676 * received yet. We choose which one wins by looking at the
4677 * attribute stamps on the two objects, the newer one wins
4679 md_remote
= replmd_replPropertyMetaData1_find_attid(rmd
, DRSUAPI_ATTID_name
);
4680 md_local
= replmd_replPropertyMetaData1_find_attid(&omd
, DRSUAPI_ATTID_name
);
4681 /* if there is no name attribute then we have to assume the
4682 object we've received is in fact newer */
4683 if (ar
->objs
->dsdb_repl_flags
& DSDB_REPL_FLAG_PRIORITISE_INCOMING
||
4684 !md_remote
|| !md_local
||
4685 replmd_replPropertyMetaData1_is_newer(md_local
, md_remote
)) {
4686 ret
= replmd_replicated_apply_search_for_parent(ar
);
4688 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
4690 /* Otherwise, just merge on the existing object, force no rename */
4691 DEBUG(4,(__location__
": Keeping object %s and rejecting older rename to %s\n",
4692 ldb_dn_get_linearized(ar
->search_msg
->dn
),
4693 ldb_dn_get_linearized(msg
->dn
)));
4696 * This assignment ensures that the strcmp()
4697 * in replmd_replicated_apply_merge() avoids
4700 msg
->dn
= ar
->search_msg
->dn
;
4701 ret
= replmd_replicated_apply_merge(ar
);
4703 if (ret
!= LDB_SUCCESS
) {
4704 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
4713 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request
*ar
);
4715 static int replmd_replicated_apply_next(struct replmd_replicated_request
*ar
)
4717 struct ldb_context
*ldb
;
4721 struct ldb_request
*search_req
;
4723 if (ar
->index_current
>= ar
->objs
->num_objects
) {
4724 /* done with it, go to next stage */
4725 return replmd_replicated_uptodate_vector(ar
);
4728 ldb
= ldb_module_get_ctx(ar
->module
);
4729 ar
->search_msg
= NULL
;
4730 ar
->isDeleted
= false;
4732 tmp_str
= ldb_binary_encode(ar
, ar
->objs
->objects
[ar
->index_current
].guid_value
);
4733 if (!tmp_str
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
4735 filter
= talloc_asprintf(ar
, "(objectGUID=%s)", tmp_str
);
4736 if (!filter
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
4737 talloc_free(tmp_str
);
4739 ret
= ldb_build_search_req(&search_req
,
4748 replmd_replicated_apply_search_callback
,
4750 LDB_REQ_SET_LOCATION(search_req
);
4752 ret
= dsdb_request_add_controls(search_req
, DSDB_SEARCH_SEARCH_ALL_PARTITIONS
|DSDB_SEARCH_SHOW_RECYCLED
);
4754 if (ret
!= LDB_SUCCESS
) {
4758 return ldb_next_request(ar
->module
, search_req
);
4762 * This is essentially a wrapper for replmd_replicated_apply_next()
4764 * This is needed to ensure that both codepaths call this handler.
4766 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request
*ar
)
4768 struct ldb_dn
*deleted_objects_dn
;
4769 struct ldb_message
*msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
4770 int ret
= dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar
->module
), msg
, msg
->dn
,
4771 &deleted_objects_dn
);
4772 if (ar
->isDeleted
&& (ret
!= LDB_SUCCESS
|| ldb_dn_compare(msg
->dn
, deleted_objects_dn
) != 0)) {
4774 * Do a delete here again, so that if there is
4775 * anything local that conflicts with this
4776 * object being deleted, it is removed. This
4777 * includes links. See MS-DRSR 4.1.10.6.9
4780 * If the object is already deleted, and there
4781 * is no more work required, it doesn't do
4785 /* This has been updated to point to the DN we eventually did the modify on */
4787 struct ldb_request
*del_req
;
4788 struct ldb_result
*res
;
4790 TALLOC_CTX
*tmp_ctx
= talloc_new(ar
);
4792 ret
= ldb_oom(ldb_module_get_ctx(ar
->module
));
4796 res
= talloc_zero(tmp_ctx
, struct ldb_result
);
4798 ret
= ldb_oom(ldb_module_get_ctx(ar
->module
));
4799 talloc_free(tmp_ctx
);
4803 /* Build a delete request, which hopefully will artually turn into nothing */
4804 ret
= ldb_build_del_req(&del_req
, ldb_module_get_ctx(ar
->module
), tmp_ctx
,
4808 ldb_modify_default_callback
,
4810 LDB_REQ_SET_LOCATION(del_req
);
4811 if (ret
!= LDB_SUCCESS
) {
4812 talloc_free(tmp_ctx
);
4817 * This is the guts of the call, call back
4818 * into our delete code, but setting the
4819 * re_delete flag so we delete anything that
4820 * shouldn't be there on a deleted or recycled
4823 ret
= replmd_delete_internals(ar
->module
, del_req
, true);
4824 if (ret
== LDB_SUCCESS
) {
4825 ret
= ldb_wait(del_req
->handle
, LDB_WAIT_ALL
);
4828 talloc_free(tmp_ctx
);
4829 if (ret
!= LDB_SUCCESS
) {
4834 ar
->index_current
++;
4835 return replmd_replicated_apply_next(ar
);
4838 static int replmd_replicated_uptodate_modify_callback(struct ldb_request
*req
,
4839 struct ldb_reply
*ares
)
4841 struct ldb_context
*ldb
;
4842 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
4843 struct replmd_replicated_request
);
4844 ldb
= ldb_module_get_ctx(ar
->module
);
4847 return ldb_module_done(ar
->req
, NULL
, NULL
,
4848 LDB_ERR_OPERATIONS_ERROR
);
4850 if (ares
->error
!= LDB_SUCCESS
) {
4851 return ldb_module_done(ar
->req
, ares
->controls
,
4852 ares
->response
, ares
->error
);
4855 if (ares
->type
!= LDB_REPLY_DONE
) {
4856 ldb_asprintf_errstring(ldb
, "Invalid LDB reply type %d", ares
->type
);
4857 return ldb_module_done(ar
->req
, NULL
, NULL
,
4858 LDB_ERR_OPERATIONS_ERROR
);
4863 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_SUCCESS
);
4866 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request
*ar
)
4868 struct ldb_context
*ldb
;
4869 struct ldb_request
*change_req
;
4870 enum ndr_err_code ndr_err
;
4871 struct ldb_message
*msg
;
4872 struct replUpToDateVectorBlob ouv
;
4873 const struct ldb_val
*ouv_value
;
4874 const struct drsuapi_DsReplicaCursor2CtrEx
*ruv
;
4875 struct replUpToDateVectorBlob nuv
;
4876 struct ldb_val nuv_value
;
4877 struct ldb_message_element
*nuv_el
= NULL
;
4878 const struct GUID
*our_invocation_id
;
4879 struct ldb_message_element
*orf_el
= NULL
;
4880 struct repsFromToBlob nrf
;
4881 struct ldb_val
*nrf_value
= NULL
;
4882 struct ldb_message_element
*nrf_el
= NULL
;
4886 time_t t
= time(NULL
);
4889 uint32_t instanceType
;
4891 ldb
= ldb_module_get_ctx(ar
->module
);
4892 ruv
= ar
->objs
->uptodateness_vector
;
4898 unix_to_nt_time(&now
, t
);
4900 if (ar
->search_msg
== NULL
) {
4901 /* this happens for a REPL_OBJ call where we are
4902 creating the target object by replicating it. The
4903 subdomain join code does this for the partition DN
4905 DEBUG(4,(__location__
": Skipping UDV and repsFrom update as no target DN\n"));
4906 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_SUCCESS
);
4909 instanceType
= ldb_msg_find_attr_as_uint(ar
->search_msg
, "instanceType", 0);
4910 if (! (instanceType
& INSTANCE_TYPE_IS_NC_HEAD
)) {
4911 DEBUG(4,(__location__
": Skipping UDV and repsFrom update as not NC root: %s\n",
4912 ldb_dn_get_linearized(ar
->search_msg
->dn
)));
4913 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_SUCCESS
);
4917 * first create the new replUpToDateVector
4919 ouv_value
= ldb_msg_find_ldb_val(ar
->search_msg
, "replUpToDateVector");
4921 ndr_err
= ndr_pull_struct_blob(ouv_value
, ar
, &ouv
,
4922 (ndr_pull_flags_fn_t
)ndr_pull_replUpToDateVectorBlob
);
4923 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
4924 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
4925 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
4928 if (ouv
.version
!= 2) {
4929 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
4934 * the new uptodateness vector will at least
4935 * contain 1 entry, one for the source_dsa
4937 * plus optional values from our old vector and the one from the source_dsa
4939 nuv
.ctr
.ctr2
.count
= ouv
.ctr
.ctr2
.count
;
4940 if (ruv
) nuv
.ctr
.ctr2
.count
+= ruv
->count
;
4941 nuv
.ctr
.ctr2
.cursors
= talloc_array(ar
,
4942 struct drsuapi_DsReplicaCursor2
,
4943 nuv
.ctr
.ctr2
.count
);
4944 if (!nuv
.ctr
.ctr2
.cursors
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
4946 /* first copy the old vector */
4947 for (i
=0; i
< ouv
.ctr
.ctr2
.count
; i
++) {
4948 nuv
.ctr
.ctr2
.cursors
[ni
] = ouv
.ctr
.ctr2
.cursors
[i
];
4952 /* get our invocation_id if we have one already attached to the ldb */
4953 our_invocation_id
= samdb_ntds_invocation_id(ldb
);
4954 if (our_invocation_id
== NULL
) {
4955 DEBUG(0, ("repl_meta_data: Could not find our own server's invocationID!\n"));
4956 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
4959 /* merge in the source_dsa vector is available */
4960 for (i
=0; (ruv
&& i
< ruv
->count
); i
++) {
4963 if (our_invocation_id
&&
4964 GUID_equal(&ruv
->cursors
[i
].source_dsa_invocation_id
,
4965 our_invocation_id
)) {
4969 for (j
=0; j
< ni
; j
++) {
4970 if (!GUID_equal(&ruv
->cursors
[i
].source_dsa_invocation_id
,
4971 &nuv
.ctr
.ctr2
.cursors
[j
].source_dsa_invocation_id
)) {
4977 if (ruv
->cursors
[i
].highest_usn
> nuv
.ctr
.ctr2
.cursors
[j
].highest_usn
) {
4978 nuv
.ctr
.ctr2
.cursors
[j
] = ruv
->cursors
[i
];
4983 if (found
) continue;
4985 /* if it's not there yet, add it */
4986 nuv
.ctr
.ctr2
.cursors
[ni
] = ruv
->cursors
[i
];
4991 * finally correct the size of the cursors array
4993 nuv
.ctr
.ctr2
.count
= ni
;
4998 TYPESAFE_QSORT(nuv
.ctr
.ctr2
.cursors
, nuv
.ctr
.ctr2
.count
, drsuapi_DsReplicaCursor2_compare
);
5001 * create the change ldb_message
5003 msg
= ldb_msg_new(ar
);
5004 if (!msg
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
5005 msg
->dn
= ar
->search_msg
->dn
;
5007 ndr_err
= ndr_push_struct_blob(&nuv_value
, msg
, &nuv
,
5008 (ndr_push_flags_fn_t
)ndr_push_replUpToDateVectorBlob
);
5009 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
5010 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
5011 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
5013 ret
= ldb_msg_add_value(msg
, "replUpToDateVector", &nuv_value
, &nuv_el
);
5014 if (ret
!= LDB_SUCCESS
) {
5015 return replmd_replicated_request_error(ar
, ret
);
5017 nuv_el
->flags
= LDB_FLAG_MOD_REPLACE
;
5020 * now create the new repsFrom value from the given repsFromTo1 structure
5024 nrf
.ctr
.ctr1
= *ar
->objs
->source_dsa
;
5025 nrf
.ctr
.ctr1
.last_attempt
= now
;
5026 nrf
.ctr
.ctr1
.last_success
= now
;
5027 nrf
.ctr
.ctr1
.result_last_attempt
= WERR_OK
;
5030 * first see if we already have a repsFrom value for the current source dsa
5031 * if so we'll later replace this value
5033 orf_el
= ldb_msg_find_element(ar
->search_msg
, "repsFrom");
5035 for (i
=0; i
< orf_el
->num_values
; i
++) {
5036 struct repsFromToBlob
*trf
;
5038 trf
= talloc(ar
, struct repsFromToBlob
);
5039 if (!trf
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
5041 ndr_err
= ndr_pull_struct_blob(&orf_el
->values
[i
], trf
, trf
,
5042 (ndr_pull_flags_fn_t
)ndr_pull_repsFromToBlob
);
5043 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
5044 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
5045 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
5048 if (trf
->version
!= 1) {
5049 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
5053 * we compare the source dsa objectGUID not the invocation_id
5054 * because we want only one repsFrom value per source dsa
5055 * and when the invocation_id of the source dsa has changed we don't need
5056 * the old repsFrom with the old invocation_id
5058 if (!GUID_equal(&trf
->ctr
.ctr1
.source_dsa_obj_guid
,
5059 &ar
->objs
->source_dsa
->source_dsa_obj_guid
)) {
5065 nrf_value
= &orf_el
->values
[i
];
5070 * copy over all old values to the new ldb_message
5072 ret
= ldb_msg_add_empty(msg
, "repsFrom", 0, &nrf_el
);
5073 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
5078 * if we haven't found an old repsFrom value for the current source dsa
5079 * we'll add a new value
5082 struct ldb_val zero_value
;
5083 ZERO_STRUCT(zero_value
);
5084 ret
= ldb_msg_add_value(msg
, "repsFrom", &zero_value
, &nrf_el
);
5085 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
5087 nrf_value
= &nrf_el
->values
[nrf_el
->num_values
- 1];
5090 /* we now fill the value which is already attached to ldb_message */
5091 ndr_err
= ndr_push_struct_blob(nrf_value
, msg
,
5093 (ndr_push_flags_fn_t
)ndr_push_repsFromToBlob
);
5094 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
5095 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
5096 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
5100 * the ldb_message_element for the attribute, has all the old values and the new one
5101 * so we'll replace the whole attribute with all values
5103 nrf_el
->flags
= LDB_FLAG_MOD_REPLACE
;
5105 if (CHECK_DEBUGLVL(4)) {
5106 char *s
= ldb_ldif_message_string(ldb
, ar
, LDB_CHANGETYPE_MODIFY
, msg
);
5107 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s
));
5111 /* prepare the ldb_modify() request */
5112 ret
= ldb_build_mod_req(&change_req
,
5118 replmd_replicated_uptodate_modify_callback
,
5120 LDB_REQ_SET_LOCATION(change_req
);
5121 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
5123 return ldb_next_request(ar
->module
, change_req
);
5126 static int replmd_replicated_uptodate_search_callback(struct ldb_request
*req
,
5127 struct ldb_reply
*ares
)
5129 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
5130 struct replmd_replicated_request
);
5134 return ldb_module_done(ar
->req
, NULL
, NULL
,
5135 LDB_ERR_OPERATIONS_ERROR
);
5137 if (ares
->error
!= LDB_SUCCESS
&&
5138 ares
->error
!= LDB_ERR_NO_SUCH_OBJECT
) {
5139 return ldb_module_done(ar
->req
, ares
->controls
,
5140 ares
->response
, ares
->error
);
5143 switch (ares
->type
) {
5144 case LDB_REPLY_ENTRY
:
5145 ar
->search_msg
= talloc_steal(ar
, ares
->message
);
5148 case LDB_REPLY_REFERRAL
:
5149 /* we ignore referrals */
5152 case LDB_REPLY_DONE
:
5153 ret
= replmd_replicated_uptodate_modify(ar
);
5154 if (ret
!= LDB_SUCCESS
) {
5155 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
5164 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request
*ar
)
5166 struct ldb_context
*ldb
;
5168 static const char *attrs
[] = {
5169 "replUpToDateVector",
5174 struct ldb_request
*search_req
;
5176 ldb
= ldb_module_get_ctx(ar
->module
);
5177 ar
->search_msg
= NULL
;
5179 ret
= ldb_build_search_req(&search_req
,
5182 ar
->objs
->partition_dn
,
5188 replmd_replicated_uptodate_search_callback
,
5190 LDB_REQ_SET_LOCATION(search_req
);
5191 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
5193 return ldb_next_request(ar
->module
, search_req
);
5198 static int replmd_extended_replicated_objects(struct ldb_module
*module
, struct ldb_request
*req
)
5200 struct ldb_context
*ldb
;
5201 struct dsdb_extended_replicated_objects
*objs
;
5202 struct replmd_replicated_request
*ar
;
5203 struct ldb_control
**ctrls
;
5206 struct replmd_private
*replmd_private
=
5207 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
5208 struct dsdb_control_replicated_update
*rep_update
;
5210 ldb
= ldb_module_get_ctx(module
);
5212 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_extended_replicated_objects\n");
5214 objs
= talloc_get_type(req
->op
.extended
.data
, struct dsdb_extended_replicated_objects
);
5216 ldb_debug(ldb
, LDB_DEBUG_FATAL
, "replmd_extended_replicated_objects: invalid extended data\n");
5217 return LDB_ERR_PROTOCOL_ERROR
;
5220 if (objs
->version
!= DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION
) {
5221 ldb_debug(ldb
, LDB_DEBUG_FATAL
, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
5222 objs
->version
, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION
);
5223 return LDB_ERR_PROTOCOL_ERROR
;
5226 ar
= replmd_ctx_init(module
, req
);
5228 return LDB_ERR_OPERATIONS_ERROR
;
5230 /* Set the flags to have the replmd_op_callback run over the full set of objects */
5231 ar
->apply_mode
= true;
5233 ar
->schema
= dsdb_get_schema(ldb
, ar
);
5235 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
, "replmd_ctx_init: no loaded schema found\n");
5237 DEBUG(0,(__location__
": %s\n", ldb_errstring(ldb
)));
5238 return LDB_ERR_CONSTRAINT_VIOLATION
;
5241 ctrls
= req
->controls
;
5243 if (req
->controls
) {
5244 req
->controls
= talloc_memdup(ar
, req
->controls
,
5245 talloc_get_size(req
->controls
));
5246 if (!req
->controls
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
5249 /* This allows layers further down to know if a change came in
5250 over replication and what the replication flags were */
5251 rep_update
= talloc_zero(ar
, struct dsdb_control_replicated_update
);
5252 if (rep_update
== NULL
) {
5253 return ldb_module_oom(module
);
5255 rep_update
->dsdb_repl_flags
= objs
->dsdb_repl_flags
;
5257 ret
= ldb_request_add_control(req
, DSDB_CONTROL_REPLICATED_UPDATE_OID
, false, rep_update
);
5258 if (ret
!= LDB_SUCCESS
) {
5262 /* If this change contained linked attributes in the body
5263 * (rather than in the links section) we need to update
5264 * backlinks in linked_attributes */
5265 ret
= ldb_request_add_control(req
, DSDB_CONTROL_APPLY_LINKS
, false, NULL
);
5266 if (ret
!= LDB_SUCCESS
) {
5270 ar
->controls
= req
->controls
;
5271 req
->controls
= ctrls
;
5273 DEBUG(4,("linked_attributes_count=%u\n", objs
->linked_attributes_count
));
5275 /* save away the linked attributes for the end of the
5277 for (i
=0; i
<ar
->objs
->linked_attributes_count
; i
++) {
5278 struct la_entry
*la_entry
;
5280 if (replmd_private
->la_ctx
== NULL
) {
5281 replmd_private
->la_ctx
= talloc_new(replmd_private
);
5283 la_entry
= talloc(replmd_private
->la_ctx
, struct la_entry
);
5284 if (la_entry
== NULL
) {
5286 return LDB_ERR_OPERATIONS_ERROR
;
5288 la_entry
->la
= talloc(la_entry
, struct drsuapi_DsReplicaLinkedAttribute
);
5289 if (la_entry
->la
== NULL
) {
5290 talloc_free(la_entry
);
5292 return LDB_ERR_OPERATIONS_ERROR
;
5294 *la_entry
->la
= ar
->objs
->linked_attributes
[i
];
5296 /* we need to steal the non-scalars so they stay
5297 around until the end of the transaction */
5298 talloc_steal(la_entry
->la
, la_entry
->la
->identifier
);
5299 talloc_steal(la_entry
->la
, la_entry
->la
->value
.blob
);
5301 DLIST_ADD(replmd_private
->la_list
, la_entry
);
5304 return replmd_replicated_apply_next(ar
);
5308 process one linked attribute structure
5310 static int replmd_process_linked_attribute(struct ldb_module
*module
,
5311 struct la_entry
*la_entry
,
5312 struct ldb_request
*parent
)
5314 struct drsuapi_DsReplicaLinkedAttribute
*la
= la_entry
->la
;
5315 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
5316 struct ldb_message
*msg
;
5317 struct ldb_message
*target_msg
= NULL
;
5318 TALLOC_CTX
*tmp_ctx
= talloc_new(la_entry
);
5319 const struct dsdb_schema
*schema
= dsdb_get_schema(ldb
, tmp_ctx
);
5321 const struct dsdb_attribute
*attr
;
5322 struct dsdb_dn
*dsdb_dn
;
5323 uint64_t seq_num
= 0;
5324 struct ldb_message_element
*old_el
;
5326 time_t t
= time(NULL
);
5327 struct ldb_result
*res
;
5328 struct ldb_result
*target_res
;
5329 const char *attrs
[4];
5330 const char *attrs2
[] = { "isDeleted", "isRecycled", NULL
};
5331 struct parsed_dn
*pdn_list
, *pdn
;
5332 struct GUID guid
= GUID_zero();
5334 bool active
= (la
->flags
& DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
)?true:false;
5335 const struct GUID
*our_invocation_id
;
5337 enum deletion_state deletion_state
= OBJECT_NOT_DELETED
;
5338 enum deletion_state target_deletion_state
= OBJECT_NOT_DELETED
;
5341 linked_attributes[0]:
5342 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
5344 identifier: struct drsuapi_DsReplicaObjectIdentifier
5345 __ndr_size : 0x0000003a (58)
5346 __ndr_size_sid : 0x00000000 (0)
5347 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
5349 __ndr_size_dn : 0x00000000 (0)
5351 attid : DRSUAPI_ATTID_member (0x1F)
5352 value: struct drsuapi_DsAttributeValue
5353 __ndr_size : 0x0000007e (126)
5355 blob : DATA_BLOB length=126
5356 flags : 0x00000001 (1)
5357 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
5358 originating_add_time : Wed Sep 2 22:20:01 2009 EST
5359 meta_data: struct drsuapi_DsReplicaMetaData
5360 version : 0x00000015 (21)
5361 originating_change_time : Wed Sep 2 23:39:07 2009 EST
5362 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
5363 originating_usn : 0x000000000001e19c (123292)
5365 (for cases where the link is to a normal DN)
5366 &target: struct drsuapi_DsReplicaObjectIdentifier3
5367 __ndr_size : 0x0000007e (126)
5368 __ndr_size_sid : 0x0000001c (28)
5369 guid : 7639e594-db75-4086-b0d4-67890ae46031
5370 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
5371 __ndr_size_dn : 0x00000022 (34)
5372 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
5375 /* find the attribute being modified */
5376 attr
= dsdb_attribute_by_attributeID_id(schema
, la
->attid
);
5378 DEBUG(0, (__location__
": Unable to find attributeID 0x%x\n", la
->attid
));
5379 talloc_free(tmp_ctx
);
5380 return LDB_ERR_OPERATIONS_ERROR
;
5383 attrs
[0] = attr
->lDAPDisplayName
;
5384 attrs
[1] = "isDeleted";
5385 attrs
[1] = "isRecycled";
5388 /* get the existing message from the db for the object with
5389 this GUID, returning attribute being modified. We will then
5390 use this msg as the basis for a modify call */
5391 ret
= dsdb_module_search(module
, tmp_ctx
, &res
, NULL
, LDB_SCOPE_SUBTREE
, attrs
,
5392 DSDB_FLAG_NEXT_MODULE
|
5393 DSDB_SEARCH_SEARCH_ALL_PARTITIONS
|
5394 DSDB_SEARCH_SHOW_RECYCLED
|
5395 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
|
5396 DSDB_SEARCH_REVEAL_INTERNALS
,
5398 "objectGUID=%s", GUID_string(tmp_ctx
, &la
->identifier
->guid
));
5399 if (ret
!= LDB_SUCCESS
) {
5400 talloc_free(tmp_ctx
);
5403 if (res
->count
!= 1) {
5404 ldb_asprintf_errstring(ldb
, "DRS linked attribute for GUID %s - DN not found",
5405 GUID_string(tmp_ctx
, &la
->identifier
->guid
));
5406 talloc_free(tmp_ctx
);
5407 return LDB_ERR_NO_SUCH_OBJECT
;
5412 * Check for deleted objects per MS-DRSR 4.1.10.6.13
5413 * ProcessLinkValue, because link updates are not applied to
5414 * recycled and tombstone objects. We don't have to delete
5415 * any existing link, that should have happened when the
5416 * object deletion was replicated or initiated.
5419 replmd_deletion_state(module
, msg
, &deletion_state
, NULL
);
5421 if (deletion_state
>= OBJECT_RECYCLED
) {
5422 talloc_free(tmp_ctx
);
5426 old_el
= ldb_msg_find_element(msg
, attr
->lDAPDisplayName
);
5427 if (old_el
== NULL
) {
5428 ret
= ldb_msg_add_empty(msg
, attr
->lDAPDisplayName
, LDB_FLAG_MOD_REPLACE
, &old_el
);
5429 if (ret
!= LDB_SUCCESS
) {
5430 ldb_module_oom(module
);
5431 talloc_free(tmp_ctx
);
5432 return LDB_ERR_OPERATIONS_ERROR
;
5435 old_el
->flags
= LDB_FLAG_MOD_REPLACE
;
5438 /* parse the existing links */
5439 ret
= get_parsed_dns(module
, tmp_ctx
, old_el
, &pdn_list
, attr
->syntax
->ldap_oid
, parent
);
5440 if (ret
!= LDB_SUCCESS
) {
5441 talloc_free(tmp_ctx
);
5445 /* get our invocationId */
5446 our_invocation_id
= samdb_ntds_invocation_id(ldb
);
5447 if (!our_invocation_id
) {
5448 ldb_debug_set(ldb
, LDB_DEBUG_ERROR
, __location__
": unable to find invocationId\n");
5449 talloc_free(tmp_ctx
);
5450 return LDB_ERR_OPERATIONS_ERROR
;
5453 ret
= replmd_check_upgrade_links(pdn_list
, old_el
->num_values
, old_el
, our_invocation_id
);
5454 if (ret
!= LDB_SUCCESS
) {
5455 talloc_free(tmp_ctx
);
5459 status
= dsdb_dn_la_from_blob(ldb
, attr
, schema
, tmp_ctx
, la
->value
.blob
, &dsdb_dn
);
5460 if (!W_ERROR_IS_OK(status
)) {
5461 ldb_asprintf_errstring(ldb
, "Failed to parsed linked attribute blob for %s on %s - %s\n",
5462 old_el
->name
, ldb_dn_get_linearized(msg
->dn
), win_errstr(status
));
5463 talloc_free(tmp_ctx
);
5464 return LDB_ERR_OPERATIONS_ERROR
;
5467 ntstatus
= dsdb_get_extended_dn_guid(dsdb_dn
->dn
, &guid
, "GUID");
5468 if (!NT_STATUS_IS_OK(ntstatus
) && !active
) {
5470 * This strange behaviour (allowing a NULL/missing
5471 * GUID) originally comes from:
5473 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
5474 * Author: Andrew Tridgell <tridge@samba.org>
5475 * Date: Mon Dec 21 21:21:55 2009 +1100
5477 * s4-drs: cope better with NULL GUIDS from DRS
5479 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
5480 * need to match by DN if possible when seeing if we should update an
5483 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
5486 ret
= dsdb_module_search_dn(module
, tmp_ctx
, &target_res
,
5487 dsdb_dn
->dn
, attrs2
,
5488 DSDB_FLAG_NEXT_MODULE
|
5489 DSDB_SEARCH_SHOW_RECYCLED
|
5490 DSDB_SEARCH_SEARCH_ALL_PARTITIONS
|
5491 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
,
5493 } else if (!NT_STATUS_IS_OK(ntstatus
)) {
5494 ldb_asprintf_errstring(ldb
, "Failed to find GUID in linked attribute blob for %s on %s from %s",
5496 ldb_dn_get_linearized(dsdb_dn
->dn
),
5497 ldb_dn_get_linearized(msg
->dn
));
5498 talloc_free(tmp_ctx
);
5499 return LDB_ERR_OPERATIONS_ERROR
;
5501 ret
= dsdb_module_search(module
, tmp_ctx
, &target_res
,
5502 NULL
, LDB_SCOPE_SUBTREE
,
5504 DSDB_FLAG_NEXT_MODULE
|
5505 DSDB_SEARCH_SHOW_RECYCLED
|
5506 DSDB_SEARCH_SEARCH_ALL_PARTITIONS
|
5507 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
,
5510 GUID_string(tmp_ctx
, &guid
));
5513 if (ret
!= LDB_SUCCESS
) {
5514 ldb_asprintf_errstring(ldb_module_get_ctx(module
), "Failed to re-resolve GUID %s: %s\n",
5515 GUID_string(tmp_ctx
, &guid
),
5516 ldb_errstring(ldb_module_get_ctx(module
)));
5517 talloc_free(tmp_ctx
);
5521 if (target_res
->count
== 0) {
5522 DEBUG(2,(__location__
": WARNING: Failed to re-resolve GUID %s - using %s\n",
5523 GUID_string(tmp_ctx
, &guid
),
5524 ldb_dn_get_linearized(dsdb_dn
->dn
)));
5525 } else if (target_res
->count
!= 1) {
5526 ldb_asprintf_errstring(ldb_module_get_ctx(module
), "More than one object found matching objectGUID %s\n",
5527 GUID_string(tmp_ctx
, &guid
));
5528 talloc_free(tmp_ctx
);
5529 return LDB_ERR_OPERATIONS_ERROR
;
5531 target_msg
= target_res
->msgs
[0];
5532 dsdb_dn
->dn
= talloc_steal(dsdb_dn
, target_msg
->dn
);
5536 * Check for deleted objects per MS-DRSR 4.1.10.6.13
5537 * ProcessLinkValue, because link updates are not applied to
5538 * recycled and tombstone objects. We don't have to delete
5539 * any existing link, that should have happened when the
5540 * object deletion was replicated or initiated.
5542 replmd_deletion_state(module
, target_msg
,
5543 &target_deletion_state
, NULL
);
5545 if (target_deletion_state
>= OBJECT_RECYCLED
) {
5546 talloc_free(tmp_ctx
);
5550 /* see if this link already exists */
5551 pdn
= parsed_dn_find(pdn_list
, old_el
->num_values
, &guid
, dsdb_dn
->dn
);
5553 /* see if this update is newer than what we have already */
5554 struct GUID invocation_id
= GUID_zero();
5555 uint32_t version
= 0;
5556 uint32_t originating_usn
= 0;
5557 NTTIME change_time
= 0;
5558 uint32_t rmd_flags
= dsdb_dn_rmd_flags(pdn
->dsdb_dn
->dn
);
5560 dsdb_get_extended_dn_guid(pdn
->dsdb_dn
->dn
, &invocation_id
, "RMD_INVOCID");
5561 dsdb_get_extended_dn_uint32(pdn
->dsdb_dn
->dn
, &version
, "RMD_VERSION");
5562 dsdb_get_extended_dn_uint32(pdn
->dsdb_dn
->dn
, &originating_usn
, "RMD_ORIGINATING_USN");
5563 dsdb_get_extended_dn_nttime(pdn
->dsdb_dn
->dn
, &change_time
, "RMD_CHANGETIME");
5565 if (!replmd_update_is_newer(&invocation_id
,
5566 &la
->meta_data
.originating_invocation_id
,
5568 la
->meta_data
.version
,
5570 la
->meta_data
.originating_change_time
)) {
5571 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
5572 old_el
->name
, ldb_dn_get_linearized(msg
->dn
),
5573 GUID_string(tmp_ctx
, &la
->meta_data
.originating_invocation_id
)));
5574 talloc_free(tmp_ctx
);
5578 /* get a seq_num for this change */
5579 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &seq_num
);
5580 if (ret
!= LDB_SUCCESS
) {
5581 talloc_free(tmp_ctx
);
5585 if (!(rmd_flags
& DSDB_RMD_FLAG_DELETED
)) {
5586 /* remove the existing backlink */
5587 ret
= replmd_add_backlink(module
, schema
, &la
->identifier
->guid
, &guid
, false, attr
, false);
5588 if (ret
!= LDB_SUCCESS
) {
5589 talloc_free(tmp_ctx
);
5594 ret
= replmd_update_la_val(tmp_ctx
, pdn
->v
, dsdb_dn
, pdn
->dsdb_dn
,
5595 &la
->meta_data
.originating_invocation_id
,
5596 la
->meta_data
.originating_usn
, seq_num
,
5597 la
->meta_data
.originating_change_time
,
5598 la
->meta_data
.version
,
5600 if (ret
!= LDB_SUCCESS
) {
5601 talloc_free(tmp_ctx
);
5606 /* add the new backlink */
5607 ret
= replmd_add_backlink(module
, schema
, &la
->identifier
->guid
, &guid
, true, attr
, false);
5608 if (ret
!= LDB_SUCCESS
) {
5609 talloc_free(tmp_ctx
);
5614 /* get a seq_num for this change */
5615 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &seq_num
);
5616 if (ret
!= LDB_SUCCESS
) {
5617 talloc_free(tmp_ctx
);
5621 old_el
->values
= talloc_realloc(msg
->elements
, old_el
->values
,
5622 struct ldb_val
, old_el
->num_values
+1);
5623 if (!old_el
->values
) {
5624 ldb_module_oom(module
);
5625 return LDB_ERR_OPERATIONS_ERROR
;
5627 old_el
->num_values
++;
5629 ret
= replmd_build_la_val(tmp_ctx
, &old_el
->values
[old_el
->num_values
-1], dsdb_dn
,
5630 &la
->meta_data
.originating_invocation_id
,
5631 la
->meta_data
.originating_usn
, seq_num
,
5632 la
->meta_data
.originating_change_time
,
5633 la
->meta_data
.version
,
5634 (la
->flags
& DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
)?false:true);
5635 if (ret
!= LDB_SUCCESS
) {
5636 talloc_free(tmp_ctx
);
5641 ret
= replmd_add_backlink(module
, schema
, &la
->identifier
->guid
, &guid
,
5643 if (ret
!= LDB_SUCCESS
) {
5644 talloc_free(tmp_ctx
);
5650 /* we only change whenChanged and uSNChanged if the seq_num
5652 ret
= add_time_element(msg
, "whenChanged", t
);
5653 if (ret
!= LDB_SUCCESS
) {
5654 talloc_free(tmp_ctx
);
5659 ret
= add_uint64_element(ldb
, msg
, "uSNChanged", seq_num
);
5660 if (ret
!= LDB_SUCCESS
) {
5661 talloc_free(tmp_ctx
);
5666 old_el
= ldb_msg_find_element(msg
, attr
->lDAPDisplayName
);
5667 if (old_el
== NULL
) {
5668 talloc_free(tmp_ctx
);
5669 return ldb_operr(ldb
);
5672 ret
= dsdb_check_single_valued_link(attr
, old_el
);
5673 if (ret
!= LDB_SUCCESS
) {
5674 talloc_free(tmp_ctx
);
5678 old_el
->flags
|= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK
;
5680 ret
= dsdb_module_modify(module
, msg
, DSDB_FLAG_NEXT_MODULE
, parent
);
5681 if (ret
!= LDB_SUCCESS
) {
5682 ldb_debug(ldb
, LDB_DEBUG_WARNING
, "Failed to apply linked attribute change '%s'\n%s\n",
5684 ldb_ldif_message_string(ldb
, tmp_ctx
, LDB_CHANGETYPE_MODIFY
, msg
));
5685 talloc_free(tmp_ctx
);
5689 talloc_free(tmp_ctx
);
5694 static int replmd_extended(struct ldb_module
*module
, struct ldb_request
*req
)
5696 if (strcmp(req
->op
.extended
.oid
, DSDB_EXTENDED_REPLICATED_OBJECTS_OID
) == 0) {
5697 return replmd_extended_replicated_objects(module
, req
);
5700 return ldb_next_request(module
, req
);
5705 we hook into the transaction operations to allow us to
5706 perform the linked attribute updates at the end of the whole
5707 transaction. This allows a forward linked attribute to be created
5708 before the object is created. During a vampire, w2k8 sends us linked
5709 attributes before the objects they are part of.
5711 static int replmd_start_transaction(struct ldb_module
*module
)
5713 /* create our private structure for this transaction */
5714 struct replmd_private
*replmd_private
= talloc_get_type(ldb_module_get_private(module
),
5715 struct replmd_private
);
5716 replmd_txn_cleanup(replmd_private
);
5718 /* free any leftover mod_usn records from cancelled
5720 while (replmd_private
->ncs
) {
5721 struct nc_entry
*e
= replmd_private
->ncs
;
5722 DLIST_REMOVE(replmd_private
->ncs
, e
);
5726 return ldb_next_start_trans(module
);
5730 on prepare commit we loop over our queued la_context structures and
5733 static int replmd_prepare_commit(struct ldb_module
*module
)
5735 struct replmd_private
*replmd_private
=
5736 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
5737 struct la_entry
*la
, *prev
;
5738 struct la_backlink
*bl
;
5741 /* walk the list backwards, to do the first entry first, as we
5742 * added the entries with DLIST_ADD() which puts them at the
5743 * start of the list */
5744 for (la
= DLIST_TAIL(replmd_private
->la_list
); la
; la
=prev
) {
5745 prev
= DLIST_PREV(la
);
5746 DLIST_REMOVE(replmd_private
->la_list
, la
);
5747 ret
= replmd_process_linked_attribute(module
, la
, NULL
);
5748 if (ret
!= LDB_SUCCESS
) {
5749 replmd_txn_cleanup(replmd_private
);
5754 /* process our backlink list, creating and deleting backlinks
5756 for (bl
=replmd_private
->la_backlinks
; bl
; bl
=bl
->next
) {
5757 ret
= replmd_process_backlink(module
, bl
, NULL
);
5758 if (ret
!= LDB_SUCCESS
) {
5759 replmd_txn_cleanup(replmd_private
);
5764 replmd_txn_cleanup(replmd_private
);
5766 /* possibly change @REPLCHANGED */
5767 ret
= replmd_notify_store(module
, NULL
);
5768 if (ret
!= LDB_SUCCESS
) {
5772 return ldb_next_prepare_commit(module
);
5775 static int replmd_del_transaction(struct ldb_module
*module
)
5777 struct replmd_private
*replmd_private
=
5778 talloc_get_type(ldb_module_get_private(module
), struct replmd_private
);
5779 replmd_txn_cleanup(replmd_private
);
5781 return ldb_next_del_trans(module
);
5785 static const struct ldb_module_ops ldb_repl_meta_data_module_ops
= {
5786 .name
= "repl_meta_data",
5787 .init_context
= replmd_init
,
5789 .modify
= replmd_modify
,
5790 .rename
= replmd_rename
,
5791 .del
= replmd_delete
,
5792 .extended
= replmd_extended
,
5793 .start_transaction
= replmd_start_transaction
,
5794 .prepare_commit
= replmd_prepare_commit
,
5795 .del_transaction
= replmd_del_transaction
,
5798 int ldb_repl_meta_data_module_init(const char *version
)
5800 LDB_MODULE_CHECK_VERSION(version
);
5801 return ldb_register_module(&ldb_repl_meta_data_module_ops
);