talloc: Fix CID 1373621 Unchecked return value
[Samba.git] / source4 / dsdb / repl / drepl_out_helpers.c
blobac0b947aadb78c1674b772c083a61eb34d27a7a5
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;
245 * Used when we have to re-try with a different NC, eg for
246 * EXOP retry or to get a current schema first
248 struct dreplsrv_partition_source_dsa *source_dsa_retry;
249 enum drsuapi_DsExtendedOperation extended_op_retry;
250 bool retry_started;
253 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq);
255 struct tevent_req *dreplsrv_op_pull_source_send(TALLOC_CTX *mem_ctx,
256 struct tevent_context *ev,
257 struct dreplsrv_out_operation *op)
259 struct tevent_req *req;
260 struct dreplsrv_op_pull_source_state *state;
261 struct tevent_req *subreq;
263 req = tevent_req_create(mem_ctx, &state,
264 struct dreplsrv_op_pull_source_state);
265 if (req == NULL) {
266 return NULL;
268 state->ev = ev;
269 state->op = op;
271 subreq = dreplsrv_out_drsuapi_send(state, ev, op->source_dsa->conn);
272 if (tevent_req_nomem(subreq, req)) {
273 return tevent_req_post(req, ev);
275 tevent_req_set_callback(subreq, dreplsrv_op_pull_source_connect_done, req);
277 return req;
280 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req);
282 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq)
284 struct tevent_req *req = tevent_req_callback_data(subreq,
285 struct tevent_req);
286 NTSTATUS status;
288 status = dreplsrv_out_drsuapi_recv(subreq);
289 TALLOC_FREE(subreq);
290 if (tevent_req_nterror(req, status)) {
291 return;
294 dreplsrv_op_pull_source_get_changes_trigger(req);
297 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq);
300 get a RODC partial attribute set for a replication call
302 static NTSTATUS dreplsrv_get_rodc_partial_attribute_set(struct dreplsrv_service *service,
303 TALLOC_CTX *mem_ctx,
304 struct drsuapi_DsPartialAttributeSet **_pas,
305 struct drsuapi_DsReplicaOIDMapping_Ctr **pfm,
306 bool for_schema)
308 struct drsuapi_DsPartialAttributeSet *pas;
309 struct dsdb_schema *schema;
310 uint32_t i;
312 pas = talloc_zero(mem_ctx, struct drsuapi_DsPartialAttributeSet);
313 NT_STATUS_HAVE_NO_MEMORY(pas);
315 schema = dsdb_get_schema(service->samdb, NULL);
317 pas->version = 1;
318 pas->attids = talloc_array(pas, enum drsuapi_DsAttributeId, schema->num_attributes);
319 if (pas->attids == NULL) {
320 TALLOC_FREE(pas);
321 return NT_STATUS_NO_MEMORY;
324 for (i=0; i<schema->num_attributes; i++) {
325 struct dsdb_attribute *a;
326 a = schema->attributes_by_attributeID_id[i];
327 if (a->systemFlags & (DS_FLAG_ATTR_NOT_REPLICATED | DS_FLAG_ATTR_IS_CONSTRUCTED)) {
328 continue;
330 if (a->searchFlags & SEARCH_FLAG_RODC_ATTRIBUTE) {
331 continue;
333 pas->attids[pas->num_attids] = dsdb_attribute_get_attid(a, for_schema);
334 pas->num_attids++;
337 pas->attids = talloc_realloc(pas, pas->attids, enum drsuapi_DsAttributeId, pas->num_attids);
338 if (pas->attids == NULL) {
339 TALLOC_FREE(pas);
340 return NT_STATUS_NO_MEMORY;
343 *_pas = pas;
345 if (pfm != NULL) {
346 dsdb_get_oid_mappings_drsuapi(schema, true, mem_ctx, pfm);
349 return NT_STATUS_OK;
354 get a GC partial attribute set for a replication call
356 static NTSTATUS dreplsrv_get_gc_partial_attribute_set(struct dreplsrv_service *service,
357 TALLOC_CTX *mem_ctx,
358 struct drsuapi_DsPartialAttributeSet **_pas,
359 struct drsuapi_DsReplicaOIDMapping_Ctr **pfm)
361 struct drsuapi_DsPartialAttributeSet *pas;
362 struct dsdb_schema *schema;
363 uint32_t i;
365 pas = talloc_zero(mem_ctx, struct drsuapi_DsPartialAttributeSet);
366 NT_STATUS_HAVE_NO_MEMORY(pas);
368 schema = dsdb_get_schema(service->samdb, NULL);
370 pas->version = 1;
371 pas->attids = talloc_array(pas, enum drsuapi_DsAttributeId, schema->num_attributes);
372 if (pas->attids == NULL) {
373 TALLOC_FREE(pas);
374 return NT_STATUS_NO_MEMORY;
377 for (i=0; i<schema->num_attributes; i++) {
378 struct dsdb_attribute *a;
379 a = schema->attributes_by_attributeID_id[i];
380 if (a->isMemberOfPartialAttributeSet) {
381 pas->attids[pas->num_attids] = dsdb_attribute_get_attid(a, false);
382 pas->num_attids++;
386 pas->attids = talloc_realloc(pas, pas->attids, enum drsuapi_DsAttributeId, pas->num_attids);
387 if (pas->attids == NULL) {
388 TALLOC_FREE(pas);
389 return NT_STATUS_NO_MEMORY;
392 *_pas = pas;
394 if (pfm != NULL) {
395 dsdb_get_oid_mappings_drsuapi(schema, true, mem_ctx, pfm);
398 return NT_STATUS_OK;
402 convert from one udv format to the other
404 static WERROR udv_convert(TALLOC_CTX *mem_ctx,
405 const struct replUpToDateVectorCtr2 *udv,
406 struct drsuapi_DsReplicaCursorCtrEx *udv_ex)
408 uint32_t i;
410 udv_ex->version = 2;
411 udv_ex->reserved1 = 0;
412 udv_ex->reserved2 = 0;
413 udv_ex->count = udv->count;
414 udv_ex->cursors = talloc_array(mem_ctx, struct drsuapi_DsReplicaCursor, udv->count);
415 W_ERROR_HAVE_NO_MEMORY(udv_ex->cursors);
417 for (i=0; i<udv->count; i++) {
418 udv_ex->cursors[i].source_dsa_invocation_id = udv->cursors[i].source_dsa_invocation_id;
419 udv_ex->cursors[i].highest_usn = udv->cursors[i].highest_usn;
422 return WERR_OK;
426 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req)
428 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
429 struct dreplsrv_op_pull_source_state);
430 struct repsFromTo1 *rf1 = state->op->source_dsa->repsFrom1;
431 struct dreplsrv_service *service = state->op->service;
432 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
433 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
434 struct drsuapi_DsGetNCChanges *r;
435 struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector;
436 struct tevent_req *subreq;
437 struct drsuapi_DsPartialAttributeSet *pas = NULL;
438 NTSTATUS status;
439 uint32_t replica_flags;
440 struct drsuapi_DsReplicaHighWaterMark highwatermark;
441 struct ldb_dn *schema_dn = ldb_get_schema_basedn(service->samdb);
442 struct drsuapi_DsReplicaOIDMapping_Ctr *mappings = NULL;
444 r = talloc(state, struct drsuapi_DsGetNCChanges);
445 if (tevent_req_nomem(r, req)) {
446 return;
449 r->out.level_out = talloc(r, uint32_t);
450 if (tevent_req_nomem(r->out.level_out, req)) {
451 return;
453 r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest);
454 if (tevent_req_nomem(r->in.req, req)) {
455 return;
457 r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr);
458 if (tevent_req_nomem(r->out.ctr, req)) {
459 return;
462 if (partition->uptodatevector.count != 0 &&
463 partition->uptodatevector_ex.count == 0) {
464 WERROR werr;
465 werr = udv_convert(partition, &partition->uptodatevector, &partition->uptodatevector_ex);
466 if (!W_ERROR_IS_OK(werr)) {
467 DEBUG(0,(__location__ ": Failed to convert UDV for %s : %s\n",
468 ldb_dn_get_linearized(partition->dn), win_errstr(werr)));
469 tevent_req_nterror(req, werror_to_ntstatus(werr));
470 return;
474 if (partition->uptodatevector_ex.count == 0) {
475 uptodateness_vector = NULL;
476 } else {
477 uptodateness_vector = &partition->uptodatevector_ex;
480 replica_flags = rf1->replica_flags;
481 highwatermark = rf1->highwatermark;
483 if (state->op->options & DRSUAPI_DRS_GET_ANC) {
484 replica_flags |= DRSUAPI_DRS_GET_ANC;
487 if (state->op->options & DRSUAPI_DRS_SYNC_FORCED) {
488 replica_flags |= DRSUAPI_DRS_SYNC_FORCED;
491 if (partition->partial_replica) {
492 status = dreplsrv_get_gc_partial_attribute_set(service, r,
493 &pas,
494 &mappings);
495 if (!NT_STATUS_IS_OK(status)) {
496 DEBUG(0,(__location__ ": Failed to construct GC partial attribute set : %s\n", nt_errstr(status)));
497 tevent_req_nterror(req, status);
498 return;
500 replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
501 } else if (partition->rodc_replica) {
502 bool for_schema = false;
503 if (ldb_dn_compare_base(schema_dn, partition->dn) == 0) {
504 for_schema = true;
506 status = dreplsrv_get_rodc_partial_attribute_set(service, r,
507 &pas,
508 &mappings,
509 for_schema);
510 if (!NT_STATUS_IS_OK(status)) {
511 DEBUG(0,(__location__ ": Failed to construct RODC partial attribute set : %s\n", nt_errstr(status)));
512 tevent_req_nterror(req, status);
513 return;
515 replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
516 if (state->op->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
517 replica_flags &= ~DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING;
518 } else {
519 replica_flags |= DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING;
522 if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
524 * If it's an exop never set the ADD_REF even if it's in
525 * repsFrom flags.
527 replica_flags &= ~DRSUAPI_DRS_ADD_REF;
530 /* is this a full resync of all objects? */
531 if (state->op->options & DRSUAPI_DRS_FULL_SYNC_NOW) {
532 ZERO_STRUCT(highwatermark);
533 /* clear the FULL_SYNC_NOW option for subsequent
534 stages of the replication cycle */
535 state->op->options &= ~DRSUAPI_DRS_FULL_SYNC_NOW;
536 state->op->options |= DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS;
537 replica_flags |= DRSUAPI_DRS_NEVER_SYNCED;
539 if (state->op->options & DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS) {
540 uptodateness_vector = NULL;
543 r->in.bind_handle = &drsuapi->bind_handle;
544 if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
545 r->in.level = 8;
546 r->in.req->req8.destination_dsa_guid = service->ntds_guid;
547 r->in.req->req8.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
548 r->in.req->req8.naming_context = &partition->nc;
549 r->in.req->req8.highwatermark = highwatermark;
550 r->in.req->req8.uptodateness_vector = uptodateness_vector;
551 r->in.req->req8.replica_flags = replica_flags;
552 r->in.req->req8.max_object_count = 133;
553 r->in.req->req8.max_ndr_size = 1336811;
554 r->in.req->req8.extended_op = state->op->extended_op;
555 r->in.req->req8.fsmo_info = state->op->fsmo_info;
556 r->in.req->req8.partial_attribute_set = pas;
557 r->in.req->req8.partial_attribute_set_ex= NULL;
558 r->in.req->req8.mapping_ctr.num_mappings= mappings == NULL ? 0 : mappings->num_mappings;
559 r->in.req->req8.mapping_ctr.mappings = mappings == NULL ? NULL : mappings->mappings;
560 } else {
561 r->in.level = 5;
562 r->in.req->req5.destination_dsa_guid = service->ntds_guid;
563 r->in.req->req5.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
564 r->in.req->req5.naming_context = &partition->nc;
565 r->in.req->req5.highwatermark = highwatermark;
566 r->in.req->req5.uptodateness_vector = uptodateness_vector;
567 r->in.req->req5.replica_flags = replica_flags;
568 r->in.req->req5.max_object_count = 133;
569 r->in.req->req5.max_ndr_size = 1336770;
570 r->in.req->req5.extended_op = state->op->extended_op;
571 r->in.req->req5.fsmo_info = state->op->fsmo_info;
574 #if 0
575 NDR_PRINT_IN_DEBUG(drsuapi_DsGetNCChanges, r);
576 #endif
578 state->ndr_struct_ptr = r;
579 subreq = dcerpc_drsuapi_DsGetNCChanges_r_send(state,
580 state->ev,
581 drsuapi->drsuapi_handle,
583 if (tevent_req_nomem(subreq, req)) {
584 return;
586 tevent_req_set_callback(subreq, dreplsrv_op_pull_source_get_changes_done, req);
589 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
590 struct drsuapi_DsGetNCChanges *r,
591 uint32_t ctr_level,
592 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
593 struct drsuapi_DsGetNCChangesCtr6 *ctr6);
595 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq)
597 struct tevent_req *req = tevent_req_callback_data(subreq,
598 struct tevent_req);
599 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
600 struct dreplsrv_op_pull_source_state);
601 NTSTATUS status;
602 struct drsuapi_DsGetNCChanges *r = talloc_get_type(state->ndr_struct_ptr,
603 struct drsuapi_DsGetNCChanges);
604 uint32_t ctr_level = 0;
605 struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
606 struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
607 enum drsuapi_DsExtendedError extended_ret = DRSUAPI_EXOP_ERR_NONE;
608 state->ndr_struct_ptr = NULL;
610 status = dcerpc_drsuapi_DsGetNCChanges_r_recv(subreq, r);
611 TALLOC_FREE(subreq);
612 if (tevent_req_nterror(req, status)) {
613 return;
616 if (!W_ERROR_IS_OK(r->out.result)) {
617 status = werror_to_ntstatus(r->out.result);
618 tevent_req_nterror(req, status);
619 return;
622 if (*r->out.level_out == 1) {
623 ctr_level = 1;
624 ctr1 = &r->out.ctr->ctr1;
625 } else if (*r->out.level_out == 2 &&
626 r->out.ctr->ctr2.mszip1.ts) {
627 ctr_level = 1;
628 ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
629 } else if (*r->out.level_out == 6) {
630 ctr_level = 6;
631 ctr6 = &r->out.ctr->ctr6;
632 } else if (*r->out.level_out == 7 &&
633 r->out.ctr->ctr7.level == 6 &&
634 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP &&
635 r->out.ctr->ctr7.ctr.mszip6.ts) {
636 ctr_level = 6;
637 ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
638 } else if (*r->out.level_out == 7 &&
639 r->out.ctr->ctr7.level == 6 &&
640 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
641 r->out.ctr->ctr7.ctr.xpress6.ts) {
642 ctr_level = 6;
643 ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
644 } else {
645 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
646 tevent_req_nterror(req, status);
647 return;
650 if (!ctr1 && !ctr6) {
651 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
652 tevent_req_nterror(req, status);
653 return;
656 if (ctr_level == 6) {
657 if (!W_ERROR_IS_OK(ctr6->drs_error)) {
658 status = werror_to_ntstatus(ctr6->drs_error);
659 tevent_req_nterror(req, status);
660 return;
662 extended_ret = ctr6->extended_ret;
665 if (ctr_level == 1) {
666 extended_ret = ctr1->extended_ret;
669 if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
670 state->op->extended_ret = extended_ret;
672 if (extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
673 status = NT_STATUS_UNSUCCESSFUL;
674 tevent_req_nterror(req, status);
675 return;
679 dreplsrv_op_pull_source_apply_changes_trigger(req, r, ctr_level, ctr1, ctr6);
682 static void dreplsrv_update_refs_trigger(struct tevent_req *req);
684 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
685 struct drsuapi_DsGetNCChanges *r,
686 uint32_t ctr_level,
687 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
688 struct drsuapi_DsGetNCChangesCtr6 *ctr6)
690 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
691 struct dreplsrv_op_pull_source_state);
692 struct repsFromTo1 rf1 = *state->op->source_dsa->repsFrom1;
693 struct dreplsrv_service *service = state->op->service;
694 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
695 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
696 struct ldb_dn *schema_dn = ldb_get_schema_basedn(service->samdb);
697 struct dsdb_schema *schema;
698 struct dsdb_schema *working_schema = NULL;
699 const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
700 uint32_t object_count;
701 struct drsuapi_DsReplicaObjectListItemEx *first_object;
702 uint32_t linked_attributes_count;
703 struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
704 const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
705 struct dsdb_extended_replicated_objects *objects;
706 bool more_data = false;
707 WERROR status;
708 NTSTATUS nt_status;
709 uint32_t dsdb_repl_flags = 0;
710 struct ldb_dn *nc_root = NULL;
711 int ret;
713 switch (ctr_level) {
714 case 1:
715 mapping_ctr = &ctr1->mapping_ctr;
716 object_count = ctr1->object_count;
717 first_object = ctr1->first_object;
718 linked_attributes_count = 0;
719 linked_attributes = NULL;
720 rf1.source_dsa_obj_guid = ctr1->source_dsa_guid;
721 rf1.source_dsa_invocation_id = ctr1->source_dsa_invocation_id;
722 rf1.highwatermark = ctr1->new_highwatermark;
723 uptodateness_vector = NULL; /* TODO: map it */
724 more_data = ctr1->more_data;
725 break;
726 case 6:
727 mapping_ctr = &ctr6->mapping_ctr;
728 object_count = ctr6->object_count;
729 first_object = ctr6->first_object;
730 linked_attributes_count = ctr6->linked_attributes_count;
731 linked_attributes = ctr6->linked_attributes;
732 rf1.source_dsa_obj_guid = ctr6->source_dsa_guid;
733 rf1.source_dsa_invocation_id = ctr6->source_dsa_invocation_id;
734 rf1.highwatermark = ctr6->new_highwatermark;
735 uptodateness_vector = ctr6->uptodateness_vector;
736 more_data = ctr6->more_data;
737 break;
738 default:
739 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
740 tevent_req_nterror(req, nt_status);
741 return;
744 schema = dsdb_get_schema(service->samdb, state);
745 if (!schema) {
746 DEBUG(0,(__location__ ": Schema is not loaded yet!\n"));
747 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
748 return;
752 * Decide what working schema to use for object conversion.
753 * We won't need a working schema for empty replicas sent.
755 if (first_object) {
756 bool is_schema = ldb_dn_compare(partition->dn, schema_dn) == 0;
757 if (is_schema) {
758 /* create working schema to convert objects with */
759 status = dsdb_repl_make_working_schema(service->samdb,
760 schema,
761 mapping_ctr,
762 object_count,
763 first_object,
764 &drsuapi->gensec_skey,
765 state, &working_schema);
766 if (!W_ERROR_IS_OK(status)) {
767 DEBUG(0,("Failed to create working schema: %s\n",
768 win_errstr(status)));
769 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
770 return;
775 if (partition->partial_replica || partition->rodc_replica) {
776 dsdb_repl_flags |= DSDB_REPL_FLAG_PARTIAL_REPLICA;
778 if (state->op->options & DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS) {
779 dsdb_repl_flags |= DSDB_REPL_FLAG_PRIORITISE_INCOMING;
781 if (state->op->options & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
782 dsdb_repl_flags |= DSDB_REPL_FLAG_EXPECT_NO_SECRETS;
785 if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
786 ret = dsdb_find_nc_root(service->samdb, partition,
787 partition->dn, &nc_root);
788 if (ret != LDB_SUCCESS) {
789 DEBUG(0,(__location__ ": Failed to find nc_root for %s\n",
790 ldb_dn_get_linearized(partition->dn)));
791 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
792 return;
794 } else {
795 nc_root = partition->dn;
798 status = dsdb_replicated_objects_convert(service->samdb,
799 working_schema ? working_schema : schema,
800 nc_root,
801 mapping_ctr,
802 object_count,
803 first_object,
804 linked_attributes_count,
805 linked_attributes,
806 &rf1,
807 uptodateness_vector,
808 &drsuapi->gensec_skey,
809 dsdb_repl_flags,
810 state, &objects);
812 if (W_ERROR_EQUAL(status, WERR_DS_DRA_SCHEMA_MISMATCH)) {
813 struct dreplsrv_partition *p;
815 if (state->retry_started) {
816 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
817 DEBUG(0,("Failed to convert objects after retry: %s/%s\n",
818 win_errstr(status), nt_errstr(nt_status)));
819 tevent_req_nterror(req, nt_status);
820 return;
824 * Change info sync or extended operation into a fetch
825 * of the schema partition, so we get all the schema
826 * objects we need.
828 * We don't want to re-do the remote exop,
829 * unless it was REPL_SECRET so we set the
830 * fallback operation to just be a fetch of
831 * the relevent partition.
835 if (state->op->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
836 state->extended_op_retry = state->op->extended_op;
837 } else {
838 state->extended_op_retry = DRSUAPI_EXOP_NONE;
840 state->op->extended_op = DRSUAPI_EXOP_NONE;
842 if (ldb_dn_compare(nc_root, partition->dn) == 0) {
843 state->source_dsa_retry = state->op->source_dsa;
844 } else {
845 status = dreplsrv_partition_find_for_nc(service,
846 NULL, NULL,
847 ldb_dn_get_linearized(nc_root),
848 &p);
849 if (!W_ERROR_IS_OK(status)) {
850 DEBUG(2, ("Failed to find requested Naming Context for %s: %s",
851 ldb_dn_get_linearized(nc_root),
852 win_errstr(status)));
853 nt_status = werror_to_ntstatus(status);
854 tevent_req_nterror(req, nt_status);
855 return;
857 status = dreplsrv_partition_source_dsa_by_guid(p,
858 &state->op->source_dsa->repsFrom1->source_dsa_obj_guid,
859 &state->source_dsa_retry);
861 if (!W_ERROR_IS_OK(status)) {
862 struct GUID_txt_buf str;
863 DEBUG(2, ("Failed to find requested source DSA for %s and %s: %s",
864 ldb_dn_get_linearized(nc_root),
865 GUID_buf_string(&state->op->source_dsa->repsFrom1->source_dsa_obj_guid, &str),
866 win_errstr(status)));
867 nt_status = werror_to_ntstatus(status);
868 tevent_req_nterror(req, nt_status);
869 return;
873 /* Find schema naming context to be synchronized first */
874 status = dreplsrv_partition_find_for_nc(service,
875 NULL, NULL,
876 ldb_dn_get_linearized(schema_dn),
877 &p);
878 if (!W_ERROR_IS_OK(status)) {
879 DEBUG(2, ("Failed to find requested Naming Context for schema: %s",
880 win_errstr(status)));
881 nt_status = werror_to_ntstatus(status);
882 tevent_req_nterror(req, nt_status);
883 return;
886 status = dreplsrv_partition_source_dsa_by_guid(p,
887 &state->op->source_dsa->repsFrom1->source_dsa_obj_guid,
888 &state->op->source_dsa);
889 if (!W_ERROR_IS_OK(status)) {
890 struct GUID_txt_buf str;
891 DEBUG(2, ("Failed to find requested source DSA for %s and %s: %s",
892 ldb_dn_get_linearized(schema_dn),
893 GUID_buf_string(&state->op->source_dsa->repsFrom1->source_dsa_obj_guid, &str),
894 win_errstr(status)));
895 nt_status = werror_to_ntstatus(status);
896 tevent_req_nterror(req, nt_status);
897 return;
899 DEBUG(4,("Wrong schema when applying reply GetNCChanges, retrying\n"));
901 state->retry_started = true;
902 dreplsrv_op_pull_source_get_changes_trigger(req);
903 return;
905 } else if (!W_ERROR_IS_OK(status)) {
906 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
907 DEBUG(0,("Failed to convert objects: %s/%s\n",
908 win_errstr(status), nt_errstr(nt_status)));
909 tevent_req_nterror(req, nt_status);
910 return;
913 status = dsdb_replicated_objects_commit(service->samdb,
914 working_schema,
915 objects,
916 &state->op->source_dsa->notify_uSN);
917 talloc_free(objects);
919 if (!W_ERROR_IS_OK(status)) {
922 * If we failed to apply the records due to a missing
923 * parent, try again after asking for the parent
924 * records first. Because we don't update the
925 * highwatermark, we start this part of the cycle
926 * again.
928 if (((state->op->options & DRSUAPI_DRS_GET_ANC) == 0)
929 && W_ERROR_EQUAL(status, WERR_DS_DRA_MISSING_PARENT)) {
930 state->op->options |= DRSUAPI_DRS_GET_ANC;
931 DEBUG(4,("Missing parent object when we didn't set the DRSUAPI_DRS_GET_ANC flag, retrying\n"));
932 dreplsrv_op_pull_source_get_changes_trigger(req);
933 return;
934 } else if (((state->op->options & DRSUAPI_DRS_GET_ANC))
935 && W_ERROR_EQUAL(status, WERR_DS_DRA_MISSING_PARENT)) {
936 DEBUG(1,("Missing parent object despite setting DRSUAPI_DRS_GET_ANC flag\n"));
937 nt_status = NT_STATUS_INVALID_NETWORK_RESPONSE;
938 } else {
939 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
941 DEBUG(0,("Failed to commit objects: %s/%s\n",
942 win_errstr(status), nt_errstr(nt_status)));
943 tevent_req_nterror(req, nt_status);
944 return;
947 if (state->op->extended_op == DRSUAPI_EXOP_NONE) {
948 /* if it applied fine, we need to update the highwatermark */
949 *state->op->source_dsa->repsFrom1 = rf1;
952 /* we don't need this maybe very large structure anymore */
953 TALLOC_FREE(r);
955 if (more_data) {
956 dreplsrv_op_pull_source_get_changes_trigger(req);
957 return;
961 * If we had to divert via doing some other thing, such as
962 * pulling the schema, then go back and do the original
963 * operation once we are done.
965 if (state->source_dsa_retry != NULL) {
966 state->op->source_dsa = state->source_dsa_retry;
967 state->op->extended_op = state->extended_op_retry;
968 state->source_dsa_retry = NULL;
969 dreplsrv_op_pull_source_get_changes_trigger(req);
970 return;
973 if (state->op->extended_op != DRSUAPI_EXOP_NONE ||
974 state->op->service->am_rodc) {
976 we don't do the UpdateRefs for extended ops or if we
977 are a RODC
979 tevent_req_done(req);
980 return;
983 /* now we need to update the repsTo record for this partition
984 on the server. These records are initially established when
985 we join the domain, but they quickly expire. We do it here
986 so we can use the already established DRSUAPI pipe
988 dreplsrv_update_refs_trigger(req);
991 static void dreplsrv_update_refs_done(struct tevent_req *subreq);
994 send a UpdateRefs request to refresh our repsTo record on the server
996 static void dreplsrv_update_refs_trigger(struct tevent_req *req)
998 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
999 struct dreplsrv_op_pull_source_state);
1000 struct dreplsrv_service *service = state->op->service;
1001 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
1002 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
1003 struct drsuapi_DsReplicaUpdateRefs *r;
1004 char *ntds_dns_name;
1005 struct tevent_req *subreq;
1007 r = talloc(state, struct drsuapi_DsReplicaUpdateRefs);
1008 if (tevent_req_nomem(r, req)) {
1009 return;
1012 ntds_dns_name = samdb_ntds_msdcs_dns_name(service->samdb, r, &service->ntds_guid);
1013 if (tevent_req_nomem(ntds_dns_name, req)) {
1014 talloc_free(r);
1015 return;
1018 r->in.bind_handle = &drsuapi->bind_handle;
1019 r->in.level = 1;
1020 r->in.req.req1.naming_context = &partition->nc;
1021 r->in.req.req1.dest_dsa_dns_name = ntds_dns_name;
1022 r->in.req.req1.dest_dsa_guid = service->ntds_guid;
1023 r->in.req.req1.options = DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_DEL_REF;
1024 if (!service->am_rodc) {
1025 r->in.req.req1.options |= DRSUAPI_DRS_WRIT_REP;
1028 state->ndr_struct_ptr = r;
1029 subreq = dcerpc_drsuapi_DsReplicaUpdateRefs_r_send(state,
1030 state->ev,
1031 drsuapi->drsuapi_handle,
1033 if (tevent_req_nomem(subreq, req)) {
1034 talloc_free(r);
1035 return;
1037 tevent_req_set_callback(subreq, dreplsrv_update_refs_done, req);
1041 receive a UpdateRefs reply
1043 static void dreplsrv_update_refs_done(struct tevent_req *subreq)
1045 struct tevent_req *req = tevent_req_callback_data(subreq,
1046 struct tevent_req);
1047 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
1048 struct dreplsrv_op_pull_source_state);
1049 struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(state->ndr_struct_ptr,
1050 struct drsuapi_DsReplicaUpdateRefs);
1051 NTSTATUS status;
1053 state->ndr_struct_ptr = NULL;
1055 status = dcerpc_drsuapi_DsReplicaUpdateRefs_r_recv(subreq, r);
1056 TALLOC_FREE(subreq);
1057 if (!NT_STATUS_IS_OK(status)) {
1058 DEBUG(0,("UpdateRefs failed with %s\n",
1059 nt_errstr(status)));
1060 tevent_req_nterror(req, status);
1061 return;
1064 if (!W_ERROR_IS_OK(r->out.result)) {
1065 status = werror_to_ntstatus(r->out.result);
1066 DEBUG(0,("UpdateRefs failed with %s/%s for %s %s\n",
1067 win_errstr(r->out.result),
1068 nt_errstr(status),
1069 r->in.req.req1.dest_dsa_dns_name,
1070 r->in.req.req1.naming_context->dn));
1072 * TODO we are currently not sending the
1073 * DsReplicaUpdateRefs at the correct moment,
1074 * we do it just after a GetNcChanges which is
1075 * not always correct.
1076 * Especially when another DC is trying to demote
1077 * it will sends us a DsReplicaSync that will trigger a getNcChanges
1078 * this call will succeed but the DsRecplicaUpdateRefs that we send
1079 * just after will not because the DC is in a demote state and
1080 * will reply us a WERR_DS_DRA_BUSY, this error will cause us to
1081 * answer to the DsReplicaSync with a non OK status, the other DC
1082 * will stop the demote due to this error.
1083 * In order to cope with this we will for the moment concider
1084 * a DS_DRA_BUSY not as an error.
1085 * It's not ideal but it should not have a too huge impact for
1086 * running production as this error otherwise never happen and
1087 * due to the fact the send a DsReplicaUpdateRefs after each getNcChanges
1089 if (!W_ERROR_EQUAL(r->out.result, WERR_DS_DRA_BUSY)) {
1090 tevent_req_nterror(req, status);
1091 return;
1095 DEBUG(4,("UpdateRefs OK for %s %s\n",
1096 r->in.req.req1.dest_dsa_dns_name,
1097 r->in.req.req1.naming_context->dn));
1099 tevent_req_done(req);
1102 WERROR dreplsrv_op_pull_source_recv(struct tevent_req *req)
1104 NTSTATUS status;
1106 if (tevent_req_is_nterror(req, &status)) {
1107 tevent_req_received(req);
1108 return ntstatus_to_werror(status);
1111 tevent_req_received(req);
1112 return WERR_OK;