root/source4/dsdb/samdb/ldb_modules/linked_attributes.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. linked_attributes_init
  2. la_store_op
  3. linked_attributes_add
  4. la_mod_search_callback
  5. linked_attributes_modify
  6. linked_attributes_del
  7. linked_attributes_rename
  8. la_op_search_callback
  9. la_do_mod_request
  10. la_mod_callback
  11. la_mod_del_callback
  12. la_rename_callback
  13. la_add_callback
  14. la_down_req

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

/* [<][>][^][v][top][bottom][index][help] */