2 Unix SMB/CIFS mplementation.
3 Helper functions for applying replicated objects
5 Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "dsdb/samdb/samdb.h"
24 #include <ldb_errors.h>
25 #include "../lib/util/dlinklist.h"
26 #include "librpc/gen_ndr/ndr_misc.h"
27 #include "librpc/gen_ndr/ndr_drsuapi.h"
28 #include "librpc/gen_ndr/ndr_drsblobs.h"
29 #include "../libcli/drsuapi/drsuapi.h"
30 #include "libcli/auth/libcli_auth.h"
31 #include "param/param.h"
34 #define DBGC_CLASS DBGC_DRS_REPL
36 static WERROR
dsdb_repl_merge_working_schema(struct ldb_context
*ldb
,
37 struct dsdb_schema
*dest_schema
,
38 const struct dsdb_schema
*ref_schema
)
40 const struct dsdb_class
*cur_class
= NULL
;
41 const struct dsdb_attribute
*cur_attr
= NULL
;
44 for (cur_class
= ref_schema
->classes
;
46 cur_class
= cur_class
->next
)
48 const struct dsdb_class
*tmp1
;
49 struct dsdb_class
*tmp2
;
51 tmp1
= dsdb_class_by_governsID_id(dest_schema
,
52 cur_class
->governsID_id
);
58 * Do a shallow copy so that original next and prev are
59 * not modified, we don't need to do a deep copy
60 * as the rest won't be modified and this is for
61 * a short lived object.
63 tmp2
= talloc(dest_schema
, struct dsdb_class
);
65 return WERR_NOT_ENOUGH_MEMORY
;
68 DLIST_ADD(dest_schema
->classes
, tmp2
);
71 for (cur_attr
= ref_schema
->attributes
;
73 cur_attr
= cur_attr
->next
)
75 const struct dsdb_attribute
*tmp1
;
76 struct dsdb_attribute
*tmp2
;
78 tmp1
= dsdb_attribute_by_attributeID_id(dest_schema
,
79 cur_attr
->attributeID_id
);
85 * Do a shallow copy so that original next and prev are
86 * not modified, we don't need to do a deep copy
87 * as the rest won't be modified and this is for
88 * a short lived object.
90 tmp2
= talloc(dest_schema
, struct dsdb_attribute
);
92 return WERR_NOT_ENOUGH_MEMORY
;
95 DLIST_ADD(dest_schema
->attributes
, tmp2
);
98 ret
= dsdb_setup_sorted_accessors(ldb
, dest_schema
);
99 if (LDB_SUCCESS
!= ret
) {
100 DEBUG(0,("Failed to add new attribute to reference schema!\n"));
101 return WERR_INTERNAL_ERROR
;
107 WERROR
dsdb_repl_resolve_working_schema(struct ldb_context
*ldb
,
108 struct dsdb_schema_prefixmap
*pfm_remote
,
109 uint32_t cycle_before_switching
,
110 struct dsdb_schema
*initial_schema
,
111 struct dsdb_schema
*resulting_schema
,
112 uint32_t object_count
,
113 const struct drsuapi_DsReplicaObjectListItemEx
*first_object
)
116 struct schema_list
*next
, *prev
;
117 const struct drsuapi_DsReplicaObjectListItemEx
*obj
;
119 struct schema_list
*schema_list
= NULL
, *schema_list_item
, *schema_list_next_item
;
121 struct dsdb_schema
*working_schema
;
122 const struct drsuapi_DsReplicaObjectListItemEx
*cur
;
123 DATA_BLOB empty_key
= data_blob_null
;
125 uint32_t ignore_attids
[] = {
126 DRSUAPI_ATTID_auxiliaryClass
,
127 DRSUAPI_ATTID_mayContain
,
128 DRSUAPI_ATTID_mustContain
,
129 DRSUAPI_ATTID_possSuperiors
,
130 DRSUAPI_ATTID_systemPossSuperiors
,
131 DRSUAPI_ATTID_INVALID
133 TALLOC_CTX
*frame
= talloc_stackframe();
135 /* create a list of objects yet to be converted */
136 for (cur
= first_object
; cur
; cur
= cur
->next_object
) {
137 schema_list_item
= talloc(frame
, struct schema_list
);
138 if (schema_list_item
== NULL
) {
139 return WERR_NOT_ENOUGH_MEMORY
;
142 schema_list_item
->obj
= cur
;
143 DLIST_ADD_END(schema_list
, schema_list_item
);
146 /* resolve objects until all are resolved and in local schema */
148 working_schema
= initial_schema
;
150 while (schema_list
) {
151 uint32_t converted_obj_count
= 0;
152 uint32_t failed_obj_count
= 0;
154 if (resulting_schema
!= working_schema
) {
156 * If the selfmade schema is not the schema used to
157 * translate and validate replicated object,
158 * Which means that we are using the bootstrap schema
159 * Then we add attributes and classes that were already
160 * translated to the working schema, the idea is that
161 * we might need to add new attributes and classes
162 * to be able to translate critical replicated objects
163 * and without that we wouldn't be able to translate them
165 werr
= dsdb_repl_merge_working_schema(ldb
,
168 if (!W_ERROR_IS_OK(werr
)) {
174 for (schema_list_item
= schema_list
;
176 schema_list_item
=schema_list_next_item
) {
177 struct dsdb_extended_replicated_object object
;
179 cur
= schema_list_item
->obj
;
182 * Save the next item, now we have saved out
183 * the current one, so we can DLIST_REMOVE it
186 schema_list_next_item
= schema_list_item
->next
;
189 * Convert the objects into LDB messages using the
190 * schema we have so far. It's ok if we fail to convert
191 * an object. We should convert more objects on next pass.
193 werr
= dsdb_convert_object_ex(ldb
, working_schema
,
199 schema_list_item
, &object
);
200 if (!W_ERROR_IS_OK(werr
)) {
201 DEBUG(4,("debug: Failed to convert schema "
202 "object %s into ldb msg, "
203 "will try during next loop\n",
204 cur
->object
.identifier
->dn
));
209 * Convert the schema from ldb_message format
210 * (OIDs as OID strings) into schema, using
211 * the remote prefixMap
213 * It's not likely, but possible to get the
214 * same object twice and we should keep
217 werr
= dsdb_schema_set_el_from_ldb_msg_dups(ldb
,
221 if (!W_ERROR_IS_OK(werr
)) {
222 DEBUG(4,("debug: failed to convert "
223 "object %s into a schema element, "
224 "will try during next loop: %s\n",
225 ldb_dn_get_linearized(object
.msg
->dn
),
229 DEBUG(8,("Converted object %s into a schema element\n",
230 ldb_dn_get_linearized(object
.msg
->dn
)));
231 DLIST_REMOVE(schema_list
, schema_list_item
);
232 TALLOC_FREE(schema_list_item
);
233 converted_obj_count
++;
238 DEBUG(4,("Schema load pass %d: converted %d, %d of %d objects left to be converted.\n",
239 pass_no
, converted_obj_count
, failed_obj_count
, object_count
));
241 /* check if we converted any objects in this pass */
242 if (converted_obj_count
== 0) {
243 DEBUG(0,("Can't continue Schema load: "
244 "didn't manage to convert any objects: "
245 "all %d remaining of %d objects "
246 "failed to convert\n",
247 failed_obj_count
, object_count
));
249 return WERR_INTERNAL_ERROR
;
253 * Don't try to load the schema if there is missing object
254 * _and_ we are on the first pass as some critical objects
257 if (failed_obj_count
== 0 || pass_no
> cycle_before_switching
) {
258 /* prepare for another cycle */
259 working_schema
= resulting_schema
;
261 ret
= dsdb_setup_sorted_accessors(ldb
, working_schema
);
262 if (LDB_SUCCESS
!= ret
) {
263 DEBUG(0,("Failed to create schema-cache indexes!\n"));
265 return WERR_INTERNAL_ERROR
;
276 * Multi-pass working schema creation
278 * - shallow copy initial schema supplied
279 * - create a working schema in multiple passes
280 * until all objects are resolved
281 * Working schema is a schema with Attributes, Classes
282 * and indexes, but w/o subClassOf, possibleSupperiors etc.
283 * It is to be used just us cache for converting attribute values.
285 WERROR
dsdb_repl_make_working_schema(struct ldb_context
*ldb
,
286 const struct dsdb_schema
*initial_schema
,
287 const struct drsuapi_DsReplicaOIDMapping_Ctr
*mapping_ctr
,
288 uint32_t object_count
,
289 const struct drsuapi_DsReplicaObjectListItemEx
*first_object
,
290 const DATA_BLOB
*gensec_skey
,
292 struct dsdb_schema
**_schema_out
)
295 struct dsdb_schema_prefixmap
*pfm_remote
;
297 struct dsdb_schema
*working_schema
;
299 /* make a copy of the iniatial_scheam so we don't mess with it */
300 working_schema
= dsdb_schema_copy_shallow(mem_ctx
, ldb
, initial_schema
);
301 if (!working_schema
) {
302 DEBUG(0,(__location__
": schema copy failed!\n"));
303 return WERR_NOT_ENOUGH_MEMORY
;
305 working_schema
->resolving_in_progress
= true;
307 /* we are going to need remote prefixMap for decoding */
308 werr
= dsdb_schema_pfm_from_drsuapi_pfm(mapping_ctr
, true,
309 working_schema
, &pfm_remote
, NULL
);
310 if (!W_ERROR_IS_OK(werr
)) {
311 DEBUG(0,(__location__
": Failed to decode remote prefixMap: %s\n",
313 talloc_free(working_schema
);
317 for (r
=0; r
< pfm_remote
->length
; r
++) {
318 const struct dsdb_schema_prefixmap_oid
*rm
= &pfm_remote
->prefixes
[r
];
319 bool found_oid
= false;
322 for (l
=0; l
< working_schema
->prefixmap
->length
; l
++) {
323 const struct dsdb_schema_prefixmap_oid
*lm
= &working_schema
->prefixmap
->prefixes
[l
];
326 cmp
= data_blob_cmp(&rm
->bin_oid
, &lm
->bin_oid
);
338 * We prefer the same is as we got from the remote peer
339 * if there's no conflict.
341 werr
= dsdb_schema_pfm_add_entry(working_schema
->prefixmap
,
342 rm
->bin_oid
, &rm
->id
, NULL
);
343 if (!W_ERROR_IS_OK(werr
)) {
344 DEBUG(0,(__location__
": Failed to merge remote prefixMap: %s",
346 talloc_free(working_schema
);
351 werr
= dsdb_repl_resolve_working_schema(ldb
,
353 0, /* cycle_before_switching */
358 if (!W_ERROR_IS_OK(werr
)) {
359 DEBUG(0, ("%s: dsdb_repl_resolve_working_schema() failed: %s",
360 __location__
, win_errstr(werr
)));
361 talloc_free(working_schema
);
365 working_schema
->resolving_in_progress
= false;
367 *_schema_out
= working_schema
;
372 static bool dsdb_attid_in_list(const uint32_t attid_list
[], uint32_t attid
)
378 for (cur
= attid_list
; *cur
!= DRSUAPI_ATTID_INVALID
; cur
++) {
386 WERROR
dsdb_convert_object_ex(struct ldb_context
*ldb
,
387 const struct dsdb_schema
*schema
,
388 struct ldb_dn
*partition_dn
,
389 const struct dsdb_schema_prefixmap
*pfm_remote
,
390 const struct drsuapi_DsReplicaObjectListItemEx
*in
,
391 const DATA_BLOB
*gensec_skey
,
392 const uint32_t *ignore_attids
,
393 uint32_t dsdb_repl_flags
,
395 struct dsdb_extended_replicated_object
*out
)
397 WERROR status
= WERR_OK
;
399 struct ldb_message
*msg
;
400 struct replPropertyMetaDataBlob
*md
;
402 struct ldb_message_element
*instanceType_e
= NULL
;
403 NTTIME whenChanged
= 0;
404 time_t whenChanged_t
;
405 const char *whenChanged_s
;
406 struct dom_sid
*sid
= NULL
;
410 if (!in
->object
.identifier
) {
414 if (!in
->object
.identifier
->dn
|| !in
->object
.identifier
->dn
[0]) {
418 if (in
->object
.attribute_ctr
.num_attributes
!= 0 && !in
->meta_data_ctr
) {
422 if (in
->object
.attribute_ctr
.num_attributes
!= in
->meta_data_ctr
->count
) {
426 sid
= &in
->object
.identifier
->sid
;
427 if (sid
->num_auths
> 0) {
428 rid
= sid
->sub_auths
[sid
->num_auths
- 1];
431 msg
= ldb_msg_new(mem_ctx
);
432 W_ERROR_HAVE_NO_MEMORY(msg
);
434 msg
->dn
= ldb_dn_new(msg
, ldb
, in
->object
.identifier
->dn
);
435 W_ERROR_HAVE_NO_MEMORY(msg
->dn
);
437 msg
->num_elements
= in
->object
.attribute_ctr
.num_attributes
;
438 msg
->elements
= talloc_array(msg
, struct ldb_message_element
,
440 W_ERROR_HAVE_NO_MEMORY(msg
->elements
);
442 md
= talloc(mem_ctx
, struct replPropertyMetaDataBlob
);
443 W_ERROR_HAVE_NO_MEMORY(md
);
447 md
->ctr
.ctr1
.count
= in
->meta_data_ctr
->count
;
448 md
->ctr
.ctr1
.reserved
= 0;
449 md
->ctr
.ctr1
.array
= talloc_array(mem_ctx
,
450 struct replPropertyMetaData1
,
452 W_ERROR_HAVE_NO_MEMORY(md
->ctr
.ctr1
.array
);
454 for (i
=0, attr_count
=0; i
< in
->meta_data_ctr
->count
; i
++, attr_count
++) {
455 struct drsuapi_DsReplicaAttribute
*a
;
456 struct drsuapi_DsReplicaMetaData
*d
;
457 struct replPropertyMetaData1
*m
;
458 struct ldb_message_element
*e
;
461 a
= &in
->object
.attribute_ctr
.attributes
[i
];
462 d
= &in
->meta_data_ctr
->meta_data
[i
];
463 m
= &md
->ctr
.ctr1
.array
[attr_count
];
464 e
= &msg
->elements
[attr_count
];
466 if (dsdb_attid_in_list(ignore_attids
, a
->attid
)) {
471 if (GUID_all_zero(&d
->originating_invocation_id
)) {
472 status
= WERR_DS_SRC_GUID_MISMATCH
;
473 DEBUG(0, ("Refusing replication of object containing invalid zero invocationID on attribute %d of %s: %s\n",
475 ldb_dn_get_linearized(msg
->dn
),
476 win_errstr(status
)));
480 if (a
->attid
== DRSUAPI_ATTID_instanceType
) {
481 if (instanceType_e
!= NULL
) {
487 for (j
=0; j
<a
->value_ctr
.num_values
; j
++) {
488 status
= drsuapi_decrypt_attribute(a
->value_ctr
.values
[j
].blob
,
491 if (!W_ERROR_IS_OK(status
)) {
495 if (W_ERROR_EQUAL(status
, WERR_TOO_MANY_SECRETS
)) {
496 WERROR get_name_status
= dsdb_attribute_drsuapi_to_ldb(ldb
, schema
, pfm_remote
,
497 a
, msg
->elements
, e
, NULL
);
498 if (W_ERROR_IS_OK(get_name_status
)) {
499 DEBUG(0, ("Unxpectedly got secret value %s on %s from DRS server\n",
500 e
->name
, ldb_dn_get_linearized(msg
->dn
)));
502 DEBUG(0, ("Unxpectedly got secret value on %s from DRS server",
503 ldb_dn_get_linearized(msg
->dn
)));
505 } else if (!W_ERROR_IS_OK(status
)) {
510 * This function also fills in the local attid value,
511 * based on comparing the remote and local prefixMap
512 * tables. If we don't convert the value, then we can
513 * have invalid values in the replPropertyMetaData we
514 * store on disk, as the prefixMap is per host, not
515 * per-domain. This may be why Microsoft added the
516 * msDS-IntID feature, however this is not used for
517 * extra attributes in the schema partition itself.
519 status
= dsdb_attribute_drsuapi_to_ldb(ldb
, schema
, pfm_remote
,
522 W_ERROR_NOT_OK_RETURN(status
);
524 m
->version
= d
->version
;
525 m
->originating_change_time
= d
->originating_change_time
;
526 m
->originating_invocation_id
= d
->originating_invocation_id
;
527 m
->originating_usn
= d
->originating_usn
;
530 if (a
->attid
== DRSUAPI_ATTID_name
) {
531 const struct ldb_val
*rdn_val
= ldb_dn_get_rdn_val(msg
->dn
);
532 if (rdn_val
== NULL
) {
533 DEBUG(0, ("Unxpectedly unable to get RDN from %s for validation",
534 ldb_dn_get_linearized(msg
->dn
)));
537 if (e
->num_values
!= 1) {
538 DEBUG(0, ("Unxpectedly got wrong number of attribute values (got %u, expected 1) when checking RDN against name of %s",
540 ldb_dn_get_linearized(msg
->dn
)));
543 if (data_blob_cmp(rdn_val
,
544 &e
->values
[0]) != 0) {
545 DEBUG(0, ("Unxpectedly got mismatching RDN values when checking RDN against name of %s",
546 ldb_dn_get_linearized(msg
->dn
)));
550 if (d
->originating_change_time
> whenChanged
) {
551 whenChanged
= d
->originating_change_time
;
556 msg
->num_elements
= attr_count
;
557 md
->ctr
.ctr1
.count
= attr_count
;
559 if (instanceType_e
== NULL
) {
563 instanceType
= ldb_msg_find_attr_as_int(msg
, "instanceType", 0);
565 if ((instanceType
& INSTANCE_TYPE_IS_NC_HEAD
)
566 && partition_dn
!= NULL
) {
567 int partition_dn_cmp
= ldb_dn_compare(partition_dn
, msg
->dn
);
568 if (partition_dn_cmp
!= 0) {
569 DEBUG(4, ("Remote server advised us of a new partition %s while processing %s, ignoring\n",
570 ldb_dn_get_linearized(msg
->dn
),
571 ldb_dn_get_linearized(partition_dn
)));
572 return WERR_DS_ADD_REPLICA_INHIBITED
;
576 if (dsdb_repl_flags
& DSDB_REPL_FLAG_PARTIAL_REPLICA
) {
577 /* the instanceType type for partial_replica
578 replication is sent via DRS with TYPE_WRITE set, but
579 must be used on the client with TYPE_WRITE removed
581 if (instanceType
& INSTANCE_TYPE_WRITE
) {
583 * Make sure we do not change the order
587 * instanceType_e->num_values = 0
589 * ldb_msg_remove_attr(msg, "instanceType");
591 struct ldb_message_element
*e
;
593 e
= ldb_msg_find_element(msg
, "instanceType");
594 if (e
!= instanceType_e
) {
595 DEBUG(0,("instanceType_e[%p] changed to e[%p]\n",
600 instanceType_e
->num_values
= 0;
602 instanceType
&= ~INSTANCE_TYPE_WRITE
;
603 if (ldb_msg_add_fmt(msg
, "instanceType", "%d", instanceType
) != LDB_SUCCESS
) {
604 return WERR_INTERNAL_ERROR
;
608 if (!(instanceType
& INSTANCE_TYPE_WRITE
)) {
609 DBG_ERR("Refusing to replicate %s from a read-only "
610 "replica into a read-write replica!\n",
611 ldb_dn_get_linearized(msg
->dn
));
612 return WERR_DS_DRA_SOURCE_IS_PARTIAL_REPLICA
;
616 whenChanged_t
= nt_time_to_unix(whenChanged
);
617 whenChanged_s
= ldb_timestring(msg
, whenChanged_t
);
618 W_ERROR_HAVE_NO_MEMORY(whenChanged_s
);
620 out
->object_guid
= in
->object
.identifier
->guid
;
622 if (in
->parent_object_guid
== NULL
) {
623 out
->parent_guid
= NULL
;
625 out
->parent_guid
= talloc(mem_ctx
, struct GUID
);
626 W_ERROR_HAVE_NO_MEMORY(out
->parent_guid
);
627 *out
->parent_guid
= *in
->parent_object_guid
;
631 out
->when_changed
= whenChanged_s
;
636 WERROR
dsdb_replicated_objects_convert(struct ldb_context
*ldb
,
637 const struct dsdb_schema
*schema
,
638 struct ldb_dn
*partition_dn
,
639 const struct drsuapi_DsReplicaOIDMapping_Ctr
*mapping_ctr
,
640 uint32_t object_count
,
641 const struct drsuapi_DsReplicaObjectListItemEx
*first_object
,
642 uint32_t linked_attributes_count
,
643 const struct drsuapi_DsReplicaLinkedAttribute
*linked_attributes
,
644 const struct repsFromTo1
*source_dsa
,
645 const struct drsuapi_DsReplicaCursor2CtrEx
*uptodateness_vector
,
646 const DATA_BLOB
*gensec_skey
,
647 uint32_t dsdb_repl_flags
,
649 struct dsdb_extended_replicated_objects
**objects
)
652 struct dsdb_schema_prefixmap
*pfm_remote
;
653 struct dsdb_extended_replicated_objects
*out
;
654 const struct drsuapi_DsReplicaObjectListItemEx
*cur
;
655 struct dsdb_syntax_ctx syntax_ctx
;
658 out
= talloc_zero(mem_ctx
, struct dsdb_extended_replicated_objects
);
659 W_ERROR_HAVE_NO_MEMORY(out
);
660 out
->version
= DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION
;
661 out
->dsdb_repl_flags
= dsdb_repl_flags
;
664 * Ensure schema is kept valid for as long as 'out'
665 * which may contain pointers to it
667 schema
= talloc_reference(out
, schema
);
668 W_ERROR_HAVE_NO_MEMORY(schema
);
670 status
= dsdb_schema_pfm_from_drsuapi_pfm(mapping_ctr
, true,
671 out
, &pfm_remote
, NULL
);
672 if (!W_ERROR_IS_OK(status
)) {
673 DEBUG(0,(__location__
": Failed to decode remote prefixMap: %s\n",
674 win_errstr(status
)));
679 /* use default syntax conversion context */
680 dsdb_syntax_ctx_init(&syntax_ctx
, ldb
, schema
);
681 syntax_ctx
.pfm_remote
= pfm_remote
;
683 if (ldb_dn_compare(partition_dn
, ldb_get_schema_basedn(ldb
)) != 0) {
685 * check for schema changes in case
686 * we are not replicating Schema NC
688 status
= dsdb_schema_info_cmp(schema
, mapping_ctr
);
689 if (!W_ERROR_IS_OK(status
)) {
690 DEBUG(4,("Can't replicate %s because remote schema has changed since we last replicated the schema\n",
691 ldb_dn_get_linearized(partition_dn
)));
697 out
->partition_dn
= partition_dn
;
699 out
->source_dsa
= source_dsa
;
700 out
->uptodateness_vector
= uptodateness_vector
;
702 out
->num_objects
= 0;
703 out
->objects
= talloc_array(out
,
704 struct dsdb_extended_replicated_object
,
706 W_ERROR_HAVE_NO_MEMORY_AND_FREE(out
->objects
, out
);
708 for (i
=0, cur
= first_object
; cur
; cur
= cur
->next_object
, i
++) {
709 if (i
== object_count
) {
714 status
= dsdb_convert_object_ex(ldb
, schema
, out
->partition_dn
,
720 &out
->objects
[out
->num_objects
]);
723 * Check to see if we have been advised of a
724 * subdomain or new application partition. We don't
725 * want to start on that here, instead the caller
726 * should consider if it would like to replicate it
727 * based on the cross-ref object.
729 if (W_ERROR_EQUAL(status
, WERR_DS_ADD_REPLICA_INHIBITED
)) {
730 struct GUID_txt_buf guid_str
;
731 DBG_ERR("Ignoring object outside partition %s %s: %s\n",
732 GUID_buf_string(&cur
->object
.identifier
->guid
,
734 cur
->object
.identifier
->dn
,
739 if (!W_ERROR_IS_OK(status
)) {
741 DEBUG(0,("Failed to convert object %s: %s\n",
742 cur
->object
.identifier
->dn
,
743 win_errstr(status
)));
747 /* Assuming we didn't skip or error, increment the number of objects */
750 out
->objects
= talloc_realloc(out
, out
->objects
,
751 struct dsdb_extended_replicated_object
,
753 if (out
->num_objects
!= 0 && out
->objects
== NULL
) {
757 if (i
!= object_count
) {
762 out
->linked_attributes
= talloc_array(out
,
763 struct drsuapi_DsReplicaLinkedAttribute
,
764 linked_attributes_count
);
765 W_ERROR_HAVE_NO_MEMORY_AND_FREE(out
->linked_attributes
, out
);
767 for (i
=0; i
< linked_attributes_count
; i
++) {
768 const struct drsuapi_DsReplicaLinkedAttribute
*ra
= &linked_attributes
[i
];
769 struct drsuapi_DsReplicaLinkedAttribute
*la
= &out
->linked_attributes
[i
];
771 if (ra
->identifier
== NULL
) {
773 return WERR_BAD_NET_RESP
;
778 la
->identifier
= talloc_zero(out
->linked_attributes
,
779 struct drsuapi_DsReplicaObjectIdentifier
);
780 W_ERROR_HAVE_NO_MEMORY_AND_FREE(la
->identifier
, out
);
783 * We typically only get the guid filled
784 * and the repl_meta_data module only cares abouf
787 la
->identifier
->guid
= ra
->identifier
->guid
;
789 if (ra
->value
.blob
!= NULL
) {
790 la
->value
.blob
= talloc_zero(out
->linked_attributes
,
792 W_ERROR_HAVE_NO_MEMORY_AND_FREE(la
->value
.blob
, out
);
794 if (ra
->value
.blob
->length
!= 0) {
795 *la
->value
.blob
= data_blob_dup_talloc(la
->value
.blob
,
797 W_ERROR_HAVE_NO_MEMORY_AND_FREE(la
->value
.blob
->data
, out
);
801 status
= dsdb_attribute_drsuapi_remote_to_local(&syntax_ctx
,
805 if (!W_ERROR_IS_OK(status
)) {
806 DEBUG(0,(__location__
": linked_attribute[%u] attid 0x%08X not found: %s\n",
807 i
, ra
->attid
, win_errstr(status
)));
812 out
->linked_attributes_count
= linked_attributes_count
;
814 /* free pfm_remote, we won't need it anymore */
815 talloc_free(pfm_remote
);
822 * Commits a list of replicated objects.
824 * @param working_schema dsdb_schema to be used for resolving
825 * Classes/Attributes during Schema replication. If not NULL,
826 * it will be set on ldb and used while committing replicated objects
828 WERROR
dsdb_replicated_objects_commit(struct ldb_context
*ldb
,
829 struct dsdb_schema
*working_schema
,
830 struct dsdb_extended_replicated_objects
*objects
,
831 uint64_t *notify_uSN
)
834 struct ldb_result
*ext_res
;
835 struct dsdb_schema
*cur_schema
= NULL
;
836 struct dsdb_schema
*new_schema
= NULL
;
838 uint64_t seq_num1
, seq_num2
;
839 bool used_global_schema
= false;
841 TALLOC_CTX
*tmp_ctx
= talloc_new(objects
);
843 DEBUG(0,("Failed to start talloc\n"));
844 return WERR_NOT_ENOUGH_MEMORY
;
847 /* wrap the extended operation in a transaction
848 See [MS-DRSR] 3.3.2 Transactions
850 ret
= ldb_transaction_start(ldb
);
851 if (ret
!= LDB_SUCCESS
) {
852 DEBUG(0,(__location__
" Failed to start transaction: %s\n",
853 ldb_errstring(ldb
)));
857 ret
= dsdb_load_partition_usn(ldb
, objects
->partition_dn
, &seq_num1
, NULL
);
858 if (ret
!= LDB_SUCCESS
) {
859 DEBUG(0,(__location__
" Failed to load partition uSN\n"));
860 ldb_transaction_cancel(ldb
);
861 TALLOC_FREE(tmp_ctx
);
866 * Set working_schema for ldb in case we are replicating from Schema NC.
867 * Schema won't be reloaded during Replicated Objects commit, as it is
868 * done in a transaction. So we need some way to search for newly
869 * added Classes and Attributes
871 if (working_schema
) {
872 /* store current schema so we can fall back in case of failure */
873 cur_schema
= dsdb_get_schema(ldb
, tmp_ctx
);
874 used_global_schema
= dsdb_uses_global_schema(ldb
);
876 ret
= dsdb_reference_schema(ldb
, working_schema
, SCHEMA_MEMORY_ONLY
);
877 if (ret
!= LDB_SUCCESS
) {
878 DEBUG(0,(__location__
"Failed to reference working schema - %s\n",
880 /* TODO: Map LDB Error to NTSTATUS? */
881 ldb_transaction_cancel(ldb
);
882 TALLOC_FREE(tmp_ctx
);
883 return WERR_INTERNAL_ERROR
;
887 ret
= ldb_extended(ldb
, DSDB_EXTENDED_REPLICATED_OBJECTS_OID
, objects
, &ext_res
);
888 if (ret
!= LDB_SUCCESS
) {
889 /* restore previous schema */
890 if (used_global_schema
) {
891 dsdb_set_global_schema(ldb
);
892 } else if (cur_schema
) {
893 dsdb_reference_schema(ldb
, cur_schema
, SCHEMA_MEMORY_ONLY
);
896 if (W_ERROR_EQUAL(objects
->error
, WERR_DS_DRA_RECYCLED_TARGET
)) {
897 DEBUG(3,("Missing target while attempting to apply records: %s\n",
898 ldb_errstring(ldb
)));
899 } else if (W_ERROR_EQUAL(objects
->error
, WERR_DS_DRA_MISSING_PARENT
)) {
900 DEBUG(3,("Missing parent while attempting to apply records: %s\n",
901 ldb_errstring(ldb
)));
903 DEBUG(1,("Failed to apply records: %s: %s\n",
904 ldb_errstring(ldb
), ldb_strerror(ret
)));
906 ldb_transaction_cancel(ldb
);
907 TALLOC_FREE(tmp_ctx
);
909 if (!W_ERROR_IS_OK(objects
->error
)) {
910 return objects
->error
;
914 talloc_free(ext_res
);
916 /* Save our updated prefixMap and check the schema is good. */
917 if (working_schema
) {
918 struct ldb_result
*ext_res_2
;
920 werr
= dsdb_write_prefixes_from_schema_to_ldb(working_schema
,
923 if (!W_ERROR_IS_OK(werr
)) {
924 /* restore previous schema */
925 if (used_global_schema
) {
926 dsdb_set_global_schema(ldb
);
927 } else if (cur_schema
) {
928 dsdb_reference_schema(ldb
,
932 DEBUG(0,("Failed to save updated prefixMap: %s\n",
934 ldb_transaction_cancel(ldb
);
935 TALLOC_FREE(tmp_ctx
);
940 * Use dsdb_schema_from_db through dsdb extended to check we
941 * can load the schema currently sitting in the transaction.
942 * We need this check because someone might have written to
943 * the schema or prefixMap before we started the transaction,
944 * which may have caused corruption.
946 ret
= ldb_extended(ldb
, DSDB_EXTENDED_SCHEMA_LOAD
,
949 if (ret
!= LDB_SUCCESS
) {
950 if (used_global_schema
) {
951 dsdb_set_global_schema(ldb
);
952 } else if (cur_schema
) {
953 dsdb_reference_schema(ldb
, cur_schema
, SCHEMA_MEMORY_ONLY
);
955 DEBUG(0,("Corrupt schema write attempt detected, "
956 "aborting schema modification operation.\n"
957 "This probably happened due to bad timing of "
958 "another schema edit: %s (%s)\n",
961 ldb_transaction_cancel(ldb
);
962 TALLOC_FREE(tmp_ctx
);
967 ret
= ldb_transaction_prepare_commit(ldb
);
968 if (ret
!= LDB_SUCCESS
) {
969 /* restore previous schema */
970 if (used_global_schema
) {
971 dsdb_set_global_schema(ldb
);
972 } else if (cur_schema
) {
973 dsdb_reference_schema(ldb
, cur_schema
, SCHEMA_MEMORY_ONLY
);
975 DBG_ERR(" Failed to prepare commit of transaction: %s (%s)\n",
978 TALLOC_FREE(tmp_ctx
);
982 ret
= dsdb_load_partition_usn(ldb
, objects
->partition_dn
, &seq_num2
, NULL
);
983 if (ret
!= LDB_SUCCESS
) {
984 /* restore previous schema */
985 if (used_global_schema
) {
986 dsdb_set_global_schema(ldb
);
987 } else if (cur_schema
) {
988 dsdb_reference_schema(ldb
, cur_schema
, SCHEMA_MEMORY_ONLY
);
990 DEBUG(0,(__location__
" Failed to load partition uSN\n"));
991 ldb_transaction_cancel(ldb
);
992 TALLOC_FREE(tmp_ctx
);
996 ret
= ldb_transaction_commit(ldb
);
997 if (ret
!= LDB_SUCCESS
) {
998 /* restore previous schema */
999 if (used_global_schema
) {
1000 dsdb_set_global_schema(ldb
);
1001 } else if (cur_schema
) {
1002 dsdb_reference_schema(ldb
, cur_schema
, SCHEMA_MEMORY_ONLY
);
1004 DEBUG(0,(__location__
" Failed to commit transaction\n"));
1005 TALLOC_FREE(tmp_ctx
);
1009 if (seq_num1
> *notify_uSN
) {
1011 * A notify was already required before
1012 * the current transaction.
1014 } else if (objects
->originating_updates
) {
1016 * Applying the replicated changes
1017 * required originating updates,
1018 * so a notify is required.
1022 * There's no need to notify the
1023 * server about the change we just from it.
1025 *notify_uSN
= seq_num2
;
1029 * Reset the Schema used by ldb. This will lead to
1030 * a schema cache being refreshed from database.
1032 if (working_schema
) {
1033 /* Reload the schema */
1034 new_schema
= dsdb_get_schema(ldb
, tmp_ctx
);
1036 * If dsdb_get_schema() fails, we just fall back
1037 * to what we had. However, the database is probably
1038 * unable to operate for other users from this
1040 if (new_schema
== NULL
|| new_schema
== working_schema
) {
1041 DBG_ERR("Failed to re-load schema after commit of "
1042 "transaction (working: %p/%"PRIu64
", new: "
1043 "%p/%"PRIu64
")\n", new_schema
,
1044 new_schema
!= NULL
?
1045 new_schema
->metadata_usn
: 0,
1046 working_schema
, working_schema
->metadata_usn
);
1047 dsdb_reference_schema(ldb
, cur_schema
, SCHEMA_MEMORY_ONLY
);
1048 if (used_global_schema
) {
1049 dsdb_set_global_schema(ldb
);
1051 TALLOC_FREE(tmp_ctx
);
1052 return WERR_INTERNAL_ERROR
;
1053 } else if (used_global_schema
) {
1054 dsdb_make_schema_global(ldb
, new_schema
);
1058 DEBUG(2,("Replicated %u objects (%u linked attributes) for %s\n",
1059 objects
->num_objects
, objects
->linked_attributes_count
,
1060 ldb_dn_get_linearized(objects
->partition_dn
)));
1062 TALLOC_FREE(tmp_ctx
);
1066 static WERROR
dsdb_origin_object_convert(struct ldb_context
*ldb
,
1067 const struct dsdb_schema
*schema
,
1068 const struct drsuapi_DsReplicaObjectListItem
*in
,
1069 TALLOC_CTX
*mem_ctx
,
1070 struct ldb_message
**_msg
)
1074 struct ldb_message
*msg
;
1076 if (!in
->object
.identifier
) {
1080 if (!in
->object
.identifier
->dn
|| !in
->object
.identifier
->dn
[0]) {
1084 msg
= ldb_msg_new(mem_ctx
);
1085 W_ERROR_HAVE_NO_MEMORY(msg
);
1087 msg
->dn
= ldb_dn_new(msg
, ldb
, in
->object
.identifier
->dn
);
1088 W_ERROR_HAVE_NO_MEMORY(msg
->dn
);
1090 msg
->num_elements
= in
->object
.attribute_ctr
.num_attributes
;
1091 msg
->elements
= talloc_array(msg
, struct ldb_message_element
,
1093 W_ERROR_HAVE_NO_MEMORY(msg
->elements
);
1095 for (i
=0; i
< msg
->num_elements
; i
++) {
1096 struct drsuapi_DsReplicaAttribute
*a
;
1097 struct ldb_message_element
*e
;
1099 a
= &in
->object
.attribute_ctr
.attributes
[i
];
1100 e
= &msg
->elements
[i
];
1102 status
= dsdb_attribute_drsuapi_to_ldb(ldb
, schema
, schema
->prefixmap
,
1103 a
, msg
->elements
, e
, NULL
);
1104 W_ERROR_NOT_OK_RETURN(status
);
1113 WERROR
dsdb_origin_objects_commit(struct ldb_context
*ldb
,
1114 TALLOC_CTX
*mem_ctx
,
1115 const struct drsuapi_DsReplicaObjectListItem
*first_object
,
1117 uint32_t dsdb_repl_flags
,
1118 struct drsuapi_DsReplicaObjectIdentifier2
**_ids
)
1121 const struct dsdb_schema
*schema
;
1122 const struct drsuapi_DsReplicaObjectListItem
*cur
;
1123 struct ldb_message
**objects
;
1124 struct drsuapi_DsReplicaObjectIdentifier2
*ids
;
1126 uint32_t num_objects
= 0;
1127 const char * const attrs
[] = {
1132 struct ldb_result
*res
;
1135 for (cur
= first_object
; cur
; cur
= cur
->next_object
) {
1139 if (num_objects
== 0) {
1143 ret
= ldb_transaction_start(ldb
);
1144 if (ret
!= LDB_SUCCESS
) {
1145 return WERR_DS_INTERNAL_FAILURE
;
1148 objects
= talloc_array(mem_ctx
, struct ldb_message
*,
1150 if (objects
== NULL
) {
1151 status
= WERR_NOT_ENOUGH_MEMORY
;
1155 schema
= dsdb_get_schema(ldb
, objects
);
1157 return WERR_DS_SCHEMA_NOT_LOADED
;
1160 for (i
=0, cur
= first_object
; cur
; cur
= cur
->next_object
, i
++) {
1161 status
= dsdb_origin_object_convert(ldb
, schema
, cur
,
1162 objects
, &objects
[i
]);
1163 if (!W_ERROR_IS_OK(status
)) {
1168 ids
= talloc_array(mem_ctx
,
1169 struct drsuapi_DsReplicaObjectIdentifier2
,
1172 status
= WERR_NOT_ENOUGH_MEMORY
;
1176 if (dsdb_repl_flags
& DSDB_REPL_FLAG_ADD_NCNAME
) {
1177 /* check for possible NC creation */
1178 for (i
=0; i
< num_objects
; i
++) {
1179 struct ldb_message
*msg
= objects
[i
];
1180 struct ldb_message_element
*el
;
1181 struct ldb_dn
*nc_dn
;
1183 if (ldb_msg_check_string_attribute(msg
, "objectClass", "crossRef") == 0) {
1186 el
= ldb_msg_find_element(msg
, "nCName");
1187 if (el
== NULL
|| el
->num_values
!= 1) {
1190 nc_dn
= ldb_dn_from_ldb_val(objects
, ldb
, &el
->values
[0]);
1191 if (!ldb_dn_validate(nc_dn
)) {
1194 ret
= dsdb_create_partial_replica_NC(ldb
, nc_dn
);
1195 if (ret
!= LDB_SUCCESS
) {
1196 status
= WERR_DS_INTERNAL_FAILURE
;
1202 for (i
=0; i
< num_objects
; i
++) {
1203 struct dom_sid
*sid
= NULL
;
1204 struct ldb_request
*add_req
;
1206 DEBUG(6,(__location__
": adding %s\n",
1207 ldb_dn_get_linearized(objects
[i
]->dn
)));
1209 ret
= ldb_build_add_req(&add_req
,
1215 ldb_op_default_callback
,
1217 if (ret
!= LDB_SUCCESS
) {
1218 status
= WERR_DS_INTERNAL_FAILURE
;
1222 ret
= ldb_request_add_control(add_req
, LDB_CONTROL_RELAX_OID
, true, NULL
);
1223 if (ret
!= LDB_SUCCESS
) {
1224 status
= WERR_DS_INTERNAL_FAILURE
;
1228 ret
= ldb_request(ldb
, add_req
);
1229 if (ret
== LDB_SUCCESS
) {
1230 ret
= ldb_wait(add_req
->handle
, LDB_WAIT_ALL
);
1232 if (ret
!= LDB_SUCCESS
) {
1233 DEBUG(0,(__location__
": Failed add of %s - %s\n",
1234 ldb_dn_get_linearized(objects
[i
]->dn
), ldb_errstring(ldb
)));
1235 status
= WERR_DS_INTERNAL_FAILURE
;
1239 talloc_free(add_req
);
1241 ret
= ldb_search(ldb
, objects
, &res
, objects
[i
]->dn
,
1242 LDB_SCOPE_BASE
, attrs
,
1244 if (ret
!= LDB_SUCCESS
) {
1245 status
= WERR_DS_INTERNAL_FAILURE
;
1248 ids
[i
].guid
= samdb_result_guid(res
->msgs
[0], "objectGUID");
1249 sid
= samdb_result_dom_sid(objects
, res
->msgs
[0], "objectSid");
1253 ZERO_STRUCT(ids
[i
].sid
);
1257 ret
= ldb_transaction_commit(ldb
);
1258 if (ret
!= LDB_SUCCESS
) {
1259 return WERR_DS_INTERNAL_FAILURE
;
1262 talloc_free(objects
);
1264 *_num
= num_objects
;
1269 talloc_free(objects
);
1270 ldb_transaction_cancel(ldb
);