catch error from as.*printf
[heimdal.git] / lib / krb5 / plugin.c
blobe19ba4a27cc3baedea591b7fcf48b9212fbdb29a
1 /*
2 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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
31 * SUCH DAMAGE.
34 #include "krb5_locl.h"
36 #ifdef HAVE_DLFCN_H
37 #include <dlfcn.h>
38 #endif
39 #include <dirent.h>
41 struct krb5_plugin {
42 void *symbol;
43 struct krb5_plugin *next;
46 struct plugin {
47 enum { DSO, SYMBOL } type;
48 union {
49 struct {
50 char *path;
51 void *dsohandle;
52 } dso;
53 struct {
54 enum krb5_plugin_type type;
55 char *name;
56 char *symbol;
57 } symbol;
58 } u;
59 struct plugin *next;
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",
68 #ifdef __APPLE__
69 "/System/Library/KerberosPlugins/KerberosFrameworkPlugins",
70 #endif
71 NULL
78 void *
79 _krb5_plugin_get_symbol(struct krb5_plugin *p)
81 return p->symbol;
84 struct krb5_plugin *
85 _krb5_plugin_get_next(struct krb5_plugin *p)
87 return p->next;
94 #ifdef HAVE_DLOPEN
96 static krb5_error_code
97 loadlib(krb5_context context, char *path)
99 struct plugin *e;
101 e = calloc(1, sizeof(*e));
102 if (e == NULL) {
103 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
104 free(path);
105 return ENOMEM;
108 #ifndef RTLD_LAZY
109 #define RTLD_LAZY 0
110 #endif
111 #ifndef RTLD_LOCAL
112 #define RTLD_LOCAL 0
113 #endif
114 e->type = DSO;
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;
120 registered = e;
122 return 0;
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_error_code
139 krb5_plugin_register(krb5_context context,
140 enum krb5_plugin_type type,
141 const char *name,
142 void *symbol)
144 struct plugin *e;
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);
154 return 0;
158 e = calloc(1, sizeof(*e));
159 if (e == NULL) {
160 HEIMDAL_MUTEX_unlock(&plugin_mutex);
161 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
162 return ENOMEM;
164 e->type = SYMBOL;
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);
169 free(e);
170 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
171 return ENOMEM;
173 e->u.symbol.symbol = symbol;
175 e->next = registered;
176 registered = e;
177 HEIMDAL_MUTEX_unlock(&plugin_mutex);
179 return 0;
182 static krb5_error_code
183 load_plugins(krb5_context context)
185 struct plugin *e;
186 krb5_error_code ret;
187 char **dirs = NULL, **di;
188 struct dirent *entry;
189 char *path;
190 DIR *d = NULL;
192 if (!plugins_needs_scan)
193 return 0;
194 plugins_needs_scan = 0;
196 #ifdef HAVE_DLOPEN
198 dirs = krb5_config_get_strings(context, NULL, "libdefaults",
199 "plugin_dir", NULL);
200 if (dirs == NULL)
201 dirs = rk_UNCONST(sysplugin_dirs);
203 for (di = dirs; *di != NULL; di++) {
204 #ifdef KRB5_USE_PATH_TOKENS
206 char * dir = NULL;
208 if (_krb5_expand_path_tokens(context, *di, &dir))
209 continue;
210 d = opendir(dir);
212 free(dir);
214 #else
215 d = opendir(*di);
216 #endif
217 if (d == NULL)
218 continue;
219 rk_cloexec_dir(d);
221 while ((entry = readdir(d)) != NULL) {
222 char *n = entry->d_name;
224 /* skip . and .. */
225 if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')))
226 continue;
228 path = NULL;
229 ret = 0;
230 #ifdef __APPLE__
231 { /* support loading bundles on MacOS */
232 size_t len = strlen(n);
233 if (len > 7 && strcmp(&n[len - 7], ".bundle") == 0)
234 ret = asprintf(&path, "%s/%s/Contents/MacOS/%.*s", *di, n, (int)(len - 7), n);
236 #endif
237 if (ret < 0 || path == NULL)
238 ret = asprintf(&path, "%s/%s", *di, n);
240 if (ret < 0 || path == NULL) {
241 ret = ENOMEM;
242 krb5_set_error_message(context, ret, "malloc: out of memory");
243 return ret;
246 /* check if already tried */
247 for (e = registered; e != NULL; e = e->next)
248 if (e->type == DSO && strcmp(e->u.dso.path, path) == 0)
249 break;
250 if (e) {
251 free(path);
252 } else {
253 loadlib(context, path); /* store or frees path */
256 closedir(d);
258 if (dirs != rk_UNCONST(sysplugin_dirs))
259 krb5_config_free_strings(dirs);
260 #endif /* HAVE_DLOPEN */
261 return 0;
264 static krb5_error_code
265 add_symbol(krb5_context context, struct krb5_plugin **list, void *symbol)
267 struct krb5_plugin *e;
269 e = calloc(1, sizeof(*e));
270 if (e == NULL) {
271 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
272 return ENOMEM;
274 e->symbol = symbol;
275 e->next = *list;
276 *list = e;
277 return 0;
280 krb5_error_code
281 _krb5_plugin_find(krb5_context context,
282 enum krb5_plugin_type type,
283 const char *name,
284 struct krb5_plugin **list)
286 struct plugin *e;
287 krb5_error_code ret;
289 *list = NULL;
291 HEIMDAL_MUTEX_lock(&plugin_mutex);
293 load_plugins(context);
295 for (ret = 0, e = registered; e != NULL; e = e->next) {
296 switch(e->type) {
297 case DSO: {
298 void *sym;
299 if (e->u.dso.dsohandle == NULL)
300 continue;
301 sym = dlsym(e->u.dso.dsohandle, name);
302 if (sym)
303 ret = add_symbol(context, list, sym);
304 break;
306 case SYMBOL:
307 if (strcmp(e->u.symbol.name, name) == 0 && e->u.symbol.type == type)
308 ret = add_symbol(context, list, e->u.symbol.symbol);
309 break;
311 if (ret) {
312 _krb5_plugin_free(*list);
313 *list = NULL;
317 HEIMDAL_MUTEX_unlock(&plugin_mutex);
318 if (ret)
319 return ret;
321 if (*list == NULL) {
322 krb5_set_error_message(context, ENOENT, "Did not find a plugin for %s", name);
323 return ENOENT;
326 return 0;
329 void
330 _krb5_plugin_free(struct krb5_plugin *list)
332 struct krb5_plugin *next;
333 while (list) {
334 next = list->next;
335 free(list);
336 list = next;