krb5, kadm5: refactor plugin API

Refactor plugin framework to use a single list of loaded plugins; add a new
plugin API where DSOs export a load function that can declare dependencies and
export multiple plugins; refactor kadm5 hook API to use krb5 plugin framework.

More information in krb5-plugin(7).
This commit is contained in:
Luke Howard
2019-01-01 21:55:36 +11:00
committed by Nico Williams
parent e9b3b2326d
commit 803efebca5
37 changed files with 1293 additions and 639 deletions

View File

@@ -80,6 +80,17 @@ plcallback(krb5_context context,
return locate->an2ln(plugctx, context, plctx->rule, plctx->aname, set_res, plctx);
}
static const char *an2ln_plugin_deps[] = { "krb5", NULL };
static struct krb5_plugin_data
an2ln_plugin_data = {
"krb5",
KRB5_PLUGIN_AN2LN,
KRB5_PLUGIN_AN2LN_VERSION_0,
an2ln_plugin_deps,
krb5_get_instance
};
static krb5_error_code
an2ln_plugin(krb5_context context, const char *rule, krb5_const_principal aname,
size_t lnsize, char *lname)
@@ -96,8 +107,8 @@ an2ln_plugin(krb5_context context, const char *rule, krb5_const_principal aname,
* really be no more than one plugin that can handle any given kind
* rule, so the effect should be deterministic anyways.
*/
ret = _krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_AN2LN,
KRB5_PLUGIN_AN2LN_VERSION_0, 0, &ctx, plcallback);
ret = _krb5_plugin_run_f(context, &an2ln_plugin_data,
0, &ctx, plcallback);
if (ret != 0) {
heim_release(ctx.luser);
return ret;

View File

@@ -1589,3 +1589,4 @@ krb5_set_home_dir_access(krb5_context context, krb5_boolean allow)
return old;
}

View File

@@ -14,12 +14,22 @@ db_plugins_plcallback(krb5_context context, const void *plug, void *plugctx,
return 0;
}
static const char *db_plugin_deps[] = { "krb5", NULL };
static struct krb5_plugin_data
db_plugin_data = {
"krb5",
KRB5_PLUGIN_DB,
KRB5_PLUGIN_DB_VERSION_0,
db_plugin_deps,
krb5_get_instance
};
static void
db_plugins_init(void *arg)
{
krb5_context context = arg;
(void)_krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_DB,
KRB5_PLUGIN_DB_VERSION_0, 0, NULL,
(void)_krb5_plugin_run_f(context, &db_plugin_data, 0, NULL,
db_plugins_plcallback);
}

View File

@@ -57,11 +57,26 @@ associated header file, such as, for example,
.Va krb5plugin_kuserok_ftable
and a pointer to which is either registered via
.Xr krb5_plugin_register 3
or found in a shared object via a symbol lookup for the symbol name
defined in the associated header file (e.g., "kuserok" for the
or via a plugin load function exported by a shared object. Plugin load
functions should be named by concatenating the name defined in the associated
header file with the string "plugin_load" (e.g. "kuserok_plugin_load" for the
plugin for
.Xr krb5_kuserok 3
).
The plugin load function has the following signature:
.Bd -literal -offset indent
krb5_error_code
kuserok_plugin_load(krb5_context context,
krb5_get_instance_func_t *get_instance,
size_t *n_plugins,
const common_plugin_ftable *const **plugins);
.Ed
.Pp
The plugin should set the get_instance output parameter to the a function
that will return the instances of its library dependencies. The output
parameters n_plugins and plugins specify the number of plugins, and an array of
pointers to function tables, respectively.
.Ed
.Pp
The plugin structs for all plugin types always begin with the same three
common fields:
@@ -200,6 +215,31 @@ krb5plugin_an2ln_ftable an2ln = {
nouser_plug_fini,
nouser_plug_an2ln,
};
static const krb5plugin_an2ln_ftable *const plugins[] = {
&an2ln
};
static uintptr_t
an2ln_get_instance(const char *libname)
{
if (strcmp(libname, "krb5") == 0)
return krb5_get_instance(libname);
return 0;
}
krb5_error_code
an2ln_plugin_load(krb5_context context,
krb5_get_instance_func_t *get_instance,
size_t *num_plugins,
const krb5plugin_an2ln_ftable * const **pplugins)
{
*get_instance = an2ln_get_instance;
*num_plugins = sizeof(plugins) / sizeof(plugins[0]);
*pplugins = plugins;
return 0;
}
.Ed
.Pp
An example kuserok plugin that rejects all requests follows. (Note that
@@ -232,12 +272,38 @@ reject_plug_kuserok(void *plug_ctx, krb5_context context, const char *rule,
return 0;
}
krb5plugin_kuserok_ftable kuserok = {
static krb5plugin_kuserok_ftable kuserok = {
KRB5_PLUGIN_KUSEROK_VERSION_0,
reject_plug_init,
reject_plug_fini,
reject_plug_kuserok,
};
static const krb5plugin_kuserok_ftable *const plugins[] = {
&kuserok
};
static uintptr_t
kuserok_get_instance(const char *libname)
{
if (strcmp(libname, "krb5") == 0)
return krb5_get_instance(libname);
return 0;
}
krb5_error_code
kuserok_plugin_load(krb5_context context,
krb5_get_instance_func_t *get_instance,
size_t *num_plugins,
const krb5plugin_kuserok_ftable * const **pplugins)
{
*krb5_instance = kuserok_get_instance;
*num_plugins = sizeof(plugins) / sizeof(plugins[0]);
*pplugins = plugins;
return 0;
}
.Ed
.Sh SEE ALSO
.Xr krb5_plugin_register 3

View File

@@ -873,14 +873,16 @@ typedef krb5_error_code
(KRB5_CALLCONV * krb5_sendto_ctx_func)(krb5_context, krb5_sendto_ctx, void *,
const krb5_data *, int *);
struct krb5_plugin;
enum krb5_plugin_type {
PLUGIN_TYPE_DATA = 1,
PLUGIN_TYPE_FUNC
PLUGIN_TYPE_FUNC /* no longer supported */
};
#define KRB5_PLUGIN_INVOKE_ALL 1
typedef uintptr_t
(KRB5_CALLCONV *krb5_get_instance_func_t)(const char *);
struct credentials; /* this is to keep the compiler happy */
struct getargs;
struct sockaddr;

View File

@@ -134,6 +134,7 @@ struct ContentInfo;
struct AlgorithmIdentifier;
typedef struct krb5_pk_init_ctx_data *krb5_pk_init_ctx;
struct krb5_dh_moduli;
struct krb5_plugin_data;
/* v4 glue */
struct _krb5_krb_auth_data;
@@ -393,4 +394,12 @@ struct krb5_pk_init_ctx_data {
# define ISPATHSEP(x) (x == '/')
#endif
struct krb5_plugin_data {
const char *module;
const char *name;
int min_version;
const char **deps;
krb5_get_instance_func_t get_instance;
};
#endif /* __KRB5_LOCL_H__ */

View File

@@ -674,6 +674,17 @@ plcallback(krb5_context context,
return KRB5_PLUGIN_NO_HANDLE;
}
static const char *locate_plugin_deps[] = { "krb5", NULL };
static struct krb5_plugin_data
locate_plugin_data = {
"krb5",
KRB5_PLUGIN_LOCATE,
KRB5_PLUGIN_LOCATE_VERSION_0,
locate_plugin_deps,
krb5_get_instance
};
static void
plugin_get_hosts(krb5_context context,
struct krb5_krbhst_data *kd,
@@ -684,8 +695,7 @@ plugin_get_hosts(krb5_context context,
if (_krb5_homedir_access(context))
ctx.flags |= KRB5_PLF_ALLOW_HOMEDIR;
_krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_LOCATE,
KRB5_PLUGIN_LOCATE_VERSION_0,
_krb5_plugin_run_f(context, &locate_plugin_data,
0, &ctx, plcallback);
}

View File

@@ -455,6 +455,17 @@ krb5_kuserok(krb5_context context,
}
static const char *kuserok_plugin_deps[] = { "krb5", NULL };
static struct krb5_plugin_data
kuserok_plugin_data = {
"krb5",
KRB5_PLUGIN_KUSEROK,
KRB5_PLUGIN_KUSEROK_VERSION_0,
kuserok_plugin_deps,
krb5_get_instance
};
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
_krb5_kuserok(krb5_context context,
krb5_principal principal,
@@ -515,9 +526,8 @@ _krb5_kuserok(krb5_context context,
for (n = 0; rules[n]; n++) {
ctx.rule = rules[n];
ret = _krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_KUSEROK,
KRB5_PLUGIN_KUSEROK_VERSION_0, 0,
&ctx, plcallback);
ret = _krb5_plugin_run_f(context, &kuserok_plugin_data,
0, &ctx, plcallback);
if (ret != KRB5_PLUGIN_NO_HANDLE)
goto out;
}

View File

@@ -366,6 +366,7 @@ EXPORTS
krb5_get_init_creds_opt_set_tkt_life
krb5_get_init_creds_opt_set_win2k
krb5_get_init_creds_password
krb5_get_instance
krb5_get_kdc_cred
krb5_get_kdc_sec_offset
krb5_get_krb524hst
@@ -756,6 +757,10 @@ EXPORTS
_krb5_build_authenticator
_krb5_kt_client_default_name
; Shared with libkadm5
_krb5_load_plugins
_krb5_unload_plugins
; Shared with libkdc
_krb5_AES_SHA1_string_to_default_iterator
_krb5_AES_SHA2_string_to_default_iterator
@@ -806,8 +811,6 @@ EXPORTS
; Recent additions
krb5_cc_type_dcc;
krb5_dcc_ops;
_krb5_plugin_find;
_krb5_plugin_free;
_krb5_expand_path_tokensv;
_krb5_find_capath;
_krb5_free_capath;

View File

@@ -58,13 +58,24 @@ cc_plugin_register_to_context(krb5_context context, const void *plug, void *plug
return KRB5_PLUGIN_NO_HANDLE;
}
static const char *ccache_plugin_deps[] = { "krb5", NULL };
static struct krb5_plugin_data
ccache_plugin_data = {
"krb5",
KRB5_PLUGIN_CCACHE,
0,
ccache_plugin_deps,
krb5_get_instance
};
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_load_ccache_plugins(krb5_context context)
{
krb5_error_code userctx = 0;
(void)_krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_CCACHE,
0, 0, &userctx, cc_plugin_register_to_context);
(void)_krb5_plugin_run_f(context, &ccache_plugin_data, 0,
&userctx, cc_plugin_register_to_context);
return userctx;
}

View File

@@ -3,6 +3,8 @@
* (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:
@@ -38,29 +40,159 @@
#endif
#include <dirent.h>
/*
* 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)
*
* 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").
*/
/* 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 krb5_dso {
heim_string_t path;
heim_dict_t plugins_by_name;
void *dsohandle;
};
static void
dso_dealloc(void *ptr)
{
struct krb5_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 krb5_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;
if (module == NULL)
return NULL;
dso = heim_dict_copy_value(module, dso_name);
if (dso == NULL) {
dso = heim_alloc(sizeof(*dso), "krb5-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;
}
/*
* All plugin function tables extend the following structure.
*/
typedef struct common_plugin_ftable_desc {
int version;
krb5_error_code (*init)(krb5_context, void **);
void (*fini)(void *);
} common_plugin_ftable;
struct krb5_plugin {
common_plugin_ftable *ftable;
void *ctx;
};
static void
plugin_free(void *ptr)
{
struct krb5_plugin *pl = ptr;
if (pl->ftable && pl->ftable->fini)
pl->ftable->fini(pl->ctx);
}
struct krb5_plugin_register_ctx {
void *symbol;
struct krb5_plugin *next;
int is_dup;
};
struct plugin {
enum { DSO, SYMBOL } type;
union {
struct {
char *path;
void *dsohandle;
} dso;
struct {
enum krb5_plugin_type type;
char *name;
char *symbol;
} symbol;
} u;
struct plugin *next;
};
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;
static HEIMDAL_MUTEX plugin_mutex = HEIMDAL_MUTEX_INITIALIZER;
static struct plugin *registered = NULL;
if (pl->ftable == pc->symbol) {
pc->is_dup = 1;
*stop = 1;
}
}
/**
* Register a plugin symbol name of specific type.
@@ -80,147 +212,67 @@ krb5_plugin_register(krb5_context context,
const char *name,
void *symbol)
{
struct plugin *e;
HEIMDAL_MUTEX_lock(&plugin_mutex);
/* check for duplicates */
for (e = registered; e != NULL; e = e->next) {
if (e->type == SYMBOL &&
strcmp(e->u.symbol.name, name) == 0 &&
e->u.symbol.type == type && e->u.symbol.symbol == symbol) {
HEIMDAL_MUTEX_unlock(&plugin_mutex);
return 0;
}
}
e = calloc(1, sizeof(*e));
if (e == NULL) {
HEIMDAL_MUTEX_unlock(&plugin_mutex);
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM;
}
e->type = SYMBOL;
e->u.symbol.type = type;
e->u.symbol.name = strdup(name);
if (e->u.symbol.name == NULL) {
HEIMDAL_MUTEX_unlock(&plugin_mutex);
free(e);
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM;
}
e->u.symbol.symbol = symbol;
e->next = registered;
registered = e;
HEIMDAL_MUTEX_unlock(&plugin_mutex);
return 0;
}
static krb5_error_code
add_symbol(krb5_context context, struct krb5_plugin **list, void *symbol)
{
struct krb5_plugin *e;
e = calloc(1, sizeof(*e));
if (e == NULL) {
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM;
}
e->symbol = symbol;
e->next = *list;
*list = e;
return 0;
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_plugin_find(krb5_context context,
enum krb5_plugin_type type,
const char *name,
struct krb5_plugin **list)
{
struct plugin *e;
krb5_error_code ret;
heim_array_t plugins;
heim_string_t hname;
struct krb5_dso *dso;
struct krb5_plugin_register_ctx ctx;
*list = NULL;
ctx.symbol = symbol;
ctx.is_dup = 0;
HEIMDAL_MUTEX_lock(&plugin_mutex);
for (ret = 0, e = registered; e != NULL; e = e->next) {
switch(e->type) {
case DSO: {
void *sym;
if (e->u.dso.dsohandle == NULL)
continue;
sym = dlsym(e->u.dso.dsohandle, name);
if (sym)
ret = add_symbol(context, list, sym);
break;
}
case SYMBOL:
if (strcmp(e->u.symbol.name, name) == 0 && e->u.symbol.type == type)
ret = add_symbol(context, list, e->u.symbol.symbol);
break;
}
if (ret) {
_krb5_plugin_free(*list);
*list = NULL;
}
/*
* 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_unlock(&plugin_mutex);
if (ret)
return ret;
HEIMDAL_MUTEX_lock(&modules_mutex);
if (*list == NULL) {
krb5_set_error_message(context, ENOENT, "Did not find a plugin for %s", name);
return ENOENT;
dso = copy_internal_dso("krb5");
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);
}
return 0;
if (!ctx.is_dup) {
/* Note: refactored plugin API only supports common plugin layout */
struct krb5_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 */
HEIMDAL_MUTEX_unlock(&modules_mutex);
heim_release(dso);
heim_release(hname);
heim_release(plugins);
return ret;
}
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_plugin_free(struct krb5_plugin *list)
{
struct krb5_plugin *next;
while (list) {
next = list->next;
free(list);
list = next;
}
}
/*
* module - dict of {
* ModuleName = [
* plugin = object{
* array = { ptr, ctx }
* }
* ]
* }
*/
static heim_dict_t modules;
struct plugin2 {
heim_string_t path;
void *dsohandle;
heim_dict_t names;
};
#ifdef HAVE_DLOPEN
static void
plug_dealloc(void *ptr)
{
struct plugin2 *p = ptr;
heim_release(p->path);
heim_release(p->names);
if (p->dsohandle)
dlclose(p->dsohandle);
}
static char *
resolve_origin(const char *di)
{
@@ -277,7 +329,7 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
{
#ifdef HAVE_DLOPEN
heim_string_t s = heim_string_create(name);
heim_dict_t module;
heim_dict_t module, modules;
struct dirent *entry;
krb5_error_code ret;
const char **di;
@@ -292,27 +344,23 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
plugin_prefix_len = (plugin_prefix ? strlen(plugin_prefix) : 0);
#endif
HEIMDAL_MUTEX_lock(&plugin_mutex);
HEIMDAL_MUTEX_lock(&modules_mutex);
if (modules == NULL) {
modules = heim_dict_create(11);
if (modules == NULL) {
HEIMDAL_MUTEX_unlock(&plugin_mutex);
return;
}
}
modules = copy_modules();
module = heim_dict_copy_value(modules, s);
if (module == NULL) {
module = heim_dict_create(11);
if (module == NULL) {
HEIMDAL_MUTEX_unlock(&plugin_mutex);
HEIMDAL_MUTEX_unlock(&modules_mutex);
heim_release(s);
heim_release(modules);
return;
}
heim_dict_set_value(modules, s, module);
}
heim_release(s);
heim_release(modules);
for (di = paths; *di != NULL; di++) {
free(dirname);
@@ -328,7 +376,7 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
char *n = entry->d_name;
char *path = NULL;
heim_string_t spath;
struct plugin2 *p;
struct krb5_dso *p;
/* skip . and .. */
if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')))
@@ -377,13 +425,12 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
/* check if already cached */
p = heim_dict_copy_value(module, spath);
if (p == NULL) {
p = heim_alloc(sizeof(*p), "krb5-plugin", plug_dealloc);
p = heim_alloc(sizeof(*p), "krb5-dso", dso_dealloc);
if (p)
p->dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY);
p->dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY|RTLD_GROUP);
if (p && p->dsohandle) {
p->path = heim_retain(spath);
p->names = heim_dict_create(11);
p->plugins_by_name = heim_dict_create(11);
heim_dict_set_value(module, spath, p);
}
}
@@ -394,7 +441,7 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
closedir(d);
}
free(dirname);
HEIMDAL_MUTEX_unlock(&plugin_mutex);
HEIMDAL_MUTEX_unlock(&modules_mutex);
heim_release(module);
#ifdef _WIN32
if (plugin_prefix)
@@ -409,42 +456,24 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_unload_plugins(krb5_context context, const char *name)
{
HEIMDAL_MUTEX_lock(&plugin_mutex);
heim_string_t sname = heim_string_create(name);
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);
modules = NULL;
HEIMDAL_MUTEX_unlock(&plugin_mutex);
}
/*
*
*/
struct common_plugin_method {
int version;
krb5_error_code (*init)(krb5_context, void **);
void (*fini)(void *);
};
struct plug {
void *dataptr;
void *ctx;
};
static void
plug_free(void *ptr)
{
struct plug *pl = ptr;
if (pl->dataptr) {
struct common_plugin_method *cpm = pl->dataptr;
cpm->fini(pl->ctx);
}
heim_release(sname);
}
struct iter_ctx {
krb5_context context;
heim_string_t n;
const char *name;
int min_version;
struct krb5_plugin_data *caller;
int flags;
heim_array_t result;
krb5_error_code (KRB5_LIB_CALL *func)(krb5_context, const void *, void *, void *);
@@ -452,48 +481,213 @@ struct iter_ctx {
krb5_error_code ret;
};
#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(krb5_context context,
const char *dsopath,
void *dsohandle,
const char *name)
{
krb5_error_code ret;
common_plugin_ftable *cpm;
struct krb5_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 = dlsym(dsohandle, name);
if (cpm == NULL)
return NULL;
krb5_warnx(context, "plugin %s uses deprecated loading mechanism", dsopath);
pl = heim_alloc(sizeof(*pl), "krb5-plugin", plugin_free);
ret = cpm->init(context, &pl->ctx);
if (ret) {
krb5_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;
}
typedef krb5_error_code
(KRB5_CALLCONV *krb5_plugin_load_t)(krb5_context context,
krb5_get_instance_func_t *func,
size_t *n_ftables,
const common_plugin_ftable *const **ftables);
static krb5_boolean
validate_plugin_deps(krb5_context context,
struct krb5_plugin_data *caller,
const char *dsopath,
krb5_get_instance_func_t get_instance)
{
size_t i;
if (get_instance == NULL) {
krb5_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) {
krb5_warnx(context, "plugin %s library %s linked against different "
"version 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);
}
return TRUE;
}
/*
* 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.
*/
static heim_array_t
add_dso_plugins_load_fn(krb5_context context,
struct krb5_plugin_data *caller,
const char *dsopath,
void *dsohandle)
{
krb5_error_code ret;
heim_array_t plugins;
krb5_plugin_load_t load_fn;
char *sym;
size_t i;
krb5_get_instance_func_t get_instance;
size_t n_ftables;
const common_plugin_ftable *const *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 = dlsym(dsohandle, sym);
free(sym);
if (load_fn == NULL)
return NULL;
ret = load_fn(context, &get_instance, &n_ftables, &ftables);
if (ret) {
krb5_warn(context, ret, "plugin %s failed to load", dsopath);
return NULL;
}
if (!validate_plugin_deps(context, caller, dsopath, get_instance))
return NULL;
plugins = heim_array_create();
for (i = 0; i < n_ftables; i++) {
const common_plugin_ftable *const cpm = ftables[i];
struct krb5_plugin *pl;
pl = heim_alloc(sizeof(*pl), "krb5-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);
}
return plugins;
}
#endif /* HAVE_DLOPEN */
static void
reduce_by_version(heim_object_t value, void *ctx, int *stop)
{
struct iter_ctx *s = ctx;
struct krb5_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 plugin2 *p = value;
struct plug *pl = heim_dict_copy_value(p->names, s->n);
struct common_plugin_method *cpm;
struct krb5_dso *p = value;
heim_array_t plugins = heim_dict_copy_value(p->plugins_by_name, s->n);
if (pl == NULL) {
if (p->dsohandle == NULL)
return;
#ifdef HAVE_DLOPEN
if (plugins == NULL && p->dsohandle) {
const char *path = heim_string_get_utf8(p->path);
pl = heim_alloc(sizeof(*pl), "struct-plug", plug_free);
cpm = pl->dataptr = dlsym(p->dsohandle, s->name);
if (cpm) {
int ret;
ret = cpm->init(s->context, &pl->ctx);
if (ret)
cpm = pl->dataptr = NULL;
plugins = add_dso_plugins_load_fn(s->context,
s->caller,
path,
p->dsohandle);
if (plugins == NULL) {
/* fallback to loading structure directly */
plugins = add_dso_plugin_struct(s->context, path,
p->dsohandle, s->caller->name);
}
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);
}
heim_dict_set_value(p->names, s->n, pl);
} else {
cpm = pl->dataptr;
}
#endif /* HAVE_DLOPEN */
if (cpm && cpm->version >= s->min_version)
heim_array_append_value(s->result, pl);
heim_release(pl);
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 plug *pl = value;
struct krb5_plugin *pl = value;
struct iter_ctx *s = ctx;
if (s->ret != KRB5_PLUGIN_NO_HANDLE)
return;
s->ret = s->func(s->context, pl->dataptr, pl->ctx, s->userctx);
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))
*stop = 1;
@@ -503,7 +697,7 @@ eval_results(heim_object_t value, void *ctx, int *stop)
* 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 struct common_plugin_method),
* 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
@@ -530,83 +724,69 @@ eval_results(heim_object_t value, void *ctx, int *stop)
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_plugin_run_f(krb5_context context,
const char *module,
const char *name,
int min_version,
struct krb5_plugin_data *caller,
int flags,
void *userctx,
krb5_error_code (KRB5_LIB_CALL *func)(krb5_context, const void *, void *, void *))
{
heim_string_t m = heim_string_create(module);
heim_dict_t dict = NULL;
void *plug_ctx;
struct common_plugin_method *cpm;
heim_string_t m = heim_string_create(caller->module);
heim_dict_t modules, dict = NULL;
struct iter_ctx s;
struct krb5_plugin *registered_plugins = NULL;
struct krb5_plugin *p;
/* Get registered plugins */
(void) _krb5_plugin_find(context, SYMBOL, name, &registered_plugins);
HEIMDAL_MUTEX_lock(&plugin_mutex);
s.context = context;
s.name = name;
s.n = heim_string_create(name);
s.caller = caller;
s.n = heim_string_create(caller->name);
s.flags = flags;
s.min_version = min_version;
s.result = heim_array_create();
s.func = func;
s.userctx = userctx;
s.ret = KRB5_PLUGIN_NO_HANDLE;
/* Get loaded plugins */
if (modules)
dict = heim_dict_copy_value(modules, m);
HEIMDAL_MUTEX_lock(&modules_mutex);
heim_release(m);
/* 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 plugin_mutex during plugin invocation */
HEIMDAL_MUTEX_unlock(&plugin_mutex);
/* We don't need to hold modules_mutex during plugin invocation */
HEIMDAL_MUTEX_unlock(&modules_mutex);
/* Invoke registered plugins (old system) */
for (p = registered_plugins; p; p = p->next) {
/*
* XXX This is the wrong way to handle registered plugins, as we
* call init/fini on each invocation! We do this because we
* have nowhere in the struct plugin registered list to store
* the context allocated by the plugin's init function. (But at
* least we do call init/fini!)
*
* What we should do is adapt the old plugin system to the new
* one and change how we register plugins so that we use the new
* struct plug to keep track of their context structures, that
* way we can init once, invoke many times, then fini.
*/
cpm = (struct common_plugin_method *)p->symbol;
s.ret = cpm->init(context, &plug_ctx);
if (s.ret)
continue;
s.ret = s.func(s.context, p->symbol, plug_ctx, s.userctx);
cpm->fini(plug_ctx);
if (s.ret != KRB5_PLUGIN_NO_HANDLE &&
!(flags & KRB5_PLUGIN_INVOKE_ALL))
break;
}
_krb5_plugin_free(registered_plugins);
/* Invoke loaded plugins (new system) */
if (s.ret == KRB5_PLUGIN_NO_HANDLE)
heim_array_iterate_f(s.result, &s, eval_results);
/* Invoke loaded plugins */
heim_array_iterate_f(s.result, &s, eval_results);
heim_release(s.result);
heim_release(s.n);
if (dict)
heim_release(dict);
heim_release(dict);
heim_release(m);
heim_release(modules);
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;
}

View File

@@ -96,6 +96,17 @@ realmcallback(krb5_context context, const void *plug, void *plugctx, void *userc
ctx->send_data, ctx->receive);
}
static const char *send_to_kdc_plugin_deps[] = { "krb5", NULL };
static struct krb5_plugin_data
send_to_kdc_plugin_data = {
"krb5",
KRB5_PLUGIN_SEND_TO_KDC,
KRB5_PLUGIN_SEND_TO_KDC_VERSION_0,
send_to_kdc_plugin_deps,
krb5_get_instance
};
static krb5_error_code
kdc_via_plugin(krb5_context context,
krb5_krbhst_info *hi,
@@ -111,8 +122,7 @@ kdc_via_plugin(krb5_context context,
userctx.send_data = send_data;
userctx.receive = receive;
return _krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_SEND_TO_KDC,
KRB5_PLUGIN_SEND_TO_KDC_VERSION_0, 0,
return _krb5_plugin_run_f(context, &send_to_kdc_plugin_data, 0,
&userctx, kdccallback);
}
@@ -131,8 +141,7 @@ realm_via_plugin(krb5_context context,
userctx.send_data = send_data;
userctx.receive = receive;
return _krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_SEND_TO_KDC,
KRB5_PLUGIN_SEND_TO_KDC_VERSION_2, 0,
return _krb5_plugin_run_f(context, &send_to_kdc_plugin_data, 0,
&userctx, realmcallback);
}

View File

@@ -66,10 +66,12 @@ resolve_lookup(void *ctx,
s.sin_port = htons(88);
s.sin_addr.s_addr = htonl(0x7f000002);
if (strcmp(realm, "NOTHERE.H5L.SE") == 0)
if (strcmp(realm, "NOTHERE.H5L.SE") == 0) {
(*add)(addctx, type, (struct sockaddr *)&s);
return 0;
}
return 0;
return KRB5_PLUGIN_NO_HANDLE;
}

View File

@@ -362,6 +362,7 @@ HEIMDAL_KRB5_2.0 {
krb5_get_init_creds_opt_set_tkt_life;
krb5_get_init_creds_opt_set_win2k;
krb5_get_init_creds_password;
krb5_get_instance;
krb5_get_kdc_cred;
krb5_get_kdc_sec_offset;
krb5_get_krb524hst;
@@ -748,6 +749,10 @@ HEIMDAL_KRB5_2.0 {
_krb5_build_authenticator;
_krb5_kt_client_default_name;
# Shared with libkadm5
_krb5_load_plugins;
_krb5_unload_plugins;
# Shared with libkdc
_krb5_AES_SHA1_string_to_default_iterator;
_krb5_AES_SHA2_string_to_default_iterator;
@@ -761,8 +766,6 @@ HEIMDAL_KRB5_2.0 {
_krb5_pk_load_id;
_krb5_pk_mk_ContentInfo;
_krb5_pk_octetstring2key;
_krb5_plugin_find;
_krb5_plugin_free;
_krb5_plugin_run_f;
_krb5_principal2principalname;
_krb5_principalname2krb5_principal;