diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f46b91723..c5f5bb16d7 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.13.10 (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.13.9 (2023-12-21) Version 2.13.9 is a hotfix release for masters and satellites that mainly diff --git a/ICINGA2_VERSION b/ICINGA2_VERSION index 42da309706..27c8405614 100644 --- a/ICINGA2_VERSION +++ b/ICINGA2_VERSION @@ -1,2 +1,2 @@ -Version: 2.13.9 +Version: 2.13.10 Revision: 1 diff --git a/lib/base/tlsstream.cpp b/lib/base/tlsstream.cpp index db54c919ed..a71451a536 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 f6e52097e1..9a6340bafc 100644 --- a/lib/base/tlsstream.hpp +++ b/lib/base/tlsstream.hpp @@ -70,12 +70,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 @@ -97,8 +97,6 @@ class UnbufferedAsioTlsStream : public AsioTcpTlsStream } private: - bool m_VerifyOK; - String m_VerifyError; String m_Hostname; void BeforeHandshake(handshake_type type);