
This is the second of two commits in a series that must be picked together. This series of two commits moves parts of lib/krb5/ infrastructure functionality to lib/base/, leaving behind wrappers. Some parts of libkrb5 are entirely generic or easily made so, and could be useful in various parts of Heimdal that are not specific to the krb5 API, such as: - lib/gssapi/ (especially since the integration of NegoEx) - lib/hx509/ - bx509d (which should really move out of kdc/) For the above we need to move these bits of lib/krb5/: - lib/krb5/config_file.c (all of it, leaving forwardings behind) - lib/krb5/config_reg.c (all of it) - lib/krb5/plugin.c (all of it, leaving forwardings behind) - lib/krb5/log.c (all of it, ditto) - lib/krb5/heim_err.et (all of it) And because of those two, these too must also move: - lib/krb5/expand_path.c (all of it, leaving forwardings behind) - lib/krb5/warn.c (just the warning functions, ditto) The changes to the moved files are mostly quite straightforward and are best reviewed with --word-diff=color. We're also creating a heim_context and a heim API to go with it. But it's as thin as possible, with as little state as necessary to enable this move. Functions for dealing with error messages use callbacks. Moving plugin.c does have one knock-on effect on all users of the old krb5 plugin API (which remains), which is that a global search and replace of struct krb5_plugin_data to struct heim_plugin_data was needed, though the layout and size of that structure doesn't change, so the ABI doesn't either. As well, we now build lib/vers/ and lib/com_err/ before lib/base/ so as to be able to move lib/krb5/heim_err.et to lib/base/ so that we can make use of HEIM_ERR_* in lib/base/, specifically in the files that moved. Once this is all done we'll be able to use config files and plugins in lib/hx509/, we'll be able to move bx509d out of kdc/, and so on. Most if not all of the new functions in lib/base/ are Heimdal-private, thus calling conventions for them are not declared. Status: - builds and passes CIs (Travis, Appveyor) - ran make check-valgrind and no new leaks or other memory errors - ready for review HOW TO REVIEW: $ # Review file moves: $ git log --stat -n1 HEAD^ $ $ # Review changes to moved files using --word-diff=color $ git log -p -b -w --word-diff=color HEAD^..HEAD \ lib/base/config_file.c \ lib/base/config_reg.c \ lib/base/expand_path.c \ lib/base/warn.c \ lib/krb5/config_file.c \ lib/krb5/config_reg.c \ lib/krb5/expand_path.c \ lib/krb5/warn.c $ $ # Review the whole thing, possibly adding -b and/or -w, and $ # maybe --word-diff=color: $ git log -p origin/master..HEAD $ git log -p -b -w origin/master..HEAD $ git log -p -b -w --word-diff=color origin/master..HEAD TBD (future commits): - make lib/gssapi use the new heimbase functions - move kx509/bx509d common code to lib/hx509/ or other approp. location - move bx509d out of kdc/
767 lines
22 KiB
C
767 lines
22 KiB
C
/*
|
|
* Copyright (c) 2006 - 2020 Kungliga Tekniska Högskolan
|
|
* (Royal Institute of Technology, Stockholm, Sweden).
|
|
* All rights reserved.
|
|
*
|
|
* Portions Copyright (c) 2018 AuriStor, Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the Institute nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "baselocl.h"
|
|
#include "common_plugin.h"
|
|
|
|
/*
|
|
* Documentation for the Heimdal plugin system is in lib/krb5/plugin.c and
|
|
* lib/krb5/krb5-plugin.7.
|
|
*/
|
|
|
|
/*
|
|
* Definitions:
|
|
*
|
|
* module - a category of plugin module, identified by subsystem
|
|
* (e.g., "krb5")
|
|
* dso - a library for a module containing a map of plugin
|
|
* types to plugins (e.g. "service_locator")
|
|
* plugin - a set of callbacks and state that follows the
|
|
* common plugin module definition (version, init, fini)
|
|
*
|
|
* Obviously it would have been clearer to use the term "module" rather than
|
|
* "DSO" given there is an internal "DSO", but "module" was already taken...
|
|
*
|
|
* modules := { module: dsos }
|
|
* dsos := { path, dsohandle, plugins-by-name }
|
|
* plugins-by-name := { plugin-name: [plug] }
|
|
* plug := { ftable, ctx }
|
|
*/
|
|
|
|
/* global module use, use copy_modules() accessor to access */
|
|
static heim_dict_t __modules;
|
|
|
|
static HEIMDAL_MUTEX modules_mutex = HEIMDAL_MUTEX_INITIALIZER;
|
|
|
|
static void
|
|
copy_modules_once(void *context)
|
|
{
|
|
heim_dict_t *modules = (heim_dict_t *)context;
|
|
|
|
*modules = heim_dict_create(11);
|
|
heim_assert(*modules, "plugin modules array allocation failure");
|
|
}
|
|
|
|
/* returns global modules list, refcount +1 */
|
|
static heim_dict_t
|
|
copy_modules(void)
|
|
{
|
|
static heim_base_once_t modules_once = HEIM_BASE_ONCE_INIT;
|
|
|
|
heim_base_once_f(&modules_once, &__modules, copy_modules_once);
|
|
|
|
return heim_retain(__modules);
|
|
}
|
|
|
|
/* returns named module, refcount +1 */
|
|
static heim_dict_t
|
|
copy_module(const char *name)
|
|
{
|
|
heim_string_t module_name = heim_string_create(name);
|
|
heim_dict_t modules = copy_modules();
|
|
heim_dict_t module;
|
|
|
|
module = heim_dict_copy_value(modules, module_name);
|
|
if (module == NULL) {
|
|
module = heim_dict_create(11);
|
|
heim_dict_set_value(modules, module_name, module);
|
|
}
|
|
|
|
heim_release(modules);
|
|
heim_release(module_name);
|
|
|
|
return module;
|
|
}
|
|
|
|
/* DSO helpers */
|
|
struct heim_dso {
|
|
heim_string_t path;
|
|
heim_dict_t plugins_by_name;
|
|
void *dsohandle;
|
|
};
|
|
|
|
static void
|
|
dso_dealloc(void *ptr)
|
|
{
|
|
struct heim_dso *p = ptr;
|
|
|
|
heim_release(p->path);
|
|
heim_release(p->plugins_by_name);
|
|
#ifdef HAVE_DLOPEN
|
|
if (p->dsohandle)
|
|
dlclose(p->dsohandle);
|
|
#endif
|
|
}
|
|
|
|
/* returns internal "DSO" for name, refcount +1 */
|
|
static struct heim_dso *
|
|
copy_internal_dso(const char *name)
|
|
{
|
|
heim_string_t dso_name = HSTR("__HEIMDAL_INTERNAL_DSO__");
|
|
heim_dict_t module = copy_module(name);
|
|
struct heim_dso *dso;
|
|
|
|
if (module == NULL)
|
|
return NULL;
|
|
|
|
dso = heim_dict_copy_value(module, dso_name);
|
|
if (dso == NULL) {
|
|
dso = heim_alloc(sizeof(*dso), "heim-dso", dso_dealloc);
|
|
|
|
dso->path = dso_name;
|
|
dso->plugins_by_name = heim_dict_create(11);
|
|
|
|
heim_dict_set_value(module, dso_name, dso);
|
|
}
|
|
|
|
heim_release(module);
|
|
|
|
return dso;
|
|
}
|
|
|
|
struct heim_plugin {
|
|
heim_plugin_common_ftable_p ftable;
|
|
void *ctx;
|
|
};
|
|
|
|
static void
|
|
plugin_free(void *ptr)
|
|
{
|
|
struct heim_plugin *pl = ptr;
|
|
|
|
if (pl->ftable && pl->ftable->fini)
|
|
pl->ftable->fini(pl->ctx);
|
|
}
|
|
|
|
struct heim_plugin_register_ctx {
|
|
void *symbol;
|
|
int is_dup;
|
|
};
|
|
|
|
static void
|
|
plugin_register_check_dup(heim_object_t value, void *ctx, int *stop)
|
|
{
|
|
struct heim_plugin_register_ctx *pc = ctx;
|
|
struct heim_plugin *pl = value;
|
|
|
|
if (pl->ftable == pc->symbol) {
|
|
pc->is_dup = 1;
|
|
*stop = 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register a plugin symbol name of specific type.
|
|
* @param context a Keberos context
|
|
* @param module name of plugin module (e.g., "krb5")
|
|
* @param name name of plugin symbol (e.g., "krb5_plugin_kuserok")
|
|
* @param ftable a pointer to a function pointer table
|
|
* @return In case of error a non zero error com_err error is returned
|
|
* and the Kerberos error string is set.
|
|
*
|
|
* @ingroup heim_support
|
|
*/
|
|
|
|
heim_error_code
|
|
heim_plugin_register(heim_context context,
|
|
heim_pcontext pcontext,
|
|
const char *module,
|
|
const char *name,
|
|
void *ftable)
|
|
{
|
|
heim_error_code ret;
|
|
heim_array_t plugins;
|
|
heim_string_t hname;
|
|
struct heim_dso *dso;
|
|
struct heim_plugin_register_ctx ctx;
|
|
|
|
ctx.symbol = ftable;
|
|
ctx.is_dup = 0;
|
|
|
|
HEIMDAL_MUTEX_lock(&modules_mutex);
|
|
|
|
dso = copy_internal_dso(module);
|
|
hname = heim_string_create(name);
|
|
plugins = heim_dict_copy_value(dso->plugins_by_name, hname);
|
|
if (plugins != NULL)
|
|
heim_array_iterate_f(plugins, &ctx, plugin_register_check_dup);
|
|
else {
|
|
plugins = heim_array_create();
|
|
heim_dict_set_value(dso->plugins_by_name, hname, plugins);
|
|
}
|
|
|
|
ret = 0;
|
|
if (!ctx.is_dup) {
|
|
/* Note: refactored plugin API only supports common plugin layout */
|
|
struct heim_plugin *pl;
|
|
|
|
pl = heim_alloc(sizeof(*pl), "heim-plugin", plugin_free);
|
|
if (pl == NULL) {
|
|
ret = heim_enomem(context);
|
|
} else {
|
|
pl->ftable = ftable;
|
|
ret = pl->ftable->init(pcontext, &pl->ctx);
|
|
if (ret == 0) {
|
|
heim_array_append_value(plugins, pl);
|
|
heim_debug(context, 5, "Registered %s plugin", name);
|
|
}
|
|
heim_release(pl);
|
|
}
|
|
}
|
|
|
|
HEIMDAL_MUTEX_unlock(&modules_mutex);
|
|
|
|
heim_release(dso);
|
|
heim_release(hname);
|
|
heim_release(plugins);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef HAVE_DLOPEN
|
|
|
|
static char *
|
|
resolve_origin(const char *di, const char *module)
|
|
{
|
|
#ifdef HAVE_DLADDR
|
|
Dl_info dl_info;
|
|
const char *dname;
|
|
char *path, *p;
|
|
|
|
if (strncmp(di, "$ORIGIN/", sizeof("$ORIGIN/") - 1) &&
|
|
strcmp(di, "$ORIGIN"))
|
|
return strdup(di);
|
|
|
|
di += sizeof("$ORIGIN") - 1;
|
|
|
|
if (dladdr(heim_plugin_register, &dl_info) == 0) {
|
|
char *s = NULL;
|
|
|
|
/* dladdr() failed */
|
|
if (asprintf(&s, LIBDIR "/plugin/%s", module) == -1)
|
|
return NULL;
|
|
return s;
|
|
}
|
|
|
|
dname = dl_info.dli_fname;
|
|
#ifdef _WIN32
|
|
p = strrchr(dname, '\\');
|
|
if (p == NULL)
|
|
#endif
|
|
p = strrchr(dname, '/');
|
|
if (p) {
|
|
if (asprintf(&path, "%.*s%s", (int) (p - dname), dname, di) == -1)
|
|
return NULL;
|
|
} else {
|
|
if (asprintf(&path, "%s%s", dname, di) == -1)
|
|
return NULL;
|
|
}
|
|
|
|
return path;
|
|
#else
|
|
char *s = NULL;
|
|
|
|
if (strncmp(di, "$ORIGIN/", sizeof("$ORIGIN/") - 1) &&
|
|
strcmp(di, "$ORIGIN"))
|
|
return strdup(di);
|
|
if (asprintf(&s, LIBDIR "/plugin/%s", module) == -1)
|
|
return NULL;
|
|
return s;
|
|
#endif /* HAVE_DLADDR */
|
|
}
|
|
|
|
#endif /* HAVE_DLOPEN */
|
|
|
|
/**
|
|
* Load plugins (new system) for the given module @module from the given
|
|
* directory @paths.
|
|
*
|
|
* Inputs:
|
|
*
|
|
* @context A heim_context
|
|
* @module Name of plugin module (typically "krb5")
|
|
* @paths Array of directory paths where to look
|
|
*/
|
|
void
|
|
heim_load_plugins(heim_context context,
|
|
const char *module,
|
|
const char **paths)
|
|
{
|
|
#ifdef HAVE_DLOPEN
|
|
heim_string_t s = heim_string_create(module);
|
|
heim_dict_t mod, modules;
|
|
struct dirent *entry;
|
|
heim_error_code ret;
|
|
const char **di;
|
|
char *dirname = NULL;
|
|
DIR *d;
|
|
#ifdef _WIN32
|
|
char *plugin_prefix;
|
|
size_t plugin_prefix_len;
|
|
|
|
if (asprintf(&plugin_prefix, "plugin_%s_", module) == -1)
|
|
return;
|
|
plugin_prefix_len = (plugin_prefix ? strlen(plugin_prefix) : 0);
|
|
#endif
|
|
|
|
HEIMDAL_MUTEX_lock(&modules_mutex);
|
|
|
|
modules = copy_modules();
|
|
|
|
mod = heim_dict_copy_value(modules, s);
|
|
if (mod == NULL) {
|
|
mod = heim_dict_create(11);
|
|
if (mod == NULL) {
|
|
HEIMDAL_MUTEX_unlock(&modules_mutex);
|
|
heim_release(s);
|
|
heim_release(modules);
|
|
return;
|
|
}
|
|
heim_dict_set_value(modules, s, mod);
|
|
}
|
|
heim_release(s);
|
|
heim_release(modules);
|
|
|
|
for (di = paths; *di != NULL; di++) {
|
|
free(dirname);
|
|
dirname = resolve_origin(*di, module);
|
|
if (dirname == NULL)
|
|
continue;
|
|
d = opendir(dirname);
|
|
if (d == NULL)
|
|
continue;
|
|
rk_cloexec_dir(d);
|
|
|
|
while ((entry = readdir(d)) != NULL) {
|
|
char *n = entry->d_name;
|
|
char *path = NULL;
|
|
heim_string_t spath;
|
|
struct heim_dso *p;
|
|
|
|
/* skip . and .. */
|
|
if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')))
|
|
continue;
|
|
|
|
ret = 0;
|
|
#ifdef _WIN32
|
|
/*
|
|
* On Windows, plugins must be loaded from the same directory as
|
|
* heimdal.dll (typically the assembly directory) and must have
|
|
* the name form "plugin_<module>_<name>.dll".
|
|
*/
|
|
{
|
|
char *ext;
|
|
|
|
if (strnicmp(n, plugin_prefix, plugin_prefix_len))
|
|
continue;
|
|
ext = strrchr(n, '.');
|
|
if (ext == NULL || stricmp(ext, ".dll"))
|
|
continue;
|
|
|
|
ret = asprintf(&path, "%s\\%s", dirname, n);
|
|
if (ret < 0 || path == NULL)
|
|
continue;
|
|
}
|
|
#endif
|
|
#ifdef __APPLE__
|
|
{ /* support loading bundles on MacOS */
|
|
size_t len = strlen(n);
|
|
if (len > 7 && strcmp(&n[len - 7], ".bundle") == 0)
|
|
ret = asprintf(&path, "%s/%s/Contents/MacOS/%.*s", dirname, n, (int)(len - 7), n);
|
|
}
|
|
#endif
|
|
if (ret < 0 || path == NULL)
|
|
ret = asprintf(&path, "%s/%s", dirname, n);
|
|
|
|
if (ret < 0 || path == NULL)
|
|
continue;
|
|
|
|
spath = heim_string_create(n);
|
|
if (spath == NULL) {
|
|
free(path);
|
|
continue;
|
|
}
|
|
|
|
/* check if already cached */
|
|
p = heim_dict_copy_value(mod, spath);
|
|
if (p == NULL) {
|
|
p = heim_alloc(sizeof(*p), "heim-dso", dso_dealloc);
|
|
if (p)
|
|
p->dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY|RTLD_GROUP);
|
|
if (p && p->dsohandle) {
|
|
p->path = heim_retain(spath);
|
|
p->plugins_by_name = heim_dict_create(11);
|
|
heim_dict_set_value(mod, spath, p);
|
|
}
|
|
}
|
|
heim_release(p);
|
|
heim_release(spath);
|
|
free(path);
|
|
}
|
|
closedir(d);
|
|
}
|
|
free(dirname);
|
|
HEIMDAL_MUTEX_unlock(&modules_mutex);
|
|
heim_release(mod);
|
|
#ifdef _WIN32
|
|
if (plugin_prefix)
|
|
free(plugin_prefix);
|
|
#endif
|
|
#endif /* HAVE_DLOPEN */
|
|
}
|
|
|
|
/**
|
|
* Unload plugins of the given @module name.
|
|
*
|
|
* Params:
|
|
*
|
|
* @module Name of module whose plusins to unload.
|
|
*/
|
|
void
|
|
heim_unload_plugins(heim_context context, const char *module)
|
|
{
|
|
heim_string_t sname = heim_string_create(module);
|
|
heim_dict_t modules;
|
|
|
|
HEIMDAL_MUTEX_lock(&modules_mutex);
|
|
|
|
modules = copy_modules();
|
|
heim_dict_delete_key(modules, sname);
|
|
|
|
HEIMDAL_MUTEX_unlock(&modules_mutex);
|
|
|
|
heim_release(modules);
|
|
heim_release(sname);
|
|
}
|
|
|
|
struct iter_ctx {
|
|
heim_context context;
|
|
heim_pcontext pcontext;
|
|
heim_string_t n;
|
|
struct heim_plugin_data *caller;
|
|
int flags;
|
|
heim_array_t result;
|
|
int32_t (HEIM_LIB_CALL *func)(void *, const void *, void *, void *);
|
|
void *userctx;
|
|
int32_t ret;
|
|
int32_t plugin_no_handle_retval;
|
|
};
|
|
|
|
#ifdef HAVE_DLOPEN
|
|
/*
|
|
* Add plugin from a DSO that exports the plugin structure directly. This is
|
|
* provided for backwards compatibility with prior versions of Heimdal, but it
|
|
* does not allow a module to export multiple plugins, nor does it allow
|
|
* instance validation.
|
|
*/
|
|
static heim_array_t
|
|
add_dso_plugin_struct(heim_context context,
|
|
heim_pcontext pcontext,
|
|
const char *dsopath,
|
|
void *dsohandle,
|
|
const char *name)
|
|
{
|
|
heim_error_code ret;
|
|
heim_plugin_common_ftable_p cpm;
|
|
struct heim_plugin *pl;
|
|
heim_array_t plugins;
|
|
|
|
if (dsohandle == NULL)
|
|
return NULL;
|
|
|
|
/* suppress error here because we may be looking for a different plugin type */
|
|
cpm = (heim_plugin_common_ftable_p)dlsym(dsohandle, name);
|
|
if (cpm == NULL)
|
|
return NULL;
|
|
|
|
heim_warnx(context, "plugin %s uses deprecated loading mechanism", dsopath);
|
|
|
|
pl = heim_alloc(sizeof(*pl), "heim-plugin", plugin_free);
|
|
|
|
ret = cpm->init(pcontext, &pl->ctx);
|
|
if (ret) {
|
|
heim_warn(context, ret, "plugin %s failed to initialize", dsopath);
|
|
heim_release(pl);
|
|
return NULL;
|
|
}
|
|
|
|
pl->ftable = cpm;
|
|
|
|
plugins = heim_array_create();
|
|
heim_array_append_value(plugins, pl);
|
|
heim_release(pl);
|
|
|
|
return plugins;
|
|
}
|
|
|
|
static int
|
|
validate_plugin_deps(heim_context context,
|
|
struct heim_plugin_data *caller,
|
|
const char *dsopath,
|
|
heim_get_instance_func_t get_instance)
|
|
{
|
|
size_t i;
|
|
|
|
if (get_instance == NULL) {
|
|
heim_warnx(context, "plugin %s omitted instance callback",
|
|
dsopath);
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; caller->deps[i] != NULL; i++) {
|
|
uintptr_t heim_instance, plugin_instance;
|
|
|
|
heim_instance = caller->get_instance(caller->deps[i]);
|
|
plugin_instance = get_instance(caller->deps[i]);
|
|
|
|
if (heim_instance == 0 || plugin_instance == 0)
|
|
continue;
|
|
|
|
if (heim_instance != plugin_instance) {
|
|
heim_warnx(context, "plugin %s library %s linked against different "
|
|
"instance of Heimdal (got %zu, us %zu)",
|
|
dsopath, caller->deps[i],
|
|
plugin_instance, heim_instance);
|
|
return FALSE;
|
|
}
|
|
heim_debug(context, 10, "Validated plugin library dependency %s for %s",
|
|
caller->deps[i], dsopath);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* New interface from Heimdal 8 where a DSO can export a load function
|
|
* that can return both a Heimdal instance identifier along with an
|
|
* array of plugins.
|
|
*/
|
|
static heim_array_t
|
|
add_dso_plugins_load_fn(heim_context context,
|
|
heim_pcontext pcontext,
|
|
struct heim_plugin_data *caller,
|
|
const char *dsopath,
|
|
void *dsohandle)
|
|
{
|
|
heim_error_code ret;
|
|
heim_array_t plugins;
|
|
heim_plugin_load_t load_fn;
|
|
char *sym;
|
|
size_t i;
|
|
heim_get_instance_func_t get_instance;
|
|
size_t n_ftables;
|
|
heim_plugin_common_ftable_cp *ftables;
|
|
|
|
if (asprintf(&sym, "%s_plugin_load", caller->name) == -1)
|
|
return NULL;
|
|
|
|
/* suppress error here because we may be looking for a different plugin type */
|
|
load_fn = (heim_plugin_load_t)dlsym(dsohandle, sym);
|
|
free(sym);
|
|
if (load_fn == NULL)
|
|
return NULL;
|
|
|
|
ret = load_fn(pcontext, &get_instance, &n_ftables, &ftables);
|
|
if (ret) {
|
|
heim_warn(context, ret, "plugin %s failed to load", dsopath);
|
|
|
|
/* fallback to loading structure directly */
|
|
return add_dso_plugin_struct(context, pcontext, dsopath,
|
|
dsohandle, caller->name);
|
|
}
|
|
|
|
if (!validate_plugin_deps(context, caller, dsopath, get_instance))
|
|
return NULL;
|
|
|
|
plugins = heim_array_create();
|
|
|
|
for (i = 0; i < n_ftables; i++) {
|
|
heim_plugin_common_ftable_cp cpm = ftables[i];
|
|
struct heim_plugin *pl;
|
|
|
|
pl = heim_alloc(sizeof(*pl), "heim-plugin", plugin_free);
|
|
|
|
ret = cpm->init(pcontext, &pl->ctx);
|
|
if (ret) {
|
|
heim_warn(context, ret, "plugin %s[%zu] failed to initialize",
|
|
dsopath, i);
|
|
} else {
|
|
pl->ftable = rk_UNCONST(cpm);
|
|
heim_array_append_value(plugins, pl);
|
|
}
|
|
heim_release(pl);
|
|
}
|
|
|
|
return plugins;
|
|
}
|
|
#endif /* HAVE_DLOPEN */
|
|
|
|
static void
|
|
reduce_by_version(heim_object_t value, void *ctx, int *stop)
|
|
{
|
|
struct iter_ctx *s = ctx;
|
|
struct heim_plugin *pl = value;
|
|
|
|
if (pl->ftable && pl->ftable->version >= s->caller->min_version)
|
|
heim_array_append_value(s->result, pl);
|
|
}
|
|
|
|
static void
|
|
search_modules(heim_object_t key, heim_object_t value, void *ctx)
|
|
{
|
|
struct iter_ctx *s = ctx;
|
|
struct heim_dso *p = value;
|
|
heim_array_t plugins = heim_dict_copy_value(p->plugins_by_name, s->n);
|
|
|
|
#ifdef HAVE_DLOPEN
|
|
if (plugins == NULL && p->dsohandle) {
|
|
const char *path = heim_string_get_utf8(p->path);
|
|
|
|
plugins = add_dso_plugins_load_fn(s->context,
|
|
s->pcontext,
|
|
s->caller,
|
|
path,
|
|
p->dsohandle);
|
|
if (plugins) {
|
|
heim_dict_set_value(p->plugins_by_name, s->n, plugins);
|
|
heim_debug(s->context, 5, "Loaded %zu %s %s plugin%s from %s",
|
|
heim_array_get_length(plugins),
|
|
s->caller->module, s->caller->name,
|
|
heim_array_get_length(plugins) > 1 ? "s" : "",
|
|
path);
|
|
}
|
|
}
|
|
#endif /* HAVE_DLOPEN */
|
|
|
|
if (plugins) {
|
|
heim_array_iterate_f(plugins, s, reduce_by_version);
|
|
heim_release(plugins);
|
|
}
|
|
}
|
|
|
|
static void
|
|
eval_results(heim_object_t value, void *ctx, int *stop)
|
|
{
|
|
struct heim_plugin *pl = value;
|
|
struct iter_ctx *s = ctx;
|
|
|
|
if (s->ret != s->plugin_no_handle_retval)
|
|
return;
|
|
|
|
s->ret = s->func(s->pcontext, pl->ftable, pl->ctx, s->userctx);
|
|
if (s->ret != s->plugin_no_handle_retval
|
|
&& !(s->flags & HEIM_PLUGIN_INVOKE_ALL))
|
|
*stop = 1;
|
|
}
|
|
|
|
/**
|
|
* Run plugins for the given @module (e.g., "krb5") and @name (e.g.,
|
|
* "kuserok"). Specifically, the @func is invoked once per-plugin with
|
|
* four arguments: the @context, the plugin symbol value (a pointer to a
|
|
* struct whose first three fields are the same as common_plugin_ftable),
|
|
* a context value produced by the plugin's init method, and @userctx.
|
|
*
|
|
* @func should unpack arguments for a plugin function and invoke it
|
|
* with arguments taken from @userctx. @func should save plugin
|
|
* outputs, if any, in @userctx.
|
|
*
|
|
* All loaded and registered plugins are invoked via @func until @func
|
|
* returns something other than @nohandle. Plugins that have nothing to
|
|
* do for the given arguments should return the same value as @nohandle.
|
|
*
|
|
* Inputs:
|
|
*
|
|
* @context A heim_context
|
|
* @pcontext A context for the plugin, such as a krb5_context
|
|
* @module Name of module (typically "krb5")
|
|
* @name Name of pluggable interface (e.g., "kuserok")
|
|
* @min_version Lowest acceptable plugin minor version number
|
|
* @flags Flags (none defined at this time)
|
|
* @nohandle Flags (none defined at this time)
|
|
* @userctx Callback data for the callback function @func
|
|
* @func A callback function, invoked once per-plugin
|
|
*
|
|
* Outputs: None, other than the return value and such outputs as are
|
|
* gathered by @func.
|
|
*/
|
|
heim_error_code
|
|
heim_plugin_run_f(heim_context context,
|
|
heim_pcontext pcontext,
|
|
struct heim_plugin_data *caller,
|
|
int flags,
|
|
int32_t nohandle,
|
|
void *userctx,
|
|
int32_t (HEIM_LIB_CALL *func)(void *, const void *, void *, void *))
|
|
{
|
|
heim_string_t m = heim_string_create(caller->module);
|
|
heim_dict_t modules, dict = NULL;
|
|
struct iter_ctx s;
|
|
|
|
s.context = context;
|
|
s.pcontext = pcontext;
|
|
s.caller = caller;
|
|
s.n = heim_string_create(caller->name);
|
|
s.flags = flags;
|
|
s.result = heim_array_create();
|
|
s.func = func;
|
|
s.userctx = userctx;
|
|
s.plugin_no_handle_retval = nohandle;
|
|
s.ret = nohandle;
|
|
|
|
HEIMDAL_MUTEX_lock(&modules_mutex);
|
|
|
|
/* Get loaded plugins */
|
|
modules = copy_modules();
|
|
dict = heim_dict_copy_value(modules, m);
|
|
|
|
/* Add loaded plugins to s.result array */
|
|
if (dict)
|
|
heim_dict_iterate_f(dict, &s, search_modules);
|
|
|
|
/* We don't need to hold modules_mutex during plugin invocation */
|
|
HEIMDAL_MUTEX_unlock(&modules_mutex);
|
|
|
|
/* Invoke loaded plugins */
|
|
heim_array_iterate_f(s.result, &s, eval_results);
|
|
|
|
heim_release(s.result);
|
|
heim_release(s.n);
|
|
heim_release(dict);
|
|
heim_release(m);
|
|
heim_release(modules);
|
|
|
|
return s.ret;
|
|
}
|