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;
276 /* check that the client isn't lying about being a RODC */
277 ret = dsdb_validate_client_flags(service->samdb, rf1);
278 if (ret != LDB_SUCCESS) {
282 if ((rf1->replica_flags & DRSUAPI_DRS_WRIT_REP) == 0) {
286 r = talloc(state, struct drsuapi_DsGetNCChanges);
287 if (tevent_req_nomem(r, req)) {
291 r->out.level_out = talloc(r, uint32_t);
292 if (tevent_req_nomem(r->out.level_out, req)) {
295 r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest);
296 if (tevent_req_nomem(r->in.req, req)) {
299 r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr);
300 if (tevent_req_nomem(r->out.ctr, req)) {
304 if (partition->uptodatevector_ex.count == 0) {
305 uptodateness_vector = NULL;
307 uptodateness_vector = &partition->uptodatevector_ex;
310 r->in.bind_handle = &drsuapi->bind_handle;
311 if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
313 r->in.req->req8.destination_dsa_guid = service->ntds_guid;
314 r->in.req->req8.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
315 r->in.req->req8.naming_context = &partition->nc;
316 r->in.req->req8.highwatermark = rf1->highwatermark;
317 r->in.req->req8.uptodateness_vector = uptodateness_vector;
318 r->in.req->req8.replica_flags = rf1->replica_flags;
319 r->in.req->req8.max_object_count = 133;
320 r->in.req->req8.max_ndr_size = 1336811;
321 r->in.req->req8.extended_op = state->op->extended_op;
322 r->in.req->req8.fsmo_info = state->op->fsmo_info;
323 r->in.req->req8.partial_attribute_set = NULL;
324 r->in.req->req8.partial_attribute_set_ex= NULL;
325 r->in.req->req8.mapping_ctr.num_mappings= 0;
326 r->in.req->req8.mapping_ctr.mappings = NULL;
329 r->in.req->req5.destination_dsa_guid = service->ntds_guid;
330 r->in.req->req5.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
331 r->in.req->req5.naming_context = &partition->nc;
332 r->in.req->req5.highwatermark = rf1->highwatermark;
333 r->in.req->req5.uptodateness_vector = uptodateness_vector;
334 r->in.req->req5.replica_flags = rf1->replica_flags;
335 r->in.req->req5.max_object_count = 133;
336 r->in.req->req5.max_ndr_size = 1336770;
337 r->in.req->req5.extended_op = state->op->extended_op;
338 r->in.req->req5.fsmo_info = state->op->fsmo_info;
342 NDR_PRINT_IN_DEBUG(drsuapi_DsGetNCChanges, r);
345 state->ndr_struct_ptr = r;
346 subreq = dcerpc_drsuapi_DsGetNCChanges_r_send(state,
348 drsuapi->drsuapi_handle,
350 if (tevent_req_nomem(subreq, req)) {
353 tevent_req_set_callback(subreq, dreplsrv_op_pull_source_get_changes_done, req);
356 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
357 struct drsuapi_DsGetNCChanges *r,
359 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
360 struct drsuapi_DsGetNCChangesCtr6 *ctr6);
362 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq)
364 struct tevent_req *req = tevent_req_callback_data(subreq,
366 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
367 struct dreplsrv_op_pull_source_state);
369 struct drsuapi_DsGetNCChanges *r = talloc_get_type(state->ndr_struct_ptr,
370 struct drsuapi_DsGetNCChanges);
371 uint32_t ctr_level = 0;
372 struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
373 struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
375 state->ndr_struct_ptr = NULL;
377 status = dcerpc_drsuapi_DsGetNCChanges_r_recv(subreq, r);
379 if (tevent_req_nterror(req, status)) {
383 if (!W_ERROR_IS_OK(r->out.result)) {
384 status = werror_to_ntstatus(r->out.result);
385 tevent_req_nterror(req, status);
389 if (*r->out.level_out == 1) {
391 ctr1 = &r->out.ctr->ctr1;
392 } else if (*r->out.level_out == 2 &&
393 r->out.ctr->ctr2.mszip1.ts) {
395 ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
396 } else if (*r->out.level_out == 6) {
398 ctr6 = &r->out.ctr->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_MSZIP &&
402 r->out.ctr->ctr7.ctr.mszip6.ts) {
404 ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
405 } else if (*r->out.level_out == 7 &&
406 r->out.ctr->ctr7.level == 6 &&
407 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
408 r->out.ctr->ctr7.ctr.xpress6.ts) {
410 ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
412 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
413 tevent_req_nterror(req, status);
417 if (!ctr1 && !ctr6) {
418 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
419 tevent_req_nterror(req, status);
423 if (ctr_level == 6) {
424 if (!W_ERROR_IS_OK(ctr6->drs_error)) {
425 status = werror_to_ntstatus(ctr6->drs_error);
426 tevent_req_nterror(req, status);
431 dreplsrv_op_pull_source_apply_changes_trigger(req, r, ctr_level, ctr1, ctr6);
434 static void dreplsrv_update_refs_trigger(struct tevent_req *req);
436 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
437 struct drsuapi_DsGetNCChanges *r,
439 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
440 struct drsuapi_DsGetNCChangesCtr6 *ctr6)
442 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
443 struct dreplsrv_op_pull_source_state);
444 struct repsFromTo1 rf1 = *state->op->source_dsa->repsFrom1;
445 struct dreplsrv_service *service = state->op->service;
446 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
447 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
448 const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
449 uint32_t object_count;
450 struct drsuapi_DsReplicaObjectListItemEx *first_object;
451 uint32_t linked_attributes_count;
452 struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
453 const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
454 struct dsdb_extended_replicated_objects *objects;
455 bool more_data = false;
461 mapping_ctr = &ctr1->mapping_ctr;
462 object_count = ctr1->object_count;
463 first_object = ctr1->first_object;
464 linked_attributes_count = 0;
465 linked_attributes = NULL;
466 rf1.highwatermark = ctr1->new_highwatermark;
467 uptodateness_vector = NULL; /* TODO: map it */
468 more_data = ctr1->more_data;
471 mapping_ctr = &ctr6->mapping_ctr;
472 object_count = ctr6->object_count;
473 first_object = ctr6->first_object;
474 linked_attributes_count = ctr6->linked_attributes_count;
475 linked_attributes = ctr6->linked_attributes;
476 rf1.highwatermark = ctr6->new_highwatermark;
477 uptodateness_vector = ctr6->uptodateness_vector;
478 more_data = ctr6->more_data;
481 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
482 tevent_req_nterror(req, nt_status);
486 status = dsdb_extended_replicated_objects_convert(service->samdb,
491 linked_attributes_count,
495 &drsuapi->gensec_skey,
497 if (!W_ERROR_IS_OK(status)) {
498 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
499 DEBUG(0,("Failed to convert objects: %s/%s\n",
500 win_errstr(status), nt_errstr(nt_status)));
501 tevent_req_nterror(req, nt_status);
505 status = dsdb_extended_replicated_objects_commit(service->samdb,
507 &state->op->source_dsa->notify_uSN);
508 talloc_free(objects);
509 if (!W_ERROR_IS_OK(status)) {
510 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
511 DEBUG(0,("Failed to commit objects: %s/%s\n",
512 win_errstr(status), nt_errstr(nt_status)));
513 tevent_req_nterror(req, nt_status);
517 /* if it applied fine, we need to update the highwatermark */
518 *state->op->source_dsa->repsFrom1 = rf1;
521 * TODO: update our uptodatevector!
524 /* we don't need this maybe very large structure anymore */
528 dreplsrv_op_pull_source_get_changes_trigger(req);
532 /* now we need to update the repsTo record for this partition
533 on the server. These records are initially established when
534 we join the domain, but they quickly expire. We do it here
535 so we can use the already established DRSUAPI pipe
537 dreplsrv_update_refs_trigger(req);
540 static void dreplsrv_update_refs_done(struct tevent_req *subreq);
543 send a UpdateRefs request to refresh our repsTo record on the server
545 static void dreplsrv_update_refs_trigger(struct tevent_req *req)
547 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
548 struct dreplsrv_op_pull_source_state);
549 struct dreplsrv_service *service = state->op->service;
550 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
551 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
552 struct drsuapi_DsReplicaUpdateRefs *r;
555 struct tevent_req *subreq;
559 r = talloc(state, struct drsuapi_DsReplicaUpdateRefs);
560 if (tevent_req_nomem(r, req)) {
564 ntds_guid_str = GUID_string(r, &service->ntds_guid);
565 if (tevent_req_nomem(ntds_guid_str, req)) {
569 ntds_dns_name = talloc_asprintf(r, "%s._msdcs.%s",
571 lp_dnsdomain(service->task->lp_ctx));
572 if (tevent_req_nomem(ntds_dns_name, req)) {
576 r->in.bind_handle = &drsuapi->bind_handle;
578 r->in.req.req1.naming_context = &partition->nc;
579 r->in.req.req1.dest_dsa_dns_name = ntds_dns_name;
580 r->in.req.req1.dest_dsa_guid = service->ntds_guid;
581 r->in.req.req1.options = DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_DEL_REF;
582 ret = samdb_rodc(service->samdb, &am_rodc);
583 if (ret == LDB_SUCCESS && !am_rodc) {
584 r->in.req.req1.options |= DRSUAPI_DRS_WRIT_REP;
587 state->ndr_struct_ptr = r;
588 subreq = dcerpc_drsuapi_DsReplicaUpdateRefs_r_send(state,
590 drsuapi->drsuapi_handle,
592 if (tevent_req_nomem(subreq, req)) {
595 tevent_req_set_callback(subreq, dreplsrv_update_refs_done, req);
599 receive a UpdateRefs reply
601 static void dreplsrv_update_refs_done(struct tevent_req *subreq)
603 struct tevent_req *req = tevent_req_callback_data(subreq,
605 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
606 struct dreplsrv_op_pull_source_state);
607 struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(state->ndr_struct_ptr,
608 struct drsuapi_DsReplicaUpdateRefs);
611 state->ndr_struct_ptr = NULL;
613 status = dcerpc_drsuapi_DsReplicaUpdateRefs_r_recv(subreq, r);
615 if (!NT_STATUS_IS_OK(status)) {
616 DEBUG(0,("UpdateRefs failed with %s\n",
618 tevent_req_nterror(req, status);
622 if (!W_ERROR_IS_OK(r->out.result)) {
623 status = werror_to_ntstatus(r->out.result);
624 DEBUG(0,("UpdateRefs failed with %s/%s for %s %s\n",
625 win_errstr(r->out.result),
627 r->in.req.req1.dest_dsa_dns_name,
628 r->in.req.req1.naming_context->dn));
629 tevent_req_nterror(req, status);
633 DEBUG(4,("UpdateRefs OK for %s %s\n",
634 r->in.req.req1.dest_dsa_dns_name,
635 r->in.req.req1.naming_context->dn));
637 tevent_req_done(req);
640 WERROR dreplsrv_op_pull_source_recv(struct tevent_req *req)
644 if (tevent_req_is_nterror(req, &status)) {
645 tevent_req_received(req);
646 return ntstatus_to_werror(status);
649 tevent_req_received(req);