Automated g4 rollback of changelist 455516399. *** Reason for rollback *** Send a connection close in QuicDispatcher if a fatal TLS alert is raised while extracting CHLO. The only known such case is that when the SNI hostname contains a null byte, an 'unrecognized name' alert will be raised. This is the prerequisite for the rollback. *** Original change description *** Automated g4 rollback of changelist 455484069. *** Reason for rollback *** This is breaking GFE TAP builds. b/236320376 *** Original change description *** Fuzz SNI and ALPN in quic_tls_server_fuzz_test. *** *** Protected by FLAGS_quic_restart_flag_quic_dispatcher_send_connection_close_for_tls_alerts. PiperOrigin-RevId: 457560550
diff --git a/quiche/quic/core/quic_dispatcher.cc b/quiche/quic/core/quic_dispatcher.cc index 81226f6..7f5b1ba 100644 --- a/quiche/quic/core/quic_dispatcher.cc +++ b/quiche/quic/core/quic_dispatcher.cc
@@ -716,37 +716,61 @@ // Packet's connection ID is unknown. Apply the validity checks. QuicPacketFate fate = ValidityChecks(*packet_info); + // |connection_close_error_code| is used if the final packet fate is + // kFateTimeWait. + QuicErrorCode connection_close_error_code = QUIC_HANDSHAKE_FAILED; + + // If a fatal TLS alert was received when extracting Client Hello, + // |tls_alert_error_detail| will be set and will be used as the error_details + // of the connection close. + std::string tls_alert_error_detail; + if (fate == kFateProcess) { ExtractChloResult extract_chlo_result = TryExtractChloOrBufferEarlyPacket(*packet_info); auto& parsed_chlo = extract_chlo_result.parsed_chlo; - if (!parsed_chlo.has_value()) { + + if (send_connection_close_for_tls_alerts_ && + extract_chlo_result.tls_alert.has_value()) { + QUIC_BUG_IF(quic_dispatcher_parsed_chlo_and_tls_alert_coexist_1, + parsed_chlo.has_value()) + << "parsed_chlo and tls_alert should not be set at the same time."; + // Fatal TLS alert when parsing Client Hello. + fate = kFateTimeWait; + uint8_t tls_alert = *extract_chlo_result.tls_alert; + connection_close_error_code = TlsAlertToQuicErrorCode(tls_alert); + tls_alert_error_detail = + absl::StrCat("TLS handshake failure (", + EncryptionLevelToString(ENCRYPTION_INITIAL), ") ", + static_cast<int>(tls_alert), ": ", + SSL_alert_desc_string_long(tls_alert)); + } else if (!parsed_chlo.has_value()) { // Client Hello incomplete. Packet has been buffered or (rarely) dropped. return; - } + } else { + // Client Hello fully received. + fate = ValidityChecksOnFullChlo(*packet_info, *parsed_chlo); - // Client Hello fully received. - fate = ValidityChecksOnFullChlo(*packet_info, *parsed_chlo); + if (fate == kFateProcess) { + QUICHE_DCHECK( + parsed_chlo->legacy_version_encapsulation_inner_packet.empty() || + !packet_info->version.UsesTls()); + if (GetQuicRestartFlag(quic_disable_legacy_version_encapsulation)) { + if (!parsed_chlo->legacy_version_encapsulation_inner_packet.empty()) { + QUIC_CODE_COUNT( + quic_disable_legacy_version_encapsulation_process_header); + } + } else { + if (MaybeHandleLegacyVersionEncapsulation( + this, parsed_chlo->legacy_version_encapsulation_inner_packet, + *packet_info)) { + return; + } + } - if (fate == kFateProcess) { - QUICHE_DCHECK( - parsed_chlo->legacy_version_encapsulation_inner_packet.empty() || - !packet_info->version.UsesTls()); - if (GetQuicRestartFlag(quic_disable_legacy_version_encapsulation)) { - if (!parsed_chlo->legacy_version_encapsulation_inner_packet.empty()) { - QUIC_CODE_COUNT( - quic_disable_legacy_version_encapsulation_process_header); - } - } else { - if (MaybeHandleLegacyVersionEncapsulation( - this, parsed_chlo->legacy_version_encapsulation_inner_packet, - *packet_info)) { - return; - } + ProcessChlo(*std::move(parsed_chlo), packet_info); + return; } - - ProcessChlo(*std::move(parsed_chlo), packet_info); - return; } } @@ -755,16 +779,19 @@ // kFateProcess have been processed above. QUIC_BUG(quic_dispatcher_bad_packet_fate) << fate; break; - case kFateTimeWait: + case kFateTimeWait: { // Add this connection_id to the time-wait state, to safely reject // future packets. QUIC_DLOG(INFO) << "Adding connection ID " << server_connection_id << " to time-wait list."; QUIC_CODE_COUNT(quic_reject_fate_time_wait); + const std::string& connection_close_error_detail = + tls_alert_error_detail.empty() ? "Reject connection" + : tls_alert_error_detail; StatelesslyTerminateConnection( server_connection_id, packet_info->form, packet_info->version_flag, packet_info->use_length_prefix, packet_info->version, - QUIC_HANDSHAKE_FAILED, "Reject connection", + connection_close_error_code, connection_close_error_detail, quic::QuicTimeWaitListManager::SEND_STATELESS_RESET); QUICHE_DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait( @@ -775,7 +802,7 @@ GetPerPacketContext()); buffered_packets_.DiscardPackets(server_connection_id); - break; + } break; case kFateDrop: break; } @@ -815,6 +842,14 @@ result.tls_alert = tls_chlo_extractor.tls_alert(); } } + + if (send_connection_close_for_tls_alerts_ && result.tls_alert.has_value()) { + QUIC_BUG_IF(quic_dispatcher_parsed_chlo_and_tls_alert_coexist_2, + has_full_tls_chlo) + << "parsed_chlo and tls_alert should not be set at the same time."; + return result; + } + if (!has_full_tls_chlo) { // This packet does not contain a full CHLO. It could be a 0-RTT // packet that arrived before the CHLO (due to loss or reordering),
diff --git a/quiche/quic/core/quic_dispatcher.h b/quiche/quic/core/quic_dispatcher.h index e230739..d262d1e 100644 --- a/quiche/quic/core/quic_dispatcher.h +++ b/quiche/quic/core/quic_dispatcher.h
@@ -475,6 +475,9 @@ // If true, change expected_server_connection_id_length_ to be the received // destination connection ID length of all IETF long headers. bool should_update_expected_server_connection_id_length_; + + const bool send_connection_close_for_tls_alerts_ = + GetQuicRestartFlag(quic_dispatcher_send_connection_close_for_tls_alerts); }; } // namespace quic
diff --git a/quiche/quic/core/quic_flags_list.h b/quiche/quic/core/quic_flags_list.h index 661477e..c45c53c 100644 --- a/quiche/quic/core/quic_flags_list.h +++ b/quiche/quic/core/quic_flags_list.h
@@ -69,6 +69,8 @@ QUIC_FLAG(quic_reloadable_flag_quic_flush_pending_frames_and_padding_bytes_on_migration, true) // If true, ietf connection migration is no longer conditioned on connection option RVCM. QUIC_FLAG(quic_reloadable_flag_quic_remove_connection_migration_connection_option_v2, false) +// If true, if a fatal tls alert is raised while extracting CHLO, QuicDispatcher will send a connection close. +QUIC_FLAG(quic_restart_flag_quic_dispatcher_send_connection_close_for_tls_alerts, true) // If true, include offset in QUIC STREAM_DATA_BLOCKED and DATA_BLOCKED frames. QUIC_FLAG(quic_reloadable_flag_quic_include_offset_in_blocked_frames, true) // If true, include stream information in idle timeout connection close detail.