Skip to content

Commit

Permalink
tls: improve wildcard matching (#11921) (#11927)
Browse files Browse the repository at this point in the history
Patching in 11885 with runtime guards and release notes

Risk Level: Medium (changes to cert matching)
Testing: new unit test
Docs Changes: n/a
Release Notes: inline
Runtime guard: envoy.reloadable_features.fix_wildcard_matching

Signed-off-by: Yann Soubeyrand <yann.soubeyrand@camptocamp.com>
Signed-off-by: Alyssa Wilk <alyssar@chromium.org>
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
  • Loading branch information
PiotrSikora authored Jul 7, 2020
1 parent a59ef68 commit b2ffdd8
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 3 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.12.5
1.12.6
4 changes: 4 additions & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Version history
---------------

1.12.6 (July 7, 2020)
=====================
* tls: fixed a bug where wilcard matching for "\*.foo.com" also matched domains of the form "a.b.foo.com". This behavior can be temporarily reverted by setting runtime feature `envoy.reloadable_features.fix_wildcard_matching` to false.

1.12.5 (June 30, 2020)
======================
* buffer: fixed CVE-2020-12603 by avoiding fragmentation, and tracking of HTTP/2 data and control frames in the output buffer.
Expand Down
1 change: 1 addition & 0 deletions source/common/runtime/runtime_features.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ constexpr const char* runtime_features[] = {
"envoy.reloadable_features.outlier_detection_support_for_grpc_status",
"envoy.reloadable_features.strict_header_validation",
"envoy.reloadable_features.strict_authority_validation",
"envoy.reloadable_features.fix_wildcard_matching",
};

// This is a list of configuration fields which are disallowed by default in Envoy
Expand Down
1 change: 1 addition & 0 deletions source/extensions/transport_sockets/tls/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ envoy_cc_library(
"//source/common/common:utility_lib",
"//source/common/network:address_lib",
"//source/common/protobuf:utility_lib",
"//source/common/runtime:runtime_lib",
"//source/common/stats:symbol_table_lib",
"//source/extensions/transport_sockets/tls/private_key:private_key_manager_lib",
"@envoy_api//envoy/admin/v2alpha:pkg_cc_proto",
Expand Down
10 changes: 8 additions & 2 deletions source/extensions/transport_sockets/tls/context_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "common/common/utility.h"
#include "common/network/address_impl.h"
#include "common/protobuf/utility.h"
#include "common/runtime/runtime_impl.h"

#include "extensions/transport_sockets/tls/utility.h"

Expand Down Expand Up @@ -635,8 +636,13 @@ bool ContextImpl::dnsNameMatch(const std::string& dns_name, const char* pattern)
size_t pattern_len = strlen(pattern);
if (pattern_len > 1 && pattern[0] == '*' && pattern[1] == '.') {
if (dns_name.length() > pattern_len - 1) {
size_t off = dns_name.length() - pattern_len + 1;
return dns_name.compare(off, pattern_len - 1, pattern + 1) == 0;
const size_t off = dns_name.length() - pattern_len + 1;
if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.fix_wildcard_matching")) {
return dns_name.substr(0, off).find('.') == std::string::npos &&
dns_name.compare(off, pattern_len - 1, pattern + 1) == 0;
} else {
return dns_name.compare(off, pattern_len - 1, pattern + 1) == 0;
}
}
}

Expand Down
1 change: 1 addition & 0 deletions test/extensions/transport_sockets/tls/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ envoy_cc_test(
"//test/mocks/ssl:ssl_mocks",
"//test/test_common:environment_lib",
"//test/test_common:simulated_time_system_lib",
"//test/test_common:test_runtime_lib",
],
)

Expand Down
37 changes: 37 additions & 0 deletions test/extensions/transport_sockets/tls/context_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "test/mocks/ssl/mocks.h"
#include "test/test_common/environment.h"
#include "test/test_common/simulated_time_system.h"
#include "test/test_common/test_runtime.h"
#include "test/test_common/utility.h"

#include "gtest/gtest.h"
Expand All @@ -44,6 +45,23 @@ class SslContextImplTest : public SslCertsTest {
TEST_F(SslContextImplTest, TestDnsNameMatching) {
EXPECT_TRUE(ContextImpl::dnsNameMatch("lyft.com", "lyft.com"));
EXPECT_TRUE(ContextImpl::dnsNameMatch("a.lyft.com", "*.lyft.com"));
EXPECT_FALSE(ContextImpl::dnsNameMatch("a.b.lyft.com", "*.lyft.com"));
EXPECT_FALSE(ContextImpl::dnsNameMatch("foo.test.com", "*.lyft.com"));
EXPECT_FALSE(ContextImpl::dnsNameMatch("lyft.com", "*.lyft.com"));
EXPECT_FALSE(ContextImpl::dnsNameMatch("alyft.com", "*.lyft.com"));
EXPECT_FALSE(ContextImpl::dnsNameMatch("alyft.com", "*lyft.com"));
EXPECT_FALSE(ContextImpl::dnsNameMatch("lyft.com", "*lyft.com"));
EXPECT_FALSE(ContextImpl::dnsNameMatch("", "*lyft.com"));
EXPECT_FALSE(ContextImpl::dnsNameMatch("lyft.com", ""));
}

TEST_F(SslContextImplTest, TestDnsNameMatchingLegacy) {
TestScopedRuntime scoped_runtime;
Runtime::LoaderSingleton::getExisting()->mergeValues(
{{"envoy.reloadable_features.fix_wildcard_matching", "false"}});
EXPECT_TRUE(ContextImpl::dnsNameMatch("lyft.com", "lyft.com"));
EXPECT_TRUE(ContextImpl::dnsNameMatch("a.lyft.com", "*.lyft.com"));
// Legacy behavior
EXPECT_TRUE(ContextImpl::dnsNameMatch("a.b.lyft.com", "*.lyft.com"));
EXPECT_FALSE(ContextImpl::dnsNameMatch("foo.test.com", "*.lyft.com"));
EXPECT_FALSE(ContextImpl::dnsNameMatch("lyft.com", "*.lyft.com"));
Expand All @@ -70,6 +88,25 @@ TEST_F(SslContextImplTest, TestVerifySubjectAltNameURIMatched) {
EXPECT_TRUE(ContextImpl::verifySubjectAltName(cert.get(), verify_subject_alt_name_list));
}

TEST_F(SslContextImplTest, TestVerifySubjectAltMultiDomain) {
bssl::UniquePtr<X509> cert = readCertFromFile(TestEnvironment::substitute(
"{{ test_rundir "
"}}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_cert.pem"));
std::vector<std::string> verify_subject_alt_name_list = {"https://a.www.example.com"};
EXPECT_FALSE(ContextImpl::verifySubjectAltName(cert.get(), verify_subject_alt_name_list));
}

TEST_F(SslContextImplTest, TestVerifySubjectAltMultiDomainLegacy) {
TestScopedRuntime scoped_runtime;
Runtime::LoaderSingleton::getExisting()->mergeValues(
{{"envoy.reloadable_features.fix_wildcard_matching", "false"}});
bssl::UniquePtr<X509> cert = readCertFromFile(TestEnvironment::substitute(
"{{ test_rundir "
"}}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_cert.pem"));
std::vector<std::string> verify_subject_alt_name_list = {"https://a.www.example.com"};
EXPECT_TRUE(ContextImpl::verifySubjectAltName(cert.get(), verify_subject_alt_name_list));
}

TEST_F(SslContextImplTest, TestVerifySubjectAltNameNotMatched) {
bssl::UniquePtr<X509> cert = readCertFromFile(TestEnvironment::substitute(
"{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem"));
Expand Down

0 comments on commit b2ffdd8

Please sign in to comment.