LCOV - code coverage report
Current view: top level - source4/dsdb/samdb/ldb_modules - linked_attributes.c (source / functions) Hit Total Coverage
Test: coverage report for master 70ed9daf Lines: 469 682 68.8 %
Date: 2024-01-11 09:59:51 Functions: 22 23 95.7 %

          Line data    Source code
       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             :    Copyright (C) Matthieu Patou <mat@matws.net> 2011
       7             :    Copyright (C) Andrew Tridgell 2009
       8             : 
       9             :    This program is free software; you can redistribute it and/or modify
      10             :    it under the terms of the GNU General Public License as published by
      11             :    the Free Software Foundation; either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    This program is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU General Public License
      20             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      21             : */
      22             : 
      23             : /*
      24             :  *  Name: ldb
      25             :  *
      26             :  *  Component: ldb linked_attributes module
      27             :  *
      28             :  *  Description: Module to ensure linked attribute pairs (i.e. forward-links
      29             :  *  and backlinks) remain in sync.
      30             :  *
      31             :  *  Backlinks are 'plain' links (without extra metadata). When the link target
      32             :  *  object is modified (e.g. renamed), we use the backlinks to keep the link
      33             :  *  source object updated. Note there are some cases where we can't do this:
      34             :  *    - one-way links, which don't have a corresponding backlink
      35             :  *    - two-way deactivated links, i.e. when a user is removed from a group,
      36             :  *      the forward 'member' link still exists (but is inactive), however, the
      37             :  *      'memberOf' backlink is deleted.
      38             :  *  In these cases, we can end up with a dangling forward link which is
      39             :  *  incorrect (i.e. the target has been renamed or deleted). We have dbcheck
      40             :  *  rules to detect and fix this, and cope otherwise by filtering at runtime
      41             :  *  (i.e. in the extended_dn module).
      42             :  *
      43             :  *  See also repl_meta_data.c, which handles updating links for deleted
      44             :  *  objects, as well as link changes received from another DC.
      45             :  *
      46             :  *  Author: Andrew Bartlett
      47             :  */
      48             : 
      49             : #include "includes.h"
      50             : #include "ldb_module.h"
      51             : #include "util/dlinklist.h"
      52             : #include "dsdb/samdb/samdb.h"
      53             : #include "librpc/gen_ndr/ndr_misc.h"
      54             : #include "dsdb/samdb/ldb_modules/util.h"
      55             : 
      56             : #undef strcasecmp
      57             : 
      58             : struct la_private_transaction {
      59             :         struct la_context *la_list;
      60             : };
      61             : 
      62             : 
      63             : struct la_private {
      64             :         struct la_private_transaction *transaction;
      65             :         bool sorted_links;
      66             : };
      67             : 
      68             : struct la_op_store {
      69             :         struct la_op_store *next;
      70             :         struct la_op_store *prev;
      71             :         enum la_op {LA_OP_ADD, LA_OP_DEL} op;
      72             :         struct GUID guid;
      73             :         char *name;
      74             : };
      75             : 
      76             : struct replace_context {
      77             :         struct la_context *ac;
      78             :         unsigned int num_elements;
      79             :         struct ldb_message_element *el;
      80             : };
      81             : 
      82             : struct la_context {
      83             :         struct la_context *next, *prev;
      84             :         const struct dsdb_schema *schema;
      85             :         struct ldb_module *module;
      86             :         struct ldb_request *req;
      87             :         struct ldb_dn *mod_dn;
      88             :         struct replace_context *rc;
      89             :         struct la_op_store *ops;
      90             :         struct ldb_extended *op_response;
      91             :         struct ldb_control **op_controls;
      92             :         /*
      93             :          * For futur use
      94             :          * will tell which GC to use for resolving links
      95             :          */
      96             :         char *gc_dns_name;
      97             : };
      98             : 
      99             : 
     100           0 : static int handle_verify_name_control(TALLOC_CTX *ctx, struct ldb_context *ldb,
     101             :                                         struct ldb_control *control, struct la_context *ac)
     102             : {
     103             :         /*
     104             :          * If we are a GC let's remove the control,
     105             :          * if there is a specified GC check that is us.
     106             :          */
     107           0 :         struct ldb_verify_name_control *lvnc = talloc_get_type_abort(control->data, struct ldb_verify_name_control);
     108           0 :         if (samdb_is_gc(ldb)) {
     109             :                 /* Because we can't easily talloc a struct ldb_dn*/
     110           0 :                 struct ldb_dn **dn = talloc_array(ctx, struct ldb_dn *, 1);
     111           0 :                 int ret = samdb_server_reference_dn(ldb, ctx, dn);
     112           0 :                 const char *dns;
     113             : 
     114           0 :                 if (ret != LDB_SUCCESS) {
     115           0 :                         return ldb_operr(ldb);
     116             :                 }
     117             : 
     118           0 :                 dns = samdb_dn_to_dnshostname(ldb, ctx, *dn);
     119           0 :                 if (!dns) {
     120           0 :                         return ldb_operr(ldb);
     121             :                 }
     122           0 :                 if (!lvnc->gc || strcasecmp(dns, lvnc->gc) == 0) {
     123           0 :                         if (!ldb_save_controls(control, ctx, NULL)) {
     124           0 :                                 return ldb_operr(ldb);
     125             :                         }
     126             :                 } else {
     127           0 :                         control->critical = true;
     128             :                 }
     129           0 :                 talloc_free(dn);
     130             :         } else {
     131             :                 /* For the moment we don't remove the control is this case in order
     132             :                  * to fail the request. It's better than having the client thinking
     133             :                  * that we honnor its control.
     134             :                  * Hopefully only a very small set of usecase should hit this problem.
     135             :                  */
     136           0 :                 if (lvnc->gc) {
     137           0 :                         ac->gc_dns_name = talloc_strdup(ac, lvnc->gc);
     138             :                 }
     139           0 :                 control->critical = true;
     140             :         }
     141             : 
     142           0 :         return LDB_SUCCESS;
     143             : }
     144             : 
     145     1958858 : static struct la_context *linked_attributes_init(struct ldb_module *module,
     146             :                                                  struct ldb_request *req)
     147             : {
     148      112946 :         struct ldb_context *ldb;
     149      112946 :         struct la_context *ac;
     150             : 
     151     1958858 :         ldb = ldb_module_get_ctx(module);
     152             : 
     153     1958858 :         ac = talloc_zero(req, struct la_context);
     154     1958858 :         if (ac == NULL) {
     155           0 :                 ldb_oom(ldb);
     156           0 :                 return NULL;
     157             :         }
     158             : 
     159     1958858 :         ac->schema = dsdb_get_schema(ldb, ac);
     160     1958858 :         ac->module = module;
     161     1958858 :         ac->req = req;
     162             : 
     163     1958858 :         return ac;
     164             : }
     165             : 
     166             : /*
     167             :   turn a DN into a GUID
     168             :  */
     169      109667 : static int la_guid_from_dn(struct ldb_module *module,
     170             :                            struct ldb_request *parent,
     171             :                            struct ldb_dn *dn, struct GUID *guid)
     172             : {
     173         276 :         NTSTATUS status;
     174         276 :         int ret;
     175             : 
     176      109667 :         status = dsdb_get_extended_dn_guid(dn, guid, "GUID");
     177      109667 :         if (NT_STATUS_IS_OK(status)) {
     178      107961 :                 return LDB_SUCCESS;
     179             :         }
     180        1430 :         if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
     181           0 :                 DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n",
     182             :                          ldb_dn_get_linearized(dn)));
     183           0 :                 return ldb_operr(ldb_module_get_ctx(module));
     184             :         }
     185             : 
     186        1430 :         ret = dsdb_module_guid_by_dn(module, dn, guid, parent);
     187        1430 :         if (ret != LDB_SUCCESS) {
     188           0 :                 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
     189             :                          ldb_dn_get_linearized(dn)));
     190           0 :                 return ret;
     191             :         }
     192        1430 :         return LDB_SUCCESS;
     193             : }
     194             : 
     195             : 
     196             : /* Common routine to handle reading the attributes and creating a
     197             :  * series of modify requests */
     198        3342 : static int la_store_op(struct la_context *ac,
     199             :                        enum la_op op, 
     200             :                        const struct dsdb_attribute *schema_attr,
     201             :                        struct ldb_val *dn,
     202             :                        const char *name)
     203             : {
     204           0 :         struct ldb_context *ldb;
     205           0 :         struct la_op_store *os;
     206           0 :         struct ldb_dn *op_dn;
     207           0 :         struct dsdb_dn *dsdb_dn;
     208           0 :         int ret;
     209             : 
     210        3342 :         ldb = ldb_module_get_ctx(ac->module);
     211             : 
     212             : 
     213        3342 :         os = talloc_zero(ac, struct la_op_store);
     214        3342 :         if (!os) {
     215           0 :                 return ldb_oom(ldb);
     216             :         }
     217             : 
     218        3342 :         dsdb_dn = dsdb_dn_parse(os, ldb, dn, schema_attr->syntax->ldap_oid);
     219             : 
     220        3342 :         if (!dsdb_dn) {
     221           0 :                 ldb_asprintf_errstring(ldb,
     222             :                                        "could not parse attribute as a DN");
     223           0 :                 TALLOC_FREE(os);
     224           0 :                 return LDB_ERR_INVALID_DN_SYNTAX;
     225             :         }
     226             : 
     227        3342 :         op_dn = dsdb_dn->dn;
     228             : 
     229        3342 :         os->op = op;
     230             : 
     231        3342 :         ret = la_guid_from_dn(ac->module, ac->req, op_dn, &os->guid);
     232        3342 :         talloc_free(op_dn);
     233        3342 :         if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
     234             :                 /* we are deleting an object, and we've found it has a
     235             :                  * forward link to a target that no longer
     236             :                  * exists. This is not an error in the delete, and we
     237             :                  * should just not do the deferred delete of the
     238             :                  * target attribute
     239             :                  */
     240           0 :                 talloc_free(os);
     241           0 :                 return LDB_SUCCESS;
     242             :         }
     243        3342 :         if (ret != LDB_SUCCESS) {
     244           0 :                 return ret;
     245             :         }
     246             : 
     247        3342 :         os->name = talloc_strdup(os, name);
     248        3342 :         if (!os->name) {
     249           0 :                 return ldb_oom(ldb);
     250             :         }
     251             : 
     252             :         /* Do deletes before adds */
     253        3342 :         if (op == LA_OP_ADD) {
     254        1787 :                 DLIST_ADD_END(ac->ops, os);
     255             :         } else {
     256             :                 /* By adding to the head of the list, we do deletes before
     257             :                  * adds when processing a replace */
     258        1555 :                 DLIST_ADD(ac->ops, os);
     259             :         }
     260             : 
     261        3342 :         return LDB_SUCCESS;
     262             : }
     263             : 
     264             : static int la_queue_mod_request(struct la_context *ac);
     265             : static int la_down_req(struct la_context *ac);
     266             : 
     267             : 
     268             : 
     269             : /* add */
     270      922851 : static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
     271             : {
     272       83765 :         struct ldb_context *ldb;
     273       83765 :         const struct dsdb_attribute *target_attr;
     274       83765 :         struct la_context *ac;
     275       83765 :         const char *attr_name;
     276       83765 :         struct ldb_control *ctrl;
     277       83765 :         unsigned int i, j;
     278       83765 :         struct ldb_control *control;
     279       83765 :         int ret;
     280             : 
     281      922851 :         ldb = ldb_module_get_ctx(module);
     282             : 
     283      922851 :         if (ldb_dn_is_special(req->op.add.message->dn)) {
     284             :                 /* do not manipulate our control entries */
     285        1517 :                 return ldb_next_request(module, req);
     286             :         }
     287             : 
     288      921334 :         ac = linked_attributes_init(module, req);
     289      921334 :         if (!ac) {
     290           0 :                 return ldb_operr(ldb);
     291             :         }
     292             : 
     293      921334 :         control = ldb_request_get_control(req, LDB_CONTROL_VERIFY_NAME_OID);
     294      921334 :         if (control != NULL && control->data != NULL) {
     295           0 :                 ret = handle_verify_name_control(req, ldb, control, ac);
     296           0 :                 if (ret != LDB_SUCCESS) {
     297           0 :                         return ldb_operr(ldb);
     298             :                 }
     299             :         }
     300             : 
     301      921334 :         if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
     302             :                 /* don't do anything special for linked attributes, repl_meta_data has done it */
     303      526880 :                 talloc_free(ac);
     304      526880 :                 return ldb_next_request(module, req);
     305             :         }
     306      394454 :         ctrl->critical = false;
     307             : 
     308      394454 :         if (!ac->schema) {
     309             :                 /* without schema, this doesn't make any sense */
     310           0 :                 talloc_free(ac);
     311           0 :                 return ldb_next_request(module, req);
     312             :         }
     313             : 
     314             : 
     315             :         /* Need to ensure we only have forward links being specified */
     316     8072970 :         for (i=0; i < req->op.add.message->num_elements; i++) {
     317     7678516 :                 const struct ldb_message_element *el = &req->op.add.message->elements[i];
     318           0 :                 const struct dsdb_attribute *schema_attr
     319     7678516 :                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
     320     7678516 :                 if (!schema_attr) {
     321           0 :                         ldb_asprintf_errstring(ldb,
     322             :                                                "%s: attribute %s is not a valid attribute in schema",
     323             :                                                __FUNCTION__,
     324           0 :                                                el->name);
     325           0 :                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
     326             :                 }
     327             : 
     328             :                 /* this could be a link with no partner, in which case
     329             :                    there is no special work to do */
     330     7678516 :                 if (schema_attr->linkID == 0) {
     331     7678293 :                         continue;
     332             :                 }
     333             : 
     334             :                 /* this part of the code should only be handling forward links */
     335         223 :                 SMB_ASSERT((schema_attr->linkID & 1) == 0);
     336             : 
     337             :                 /* Even link IDs are for the originating attribute */
     338         223 :                 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
     339         223 :                 if (!target_attr) {
     340             :                         /*
     341             :                          * windows 2003 has a broken schema where
     342             :                          * the definition of msDS-IsDomainFor
     343             :                          * is missing (which is supposed to be
     344             :                          * the backlink of the msDS-HasDomainNCs
     345             :                          * attribute
     346             :                          */
     347          38 :                         continue;
     348             :                 }
     349             : 
     350         185 :                 attr_name = target_attr->lDAPDisplayName;
     351             : 
     352         534 :                 for (j = 0; j < el->num_values; j++) {
     353         349 :                         ret = la_store_op(ac, LA_OP_ADD,
     354             :                                           schema_attr,
     355         349 :                                           &el->values[j],
     356             :                                           attr_name);
     357         349 :                         if (ret != LDB_SUCCESS) {
     358           0 :                                 return ret;
     359             :                         }
     360             :                 }
     361             :         }
     362             : 
     363             :         /* if no linked attributes are present continue */
     364      394454 :         if (ac->ops == NULL) {
     365             :                 /* nothing to do for this module, proceed */
     366      394293 :                 talloc_free(ac);
     367      394293 :                 return ldb_next_request(module, req);
     368             :         }
     369             : 
     370             :         /* start with the original request */
     371         161 :         return la_down_req(ac);
     372             : }
     373             : 
     374             : /* For a delete or rename, we need to find out what linked attributes
     375             :  * are currently on this DN, and then deal with them.  This is the
     376             :  * callback to the base search */
     377             : 
     378        4392 : static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
     379             : {
     380           0 :         struct ldb_context *ldb;
     381           0 :         const struct dsdb_attribute *schema_attr;
     382           0 :         const struct dsdb_attribute *target_attr;
     383           0 :         struct ldb_message_element *search_el;
     384           0 :         struct replace_context *rc;
     385           0 :         struct la_context *ac;
     386           0 :         const char *attr_name;
     387           0 :         unsigned int i, j;
     388        4392 :         int ret = LDB_SUCCESS;
     389             : 
     390        4392 :         ac = talloc_get_type(req->context, struct la_context);
     391        4392 :         ldb = ldb_module_get_ctx(ac->module);
     392        4392 :         rc = ac->rc;
     393             : 
     394        4392 :         if (!ares) {
     395           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     396             :                                         LDB_ERR_OPERATIONS_ERROR);
     397             :         }
     398        4392 :         if (ares->error != LDB_SUCCESS) {
     399           0 :                 return ldb_module_done(ac->req, ares->controls,
     400             :                                         ares->response, ares->error);
     401             :         }
     402             : 
     403             :         /* Only entries are interesting, and we only want the olddn */
     404        4392 :         switch (ares->type) {
     405        2196 :         case LDB_REPLY_ENTRY:
     406             : 
     407        2196 :                 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
     408           0 :                         ldb_asprintf_errstring(ldb,
     409             :                                                "linked_attributes: %s is not the DN we were looking for",
     410           0 :                                                ldb_dn_get_linearized(ares->message->dn));
     411             :                         /* Guh?  We only asked for this DN */
     412           0 :                         talloc_free(ares);
     413           0 :                         return ldb_module_done(ac->req, NULL, NULL,
     414             :                                                 LDB_ERR_OPERATIONS_ERROR);
     415             :                 }
     416             : 
     417        2196 :                 ac->mod_dn = talloc_steal(ac, ares->message->dn);
     418             : 
     419             :                 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
     420        2274 :                 for (i = 0; rc && i < rc->num_elements; i++) {
     421             : 
     422          78 :                         schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
     423          78 :                         if (!schema_attr) {
     424           0 :                                 ldb_asprintf_errstring(ldb,
     425             :                                         "%s: attribute %s is not a valid attribute in schema",
     426             :                                         __FUNCTION__,
     427           0 :                                         rc->el[i].name);
     428           0 :                                 talloc_free(ares);
     429           0 :                                 return ldb_module_done(ac->req, NULL, NULL,
     430             :                                                 LDB_ERR_OBJECT_CLASS_VIOLATION);
     431             :                         }
     432             : 
     433          78 :                         search_el = ldb_msg_find_element(ares->message,
     434          78 :                                                          rc->el[i].name);
     435             : 
     436             :                         /* See if this element already exists */
     437             :                         /* otherwise just ignore as
     438             :                          * the add has already been scheduled */
     439          78 :                         if ( ! search_el) {
     440          12 :                                 continue;
     441             :                         }
     442             : 
     443          66 :                         target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
     444          66 :                         if (!target_attr) {
     445             :                                 /*
     446             :                                  * windows 2003 has a broken schema where
     447             :                                  * the definition of msDS-IsDomainFor
     448             :                                  * is missing (which is supposed to be
     449             :                                  * the backlink of the msDS-HasDomainNCs
     450             :                                  * attribute
     451             :                                  */
     452           0 :                                 continue;
     453             :                         }
     454          66 :                         attr_name = target_attr->lDAPDisplayName;
     455             : 
     456             :                         /* Now we know what was there, we can remove it for the re-add */
     457         188 :                         for (j = 0; j < search_el->num_values; j++) {
     458         122 :                                 ret = la_store_op(ac, LA_OP_DEL,
     459             :                                                   schema_attr, 
     460         122 :                                                   &search_el->values[j],
     461             :                                                   attr_name);
     462         122 :                                 if (ret != LDB_SUCCESS) {
     463           0 :                                         talloc_free(ares);
     464           0 :                                         return ldb_module_done(ac->req,
     465             :                                                                NULL, NULL, ret);
     466             :                                 }
     467             :                         }
     468             :                 }
     469             : 
     470        2196 :                 break;
     471             : 
     472           0 :         case LDB_REPLY_REFERRAL:
     473             :                 /* ignore */
     474           0 :                 break;
     475             : 
     476        2196 :         case LDB_REPLY_DONE:
     477             : 
     478        2196 :                 talloc_free(ares);
     479             : 
     480        2196 :                 if (ac->req->operation == LDB_ADD) {
     481             :                         /* Start the modifies to the backlinks */
     482         161 :                         ret = la_queue_mod_request(ac);
     483             : 
     484         161 :                         if (ret != LDB_SUCCESS) {
     485           0 :                                 return ldb_module_done(ac->req, NULL, NULL,
     486             :                                                        ret);
     487             :                         }
     488             :                 } else {
     489             :                         /* Start with the original request */
     490        2035 :                         ret = la_down_req(ac);
     491        2035 :                         if (ret != LDB_SUCCESS) {
     492           0 :                                 return ldb_module_done(ac->req, NULL, NULL, ret);
     493             :                         }
     494             :                 }
     495        2196 :                 return LDB_SUCCESS;
     496             :         }
     497             : 
     498        2196 :         talloc_free(ares);
     499        2196 :         return ret;
     500             : }
     501             : 
     502             : 
     503             : /* modify */
     504     1264260 : static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
     505             : {
     506             :         /* Look over list of modifications */
     507             :         /* Find if any are for linked attributes */
     508             :         /* Determine the effect of the modification */
     509             :         /* Apply the modify to the linked entry */
     510             : 
     511       30499 :         struct ldb_control *control;
     512       30499 :         struct ldb_context *ldb;
     513       30499 :         unsigned int i, j;
     514       30499 :         struct la_context *ac;
     515       30499 :         struct ldb_request *search_req;
     516       30499 :         const char **attrs;
     517       30499 :         struct ldb_control *ctrl;
     518       30499 :         int ret;
     519             : 
     520     1264260 :         ldb = ldb_module_get_ctx(module);
     521             : 
     522     1264260 :         if (ldb_dn_is_special(req->op.mod.message->dn)) {
     523             :                 /* do not manipulate our control entries */
     524      226736 :                 return ldb_next_request(module, req);
     525             :         }
     526             : 
     527     1037524 :         ac = linked_attributes_init(module, req);
     528     1037524 :         if (!ac) {
     529           0 :                 return ldb_operr(ldb);
     530             :         }
     531             : 
     532     1037524 :         control = ldb_request_get_control(req, LDB_CONTROL_VERIFY_NAME_OID);
     533     1037524 :         if (control != NULL && control->data != NULL) {
     534           0 :                 ret = handle_verify_name_control(req, ldb, control, ac);
     535           0 :                 if (ret != LDB_SUCCESS) {
     536           0 :                         return ldb_operr(ldb);
     537             :                 }
     538             :         }
     539             : 
     540     1037524 :         if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
     541             :                 /* don't do anything special for linked attributes, repl_meta_data has done it */
     542      869035 :                 talloc_free(ac);
     543      869035 :                 return ldb_next_request(module, req);
     544             :         }
     545      168489 :         ctrl->critical = false;
     546             : 
     547      168489 :         if (!ac->schema) {
     548             :                 /* without schema, this doesn't make any sense */
     549           0 :                 return ldb_next_request(module, req);
     550             :         }
     551             : 
     552      168489 :         ac->rc = talloc_zero(ac, struct replace_context);
     553      168489 :         if (!ac->rc) {
     554           0 :                 return ldb_oom(ldb);
     555             :         }
     556             : 
     557     1409545 :         for (i=0; i < req->op.mod.message->num_elements; i++) {
     558     1241056 :                 bool store_el = false;
     559           0 :                 const char *attr_name;
     560           0 :                 const struct dsdb_attribute *target_attr;
     561     1241056 :                 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
     562           0 :                 const struct dsdb_attribute *schema_attr
     563     1241056 :                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
     564     1241056 :                 if (!schema_attr) {
     565           0 :                         ldb_asprintf_errstring(ldb,
     566             :                                                "%s: attribute %s is not a valid attribute in schema",
     567             :                                                __FUNCTION__,
     568           0 :                                                el->name);
     569           0 :                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
     570             :                 }
     571             :                 /* We have a valid attribute, now find out if it is a forward link
     572             :                    (Even link IDs are for the originating attribute) */
     573     1241056 :                 if (schema_attr->linkID == 0) {
     574     1238097 :                         continue;
     575             :                 }
     576             : 
     577             :                 /* this part of the code should only be handling forward links */
     578        2959 :                 SMB_ASSERT((schema_attr->linkID & 1) == 0);
     579             : 
     580             :                 /* Now find the target attribute */
     581        2959 :                 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
     582        2959 :                 if (!target_attr) {
     583             :                         /*
     584             :                          * windows 2003 has a broken schema where
     585             :                          * the definition of msDS-IsDomainFor
     586             :                          * is missing (which is supposed to be
     587             :                          * the backlink of the msDS-HasDomainNCs
     588             :                          * attribute
     589             :                          */
     590          24 :                         continue;
     591             :                 }
     592             : 
     593        2935 :                 attr_name = target_attr->lDAPDisplayName;
     594             : 
     595        2935 :                 switch (el->flags & LDB_FLAG_MOD_MASK) {
     596          14 :                 case LDB_FLAG_MOD_REPLACE:
     597             :                         /* treat as just a normal add the delete part is handled by the callback */
     598          14 :                         store_el = true;
     599             : 
     600           0 :                         FALL_THROUGH;
     601        1438 :                 case LDB_FLAG_MOD_ADD:
     602             : 
     603             :                         /* For each value being added, we need to setup the adds */
     604        2876 :                         for (j = 0; j < el->num_values; j++) {
     605        1438 :                                 ret = la_store_op(ac, LA_OP_ADD,
     606             :                                                   schema_attr,
     607        1438 :                                                   &el->values[j],
     608             :                                                   attr_name);
     609        1438 :                                 if (ret != LDB_SUCCESS) {
     610           0 :                                         return ret;
     611             :                                 }
     612             :                         }
     613        1438 :                         break;
     614             : 
     615        1497 :                 case LDB_FLAG_MOD_DELETE:
     616             : 
     617        1497 :                         if (el->num_values) {
     618             :                                 /* For each value being deleted, we need to setup the delete */
     619        2866 :                                 for (j = 0; j < el->num_values; j++) {
     620        1433 :                                         ret = la_store_op(ac, LA_OP_DEL,
     621             :                                                           schema_attr,
     622        1433 :                                                           &el->values[j],
     623             :                                                           attr_name);
     624        1433 :                                         if (ret != LDB_SUCCESS) {
     625           0 :                                                 return ret;
     626             :                                         }
     627             :                                 }
     628             :                         } else {
     629             :                                 /* Flag that there was a DELETE
     630             :                                  * without a value specified, so we
     631             :                                  * need to look for the old value */
     632          64 :                                 store_el = true;
     633             :                         }
     634             : 
     635        1497 :                         break;
     636             :                 }
     637             : 
     638        2935 :                 if (store_el) {
     639           0 :                         struct ldb_message_element *search_el;
     640             : 
     641          78 :                         search_el = talloc_realloc(ac->rc, ac->rc->el,
     642             :                                                    struct ldb_message_element,
     643             :                                                    ac->rc->num_elements +1);
     644          78 :                         if (!search_el) {
     645           0 :                                 return ldb_oom(ldb);
     646             :                         }
     647          78 :                         ac->rc->el = search_el;
     648             : 
     649          78 :                         ac->rc->el[ac->rc->num_elements] = *el;
     650          78 :                         ac->rc->num_elements++;
     651             :                 }
     652             :         }
     653             : 
     654      168489 :         if (ac->ops || ac->rc->el) {
     655             :                 /* both replace and delete without values are handled in the callback
     656             :                  * after the search on the entry to be modified is performed */
     657             : 
     658        2035 :                 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
     659        2035 :                 if (!attrs) {
     660           0 :                         return ldb_oom(ldb);
     661             :                 }
     662        2113 :                 for (i = 0; i < ac->rc->num_elements; i++) {
     663          78 :                         attrs[i] = ac->rc->el[i].name;
     664             :                 }
     665        2035 :                 attrs[i] = NULL;
     666             : 
     667             :                 /* The callback does all the hard work here */
     668        2035 :                 ret = ldb_build_search_req(&search_req, ldb, ac,
     669        2035 :                                            req->op.mod.message->dn,
     670             :                                            LDB_SCOPE_BASE,
     671             :                                            "(objectClass=*)", attrs,
     672             :                                            NULL,
     673             :                                            ac, la_mod_search_callback,
     674             :                                            req);
     675        2035 :                 LDB_REQ_SET_LOCATION(search_req);
     676             : 
     677             :                 /* We need to figure out our own extended DN, to fill in as the backlink target */
     678        2035 :                 if (ret == LDB_SUCCESS) {
     679        2035 :                         ret = dsdb_request_add_controls(search_req,
     680             :                                                         DSDB_SEARCH_SHOW_RECYCLED |
     681             :                                                         DSDB_SEARCH_SHOW_EXTENDED_DN);
     682             :                 }
     683        2035 :                 if (ret == LDB_SUCCESS) {
     684        2035 :                         talloc_steal(search_req, attrs);
     685             : 
     686        2035 :                         ret = ldb_next_request(module, search_req);
     687             :                 }
     688             : 
     689             :         } else {
     690             :                 /* nothing to do for this module, proceed */
     691      166454 :                 talloc_free(ac);
     692      166454 :                 ret = ldb_next_request(module, req);
     693             :         }
     694             : 
     695      168489 :         return ret;
     696             : }
     697             : 
     698             : 
     699        6625 : static int linked_attributes_fix_link_slow(struct ldb_module *module,
     700             :                                            struct ldb_request *parent,
     701             :                                            struct ldb_message *msg,
     702             :                                            struct ldb_dn *new_dn,
     703             :                                            struct GUID self_guid,
     704             :                                            const char *syntax_oid,
     705             :                                            const char *reverse_syntax_oid)
     706             : {
     707          52 :         int ret;
     708          52 :         unsigned int i;
     709          52 :         struct GUID link_guid;
     710        6625 :         struct ldb_message_element *el = &msg->elements[0];
     711        6625 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
     712        6625 :         bool has_unique_value = strcmp(reverse_syntax_oid, LDB_SYNTAX_DN) == 0;
     713        6625 :         TALLOC_CTX *tmp_ctx = talloc_new(module);
     714        6625 :         if (tmp_ctx == NULL) {
     715           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     716             :         }
     717             :         /*
     718             :          * The msg has one element (el) containing links of one particular
     719             :          * type from the remote object. We know that at least one of those
     720             :          * links points to the object being renamed (identified by self_guid,
     721             :          * renamed to new_dn). Usually only one of the links will point back
     722             :          * to renamed object, but there can be more when the reverse link is a
     723             :          * DN+Binary link.
     724             :          *
     725             :          * This is used for unsorted links, which is to say back links and
     726             :          * forward links on old databases. It necessarily involves a linear
     727             :          * search, though when the link is a plain DN link, we can skip
     728             :          * checking as soon as we find it.
     729             :          *
     730             :          * NOTE: if there are duplicate links, the extra ones will end up as
     731             :          * dangling links to the old DN. This may or may not be worse than
     732             :          * leaving them as duplicate links.
     733             :          */
     734       16264 :         for (i = 0; i < el->num_values; i++) {
     735       15875 :                 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(msg,
     736             :                                                         ldb,
     737       15804 :                                                         &el->values[i],
     738             :                                                         syntax_oid);
     739       15804 :                 if (dsdb_dn == NULL) {
     740           0 :                         talloc_free(tmp_ctx);
     741           0 :                         return LDB_ERR_INVALID_DN_SYNTAX;
     742             :                 }
     743             : 
     744       15804 :                 ret = la_guid_from_dn(module, parent, dsdb_dn->dn, &link_guid);
     745       15804 :                 if (ret != LDB_SUCCESS) {
     746           0 :                         talloc_free(tmp_ctx);
     747           0 :                         return ret;
     748             :                 }
     749             : 
     750             :                 /*
     751             :                  * By comparing using the GUID we ensure that even if somehow
     752             :                  * the name has got out of sync, this rename will fix it.
     753             :                  *
     754             :                  * If somehow we don't have a GUID on the DN in the DB, the
     755             :                  * la_guid_from_dn call will be more costly, but still give us
     756             :                  * a GUID. dbcheck will fix this if run.
     757             :                  */
     758       15804 :                 if (!GUID_equal(&self_guid, &link_guid)) {
     759        8758 :                         continue;
     760             :                 }
     761             : 
     762        7046 :                 ret = ldb_dn_update_components(dsdb_dn->dn, new_dn);
     763        7046 :                 if (ret != LDB_SUCCESS) {
     764           0 :                         talloc_free(tmp_ctx);
     765           0 :                         return ret;
     766             :                 }
     767             : 
     768        7046 :                 el->values[i] = data_blob_string_const(
     769        7046 :                         dsdb_dn_get_extended_linearized(el->values, dsdb_dn, 1));
     770        7046 :                 if (has_unique_value) {
     771        6114 :                         break;
     772             :                 }
     773             :         }
     774             : 
     775        6625 :         talloc_free(tmp_ctx);
     776        6625 :         return LDB_SUCCESS;
     777             : }
     778             : 
     779             : 
     780        2735 : static int linked_attributes_fix_forward_link(struct ldb_module *module,
     781             :                                               struct ldb_message *msg,
     782             :                                               struct ldb_dn *new_dn,
     783             :                                               struct GUID self_guid,
     784             :                                               const char *syntax_oid)
     785             : {
     786           2 :         int ret;
     787        2735 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
     788        2735 :         struct parsed_dn *pdn_list = NULL;
     789        2735 :         struct parsed_dn *exact = NULL;
     790        2735 :         struct parsed_dn *next = NULL;
     791           2 :         bool is_plain_dn;
     792        2735 :         struct ldb_message_element *el = &msg->elements[0];
     793        2735 :         unsigned int num_parsed_dns = el->num_values;
     794             : 
     795        2735 :         TALLOC_CTX *tmp_ctx = talloc_new(module);
     796        2735 :         if (tmp_ctx == NULL) {
     797           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     798             :         }
     799             : 
     800             :         /*
     801             :          * The msg has a single element (el) containing forward links which we
     802             :          * trust are sorted in GUID order. We know that at least one of those
     803             :          * links points to the object being renamed (identified by self_guid,
     804             :          * renamed to new_dn), because that object has a backlink pointing
     805             :          * here.
     806             :          *
     807             :          * In most cases we assume there will only be one forward link, which
     808             :          * is found by parsed_dn_find(), but in the case of DN+Binary links
     809             :          * (e.g. msDS-RevealedUsers) there may be many forward links that
     810             :          * share the same DN/GUID but differ in the binary part. For those we
     811             :          * need to look around the link found by parsed_dn_find() and convert
     812             :          * them all -- there is no way to know which forward link belongs to
     813             :          * which backlink.
     814             :          */
     815             : 
     816        2735 :         ret = get_parsed_dns_trusted(tmp_ctx, el, &pdn_list);
     817        2735 :         if (ret != LDB_SUCCESS) {
     818           0 :                 ldb_asprintf_errstring(ldb, "get_parsed_dn_trusted() "
     819             :                                        "error fixing %s links for %s",
     820             :                                        el->name,
     821             :                                        ldb_dn_get_linearized(msg->dn));
     822           0 :                 talloc_free(tmp_ctx);
     823           0 :                 return ret;
     824             :         }
     825             : 
     826             :         /* find our DN in the values */
     827        2735 :         ret = parsed_dn_find(ldb, pdn_list, num_parsed_dns,
     828             :                              &self_guid,
     829             :                              NULL,
     830             :                              data_blob_null, 0,
     831             :                              &exact, &next,
     832             :                              syntax_oid,
     833             :                              false);
     834             : 
     835        2735 :         if (ret != LDB_SUCCESS) {
     836           0 :                 ldb_asprintf_errstring(ldb, "parsed_dn_find() "
     837             :                                        "error fixing %s links for %s",
     838             :                                        el->name,
     839             :                                        ldb_dn_get_linearized(msg->dn));
     840           0 :                 talloc_free(tmp_ctx);
     841           0 :                 return ret;
     842             :         }
     843             : 
     844        2735 :         if (exact == NULL) {
     845             :                 /*
     846             :                  * Our only caller doesn’t want to know about errors finding a
     847             :                  * forward link for which we have a backlink — in particular,
     848             :                  * during the tombstoning of an object, the forward links have
     849             :                  * already been removed when this routine is called by
     850             :                  * dsdb_module_rename() inside replmd_delete_internals().
     851             :                  */
     852        1376 :                 talloc_free(tmp_ctx);
     853        1376 :                 return LDB_SUCCESS;
     854             :         }
     855             : 
     856        1359 :         is_plain_dn = strcmp(syntax_oid, LDB_SYNTAX_DN) == 0;
     857             : 
     858        1359 :         if (is_plain_dn) {
     859             :                 /*
     860             :                  *  The common case -- we only have to update a single link
     861             :                  */
     862         747 :                 ret = ldb_dn_update_components(exact->dsdb_dn->dn, new_dn);
     863         747 :                 if (ret != LDB_SUCCESS) {
     864           0 :                         DBG_ERR("could not update components  %s  %s\n",
     865             :                                 ldb_dn_get_linearized(exact->dsdb_dn->dn),
     866             :                                 ldb_dn_get_linearized(new_dn)
     867             :                                 );
     868             : 
     869           0 :                         talloc_free(tmp_ctx);
     870           0 :                         return ret;
     871             :                 }
     872         747 :                 *(exact->v) = data_blob_string_const(
     873         747 :                                 dsdb_dn_get_extended_linearized(el->values,
     874         747 :                                                                 exact->dsdb_dn,
     875             :                                                                 1));
     876             :         } else {
     877             :                 /*
     878             :                  * The forward link is a DN+Binary (or in some alternate
     879             :                  * universes, DN+String), which means the parsed_dns are keyed
     880             :                  * on GUID+Binary. We don't know the binary part, which means
     881             :                  * from our point of view the list can have entries with
     882             :                  * duplicate GUIDs that we can't tell apart. We don't know
     883             :                  * which backlink belongs to which GUID+binary, and the binary
     884             :                  * search will always find the same one. That means one link
     885             :                  * link will get fixed n times, whil n-1 links get fixed
     886             :                  * never.
     887             :                  *
     888             :                  * If we instead fixing all the possible links, we end up
     889             :                  * fixing n links n times, which at least works and is
     890             :                  * probably not too costly because n is probably small.
     891             :                  */
     892         612 :                 struct parsed_dn *first = exact;
     893         612 :                 struct parsed_dn *last = exact;
     894         612 :                 struct parsed_dn *p = NULL;
     895             :                 int cmp;
     896         612 :                 while (first > pdn_list) {
     897         565 :                         p = first - 1;
     898         565 :                         if (p->dsdb_dn == NULL) {
     899           0 :                                 ret = really_parse_trusted_dn(tmp_ctx,
     900             :                                                               ldb, p,
     901             :                                                               syntax_oid);
     902           0 :                                 if (ret != LDB_SUCCESS) {
     903           0 :                                         talloc_free(tmp_ctx);
     904           0 :                                         return ret;
     905             :                                 }
     906             :                         }
     907         565 :                         cmp = ndr_guid_compare(&exact->guid, &p->guid);
     908         565 :                         if (cmp != 0) {
     909         565 :                                 break;
     910             :                         }
     911           0 :                         first = p;
     912             :                 }
     913             : 
     914        2368 :                 while (last < pdn_list + num_parsed_dns - 1) {
     915        2298 :                         p = last + 1;
     916        2298 :                         if (p->dsdb_dn == NULL) {
     917        1711 :                                 ret = really_parse_trusted_dn(tmp_ctx,
     918             :                                                               ldb, p,
     919             :                                                               syntax_oid);
     920        1711 :                                 if (ret != LDB_SUCCESS) {
     921           0 :                                         talloc_free(tmp_ctx);
     922           0 :                                         return ret;
     923             :                                 }
     924             :                         }
     925        2298 :                         cmp = ndr_guid_compare(&exact->guid, &p->guid);
     926        2298 :                         if (cmp != 0) {
     927         542 :                                 break;
     928             :                         }
     929        1756 :                         last = p;
     930             :                 }
     931             : 
     932        2980 :                 for (p = first; p <= last; p++) {
     933        2368 :                         ret = ldb_dn_update_components(p->dsdb_dn->dn, new_dn);
     934        2368 :                         if (ret != LDB_SUCCESS) {
     935           0 :                                 DBG_ERR("could not update components  %s  %s\n",
     936             :                                         ldb_dn_get_linearized(p->dsdb_dn->dn),
     937             :                                         ldb_dn_get_linearized(new_dn)
     938             :                                         );
     939           0 :                                 talloc_free(tmp_ctx);
     940           0 :                                 return ret;
     941             :                         }
     942        2368 :                         *(p->v) = data_blob_string_const(
     943        2368 :                                    dsdb_dn_get_extended_linearized(el->values,
     944             :                                                                    p->dsdb_dn,
     945             :                                                                    1));
     946             :                 }
     947             :         }
     948             : 
     949        1359 :         talloc_free(tmp_ctx);
     950        1359 :         return LDB_SUCCESS;
     951             : }
     952             : 
     953             : 
     954        6105 : static int linked_attributes_fix_links(struct ldb_module *module,
     955             :                                        struct GUID self_guid,
     956             :                                        struct ldb_dn *old_dn,
     957             :                                        struct ldb_dn *new_dn,
     958             :                                        struct ldb_message_element *el,
     959             :                                        struct dsdb_schema *schema,
     960             :                                        const struct dsdb_attribute *schema_attr,
     961             :                                        struct ldb_request *parent)
     962             : {
     963          27 :         unsigned int i;
     964        6105 :         TALLOC_CTX *tmp_ctx = NULL;
     965        6105 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
     966        6105 :         const struct dsdb_attribute *target = NULL;
     967          27 :         const char *attrs[2];
     968          27 :         int ret;
     969        6105 :         struct la_private *la_private = NULL;
     970             : 
     971        6105 :         target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
     972        6105 :         if (target == NULL) {
     973             :                 /* there is no counterpart link to change */
     974         495 :                 return LDB_SUCCESS;
     975             :         }
     976             : 
     977        5607 :         tmp_ctx = talloc_new(module);
     978        5607 :         if (tmp_ctx == NULL) {
     979           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     980             :         }
     981             : 
     982        5607 :         la_private = talloc_get_type(ldb_module_get_private(module),
     983             :                                      struct la_private);
     984        5607 :         if (la_private == NULL) {
     985           0 :                 talloc_free(tmp_ctx);
     986           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     987             :         }
     988             : 
     989        5607 :         attrs[0] = target->lDAPDisplayName;
     990        5607 :         attrs[1] = NULL;
     991             : 
     992       15025 :         for (i=0; i<el->num_values; i++) {
     993        9418 :                 struct dsdb_dn *dsdb_dn = NULL;
     994        9418 :                 struct ldb_result *res = NULL;
     995        9418 :                 struct ldb_message *msg  = NULL;
     996        9418 :                 struct ldb_message_element *el2 = NULL;
     997          54 :                 struct GUID link_guid;
     998        9418 :                 char *link_guid_str = NULL;
     999             : 
    1000        9472 :                 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i],
    1001        9418 :                                         schema_attr->syntax->ldap_oid);
    1002        9418 :                 if (dsdb_dn == NULL) {
    1003           0 :                         talloc_free(tmp_ctx);
    1004           0 :                         return LDB_ERR_INVALID_DN_SYNTAX;
    1005             :                 }
    1006             : 
    1007        9418 :                 ret = la_guid_from_dn(module, parent, dsdb_dn->dn, &link_guid);
    1008        9418 :                 if (ret != LDB_SUCCESS) {
    1009           0 :                         ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - GUID not found - %s",
    1010           0 :                                                el->name, target->lDAPDisplayName,
    1011             :                                                ldb_dn_get_linearized(old_dn),
    1012             :                                                ldb_dn_get_linearized(dsdb_dn->dn),
    1013             :                                                ldb_errstring(ldb));
    1014           0 :                         talloc_free(tmp_ctx);
    1015           0 :                         return ret;
    1016             :                 }
    1017             : 
    1018        9418 :                 link_guid_str = GUID_string(tmp_ctx, &link_guid);
    1019        9418 :                 if (link_guid_str == NULL) {
    1020           0 :                         talloc_free(tmp_ctx);
    1021           0 :                         return LDB_ERR_OPERATIONS_ERROR;
    1022             :                 }
    1023             : 
    1024             :                 /*
    1025             :                  * get the existing message from the db for the object with
    1026             :                  * this GUID, returning attribute being modified. We will then
    1027             :                  * use this msg as the basis for a modify call
    1028             :                  */
    1029             : 
    1030        9418 :                 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
    1031             :                                          DSDB_FLAG_NEXT_MODULE |
    1032             :                                          DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
    1033             :                                          DSDB_SEARCH_SHOW_RECYCLED |
    1034             :                                          DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
    1035             :                                          DSDB_SEARCH_REVEAL_INTERNALS,
    1036             :                                          parent,
    1037             :                                          "objectGUID=%s", link_guid_str);
    1038        9418 :                 if (ret != LDB_SUCCESS) {
    1039           0 :                         ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - target GUID %s not found - %s",
    1040           0 :                                                el->name, target->lDAPDisplayName,
    1041             :                                                ldb_dn_get_linearized(old_dn),
    1042             :                                                ldb_dn_get_linearized(dsdb_dn->dn),
    1043             :                                                link_guid_str,
    1044             :                                                ldb_errstring(ldb));
    1045           0 :                         talloc_free(tmp_ctx);
    1046           0 :                         return ret;
    1047             :                 }
    1048        9418 :                 if (res->count == 0) {
    1049             :                         /* Forward link without backlink object remaining - nothing to do here */
    1050          58 :                         continue;
    1051             :                 }
    1052        9416 :                 if (res->count != 1) {
    1053           0 :                         ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - target GUID %s found more than once!",
    1054           0 :                                                el->name, target->lDAPDisplayName,
    1055             :                                                ldb_dn_get_linearized(old_dn),
    1056             :                                                ldb_dn_get_linearized(dsdb_dn->dn),
    1057             :                                                link_guid_str);
    1058           0 :                         talloc_free(tmp_ctx);
    1059           0 :                         return LDB_ERR_OPERATIONS_ERROR;
    1060             :                 }
    1061             : 
    1062        9416 :                 msg = res->msgs[0];
    1063             : 
    1064        9416 :                 if (msg->num_elements == 0) {
    1065             :                         /* Forward link without backlink remaining - nothing to do here */
    1066          56 :                         continue;
    1067        9360 :                 } else if (msg->num_elements != 1) {
    1068           0 :                         ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
    1069             :                                                msg->num_elements, ldb_dn_get_linearized(msg->dn));
    1070           0 :                         talloc_free(tmp_ctx);
    1071           0 :                         return LDB_ERR_OPERATIONS_ERROR;
    1072             :                 }
    1073        9360 :                 if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
    1074           0 :                         ldb_asprintf_errstring(ldb, "Bad returned attribute in linked_attributes_fix_links: got %s, expected %s for %s", msg->elements[0].name, target->lDAPDisplayName, ldb_dn_get_linearized(msg->dn));
    1075           0 :                         talloc_free(tmp_ctx);
    1076           0 :                         return LDB_ERR_OPERATIONS_ERROR;
    1077             :                 }
    1078        9360 :                 el2 = &msg->elements[0];
    1079             : 
    1080        9360 :                 el2->flags = LDB_FLAG_MOD_REPLACE;
    1081             : 
    1082        9360 :                 if (target->linkID & 1 ||
    1083        2740 :                         ! la_private->sorted_links) {
    1084             :                         /* handle backlinks (which aren't sorted in the DB)
    1085             :                            and forward links in old unsorted databases. */
    1086        6625 :                         ret = linked_attributes_fix_link_slow(
    1087             :                                 module,
    1088             :                                 parent,
    1089             :                                 msg,
    1090             :                                 new_dn,
    1091             :                                 self_guid,
    1092        6625 :                                 target->syntax->ldap_oid,
    1093        6625 :                                 schema_attr->syntax->ldap_oid);
    1094             :                 } else {
    1095             :                         /* we can binary search to find forward links */
    1096        2735 :                         ret = linked_attributes_fix_forward_link(
    1097             :                                 module,
    1098             :                                 msg,
    1099             :                                 new_dn,
    1100             :                                 self_guid,
    1101        2735 :                                 target->syntax->ldap_oid);
    1102             :                 }
    1103        9360 :                 if (ret != LDB_SUCCESS) {
    1104           0 :                         talloc_free(tmp_ctx);
    1105           0 :                         return ret;
    1106             :                 }
    1107        9360 :                 ret = dsdb_check_single_valued_link(target, el2);
    1108        9360 :                 if (ret != LDB_SUCCESS) {
    1109           0 :                         talloc_free(tmp_ctx);
    1110           0 :                         return ret;
    1111             :                 }
    1112             : 
    1113             :                 /* we may be putting multiple values in an attribute -
    1114             :                    disable checking for this attribute */
    1115        9360 :                 el2->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
    1116             : 
    1117        9360 :                 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
    1118        9360 :                 if (ret != LDB_SUCCESS) {
    1119           0 :                         ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
    1120           0 :                                                el->name, target->lDAPDisplayName,
    1121             :                                                ldb_dn_get_linearized(old_dn),
    1122             :                                                ldb_dn_get_linearized(dsdb_dn->dn),
    1123             :                                                ldb_errstring(ldb));
    1124           0 :                         talloc_free(tmp_ctx);
    1125           0 :                         return ret;
    1126             :                 }
    1127             :         }
    1128             : 
    1129        5607 :         talloc_free(tmp_ctx);
    1130        5607 :         return LDB_SUCCESS;
    1131             : }
    1132             : 
    1133             : 
    1134             : /* rename */
    1135       81103 : static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
    1136             : {
    1137         151 :         struct ldb_result *res;
    1138         151 :         struct ldb_message *msg;
    1139         151 :         unsigned int i;
    1140       81103 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
    1141         151 :         struct dsdb_schema *schema;
    1142         151 :         int ret;
    1143         151 :         struct GUID guid;
    1144             : 
    1145             :         /*
    1146             :            - load the current msg
    1147             :            - find any linked attributes
    1148             :            - if its a link then find the target object
    1149             :            - modify the target linked attributes with the new DN
    1150             :         */
    1151       81103 :         ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
    1152             :                                     NULL,
    1153             :                                     DSDB_FLAG_NEXT_MODULE |
    1154             :                                     DSDB_SEARCH_SHOW_EXTENDED_DN |
    1155             :                                     DSDB_SEARCH_SHOW_RECYCLED, req);
    1156       81103 :         if (ret != LDB_SUCCESS) {
    1157           0 :                 return ret;
    1158             :         }
    1159             : 
    1160       81103 :         schema = dsdb_get_schema(ldb, res);
    1161       81103 :         if (!schema) {
    1162           0 :                 return ldb_oom(ldb);
    1163             :         }
    1164             : 
    1165       81103 :         msg = res->msgs[0];
    1166             : 
    1167       81103 :         ret = la_guid_from_dn(module, req, msg->dn, &guid);
    1168       81103 :         if (ret != LDB_SUCCESS) {
    1169           0 :                 return ret;
    1170             :         }
    1171             : 
    1172     1739910 :         for (i=0; i<msg->num_elements; i++) {
    1173     1658807 :                 struct ldb_message_element *el = &msg->elements[i];
    1174        4315 :                 const struct dsdb_attribute *schema_attr
    1175     1658807 :                         = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
    1176     1658807 :                 if (!schema_attr || schema_attr->linkID == 0) {
    1177     1652702 :                         continue;
    1178             :                 }
    1179        6105 :                 ret = linked_attributes_fix_links(module, guid, msg->dn, req->op.rename.newdn, el,
    1180             :                                                   schema, schema_attr, req);
    1181        6105 :                 if (ret != LDB_SUCCESS) {
    1182           0 :                         talloc_free(res);
    1183           0 :                         return ret;
    1184             :                 }
    1185             :         }
    1186             : 
    1187       81103 :         talloc_free(res);
    1188             : 
    1189       81103 :         return ldb_next_request(module, req);
    1190             : }
    1191             : 
    1192             : 
    1193             : /* queue a linked attributes modify request in the la_private
    1194             :    structure */
    1195        2194 : static int la_queue_mod_request(struct la_context *ac)
    1196             : {
    1197           0 :         struct la_private *la_private =
    1198        2194 :                 talloc_get_type(ldb_module_get_private(ac->module),
    1199             :                                 struct la_private);
    1200             : 
    1201        2194 :         if (la_private == NULL || la_private->transaction == NULL) {
    1202           0 :                 ldb_debug(ldb_module_get_ctx(ac->module),
    1203             :                           LDB_DEBUG_ERROR,
    1204             :                           __location__ ": No la_private transaction setup\n");
    1205           0 :                 return ldb_operr(ldb_module_get_ctx(ac->module));
    1206             :         }
    1207             : 
    1208        2194 :         talloc_steal(la_private->transaction, ac);
    1209        2194 :         DLIST_ADD(la_private->transaction->la_list, ac);
    1210             : 
    1211        2194 :         return ldb_module_done(ac->req, ac->op_controls,
    1212             :                                ac->op_response, LDB_SUCCESS);
    1213             : }
    1214             : 
    1215             : /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
    1216        2035 : static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
    1217             : {
    1218           0 :         struct la_context *ac;
    1219           0 :         struct ldb_context *ldb;
    1220           0 :         int ret;
    1221             : 
    1222        2035 :         ac = talloc_get_type(req->context, struct la_context);
    1223        2035 :         ldb = ldb_module_get_ctx(ac->module);
    1224             : 
    1225        2035 :         if (!ares) {
    1226           0 :                 return ldb_module_done(ac->req, NULL, NULL,
    1227             :                                         LDB_ERR_OPERATIONS_ERROR);
    1228             :         }
    1229        2035 :         if (ares->error != LDB_SUCCESS) {
    1230           2 :                 return ldb_module_done(ac->req, ares->controls,
    1231             :                                         ares->response, ares->error);
    1232             :         }
    1233             : 
    1234        2033 :         if (ares->type != LDB_REPLY_DONE) {
    1235           0 :                 ldb_set_errstring(ldb,
    1236             :                      "invalid reply type in linked attributes delete callback");
    1237           0 :                 talloc_free(ares);
    1238           0 :                 return ldb_module_done(ac->req, NULL, NULL,
    1239             :                                         LDB_ERR_OPERATIONS_ERROR);
    1240             :         }
    1241             : 
    1242        2033 :         ac->op_controls = talloc_steal(ac, ares->controls);
    1243        2033 :         ac->op_response = talloc_steal(ac, ares->response);
    1244             : 
    1245             :         /* If we have modifies to make, this is the time to do them for modify and delete */
    1246        2033 :         ret = la_queue_mod_request(ac);
    1247             : 
    1248        2033 :         if (ret != LDB_SUCCESS) {
    1249           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
    1250             :         }
    1251        2033 :         talloc_free(ares);
    1252             : 
    1253             :         /* la_queue_mod_request has already sent the callbacks */
    1254        2033 :         return LDB_SUCCESS;
    1255             : 
    1256             : }
    1257             : 
    1258             : /* Having done the original add, then try to fix up all the linked attributes
    1259             : 
    1260             :   This is done after the add so the links can get the extended DNs correctly.
    1261             :  */
    1262         161 : static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
    1263             : {
    1264           0 :         struct la_context *ac;
    1265           0 :         struct ldb_context *ldb;
    1266           0 :         int ret;
    1267             : 
    1268         161 :         ac = talloc_get_type(req->context, struct la_context);
    1269         161 :         ldb = ldb_module_get_ctx(ac->module);
    1270             : 
    1271         161 :         if (!ares) {
    1272           0 :                 return ldb_module_done(ac->req, NULL, NULL,
    1273             :                                         LDB_ERR_OPERATIONS_ERROR);
    1274             :         }
    1275         161 :         if (ares->error != LDB_SUCCESS) {
    1276           0 :                 return ldb_module_done(ac->req, ares->controls,
    1277             :                                         ares->response, ares->error);
    1278             :         }
    1279             : 
    1280         161 :         if (ares->type != LDB_REPLY_DONE) {
    1281           0 :                 ldb_set_errstring(ldb,
    1282             :                         "invalid reply type in linked attributes add callback");
    1283           0 :                 talloc_free(ares);
    1284           0 :                 return ldb_module_done(ac->req, NULL, NULL,
    1285             :                                         LDB_ERR_OPERATIONS_ERROR);
    1286             :         }
    1287             : 
    1288         161 :         if (ac->ops) {
    1289           0 :                 struct ldb_request *search_req;
    1290           0 :                 static const char *attrs[] = { NULL };
    1291             : 
    1292             :                 /* The callback does all the hard work here - we need
    1293             :                  * the objectGUID and SID of the added record */
    1294         161 :                 ret = ldb_build_search_req(&search_req, ldb, ac,
    1295         161 :                                            ac->req->op.add.message->dn,
    1296             :                                            LDB_SCOPE_BASE,
    1297             :                                            "(objectClass=*)", attrs,
    1298             :                                            NULL,
    1299             :                                            ac, la_mod_search_callback,
    1300             :                                            ac->req);
    1301         161 :                 LDB_REQ_SET_LOCATION(search_req);
    1302             : 
    1303         161 :                 if (ret == LDB_SUCCESS) {
    1304         161 :                         ret = dsdb_request_add_controls(search_req,
    1305             :                                                         DSDB_SEARCH_SHOW_RECYCLED |
    1306             :                                                         DSDB_SEARCH_SHOW_EXTENDED_DN);
    1307             :                 }
    1308         161 :                 if (ret != LDB_SUCCESS) {
    1309           0 :                         return ldb_module_done(ac->req, NULL, NULL,
    1310             :                                                ret);
    1311             :                 }
    1312             : 
    1313         161 :                 ac->op_controls = talloc_steal(ac, ares->controls);
    1314         161 :                 ac->op_response = talloc_steal(ac, ares->response);
    1315             : 
    1316         161 :                 return ldb_next_request(ac->module, search_req);
    1317             : 
    1318             :         } else {
    1319           0 :                 return ldb_module_done(ac->req, ares->controls,
    1320             :                                        ares->response, ares->error);
    1321             :         }
    1322             : }
    1323             : 
    1324             : /* Reconstruct the original request, but pointing at our local callback to finish things off */
    1325        2196 : static int la_down_req(struct la_context *ac)
    1326             : {
    1327           0 :         struct ldb_request *down_req;
    1328           0 :         struct ldb_context *ldb;
    1329           0 :         int ret;
    1330             : 
    1331        2196 :         ldb = ldb_module_get_ctx(ac->module);
    1332             : 
    1333        2196 :         switch (ac->req->operation) {
    1334         161 :         case LDB_ADD:
    1335         161 :                 ret = ldb_build_add_req(&down_req, ldb, ac,
    1336         161 :                                         ac->req->op.add.message,
    1337         161 :                                         ac->req->controls,
    1338             :                                         ac, la_add_callback,
    1339             :                                         ac->req);
    1340         161 :                 LDB_REQ_SET_LOCATION(down_req);
    1341         161 :                 break;
    1342        2035 :         case LDB_MODIFY:
    1343        2035 :                 ret = ldb_build_mod_req(&down_req, ldb, ac,
    1344        2035 :                                         ac->req->op.mod.message,
    1345        2035 :                                         ac->req->controls,
    1346             :                                         ac, la_mod_del_callback,
    1347             :                                         ac->req);
    1348        2035 :                 LDB_REQ_SET_LOCATION(down_req);
    1349        2035 :                 break;
    1350           0 :         default:
    1351           0 :                 ret = LDB_ERR_OPERATIONS_ERROR;
    1352             :         }
    1353        2196 :         if (ret != LDB_SUCCESS) {
    1354           0 :                 return ret;
    1355             :         }
    1356             : 
    1357        2196 :         return ldb_next_request(ac->module, down_req);
    1358             : }
    1359             : 
    1360             : /*
    1361             :   use the GUID part of an extended DN to find the target DN, in case
    1362             :   it has moved
    1363             :  */
    1364        3340 : static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
    1365             :                              struct GUID *guid, struct ldb_dn **dn)
    1366             : {
    1367        3340 :         return dsdb_module_dn_by_guid(ac->module, ac, guid, dn, ac->req);
    1368             : }
    1369             : 
    1370             : /* apply one la_context op change */
    1371        3340 : static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
    1372             : {
    1373           0 :         struct ldb_message_element *ret_el;
    1374           0 :         struct ldb_message *new_msg;
    1375           0 :         struct ldb_context *ldb;
    1376           0 :         int ret;
    1377             : 
    1378        3340 :         if (ac->mod_dn == NULL) {
    1379             :                 /* we didn't find the DN that we searched for */
    1380           0 :                 return LDB_SUCCESS;
    1381             :         }
    1382             : 
    1383        3340 :         ldb = ldb_module_get_ctx(ac->module);
    1384             : 
    1385             :         /* Create the modify request */
    1386        3340 :         new_msg = ldb_msg_new(ac);
    1387        3340 :         if (!new_msg) {
    1388           0 :                 return ldb_oom(ldb);
    1389             :         }
    1390             : 
    1391        3340 :         ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
    1392        3340 :         if (ret != LDB_SUCCESS) {
    1393          21 :                 return ret;
    1394             :         }
    1395             : 
    1396        3319 :         if (op->op == LA_OP_ADD) {
    1397        1764 :                 ret = ldb_msg_add_empty(new_msg, op->name,
    1398             :                                         LDB_FLAG_MOD_ADD, &ret_el);
    1399             :         } else {
    1400        1555 :                 ret = ldb_msg_add_empty(new_msg, op->name,
    1401             :                                         LDB_FLAG_MOD_DELETE, &ret_el);
    1402             :         }
    1403        3319 :         if (ret != LDB_SUCCESS) {
    1404           0 :                 return ret;
    1405             :         }
    1406        3319 :         ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
    1407        3319 :         if (!ret_el->values) {
    1408           0 :                 return ldb_oom(ldb);
    1409             :         }
    1410        3319 :         ret_el->num_values = 1;
    1411        3319 :         ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->mod_dn, 1));
    1412             : 
    1413             :         /* a backlink should never be single valued. Unfortunately the
    1414             :            exchange schema has a attribute
    1415             :            msExchBridgeheadedLocalConnectorsDNBL which is single
    1416             :            valued and a backlink. We need to cope with that by
    1417             :            ignoring the single value flag */
    1418        3319 :         ret_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
    1419             : 
    1420             : #if 0
    1421             :         ldb_debug(ldb, LDB_DEBUG_WARNING,
    1422             :                   "link on %s %s: %s %s\n",
    1423             :                   ldb_dn_get_linearized(new_msg->dn), ret_el->name,
    1424             :                   ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
    1425             : #endif
    1426             : 
    1427        3319 :         if (DEBUGLVL(4)) {
    1428           0 :                 DEBUG(4,("Applying linked attribute change:\n%s\n",
    1429             :                          ldb_ldif_message_redacted_string(ldb, op,
    1430             :                                                           LDB_CHANGETYPE_MODIFY,
    1431             :                                                           new_msg)));
    1432             :         }
    1433             : 
    1434        3319 :         ret = dsdb_module_modify(module, new_msg, DSDB_FLAG_NEXT_MODULE, ac->req);
    1435        3319 :         if (ret != LDB_SUCCESS) {
    1436           0 :                 ldb_debug(ldb, LDB_DEBUG_WARNING, __location__ ": failed to apply linked attribute change '%s'\n%s\n",
    1437             :                           ldb_errstring(ldb),
    1438             :                           ldb_ldif_message_redacted_string(ldb, op,
    1439             :                                                            LDB_CHANGETYPE_MODIFY,
    1440             :                                                            new_msg));
    1441             :         }
    1442             : 
    1443        3319 :         return ret;
    1444             : }
    1445             : 
    1446             : /* apply one set of la_context changes */
    1447        2194 : static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
    1448             : {
    1449           0 :         struct la_op_store *op;
    1450             : 
    1451        5534 :         for (op = ac->ops; op; op=op->next) {
    1452        3340 :                 int ret = la_do_op_request(module, ac, op);
    1453        3340 :                 if (ret != LDB_SUCCESS) {
    1454          21 :                         if (ret != LDB_ERR_NO_SUCH_OBJECT) {
    1455           0 :                                 return ret;
    1456             :                         }
    1457             :                 }
    1458             :         }
    1459             : 
    1460        2194 :         return LDB_SUCCESS;
    1461             : }
    1462             : 
    1463             : 
    1464             : /*
    1465             :   we hook into the transaction operations to allow us to
    1466             :   perform the linked attribute updates at the end of the whole
    1467             :   transaction. This allows a forward linked attribute to be created
    1468             :   before the target is created, as long as the target is created
    1469             :   in the same transaction
    1470             :  */
    1471      349875 : static int linked_attributes_start_transaction(struct ldb_module *module)
    1472             : {
    1473             :         /* create our private structure for this transaction */
    1474        2157 :         struct la_private *la_private =
    1475      349875 :                 talloc_get_type(ldb_module_get_private(module),
    1476             :                                 struct la_private);
    1477             : 
    1478      349875 :         if (la_private == NULL) {
    1479           0 :                 return ldb_oom(ldb_module_get_ctx(module));
    1480             :         }
    1481      349875 :         talloc_free(la_private->transaction);
    1482      349875 :         la_private->transaction = talloc(module, struct la_private_transaction);
    1483      349875 :         if (la_private->transaction == NULL) {
    1484           0 :                 return ldb_oom(ldb_module_get_ctx(module));
    1485             :         }
    1486      349875 :         la_private->transaction->la_list = NULL;
    1487      349875 :         return ldb_next_start_trans(module);
    1488             : }
    1489             : 
    1490             : /*
    1491             :   on prepare commit we loop over our queued la_context structures
    1492             :   and apply each of them
    1493             :  */
    1494      304062 : static int linked_attributes_prepare_commit(struct ldb_module *module)
    1495             : {
    1496        2152 :         struct la_context *ac;
    1497        2152 :         struct la_private *la_private =
    1498      304062 :                 talloc_get_type(ldb_module_get_private(module),
    1499             :                                 struct la_private);
    1500      304062 :         if (la_private == NULL || la_private->transaction == NULL) {
    1501           0 :                 DBG_ERR("prepare_commit without begin_transaction\n");
    1502             :                 /* prepare commit without begin_transaction - let someone else
    1503             :                  * return the error, just don't segfault */
    1504           0 :                 return ldb_next_prepare_commit(module);
    1505             :         }
    1506             :         /* walk the list backwards, to do the first entry first, as we
    1507             :          * added the entries with DLIST_ADD() which puts them at the
    1508             :          * start of the list */
    1509             : 
    1510             :         /* Start at the end of the list - so we can start
    1511             :          * there, but ensure we don't create a loop by NULLing
    1512             :          * it out in the first element */
    1513      304062 :         ac = DLIST_TAIL(la_private->transaction->la_list);
    1514             : 
    1515      306256 :         for (; ac; ac=DLIST_PREV(ac)) {
    1516           0 :                 int ret;
    1517        2194 :                 ac->req = NULL;
    1518        2194 :                 ret = la_do_mod_request(module, ac);
    1519        2194 :                 if (ret != LDB_SUCCESS) {
    1520           0 :                         DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
    1521           0 :                         TALLOC_FREE(la_private->transaction);
    1522           0 :                         return ret;
    1523             :                 }
    1524             :         }
    1525             : 
    1526      304062 :         TALLOC_FREE(la_private->transaction);
    1527             : 
    1528      304062 :         return ldb_next_prepare_commit(module);
    1529             : }
    1530             : 
    1531       45378 : static int linked_attributes_del_transaction(struct ldb_module *module)
    1532             : {
    1533           4 :         struct la_private *la_private =
    1534       45378 :                 talloc_get_type(ldb_module_get_private(module),
    1535             :                                 struct la_private);
    1536       45378 :         TALLOC_FREE(la_private->transaction);
    1537       45378 :         return ldb_next_del_trans(module);
    1538             : }
    1539             : 
    1540      180740 : static int linked_attributes_ldb_init(struct ldb_module *module)
    1541             : {
    1542        6008 :         int ret;
    1543      180740 :         struct la_private *la_private = NULL;
    1544      180740 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
    1545             : 
    1546      180740 :         ret = ldb_mod_register_control(module, LDB_CONTROL_VERIFY_NAME_OID);
    1547      180740 :         if (ret != LDB_SUCCESS) {
    1548           0 :                 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
    1549             :                         "verify_name: Unable to register control with rootdse!\n");
    1550           0 :                 return ldb_operr(ldb_module_get_ctx(module));
    1551             :         }
    1552             : 
    1553      180740 :         la_private = talloc_zero(module, struct la_private);
    1554      180740 :         if (la_private == NULL) {
    1555           0 :                 ldb_oom(ldb);
    1556           0 :                 return LDB_ERR_OPERATIONS_ERROR;
    1557             :         }
    1558             : 
    1559      180740 :         ret = dsdb_check_samba_compatible_feature(module,
    1560             :                                                   SAMBA_SORTED_LINKS_FEATURE,
    1561             :                                                   &la_private->sorted_links);
    1562      180740 :         if (ret != LDB_SUCCESS) {
    1563           0 :                 talloc_free(la_private);
    1564           0 :                 return ret;
    1565             :         }
    1566             : 
    1567      180740 :         ldb_module_set_private(module, la_private);
    1568      180740 :         return ldb_next_init(module);
    1569             : }
    1570             : 
    1571             : 
    1572             : static const struct ldb_module_ops ldb_linked_attributes_module_ops = {
    1573             :         .name              = "linked_attributes",
    1574             :         .add               = linked_attributes_add,
    1575             :         .modify            = linked_attributes_modify,
    1576             :         .rename            = linked_attributes_rename,
    1577             :         .init_context      = linked_attributes_ldb_init,
    1578             :         .start_transaction = linked_attributes_start_transaction,
    1579             :         .prepare_commit    = linked_attributes_prepare_commit,
    1580             :         .del_transaction   = linked_attributes_del_transaction,
    1581             : };
    1582             : 
    1583        5903 : int ldb_linked_attributes_module_init(const char *version)
    1584             : {
    1585        5903 :         LDB_MODULE_CHECK_VERSION(version);
    1586        5903 :         return ldb_register_module(&ldb_linked_attributes_module_ops);
    1587             : }

Generated by: LCOV version 1.14