Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
44 changes: 36 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,49 @@ Envoy::Ssl::CertificateDetailsPtr Utility::certificateDetails(X509* cert, const
return certificate_details;
}

bool Utility::labelWildcardMatch(absl::string_view dns_label, absl::string_view pattern) {
if (pattern.empty()) {
Comment thread
suniltheta marked this conversation as resolved.
Outdated
return dns_label.empty();
}
// Only valid if wildcard character appear once
Comment thread
suniltheta marked this conversation as resolved.
Outdated
if (std::count(pattern.begin(), pattern.end(), '*') == 1) {
constexpr char glob = '*';
Comment thread
suniltheta marked this conversation as resolved.
Outdated
// Check the special case of a single * pattern, as it's common.
if (pattern.size() == 1 && pattern[0] == glob) {
Comment thread
suniltheta marked this conversation as resolved.
Outdated
return true;
}
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('*') != std::string::npos) &&
Comment thread
suniltheta marked this conversation as resolved.
Outdated
(split_pattern[1].find('*') == std::string::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.
* 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
20 changes: 18 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,28 @@ 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", "*.lyft.com"));
Comment thread
suniltheta marked this conversation as resolved.
Outdated
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"));
Comment thread
suniltheta marked this conversation as resolved.
EXPECT_TRUE(Utility::dnsNameMatch("test.lyft.com", "t*.lyft.com"));
Comment thread
suniltheta marked this conversation as resolved.
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"));
Comment thread
suniltheta marked this conversation as resolved.
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