summaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorSimo Sorce <idra@samba.org>2009-01-28 08:43:12 -0500
committerSimo Sorce <idra@samba.org>2009-01-28 08:43:12 -0500
commit11e7110cee5c95bebe5f746287e7792e88cf6e83 (patch)
tree1cb140fbafd45c948868d048fd3b01539e2026d8 /server
parentebd7edeba105768909aba02ceee922b3c6708581 (diff)
downloadsssd-11e7110cee5c95bebe5f746287e7792e88cf6e83.tar.gz
sssd-11e7110cee5c95bebe5f746287e7792e88cf6e83.tar.xz
sssd-11e7110cee5c95bebe5f746287e7792e88cf6e83.zip
Add a memberof plugin so that we can properly apply the same group memberships
as in IPA if necessary. This patch slightly modifies ldb to split out a modules header file without exposing the private headers.
Diffstat (limited to 'server')
-rw-r--r--server/Makefile.in18
-rw-r--r--server/ldb_modules/ldb_module.h111
-rw-r--r--server/ldb_modules/memberof.c2187
-rw-r--r--server/server.mk6
4 files changed, 2315 insertions, 7 deletions
diff --git a/server/Makefile.in b/server/Makefile.in
index 1aae86fde..542c66c23 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -57,6 +57,11 @@ CFLAGS += -g -I$(srcdir)/include -Iinclude -I$(srcdir) -I$(srcdir)/.. \
MDLD = @MDLD@
MDLD_FLAGS = @MDLD_FLAGS@
+default: all
+
+include $(srvdir)/rules.mk
+include $(srvdir)/server.mk
+
OBJS = $(SERVER_OBJ) @LIBREPLACEOBJ@ $(EXTRA_OBJ)
headers =
@@ -66,14 +71,10 @@ DBUS_SYSBUS_POLICY_DIR = @sysbuspath@
LIBEXECBINS = sbin/sssd_nss sbin/sssd_dp sbin/sssd_be sbin/sssd_info sbin/sssd_pk
DBUS_SYSBUS_POLICIES = infopipe/org.freeipa.sssd.infopipe.conf
BINS = sbin/sssd $(LIBEXECBINS)
-SOLIBS = lib/libsss_proxy.$(SHLIBEXT)
+SOLIBS = lib/libsss_proxy.$(SHLIBEXT) lib/memberof.$(SHLIBEXT)
DIRS = sbin lib
-default: all
-
-include $(srvdir)/rules.mk
-
all: showflags dirs $(OBJS) $(BINS) $(SOLIBS)
shared-build: all
@@ -82,6 +83,7 @@ dirs:
@mkdir -p $(DIRS)
clean::
+ rm -f $(OBJS) $(BINS) $(MODULES)
rm -f *.o */*.o */*/*.o
rm -f $(BINS) $(SOLIBS)
@@ -95,9 +97,11 @@ realdistclean:: distclean
install:: all installdirs installheaders installlibs installbin installsupport
${INSTALLCMD} -d $(DESTDIR)$(sbindir)
- ${INSTALLCMD} -m 700 sbin/sssd $(DESTDIR)$(sbindir)
+ ${INSTALLCMD} -m 755 sbin/sssd $(DESTDIR)$(sbindir)
${INSTALLCMD} -d $(DESTDIR)$(SSSD_LIBEXEC_PATH)
- ${INSTALLCMD} -m 700 $(LIBEXECBINS) $(DESTDIR)$(SSSD_LIBEXEC_PATH)
+ ${INSTALLCMD} -m 755 $(LIBEXECBINS) $(DESTDIR)$(SSSD_LIBEXEC_PATH)
+ ${INSTALLCMD} -m 755 lib/libsss_proxy.$(SHLIBEXT) $(DESTDIR)$(libdir)
+ ${INSTALLCMD} -m 755 lib/memberof.$(SHLIBEXT) $(DESTDIR)$(libdir)
installdirs::
mkdir -p $(DESTDIR)$(includedir) $(DESTDIR)$(libdir) $(DESTDIR)$(sbindir) $(DBUS_SYSBUS_POLICY_DIR)
diff --git a/server/ldb_modules/ldb_module.h b/server/ldb_modules/ldb_module.h
new file mode 100644
index 000000000..f41941da8
--- /dev/null
+++ b/server/ldb_modules/ldb_module.h
@@ -0,0 +1,111 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2008
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb module header
+ *
+ * Description: defines ldb modules structures and helpers
+ *
+ */
+
+#ifndef _LDB_MODULE_H_
+#define _LDB_MODULE_H_
+
+struct ldb_context;
+struct ldb_module;
+
+/*
+ these function pointers define the operations that a ldb module can intercept
+*/
+struct ldb_module_ops {
+ const char *name;
+ int (*init_context) (struct ldb_module *);
+ int (*search)(struct ldb_module *, struct ldb_request *); /* search */
+ int (*add)(struct ldb_module *, struct ldb_request *); /* add */
+ int (*modify)(struct ldb_module *, struct ldb_request *); /* modify */
+ int (*del)(struct ldb_module *, struct ldb_request *); /* delete */
+ int (*rename)(struct ldb_module *, struct ldb_request *); /* rename */
+ int (*request)(struct ldb_module *, struct ldb_request *); /* match any other operation */
+ int (*extended)(struct ldb_module *, struct ldb_request *); /* extended operations */
+ int (*start_transaction)(struct ldb_module *);
+ int (*end_transaction)(struct ldb_module *);
+ int (*del_transaction)(struct ldb_module *);
+ int (*sequence_number)(struct ldb_module *, struct ldb_request *);
+ void *private_data;
+};
+
+
+/* MODULES specific headers -- SSS */
+
+/* The following definitions come from lib/ldb/common/ldb_modules.c */
+
+struct ldb_context *ldb_module_get_ctx(struct ldb_module *module);
+
+int ldb_next_request(struct ldb_module *module, struct ldb_request *request);
+int ldb_next_start_trans(struct ldb_module *module);
+int ldb_next_end_trans(struct ldb_module *module);
+int ldb_next_del_trans(struct ldb_module *module);
+int ldb_next_init(struct ldb_module *module);
+
+void ldb_set_errstring(struct ldb_context *ldb, const char *err_string);
+void ldb_asprintf_errstring(struct ldb_context *ldb, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
+void ldb_reset_err_string(struct ldb_context *ldb);
+
+const char *ldb_default_modules_dir(void);
+
+int ldb_register_module(const struct ldb_module_ops *);
+
+typedef int (*ldb_connect_fn)(struct ldb_context *ldb, const char *url,
+ unsigned int flags, const char *options[],
+ struct ldb_module **module);
+
+struct ldb_backend_ops {
+ const char *name;
+ ldb_connect_fn connect_fn;
+};
+
+const char *ldb_default_modules_dir(void);
+
+int ldb_register_backend(const char *url_prefix, ldb_connect_fn);
+
+void *ldb_dso_load_symbol(struct ldb_context *ldb, const char *name,
+ const char *symbol);
+
+struct ldb_handle *ldb_handle_new(TALLOC_CTX *mem_ctx, struct ldb_context *ldb);
+
+int ldb_module_send_entry(struct ldb_request *req,
+ struct ldb_message *msg);
+
+int ldb_module_send_referral(struct ldb_request *req,
+ char *ref);
+
+int ldb_module_done(struct ldb_request *req,
+ struct ldb_control **ctrls,
+ struct ldb_extended *response,
+ int error);
+
+int ldb_mod_register_control(struct ldb_module *module, const char *oid);
+
+#endif
diff --git a/server/ldb_modules/memberof.c b/server/ldb_modules/memberof.c
new file mode 100644
index 000000000..f3942728b
--- /dev/null
+++ b/server/ldb_modules/memberof.c
@@ -0,0 +1,2187 @@
+/*
+ SSSD memberof module
+
+ Copyright (C) Simo Sorce <idra@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "talloc.h"
+#include "ldb.h"
+#include "ldb_errors.h"
+#include "ldb_modules/ldb_module.h"
+
+#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_array_entry {
+ struct ldb_dn *dn;
+ struct ldb_message *pmsg;
+};
+
+struct mbof_array {
+ struct mbof_array_entry *entries;
+ int num;
+};
+
+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_add_ctx {
+ struct mbof_ctx *ctx;
+
+ struct mbof_add_operation *add_list;
+ struct mbof_add_operation *current_op;
+};
+
+struct mbof_del_ancestors_ctx {
+ struct mbof_dn_array *orig_list;
+ struct mbof_array *new_list;
+
+ struct ldb_message *entry;
+ int cur;
+};
+
+struct mbof_del_operation {
+ struct mbof_del_ctx *del_ctx;
+ struct mbof_del_operation *parent;
+ struct mbof_del_operation **children;
+ int num_children;
+ int cur_child;
+
+ struct ldb_dn *entry_dn;
+ struct mbof_dn_array *rm_list;
+
+ struct ldb_message *entry;
+ struct ldb_message **parents;
+ int num_parents;
+ int cur_parent;
+
+ struct mbof_del_ancestors_ctx *anc_ctx;
+};
+
+struct mbof_del_ctx {
+ struct mbof_ctx *ctx;
+
+ struct mbof_del_operation *first;
+ struct mbof_dn *history;
+
+ void *followup_ctx;
+ int (*followup_fn)(void *);
+};
+
+struct mbof_mod_ctx {
+ struct mbof_ctx *ctx;
+
+ const struct ldb_message_element *membel;
+ struct ldb_message *entry;
+
+ struct mbof_dn_array *to_add;
+};
+
+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;
+}
+
+
+
+
+/* add operation */
+
+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 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 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;
+ const 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)) {
+ /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ /* fail operation if memberof is ever specified */
+ el = ldb_msg_find_element(req->op.add.message, "memberof");
+ if (el) {
+ /* FIXME: should we simply filter it instead ? */
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ /* continue with normal ops if there are no members */
+ el = ldb_msg_find_element(req->op.add.message, "member");
+ if (!el) {
+ return ldb_next_request(module, req);
+ }
+
+ 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;
+
+ 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] = req->op.add.message->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)) {
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+ if (ldb_dn_compare(valdn, req->op.add.message->dn) == 0) {
+ ldb_set_errstring(ldb, "Adding self as member is not permitted!");
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ ret = mbof_append_addop(add_ctx, parents, valdn);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ /* add original object */
+ ret = ldb_build_add_req(&add_req, ldb, add_ctx,
+ req->op.add.message, 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_free(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->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 */
+ talloc_free(ares);
+ return ldb_module_done(ctx->req,
+ ctx->ret_ctrls,
+ ctx->ret_resp,
+ LDB_SUCCESS);
+ }
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ares);
+ return ldb_module_done(ctx->req, NULL, NULL, ret);
+ }
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+static int mbof_next_add(struct mbof_add_operation *addop)
+{
+ static const char *attrs[] = {"member", "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 mbof_ctx *ctx;
+ int ret;
+
+ addop = talloc_get_type(req->context, struct mbof_add_operation);
+ add_ctx = addop->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:
+ if (addop->entry != NULL) {
+ /* 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:
+ if (addop->entry == NULL) {
+ /* this target does not exists, too bad! */
+ return ldb_module_done(ctx->req, NULL, NULL,
+ LDB_ERR_CONSTRAINT_VIOLATION);
+ }
+
+ ret = mbof_add_operation(addop);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ares);
+ return ldb_module_done(ctx->req, NULL, NULL, ret);
+ }
+ }
+
+ talloc_free(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;
+
+ 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, "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) {
+ 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 {
+ /* 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, "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) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (!ldb_dn_validate(valdn)) {
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+ ret = mbof_append_addop(add_ctx, parents, valdn);
+ if (ret != LDB_SUCCESS) {
+ 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, "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);
+}
+
+
+
+
+/* delete operations */
+
+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_build_first_rm_list(struct ldb_context *ldb,
+ struct mbof_del_operation *first);
+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 memberof_del(struct ldb_module *module, struct ldb_request *req)
+{
+ static const char *attrs[] = {"member", "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)(member=%s))",
+ dn, 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_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;
+
+ 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! */
+ 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_free(ares);
+ return ldb_module_done(ctx->req, NULL, NULL, ret);
+ }
+ }
+
+ talloc_free(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_free(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 any parents, fire a removal sequence */
+ ret = mbof_del_cleanup_parents(del_ctx);
+ }
+ else if (ldb_msg_find_element(del_ctx->first->entry, "member")) {
+ /* if there are any children, fire a removal sequence */
+ ret = mbof_del_cleanup_children(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_free(ares);
+ return ldb_module_done(ctx->req, NULL, NULL, ret);
+ }
+
+ talloc_free(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, "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_free(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, "member")) {
+ /* if there are any children, fire a removal sequence */
+ ret = mbof_del_cleanup_children(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_free(ares);
+ return ldb_module_done(ctx->req, NULL, NULL, ret);
+ }
+
+ talloc_free(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);
+
+ ret = mbof_build_first_rm_list(ldb, first);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ el = ldb_msg_find_element(first->entry, "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)) {
+ 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_build_first_rm_list(struct ldb_context *ldb,
+ struct mbof_del_operation *first)
+{
+ const struct ldb_message_element *el;
+ struct mbof_dn_array *rm_list;
+ struct ldb_dn *valdn;
+ int i;
+
+ rm_list = talloc_zero(first, struct mbof_dn_array);
+ if (!rm_list) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* create memberof removal list if part of any group */
+ el = ldb_msg_find_element(first->entry, "memberof");
+ if (el) {
+ rm_list->dns = talloc_array(rm_list, struct ldb_dn *,
+ el->num_values + 1);
+ if (!rm_list->dns) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ rm_list->num = el->num_values + 1;
+
+ for (i = 0; i < el->num_values; i++) {
+ valdn = ldb_dn_from_ldb_val(rm_list, ldb, &el->values[i]);
+ if (!valdn || !ldb_dn_validate(valdn)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ rm_list->dns[i] = valdn;
+ }
+ }
+
+ if (!rm_list->dns) {
+ rm_list->dns = talloc_array(rm_list, struct ldb_dn *, 1);
+ if (!rm_list->dns) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ rm_list->num = 1;
+ i = 0;
+ }
+
+ rm_list->dns[i] = first->entry_dn;
+ if (!rm_list->dns[i]) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ first->rm_list = rm_list;
+
+ return LDB_SUCCESS;
+}
+
+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[] = {"member", "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)(member=%s))",
+ dn, 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 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;
+
+ 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) {
+ /* 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_free(ares);
+ return LDB_SUCCESS;
+}
+
+/* FIXME: very inefficient */
+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;
+ const struct ldb_message_element *el;
+ struct mbof_dn_array *orig_list;
+ struct mbof_array *new_list;
+ struct ldb_dn *valdn;
+ int i, j;
+ bool found;
+
+ 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;
+
+ orig_list = talloc_zero(anc_ctx, struct mbof_dn_array);
+ if (!orig_list) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ anc_ctx->orig_list = orig_list;
+
+ new_list = talloc_zero(anc_ctx, struct mbof_array);
+ if (!new_list) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* create new memberof list */
+ el = ldb_msg_find_element(delop->entry, "memberof");
+ if (!el) {
+ /* can't be, if we get there it is because this entry
+ * had at least one parent */
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ orig_list->num = el->num_values;
+ orig_list->dns = talloc_zero_array(orig_list,
+ struct ldb_dn *,
+ orig_list->num);
+ if (!orig_list->dns) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ new_list->entries = talloc_zero_array(new_list,
+ struct mbof_array_entry,
+ orig_list->num);
+ if (!new_list->entries) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* add to the list only the values that are not on the rm_list */
+ for (i = 0; i < el->num_values; i++) {
+ valdn = ldb_dn_from_ldb_val(orig_list, ldb, &el->values[i]);
+ if (!valdn) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ orig_list->dns[i] = valdn;
+ for (j = 0; j < parent->rm_list->num; j++) {
+ if (ldb_dn_compare(valdn, parent->rm_list->dns[j]) == 0)
+ break;
+ }
+ if (j < parent->rm_list->num) {
+ continue;
+ }
+ new_list->entries[new_list->num].dn = valdn;
+ new_list->num++;
+ }
+
+ /* now check that we have the right memberof for all direct parents */
+ for (i = 0; i < delop->num_parents; i++) {
+ found = false;
+ for (j = 0; j < orig_list->num; j++) {
+ if (new_list->entries[j].pmsg) {
+ /* parent has already been paired, try next */
+ continue;
+ }
+ if (new_list->entries[j].dn == NULL) {
+ /* we reached the end of current entries, pair new parent */
+ new_list->entries[j].dn = delop->parents[i]->dn;
+ new_list->entries[j].pmsg = delop->parents[i];
+ /* update also the number tracking the entries */
+ new_list->num++;
+ /* mark we found an entry; */
+ found = true;
+ break;
+ }
+ if (ldb_dn_compare(delop->parents[i]->dn,
+ new_list->entries[j].dn) == 0) {
+ /* pair parent and move to next */
+ new_list->entries[j].pmsg = delop->parents[i];
+ /* mark we found an entry; */
+ found = true;
+ break;
+ }
+ }
+ if (!found && j == orig_list->num) {
+ /* houston we have a problem here */
+ /* it seem we have more parents then allotted slots which is
+ * impossible because we have slots for a number of parents equal to
+ * the original number minus 1 and we are only potentially removing
+ * parents not adding. Return a bogus error, this must be an error
+ * somewhere else or the database has been corrupted */
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+ }
+
+ /* attach the list to the operation */
+ delop->anc_ctx->new_list = new_list;
+
+ /* do we have any member in the list 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);
+ }
+
+ /* 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_array *new_list;
+ static const char *attrs[] = {"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->entries[anc_ctx->cur].dn,
+ 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_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) {
+ /* 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, "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) {
+ 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->entries[j].dn) == 0)
+ break;
+ }
+ if (j < new_list->num) {
+ talloc_free(valdn);
+ continue;
+ }
+ new_list->entries = talloc_realloc(new_list,
+ new_list->entries,
+ struct mbof_array_entry,
+ new_list->num + 1);
+ if (!new_list->entries) {
+ return ldb_module_done(ctx->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ new_list->entries[new_list->num].dn = 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 >= new_list->num) {
+ /* ok, end of the story, proceed to modify the entry */
+ ret = mbof_del_mod_entry(delop);
+ } else {
+ /* ok process the next one */
+ ret = mbof_del_ancestors(delop);
+ }
+
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ctx->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ }
+
+ talloc_free(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_array *new_list;
+ struct ldb_request *mod_req;
+ struct ldb_message *msg;
+ struct ldb_message_element *el;
+ const char *val;
+ int i, j, ret;
+
+ del_ctx = delop->del_ctx;
+ ctx = del_ctx->ctx;
+ ldb = ldb_module_get_ctx(ctx->module);
+ new_list = delop->anc_ctx->new_list;
+
+ /* 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, "memberof", LDB_FLAG_MOD_REPLACE, &el);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ el->values = talloc_array(msg, 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->entries[i].dn, msg->dn) == 0)
+ continue;
+ val = ldb_dn_get_linearized(new_list->entries[i].dn);
+ 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++;
+ }
+ el->num_values = j;
+ }
+ else {
+ ret = ldb_msg_add_empty(msg, "memberof", LDB_FLAG_MOD_DELETE, &el);
+ 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 mbof_ctx *ctx;
+ int ret;
+
+ delop = talloc_get_type(req->context, struct mbof_del_operation);
+ del_ctx = delop->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_free(ares);
+ return ldb_module_done(ctx->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ case LDB_REPLY_REFERRAL:
+ /* ignore */
+ break;
+
+ case LDB_REPLY_DONE:
+
+ ret = mbof_del_progeny(delop);
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ares);
+ return ldb_module_done(ctx->req, NULL, NULL, ret);
+ }
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+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 mbof_dn_array *rm_list;
+ struct mbof_dn_array *orig_list;
+ struct mbof_array *new_list;
+ struct ldb_context *ldb;
+ struct ldb_dn *valdn;
+ int i, j, ret;
+
+ orig_list = delop->anc_ctx->orig_list;
+ new_list = delop->anc_ctx->new_list;
+ 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, "member");
+ if (el) {
+ /* build rm_list for children */
+ rm_list = talloc_zero(delop, struct mbof_dn_array);
+ if (!rm_list) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ rm_list->dns = talloc_zero_array(rm_list,
+ struct ldb_dn *,
+ orig_list->num);
+ if (!rm_list->dns) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i = 0; i < orig_list->num; i++) {
+ for (j = 0; j < new_list->num; j++) {
+ if (ldb_dn_compare(orig_list->dns[i],
+ new_list->entries[j].dn) == 0)
+ break;
+ }
+ /* present in both, therefoire not deleted */
+ if (j < new_list->num) continue;
+
+ /* this was removed, add to the rm_list */
+ rm_list->dns[rm_list->num] = orig_list->dns[i];
+ rm_list->num++;
+ }
+
+ delop->rm_list = rm_list;
+
+ 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)) {
+ 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 follow functions to run */
+ if (del_ctx->followup_fn) {
+ return del_ctx->followup_fn(del_ctx->followup_ctx);
+ }
+
+ /* 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 &&
+ top->cur_child < top->num_children) {
+
+ cop = top->children[top->cur_child];
+ top->cur_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) {
+ /* it's a dup */
+ continue;
+ }
+
+ *nextop = cop;
+ return LDB_SUCCESS;
+ }
+ }
+
+ /* we have no more ops */
+ *nextop = NULL;
+ return LDB_SUCCESS;
+}
+
+
+
+/* mod operation */
+
+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_add(struct mbof_mod_ctx *mod_ctx,
+ struct mbof_dn_array *ael);
+static int mbof_mod_followup(void *ctx);
+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)
+{
+ const struct ldb_message_element *el;
+ struct mbof_mod_ctx *mod_ctx;
+ struct mbof_ctx *ctx;
+ static const char *attrs[] = {"member", "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);
+ }
+
+ /* fail operation if memberof is ever specified */
+ el = ldb_msg_find_element(req->op.mod.message, "memberof");
+ if (el) {
+ /* FIXME: should we simply filter it instead ? */
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ /* continue with normal ops if there are no members */
+ el = ldb_msg_find_element(req->op.mod.message, "member");
+ if (!el) {
+ return ldb_next_request(module, req);
+ }
+
+ /* TODO: fail if this is not a group ? */
+
+ ctx = mbof_init(module, req);
+ if (!ctx) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* can't do anything,
+ * must check first what's on the entry */
+ 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->membel = el;
+
+ ret = ldb_build_search_req(&search, ldb, mod_ctx,
+ req->op.mod.message->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 mbof_ctx *ctx;
+ int ret;
+
+ mod_ctx = talloc_get_type(req->context, struct mbof_mod_ctx);
+ ctx = mod_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:
+ if (mod_ctx->entry != NULL) {
+ /* 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) {
+ /* 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_free(ares);
+ return ldb_module_done(ctx->req, NULL, NULL, ret);
+ }
+ }
+
+ talloc_free(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,
+ ctx->req->op.mod.message, 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;
+ bool done = false;
+ 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_free(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);
+
+ /* next step */
+ ret = mbof_mod_process(mod_ctx, &done);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ares);
+ return ldb_module_done(ctx->req, NULL, NULL, ret);
+ }
+ if (done) {
+ talloc_free(ares);
+ return ldb_module_done(ctx->req,
+ ctx->ret_ctrls,
+ ctx->ret_resp,
+ LDB_SUCCESS);
+ }
+
+ talloc_free(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, "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, "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_followup(void *ctx)
+{
+ struct mbof_mod_ctx *mod_ctx;
+
+ mod_ctx = talloc_get_type(ctx, struct mbof_mod_ctx);
+
+ return mbof_mod_add(mod_ctx, mod_ctx->to_add);
+}
+
+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, "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;
+
+ 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;
+
+ /* 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;
+
+ ret = mbof_build_first_rm_list(ldb, first);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* 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->followup_ctx = mod_ctx;
+ del_ctx->followup_fn = mbof_mod_followup;
+ }
+
+ /* 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)) {
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+ ar->dns[i] = valdn;
+ }
+
+ return LDB_SUCCESS;
+}
+
+
+
+
+/* module init code */
+
+static int memberof_init(struct ldb_module *module)
+{
+ 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,
+};
diff --git a/server/server.mk b/server/server.mk
index 893a4985b..348bbc8a7 100644
--- a/server/server.mk
+++ b/server/server.mk
@@ -38,6 +38,9 @@ INFOPIPE_OBJ = \
POLKIT_OBJ = \
polkit/sssd_polkit.o
+MEMBEROF_OBJ = \
+ ldb_modules/memberof.o
+
sbin/sssd: $(SERVER_OBJ) $(UTIL_OBJ)
$(CC) -o sbin/sssd $(SERVER_OBJ) $(UTIL_OBJ) $(LDFLAGS) $(LIBS)
@@ -58,3 +61,6 @@ sbin/sssd_pk: $(POLKIT_OBJ) $(UTIL_OBJ)
lib/libsss_proxy.$(SHLIBEXT): $(PROXY_BE_OBJ)
$(SHLD) $(SHLD_FLAGS) -o $@ $(PROXY_BE_OBJ) $(LDFLAGS) $(LIBS)
+
+lib/memberof.$(SHLIBEXT): $(MEMBEROF_OBJ)
+ $(SHLD) $(SHLD_FLAGS) -o $@ $(MEMBEROF_OBJ) $(LDFLAGS) $(LDB_LIBS)