SSL negotiation failures with TLSv1 and TLSv1.3 against gateway/ical on Debian 10
-
It’s all well and nice to forbid TLSv1.0/1.1, but for one, TLSv1 is not fundamentally broken, iff you use proper cipherlists and implement remedies against BREACH, BEAST and CRIME (i.e., no compression, and BEAST/BREACH is generally not applicable to email servers/hard to transfer to email traffic), which I am, and for another, there are compatability requirements in environments (looking at you, Outlook 2007) which require TLSv1 for clients. So, yes, there is actually a case in point for enabling TLSv1 on Kopano. For (unauthenticated) SMTP, a similar point can be made (i.e., interoperability requirements, and some encryption is better than none at all).
Anyway, thanks for the heads up about Kopano 8.7 not being officially supported on Buster; I’ve always had self-built packages, and never noticed this. I’m currently trying to backport the changes to the ECChannel infrastructure (i.e. OpenSSL initialization and teardown) and then post the patches for that.
-
@thctlo said in SSL negotiation failures with TLSv1 and TLSv1.3 against gateway/ical on Debian 10:
this means that system wide settings for MinVersion and CipherSettings from openssl.cnf are ignored
There is a bug in openssl thast most probley the cause of it.
Which bug are you referring to? Have a link? I’d wager that it’s much rather the initialization of ECChannel which is the culprit here, as it does not use the “new” OpenSSL 1.1±way of initializing the SSL library, but rather the old way with multiple function calls. But that’s back-portable.
-
The following patch does the trick (basically, it modifies the Kopano SSL-setup code to be much like Apaches), and also updates some parts of the TLS/SSL-code to comply with the documentation of OpenSSL (don’t SSL_shutdown on failed sockets, etc.):
diff --git a/common/ECChannel.cpp b/common/ECChannel.cpp index 85c774470..cc2215250 100644 --- a/common/ECChannel.cpp +++ b/common/ECChannel.cpp @@ -62,7 +62,7 @@ HRESULT ECChannel::HrSetCtx(ECConfig *lpConfig) const char *ssl_ciphers = lpConfig->GetSetting("ssl_ciphers"); const char *ssl_curves = lpConfig->GetSetting("ssl_curves"); char *ssl_name = NULL; - int ssl_op = 0, ssl_include = 0, ssl_exclude = 0; + int ssl_include = 0, ssl_exclude = 0; #if !defined(OPENSSL_NO_ECDH) && defined(NID_X9_62_prime256v1) EC_KEY *ecdh; #endif @@ -90,15 +90,37 @@ HRESULT ECChannel::HrSetCtx(ECConfig *lpConfig) lpCTX = NULL; } - SSL_library_init(); +#if OPENSSL_VERSION_NUMBER >= 0x1010000fL + // New style init. + OPENSSL_init_ssl(OPENSSL_INIT_ENGINE_ALL_BUILTIN, NULL); + + // Default TLS context for OpenSSL >= 1.1. + lpCTX = SSL_CTX_new(TLS_server_method()); +#else // OPENSSL_VERSION_NUMBER < 0x1010000fL + // Old style init, modelled after Apache mod_ssl. + ERR_load_crypto_strings(); SSL_load_error_strings(); + SSL_library_init(); +# ifndef OPENSSL_NO_ENGINE + ENGINE_load_builtin_engines(); +# endif // !OPENSSL_NO_ENGINE + OpenSSL_add_all_algorithms(); + OPENSSL_load_builtin_modules(); // enable *all* server methods, not just ssl2 and ssl3, but also tls1 and tls1.1 lpCTX = SSL_CTX_new(SSLv23_server_method()); +#endif // OPENSSL_VERSION_NUMBER + #ifndef SSL_OP_NO_RENEGOTIATION -# define SSL_OP_NO_RENEGOTIATION 0 /* unavailable in openSSL 1.0 */ +# define SSL_OP_NO_RENEGOTIATION 0 /* unavailable in OpenSSL 1.0 */ +#endif +#ifndef SSL_OP_NO_COMPRESSION +# define SSL_OP_NO_COMPRESSION 0 +#endif +#ifndef SSL_TXT_TLSV1_3 +# define SSL_TXT_TLSV1_3 "TLSv1.3" #endif - SSL_CTX_set_options(lpCTX, SSL_OP_ALL | SSL_OP_NO_RENEGOTIATION); + SSL_CTX_set_options(lpCTX, SSL_OP_ALL | SSL_OP_NO_RENEGOTIATION | SSL_OP_NO_COMPRESSION); ssl_name = strtok(ssl_protocols.get(), " "); while(ssl_name != NULL) { int ssl_proto = 0; @@ -109,26 +131,25 @@ HRESULT ECChannel::HrSetCtx(ECConfig *lpConfig) ssl_neg = true; } +#ifndef OPENSSL_NO_SSL3 if (strcasecmp(ssl_name, SSL_TXT_SSLV3) == 0) - ssl_proto = 0x02; -#ifdef SSL_TXT_SSLV2 - else if (strcasecmp(ssl_name, SSL_TXT_SSLV2) == 0) ssl_proto = 0x01; -#endif - else if (strcasecmp(ssl_name, SSL_TXT_TLSV1) == 0) - ssl_proto = 0x04; -#ifdef SSL_TXT_TLSV1_1 + else +#endif // !OPENSSL_NO_SSL3 + if (strcasecmp(ssl_name, SSL_TXT_TLSV1) == 0) + ssl_proto = 0x02; +#ifdef SSL_OP_NO_TLSv1_1 else if (strcasecmp(ssl_name, SSL_TXT_TLSV1_1) == 0) - ssl_proto = 0x08; -#endif -#ifdef SSL_TXT_TLSV1_2 + ssl_proto = 0x04; +#endif // SSL_OP_NO_TLSv1_1 +#ifdef SSL_OP_NO_TLSv1_2 else if (strcasecmp(ssl_name, SSL_TXT_TLSV1_2) == 0) - ssl_proto = 0x10; -#endif + ssl_proto = 0x08; +#endif // SSL_OP_NO_TLSv1_2 #ifdef SSL_OP_NO_TLSv1_3 - else if (strcasecmp(ssl_name, "TLSv1.3") == 0) - ssl_proto = 0x20; -#endif + else if (strcasecmp(ssl_name, SSL_TXT_TLSV1_3) == 0) + ssl_proto = 0x10; +#endif // SSL_OP_NO_TLSv1_3 else if (!ssl_neg) { ec_log_err("Unknown protocol \"%s\" in ssl_protocols setting", ssl_name); hr = MAPI_E_CALL_FAILED; @@ -146,26 +167,123 @@ HRESULT ECChannel::HrSetCtx(ECConfig *lpConfig) if (ssl_include != 0) // Exclude everything, except those that are included (and let excludes still override those) ssl_exclude |= 0x1f & ~ssl_include; + +#if OPENSSL_VERSION_NUMBER >= 0x1010000fL + int ver; + + // Find version range top for proto version setup. +# ifdef SSL_OP_NO_TLSv1_3 + if (ssl_include == 0 && (ssl_exclude & 0x10) == 0) + // No explicit excludes at top of version list; accept higher protocols. + ver = 0; + else if ((ssl_exclude & 0x10) == 0) + ver = TLS1_3_VERSION; +# else // !SSL_OP_NO_TLSv1_3 + if (ssl_include == 0 && (ssl_exclude & 0x08) == 0) + // No explicit excludes at top of version list; accept higher protocols. + ver = 0; +# endif // SSL_OP_NO_TLSv1_3 + else if ((ssl_exclude & 0x08) == 0) + ver = TLS1_2_VERSION; + else if ((ssl_exclude & 0x04) == 0) + ver = TLS1_1_VERSION; + else if ((ssl_exclude & 0x02) == 0) + ver = TLS1_VERSION; +# ifndef OPENSSL_NO_SSL3 + else if ((ssl_exclude & 0x01) == 0) + ver = SSL3_VERSION; +# endif // OPENSSL_NO_SSL3 + else { + ec_log_err("All available TLS protocols excluded, cannot set up SSL context"); + hr = MAPI_E_CALL_FAILED; + goto exit; + } + ec_log_info("Maximum TLS protocol version to use: 0x%lx", ver); + SSL_CTX_set_max_proto_version(lpCTX, ver); + + // Find version range bottom for proto version setup. This loops over the + // range of available protocols and only allows a consecutive chain, just + // like Apache does. +# ifdef SSL_OP_NO_TLSv1_3 + if (ver == 0) + // Allways allow highest available protocol. + ver = TLS1_3_VERSION; + if (ver == TLS1_3_VERSION && (ssl_exclude & 0x08) == 0) + ver = TLS1_2_VERSION; +# else // !SSL_OP_NO_TLSv1_3 + if (ver == 0) + // Always allow highest available protocol. + ver = TLS1_2_VERSION; +# endif // SSL_OP_NO_TLSv1_3 + if (ver == TLS1_2_VERSION && (ssl_exclude & 0x04) == 0) + ver = TLS1_1_VERSION; + if (ver == TLS1_1_VERSION && (ssl_exclude & 0x02) == 0) + ver = TLS1_VERSION; +# ifndef OPENSSL_NO_SSL3 + if (ver == TLS1_VERSION && (ssl_exclude & 0x01) == 0) + ver = SSL3_VERSION; +# endif // !OPENSSL_NO_SSL3 + ec_log_info("Minimum TLS protocol version to use: 0x%lx", ver); + SSL_CTX_set_min_proto_version(lpCTX, ver); +#else // OPENSSL_VERSION_NUMBER < 0x1010000fL + long ssl_opset = 0, ssl_opclear = 0; + +# ifndef OPENSSL_NO_SSL2 + // Never offer SSLv2. + ssl_opset = SSL_OP_NO_SSLv2; +# endif // !OPENSSL_NO_SSL2 + + // Find flags to set and clear. +# ifndef OPENSSL_NO_SSL3 if ((ssl_exclude & 0x01) != 0) - ssl_op |= SSL_OP_NO_SSLv2; + ssl_opset |= SSL_OP_NO_SSLv3; + else + ssl_opclear |= SSL_OP_NO_SSLv3; +# endif // !OPENSSL_NO_SSL3 if ((ssl_exclude & 0x02) != 0) - ssl_op |= SSL_OP_NO_SSLv3; + ssl_opset |= SSL_OP_NO_TLSv1; + else + ssl_opclear |= SSL_OP_NO_TLSv1; +# ifdef SSL_OP_NO_TLSv1_1 if ((ssl_exclude & 0x04) != 0) - ssl_op |= SSL_OP_NO_TLSv1; -#ifdef SSL_OP_NO_TLSv1_1 + ssl_opset |= SSL_OP_NO_TLSv1_1; + else + ssl_opclear |= SSL_OP_NO_TLSv1_1; +# endif // SSL_OP_NO_TLSv1_1 +# ifdef SSL_OP_NO_TLSv1_2 if ((ssl_exclude & 0x08) != 0) - ssl_op |= SSL_OP_NO_TLSv1_1; -#endif -#ifdef SSL_OP_NO_TLSv1_2 + ssl_opset |= SSL_OP_NO_TLSv1_2; + else + ssl_opclear |= SSL_OP_NO_TLSv1_2; +# endif // SSL_OP_NO_TLSv1_2 +# ifdef SSL_OP_NO_TLSv1_3 if ((ssl_exclude & 0x10) != 0) - ssl_op |= SSL_OP_NO_TLSv1_2; -#endif -#ifdef SSL_OP_NO_TLSv1_3 - if ((ssl_exclude & 0x20) != 0) - ssl_op |= SSL_OP_NO_TLSv1_3; -#endif - if (ssl_protocols) - SSL_CTX_set_options(lpCTX, ssl_op); + ssl_opset |= SSL_OP_NO_TLSv1_3; + else + ssl_opclear |= SSL_OP_NO_TLSv1_3; +# endif // SSL_OP_NO_TLSv1_3 + + // Check whether any options were found. + if (ssl_opclear == 0) { + ec_log_err("All available TLS protocols excluded, cannot set up SSL context"); + hr = MAPI_E_CALL_FAILED; + goto exit; + } + + // Set up options to set up. + ssl_opset = ssl_opset & ~SSL_CTX_get_options(lpCTX); + if (ssl_opset != 0) { + ec_log_info("Setting options 0x%lx on SSL context", ssl_opset); + SSL_CTX_set_options(lpCTX, ssl_opset); + } + + // Clear out options that should be cleared. + ssl_opclear = ssl_opclear & SSL_CTX_get_options(lpCTX); + if (ssl_opclear != 0) { + ec_log_warn("Resetting already set options 0x%lx on SSL context", ssl_opclear); + SSL_CTX_clear_options(lpCTX, ssl_opclear); + } +#endif // OPENSSL_VERSION_NUMBER #if !defined(OPENSSL_NO_ECDH) && defined(NID_X9_62_prime256v1) ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); @@ -266,22 +384,30 @@ HRESULT ECChannel::HrEnableTLS(void) hr = MAPI_E_CALL_FAILED; goto exit; } - SSL_clear(lpSSL); + +#if OPENSSL_VERSION_NUMBER >= 0x1010100fL + ec_log_info("ECChannel::HrEnableTLS(): min TLS version 0x%lx", SSL_get_min_proto_version(lpSSL)); + ec_log_info("ECChannel::HrEnableTLS(): max TLS version 0x%lx", SSL_get_max_proto_version(lpSSL)); +#endif + ec_log_info("ECChannel::HrEnableTLS(): TLS flags 0x%lx", SSL_get_options(lpSSL)); + if (SSL_set_fd(lpSSL, fd) != 1) { ec_log_err("ECChannel::HrEnableTLS(): SSL_set_fd failed"); hr = MAPI_E_CALL_FAILED; goto exit; } - SSL_set_accept_state(lpSSL); + ERR_clear_error(); if ((rc = SSL_accept(lpSSL)) != 1) { - ec_log_err("ECChannel::HrEnableTLS(): SSL_accept failed: %d", SSL_get_error(lpSSL, rc)); + int err = SSL_get_error(lpSSL, rc); + ec_log_err("ECChannel::HrEnableTLS(): SSL_accept failed: %d", err); + if (err != SSL_ERROR_SYSCALL || err != SSL_ERROR_SSL) + SSL_shutdown(lpSSL); hr = MAPI_E_CALL_FAILED; goto exit; } exit: if (hr != hrSuccess && lpSSL) { - SSL_shutdown(lpSSL); SSL_free(lpSSL); lpSSL = NULL; } diff --git a/common/SSLUtil.cpp b/common/SSLUtil.cpp index b2b3e9600..e496b38ec 100644 --- a/common/SSLUtil.cpp +++ b/common/SSLUtil.cpp @@ -65,16 +65,25 @@ void ssl_threading_cleanup() { */ void SSL_library_cleanup() { -#ifndef OPENSSL_NO_ENGINE +#if OPENSSL_VERSION_NUMBER >= 0x1010000fL + // No cleanup required, is a current OpenSSL. +#else // OPENSSL_VERSION_NUMBER < 0x1010000fL + // Clean up any possible allocations. + OBJ_cleanup(); + CONF_modules_free(); + EVP_cleanup(); +# ifndef OPENSSL_NO_ENGINE ENGINE_cleanup(); -#endif +# endif +# if OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined(OPENSSL_NO_COMP) + SSL_COMP_free_compression_methods(); +# endif ERR_free_strings(); -#ifdef OLD_API +# ifdef OLD_API ERR_remove_state(0); -#endif - EVP_cleanup(); +# endif CRYPTO_cleanup_all_ex_data(); - CONF_modules_unload(0); +#endif // OPENSSL_VERSION_NUMBER } void ssl_random_init() diff --git a/common/include/kopano/ECChannel.h b/common/include/kopano/ECChannel.h index b43c0fd99..0a4279395 100644 --- a/common/include/kopano/ECChannel.h +++ b/common/include/kopano/ECChannel.h @@ -14,6 +14,7 @@ #include <cstdio> #include <iostream> #include <sys/socket.h> +#include <openssl/opensslconf.h> #include <openssl/ssl.h> #include <openssl/err.h> #include <kopano/ECConfig.h>
It applies to 8.7.x and should also - with some updates - apply to 9.0.x (you’d have to decide whether you want to keep the min/max-protocol specifications, or include an Apache-like heuristic to derive min/max from a list, as this patch does).
-
Against current kc-8.7.x head, with some additional patches due to the seemingly incomplete implementation of the usage of std::atomic<SSL_CTX*>:
diff --git a/common/ECChannel.cpp b/common/ECChannel.cpp index 58f9dd746..0b8719719 100644 --- a/common/ECChannel.cpp +++ b/common/ECChannel.cpp @@ -48,12 +48,12 @@ std::atomic<SSL_CTX *> ECChannel::lpCTX{nullptr}; HRESULT ECChannel::HrSetCtx(ECConfig *lpConfig) { + HRESULT hr = MAPI_E_CALL_FAILED; if (lpConfig == NULL) { ec_log_err("ECChannel::HrSetCtx(): invalid parameters"); - return MAPI_E_CALL_FAILED; + return hr; } - HRESULT hr = MAPI_E_CALL_FAILED; const char *szFile = nullptr, *szPath = nullptr;; auto cert_file = lpConfig->GetSetting("ssl_certificate_file"); auto key_file = lpConfig->GetSetting("ssl_private_key_file"); @@ -61,40 +61,69 @@ HRESULT ECChannel::HrSetCtx(ECConfig *lpConfig) const char *ssl_ciphers = lpConfig->GetSetting("ssl_ciphers"); const char *ssl_curves = lpConfig->GetSetting("ssl_curves"); char *ssl_name = NULL; - int ssl_op = 0, ssl_include = 0, ssl_exclude = 0; + int ssl_include = 0, ssl_exclude = 0; #if !defined(OPENSSL_NO_ECDH) && defined(NID_X9_62_prime256v1) EC_KEY *ecdh; #endif if (cert_file == nullptr || key_file == nullptr) { ec_log_err("ECChannel::HrSetCtx(): no cert or key file"); - return MAPI_E_CALL_FAILED; + return hr; } auto key_fh = fopen(key_file, "r"); if (key_fh == nullptr) { ec_log_err("ECChannel::HrSetCtx(): cannot open key file"); - return MAPI_E_CALL_FAILED; + return hr; } fclose(key_fh); auto cert_fh = fopen(cert_file, "r"); if (cert_fh == nullptr) { ec_log_err("ECChannel::HrSetCtx(): cannot open cert file"); - return MAPI_E_CALL_FAILED; + return hr; } fclose(cert_fh); +#if OPENSSL_VERSION_NUMBER >= 0x1010000fL + // New style init. + if (lpCTX == nullptr) + OPENSSL_init_ssl(OPENSSL_INIT_ENGINE_ALL_BUILTIN, NULL); + + // Default TLS context for OpenSSL >= 1.1, enables all methods. + auto newctx = SSL_CTX_new(TLS_server_method()); +#else // OPENSSL_VERSION_NUMBER < 0x1010000fL + // Old style init, modelled after Apache mod_ssl. if (lpCTX == nullptr) { - SSL_library_init(); + ERR_load_crypto_strings(); SSL_load_error_strings(); + SSL_library_init(); +# ifndef OPENSSL_NO_ENGINE + ENGINE_load_builtin_engines(); +# endif // !OPENSSL_NO_ENGINE + OpenSSL_add_all_algorithms(); + OPENSSL_load_builtin_modules(); } // enable *all* server methods, not just ssl2 and ssl3, but also tls1 and tls1.1 auto newctx = SSL_CTX_new(SSLv23_server_method()); +#endif // OPENSSL_VERSION_NUMBER + + // Check context. + if (newctx == nullptr) { + ec_log_err("ECChannel::HrSetCtx(): failed to create new SSL context"); + return hr; + } + #ifndef SSL_OP_NO_RENEGOTIATION -# define SSL_OP_NO_RENEGOTIATION 0 /* unavailable in openSSL 1.0 */ +# define SSL_OP_NO_RENEGOTIATION 0 /* unavailable in OpenSSL 1.0 */ +#endif +#ifndef SSL_OP_NO_COMPRESSION +# define SSL_OP_NO_COMPRESSION 0 #endif - SSL_CTX_set_options(newctx, SSL_OP_ALL | SSL_OP_NO_RENEGOTIATION); +#ifndef SSL_TXT_TLSV1_3 +# define SSL_TXT_TLSV1_3 "TLSv1.3" +#endif + SSL_CTX_set_options(newctx, SSL_OP_ALL | SSL_OP_NO_RENEGOTIATION | SSL_OP_NO_COMPRESSION); ssl_name = strtok(ssl_protocols.get(), " "); while(ssl_name != NULL) { int ssl_proto = 0; @@ -105,26 +134,25 @@ HRESULT ECChannel::HrSetCtx(ECConfig *lpConfig) ssl_neg = true; } +#ifndef OPENSSL_NO_SSL3 if (strcasecmp(ssl_name, SSL_TXT_SSLV3) == 0) - ssl_proto = 0x02; -#ifdef SSL_TXT_SSLV2 - else if (strcasecmp(ssl_name, SSL_TXT_SSLV2) == 0) ssl_proto = 0x01; -#endif - else if (strcasecmp(ssl_name, SSL_TXT_TLSV1) == 0) - ssl_proto = 0x04; -#ifdef SSL_TXT_TLSV1_1 + else +#endif // !OPENSSL_NO_SSL3 + if (strcasecmp(ssl_name, SSL_TXT_TLSV1) == 0) + ssl_proto = 0x02; +#ifdef SSL_OP_NO_TLSv1_1 else if (strcasecmp(ssl_name, SSL_TXT_TLSV1_1) == 0) - ssl_proto = 0x08; -#endif -#ifdef SSL_TXT_TLSV1_2 + ssl_proto = 0x04; +#endif // SSL_OP_NO_TLSv1_1 +#ifdef SSL_OP_NO_TLSv1_2 else if (strcasecmp(ssl_name, SSL_TXT_TLSV1_2) == 0) - ssl_proto = 0x10; -#endif + ssl_proto = 0x08; +#endif // SSL_OP_NO_TLSv1_2 #ifdef SSL_OP_NO_TLSv1_3 - else if (strcasecmp(ssl_name, "TLSv1.3") == 0) - ssl_proto = 0x20; -#endif + else if (strcasecmp(ssl_name, SSL_TXT_TLSV1_3) == 0) + ssl_proto = 0x10; +#endif // SSL_OP_NO_TLSv1_3 else if (!ssl_neg) { ec_log_err("Unknown protocol \"%s\" in ssl_protocols setting", ssl_name); goto exit; @@ -138,29 +166,124 @@ HRESULT ECChannel::HrSetCtx(ECConfig *lpConfig) ssl_name = strtok(NULL, " "); } + // Exclude everything, except those that are included (and let excludes still override those) if (ssl_include != 0) - // Exclude everything, except those that are included (and let excludes still override those) ssl_exclude |= ~ssl_include; + +#if OPENSSL_VERSION_NUMBER >= 0x1010000fL + int ver; + + // Find version range top for proto version setup. +# ifdef SSL_OP_NO_TLSv1_3 + if (ssl_include == 0 && (ssl_exclude & 0x10) == 0) + // No explicit excludes at top of version list; accept higher protocols. + ver = 0; + else if ((ssl_exclude & 0x10) == 0) + ver = TLS1_3_VERSION; +# else // !SSL_OP_NO_TLSv1_3 + if (ssl_include == 0 && (ssl_exclude & 0x08) == 0) + // No explicit excludes at top of version list; accept higher protocols. + ver = 0; +# endif // SSL_OP_NO_TLSv1_3 + else if ((ssl_exclude & 0x08) == 0) + ver = TLS1_2_VERSION; + else if ((ssl_exclude & 0x04) == 0) + ver = TLS1_1_VERSION; + else if ((ssl_exclude & 0x02) == 0) + ver = TLS1_VERSION; +# ifndef OPENSSL_NO_SSL3 + else if ((ssl_exclude & 0x01) == 0) + ver = SSL3_VERSION; +# endif // OPENSSL_NO_SSL3 + else { + ec_log_err("All available TLS protocols excluded, cannot set up SSL context"); + goto exit; + } + ec_log_info("Maximum TLS protocol version to use: 0x%x", ver); + SSL_CTX_set_max_proto_version(newctx, ver); + + // Find version range bottom for proto version setup. This loops over the + // range of available protocols and only allows a consecutive chain, just + // like Apache does. +# ifdef SSL_OP_NO_TLSv1_3 + if (ver == 0) + // Allways allow highest available protocol. + ver = TLS1_3_VERSION; + if (ver == TLS1_3_VERSION && (ssl_exclude & 0x08) == 0) + ver = TLS1_2_VERSION; +# else // !SSL_OP_NO_TLSv1_3 + if (ver == 0) + // Always allow highest available protocol. + ver = TLS1_2_VERSION; +# endif // SSL_OP_NO_TLSv1_3 + if (ver == TLS1_2_VERSION && (ssl_exclude & 0x04) == 0) + ver = TLS1_1_VERSION; + if (ver == TLS1_1_VERSION && (ssl_exclude & 0x02) == 0) + ver = TLS1_VERSION; +# ifndef OPENSSL_NO_SSL3 + if (ver == TLS1_VERSION && (ssl_exclude & 0x01) == 0) + ver = SSL3_VERSION; +# endif // !OPENSSL_NO_SSL3 + ec_log_info("Minimum TLS protocol version to use: 0x%x", ver); + SSL_CTX_set_min_proto_version(newctx, ver); +#else // OPENSSL_VERSION_NUMBER < 0x1010000fL + long ssl_opset = 0, ssl_opclear = 0; + +# ifndef OPENSSL_NO_SSL2 + // Never offer SSLv2. + ssl_opset = SSL_OP_NO_SSLv2; +# endif // !OPENSSL_NO_SSL2 + + // Find flags to set and clear. +# ifndef OPENSSL_NO_SSL3 if ((ssl_exclude & 0x01) != 0) - ssl_op |= SSL_OP_NO_SSLv2; + ssl_opset |= SSL_OP_NO_SSLv3; + else + ssl_opclear |= SSL_OP_NO_SSLv3; +# endif // !OPENSSL_NO_SSL3 if ((ssl_exclude & 0x02) != 0) - ssl_op |= SSL_OP_NO_SSLv3; + ssl_opset |= SSL_OP_NO_TLSv1; + else + ssl_opclear |= SSL_OP_NO_TLSv1; +# ifdef SSL_OP_NO_TLSv1_1 if ((ssl_exclude & 0x04) != 0) - ssl_op |= SSL_OP_NO_TLSv1; -#ifdef SSL_OP_NO_TLSv1_1 + ssl_opset |= SSL_OP_NO_TLSv1_1; + else + ssl_opclear |= SSL_OP_NO_TLSv1_1; +# endif // SSL_OP_NO_TLSv1_1 +# ifdef SSL_OP_NO_TLSv1_2 if ((ssl_exclude & 0x08) != 0) - ssl_op |= SSL_OP_NO_TLSv1_1; -#endif -#ifdef SSL_OP_NO_TLSv1_2 + ssl_opset |= SSL_OP_NO_TLSv1_2; + else + ssl_opclear |= SSL_OP_NO_TLSv1_2; +# endif // SSL_OP_NO_TLSv1_2 +# ifdef SSL_OP_NO_TLSv1_3 if ((ssl_exclude & 0x10) != 0) - ssl_op |= SSL_OP_NO_TLSv1_2; -#endif -#ifdef SSL_OP_NO_TLSv1_3 - if ((ssl_exclude & 0x20) != 0) - ssl_op |= SSL_OP_NO_TLSv1_3; -#endif - if (ssl_protocols) - SSL_CTX_set_options(newctx, ssl_op); + ssl_opset |= SSL_OP_NO_TLSv1_3; + else + ssl_opclear |= SSL_OP_NO_TLSv1_3; +# endif // SSL_OP_NO_TLSv1_3 + + // Check whether any options were found. + if (ssl_opclear == 0) { + ec_log_err("All available TLS protocols excluded, cannot set up SSL context"); + goto exit; + } + + // Set up options to set up. + ssl_opset = ssl_opset & ~SSL_CTX_get_options(newctx); + if (ssl_opset != 0) { + ec_log_info("Setting options 0x%lx on SSL context", ssl_opset); + SSL_CTX_set_options(newctx, ssl_opset); + } + + // Clear out options that should be cleared. + ssl_opclear = ssl_opclear & SSL_CTX_get_options(newctx); + if (ssl_opclear != 0) { + ec_log_warn("Resetting already set options 0x%lx on SSL context", ssl_opclear); + SSL_CTX_clear_options(newctx, ssl_opclear); + } +#endif // OPENSSL_VERSION_NUMBER #if !defined(OPENSSL_NO_ECDH) && defined(NID_X9_62_prime256v1) ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); @@ -180,7 +303,6 @@ HRESULT ECChannel::HrSetCtx(ECConfig *lpConfig) #if !defined(OPENSSL_NO_ECDH) && defined(SSL_CTX_set1_curves_list) if (ssl_curves && SSL_CTX_set1_curves_list(newctx, ssl_curves) != 1) { ec_log_err("Can not set SSL curve list to \"%s\": %s", ssl_curves, ERR_error_string(ERR_get_error(), 0)); - hr = MAPI_E_CALL_FAILED; goto exit; } @@ -211,21 +333,26 @@ HRESULT ECChannel::HrSetCtx(ECConfig *lpConfig) if (lpConfig->GetSetting("ssl_verify_path")[0]) szPath = lpConfig->GetSetting("ssl_verify_path"); if ((szFile != nullptr || szPath != nullptr) && - SSL_CTX_load_verify_locations(newctx, szFile, szPath) != 1) + SSL_CTX_load_verify_locations(newctx, szFile, szPath) != 1) { ec_log_err("SSL CTX error loading verify locations: %s", ERR_error_string(ERR_get_error(), 0)); + goto exit; + } + + // Swap in generated SSL context. + newctx = lpCTX.exchange(newctx); + hr = hrSuccess; - lpCTX = std::move(newctx); exit: - if (hr != hrSuccess) + if (newctx != nullptr) SSL_CTX_free(newctx); return hr; } HRESULT ECChannel::HrFreeCtx() { - if (lpCTX) { - SSL_CTX_free(lpCTX); - lpCTX = NULL; - } + // Swap out current SSL context. + auto oldctx = lpCTX.exchange(nullptr); + if (oldctx != nullptr) + SSL_CTX_free(oldctx); return hrSuccess; } @@ -259,22 +386,30 @@ HRESULT ECChannel::HrEnableTLS(void) hr = MAPI_E_CALL_FAILED; goto exit; } - SSL_clear(lpSSL); + +#if OPENSSL_VERSION_NUMBER >= 0x1010100fL + ec_log_info("ECChannel::HrEnableTLS(): min TLS version 0x%lx", SSL_get_min_proto_version(lpSSL)); + ec_log_info("ECChannel::HrEnableTLS(): max TLS version 0x%lx", SSL_get_max_proto_version(lpSSL)); +#endif + ec_log_info("ECChannel::HrEnableTLS(): TLS flags 0x%lx", SSL_get_options(lpSSL)); + if (SSL_set_fd(lpSSL, fd) != 1) { ec_log_err("ECChannel::HrEnableTLS(): SSL_set_fd failed"); hr = MAPI_E_CALL_FAILED; goto exit; } - SSL_set_accept_state(lpSSL); + ERR_clear_error(); if ((rc = SSL_accept(lpSSL)) != 1) { - ec_log_err("ECChannel::HrEnableTLS(): SSL_accept failed: %d", SSL_get_error(lpSSL, rc)); + int err = SSL_get_error(lpSSL, rc); + ec_log_err("ECChannel::HrEnableTLS(): SSL_accept failed: %d", err); + if (err != SSL_ERROR_SYSCALL && err != SSL_ERROR_SSL) + SSL_shutdown(lpSSL); hr = MAPI_E_CALL_FAILED; goto exit; } exit: if (hr != hrSuccess && lpSSL) { - SSL_shutdown(lpSSL); SSL_free(lpSSL); lpSSL = NULL; } diff --git a/common/SSLUtil.cpp b/common/SSLUtil.cpp index b2b3e9600..e496b38ec 100644 --- a/common/SSLUtil.cpp +++ b/common/SSLUtil.cpp @@ -65,16 +65,25 @@ void ssl_threading_cleanup() { */ void SSL_library_cleanup() { -#ifndef OPENSSL_NO_ENGINE +#if OPENSSL_VERSION_NUMBER >= 0x1010000fL + // No cleanup required, is a current OpenSSL. +#else // OPENSSL_VERSION_NUMBER < 0x1010000fL + // Clean up any possible allocations. + OBJ_cleanup(); + CONF_modules_free(); + EVP_cleanup(); +# ifndef OPENSSL_NO_ENGINE ENGINE_cleanup(); -#endif +# endif +# if OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined(OPENSSL_NO_COMP) + SSL_COMP_free_compression_methods(); +# endif ERR_free_strings(); -#ifdef OLD_API +# ifdef OLD_API ERR_remove_state(0); -#endif - EVP_cleanup(); +# endif CRYPTO_cleanup_all_ex_data(); - CONF_modules_unload(0); +#endif // OPENSSL_VERSION_NUMBER } void ssl_random_init() diff --git a/common/include/kopano/ECChannel.h b/common/include/kopano/ECChannel.h index 71f14756b..f33520196 100644 --- a/common/include/kopano/ECChannel.h +++ b/common/include/kopano/ECChannel.h @@ -15,6 +15,7 @@ #include <cstdio> #include <iostream> #include <sys/socket.h> +#include <openssl/opensslconf.h> #include <openssl/ssl.h> #include <openssl/err.h> #include <kopano/ECConfig.h>
-
Hi @modelnine,
thanks for you patch. Are you sure its against the latest head of the 8.7 branch? for me it does not apply cleanly.
I think it would be beneficial if you open a pull request on https://github.com/Kopano-dev/kopano-core. This way these changes can also more easily be attributed to you.
-
Commit bed83df42 should do. I don’t really have a plan to do anything else to 8.7 with regard to the ssl code there.
-
Looks like there are some SSL/TLS issues with gateway/ical after upgrading to core-9.0.2.158.76758b3-Debian_10-amd64
kopano-ical
2020-01-03T23:23:31.509414: [kopano-ical|T17125] [=======] Starting kopano-ical version 9.0.2 (pid 17125 uid 999) 2020-01-03T23:23:31.513151: [kopano-ical|T17125] [error ] Error loading SSL context, ICALS will be disabled: call failed (80004005) 2020-01-03T23:24:59.038655: [kopano-ical|T17125] [crit ] ---------------------------------------------------------------------- 2020-01-03T23:24:59.038788: [kopano-ical|T17125] [crit ] Fatal error detected. Please report all following information. 2020-01-03T23:24:59.038847: [kopano-ical|T17125] [crit ] kopano-ical 9.0.2 2020-01-03T23:24:59.038875: [kopano-ical|T17125] [crit ] OS: Debian GNU/Linux 10 (buster) (Linux 4.19.0-6-amd64 x86_64) 2020-01-03T23:24:59.038885: [kopano-ical|T17125] [crit ] Thread name: kopano-ical 2020-01-03T23:24:59.038904: [kopano-ical|T17125] [crit ] Peak RSS: 18296 2020-01-03T23:24:59.038913: [kopano-ical|T17125] [crit ] Pid 17125 caught SIGSEGV (11), traceback: 2020-01-03T23:24:59.038922: [kopano-ical|T17125] [crit ] Backtrace: 2020-01-03T23:24:59.039362: [kopano-ical|T17125] [crit ] f0. /usr/lib/x86_64-linux-gnu/libkcutil.so.0(+0x50800) [0x7f4e8137c800] 2020-01-03T23:24:59.039393: [kopano-ical|T17125] [crit ] f1. /usr/lib/x86_64-linux-gnu/libkcutil.so.0(+0x37836) [0x7f4e81363836] 2020-01-03T23:24:59.039403: [kopano-ical|T17125] [crit ] f2. /usr/lib/x86_64-linux-gnu/libkcutil.so.0(+0x38a3e) [0x7f4e81364a3e] 2020-01-03T23:24:59.039411: [kopano-ical|T17125] [crit ] f3. /lib/x86_64-linux-gnu/libpthread.so.0(+0x12730) [0x7f4e812bf730] 2020-01-03T23:24:59.039420: [kopano-ical|T17125] [crit ] f4. /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1(OPENSSL_sk_pop_free+0xf) [0x7f4e7ee202cf] 2020-01-03T23:24:59.039429: [kopano-ical|T17125] [crit ] f5. /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1(X509_VERIFY_PARAM_free+0x19) [0x7f4e7ee3a819] 2020-01-03T23:24:59.039437: [kopano-ical|T17125] [crit ] f6. /usr/lib/x86_64-linux-gnu/libssl.so.1.1(SSL_CTX_free+0x35) [0x7f4e7f5e44b5] 2020-01-03T23:24:59.039446: [kopano-ical|T17125] [crit ] f7. /usr/lib/x86_64-linux-gnu/libkcutil.so.0(_ZN2KC9ECChannel9HrFreeCtxEv+0x20) [0x7f4e81353760] 2020-01-03T23:24:59.039454: [kopano-ical|T17125] [crit ] f8. /usr/sbin/kopano-ical(+0xd6f6) [0x5598998766f6] 2020-01-03T23:24:59.039461: [kopano-ical|T17125] [crit ] f9. /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb) [0x7f4e7ef4209b] 2020-01-03T23:24:59.039470: [kopano-ical|T17125] [crit ] f10. /usr/sbin/kopano-ical(+0xeaba) [0x559899877aba] 2020-01-03T23:24:59.039491: [kopano-ical|T17125] [crit ] Signal errno: Success, signal code: 128 2020-01-03T23:24:59.039500: [kopano-ical|T17125] [crit ] Sender pid: 0, sender uid: 0, si_status: 0 2020-01-03T23:24:59.039508: [kopano-ical|T17125] [crit ] Signal value: 0, faulting address: (nil) 2020-01-03T23:24:59.039516: [kopano-ical|T17125] [crit ] When reporting this traceback, please include Linux distribution name (and version), system architecture and Kopano version
kopano-gateway
2020-01-03T23:31:06.422304: [kopano-gateway|T17668] [=======] Starting kopano-gateway version 9.0.2 (pid 17668 uid 0) 2020-01-03T23:31:06.426906: [kopano-gateway|T17668] [error ] Error loading SSL context, POP3S and IMAPS will be disabled 2020-01-03T23:31:06.458667: [kopano-gateway|T17668] [=======] Starting kopano-gateway version 9.0.2 (pid 17668 uid 999) 2020-01-03T23:31:06.459425: [kopano-gateway|T17668] [error ] K-1559: bind 0.0.0.0:995: Permission denied 2020-01-03T23:31:06.459612: [kopano-gateway|T17668] [error ] K-1559: bind [::]:995: Permission denied
Don’t changed anything in the config, ssl key and cert permissions are ok … can’t find any failures in my setup …
-
Hai, i updated today to
9.0.2.158.3dd898471-0+246.1
still the same :
2020-01-06T09:32:23.774861: [kopano-gateway|T4378] [=======] POP3/IMAP Gateway will now exit 2020-01-06T09:32:23.800320: [kopano-gateway|T5646] [=======] Starting kopano-gateway version 9.0.2 (pid 5646 uid 0) 2020-01-06T09:32:23.803143: [kopano-gateway|T5646] [error ] Error loading SSL context, POP3S and IMAPS will be disabled 2020-01-06T09:32:23.834483: [kopano-gateway|T5646] [=======] Starting kopano-gateway version 9.0.2 (pid 5646 uid 999) 2020-01-06T09:32:23.834794: [kopano-gateway|T5646] [error ] K-1559: bind 0.0.0.0:995: Permission denied 2020-01-06T09:32:23.834829: [kopano-gateway|T5646] [error ] K-1559: bind [::]:995: Permission denied
-
Same issue since upgrading from 8.7.x to core-9.0.2.136.c285120-Debian_9.0-amd64
2020-01-06T10:13:07.121642: [=======] Starting kopano-gateway version 9.0.2 (pid 23159 uid 998) 2020-01-06T10:13:07.121688: [info ] Coredump status left at system default. 2020-01-06T10:13:07.121804: [info ] Re-using fd 4 for 0.0.0.0%lo:110 2020-01-06T10:13:07.121828: [info ] Re-using fd 5 for [::]%lo:110 2020-01-06T10:13:07.121912: [info ] Re-using fd 6 for 0.0.0.0%lo:143 2020-01-06T10:13:07.121943: [info ] Re-using fd 7 for [::]%lo:143 2020-01-06T10:13:07.122039: [info ] Listening on 0.0.0.0:993 (fd 9) 2020-01-06T10:13:07.122094: [info ] Listening on [::]:993 (fd 10) 2020-01-06T10:13:07.124085: [error ] Error loading SSL context, POP3S and IMAPS will be disabled 2020-01-06T10:13:07.134085: [info ] Logger process started on pid 23165
Same SSL Key Files for every Kopano-Component, only Gateway is complaning
Checked permissions, changed every possible config parameter with no success… -
If you kopano-gateway is not starting at all.
change :
run_as_user = kopano run_as_group = kopano
to
run_as_user = root run_as_group = root
Still no SSL, but at least gateway now starts.
-
@thctlo said in SSL negotiation failures with TLSv1 and TLSv1.3 against gateway/ical on Debian 10:
If you kopano-gateway is not starting at all.
change :
run_as_user = kopano run_as_group = kopano
to
run_as_user = root run_as_group = root
Still no SSL, but at least gateway now starts.
Yes, the “K-1559: bind [::]:995: Permission denied” goes away by either running as root or executing
setcap cap_net_bind_service=+ep /usr/sbin/kopano-gateway
-
@fbartels I’ve created a PR for this patch on GitHub; for my development, I’m not working with github, so I needed to set up the corresponding repository first. Anyway, independent of the actual changes for OpenSSL 1.1.x, parts of the patch should be merged. As of commit 9f9a199, the logic in ECChannel::HrSetCtx(ECConfig *lpConfig) is currently broken (always returns MAPI_E_CALL_FAILED) in the kc-8.7.x tag, always frees the freshly generated context, and on duplicate HrSetCtx calls leads to a memory leak of an SSL_CTX. Same thing goes for HrFreeCtx, which seems not to have been adapted for the use of std::atomic to refer to the SSL_CTX.
-
Unfortunately the above mentioned solution does not work for me. I still get ‘Error loading SSL context, POP3S and IMAPS will be disabled’.
System: debian 10, kopano 9.0.2
I run as root and did ‘setcap cap_net_bind_service=+ep /usr/sbin/kopano-gateway’
Do I have to completely restart kopano?
Regards, Tom
-
@TomSchmidt the problem is one in the actual SSL_CTX setup of Kopano, which is currently broken (due to an incomplete and functionally broken patch which was applied just before christmas). The code seems to be similar in the 8.7.x and 9.0.x branches, so the first of the patches that I posted as PR 21 against the github branches (see https://github.com/Kopano-dev/kopano-core/pull/21/commits/60299e18d120d94f2c58ec75a354c73105015921) should apply to 9.0.x, too, to fix the invalid flow of control setting up the SSL context.
Please be aware that the patch restores the SSL-functionality of Kopano, but is incomplete in the sense that it is not a proper implementation of atomic accesses to SSL_CTX, which would require locking; basically, tacking on std::atomic for accesses to the SSL_CTX is not enough to actually make sure that reloading the SSL context while a Kopano server is operating does not lead to spurious segmentation faults due to race conditions (SSL_CTX can be used after being freed). So, don’t reload kopano-gateway to get new certificate material, always restart it, even with this patch.
If there’s interest, I’ll gladly supply a patch to implement the proper locking, but I gather that Kopano is working on something here and will fix this themselves at some point in time.
-
@modelnine,
thank you yery much to make things clear. This is quite sad while imaps isn’t state of the art but many still use it.
I’m wondering that there’s a major release upgrade and this isn’t working.Stupid me tested in a sandbox but I did not test the imaps function. I cannot go back to 8.7
So I will wait for things coming.
-
@fbartels is there any possibility of getting one of the two patchsets (or both) which are now in my Github-tree into Kopano some time soon? ;-) Basically, as it stands, for 9.0 and 8.7, (at least) all server-side SSL is currently broken. Thanks!
-
Hi @modelnine,
as far as I know they are currently in review, but apart from my answers here I am not involved in this topic.
-
@TomSchmidt: I use stunnel to have imaps as long as kopano-gateway fails to provide imaps.
-
@embexx
Hi, nice idea, but I have to connect two Thunderbird-Users and TB can’t use z-push as AcitveSync. The addons promising ActiveSync are crap. One of them is my Mom using Win10. So this idea crashes here :-)But thank a lot for replying!
tom
-
@TomSchmidt stunnel is like a proxy or SSL-wrapper. With stunnel iprovide imaps with kopano-gateway to connect my TB-users.
ActiveSync has not too much to do with that.