diff --git a/CHANGELOG.md b/CHANGELOG.md index df4db04df5b..cde9ecb7c71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ documentation before upgrading to a new release. Released closed milestones can be found on [GitHub](https://github.com/Icinga/icinga2/milestones?state=closed). +## 2.11.12 (2024-11-12) + +This security release fixes a TLS certificate validation bypass. +Given the severity of that issue, users are advised to upgrade all nodes immediately. + +* Security: fix TLS certificate validation bypass. CVE-2024-49369 +* Security: update OpenSSL shipped on Windows to v3.0.15. +* Windows: sign MSI packages with a certificate the OS trusts by default. + ## 2.11.11 (2021-08-19) The main focus of these versions is a security vulnerability in the TLS certificate verification of our metrics writers ElasticsearchWriter, GelfWriter and InfluxdbWriter. diff --git a/CMakeLists.txt b/CMakeLists.txt index a974a817d4e..4602f3b8080 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -517,16 +517,16 @@ if(WIN32) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/NSCP.msi DESTINATION ${CMAKE_INSTALL_SBINDIR}) - if (OPENSSL_VERSION_MINOR GREATER_EQUAL 1) - if (CMAKE_VS_PLATFORM_NAME STREQUAL "x64") - list (APPEND ICINGA2_OPENSSL_DLLS ${OPENSSL_INCLUDE_DIR}/../bin/libcrypto-1_1-x64.dll ${OPENSSL_INCLUDE_DIR}/../bin/libssl-1_1-x64.dll) - else() - list (APPEND ICINGA2_OPENSSL_DLLS ${OPENSSL_INCLUDE_DIR}/../bin/libcrypto-1_1.dll ${OPENSSL_INCLUDE_DIR}/../bin/libssl-1_1.dll) - endif() + if (CMAKE_VS_PLATFORM_NAME STREQUAL "x64") + set(ICINGA2_OPENSSL_DLL_ARCH "-x64") else() - list (APPEND ICINGA2_OPENSSL_DLLS ${OPENSSL_INCLUDE_DIR}/../bin/libeay32.dll ${OPENSSL_INCLUDE_DIR}/../bin/ssleay32.dll) + set(ICINGA2_OPENSSL_DLL_ARCH "") endif() + foreach(ICINGA2_OPENSSL_LIB crypto ssl) + list(APPEND ICINGA2_OPENSSL_DLLS ${OPENSSL_INCLUDE_DIR}/../bin/lib${ICINGA2_OPENSSL_LIB}-3${ICINGA2_OPENSSL_DLL_ARCH}.dll) + endforeach() + install( PROGRAMS ${ICINGA2_OPENSSL_DLLS} DESTINATION ${CMAKE_INSTALL_SBINDIR} diff --git a/VERSION b/VERSION index cdea9a9e643..086463eae6f 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ -Version: 2.11.11 +Version: 2.11.12 Revision: 1 diff --git a/doc/win-dev.ps1 b/doc/win-dev.ps1 index 9c6c53ed104..cb6862c745b 100644 --- a/doc/win-dev.ps1 +++ b/doc/win-dev.ps1 @@ -14,7 +14,7 @@ function ThrowOnNativeFailure { $VsVersion = 2019 $MsvcVersion = '14.2' $BoostVersion = @(1, 71, 0) -$OpensslVersion = '1_1_1k' +$OpensslVersion = '3_0_15' switch ($Env:BITS) { 32 { } diff --git a/lib/base/tlsstream.cpp b/lib/base/tlsstream.cpp index db54c919ed5..a71451a5369 100644 --- a/lib/base/tlsstream.cpp +++ b/lib/base/tlsstream.cpp @@ -18,14 +18,48 @@ using namespace icinga; -bool UnbufferedAsioTlsStream::IsVerifyOK() const +/** + * Checks whether the TLS handshake was completed with a valid peer certificate. + * + * @return true if the peer presented a valid certificate, false otherwise + */ +bool UnbufferedAsioTlsStream::IsVerifyOK() { - return m_VerifyOK; + if (!SSL_is_init_finished(native_handle())) { + // handshake was not completed + return false; + } + + if (GetPeerCertificate() == nullptr) { + // no peer certificate was sent + return false; + } + + return SSL_get_verify_result(native_handle()) == X509_V_OK; } -String UnbufferedAsioTlsStream::GetVerifyError() const +/** + * Returns a human-readable error string for situations where IsVerifyOK() returns false. + * + * If the handshake was completed and a peer certificate was provided, + * the string additionally contains the OpenSSL verification error code. + * + * @return string containing the error message + */ +String UnbufferedAsioTlsStream::GetVerifyError() { - return m_VerifyError; + if (!SSL_is_init_finished(native_handle())) { + return "handshake not completed"; + } + + if (GetPeerCertificate() == nullptr) { + return "no peer certificate provided"; + } + + std::ostringstream buf; + long err = SSL_get_verify_result(native_handle()); + buf << "code " << err << ": " << X509_verify_cert_error_string(err); + return buf.str(); } std::shared_ptr UnbufferedAsioTlsStream::GetPeerCertificate() @@ -43,17 +77,17 @@ void UnbufferedAsioTlsStream::BeforeHandshake(handshake_type type) set_verify_mode(ssl::verify_peer | ssl::verify_client_once); - set_verify_callback([this](bool preverified, ssl::verify_context& ctx) { - if (!preverified) { - m_VerifyOK = false; - - std::ostringstream msgbuf; - int err = X509_STORE_CTX_get_error(ctx.native_handle()); - - msgbuf << "code " << err << ": " << X509_verify_cert_error_string(err); - m_VerifyError = msgbuf.str(); - } + set_verify_callback([](bool preverified, ssl::verify_context& ctx) { + (void) preverified; + (void) ctx; + /* Continue the handshake even if an invalid peer certificate was presented. The verification result has to be + * checked using the IsVerifyOK() method. + * + * Such connections are used for the initial enrollment of nodes where they use a self-signed certificate to + * send a certificate request and receive their valid certificate after approval (manually by the administrator + * or using a certificate ticket). + */ return true; }); diff --git a/lib/base/tlsstream.hpp b/lib/base/tlsstream.hpp index 05b3550d54c..3b1d121ffc3 100644 --- a/lib/base/tlsstream.hpp +++ b/lib/base/tlsstream.hpp @@ -69,12 +69,12 @@ class UnbufferedAsioTlsStream : public AsioTcpTlsStream public: inline UnbufferedAsioTlsStream(UnbufferedAsioTlsStreamParams& init) - : AsioTcpTlsStream(init.IoContext, init.SslContext), m_VerifyOK(true), m_Hostname(init.Hostname) + : AsioTcpTlsStream(init.IoContext, init.SslContext), m_Hostname(init.Hostname) { } - bool IsVerifyOK() const; - String GetVerifyError() const; + bool IsVerifyOK(); + String GetVerifyError(); std::shared_ptr GetPeerCertificate(); template @@ -96,8 +96,6 @@ class UnbufferedAsioTlsStream : public AsioTcpTlsStream } private: - bool m_VerifyOK; - String m_VerifyError; String m_Hostname; void BeforeHandshake(handshake_type type); diff --git a/tools/win32/configure.ps1 b/tools/win32/configure.ps1 index a221ff5ab9a..b2a4fb655ec 100644 --- a/tools/win32/configure.ps1 +++ b/tools/win32/configure.ps1 @@ -30,7 +30,7 @@ if (-not (Test-Path env:CMAKE_GENERATOR_PLATFORM)) { } } if (-not (Test-Path env:OPENSSL_ROOT_DIR)) { - $env:OPENSSL_ROOT_DIR = "c:\local\OpenSSL_1_1_1k-Win${env:BITS}" + $env:OPENSSL_ROOT_DIR = "c:\local\OpenSSL_3_0_15-Win${env:BITS}" } if (-not (Test-Path env:BOOST_ROOT)) { $env:BOOST_ROOT = "c:\local\boost_1_71_0-Win${env:BITS}"