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 33850 : copy_modules_once(void *context)
70 : {
71 33850 : heim_dict_t *modules = (heim_dict_t *)context;
72 :
73 33850 : *modules = heim_dict_create(11);
74 33850 : heim_assert(*modules, "plugin modules array allocation failure");
75 33850 : }
76 :
77 : /* returns global modules list, refcount +1 */
78 : static heim_dict_t
79 1433561 : copy_modules(void)
80 : {
81 42936 : static heim_base_once_t modules_once = HEIM_BASE_ONCE_INIT;
82 :
83 1433561 : heim_base_once_f(&modules_once, &__modules, copy_modules_once);
84 :
85 1433561 : return heim_retain(__modules);
86 : }
87 :
88 : /* returns named module, refcount +1 */
89 : static heim_dict_t
90 33073 : copy_module(const char *name)
91 : {
92 33073 : heim_string_t module_name = heim_string_create(name);
93 33073 : heim_dict_t modules = copy_modules();
94 1282 : heim_dict_t module;
95 :
96 33073 : module = heim_dict_copy_value(modules, module_name);
97 33073 : if (module == NULL) {
98 32887 : module = heim_dict_create(11);
99 32887 : heim_dict_set_value(modules, module_name, module);
100 : }
101 :
102 33073 : heim_release(modules);
103 33073 : heim_release(module_name);
104 :
105 33073 : 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 33073 : copy_internal_dso(const char *name)
131 : {
132 33073 : heim_string_t dso_name = HSTR("__HEIMDAL_INTERNAL_DSO__");
133 33073 : heim_dict_t module = copy_module(name);
134 1282 : struct heim_dso *dso;
135 :
136 33073 : if (module == NULL)
137 0 : return NULL;
138 :
139 33073 : dso = heim_dict_copy_value(module, dso_name);
140 33073 : if (dso == NULL) {
141 32887 : dso = heim_alloc(sizeof(*dso), "heim-dso", dso_dealloc);
142 :
143 32887 : dso->path = dso_name;
144 32887 : dso->plugins_by_name = heim_dict_create(11);
145 :
146 32887 : heim_dict_set_value(module, dso_name, dso);
147 : }
148 :
149 33073 : heim_release(module);
150 :
151 33073 : 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 33073 : heim_plugin_register(heim_context context,
199 : heim_pcontext pcontext,
200 : const char *module,
201 : const char *name,
202 : const void *ftable)
203 : {
204 1282 : heim_error_code ret;
205 1282 : heim_array_t plugins;
206 1282 : heim_string_t hname;
207 1282 : struct heim_dso *dso;
208 1282 : struct heim_plugin_register_ctx ctx;
209 :
210 33073 : ctx.symbol = ftable;
211 33073 : ctx.is_dup = 0;
212 :
213 1282 : HEIMDAL_MUTEX_lock(&modules_mutex);
214 :
215 33073 : dso = copy_internal_dso(module);
216 33073 : hname = heim_string_create(name);
217 33073 : plugins = heim_dict_copy_value(dso->plugins_by_name, hname);
218 33073 : if (plugins != NULL)
219 0 : heim_array_iterate_f(plugins, &ctx, plugin_register_check_dup);
220 : else {
221 33073 : plugins = heim_array_create();
222 33073 : heim_dict_set_value(dso->plugins_by_name, hname, plugins);
223 : }
224 :
225 33073 : ret = 0;
226 33073 : if (!ctx.is_dup) {
227 : /* Note: refactored plugin API only supports common plugin layout */
228 1282 : struct heim_plugin *pl;
229 :
230 33073 : pl = heim_alloc(sizeof(*pl), "heim-plugin", plugin_free);
231 33073 : if (pl == NULL) {
232 0 : ret = heim_enomem(context);
233 : } else {
234 33073 : pl->ftable = ftable;
235 33073 : ret = pl->ftable->init(pcontext, &pl->ctx);
236 33073 : if (ret == 0) {
237 33073 : heim_array_append_value(plugins, pl);
238 33073 : heim_debug(context, 5, "Registered %s plugin", name);
239 : }
240 33073 : heim_release(pl);
241 : }
242 : }
243 :
244 1282 : HEIMDAL_MUTEX_unlock(&modules_mutex);
245 :
246 33073 : heim_release(dso);
247 33073 : heim_release(hname);
248 33073 : heim_release(plugins);
249 :
250 33073 : 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 33943 : 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 33943 : }
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 525212 : reduce_by_version(heim_object_t value, void *ctx, int *stop)
652 : {
653 525212 : struct iter_ctx *s = ctx;
654 525212 : struct heim_plugin *pl = value;
655 :
656 525212 : if (pl->ftable && pl->ftable->minor_version >= s->caller->min_version)
657 525212 : heim_array_append_value(s->result, pl);
658 525212 : }
659 :
660 : static void
661 1361035 : search_modules(heim_object_t key, heim_object_t value, void *ctx)
662 : {
663 1361035 : struct iter_ctx *s = ctx;
664 1361035 : struct heim_dso *p = value;
665 1361035 : 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 1361035 : if (plugins) {
688 525212 : heim_array_iterate_f(plugins, s, reduce_by_version);
689 525212 : heim_release(plugins);
690 : }
691 1361035 : }
692 :
693 : static void
694 525212 : eval_results(heim_object_t value, void *ctx, int *stop)
695 : {
696 525212 : struct heim_plugin *pl = value;
697 525212 : struct iter_ctx *s = ctx;
698 :
699 525212 : if (s->ret != s->plugin_no_handle_retval)
700 0 : return;
701 :
702 525212 : s->ret = s->func(s->pcontext, pl->ftable, pl->ctx, s->userctx);
703 525212 : if (s->ret != s->plugin_no_handle_retval
704 260477 : && !(s->flags & HEIM_PLUGIN_INVOKE_ALL))
705 260477 : *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 1400488 : 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 1400488 : heim_string_t m = heim_string_create(caller->module);
748 1400488 : heim_dict_t modules, dict = NULL;
749 41654 : struct iter_ctx s;
750 :
751 1400488 : s.context = context;
752 1400488 : s.pcontext = pcontext;
753 1400488 : s.caller = caller;
754 1400488 : s.n = heim_string_create(caller->name);
755 1400488 : s.flags = flags;
756 1400488 : s.result = heim_array_create();
757 1400488 : s.func = func;
758 1400488 : s.userctx = userctx;
759 1400488 : s.plugin_no_handle_retval = nohandle;
760 1400488 : s.ret = nohandle;
761 :
762 41654 : HEIMDAL_MUTEX_lock(&modules_mutex);
763 :
764 : /* Get loaded plugins */
765 1400488 : modules = copy_modules();
766 1400488 : dict = heim_dict_copy_value(modules, m);
767 :
768 : /* Add loaded plugins to s.result array */
769 1400488 : if (dict)
770 1361035 : heim_dict_iterate_f(dict, &s, search_modules);
771 :
772 : /* We don't need to hold modules_mutex during plugin invocation */
773 41654 : HEIMDAL_MUTEX_unlock(&modules_mutex);
774 :
775 : /* Invoke loaded plugins */
776 1400488 : heim_array_iterate_f(s.result, &s, eval_results);
777 :
778 1400488 : heim_release(s.result);
779 1400488 : heim_release(s.n);
780 1400488 : heim_release(dict);
781 1400488 : heim_release(m);
782 1400488 : heim_release(modules);
783 :
784 1400488 : return s.ret;
785 : }
|