1/* 2 Unix SMB/CIFS mplementation. 3 DSDB replication service helper function for outgoing traffic 4 5 Copyright (C) Stefan Metzmacher 2007 6 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. 11 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. 16 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/>. 19 20*/ 21 22#include "includes.h" 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 38struct dreplsrv_out_drsuapi_state { 39 struct composite_context *creq; 40 41 struct dreplsrv_out_connection *conn; 42 43 struct dreplsrv_drsuapi_connection *drsuapi; 44 45 struct drsuapi_DsBindInfoCtr bind_info_ctr; 46 struct drsuapi_DsBind bind_r; 47}; 48 49static void dreplsrv_out_drsuapi_connect_recv(struct composite_context *creq); 50 51struct composite_context *dreplsrv_out_drsuapi_send(struct dreplsrv_out_connection *conn) 52{ 53 struct composite_context *c; 54 struct composite_context *creq; 55 struct dreplsrv_out_drsuapi_state *st; 56 57 c = composite_create(conn, conn->service->task->event_ctx); 58 if (c == NULL) return NULL; 59 60 st = talloc_zero(c, struct dreplsrv_out_drsuapi_state); 61 if (composite_nomem(st, c)) return c; 62 63 c->private_data = st; 64 65 st->creq = c; 66 st->conn = conn; 67 st->drsuapi = conn->drsuapi; 68 69 if (st->drsuapi && !st->drsuapi->pipe->conn->dead) { 70 composite_done(c); 71 return c; 72 } else if (st->drsuapi && st->drsuapi->pipe->conn->dead) { 73 talloc_free(st->drsuapi); 74 conn->drsuapi = NULL; 75 } 76 77 st->drsuapi = talloc_zero(st, struct dreplsrv_drsuapi_connection); 78 if (composite_nomem(st->drsuapi, c)) return c; 79 80 creq = dcerpc_pipe_connect_b_send(st, conn->binding, &ndr_table_drsuapi, 81 conn->service->system_session_info->credentials, 82 c->event_ctx, conn->service->task->lp_ctx); 83 composite_continue(c, creq, dreplsrv_out_drsuapi_connect_recv, st); 84 85 return c; 86} 87 88static void dreplsrv_out_drsuapi_bind_send(struct dreplsrv_out_drsuapi_state *st); 89 90static void dreplsrv_out_drsuapi_connect_recv(struct composite_context *creq) 91{ 92 struct dreplsrv_out_drsuapi_state *st = talloc_get_type(creq->async.private_data, 93 struct dreplsrv_out_drsuapi_state); 94 struct composite_context *c = st->creq; 95 96 c->status = dcerpc_pipe_connect_b_recv(creq, st->drsuapi, &st->drsuapi->pipe); 97 if (!composite_is_ok(c)) return; 98 99 c->status = gensec_session_key(st->drsuapi->pipe->conn->security_state.generic_state, 100 &st->drsuapi->gensec_skey); 101 if (!composite_is_ok(c)) return; 102 103 dreplsrv_out_drsuapi_bind_send(st); 104} 105 106static void dreplsrv_out_drsuapi_bind_recv(struct rpc_request *req); 107 108static void dreplsrv_out_drsuapi_bind_send(struct dreplsrv_out_drsuapi_state *st) 109{ 110 struct composite_context *c = st->creq; 111 struct rpc_request *req; 112 113 st->bind_info_ctr.length = 28; 114 st->bind_info_ctr.info.info28 = st->conn->service->bind_info28; 115 116 st->bind_r.in.bind_guid = &st->conn->service->ntds_guid; 117 st->bind_r.in.bind_info = &st->bind_info_ctr; 118 st->bind_r.out.bind_handle = &st->drsuapi->bind_handle; 119 120 req = dcerpc_drsuapi_DsBind_send(st->drsuapi->pipe, st, &st->bind_r); 121 composite_continue_rpc(c, req, dreplsrv_out_drsuapi_bind_recv, st); 122} 123 124static void dreplsrv_out_drsuapi_bind_recv(struct rpc_request *req) 125{ 126 struct dreplsrv_out_drsuapi_state *st = talloc_get_type(req->async.private_data, 127 struct dreplsrv_out_drsuapi_state); 128 struct composite_context *c = st->creq; 129 130 c->status = dcerpc_ndr_request_recv(req); 131 if (!composite_is_ok(c)) return; 132 133 if (!W_ERROR_IS_OK(st->bind_r.out.result)) { 134 composite_error(c, werror_to_ntstatus(st->bind_r.out.result)); 135 return; 136 } 137 138 ZERO_STRUCT(st->drsuapi->remote_info28); 139 if (st->bind_r.out.bind_info) { 140 switch (st->bind_r.out.bind_info->length) { 141 case 24: { 142 struct drsuapi_DsBindInfo24 *info24; 143 info24 = &st->bind_r.out.bind_info->info.info24; 144 st->drsuapi->remote_info28.supported_extensions = info24->supported_extensions; 145 st->drsuapi->remote_info28.site_guid = info24->site_guid; 146 st->drsuapi->remote_info28.pid = info24->pid; 147 st->drsuapi->remote_info28.repl_epoch = 0; 148 break; 149 } 150 case 48: { 151 struct drsuapi_DsBindInfo48 *info48; 152 info48 = &st->bind_r.out.bind_info->info.info48; 153 st->drsuapi->remote_info28.supported_extensions = info48->supported_extensions; 154 st->drsuapi->remote_info28.site_guid = info48->site_guid; 155 st->drsuapi->remote_info28.pid = info48->pid; 156 st->drsuapi->remote_info28.repl_epoch = info48->repl_epoch; 157 break; 158 } 159 case 28: 160 st->drsuapi->remote_info28 = st->bind_r.out.bind_info->info.info28; 161 break; 162 } 163 } 164 165 composite_done(c); 166} 167 168NTSTATUS dreplsrv_out_drsuapi_recv(struct composite_context *c) 169{ 170 NTSTATUS status; 171 struct dreplsrv_out_drsuapi_state *st = talloc_get_type(c->private_data, 172 struct dreplsrv_out_drsuapi_state); 173 174 status = composite_wait(c); 175 176 if (NT_STATUS_IS_OK(status)) { 177 st->conn->drsuapi = talloc_steal(st->conn, st->drsuapi); 178 } 179 180 talloc_free(c); 181 return status; 182} 183 184struct dreplsrv_op_pull_source_state { 185 struct composite_context *creq; 186 187 struct dreplsrv_out_operation *op; 188 189 struct dreplsrv_drsuapi_connection *drsuapi; 190 191 bool have_all; 192 193 uint32_t ctr_level; 194 struct drsuapi_DsGetNCChangesCtr1 *ctr1; 195 struct drsuapi_DsGetNCChangesCtr6 *ctr6; 196}; 197 198static void dreplsrv_op_pull_source_connect_recv(struct composite_context *creq); 199 200struct composite_context *dreplsrv_op_pull_source_send(struct dreplsrv_out_operation *op) 201{ 202 struct composite_context *c; 203 struct composite_context *creq; 204 struct dreplsrv_op_pull_source_state *st; 205 206 c = composite_create(op, op->service->task->event_ctx); 207 if (c == NULL) return NULL; 208 209 st = talloc_zero(c, struct dreplsrv_op_pull_source_state); 210 if (composite_nomem(st, c)) return c; 211 212 st->creq = c; 213 st->op = op; 214 215 creq = dreplsrv_out_drsuapi_send(op->source_dsa->conn); 216 composite_continue(c, creq, dreplsrv_op_pull_source_connect_recv, st); 217 218 return c; 219} 220 221static void dreplsrv_op_pull_source_get_changes_send(struct dreplsrv_op_pull_source_state *st); 222 223static void dreplsrv_op_pull_source_connect_recv(struct composite_context *creq) 224{ 225 struct dreplsrv_op_pull_source_state *st = talloc_get_type(creq->async.private_data, 226 struct dreplsrv_op_pull_source_state); 227 struct composite_context *c = st->creq; 228 229 c->status = dreplsrv_out_drsuapi_recv(creq); 230 if (!composite_is_ok(c)) return; 231 232 dreplsrv_op_pull_source_get_changes_send(st); 233} 234 235static void dreplsrv_op_pull_source_get_changes_recv(struct rpc_request *req); 236 237static void dreplsrv_op_pull_source_get_changes_send(struct dreplsrv_op_pull_source_state *st) 238{ 239 struct composite_context *c = st->creq; 240 struct repsFromTo1 *rf1 = st->op->source_dsa->repsFrom1; 241 struct dreplsrv_service *service = st->op->service; 242 struct dreplsrv_partition *partition = st->op->source_dsa->partition; 243 struct dreplsrv_drsuapi_connection *drsuapi = st->op->source_dsa->conn->drsuapi; 244 struct rpc_request *req; 245 struct drsuapi_DsGetNCChanges *r; 246 247 r = talloc(st, struct drsuapi_DsGetNCChanges); 248 if (composite_nomem(r, c)) return; 249 250 r->out.level_out = talloc(r, int32_t); 251 if (composite_nomem(r->out.level_out, c)) return; 252 r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest); 253 if (composite_nomem(r->in.req, c)) return; 254 r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr); 255 if (composite_nomem(r->out.ctr, c)) return; 256 257 r->in.bind_handle = &drsuapi->bind_handle; 258 if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) { 259 r->in.level = 8; 260 r->in.req->req8.destination_dsa_guid = service->ntds_guid; 261 r->in.req->req8.source_dsa_invocation_id= rf1->source_dsa_invocation_id; 262 r->in.req->req8.naming_context = &partition->nc; 263 r->in.req->req8.highwatermark = rf1->highwatermark; 264 r->in.req->req8.uptodateness_vector = NULL;/*&partition->uptodatevector_ex;*/ 265 r->in.req->req8.replica_flags = rf1->replica_flags; 266 r->in.req->req8.max_object_count = 133; 267 r->in.req->req8.max_ndr_size = 1336811; 268 r->in.req->req8.extended_op = DRSUAPI_EXOP_NONE; 269 r->in.req->req8.fsmo_info = 0; 270 r->in.req->req8.partial_attribute_set = NULL; 271 r->in.req->req8.partial_attribute_set_ex= NULL; 272 r->in.req->req8.mapping_ctr.num_mappings= 0; 273 r->in.req->req8.mapping_ctr.mappings = NULL; 274 } else { 275 r->in.level = 5; 276 r->in.req->req5.destination_dsa_guid = service->ntds_guid; 277 r->in.req->req5.source_dsa_invocation_id= rf1->source_dsa_invocation_id; 278 r->in.req->req5.naming_context = &partition->nc; 279 r->in.req->req5.highwatermark = rf1->highwatermark; 280 r->in.req->req5.uptodateness_vector = NULL;/*&partition->uptodatevector_ex;*/ 281 r->in.req->req5.replica_flags = rf1->replica_flags; 282 r->in.req->req5.max_object_count = 133; 283 r->in.req->req5.max_ndr_size = 1336770; 284 r->in.req->req5.extended_op = DRSUAPI_EXOP_NONE; 285 r->in.req->req5.fsmo_info = 0; 286 } 287 288 req = dcerpc_drsuapi_DsGetNCChanges_send(drsuapi->pipe, r, r); 289 composite_continue_rpc(c, req, dreplsrv_op_pull_source_get_changes_recv, st); 290} 291 292static void dreplsrv_op_pull_source_apply_changes_send(struct dreplsrv_op_pull_source_state *st, 293 struct drsuapi_DsGetNCChanges *r, 294 uint32_t ctr_level, 295 struct drsuapi_DsGetNCChangesCtr1 *ctr1, 296 struct drsuapi_DsGetNCChangesCtr6 *ctr6); 297 298static void dreplsrv_op_pull_source_get_changes_recv(struct rpc_request *req) 299{ 300 struct dreplsrv_op_pull_source_state *st = talloc_get_type(req->async.private_data, 301 struct dreplsrv_op_pull_source_state); 302 struct composite_context *c = st->creq; 303 struct drsuapi_DsGetNCChanges *r = talloc_get_type(req->ndr.struct_ptr, 304 struct drsuapi_DsGetNCChanges); 305 uint32_t ctr_level = 0; 306 struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL; 307 struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL; 308 309 c->status = dcerpc_ndr_request_recv(req); 310 if (!composite_is_ok(c)) return; 311 312 if (!W_ERROR_IS_OK(r->out.result)) { 313 composite_error(c, werror_to_ntstatus(r->out.result)); 314 return; 315 } 316 317 if (*r->out.level_out == 1) { 318 ctr_level = 1; 319 ctr1 = &r->out.ctr->ctr1; 320 } else if (*r->out.level_out == 2 && 321 r->out.ctr->ctr2.mszip1.ts) { 322 ctr_level = 1; 323 ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1; 324 } else if (*r->out.level_out == 6) { 325 ctr_level = 6; 326 ctr6 = &r->out.ctr->ctr6; 327 } else if (*r->out.level_out == 7 && 328 r->out.ctr->ctr7.level == 6 && 329 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP && 330 r->out.ctr->ctr7.ctr.mszip6.ts) { 331 ctr_level = 6; 332 ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6; 333 } else if (*r->out.level_out == 7 && 334 r->out.ctr->ctr7.level == 6 && 335 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS && 336 r->out.ctr->ctr7.ctr.xpress6.ts) { 337 ctr_level = 6; 338 ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6; 339 } else { 340 composite_error(c, werror_to_ntstatus(WERR_BAD_NET_RESP)); 341 return; 342 } 343 344 if (!ctr1 && !ctr6) { 345 composite_error(c, werror_to_ntstatus(WERR_BAD_NET_RESP)); 346 return; 347 } 348 349 if (ctr_level == 6) { 350 if (!W_ERROR_IS_OK(ctr6->drs_error)) { 351 composite_error(c, werror_to_ntstatus(ctr6->drs_error)); 352 return; 353 } 354 } 355 356 dreplsrv_op_pull_source_apply_changes_send(st, r, ctr_level, ctr1, ctr6); 357} 358 359static void dreplsrv_update_refs_send(struct dreplsrv_op_pull_source_state *st); 360 361static void dreplsrv_op_pull_source_apply_changes_send(struct dreplsrv_op_pull_source_state *st, 362 struct drsuapi_DsGetNCChanges *r, 363 uint32_t ctr_level, 364 struct drsuapi_DsGetNCChangesCtr1 *ctr1, 365 struct drsuapi_DsGetNCChangesCtr6 *ctr6) 366{ 367 struct composite_context *c = st->creq; 368 struct repsFromTo1 rf1 = *st->op->source_dsa->repsFrom1; 369 struct dreplsrv_service *service = st->op->service; 370 struct dreplsrv_partition *partition = st->op->source_dsa->partition; 371 struct dreplsrv_drsuapi_connection *drsuapi = st->op->source_dsa->conn->drsuapi; 372 const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr; 373 uint32_t object_count; 374 struct drsuapi_DsReplicaObjectListItemEx *first_object; 375 uint32_t linked_attributes_count; 376 struct drsuapi_DsReplicaLinkedAttribute *linked_attributes; 377 const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector; 378 bool more_data = false; 379 WERROR status; 380 381 switch (ctr_level) { 382 case 1: 383 mapping_ctr = &ctr1->mapping_ctr; 384 object_count = ctr1->object_count; 385 first_object = ctr1->first_object; 386 linked_attributes_count = 0; 387 linked_attributes = NULL; 388 rf1.highwatermark = ctr1->new_highwatermark; 389 uptodateness_vector = NULL; /* TODO: map it */ 390 more_data = ctr1->more_data; 391 break; 392 case 6: 393 mapping_ctr = &ctr6->mapping_ctr; 394 object_count = ctr6->object_count; 395 first_object = ctr6->first_object; 396 linked_attributes_count = ctr6->linked_attributes_count; 397 linked_attributes = ctr6->linked_attributes; 398 rf1.highwatermark = ctr6->new_highwatermark; 399 uptodateness_vector = ctr6->uptodateness_vector; 400 more_data = ctr6->more_data; 401 break; 402 default: 403 composite_error(c, werror_to_ntstatus(WERR_BAD_NET_RESP)); 404 return; 405 } 406 407 status = dsdb_extended_replicated_objects_commit(service->samdb, 408 partition->nc.dn, 409 mapping_ctr, 410 object_count, 411 first_object, 412 linked_attributes_count, 413 linked_attributes, 414 &rf1, 415 uptodateness_vector, 416 &drsuapi->gensec_skey, 417 st, NULL, 418 &st->op->source_dsa->notify_uSN); 419 if (!W_ERROR_IS_OK(status)) { 420 DEBUG(0,("Failed to commit objects: %s\n", win_errstr(status))); 421 composite_error(c, werror_to_ntstatus(status)); 422 return; 423 } 424 425 /* if it applied fine, we need to update the highwatermark */ 426 *st->op->source_dsa->repsFrom1 = rf1; 427 428 /* 429 * TODO: update our uptodatevector! 430 */ 431 432 if (more_data) { 433 dreplsrv_op_pull_source_get_changes_send(st); 434 return; 435 } 436 437 /* now we need to update the repsTo record for this partition 438 on the server. These records are initially established when 439 we join the domain, but they quickly expire. We do it here 440 so we can use the already established DRSUAPI pipe 441 */ 442 dreplsrv_update_refs_send(st); 443} 444 445WERROR dreplsrv_op_pull_source_recv(struct composite_context *c) 446{ 447 NTSTATUS status; 448 449 status = composite_wait(c); 450 451 talloc_free(c); 452 return ntstatus_to_werror(status); 453} 454 455/* 456 receive a UpdateRefs reply 457 */ 458static void dreplsrv_update_refs_recv(struct rpc_request *req) 459{ 460 struct dreplsrv_op_pull_source_state *st = talloc_get_type(req->async.private_data, 461 struct dreplsrv_op_pull_source_state); 462 struct composite_context *c = st->creq; 463 struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(req->ndr.struct_ptr, 464 struct drsuapi_DsReplicaUpdateRefs); 465 466 c->status = dcerpc_ndr_request_recv(req); 467 if (!composite_is_ok(c)) { 468 DEBUG(0,("UpdateRefs failed with %s\n", 469 nt_errstr(c->status))); 470 return; 471 } 472 473 if (!W_ERROR_IS_OK(r->out.result)) { 474 DEBUG(0,("UpdateRefs failed with %s for %s %s\n", 475 win_errstr(r->out.result), 476 r->in.req.req1.dest_dsa_dns_name, 477 r->in.req.req1.naming_context->dn)); 478 composite_error(c, werror_to_ntstatus(r->out.result)); 479 return; 480 } 481 482 DEBUG(4,("UpdateRefs OK for %s %s\n", 483 r->in.req.req1.dest_dsa_dns_name, 484 r->in.req.req1.naming_context->dn)); 485 486 composite_done(c); 487} 488 489/* 490 send a UpdateRefs request to refresh our repsTo record on the server 491 */ 492static void dreplsrv_update_refs_send(struct dreplsrv_op_pull_source_state *st) 493{ 494 struct composite_context *c = st->creq; 495 struct dreplsrv_service *service = st->op->service; 496 struct dreplsrv_partition *partition = st->op->source_dsa->partition; 497 struct dreplsrv_drsuapi_connection *drsuapi = st->op->source_dsa->conn->drsuapi; 498 struct rpc_request *req; 499 struct drsuapi_DsReplicaUpdateRefs *r; 500 char *ntds_guid_str; 501 char *ntds_dns_name; 502 503 r = talloc(st, struct drsuapi_DsReplicaUpdateRefs); 504 if (composite_nomem(r, c)) return; 505 506 ntds_guid_str = GUID_string(r, &service->ntds_guid); 507 if (composite_nomem(ntds_guid_str, c)) return; 508 509 /* lp_realm() is not really right here */ 510 ntds_dns_name = talloc_asprintf(r, "%s._msdcs.%s", 511 ntds_guid_str, 512 lp_realm(service->task->lp_ctx)); 513 if (composite_nomem(ntds_dns_name, c)) return; 514 515 r->in.bind_handle = &drsuapi->bind_handle; 516 r->in.level = 1; 517 r->in.req.req1.naming_context = &partition->nc; 518 r->in.req.req1.dest_dsa_dns_name = ntds_dns_name; 519 r->in.req.req1.dest_dsa_guid = service->ntds_guid; 520 r->in.req.req1.options = 521 DRSUAPI_DS_REPLICA_UPDATE_ADD_REFERENCE | 522 DRSUAPI_DS_REPLICA_UPDATE_DELETE_REFERENCE; 523 if (!lp_parm_bool(service->task->lp_ctx, NULL, "repl", "RODC", false)) { 524 r->in.req.req1.options |= DRSUAPI_DS_REPLICA_UPDATE_WRITEABLE; 525 } 526 527 req = dcerpc_drsuapi_DsReplicaUpdateRefs_send(drsuapi->pipe, r, r); 528 composite_continue_rpc(c, req, dreplsrv_update_refs_recv, st); 529} 530