1/* 2 Unix SMB/CIFS implementation. 3 4 Copyright (C) Stefan (metze) Metzmacher 2005 5 Copyright (C) Guenther Deschner 2008 6 Copyright (C) Michael Adam 2008 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 3 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program. If not, see <http://www.gnu.org/licenses/>. 20*/ 21 22 23#include "includes.h" 24#include "libnet/libnet.h" 25#include "../libcli/drsuapi/drsuapi.h" 26#include "../librpc/gen_ndr/cli_drsuapi.h" 27 28/**************************************************************** 29****************************************************************/ 30 31static int libnet_dssync_free_context(struct dssync_context *ctx) 32{ 33 if (!ctx) { 34 return 0; 35 } 36 37 if (is_valid_policy_hnd(&ctx->bind_handle) && ctx->cli) { 38 rpccli_drsuapi_DsUnbind(ctx->cli, ctx, &ctx->bind_handle, NULL); 39 } 40 41 return 0; 42} 43 44/**************************************************************** 45****************************************************************/ 46 47NTSTATUS libnet_dssync_init_context(TALLOC_CTX *mem_ctx, 48 struct dssync_context **ctx_p) 49{ 50 struct dssync_context *ctx; 51 52 ctx = TALLOC_ZERO_P(mem_ctx, struct dssync_context); 53 NT_STATUS_HAVE_NO_MEMORY(ctx); 54 55 talloc_set_destructor(ctx, libnet_dssync_free_context); 56 ctx->clean_old_entries = false; 57 58 *ctx_p = ctx; 59 60 return NT_STATUS_OK; 61} 62 63/**************************************************************** 64****************************************************************/ 65 66static void parse_obj_identifier(struct drsuapi_DsReplicaObjectIdentifier *id, 67 uint32_t *rid) 68{ 69 if (!id || !rid) { 70 return; 71 } 72 73 *rid = 0; 74 75 if (id->sid.num_auths > 0) { 76 *rid = id->sid.sub_auths[id->sid.num_auths - 1]; 77 } 78} 79 80/**************************************************************** 81****************************************************************/ 82 83static void libnet_dssync_decrypt_attributes(TALLOC_CTX *mem_ctx, 84 DATA_BLOB *session_key, 85 struct drsuapi_DsReplicaObjectListItemEx *cur) 86{ 87 for (; cur; cur = cur->next_object) { 88 89 uint32_t i; 90 uint32_t rid = 0; 91 92 parse_obj_identifier(cur->object.identifier, &rid); 93 94 for (i=0; i < cur->object.attribute_ctr.num_attributes; i++) { 95 96 struct drsuapi_DsReplicaAttribute *attr; 97 98 attr = &cur->object.attribute_ctr.attributes[i]; 99 100 if (attr->value_ctr.num_values < 1) { 101 continue; 102 } 103 104 if (!attr->value_ctr.values[0].blob) { 105 continue; 106 } 107 108 drsuapi_decrypt_attribute(mem_ctx, 109 session_key, 110 rid, 111 attr); 112 } 113 } 114} 115/**************************************************************** 116****************************************************************/ 117 118static NTSTATUS libnet_dssync_bind(TALLOC_CTX *mem_ctx, 119 struct dssync_context *ctx) 120{ 121 NTSTATUS status; 122 WERROR werr; 123 124 struct GUID bind_guid; 125 struct drsuapi_DsBindInfoCtr bind_info; 126 struct drsuapi_DsBindInfo28 info28; 127 128 ZERO_STRUCT(info28); 129 130 GUID_from_string(DRSUAPI_DS_BIND_GUID, &bind_guid); 131 132 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_BASE; 133 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION; 134 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI; 135 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2; 136 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS; 137 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1; 138 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION; 139 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE; 140 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2; 141 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION; 142 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2; 143 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD; 144 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND; 145 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO; 146 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION; 147 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01; 148 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP; 149 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY; 150 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3; 151 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2; 152 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6; 153 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS; 154 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8; 155 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5; 156 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6; 157 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3; 158 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7; 159 info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT; 160 info28.site_guid = GUID_zero(); 161 info28.pid = 508; 162 info28.repl_epoch = 0; 163 164 bind_info.length = 28; 165 bind_info.info.info28 = info28; 166 167 status = rpccli_drsuapi_DsBind(ctx->cli, mem_ctx, 168 &bind_guid, 169 &bind_info, 170 &ctx->bind_handle, 171 &werr); 172 173 if (!NT_STATUS_IS_OK(status)) { 174 return status; 175 } 176 177 if (!W_ERROR_IS_OK(werr)) { 178 return werror_to_ntstatus(werr); 179 } 180 181 ZERO_STRUCT(ctx->remote_info28); 182 switch (bind_info.length) { 183 case 24: { 184 struct drsuapi_DsBindInfo24 *info24; 185 info24 = &bind_info.info.info24; 186 ctx->remote_info28.site_guid = info24->site_guid; 187 ctx->remote_info28.supported_extensions = info24->supported_extensions; 188 ctx->remote_info28.pid = info24->pid; 189 ctx->remote_info28.repl_epoch = 0; 190 break; 191 } 192 case 28: 193 ctx->remote_info28 = bind_info.info.info28; 194 break; 195 case 48: { 196 struct drsuapi_DsBindInfo48 *info48; 197 info48 = &bind_info.info.info48; 198 ctx->remote_info28.site_guid = info48->site_guid; 199 ctx->remote_info28.supported_extensions = info48->supported_extensions; 200 ctx->remote_info28.pid = info48->pid; 201 ctx->remote_info28.repl_epoch = info48->repl_epoch; 202 break; 203 } 204 default: 205 DEBUG(1, ("Warning: invalid info length in bind info: %d\n", 206 bind_info.length)); 207 break; 208 } 209 210 return status; 211} 212 213/**************************************************************** 214****************************************************************/ 215 216static NTSTATUS libnet_dssync_lookup_nc(TALLOC_CTX *mem_ctx, 217 struct dssync_context *ctx) 218{ 219 NTSTATUS status; 220 WERROR werr; 221 int32_t level = 1; 222 union drsuapi_DsNameRequest req; 223 int32_t level_out; 224 struct drsuapi_DsNameString names[1]; 225 union drsuapi_DsNameCtr ctr; 226 227 names[0].str = talloc_asprintf(mem_ctx, "%s\\", ctx->domain_name); 228 NT_STATUS_HAVE_NO_MEMORY(names[0].str); 229 230 req.req1.codepage = 1252; /* german */ 231 req.req1.language = 0x00000407; /* german */ 232 req.req1.count = 1; 233 req.req1.names = names; 234 req.req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS; 235 req.req1.format_offered = DRSUAPI_DS_NAME_FORMAT_UNKNOWN; 236 req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; 237 238 status = rpccli_drsuapi_DsCrackNames(ctx->cli, mem_ctx, 239 &ctx->bind_handle, 240 level, 241 &req, 242 &level_out, 243 &ctr, 244 &werr); 245 if (!NT_STATUS_IS_OK(status)) { 246 ctx->error_message = talloc_asprintf(ctx, 247 "Failed to lookup DN for domain name: %s", 248 get_friendly_werror_msg(werr)); 249 return status; 250 } 251 252 if (!W_ERROR_IS_OK(werr)) { 253 return werror_to_ntstatus(werr); 254 } 255 256 if (ctr.ctr1->count != 1) { 257 return NT_STATUS_UNSUCCESSFUL; 258 } 259 260 if (ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { 261 return NT_STATUS_UNSUCCESSFUL; 262 } 263 264 ctx->nc_dn = talloc_strdup(mem_ctx, ctr.ctr1->array[0].result_name); 265 NT_STATUS_HAVE_NO_MEMORY(ctx->nc_dn); 266 267 if (!ctx->dns_domain_name) { 268 ctx->dns_domain_name = talloc_strdup_upper(mem_ctx, 269 ctr.ctr1->array[0].dns_domain_name); 270 NT_STATUS_HAVE_NO_MEMORY(ctx->dns_domain_name); 271 } 272 273 return NT_STATUS_OK; 274} 275 276/**************************************************************** 277****************************************************************/ 278 279static NTSTATUS libnet_dssync_init(TALLOC_CTX *mem_ctx, 280 struct dssync_context *ctx) 281{ 282 NTSTATUS status; 283 284 status = libnet_dssync_bind(mem_ctx, ctx); 285 if (!NT_STATUS_IS_OK(status)) { 286 return status; 287 } 288 289 if (!ctx->nc_dn) { 290 status = libnet_dssync_lookup_nc(mem_ctx, ctx); 291 } 292 293 return status; 294} 295 296/**************************************************************** 297****************************************************************/ 298 299static NTSTATUS libnet_dssync_build_request(TALLOC_CTX *mem_ctx, 300 struct dssync_context *ctx, 301 const char *dn, 302 struct replUpToDateVectorBlob *utdv, 303 int32_t *plevel, 304 union drsuapi_DsGetNCChangesRequest *preq) 305{ 306 NTSTATUS status; 307 uint32_t count; 308 int32_t level; 309 union drsuapi_DsGetNCChangesRequest req; 310 struct dom_sid null_sid; 311 enum drsuapi_DsExtendedOperation extended_op; 312 struct drsuapi_DsReplicaObjectIdentifier *nc = NULL; 313 struct drsuapi_DsReplicaCursorCtrEx *cursors = NULL; 314 315 uint32_t replica_flags = DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE | 316 DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP | 317 DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS | 318 DRSUAPI_DS_REPLICA_NEIGHBOUR_RETURN_OBJECT_PARENTS | 319 DRSUAPI_DS_REPLICA_NEIGHBOUR_NEVER_SYNCED; 320 321 ZERO_STRUCT(null_sid); 322 ZERO_STRUCT(req); 323 324 if (ctx->remote_info28.supported_extensions 325 & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) 326 { 327 level = 8; 328 } else { 329 level = 5; 330 } 331 332 nc = TALLOC_ZERO_P(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier); 333 if (!nc) { 334 status = NT_STATUS_NO_MEMORY; 335 goto fail; 336 } 337 nc->dn = dn; 338 nc->guid = GUID_zero(); 339 nc->sid = null_sid; 340 341 if (!ctx->single_object_replication && 342 !ctx->force_full_replication && utdv) 343 { 344 cursors = TALLOC_ZERO_P(mem_ctx, 345 struct drsuapi_DsReplicaCursorCtrEx); 346 if (!cursors) { 347 status = NT_STATUS_NO_MEMORY; 348 goto fail; 349 } 350 351 switch (utdv->version) { 352 case 1: 353 cursors->count = utdv->ctr.ctr1.count; 354 cursors->cursors = utdv->ctr.ctr1.cursors; 355 break; 356 case 2: 357 cursors->count = utdv->ctr.ctr2.count; 358 cursors->cursors = talloc_array(cursors, 359 struct drsuapi_DsReplicaCursor, 360 cursors->count); 361 if (!cursors->cursors) { 362 status = NT_STATUS_NO_MEMORY; 363 goto fail; 364 } 365 for (count = 0; count < cursors->count; count++) { 366 cursors->cursors[count].source_dsa_invocation_id = 367 utdv->ctr.ctr2.cursors[count].source_dsa_invocation_id; 368 cursors->cursors[count].highest_usn = 369 utdv->ctr.ctr2.cursors[count].highest_usn; 370 } 371 break; 372 } 373 } 374 375 if (ctx->single_object_replication) { 376 extended_op = DRSUAPI_EXOP_REPL_OBJ; 377 } else { 378 extended_op = DRSUAPI_EXOP_NONE; 379 } 380 381 if (level == 8) { 382 req.req8.naming_context = nc; 383 req.req8.replica_flags = replica_flags; 384 req.req8.max_object_count = 402; 385 req.req8.max_ndr_size = 402116; 386 req.req8.uptodateness_vector = cursors; 387 req.req8.extended_op = extended_op; 388 } else if (level == 5) { 389 req.req5.naming_context = nc; 390 req.req5.replica_flags = replica_flags; 391 req.req5.max_object_count = 402; 392 req.req5.max_ndr_size = 402116; 393 req.req5.uptodateness_vector = cursors; 394 req.req5.extended_op = extended_op; 395 } else { 396 status = NT_STATUS_INVALID_PARAMETER; 397 goto fail; 398 } 399 400 if (plevel) { 401 *plevel = level; 402 } 403 404 if (preq) { 405 *preq = req; 406 } 407 408 return NT_STATUS_OK; 409 410fail: 411 TALLOC_FREE(nc); 412 TALLOC_FREE(cursors); 413 return status; 414} 415 416static NTSTATUS libnet_dssync_getncchanges(TALLOC_CTX *mem_ctx, 417 struct dssync_context *ctx, 418 int32_t level, 419 union drsuapi_DsGetNCChangesRequest *req, 420 struct replUpToDateVectorBlob **pnew_utdv) 421{ 422 NTSTATUS status; 423 WERROR werr; 424 union drsuapi_DsGetNCChangesCtr ctr; 425 struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL; 426 struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL; 427 struct replUpToDateVectorBlob *new_utdv = NULL; 428 int32_t level_out = 0; 429 int32_t out_level = 0; 430 int y; 431 bool last_query; 432 433 if (!ctx->single_object_replication) { 434 new_utdv = TALLOC_ZERO_P(mem_ctx, struct replUpToDateVectorBlob); 435 if (!new_utdv) { 436 status = NT_STATUS_NO_MEMORY; 437 goto out; 438 } 439 } 440 441 for (y=0, last_query = false; !last_query; y++) { 442 struct drsuapi_DsReplicaObjectListItemEx *first_object = NULL; 443 struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr = NULL; 444 445 if (level == 8) { 446 DEBUG(1,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y, 447 (long long)req->req8.highwatermark.tmp_highest_usn, 448 (long long)req->req8.highwatermark.highest_usn)); 449 } else if (level == 5) { 450 DEBUG(1,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y, 451 (long long)req->req5.highwatermark.tmp_highest_usn, 452 (long long)req->req5.highwatermark.highest_usn)); 453 } 454 455 status = rpccli_drsuapi_DsGetNCChanges(ctx->cli, mem_ctx, 456 &ctx->bind_handle, 457 level, 458 req, 459 &level_out, 460 &ctr, 461 &werr); 462 if (!NT_STATUS_IS_OK(status)) { 463 ctx->error_message = talloc_asprintf(ctx, 464 "Failed to get NC Changes: %s", 465 get_friendly_werror_msg(werr)); 466 goto out; 467 } 468 469 if (!W_ERROR_IS_OK(werr)) { 470 status = werror_to_ntstatus(werr); 471 goto out; 472 } 473 474 if (level_out == 1) { 475 out_level = 1; 476 ctr1 = &ctr.ctr1; 477 } else if (level_out == 2 && ctr.ctr2.mszip1.ts) { 478 out_level = 1; 479 ctr1 = &ctr.ctr2.mszip1.ts->ctr1; 480 } else if (level_out == 6) { 481 out_level = 6; 482 ctr6 = &ctr.ctr6; 483 } else if (level_out == 7 484 && ctr.ctr7.level == 6 485 && ctr.ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP 486 && ctr.ctr7.ctr.mszip6.ts) { 487 out_level = 6; 488 ctr6 = &ctr.ctr7.ctr.mszip6.ts->ctr6; 489 } else if (level_out == 7 490 && ctr.ctr7.level == 6 491 && ctr.ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS 492 && ctr.ctr7.ctr.xpress6.ts) { 493 out_level = 6; 494 ctr6 = &ctr.ctr7.ctr.xpress6.ts->ctr6; 495 } 496 497 if (out_level == 1) { 498 DEBUG(1,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y, 499 (long long)ctr1->new_highwatermark.tmp_highest_usn, 500 (long long)ctr1->new_highwatermark.highest_usn)); 501 502 first_object = ctr1->first_object; 503 mapping_ctr = &ctr1->mapping_ctr; 504 505 if (ctr1->more_data) { 506 req->req5.highwatermark = ctr1->new_highwatermark; 507 } else { 508 last_query = true; 509 if (ctr1->uptodateness_vector && 510 !ctx->single_object_replication) 511 { 512 new_utdv->version = 1; 513 new_utdv->ctr.ctr1.count = 514 ctr1->uptodateness_vector->count; 515 new_utdv->ctr.ctr1.cursors = 516 ctr1->uptodateness_vector->cursors; 517 } 518 } 519 } else if (out_level == 6) { 520 DEBUG(1,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y, 521 (long long)ctr6->new_highwatermark.tmp_highest_usn, 522 (long long)ctr6->new_highwatermark.highest_usn)); 523 524 first_object = ctr6->first_object; 525 mapping_ctr = &ctr6->mapping_ctr; 526 527 if (ctr6->more_data) { 528 req->req8.highwatermark = ctr6->new_highwatermark; 529 } else { 530 last_query = true; 531 if (ctr6->uptodateness_vector && 532 !ctx->single_object_replication) 533 { 534 new_utdv->version = 2; 535 new_utdv->ctr.ctr2.count = 536 ctr6->uptodateness_vector->count; 537 new_utdv->ctr.ctr2.cursors = 538 ctr6->uptodateness_vector->cursors; 539 } 540 } 541 } 542 543 status = cli_get_session_key(mem_ctx, ctx->cli, &ctx->session_key); 544 if (!NT_STATUS_IS_OK(status)) { 545 ctx->error_message = talloc_asprintf(ctx, 546 "Failed to get Session Key: %s", 547 nt_errstr(status)); 548 goto out; 549 } 550 551 libnet_dssync_decrypt_attributes(mem_ctx, 552 &ctx->session_key, 553 first_object); 554 555 if (ctx->ops->process_objects) { 556 status = ctx->ops->process_objects(ctx, mem_ctx, 557 first_object, 558 mapping_ctr); 559 if (!NT_STATUS_IS_OK(status)) { 560 ctx->error_message = talloc_asprintf(ctx, 561 "Failed to call processing function: %s", 562 nt_errstr(status)); 563 goto out; 564 } 565 } 566 } 567 568 *pnew_utdv = new_utdv; 569 570out: 571 return status; 572} 573 574static NTSTATUS libnet_dssync_process(TALLOC_CTX *mem_ctx, 575 struct dssync_context *ctx) 576{ 577 NTSTATUS status; 578 579 int32_t level = 0; 580 union drsuapi_DsGetNCChangesRequest req; 581 struct replUpToDateVectorBlob *old_utdv = NULL; 582 struct replUpToDateVectorBlob *pnew_utdv = NULL; 583 const char **dns; 584 uint32_t dn_count; 585 uint32_t count; 586 587 if (ctx->ops->startup) { 588 status = ctx->ops->startup(ctx, mem_ctx, &old_utdv); 589 if (!NT_STATUS_IS_OK(status)) { 590 ctx->error_message = talloc_asprintf(ctx, 591 "Failed to call startup operation: %s", 592 nt_errstr(status)); 593 goto out; 594 } 595 } 596 597 if (ctx->single_object_replication && ctx->object_dns) { 598 dns = ctx->object_dns; 599 dn_count = ctx->object_count; 600 } else { 601 dns = &ctx->nc_dn; 602 dn_count = 1; 603 } 604 605 status = NT_STATUS_OK; 606 607 for (count=0; count < dn_count; count++) { 608 status = libnet_dssync_build_request(mem_ctx, ctx, 609 dns[count], 610 old_utdv, &level, 611 &req); 612 if (!NT_STATUS_IS_OK(status)) { 613 goto out; 614 } 615 616 status = libnet_dssync_getncchanges(mem_ctx, ctx, level, &req, 617 &pnew_utdv); 618 if (!NT_STATUS_IS_OK(status)) { 619 ctx->error_message = talloc_asprintf(ctx, 620 "Failed to call DsGetNCCHanges: %s", 621 nt_errstr(status)); 622 goto out; 623 } 624 } 625 626 if (ctx->ops->finish) { 627 status = ctx->ops->finish(ctx, mem_ctx, pnew_utdv); 628 if (!NT_STATUS_IS_OK(status)) { 629 ctx->error_message = talloc_asprintf(ctx, 630 "Failed to call finishing operation: %s", 631 nt_errstr(status)); 632 goto out; 633 } 634 } 635 636 out: 637 return status; 638} 639 640/**************************************************************** 641****************************************************************/ 642 643NTSTATUS libnet_dssync(TALLOC_CTX *mem_ctx, 644 struct dssync_context *ctx) 645{ 646 NTSTATUS status; 647 TALLOC_CTX *tmp_ctx; 648 649 tmp_ctx = talloc_new(mem_ctx); 650 if (!tmp_ctx) { 651 return NT_STATUS_NO_MEMORY; 652 } 653 654 status = libnet_dssync_init(tmp_ctx, ctx); 655 if (!NT_STATUS_IS_OK(status)) { 656 goto out; 657 } 658 659 status = libnet_dssync_process(tmp_ctx, ctx); 660 if (!NT_STATUS_IS_OK(status)) { 661 goto out; 662 } 663 664 out: 665 TALLOC_FREE(tmp_ctx); 666 return status; 667} 668 669