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 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);
65 state->drsuapi = conn->drsuapi;
67 if (state->drsuapi && !state->drsuapi->pipe->conn->dead) {
69 return tevent_req_post(req, ev);
72 if (state->drsuapi && state->drsuapi->pipe->conn->dead) {
73 talloc_free(state->drsuapi);
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);
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,
99 struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
100 struct dreplsrv_out_drsuapi_state);
102 struct rpc_request *rreq;
104 status = dcerpc_pipe_connect_b_recv(creq,
106 &state->drsuapi->pipe);
107 if (tevent_req_nterror(req, status)) {
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)) {
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,
127 if (tevent_req_nomem(rreq, req)) {
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,
137 struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
138 struct dreplsrv_out_drsuapi_state);
141 status = dcerpc_drsuapi_DsBind_recv(rreq);
142 if (tevent_req_nterror(req, status)) {
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);
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) {
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;
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;
179 *info28 = state->bind_r.out.bind_info->info.info28;
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);
193 if (tevent_req_is_nterror(req, &status)) {
194 tevent_req_received(req);
198 state->conn->drsuapi = talloc_move(state->conn, &state->drsuapi);
200 tevent_req_received(req);
204 struct dreplsrv_op_pull_source_state {
205 struct dreplsrv_out_operation *op;
206 void *ndr_struct_ptr;
209 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq);
211 struct tevent_req *dreplsrv_op_pull_source_send(TALLOC_CTX *mem_ctx,
212 struct tevent_context *ev,
213 struct dreplsrv_out_operation *op)
215 struct tevent_req *req;
216 struct dreplsrv_op_pull_source_state *state;
217 struct tevent_req *subreq;
219 req = tevent_req_create(mem_ctx, &state,
220 struct dreplsrv_op_pull_source_state);
227 subreq = dreplsrv_out_drsuapi_send(state, ev, op->source_dsa->conn);
228 if (tevent_req_nomem(subreq, req)) {
229 return tevent_req_post(req, ev);
231 tevent_req_set_callback(subreq, dreplsrv_op_pull_source_connect_done, req);
236 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req);
238 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq)
240 struct tevent_req *req = tevent_req_callback_data(subreq,
244 status = dreplsrv_out_drsuapi_recv(subreq);
246 if (tevent_req_nterror(req, status)) {
250 dreplsrv_op_pull_source_get_changes_trigger(req);
253 static void dreplsrv_op_pull_source_get_changes_done(struct rpc_request *rreq);
255 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req)
257 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
258 struct dreplsrv_op_pull_source_state);
259 struct repsFromTo1 *rf1 = state->op->source_dsa->repsFrom1;
260 struct dreplsrv_service *service = state->op->service;
261 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
262 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
263 struct rpc_request *rreq;
264 struct drsuapi_DsGetNCChanges *r;
265 struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector;
267 r = talloc(state, struct drsuapi_DsGetNCChanges);
268 if (tevent_req_nomem(r, req)) {
272 r->out.level_out = talloc(r, int32_t);
273 if (tevent_req_nomem(r->out.level_out, req)) {
276 r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest);
277 if (tevent_req_nomem(r->in.req, req)) {
280 r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr);
281 if (tevent_req_nomem(r->out.ctr, req)) {
285 if (partition->uptodatevector_ex.count == 0) {
286 uptodateness_vector = NULL;
288 uptodateness_vector = &partition->uptodatevector_ex;
291 r->in.bind_handle = &drsuapi->bind_handle;
292 if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
294 r->in.req->req8.destination_dsa_guid = service->ntds_guid;
295 r->in.req->req8.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
296 r->in.req->req8.naming_context = &partition->nc;
297 r->in.req->req8.highwatermark = rf1->highwatermark;
298 r->in.req->req8.uptodateness_vector = uptodateness_vector;
299 r->in.req->req8.replica_flags = rf1->replica_flags;
300 r->in.req->req8.max_object_count = 133;
301 r->in.req->req8.max_ndr_size = 1336811;
302 r->in.req->req8.extended_op = state->op->extended_op;
303 r->in.req->req8.fsmo_info = state->op->fsmo_info;
304 r->in.req->req8.partial_attribute_set = NULL;
305 r->in.req->req8.partial_attribute_set_ex= NULL;
306 r->in.req->req8.mapping_ctr.num_mappings= 0;
307 r->in.req->req8.mapping_ctr.mappings = NULL;
310 r->in.req->req5.destination_dsa_guid = service->ntds_guid;
311 r->in.req->req5.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
312 r->in.req->req5.naming_context = &partition->nc;
313 r->in.req->req5.highwatermark = rf1->highwatermark;
314 r->in.req->req5.uptodateness_vector = uptodateness_vector;
315 r->in.req->req5.replica_flags = rf1->replica_flags;
316 r->in.req->req5.max_object_count = 133;
317 r->in.req->req5.max_ndr_size = 1336770;
318 r->in.req->req5.extended_op = state->op->extended_op;
319 r->in.req->req5.fsmo_info = state->op->fsmo_info;
323 NDR_PRINT_IN_DEBUG(drsuapi_DsGetNCChanges, r);
326 state->ndr_struct_ptr = r;
327 rreq = dcerpc_drsuapi_DsGetNCChanges_send(drsuapi->pipe, r, r);
328 if (tevent_req_nomem(rreq, req)) {
331 composite_continue_rpc(NULL, rreq, dreplsrv_op_pull_source_get_changes_done, req);
334 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
335 struct drsuapi_DsGetNCChanges *r,
337 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
338 struct drsuapi_DsGetNCChangesCtr6 *ctr6);
340 static void dreplsrv_op_pull_source_get_changes_done(struct rpc_request *rreq)
342 struct tevent_req *req = talloc_get_type(rreq->async.private_data,
344 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
345 struct dreplsrv_op_pull_source_state);
347 struct drsuapi_DsGetNCChanges *r = talloc_get_type(state->ndr_struct_ptr,
348 struct drsuapi_DsGetNCChanges);
349 uint32_t ctr_level = 0;
350 struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
351 struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
353 state->ndr_struct_ptr = NULL;
355 status = dcerpc_drsuapi_DsGetNCChanges_recv(rreq);
356 if (tevent_req_nterror(req, status)) {
360 if (!W_ERROR_IS_OK(r->out.result)) {
361 status = werror_to_ntstatus(r->out.result);
362 tevent_req_nterror(req, status);
366 if (*r->out.level_out == 1) {
368 ctr1 = &r->out.ctr->ctr1;
369 } else if (*r->out.level_out == 2 &&
370 r->out.ctr->ctr2.mszip1.ts) {
372 ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
373 } else if (*r->out.level_out == 6) {
375 ctr6 = &r->out.ctr->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_MSZIP &&
379 r->out.ctr->ctr7.ctr.mszip6.ts) {
381 ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
382 } else if (*r->out.level_out == 7 &&
383 r->out.ctr->ctr7.level == 6 &&
384 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
385 r->out.ctr->ctr7.ctr.xpress6.ts) {
387 ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
389 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
390 tevent_req_nterror(req, status);
394 if (!ctr1 && !ctr6) {
395 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
396 tevent_req_nterror(req, status);
400 if (ctr_level == 6) {
401 if (!W_ERROR_IS_OK(ctr6->drs_error)) {
402 status = werror_to_ntstatus(ctr6->drs_error);
403 tevent_req_nterror(req, status);
408 dreplsrv_op_pull_source_apply_changes_trigger(req, r, ctr_level, ctr1, ctr6);
411 static void dreplsrv_update_refs_trigger(struct tevent_req *req);
413 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
414 struct drsuapi_DsGetNCChanges *r,
416 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
417 struct drsuapi_DsGetNCChangesCtr6 *ctr6)
419 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
420 struct dreplsrv_op_pull_source_state);
421 struct repsFromTo1 rf1 = *state->op->source_dsa->repsFrom1;
422 struct dreplsrv_service *service = state->op->service;
423 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
424 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
425 const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
426 uint32_t object_count;
427 struct drsuapi_DsReplicaObjectListItemEx *first_object;
428 uint32_t linked_attributes_count;
429 struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
430 const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
431 struct dsdb_extended_replicated_objects *objects;
432 bool more_data = false;
438 mapping_ctr = &ctr1->mapping_ctr;
439 object_count = ctr1->object_count;
440 first_object = ctr1->first_object;
441 linked_attributes_count = 0;
442 linked_attributes = NULL;
443 rf1.highwatermark = ctr1->new_highwatermark;
444 uptodateness_vector = NULL; /* TODO: map it */
445 more_data = ctr1->more_data;
448 mapping_ctr = &ctr6->mapping_ctr;
449 object_count = ctr6->object_count;
450 first_object = ctr6->first_object;
451 linked_attributes_count = ctr6->linked_attributes_count;
452 linked_attributes = ctr6->linked_attributes;
453 rf1.highwatermark = ctr6->new_highwatermark;
454 uptodateness_vector = ctr6->uptodateness_vector;
455 more_data = ctr6->more_data;
458 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
459 tevent_req_nterror(req, nt_status);
463 status = dsdb_extended_replicated_objects_convert(service->samdb,
468 linked_attributes_count,
472 &drsuapi->gensec_skey,
474 if (!W_ERROR_IS_OK(status)) {
475 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
476 DEBUG(0,("Failed to convert objects: %s/%s\n",
477 win_errstr(status), nt_errstr(nt_status)));
478 tevent_req_nterror(req, nt_status);
482 status = dsdb_extended_replicated_objects_commit(service->samdb,
484 &state->op->source_dsa->notify_uSN);
485 talloc_free(objects);
486 if (!W_ERROR_IS_OK(status)) {
487 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
488 DEBUG(0,("Failed to commit objects: %s/%s\n",
489 win_errstr(status), nt_errstr(nt_status)));
490 tevent_req_nterror(req, nt_status);
494 /* if it applied fine, we need to update the highwatermark */
495 *state->op->source_dsa->repsFrom1 = rf1;
498 * TODO: update our uptodatevector!
501 /* we don't need this maybe very large structure anymore */
505 dreplsrv_op_pull_source_get_changes_trigger(req);
509 /* now we need to update the repsTo record for this partition
510 on the server. These records are initially established when
511 we join the domain, but they quickly expire. We do it here
512 so we can use the already established DRSUAPI pipe
514 dreplsrv_update_refs_trigger(req);
517 static void dreplsrv_update_refs_done(struct rpc_request *rreq);
520 send a UpdateRefs request to refresh our repsTo record on the server
522 static void dreplsrv_update_refs_trigger(struct tevent_req *req)
524 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
525 struct dreplsrv_op_pull_source_state);
526 struct dreplsrv_service *service = state->op->service;
527 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
528 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
529 struct rpc_request *rreq;
530 struct drsuapi_DsReplicaUpdateRefs *r;
534 r = talloc(state, struct drsuapi_DsReplicaUpdateRefs);
535 if (tevent_req_nomem(r, req)) {
539 ntds_guid_str = GUID_string(r, &service->ntds_guid);
540 if (tevent_req_nomem(ntds_guid_str, req)) {
544 ntds_dns_name = talloc_asprintf(r, "%s._msdcs.%s",
546 lp_dnsdomain(service->task->lp_ctx));
547 if (tevent_req_nomem(ntds_dns_name, req)) {
551 r->in.bind_handle = &drsuapi->bind_handle;
553 r->in.req.req1.naming_context = &partition->nc;
554 r->in.req.req1.dest_dsa_dns_name = ntds_dns_name;
555 r->in.req.req1.dest_dsa_guid = service->ntds_guid;
556 r->in.req.req1.options = DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_DEL_REF;
557 if (!samdb_rodc(service->samdb)) {
558 r->in.req.req1.options |= DRSUAPI_DRS_WRIT_REP;
561 state->ndr_struct_ptr = r;
562 rreq = dcerpc_drsuapi_DsReplicaUpdateRefs_send(drsuapi->pipe, r, r);
563 if (tevent_req_nomem(rreq, req)) {
566 composite_continue_rpc(NULL, rreq, dreplsrv_update_refs_done, req);
570 receive a UpdateRefs reply
572 static void dreplsrv_update_refs_done(struct rpc_request *rreq)
574 struct tevent_req *req = talloc_get_type(rreq->async.private_data,
576 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
577 struct dreplsrv_op_pull_source_state);
578 struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(state->ndr_struct_ptr,
579 struct drsuapi_DsReplicaUpdateRefs);
582 state->ndr_struct_ptr = NULL;
584 status = dcerpc_drsuapi_DsReplicaUpdateRefs_recv(rreq);
585 if (!NT_STATUS_IS_OK(status)) {
586 DEBUG(0,("UpdateRefs failed with %s\n",
588 tevent_req_nterror(req, status);
592 if (!W_ERROR_IS_OK(r->out.result)) {
593 status = werror_to_ntstatus(r->out.result);
594 DEBUG(0,("UpdateRefs failed with %s/%s for %s %s\n",
595 win_errstr(r->out.result),
597 r->in.req.req1.dest_dsa_dns_name,
598 r->in.req.req1.naming_context->dn));
599 tevent_req_nterror(req, status);
603 DEBUG(4,("UpdateRefs OK for %s %s\n",
604 r->in.req.req1.dest_dsa_dns_name,
605 r->in.req.req1.naming_context->dn));
607 tevent_req_done(req);
610 WERROR dreplsrv_op_pull_source_recv(struct tevent_req *req)
614 if (tevent_req_is_nterror(req, &status)) {
615 tevent_req_received(req);
616 return ntstatus_to_werror(status);
619 tevent_req_received(req);