Path: blob/main/crypto/krb5/src/util/support/plugins.c
34889 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* util/support/plugins.c - Plugin module support functions */2/*3* Copyright 2006, 2008 by the Massachusetts Institute of Technology.4* All Rights Reserved.5*6* Export of this software from the United States of America may7* require a specific license from the United States Government.8* It is the responsibility of any person or organization contemplating9* export to obtain such a license before exporting.10*11* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and12* distribute this software and its documentation for any purpose and13* without fee is hereby granted, provided that the above copyright14* notice appear in all copies and that both that copyright notice and15* this permission notice appear in supporting documentation, and that16* the name of M.I.T. not be used in advertising or publicity pertaining17* to distribution of the software without specific, written prior18* permission. Furthermore if you modify this software you must label19* your software as modified software and not distribute it in such a20* fashion that it might be confused with the original M.I.T. software.21* M.I.T. makes no representations about the suitability of22* this software for any purpose. It is provided "as is" without express23* or implied warranty.24*/2526#include "k5-platform.h"27#include "k5-plugin.h"28#if USE_DLOPEN29#include <dlfcn.h>30#endif3132#if USE_DLOPEN33#ifdef RTLD_GROUP34#define GROUP RTLD_GROUP35#else36#define GROUP 037#endif38#ifdef RTLD_NODELETE39#define NODELETE RTLD_NODELETE40#else41#define NODELETE 042#endif43#define PLUGIN_DLOPEN_FLAGS (RTLD_NOW | RTLD_LOCAL | GROUP | NODELETE)44#endif4546/*47* glibc bug 11941, fixed in release 2.25, can cause an assertion failure in48* dlclose() on process exit. Our workaround is to leak dlopen() handles49* (which doesn't typically manifest in leak detection tools because the50* handles are still reachable via a global table in libdl). Because we51* dlopen() with RTLD_NODELETE, we weren't going to unload the plugin objects52* anyway.53*/54#ifdef __GLIBC_PREREQ55#if ! __GLIBC_PREREQ(2, 25)56#define dlclose(x)57#endif58#endif5960#include <stdarg.h>61static void Tprintf (const char *fmt, ...)62{63#ifdef DEBUG64va_list va;65va_start (va, fmt);66vfprintf (stderr, fmt, va);67va_end (va);68#endif69}7071struct plugin_file_handle {72#if defined(USE_DLOPEN)73void *dlhandle;74#elif defined(_WIN32)75HMODULE module;76#else77char dummy;78#endif79};8081#if defined(USE_DLOPEN)8283static long84open_plugin_dlfcn(struct plugin_file_handle *h, const char *filename,85struct errinfo *ep)86{87const char *e;8889h->dlhandle = dlopen(filename, PLUGIN_DLOPEN_FLAGS);90if (h->dlhandle == NULL) {91e = dlerror();92if (e == NULL)93e = _("unknown failure");94Tprintf("dlopen(%s): %s\n", filename, e);95k5_set_error(ep, ENOENT, _("unable to load plugin [%s]: %s"),96filename, e);97return ENOENT;98}99return 0;100}101#define open_plugin open_plugin_dlfcn102103static long104get_sym_dlfcn(struct plugin_file_handle *h, const char *csymname,105void **sym_out, struct errinfo *ep)106{107const char *e;108109if (h->dlhandle == NULL)110return ENOENT;111*sym_out = dlsym(h->dlhandle, csymname);112if (*sym_out == NULL) {113e = dlerror();114if (e == NULL)115e = _("unknown failure");116Tprintf("dlsym(%s): %s\n", csymname, e);117k5_set_error(ep, ENOENT, "%s", e);118return ENOENT;119}120return 0;121}122#define get_sym get_sym_dlfcn123124static void125close_plugin_dlfcn(struct plugin_file_handle *h)126{127if (h->dlhandle != NULL)128dlclose(h->dlhandle);129}130#define close_plugin close_plugin_dlfcn131132#elif defined(_WIN32)133134static long135open_plugin_win32(struct plugin_file_handle *h, const char *filename,136struct errinfo *ep)137{138h->module = LoadLibrary(filename);139if (h == NULL) {140Tprintf("Unable to load dll: %s\n", filename);141k5_set_error(ep, ENOENT, _("unable to load DLL [%s]"), filename);142return ENOENT;143}144return 0;145}146#define open_plugin open_plugin_win32147148static long149get_sym_win32(struct plugin_file_handle *h, const char *csymname,150void **sym_out, struct errinfo *ep)151{152LPVOID lpMsgBuf;153DWORD dw;154155if (h->module == NULL)156return ENOENT;157*sym_out = GetProcAddress(h->module, csymname);158if (*sym_out == NULL) {159Tprintf("GetProcAddress(%s): %i\n", csymname, GetLastError());160dw = GetLastError();161if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |162FORMAT_MESSAGE_FROM_SYSTEM,163NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),164(LPTSTR)&lpMsgBuf, 0, NULL)) {165k5_set_error(ep, ENOENT, _("unable to get DLL Symbol: %s"),166(char *)lpMsgBuf);167LocalFree(lpMsgBuf);168}169return ENOENT;170}171return 0;172}173#define get_sym get_sym_win32174175static void176close_plugin_win32(struct plugin_file_handle *h)177{178if (h->module != NULL)179FreeLibrary(h->module);180}181#define close_plugin close_plugin_win32182183#else184185static long186open_plugin_dummy(struct plugin_file_handle *h, const char *filename,187struct errinfo *ep)188{189k5_set_error(ep, ENOENT, _("plugin loading unavailable"));190return ENOENT;191}192#define open_plugin open_plugin_dummy193194static long195get_sym_dummy(struct plugin_file_handle *h, const char *csymname,196void **sym_out, struct errinfo *ep)197{198return ENOENT;199}200#define get_sym get_sym_dummy201202static void203close_plugin_dummy(struct plugin_file_handle *h)204{205}206#define close_plugin close_plugin_dummy207208#endif209210long KRB5_CALLCONV211krb5int_open_plugin(const char *filename,212struct plugin_file_handle **handle_out, struct errinfo *ep)213{214long ret;215struct plugin_file_handle *h;216217*handle_out = NULL;218219h = calloc(1, sizeof(*h));220if (h == NULL)221return ENOMEM;222223ret = open_plugin(h, filename, ep);224if (ret) {225free(h);226return ret;227}228229*handle_out = h;230return 0;231}232233long KRB5_CALLCONV234krb5int_get_plugin_data(struct plugin_file_handle *h, const char *csymname,235void **sym_out, struct errinfo *ep)236{237return get_sym(h, csymname, sym_out, ep);238}239240long KRB5_CALLCONV241krb5int_get_plugin_func(struct plugin_file_handle *h, const char *csymname,242void (**sym_out)(void), struct errinfo *ep)243{244void *dptr = NULL;245long ret = get_sym(h, csymname, &dptr, ep);246247if (!ret)248*sym_out = (void (*)(void))dptr;249return ret;250}251252void KRB5_CALLCONV253krb5int_close_plugin (struct plugin_file_handle *h)254{255close_plugin(h);256free(h);257}258259static long260krb5int_plugin_file_handle_array_init (struct plugin_file_handle ***harray)261{262long err = 0;263264*harray = calloc (1, sizeof (**harray)); /* calloc initializes to NULL */265if (*harray == NULL) { err = ENOMEM; }266267return err;268}269270static long271krb5int_plugin_file_handle_array_add (struct plugin_file_handle ***harray, size_t *count,272struct plugin_file_handle *p)273{274long err = 0;275struct plugin_file_handle **newharray = NULL;276size_t newcount = *count + 1;277278newharray = realloc (*harray, ((newcount + 1) * sizeof (**harray))); /* +1 for NULL */279if (newharray == NULL) {280err = ENOMEM;281} else {282newharray[newcount - 1] = p;283newharray[newcount] = NULL;284*count = newcount;285*harray = newharray;286}287288return err;289}290291static void292krb5int_plugin_file_handle_array_free (struct plugin_file_handle **harray)293{294size_t i;295296if (harray != NULL) {297for (i = 0; harray[i] != NULL; i++) {298krb5int_close_plugin (harray[i]);299}300free (harray);301}302}303304#if TARGET_OS_MAC305#define FILEEXTS { "", ".bundle", ".dylib", ".so", NULL }306#elif defined(_WIN32)307#define FILEEXTS { "", ".dll", NULL }308#else309#define FILEEXTS { "", ".so", NULL }310#endif311312313static void314krb5int_free_plugin_filenames (char **filenames)315{316size_t i;317318if (filenames != NULL) {319for (i = 0; filenames[i] != NULL; i++) {320free (filenames[i]);321}322free (filenames);323}324}325326327static long328krb5int_get_plugin_filenames (const char * const *filebases, char ***filenames)329{330long err = 0;331static const char *const fileexts[] = FILEEXTS;332char **tempnames = NULL;333size_t bases_count = 0;334size_t exts_count = 0;335size_t i;336337if (!filebases) { err = EINVAL; }338if (!filenames) { err = EINVAL; }339340if (!err) {341for (i = 0; filebases[i]; i++) { bases_count++; }342for (i = 0; fileexts[i]; i++) { exts_count++; }343tempnames = calloc ((bases_count * exts_count)+1, sizeof (char *));344if (!tempnames) { err = ENOMEM; }345}346347if (!err) {348size_t j;349for (i = 0; !err && filebases[i]; i++) {350for (j = 0; !err && fileexts[j]; j++) {351if (asprintf(&tempnames[(i*exts_count)+j], "%s%s",352filebases[i], fileexts[j]) < 0) {353tempnames[(i*exts_count)+j] = NULL;354err = ENOMEM;355}356}357}358tempnames[bases_count * exts_count] = NULL; /* NUL-terminate */359}360361if (!err) {362*filenames = tempnames;363tempnames = NULL;364}365366krb5int_free_plugin_filenames(tempnames);367368return err;369}370371372/* Takes a NULL-terminated list of directories. If filebases is NULL, filebases is ignored373* all plugins in the directories are loaded. If filebases is a NULL-terminated array of names,374* only plugins in the directories with those name (plus any platform extension) are loaded. */375376long KRB5_CALLCONV377krb5int_open_plugin_dirs (const char * const *dirnames,378const char * const *filebases,379struct plugin_dir_handle *dirhandle,380struct errinfo *ep)381{382long err = 0;383struct plugin_file_handle **h = NULL;384size_t count = 0;385char **filenames = NULL;386size_t i;387388if (!err) {389err = krb5int_plugin_file_handle_array_init (&h);390}391392if (!err && (filebases != NULL)) {393err = krb5int_get_plugin_filenames (filebases, &filenames);394}395396for (i = 0; !err && dirnames[i] != NULL; i++) {397if (filenames != NULL) {398/* load plugins with names from filenames from each directory */399size_t j;400401for (j = 0; !err && filenames[j] != NULL; j++) {402struct plugin_file_handle *handle = NULL;403char *filepath = NULL;404405if (!err)406err = k5_path_join(dirnames[i], filenames[j], &filepath);407408if (!err && krb5int_open_plugin(filepath, &handle, ep) == 0) {409err = krb5int_plugin_file_handle_array_add (&h, &count, handle);410if (!err)411handle = NULL; /* h takes ownership */412}413414free(filepath);415if (handle != NULL) { krb5int_close_plugin (handle); }416}417} else {418char **fnames = NULL;419size_t j;420421err = k5_dir_filenames(dirnames[i], &fnames);422for (j = 0; !err && fnames[j] != NULL; j++) {423char *filepath = NULL;424struct plugin_file_handle *handle = NULL;425426if (strcmp(fnames[j], ".") == 0 ||427strcmp(fnames[j], "..") == 0)428continue;429430err = k5_path_join(dirnames[i], fnames[j], &filepath);431432if (!err && krb5int_open_plugin(filepath, &handle, ep) == 0) {433err = krb5int_plugin_file_handle_array_add(&h, &count,434handle);435if (!err)436handle = NULL; /* h takes ownership */437}438439free(filepath);440if (handle != NULL)441krb5int_close_plugin(handle);442}443444k5_free_filenames(fnames);445}446}447448if (err == ENOENT) {449err = 0; /* ran out of plugins -- do nothing */450}451452if (!err) {453dirhandle->files = h;454h = NULL; /* dirhandle->files takes ownership */455}456457if (filenames != NULL) { krb5int_free_plugin_filenames (filenames); }458if (h != NULL) { krb5int_plugin_file_handle_array_free (h); }459460return err;461}462463void KRB5_CALLCONV464krb5int_close_plugin_dirs (struct plugin_dir_handle *dirhandle)465{466size_t i;467468if (dirhandle->files != NULL) {469for (i = 0; dirhandle->files[i] != NULL; i++) {470krb5int_close_plugin (dirhandle->files[i]);471}472free (dirhandle->files);473dirhandle->files = NULL;474}475}476477void KRB5_CALLCONV478krb5int_free_plugin_dir_data (void **ptrs)479{480/* Nothing special to be done per pointer. */481free(ptrs);482}483484long KRB5_CALLCONV485krb5int_get_plugin_dir_data (struct plugin_dir_handle *dirhandle,486const char *symname,487void ***ptrs,488struct errinfo *ep)489{490long err = 0;491void **p = NULL;492size_t count = 0;493494/* XXX Do we need to add a leading "_" to the symbol name on any495modern platforms? */496497Tprintf("get_plugin_data_sym(%s)\n", symname);498499if (!err) {500p = calloc (1, sizeof (*p)); /* calloc initializes to NULL */501if (p == NULL) { err = ENOMEM; }502}503504if (!err && (dirhandle != NULL) && (dirhandle->files != NULL)) {505size_t i = 0;506507for (i = 0; !err && (dirhandle->files[i] != NULL); i++) {508void *sym = NULL;509510if (krb5int_get_plugin_data (dirhandle->files[i], symname, &sym, ep) == 0) {511void **newp = NULL;512513count++;514newp = realloc (p, ((count + 1) * sizeof (*p))); /* +1 for NULL */515if (newp == NULL) {516err = ENOMEM;517} else {518p = newp;519p[count - 1] = sym;520p[count] = NULL;521}522}523}524}525526if (!err) {527*ptrs = p;528p = NULL; /* ptrs takes ownership */529}530531free(p);532533return err;534}535536void KRB5_CALLCONV537krb5int_free_plugin_dir_func (void (**ptrs)(void))538{539/* Nothing special to be done per pointer. */540free(ptrs);541}542543long KRB5_CALLCONV544krb5int_get_plugin_dir_func (struct plugin_dir_handle *dirhandle,545const char *symname,546void (***ptrs)(void),547struct errinfo *ep)548{549long err = 0;550void (**p)(void) = NULL;551size_t count = 0;552553/* XXX Do we need to add a leading "_" to the symbol name on any554modern platforms? */555556Tprintf("get_plugin_data_sym(%s)\n", symname);557558if (!err) {559p = calloc (1, sizeof (*p)); /* calloc initializes to NULL */560if (p == NULL) { err = ENOMEM; }561}562563if (!err && (dirhandle != NULL) && (dirhandle->files != NULL)) {564size_t i = 0;565566for (i = 0; !err && (dirhandle->files[i] != NULL); i++) {567void (*sym)(void) = NULL;568569if (krb5int_get_plugin_func (dirhandle->files[i], symname, &sym, ep) == 0) {570void (**newp)(void) = NULL;571572count++;573newp = realloc (p, ((count + 1) * sizeof (*p))); /* +1 for NULL */574if (newp == NULL) {575err = ENOMEM;576} else {577p = newp;578p[count - 1] = sym;579p[count] = NULL;580}581}582}583}584585if (!err) {586*ptrs = p;587p = NULL; /* ptrs takes ownership */588}589590free(p);591592return err;593}594595596