2 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include "krb5_locl.h"
43 struct krb5_plugin
*next
;
47 enum { DSO
, SYMBOL
} type
;
54 enum krb5_plugin_type type
;
62 static HEIMDAL_MUTEX plugin_mutex
= HEIMDAL_MUTEX_INITIALIZER
;
63 static struct plugin
*registered
= NULL
;
66 * Register a plugin symbol name of specific type.
67 * @param context a Keberos context
68 * @param type type of plugin symbol
69 * @param name name of plugin symbol
70 * @param symbol a pointer to the named symbol
71 * @return In case of error a non zero error com_err error is returned
72 * and the Kerberos error string is set.
74 * @ingroup krb5_support
77 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
78 krb5_plugin_register(krb5_context context
,
79 enum krb5_plugin_type type
,
85 HEIMDAL_MUTEX_lock(&plugin_mutex
);
87 /* check for duplicates */
88 for (e
= registered
; e
!= NULL
; e
= e
->next
) {
89 if (e
->type
== SYMBOL
&&
90 strcmp(e
->u
.symbol
.name
, name
) == 0 &&
91 e
->u
.symbol
.type
== type
&& e
->u
.symbol
.symbol
== symbol
) {
92 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
97 e
= calloc(1, sizeof(*e
));
99 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
100 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
104 e
->u
.symbol
.type
= type
;
105 e
->u
.symbol
.name
= strdup(name
);
106 if (e
->u
.symbol
.name
== NULL
) {
107 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
109 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
112 e
->u
.symbol
.symbol
= symbol
;
114 e
->next
= registered
;
116 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
121 static krb5_error_code
122 add_symbol(krb5_context context
, struct krb5_plugin
**list
, void *symbol
)
124 struct krb5_plugin
*e
;
126 e
= calloc(1, sizeof(*e
));
128 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
137 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
138 _krb5_plugin_find(krb5_context context
,
139 enum krb5_plugin_type type
,
141 struct krb5_plugin
**list
)
148 HEIMDAL_MUTEX_lock(&plugin_mutex
);
150 for (ret
= 0, e
= registered
; e
!= NULL
; e
= e
->next
) {
154 if (e
->u
.dso
.dsohandle
== NULL
)
156 sym
= dlsym(e
->u
.dso
.dsohandle
, name
);
158 ret
= add_symbol(context
, list
, sym
);
162 if (strcmp(e
->u
.symbol
.name
, name
) == 0 && e
->u
.symbol
.type
== type
)
163 ret
= add_symbol(context
, list
, e
->u
.symbol
.symbol
);
167 _krb5_plugin_free(*list
);
172 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
177 krb5_set_error_message(context
, ENOENT
, "Did not find a plugin for %s", name
);
184 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
185 _krb5_plugin_free(struct krb5_plugin
*list
)
187 struct krb5_plugin
*next
;
198 * array = { ptr, ctx }
204 static heim_dict_t modules
;
213 plug_dealloc(void *ptr
)
215 struct plugin2
*p
= ptr
;
216 heim_release(p
->path
);
217 heim_release(p
->names
);
219 dlclose(p
->dsohandle
);
223 resolve_origin(const char *di
)
231 if (strncmp(di
, "$ORIGIN/", sizeof("$ORIGIN/") - 1) &&
232 strcmp(di
, "$ORIGIN"))
236 return strdup(LIBDIR
"/plugin/krb5");
237 #else /* !HAVE_DLADDR */
238 di
+= sizeof("$ORIGIN") - 1;
240 if (dladdr(_krb5_load_plugins
, &dl_info
) == 0)
241 return strdup(LIBDIR
"/plugin/krb5");
243 dname
= dl_info
.dli_fname
;
245 p
= strrchr(dname
, '\\');
248 p
= strrchr(dname
, '/');
250 if (asprintf(&path
, "%.*s%s", (int) (p
- dname
), dname
, di
) == -1)
253 if (asprintf(&path
, "%s%s", dname
, di
) == -1)
258 #endif /* !HAVE_DLADDR */
263 * Load plugins (new system) for the given module @name (typically
264 * "krb5") from the given directory @paths.
268 * @context A krb5_context
269 * @name Name of plugin module (typically "krb5")
270 * @paths Array of directory paths where to look
272 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
273 _krb5_load_plugins(krb5_context context
, const char *name
, const char **paths
)
276 heim_string_t s
= heim_string_create(name
);
278 struct dirent
*entry
;
281 char *dirname
= NULL
;
284 const char * plugin_prefix
;
285 size_t plugin_prefix_len
;
287 if (asprintf(&plugin_prefix
, "plugin_%s_", name
) == -1)
289 plugin_prefix_len
= (plugin_prefix
? strlen(plugin_prefix
) : 0);
292 HEIMDAL_MUTEX_lock(&plugin_mutex
);
294 if (modules
== NULL
) {
295 modules
= heim_dict_create(11);
296 if (modules
== NULL
) {
297 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
302 module
= heim_dict_copy_value(modules
, s
);
303 if (module
== NULL
) {
304 module
= heim_dict_create(11);
305 if (module
== NULL
) {
306 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
310 heim_dict_set_value(modules
, s
, module
);
314 for (di
= paths
; *di
!= NULL
; di
++) {
316 dirname
= resolve_origin(*di
);
319 d
= opendir(dirname
);
324 while ((entry
= readdir(d
)) != NULL
) {
325 char *n
= entry
->d_name
;
331 if (n
[0] == '.' && (n
[1] == '\0' || (n
[1] == '.' && n
[2] == '\0')))
337 * On Windows, plugins must be loaded from the same directory as
338 * heimdal.dll (typically the assembly directory) and must have
339 * the name form "plugin_<module>_<name>.dll".
344 if (strnicmp(n
, plugin_prefix
, plugin_prefix_len
))
346 ext
= strrchr(n
, '.');
347 if (ext
== NULL
|| stricmp(ext
, ".dll"))
350 ret
= asprintf(&path
, "%s\\%s", dirname
, n
);
351 if (ret
< 0 || path
== NULL
)
356 { /* support loading bundles on MacOS */
357 size_t len
= strlen(n
);
358 if (len
> 7 && strcmp(&n
[len
- 7], ".bundle") == 0)
359 ret
= asprintf(&path
, "%s/%s/Contents/MacOS/%.*s", dirname
, n
, (int)(len
- 7), n
);
362 if (ret
< 0 || path
== NULL
)
363 ret
= asprintf(&path
, "%s/%s", dirname
, n
);
365 if (ret
< 0 || path
== NULL
)
368 spath
= heim_string_create(n
);
374 /* check if already cached */
375 p
= heim_dict_copy_value(module
, spath
);
377 p
= heim_alloc(sizeof(*p
), "krb5-plugin", plug_dealloc
);
379 p
->dsohandle
= dlopen(path
, RTLD_LOCAL
|RTLD_LAZY
);
381 if (p
&& p
->dsohandle
) {
382 p
->path
= heim_retain(spath
);
383 p
->names
= heim_dict_create(11);
384 heim_dict_set_value(module
, spath
, p
);
394 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
395 heim_release(module
);
400 #endif /* HAVE_DLOPEN */
404 * Unload plugins (new system)
406 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
407 _krb5_unload_plugins(krb5_context context
, const char *name
)
409 HEIMDAL_MUTEX_lock(&plugin_mutex
);
410 heim_release(modules
);
412 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
419 struct common_plugin_method
{
421 krb5_error_code (*init
)(krb5_context
, void **);
422 void (*fini
)(void *);
433 struct plug
*pl
= ptr
;
435 struct common_plugin_method
*cpm
= pl
->dataptr
;
441 krb5_context context
;
447 krb5_error_code (KRB5_LIB_CALL
*func
)(krb5_context
, const void *, void *, void *);
453 search_modules(heim_object_t key
, heim_object_t value
, void *ctx
)
455 struct iter_ctx
*s
= ctx
;
456 struct plugin2
*p
= value
;
457 struct plug
*pl
= heim_dict_copy_value(p
->names
, s
->n
);
458 struct common_plugin_method
*cpm
;
461 if (p
->dsohandle
== NULL
)
464 pl
= heim_alloc(sizeof(*pl
), "struct-plug", plug_free
);
466 cpm
= pl
->dataptr
= dlsym(p
->dsohandle
, s
->name
);
470 ret
= cpm
->init(s
->context
, &pl
->ctx
);
472 cpm
= pl
->dataptr
= NULL
;
474 heim_dict_set_value(p
->names
, s
->n
, pl
);
479 if (cpm
&& cpm
->version
>= s
->min_version
)
480 heim_array_append_value(s
->result
, pl
);
485 eval_results(heim_object_t value
, void *ctx
, int *stop
)
487 struct plug
*pl
= value
;
488 struct iter_ctx
*s
= ctx
;
490 if (s
->ret
!= KRB5_PLUGIN_NO_HANDLE
)
493 s
->ret
= s
->func(s
->context
, pl
->dataptr
, pl
->ctx
, s
->userctx
);
494 if (s
->ret
!= KRB5_PLUGIN_NO_HANDLE
495 && !(s
->flags
& KRB5_PLUGIN_INVOKE_ALL
))
500 * Run plugins for the given @module (e.g., "krb5") and @name (e.g.,
501 * "kuserok"). Specifically, the @func is invoked once per-plugin with
502 * four arguments: the @context, the plugin symbol value (a pointer to a
503 * struct whose first three fields are the same as struct common_plugin_method),
504 * a context value produced by the plugin's init method, and @userctx.
506 * @func should unpack arguments for a plugin function and invoke it
507 * with arguments taken from @userctx. @func should save plugin
508 * outputs, if any, in @userctx.
510 * All loaded and registered plugins are invoked via @func until @func
511 * returns something other than KRB5_PLUGIN_NO_HANDLE. Plugins that
512 * have nothing to do for the given arguments should return
513 * KRB5_PLUGIN_NO_HANDLE.
517 * @context A krb5_context
518 * @module Name of module (typically "krb5")
519 * @name Name of pluggable interface (e.g., "kuserok")
520 * @min_version Lowest acceptable plugin minor version number
521 * @flags Flags (none defined at this time)
522 * @userctx Callback data for the callback function @func
523 * @func A callback function, invoked once per-plugin
525 * Outputs: None, other than the return value and such outputs as are
528 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
529 _krb5_plugin_run_f(krb5_context context
,
535 krb5_error_code (KRB5_LIB_CALL
*func
)(krb5_context
, const void *, void *, void *))
537 heim_string_t m
= heim_string_create(module
);
540 struct common_plugin_method
*cpm
;
542 struct krb5_plugin
*registered_plugins
= NULL
;
543 struct krb5_plugin
*p
;
545 /* Get registered plugins */
546 (void) _krb5_plugin_find(context
, SYMBOL
, name
, ®istered_plugins
);
548 HEIMDAL_MUTEX_lock(&plugin_mutex
);
552 s
.n
= heim_string_create(name
);
554 s
.min_version
= min_version
;
555 s
.result
= heim_array_create();
558 s
.ret
= KRB5_PLUGIN_NO_HANDLE
;
560 /* Get loaded plugins */
561 dict
= heim_dict_copy_value(modules
, m
);
564 /* Add loaded plugins to s.result array */
566 heim_dict_iterate_f(dict
, &s
, search_modules
);
568 /* We don't need to hold plugin_mutex during plugin invocation */
569 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
571 /* Invoke registered plugins (old system) */
572 for (p
= registered_plugins
; p
; p
= p
->next
) {
574 * XXX This is the wrong way to handle registered plugins, as we
575 * call init/fini on each invocation! We do this because we
576 * have nowhere in the struct plugin registered list to store
577 * the context allocated by the plugin's init function. (But at
578 * least we do call init/fini!)
580 * What we should do is adapt the old plugin system to the new
581 * one and change how we register plugins so that we use the new
582 * struct plug to keep track of their context structures, that
583 * way we can init once, invoke many times, then fini.
585 cpm
= (struct common_plugin_method
*)p
->symbol
;
586 s
.ret
= cpm
->init(context
, &plug_ctx
);
589 s
.ret
= s
.func(s
.context
, p
->symbol
, plug_ctx
, s
.userctx
);
591 if (s
.ret
!= KRB5_PLUGIN_NO_HANDLE
&&
592 !(flags
& KRB5_PLUGIN_INVOKE_ALL
))
595 _krb5_plugin_free(registered_plugins
);
597 /* Invoke loaded plugins (new system) */
598 if (s
.ret
== KRB5_PLUGIN_NO_HANDLE
)
599 heim_array_iterate_f(s
.result
, &s
, eval_results
);
601 heim_release(s
.result
);