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
;
64 static int plugins_needs_scan
= 1;
66 static const char *sysplugin_dirs
[] = {
67 LIBDIR
"/plugin/krb5",
69 "/System/Library/KerberosPlugins/KerberosFrameworkPlugins",
79 _krb5_plugin_get_symbol(struct krb5_plugin
*p
)
85 _krb5_plugin_get_next(struct krb5_plugin
*p
)
96 static krb5_error_code
97 loadlib(krb5_context context
, char *path
)
101 e
= calloc(1, sizeof(*e
));
103 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
115 /* ignore error from dlopen, and just keep it as negative cache entry */
116 e
->u
.dso
.dsohandle
= dlopen(path
, RTLD_LOCAL
|RTLD_LAZY
);
117 e
->u
.dso
.path
= path
;
119 e
->next
= registered
;
124 #endif /* HAVE_DLOPEN */
127 * Register a plugin symbol name of specific type.
128 * @param context a Keberos context
129 * @param type type of plugin symbol
130 * @param name name of plugin symbol
131 * @param symbol a pointer to the named symbol
132 * @return In case of error a non zero error com_err error is returned
133 * and the Kerberos error string is set.
135 * @ingroup krb5_support
138 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
139 krb5_plugin_register(krb5_context context
,
140 enum krb5_plugin_type type
,
146 HEIMDAL_MUTEX_lock(&plugin_mutex
);
148 /* check for duplicates */
149 for (e
= registered
; e
!= NULL
; e
= e
->next
) {
150 if (e
->type
== SYMBOL
&&
151 strcmp(e
->u
.symbol
.name
, name
) == 0 &&
152 e
->u
.symbol
.type
== type
&& e
->u
.symbol
.symbol
== symbol
) {
153 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
158 e
= calloc(1, sizeof(*e
));
160 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
161 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
165 e
->u
.symbol
.type
= type
;
166 e
->u
.symbol
.name
= strdup(name
);
167 if (e
->u
.symbol
.name
== NULL
) {
168 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
170 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
173 e
->u
.symbol
.symbol
= symbol
;
175 e
->next
= registered
;
177 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
183 is_valid_plugin_filename(const char * n
)
185 if (n
[0] == '.' && (n
[1] == '\0' || (n
[1] == '.' && n
[2] == '\0')))
189 /* On Windows, we only attempt to load .dll files as plug-ins. */
193 ext
= strrchr(n
, '.');
197 return !stricmp(ext
, ".dll");
205 trim_trailing_slash(char * path
)
210 while (l
> 0 && (path
[l
- 1] == '/'
211 #ifdef BACKSLASH_PATH_DELIM
212 || path
[l
- 1] == '\\'
219 static krb5_error_code
220 load_plugins(krb5_context context
)
224 char **dirs
= NULL
, **di
;
225 struct dirent
*entry
;
229 if (!plugins_needs_scan
)
231 plugins_needs_scan
= 0;
235 dirs
= krb5_config_get_strings(context
, NULL
, "libdefaults",
238 dirs
= rk_UNCONST(sysplugin_dirs
);
240 for (di
= dirs
; *di
!= NULL
; di
++) {
243 #ifdef KRB5_USE_PATH_TOKENS
244 if (_krb5_expand_path_tokens(context
, *di
, &dir
))
248 trim_trailing_slash(dir
);
257 while ((entry
= readdir(d
)) != NULL
) {
258 char *n
= entry
->d_name
;
261 if (!is_valid_plugin_filename(n
))
267 { /* support loading bundles on MacOS */
268 size_t len
= strlen(n
);
269 if (len
> 7 && strcmp(&n
[len
- 7], ".bundle") == 0)
270 ret
= asprintf(&path
, "%s/%s/Contents/MacOS/%.*s", dir
, n
, (int)(len
- 7), n
);
273 if (ret
< 0 || path
== NULL
)
274 ret
= asprintf(&path
, "%s/%s", dir
, n
);
276 if (ret
< 0 || path
== NULL
) {
278 krb5_set_error_message(context
, ret
, "malloc: out of memory");
282 /* check if already tried */
283 for (e
= registered
; e
!= NULL
; e
= e
->next
)
284 if (e
->type
== DSO
&& strcmp(e
->u
.dso
.path
, path
) == 0)
289 loadlib(context
, path
); /* store or frees path */
298 if (dirs
!= rk_UNCONST(sysplugin_dirs
))
299 krb5_config_free_strings(dirs
);
300 #endif /* HAVE_DLOPEN */
304 static krb5_error_code
305 add_symbol(krb5_context context
, struct krb5_plugin
**list
, void *symbol
)
307 struct krb5_plugin
*e
;
309 e
= calloc(1, sizeof(*e
));
311 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
321 _krb5_plugin_find(krb5_context context
,
322 enum krb5_plugin_type type
,
324 struct krb5_plugin
**list
)
331 HEIMDAL_MUTEX_lock(&plugin_mutex
);
333 load_plugins(context
);
335 for (ret
= 0, e
= registered
; e
!= NULL
; e
= e
->next
) {
339 if (e
->u
.dso
.dsohandle
== NULL
)
341 sym
= dlsym(e
->u
.dso
.dsohandle
, name
);
343 ret
= add_symbol(context
, list
, sym
);
347 if (strcmp(e
->u
.symbol
.name
, name
) == 0 && e
->u
.symbol
.type
== type
)
348 ret
= add_symbol(context
, list
, e
->u
.symbol
.symbol
);
352 _krb5_plugin_free(*list
);
357 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
362 krb5_set_error_message(context
, ENOENT
, "Did not find a plugin for %s", name
);
370 _krb5_plugin_free(struct krb5_plugin
*list
)
372 struct krb5_plugin
*next
;
383 * array = { ptr, ctx }
389 static heim_dict_t modules
;
398 plug_dealloc(void *ptr
)
400 struct plugin2
*p
= ptr
;
401 heim_release(p
->path
);
402 heim_release(p
->names
);
404 dlclose(p
->dsohandle
);
409 _krb5_load_plugins(krb5_context context
, const char *name
, const char **paths
)
412 heim_string_t s
= heim_string_create(name
);
414 struct dirent
*entry
;
419 HEIMDAL_MUTEX_lock(&plugin_mutex
);
421 if (modules
== NULL
) {
422 modules
= heim_dict_create(11);
423 if (modules
== NULL
) {
424 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
429 module
= heim_dict_copy_value(modules
, s
);
430 if (module
== NULL
) {
431 module
= heim_dict_create(11);
432 if (module
== NULL
) {
433 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
437 heim_dict_add_value(modules
, s
, module
);
441 for (di
= paths
; *di
!= NULL
; di
++) {
447 while ((entry
= readdir(d
)) != NULL
) {
448 char *n
= entry
->d_name
;
454 if (n
[0] == '.' && (n
[1] == '\0' || (n
[1] == '.' && n
[2] == '\0')))
459 { /* support loading bundles on MacOS */
460 size_t len
= strlen(n
);
461 if (len
> 7 && strcmp(&n
[len
- 7], ".bundle") == 0)
462 ret
= asprintf(&path
, "%s/%s/Contents/MacOS/%.*s", *di
, n
, (int)(len
- 7), n
);
465 if (ret
< 0 || path
== NULL
)
466 ret
= asprintf(&path
, "%s/%s", *di
, n
);
468 if (ret
< 0 || path
== NULL
)
471 spath
= heim_string_create(n
);
477 /* check if already cached */
478 p
= heim_dict_copy_value(module
, spath
);
480 p
= heim_alloc(sizeof(*p
), "krb5-plugin", plug_dealloc
);
482 p
->dsohandle
= dlopen(path
, RTLD_LOCAL
|RTLD_LAZY
);
485 p
->path
= heim_retain(spath
);
486 p
->names
= heim_dict_create(11);
487 heim_dict_add_value(module
, spath
, p
);
496 heim_release(module
);
497 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
498 #endif /* HAVE_DLOPEN */
502 _krb5_unload_plugins(krb5_context context
, const char *name
)
504 HEIMDAL_MUTEX_lock(&plugin_mutex
);
505 heim_release(modules
);
507 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
514 struct common_plugin_method
{
516 krb5_error_code (*init
)(krb5_context
, void **);
517 void (*fini
)(void *);
528 struct plug
*pl
= ptr
;
530 struct common_plugin_method
*cpm
= pl
->dataptr
;
536 krb5_context context
;
541 krb5_error_code (*func
)(krb5_context
, const void *, void *, void *);
547 search_modules(void *ctx
, heim_object_t key
, heim_object_t value
)
549 struct iter_ctx
*s
= ctx
;
550 struct plugin2
*p
= value
;
551 struct plug
*pl
= heim_dict_copy_value(p
->names
, s
->n
);
552 struct common_plugin_method
*cpm
;
555 if (p
->dsohandle
== NULL
)
558 pl
= heim_alloc(sizeof(*pl
), "struct-plug", plug_free
);
560 cpm
= pl
->dataptr
= dlsym(p
->dsohandle
, s
->name
);
564 ret
= cpm
->init(s
->context
, &pl
->ctx
);
566 cpm
= pl
->dataptr
= NULL
;
568 heim_dict_add_value(p
->names
, s
->n
, pl
);
573 if (cpm
&& cpm
->version
>= s
->min_version
)
574 heim_array_append_value(s
->result
, pl
);
580 eval_results(heim_object_t value
, void *ctx
)
582 struct plug
*pl
= value
;
583 struct iter_ctx
*s
= ctx
;
585 if (s
->ret
!= KRB5_PLUGIN_NO_HANDLE
)
588 s
->ret
= s
->func(s
->context
, pl
->dataptr
, pl
->ctx
, s
->userctx
);
592 _krb5_plugin_run_f(krb5_context context
,
598 krb5_error_code (*func
)(krb5_context
, const void *, void *, void *))
600 heim_string_t m
= heim_string_create(module
);
604 HEIMDAL_MUTEX_lock(&plugin_mutex
);
606 dict
= heim_dict_copy_value(modules
, m
);
609 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
610 return KRB5_PLUGIN_NO_HANDLE
;
615 s
.n
= heim_string_create(name
);
616 s
.min_version
= min_version
;
617 s
.result
= heim_array_create();
621 heim_dict_iterate_f(dict
, search_modules
, &s
);
625 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
627 s
.ret
= KRB5_PLUGIN_NO_HANDLE
;
629 heim_array_iterate_f(s
.result
, eval_results
, &s
);
631 heim_release(s
.result
);