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"
40 #define DBGC_CLASS DBGC_DRS_REPL
42 struct dreplsrv_out_drsuapi_state
{
43 struct tevent_context
*ev
;
45 struct dreplsrv_out_connection
*conn
;
47 struct dreplsrv_drsuapi_connection
*drsuapi
;
49 struct drsuapi_DsBindInfoCtr bind_info_ctr
;
50 struct drsuapi_DsBind bind_r
;
53 static void dreplsrv_out_drsuapi_connect_done(struct composite_context
*creq
);
55 struct tevent_req
*dreplsrv_out_drsuapi_send(TALLOC_CTX
*mem_ctx
,
56 struct tevent_context
*ev
,
57 struct dreplsrv_out_connection
*conn
)
59 struct tevent_req
*req
;
60 struct dreplsrv_out_drsuapi_state
*state
;
61 struct composite_context
*creq
;
63 req
= tevent_req_create(mem_ctx
, &state
,
64 struct dreplsrv_out_drsuapi_state
);
71 state
->drsuapi
= conn
->drsuapi
;
73 if (state
->drsuapi
!= NULL
) {
74 struct dcerpc_binding_handle
*b
=
75 state
->drsuapi
->pipe
->binding_handle
;
76 bool is_connected
= dcerpc_binding_handle_is_connected(b
);
80 return tevent_req_post(req
, ev
);
83 TALLOC_FREE(conn
->drsuapi
);
86 state
->drsuapi
= talloc_zero(state
, struct dreplsrv_drsuapi_connection
);
87 if (tevent_req_nomem(state
->drsuapi
, req
)) {
88 return tevent_req_post(req
, ev
);
91 creq
= dcerpc_pipe_connect_b_send(state
, conn
->binding
, &ndr_table_drsuapi
,
92 conn
->service
->system_session_info
->credentials
,
93 ev
, conn
->service
->task
->lp_ctx
);
94 if (tevent_req_nomem(creq
, req
)) {
95 return tevent_req_post(req
, ev
);
97 composite_continue(NULL
, creq
, dreplsrv_out_drsuapi_connect_done
, req
);
102 static void dreplsrv_out_drsuapi_bind_done(struct tevent_req
*subreq
);
104 static void dreplsrv_out_drsuapi_connect_done(struct composite_context
*creq
)
106 struct tevent_req
*req
= talloc_get_type(creq
->async
.private_data
,
108 struct dreplsrv_out_drsuapi_state
*state
= tevent_req_data(req
,
109 struct dreplsrv_out_drsuapi_state
);
111 struct tevent_req
*subreq
;
113 status
= dcerpc_pipe_connect_b_recv(creq
,
115 &state
->drsuapi
->pipe
);
116 if (tevent_req_nterror(req
, status
)) {
120 state
->drsuapi
->drsuapi_handle
= state
->drsuapi
->pipe
->binding_handle
;
122 status
= gensec_session_key(state
->drsuapi
->pipe
->conn
->security_state
.generic_state
,
124 &state
->drsuapi
->gensec_skey
);
125 if (tevent_req_nterror(req
, status
)) {
129 state
->bind_info_ctr
.length
= 28;
130 state
->bind_info_ctr
.info
.info28
= state
->conn
->service
->bind_info28
;
132 state
->bind_r
.in
.bind_guid
= &state
->conn
->service
->ntds_guid
;
133 state
->bind_r
.in
.bind_info
= &state
->bind_info_ctr
;
134 state
->bind_r
.out
.bind_handle
= &state
->drsuapi
->bind_handle
;
136 subreq
= dcerpc_drsuapi_DsBind_r_send(state
,
138 state
->drsuapi
->drsuapi_handle
,
140 if (tevent_req_nomem(subreq
, req
)) {
143 tevent_req_set_callback(subreq
, dreplsrv_out_drsuapi_bind_done
, req
);
146 static void dreplsrv_out_drsuapi_bind_done(struct tevent_req
*subreq
)
148 struct tevent_req
*req
= tevent_req_callback_data(subreq
,
150 struct dreplsrv_out_drsuapi_state
*state
= tevent_req_data(req
,
151 struct dreplsrv_out_drsuapi_state
);
154 status
= dcerpc_drsuapi_DsBind_r_recv(subreq
, state
);
156 if (tevent_req_nterror(req
, status
)) {
160 if (!W_ERROR_IS_OK(state
->bind_r
.out
.result
)) {
161 status
= werror_to_ntstatus(state
->bind_r
.out
.result
);
162 tevent_req_nterror(req
, status
);
166 ZERO_STRUCT(state
->drsuapi
->remote_info28
);
167 if (state
->bind_r
.out
.bind_info
) {
168 struct drsuapi_DsBindInfo28
*info28
;
169 info28
= &state
->drsuapi
->remote_info28
;
171 switch (state
->bind_r
.out
.bind_info
->length
) {
173 struct drsuapi_DsBindInfo24
*info24
;
174 info24
= &state
->bind_r
.out
.bind_info
->info
.info24
;
176 info28
->supported_extensions
= info24
->supported_extensions
;
177 info28
->site_guid
= info24
->site_guid
;
178 info28
->pid
= info24
->pid
;
179 info28
->repl_epoch
= 0;
183 *info28
= state
->bind_r
.out
.bind_info
->info
.info28
;
187 struct drsuapi_DsBindInfo32
*info32
;
188 info32
= &state
->bind_r
.out
.bind_info
->info
.info32
;
190 info28
->supported_extensions
= info32
->supported_extensions
;
191 info28
->site_guid
= info32
->site_guid
;
192 info28
->pid
= info32
->pid
;
193 info28
->repl_epoch
= info32
->repl_epoch
;
197 struct drsuapi_DsBindInfo48
*info48
;
198 info48
= &state
->bind_r
.out
.bind_info
->info
.info48
;
200 info28
->supported_extensions
= info48
->supported_extensions
;
201 info28
->site_guid
= info48
->site_guid
;
202 info28
->pid
= info48
->pid
;
203 info28
->repl_epoch
= info48
->repl_epoch
;
207 struct drsuapi_DsBindInfo52
*info52
;
208 info52
= &state
->bind_r
.out
.bind_info
->info
.info52
;
210 info28
->supported_extensions
= info52
->supported_extensions
;
211 info28
->site_guid
= info52
->site_guid
;
212 info28
->pid
= info52
->pid
;
213 info28
->repl_epoch
= info52
->repl_epoch
;
217 DEBUG(1, ("Warning: invalid info length in bind info: %d\n",
218 state
->bind_r
.out
.bind_info
->length
));
223 tevent_req_done(req
);
226 NTSTATUS
dreplsrv_out_drsuapi_recv(struct tevent_req
*req
)
228 struct dreplsrv_out_drsuapi_state
*state
= tevent_req_data(req
,
229 struct dreplsrv_out_drsuapi_state
);
232 if (tevent_req_is_nterror(req
, &status
)) {
233 tevent_req_received(req
);
237 state
->conn
->drsuapi
= talloc_move(state
->conn
, &state
->drsuapi
);
239 tevent_req_received(req
);
243 struct dreplsrv_op_pull_source_state
{
244 struct tevent_context
*ev
;
245 struct dreplsrv_out_operation
*op
;
246 void *ndr_struct_ptr
;
248 * Used when we have to re-try with a different NC, eg for
249 * EXOP retry or to get a current schema first
251 struct dreplsrv_partition_source_dsa
*source_dsa_retry
;
252 enum drsuapi_DsExtendedOperation extended_op_retry
;
256 static void dreplsrv_op_pull_source_connect_done(struct tevent_req
*subreq
);
258 struct tevent_req
*dreplsrv_op_pull_source_send(TALLOC_CTX
*mem_ctx
,
259 struct tevent_context
*ev
,
260 struct dreplsrv_out_operation
*op
)
262 struct tevent_req
*req
;
263 struct dreplsrv_op_pull_source_state
*state
;
264 struct tevent_req
*subreq
;
266 req
= tevent_req_create(mem_ctx
, &state
,
267 struct dreplsrv_op_pull_source_state
);
274 subreq
= dreplsrv_out_drsuapi_send(state
, ev
, op
->source_dsa
->conn
);
275 if (tevent_req_nomem(subreq
, req
)) {
276 return tevent_req_post(req
, ev
);
278 tevent_req_set_callback(subreq
, dreplsrv_op_pull_source_connect_done
, req
);
283 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req
*req
);
285 static void dreplsrv_op_pull_source_connect_done(struct tevent_req
*subreq
)
287 struct tevent_req
*req
= tevent_req_callback_data(subreq
,
291 status
= dreplsrv_out_drsuapi_recv(subreq
);
293 if (tevent_req_nterror(req
, status
)) {
297 dreplsrv_op_pull_source_get_changes_trigger(req
);
300 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req
*subreq
);
303 get a RODC partial attribute set for a replication call
305 static NTSTATUS
dreplsrv_get_rodc_partial_attribute_set(struct dreplsrv_service
*service
,
307 struct drsuapi_DsPartialAttributeSet
**_pas
,
308 struct drsuapi_DsReplicaOIDMapping_Ctr
**pfm
,
311 struct drsuapi_DsPartialAttributeSet
*pas
;
312 struct dsdb_schema
*schema
;
315 pas
= talloc_zero(mem_ctx
, struct drsuapi_DsPartialAttributeSet
);
316 NT_STATUS_HAVE_NO_MEMORY(pas
);
318 schema
= dsdb_get_schema(service
->samdb
, NULL
);
321 pas
->attids
= talloc_array(pas
, enum drsuapi_DsAttributeId
, schema
->num_attributes
);
322 if (pas
->attids
== NULL
) {
324 return NT_STATUS_NO_MEMORY
;
327 for (i
=0; i
<schema
->num_attributes
; i
++) {
328 struct dsdb_attribute
*a
;
329 a
= schema
->attributes_by_attributeID_id
[i
];
330 if (a
->systemFlags
& (DS_FLAG_ATTR_NOT_REPLICATED
| DS_FLAG_ATTR_IS_CONSTRUCTED
)) {
333 if (a
->searchFlags
& SEARCH_FLAG_RODC_ATTRIBUTE
) {
336 pas
->attids
[pas
->num_attids
] = dsdb_attribute_get_attid(a
, for_schema
);
340 pas
->attids
= talloc_realloc(pas
, pas
->attids
, enum drsuapi_DsAttributeId
, pas
->num_attids
);
341 if (pas
->attids
== NULL
) {
343 return NT_STATUS_NO_MEMORY
;
349 dsdb_get_oid_mappings_drsuapi(schema
, true, mem_ctx
, pfm
);
357 get a GC partial attribute set for a replication call
359 static NTSTATUS
dreplsrv_get_gc_partial_attribute_set(struct dreplsrv_service
*service
,
361 struct drsuapi_DsPartialAttributeSet
**_pas
,
362 struct drsuapi_DsReplicaOIDMapping_Ctr
**pfm
)
364 struct drsuapi_DsPartialAttributeSet
*pas
;
365 struct dsdb_schema
*schema
;
368 pas
= talloc_zero(mem_ctx
, struct drsuapi_DsPartialAttributeSet
);
369 NT_STATUS_HAVE_NO_MEMORY(pas
);
371 schema
= dsdb_get_schema(service
->samdb
, NULL
);
374 pas
->attids
= talloc_array(pas
, enum drsuapi_DsAttributeId
, schema
->num_attributes
);
375 if (pas
->attids
== NULL
) {
377 return NT_STATUS_NO_MEMORY
;
380 for (i
=0; i
<schema
->num_attributes
; i
++) {
381 struct dsdb_attribute
*a
;
382 a
= schema
->attributes_by_attributeID_id
[i
];
383 if (a
->isMemberOfPartialAttributeSet
) {
384 pas
->attids
[pas
->num_attids
] = dsdb_attribute_get_attid(a
, false);
389 pas
->attids
= talloc_realloc(pas
, pas
->attids
, enum drsuapi_DsAttributeId
, pas
->num_attids
);
390 if (pas
->attids
== NULL
) {
392 return NT_STATUS_NO_MEMORY
;
398 dsdb_get_oid_mappings_drsuapi(schema
, true, mem_ctx
, pfm
);
405 convert from one udv format to the other
407 static WERROR
udv_convert(TALLOC_CTX
*mem_ctx
,
408 const struct replUpToDateVectorCtr2
*udv
,
409 struct drsuapi_DsReplicaCursorCtrEx
*udv_ex
)
414 udv_ex
->reserved1
= 0;
415 udv_ex
->reserved2
= 0;
416 udv_ex
->count
= udv
->count
;
417 udv_ex
->cursors
= talloc_array(mem_ctx
, struct drsuapi_DsReplicaCursor
, udv
->count
);
418 W_ERROR_HAVE_NO_MEMORY(udv_ex
->cursors
);
420 for (i
=0; i
<udv
->count
; i
++) {
421 udv_ex
->cursors
[i
].source_dsa_invocation_id
= udv
->cursors
[i
].source_dsa_invocation_id
;
422 udv_ex
->cursors
[i
].highest_usn
= udv
->cursors
[i
].highest_usn
;
429 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req
*req
)
431 struct dreplsrv_op_pull_source_state
*state
= tevent_req_data(req
,
432 struct dreplsrv_op_pull_source_state
);
433 struct repsFromTo1
*rf1
= state
->op
->source_dsa
->repsFrom1
;
434 struct dreplsrv_service
*service
= state
->op
->service
;
435 struct dreplsrv_partition
*partition
= state
->op
->source_dsa
->partition
;
436 struct dreplsrv_drsuapi_connection
*drsuapi
= state
->op
->source_dsa
->conn
->drsuapi
;
437 struct drsuapi_DsGetNCChanges
*r
;
438 struct drsuapi_DsReplicaCursorCtrEx
*uptodateness_vector
;
439 struct tevent_req
*subreq
;
440 struct drsuapi_DsPartialAttributeSet
*pas
= NULL
;
442 uint32_t replica_flags
;
443 struct drsuapi_DsReplicaHighWaterMark highwatermark
;
444 struct ldb_dn
*schema_dn
= ldb_get_schema_basedn(service
->samdb
);
445 struct drsuapi_DsReplicaOIDMapping_Ctr
*mappings
= NULL
;
447 r
= talloc(state
, struct drsuapi_DsGetNCChanges
);
448 if (tevent_req_nomem(r
, req
)) {
452 r
->out
.level_out
= talloc(r
, uint32_t);
453 if (tevent_req_nomem(r
->out
.level_out
, req
)) {
456 r
->in
.req
= talloc(r
, union drsuapi_DsGetNCChangesRequest
);
457 if (tevent_req_nomem(r
->in
.req
, req
)) {
460 r
->out
.ctr
= talloc(r
, union drsuapi_DsGetNCChangesCtr
);
461 if (tevent_req_nomem(r
->out
.ctr
, req
)) {
465 if (partition
->uptodatevector
.count
!= 0 &&
466 partition
->uptodatevector_ex
.count
== 0) {
468 werr
= udv_convert(partition
, &partition
->uptodatevector
, &partition
->uptodatevector_ex
);
469 if (!W_ERROR_IS_OK(werr
)) {
470 DEBUG(0,(__location__
": Failed to convert UDV for %s : %s\n",
471 ldb_dn_get_linearized(partition
->dn
), win_errstr(werr
)));
472 tevent_req_nterror(req
, werror_to_ntstatus(werr
));
477 if (partition
->uptodatevector_ex
.count
== 0) {
478 uptodateness_vector
= NULL
;
480 uptodateness_vector
= &partition
->uptodatevector_ex
;
483 replica_flags
= rf1
->replica_flags
;
484 highwatermark
= rf1
->highwatermark
;
486 if (state
->op
->options
& DRSUAPI_DRS_GET_ANC
) {
487 replica_flags
|= DRSUAPI_DRS_GET_ANC
;
490 if (state
->op
->options
& DRSUAPI_DRS_SYNC_FORCED
) {
491 replica_flags
|= DRSUAPI_DRS_SYNC_FORCED
;
494 if (partition
->partial_replica
) {
495 status
= dreplsrv_get_gc_partial_attribute_set(service
, r
,
498 if (!NT_STATUS_IS_OK(status
)) {
499 DEBUG(0,(__location__
": Failed to construct GC partial attribute set : %s\n", nt_errstr(status
)));
500 tevent_req_nterror(req
, status
);
503 replica_flags
&= ~DRSUAPI_DRS_WRIT_REP
;
504 } else if (partition
->rodc_replica
|| state
->op
->extended_op
== DRSUAPI_EXOP_REPL_SECRET
) {
505 bool for_schema
= false;
506 if (ldb_dn_compare_base(schema_dn
, partition
->dn
) == 0) {
509 status
= dreplsrv_get_rodc_partial_attribute_set(service
, r
,
513 if (!NT_STATUS_IS_OK(status
)) {
514 DEBUG(0,(__location__
": Failed to construct RODC partial attribute set : %s\n", nt_errstr(status
)));
515 tevent_req_nterror(req
, status
);
518 replica_flags
&= ~DRSUAPI_DRS_WRIT_REP
;
519 if (state
->op
->extended_op
== DRSUAPI_EXOP_REPL_SECRET
) {
520 replica_flags
&= ~DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING
;
522 replica_flags
|= DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING
;
529 * Client Behavior When Sending the IDL_DRSGetNCChanges Request
532 * ReplicateNCRequestMsg
534 replica_flags
|= DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP
;
536 replica_flags
|= DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP
;
539 if (state
->op
->extended_op
!= DRSUAPI_EXOP_NONE
) {
541 * If it's an exop never set the ADD_REF even if it's in
544 replica_flags
&= ~DRSUAPI_DRS_ADD_REF
;
547 /* is this a full resync of all objects? */
548 if (state
->op
->options
& DRSUAPI_DRS_FULL_SYNC_NOW
) {
549 ZERO_STRUCT(highwatermark
);
550 /* clear the FULL_SYNC_NOW option for subsequent
551 stages of the replication cycle */
552 state
->op
->options
&= ~DRSUAPI_DRS_FULL_SYNC_NOW
;
553 state
->op
->options
|= DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS
;
554 replica_flags
|= DRSUAPI_DRS_NEVER_SYNCED
;
556 if (state
->op
->options
& DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS
) {
557 uptodateness_vector
= NULL
;
560 r
->in
.bind_handle
= &drsuapi
->bind_handle
;
561 if (drsuapi
->remote_info28
.supported_extensions
& DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
) {
563 r
->in
.req
->req8
.destination_dsa_guid
= service
->ntds_guid
;
564 r
->in
.req
->req8
.source_dsa_invocation_id
= rf1
->source_dsa_invocation_id
;
565 r
->in
.req
->req8
.naming_context
= &partition
->nc
;
566 r
->in
.req
->req8
.highwatermark
= highwatermark
;
567 r
->in
.req
->req8
.uptodateness_vector
= uptodateness_vector
;
568 r
->in
.req
->req8
.replica_flags
= replica_flags
;
569 r
->in
.req
->req8
.max_object_count
= 133;
570 r
->in
.req
->req8
.max_ndr_size
= 1336811;
571 r
->in
.req
->req8
.extended_op
= state
->op
->extended_op
;
572 r
->in
.req
->req8
.fsmo_info
= state
->op
->fsmo_info
;
573 r
->in
.req
->req8
.partial_attribute_set
= pas
;
574 r
->in
.req
->req8
.partial_attribute_set_ex
= NULL
;
575 r
->in
.req
->req8
.mapping_ctr
.num_mappings
= mappings
== NULL
? 0 : mappings
->num_mappings
;
576 r
->in
.req
->req8
.mapping_ctr
.mappings
= mappings
== NULL
? NULL
: mappings
->mappings
;
579 r
->in
.req
->req5
.destination_dsa_guid
= service
->ntds_guid
;
580 r
->in
.req
->req5
.source_dsa_invocation_id
= rf1
->source_dsa_invocation_id
;
581 r
->in
.req
->req5
.naming_context
= &partition
->nc
;
582 r
->in
.req
->req5
.highwatermark
= highwatermark
;
583 r
->in
.req
->req5
.uptodateness_vector
= uptodateness_vector
;
584 r
->in
.req
->req5
.replica_flags
= replica_flags
;
585 r
->in
.req
->req5
.max_object_count
= 133;
586 r
->in
.req
->req5
.max_ndr_size
= 1336770;
587 r
->in
.req
->req5
.extended_op
= state
->op
->extended_op
;
588 r
->in
.req
->req5
.fsmo_info
= state
->op
->fsmo_info
;
592 NDR_PRINT_IN_DEBUG(drsuapi_DsGetNCChanges
, r
);
595 state
->ndr_struct_ptr
= r
;
596 subreq
= dcerpc_drsuapi_DsGetNCChanges_r_send(state
,
598 drsuapi
->drsuapi_handle
,
600 if (tevent_req_nomem(subreq
, req
)) {
603 tevent_req_set_callback(subreq
, dreplsrv_op_pull_source_get_changes_done
, req
);
606 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req
*req
,
607 struct drsuapi_DsGetNCChanges
*r
,
609 struct drsuapi_DsGetNCChangesCtr1
*ctr1
,
610 struct drsuapi_DsGetNCChangesCtr6
*ctr6
);
612 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req
*subreq
)
614 struct tevent_req
*req
= tevent_req_callback_data(subreq
,
616 struct dreplsrv_op_pull_source_state
*state
= tevent_req_data(req
,
617 struct dreplsrv_op_pull_source_state
);
619 struct drsuapi_DsGetNCChanges
*r
= talloc_get_type(state
->ndr_struct_ptr
,
620 struct drsuapi_DsGetNCChanges
);
621 uint32_t ctr_level
= 0;
622 struct drsuapi_DsGetNCChangesCtr1
*ctr1
= NULL
;
623 struct drsuapi_DsGetNCChangesCtr6
*ctr6
= NULL
;
624 enum drsuapi_DsExtendedError extended_ret
= DRSUAPI_EXOP_ERR_NONE
;
625 state
->ndr_struct_ptr
= NULL
;
627 status
= dcerpc_drsuapi_DsGetNCChanges_r_recv(subreq
, r
);
629 if (tevent_req_nterror(req
, status
)) {
633 if (!W_ERROR_IS_OK(r
->out
.result
)) {
634 status
= werror_to_ntstatus(r
->out
.result
);
635 tevent_req_nterror(req
, status
);
639 if (*r
->out
.level_out
== 1) {
641 ctr1
= &r
->out
.ctr
->ctr1
;
642 } else if (*r
->out
.level_out
== 2 &&
643 r
->out
.ctr
->ctr2
.mszip1
.ts
) {
645 ctr1
= &r
->out
.ctr
->ctr2
.mszip1
.ts
->ctr1
;
646 } else if (*r
->out
.level_out
== 6) {
648 ctr6
= &r
->out
.ctr
->ctr6
;
649 } else if (*r
->out
.level_out
== 7 &&
650 r
->out
.ctr
->ctr7
.level
== 6 &&
651 r
->out
.ctr
->ctr7
.type
== DRSUAPI_COMPRESSION_TYPE_MSZIP
&&
652 r
->out
.ctr
->ctr7
.ctr
.mszip6
.ts
) {
654 ctr6
= &r
->out
.ctr
->ctr7
.ctr
.mszip6
.ts
->ctr6
;
655 } else if (*r
->out
.level_out
== 7 &&
656 r
->out
.ctr
->ctr7
.level
== 6 &&
657 r
->out
.ctr
->ctr7
.type
== DRSUAPI_COMPRESSION_TYPE_XPRESS
&&
658 r
->out
.ctr
->ctr7
.ctr
.xpress6
.ts
) {
660 ctr6
= &r
->out
.ctr
->ctr7
.ctr
.xpress6
.ts
->ctr6
;
662 status
= werror_to_ntstatus(WERR_BAD_NET_RESP
);
663 tevent_req_nterror(req
, status
);
667 if (!ctr1
&& !ctr6
) {
668 status
= werror_to_ntstatus(WERR_BAD_NET_RESP
);
669 tevent_req_nterror(req
, status
);
673 if (ctr_level
== 6) {
674 if (!W_ERROR_IS_OK(ctr6
->drs_error
)) {
675 status
= werror_to_ntstatus(ctr6
->drs_error
);
676 tevent_req_nterror(req
, status
);
679 extended_ret
= ctr6
->extended_ret
;
682 if (ctr_level
== 1) {
683 extended_ret
= ctr1
->extended_ret
;
686 if (state
->op
->extended_op
!= DRSUAPI_EXOP_NONE
) {
687 state
->op
->extended_ret
= extended_ret
;
689 if (extended_ret
!= DRSUAPI_EXOP_ERR_SUCCESS
) {
690 status
= NT_STATUS_UNSUCCESSFUL
;
691 tevent_req_nterror(req
, status
);
696 dreplsrv_op_pull_source_apply_changes_trigger(req
, r
, ctr_level
, ctr1
, ctr6
);
699 static void dreplsrv_update_refs_trigger(struct tevent_req
*req
);
701 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req
*req
,
702 struct drsuapi_DsGetNCChanges
*r
,
704 struct drsuapi_DsGetNCChangesCtr1
*ctr1
,
705 struct drsuapi_DsGetNCChangesCtr6
*ctr6
)
707 struct dreplsrv_op_pull_source_state
*state
= tevent_req_data(req
,
708 struct dreplsrv_op_pull_source_state
);
709 struct repsFromTo1 rf1
= *state
->op
->source_dsa
->repsFrom1
;
710 struct dreplsrv_service
*service
= state
->op
->service
;
711 struct dreplsrv_partition
*partition
= state
->op
->source_dsa
->partition
;
712 struct dreplsrv_drsuapi_connection
*drsuapi
= state
->op
->source_dsa
->conn
->drsuapi
;
713 struct ldb_dn
*schema_dn
= ldb_get_schema_basedn(service
->samdb
);
714 struct dsdb_schema
*schema
;
715 struct dsdb_schema
*working_schema
= NULL
;
716 const struct drsuapi_DsReplicaOIDMapping_Ctr
*mapping_ctr
;
717 uint32_t object_count
;
718 struct drsuapi_DsReplicaObjectListItemEx
*first_object
;
719 uint32_t linked_attributes_count
;
720 struct drsuapi_DsReplicaLinkedAttribute
*linked_attributes
;
721 const struct drsuapi_DsReplicaCursor2CtrEx
*uptodateness_vector
;
722 struct dsdb_extended_replicated_objects
*objects
;
723 bool more_data
= false;
726 uint32_t dsdb_repl_flags
= 0;
727 struct ldb_dn
*nc_root
= NULL
;
732 mapping_ctr
= &ctr1
->mapping_ctr
;
733 object_count
= ctr1
->object_count
;
734 first_object
= ctr1
->first_object
;
735 linked_attributes_count
= 0;
736 linked_attributes
= NULL
;
737 rf1
.source_dsa_obj_guid
= ctr1
->source_dsa_guid
;
738 rf1
.source_dsa_invocation_id
= ctr1
->source_dsa_invocation_id
;
739 rf1
.highwatermark
= ctr1
->new_highwatermark
;
740 uptodateness_vector
= NULL
; /* TODO: map it */
741 more_data
= ctr1
->more_data
;
744 mapping_ctr
= &ctr6
->mapping_ctr
;
745 object_count
= ctr6
->object_count
;
746 first_object
= ctr6
->first_object
;
747 linked_attributes_count
= ctr6
->linked_attributes_count
;
748 linked_attributes
= ctr6
->linked_attributes
;
749 rf1
.source_dsa_obj_guid
= ctr6
->source_dsa_guid
;
750 rf1
.source_dsa_invocation_id
= ctr6
->source_dsa_invocation_id
;
751 rf1
.highwatermark
= ctr6
->new_highwatermark
;
752 uptodateness_vector
= ctr6
->uptodateness_vector
;
753 more_data
= ctr6
->more_data
;
756 nt_status
= werror_to_ntstatus(WERR_BAD_NET_RESP
);
757 tevent_req_nterror(req
, nt_status
);
761 schema
= dsdb_get_schema(service
->samdb
, state
);
763 DEBUG(0,(__location__
": Schema is not loaded yet!\n"));
764 tevent_req_nterror(req
, NT_STATUS_INTERNAL_ERROR
);
769 * Decide what working schema to use for object conversion.
770 * We won't need a working schema for empty replicas sent.
773 bool is_schema
= ldb_dn_compare(partition
->dn
, schema_dn
) == 0;
775 /* create working schema to convert objects with */
776 status
= dsdb_repl_make_working_schema(service
->samdb
,
781 &drsuapi
->gensec_skey
,
782 state
, &working_schema
);
783 if (!W_ERROR_IS_OK(status
)) {
784 DEBUG(0,("Failed to create working schema: %s\n",
785 win_errstr(status
)));
786 tevent_req_nterror(req
, NT_STATUS_INTERNAL_ERROR
);
792 if (partition
->partial_replica
|| partition
->rodc_replica
) {
793 dsdb_repl_flags
|= DSDB_REPL_FLAG_PARTIAL_REPLICA
;
795 if (state
->op
->options
& DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS
) {
796 dsdb_repl_flags
|= DSDB_REPL_FLAG_PRIORITISE_INCOMING
;
798 if (state
->op
->options
& DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING
) {
799 dsdb_repl_flags
|= DSDB_REPL_FLAG_EXPECT_NO_SECRETS
;
802 if (state
->op
->extended_op
!= DRSUAPI_EXOP_NONE
) {
803 ret
= dsdb_find_nc_root(service
->samdb
, partition
,
804 partition
->dn
, &nc_root
);
805 if (ret
!= LDB_SUCCESS
) {
806 DEBUG(0,(__location__
": Failed to find nc_root for %s\n",
807 ldb_dn_get_linearized(partition
->dn
)));
808 tevent_req_nterror(req
, NT_STATUS_INTERNAL_ERROR
);
812 nc_root
= partition
->dn
;
815 status
= dsdb_replicated_objects_convert(service
->samdb
,
816 working_schema
? working_schema
: schema
,
821 linked_attributes_count
,
825 &drsuapi
->gensec_skey
,
829 if (W_ERROR_EQUAL(status
, WERR_DS_DRA_SCHEMA_MISMATCH
)) {
830 struct dreplsrv_partition
*p
;
832 if (state
->retry_started
) {
833 nt_status
= werror_to_ntstatus(WERR_BAD_NET_RESP
);
834 DEBUG(0,("Failed to convert objects after retry: %s/%s\n",
835 win_errstr(status
), nt_errstr(nt_status
)));
836 tevent_req_nterror(req
, nt_status
);
841 * Change info sync or extended operation into a fetch
842 * of the schema partition, so we get all the schema
845 * We don't want to re-do the remote exop,
846 * unless it was REPL_SECRET so we set the
847 * fallback operation to just be a fetch of
848 * the relevent partition.
852 if (state
->op
->extended_op
== DRSUAPI_EXOP_REPL_SECRET
) {
853 state
->extended_op_retry
= state
->op
->extended_op
;
855 state
->extended_op_retry
= DRSUAPI_EXOP_NONE
;
857 state
->op
->extended_op
= DRSUAPI_EXOP_NONE
;
859 if (ldb_dn_compare(nc_root
, partition
->dn
) == 0) {
860 state
->source_dsa_retry
= state
->op
->source_dsa
;
862 status
= dreplsrv_partition_find_for_nc(service
,
864 ldb_dn_get_linearized(nc_root
),
866 if (!W_ERROR_IS_OK(status
)) {
867 DEBUG(2, ("Failed to find requested Naming Context for %s: %s",
868 ldb_dn_get_linearized(nc_root
),
869 win_errstr(status
)));
870 nt_status
= werror_to_ntstatus(status
);
871 tevent_req_nterror(req
, nt_status
);
874 status
= dreplsrv_partition_source_dsa_by_guid(p
,
875 &state
->op
->source_dsa
->repsFrom1
->source_dsa_obj_guid
,
876 &state
->source_dsa_retry
);
878 if (!W_ERROR_IS_OK(status
)) {
879 struct GUID_txt_buf str
;
880 DEBUG(2, ("Failed to find requested source DSA for %s and %s: %s",
881 ldb_dn_get_linearized(nc_root
),
882 GUID_buf_string(&state
->op
->source_dsa
->repsFrom1
->source_dsa_obj_guid
, &str
),
883 win_errstr(status
)));
884 nt_status
= werror_to_ntstatus(status
);
885 tevent_req_nterror(req
, nt_status
);
890 /* Find schema naming context to be synchronized first */
891 status
= dreplsrv_partition_find_for_nc(service
,
893 ldb_dn_get_linearized(schema_dn
),
895 if (!W_ERROR_IS_OK(status
)) {
896 DEBUG(2, ("Failed to find requested Naming Context for schema: %s",
897 win_errstr(status
)));
898 nt_status
= werror_to_ntstatus(status
);
899 tevent_req_nterror(req
, nt_status
);
903 status
= dreplsrv_partition_source_dsa_by_guid(p
,
904 &state
->op
->source_dsa
->repsFrom1
->source_dsa_obj_guid
,
905 &state
->op
->source_dsa
);
906 if (!W_ERROR_IS_OK(status
)) {
907 struct GUID_txt_buf str
;
908 DEBUG(2, ("Failed to find requested source DSA for %s and %s: %s",
909 ldb_dn_get_linearized(schema_dn
),
910 GUID_buf_string(&state
->op
->source_dsa
->repsFrom1
->source_dsa_obj_guid
, &str
),
911 win_errstr(status
)));
912 nt_status
= werror_to_ntstatus(status
);
913 tevent_req_nterror(req
, nt_status
);
916 DEBUG(4,("Wrong schema when applying reply GetNCChanges, retrying\n"));
918 state
->retry_started
= true;
919 dreplsrv_op_pull_source_get_changes_trigger(req
);
922 } else if (!W_ERROR_IS_OK(status
)) {
923 nt_status
= werror_to_ntstatus(WERR_BAD_NET_RESP
);
924 DEBUG(0,("Failed to convert objects: %s/%s\n",
925 win_errstr(status
), nt_errstr(nt_status
)));
926 tevent_req_nterror(req
, nt_status
);
930 status
= dsdb_replicated_objects_commit(service
->samdb
,
933 &state
->op
->source_dsa
->notify_uSN
);
934 talloc_free(objects
);
936 if (!W_ERROR_IS_OK(status
)) {
939 * If we failed to apply the records due to a missing
940 * parent, try again after asking for the parent
941 * records first. Because we don't update the
942 * highwatermark, we start this part of the cycle
945 if (((state
->op
->options
& DRSUAPI_DRS_GET_ANC
) == 0)
946 && W_ERROR_EQUAL(status
, WERR_DS_DRA_MISSING_PARENT
)) {
947 state
->op
->options
|= DRSUAPI_DRS_GET_ANC
;
948 DEBUG(4,("Missing parent object when we didn't set the DRSUAPI_DRS_GET_ANC flag, retrying\n"));
949 dreplsrv_op_pull_source_get_changes_trigger(req
);
951 } else if (((state
->op
->options
& DRSUAPI_DRS_GET_ANC
))
952 && W_ERROR_EQUAL(status
, WERR_DS_DRA_MISSING_PARENT
)) {
953 DEBUG(1,("Missing parent object despite setting DRSUAPI_DRS_GET_ANC flag\n"));
954 nt_status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
956 nt_status
= werror_to_ntstatus(WERR_BAD_NET_RESP
);
958 DEBUG(0,("Failed to commit objects: %s/%s\n",
959 win_errstr(status
), nt_errstr(nt_status
)));
960 tevent_req_nterror(req
, nt_status
);
964 if (state
->op
->extended_op
== DRSUAPI_EXOP_NONE
) {
965 /* if it applied fine, we need to update the highwatermark */
966 *state
->op
->source_dsa
->repsFrom1
= rf1
;
969 /* we don't need this maybe very large structure anymore */
973 dreplsrv_op_pull_source_get_changes_trigger(req
);
978 * If we had to divert via doing some other thing, such as
979 * pulling the schema, then go back and do the original
980 * operation once we are done.
982 if (state
->source_dsa_retry
!= NULL
) {
983 state
->op
->source_dsa
= state
->source_dsa_retry
;
984 state
->op
->extended_op
= state
->extended_op_retry
;
985 state
->source_dsa_retry
= NULL
;
986 dreplsrv_op_pull_source_get_changes_trigger(req
);
990 if (state
->op
->extended_op
!= DRSUAPI_EXOP_NONE
||
991 state
->op
->service
->am_rodc
) {
993 we don't do the UpdateRefs for extended ops or if we
996 tevent_req_done(req
);
1000 /* now we need to update the repsTo record for this partition
1001 on the server. These records are initially established when
1002 we join the domain, but they quickly expire. We do it here
1003 so we can use the already established DRSUAPI pipe
1005 dreplsrv_update_refs_trigger(req
);
1008 static void dreplsrv_update_refs_done(struct tevent_req
*subreq
);
1011 send a UpdateRefs request to refresh our repsTo record on the server
1013 static void dreplsrv_update_refs_trigger(struct tevent_req
*req
)
1015 struct dreplsrv_op_pull_source_state
*state
= tevent_req_data(req
,
1016 struct dreplsrv_op_pull_source_state
);
1017 struct dreplsrv_service
*service
= state
->op
->service
;
1018 struct dreplsrv_partition
*partition
= state
->op
->source_dsa
->partition
;
1019 struct dreplsrv_drsuapi_connection
*drsuapi
= state
->op
->source_dsa
->conn
->drsuapi
;
1020 struct drsuapi_DsReplicaUpdateRefs
*r
;
1021 char *ntds_dns_name
;
1022 struct tevent_req
*subreq
;
1024 r
= talloc(state
, struct drsuapi_DsReplicaUpdateRefs
);
1025 if (tevent_req_nomem(r
, req
)) {
1029 ntds_dns_name
= samdb_ntds_msdcs_dns_name(service
->samdb
, r
, &service
->ntds_guid
);
1030 if (tevent_req_nomem(ntds_dns_name
, req
)) {
1035 r
->in
.bind_handle
= &drsuapi
->bind_handle
;
1037 r
->in
.req
.req1
.naming_context
= &partition
->nc
;
1038 r
->in
.req
.req1
.dest_dsa_dns_name
= ntds_dns_name
;
1039 r
->in
.req
.req1
.dest_dsa_guid
= service
->ntds_guid
;
1040 r
->in
.req
.req1
.options
= DRSUAPI_DRS_ADD_REF
| DRSUAPI_DRS_DEL_REF
;
1041 if (!service
->am_rodc
) {
1042 r
->in
.req
.req1
.options
|= DRSUAPI_DRS_WRIT_REP
;
1045 state
->ndr_struct_ptr
= r
;
1046 subreq
= dcerpc_drsuapi_DsReplicaUpdateRefs_r_send(state
,
1048 drsuapi
->drsuapi_handle
,
1050 if (tevent_req_nomem(subreq
, req
)) {
1054 tevent_req_set_callback(subreq
, dreplsrv_update_refs_done
, req
);
1058 receive a UpdateRefs reply
1060 static void dreplsrv_update_refs_done(struct tevent_req
*subreq
)
1062 struct tevent_req
*req
= tevent_req_callback_data(subreq
,
1064 struct dreplsrv_op_pull_source_state
*state
= tevent_req_data(req
,
1065 struct dreplsrv_op_pull_source_state
);
1066 struct drsuapi_DsReplicaUpdateRefs
*r
= talloc_get_type(state
->ndr_struct_ptr
,
1067 struct drsuapi_DsReplicaUpdateRefs
);
1070 state
->ndr_struct_ptr
= NULL
;
1072 status
= dcerpc_drsuapi_DsReplicaUpdateRefs_r_recv(subreq
, r
);
1073 TALLOC_FREE(subreq
);
1074 if (!NT_STATUS_IS_OK(status
)) {
1075 DEBUG(0,("UpdateRefs failed with %s\n",
1076 nt_errstr(status
)));
1077 tevent_req_nterror(req
, status
);
1081 if (!W_ERROR_IS_OK(r
->out
.result
)) {
1082 status
= werror_to_ntstatus(r
->out
.result
);
1083 DEBUG(0,("UpdateRefs failed with %s/%s for %s %s\n",
1084 win_errstr(r
->out
.result
),
1086 r
->in
.req
.req1
.dest_dsa_dns_name
,
1087 r
->in
.req
.req1
.naming_context
->dn
));
1089 * TODO we are currently not sending the
1090 * DsReplicaUpdateRefs at the correct moment,
1091 * we do it just after a GetNcChanges which is
1092 * not always correct.
1093 * Especially when another DC is trying to demote
1094 * it will sends us a DsReplicaSync that will trigger a getNcChanges
1095 * this call will succeed but the DsRecplicaUpdateRefs that we send
1096 * just after will not because the DC is in a demote state and
1097 * will reply us a WERR_DS_DRA_BUSY, this error will cause us to
1098 * answer to the DsReplicaSync with a non OK status, the other DC
1099 * will stop the demote due to this error.
1100 * In order to cope with this we will for the moment concider
1101 * a DS_DRA_BUSY not as an error.
1102 * It's not ideal but it should not have a too huge impact for
1103 * running production as this error otherwise never happen and
1104 * due to the fact the send a DsReplicaUpdateRefs after each getNcChanges
1106 if (!W_ERROR_EQUAL(r
->out
.result
, WERR_DS_DRA_BUSY
)) {
1107 tevent_req_nterror(req
, status
);
1112 DEBUG(4,("UpdateRefs OK for %s %s\n",
1113 r
->in
.req
.req1
.dest_dsa_dns_name
,
1114 r
->in
.req
.req1
.naming_context
->dn
));
1116 tevent_req_done(req
);
1119 WERROR
dreplsrv_op_pull_source_recv(struct tevent_req
*req
)
1123 if (tevent_req_is_nterror(req
, &status
)) {
1124 tevent_req_received(req
);
1125 return ntstatus_to_werror(status
);
1128 tevent_req_received(req
);