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 r = talloc(state, struct drsuapi_DsGetNCChanges);
276 if (tevent_req_nomem(r, req)) {
280 r->out.level_out = talloc(r, uint32_t);
281 if (tevent_req_nomem(r->out.level_out, req)) {
284 r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest);
285 if (tevent_req_nomem(r->in.req, req)) {
288 r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr);
289 if (tevent_req_nomem(r->out.ctr, req)) {
293 if (partition->uptodatevector_ex.count == 0) {
294 uptodateness_vector = NULL;
296 uptodateness_vector = &partition->uptodatevector_ex;
299 r->in.bind_handle = &drsuapi->bind_handle;
300 if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
302 r->in.req->req8.destination_dsa_guid = service->ntds_guid;
303 r->in.req->req8.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
304 r->in.req->req8.naming_context = &partition->nc;
305 r->in.req->req8.highwatermark = rf1->highwatermark;
306 r->in.req->req8.uptodateness_vector = uptodateness_vector;
307 r->in.req->req8.replica_flags = rf1->replica_flags;
308 r->in.req->req8.max_object_count = 133;
309 r->in.req->req8.max_ndr_size = 1336811;
310 r->in.req->req8.extended_op = state->op->extended_op;
311 r->in.req->req8.fsmo_info = state->op->fsmo_info;
312 r->in.req->req8.partial_attribute_set = NULL;
313 r->in.req->req8.partial_attribute_set_ex= NULL;
314 r->in.req->req8.mapping_ctr.num_mappings= 0;
315 r->in.req->req8.mapping_ctr.mappings = NULL;
318 r->in.req->req5.destination_dsa_guid = service->ntds_guid;
319 r->in.req->req5.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
320 r->in.req->req5.naming_context = &partition->nc;
321 r->in.req->req5.highwatermark = rf1->highwatermark;
322 r->in.req->req5.uptodateness_vector = uptodateness_vector;
323 r->in.req->req5.replica_flags = rf1->replica_flags;
324 r->in.req->req5.max_object_count = 133;
325 r->in.req->req5.max_ndr_size = 1336770;
326 r->in.req->req5.extended_op = state->op->extended_op;
327 r->in.req->req5.fsmo_info = state->op->fsmo_info;
331 NDR_PRINT_IN_DEBUG(drsuapi_DsGetNCChanges, r);
334 state->ndr_struct_ptr = r;
335 subreq = dcerpc_drsuapi_DsGetNCChanges_r_send(state,
337 drsuapi->drsuapi_handle,
339 if (tevent_req_nomem(subreq, req)) {
342 tevent_req_set_callback(subreq, dreplsrv_op_pull_source_get_changes_done, req);
345 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
346 struct drsuapi_DsGetNCChanges *r,
348 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
349 struct drsuapi_DsGetNCChangesCtr6 *ctr6);
351 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq)
353 struct tevent_req *req = tevent_req_callback_data(subreq,
355 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
356 struct dreplsrv_op_pull_source_state);
358 struct drsuapi_DsGetNCChanges *r = talloc_get_type(state->ndr_struct_ptr,
359 struct drsuapi_DsGetNCChanges);
360 uint32_t ctr_level = 0;
361 struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
362 struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
364 state->ndr_struct_ptr = NULL;
366 status = dcerpc_drsuapi_DsGetNCChanges_r_recv(subreq, r);
368 if (tevent_req_nterror(req, status)) {
372 if (!W_ERROR_IS_OK(r->out.result)) {
373 status = werror_to_ntstatus(r->out.result);
374 tevent_req_nterror(req, status);
378 if (*r->out.level_out == 1) {
380 ctr1 = &r->out.ctr->ctr1;
381 } else if (*r->out.level_out == 2 &&
382 r->out.ctr->ctr2.mszip1.ts) {
384 ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
385 } else if (*r->out.level_out == 6) {
387 ctr6 = &r->out.ctr->ctr6;
388 } else if (*r->out.level_out == 7 &&
389 r->out.ctr->ctr7.level == 6 &&
390 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP &&
391 r->out.ctr->ctr7.ctr.mszip6.ts) {
393 ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
394 } else if (*r->out.level_out == 7 &&
395 r->out.ctr->ctr7.level == 6 &&
396 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
397 r->out.ctr->ctr7.ctr.xpress6.ts) {
399 ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
401 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
402 tevent_req_nterror(req, status);
406 if (!ctr1 && !ctr6) {
407 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
408 tevent_req_nterror(req, status);
412 if (ctr_level == 6) {
413 if (!W_ERROR_IS_OK(ctr6->drs_error)) {
414 status = werror_to_ntstatus(ctr6->drs_error);
415 tevent_req_nterror(req, status);
420 dreplsrv_op_pull_source_apply_changes_trigger(req, r, ctr_level, ctr1, ctr6);
423 static void dreplsrv_update_refs_trigger(struct tevent_req *req);
425 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
426 struct drsuapi_DsGetNCChanges *r,
428 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
429 struct drsuapi_DsGetNCChangesCtr6 *ctr6)
431 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
432 struct dreplsrv_op_pull_source_state);
433 struct repsFromTo1 rf1 = *state->op->source_dsa->repsFrom1;
434 struct dreplsrv_service *service = state->op->service;
435 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
436 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
437 const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
438 uint32_t object_count;
439 struct drsuapi_DsReplicaObjectListItemEx *first_object;
440 uint32_t linked_attributes_count;
441 struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
442 const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
443 struct dsdb_extended_replicated_objects *objects;
444 bool more_data = false;
450 mapping_ctr = &ctr1->mapping_ctr;
451 object_count = ctr1->object_count;
452 first_object = ctr1->first_object;
453 linked_attributes_count = 0;
454 linked_attributes = NULL;
455 rf1.highwatermark = ctr1->new_highwatermark;
456 uptodateness_vector = NULL; /* TODO: map it */
457 more_data = ctr1->more_data;
460 mapping_ctr = &ctr6->mapping_ctr;
461 object_count = ctr6->object_count;
462 first_object = ctr6->first_object;
463 linked_attributes_count = ctr6->linked_attributes_count;
464 linked_attributes = ctr6->linked_attributes;
465 rf1.highwatermark = ctr6->new_highwatermark;
466 uptodateness_vector = ctr6->uptodateness_vector;
467 more_data = ctr6->more_data;
470 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
471 tevent_req_nterror(req, nt_status);
475 status = dsdb_extended_replicated_objects_convert(service->samdb,
480 linked_attributes_count,
484 &drsuapi->gensec_skey,
486 if (!W_ERROR_IS_OK(status)) {
487 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
488 DEBUG(0,("Failed to convert objects: %s/%s\n",
489 win_errstr(status), nt_errstr(nt_status)));
490 tevent_req_nterror(req, nt_status);
494 status = dsdb_extended_replicated_objects_commit(service->samdb,
496 &state->op->source_dsa->notify_uSN);
497 talloc_free(objects);
498 if (!W_ERROR_IS_OK(status)) {
499 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
500 DEBUG(0,("Failed to commit objects: %s/%s\n",
501 win_errstr(status), nt_errstr(nt_status)));
502 tevent_req_nterror(req, nt_status);
506 /* if it applied fine, we need to update the highwatermark */
507 *state->op->source_dsa->repsFrom1 = rf1;
510 * TODO: update our uptodatevector!
513 /* we don't need this maybe very large structure anymore */
517 dreplsrv_op_pull_source_get_changes_trigger(req);
521 /* now we need to update the repsTo record for this partition
522 on the server. These records are initially established when
523 we join the domain, but they quickly expire. We do it here
524 so we can use the already established DRSUAPI pipe
526 dreplsrv_update_refs_trigger(req);
529 static void dreplsrv_update_refs_done(struct tevent_req *subreq);
532 send a UpdateRefs request to refresh our repsTo record on the server
534 static void dreplsrv_update_refs_trigger(struct tevent_req *req)
536 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
537 struct dreplsrv_op_pull_source_state);
538 struct dreplsrv_service *service = state->op->service;
539 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
540 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
541 struct drsuapi_DsReplicaUpdateRefs *r;
544 struct tevent_req *subreq;
548 r = talloc(state, struct drsuapi_DsReplicaUpdateRefs);
549 if (tevent_req_nomem(r, req)) {
553 ntds_guid_str = GUID_string(r, &service->ntds_guid);
554 if (tevent_req_nomem(ntds_guid_str, req)) {
558 ntds_dns_name = talloc_asprintf(r, "%s._msdcs.%s",
560 lp_dnsdomain(service->task->lp_ctx));
561 if (tevent_req_nomem(ntds_dns_name, req)) {
565 r->in.bind_handle = &drsuapi->bind_handle;
567 r->in.req.req1.naming_context = &partition->nc;
568 r->in.req.req1.dest_dsa_dns_name = ntds_dns_name;
569 r->in.req.req1.dest_dsa_guid = service->ntds_guid;
570 r->in.req.req1.options = DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_DEL_REF;
571 ret = samdb_rodc(service->samdb, &am_rodc);
572 if (ret == LDB_SUCCESS && !am_rodc) {
573 r->in.req.req1.options |= DRSUAPI_DRS_WRIT_REP;
576 state->ndr_struct_ptr = r;
577 subreq = dcerpc_drsuapi_DsReplicaUpdateRefs_r_send(state,
579 drsuapi->drsuapi_handle,
581 if (tevent_req_nomem(subreq, req)) {
584 tevent_req_set_callback(subreq, dreplsrv_update_refs_done, req);
588 receive a UpdateRefs reply
590 static void dreplsrv_update_refs_done(struct tevent_req *subreq)
592 struct tevent_req *req = tevent_req_callback_data(subreq,
594 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
595 struct dreplsrv_op_pull_source_state);
596 struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(state->ndr_struct_ptr,
597 struct drsuapi_DsReplicaUpdateRefs);
600 state->ndr_struct_ptr = NULL;
602 status = dcerpc_drsuapi_DsReplicaUpdateRefs_r_recv(subreq, r);
604 if (!NT_STATUS_IS_OK(status)) {
605 DEBUG(0,("UpdateRefs failed with %s\n",
607 tevent_req_nterror(req, status);
611 if (!W_ERROR_IS_OK(r->out.result)) {
612 status = werror_to_ntstatus(r->out.result);
613 DEBUG(0,("UpdateRefs failed with %s/%s for %s %s\n",
614 win_errstr(r->out.result),
616 r->in.req.req1.dest_dsa_dns_name,
617 r->in.req.req1.naming_context->dn));
618 tevent_req_nterror(req, status);
622 DEBUG(4,("UpdateRefs OK for %s %s\n",
623 r->in.req.req1.dest_dsa_dns_name,
624 r->in.req.req1.naming_context->dn));
626 tevent_req_done(req);
629 WERROR dreplsrv_op_pull_source_recv(struct tevent_req *req)
633 if (tevent_req_is_nterror(req, &status)) {
634 tevent_req_received(req);
635 return ntstatus_to_werror(status);
638 tevent_req_received(req);