Require on-the-wire SNI to pass IsValidSNI check
This requirement existed in QUIC Crypto; it should exist when we run QUIC
with TLS.
Restrict sni in ietf quic draft versions. protected by reloadable flag quic_tls_enforce_valid_sni.
PiperOrigin-RevId: 310054163
Change-Id: I9ffdea55c350e9c1592a71debb3fbb271eca7750
diff --git a/quic/core/tls_client_handshaker.cc b/quic/core/tls_client_handshaker.cc
index 324c378..09ab71e 100644
--- a/quic/core/tls_client_handshaker.cc
+++ b/quic/core/tls_client_handshaker.cc
@@ -12,6 +12,7 @@
#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
#include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h"
#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.h"
#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
#include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h"
@@ -87,7 +88,14 @@
// Set the SNI to send, if any.
SSL_set_connect_state(ssl());
+ if (QUIC_DLOG_INFO_IS_ON() &&
+ !QuicHostnameUtils::IsValidSNI(server_id_.host())) {
+ QUIC_DLOG(INFO) << "Client configured with invalid hostname \""
+ << server_id_.host() << "\", not sending as SNI";
+ }
if (!server_id_.host().empty() &&
+ (QuicHostnameUtils::IsValidSNI(server_id_.host()) ||
+ allow_invalid_sni_for_tests_) &&
SSL_set_tlsext_host_name(ssl(), server_id_.host().c_str()) != 1) {
return false;
}
diff --git a/quic/core/tls_client_handshaker.h b/quic/core/tls_client_handshaker.h
index fdd68c2..cc314b7 100644
--- a/quic/core/tls_client_handshaker.h
+++ b/quic/core/tls_client_handshaker.h
@@ -75,6 +75,7 @@
std::unique_ptr<ApplicationState> application_state) override;
void AllowEmptyAlpnForTests() { allow_empty_alpn_for_tests_ = true; }
+ void AllowInvalidSNIForTests() { allow_invalid_sni_for_tests_ = true; }
protected:
const TlsConnection* tls_connection() const override {
@@ -169,6 +170,7 @@
crypto_negotiated_params_;
bool allow_empty_alpn_for_tests_ = false;
+ bool allow_invalid_sni_for_tests_ = false;
const bool has_application_state_;
diff --git a/quic/core/tls_client_handshaker_test.cc b/quic/core/tls_client_handshaker_test.cc
index fe757b4..2629bd4 100644
--- a/quic/core/tls_client_handshaker_test.cc
+++ b/quic/core/tls_client_handshaker_test.cc
@@ -392,6 +392,26 @@
EXPECT_TRUE(server_stream()->encryption_established());
}
+TEST_P(TlsClientHandshakerTest, InvalidSNI) {
+ // Test that a client will skip sending SNI if configured to send an invalid
+ // hostname. In this case, the inclusion of '!' is invalid.
+ server_id_ = QuicServerId("invalid!.example.com", 443);
+ crypto_config_.reset(new QuicCryptoClientConfig(
+ std::make_unique<FakeProofVerifier>(), nullptr));
+ CreateConnection();
+ InitializeFakeServer();
+
+ stream()->CryptoConnect();
+ crypto_test_utils::CommunicateHandshakeMessages(
+ connection_, stream(), server_connection_, server_stream());
+
+ EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol());
+ EXPECT_TRUE(stream()->encryption_established());
+ EXPECT_TRUE(stream()->one_rtt_keys_available());
+
+ EXPECT_EQ(server_stream()->crypto_negotiated_params().sni, "");
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/core/tls_server_handshaker.cc b/quic/core/tls_server_handshaker.cc
index 890abae..54a066f 100644
--- a/quic/core/tls_server_handshaker.cc
+++ b/quic/core/tls_server_handshaker.cc
@@ -484,6 +484,15 @@
hostname_ = hostname;
crypto_negotiated_params_->sni =
QuicHostnameUtils::NormalizeHostname(hostname_);
+ if (GetQuicReloadableFlag(quic_tls_enforce_valid_sni)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_tls_enforce_valid_sni);
+ if (!QuicHostnameUtils::IsValidSNI(hostname_)) {
+ // TODO(b/151676147): Include this error string in the CONNECTION_CLOSE
+ // frame.
+ QUIC_LOG(ERROR) << "Invalid SNI provided: \"" << hostname_ << "\"";
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ }
} else {
QUIC_LOG(INFO) << "No hostname indicated in SNI";
}
diff --git a/quic/core/tls_server_handshaker_test.cc b/quic/core/tls_server_handshaker_test.cc
index 28f13c6..89fb325 100644
--- a/quic/core/tls_server_handshaker_test.cc
+++ b/quic/core/tls_server_handshaker_test.cc
@@ -356,6 +356,20 @@
ExpectHandshakeSuccessful();
}
+TEST_F(TlsServerHandshakerTest, RejectInvalidSNI) {
+ SetQuicReloadableFlag(quic_tls_enforce_valid_sni, true);
+ server_id_ = QuicServerId("invalid!.example.com", kServerPort, false);
+ InitializeFakeClient();
+ static_cast<TlsClientHandshaker*>(
+ QuicCryptoClientStreamPeer::GetHandshaker(client_stream()))
+ ->AllowInvalidSNIForTests();
+
+ // Run the handshake and expect it to fail.
+ AdvanceHandshakeWithFakeClient();
+ EXPECT_FALSE(server_stream()->encryption_established());
+ EXPECT_FALSE(server_stream()->one_rtt_keys_available());
+}
+
TEST_F(TlsServerHandshakerTest, Resumption) {
// Do the first handshake
InitializeFakeClient();