1/* 2 ldb database library 3 4 Copyright (C) Simo Sorce 2006 5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006 6 7 ** NOTE! The following LGPL license applies to the ldb 8 ** library. This does NOT imply that all of Samba is released 9 ** under the LGPL 10 11 This library is free software; you can redistribute it and/or 12 modify it under the terms of the GNU Lesser General Public 13 License as published by the Free Software Foundation; either 14 version 3 of the License, or (at your option) any later version. 15 16 This library is distributed in the hope that it will be useful, 17 but WITHOUT ANY WARRANTY; without even the implied warranty of 18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 Lesser General Public License for more details. 20 21 You should have received a copy of the GNU Lesser General Public 22 License along with this library; if not, see <http://www.gnu.org/licenses/>. 23*/ 24 25/* 26 * Name: ldb 27 * 28 * Component: objectClass sorting module 29 * 30 * Description: sort the objectClass attribute into the class hierarchy 31 * 32 * Author: Andrew Bartlett 33 */ 34 35#include "includes.h" 36#include "ldb/include/includes.h" 37 38struct oc_context { 39 40 enum oc_step {OC_DO_REQ, OC_SEARCH_SELF, OC_DO_MOD} step; 41 42 struct ldb_module *module; 43 struct ldb_request *orig_req; 44 45 struct ldb_request *down_req; 46 47 struct ldb_request *search_req; 48 struct ldb_reply *search_res; 49 50 struct ldb_request *mod_req; 51}; 52 53struct class_list { 54 struct class_list *prev, *next; 55 const char *objectclass; 56}; 57 58static struct ldb_handle *oc_init_handle(struct ldb_request *req, struct ldb_module *module) 59{ 60 struct oc_context *ac; 61 struct ldb_handle *h; 62 63 h = talloc_zero(req, struct ldb_handle); 64 if (h == NULL) { 65 ldb_set_errstring(module->ldb, "Out of Memory"); 66 return NULL; 67 } 68 69 h->module = module; 70 71 ac = talloc_zero(h, struct oc_context); 72 if (ac == NULL) { 73 ldb_set_errstring(module->ldb, "Out of Memory"); 74 talloc_free(h); 75 return NULL; 76 } 77 78 h->private_data = (void *)ac; 79 80 h->state = LDB_ASYNC_INIT; 81 h->status = LDB_SUCCESS; 82 83 ac->module = module; 84 ac->orig_req = req; 85 86 return h; 87} 88 89static int objectclass_sort(struct ldb_module *module, 90 TALLOC_CTX *mem_ctx, 91 struct ldb_message_element *objectclass_element, 92 struct class_list **sorted_out) 93{ 94 int i; 95 int layer; 96 struct class_list *sorted = NULL, *parent_class = NULL, 97 *subclass = NULL, *unsorted = NULL, *current, *poss_subclass; 98 /* DESIGN: 99 * 100 * We work on 4 different 'bins' (implemented here as linked lists): 101 * 102 * * sorted: the eventual list, in the order we wish to push 103 * into the database. This is the only ordered list. 104 * 105 * * parent_class: The current parent class 'bin' we are 106 * trying to find subclasses for 107 * 108 * * subclass: The subclasses we have found so far 109 * 110 * * unsorted: The remaining objectClasses 111 * 112 * The process is a matter of filtering objectClasses up from 113 * unsorted into sorted. Order is irrelevent in the later 3 'bins'. 114 * 115 * We start with 'top' (found and promoted to parent_class 116 * initially). Then we find (in unsorted) all the direct 117 * subclasses of 'top'. parent_classes is concatenated onto 118 * the end of 'sorted', and subclass becomes the list in 119 * parent_class. 120 * 121 * We then repeat, until we find no more subclasses. Any left 122 * over classes are added to the end. 123 * 124 */ 125 126 /* Firstly, dump all the objectClass elements into the 127 * unsorted bin, except for 'top', which is special */ 128 for (i=0; i < objectclass_element->num_values; i++) { 129 current = talloc(mem_ctx, struct class_list); 130 if (!current) { 131 ldb_set_errstring(module->ldb, "objectclass: out of memory allocating objectclass list"); 132 talloc_free(mem_ctx); 133 return LDB_ERR_OPERATIONS_ERROR; 134 } 135 current->objectclass = (const char *)objectclass_element->values[i].data; 136 137 /* this is the root of the tree. We will start 138 * looking for subclasses from here */ 139 if (ldb_attr_cmp("top", current->objectclass) == 0) { 140 DLIST_ADD(parent_class, current); 141 } else { 142 DLIST_ADD(unsorted, current); 143 } 144 } 145 146 /* DEBUGGING aid: how many layers are we down now? */ 147 layer = 0; 148 do { 149 layer++; 150 /* Find all the subclasses of classes in the 151 * parent_classes. Push them onto the subclass list */ 152 153 /* Ensure we don't bother if there are no unsorted entries left */ 154 for (current = parent_class; unsorted && current; current = current->next) { 155 const char **subclasses = ldb_subclass_list(module->ldb, current->objectclass); 156 157 /* Walk the list of possible subclasses in unsorted */ 158 for (poss_subclass = unsorted; poss_subclass; ) { 159 struct class_list *next; 160 161 /* Save the next pointer, as the DLIST_ macros will change poss_subclass->next */ 162 next = poss_subclass->next; 163 164 for (i = 0; subclasses && subclasses[i]; i++) { 165 if (ldb_attr_cmp(poss_subclass->objectclass, subclasses[i]) == 0) { 166 DLIST_REMOVE(unsorted, poss_subclass); 167 DLIST_ADD(subclass, poss_subclass); 168 169 break; 170 } 171 } 172 poss_subclass = next; 173 } 174 } 175 176 /* Now push the parent_classes as sorted, we are done with 177 these. Add to the END of the list by concatenation */ 178 DLIST_CONCATENATE(sorted, parent_class, struct class_list *); 179 180 /* and now find subclasses of these */ 181 parent_class = subclass; 182 subclass = NULL; 183 184 /* If we didn't find any subclasses we will fall out 185 * the bottom here */ 186 } while (parent_class); 187 188 /* This shouldn't happen, and would break MMC, but we can't 189 * afford to loose objectClasses. Perhaps there was no 'top', 190 * or some other schema error? 191 * 192 * Detecting schema errors is the job of the schema module, so 193 * at this layer we just try not to loose data 194 */ 195 DLIST_CONCATENATE(sorted, unsorted, struct class_list *); 196 197 *sorted_out = sorted; 198 return LDB_SUCCESS; 199} 200 201static int objectclass_add(struct ldb_module *module, struct ldb_request *req) 202{ 203 struct ldb_message_element *objectclass_element; 204 struct class_list *sorted, *current; 205 struct ldb_request *down_req; 206 struct ldb_message *msg; 207 int ret; 208 TALLOC_CTX *mem_ctx; 209 210 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectclass_add\n"); 211 212 if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */ 213 return ldb_next_request(module, req); 214 } 215 216 objectclass_element = ldb_msg_find_element(req->op.add.message, "objectClass"); 217 218 /* If no part of this add has an objectClass, then we don't 219 * need to make any changes. cn=rootdse doesn't have an objectClass */ 220 if (!objectclass_element) { 221 return ldb_next_request(module, req); 222 } 223 224 mem_ctx = talloc_new(req); 225 if (mem_ctx == NULL) { 226 return LDB_ERR_OPERATIONS_ERROR; 227 } 228 229 ret = objectclass_sort(module, mem_ctx, objectclass_element, &sorted); 230 if (ret != LDB_SUCCESS) { 231 return ret; 232 } 233 234 /* prepare the first operation */ 235 down_req = talloc(req, struct ldb_request); 236 if (down_req == NULL) { 237 ldb_set_errstring(module->ldb, "Out of memory!"); 238 talloc_free(mem_ctx); 239 return LDB_ERR_OPERATIONS_ERROR; 240 } 241 242 *down_req = *req; /* copy the request */ 243 244 down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message); 245 246 if (down_req->op.add.message == NULL) { 247 talloc_free(mem_ctx); 248 return LDB_ERR_OPERATIONS_ERROR; 249 } 250 251 ldb_msg_remove_attr(msg, "objectClass"); 252 ret = ldb_msg_add_empty(msg, "objectClass", 0, NULL); 253 254 if (ret != LDB_SUCCESS) { 255 talloc_free(mem_ctx); 256 return ret; 257 } 258 259 /* We must completely replace the existing objectClass entry, 260 * because we need it sorted */ 261 262 /* Move from the linked list back into an ldb msg */ 263 for (current = sorted; current; current = current->next) { 264 ret = ldb_msg_add_string(msg, "objectClass", current->objectclass); 265 if (ret != LDB_SUCCESS) { 266 ldb_set_errstring(module->ldb, "objectclass: could not re-add sorted objectclass to modify msg"); 267 talloc_free(mem_ctx); 268 return ret; 269 } 270 } 271 272 talloc_free(mem_ctx); 273 ret = ldb_msg_sanity_check(module->ldb, msg); 274 275 if (ret != LDB_SUCCESS) { 276 return ret; 277 } 278 279 /* go on with the call chain */ 280 ret = ldb_next_request(module, down_req); 281 282 /* do not free down_req as the call results may be linked to it, 283 * it will be freed when the upper level request get freed */ 284 if (ret == LDB_SUCCESS) { 285 req->handle = down_req->handle; 286 } 287 return ret; 288} 289 290static int objectclass_modify(struct ldb_module *module, struct ldb_request *req) 291{ 292 struct ldb_message_element *objectclass_element; 293 struct ldb_message *msg; 294 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectclass_modify\n"); 295 296 if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */ 297 return ldb_next_request(module, req); 298 } 299 300 objectclass_element = ldb_msg_find_element(req->op.mod.message, "objectClass"); 301 302 /* If no part of this touches the objectClass, then we don't 303 * need to make any changes. */ 304 /* If the only operation is the deletion of the objectClass then go on */ 305 if (!objectclass_element) { 306 return ldb_next_request(module, req); 307 } 308 309 switch (objectclass_element->flags & LDB_FLAG_MOD_MASK) { 310 case LDB_FLAG_MOD_DELETE: 311 /* Delete everything? Probably totally illigal, but hey! */ 312 if (objectclass_element->num_values == 0) { 313 return ldb_next_request(module, req); 314 } 315 break; 316 case LDB_FLAG_MOD_REPLACE: 317 { 318 struct ldb_request *down_req; 319 struct class_list *sorted, *current; 320 TALLOC_CTX *mem_ctx; 321 int ret; 322 mem_ctx = talloc_new(req); 323 if (mem_ctx == NULL) { 324 return LDB_ERR_OPERATIONS_ERROR; 325 } 326 327 /* prepare the first operation */ 328 down_req = talloc(req, struct ldb_request); 329 if (down_req == NULL) { 330 ldb_set_errstring(module->ldb, "Out of memory!"); 331 talloc_free(mem_ctx); 332 return LDB_ERR_OPERATIONS_ERROR; 333 } 334 335 *down_req = *req; /* copy the request */ 336 337 down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message); 338 339 if (down_req->op.add.message == NULL) { 340 talloc_free(mem_ctx); 341 return LDB_ERR_OPERATIONS_ERROR; 342 } 343 344 ret = objectclass_sort(module, mem_ctx, objectclass_element, &sorted); 345 if (ret != LDB_SUCCESS) { 346 return ret; 347 } 348 349 /* We must completely replace the existing objectClass entry, 350 * because we need it sorted */ 351 352 ldb_msg_remove_attr(msg, "objectClass"); 353 ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL); 354 355 if (ret != LDB_SUCCESS) { 356 talloc_free(mem_ctx); 357 return ret; 358 } 359 360 /* Move from the linked list back into an ldb msg */ 361 for (current = sorted; current; current = current->next) { 362 ret = ldb_msg_add_string(msg, "objectClass", current->objectclass); 363 if (ret != LDB_SUCCESS) { 364 ldb_set_errstring(module->ldb, "objectclass: could not re-add sorted objectclass to modify msg"); 365 talloc_free(mem_ctx); 366 return ret; 367 } 368 } 369 370 talloc_free(mem_ctx); 371 372 ret = ldb_msg_sanity_check(module->ldb, msg); 373 if (ret != LDB_SUCCESS) { 374 talloc_free(mem_ctx); 375 return ret; 376 } 377 378 /* go on with the call chain */ 379 ret = ldb_next_request(module, down_req); 380 381 /* do not free down_req as the call results may be linked to it, 382 * it will be freed when the upper level request get freed */ 383 if (ret == LDB_SUCCESS) { 384 req->handle = down_req->handle; 385 } 386 return ret; 387 } 388 } 389 390 { 391 struct ldb_handle *h; 392 struct oc_context *ac; 393 394 h = oc_init_handle(req, module); 395 if (!h) { 396 return LDB_ERR_OPERATIONS_ERROR; 397 } 398 ac = talloc_get_type(h->private_data, struct oc_context); 399 400 /* return or own handle to deal with this call */ 401 req->handle = h; 402 403 /* prepare the first operation */ 404 ac->down_req = talloc(ac, struct ldb_request); 405 if (ac->down_req == NULL) { 406 ldb_set_errstring(module->ldb, "Out of memory!"); 407 return LDB_ERR_OPERATIONS_ERROR; 408 } 409 410 *(ac->down_req) = *req; /* copy the request */ 411 412 ac->down_req->context = NULL; 413 ac->down_req->callback = NULL; 414 ldb_set_timeout_from_prev_req(module->ldb, req, ac->down_req); 415 416 ac->step = OC_DO_REQ; 417 418 return ldb_next_request(module, ac->down_req); 419 } 420} 421 422static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) 423{ 424 struct oc_context *ac; 425 426 if (!context || !ares) { 427 ldb_set_errstring(ldb, "NULL Context or Result in callback"); 428 return LDB_ERR_OPERATIONS_ERROR; 429 } 430 431 ac = talloc_get_type(context, struct oc_context); 432 433 /* we are interested only in the single reply (base search) we receive here */ 434 if (ares->type == LDB_REPLY_ENTRY) { 435 if (ac->search_res != NULL) { 436 ldb_set_errstring(ldb, "Too many results"); 437 talloc_free(ares); 438 return LDB_ERR_OPERATIONS_ERROR; 439 } 440 441 ac->search_res = talloc_move(ac, &ares); 442 } else { 443 talloc_free(ares); 444 } 445 446 return LDB_SUCCESS; 447} 448 449static int objectclass_search_self(struct ldb_handle *h) { 450 451 struct oc_context *ac; 452 static const char * const attrs[] = { "objectClass", NULL }; 453 454 ac = talloc_get_type(h->private_data, struct oc_context); 455 456 /* prepare the search operation */ 457 ac->search_req = talloc_zero(ac, struct ldb_request); 458 if (ac->search_req == NULL) { 459 ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n"); 460 return LDB_ERR_OPERATIONS_ERROR; 461 } 462 463 ac->search_req->operation = LDB_SEARCH; 464 ac->search_req->op.search.base = ac->orig_req->op.mod.message->dn; 465 ac->search_req->op.search.scope = LDB_SCOPE_BASE; 466 ac->search_req->op.search.tree = ldb_parse_tree(ac->search_req, NULL); 467 if (ac->search_req->op.search.tree == NULL) { 468 ldb_set_errstring(ac->module->ldb, "objectclass: Internal error producing null search"); 469 return LDB_ERR_OPERATIONS_ERROR; 470 } 471 ac->search_req->op.search.attrs = attrs; 472 ac->search_req->controls = NULL; 473 ac->search_req->context = ac; 474 ac->search_req->callback = get_self_callback; 475 ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->search_req); 476 477 ac->step = OC_SEARCH_SELF; 478 479 return ldb_next_request(ac->module, ac->search_req); 480} 481 482static int objectclass_do_mod(struct ldb_handle *h) { 483 484 struct oc_context *ac; 485 struct ldb_message_element *objectclass_element; 486 struct ldb_message *msg; 487 TALLOC_CTX *mem_ctx; 488 struct class_list *sorted, *current; 489 int ret; 490 491 ac = talloc_get_type(h->private_data, struct oc_context); 492 493 mem_ctx = talloc_new(ac); 494 if (mem_ctx == NULL) { 495 return LDB_ERR_OPERATIONS_ERROR; 496 } 497 498 ac->mod_req = talloc(ac, struct ldb_request); 499 if (ac->mod_req == NULL) { 500 talloc_free(mem_ctx); 501 return LDB_ERR_OPERATIONS_ERROR; 502 } 503 504 ac->mod_req->operation = LDB_MODIFY; 505 ac->mod_req->controls = NULL; 506 ac->mod_req->context = ac; 507 ac->mod_req->callback = NULL; 508 ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->mod_req); 509 510 /* use a new message structure */ 511 ac->mod_req->op.mod.message = msg = ldb_msg_new(ac->mod_req); 512 if (msg == NULL) { 513 ldb_set_errstring(ac->module->ldb, "objectclass: could not create new modify msg"); 514 talloc_free(mem_ctx); 515 return LDB_ERR_OPERATIONS_ERROR; 516 } 517 518 /* This is now the objectClass list from the database */ 519 objectclass_element = ldb_msg_find_element(ac->search_res->message, 520 "objectClass"); 521 if (!objectclass_element) { 522 /* Where did it go? Move along now, nothing to see here */ 523 talloc_free(mem_ctx); 524 return LDB_SUCCESS; 525 } 526 527 /* modify dn */ 528 msg->dn = ac->orig_req->op.mod.message->dn; 529 530 ret = objectclass_sort(ac->module, mem_ctx, objectclass_element, &sorted); 531 if (ret != LDB_SUCCESS) { 532 return ret; 533 } 534 535 /* We must completely replace the existing objectClass entry. 536 * We could do a constrained add/del, but we are meant to be 537 * in a transaction... */ 538 539 ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL); 540 if (ret != LDB_SUCCESS) { 541 ldb_set_errstring(ac->module->ldb, "objectclass: could not clear objectclass in modify msg"); 542 talloc_free(mem_ctx); 543 return ret; 544 } 545 546 /* Move from the linked list back into an ldb msg */ 547 for (current = sorted; current; current = current->next) { 548 ret = ldb_msg_add_string(msg, "objectClass", current->objectclass); 549 if (ret != LDB_SUCCESS) { 550 ldb_set_errstring(ac->module->ldb, "objectclass: could not re-add sorted objectclass to modify msg"); 551 talloc_free(mem_ctx); 552 return ret; 553 } 554 } 555 556 ret = ldb_msg_sanity_check(ac->module->ldb, msg); 557 if (ret != LDB_SUCCESS) { 558 talloc_free(mem_ctx); 559 return ret; 560 } 561 562 563 h->state = LDB_ASYNC_INIT; 564 h->status = LDB_SUCCESS; 565 566 ac->step = OC_DO_MOD; 567 568 talloc_free(mem_ctx); 569 /* perform the search */ 570 return ldb_next_request(ac->module, ac->mod_req); 571} 572 573static int oc_wait(struct ldb_handle *handle) { 574 struct oc_context *ac; 575 int ret; 576 577 if (!handle || !handle->private_data) { 578 return LDB_ERR_OPERATIONS_ERROR; 579 } 580 581 if (handle->state == LDB_ASYNC_DONE) { 582 return handle->status; 583 } 584 585 handle->state = LDB_ASYNC_PENDING; 586 handle->status = LDB_SUCCESS; 587 588 ac = talloc_get_type(handle->private_data, struct oc_context); 589 590 switch (ac->step) { 591 case OC_DO_REQ: 592 ret = ldb_wait(ac->down_req->handle, LDB_WAIT_NONE); 593 594 if (ret != LDB_SUCCESS) { 595 handle->status = ret; 596 goto done; 597 } 598 if (ac->down_req->handle->status != LDB_SUCCESS) { 599 handle->status = ac->down_req->handle->status; 600 goto done; 601 } 602 603 if (ac->down_req->handle->state != LDB_ASYNC_DONE) { 604 return LDB_SUCCESS; 605 } 606 607 /* mods done, go on */ 608 return objectclass_search_self(handle); 609 610 case OC_SEARCH_SELF: 611 ret = ldb_wait(ac->search_req->handle, LDB_WAIT_NONE); 612 613 if (ret != LDB_SUCCESS) { 614 handle->status = ret; 615 goto done; 616 } 617 if (ac->search_req->handle->status != LDB_SUCCESS) { 618 handle->status = ac->search_req->handle->status; 619 goto done; 620 } 621 622 if (ac->search_req->handle->state != LDB_ASYNC_DONE) { 623 return LDB_SUCCESS; 624 } 625 626 /* self search done, go on */ 627 return objectclass_do_mod(handle); 628 629 case OC_DO_MOD: 630 ret = ldb_wait(ac->mod_req->handle, LDB_WAIT_NONE); 631 632 if (ret != LDB_SUCCESS) { 633 handle->status = ret; 634 goto done; 635 } 636 if (ac->mod_req->handle->status != LDB_SUCCESS) { 637 handle->status = ac->mod_req->handle->status; 638 goto done; 639 } 640 641 if (ac->mod_req->handle->state != LDB_ASYNC_DONE) { 642 return LDB_SUCCESS; 643 } 644 645 break; 646 647 default: 648 ret = LDB_ERR_OPERATIONS_ERROR; 649 goto done; 650 } 651 652 ret = LDB_SUCCESS; 653 654done: 655 handle->state = LDB_ASYNC_DONE; 656 return ret; 657} 658 659static int oc_wait_all(struct ldb_handle *handle) { 660 661 int ret; 662 663 while (handle->state != LDB_ASYNC_DONE) { 664 ret = oc_wait(handle); 665 if (ret != LDB_SUCCESS) { 666 return ret; 667 } 668 } 669 670 return handle->status; 671} 672 673static int objectclass_wait(struct ldb_handle *handle, enum ldb_wait_type type) 674{ 675 if (type == LDB_WAIT_ALL) { 676 return oc_wait_all(handle); 677 } else { 678 return oc_wait(handle); 679 } 680} 681 682static const struct ldb_module_ops objectclass_ops = { 683 .name = "objectclass", 684 .add = objectclass_add, 685 .modify = objectclass_modify, 686 .wait = objectclass_wait 687}; 688 689int ldb_objectclass_init(void) 690{ 691 return ldb_register_module(&objectclass_ops); 692} 693 694