LCOV - code coverage report
Current view: top level - third_party/heimdal/lib/base - plugin.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 115 146 78.8 %
Date: 2024-05-31 13:13:24 Functions: 10 14 71.4 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2006 - 2020 Kungliga Tekniska Högskolan
       3             :  * (Royal Institute of Technology, Stockholm, Sweden).
       4             :  * All rights reserved.
       5             :  *
       6             :  * Portions Copyright (c) 2018 AuriStor, Inc.
       7             :  *
       8             :  * Redistribution and use in source and binary forms, with or without
       9             :  * modification, are permitted provided that the following conditions
      10             :  * are met:
      11             :  *
      12             :  * 1. Redistributions of source code must retain the above copyright
      13             :  *    notice, this list of conditions and the following disclaimer.
      14             :  *
      15             :  * 2. Redistributions in binary form must reproduce the above copyright
      16             :  *    notice, this list of conditions and the following disclaimer in the
      17             :  *    documentation and/or other materials provided with the distribution.
      18             :  *
      19             :  * 3. Neither the name of the Institute nor the names of its contributors
      20             :  *    may be used to endorse or promote products derived from this software
      21             :  *    without specific prior written permission.
      22             :  *
      23             :  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
      24             :  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      25             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      26             :  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
      27             :  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      28             :  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      29             :  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      30             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      31             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      32             :  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      33             :  * SUCH DAMAGE.
      34             :  */
      35             : 
      36             : #include "baselocl.h"
      37             : #include "common_plugin.h"
      38             : 
      39             : /*
      40             :  * Documentation for the Heimdal plugin system is in lib/krb5/plugin.c and
      41             :  * lib/krb5/krb5-plugin.7.
      42             :  */
      43             : 
      44             : /*
      45             :  * Definitions:
      46             :  *
      47             :  *      module      - a category of plugin module, identified by subsystem
      48             :  *                    (e.g., "krb5")
      49             :  *      dso         - a library for a module containing a map of plugin
      50             :  *                    types to plugins (e.g. "service_locator")
      51             :  *      plugin      - a set of callbacks and state that follows the
      52             :  *                    common plugin module definition (version, init, fini)
      53             :  *
      54             :  * Obviously it would have been clearer to use the term "module" rather than
      55             :  * "DSO" given there is an internal "DSO", but "module" was already taken...
      56             :  *
      57             :  *      modules := { module: dsos }
      58             :  *      dsos := { path, dsohandle, plugins-by-name }
      59             :  *      plugins-by-name := { plugin-name: [plug] }
      60             :  *      plug := { ftable, ctx }
      61             :  */
      62             : 
      63             : /* global module use, use copy_modules() accessor to access */
      64             : static heim_dict_t __modules;
      65             : 
      66             : static HEIMDAL_MUTEX modules_mutex = HEIMDAL_MUTEX_INITIALIZER;
      67             : 
      68             : static void
      69       34143 : copy_modules_once(void *context)
      70             : {
      71       34143 :     heim_dict_t *modules = (heim_dict_t *)context;
      72             : 
      73       34143 :     *modules = heim_dict_create(11);
      74       34143 :     heim_assert(*modules, "plugin modules array allocation failure");
      75       34143 : }
      76             : 
      77             : /* returns global modules list, refcount +1 */
      78             : static heim_dict_t
      79     1412198 : copy_modules(void)
      80             : {
      81       44576 :     static heim_base_once_t modules_once = HEIM_BASE_ONCE_INIT;
      82             : 
      83     1412198 :     heim_base_once_f(&modules_once, &__modules, copy_modules_once);
      84             : 
      85     1412198 :     return heim_retain(__modules);
      86             : }
      87             : 
      88             : /* returns named module, refcount +1 */
      89             : static heim_dict_t
      90       33357 : copy_module(const char *name)
      91             : {
      92       33357 :     heim_string_t module_name = heim_string_create(name);
      93       33357 :     heim_dict_t modules = copy_modules();
      94        1303 :     heim_dict_t module;
      95             : 
      96       33357 :     module = heim_dict_copy_value(modules, module_name);
      97       33357 :     if (module == NULL) {
      98       33171 :         module = heim_dict_create(11);
      99       33171 :         heim_dict_set_value(modules, module_name, module);
     100             :     }
     101             : 
     102       33357 :     heim_release(modules);
     103       33357 :     heim_release(module_name);
     104             : 
     105       33357 :     return module;
     106             : }
     107             : 
     108             : /* DSO helpers */
     109             : struct heim_dso {
     110             :     heim_string_t path;
     111             :     heim_dict_t plugins_by_name;
     112             :     void *dsohandle;
     113             : };
     114             : 
     115             : static void HEIM_CALLCONV
     116           0 : dso_dealloc(void *ptr)
     117             : {
     118           0 :     struct heim_dso *p = ptr;
     119             : 
     120           0 :     heim_release(p->path);
     121           0 :     heim_release(p->plugins_by_name);
     122             : #ifdef HAVE_DLOPEN
     123             :     if (p->dsohandle)
     124             :         dlclose(p->dsohandle);
     125             : #endif
     126           0 : }
     127             : 
     128             : /* returns internal "DSO" for name, refcount +1 */
     129             : static struct heim_dso *
     130       33357 : copy_internal_dso(const char *name)
     131             : {
     132       33357 :     heim_string_t dso_name = HSTR("__HEIMDAL_INTERNAL_DSO__");
     133       33357 :     heim_dict_t module = copy_module(name);
     134        1303 :     struct heim_dso *dso;
     135             : 
     136       33357 :     if (module == NULL)
     137           0 :         return NULL;
     138             : 
     139       33357 :     dso = heim_dict_copy_value(module, dso_name);
     140       33357 :     if (dso == NULL) {
     141       33171 :         dso = heim_alloc(sizeof(*dso), "heim-dso", dso_dealloc);
     142             : 
     143       33171 :         dso->path = dso_name;
     144       33171 :         dso->plugins_by_name = heim_dict_create(11);
     145             : 
     146       33171 :         heim_dict_set_value(module, dso_name, dso);
     147             :     }
     148             : 
     149       33357 :     heim_release(module);
     150             : 
     151       33357 :     return dso;
     152             : }
     153             : 
     154             : struct heim_plugin {
     155             :     heim_plugin_common_ftable_const_p ftable;
     156             :     void *ctx;
     157             : };
     158             : 
     159             : static void HEIM_CALLCONV
     160           0 : plugin_free(void *ptr)
     161             : {
     162           0 :     struct heim_plugin *pl = ptr;
     163             : 
     164           0 :     if (pl->ftable && pl->ftable->fini)
     165           0 :         pl->ftable->fini(pl->ctx);
     166           0 : }
     167             : 
     168             : struct heim_plugin_register_ctx {
     169             :     const void *symbol;
     170             :     int is_dup;
     171             : };
     172             : 
     173             : static void
     174           0 : plugin_register_check_dup(heim_object_t value, void *ctx, int *stop)
     175             : {
     176           0 :     struct heim_plugin_register_ctx *pc = ctx;
     177           0 :     struct heim_plugin *pl = value;
     178             : 
     179           0 :     if (pl->ftable == pc->symbol) {
     180           0 :         pc->is_dup = 1;
     181           0 :         *stop = 1;
     182             :     }
     183           0 : }
     184             : 
     185             : /**
     186             :  * Register a plugin symbol name of specific type.
     187             :  * @param context a Keberos context
     188             :  * @param module name of plugin module (e.g., "krb5")
     189             :  * @param name name of plugin symbol (e.g., "krb5_plugin_kuserok")
     190             :  * @param ftable a pointer to a function pointer table
     191             :  * @return In case of error a non zero error com_err error is returned
     192             :  * and the Kerberos error string is set.
     193             :  *
     194             :  * @ingroup heim_support
     195             :  */
     196             : 
     197             : heim_error_code
     198       33357 : heim_plugin_register(heim_context context,
     199             :                      heim_pcontext pcontext,
     200             :                      const char *module,
     201             :                      const char *name,
     202             :                      const void *ftable)
     203             : {
     204        1303 :     heim_error_code ret;
     205        1303 :     heim_array_t plugins;
     206        1303 :     heim_string_t hname;
     207        1303 :     struct heim_dso *dso;
     208        1303 :     struct heim_plugin_register_ctx ctx;
     209             : 
     210       33357 :     ctx.symbol = ftable;
     211       33357 :     ctx.is_dup = 0;
     212             : 
     213        1303 :     HEIMDAL_MUTEX_lock(&modules_mutex);
     214             : 
     215       33357 :     dso = copy_internal_dso(module);
     216       33357 :     hname = heim_string_create(name);
     217       33357 :     plugins = heim_dict_copy_value(dso->plugins_by_name, hname);
     218       33357 :     if (plugins != NULL)
     219           0 :         heim_array_iterate_f(plugins, &ctx, plugin_register_check_dup);
     220             :     else {
     221       33357 :         plugins = heim_array_create();
     222       33357 :         heim_dict_set_value(dso->plugins_by_name, hname, plugins);
     223             :     }
     224             : 
     225       33357 :     ret = 0;
     226       33357 :     if (!ctx.is_dup) {
     227             :         /* Note: refactored plugin API only supports common plugin layout */
     228        1303 :         struct heim_plugin *pl;
     229             : 
     230       33357 :         pl = heim_alloc(sizeof(*pl), "heim-plugin", plugin_free);
     231       33357 :         if (pl == NULL) {
     232           0 :             ret = heim_enomem(context);
     233             :         } else {
     234       33357 :             pl->ftable = ftable;
     235       33357 :             ret = pl->ftable->init(pcontext, &pl->ctx);
     236       33357 :             if (ret == 0) {
     237       33357 :                 heim_array_append_value(plugins, pl);
     238       33357 :                 heim_debug(context, 5, "Registered %s plugin", name);
     239             :             }
     240       33357 :             heim_release(pl);
     241             :         }
     242             :     }
     243             : 
     244        1303 :     HEIMDAL_MUTEX_unlock(&modules_mutex);
     245             : 
     246       33357 :     heim_release(dso);
     247       33357 :     heim_release(hname);
     248       33357 :     heim_release(plugins);
     249             : 
     250       33357 :     return ret;
     251             : }
     252             : 
     253             : #ifdef HAVE_DLOPEN
     254             : 
     255             : static char *
     256             : resolve_origin(const char *di, const char *module)
     257             : {
     258             : #ifdef HAVE_DLADDR
     259             :     Dl_info dl_info;
     260             :     const char *dname;
     261             :     char *path, *p;
     262             : 
     263             :     if (strncmp(di, "$ORIGIN/", sizeof("$ORIGIN/") - 1) != 0 &&
     264             :         strcmp(di, "$ORIGIN") != 0)
     265             :         return strdup(di);
     266             : 
     267             :     di += sizeof("$ORIGIN") - 1;
     268             : 
     269             :     if (dladdr(heim_plugin_register, &dl_info) == 0) {
     270             :         char *s = NULL;
     271             : 
     272             :         /* dladdr() failed */
     273             :         if (asprintf(&s, LIBDIR "/plugin/%s", module) == -1)
     274             :             return NULL;
     275             :         return s;
     276             :     }
     277             : 
     278             :     dname = dl_info.dli_fname;
     279             : #ifdef _WIN32
     280             :     p = strrchr(dname, '\\');
     281             :     if (p == NULL)
     282             : #endif
     283             :         p = strrchr(dname, '/');
     284             :     if (p) {
     285             :         if (asprintf(&path, "%.*s%s", (int) (p - dname), dname, di) == -1)
     286             :             return NULL;
     287             :     } else {
     288             :         if (asprintf(&path, "%s%s", dname, di) == -1)
     289             :             return NULL;
     290             :     }
     291             : 
     292             :     return path;
     293             : #else
     294             :     char *s = NULL;
     295             : 
     296             :     if (strncmp(di, "$ORIGIN/", sizeof("$ORIGIN/") - 1) != 0 &&
     297             :         strcmp(di, "$ORIGIN") != 0)
     298             :         return strdup(di);
     299             :     if (asprintf(&s, LIBDIR "/plugin/%s", module) == -1)
     300             :         return NULL;
     301             :     return s;
     302             : #endif /* HAVE_DLADDR */
     303             : }
     304             : 
     305             : #endif /* HAVE_DLOPEN */
     306             : 
     307             : /**
     308             :  * Load plugins (new system) for the given module @module from the given
     309             :  * directory @paths.
     310             :  *
     311             :  * Inputs:
     312             :  *
     313             :  * @context A heim_context
     314             :  * @module Name of plugin module (typically "krb5")
     315             :  * @paths Array of directory paths where to look
     316             :  */
     317             : void
     318       34236 : heim_load_plugins(heim_context context,
     319             :                   const char *module,
     320             :                   const char **paths)
     321             : {
     322             : #ifdef HAVE_DLOPEN
     323             :     heim_string_t s = heim_string_create(module);
     324             :     heim_dict_t mod, modules;
     325             :     struct dirent *entry;
     326             :     heim_error_code ret;
     327             :     const char **di;
     328             :     char *dirname = NULL;
     329             :     DIR *d;
     330             : #ifdef _WIN32
     331             :     char *plugin_prefix;
     332             :     size_t plugin_prefix_len;
     333             : 
     334             :     if (asprintf(&plugin_prefix, "plugin_%s_", module) == -1)
     335             :         return;
     336             :     plugin_prefix_len = (plugin_prefix ? strlen(plugin_prefix) : 0);
     337             : #endif
     338             : 
     339             :     HEIMDAL_MUTEX_lock(&modules_mutex);
     340             : 
     341             :     modules = copy_modules();
     342             : 
     343             :     mod = heim_dict_copy_value(modules, s);
     344             :     if (mod == NULL) {
     345             :         mod = heim_dict_create(11);
     346             :         if (mod == NULL) {
     347             :             HEIMDAL_MUTEX_unlock(&modules_mutex);
     348             :             heim_release(s);
     349             :             heim_release(modules);
     350             :             heim_debug(context, 5, "Load plugins for module %s failed", module);
     351             :             return;
     352             :         }
     353             :         heim_dict_set_value(modules, s, mod);
     354             :     }
     355             :     heim_release(s);
     356             :     heim_release(modules);
     357             : 
     358             :     for (di = paths; *di != NULL; di++) {
     359             :         free(dirname);
     360             :         dirname = resolve_origin(*di, module);
     361             :         if (dirname == NULL) {
     362             :             heim_debug(context, 10, "Could not resolve %s", *di);
     363             :             continue;
     364             :         }
     365             :         d = opendir(dirname);
     366             :         if (d == NULL) {
     367             :             heim_debug(context, 10, "No such directory %s", dirname);
     368             :             continue;
     369             :         }
     370             :         rk_cloexec_dir(d);
     371             : 
     372             :         heim_debug(context, 10, "Load plugins for module %s; search %s (%s)",
     373             :                    module, *di, dirname);
     374             : 
     375             :         while ((entry = readdir(d)) != NULL) {
     376             :             char *n = entry->d_name;
     377             :             char *path = NULL;
     378             :             heim_string_t spath;
     379             :             struct heim_dso *p;
     380             : 
     381             :             /* skip . and .. */
     382             :             if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')))
     383             :                 continue;
     384             : 
     385             :             ret = 0;
     386             : #ifdef _WIN32
     387             :             /*
     388             :              * On Windows, plugins must be loaded from the same directory as
     389             :              * heimdal.dll (typically the assembly directory) and must have
     390             :              * the name form "plugin_<module>_<name>.dll".
     391             :              */
     392             :             {
     393             :                 char *ext;
     394             : 
     395             :                 if (strnicmp(n, plugin_prefix, plugin_prefix_len) != 0)
     396             :                     continue;
     397             :                 ext = strrchr(n, '.');
     398             :                 if (ext == NULL || stricmp(ext, ".dll") != 0)
     399             :                      continue;
     400             : 
     401             :                 ret = asprintf(&path, "%s\\%s", dirname, n);
     402             :                 if (ret < 0 || path == NULL)
     403             :                     continue;
     404             :             }
     405             : #endif
     406             : #ifdef __APPLE__
     407             :             { /* support loading bundles on MacOS */
     408             :                 size_t len = strlen(n);
     409             :                 if (len > 7 && strcmp(&n[len - 7],  ".bundle") == 0)
     410             :                     ret = asprintf(&path, "%s/%s/Contents/MacOS/%.*s", dirname, n, (int)(len - 7), n);
     411             :             }
     412             : #endif
     413             :             if (ret < 0 || path == NULL)
     414             :                 ret = asprintf(&path, "%s/%s", dirname, n);
     415             : 
     416             :             if (ret < 0 || path == NULL)
     417             :                 continue;
     418             : 
     419             :             spath = heim_string_create(n);
     420             :             if (spath == NULL) {
     421             :                 free(path);
     422             :                 continue;
     423             :             }
     424             : 
     425             :             /* check if already cached */
     426             :             p = heim_dict_copy_value(mod, spath);
     427             :             if (p == NULL) {
     428             :                 p = heim_alloc(sizeof(*p), "heim-dso", dso_dealloc);
     429             :                 if (p)
     430             :                     p->dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY|RTLD_GROUP);
     431             :                 if (p && p->dsohandle) {
     432             :                     p->path = heim_retain(spath);
     433             :                     p->plugins_by_name = heim_dict_create(11);
     434             :                     heim_dict_set_value(mod, spath, p);
     435             :                     heim_debug(context, 10, "Load plugins for module %s; "
     436             :                                "found DSO %s", module, path);
     437             :                 }
     438             :             }
     439             :             heim_release(p);
     440             :             heim_release(spath);
     441             :             free(path);
     442             :         }
     443             :         closedir(d);
     444             :     }
     445             :     free(dirname);
     446             :     HEIMDAL_MUTEX_unlock(&modules_mutex);
     447             :     heim_release(mod);
     448             : #ifdef _WIN32
     449             :     if (plugin_prefix)
     450             :         free(plugin_prefix);
     451             : #endif
     452             : #endif /* HAVE_DLOPEN */
     453       34236 : }
     454             : 
     455             : /**
     456             :  * Unload plugins of the given @module name.
     457             :  *
     458             :  * Params:
     459             :  *
     460             :  * @module Name of module whose plusins to unload.
     461             :  */
     462             : void
     463           0 : heim_unload_plugins(heim_context context, const char *module)
     464             : {
     465           0 :     heim_string_t sname = heim_string_create(module);
     466           0 :     heim_dict_t modules;
     467             : 
     468           0 :     HEIMDAL_MUTEX_lock(&modules_mutex);
     469             : 
     470           0 :     modules = copy_modules();
     471           0 :     heim_dict_delete_key(modules, sname);
     472             : 
     473           0 :     HEIMDAL_MUTEX_unlock(&modules_mutex);
     474             : 
     475           0 :     heim_release(modules);
     476           0 :     heim_release(sname);
     477           0 : }
     478             : 
     479             : struct iter_ctx {
     480             :     heim_context context;
     481             :     heim_pcontext pcontext;
     482             :     heim_string_t n;
     483             :     const struct heim_plugin_data *caller;
     484             :     int flags;
     485             :     heim_array_t result;
     486             :     int32_t (HEIM_LIB_CALL *func)(void *, const void *, void *, void *);
     487             :     void *userctx;
     488             :     int32_t ret;
     489             :     int32_t plugin_no_handle_retval;
     490             : };
     491             : 
     492             : #ifdef HAVE_DLOPEN
     493             : /*
     494             :  * Add plugin from a DSO that exports the plugin structure directly. This is
     495             :  * provided for backwards compatibility with prior versions of Heimdal, but it
     496             :  * does not allow a module to export multiple plugins, nor does it allow
     497             :  * instance validation.
     498             :  */
     499             : static heim_array_t
     500             : add_dso_plugin_struct(heim_context context,
     501             :                       heim_pcontext pcontext,
     502             :                       const char *dsopath,
     503             :                       void *dsohandle,
     504             :                       const char *name)
     505             : {
     506             :     heim_error_code ret;
     507             :     heim_plugin_common_ftable_p cpm;
     508             :     struct heim_plugin *pl;
     509             :     heim_array_t plugins;
     510             : 
     511             :     if (dsohandle == NULL)
     512             :         return NULL;
     513             : 
     514             :     /* suppress error here because we may be looking for a different plugin type */
     515             :     cpm = (heim_plugin_common_ftable_p)dlsym(dsohandle, name);
     516             :     if (cpm == NULL) {
     517             :         heim_debug(context, 15, "Symbol %s not found in %s", name, dsopath);
     518             :         return NULL;
     519             :     }
     520             : 
     521             :     heim_warnx(context, "plugin %s uses deprecated loading mechanism", dsopath);
     522             : 
     523             :     pl = heim_alloc(sizeof(*pl), "heim-plugin", plugin_free);
     524             : 
     525             :     ret = cpm->init(pcontext, &pl->ctx);
     526             :     if (ret) {
     527             :         heim_warn(context, ret, "plugin %s failed to initialize", dsopath);
     528             :         heim_release(pl);
     529             :         return NULL;
     530             :     }
     531             : 
     532             :     pl->ftable = cpm;
     533             : 
     534             :     plugins = heim_array_create();
     535             :     heim_array_append_value(plugins, pl);
     536             :     heim_release(pl);
     537             : 
     538             :     return plugins;
     539             : }
     540             : 
     541             : static int
     542             : validate_plugin_deps(heim_context context,
     543             :                      const struct heim_plugin_data *caller,
     544             :                      const char *dsopath,
     545             :                      heim_get_instance_func_t get_instance)
     546             : {
     547             :     size_t i;
     548             : 
     549             :     if (get_instance == NULL) {
     550             :         heim_warnx(context, "plugin %s omitted instance callback",
     551             :                    dsopath);
     552             :         return FALSE;
     553             :     }
     554             : 
     555             :     for (i = 0; caller->deps[i] != NULL; i++) {
     556             :         uintptr_t heim_instance, plugin_instance;
     557             : 
     558             :         heim_instance = caller->get_instance(caller->deps[i]);
     559             :         plugin_instance = get_instance(caller->deps[i]);
     560             : 
     561             :         if (heim_instance == 0 || plugin_instance == 0)
     562             :             continue;
     563             : 
     564             :         if (heim_instance != plugin_instance) {
     565             :             heim_warnx(context, "plugin %s library %s linked against different "
     566             :                        "instance of Heimdal (got %zu, us %zu)",
     567             :                        dsopath, caller->deps[i],
     568             :                        plugin_instance, heim_instance);
     569             :             return FALSE;
     570             :         }
     571             :         heim_debug(context, 10, "Validated plugin library dependency %s for %s",
     572             :                    caller->deps[i], dsopath);
     573             :     }
     574             : 
     575             :     return TRUE;
     576             : }
     577             : 
     578             : /*
     579             :  * New interface from Heimdal 8 where a DSO can export a load function
     580             :  * that can return both a Heimdal instance identifier along with an
     581             :  * array of plugins.
     582             :  */
     583             : static heim_array_t
     584             : add_dso_plugins_load_fn(heim_context context,
     585             :                         heim_pcontext pcontext,
     586             :                         const struct heim_plugin_data *caller,
     587             :                         const char *dsopath,
     588             :                         void *dsohandle)
     589             : {
     590             :     heim_error_code ret;
     591             :     heim_array_t plugins;
     592             :     heim_plugin_load_t load_fn;
     593             :     char *sym = NULL;
     594             :     size_t i;
     595             :     heim_get_instance_func_t get_instance;
     596             :     size_t n_ftables;
     597             :     heim_plugin_common_ftable_cp *ftables;
     598             : 
     599             :     if (asprintf(&sym, "%s_plugin_load", caller->name) == -1 || sym == NULL)
     600             :         return NULL;
     601             : 
     602             :     /* suppress error here because we may be looking for a different plugin type */
     603             :     load_fn = (heim_plugin_load_t)dlsym(dsohandle, sym);
     604             :     if (load_fn == NULL) {
     605             :         heim_debug(context, 15, "Symbol %s not found in %s", sym, dsopath);
     606             :         free(sym);
     607             :         return NULL;
     608             :     }
     609             : 
     610             :     ret = load_fn(pcontext, &get_instance, &n_ftables, &ftables);
     611             :     if (ret) {
     612             :         heim_warn(context, ret, "plugin %s failed to load", dsopath);
     613             :         free(sym);
     614             : 
     615             :         /* fallback to loading structure directly */
     616             :         return add_dso_plugin_struct(context, pcontext, dsopath,
     617             :                                      dsohandle, caller->name);
     618             :     }
     619             : 
     620             :     if (!validate_plugin_deps(context, caller, dsopath, get_instance)) {
     621             :         free(sym);
     622             :         return NULL;
     623             :     }
     624             : 
     625             :     plugins = heim_array_create();
     626             : 
     627             :     for (i = 0; i < n_ftables; i++) {
     628             :         heim_plugin_common_ftable_cp cpm = ftables[i];
     629             :         struct heim_plugin *pl;
     630             : 
     631             :         pl = heim_alloc(sizeof(*pl), "heim-plugin", plugin_free);
     632             : 
     633             :         ret = cpm->init(pcontext, &pl->ctx);
     634             :         if (ret) {
     635             :             heim_warn(context, ret, "plugin %s[%zu] failed to initialize",
     636             :                       dsopath, i);
     637             :         } else {
     638             :             pl->ftable = cpm;
     639             :             heim_array_append_value(plugins, pl);
     640             :         }
     641             :         heim_release(pl);
     642             :     }
     643             : 
     644             :     heim_debug(context, 15, "DSO %s loaded (%s)", dsopath, sym);
     645             :     free(sym);
     646             :     return plugins;
     647             : }
     648             : #endif /* HAVE_DLOPEN */
     649             : 
     650             : static void
     651      543719 : reduce_by_version(heim_object_t value, void *ctx, int *stop)
     652             : {
     653      543719 :     struct iter_ctx *s = ctx;
     654      543719 :     struct heim_plugin *pl = value;
     655             : 
     656      543719 :     if (pl->ftable && pl->ftable->minor_version >= s->caller->min_version)
     657      543719 :         heim_array_append_value(s->result, pl);
     658      543719 : }
     659             : 
     660             : static void
     661     1339087 : search_modules(heim_object_t key, heim_object_t value, void *ctx)
     662             : {
     663     1339087 :     struct iter_ctx *s = ctx;
     664     1339087 :     struct heim_dso *p = value;
     665     1339087 :     heim_array_t plugins = heim_dict_copy_value(p->plugins_by_name, s->n);
     666             : 
     667             : #ifdef HAVE_DLOPEN
     668             :     if (plugins == NULL && p->dsohandle) {
     669             :         const char *path = heim_string_get_utf8(p->path);
     670             : 
     671             :         plugins = add_dso_plugins_load_fn(s->context,
     672             :                                           s->pcontext,
     673             :                                           s->caller,
     674             :                                           path,
     675             :                                           p->dsohandle);
     676             :         if (plugins) {
     677             :             heim_dict_set_value(p->plugins_by_name, s->n, plugins);
     678             :             heim_debug(s->context, 5, "Loaded %zu %s %s plugin%s from %s",
     679             :                        heim_array_get_length(plugins),
     680             :                        s->caller->module, s->caller->name,
     681             :                        heim_array_get_length(plugins) > 1 ? "s" : "",
     682             :                        path);
     683             :         }
     684             :     }
     685             : #endif /* HAVE_DLOPEN */
     686             : 
     687     1339087 :     if (plugins) {
     688      543719 :         heim_array_iterate_f(plugins, s, reduce_by_version);
     689      543719 :         heim_release(plugins);
     690             :     }
     691     1339087 : }
     692             : 
     693             : static void
     694      543719 : eval_results(heim_object_t value, void *ctx, int *stop)
     695             : {
     696      543719 :     struct heim_plugin *pl = value;
     697      543719 :     struct iter_ctx *s = ctx;
     698             : 
     699      543719 :     if (s->ret != s->plugin_no_handle_retval)
     700           0 :         return;
     701             : 
     702      543719 :     s->ret = s->func(s->pcontext, pl->ftable, pl->ctx, s->userctx);
     703      543719 :     if (s->ret != s->plugin_no_handle_retval
     704      289075 :         && !(s->flags & HEIM_PLUGIN_INVOKE_ALL))
     705      289075 :         *stop = 1;
     706             : }
     707             : 
     708             : /**
     709             :  * Run plugins for the given @module (e.g., "krb5") and @name (e.g.,
     710             :  * "kuserok").  Specifically, the @func is invoked once per-plugin with
     711             :  * four arguments: the @context, the plugin symbol value (a pointer to a
     712             :  * struct whose first three fields are the same as common_plugin_ftable),
     713             :  * a context value produced by the plugin's init method, and @userctx.
     714             :  *
     715             :  * @func should unpack arguments for a plugin function and invoke it
     716             :  * with arguments taken from @userctx.  @func should save plugin
     717             :  * outputs, if any, in @userctx.
     718             :  *
     719             :  * All loaded and registered plugins are invoked via @func until @func
     720             :  * returns something other than @nohandle.  Plugins that have nothing to
     721             :  * do for the given arguments should return the same value as @nohandle.
     722             :  *
     723             :  * Inputs:
     724             :  *
     725             :  * @context     A heim_context
     726             :  * @pcontext    A context for the plugin, such as a krb5_context
     727             :  * @module      Name of module (typically "krb5")
     728             :  * @name        Name of pluggable interface (e.g., "kuserok")
     729             :  * @min_version Lowest acceptable plugin minor version number
     730             :  * @flags       Flags (none defined at this time)
     731             :  * @nohandle    Flags (none defined at this time)
     732             :  * @userctx     Callback data for the callback function @func
     733             :  * @func        A callback function, invoked once per-plugin
     734             :  *
     735             :  * Outputs: None, other than the return value and such outputs as are
     736             :  *          gathered by @func.
     737             :  */
     738             : heim_error_code
     739     1378841 : heim_plugin_run_f(heim_context context,
     740             :                   heim_pcontext pcontext,
     741             :                   const struct heim_plugin_data *caller,
     742             :                   int flags,
     743             :                   int32_t nohandle,
     744             :                   void *userctx,
     745             :                   int32_t (HEIM_LIB_CALL *func)(void *, const void *, void *, void *))
     746             : {
     747     1378841 :     heim_string_t m = heim_string_create(caller->module);
     748     1378841 :     heim_dict_t modules, dict = NULL;
     749       43273 :     struct iter_ctx s;
     750             : 
     751     1378841 :     s.context = context;
     752     1378841 :     s.pcontext = pcontext;
     753     1378841 :     s.caller = caller;
     754     1378841 :     s.n = heim_string_create(caller->name);
     755     1378841 :     s.flags = flags;
     756     1378841 :     s.result = heim_array_create();
     757     1378841 :     s.func = func;
     758     1378841 :     s.userctx = userctx;
     759     1378841 :     s.plugin_no_handle_retval = nohandle;
     760     1378841 :     s.ret = nohandle;
     761             : 
     762       43273 :     HEIMDAL_MUTEX_lock(&modules_mutex);
     763             : 
     764             :     /* Get loaded plugins */
     765     1378841 :     modules = copy_modules();
     766     1378841 :     dict = heim_dict_copy_value(modules, m);
     767             : 
     768             :     /* Add loaded plugins to s.result array */
     769     1378841 :     if (dict)
     770     1339087 :         heim_dict_iterate_f(dict, &s, search_modules);
     771             : 
     772             :     /* We don't need to hold modules_mutex during plugin invocation */
     773       43273 :     HEIMDAL_MUTEX_unlock(&modules_mutex);
     774             : 
     775             :     /* Invoke loaded plugins */
     776     1378841 :     heim_array_iterate_f(s.result, &s, eval_results);
     777             : 
     778     1378841 :     heim_release(s.result);
     779     1378841 :     heim_release(s.n);
     780     1378841 :     heim_release(dict);
     781     1378841 :     heim_release(m);
     782     1378841 :     heim_release(modules);
     783             : 
     784     1378841 :     return s.ret;
     785             : }

Generated by: LCOV version 1.14