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