mirror of
https://github.com/samba-team/samba.git
synced 2025-01-06 13:18:07 +03:00
dc6927fdca
It should be off by default, as this is not needed by default. It crashes named on startup, if bind is built with jemalloc support. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15643 Signed-off-by: Andreas Schneider <asn@samba.org> Reviewed-by: Stefan Metzmacher <metze@samba.org>
1229 lines
30 KiB
C
1229 lines
30 KiB
C
/*
|
|
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 a referral
|
|
*
|
|
* 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
|
|
modifications.
|
|
|
|
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_ENABLE_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
|
|
/*
|
|
* On systems where e.g. different kerberos libraries are used, like a
|
|
* mix of Heimdal and MIT Kerberos, LDB_MODULES_ENABLE_DEEPBIND should
|
|
* be set to avoid issues.
|
|
*
|
|
* By default Linux distributions only have one Kerberos library.
|
|
*/
|
|
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) {
|
|
talloc_asprintf_addbuf(&ret, "req[%u] %p : %s\n",
|
|
i++, req, ldb_req_location(req));
|
|
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;
|
|
}
|