Package: release.debian.org Severity: normal X-Debbugs-Cc: icin...@packages.debian.org Control: affects -1 + src:icinga2 User: release.debian....@packages.debian.org Usertags: unblock
Please unblock package icinga2 [ Reason ] Fixes CVE-2025-48057. [ Impact ] Unfixed security issue. [ Tests ] Upstream test suite. [ Risks ] Low, we're not building with OpenSSL < 1.1.0. [ Checklist ] [x] all changes are documented in the d/changelog [x] I reviewed all changes and I approve them [x] attach debdiff against the package in testing [ Other info ] N/A unblock icinga2/2.14.6-1 Kind Regards, Bas
diff -Nru icinga2-2.14.5/CHANGELOG.md icinga2-2.14.6/CHANGELOG.md --- icinga2-2.14.5/CHANGELOG.md 2025-02-05 15:12:30.000000000 +0100 +++ icinga2-2.14.6/CHANGELOG.md 2025-05-21 12:31:48.000000000 +0200 @@ -7,6 +7,19 @@ Released closed milestones can be found on [GitHub](https://github.com/Icinga/icinga2/milestones?state=closed). +## 2.14.6 (2025-05-27) + +This security release fixes a critical issue in the certificate renewal logic in Icinga 2, which +might incorrectly renew an invalid certificate. However, only nodes with access to the Icinga CA +private key running with OpenSSL older than version 1.1.0 (released in 2016) are vulnerable. So this +typically affects Icinga 2 masters running on operating systems like RHEL 7 and Amazon Linux 2. + +* CVE-2025-48057: Prevent invalid certificates from being renewed with OpenSSL older than v1.1.0. +* Fix use-after-free in VerifyCertificate(): Additionally, a use-after-free was found in the same + function which is fixed as well, but in case it is triggered, typically only a wrong error code + may be shown in a log message. +* Windows: Update OpenSSL shipped on Windows to v3.0.16. + ## 2.14.5 (2025-02-06) This release fixes a regression introduced in 2.14.4 that caused the `icinga2 node setup`, diff -Nru icinga2-2.14.5/debian/changelog icinga2-2.14.6/debian/changelog --- icinga2-2.14.5/debian/changelog 2025-02-06 05:45:24.000000000 +0100 +++ icinga2-2.14.6/debian/changelog 2025-05-28 12:47:15.000000000 +0200 @@ -1,3 +1,12 @@ +icinga2 (2.14.6-1) unstable; urgency=high + + * Team upload. + * New upstream release. + Fixes CVE-2025-48057. + * Bump Standards-Version to 4.7.2, no changes. + + -- Bas Couwenberg <sebas...@debian.org> Wed, 28 May 2025 12:47:15 +0200 + icinga2 (2.14.5-1) unstable; urgency=medium * Team upload. diff -Nru icinga2-2.14.5/debian/control icinga2-2.14.6/debian/control --- icinga2-2.14.5/debian/control 2024-11-15 17:29:56.000000000 +0100 +++ icinga2-2.14.6/debian/control 2025-03-20 12:04:25.000000000 +0100 @@ -31,7 +31,7 @@ pkgconf, po-debconf, tzdata -Standards-Version: 4.7.0 +Standards-Version: 4.7.2 Vcs-Browser: https://salsa.debian.org/nagios-team/icinga2 Vcs-Git: https://salsa.debian.org/nagios-team/icinga2.git Homepage: https://icinga.com diff -Nru icinga2-2.14.5/doc/win-dev.ps1 icinga2-2.14.6/doc/win-dev.ps1 --- icinga2-2.14.5/doc/win-dev.ps1 2025-02-05 15:12:30.000000000 +0100 +++ icinga2-2.14.6/doc/win-dev.ps1 2025-05-21 12:31:48.000000000 +0200 @@ -14,7 +14,7 @@ $VsVersion = 2019 $MsvcVersion = '14.2' $BoostVersion = @(1, 86, 0) -$OpensslVersion = '3_0_15' +$OpensslVersion = '3_0_16' switch ($Env:BITS) { 32 { } diff -Nru icinga2-2.14.5/.github/workflows/linux.bash icinga2-2.14.6/.github/workflows/linux.bash --- icinga2-2.14.5/.github/workflows/linux.bash 2025-02-05 15:12:30.000000000 +0100 +++ icinga2-2.14.6/.github/workflows/linux.bash 2025-05-21 12:31:48.000000000 +0200 @@ -30,7 +30,7 @@ amazonlinux:20*) dnf install -y bison cmake flex gcc-c++ ninja-build \ - {boost,libedit,mariadb1\*,ncurses,openssl,postgresql,systemd}-devel + {boost,libedit,mariadb-connector-c,ncurses,openssl,postgresql,systemd}-devel ;; debian:*|ubuntu:*) diff -Nru icinga2-2.14.5/ICINGA2_VERSION icinga2-2.14.6/ICINGA2_VERSION --- icinga2-2.14.5/ICINGA2_VERSION 2025-02-05 15:12:30.000000000 +0100 +++ icinga2-2.14.6/ICINGA2_VERSION 2025-05-21 12:31:48.000000000 +0200 @@ -1,2 +1,2 @@ -Version: 2.14.5 +Version: 2.14.6 Revision: 1 diff -Nru icinga2-2.14.5/lib/base/tlsutility.cpp icinga2-2.14.6/lib/base/tlsutility.cpp --- icinga2-2.14.5/lib/base/tlsutility.cpp 2025-02-05 15:12:30.000000000 +0100 +++ icinga2-2.14.6/lib/base/tlsutility.cpp 2025-05-21 12:31:48.000000000 +0200 @@ -983,27 +983,47 @@ bool VerifyCertificate(const std::shared_ptr<X509> &caCertificate, const std::shared_ptr<X509> &certificate, const String& crlFile) { - X509_STORE *store = X509_STORE_new(); + return VerifyCertificate(caCertificate.get(), certificate.get(), crlFile); +} + +bool VerifyCertificate(X509* caCertificate, X509* certificate, const String& crlFile) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + /* + * OpenSSL older than version 1.1.0 stored a valid flag in the struct behind X509* which leads to certain validation + * steps to be skipped on subsequent verification operations. If a certificate is verified multiple times with a + * different configuration, for example with different trust anchors, this can result in the certificate + * incorrectly being treated as valid. + * + * This issue is worked around by serializing and deserializing the certificate which creates a new struct instance + * with the valid flag cleared, hence performing the full validation. + * + * The flag in question was removed in OpenSSL 1.1.0, so this extra step isn't necessary for more recent versions: + * https://github.com/openssl/openssl/commit/0e76014e584ba78ef1d6ecb4572391ef61c4fb51 + */ + std::shared_ptr<X509> copy = StringToCertificate(CertificateToString(certificate)); + VERIFY(copy.get() != certificate); + certificate = copy.get(); +#endif + + std::unique_ptr<X509_STORE, decltype(&X509_STORE_free)> store{X509_STORE_new(), &X509_STORE_free}; if (!store) return false; - X509_STORE_add_cert(store, caCertificate.get()); + X509_STORE_add_cert(store.get(), caCertificate); if (!crlFile.IsEmpty()) { - AddCRLToSSLContext(store, crlFile); + AddCRLToSSLContext(store.get(), crlFile); } - X509_STORE_CTX *csc = X509_STORE_CTX_new(); - X509_STORE_CTX_init(csc, store, certificate.get(), nullptr); - - int rc = X509_verify_cert(csc); + std::unique_ptr<X509_STORE_CTX, decltype(&X509_STORE_CTX_free)> csc{X509_STORE_CTX_new(), &X509_STORE_CTX_free}; + X509_STORE_CTX_init(csc.get(), store.get(), certificate, nullptr); - X509_STORE_CTX_free(csc); - X509_STORE_free(store); + int rc = X509_verify_cert(csc.get()); if (rc == 0) { - int err = X509_STORE_CTX_get_error(csc); + int err = X509_STORE_CTX_get_error(csc.get()); BOOST_THROW_EXCEPTION(openssl_error() << boost::errinfo_api_function("X509_verify_cert") diff -Nru icinga2-2.14.5/lib/base/tlsutility.hpp icinga2-2.14.6/lib/base/tlsutility.hpp --- icinga2-2.14.5/lib/base/tlsutility.hpp 2025-02-05 15:12:30.000000000 +0100 +++ icinga2-2.14.6/lib/base/tlsutility.hpp 2025-05-21 12:31:48.000000000 +0200 @@ -79,6 +79,7 @@ String BinaryToHex(const unsigned char* data, size_t length); bool VerifyCertificate(const std::shared_ptr<X509>& caCertificate, const std::shared_ptr<X509>& certificate, const String& crlFile); +bool VerifyCertificate(X509* caCertificate, X509* certificate, const String& crlFile); bool IsCa(const std::shared_ptr<X509>& cacert); int GetCertificateVersion(const std::shared_ptr<X509>& cert); String GetSignatureAlgorithm(const std::shared_ptr<X509>& cert); diff -Nru icinga2-2.14.5/test/base-tlsutility.cpp icinga2-2.14.6/test/base-tlsutility.cpp --- icinga2-2.14.5/test/base-tlsutility.cpp 2025-02-05 15:12:30.000000000 +0100 +++ icinga2-2.14.6/test/base-tlsutility.cpp 2025-05-21 12:31:48.000000000 +0200 @@ -132,4 +132,24 @@ }))); } +BOOST_AUTO_TEST_CASE(VerifyCertificate_revalidate) +{ + X509_NAME *caSubject = X509_NAME_new(); + X509_NAME_add_entry_by_txt(caSubject, "CN", MBSTRING_ASC, (const unsigned char*)"Icinga CA", -1, -1, 0); + + auto signingCaKey = GenKeypair(); + auto signingCaCert = CreateCert(signingCaKey, caSubject, caSubject, signingCaKey, true); + + X509_NAME *leafSubject = X509_NAME_new(); + X509_NAME_add_entry_by_txt(leafSubject, "CN", MBSTRING_ASC, (const unsigned char*)"Leaf Certificate", -1, -1, 0); + auto leafKey = GenKeypair(); + auto leafCert = CreateCert(leafKey, leafSubject, caSubject, signingCaKey, false); + BOOST_CHECK(VerifyCertificate(signingCaCert, leafCert, "")); + + // Create a second CA with a different key, the leaf certificate is supposed to fail validation against that CA. + auto otherCaKey = GenKeypair(); + auto otherCaCert = CreateCert(otherCaKey, caSubject, caSubject, otherCaKey, true); + BOOST_CHECK_THROW(VerifyCertificate(otherCaCert, leafCert, ""), openssl_error); +} + BOOST_AUTO_TEST_SUITE_END() diff -Nru icinga2-2.14.5/test/CMakeLists.txt icinga2-2.14.6/test/CMakeLists.txt --- icinga2-2.14.5/test/CMakeLists.txt 2025-02-05 15:12:30.000000000 +0100 +++ icinga2-2.14.6/test/CMakeLists.txt 2025-05-21 12:31:48.000000000 +0200 @@ -123,6 +123,7 @@ base_tlsutility/iscertuptodate_ok base_tlsutility/iscertuptodate_expiring base_tlsutility/iscertuptodate_old + base_tlsutility/VerifyCertificate_revalidate base_type/gettype base_type/assign base_type/byname diff -Nru icinga2-2.14.5/tools/win32/configure.ps1 icinga2-2.14.6/tools/win32/configure.ps1 --- icinga2-2.14.5/tools/win32/configure.ps1 2025-02-05 15:12:30.000000000 +0100 +++ icinga2-2.14.6/tools/win32/configure.ps1 2025-05-21 12:31:48.000000000 +0200 @@ -33,7 +33,7 @@ $env:CMAKE_ARGS = '[]' } if (-not (Test-Path env:OPENSSL_ROOT_DIR)) { - $env:OPENSSL_ROOT_DIR = "c:\local\OpenSSL_3_0_15-Win${env:BITS}" + $env:OPENSSL_ROOT_DIR = "c:\local\OpenSSL_3_0_16-Win${env:BITS}" } if (-not (Test-Path env:BOOST_ROOT)) { $env:BOOST_ROOT = "c:\local\boost_1_86_0-Win${env:BITS}"