summaryrefslogtreecommitdiff
path: root/crypto
diff options
context:
space:
mode:
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 */