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 &&
276 state->op->extended_op == DRSUAPI_EXOP_NONE) {
280 r = talloc(state, struct drsuapi_DsGetNCChanges);
281 if (tevent_req_nomem(r, req)) {
285 r->out.level_out = talloc(r, uint32_t);
286 if (tevent_req_nomem(r->out.level_out, req)) {
289 r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest);
290 if (tevent_req_nomem(r->in.req, req)) {
293 r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr);
294 if (tevent_req_nomem(r->out.ctr, req)) {
298 if (partition->uptodatevector_ex.count == 0) {
299 uptodateness_vector = NULL;
301 uptodateness_vector = &partition->uptodatevector_ex;
304 r->in.bind_handle = &drsuapi->bind_handle;
305 if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
307 r->in.req->req8.destination_dsa_guid = service->ntds_guid;
308 r->in.req->req8.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
309 r->in.req->req8.naming_context = &partition->nc;
310 r->in.req->req8.highwatermark = rf1->highwatermark;
311 r->in.req->req8.uptodateness_vector = uptodateness_vector;
312 r->in.req->req8.replica_flags = rf1->replica_flags;
313 r->in.req->req8.max_object_count = 133;
314 r->in.req->req8.max_ndr_size = 1336811;
315 r->in.req->req8.extended_op = state->op->extended_op;
316 r->in.req->req8.fsmo_info = state->op->fsmo_info;
317 r->in.req->req8.partial_attribute_set = NULL;
318 r->in.req->req8.partial_attribute_set_ex= NULL;
319 r->in.req->req8.mapping_ctr.num_mappings= 0;
320 r->in.req->req8.mapping_ctr.mappings = NULL;
323 r->in.req->req5.destination_dsa_guid = service->ntds_guid;
324 r->in.req->req5.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
325 r->in.req->req5.naming_context = &partition->nc;
326 r->in.req->req5.highwatermark = rf1->highwatermark;
327 r->in.req->req5.uptodateness_vector = uptodateness_vector;
328 r->in.req->req5.replica_flags = rf1->replica_flags;
329 r->in.req->req5.max_object_count = 133;
330 r->in.req->req5.max_ndr_size = 1336770;
331 r->in.req->req5.extended_op = state->op->extended_op;
332 r->in.req->req5.fsmo_info = state->op->fsmo_info;
336 NDR_PRINT_IN_DEBUG(drsuapi_DsGetNCChanges, r);
339 state->ndr_struct_ptr = r;
340 subreq = dcerpc_drsuapi_DsGetNCChanges_r_send(state,
342 drsuapi->drsuapi_handle,
344 if (tevent_req_nomem(subreq, req)) {
347 tevent_req_set_callback(subreq, dreplsrv_op_pull_source_get_changes_done, req);
350 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
351 struct drsuapi_DsGetNCChanges *r,
353 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
354 struct drsuapi_DsGetNCChangesCtr6 *ctr6);
356 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq)
358 struct tevent_req *req = tevent_req_callback_data(subreq,
360 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
361 struct dreplsrv_op_pull_source_state);
363 struct drsuapi_DsGetNCChanges *r = talloc_get_type(state->ndr_struct_ptr,
364 struct drsuapi_DsGetNCChanges);
365 uint32_t ctr_level = 0;
366 struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
367 struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
368 enum drsuapi_DsExtendedError extended_ret;
369 state->ndr_struct_ptr = NULL;
371 status = dcerpc_drsuapi_DsGetNCChanges_r_recv(subreq, r);
373 if (tevent_req_nterror(req, status)) {
377 if (!W_ERROR_IS_OK(r->out.result)) {
378 status = werror_to_ntstatus(r->out.result);
379 tevent_req_nterror(req, status);
383 if (*r->out.level_out == 1) {
385 ctr1 = &r->out.ctr->ctr1;
386 } else if (*r->out.level_out == 2 &&
387 r->out.ctr->ctr2.mszip1.ts) {
389 ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
390 } else if (*r->out.level_out == 6) {
392 ctr6 = &r->out.ctr->ctr6;
393 } else if (*r->out.level_out == 7 &&
394 r->out.ctr->ctr7.level == 6 &&
395 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP &&
396 r->out.ctr->ctr7.ctr.mszip6.ts) {
398 ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
399 } else if (*r->out.level_out == 7 &&
400 r->out.ctr->ctr7.level == 6 &&
401 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
402 r->out.ctr->ctr7.ctr.xpress6.ts) {
404 ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
406 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
407 tevent_req_nterror(req, status);
411 if (!ctr1 && !ctr6) {
412 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
413 tevent_req_nterror(req, status);
417 if (ctr_level == 6) {
418 if (!W_ERROR_IS_OK(ctr6->drs_error)) {
419 status = werror_to_ntstatus(ctr6->drs_error);
420 tevent_req_nterror(req, status);
423 extended_ret = ctr6->extended_ret;
426 if (ctr_level == 1) {
427 extended_ret = ctr1->extended_ret;
430 if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
431 state->op->extended_ret = extended_ret;
433 if (extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
434 status = NT_STATUS_UNSUCCESSFUL;
435 tevent_req_nterror(req, status);
440 dreplsrv_op_pull_source_apply_changes_trigger(req, r, ctr_level, ctr1, ctr6);
443 static void dreplsrv_update_refs_trigger(struct tevent_req *req);
445 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
446 struct drsuapi_DsGetNCChanges *r,
448 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
449 struct drsuapi_DsGetNCChangesCtr6 *ctr6)
451 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
452 struct dreplsrv_op_pull_source_state);
453 struct repsFromTo1 rf1 = *state->op->source_dsa->repsFrom1;
454 struct dreplsrv_service *service = state->op->service;
455 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
456 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
457 const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
458 uint32_t object_count;
459 struct drsuapi_DsReplicaObjectListItemEx *first_object;
460 uint32_t linked_attributes_count;
461 struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
462 const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
463 struct dsdb_extended_replicated_objects *objects;
464 bool more_data = false;
470 mapping_ctr = &ctr1->mapping_ctr;
471 object_count = ctr1->object_count;
472 first_object = ctr1->first_object;
473 linked_attributes_count = 0;
474 linked_attributes = NULL;
475 rf1.highwatermark = ctr1->new_highwatermark;
476 uptodateness_vector = NULL; /* TODO: map it */
477 more_data = ctr1->more_data;
480 mapping_ctr = &ctr6->mapping_ctr;
481 object_count = ctr6->object_count;
482 first_object = ctr6->first_object;
483 linked_attributes_count = ctr6->linked_attributes_count;
484 linked_attributes = ctr6->linked_attributes;
485 rf1.highwatermark = ctr6->new_highwatermark;
486 uptodateness_vector = ctr6->uptodateness_vector;
487 more_data = ctr6->more_data;
490 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
491 tevent_req_nterror(req, nt_status);
495 status = dsdb_extended_replicated_objects_convert(service->samdb,
500 linked_attributes_count,
504 &drsuapi->gensec_skey,
506 if (!W_ERROR_IS_OK(status)) {
507 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
508 DEBUG(0,("Failed to convert objects: %s/%s\n",
509 win_errstr(status), nt_errstr(nt_status)));
510 tevent_req_nterror(req, nt_status);
514 status = dsdb_extended_replicated_objects_commit(service->samdb,
516 &state->op->source_dsa->notify_uSN);
517 talloc_free(objects);
518 if (!W_ERROR_IS_OK(status)) {
519 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
520 DEBUG(0,("Failed to commit objects: %s/%s\n",
521 win_errstr(status), nt_errstr(nt_status)));
522 tevent_req_nterror(req, nt_status);
526 /* if it applied fine, we need to update the highwatermark */
527 *state->op->source_dsa->repsFrom1 = rf1;
530 * TODO: update our uptodatevector!
533 /* we don't need this maybe very large structure anymore */
537 dreplsrv_op_pull_source_get_changes_trigger(req);
541 /* now we need to update the repsTo record for this partition
542 on the server. These records are initially established when
543 we join the domain, but they quickly expire. We do it here
544 so we can use the already established DRSUAPI pipe
546 if (state->op->extended_op == DRSUAPI_EXOP_NONE) {
547 dreplsrv_update_refs_trigger(req);
549 tevent_req_done(req);
553 static void dreplsrv_update_refs_done(struct tevent_req *subreq);
556 send a UpdateRefs request to refresh our repsTo record on the server
558 static void dreplsrv_update_refs_trigger(struct tevent_req *req)
560 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
561 struct dreplsrv_op_pull_source_state);
562 struct dreplsrv_service *service = state->op->service;
563 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
564 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
565 struct drsuapi_DsReplicaUpdateRefs *r;
568 struct tevent_req *subreq;
572 r = talloc(state, struct drsuapi_DsReplicaUpdateRefs);
573 if (tevent_req_nomem(r, req)) {
577 ntds_guid_str = GUID_string(r, &service->ntds_guid);
578 if (tevent_req_nomem(ntds_guid_str, req)) {
582 ntds_dns_name = talloc_asprintf(r, "%s._msdcs.%s",
584 lpcfg_dnsdomain(service->task->lp_ctx));
585 if (tevent_req_nomem(ntds_dns_name, req)) {
589 r->in.bind_handle = &drsuapi->bind_handle;
591 r->in.req.req1.naming_context = &partition->nc;
592 r->in.req.req1.dest_dsa_dns_name = ntds_dns_name;
593 r->in.req.req1.dest_dsa_guid = service->ntds_guid;
594 r->in.req.req1.options = DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_DEL_REF;
595 ret = samdb_rodc(service->samdb, &am_rodc);
596 if (ret == LDB_SUCCESS && !am_rodc) {
597 r->in.req.req1.options |= DRSUAPI_DRS_WRIT_REP;
600 state->ndr_struct_ptr = r;
601 subreq = dcerpc_drsuapi_DsReplicaUpdateRefs_r_send(state,
603 drsuapi->drsuapi_handle,
605 if (tevent_req_nomem(subreq, req)) {
608 tevent_req_set_callback(subreq, dreplsrv_update_refs_done, req);
612 receive a UpdateRefs reply
614 static void dreplsrv_update_refs_done(struct tevent_req *subreq)
616 struct tevent_req *req = tevent_req_callback_data(subreq,
618 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
619 struct dreplsrv_op_pull_source_state);
620 struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(state->ndr_struct_ptr,
621 struct drsuapi_DsReplicaUpdateRefs);
624 state->ndr_struct_ptr = NULL;
626 status = dcerpc_drsuapi_DsReplicaUpdateRefs_r_recv(subreq, r);
628 if (!NT_STATUS_IS_OK(status)) {
629 DEBUG(0,("UpdateRefs failed with %s\n",
631 tevent_req_nterror(req, status);
635 if (!W_ERROR_IS_OK(r->out.result)) {
636 status = werror_to_ntstatus(r->out.result);
637 DEBUG(0,("UpdateRefs failed with %s/%s for %s %s\n",
638 win_errstr(r->out.result),
640 r->in.req.req1.dest_dsa_dns_name,
641 r->in.req.req1.naming_context->dn));
642 tevent_req_nterror(req, status);
646 DEBUG(4,("UpdateRefs OK for %s %s\n",
647 r->in.req.req1.dest_dsa_dns_name,
648 r->in.req.req1.naming_context->dn));
650 tevent_req_done(req);
653 WERROR dreplsrv_op_pull_source_recv(struct tevent_req *req)
657 if (tevent_req_is_nterror(req, &status)) {
658 tevent_req_received(req);
659 return ntstatus_to_werror(status);
662 tevent_req_received(req);