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_schema_cycle {
244 struct repsFromTo1 repsFrom1;
246 struct drsuapi_DsReplicaObjectListItemEx *first_object;
247 struct drsuapi_DsReplicaObjectListItemEx *last_object;
248 uint32_t linked_attributes_count;
249 struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
252 struct dreplsrv_op_pull_source_state {
253 struct tevent_context *ev;
254 struct dreplsrv_out_operation *op;
255 void *ndr_struct_ptr;
257 * Used when we have to re-try with a different NC, eg for
258 * EXOP retry or to get a current schema first
260 struct dreplsrv_partition_source_dsa *source_dsa_retry;
261 enum drsuapi_DsExtendedOperation extended_op_retry;
263 struct dreplsrv_op_pull_source_schema_cycle *schema_cycle;
266 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq);
268 struct tevent_req *dreplsrv_op_pull_source_send(TALLOC_CTX *mem_ctx,
269 struct tevent_context *ev,
270 struct dreplsrv_out_operation *op)
272 struct tevent_req *req;
273 struct dreplsrv_op_pull_source_state *state;
274 struct tevent_req *subreq;
276 req = tevent_req_create(mem_ctx, &state,
277 struct dreplsrv_op_pull_source_state);
284 subreq = dreplsrv_out_drsuapi_send(state, ev, op->source_dsa->conn);
285 if (tevent_req_nomem(subreq, req)) {
286 return tevent_req_post(req, ev);
288 tevent_req_set_callback(subreq, dreplsrv_op_pull_source_connect_done, req);
293 static bool dreplsrv_op_pull_source_detect_schema_cycle(struct tevent_req *req)
295 struct dreplsrv_op_pull_source_state *state =
297 struct dreplsrv_op_pull_source_state);
298 bool is_schema = false;
300 if (state->op->extended_op == DRSUAPI_EXOP_NONE) {
301 struct dreplsrv_out_operation *op = state->op;
302 struct dreplsrv_service *service = op->service;
303 struct ldb_dn *schema_dn = ldb_get_schema_basedn(service->samdb);
304 struct dreplsrv_partition *partition = op->source_dsa->partition;
306 is_schema = ldb_dn_compare(partition->dn, schema_dn) == 0;
310 struct dreplsrv_op_pull_source_schema_cycle *sc;
312 sc = talloc_zero(state,
313 struct dreplsrv_op_pull_source_schema_cycle);
314 if (tevent_req_nomem(sc, req)) {
317 sc->repsFrom1 = *state->op->source_dsa->repsFrom1;
319 state->schema_cycle = sc;
325 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req);
327 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq)
329 struct tevent_req *req = tevent_req_callback_data(subreq,
334 status = dreplsrv_out_drsuapi_recv(subreq);
336 if (tevent_req_nterror(req, status)) {
340 ok = dreplsrv_op_pull_source_detect_schema_cycle(req);
345 dreplsrv_op_pull_source_get_changes_trigger(req);
348 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq);
351 get a RODC partial attribute set for a replication call
353 static NTSTATUS dreplsrv_get_rodc_partial_attribute_set(struct dreplsrv_service *service,
355 struct drsuapi_DsPartialAttributeSet **_pas,
356 struct drsuapi_DsReplicaOIDMapping_Ctr **pfm,
359 struct drsuapi_DsPartialAttributeSet *pas;
360 struct dsdb_schema *schema;
363 pas = talloc_zero(mem_ctx, struct drsuapi_DsPartialAttributeSet);
364 NT_STATUS_HAVE_NO_MEMORY(pas);
366 schema = dsdb_get_schema(service->samdb, NULL);
369 pas->attids = talloc_array(pas, enum drsuapi_DsAttributeId, schema->num_attributes);
370 if (pas->attids == NULL) {
372 return NT_STATUS_NO_MEMORY;
375 for (i=0; i<schema->num_attributes; i++) {
376 struct dsdb_attribute *a;
377 a = schema->attributes_by_attributeID_id[i];
378 if (a->systemFlags & (DS_FLAG_ATTR_NOT_REPLICATED | DS_FLAG_ATTR_IS_CONSTRUCTED)) {
381 if (a->searchFlags & SEARCH_FLAG_RODC_ATTRIBUTE) {
384 pas->attids[pas->num_attids] = dsdb_attribute_get_attid(a, for_schema);
388 pas->attids = talloc_realloc(pas, pas->attids, enum drsuapi_DsAttributeId, pas->num_attids);
389 if (pas->attids == NULL) {
391 return NT_STATUS_NO_MEMORY;
397 dsdb_get_oid_mappings_drsuapi(schema, true, mem_ctx, pfm);
405 get a GC partial attribute set for a replication call
407 static NTSTATUS dreplsrv_get_gc_partial_attribute_set(struct dreplsrv_service *service,
409 struct drsuapi_DsPartialAttributeSet **_pas,
410 struct drsuapi_DsReplicaOIDMapping_Ctr **pfm)
412 struct drsuapi_DsPartialAttributeSet *pas;
413 struct dsdb_schema *schema;
416 pas = talloc_zero(mem_ctx, struct drsuapi_DsPartialAttributeSet);
417 NT_STATUS_HAVE_NO_MEMORY(pas);
419 schema = dsdb_get_schema(service->samdb, NULL);
422 pas->attids = talloc_array(pas, enum drsuapi_DsAttributeId, schema->num_attributes);
423 if (pas->attids == NULL) {
425 return NT_STATUS_NO_MEMORY;
428 for (i=0; i<schema->num_attributes; i++) {
429 struct dsdb_attribute *a;
430 a = schema->attributes_by_attributeID_id[i];
431 if (a->isMemberOfPartialAttributeSet) {
432 pas->attids[pas->num_attids] = dsdb_attribute_get_attid(a, false);
437 pas->attids = talloc_realloc(pas, pas->attids, enum drsuapi_DsAttributeId, pas->num_attids);
438 if (pas->attids == NULL) {
440 return NT_STATUS_NO_MEMORY;
446 dsdb_get_oid_mappings_drsuapi(schema, true, mem_ctx, pfm);
453 convert from one udv format to the other
455 static WERROR udv_convert(TALLOC_CTX *mem_ctx,
456 const struct replUpToDateVectorCtr2 *udv,
457 struct drsuapi_DsReplicaCursorCtrEx *udv_ex)
462 udv_ex->reserved1 = 0;
463 udv_ex->reserved2 = 0;
464 udv_ex->count = udv->count;
465 udv_ex->cursors = talloc_array(mem_ctx, struct drsuapi_DsReplicaCursor, udv->count);
466 W_ERROR_HAVE_NO_MEMORY(udv_ex->cursors);
468 for (i=0; i<udv->count; i++) {
469 udv_ex->cursors[i].source_dsa_invocation_id = udv->cursors[i].source_dsa_invocation_id;
470 udv_ex->cursors[i].highest_usn = udv->cursors[i].highest_usn;
477 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req)
479 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
480 struct dreplsrv_op_pull_source_state);
481 const struct repsFromTo1 *rf1 = state->op->source_dsa->repsFrom1;
482 struct dreplsrv_service *service = state->op->service;
483 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
484 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
485 struct drsuapi_DsGetNCChanges *r;
486 struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector;
487 struct tevent_req *subreq;
488 struct drsuapi_DsPartialAttributeSet *pas = NULL;
490 uint32_t replica_flags;
491 struct drsuapi_DsReplicaHighWaterMark highwatermark;
492 struct drsuapi_DsReplicaOIDMapping_Ctr *mappings = NULL;
493 bool is_schema = false;
495 if (state->schema_cycle != NULL) {
497 rf1 = &state->schema_cycle->repsFrom1;
500 r = talloc(state, struct drsuapi_DsGetNCChanges);
501 if (tevent_req_nomem(r, req)) {
505 r->out.level_out = talloc(r, uint32_t);
506 if (tevent_req_nomem(r->out.level_out, req)) {
509 r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest);
510 if (tevent_req_nomem(r->in.req, req)) {
513 r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr);
514 if (tevent_req_nomem(r->out.ctr, req)) {
518 if (partition->uptodatevector.count != 0 &&
519 partition->uptodatevector_ex.count == 0) {
521 werr = udv_convert(partition, &partition->uptodatevector, &partition->uptodatevector_ex);
522 if (!W_ERROR_IS_OK(werr)) {
523 DEBUG(0,(__location__ ": Failed to convert UDV for %s : %s\n",
524 ldb_dn_get_linearized(partition->dn), win_errstr(werr)));
525 tevent_req_nterror(req, werror_to_ntstatus(werr));
530 if (partition->uptodatevector_ex.count == 0) {
531 uptodateness_vector = NULL;
533 uptodateness_vector = &partition->uptodatevector_ex;
536 replica_flags = rf1->replica_flags;
537 highwatermark = rf1->highwatermark;
539 if (state->op->options & DRSUAPI_DRS_GET_ANC) {
540 replica_flags |= DRSUAPI_DRS_GET_ANC;
543 if (state->op->options & DRSUAPI_DRS_SYNC_FORCED) {
544 replica_flags |= DRSUAPI_DRS_SYNC_FORCED;
547 if (partition->partial_replica) {
548 status = dreplsrv_get_gc_partial_attribute_set(service, r,
551 if (!NT_STATUS_IS_OK(status)) {
552 DEBUG(0,(__location__ ": Failed to construct GC partial attribute set : %s\n", nt_errstr(status)));
553 tevent_req_nterror(req, status);
556 replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
557 } else if (partition->rodc_replica || state->op->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
558 status = dreplsrv_get_rodc_partial_attribute_set(service, r,
562 if (!NT_STATUS_IS_OK(status)) {
563 DEBUG(0,(__location__ ": Failed to construct RODC partial attribute set : %s\n", nt_errstr(status)));
564 tevent_req_nterror(req, status);
567 replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
568 if (state->op->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
569 replica_flags &= ~DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING;
571 replica_flags |= DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING;
578 * Client Behavior When Sending the IDL_DRSGetNCChanges Request
581 * ReplicateNCRequestMsg
583 replica_flags |= DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP;
585 replica_flags |= DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP;
588 if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
590 * If it's an exop never set the ADD_REF even if it's in
593 replica_flags &= ~DRSUAPI_DRS_ADD_REF;
596 /* is this a full resync of all objects? */
597 if (state->op->options & DRSUAPI_DRS_FULL_SYNC_NOW) {
598 ZERO_STRUCT(highwatermark);
599 /* clear the FULL_SYNC_NOW option for subsequent
600 stages of the replication cycle */
601 state->op->options &= ~DRSUAPI_DRS_FULL_SYNC_NOW;
602 state->op->options |= DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS;
603 replica_flags |= DRSUAPI_DRS_NEVER_SYNCED;
605 if (state->op->options & DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS) {
606 uptodateness_vector = NULL;
609 r->in.bind_handle = &drsuapi->bind_handle;
611 if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V10) {
613 r->in.req->req10.destination_dsa_guid = service->ntds_guid;
614 r->in.req->req10.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
615 r->in.req->req10.naming_context = &partition->nc;
616 r->in.req->req10.highwatermark = highwatermark;
617 r->in.req->req10.uptodateness_vector = uptodateness_vector;
618 r->in.req->req10.replica_flags = replica_flags;
619 r->in.req->req10.max_object_count = 133;
620 r->in.req->req10.max_ndr_size = 1336811;
621 r->in.req->req10.extended_op = state->op->extended_op;
622 r->in.req->req10.fsmo_info = state->op->fsmo_info;
623 r->in.req->req10.partial_attribute_set = pas;
624 r->in.req->req10.partial_attribute_set_ex= NULL;
625 r->in.req->req10.mapping_ctr.num_mappings= mappings == NULL ? 0 : mappings->num_mappings;
626 r->in.req->req10.mapping_ctr.mappings = mappings == NULL ? NULL : mappings->mappings;
628 /* the only difference to v8 is the more_flags */
629 r->in.req->req10.more_flags = state->op->more_flags;
631 } else if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
633 r->in.req->req8.destination_dsa_guid = service->ntds_guid;
634 r->in.req->req8.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
635 r->in.req->req8.naming_context = &partition->nc;
636 r->in.req->req8.highwatermark = highwatermark;
637 r->in.req->req8.uptodateness_vector = uptodateness_vector;
638 r->in.req->req8.replica_flags = replica_flags;
639 r->in.req->req8.max_object_count = 133;
640 r->in.req->req8.max_ndr_size = 1336811;
641 r->in.req->req8.extended_op = state->op->extended_op;
642 r->in.req->req8.fsmo_info = state->op->fsmo_info;
643 r->in.req->req8.partial_attribute_set = pas;
644 r->in.req->req8.partial_attribute_set_ex= NULL;
645 r->in.req->req8.mapping_ctr.num_mappings= mappings == NULL ? 0 : mappings->num_mappings;
646 r->in.req->req8.mapping_ctr.mappings = mappings == NULL ? NULL : mappings->mappings;
649 r->in.req->req5.destination_dsa_guid = service->ntds_guid;
650 r->in.req->req5.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
651 r->in.req->req5.naming_context = &partition->nc;
652 r->in.req->req5.highwatermark = highwatermark;
653 r->in.req->req5.uptodateness_vector = uptodateness_vector;
654 r->in.req->req5.replica_flags = replica_flags;
655 r->in.req->req5.max_object_count = 133;
656 r->in.req->req5.max_ndr_size = 1336770;
657 r->in.req->req5.extended_op = state->op->extended_op;
658 r->in.req->req5.fsmo_info = state->op->fsmo_info;
662 NDR_PRINT_IN_DEBUG(drsuapi_DsGetNCChanges, r);
665 state->ndr_struct_ptr = r;
666 subreq = dcerpc_drsuapi_DsGetNCChanges_r_send(state,
668 drsuapi->drsuapi_handle,
670 if (tevent_req_nomem(subreq, req)) {
673 tevent_req_set_callback(subreq, dreplsrv_op_pull_source_get_changes_done, req);
676 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
677 struct drsuapi_DsGetNCChanges *r,
679 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
680 struct drsuapi_DsGetNCChangesCtr6 *ctr6);
682 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq)
684 struct tevent_req *req = tevent_req_callback_data(subreq,
686 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
687 struct dreplsrv_op_pull_source_state);
689 struct drsuapi_DsGetNCChanges *r = talloc_get_type(state->ndr_struct_ptr,
690 struct drsuapi_DsGetNCChanges);
691 uint32_t ctr_level = 0;
692 struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
693 struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
694 enum drsuapi_DsExtendedError extended_ret = DRSUAPI_EXOP_ERR_NONE;
695 state->ndr_struct_ptr = NULL;
697 status = dcerpc_drsuapi_DsGetNCChanges_r_recv(subreq, r);
699 if (tevent_req_nterror(req, status)) {
703 if (!W_ERROR_IS_OK(r->out.result)) {
704 status = werror_to_ntstatus(r->out.result);
705 tevent_req_nterror(req, status);
709 if (*r->out.level_out == 1) {
711 ctr1 = &r->out.ctr->ctr1;
712 } else if (*r->out.level_out == 2 &&
713 r->out.ctr->ctr2.mszip1.ts) {
715 ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
716 } else if (*r->out.level_out == 6) {
718 ctr6 = &r->out.ctr->ctr6;
719 } else if (*r->out.level_out == 7 &&
720 r->out.ctr->ctr7.level == 6 &&
721 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP &&
722 r->out.ctr->ctr7.ctr.mszip6.ts) {
724 ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
725 } else if (*r->out.level_out == 7 &&
726 r->out.ctr->ctr7.level == 6 &&
727 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
728 r->out.ctr->ctr7.ctr.xpress6.ts) {
730 ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
732 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
733 tevent_req_nterror(req, status);
737 if (!ctr1 && !ctr6) {
738 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
739 tevent_req_nterror(req, status);
743 if (ctr_level == 6) {
744 if (!W_ERROR_IS_OK(ctr6->drs_error)) {
745 status = werror_to_ntstatus(ctr6->drs_error);
746 tevent_req_nterror(req, status);
749 extended_ret = ctr6->extended_ret;
752 if (ctr_level == 1) {
753 extended_ret = ctr1->extended_ret;
756 if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
757 state->op->extended_ret = extended_ret;
759 if (extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
760 status = NT_STATUS_UNSUCCESSFUL;
761 tevent_req_nterror(req, status);
766 dreplsrv_op_pull_source_apply_changes_trigger(req, r, ctr_level, ctr1, ctr6);
770 * If processing a chunk of replication data fails, check if it is due to a
771 * problem that can be fixed by setting extra flags in the GetNCChanges request,
772 * i.e. GET_ANC or GET_TGT.
773 * @returns NT_STATUS_OK if the request was retried, and an error code if not
775 static NTSTATUS dreplsrv_op_pull_retry_with_flags(struct tevent_req *req,
778 struct dreplsrv_op_pull_source_state *state;
779 NTSTATUS nt_status = NT_STATUS_OK;
781 state = tevent_req_data(req, struct dreplsrv_op_pull_source_state);
784 * Check if we failed to apply the records due to a missing parent or
785 * target object. If so, try again and ask for any mising parent/target
786 * objects to be included this time.
788 if (W_ERROR_EQUAL(error_code, WERR_DS_DRA_RECYCLED_TARGET)) {
790 if (state->op->more_flags & DRSUAPI_DRS_GET_TGT) {
791 DEBUG(1,("Missing target object despite setting DRSUAPI_DRS_GET_TGT flag\n"));
792 nt_status = NT_STATUS_INVALID_NETWORK_RESPONSE;
794 state->op->more_flags |= DRSUAPI_DRS_GET_TGT;
795 DEBUG(1,("Missing target object when we didn't set the DRSUAPI_DRS_GET_TGT flag, retrying\n"));
796 dreplsrv_op_pull_source_get_changes_trigger(req);
798 } else if (W_ERROR_EQUAL(error_code, WERR_DS_DRA_MISSING_PARENT)) {
800 if (state->op->options & DRSUAPI_DRS_GET_ANC) {
801 DEBUG(1,("Missing parent object despite setting DRSUAPI_DRS_GET_ANC flag\n"));
802 nt_status = NT_STATUS_INVALID_NETWORK_RESPONSE;
804 state->op->options |= DRSUAPI_DRS_GET_ANC;
805 DEBUG(4,("Missing parent object when we didn't set the DRSUAPI_DRS_GET_ANC flag, retrying\n"));
806 dreplsrv_op_pull_source_get_changes_trigger(req);
809 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
816 static void dreplsrv_update_refs_trigger(struct tevent_req *req);
818 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
819 struct drsuapi_DsGetNCChanges *r,
821 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
822 struct drsuapi_DsGetNCChangesCtr6 *ctr6)
824 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
825 struct dreplsrv_op_pull_source_state);
826 struct repsFromTo1 rf1 = *state->op->source_dsa->repsFrom1;
827 struct dreplsrv_service *service = state->op->service;
828 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
829 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
830 struct ldb_dn *schema_dn = ldb_get_schema_basedn(service->samdb);
831 struct dreplsrv_op_pull_source_schema_cycle *sc = NULL;
832 struct dsdb_schema *schema;
833 struct dsdb_schema *working_schema = NULL;
834 const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
835 uint32_t object_count;
836 struct drsuapi_DsReplicaObjectListItemEx *first_object;
837 uint32_t linked_attributes_count;
838 struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
839 const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
840 struct dsdb_extended_replicated_objects *objects;
841 bool more_data = false;
844 uint32_t dsdb_repl_flags = 0;
845 struct ldb_dn *nc_root = NULL;
846 bool was_schema = false;
851 mapping_ctr = &ctr1->mapping_ctr;
852 object_count = ctr1->object_count;
853 first_object = ctr1->first_object;
854 linked_attributes_count = 0;
855 linked_attributes = NULL;
856 rf1.source_dsa_obj_guid = ctr1->source_dsa_guid;
857 rf1.source_dsa_invocation_id = ctr1->source_dsa_invocation_id;
858 rf1.highwatermark = ctr1->new_highwatermark;
859 uptodateness_vector = NULL; /* TODO: map it */
860 more_data = ctr1->more_data;
863 mapping_ctr = &ctr6->mapping_ctr;
864 object_count = ctr6->object_count;
865 first_object = ctr6->first_object;
866 linked_attributes_count = ctr6->linked_attributes_count;
867 linked_attributes = ctr6->linked_attributes;
868 rf1.source_dsa_obj_guid = ctr6->source_dsa_guid;
869 rf1.source_dsa_invocation_id = ctr6->source_dsa_invocation_id;
870 rf1.highwatermark = ctr6->new_highwatermark;
871 uptodateness_vector = ctr6->uptodateness_vector;
872 more_data = ctr6->more_data;
875 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
876 tevent_req_nterror(req, nt_status);
881 * We need to cache the schema changes until we replicated
882 * everything before we can apply the new schema.
884 if (state->schema_cycle != NULL) {
885 TALLOC_CTX *mem = NULL;
886 struct drsuapi_DsReplicaObjectListItemEx **ptr = NULL;
887 struct drsuapi_DsReplicaObjectListItemEx *l = NULL;
890 sc = state->schema_cycle;
894 if (sc->first_object == NULL) {
896 ptr = &sc->first_object;
898 mem = sc->last_object;
899 ptr = &sc->last_object->next_object;
901 *ptr = talloc_move(mem, &first_object);
902 for (l = *ptr; l != NULL; l = l->next_object) {
904 if (l->next_object == NULL) {
910 if (sc->linked_attributes_count == 0) {
911 sc->linked_attributes = talloc_move(sc, &linked_attributes);
912 sc->linked_attributes_count = linked_attributes_count;
913 linked_attributes_count = 0;
914 } else if (linked_attributes_count > 0) {
915 struct drsuapi_DsReplicaLinkedAttribute *new_las = NULL;
916 struct drsuapi_DsReplicaLinkedAttribute *tmp_las = NULL;
921 new_count = sc->linked_attributes_count;
922 new_count += linked_attributes_count;
923 if (new_count > UINT32_MAX) {
924 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
925 tevent_req_nterror(req, nt_status);
928 add_size = linked_attributes_count;
929 add_size *= sizeof(linked_attributes[0]);
930 if (add_size > SIZE_MAX) {
931 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
932 tevent_req_nterror(req, nt_status);
935 add_idx = sc->linked_attributes_count;
937 tmp_las = talloc_realloc(sc,
938 sc->linked_attributes,
939 struct drsuapi_DsReplicaLinkedAttribute,
941 if (tevent_req_nomem(tmp_las, req)) {
944 new_las = talloc_move(tmp_las, &linked_attributes);
945 memcpy(&tmp_las[add_idx], new_las, add_size);
946 sc->linked_attributes = tmp_las;
947 sc->linked_attributes_count = new_count;
948 linked_attributes_count = 0;
952 /* we don't need this structure anymore */
955 dreplsrv_op_pull_source_get_changes_trigger(req);
959 /* detach sc from state */
960 state->schema_cycle = NULL;
963 schema = dsdb_get_schema(service->samdb, state);
965 DEBUG(0,(__location__ ": Schema is not loaded yet!\n"));
966 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
971 * Decide what working schema to use for object conversion.
972 * We won't need a working schema for empty replicas sent.
975 first_object = talloc_move(r, &sc->first_object);
976 object_count = sc->object_count;
977 linked_attributes = talloc_move(r, &sc->linked_attributes);
978 linked_attributes_count = sc->linked_attributes_count;
981 if (first_object != NULL) {
982 /* create working schema to convert objects with */
983 status = dsdb_repl_make_working_schema(service->samdb,
988 &drsuapi->gensec_skey,
989 state, &working_schema);
990 if (!W_ERROR_IS_OK(status)) {
991 DEBUG(0,("Failed to create working schema: %s\n",
992 win_errstr(status)));
993 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
999 if (partition->partial_replica || partition->rodc_replica) {
1000 dsdb_repl_flags |= DSDB_REPL_FLAG_PARTIAL_REPLICA;
1002 if (state->op->options & DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS) {
1003 dsdb_repl_flags |= DSDB_REPL_FLAG_PRIORITISE_INCOMING;
1005 if (state->op->options & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
1006 dsdb_repl_flags |= DSDB_REPL_FLAG_EXPECT_NO_SECRETS;
1008 if (state->op->options & DRSUAPI_DRS_CRITICAL_ONLY ||
1009 state->op->extended_op != DRSUAPI_EXOP_NONE) {
1010 dsdb_repl_flags |= DSDB_REPL_FLAG_OBJECT_SUBSET;
1013 if (state->op->more_flags & DRSUAPI_DRS_GET_TGT) {
1014 dsdb_repl_flags |= DSDB_REPL_FLAG_TARGETS_UPTODATE;
1017 if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
1018 ret = dsdb_find_nc_root(service->samdb, partition,
1019 partition->dn, &nc_root);
1020 if (ret != LDB_SUCCESS) {
1021 DEBUG(0,(__location__ ": Failed to find nc_root for %s\n",
1022 ldb_dn_get_linearized(partition->dn)));
1023 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
1027 nc_root = partition->dn;
1030 status = dsdb_replicated_objects_convert(service->samdb,
1031 working_schema ? working_schema : schema,
1036 linked_attributes_count,
1039 uptodateness_vector,
1040 &drsuapi->gensec_skey,
1044 if (W_ERROR_EQUAL(status, WERR_DS_DRA_SCHEMA_MISMATCH)) {
1045 struct dreplsrv_partition *p;
1049 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
1050 DBG_ERR("Got mismatch for schema partition: %s/%s\n",
1051 win_errstr(status), nt_errstr(nt_status));
1052 tevent_req_nterror(req, nt_status);
1056 if (state->retry_started) {
1057 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
1058 DEBUG(0,("Failed to convert objects after retry: %s/%s\n",
1059 win_errstr(status), nt_errstr(nt_status)));
1060 tevent_req_nterror(req, nt_status);
1065 * Change info sync or extended operation into a fetch
1066 * of the schema partition, so we get all the schema
1069 * We don't want to re-do the remote exop,
1070 * unless it was REPL_SECRET so we set the
1071 * fallback operation to just be a fetch of
1072 * the relevent partition.
1076 if (state->op->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
1077 state->extended_op_retry = state->op->extended_op;
1079 state->extended_op_retry = DRSUAPI_EXOP_NONE;
1081 state->op->extended_op = DRSUAPI_EXOP_NONE;
1083 if (ldb_dn_compare(nc_root, partition->dn) == 0) {
1084 state->source_dsa_retry = state->op->source_dsa;
1086 status = dreplsrv_partition_find_for_nc(service,
1088 ldb_dn_get_linearized(nc_root),
1090 if (!W_ERROR_IS_OK(status)) {
1091 DEBUG(2, ("Failed to find requested Naming Context for %s: %s",
1092 ldb_dn_get_linearized(nc_root),
1093 win_errstr(status)));
1094 nt_status = werror_to_ntstatus(status);
1095 tevent_req_nterror(req, nt_status);
1098 status = dreplsrv_partition_source_dsa_by_guid(p,
1099 &state->op->source_dsa->repsFrom1->source_dsa_obj_guid,
1100 &state->source_dsa_retry);
1102 if (!W_ERROR_IS_OK(status)) {
1103 struct GUID_txt_buf str;
1104 DEBUG(2, ("Failed to find requested source DSA for %s and %s: %s",
1105 ldb_dn_get_linearized(nc_root),
1106 GUID_buf_string(&state->op->source_dsa->repsFrom1->source_dsa_obj_guid, &str),
1107 win_errstr(status)));
1108 nt_status = werror_to_ntstatus(status);
1109 tevent_req_nterror(req, nt_status);
1114 /* Find schema naming context to be synchronized first */
1115 status = dreplsrv_partition_find_for_nc(service,
1117 ldb_dn_get_linearized(schema_dn),
1119 if (!W_ERROR_IS_OK(status)) {
1120 DEBUG(2, ("Failed to find requested Naming Context for schema: %s",
1121 win_errstr(status)));
1122 nt_status = werror_to_ntstatus(status);
1123 tevent_req_nterror(req, nt_status);
1127 status = dreplsrv_partition_source_dsa_by_guid(p,
1128 &state->op->source_dsa->repsFrom1->source_dsa_obj_guid,
1129 &state->op->source_dsa);
1130 if (!W_ERROR_IS_OK(status)) {
1131 struct GUID_txt_buf str;
1132 DEBUG(2, ("Failed to find requested source DSA for %s and %s: %s",
1133 ldb_dn_get_linearized(schema_dn),
1134 GUID_buf_string(&state->op->source_dsa->repsFrom1->source_dsa_obj_guid, &str),
1135 win_errstr(status)));
1136 nt_status = werror_to_ntstatus(status);
1137 tevent_req_nterror(req, nt_status);
1140 DEBUG(4,("Wrong schema when applying reply GetNCChanges, retrying\n"));
1142 state->retry_started = true;
1144 ok = dreplsrv_op_pull_source_detect_schema_cycle(req);
1149 dreplsrv_op_pull_source_get_changes_trigger(req);
1152 } else if (!W_ERROR_IS_OK(status)) {
1153 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
1154 DEBUG(0,("Failed to convert objects: %s/%s\n",
1155 win_errstr(status), nt_errstr(nt_status)));
1156 tevent_req_nterror(req, nt_status);
1160 status = dsdb_replicated_objects_commit(service->samdb,
1163 &state->op->source_dsa->notify_uSN);
1164 talloc_free(objects);
1166 if (!W_ERROR_IS_OK(status)) {
1169 * Check if this error can be fixed by resending the GetNCChanges
1170 * request with extra flags set (i.e. GET_ANC/GET_TGT)
1172 nt_status = dreplsrv_op_pull_retry_with_flags(req, status);
1174 if (NT_STATUS_IS_OK(nt_status)) {
1177 * We resent the request. Don't update the highwatermark,
1178 * we'll start this part of the cycle again.
1183 DEBUG(0,("Failed to commit objects: %s/%s\n",
1184 win_errstr(status), nt_errstr(nt_status)));
1185 tevent_req_nterror(req, nt_status);
1189 if (state->op->extended_op == DRSUAPI_EXOP_NONE) {
1190 /* if it applied fine, we need to update the highwatermark */
1191 *state->op->source_dsa->repsFrom1 = rf1;
1194 /* we don't need this maybe very large structure anymore */
1198 dreplsrv_op_pull_source_get_changes_trigger(req);
1203 * If we had to divert via doing some other thing, such as
1204 * pulling the schema, then go back and do the original
1205 * operation once we are done.
1207 if (state->source_dsa_retry != NULL) {
1208 state->op->source_dsa = state->source_dsa_retry;
1209 state->op->extended_op = state->extended_op_retry;
1210 state->source_dsa_retry = NULL;
1211 dreplsrv_op_pull_source_get_changes_trigger(req);
1215 if (state->op->extended_op != DRSUAPI_EXOP_NONE ||
1216 state->op->service->am_rodc) {
1218 we don't do the UpdateRefs for extended ops or if we
1221 tevent_req_done(req);
1225 /* now we need to update the repsTo record for this partition
1226 on the server. These records are initially established when
1227 we join the domain, but they quickly expire. We do it here
1228 so we can use the already established DRSUAPI pipe
1230 dreplsrv_update_refs_trigger(req);
1233 static void dreplsrv_update_refs_done(struct tevent_req *subreq);
1236 send a UpdateRefs request to refresh our repsTo record on the server
1238 static void dreplsrv_update_refs_trigger(struct tevent_req *req)
1240 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
1241 struct dreplsrv_op_pull_source_state);
1242 struct dreplsrv_service *service = state->op->service;
1243 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
1244 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
1245 struct drsuapi_DsReplicaUpdateRefs *r;
1246 char *ntds_dns_name;
1247 struct tevent_req *subreq;
1249 r = talloc(state, struct drsuapi_DsReplicaUpdateRefs);
1250 if (tevent_req_nomem(r, req)) {
1254 ntds_dns_name = samdb_ntds_msdcs_dns_name(service->samdb, r, &service->ntds_guid);
1255 if (tevent_req_nomem(ntds_dns_name, req)) {
1260 r->in.bind_handle = &drsuapi->bind_handle;
1262 r->in.req.req1.naming_context = &partition->nc;
1263 r->in.req.req1.dest_dsa_dns_name = ntds_dns_name;
1264 r->in.req.req1.dest_dsa_guid = service->ntds_guid;
1265 r->in.req.req1.options = DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_DEL_REF;
1266 if (!service->am_rodc) {
1267 r->in.req.req1.options |= DRSUAPI_DRS_WRIT_REP;
1270 state->ndr_struct_ptr = r;
1271 subreq = dcerpc_drsuapi_DsReplicaUpdateRefs_r_send(state,
1273 drsuapi->drsuapi_handle,
1275 if (tevent_req_nomem(subreq, req)) {
1279 tevent_req_set_callback(subreq, dreplsrv_update_refs_done, req);
1283 receive a UpdateRefs reply
1285 static void dreplsrv_update_refs_done(struct tevent_req *subreq)
1287 struct tevent_req *req = tevent_req_callback_data(subreq,
1289 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
1290 struct dreplsrv_op_pull_source_state);
1291 struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(state->ndr_struct_ptr,
1292 struct drsuapi_DsReplicaUpdateRefs);
1295 state->ndr_struct_ptr = NULL;
1297 status = dcerpc_drsuapi_DsReplicaUpdateRefs_r_recv(subreq, r);
1298 TALLOC_FREE(subreq);
1299 if (!NT_STATUS_IS_OK(status)) {
1300 DEBUG(0,("UpdateRefs failed with %s\n",
1301 nt_errstr(status)));
1302 tevent_req_nterror(req, status);
1306 if (!W_ERROR_IS_OK(r->out.result)) {
1307 status = werror_to_ntstatus(r->out.result);
1308 DEBUG(0,("UpdateRefs failed with %s/%s for %s %s\n",
1309 win_errstr(r->out.result),
1311 r->in.req.req1.dest_dsa_dns_name,
1312 r->in.req.req1.naming_context->dn));
1314 * TODO we are currently not sending the
1315 * DsReplicaUpdateRefs at the correct moment,
1316 * we do it just after a GetNcChanges which is
1317 * not always correct.
1318 * Especially when another DC is trying to demote
1319 * it will sends us a DsReplicaSync that will trigger a getNcChanges
1320 * this call will succeed but the DsRecplicaUpdateRefs that we send
1321 * just after will not because the DC is in a demote state and
1322 * will reply us a WERR_DS_DRA_BUSY, this error will cause us to
1323 * answer to the DsReplicaSync with a non OK status, the other DC
1324 * will stop the demote due to this error.
1325 * In order to cope with this we will for the moment concider
1326 * a DS_DRA_BUSY not as an error.
1327 * It's not ideal but it should not have a too huge impact for
1328 * running production as this error otherwise never happen and
1329 * due to the fact the send a DsReplicaUpdateRefs after each getNcChanges
1331 if (!W_ERROR_EQUAL(r->out.result, WERR_DS_DRA_BUSY)) {
1332 tevent_req_nterror(req, status);
1337 DEBUG(4,("UpdateRefs OK for %s %s\n",
1338 r->in.req.req1.dest_dsa_dns_name,
1339 r->in.req.req1.naming_context->dn));
1341 tevent_req_done(req);
1344 WERROR dreplsrv_op_pull_source_recv(struct tevent_req *req)
1348 if (tevent_req_is_nterror(req, &status)) {
1349 tevent_req_received(req);
1350 return ntstatus_to_werror(status);
1353 tevent_req_received(req);