s4:dsdb/repl: make use of explicit dcerpc_*_recv functions
[Samba/kamenim.git] / source4 / dsdb / repl / drepl_out_helpers.c
blobf80f707322900f29ac7f4874ca2623247aa1dea9
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 "lib/messaging/irpc.h"
28 #include "dsdb/repl/drepl_service.h"
29 #include "lib/ldb/include/ldb_errors.h"
30 #include "../lib/util/dlinklist.h"
31 #include "librpc/gen_ndr/ndr_misc.h"
32 #include "librpc/gen_ndr/ndr_drsuapi.h"
33 #include "librpc/gen_ndr/ndr_drsblobs.h"
34 #include "libcli/composite/composite.h"
35 #include "auth/gensec/gensec.h"
36 #include "param/param.h"
37 #include "../lib/util/tevent_ntstatus.h"
39 struct dreplsrv_out_drsuapi_state {
40 struct dreplsrv_out_connection *conn;
42 struct dreplsrv_drsuapi_connection *drsuapi;
44 struct drsuapi_DsBindInfoCtr bind_info_ctr;
45 struct drsuapi_DsBind bind_r;
48 static void dreplsrv_out_drsuapi_connect_done(struct composite_context *creq);
50 struct tevent_req *dreplsrv_out_drsuapi_send(TALLOC_CTX *mem_ctx,
51 struct tevent_context *ev,
52 struct dreplsrv_out_connection *conn)
54 struct tevent_req *req;
55 struct dreplsrv_out_drsuapi_state *state;
56 struct composite_context *creq;
58 req = tevent_req_create(mem_ctx, &state,
59 struct dreplsrv_out_drsuapi_state);
60 if (req == NULL) {
61 return NULL;
64 state->conn = conn;
65 state->drsuapi = conn->drsuapi;
67 if (state->drsuapi && !state->drsuapi->pipe->conn->dead) {
68 tevent_req_done(req);
69 return tevent_req_post(req, ev);
72 if (state->drsuapi && state->drsuapi->pipe->conn->dead) {
73 talloc_free(state->drsuapi);
74 conn->drsuapi = NULL;
77 state->drsuapi = talloc_zero(state, struct dreplsrv_drsuapi_connection);
78 if (tevent_req_nomem(state->drsuapi, req)) {
79 return tevent_req_post(req, ev);
82 creq = dcerpc_pipe_connect_b_send(state, conn->binding, &ndr_table_drsuapi,
83 conn->service->system_session_info->credentials,
84 ev, conn->service->task->lp_ctx);
85 if (tevent_req_nomem(creq, req)) {
86 return tevent_req_post(req, ev);
88 composite_continue(NULL, creq, dreplsrv_out_drsuapi_connect_done, req);
90 return req;
93 static void dreplsrv_out_drsuapi_bind_done(struct rpc_request *rreq);
95 static void dreplsrv_out_drsuapi_connect_done(struct composite_context *creq)
97 struct tevent_req *req = talloc_get_type(creq->async.private_data,
98 struct tevent_req);
99 struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
100 struct dreplsrv_out_drsuapi_state);
101 NTSTATUS status;
102 struct rpc_request *rreq;
104 status = dcerpc_pipe_connect_b_recv(creq,
105 state->drsuapi,
106 &state->drsuapi->pipe);
107 if (tevent_req_nterror(req, status)) {
108 return;
111 status = gensec_session_key(state->drsuapi->pipe->conn->security_state.generic_state,
112 &state->drsuapi->gensec_skey);
113 if (tevent_req_nterror(req, status)) {
114 return;
117 state->bind_info_ctr.length = 28;
118 state->bind_info_ctr.info.info28 = state->conn->service->bind_info28;
120 state->bind_r.in.bind_guid = &state->conn->service->ntds_guid;
121 state->bind_r.in.bind_info = &state->bind_info_ctr;
122 state->bind_r.out.bind_handle = &state->drsuapi->bind_handle;
124 rreq = dcerpc_drsuapi_DsBind_send(state->drsuapi->pipe,
125 state,
126 &state->bind_r);
127 if (tevent_req_nomem(rreq, req)) {
128 return;
130 composite_continue_rpc(NULL, rreq, dreplsrv_out_drsuapi_bind_done, req);
133 static void dreplsrv_out_drsuapi_bind_done(struct rpc_request *rreq)
135 struct tevent_req *req = talloc_get_type(rreq->async.private_data,
136 struct tevent_req);
137 struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
138 struct dreplsrv_out_drsuapi_state);
139 NTSTATUS status;
141 status = dcerpc_drsuapi_DsBind_recv(rreq);
142 if (tevent_req_nterror(req, status)) {
143 return;
146 if (!W_ERROR_IS_OK(state->bind_r.out.result)) {
147 status = werror_to_ntstatus(state->bind_r.out.result);
148 tevent_req_nterror(req, status);
149 return;
152 ZERO_STRUCT(state->drsuapi->remote_info28);
153 if (state->bind_r.out.bind_info) {
154 struct drsuapi_DsBindInfo28 *info28;
155 info28 = &state->drsuapi->remote_info28;
157 switch (state->bind_r.out.bind_info->length) {
158 case 24: {
159 struct drsuapi_DsBindInfo24 *info24;
160 info24 = &state->bind_r.out.bind_info->info.info24;
162 info28->supported_extensions = info24->supported_extensions;
163 info28->site_guid = info24->site_guid;
164 info28->pid = info24->pid;
165 info28->repl_epoch = 0;
166 break;
168 case 48: {
169 struct drsuapi_DsBindInfo48 *info48;
170 info48 = &state->bind_r.out.bind_info->info.info48;
172 info28->supported_extensions = info48->supported_extensions;
173 info28->site_guid = info48->site_guid;
174 info28->pid = info48->pid;
175 info28->repl_epoch = info48->repl_epoch;
176 break;
178 case 28:
179 *info28 = state->bind_r.out.bind_info->info.info28;
180 break;
184 tevent_req_done(req);
187 NTSTATUS dreplsrv_out_drsuapi_recv(struct tevent_req *req)
189 struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
190 struct dreplsrv_out_drsuapi_state);
191 NTSTATUS status;
193 if (tevent_req_is_nterror(req, &status)) {
194 tevent_req_received(req);
195 return status;
198 state->conn->drsuapi = talloc_move(state->conn, &state->drsuapi);
200 tevent_req_received(req);
201 return NT_STATUS_OK;
204 struct dreplsrv_op_pull_source_state {
205 struct dreplsrv_out_operation *op;
208 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq);
210 struct tevent_req *dreplsrv_op_pull_source_send(TALLOC_CTX *mem_ctx,
211 struct tevent_context *ev,
212 struct dreplsrv_out_operation *op)
214 struct tevent_req *req;
215 struct dreplsrv_op_pull_source_state *state;
216 struct tevent_req *subreq;
218 req = tevent_req_create(mem_ctx, &state,
219 struct dreplsrv_op_pull_source_state);
220 if (req == NULL) {
221 return NULL;
224 state->op = op;
226 subreq = dreplsrv_out_drsuapi_send(state, ev, op->source_dsa->conn);
227 if (tevent_req_nomem(subreq, req)) {
228 return tevent_req_post(req, ev);
230 tevent_req_set_callback(subreq, dreplsrv_op_pull_source_connect_done, req);
232 return req;
235 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req);
237 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq)
239 struct tevent_req *req = tevent_req_callback_data(subreq,
240 struct tevent_req);
241 NTSTATUS status;
243 status = dreplsrv_out_drsuapi_recv(subreq);
244 TALLOC_FREE(subreq);
245 if (tevent_req_nterror(req, status)) {
246 return;
249 dreplsrv_op_pull_source_get_changes_trigger(req);
252 static void dreplsrv_op_pull_source_get_changes_done(struct rpc_request *rreq);
254 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req)
256 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
257 struct dreplsrv_op_pull_source_state);
258 struct repsFromTo1 *rf1 = state->op->source_dsa->repsFrom1;
259 struct dreplsrv_service *service = state->op->service;
260 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
261 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
262 struct rpc_request *rreq;
263 struct drsuapi_DsGetNCChanges *r;
264 struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector;
266 r = talloc(state, struct drsuapi_DsGetNCChanges);
267 if (tevent_req_nomem(r, req)) {
268 return;
271 r->out.level_out = talloc(r, int32_t);
272 if (tevent_req_nomem(r->out.level_out, req)) {
273 return;
275 r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest);
276 if (tevent_req_nomem(r->in.req, req)) {
277 return;
279 r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr);
280 if (tevent_req_nomem(r->out.ctr, req)) {
281 return;
284 if (partition->uptodatevector_ex.count == 0) {
285 uptodateness_vector = NULL;
286 } else {
287 uptodateness_vector = &partition->uptodatevector_ex;
290 r->in.bind_handle = &drsuapi->bind_handle;
291 if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
292 r->in.level = 8;
293 r->in.req->req8.destination_dsa_guid = service->ntds_guid;
294 r->in.req->req8.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
295 r->in.req->req8.naming_context = &partition->nc;
296 r->in.req->req8.highwatermark = rf1->highwatermark;
297 r->in.req->req8.uptodateness_vector = uptodateness_vector;
298 r->in.req->req8.replica_flags = rf1->replica_flags;
299 r->in.req->req8.max_object_count = 133;
300 r->in.req->req8.max_ndr_size = 1336811;
301 r->in.req->req8.extended_op = state->op->extended_op;
302 r->in.req->req8.fsmo_info = state->op->fsmo_info;
303 r->in.req->req8.partial_attribute_set = NULL;
304 r->in.req->req8.partial_attribute_set_ex= NULL;
305 r->in.req->req8.mapping_ctr.num_mappings= 0;
306 r->in.req->req8.mapping_ctr.mappings = NULL;
307 } else {
308 r->in.level = 5;
309 r->in.req->req5.destination_dsa_guid = service->ntds_guid;
310 r->in.req->req5.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
311 r->in.req->req5.naming_context = &partition->nc;
312 r->in.req->req5.highwatermark = rf1->highwatermark;
313 r->in.req->req5.uptodateness_vector = uptodateness_vector;
314 r->in.req->req5.replica_flags = rf1->replica_flags;
315 r->in.req->req5.max_object_count = 133;
316 r->in.req->req5.max_ndr_size = 1336770;
317 r->in.req->req5.extended_op = state->op->extended_op;
318 r->in.req->req5.fsmo_info = state->op->fsmo_info;
321 #if 0
322 NDR_PRINT_IN_DEBUG(drsuapi_DsGetNCChanges, r);
323 #endif
325 rreq = dcerpc_drsuapi_DsGetNCChanges_send(drsuapi->pipe, r, r);
326 if (tevent_req_nomem(rreq, req)) {
327 return;
329 composite_continue_rpc(NULL, rreq, dreplsrv_op_pull_source_get_changes_done, req);
332 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
333 struct drsuapi_DsGetNCChanges *r,
334 uint32_t ctr_level,
335 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
336 struct drsuapi_DsGetNCChangesCtr6 *ctr6);
338 static void dreplsrv_op_pull_source_get_changes_done(struct rpc_request *rreq)
340 struct tevent_req *req = talloc_get_type(rreq->async.private_data,
341 struct tevent_req);
342 NTSTATUS status;
343 struct drsuapi_DsGetNCChanges *r = talloc_get_type(rreq->ndr.struct_ptr,
344 struct drsuapi_DsGetNCChanges);
345 uint32_t ctr_level = 0;
346 struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
347 struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
349 status = dcerpc_drsuapi_DsGetNCChanges_recv(rreq);
350 if (tevent_req_nterror(req, status)) {
351 return;
354 if (!W_ERROR_IS_OK(r->out.result)) {
355 status = werror_to_ntstatus(r->out.result);
356 tevent_req_nterror(req, status);
357 return;
360 if (*r->out.level_out == 1) {
361 ctr_level = 1;
362 ctr1 = &r->out.ctr->ctr1;
363 } else if (*r->out.level_out == 2 &&
364 r->out.ctr->ctr2.mszip1.ts) {
365 ctr_level = 1;
366 ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
367 } else if (*r->out.level_out == 6) {
368 ctr_level = 6;
369 ctr6 = &r->out.ctr->ctr6;
370 } else if (*r->out.level_out == 7 &&
371 r->out.ctr->ctr7.level == 6 &&
372 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP &&
373 r->out.ctr->ctr7.ctr.mszip6.ts) {
374 ctr_level = 6;
375 ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
376 } else if (*r->out.level_out == 7 &&
377 r->out.ctr->ctr7.level == 6 &&
378 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
379 r->out.ctr->ctr7.ctr.xpress6.ts) {
380 ctr_level = 6;
381 ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
382 } else {
383 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
384 tevent_req_nterror(req, status);
385 return;
388 if (!ctr1 && !ctr6) {
389 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
390 tevent_req_nterror(req, status);
391 return;
394 if (ctr_level == 6) {
395 if (!W_ERROR_IS_OK(ctr6->drs_error)) {
396 status = werror_to_ntstatus(ctr6->drs_error);
397 tevent_req_nterror(req, status);
398 return;
402 dreplsrv_op_pull_source_apply_changes_trigger(req, r, ctr_level, ctr1, ctr6);
405 static void dreplsrv_update_refs_trigger(struct tevent_req *req);
407 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
408 struct drsuapi_DsGetNCChanges *r,
409 uint32_t ctr_level,
410 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
411 struct drsuapi_DsGetNCChangesCtr6 *ctr6)
413 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
414 struct dreplsrv_op_pull_source_state);
415 struct repsFromTo1 rf1 = *state->op->source_dsa->repsFrom1;
416 struct dreplsrv_service *service = state->op->service;
417 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
418 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
419 const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
420 uint32_t object_count;
421 struct drsuapi_DsReplicaObjectListItemEx *first_object;
422 uint32_t linked_attributes_count;
423 struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
424 const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
425 struct dsdb_extended_replicated_objects *objects;
426 bool more_data = false;
427 WERROR status;
428 NTSTATUS nt_status;
430 switch (ctr_level) {
431 case 1:
432 mapping_ctr = &ctr1->mapping_ctr;
433 object_count = ctr1->object_count;
434 first_object = ctr1->first_object;
435 linked_attributes_count = 0;
436 linked_attributes = NULL;
437 rf1.highwatermark = ctr1->new_highwatermark;
438 uptodateness_vector = NULL; /* TODO: map it */
439 more_data = ctr1->more_data;
440 break;
441 case 6:
442 mapping_ctr = &ctr6->mapping_ctr;
443 object_count = ctr6->object_count;
444 first_object = ctr6->first_object;
445 linked_attributes_count = ctr6->linked_attributes_count;
446 linked_attributes = ctr6->linked_attributes;
447 rf1.highwatermark = ctr6->new_highwatermark;
448 uptodateness_vector = ctr6->uptodateness_vector;
449 more_data = ctr6->more_data;
450 break;
451 default:
452 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
453 tevent_req_nterror(req, nt_status);
454 return;
457 status = dsdb_extended_replicated_objects_convert(service->samdb,
458 partition->nc.dn,
459 mapping_ctr,
460 object_count,
461 first_object,
462 linked_attributes_count,
463 linked_attributes,
464 &rf1,
465 uptodateness_vector,
466 &drsuapi->gensec_skey,
467 state, &objects);
468 if (!W_ERROR_IS_OK(status)) {
469 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
470 DEBUG(0,("Failed to convert objects: %s/%s\n",
471 win_errstr(status), nt_errstr(nt_status)));
472 tevent_req_nterror(req, nt_status);
473 return;
476 status = dsdb_extended_replicated_objects_commit(service->samdb,
477 objects,
478 &state->op->source_dsa->notify_uSN);
479 talloc_free(objects);
480 if (!W_ERROR_IS_OK(status)) {
481 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
482 DEBUG(0,("Failed to commit objects: %s/%s\n",
483 win_errstr(status), nt_errstr(nt_status)));
484 tevent_req_nterror(req, nt_status);
485 return;
488 /* if it applied fine, we need to update the highwatermark */
489 *state->op->source_dsa->repsFrom1 = rf1;
492 * TODO: update our uptodatevector!
495 /* we don't need this maybe very large structure anymore */
496 TALLOC_FREE(r);
498 if (more_data) {
499 dreplsrv_op_pull_source_get_changes_trigger(req);
500 return;
503 /* now we need to update the repsTo record for this partition
504 on the server. These records are initially established when
505 we join the domain, but they quickly expire. We do it here
506 so we can use the already established DRSUAPI pipe
508 dreplsrv_update_refs_trigger(req);
511 static void dreplsrv_update_refs_done(struct rpc_request *rreq);
514 send a UpdateRefs request to refresh our repsTo record on the server
516 static void dreplsrv_update_refs_trigger(struct tevent_req *req)
518 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
519 struct dreplsrv_op_pull_source_state);
520 struct dreplsrv_service *service = state->op->service;
521 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
522 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
523 struct rpc_request *rreq;
524 struct drsuapi_DsReplicaUpdateRefs *r;
525 char *ntds_guid_str;
526 char *ntds_dns_name;
528 r = talloc(state, struct drsuapi_DsReplicaUpdateRefs);
529 if (tevent_req_nomem(r, req)) {
530 return;
533 ntds_guid_str = GUID_string(r, &service->ntds_guid);
534 if (tevent_req_nomem(ntds_guid_str, req)) {
535 return;
538 ntds_dns_name = talloc_asprintf(r, "%s._msdcs.%s",
539 ntds_guid_str,
540 lp_dnsdomain(service->task->lp_ctx));
541 if (tevent_req_nomem(ntds_dns_name, req)) {
542 return;
545 r->in.bind_handle = &drsuapi->bind_handle;
546 r->in.level = 1;
547 r->in.req.req1.naming_context = &partition->nc;
548 r->in.req.req1.dest_dsa_dns_name = ntds_dns_name;
549 r->in.req.req1.dest_dsa_guid = service->ntds_guid;
550 r->in.req.req1.options = DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_DEL_REF;
551 if (!samdb_rodc(service->samdb)) {
552 r->in.req.req1.options |= DRSUAPI_DRS_WRIT_REP;
555 rreq = dcerpc_drsuapi_DsReplicaUpdateRefs_send(drsuapi->pipe, r, r);
556 if (tevent_req_nomem(rreq, req)) {
557 return;
559 composite_continue_rpc(NULL, rreq, dreplsrv_update_refs_done, req);
563 receive a UpdateRefs reply
565 static void dreplsrv_update_refs_done(struct rpc_request *rreq)
567 struct tevent_req *req = talloc_get_type(rreq->async.private_data,
568 struct tevent_req);
569 struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(rreq->ndr.struct_ptr,
570 struct drsuapi_DsReplicaUpdateRefs);
571 NTSTATUS status;
573 status = dcerpc_drsuapi_DsReplicaUpdateRefs_recv(rreq);
574 if (!NT_STATUS_IS_OK(status)) {
575 DEBUG(0,("UpdateRefs failed with %s\n",
576 nt_errstr(status)));
577 tevent_req_nterror(req, status);
578 return;
581 if (!W_ERROR_IS_OK(r->out.result)) {
582 status = werror_to_ntstatus(r->out.result);
583 DEBUG(0,("UpdateRefs failed with %s/%s for %s %s\n",
584 win_errstr(r->out.result),
585 nt_errstr(status),
586 r->in.req.req1.dest_dsa_dns_name,
587 r->in.req.req1.naming_context->dn));
588 tevent_req_nterror(req, status);
589 return;
592 DEBUG(4,("UpdateRefs OK for %s %s\n",
593 r->in.req.req1.dest_dsa_dns_name,
594 r->in.req.req1.naming_context->dn));
596 tevent_req_done(req);
599 WERROR dreplsrv_op_pull_source_recv(struct tevent_req *req)
601 NTSTATUS status;
603 if (tevent_req_is_nterror(req, &status)) {
604 tevent_req_received(req);
605 return ntstatus_to_werror(status);
608 tevent_req_received(req);
609 return WERR_OK;