repl: Avoid use-after-free when working with the working_schema
[Samba.git] / source4 / dsdb / repl / drepl_out_helpers.c
blob64816ad2725ce808a17d53abca47017852837aa1
1 /*
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/>.
22 #include "includes.h"
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);
62 if (req == NULL) {
63 return NULL;
66 state->ev = ev;
67 state->conn = conn;
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);
75 if (is_connected) {
76 tevent_req_done(req);
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);
96 return 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,
104 struct tevent_req);
105 struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
106 struct dreplsrv_out_drsuapi_state);
107 NTSTATUS status;
108 struct tevent_req *subreq;
110 status = dcerpc_pipe_connect_b_recv(creq,
111 state->drsuapi,
112 &state->drsuapi->pipe);
113 if (tevent_req_nterror(req, status)) {
114 return;
117 state->drsuapi->drsuapi_handle = state->drsuapi->pipe->binding_handle;
119 status = gensec_session_key(state->drsuapi->pipe->conn->security_state.generic_state,
120 state->drsuapi,
121 &state->drsuapi->gensec_skey);
122 if (tevent_req_nterror(req, status)) {
123 return;
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,
134 state->ev,
135 state->drsuapi->drsuapi_handle,
136 &state->bind_r);
137 if (tevent_req_nomem(subreq, req)) {
138 return;
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,
146 struct tevent_req);
147 struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
148 struct dreplsrv_out_drsuapi_state);
149 NTSTATUS status;
151 status = dcerpc_drsuapi_DsBind_r_recv(subreq, state);
152 TALLOC_FREE(subreq);
153 if (tevent_req_nterror(req, status)) {
154 return;
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);
160 return;
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) {
169 case 24: {
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;
177 break;
179 case 28: {
180 *info28 = state->bind_r.out.bind_info->info.info28;
181 break;
183 case 32: {
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;
191 break;
193 case 48: {
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;
201 break;
203 case 52: {
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;
211 break;
213 default:
214 DEBUG(1, ("Warning: invalid info length in bind info: %d\n",
215 state->bind_r.out.bind_info->length));
216 break;
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);
227 NTSTATUS status;
229 if (tevent_req_is_nterror(req, &status)) {
230 tevent_req_received(req);
231 return status;
234 state->conn->drsuapi = talloc_move(state->conn, &state->drsuapi);
236 tevent_req_received(req);
237 return NT_STATUS_OK;
240 struct dreplsrv_op_pull_source_state {
241 struct tevent_context *ev;
242 struct dreplsrv_out_operation *op;
243 void *ndr_struct_ptr;
246 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq);
248 struct tevent_req *dreplsrv_op_pull_source_send(TALLOC_CTX *mem_ctx,
249 struct tevent_context *ev,
250 struct dreplsrv_out_operation *op)
252 struct tevent_req *req;
253 struct dreplsrv_op_pull_source_state *state;
254 struct tevent_req *subreq;
256 req = tevent_req_create(mem_ctx, &state,
257 struct dreplsrv_op_pull_source_state);
258 if (req == NULL) {
259 return NULL;
261 state->ev = ev;
262 state->op = op;
264 subreq = dreplsrv_out_drsuapi_send(state, ev, op->source_dsa->conn);
265 if (tevent_req_nomem(subreq, req)) {
266 return tevent_req_post(req, ev);
268 tevent_req_set_callback(subreq, dreplsrv_op_pull_source_connect_done, req);
270 return req;
273 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req);
275 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq)
277 struct tevent_req *req = tevent_req_callback_data(subreq,
278 struct tevent_req);
279 NTSTATUS status;
281 status = dreplsrv_out_drsuapi_recv(subreq);
282 TALLOC_FREE(subreq);
283 if (tevent_req_nterror(req, status)) {
284 return;
287 dreplsrv_op_pull_source_get_changes_trigger(req);
290 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq);
293 get a RODC partial attribute set for a replication call
295 static NTSTATUS dreplsrv_get_rodc_partial_attribute_set(struct dreplsrv_service *service,
296 TALLOC_CTX *mem_ctx,
297 struct drsuapi_DsPartialAttributeSet **_pas,
298 bool for_schema)
300 struct drsuapi_DsPartialAttributeSet *pas;
301 struct dsdb_schema *schema;
302 uint32_t i;
304 pas = talloc_zero(mem_ctx, struct drsuapi_DsPartialAttributeSet);
305 NT_STATUS_HAVE_NO_MEMORY(pas);
307 schema = dsdb_get_schema(service->samdb, NULL);
309 pas->version = 1;
310 pas->attids = talloc_array(pas, enum drsuapi_DsAttributeId, schema->num_attributes);
311 if (pas->attids == NULL) {
312 TALLOC_FREE(pas);
313 return NT_STATUS_NO_MEMORY;
316 for (i=0; i<schema->num_attributes; i++) {
317 struct dsdb_attribute *a;
318 a = schema->attributes_by_attributeID_id[i];
319 if (a->systemFlags & (DS_FLAG_ATTR_NOT_REPLICATED | DS_FLAG_ATTR_IS_CONSTRUCTED)) {
320 continue;
322 if (a->searchFlags & SEARCH_FLAG_RODC_ATTRIBUTE) {
323 continue;
325 pas->attids[pas->num_attids] = dsdb_attribute_get_attid(a, for_schema);
326 pas->num_attids++;
329 pas->attids = talloc_realloc(pas, pas->attids, enum drsuapi_DsAttributeId, pas->num_attids);
330 if (pas->attids == NULL) {
331 TALLOC_FREE(pas);
332 return NT_STATUS_NO_MEMORY;
335 *_pas = pas;
336 return NT_STATUS_OK;
341 get a GC partial attribute set for a replication call
343 static NTSTATUS dreplsrv_get_gc_partial_attribute_set(struct dreplsrv_service *service,
344 TALLOC_CTX *mem_ctx,
345 struct drsuapi_DsPartialAttributeSet **_pas)
347 struct drsuapi_DsPartialAttributeSet *pas;
348 struct dsdb_schema *schema;
349 uint32_t i;
351 pas = talloc_zero(mem_ctx, struct drsuapi_DsPartialAttributeSet);
352 NT_STATUS_HAVE_NO_MEMORY(pas);
354 schema = dsdb_get_schema(service->samdb, NULL);
356 pas->version = 1;
357 pas->attids = talloc_array(pas, enum drsuapi_DsAttributeId, schema->num_attributes);
358 if (pas->attids == NULL) {
359 TALLOC_FREE(pas);
360 return NT_STATUS_NO_MEMORY;
363 for (i=0; i<schema->num_attributes; i++) {
364 struct dsdb_attribute *a;
365 a = schema->attributes_by_attributeID_id[i];
366 if (a->isMemberOfPartialAttributeSet) {
367 pas->attids[pas->num_attids] = dsdb_attribute_get_attid(a, false);
368 pas->num_attids++;
372 pas->attids = talloc_realloc(pas, pas->attids, enum drsuapi_DsAttributeId, pas->num_attids);
373 if (pas->attids == NULL) {
374 TALLOC_FREE(pas);
375 return NT_STATUS_NO_MEMORY;
378 *_pas = pas;
379 return NT_STATUS_OK;
383 convert from one udv format to the other
385 static WERROR udv_convert(TALLOC_CTX *mem_ctx,
386 const struct replUpToDateVectorCtr2 *udv,
387 struct drsuapi_DsReplicaCursorCtrEx *udv_ex)
389 uint32_t i;
391 udv_ex->version = 2;
392 udv_ex->reserved1 = 0;
393 udv_ex->reserved2 = 0;
394 udv_ex->count = udv->count;
395 udv_ex->cursors = talloc_array(mem_ctx, struct drsuapi_DsReplicaCursor, udv->count);
396 W_ERROR_HAVE_NO_MEMORY(udv_ex->cursors);
398 for (i=0; i<udv->count; i++) {
399 udv_ex->cursors[i].source_dsa_invocation_id = udv->cursors[i].source_dsa_invocation_id;
400 udv_ex->cursors[i].highest_usn = udv->cursors[i].highest_usn;
403 return WERR_OK;
407 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req)
409 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
410 struct dreplsrv_op_pull_source_state);
411 struct repsFromTo1 *rf1 = state->op->source_dsa->repsFrom1;
412 struct dreplsrv_service *service = state->op->service;
413 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
414 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
415 struct drsuapi_DsGetNCChanges *r;
416 struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector;
417 struct tevent_req *subreq;
418 struct drsuapi_DsPartialAttributeSet *pas = NULL;
419 NTSTATUS status;
420 uint32_t replica_flags;
421 struct drsuapi_DsReplicaHighWaterMark highwatermark;
422 struct ldb_dn *schema_dn = ldb_get_schema_basedn(service->samdb);
424 r = talloc(state, struct drsuapi_DsGetNCChanges);
425 if (tevent_req_nomem(r, req)) {
426 return;
429 r->out.level_out = talloc(r, uint32_t);
430 if (tevent_req_nomem(r->out.level_out, req)) {
431 return;
433 r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest);
434 if (tevent_req_nomem(r->in.req, req)) {
435 return;
437 r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr);
438 if (tevent_req_nomem(r->out.ctr, req)) {
439 return;
442 if (partition->uptodatevector.count != 0 &&
443 partition->uptodatevector_ex.count == 0) {
444 WERROR werr;
445 werr = udv_convert(partition, &partition->uptodatevector, &partition->uptodatevector_ex);
446 if (!W_ERROR_IS_OK(werr)) {
447 DEBUG(0,(__location__ ": Failed to convert UDV for %s : %s\n",
448 ldb_dn_get_linearized(partition->dn), win_errstr(werr)));
452 if (partition->uptodatevector_ex.count == 0) {
453 uptodateness_vector = NULL;
454 } else {
455 uptodateness_vector = &partition->uptodatevector_ex;
458 replica_flags = rf1->replica_flags;
459 highwatermark = rf1->highwatermark;
461 if (state->op->options & DRSUAPI_DRS_GET_ANC) {
462 replica_flags |= DRSUAPI_DRS_GET_ANC;
465 if (partition->partial_replica) {
466 status = dreplsrv_get_gc_partial_attribute_set(service, r, &pas);
467 if (!NT_STATUS_IS_OK(status)) {
468 DEBUG(0,(__location__ ": Failed to construct GC partial attribute set : %s\n", nt_errstr(status)));
469 return;
471 replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
472 } else if (partition->rodc_replica) {
473 bool for_schema = false;
474 if (ldb_dn_compare_base(schema_dn, partition->dn) == 0) {
475 for_schema = true;
478 status = dreplsrv_get_rodc_partial_attribute_set(service, r, &pas, for_schema);
479 if (!NT_STATUS_IS_OK(status)) {
480 DEBUG(0,(__location__ ": Failed to construct RODC partial attribute set : %s\n", nt_errstr(status)));
481 return;
483 replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
484 if (state->op->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
485 replica_flags &= ~DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING;
486 } else {
487 replica_flags |= DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING;
490 if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
492 * If it's an exop never set the ADD_REF even if it's in
493 * repsFrom flags.
495 replica_flags &= ~DRSUAPI_DRS_ADD_REF;
498 /* is this a full resync of all objects? */
499 if (state->op->options & DRSUAPI_DRS_FULL_SYNC_NOW) {
500 ZERO_STRUCT(highwatermark);
501 /* clear the FULL_SYNC_NOW option for subsequent
502 stages of the replication cycle */
503 state->op->options &= ~DRSUAPI_DRS_FULL_SYNC_NOW;
504 state->op->options |= DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS;
505 replica_flags |= DRSUAPI_DRS_NEVER_SYNCED;
507 if (state->op->options & DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS) {
508 uptodateness_vector = NULL;
511 r->in.bind_handle = &drsuapi->bind_handle;
512 if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
513 r->in.level = 8;
514 r->in.req->req8.destination_dsa_guid = service->ntds_guid;
515 r->in.req->req8.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
516 r->in.req->req8.naming_context = &partition->nc;
517 r->in.req->req8.highwatermark = highwatermark;
518 r->in.req->req8.uptodateness_vector = uptodateness_vector;
519 r->in.req->req8.replica_flags = replica_flags;
520 r->in.req->req8.max_object_count = 133;
521 r->in.req->req8.max_ndr_size = 1336811;
522 r->in.req->req8.extended_op = state->op->extended_op;
523 r->in.req->req8.fsmo_info = state->op->fsmo_info;
524 r->in.req->req8.partial_attribute_set = pas;
525 r->in.req->req8.partial_attribute_set_ex= NULL;
526 r->in.req->req8.mapping_ctr.num_mappings= 0;
527 r->in.req->req8.mapping_ctr.mappings = NULL;
528 } else {
529 r->in.level = 5;
530 r->in.req->req5.destination_dsa_guid = service->ntds_guid;
531 r->in.req->req5.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
532 r->in.req->req5.naming_context = &partition->nc;
533 r->in.req->req5.highwatermark = highwatermark;
534 r->in.req->req5.uptodateness_vector = uptodateness_vector;
535 r->in.req->req5.replica_flags = replica_flags;
536 r->in.req->req5.max_object_count = 133;
537 r->in.req->req5.max_ndr_size = 1336770;
538 r->in.req->req5.extended_op = state->op->extended_op;
539 r->in.req->req5.fsmo_info = state->op->fsmo_info;
542 #if 0
543 NDR_PRINT_IN_DEBUG(drsuapi_DsGetNCChanges, r);
544 #endif
546 state->ndr_struct_ptr = r;
547 subreq = dcerpc_drsuapi_DsGetNCChanges_r_send(state,
548 state->ev,
549 drsuapi->drsuapi_handle,
551 if (tevent_req_nomem(subreq, req)) {
552 return;
554 tevent_req_set_callback(subreq, dreplsrv_op_pull_source_get_changes_done, req);
557 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
558 struct drsuapi_DsGetNCChanges *r,
559 uint32_t ctr_level,
560 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
561 struct drsuapi_DsGetNCChangesCtr6 *ctr6);
563 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq)
565 struct tevent_req *req = tevent_req_callback_data(subreq,
566 struct tevent_req);
567 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
568 struct dreplsrv_op_pull_source_state);
569 NTSTATUS status;
570 struct drsuapi_DsGetNCChanges *r = talloc_get_type(state->ndr_struct_ptr,
571 struct drsuapi_DsGetNCChanges);
572 uint32_t ctr_level = 0;
573 struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
574 struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
575 enum drsuapi_DsExtendedError extended_ret = DRSUAPI_EXOP_ERR_NONE;
576 state->ndr_struct_ptr = NULL;
578 status = dcerpc_drsuapi_DsGetNCChanges_r_recv(subreq, r);
579 TALLOC_FREE(subreq);
580 if (tevent_req_nterror(req, status)) {
581 return;
584 if (!W_ERROR_IS_OK(r->out.result)) {
585 status = werror_to_ntstatus(r->out.result);
586 tevent_req_nterror(req, status);
587 return;
590 if (*r->out.level_out == 1) {
591 ctr_level = 1;
592 ctr1 = &r->out.ctr->ctr1;
593 } else if (*r->out.level_out == 2 &&
594 r->out.ctr->ctr2.mszip1.ts) {
595 ctr_level = 1;
596 ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
597 } else if (*r->out.level_out == 6) {
598 ctr_level = 6;
599 ctr6 = &r->out.ctr->ctr6;
600 } else if (*r->out.level_out == 7 &&
601 r->out.ctr->ctr7.level == 6 &&
602 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP &&
603 r->out.ctr->ctr7.ctr.mszip6.ts) {
604 ctr_level = 6;
605 ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
606 } else if (*r->out.level_out == 7 &&
607 r->out.ctr->ctr7.level == 6 &&
608 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
609 r->out.ctr->ctr7.ctr.xpress6.ts) {
610 ctr_level = 6;
611 ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
612 } else {
613 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
614 tevent_req_nterror(req, status);
615 return;
618 if (!ctr1 && !ctr6) {
619 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
620 tevent_req_nterror(req, status);
621 return;
624 if (ctr_level == 6) {
625 if (!W_ERROR_IS_OK(ctr6->drs_error)) {
626 status = werror_to_ntstatus(ctr6->drs_error);
627 tevent_req_nterror(req, status);
628 return;
630 extended_ret = ctr6->extended_ret;
633 if (ctr_level == 1) {
634 extended_ret = ctr1->extended_ret;
637 if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
638 state->op->extended_ret = extended_ret;
640 if (extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
641 status = NT_STATUS_UNSUCCESSFUL;
642 tevent_req_nterror(req, status);
643 return;
647 dreplsrv_op_pull_source_apply_changes_trigger(req, r, ctr_level, ctr1, ctr6);
650 static void dreplsrv_update_refs_trigger(struct tevent_req *req);
652 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
653 struct drsuapi_DsGetNCChanges *r,
654 uint32_t ctr_level,
655 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
656 struct drsuapi_DsGetNCChangesCtr6 *ctr6)
658 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
659 struct dreplsrv_op_pull_source_state);
660 struct repsFromTo1 rf1 = *state->op->source_dsa->repsFrom1;
661 struct dreplsrv_service *service = state->op->service;
662 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
663 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
664 struct ldb_dn *schema_dn = ldb_get_schema_basedn(service->samdb);
665 struct dsdb_schema *schema;
666 struct dsdb_schema *working_schema = NULL;
667 const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
668 uint32_t object_count;
669 struct drsuapi_DsReplicaObjectListItemEx *first_object;
670 uint32_t linked_attributes_count;
671 struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
672 const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
673 struct dsdb_extended_replicated_objects *objects;
674 bool more_data = false;
675 WERROR status;
676 NTSTATUS nt_status;
677 uint32_t dsdb_repl_flags = 0;
678 struct ldb_dn *nc_root = NULL;
679 int ret;
681 switch (ctr_level) {
682 case 1:
683 mapping_ctr = &ctr1->mapping_ctr;
684 object_count = ctr1->object_count;
685 first_object = ctr1->first_object;
686 linked_attributes_count = 0;
687 linked_attributes = NULL;
688 rf1.source_dsa_obj_guid = ctr1->source_dsa_guid;
689 rf1.source_dsa_invocation_id = ctr1->source_dsa_invocation_id;
690 rf1.highwatermark = ctr1->new_highwatermark;
691 uptodateness_vector = NULL; /* TODO: map it */
692 more_data = ctr1->more_data;
693 break;
694 case 6:
695 mapping_ctr = &ctr6->mapping_ctr;
696 object_count = ctr6->object_count;
697 first_object = ctr6->first_object;
698 linked_attributes_count = ctr6->linked_attributes_count;
699 linked_attributes = ctr6->linked_attributes;
700 rf1.source_dsa_obj_guid = ctr6->source_dsa_guid;
701 rf1.source_dsa_invocation_id = ctr6->source_dsa_invocation_id;
702 rf1.highwatermark = ctr6->new_highwatermark;
703 uptodateness_vector = ctr6->uptodateness_vector;
704 more_data = ctr6->more_data;
705 break;
706 default:
707 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
708 tevent_req_nterror(req, nt_status);
709 return;
712 schema = dsdb_get_schema(service->samdb, state);
713 if (!schema) {
714 DEBUG(0,(__location__ ": Schema is not loaded yet!\n"));
715 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
716 return;
720 * Decide what working schema to use for object conversion.
721 * We won't need a working schema for empty replicas sent.
723 if (first_object) {
724 bool is_schema = ldb_dn_compare(partition->dn, schema_dn) == 0;
725 if (is_schema) {
726 /* create working schema to convert objects with */
727 status = dsdb_repl_make_working_schema(service->samdb,
728 schema,
729 mapping_ctr,
730 object_count,
731 first_object,
732 &drsuapi->gensec_skey,
733 state, &working_schema);
734 if (!W_ERROR_IS_OK(status)) {
735 DEBUG(0,("Failed to create working schema: %s\n",
736 win_errstr(status)));
737 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
738 return;
743 if (partition->partial_replica || partition->rodc_replica) {
744 dsdb_repl_flags |= DSDB_REPL_FLAG_PARTIAL_REPLICA;
746 if (state->op->options & DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS) {
747 dsdb_repl_flags |= DSDB_REPL_FLAG_PRIORITISE_INCOMING;
749 if (state->op->options & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
750 dsdb_repl_flags |= DSDB_REPL_FLAG_EXPECT_NO_SECRETS;
753 if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
754 ret = dsdb_find_nc_root(service->samdb, partition,
755 partition->dn, &nc_root);
756 if (ret != LDB_SUCCESS) {
757 DEBUG(0,(__location__ ": Failed to find nc_root for %s\n",
758 ldb_dn_get_linearized(partition->dn)));
759 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
760 return;
762 } else {
763 nc_root = partition->dn;
766 status = dsdb_replicated_objects_convert(service->samdb,
767 working_schema ? working_schema : schema,
768 nc_root,
769 mapping_ctr,
770 object_count,
771 first_object,
772 linked_attributes_count,
773 linked_attributes,
774 &rf1,
775 uptodateness_vector,
776 &drsuapi->gensec_skey,
777 dsdb_repl_flags,
778 state, &objects);
780 if (W_ERROR_EQUAL(status, WERR_DS_DRA_SCHEMA_MISMATCH)
781 && state->op->source_dsa_retry == NULL) {
782 struct dreplsrv_partition *p;
785 * Change info sync or extended operation into a fetch
786 * of the schema partition, so we get all the schema
787 * objects we need.
789 * We don't want to re-do the remote exop,
790 * unless it was REPL_SECRET so we set the
791 * fallback operation to just be a fetch of
792 * the relevent partition.
796 if (state->op->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
797 state->op->extended_op_retry = state->op->extended_op;
798 } else {
799 state->op->extended_op_retry = DRSUAPI_EXOP_NONE;
801 state->op->extended_op = DRSUAPI_EXOP_NONE;
803 if (ldb_dn_compare(nc_root, partition->dn) == 0) {
804 state->op->source_dsa_retry = state->op->source_dsa;
805 } else {
806 status = dreplsrv_partition_find_for_nc(service,
807 NULL, NULL,
808 ldb_dn_get_linearized(nc_root),
809 &p);
810 if (!W_ERROR_IS_OK(status)) {
811 DEBUG(2, ("Failed to find requested Naming Context for %s: %s",
812 ldb_dn_get_linearized(nc_root),
813 win_errstr(status)));
814 nt_status = werror_to_ntstatus(status);
815 tevent_req_nterror(req, nt_status);
816 return;
818 status = dreplsrv_partition_source_dsa_by_guid(p,
819 &state->op->source_dsa->repsFrom1->source_dsa_obj_guid,
820 &state->op->source_dsa_retry);
822 if (!W_ERROR_IS_OK(status)) {
823 struct GUID_txt_buf str;
824 DEBUG(2, ("Failed to find requested source DSA for %s and %s: %s",
825 ldb_dn_get_linearized(nc_root),
826 GUID_buf_string(&state->op->source_dsa->repsFrom1->source_dsa_obj_guid, &str),
827 win_errstr(status)));
828 nt_status = werror_to_ntstatus(status);
829 tevent_req_nterror(req, nt_status);
830 return;
834 /* Find schmea naming context to be synchronized first */
835 status = dreplsrv_partition_find_for_nc(service,
836 NULL, NULL,
837 ldb_dn_get_linearized(schema_dn),
838 &p);
839 if (!W_ERROR_IS_OK(status)) {
840 DEBUG(2, ("Failed to find requested Naming Context for schema: %s",
841 win_errstr(status)));
842 nt_status = werror_to_ntstatus(status);
843 tevent_req_nterror(req, nt_status);
844 return;
847 status = dreplsrv_partition_source_dsa_by_guid(p,
848 &state->op->source_dsa->repsFrom1->source_dsa_obj_guid,
849 &state->op->source_dsa);
850 if (!W_ERROR_IS_OK(status)) {
851 struct GUID_txt_buf str;
852 DEBUG(2, ("Failed to find requested source DSA for %s and %s: %s",
853 ldb_dn_get_linearized(schema_dn),
854 GUID_buf_string(&state->op->source_dsa->repsFrom1->source_dsa_obj_guid, &str),
855 win_errstr(status)));
856 nt_status = werror_to_ntstatus(status);
857 tevent_req_nterror(req, nt_status);
858 return;
860 DEBUG(4,("Wrong schema when applying reply GetNCChanges, retrying\n"));
862 dreplsrv_op_pull_source_get_changes_trigger(req);
863 return;
865 } else if (!W_ERROR_IS_OK(status)) {
866 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
867 DEBUG(0,("Failed to convert objects: %s/%s\n",
868 win_errstr(status), nt_errstr(nt_status)));
869 tevent_req_nterror(req, nt_status);
870 return;
873 status = dsdb_replicated_objects_commit(service->samdb,
874 working_schema,
875 objects,
876 &state->op->source_dsa->notify_uSN);
877 talloc_free(objects);
879 if (!W_ERROR_IS_OK(status)) {
882 * If we failed to apply the records due to a missing
883 * parent, try again after asking for the parent
884 * records first. Because we don't update the
885 * highwatermark, we start this part of the cycle
886 * again.
888 if (((state->op->options & DRSUAPI_DRS_GET_ANC) == 0)
889 && W_ERROR_EQUAL(status, WERR_DS_DRA_MISSING_PARENT)) {
890 state->op->options |= DRSUAPI_DRS_GET_ANC;
891 DEBUG(4,("Missing parent object when we didn't set the DRSUAPI_DRS_GET_ANC flag, retrying\n"));
892 dreplsrv_op_pull_source_get_changes_trigger(req);
893 return;
894 } else if (((state->op->options & DRSUAPI_DRS_GET_ANC))
895 && W_ERROR_EQUAL(status, WERR_DS_DRA_MISSING_PARENT)) {
896 DEBUG(1,("Missing parent object despite setting DRSUAPI_DRS_GET_ANC flag\n"));
897 nt_status = NT_STATUS_INVALID_NETWORK_RESPONSE;
898 } else {
899 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
901 DEBUG(0,("Failed to commit objects: %s/%s\n",
902 win_errstr(status), nt_errstr(nt_status)));
903 tevent_req_nterror(req, nt_status);
904 return;
907 if (state->op->extended_op == DRSUAPI_EXOP_NONE) {
908 /* if it applied fine, we need to update the highwatermark */
909 *state->op->source_dsa->repsFrom1 = rf1;
912 /* we don't need this maybe very large structure anymore */
913 TALLOC_FREE(r);
915 if (more_data) {
916 dreplsrv_op_pull_source_get_changes_trigger(req);
917 return;
921 * If we had to divert via doing some other thing, such as
922 * pulling the schema, then go back and do the original
923 * operation once we are done.
925 if (state->op->source_dsa_retry != NULL) {
926 state->op->source_dsa = state->op->source_dsa_retry;
927 state->op->extended_op = state->op->extended_op_retry;
928 state->op->source_dsa_retry = NULL;
929 dreplsrv_op_pull_source_get_changes_trigger(req);
930 return;
933 if (state->op->extended_op != DRSUAPI_EXOP_NONE ||
934 state->op->service->am_rodc) {
936 we don't do the UpdateRefs for extended ops or if we
937 are a RODC
939 tevent_req_done(req);
940 return;
943 /* now we need to update the repsTo record for this partition
944 on the server. These records are initially established when
945 we join the domain, but they quickly expire. We do it here
946 so we can use the already established DRSUAPI pipe
948 dreplsrv_update_refs_trigger(req);
951 static void dreplsrv_update_refs_done(struct tevent_req *subreq);
954 send a UpdateRefs request to refresh our repsTo record on the server
956 static void dreplsrv_update_refs_trigger(struct tevent_req *req)
958 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
959 struct dreplsrv_op_pull_source_state);
960 struct dreplsrv_service *service = state->op->service;
961 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
962 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
963 struct drsuapi_DsReplicaUpdateRefs *r;
964 char *ntds_dns_name;
965 struct tevent_req *subreq;
967 r = talloc(state, struct drsuapi_DsReplicaUpdateRefs);
968 if (tevent_req_nomem(r, req)) {
969 return;
972 ntds_dns_name = samdb_ntds_msdcs_dns_name(service->samdb, r, &service->ntds_guid);
973 if (tevent_req_nomem(ntds_dns_name, req)) {
974 talloc_free(r);
975 return;
978 r->in.bind_handle = &drsuapi->bind_handle;
979 r->in.level = 1;
980 r->in.req.req1.naming_context = &partition->nc;
981 r->in.req.req1.dest_dsa_dns_name = ntds_dns_name;
982 r->in.req.req1.dest_dsa_guid = service->ntds_guid;
983 r->in.req.req1.options = DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_DEL_REF;
984 if (!service->am_rodc) {
985 r->in.req.req1.options |= DRSUAPI_DRS_WRIT_REP;
988 state->ndr_struct_ptr = r;
989 subreq = dcerpc_drsuapi_DsReplicaUpdateRefs_r_send(state,
990 state->ev,
991 drsuapi->drsuapi_handle,
993 if (tevent_req_nomem(subreq, req)) {
994 talloc_free(r);
995 return;
997 tevent_req_set_callback(subreq, dreplsrv_update_refs_done, req);
1001 receive a UpdateRefs reply
1003 static void dreplsrv_update_refs_done(struct tevent_req *subreq)
1005 struct tevent_req *req = tevent_req_callback_data(subreq,
1006 struct tevent_req);
1007 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
1008 struct dreplsrv_op_pull_source_state);
1009 struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(state->ndr_struct_ptr,
1010 struct drsuapi_DsReplicaUpdateRefs);
1011 NTSTATUS status;
1013 state->ndr_struct_ptr = NULL;
1015 status = dcerpc_drsuapi_DsReplicaUpdateRefs_r_recv(subreq, r);
1016 TALLOC_FREE(subreq);
1017 if (!NT_STATUS_IS_OK(status)) {
1018 DEBUG(0,("UpdateRefs failed with %s\n",
1019 nt_errstr(status)));
1020 tevent_req_nterror(req, status);
1021 return;
1024 if (!W_ERROR_IS_OK(r->out.result)) {
1025 status = werror_to_ntstatus(r->out.result);
1026 DEBUG(0,("UpdateRefs failed with %s/%s for %s %s\n",
1027 win_errstr(r->out.result),
1028 nt_errstr(status),
1029 r->in.req.req1.dest_dsa_dns_name,
1030 r->in.req.req1.naming_context->dn));
1032 * TODO we are currently not sending the
1033 * DsReplicaUpdateRefs at the correct moment,
1034 * we do it just after a GetNcChanges which is
1035 * not always correct.
1036 * Especially when another DC is trying to demote
1037 * it will sends us a DsReplicaSync that will trigger a getNcChanges
1038 * this call will succeed but the DsRecplicaUpdateRefs that we send
1039 * just after will not because the DC is in a demote state and
1040 * will reply us a WERR_DS_DRA_BUSY, this error will cause us to
1041 * answer to the DsReplicaSync with a non OK status, the other DC
1042 * will stop the demote due to this error.
1043 * In order to cope with this we will for the moment concider
1044 * a DS_DRA_BUSY not as an error.
1045 * It's not ideal but it should not have a too huge impact for
1046 * running production as this error otherwise never happen and
1047 * due to the fact the send a DsReplicaUpdateRefs after each getNcChanges
1049 if (!W_ERROR_EQUAL(r->out.result, WERR_DS_DRA_BUSY)) {
1050 tevent_req_nterror(req, status);
1051 return;
1055 DEBUG(4,("UpdateRefs OK for %s %s\n",
1056 r->in.req.req1.dest_dsa_dns_name,
1057 r->in.req.req1.naming_context->dn));
1059 tevent_req_done(req);
1062 WERROR dreplsrv_op_pull_source_recv(struct tevent_req *req)
1064 NTSTATUS status;
1066 if (tevent_req_is_nterror(req, &status)) {
1067 tevent_req_received(req);
1068 return ntstatus_to_werror(status);
1071 tevent_req_received(req);
1072 return WERR_OK;