summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Antonuk <alan.antonuk@gmail.com>2017-12-04 20:33:51 -0800
committerAlan Antonuk <alan.antonuk@gmail.com>2017-12-10 22:38:25 -0800
commitb80de27b8782772dfb3fe8253940663697f680ee (patch)
tree833a69221f00ead116b849e9a3a01bbe90c10b4f
parentcdfce5a328a59bc9bb51428be0585715546c4634 (diff)
downloadrabbitmq-c-b80de27b8782772dfb3fe8253940663697f680ee.tar.gz
ssl: OpenSSL should require explicitly un-init
Instead of un-initializing OpenSSL when the connection count reaches 0 require API users to explicitly un-initialize OpenSSL if they care to. In the common case this will require much less work to start a new SSL connection. As a side effect: cleanup and simplify the OpenSSL initialization routines, including removing the thread-safety checks done for the BIO since they will be called under the OpenSSL locking.
-rw-r--r--librabbitmq/amqp_openssl.c209
-rw-r--r--librabbitmq/amqp_openssl_bio.c25
-rw-r--r--librabbitmq/amqp_openssl_bio.h2
-rw-r--r--librabbitmq/amqp_ssl_socket.h39
-rw-r--r--librabbitmq/win32/threads.c5
-rw-r--r--librabbitmq/win32/threads.h1
6 files changed, 180 insertions, 101 deletions
diff --git a/librabbitmq/amqp_openssl.c b/librabbitmq/amqp_openssl.c
index a73cd06..234a698 100644
--- a/librabbitmq/amqp_openssl.c
+++ b/librabbitmq/amqp_openssl.c
@@ -47,20 +47,26 @@
#include <stdlib.h>
#include <string.h>
-static int initialize_openssl(void);
-static int destroy_openssl(void);
+static int initialize_ssl_and_increment_connections(void);
+static int decrement_ssl_connections(void);
-static int open_ssl_connections = 0;
-static amqp_boolean_t do_initialize_openssl = 1;
-static amqp_boolean_t openssl_initialized = 0;
-
-static unsigned long amqp_ssl_threadid_callback(void);
-static void amqp_ssl_locking_callback(int mode, int n, const char *file,
- int line);
+static unsigned long ssl_threadid_callback(void);
+static void ssl_locking_callback(int mode, int n, const char *file, int line);
+static pthread_mutex_t *amqp_openssl_lockarray = NULL;
static pthread_mutex_t openssl_init_mutex = PTHREAD_MUTEX_INITIALIZER;
+static amqp_boolean_t do_initialize_openssl = 1;
+static amqp_boolean_t openssl_initialized = 0;
+static int openssl_connections = 0;
-static pthread_mutex_t *amqp_openssl_lockarray = NULL;
+#define CHECK_SUCCESS(condition) \
+ do { \
+ int check_success_ret = (condition); \
+ if (check_success_ret) { \
+ amqp_abort("Check %s failed <%d>: %s", #condition, check_success_ret, \
+ strerror(check_success_ret)); \
+ } \
+ } while (0)
struct amqp_ssl_socket_t {
const struct amqp_socket_class_t *klass;
@@ -305,7 +311,7 @@ static void amqp_ssl_socket_delete(void *base) {
SSL_CTX_free(self->ctx);
free(self);
}
- destroy_openssl();
+ decrement_ssl_connections();
}
static const struct amqp_socket_class_t amqp_ssl_socket_class = {
@@ -329,7 +335,7 @@ amqp_socket_t *amqp_ssl_socket_new(amqp_connection_state_t state) {
self->verify_peer = 1;
self->verify_hostname = 1;
- status = initialize_openssl();
+ status = initialize_ssl_and_increment_connections();
if (status) {
goto error;
}
@@ -525,101 +531,142 @@ int amqp_ssl_socket_set_ssl_versions(amqp_socket_t *base,
}
void amqp_set_initialize_ssl_library(amqp_boolean_t do_initialize) {
- if (!openssl_initialized) {
+ CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
+
+ if (openssl_connections == 0 && !openssl_initialized) {
do_initialize_openssl = do_initialize;
}
+ CHECK_SUCCESS(!pthread_mutex_unlock(&openssl_init_mutex));
}
-unsigned long amqp_ssl_threadid_callback(void) {
+static unsigned long ssl_threadid_callback(void) {
return (unsigned long)pthread_self();
}
-void amqp_ssl_locking_callback(int mode, int n, AMQP_UNUSED const char *file,
- AMQP_UNUSED int line) {
+static void ssl_locking_callback(int mode, int n, AMQP_UNUSED const char *file,
+ AMQP_UNUSED int line) {
if (mode & CRYPTO_LOCK) {
- if (pthread_mutex_lock(&amqp_openssl_lockarray[n])) {
- amqp_abort("Runtime error: Failure in trying to lock OpenSSL mutex");
- }
+ CHECK_SUCCESS(pthread_mutex_lock(&amqp_openssl_lockarray[n]));
} else {
- if (pthread_mutex_unlock(&amqp_openssl_lockarray[n])) {
- amqp_abort("Runtime error: Failure in trying to unlock OpenSSL mutex");
- }
+ CHECK_SUCCESS(pthread_mutex_unlock(&amqp_openssl_lockarray[n]));
}
}
-static int initialize_openssl(void) {
- if (pthread_mutex_lock(&openssl_init_mutex)) {
- return -1;
- }
- if (do_initialize_openssl) {
- if (NULL == amqp_openssl_lockarray) {
- int i = 0;
- amqp_openssl_lockarray =
- calloc(CRYPTO_num_locks(), sizeof(pthread_mutex_t));
- if (!amqp_openssl_lockarray) {
- pthread_mutex_unlock(&openssl_init_mutex);
- return -1;
- }
- for (i = 0; i < CRYPTO_num_locks(); ++i) {
- if (pthread_mutex_init(&amqp_openssl_lockarray[i], NULL)) {
- free(amqp_openssl_lockarray);
- amqp_openssl_lockarray = NULL;
- pthread_mutex_unlock(&openssl_init_mutex);
- return -1;
- }
+static int setup_openssl(void) {
+ int status;
+
+ int i;
+ amqp_openssl_lockarray = calloc(CRYPTO_num_locks(), sizeof(pthread_mutex_t));
+ if (!amqp_openssl_lockarray) {
+ status = AMQP_STATUS_NO_MEMORY;
+ goto out;
+ }
+ for (i = 0; i < CRYPTO_num_locks(); i++) {
+ if (pthread_mutex_init(&amqp_openssl_lockarray[i], NULL)) {
+ int j;
+ for (j = 0; j < i; j++) {
+ pthread_mutex_destroy(&amqp_openssl_lockarray[j]);
}
+ free(amqp_openssl_lockarray);
+ status = AMQP_STATUS_SSL_ERROR;
+ goto out;
}
+ }
+ CRYPTO_set_id_callback(ssl_threadid_callback);
+ CRYPTO_set_locking_callback(ssl_locking_callback);
- if (0 == open_ssl_connections) {
- CRYPTO_set_id_callback(amqp_ssl_threadid_callback);
- CRYPTO_set_locking_callback(amqp_ssl_locking_callback);
+ OPENSSL_config(NULL);
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ status = AMQP_STATUS_OK;
+out:
+ return status;
+}
+
+int amqp_initialize_ssl_library(void) {
+ int status;
+ CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
+
+ if (!openssl_initialized) {
+ status = setup_openssl();
+ if (status) {
+ goto out;
}
+ openssl_initialized = 1;
+ }
- if (!openssl_initialized) {
- OPENSSL_config(NULL);
+ status = AMQP_STATUS_OK;
+out:
+ CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
+ return status;
+}
- SSL_library_init();
- SSL_load_error_strings();
+static int initialize_ssl_and_increment_connections() {
+ int status;
+ CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
- openssl_initialized = 1;
+ if (do_initialize_openssl && !openssl_initialized) {
+ status = setup_openssl();
+ if (status) {
+ goto exit;
}
+ openssl_initialized = 1;
}
- ++open_ssl_connections;
+ openssl_connections += 1;
+ status = AMQP_STATUS_OK;
+exit:
+ CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
+ return status;
+}
+
+static int decrement_ssl_connections(void) {
+ CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
+
+ if (openssl_connections > 0) {
+ openssl_connections--;
+ }
- pthread_mutex_unlock(&openssl_init_mutex);
- return 0;
+ CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
+ return AMQP_STATUS_OK;
}
-static int destroy_openssl(void) {
- if (pthread_mutex_lock(&openssl_init_mutex)) {
- return -1;
- }
-
- if (open_ssl_connections > 0) {
- --open_ssl_connections;
- }
-
- if (0 == open_ssl_connections && do_initialize_openssl) {
- /* Unsetting these allows the rabbitmq-c library to be unloaded
- * safely. We do leak the amqp_openssl_lockarray. Which is only
- * an issue if you repeatedly unload and load the library
- */
- ERR_remove_state(0);
- FIPS_mode_set(0);
- CRYPTO_set_locking_callback(NULL);
- CRYPTO_set_id_callback(NULL);
- ENGINE_cleanup();
- CONF_modules_free();
- EVP_cleanup();
- CRYPTO_cleanup_all_ex_data();
- ERR_free_strings();
- openssl_initialized = 0;
+int amqp_uninitialize_ssl_library(void) {
+ int status;
+ CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
+
+ if (openssl_connections > 0) {
+ status = AMQP_STATUS_SOCKET_INUSE;
+ goto out;
+ }
+
+ ERR_remove_state(0);
+ FIPS_mode_set(0);
+
+ CRYPTO_set_locking_callback(NULL);
+ CRYPTO_set_id_callback(NULL);
+ {
+ int i;
+ for (i = 0; i < CRYPTO_num_locks(); i++) {
+ pthread_mutex_destroy(&amqp_openssl_lockarray[i]);
+ }
+ free(amqp_openssl_lockarray);
+ }
+
+ ENGINE_cleanup();
+ CONF_modules_free();
+ EVP_cleanup();
+ CRYPTO_cleanup_all_ex_data();
+ ERR_free_strings();
#if (OPENSSL_VERSION_NUMBER >= 0x10002003L)
- SSL_COMP_free_compression_methods();
+ SSL_COMP_free_compression_methods();
#endif
- }
- pthread_mutex_unlock(&openssl_init_mutex);
- return 0;
+ openssl_initialized = 0;
+
+ status = AMQP_STATUS_OK;
+out:
+ CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
+ return status;
}
diff --git a/librabbitmq/amqp_openssl_bio.c b/librabbitmq/amqp_openssl_bio.c
index df0ce2d..b0f4aad 100644
--- a/librabbitmq/amqp_openssl_bio.c
+++ b/librabbitmq/amqp_openssl_bio.c
@@ -23,8 +23,8 @@
#include "amqp_openssl_bio.h"
#include "amqp_socket.h"
-#include "threads.h"
+#include <assert.h>
#include <errno.h>
#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__)))
#ifndef WIN32_LEAN_AND_MEAN
@@ -40,11 +40,9 @@
#define AMQP_USE_AMQP_BIO
#endif
-#ifdef AMQP_USE_AMQP_BIO
-
-static pthread_once_t bio_init_once = PTHREAD_ONCE_INIT;
+static int amqp_ssl_bio_initialized = 0;
-static int bio_initialized = 0;
+#ifdef AMQP_USE_AMQP_BIO
static BIO_METHOD amqp_bio_method;
static int amqp_openssl_bio_should_retry(int res) {
@@ -133,24 +131,23 @@ static int BIO_meth_set_read(BIO_METHOD *biom, int (*rfn)(BIO *, char *, int)) {
biom->bread = rfn;
return 0;
}
-#endif
+#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
+#endif /* AMQP_USE_AMQP_BIO */
-static void amqp_openssl_bio_init(void) {
+void amqp_openssl_bio_init(void) {
+ assert(!amqp_ssl_bio_initialized);
+#ifdef AMQP_USE_AMQP_BIO
memcpy(&amqp_bio_method, BIO_s_socket(), sizeof(amqp_bio_method));
BIO_meth_set_write(&amqp_bio_method, amqp_openssl_bio_write);
BIO_meth_set_read(&amqp_bio_method, amqp_openssl_bio_read);
+#endif
- bio_initialized = 1;
+ amqp_ssl_bio_initialized = 1;
}
-#endif /* AMQP_USE_AMQP_BIO */
-
BIO_METHOD *amqp_openssl_bio(void) {
+ assert(amqp_ssl_bio_initialized);
#ifdef AMQP_USE_AMQP_BIO
- if (!bio_initialized) {
- pthread_once(&bio_init_once, amqp_openssl_bio_init);
- }
-
return &amqp_bio_method;
#else
return BIO_s_socket();
diff --git a/librabbitmq/amqp_openssl_bio.h b/librabbitmq/amqp_openssl_bio.h
index 638717d..68be2de 100644
--- a/librabbitmq/amqp_openssl_bio.h
+++ b/librabbitmq/amqp_openssl_bio.h
@@ -25,6 +25,8 @@
#include <openssl/bio.h>
+void amqp_openssl_bio_init(void);
+
BIO_METHOD* amqp_openssl_bio(void);
#endif /* ifndef AMQP_OPENSSL_BIO */
diff --git a/librabbitmq/amqp_ssl_socket.h b/librabbitmq/amqp_ssl_socket.h
index b87233d..9977ae4 100644
--- a/librabbitmq/amqp_ssl_socket.h
+++ b/librabbitmq/amqp_ssl_socket.h
@@ -181,19 +181,18 @@ int AMQP_CALL amqp_ssl_socket_set_ssl_versions(amqp_socket_t *self,
amqp_tls_version_t max);
/**
- * Sets whether rabbitmq-c initializes the underlying SSL library.
+ * Sets whether rabbitmq-c will initialize OpenSSL.
*
- * For SSL libraries that require a one-time initialization across
- * a whole program (e.g., OpenSSL) this sets whether or not rabbitmq-c
- * will initialize the SSL library when the first call to
- * amqp_open_socket() is made. You should call this function with
+ * OpenSSL requires a one-time initialization across a whole program, this sets
+ * whether or not rabbitmq-c will initialize the SSL library when the first call
+ * to amqp_ssl_socket_new() is made. You should call this function with
* do_init = 0 if the underlying SSL library is initialized somewhere else
* the program.
*
* Failing to initialize or double initialization of the SSL library will
* result in undefined behavior
*
- * By default rabbitmq-c will initialize the underlying SSL library
+ * By default rabbitmq-c will initialize the underlying SSL library.
*
* NOTE: calling this function after the first socket has been opened with
* amqp_open_socket() will not have any effect.
@@ -207,6 +206,34 @@ int AMQP_CALL amqp_ssl_socket_set_ssl_versions(amqp_socket_t *self,
AMQP_PUBLIC_FUNCTION
void AMQP_CALL amqp_set_initialize_ssl_library(amqp_boolean_t do_initialize);
+/**
+ * Initialize the underlying SSL/TLS library.
+ *
+ * The OpenSSL library requires a one-time initialization across the whole
+ * program.
+ *
+ * This function unconditionally initializes OpenSSL so that rabbitmq-c may
+ * use it.
+ *
+ * This function is thread-safe, and may be called more than once.
+ *
+ * \return AMQP_STATUS_OK on success.
+ *
+ * \since v0.9.0
+ */
+AMQP_PUBLIC_FUNCTION
+int AMQP_CALL amqp_initialize_ssl_library(void);
+
+/**
+ * Uninitialize the underlying SSL/TLS library.
+ *
+ * \return AMQP_STATUS_OK on success.
+ *
+ * \since v0.9.0
+ */
+AMQP_PUBLIC_FUNCTION
+int AMQP_CALL amqp_uninitialize_ssl_library(void);
+
AMQP_END_DECLS
#endif /* AMQP_SSL_H */
diff --git a/librabbitmq/win32/threads.c b/librabbitmq/win32/threads.c
index cbe103b..cce3158 100644
--- a/librabbitmq/win32/threads.c
+++ b/librabbitmq/win32/threads.c
@@ -49,3 +49,8 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex) {
ReleaseSRWLockExclusive(mutex);
return 0;
}
+
+int pthread_mutex_destroy(pthread_mutex_t *mutex) {
+ /* SRW's do not require destruction. */
+ return 0;
+}
diff --git a/librabbitmq/win32/threads.h b/librabbitmq/win32/threads.h
index 5862df4..3ae571f 100644
--- a/librabbitmq/win32/threads.h
+++ b/librabbitmq/win32/threads.h
@@ -44,5 +44,6 @@ DWORD pthread_self(void);
int pthread_mutex_init(pthread_mutex_t *, void *attr);
int pthread_mutex_lock(pthread_mutex_t *);
int pthread_mutex_unlock(pthread_mutex_t *);
+int pthread_mutex_destroy(pthread_mutex_t *);
#endif /* AMQP_THREAD_H */