diff --git a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc index dc40755b9ebf..338977cedb8e 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc +++ b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc @@ -4,6 +4,7 @@ #include +#include #include "envoy/extensions/transport_sockets/tls/v3/common.pb.h" #include "envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.pb.h" #include "envoy/network/transport_socket.h" @@ -109,40 +110,15 @@ std::shared_ptr SPIFFEValidator::loadTrustBundles() { error = true; return false; } - - STACK_OF(GENERAL_NAME)* san_names = static_cast( - X509_get_ext_d2i(x509.get(), NID_subject_alt_name, nullptr, nullptr)); - if (san_names != nullptr) { - for (size_t i = 0; i < sk_GENERAL_NAME_num(san_names); i++) { - const GENERAL_NAME* current_name = sk_GENERAL_NAME_value(san_names, i); - if (current_name->type == GEN_URI) { - const char* uri = reinterpret_cast( - ASN1_STRING_get0_data(current_name->d.uniformResourceIdentifier)); - if (absl::StartsWith(uri, "spiffe://")) { - std::string san_string(uri); - const std::string& san_domain = extractTrustDomain(san_string); - if (domain_name != san_domain) { - ENVOY_LOG(error, "Domain specified in bundle '{}' and in SAN '{}' do not match", - domain_name, san_domain); - error = true; - return false; - } - - if (X509_STORE_add_cert(spiffeDataPtr->trust_bundle_stores[domain_name].get(), - x509.get()) != 1) { - ENVOY_LOG(error, "Failed to add x509 object while loading '{}'", - trust_bundle_file_name_); - error = true; - return false; - } - X509_up_ref(x509.get()); - spiffeDataPtr->ca_certs.push_back(std::move(x509)); - break; - } - } - } - sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); + if (X509_STORE_add_cert(spiffeDataPtr->trust_bundle_stores[domain_name].get(), + x509.get()) != 1) { + ENVOY_LOG(error, "Failed to add x509 object while loading '{}'", + trust_bundle_file_name_); + error = true; + return false; } + X509_up_ref(x509.get()); + spiffeDataPtr->ca_certs.push_back(std::move(x509)); } } } diff --git a/test/common/tls/test_data/certs.sh b/test/common/tls/test_data/certs.sh index 75a8898d304d..e5ac587f0d79 100755 --- a/test/common/tls/test_data/certs.sh +++ b/test/common/tls/test_data/certs.sh @@ -16,6 +16,66 @@ cleanup() { rm ./intermediate_crl_* } + +# $@= (repeatable for multiple domains and certs per domain) +generate_spiffe_trust_bundle_mapping() { + local trust_domains=() + + # Collecting all trust domain arguments + while [[ $# -gt 0 ]]; do + trust_domains+=( "$1" "$2" "$3" ) + shift 3 + done + + echo "{" > trust_bundles.json + echo " \"trust_domains\": {" >> trust_bundles.json + + local first_domain=true + for (( i=0; i<${#trust_domains[@]}; i+=3 )); do + local trust_domain="${trust_domains[i]}" + local ca_cert_files=(${trust_domains[i+1]//,/ }) + local sequence_number="${trust_domains[i+2]}" + + if [ "$first_domain" = false ]; then + echo " }," >> trust_bundles.json + fi + first_domain=false + + echo " \"$trust_domain\": {" >> trust_bundles.json + echo " \"sequence_number\": $sequence_number," >> trust_bundles.json + echo " \"keys\": [" >> trust_bundles.json + + local first_key=true + for ca_cert_file in "${ca_cert_files[@]}"; do + local base64_der=$(openssl x509 -in "$ca_cert_file" -outform DER | base64 | tr -d '\n\r') + local modulus=$(openssl x509 -in "$ca_cert_file" -noout -modulus | cut -d'=' -f2 | xxd -r -p | base64 | tr -d "=\n" | tr '/+' '_-') + + if [ "$first_key" = false ]; then + echo "," >> trust_bundles.json + fi + first_key=false + + cat <> trust_bundles.json + { + "kty": "RSA", + "use": "x509-svid", + "x5c": [ + "$base64_der" + ], + "n": "$modulus", + "e": "AQAB" + } +EOF + done + + echo " ]" >> trust_bundles.json + done + + echo " }" >> trust_bundles.json + echo " }" >> trust_bundles.json + echo "}" >> trust_bundles.json +} + # $1= $2=[issuer name] generate_ca() { local extra_args=() @@ -398,6 +458,8 @@ generate_x509_cert spiffe_san ca generate_rsa_key non_spiffe_san generate_x509_cert non_spiffe_san ca +generate_spiffe_trust_bundle_mapping "example.com" "ca_cert.pem" 12035488 "lyft.com" "ca_cert.pem" 12035489 + cp -f spiffe_san_cert.cfg expired_spiffe_san_cert.cfg generate_rsa_key expired_spiffe_san generate_x509_cert expired_spiffe_san ca -1 diff --git a/test/common/tls/test_data/trust_bundles.json b/test/common/tls/test_data/trust_bundles.json new file mode 100644 index 000000000000..1c90b08b2208 --- /dev/null +++ b/test/common/tls/test_data/trust_bundles.json @@ -0,0 +1,32 @@ +{ + "trust_domains": { + "example.com": { + "sequence_number": 12035488, + "keys": [ + { + "kty": "RSA", + "use": "x509-svid", + "x5c": [ + "MIID3TCCAsWgAwIBAgIUdmyO+EFUvRajCw+EOB+l8GkoygwwDQYJKoZIhvcNAQELBQAwdjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsMEEx5ZnQgRW5naW5lZXJpbmcxEDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjQxMDExMjIzMzA2WhcNMjYxMDExMjIzMzA2WjB2MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwETHlmdDEZMBcGA1UECwwQTHlmdCBFbmdpbmVlcmluZzEQMA4GA1UEAwwHVGVzdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKXhLaaPG1XmO+gIMILimAG4znTkbppPncqpHxs34mY+VtvAvy6wrScqUnSZylQoyrGtQuIMrCF3D7XhMjmYAH7gASCZninmsQqcvIKUP9yFEvRmLgEbR/dGjEB4aKIl230LZgKMl35I2mqvxvS8etx8BBOvDaS6sDeySDGq3UyDiVTFnSI0e2haC6ktp+zIJL7iqDhJpCVkNueBiDLfU2z/9F5jx2huaS2Y7tEndWGhJRd9zvqxf/Fw6CtruSou8TNXP+4y+Wtkdp7wWNJFYVoLoV2ugx+jOvPKLoy+JVa2hYviH2cN5w524dO3iGOWv/u/+k0BRw5GpOcJUq2CcesCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFA3ZY65POlo1Y6C9pujLEpqQOWOsMB8GA1UdIwQYMBaAFA3ZY65POlo1Y6C9pujLEpqQOWOsMA0GCSqGSIb3DQEBCwUAA4IBAQBcZXbRD0sAZPXXe/E5kOy0gN2iiux7JkBOdIXe7QVgBV1sCOhHHEWJ/Vawz5wK1/UXj7P3AqJKT3PbWy2vWNmoXxtD4ix8cIBPU3gxQ9HrhTgjof0mXIsujy+mUwm5Dmu/+xt1NaUXb9MvXUqlNTkdTyn3TMhQ7C0FzQYERTgwNPv2wJki+uq1Tt0G8XyW7QFCiUsdXPtEbYxv1dOwsGAYf/N426XuUNn6luqdHonMb0Xp7LpNM4pBH8Nh4G87itUmsRFUQ2s7QVff7FYOHYhEflC07DPmtOhOVXaGtut/2BFU/GbKQlLNcAvcVOpMA/Z9pobsffgGYBrZgmN3Ljnt" + ], + "n": "peEtpo8bVeY76AgwguKYAbjOdORumk-dyqkfGzfiZj5W28C_LrCtJypSdJnKVCjKsa1C4gysIXcPteEyOZgAfuABIJmeKeaxCpy8gpQ_3IUS9GYuARtH90aMQHhooiXbfQtmAoyXfkjaaq_G9Lx63HwEE68NpLqwN7JIMardTIOJVMWdIjR7aFoLqS2n7MgkvuKoOEmkJWQ254GIMt9TbP_0XmPHaG5pLZju0Sd1YaElF33O-rF_8XDoK2u5Ki7xM1c_7jL5a2R2nvBY0kVhWguhXa6DH6M688oujL4lVraFi-IfZw3nDnbh07eIY5a_-7_6TQFHDkak5wlSrYJx6w", + "e": "AQAB" + } + ] + }, + "lyft.com": { + "sequence_number": 12035489, + "keys": [ + { + "kty": "RSA", + "use": "x509-svid", + "x5c": [ + "MIID3TCCAsWgAwIBAgIUdmyO+EFUvRajCw+EOB+l8GkoygwwDQYJKoZIhvcNAQELBQAwdjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsMEEx5ZnQgRW5naW5lZXJpbmcxEDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjQxMDExMjIzMzA2WhcNMjYxMDExMjIzMzA2WjB2MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwETHlmdDEZMBcGA1UECwwQTHlmdCBFbmdpbmVlcmluZzEQMA4GA1UEAwwHVGVzdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKXhLaaPG1XmO+gIMILimAG4znTkbppPncqpHxs34mY+VtvAvy6wrScqUnSZylQoyrGtQuIMrCF3D7XhMjmYAH7gASCZninmsQqcvIKUP9yFEvRmLgEbR/dGjEB4aKIl230LZgKMl35I2mqvxvS8etx8BBOvDaS6sDeySDGq3UyDiVTFnSI0e2haC6ktp+zIJL7iqDhJpCVkNueBiDLfU2z/9F5jx2huaS2Y7tEndWGhJRd9zvqxf/Fw6CtruSou8TNXP+4y+Wtkdp7wWNJFYVoLoV2ugx+jOvPKLoy+JVa2hYviH2cN5w524dO3iGOWv/u/+k0BRw5GpOcJUq2CcesCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFA3ZY65POlo1Y6C9pujLEpqQOWOsMB8GA1UdIwQYMBaAFA3ZY65POlo1Y6C9pujLEpqQOWOsMA0GCSqGSIb3DQEBCwUAA4IBAQBcZXbRD0sAZPXXe/E5kOy0gN2iiux7JkBOdIXe7QVgBV1sCOhHHEWJ/Vawz5wK1/UXj7P3AqJKT3PbWy2vWNmoXxtD4ix8cIBPU3gxQ9HrhTgjof0mXIsujy+mUwm5Dmu/+xt1NaUXb9MvXUqlNTkdTyn3TMhQ7C0FzQYERTgwNPv2wJki+uq1Tt0G8XyW7QFCiUsdXPtEbYxv1dOwsGAYf/N426XuUNn6luqdHonMb0Xp7LpNM4pBH8Nh4G87itUmsRFUQ2s7QVff7FYOHYhEflC07DPmtOhOVXaGtut/2BFU/GbKQlLNcAvcVOpMA/Z9pobsffgGYBrZgmN3Ljnt" + ], + "n": "peEtpo8bVeY76AgwguKYAbjOdORumk-dyqkfGzfiZj5W28C_LrCtJypSdJnKVCjKsa1C4gysIXcPteEyOZgAfuABIJmeKeaxCpy8gpQ_3IUS9GYuARtH90aMQHhooiXbfQtmAoyXfkjaaq_G9Lx63HwEE68NpLqwN7JIMardTIOJVMWdIjR7aFoLqS2n7MgkvuKoOEmkJWQ254GIMt9TbP_0XmPHaG5pLZju0Sd1YaElF33O-rF_8XDoK2u5Ki7xM1c_7jL5a2R2nvBY0kVhWguhXa6DH6M688oujL4lVraFi-IfZw3nDnbh07eIY5a_-7_6TQFHDkak5wlSrYJx6w", + "e": "AQAB" + } + ] + } + } +} diff --git a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc index 806b64f54467..6e29fc5fdd18 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc +++ b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc @@ -39,13 +39,20 @@ using SSLContextPtr = CSmartPtr; class TestSPIFFEValidator : public testing::Test { public: - TestSPIFFEValidator() : stats_(generateSslStats(*store_.rootScope())) {} + TestSPIFFEValidator() + : stats_(generateSslStats(*store_.rootScope())) { + } + void initialize(std::string yaml, TimeSource& time_source) { envoy::config::core::v3::TypedExtensionConfig typed_conf; TestUtility::loadFromYaml(yaml, typed_conf); config_ = std::make_unique( typed_conf, allow_expired_certificate_, san_matchers_); + + // Mocking time source ON_CALL(factory_context_, timeSource()).WillByDefault(testing::ReturnRef(time_source)); + + // Initialize SPIFFEValidator with mocked context and stats validator_ = std::make_unique(config_.get(), stats_, factory_context_); } @@ -54,8 +61,18 @@ class TestSPIFFEValidator : public testing::Test { TestUtility::loadFromYaml(yaml, typed_conf); config_ = std::make_unique( typed_conf, allow_expired_certificate_, san_matchers_); + + EXPECT_CALL(factory_context_.dispatcher_, createFilesystemWatcher_()) + .WillRepeatedly(testing::Invoke([] { + Filesystem::MockWatcher* mock_watcher = new NiceMock(); + EXPECT_CALL(*mock_watcher, addWatch(TestEnvironment::substitute( + "{{ test_rundir }}/test/common/tls/test_data/trust_bundles.json"), _, _)) + .WillRepeatedly(testing::Return(absl::OkStatus())); + return mock_watcher; + })); + validator_ = std::make_unique(config_.get(), stats_, factory_context_); - }; + } void initialize() { validator_ = std::make_unique(stats_, factory_context_); } @@ -92,6 +109,8 @@ class TestSPIFFEValidator : public testing::Test { private: NiceMock factory_context_; + NiceMock dispatcher_; + //Envoy::Filesystem::MockWatcher* watcher_; bool allow_expired_certificate_{false}; TestCertificateValidationContextConfigPtr config_; std::vector san_matchers_{}; @@ -721,6 +740,200 @@ name: envoy.tls.cert_validator.spiffe validator().updateDigestForSessionId(md, hash_buffer, SHA256_DIGEST_LENGTH); } +TEST_F(TestSPIFFEValidator, TestDoVerifyCertChainMultipleTrustDomainBundleMapping) { + initialize(TestEnvironment::substitute(R"EOF( +name: envoy.tls.cert_validator.spiffe +typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig + trust_bundles: + filename: "{{ test_rundir }}/test/common/tls/test_data/trust_bundles.json" + )EOF")); + + X509StorePtr store = X509_STORE_new(); + SSLContextPtr ssl_ctx = SSL_CTX_new(TLS_method()); + TestSslExtendedSocketInfo info; + + // Set up expectations for onChanged to simulate file modification events + //EXPECT_CALL(mock_watcher, onChanged(Filesystem::Watcher::Events::Modified)) + // .Times(testing::AtLeast(1)) // Expect one or more calls + // .WillRepeatedly(testing::Return(absl::OkStatus())); + + { + // Trust domain matches so should be accepted. + auto cert = readCertFromFile(TestEnvironment::substitute( + "{{ test_rundir }}/test/common/tls/test_data/san_uri_cert.pem")); + bssl::UniquePtr cert_chain(sk_X509_new_null()); + sk_X509_push(cert_chain.get(), cert.release()); + EXPECT_EQ(ValidationResults::ValidationStatus::Successful, + validator() + .doVerifyCertChain(*cert_chain, info.createValidateResultCallback(), + /*transport_socket_options=*/nullptr, *ssl_ctx, {}, false, "") + .status); + } + + { + auto cert = readCertFromFile(TestEnvironment::substitute( + "{{ test_rundir }}/test/common/tls/test_data/spiffe_san_cert.pem")); + bssl::UniquePtr cert_chain(sk_X509_new_null()); + sk_X509_push(cert_chain.get(), cert.release()); + EXPECT_EQ(ValidationResults::ValidationStatus::Successful, + validator() + .doVerifyCertChain(*cert_chain, info.createValidateResultCallback(), + /*transport_socket_options=*/nullptr, *ssl_ctx, {}, false, "") + .status); + } + + { + // Trust domain matches but it has expired. + auto cert = readCertFromFile( + TestEnvironment::substitute("{{ test_rundir " + "}}/test/common/tls/test_data/expired_spiffe_san_cert.pem")); + bssl::UniquePtr cert_chain(sk_X509_new_null()); + sk_X509_push(cert_chain.get(), cert.release()); + EXPECT_EQ(ValidationResults::ValidationStatus::Failed, + validator() + .doVerifyCertChain(*cert_chain, info.createValidateResultCallback(), + /*transport_socket_options=*/nullptr, *ssl_ctx, {}, false, "") + .status); + } + + { + // Does not have san. + auto cert = readCertFromFile(TestEnvironment::substitute( + "{{ test_rundir }}/test/common/tls/test_data/extensions_cert.pem")); + bssl::UniquePtr cert_chain(sk_X509_new_null()); + sk_X509_push(cert_chain.get(), cert.release()); + EXPECT_EQ(ValidationResults::ValidationStatus::Failed, + validator() + .doVerifyCertChain(*cert_chain, info.createValidateResultCallback(), + /*transport_socket_options=*/nullptr, *ssl_ctx, {}, false, "") + .status); + } + + EXPECT_EQ(2, stats().fail_verify_error_.value()); +} + +TEST_F(TestSPIFFEValidator, TestDoVerifyCertChainIntermediateCertsBundleMapping) { + initialize(TestEnvironment::substitute(R"EOF( +name: envoy.tls.cert_validator.spiffe +typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig + trust_bundles: + filename: "{{ test_rundir }}/test/common/tls/test_data/trust_bundles.json" + )EOF")); + + TestSslExtendedSocketInfo info; + // Chain contains workload, intermediate, and ca cert, so it should be accepted. + auto cert = + readCertFromFile(TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/" + "spiffe_san_signed_by_intermediate_cert.pem")); + auto intermediate_ca_cert = + readCertFromFile(TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/" + "intermediate_ca_cert.pem")); + + SSLContextPtr ssl_ctx = SSL_CTX_new(TLS_method()); + bssl::UniquePtr cert_chain(sk_X509_new_null()); + sk_X509_push(cert_chain.get(), cert.release()); + sk_X509_push(cert_chain.get(), intermediate_ca_cert.release()); + EXPECT_EQ(ValidationResults::ValidationStatus::Successful, + validator() + .doVerifyCertChain(*cert_chain, info.createValidateResultCallback(), + /*transport_socket_options=*/nullptr, *ssl_ctx, {}, false, "") + .status); +} + +TEST_F(TestSPIFFEValidator, TestDoVerifyCertChainSANMatchingTrustBundles) { + const auto config = TestEnvironment::substitute(R"EOF( +name: envoy.tls.cert_validator.spiffe +typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig + trust_bundles: + filename: "{{ test_rundir }}/test/common/tls/test_data/trust_bundles.json" + )EOF"); + + X509StorePtr store = X509_STORE_new(); + SSLContextPtr ssl_ctx = SSL_CTX_new(TLS_method()); + // URI SAN = spiffe://lyft.com/test-team + auto cert = readCertFromFile( + TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_uri_cert.pem")); + X509StoreContextPtr store_ctx = X509_STORE_CTX_new(); + EXPECT_TRUE(X509_STORE_CTX_init(store_ctx.get(), store.get(), cert.get(), nullptr)); + bssl::UniquePtr cert_chain(sk_X509_new_null()); + sk_X509_push(cert_chain.get(), cert.release()); + TestSslExtendedSocketInfo info; + info.setCertificateValidationStatus(Envoy::Ssl::ClientValidationStatus::NotValidated); + { + envoy::type::matcher::v3::StringMatcher matcher; + matcher.set_prefix("spiffe://lyft.com/"); + setSanMatchers({matcher}); + initialize(config); + ValidationResults results = validator().doVerifyCertChain( + *cert_chain, info.createValidateResultCallback(), + /*transport_socket_options=*/nullptr, *ssl_ctx, {}, false, ""); + EXPECT_EQ(ValidationResults::ValidationStatus::Successful, results.status); + EXPECT_EQ(Envoy::Ssl::ClientValidationStatus::Validated, results.detailed_status); + } + { + envoy::type::matcher::v3::StringMatcher matcher; + matcher.set_prefix("spiffe://example.com/"); + setSanMatchers({matcher}); + initialize(config); + ValidationResults results = validator().doVerifyCertChain( + *cert_chain, info.createValidateResultCallback(), + /*transport_socket_options=*/nullptr, *ssl_ctx, {}, false, ""); + EXPECT_EQ(ValidationResults::ValidationStatus::Failed, results.status); + EXPECT_EQ(Envoy::Ssl::ClientValidationStatus::Failed, results.detailed_status); + EXPECT_EQ(1, stats().fail_verify_san_.value()); + stats().fail_verify_san_.reset(); + } +} + +TEST_F(TestSPIFFEValidator, TestDoVerifyCertChainSANMatchingTrustBundlesMapping) { + const auto config = TestEnvironment::substitute(R"EOF( +name: envoy.tls.cert_validator.spiffe +typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig + trust_bundles: + filename: "{{ test_rundir }}/test/common/tls/test_data/trust_bundles.json" + )EOF"); + + X509StorePtr store = X509_STORE_new(); + SSLContextPtr ssl_ctx = SSL_CTX_new(TLS_method()); + // URI SAN = spiffe://lyft.com/test-team + auto cert = readCertFromFile( + TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_uri_cert.pem")); + X509StoreContextPtr store_ctx = X509_STORE_CTX_new(); + EXPECT_TRUE(X509_STORE_CTX_init(store_ctx.get(), store.get(), cert.get(), nullptr)); + bssl::UniquePtr cert_chain(sk_X509_new_null()); + sk_X509_push(cert_chain.get(), cert.release()); + TestSslExtendedSocketInfo info; + info.setCertificateValidationStatus(Envoy::Ssl::ClientValidationStatus::NotValidated); + { + envoy::type::matcher::v3::StringMatcher matcher; + matcher.set_prefix("spiffe://lyft.com/"); + setSanMatchers({matcher}); + initialize(config); + ValidationResults results = validator().doVerifyCertChain( + *cert_chain, info.createValidateResultCallback(), + /*transport_socket_options=*/nullptr, *ssl_ctx, {}, false, ""); + EXPECT_EQ(ValidationResults::ValidationStatus::Successful, results.status); + EXPECT_EQ(Envoy::Ssl::ClientValidationStatus::Validated, results.detailed_status); + } + { + envoy::type::matcher::v3::StringMatcher matcher; + matcher.set_prefix("spiffe://example.com/"); + setSanMatchers({matcher}); + initialize(config); + ValidationResults results = validator().doVerifyCertChain( + *cert_chain, info.createValidateResultCallback(), + /*transport_socket_options=*/nullptr, *ssl_ctx, {}, false, ""); + EXPECT_EQ(ValidationResults::ValidationStatus::Failed, results.status); + EXPECT_EQ(Envoy::Ssl::ClientValidationStatus::Failed, results.detailed_status); + EXPECT_EQ(1, stats().fail_verify_san_.value()); + stats().fail_verify_san_.reset(); + } +} + } // namespace Tls } // namespace TransportSockets } // namespace Extensions