From 1c48b5a62f73234ed26bb20f0ab345ab61cda0ab Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Thu, 18 Feb 2010 07:49:04 -0500 Subject: Rename server/ directory to src/ Also update BUILD.txt --- server/ldb_modules/memberof.c | 3612 ----------------------------------------- 1 file changed, 3612 deletions(-) delete mode 100644 server/ldb_modules/memberof.c (limited to 'server/ldb_modules') diff --git a/server/ldb_modules/memberof.c b/server/ldb_modules/memberof.c deleted file mode 100644 index e0a241a5e..000000000 --- a/server/ldb_modules/memberof.c +++ /dev/null @@ -1,3612 +0,0 @@ -/* - SSSD memberof module - - Copyright (C) Simo Sorce 2008 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include "ldb_module.h" -#include "util/util.h" -#include "dhash.h" - -#define DB_MEMBER "member" -#define DB_MEMBEROF "memberof" -#define DB_MEMBERUID "memberuid" -#define DB_NAME "name" -#define DB_USER_CLASS "user" -#define DB_OC "objectClass" - -#ifndef talloc_zfree -#define talloc_zfree(ptr) do { talloc_free(ptr); ptr = NULL; } while(0) -#endif - -#ifndef MAX -#define MAX(a,b) (((a) > (b)) ? (a) : (b)) -#endif - -struct mbof_dn_array { - struct ldb_dn **dns; - int num; -}; - -struct mbof_dn { - struct mbof_dn *next; - struct ldb_dn *dn; -}; - -struct mbof_ctx { - struct ldb_module *module; - struct ldb_request *req; - - struct ldb_control **ret_ctrls; - struct ldb_extended *ret_resp; -}; - -struct mbof_add_operation { - struct mbof_add_ctx *add_ctx; - struct mbof_add_operation *next; - - struct mbof_dn_array *parents; - struct ldb_dn *entry_dn; - - struct ldb_message *entry; -}; - -struct mbof_memberuid_op { - struct ldb_dn *dn; - struct ldb_message_element *el; -}; - -struct mbof_add_ctx { - struct mbof_ctx *ctx; - - struct mbof_add_operation *add_list; - struct mbof_add_operation *current_op; - - struct ldb_message *msg; - struct ldb_dn *msg_dn; - bool terminate; - - struct mbof_dn *missing; - - struct mbof_memberuid_op *muops; - int num_muops; - int cur_muop; -}; - -struct mbof_del_ancestors_ctx { - struct mbof_dn_array *new_list; - int num_direct; - int cur; - - struct ldb_message *entry; -}; - -struct mbof_del_operation { - struct mbof_del_ctx *del_ctx; - struct mbof_del_operation *parent; - struct mbof_del_operation **children; - int num_children; - int next_child; - - struct ldb_dn *entry_dn; - - struct ldb_message *entry; - struct ldb_message **parents; - int num_parents; - int cur_parent; - - struct mbof_del_ancestors_ctx *anc_ctx; -}; - -struct mbof_mod_ctx; - -struct mbof_del_ctx { - struct mbof_ctx *ctx; - - struct mbof_del_operation *first; - struct mbof_dn *history; - - struct ldb_message **mus; - int num_mus; - - struct mbof_memberuid_op *muops; - int num_muops; - int cur_muop; - - struct mbof_mod_ctx *follow_mod; - bool is_mod; -}; - -struct mbof_mod_ctx { - struct mbof_ctx *ctx; - - const struct ldb_message_element *membel; - struct ldb_message *entry; - - struct mbof_dn_array *to_add; - - struct ldb_message *msg; - bool terminate; -}; - -static struct mbof_ctx *mbof_init(struct ldb_module *module, - struct ldb_request *req) -{ - struct mbof_ctx *ctx; - - ctx = talloc_zero(req, struct mbof_ctx); - if (!ctx) { - return NULL; - } - - ctx->module = module; - ctx->req = req; - - return ctx; -} - -static int entry_is_user_object(struct ldb_message *entry) -{ - struct ldb_message_element *el; - struct ldb_val *val; - int i; - - el = ldb_msg_find_element(entry, DB_OC); - if (!el) { - return LDB_ERR_OPERATIONS_ERROR; - } - - /* see if this is a user */ - for (i = 0; i < el->num_values; i++) { - val = &(el->values[i]); - if (strncasecmp(DB_USER_CLASS, (char *)val->data, val->length) == 0) { - return LDB_SUCCESS; - } - } - - return LDB_ERR_NO_SUCH_ATTRIBUTE; -} - -static int mbof_append_muop(TALLOC_CTX *memctx, - struct mbof_memberuid_op **_muops, - int *_num_muops, - int flags, - struct ldb_dn *parent, - const char *name) -{ - struct mbof_memberuid_op *muops = *_muops; - int num_muops = *_num_muops; - struct mbof_memberuid_op *op; - struct ldb_val *val; - int i; - - op = NULL; - if (muops) { - for (i = 0; i < num_muops; i++) { - if (ldb_dn_compare(parent, muops[i].dn) == 0) { - op = &muops[i]; - break; - } - } - } - if (!op) { - muops = talloc_realloc(memctx, muops, - struct mbof_memberuid_op, - num_muops + 1); - if (!muops) { - return LDB_ERR_OPERATIONS_ERROR; - } - op = &muops[num_muops]; - num_muops++; - *_muops = muops; - *_num_muops = num_muops; - - op->dn = parent; - op->el = NULL; - } - - if (!op->el) { - op->el = talloc_zero(muops, struct ldb_message_element); - if (!op->el) { - return LDB_ERR_OPERATIONS_ERROR; - } - op->el->name = talloc_strdup(op->el, DB_MEMBERUID); - if (!op->el->name) { - return LDB_ERR_OPERATIONS_ERROR; - } - op->el->flags = flags; - } - - for (i = 0; i < op->el->num_values; i++) { - if (strcmp((char *)op->el->values[i].data, name) == 0) { - /* we already have this value, get out*/ - return LDB_SUCCESS; - } - } - - val = talloc_realloc(op->el, op->el->values, - struct ldb_val, op->el->num_values + 1); - if (!val) { - return LDB_ERR_OPERATIONS_ERROR; - } - val[op->el->num_values].data = (uint8_t *)talloc_strdup(val, name); - if (!val[op->el->num_values].data) { - return LDB_ERR_OPERATIONS_ERROR; - } - val[op->el->num_values].length = strlen(name); - - op->el->values = val; - op->el->num_values++; - - return LDB_SUCCESS; -} - - -/* add operation */ - -/* An add operation is quite simple. - * First of all a new object cannot yet have parents, so the only memberof - * attribute that can be added to any member contains just one object DN. - * - * The real add operation is done first, to assure nothing else fails. - * Then we list all members of the object just created, and for each member - * we create an "add operation" and we pass it a parent list of one member - * (the object we just added again). - * - * For each add operation we lookup the object we want to operate on. - * We take the list of memberof attributes and sort out which parents are - * still missing from the parent list we have provided. - * We modify the object memberof attributes to reflect the new memberships. - * Then we list all members of this object, and for each once again we create - * an "add operation" as we did in the initial object. - * - * Processing stops when the target object does not have members or when it - * already has all the parents (can happen if nested groups create loops). - * - * Group cache unrolling: - * Every time we add a memberof attribute to an actual user object, - * we proceed to store the user name. - * - * At the end we will add a memberuid attribute to our new object that - * includes all direct and indeirect user members names. - */ - -static int mbof_append_addop(struct mbof_add_ctx *add_ctx, - struct mbof_dn_array *parents, - struct ldb_dn *entry_dn) -{ - struct mbof_add_operation *lastop = NULL; - struct mbof_add_operation *addop; - - /* test if this is a duplicate */ - /* FIXME: this is not efficient */ - if (add_ctx->add_list) { - do { - if (lastop) { - lastop = lastop->next; - } else { - lastop = add_ctx->add_list; - } - - /* FIXME: check if this is right, might have to compare parents */ - if (ldb_dn_compare(lastop->entry_dn, entry_dn) == 0) { - /* duplicate found */ - return LDB_SUCCESS; - } - } while (lastop->next); - } - - addop = talloc_zero(add_ctx, struct mbof_add_operation); - if (!addop) { - return LDB_ERR_OPERATIONS_ERROR; - } - - addop->add_ctx = add_ctx; - addop->parents = parents; - addop->entry_dn = entry_dn; - - if (add_ctx->add_list) { - lastop->next = addop; - } else { - add_ctx->add_list = addop; - } - - return LDB_SUCCESS; -} - -static int memberof_recompute_task(struct ldb_module *module, - struct ldb_request *req); - -static int mbof_add_callback(struct ldb_request *req, - struct ldb_reply *ares); -static int mbof_next_add(struct mbof_add_operation *addop); -static int mbof_next_add_callback(struct ldb_request *req, - struct ldb_reply *ares); -static int mbof_add_operation(struct mbof_add_operation *addop); -static int mbof_add_missing(struct mbof_add_ctx *add_ctx, struct ldb_dn *dn); -static int mbof_add_cleanup(struct mbof_add_ctx *add_ctx); -static int mbof_add_cleanup_callback(struct ldb_request *req, - struct ldb_reply *ares); -static int mbof_add_muop(struct mbof_add_ctx *add_ctx); -static int mbof_add_muop_callback(struct ldb_request *req, - struct ldb_reply *ares); - -static int memberof_add(struct ldb_module *module, struct ldb_request *req) -{ - struct ldb_context *ldb = ldb_module_get_ctx(module); - struct mbof_add_ctx *add_ctx; - struct mbof_ctx *ctx; - struct ldb_request *add_req; - struct ldb_message_element *el; - struct mbof_dn_array *parents; - struct ldb_dn *valdn; - int i, ret; - - if (ldb_dn_is_special(req->op.add.message->dn)) { - - if (strcmp("@MEMBEROF-REBUILD", - ldb_dn_get_linearized(req->op.add.message->dn)) == 0) { - return memberof_recompute_task(module, req); - } - - /* do not manipulate other control entries */ - return ldb_next_request(module, req); - } - - /* check if memberof is specified */ - el = ldb_msg_find_element(req->op.add.message, DB_MEMBEROF); - if (el) { - ldb_debug(ldb, LDB_DEBUG_ERROR, - "Error: the memberof attribute is readonly."); - return LDB_ERR_UNWILLING_TO_PERFORM; - } - - /* check if memberuid is specified */ - el = ldb_msg_find_element(req->op.add.message, DB_MEMBERUID); - if (el) { - ldb_debug(ldb, LDB_DEBUG_ERROR, - "Error: the memberuid attribute is readonly."); - return LDB_ERR_UNWILLING_TO_PERFORM; - } - - ctx = mbof_init(module, req); - if (!ctx) { - return LDB_ERR_OPERATIONS_ERROR; - } - - add_ctx = talloc_zero(ctx, struct mbof_add_ctx); - if (!add_ctx) { - return LDB_ERR_OPERATIONS_ERROR; - } - add_ctx->ctx = ctx; - - add_ctx->msg = ldb_msg_copy(add_ctx, req->op.add.message); - if (!add_ctx->msg) { - return LDB_ERR_OPERATIONS_ERROR; - } - add_ctx->msg_dn = add_ctx->msg->dn; - - /* continue with normal ops if there are no members */ - el = ldb_msg_find_element(add_ctx->msg, DB_MEMBER); - if (!el) { - add_ctx->terminate = true; - goto done; - } - - parents = talloc_zero(add_ctx, struct mbof_dn_array); - if (!parents) { - return LDB_ERR_OPERATIONS_ERROR; - } - parents->dns = talloc_array(parents, struct ldb_dn *, 1); - if (!parents->dns) { - return LDB_ERR_OPERATIONS_ERROR; - } - parents->dns[0] = add_ctx->msg_dn; - parents->num = 1; - - /* process new members */ - /* check we are not adding ourselves as member as well */ - for (i = 0; i < el->num_values; i++) { - valdn = ldb_dn_from_ldb_val(add_ctx, ldb, &el->values[i]); - if (!valdn || !ldb_dn_validate(valdn)) { - ldb_debug(ldb, LDB_DEBUG_ERROR, "Invalid dn value: [%s]", - (const char *)el->values[i].data); - return LDB_ERR_INVALID_DN_SYNTAX; - } - if (ldb_dn_compare(valdn, req->op.add.message->dn) == 0) { - ldb_debug(ldb, LDB_DEBUG_ERROR, - "Adding self as member is not permitted! Skipping"); - continue; - } - ret = mbof_append_addop(add_ctx, parents, valdn); - if (ret != LDB_SUCCESS) { - return ret; - } - } - -done: - /* add original object */ - ret = ldb_build_add_req(&add_req, ldb, add_ctx, - add_ctx->msg, req->controls, - add_ctx, mbof_add_callback, - req); - if (ret != LDB_SUCCESS) { - return ret; - } - - return ldb_next_request(module, add_req); -} - -static int mbof_add_callback(struct ldb_request *req, - struct ldb_reply *ares) -{ - struct mbof_add_ctx *add_ctx; - struct mbof_ctx *ctx; - int ret; - - add_ctx = talloc_get_type(req->context, struct mbof_add_ctx); - ctx = add_ctx->ctx; - - if (!ares) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ctx->req, - ares->controls, - ares->response, - ares->error); - } - - switch (ares->type) { - case LDB_REPLY_ENTRY: - /* shouldn't happen */ - talloc_zfree(ares); - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - case LDB_REPLY_REFERRAL: - /* ignore */ - break; - - case LDB_REPLY_DONE: - if (add_ctx->terminate) { - return ldb_module_done(ctx->req, - ctx->ret_ctrls, - ctx->ret_resp, - LDB_SUCCESS); - } - - if (add_ctx->current_op == NULL) { - /* first operation */ - ctx->ret_ctrls = talloc_steal(ctx, ares->controls); - ctx->ret_resp = talloc_steal(ctx, ares->response); - ret = mbof_next_add(add_ctx->add_list); - } - else if (add_ctx->current_op->next) { - /* next operation */ - ret = mbof_next_add(add_ctx->current_op->next); - } - else { - /* no more operations */ - if (add_ctx->missing) { - ret = mbof_add_cleanup(add_ctx); - } - else if (add_ctx->muops) { - ret = mbof_add_muop(add_ctx); - } - else { - return ldb_module_done(ctx->req, - ctx->ret_ctrls, - ctx->ret_resp, - LDB_SUCCESS); - } - } - - if (ret != LDB_SUCCESS) { - talloc_zfree(ares); - return ldb_module_done(ctx->req, NULL, NULL, ret); - } - } - - talloc_zfree(ares); - return LDB_SUCCESS; -} - -static int mbof_next_add(struct mbof_add_operation *addop) -{ - static const char *attrs[] = { DB_OC, DB_NAME, - DB_MEMBER, DB_MEMBEROF, NULL }; - struct ldb_context *ldb; - struct ldb_request *req; - struct mbof_add_ctx *add_ctx; - struct mbof_ctx *ctx; - int ret; - - add_ctx = addop->add_ctx; - ctx = add_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - /* mark the operation as being handled */ - add_ctx->current_op = addop; - - ret = ldb_build_search_req(&req, ldb, ctx, - addop->entry_dn, LDB_SCOPE_BASE, - NULL, attrs, NULL, - addop, mbof_next_add_callback, - ctx->req); - if (ret != LDB_SUCCESS) { - return ret; - } - - return ldb_request(ldb, req); -} - -static int mbof_next_add_callback(struct ldb_request *req, - struct ldb_reply *ares) -{ - struct mbof_add_operation *addop; - struct mbof_add_ctx *add_ctx; - struct ldb_context *ldb; - struct mbof_ctx *ctx; - int ret; - - addop = talloc_get_type(req->context, struct mbof_add_operation); - add_ctx = addop->add_ctx; - ctx = add_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - if (!ares) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ctx->req, - ares->controls, - ares->response, - ares->error); - } - - switch (ares->type) { - case LDB_REPLY_ENTRY: - if (addop->entry != NULL) { - ldb_debug(ldb, LDB_DEBUG_TRACE, - "Found multiple entries for (%s)", - ldb_dn_get_linearized(addop->entry_dn)); - /* more than one entry per dn ?? db corrupted ? */ - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - addop->entry = talloc_steal(addop, ares->message); - if (addop->entry == NULL) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - break; - case LDB_REPLY_REFERRAL: - /* ignore */ - break; - - case LDB_REPLY_DONE: - talloc_zfree(ares); - if (addop->entry == NULL) { - ldb_debug(ldb, LDB_DEBUG_TRACE, "Entry not found (%s)", - ldb_dn_get_linearized(addop->entry_dn)); - - /* this target does not exists, save as missing */ - ret = mbof_add_missing(add_ctx, addop->entry_dn); - if (ret != LDB_SUCCESS) { - return ldb_module_done(ctx->req, NULL, NULL, ret); - } - /* now try the next operation */ - if (add_ctx->current_op->next) { - ret = mbof_next_add(add_ctx->current_op->next); - } - else { - /* no more operations */ - if (add_ctx->missing) { - ret = mbof_add_cleanup(add_ctx); - } - else if (add_ctx->muops) { - ret = mbof_add_muop(add_ctx); - } - else { - return ldb_module_done(ctx->req, - ctx->ret_ctrls, - ctx->ret_resp, - LDB_SUCCESS); - } - } - if (ret != LDB_SUCCESS) { - return ldb_module_done(ctx->req, NULL, NULL, ret); - } - } - else { - ret = mbof_add_operation(addop); - if (ret != LDB_SUCCESS) { - return ldb_module_done(ctx->req, NULL, NULL, ret); - } - } - return LDB_SUCCESS; - } - - talloc_zfree(ares); - return LDB_SUCCESS; -} - -/* if it is a group, add all members for cascade effect - * add memberof attribute to this entry - */ -static int mbof_add_operation(struct mbof_add_operation *addop) -{ - - TALLOC_CTX *tmp_ctx; - struct mbof_ctx *ctx; - struct mbof_add_ctx *add_ctx; - struct ldb_context *ldb; - struct ldb_message_element *el; - struct ldb_request *mod_req; - struct ldb_message *msg; - struct ldb_dn *elval_dn; - struct ldb_dn *valdn; - struct mbof_dn_array *parents; - int i, j, ret; - const char *val; - const char *name; - - add_ctx = addop->add_ctx; - ctx = add_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - parents = talloc_zero(add_ctx, struct mbof_dn_array); - if (!parents) { - return LDB_ERR_OPERATIONS_ERROR; - } - /* can't be more than the immediate parent */ - parents->dns = talloc_array(parents, struct ldb_dn *, - addop->parents->num); - if (!parents->dns) { - return LDB_ERR_OPERATIONS_ERROR; - } - - /* create new parent set for this entry */ - for (i = 0; i < addop->parents->num; i++) { - /* never add yourself as memberof */ - if (ldb_dn_compare(addop->parents->dns[i], addop->entry_dn) == 0) { - continue; - } - parents->dns[parents->num] = addop->parents->dns[i]; - parents->num++; - } - - /* remove entries that are already there */ - el = ldb_msg_find_element(addop->entry, DB_MEMBEROF); - if (el) { - - tmp_ctx = talloc_new(addop); - if (!tmp_ctx) return LDB_ERR_OPERATIONS_ERROR; - - for (i = 0; i < el->num_values; i++) { - elval_dn = ldb_dn_from_ldb_val(tmp_ctx, ldb, &el->values[i]); - if (!elval_dn) { - ldb_debug(ldb, LDB_DEBUG_TRACE, "Invalid DN in memberof [%s]", - (const char *)el->values[i].data); - talloc_free(tmp_ctx); - return LDB_ERR_OPERATIONS_ERROR; - } - for (j = 0; j < parents->num; j++) { - if (ldb_dn_compare(parents->dns[j], elval_dn) == 0) { - /* duplicate found */ - break; - } - } - if (j < parents->num) { - /* remove duplicate */ - for (;j+1 < parents->num; j++) { - parents->dns[j] = parents->dns[j+1]; - } - parents->num--; - } - } - - if (parents->num == 0) { - /* already contains all parents as memberof, skip to next */ - talloc_free(tmp_ctx); - talloc_free(addop->entry); - addop->entry = NULL; - - if (addop->next) { - return mbof_next_add(addop->next); - } - else if (add_ctx->muops) { - return mbof_add_muop(add_ctx); - } - else { - /* that was the last entry, get out */ - return ldb_module_done(ctx->req, - ctx->ret_ctrls, - ctx->ret_resp, - LDB_SUCCESS); - } - } - talloc_free(tmp_ctx); - } - - /* if it is a group add all members */ - el = ldb_msg_find_element(addop->entry, DB_MEMBER); - if (el) { - for (i = 0; i < el->num_values; i++) { - valdn = ldb_dn_from_ldb_val(add_ctx, ldb, &el->values[i]); - if (!valdn) { - ldb_debug(ldb, LDB_DEBUG_TRACE, "Invalid DN in member [%s]", - (const char *)el->values[i].data); - return LDB_ERR_OPERATIONS_ERROR; - } - if (!ldb_dn_validate(valdn)) { - ldb_debug(ldb, LDB_DEBUG_TRACE, - "Invalid DN syntax for member [%s]", - (const char *)el->values[i].data); - return LDB_ERR_INVALID_DN_SYNTAX; - } - ret = mbof_append_addop(add_ctx, parents, valdn); - if (ret != LDB_SUCCESS) { - return ret; - } - } - } - - /* check if we need to store memberuid ops for this entry */ - ret = entry_is_user_object(addop->entry); - switch (ret) { - case LDB_SUCCESS: - /* it's a user object */ - name = ldb_msg_find_attr_as_string(addop->entry, DB_NAME, NULL); - if (!name) { - return LDB_ERR_OPERATIONS_ERROR; - } - - for (i = 0; i < parents->num; i++) { - ret = mbof_append_muop(add_ctx, &add_ctx->muops, - &add_ctx->num_muops, - LDB_FLAG_MOD_ADD, - parents->dns[i], name); - if (ret != LDB_SUCCESS) { - return ret; - } - } - - break; - - case LDB_ERR_NO_SUCH_ATTRIBUTE: - /* it is not a user object, continue */ - break; - - default: - /* an error occured, return */ - return ret; - } - - /* we are done with the entry now */ - talloc_free(addop->entry); - addop->entry = NULL; - - /* add memberof to entry */ - msg = ldb_msg_new(addop); - if (!msg) return LDB_ERR_OPERATIONS_ERROR; - - msg->dn = addop->entry_dn; - - ret = ldb_msg_add_empty(msg, DB_MEMBEROF, LDB_FLAG_MOD_ADD, &el); - if (ret != LDB_SUCCESS) { - return ret; - } - el->values = talloc_array(msg, struct ldb_val, parents->num); - if (!el->values) { - return LDB_ERR_OPERATIONS_ERROR; - } - for (i = 0, j = 0; i < parents->num; i++) { - if (ldb_dn_compare(parents->dns[i], msg->dn) == 0) continue; - val = ldb_dn_get_linearized(parents->dns[i]); - el->values[j].length = strlen(val); - el->values[j].data = (uint8_t *)talloc_strdup(el->values, val); - if (!el->values[j].data) { - return LDB_ERR_OPERATIONS_ERROR; - } - j++; - } - el->num_values = j; - - ret = ldb_build_mod_req(&mod_req, ldb, add_ctx, - msg, NULL, - add_ctx, mbof_add_callback, - ctx->req); - if (ret != LDB_SUCCESS) { - return ret; - } - talloc_steal(mod_req, msg); - - return ldb_next_request(ctx->module, mod_req); -} - -static int mbof_add_missing(struct mbof_add_ctx *add_ctx, struct ldb_dn *dn) -{ - struct mbof_dn *mdn; - - mdn = talloc(add_ctx, struct mbof_dn); - if (!mdn) { - return LDB_ERR_OPERATIONS_ERROR; - } - mdn->dn = talloc_steal(mdn, dn); - - /* add to the list */ - mdn->next = add_ctx->missing; - add_ctx->missing = mdn; - - return LDB_SUCCESS; -} - -/* remove unexisting members and add memberuid attribute */ -static int mbof_add_cleanup(struct mbof_add_ctx *add_ctx) -{ - struct ldb_context *ldb; - struct ldb_message *msg; - struct ldb_request *mod_req; - struct ldb_message_element *el; - struct mbof_ctx *ctx; - struct mbof_dn *iter; - const char *val; - int ret, i, num; - - ctx = add_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - num = 0; - for (iter = add_ctx->missing; iter; iter = iter->next) { - num++; - } - if (num == 0) { - return LDB_ERR_OPERATIONS_ERROR; - } - - msg = ldb_msg_new(add_ctx); - if (!msg) return LDB_ERR_OPERATIONS_ERROR; - - msg->dn = add_ctx->msg_dn; - - ret = ldb_msg_add_empty(msg, DB_MEMBER, LDB_FLAG_MOD_DELETE, &el); - if (ret != LDB_SUCCESS) { - return ret; - } - el->values = talloc_array(msg, struct ldb_val, num); - if (!el->values) { - return LDB_ERR_OPERATIONS_ERROR; - } - el->num_values = num; - for (i = 0, iter = add_ctx->missing; iter; iter = iter->next, i++) { - val = ldb_dn_get_linearized(iter->dn); - el->values[i].length = strlen(val); - el->values[i].data = (uint8_t *)talloc_strdup(el->values, val); - if (!el->values[i].data) { - return LDB_ERR_OPERATIONS_ERROR; - } - } - - ret = ldb_build_mod_req(&mod_req, ldb, add_ctx, - msg, NULL, - add_ctx, mbof_add_cleanup_callback, - ctx->req); - if (ret != LDB_SUCCESS) { - return ret; - } - - return ldb_next_request(ctx->module, mod_req); -} - -static int mbof_add_cleanup_callback(struct ldb_request *req, - struct ldb_reply *ares) -{ - struct mbof_add_ctx *add_ctx; - struct mbof_ctx *ctx; - int ret; - - add_ctx = talloc_get_type(req->context, struct mbof_add_ctx); - ctx = add_ctx->ctx; - - if (!ares) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ctx->req, - ares->controls, - ares->response, - ares->error); - } - - switch (ares->type) { - case LDB_REPLY_ENTRY: - /* shouldn't happen */ - talloc_zfree(ares); - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - case LDB_REPLY_REFERRAL: - /* ignore */ - break; - - case LDB_REPLY_DONE: - if (add_ctx->muops) { - ret = mbof_add_muop(add_ctx); - } - else { - return ldb_module_done(ctx->req, - ctx->ret_ctrls, - ctx->ret_resp, - LDB_SUCCESS); - } - - if (ret != LDB_SUCCESS) { - talloc_zfree(ares); - return ldb_module_done(ctx->req, NULL, NULL, ret); - } - } - - talloc_zfree(ares); - return LDB_SUCCESS; -} - -/* add memberuid attributes to parent groups */ -static int mbof_add_muop(struct mbof_add_ctx *add_ctx) -{ - struct ldb_context *ldb; - struct ldb_message *msg; - struct ldb_request *mod_req; - struct mbof_ctx *ctx; - int ret; - - ctx = add_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - msg = ldb_msg_new(add_ctx); - if (!msg) return LDB_ERR_OPERATIONS_ERROR; - - msg->dn = add_ctx->muops[add_ctx->cur_muop].dn; - msg->elements = add_ctx->muops[add_ctx->cur_muop].el; - msg->num_elements = 1; - - ret = ldb_build_mod_req(&mod_req, ldb, add_ctx, - msg, NULL, - add_ctx, mbof_add_muop_callback, - ctx->req); - if (ret != LDB_SUCCESS) { - return ret; - } - - return ldb_next_request(ctx->module, mod_req); -} - -static int mbof_add_muop_callback(struct ldb_request *req, - struct ldb_reply *ares) -{ - struct mbof_add_ctx *add_ctx; - struct mbof_ctx *ctx; - int ret; - - add_ctx = talloc_get_type(req->context, struct mbof_add_ctx); - ctx = add_ctx->ctx; - - if (!ares) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ctx->req, - ares->controls, - ares->response, - ares->error); - } - - switch (ares->type) { - case LDB_REPLY_ENTRY: - /* shouldn't happen */ - talloc_zfree(ares); - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - case LDB_REPLY_REFERRAL: - /* ignore */ - break; - - case LDB_REPLY_DONE: - add_ctx->cur_muop++; - if (add_ctx->cur_muop < add_ctx->num_muops) { - ret = mbof_add_muop(add_ctx); - } - else { - return ldb_module_done(ctx->req, - ctx->ret_ctrls, - ctx->ret_resp, - LDB_SUCCESS); - } - - if (ret != LDB_SUCCESS) { - talloc_zfree(ares); - return ldb_module_done(ctx->req, NULL, NULL, ret); - } - } - - talloc_zfree(ares); - return LDB_SUCCESS; -} - - - - -/* delete operations */ - -/* The implementation of delete operations is a bit more complex than an add - * operation. This is because we need to recompute memberships of potentially - * quite far descendants and we also have to account for loops and how to - * break them without ending in an endless loop ourselves. - * The difficulty is in the fact that while the member -> memberof link is - * direct, memberof -> member is not as membership is transitive. - * - * Ok, first of all, contrary to the add operation, a delete operation - * involves an existing object that may have existing parents. So, first, we - * search the object itself to get the original membership lists (member and - * memberof) for this object, and we also search for any object that has it as - * one of its members. - * Once we have the results, we store object and parents and proceed with the - * original operation to make sure it is valid. - * - * Once the original op returns we proceed fixing parents (parents being each - * object that has the delete operation target object as member), if any. - * - * For each parent we retrieved we proceed to delete the member attribute that - * points to the object we just deleted. Once done for all parents (or if no - * parents exists), we proceed with the children and descendants. - * - * To handle the children we create a first ancestor operation that reflects - * the delete we just made. We set as parents of this object the parents just - * retrieved with the first search. Then we create a remove list. - * - * The remove list contains all objects in the original memberof list and the - * object dn itself of the original delete operation target object (the first - * ancestor). - * - * An operation is identified by an object that contains a tree of - * descendants: - * The remove list for the children, the immediate parent, and the dn and - * entry of the object this operation is about. - * - * We now proceed with adding a new operation for each original member of the - * first ancestor. - * - * In each operation we must first lookup the target object and each immediate - * parent (all the objects in the tree that have target as a "member"). - * - * Then we proceed to calculate the new memberof list that we are going to set - * on the target object. - * The new memberof list starts with including all the objects that have the - * target as their direct member. - * Finally for each entry in this provisional new memberof list we add all its - * memberof elements to the new memberof list (taking care of excluding - * duplicates). This way we are certain all direct and indirect membership are - * accounted for. - * - * At this point we have the final new memberof list for this operation and we - * can proceed to modify the entry. - * - * Once the entry has been modified we proceed again to check if there are any - * children of this entry (the entry has "member"s). - * We create a new remove list that is the difference between the original - * entry memberof list and the new memberof list we just stored back in the - * object. - * Then for each member we create a new operation. - * - * We continue to process operations until no new operations need to be - * performed. - * - * Ordering is important here, se the mbof_del_get_next() function to - * understand how we proceed to select which new operation to process. - * - * As a final operation remove any memberuid corresponding to a removal of - * a memberof field from a user entry - */ - -static int mbof_del_search_callback(struct ldb_request *req, - struct ldb_reply *ares); -static int mbof_orig_del(struct mbof_del_ctx *ctx); -static int mbof_orig_del_callback(struct ldb_request *req, - struct ldb_reply *ares); -static int mbof_del_cleanup_parents(struct mbof_del_ctx *del_ctx); -static int mbof_del_clean_par_callback(struct ldb_request *req, - struct ldb_reply *ares); -static int mbof_del_cleanup_children(struct mbof_del_ctx *del_ctx); -static int mbof_append_delop(struct mbof_del_operation *parent, - struct ldb_dn *entry_dn); -static int mbof_del_execute_op(struct mbof_del_operation *delop); -static int mbof_del_exop_search_callback(struct ldb_request *req, - struct ldb_reply *ares); -static int mbof_del_execute_cont(struct mbof_del_operation *delop); -static int mbof_del_ancestors(struct mbof_del_operation *delop); -static int mbof_del_anc_callback(struct ldb_request *req, - struct ldb_reply *ares); -static int mbof_del_mod_entry(struct mbof_del_operation *delop); -static int mbof_del_mod_callback(struct ldb_request *req, - struct ldb_reply *ares); -static int mbof_del_progeny(struct mbof_del_operation *delop); -static int mbof_del_get_next(struct mbof_del_operation *delop, - struct mbof_del_operation **nextop); -static int mbof_del_fill_muop(struct mbof_del_ctx *del_ctx, - struct ldb_message *entry); -static int mbof_del_muop(struct mbof_del_ctx *ctx); -static int mbof_del_muop_callback(struct ldb_request *req, - struct ldb_reply *ares); - - -static int memberof_del(struct ldb_module *module, struct ldb_request *req) -{ - static const char *attrs[] = { DB_OC, DB_NAME, - DB_MEMBER, DB_MEMBEROF, NULL }; - struct ldb_context *ldb = ldb_module_get_ctx(module); - struct mbof_del_operation *first; - struct ldb_request *search; - char *expression; - const char *dn; - struct mbof_del_ctx *del_ctx; - struct mbof_ctx *ctx; - int ret; - - if (ldb_dn_is_special(req->op.del.dn)) { - /* do not manipulate our control entries */ - return ldb_next_request(module, req); - } - - ctx = mbof_init(module, req); - if (!ctx) { - return LDB_ERR_OPERATIONS_ERROR; - } - - del_ctx = talloc_zero(ctx, struct mbof_del_ctx); - if (!del_ctx) { - talloc_free(ctx); - return LDB_ERR_OPERATIONS_ERROR; - } - del_ctx->ctx = ctx; - - /* create first entry */ - /* the first entry is the parent of all entries and the one where we remove - * member from, it does not get the same treatment as others */ - first = talloc_zero(del_ctx, struct mbof_del_operation); - if (!first) { - talloc_free(ctx); - return LDB_ERR_OPERATIONS_ERROR; - } - del_ctx->first = first; - - first->del_ctx = del_ctx; - first->entry_dn = req->op.del.dn; - - dn = ldb_dn_get_linearized(req->op.del.dn); - if (!dn) { - talloc_free(ctx); - return LDB_ERR_OPERATIONS_ERROR; - } - expression = talloc_asprintf(del_ctx, - "(|(distinguishedName=%s)(%s=%s))", - dn, DB_MEMBER, dn); - if (!expression) { - talloc_free(ctx); - return LDB_ERR_OPERATIONS_ERROR; - } - - ret = ldb_build_search_req(&search, ldb, del_ctx, - NULL, LDB_SCOPE_SUBTREE, - expression, attrs, NULL, - first, mbof_del_search_callback, - req); - if (ret != LDB_SUCCESS) { - talloc_free(ctx); - return ret; - } - - return ldb_request(ldb, search); -} - -static int mbof_del_search_callback(struct ldb_request *req, - struct ldb_reply *ares) -{ - struct mbof_del_operation *first; - struct ldb_context *ldb; - struct ldb_message *msg; - struct mbof_del_ctx *del_ctx; - struct mbof_ctx *ctx; - int ret; - - first = talloc_get_type(req->context, struct mbof_del_operation); - del_ctx = first->del_ctx; - ctx = del_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - if (!ares) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ctx->req, - ares->controls, - ares->response, - ares->error); - } - - switch (ares->type) { - case LDB_REPLY_ENTRY: - msg = ares->message; - - if (ldb_dn_compare(msg->dn, ctx->req->op.del.dn) == 0) { - - if (first->entry != NULL) { - /* more than one entry per dn ?? db corrupted ? */ - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - first->entry = talloc_steal(first, msg); - if (first->entry == NULL) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - } else { - first->parents = talloc_realloc(first, first->parents, - struct ldb_message *, - first->num_parents + 1); - if (!first->parents) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - msg = talloc_steal(first->parents, ares->message); - if (!msg) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - first->parents[first->num_parents] = msg; - first->num_parents++; - } - break; - case LDB_REPLY_REFERRAL: - /* ignore */ - break; - - case LDB_REPLY_DONE: - if (first->entry == NULL) { - /* this target does not exists, too bad! */ - ldb_debug(ldb, LDB_DEBUG_TRACE, - "Target entry (%s) not found", - ldb_dn_get_linearized(first->entry_dn)); - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_NO_SUCH_OBJECT); - } - - /* now perform the requested delete, before proceeding further */ - ret = mbof_orig_del(del_ctx); - if (ret != LDB_SUCCESS) { - talloc_zfree(ares); - return ldb_module_done(ctx->req, NULL, NULL, ret); - } - } - - talloc_zfree(ares); - return LDB_SUCCESS; -} - -static int mbof_orig_del(struct mbof_del_ctx *del_ctx) -{ - struct ldb_request *del_req; - struct mbof_ctx *ctx; - int ret; - - ctx = del_ctx->ctx; - - ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ctx->module), - ctx->req, ctx->req->op.del.dn, NULL, - del_ctx, mbof_orig_del_callback, - ctx->req); - if (ret != LDB_SUCCESS) { - return ret; - } - - return ldb_next_request(ctx->module, del_req); -} - -static int mbof_orig_del_callback(struct ldb_request *req, - struct ldb_reply *ares) -{ - struct ldb_context *ldb; - struct mbof_del_ctx *del_ctx; - struct mbof_ctx *ctx; - int ret; - - del_ctx = talloc_get_type(req->context, struct mbof_del_ctx); - ctx = del_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - if (!ares) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ctx->req, - ares->controls, - ares->response, - ares->error); - } - - if (ares->type != LDB_REPLY_DONE) { - talloc_zfree(ares); - ldb_set_errstring(ldb, "Invalid reply type!"); - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - /* save real call stuff */ - ctx->ret_ctrls = talloc_steal(ctx, ares->controls); - ctx->ret_resp = talloc_steal(ctx, ares->response); - - /* prep following clean ops */ - if (del_ctx->first->num_parents) { - - /* if there are parents there may be memberuids to remove */ - ret = mbof_del_fill_muop(del_ctx, del_ctx->first->entry); - if (ret != LDB_SUCCESS) { - talloc_zfree(ares); - return ldb_module_done(ctx->req, NULL, NULL, ret); - } - - /* if there are any parents, fire a removal sequence */ - ret = mbof_del_cleanup_parents(del_ctx); - } - else if (ldb_msg_find_element(del_ctx->first->entry, DB_MEMBER)) { - /* if there are any children, fire a removal sequence */ - ret = mbof_del_cleanup_children(del_ctx); - } - /* see if there are memberuid operations to perform */ - else if (del_ctx->muops) { - return mbof_del_muop(del_ctx); - } - else { - /* no parents nor children, end ops */ - return ldb_module_done(ctx->req, - ares->controls, - ares->response, - LDB_SUCCESS); - } - if (ret != LDB_SUCCESS) { - talloc_zfree(ares); - return ldb_module_done(ctx->req, NULL, NULL, ret); - } - - talloc_zfree(ares); - return LDB_SUCCESS; -} - -static int mbof_del_cleanup_parents(struct mbof_del_ctx *del_ctx) -{ - struct mbof_del_operation *first; - struct mbof_ctx *ctx; - struct ldb_context *ldb; - struct ldb_request *mod_req; - struct ldb_message *msg; - struct ldb_message_element *el; - const char *val; - int ret; - - first = del_ctx->first; - ctx = del_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - msg = ldb_msg_new(first->parents); - if (!msg) return LDB_ERR_OPERATIONS_ERROR; - - msg->dn = first->parents[first->cur_parent]->dn; - first->cur_parent++; - - ret = ldb_msg_add_empty(msg, DB_MEMBER, LDB_FLAG_MOD_DELETE, &el); - if (ret != LDB_SUCCESS) { - return ret; - } - el->values = talloc_array(msg, struct ldb_val, 1); - if (!el->values) { - return LDB_ERR_OPERATIONS_ERROR; - } - val = ldb_dn_get_linearized(first->entry_dn); - el->values[0].length = strlen(val); - el->values[0].data = (uint8_t *)talloc_strdup(el->values, val); - if (!el->values[0].data) { - return LDB_ERR_OPERATIONS_ERROR; - } - el->num_values = 1; - - ret = ldb_build_mod_req(&mod_req, ldb, first->parents, - msg, NULL, - del_ctx, mbof_del_clean_par_callback, - ctx->req); - if (ret != LDB_SUCCESS) { - return ret; - } - - return ldb_next_request(ctx->module, mod_req); -} - -static int mbof_del_clean_par_callback(struct ldb_request *req, - struct ldb_reply *ares) -{ - struct mbof_del_operation *first; - struct ldb_context *ldb; - struct mbof_del_ctx *del_ctx; - struct mbof_ctx *ctx; - int ret; - - del_ctx = talloc_get_type(req->context, struct mbof_del_ctx); - first = del_ctx->first; - ctx = del_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - if (!ares) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ctx->req, - ares->controls, - ares->response, - ares->error); - } - - if (ares->type != LDB_REPLY_DONE) { - talloc_zfree(ares); - ldb_set_errstring(ldb, "Invalid reply type!"); - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - if (first->num_parents > first->cur_parent) { - /* still parents to cleanup, go on */ - ret = mbof_del_cleanup_parents(del_ctx); - } - else { - /* continue */ - if (ldb_msg_find_element(first->entry, DB_MEMBER)) { - /* if there are any children, fire a removal sequence */ - ret = mbof_del_cleanup_children(del_ctx); - } - /* see if there are memberuid operations to perform */ - else if (del_ctx->muops) { - return mbof_del_muop(del_ctx); - } - else { - /* no children, end ops */ - return ldb_module_done(ctx->req, - ctx->ret_ctrls, - ctx->ret_resp, - LDB_SUCCESS); - } - } - - if (ret != LDB_SUCCESS) { - talloc_zfree(ares); - return ldb_module_done(ctx->req, NULL, NULL, ret); - } - - talloc_zfree(ares); - return LDB_SUCCESS; -} - -static int mbof_del_cleanup_children(struct mbof_del_ctx *del_ctx) -{ - struct mbof_del_operation *first; - struct mbof_ctx *ctx; - struct ldb_context *ldb; - const struct ldb_message_element *el; - struct ldb_dn *valdn; - int i, ret; - - first = del_ctx->first; - ctx = del_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - el = ldb_msg_find_element(first->entry, DB_MEMBER); - - /* prepare del sets */ - for (i = 0; i < el->num_values; i++) { - valdn = ldb_dn_from_ldb_val(first, ldb, &el->values[i]); - if (!valdn || !ldb_dn_validate(valdn)) { - ldb_debug(ldb, LDB_DEBUG_TRACE, - "Invalid dn syntax for member [%s]", - (const char *)el->values[i].data); - return LDB_ERR_INVALID_DN_SYNTAX; - } - ret = mbof_append_delop(first, valdn); - if (ret != LDB_SUCCESS) { - return ret; - } - } - - /* now that sets are built, start processing */ - return mbof_del_execute_op(first->children[0]); -} - -static int mbof_append_delop(struct mbof_del_operation *parent, - struct ldb_dn *entry_dn) -{ - struct mbof_del_operation *delop; - - delop = talloc_zero(parent, struct mbof_del_operation); - if (!delop) { - return LDB_ERR_OPERATIONS_ERROR; - } - - delop->del_ctx = parent->del_ctx; - delop->parent = parent; - delop->entry_dn = entry_dn; - - parent->children = talloc_realloc(parent, parent->children, - struct mbof_del_operation *, - parent->num_children +1); - if (!parent->children) { - talloc_free(delop); - return LDB_ERR_OPERATIONS_ERROR; - } - - parent->children[parent->num_children] = delop; - parent->num_children++; - - return LDB_SUCCESS; -} - -static int mbof_del_execute_op(struct mbof_del_operation *delop) -{ - struct mbof_del_ctx *del_ctx; - struct mbof_ctx *ctx; - struct ldb_context *ldb; - struct ldb_request *search; - char *expression; - const char *dn; - static const char *attrs[] = { DB_OC, DB_NAME, - DB_MEMBER, DB_MEMBEROF, NULL }; - int ret; - - del_ctx = delop->del_ctx; - ctx = del_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - /* load entry */ - dn = ldb_dn_get_linearized(delop->entry_dn); - if (!dn) { - return LDB_ERR_OPERATIONS_ERROR; - } - expression = talloc_asprintf(del_ctx, - "(|(distinguishedName=%s)(%s=%s))", - dn, DB_MEMBER, dn); - if (!expression) { - return LDB_ERR_OPERATIONS_ERROR; - } - - ret = ldb_build_search_req(&search, ldb, delop, - NULL, LDB_SCOPE_SUBTREE, - expression, attrs, NULL, - delop, mbof_del_exop_search_callback, - ctx->req); - if (ret != LDB_SUCCESS) { - talloc_free(ctx); - return ret; - } - - return ldb_request(ldb, search); -} - -static int mbof_del_exop_search_callback(struct ldb_request *req, - struct ldb_reply *ares) -{ - struct mbof_del_operation *delop; - struct mbof_del_ctx *del_ctx; - struct ldb_context *ldb; - struct mbof_ctx *ctx; - struct ldb_message *msg; - int ret; - - delop = talloc_get_type(req->context, struct mbof_del_operation); - del_ctx = delop->del_ctx; - ctx = del_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - if (!ares) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ctx->req, - ares->controls, - ares->response, - ares->error); - } - - switch (ares->type) { - case LDB_REPLY_ENTRY: - msg = ares->message; - - if (ldb_dn_compare(msg->dn, delop->entry_dn) == 0) { - - if (delop->entry != NULL) { - ldb_debug(ldb, LDB_DEBUG_TRACE, - "Found multiple entries for (%s)", - ldb_dn_get_linearized(delop->entry_dn)); - /* more than one entry per dn ?? db corrupted ? */ - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - delop->entry = talloc_steal(delop, msg); - if (delop->entry == NULL) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - } else { - delop->parents = talloc_realloc(delop, delop->parents, - struct ldb_message *, - delop->num_parents + 1); - if (!delop->parents) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - msg = talloc_steal(delop->parents, msg); - if (!msg) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - delop->parents[delop->num_parents] = msg; - delop->num_parents++; - } - break; - case LDB_REPLY_REFERRAL: - /* ignore */ - break; - - case LDB_REPLY_DONE: - if (delop->entry == NULL) { - /* no target, no party! */ - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - /* ok process the entry */ - ret = mbof_del_execute_cont(delop); - - if (ret != LDB_SUCCESS) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - } - - talloc_zfree(ares); - return LDB_SUCCESS; -} - -static int mbof_del_execute_cont(struct mbof_del_operation *delop) -{ - struct mbof_del_ancestors_ctx *anc_ctx; - struct mbof_del_operation *parent; - struct mbof_del_ctx *del_ctx; - struct mbof_ctx *ctx; - struct ldb_context *ldb; - struct mbof_dn_array *new_list; - int i; - - del_ctx = delop->del_ctx; - parent = delop->parent; - ctx = del_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - anc_ctx = talloc_zero(delop, struct mbof_del_ancestors_ctx); - if (!anc_ctx) { - return LDB_ERR_OPERATIONS_ERROR; - } - delop->anc_ctx = anc_ctx; - - new_list = talloc_zero(anc_ctx, struct mbof_dn_array); - if (!new_list) { - return LDB_ERR_OPERATIONS_ERROR; - } - - /* at the very least we have a number of memberof elements - * equal to the number of objects that have this entry as - * direct member */ - new_list->num = delop->num_parents; - - /* attach the list to the operation */ - delop->anc_ctx->new_list = new_list; - delop->anc_ctx->num_direct = new_list->num; - - /* do we have any direct parent at all ? */ - if (new_list->num == 0) { - /* no entries at all, entry ended up being orphaned */ - /* skip to directly set the new memberof list for this entry */ - - return mbof_del_mod_entry(delop); - } - - /* fill in the list if we have parents */ - new_list->dns = talloc_zero_array(new_list, - struct ldb_dn *, - new_list->num); - if (!new_list->dns) { - return LDB_ERR_OPERATIONS_ERROR; - } - for (i = 0; i < delop->num_parents; i++) { - new_list->dns[i] = delop->parents[i]->dn; - } - - /* before proceeding we also need to fetch the ancestors (anew as some may - * have changed by preceeding operations) */ - return mbof_del_ancestors(delop); -} - -static int mbof_del_ancestors(struct mbof_del_operation *delop) -{ - struct mbof_del_ancestors_ctx *anc_ctx; - struct mbof_del_ctx *del_ctx; - struct mbof_ctx *ctx; - struct ldb_context *ldb; - struct mbof_dn_array *new_list; - static const char *attrs[] = { DB_MEMBEROF, NULL }; - struct ldb_request *search; - int ret; - - del_ctx = delop->del_ctx; - ctx = del_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - anc_ctx = delop->anc_ctx; - new_list = anc_ctx->new_list; - - ret = ldb_build_search_req(&search, ldb, anc_ctx, - new_list->dns[anc_ctx->cur], - LDB_SCOPE_BASE, NULL, attrs, NULL, - delop, mbof_del_anc_callback, - ctx->req); - if (ret != LDB_SUCCESS) { - return ret; - } - - return ldb_request(ldb, search); -} - -static int mbof_del_anc_callback(struct ldb_request *req, - struct ldb_reply *ares) -{ - struct mbof_del_ancestors_ctx *anc_ctx; - struct mbof_del_operation *delop; - struct mbof_del_ctx *del_ctx; - struct mbof_ctx *ctx; - struct ldb_context *ldb; - struct ldb_message *msg; - const struct ldb_message_element *el; - struct mbof_dn_array *new_list; - struct ldb_dn *valdn; - int i, j, ret; - - delop = talloc_get_type(req->context, struct mbof_del_operation); - del_ctx = delop->del_ctx; - ctx = del_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - anc_ctx = delop->anc_ctx; - new_list = anc_ctx->new_list; - - if (!ares) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ctx->req, - ares->controls, - ares->response, - ares->error); - } - - switch (ares->type) { - case LDB_REPLY_ENTRY: - msg = ares->message; - - if (anc_ctx->entry != NULL) { - ldb_debug(ldb, LDB_DEBUG_TRACE, - "Found multiple entries for (%s)", - ldb_dn_get_linearized(anc_ctx->entry->dn)); - /* more than one entry per dn ?? db corrupted ? */ - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - anc_ctx->entry = talloc_steal(anc_ctx, msg); - if (anc_ctx->entry == NULL) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - break; - case LDB_REPLY_REFERRAL: - /* ignore */ - break; - - case LDB_REPLY_DONE: - if (anc_ctx->entry == NULL) { - /* no target, no party! */ - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - /* check entry */ - el = ldb_msg_find_element(anc_ctx->entry, DB_MEMBEROF); - if (el) { - for (i = 0; i < el->num_values; i++) { - valdn = ldb_dn_from_ldb_val(new_list, ldb, &el->values[i]); - if (!valdn) { - ldb_debug(ldb, LDB_DEBUG_TRACE, - "Invalid dn for memberof: (%s)", - (const char *)el->values[i].data); - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - for (j = 0; j < new_list->num; j++) { - if (ldb_dn_compare(valdn, new_list->dns[j]) == 0) - break; - } - if (j < new_list->num) { - talloc_free(valdn); - continue; - } - new_list->dns = talloc_realloc(new_list, - new_list->dns, - struct ldb_dn *, - new_list->num + 1); - if (!new_list->dns) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - new_list->dns[new_list->num] = valdn; - new_list->num++; - } - } - - /* done with this one */ - talloc_free(anc_ctx->entry); - anc_ctx->entry = NULL; - anc_ctx->cur++; - - /* check if we need to process any more */ - if (anc_ctx->cur < anc_ctx->num_direct) { - /* ok process the next one */ - ret = mbof_del_ancestors(delop); - } else { - /* ok, end of the story, proceed to modify the entry */ - ret = mbof_del_mod_entry(delop); - } - - if (ret != LDB_SUCCESS) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - } - - talloc_zfree(ares); - return LDB_SUCCESS; -} - -static int mbof_del_mod_entry(struct mbof_del_operation *delop) -{ - struct mbof_del_ctx *del_ctx; - struct mbof_ctx *ctx; - struct ldb_context *ldb; - struct mbof_dn_array *new_list; - struct ldb_request *mod_req; - struct ldb_message *msg; - struct ldb_message_element *el; - struct ldb_dn **diff = NULL; - const char *name; - const char *val; - int i, j, k; - bool is_user; - int ret; - - del_ctx = delop->del_ctx; - ctx = del_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - new_list = delop->anc_ctx->new_list; - - /* if this is a user we need to find out which entries have been - * removed so that we can later schedule removal of memberuid - * attributes from these entries */ - ret = entry_is_user_object(delop->entry); - switch (ret) { - case LDB_SUCCESS: - /* it's a user object */ - is_user = true; - break; - case LDB_ERR_NO_SUCH_ATTRIBUTE: - /* it is not a user object, continue */ - is_user = false; - break; - default: - /* an error occured, return */ - return ret; - } - - if (is_user) { - /* prepare memberuid delete list */ - /* copy all original memberof entries, and then later remove - * the ones that will survive in the entry */ - el = ldb_msg_find_element(delop->entry, DB_MEMBEROF); - if (!el || !el->num_values) { - return LDB_ERR_OPERATIONS_ERROR; - } - diff = talloc_array(del_ctx->muops, struct ldb_dn *, - el->num_values + 1); - if (!diff) { - return LDB_ERR_OPERATIONS_ERROR; - } - for (i = 0, j = 0; i < el->num_values; i++) { - diff[j] = ldb_dn_from_ldb_val(diff, ldb, &el->values[i]); - if (!diff[j]) { - return LDB_ERR_OPERATIONS_ERROR; - } - /* skip the deleted entry if this is a delete op */ - if (!del_ctx->is_mod) { - if (ldb_dn_compare(del_ctx->first->entry_dn, diff[j]) == 0) { - continue; - } - } - j++; - } - /* zero terminate array */ - diff[j] = NULL; - } - - /* change memberof on entry */ - msg = ldb_msg_new(delop); - if (!msg) return LDB_ERR_OPERATIONS_ERROR; - - msg->dn = delop->entry_dn; - - if (new_list->num) { - ret = ldb_msg_add_empty(msg, DB_MEMBEROF, LDB_FLAG_MOD_REPLACE, &el); - if (ret != LDB_SUCCESS) { - return ret; - } - - el->values = talloc_array(el, struct ldb_val, new_list->num); - if (!el->values) { - return LDB_ERR_OPERATIONS_ERROR; - } - for (i = 0, j = 0; i < new_list->num; i++) { - if (ldb_dn_compare(new_list->dns[i], msg->dn) == 0) - continue; - val = ldb_dn_get_linearized(new_list->dns[i]); - if (!val) { - return LDB_ERR_OPERATIONS_ERROR; - } - el->values[j].length = strlen(val); - el->values[j].data = (uint8_t *)talloc_strdup(el->values, val); - if (!el->values[j].data) { - return LDB_ERR_OPERATIONS_ERROR; - } - j++; - - if (is_user) { - /* compare the entry's original memberof list with the new - * one and for each missing entry add a memberuid removal - * operation */ - for (k = 0; diff[k]; k++) { - if (ldb_dn_compare(new_list->dns[i], diff[k]) == 0) { - break; - } - } - if (diff[k]) { - talloc_zfree(diff[k]); - for (; diff[k + 1]; k++) { - diff[k] = diff[k + 1]; - } - diff[k] = NULL; - } - } - } - el->num_values = j; - - } - else { - ret = ldb_msg_add_empty(msg, DB_MEMBEROF, LDB_FLAG_MOD_DELETE, &el); - if (ret != LDB_SUCCESS) { - return ret; - } - } - - if (is_user && diff[0]) { - /* file memberuid removal operations */ - name = ldb_msg_find_attr_as_string(delop->entry, DB_NAME, NULL); - if (!name) { - return LDB_ERR_OPERATIONS_ERROR; - } - - for (i = 0; diff[i]; i++) { - ret = mbof_append_muop(del_ctx, &del_ctx->muops, - &del_ctx->num_muops, - LDB_FLAG_MOD_DELETE, - diff[i], name); - if (ret != LDB_SUCCESS) { - return ret; - } - } - } - - ret = ldb_build_mod_req(&mod_req, ldb, delop, - msg, NULL, - delop, mbof_del_mod_callback, - ctx->req); - if (ret != LDB_SUCCESS) { - return ret; - } - talloc_steal(mod_req, msg); - - return ldb_next_request(ctx->module, mod_req); -} - -static int mbof_del_mod_callback(struct ldb_request *req, - struct ldb_reply *ares) -{ - struct mbof_del_operation *delop; - struct mbof_del_ctx *del_ctx; - struct ldb_context *ldb; - struct mbof_ctx *ctx; - int ret; - - delop = talloc_get_type(req->context, struct mbof_del_operation); - del_ctx = delop->del_ctx; - ctx = del_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - if (!ares) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ctx->req, - ares->controls, - ares->response, - ares->error); - } - - switch (ares->type) { - case LDB_REPLY_ENTRY: - ldb_debug(ldb, LDB_DEBUG_TRACE, "Got an entry on a non search op ?!"); - /* shouldn't happen */ - talloc_zfree(ares); - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - case LDB_REPLY_REFERRAL: - /* ignore */ - talloc_zfree(ares); - break; - - case LDB_REPLY_DONE: - - ret = mbof_del_progeny(delop); - - if (ret != LDB_SUCCESS) { - talloc_zfree(ares); - return ldb_module_done(ctx->req, NULL, NULL, ret); - } - } - - return LDB_SUCCESS; -} - -static int mbof_mod_add(struct mbof_mod_ctx *mod_ctx, - struct mbof_dn_array *ael); - -static int mbof_del_progeny(struct mbof_del_operation *delop) -{ - struct mbof_ctx *ctx; - struct mbof_del_ctx *del_ctx; - struct mbof_del_operation *nextop; - const struct ldb_message_element *el; - struct ldb_context *ldb; - struct ldb_dn *valdn; - int i, ret; - - del_ctx = delop->del_ctx; - ctx = del_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - /* now verify if this entry is a group and members need to be processed as - * well */ - - el = ldb_msg_find_element(delop->entry, DB_MEMBER); - if (el) { - for (i = 0; i < el->num_values; i++) { - valdn = ldb_dn_from_ldb_val(delop, ldb, &el->values[i]); - if (!valdn || !ldb_dn_validate(valdn)) { - ldb_debug(ldb, LDB_DEBUG_TRACE, - "Invalid DN for member: (%s)", - (const char *)el->values[i].data); - return LDB_ERR_INVALID_DN_SYNTAX; - } - ret = mbof_append_delop(delop, valdn); - if (ret != LDB_SUCCESS) { - return ret; - } - } - } - - /* finally find the next entry to handle */ - ret = mbof_del_get_next(delop, &nextop); - if (ret != LDB_SUCCESS) { - return ret; - } - - if (nextop) { - return mbof_del_execute_op(nextop); - } - - /* see if there are memberuid operations to perform */ - if (del_ctx->muops) { - return mbof_del_muop(del_ctx); - } - /* see if there are follow functions to run */ - if (del_ctx->follow_mod) { - return mbof_mod_add(del_ctx->follow_mod, - del_ctx->follow_mod->to_add); - } - - /* ok, no more ops, this means our job is done */ - return ldb_module_done(ctx->req, - ctx->ret_ctrls, - ctx->ret_resp, - LDB_SUCCESS); -} - -static int mbof_del_get_next(struct mbof_del_operation *delop, - struct mbof_del_operation **nextop) -{ - struct mbof_del_operation *top, *cop; - struct mbof_del_ctx *del_ctx; - struct mbof_ctx *ctx; - struct mbof_dn *save, *tmp; - - del_ctx = delop->del_ctx; - ctx = del_ctx->ctx; - - /* first of all, save the current delop in the history */ - save = talloc_zero(del_ctx, struct mbof_dn); - if (!save) { - return LDB_ERR_OPERATIONS_ERROR; - } - save->dn = delop->entry_dn; - - if (del_ctx->history) { - tmp = del_ctx->history; - while (tmp->next) tmp = tmp->next; - tmp->next = save; - } else { - del_ctx->history = save; - } - - /* Find next one */ - for (top = delop; top; top = top->parent) { - if (top->num_children == 0 || top->next_child >= top->num_children) { - /* no children, go for next one */ - continue; - } - - while (top->next_child < top->num_children) { - cop = top->children[top->next_child]; - top->next_child++; - - /* verify this operation has not already been performed */ - for (tmp = del_ctx->history; tmp; tmp = tmp->next) { - if (ldb_dn_compare(tmp->dn, cop->entry_dn) == 0) { - break; - } - } - if (tmp == NULL) { - /* and return the current one */ - *nextop = cop; - return LDB_SUCCESS; - } - } - } - - /* we have no more ops */ - *nextop = NULL; - return LDB_SUCCESS; -} - -static int mbof_del_fill_muop(struct mbof_del_ctx *del_ctx, - struct ldb_message *entry) -{ - struct ldb_message_element *el; - char *name; - int ret; - int i; - - el = ldb_msg_find_element(entry, DB_MEMBEROF); - if (!el || el->num_values == 0) { - /* no memberof attributes ... */ - return LDB_SUCCESS; - } - - ret = entry_is_user_object(entry); - switch (ret) { - case LDB_SUCCESS: - /* it's a user object, continue */ - break; - - case LDB_ERR_NO_SUCH_ATTRIBUTE: - /* it is not a user object, just return */ - return LDB_SUCCESS; - - default: - /* an error occured, return */ - return ret; - } - - name = talloc_strdup(del_ctx, - ldb_msg_find_attr_as_string(entry, DB_NAME, NULL)); - if (!name) { - return LDB_ERR_OPERATIONS_ERROR; - } - - for (i = 0; i < el->num_values; i++) { - struct ldb_dn *valdn; - - valdn = ldb_dn_from_ldb_val(del_ctx->muops, - ldb_module_get_ctx(del_ctx->ctx->module), - &el->values[i]); - if (!valdn || !ldb_dn_validate(valdn)) { - ldb_debug(ldb_module_get_ctx(del_ctx->ctx->module), - LDB_DEBUG_ERROR, - "Invalid dn value: [%s]", - (const char *)el->values[i].data); - } - - ret = mbof_append_muop(del_ctx, &del_ctx->muops, - &del_ctx->num_muops, - LDB_FLAG_MOD_DELETE, - valdn, name); - if (ret != LDB_SUCCESS) { - return ret; - } - } - - return LDB_SUCCESS; -} - -/* del memberuid attributes to parent groups */ -static int mbof_del_muop(struct mbof_del_ctx *del_ctx) -{ - struct ldb_context *ldb; - struct ldb_message *msg; - struct ldb_request *mod_req; - struct mbof_ctx *ctx; - int ret; - - ctx = del_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - msg = ldb_msg_new(del_ctx); - if (!msg) return LDB_ERR_OPERATIONS_ERROR; - - msg->dn = del_ctx->muops[del_ctx->cur_muop].dn; - msg->elements = del_ctx->muops[del_ctx->cur_muop].el; - msg->num_elements = 1; - - ret = ldb_build_mod_req(&mod_req, ldb, del_ctx, - msg, NULL, - del_ctx, mbof_del_muop_callback, - ctx->req); - if (ret != LDB_SUCCESS) { - return ret; - } - - return ldb_next_request(ctx->module, mod_req); -} - -static int mbof_del_muop_callback(struct ldb_request *req, - struct ldb_reply *ares) -{ - struct mbof_del_ctx *del_ctx; - struct mbof_ctx *ctx; - int ret; - - del_ctx = talloc_get_type(req->context, struct mbof_del_ctx); - ctx = del_ctx->ctx; - - if (!ares) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ctx->req, - ares->controls, - ares->response, - ares->error); - } - - switch (ares->type) { - case LDB_REPLY_ENTRY: - /* shouldn't happen */ - talloc_zfree(ares); - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - case LDB_REPLY_REFERRAL: - /* ignore */ - break; - - case LDB_REPLY_DONE: - del_ctx->cur_muop++; - if (del_ctx->cur_muop < del_ctx->num_muops) { - ret = mbof_del_muop(del_ctx); - } - /* see if there are follow functions to run */ - else if (del_ctx->follow_mod) { - return mbof_mod_add(del_ctx->follow_mod, - del_ctx->follow_mod->to_add); - } - else { - return ldb_module_done(ctx->req, - ctx->ret_ctrls, - ctx->ret_resp, - LDB_SUCCESS); - } - - if (ret != LDB_SUCCESS) { - talloc_zfree(ares); - return ldb_module_done(ctx->req, NULL, NULL, ret); - } - } - - talloc_zfree(ares); - return LDB_SUCCESS; -} - - - -/* mod operation */ - -/* A modify operation just implements either an add operation, or a delete - * operation or both (replace) in turn. - * The only difference between a modify and a pure add or a pure delete is that - * the object is not created a new or not completely removed, but the setup just - * treats it in the same way children objects are treated in a pure add or delete - * operation. A list of appropriate parents and objects to modify is built, then - * we jump directly in the add or delete code. - * If both add and delete are necessary, delete operations are performed first - * and then a followup add operation is concatenated */ - -static int mbof_mod_callback(struct ldb_request *req, - struct ldb_reply *ares); -static int mbof_orig_mod(struct mbof_mod_ctx *mod_ctx); -static int mbof_orig_mod_callback(struct ldb_request *req, - struct ldb_reply *ares); -static int mbof_mod_process(struct mbof_mod_ctx *mod_ctx, bool *done); -static int mbof_mod_delete(struct mbof_mod_ctx *mod_ctx, - struct mbof_dn_array *del); -static int mbof_fill_dn_array(TALLOC_CTX *memctx, - struct ldb_context *ldb, - const struct ldb_message_element *el, - struct mbof_dn_array **dn_array); - -static int memberof_mod(struct ldb_module *module, struct ldb_request *req) -{ - struct ldb_message_element *el; - struct mbof_mod_ctx *mod_ctx; - struct mbof_ctx *ctx; - static const char *attrs[] = {DB_MEMBER, DB_MEMBEROF, NULL}; - struct ldb_context *ldb = ldb_module_get_ctx(module); - struct ldb_request *search; - int ret; - - if (ldb_dn_is_special(req->op.mod.message->dn)) { - /* do not manipulate our control entries */ - return ldb_next_request(module, req); - } - - /* check if memberof is specified */ - el = ldb_msg_find_element(req->op.mod.message, DB_MEMBEROF); - if (el) { - ldb_debug(ldb, LDB_DEBUG_ERROR, - "Error: the memberof attribute is readonly."); - return LDB_ERR_UNWILLING_TO_PERFORM; - } - - /* check if memberuid is specified */ - el = ldb_msg_find_element(req->op.mod.message, DB_MEMBERUID); - if (el) { - ldb_debug(ldb, LDB_DEBUG_ERROR, - "Error: the memberuid attribute is readonly."); - return LDB_ERR_UNWILLING_TO_PERFORM; - } - - ctx = mbof_init(module, req); - if (!ctx) { - return LDB_ERR_OPERATIONS_ERROR; - } - - mod_ctx = talloc_zero(ctx, struct mbof_mod_ctx); - if (!mod_ctx) { - talloc_free(ctx); - return LDB_ERR_OPERATIONS_ERROR; - } - mod_ctx->ctx = ctx; - - mod_ctx->msg = ldb_msg_copy(mod_ctx, req->op.mod.message); - if (!mod_ctx->msg) { - return LDB_ERR_OPERATIONS_ERROR; - } - - /* continue with normal ops if there are no members */ - el = ldb_msg_find_element(mod_ctx->msg, DB_MEMBER); - if (!el) { - mod_ctx->terminate = true; - return mbof_orig_mod(mod_ctx); - } - - mod_ctx->membel = el; - - /* can't do anything, - * must check first what's on the entry */ - ret = ldb_build_search_req(&search, ldb, mod_ctx, - mod_ctx->msg->dn, LDB_SCOPE_BASE, - NULL, attrs, NULL, - mod_ctx, mbof_mod_callback, - req); - if (ret != LDB_SUCCESS) { - talloc_free(ctx); - return ret; - } - - return ldb_request(ldb, search); -} - - -static int mbof_mod_callback(struct ldb_request *req, - struct ldb_reply *ares) -{ - struct mbof_mod_ctx *mod_ctx; - struct ldb_context *ldb; - struct mbof_ctx *ctx; - int ret; - - mod_ctx = talloc_get_type(req->context, struct mbof_mod_ctx); - ctx = mod_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - if (!ares) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ctx->req, - ares->controls, - ares->response, - ares->error); - } - - switch (ares->type) { - case LDB_REPLY_ENTRY: - if (mod_ctx->entry != NULL) { - ldb_debug(ldb, LDB_DEBUG_TRACE, - "Found multiple entries for (%s)", - ldb_dn_get_linearized(mod_ctx->msg->dn)); - /* more than one entry per dn ?? db corrupted ? */ - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - mod_ctx->entry = talloc_steal(mod_ctx, ares->message); - if (mod_ctx->entry == NULL) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - break; - case LDB_REPLY_REFERRAL: - /* ignore */ - break; - - case LDB_REPLY_DONE: - if (mod_ctx->entry == NULL) { - ldb_debug(ldb, LDB_DEBUG_TRACE, "Entry not found (%s)", - ldb_dn_get_linearized(mod_ctx->msg->dn)); - /* this target does not exists, too bad! */ - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_NO_SUCH_OBJECT); - } - - ret = mbof_orig_mod(mod_ctx); - if (ret != LDB_SUCCESS) { - talloc_zfree(ares); - return ldb_module_done(ctx->req, NULL, NULL, ret); - } - } - - talloc_zfree(ares); - return LDB_SUCCESS; -} - -static int mbof_orig_mod(struct mbof_mod_ctx *mod_ctx) -{ - struct ldb_request *mod_req; - struct ldb_context *ldb; - struct mbof_ctx *ctx; - int ret; - - ctx = mod_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - ret = ldb_build_mod_req(&mod_req, ldb, ctx->req, - mod_ctx->msg, ctx->req->controls, - mod_ctx, mbof_orig_mod_callback, - ctx->req); - if (ret != LDB_SUCCESS) { - return ret; - } - - return ldb_next_request(ctx->module, mod_req); -} - -static int mbof_orig_mod_callback(struct ldb_request *req, - struct ldb_reply *ares) -{ - struct ldb_context *ldb; - struct mbof_mod_ctx *mod_ctx; - struct mbof_ctx *ctx; - int ret; - - mod_ctx = talloc_get_type(req->context, struct mbof_mod_ctx); - ctx = mod_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - if (!ares) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ctx->req, - ares->controls, - ares->response, - ares->error); - } - - if (ares->type != LDB_REPLY_DONE) { - talloc_zfree(ares); - ldb_debug(ldb, LDB_DEBUG_TRACE, "Invalid reply type!"); - ldb_set_errstring(ldb, "Invalid reply type!"); - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - /* save real call stuff */ - ctx->ret_ctrls = talloc_steal(ctx, ares->controls); - ctx->ret_resp = talloc_steal(ctx, ares->response); - - if (!mod_ctx->terminate) { - /* next step */ - ret = mbof_mod_process(mod_ctx, &mod_ctx->terminate); - if (ret != LDB_SUCCESS) { - talloc_zfree(ares); - return ldb_module_done(ctx->req, NULL, NULL, ret); - } - } - - if (mod_ctx->terminate) { - talloc_zfree(ares); - return ldb_module_done(ctx->req, - ctx->ret_ctrls, - ctx->ret_resp, - LDB_SUCCESS); - } - - talloc_zfree(ares); - return LDB_SUCCESS; -} - -static int mbof_mod_process(struct mbof_mod_ctx *mod_ctx, bool *done) -{ - const struct ldb_message_element *el; - struct ldb_context *ldb; - struct mbof_ctx *ctx; - struct mbof_dn_array *removed; - struct mbof_dn_array *added; - int i, j, ret; - - ctx = mod_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - switch (mod_ctx->membel->flags) { - case LDB_FLAG_MOD_ADD: - - ret = mbof_fill_dn_array(mod_ctx, ldb, mod_ctx->membel, &added); - if (ret != LDB_SUCCESS) { - return ret; - } - - return mbof_mod_add(mod_ctx, added); - - case LDB_FLAG_MOD_DELETE: - - if (mod_ctx->membel->num_values == 0) { - el = ldb_msg_find_element(mod_ctx->entry, DB_MEMBER); - } else { - el = mod_ctx->membel; - } - - if (!el) { - /* nothing to do really */ - *done = true; - return LDB_SUCCESS; - } - - ret = mbof_fill_dn_array(mod_ctx, ldb, el, &removed); - if (ret != LDB_SUCCESS) { - return ret; - } - - return mbof_mod_delete(mod_ctx, removed); - - case LDB_FLAG_MOD_REPLACE: - - removed = NULL; - el = ldb_msg_find_element(mod_ctx->entry, DB_MEMBER); - if (el) { - ret = mbof_fill_dn_array(mod_ctx, ldb, el, &removed); - if (ret != LDB_SUCCESS) { - return ret; - } - } - - added = NULL; - el = mod_ctx->membel; - if (el) { - ret = mbof_fill_dn_array(mod_ctx, ldb, el, &added); - if (ret != LDB_SUCCESS) { - return ret; - } - } - - /* remove from arrays values that ended up unchanged */ - if (removed && removed->num && added && added->num) { - for (i = 0; i < added->num; i++) { - for (j = 0; j < removed->num; j++) { - if (ldb_dn_compare(added->dns[i], removed->dns[j]) == 0) { - break; - } - } - if (j < removed->num) { - /* preexisting one, not removed, nor added */ - for (; j+1 < removed->num; j++) { - removed->dns[j] = removed->dns[j+1]; - } - removed->num--; - for (j = i; j+1 < added->num; j++) { - added->dns[j] = added->dns[j+1]; - } - added->num--; - } - } - } - - /* if we need to add something put it away so that it - * can be done after all delete operations are over */ - if (added && added->num) { - mod_ctx->to_add = added; - } - - /* if we have something to remove do it first */ - if (removed && removed->num) { - return mbof_mod_delete(mod_ctx, removed); - } - - /* if there is nothing to remove and we have stuff to add - * do it right away */ - if (mod_ctx->to_add) { - return mbof_mod_add(mod_ctx, added); - } - - /* the replacement function resulted in a null op, - * nothing to do, return happily */ - *done = true; - return LDB_SUCCESS; - } - - return LDB_ERR_OPERATIONS_ERROR; -} - -static int mbof_mod_add(struct mbof_mod_ctx *mod_ctx, - struct mbof_dn_array *ael) -{ - const struct ldb_message_element *el; - struct mbof_dn_array *parents; - struct mbof_add_ctx *add_ctx; - struct ldb_context *ldb; - struct mbof_ctx *ctx; - int i, ret; - - ctx = mod_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - el = ldb_msg_find_element(mod_ctx->entry, DB_MEMBEROF); - - /* all the parents + itself */ - ret = mbof_fill_dn_array(mod_ctx, ldb, el, &parents); - if (ret != LDB_SUCCESS) { - return ret; - } - - parents->dns = talloc_realloc(parents, parents->dns, - struct ldb_dn *, parents->num + 1); - if (!parents->dns) { - return LDB_ERR_OPERATIONS_ERROR; - } - parents->dns[parents->num] = mod_ctx->entry->dn; - parents->num++; - - add_ctx = talloc_zero(mod_ctx, struct mbof_add_ctx); - if (!add_ctx) { - return LDB_ERR_OPERATIONS_ERROR; - } - add_ctx->ctx = ctx; - add_ctx->msg_dn = mod_ctx->msg->dn; - - for (i = 0; i < ael->num; i++) { - ret = mbof_append_addop(add_ctx, parents, ael->dns[i]); - if (ret != LDB_SUCCESS) { - return ret; - } - } - - return mbof_next_add(add_ctx->add_list); -} - -static int mbof_mod_delete(struct mbof_mod_ctx *mod_ctx, - struct mbof_dn_array *del) -{ - struct mbof_del_operation *first; - struct mbof_del_ctx *del_ctx; - struct ldb_context *ldb; - struct mbof_ctx *ctx; - int i, ret; - - ctx = mod_ctx->ctx; - ldb = ldb_module_get_ctx(ctx->module); - - del_ctx = talloc_zero(mod_ctx, struct mbof_del_ctx); - if (!del_ctx) { - return LDB_ERR_OPERATIONS_ERROR; - } - del_ctx->ctx = ctx; - del_ctx->is_mod = true; - - /* create first entry */ - /* the first entry is the parent of all entries and the one where we - * remove member from, it does not get the same treatment as others */ - first = talloc_zero(del_ctx, struct mbof_del_operation); - if (!first) { - return LDB_ERR_OPERATIONS_ERROR; - } - del_ctx->first = first; - - first->del_ctx = del_ctx; - first->entry = mod_ctx->entry; - first->entry_dn = mod_ctx->entry->dn; - - /* prepare del sets */ - for (i = 0; i < del->num; i++) { - ret = mbof_append_delop(first, del->dns[i]); - if (ret != LDB_SUCCESS) { - return ret; - } - } - - /* add followup function if we also have stuff to add */ - if (mod_ctx->to_add) { - del_ctx->follow_mod = mod_ctx; - } - - /* now that sets are built, start processing */ - return mbof_del_execute_op(first->children[0]); -} - -static int mbof_fill_dn_array(TALLOC_CTX *memctx, - struct ldb_context *ldb, - const struct ldb_message_element *el, - struct mbof_dn_array **dn_array) -{ - struct mbof_dn_array *ar; - struct ldb_dn *valdn; - int i; - - ar = talloc_zero(memctx, struct mbof_dn_array); - if (!ar) { - return LDB_ERR_OPERATIONS_ERROR; - } - *dn_array = ar; - - if (!el || el->num_values == 0) { - return LDB_SUCCESS; - } - - ar->dns = talloc_array(ar, struct ldb_dn *, el->num_values); - if (!ar->dns) { - return LDB_ERR_OPERATIONS_ERROR; - } - ar->num = el->num_values; - - for (i = 0; i < ar->num; i++) { - valdn = ldb_dn_from_ldb_val(ar, ldb, &el->values[i]); - if (!valdn || !ldb_dn_validate(valdn)) { - ldb_debug(ldb, LDB_DEBUG_TRACE, "Invalid dn value: [%s]", - (const char *)el->values[i].data); - return LDB_ERR_INVALID_DN_SYNTAX; - } - ar->dns[i] = valdn; - } - - return LDB_SUCCESS; -} - - -/************************* - * Cleanup task routines * - *************************/ - -struct mbof_member { - struct mbof_member *prev; - struct mbof_member *next; - - struct ldb_dn *dn; - const char *name; - bool orig_has_memberof; - bool orig_has_memberuid; - struct ldb_message_element *orig_members; - - struct mbof_member **members; - - hash_table_t *memberofs; - - struct ldb_message_element *memuids; - - enum { MBOF_GROUP_TO_DO = 0, - MBOF_GROUP_DONE, - MBOF_USER, - MBOF_ITER_ERROR } status; -}; - -struct mbof_rcmp_context { - struct ldb_module *module; - struct ldb_request *req; - - struct mbof_member *user_list; - hash_table_t *user_table; - - struct mbof_member *group_list; - hash_table_t *group_table; -}; - -static void *hash_alloc(const size_t size, void *pvt) -{ - return talloc_size(pvt, size); -} - -static void hash_free(void *ptr, void *pvt) -{ - talloc_free(ptr); -} - -static int mbof_steal_msg_el(TALLOC_CTX *memctx, - const char *name, - struct ldb_message *msg, - struct ldb_message_element **_dest) -{ - struct ldb_message_element *src; - struct ldb_message_element *dest; - - src = ldb_msg_find_element(msg, name); - if (!src) { - return LDB_ERR_NO_SUCH_ATTRIBUTE; - } - - dest = talloc_zero(memctx, struct ldb_message_element); - if (!dest) { - return LDB_ERR_OPERATIONS_ERROR; - } - - *dest = *src; - talloc_steal(dest, dest->name); - talloc_steal(dest, dest->values); - - *_dest = dest; - return LDB_SUCCESS; -} - -static int mbof_rcmp_usr_callback(struct ldb_request *req, - struct ldb_reply *ares); -static int mbof_rcmp_search_groups(struct mbof_rcmp_context *ctx); -static int mbof_rcmp_grp_callback(struct ldb_request *req, - struct ldb_reply *ares); -static int mbof_member_update(struct mbof_rcmp_context *ctx, - struct mbof_member *parent, - struct mbof_member *mem); -static bool mbof_member_iter(hash_entry_t *item, void *user_data); -static int mbof_add_memuid(struct mbof_member *grp, const char *user); -static int mbof_rcmp_update(struct mbof_rcmp_context *ctx); -static int mbof_rcmp_mod_callback(struct ldb_request *req, - struct ldb_reply *ares); - -static int memberof_recompute_task(struct ldb_module *module, - struct ldb_request *req) -{ - struct ldb_context *ldb = ldb_module_get_ctx(module); - static const char *attrs[] = { DB_NAME, DB_MEMBEROF, NULL }; - static const char *filter = "(objectclass=user)"; - struct mbof_rcmp_context *ctx; - struct ldb_request *src_req; - int ret; - - ctx = talloc_zero(req, struct mbof_rcmp_context); - if (!ctx) { - return LDB_ERR_OPERATIONS_ERROR; - } - ctx->module = module; - ctx->req = req; - - ret = hash_create_ex(1024, &ctx->user_table, 0, 0, 0, 0, - hash_alloc, hash_free, ctx, NULL, NULL); - if (ret != HASH_SUCCESS) { - return LDB_ERR_OPERATIONS_ERROR; - } - - ret = ldb_build_search_req(&src_req, ldb, ctx, - NULL, LDB_SCOPE_SUBTREE, - filter, attrs, NULL, - ctx, mbof_rcmp_usr_callback, ctx->req); - if (ret != LDB_SUCCESS) { - return ret; - } - - return ldb_request(ldb, src_req); -} - -static int mbof_rcmp_usr_callback(struct ldb_request *req, - struct ldb_reply *ares) -{ - struct mbof_rcmp_context *ctx; - struct mbof_member *usr; - hash_value_t value; - hash_key_t key; - const char *name; - int ret; - - ctx = talloc_get_type(req->context, struct mbof_rcmp_context); - - if (!ares) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ctx->req, - ares->controls, - ares->response, - ares->error); - } - - switch (ares->type) { - case LDB_REPLY_ENTRY: - - usr = talloc_zero(ctx, struct mbof_member); - if (!usr) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - usr->status = MBOF_USER; - usr->dn = talloc_steal(usr, ares->message->dn); - name = ldb_msg_find_attr_as_string(ares->message, DB_NAME, NULL); - if (name) { - usr->name = talloc_steal(usr, name); - } - - if (ldb_msg_find_element(ares->message, DB_MEMBEROF)) { - usr->orig_has_memberof = true; - } - - DLIST_ADD(ctx->user_list, usr); - - key.type = HASH_KEY_STRING; - key.str = discard_const(ldb_dn_get_linearized(usr->dn)); - value.type = HASH_VALUE_PTR; - value.ptr = usr; - - ret = hash_enter(ctx->user_table, &key, &value); - if (ret != HASH_SUCCESS) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - break; - - case LDB_REPLY_REFERRAL: - /* ignore */ - break; - - case LDB_REPLY_DONE: - talloc_zfree(ares); - - /* and now search groups */ - return mbof_rcmp_search_groups(ctx); - } - - talloc_zfree(ares); - return LDB_SUCCESS; -} - -static int mbof_rcmp_search_groups(struct mbof_rcmp_context *ctx) -{ - struct ldb_context *ldb = ldb_module_get_ctx(ctx->module); - static const char *attrs[] = { DB_MEMBEROF, DB_MEMBERUID, - DB_NAME, DB_MEMBER, NULL }; - static const char *filter = "(objectclass=group)"; - struct ldb_request *req; - int ret; - - ret = hash_create_ex(1024, &ctx->group_table, 0, 0, 0, 0, - hash_alloc, hash_free, ctx, NULL, NULL); - if (ret != HASH_SUCCESS) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - ret = ldb_build_search_req(&req, ldb, ctx, - NULL, LDB_SCOPE_SUBTREE, - filter, attrs, NULL, - ctx, mbof_rcmp_grp_callback, ctx->req); - if (ret != LDB_SUCCESS) { - return ret; - } - - return ldb_request(ldb, req); -} - -static int mbof_rcmp_grp_callback(struct ldb_request *req, - struct ldb_reply *ares) -{ - struct ldb_context *ldb; - struct mbof_rcmp_context *ctx; - struct ldb_message_element *el; - struct mbof_member *iter; - struct mbof_member *grp; - hash_value_t value; - hash_key_t key; - const char *name; - int i, j; - int ret; - - ctx = talloc_get_type(req->context, struct mbof_rcmp_context); - ldb = ldb_module_get_ctx(ctx->module); - - if (!ares) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ctx->req, - ares->controls, - ares->response, - ares->error); - } - - switch (ares->type) { - case LDB_REPLY_ENTRY: - - grp = talloc_zero(ctx, struct mbof_member); - if (!grp) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - grp->status = MBOF_GROUP_TO_DO; - grp->dn = talloc_steal(grp, ares->message->dn); - grp->name = ldb_msg_find_attr_as_string(ares->message, DB_NAME, NULL); - name = ldb_msg_find_attr_as_string(ares->message, DB_NAME, NULL); - if (name) { - grp->name = talloc_steal(grp, name); - } - - if (ldb_msg_find_element(ares->message, DB_MEMBEROF)) { - grp->orig_has_memberof = true; - } - - if (ldb_msg_find_element(ares->message, DB_MEMBERUID)) { - grp->orig_has_memberuid = true; - } - - ret = mbof_steal_msg_el(grp, DB_MEMBER, - ares->message, &grp->orig_members); - if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_ATTRIBUTE) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - DLIST_ADD(ctx->group_list, grp); - - key.type = HASH_KEY_STRING; - key.str = discard_const(ldb_dn_get_linearized(grp->dn)); - value.type = HASH_VALUE_PTR; - value.ptr = grp; - - ret = hash_enter(ctx->group_table, &key, &value); - if (ret != HASH_SUCCESS) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - break; - - case LDB_REPLY_REFERRAL: - /* ignore */ - break; - - case LDB_REPLY_DONE: - talloc_zfree(ares); - - if (!ctx->group_list) { - /* no groups ? */ - return ldb_module_done(ctx->req, NULL, NULL, LDB_SUCCESS); - } - - /* for each group compute the members list */ - for (iter = ctx->group_list; iter; iter = iter->next) { - - el = iter->orig_members; - if (!el || el->num_values == 0) { - /* no members */ - continue; - } - - /* we have at most num_values group members */ - iter->members = talloc_array(iter, struct mbof_member *, - el->num_values +1); - if (!iter->members) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - for (i = 0, j = 0; i < el->num_values; i++) { - key.type = HASH_KEY_STRING; - key.str = (char *)el->values[i].data; - - ret = hash_lookup(ctx->user_table, &key, &value); - switch (ret) { - case HASH_SUCCESS: - iter->members[j] = (struct mbof_member *)value.ptr; - j++; - break; - - case HASH_ERROR_KEY_NOT_FOUND: - /* not a user, see if it is a group */ - - ret = hash_lookup(ctx->group_table, &key, &value); - if (ret != HASH_SUCCESS) { - if (ret != HASH_ERROR_KEY_NOT_FOUND) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - } - if (ret == HASH_ERROR_KEY_NOT_FOUND) { - /* not a known user, nor a known group ? - give a warning an continue */ - ldb_debug(ldb, LDB_DEBUG_ERROR, - "member attribute [%s] has no corresponding" - " entry!", key.str); - break; - } - - iter->members[j] = (struct mbof_member *)value.ptr; - j++; - break; - - default: - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - } - /* terminate */ - iter->members[j] = NULL; - - talloc_zfree(iter->orig_members); - } - - /* now generate correct memberof tables */ - while (ctx->group_list->status == MBOF_GROUP_TO_DO) { - - grp = ctx->group_list; - - /* move to end of list and mark as done. - * NOTE: this is not efficient, but will do for now */ - DLIST_DEMOTE(ctx->group_list, grp, struct mbof_member *); - grp->status = MBOF_GROUP_DONE; - - /* verify if members need updating */ - if (!grp->members) { - continue; - } - for (i = 0; grp->members[i]; i++) { - ret = mbof_member_update(ctx, grp, grp->members[i]); - if (ret != LDB_SUCCESS) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - } - } - - /* ok all done, now go on and modify the tree */ - return mbof_rcmp_update(ctx); - } - - talloc_zfree(ares); - return LDB_SUCCESS; -} - -static int mbof_member_update(struct mbof_rcmp_context *ctx, - struct mbof_member *parent, - struct mbof_member *mem) -{ - hash_value_t value; - hash_key_t key; - int ret; - - /* ignore loops */ - if (parent == mem) return LDB_SUCCESS; - - key.type = HASH_KEY_STRING; - key.str = discard_const(ldb_dn_get_linearized(parent->dn)); - - if (!mem->memberofs) { - ret = hash_create_ex(32, &mem->memberofs, 0, 0, 0, 0, - hash_alloc, hash_free, mem, NULL, NULL); - if (ret != HASH_SUCCESS) { - return LDB_ERR_OPERATIONS_ERROR; - } - - ret = HASH_ERROR_KEY_NOT_FOUND; - - } else { - - ret = hash_lookup(mem->memberofs, &key, &value); - if (ret != HASH_SUCCESS) { - if (ret != HASH_ERROR_KEY_NOT_FOUND) { - /* fatal error */ - return LDB_ERR_OPERATIONS_ERROR; - } - } - } - - if (ret == HASH_ERROR_KEY_NOT_FOUND) { - - /* it's missing, update member */ - value.type = HASH_VALUE_PTR; - value.ptr = parent; - - ret = hash_enter(mem->memberofs, &key, &value); - if (ret != HASH_SUCCESS) { - return LDB_ERR_OPERATIONS_ERROR; - } - - if (mem->status == MBOF_USER) { - /* add corresponding memuid to the group */ - ret = mbof_add_memuid(parent, mem->name); - if (ret != LDB_SUCCESS) { - return ret; - } - } - - /* if we updated a group, mark it as TO DO again */ - if (mem->status == MBOF_GROUP_DONE) { - mem->status = MBOF_GROUP_TO_DO; - } - } - - /* now see if the parent has memberofs to pass down */ - if (parent->memberofs) { - ret = hash_iterate(parent->memberofs, mbof_member_iter, mem); - if (ret != HASH_SUCCESS) { - return LDB_ERR_OPERATIONS_ERROR; - } - if (mem->status == MBOF_ITER_ERROR) { - return LDB_ERR_OPERATIONS_ERROR; - } - } - - /* finally, if it was made TO DO move it to the head */ - if (mem->status == MBOF_GROUP_TO_DO) { - DLIST_PROMOTE(ctx->group_list, mem); - } - - return LDB_SUCCESS; -} - -static bool mbof_member_iter(hash_entry_t *item, void *user_data) -{ - struct mbof_member *parent; - struct mbof_member *mem; - hash_value_t value; - int ret; - - mem = talloc_get_type(user_data, struct mbof_member); - - /* exclude self */ - if (strcmp(item->key.str, ldb_dn_get_linearized(mem->dn)) == 0) { - return true; - } - - /* check if we already have it */ - ret = hash_lookup(mem->memberofs, &item->key, &value); - if (ret != HASH_SUCCESS) { - if (ret != HASH_ERROR_KEY_NOT_FOUND) { - /* fatal error */ - mem->status = MBOF_ITER_ERROR; - return false; - } - - /* was not already here, add it and mark group as TO DO */ - ret = hash_enter(mem->memberofs, &item->key, &item->value); - if (ret != HASH_SUCCESS) { - return LDB_ERR_OPERATIONS_ERROR; - } - - if (mem->status == MBOF_GROUP_DONE) { - mem->status = MBOF_GROUP_TO_DO; - } - - if (mem->status == MBOF_USER) { - /* add corresponding memuid to the group */ - parent = (struct mbof_member *)item->value.ptr; - ret = mbof_add_memuid(parent, mem->name); - if (ret != LDB_SUCCESS) { - mem->status = MBOF_ITER_ERROR; - return false; - } - } - } - - return true; -} - -static int mbof_add_memuid(struct mbof_member *grp, const char *user) -{ - struct ldb_val *vals; - int n; - - if (!grp->memuids) { - grp->memuids = talloc_zero(grp, struct ldb_message_element); - if (!grp->memuids) { - return LDB_ERR_OPERATIONS_ERROR; - } - - grp->memuids->name = talloc_strdup(grp->memuids, DB_MEMBERUID); - if (!grp->memuids->name) { - return LDB_ERR_OPERATIONS_ERROR; - } - } - - n = grp->memuids->num_values; - vals = talloc_realloc(grp->memuids, - grp->memuids->values, - struct ldb_val, n + 1); - if (!vals) { - return LDB_ERR_OPERATIONS_ERROR; - } - - vals[n].data = (uint8_t *)talloc_strdup(vals, user); - vals[n].length = strlen(user); - - grp->memuids->values = vals; - grp->memuids->num_values = n + 1; - - return LDB_SUCCESS; -} - -static int mbof_rcmp_update(struct mbof_rcmp_context *ctx) -{ - struct ldb_context *ldb = ldb_module_get_ctx(ctx->module); - struct ldb_message_element *el; - struct ldb_message *msg = NULL; - struct ldb_request *req; - struct mbof_member *x = NULL; - hash_key_t *keys; - unsigned long count; - int flags; - int ret, i; - - /* we process all users first and then all groups */ - if (ctx->user_list) { - /* take the next entry and remove it from the list */ - x = ctx->user_list; - DLIST_REMOVE(ctx->user_list, x); - } - else if (ctx->group_list) { - /* take the next entry and remove it from the list */ - x = ctx->group_list; - DLIST_REMOVE(ctx->group_list, x); - } - else { - /* processing terminated, return */ - ret = LDB_SUCCESS; - goto done; - } - - msg = ldb_msg_new(ctx); - if (!msg) { - ret = LDB_ERR_OPERATIONS_ERROR; - goto done; - } - - msg->dn = x->dn; - - /* process memberof */ - if (x->memberofs) { - ret = hash_keys(x->memberofs, &count, &keys); - if (ret != HASH_SUCCESS) { - ret = LDB_ERR_OPERATIONS_ERROR; - goto done; - } - - if (x->orig_has_memberof) { - flags = LDB_FLAG_MOD_REPLACE; - } else { - flags = LDB_FLAG_MOD_ADD; - } - - ret = ldb_msg_add_empty(msg, DB_MEMBEROF, flags, &el); - if (ret != LDB_SUCCESS) { - goto done; - } - - el->values = talloc_array(el, struct ldb_val, count); - if (!el->values) { - ret = LDB_ERR_OPERATIONS_ERROR; - goto done; - } - el->num_values = count; - - for (i = 0; i < count; i++) { - el->values[i].data = (uint8_t *)keys[i].str; - el->values[i].length = strlen(keys[i].str); - } - } else if (x->orig_has_memberof) { - ret = ldb_msg_add_empty(msg, DB_MEMBEROF, LDB_FLAG_MOD_DELETE, NULL); - if (ret != LDB_SUCCESS) { - goto done; - } - } - - /* process memberuid */ - if (x->memuids) { - if (x->orig_has_memberuid) { - flags = LDB_FLAG_MOD_REPLACE; - } else { - flags = LDB_FLAG_MOD_ADD; - } - - ret = ldb_msg_add(msg, x->memuids, flags); - if (ret != LDB_SUCCESS) { - goto done; - } - } - else if (x->orig_has_memberuid) { - ret = ldb_msg_add_empty(msg, DB_MEMBERUID, LDB_FLAG_MOD_DELETE, NULL); - if (ret != LDB_SUCCESS) { - goto done; - } - } - - ret = ldb_build_mod_req(&req, ldb, ctx, msg, NULL, - ctx, mbof_rcmp_mod_callback, - ctx->req); - if (ret != LDB_SUCCESS) { - goto done; - } - talloc_steal(req, msg); - - /* fire next call */ - return ldb_next_request(ctx->module, req); - -done: - /* all users and groups have been processed */ - return ldb_module_done(ctx->req, NULL, NULL, ret); -} - -static int mbof_rcmp_mod_callback(struct ldb_request *req, - struct ldb_reply *ares) -{ - struct ldb_context *ldb; - struct mbof_rcmp_context *ctx; - - ctx = talloc_get_type(req->context, struct mbof_rcmp_context); - ldb = ldb_module_get_ctx(ctx->module); - - if (!ares) { - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ctx->req, - ares->controls, - ares->response, - ares->error); - } - - switch (ares->type) { - case LDB_REPLY_ENTRY: - ldb_debug(ldb, LDB_DEBUG_TRACE, "Got an entry on a non search op ?!"); - /* shouldn't happen */ - talloc_zfree(ares); - return ldb_module_done(ctx->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - case LDB_REPLY_REFERRAL: - /* ignore */ - talloc_zfree(ares); - break; - - case LDB_REPLY_DONE: - talloc_zfree(ares); - - /* update the next one */ - return mbof_rcmp_update(ctx); - } - - return LDB_SUCCESS; -} - - - -/* module init code */ - -static int memberof_init(struct ldb_module *module) -{ - struct ldb_context *ldb = ldb_module_get_ctx(module); - int ret; - - /* set syntaxes for member and memberof so that comparisons in filters and - * such are done right */ - ret = ldb_schema_attribute_add(ldb, DB_MEMBER, 0, LDB_SYNTAX_DN); - ret = ldb_schema_attribute_add(ldb, DB_MEMBEROF, 0, LDB_SYNTAX_DN); - - return ldb_next_init(module); -} - -const struct ldb_module_ops ldb_memberof_module_ops = { - .name = "memberof", - .init_context = memberof_init, - .add = memberof_add, - .modify = memberof_mod, - .del = memberof_del, -}; -- cgit