diff --git a/libamqpprox/amqpprox_tlsutil.cpp b/libamqpprox/amqpprox_tlsutil.cpp index c3a2b9f..9331d02 100644 --- a/libamqpprox/amqpprox_tlsutil.cpp +++ b/libamqpprox/amqpprox_tlsutil.cpp @@ -82,15 +82,35 @@ void TlsUtil::logTlsConnectionAlert(const SSL *s, int where, int ret) bool TlsUtil::logCertVerificationFailure(bool preverified, boost::asio::ssl::verify_context &ctx) { - if (!preverified) { - char subject_name[256]; - X509 *cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); - X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 255); + if (preverified) { + return preverified; + } + + X509_STORE_CTX *storeCtx = ctx.native_handle(); + + if (!storeCtx) { + LOG_ERROR << "Certificate verification failed: no verification " + "context available"; + return preverified; + } - LOG_ERROR << "Certificate verification failed: [" << subject_name - << "]: "; + const int errorCode = X509_STORE_CTX_get_error(storeCtx); + const int errorDepth = X509_STORE_CTX_get_error_depth(storeCtx); + + X509 *cert = X509_STORE_CTX_get_current_cert(storeCtx); + X509_NAME *subject = cert ? X509_get_subject_name(cert) : nullptr; + + char subjectName[256] = "unavailable"; + + if (subject) { + X509_NAME_oneline(subject, subjectName, sizeof(subjectName)); } + LOG_ERROR << "Certificate verification failed: subjectName=" << subjectName + << " errorCode=" << errorCode + << " errorString=" << X509_verify_cert_error_string(errorCode) + << " depth=" << errorDepth; + return preverified; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3d801a6..92e8da0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -41,6 +41,7 @@ add_executable(amqpprox_tests amqpprox_sessionstate.t.cpp amqpprox_statcollector.t.cpp amqpprox_statsnapshot.t.cpp + amqpprox_tlsutil.t.cpp amqpprox_types.t.cpp amqpprox_vhoststate.t.cpp ) diff --git a/tests/amqpprox_tlsutil.t.cpp b/tests/amqpprox_tlsutil.t.cpp new file mode 100644 index 0000000..86412cc --- /dev/null +++ b/tests/amqpprox_tlsutil.t.cpp @@ -0,0 +1,125 @@ +/* +** Copyright 2026 Bloomberg Finance L.P. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include + +#include + +#include + +#include +#include + +using namespace Bloomberg; +using namespace amqpprox; + +namespace { + +class StoreContextGuard { + X509_STORE_CTX *d_ctx; + + public: + StoreContextGuard() + : d_ctx(X509_STORE_CTX_new()) + { + } + + ~StoreContextGuard() + { + if (d_ctx) { + X509_STORE_CTX_free(d_ctx); + } + } + + StoreContextGuard(const StoreContextGuard &) = delete; + StoreContextGuard &operator=(const StoreContextGuard &) = delete; + + X509_STORE_CTX *get() { return d_ctx; } +}; + +class CertificateGuard { + X509 *d_cert; + + public: + CertificateGuard() + : d_cert(X509_new()) + { + } + + ~CertificateGuard() + { + if (d_cert) { + X509_free(d_cert); + } + } + + CertificateGuard(const CertificateGuard &) = delete; + CertificateGuard &operator=(const CertificateGuard &) = delete; + + X509 *get() { return d_cert; } +}; + +} + +TEST(TlsUtil, LogCertVerificationFailureNullCurrentCert) +{ + StoreContextGuard storeCtx; + ASSERT_NE(storeCtx.get(), nullptr); + ASSERT_EQ(X509_STORE_CTX_init(storeCtx.get(), nullptr, nullptr, nullptr), + 1); + ASSERT_EQ(X509_STORE_CTX_get_current_cert(storeCtx.get()), nullptr); + + boost::asio::ssl::verify_context ctx(storeCtx.get()); + + EXPECT_FALSE(TlsUtil::logCertVerificationFailure(false, ctx)); +} + +TEST(TlsUtil, LogCertVerificationFailureNullStoreContext) +{ + boost::asio::ssl::verify_context ctx(nullptr); + + EXPECT_FALSE(TlsUtil::logCertVerificationFailure(false, ctx)); +} + +TEST(TlsUtil, LogCertVerificationFailureWithSubject) +{ + StoreContextGuard storeCtx; + ASSERT_NE(storeCtx.get(), nullptr); + ASSERT_EQ(X509_STORE_CTX_init(storeCtx.get(), nullptr, nullptr, nullptr), + 1); + + CertificateGuard cert; + ASSERT_NE(cert.get(), nullptr); + ASSERT_NE(X509_get_subject_name(cert.get()), nullptr); + + X509_STORE_CTX_set_current_cert(storeCtx.get(), cert.get()); + + boost::asio::ssl::verify_context ctx(storeCtx.get()); + + EXPECT_FALSE(TlsUtil::logCertVerificationFailure(false, ctx)); +} + +TEST(TlsUtil, LogCertVerificationPreverifiedPassesThrough) +{ + StoreContextGuard storeCtx; + ASSERT_NE(storeCtx.get(), nullptr); + ASSERT_EQ(X509_STORE_CTX_init(storeCtx.get(), nullptr, nullptr, nullptr), + 1); + + boost::asio::ssl::verify_context ctx(storeCtx.get()); + + EXPECT_TRUE(TlsUtil::logCertVerificationFailure(true, ctx)); +}