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 * 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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