diff options
Diffstat (limited to 'lib/krb5/plugin.c')
| -rw-r--r-- | lib/krb5/plugin.c | 574 |
1 files changed, 88 insertions, 486 deletions
diff --git a/lib/krb5/plugin.c b/lib/krb5/plugin.c index f4bf99953ebb..b4035d39d58a 100644 --- a/lib/krb5/plugin.c +++ b/lib/krb5/plugin.c @@ -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: @@ -32,35 +34,30 @@ */ #include "krb5_locl.h" +#include "common_plugin.h" -#ifdef HAVE_DLFCN_H -#include <dlfcn.h> -#endif -#include <dirent.h> - -struct krb5_plugin { - void *symbol; - struct krb5_plugin *next; -}; - -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 HEIMDAL_MUTEX plugin_mutex = HEIMDAL_MUTEX_INITIALIZER; -static struct plugin *registered = NULL; +/* + * 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"). + */ /** * Register a plugin symbol name of specific type. @@ -78,187 +75,22 @@ 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) -{ - 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; - - *list = NULL; - - 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; - } - } - - HEIMDAL_MUTEX_unlock(&plugin_mutex); - if (ret) - return ret; - - if (*list == NULL) { - krb5_set_error_message(context, ENOENT, "Did not find a plugin for %s", name); - return ENOENT; - } - - return 0; -} - -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; -}; - -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) + const void *symbol) { -#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"); - - 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; + /* + * 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; } - return path; -#endif /* !HAVE_DLADDR */ + return heim_plugin_register(context->hcontext, (heim_pcontext)context, + "krb5", name, symbol); } - /** * Load plugins (new system) for the given module @name (typically * "krb5") from the given directory @paths. @@ -272,132 +104,7 @@ resolve_origin(const char *di) KRB5_LIB_FUNCTION void KRB5_LIB_CALL _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; - struct dirent *entry; - krb5_error_code ret; - const char **di; - char *dirname = NULL; - DIR *d; -#ifdef _WIN32 - const char * plugin_prefix; - size_t plugin_prefix_len; - - if (asprintf(&plugin_prefix, "plugin_%s_", name) == -1) - return; - plugin_prefix_len = (plugin_prefix ? strlen(plugin_prefix) : 0); -#endif - - HEIMDAL_MUTEX_lock(&plugin_mutex); - - if (modules == NULL) { - modules = heim_dict_create(11); - if (modules == NULL) { - HEIMDAL_MUTEX_unlock(&plugin_mutex); - return; - } - } - - module = heim_dict_copy_value(modules, s); - if (module == NULL) { - module = heim_dict_create(11); - if (module == NULL) { - HEIMDAL_MUTEX_unlock(&plugin_mutex); - heim_release(s); - return; - } - heim_dict_set_value(modules, s, module); - } - heim_release(s); - - for (di = paths; *di != NULL; di++) { - free(dirname); - dirname = resolve_origin(*di); - 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 plugin2 *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(module, spath); - if (p == NULL) { - p = heim_alloc(sizeof(*p), "krb5-plugin", plug_dealloc); - if (p) - p->dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY); - - if (p && p->dsohandle) { - p->path = heim_retain(spath); - p->names = heim_dict_create(11); - heim_dict_set_value(module, spath, p); - } - } - heim_release(p); - heim_release(spath); - free(path); - } - closedir(d); - } - free(dirname); - HEIMDAL_MUTEX_unlock(&plugin_mutex); - heim_release(module); -#ifdef _WIN32 - if (plugin_prefix) - free(plugin_prefix); -#endif -#endif /* HAVE_DLOPEN */ + heim_load_plugins(context->hcontext, name, paths); } /** @@ -406,101 +113,14 @@ _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_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); - } -} - -struct iter_ctx { - krb5_context context; - heim_string_t n; - const char *name; - int min_version; - int flags; - heim_array_t result; - krb5_error_code (KRB5_LIB_CALL *func)(krb5_context, const void *, void *, void *); - void *userctx; - krb5_error_code ret; -}; - -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; - - if (pl == NULL) { - if (p->dsohandle == NULL) - return; - - 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; - } - heim_dict_set_value(p->names, s->n, pl); - } else { - cpm = pl->dataptr; - } - - if (cpm && cpm->version >= s->min_version) - heim_array_append_value(s->result, pl); - heim_release(pl); -} - -static void -eval_results(heim_object_t value, void *ctx, int *stop) -{ - struct plug *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); - if (s->ret != KRB5_PLUGIN_NO_HANDLE - && !(s->flags & KRB5_PLUGIN_INVOKE_ALL)) - *stop = 1; + heim_unload_plugins(context->hcontext, name); } /** * 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 @@ -527,80 +147,62 @@ 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, + const struct heim_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; - void *plug_ctx; - struct common_plugin_method *cpm; - struct iter_ctx s; - struct krb5_plugin *registered_plugins = NULL; - struct krb5_plugin *p; - - /* Get registered plugins */ - (void) _krb5_plugin_find(context, PLUGIN_TYPE_DATA, name, ®istered_plugins); - - HEIMDAL_MUTEX_lock(&plugin_mutex); - - s.context = context; - s.name = name; - s.n = heim_string_create(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 */ - dict = heim_dict_copy_value(modules, m); - heim_release(m); - - /* Add loaded plugins to s.result array */ - if (dict) - heim_dict_iterate_f(dict, &s, search_modules); + int32_t (HEIM_LIB_CALL *func2)(void *, const void *, void *, void *) = (void *)func; + return heim_plugin_run_f(context->hcontext, (heim_pcontext)context, caller, + flags, KRB5_PLUGIN_NO_HANDLE, userctx, func2); +} - /* We don't need to hold plugin_mutex during plugin invocation */ - HEIMDAL_MUTEX_unlock(&plugin_mutex); +/** + * 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 + */ - /* 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); +#ifdef WIN32 +static uintptr_t +djb2(uintptr_t hash, unsigned char *str) +{ + int c; - /* Invoke loaded plugins (new system) */ - if (s.ret == KRB5_PLUGIN_NO_HANDLE) - heim_array_iterate_f(s.result, &s, eval_results); + while (c = *str++) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - heim_release(s.result); - heim_release(s.n); - heim_release(dict); + return hash; +} +#endif - return s.ret; +KRB5_LIB_FUNCTION uintptr_t KRB5_LIB_CALL +krb5_get_instance(const char *libname) +{ +#ifdef WIN32 + char *version; + char *name; + uintptr_t instance; + + if (win32_getLibraryVersion("heimdal", &name, &version)) + return 0; + instance = djb2(5381, name); + instance = djb2(instance, version); + free(name); + free(version); + return instance; +#else + static const char *instance = "libkrb5"; + + if (strcmp(libname, "krb5") == 0) + return (uintptr_t)instance; + return 0; +#endif } |
