Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Bug Fixes

* data plane: fixing error handling where writing to a socket failed while under the stack of processing. This should genreally affect HTTP/3. This behavioral change can be reverted by setting ``envoy.reloadable_features.allow_upstream_inline_write`` to false.
* eds: fix the eds cluster update by allowing update on the locality of the cluster endpoints. This behavioral change can be temporarily reverted by setting runtime guard ``envoy.reloadable_features.support_locality_update_on_eds_cluster_endpoints`` to false.
* tls: fix a bug while matching a certificate SAN with an exact value in ``match_typed_subject_alt_names`` of a listener where wildcard ``*`` character is not the only character of the dns label. Example, ``baz*.example.net`` and ``*baz.example.net`` and ``b*z.example.net`` will match ``baz1.example.net`` and ``foobaz.example.net`` and ``buzz.example.net``, respectively.
* xray: fix the AWS X-Ray tracer extension to not sample the trace if ``sampled=`` keyword is not present in the header ``x-amzn-trace-id``.

Removed Config or Runtime
Expand Down
41 changes: 33 additions & 8 deletions source/extensions/transport_sockets/tls/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,46 @@ Envoy::Ssl::CertificateDetailsPtr Utility::certificateDetails(X509* cert, const
return certificate_details;
}

bool Utility::labelWildcardMatch(absl::string_view dns_label, absl::string_view pattern) {
constexpr char glob = '*';
// Check the special case of a single * pattern, as it's common.
if (pattern.size() == 1 && pattern[0] == glob) {
return true;
}
// Only valid if wildcard character appear once.
if (std::count(pattern.begin(), pattern.end(), glob) == 1) {
std::vector<absl::string_view> split_pattern = absl::StrSplit(pattern, glob);
return (pattern.size() <= dns_label.size() + 1) &&
absl::StartsWith(dns_label, split_pattern[0]) &&
absl::EndsWith(dns_label, split_pattern[1]);
}
return false;
}

bool Utility::dnsNameMatch(absl::string_view dns_name, absl::string_view pattern) {
// A-label ACE prefix https://www.rfc-editor.org/rfc/rfc5890#section-2.3.2.5.
constexpr absl::string_view ACE_prefix = "xn--";
const std::string lower_case_dns_name = absl::AsciiStrToLower(dns_name);
const std::string lower_case_pattern = absl::AsciiStrToLower(pattern);
if (lower_case_dns_name == lower_case_pattern) {
return true;
}

size_t pattern_len = lower_case_pattern.length();
if (pattern_len > 1 && lower_case_pattern[0] == '*' && lower_case_pattern[1] == '.') {
if (lower_case_dns_name.length() > pattern_len - 1) {
const size_t off = lower_case_dns_name.length() - pattern_len + 1;
return lower_case_dns_name.substr(0, off).find('.') == std::string::npos &&
lower_case_dns_name.substr(off, pattern_len - 1) ==
lower_case_pattern.substr(1, pattern_len - 1);
}
std::vector<absl::string_view> split_pattern =
absl::StrSplit(lower_case_pattern, absl::MaxSplits('.', 1));
std::vector<absl::string_view> split_dns_name =
absl::StrSplit(lower_case_dns_name, absl::MaxSplits('.', 1));

// dns name and pattern should contain more than 1 label to match.
if (split_pattern.size() < 2 || split_dns_name.size() < 2) {
return false;
}
// Only the left-most label in the pattern contains wildcard '*' and is not an A-label.
if ((split_pattern[0].find('*') != absl::string_view::npos) &&
(split_pattern[1].find('*') == absl::string_view::npos) &&
(!absl::StartsWith(split_pattern[0], ACE_prefix))) {
return (split_dns_name[1] == split_pattern[1]) &&
labelWildcardMatch(split_dns_name[0], split_pattern[0]);
}

return false;
Expand Down
16 changes: 14 additions & 2 deletions source/extensions/transport_sockets/tls/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,25 @@ Envoy::Ssl::CertificateDetailsPtr certificateDetails(X509* cert, const std::stri
TimeSource& time_source);

/**
* Determines whether the given name matches 'pattern' which may optionally begin with a wildcard.
* Determines whether the given name matches 'pattern' which may optionally begin with a wildcard
* or contain a wildcard inside the pattern's first label.
* See: https://www.rfc-editor.org/rfc/rfc6125#section-6.4.3.
* @param dns_name the DNS name to match
* @param pattern the pattern to match against (*.example.com)
* @param pattern the pattern to match against (*.example.com) or (test*.example.com)
* @return true if the san matches pattern
*/
bool dnsNameMatch(absl::string_view dns_name, absl::string_view pattern);

/**
* Determines whether the given DNS label matches 'pattern' which may contain a wildcard. e.g.,
* patterns "baz*" and "*baz" and "b*z" would match DNS labels "baz1" and "foobaz" and "buzz",
* respectively.
* @param dns_label the DNS name label to match in lower case
* @param pattern the pattern to match against in lower case
* @return true if the dns_label matches pattern
*/
bool labelWildcardMatch(absl::string_view dns_label, absl::string_view pattern);

/**
* Retrieves the serial number of a certificate.
* @param cert the certificate
Expand Down
22 changes: 20 additions & 2 deletions test/extensions/transport_sockets/tls/utility_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,30 @@ TEST(UtilityTest, TestDnsNameMatching) {
EXPECT_TRUE(Utility::dnsNameMatch("lyft.com", "lyft.com"));
EXPECT_TRUE(Utility::dnsNameMatch("a.lyft.com", "*.lyft.com"));
EXPECT_TRUE(Utility::dnsNameMatch("a.LYFT.com", "*.lyft.COM"));
EXPECT_TRUE(Utility::dnsNameMatch("lyft.com", "*yft.com"));
EXPECT_TRUE(Utility::dnsNameMatch("LYFT.com", "*yft.com"));
EXPECT_TRUE(Utility::dnsNameMatch("lyft.com", "*lyft.com"));
EXPECT_TRUE(Utility::dnsNameMatch("lyft.com", "lyf*.com"));
EXPECT_TRUE(Utility::dnsNameMatch("lyft.com", "lyft*.com"));
EXPECT_TRUE(Utility::dnsNameMatch("lyft.com", "l*ft.com"));
EXPECT_TRUE(Utility::dnsNameMatch("t.lyft.com", "t*.lyft.com"));
EXPECT_TRUE(Utility::dnsNameMatch("test.lyft.com", "t*.lyft.com"));
EXPECT_TRUE(Utility::dnsNameMatch("l-lots-of-stuff-ft.com", "l*ft.com"));
EXPECT_FALSE(Utility::dnsNameMatch("t.lyft.com", "t*t.lyft.com"));
EXPECT_FALSE(Utility::dnsNameMatch("lyft.com", "l*ft.co"));
EXPECT_FALSE(Utility::dnsNameMatch("lyft.com", "ly?t.com"));
EXPECT_FALSE(Utility::dnsNameMatch("lyft.com", "lf*t.com"));
EXPECT_FALSE(Utility::dnsNameMatch(".lyft.com", "*lyft.com"));
EXPECT_FALSE(Utility::dnsNameMatch("lyft.com", "**lyft.com"));
EXPECT_FALSE(Utility::dnsNameMatch("lyft.com", "lyft**.com"));
EXPECT_FALSE(Utility::dnsNameMatch("lyft.com", "ly**ft.com"));
EXPECT_FALSE(Utility::dnsNameMatch("lyft.com", "lyft.c*m"));
EXPECT_FALSE(Utility::dnsNameMatch("lyft.com", "*yft.c*m"));
EXPECT_FALSE(Utility::dnsNameMatch("test.lyft.com.extra", "*.lyft.com"));
EXPECT_FALSE(Utility::dnsNameMatch("a.b.lyft.com", "*.lyft.com"));
EXPECT_FALSE(Utility::dnsNameMatch("foo.test.com", "*.lyft.com"));
EXPECT_FALSE(Utility::dnsNameMatch("lyft.com", "*.lyft.com"));
EXPECT_FALSE(Utility::dnsNameMatch("alyft.com", "*.lyft.com"));
EXPECT_FALSE(Utility::dnsNameMatch("alyft.com", "*lyft.com"));
EXPECT_FALSE(Utility::dnsNameMatch("lyft.com", "*lyft.com"));
EXPECT_FALSE(Utility::dnsNameMatch("", "*lyft.com"));
EXPECT_FALSE(Utility::dnsNameMatch("lyft.com", ""));
}
Expand Down