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 "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 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);
68 state->drsuapi = conn->drsuapi;
70 if (state->drsuapi && !state->drsuapi->pipe->conn->dead) {
72 return tevent_req_post(req, ev);
75 if (state->drsuapi && state->drsuapi->pipe->conn->dead) {
76 talloc_free(state->drsuapi);
80 state->drsuapi = talloc_zero(state, struct dreplsrv_drsuapi_connection);
81 if (tevent_req_nomem(state->drsuapi, req)) {
82 return tevent_req_post(req, ev);
85 creq = dcerpc_pipe_connect_b_send(state, conn->binding, &ndr_table_drsuapi,
86 conn->service->system_session_info->credentials,
87 ev, conn->service->task->lp_ctx);
88 if (tevent_req_nomem(creq, req)) {
89 return tevent_req_post(req, ev);
91 composite_continue(NULL, creq, dreplsrv_out_drsuapi_connect_done, req);
96 static void dreplsrv_out_drsuapi_bind_done(struct tevent_req *subreq);
98 static void dreplsrv_out_drsuapi_connect_done(struct composite_context *creq)
100 struct tevent_req *req = talloc_get_type(creq->async.private_data,
102 struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
103 struct dreplsrv_out_drsuapi_state);
105 struct tevent_req *subreq;
107 status = dcerpc_pipe_connect_b_recv(creq,
109 &state->drsuapi->pipe);
110 if (tevent_req_nterror(req, status)) {
114 state->drsuapi->drsuapi_handle = state->drsuapi->pipe->binding_handle;
116 status = gensec_session_key(state->drsuapi->pipe->conn->security_state.generic_state,
117 &state->drsuapi->gensec_skey);
118 if (tevent_req_nterror(req, status)) {
122 state->bind_info_ctr.length = 28;
123 state->bind_info_ctr.info.info28 = state->conn->service->bind_info28;
125 state->bind_r.in.bind_guid = &state->conn->service->ntds_guid;
126 state->bind_r.in.bind_info = &state->bind_info_ctr;
127 state->bind_r.out.bind_handle = &state->drsuapi->bind_handle;
129 subreq = dcerpc_drsuapi_DsBind_r_send(state,
131 state->drsuapi->drsuapi_handle,
133 if (tevent_req_nomem(subreq, req)) {
136 tevent_req_set_callback(subreq, dreplsrv_out_drsuapi_bind_done, req);
139 static void dreplsrv_out_drsuapi_bind_done(struct tevent_req *subreq)
141 struct tevent_req *req = tevent_req_callback_data(subreq,
143 struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
144 struct dreplsrv_out_drsuapi_state);
147 status = dcerpc_drsuapi_DsBind_r_recv(subreq, state);
149 if (tevent_req_nterror(req, status)) {
153 if (!W_ERROR_IS_OK(state->bind_r.out.result)) {
154 status = werror_to_ntstatus(state->bind_r.out.result);
155 tevent_req_nterror(req, status);
159 ZERO_STRUCT(state->drsuapi->remote_info28);
160 if (state->bind_r.out.bind_info) {
161 struct drsuapi_DsBindInfo28 *info28;
162 info28 = &state->drsuapi->remote_info28;
164 switch (state->bind_r.out.bind_info->length) {
166 struct drsuapi_DsBindInfo24 *info24;
167 info24 = &state->bind_r.out.bind_info->info.info24;
169 info28->supported_extensions = info24->supported_extensions;
170 info28->site_guid = info24->site_guid;
171 info28->pid = info24->pid;
172 info28->repl_epoch = 0;
176 struct drsuapi_DsBindInfo48 *info48;
177 info48 = &state->bind_r.out.bind_info->info.info48;
179 info28->supported_extensions = info48->supported_extensions;
180 info28->site_guid = info48->site_guid;
181 info28->pid = info48->pid;
182 info28->repl_epoch = info48->repl_epoch;
186 *info28 = state->bind_r.out.bind_info->info.info28;
191 tevent_req_done(req);
194 NTSTATUS dreplsrv_out_drsuapi_recv(struct tevent_req *req)
196 struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
197 struct dreplsrv_out_drsuapi_state);
200 if (tevent_req_is_nterror(req, &status)) {
201 tevent_req_received(req);
205 state->conn->drsuapi = talloc_move(state->conn, &state->drsuapi);
207 tevent_req_received(req);
211 struct dreplsrv_op_pull_source_state {
212 struct tevent_context *ev;
213 struct dreplsrv_out_operation *op;
214 void *ndr_struct_ptr;
217 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq);
219 struct tevent_req *dreplsrv_op_pull_source_send(TALLOC_CTX *mem_ctx,
220 struct tevent_context *ev,
221 struct dreplsrv_out_operation *op)
223 struct tevent_req *req;
224 struct dreplsrv_op_pull_source_state *state;
225 struct tevent_req *subreq;
227 req = tevent_req_create(mem_ctx, &state,
228 struct dreplsrv_op_pull_source_state);
235 subreq = dreplsrv_out_drsuapi_send(state, ev, op->source_dsa->conn);
236 if (tevent_req_nomem(subreq, req)) {
237 return tevent_req_post(req, ev);
239 tevent_req_set_callback(subreq, dreplsrv_op_pull_source_connect_done, req);
244 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req);
246 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq)
248 struct tevent_req *req = tevent_req_callback_data(subreq,
252 status = dreplsrv_out_drsuapi_recv(subreq);
254 if (tevent_req_nterror(req, status)) {
258 dreplsrv_op_pull_source_get_changes_trigger(req);
261 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq);
263 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req)
265 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
266 struct dreplsrv_op_pull_source_state);
267 struct repsFromTo1 *rf1 = state->op->source_dsa->repsFrom1;
268 struct dreplsrv_service *service = state->op->service;
269 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
270 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
271 struct drsuapi_DsGetNCChanges *r;
272 struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector;
273 struct tevent_req *subreq;
275 if ((rf1->replica_flags & DRSUAPI_DRS_WRIT_REP) == 0) {
279 r = talloc(state, struct drsuapi_DsGetNCChanges);
280 if (tevent_req_nomem(r, req)) {
284 r->out.level_out = talloc(r, uint32_t);
285 if (tevent_req_nomem(r->out.level_out, req)) {
288 r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest);
289 if (tevent_req_nomem(r->in.req, req)) {
292 r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr);
293 if (tevent_req_nomem(r->out.ctr, req)) {
297 if (partition->uptodatevector_ex.count == 0) {
298 uptodateness_vector = NULL;
300 uptodateness_vector = &partition->uptodatevector_ex;
303 r->in.bind_handle = &drsuapi->bind_handle;
304 if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
306 r->in.req->req8.destination_dsa_guid = service->ntds_guid;
307 r->in.req->req8.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
308 r->in.req->req8.naming_context = &partition->nc;
309 r->in.req->req8.highwatermark = rf1->highwatermark;
310 r->in.req->req8.uptodateness_vector = uptodateness_vector;
311 r->in.req->req8.replica_flags = rf1->replica_flags;
312 r->in.req->req8.max_object_count = 133;
313 r->in.req->req8.max_ndr_size = 1336811;
314 r->in.req->req8.extended_op = state->op->extended_op;
315 r->in.req->req8.fsmo_info = state->op->fsmo_info;
316 r->in.req->req8.partial_attribute_set = NULL;
317 r->in.req->req8.partial_attribute_set_ex= NULL;
318 r->in.req->req8.mapping_ctr.num_mappings= 0;
319 r->in.req->req8.mapping_ctr.mappings = NULL;
322 r->in.req->req5.destination_dsa_guid = service->ntds_guid;
323 r->in.req->req5.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
324 r->in.req->req5.naming_context = &partition->nc;
325 r->in.req->req5.highwatermark = rf1->highwatermark;
326 r->in.req->req5.uptodateness_vector = uptodateness_vector;
327 r->in.req->req5.replica_flags = rf1->replica_flags;
328 r->in.req->req5.max_object_count = 133;
329 r->in.req->req5.max_ndr_size = 1336770;
330 r->in.req->req5.extended_op = state->op->extended_op;
331 r->in.req->req5.fsmo_info = state->op->fsmo_info;
335 NDR_PRINT_IN_DEBUG(drsuapi_DsGetNCChanges, r);
338 state->ndr_struct_ptr = r;
339 subreq = dcerpc_drsuapi_DsGetNCChanges_r_send(state,
341 drsuapi->drsuapi_handle,
343 if (tevent_req_nomem(subreq, req)) {
346 tevent_req_set_callback(subreq, dreplsrv_op_pull_source_get_changes_done, req);
349 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
350 struct drsuapi_DsGetNCChanges *r,
352 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
353 struct drsuapi_DsGetNCChangesCtr6 *ctr6);
355 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq)
357 struct tevent_req *req = tevent_req_callback_data(subreq,
359 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
360 struct dreplsrv_op_pull_source_state);
362 struct drsuapi_DsGetNCChanges *r = talloc_get_type(state->ndr_struct_ptr,
363 struct drsuapi_DsGetNCChanges);
364 uint32_t ctr_level = 0;
365 struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
366 struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
368 state->ndr_struct_ptr = NULL;
370 status = dcerpc_drsuapi_DsGetNCChanges_r_recv(subreq, r);
372 if (tevent_req_nterror(req, status)) {
376 if (!W_ERROR_IS_OK(r->out.result)) {
377 status = werror_to_ntstatus(r->out.result);
378 tevent_req_nterror(req, status);
382 if (*r->out.level_out == 1) {
384 ctr1 = &r->out.ctr->ctr1;
385 } else if (*r->out.level_out == 2 &&
386 r->out.ctr->ctr2.mszip1.ts) {
388 ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
389 } else if (*r->out.level_out == 6) {
391 ctr6 = &r->out.ctr->ctr6;
392 } else if (*r->out.level_out == 7 &&
393 r->out.ctr->ctr7.level == 6 &&
394 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP &&
395 r->out.ctr->ctr7.ctr.mszip6.ts) {
397 ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
398 } else if (*r->out.level_out == 7 &&
399 r->out.ctr->ctr7.level == 6 &&
400 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
401 r->out.ctr->ctr7.ctr.xpress6.ts) {
403 ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
405 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
406 tevent_req_nterror(req, status);
410 if (!ctr1 && !ctr6) {
411 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
412 tevent_req_nterror(req, status);
416 if (ctr_level == 6) {
417 if (!W_ERROR_IS_OK(ctr6->drs_error)) {
418 status = werror_to_ntstatus(ctr6->drs_error);
419 tevent_req_nterror(req, status);
424 dreplsrv_op_pull_source_apply_changes_trigger(req, r, ctr_level, ctr1, ctr6);
427 static void dreplsrv_update_refs_trigger(struct tevent_req *req);
429 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
430 struct drsuapi_DsGetNCChanges *r,
432 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
433 struct drsuapi_DsGetNCChangesCtr6 *ctr6)
435 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
436 struct dreplsrv_op_pull_source_state);
437 struct repsFromTo1 rf1 = *state->op->source_dsa->repsFrom1;
438 struct dreplsrv_service *service = state->op->service;
439 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
440 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
441 const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
442 uint32_t object_count;
443 struct drsuapi_DsReplicaObjectListItemEx *first_object;
444 uint32_t linked_attributes_count;
445 struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
446 const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
447 struct dsdb_extended_replicated_objects *objects;
448 bool more_data = false;
454 mapping_ctr = &ctr1->mapping_ctr;
455 object_count = ctr1->object_count;
456 first_object = ctr1->first_object;
457 linked_attributes_count = 0;
458 linked_attributes = NULL;
459 rf1.highwatermark = ctr1->new_highwatermark;
460 uptodateness_vector = NULL; /* TODO: map it */
461 more_data = ctr1->more_data;
464 mapping_ctr = &ctr6->mapping_ctr;
465 object_count = ctr6->object_count;
466 first_object = ctr6->first_object;
467 linked_attributes_count = ctr6->linked_attributes_count;
468 linked_attributes = ctr6->linked_attributes;
469 rf1.highwatermark = ctr6->new_highwatermark;
470 uptodateness_vector = ctr6->uptodateness_vector;
471 more_data = ctr6->more_data;
474 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
475 tevent_req_nterror(req, nt_status);
479 status = dsdb_extended_replicated_objects_convert(service->samdb,
484 linked_attributes_count,
488 &drsuapi->gensec_skey,
490 if (!W_ERROR_IS_OK(status)) {
491 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
492 DEBUG(0,("Failed to convert objects: %s/%s\n",
493 win_errstr(status), nt_errstr(nt_status)));
494 tevent_req_nterror(req, nt_status);
498 status = dsdb_extended_replicated_objects_commit(service->samdb,
500 &state->op->source_dsa->notify_uSN);
501 talloc_free(objects);
502 if (!W_ERROR_IS_OK(status)) {
503 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
504 DEBUG(0,("Failed to commit objects: %s/%s\n",
505 win_errstr(status), nt_errstr(nt_status)));
506 tevent_req_nterror(req, nt_status);
510 /* if it applied fine, we need to update the highwatermark */
511 *state->op->source_dsa->repsFrom1 = rf1;
514 * TODO: update our uptodatevector!
517 /* we don't need this maybe very large structure anymore */
521 dreplsrv_op_pull_source_get_changes_trigger(req);
525 /* now we need to update the repsTo record for this partition
526 on the server. These records are initially established when
527 we join the domain, but they quickly expire. We do it here
528 so we can use the already established DRSUAPI pipe
530 if (state->op->extended_op == DRSUAPI_EXOP_NONE) {
531 dreplsrv_update_refs_trigger(req);
535 static void dreplsrv_update_refs_done(struct tevent_req *subreq);
538 send a UpdateRefs request to refresh our repsTo record on the server
540 static void dreplsrv_update_refs_trigger(struct tevent_req *req)
542 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
543 struct dreplsrv_op_pull_source_state);
544 struct dreplsrv_service *service = state->op->service;
545 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
546 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
547 struct drsuapi_DsReplicaUpdateRefs *r;
550 struct tevent_req *subreq;
554 r = talloc(state, struct drsuapi_DsReplicaUpdateRefs);
555 if (tevent_req_nomem(r, req)) {
559 ntds_guid_str = GUID_string(r, &service->ntds_guid);
560 if (tevent_req_nomem(ntds_guid_str, req)) {
564 ntds_dns_name = talloc_asprintf(r, "%s._msdcs.%s",
566 lp_dnsdomain(service->task->lp_ctx));
567 if (tevent_req_nomem(ntds_dns_name, req)) {
571 r->in.bind_handle = &drsuapi->bind_handle;
573 r->in.req.req1.naming_context = &partition->nc;
574 r->in.req.req1.dest_dsa_dns_name = ntds_dns_name;
575 r->in.req.req1.dest_dsa_guid = service->ntds_guid;
576 r->in.req.req1.options = DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_DEL_REF;
577 ret = samdb_rodc(service->samdb, &am_rodc);
578 if (ret == LDB_SUCCESS && !am_rodc) {
579 r->in.req.req1.options |= DRSUAPI_DRS_WRIT_REP;
582 state->ndr_struct_ptr = r;
583 subreq = dcerpc_drsuapi_DsReplicaUpdateRefs_r_send(state,
585 drsuapi->drsuapi_handle,
587 if (tevent_req_nomem(subreq, req)) {
590 tevent_req_set_callback(subreq, dreplsrv_update_refs_done, req);
594 receive a UpdateRefs reply
596 static void dreplsrv_update_refs_done(struct tevent_req *subreq)
598 struct tevent_req *req = tevent_req_callback_data(subreq,
600 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
601 struct dreplsrv_op_pull_source_state);
602 struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(state->ndr_struct_ptr,
603 struct drsuapi_DsReplicaUpdateRefs);
606 state->ndr_struct_ptr = NULL;
608 status = dcerpc_drsuapi_DsReplicaUpdateRefs_r_recv(subreq, r);
610 if (!NT_STATUS_IS_OK(status)) {
611 DEBUG(0,("UpdateRefs failed with %s\n",
613 tevent_req_nterror(req, status);
617 if (!W_ERROR_IS_OK(r->out.result)) {
618 status = werror_to_ntstatus(r->out.result);
619 DEBUG(0,("UpdateRefs failed with %s/%s for %s %s\n",
620 win_errstr(r->out.result),
622 r->in.req.req1.dest_dsa_dns_name,
623 r->in.req.req1.naming_context->dn));
624 tevent_req_nterror(req, status);
628 DEBUG(4,("UpdateRefs OK for %s %s\n",
629 r->in.req.req1.dest_dsa_dns_name,
630 r->in.req.req1.naming_context->dn));
632 tevent_req_done(req);
635 WERROR dreplsrv_op_pull_source_recv(struct tevent_req *req)
639 if (tevent_req_is_nterror(req, &status)) {
640 tevent_req_received(req);
641 return ntstatus_to_werror(status);
644 tevent_req_received(req);