diff options
-rw-r--r-- | build/crypto.m4 | 2 | ||||
-rw-r--r-- | crypto/apr_crypto_internal.c | 269 |
2 files changed, 268 insertions, 3 deletions
diff --git a/build/crypto.m4 b/build/crypto.m4 index d4719b34f..1930452da 100644 --- a/build/crypto.m4 +++ b/build/crypto.m4 @@ -132,7 +132,7 @@ AC_DEFUN([APU_CHECK_CRYPTO_OPENSSL], [ dnl Since we have already done the AC_CHECK_LIB tests, if we have it, dnl we know the library is there. if test "$apu_have_openssl" = "1"; then - APR_ADDTO(LDADD_crypto_openssl, [$openssl_LDFLAGS -lcrypto]) + APR_ADDTO(LDADD_crypto_openssl, [$openssl_LDFLAGS -lssl -lcrypto]) apu_have_crypto=1 AC_MSG_CHECKING([for const input buffers in OpenSSL]) diff --git a/crypto/apr_crypto_internal.c b/crypto/apr_crypto_internal.c index 6b4c1c9eb..ecec5a428 100644 --- a/crypto/apr_crypto_internal.c +++ b/crypto/apr_crypto_internal.c @@ -24,12 +24,23 @@ #if APU_HAVE_CRYPTO #if APU_HAVE_OPENSSL +#include "apr_thread_mutex.h" #include <openssl/crypto.h> #include <openssl/engine.h> #include <openssl/conf.h> #include <openssl/comp.h> #include <openssl/evp.h> +#include <openssl/ssl.h> + +#if APR_HAS_THREADS && APR_USE_OPENSSL_PRE_1_1_API +static apr_status_t ossl_thread_setup(apr_pool_t *pool); +#else +static APR_INLINE apr_status_t ossl_thread_setup(apr_pool_t *pool) +{ + return APR_SUCCESS; +} +#endif const char *apr__crypto_openssl_version(void) { @@ -51,7 +62,10 @@ apr_status_t apr__crypto_openssl_init(const char *params, ENGINE_load_builtin_engines(); ENGINE_register_all_complete(); - return APR_SUCCESS; + SSL_load_error_strings(); + SSL_library_init(); + + return ossl_thread_setup(pool); } apr_status_t apr__crypto_openssl_term(void) @@ -81,11 +95,262 @@ apr_status_t apr__crypto_openssl_term(void) #else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ OPENSSL_cleanup(); -#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ +#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ + + return APR_SUCCESS; +} + +/* + * To ensure thread-safetyness in OpenSSL - work in progress + * Taken from httpd's mod_ssl code. + */ + +#if APR_HAS_THREADS && APR_USE_OPENSSL_PRE_1_1_API + +static apr_thread_mutex_t **ossl_locks; +static int ossl_num_locks; + +static void ossl_thread_locking(int mode, int type, const char *file, int line) +{ + if (type < ossl_num_locks) { + if (mode & CRYPTO_LOCK) { + (void)apr_thread_mutex_lock(ossl_locks[type]); + } + else { + (void)apr_thread_mutex_unlock(ossl_locks[type]); + } + } +} + +/* Dynamic lock structure */ +struct CRYPTO_dynlock_value { + apr_pool_t *pool; + apr_thread_mutex_t *mutex; + const char* file; + int line; +}; + +/* Global reference to the pool passed into ossl_thread_setup() */ +static apr_pool_t *ossl_dynlock_pool = NULL; + +/* + * Dynamic lock creation callback + */ +static +struct CRYPTO_dynlock_value *ossl_dynlock_create(const char *file, int line) +{ + struct CRYPTO_dynlock_value *value; + apr_status_t rv; + apr_pool_t *p; + + /* + * We need a pool to allocate our mutex. Since we can't clear + * allocated memory from a pool, create a subpool that we can blow + * away in the destruction callback. + */ + rv = apr_pool_create(&p, ossl_dynlock_pool); + if (rv != APR_SUCCESS) { + return NULL; + } + value = apr_palloc(p, sizeof(*value)); + + rv = apr_thread_mutex_create(&value->mutex, APR_THREAD_MUTEX_DEFAULT, p); + if (rv != APR_SUCCESS) { + apr_pool_destroy(p); + return NULL; + } + + /* Keep our own copy of the place from which we were created, + using our own pool. */ + value->file = apr_pstrdup(p, file); + value->line = line; + value->pool = p; + return value; +} + +/* + * Dynamic locking and unlocking function + */ + +static void ossl_dynlock_locking(int mode, struct CRYPTO_dynlock_value *l, + const char *file, int line) +{ + if (mode & CRYPTO_LOCK) { + (void)apr_thread_mutex_lock(l->mutex); + } + else { + (void)apr_thread_mutex_unlock(l->mutex); + } +} + +/* + * Dynamic lock destruction callback + */ +static void ossl_dynlock_destroy(struct CRYPTO_dynlock_value *l, + const char *file, int line) +{ + /* Trust that whomever owned the CRYPTO_dynlock_value we were + * passed has no future use for it... + */ + apr_pool_destroy(l->pool); +} + +/* Windows and BeOS can use the default THREADID callback shipped with OpenSSL + * 1.0.x, as can any platform with a thread-safe errno. + */ +#define OSSL_DEFAULT_THREADID_IS_SAFE (OPENSSL_VERSION_NUMBER >= 0x10000000L \ + && (defined(_REENTRANT) \ + || __BEOS__ \ + || _WIN32 \ + )) +#if OSSL_DEFAULT_THREADID_IS_SAFE + +/* We don't need to set up a threadid callback on this platform. */ +static APR_INLINE apr_status_t ossl_thread_id_setup(apr_pool_t *pool) +{ return APR_SUCCESS; } +static APR_INLINE apr_status_t ossl_thread_id_cleanup(void) +{ + return APR_SUCCESS; +} + +#else + +/** + * Used by both versions of ossl_thread_id(). Returns an unsigned long that + * should be unique to the currently executing thread. + */ +static unsigned long ossl_thread_id_internal(void) +{ + /* OpenSSL needs this to return an unsigned long. On OS/390, the pthread + * id is a structure twice that big. Use the TCB pointer instead as a + * unique unsigned long. + */ +#ifdef __MVS__ + struct PSA { + char unmapped[540]; /* PSATOLD is at offset 540 in the PSA */ + unsigned long PSATOLD; + } *psaptr = 0; /* PSA is at address 0 */ + + return psaptr->PSATOLD; +#else + return (unsigned long) apr_os_thread_current(); +#endif +} + +#ifndef OPENSSL_NO_DEPRECATED + +static unsigned long ossl_thread_id(void) +{ + return ossl_thread_id_internal(); +} + +#else + +static void ossl_thread_id(CRYPTO_THREADID *id) +{ + /* XXX Ideally we would be using the _set_pointer() callback on platforms + * that have a pointer-based thread "identity". But this entire API is + * fraught with problems (see PR60947) and has been removed completely in + * OpenSSL 1.1.0, so I'm not too invested in fixing it right now. */ + CRYPTO_THREADID_set_numeric(id, ossl_thread_id_internal()); +} + +#endif /* OPENSSL_NO_DEPRECATED */ + +static apr_status_t ossl_thread_id_cleanup(void) +{ +#ifndef OPENSSL_NO_DEPRECATED + CRYPTO_set_id_callback(NULL); +#else + /* XXX This does nothing. The new-style THREADID callback is write-once. */ + CRYPTO_THREADID_set_callback(NULL); +#endif + + return APR_SUCCESS; +} + +static apr_status_t ossl_thread_id_setup(apr_pool_t *pool) +{ +#ifndef OPENSSL_NO_DEPRECATED + /* This API is deprecated, but we prefer it to its replacement since it + * allows us to unset the callback when this module is being unloaded. */ + CRYPTO_set_id_callback(ossl_thread_id); +#else + /* This is a last resort. We can only set this once, which means that we'd + * better not get reloaded into a different address. See PR60947. + */ + CRYPTO_THREADID_set_callback(ossl_thread_id); + + if (CRYPTO_THREADID_get_callback() != ossl_thread_id) { + return APR_EGENERAL; + } +#endif + + return APR_SUCCESS; +} + +#endif /* OSSL_DEFAULT_THREADID_IS_SAFE */ + +static apr_status_t ossl_thread_cleanup(void *data) +{ + CRYPTO_set_dynlock_create_callback(NULL); + CRYPTO_set_dynlock_lock_callback(NULL); + CRYPTO_set_dynlock_destroy_callback(NULL); + ossl_dynlock_pool = NULL; + + CRYPTO_set_locking_callback(NULL); + + ossl_thread_id_cleanup(); + + return APR_SUCCESS; +} + +static apr_status_t ossl_thread_setup(apr_pool_t *pool) +{ + apr_status_t rv; + int i, num_locks; + + rv = ossl_thread_id_setup(pool); + if (rv != APR_SUCCESS) { + return rv; + } + + num_locks = CRYPTO_num_locks(); + ossl_locks = apr_palloc(pool, num_locks * sizeof(*ossl_locks)); + if (!ossl_locks) { + return APR_ENOMEM; + } + for (i = 0; i < num_locks; i++) { + rv = apr_thread_mutex_create(&ossl_locks[i], + APR_THREAD_MUTEX_DEFAULT, pool); + if (rv != APR_SUCCESS) { + return rv; + } + } + ossl_num_locks = num_locks; + + CRYPTO_set_locking_callback(ossl_thread_locking); + + /* Set up dynamic locking scaffolding for OpenSSL to use at its + * convenience. + */ + ossl_dynlock_pool = pool; + CRYPTO_set_dynlock_create_callback(ossl_dynlock_create); + CRYPTO_set_dynlock_lock_callback(ossl_dynlock_locking); + CRYPTO_set_dynlock_destroy_callback(ossl_dynlock_destroy); + + apr_pool_cleanup_register(pool, NULL, ossl_thread_cleanup, + apr_pool_cleanup_null); + return APR_SUCCESS; +} + +#endif /* #if APR_HAS_THREADS && APR_USE_OPENSSL_PRE_1_1_API */ + + #endif /* APU_HAVE_OPENSSL */ |