diff options
-rw-r--r-- | debian/changelog | 8 | ||||
-rw-r--r-- | src/GNUmakefile | 2 | ||||
-rw-r--r-- | src/openssl.c | 159 |
3 files changed, 165 insertions, 4 deletions
diff --git a/debian/changelog b/debian/changelog index 4d5e440..8ace2cc 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +liblua-openssl (20141231-0) unstable; urgency=low + + * Add multi-threaded re-entrancy protection, including explicitly + synchronizing OpenSSL initialization because OpenSSL doesn't appear to + use its own locking callbacks from initialization routines. + + -- William Ahern <william@25thandClement.com> Fri, 31 Jan 2014 14:27:30 -0800 + liblua-openssl (20131209-1) unstable; urgency=low * Initial release after splitting from cqueues project. diff --git a/src/GNUmakefile b/src/GNUmakefile index fcf79a6..a0c2f00 100644 --- a/src/GNUmakefile +++ b/src/GNUmakefile @@ -29,7 +29,7 @@ ifeq ($(CC_$(d)), sunpro) CPPFLAGS_$(d) += -DOPENSSL_NO_EC endif -LDFLAGS_$(d) += -lssl -lcrypto +LDFLAGS_$(d) += -lssl -lcrypto -lpthread -ldl # # C O M P I L A T I O N R U L E S diff --git a/src/openssl.c b/src/openssl.c index c8af43d..64bbba4 100644 --- a/src/openssl.c +++ b/src/openssl.c @@ -1,7 +1,7 @@ /* ========================================================================== * openssl.c - Lua OpenSSL * -------------------------------------------------------------------------- - * Copyright (c) 2012 William Ahern + * Copyright (c) 2012-2014 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -39,6 +39,10 @@ #include <netinet/in.h> /* struct in_addr struct in6_addr */ #include <arpa/inet.h> /* inet_pton(3) */ +#include <pthread.h> /* pthread_mutex_init(3) pthread_mutex_lock(3) pthread_mutex_unlock(3) */ + +#include <dlfcn.h> /* dladdr(3) dlopen(3) */ + #include <openssl/err.h> #include <openssl/bn.h> #include <openssl/asn1.h> @@ -74,6 +78,13 @@ #define CIPHER_CLASS "EVP_CIPHER_CTX" /* not a pointer */ +#if __GNUC__ +#define NOTUSED __attribute__((unused)) +#else +#define NOTUSED +#endif + + #define countof(a) (sizeof (a) / sizeof *(a)) #define endof(a) (&(a)[countof(a)]) @@ -3906,9 +3917,151 @@ int luaopen__openssl_rand(lua_State *L) { } /* luaopen__openssl_rand() */ +/* + * Multithread Reentrancy Protection + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static struct { + pthread_mutex_t *lock; + int nlock; + + void *dlref; +} mt_state; + + +static void mt_lock(int mode, int type, const char *file NOTUSED, int line NOTUSED) { + if (mode & CRYPTO_LOCK) + pthread_mutex_lock(&mt_state.lock[type]); + else + pthread_mutex_unlock(&mt_state.lock[type]); +} /* mt_lock() */ + + +/* + * Sources include Google and especially the Wine Project. See get_unix_tid + * at http://source.winehq.org/git/wine.git/?a=blob;f=dlls/ntdll/server.c. + */ +#if __FreeBSD__ +#include <sys/thr.h> /* thr_self(2) */ +#elif __NetBSD__ +#include <lwp.h> /* _lwp_self(2) */ +#endif + +static unsigned long mt_gettid(void) { +#if __APPLE__ + return pthread_mach_thread_np(pthread_self()); +#elif __DragonFly__ + return lwp_gettid(); +#elif __FreeBSD__ + long id; + + thr_self(&id); + + return id; +#elif __NetBSD__ + return _lwp_self(); +#else + /* + * pthread_t is an integer on Solaris and Linux, and a unique pointer + * on OpenBSD. + */ + return (unsigned long)pthread_self(); +#endif +} /* mt_gettid() */ + + +static int mt_init(void) { + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + int bound = 0, error = 0; + + pthread_mutex_lock(&mutex); + + if (!CRYPTO_get_locking_callback()) { + if (!mt_state.lock) { + int i; + + mt_state.nlock = CRYPTO_num_locks(); + + if (!(mt_state.lock = malloc(mt_state.nlock * sizeof *mt_state.lock))) { + error = errno; + goto leave; + } + + for (i = 0; i < mt_state.nlock; i++) { + pthread_mutex_init(&mt_state.lock[i], NULL); + } + } + + CRYPTO_set_locking_callback(&mt_lock); + bound = 1; + } + + if (!CRYPTO_get_id_callback()) { + CRYPTO_set_id_callback(&mt_gettid); + bound = 1; + } + + /* + * Prevent loader from unlinking us if we've registered a callback + * with OpenSSL by taking another reference to ourselves. + */ + if (bound && !mt_state.dlref) { + Dl_info info; + + if (!dladdr(&luaopen__openssl_rand, &info)) { + error = -1; + goto leave; + } + + if (!(mt_state.dlref = dlopen(info.dli_fname, RTLD_NOW|RTLD_LOCAL))) { + error = -1; + goto leave; + } + } + +leave: + pthread_mutex_unlock(&mutex); + + return error; +} /* mt_init() */ + + static void initall(lua_State *L) { - ERR_load_crypto_strings(); - OpenSSL_add_all_algorithms(); + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + static int initssl; + int error; + + if ((error = mt_init())) { + if (error == -1) { + luaL_error(L, "openssl.init: %s", dlerror()); + } else { + char why[256]; + + if (0 != strerror_r(error, why, sizeof why) || *why == '\0') + luaL_error(L, "openssl.init: Unknown error: %d", error); + + luaL_error(L, "openssl.init: %s", why); + } + } + + pthread_mutex_lock(&mutex); + + if (!initssl) { + initssl = 1; + + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_algorithms(); + + /* + * TODO: Figure out a way to detect whether OpenSSL has + * already been configured. + */ + OPENSSL_config(NULL); + } + + pthread_mutex_unlock(&mutex); addclass(L, BIGNUM_CLASS, bn_methods, bn_metatable); addclass(L, PUBKEY_CLASS, pk_methods, pk_metatable); |