1 /* $NetBSD: plugin.c,v 1.1.1.2 2014/04/24 12:45:51 pettai Exp $ */
4 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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.
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.
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
36 #include "krb5_locl.h"
45 struct krb5_plugin
*next
;
49 enum { DSO
, SYMBOL
} type
;
56 enum krb5_plugin_type type
;
64 static HEIMDAL_MUTEX plugin_mutex
= HEIMDAL_MUTEX_INITIALIZER
;
65 static struct plugin
*registered
= NULL
;
66 static int plugins_needs_scan
= 1;
68 static const char *sysplugin_dirs
[] = {
69 LIBDIR
"/plugin/krb5",
71 "/System/Library/KerberosPlugins/KerberosFrameworkPlugins",
81 _krb5_plugin_get_symbol(struct krb5_plugin
*p
)
87 _krb5_plugin_get_next(struct krb5_plugin
*p
)
98 static krb5_error_code
99 loadlib(krb5_context context
, char *path
)
103 e
= calloc(1, sizeof(*e
));
105 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
117 /* ignore error from dlopen, and just keep it as negative cache entry */
118 e
->u
.dso
.dsohandle
= dlopen(path
, RTLD_LOCAL
|RTLD_LAZY
);
119 e
->u
.dso
.path
= path
;
121 e
->next
= registered
;
126 #endif /* HAVE_DLOPEN */
129 * Register a plugin symbol name of specific type.
130 * @param context a Keberos context
131 * @param type type of plugin symbol
132 * @param name name of plugin symbol
133 * @param symbol a pointer to the named symbol
134 * @return In case of error a non zero error com_err error is returned
135 * and the Kerberos error string is set.
137 * @ingroup krb5_support
140 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
141 krb5_plugin_register(krb5_context context
,
142 enum krb5_plugin_type type
,
148 HEIMDAL_MUTEX_lock(&plugin_mutex
);
150 /* check for duplicates */
151 for (e
= registered
; e
!= NULL
; e
= e
->next
) {
152 if (e
->type
== SYMBOL
&&
153 strcmp(e
->u
.symbol
.name
, name
) == 0 &&
154 e
->u
.symbol
.type
== type
&& e
->u
.symbol
.symbol
== symbol
) {
155 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
160 e
= calloc(1, sizeof(*e
));
162 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
163 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
167 e
->u
.symbol
.type
= type
;
168 e
->u
.symbol
.name
= strdup(name
);
169 if (e
->u
.symbol
.name
== NULL
) {
170 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
172 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
175 e
->u
.symbol
.symbol
= symbol
;
177 e
->next
= registered
;
179 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
185 is_valid_plugin_filename(const char * n
)
187 if (n
[0] == '.' && (n
[1] == '\0' || (n
[1] == '.' && n
[2] == '\0')))
191 /* On Windows, we only attempt to load .dll files as plug-ins. */
195 ext
= strrchr(n
, '.');
199 return !stricmp(ext
, ".dll");
207 trim_trailing_slash(char * path
)
212 while (l
> 0 && (path
[l
- 1] == '/'
213 #ifdef BACKSLASH_PATH_DELIM
214 || path
[l
- 1] == '\\'
221 static krb5_error_code
222 load_plugins(krb5_context context
)
226 char **dirs
= NULL
, **di
;
227 struct dirent
*entry
;
231 if (!plugins_needs_scan
)
233 plugins_needs_scan
= 0;
237 dirs
= krb5_config_get_strings(context
, NULL
, "libdefaults",
240 dirs
= rk_UNCONST(sysplugin_dirs
);
242 for (di
= dirs
; *di
!= NULL
; di
++) {
245 #ifdef KRB5_USE_PATH_TOKENS
246 if (_krb5_expand_path_tokens(context
, *di
, &dir
))
250 trim_trailing_slash(dir
);
259 while ((entry
= readdir(d
)) != NULL
) {
260 char *n
= entry
->d_name
;
263 if (!is_valid_plugin_filename(n
))
269 { /* support loading bundles on MacOS */
270 size_t len
= strlen(n
);
271 if (len
> 7 && strcmp(&n
[len
- 7], ".bundle") == 0)
272 ret
= asprintf(&path
, "%s/%s/Contents/MacOS/%.*s", dir
, n
, (int)(len
- 7), n
);
275 if (ret
< 0 || path
== NULL
)
276 ret
= asprintf(&path
, "%s/%s", dir
, n
);
278 if (ret
< 0 || path
== NULL
) {
280 krb5_set_error_message(context
, ret
, "malloc: out of memory");
284 /* check if already tried */
285 for (e
= registered
; e
!= NULL
; e
= e
->next
)
286 if (e
->type
== DSO
&& strcmp(e
->u
.dso
.path
, path
) == 0)
291 loadlib(context
, path
); /* store or frees path */
300 if (dirs
!= rk_UNCONST(sysplugin_dirs
))
301 krb5_config_free_strings(dirs
);
302 #endif /* HAVE_DLOPEN */
306 static krb5_error_code
307 add_symbol(krb5_context context
, struct krb5_plugin
**list
, void *symbol
)
309 struct krb5_plugin
*e
;
311 e
= calloc(1, sizeof(*e
));
313 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
323 _krb5_plugin_find(krb5_context context
,
324 enum krb5_plugin_type type
,
326 struct krb5_plugin
**list
)
333 HEIMDAL_MUTEX_lock(&plugin_mutex
);
335 load_plugins(context
);
337 for (ret
= 0, e
= registered
; e
!= NULL
; e
= e
->next
) {
341 if (e
->u
.dso
.dsohandle
== NULL
)
343 sym
= dlsym(e
->u
.dso
.dsohandle
, name
);
345 ret
= add_symbol(context
, list
, sym
);
349 if (strcmp(e
->u
.symbol
.name
, name
) == 0 && e
->u
.symbol
.type
== type
)
350 ret
= add_symbol(context
, list
, e
->u
.symbol
.symbol
);
354 _krb5_plugin_free(*list
);
359 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
364 krb5_set_error_message(context
, ENOENT
, "Did not find a plugin for %s", name
);
372 _krb5_plugin_free(struct krb5_plugin
*list
)
374 struct krb5_plugin
*next
;
385 * array = { ptr, ctx }
391 static heim_dict_t modules
;
400 plug_dealloc(void *ptr
)
402 struct plugin2
*p
= ptr
;
403 heim_release(p
->path
);
404 heim_release(p
->names
);
406 dlclose(p
->dsohandle
);
411 _krb5_load_plugins(krb5_context context
, const char *name
, const char **paths
)
414 heim_string_t s
= heim_string_create(name
);
416 struct dirent
*entry
;
421 HEIMDAL_MUTEX_lock(&plugin_mutex
);
423 if (modules
== NULL
) {
424 modules
= heim_dict_create(11);
425 if (modules
== NULL
) {
426 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
431 module
= heim_dict_copy_value(modules
, s
);
432 if (module
== NULL
) {
433 module
= heim_dict_create(11);
434 if (module
== NULL
) {
435 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
439 heim_dict_add_value(modules
, s
, module
);
443 for (di
= paths
; *di
!= NULL
; di
++) {
449 while ((entry
= readdir(d
)) != NULL
) {
450 char *n
= entry
->d_name
;
456 if (n
[0] == '.' && (n
[1] == '\0' || (n
[1] == '.' && n
[2] == '\0')))
461 { /* support loading bundles on MacOS */
462 size_t len
= strlen(n
);
463 if (len
> 7 && strcmp(&n
[len
- 7], ".bundle") == 0)
464 ret
= asprintf(&path
, "%s/%s/Contents/MacOS/%.*s", *di
, n
, (int)(len
- 7), n
);
467 if (ret
< 0 || path
== NULL
)
468 ret
= asprintf(&path
, "%s/%s", *di
, n
);
470 if (ret
< 0 || path
== NULL
)
473 spath
= heim_string_create(n
);
479 /* check if already cached */
480 p
= heim_dict_copy_value(module
, spath
);
482 p
= heim_alloc(sizeof(*p
), "krb5-plugin", plug_dealloc
);
484 p
->dsohandle
= dlopen(path
, RTLD_LOCAL
|RTLD_LAZY
);
487 p
->path
= heim_retain(spath
);
488 p
->names
= heim_dict_create(11);
489 heim_dict_add_value(module
, spath
, p
);
498 heim_release(module
);
499 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
500 #endif /* HAVE_DLOPEN */
504 _krb5_unload_plugins(krb5_context context
, const char *name
)
506 HEIMDAL_MUTEX_lock(&plugin_mutex
);
507 heim_release(modules
);
509 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
516 struct common_plugin_method
{
518 krb5_error_code (*init
)(krb5_context
, void **);
519 void (*fini
)(void *);
530 struct plug
*pl
= ptr
;
532 struct common_plugin_method
*cpm
= pl
->dataptr
;
538 krb5_context context
;
543 krb5_error_code (*func
)(krb5_context
, const void *, void *, void *);
549 search_modules(void *ctx
, heim_object_t key
, heim_object_t value
)
551 struct iter_ctx
*s
= ctx
;
552 struct plugin2
*p
= value
;
553 struct plug
*pl
= heim_dict_copy_value(p
->names
, s
->n
);
554 struct common_plugin_method
*cpm
;
557 if (p
->dsohandle
== NULL
)
560 pl
= heim_alloc(sizeof(*pl
), "struct-plug", plug_free
);
562 cpm
= pl
->dataptr
= dlsym(p
->dsohandle
, s
->name
);
566 ret
= cpm
->init(s
->context
, &pl
->ctx
);
568 cpm
= pl
->dataptr
= NULL
;
570 heim_dict_add_value(p
->names
, s
->n
, pl
);
575 if (cpm
&& cpm
->version
>= s
->min_version
)
576 heim_array_append_value(s
->result
, pl
);
582 eval_results(heim_object_t value
, void *ctx
)
584 struct plug
*pl
= value
;
585 struct iter_ctx
*s
= ctx
;
587 if (s
->ret
!= KRB5_PLUGIN_NO_HANDLE
)
590 s
->ret
= s
->func(s
->context
, pl
->dataptr
, pl
->ctx
, s
->userctx
);
594 _krb5_plugin_run_f(krb5_context context
,
600 krb5_error_code (*func
)(krb5_context
, const void *, void *, void *))
602 heim_string_t m
= heim_string_create(module
);
606 HEIMDAL_MUTEX_lock(&plugin_mutex
);
608 dict
= heim_dict_copy_value(modules
, m
);
611 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
612 return KRB5_PLUGIN_NO_HANDLE
;
617 s
.n
= heim_string_create(name
);
618 s
.min_version
= min_version
;
619 s
.result
= heim_array_create();
623 heim_dict_iterate_f(dict
, search_modules
, &s
);
627 HEIMDAL_MUTEX_unlock(&plugin_mutex
);
629 s
.ret
= KRB5_PLUGIN_NO_HANDLE
;
631 heim_array_iterate_f(s
.result
, eval_results
, &s
);
633 heim_release(s
.result
);