1/* 2 ldb database library 3 4 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007 5 Copyright (C) Simo Sorce <idra@samba.org> 2008 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 * Name: ldb 23 * 24 * Component: ldb linked_attributes module 25 * 26 * Description: Module to ensure linked attribute pairs remain in sync 27 * 28 * Author: Andrew Bartlett 29 */ 30 31#include "includes.h" 32#include "ldb_module.h" 33#include "dlinklist.h" 34#include "dsdb/samdb/samdb.h" 35#include "librpc/gen_ndr/ndr_misc.h" 36 37struct la_private { 38 struct la_context *la_list; 39}; 40 41struct la_op_store { 42 struct la_op_store *next; 43 struct la_op_store *prev; 44 enum la_op {LA_OP_ADD, LA_OP_DEL} op; 45 struct GUID guid; 46 char *name; 47 char *value; 48}; 49 50struct replace_context { 51 struct la_context *ac; 52 unsigned int num_elements; 53 struct ldb_message_element *el; 54}; 55 56struct la_context { 57 struct la_context *next, *prev; 58 const struct dsdb_schema *schema; 59 struct ldb_module *module; 60 struct ldb_request *req; 61 struct ldb_dn *partition_dn; 62 struct ldb_dn *add_dn; 63 struct ldb_dn *del_dn; 64 struct replace_context *rc; 65 struct la_op_store *ops; 66 struct ldb_extended *op_response; 67 struct ldb_control **op_controls; 68}; 69 70static struct la_context *linked_attributes_init(struct ldb_module *module, 71 struct ldb_request *req) 72{ 73 struct ldb_context *ldb; 74 struct la_context *ac; 75 const struct ldb_control *partition_ctrl; 76 77 ldb = ldb_module_get_ctx(module); 78 79 ac = talloc_zero(req, struct la_context); 80 if (ac == NULL) { 81 ldb_oom(ldb); 82 return NULL; 83 } 84 85 ac->schema = dsdb_get_schema(ldb); 86 ac->module = module; 87 ac->req = req; 88 89 /* remember the partition DN that came in, if given */ 90 partition_ctrl = ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID); 91 if (partition_ctrl) { 92 const struct dsdb_control_current_partition *partition; 93 partition = talloc_get_type(partition_ctrl->data, 94 struct dsdb_control_current_partition); 95 SMB_ASSERT(partition && partition->version == DSDB_CONTROL_CURRENT_PARTITION_VERSION); 96 97 ac->partition_dn = ldb_dn_copy(ac, partition->dn); 98 } 99 100 return ac; 101} 102 103/* 104 turn a DN into a GUID 105 */ 106static int la_guid_from_dn(struct la_context *ac, struct ldb_dn *dn, struct GUID *guid) 107{ 108 const struct ldb_val *guid_val; 109 int ret; 110 111 guid_val = ldb_dn_get_extended_component(dn, "GUID"); 112 if (guid_val) { 113 /* there is a GUID embedded in the DN */ 114 enum ndr_err_code ndr_err; 115 ndr_err = ndr_pull_struct_blob(guid_val, ac, NULL, guid, 116 (ndr_pull_flags_fn_t)ndr_pull_GUID); 117 if (ndr_err != NDR_ERR_SUCCESS) { 118 DEBUG(0,(__location__ ": Failed to parse GUID\n")); 119 return LDB_ERR_OPERATIONS_ERROR; 120 } 121 } else { 122 ret = dsdb_find_guid_by_dn(ldb_module_get_ctx(ac->module), dn, guid); 123 if (ret != LDB_SUCCESS) { 124 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n", 125 ldb_dn_get_linearized(dn))); 126 return ret; 127 } 128 } 129 return LDB_SUCCESS; 130} 131 132 133/* Common routine to handle reading the attributes and creating a 134 * series of modify requests */ 135static int la_store_op(struct la_context *ac, 136 enum la_op op, struct ldb_val *dn, 137 const char *name) 138{ 139 struct ldb_context *ldb; 140 struct la_op_store *os; 141 struct ldb_dn *op_dn; 142 int ret; 143 144 ldb = ldb_module_get_ctx(ac->module); 145 146 op_dn = ldb_dn_from_ldb_val(ac, ldb, dn); 147 if (!op_dn) { 148 ldb_asprintf_errstring(ldb, 149 "could not parse attribute as a DN"); 150 return LDB_ERR_INVALID_DN_SYNTAX; 151 } 152 153 os = talloc_zero(ac, struct la_op_store); 154 if (!os) { 155 ldb_oom(ldb); 156 return LDB_ERR_OPERATIONS_ERROR; 157 } 158 159 os->op = op; 160 161 ret = la_guid_from_dn(ac, op_dn, &os->guid); 162 if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) { 163 /* we are deleting an object, and we've found it has a 164 * forward link to a target that no longer 165 * exists. This is not an error in the delete, and we 166 * should just not do the deferred delete of the 167 * target attribute 168 */ 169 talloc_free(os); 170 return LDB_SUCCESS; 171 } 172 if (ret != LDB_SUCCESS) { 173 return ret; 174 } 175 176 os->name = talloc_strdup(os, name); 177 if (!os->name) { 178 ldb_oom(ldb); 179 return LDB_ERR_OPERATIONS_ERROR; 180 } 181 182 /* Do deletes before adds */ 183 if (op == LA_OP_ADD) { 184 DLIST_ADD_END(ac->ops, os, struct la_op_store *); 185 } else { 186 /* By adding to the head of the list, we do deletes before 187 * adds when processing a replace */ 188 DLIST_ADD(ac->ops, os); 189 } 190 191 return LDB_SUCCESS; 192} 193 194static int la_op_search_callback(struct ldb_request *req, 195 struct ldb_reply *ares); 196static int la_queue_mod_request(struct la_context *ac); 197static int la_down_req(struct la_context *ac); 198 199 200 201/* add */ 202static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req) 203{ 204 struct ldb_context *ldb; 205 const struct dsdb_attribute *target_attr; 206 struct la_context *ac; 207 const char *attr_name; 208 int ret; 209 int i, j; 210 211 ldb = ldb_module_get_ctx(module); 212 213 if (ldb_dn_is_special(req->op.add.message->dn)) { 214 /* do not manipulate our control entries */ 215 return ldb_next_request(module, req); 216 } 217 218 ac = linked_attributes_init(module, req); 219 if (!ac) { 220 return LDB_ERR_OPERATIONS_ERROR; 221 } 222 223 if (!ac->schema) { 224 /* without schema, this doesn't make any sense */ 225 talloc_free(ac); 226 return ldb_next_request(module, req); 227 } 228 229 /* Need to ensure we only have forward links being specified */ 230 for (i=0; i < req->op.add.message->num_elements; i++) { 231 const struct ldb_message_element *el = &req->op.add.message->elements[i]; 232 const struct dsdb_attribute *schema_attr 233 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name); 234 if (!schema_attr) { 235 ldb_asprintf_errstring(ldb, 236 "attribute %s is not a valid attribute in schema", el->name); 237 return LDB_ERR_OBJECT_CLASS_VIOLATION; 238 } 239 /* We have a valid attribute, now find out if it is linked */ 240 if (schema_attr->linkID == 0) { 241 continue; 242 } 243 244 if ((schema_attr->linkID & 1) == 1) { 245 /* Odd is for the target. Illegal to modify */ 246 ldb_asprintf_errstring(ldb, 247 "attribute %s must not be modified directly, it is a linked attribute", el->name); 248 return LDB_ERR_UNWILLING_TO_PERFORM; 249 } 250 251 /* Even link IDs are for the originating attribute */ 252 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1); 253 if (!target_attr) { 254 /* 255 * windows 2003 has a broken schema where 256 * the definition of msDS-IsDomainFor 257 * is missing (which is supposed to be 258 * the backlink of the msDS-HasDomainNCs 259 * attribute 260 */ 261 continue; 262 } 263 264 attr_name = target_attr->lDAPDisplayName; 265 266 for (j = 0; j < el->num_values; j++) { 267 ret = la_store_op(ac, LA_OP_ADD, 268 &el->values[j], 269 attr_name); 270 if (ret != LDB_SUCCESS) { 271 return ret; 272 } 273 } 274 } 275 276 /* if no linked attributes are present continue */ 277 if (ac->ops == NULL) { 278 /* nothing to do for this module, proceed */ 279 talloc_free(ac); 280 return ldb_next_request(module, req); 281 } 282 283 /* start with the original request */ 284 return la_down_req(ac); 285} 286 287/* For a delete or rename, we need to find out what linked attributes 288 * are currently on this DN, and then deal with them. This is the 289 * callback to the base search */ 290 291static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares) 292{ 293 struct ldb_context *ldb; 294 const struct dsdb_attribute *schema_attr; 295 const struct dsdb_attribute *target_attr; 296 struct ldb_message_element *search_el; 297 struct replace_context *rc; 298 struct la_context *ac; 299 const char *attr_name; 300 int i, j; 301 int ret = LDB_SUCCESS; 302 303 ac = talloc_get_type(req->context, struct la_context); 304 ldb = ldb_module_get_ctx(ac->module); 305 rc = ac->rc; 306 307 if (!ares) { 308 return ldb_module_done(ac->req, NULL, NULL, 309 LDB_ERR_OPERATIONS_ERROR); 310 } 311 if (ares->error != LDB_SUCCESS) { 312 return ldb_module_done(ac->req, ares->controls, 313 ares->response, ares->error); 314 } 315 316 /* Only entries are interesting, and we only want the olddn */ 317 switch (ares->type) { 318 case LDB_REPLY_ENTRY: 319 320 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) { 321 ldb_asprintf_errstring(ldb, 322 "linked_attributes: %s is not the DN we were looking for", ldb_dn_get_linearized(ares->message->dn)); 323 /* Guh? We only asked for this DN */ 324 talloc_free(ares); 325 return ldb_module_done(ac->req, NULL, NULL, 326 LDB_ERR_OPERATIONS_ERROR); 327 } 328 329 ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn); 330 331 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */ 332 for (i = 0; rc && i < rc->num_elements; i++) { 333 334 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name); 335 if (!schema_attr) { 336 ldb_asprintf_errstring(ldb, 337 "attribute %s is not a valid attribute in schema", 338 rc->el[i].name); 339 talloc_free(ares); 340 return ldb_module_done(ac->req, NULL, NULL, 341 LDB_ERR_OBJECT_CLASS_VIOLATION); 342 } 343 344 search_el = ldb_msg_find_element(ares->message, 345 rc->el[i].name); 346 347 /* See if this element already exists */ 348 /* otherwise just ignore as 349 * the add has already been scheduled */ 350 if ( ! search_el) { 351 continue; 352 } 353 354 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1); 355 if (!target_attr) { 356 /* 357 * windows 2003 has a broken schema where 358 * the definition of msDS-IsDomainFor 359 * is missing (which is supposed to be 360 * the backlink of the msDS-HasDomainNCs 361 * attribute 362 */ 363 continue; 364 } 365 attr_name = target_attr->lDAPDisplayName; 366 367 /* Now we know what was there, we can remove it for the re-add */ 368 for (j = 0; j < search_el->num_values; j++) { 369 ret = la_store_op(ac, LA_OP_DEL, 370 &search_el->values[j], 371 attr_name); 372 if (ret != LDB_SUCCESS) { 373 talloc_free(ares); 374 return ldb_module_done(ac->req, 375 NULL, NULL, ret); 376 } 377 } 378 } 379 380 break; 381 382 case LDB_REPLY_REFERRAL: 383 /* ignore */ 384 break; 385 386 case LDB_REPLY_DONE: 387 388 talloc_free(ares); 389 390 if (ac->req->operation == LDB_ADD) { 391 /* Start the modifies to the backlinks */ 392 ret = la_queue_mod_request(ac); 393 394 if (ret != LDB_SUCCESS) { 395 return ldb_module_done(ac->req, NULL, NULL, 396 ret); 397 } 398 } else { 399 /* Start with the original request */ 400 ret = la_down_req(ac); 401 if (ret != LDB_SUCCESS) { 402 return ldb_module_done(ac->req, NULL, NULL, ret); 403 } 404 } 405 return LDB_SUCCESS; 406 } 407 408 talloc_free(ares); 409 return ret; 410} 411 412 413/* modify */ 414static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req) 415{ 416 /* Look over list of modifications */ 417 /* Find if any are for linked attributes */ 418 /* Determine the effect of the modification */ 419 /* Apply the modify to the linked entry */ 420 421 struct ldb_context *ldb; 422 int i, j; 423 struct la_context *ac; 424 struct ldb_request *search_req; 425 const char **attrs; 426 427 int ret; 428 429 ldb = ldb_module_get_ctx(module); 430 431 if (ldb_dn_is_special(req->op.mod.message->dn)) { 432 /* do not manipulate our control entries */ 433 return ldb_next_request(module, req); 434 } 435 436 ac = linked_attributes_init(module, req); 437 if (!ac) { 438 return LDB_ERR_OPERATIONS_ERROR; 439 } 440 441 if (!ac->schema) { 442 /* without schema, this doesn't make any sense */ 443 return ldb_next_request(module, req); 444 } 445 446 ac->rc = talloc_zero(ac, struct replace_context); 447 if (!ac->rc) { 448 ldb_oom(ldb); 449 return LDB_ERR_OPERATIONS_ERROR; 450 } 451 452 for (i=0; i < req->op.mod.message->num_elements; i++) { 453 bool store_el = false; 454 const char *attr_name; 455 const struct dsdb_attribute *target_attr; 456 const struct ldb_message_element *el = &req->op.mod.message->elements[i]; 457 const struct dsdb_attribute *schema_attr 458 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name); 459 if (!schema_attr) { 460 ldb_asprintf_errstring(ldb, 461 "attribute %s is not a valid attribute in schema", el->name); 462 return LDB_ERR_OBJECT_CLASS_VIOLATION; 463 } 464 /* We have a valid attribute, now find out if it is linked */ 465 if (schema_attr->linkID == 0) { 466 continue; 467 } 468 469 if ((schema_attr->linkID & 1) == 1) { 470 /* Odd is for the target. Illegal to modify */ 471 ldb_asprintf_errstring(ldb, 472 "attribute %s must not be modified directly, it is a linked attribute", el->name); 473 return LDB_ERR_UNWILLING_TO_PERFORM; 474 } 475 476 /* Even link IDs are for the originating attribute */ 477 478 /* Now find the target attribute */ 479 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1); 480 if (!target_attr) { 481 /* 482 * windows 2003 has a broken schema where 483 * the definition of msDS-IsDomainFor 484 * is missing (which is supposed to be 485 * the backlink of the msDS-HasDomainNCs 486 * attribute 487 */ 488 continue; 489 } 490 491 attr_name = target_attr->lDAPDisplayName; 492 493 switch (el->flags & LDB_FLAG_MOD_MASK) { 494 case LDB_FLAG_MOD_REPLACE: 495 /* treat as just a normal add the delete part is handled by the callback */ 496 store_el = true; 497 498 /* break intentionally missing */ 499 500 case LDB_FLAG_MOD_ADD: 501 502 /* For each value being added, we need to setup the adds */ 503 for (j = 0; j < el->num_values; j++) { 504 ret = la_store_op(ac, LA_OP_ADD, 505 &el->values[j], 506 attr_name); 507 if (ret != LDB_SUCCESS) { 508 return ret; 509 } 510 } 511 break; 512 513 case LDB_FLAG_MOD_DELETE: 514 515 if (el->num_values) { 516 /* For each value being deleted, we need to setup the delete */ 517 for (j = 0; j < el->num_values; j++) { 518 ret = la_store_op(ac, LA_OP_DEL, 519 &el->values[j], 520 attr_name); 521 if (ret != LDB_SUCCESS) { 522 return ret; 523 } 524 } 525 } else { 526 /* Flag that there was a DELETE 527 * without a value specified, so we 528 * need to look for the old value */ 529 store_el = true; 530 } 531 532 break; 533 } 534 535 if (store_el) { 536 struct ldb_message_element *search_el; 537 538 search_el = talloc_realloc(ac->rc, ac->rc->el, 539 struct ldb_message_element, 540 ac->rc->num_elements +1); 541 if (!search_el) { 542 ldb_oom(ldb); 543 return LDB_ERR_OPERATIONS_ERROR; 544 } 545 ac->rc->el = search_el; 546 547 ac->rc->el[ac->rc->num_elements] = *el; 548 ac->rc->num_elements++; 549 } 550 } 551 552 if (ac->ops || ac->rc->el) { 553 /* both replace and delete without values are handled in the callback 554 * after the search on the entry to be modified is performed */ 555 556 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1); 557 if (!attrs) { 558 ldb_oom(ldb); 559 return LDB_ERR_OPERATIONS_ERROR; 560 } 561 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) { 562 attrs[i] = ac->rc->el[i].name; 563 } 564 attrs[i] = NULL; 565 566 /* The callback does all the hard work here */ 567 ret = ldb_build_search_req(&search_req, ldb, ac, 568 req->op.mod.message->dn, 569 LDB_SCOPE_BASE, 570 "(objectClass=*)", attrs, 571 NULL, 572 ac, la_mod_search_callback, 573 req); 574 575 /* We need to figure out our own extended DN, to fill in as the backlink target */ 576 if (ret == LDB_SUCCESS) { 577 ret = ldb_request_add_control(search_req, 578 LDB_CONTROL_EXTENDED_DN_OID, 579 false, NULL); 580 } 581 if (ret == LDB_SUCCESS) { 582 talloc_steal(search_req, attrs); 583 584 ret = ldb_next_request(module, search_req); 585 } 586 587 } else { 588 /* nothing to do for this module, proceed */ 589 talloc_free(ac); 590 ret = ldb_next_request(module, req); 591 } 592 593 return ret; 594} 595 596/* delete */ 597static int linked_attributes_del(struct ldb_module *module, struct ldb_request *req) 598{ 599 struct ldb_context *ldb; 600 struct ldb_request *search_req; 601 struct la_context *ac; 602 const char **attrs; 603 WERROR werr; 604 int ret; 605 606 /* This gets complex: We need to: 607 - Do a search for the entry 608 - Wait for these result to appear 609 - In the callback for the result, issue a modify 610 request based on the linked attributes found 611 - Wait for each modify result 612 - Regain our sainity 613 */ 614 615 ldb = ldb_module_get_ctx(module); 616 617 ac = linked_attributes_init(module, req); 618 if (!ac) { 619 return LDB_ERR_OPERATIONS_ERROR; 620 } 621 622 if (!ac->schema) { 623 /* without schema, this doesn't make any sense */ 624 return ldb_next_request(module, req); 625 } 626 627 werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs); 628 if (!W_ERROR_IS_OK(werr)) { 629 return LDB_ERR_OPERATIONS_ERROR; 630 } 631 632 ret = ldb_build_search_req(&search_req, ldb, req, 633 req->op.del.dn, LDB_SCOPE_BASE, 634 "(objectClass=*)", attrs, 635 NULL, 636 ac, la_op_search_callback, 637 req); 638 639 if (ret != LDB_SUCCESS) { 640 return ret; 641 } 642 643 talloc_steal(search_req, attrs); 644 645 return ldb_next_request(module, search_req); 646} 647 648/* rename */ 649static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req) 650{ 651 struct la_context *ac; 652 653 /* This gets complex: We need to: 654 - Do a search for the entry 655 - Wait for these result to appear 656 - In the callback for the result, issue a modify 657 request based on the linked attributes found 658 - Wait for each modify result 659 - Regain our sainity 660 */ 661 662 ac = linked_attributes_init(module, req); 663 if (!ac) { 664 return LDB_ERR_OPERATIONS_ERROR; 665 } 666 667 if (!ac->schema) { 668 /* without schema, this doesn't make any sense */ 669 return ldb_next_request(module, req); 670 } 671 672 /* start with the original request */ 673 return la_down_req(ac); 674} 675 676 677static int la_op_search_callback(struct ldb_request *req, 678 struct ldb_reply *ares) 679{ 680 struct ldb_context *ldb; 681 struct la_context *ac; 682 const struct dsdb_attribute *schema_attr; 683 const struct dsdb_attribute *target_attr; 684 const struct ldb_message_element *el; 685 const char *attr_name; 686 int i, j; 687 int ret; 688 689 ac = talloc_get_type(req->context, struct la_context); 690 ldb = ldb_module_get_ctx(ac->module); 691 692 if (!ares) { 693 return ldb_module_done(ac->req, NULL, NULL, 694 LDB_ERR_OPERATIONS_ERROR); 695 } 696 if (ares->error != LDB_SUCCESS) { 697 return ldb_module_done(ac->req, ares->controls, 698 ares->response, ares->error); 699 } 700 701 /* Only entries are interesting, and we only want the olddn */ 702 switch (ares->type) { 703 case LDB_REPLY_ENTRY: 704 ret = ldb_dn_compare(ares->message->dn, req->op.search.base); 705 if (ret != 0) { 706 /* Guh? We only asked for this DN */ 707 talloc_free(ares); 708 return ldb_module_done(ac->req, NULL, NULL, 709 LDB_ERR_OPERATIONS_ERROR); 710 } 711 if (ares->message->num_elements == 0) { 712 /* only bother at all if there were some 713 * linked attributes found */ 714 talloc_free(ares); 715 return LDB_SUCCESS; 716 } 717 718 switch (ac->req->operation) { 719 case LDB_DELETE: 720 ac->del_dn = talloc_steal(ac, ares->message->dn); 721 break; 722 case LDB_RENAME: 723 ac->add_dn = talloc_steal(ac, ares->message->dn); 724 ac->del_dn = talloc_steal(ac, ac->req->op.rename.olddn); 725 break; 726 default: 727 talloc_free(ares); 728 ldb_set_errstring(ldb, 729 "operations must be delete or rename"); 730 return ldb_module_done(ac->req, NULL, NULL, 731 LDB_ERR_OPERATIONS_ERROR); 732 } 733 734 for (i = 0; i < ares->message->num_elements; i++) { 735 el = &ares->message->elements[i]; 736 737 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name); 738 if (!schema_attr) { 739 ldb_asprintf_errstring(ldb, 740 "attribute %s is not a valid attribute" 741 " in schema", el->name); 742 talloc_free(ares); 743 return ldb_module_done(ac->req, NULL, NULL, 744 LDB_ERR_OBJECT_CLASS_VIOLATION); 745 } 746 747 /* Valid attribute, now find out if it is linked */ 748 if (schema_attr->linkID == 0) { 749 /* Not a linked attribute, skip */ 750 continue; 751 } 752 753 if ((schema_attr->linkID & 1) == 0) { 754 /* Odd is for the target. */ 755 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1); 756 if (!target_attr) { 757 continue; 758 } 759 attr_name = target_attr->lDAPDisplayName; 760 } else { 761 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID - 1); 762 if (!target_attr) { 763 continue; 764 } 765 attr_name = target_attr->lDAPDisplayName; 766 } 767 for (j = 0; j < el->num_values; j++) { 768 ret = la_store_op(ac, LA_OP_DEL, 769 &el->values[j], 770 attr_name); 771 772 /* for renames, ensure we add it back */ 773 if (ret == LDB_SUCCESS 774 && ac->req->operation == LDB_RENAME) { 775 ret = la_store_op(ac, LA_OP_ADD, 776 &el->values[j], 777 attr_name); 778 } 779 if (ret != LDB_SUCCESS) { 780 talloc_free(ares); 781 return ldb_module_done(ac->req, 782 NULL, NULL, ret); 783 } 784 } 785 } 786 787 break; 788 789 case LDB_REPLY_REFERRAL: 790 /* ignore */ 791 break; 792 793 case LDB_REPLY_DONE: 794 795 talloc_free(ares); 796 797 798 switch (ac->req->operation) { 799 case LDB_DELETE: 800 /* start the mod requests chain */ 801 ret = la_down_req(ac); 802 if (ret != LDB_SUCCESS) { 803 return ldb_module_done(ac->req, NULL, NULL, ret); 804 } 805 return ret; 806 807 case LDB_RENAME: 808 /* start the mod requests chain */ 809 ret = la_queue_mod_request(ac); 810 if (ret != LDB_SUCCESS) { 811 return ldb_module_done(ac->req, NULL, NULL, 812 ret); 813 } 814 return ret; 815 816 default: 817 talloc_free(ares); 818 ldb_set_errstring(ldb, 819 "operations must be delete or rename"); 820 return ldb_module_done(ac->req, NULL, NULL, 821 LDB_ERR_OPERATIONS_ERROR); 822 } 823 } 824 825 talloc_free(ares); 826 return LDB_SUCCESS; 827} 828 829/* queue a linked attributes modify request in the la_private 830 structure */ 831static int la_queue_mod_request(struct la_context *ac) 832{ 833 struct la_private *la_private = 834 talloc_get_type(ldb_module_get_private(ac->module), struct la_private); 835 836 if (la_private == NULL) { 837 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n"); 838 return LDB_ERR_OPERATIONS_ERROR; 839 } 840 841 talloc_steal(la_private, ac); 842 DLIST_ADD(la_private->la_list, ac); 843 844 return ldb_module_done(ac->req, ac->op_controls, 845 ac->op_response, LDB_SUCCESS); 846} 847 848/* Having done the original operation, then try to fix up all the linked attributes for modify and delete */ 849static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares) 850{ 851 int ret; 852 struct la_context *ac; 853 struct ldb_context *ldb; 854 855 ac = talloc_get_type(req->context, struct la_context); 856 ldb = ldb_module_get_ctx(ac->module); 857 858 if (!ares) { 859 return ldb_module_done(ac->req, NULL, NULL, 860 LDB_ERR_OPERATIONS_ERROR); 861 } 862 if (ares->error != LDB_SUCCESS) { 863 return ldb_module_done(ac->req, ares->controls, 864 ares->response, ares->error); 865 } 866 867 if (ares->type != LDB_REPLY_DONE) { 868 ldb_set_errstring(ldb, 869 "invalid ldb_reply_type in callback"); 870 talloc_free(ares); 871 return ldb_module_done(ac->req, NULL, NULL, 872 LDB_ERR_OPERATIONS_ERROR); 873 } 874 875 ac->op_controls = talloc_steal(ac, ares->controls); 876 ac->op_response = talloc_steal(ac, ares->response); 877 878 /* If we have modfies to make, this is the time to do them for modify and delete */ 879 ret = la_queue_mod_request(ac); 880 881 if (ret != LDB_SUCCESS) { 882 return ldb_module_done(ac->req, NULL, NULL, ret); 883 } 884 talloc_free(ares); 885 886 /* la_queue_mod_request has already sent the callbacks */ 887 return LDB_SUCCESS; 888 889} 890 891/* Having done the original rename try to fix up all the linked attributes */ 892static int la_rename_callback(struct ldb_request *req, struct ldb_reply *ares) 893{ 894 int ret; 895 struct la_context *ac; 896 struct ldb_request *search_req; 897 const char **attrs; 898 WERROR werr; 899 struct ldb_context *ldb; 900 901 ac = talloc_get_type(req->context, struct la_context); 902 ldb = ldb_module_get_ctx(ac->module); 903 904 if (!ares) { 905 return ldb_module_done(ac->req, NULL, NULL, 906 LDB_ERR_OPERATIONS_ERROR); 907 } 908 if (ares->error != LDB_SUCCESS) { 909 return ldb_module_done(ac->req, ares->controls, 910 ares->response, ares->error); 911 } 912 913 if (ares->type != LDB_REPLY_DONE) { 914 ldb_set_errstring(ldb, 915 "invalid ldb_reply_type in callback"); 916 talloc_free(ares); 917 return ldb_module_done(ac->req, NULL, NULL, 918 LDB_ERR_OPERATIONS_ERROR); 919 } 920 921 werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs); 922 if (!W_ERROR_IS_OK(werr)) { 923 return LDB_ERR_OPERATIONS_ERROR; 924 } 925 926 ret = ldb_build_search_req(&search_req, ldb, req, 927 ac->req->op.rename.newdn, LDB_SCOPE_BASE, 928 "(objectClass=*)", attrs, 929 NULL, 930 ac, la_op_search_callback, 931 req); 932 933 if (ret != LDB_SUCCESS) { 934 return ret; 935 } 936 937 talloc_steal(search_req, attrs); 938 939 if (ret == LDB_SUCCESS) { 940 ret = ldb_request_add_control(search_req, 941 LDB_CONTROL_EXTENDED_DN_OID, 942 false, NULL); 943 } 944 if (ret != LDB_SUCCESS) { 945 return ldb_module_done(ac->req, NULL, NULL, 946 ret); 947 } 948 949 ac->op_controls = talloc_steal(ac, ares->controls); 950 ac->op_response = talloc_steal(ac, ares->response); 951 952 return ldb_next_request(ac->module, search_req); 953} 954 955/* Having done the original add, then try to fix up all the linked attributes 956 957 This is done after the add so the links can get the extended DNs correctly. 958 */ 959static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares) 960{ 961 int ret; 962 struct la_context *ac; 963 struct ldb_context *ldb; 964 965 ac = talloc_get_type(req->context, struct la_context); 966 ldb = ldb_module_get_ctx(ac->module); 967 968 if (!ares) { 969 return ldb_module_done(ac->req, NULL, NULL, 970 LDB_ERR_OPERATIONS_ERROR); 971 } 972 if (ares->error != LDB_SUCCESS) { 973 return ldb_module_done(ac->req, ares->controls, 974 ares->response, ares->error); 975 } 976 977 if (ares->type != LDB_REPLY_DONE) { 978 ldb_set_errstring(ldb, 979 "invalid ldb_reply_type in callback"); 980 talloc_free(ares); 981 return ldb_module_done(ac->req, NULL, NULL, 982 LDB_ERR_OPERATIONS_ERROR); 983 } 984 985 if (ac->ops) { 986 struct ldb_request *search_req; 987 static const char *attrs[] = { NULL }; 988 989 /* The callback does all the hard work here - we need 990 * the objectGUID and SID of the added record */ 991 ret = ldb_build_search_req(&search_req, ldb, ac, 992 ac->req->op.add.message->dn, 993 LDB_SCOPE_BASE, 994 "(objectClass=*)", attrs, 995 NULL, 996 ac, la_mod_search_callback, 997 ac->req); 998 999 if (ret == LDB_SUCCESS) { 1000 ret = ldb_request_add_control(search_req, 1001 LDB_CONTROL_EXTENDED_DN_OID, 1002 false, NULL); 1003 } 1004 if (ret != LDB_SUCCESS) { 1005 return ldb_module_done(ac->req, NULL, NULL, 1006 ret); 1007 } 1008 1009 ac->op_controls = talloc_steal(ac, ares->controls); 1010 ac->op_response = talloc_steal(ac, ares->response); 1011 1012 return ldb_next_request(ac->module, search_req); 1013 1014 } else { 1015 return ldb_module_done(ac->req, ares->controls, 1016 ares->response, ares->error); 1017 } 1018} 1019 1020/* Reconstruct the original request, but pointing at our local callback to finish things off */ 1021static int la_down_req(struct la_context *ac) 1022{ 1023 struct ldb_request *down_req; 1024 int ret; 1025 struct ldb_context *ldb; 1026 1027 ldb = ldb_module_get_ctx(ac->module); 1028 1029 switch (ac->req->operation) { 1030 case LDB_ADD: 1031 ret = ldb_build_add_req(&down_req, ldb, ac, 1032 ac->req->op.add.message, 1033 ac->req->controls, 1034 ac, la_add_callback, 1035 ac->req); 1036 break; 1037 case LDB_MODIFY: 1038 ret = ldb_build_mod_req(&down_req, ldb, ac, 1039 ac->req->op.mod.message, 1040 ac->req->controls, 1041 ac, la_mod_del_callback, 1042 ac->req); 1043 break; 1044 case LDB_DELETE: 1045 ret = ldb_build_del_req(&down_req, ldb, ac, 1046 ac->req->op.del.dn, 1047 ac->req->controls, 1048 ac, la_mod_del_callback, 1049 ac->req); 1050 break; 1051 case LDB_RENAME: 1052 ret = ldb_build_rename_req(&down_req, ldb, ac, 1053 ac->req->op.rename.olddn, 1054 ac->req->op.rename.newdn, 1055 ac->req->controls, 1056 ac, la_rename_callback, 1057 ac->req); 1058 break; 1059 default: 1060 ret = LDB_ERR_OPERATIONS_ERROR; 1061 } 1062 if (ret != LDB_SUCCESS) { 1063 return ret; 1064 } 1065 1066 return ldb_next_request(ac->module, down_req); 1067} 1068 1069/* 1070 use the GUID part of an extended DN to find the target DN, in case 1071 it has moved 1072 */ 1073static int la_find_dn_target(struct ldb_module *module, struct la_context *ac, 1074 struct GUID *guid, struct ldb_dn **dn) 1075{ 1076 return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, GUID_string(ac, guid), dn); 1077} 1078 1079/* apply one la_context op change */ 1080static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op) 1081{ 1082 struct ldb_message_element *ret_el; 1083 struct ldb_request *mod_req; 1084 struct ldb_message *new_msg; 1085 struct ldb_context *ldb; 1086 int ret; 1087 1088 ldb = ldb_module_get_ctx(ac->module); 1089 1090 /* Create the modify request */ 1091 new_msg = ldb_msg_new(ac); 1092 if (!new_msg) { 1093 ldb_oom(ldb); 1094 return LDB_ERR_OPERATIONS_ERROR; 1095 } 1096 1097 ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn); 1098 if (ret != LDB_SUCCESS) { 1099 return ret; 1100 } 1101 1102 if (op->op == LA_OP_ADD) { 1103 ret = ldb_msg_add_empty(new_msg, op->name, 1104 LDB_FLAG_MOD_ADD, &ret_el); 1105 } else { 1106 ret = ldb_msg_add_empty(new_msg, op->name, 1107 LDB_FLAG_MOD_DELETE, &ret_el); 1108 } 1109 if (ret != LDB_SUCCESS) { 1110 return ret; 1111 } 1112 ret_el->values = talloc_array(new_msg, struct ldb_val, 1); 1113 if (!ret_el->values) { 1114 ldb_oom(ldb); 1115 return LDB_ERR_OPERATIONS_ERROR; 1116 } 1117 ret_el->num_values = 1; 1118 if (op->op == LA_OP_ADD) { 1119 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1)); 1120 } else { 1121 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1)); 1122 } 1123 1124#if 0 1125 ldb_debug(ldb, LDB_DEBUG_WARNING, 1126 "link on %s %s: %s %s\n", 1127 ldb_dn_get_linearized(new_msg->dn), ret_el->name, 1128 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted"); 1129#endif 1130 1131 ret = ldb_build_mod_req(&mod_req, ldb, op, 1132 new_msg, 1133 NULL, 1134 NULL, 1135 ldb_op_default_callback, 1136 NULL); 1137 if (ret != LDB_SUCCESS) { 1138 return ret; 1139 } 1140 talloc_steal(mod_req, new_msg); 1141 1142 if (DEBUGLVL(4)) { 1143 DEBUG(4,("Applying linked attribute change:\n%s\n", 1144 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg))); 1145 } 1146 1147 /* Run the new request */ 1148 ret = ldb_next_request(module, mod_req); 1149 1150 /* we need to wait for this to finish, as we are being called 1151 from the synchronous end_transaction hook of this module */ 1152 if (ret == LDB_SUCCESS) { 1153 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL); 1154 } 1155 1156 if (ret != LDB_SUCCESS) { 1157 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s' %s\n", 1158 ldb_errstring(ldb), 1159 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)); 1160 } 1161 1162 return ret; 1163} 1164 1165/* apply one set of la_context changes */ 1166static int la_do_mod_request(struct ldb_module *module, struct la_context *ac) 1167{ 1168 struct la_op_store *op; 1169 1170 for (op = ac->ops; op; op=op->next) { 1171 int ret = la_do_op_request(module, ac, op); 1172 if (ret != LDB_SUCCESS) { 1173 if (ret != LDB_ERR_NO_SUCH_OBJECT) { 1174 return ret; 1175 } 1176 } 1177 } 1178 1179 return LDB_SUCCESS; 1180} 1181 1182 1183/* 1184 we hook into the transaction operations to allow us to 1185 perform the linked attribute updates at the end of the whole 1186 transaction. This allows a forward linked attribute to be created 1187 before the target is created, as long as the target is created 1188 in the same transaction 1189 */ 1190static int linked_attributes_start_transaction(struct ldb_module *module) 1191{ 1192 /* create our private structure for this transaction */ 1193 struct la_private *la_private = talloc_get_type(ldb_module_get_private(module), 1194 struct la_private); 1195 talloc_free(la_private); 1196 la_private = talloc(module, struct la_private); 1197 if (la_private == NULL) { 1198 return LDB_ERR_OPERATIONS_ERROR; 1199 } 1200 la_private->la_list = NULL; 1201 ldb_module_set_private(module, la_private); 1202 return ldb_next_start_trans(module); 1203} 1204 1205/* 1206 on prepare commit we loop over our queued la_context structures 1207 and apply each of them 1208 */ 1209static int linked_attributes_prepare_commit(struct ldb_module *module) 1210{ 1211 struct la_private *la_private = 1212 talloc_get_type(ldb_module_get_private(module), struct la_private); 1213 struct la_context *ac; 1214 1215 /* walk the list backwards, to do the first entry first, as we 1216 * added the entries with DLIST_ADD() which puts them at the 1217 * start of the list */ 1218 for (ac = la_private->la_list; ac && ac->next; ac=ac->next) ; 1219 1220 for (; ac; ac=ac->prev) { 1221 int ret; 1222 ac->req = NULL; 1223 ret = la_do_mod_request(module, ac); 1224 if (ret != LDB_SUCCESS) { 1225 DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret)); 1226 talloc_free(la_private); 1227 ldb_module_set_private(module, NULL); 1228 return ret; 1229 } 1230 } 1231 1232 talloc_free(la_private); 1233 ldb_module_set_private(module, NULL); 1234 1235 return ldb_next_prepare_commit(module); 1236} 1237 1238static int linked_attributes_del_transaction(struct ldb_module *module) 1239{ 1240 struct la_private *la_private = 1241 talloc_get_type(ldb_module_get_private(module), struct la_private); 1242 talloc_free(la_private); 1243 ldb_module_set_private(module, NULL); 1244 return ldb_next_del_trans(module); 1245} 1246 1247 1248_PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = { 1249 .name = "linked_attributes", 1250 .add = linked_attributes_add, 1251 .modify = linked_attributes_modify, 1252 .del = linked_attributes_del, 1253 .rename = linked_attributes_rename, 1254 .start_transaction = linked_attributes_start_transaction, 1255 .prepare_commit = linked_attributes_prepare_commit, 1256 .del_transaction = linked_attributes_del_transaction, 1257}; 1258