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 "lib/ldb/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 "../lib/crypto/crypto.h"
30 #include "../libcli/drsuapi/drsuapi.h"
31 #include "libcli/auth/libcli_auth.h"
32 #include "param/param.h"
34 WERROR
dsdb_convert_object_ex(struct ldb_context
*ldb
,
35 const struct dsdb_schema
*schema
,
36 const struct dsdb_schema_prefixmap
*pfm_remote
,
37 const struct drsuapi_DsReplicaObjectListItemEx
*in
,
38 const DATA_BLOB
*gensec_skey
,
40 struct dsdb_extended_replicated_object
*out
)
45 struct ldb_message
*msg
;
46 struct replPropertyMetaDataBlob
*md
;
47 struct ldb_val guid_value
;
48 NTTIME whenChanged
= 0;
50 const char *whenChanged_s
;
51 const char *rdn_name
= NULL
;
52 const struct ldb_val
*rdn_value
= NULL
;
53 const struct dsdb_attribute
*rdn_attr
= NULL
;
55 struct drsuapi_DsReplicaAttribute
*name_a
= NULL
;
56 struct drsuapi_DsReplicaMetaData
*name_d
= NULL
;
57 struct replPropertyMetaData1
*rdn_m
= NULL
;
58 struct dom_sid
*sid
= NULL
;
62 if (!in
->object
.identifier
) {
66 if (!in
->object
.identifier
->dn
|| !in
->object
.identifier
->dn
[0]) {
70 if (in
->object
.attribute_ctr
.num_attributes
!= 0 && !in
->meta_data_ctr
) {
74 if (in
->object
.attribute_ctr
.num_attributes
!= in
->meta_data_ctr
->count
) {
78 sid
= &in
->object
.identifier
->sid
;
79 if (sid
->num_auths
> 0) {
80 rid
= sid
->sub_auths
[sid
->num_auths
- 1];
83 msg
= ldb_msg_new(mem_ctx
);
84 W_ERROR_HAVE_NO_MEMORY(msg
);
86 msg
->dn
= ldb_dn_new(msg
, ldb
, in
->object
.identifier
->dn
);
87 W_ERROR_HAVE_NO_MEMORY(msg
->dn
);
89 rdn_name
= ldb_dn_get_rdn_name(msg
->dn
);
90 rdn_attr
= dsdb_attribute_by_lDAPDisplayName(schema
, rdn_name
);
94 rdn_attid
= rdn_attr
->attributeID_id
;
95 rdn_value
= ldb_dn_get_rdn_val(msg
->dn
);
97 msg
->num_elements
= in
->object
.attribute_ctr
.num_attributes
;
98 msg
->elements
= talloc_array(msg
, struct ldb_message_element
,
100 W_ERROR_HAVE_NO_MEMORY(msg
->elements
);
102 md
= talloc(mem_ctx
, struct replPropertyMetaDataBlob
);
103 W_ERROR_HAVE_NO_MEMORY(md
);
107 md
->ctr
.ctr1
.count
= in
->meta_data_ctr
->count
;
108 md
->ctr
.ctr1
.reserved
= 0;
109 md
->ctr
.ctr1
.array
= talloc_array(mem_ctx
,
110 struct replPropertyMetaData1
,
111 md
->ctr
.ctr1
.count
+ 1); /* +1 because of the RDN attribute */
112 W_ERROR_HAVE_NO_MEMORY(md
->ctr
.ctr1
.array
);
114 for (i
=0; i
< in
->meta_data_ctr
->count
; i
++) {
115 struct drsuapi_DsReplicaAttribute
*a
;
116 struct drsuapi_DsReplicaMetaData
*d
;
117 struct replPropertyMetaData1
*m
;
118 struct ldb_message_element
*e
;
121 a
= &in
->object
.attribute_ctr
.attributes
[i
];
122 d
= &in
->meta_data_ctr
->meta_data
[i
];
123 m
= &md
->ctr
.ctr1
.array
[i
];
124 e
= &msg
->elements
[i
];
126 for (j
=0; j
<a
->value_ctr
.num_values
; j
++) {
127 status
= drsuapi_decrypt_attribute(a
->value_ctr
.values
[j
].blob
, gensec_skey
, rid
, a
);
128 W_ERROR_NOT_OK_RETURN(status
);
131 status
= dsdb_attribute_drsuapi_to_ldb(ldb
, schema
, pfm_remote
,
132 a
, msg
->elements
, e
);
133 W_ERROR_NOT_OK_RETURN(status
);
136 m
->version
= d
->version
;
137 m
->originating_change_time
= d
->originating_change_time
;
138 m
->originating_invocation_id
= d
->originating_invocation_id
;
139 m
->originating_usn
= d
->originating_usn
;
142 if (d
->originating_change_time
> whenChanged
) {
143 whenChanged
= d
->originating_change_time
;
146 if (a
->attid
== DRSUAPI_ATTID_name
) {
149 rdn_m
= &md
->ctr
.ctr1
.array
[md
->ctr
.ctr1
.count
];
154 struct ldb_message_element
*el
;
155 el
= ldb_msg_find_element(msg
, rdn_attr
->lDAPDisplayName
);
157 ret
= ldb_msg_add_value(msg
, rdn_attr
->lDAPDisplayName
, rdn_value
, NULL
);
158 if (ret
!= LDB_SUCCESS
) {
162 if (el
->num_values
!= 1) {
163 DEBUG(0,(__location__
": Unexpected num_values=%u\n",
167 if (!ldb_val_equal_exact(&el
->values
[0], rdn_value
)) {
168 DEBUG(0,(__location__
": RDN value changed? '%*.*s' '%*.*s'\n",
169 (int)el
->values
[0].length
, (int)el
->values
[0].length
, el
->values
[0].data
,
170 (int)rdn_value
->length
, (int)rdn_value
->length
, rdn_value
->data
));
175 rdn_m
->attid
= rdn_attid
;
176 rdn_m
->version
= name_d
->version
;
177 rdn_m
->originating_change_time
= name_d
->originating_change_time
;
178 rdn_m
->originating_invocation_id
= name_d
->originating_invocation_id
;
179 rdn_m
->originating_usn
= name_d
->originating_usn
;
180 rdn_m
->local_usn
= 0;
181 md
->ctr
.ctr1
.count
++;
185 whenChanged_t
= nt_time_to_unix(whenChanged
);
186 whenChanged_s
= ldb_timestring(msg
, whenChanged_t
);
187 W_ERROR_HAVE_NO_MEMORY(whenChanged_s
);
189 nt_status
= GUID_to_ndr_blob(&in
->object
.identifier
->guid
, msg
, &guid_value
);
190 if (!NT_STATUS_IS_OK(nt_status
)) {
191 return ntstatus_to_werror(nt_status
);
195 out
->guid_value
= guid_value
;
196 out
->when_changed
= whenChanged_s
;
201 WERROR
dsdb_replicated_objects_convert(struct ldb_context
*ldb
,
202 const char *partition_dn_str
,
203 const struct drsuapi_DsReplicaOIDMapping_Ctr
*mapping_ctr
,
204 uint32_t object_count
,
205 const struct drsuapi_DsReplicaObjectListItemEx
*first_object
,
206 uint32_t linked_attributes_count
,
207 const struct drsuapi_DsReplicaLinkedAttribute
*linked_attributes
,
208 const struct repsFromTo1
*source_dsa
,
209 const struct drsuapi_DsReplicaCursor2CtrEx
*uptodateness_vector
,
210 const DATA_BLOB
*gensec_skey
,
212 struct dsdb_extended_replicated_objects
**objects
)
215 struct ldb_dn
*partition_dn
;
216 const struct dsdb_schema
*schema
;
217 struct dsdb_schema_prefixmap
*pfm_remote
;
218 struct dsdb_extended_replicated_objects
*out
;
219 const struct drsuapi_DsReplicaObjectListItemEx
*cur
;
222 out
= talloc_zero(mem_ctx
, struct dsdb_extended_replicated_objects
);
223 W_ERROR_HAVE_NO_MEMORY(out
);
224 out
->version
= DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION
;
226 /* Get the schema, and ensure it's kept valid for as long as 'out' which may contain pointers to it */
227 schema
= dsdb_get_schema(ldb
, out
);
230 return WERR_DS_SCHEMA_NOT_LOADED
;
233 partition_dn
= ldb_dn_new(out
, ldb
, partition_dn_str
);
234 W_ERROR_HAVE_NO_MEMORY_AND_FREE(partition_dn
, out
);
236 status
= dsdb_schema_pfm_from_drsuapi_pfm(mapping_ctr
, true,
237 out
, &pfm_remote
, NULL
);
238 if (!W_ERROR_IS_OK(status
)) {
239 DEBUG(0,(__location__
": Failed to decode remote prefixMap: %s",
240 win_errstr(status
)));
245 if (ldb_dn_compare(partition_dn
, ldb_get_schema_basedn(ldb
)) != 0) {
247 * check for schema changes in case
248 * we are not replicating Schema NC
250 status
= dsdb_schema_info_cmp(schema
, mapping_ctr
);
251 if (!W_ERROR_IS_OK(status
)) {
252 DEBUG(1,("Remote schema has changed while replicating %s\n",
259 out
->partition_dn
= partition_dn
;
261 out
->source_dsa
= source_dsa
;
262 out
->uptodateness_vector
= uptodateness_vector
;
264 out
->num_objects
= object_count
;
265 out
->objects
= talloc_array(out
,
266 struct dsdb_extended_replicated_object
,
268 W_ERROR_HAVE_NO_MEMORY_AND_FREE(out
->objects
, out
);
270 /* pass the linked attributes down to the repl_meta_data
272 out
->linked_attributes_count
= linked_attributes_count
;
273 out
->linked_attributes
= linked_attributes
;
275 for (i
=0, cur
= first_object
; cur
; cur
= cur
->next_object
, i
++) {
276 if (i
== out
->num_objects
) {
281 status
= dsdb_convert_object_ex(ldb
, schema
, pfm_remote
,
283 out
->objects
, &out
->objects
[i
]);
284 if (!W_ERROR_IS_OK(status
)) {
286 DEBUG(0,("Failed to convert object %s: %s\n",
287 cur
->object
.identifier
->dn
,
288 win_errstr(status
)));
292 if (i
!= out
->num_objects
) {
297 /* free pfm_remote, we won't need it anymore */
298 talloc_free(pfm_remote
);
304 WERROR
dsdb_replicated_objects_commit(struct ldb_context
*ldb
,
305 struct dsdb_extended_replicated_objects
*objects
,
306 uint64_t *notify_uSN
)
308 struct ldb_result
*ext_res
;
310 uint64_t seq_num1
, seq_num2
;
312 /* TODO: handle linked attributes */
314 /* wrap the extended operation in a transaction
315 See [MS-DRSR] 3.3.2 Transactions
317 ret
= ldb_transaction_start(ldb
);
318 if (ret
!= LDB_SUCCESS
) {
319 DEBUG(0,(__location__
" Failed to start transaction\n"));
323 ret
= dsdb_load_partition_usn(ldb
, objects
->partition_dn
, &seq_num1
, NULL
);
324 if (ret
!= LDB_SUCCESS
) {
325 DEBUG(0,(__location__
" Failed to load partition uSN\n"));
326 ldb_transaction_cancel(ldb
);
330 ret
= ldb_extended(ldb
, DSDB_EXTENDED_REPLICATED_OBJECTS_OID
, objects
, &ext_res
);
331 if (ret
!= LDB_SUCCESS
) {
332 DEBUG(0,("Failed to apply records: %s: %s\n",
333 ldb_errstring(ldb
), ldb_strerror(ret
)));
334 ldb_transaction_cancel(ldb
);
337 talloc_free(ext_res
);
339 ret
= ldb_transaction_prepare_commit(ldb
);
340 if (ret
!= LDB_SUCCESS
) {
341 DEBUG(0,(__location__
" Failed to prepare commit of transaction: %s\n",
342 ldb_errstring(ldb
)));
346 ret
= dsdb_load_partition_usn(ldb
, objects
->partition_dn
, &seq_num2
, NULL
);
347 if (ret
!= LDB_SUCCESS
) {
348 DEBUG(0,(__location__
" Failed to load partition uSN\n"));
349 ldb_transaction_cancel(ldb
);
353 /* if this replication partner didn't need to be notified
354 before this transaction then it still doesn't need to be
355 notified, as the changes came from this server */
356 if (seq_num2
> seq_num1
&& seq_num1
<= *notify_uSN
) {
357 *notify_uSN
= seq_num2
;
360 ret
= ldb_transaction_commit(ldb
);
361 if (ret
!= LDB_SUCCESS
) {
362 DEBUG(0,(__location__
" Failed to commit transaction\n"));
367 DEBUG(2,("Replicated %u objects (%u linked attributes) for %s\n",
368 objects
->num_objects
, objects
->linked_attributes_count
,
369 ldb_dn_get_linearized(objects
->partition_dn
)));
374 static WERROR
dsdb_origin_object_convert(struct ldb_context
*ldb
,
375 const struct dsdb_schema
*schema
,
376 const struct drsuapi_DsReplicaObjectListItem
*in
,
378 struct ldb_message
**_msg
)
382 struct ldb_message
*msg
;
384 if (!in
->object
.identifier
) {
388 if (!in
->object
.identifier
->dn
|| !in
->object
.identifier
->dn
[0]) {
392 msg
= ldb_msg_new(mem_ctx
);
393 W_ERROR_HAVE_NO_MEMORY(msg
);
395 msg
->dn
= ldb_dn_new(msg
, ldb
, in
->object
.identifier
->dn
);
396 W_ERROR_HAVE_NO_MEMORY(msg
->dn
);
398 msg
->num_elements
= in
->object
.attribute_ctr
.num_attributes
;
399 msg
->elements
= talloc_array(msg
, struct ldb_message_element
,
401 W_ERROR_HAVE_NO_MEMORY(msg
->elements
);
403 for (i
=0; i
< msg
->num_elements
; i
++) {
404 struct drsuapi_DsReplicaAttribute
*a
;
405 struct ldb_message_element
*e
;
407 a
= &in
->object
.attribute_ctr
.attributes
[i
];
408 e
= &msg
->elements
[i
];
410 status
= dsdb_attribute_drsuapi_to_ldb(ldb
, schema
, schema
->prefixmap
,
411 a
, msg
->elements
, e
);
412 W_ERROR_NOT_OK_RETURN(status
);
421 WERROR
dsdb_origin_objects_commit(struct ldb_context
*ldb
,
423 const struct drsuapi_DsReplicaObjectListItem
*first_object
,
425 struct drsuapi_DsReplicaObjectIdentifier2
**_ids
)
428 const struct dsdb_schema
*schema
;
429 const struct drsuapi_DsReplicaObjectListItem
*cur
;
430 struct ldb_message
**objects
;
431 struct drsuapi_DsReplicaObjectIdentifier2
*ids
;
433 uint32_t num_objects
= 0;
434 const char * const attrs
[] = {
439 struct ldb_result
*res
;
442 for (cur
= first_object
; cur
; cur
= cur
->next_object
) {
446 if (num_objects
== 0) {
450 ret
= ldb_transaction_start(ldb
);
451 if (ret
!= LDB_SUCCESS
) {
452 return WERR_DS_INTERNAL_FAILURE
;
455 objects
= talloc_array(mem_ctx
, struct ldb_message
*,
457 if (objects
== NULL
) {
462 schema
= dsdb_get_schema(ldb
, objects
);
464 return WERR_DS_SCHEMA_NOT_LOADED
;
467 for (i
=0, cur
= first_object
; cur
; cur
= cur
->next_object
, i
++) {
468 status
= dsdb_origin_object_convert(ldb
, schema
, cur
,
469 objects
, &objects
[i
]);
470 if (!W_ERROR_IS_OK(status
)) {
475 ids
= talloc_array(mem_ctx
,
476 struct drsuapi_DsReplicaObjectIdentifier2
,
483 for (i
=0; i
< num_objects
; i
++) {
484 struct dom_sid
*sid
= NULL
;
485 struct ldb_request
*add_req
;
487 DEBUG(6,(__location__
": adding %s\n",
488 ldb_dn_get_linearized(objects
[i
]->dn
)));
490 ret
= ldb_build_add_req(&add_req
,
496 ldb_op_default_callback
,
498 if (ret
!= LDB_SUCCESS
) {
499 status
= WERR_DS_INTERNAL_FAILURE
;
503 ret
= ldb_request_add_control(add_req
, LDB_CONTROL_RELAX_OID
, true, NULL
);
504 if (ret
!= LDB_SUCCESS
) {
505 status
= WERR_DS_INTERNAL_FAILURE
;
509 ret
= ldb_request(ldb
, add_req
);
510 if (ret
== LDB_SUCCESS
) {
511 ret
= ldb_wait(add_req
->handle
, LDB_WAIT_ALL
);
513 if (ret
!= LDB_SUCCESS
) {
514 DEBUG(0,(__location__
": Failed add of %s - %s\n",
515 ldb_dn_get_linearized(objects
[i
]->dn
), ldb_errstring(ldb
)));
516 status
= WERR_DS_INTERNAL_FAILURE
;
520 talloc_free(add_req
);
522 ret
= ldb_search(ldb
, objects
, &res
, objects
[i
]->dn
,
523 LDB_SCOPE_BASE
, attrs
,
525 if (ret
!= LDB_SUCCESS
) {
526 status
= WERR_DS_INTERNAL_FAILURE
;
529 ids
[i
].guid
= samdb_result_guid(res
->msgs
[0], "objectGUID");
530 sid
= samdb_result_dom_sid(objects
, res
->msgs
[0], "objectSid");
534 ZERO_STRUCT(ids
[i
].sid
);
538 ret
= ldb_transaction_commit(ldb
);
539 if (ret
!= LDB_SUCCESS
) {
540 return WERR_DS_INTERNAL_FAILURE
;
543 talloc_free(objects
);
550 talloc_free(objects
);
551 ldb_transaction_cancel(ldb
);