From: Timo Sirainen Date: Mon, 20 Jun 2016 09:58:08 +0000 (+0300) Subject: lib-dcrypt, lib-ssl-iostream: Share OpenSSL init/deinit code. X-Git-Tag: 2.2.25.rc1~96 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bff052bd29dbf7175ee6cd14bd14bcea1900b869;p=thirdparty%2Fdovecot%2Fcore.git lib-dcrypt, lib-ssl-iostream: Share OpenSSL init/deinit code. --- diff --git a/src/Makefile.am b/src/Makefile.am index 086b7c8fb5..dd76173d8a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,11 +9,11 @@ LIBDOVECOT_SUBDIRS = \ lib-auth \ lib-master \ lib-charset \ + lib-ssl-iostream \ lib-dcrypt \ lib-dns \ lib-dict \ lib-sasl \ - lib-ssl-iostream \ lib-stats \ lib-http \ lib-fs \ diff --git a/src/lib-dcrypt/Makefile.am b/src/lib-dcrypt/Makefile.am index c7618f0dc5..af25c10b53 100644 --- a/src/lib-dcrypt/Makefile.am +++ b/src/lib-dcrypt/Makefile.am @@ -3,7 +3,8 @@ pkglib_LTLIBRARIES = AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ - -I$(top_srcdir)/src/lib-test + -I$(top_srcdir)/src/lib-test \ + -I$(top_srcdir)/src/lib-ssl-iostream libdcrypt_la_SOURCES = \ dcrypt.c \ @@ -16,7 +17,8 @@ libdcrypt_la_CFLAGS = $(AM_CPPFLAGS) \ if BUILD_OPENSSL pkglib_LTLIBRARIES += libdcrypt_openssl.la libdcrypt_openssl_la_SOURCES = dcrypt-openssl.c dcrypt.c -libdcrypt_openssl_la_LDFLAGS = -module -avoid-version -shared $(SSL_LIBS) ../lib/liblib.la +libdcrypt_openssl_la_LDFLAGS = -module -avoid-version -shared ../lib-ssl-iostream/libdovecot_openssl_common.la ../lib/liblib.la +libdcrypt_openssl_la_DEPENDENCIES = ../lib-ssl-iostream/libdovecot_openssl_common.la ../lib/liblib.la libdcrypt_openssl_la_CFLAGS = $(AM_CPPFLAGS) \ -DDCRYPT_MODULE_DIR=\"$(pkglibdir)\" $(SSL_CFLAGS) diff --git a/src/lib-dcrypt/dcrypt-openssl.c b/src/lib-dcrypt/dcrypt-openssl.c index af230ee028..e7cfbb5374 100644 --- a/src/lib-dcrypt/dcrypt-openssl.c +++ b/src/lib-dcrypt/dcrypt-openssl.c @@ -6,6 +6,7 @@ #include "randgen.h" #include "array.h" #include "module-dir.h" +#include "dovecot-openssl-common.h" #include #include #include @@ -2059,14 +2060,11 @@ static struct dcrypt_vfs dcrypt_openssl_vfs = { void dcrypt_openssl_init(struct module *module ATTR_UNUSED) { - OpenSSL_add_all_algorithms(); - ERR_load_crypto_strings(); + dovecot_openssl_common_global_ref(); dcrypt_set_vfs(&dcrypt_openssl_vfs); } void dcrypt_openssl_deinit(void) { -#if OPENSSL_API_COMPAT < 0x10100000L - OBJ_cleanup(); -#endif + dovecot_openssl_common_global_unref(); } diff --git a/src/lib-ssl-iostream/Makefile.am b/src/lib-ssl-iostream/Makefile.am index 7b4ff2c309..769de87b28 100644 --- a/src/lib-ssl-iostream/Makefile.am +++ b/src/lib-ssl-iostream/Makefile.am @@ -10,8 +10,13 @@ AM_CPPFLAGS = \ if BUILD_OPENSSL module_LTLIBRARIES = libssl_iostream_openssl.la +noinst_LTLIBRARIES += libdovecot_openssl_common.la +libdovecot_openssl_common_la_LIBADD = $(SSL_LIBS) +libdovecot_openssl_common_la_SOURCES = \ + dovecot-openssl-common.c + libssl_iostream_openssl_la_LDFLAGS = -module -avoid-version -libssl_iostream_openssl_la_LIBADD = $(SSL_LIBS) +libssl_iostream_openssl_la_LIBADD = libdovecot_openssl_common.la libssl_iostream_openssl_la_SOURCES = \ iostream-openssl.c \ iostream-openssl-common.c \ @@ -25,6 +30,9 @@ libssl_iostream_la_SOURCES = \ iostream-ssl.c \ $(ssl_sources) +noinst_HEADERS = \ + dovecot-openssl-common.h + headers = \ iostream-openssl.h \ iostream-ssl.h \ diff --git a/src/lib-ssl-iostream/dovecot-openssl-common.c b/src/lib-ssl-iostream/dovecot-openssl-common.c new file mode 100644 index 0000000000..7e622fb045 --- /dev/null +++ b/src/lib-ssl-iostream/dovecot-openssl-common.c @@ -0,0 +1,81 @@ +/* Copyright (c) 2016 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "dovecot-openssl-common.h" + +#include +#include +#include + +static int openssl_init_refcount = 0; +static ENGINE *dovecot_openssl_engine; + +void dovecot_openssl_common_global_ref(void) +{ + unsigned char buf; + + if (openssl_init_refcount++ > 0) + return; + + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + + /* PRNG initialization might want to use /dev/urandom, make sure it + does it before chrooting. We might not have enough entropy at + the first try, so this function may fail. It's still been + initialized though. */ + (void)RAND_bytes(&buf, 1); +} + +bool dovecot_openssl_common_global_unref(void) +{ + i_assert(openssl_init_refcount > 0); + + if (--openssl_init_refcount > 0) + return TRUE; + + if (dovecot_openssl_engine != NULL) { + ENGINE_finish(dovecot_openssl_engine); + dovecot_openssl_engine = NULL; + } +#if OPENSSL_VERSION_NUMBER < 0x10001000L + OBJ_cleanup(); +#endif +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_COMP_free_compression_methods(); +#endif + ENGINE_cleanup(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); + ERR_remove_state(0); + ERR_free_strings(); + return FALSE; +} + +int dovecot_openssl_common_global_set_engine(const char *engine, + const char **error_r) +{ + if (dovecot_openssl_engine != NULL) + return 1; + + ENGINE_load_builtin_engines(); + dovecot_openssl_engine = ENGINE_by_id(engine); + if (dovecot_openssl_engine == NULL) { + *error_r = t_strdup_printf("Unknown engine '%s'", engine); + return 0; + } + if (ENGINE_init(dovecot_openssl_engine) == 0) { + *error_r = t_strdup_printf("ENGINE_init(%s) failed", engine); + ENGINE_free(dovecot_openssl_engine); + dovecot_openssl_engine = NULL; + return -1; + } + if (ENGINE_set_default_RSA(dovecot_openssl_engine) == 0) + i_unreached(); + if (ENGINE_set_default_DSA(dovecot_openssl_engine) == 0) + i_unreached(); + if (ENGINE_set_default_ciphers(dovecot_openssl_engine) == 0) + i_unreached(); + return 1; +} diff --git a/src/lib-ssl-iostream/dovecot-openssl-common.h b/src/lib-ssl-iostream/dovecot-openssl-common.h new file mode 100644 index 0000000000..31854d3b1b --- /dev/null +++ b/src/lib-ssl-iostream/dovecot-openssl-common.h @@ -0,0 +1,16 @@ +#ifndef DOVECOT_OPENSSL_COMMON_H +#define DOVECOT_OPENSSL_COMMON_H + +/* Initialize OpenSSL if this is the first instance. + Increase initialization reference count. */ +void dovecot_openssl_common_global_ref(void); +/* Deinitialize OpenSSL if this is the last instance. Returns TRUE if there + are more instances left. */ +bool dovecot_openssl_common_global_unref(void); + +/* Set OpenSSL engine if it's not already set. Returns 1 on success, 0 if engine + is unknown, -1 on other error. error_r is set on 0/-1. */ +int dovecot_openssl_common_global_set_engine(const char *engine, + const char **error_r); + +#endif diff --git a/src/lib-ssl-iostream/iostream-openssl-context.c b/src/lib-ssl-iostream/iostream-openssl-context.c index b976a0c03c..2b6199df91 100644 --- a/src/lib-ssl-iostream/iostream-openssl-context.c +++ b/src/lib-ssl-iostream/iostream-openssl-context.c @@ -3,14 +3,13 @@ #include "lib.h" #include "safe-memset.h" #include "iostream-openssl.h" +#include "dovecot-openssl-common.h" #include #include -#include #include #include #include -#include #if !defined(OPENSSL_NO_ECDH) && OPENSSL_VERSION_NUMBER >= 0x10000000L # define HAVE_ECDH @@ -22,7 +21,6 @@ struct ssl_iostream_password_context { }; static bool ssl_global_initialized = FALSE; -static ENGINE *ssl_iostream_engine; int dovecot_ssl_extdata_index; static int ssl_iostream_init_global(const struct ssl_iostream_settings *set, @@ -542,52 +540,36 @@ void openssl_iostream_context_deinit(struct ssl_iostream_context *ctx) void openssl_iostream_global_deinit(void) { - if (ssl_iostream_engine != NULL) - ENGINE_finish(ssl_iostream_engine); - ENGINE_cleanup(); - EVP_cleanup(); - CRYPTO_cleanup_all_ex_data(); - ERR_remove_state(0); - ERR_free_strings(); + dovecot_openssl_common_global_unref(); } static int ssl_iostream_init_global(const struct ssl_iostream_settings *set, const char **error_r) { static char dovecot[] = "dovecot"; - unsigned char buf; + const char *error; if (ssl_global_initialized) return 0; ssl_global_initialized = TRUE; - SSL_library_init(); - SSL_load_error_strings(); - OpenSSL_add_all_algorithms(); + dovecot_openssl_common_global_ref(); dovecot_ssl_extdata_index = SSL_get_ex_new_index(0, dovecot, NULL, NULL, NULL); - /* PRNG initialization might want to use /dev/urandom, make sure it - does it before chrooting. We might not have enough entropy at - the first try, so this function may fail. It's still been - initialized though. */ - (void)RAND_bytes(&buf, 1); - if (set->crypto_device != NULL && *set->crypto_device != '\0') { - ENGINE_load_builtin_engines(); - ssl_iostream_engine = ENGINE_by_id(set->crypto_device); - if (ssl_iostream_engine == NULL) { - *error_r = t_strdup_printf( + switch (dovecot_openssl_common_global_set_engine(set->crypto_device, &error)) { + case 0: + error = t_strdup_printf( "Unknown ssl_crypto_device: %s", set->crypto_device); + /* fall through */ + case -1: + *error_r = error; /* we'll deinit at exit in any case */ return -1; } - ENGINE_init(ssl_iostream_engine); - ENGINE_set_default_RSA(ssl_iostream_engine); - ENGINE_set_default_DSA(ssl_iostream_engine); - ENGINE_set_default_ciphers(ssl_iostream_engine); } return 0; }