xref: /freebsd/crypto/heimdal/lib/krb5/plugin.c (revision 6a068746777241722b2b32c5d0bc443a2a64d80b)
1c19800e8SDoug Rabson /*
2*ae771770SStanislav Sedov  * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
3c19800e8SDoug Rabson  * (Royal Institute of Technology, Stockholm, Sweden).
4c19800e8SDoug Rabson  * All rights reserved.
5c19800e8SDoug Rabson  *
6c19800e8SDoug Rabson  * Redistribution and use in source and binary forms, with or without
7c19800e8SDoug Rabson  * modification, are permitted provided that the following conditions
8c19800e8SDoug Rabson  * are met:
9c19800e8SDoug Rabson  *
10c19800e8SDoug Rabson  * 1. Redistributions of source code must retain the above copyright
11c19800e8SDoug Rabson  *    notice, this list of conditions and the following disclaimer.
12c19800e8SDoug Rabson  *
13c19800e8SDoug Rabson  * 2. Redistributions in binary form must reproduce the above copyright
14c19800e8SDoug Rabson  *    notice, this list of conditions and the following disclaimer in the
15c19800e8SDoug Rabson  *    documentation and/or other materials provided with the distribution.
16c19800e8SDoug Rabson  *
17c19800e8SDoug Rabson  * 3. Neither the name of the Institute nor the names of its contributors
18c19800e8SDoug Rabson  *    may be used to endorse or promote products derived from this software
19c19800e8SDoug Rabson  *    without specific prior written permission.
20c19800e8SDoug Rabson  *
21c19800e8SDoug Rabson  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22c19800e8SDoug Rabson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23c19800e8SDoug Rabson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24c19800e8SDoug Rabson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25c19800e8SDoug Rabson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26c19800e8SDoug Rabson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27c19800e8SDoug Rabson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28c19800e8SDoug Rabson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29c19800e8SDoug Rabson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30c19800e8SDoug Rabson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31c19800e8SDoug Rabson  * SUCH DAMAGE.
32c19800e8SDoug Rabson  */
33c19800e8SDoug Rabson 
34c19800e8SDoug Rabson #include "krb5_locl.h"
35*ae771770SStanislav Sedov 
36c19800e8SDoug Rabson #ifdef HAVE_DLFCN_H
37c19800e8SDoug Rabson #include <dlfcn.h>
38c19800e8SDoug Rabson #endif
39c19800e8SDoug Rabson #include <dirent.h>
40c19800e8SDoug Rabson 
41c19800e8SDoug Rabson struct krb5_plugin {
42c19800e8SDoug Rabson     void *symbol;
43c19800e8SDoug Rabson     struct krb5_plugin *next;
44c19800e8SDoug Rabson };
45c19800e8SDoug Rabson 
46c19800e8SDoug Rabson struct plugin {
47*ae771770SStanislav Sedov     enum { DSO, SYMBOL } type;
48*ae771770SStanislav Sedov     union {
49*ae771770SStanislav Sedov 	struct {
50*ae771770SStanislav Sedov 	    char *path;
51*ae771770SStanislav Sedov 	    void *dsohandle;
52*ae771770SStanislav Sedov 	} dso;
53*ae771770SStanislav Sedov 	struct {
54c19800e8SDoug Rabson 	    enum krb5_plugin_type type;
55*ae771770SStanislav Sedov 	    char *name;
56*ae771770SStanislav Sedov 	    char *symbol;
57*ae771770SStanislav Sedov 	} symbol;
58*ae771770SStanislav Sedov     } u;
59c19800e8SDoug Rabson     struct plugin *next;
60c19800e8SDoug Rabson };
61c19800e8SDoug Rabson 
62c19800e8SDoug Rabson static HEIMDAL_MUTEX plugin_mutex = HEIMDAL_MUTEX_INITIALIZER;
63c19800e8SDoug Rabson static struct plugin *registered = NULL;
64*ae771770SStanislav Sedov static int plugins_needs_scan = 1;
65c19800e8SDoug Rabson 
66*ae771770SStanislav Sedov static const char *sysplugin_dirs[] =  {
67*ae771770SStanislav Sedov     LIBDIR "/plugin/krb5",
68*ae771770SStanislav Sedov #ifdef __APPLE__
69*ae771770SStanislav Sedov     "/System/Library/KerberosPlugins/KerberosFrameworkPlugins",
70*ae771770SStanislav Sedov #endif
71*ae771770SStanislav Sedov     NULL
72*ae771770SStanislav Sedov };
73c19800e8SDoug Rabson 
74c19800e8SDoug Rabson /*
75c19800e8SDoug Rabson  *
76c19800e8SDoug Rabson  */
77c19800e8SDoug Rabson 
78c19800e8SDoug Rabson void *
_krb5_plugin_get_symbol(struct krb5_plugin * p)79c19800e8SDoug Rabson _krb5_plugin_get_symbol(struct krb5_plugin *p)
80c19800e8SDoug Rabson {
81c19800e8SDoug Rabson     return p->symbol;
82c19800e8SDoug Rabson }
83c19800e8SDoug Rabson 
84c19800e8SDoug Rabson struct krb5_plugin *
_krb5_plugin_get_next(struct krb5_plugin * p)85c19800e8SDoug Rabson _krb5_plugin_get_next(struct krb5_plugin *p)
86c19800e8SDoug Rabson {
87c19800e8SDoug Rabson     return p->next;
88c19800e8SDoug Rabson }
89c19800e8SDoug Rabson 
90c19800e8SDoug Rabson /*
91c19800e8SDoug Rabson  *
92c19800e8SDoug Rabson  */
93c19800e8SDoug Rabson 
94c19800e8SDoug Rabson #ifdef HAVE_DLOPEN
95c19800e8SDoug Rabson 
96c19800e8SDoug Rabson static krb5_error_code
loadlib(krb5_context context,char * path)97*ae771770SStanislav Sedov loadlib(krb5_context context, char *path)
98c19800e8SDoug Rabson {
99*ae771770SStanislav Sedov     struct plugin *e;
100*ae771770SStanislav Sedov 
101*ae771770SStanislav Sedov     e = calloc(1, sizeof(*e));
102*ae771770SStanislav Sedov     if (e == NULL) {
103*ae771770SStanislav Sedov 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
104*ae771770SStanislav Sedov 	free(path);
105c19800e8SDoug Rabson 	return ENOMEM;
106c19800e8SDoug Rabson     }
107c19800e8SDoug Rabson 
108c19800e8SDoug Rabson #ifndef RTLD_LAZY
109c19800e8SDoug Rabson #define RTLD_LAZY 0
110c19800e8SDoug Rabson #endif
111*ae771770SStanislav Sedov #ifndef RTLD_LOCAL
112*ae771770SStanislav Sedov #define RTLD_LOCAL 0
113*ae771770SStanislav Sedov #endif
114*ae771770SStanislav Sedov     e->type = DSO;
115*ae771770SStanislav Sedov     /* ignore error from dlopen, and just keep it as negative cache entry */
116*ae771770SStanislav Sedov     e->u.dso.dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY);
117*ae771770SStanislav Sedov     e->u.dso.path = path;
118c19800e8SDoug Rabson 
119*ae771770SStanislav Sedov     e->next = registered;
120*ae771770SStanislav Sedov     registered = e;
121c19800e8SDoug Rabson 
122c19800e8SDoug Rabson     return 0;
123c19800e8SDoug Rabson }
124c19800e8SDoug Rabson #endif /* HAVE_DLOPEN */
125c19800e8SDoug Rabson 
126c19800e8SDoug Rabson /**
127c19800e8SDoug Rabson  * Register a plugin symbol name of specific type.
128c19800e8SDoug Rabson  * @param context a Keberos context
129c19800e8SDoug Rabson  * @param type type of plugin symbol
130c19800e8SDoug Rabson  * @param name name of plugin symbol
131c19800e8SDoug Rabson  * @param symbol a pointer to the named symbol
132c19800e8SDoug Rabson  * @return In case of error a non zero error com_err error is returned
133c19800e8SDoug Rabson  * and the Kerberos error string is set.
134c19800e8SDoug Rabson  *
135c19800e8SDoug Rabson  * @ingroup krb5_support
136c19800e8SDoug Rabson  */
137c19800e8SDoug Rabson 
138*ae771770SStanislav Sedov KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_plugin_register(krb5_context context,enum krb5_plugin_type type,const char * name,void * symbol)139c19800e8SDoug Rabson krb5_plugin_register(krb5_context context,
140c19800e8SDoug Rabson 		     enum krb5_plugin_type type,
141c19800e8SDoug Rabson 		     const char *name,
142c19800e8SDoug Rabson 		     void *symbol)
143c19800e8SDoug Rabson {
144c19800e8SDoug Rabson     struct plugin *e;
145c19800e8SDoug Rabson 
146*ae771770SStanislav Sedov     HEIMDAL_MUTEX_lock(&plugin_mutex);
147*ae771770SStanislav Sedov 
148*ae771770SStanislav Sedov     /* check for duplicates */
149*ae771770SStanislav Sedov     for (e = registered; e != NULL; e = e->next) {
150*ae771770SStanislav Sedov 	if (e->type == SYMBOL &&
151*ae771770SStanislav Sedov 	    strcmp(e->u.symbol.name, name) == 0 &&
152*ae771770SStanislav Sedov 	    e->u.symbol.type == type && e->u.symbol.symbol == symbol) {
153*ae771770SStanislav Sedov 	    HEIMDAL_MUTEX_unlock(&plugin_mutex);
154*ae771770SStanislav Sedov 	    return 0;
155*ae771770SStanislav Sedov 	}
156*ae771770SStanislav Sedov     }
157*ae771770SStanislav Sedov 
158c19800e8SDoug Rabson     e = calloc(1, sizeof(*e));
159c19800e8SDoug Rabson     if (e == NULL) {
160*ae771770SStanislav Sedov 	HEIMDAL_MUTEX_unlock(&plugin_mutex);
161*ae771770SStanislav Sedov 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
162c19800e8SDoug Rabson 	return ENOMEM;
163c19800e8SDoug Rabson     }
164*ae771770SStanislav Sedov     e->type = SYMBOL;
165*ae771770SStanislav Sedov     e->u.symbol.type = type;
166*ae771770SStanislav Sedov     e->u.symbol.name = strdup(name);
167*ae771770SStanislav Sedov     if (e->u.symbol.name == NULL) {
168*ae771770SStanislav Sedov 	HEIMDAL_MUTEX_unlock(&plugin_mutex);
169c19800e8SDoug Rabson 	free(e);
170*ae771770SStanislav Sedov 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
171c19800e8SDoug Rabson 	return ENOMEM;
172c19800e8SDoug Rabson     }
173*ae771770SStanislav Sedov     e->u.symbol.symbol = symbol;
174c19800e8SDoug Rabson 
175c19800e8SDoug Rabson     e->next = registered;
176c19800e8SDoug Rabson     registered = e;
177c19800e8SDoug Rabson     HEIMDAL_MUTEX_unlock(&plugin_mutex);
178c19800e8SDoug Rabson 
179c19800e8SDoug Rabson     return 0;
180c19800e8SDoug Rabson }
181c19800e8SDoug Rabson 
182*ae771770SStanislav Sedov static int
is_valid_plugin_filename(const char * n)183*ae771770SStanislav Sedov is_valid_plugin_filename(const char * n)
184*ae771770SStanislav Sedov {
185*ae771770SStanislav Sedov     if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')))
186*ae771770SStanislav Sedov         return 0;
187*ae771770SStanislav Sedov 
188*ae771770SStanislav Sedov #ifdef _WIN32
189*ae771770SStanislav Sedov     /* On Windows, we only attempt to load .dll files as plug-ins. */
190*ae771770SStanislav Sedov     {
191*ae771770SStanislav Sedov         const char * ext;
192*ae771770SStanislav Sedov 
193*ae771770SStanislav Sedov         ext = strrchr(n, '.');
194*ae771770SStanislav Sedov         if (ext == NULL)
195*ae771770SStanislav Sedov             return 0;
196*ae771770SStanislav Sedov 
197*ae771770SStanislav Sedov         return !stricmp(ext, ".dll");
198*ae771770SStanislav Sedov     }
199*ae771770SStanislav Sedov #else
200*ae771770SStanislav Sedov     return 1;
201*ae771770SStanislav Sedov #endif
202*ae771770SStanislav Sedov }
203*ae771770SStanislav Sedov 
204*ae771770SStanislav Sedov static void
trim_trailing_slash(char * path)205*ae771770SStanislav Sedov trim_trailing_slash(char * path)
206*ae771770SStanislav Sedov {
207*ae771770SStanislav Sedov     size_t l;
208*ae771770SStanislav Sedov 
209*ae771770SStanislav Sedov     l = strlen(path);
210*ae771770SStanislav Sedov     while (l > 0 && (path[l - 1] == '/'
211*ae771770SStanislav Sedov #ifdef BACKSLASH_PATH_DELIM
212*ae771770SStanislav Sedov                      || path[l - 1] == '\\'
213*ae771770SStanislav Sedov #endif
214*ae771770SStanislav Sedov                )) {
215*ae771770SStanislav Sedov         path[--l] = '\0';
216*ae771770SStanislav Sedov     }
217*ae771770SStanislav Sedov }
218*ae771770SStanislav Sedov 
219*ae771770SStanislav Sedov static krb5_error_code
load_plugins(krb5_context context)220*ae771770SStanislav Sedov load_plugins(krb5_context context)
221*ae771770SStanislav Sedov {
222*ae771770SStanislav Sedov     struct plugin *e;
223*ae771770SStanislav Sedov     krb5_error_code ret;
224*ae771770SStanislav Sedov     char **dirs = NULL, **di;
225*ae771770SStanislav Sedov     struct dirent *entry;
226*ae771770SStanislav Sedov     char *path;
227*ae771770SStanislav Sedov     DIR *d = NULL;
228*ae771770SStanislav Sedov 
229*ae771770SStanislav Sedov     if (!plugins_needs_scan)
230*ae771770SStanislav Sedov 	return 0;
231*ae771770SStanislav Sedov     plugins_needs_scan = 0;
232*ae771770SStanislav Sedov 
233*ae771770SStanislav Sedov #ifdef HAVE_DLOPEN
234*ae771770SStanislav Sedov 
235*ae771770SStanislav Sedov     dirs = krb5_config_get_strings(context, NULL, "libdefaults",
236*ae771770SStanislav Sedov 				   "plugin_dir", NULL);
237*ae771770SStanislav Sedov     if (dirs == NULL)
238*ae771770SStanislav Sedov 	dirs = rk_UNCONST(sysplugin_dirs);
239*ae771770SStanislav Sedov 
240*ae771770SStanislav Sedov     for (di = dirs; *di != NULL; di++) {
241*ae771770SStanislav Sedov         char * dir = *di;
242*ae771770SStanislav Sedov 
243*ae771770SStanislav Sedov #ifdef KRB5_USE_PATH_TOKENS
244*ae771770SStanislav Sedov         if (_krb5_expand_path_tokens(context, *di, &dir))
245*ae771770SStanislav Sedov             goto next_dir;
246*ae771770SStanislav Sedov #endif
247*ae771770SStanislav Sedov 
248*ae771770SStanislav Sedov         trim_trailing_slash(dir);
249*ae771770SStanislav Sedov 
250*ae771770SStanislav Sedov         d = opendir(dir);
251*ae771770SStanislav Sedov 
252*ae771770SStanislav Sedov 	if (d == NULL)
253*ae771770SStanislav Sedov 	    goto next_dir;
254*ae771770SStanislav Sedov 
255*ae771770SStanislav Sedov 	rk_cloexec_dir(d);
256*ae771770SStanislav Sedov 
257*ae771770SStanislav Sedov 	while ((entry = readdir(d)) != NULL) {
258*ae771770SStanislav Sedov 	    char *n = entry->d_name;
259*ae771770SStanislav Sedov 
260*ae771770SStanislav Sedov 	    /* skip . and .. */
261*ae771770SStanislav Sedov             if (!is_valid_plugin_filename(n))
262*ae771770SStanislav Sedov 		continue;
263*ae771770SStanislav Sedov 
264*ae771770SStanislav Sedov 	    path = NULL;
265*ae771770SStanislav Sedov 	    ret = 0;
266*ae771770SStanislav Sedov #ifdef __APPLE__
267*ae771770SStanislav Sedov 	    { /* support loading bundles on MacOS */
268*ae771770SStanislav Sedov 		size_t len = strlen(n);
269*ae771770SStanislav Sedov 		if (len > 7 && strcmp(&n[len - 7],  ".bundle") == 0)
270*ae771770SStanislav Sedov 		    ret = asprintf(&path, "%s/%s/Contents/MacOS/%.*s", dir, n, (int)(len - 7), n);
271*ae771770SStanislav Sedov 	    }
272*ae771770SStanislav Sedov #endif
273*ae771770SStanislav Sedov 	    if (ret < 0 || path == NULL)
274*ae771770SStanislav Sedov 		ret = asprintf(&path, "%s/%s", dir, n);
275*ae771770SStanislav Sedov 
276*ae771770SStanislav Sedov 	    if (ret < 0 || path == NULL) {
277*ae771770SStanislav Sedov 		ret = ENOMEM;
278*ae771770SStanislav Sedov 		krb5_set_error_message(context, ret, "malloc: out of memory");
279*ae771770SStanislav Sedov 		return ret;
280*ae771770SStanislav Sedov 	    }
281*ae771770SStanislav Sedov 
282*ae771770SStanislav Sedov 	    /* check if already tried */
283*ae771770SStanislav Sedov 	    for (e = registered; e != NULL; e = e->next)
284*ae771770SStanislav Sedov 		if (e->type == DSO && strcmp(e->u.dso.path, path) == 0)
285*ae771770SStanislav Sedov 		    break;
286*ae771770SStanislav Sedov 	    if (e) {
287*ae771770SStanislav Sedov 		free(path);
288*ae771770SStanislav Sedov 	    } else {
289*ae771770SStanislav Sedov 		loadlib(context, path); /* store or frees path */
290*ae771770SStanislav Sedov 	    }
291*ae771770SStanislav Sedov 	}
292*ae771770SStanislav Sedov 	closedir(d);
293*ae771770SStanislav Sedov 
294*ae771770SStanislav Sedov     next_dir:
295*ae771770SStanislav Sedov         if (dir != *di)
296*ae771770SStanislav Sedov             free(dir);
297*ae771770SStanislav Sedov     }
298*ae771770SStanislav Sedov     if (dirs != rk_UNCONST(sysplugin_dirs))
299*ae771770SStanislav Sedov 	krb5_config_free_strings(dirs);
300*ae771770SStanislav Sedov #endif /* HAVE_DLOPEN */
301*ae771770SStanislav Sedov     return 0;
302*ae771770SStanislav Sedov }
303*ae771770SStanislav Sedov 
304*ae771770SStanislav Sedov static krb5_error_code
add_symbol(krb5_context context,struct krb5_plugin ** list,void * symbol)305*ae771770SStanislav Sedov add_symbol(krb5_context context, struct krb5_plugin **list, void *symbol)
306*ae771770SStanislav Sedov {
307*ae771770SStanislav Sedov     struct krb5_plugin *e;
308*ae771770SStanislav Sedov 
309*ae771770SStanislav Sedov     e = calloc(1, sizeof(*e));
310*ae771770SStanislav Sedov     if (e == NULL) {
311*ae771770SStanislav Sedov 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
312*ae771770SStanislav Sedov 	return ENOMEM;
313*ae771770SStanislav Sedov     }
314*ae771770SStanislav Sedov     e->symbol = symbol;
315*ae771770SStanislav Sedov     e->next = *list;
316*ae771770SStanislav Sedov     *list = e;
317*ae771770SStanislav Sedov     return 0;
318*ae771770SStanislav Sedov }
319*ae771770SStanislav Sedov 
320c19800e8SDoug Rabson krb5_error_code
_krb5_plugin_find(krb5_context context,enum krb5_plugin_type type,const char * name,struct krb5_plugin ** list)321c19800e8SDoug Rabson _krb5_plugin_find(krb5_context context,
322c19800e8SDoug Rabson 		  enum krb5_plugin_type type,
323c19800e8SDoug Rabson 		  const char *name,
324c19800e8SDoug Rabson 		  struct krb5_plugin **list)
325c19800e8SDoug Rabson {
326*ae771770SStanislav Sedov     struct plugin *e;
327c19800e8SDoug Rabson     krb5_error_code ret;
328c19800e8SDoug Rabson 
329c19800e8SDoug Rabson     *list = NULL;
330c19800e8SDoug Rabson 
331c19800e8SDoug Rabson     HEIMDAL_MUTEX_lock(&plugin_mutex);
332c19800e8SDoug Rabson 
333*ae771770SStanislav Sedov     load_plugins(context);
334*ae771770SStanislav Sedov 
335*ae771770SStanislav Sedov     for (ret = 0, e = registered; e != NULL; e = e->next) {
336*ae771770SStanislav Sedov 	switch(e->type) {
337*ae771770SStanislav Sedov 	case DSO: {
338*ae771770SStanislav Sedov 	    void *sym;
339*ae771770SStanislav Sedov 	    if (e->u.dso.dsohandle == NULL)
340c19800e8SDoug Rabson 		continue;
341*ae771770SStanislav Sedov 	    sym = dlsym(e->u.dso.dsohandle, name);
342*ae771770SStanislav Sedov 	    if (sym)
343*ae771770SStanislav Sedov 		ret = add_symbol(context, list, sym);
344*ae771770SStanislav Sedov 	    break;
345*ae771770SStanislav Sedov 	}
346*ae771770SStanislav Sedov 	case SYMBOL:
347*ae771770SStanislav Sedov 	    if (strcmp(e->u.symbol.name, name) == 0 && e->u.symbol.type == type)
348*ae771770SStanislav Sedov 		ret = add_symbol(context, list, e->u.symbol.symbol);
349*ae771770SStanislav Sedov 	    break;
350*ae771770SStanislav Sedov 	}
351*ae771770SStanislav Sedov 	if (ret) {
352*ae771770SStanislav Sedov 	    _krb5_plugin_free(*list);
353*ae771770SStanislav Sedov 	    *list = NULL;
354*ae771770SStanislav Sedov 	}
355*ae771770SStanislav Sedov     }
356c19800e8SDoug Rabson 
357c19800e8SDoug Rabson     HEIMDAL_MUTEX_unlock(&plugin_mutex);
358c19800e8SDoug Rabson     if (ret)
359*ae771770SStanislav Sedov 	return ret;
360c19800e8SDoug Rabson 
361c19800e8SDoug Rabson     if (*list == NULL) {
362*ae771770SStanislav Sedov 	krb5_set_error_message(context, ENOENT, "Did not find a plugin for %s", name);
363c19800e8SDoug Rabson 	return ENOENT;
364c19800e8SDoug Rabson     }
365c19800e8SDoug Rabson 
366c19800e8SDoug Rabson     return 0;
367c19800e8SDoug Rabson }
368c19800e8SDoug Rabson 
369c19800e8SDoug Rabson void
_krb5_plugin_free(struct krb5_plugin * list)370c19800e8SDoug Rabson _krb5_plugin_free(struct krb5_plugin *list)
371c19800e8SDoug Rabson {
372c19800e8SDoug Rabson     struct krb5_plugin *next;
373c19800e8SDoug Rabson     while (list) {
374c19800e8SDoug Rabson 	next = list->next;
375c19800e8SDoug Rabson 	free(list);
376c19800e8SDoug Rabson 	list = next;
377c19800e8SDoug Rabson     }
378c19800e8SDoug Rabson }
379*ae771770SStanislav Sedov /*
380*ae771770SStanislav Sedov  * module - dict of {
381*ae771770SStanislav Sedov  *      ModuleName = [
382*ae771770SStanislav Sedov  *          plugin = object{
383*ae771770SStanislav Sedov  *              array = { ptr, ctx }
384*ae771770SStanislav Sedov  *          }
385*ae771770SStanislav Sedov  *      ]
386*ae771770SStanislav Sedov  * }
387*ae771770SStanislav Sedov  */
388*ae771770SStanislav Sedov 
389*ae771770SStanislav Sedov static heim_dict_t modules;
390*ae771770SStanislav Sedov 
391*ae771770SStanislav Sedov struct plugin2 {
392*ae771770SStanislav Sedov     heim_string_t path;
393*ae771770SStanislav Sedov     void *dsohandle;
394*ae771770SStanislav Sedov     heim_dict_t names;
395*ae771770SStanislav Sedov };
396*ae771770SStanislav Sedov 
397*ae771770SStanislav Sedov static void
plug_dealloc(void * ptr)398*ae771770SStanislav Sedov plug_dealloc(void *ptr)
399*ae771770SStanislav Sedov {
400*ae771770SStanislav Sedov     struct plugin2 *p = ptr;
401*ae771770SStanislav Sedov     heim_release(p->path);
402*ae771770SStanislav Sedov     heim_release(p->names);
403*ae771770SStanislav Sedov     if (p->dsohandle)
404*ae771770SStanislav Sedov 	dlclose(p->dsohandle);
405*ae771770SStanislav Sedov }
406*ae771770SStanislav Sedov 
407*ae771770SStanislav Sedov 
408*ae771770SStanislav Sedov void
_krb5_load_plugins(krb5_context context,const char * name,const char ** paths)409*ae771770SStanislav Sedov _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
410*ae771770SStanislav Sedov {
411*ae771770SStanislav Sedov #ifdef HAVE_DLOPEN
412*ae771770SStanislav Sedov     heim_string_t s = heim_string_create(name);
413*ae771770SStanislav Sedov     heim_dict_t module;
414*ae771770SStanislav Sedov     struct dirent *entry;
415*ae771770SStanislav Sedov     krb5_error_code ret;
416*ae771770SStanislav Sedov     const char **di;
417*ae771770SStanislav Sedov     DIR *d;
418*ae771770SStanislav Sedov 
419*ae771770SStanislav Sedov     HEIMDAL_MUTEX_lock(&plugin_mutex);
420*ae771770SStanislav Sedov 
421*ae771770SStanislav Sedov     if (modules == NULL) {
422*ae771770SStanislav Sedov 	modules = heim_dict_create(11);
423*ae771770SStanislav Sedov 	if (modules == NULL) {
424*ae771770SStanislav Sedov 	    HEIMDAL_MUTEX_unlock(&plugin_mutex);
425*ae771770SStanislav Sedov 	    return;
426*ae771770SStanislav Sedov 	}
427*ae771770SStanislav Sedov     }
428*ae771770SStanislav Sedov 
429*ae771770SStanislav Sedov     module = heim_dict_copy_value(modules, s);
430*ae771770SStanislav Sedov     if (module == NULL) {
431*ae771770SStanislav Sedov 	module = heim_dict_create(11);
432*ae771770SStanislav Sedov 	if (module == NULL) {
433*ae771770SStanislav Sedov 	    HEIMDAL_MUTEX_unlock(&plugin_mutex);
434*ae771770SStanislav Sedov 	    heim_release(s);
435*ae771770SStanislav Sedov 	    return;
436*ae771770SStanislav Sedov 	}
437*ae771770SStanislav Sedov 	heim_dict_add_value(modules, s, module);
438*ae771770SStanislav Sedov     }
439*ae771770SStanislav Sedov     heim_release(s);
440*ae771770SStanislav Sedov 
441*ae771770SStanislav Sedov     for (di = paths; *di != NULL; di++) {
442*ae771770SStanislav Sedov 	d = opendir(*di);
443*ae771770SStanislav Sedov 	if (d == NULL)
444*ae771770SStanislav Sedov 	    continue;
445*ae771770SStanislav Sedov 	rk_cloexec_dir(d);
446*ae771770SStanislav Sedov 
447*ae771770SStanislav Sedov 	while ((entry = readdir(d)) != NULL) {
448*ae771770SStanislav Sedov 	    char *n = entry->d_name;
449*ae771770SStanislav Sedov 	    char *path = NULL;
450*ae771770SStanislav Sedov 	    heim_string_t spath;
451*ae771770SStanislav Sedov 	    struct plugin2 *p;
452*ae771770SStanislav Sedov 
453*ae771770SStanislav Sedov 	    /* skip . and .. */
454*ae771770SStanislav Sedov 	    if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')))
455*ae771770SStanislav Sedov 		continue;
456*ae771770SStanislav Sedov 
457*ae771770SStanislav Sedov 	    ret = 0;
458*ae771770SStanislav Sedov #ifdef __APPLE__
459*ae771770SStanislav Sedov 	    { /* support loading bundles on MacOS */
460*ae771770SStanislav Sedov 		size_t len = strlen(n);
461*ae771770SStanislav Sedov 		if (len > 7 && strcmp(&n[len - 7],  ".bundle") == 0)
462*ae771770SStanislav Sedov 		    ret = asprintf(&path, "%s/%s/Contents/MacOS/%.*s", *di, n, (int)(len - 7), n);
463*ae771770SStanislav Sedov 	    }
464*ae771770SStanislav Sedov #endif
465*ae771770SStanislav Sedov 	    if (ret < 0 || path == NULL)
466*ae771770SStanislav Sedov 		ret = asprintf(&path, "%s/%s", *di, n);
467*ae771770SStanislav Sedov 
468*ae771770SStanislav Sedov 	    if (ret < 0 || path == NULL)
469*ae771770SStanislav Sedov 		continue;
470*ae771770SStanislav Sedov 
471*ae771770SStanislav Sedov 	    spath = heim_string_create(n);
472*ae771770SStanislav Sedov 	    if (spath == NULL) {
473*ae771770SStanislav Sedov 		free(path);
474*ae771770SStanislav Sedov 		continue;
475*ae771770SStanislav Sedov 	    }
476*ae771770SStanislav Sedov 
477*ae771770SStanislav Sedov 	    /* check if already cached */
478*ae771770SStanislav Sedov 	    p = heim_dict_copy_value(module, spath);
479*ae771770SStanislav Sedov 	    if (p == NULL) {
480*ae771770SStanislav Sedov 		p = heim_alloc(sizeof(*p), "krb5-plugin", plug_dealloc);
481*ae771770SStanislav Sedov 		if (p)
482*ae771770SStanislav Sedov 		    p->dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY);
483*ae771770SStanislav Sedov 
484*ae771770SStanislav Sedov 		if (p->dsohandle) {
485*ae771770SStanislav Sedov 		    p->path = heim_retain(spath);
486*ae771770SStanislav Sedov 		    p->names = heim_dict_create(11);
487*ae771770SStanislav Sedov 		    heim_dict_add_value(module, spath, p);
488*ae771770SStanislav Sedov 		}
489*ae771770SStanislav Sedov 	    }
490*ae771770SStanislav Sedov 	    heim_release(spath);
491*ae771770SStanislav Sedov 	    heim_release(p);
492*ae771770SStanislav Sedov 	    free(path);
493*ae771770SStanislav Sedov 	}
494*ae771770SStanislav Sedov 	closedir(d);
495*ae771770SStanislav Sedov     }
496*ae771770SStanislav Sedov     heim_release(module);
497*ae771770SStanislav Sedov     HEIMDAL_MUTEX_unlock(&plugin_mutex);
498*ae771770SStanislav Sedov #endif /* HAVE_DLOPEN */
499*ae771770SStanislav Sedov }
500*ae771770SStanislav Sedov 
501*ae771770SStanislav Sedov void
_krb5_unload_plugins(krb5_context context,const char * name)502*ae771770SStanislav Sedov _krb5_unload_plugins(krb5_context context, const char *name)
503*ae771770SStanislav Sedov {
504*ae771770SStanislav Sedov     HEIMDAL_MUTEX_lock(&plugin_mutex);
505*ae771770SStanislav Sedov     heim_release(modules);
506*ae771770SStanislav Sedov     modules = NULL;
507*ae771770SStanislav Sedov     HEIMDAL_MUTEX_unlock(&plugin_mutex);
508*ae771770SStanislav Sedov }
509*ae771770SStanislav Sedov 
510*ae771770SStanislav Sedov /*
511*ae771770SStanislav Sedov  *
512*ae771770SStanislav Sedov  */
513*ae771770SStanislav Sedov 
514*ae771770SStanislav Sedov struct common_plugin_method {
515*ae771770SStanislav Sedov     int			version;
516*ae771770SStanislav Sedov     krb5_error_code	(*init)(krb5_context, void **);
517*ae771770SStanislav Sedov     void		(*fini)(void *);
518*ae771770SStanislav Sedov };
519*ae771770SStanislav Sedov 
520*ae771770SStanislav Sedov struct plug {
521*ae771770SStanislav Sedov     void *dataptr;
522*ae771770SStanislav Sedov     void *ctx;
523*ae771770SStanislav Sedov };
524*ae771770SStanislav Sedov 
525*ae771770SStanislav Sedov static void
plug_free(void * ptr)526*ae771770SStanislav Sedov plug_free(void *ptr)
527*ae771770SStanislav Sedov {
528*ae771770SStanislav Sedov     struct plug *pl = ptr;
529*ae771770SStanislav Sedov     if (pl->dataptr) {
530*ae771770SStanislav Sedov 	struct common_plugin_method *cpm = pl->dataptr;
531*ae771770SStanislav Sedov 	cpm->fini(pl->ctx);
532*ae771770SStanislav Sedov     }
533*ae771770SStanislav Sedov }
534*ae771770SStanislav Sedov 
535*ae771770SStanislav Sedov struct iter_ctx {
536*ae771770SStanislav Sedov     krb5_context context;
537*ae771770SStanislav Sedov     heim_string_t n;
538*ae771770SStanislav Sedov     const char *name;
539*ae771770SStanislav Sedov     int min_version;
540*ae771770SStanislav Sedov     heim_array_t result;
541*ae771770SStanislav Sedov     krb5_error_code (*func)(krb5_context, const void *, void *, void *);
542*ae771770SStanislav Sedov     void *userctx;
543*ae771770SStanislav Sedov     krb5_error_code ret;
544*ae771770SStanislav Sedov };
545*ae771770SStanislav Sedov 
546*ae771770SStanislav Sedov static void
search_modules(void * ctx,heim_object_t key,heim_object_t value)547*ae771770SStanislav Sedov search_modules(void *ctx, heim_object_t key, heim_object_t value)
548*ae771770SStanislav Sedov {
549*ae771770SStanislav Sedov     struct iter_ctx *s = ctx;
550*ae771770SStanislav Sedov     struct plugin2 *p = value;
551*ae771770SStanislav Sedov     struct plug *pl = heim_dict_copy_value(p->names, s->n);
552*ae771770SStanislav Sedov     struct common_plugin_method *cpm;
553*ae771770SStanislav Sedov 
554*ae771770SStanislav Sedov     if (pl == NULL) {
555*ae771770SStanislav Sedov 	if (p->dsohandle == NULL)
556*ae771770SStanislav Sedov 	    return;
557*ae771770SStanislav Sedov 
558*ae771770SStanislav Sedov 	pl = heim_alloc(sizeof(*pl), "struct-plug", plug_free);
559*ae771770SStanislav Sedov 
560*ae771770SStanislav Sedov 	cpm = pl->dataptr = dlsym(p->dsohandle, s->name);
561*ae771770SStanislav Sedov 	if (cpm) {
562*ae771770SStanislav Sedov 	    int ret;
563*ae771770SStanislav Sedov 
564*ae771770SStanislav Sedov 	    ret = cpm->init(s->context, &pl->ctx);
565*ae771770SStanislav Sedov 	    if (ret)
566*ae771770SStanislav Sedov 		cpm = pl->dataptr = NULL;
567*ae771770SStanislav Sedov 	}
568*ae771770SStanislav Sedov 	heim_dict_add_value(p->names, s->n, pl);
569*ae771770SStanislav Sedov     } else {
570*ae771770SStanislav Sedov 	cpm = pl->dataptr;
571*ae771770SStanislav Sedov     }
572*ae771770SStanislav Sedov 
573*ae771770SStanislav Sedov     if (cpm && cpm->version >= s->min_version)
574*ae771770SStanislav Sedov 	heim_array_append_value(s->result, pl);
575*ae771770SStanislav Sedov 
576*ae771770SStanislav Sedov     heim_release(pl);
577*ae771770SStanislav Sedov }
578*ae771770SStanislav Sedov 
579*ae771770SStanislav Sedov static void
eval_results(heim_object_t value,void * ctx)580*ae771770SStanislav Sedov eval_results(heim_object_t value, void *ctx)
581*ae771770SStanislav Sedov {
582*ae771770SStanislav Sedov     struct plug *pl = value;
583*ae771770SStanislav Sedov     struct iter_ctx *s = ctx;
584*ae771770SStanislav Sedov 
585*ae771770SStanislav Sedov     if (s->ret != KRB5_PLUGIN_NO_HANDLE)
586*ae771770SStanislav Sedov 	return;
587*ae771770SStanislav Sedov 
588*ae771770SStanislav Sedov     s->ret = s->func(s->context, pl->dataptr, pl->ctx, s->userctx);
589*ae771770SStanislav Sedov }
590*ae771770SStanislav Sedov 
591*ae771770SStanislav Sedov krb5_error_code
_krb5_plugin_run_f(krb5_context context,const char * module,const char * name,int min_version,int flags,void * userctx,krb5_error_code (* func)(krb5_context,const void *,void *,void *))592*ae771770SStanislav Sedov _krb5_plugin_run_f(krb5_context context,
593*ae771770SStanislav Sedov 		   const char *module,
594*ae771770SStanislav Sedov 		   const char *name,
595*ae771770SStanislav Sedov 		   int min_version,
596*ae771770SStanislav Sedov 		   int flags,
597*ae771770SStanislav Sedov 		   void *userctx,
598*ae771770SStanislav Sedov 		   krb5_error_code (*func)(krb5_context, const void *, void *, void *))
599*ae771770SStanislav Sedov {
600*ae771770SStanislav Sedov     heim_string_t m = heim_string_create(module);
601*ae771770SStanislav Sedov     heim_dict_t dict;
602*ae771770SStanislav Sedov     struct iter_ctx s;
603*ae771770SStanislav Sedov 
604*ae771770SStanislav Sedov     HEIMDAL_MUTEX_lock(&plugin_mutex);
605*ae771770SStanislav Sedov 
606*ae771770SStanislav Sedov     dict = heim_dict_copy_value(modules, m);
607*ae771770SStanislav Sedov     heim_release(m);
608*ae771770SStanislav Sedov     if (dict == NULL) {
609*ae771770SStanislav Sedov 	HEIMDAL_MUTEX_unlock(&plugin_mutex);
610*ae771770SStanislav Sedov 	return KRB5_PLUGIN_NO_HANDLE;
611*ae771770SStanislav Sedov     }
612*ae771770SStanislav Sedov 
613*ae771770SStanislav Sedov     s.context = context;
614*ae771770SStanislav Sedov     s.name = name;
615*ae771770SStanislav Sedov     s.n = heim_string_create(name);
616*ae771770SStanislav Sedov     s.min_version = min_version;
617*ae771770SStanislav Sedov     s.result = heim_array_create();
618*ae771770SStanislav Sedov     s.func = func;
619*ae771770SStanislav Sedov     s.userctx = userctx;
620*ae771770SStanislav Sedov 
621*ae771770SStanislav Sedov     heim_dict_iterate_f(dict, search_modules, &s);
622*ae771770SStanislav Sedov 
623*ae771770SStanislav Sedov     heim_release(dict);
624*ae771770SStanislav Sedov 
625*ae771770SStanislav Sedov     HEIMDAL_MUTEX_unlock(&plugin_mutex);
626*ae771770SStanislav Sedov 
627*ae771770SStanislav Sedov     s.ret = KRB5_PLUGIN_NO_HANDLE;
628*ae771770SStanislav Sedov 
629*ae771770SStanislav Sedov     heim_array_iterate_f(s.result, eval_results, &s);
630*ae771770SStanislav Sedov 
631*ae771770SStanislav Sedov     heim_release(s.result);
632*ae771770SStanislav Sedov     heim_release(s.n);
633*ae771770SStanislav Sedov 
634*ae771770SStanislav Sedov     return s.ret;
635*ae771770SStanislav Sedov }
636