LCOV - code coverage report
Current view: top level - ldb_modules - memberof.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 1116 1811 61.6 %
Date: 2015-10-19 Functions: 62 74 83.8 %

          Line data    Source code
       1             : /*
       2             :    SSSD memberof module
       3             : 
       4             :    Copyright (C) Simo Sorce <idra@samba.org> 2008-2011
       5             : 
       6             :    This program is free software; you can redistribute it and/or modify
       7             :    it under the terms of the GNU General Public License as published by
       8             :    the Free Software Foundation; either version 3 of the License, or
       9             :    (at your option) any later version.
      10             : 
      11             :    This program is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU General Public License
      17             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             : */
      19             : 
      20             : #include <string.h>
      21             : #include <dhash.h>
      22             : 
      23             : #include "ldb_module.h"
      24             : #include "util/util.h"
      25             : 
      26             : #define DB_MEMBER "member"
      27             : #define DB_GHOST "ghost"
      28             : #define DB_MEMBEROF "memberof"
      29             : #define DB_MEMBERUID "memberuid"
      30             : #define DB_NAME "name"
      31             : #define DB_USER_CLASS "user"
      32             : #define DB_GROUP_CLASS "group"
      33             : #define DB_CACHE_EXPIRE "dataExpireTimestamp"
      34             : #define DB_OC "objectClass"
      35             : 
      36             : #ifndef MAX
      37             : #define MAX(a,b) (((a) > (b)) ? (a) : (b))
      38             : #endif
      39             : 
      40             : struct mbof_val_array {
      41             :     struct ldb_val *vals;
      42             :     int num;
      43             : };
      44             : 
      45             : struct mbof_dn_array {
      46             :     struct ldb_dn **dns;
      47             :     int num;
      48             : };
      49             : 
      50             : struct mbof_dn {
      51             :     struct mbof_dn *next;
      52             :     struct ldb_dn *dn;
      53             : };
      54             : 
      55             : struct mbof_ctx {
      56             :     struct ldb_module *module;
      57             :     struct ldb_request *req;
      58             : 
      59             :     struct ldb_control **ret_ctrls;
      60             :     struct ldb_extended *ret_resp;
      61             : };
      62             : 
      63             : struct mbof_add_operation {
      64             :     struct mbof_add_ctx *add_ctx;
      65             :     struct mbof_add_operation *next;
      66             : 
      67             :     struct mbof_dn_array *parents;
      68             :     struct ldb_dn *entry_dn;
      69             : 
      70             :     struct ldb_message *entry;
      71             : };
      72             : 
      73             : struct mbof_memberuid_op {
      74             :     struct ldb_dn *dn;
      75             :     struct ldb_message_element *el;
      76             : };
      77             : 
      78             : struct mbof_add_ctx {
      79             :     struct mbof_ctx *ctx;
      80             : 
      81             :     struct mbof_add_operation *add_list;
      82             :     struct mbof_add_operation *current_op;
      83             : 
      84             :     struct ldb_message *msg;
      85             :     struct ldb_dn *msg_dn;
      86             :     bool terminate;
      87             : 
      88             :     struct mbof_dn *missing;
      89             : 
      90             :     struct mbof_memberuid_op *muops;
      91             :     int num_muops;
      92             :     int cur_muop;
      93             : };
      94             : 
      95             : struct mbof_del_ancestors_ctx {
      96             :     struct mbof_dn_array *new_list;
      97             :     int num_direct;
      98             :     int cur;
      99             : 
     100             :     struct ldb_message *entry;
     101             : };
     102             : 
     103             : struct mbof_del_operation {
     104             :     struct mbof_del_ctx *del_ctx;
     105             :     struct mbof_del_operation *parent;
     106             :     struct mbof_del_operation **children;
     107             :     int num_children;
     108             :     int next_child;
     109             : 
     110             :     struct ldb_dn *entry_dn;
     111             : 
     112             :     struct ldb_message *entry;
     113             :     struct ldb_message **parents;
     114             :     int num_parents;
     115             :     int cur_parent;
     116             : 
     117             :     struct mbof_del_ancestors_ctx *anc_ctx;
     118             : };
     119             : 
     120             : struct mbof_mod_ctx;
     121             : 
     122             : struct mbof_del_ctx {
     123             :     struct mbof_ctx *ctx;
     124             : 
     125             :     struct mbof_del_operation *first;
     126             :     struct mbof_dn *history;
     127             : 
     128             :     struct ldb_message **mus;
     129             :     int num_mus;
     130             : 
     131             :     struct mbof_memberuid_op *muops;
     132             :     int num_muops;
     133             :     int cur_muop;
     134             : 
     135             :     struct mbof_memberuid_op *ghops;
     136             :     int num_ghops;
     137             :     int cur_ghop;
     138             : 
     139             :     struct mbof_mod_ctx *follow_mod;
     140             :     bool is_mod;
     141             : };
     142             : 
     143             : struct mbof_mod_del_op {
     144             :     struct mbof_mod_ctx *mod_ctx;
     145             : 
     146             :     struct ldb_message *mod_msg;
     147             :     struct ldb_message_element *el;
     148             : 
     149             :     hash_table_t *inherited_gh;
     150             : };
     151             : 
     152             : struct mbof_mod_ctx {
     153             :     struct mbof_ctx *ctx;
     154             : 
     155             :     const struct ldb_message_element *membel;
     156             :     const struct ldb_message_element *ghel;
     157             :     struct ldb_message *entry;
     158             : 
     159             :     struct mbof_dn_array *mb_add;
     160             :     struct mbof_dn_array *mb_remove;
     161             : 
     162             :     struct mbof_val_array *gh_add;
     163             :     struct mbof_val_array *gh_remove;
     164             :     struct mbof_mod_del_op *igh;
     165             : 
     166             :     struct ldb_message *msg;
     167             :     bool terminate;
     168             : };
     169             : 
     170        1632 : static struct mbof_ctx *mbof_init(struct ldb_module *module,
     171             :                                   struct ldb_request *req)
     172             : {
     173             :     struct mbof_ctx *ctx;
     174             : 
     175        1632 :     ctx = talloc_zero(req, struct mbof_ctx);
     176        1632 :     if (!ctx) {
     177           0 :         return NULL;
     178             :     }
     179             : 
     180        1632 :     ctx->module = module;
     181        1632 :     ctx->req = req;
     182             : 
     183        1632 :     return ctx;
     184             : }
     185             : 
     186         366 : static void *hash_alloc(const size_t size, void *pvt)
     187             : {
     188         366 :     return talloc_size(pvt, size);
     189             : }
     190             : 
     191           0 : static void hash_free(void *ptr, void *pvt)
     192             : {
     193           0 :     talloc_free(ptr);
     194           0 : }
     195             : 
     196         830 : static int entry_has_objectclass(struct ldb_message *entry,
     197             :                                  const char *objectclass)
     198             : {
     199             :     struct ldb_message_element *el;
     200             :     struct ldb_val *val;
     201             :     int i;
     202             : 
     203         830 :     el = ldb_msg_find_element(entry, DB_OC);
     204         830 :     if (!el) {
     205           0 :         return LDB_ERR_OPERATIONS_ERROR;
     206             :     }
     207             : 
     208             :     /* see if this is a user */
     209        1233 :     for (i = 0; i < el->num_values; i++) {
     210         830 :         val = &(el->values[i]);
     211         830 :         if (strncasecmp(objectclass, (char *)val->data, val->length) == 0) {
     212         427 :             return LDB_SUCCESS;
     213             :         }
     214             :     }
     215             : 
     216         403 :     return LDB_ERR_NO_SUCH_ATTRIBUTE;
     217             : }
     218             : 
     219         569 : static int entry_is_user_object(struct ldb_message *entry)
     220             : {
     221         569 :     return entry_has_objectclass(entry, DB_USER_CLASS);
     222             : }
     223             : 
     224         261 : static int entry_is_group_object(struct ldb_message *entry)
     225             : {
     226         261 :     return entry_has_objectclass(entry, DB_GROUP_CLASS);
     227             : }
     228             : 
     229        1648 : static int mbof_append_muop(TALLOC_CTX *memctx,
     230             :                             struct mbof_memberuid_op **_muops,
     231             :                             int *_num_muops,
     232             :                             int flags,
     233             :                             struct ldb_dn *parent,
     234             :                             const char *name,
     235             :                             const char *element_name)
     236             : {
     237        1648 :     struct mbof_memberuid_op *muops = *_muops;
     238        1648 :     int num_muops = *_num_muops;
     239             :     struct mbof_memberuid_op *op;
     240             :     struct ldb_val *val;
     241             :     int i;
     242             : 
     243        1648 :     op = NULL;
     244        1648 :     if (muops) {
     245        3436 :         for (i = 0; i < num_muops; i++) {
     246        2910 :             if (ldb_dn_compare(parent, muops[i].dn) == 0) {
     247         861 :                 op = &muops[i];
     248         861 :                 break;
     249             :             }
     250             :         }
     251             :     }
     252        1648 :     if (!op) {
     253         787 :         muops = talloc_realloc(memctx, muops,
     254             :                                struct mbof_memberuid_op,
     255             :                                num_muops + 1);
     256         787 :         if (!muops) {
     257           0 :             return LDB_ERR_OPERATIONS_ERROR;
     258             :         }
     259         787 :         op = &muops[num_muops];
     260         787 :         num_muops++;
     261         787 :         *_muops = muops;
     262         787 :         *_num_muops = num_muops;
     263             : 
     264         787 :         op->dn = parent;
     265         787 :         op->el = NULL;
     266             :     }
     267             : 
     268        1648 :     if (!op->el) {
     269         787 :         op->el = talloc_zero(muops, struct ldb_message_element);
     270         787 :         if (!op->el) {
     271           0 :             return LDB_ERR_OPERATIONS_ERROR;
     272             :         }
     273         787 :         op->el->name = talloc_strdup(op->el, element_name);
     274         787 :         if (!op->el->name) {
     275           0 :             return LDB_ERR_OPERATIONS_ERROR;
     276             :         }
     277         787 :         op->el->flags = flags;
     278             :     }
     279             : 
     280        6653 :     for (i = 0; i < op->el->num_values; i++) {
     281        5605 :         if (strcmp((char *)op->el->values[i].data, name) == 0) {
     282             :             /* we already have this value, get out*/
     283         600 :             return LDB_SUCCESS;
     284             :         }
     285             :     }
     286             : 
     287        1048 :     val = talloc_realloc(op->el, op->el->values,
     288             :                          struct ldb_val, op->el->num_values + 1);
     289        1048 :     if (!val) {
     290           0 :         return LDB_ERR_OPERATIONS_ERROR;
     291             :     }
     292        1048 :     val[op->el->num_values].data = (uint8_t *)talloc_strdup(val, name);
     293        1048 :     if (!val[op->el->num_values].data) {
     294           0 :         return LDB_ERR_OPERATIONS_ERROR;
     295             :     }
     296        1048 :     val[op->el->num_values].length = strlen(name);
     297             : 
     298        1048 :     op->el->values = val;
     299        1048 :     op->el->num_values++;
     300             : 
     301        1048 :     return LDB_SUCCESS;
     302             : }
     303             : 
     304             : 
     305             : /* add operation */
     306             : 
     307             : /* An add operation is quite simple.
     308             :  * First of all a new object cannot yet have parents, so the only memberof
     309             :  * attribute that can be added to any member contains just one object DN.
     310             :  *
     311             :  * The real add operation is done first, to assure nothing else fails.
     312             :  * Then we list all members of the object just created, and for each member
     313             :  * we create an "add operation" and we pass it a parent list of one member
     314             :  * (the object we just added again).
     315             :  *
     316             :  * For each add operation we lookup the object we want to operate on.
     317             :  * We take the list of memberof attributes and sort out which parents are
     318             :  * still missing from the parent list we have provided.
     319             :  * We modify the object memberof attributes to reflect the new memberships.
     320             :  * Then we list all members of this object, and for each once again we create
     321             :  * an "add operation" as we did in the initial object.
     322             :  *
     323             :  * Processing stops when the target object does not have members or when it
     324             :  * already has all the parents (can happen if nested groups create loops).
     325             :  *
     326             :  * Group cache unrolling:
     327             :  * Every time we add a memberof attribute to an actual user object,
     328             :  * we proceed to store the user name.
     329             :  *
     330             :  * At the end we will add a memberuid attribute to our new object that
     331             :  * includes all direct and indirect user members names.
     332             :  *
     333             :  * Group objects can also contain a "ghost" attribute. A ghost attribute
     334             :  * represents a user that is a member of the group but has not yet been
     335             :  * looked up so there is no real user entry with member/memberof links.
     336             :  *
     337             :  * If an object being added contains a "ghost" attribute, the ghost attribute
     338             :  * is in turn copied to all parents of that object so that retrieving a
     339             :  * group returns both its direct and indirect members. The ghost attribute is
     340             :  * similar to the memberuid attribute in many respects. One difference is that
     341             :  * the memberuid attribute is completely generated and managed by the memberof
     342             :  * plugin - in contrast, the ghost attribute is added to the entry that "owns"
     343             :  * it and only propagated to parent groups.
     344             :  */
     345             : 
     346         429 : static int mbof_append_addop(struct mbof_add_ctx *add_ctx,
     347             :                              struct mbof_dn_array *parents,
     348             :                              struct ldb_dn *entry_dn)
     349             : {
     350         429 :     struct mbof_add_operation *lastop = NULL;
     351             :     struct mbof_add_operation *addop;
     352             : 
     353             :     /* test if this is a duplicate */
     354             :     /* FIXME: this is not efficient */
     355         429 :     if (add_ctx->add_list) {
     356             :         do {
     357         885 :             if (lastop) {
     358         624 :                 lastop = lastop->next;
     359             :             } else {
     360         261 :                 lastop = add_ctx->add_list;
     361             :             }
     362             : 
     363             :             /* FIXME: check if this is right, might have to compare parents */
     364         885 :             if (ldb_dn_compare(lastop->entry_dn, entry_dn) == 0) {
     365             :                 /* duplicate found */
     366           0 :                 return LDB_SUCCESS;
     367             :             }
     368         885 :         } while (lastop->next);
     369             :     }
     370             : 
     371         429 :     addop = talloc_zero(add_ctx, struct mbof_add_operation);
     372         429 :     if (!addop) {
     373           0 :         return LDB_ERR_OPERATIONS_ERROR;
     374             :     }
     375             : 
     376         429 :     addop->add_ctx = add_ctx;
     377         429 :     addop->parents = parents;
     378         429 :     addop->entry_dn = entry_dn;
     379             : 
     380         429 :     if (add_ctx->add_list) {
     381         261 :         lastop->next = addop;
     382             :     } else {
     383         168 :         add_ctx->add_list = addop;
     384             :     }
     385             : 
     386         429 :     return LDB_SUCCESS;
     387             : }
     388             : 
     389         198 : static int mbof_add_fill_ghop_ex(struct mbof_add_ctx *add_ctx,
     390             :                                  struct ldb_message *entry,
     391             :                                  struct mbof_dn_array *parents,
     392             :                                  struct ldb_val *ghvals,
     393             :                                  unsigned int num_gh_vals)
     394             : {
     395             :     int ret;
     396             :     int i, j;
     397             : 
     398         198 :     if (!parents || parents->num == 0) {
     399             :         /* no parents attributes ... */
     400           0 :         return LDB_SUCCESS;
     401             :     }
     402             : 
     403         198 :     ret = entry_is_group_object(entry);
     404         198 :     switch (ret) {
     405             :     case LDB_SUCCESS:
     406             :         /* it's a group object, continue */
     407         198 :         break;
     408             : 
     409             :     case LDB_ERR_NO_SUCH_ATTRIBUTE:
     410             :         /* it is not a group object, just return */
     411           0 :         return LDB_SUCCESS;
     412             : 
     413             :     default:
     414             :         /* an error occured, return */
     415           0 :         return ret;
     416             :     }
     417             : 
     418         198 :     ldb_debug(ldb_module_get_ctx(add_ctx->ctx->module),
     419             :               LDB_DEBUG_TRACE,
     420             :               "will add %d ghost users to %d parents\n",
     421             :               num_gh_vals, parents->num);
     422             : 
     423         468 :     for (i = 0; i < parents->num; i++) {
     424        1185 :         for (j = 0; j < num_gh_vals; j++) {
     425         915 :             ret = mbof_append_muop(add_ctx, &add_ctx->muops,
     426             :                                    &add_ctx->num_muops,
     427             :                                    LDB_FLAG_MOD_ADD,
     428         915 :                                    parents->dns[i],
     429         915 :                                    (const char *) ghvals[j].data,
     430             :                                    DB_GHOST);
     431         915 :             if (ret != LDB_SUCCESS) {
     432           0 :                 return ret;
     433             :             }
     434             :         }
     435             :     }
     436             : 
     437         198 :     return LDB_SUCCESS;
     438             : }
     439             : 
     440             : static int memberof_recompute_task(struct ldb_module *module,
     441             :                                    struct ldb_request *req);
     442             : 
     443             : static int mbof_add_callback(struct ldb_request *req,
     444             :                              struct ldb_reply *ares);
     445             : static int mbof_next_add(struct mbof_add_operation *addop);
     446             : static int mbof_next_add_callback(struct ldb_request *req,
     447             :                                   struct ldb_reply *ares);
     448             : static int mbof_add_operation(struct mbof_add_operation *addop);
     449             : static int mbof_add_fill_ghop(struct mbof_add_ctx *add_ctx,
     450             :                               struct ldb_message *entry,
     451             :                               struct mbof_dn_array *parents);
     452             : static int mbof_add_missing(struct mbof_add_ctx *add_ctx, struct ldb_dn *dn);
     453             : static int mbof_add_cleanup(struct mbof_add_ctx *add_ctx);
     454             : static int mbof_add_cleanup_callback(struct ldb_request *req,
     455             :                                      struct ldb_reply *ares);
     456             : static int mbof_add_muop(struct mbof_add_ctx *add_ctx);
     457             : static int mbof_add_muop_callback(struct ldb_request *req,
     458             :                                   struct ldb_reply *ares);
     459             : 
     460         575 : static int memberof_add(struct ldb_module *module, struct ldb_request *req)
     461             : {
     462         575 :     struct ldb_context *ldb = ldb_module_get_ctx(module);
     463             :     struct mbof_add_ctx *add_ctx;
     464             :     struct mbof_ctx *ctx;
     465             :     struct ldb_request *add_req;
     466             :     struct ldb_message_element *el;
     467             :     struct mbof_dn_array *parents;
     468             :     struct ldb_dn *valdn;
     469             :     int i, ret;
     470             : 
     471         575 :     if (ldb_dn_is_special(req->op.add.message->dn)) {
     472             : 
     473           0 :         if (strcmp("@MEMBEROF-REBUILD",
     474           0 :                    ldb_dn_get_linearized(req->op.add.message->dn)) == 0) {
     475           0 :             return memberof_recompute_task(module, req);
     476             :         }
     477             : 
     478             :         /* do not manipulate other control entries */
     479           0 :         return ldb_next_request(module, req);
     480             :     }
     481             : 
     482             :     /* check if memberof is specified */
     483         575 :     el = ldb_msg_find_element(req->op.add.message, DB_MEMBEROF);
     484         575 :     if (el) {
     485           0 :         ldb_debug(ldb, LDB_DEBUG_ERROR,
     486             :                   "Error: the memberof attribute is readonly.");
     487           0 :         return LDB_ERR_UNWILLING_TO_PERFORM;
     488             :     }
     489             : 
     490             :     /* check if memberuid is specified */
     491         575 :     el = ldb_msg_find_element(req->op.add.message, DB_MEMBERUID);
     492         575 :     if (el) {
     493           0 :         ldb_debug(ldb, LDB_DEBUG_ERROR,
     494             :                   "Error: the memberuid attribute is readonly.");
     495           0 :         return LDB_ERR_UNWILLING_TO_PERFORM;
     496             :     }
     497             : 
     498         575 :     ctx = mbof_init(module, req);
     499         575 :     if (!ctx) {
     500           0 :         return LDB_ERR_OPERATIONS_ERROR;
     501             :     }
     502             : 
     503         575 :     add_ctx = talloc_zero(ctx, struct mbof_add_ctx);
     504         575 :     if (!add_ctx) {
     505           0 :         return LDB_ERR_OPERATIONS_ERROR;
     506             :     }
     507         575 :     add_ctx->ctx = ctx;
     508             : 
     509         575 :     add_ctx->msg = ldb_msg_copy(add_ctx, req->op.add.message);
     510         575 :     if (!add_ctx->msg) {
     511           0 :         return LDB_ERR_OPERATIONS_ERROR;
     512             :     }
     513         575 :     add_ctx->msg_dn = add_ctx->msg->dn;
     514             : 
     515             :     /* continue with normal ops if there are no members */
     516         575 :     el = ldb_msg_find_element(add_ctx->msg, DB_MEMBER);
     517         575 :     if (!el) {
     518         575 :         add_ctx->terminate = true;
     519         575 :         goto done;
     520             :     }
     521             : 
     522           0 :     parents = talloc_zero(add_ctx, struct mbof_dn_array);
     523           0 :     if (!parents) {
     524           0 :         return LDB_ERR_OPERATIONS_ERROR;
     525             :     }
     526           0 :     parents->dns = talloc_array(parents, struct ldb_dn *, 1);
     527           0 :     if (!parents->dns) {
     528           0 :         return LDB_ERR_OPERATIONS_ERROR;
     529             :     }
     530           0 :     parents->dns[0] = add_ctx->msg_dn;
     531           0 :     parents->num = 1;
     532             : 
     533             :     /* process new members */
     534             :     /* check we are not adding ourselves as member as well */
     535           0 :     for (i = 0; i < el->num_values; i++) {
     536           0 :         valdn = ldb_dn_from_ldb_val(add_ctx, ldb, &el->values[i]);
     537           0 :         if (!valdn || !ldb_dn_validate(valdn)) {
     538           0 :             ldb_debug(ldb, LDB_DEBUG_ERROR, "Invalid dn value: [%s]",
     539           0 :                                             (const char *)el->values[i].data);
     540           0 :             return LDB_ERR_INVALID_DN_SYNTAX;
     541             :         }
     542           0 :         if (ldb_dn_compare(valdn, req->op.add.message->dn) == 0) {
     543           0 :             ldb_debug(ldb, LDB_DEBUG_ERROR,
     544             :                       "Adding self as member is not permitted! Skipping");
     545           0 :             continue;
     546             :         }
     547           0 :         ret = mbof_append_addop(add_ctx, parents, valdn);
     548           0 :         if (ret != LDB_SUCCESS) {
     549           0 :             return ret;
     550             :         }
     551             :     }
     552             : 
     553             : done:
     554             :     /* add original object */
     555        1150 :     ret = ldb_build_add_req(&add_req, ldb, add_ctx,
     556         575 :                             add_ctx->msg, req->controls,
     557             :                             add_ctx, mbof_add_callback,
     558             :                             req);
     559         575 :     if (ret != LDB_SUCCESS) {
     560           0 :         return ret;
     561             :     }
     562             : 
     563         575 :     return ldb_next_request(module, add_req);
     564             : }
     565             : 
     566         999 : static int mbof_add_callback(struct ldb_request *req,
     567             :                              struct ldb_reply *ares)
     568             : {
     569             :     struct mbof_add_ctx *add_ctx;
     570             :     struct mbof_ctx *ctx;
     571             :     int ret;
     572             : 
     573         999 :     add_ctx = talloc_get_type(req->context, struct mbof_add_ctx);
     574         999 :     ctx = add_ctx->ctx;
     575             : 
     576         999 :     if (!ares) {
     577           0 :         return ldb_module_done(ctx->req, NULL, NULL,
     578             :                                LDB_ERR_OPERATIONS_ERROR);
     579             :     }
     580         999 :     if (ares->error != LDB_SUCCESS) {
     581           0 :         return ldb_module_done(ctx->req,
     582             :                                ares->controls,
     583             :                                ares->response,
     584             :                                ares->error);
     585             :     }
     586             : 
     587         999 :     switch (ares->type) {
     588             :     case LDB_REPLY_ENTRY:
     589             :         /* shouldn't happen */
     590           0 :         talloc_zfree(ares);
     591           0 :         return ldb_module_done(ctx->req, NULL, NULL,
     592             :                                LDB_ERR_OPERATIONS_ERROR);
     593             :     case LDB_REPLY_REFERRAL:
     594             :         /* ignore */
     595           0 :         break;
     596             : 
     597             :     case LDB_REPLY_DONE:
     598         999 :         if (add_ctx->terminate) {
     599         575 :             return ldb_module_done(ctx->req,
     600             :                                    ctx->ret_ctrls,
     601             :                                    ctx->ret_resp,
     602             :                                    LDB_SUCCESS);
     603             :         }
     604             : 
     605         424 :         if (add_ctx->current_op == NULL) {
     606             :             /* first operation */
     607           0 :             ctx->ret_ctrls = talloc_steal(ctx, ares->controls);
     608           0 :             ctx->ret_resp = talloc_steal(ctx, ares->response);
     609           0 :             ret = mbof_next_add(add_ctx->add_list);
     610             :         }
     611         424 :         else if (add_ctx->current_op->next) {
     612             :             /* next operation */
     613         261 :             ret = mbof_next_add(add_ctx->current_op->next);
     614             :         }
     615             :         else {
     616             :             /* no more operations */
     617         163 :             if (add_ctx->missing) {
     618           0 :                 ret = mbof_add_cleanup(add_ctx);
     619             :             }
     620         163 :             else if (add_ctx->muops) {
     621         136 :                 ret = mbof_add_muop(add_ctx);
     622             :             }
     623             :             else {
     624          27 :                 return ldb_module_done(ctx->req,
     625             :                                        ctx->ret_ctrls,
     626             :                                        ctx->ret_resp,
     627             :                                        LDB_SUCCESS);
     628             :             }
     629             :         }
     630             : 
     631         397 :         if (ret != LDB_SUCCESS) {
     632           0 :             talloc_zfree(ares);
     633           0 :             return ldb_module_done(ctx->req, NULL, NULL, ret);
     634             :         }
     635             :     }
     636             : 
     637         397 :     talloc_zfree(ares);
     638         397 :     return LDB_SUCCESS;
     639             : }
     640             : 
     641         429 : static int mbof_next_add(struct mbof_add_operation *addop)
     642             : {
     643             :     static const char *attrs[] = { DB_OC, DB_NAME,
     644             :                                    DB_MEMBER, DB_GHOST,
     645             :                                    DB_MEMBEROF, NULL };
     646             :     struct ldb_context *ldb;
     647             :     struct ldb_request *req;
     648             :     struct mbof_add_ctx *add_ctx;
     649             :     struct mbof_ctx *ctx;
     650             :     int ret;
     651             : 
     652         429 :     add_ctx = addop->add_ctx;
     653         429 :     ctx = add_ctx->ctx;
     654         429 :     ldb = ldb_module_get_ctx(ctx->module);
     655             : 
     656             :     /* mark the operation as being handled */
     657         429 :     add_ctx->current_op = addop;
     658             : 
     659         429 :     ret = ldb_build_search_req(&req, ldb, ctx,
     660             :                                addop->entry_dn, LDB_SCOPE_BASE,
     661             :                                NULL, attrs, NULL,
     662             :                                addop, mbof_next_add_callback,
     663             :                                ctx->req);
     664         429 :     if (ret != LDB_SUCCESS) {
     665           0 :         return ret;
     666             :     }
     667             : 
     668         429 :     return ldb_request(ldb, req);
     669             : }
     670             : 
     671         854 : static int mbof_next_add_callback(struct ldb_request *req,
     672             :                                   struct ldb_reply *ares)
     673             : {
     674             :     struct mbof_add_operation *addop;
     675             :     struct mbof_add_ctx *add_ctx;
     676             :     struct ldb_context *ldb;
     677             :     struct mbof_ctx *ctx;
     678             :     int ret;
     679             : 
     680         854 :     addop = talloc_get_type(req->context, struct mbof_add_operation);
     681         854 :     add_ctx = addop->add_ctx;
     682         854 :     ctx = add_ctx->ctx;
     683         854 :     ldb = ldb_module_get_ctx(ctx->module);
     684             : 
     685         854 :     if (!ares) {
     686           0 :         return ldb_module_done(ctx->req, NULL, NULL,
     687             :                                LDB_ERR_OPERATIONS_ERROR);
     688             :     }
     689         854 :     if (ares->error != LDB_SUCCESS) {
     690           0 :         return ldb_module_done(ctx->req,
     691             :                                ares->controls,
     692             :                                ares->response,
     693             :                                ares->error);
     694             :     }
     695             : 
     696         854 :     switch (ares->type) {
     697             :     case LDB_REPLY_ENTRY:
     698         425 :         if (addop->entry != NULL) {
     699           0 :             ldb_debug(ldb, LDB_DEBUG_TRACE,
     700             :                            "Found multiple entries for (%s)",
     701             :                            ldb_dn_get_linearized(addop->entry_dn));
     702             :             /* more than one entry per dn ?? db corrupted ? */
     703           0 :             return ldb_module_done(ctx->req, NULL, NULL,
     704             :                                    LDB_ERR_OPERATIONS_ERROR);
     705             :         }
     706             : 
     707         425 :         addop->entry = talloc_steal(addop, ares->message);
     708         425 :         if (addop->entry == NULL) {
     709           0 :             return ldb_module_done(ctx->req, NULL, NULL,
     710             :                                    LDB_ERR_OPERATIONS_ERROR);
     711             :         }
     712             : 
     713         425 :         break;
     714             :     case LDB_REPLY_REFERRAL:
     715             :         /* ignore */
     716           0 :         break;
     717             : 
     718             :     case LDB_REPLY_DONE:
     719         429 :         talloc_zfree(ares);
     720         429 :         if (addop->entry == NULL) {
     721           4 :             ldb_debug(ldb, LDB_DEBUG_TRACE, "Entry not found (%s)",
     722             :                            ldb_dn_get_linearized(addop->entry_dn));
     723             : 
     724             :             /* this target does not exists, save as missing */
     725           4 :             ret = mbof_add_missing(add_ctx, addop->entry_dn);
     726           4 :             if (ret != LDB_SUCCESS) {
     727           0 :                 return ldb_module_done(ctx->req, NULL, NULL, ret);
     728             :             }
     729             :             /* now try the next operation */
     730           4 :             if (add_ctx->current_op->next) {
     731           0 :                 ret = mbof_next_add(add_ctx->current_op->next);
     732             :             }
     733             :             else {
     734             :                 /* no more operations */
     735           4 :                 if (add_ctx->missing) {
     736           4 :                     ret = mbof_add_cleanup(add_ctx);
     737             :                 }
     738           0 :                 else if (add_ctx->muops) {
     739           0 :                     ret = mbof_add_muop(add_ctx);
     740             :                 }
     741             :                 else {
     742           0 :                     return ldb_module_done(ctx->req,
     743             :                                            ctx->ret_ctrls,
     744             :                                            ctx->ret_resp,
     745             :                                            LDB_SUCCESS);
     746             :                 }
     747             :             }
     748           4 :             if (ret != LDB_SUCCESS) {
     749           0 :                 return ldb_module_done(ctx->req, NULL, NULL, ret);
     750             :             }
     751             :         }
     752             :         else {
     753         425 :             ret = mbof_add_operation(addop);
     754         425 :             if (ret != LDB_SUCCESS) {
     755           0 :                 return ldb_module_done(ctx->req, NULL, NULL, ret);
     756             :             }
     757             :         }
     758         429 :         return LDB_SUCCESS;
     759             :     }
     760             : 
     761         425 :     talloc_zfree(ares);
     762         425 :     return LDB_SUCCESS;
     763             : }
     764             : 
     765             : /* if it is a group, add all members for cascade effect
     766             :  * add memberof attribute to this entry
     767             :  */
     768         425 : static int mbof_add_operation(struct mbof_add_operation *addop)
     769             : {
     770             : 
     771             :     TALLOC_CTX *tmp_ctx;
     772             :     struct mbof_ctx *ctx;
     773             :     struct mbof_add_ctx *add_ctx;
     774             :     struct ldb_context *ldb;
     775             :     struct ldb_message_element *el;
     776             :     struct ldb_request *mod_req;
     777             :     struct ldb_message *msg;
     778             :     struct ldb_dn *elval_dn;
     779             :     struct ldb_dn *valdn;
     780             :     struct mbof_dn_array *parents;
     781             :     int i, j, ret;
     782             :     const char *val;
     783             :     const char *name;
     784             : 
     785         425 :     add_ctx = addop->add_ctx;
     786         425 :     ctx = add_ctx->ctx;
     787         425 :     ldb = ldb_module_get_ctx(ctx->module);
     788             : 
     789         425 :     parents = talloc_zero(add_ctx, struct mbof_dn_array);
     790         425 :     if (!parents) {
     791           0 :         return LDB_ERR_OPERATIONS_ERROR;
     792             :     }
     793             :     /* can't be more than the immediate parent */
     794         425 :     parents->dns = talloc_array(parents, struct ldb_dn *,
     795             :                                 addop->parents->num);
     796         425 :     if (!parents->dns) {
     797           0 :         return LDB_ERR_OPERATIONS_ERROR;
     798             :     }
     799             : 
     800             :     /* create new parent set for this entry */
     801        1030 :     for (i = 0; i < addop->parents->num; i++) {
     802             :         /* never add yourself as memberof */
     803         605 :         if (ldb_dn_compare(addop->parents->dns[i], addop->entry_dn) == 0) {
     804          10 :             continue;
     805             :         }
     806         595 :         parents->dns[parents->num] = addop->parents->dns[i];
     807         595 :         parents->num++;
     808             :     }
     809             : 
     810             :     /* remove entries that are already there */
     811         425 :     el = ldb_msg_find_element(addop->entry, DB_MEMBEROF);
     812         425 :     if (el) {
     813             : 
     814         308 :         tmp_ctx = talloc_new(addop);
     815         308 :         if (!tmp_ctx) return LDB_ERR_OPERATIONS_ERROR;
     816             : 
     817        1357 :         for (i = 0; i < el->num_values; i++) {
     818        1049 :             elval_dn = ldb_dn_from_ldb_val(tmp_ctx, ldb, &el->values[i]);
     819        1049 :             if (!elval_dn) {
     820           0 :                 ldb_debug(ldb, LDB_DEBUG_TRACE, "Invalid DN in memberof [%s]",
     821           0 :                                             (const char *)el->values[i].data);
     822           0 :                 talloc_free(tmp_ctx);
     823           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     824             :             }
     825        2173 :             for (j = 0; j < parents->num; j++) {
     826        1124 :                 if (ldb_dn_compare(parents->dns[j], elval_dn) == 0) {
     827             :                     /* duplicate found */
     828           0 :                     break;
     829             :                 }
     830             :             }
     831        1049 :             if (j < parents->num) {
     832             :                 /* remove duplicate */
     833           0 :                 for (;j+1 < parents->num; j++) {
     834           0 :                     parents->dns[j] = parents->dns[j+1];
     835             :                 }
     836           0 :                 parents->num--;
     837             :             }
     838             :         }
     839             : 
     840         308 :         if (parents->num == 0) {
     841             :             /* already contains all parents as memberof, skip to next */
     842           1 :             talloc_free(tmp_ctx);
     843           1 :             talloc_free(addop->entry);
     844           1 :             addop->entry = NULL;
     845             : 
     846           1 :             if (addop->next) {
     847           0 :                 return mbof_next_add(addop->next);
     848             :             }
     849           1 :             else if (add_ctx->muops) {
     850           0 :                 return mbof_add_muop(add_ctx);
     851             :             }
     852             :             else {
     853             :                 /* that was the last entry, get out */
     854           1 :                 return ldb_module_done(ctx->req,
     855             :                                        ctx->ret_ctrls,
     856             :                                        ctx->ret_resp,
     857             :                                        LDB_SUCCESS);
     858             :             }
     859             :         }
     860         307 :         talloc_free(tmp_ctx);
     861             :     }
     862             : 
     863             :     /* if it is a group add all members */
     864         424 :     el = ldb_msg_find_element(addop->entry, DB_MEMBER);
     865         424 :     if (el) {
     866         522 :         for (i = 0; i < el->num_values; i++) {
     867         261 :             valdn = ldb_dn_from_ldb_val(add_ctx, ldb, &el->values[i]);
     868         261 :             if (!valdn) {
     869           0 :                 ldb_debug(ldb, LDB_DEBUG_TRACE, "Invalid DN in member [%s]",
     870           0 :                                             (const char *)el->values[i].data);
     871           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     872             :             }
     873         261 :             if (!ldb_dn_validate(valdn)) {
     874           0 :                 ldb_debug(ldb, LDB_DEBUG_TRACE,
     875             :                                "Invalid DN syntax for member [%s]",
     876           0 :                                             (const char *)el->values[i].data);
     877           0 :                 return LDB_ERR_INVALID_DN_SYNTAX;
     878             :             }
     879         261 :             ret = mbof_append_addop(add_ctx, parents, valdn);
     880         261 :             if (ret != LDB_SUCCESS) {
     881           0 :                 return ret;
     882             :             }
     883             :         }
     884             :     }
     885             : 
     886             :     /* check if we need to store memberuid ops for this entry */
     887         424 :     ret = entry_is_user_object(addop->entry);
     888         424 :     switch (ret) {
     889             :     case LDB_SUCCESS:
     890             :         /* it's a user object  */
     891         100 :         name = ldb_msg_find_attr_as_string(addop->entry, DB_NAME, NULL);
     892         100 :         if (!name) {
     893           0 :             return LDB_ERR_OPERATIONS_ERROR;
     894             :         }
     895             : 
     896         335 :         for (i = 0; i < parents->num; i++) {
     897         235 :             ret = mbof_append_muop(add_ctx, &add_ctx->muops,
     898             :                                    &add_ctx->num_muops,
     899             :                                    LDB_FLAG_MOD_ADD,
     900         235 :                                    parents->dns[i], name,
     901             :                                    DB_MEMBERUID);
     902         235 :             if (ret != LDB_SUCCESS) {
     903           0 :                 return ret;
     904             :             }
     905             :         }
     906             : 
     907         100 :         break;
     908             : 
     909             :     case LDB_ERR_NO_SUCH_ATTRIBUTE:
     910             :         /* it is not a user object, continue */
     911         324 :         break;
     912             : 
     913             :     default:
     914             :         /* an error occured, return */
     915           0 :         return ret;
     916             :     }
     917             : 
     918         424 :     ret = mbof_add_fill_ghop(add_ctx, addop->entry, parents);
     919         424 :     if (ret != LDB_SUCCESS) {
     920           0 :         return ret;
     921             :     }
     922             : 
     923             :     /* we are done with the entry now */
     924         424 :     talloc_free(addop->entry);
     925         424 :     addop->entry = NULL;
     926             : 
     927             :     /* add memberof to entry */
     928         424 :     msg = ldb_msg_new(addop);
     929         424 :     if (!msg) return LDB_ERR_OPERATIONS_ERROR;
     930             : 
     931         424 :     msg->dn = addop->entry_dn;
     932             : 
     933         424 :     ret = ldb_msg_add_empty(msg, DB_MEMBEROF, LDB_FLAG_MOD_ADD, &el);
     934         424 :     if (ret != LDB_SUCCESS) {
     935           0 :         return ret;
     936             :     }
     937         424 :     el->values = talloc_array(msg, struct ldb_val, parents->num);
     938         424 :     if (!el->values) {
     939           0 :         return LDB_ERR_OPERATIONS_ERROR;
     940             :     }
     941        1019 :     for (i = 0, j = 0; i < parents->num; i++) {
     942         595 :         if (ldb_dn_compare(parents->dns[i], msg->dn) == 0) continue;
     943         595 :         val = ldb_dn_get_linearized(parents->dns[i]);
     944         595 :         el->values[j].length = strlen(val);
     945         595 :         el->values[j].data = (uint8_t *)talloc_strdup(el->values, val);
     946         595 :         if (!el->values[j].data) {
     947           0 :             return LDB_ERR_OPERATIONS_ERROR;
     948             :         }
     949         595 :         j++;
     950             :     }
     951         424 :     el->num_values = j;
     952             : 
     953         424 :     ret = ldb_build_mod_req(&mod_req, ldb, add_ctx,
     954             :                             msg, NULL,
     955             :                             add_ctx, mbof_add_callback,
     956             :                             ctx->req);
     957         424 :     if (ret != LDB_SUCCESS) {
     958           0 :         return ret;
     959             :     }
     960         424 :     talloc_steal(mod_req, msg);
     961             : 
     962         424 :     return ldb_next_request(ctx->module, mod_req);
     963             : }
     964             : 
     965         424 : static int mbof_add_fill_ghop(struct mbof_add_ctx *add_ctx,
     966             :                               struct ldb_message *entry,
     967             :                               struct mbof_dn_array *parents)
     968             : {
     969             :     struct ldb_message_element *ghel;
     970             : 
     971         424 :     ghel = ldb_msg_find_element(entry, DB_GHOST);
     972         424 :     if (ghel == NULL || ghel->num_values == 0) {
     973             :         /* No ghel attribute, just return success */
     974         244 :         return LDB_SUCCESS;
     975             :     }
     976             : 
     977         180 :     return mbof_add_fill_ghop_ex(add_ctx, entry, parents,
     978             :                                  ghel->values, ghel->num_values);
     979             : }
     980             : 
     981           4 : static int mbof_add_missing(struct mbof_add_ctx *add_ctx, struct ldb_dn *dn)
     982             : {
     983             :     struct mbof_dn *mdn;
     984             : 
     985           4 :     mdn = talloc(add_ctx, struct mbof_dn);
     986           4 :     if (!mdn) {
     987           0 :         return LDB_ERR_OPERATIONS_ERROR;
     988             :     }
     989           4 :     mdn->dn = talloc_steal(mdn, dn);
     990             : 
     991             :     /* add to the list */
     992           4 :     mdn->next = add_ctx->missing;
     993           4 :     add_ctx->missing = mdn;
     994             : 
     995           4 :     return LDB_SUCCESS;
     996             : }
     997             : 
     998             : /* remove unexisting members and add memberuid attribute */
     999           4 : static int mbof_add_cleanup(struct mbof_add_ctx *add_ctx)
    1000             : {
    1001             :     struct ldb_context *ldb;
    1002             :     struct ldb_message *msg;
    1003             :     struct ldb_request *mod_req;
    1004             :     struct ldb_message_element *el;
    1005             :     struct mbof_ctx *ctx;
    1006             :     struct mbof_dn *iter;
    1007             :     const char *val;
    1008             :     int ret, i, num;
    1009             : 
    1010           4 :     ctx = add_ctx->ctx;
    1011           4 :     ldb = ldb_module_get_ctx(ctx->module);
    1012             : 
    1013           4 :     num = 0;
    1014           8 :     for (iter = add_ctx->missing; iter; iter = iter->next) {
    1015           4 :         num++;
    1016             :     }
    1017           4 :     if (num == 0) {
    1018           0 :         return LDB_ERR_OPERATIONS_ERROR;
    1019             :     }
    1020             : 
    1021           4 :     msg = ldb_msg_new(add_ctx);
    1022           4 :     if (!msg) return LDB_ERR_OPERATIONS_ERROR;
    1023             : 
    1024           4 :     msg->dn = add_ctx->msg_dn;
    1025             : 
    1026           4 :     ret = ldb_msg_add_empty(msg, DB_MEMBER, LDB_FLAG_MOD_DELETE, &el);
    1027           4 :     if (ret != LDB_SUCCESS) {
    1028           0 :         return ret;
    1029             :     }
    1030           4 :     el->values = talloc_array(msg, struct ldb_val, num);
    1031           4 :     if (!el->values) {
    1032           0 :         return LDB_ERR_OPERATIONS_ERROR;
    1033             :     }
    1034           4 :     el->num_values = num;
    1035           8 :     for (i = 0, iter = add_ctx->missing; iter; iter = iter->next, i++) {
    1036           4 :         val = ldb_dn_get_linearized(iter->dn);
    1037           4 :         el->values[i].length = strlen(val);
    1038           4 :         el->values[i].data = (uint8_t *)talloc_strdup(el->values, val);
    1039           4 :         if (!el->values[i].data) {
    1040           0 :             return LDB_ERR_OPERATIONS_ERROR;
    1041             :         }
    1042             :     }
    1043             : 
    1044           4 :     ret = ldb_build_mod_req(&mod_req, ldb, add_ctx,
    1045             :                             msg, NULL,
    1046             :                             add_ctx, mbof_add_cleanup_callback,
    1047             :                             ctx->req);
    1048           4 :     if (ret != LDB_SUCCESS) {
    1049           0 :         return ret;
    1050             :     }
    1051             : 
    1052           4 :     return ldb_next_request(ctx->module, mod_req);
    1053             : }
    1054             : 
    1055           4 : static int mbof_add_cleanup_callback(struct ldb_request *req,
    1056             :                                      struct ldb_reply *ares)
    1057             : {
    1058             :     struct mbof_add_ctx *add_ctx;
    1059             :     struct mbof_ctx *ctx;
    1060             :     int ret;
    1061             : 
    1062           4 :     add_ctx = talloc_get_type(req->context, struct mbof_add_ctx);
    1063           4 :     ctx = add_ctx->ctx;
    1064             : 
    1065           4 :     if (!ares) {
    1066           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    1067             :                                LDB_ERR_OPERATIONS_ERROR);
    1068             :     }
    1069           4 :     if (ares->error != LDB_SUCCESS) {
    1070           0 :         return ldb_module_done(ctx->req,
    1071             :                                ares->controls,
    1072             :                                ares->response,
    1073             :                                ares->error);
    1074             :     }
    1075             : 
    1076           4 :     switch (ares->type) {
    1077             :     case LDB_REPLY_ENTRY:
    1078             :         /* shouldn't happen */
    1079           0 :         talloc_zfree(ares);
    1080           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    1081             :                                LDB_ERR_OPERATIONS_ERROR);
    1082             :     case LDB_REPLY_REFERRAL:
    1083             :         /* ignore */
    1084           0 :         break;
    1085             : 
    1086             :     case LDB_REPLY_DONE:
    1087           4 :         if (add_ctx->muops) {
    1088           0 :             ret = mbof_add_muop(add_ctx);
    1089             :         }
    1090             :         else {
    1091           4 :             return ldb_module_done(ctx->req,
    1092             :                                    ctx->ret_ctrls,
    1093             :                                    ctx->ret_resp,
    1094             :                                    LDB_SUCCESS);
    1095             :         }
    1096             : 
    1097           0 :         if (ret != LDB_SUCCESS) {
    1098           0 :             talloc_zfree(ares);
    1099           0 :             return ldb_module_done(ctx->req, NULL, NULL, ret);
    1100             :         }
    1101             :     }
    1102             : 
    1103           0 :     talloc_zfree(ares);
    1104           0 :     return LDB_SUCCESS;
    1105             : }
    1106             : 
    1107             : /* add memberuid attributes to parent groups */
    1108         361 : static int mbof_add_muop(struct mbof_add_ctx *add_ctx)
    1109             : {
    1110             :     struct ldb_context *ldb;
    1111             :     struct ldb_message *msg;
    1112             :     struct ldb_request *mod_req;
    1113             :     struct mbof_ctx *ctx;
    1114             :     int ret;
    1115             : 
    1116         361 :     ctx = add_ctx->ctx;
    1117         361 :     ldb = ldb_module_get_ctx(ctx->module);
    1118             : 
    1119         361 :     msg = ldb_msg_new(add_ctx);
    1120         361 :     if (!msg) return LDB_ERR_OPERATIONS_ERROR;
    1121             : 
    1122         361 :     msg->dn = add_ctx->muops[add_ctx->cur_muop].dn;
    1123         361 :     msg->elements = add_ctx->muops[add_ctx->cur_muop].el;
    1124         361 :     msg->num_elements = 1;
    1125             : 
    1126         361 :     ret = ldb_build_mod_req(&mod_req, ldb, add_ctx,
    1127             :                             msg, NULL,
    1128             :                             add_ctx, mbof_add_muop_callback,
    1129             :                             ctx->req);
    1130         361 :     if (ret != LDB_SUCCESS) {
    1131           0 :         return ret;
    1132             :     }
    1133             : 
    1134         361 :     ret = ldb_request_add_control(mod_req, LDB_CONTROL_PERMISSIVE_MODIFY_OID,
    1135             :                                   false, NULL);
    1136         361 :     if (ret != LDB_SUCCESS) {
    1137           0 :         talloc_free(mod_req);
    1138           0 :         return ret;
    1139             :     }
    1140             : 
    1141         361 :     return ldb_next_request(ctx->module, mod_req);
    1142             : }
    1143             : 
    1144         361 : static int mbof_add_muop_callback(struct ldb_request *req,
    1145             :                                   struct ldb_reply *ares)
    1146             : {
    1147             :     struct mbof_add_ctx *add_ctx;
    1148             :     struct mbof_ctx *ctx;
    1149             :     int ret;
    1150             : 
    1151         361 :     add_ctx = talloc_get_type(req->context, struct mbof_add_ctx);
    1152         361 :     ctx = add_ctx->ctx;
    1153             : 
    1154         361 :     if (!ares) {
    1155           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    1156             :                                LDB_ERR_OPERATIONS_ERROR);
    1157             :     }
    1158         361 :     if (ares->error != LDB_SUCCESS) {
    1159           0 :         return ldb_module_done(ctx->req,
    1160             :                                ares->controls,
    1161             :                                ares->response,
    1162             :                                ares->error);
    1163             :     }
    1164             : 
    1165         361 :     switch (ares->type) {
    1166             :     case LDB_REPLY_ENTRY:
    1167             :         /* shouldn't happen */
    1168           0 :         talloc_zfree(ares);
    1169           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    1170             :                                LDB_ERR_OPERATIONS_ERROR);
    1171             :     case LDB_REPLY_REFERRAL:
    1172             :         /* ignore */
    1173           0 :         break;
    1174             : 
    1175             :     case LDB_REPLY_DONE:
    1176         361 :         add_ctx->cur_muop++;
    1177         361 :         if (add_ctx->cur_muop < add_ctx->num_muops) {
    1178         207 :             ret = mbof_add_muop(add_ctx);
    1179             :         }
    1180             :         else {
    1181         154 :             return ldb_module_done(ctx->req,
    1182             :                                    ctx->ret_ctrls,
    1183             :                                    ctx->ret_resp,
    1184             :                                    LDB_SUCCESS);
    1185             :         }
    1186             : 
    1187         207 :         if (ret != LDB_SUCCESS) {
    1188           0 :             talloc_zfree(ares);
    1189           0 :             return ldb_module_done(ctx->req, NULL, NULL, ret);
    1190             :         }
    1191             :     }
    1192             : 
    1193         207 :     talloc_zfree(ares);
    1194         207 :     return LDB_SUCCESS;
    1195             : }
    1196             : 
    1197             : 
    1198             : 
    1199             : 
    1200             : /* delete operations */
    1201             : 
    1202             : /* The implementation of delete operations is a bit more complex than an add
    1203             :  * operation. This is because we need to recompute memberships of potentially
    1204             :  * quite far descendants and we also have to account for loops and how to
    1205             :  * break them without ending in an endless loop ourselves.
    1206             :  * The difficulty is in the fact that while the member -> memberof link is
    1207             :  * direct, memberof -> member is not as membership is transitive.
    1208             :  *
    1209             :  * Ok, first of all, contrary to the add operation, a delete operation
    1210             :  * involves an existing object that may have existing parents. So, first, we
    1211             :  * search  the object itself to get the original membership lists (member and
    1212             :  * memberof) for this object, and we also search for any object that has it as
    1213             :  * one of its members.
    1214             :  * Once we have the results, we store object and parents and proceed with the
    1215             :  * original operation to make sure it is valid.
    1216             :  *
    1217             :  * Once the original op returns we proceed fixing parents (parents being each
    1218             :  * object that has the delete operation target object as member), if any.
    1219             :  *
    1220             :  * For each parent we retrieved we proceed to delete the member attribute that
    1221             :  * points to the object we just deleted. Once done for all parents (or if no
    1222             :  * parents exists), we proceed with the children and descendants.
    1223             :  *
    1224             :  * To handle the children we create a first ancestor operation that reflects
    1225             :  * the delete we just made. We set as parents of this object the parents just
    1226             :  * retrieved with the first search. Then we create a remove list.
    1227             :  *
    1228             :  * The remove list contains all objects in the original memberof list and the
    1229             :  * object dn itself of the original delete operation target object (the first
    1230             :  * ancestor).
    1231             :  *
    1232             :  * An operation is identified by an object that contains a tree of
    1233             :  * descendants:
    1234             :  * The remove list for the children, the immediate parent, and the dn and
    1235             :  * entry of the object this operation is about.
    1236             :  *
    1237             :  * We now proceed with adding a new operation for each original member of the
    1238             :  * first ancestor.
    1239             :  *
    1240             :  * In each operation we must first lookup the target object and each immediate
    1241             :  * parent (all the objects in the tree that have target as a "member").
    1242             :  *
    1243             :  * Then we proceed to calculate the new memberof list that we are going to set
    1244             :  * on the target object.
    1245             :  * The new memberof list starts with including all the objects that have the
    1246             :  * target as their direct member.
    1247             :  * Finally for each entry in this provisional new memberof list we add all its
    1248             :  * memberof elements to the new memberof list (taking care of excluding
    1249             :  * duplicates). This way we are certain all direct and indirect membership are
    1250             :  * accounted for.
    1251             :  *
    1252             :  * At this point we have the final new memberof list for this operation and we
    1253             :  * can proceed to modify the entry.
    1254             :  *
    1255             :  * Once the entry has been modified we proceed again to check if there are any
    1256             :  * children of this entry (the entry has "member"s).
    1257             :  * We create a new remove list that is the difference between the original
    1258             :  * entry memberof list and the new memberof list we just stored back in the
    1259             :  * object.
    1260             :  * Then for each member we create a new operation.
    1261             :  *
    1262             :  * We continue to process operations until no new operations need to be
    1263             :  * performed.
    1264             :  *
    1265             :  * Ordering is important here, se the mbof_del_get_next() function to
    1266             :  * understand how we proceed to select which new operation to process.
    1267             :  *
    1268             :  * As a final operation remove any memberuid corresponding to a removal of
    1269             :  * a memberof field from a user entry. Also if the original entry had a ghost
    1270             :  * attribute, we need to remove that attribute from all its parents as well.
    1271             :  *
    1272             :  * There is one catch though - at the memberof level, we can't know if the
    1273             :  * attribute being removed from a parent group is just inherited from the group
    1274             :  * being removed or also a direct member of the parent group. To make sure
    1275             :  * that the attribute is displayed next time the group is requested, we also
    1276             :  * set expire the parent group at the same time.
    1277             :  */
    1278             : 
    1279             : static int mbof_del_search_callback(struct ldb_request *req,
    1280             :                                     struct ldb_reply *ares);
    1281             : static int mbof_orig_del(struct mbof_del_ctx *ctx);
    1282             : static int mbof_orig_del_callback(struct ldb_request *req,
    1283             :                                   struct ldb_reply *ares);
    1284             : static int mbof_del_cleanup_parents(struct mbof_del_ctx *del_ctx);
    1285             : static int mbof_del_clean_par_callback(struct ldb_request *req,
    1286             :                                        struct ldb_reply *ares);
    1287             : static int mbof_del_cleanup_children(struct mbof_del_ctx *del_ctx);
    1288             : static int mbof_append_delop(struct mbof_del_operation *parent,
    1289             :                              struct ldb_dn *entry_dn);
    1290             : static int mbof_del_execute_op(struct mbof_del_operation *delop);
    1291             : static int mbof_del_exop_search_callback(struct ldb_request *req,
    1292             :                                          struct ldb_reply *ares);
    1293             : static int mbof_del_execute_cont(struct mbof_del_operation *delop);
    1294             : static int mbof_del_ancestors(struct mbof_del_operation *delop);
    1295             : static int mbof_del_anc_callback(struct ldb_request *req,
    1296             :                                  struct ldb_reply *ares);
    1297             : static int mbof_del_mod_entry(struct mbof_del_operation *delop);
    1298             : static int mbof_del_mod_callback(struct ldb_request *req,
    1299             :                                  struct ldb_reply *ares);
    1300             : static int mbof_del_progeny(struct mbof_del_operation *delop);
    1301             : static int mbof_del_get_next(struct mbof_del_operation *delop,
    1302             :                              struct mbof_del_operation **nextop);
    1303             : static int mbof_del_fill_muop(struct mbof_del_ctx *del_ctx,
    1304             :                               struct ldb_message *entry);
    1305             : static int mbof_del_fill_ghop(struct mbof_del_ctx *del_ctx,
    1306             :                               struct ldb_message *entry);
    1307             : static int mbof_del_muop(struct mbof_del_ctx *ctx);
    1308             : static int mbof_del_muop_callback(struct ldb_request *req,
    1309             :                                   struct ldb_reply *ares);
    1310             : static int mbof_del_ghop(struct mbof_del_ctx *del_ctx);
    1311             : static int mbof_del_ghop_callback(struct ldb_request *req,
    1312             :                                   struct ldb_reply *ares);
    1313             : static void free_delop_contents(struct mbof_del_operation *delop);
    1314             : 
    1315             : 
    1316         339 : static int memberof_del(struct ldb_module *module, struct ldb_request *req)
    1317             : {
    1318             :     static const char *attrs[] = { DB_OC, DB_NAME,
    1319             :                                    DB_MEMBER, DB_MEMBEROF,
    1320             :                                    DB_GHOST, NULL };
    1321         339 :     struct ldb_context *ldb = ldb_module_get_ctx(module);
    1322             :     struct mbof_del_operation *first;
    1323             :     struct ldb_request *search;
    1324             :     char *expression;
    1325             :     const char *dn;
    1326             :     char *clean_dn;
    1327             :     struct mbof_del_ctx *del_ctx;
    1328             :     struct mbof_ctx *ctx;
    1329             :     int ret;
    1330             :     errno_t sret;
    1331             : 
    1332         339 :     if (ldb_dn_is_special(req->op.del.dn)) {
    1333             :         /* do not manipulate our control entries */
    1334           0 :         return ldb_next_request(module, req);
    1335             :     }
    1336             : 
    1337         339 :     ctx = mbof_init(module, req);
    1338         339 :     if (!ctx) {
    1339           0 :         return LDB_ERR_OPERATIONS_ERROR;
    1340             :     }
    1341             : 
    1342         339 :     del_ctx = talloc_zero(ctx, struct mbof_del_ctx);
    1343         339 :     if (!del_ctx) {
    1344           0 :         talloc_free(ctx);
    1345           0 :         return LDB_ERR_OPERATIONS_ERROR;
    1346             :     }
    1347         339 :     del_ctx->ctx = ctx;
    1348             : 
    1349             :     /* create first entry */
    1350             :     /* the first entry is the parent of all entries and the one where we remove
    1351             :      * member from, it does not get the same treatment as others */
    1352         339 :     first = talloc_zero(del_ctx, struct mbof_del_operation);
    1353         339 :     if (!first) {
    1354           0 :         talloc_free(ctx);
    1355           0 :         return LDB_ERR_OPERATIONS_ERROR;
    1356             :     }
    1357         339 :     del_ctx->first = first;
    1358             : 
    1359         339 :     first->del_ctx = del_ctx;
    1360         339 :     first->entry_dn = req->op.del.dn;
    1361             : 
    1362         339 :     dn = ldb_dn_get_linearized(req->op.del.dn);
    1363         339 :     if (!dn) {
    1364           0 :         talloc_free(ctx);
    1365           0 :         return LDB_ERR_OPERATIONS_ERROR;
    1366             :     }
    1367             : 
    1368         339 :     sret = sss_filter_sanitize(del_ctx, dn, &clean_dn);
    1369         339 :     if (sret != 0) {
    1370           0 :         talloc_free(ctx);
    1371           0 :         return LDB_ERR_OPERATIONS_ERROR;
    1372             :     }
    1373             : 
    1374         339 :     expression = talloc_asprintf(del_ctx,
    1375             :                                  "(|(distinguishedName=%s)(%s=%s))",
    1376             :                                  clean_dn, DB_MEMBER, clean_dn);
    1377         339 :     if (!expression) {
    1378           0 :         talloc_free(ctx);
    1379           0 :         return LDB_ERR_OPERATIONS_ERROR;
    1380             :     }
    1381         339 :     talloc_zfree(clean_dn);
    1382             : 
    1383         339 :     ret = ldb_build_search_req(&search, ldb, del_ctx,
    1384             :                                NULL, LDB_SCOPE_SUBTREE,
    1385             :                                expression, attrs, NULL,
    1386             :                                first, mbof_del_search_callback,
    1387             :                                req);
    1388         339 :     if (ret != LDB_SUCCESS) {
    1389           0 :         talloc_free(ctx);
    1390           0 :         return ret;
    1391             :     }
    1392             : 
    1393         339 :     return ldb_request(ldb, search);
    1394             : }
    1395             : 
    1396         782 : static int mbof_del_search_callback(struct ldb_request *req,
    1397             :                                     struct ldb_reply *ares)
    1398             : {
    1399             :     struct mbof_del_operation *first;
    1400             :     struct ldb_context *ldb;
    1401             :     struct ldb_message *msg;
    1402             :     struct mbof_del_ctx *del_ctx;
    1403             :     struct mbof_ctx *ctx;
    1404             :     int ret;
    1405             : 
    1406         782 :     first = talloc_get_type(req->context, struct mbof_del_operation);
    1407         782 :     del_ctx = first->del_ctx;
    1408         782 :     ctx = del_ctx->ctx;
    1409         782 :     ldb = ldb_module_get_ctx(ctx->module);
    1410             : 
    1411         782 :     if (!ares) {
    1412           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    1413             :                                LDB_ERR_OPERATIONS_ERROR);
    1414             :     }
    1415         782 :     if (ares->error != LDB_SUCCESS) {
    1416           0 :         return ldb_module_done(ctx->req,
    1417             :                                ares->controls,
    1418             :                                ares->response,
    1419             :                                ares->error);
    1420             :     }
    1421             : 
    1422         782 :     switch (ares->type) {
    1423             :     case LDB_REPLY_ENTRY:
    1424         443 :         msg = ares->message;
    1425             : 
    1426         443 :         if (ldb_dn_compare(msg->dn, ctx->req->op.del.dn) == 0) {
    1427             : 
    1428         329 :             if (first->entry != NULL) {
    1429             :                 /* more than one entry per dn ?? db corrupted ? */
    1430           0 :                 return ldb_module_done(ctx->req, NULL, NULL,
    1431             :                                        LDB_ERR_OPERATIONS_ERROR);
    1432             :             }
    1433             : 
    1434         329 :             first->entry = talloc_steal(first, msg);
    1435         329 :             if (first->entry == NULL) {
    1436           0 :                 return ldb_module_done(ctx->req, NULL, NULL,
    1437             :                                        LDB_ERR_OPERATIONS_ERROR);
    1438             :             }
    1439             :         } else {
    1440         114 :             first->parents = talloc_realloc(first, first->parents,
    1441             :                                              struct ldb_message *,
    1442             :                                              first->num_parents + 1);
    1443         114 :             if (!first->parents) {
    1444           0 :                 return ldb_module_done(ctx->req, NULL, NULL,
    1445             :                                        LDB_ERR_OPERATIONS_ERROR);
    1446             :             }
    1447         114 :             msg = talloc_steal(first->parents, ares->message);
    1448         114 :             if (!msg) {
    1449           0 :                 return ldb_module_done(ctx->req, NULL, NULL,
    1450             :                                        LDB_ERR_OPERATIONS_ERROR);
    1451             :             }
    1452         114 :             first->parents[first->num_parents] = msg;
    1453         114 :             first->num_parents++;
    1454             :         }
    1455         443 :         break;
    1456             :     case LDB_REPLY_REFERRAL:
    1457             :         /* ignore */
    1458           0 :         break;
    1459             : 
    1460             :     case LDB_REPLY_DONE:
    1461         339 :         if (first->entry == NULL) {
    1462             :             /* this target does not exists, too bad! */
    1463          10 :             ldb_debug(ldb, LDB_DEBUG_TRACE,
    1464             :                            "Target entry (%s) not found",
    1465             :                            ldb_dn_get_linearized(first->entry_dn));
    1466          10 :             return ldb_module_done(ctx->req, NULL, NULL,
    1467             :                                    LDB_ERR_NO_SUCH_OBJECT);
    1468             :         }
    1469             : 
    1470             :         /* now perform the requested delete, before proceeding further */
    1471         329 :         ret =  mbof_orig_del(del_ctx);
    1472         329 :         if (ret != LDB_SUCCESS) {
    1473           0 :             talloc_zfree(ares);
    1474           0 :             return ldb_module_done(ctx->req, NULL, NULL, ret);
    1475             :         }
    1476             :     }
    1477             : 
    1478         772 :     talloc_zfree(ares);
    1479         772 :     return LDB_SUCCESS;
    1480             : }
    1481             : 
    1482         329 : static int mbof_orig_del(struct mbof_del_ctx *del_ctx)
    1483             : {
    1484             :     struct ldb_request *del_req;
    1485             :     struct mbof_ctx *ctx;
    1486             :     int ret;
    1487             : 
    1488         329 :     ctx = del_ctx->ctx;
    1489             : 
    1490         987 :     ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ctx->module),
    1491         658 :                             ctx->req, ctx->req->op.del.dn, NULL,
    1492             :                             del_ctx, mbof_orig_del_callback,
    1493             :                             ctx->req);
    1494         329 :     if (ret != LDB_SUCCESS) {
    1495           0 :         return ret;
    1496             :     }
    1497             : 
    1498         329 :     return ldb_next_request(ctx->module, del_req);
    1499             : }
    1500             : 
    1501         329 : static int mbof_orig_del_callback(struct ldb_request *req,
    1502             :                                   struct ldb_reply *ares)
    1503             : {
    1504             :     struct ldb_context *ldb;
    1505             :     struct mbof_del_ctx *del_ctx;
    1506             :     struct mbof_ctx *ctx;
    1507             :     int ret;
    1508             : 
    1509         329 :     del_ctx = talloc_get_type(req->context, struct mbof_del_ctx);
    1510         329 :     ctx = del_ctx->ctx;
    1511         329 :     ldb = ldb_module_get_ctx(ctx->module);
    1512             : 
    1513         329 :     if (!ares) {
    1514           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    1515             :                                LDB_ERR_OPERATIONS_ERROR);
    1516             :     }
    1517         329 :     if (ares->error != LDB_SUCCESS) {
    1518           0 :         return ldb_module_done(ctx->req,
    1519             :                                ares->controls,
    1520             :                                ares->response,
    1521             :                                ares->error);
    1522             :     }
    1523             : 
    1524         329 :     if (ares->type != LDB_REPLY_DONE) {
    1525           0 :         talloc_zfree(ares);
    1526           0 :         ldb_set_errstring(ldb, "Invalid reply type!");
    1527           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    1528             :                                LDB_ERR_OPERATIONS_ERROR);
    1529             :     }
    1530             : 
    1531             :     /* save real call stuff */
    1532         329 :     ctx->ret_ctrls = talloc_steal(ctx, ares->controls);
    1533         329 :     ctx->ret_resp = talloc_steal(ctx, ares->response);
    1534             : 
    1535             :     /* prep following clean ops */
    1536         329 :     if (del_ctx->first->num_parents) {
    1537             : 
    1538             :         /* if there are parents there may be memberuids to remove */
    1539          76 :         ret = mbof_del_fill_muop(del_ctx, del_ctx->first->entry);
    1540          76 :         if (ret != LDB_SUCCESS) {
    1541           0 :             talloc_zfree(ares);
    1542           0 :             return ldb_module_done(ctx->req, NULL, NULL, ret);
    1543             :         }
    1544             : 
    1545             :         /* ..or ghost attributes to remove */
    1546          76 :         ret = mbof_del_fill_ghop(del_ctx, del_ctx->first->entry);
    1547          76 :         if (ret != LDB_SUCCESS) {
    1548           0 :             talloc_zfree(ares);
    1549           0 :             return ldb_module_done(ctx->req, NULL, NULL, ret);
    1550             :         }
    1551             : 
    1552             :         /* if there are any parents, fire a removal sequence */
    1553          76 :         ret = mbof_del_cleanup_parents(del_ctx);
    1554             :     }
    1555         253 :     else if (ldb_msg_find_element(del_ctx->first->entry, DB_MEMBER)) {
    1556             :         /* if there are any children, fire a removal sequence */
    1557           4 :         ret = mbof_del_cleanup_children(del_ctx);
    1558             :     }
    1559             :     /* see if there are memberuid operations to perform */
    1560         249 :     else if (del_ctx->muops) {
    1561           0 :         return mbof_del_muop(del_ctx);
    1562             :     }
    1563             :     /* see if we need to remove some ghost users */
    1564         249 :     else if (del_ctx->ghops) {
    1565           0 :         return mbof_del_ghop(del_ctx);
    1566             :     }
    1567             :     else {
    1568             :         /* no parents nor children, end ops */
    1569         249 :         return ldb_module_done(ctx->req,
    1570             :                                ares->controls,
    1571             :                                ares->response,
    1572             :                                LDB_SUCCESS);
    1573             :     }
    1574          80 :     if (ret != LDB_SUCCESS) {
    1575           0 :         talloc_zfree(ares);
    1576           0 :         return ldb_module_done(ctx->req, NULL, NULL, ret);
    1577             :     }
    1578             : 
    1579          80 :     talloc_zfree(ares);
    1580          80 :     return LDB_SUCCESS;
    1581             : }
    1582             : 
    1583         114 : static int mbof_del_cleanup_parents(struct mbof_del_ctx *del_ctx)
    1584             : {
    1585             :     struct mbof_del_operation *first;
    1586             :     struct mbof_ctx *ctx;
    1587             :     struct ldb_context *ldb;
    1588             :     struct ldb_request *mod_req;
    1589             :     struct ldb_message *msg;
    1590             :     struct ldb_message_element *el;
    1591             :     const char *val;
    1592             :     int ret;
    1593             : 
    1594         114 :     first = del_ctx->first;
    1595         114 :     ctx = del_ctx->ctx;
    1596         114 :     ldb = ldb_module_get_ctx(ctx->module);
    1597             : 
    1598         114 :     msg = ldb_msg_new(first->parents);
    1599         114 :     if (!msg) return LDB_ERR_OPERATIONS_ERROR;
    1600             : 
    1601         114 :     msg->dn = first->parents[first->cur_parent]->dn;
    1602         114 :     first->cur_parent++;
    1603             : 
    1604         114 :     ret = ldb_msg_add_empty(msg, DB_MEMBER, LDB_FLAG_MOD_DELETE, &el);
    1605         114 :     if (ret != LDB_SUCCESS) {
    1606           0 :         return ret;
    1607             :     }
    1608         114 :     el->values = talloc_array(msg, struct ldb_val, 1);
    1609         114 :     if (!el->values) {
    1610           0 :         return LDB_ERR_OPERATIONS_ERROR;
    1611             :     }
    1612         114 :     val = ldb_dn_get_linearized(first->entry_dn);
    1613         114 :     el->values[0].length = strlen(val);
    1614         114 :     el->values[0].data = (uint8_t *)talloc_strdup(el->values, val);
    1615         114 :     if (!el->values[0].data) {
    1616           0 :         return LDB_ERR_OPERATIONS_ERROR;
    1617             :     }
    1618         114 :     el->num_values = 1;
    1619             : 
    1620         114 :     ret = ldb_build_mod_req(&mod_req, ldb, first->parents,
    1621             :                             msg, NULL,
    1622             :                             del_ctx, mbof_del_clean_par_callback,
    1623             :                             ctx->req);
    1624         114 :     if (ret != LDB_SUCCESS) {
    1625           0 :         return ret;
    1626             :     }
    1627             : 
    1628         114 :     return ldb_next_request(ctx->module, mod_req);
    1629             : }
    1630             : 
    1631         114 : static int mbof_del_clean_par_callback(struct ldb_request *req,
    1632             :                                        struct ldb_reply *ares)
    1633             : {
    1634             :     struct mbof_del_operation *first;
    1635             :     struct ldb_context *ldb;
    1636             :     struct mbof_del_ctx *del_ctx;
    1637             :     struct mbof_ctx *ctx;
    1638             :     int ret;
    1639             : 
    1640         114 :     del_ctx = talloc_get_type(req->context, struct mbof_del_ctx);
    1641         114 :     first = del_ctx->first;
    1642         114 :     ctx = del_ctx->ctx;
    1643         114 :     ldb = ldb_module_get_ctx(ctx->module);
    1644             : 
    1645         114 :     if (!ares) {
    1646           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    1647             :                                LDB_ERR_OPERATIONS_ERROR);
    1648             :     }
    1649             : 
    1650         114 :     if (ares->error != LDB_SUCCESS) {
    1651           0 :         return ldb_module_done(ctx->req,
    1652             :                                ares->controls,
    1653             :                                ares->response,
    1654             :                                ares->error);
    1655             :     }
    1656             : 
    1657         114 :     if (ares->type != LDB_REPLY_DONE) {
    1658           0 :         talloc_zfree(ares);
    1659           0 :         ldb_set_errstring(ldb, "Invalid reply type!");
    1660           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    1661             :                                LDB_ERR_OPERATIONS_ERROR);
    1662             :     }
    1663             : 
    1664         114 :     if (first->num_parents > first->cur_parent) {
    1665             :         /* still parents to cleanup, go on */
    1666          38 :         ret = mbof_del_cleanup_parents(del_ctx);
    1667             :     }
    1668             :     else {
    1669             :         /* continue */
    1670          76 :         if (ldb_msg_find_element(first->entry, DB_MEMBER)) {
    1671             :             /* if there are any children, fire a removal sequence */
    1672          16 :             ret = mbof_del_cleanup_children(del_ctx);
    1673             :         }
    1674             :         /* see if there are memberuid operations to perform */
    1675          60 :         else if (del_ctx->muops) {
    1676          15 :             return mbof_del_muop(del_ctx);
    1677             :         }
    1678             :         /* see if we need to remove some ghost users */
    1679          45 :         else if (del_ctx->ghops) {
    1680          36 :             return mbof_del_ghop(del_ctx);
    1681             :         }
    1682             :         else {
    1683             :             /* no children, end ops */
    1684           9 :             return ldb_module_done(ctx->req,
    1685             :                                    ctx->ret_ctrls,
    1686             :                                    ctx->ret_resp,
    1687             :                                    LDB_SUCCESS);
    1688             :         }
    1689             :     }
    1690             : 
    1691          54 :     if (ret != LDB_SUCCESS) {
    1692           0 :         talloc_zfree(ares);
    1693           0 :         return ldb_module_done(ctx->req, NULL, NULL, ret);
    1694             :     }
    1695             : 
    1696          54 :     talloc_zfree(ares);
    1697          54 :     return LDB_SUCCESS;
    1698             : }
    1699             : 
    1700          20 : static int mbof_del_cleanup_children(struct mbof_del_ctx *del_ctx)
    1701             : {
    1702             :     struct mbof_del_operation *first;
    1703             :     struct mbof_ctx *ctx;
    1704             :     struct ldb_context *ldb;
    1705             :     const struct ldb_message_element *el;
    1706             :     struct ldb_dn *valdn;
    1707             :     int i, ret;
    1708             : 
    1709          20 :     first = del_ctx->first;
    1710          20 :     ctx = del_ctx->ctx;
    1711          20 :     ldb = ldb_module_get_ctx(ctx->module);
    1712             : 
    1713          20 :     el = ldb_msg_find_element(first->entry, DB_MEMBER);
    1714             : 
    1715             :     /* prepare del sets */
    1716          43 :     for (i = 0; i < el->num_values; i++) {
    1717          23 :         valdn = ldb_dn_from_ldb_val(first, ldb, &el->values[i]);
    1718          23 :         if (!valdn || !ldb_dn_validate(valdn)) {
    1719           0 :             ldb_debug(ldb, LDB_DEBUG_TRACE,
    1720             :                            "Invalid dn syntax for member [%s]",
    1721           0 :                                         (const char *)el->values[i].data);
    1722           0 :             return LDB_ERR_INVALID_DN_SYNTAX;
    1723             :         }
    1724          23 :         ret = mbof_append_delop(first, valdn);
    1725          23 :         if (ret != LDB_SUCCESS) {
    1726           0 :             return ret;
    1727             :         }
    1728             :     }
    1729             : 
    1730             :     /* now that sets are built, start processing */
    1731          20 :     return mbof_del_execute_op(first->children[0]);
    1732             : }
    1733             : 
    1734          69 : static int mbof_append_delop(struct mbof_del_operation *parent,
    1735             :                              struct ldb_dn *entry_dn)
    1736             : {
    1737             :     struct mbof_del_operation *delop;
    1738             : 
    1739          69 :     delop = talloc_zero(parent, struct mbof_del_operation);
    1740          69 :     if (!delop) {
    1741           0 :         return LDB_ERR_OPERATIONS_ERROR;
    1742             :     }
    1743             : 
    1744          69 :     delop->del_ctx = parent->del_ctx;
    1745          69 :     delop->parent = parent;
    1746          69 :     delop->entry_dn = entry_dn;
    1747             : 
    1748          69 :     parent->children = talloc_realloc(parent, parent->children,
    1749             :                                       struct mbof_del_operation *,
    1750             :                                       parent->num_children +1);
    1751          69 :     if (!parent->children) {
    1752           0 :         talloc_free(delop);
    1753           0 :         return LDB_ERR_OPERATIONS_ERROR;
    1754             :     }
    1755             : 
    1756          69 :     parent->children[parent->num_children] = delop;
    1757          69 :     parent->num_children++;
    1758             : 
    1759          69 :     return LDB_SUCCESS;
    1760             : }
    1761             : 
    1762          69 : static int mbof_del_execute_op(struct mbof_del_operation *delop)
    1763             : {
    1764             :     struct mbof_del_ctx *del_ctx;
    1765             :     struct mbof_ctx *ctx;
    1766             :     struct ldb_context *ldb;
    1767             :     struct ldb_request *search;
    1768             :     char *expression;
    1769             :     const char *dn;
    1770             :     char *clean_dn;
    1771             :     static const char *attrs[] = { DB_OC, DB_NAME,
    1772             :                                    DB_MEMBER, DB_MEMBEROF, NULL };
    1773             :     int ret;
    1774             : 
    1775          69 :     del_ctx = delop->del_ctx;
    1776          69 :     ctx = del_ctx->ctx;
    1777          69 :     ldb = ldb_module_get_ctx(ctx->module);
    1778             : 
    1779             :     /* load entry */
    1780          69 :     dn = ldb_dn_get_linearized(delop->entry_dn);
    1781          69 :     if (!dn) {
    1782           0 :         return LDB_ERR_OPERATIONS_ERROR;
    1783             :     }
    1784             : 
    1785          69 :     ret = sss_filter_sanitize(del_ctx, dn, &clean_dn);
    1786          69 :     if (ret != 0) {
    1787           0 :         return LDB_ERR_OPERATIONS_ERROR;
    1788             :     }
    1789             : 
    1790          69 :     expression = talloc_asprintf(del_ctx,
    1791             :                                  "(|(distinguishedName=%s)(%s=%s))",
    1792             :                                  clean_dn, DB_MEMBER, clean_dn);
    1793          69 :     if (!expression) {
    1794           0 :         return LDB_ERR_OPERATIONS_ERROR;
    1795             :     }
    1796          69 :     talloc_zfree(clean_dn);
    1797             : 
    1798          69 :     ret = ldb_build_search_req(&search, ldb, delop,
    1799             :                                NULL, LDB_SCOPE_SUBTREE,
    1800             :                                expression, attrs, NULL,
    1801             :                                delop, mbof_del_exop_search_callback,
    1802             :                                ctx->req);
    1803          69 :     if (ret != LDB_SUCCESS) {
    1804           0 :         talloc_free(ctx);
    1805           0 :         return ret;
    1806             :     }
    1807             : 
    1808          69 :     return ldb_request(ldb, search);
    1809             : }
    1810             : 
    1811         183 : static int mbof_del_exop_search_callback(struct ldb_request *req,
    1812             :                                          struct ldb_reply *ares)
    1813             : {
    1814             :     struct mbof_del_operation *delop;
    1815             :     struct mbof_del_ctx *del_ctx;
    1816             :     struct ldb_context *ldb;
    1817             :     struct mbof_ctx *ctx;
    1818             :     struct ldb_message *msg;
    1819             :     int ret;
    1820             : 
    1821         183 :     delop = talloc_get_type(req->context, struct mbof_del_operation);
    1822         183 :     del_ctx = delop->del_ctx;
    1823         183 :     ctx = del_ctx->ctx;
    1824         183 :     ldb = ldb_module_get_ctx(ctx->module);
    1825             : 
    1826         183 :     if (!ares) {
    1827           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    1828             :                                LDB_ERR_OPERATIONS_ERROR);
    1829             :     }
    1830         183 :     if (ares->error != LDB_SUCCESS) {
    1831           0 :         return ldb_module_done(ctx->req,
    1832             :                                ares->controls,
    1833             :                                ares->response,
    1834             :                                ares->error);
    1835             :     }
    1836             : 
    1837         183 :     switch (ares->type) {
    1838             :     case LDB_REPLY_ENTRY:
    1839         114 :         msg = ares->message;
    1840             : 
    1841         114 :         if (ldb_dn_compare(msg->dn, delop->entry_dn) == 0) {
    1842             : 
    1843          69 :             if (delop->entry != NULL) {
    1844           0 :                 ldb_debug(ldb, LDB_DEBUG_TRACE,
    1845             :                                "Found multiple entries for (%s)",
    1846             :                                ldb_dn_get_linearized(delop->entry_dn));
    1847             :                 /* more than one entry per dn ?? db corrupted ? */
    1848           0 :                 return ldb_module_done(ctx->req, NULL, NULL,
    1849             :                                        LDB_ERR_OPERATIONS_ERROR);
    1850             :             }
    1851             : 
    1852          69 :             delop->entry = talloc_steal(delop, msg);
    1853          69 :             if (delop->entry == NULL) {
    1854           0 :                 return ldb_module_done(ctx->req, NULL, NULL,
    1855             :                                        LDB_ERR_OPERATIONS_ERROR);
    1856             :             }
    1857             :         } else {
    1858          45 :             delop->parents = talloc_realloc(delop, delop->parents,
    1859             :                                             struct ldb_message *,
    1860             :                                             delop->num_parents + 1);
    1861          45 :             if (!delop->parents) {
    1862           0 :                 return ldb_module_done(ctx->req, NULL, NULL,
    1863             :                                        LDB_ERR_OPERATIONS_ERROR);
    1864             :             }
    1865          45 :             msg = talloc_steal(delop->parents, msg);
    1866          45 :             if (!msg) {
    1867           0 :                 return ldb_module_done(ctx->req, NULL, NULL,
    1868             :                                        LDB_ERR_OPERATIONS_ERROR);
    1869             :             }
    1870          45 :             delop->parents[delop->num_parents] = msg;
    1871          45 :             delop->num_parents++;
    1872             :         }
    1873         114 :         break;
    1874             :     case LDB_REPLY_REFERRAL:
    1875             :         /* ignore */
    1876           0 :         break;
    1877             : 
    1878             :     case LDB_REPLY_DONE:
    1879          69 :         if (delop->entry == NULL) {
    1880             :             /* no target, no party! */
    1881           0 :             return ldb_module_done(ctx->req, NULL, NULL,
    1882             :                                    LDB_ERR_OPERATIONS_ERROR);
    1883             :         }
    1884             : 
    1885             :         /* ok process the entry */
    1886          69 :         ret = mbof_del_execute_cont(delop);
    1887             : 
    1888          69 :         if (ret != LDB_SUCCESS) {
    1889           0 :             return ldb_module_done(ctx->req, NULL, NULL,
    1890             :                                    LDB_ERR_OPERATIONS_ERROR);
    1891             :         }
    1892             :     }
    1893             : 
    1894         183 :     talloc_zfree(ares);
    1895         183 :     return LDB_SUCCESS;
    1896             : }
    1897             : 
    1898          69 : static int mbof_del_execute_cont(struct mbof_del_operation *delop)
    1899             : {
    1900             :     struct mbof_del_ancestors_ctx *anc_ctx;
    1901             :     struct mbof_dn_array *new_list;
    1902             :     int i;
    1903             : 
    1904          69 :     anc_ctx = talloc_zero(delop, struct mbof_del_ancestors_ctx);
    1905          69 :     if (!anc_ctx) {
    1906           0 :         return LDB_ERR_OPERATIONS_ERROR;
    1907             :     }
    1908          69 :     delop->anc_ctx = anc_ctx;
    1909             : 
    1910          69 :     new_list = talloc_zero(anc_ctx, struct mbof_dn_array);
    1911          69 :     if (!new_list) {
    1912           0 :         return LDB_ERR_OPERATIONS_ERROR;
    1913             :     }
    1914             : 
    1915             :     /* at the very least we have a number of memberof elements
    1916             :      * equal to the number of objects that have this entry as
    1917             :      * direct member */
    1918          69 :     new_list->num = delop->num_parents;
    1919             : 
    1920             :     /* attach the list to the operation */
    1921          69 :     delop->anc_ctx->new_list = new_list;
    1922          69 :     delop->anc_ctx->num_direct = new_list->num;
    1923             : 
    1924             :     /* do we have any direct parent at all ? */
    1925          69 :     if (new_list->num == 0) {
    1926             :         /* no entries at all, entry ended up being orphaned */
    1927             :         /* skip to directly set the new memberof list for this entry */
    1928             : 
    1929          33 :         return mbof_del_mod_entry(delop);
    1930             :     }
    1931             : 
    1932             :     /* fill in the list if we have parents */
    1933          36 :     new_list->dns = talloc_zero_array(new_list,
    1934             :                                       struct ldb_dn *,
    1935             :                                       new_list->num);
    1936          36 :     if (!new_list->dns) {
    1937           0 :         return LDB_ERR_OPERATIONS_ERROR;
    1938             :     }
    1939          81 :     for (i = 0; i < delop->num_parents; i++) {
    1940          45 :         new_list->dns[i] = delop->parents[i]->dn;
    1941             :     }
    1942             : 
    1943             :     /* before proceeding we also need to fetch the ancestors (anew as some may
    1944             :      * have changed by preceeding operations) */
    1945          36 :     return mbof_del_ancestors(delop);
    1946             : }
    1947             : 
    1948          45 : static int mbof_del_ancestors(struct mbof_del_operation *delop)
    1949             : {
    1950             :     struct mbof_del_ancestors_ctx *anc_ctx;
    1951             :     struct mbof_del_ctx *del_ctx;
    1952             :     struct mbof_ctx *ctx;
    1953             :     struct ldb_context *ldb;
    1954             :     struct mbof_dn_array *new_list;
    1955             :     static const char *attrs[] = { DB_MEMBEROF, NULL };
    1956             :     struct ldb_request *search;
    1957             :     int ret;
    1958             : 
    1959          45 :     del_ctx = delop->del_ctx;
    1960          45 :     ctx = del_ctx->ctx;
    1961          45 :     ldb = ldb_module_get_ctx(ctx->module);
    1962          45 :     anc_ctx = delop->anc_ctx;
    1963          45 :     new_list = anc_ctx->new_list;
    1964             : 
    1965          90 :     ret = ldb_build_search_req(&search, ldb, anc_ctx,
    1966          45 :                                new_list->dns[anc_ctx->cur],
    1967             :                                LDB_SCOPE_BASE, NULL, attrs, NULL,
    1968             :                                delop, mbof_del_anc_callback,
    1969             :                                ctx->req);
    1970          45 :     if (ret != LDB_SUCCESS) {
    1971           0 :         return ret;
    1972             :     }
    1973             : 
    1974          45 :     return ldb_request(ldb, search);
    1975             : }
    1976             : 
    1977          90 : static int mbof_del_anc_callback(struct ldb_request *req,
    1978             :                                  struct ldb_reply *ares)
    1979             : {
    1980             :     struct mbof_del_ancestors_ctx *anc_ctx;
    1981             :     struct mbof_del_operation *delop;
    1982             :     struct mbof_del_ctx *del_ctx;
    1983             :     struct mbof_ctx *ctx;
    1984             :     struct ldb_context *ldb;
    1985             :     struct ldb_message *msg;
    1986             :     const struct ldb_message_element *el;
    1987             :     struct mbof_dn_array *new_list;
    1988             :     struct ldb_dn *valdn;
    1989             :     int i, j, ret;
    1990             : 
    1991          90 :     delop = talloc_get_type(req->context, struct mbof_del_operation);
    1992          90 :     del_ctx = delop->del_ctx;
    1993          90 :     ctx = del_ctx->ctx;
    1994          90 :     ldb = ldb_module_get_ctx(ctx->module);
    1995          90 :     anc_ctx = delop->anc_ctx;
    1996          90 :     new_list = anc_ctx->new_list;
    1997             : 
    1998          90 :     if (!ares) {
    1999           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    2000             :                                LDB_ERR_OPERATIONS_ERROR);
    2001             :     }
    2002          90 :     if (ares->error != LDB_SUCCESS) {
    2003           0 :         return ldb_module_done(ctx->req,
    2004             :                                ares->controls,
    2005             :                                ares->response,
    2006             :                                ares->error);
    2007             :     }
    2008             : 
    2009          90 :     switch (ares->type) {
    2010             :     case LDB_REPLY_ENTRY:
    2011          45 :         msg = ares->message;
    2012             : 
    2013          45 :         if (anc_ctx->entry != NULL) {
    2014           0 :             ldb_debug(ldb, LDB_DEBUG_TRACE,
    2015             :                            "Found multiple entries for (%s)",
    2016           0 :                            ldb_dn_get_linearized(anc_ctx->entry->dn));
    2017             :             /* more than one entry per dn ?? db corrupted ? */
    2018           0 :             return ldb_module_done(ctx->req, NULL, NULL,
    2019             :                                    LDB_ERR_OPERATIONS_ERROR);
    2020             :         }
    2021             : 
    2022          45 :         anc_ctx->entry = talloc_steal(anc_ctx, msg);
    2023          45 :         if (anc_ctx->entry == NULL) {
    2024           0 :             return ldb_module_done(ctx->req, NULL, NULL,
    2025             :                                    LDB_ERR_OPERATIONS_ERROR);
    2026             :         }
    2027          45 :         break;
    2028             :     case LDB_REPLY_REFERRAL:
    2029             :         /* ignore */
    2030           0 :         break;
    2031             : 
    2032             :     case LDB_REPLY_DONE:
    2033          45 :         if (anc_ctx->entry == NULL) {
    2034             :             /* no target, no party! */
    2035           0 :             return ldb_module_done(ctx->req, NULL, NULL,
    2036             :                                    LDB_ERR_OPERATIONS_ERROR);
    2037             :         }
    2038             : 
    2039             :         /* check entry */
    2040          45 :         el = ldb_msg_find_element(anc_ctx->entry, DB_MEMBEROF);
    2041          45 :         if (el) {
    2042         116 :             for (i = 0; i < el->num_values; i++) {
    2043          89 :                 valdn = ldb_dn_from_ldb_val(new_list, ldb, &el->values[i]);
    2044          89 :                 if (!valdn) {
    2045           0 :                     ldb_debug(ldb, LDB_DEBUG_TRACE,
    2046             :                                    "Invalid dn for memberof: (%s)",
    2047           0 :                                    (const char *)el->values[i].data);
    2048           0 :                     return ldb_module_done(ctx->req, NULL, NULL,
    2049             :                                            LDB_ERR_OPERATIONS_ERROR);
    2050             :                 }
    2051         337 :                 for (j = 0; j < new_list->num; j++) {
    2052         248 :                     if (ldb_dn_compare(valdn, new_list->dns[j]) == 0)
    2053           0 :                         break;
    2054             :                 }
    2055          89 :                 if (j < new_list->num) {
    2056           0 :                     talloc_free(valdn);
    2057           0 :                     continue;
    2058             :                 }
    2059             :                 /* do not re-add the original deleted entry by mistake */
    2060          89 :                 if (ldb_dn_compare(valdn, del_ctx->first->entry_dn) == 0) {
    2061           0 :                     talloc_free(valdn);
    2062           0 :                     continue;
    2063             :                 }
    2064          89 :                 new_list->dns = talloc_realloc(new_list,
    2065             :                                                new_list->dns,
    2066             :                                                struct ldb_dn *,
    2067             :                                                new_list->num + 1);
    2068          89 :                 if (!new_list->dns) {
    2069           0 :                     return ldb_module_done(ctx->req, NULL, NULL,
    2070             :                                            LDB_ERR_OPERATIONS_ERROR);
    2071             :                 }
    2072          89 :                 new_list->dns[new_list->num] = valdn;
    2073          89 :                 new_list->num++;
    2074             :             }
    2075             :         }
    2076             : 
    2077             :         /* done with this one */
    2078          45 :         talloc_free(anc_ctx->entry);
    2079          45 :         anc_ctx->entry = NULL;
    2080          45 :         anc_ctx->cur++;
    2081             : 
    2082             :         /* check if we need to process any more */
    2083          45 :         if (anc_ctx->cur < anc_ctx->num_direct) {
    2084             :             /* ok process the next one */
    2085           9 :             ret = mbof_del_ancestors(delop);
    2086             :         } else {
    2087             :             /* ok, end of the story, proceed to modify the entry */
    2088          36 :             ret = mbof_del_mod_entry(delop);
    2089             :         }
    2090             : 
    2091          45 :         if (ret != LDB_SUCCESS) {
    2092           0 :             return ldb_module_done(ctx->req, NULL, NULL,
    2093             :                                    LDB_ERR_OPERATIONS_ERROR);
    2094             :         }
    2095             :     }
    2096             : 
    2097          90 :     talloc_zfree(ares);
    2098          90 :     return LDB_SUCCESS;
    2099             : }
    2100             : 
    2101          69 : static int mbof_del_mod_entry(struct mbof_del_operation *delop)
    2102             : {
    2103             :     struct mbof_del_ctx *del_ctx;
    2104             :     struct mbof_ctx *ctx;
    2105             :     struct ldb_context *ldb;
    2106             :     struct mbof_dn_array *new_list;
    2107             :     struct ldb_request *mod_req;
    2108             :     struct ldb_message *msg;
    2109             :     struct ldb_message_element *el;
    2110          69 :     struct ldb_dn **diff = NULL;
    2111             :     const char *name;
    2112             :     const char *val;
    2113             :     int i, j, k;
    2114             :     bool is_user;
    2115             :     int ret;
    2116             : 
    2117          69 :     del_ctx = delop->del_ctx;
    2118          69 :     ctx = del_ctx->ctx;
    2119          69 :     ldb = ldb_module_get_ctx(ctx->module);
    2120          69 :     new_list = delop->anc_ctx->new_list;
    2121             : 
    2122             :     /* if this is a user we need to find out which entries have been
    2123             :      * removed so that we can later schedule removal of memberuid
    2124             :      * attributes from these entries */
    2125          69 :     ret = entry_is_user_object(delop->entry);
    2126          69 :     switch (ret) {
    2127             :     case LDB_SUCCESS:
    2128             :         /* it's a user object  */
    2129          51 :         is_user = true;
    2130          51 :         break;
    2131             :     case LDB_ERR_NO_SUCH_ATTRIBUTE:
    2132             :         /* it is not a user object, continue */
    2133          18 :         is_user = false;
    2134          18 :         break;
    2135             :     default:
    2136             :         /* an error occured, return */
    2137           0 :         return ret;
    2138             :     }
    2139             : 
    2140          69 :     if (is_user) {
    2141             :         /* prepare memberuid delete list */
    2142             :         /* copy all original memberof entries, and then later remove
    2143             :          * the ones that will survive in the entry */
    2144          51 :         el = ldb_msg_find_element(delop->entry, DB_MEMBEROF);
    2145          51 :         if (!el || !el->num_values) {
    2146           0 :             return LDB_ERR_OPERATIONS_ERROR;
    2147             :         }
    2148          51 :         diff = talloc_array(del_ctx->muops, struct ldb_dn *,
    2149             :                             el->num_values + 1);
    2150          51 :         if (!diff) {
    2151           0 :             return LDB_ERR_OPERATIONS_ERROR;
    2152             :         }
    2153         301 :         for (i = 0, j = 0; i < el->num_values; i++) {
    2154         250 :             diff[j] = ldb_dn_from_ldb_val(diff, ldb, &el->values[i]);
    2155         250 :             if (!diff[j]) {
    2156           0 :                 return LDB_ERR_OPERATIONS_ERROR;
    2157             :             }
    2158             :             /* skip the deleted entry if this is a delete op */
    2159         250 :             if (!del_ctx->is_mod) {
    2160         225 :                 if (ldb_dn_compare(del_ctx->first->entry_dn, diff[j]) == 0) {
    2161          38 :                     continue;
    2162             :                 }
    2163             :             }
    2164         212 :             j++;
    2165             :         }
    2166             :         /* zero terminate array */
    2167          51 :         diff[j] = NULL;
    2168             :     }
    2169             : 
    2170             :     /* change memberof on entry */
    2171          69 :     msg = ldb_msg_new(delop);
    2172          69 :     if (!msg) return LDB_ERR_OPERATIONS_ERROR;
    2173             : 
    2174          69 :     msg->dn = delop->entry_dn;
    2175             : 
    2176          69 :     if (new_list->num) {
    2177          36 :         ret = ldb_msg_add_empty(msg, DB_MEMBEROF, LDB_FLAG_MOD_REPLACE, &el);
    2178          36 :         if (ret != LDB_SUCCESS) {
    2179           0 :             return ret;
    2180             :         }
    2181             : 
    2182          36 :         el->values = talloc_array(el, struct ldb_val, new_list->num);
    2183          36 :         if (!el->values) {
    2184           0 :             return LDB_ERR_OPERATIONS_ERROR;
    2185             :         }
    2186         170 :         for (i = 0, j = 0; i < new_list->num; i++) {
    2187         134 :             if (ldb_dn_compare(new_list->dns[i], msg->dn) == 0)
    2188           0 :                 continue;
    2189         134 :             val = ldb_dn_get_linearized(new_list->dns[i]);
    2190         134 :             if (!val) {
    2191           0 :                 return LDB_ERR_OPERATIONS_ERROR;
    2192             :             }
    2193         134 :             el->values[j].length = strlen(val);
    2194         134 :             el->values[j].data = (uint8_t *)talloc_strdup(el->values, val);
    2195         134 :             if (!el->values[j].data) {
    2196           0 :                 return LDB_ERR_OPERATIONS_ERROR;
    2197             :             }
    2198         134 :             j++;
    2199             : 
    2200         134 :             if (is_user) {
    2201             :                 /* compare the entry's original memberof list with the new
    2202             :                  * one and for each missing entry add a memberuid removal
    2203             :                  * operation */
    2204         218 :                 for (k = 0; diff[k]; k++) {
    2205         218 :                     if (ldb_dn_compare(new_list->dns[i], diff[k]) == 0) {
    2206          82 :                         break;
    2207             :                     }
    2208             :                 }
    2209          82 :                 if (diff[k]) {
    2210          82 :                     talloc_zfree(diff[k]);
    2211         365 :                     for (; diff[k + 1]; k++) {
    2212         283 :                         diff[k] = diff[k + 1];
    2213             :                     }
    2214          82 :                     diff[k] = NULL;
    2215             :                 }
    2216             :             }
    2217             :         }
    2218          36 :         el->num_values = j;
    2219             : 
    2220             :     }
    2221             :     else {
    2222          33 :         ret = ldb_msg_add_empty(msg, DB_MEMBEROF, LDB_FLAG_MOD_DELETE, &el);
    2223          33 :         if (ret != LDB_SUCCESS) {
    2224           0 :             return ret;
    2225             :         }
    2226             :     }
    2227             : 
    2228          69 :     if (is_user && diff[0]) {
    2229             :         /* file memberuid removal operations */
    2230          46 :         name = ldb_msg_find_attr_as_string(delop->entry, DB_NAME, NULL);
    2231          46 :         if (!name) {
    2232           0 :             return LDB_ERR_OPERATIONS_ERROR;
    2233             :         }
    2234             : 
    2235         176 :         for (i = 0; diff[i]; i++) {
    2236         130 :             ret = mbof_append_muop(del_ctx, &del_ctx->muops,
    2237             :                                    &del_ctx->num_muops,
    2238             :                                    LDB_FLAG_MOD_DELETE,
    2239         130 :                                    diff[i], name,
    2240             :                                    DB_MEMBERUID);
    2241         130 :             if (ret != LDB_SUCCESS) {
    2242           0 :                 return ret;
    2243             :             }
    2244             :         }
    2245             :     }
    2246             : 
    2247          69 :     ret = ldb_build_mod_req(&mod_req, ldb, delop,
    2248             :                             msg, NULL,
    2249             :                             delop, mbof_del_mod_callback,
    2250             :                             ctx->req);
    2251          69 :     if (ret != LDB_SUCCESS) {
    2252           0 :         return ret;
    2253             :     }
    2254          69 :     talloc_steal(mod_req, msg);
    2255             : 
    2256          69 :     return ldb_next_request(ctx->module, mod_req);
    2257             : }
    2258             : 
    2259          69 : static int mbof_del_mod_callback(struct ldb_request *req,
    2260             :                                  struct ldb_reply *ares)
    2261             : {
    2262             :     struct mbof_del_operation *delop;
    2263             :     struct mbof_del_ctx *del_ctx;
    2264             :     struct ldb_context *ldb;
    2265             :     struct mbof_ctx *ctx;
    2266             :     int ret;
    2267             : 
    2268          69 :     delop = talloc_get_type(req->context, struct mbof_del_operation);
    2269          69 :     del_ctx = delop->del_ctx;
    2270          69 :     ctx = del_ctx->ctx;
    2271          69 :     ldb = ldb_module_get_ctx(ctx->module);
    2272             : 
    2273          69 :     if (!ares) {
    2274           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    2275             :                                LDB_ERR_OPERATIONS_ERROR);
    2276             :     }
    2277          69 :     if (ares->error != LDB_SUCCESS) {
    2278           0 :         return ldb_module_done(ctx->req,
    2279             :                                ares->controls,
    2280             :                                ares->response,
    2281             :                                ares->error);
    2282             :     }
    2283             : 
    2284          69 :     switch (ares->type) {
    2285             :     case LDB_REPLY_ENTRY:
    2286           0 :         ldb_debug(ldb, LDB_DEBUG_TRACE, "Got an entry on a non search op ?!");
    2287             :         /* shouldn't happen */
    2288           0 :         talloc_zfree(ares);
    2289           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    2290             :                                LDB_ERR_OPERATIONS_ERROR);
    2291             :     case LDB_REPLY_REFERRAL:
    2292             :         /* ignore */
    2293           0 :         talloc_zfree(ares);
    2294           0 :         break;
    2295             : 
    2296             :     case LDB_REPLY_DONE:
    2297             : 
    2298          69 :         ret = mbof_del_progeny(delop);
    2299             : 
    2300          69 :         if (ret != LDB_SUCCESS) {
    2301           0 :             talloc_zfree(ares);
    2302           0 :             return ldb_module_done(ctx->req, NULL, NULL, ret);
    2303             :         }
    2304             :     }
    2305             : 
    2306          69 :     return LDB_SUCCESS;
    2307             : }
    2308             : 
    2309             : static int mbof_mod_add(struct mbof_mod_ctx *mod_ctx,
    2310             :                         struct mbof_dn_array *ael,
    2311             :                         struct mbof_val_array *addgh);
    2312             : 
    2313          69 : static int mbof_del_progeny(struct mbof_del_operation *delop)
    2314             : {
    2315             :     struct mbof_ctx *ctx;
    2316             :     struct mbof_del_ctx *del_ctx;
    2317             :     struct mbof_del_operation *nextop;
    2318             :     const struct ldb_message_element *el;
    2319             :     struct ldb_context *ldb;
    2320             :     struct ldb_dn *valdn;
    2321             :     int i, ret;
    2322             : 
    2323          69 :     del_ctx = delop->del_ctx;
    2324          69 :     ctx = del_ctx->ctx;
    2325          69 :     ldb = ldb_module_get_ctx(ctx->module);
    2326             : 
    2327             :     /* now verify if this entry is a group and members need to be processed as
    2328             :      * well */
    2329             : 
    2330          69 :     el = ldb_msg_find_element(delop->entry, DB_MEMBER);
    2331          69 :     if (el) {
    2332          51 :         for (i = 0; i < el->num_values; i++) {
    2333          33 :             valdn = ldb_dn_from_ldb_val(delop, ldb, &el->values[i]);
    2334          33 :             if (!valdn || !ldb_dn_validate(valdn)) {
    2335           0 :                 ldb_debug(ldb, LDB_DEBUG_TRACE,
    2336             :                                "Invalid DN for member: (%s)",
    2337           0 :                                (const char *)el->values[i].data);
    2338           0 :                 return LDB_ERR_INVALID_DN_SYNTAX;
    2339             :             }
    2340          33 :             ret = mbof_append_delop(delop, valdn);
    2341          33 :             if (ret != LDB_SUCCESS) {
    2342           0 :                 return ret;
    2343             :             }
    2344             :         }
    2345             :     }
    2346             : 
    2347             :     /* finally find the next entry to handle */
    2348          69 :     ret = mbof_del_get_next(delop, &nextop);
    2349          69 :     if (ret != LDB_SUCCESS) {
    2350           0 :         return ret;
    2351             :     }
    2352             : 
    2353          69 :     free_delop_contents(delop);
    2354             : 
    2355          69 :     if (nextop) {
    2356          36 :         return mbof_del_execute_op(nextop);
    2357             :     }
    2358             : 
    2359             :     /* see if there are memberuid operations to perform */
    2360          33 :     if (del_ctx->muops) {
    2361          29 :         return mbof_del_muop(del_ctx);
    2362             :     }
    2363             :     /* see if we need to remove some ghost users */
    2364           4 :     else if (del_ctx->ghops) {
    2365           0 :         return mbof_del_ghop(del_ctx);
    2366             :     }
    2367             :     /* see if there are follow functions to run */
    2368           4 :     if (del_ctx->follow_mod) {
    2369           0 :         return mbof_mod_add(del_ctx->follow_mod,
    2370           0 :                             del_ctx->follow_mod->mb_add,
    2371           0 :                             del_ctx->follow_mod->gh_add);
    2372             :     }
    2373             : 
    2374             :     /* ok, no more ops, this means our job is done */
    2375           4 :     return ldb_module_done(ctx->req,
    2376             :                            ctx->ret_ctrls,
    2377             :                            ctx->ret_resp,
    2378             :                            LDB_SUCCESS);
    2379             : }
    2380             : 
    2381          69 : static int mbof_del_get_next(struct mbof_del_operation *delop,
    2382             :                              struct mbof_del_operation **nextop)
    2383             : {
    2384             :     struct mbof_del_operation *top, *cop;
    2385             :     struct mbof_del_ctx *del_ctx;
    2386             :     struct mbof_dn *save, *tmp;
    2387             : 
    2388          69 :     del_ctx = delop->del_ctx;
    2389             : 
    2390             :     /* first of all, save the current delop in the history */
    2391          69 :     save = talloc_zero(del_ctx, struct mbof_dn);
    2392          69 :     if (!save) {
    2393           0 :         return LDB_ERR_OPERATIONS_ERROR;
    2394             :     }
    2395          69 :     save->dn = delop->entry_dn;
    2396             : 
    2397          69 :     if (del_ctx->history) {
    2398          36 :         tmp = del_ctx->history;
    2399          36 :         while (tmp->next) tmp = tmp->next;
    2400          36 :         tmp->next = save;
    2401             :     } else {
    2402          33 :         del_ctx->history = save;
    2403             :     }
    2404             : 
    2405             :     /* Find next one */
    2406         171 :     for (top = delop; top; top = top->parent) {
    2407         138 :         if (top->num_children == 0 || top->next_child >= top->num_children) {
    2408             :             /* no children, go for next one */
    2409          72 :             continue;
    2410             :         }
    2411             : 
    2412         165 :         while (top->next_child < top->num_children) {
    2413          69 :             cop = top->children[top->next_child];
    2414          69 :             top->next_child++;
    2415             : 
    2416             :             /* verify this operation has not already been performed */
    2417         331 :             for (tmp = del_ctx->history; tmp; tmp = tmp->next) {
    2418         295 :                 if (ldb_dn_compare(tmp->dn, cop->entry_dn) == 0) {
    2419          33 :                     break;
    2420             :                 }
    2421             :             }
    2422          69 :             if (tmp == NULL) {
    2423             :                 /* and return the current one */
    2424          36 :                 *nextop = cop;
    2425          36 :                 return LDB_SUCCESS;
    2426             :             }
    2427             :         }
    2428             :     }
    2429             : 
    2430             :     /* we have no more ops */
    2431          33 :     *nextop = NULL;
    2432          33 :     return LDB_SUCCESS;
    2433             : }
    2434             : 
    2435          76 : static int mbof_del_fill_muop(struct mbof_del_ctx *del_ctx,
    2436             :                               struct ldb_message *entry)
    2437             : {
    2438             :     struct ldb_message_element *el;
    2439             :     char *name;
    2440             :     int ret;
    2441             :     int i;
    2442             : 
    2443          76 :     el = ldb_msg_find_element(entry, DB_MEMBEROF);
    2444          76 :     if (!el || el->num_values == 0) {
    2445             :         /* no memberof attributes ... */
    2446           0 :         return LDB_SUCCESS;
    2447             :     }
    2448             : 
    2449          76 :     ret = entry_is_user_object(entry);
    2450          76 :     switch (ret) {
    2451             :     case LDB_SUCCESS:
    2452             :         /* it's a user object, continue */
    2453          15 :         break;
    2454             : 
    2455             :     case LDB_ERR_NO_SUCH_ATTRIBUTE:
    2456             :         /* it is not a user object, just return */
    2457          61 :         return LDB_SUCCESS;
    2458             : 
    2459             :     default:
    2460             :         /* an error occured, return */
    2461           0 :         return ret;
    2462             :     }
    2463             : 
    2464          15 :     name = talloc_strdup(del_ctx,
    2465             :                          ldb_msg_find_attr_as_string(entry, DB_NAME, NULL));
    2466          15 :     if (!name) {
    2467           0 :         return LDB_ERR_OPERATIONS_ERROR;
    2468             :     }
    2469             : 
    2470          68 :     for (i = 0; i < el->num_values; i++) {
    2471             :         struct ldb_dn *valdn;
    2472             : 
    2473         159 :         valdn = ldb_dn_from_ldb_val(del_ctx->muops,
    2474          53 :                                     ldb_module_get_ctx(del_ctx->ctx->module),
    2475         106 :                                     &el->values[i]);
    2476          53 :         if (!valdn || !ldb_dn_validate(valdn)) {
    2477           0 :             ldb_debug(ldb_module_get_ctx(del_ctx->ctx->module),
    2478             :                       LDB_DEBUG_ERROR,
    2479             :                       "Invalid dn value: [%s]",
    2480           0 :                       (const char *)el->values[i].data);
    2481             :         }
    2482             : 
    2483          53 :         ret = mbof_append_muop(del_ctx, &del_ctx->muops,
    2484             :                                &del_ctx->num_muops,
    2485             :                                LDB_FLAG_MOD_DELETE,
    2486             :                                valdn, name,
    2487             :                                DB_MEMBERUID);
    2488          53 :         if (ret != LDB_SUCCESS) {
    2489           0 :             return ret;
    2490             :         }
    2491             :     }
    2492             : 
    2493          15 :     return LDB_SUCCESS;
    2494             : }
    2495             : 
    2496          63 : static int mbof_del_fill_ghop_ex(struct mbof_del_ctx *del_ctx,
    2497             :                                  struct ldb_message *entry,
    2498             :                                  struct ldb_val *ghvals,
    2499             :                                  unsigned int num_gh_vals)
    2500             : {
    2501             :     struct ldb_message_element *mbof;
    2502             :     struct ldb_dn *valdn;
    2503             :     int ret;
    2504             :     int i, j;
    2505             : 
    2506          63 :     mbof = ldb_msg_find_element(entry, DB_MEMBEROF);
    2507          63 :     if (!mbof || mbof->num_values == 0) {
    2508             :         /* no memberof attributes ... */
    2509           0 :         return LDB_SUCCESS;
    2510             :     }
    2511             : 
    2512          63 :     ret = entry_is_group_object(entry);
    2513          63 :     switch (ret) {
    2514             :     case LDB_SUCCESS:
    2515             :         /* it's a group object, continue */
    2516          63 :         break;
    2517             : 
    2518             :     case LDB_ERR_NO_SUCH_ATTRIBUTE:
    2519             :         /* it is not a group object, just return */
    2520           0 :         return LDB_SUCCESS;
    2521             : 
    2522             :     default:
    2523             :         /* an error occured, return */
    2524           0 :         return ret;
    2525             :     }
    2526             : 
    2527          63 :     ldb_debug(ldb_module_get_ctx(del_ctx->ctx->module),
    2528             :               LDB_DEBUG_TRACE,
    2529             :               "will delete %d ghost users from %d parents\n",
    2530             :               num_gh_vals, mbof->num_values);
    2531             : 
    2532         378 :     for (i = 0; i < mbof->num_values; i++) {
    2533         945 :         valdn = ldb_dn_from_ldb_val(del_ctx->ghops,
    2534         315 :                                     ldb_module_get_ctx(del_ctx->ctx->module),
    2535         630 :                                     &mbof->values[i]);
    2536         315 :         if (!valdn || !ldb_dn_validate(valdn)) {
    2537           0 :             ldb_debug(ldb_module_get_ctx(del_ctx->ctx->module),
    2538             :                       LDB_DEBUG_ERROR,
    2539             :                       "Invalid dn value: [%s]",
    2540           0 :                       (const char *)mbof->values[i].data);
    2541             :         }
    2542             : 
    2543         315 :         ldb_debug(ldb_module_get_ctx(del_ctx->ctx->module),
    2544             :                   LDB_DEBUG_TRACE,
    2545             :                   "processing ghosts in parent [%s]\n",
    2546         315 :                   (const char *) mbof->values[i].data);
    2547             : 
    2548         630 :         for (j = 0; j < num_gh_vals; j++) {
    2549         315 :             ret = mbof_append_muop(del_ctx, &del_ctx->ghops,
    2550             :                                    &del_ctx->num_ghops,
    2551             :                                    LDB_FLAG_MOD_DELETE,
    2552             :                                    valdn,
    2553         315 :                                    (const char *) ghvals[j].data,
    2554             :                                    DB_GHOST);
    2555         315 :             if (ret != LDB_SUCCESS) {
    2556           0 :                 return ret;
    2557             :             }
    2558             :         }
    2559             :     }
    2560             : 
    2561          63 :     return LDB_SUCCESS;
    2562             : }
    2563             : 
    2564          76 : static int mbof_del_fill_ghop(struct mbof_del_ctx *del_ctx,
    2565             :                               struct ldb_message *entry)
    2566             : {
    2567             :     struct ldb_message_element *ghel;
    2568             : 
    2569          76 :     ghel = ldb_msg_find_element(entry, DB_GHOST);
    2570          76 :     if (ghel == NULL || ghel->num_values == 0) {
    2571             :         /* No ghel attribute, just return success */
    2572          40 :         return LDB_SUCCESS;
    2573             :     }
    2574             : 
    2575          36 :     return mbof_del_fill_ghop_ex(del_ctx, entry,
    2576             :                                  ghel->values, ghel->num_values);
    2577             : }
    2578             : 
    2579             : /* del memberuid attributes from parent groups */
    2580         111 : static int mbof_del_muop(struct mbof_del_ctx *del_ctx)
    2581             : {
    2582             :     struct ldb_context *ldb;
    2583             :     struct ldb_message *msg;
    2584             :     struct ldb_request *mod_req;
    2585             :     struct mbof_ctx *ctx;
    2586             :     int ret;
    2587             : 
    2588         111 :     ctx = del_ctx->ctx;
    2589         111 :     ldb = ldb_module_get_ctx(ctx->module);
    2590             : 
    2591         111 :     msg = ldb_msg_new(del_ctx);
    2592         111 :     if (!msg) return LDB_ERR_OPERATIONS_ERROR;
    2593             : 
    2594         111 :     msg->dn = del_ctx->muops[del_ctx->cur_muop].dn;
    2595         111 :     msg->elements = del_ctx->muops[del_ctx->cur_muop].el;
    2596         111 :     msg->num_elements = 1;
    2597             : 
    2598         111 :     ret = ldb_build_mod_req(&mod_req, ldb, del_ctx,
    2599             :                             msg, NULL,
    2600             :                             del_ctx, mbof_del_muop_callback,
    2601             :                             ctx->req);
    2602         111 :     if (ret != LDB_SUCCESS) {
    2603           0 :         return ret;
    2604             :     }
    2605             : 
    2606         111 :     return ldb_next_request(ctx->module, mod_req);
    2607             : }
    2608             : 
    2609         111 : static int mbof_del_muop_callback(struct ldb_request *req,
    2610             :                                   struct ldb_reply *ares)
    2611             : {
    2612             :     struct mbof_del_ctx *del_ctx;
    2613             :     struct mbof_ctx *ctx;
    2614             :     int ret;
    2615             : 
    2616         111 :     del_ctx = talloc_get_type(req->context, struct mbof_del_ctx);
    2617         111 :     ctx = del_ctx->ctx;
    2618             : 
    2619         111 :     if (!ares) {
    2620           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    2621             :                                LDB_ERR_OPERATIONS_ERROR);
    2622             :     }
    2623             :     /* if the attribute was not present it means the db is not
    2624             :      * perfectly consistent but failing here is not useful
    2625             :      * anyway and missing entries cause no harm if we are trying
    2626             :      * to remove them anyway */
    2627         111 :     if (ares->error != LDB_SUCCESS &&
    2628           0 :         ares->error != LDB_ERR_NO_SUCH_ATTRIBUTE) {
    2629           0 :         return ldb_module_done(ctx->req,
    2630             :                                ares->controls,
    2631             :                                ares->response,
    2632             :                                ares->error);
    2633             :     }
    2634             : 
    2635         111 :     switch (ares->type) {
    2636             :     case LDB_REPLY_ENTRY:
    2637             :         /* shouldn't happen */
    2638           0 :         talloc_zfree(ares);
    2639           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    2640             :                                LDB_ERR_OPERATIONS_ERROR);
    2641             :     case LDB_REPLY_REFERRAL:
    2642             :         /* ignore */
    2643           0 :         break;
    2644             : 
    2645             :     case LDB_REPLY_DONE:
    2646         111 :         del_ctx->cur_muop++;
    2647         111 :         if (del_ctx->cur_muop < del_ctx->num_muops) {
    2648          67 :             ret = mbof_del_muop(del_ctx);
    2649             :         }
    2650             :         /* see if we need to remove some ghost users */
    2651          44 :         else if (del_ctx->ghops) {
    2652           0 :             return mbof_del_ghop(del_ctx);
    2653             :         }
    2654             :         /* see if there are follow functions to run */
    2655          44 :         else if (del_ctx->follow_mod) {
    2656           0 :             return mbof_mod_add(del_ctx->follow_mod,
    2657           0 :                                 del_ctx->follow_mod->mb_add,
    2658           0 :                                 del_ctx->follow_mod->gh_add);
    2659             :         }
    2660             :         else {
    2661          44 :             return ldb_module_done(ctx->req,
    2662             :                                    ctx->ret_ctrls,
    2663             :                                    ctx->ret_resp,
    2664             :                                    LDB_SUCCESS);
    2665             :         }
    2666             : 
    2667          67 :         if (ret != LDB_SUCCESS) {
    2668           0 :             talloc_zfree(ares);
    2669           0 :             return ldb_module_done(ctx->req, NULL, NULL, ret);
    2670             :         }
    2671             :     }
    2672             : 
    2673          67 :     talloc_zfree(ares);
    2674          67 :     return LDB_SUCCESS;
    2675             : }
    2676             : 
    2677             : /* del ghost attributes from parent groups */
    2678         315 : static int mbof_del_ghop(struct mbof_del_ctx *del_ctx)
    2679             : {
    2680             :     struct ldb_context *ldb;
    2681             :     struct ldb_message *msg;
    2682             :     struct ldb_request *mod_req;
    2683             :     struct mbof_ctx *ctx;
    2684             :     int ret;
    2685             : 
    2686         315 :     ctx = del_ctx->ctx;
    2687         315 :     ldb = ldb_module_get_ctx(ctx->module);
    2688             : 
    2689         315 :     msg = ldb_msg_new(del_ctx);
    2690         315 :     if (!msg) return LDB_ERR_OPERATIONS_ERROR;
    2691             : 
    2692         315 :     msg->dn = del_ctx->ghops[del_ctx->cur_ghop].dn;
    2693             : 
    2694         315 :     ret = ldb_msg_add(msg, del_ctx->ghops[del_ctx->cur_ghop].el,
    2695             :                       LDB_FLAG_MOD_DELETE);
    2696         315 :     if (ret != LDB_SUCCESS) {
    2697           0 :         return ret;
    2698             :     }
    2699             : 
    2700             :     /* Also expire any parent groups to force reloading direct members in
    2701             :      * case the ghost users we remove now were actually *also* direct members
    2702             :      * of the parent groups
    2703             :      */
    2704         315 :     ret = ldb_msg_add_empty(msg, DB_CACHE_EXPIRE, LDB_FLAG_MOD_REPLACE, NULL);
    2705         315 :     if (ret != LDB_SUCCESS) {
    2706           0 :         return ret;
    2707             :     }
    2708             : 
    2709         315 :     ret = ldb_msg_add_string(msg, DB_CACHE_EXPIRE, "1");
    2710         315 :     if (ret != LDB_SUCCESS) {
    2711           0 :         return ret;
    2712             :     }
    2713             : 
    2714         315 :     ret = ldb_build_mod_req(&mod_req, ldb, del_ctx,
    2715             :                             msg, NULL,
    2716             :                             del_ctx, mbof_del_ghop_callback,
    2717             :                             ctx->req);
    2718         315 :     if (ret != LDB_SUCCESS) {
    2719           0 :         return ret;
    2720             :     }
    2721             : 
    2722         315 :     return ldb_next_request(ctx->module, mod_req);
    2723             : }
    2724             : 
    2725         315 : static int mbof_del_ghop_callback(struct ldb_request *req,
    2726             :                                   struct ldb_reply *ares)
    2727             : {
    2728             :     struct mbof_del_ctx *del_ctx;
    2729             :     struct mbof_ctx *ctx;
    2730             :     int ret;
    2731             : 
    2732         315 :     del_ctx = talloc_get_type(req->context, struct mbof_del_ctx);
    2733         315 :     ctx = del_ctx->ctx;
    2734             : 
    2735         315 :     if (!ares) {
    2736           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    2737             :                                LDB_ERR_OPERATIONS_ERROR);
    2738             :     }
    2739             : 
    2740             :     /* We must treat no such attribute as non-fatal b/c the entry
    2741             :      * might have been directly nested in the parent as well and
    2742             :      * updated with another replace operation.
    2743             :      */
    2744         315 :     if (ares->error != LDB_SUCCESS &&
    2745           0 :         ares->error != LDB_ERR_NO_SUCH_ATTRIBUTE) {
    2746           0 :         return ldb_module_done(ctx->req,
    2747             :                                ares->controls,
    2748             :                                ares->response,
    2749             :                                ares->error);
    2750             :     }
    2751             : 
    2752         315 :     switch (ares->type) {
    2753             :     case LDB_REPLY_ENTRY:
    2754             :         /* shouldn't happen */
    2755           0 :         talloc_zfree(ares);
    2756           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    2757             :                                LDB_ERR_OPERATIONS_ERROR);
    2758             :     case LDB_REPLY_REFERRAL:
    2759             :         /* ignore */
    2760           0 :         break;
    2761             : 
    2762             :     case LDB_REPLY_DONE:
    2763         315 :         del_ctx->cur_ghop++;
    2764         315 :         if (del_ctx->cur_ghop < del_ctx->num_ghops) {
    2765         252 :             ret = mbof_del_ghop(del_ctx);
    2766             :         }
    2767             :         /* see if there are follow functions to run */
    2768          63 :         else if (del_ctx->follow_mod) {
    2769          18 :             return mbof_mod_add(del_ctx->follow_mod,
    2770           9 :                                 del_ctx->follow_mod->mb_add,
    2771           9 :                                 del_ctx->follow_mod->gh_add);
    2772             :         }
    2773             :         else {
    2774          54 :             return ldb_module_done(ctx->req,
    2775             :                                    ctx->ret_ctrls,
    2776             :                                    ctx->ret_resp,
    2777             :                                    LDB_SUCCESS);
    2778             :         }
    2779             : 
    2780         252 :         if (ret != LDB_SUCCESS) {
    2781           0 :             talloc_zfree(ares);
    2782           0 :             return ldb_module_done(ctx->req, NULL, NULL, ret);
    2783             :         }
    2784             :     }
    2785             : 
    2786         252 :     talloc_zfree(ares);
    2787         252 :     return LDB_SUCCESS;
    2788             : }
    2789             : 
    2790             : /* delop may carry on a lot of memory, so we need a function to clean up
    2791             :  * the payload without breaking the delop chain */
    2792          69 : static void free_delop_contents(struct mbof_del_operation *delop)
    2793             : {
    2794          69 :     talloc_zfree(delop->entry);
    2795          69 :     talloc_zfree(delop->parents);
    2796          69 :     talloc_zfree(delop->anc_ctx);
    2797          69 :     delop->num_parents = 0;
    2798          69 :     delop->cur_parent = 0;
    2799          69 : }
    2800             : 
    2801             : /* mod operation */
    2802             : 
    2803             : /* A modify operation just implements either an add operation, or a delete
    2804             :  * operation or both (replace) in turn.
    2805             :  * One difference between a modify and a pure add or a pure delete is that
    2806             :  * the object is not created a new or not completely removed, but the setup just
    2807             :  * treats it in the same way children objects are treated in a pure add or delete
    2808             :  * operation. A list of appropriate parents and objects to modify is built, then
    2809             :  * we jump directly in the add or delete code.
    2810             :  * If both add and delete are necessary, delete operations are performed first
    2811             :  * and then a followup add operation is concatenated
    2812             :  *
    2813             :  * Another difference is the ghost users. Because of its semi-managed nature,
    2814             :  * the ghost attribute requires some special care. During a modify operation, the
    2815             :  * ghost attribute can be set to a new list. That list coming, from an
    2816             :  * application, would typically only include the direct ghost
    2817             :  * members. However, we want to keep both direct and indirect ghost members
    2818             :  * in the cache to be able to return them all in a single call. To solve
    2819             :  * that problem, we also iterate over members of the group being modified,
    2820             :  * collect all ghost entries and add them back in case the original modify
    2821             :  * operation wiped them out.
    2822             :  */
    2823             : 
    2824             : static int mbof_mod_callback(struct ldb_request *req,
    2825             :                              struct ldb_reply *ares);
    2826             : static int mbof_collect_child_ghosts(struct mbof_mod_ctx *mod_ctx);
    2827             : static int mbof_get_ghost_from_parent(struct mbof_mod_del_op *igh);
    2828             : static int mbof_get_ghost_from_parent_cb(struct ldb_request *req,
    2829             :                                          struct ldb_reply *ares);
    2830             : static int mbof_orig_mod(struct mbof_mod_ctx *mod_ctx);
    2831             : static int mbof_orig_mod_callback(struct ldb_request *req,
    2832             :                                   struct ldb_reply *ares);
    2833             : static int mbof_inherited_mod(struct mbof_mod_ctx *mod_ctx);
    2834             : static int mbof_inherited_mod_callback(struct ldb_request *req,
    2835             :                                        struct ldb_reply *ares);
    2836             : static int mbof_mod_process(struct mbof_mod_ctx *mod_ctx, bool *done);
    2837             : static int mbof_mod_process_membel(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
    2838             :                                    struct ldb_message *entry,
    2839             :                                    const struct ldb_message_element *membel,
    2840             :                                    struct mbof_dn_array **_added,
    2841             :                                    struct mbof_dn_array **_removed);
    2842             : static int mbof_mod_process_ghel(TALLOC_CTX *mem_ctx,
    2843             :                                  struct ldb_message *entry,
    2844             :                                  const struct ldb_message_element *ghel,
    2845             :                                  const struct ldb_message_element *inherited,
    2846             :                                  struct mbof_val_array **_added,
    2847             :                                  struct mbof_val_array **_removed);
    2848             : static int mbof_mod_delete(struct mbof_mod_ctx *mod_ctx,
    2849             :                            struct mbof_dn_array *del,
    2850             :                            struct mbof_val_array *delgh);
    2851             : static int mbof_fill_dn_array(TALLOC_CTX *memctx,
    2852             :                               struct ldb_context *ldb,
    2853             :                               const struct ldb_message_element *el,
    2854             :                               struct mbof_dn_array **dn_array);
    2855             : static int mbof_fill_vals_array(TALLOC_CTX *memctx,
    2856             :                                 unsigned int num_values,
    2857             :                                 struct ldb_val *values,
    2858             :                                 struct mbof_val_array **val_array);
    2859             : static int mbof_fill_vals_array_el(TALLOC_CTX *memctx,
    2860             :                                    const struct ldb_message_element *el,
    2861             :                                    struct mbof_val_array **val_array);
    2862             : 
    2863         718 : static int memberof_mod(struct ldb_module *module, struct ldb_request *req)
    2864             : {
    2865             :     struct ldb_message_element *el;
    2866             :     struct mbof_mod_ctx *mod_ctx;
    2867             :     struct mbof_ctx *ctx;
    2868             :     static const char *attrs[] = { DB_OC, DB_GHOST,
    2869             :                                    DB_MEMBER, DB_MEMBEROF, NULL};
    2870         718 :     struct ldb_context *ldb = ldb_module_get_ctx(module);
    2871             :     struct ldb_request *search;
    2872             :     int ret;
    2873             : 
    2874         718 :     if (ldb_dn_is_special(req->op.mod.message->dn)) {
    2875             :         /* do not manipulate our control entries */
    2876           0 :         return ldb_next_request(module, req);
    2877             :     }
    2878             : 
    2879             :     /* check if memberof is specified */
    2880         718 :     el = ldb_msg_find_element(req->op.mod.message, DB_MEMBEROF);
    2881         718 :     if (el) {
    2882           0 :         ldb_debug(ldb, LDB_DEBUG_ERROR,
    2883             :                   "Error: the memberof attribute is readonly.");
    2884           0 :         return LDB_ERR_UNWILLING_TO_PERFORM;
    2885             :     }
    2886             : 
    2887             :     /* check if memberuid is specified */
    2888         718 :     el = ldb_msg_find_element(req->op.mod.message, DB_MEMBERUID);
    2889         718 :     if (el) {
    2890           0 :         ldb_debug(ldb, LDB_DEBUG_ERROR,
    2891             :                   "Error: the memberuid attribute is readonly.");
    2892           0 :         return LDB_ERR_UNWILLING_TO_PERFORM;
    2893             :     }
    2894             : 
    2895         718 :     ctx = mbof_init(module, req);
    2896         718 :     if (!ctx) {
    2897           0 :         return LDB_ERR_OPERATIONS_ERROR;
    2898             :     }
    2899             : 
    2900         718 :     mod_ctx = talloc_zero(ctx, struct mbof_mod_ctx);
    2901         718 :     if (!mod_ctx) {
    2902           0 :         talloc_free(ctx);
    2903           0 :         return LDB_ERR_OPERATIONS_ERROR;
    2904             :     }
    2905         718 :     mod_ctx->ctx = ctx;
    2906             : 
    2907         718 :     mod_ctx->msg = ldb_msg_copy(mod_ctx, req->op.mod.message);
    2908         718 :     if (!mod_ctx->msg) {
    2909           0 :         return LDB_ERR_OPERATIONS_ERROR;
    2910             :     }
    2911             : 
    2912         718 :     mod_ctx->membel = ldb_msg_find_element(mod_ctx->msg, DB_MEMBER);
    2913         718 :     mod_ctx->ghel = ldb_msg_find_element(mod_ctx->msg, DB_GHOST);
    2914             : 
    2915             :     /* continue with normal ops if there are no members and no ghosts */
    2916         718 :     if (mod_ctx->membel == NULL && mod_ctx->ghel == NULL) {
    2917         480 :         mod_ctx->terminate = true;
    2918         480 :         return mbof_orig_mod(mod_ctx);
    2919             :     }
    2920             : 
    2921             :     /* can't do anything,
    2922             :      * must check first what's on the entry */
    2923         238 :     ret = ldb_build_search_req(&search, ldb, mod_ctx,
    2924         238 :                                mod_ctx->msg->dn, LDB_SCOPE_BASE,
    2925             :                                NULL, attrs, NULL,
    2926             :                                mod_ctx, mbof_mod_callback,
    2927             :                                req);
    2928         238 :     if (ret != LDB_SUCCESS) {
    2929           0 :         talloc_free(ctx);
    2930           0 :         return ret;
    2931             :     }
    2932             : 
    2933         238 :     return ldb_request(ldb, search);
    2934             : }
    2935             : 
    2936             : 
    2937         476 : static int mbof_mod_callback(struct ldb_request *req,
    2938             :                              struct ldb_reply *ares)
    2939             : {
    2940             :     struct mbof_mod_ctx *mod_ctx;
    2941             :     struct ldb_context *ldb;
    2942             :     struct mbof_ctx *ctx;
    2943             :     int ret;
    2944             : 
    2945         476 :     mod_ctx = talloc_get_type(req->context, struct mbof_mod_ctx);
    2946         476 :     ctx = mod_ctx->ctx;
    2947         476 :     ldb = ldb_module_get_ctx(ctx->module);
    2948             : 
    2949         476 :     if (!ares) {
    2950           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    2951             :                                LDB_ERR_OPERATIONS_ERROR);
    2952             :     }
    2953         476 :     if (ares->error != LDB_SUCCESS) {
    2954           0 :         return ldb_module_done(ctx->req,
    2955             :                                ares->controls,
    2956             :                                ares->response,
    2957             :                                ares->error);
    2958             :     }
    2959             : 
    2960         476 :     switch (ares->type) {
    2961             :     case LDB_REPLY_ENTRY:
    2962         238 :         if (mod_ctx->entry != NULL) {
    2963           0 :             ldb_debug(ldb, LDB_DEBUG_TRACE,
    2964             :                            "Found multiple entries for (%s)",
    2965           0 :                            ldb_dn_get_linearized(mod_ctx->msg->dn));
    2966             :             /* more than one entry per dn ?? db corrupted ? */
    2967           0 :             return ldb_module_done(ctx->req, NULL, NULL,
    2968             :                                    LDB_ERR_OPERATIONS_ERROR);
    2969             :         }
    2970             : 
    2971         238 :         mod_ctx->entry = talloc_steal(mod_ctx, ares->message);
    2972         238 :         if (mod_ctx->entry == NULL) {
    2973           0 :             return ldb_module_done(ctx->req, NULL, NULL,
    2974             :                                    LDB_ERR_OPERATIONS_ERROR);
    2975             :         }
    2976         238 :         break;
    2977             :     case LDB_REPLY_REFERRAL:
    2978             :         /* ignore */
    2979           0 :         break;
    2980             : 
    2981             :     case LDB_REPLY_DONE:
    2982         238 :         if (mod_ctx->entry == NULL) {
    2983           0 :             ldb_debug(ldb, LDB_DEBUG_TRACE, "Entry not found (%s)",
    2984           0 :                            ldb_dn_get_linearized(mod_ctx->msg->dn));
    2985             :             /* this target does not exists, too bad! */
    2986           0 :             return ldb_module_done(ctx->req, NULL, NULL,
    2987             :                                    LDB_ERR_NO_SUCH_OBJECT);
    2988             :         }
    2989             : 
    2990         238 :         ret = mbof_collect_child_ghosts(mod_ctx);
    2991         238 :         if (ret != LDB_SUCCESS) {
    2992           0 :             talloc_zfree(ares);
    2993           0 :             return ldb_module_done(ctx->req, NULL, NULL, ret);
    2994             :         }
    2995             :     }
    2996             : 
    2997         476 :     talloc_zfree(ares);
    2998         476 :     return LDB_SUCCESS;
    2999             : }
    3000             : 
    3001         238 : static int mbof_collect_child_ghosts(struct mbof_mod_ctx *mod_ctx)
    3002             : {
    3003             :     int ret;
    3004             :     const struct ldb_message_element *member;
    3005             : 
    3006         238 :     member = ldb_msg_find_element(mod_ctx->entry, DB_MEMBER);
    3007             : 
    3008         353 :     if (member == NULL || member->num_values == 0 ||
    3009         185 :         mod_ctx->ghel == NULL || mod_ctx->ghel->flags != LDB_FLAG_MOD_REPLACE) {
    3010         212 :         ret = mbof_orig_mod(mod_ctx);
    3011         212 :         if (ret != LDB_SUCCESS) {
    3012           0 :             return ret;
    3013             :         }
    3014             : 
    3015         212 :         return LDB_SUCCESS;
    3016             :     }
    3017             : 
    3018          26 :     mod_ctx->igh = talloc_zero(mod_ctx, struct mbof_mod_del_op);
    3019          26 :     if (mod_ctx->igh == NULL) {
    3020           0 :         return LDB_ERR_OPERATIONS_ERROR;
    3021             :     }
    3022          26 :     mod_ctx->igh->mod_ctx = mod_ctx;
    3023             : 
    3024          26 :     ret = hash_create_ex(1024, &mod_ctx->igh->inherited_gh, 0, 0, 0, 0,
    3025             :                          hash_alloc, hash_free, mod_ctx, NULL, NULL);
    3026          26 :     if (ret != HASH_SUCCESS) {
    3027           0 :         return LDB_ERR_OPERATIONS_ERROR;
    3028             :     }
    3029             : 
    3030             : 
    3031          26 :     return mbof_get_ghost_from_parent(mod_ctx->igh);
    3032             : }
    3033             : 
    3034          26 : static int mbof_get_ghost_from_parent(struct mbof_mod_del_op *igh)
    3035             : {
    3036             :     struct ldb_request *search;
    3037             :     struct ldb_context *ldb;
    3038             :     struct mbof_ctx *ctx;
    3039             :     int ret;
    3040             :     static const char *attrs[] = { DB_GHOST, NULL };
    3041             :     char *expression;
    3042             :     char *clean_dn;
    3043             :     const char *dn;
    3044             : 
    3045          26 :     ctx = igh->mod_ctx->ctx;
    3046          26 :     ldb = ldb_module_get_ctx(ctx->module);
    3047             : 
    3048          26 :     dn = ldb_dn_get_linearized(igh->mod_ctx->entry->dn);
    3049          26 :     if (!dn) {
    3050           0 :         talloc_free(ctx);
    3051           0 :         return LDB_ERR_OPERATIONS_ERROR;
    3052             :     }
    3053             : 
    3054          26 :     ret = sss_filter_sanitize(igh, dn, &clean_dn);
    3055          26 :     if (ret != 0) {
    3056           0 :         return LDB_ERR_OPERATIONS_ERROR;
    3057             :     }
    3058             : 
    3059          26 :     expression = talloc_asprintf(igh,
    3060             :                                  "(&(%s=%s)(%s=%s))",
    3061             :                                  DB_OC, DB_GROUP_CLASS,
    3062             :                                  DB_MEMBEROF, clean_dn);
    3063          26 :     if (!expression) {
    3064           0 :         return LDB_ERR_OPERATIONS_ERROR;
    3065             :     }
    3066          26 :     talloc_zfree(clean_dn);
    3067             : 
    3068          26 :     ret = ldb_build_search_req(&search, ldb, igh,
    3069             :                                NULL,
    3070             :                                LDB_SCOPE_SUBTREE,
    3071             :                                expression, attrs, NULL,
    3072             :                                igh, mbof_get_ghost_from_parent_cb,
    3073             :                                ctx->req);
    3074          26 :     if (ret != LDB_SUCCESS) {
    3075           0 :         return ret;
    3076             :     }
    3077             : 
    3078          26 :     return ldb_request(ldb, search);
    3079             : }
    3080             : 
    3081         116 : static int mbof_get_ghost_from_parent_cb(struct ldb_request *req,
    3082             :                                          struct ldb_reply *ares)
    3083             : {
    3084             :     struct mbof_mod_del_op *igh;
    3085             :     struct mbof_ctx *ctx;
    3086             :     struct ldb_message_element *el;
    3087             :     struct ldb_val *dupval;
    3088             :     int ret;
    3089             :     hash_value_t value;
    3090             :     hash_key_t key;
    3091             :     int i;
    3092             : 
    3093         116 :     igh = talloc_get_type(req->context, struct mbof_mod_del_op);
    3094         116 :     ctx = igh->mod_ctx->ctx;
    3095             : 
    3096         116 :     if (!ares) {
    3097           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    3098             :                                LDB_ERR_OPERATIONS_ERROR);
    3099             :     }
    3100         116 :     if (ares->error != LDB_SUCCESS) {
    3101           0 :         return ldb_module_done(ctx->req,
    3102             :                                ares->controls,
    3103             :                                ares->response,
    3104             :                                ares->error);
    3105             :     }
    3106             : 
    3107         116 :     switch (ares->type) {
    3108             :     case LDB_REPLY_ENTRY:
    3109          90 :         el = ldb_msg_find_element(ares->message, DB_GHOST);
    3110          90 :         if (!el) {
    3111           0 :             break;
    3112             :         }
    3113             : 
    3114         585 :         for (i=0; i < el->num_values; i++) {
    3115         495 :             key.type = HASH_KEY_STRING;
    3116         495 :             key.str = (char *) el->values[i].data;
    3117             : 
    3118         495 :             if (hash_has_key(igh->inherited_gh, &key)) {
    3119             :                 /* We already have this user. Don't re-add him */
    3120         360 :                 continue;
    3121             :             }
    3122             : 
    3123         135 :             dupval = talloc_zero(igh->inherited_gh, struct ldb_val);
    3124         135 :             if (dupval == NULL) {
    3125           0 :                 return LDB_ERR_OPERATIONS_ERROR;
    3126             :             }
    3127             : 
    3128         135 :             *dupval = ldb_val_dup(igh->inherited_gh, &el->values[i]);
    3129         135 :             if (dupval->data == NULL) {
    3130           0 :                 return LDB_ERR_OPERATIONS_ERROR;
    3131             :             }
    3132             : 
    3133         135 :             value.type = HASH_VALUE_PTR;
    3134         135 :             value.ptr = dupval;
    3135             : 
    3136         135 :             ret = hash_enter(igh->inherited_gh, &key, &value);
    3137         135 :             if (ret != HASH_SUCCESS) {
    3138           0 :                 return LDB_ERR_OPERATIONS_ERROR;
    3139             :             }
    3140             :         }
    3141          90 :         break;
    3142             : 
    3143             :     case LDB_REPLY_REFERRAL:
    3144             :         /* ignore */
    3145           0 :         break;
    3146             : 
    3147             :     case LDB_REPLY_DONE:
    3148             :         /* All the children are gathered, let's do the real
    3149             :          * modify operation
    3150             :          */
    3151          26 :         ret = mbof_orig_mod(igh->mod_ctx);
    3152          26 :         if (ret != LDB_SUCCESS) {
    3153           0 :             talloc_zfree(ares);
    3154           0 :             return ldb_module_done(ctx->req, NULL, NULL, ret);
    3155             :         }
    3156          26 :         break;
    3157             :     }
    3158             : 
    3159         116 :     talloc_zfree(ares);
    3160         116 :     return LDB_SUCCESS;
    3161             : }
    3162             : 
    3163         718 : static int mbof_orig_mod(struct mbof_mod_ctx *mod_ctx)
    3164             : {
    3165             :     struct ldb_request *mod_req;
    3166             :     struct ldb_context *ldb;
    3167             :     struct mbof_ctx *ctx;
    3168             :     int ret;
    3169             : 
    3170         718 :     ctx = mod_ctx->ctx;
    3171         718 :     ldb = ldb_module_get_ctx(ctx->module);
    3172             : 
    3173        2154 :     ret = ldb_build_mod_req(&mod_req, ldb, ctx->req,
    3174        1436 :                             mod_ctx->msg, ctx->req->controls,
    3175             :                             mod_ctx, mbof_orig_mod_callback,
    3176             :                             ctx->req);
    3177         718 :     if (ret != LDB_SUCCESS) {
    3178           0 :         return ret;
    3179             :     }
    3180             : 
    3181         718 :     return ldb_next_request(ctx->module, mod_req);
    3182             : }
    3183             : 
    3184         718 : static int mbof_orig_mod_callback(struct ldb_request *req,
    3185             :                                   struct ldb_reply *ares)
    3186             : {
    3187             :     struct ldb_context *ldb;
    3188             :     struct mbof_mod_ctx *mod_ctx;
    3189             :     struct mbof_ctx *ctx;
    3190             :     int ret;
    3191             : 
    3192         718 :     mod_ctx = talloc_get_type(req->context, struct mbof_mod_ctx);
    3193         718 :     ctx = mod_ctx->ctx;
    3194         718 :     ldb = ldb_module_get_ctx(ctx->module);
    3195             : 
    3196         718 :     if (!ares) {
    3197           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    3198             :                                LDB_ERR_OPERATIONS_ERROR);
    3199             :     }
    3200         718 :     if (ares->error != LDB_SUCCESS) {
    3201           0 :         return ldb_module_done(ctx->req,
    3202             :                                ares->controls,
    3203             :                                ares->response,
    3204             :                                ares->error);
    3205             :     }
    3206             : 
    3207         718 :     if (ares->type != LDB_REPLY_DONE) {
    3208           0 :         talloc_zfree(ares);
    3209           0 :         ldb_debug(ldb, LDB_DEBUG_TRACE, "Invalid reply type!");
    3210           0 :         ldb_set_errstring(ldb, "Invalid reply type!");
    3211           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    3212             :                                LDB_ERR_OPERATIONS_ERROR);
    3213             :     }
    3214             : 
    3215             :     /* save real call stuff */
    3216         718 :     ctx->ret_ctrls = talloc_steal(ctx, ares->controls);
    3217         718 :     ctx->ret_resp = talloc_steal(ctx, ares->response);
    3218             : 
    3219         718 :     if (!mod_ctx->terminate) {
    3220             :         /* next step */
    3221         264 :         if (mod_ctx->igh && mod_ctx->igh->inherited_gh &&
    3222          26 :             hash_count(mod_ctx->igh->inherited_gh) > 0) {
    3223          18 :             ret = mbof_inherited_mod(mod_ctx);
    3224             :         } else {
    3225         220 :             ret = mbof_mod_process(mod_ctx, &mod_ctx->terminate);
    3226             :         }
    3227             : 
    3228         238 :         if (ret != LDB_SUCCESS) {
    3229           0 :             talloc_zfree(ares);
    3230           0 :             return ldb_module_done(ctx->req, NULL, NULL, ret);
    3231             :         }
    3232             :     }
    3233             : 
    3234         718 :     if (mod_ctx->terminate) {
    3235         499 :         talloc_zfree(ares);
    3236         499 :         return ldb_module_done(ctx->req,
    3237             :                                ctx->ret_ctrls,
    3238             :                                ctx->ret_resp,
    3239             :                                LDB_SUCCESS);
    3240             :     }
    3241             : 
    3242         219 :     talloc_zfree(ares);
    3243         219 :     return LDB_SUCCESS;
    3244             : }
    3245             : 
    3246          18 : static int mbof_inherited_mod(struct mbof_mod_ctx *mod_ctx)
    3247             : {
    3248             :     struct ldb_request *mod_req;
    3249             :     struct ldb_context *ldb;
    3250             :     struct mbof_ctx *ctx;
    3251             :     int ret;
    3252             :     struct ldb_message_element *el;
    3253             :     struct ldb_message *msg;
    3254             :     struct ldb_val *val;
    3255             :     struct ldb_val *dupval;
    3256             :     hash_value_t *values;
    3257             :     unsigned long num_values;
    3258             :     int i, j;
    3259             : 
    3260          18 :     ctx = mod_ctx->ctx;
    3261          18 :     ldb = ldb_module_get_ctx(ctx->module);
    3262             : 
    3263             :     /* add back the inherited children to entry */
    3264          18 :     msg = ldb_msg_new(mod_ctx);
    3265          18 :     if (!msg) return LDB_ERR_OPERATIONS_ERROR;
    3266             : 
    3267          18 :     msg->dn = mod_ctx->entry->dn;
    3268             : 
    3269             :     /* We only inherit during replaces, so it's safe to only look
    3270             :      * at the replaced set
    3271             :      */
    3272          18 :     ret = ldb_msg_add_empty(msg, DB_GHOST, LDB_FLAG_MOD_ADD, &el);
    3273          18 :     if (ret != LDB_SUCCESS) {
    3274           0 :         return ret;
    3275             :     }
    3276             : 
    3277          18 :     ret = hash_values(mod_ctx->igh->inherited_gh, &num_values, &values);
    3278          18 :     if (ret != HASH_SUCCESS) {
    3279           0 :         return LDB_ERR_OPERATIONS_ERROR;
    3280             :     }
    3281             : 
    3282          18 :     el->values = talloc_array(msg, struct ldb_val, num_values);
    3283          18 :     if (!el->values) {
    3284           0 :         return LDB_ERR_OPERATIONS_ERROR;
    3285             :     }
    3286             : 
    3287         153 :     for (i = 0, j = 0; i < num_values; i++) {
    3288         135 :         val = talloc_get_type(values[i].ptr, struct ldb_val);
    3289             : 
    3290         135 :         dupval = ldb_msg_find_val(mod_ctx->ghel, val);
    3291         135 :         if (dupval) {
    3292           0 :             continue;
    3293             :         }
    3294             : 
    3295         135 :         el->values[j].length = strlen((const char *) val->data);
    3296         270 :         el->values[j].data = (uint8_t *) talloc_strdup(el->values,
    3297         135 :                                                     (const char *) val->data);
    3298         135 :         if (!el->values[j].data) {
    3299           0 :             return LDB_ERR_OPERATIONS_ERROR;
    3300             :         }
    3301         135 :         j++;
    3302             :     }
    3303          18 :     el->num_values = j;
    3304             : 
    3305          18 :     if (el->num_values == 0) {
    3306             :         /* nothing to do */
    3307             :         /* We cannot modify element which has 0 values */
    3308           0 :         msg->num_elements = 0;
    3309             :     }
    3310             : 
    3311          18 :     mod_ctx->igh->mod_msg = msg;
    3312          18 :     mod_ctx->igh->el = el;
    3313             : 
    3314          36 :     ret = ldb_build_mod_req(&mod_req, ldb, ctx->req,
    3315          18 :                             msg, ctx->req->controls,
    3316             :                             mod_ctx, mbof_inherited_mod_callback,
    3317             :                             ctx->req);
    3318          18 :     if (ret != LDB_SUCCESS) {
    3319           0 :         return ret;
    3320             :     }
    3321             : 
    3322          18 :     return ldb_next_request(ctx->module, mod_req);
    3323             : }
    3324             : 
    3325          18 : static int mbof_inherited_mod_callback(struct ldb_request *req,
    3326             :                                        struct ldb_reply *ares)
    3327             : {
    3328             :     struct ldb_context *ldb;
    3329             :     struct mbof_mod_ctx *mod_ctx;
    3330             :     struct mbof_ctx *ctx;
    3331             :     int ret;
    3332             : 
    3333          18 :     mod_ctx = talloc_get_type(req->context, struct mbof_mod_ctx);
    3334          18 :     ctx = mod_ctx->ctx;
    3335          18 :     ldb = ldb_module_get_ctx(ctx->module);
    3336             : 
    3337          18 :     if (!ares) {
    3338           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    3339             :                                LDB_ERR_OPERATIONS_ERROR);
    3340             :     }
    3341          18 :     if (ares->error != LDB_SUCCESS) {
    3342           0 :         return ldb_module_done(ctx->req,
    3343             :                                ares->controls,
    3344             :                                ares->response,
    3345             :                                ares->error);
    3346             :     }
    3347             : 
    3348          18 :     if (ares->type != LDB_REPLY_DONE) {
    3349           0 :         talloc_zfree(ares);
    3350           0 :         ldb_debug(ldb, LDB_DEBUG_TRACE, "Invalid reply type!");
    3351           0 :         ldb_set_errstring(ldb, "Invalid reply type!");
    3352           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    3353             :                                LDB_ERR_OPERATIONS_ERROR);
    3354             :     }
    3355             : 
    3356          18 :     ret = mbof_mod_process(mod_ctx, &mod_ctx->terminate);
    3357          18 :     if (ret != LDB_SUCCESS) {
    3358           0 :         talloc_zfree(ares);
    3359           0 :         return ldb_module_done(ctx->req, NULL, NULL, ret);
    3360             :     }
    3361             : 
    3362          18 :     if (mod_ctx->terminate) {
    3363           2 :         talloc_zfree(ares);
    3364           2 :         return ldb_module_done(ctx->req,
    3365             :                                ctx->ret_ctrls,
    3366             :                                ctx->ret_resp,
    3367             :                                LDB_SUCCESS);
    3368             :     }
    3369             : 
    3370          16 :     talloc_zfree(ares);
    3371          16 :     return LDB_SUCCESS;
    3372             : }
    3373             : 
    3374         238 : static int mbof_mod_process(struct mbof_mod_ctx *mod_ctx, bool *done)
    3375             : {
    3376             :     struct ldb_context *ldb;
    3377             :     struct mbof_ctx *ctx;
    3378             :     int ret;
    3379             : 
    3380         238 :     ctx = mod_ctx->ctx;
    3381         238 :     ldb = ldb_module_get_ctx(ctx->module);
    3382             : 
    3383         238 :     ret = mbof_mod_process_membel(mod_ctx, ldb, mod_ctx->entry, mod_ctx->membel,
    3384             :                                   &mod_ctx->mb_add, &mod_ctx->mb_remove);
    3385         238 :     if (ret != LDB_SUCCESS) {
    3386           0 :         return ret;
    3387             :     }
    3388             : 
    3389         502 :     ret = mbof_mod_process_ghel(mod_ctx, mod_ctx->entry, mod_ctx->ghel,
    3390         264 :                                 mod_ctx->igh ? mod_ctx->igh->el : NULL,
    3391             :                                 &mod_ctx->gh_add, &mod_ctx->gh_remove);
    3392         238 :     if (ret != LDB_SUCCESS) {
    3393           0 :         return ret;
    3394             :     }
    3395             : 
    3396             :     /* Process the operations */
    3397             :     /* if we have something to remove do it first */
    3398         463 :     if ((mod_ctx->mb_remove && mod_ctx->mb_remove->num) ||
    3399         252 :         (mod_ctx->gh_remove && mod_ctx->gh_remove->num)) {
    3400          40 :         return mbof_mod_delete(mod_ctx, mod_ctx->mb_remove, mod_ctx->gh_remove);
    3401             :     }
    3402             : 
    3403             :     /* if there is nothing to remove and we have stuff to add
    3404             :      * do it right away */
    3405         228 :     if ((mod_ctx->mb_add && mod_ctx->mb_add->num) ||
    3406          39 :         (mod_ctx->gh_add && mod_ctx->gh_add->num)) {
    3407         177 :         return mbof_mod_add(mod_ctx, mod_ctx->mb_add, mod_ctx->gh_add);
    3408             :     }
    3409             : 
    3410             :     /* the replacement function resulted in a null op,
    3411             :      * nothing to do, return happily */
    3412          21 :     *done = true;
    3413          21 :     return LDB_SUCCESS;
    3414             : }
    3415             : 
    3416         238 : static int mbof_mod_process_membel(TALLOC_CTX *mem_ctx,
    3417             :                                    struct ldb_context *ldb,
    3418             :                                    struct ldb_message *entry,
    3419             :                                    const struct ldb_message_element *membel,
    3420             :                                    struct mbof_dn_array **_added,
    3421             :                                    struct mbof_dn_array **_removed)
    3422             : {
    3423             :     const struct ldb_message_element *el;
    3424         238 :     struct mbof_dn_array *removed = NULL;
    3425         238 :     struct mbof_dn_array *added = NULL;
    3426             :     int i, j, ret;
    3427             : 
    3428         238 :     if (!membel) {
    3429             :         /* Nothing to do.. */
    3430          57 :         return LDB_SUCCESS;
    3431             :     }
    3432             : 
    3433         181 :     switch (membel->flags) {
    3434             :     case LDB_FLAG_MOD_ADD:
    3435             : 
    3436         100 :         ret = mbof_fill_dn_array(mem_ctx, ldb, membel, &added);
    3437         100 :         if (ret != LDB_SUCCESS) {
    3438           0 :             return ret;
    3439             :         }
    3440         100 :         break;
    3441             : 
    3442             :     case LDB_FLAG_MOD_DELETE:
    3443             : 
    3444          13 :         if (membel->num_values == 0) {
    3445           0 :             el = ldb_msg_find_element(entry, DB_MEMBER);
    3446             :         } else {
    3447          13 :             el = membel;
    3448             :         }
    3449             : 
    3450          13 :         if (!el) {
    3451             :             /* nothing to do really */
    3452           0 :             break;
    3453             :         }
    3454             : 
    3455          13 :         ret = mbof_fill_dn_array(mem_ctx, ldb, el, &removed);
    3456          13 :         if (ret != LDB_SUCCESS) {
    3457           0 :             return ret;
    3458             :         }
    3459          13 :         break;
    3460             : 
    3461             :     case LDB_FLAG_MOD_REPLACE:
    3462             : 
    3463          68 :         removed = NULL;
    3464          68 :         el = ldb_msg_find_element(entry, DB_MEMBER);
    3465          68 :         if (el) {
    3466           0 :             ret = mbof_fill_dn_array(mem_ctx, ldb, el, &removed);
    3467           0 :             if (ret != LDB_SUCCESS) {
    3468           0 :                 return ret;
    3469             :             }
    3470             :         }
    3471             : 
    3472          68 :         added = NULL;
    3473          68 :         el = membel;
    3474          68 :         if (el) {
    3475          68 :             ret = mbof_fill_dn_array(mem_ctx, ldb, el, &added);
    3476          68 :             if (ret != LDB_SUCCESS) {
    3477           0 :                 talloc_free(removed);
    3478           0 :                 return ret;
    3479             :             }
    3480             :         }
    3481             : 
    3482             :         /* remove from arrays values that ended up unchanged */
    3483          68 :         if (removed && removed->num && added && added->num) {
    3484           0 :             for (i = 0; i < added->num; i++) {
    3485           0 :                 for (j = 0; j < removed->num; j++) {
    3486           0 :                     if (ldb_dn_compare(added->dns[i], removed->dns[j]) == 0) {
    3487           0 :                         break;
    3488             :                     }
    3489             :                 }
    3490           0 :                 if (j < removed->num) {
    3491             :                     /* preexisting one, not removed, nor added */
    3492           0 :                     for (; j+1 < removed->num; j++) {
    3493           0 :                         removed->dns[j] = removed->dns[j+1];
    3494             :                     }
    3495           0 :                     removed->num--;
    3496           0 :                     for (j = i; j+1 < added->num; j++) {
    3497           0 :                         added->dns[j] = added->dns[j+1];
    3498             :                     }
    3499           0 :                     added->num--;
    3500           0 :                     i--;
    3501             :                 }
    3502             :             }
    3503             :         }
    3504          68 :         break;
    3505             : 
    3506             :     default:
    3507           0 :         return LDB_ERR_OPERATIONS_ERROR;
    3508             :     }
    3509             : 
    3510         181 :     *_added = added;
    3511         181 :     *_removed = removed;
    3512         181 :     return LDB_SUCCESS;
    3513             : }
    3514             : 
    3515         238 : static int mbof_mod_process_ghel(TALLOC_CTX *mem_ctx,
    3516             :                                  struct ldb_message *entry,
    3517             :                                  const struct ldb_message_element *ghel,
    3518             :                                  const struct ldb_message_element *inherited,
    3519             :                                  struct mbof_val_array **_added,
    3520             :                                  struct mbof_val_array **_removed)
    3521             : {
    3522             :     const struct ldb_message_element *el;
    3523         238 :     struct mbof_val_array *removed = NULL;
    3524         238 :     struct mbof_val_array *added = NULL;
    3525             :     int i, j, ret;
    3526             : 
    3527         238 :     if (!ghel) {
    3528             :         /* Nothing to do.. */
    3529         106 :         return LDB_SUCCESS;
    3530             :     }
    3531             : 
    3532         132 :     el = ldb_msg_find_element(entry, DB_MEMBEROF);
    3533         132 :     if (!el || el->num_values == 0) {
    3534             :         /* no memberof attributes ... */
    3535          96 :         return LDB_SUCCESS;
    3536             :     }
    3537             : 
    3538          36 :     switch (ghel->flags) {
    3539             :     case LDB_FLAG_MOD_ADD:
    3540           9 :         ret = mbof_fill_vals_array_el(mem_ctx, ghel, &added);
    3541           9 :         if (ret != LDB_SUCCESS) {
    3542           0 :             return ret;
    3543             :         }
    3544           9 :         break;
    3545             : 
    3546             :     case LDB_FLAG_MOD_DELETE:
    3547           9 :         if (ghel->num_values == 0) {
    3548           0 :             el = ldb_msg_find_element(entry, DB_GHOST);
    3549             :         } else {
    3550           9 :             el = ghel;
    3551             :         }
    3552             : 
    3553           9 :         if (!el) {
    3554             :             /* nothing to do really */
    3555           0 :             break;
    3556             :         }
    3557             : 
    3558           9 :         ret = mbof_fill_vals_array_el(mem_ctx, ghel, &removed);
    3559           9 :         if (ret != LDB_SUCCESS) {
    3560           0 :             return ret;
    3561             :         }
    3562           9 :         break;
    3563             : 
    3564             :     case LDB_FLAG_MOD_REPLACE:
    3565          18 :         el = ldb_msg_find_element(entry, DB_GHOST);
    3566          18 :         if (el) {
    3567          18 :             ret = mbof_fill_vals_array_el(mem_ctx, el, &removed);
    3568          18 :             if (ret != LDB_SUCCESS) {
    3569           0 :                 return ret;
    3570             :             }
    3571             :         }
    3572             : 
    3573          18 :         el = ghel;
    3574          18 :         if (el) {
    3575          18 :             ret = mbof_fill_vals_array_el(mem_ctx, el, &added);
    3576          18 :             if (ret != LDB_SUCCESS) {
    3577           0 :                 talloc_free(removed);
    3578           0 :                 return ret;
    3579             :             }
    3580             :         }
    3581             : 
    3582          18 :         if (inherited) {
    3583          16 :             ret = mbof_fill_vals_array_el(mem_ctx, inherited, &added);
    3584          16 :             if (ret != LDB_SUCCESS) {
    3585           0 :                 talloc_free(added);
    3586           0 :                 talloc_free(removed);
    3587           0 :                 return ret;
    3588             :             }
    3589             :         }
    3590             : 
    3591             :         /* remove from arrays values that ended up unchanged */
    3592          18 :         if (removed && removed->num && added && added->num) {
    3593         144 :             for (i = 0; i < added->num; i++) {
    3594         481 :                 for (j = 0; j < removed->num; j++) {
    3595         472 :                     if (strcmp((const char *) added->vals[i].data,
    3596         472 :                                (const char *) removed->vals[j].data) == 0) {
    3597         117 :                         break;
    3598             :                     }
    3599             :                 }
    3600         126 :                 if (j < removed->num) {
    3601             :                     /* preexisting one, not removed, nor added */
    3602         452 :                     for (; j+1 < removed->num; j++) {
    3603         335 :                         removed->vals[j] = removed->vals[j+1];
    3604             :                     }
    3605         117 :                     removed->num--;
    3606         645 :                     for (j = i; j+1 < added->num; j++) {
    3607         528 :                         added->vals[j] = added->vals[j+1];
    3608             :                     }
    3609         117 :                     added->num--;
    3610         117 :                     i--;
    3611             :                 }
    3612             :             }
    3613             :         }
    3614          18 :         break;
    3615             : 
    3616             :     default:
    3617           0 :         return LDB_ERR_OPERATIONS_ERROR;
    3618             :     }
    3619             : 
    3620          36 :     *_added = added;
    3621          36 :     *_removed = removed;
    3622          36 :     return LDB_SUCCESS;
    3623             : }
    3624             : 
    3625         186 : static int mbof_mod_add(struct mbof_mod_ctx *mod_ctx,
    3626             :                         struct mbof_dn_array *ael,
    3627             :                         struct mbof_val_array *addgh)
    3628             : {
    3629             :     const struct ldb_message_element *el;
    3630             :     struct mbof_dn_array *parents;
    3631             :     struct mbof_add_ctx *add_ctx;
    3632             :     struct ldb_context *ldb;
    3633             :     struct mbof_ctx *ctx;
    3634             :     int i, ret;
    3635             : 
    3636         186 :     ctx = mod_ctx->ctx;
    3637         186 :     ldb = ldb_module_get_ctx(ctx->module);
    3638             : 
    3639         186 :     el = ldb_msg_find_element(mod_ctx->entry, DB_MEMBEROF);
    3640             : 
    3641             :     /* all the parents + itself */
    3642         186 :     ret = mbof_fill_dn_array(mod_ctx, ldb, el, &parents);
    3643         186 :     if (ret != LDB_SUCCESS) {
    3644           0 :         return ret;
    3645             :     }
    3646             : 
    3647         186 :     add_ctx = talloc_zero(mod_ctx, struct mbof_add_ctx);
    3648         186 :     if (!add_ctx) {
    3649           0 :         return LDB_ERR_OPERATIONS_ERROR;
    3650             :     }
    3651         186 :     add_ctx->ctx = ctx;
    3652         186 :     add_ctx->msg_dn = mod_ctx->msg->dn;
    3653             : 
    3654         186 :     if (addgh != NULL) {
    3655             :         /* Build the memberuid add op */
    3656          18 :         ret =  mbof_add_fill_ghop_ex(add_ctx, mod_ctx->entry,
    3657          18 :                                      parents, addgh->vals, addgh->num);
    3658          18 :         if (ret != LDB_SUCCESS) {
    3659           0 :             return ret;
    3660             :         }
    3661             :     }
    3662             : 
    3663         186 :     if (ael != NULL && ael->num > 0) {
    3664             :         /* Add itself to the list of the parents to also get the memberuid */
    3665         168 :         parents->dns = talloc_realloc(parents, parents->dns,
    3666             :                                     struct ldb_dn *, parents->num + 1);
    3667         168 :         if (!parents->dns) {
    3668           0 :             return LDB_ERR_OPERATIONS_ERROR;
    3669             :         }
    3670         168 :         parents->dns[parents->num] = mod_ctx->entry->dn;
    3671         168 :         parents->num++;
    3672             : 
    3673             :         /* Build the member-add array */
    3674         336 :         for (i = 0; i < ael->num; i++) {
    3675         168 :             ret = mbof_append_addop(add_ctx, parents, ael->dns[i]);
    3676         168 :             if (ret != LDB_SUCCESS) {
    3677           0 :                 return ret;
    3678             :             }
    3679             :         }
    3680             : 
    3681         168 :         return mbof_next_add(add_ctx->add_list);
    3682             :     }
    3683             : 
    3684          18 :     return mbof_add_muop(add_ctx);
    3685             : }
    3686             : 
    3687          40 : static int mbof_mod_delete(struct mbof_mod_ctx *mod_ctx,
    3688             :                            struct mbof_dn_array *del,
    3689             :                            struct mbof_val_array *delgh)
    3690             : {
    3691             :     struct mbof_del_operation *first;
    3692             :     struct mbof_del_ctx *del_ctx;
    3693             :     struct mbof_ctx *ctx;
    3694             :     int i, ret;
    3695             : 
    3696          40 :     ctx = mod_ctx->ctx;
    3697             : 
    3698          40 :     del_ctx = talloc_zero(mod_ctx, struct mbof_del_ctx);
    3699          40 :     if (!del_ctx) {
    3700           0 :         return LDB_ERR_OPERATIONS_ERROR;
    3701             :     }
    3702          40 :     del_ctx->ctx = ctx;
    3703          40 :     del_ctx->is_mod = true;
    3704             : 
    3705             :     /* create first entry */
    3706             :     /* the first entry is the parent of all entries and the one where we
    3707             :      * remove member from, it does not get the same treatment as others */
    3708          40 :     first = talloc_zero(del_ctx, struct mbof_del_operation);
    3709          40 :     if (!first) {
    3710           0 :         return LDB_ERR_OPERATIONS_ERROR;
    3711             :     }
    3712          40 :     del_ctx->first = first;
    3713             : 
    3714             :     /* add followup function if we also have stuff to add */
    3715          80 :     if ((mod_ctx->mb_add && mod_ctx->mb_add->num > 0) ||
    3716          58 :         (mod_ctx->gh_add && mod_ctx->gh_add->num > 0)) {
    3717           9 :         del_ctx->follow_mod = mod_ctx;
    3718             :     }
    3719             : 
    3720          40 :     first->del_ctx = del_ctx;
    3721          40 :     first->entry = mod_ctx->entry;
    3722          40 :     first->entry_dn = mod_ctx->entry->dn;
    3723             : 
    3724          40 :     if (delgh != NULL) {
    3725          27 :         ret = mbof_del_fill_ghop_ex(del_ctx, del_ctx->first->entry,
    3726          27 :                                     delgh->vals, delgh->num);
    3727          27 :         if (ret != LDB_SUCCESS) {
    3728           0 :             return ret;
    3729             :         }
    3730             :     }
    3731             : 
    3732             :     /* prepare del sets */
    3733          40 :     if (del != NULL && del->num > 0) {
    3734          26 :         for (i = 0; i < del->num; i++) {
    3735          13 :             ret = mbof_append_delop(first, del->dns[i]);
    3736          13 :             if (ret != LDB_SUCCESS) {
    3737           0 :                 return ret;
    3738             :             }
    3739             :         }
    3740             : 
    3741             :         /* now that sets are built, start processing */
    3742          13 :         return mbof_del_execute_op(first->children[0]);
    3743             :     }
    3744             : 
    3745             :     /* No member processing, just delete ghosts */
    3746          27 :     return mbof_del_ghop(del_ctx);
    3747             : }
    3748             : 
    3749         367 : static int mbof_fill_dn_array(TALLOC_CTX *memctx,
    3750             :                               struct ldb_context *ldb,
    3751             :                               const struct ldb_message_element *el,
    3752             :                               struct mbof_dn_array **dn_array)
    3753             : {
    3754             :     struct mbof_dn_array *ar;
    3755             :     struct ldb_dn *valdn;
    3756             :     int i;
    3757             : 
    3758         367 :     ar = talloc_zero(memctx, struct mbof_dn_array);
    3759         367 :     if (!ar) {
    3760           0 :         return LDB_ERR_OPERATIONS_ERROR;
    3761             :     }
    3762         367 :     *dn_array = ar;
    3763             : 
    3764         367 :     if (!el || el->num_values == 0) {
    3765         148 :         return LDB_SUCCESS;
    3766             :     }
    3767             : 
    3768         219 :     ar->dns = talloc_array(ar, struct ldb_dn *, el->num_values);
    3769         219 :     if (!ar->dns) {
    3770           0 :         return LDB_ERR_OPERATIONS_ERROR;
    3771             :     }
    3772         219 :     ar->num = el->num_values;
    3773             : 
    3774         634 :     for (i = 0; i < ar->num; i++) {
    3775         415 :         valdn = ldb_dn_from_ldb_val(ar, ldb, &el->values[i]);
    3776         415 :         if (!valdn || !ldb_dn_validate(valdn)) {
    3777           0 :             ldb_debug(ldb, LDB_DEBUG_TRACE, "Invalid dn value: [%s]",
    3778           0 :                                             (const char *)el->values[i].data);
    3779           0 :             return LDB_ERR_INVALID_DN_SYNTAX;
    3780             :         }
    3781         415 :         ar->dns[i] = valdn;
    3782             :     }
    3783             : 
    3784         219 :     return LDB_SUCCESS;
    3785             : }
    3786             : 
    3787          70 : static int mbof_fill_vals_array(TALLOC_CTX *memctx,
    3788             :                                 unsigned int num_values,
    3789             :                                 struct ldb_val *values,
    3790             :                                 struct mbof_val_array **val_array)
    3791             : {
    3792          70 :     struct mbof_val_array *var = *val_array;
    3793             :     int i, vi;
    3794             : 
    3795          70 :     if (var == NULL) {
    3796          54 :         var = talloc_zero(memctx, struct mbof_val_array);
    3797          54 :         if (!var) {
    3798           0 :             return LDB_ERR_OPERATIONS_ERROR;
    3799             :         }
    3800          54 :         *val_array = var;
    3801             :     }
    3802             : 
    3803          70 :     if (values == NULL || num_values == 0) {
    3804           0 :         return LDB_SUCCESS;
    3805             :     }
    3806             : 
    3807             :     /* We do not care about duplicate values now.
    3808             :      * They will be filtered later */
    3809          70 :     vi = var->num;
    3810          70 :     var->num += num_values;
    3811          70 :     var->vals = talloc_realloc(memctx, var->vals, struct ldb_val, var->num);
    3812          70 :     if (!var->vals) {
    3813           0 :         return LDB_ERR_OPERATIONS_ERROR;
    3814             :     }
    3815             : 
    3816             :     /* FIXME - use ldb_val_dup() */
    3817         349 :     for (i = 0; i < num_values; i++) {
    3818         279 :         var->vals[vi].length = strlen((const char *) values[i].data);
    3819         558 :         var->vals[vi].data = (uint8_t *) talloc_strdup(var,
    3820         279 :                                           (const char *) values[i].data);
    3821         279 :         if (var->vals[vi].data == NULL) {
    3822           0 :             return LDB_ERR_OPERATIONS_ERROR;
    3823             :         }
    3824         279 :         vi++;
    3825             :     }
    3826             : 
    3827          70 :     return LDB_SUCCESS;
    3828             : }
    3829             : 
    3830          70 : static int mbof_fill_vals_array_el(TALLOC_CTX *memctx,
    3831             :                                    const struct ldb_message_element *el,
    3832             :                                    struct mbof_val_array **val_array)
    3833             : {
    3834          70 :     if (el == NULL) {
    3835           0 :         return LDB_SUCCESS;
    3836             :     }
    3837             : 
    3838          70 :     return mbof_fill_vals_array(memctx, el->num_values, el->values,
    3839             :                                 val_array);
    3840             : }
    3841             : 
    3842             : /*************************
    3843             :  * Cleanup task routines *
    3844             :  *************************/
    3845             : 
    3846             : struct mbof_member {
    3847             :     struct mbof_member *prev;
    3848             :     struct mbof_member *next;
    3849             : 
    3850             :     struct ldb_dn *dn;
    3851             :     const char *name;
    3852             :     bool orig_has_memberof;
    3853             :     bool orig_has_memberuid;
    3854             :     struct ldb_message_element *orig_members;
    3855             : 
    3856             :     struct mbof_member **members;
    3857             : 
    3858             :     hash_table_t *memberofs;
    3859             : 
    3860             :     struct ldb_message_element *memuids;
    3861             : 
    3862             :     enum { MBOF_GROUP_TO_DO = 0,
    3863             :            MBOF_GROUP_DONE,
    3864             :            MBOF_USER,
    3865             :            MBOF_ITER_ERROR } status;
    3866             : };
    3867             : 
    3868             : struct mbof_rcmp_context {
    3869             :     struct ldb_module *module;
    3870             :     struct ldb_request *req;
    3871             : 
    3872             :     struct mbof_member *user_list;
    3873             :     hash_table_t *user_table;
    3874             : 
    3875             :     struct mbof_member *group_list;
    3876             :     hash_table_t *group_table;
    3877             : };
    3878             : 
    3879           0 : static int mbof_steal_msg_el(TALLOC_CTX *memctx,
    3880             :                              const char *name,
    3881             :                              struct ldb_message *msg,
    3882             :                              struct ldb_message_element **_dest)
    3883             : {
    3884             :     struct ldb_message_element *src;
    3885             :     struct ldb_message_element *dest;
    3886             : 
    3887           0 :     src = ldb_msg_find_element(msg, name);
    3888           0 :     if (!src) {
    3889           0 :         return LDB_ERR_NO_SUCH_ATTRIBUTE;
    3890             :     }
    3891             : 
    3892           0 :     dest = talloc_zero(memctx, struct ldb_message_element);
    3893           0 :     if (!dest) {
    3894           0 :         return LDB_ERR_OPERATIONS_ERROR;
    3895             :     }
    3896             : 
    3897           0 :     *dest = *src;
    3898           0 :     talloc_steal(dest, dest->name);
    3899           0 :     talloc_steal(dest, dest->values);
    3900             : 
    3901           0 :     *_dest = dest;
    3902           0 :     return LDB_SUCCESS;
    3903             : }
    3904             : 
    3905             : static int mbof_rcmp_usr_callback(struct ldb_request *req,
    3906             :                                   struct ldb_reply *ares);
    3907             : static int mbof_rcmp_search_groups(struct mbof_rcmp_context *ctx);
    3908             : static int mbof_rcmp_grp_callback(struct ldb_request *req,
    3909             :                                   struct ldb_reply *ares);
    3910             : static int mbof_member_update(struct mbof_rcmp_context *ctx,
    3911             :                               struct mbof_member *parent,
    3912             :                               struct mbof_member *mem);
    3913             : static bool mbof_member_iter(hash_entry_t *item, void *user_data);
    3914             : static int mbof_add_memuid(struct mbof_member *grp, const char *user);
    3915             : static int mbof_rcmp_update(struct mbof_rcmp_context *ctx);
    3916             : static int mbof_rcmp_mod_callback(struct ldb_request *req,
    3917             :                                   struct ldb_reply *ares);
    3918             : 
    3919           0 : static int memberof_recompute_task(struct ldb_module *module,
    3920             :                                    struct ldb_request *req)
    3921             : {
    3922           0 :     struct ldb_context *ldb = ldb_module_get_ctx(module);
    3923             :     static const char *attrs[] = { DB_NAME, DB_MEMBEROF, NULL };
    3924             :     static const char *filter = "(objectclass=user)";
    3925             :     struct mbof_rcmp_context *ctx;
    3926             :     struct ldb_request *src_req;
    3927             :     int ret;
    3928             : 
    3929           0 :     ctx = talloc_zero(req, struct mbof_rcmp_context);
    3930           0 :     if (!ctx) {
    3931           0 :         return LDB_ERR_OPERATIONS_ERROR;
    3932             :     }
    3933           0 :     ctx->module = module;
    3934           0 :     ctx->req = req;
    3935             : 
    3936           0 :     ret = hash_create_ex(1024, &ctx->user_table, 0, 0, 0, 0,
    3937             :                          hash_alloc, hash_free, ctx, NULL, NULL);
    3938           0 :     if (ret != HASH_SUCCESS) {
    3939           0 :         return LDB_ERR_OPERATIONS_ERROR;
    3940             :     }
    3941             : 
    3942           0 :     ret = ldb_build_search_req(&src_req, ldb, ctx,
    3943             :                                NULL, LDB_SCOPE_SUBTREE,
    3944             :                                filter, attrs, NULL,
    3945             :                                ctx, mbof_rcmp_usr_callback, ctx->req);
    3946           0 :     if (ret != LDB_SUCCESS) {
    3947           0 :         return ret;
    3948             :     }
    3949             : 
    3950           0 :     return ldb_request(ldb, src_req);
    3951             : }
    3952             : 
    3953           0 : static int mbof_rcmp_usr_callback(struct ldb_request *req,
    3954             :                                   struct ldb_reply *ares)
    3955             : {
    3956             :     struct mbof_rcmp_context *ctx;
    3957             :     struct mbof_member *usr;
    3958             :     hash_value_t value;
    3959             :     hash_key_t key;
    3960             :     const char *name;
    3961             :     int ret;
    3962             : 
    3963           0 :     ctx = talloc_get_type(req->context, struct mbof_rcmp_context);
    3964             : 
    3965           0 :     if (!ares) {
    3966           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    3967             :                                LDB_ERR_OPERATIONS_ERROR);
    3968             :     }
    3969           0 :     if (ares->error != LDB_SUCCESS) {
    3970           0 :         return ldb_module_done(ctx->req,
    3971             :                                ares->controls,
    3972             :                                ares->response,
    3973             :                                ares->error);
    3974             :     }
    3975             : 
    3976           0 :     switch (ares->type) {
    3977             :     case LDB_REPLY_ENTRY:
    3978             : 
    3979           0 :         usr = talloc_zero(ctx, struct mbof_member);
    3980           0 :         if (!usr) {
    3981           0 :             return ldb_module_done(ctx->req, NULL, NULL,
    3982             :                                    LDB_ERR_OPERATIONS_ERROR);
    3983             :         }
    3984             : 
    3985           0 :         usr->status = MBOF_USER;
    3986           0 :         usr->dn = talloc_steal(usr, ares->message->dn);
    3987           0 :         name = ldb_msg_find_attr_as_string(ares->message, DB_NAME, NULL);
    3988           0 :         if (name) {
    3989           0 :             usr->name = talloc_steal(usr, name);
    3990             :         }
    3991             : 
    3992           0 :         if (ldb_msg_find_element(ares->message, DB_MEMBEROF)) {
    3993           0 :             usr->orig_has_memberof = true;
    3994             :         }
    3995             : 
    3996           0 :         DLIST_ADD(ctx->user_list, usr);
    3997             : 
    3998           0 :         key.type = HASH_KEY_STRING;
    3999           0 :         key.str = discard_const(ldb_dn_get_linearized(usr->dn));
    4000           0 :         value.type = HASH_VALUE_PTR;
    4001           0 :         value.ptr = usr;
    4002             : 
    4003           0 :         ret = hash_enter(ctx->user_table, &key, &value);
    4004           0 :         if (ret != HASH_SUCCESS) {
    4005           0 :             return ldb_module_done(ctx->req, NULL, NULL,
    4006             :                                    LDB_ERR_OPERATIONS_ERROR);
    4007             :         }
    4008             : 
    4009           0 :         break;
    4010             : 
    4011             :     case LDB_REPLY_REFERRAL:
    4012             :         /* ignore */
    4013           0 :         break;
    4014             : 
    4015             :     case LDB_REPLY_DONE:
    4016           0 :         talloc_zfree(ares);
    4017             : 
    4018             :         /* and now search groups */
    4019           0 :         return mbof_rcmp_search_groups(ctx);
    4020             :     }
    4021             : 
    4022           0 :     talloc_zfree(ares);
    4023           0 :     return LDB_SUCCESS;
    4024             : }
    4025             : 
    4026           0 : static int mbof_rcmp_search_groups(struct mbof_rcmp_context *ctx)
    4027             : {
    4028           0 :     struct ldb_context *ldb = ldb_module_get_ctx(ctx->module);
    4029             :     static const char *attrs[] = { DB_MEMBEROF, DB_MEMBERUID,
    4030             :                                    DB_NAME, DB_MEMBER, NULL };
    4031             :     static const char *filter = "(objectclass=group)";
    4032             :     struct ldb_request *req;
    4033             :     int ret;
    4034             : 
    4035           0 :     ret = hash_create_ex(1024, &ctx->group_table, 0, 0, 0, 0,
    4036             :                          hash_alloc, hash_free, ctx, NULL, NULL);
    4037           0 :     if (ret != HASH_SUCCESS) {
    4038           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    4039             :                                LDB_ERR_OPERATIONS_ERROR);
    4040             :     }
    4041             : 
    4042           0 :     ret = ldb_build_search_req(&req, ldb, ctx,
    4043             :                                NULL, LDB_SCOPE_SUBTREE,
    4044             :                                filter, attrs, NULL,
    4045             :                                ctx, mbof_rcmp_grp_callback, ctx->req);
    4046           0 :     if (ret != LDB_SUCCESS) {
    4047           0 :         return ret;
    4048             :     }
    4049             : 
    4050           0 :     return ldb_request(ldb, req);
    4051             : }
    4052             : 
    4053           0 : static int mbof_rcmp_grp_callback(struct ldb_request *req,
    4054             :                                   struct ldb_reply *ares)
    4055             : {
    4056             :     struct ldb_context *ldb;
    4057             :     struct mbof_rcmp_context *ctx;
    4058             :     struct ldb_message_element *el;
    4059             :     struct mbof_member *iter;
    4060             :     struct mbof_member *grp;
    4061             :     hash_value_t value;
    4062             :     hash_key_t key;
    4063             :     const char *name;
    4064             :     int i, j;
    4065             :     int ret;
    4066             : 
    4067           0 :     ctx = talloc_get_type(req->context, struct mbof_rcmp_context);
    4068           0 :     ldb = ldb_module_get_ctx(ctx->module);
    4069             : 
    4070           0 :     if (!ares) {
    4071           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    4072             :                                LDB_ERR_OPERATIONS_ERROR);
    4073             :     }
    4074           0 :     if (ares->error != LDB_SUCCESS) {
    4075           0 :         return ldb_module_done(ctx->req,
    4076             :                                ares->controls,
    4077             :                                ares->response,
    4078             :                                ares->error);
    4079             :     }
    4080             : 
    4081           0 :     switch (ares->type) {
    4082             :     case LDB_REPLY_ENTRY:
    4083             : 
    4084           0 :         grp = talloc_zero(ctx, struct mbof_member);
    4085           0 :         if (!grp) {
    4086           0 :             return ldb_module_done(ctx->req, NULL, NULL,
    4087             :                                    LDB_ERR_OPERATIONS_ERROR);
    4088             :         }
    4089             : 
    4090           0 :         grp->status = MBOF_GROUP_TO_DO;
    4091           0 :         grp->dn = talloc_steal(grp, ares->message->dn);
    4092           0 :         grp->name = ldb_msg_find_attr_as_string(ares->message, DB_NAME, NULL);
    4093           0 :         name = ldb_msg_find_attr_as_string(ares->message, DB_NAME, NULL);
    4094           0 :         if (name) {
    4095           0 :             grp->name = talloc_steal(grp, name);
    4096             :         }
    4097             : 
    4098           0 :         if (ldb_msg_find_element(ares->message, DB_MEMBEROF)) {
    4099           0 :             grp->orig_has_memberof = true;
    4100             :         }
    4101             : 
    4102           0 :         if (ldb_msg_find_element(ares->message, DB_MEMBERUID)) {
    4103           0 :             grp->orig_has_memberuid = true;
    4104             :         }
    4105             : 
    4106           0 :         ret = mbof_steal_msg_el(grp, DB_MEMBER,
    4107             :                                 ares->message, &grp->orig_members);
    4108           0 :         if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
    4109           0 :             return ldb_module_done(ctx->req, NULL, NULL,
    4110             :                                    LDB_ERR_OPERATIONS_ERROR);
    4111             :         }
    4112             : 
    4113           0 :         DLIST_ADD(ctx->group_list, grp);
    4114             : 
    4115           0 :         key.type = HASH_KEY_STRING;
    4116           0 :         key.str = discard_const(ldb_dn_get_linearized(grp->dn));
    4117           0 :         value.type = HASH_VALUE_PTR;
    4118           0 :         value.ptr = grp;
    4119             : 
    4120           0 :         ret = hash_enter(ctx->group_table, &key, &value);
    4121           0 :         if (ret != HASH_SUCCESS) {
    4122           0 :             return ldb_module_done(ctx->req, NULL, NULL,
    4123             :                                    LDB_ERR_OPERATIONS_ERROR);
    4124             :         }
    4125             : 
    4126           0 :         break;
    4127             : 
    4128             :     case LDB_REPLY_REFERRAL:
    4129             :         /* ignore */
    4130           0 :         break;
    4131             : 
    4132             :     case LDB_REPLY_DONE:
    4133           0 :         talloc_zfree(ares);
    4134             : 
    4135           0 :         if (!ctx->group_list) {
    4136             :             /* no groups ? */
    4137           0 :             return ldb_module_done(ctx->req, NULL, NULL, LDB_SUCCESS);
    4138             :         }
    4139             : 
    4140             :         /* for each group compute the members list */
    4141           0 :         for (iter = ctx->group_list; iter; iter = iter->next) {
    4142             : 
    4143           0 :             el = iter->orig_members;
    4144           0 :             if (!el || el->num_values == 0) {
    4145             :                 /* no members */
    4146           0 :                 continue;
    4147             :             }
    4148             : 
    4149             :             /* we have at most num_values group members */
    4150           0 :             iter->members = talloc_array(iter, struct mbof_member *,
    4151             :                                          el->num_values +1);
    4152           0 :             if (!iter->members) {
    4153           0 :                 return ldb_module_done(ctx->req, NULL, NULL,
    4154             :                                        LDB_ERR_OPERATIONS_ERROR);
    4155             :             }
    4156             : 
    4157           0 :             for (i = 0, j = 0; i < el->num_values; i++) {
    4158           0 :                 key.type = HASH_KEY_STRING;
    4159           0 :                 key.str = (char *)el->values[i].data;
    4160             : 
    4161           0 :                 ret = hash_lookup(ctx->user_table, &key, &value);
    4162           0 :                 switch (ret) {
    4163             :                 case HASH_SUCCESS:
    4164           0 :                     iter->members[j] = (struct mbof_member *)value.ptr;
    4165           0 :                     j++;
    4166           0 :                     break;
    4167             : 
    4168             :                 case HASH_ERROR_KEY_NOT_FOUND:
    4169             :                     /* not a user, see if it is a group */
    4170             : 
    4171           0 :                     ret = hash_lookup(ctx->group_table, &key, &value);
    4172           0 :                     if (ret != HASH_SUCCESS) {
    4173           0 :                         if (ret != HASH_ERROR_KEY_NOT_FOUND) {
    4174           0 :                             return ldb_module_done(ctx->req, NULL, NULL,
    4175             :                                                    LDB_ERR_OPERATIONS_ERROR);
    4176             :                         }
    4177             :                     }
    4178           0 :                     if (ret == HASH_ERROR_KEY_NOT_FOUND) {
    4179             :                         /* not a known user, nor a known group ?
    4180             :                            give a warning an continue */
    4181           0 :                         ldb_debug(ldb, LDB_DEBUG_ERROR,
    4182             :                                   "member attribute [%s] has no corresponding"
    4183             :                                   " entry!", key.str);
    4184           0 :                         break;
    4185             :                     }
    4186             : 
    4187           0 :                     iter->members[j] = (struct mbof_member *)value.ptr;
    4188           0 :                     j++;
    4189           0 :                     break;
    4190             : 
    4191             :                 default:
    4192           0 :                     return ldb_module_done(ctx->req, NULL, NULL,
    4193             :                                            LDB_ERR_OPERATIONS_ERROR);
    4194             :                 }
    4195             :             }
    4196             :             /* terminate */
    4197           0 :             iter->members[j] = NULL;
    4198             : 
    4199           0 :             talloc_zfree(iter->orig_members);
    4200             :         }
    4201             : 
    4202             :         /* now generate correct memberof tables */
    4203           0 :         while (ctx->group_list->status == MBOF_GROUP_TO_DO) {
    4204             : 
    4205           0 :             grp = ctx->group_list;
    4206             : 
    4207             :             /* move to end of list and mark as done.
    4208             :              * NOTE: this is not efficient, but will do for now */
    4209           0 :             DLIST_DEMOTE(ctx->group_list, grp, struct mbof_member *);
    4210           0 :             grp->status = MBOF_GROUP_DONE;
    4211             : 
    4212             :             /* verify if members need updating */
    4213           0 :             if (!grp->members) {
    4214           0 :                 continue;
    4215             :             }
    4216           0 :             for (i = 0; grp->members[i]; i++) {
    4217           0 :                 ret = mbof_member_update(ctx, grp, grp->members[i]);
    4218           0 :                 if (ret != LDB_SUCCESS) {
    4219           0 :                     return ldb_module_done(ctx->req, NULL, NULL,
    4220             :                                            LDB_ERR_OPERATIONS_ERROR);
    4221             :                 }
    4222             :             }
    4223             :         }
    4224             : 
    4225             :         /* ok all done, now go on and modify the tree */
    4226           0 :         return mbof_rcmp_update(ctx);
    4227             :     }
    4228             : 
    4229           0 :     talloc_zfree(ares);
    4230           0 :     return LDB_SUCCESS;
    4231             : }
    4232             : 
    4233           0 : static int mbof_member_update(struct mbof_rcmp_context *ctx,
    4234             :                               struct mbof_member *parent,
    4235             :                               struct mbof_member *mem)
    4236             : {
    4237             :     hash_value_t value;
    4238             :     hash_key_t key;
    4239             :     int ret;
    4240             : 
    4241             :     /* ignore loops */
    4242           0 :     if (parent == mem) return LDB_SUCCESS;
    4243             : 
    4244           0 :     key.type = HASH_KEY_STRING;
    4245           0 :     key.str = discard_const(ldb_dn_get_linearized(parent->dn));
    4246             : 
    4247           0 :     if (!mem->memberofs) {
    4248           0 :         ret = hash_create_ex(32, &mem->memberofs, 0, 0, 0, 0,
    4249             :                              hash_alloc, hash_free, mem, NULL, NULL);
    4250           0 :         if (ret != HASH_SUCCESS) {
    4251           0 :             return LDB_ERR_OPERATIONS_ERROR;
    4252             :         }
    4253             : 
    4254           0 :         ret = HASH_ERROR_KEY_NOT_FOUND;
    4255             : 
    4256             :     } else {
    4257             : 
    4258           0 :         ret = hash_lookup(mem->memberofs, &key, &value);
    4259           0 :         if (ret != HASH_SUCCESS) {
    4260           0 :             if (ret != HASH_ERROR_KEY_NOT_FOUND) {
    4261             :                 /* fatal error */
    4262           0 :                 return LDB_ERR_OPERATIONS_ERROR;
    4263             :             }
    4264             :         }
    4265             :     }
    4266             : 
    4267           0 :     if (ret == HASH_ERROR_KEY_NOT_FOUND) {
    4268             : 
    4269             :         /* it's missing, update member */
    4270           0 :         value.type = HASH_VALUE_PTR;
    4271           0 :         value.ptr = parent;
    4272             : 
    4273           0 :         ret = hash_enter(mem->memberofs, &key, &value);
    4274           0 :         if (ret != HASH_SUCCESS) {
    4275           0 :             return LDB_ERR_OPERATIONS_ERROR;
    4276             :         }
    4277             : 
    4278           0 :         if (mem->status == MBOF_USER) {
    4279             :             /* add corresponding memuid to the group */
    4280           0 :             ret = mbof_add_memuid(parent, mem->name);
    4281           0 :             if (ret != LDB_SUCCESS) {
    4282           0 :                 return ret;
    4283             :             }
    4284             :         }
    4285             : 
    4286             :         /* if we updated a group, mark it as TO DO again */
    4287           0 :         if (mem->status == MBOF_GROUP_DONE) {
    4288           0 :             mem->status = MBOF_GROUP_TO_DO;
    4289             :         }
    4290             :     }
    4291             : 
    4292             :     /* now see if the parent has memberofs to pass down */
    4293           0 :     if (parent->memberofs) {
    4294           0 :         ret = hash_iterate(parent->memberofs, mbof_member_iter, mem);
    4295           0 :         if (ret != HASH_SUCCESS) {
    4296           0 :             return LDB_ERR_OPERATIONS_ERROR;
    4297             :         }
    4298           0 :         if (mem->status == MBOF_ITER_ERROR) {
    4299           0 :             return LDB_ERR_OPERATIONS_ERROR;
    4300             :         }
    4301             :     }
    4302             : 
    4303             :     /* finally, if it was made TO DO move it to the head */
    4304           0 :     if (mem->status == MBOF_GROUP_TO_DO) {
    4305           0 :         DLIST_PROMOTE(ctx->group_list, mem);
    4306             :     }
    4307             : 
    4308           0 :     return LDB_SUCCESS;
    4309             : }
    4310             : 
    4311           0 : static bool mbof_member_iter(hash_entry_t *item, void *user_data)
    4312             : {
    4313             :     struct mbof_member *parent;
    4314             :     struct mbof_member *mem;
    4315             :     hash_value_t value;
    4316             :     int ret;
    4317             : 
    4318           0 :     mem = talloc_get_type(user_data, struct mbof_member);
    4319             : 
    4320             :     /* exclude self */
    4321           0 :     if (strcmp(item->key.str, ldb_dn_get_linearized(mem->dn)) == 0) {
    4322           0 :         return true;
    4323             :     }
    4324             : 
    4325             :     /* check if we already have it */
    4326           0 :     ret = hash_lookup(mem->memberofs, &item->key, &value);
    4327           0 :     if (ret != HASH_SUCCESS) {
    4328           0 :         if (ret != HASH_ERROR_KEY_NOT_FOUND) {
    4329             :             /* fatal error */
    4330           0 :             mem->status = MBOF_ITER_ERROR;
    4331           0 :             return false;
    4332             :         }
    4333             : 
    4334             :         /* was not already here, add it and mark group as TO DO */
    4335           0 :         ret = hash_enter(mem->memberofs, &item->key, &item->value);
    4336           0 :         if (ret != HASH_SUCCESS) {
    4337           0 :             return LDB_ERR_OPERATIONS_ERROR;
    4338             :         }
    4339             : 
    4340           0 :         if (mem->status == MBOF_GROUP_DONE) {
    4341           0 :             mem->status = MBOF_GROUP_TO_DO;
    4342             :         }
    4343             : 
    4344           0 :         if (mem->status == MBOF_USER) {
    4345             :             /* add corresponding memuid to the group */
    4346           0 :             parent = (struct mbof_member *)item->value.ptr;
    4347           0 :             ret = mbof_add_memuid(parent, mem->name);
    4348           0 :             if (ret != LDB_SUCCESS) {
    4349           0 :                 mem->status = MBOF_ITER_ERROR;
    4350           0 :                 return false;
    4351             :             }
    4352             :         }
    4353             :     }
    4354             : 
    4355           0 :     return true;
    4356             : }
    4357             : 
    4358           0 : static int mbof_add_memuid(struct mbof_member *grp, const char *user)
    4359             : {
    4360             :     struct ldb_val *vals;
    4361             :     int n;
    4362             : 
    4363           0 :     if (!grp->memuids) {
    4364           0 :         grp->memuids = talloc_zero(grp, struct ldb_message_element);
    4365           0 :         if (!grp->memuids) {
    4366           0 :             return LDB_ERR_OPERATIONS_ERROR;
    4367             :         }
    4368             : 
    4369           0 :         grp->memuids->name = talloc_strdup(grp->memuids, DB_MEMBERUID);
    4370           0 :         if (!grp->memuids->name) {
    4371           0 :             return LDB_ERR_OPERATIONS_ERROR;
    4372             :         }
    4373             :     }
    4374             : 
    4375           0 :     n = grp->memuids->num_values;
    4376           0 :     vals = talloc_realloc(grp->memuids,
    4377             :                           grp->memuids->values,
    4378             :                           struct ldb_val, n + 1);
    4379           0 :     if (!vals) {
    4380           0 :         return LDB_ERR_OPERATIONS_ERROR;
    4381             :     }
    4382             : 
    4383           0 :     vals[n].data = (uint8_t *)talloc_strdup(vals, user);
    4384           0 :     vals[n].length = strlen(user);
    4385             : 
    4386           0 :     grp->memuids->values = vals;
    4387           0 :     grp->memuids->num_values = n + 1;
    4388             : 
    4389           0 :     return LDB_SUCCESS;
    4390             : }
    4391             : 
    4392           0 : static int mbof_rcmp_update(struct mbof_rcmp_context *ctx)
    4393             : {
    4394           0 :     struct ldb_context *ldb = ldb_module_get_ctx(ctx->module);
    4395             :     struct ldb_message_element *el;
    4396           0 :     struct ldb_message *msg = NULL;
    4397             :     struct ldb_request *req;
    4398           0 :     struct mbof_member *x = NULL;
    4399             :     hash_key_t *keys;
    4400             :     unsigned long count;
    4401             :     int flags;
    4402             :     int ret, i;
    4403             : 
    4404             :     /* we process all users first and then all groups */
    4405           0 :     if (ctx->user_list) {
    4406             :         /* take the next entry and remove it from the list */
    4407           0 :         x = ctx->user_list;
    4408           0 :         DLIST_REMOVE(ctx->user_list, x);
    4409             :     }
    4410           0 :     else if (ctx->group_list) {
    4411             :         /* take the next entry and remove it from the list */
    4412           0 :         x = ctx->group_list;
    4413           0 :         DLIST_REMOVE(ctx->group_list, x);
    4414             :     }
    4415             :     else {
    4416             :         /* processing terminated, return */
    4417           0 :         ret = LDB_SUCCESS;
    4418           0 :         goto done;
    4419             :     }
    4420             : 
    4421           0 :     msg = ldb_msg_new(ctx);
    4422           0 :     if (!msg) {
    4423           0 :         ret = LDB_ERR_OPERATIONS_ERROR;
    4424           0 :         goto done;
    4425             :     }
    4426             : 
    4427           0 :     msg->dn = x->dn;
    4428             : 
    4429             :     /* process memberof */
    4430           0 :     if (x->memberofs) {
    4431           0 :         ret = hash_keys(x->memberofs, &count, &keys);
    4432           0 :         if (ret != HASH_SUCCESS) {
    4433           0 :             ret = LDB_ERR_OPERATIONS_ERROR;
    4434           0 :             goto done;
    4435             :         }
    4436             : 
    4437           0 :         if (x->orig_has_memberof) {
    4438           0 :             flags = LDB_FLAG_MOD_REPLACE;
    4439             :         } else {
    4440           0 :             flags = LDB_FLAG_MOD_ADD;
    4441             :         }
    4442             : 
    4443           0 :         ret = ldb_msg_add_empty(msg, DB_MEMBEROF, flags, &el);
    4444           0 :         if (ret != LDB_SUCCESS) {
    4445           0 :             goto done;
    4446             :         }
    4447             : 
    4448           0 :         el->values = talloc_array(el, struct ldb_val, count);
    4449           0 :         if (!el->values) {
    4450           0 :             ret = LDB_ERR_OPERATIONS_ERROR;
    4451           0 :             goto done;
    4452             :         }
    4453           0 :         el->num_values = count;
    4454             : 
    4455           0 :         for (i = 0; i < count; i++) {
    4456           0 :             el->values[i].data = (uint8_t *)keys[i].str;
    4457           0 :             el->values[i].length = strlen(keys[i].str);
    4458             :         }
    4459           0 :     } else if (x->orig_has_memberof) {
    4460           0 :         ret = ldb_msg_add_empty(msg, DB_MEMBEROF, LDB_FLAG_MOD_DELETE, NULL);
    4461           0 :         if (ret != LDB_SUCCESS) {
    4462           0 :             goto done;
    4463             :         }
    4464             :     }
    4465             : 
    4466             :     /* process memberuid */
    4467           0 :     if (x->memuids) {
    4468           0 :         if (x->orig_has_memberuid) {
    4469           0 :             flags = LDB_FLAG_MOD_REPLACE;
    4470             :         } else {
    4471           0 :             flags = LDB_FLAG_MOD_ADD;
    4472             :         }
    4473             : 
    4474           0 :         ret = ldb_msg_add(msg, x->memuids, flags);
    4475           0 :         if (ret != LDB_SUCCESS) {
    4476           0 :             goto done;
    4477             :         }
    4478             :     }
    4479           0 :     else if (x->orig_has_memberuid) {
    4480           0 :         ret = ldb_msg_add_empty(msg, DB_MEMBERUID, LDB_FLAG_MOD_DELETE, NULL);
    4481           0 :         if (ret != LDB_SUCCESS) {
    4482           0 :             goto done;
    4483             :         }
    4484             :     }
    4485             : 
    4486           0 :     ret = ldb_build_mod_req(&req, ldb, ctx, msg, NULL,
    4487             :                             ctx, mbof_rcmp_mod_callback,
    4488             :                             ctx->req);
    4489           0 :     if (ret != LDB_SUCCESS) {
    4490           0 :         goto done;
    4491             :     }
    4492           0 :     talloc_steal(req, msg);
    4493             : 
    4494             :     /* fire next call */
    4495           0 :     return ldb_next_request(ctx->module, req);
    4496             : 
    4497             : done:
    4498             :     /* all users and groups have been processed */
    4499           0 :     return ldb_module_done(ctx->req, NULL, NULL, ret);
    4500             : }
    4501             : 
    4502           0 : static int mbof_rcmp_mod_callback(struct ldb_request *req,
    4503             :                                   struct ldb_reply *ares)
    4504             : {
    4505             :     struct ldb_context *ldb;
    4506             :     struct mbof_rcmp_context *ctx;
    4507             : 
    4508           0 :     ctx = talloc_get_type(req->context, struct mbof_rcmp_context);
    4509           0 :     ldb = ldb_module_get_ctx(ctx->module);
    4510             : 
    4511           0 :     if (!ares) {
    4512           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    4513             :                                LDB_ERR_OPERATIONS_ERROR);
    4514             :     }
    4515           0 :     if (ares->error != LDB_SUCCESS) {
    4516           0 :         return ldb_module_done(ctx->req,
    4517             :                                ares->controls,
    4518             :                                ares->response,
    4519             :                                ares->error);
    4520             :     }
    4521             : 
    4522           0 :     switch (ares->type) {
    4523             :     case LDB_REPLY_ENTRY:
    4524           0 :         ldb_debug(ldb, LDB_DEBUG_TRACE, "Got an entry on a non search op ?!");
    4525             :         /* shouldn't happen */
    4526           0 :         talloc_zfree(ares);
    4527           0 :         return ldb_module_done(ctx->req, NULL, NULL,
    4528             :                                LDB_ERR_OPERATIONS_ERROR);
    4529             :     case LDB_REPLY_REFERRAL:
    4530             :         /* ignore */
    4531           0 :         talloc_zfree(ares);
    4532           0 :         break;
    4533             : 
    4534             :     case LDB_REPLY_DONE:
    4535           0 :         talloc_zfree(ares);
    4536             : 
    4537             :         /* update the next one */
    4538           0 :         return mbof_rcmp_update(ctx);
    4539             :     }
    4540             : 
    4541           0 :     return LDB_SUCCESS;
    4542             : }
    4543             : 
    4544             : 
    4545             : 
    4546             : /* module init code */
    4547             : 
    4548        1081 : static int memberof_init(struct ldb_module *module)
    4549             : {
    4550        1081 :     struct ldb_context *ldb = ldb_module_get_ctx(module);
    4551             :     int ret;
    4552             : 
    4553             :     /* set syntaxes for member and memberof so that comparisons in filters and
    4554             :      * such are done right */
    4555        1081 :     ret = ldb_schema_attribute_add(ldb, DB_MEMBER, 0, LDB_SYNTAX_DN);
    4556        1081 :     if (ret != 0) return LDB_ERR_OPERATIONS_ERROR;
    4557             : 
    4558        1081 :     ret = ldb_schema_attribute_add(ldb, DB_MEMBEROF, 0, LDB_SYNTAX_DN);
    4559        1081 :     if (ret != 0) return LDB_ERR_OPERATIONS_ERROR;
    4560             : 
    4561        1081 :     return ldb_next_init(module);
    4562             : }
    4563             : 
    4564             : const struct ldb_module_ops ldb_memberof_module_ops = {
    4565             :     .name = "memberof",
    4566             :     .init_context = memberof_init,
    4567             :     .add = memberof_add,
    4568             :     .modify = memberof_mod,
    4569             :     .del = memberof_del,
    4570             : };
    4571             : 
    4572             : int ldb_init_module(const char *version)
    4573             : {
    4574             : #if defined(SSS_LDB_VERSION_CHECK) && defined(LDB_MODULE_CHECK_VERSION)
    4575             :     LDB_MODULE_CHECK_VERSION(version);
    4576             : #endif /* SSS_LDB_VERSION_CHECK && LDB_MODULE_CHECK_VERSION */
    4577          22 :     return ldb_register_module(&ldb_memberof_module_ops);
    4578             : }

Generated by: LCOV version 1.10