Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
69 changes: 69 additions & 0 deletions source/extensions/transport_sockets/tls/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,60 @@ 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;
}
size_t i = 0, p = 0, i_star = dns_label.size(), p_star = 0;
while (i < dns_label.size()) {
if (p < pattern.size() &&
absl::ascii_tolower(dns_label[i]) == absl::ascii_tolower(pattern[p])) {
Comment thread
suniltheta marked this conversation as resolved.
Outdated
++i;
++p;
} else if (p < pattern.size() && pattern[p] == glob) {
i_star = i;
p_star = p++;
} else if (i_star != dns_label.size()) {
i = ++i_star;
p = p_star + 1;
} else {
return false;
}
}

while (p < pattern.size() && pattern[p] == glob) {
++p;
}

return p == pattern.size() && i == dns_label.size();
}

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;
}

// https://www.rfc-editor.org/rfc/rfc6125#section-6.4.3 part #2
// If the wildcard character is the only character of the left-most
// label in the presented identifier, the client SHOULD NOT compare
// against anything but the left-most label of the reference identifier
// (e.g., *.example.com would match foo.example.com but
// not bar.foo.example.com or example.com)
size_t pattern_len = lower_case_pattern.length();
Comment thread
suniltheta marked this conversation as resolved.
Outdated
if (pattern_len > 1 && lower_case_pattern[0] == '*' && lower_case_pattern[1] == '.') {
if (lower_case_dns_name.length() > pattern_len - 1) {
Expand All @@ -88,6 +135,28 @@ bool Utility::dnsNameMatch(absl::string_view dns_name, absl::string_view pattern
}
}

// https://www.rfc-editor.org/rfc/rfc6125#section-6.4.3 part #3
// Match a presented identifier in which the wildcard character is not the only
// character of the label and don't match patter if the wildcard character is
// embedded within an A-label (A-label always starts with the ACE prefix "xn--")
// (e.g., baz*.example.net and *baz.example.net and b*z.example.net would
// be taken to match baz1.example.net and foobaz.example.net and
// buzz.example.net, respectively)
size_t pattern_left_label_len = lower_case_pattern.find('.');
size_t dns_name_left_label_len = lower_case_dns_name.find('.');
// Only the left-most label in the pattern contains wildcard '*' and is not an A-label
if ((pattern_left_label_len != std::string::npos) &&
Comment thread
suniltheta marked this conversation as resolved.
Outdated
(dns_name_left_label_len != std::string::npos) &&
(lower_case_pattern.find('*') != std::string::npos) &&
(lower_case_pattern.find('*') < pattern_left_label_len) &&
(lower_case_pattern.substr(pattern_left_label_len).find('*') == std::string::npos) &&
(!absl::StartsWith(lower_case_pattern.substr(0, pattern_left_label_len), ACE_prefix))) {
return labelWildcardMatch(lower_case_dns_name.substr(0, dns_name_left_label_len),
lower_case_pattern.substr(0, pattern_left_label_len)) &&
(lower_case_dns_name.substr(dns_name_left_label_len) ==
lower_case_pattern.substr(pattern_left_label_len));
}

return false;
}

Expand Down
15 changes: 13 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,24 @@ 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.
* @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
* @param pattern the pattern to match against
* @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
19 changes: 17 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,27 @@ 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("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