OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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/quic/quic_crypto_server_stream.h" | |
6 | |
7 #include <memory> | |
8 | |
9 #include "base/base64.h" | |
10 #include "crypto/secure_hash.h" | |
11 #include "net/quic/crypto/crypto_protocol.h" | |
12 #include "net/quic/crypto/crypto_utils.h" | |
13 #include "net/quic/crypto/quic_crypto_server_config.h" | |
14 #include "net/quic/crypto/quic_random.h" | |
15 #include "net/quic/proto/cached_network_parameters.pb.h" | |
16 #include "net/quic/quic_config.h" | |
17 #include "net/quic/quic_flags.h" | |
18 #include "net/quic/quic_protocol.h" | |
19 #include "net/quic/quic_server_session_base.h" | |
20 | |
21 using base::StringPiece; | |
22 using std::string; | |
23 | |
24 namespace net { | |
25 | |
26 namespace { | |
27 bool HasFixedTag(const CryptoHandshakeMessage& message) { | |
28 const QuicTag* received_tags; | |
29 size_t received_tags_length; | |
30 QuicErrorCode error = | |
31 message.GetTaglist(kCOPT, &received_tags, &received_tags_length); | |
32 if (error == QUIC_NO_ERROR) { | |
33 DCHECK(received_tags); | |
34 for (size_t i = 0; i < received_tags_length; ++i) { | |
35 if (received_tags[i] == kFIXD) { | |
36 return true; | |
37 } | |
38 } | |
39 } | |
40 return false; | |
41 } | |
42 } // namespace | |
43 | |
44 void ServerHelloNotifier::OnPacketAcked(int acked_bytes, | |
45 QuicTime::Delta ack_delay_time) { | |
46 DCHECK(!FLAGS_quic_no_shlo_listener); | |
47 // The SHLO is sent in one packet. | |
48 server_stream_->OnServerHelloAcked(); | |
49 } | |
50 | |
51 void ServerHelloNotifier::OnPacketRetransmitted(int /*retransmitted_bytes*/) {} | |
52 | |
53 QuicCryptoServerStreamBase::QuicCryptoServerStreamBase( | |
54 QuicServerSessionBase* session) | |
55 : QuicCryptoStream(session) {} | |
56 | |
57 // TODO(jokulik): Once stateless rejects support is inherent in the version | |
58 // number, this function will likely go away entirely. | |
59 // static | |
60 bool QuicCryptoServerStreamBase::DoesPeerSupportStatelessRejects( | |
61 const CryptoHandshakeMessage& message) { | |
62 const QuicTag* received_tags; | |
63 size_t received_tags_length; | |
64 QuicErrorCode error = | |
65 message.GetTaglist(kCOPT, &received_tags, &received_tags_length); | |
66 if (error != QUIC_NO_ERROR) { | |
67 return false; | |
68 } | |
69 for (size_t i = 0; i < received_tags_length; ++i) { | |
70 if (received_tags[i] == kSREJ) { | |
71 return true; | |
72 } | |
73 } | |
74 return false; | |
75 } | |
76 | |
77 QuicCryptoServerStream::QuicCryptoServerStream( | |
78 const QuicCryptoServerConfig* crypto_config, | |
79 QuicCompressedCertsCache* compressed_certs_cache, | |
80 bool use_stateless_rejects_if_peer_supported, | |
81 QuicServerSessionBase* session) | |
82 : QuicCryptoServerStreamBase(session), | |
83 crypto_config_(crypto_config), | |
84 compressed_certs_cache_(compressed_certs_cache), | |
85 validate_client_hello_cb_(nullptr), | |
86 num_handshake_messages_(0), | |
87 num_handshake_messages_with_server_nonces_(0), | |
88 send_server_config_update_cb_(nullptr), | |
89 num_server_config_update_messages_sent_(0), | |
90 use_stateless_rejects_if_peer_supported_( | |
91 use_stateless_rejects_if_peer_supported), | |
92 peer_supports_stateless_rejects_(false) { | |
93 DCHECK_EQ(Perspective::IS_SERVER, session->connection()->perspective()); | |
94 } | |
95 | |
96 QuicCryptoServerStream::~QuicCryptoServerStream() { | |
97 CancelOutstandingCallbacks(); | |
98 } | |
99 | |
100 void QuicCryptoServerStream::CancelOutstandingCallbacks() { | |
101 // Detach from the validation callback. Calling this multiple times is safe. | |
102 if (validate_client_hello_cb_ != nullptr) { | |
103 validate_client_hello_cb_->Cancel(); | |
104 validate_client_hello_cb_ = nullptr; | |
105 } | |
106 if (send_server_config_update_cb_ != nullptr) { | |
107 send_server_config_update_cb_->Cancel(); | |
108 send_server_config_update_cb_ = nullptr; | |
109 } | |
110 } | |
111 | |
112 void QuicCryptoServerStream::OnHandshakeMessage( | |
113 const CryptoHandshakeMessage& message) { | |
114 QuicCryptoServerStreamBase::OnHandshakeMessage(message); | |
115 ++num_handshake_messages_; | |
116 | |
117 bool require_kfixd = !FLAGS_quic_deprecate_kfixd; | |
118 | |
119 if (require_kfixd && !HasFixedTag(message)) { | |
120 CloseConnectionWithDetails(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, | |
121 "Missing kFIXD"); | |
122 return; | |
123 } | |
124 | |
125 // Do not process handshake messages after the handshake is confirmed. | |
126 if (handshake_confirmed_) { | |
127 CloseConnectionWithDetails(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE, | |
128 "Unexpected handshake message from client"); | |
129 return; | |
130 } | |
131 | |
132 if (message.tag() != kCHLO) { | |
133 CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, | |
134 "Handshake packet not CHLO"); | |
135 return; | |
136 } | |
137 | |
138 if (validate_client_hello_cb_ != nullptr) { | |
139 // Already processing some other handshake message. The protocol | |
140 // does not allow for clients to send multiple handshake messages | |
141 // before the server has a chance to respond. | |
142 CloseConnectionWithDetails( | |
143 QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO, | |
144 "Unexpected handshake message while processing CHLO"); | |
145 return; | |
146 } | |
147 | |
148 CryptoUtils::HashHandshakeMessage(message, &chlo_hash_); | |
149 | |
150 validate_client_hello_cb_ = new ValidateCallback(this); | |
151 crypto_config_->ValidateClientHello( | |
152 message, session()->connection()->peer_address().address(), | |
153 session()->connection()->self_address().address(), version(), | |
154 session()->connection()->clock(), &crypto_proof_, | |
155 validate_client_hello_cb_); | |
156 } | |
157 | |
158 void QuicCryptoServerStream::FinishProcessingHandshakeMessage( | |
159 const CryptoHandshakeMessage& message, | |
160 const ValidateClientHelloResultCallback::Result& result, | |
161 std::unique_ptr<ProofSource::Details> details) { | |
162 // Clear the callback that got us here. | |
163 DCHECK(validate_client_hello_cb_ != nullptr); | |
164 validate_client_hello_cb_ = nullptr; | |
165 | |
166 if (use_stateless_rejects_if_peer_supported_) { | |
167 peer_supports_stateless_rejects_ = DoesPeerSupportStatelessRejects(message); | |
168 } | |
169 | |
170 CryptoHandshakeMessage reply; | |
171 DiversificationNonce diversification_nonce; | |
172 string error_details; | |
173 QuicErrorCode error = | |
174 ProcessClientHello(message, result, std::move(details), &reply, | |
175 &diversification_nonce, &error_details); | |
176 | |
177 if (error != QUIC_NO_ERROR) { | |
178 CloseConnectionWithDetails(error, error_details); | |
179 return; | |
180 } | |
181 | |
182 if (reply.tag() != kSHLO) { | |
183 if (reply.tag() == kSREJ) { | |
184 DCHECK(use_stateless_rejects_if_peer_supported_); | |
185 DCHECK(peer_supports_stateless_rejects_); | |
186 // Before sending the SREJ, cause the connection to save crypto packets | |
187 // so that they can be added to the time wait list manager and | |
188 // retransmitted. | |
189 session()->connection()->EnableSavingCryptoPackets(); | |
190 } | |
191 SendHandshakeMessage(reply); | |
192 | |
193 if (reply.tag() == kSREJ) { | |
194 DCHECK(use_stateless_rejects_if_peer_supported_); | |
195 DCHECK(peer_supports_stateless_rejects_); | |
196 DCHECK(!handshake_confirmed()); | |
197 DVLOG(1) << "Closing connection " | |
198 << session()->connection()->connection_id() | |
199 << " because of a stateless reject."; | |
200 session()->connection()->CloseConnection( | |
201 QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, "stateless reject", | |
202 ConnectionCloseBehavior::SILENT_CLOSE); | |
203 } | |
204 return; | |
205 } | |
206 | |
207 // If we are returning a SHLO then we accepted the handshake. Now | |
208 // process the negotiated configuration options as part of the | |
209 // session config. | |
210 QuicConfig* config = session()->config(); | |
211 OverrideQuicConfigDefaults(config); | |
212 error = config->ProcessPeerHello(message, CLIENT, &error_details); | |
213 if (error != QUIC_NO_ERROR) { | |
214 CloseConnectionWithDetails(error, error_details); | |
215 return; | |
216 } | |
217 | |
218 session()->OnConfigNegotiated(); | |
219 | |
220 config->ToHandshakeMessage(&reply); | |
221 | |
222 // Receiving a full CHLO implies the client is prepared to decrypt with | |
223 // the new server write key. We can start to encrypt with the new server | |
224 // write key. | |
225 // | |
226 // NOTE: the SHLO will be encrypted with the new server write key. | |
227 session()->connection()->SetEncrypter( | |
228 ENCRYPTION_INITIAL, | |
229 crypto_negotiated_params_.initial_crypters.encrypter.release()); | |
230 session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); | |
231 // Set the decrypter immediately so that we no longer accept unencrypted | |
232 // packets. | |
233 session()->connection()->SetDecrypter( | |
234 ENCRYPTION_INITIAL, | |
235 crypto_negotiated_params_.initial_crypters.decrypter.release()); | |
236 if (version() > QUIC_VERSION_32) { | |
237 session()->connection()->SetDiversificationNonce(diversification_nonce); | |
238 } | |
239 | |
240 // We want to be notified when the SHLO is ACKed so that we can disable | |
241 // HANDSHAKE_MODE in the sent packet manager. | |
242 scoped_refptr<ServerHelloNotifier> server_hello_notifier( | |
243 new ServerHelloNotifier(this)); | |
244 SendHandshakeMessage(reply, FLAGS_quic_no_shlo_listener | |
245 ? nullptr | |
246 : server_hello_notifier.get()); | |
247 | |
248 session()->connection()->SetEncrypter( | |
249 ENCRYPTION_FORWARD_SECURE, | |
250 crypto_negotiated_params_.forward_secure_crypters.encrypter.release()); | |
251 session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); | |
252 | |
253 session()->connection()->SetAlternativeDecrypter( | |
254 ENCRYPTION_FORWARD_SECURE, | |
255 crypto_negotiated_params_.forward_secure_crypters.decrypter.release(), | |
256 false /* don't latch */); | |
257 | |
258 encryption_established_ = true; | |
259 handshake_confirmed_ = true; | |
260 session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); | |
261 } | |
262 | |
263 void QuicCryptoServerStream::SendServerConfigUpdate( | |
264 const CachedNetworkParameters* cached_network_params) { | |
265 if (!handshake_confirmed_) { | |
266 return; | |
267 } | |
268 | |
269 if (FLAGS_enable_async_get_proof) { | |
270 if (send_server_config_update_cb_ != nullptr) { | |
271 DVLOG(1) | |
272 << "Skipped server config update since one is already in progress"; | |
273 return; | |
274 } | |
275 | |
276 std::unique_ptr<SendServerConfigUpdateCallback> cb( | |
277 new SendServerConfigUpdateCallback(this)); | |
278 send_server_config_update_cb_ = cb.get(); | |
279 crypto_config_->BuildServerConfigUpdateMessage( | |
280 session()->connection()->version(), chlo_hash_, | |
281 previous_source_address_tokens_, | |
282 session()->connection()->self_address().address(), | |
283 session()->connection()->peer_address().address(), | |
284 session()->connection()->clock(), | |
285 session()->connection()->random_generator(), compressed_certs_cache_, | |
286 crypto_negotiated_params_, cached_network_params, std::move(cb)); | |
287 return; | |
288 } | |
289 | |
290 CryptoHandshakeMessage server_config_update_message; | |
291 if (!crypto_config_->BuildServerConfigUpdateMessage( | |
292 session()->connection()->version(), chlo_hash_, | |
293 previous_source_address_tokens_, | |
294 session()->connection()->self_address().address(), | |
295 session()->connection()->peer_address().address(), | |
296 session()->connection()->clock(), | |
297 session()->connection()->random_generator(), compressed_certs_cache_, | |
298 crypto_negotiated_params_, cached_network_params, | |
299 &server_config_update_message)) { | |
300 DVLOG(1) << "Server: Failed to build server config update (SCUP)!"; | |
301 return; | |
302 } | |
303 | |
304 DVLOG(1) << "Server: Sending server config update: " | |
305 << server_config_update_message.DebugString(); | |
306 const QuicData& data = server_config_update_message.GetSerialized(); | |
307 WriteOrBufferData(StringPiece(data.data(), data.length()), false, nullptr); | |
308 | |
309 ++num_server_config_update_messages_sent_; | |
310 } | |
311 | |
312 QuicCryptoServerStream::SendServerConfigUpdateCallback:: | |
313 SendServerConfigUpdateCallback(QuicCryptoServerStream* parent) | |
314 : parent_(parent) {} | |
315 | |
316 void QuicCryptoServerStream::SendServerConfigUpdateCallback::Cancel() { | |
317 parent_ = nullptr; | |
318 } | |
319 | |
320 // From BuildServerConfigUpdateMessageResultCallback | |
321 void QuicCryptoServerStream::SendServerConfigUpdateCallback::Run( | |
322 bool ok, | |
323 const CryptoHandshakeMessage& message) { | |
324 if (parent_ == nullptr) { | |
325 return; | |
326 } | |
327 parent_->FinishSendServerConfigUpdate(ok, message); | |
328 } | |
329 | |
330 void QuicCryptoServerStream::FinishSendServerConfigUpdate( | |
331 bool ok, | |
332 const CryptoHandshakeMessage& message) { | |
333 // Clear the callback that got us here. | |
334 DCHECK(send_server_config_update_cb_ != nullptr); | |
335 send_server_config_update_cb_ = nullptr; | |
336 | |
337 if (!ok) { | |
338 DVLOG(1) << "Server: Failed to build server config update (SCUP)!"; | |
339 return; | |
340 } | |
341 | |
342 DVLOG(1) << "Server: Sending server config update: " << message.DebugString(); | |
343 const QuicData& data = message.GetSerialized(); | |
344 WriteOrBufferData(StringPiece(data.data(), data.length()), false, nullptr); | |
345 | |
346 ++num_server_config_update_messages_sent_; | |
347 } | |
348 | |
349 void QuicCryptoServerStream::OnServerHelloAcked() { | |
350 session()->connection()->OnHandshakeComplete(); | |
351 } | |
352 | |
353 uint8_t QuicCryptoServerStream::NumHandshakeMessages() const { | |
354 return num_handshake_messages_; | |
355 } | |
356 | |
357 uint8_t QuicCryptoServerStream::NumHandshakeMessagesWithServerNonces() const { | |
358 return num_handshake_messages_with_server_nonces_; | |
359 } | |
360 | |
361 int QuicCryptoServerStream::NumServerConfigUpdateMessagesSent() const { | |
362 return num_server_config_update_messages_sent_; | |
363 } | |
364 | |
365 const CachedNetworkParameters* | |
366 QuicCryptoServerStream::PreviousCachedNetworkParams() const { | |
367 return previous_cached_network_params_.get(); | |
368 } | |
369 | |
370 bool QuicCryptoServerStream::UseStatelessRejectsIfPeerSupported() const { | |
371 return use_stateless_rejects_if_peer_supported_; | |
372 } | |
373 | |
374 bool QuicCryptoServerStream::PeerSupportsStatelessRejects() const { | |
375 return peer_supports_stateless_rejects_; | |
376 } | |
377 | |
378 void QuicCryptoServerStream::SetPeerSupportsStatelessRejects( | |
379 bool peer_supports_stateless_rejects) { | |
380 peer_supports_stateless_rejects_ = peer_supports_stateless_rejects; | |
381 } | |
382 | |
383 void QuicCryptoServerStream::SetPreviousCachedNetworkParams( | |
384 CachedNetworkParameters cached_network_params) { | |
385 previous_cached_network_params_.reset( | |
386 new CachedNetworkParameters(cached_network_params)); | |
387 } | |
388 | |
389 bool QuicCryptoServerStream::GetBase64SHA256ClientChannelID( | |
390 string* output) const { | |
391 if (!encryption_established_ || | |
392 crypto_negotiated_params_.channel_id.empty()) { | |
393 return false; | |
394 } | |
395 | |
396 const string& channel_id(crypto_negotiated_params_.channel_id); | |
397 std::unique_ptr<crypto::SecureHash> hash( | |
398 crypto::SecureHash::Create(crypto::SecureHash::SHA256)); | |
399 hash->Update(channel_id.data(), channel_id.size()); | |
400 uint8_t digest[32]; | |
401 hash->Finish(digest, sizeof(digest)); | |
402 | |
403 base::Base64Encode( | |
404 string(reinterpret_cast<const char*>(digest), sizeof(digest)), output); | |
405 // Remove padding. | |
406 size_t len = output->size(); | |
407 if (len >= 2) { | |
408 if ((*output)[len - 1] == '=') { | |
409 len--; | |
410 if ((*output)[len - 1] == '=') { | |
411 len--; | |
412 } | |
413 output->resize(len); | |
414 } | |
415 } | |
416 return true; | |
417 } | |
418 | |
419 QuicErrorCode QuicCryptoServerStream::ProcessClientHello( | |
420 const CryptoHandshakeMessage& message, | |
421 const ValidateClientHelloResultCallback::Result& result, | |
422 std::unique_ptr<ProofSource::Details> proof_source_details, | |
423 CryptoHandshakeMessage* reply, | |
424 DiversificationNonce* out_diversification_nonce, | |
425 string* error_details) { | |
426 QuicServerSessionBase* session_base = | |
427 static_cast<QuicServerSessionBase*>(session()); | |
428 if (!session_base->CanAcceptClientHello(message, error_details)) { | |
429 return QUIC_HANDSHAKE_FAILED; | |
430 } | |
431 | |
432 if (!result.info.server_nonce.empty()) { | |
433 ++num_handshake_messages_with_server_nonces_; | |
434 } | |
435 // Store the bandwidth estimate from the client. | |
436 if (result.cached_network_params.bandwidth_estimate_bytes_per_second() > 0) { | |
437 previous_cached_network_params_.reset( | |
438 new CachedNetworkParameters(result.cached_network_params)); | |
439 } | |
440 previous_source_address_tokens_ = result.info.source_address_tokens; | |
441 | |
442 const bool use_stateless_rejects_in_crypto_config = | |
443 use_stateless_rejects_if_peer_supported_ && | |
444 peer_supports_stateless_rejects_; | |
445 QuicConnection* connection = session()->connection(); | |
446 const QuicConnectionId server_designated_connection_id = | |
447 use_stateless_rejects_in_crypto_config | |
448 ? GenerateConnectionIdForReject(connection->connection_id()) | |
449 : 0; | |
450 return crypto_config_->ProcessClientHello( | |
451 result, /*reject_only=*/false, connection->connection_id(), | |
452 connection->self_address().address(), connection->peer_address(), | |
453 version(), connection->supported_versions(), | |
454 use_stateless_rejects_in_crypto_config, server_designated_connection_id, | |
455 connection->clock(), connection->random_generator(), | |
456 compressed_certs_cache_, &crypto_negotiated_params_, &crypto_proof_, | |
457 reply, out_diversification_nonce, error_details); | |
458 } | |
459 | |
460 void QuicCryptoServerStream::OverrideQuicConfigDefaults(QuicConfig* config) {} | |
461 | |
462 QuicCryptoServerStream::ValidateCallback::ValidateCallback( | |
463 QuicCryptoServerStream* parent) | |
464 : parent_(parent) {} | |
465 | |
466 void QuicCryptoServerStream::ValidateCallback::Cancel() { | |
467 parent_ = nullptr; | |
468 } | |
469 | |
470 void QuicCryptoServerStream::ValidateCallback::RunImpl( | |
471 const CryptoHandshakeMessage& client_hello, | |
472 const Result& result, | |
473 std::unique_ptr<ProofSource::Details> details) { | |
474 if (parent_ != nullptr) { | |
475 parent_->FinishProcessingHandshakeMessage(client_hello, result, | |
476 std::move(details)); | |
477 } | |
478 } | |
479 | |
480 QuicConnectionId QuicCryptoServerStream::GenerateConnectionIdForReject( | |
481 QuicConnectionId connection_id) { | |
482 // TODO(rch): Remove this method when | |
483 // reloadable_flag_quic_dispatcher_creates_id2 is removed. | |
484 QuicServerSessionBase* session_base = | |
485 static_cast<QuicServerSessionBase*>(session()); | |
486 return session_base->GenerateConnectionIdForReject(connection_id); | |
487 } | |
488 | |
489 } // namespace net | |
OLD | NEW |