diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
commit | 4f5791ebd03eaec1c7da0865a383175b05102712 (patch) | |
tree | 8ce7b00f7a76baa386372422adebbe64510812d4 /lib/ldb/common/ldb_modules.c | |
parent | Initial commit. (diff) | |
download | samba-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.c | 1242 |
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; +} |