1178825Sdfr/*
2233294Sstas * Copyright (c) 2006 - 2007 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
5178825Sdfr *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
9178825Sdfr *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
12178825Sdfr *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
16178825Sdfr *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
20178825Sdfr *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
32178825Sdfr */
33178825Sdfr
34178825Sdfr#include "krb5_locl.h"
35233294Sstas
36178825Sdfr#ifdef HAVE_DLFCN_H
37178825Sdfr#include <dlfcn.h>
38178825Sdfr#endif
39178825Sdfr#include <dirent.h>
40178825Sdfr
41178825Sdfrstruct krb5_plugin {
42178825Sdfr    void *symbol;
43178825Sdfr    struct krb5_plugin *next;
44178825Sdfr};
45178825Sdfr
46178825Sdfrstruct plugin {
47233294Sstas    enum { DSO, SYMBOL } type;
48233294Sstas    union {
49233294Sstas	struct {
50233294Sstas	    char *path;
51233294Sstas	    void *dsohandle;
52233294Sstas	} dso;
53233294Sstas	struct {
54233294Sstas	    enum krb5_plugin_type type;
55233294Sstas	    char *name;
56233294Sstas	    char *symbol;
57233294Sstas	} symbol;
58233294Sstas    } u;
59178825Sdfr    struct plugin *next;
60178825Sdfr};
61178825Sdfr
62178825Sdfrstatic HEIMDAL_MUTEX plugin_mutex = HEIMDAL_MUTEX_INITIALIZER;
63178825Sdfrstatic struct plugin *registered = NULL;
64233294Sstasstatic int plugins_needs_scan = 1;
65178825Sdfr
66233294Sstasstatic const char *sysplugin_dirs[] =  {
67233294Sstas    LIBDIR "/plugin/krb5",
68233294Sstas#ifdef __APPLE__
69233294Sstas    "/System/Library/KerberosPlugins/KerberosFrameworkPlugins",
70233294Sstas#endif
71233294Sstas    NULL
72233294Sstas};
73178825Sdfr
74178825Sdfr/*
75178825Sdfr *
76178825Sdfr */
77178825Sdfr
78178825Sdfrvoid *
79178825Sdfr_krb5_plugin_get_symbol(struct krb5_plugin *p)
80178825Sdfr{
81178825Sdfr    return p->symbol;
82178825Sdfr}
83178825Sdfr
84178825Sdfrstruct krb5_plugin *
85178825Sdfr_krb5_plugin_get_next(struct krb5_plugin *p)
86178825Sdfr{
87178825Sdfr    return p->next;
88178825Sdfr}
89178825Sdfr
90178825Sdfr/*
91178825Sdfr *
92178825Sdfr */
93178825Sdfr
94178825Sdfr#ifdef HAVE_DLOPEN
95178825Sdfr
96178825Sdfrstatic krb5_error_code
97233294Sstasloadlib(krb5_context context, char *path)
98178825Sdfr{
99233294Sstas    struct plugin *e;
100233294Sstas
101233294Sstas    e = calloc(1, sizeof(*e));
102233294Sstas    if (e == NULL) {
103233294Sstas	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
104233294Sstas	free(path);
105178825Sdfr	return ENOMEM;
106178825Sdfr    }
107178825Sdfr
108178825Sdfr#ifndef RTLD_LAZY
109178825Sdfr#define RTLD_LAZY 0
110178825Sdfr#endif
111233294Sstas#ifndef RTLD_LOCAL
112233294Sstas#define RTLD_LOCAL 0
113233294Sstas#endif
114233294Sstas    e->type = DSO;
115233294Sstas    /* ignore error from dlopen, and just keep it as negative cache entry */
116233294Sstas    e->u.dso.dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY);
117233294Sstas    e->u.dso.path = path;
118178825Sdfr
119233294Sstas    e->next = registered;
120233294Sstas    registered = e;
121178825Sdfr
122178825Sdfr    return 0;
123178825Sdfr}
124178825Sdfr#endif /* HAVE_DLOPEN */
125178825Sdfr
126178825Sdfr/**
127178825Sdfr * Register a plugin symbol name of specific type.
128178825Sdfr * @param context a Keberos context
129178825Sdfr * @param type type of plugin symbol
130178825Sdfr * @param name name of plugin symbol
131178825Sdfr * @param symbol a pointer to the named symbol
132178825Sdfr * @return In case of error a non zero error com_err error is returned
133178825Sdfr * and the Kerberos error string is set.
134178825Sdfr *
135178825Sdfr * @ingroup krb5_support
136178825Sdfr */
137178825Sdfr
138233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
139178825Sdfrkrb5_plugin_register(krb5_context context,
140178825Sdfr		     enum krb5_plugin_type type,
141233294Sstas		     const char *name,
142178825Sdfr		     void *symbol)
143178825Sdfr{
144178825Sdfr    struct plugin *e;
145178825Sdfr
146233294Sstas    HEIMDAL_MUTEX_lock(&plugin_mutex);
147233294Sstas
148233294Sstas    /* check for duplicates */
149233294Sstas    for (e = registered; e != NULL; e = e->next) {
150233294Sstas	if (e->type == SYMBOL &&
151233294Sstas	    strcmp(e->u.symbol.name, name) == 0 &&
152233294Sstas	    e->u.symbol.type == type && e->u.symbol.symbol == symbol) {
153233294Sstas	    HEIMDAL_MUTEX_unlock(&plugin_mutex);
154233294Sstas	    return 0;
155233294Sstas	}
156233294Sstas    }
157233294Sstas
158178825Sdfr    e = calloc(1, sizeof(*e));
159178825Sdfr    if (e == NULL) {
160233294Sstas	HEIMDAL_MUTEX_unlock(&plugin_mutex);
161233294Sstas	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
162178825Sdfr	return ENOMEM;
163178825Sdfr    }
164233294Sstas    e->type = SYMBOL;
165233294Sstas    e->u.symbol.type = type;
166233294Sstas    e->u.symbol.name = strdup(name);
167233294Sstas    if (e->u.symbol.name == NULL) {
168233294Sstas	HEIMDAL_MUTEX_unlock(&plugin_mutex);
169178825Sdfr	free(e);
170233294Sstas	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
171178825Sdfr	return ENOMEM;
172178825Sdfr    }
173233294Sstas    e->u.symbol.symbol = symbol;
174178825Sdfr
175178825Sdfr    e->next = registered;
176178825Sdfr    registered = e;
177178825Sdfr    HEIMDAL_MUTEX_unlock(&plugin_mutex);
178178825Sdfr
179178825Sdfr    return 0;
180178825Sdfr}
181178825Sdfr
182233294Sstasstatic int
183233294Sstasis_valid_plugin_filename(const char * n)
184178825Sdfr{
185233294Sstas    if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')))
186233294Sstas        return 0;
187233294Sstas
188233294Sstas#ifdef _WIN32
189233294Sstas    /* On Windows, we only attempt to load .dll files as plug-ins. */
190233294Sstas    {
191233294Sstas        const char * ext;
192233294Sstas
193233294Sstas        ext = strrchr(n, '.');
194233294Sstas        if (ext == NULL)
195233294Sstas            return 0;
196233294Sstas
197233294Sstas        return !stricmp(ext, ".dll");
198233294Sstas    }
199233294Sstas#else
200233294Sstas    return 1;
201233294Sstas#endif
202233294Sstas}
203233294Sstas
204233294Sstasstatic void
205233294Sstastrim_trailing_slash(char * path)
206233294Sstas{
207233294Sstas    size_t l;
208233294Sstas
209233294Sstas    l = strlen(path);
210233294Sstas    while (l > 0 && (path[l - 1] == '/'
211233294Sstas#ifdef BACKSLASH_PATH_DELIM
212233294Sstas                     || path[l - 1] == '\\'
213233294Sstas#endif
214233294Sstas               )) {
215233294Sstas        path[--l] = '\0';
216233294Sstas    }
217233294Sstas}
218233294Sstas
219233294Sstasstatic krb5_error_code
220233294Sstasload_plugins(krb5_context context)
221233294Sstas{
222233294Sstas    struct plugin *e;
223178825Sdfr    krb5_error_code ret;
224178825Sdfr    char **dirs = NULL, **di;
225178825Sdfr    struct dirent *entry;
226178825Sdfr    char *path;
227178825Sdfr    DIR *d = NULL;
228178825Sdfr
229233294Sstas    if (!plugins_needs_scan)
230233294Sstas	return 0;
231233294Sstas    plugins_needs_scan = 0;
232178825Sdfr
233178825Sdfr#ifdef HAVE_DLOPEN
234178825Sdfr
235233294Sstas    dirs = krb5_config_get_strings(context, NULL, "libdefaults",
236178825Sdfr				   "plugin_dir", NULL);
237233294Sstas    if (dirs == NULL)
238233294Sstas	dirs = rk_UNCONST(sysplugin_dirs);
239178825Sdfr
240178825Sdfr    for (di = dirs; *di != NULL; di++) {
241233294Sstas        char * dir = *di;
242178825Sdfr
243233294Sstas#ifdef KRB5_USE_PATH_TOKENS
244233294Sstas        if (_krb5_expand_path_tokens(context, *di, &dir))
245233294Sstas            goto next_dir;
246233294Sstas#endif
247233294Sstas
248233294Sstas        trim_trailing_slash(dir);
249233294Sstas
250233294Sstas        d = opendir(dir);
251233294Sstas
252178825Sdfr	if (d == NULL)
253233294Sstas	    goto next_dir;
254178825Sdfr
255233294Sstas	rk_cloexec_dir(d);
256233294Sstas
257178825Sdfr	while ((entry = readdir(d)) != NULL) {
258233294Sstas	    char *n = entry->d_name;
259233294Sstas
260233294Sstas	    /* skip . and .. */
261233294Sstas            if (!is_valid_plugin_filename(n))
262233294Sstas		continue;
263233294Sstas
264233294Sstas	    path = NULL;
265233294Sstas	    ret = 0;
266233294Sstas#ifdef __APPLE__
267233294Sstas	    { /* support loading bundles on MacOS */
268233294Sstas		size_t len = strlen(n);
269233294Sstas		if (len > 7 && strcmp(&n[len - 7],  ".bundle") == 0)
270233294Sstas		    ret = asprintf(&path, "%s/%s/Contents/MacOS/%.*s", dir, n, (int)(len - 7), n);
271233294Sstas	    }
272233294Sstas#endif
273233294Sstas	    if (ret < 0 || path == NULL)
274233294Sstas		ret = asprintf(&path, "%s/%s", dir, n);
275233294Sstas
276233294Sstas	    if (ret < 0 || path == NULL) {
277178825Sdfr		ret = ENOMEM;
278233294Sstas		krb5_set_error_message(context, ret, "malloc: out of memory");
279233294Sstas		return ret;
280178825Sdfr	    }
281233294Sstas
282233294Sstas	    /* check if already tried */
283233294Sstas	    for (e = registered; e != NULL; e = e->next)
284233294Sstas		if (e->type == DSO && strcmp(e->u.dso.path, path) == 0)
285233294Sstas		    break;
286233294Sstas	    if (e) {
287233294Sstas		free(path);
288233294Sstas	    } else {
289233294Sstas		loadlib(context, path); /* store or frees path */
290233294Sstas	    }
291178825Sdfr	}
292178825Sdfr	closedir(d);
293233294Sstas
294233294Sstas    next_dir:
295233294Sstas        if (dir != *di)
296233294Sstas            free(dir);
297178825Sdfr    }
298233294Sstas    if (dirs != rk_UNCONST(sysplugin_dirs))
299178825Sdfr	krb5_config_free_strings(dirs);
300178825Sdfr#endif /* HAVE_DLOPEN */
301233294Sstas    return 0;
302233294Sstas}
303178825Sdfr
304233294Sstasstatic krb5_error_code
305233294Sstasadd_symbol(krb5_context context, struct krb5_plugin **list, void *symbol)
306233294Sstas{
307233294Sstas    struct krb5_plugin *e;
308233294Sstas
309233294Sstas    e = calloc(1, sizeof(*e));
310233294Sstas    if (e == NULL) {
311233294Sstas	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
312233294Sstas	return ENOMEM;
313233294Sstas    }
314233294Sstas    e->symbol = symbol;
315233294Sstas    e->next = *list;
316233294Sstas    *list = e;
317233294Sstas    return 0;
318233294Sstas}
319233294Sstas
320233294Sstaskrb5_error_code
321233294Sstas_krb5_plugin_find(krb5_context context,
322233294Sstas		  enum krb5_plugin_type type,
323233294Sstas		  const char *name,
324233294Sstas		  struct krb5_plugin **list)
325233294Sstas{
326233294Sstas    struct plugin *e;
327233294Sstas    krb5_error_code ret;
328233294Sstas
329233294Sstas    *list = NULL;
330233294Sstas
331233294Sstas    HEIMDAL_MUTEX_lock(&plugin_mutex);
332233294Sstas
333233294Sstas    load_plugins(context);
334233294Sstas
335233294Sstas    for (ret = 0, e = registered; e != NULL; e = e->next) {
336233294Sstas	switch(e->type) {
337233294Sstas	case DSO: {
338233294Sstas	    void *sym;
339233294Sstas	    if (e->u.dso.dsohandle == NULL)
340233294Sstas		continue;
341233294Sstas	    sym = dlsym(e->u.dso.dsohandle, name);
342233294Sstas	    if (sym)
343233294Sstas		ret = add_symbol(context, list, sym);
344233294Sstas	    break;
345233294Sstas	}
346233294Sstas	case SYMBOL:
347233294Sstas	    if (strcmp(e->u.symbol.name, name) == 0 && e->u.symbol.type == type)
348233294Sstas		ret = add_symbol(context, list, e->u.symbol.symbol);
349233294Sstas	    break;
350233294Sstas	}
351233294Sstas	if (ret) {
352233294Sstas	    _krb5_plugin_free(*list);
353233294Sstas	    *list = NULL;
354233294Sstas	}
355233294Sstas    }
356233294Sstas
357233294Sstas    HEIMDAL_MUTEX_unlock(&plugin_mutex);
358233294Sstas    if (ret)
359233294Sstas	return ret;
360233294Sstas
361178825Sdfr    if (*list == NULL) {
362233294Sstas	krb5_set_error_message(context, ENOENT, "Did not find a plugin for %s", name);
363178825Sdfr	return ENOENT;
364178825Sdfr    }
365178825Sdfr
366178825Sdfr    return 0;
367178825Sdfr}
368178825Sdfr
369178825Sdfrvoid
370178825Sdfr_krb5_plugin_free(struct krb5_plugin *list)
371178825Sdfr{
372178825Sdfr    struct krb5_plugin *next;
373178825Sdfr    while (list) {
374178825Sdfr	next = list->next;
375178825Sdfr	free(list);
376178825Sdfr	list = next;
377178825Sdfr    }
378178825Sdfr}
379233294Sstas/*
380233294Sstas * module - dict of {
381233294Sstas *      ModuleName = [
382233294Sstas *          plugin = object{
383233294Sstas *              array = { ptr, ctx }
384233294Sstas *          }
385233294Sstas *      ]
386233294Sstas * }
387233294Sstas */
388233294Sstas
389233294Sstasstatic heim_dict_t modules;
390233294Sstas
391233294Sstasstruct plugin2 {
392233294Sstas    heim_string_t path;
393233294Sstas    void *dsohandle;
394233294Sstas    heim_dict_t names;
395233294Sstas};
396233294Sstas
397233294Sstasstatic void
398233294Sstasplug_dealloc(void *ptr)
399233294Sstas{
400233294Sstas    struct plugin2 *p = ptr;
401233294Sstas    heim_release(p->path);
402233294Sstas    heim_release(p->names);
403233294Sstas    if (p->dsohandle)
404233294Sstas	dlclose(p->dsohandle);
405233294Sstas}
406233294Sstas
407233294Sstas
408233294Sstasvoid
409233294Sstas_krb5_load_plugins(krb5_context context, const char *name, const char **paths)
410233294Sstas{
411233294Sstas#ifdef HAVE_DLOPEN
412233294Sstas    heim_string_t s = heim_string_create(name);
413233294Sstas    heim_dict_t module;
414233294Sstas    struct dirent *entry;
415233294Sstas    krb5_error_code ret;
416233294Sstas    const char **di;
417233294Sstas    DIR *d;
418233294Sstas
419233294Sstas    HEIMDAL_MUTEX_lock(&plugin_mutex);
420233294Sstas
421233294Sstas    if (modules == NULL) {
422233294Sstas	modules = heim_dict_create(11);
423233294Sstas	if (modules == NULL) {
424233294Sstas	    HEIMDAL_MUTEX_unlock(&plugin_mutex);
425233294Sstas	    return;
426233294Sstas	}
427233294Sstas    }
428233294Sstas
429233294Sstas    module = heim_dict_copy_value(modules, s);
430233294Sstas    if (module == NULL) {
431233294Sstas	module = heim_dict_create(11);
432233294Sstas	if (module == NULL) {
433233294Sstas	    HEIMDAL_MUTEX_unlock(&plugin_mutex);
434233294Sstas	    heim_release(s);
435233294Sstas	    return;
436233294Sstas	}
437233294Sstas	heim_dict_add_value(modules, s, module);
438233294Sstas    }
439233294Sstas    heim_release(s);
440233294Sstas
441233294Sstas    for (di = paths; *di != NULL; di++) {
442233294Sstas	d = opendir(*di);
443233294Sstas	if (d == NULL)
444233294Sstas	    continue;
445233294Sstas	rk_cloexec_dir(d);
446233294Sstas
447233294Sstas	while ((entry = readdir(d)) != NULL) {
448233294Sstas	    char *n = entry->d_name;
449233294Sstas	    char *path = NULL;
450233294Sstas	    heim_string_t spath;
451233294Sstas	    struct plugin2 *p;
452233294Sstas
453233294Sstas	    /* skip . and .. */
454233294Sstas	    if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')))
455233294Sstas		continue;
456233294Sstas
457233294Sstas	    ret = 0;
458233294Sstas#ifdef __APPLE__
459233294Sstas	    { /* support loading bundles on MacOS */
460233294Sstas		size_t len = strlen(n);
461233294Sstas		if (len > 7 && strcmp(&n[len - 7],  ".bundle") == 0)
462233294Sstas		    ret = asprintf(&path, "%s/%s/Contents/MacOS/%.*s", *di, n, (int)(len - 7), n);
463233294Sstas	    }
464233294Sstas#endif
465233294Sstas	    if (ret < 0 || path == NULL)
466233294Sstas		ret = asprintf(&path, "%s/%s", *di, n);
467233294Sstas
468233294Sstas	    if (ret < 0 || path == NULL)
469233294Sstas		continue;
470233294Sstas
471233294Sstas	    spath = heim_string_create(n);
472233294Sstas	    if (spath == NULL) {
473233294Sstas		free(path);
474233294Sstas		continue;
475233294Sstas	    }
476233294Sstas
477233294Sstas	    /* check if already cached */
478233294Sstas	    p = heim_dict_copy_value(module, spath);
479233294Sstas	    if (p == NULL) {
480233294Sstas		p = heim_alloc(sizeof(*p), "krb5-plugin", plug_dealloc);
481233294Sstas		if (p)
482233294Sstas		    p->dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY);
483233294Sstas
484233294Sstas		if (p->dsohandle) {
485233294Sstas		    p->path = heim_retain(spath);
486233294Sstas		    p->names = heim_dict_create(11);
487233294Sstas		    heim_dict_add_value(module, spath, p);
488233294Sstas		}
489233294Sstas	    }
490233294Sstas	    heim_release(spath);
491233294Sstas	    heim_release(p);
492233294Sstas	    free(path);
493233294Sstas	}
494233294Sstas	closedir(d);
495233294Sstas    }
496233294Sstas    heim_release(module);
497233294Sstas    HEIMDAL_MUTEX_unlock(&plugin_mutex);
498233294Sstas#endif /* HAVE_DLOPEN */
499233294Sstas}
500233294Sstas
501233294Sstasvoid
502233294Sstas_krb5_unload_plugins(krb5_context context, const char *name)
503233294Sstas{
504233294Sstas    HEIMDAL_MUTEX_lock(&plugin_mutex);
505233294Sstas    heim_release(modules);
506233294Sstas    modules = NULL;
507233294Sstas    HEIMDAL_MUTEX_unlock(&plugin_mutex);
508233294Sstas}
509233294Sstas
510233294Sstas/*
511233294Sstas *
512233294Sstas */
513233294Sstas
514233294Sstasstruct common_plugin_method {
515233294Sstas    int			version;
516233294Sstas    krb5_error_code	(*init)(krb5_context, void **);
517233294Sstas    void		(*fini)(void *);
518233294Sstas};
519233294Sstas
520233294Sstasstruct plug {
521233294Sstas    void *dataptr;
522233294Sstas    void *ctx;
523233294Sstas};
524233294Sstas
525233294Sstasstatic void
526233294Sstasplug_free(void *ptr)
527233294Sstas{
528233294Sstas    struct plug *pl = ptr;
529233294Sstas    if (pl->dataptr) {
530233294Sstas	struct common_plugin_method *cpm = pl->dataptr;
531233294Sstas	cpm->fini(pl->ctx);
532233294Sstas    }
533233294Sstas}
534233294Sstas
535233294Sstasstruct iter_ctx {
536233294Sstas    krb5_context context;
537233294Sstas    heim_string_t n;
538233294Sstas    const char *name;
539233294Sstas    int min_version;
540233294Sstas    heim_array_t result;
541233294Sstas    krb5_error_code (*func)(krb5_context, const void *, void *, void *);
542233294Sstas    void *userctx;
543233294Sstas    krb5_error_code ret;
544233294Sstas};
545233294Sstas
546233294Sstasstatic void
547233294Sstassearch_modules(void *ctx, heim_object_t key, heim_object_t value)
548233294Sstas{
549233294Sstas    struct iter_ctx *s = ctx;
550233294Sstas    struct plugin2 *p = value;
551233294Sstas    struct plug *pl = heim_dict_copy_value(p->names, s->n);
552233294Sstas    struct common_plugin_method *cpm;
553233294Sstas
554233294Sstas    if (pl == NULL) {
555233294Sstas	if (p->dsohandle == NULL)
556233294Sstas	    return;
557233294Sstas
558233294Sstas	pl = heim_alloc(sizeof(*pl), "struct-plug", plug_free);
559233294Sstas
560233294Sstas	cpm = pl->dataptr = dlsym(p->dsohandle, s->name);
561233294Sstas	if (cpm) {
562233294Sstas	    int ret;
563233294Sstas
564233294Sstas	    ret = cpm->init(s->context, &pl->ctx);
565233294Sstas	    if (ret)
566233294Sstas		cpm = pl->dataptr = NULL;
567233294Sstas	}
568233294Sstas	heim_dict_add_value(p->names, s->n, pl);
569233294Sstas    } else {
570233294Sstas	cpm = pl->dataptr;
571233294Sstas    }
572233294Sstas
573233294Sstas    if (cpm && cpm->version >= s->min_version)
574233294Sstas	heim_array_append_value(s->result, pl);
575233294Sstas
576233294Sstas    heim_release(pl);
577233294Sstas}
578233294Sstas
579233294Sstasstatic void
580233294Sstaseval_results(heim_object_t value, void *ctx)
581233294Sstas{
582233294Sstas    struct plug *pl = value;
583233294Sstas    struct iter_ctx *s = ctx;
584233294Sstas
585233294Sstas    if (s->ret != KRB5_PLUGIN_NO_HANDLE)
586233294Sstas	return;
587233294Sstas
588233294Sstas    s->ret = s->func(s->context, pl->dataptr, pl->ctx, s->userctx);
589233294Sstas}
590233294Sstas
591233294Sstaskrb5_error_code
592233294Sstas_krb5_plugin_run_f(krb5_context context,
593233294Sstas		   const char *module,
594233294Sstas		   const char *name,
595233294Sstas		   int min_version,
596233294Sstas		   int flags,
597233294Sstas		   void *userctx,
598233294Sstas		   krb5_error_code (*func)(krb5_context, const void *, void *, void *))
599233294Sstas{
600233294Sstas    heim_string_t m = heim_string_create(module);
601233294Sstas    heim_dict_t dict;
602233294Sstas    struct iter_ctx s;
603233294Sstas
604233294Sstas    HEIMDAL_MUTEX_lock(&plugin_mutex);
605233294Sstas
606233294Sstas    dict = heim_dict_copy_value(modules, m);
607233294Sstas    heim_release(m);
608233294Sstas    if (dict == NULL) {
609233294Sstas	HEIMDAL_MUTEX_unlock(&plugin_mutex);
610233294Sstas	return KRB5_PLUGIN_NO_HANDLE;
611233294Sstas    }
612233294Sstas
613233294Sstas    s.context = context;
614233294Sstas    s.name = name;
615233294Sstas    s.n = heim_string_create(name);
616233294Sstas    s.min_version = min_version;
617233294Sstas    s.result = heim_array_create();
618233294Sstas    s.func = func;
619233294Sstas    s.userctx = userctx;
620233294Sstas
621233294Sstas    heim_dict_iterate_f(dict, search_modules, &s);
622233294Sstas
623233294Sstas    heim_release(dict);
624233294Sstas
625233294Sstas    HEIMDAL_MUTEX_unlock(&plugin_mutex);
626233294Sstas
627233294Sstas    s.ret = KRB5_PLUGIN_NO_HANDLE;
628233294Sstas
629233294Sstas    heim_array_iterate_f(s.result, eval_results, &s);
630233294Sstas
631233294Sstas    heim_release(s.result);
632233294Sstas    heim_release(s.n);
633233294Sstas
634233294Sstas    return s.ret;
635233294Sstas}
636