Move some infra bits of lib/krb5/ to lib/base/ (2)

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/
This commit is contained in:
Nicolas Williams
2020-02-24 18:50:01 -06:00
parent b2823cbd74
commit ea90ca8666
51 changed files with 3886 additions and 1962 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
* Copyright (c) 2006 - 2020 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -33,30 +33,31 @@
* SUCH DAMAGE.
*/
#include "krb5_locl.h"
#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
* (typically "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)
* 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 }
*
* Some existing plugin consumers outside libkrb5 use the "krb5" module
* namespace, but going forward the module should match the consumer library
* name (e.g. libhdb should use the "hdb" module rather than "krb5").
* 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 */
@@ -94,8 +95,8 @@ copy_module(const char *name)
module = heim_dict_copy_value(modules, module_name);
if (module == NULL) {
module = heim_dict_create(11);
heim_dict_set_value(modules, module_name, module);
module = heim_dict_create(11);
heim_dict_set_value(modules, module_name, module);
}
heim_release(modules);
@@ -105,7 +106,7 @@ copy_module(const char *name)
}
/* DSO helpers */
struct krb5_dso {
struct heim_dso {
heim_string_t path;
heim_dict_t plugins_by_name;
void *dsohandle;
@@ -114,35 +115,35 @@ struct krb5_dso {
static void
dso_dealloc(void *ptr)
{
struct krb5_dso *p = 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);
dlclose(p->dsohandle);
#endif
}
/* returns internal "DSO" for name, refcount +1 */
static struct krb5_dso *
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 krb5_dso *dso;
struct heim_dso *dso;
if (module == NULL)
return NULL;
return NULL;
dso = heim_dict_copy_value(module, dso_name);
if (dso == NULL) {
dso = heim_alloc(sizeof(*dso), "krb5-dso", dso_dealloc);
dso = heim_alloc(sizeof(*dso), "heim-dso", dso_dealloc);
dso->path = dso_name;
dso->plugins_by_name = heim_dict_create(11);
dso->path = dso_name;
dso->plugins_by_name = heim_dict_create(11);
heim_dict_set_value(module, dso_name, dso);
heim_dict_set_value(module, dso_name, dso);
}
heim_release(module);
@@ -150,21 +151,21 @@ copy_internal_dso(const char *name)
return dso;
}
struct krb5_plugin {
krb5_plugin_common_ftable_p ftable;
struct heim_plugin {
heim_plugin_common_ftable_p ftable;
void *ctx;
};
static void
plugin_free(void *ptr)
{
struct krb5_plugin *pl = ptr;
struct heim_plugin *pl = ptr;
if (pl->ftable && pl->ftable->fini)
pl->ftable->fini(pl->ctx);
pl->ftable->fini(pl->ctx);
}
struct krb5_plugin_register_ctx {
struct heim_plugin_register_ctx {
void *symbol;
int is_dup;
};
@@ -172,82 +173,73 @@ struct krb5_plugin_register_ctx {
static void
plugin_register_check_dup(heim_object_t value, void *ctx, int *stop)
{
struct krb5_plugin_register_ctx *pc = ctx;
struct krb5_plugin *pl = value;
struct heim_plugin_register_ctx *pc = ctx;
struct heim_plugin *pl = value;
if (pl->ftable == pc->symbol) {
pc->is_dup = 1;
*stop = 1;
pc->is_dup = 1;
*stop = 1;
}
}
/**
* Register a plugin symbol name of specific type.
* @param context a Keberos context
* @param type type of plugin symbol
* @param name name of plugin symbol
* @param symbol a pointer to the named symbol
* @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 krb5_support
* @ingroup heim_support
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_plugin_register(krb5_context context,
enum krb5_plugin_type type,
const char *name,
void *symbol)
heim_error_code
heim_plugin_register(heim_context context,
heim_pcontext pcontext,
const char *module,
const char *name,
void *ftable)
{
krb5_error_code ret;
heim_error_code ret;
heim_array_t plugins;
heim_string_t hname;
struct krb5_dso *dso;
struct krb5_plugin_register_ctx ctx;
struct heim_dso *dso;
struct heim_plugin_register_ctx ctx;
ctx.symbol = symbol;
ctx.symbol = ftable;
ctx.is_dup = 0;
/*
* It's not clear that PLUGIN_TYPE_FUNC was ever used or supported. It likely
* would have caused _krb5_plugin_run_f() to crash as the previous implementation
* assumed PLUGIN_TYPE_DATA.
*/
if (type != PLUGIN_TYPE_DATA) {
krb5_warnx(context, "krb5_plugin_register: PLUGIN_TYPE_DATA no longer supported");
return EINVAL;
}
HEIMDAL_MUTEX_lock(&modules_mutex);
dso = copy_internal_dso("krb5");
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);
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);
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 krb5_plugin *pl;
/* Note: refactored plugin API only supports common plugin layout */
struct heim_plugin *pl;
pl = heim_alloc(sizeof(*pl), "krb5-plugin", plugin_free);
if (pl == NULL) {
ret = krb5_enomem(context);
} else {
pl->ftable = symbol;
ret = pl->ftable->init(context, &pl->ctx);
if (ret == 0) {
heim_array_append_value(plugins, pl);
_krb5_debug(context, 5, "Registered %s plugin", name);
}
heim_release(pl);
}
} else
ret = 0; /* ignore duplicates to match previous behavior */
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);
@@ -261,32 +253,34 @@ krb5_plugin_register(krb5_context context,
#ifdef HAVE_DLOPEN
static char *
resolve_origin(const char *di)
resolve_origin(const char *di, const char *module)
{
#ifdef HAVE_DLADDR
Dl_info dl_info;
const char *dname;
char *path, *p;
#endif
if (strncmp(di, "$ORIGIN/", sizeof("$ORIGIN/") - 1) &&
strcmp(di, "$ORIGIN"))
return strdup(di);
#ifndef HAVE_DLADDR
return strdup(LIBDIR "/plugin/krb5");
#else /* !HAVE_DLADDR */
di += sizeof("$ORIGIN") - 1;
if (dladdr(_krb5_load_plugins, &dl_info) == 0)
return strdup(LIBDIR "/plugin/krb5");
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, '/');
p = strrchr(dname, '/');
if (p) {
if (asprintf(&path, "%.*s%s", (int) (p - dname), dname, di) == -1)
return NULL;
@@ -296,29 +290,40 @@ resolve_origin(const char *di)
}
return path;
#endif /* !HAVE_DLADDR */
#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 @name (typically
* "krb5") from the given directory @paths.
* Load plugins (new system) for the given module @module from the given
* directory @paths.
*
* Inputs:
*
* @context A krb5_context
* @name Name of plugin module (typically "krb5")
* @paths Array of directory paths where to look
* @context A heim_context
* @module Name of plugin module (typically "krb5")
* @paths Array of directory paths where to look
*/
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_load_plugins(krb5_context context, const char *name, const char **paths)
void
heim_load_plugins(heim_context context,
const char *module,
const char **paths)
{
#ifdef HAVE_DLOPEN
heim_string_t s = heim_string_create(name);
heim_dict_t module, modules;
heim_string_t s = heim_string_create(module);
heim_dict_t mod, modules;
struct dirent *entry;
krb5_error_code ret;
heim_error_code ret;
const char **di;
char *dirname = NULL;
DIR *d;
@@ -326,8 +331,8 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
char *plugin_prefix;
size_t plugin_prefix_len;
if (asprintf(&plugin_prefix, "plugin_%s_", name) == -1)
return;
if (asprintf(&plugin_prefix, "plugin_%s_", module) == -1)
return;
plugin_prefix_len = (plugin_prefix ? strlen(plugin_prefix) : 0);
#endif
@@ -335,115 +340,119 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
modules = copy_modules();
module = heim_dict_copy_value(modules, s);
if (module == NULL) {
module = heim_dict_create(11);
if (module == NULL) {
HEIMDAL_MUTEX_unlock(&modules_mutex);
heim_release(s);
heim_release(modules);
return;
}
heim_dict_set_value(modules, s, module);
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);
dirname = resolve_origin(*di, module);
if (dirname == NULL)
continue;
d = opendir(dirname);
if (d == NULL)
continue;
rk_cloexec_dir(d);
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 krb5_dso *p;
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;
/* skip . and .. */
if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')))
continue;
ret = 0;
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;
/*
* 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;
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;
}
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);
}
{ /* 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)
ret = asprintf(&path, "%s/%s", dirname, n);
if (ret < 0 || path == NULL)
continue;
if (ret < 0 || path == NULL)
continue;
spath = heim_string_create(n);
if (spath == NULL) {
free(path);
continue;
}
spath = heim_string_create(n);
if (spath == NULL) {
free(path);
continue;
}
/* check if already cached */
p = heim_dict_copy_value(module, spath);
if (p == NULL) {
p = heim_alloc(sizeof(*p), "krb5-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(module, spath, p);
}
}
/* 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);
heim_release(spath);
free(path);
}
closedir(d);
}
free(dirname);
HEIMDAL_MUTEX_unlock(&modules_mutex);
heim_release(module);
heim_release(mod);
#ifdef _WIN32
if (plugin_prefix)
free(plugin_prefix);
free(plugin_prefix);
#endif
#endif /* HAVE_DLOPEN */
}
/**
* Unload plugins (new system)
* Unload plugins of the given @module name.
*
* Params:
*
* @module Name of module whose plusins to unload.
*/
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_unload_plugins(krb5_context context, const char *name)
void
heim_unload_plugins(heim_context context, const char *module)
{
heim_string_t sname = heim_string_create(name);
heim_string_t sname = heim_string_create(module);
heim_dict_t modules;
HEIMDAL_MUTEX_lock(&modules_mutex);
@@ -458,14 +467,16 @@ _krb5_unload_plugins(krb5_context context, const char *name)
}
struct iter_ctx {
krb5_context context;
heim_context context;
heim_pcontext pcontext;
heim_string_t n;
struct krb5_plugin_data *caller;
struct heim_plugin_data *caller;
int flags;
heim_array_t result;
krb5_error_code (KRB5_LIB_CALL *func)(krb5_context, const void *, void *, void *);
int32_t (HEIM_LIB_CALL *func)(void *, const void *, void *, void *);
void *userctx;
krb5_error_code ret;
int32_t ret;
int32_t plugin_no_handle_retval;
};
#ifdef HAVE_DLOPEN
@@ -476,33 +487,34 @@ struct iter_ctx {
* instance validation.
*/
static heim_array_t
add_dso_plugin_struct(krb5_context context,
const char *dsopath,
void *dsohandle,
const char *name)
add_dso_plugin_struct(heim_context context,
heim_pcontext pcontext,
const char *dsopath,
void *dsohandle,
const char *name)
{
krb5_error_code ret;
krb5_plugin_common_ftable_p cpm;
struct krb5_plugin *pl;
heim_error_code ret;
heim_plugin_common_ftable_p cpm;
struct heim_plugin *pl;
heim_array_t plugins;
if (dsohandle == NULL)
return NULL;
return NULL;
/* suppress error here because we may be looking for a different plugin type */
cpm = (krb5_plugin_common_ftable_p)dlsym(dsohandle, name);
cpm = (heim_plugin_common_ftable_p)dlsym(dsohandle, name);
if (cpm == NULL)
return NULL;
return NULL;
krb5_warnx(context, "plugin %s uses deprecated loading mechanism", dsopath);
heim_warnx(context, "plugin %s uses deprecated loading mechanism", dsopath);
pl = heim_alloc(sizeof(*pl), "krb5-plugin", plugin_free);
pl = heim_alloc(sizeof(*pl), "heim-plugin", plugin_free);
ret = cpm->init(context, &pl->ctx);
ret = cpm->init(pcontext, &pl->ctx);
if (ret) {
krb5_warn(context, ret, "plugin %s failed to initialize", dsopath);
heim_release(pl);
return NULL;
heim_warn(context, ret, "plugin %s failed to initialize", dsopath);
heim_release(pl);
return NULL;
}
pl->ftable = cpm;
@@ -514,38 +526,38 @@ add_dso_plugin_struct(krb5_context context,
return plugins;
}
static krb5_boolean
validate_plugin_deps(krb5_context context,
struct krb5_plugin_data *caller,
const char *dsopath,
krb5_get_instance_func_t get_instance)
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) {
krb5_warnx(context, "plugin %s omitted instance callback",
dsopath);
return FALSE;
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;
uintptr_t heim_instance, plugin_instance;
heim_instance = caller->get_instance(caller->deps[i]);
plugin_instance = get_instance(caller->deps[i]);
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 == 0 || plugin_instance == 0)
continue;
if (heim_instance != plugin_instance) {
krb5_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;
}
_krb5_debug(context, 10, "Validated plugin library dependency %s for %s",
caller->deps[i], dsopath);
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;
@@ -553,62 +565,63 @@ validate_plugin_deps(krb5_context context,
/*
* New interface from Heimdal 8 where a DSO can export a load function
* that can return both a libkrb5 instance identifier along with an array
* of plugins.
* that can return both a Heimdal instance identifier along with an
* array of plugins.
*/
static heim_array_t
add_dso_plugins_load_fn(krb5_context context,
struct krb5_plugin_data *caller,
const char *dsopath,
void *dsohandle)
add_dso_plugins_load_fn(heim_context context,
heim_pcontext pcontext,
struct heim_plugin_data *caller,
const char *dsopath,
void *dsohandle)
{
krb5_error_code ret;
heim_error_code ret;
heim_array_t plugins;
krb5_plugin_load_t load_fn;
heim_plugin_load_t load_fn;
char *sym;
size_t i;
krb5_get_instance_func_t get_instance;
heim_get_instance_func_t get_instance;
size_t n_ftables;
krb5_plugin_common_ftable_cp *ftables;
heim_plugin_common_ftable_cp *ftables;
if (asprintf(&sym, "%s_plugin_load", caller->name) == -1)
return NULL;
return NULL;
/* suppress error here because we may be looking for a different plugin type */
load_fn = (krb5_plugin_load_t)dlsym(dsohandle, sym);
load_fn = (heim_plugin_load_t)dlsym(dsohandle, sym);
free(sym);
if (load_fn == NULL)
return NULL;
return NULL;
ret = load_fn(context, &get_instance, &n_ftables, &ftables);
ret = load_fn(pcontext, &get_instance, &n_ftables, &ftables);
if (ret) {
krb5_warn(context, ret, "plugin %s failed to load", dsopath);
heim_warn(context, ret, "plugin %s failed to load", dsopath);
/* fallback to loading structure directly */
return add_dso_plugin_struct(context, dsopath,
dsohandle, caller->name);
/* 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;
return NULL;
plugins = heim_array_create();
for (i = 0; i < n_ftables; i++) {
krb5_plugin_common_ftable_cp cpm = ftables[i];
struct krb5_plugin *pl;
heim_plugin_common_ftable_cp cpm = ftables[i];
struct heim_plugin *pl;
pl = heim_alloc(sizeof(*pl), "krb5-plugin", plugin_free);
pl = heim_alloc(sizeof(*pl), "heim-plugin", plugin_free);
ret = cpm->init(context, &pl->ctx);
if (ret) {
krb5_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);
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;
@@ -619,56 +632,57 @@ static void
reduce_by_version(heim_object_t value, void *ctx, int *stop)
{
struct iter_ctx *s = ctx;
struct krb5_plugin *pl = value;
struct heim_plugin *pl = value;
if (pl->ftable && pl->ftable->version >= s->caller->min_version)
heim_array_append_value(s->result, pl);
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 krb5_dso *p = value;
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);
const char *path = heim_string_get_utf8(p->path);
plugins = add_dso_plugins_load_fn(s->context,
s->caller,
path,
p->dsohandle);
if (plugins) {
heim_dict_set_value(p->plugins_by_name, s->n, plugins);
_krb5_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);
}
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);
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 krb5_plugin *pl = value;
struct heim_plugin *pl = value;
struct iter_ctx *s = ctx;
if (s->ret != KRB5_PLUGIN_NO_HANDLE)
return;
if (s->ret != s->plugin_no_handle_retval)
return;
s->ret = s->func(s->context, pl->ftable, pl->ctx, s->userctx);
if (s->ret != KRB5_PLUGIN_NO_HANDLE
&& !(s->flags & KRB5_PLUGIN_INVOKE_ALL))
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;
}
@@ -684,42 +698,47 @@ eval_results(heim_object_t value, void *ctx, int *stop)
* outputs, if any, in @userctx.
*
* All loaded and registered plugins are invoked via @func until @func
* returns something other than KRB5_PLUGIN_NO_HANDLE. Plugins that
* have nothing to do for the given arguments should return
* KRB5_PLUGIN_NO_HANDLE.
* 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 krb5_context
* @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.
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_plugin_run_f(krb5_context context,
struct krb5_plugin_data *caller,
int flags,
void *userctx,
krb5_error_code (KRB5_LIB_CALL *func)(krb5_context, const void *, void *, void *))
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.ret = KRB5_PLUGIN_NO_HANDLE;
s.plugin_no_handle_retval = nohandle;
s.ret = nohandle;
HEIMDAL_MUTEX_lock(&modules_mutex);
@@ -729,7 +748,7 @@ _krb5_plugin_run_f(krb5_context context,
/* Add loaded plugins to s.result array */
if (dict)
heim_dict_iterate_f(dict, &s, search_modules);
heim_dict_iterate_f(dict, &s, search_modules);
/* We don't need to hold modules_mutex during plugin invocation */
HEIMDAL_MUTEX_unlock(&modules_mutex);
@@ -745,27 +764,3 @@ _krb5_plugin_run_f(krb5_context context,
return s.ret;
}
/**
* Return a cookie identifying this instance of a library.
*
* Inputs:
*
* @context A krb5_context
* @module Our library name or a library we depend on
*
* Outputs: The instance cookie
*
* @ingroup krb5_support
*/
KRB5_LIB_FUNCTION uintptr_t KRB5_LIB_CALL
krb5_get_instance(const char *libname)
{
static const char *instance = "libkrb5";
if (strcmp(libname, "krb5") == 0)
return (uintptr_t)instance;
return 0;
}