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_ndr_request_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 composite_context *creq;
207 struct dreplsrv_out_operation *op;
209 struct dreplsrv_drsuapi_connection *drsuapi;
214 struct drsuapi_DsGetNCChangesCtr1 *ctr1;
215 struct drsuapi_DsGetNCChangesCtr6 *ctr6;
218 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq);
220 struct composite_context *dreplsrv_op_pull_source_send(struct dreplsrv_out_operation *op)
222 struct composite_context *c;
223 struct dreplsrv_op_pull_source_state *st;
224 struct tevent_req *subreq;
226 c = composite_create(op, op->service->task->event_ctx);
227 if (c == NULL) return NULL;
229 st = talloc_zero(c, struct dreplsrv_op_pull_source_state);
230 if (composite_nomem(st, c)) return c;
235 subreq = dreplsrv_out_drsuapi_send(st,
236 op->service->task->event_ctx,
237 op->source_dsa->conn);
238 if (composite_nomem(subreq, c)) return c;
239 tevent_req_set_callback(subreq, dreplsrv_op_pull_source_connect_done, st);
244 static void dreplsrv_op_pull_source_get_changes_send(struct dreplsrv_op_pull_source_state *st);
246 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq)
248 struct dreplsrv_op_pull_source_state *st = tevent_req_callback_data(subreq,
249 struct dreplsrv_op_pull_source_state);
250 struct composite_context *c = st->creq;
252 c->status = dreplsrv_out_drsuapi_recv(subreq);
254 if (!composite_is_ok(c)) return;
256 dreplsrv_op_pull_source_get_changes_send(st);
259 static void dreplsrv_op_pull_source_get_changes_recv(struct rpc_request *req);
261 static void dreplsrv_op_pull_source_get_changes_send(struct dreplsrv_op_pull_source_state *st)
263 struct composite_context *c = st->creq;
264 struct repsFromTo1 *rf1 = st->op->source_dsa->repsFrom1;
265 struct dreplsrv_service *service = st->op->service;
266 struct dreplsrv_partition *partition = st->op->source_dsa->partition;
267 struct dreplsrv_drsuapi_connection *drsuapi = st->op->source_dsa->conn->drsuapi;
268 struct rpc_request *req;
269 struct drsuapi_DsGetNCChanges *r;
271 r = talloc(st, struct drsuapi_DsGetNCChanges);
272 if (composite_nomem(r, c)) return;
274 r->out.level_out = talloc(r, int32_t);
275 if (composite_nomem(r->out.level_out, c)) return;
276 r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest);
277 if (composite_nomem(r->in.req, c)) return;
278 r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr);
279 if (composite_nomem(r->out.ctr, c)) return;
281 r->in.bind_handle = &drsuapi->bind_handle;
282 if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
284 r->in.req->req8.destination_dsa_guid = service->ntds_guid;
285 r->in.req->req8.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
286 r->in.req->req8.naming_context = &partition->nc;
287 r->in.req->req8.highwatermark = rf1->highwatermark;
288 r->in.req->req8.uptodateness_vector = NULL;/*&partition->uptodatevector_ex;*/
289 r->in.req->req8.replica_flags = rf1->replica_flags;
290 r->in.req->req8.max_object_count = 133;
291 r->in.req->req8.max_ndr_size = 1336811;
292 r->in.req->req8.extended_op = st->op->extended_op;
293 r->in.req->req8.fsmo_info = st->op->fsmo_info;
294 r->in.req->req8.partial_attribute_set = NULL;
295 r->in.req->req8.partial_attribute_set_ex= NULL;
296 r->in.req->req8.mapping_ctr.num_mappings= 0;
297 r->in.req->req8.mapping_ctr.mappings = NULL;
300 r->in.req->req5.destination_dsa_guid = service->ntds_guid;
301 r->in.req->req5.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
302 r->in.req->req5.naming_context = &partition->nc;
303 r->in.req->req5.highwatermark = rf1->highwatermark;
304 r->in.req->req5.uptodateness_vector = NULL;/*&partition->uptodatevector_ex;*/
305 r->in.req->req5.replica_flags = rf1->replica_flags;
306 r->in.req->req5.max_object_count = 133;
307 r->in.req->req5.max_ndr_size = 1336770;
308 r->in.req->req5.extended_op = st->op->extended_op;
309 r->in.req->req5.fsmo_info = st->op->fsmo_info;
312 req = dcerpc_drsuapi_DsGetNCChanges_send(drsuapi->pipe, r, r);
313 composite_continue_rpc(c, req, dreplsrv_op_pull_source_get_changes_recv, st);
316 static void dreplsrv_op_pull_source_apply_changes_send(struct dreplsrv_op_pull_source_state *st,
317 struct drsuapi_DsGetNCChanges *r,
319 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
320 struct drsuapi_DsGetNCChangesCtr6 *ctr6);
322 static void dreplsrv_op_pull_source_get_changes_recv(struct rpc_request *req)
324 struct dreplsrv_op_pull_source_state *st = talloc_get_type(req->async.private_data,
325 struct dreplsrv_op_pull_source_state);
326 struct composite_context *c = st->creq;
327 struct drsuapi_DsGetNCChanges *r = talloc_get_type(req->ndr.struct_ptr,
328 struct drsuapi_DsGetNCChanges);
329 uint32_t ctr_level = 0;
330 struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
331 struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
333 c->status = dcerpc_ndr_request_recv(req);
334 if (!composite_is_ok(c)) return;
336 if (!W_ERROR_IS_OK(r->out.result)) {
337 composite_error(c, werror_to_ntstatus(r->out.result));
341 if (*r->out.level_out == 1) {
343 ctr1 = &r->out.ctr->ctr1;
344 } else if (*r->out.level_out == 2 &&
345 r->out.ctr->ctr2.mszip1.ts) {
347 ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
348 } else if (*r->out.level_out == 6) {
350 ctr6 = &r->out.ctr->ctr6;
351 } else if (*r->out.level_out == 7 &&
352 r->out.ctr->ctr7.level == 6 &&
353 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP &&
354 r->out.ctr->ctr7.ctr.mszip6.ts) {
356 ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
357 } else if (*r->out.level_out == 7 &&
358 r->out.ctr->ctr7.level == 6 &&
359 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
360 r->out.ctr->ctr7.ctr.xpress6.ts) {
362 ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
364 composite_error(c, werror_to_ntstatus(WERR_BAD_NET_RESP));
368 if (!ctr1 && !ctr6) {
369 composite_error(c, werror_to_ntstatus(WERR_BAD_NET_RESP));
373 if (ctr_level == 6) {
374 if (!W_ERROR_IS_OK(ctr6->drs_error)) {
375 composite_error(c, werror_to_ntstatus(ctr6->drs_error));
380 dreplsrv_op_pull_source_apply_changes_send(st, r, ctr_level, ctr1, ctr6);
383 static void dreplsrv_update_refs_send(struct dreplsrv_op_pull_source_state *st);
385 static void dreplsrv_op_pull_source_apply_changes_send(struct dreplsrv_op_pull_source_state *st,
386 struct drsuapi_DsGetNCChanges *r,
388 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
389 struct drsuapi_DsGetNCChangesCtr6 *ctr6)
391 struct composite_context *c = st->creq;
392 struct repsFromTo1 rf1 = *st->op->source_dsa->repsFrom1;
393 struct dreplsrv_service *service = st->op->service;
394 struct dreplsrv_partition *partition = st->op->source_dsa->partition;
395 struct dreplsrv_drsuapi_connection *drsuapi = st->op->source_dsa->conn->drsuapi;
396 const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
397 uint32_t object_count;
398 struct drsuapi_DsReplicaObjectListItemEx *first_object;
399 uint32_t linked_attributes_count;
400 struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
401 const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
402 struct dsdb_extended_replicated_objects *objects;
403 bool more_data = false;
408 mapping_ctr = &ctr1->mapping_ctr;
409 object_count = ctr1->object_count;
410 first_object = ctr1->first_object;
411 linked_attributes_count = 0;
412 linked_attributes = NULL;
413 rf1.highwatermark = ctr1->new_highwatermark;
414 uptodateness_vector = NULL; /* TODO: map it */
415 more_data = ctr1->more_data;
418 mapping_ctr = &ctr6->mapping_ctr;
419 object_count = ctr6->object_count;
420 first_object = ctr6->first_object;
421 linked_attributes_count = ctr6->linked_attributes_count;
422 linked_attributes = ctr6->linked_attributes;
423 rf1.highwatermark = ctr6->new_highwatermark;
424 uptodateness_vector = ctr6->uptodateness_vector;
425 more_data = ctr6->more_data;
428 composite_error(c, werror_to_ntstatus(WERR_BAD_NET_RESP));
432 status = dsdb_extended_replicated_objects_convert(service->samdb,
437 linked_attributes_count,
441 &drsuapi->gensec_skey,
443 if (!W_ERROR_IS_OK(status)) {
444 DEBUG(0,("Failed to convert objects: %s\n", win_errstr(status)));
445 composite_error(c, werror_to_ntstatus(status));
449 status = dsdb_extended_replicated_objects_commit(service->samdb,
451 &st->op->source_dsa->notify_uSN);
452 talloc_free(objects);
453 if (!W_ERROR_IS_OK(status)) {
454 DEBUG(0,("Failed to commit objects: %s\n", win_errstr(status)));
455 composite_error(c, werror_to_ntstatus(status));
459 /* if it applied fine, we need to update the highwatermark */
460 *st->op->source_dsa->repsFrom1 = rf1;
463 * TODO: update our uptodatevector!
467 dreplsrv_op_pull_source_get_changes_send(st);
471 /* now we need to update the repsTo record for this partition
472 on the server. These records are initially established when
473 we join the domain, but they quickly expire. We do it here
474 so we can use the already established DRSUAPI pipe
476 dreplsrv_update_refs_send(st);
479 WERROR dreplsrv_op_pull_source_recv(struct composite_context *c)
483 status = composite_wait(c);
486 return ntstatus_to_werror(status);
490 receive a UpdateRefs reply
492 static void dreplsrv_update_refs_recv(struct rpc_request *req)
494 struct dreplsrv_op_pull_source_state *st = talloc_get_type(req->async.private_data,
495 struct dreplsrv_op_pull_source_state);
496 struct composite_context *c = st->creq;
497 struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(req->ndr.struct_ptr,
498 struct drsuapi_DsReplicaUpdateRefs);
500 c->status = dcerpc_ndr_request_recv(req);
501 if (!composite_is_ok(c)) {
502 DEBUG(0,("UpdateRefs failed with %s\n",
503 nt_errstr(c->status)));
507 if (!W_ERROR_IS_OK(r->out.result)) {
508 DEBUG(0,("UpdateRefs failed with %s for %s %s\n",
509 win_errstr(r->out.result),
510 r->in.req.req1.dest_dsa_dns_name,
511 r->in.req.req1.naming_context->dn));
512 composite_error(c, werror_to_ntstatus(r->out.result));
516 DEBUG(4,("UpdateRefs OK for %s %s\n",
517 r->in.req.req1.dest_dsa_dns_name,
518 r->in.req.req1.naming_context->dn));
524 send a UpdateRefs request to refresh our repsTo record on the server
526 static void dreplsrv_update_refs_send(struct dreplsrv_op_pull_source_state *st)
528 struct composite_context *c = st->creq;
529 struct dreplsrv_service *service = st->op->service;
530 struct dreplsrv_partition *partition = st->op->source_dsa->partition;
531 struct dreplsrv_drsuapi_connection *drsuapi = st->op->source_dsa->conn->drsuapi;
532 struct rpc_request *req;
533 struct drsuapi_DsReplicaUpdateRefs *r;
537 r = talloc(st, struct drsuapi_DsReplicaUpdateRefs);
538 if (composite_nomem(r, c)) return;
540 ntds_guid_str = GUID_string(r, &service->ntds_guid);
541 if (composite_nomem(ntds_guid_str, c)) return;
543 ntds_dns_name = talloc_asprintf(r, "%s._msdcs.%s",
545 lp_dnsdomain(service->task->lp_ctx));
546 if (composite_nomem(ntds_dns_name, c)) return;
548 r->in.bind_handle = &drsuapi->bind_handle;
550 r->in.req.req1.naming_context = &partition->nc;
551 r->in.req.req1.dest_dsa_dns_name = ntds_dns_name;
552 r->in.req.req1.dest_dsa_guid = service->ntds_guid;
553 r->in.req.req1.options =
554 DRSUAPI_DS_REPLICA_UPDATE_ADD_REFERENCE |
555 DRSUAPI_DS_REPLICA_UPDATE_DELETE_REFERENCE;
556 if (!samdb_rodc(service->task->lp_ctx)) {
557 r->in.req.req1.options |= DRSUAPI_DS_REPLICA_UPDATE_WRITEABLE;
560 req = dcerpc_drsuapi_DsReplicaUpdateRefs_send(drsuapi->pipe, r, r);
561 composite_continue_rpc(c, req, dreplsrv_update_refs_recv, st);