From a1b7e1e523ec5737e156481b1cf62cf377d3d27a Mon Sep 17 00:00:00 2001 From: Gregory Cooke Date: Mon, 26 Feb 2024 13:09:55 -0800 Subject: [PATCH] [Security] Crl helpers (#35963) Re-applies after the revert Fixes the Openssl 1.0.2 portability errors Closes #35963 COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/35963 from gtcooke94:CrlHelpers 9cfb401a47ac0d553d3243ff9e7008a356051690 PiperOrigin-RevId: 610503595 --- BUILD | 1 + CMakeLists.txt | 2 + build_autogenerated.yaml | 6 +- .../credentials/tls/grpc_tls_crl_provider.cc | 27 ++- src/core/tsi/ssl_transport_security.cc | 9 +- src/core/tsi/ssl_transport_security_utils.cc | 70 ++++++ src/core/tsi/ssl_transport_security_utils.h | 18 ++ src/python/grpcio/grpc/aio/_interceptor.py | 2 +- test/core/security/BUILD | 2 + .../security/grpc_tls_crl_provider_test.cc | 67 ++++-- test/core/tsi/BUILD | 11 + .../tsi/ssl_transport_security_utils_test.cc | 208 +++++++++++++++++- test/core/tsi/test_creds/crl_data/BUILD | 1 + test/core/tsi/test_creds/crl_data/README | 5 + test/core/tsi/test_creds/crl_data/crls/BUILD | 2 + .../crl_data/crls/invalid_content.crl | 15 ++ .../crl_data/crls/invalid_signature.crl | 15 ++ test/core/tsi/test_creds/crl_data/evil_ca.key | 28 +++ test/core/tsi/test_creds/crl_data/evil_ca.pem | 21 ++ .../tsi/test_creds/crl_data/evil_ca_gen.sh | 18 ++ .../test_creds/crl_data/intermediate_gen.sh | 2 - test/core/tsi/transport_security_test_lib.cc | 24 ++ test/core/tsi/transport_security_test_lib.h | 8 + 23 files changed, 518 insertions(+), 44 deletions(-) create mode 100644 test/core/tsi/test_creds/crl_data/crls/invalid_content.crl create mode 100644 test/core/tsi/test_creds/crl_data/crls/invalid_signature.crl create mode 100644 test/core/tsi/test_creds/crl_data/evil_ca.key create mode 100644 test/core/tsi/test_creds/crl_data/evil_ca.pem create mode 100644 test/core/tsi/test_creds/crl_data/evil_ca_gen.sh diff --git a/BUILD b/BUILD index 96b6778cb0f..af9e76174d4 100644 --- a/BUILD +++ b/BUILD @@ -3879,6 +3879,7 @@ grpc_cc_library( external_deps = [ "absl/base:core_headers", "absl/status", + "absl/status:statusor", "absl/strings", "libcrypto", "libssl", diff --git a/CMakeLists.txt b/CMakeLists.txt index fb3dc1cb780..6be079f93a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15896,6 +15896,7 @@ add_executable(grpc_tls_crl_provider_test test/core/event_engine/event_engine_test_utils.cc test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.cc test/core/security/grpc_tls_crl_provider_test.cc + test/core/tsi/transport_security_test_lib.cc ) if(WIN32 AND MSVC) if(BUILD_SHARED_LIBS) @@ -28070,6 +28071,7 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_executable(ssl_transport_security_utils_test test/core/tsi/ssl_transport_security_utils_test.cc + test/core/tsi/transport_security_test_lib.cc ) if(WIN32 AND MSVC) if(BUILD_SHARED_LIBS) diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index ea4c8a834f7..cd50e12b948 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -10702,12 +10702,14 @@ targets: headers: - test/core/event_engine/event_engine_test_utils.h - test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.h + - test/core/tsi/transport_security_test_lib.h src: - test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.proto - test/core/util/fuzz_config_vars.proto - test/core/event_engine/event_engine_test_utils.cc - test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.cc - test/core/security/grpc_tls_crl_provider_test.cc + - test/core/tsi/transport_security_test_lib.cc deps: - gtest - protobuf @@ -17928,9 +17930,11 @@ targets: gtest: true build: test language: c++ - headers: [] + headers: + - test/core/tsi/transport_security_test_lib.h src: - test/core/tsi/ssl_transport_security_utils_test.cc + - test/core/tsi/transport_security_test_lib.cc deps: - gtest - grpc_test_util diff --git a/src/core/lib/security/credentials/tls/grpc_tls_crl_provider.cc b/src/core/lib/security/credentials/tls/grpc_tls_crl_provider.cc index f9c0868f7c1..f8adf5f12cc 100644 --- a/src/core/lib/security/credentials/tls/grpc_tls_crl_provider.cc +++ b/src/core/lib/security/credentials/tls/grpc_tls_crl_provider.cc @@ -53,15 +53,22 @@ namespace grpc_core { namespace experimental { namespace { -std::string IssuerFromCrl(X509_CRL* crl) { +// TODO(gtcooke94) Move ssl_transport_security_utils to it's own BUILD target +// and add this to it. +absl::StatusOr IssuerFromCrl(X509_CRL* crl) { if (crl == nullptr) { - return ""; + return absl::InvalidArgumentError("crl cannot be null"); } - char* buf = X509_NAME_oneline(X509_CRL_get_issuer(crl), nullptr, 0); - std::string ret; - if (buf != nullptr) { - ret = buf; + X509_NAME* issuer = X509_CRL_get_issuer(crl); + if (issuer == nullptr) { + return absl::InvalidArgumentError("crl cannot have null issuer"); } + unsigned char* buf = nullptr; + int len = i2d_X509_NAME(issuer, &buf); + if (len < 0 || buf == nullptr) { + return absl::InvalidArgumentError("crl cannot have null issuer"); + } + std::string ret(reinterpret_cast(buf), len); OPENSSL_free(buf); return ret; } @@ -103,11 +110,11 @@ absl::StatusOr> Crl::Parse(absl::string_view crl_string) { } absl::StatusOr> CrlImpl::Create(X509_CRL* crl) { - std::string issuer = IssuerFromCrl(crl); - if (issuer.empty()) { - return absl::InvalidArgumentError("Issuer of crl cannot be empty"); + absl::StatusOr issuer = IssuerFromCrl(crl); + if (!issuer.ok()) { + return issuer.status(); } - return std::make_unique(crl, issuer); + return std::make_unique(crl, *issuer); } CrlImpl::~CrlImpl() { X509_CRL_free(crl_); } diff --git a/src/core/tsi/ssl_transport_security.cc b/src/core/tsi/ssl_transport_security.cc index 7bba63a63f0..c9082e8c192 100644 --- a/src/core/tsi/ssl_transport_security.cc +++ b/src/core/tsi/ssl_transport_security.cc @@ -1004,15 +1004,14 @@ static int GetCrlFromProvider(X509_STORE_CTX* ctx, X509_CRL** crl_out, auto* provider = static_cast( SSL_CTX_get_ex_data(ssl_ctx, g_ssl_ctx_ex_crl_provider_index)); - char* buf = X509_NAME_oneline(X509_get_issuer_name(cert), nullptr, 0); - if (buf == nullptr) { - gpr_log(GPR_ERROR, "Certificate has null issuer, cannot do CRL lookup"); + absl::StatusOr issuer_name = grpc_core::IssuerFromCert(cert); + if (!issuer_name.ok()) { + gpr_log(GPR_INFO, "Could not get certificate issuer name"); return 0; } - grpc_core::experimental::CertificateInfoImpl cert_impl(buf); + grpc_core::experimental::CertificateInfoImpl cert_impl(*issuer_name); std::shared_ptr internal_crl = provider->GetCrl(cert_impl); - OPENSSL_free(buf); // There wasn't a CRL found in the provider. Returning 0 will end up causing // OpenSSL to return X509_V_ERR_UNABLE_TO_GET_CRL. We then catch that error // and behave how we want for a missing CRL. diff --git a/src/core/tsi/ssl_transport_security_utils.cc b/src/core/tsi/ssl_transport_security_utils.cc index 7d258046600..62bf21a2eed 100644 --- a/src/core/tsi/ssl_transport_security_utils.cc +++ b/src/core/tsi/ssl_transport_security_utils.cc @@ -23,6 +23,10 @@ #include #include #include +#include + +#include "absl/status/status.h" +#include "absl/status/statusor.h" #include "src/core/tsi/transport_security_interface.h" @@ -247,4 +251,70 @@ tsi_result SslProtectorUnprotect(const unsigned char* protected_frames_bytes, return result; } +bool VerifyCrlSignature(X509_CRL* crl, X509* issuer) { + if (issuer == nullptr || crl == nullptr) { + return false; + } + EVP_PKEY* ikey = X509_get_pubkey(issuer); + if (ikey == nullptr) { + // Can't verify signature because we couldn't get the pubkey, fail the + // check. + EVP_PKEY_free(ikey); + return false; + } + bool ret = X509_CRL_verify(crl, ikey) == 1; + EVP_PKEY_free(ikey); + return ret; +} + +bool VerifyCrlCertIssuerNamesMatch(X509_CRL* crl, X509* cert) { + if (cert == nullptr || crl == nullptr) { + return false; + } + X509_NAME* cert_issuer_name = X509_get_issuer_name(cert); + if (cert == nullptr) { + return false; + } + X509_NAME* crl_issuer_name = X509_CRL_get_issuer(crl); + if (crl_issuer_name == nullptr) { + return false; + } + return X509_NAME_cmp(cert_issuer_name, crl_issuer_name) == 0; +} + +bool HasCrlSignBit(X509* cert) { + if (cert == nullptr) { + return false; + } + // X509_get_key_usage was introduced in 1.1.1 + // A missing key usage extension means all key usages are valid. +#if OPENSSL_VERSION_NUMBER < 0x10100000 + // X509_check_ca sets cert->ex_flags. We dont use the return value, but those + // flags being set is important. + // https://github.com/openssl/openssl/blob/e818b74be2170fbe957a07b0da4401c2b694b3b8/crypto/x509v3/v3_purp.c#L585 + X509_check_ca(cert); + if (!(cert->ex_flags & EXFLAG_KUSAGE)) { + return true; + } + return (cert->ex_kusage & KU_CRL_SIGN) != 0; +#else + return (X509_get_key_usage(cert) & KU_CRL_SIGN) != 0; +#endif // OPENSSL_VERSION_NUMBER < 0x10100000 +} + +absl::StatusOr IssuerFromCert(X509* cert) { + if (cert == nullptr) { + return absl::InvalidArgumentError("cert cannot be null"); + } + X509_NAME* issuer = X509_get_issuer_name(cert); + unsigned char* buf = nullptr; + int len = i2d_X509_NAME(issuer, &buf); + if (len < 0 || buf == nullptr) { + return absl::InvalidArgumentError("could not read issuer name from cert"); + } + std::string ret(reinterpret_cast(buf), len); + OPENSSL_free(buf); + return ret; +} + } // namespace grpc_core diff --git a/src/core/tsi/ssl_transport_security_utils.h b/src/core/tsi/ssl_transport_security_utils.h index 14b38e644d1..22cacc84fbc 100644 --- a/src/core/tsi/ssl_transport_security_utils.h +++ b/src/core/tsi/ssl_transport_security_utils.h @@ -23,6 +23,8 @@ #include +#include "absl/status/status.h" +#include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include @@ -142,6 +144,22 @@ tsi_result SslProtectorUnprotect(const unsigned char* protected_frames_bytes, unsigned char* unprotected_bytes, size_t* unprotected_bytes_size); +// Verifies that `crl` was signed by `issuer. +// return: true if valid, false otherwise. +bool VerifyCrlSignature(X509_CRL* crl, X509* issuer); + +// Verifies the CRL issuer and certificate issuer name match. +// return: true if equal, false if not. +bool VerifyCrlCertIssuerNamesMatch(X509_CRL* crl, X509* cert); + +// Verifies the certificate in question has the cRLSign bit present. +// return: true if cRLSign bit is present, false otherwise. +bool HasCrlSignBit(X509* cert); + +// Gets a stable representation of the issuer name from an X509 certificate. +// return: a std::string of the DER encoding of the X509_NAME issuer name. +absl::StatusOr IssuerFromCert(X509* cert); + } // namespace grpc_core #endif // GRPC_SRC_CORE_TSI_SSL_TRANSPORT_SECURITY_UTILS_H diff --git a/src/python/grpcio/grpc/aio/_interceptor.py b/src/python/grpcio/grpc/aio/_interceptor.py index 38312b7955b..e7ceb00fbbf 100644 --- a/src/python/grpcio/grpc/aio/_interceptor.py +++ b/src/python/grpcio/grpc/aio/_interceptor.py @@ -476,7 +476,7 @@ class _InterceptedStreamResponseMixin: _response_aiter: Optional[AsyncIterable[ResponseType]] def _init_stream_response_mixin(self) -> None: - # Is initalized later, otherwise if the iterator is not finnally + # Is initalized later, otherwise if the iterator is not finally # consumed a logging warning is emmited by Asyncio. self._response_aiter = None diff --git a/test/core/security/BUILD b/test/core/security/BUILD index 6b7f7bf4899..2899bc9f523 100644 --- a/test/core/security/BUILD +++ b/test/core/security/BUILD @@ -569,6 +569,7 @@ grpc_cc_test( srcs = ["grpc_tls_crl_provider_test.cc"], data = [ "//test/core/tsi/test_creds/crl_data:ca.pem", + "//test/core/tsi/test_creds/crl_data:intermediate_ca.pem", "//test/core/tsi/test_creds/crl_data/crls:ab06acdd.r0", "//test/core/tsi/test_creds/crl_data/crls:b9322cac.r0", "//test/core/tsi/test_creds/crl_data/crls:current.crl", @@ -585,6 +586,7 @@ grpc_cc_test( "//test/core/event_engine:event_engine_test_utils", "//test/core/event_engine/fuzzing_event_engine", "//test/core/event_engine/fuzzing_event_engine:fuzzing_event_engine_proto", + "//test/core/tsi:transport_security_test_lib", "//test/core/util:fuzz_config_vars_proto", "//test/core/util:grpc_test_util", ], diff --git a/test/core/security/grpc_tls_crl_provider_test.cc b/test/core/security/grpc_tls_crl_provider_test.cc index f47028b160b..76cc0bdf34d 100644 --- a/test/core/security/grpc_tls_crl_provider_test.cc +++ b/test/core/security/grpc_tls_crl_provider_test.cc @@ -40,16 +40,15 @@ #include "test/core/event_engine/event_engine_test_utils.h" #include "test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.h" #include "test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.pb.h" +#include "test/core/tsi/transport_security_test_lib.h" #include "test/core/util/test_config.h" #include "test/core/util/tls_utils.h" static constexpr absl::string_view kCrlPath = "test/core/tsi/test_creds/crl_data/crls/current.crl"; static constexpr absl::string_view kCrlName = "current.crl"; -static constexpr absl::string_view kCrlIssuer = - "/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=testca"; -static constexpr absl::string_view kCrlIntermediateIssuer = - "/CN=intermediatecert.example.com"; +static constexpr absl::string_view kCrlIntermediateIssuerPath = + "test/core/tsi/test_creds/crl_data/intermediate_ca.pem"; static constexpr absl::string_view kCrlDirectory = "test/core/tsi/test_creds/crl_data/crls"; static constexpr absl::string_view kRootCert = @@ -88,9 +87,35 @@ class FakeDirectoryReader : public DirectoryReader { std::vector(); }; -class DirectoryReloaderCrlProviderTest : public ::testing::Test { +class CrlProviderTest : public ::testing::Test { public: void SetUp() override { + std::string pem_cert = GetFileContents(kRootCert.data()); + X509* issuer = ReadPemCert(pem_cert); + auto base_crl_issuer = IssuerFromCert(issuer); + ASSERT_EQ(base_crl_issuer.status(), absl::OkStatus()); + base_crl_issuer_ = *base_crl_issuer; + std::string intermediate_string = + GetFileContents(kCrlIntermediateIssuerPath.data()); + X509* intermediate_issuer = ReadPemCert(intermediate_string); + auto intermediate_crl_issuer = IssuerFromCert(intermediate_issuer); + ASSERT_EQ(intermediate_crl_issuer.status(), absl::OkStatus()); + intermediate_crl_issuer_ = *intermediate_crl_issuer; + X509_free(issuer); + X509_free(intermediate_issuer); + } + + void TearDown() override {} + + protected: + std::string base_crl_issuer_; + std::string intermediate_crl_issuer_; +}; + +class DirectoryReloaderCrlProviderTest : public CrlProviderTest { + public: + void SetUp() override { + CrlProviderTest::SetUp(); event_engine_ = std::make_shared( grpc_event_engine::experimental::FuzzingEventEngine::Options(), @@ -140,15 +165,15 @@ class DirectoryReloaderCrlProviderTest : public ::testing::Test { event_engine_; }; -TEST(CrlProviderTest, CanParseCrl) { +TEST_F(CrlProviderTest, CanParseCrl) { std::string crl_string = GetFileContents(kCrlPath.data()); absl::StatusOr> crl = Crl::Parse(crl_string); ASSERT_TRUE(crl.ok()) << crl.status(); ASSERT_NE(*crl, nullptr); - EXPECT_EQ((*crl)->Issuer(), kCrlIssuer); + EXPECT_EQ((*crl)->Issuer(), base_crl_issuer_); } -TEST(CrlProviderTest, InvalidFile) { +TEST_F(CrlProviderTest, InvalidFile) { std::string crl_string = "INVALID CRL FILE"; absl::StatusOr> crl = Crl::Parse(crl_string); EXPECT_EQ(crl.status(), @@ -156,18 +181,18 @@ TEST(CrlProviderTest, InvalidFile) { "Conversion from PEM string to X509 CRL failed.")); } -TEST(CrlProviderTest, StaticCrlProviderLookup) { +TEST_F(CrlProviderTest, StaticCrlProviderLookup) { std::vector crl_strings = {GetFileContents(kCrlPath.data())}; absl::StatusOr> provider = experimental::CreateStaticCrlProvider(crl_strings); ASSERT_TRUE(provider.ok()) << provider.status(); - CertificateInfoImpl cert(kCrlIssuer); + CertificateInfoImpl cert(base_crl_issuer_); auto crl = (*provider)->GetCrl(cert); ASSERT_NE(crl, nullptr); - EXPECT_EQ(crl->Issuer(), kCrlIssuer); + EXPECT_EQ(crl->Issuer(), base_crl_issuer_); } -TEST(CrlProviderTest, StaticCrlProviderLookupIssuerNotFound) { +TEST_F(CrlProviderTest, StaticCrlProviderLookupIssuerNotFound) { std::vector crl_strings = {GetFileContents(kCrlPath.data())}; absl::StatusOr> provider = experimental::CreateStaticCrlProvider(crl_strings); @@ -181,14 +206,14 @@ TEST_F(DirectoryReloaderCrlProviderTest, CrlLookupGood) { auto provider = CreateCrlProvider(kCrlDirectory, std::chrono::seconds(60), nullptr); ASSERT_TRUE(provider.ok()) << provider.status(); - CertificateInfoImpl cert(kCrlIssuer); + CertificateInfoImpl cert(base_crl_issuer_); auto crl = (*provider)->GetCrl(cert); ASSERT_NE(crl, nullptr); - EXPECT_EQ(crl->Issuer(), kCrlIssuer); - CertificateInfoImpl intermediate(kCrlIntermediateIssuer); + EXPECT_EQ(crl->Issuer(), base_crl_issuer_); + CertificateInfoImpl intermediate(intermediate_crl_issuer_); auto intermediate_crl = (*provider)->GetCrl(intermediate); ASSERT_NE(intermediate_crl, nullptr); - EXPECT_EQ(intermediate_crl->Issuer(), kCrlIntermediateIssuer); + EXPECT_EQ(intermediate_crl->Issuer(), intermediate_crl_issuer_); } TEST_F(DirectoryReloaderCrlProviderTest, CrlLookupMissingIssuer) { @@ -204,7 +229,7 @@ TEST_F(DirectoryReloaderCrlProviderTest, ReloadsAndDeletes) { const std::chrono::seconds kRefreshDuration(60); auto provider = CreateCrlProvider(kRefreshDuration, nullptr); ASSERT_TRUE(provider.ok()) << provider.status(); - CertificateInfoImpl cert(kCrlIssuer); + CertificateInfoImpl cert(base_crl_issuer_); auto should_be_no_crl = (*provider)->GetCrl(cert); ASSERT_EQ(should_be_no_crl, nullptr); // Give the provider files to find in the directory @@ -212,7 +237,7 @@ TEST_F(DirectoryReloaderCrlProviderTest, ReloadsAndDeletes) { event_engine_->TickForDuration(kRefreshDuration); auto crl = (*provider)->GetCrl(cert); ASSERT_NE(crl, nullptr); - EXPECT_EQ(crl->Issuer(), kCrlIssuer); + EXPECT_EQ(crl->Issuer(), base_crl_issuer_); // Now we won't see any files in our directory directory_reader_->SetFilesInDirectory({}); event_engine_->TickForDuration(kRefreshDuration); @@ -229,10 +254,10 @@ TEST_F(DirectoryReloaderCrlProviderTest, WithCorruption) { auto provider = CreateCrlProvider(kRefreshDuration, std::move(reload_error_callback)); ASSERT_TRUE(provider.ok()) << provider.status(); - CertificateInfoImpl cert(kCrlIssuer); + CertificateInfoImpl cert(base_crl_issuer_); auto crl = (*provider)->GetCrl(cert); ASSERT_NE(crl, nullptr); - EXPECT_EQ(crl->Issuer(), kCrlIssuer); + EXPECT_EQ(crl->Issuer(), base_crl_issuer_); EXPECT_EQ(reload_errors.size(), 0); // Point the provider at a non-crl file so loading fails // Should result in the CRL Reloader keeping the old CRL data @@ -240,7 +265,7 @@ TEST_F(DirectoryReloaderCrlProviderTest, WithCorruption) { event_engine_->TickForDuration(kRefreshDuration); auto crl_post_update = (*provider)->GetCrl(cert); ASSERT_NE(crl_post_update, nullptr); - EXPECT_EQ(crl_post_update->Issuer(), kCrlIssuer); + EXPECT_EQ(crl_post_update->Issuer(), base_crl_issuer_); EXPECT_EQ(reload_errors.size(), 1); } diff --git a/test/core/tsi/BUILD b/test/core/tsi/BUILD index 372ba9a3747..be8a6f3de82 100644 --- a/test/core/tsi/BUILD +++ b/test/core/tsi/BUILD @@ -64,12 +64,23 @@ grpc_cc_test( grpc_cc_test( name = "ssl_transport_security_utils_test", srcs = ["ssl_transport_security_utils_test.cc"], + data = [ + "//test/core/tsi/test_creds/crl_data:ca.pem", + "//test/core/tsi/test_creds/crl_data:evil_ca.pem", + "//test/core/tsi/test_creds/crl_data:intermediate_ca.pem", + "//test/core/tsi/test_creds/crl_data:leaf_signed_by_intermediate.pem", + "//test/core/tsi/test_creds/crl_data/crls:current.crl", + "//test/core/tsi/test_creds/crl_data/crls:intermediate.crl", + "//test/core/tsi/test_creds/crl_data/crls:invalid_content.crl", + "//test/core/tsi/test_creds/crl_data/crls:invalid_signature.crl", + ], external_deps = ["gtest"], language = "C++", tags = ["no_windows"], deps = [ "//:gpr", "//:grpc", + "//test/core/tsi:transport_security_test_lib", "//test/core/util:grpc_test_util", ], ) diff --git a/test/core/tsi/ssl_transport_security_utils_test.cc b/test/core/tsi/ssl_transport_security_utils_test.cc index 00e14627a14..bf5466eae1a 100644 --- a/test/core/tsi/ssl_transport_security_utils_test.cc +++ b/test/core/tsi/ssl_transport_security_utils_test.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include "absl/status/status.h" #include "absl/status/statusor.h" @@ -32,13 +33,30 @@ #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "src/core/lib/gprpp/load_file.h" +#include "src/core/lib/slice/slice.h" #include "src/core/tsi/transport_security.h" #include "src/core/tsi/transport_security_interface.h" +#include "test/core/tsi/transport_security_test_lib.h" #include "test/core/util/test_config.h" namespace grpc_core { namespace testing { +const char* kValidCrl = "test/core/tsi/test_creds/crl_data/crls/current.crl"; +const char* kCrlIssuer = "test/core/tsi/test_creds/crl_data/ca.pem"; +const char* kModifiedSignature = + "test/core/tsi/test_creds/crl_data/crls/invalid_signature.crl"; +const char* kModifiedContent = + "test/core/tsi/test_creds/crl_data/crls/invalid_content.crl"; +const char* kIntermediateCrl = + "test/core/tsi/test_creds/crl_data/crls/intermediate.crl"; +const char* kIntermediateCrlIssuer = + "test/core/tsi/test_creds/crl_data/intermediate_ca.pem"; +const char* kLeafCert = + "test/core/tsi/test_creds/crl_data/leaf_signed_by_intermediate.pem"; +const char* kEvilCa = "test/core/tsi/test_creds/crl_data/evil_ca.pem"; + using ::testing::ContainerEq; using ::testing::NotNull; using ::testing::TestWithParam; @@ -316,8 +334,8 @@ TEST_P(FlowTest, &protected_output_frames_size), tsi_result::TSI_OK); - // If |GetParam().plaintext_size| is larger than the inner client_buffer size - // (kMaxPlaintextBytesPerTlsRecord), then |Protect| will copy up to + // If |GetParam().plaintext_size| is larger than the inner client_buffer + // size (kMaxPlaintextBytesPerTlsRecord), then |Protect| will copy up to // |kMaxPlaintextBytesPerTlsRecord| bytes and output the protected // frame. Otherwise we need to manually flush the copied data in order // to get the protected frame. @@ -378,8 +396,8 @@ TEST_P(FlowTest, &protected_output_frames_size), tsi_result::TSI_OK); - // If |GetParam().plaintext_size| is larger than the inner server_buffer size - // (kMaxPlaintextBytesPerTlsRecord), then |Protect| will copy up to + // If |GetParam().plaintext_size| is larger than the inner server_buffer + // size (kMaxPlaintextBytesPerTlsRecord), then |Protect| will copy up to // |kMaxPlaintextBytesPerTlsRecord| bytes and output the protected // frame. Otherwise we need to manually flush the copied data in order // to get the protected frame. @@ -429,6 +447,188 @@ INSTANTIATE_TEST_SUITE_P(FrameProtectorUtil, FlowTest, #endif // OPENSSL_IS_BORINGSSL +class CrlUtils : public ::testing::Test { + public: + static void SetUpTestSuite() { +#if OPENSSL_VERSION_NUMBER >= 0x10100000 + OPENSSL_init_ssl(/*opts=*/0, /*settings=*/nullptr); +#else + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); +#endif + } + + void SetUp() override { + absl::StatusOr root_crl = LoadFile(kValidCrl, false); + ASSERT_EQ(root_crl.status(), absl::OkStatus()) << root_crl.status(); + root_crl_ = ReadCrl(root_crl->as_string_view()); + absl::StatusOr intermediate_crl = LoadFile(kIntermediateCrl, false); + ASSERT_EQ(intermediate_crl.status(), absl::OkStatus()) + << intermediate_crl.status(); + intermediate_crl_ = ReadCrl(intermediate_crl->as_string_view()); + absl::StatusOr invalid_signature_crl = + LoadFile(kModifiedSignature, false); + ASSERT_EQ(invalid_signature_crl.status(), absl::OkStatus()) + << invalid_signature_crl.status(); + invalid_signature_crl_ = ReadCrl(invalid_signature_crl->as_string_view()); + + absl::StatusOr root_ca = LoadFile(kCrlIssuer, false); + ASSERT_EQ(root_ca.status(), absl::OkStatus()); + root_ca_ = ReadPemCert(root_ca->as_string_view()); + absl::StatusOr intermediate_ca = + LoadFile(kIntermediateCrlIssuer, false); + ASSERT_EQ(intermediate_ca.status(), absl::OkStatus()); + intermediate_ca_ = ReadPemCert(intermediate_ca->as_string_view()); + absl::StatusOr leaf_cert = LoadFile(kLeafCert, false); + ASSERT_EQ(leaf_cert.status(), absl::OkStatus()); + leaf_cert_ = ReadPemCert(leaf_cert->as_string_view()); + absl::StatusOr evil_ca = LoadFile(kEvilCa, false); + ASSERT_EQ(evil_ca.status(), absl::OkStatus()); + evil_ca_ = ReadPemCert(evil_ca->as_string_view()); + } + + void TearDown() override { + X509_CRL_free(root_crl_); + X509_CRL_free(intermediate_crl_); + X509_CRL_free(invalid_signature_crl_); + X509_free(root_ca_); + X509_free(intermediate_ca_); + X509_free(leaf_cert_); + X509_free(evil_ca_); + } + + protected: + X509_CRL* root_crl_; + X509_CRL* intermediate_crl_; + X509_CRL* invalid_signature_crl_; + X509* root_ca_; + X509* intermediate_ca_; + X509* leaf_cert_; + X509* evil_ca_; +}; + +TEST_F(CrlUtils, VerifySignatureValid) { + EXPECT_TRUE(VerifyCrlSignature(root_crl_, root_ca_)); +} + +TEST_F(CrlUtils, VerifySignatureIntermediateValid) { + EXPECT_TRUE(VerifyCrlSignature(intermediate_crl_, intermediate_ca_)); +} + +TEST_F(CrlUtils, VerifySignatureModifiedSignature) { + EXPECT_FALSE(VerifyCrlSignature(invalid_signature_crl_, root_ca_)); +} + +TEST_F(CrlUtils, VerifySignatureModifiedContent) { + absl::StatusOr crl_slice = LoadFile(kModifiedContent, false); + ASSERT_EQ(crl_slice.status(), absl::OkStatus()) << crl_slice.status(); + X509_CRL* crl = ReadCrl(crl_slice->as_string_view()); + EXPECT_EQ(crl, nullptr); +} + +TEST_F(CrlUtils, VerifySignatureWrongIssuer) { + EXPECT_FALSE(VerifyCrlSignature(root_crl_, intermediate_ca_)); +} + +TEST_F(CrlUtils, VerifySignatureWrongIssuer2) { + EXPECT_FALSE(VerifyCrlSignature(intermediate_crl_, root_ca_)); +} + +TEST_F(CrlUtils, VerifySignatureNullCrl) { + EXPECT_FALSE(VerifyCrlSignature(nullptr, root_ca_)); +} + +TEST_F(CrlUtils, VerifySignatureNullCert) { + EXPECT_FALSE(VerifyCrlSignature(intermediate_crl_, nullptr)); +} + +TEST_F(CrlUtils, VerifySignatureNullCrlAndCert) { + EXPECT_FALSE(VerifyCrlSignature(nullptr, nullptr)); +} + +TEST_F(CrlUtils, VerifyIssuerNamesMatch) { + EXPECT_TRUE(VerifyCrlCertIssuerNamesMatch(root_crl_, root_ca_)); +} + +TEST_F(CrlUtils, VerifyIssuerNamesDontMatch) { + EXPECT_FALSE(VerifyCrlCertIssuerNamesMatch(root_crl_, leaf_cert_)); +} + +TEST_F(CrlUtils, DuplicatedIssuerNamePassesButSignatureCheckFails) { + // The issuer names will match, but it should fail a signature check + EXPECT_TRUE(VerifyCrlCertIssuerNamesMatch(root_crl_, evil_ca_)); + EXPECT_FALSE(VerifyCrlSignature(root_crl_, evil_ca_)); +} + +TEST_F(CrlUtils, VerifyIssuerNameNullCrl) { + EXPECT_FALSE(VerifyCrlCertIssuerNamesMatch(nullptr, root_ca_)); +} + +TEST_F(CrlUtils, VerifyIssuerNameNullCert) { + EXPECT_FALSE(VerifyCrlCertIssuerNamesMatch(intermediate_crl_, nullptr)); +} + +TEST_F(CrlUtils, VerifyIssuerNameNullCrlAndCert) { + EXPECT_FALSE(VerifyCrlCertIssuerNamesMatch(nullptr, nullptr)); +} + +TEST_F(CrlUtils, HasCrlSignBitExists) { EXPECT_TRUE(HasCrlSignBit(root_ca_)); } + +TEST_F(CrlUtils, HasCrlSignBitMissing) { + EXPECT_FALSE(HasCrlSignBit(leaf_cert_)); +} + +TEST_F(CrlUtils, HasCrlSignBitNullCert) { + EXPECT_FALSE(HasCrlSignBit(nullptr)); +} + +TEST_F(CrlUtils, IssuerFromIntermediateCert) { + auto issuer = IssuerFromCert(intermediate_ca_); + // Build the known name for comparison + unsigned char* buf = nullptr; + X509_NAME* expected_issuer_name = X509_NAME_new(); + ASSERT_TRUE( + X509_NAME_add_entry_by_txt(expected_issuer_name, "C", MBSTRING_ASC, + (const unsigned char*)"AU", -1, -1, 0)); + ASSERT_TRUE(X509_NAME_add_entry_by_txt( + expected_issuer_name, "ST", MBSTRING_ASC, + (const unsigned char*)"Some-State", -1, -1, 0)); + ASSERT_TRUE(X509_NAME_add_entry_by_txt( + expected_issuer_name, "O", MBSTRING_ASC, + (const unsigned char*)"Internet Widgits Pty Ltd", -1, -1, 0)); + ASSERT_TRUE( + X509_NAME_add_entry_by_txt(expected_issuer_name, "CN", MBSTRING_ASC, + (const unsigned char*)"testca", -1, -1, 0)); + int len = i2d_X509_NAME(expected_issuer_name, &buf); + std::string expected_issuer_name_der(reinterpret_cast(buf), len); + OPENSSL_free(buf); + X509_NAME_free(expected_issuer_name); + ASSERT_EQ(issuer.status(), absl::OkStatus()); + EXPECT_EQ(*issuer, expected_issuer_name_der); +} + +TEST_F(CrlUtils, IssuerFromLeaf) { + auto issuer = IssuerFromCert(leaf_cert_); + // Build the known name for comparison + unsigned char* buf = nullptr; + X509_NAME* expected_issuer_name = X509_NAME_new(); + ASSERT_TRUE(X509_NAME_add_entry_by_txt( + expected_issuer_name, "CN", MBSTRING_ASC, + (const unsigned char*)"intermediatecert.example.com", -1, -1, 0)); + int len = i2d_X509_NAME(expected_issuer_name, &buf); + std::string expected_issuer_name_der(reinterpret_cast(buf), len); + OPENSSL_free(buf); + X509_NAME_free(expected_issuer_name); + ASSERT_EQ(issuer.status(), absl::OkStatus()); + EXPECT_EQ(*issuer, expected_issuer_name_der); +} + +TEST_F(CrlUtils, IssuerFromCertNull) { + auto issuer = IssuerFromCert(nullptr); + EXPECT_EQ(issuer.status().code(), absl::StatusCode::kInvalidArgument); +} + } // namespace testing } // namespace grpc_core diff --git a/test/core/tsi/test_creds/crl_data/BUILD b/test/core/tsi/test_creds/crl_data/BUILD index ae3e58f7f72..d079cd5a7f0 100644 --- a/test/core/tsi/test_creds/crl_data/BUILD +++ b/test/core/tsi/test_creds/crl_data/BUILD @@ -25,4 +25,5 @@ exports_files([ "leaf_and_intermediate_chain.pem", "intermediate_ca.key", "intermediate_ca.pem", + "evil_ca.pem", ]) diff --git a/test/core/tsi/test_creds/crl_data/README b/test/core/tsi/test_creds/crl_data/README index 80f3568e95a..d19258c9440 100644 --- a/test/core/tsi/test_creds/crl_data/README +++ b/test/core/tsi/test_creds/crl_data/README @@ -46,6 +46,11 @@ Generate a chain with a leaf cert signed by an intermediate CA and revoke the in Run `intermediate_gen.sh` from the `test/core/tsi/test_creds/crl_data` directory +Generate a CA with the same issuer name but a different public key than the base CA +---------------------------------------------------------------------------- + +Run `evil_ca_gen.sh` from the `test/core/tsi/test_creds/crl_data` directory + Clean up: --------- diff --git a/test/core/tsi/test_creds/crl_data/crls/BUILD b/test/core/tsi/test_creds/crl_data/crls/BUILD index 922d279aa6a..ecbaa82a9df 100644 --- a/test/core/tsi/test_creds/crl_data/crls/BUILD +++ b/test/core/tsi/test_creds/crl_data/crls/BUILD @@ -19,4 +19,6 @@ exports_files([ "b9322cac.r0", "current.crl", "intermediate.crl", + "invalid_signature.crl", + "invalid_content.crl", ]) diff --git a/test/core/tsi/test_creds/crl_data/crls/invalid_content.crl b/test/core/tsi/test_creds/crl_data/crls/invalid_content.crl new file mode 100644 index 00000000000..22d8b48f0a0 --- /dev/null +++ b/test/core/tsi/test_creds/crl_data/crls/invalid_content.crl @@ -0,0 +1,15 @@ +-----BEGIN X509 CRL----- +AIICUDCCATgCAQEwDQYJKoZIhvcNAQELBQAwVjELMAkGA1UEBhMCQVUxEzARBgNV +BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 +ZDEPMA0GA1UEAwwGdGVzdGNhFw0yMzAzMDMxODA2NDNaFw0zMzAyMjgxODA2NDNa +MIGcMCUCFEpMyQOrk+uXDu20PhHwDJeua83mFw0yMzAzMDMxNjU5NTNaMCUCFEpM +yQOrk+uXDu20PhHwDJeua83nFw0yMzAzMDMxNzMxNDBaMCUCFEpMyQOrk+uXDu20 +PhHwDJeua83xFw0yMzAzMDMxODA2NDNaMCUCFFIgumScY9chZ0u8tUhjsOUh38hB +Fw0yMjAyMDQyMjExMTFaoA8wDTALBgNVHRQEBAICEAgwDQYJKoZIhvcNAQELBQAD +ggEBADohIZwm/gWLIc2yFJJbKzkdRmOq1s/MqnJxi5NutNumXTIPrZJqGzk8O4U6 +VasicIB2YD0o3arzUxCDyHv7VyJI7SVS0lqlmOxoOEOv2+CB6MxAOdKItkzbVVxu +0erx5HcKAGa7ZIAeekX1F1DcAgpN5Gt5uGhkMw3ObTCpEFRw+ZKET3WFQ6bG4AJ6 +GwOnNYG1LjaNigxG/k4K7A+grs/XnsNcpULbCROl7Qw4kyf1esrjS9utEO0YQQz4 +LgBTPZzQHlsirmxp+e5WR8LiDsKmbmAaBL+gV1Bkjj73c4pNJvoV/V1Ubdv0LCvH +DjrJtp10F0RGMRm6m9OuZYUSFzs= +-----END X509 CRL----- diff --git a/test/core/tsi/test_creds/crl_data/crls/invalid_signature.crl b/test/core/tsi/test_creds/crl_data/crls/invalid_signature.crl new file mode 100644 index 00000000000..2431949ce77 --- /dev/null +++ b/test/core/tsi/test_creds/crl_data/crls/invalid_signature.crl @@ -0,0 +1,15 @@ +-----BEGIN X509 CRL----- +MIICUDCCATgCAQEwDQYJKoZIhvcNAQELBQAwVjELMAkGA1UEBhMCQVUxEzARBgNV +BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 +ZDEPMA0GA1UEAwwGdGVzdGNhFw0yMzAzMDMxODA2NDNaFw0zMzAyMjgxODA2NDNa +MIGcMCUCFEpMyQOrk+uXDu20PhHwDJeua83mFw0yMzAzMDMxNjU5NTNaMCUCFEpM +yQOrk+uXDu20PhHwDJeua83nFw0yMzAzMDMxNzMxNDBaMCUCFEpMyQOrk+uXDu20 +PhHwDJeua83xFw0yMzAzMDMxODA2NDNaMCUCFFIgumScY9chZ0u8tUhjsOUh38hB +Fw0yMjAyMDQyMjExMTFaoA8wDTALBgNVHRQEBAICEAgwDQYJKoZIhvcNAQELBQAD +ggEBADohIZwm/gWLIc2yFJJbKzkdRmOq1s/MqnJxi5NutNumXTIPrZJqGzk8O4U6 +VasicIB2YD0o3arzUxCDyHv7VyJI7SVS0lqlmOxoOEOv2+CB6MxAOdKItkzbVVxu +0erx5HcKAGa7ZIAeekX1F1DcAgpN5Gt5uGhkMw3ObTCpEFRw+ZKET3WFQ6bG4AJ6 +GwOnNYG1LjaNigxG/k4K7A+grs/XnsNcpULbCROl7Qw4kyf1esrjS9utEO0YQQz4 +LgBTPZzQHlsirmxp+e5WR8LiDsKmbmAaBL+gV1Bkjj73c4pNJvoV/V1Ubdv0LCvH +DjrJtp10F0RGMRm6m9OuZYUSFza= +-----END X509 CRL----- diff --git a/test/core/tsi/test_creds/crl_data/evil_ca.key b/test/core/tsi/test_creds/crl_data/evil_ca.key new file mode 100644 index 00000000000..41614f98bc4 --- /dev/null +++ b/test/core/tsi/test_creds/crl_data/evil_ca.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7RwHo8bWaioeI +oqq4qRkiRfqAl/XlaRCyygkMtkjuOy0LA42+LFXXNvDD8eVvVd3615Qopm0XzABd +iz2QiJBZH9qvvmZFg7vG4rbNMCHIN+0YYIOp5tJuyBUVhZ+/f/jZ+LoJeZgTRngQ +tMUmhs7kn4ttT+DC7ZHKhPf5vUokSPG4N2tBx21y2BzRup36q09vfvZeVEe5YxAM +KGWEOcCY/S5vTVeEJCqP2OfMmskIHq2cYWr6ZJzBpdhJXX6rTDWYlCzX49mzPrn6 +povhA/bENv9Gy1OHqPKt+EWEJCaurerkFwF74OG9zp/jCKZJTVkyxnCYjT2rYiDX +gWvNwdeHAgMBAAECggEADyya44Mzj0Y6jXV8tsIA0YLxCrAFZ7q3ydIj9z3ih+cP +PcK3yUPHYCJJUjR3PipWIP03Dy949xd7pMNjpXfjQPgbRz0lWpboxUiDvk7FlfcD +b4O2d12cCbI4Px+uHh1M48B1tnnTOtCYFDvJc6yITARUuZ03cs6UDwrvcB1dygsO +2sZLUOkWQb2DCMq86bxmkHvjuh3gj/CMTJv0Kprlo3YcKNgCwiNygEzlusyIcwpf +dU/SNoWcxY+F0F6wFC0uj75wWqDB6bmfCpY8Bb3Ey7TgWDTWjsB/NQsWbSxZ9o5i +qjQ6WSLKpLLLB/8dXxhk3Nz9tfonavBpLB+4fNpFFQKBgQDi61A3/U88iEo+sxMm +L3i0OS9g/mAnYQ7zYjq42eVyDTfa+eBck1Jmp1KEblfy7Eo3iyApNFoIzFz8va8N +tPNFK/K4mrf1aiFOk0SnvCstW8SBS99hBHXqrMnXrRh+L/OafM4sj88P4RbZxcIs +9RNiDIqcXAPDVU5aHIhs7CFzYwKBgQDTRyOR9PoTQnu0HV0ODDNzmP1eRWrXZ62N +khe9bm0TIG25Q1wsoR6MT5fxZlTe62FH7A5QgEheRtMctr+XGC2H+3N3MUxsTy37 +knPFiDl6Gs5DqKroewiDNbkziMOgctG/z6ORPiGghTRsn6y5dBaMstfvgip8fj5z +ytzgSfiujQKBgHZraOSfK++iDGTmHRMraOlcgm4ysck7LIs08wIurD+1yDVde4m0 +VCdAIJ792qXqS9zqnPED4gx/YfN/pdAYY2/wvG08SM4pAZK45fZHC51TK5xyFPPT +WRoL7BXCvmpz6cPwZ8P3lI5r3/nr6yZ9Cw17EAcDOe+BIC+EfmmhXN+TAoGBAIp0 +oDbSV9+vPen3JDhEfqNOqxvQWgf3haC1EKGvcAOMyNsT7Z/BpodE0cn8ybmcfw/m +/ip7JvHBcC/tAvk9evkWK8D8qZyA9x1aCEx2zVPbpThpnDbmCdoSpt/CzJClLheJ +NyPDl73eDVDyAvs1vGFQAnqOztDu2nZ/huflEfcxAoGAbLUQV5PjqJrsIosEMXsv +qOzQZ5BBEk/jo9zqYSNXWVs0I9Invj5iAYewoM5qn9DFQ3q3O/mPHxF6HT7JHfjn +T8wdOTQk5L1yaaSFsiti3C3AQ2zShT1k6m3V+mf0iWJw878LCURQQFNIHu7zVdXy +4xwQpVw2CN7iufRYN7kOcDo= +-----END PRIVATE KEY----- diff --git a/test/core/tsi/test_creds/crl_data/evil_ca.pem b/test/core/tsi/test_creds/crl_data/evil_ca.pem new file mode 100644 index 00000000000..6708690b1db --- /dev/null +++ b/test/core/tsi/test_creds/crl_data/evil_ca.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDeTCCAmGgAwIBAgIUULA9nt1NB3W1i4RevrKeRQQLkaIwDQYJKoZIhvcNAQEL +BQAwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTI0 +MDEyMjIxNDAyMFoXDTM0MDExOTIxNDAyMFowVjELMAkGA1UEBhMCQVUxEzARBgNV +BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 +ZDEPMA0GA1UEAwwGdGVzdGNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAu0cB6PG1moqHiKKquKkZIkX6gJf15WkQssoJDLZI7jstCwONvixV1zbww/Hl +b1Xd+teUKKZtF8wAXYs9kIiQWR/ar75mRYO7xuK2zTAhyDftGGCDqebSbsgVFYWf +v3/42fi6CXmYE0Z4ELTFJobO5J+LbU/gwu2RyoT3+b1KJEjxuDdrQcdtctgc0bqd ++qtPb372XlRHuWMQDChlhDnAmP0ub01XhCQqj9jnzJrJCB6tnGFq+mScwaXYSV1+ +q0w1mJQs1+PZsz65+qaL4QP2xDb/RstTh6jyrfhFhCQmrq3q5BcBe+Dhvc6f4wim +SU1ZMsZwmI09q2Ig14FrzcHXhwIDAQABoz8wPTAMBgNVHRMEBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUjcQvfJ6kAUgljgToPpQ0DmCW0Q8wDQYJKoZI +hvcNAQELBQADggEBALLNhOYqlhOcCsTD1SPfm9MAjfpV1EjSjDCpIfwCk5gI2CUX +g7MyUzn2gQJUiYx74BKmjv6W/sLzNxqR0wZQUr4d/7HX+Lm0xCCYdIUELEM8lZ30 +maBJ599cQnLXDB1ZFEekj3DMM6jL7OQnBaDs5jW4GcDcuwd5cgXfgIaZVjBVJ11Y +CFAhIuh5CM8xhqxWYWY+h0VLU64s8WCNrBEy1OU5KpQRfpd4cvpoWn7E1SfhK1Iq +Bp+1k4oDBpGGw4NLXI3i1aU8x1+KoXxNRg5dOED0OLgppvaWB2yIpqBlcZDaNpq4 +P+WFGBiSUpWU5yYwCDvQAgTWtWkmyflVwslHaGs= +-----END CERTIFICATE----- diff --git a/test/core/tsi/test_creds/crl_data/evil_ca_gen.sh b/test/core/tsi/test_creds/crl_data/evil_ca_gen.sh new file mode 100644 index 00000000000..31dec70057c --- /dev/null +++ b/test/core/tsi/test_creds/crl_data/evil_ca_gen.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# Copyright 2024 gRPC authors. +# +# 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. + +# Generates a CA with the same issuer name as the good CA in this directory +openssl req -x509 -new -newkey rsa:2048 -nodes -keyout evil_ca.key -out evil_ca.pem \ + -config ca-openssl.cnf -days 3650 -extensions v3_req diff --git a/test/core/tsi/test_creds/crl_data/intermediate_gen.sh b/test/core/tsi/test_creds/crl_data/intermediate_gen.sh index dd9fa2b0473..4a3ec030726 100644 --- a/test/core/tsi/test_creds/crl_data/intermediate_gen.sh +++ b/test/core/tsi/test_creds/crl_data/intermediate_gen.sh @@ -62,11 +62,9 @@ cp "./intermediate_ca/intermediate.crl" ./crls cp current.crl ./crls/ openssl rehash ./crls/ -mkdir crls_missing_intermediate cp current.crl ./crls_missing_intermediate/ openssl rehash ./crls_missing_intermediate/ -mkdir crls_missing_root cp intermediate.crl ./crls_missing_root/ openssl rehash ./crls_missing_root/ diff --git a/test/core/tsi/transport_security_test_lib.cc b/test/core/tsi/transport_security_test_lib.cc index 811ecff0076..a0be6edf466 100644 --- a/test/core/tsi/transport_security_test_lib.cc +++ b/test/core/tsi/transport_security_test_lib.cc @@ -762,3 +762,27 @@ std::string GenerateSelfSignedCertificate( BN_free(n); return pem; } + +X509* ReadPemCert(absl::string_view pem_cert) { + BIO* cert_bio = + BIO_new_mem_buf(pem_cert.data(), static_cast(pem_cert.size())); + // Errors on BIO + if (cert_bio == nullptr) { + return nullptr; + } + X509* cert = PEM_read_bio_X509(cert_bio, nullptr, nullptr, nullptr); + BIO_free(cert_bio); + return cert; +} + +X509_CRL* ReadCrl(absl::string_view crl_pem) { + BIO* crl_bio = + BIO_new_mem_buf(crl_pem.data(), static_cast(crl_pem.size())); + // Errors on BIO + if (crl_bio == nullptr) { + return nullptr; + } + X509_CRL* crl = PEM_read_bio_X509_CRL(crl_bio, nullptr, nullptr, nullptr); + BIO_free(crl_bio); + return crl; +} diff --git a/test/core/tsi/transport_security_test_lib.h b/test/core/tsi/transport_security_test_lib.h index 69f07c1269c..6e3efec2bff 100644 --- a/test/core/tsi/transport_security_test_lib.h +++ b/test/core/tsi/transport_security_test_lib.h @@ -19,6 +19,8 @@ #ifndef GRPC_TEST_CORE_TSI_TRANSPORT_SECURITY_TEST_LIB_H #define GRPC_TEST_CORE_TSI_TRANSPORT_SECURITY_TEST_LIB_H +#include + #include #include "src/core/tsi/transport_security_interface.h" @@ -238,4 +240,10 @@ struct SelfSignedCertificateOptions { std::string GenerateSelfSignedCertificate( const SelfSignedCertificateOptions& options); +// Returns the OpenSSL representation of a PEM cert. +X509* ReadPemCert(absl::string_view pem_cert); + +// Returns the OpenSSL representation of a CRL. +X509_CRL* ReadCrl(absl::string_view crl_pem); + #endif // GRPC_TEST_CORE_TSI_TRANSPORT_SECURITY_TEST_LIB_H