Fix some DLL hell: use dladdr() to find plugin dir
Normally one would dlopen() a shared object's basename, not its absolute path. However, lib/krb5/plugin.c, in an effort to be zero-conf-ish, wants to readdir() to find plugins to load, and in the process it ends up defeating the RTLD's search-the-caller's-rpath. This commit partially addresses this by allowing the use of $ORIGIN in plugin_dir values and using them for the default (except on OS X). This allows multiple Heimdal versions installed on the same host, but with different plugin ABIs, to co-exist. A step forward for doing make check on hosts where Heimdal is installed. For now we hardcode $ORIGIN/../lib/plugin/krb5 (linux, Solaris, *BSD), or $ORIGIN (Windows; for assemblies objects need to be in the same directory) and we eval $ORIGIN by using dladdr() (Linux, Solaris) or GetModuleHandleEx() (Win32, via a dladdr() wrapper in libroken) to find the path to libkrb5 whose dirname to use as $ORIGIN. For Windows, because we need the plugins to be in the same directory as libkrb5, we require a prefix on plugin DLLs ("plugin_krb5_") to distinguish them from other objects. We should add a special token to mean "look in $ORIGIN, sure, but dlopen() the plugin basenames only (so the RTLD can search the rpath)".
This commit is contained in:
@@ -9,3 +9,11 @@ AC_DEFUN([rk_DLOPEN], [
|
|||||||
#endif],[0,0])
|
#endif],[0,0])
|
||||||
AM_CONDITIONAL(HAVE_DLOPEN, test "$ac_cv_funclib_dlopen" != no)
|
AM_CONDITIONAL(HAVE_DLOPEN, test "$ac_cv_funclib_dlopen" != no)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([rk_DLADDR], [
|
||||||
|
AC_FIND_FUNC_NO_LIBS(dladdr, dl,[
|
||||||
|
#ifdef HAVE_DLFCN_H
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#endif],[0,0])
|
||||||
|
AM_CONDITIONAL(HAVE_DLADDR, test "$ac_cv_funclib_dladdr" != no)
|
||||||
|
])
|
||||||
|
@@ -500,6 +500,7 @@ AC_MSG_RESULT($ac_rk_have___sync_add_and_fetch)
|
|||||||
AC_FUNC_MMAP
|
AC_FUNC_MMAP
|
||||||
|
|
||||||
KRB_CAPABILITIES
|
KRB_CAPABILITIES
|
||||||
|
rk_DLADDR
|
||||||
|
|
||||||
AC_CHECK_GETPWNAM_R_POSIX
|
AC_CHECK_GETPWNAM_R_POSIX
|
||||||
|
|
||||||
|
@@ -303,6 +303,10 @@ static const char *const rcsid[] = { (const char *)rcsid, "@(#)" msg }
|
|||||||
/* MSVC doesn't provide a <dlfcn.h>, but we implement it in lib/roken. */
|
/* MSVC doesn't provide a <dlfcn.h>, but we implement it in lib/roken. */
|
||||||
#define HAVE_DLOPEN 1
|
#define HAVE_DLOPEN 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `dladdr' function. */
|
||||||
|
/* MSVC doesn't provide a <dlfcn.h>, but we implement it in lib/roken. */
|
||||||
|
#define HAVE_DLADDR 1
|
||||||
|
|
||||||
/* Define to 1 if you have the `dn_expand' function. */
|
/* Define to 1 if you have the `dn_expand' function. */
|
||||||
/* #undef HAVE_DN_EXPAND */
|
/* #undef HAVE_DN_EXPAND */
|
||||||
|
|
||||||
|
@@ -348,8 +348,12 @@ kt_ops_copy(krb5_context context, const krb5_context src_context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const char *sysplugin_dirs[] = {
|
static const char *sysplugin_dirs[] = {
|
||||||
LIBDIR "/plugin/krb5",
|
"$ORIGIN/../lib/plugin/krb5",
|
||||||
|
#ifdef _WIN32
|
||||||
|
"$ORIGIN/../lib",
|
||||||
|
#endif
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
LIBDIR "/plugin/krb5",
|
||||||
"/Library/KerberosPlugins/KerberosFrameworkPlugins",
|
"/Library/KerberosPlugins/KerberosFrameworkPlugins",
|
||||||
"/System/Library/KerberosPlugins/KerberosFrameworkPlugins",
|
"/System/Library/KerberosPlugins/KerberosFrameworkPlugins",
|
||||||
#endif
|
#endif
|
||||||
@@ -362,10 +366,14 @@ init_context_once(void *ctx)
|
|||||||
krb5_context context = ctx;
|
krb5_context context = ctx;
|
||||||
char **dirs;
|
char **dirs;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
dirs = rk_UNCONST(sysplugin_dirs);
|
||||||
|
#else
|
||||||
dirs = krb5_config_get_strings(context, NULL, "libdefaults",
|
dirs = krb5_config_get_strings(context, NULL, "libdefaults",
|
||||||
"plugin_dir", NULL);
|
"plugin_dir", NULL);
|
||||||
if (dirs == NULL)
|
if (dirs == NULL)
|
||||||
dirs = rk_UNCONST(sysplugin_dirs);
|
dirs = rk_UNCONST(sysplugin_dirs);
|
||||||
|
#endif
|
||||||
|
|
||||||
_krb5_load_plugins(context, "krb5", (const char **)dirs);
|
_krb5_load_plugins(context, "krb5", (const char **)dirs);
|
||||||
|
|
||||||
|
@@ -219,6 +219,38 @@ plug_dealloc(void *ptr)
|
|||||||
dlclose(p->dsohandle);
|
dlclose(p->dsohandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
resolve_origin(const char *di)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_DLADDR
|
||||||
|
Dl_info dl_info;
|
||||||
|
const char *dname;
|
||||||
|
char *path, *p;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (strncmp(di, "$ORIGIN/", sizeof("$ORIGIN/") - 1) &&
|
||||||
|
strcmp(di, "$ORIGIN"))
|
||||||
|
return strdup(di);
|
||||||
|
|
||||||
|
#ifndef HAVE_DLADDR
|
||||||
|
return strdup(LIBDIR "/plugin/krb5");
|
||||||
|
#else
|
||||||
|
di += sizeof("$ORIGIN") - 1;
|
||||||
|
|
||||||
|
if (dladdr(_krb5_load_plugins, &dl_info) == 0)
|
||||||
|
return strdup(LIBDIR "/plugin/krb5");
|
||||||
|
|
||||||
|
dname = dl_info.dli_fname;
|
||||||
|
p = strrchr(dname, '/');
|
||||||
|
if (p)
|
||||||
|
*p = '\0';
|
||||||
|
|
||||||
|
if (asprintf(&path, "%s%s", dname, di) == -1)
|
||||||
|
return NULL;
|
||||||
|
return path;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load plugins (new system) for the given module @name (typicall
|
* Load plugins (new system) for the given module @name (typicall
|
||||||
@@ -239,6 +271,7 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
|
|||||||
struct dirent *entry;
|
struct dirent *entry;
|
||||||
krb5_error_code ret;
|
krb5_error_code ret;
|
||||||
const char **di;
|
const char **di;
|
||||||
|
char *dirname = NULL;
|
||||||
DIR *d;
|
DIR *d;
|
||||||
|
|
||||||
HEIMDAL_MUTEX_lock(&plugin_mutex);
|
HEIMDAL_MUTEX_lock(&plugin_mutex);
|
||||||
@@ -264,7 +297,11 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
|
|||||||
heim_release(s);
|
heim_release(s);
|
||||||
|
|
||||||
for (di = paths; *di != NULL; di++) {
|
for (di = paths; *di != NULL; di++) {
|
||||||
d = opendir(*di);
|
free(dirname);
|
||||||
|
dirname = resolve_origin(*di);
|
||||||
|
if (dirname == NULL)
|
||||||
|
continue;
|
||||||
|
d = opendir(dirname);
|
||||||
if (d == NULL)
|
if (d == NULL)
|
||||||
continue;
|
continue;
|
||||||
rk_cloexec_dir(d);
|
rk_cloexec_dir(d);
|
||||||
@@ -279,16 +316,21 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
|
|||||||
if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')))
|
if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (strncmp(n, "plugin_krb5_", sizeof("plugin_krb5_") - 1))
|
||||||
|
continue;
|
||||||
|
#endif
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
{ /* support loading bundles on MacOS */
|
{ /* support loading bundles on MacOS */
|
||||||
size_t len = strlen(n);
|
size_t len = strlen(n);
|
||||||
if (len > 7 && strcmp(&n[len - 7], ".bundle") == 0)
|
if (len > 7 && strcmp(&n[len - 7], ".bundle") == 0)
|
||||||
ret = asprintf(&path, "%s/%s/Contents/MacOS/%.*s", *di, n, (int)(len - 7), n);
|
ret = asprintf(&path, "%s/%s/Contents/MacOS/%.*s", dirname, n, (int)(len - 7), n);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (ret < 0 || path == NULL)
|
if (ret < 0 || path == NULL)
|
||||||
ret = asprintf(&path, "%s/%s", *di, n);
|
ret = asprintf(&path, "%s/%s", dirname, n);
|
||||||
|
|
||||||
if (ret < 0 || path == NULL)
|
if (ret < 0 || path == NULL)
|
||||||
continue;
|
continue;
|
||||||
@@ -318,6 +360,7 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
|
|||||||
}
|
}
|
||||||
closedir(d);
|
closedir(d);
|
||||||
}
|
}
|
||||||
|
free(dirname);
|
||||||
HEIMDAL_MUTEX_unlock(&plugin_mutex);
|
HEIMDAL_MUTEX_unlock(&plugin_mutex);
|
||||||
heim_release(module);
|
heim_release(module);
|
||||||
#endif /* HAVE_DLOPEN */
|
#endif /* HAVE_DLOPEN */
|
||||||
|
@@ -32,6 +32,8 @@
|
|||||||
#ifndef __dlfcn_h__
|
#ifndef __dlfcn_h__
|
||||||
#define __dlfcn_h__
|
#define __dlfcn_h__
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
#ifndef ROKEN_LIB_FUNCTION
|
#ifndef ROKEN_LIB_FUNCTION
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define ROKEN_LIB_FUNCTION
|
#define ROKEN_LIB_FUNCTION
|
||||||
@@ -75,6 +77,11 @@ dlopen(const char *, int);
|
|||||||
ROKEN_LIB_FUNCTION DLSYM_RET_TYPE ROKEN_LIB_CALL
|
ROKEN_LIB_FUNCTION DLSYM_RET_TYPE ROKEN_LIB_CALL
|
||||||
dlsym(void *, const char *);
|
dlsym(void *, const char *);
|
||||||
|
|
||||||
|
typedef struct Dl_info {
|
||||||
|
char *dli_fname;
|
||||||
|
char _dli_buf[MAX_PATH + 2];
|
||||||
|
} Dl_info, Dl_info_t;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
#endif
|
#endif
|
||||||
|
@@ -163,3 +163,38 @@ dlsym(void * vhm, const char * func_name)
|
|||||||
return (DLSYM_RET_TYPE)(ULONG_PTR)GetProcAddress(hm, func_name);
|
return (DLSYM_RET_TYPE)(ULONG_PTR)GetProcAddress(hm, func_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
|
||||||
|
dladdr(void *addr, Dl_info *dli)
|
||||||
|
{
|
||||||
|
HMODULE hm;
|
||||||
|
int ret;
|
||||||
|
DWORD nsize;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
memset(dli, 0, sizeof(*dli));
|
||||||
|
|
||||||
|
if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
||||||
|
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||||
|
(LPCTSTR)addr, &hm))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
nsize = GetModuleFileName(hm, dli->_dli_buf, sizeof(dli->_dli_buf));
|
||||||
|
dli->_dli_buf[sizeof(dli->_dli_buf) - 1] = '\0';
|
||||||
|
if (nsize >= sizeof(dli->_dli_buf))
|
||||||
|
return 0; /* truncated? can't be... */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Normalize path component separators, since our caller may want to
|
||||||
|
* portably take the dirname or basename of dli->dli_fname,
|
||||||
|
* searching for the last '/'.
|
||||||
|
*/
|
||||||
|
for (p = dli->_dli_buf;
|
||||||
|
p < &dli->_dli_buf[sizeof(dli->_dli_buf) - 1] && *p;
|
||||||
|
p++) {
|
||||||
|
if (*p == '\\')
|
||||||
|
*p = '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
dli->dli_fname = dli->_dli_buf;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user