summaryrefslogtreecommitdiffstats
path: root/lib/ldb/common/ldb_modules.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /lib/ldb/common/ldb_modules.c
parentInitial commit. (diff)
downloadsamba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz
samba-4f5791ebd03eaec1c7da0865a383175b05102712.zip
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/ldb/common/ldb_modules.c')
-rw-r--r--lib/ldb/common/ldb_modules.c1242
1 files changed, 1242 insertions, 0 deletions
diff --git a/lib/ldb/common/ldb_modules.c b/lib/ldb/common/ldb_modules.c
new file mode 100644
index 0000000..2d0494a
--- /dev/null
+++ b/lib/ldb/common/ldb_modules.c
@@ -0,0 +1,1242 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2004-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 modules core
+ *
+ * Description: core modules routines
+ *
+ * Author: Simo Sorce
+ */
+
+#include "ldb_private.h"
+#include "dlinklist.h"
+#include "system/dir.h"
+
+static char *ldb_modules_strdup_no_spaces(TALLOC_CTX *mem_ctx, const char *string)
+{
+ size_t i, len;
+ char *trimmed;
+
+ trimmed = talloc_strdup(mem_ctx, string);
+ if (!trimmed) {
+ return NULL;
+ }
+
+ len = strlen(trimmed);
+ for (i = 0; trimmed[i] != '\0'; i++) {
+ switch (trimmed[i]) {
+ case ' ':
+ case '\t':
+ case '\n':
+ memmove(&trimmed[i], &trimmed[i + 1], len -i -1);
+ break;
+ }
+ }
+
+ return trimmed;
+}
+
+
+/* modules are called in inverse order on the stack.
+ Lets place them as an admin would think the right order is.
+ Modules order is important */
+const char **ldb_modules_list_from_string(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *string)
+{
+ char **modules = NULL;
+ const char **m;
+ char *modstr, *p;
+ unsigned int i;
+
+ /* spaces not admitted */
+ modstr = ldb_modules_strdup_no_spaces(mem_ctx, string);
+ if ( ! modstr) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_strdup_no_spaces()");
+ return NULL;
+ }
+
+ modules = talloc_realloc(mem_ctx, modules, char *, 2);
+ if ( ! modules ) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_list_from_string()");
+ talloc_free(modstr);
+ return NULL;
+ }
+ talloc_steal(modules, modstr);
+
+ if (modstr[0] == '\0') {
+ modules[0] = NULL;
+ m = discard_const_p(const char *, modules);
+ return m;
+ }
+
+ i = 0;
+ /* The str*r*chr walks backwards: This is how we get the inverse order mentioned above */
+ while ((p = strrchr(modstr, ',')) != NULL) {
+ *p = '\0';
+ p++;
+ modules[i] = p;
+
+ i++;
+ modules = talloc_realloc(mem_ctx, modules, char *, i + 2);
+ if ( ! modules ) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_list_from_string()");
+ return NULL;
+ }
+
+ }
+ modules[i] = modstr;
+
+ modules[i + 1] = NULL;
+
+ m = discard_const_p(const char *, modules);
+
+ return m;
+}
+
+static struct backends_list_entry {
+ struct ldb_backend_ops *ops;
+ struct backends_list_entry *prev, *next;
+} *ldb_backends = NULL;
+
+static struct ops_list_entry {
+ const struct ldb_module_ops *ops;
+ struct ops_list_entry *next;
+} *registered_modules = NULL;
+
+static struct backends_list_entry *ldb_find_backend(const char *url_prefix)
+{
+ struct backends_list_entry *backend;
+
+ for (backend = ldb_backends; backend; backend = backend->next) {
+ if (strcmp(backend->ops->name, url_prefix) == 0) {
+ return backend;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ register a new ldb backend
+
+ if override is true, then override any existing backend for this prefix
+*/
+int ldb_register_backend(const char *url_prefix, ldb_connect_fn connectfn, bool override)
+{
+ struct backends_list_entry *be;
+
+ be = ldb_find_backend(url_prefix);
+ if (be) {
+ if (!override) {
+ return LDB_SUCCESS;
+ }
+ } else {
+ be = talloc_zero(ldb_backends, struct backends_list_entry);
+ if (!be) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ be->ops = talloc_zero(be, struct ldb_backend_ops);
+ if (!be->ops) {
+ talloc_free(be);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ DLIST_ADD_END(ldb_backends, be);
+ }
+
+ be->ops->name = url_prefix;
+ be->ops->connect_fn = connectfn;
+
+ return LDB_SUCCESS;
+}
+
+/*
+ Return the ldb module form of a database.
+ The URL looks something like this:
+ tdb://PATH
+ ldb://PATH
+ mdb://PATH
+ ldapi://PATH
+ PATH (unadorned PATH defaults to tdb://)
+
+ for a complete list of backends (including possibly unmaintained ones) grep
+ for calls to ldb_register_backend().
+
+ the options are passed uninterpreted to the backend, and are
+ backend specific.
+
+ This allows modules to get at only the backend module, for example where a
+ module may wish to direct certain requests at a particular backend.
+*/
+int ldb_module_connect_backend(struct ldb_context *ldb,
+ const char *url,
+ const char *options[],
+ struct ldb_module **backend_module)
+{
+ int ret;
+ char *backend;
+ struct backends_list_entry *be;
+ char *colon = NULL;
+
+ colon = strchr(url, ':');
+ if (colon != NULL) {
+ backend = talloc_strndup(ldb, url, colon-url);
+ } else {
+ /* Default to tdb */
+ backend = talloc_strdup(ldb, "tdb");
+ }
+ if (backend == NULL) {
+ return ldb_oom(ldb);
+ }
+
+ be = ldb_find_backend(backend);
+
+ talloc_free(backend);
+
+ if (be == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL,
+ "Unable to find backend for '%s' - do you need to set LDB_MODULES_PATH?", url);
+ return LDB_ERR_OTHER;
+ }
+
+ ret = be->ops->connect_fn(ldb, url, ldb->flags, options, backend_module);
+
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "Failed to connect to '%s' with backend '%s': %s", url, be->ops->name, ldb_errstring(ldb));
+ return ret;
+ }
+ return ret;
+}
+
+static struct ldb_hooks {
+ struct ldb_hooks *next, *prev;
+ ldb_hook_fn hook_fn;
+} *ldb_hooks;
+
+/*
+ register a ldb hook function
+ */
+int ldb_register_hook(ldb_hook_fn hook_fn)
+{
+ struct ldb_hooks *lc;
+ lc = talloc_zero(ldb_hooks, struct ldb_hooks);
+ if (lc == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ lc->hook_fn = hook_fn;
+ DLIST_ADD_END(ldb_hooks, lc);
+ return LDB_SUCCESS;
+}
+
+/*
+ call ldb hooks of a given type
+ */
+int ldb_modules_hook(struct ldb_context *ldb, enum ldb_module_hook_type t)
+{
+ struct ldb_hooks *lc;
+ for (lc = ldb_hooks; lc; lc=lc->next) {
+ int ret = lc->hook_fn(ldb, t);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ return LDB_SUCCESS;
+}
+
+
+static const struct ldb_module_ops *ldb_find_module_ops(const char *name)
+{
+ struct ops_list_entry *e;
+
+ for (e = registered_modules; e; e = e->next) {
+ if (strcmp(e->ops->name, name) == 0)
+ return e->ops;
+ }
+
+ return NULL;
+}
+
+
+int ldb_register_module(const struct ldb_module_ops *ops)
+{
+ struct ops_list_entry *entry;
+
+ if (ldb_find_module_ops(ops->name) != NULL)
+ return LDB_ERR_ENTRY_ALREADY_EXISTS;
+
+ /*
+ * ldb modules are not (yet) unloaded and
+ * are only loaded once (the above check
+ * makes sure of this). Allocate off the NULL
+ * context. We never want this to be freed
+ * until process shutdown. If eventually we
+ * want to unload ldb modules we can add a
+ * deregister function that walks and
+ * frees the list.
+ */
+ entry = talloc(NULL, struct ops_list_entry);
+ if (entry == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ entry->ops = ops;
+ entry->next = registered_modules;
+ registered_modules = entry;
+
+ return LDB_SUCCESS;
+}
+
+/*
+ load a list of modules
+ */
+int ldb_module_load_list(struct ldb_context *ldb, const char **module_list,
+ struct ldb_module *backend, struct ldb_module **out)
+{
+ struct ldb_module *module;
+ unsigned int i;
+
+ module = backend;
+
+ for (i = 0; module_list && module_list[i] != NULL; i++) {
+ struct ldb_module *current;
+ const struct ldb_module_ops *ops;
+
+ if (strcmp(module_list[i], "") == 0) {
+ continue;
+ }
+
+ ops = ldb_find_module_ops(module_list[i]);
+
+ if (ops == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "WARNING: Module [%s] not found - do you need to set LDB_MODULES_PATH?",
+ module_list[i]);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ current = talloc_zero(ldb, struct ldb_module);
+ if (current == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ talloc_set_name(current, "ldb_module: %s", module_list[i]);
+
+ current->ldb = ldb;
+ current->ops = ops;
+
+ DLIST_ADD(module, current);
+ }
+ *out = module;
+ return LDB_SUCCESS;
+}
+
+/*
+ initialise a chain of modules
+ */
+int ldb_module_init_chain(struct ldb_context *ldb, struct ldb_module *module)
+{
+ while (module && module->ops->init_context == NULL)
+ module = module->next;
+
+ /* init is different in that it is not an error if modules
+ * do not require initialization */
+
+ if (module) {
+ int ret = module->ops->init_context(module);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "module %s initialization failed : %s",
+ module->ops->name, ldb_strerror(ret));
+ return ret;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+int ldb_load_modules(struct ldb_context *ldb, const char *options[])
+{
+ const char *modules_string;
+ const char **modules = NULL;
+ int ret;
+ TALLOC_CTX *mem_ctx = talloc_new(ldb);
+ if (!mem_ctx) {
+ return ldb_oom(ldb);
+ }
+
+ /* find out which modules we are requested to activate */
+
+ /* check if we have a custom module list passd as ldb option */
+ if (options) {
+ modules_string = ldb_options_find(ldb, options, "modules");
+ if (modules_string) {
+ modules = ldb_modules_list_from_string(ldb, mem_ctx, modules_string);
+ }
+ }
+
+ /* if not overloaded by options and the backend is not ldap try to load the modules list from ldb */
+ if ((modules == NULL) && (strcmp("ldap", ldb->modules->ops->name) != 0)) {
+ const char * const attrs[] = { "@LIST" , NULL};
+ struct ldb_result *res = NULL;
+ struct ldb_dn *mods_dn;
+
+ mods_dn = ldb_dn_new(mem_ctx, ldb, "@MODULES");
+ if (mods_dn == NULL) {
+ talloc_free(mem_ctx);
+ return ldb_oom(ldb);
+ }
+
+ ret = ldb_search(ldb, mods_dn, &res, mods_dn, LDB_SCOPE_BASE, attrs, "@LIST=*");
+
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "no modules required by the db");
+ } else if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "ldb error (%s) occurred searching for modules, bailing out", ldb_errstring(ldb));
+ talloc_free(mem_ctx);
+ return ret;
+ } else {
+ const char *module_list;
+ if (res->count == 0) {
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "no modules required by the db");
+ } else if (res->count > 1) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "Too many records found (%u), bailing out", res->count);
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ } else {
+ module_list = ldb_msg_find_attr_as_string(res->msgs[0], "@LIST", NULL);
+ if (!module_list) {
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "no modules required by the db");
+ }
+ modules = ldb_modules_list_from_string(ldb, mem_ctx,
+ module_list);
+ }
+ }
+
+ talloc_free(mods_dn);
+ }
+
+ if (modules != NULL) {
+ ret = ldb_module_load_list(ldb, modules, ldb->modules, &ldb->modules);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ } else {
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "No modules specified for this database");
+ }
+
+ ret = ldb_module_init_chain(ldb, ldb->modules);
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ by using this we allow ldb modules to only implement the functions they care about,
+ which makes writing a module simpler, and makes it more likely to keep working
+ when ldb is extended
+*/
+#define FIND_OP_NOERR(module, op) do { \
+ module = module->next; \
+ while (module && module->ops->op == NULL) module = module->next; \
+ if ((module && module->ldb->flags & LDB_FLG_ENABLE_TRACING)) { \
+ ldb_debug(module->ldb, LDB_DEBUG_TRACE, "ldb_trace_next_request: (%s)->" #op, \
+ module->ops->name); \
+ } \
+} while (0)
+
+#define FIND_OP(module, op) do { \
+ struct ldb_context *ldb = module->ldb; \
+ FIND_OP_NOERR(module, op); \
+ if (module == NULL) { \
+ ldb_asprintf_errstring(ldb, "Unable to find backend operation for " #op ); \
+ return LDB_ERR_OPERATIONS_ERROR; \
+ } \
+} while (0)
+
+
+struct ldb_module *ldb_module_new(TALLOC_CTX *memctx,
+ struct ldb_context *ldb,
+ const char *module_name,
+ const struct ldb_module_ops *ops)
+{
+ struct ldb_module *module;
+
+ module = talloc(memctx, struct ldb_module);
+ if (!module) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+ talloc_set_name_const(module, module_name);
+ module->ldb = ldb;
+ module->prev = module->next = NULL;
+ module->ops = ops;
+
+ return module;
+}
+
+const char * ldb_module_get_name(struct ldb_module *module)
+{
+ return module->ops->name;
+}
+
+struct ldb_context *ldb_module_get_ctx(struct ldb_module *module)
+{
+ return module->ldb;
+}
+
+const struct ldb_module_ops *ldb_module_get_ops(struct ldb_module *module)
+{
+ return module->ops;
+}
+
+void *ldb_module_get_private(struct ldb_module *module)
+{
+ return module->private_data;
+}
+
+void ldb_module_set_private(struct ldb_module *module, void *private_data)
+{
+ module->private_data = private_data;
+}
+
+/*
+ helper functions to call the next module in chain
+*/
+
+int ldb_next_request(struct ldb_module *module, struct ldb_request *request)
+{
+ int ret;
+
+ if (request->callback == NULL) {
+ ldb_set_errstring(module->ldb, "Requests MUST define callbacks");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ request->handle->nesting++;
+
+ switch (request->operation) {
+ case LDB_SEARCH:
+ FIND_OP(module, search);
+ ret = module->ops->search(module, request);
+ break;
+ case LDB_ADD:
+ FIND_OP(module, add);
+ ret = module->ops->add(module, request);
+ break;
+ case LDB_MODIFY:
+ FIND_OP(module, modify);
+ ret = module->ops->modify(module, request);
+ break;
+ case LDB_DELETE:
+ FIND_OP(module, del);
+ ret = module->ops->del(module, request);
+ break;
+ case LDB_RENAME:
+ FIND_OP(module, rename);
+ ret = module->ops->rename(module, request);
+ break;
+ case LDB_EXTENDED:
+ FIND_OP(module, extended);
+ ret = module->ops->extended(module, request);
+ break;
+ default:
+ FIND_OP(module, request);
+ ret = module->ops->request(module, request);
+ break;
+ }
+
+ request->handle->nesting--;
+
+ if (ret == LDB_SUCCESS) {
+ return ret;
+ }
+ if (!ldb_errstring(module->ldb)) {
+ const char *op;
+ switch (request->operation) {
+ case LDB_SEARCH:
+ op = "LDB_SEARCH";
+ break;
+ case LDB_ADD:
+ op = "LDB_ADD";
+ break;
+ case LDB_MODIFY:
+ op = "LDB_MODIFY";
+ break;
+ case LDB_DELETE:
+ op = "LDB_DELETE";
+ break;
+ case LDB_RENAME:
+ op = "LDB_RENAME";
+ break;
+ case LDB_EXTENDED:
+ op = "LDB_EXTENDED";
+ break;
+ default:
+ op = "request";
+ break;
+ }
+
+ /* Set a default error string, to place the blame somewhere */
+ ldb_asprintf_errstring(module->ldb, "error in module %s: %s during %s (%d)", module->ops->name, ldb_strerror(ret), op, ret);
+ }
+
+ if (!(request->handle->flags & LDB_HANDLE_FLAG_DONE_CALLED)) {
+ /* It is _extremely_ common that a module returns a
+ * failure without calling ldb_module_done(), but that
+ * guarantees we will end up hanging in
+ * ldb_wait(). This fixes it without having to rewrite
+ * all our modules, and leaves us one less sharp
+ * corner for module developers to cut themselves on
+ */
+ ret = ldb_module_done(request, NULL, NULL, ret);
+ }
+ return ret;
+}
+
+int ldb_next_init(struct ldb_module *module)
+{
+ module = module->next;
+
+ return ldb_module_init_chain(module->ldb, module);
+}
+
+int ldb_next_start_trans(struct ldb_module *module)
+{
+ int ret;
+ FIND_OP(module, start_transaction);
+ ret = module->ops->start_transaction(module);
+ if (ret == LDB_SUCCESS) {
+ return ret;
+ }
+ if (!ldb_errstring(module->ldb)) {
+ /* Set a default error string, to place the blame somewhere */
+ ldb_asprintf_errstring(module->ldb, "start_trans error in module %s: %s (%d)", module->ops->name, ldb_strerror(ret), ret);
+ }
+ if ((module && module->ldb->flags & LDB_FLG_ENABLE_TRACING)) {
+ ldb_debug(module->ldb, LDB_DEBUG_TRACE, "ldb_next_start_trans error: %s",
+ ldb_errstring(module->ldb));
+ }
+ return ret;
+}
+
+int ldb_next_end_trans(struct ldb_module *module)
+{
+ int ret;
+ FIND_OP(module, end_transaction);
+ ret = module->ops->end_transaction(module);
+ if (ret == LDB_SUCCESS) {
+ return ret;
+ }
+ if (!ldb_errstring(module->ldb)) {
+ /* Set a default error string, to place the blame somewhere */
+ ldb_asprintf_errstring(module->ldb, "end_trans error in module %s: %s (%d)", module->ops->name, ldb_strerror(ret), ret);
+ }
+ if ((module && module->ldb->flags & LDB_FLG_ENABLE_TRACING)) {
+ ldb_debug(module->ldb, LDB_DEBUG_TRACE, "ldb_next_end_trans error: %s",
+ ldb_errstring(module->ldb));
+ }
+ return ret;
+}
+
+int ldb_next_read_lock(struct ldb_module *module)
+{
+ int ret;
+ FIND_OP(module, read_lock);
+ ret = module->ops->read_lock(module);
+ if (ret == LDB_SUCCESS) {
+ return ret;
+ }
+ if (!ldb_errstring(module->ldb)) {
+ /* Set a default error string, to place the blame somewhere */
+ ldb_asprintf_errstring(module->ldb,
+ "read_lock error in module %s: %s (%d)",
+ module->ops->name, ldb_strerror(ret),
+ ret);
+ }
+ if ((module && module->ldb->flags & LDB_FLG_ENABLE_TRACING)) {
+ ldb_debug(module->ldb, LDB_DEBUG_TRACE,
+ "ldb_next_read_lock error: %s",
+ ldb_errstring(module->ldb));
+ }
+ return ret;
+}
+
+int ldb_next_read_unlock(struct ldb_module *module)
+{
+ int ret;
+ FIND_OP(module, read_unlock);
+ ret = module->ops->read_unlock(module);
+ if (ret == LDB_SUCCESS) {
+ return ret;
+ }
+ if (!ldb_errstring(module->ldb)) {
+ /* Set a default error string, to place the blame somewhere */
+ ldb_asprintf_errstring(module->ldb,
+ "read_unlock error in module %s: %s (%d)",
+ module->ops->name, ldb_strerror(ret),
+ ret);
+ }
+ if ((module && module->ldb->flags & LDB_FLG_ENABLE_TRACING)) {
+ ldb_debug(module->ldb, LDB_DEBUG_TRACE,
+ "ldb_next_read_unlock error: %s",
+ ldb_errstring(module->ldb));
+ }
+ return ret;
+}
+
+int ldb_next_prepare_commit(struct ldb_module *module)
+{
+ int ret;
+ FIND_OP_NOERR(module, prepare_commit);
+ if (module == NULL) {
+ /* we are allowed to have no prepare commit in
+ backends */
+ return LDB_SUCCESS;
+ }
+ ret = module->ops->prepare_commit(module);
+ if (ret == LDB_SUCCESS) {
+ return ret;
+ }
+ if (!ldb_errstring(module->ldb)) {
+ /* Set a default error string, to place the blame somewhere */
+ ldb_asprintf_errstring(module->ldb, "prepare_commit error in module %s: %s (%d)", module->ops->name, ldb_strerror(ret), ret);
+ }
+ if ((module && module->ldb->flags & LDB_FLG_ENABLE_TRACING)) {
+ ldb_debug(module->ldb, LDB_DEBUG_TRACE, "ldb_next_prepare_commit error: %s",
+ ldb_errstring(module->ldb));
+ }
+ return ret;
+}
+
+int ldb_next_del_trans(struct ldb_module *module)
+{
+ int ret;
+ FIND_OP(module, del_transaction);
+ ret = module->ops->del_transaction(module);
+ if (ret == LDB_SUCCESS) {
+ return ret;
+ }
+ if (!ldb_errstring(module->ldb)) {
+ /* Set a default error string, to place the blame somewhere */
+ ldb_asprintf_errstring(module->ldb, "del_trans error in module %s: %s (%d)", module->ops->name, ldb_strerror(ret), ret);
+ }
+ if ((module && module->ldb->flags & LDB_FLG_ENABLE_TRACING)) {
+ ldb_debug(module->ldb, LDB_DEBUG_TRACE, "ldb_next_del_trans error: %s",
+ ldb_errstring(module->ldb));
+ }
+ return ret;
+}
+
+/* calls the request callback to send an entry
+ *
+ * params:
+ * req: the original request passed to your module
+ * msg: reply message (must be a talloc pointer, and it will be stolen
+ * on the ldb_reply that is sent to the callback)
+ * ctrls: controls to send in the reply (must be a talloc pointer, and it will be stolen
+ * on the ldb_reply that is sent to the callback)
+ */
+
+int ldb_module_send_entry(struct ldb_request *req,
+ struct ldb_message *msg,
+ struct ldb_control **ctrls)
+{
+ struct ldb_reply *ares;
+
+ ares = talloc_zero(req, struct ldb_reply);
+ if (!ares) {
+ ldb_oom(req->handle->ldb);
+ req->callback(req, NULL);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ares->type = LDB_REPLY_ENTRY;
+ ares->message = talloc_steal(ares, msg);
+ ares->controls = talloc_steal(ares, ctrls);
+ ares->error = LDB_SUCCESS;
+
+ if ((req->handle->ldb->flags & LDB_FLG_ENABLE_TRACING) &&
+ req->handle->nesting == 0) {
+ char *s;
+ struct ldb_ldif ldif;
+
+ ldif.changetype = LDB_CHANGETYPE_NONE;
+ ldif.msg = discard_const_p(struct ldb_message, msg);
+
+ ldb_debug_add(req->handle->ldb, "ldb_trace_response: ENTRY\n");
+
+ /*
+ * The choice to call
+ * ldb_ldif_write_redacted_trace_string() is CRITICAL
+ * for security. It ensures that we do not output
+ * passwords into debug logs
+ */
+
+ s = ldb_ldif_write_redacted_trace_string(req->handle->ldb, msg, &ldif);
+ ldb_debug_add(req->handle->ldb, "%s\n", s);
+ talloc_free(s);
+ ldb_debug_end(req->handle->ldb, LDB_DEBUG_TRACE);
+ }
+
+ return req->callback(req, ares);
+}
+
+/* calls the request callback to send an referrals
+ *
+ * params:
+ * req: the original request passed to your module
+ * ref: referral string (must be a talloc pointer, steal)
+ */
+
+int ldb_module_send_referral(struct ldb_request *req,
+ char *ref)
+{
+ struct ldb_reply *ares;
+
+ ares = talloc_zero(req, struct ldb_reply);
+ if (!ares) {
+ ldb_oom(req->handle->ldb);
+ req->callback(req, NULL);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ares->type = LDB_REPLY_REFERRAL;
+ ares->referral = talloc_steal(ares, ref);
+ ares->error = LDB_SUCCESS;
+
+ if ((req->handle->ldb->flags & LDB_FLG_ENABLE_TRACING) &&
+ req->handle->nesting == 0) {
+ ldb_debug_add(req->handle->ldb, "ldb_trace_response: REFERRAL\n");
+ ldb_debug_add(req->handle->ldb, "ref: %s\n", ref);
+ ldb_debug_end(req->handle->ldb, LDB_DEBUG_TRACE);
+ }
+
+ return req->callback(req, ares);
+}
+
+/* calls the original request callback
+ *
+ * params:
+ * req: the original request passed to your module
+ * ctrls: controls to send in the reply (must be a talloc pointer, steal)
+ * response: results for extended request (steal)
+ * error: LDB_SUCCESS for a successful return
+ * any other ldb error otherwise
+ */
+int ldb_module_done(struct ldb_request *req,
+ struct ldb_control **ctrls,
+ struct ldb_extended *response,
+ int error)
+{
+ struct ldb_reply *ares;
+
+ ares = talloc_zero(req, struct ldb_reply);
+ if (!ares) {
+ ldb_oom(req->handle->ldb);
+ req->callback(req, NULL);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ares->type = LDB_REPLY_DONE;
+ ares->controls = talloc_steal(ares, ctrls);
+ ares->response = talloc_steal(ares, response);
+ ares->error = error;
+
+ req->handle->flags |= LDB_HANDLE_FLAG_DONE_CALLED;
+
+ if ((req->handle->ldb->flags & LDB_FLG_ENABLE_TRACING) &&
+ req->handle->nesting == 0) {
+ ldb_debug_add(req->handle->ldb, "ldb_trace_response: DONE\n");
+ ldb_debug_add(req->handle->ldb, "error: %d\n", error);
+ if (ldb_errstring(req->handle->ldb)) {
+ ldb_debug_add(req->handle->ldb, "msg: %s\n",
+ ldb_errstring(req->handle->ldb));
+ }
+ ldb_debug_end(req->handle->ldb, LDB_DEBUG_TRACE);
+ }
+
+ return req->callback(req, ares);
+}
+
+/* to be used *only* in modules init functions.
+ * this function is synchronous and will register
+ * the requested OID in the rootdse module if present
+ * otherwise it will return an error */
+int ldb_mod_register_control(struct ldb_module *module, const char *oid)
+{
+ struct ldb_request *req;
+ int ret;
+
+ req = talloc_zero(module, struct ldb_request);
+ if (req == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req->operation = LDB_REQ_REGISTER_CONTROL;
+ req->op.reg_control.oid = oid;
+ req->callback = ldb_op_default_callback;
+
+ ldb_set_timeout(module->ldb, req, 0);
+
+ req->handle = ldb_handle_new(req, module->ldb);
+ if (req->handle == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_request(module->ldb, req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+ talloc_free(req);
+
+ return ret;
+}
+
+static int ldb_modules_load_dir(const char *modules_dir, const char *version);
+
+
+/*
+ load one module. A static list of loaded module inode numbers is
+ used to prevent a module being loaded twice
+
+ dlopen() is used on the module, and dlsym() is then used to look for
+ a ldb_init_module() function. If present, that function is called
+ with the ldb version number as an argument.
+
+ The ldb_init_module() function will typically call
+ ldb_register_module() and ldb_register_backend() to register a
+ module or backend, but it may also be used to register command line
+ handling functions, ldif handlers or any other local
+ modififications.
+
+ The ldb_init_module() function does not get a ldb_context passed in,
+ as modules will be used for multiple ldb context handles. The call
+ from the first ldb_init() is just a convenient way to ensure it is
+ called early enough.
+ */
+static int ldb_modules_load_path(const char *path, const char *version)
+{
+ void *handle;
+ int (*init_fn)(const char *);
+ int ret;
+ struct stat st;
+ static struct loaded {
+ struct loaded *next, *prev;
+ ino_t st_ino;
+ dev_t st_dev;
+ } *loaded;
+ struct loaded *le;
+ int dlopen_flags;
+
+#ifdef RTLD_DEEPBIND
+ bool deepbind_enabled = (getenv("LDB_MODULES_DISABLE_DEEPBIND") == NULL);
+#endif
+
+ ret = stat(path, &st);
+ if (ret != 0) {
+ fprintf(stderr, "ldb: unable to stat module %s : %s\n", path, strerror(errno));
+ return LDB_ERR_UNAVAILABLE;
+ }
+
+ for (le=loaded; le; le=le->next) {
+ if (le->st_ino == st.st_ino &&
+ le->st_dev == st.st_dev) {
+ /* its already loaded */
+ return LDB_SUCCESS;
+ }
+ }
+
+ le = talloc(loaded, struct loaded);
+ if (le == NULL) {
+ fprintf(stderr, "ldb: unable to allocated loaded entry\n");
+ return LDB_ERR_UNAVAILABLE;
+ }
+
+ le->st_ino = st.st_ino;
+ le->st_dev = st.st_dev;
+
+ DLIST_ADD_END(loaded, le);
+
+ /* if it is a directory, recurse */
+ if (S_ISDIR(st.st_mode)) {
+ return ldb_modules_load_dir(path, version);
+ }
+
+ dlopen_flags = RTLD_NOW;
+#ifdef RTLD_DEEPBIND
+ /*
+ * use deepbind if possible, to avoid issues with different
+ * system library variants, for example ldb modules may be linked
+ * against Heimdal while the application may use MIT kerberos.
+ *
+ * See the dlopen manpage for details.
+ *
+ * One typical user is the bind_dlz module of Samba,
+ * but symbol versioning might be enough...
+ *
+ * We need a way to disable this in order to allow the
+ * ldb_*ldap modules to work with a preloaded socket wrapper.
+ *
+ * So in future we may remove this completely
+ * or at least invert the default behavior.
+ */
+ if (deepbind_enabled) {
+ dlopen_flags |= RTLD_DEEPBIND;
+ }
+#endif
+
+ handle = dlopen(path, dlopen_flags);
+ if (handle == NULL) {
+ fprintf(stderr, "ldb: unable to dlopen %s : %s\n", path, dlerror());
+ return LDB_SUCCESS;
+ }
+
+ init_fn = dlsym(handle, "ldb_init_module");
+ if (init_fn == NULL) {
+ /* ignore it, it could be an old-style
+ * module. Once we've converted all modules we
+ * could consider this an error */
+ dlclose(handle);
+ return LDB_SUCCESS;
+ }
+
+ ret = init_fn(version);
+ if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
+ /* the module is already registered - ignore this, as
+ * it can happen if LDB_MODULES_PATH points at both
+ * the build and install directory
+ */
+ ret = LDB_SUCCESS;
+ }
+ return ret;
+}
+
+static int qsort_string(const char **s1, const char **s2)
+{
+ return strcmp(*s1, *s2);
+}
+
+
+/*
+ load all modules from the given ldb modules directory. This is run once
+ during the first ldb_init() call.
+
+ Modules are loaded in alphabetical order to ensure that any module
+ load ordering dependencies are reproducible. Modules should avoid
+ relying on load order
+ */
+static int ldb_modules_load_dir(const char *modules_dir, const char *version)
+{
+ DIR *dir;
+ struct dirent *de;
+ const char **modlist = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ unsigned i, num_modules = 0;
+
+ dir = opendir(modules_dir);
+ if (dir == NULL) {
+ if (errno == ENOENT) {
+ talloc_free(tmp_ctx);
+ /* we don't have any modules */
+ return LDB_SUCCESS;
+ }
+ talloc_free(tmp_ctx);
+ fprintf(stderr, "ldb: unable to open modules directory '%s' - %s\n",
+ modules_dir, strerror(errno));
+ return LDB_ERR_UNAVAILABLE;
+ }
+
+
+ while ((de = readdir(dir))) {
+ if (ISDOT(de->d_name) || ISDOTDOT(de->d_name))
+ continue;
+
+ modlist = talloc_realloc(tmp_ctx, modlist, const char *, num_modules+1);
+ if (modlist == NULL) {
+ talloc_free(tmp_ctx);
+ closedir(dir);
+ fprintf(stderr, "ldb: unable to allocate modules list\n");
+ return LDB_ERR_UNAVAILABLE;
+ }
+ modlist[num_modules] = talloc_asprintf(modlist, "%s/%s", modules_dir, de->d_name);
+ if (modlist[num_modules] == NULL) {
+ talloc_free(tmp_ctx);
+ closedir(dir);
+ fprintf(stderr, "ldb: unable to allocate module list entry\n");
+ return LDB_ERR_UNAVAILABLE;
+ }
+ num_modules++;
+ }
+
+ closedir(dir);
+
+ /* sort the directory, so we get consistent load ordering */
+ TYPESAFE_QSORT(modlist, num_modules, qsort_string);
+
+ for (i=0; i<num_modules; i++) {
+ int ret = ldb_modules_load_path(modlist[i], version);
+ if (ret != LDB_SUCCESS) {
+ fprintf(stderr, "ldb: failed to initialise module %s : %s\n",
+ modlist[i], ldb_strerror(ret));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ }
+
+ talloc_free(tmp_ctx);
+
+ return LDB_SUCCESS;
+}
+
+/*
+ load any additional modules from the given directory
+*/
+void ldb_set_modules_dir(struct ldb_context *ldb, const char *path)
+{
+ int ret = ldb_modules_load_dir(path, LDB_VERSION);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "Failed to load modules from: %s\n", path);
+ }
+}
+
+
+/*
+ load all modules static (builtin) modules
+ */
+static int ldb_modules_load_static(const char *version)
+{
+ static bool initialised;
+#define _MODULE_PROTO(init) extern int init(const char *);
+ STATIC_ldb_MODULES_PROTO;
+ const ldb_module_init_fn static_init_functions[] = { STATIC_ldb_MODULES };
+ unsigned i;
+
+ if (initialised) {
+ return LDB_SUCCESS;
+ }
+ initialised = true;
+
+ for (i=0; static_init_functions[i]; i++) {
+ int ret = static_init_functions[i](version);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ return LDB_SUCCESS;
+}
+
+/*
+ load all modules from the given ldb modules path, colon
+ separated.
+
+ modules are loaded recursively for all subdirectories in the paths
+ */
+int ldb_modules_load(const char *modules_path, const char *version)
+{
+ char *tok, *path, *tok_ptr=NULL;
+ int ret;
+
+ ret = ldb_modules_load_static(version);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ path = talloc_strdup(NULL, modules_path);
+ if (path == NULL) {
+ fprintf(stderr, "ldb: failed to allocate modules_path\n");
+ return LDB_ERR_UNAVAILABLE;
+ }
+
+ for (tok=strtok_r(path, ":", &tok_ptr);
+ tok;
+ tok=strtok_r(NULL, ":", &tok_ptr)) {
+ ret = ldb_modules_load_path(tok, version);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(path);
+ return ret;
+ }
+ }
+ talloc_free(path);
+
+ return LDB_SUCCESS;
+}
+
+
+/*
+ return a string representation of the calling chain for the given
+ ldb request
+ */
+char *ldb_module_call_chain(struct ldb_request *req, TALLOC_CTX *mem_ctx)
+{
+ char *ret;
+ unsigned int i = 0;
+
+ ret = talloc_strdup(mem_ctx, "");
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ while (req && req->handle) {
+ char *s = talloc_asprintf_append_buffer(ret, "req[%u] %p : %s\n",
+ i++, req, ldb_req_location(req));
+ if (s == NULL) {
+ talloc_free(ret);
+ return NULL;
+ }
+ ret = s;
+ req = req->handle->parent;
+ }
+ return ret;
+}
+
+
+/*
+ return the next module in the chain
+ */
+struct ldb_module *ldb_module_next(struct ldb_module *module)
+{
+ return module->next;
+}
+
+/*
+ set the next module in the module chain
+ */
+void ldb_module_set_next(struct ldb_module *module, struct ldb_module *next)
+{
+ module->next = next;
+}
+
+
+/*
+ get the popt_options pointer in the ldb structure. This allows a ldb
+ module to change the command line parsing
+ */
+struct poptOption **ldb_module_popt_options(struct ldb_context *ldb)
+{
+ return &ldb->popt_options;
+}
+
+
+/*
+ return the current ldb flags LDB_FLG_*
+ */
+uint32_t ldb_module_flags(struct ldb_context *ldb)
+{
+ return ldb->flags;
+}