diff options
author | Alan Antonuk <alan.antonuk@gmail.com> | 2017-12-04 20:33:51 -0800 |
---|---|---|
committer | Alan Antonuk <alan.antonuk@gmail.com> | 2017-12-10 22:38:25 -0800 |
commit | b80de27b8782772dfb3fe8253940663697f680ee (patch) | |
tree | 833a69221f00ead116b849e9a3a01bbe90c10b4f | |
parent | cdfce5a328a59bc9bb51428be0585715546c4634 (diff) | |
download | rabbitmq-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.c | 209 | ||||
-rw-r--r-- | librabbitmq/amqp_openssl_bio.c | 25 | ||||
-rw-r--r-- | librabbitmq/amqp_openssl_bio.h | 2 | ||||
-rw-r--r-- | librabbitmq/amqp_ssl_socket.h | 39 | ||||
-rw-r--r-- | librabbitmq/win32/threads.c | 5 | ||||
-rw-r--r-- | librabbitmq/win32/threads.h | 1 |
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 */ |