2 Unix SMB/CIFS mplementation.
3 DSDB replication service helper function for outgoing traffic
5 Copyright (C) Stefan Metzmacher 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 "auth/auth.h"
25 #include "smbd/service.h"
26 #include "lib/events/events.h"
27 #include "dsdb/repl/drepl_service.h"
28 #include <ldb_errors.h>
29 #include "../lib/util/dlinklist.h"
30 #include "librpc/gen_ndr/ndr_misc.h"
31 #include "librpc/gen_ndr/ndr_drsuapi.h"
32 #include "librpc/gen_ndr/ndr_drsblobs.h"
33 #include "libcli/composite/composite.h"
34 #include "auth/gensec/gensec.h"
35 #include "param/param.h"
36 #include "../lib/util/tevent_ntstatus.h"
37 #include "libcli/security/security.h"
39 struct dreplsrv_out_drsuapi_state
{
40 struct tevent_context
*ev
;
42 struct dreplsrv_out_connection
*conn
;
44 struct dreplsrv_drsuapi_connection
*drsuapi
;
46 struct drsuapi_DsBindInfoCtr bind_info_ctr
;
47 struct drsuapi_DsBind bind_r
;
50 static void dreplsrv_out_drsuapi_connect_done(struct composite_context
*creq
);
52 struct tevent_req
*dreplsrv_out_drsuapi_send(TALLOC_CTX
*mem_ctx
,
53 struct tevent_context
*ev
,
54 struct dreplsrv_out_connection
*conn
)
56 struct tevent_req
*req
;
57 struct dreplsrv_out_drsuapi_state
*state
;
58 struct composite_context
*creq
;
60 req
= tevent_req_create(mem_ctx
, &state
,
61 struct dreplsrv_out_drsuapi_state
);
68 state
->drsuapi
= conn
->drsuapi
;
70 if (state
->drsuapi
!= NULL
) {
71 struct dcerpc_binding_handle
*b
=
72 state
->drsuapi
->pipe
->binding_handle
;
73 bool is_connected
= dcerpc_binding_handle_is_connected(b
);
77 return tevent_req_post(req
, ev
);
80 TALLOC_FREE(conn
->drsuapi
);
83 state
->drsuapi
= talloc_zero(state
, struct dreplsrv_drsuapi_connection
);
84 if (tevent_req_nomem(state
->drsuapi
, req
)) {
85 return tevent_req_post(req
, ev
);
88 creq
= dcerpc_pipe_connect_b_send(state
, conn
->binding
, &ndr_table_drsuapi
,
89 conn
->service
->system_session_info
->credentials
,
90 ev
, conn
->service
->task
->lp_ctx
);
91 if (tevent_req_nomem(creq
, req
)) {
92 return tevent_req_post(req
, ev
);
94 composite_continue(NULL
, creq
, dreplsrv_out_drsuapi_connect_done
, req
);
99 static void dreplsrv_out_drsuapi_bind_done(struct tevent_req
*subreq
);
101 static void dreplsrv_out_drsuapi_connect_done(struct composite_context
*creq
)
103 struct tevent_req
*req
= talloc_get_type(creq
->async
.private_data
,
105 struct dreplsrv_out_drsuapi_state
*state
= tevent_req_data(req
,
106 struct dreplsrv_out_drsuapi_state
);
108 struct tevent_req
*subreq
;
110 status
= dcerpc_pipe_connect_b_recv(creq
,
112 &state
->drsuapi
->pipe
);
113 if (tevent_req_nterror(req
, status
)) {
117 state
->drsuapi
->drsuapi_handle
= state
->drsuapi
->pipe
->binding_handle
;
119 status
= gensec_session_key(state
->drsuapi
->pipe
->conn
->security_state
.generic_state
,
121 &state
->drsuapi
->gensec_skey
);
122 if (tevent_req_nterror(req
, status
)) {
126 state
->bind_info_ctr
.length
= 28;
127 state
->bind_info_ctr
.info
.info28
= state
->conn
->service
->bind_info28
;
129 state
->bind_r
.in
.bind_guid
= &state
->conn
->service
->ntds_guid
;
130 state
->bind_r
.in
.bind_info
= &state
->bind_info_ctr
;
131 state
->bind_r
.out
.bind_handle
= &state
->drsuapi
->bind_handle
;
133 subreq
= dcerpc_drsuapi_DsBind_r_send(state
,
135 state
->drsuapi
->drsuapi_handle
,
137 if (tevent_req_nomem(subreq
, req
)) {
140 tevent_req_set_callback(subreq
, dreplsrv_out_drsuapi_bind_done
, req
);
143 static void dreplsrv_out_drsuapi_bind_done(struct tevent_req
*subreq
)
145 struct tevent_req
*req
= tevent_req_callback_data(subreq
,
147 struct dreplsrv_out_drsuapi_state
*state
= tevent_req_data(req
,
148 struct dreplsrv_out_drsuapi_state
);
151 status
= dcerpc_drsuapi_DsBind_r_recv(subreq
, state
);
153 if (tevent_req_nterror(req
, status
)) {
157 if (!W_ERROR_IS_OK(state
->bind_r
.out
.result
)) {
158 status
= werror_to_ntstatus(state
->bind_r
.out
.result
);
159 tevent_req_nterror(req
, status
);
163 ZERO_STRUCT(state
->drsuapi
->remote_info28
);
164 if (state
->bind_r
.out
.bind_info
) {
165 struct drsuapi_DsBindInfo28
*info28
;
166 info28
= &state
->drsuapi
->remote_info28
;
168 switch (state
->bind_r
.out
.bind_info
->length
) {
170 struct drsuapi_DsBindInfo24
*info24
;
171 info24
= &state
->bind_r
.out
.bind_info
->info
.info24
;
173 info28
->supported_extensions
= info24
->supported_extensions
;
174 info28
->site_guid
= info24
->site_guid
;
175 info28
->pid
= info24
->pid
;
176 info28
->repl_epoch
= 0;
180 *info28
= state
->bind_r
.out
.bind_info
->info
.info28
;
184 struct drsuapi_DsBindInfo32
*info32
;
185 info32
= &state
->bind_r
.out
.bind_info
->info
.info32
;
187 info28
->supported_extensions
= info32
->supported_extensions
;
188 info28
->site_guid
= info32
->site_guid
;
189 info28
->pid
= info32
->pid
;
190 info28
->repl_epoch
= info32
->repl_epoch
;
194 struct drsuapi_DsBindInfo48
*info48
;
195 info48
= &state
->bind_r
.out
.bind_info
->info
.info48
;
197 info28
->supported_extensions
= info48
->supported_extensions
;
198 info28
->site_guid
= info48
->site_guid
;
199 info28
->pid
= info48
->pid
;
200 info28
->repl_epoch
= info48
->repl_epoch
;
204 struct drsuapi_DsBindInfo52
*info52
;
205 info52
= &state
->bind_r
.out
.bind_info
->info
.info52
;
207 info28
->supported_extensions
= info52
->supported_extensions
;
208 info28
->site_guid
= info52
->site_guid
;
209 info28
->pid
= info52
->pid
;
210 info28
->repl_epoch
= info52
->repl_epoch
;
214 DEBUG(1, ("Warning: invalid info length in bind info: %d\n",
215 state
->bind_r
.out
.bind_info
->length
));
220 tevent_req_done(req
);
223 NTSTATUS
dreplsrv_out_drsuapi_recv(struct tevent_req
*req
)
225 struct dreplsrv_out_drsuapi_state
*state
= tevent_req_data(req
,
226 struct dreplsrv_out_drsuapi_state
);
229 if (tevent_req_is_nterror(req
, &status
)) {
230 tevent_req_received(req
);
234 state
->conn
->drsuapi
= talloc_move(state
->conn
, &state
->drsuapi
);
236 tevent_req_received(req
);
240 struct dreplsrv_op_pull_source_state
{
241 struct tevent_context
*ev
;
242 struct dreplsrv_out_operation
*op
;
243 void *ndr_struct_ptr
;
245 * Used when we have to re-try with a different NC, eg for
246 * EXOP retry or to get a current schema first
248 struct dreplsrv_partition_source_dsa
*source_dsa_retry
;
249 enum drsuapi_DsExtendedOperation extended_op_retry
;
253 static void dreplsrv_op_pull_source_connect_done(struct tevent_req
*subreq
);
255 struct tevent_req
*dreplsrv_op_pull_source_send(TALLOC_CTX
*mem_ctx
,
256 struct tevent_context
*ev
,
257 struct dreplsrv_out_operation
*op
)
259 struct tevent_req
*req
;
260 struct dreplsrv_op_pull_source_state
*state
;
261 struct tevent_req
*subreq
;
263 req
= tevent_req_create(mem_ctx
, &state
,
264 struct dreplsrv_op_pull_source_state
);
271 subreq
= dreplsrv_out_drsuapi_send(state
, ev
, op
->source_dsa
->conn
);
272 if (tevent_req_nomem(subreq
, req
)) {
273 return tevent_req_post(req
, ev
);
275 tevent_req_set_callback(subreq
, dreplsrv_op_pull_source_connect_done
, req
);
280 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req
*req
);
282 static void dreplsrv_op_pull_source_connect_done(struct tevent_req
*subreq
)
284 struct tevent_req
*req
= tevent_req_callback_data(subreq
,
288 status
= dreplsrv_out_drsuapi_recv(subreq
);
290 if (tevent_req_nterror(req
, status
)) {
294 dreplsrv_op_pull_source_get_changes_trigger(req
);
297 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req
*subreq
);
300 get a RODC partial attribute set for a replication call
302 static NTSTATUS
dreplsrv_get_rodc_partial_attribute_set(struct dreplsrv_service
*service
,
304 struct drsuapi_DsPartialAttributeSet
**_pas
,
305 struct drsuapi_DsReplicaOIDMapping_Ctr
**pfm
,
308 struct drsuapi_DsPartialAttributeSet
*pas
;
309 struct dsdb_schema
*schema
;
312 pas
= talloc_zero(mem_ctx
, struct drsuapi_DsPartialAttributeSet
);
313 NT_STATUS_HAVE_NO_MEMORY(pas
);
315 schema
= dsdb_get_schema(service
->samdb
, NULL
);
318 pas
->attids
= talloc_array(pas
, enum drsuapi_DsAttributeId
, schema
->num_attributes
);
319 if (pas
->attids
== NULL
) {
321 return NT_STATUS_NO_MEMORY
;
324 for (i
=0; i
<schema
->num_attributes
; i
++) {
325 struct dsdb_attribute
*a
;
326 a
= schema
->attributes_by_attributeID_id
[i
];
327 if (a
->systemFlags
& (DS_FLAG_ATTR_NOT_REPLICATED
| DS_FLAG_ATTR_IS_CONSTRUCTED
)) {
330 if (a
->searchFlags
& SEARCH_FLAG_RODC_ATTRIBUTE
) {
333 pas
->attids
[pas
->num_attids
] = dsdb_attribute_get_attid(a
, for_schema
);
337 pas
->attids
= talloc_realloc(pas
, pas
->attids
, enum drsuapi_DsAttributeId
, pas
->num_attids
);
338 if (pas
->attids
== NULL
) {
340 return NT_STATUS_NO_MEMORY
;
346 dsdb_get_oid_mappings_drsuapi(schema
, true, mem_ctx
, pfm
);
354 get a GC partial attribute set for a replication call
356 static NTSTATUS
dreplsrv_get_gc_partial_attribute_set(struct dreplsrv_service
*service
,
358 struct drsuapi_DsPartialAttributeSet
**_pas
,
359 struct drsuapi_DsReplicaOIDMapping_Ctr
**pfm
)
361 struct drsuapi_DsPartialAttributeSet
*pas
;
362 struct dsdb_schema
*schema
;
365 pas
= talloc_zero(mem_ctx
, struct drsuapi_DsPartialAttributeSet
);
366 NT_STATUS_HAVE_NO_MEMORY(pas
);
368 schema
= dsdb_get_schema(service
->samdb
, NULL
);
371 pas
->attids
= talloc_array(pas
, enum drsuapi_DsAttributeId
, schema
->num_attributes
);
372 if (pas
->attids
== NULL
) {
374 return NT_STATUS_NO_MEMORY
;
377 for (i
=0; i
<schema
->num_attributes
; i
++) {
378 struct dsdb_attribute
*a
;
379 a
= schema
->attributes_by_attributeID_id
[i
];
380 if (a
->isMemberOfPartialAttributeSet
) {
381 pas
->attids
[pas
->num_attids
] = dsdb_attribute_get_attid(a
, false);
386 pas
->attids
= talloc_realloc(pas
, pas
->attids
, enum drsuapi_DsAttributeId
, pas
->num_attids
);
387 if (pas
->attids
== NULL
) {
389 return NT_STATUS_NO_MEMORY
;
395 dsdb_get_oid_mappings_drsuapi(schema
, true, mem_ctx
, pfm
);
402 convert from one udv format to the other
404 static WERROR
udv_convert(TALLOC_CTX
*mem_ctx
,
405 const struct replUpToDateVectorCtr2
*udv
,
406 struct drsuapi_DsReplicaCursorCtrEx
*udv_ex
)
411 udv_ex
->reserved1
= 0;
412 udv_ex
->reserved2
= 0;
413 udv_ex
->count
= udv
->count
;
414 udv_ex
->cursors
= talloc_array(mem_ctx
, struct drsuapi_DsReplicaCursor
, udv
->count
);
415 W_ERROR_HAVE_NO_MEMORY(udv_ex
->cursors
);
417 for (i
=0; i
<udv
->count
; i
++) {
418 udv_ex
->cursors
[i
].source_dsa_invocation_id
= udv
->cursors
[i
].source_dsa_invocation_id
;
419 udv_ex
->cursors
[i
].highest_usn
= udv
->cursors
[i
].highest_usn
;
426 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req
*req
)
428 struct dreplsrv_op_pull_source_state
*state
= tevent_req_data(req
,
429 struct dreplsrv_op_pull_source_state
);
430 struct repsFromTo1
*rf1
= state
->op
->source_dsa
->repsFrom1
;
431 struct dreplsrv_service
*service
= state
->op
->service
;
432 struct dreplsrv_partition
*partition
= state
->op
->source_dsa
->partition
;
433 struct dreplsrv_drsuapi_connection
*drsuapi
= state
->op
->source_dsa
->conn
->drsuapi
;
434 struct drsuapi_DsGetNCChanges
*r
;
435 struct drsuapi_DsReplicaCursorCtrEx
*uptodateness_vector
;
436 struct tevent_req
*subreq
;
437 struct drsuapi_DsPartialAttributeSet
*pas
= NULL
;
439 uint32_t replica_flags
;
440 struct drsuapi_DsReplicaHighWaterMark highwatermark
;
441 struct ldb_dn
*schema_dn
= ldb_get_schema_basedn(service
->samdb
);
442 struct drsuapi_DsReplicaOIDMapping_Ctr
*mappings
= NULL
;
444 r
= talloc(state
, struct drsuapi_DsGetNCChanges
);
445 if (tevent_req_nomem(r
, req
)) {
449 r
->out
.level_out
= talloc(r
, uint32_t);
450 if (tevent_req_nomem(r
->out
.level_out
, req
)) {
453 r
->in
.req
= talloc(r
, union drsuapi_DsGetNCChangesRequest
);
454 if (tevent_req_nomem(r
->in
.req
, req
)) {
457 r
->out
.ctr
= talloc(r
, union drsuapi_DsGetNCChangesCtr
);
458 if (tevent_req_nomem(r
->out
.ctr
, req
)) {
462 if (partition
->uptodatevector
.count
!= 0 &&
463 partition
->uptodatevector_ex
.count
== 0) {
465 werr
= udv_convert(partition
, &partition
->uptodatevector
, &partition
->uptodatevector_ex
);
466 if (!W_ERROR_IS_OK(werr
)) {
467 DEBUG(0,(__location__
": Failed to convert UDV for %s : %s\n",
468 ldb_dn_get_linearized(partition
->dn
), win_errstr(werr
)));
469 tevent_req_nterror(req
, werror_to_ntstatus(werr
));
474 if (partition
->uptodatevector_ex
.count
== 0) {
475 uptodateness_vector
= NULL
;
477 uptodateness_vector
= &partition
->uptodatevector_ex
;
480 replica_flags
= rf1
->replica_flags
;
481 highwatermark
= rf1
->highwatermark
;
483 if (state
->op
->options
& DRSUAPI_DRS_GET_ANC
) {
484 replica_flags
|= DRSUAPI_DRS_GET_ANC
;
487 if (state
->op
->options
& DRSUAPI_DRS_SYNC_FORCED
) {
488 replica_flags
|= DRSUAPI_DRS_SYNC_FORCED
;
491 if (partition
->partial_replica
) {
492 status
= dreplsrv_get_gc_partial_attribute_set(service
, r
,
495 if (!NT_STATUS_IS_OK(status
)) {
496 DEBUG(0,(__location__
": Failed to construct GC partial attribute set : %s\n", nt_errstr(status
)));
497 tevent_req_nterror(req
, status
);
500 replica_flags
&= ~DRSUAPI_DRS_WRIT_REP
;
501 } else if (partition
->rodc_replica
) {
502 bool for_schema
= false;
503 if (ldb_dn_compare_base(schema_dn
, partition
->dn
) == 0) {
506 status
= dreplsrv_get_rodc_partial_attribute_set(service
, r
,
510 if (!NT_STATUS_IS_OK(status
)) {
511 DEBUG(0,(__location__
": Failed to construct RODC partial attribute set : %s\n", nt_errstr(status
)));
512 tevent_req_nterror(req
, status
);
515 replica_flags
&= ~DRSUAPI_DRS_WRIT_REP
;
516 if (state
->op
->extended_op
== DRSUAPI_EXOP_REPL_SECRET
) {
517 replica_flags
&= ~DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING
;
519 replica_flags
|= DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING
;
522 if (state
->op
->extended_op
!= DRSUAPI_EXOP_NONE
) {
524 * If it's an exop never set the ADD_REF even if it's in
527 replica_flags
&= ~DRSUAPI_DRS_ADD_REF
;
530 /* is this a full resync of all objects? */
531 if (state
->op
->options
& DRSUAPI_DRS_FULL_SYNC_NOW
) {
532 ZERO_STRUCT(highwatermark
);
533 /* clear the FULL_SYNC_NOW option for subsequent
534 stages of the replication cycle */
535 state
->op
->options
&= ~DRSUAPI_DRS_FULL_SYNC_NOW
;
536 state
->op
->options
|= DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS
;
537 replica_flags
|= DRSUAPI_DRS_NEVER_SYNCED
;
539 if (state
->op
->options
& DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS
) {
540 uptodateness_vector
= NULL
;
543 r
->in
.bind_handle
= &drsuapi
->bind_handle
;
544 if (drsuapi
->remote_info28
.supported_extensions
& DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
) {
546 r
->in
.req
->req8
.destination_dsa_guid
= service
->ntds_guid
;
547 r
->in
.req
->req8
.source_dsa_invocation_id
= rf1
->source_dsa_invocation_id
;
548 r
->in
.req
->req8
.naming_context
= &partition
->nc
;
549 r
->in
.req
->req8
.highwatermark
= highwatermark
;
550 r
->in
.req
->req8
.uptodateness_vector
= uptodateness_vector
;
551 r
->in
.req
->req8
.replica_flags
= replica_flags
;
552 r
->in
.req
->req8
.max_object_count
= 133;
553 r
->in
.req
->req8
.max_ndr_size
= 1336811;
554 r
->in
.req
->req8
.extended_op
= state
->op
->extended_op
;
555 r
->in
.req
->req8
.fsmo_info
= state
->op
->fsmo_info
;
556 r
->in
.req
->req8
.partial_attribute_set
= pas
;
557 r
->in
.req
->req8
.partial_attribute_set_ex
= NULL
;
558 r
->in
.req
->req8
.mapping_ctr
.num_mappings
= mappings
== NULL
? 0 : mappings
->num_mappings
;
559 r
->in
.req
->req8
.mapping_ctr
.mappings
= mappings
== NULL
? NULL
: mappings
->mappings
;
562 r
->in
.req
->req5
.destination_dsa_guid
= service
->ntds_guid
;
563 r
->in
.req
->req5
.source_dsa_invocation_id
= rf1
->source_dsa_invocation_id
;
564 r
->in
.req
->req5
.naming_context
= &partition
->nc
;
565 r
->in
.req
->req5
.highwatermark
= highwatermark
;
566 r
->in
.req
->req5
.uptodateness_vector
= uptodateness_vector
;
567 r
->in
.req
->req5
.replica_flags
= replica_flags
;
568 r
->in
.req
->req5
.max_object_count
= 133;
569 r
->in
.req
->req5
.max_ndr_size
= 1336770;
570 r
->in
.req
->req5
.extended_op
= state
->op
->extended_op
;
571 r
->in
.req
->req5
.fsmo_info
= state
->op
->fsmo_info
;
575 NDR_PRINT_IN_DEBUG(drsuapi_DsGetNCChanges
, r
);
578 state
->ndr_struct_ptr
= r
;
579 subreq
= dcerpc_drsuapi_DsGetNCChanges_r_send(state
,
581 drsuapi
->drsuapi_handle
,
583 if (tevent_req_nomem(subreq
, req
)) {
586 tevent_req_set_callback(subreq
, dreplsrv_op_pull_source_get_changes_done
, req
);
589 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req
*req
,
590 struct drsuapi_DsGetNCChanges
*r
,
592 struct drsuapi_DsGetNCChangesCtr1
*ctr1
,
593 struct drsuapi_DsGetNCChangesCtr6
*ctr6
);
595 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req
*subreq
)
597 struct tevent_req
*req
= tevent_req_callback_data(subreq
,
599 struct dreplsrv_op_pull_source_state
*state
= tevent_req_data(req
,
600 struct dreplsrv_op_pull_source_state
);
602 struct drsuapi_DsGetNCChanges
*r
= talloc_get_type(state
->ndr_struct_ptr
,
603 struct drsuapi_DsGetNCChanges
);
604 uint32_t ctr_level
= 0;
605 struct drsuapi_DsGetNCChangesCtr1
*ctr1
= NULL
;
606 struct drsuapi_DsGetNCChangesCtr6
*ctr6
= NULL
;
607 enum drsuapi_DsExtendedError extended_ret
= DRSUAPI_EXOP_ERR_NONE
;
608 state
->ndr_struct_ptr
= NULL
;
610 status
= dcerpc_drsuapi_DsGetNCChanges_r_recv(subreq
, r
);
612 if (tevent_req_nterror(req
, status
)) {
616 if (!W_ERROR_IS_OK(r
->out
.result
)) {
617 status
= werror_to_ntstatus(r
->out
.result
);
618 tevent_req_nterror(req
, status
);
622 if (*r
->out
.level_out
== 1) {
624 ctr1
= &r
->out
.ctr
->ctr1
;
625 } else if (*r
->out
.level_out
== 2 &&
626 r
->out
.ctr
->ctr2
.mszip1
.ts
) {
628 ctr1
= &r
->out
.ctr
->ctr2
.mszip1
.ts
->ctr1
;
629 } else if (*r
->out
.level_out
== 6) {
631 ctr6
= &r
->out
.ctr
->ctr6
;
632 } else if (*r
->out
.level_out
== 7 &&
633 r
->out
.ctr
->ctr7
.level
== 6 &&
634 r
->out
.ctr
->ctr7
.type
== DRSUAPI_COMPRESSION_TYPE_MSZIP
&&
635 r
->out
.ctr
->ctr7
.ctr
.mszip6
.ts
) {
637 ctr6
= &r
->out
.ctr
->ctr7
.ctr
.mszip6
.ts
->ctr6
;
638 } else if (*r
->out
.level_out
== 7 &&
639 r
->out
.ctr
->ctr7
.level
== 6 &&
640 r
->out
.ctr
->ctr7
.type
== DRSUAPI_COMPRESSION_TYPE_XPRESS
&&
641 r
->out
.ctr
->ctr7
.ctr
.xpress6
.ts
) {
643 ctr6
= &r
->out
.ctr
->ctr7
.ctr
.xpress6
.ts
->ctr6
;
645 status
= werror_to_ntstatus(WERR_BAD_NET_RESP
);
646 tevent_req_nterror(req
, status
);
650 if (!ctr1
&& !ctr6
) {
651 status
= werror_to_ntstatus(WERR_BAD_NET_RESP
);
652 tevent_req_nterror(req
, status
);
656 if (ctr_level
== 6) {
657 if (!W_ERROR_IS_OK(ctr6
->drs_error
)) {
658 status
= werror_to_ntstatus(ctr6
->drs_error
);
659 tevent_req_nterror(req
, status
);
662 extended_ret
= ctr6
->extended_ret
;
665 if (ctr_level
== 1) {
666 extended_ret
= ctr1
->extended_ret
;
669 if (state
->op
->extended_op
!= DRSUAPI_EXOP_NONE
) {
670 state
->op
->extended_ret
= extended_ret
;
672 if (extended_ret
!= DRSUAPI_EXOP_ERR_SUCCESS
) {
673 status
= NT_STATUS_UNSUCCESSFUL
;
674 tevent_req_nterror(req
, status
);
679 dreplsrv_op_pull_source_apply_changes_trigger(req
, r
, ctr_level
, ctr1
, ctr6
);
682 static void dreplsrv_update_refs_trigger(struct tevent_req
*req
);
684 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req
*req
,
685 struct drsuapi_DsGetNCChanges
*r
,
687 struct drsuapi_DsGetNCChangesCtr1
*ctr1
,
688 struct drsuapi_DsGetNCChangesCtr6
*ctr6
)
690 struct dreplsrv_op_pull_source_state
*state
= tevent_req_data(req
,
691 struct dreplsrv_op_pull_source_state
);
692 struct repsFromTo1 rf1
= *state
->op
->source_dsa
->repsFrom1
;
693 struct dreplsrv_service
*service
= state
->op
->service
;
694 struct dreplsrv_partition
*partition
= state
->op
->source_dsa
->partition
;
695 struct dreplsrv_drsuapi_connection
*drsuapi
= state
->op
->source_dsa
->conn
->drsuapi
;
696 struct ldb_dn
*schema_dn
= ldb_get_schema_basedn(service
->samdb
);
697 struct dsdb_schema
*schema
;
698 struct dsdb_schema
*working_schema
= NULL
;
699 const struct drsuapi_DsReplicaOIDMapping_Ctr
*mapping_ctr
;
700 uint32_t object_count
;
701 struct drsuapi_DsReplicaObjectListItemEx
*first_object
;
702 uint32_t linked_attributes_count
;
703 struct drsuapi_DsReplicaLinkedAttribute
*linked_attributes
;
704 const struct drsuapi_DsReplicaCursor2CtrEx
*uptodateness_vector
;
705 struct dsdb_extended_replicated_objects
*objects
;
706 bool more_data
= false;
709 uint32_t dsdb_repl_flags
= 0;
710 struct ldb_dn
*nc_root
= NULL
;
715 mapping_ctr
= &ctr1
->mapping_ctr
;
716 object_count
= ctr1
->object_count
;
717 first_object
= ctr1
->first_object
;
718 linked_attributes_count
= 0;
719 linked_attributes
= NULL
;
720 rf1
.source_dsa_obj_guid
= ctr1
->source_dsa_guid
;
721 rf1
.source_dsa_invocation_id
= ctr1
->source_dsa_invocation_id
;
722 rf1
.highwatermark
= ctr1
->new_highwatermark
;
723 uptodateness_vector
= NULL
; /* TODO: map it */
724 more_data
= ctr1
->more_data
;
727 mapping_ctr
= &ctr6
->mapping_ctr
;
728 object_count
= ctr6
->object_count
;
729 first_object
= ctr6
->first_object
;
730 linked_attributes_count
= ctr6
->linked_attributes_count
;
731 linked_attributes
= ctr6
->linked_attributes
;
732 rf1
.source_dsa_obj_guid
= ctr6
->source_dsa_guid
;
733 rf1
.source_dsa_invocation_id
= ctr6
->source_dsa_invocation_id
;
734 rf1
.highwatermark
= ctr6
->new_highwatermark
;
735 uptodateness_vector
= ctr6
->uptodateness_vector
;
736 more_data
= ctr6
->more_data
;
739 nt_status
= werror_to_ntstatus(WERR_BAD_NET_RESP
);
740 tevent_req_nterror(req
, nt_status
);
744 schema
= dsdb_get_schema(service
->samdb
, state
);
746 DEBUG(0,(__location__
": Schema is not loaded yet!\n"));
747 tevent_req_nterror(req
, NT_STATUS_INTERNAL_ERROR
);
752 * Decide what working schema to use for object conversion.
753 * We won't need a working schema for empty replicas sent.
756 bool is_schema
= ldb_dn_compare(partition
->dn
, schema_dn
) == 0;
758 /* create working schema to convert objects with */
759 status
= dsdb_repl_make_working_schema(service
->samdb
,
764 &drsuapi
->gensec_skey
,
765 state
, &working_schema
);
766 if (!W_ERROR_IS_OK(status
)) {
767 DEBUG(0,("Failed to create working schema: %s\n",
768 win_errstr(status
)));
769 tevent_req_nterror(req
, NT_STATUS_INTERNAL_ERROR
);
775 if (partition
->partial_replica
|| partition
->rodc_replica
) {
776 dsdb_repl_flags
|= DSDB_REPL_FLAG_PARTIAL_REPLICA
;
778 if (state
->op
->options
& DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS
) {
779 dsdb_repl_flags
|= DSDB_REPL_FLAG_PRIORITISE_INCOMING
;
781 if (state
->op
->options
& DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING
) {
782 dsdb_repl_flags
|= DSDB_REPL_FLAG_EXPECT_NO_SECRETS
;
785 if (state
->op
->extended_op
!= DRSUAPI_EXOP_NONE
) {
786 ret
= dsdb_find_nc_root(service
->samdb
, partition
,
787 partition
->dn
, &nc_root
);
788 if (ret
!= LDB_SUCCESS
) {
789 DEBUG(0,(__location__
": Failed to find nc_root for %s\n",
790 ldb_dn_get_linearized(partition
->dn
)));
791 tevent_req_nterror(req
, NT_STATUS_INTERNAL_ERROR
);
795 nc_root
= partition
->dn
;
798 status
= dsdb_replicated_objects_convert(service
->samdb
,
799 working_schema
? working_schema
: schema
,
804 linked_attributes_count
,
808 &drsuapi
->gensec_skey
,
812 if (W_ERROR_EQUAL(status
, WERR_DS_DRA_SCHEMA_MISMATCH
)) {
813 struct dreplsrv_partition
*p
;
815 if (state
->retry_started
) {
816 nt_status
= werror_to_ntstatus(WERR_BAD_NET_RESP
);
817 DEBUG(0,("Failed to convert objects after retry: %s/%s\n",
818 win_errstr(status
), nt_errstr(nt_status
)));
819 tevent_req_nterror(req
, nt_status
);
824 * Change info sync or extended operation into a fetch
825 * of the schema partition, so we get all the schema
828 * We don't want to re-do the remote exop,
829 * unless it was REPL_SECRET so we set the
830 * fallback operation to just be a fetch of
831 * the relevent partition.
835 if (state
->op
->extended_op
== DRSUAPI_EXOP_REPL_SECRET
) {
836 state
->extended_op_retry
= state
->op
->extended_op
;
838 state
->extended_op_retry
= DRSUAPI_EXOP_NONE
;
840 state
->op
->extended_op
= DRSUAPI_EXOP_NONE
;
842 if (ldb_dn_compare(nc_root
, partition
->dn
) == 0) {
843 state
->source_dsa_retry
= state
->op
->source_dsa
;
845 status
= dreplsrv_partition_find_for_nc(service
,
847 ldb_dn_get_linearized(nc_root
),
849 if (!W_ERROR_IS_OK(status
)) {
850 DEBUG(2, ("Failed to find requested Naming Context for %s: %s",
851 ldb_dn_get_linearized(nc_root
),
852 win_errstr(status
)));
853 nt_status
= werror_to_ntstatus(status
);
854 tevent_req_nterror(req
, nt_status
);
857 status
= dreplsrv_partition_source_dsa_by_guid(p
,
858 &state
->op
->source_dsa
->repsFrom1
->source_dsa_obj_guid
,
859 &state
->source_dsa_retry
);
861 if (!W_ERROR_IS_OK(status
)) {
862 struct GUID_txt_buf str
;
863 DEBUG(2, ("Failed to find requested source DSA for %s and %s: %s",
864 ldb_dn_get_linearized(nc_root
),
865 GUID_buf_string(&state
->op
->source_dsa
->repsFrom1
->source_dsa_obj_guid
, &str
),
866 win_errstr(status
)));
867 nt_status
= werror_to_ntstatus(status
);
868 tevent_req_nterror(req
, nt_status
);
873 /* Find schema naming context to be synchronized first */
874 status
= dreplsrv_partition_find_for_nc(service
,
876 ldb_dn_get_linearized(schema_dn
),
878 if (!W_ERROR_IS_OK(status
)) {
879 DEBUG(2, ("Failed to find requested Naming Context for schema: %s",
880 win_errstr(status
)));
881 nt_status
= werror_to_ntstatus(status
);
882 tevent_req_nterror(req
, nt_status
);
886 status
= dreplsrv_partition_source_dsa_by_guid(p
,
887 &state
->op
->source_dsa
->repsFrom1
->source_dsa_obj_guid
,
888 &state
->op
->source_dsa
);
889 if (!W_ERROR_IS_OK(status
)) {
890 struct GUID_txt_buf str
;
891 DEBUG(2, ("Failed to find requested source DSA for %s and %s: %s",
892 ldb_dn_get_linearized(schema_dn
),
893 GUID_buf_string(&state
->op
->source_dsa
->repsFrom1
->source_dsa_obj_guid
, &str
),
894 win_errstr(status
)));
895 nt_status
= werror_to_ntstatus(status
);
896 tevent_req_nterror(req
, nt_status
);
899 DEBUG(4,("Wrong schema when applying reply GetNCChanges, retrying\n"));
901 state
->retry_started
= true;
902 dreplsrv_op_pull_source_get_changes_trigger(req
);
905 } else if (!W_ERROR_IS_OK(status
)) {
906 nt_status
= werror_to_ntstatus(WERR_BAD_NET_RESP
);
907 DEBUG(0,("Failed to convert objects: %s/%s\n",
908 win_errstr(status
), nt_errstr(nt_status
)));
909 tevent_req_nterror(req
, nt_status
);
913 status
= dsdb_replicated_objects_commit(service
->samdb
,
916 &state
->op
->source_dsa
->notify_uSN
);
917 talloc_free(objects
);
919 if (!W_ERROR_IS_OK(status
)) {
922 * If we failed to apply the records due to a missing
923 * parent, try again after asking for the parent
924 * records first. Because we don't update the
925 * highwatermark, we start this part of the cycle
928 if (((state
->op
->options
& DRSUAPI_DRS_GET_ANC
) == 0)
929 && W_ERROR_EQUAL(status
, WERR_DS_DRA_MISSING_PARENT
)) {
930 state
->op
->options
|= DRSUAPI_DRS_GET_ANC
;
931 DEBUG(4,("Missing parent object when we didn't set the DRSUAPI_DRS_GET_ANC flag, retrying\n"));
932 dreplsrv_op_pull_source_get_changes_trigger(req
);
934 } else if (((state
->op
->options
& DRSUAPI_DRS_GET_ANC
))
935 && W_ERROR_EQUAL(status
, WERR_DS_DRA_MISSING_PARENT
)) {
936 DEBUG(1,("Missing parent object despite setting DRSUAPI_DRS_GET_ANC flag\n"));
937 nt_status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
939 nt_status
= werror_to_ntstatus(WERR_BAD_NET_RESP
);
941 DEBUG(0,("Failed to commit objects: %s/%s\n",
942 win_errstr(status
), nt_errstr(nt_status
)));
943 tevent_req_nterror(req
, nt_status
);
947 if (state
->op
->extended_op
== DRSUAPI_EXOP_NONE
) {
948 /* if it applied fine, we need to update the highwatermark */
949 *state
->op
->source_dsa
->repsFrom1
= rf1
;
952 /* we don't need this maybe very large structure anymore */
956 dreplsrv_op_pull_source_get_changes_trigger(req
);
961 * If we had to divert via doing some other thing, such as
962 * pulling the schema, then go back and do the original
963 * operation once we are done.
965 if (state
->source_dsa_retry
!= NULL
) {
966 state
->op
->source_dsa
= state
->source_dsa_retry
;
967 state
->op
->extended_op
= state
->extended_op_retry
;
968 state
->source_dsa_retry
= NULL
;
969 dreplsrv_op_pull_source_get_changes_trigger(req
);
973 if (state
->op
->extended_op
!= DRSUAPI_EXOP_NONE
||
974 state
->op
->service
->am_rodc
) {
976 we don't do the UpdateRefs for extended ops or if we
979 tevent_req_done(req
);
983 /* now we need to update the repsTo record for this partition
984 on the server. These records are initially established when
985 we join the domain, but they quickly expire. We do it here
986 so we can use the already established DRSUAPI pipe
988 dreplsrv_update_refs_trigger(req
);
991 static void dreplsrv_update_refs_done(struct tevent_req
*subreq
);
994 send a UpdateRefs request to refresh our repsTo record on the server
996 static void dreplsrv_update_refs_trigger(struct tevent_req
*req
)
998 struct dreplsrv_op_pull_source_state
*state
= tevent_req_data(req
,
999 struct dreplsrv_op_pull_source_state
);
1000 struct dreplsrv_service
*service
= state
->op
->service
;
1001 struct dreplsrv_partition
*partition
= state
->op
->source_dsa
->partition
;
1002 struct dreplsrv_drsuapi_connection
*drsuapi
= state
->op
->source_dsa
->conn
->drsuapi
;
1003 struct drsuapi_DsReplicaUpdateRefs
*r
;
1004 char *ntds_dns_name
;
1005 struct tevent_req
*subreq
;
1007 r
= talloc(state
, struct drsuapi_DsReplicaUpdateRefs
);
1008 if (tevent_req_nomem(r
, req
)) {
1012 ntds_dns_name
= samdb_ntds_msdcs_dns_name(service
->samdb
, r
, &service
->ntds_guid
);
1013 if (tevent_req_nomem(ntds_dns_name
, req
)) {
1018 r
->in
.bind_handle
= &drsuapi
->bind_handle
;
1020 r
->in
.req
.req1
.naming_context
= &partition
->nc
;
1021 r
->in
.req
.req1
.dest_dsa_dns_name
= ntds_dns_name
;
1022 r
->in
.req
.req1
.dest_dsa_guid
= service
->ntds_guid
;
1023 r
->in
.req
.req1
.options
= DRSUAPI_DRS_ADD_REF
| DRSUAPI_DRS_DEL_REF
;
1024 if (!service
->am_rodc
) {
1025 r
->in
.req
.req1
.options
|= DRSUAPI_DRS_WRIT_REP
;
1028 state
->ndr_struct_ptr
= r
;
1029 subreq
= dcerpc_drsuapi_DsReplicaUpdateRefs_r_send(state
,
1031 drsuapi
->drsuapi_handle
,
1033 if (tevent_req_nomem(subreq
, req
)) {
1037 tevent_req_set_callback(subreq
, dreplsrv_update_refs_done
, req
);
1041 receive a UpdateRefs reply
1043 static void dreplsrv_update_refs_done(struct tevent_req
*subreq
)
1045 struct tevent_req
*req
= tevent_req_callback_data(subreq
,
1047 struct dreplsrv_op_pull_source_state
*state
= tevent_req_data(req
,
1048 struct dreplsrv_op_pull_source_state
);
1049 struct drsuapi_DsReplicaUpdateRefs
*r
= talloc_get_type(state
->ndr_struct_ptr
,
1050 struct drsuapi_DsReplicaUpdateRefs
);
1053 state
->ndr_struct_ptr
= NULL
;
1055 status
= dcerpc_drsuapi_DsReplicaUpdateRefs_r_recv(subreq
, r
);
1056 TALLOC_FREE(subreq
);
1057 if (!NT_STATUS_IS_OK(status
)) {
1058 DEBUG(0,("UpdateRefs failed with %s\n",
1059 nt_errstr(status
)));
1060 tevent_req_nterror(req
, status
);
1064 if (!W_ERROR_IS_OK(r
->out
.result
)) {
1065 status
= werror_to_ntstatus(r
->out
.result
);
1066 DEBUG(0,("UpdateRefs failed with %s/%s for %s %s\n",
1067 win_errstr(r
->out
.result
),
1069 r
->in
.req
.req1
.dest_dsa_dns_name
,
1070 r
->in
.req
.req1
.naming_context
->dn
));
1072 * TODO we are currently not sending the
1073 * DsReplicaUpdateRefs at the correct moment,
1074 * we do it just after a GetNcChanges which is
1075 * not always correct.
1076 * Especially when another DC is trying to demote
1077 * it will sends us a DsReplicaSync that will trigger a getNcChanges
1078 * this call will succeed but the DsRecplicaUpdateRefs that we send
1079 * just after will not because the DC is in a demote state and
1080 * will reply us a WERR_DS_DRA_BUSY, this error will cause us to
1081 * answer to the DsReplicaSync with a non OK status, the other DC
1082 * will stop the demote due to this error.
1083 * In order to cope with this we will for the moment concider
1084 * a DS_DRA_BUSY not as an error.
1085 * It's not ideal but it should not have a too huge impact for
1086 * running production as this error otherwise never happen and
1087 * due to the fact the send a DsReplicaUpdateRefs after each getNcChanges
1089 if (!W_ERROR_EQUAL(r
->out
.result
, WERR_DS_DRA_BUSY
)) {
1090 tevent_req_nterror(req
, status
);
1095 DEBUG(4,("UpdateRefs OK for %s %s\n",
1096 r
->in
.req
.req1
.dest_dsa_dns_name
,
1097 r
->in
.req
.req1
.naming_context
->dn
));
1099 tevent_req_done(req
);
1102 WERROR
dreplsrv_op_pull_source_recv(struct tevent_req
*req
)
1106 if (tevent_req_is_nterror(req
, &status
)) {
1107 tevent_req_received(req
);
1108 return ntstatus_to_werror(status
);
1111 tevent_req_received(req
);