summaryrefslogtreecommitdiff
path: root/crypto
diff options
context:
space:
mode:
authorYann Ylavic <ylavic@apache.org>2018-08-04 16:48:20 +0000
committerYann Ylavic <ylavic@apache.org>2018-08-04 16:48:20 +0000
commit5a1b9a92dad639eea47c78e28bae25eafa014331 (patch)
treeaaa10b6cb5b950d8802b5292d318782f47dfa02c /crypto
parent8ca89dd5ecf6de93c260d4d9d575b7b59a1f978b (diff)
downloadapr-5a1b9a92dad639eea47c78e28bae25eafa014331.tar.gz
crypto: follow up to r1833421.
Link with OpenSSL's libssl in addition to libcrypto, so that we can initialize and deinitialize them together (libssl can't do this independently). Also, for older OpenSSL versions, initialize the threading locks needed by the lib at runtime. git-svn-id: https://svn.apache.org/repos/asf/apr/apr/trunk@1837430 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'crypto')
-rw-r--r--crypto/apr_crypto_internal.c269
1 files changed, 267 insertions, 2 deletions
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 */