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