xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/tls_chlo_extractor.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright (c) 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "quiche/quic/core/tls_chlo_extractor.h"
6 
7 #include <cstdint>
8 #include <cstring>
9 #include <memory>
10 #include <vector>
11 
12 #include "absl/strings/str_cat.h"
13 #include "absl/strings/string_view.h"
14 #include "openssl/ssl.h"
15 #include "quiche/quic/core/frames/quic_crypto_frame.h"
16 #include "quiche/quic/core/quic_data_reader.h"
17 #include "quiche/quic/core/quic_error_codes.h"
18 #include "quiche/quic/core/quic_framer.h"
19 #include "quiche/quic/core/quic_time.h"
20 #include "quiche/quic/core/quic_types.h"
21 #include "quiche/quic/core/quic_versions.h"
22 #include "quiche/quic/platform/api/quic_bug_tracker.h"
23 #include "quiche/quic/platform/api/quic_flags.h"
24 #include "quiche/common/platform/api/quiche_logging.h"
25 
26 namespace quic {
27 
28 namespace {
HasExtension(const SSL_CLIENT_HELLO * client_hello,uint16_t extension)29 bool HasExtension(const SSL_CLIENT_HELLO* client_hello, uint16_t extension) {
30   const uint8_t* unused_extension_bytes;
31   size_t unused_extension_len;
32   return 1 == SSL_early_callback_ctx_extension_get(client_hello, extension,
33                                                    &unused_extension_bytes,
34                                                    &unused_extension_len);
35 }
36 
GetSupportedGroups(const SSL_CLIENT_HELLO * client_hello)37 std::vector<uint16_t> GetSupportedGroups(const SSL_CLIENT_HELLO* client_hello) {
38   const uint8_t* extension_data;
39   size_t extension_len;
40   int rv = SSL_early_callback_ctx_extension_get(
41       client_hello, TLSEXT_TYPE_supported_groups, &extension_data,
42       &extension_len);
43   if (rv != 1) {
44     return {};
45   }
46 
47   // See https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.7 for the
48   // format of this extension.
49   QuicDataReader named_groups_reader(
50       reinterpret_cast<const char*>(extension_data), extension_len);
51   uint16_t named_groups_len;
52   if (!named_groups_reader.ReadUInt16(&named_groups_len) ||
53       named_groups_len + sizeof(uint16_t) != extension_len) {
54     QUIC_CODE_COUNT(quic_chlo_supported_groups_invalid_length);
55     return {};
56   }
57 
58   std::vector<uint16_t> named_groups;
59   while (!named_groups_reader.IsDoneReading()) {
60     uint16_t named_group;
61     if (!named_groups_reader.ReadUInt16(&named_group)) {
62       QUIC_CODE_COUNT(quic_chlo_supported_groups_odd_length);
63       QUIC_LOG_FIRST_N(WARNING, 10) << "Failed to read named groups";
64       break;
65     }
66     named_groups.push_back(named_group);
67   }
68   return named_groups;
69 }
70 
71 }  // namespace
72 
TlsChloExtractor()73 TlsChloExtractor::TlsChloExtractor()
74     : crypto_stream_sequencer_(this),
75       state_(State::kInitial),
76       parsed_crypto_frame_in_this_packet_(false) {}
77 
TlsChloExtractor(TlsChloExtractor && other)78 TlsChloExtractor::TlsChloExtractor(TlsChloExtractor&& other)
79     : TlsChloExtractor() {
80   *this = std::move(other);
81 }
82 
operator =(TlsChloExtractor && other)83 TlsChloExtractor& TlsChloExtractor::operator=(TlsChloExtractor&& other) {
84   framer_ = std::move(other.framer_);
85   if (framer_) {
86     framer_->set_visitor(this);
87   }
88   crypto_stream_sequencer_ = std::move(other.crypto_stream_sequencer_);
89   crypto_stream_sequencer_.set_stream(this);
90   ssl_ = std::move(other.ssl_);
91   if (ssl_) {
92     std::pair<SSL_CTX*, int> shared_handles = GetSharedSslHandles();
93     int ex_data_index = shared_handles.second;
94     const int rv = SSL_set_ex_data(ssl_.get(), ex_data_index, this);
95     QUICHE_CHECK_EQ(rv, 1) << "Internal allocation failure in SSL_set_ex_data";
96   }
97   state_ = other.state_;
98   error_details_ = std::move(other.error_details_);
99   parsed_crypto_frame_in_this_packet_ =
100       other.parsed_crypto_frame_in_this_packet_;
101   supported_groups_ = std::move(other.supported_groups_);
102   alpns_ = std::move(other.alpns_);
103   server_name_ = std::move(other.server_name_);
104   client_hello_bytes_ = std::move(other.client_hello_bytes_);
105   return *this;
106 }
107 
IngestPacket(const ParsedQuicVersion & version,const QuicReceivedPacket & packet)108 void TlsChloExtractor::IngestPacket(const ParsedQuicVersion& version,
109                                     const QuicReceivedPacket& packet) {
110   if (state_ == State::kUnrecoverableFailure) {
111     QUIC_DLOG(ERROR) << "Not ingesting packet after unrecoverable error";
112     return;
113   }
114   if (version == UnsupportedQuicVersion()) {
115     QUIC_DLOG(ERROR) << "Not ingesting packet with unsupported version";
116     return;
117   }
118   if (version.handshake_protocol != PROTOCOL_TLS1_3) {
119     QUIC_DLOG(ERROR) << "Not ingesting packet with non-TLS version " << version;
120     return;
121   }
122   if (framer_) {
123     // This is not the first packet we have ingested, check if version matches.
124     if (!framer_->IsSupportedVersion(version)) {
125       QUIC_DLOG(ERROR)
126           << "Not ingesting packet with version mismatch, expected "
127           << framer_->version() << ", got " << version;
128       return;
129     }
130   } else {
131     // This is the first packet we have ingested, setup parser.
132     framer_ = std::make_unique<QuicFramer>(
133         ParsedQuicVersionVector{version}, QuicTime::Zero(),
134         Perspective::IS_SERVER, /*expected_server_connection_id_length=*/0);
135     // Note that expected_server_connection_id_length only matters for short
136     // headers and we explicitly drop those so we can pass any value here.
137     framer_->set_visitor(this);
138   }
139 
140   // When the framer parses |packet|, if it sees a CRYPTO frame it will call
141   // OnCryptoFrame below and that will set parsed_crypto_frame_in_this_packet_
142   // to true.
143   parsed_crypto_frame_in_this_packet_ = false;
144   const bool parse_success = framer_->ProcessPacket(packet);
145   if (state_ == State::kInitial && parsed_crypto_frame_in_this_packet_) {
146     // If we parsed a CRYPTO frame but didn't advance the state from initial,
147     // then it means that we will need more packets to reassemble the full CHLO,
148     // so we advance the state here. This can happen when the first packet
149     // received is not the first one in the crypto stream. This allows us to
150     // differentiate our state between single-packet CHLO and multi-packet CHLO.
151     state_ = State::kParsedPartialChloFragment;
152   }
153 
154   if (!parse_success) {
155     // This could be due to the packet being non-initial for example.
156     QUIC_DLOG(ERROR) << "Failed to process packet";
157     return;
158   }
159 }
160 
161 // This is called when the framer parsed the unencrypted parts of the header.
OnUnauthenticatedPublicHeader(const QuicPacketHeader & header)162 bool TlsChloExtractor::OnUnauthenticatedPublicHeader(
163     const QuicPacketHeader& header) {
164   if (header.form != IETF_QUIC_LONG_HEADER_PACKET) {
165     QUIC_DLOG(ERROR) << "Not parsing non-long-header packet " << header;
166     return false;
167   }
168   if (header.long_packet_type != INITIAL) {
169     QUIC_DLOG(ERROR) << "Not parsing non-initial packet " << header;
170     return false;
171   }
172   // QuicFramer is constructed without knowledge of the server's connection ID
173   // so it needs to be set up here in order to decrypt the packet.
174   framer_->SetInitialObfuscators(header.destination_connection_id);
175   return true;
176 }
177 
178 // This is called by the framer if it detects a change in version during
179 // parsing.
OnProtocolVersionMismatch(ParsedQuicVersion version)180 bool TlsChloExtractor::OnProtocolVersionMismatch(ParsedQuicVersion version) {
181   // This should never be called because we already check versions in
182   // IngestPacket.
183   QUIC_BUG(quic_bug_10855_1) << "Unexpected version mismatch, expected "
184                              << framer_->version() << ", got " << version;
185   return false;
186 }
187 
188 // This is called by the QuicStreamSequencer if it encounters an unrecoverable
189 // error that will prevent it from reassembling the crypto stream data.
OnUnrecoverableError(QuicErrorCode error,const std::string & details)190 void TlsChloExtractor::OnUnrecoverableError(QuicErrorCode error,
191                                             const std::string& details) {
192   HandleUnrecoverableError(absl::StrCat(
193       "Crypto stream error ", QuicErrorCodeToString(error), ": ", details));
194 }
195 
OnUnrecoverableError(QuicErrorCode error,QuicIetfTransportErrorCodes ietf_error,const std::string & details)196 void TlsChloExtractor::OnUnrecoverableError(
197     QuicErrorCode error, QuicIetfTransportErrorCodes ietf_error,
198     const std::string& details) {
199   HandleUnrecoverableError(absl::StrCat(
200       "Crypto stream error ", QuicErrorCodeToString(error), "(",
201       QuicIetfTransportErrorCodeString(ietf_error), "): ", details));
202 }
203 
204 // This is called by the framer if it sees a CRYPTO frame during parsing.
OnCryptoFrame(const QuicCryptoFrame & frame)205 bool TlsChloExtractor::OnCryptoFrame(const QuicCryptoFrame& frame) {
206   if (frame.level != ENCRYPTION_INITIAL) {
207     // Since we drop non-INITIAL packets in OnUnauthenticatedPublicHeader,
208     // we should never receive any CRYPTO frames at other encryption levels.
209     QUIC_BUG(quic_bug_10855_2) << "Parsed bad-level CRYPTO frame " << frame;
210     return false;
211   }
212   // parsed_crypto_frame_in_this_packet_ is checked in IngestPacket to allow
213   // advancing our state to track the difference between single-packet CHLO
214   // and multi-packet CHLO.
215   parsed_crypto_frame_in_this_packet_ = true;
216   crypto_stream_sequencer_.OnCryptoFrame(frame);
217   return true;
218 }
219 
220 // Called by the QuicStreamSequencer when it receives a CRYPTO frame that
221 // advances the amount of contiguous data we now have starting from offset 0.
OnDataAvailable()222 void TlsChloExtractor::OnDataAvailable() {
223   // Lazily set up BoringSSL handle.
224   SetupSslHandle();
225 
226   // Get data from the stream sequencer and pass it to BoringSSL.
227   struct iovec iov;
228   while (crypto_stream_sequencer_.GetReadableRegion(&iov)) {
229     const int rv = SSL_provide_quic_data(
230         ssl_.get(), ssl_encryption_initial,
231         reinterpret_cast<const uint8_t*>(iov.iov_base), iov.iov_len);
232     if (rv != 1) {
233       HandleUnrecoverableError("SSL_provide_quic_data failed");
234       return;
235     }
236     crypto_stream_sequencer_.MarkConsumed(iov.iov_len);
237   }
238 
239   // Instruct BoringSSL to attempt parsing a full CHLO from the provided data.
240   // We ignore the return value since we know the handshake is going to fail
241   // because we explicitly cancel processing once we've parsed the CHLO.
242   (void)SSL_do_handshake(ssl_.get());
243 }
244 
245 // static
GetInstanceFromSSL(SSL * ssl)246 TlsChloExtractor* TlsChloExtractor::GetInstanceFromSSL(SSL* ssl) {
247   std::pair<SSL_CTX*, int> shared_handles = GetSharedSslHandles();
248   int ex_data_index = shared_handles.second;
249   return reinterpret_cast<TlsChloExtractor*>(
250       SSL_get_ex_data(ssl, ex_data_index));
251 }
252 
253 // static
SetReadSecretCallback(SSL * ssl,enum ssl_encryption_level_t,const SSL_CIPHER *,const uint8_t *,size_t)254 int TlsChloExtractor::SetReadSecretCallback(
255     SSL* ssl, enum ssl_encryption_level_t /*level*/,
256     const SSL_CIPHER* /*cipher*/, const uint8_t* /*secret*/,
257     size_t /*secret_length*/) {
258   GetInstanceFromSSL(ssl)->HandleUnexpectedCallback("SetReadSecretCallback");
259   return 0;
260 }
261 
262 // static
SetWriteSecretCallback(SSL * ssl,enum ssl_encryption_level_t,const SSL_CIPHER *,const uint8_t *,size_t)263 int TlsChloExtractor::SetWriteSecretCallback(
264     SSL* ssl, enum ssl_encryption_level_t /*level*/,
265     const SSL_CIPHER* /*cipher*/, const uint8_t* /*secret*/,
266     size_t /*secret_length*/) {
267   GetInstanceFromSSL(ssl)->HandleUnexpectedCallback("SetWriteSecretCallback");
268   return 0;
269 }
270 
271 // static
WriteMessageCallback(SSL * ssl,enum ssl_encryption_level_t,const uint8_t *,size_t)272 int TlsChloExtractor::WriteMessageCallback(
273     SSL* ssl, enum ssl_encryption_level_t /*level*/, const uint8_t* /*data*/,
274     size_t /*len*/) {
275   GetInstanceFromSSL(ssl)->HandleUnexpectedCallback("WriteMessageCallback");
276   return 0;
277 }
278 
279 // static
FlushFlightCallback(SSL * ssl)280 int TlsChloExtractor::FlushFlightCallback(SSL* ssl) {
281   GetInstanceFromSSL(ssl)->HandleUnexpectedCallback("FlushFlightCallback");
282   return 0;
283 }
284 
HandleUnexpectedCallback(const std::string & callback_name)285 void TlsChloExtractor::HandleUnexpectedCallback(
286     const std::string& callback_name) {
287   std::string error_details =
288       absl::StrCat("Unexpected callback ", callback_name);
289   QUIC_BUG(quic_bug_10855_3) << error_details;
290   HandleUnrecoverableError(error_details);
291 }
292 
293 // static
SendAlertCallback(SSL * ssl,enum ssl_encryption_level_t,uint8_t desc)294 int TlsChloExtractor::SendAlertCallback(SSL* ssl,
295                                         enum ssl_encryption_level_t /*level*/,
296                                         uint8_t desc) {
297   GetInstanceFromSSL(ssl)->SendAlert(desc);
298   return 0;
299 }
300 
SendAlert(uint8_t tls_alert_value)301 void TlsChloExtractor::SendAlert(uint8_t tls_alert_value) {
302   if (tls_alert_value == SSL3_AD_HANDSHAKE_FAILURE && HasParsedFullChlo()) {
303     // This is the most common scenario. Since we return an error from
304     // SelectCertCallback in order to cancel further processing, BoringSSL will
305     // try to send this alert to tell the client that the handshake failed.
306     return;
307   }
308   HandleUnrecoverableError(absl::StrCat(
309       "BoringSSL attempted to send alert ", static_cast<int>(tls_alert_value),
310       " ", SSL_alert_desc_string_long(tls_alert_value)));
311   if (state_ == State::kUnrecoverableFailure) {
312     tls_alert_ = tls_alert_value;
313   }
314 }
315 
316 // static
SelectCertCallback(const SSL_CLIENT_HELLO * client_hello)317 enum ssl_select_cert_result_t TlsChloExtractor::SelectCertCallback(
318     const SSL_CLIENT_HELLO* client_hello) {
319   GetInstanceFromSSL(client_hello->ssl)->HandleParsedChlo(client_hello);
320   // Always return an error to cancel any further processing in BoringSSL.
321   return ssl_select_cert_error;
322 }
323 
324 // Extracts the server name and ALPN from the parsed ClientHello.
HandleParsedChlo(const SSL_CLIENT_HELLO * client_hello)325 void TlsChloExtractor::HandleParsedChlo(const SSL_CLIENT_HELLO* client_hello) {
326   const char* server_name =
327       SSL_get_servername(client_hello->ssl, TLSEXT_NAMETYPE_host_name);
328   if (server_name) {
329     server_name_ = std::string(server_name);
330   }
331 
332   resumption_attempted_ =
333       HasExtension(client_hello, TLSEXT_TYPE_pre_shared_key);
334   early_data_attempted_ = HasExtension(client_hello, TLSEXT_TYPE_early_data);
335 
336   QUICHE_DCHECK(client_hello_bytes_.empty());
337   client_hello_bytes_.assign(
338       client_hello->client_hello,
339       client_hello->client_hello + client_hello->client_hello_len);
340 
341   const uint8_t* alpn_data;
342   size_t alpn_len;
343   int rv = SSL_early_callback_ctx_extension_get(
344       client_hello, TLSEXT_TYPE_application_layer_protocol_negotiation,
345       &alpn_data, &alpn_len);
346   if (rv == 1) {
347     QuicDataReader alpns_reader(reinterpret_cast<const char*>(alpn_data),
348                                 alpn_len);
349     absl::string_view alpns_payload;
350     if (!alpns_reader.ReadStringPiece16(&alpns_payload)) {
351       HandleUnrecoverableError("Failed to read alpns_payload");
352       return;
353     }
354     QuicDataReader alpns_payload_reader(alpns_payload);
355     while (!alpns_payload_reader.IsDoneReading()) {
356       absl::string_view alpn_payload;
357       if (!alpns_payload_reader.ReadStringPiece8(&alpn_payload)) {
358         HandleUnrecoverableError("Failed to read alpn_payload");
359         return;
360       }
361       alpns_.emplace_back(std::string(alpn_payload));
362     }
363   }
364 
365   supported_groups_ = GetSupportedGroups(client_hello);
366 
367   // Update our state now that we've parsed a full CHLO.
368   if (state_ == State::kInitial) {
369     state_ = State::kParsedFullSinglePacketChlo;
370   } else if (state_ == State::kParsedPartialChloFragment) {
371     state_ = State::kParsedFullMultiPacketChlo;
372   } else {
373     QUIC_BUG(quic_bug_10855_4)
374         << "Unexpected state on successful parse " << StateToString(state_);
375   }
376 }
377 
378 // static
GetSharedSslHandles()379 std::pair<SSL_CTX*, int> TlsChloExtractor::GetSharedSslHandles() {
380   // Use a lambda to benefit from C++11 guarantee that static variables are
381   // initialized lazily in a thread-safe manner. |shared_handles| is therefore
382   // guaranteed to be initialized exactly once and never destructed.
383   static std::pair<SSL_CTX*, int>* shared_handles = []() {
384     CRYPTO_library_init();
385     SSL_CTX* ssl_ctx = SSL_CTX_new(TLS_with_buffers_method());
386     SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
387     SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
388     static const SSL_QUIC_METHOD kQuicCallbacks{
389         TlsChloExtractor::SetReadSecretCallback,
390         TlsChloExtractor::SetWriteSecretCallback,
391         TlsChloExtractor::WriteMessageCallback,
392         TlsChloExtractor::FlushFlightCallback,
393         TlsChloExtractor::SendAlertCallback};
394     SSL_CTX_set_quic_method(ssl_ctx, &kQuicCallbacks);
395     SSL_CTX_set_select_certificate_cb(ssl_ctx,
396                                       TlsChloExtractor::SelectCertCallback);
397     int ex_data_index =
398         SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);
399     return new std::pair<SSL_CTX*, int>(ssl_ctx, ex_data_index);
400   }();
401   return *shared_handles;
402 }
403 
404 // Sets up the per-instance SSL handle needed by BoringSSL.
SetupSslHandle()405 void TlsChloExtractor::SetupSslHandle() {
406   if (ssl_) {
407     // Handles have already been set up.
408     return;
409   }
410 
411   std::pair<SSL_CTX*, int> shared_handles = GetSharedSslHandles();
412   SSL_CTX* ssl_ctx = shared_handles.first;
413   int ex_data_index = shared_handles.second;
414 
415   ssl_ = bssl::UniquePtr<SSL>(SSL_new(ssl_ctx));
416   const int rv = SSL_set_ex_data(ssl_.get(), ex_data_index, this);
417   QUICHE_CHECK_EQ(rv, 1) << "Internal allocation failure in SSL_set_ex_data";
418   SSL_set_accept_state(ssl_.get());
419 
420   // Make sure we use the right TLS extension codepoint.
421   int use_legacy_extension = 0;
422   if (framer_->version().UsesLegacyTlsExtension()) {
423     use_legacy_extension = 1;
424   }
425   SSL_set_quic_use_legacy_codepoint(ssl_.get(), use_legacy_extension);
426 }
427 
428 // Called by other methods to record any unrecoverable failures they experience.
HandleUnrecoverableError(const std::string & error_details)429 void TlsChloExtractor::HandleUnrecoverableError(
430     const std::string& error_details) {
431   if (HasParsedFullChlo()) {
432     // Ignore errors if we've parsed everything successfully.
433     QUIC_DLOG(ERROR) << "Ignoring error: " << error_details;
434     return;
435   }
436   QUIC_DLOG(ERROR) << "Handling error: " << error_details;
437 
438   state_ = State::kUnrecoverableFailure;
439 
440   if (error_details_.empty()) {
441     error_details_ = error_details;
442   } else {
443     error_details_ = absl::StrCat(error_details_, "; ", error_details);
444   }
445 }
446 
447 // static
StateToString(State state)448 std::string TlsChloExtractor::StateToString(State state) {
449   switch (state) {
450     case State::kInitial:
451       return "Initial";
452     case State::kParsedFullSinglePacketChlo:
453       return "ParsedFullSinglePacketChlo";
454     case State::kParsedFullMultiPacketChlo:
455       return "ParsedFullMultiPacketChlo";
456     case State::kParsedPartialChloFragment:
457       return "ParsedPartialChloFragment";
458     case State::kUnrecoverableFailure:
459       return "UnrecoverableFailure";
460   }
461   return absl::StrCat("Unknown(", static_cast<int>(state), ")");
462 }
463 
operator <<(std::ostream & os,const TlsChloExtractor::State & state)464 std::ostream& operator<<(std::ostream& os,
465                          const TlsChloExtractor::State& state) {
466   os << TlsChloExtractor::StateToString(state);
467   return os;
468 }
469 
470 }  // namespace quic
471