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