1 // Copyright 2012 The Chromium Authors
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/socket/ssl_connect_job.h"
6
7 #include <cstdlib>
8 #include <memory>
9 #include <utility>
10
11 #include "base/feature_list.h"
12 #include "base/functional/bind.h"
13 #include "base/functional/callback_helpers.h"
14 #include "base/metrics/histogram_functions.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "net/base/connection_endpoint_metadata.h"
17 #include "net/base/features.h"
18 #include "net/base/host_port_pair.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/trace_constants.h"
21 #include "net/base/tracing.h"
22 #include "net/base/url_util.h"
23 #include "net/cert/x509_util.h"
24 #include "net/http/http_proxy_connect_job.h"
25 #include "net/log/net_log_source_type.h"
26 #include "net/log/net_log_values.h"
27 #include "net/log/net_log_with_source.h"
28 #include "net/socket/client_socket_factory.h"
29 #include "net/socket/client_socket_handle.h"
30 #include "net/socket/socks_connect_job.h"
31 #include "net/socket/ssl_client_socket.h"
32 #include "net/socket/transport_connect_job.h"
33 #include "net/ssl/ssl_cert_request_info.h"
34 #include "net/ssl/ssl_connection_status_flags.h"
35 #include "net/ssl/ssl_info.h"
36 #include "third_party/abseil-cpp/absl/types/variant.h"
37 #include "third_party/boringssl/src/include/openssl/pool.h"
38 #include "third_party/boringssl/src/include/openssl/ssl.h"
39
40 namespace net {
41
42 namespace {
43
44 // Timeout for the SSL handshake portion of the connect.
45 constexpr base::TimeDelta kSSLHandshakeTimeout(base::Seconds(30));
46
47 } // namespace
48
SSLSocketParams(ConnectJobParams nested_params,const HostPortPair & host_and_port,const SSLConfig & ssl_config,NetworkAnonymizationKey network_anonymization_key)49 SSLSocketParams::SSLSocketParams(
50 ConnectJobParams nested_params,
51 const HostPortPair& host_and_port,
52 const SSLConfig& ssl_config,
53 NetworkAnonymizationKey network_anonymization_key)
54 : nested_params_(nested_params),
55 host_and_port_(host_and_port),
56 ssl_config_(ssl_config),
57 network_anonymization_key_(network_anonymization_key) {
58 CHECK(!nested_params_.is_ssl());
59 }
60
61 SSLSocketParams::~SSLSocketParams() = default;
62
GetConnectionType() const63 SSLSocketParams::ConnectionType SSLSocketParams::GetConnectionType() const {
64 if (nested_params_.is_socks()) {
65 return SOCKS_PROXY;
66 }
67 if (nested_params_.is_http_proxy()) {
68 return HTTP_PROXY;
69 }
70 return DIRECT;
71 }
72
Create(RequestPriority priority,const SocketTag & socket_tag,const CommonConnectJobParams * common_connect_job_params,scoped_refptr<SSLSocketParams> params,ConnectJob::Delegate * delegate,const NetLogWithSource * net_log)73 std::unique_ptr<SSLConnectJob> SSLConnectJob::Factory::Create(
74 RequestPriority priority,
75 const SocketTag& socket_tag,
76 const CommonConnectJobParams* common_connect_job_params,
77 scoped_refptr<SSLSocketParams> params,
78 ConnectJob::Delegate* delegate,
79 const NetLogWithSource* net_log) {
80 return std::make_unique<SSLConnectJob>(priority, socket_tag,
81 common_connect_job_params,
82 std::move(params), delegate, net_log);
83 }
84
SSLConnectJob(RequestPriority priority,const SocketTag & socket_tag,const CommonConnectJobParams * common_connect_job_params,scoped_refptr<SSLSocketParams> params,ConnectJob::Delegate * delegate,const NetLogWithSource * net_log)85 SSLConnectJob::SSLConnectJob(
86 RequestPriority priority,
87 const SocketTag& socket_tag,
88 const CommonConnectJobParams* common_connect_job_params,
89 scoped_refptr<SSLSocketParams> params,
90 ConnectJob::Delegate* delegate,
91 const NetLogWithSource* net_log)
92 : ConnectJob(
93 priority,
94 socket_tag,
95 // The SSLConnectJob's timer is only started during the SSL handshake.
96 base::TimeDelta(),
97 common_connect_job_params,
98 delegate,
99 net_log,
100 NetLogSourceType::SSL_CONNECT_JOB,
101 NetLogEventType::SSL_CONNECT_JOB_CONNECT),
102 params_(std::move(params)),
103 callback_(base::BindRepeating(&SSLConnectJob::OnIOComplete,
104 base::Unretained(this))) {}
105
~SSLConnectJob()106 SSLConnectJob::~SSLConnectJob() {
107 // In the case the job was canceled, need to delete nested job first to
108 // correctly order NetLog events.
109 nested_connect_job_.reset();
110 }
111
GetLoadState() const112 LoadState SSLConnectJob::GetLoadState() const {
113 switch (next_state_) {
114 case STATE_TRANSPORT_CONNECT:
115 case STATE_SOCKS_CONNECT:
116 case STATE_TUNNEL_CONNECT:
117 return LOAD_STATE_IDLE;
118 case STATE_TRANSPORT_CONNECT_COMPLETE:
119 case STATE_SOCKS_CONNECT_COMPLETE:
120 return nested_connect_job_->GetLoadState();
121 case STATE_TUNNEL_CONNECT_COMPLETE:
122 if (nested_socket_) {
123 return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL;
124 }
125 return nested_connect_job_->GetLoadState();
126 case STATE_SSL_CONNECT:
127 case STATE_SSL_CONNECT_COMPLETE:
128 return LOAD_STATE_SSL_HANDSHAKE;
129 default:
130 NOTREACHED();
131 return LOAD_STATE_IDLE;
132 }
133 }
134
HasEstablishedConnection() const135 bool SSLConnectJob::HasEstablishedConnection() const {
136 // If waiting on a nested ConnectJob, defer to that ConnectJob's state.
137 if (nested_connect_job_) {
138 return nested_connect_job_->HasEstablishedConnection();
139 }
140 // Otherwise, return true if a socket has been created.
141 return nested_socket_ || ssl_socket_;
142 }
143
OnConnectJobComplete(int result,ConnectJob * job)144 void SSLConnectJob::OnConnectJobComplete(int result, ConnectJob* job) {
145 DCHECK_EQ(job, nested_connect_job_.get());
146 OnIOComplete(result);
147 }
148
OnNeedsProxyAuth(const HttpResponseInfo & response,HttpAuthController * auth_controller,base::OnceClosure restart_with_auth_callback,ConnectJob * job)149 void SSLConnectJob::OnNeedsProxyAuth(
150 const HttpResponseInfo& response,
151 HttpAuthController* auth_controller,
152 base::OnceClosure restart_with_auth_callback,
153 ConnectJob* job) {
154 DCHECK_EQ(next_state_, STATE_TUNNEL_CONNECT_COMPLETE);
155
156 // The timer shouldn't have started running yet, since the handshake only
157 // starts after a tunnel has been established through the proxy.
158 DCHECK(!TimerIsRunning());
159
160 // Just pass the callback up to the consumer. This class doesn't need to do
161 // anything once credentials are provided.
162 NotifyDelegateOfProxyAuth(response, auth_controller,
163 std::move(restart_with_auth_callback));
164 }
165
GetConnectionAttempts() const166 ConnectionAttempts SSLConnectJob::GetConnectionAttempts() const {
167 return connection_attempts_;
168 }
169
GetResolveErrorInfo() const170 ResolveErrorInfo SSLConnectJob::GetResolveErrorInfo() const {
171 return resolve_error_info_;
172 }
173
IsSSLError() const174 bool SSLConnectJob::IsSSLError() const {
175 return ssl_negotiation_started_;
176 }
177
GetCertRequestInfo()178 scoped_refptr<SSLCertRequestInfo> SSLConnectJob::GetCertRequestInfo() {
179 return ssl_cert_request_info_;
180 }
181
HandshakeTimeoutForTesting()182 base::TimeDelta SSLConnectJob::HandshakeTimeoutForTesting() {
183 return kSSLHandshakeTimeout;
184 }
185
OnIOComplete(int result)186 void SSLConnectJob::OnIOComplete(int result) {
187 int rv = DoLoop(result);
188 if (rv != ERR_IO_PENDING) {
189 NotifyDelegateOfCompletion(rv); // Deletes |this|.
190 }
191 }
192
DoLoop(int result)193 int SSLConnectJob::DoLoop(int result) {
194 TRACE_EVENT0(NetTracingCategory(), "SSLConnectJob::DoLoop");
195 DCHECK_NE(next_state_, STATE_NONE);
196
197 int rv = result;
198 do {
199 State state = next_state_;
200 next_state_ = STATE_NONE;
201 switch (state) {
202 case STATE_TRANSPORT_CONNECT:
203 DCHECK_EQ(OK, rv);
204 rv = DoTransportConnect();
205 break;
206 case STATE_TRANSPORT_CONNECT_COMPLETE:
207 rv = DoTransportConnectComplete(rv);
208 break;
209 case STATE_SOCKS_CONNECT:
210 DCHECK_EQ(OK, rv);
211 rv = DoSOCKSConnect();
212 break;
213 case STATE_SOCKS_CONNECT_COMPLETE:
214 rv = DoSOCKSConnectComplete(rv);
215 break;
216 case STATE_TUNNEL_CONNECT:
217 DCHECK_EQ(OK, rv);
218 rv = DoTunnelConnect();
219 break;
220 case STATE_TUNNEL_CONNECT_COMPLETE:
221 rv = DoTunnelConnectComplete(rv);
222 break;
223 case STATE_SSL_CONNECT:
224 DCHECK_EQ(OK, rv);
225 rv = DoSSLConnect();
226 break;
227 case STATE_SSL_CONNECT_COMPLETE:
228 rv = DoSSLConnectComplete(rv);
229 break;
230 default:
231 NOTREACHED() << "bad state";
232 rv = ERR_FAILED;
233 break;
234 }
235 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
236
237 return rv;
238 }
239
DoTransportConnect()240 int SSLConnectJob::DoTransportConnect() {
241 DCHECK(!nested_connect_job_);
242 DCHECK(params_->GetDirectConnectionParams());
243 DCHECK(!TimerIsRunning());
244
245 next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
246 // If this is an ECH retry, connect to the same server as before.
247 std::optional<TransportConnectJob::EndpointResultOverride>
248 endpoint_result_override;
249 if (ech_retry_configs_) {
250 DCHECK(ssl_client_context()->config().ech_enabled);
251 DCHECK(endpoint_result_);
252 endpoint_result_override.emplace(*endpoint_result_, dns_aliases_);
253 }
254 nested_connect_job_ = std::make_unique<TransportConnectJob>(
255 priority(), socket_tag(), common_connect_job_params(),
256 params_->GetDirectConnectionParams(), this, &net_log(),
257 std::move(endpoint_result_override));
258 return nested_connect_job_->Connect();
259 }
260
DoTransportConnectComplete(int result)261 int SSLConnectJob::DoTransportConnectComplete(int result) {
262 resolve_error_info_ = nested_connect_job_->GetResolveErrorInfo();
263 ConnectionAttempts connection_attempts =
264 nested_connect_job_->GetConnectionAttempts();
265 connection_attempts_.insert(connection_attempts_.end(),
266 connection_attempts.begin(),
267 connection_attempts.end());
268 if (result == OK) {
269 next_state_ = STATE_SSL_CONNECT;
270 nested_socket_ = nested_connect_job_->PassSocket();
271 nested_socket_->GetPeerAddress(&server_address_);
272 dns_aliases_ = nested_socket_->GetDnsAliases();
273 }
274
275 return result;
276 }
277
DoSOCKSConnect()278 int SSLConnectJob::DoSOCKSConnect() {
279 DCHECK(!nested_connect_job_);
280 DCHECK(params_->GetSocksProxyConnectionParams());
281 DCHECK(!TimerIsRunning());
282
283 next_state_ = STATE_SOCKS_CONNECT_COMPLETE;
284 nested_connect_job_ = std::make_unique<SOCKSConnectJob>(
285 priority(), socket_tag(), common_connect_job_params(),
286 params_->GetSocksProxyConnectionParams(), this, &net_log());
287 return nested_connect_job_->Connect();
288 }
289
DoSOCKSConnectComplete(int result)290 int SSLConnectJob::DoSOCKSConnectComplete(int result) {
291 resolve_error_info_ = nested_connect_job_->GetResolveErrorInfo();
292 if (result == OK) {
293 next_state_ = STATE_SSL_CONNECT;
294 nested_socket_ = nested_connect_job_->PassSocket();
295 }
296
297 return result;
298 }
299
DoTunnelConnect()300 int SSLConnectJob::DoTunnelConnect() {
301 DCHECK(!nested_connect_job_);
302 DCHECK(params_->GetHttpProxyConnectionParams());
303 DCHECK(!TimerIsRunning());
304
305 next_state_ = STATE_TUNNEL_CONNECT_COMPLETE;
306 nested_connect_job_ = std::make_unique<HttpProxyConnectJob>(
307 priority(), socket_tag(), common_connect_job_params(),
308 params_->GetHttpProxyConnectionParams(), this, &net_log());
309 return nested_connect_job_->Connect();
310 }
311
DoTunnelConnectComplete(int result)312 int SSLConnectJob::DoTunnelConnectComplete(int result) {
313 resolve_error_info_ = nested_connect_job_->GetResolveErrorInfo();
314 nested_socket_ = nested_connect_job_->PassSocket();
315
316 if (result < 0) {
317 // Extract the information needed to prompt for appropriate proxy
318 // authentication so that when ClientSocketPoolBaseHelper calls
319 // |GetAdditionalErrorState|, we can easily set the state.
320 if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
321 ssl_cert_request_info_ = nested_connect_job_->GetCertRequestInfo();
322 }
323 return result;
324 }
325
326 next_state_ = STATE_SSL_CONNECT;
327 return result;
328 }
329
DoSSLConnect()330 int SSLConnectJob::DoSSLConnect() {
331 TRACE_EVENT0(NetTracingCategory(), "SSLConnectJob::DoSSLConnect");
332 DCHECK(!TimerIsRunning());
333
334 next_state_ = STATE_SSL_CONNECT_COMPLETE;
335
336 // Set the timeout to just the time allowed for the SSL handshake.
337 ResetTimer(kSSLHandshakeTimeout);
338
339 // Get the transport's connect start and DNS times.
340 const LoadTimingInfo::ConnectTiming& socket_connect_timing =
341 nested_connect_job_->connect_timing();
342
343 // Overwriting |connect_start| serves two purposes - it adjusts timing so
344 // |connect_start| doesn't include dns times, and it adjusts the time so
345 // as not to include time spent waiting for an idle socket.
346 connect_timing_.connect_start = socket_connect_timing.connect_start;
347 connect_timing_.domain_lookup_start =
348 socket_connect_timing.domain_lookup_start;
349 connect_timing_.domain_lookup_end = socket_connect_timing.domain_lookup_end;
350
351 ssl_negotiation_started_ = true;
352 connect_timing_.ssl_start = base::TimeTicks::Now();
353
354 // Save the `HostResolverEndpointResult`. `nested_connect_job_` is destroyed
355 // at the end of this function.
356 endpoint_result_ = nested_connect_job_->GetHostResolverEndpointResult();
357
358 SSLConfig ssl_config = params_->ssl_config();
359 ssl_config.ignore_certificate_errors =
360 *common_connect_job_params()->ignore_certificate_errors;
361 ssl_config.network_anonymization_key = params_->network_anonymization_key();
362
363 if (ssl_client_context()->config().ech_enabled) {
364 if (ech_retry_configs_) {
365 ssl_config.ech_config_list = *ech_retry_configs_;
366 } else if (endpoint_result_) {
367 ssl_config.ech_config_list = endpoint_result_->metadata.ech_config_list;
368 }
369 if (!ssl_config.ech_config_list.empty()) {
370 // Overriding the DNS lookup only works for direct connections. We
371 // currently do not support ECH with other connection types.
372 DCHECK_EQ(params_->GetConnectionType(), SSLSocketParams::DIRECT);
373 }
374 }
375
376 ssl_socket_ = client_socket_factory()->CreateSSLClientSocket(
377 ssl_client_context(), std::move(nested_socket_), params_->host_and_port(),
378 ssl_config);
379 nested_connect_job_.reset();
380 return ssl_socket_->Connect(callback_);
381 }
382
DoSSLConnectComplete(int result)383 int SSLConnectJob::DoSSLConnectComplete(int result) {
384 connect_timing_.ssl_end = base::TimeTicks::Now();
385
386 if (result != OK && !server_address_.address().empty()) {
387 connection_attempts_.push_back(ConnectionAttempt(server_address_, result));
388 server_address_ = IPEndPoint();
389 }
390
391 // Historically, many servers which negotiated SHA-1 server signatures in
392 // TLS 1.2 actually support SHA-2 but preferentially sign SHA-1 if available.
393 // In order to get accurate metrics while deprecating SHA-1, we initially
394 // connected with SHA-1 disabled and then retried with enabled.
395 //
396 // SHA-1 is now always disabled, but we retained the fallback to separate the
397 // effect of disabling SHA-1 from the effect of having a single automatic
398 // retry on a potentially unreliably network connection.
399 //
400 // TODO(https://crbug.com/658905): Remove this now redundant retry.
401 if (disable_legacy_crypto_with_fallback_ &&
402 (result == ERR_CONNECTION_CLOSED || result == ERR_CONNECTION_RESET ||
403 result == ERR_SSL_PROTOCOL_ERROR ||
404 result == ERR_SSL_VERSION_OR_CIPHER_MISMATCH)) {
405 ResetStateForRestart();
406 disable_legacy_crypto_with_fallback_ = false;
407 next_state_ = GetInitialState(params_->GetConnectionType());
408 return OK;
409 }
410
411 // We record metrics based on whether the server advertised ECH support in
412 // DNS. This allows the metrics to measure the same set of servers in both
413 // control and experiment group.
414 const bool is_ech_capable =
415 endpoint_result_ && !endpoint_result_->metadata.ech_config_list.empty();
416 const bool ech_enabled = ssl_client_context()->config().ech_enabled;
417
418 if (!ech_retry_configs_ && result == ERR_ECH_NOT_NEGOTIATED && ech_enabled) {
419 // We used ECH, and the server could not decrypt the ClientHello. However,
420 // it was able to handshake with the public name and send authenticated
421 // retry configs. If this is not the first time around, retry the connection
422 // with the new ECHConfigList, or with ECH disabled (empty retry configs),
423 // as directed.
424 //
425 // See
426 // https://www.ietf.org/archive/id/draft-ietf-tls-esni-13.html#section-6.1.6
427 DCHECK(is_ech_capable);
428 ech_retry_configs_ = ssl_socket_->GetECHRetryConfigs();
429 net_log().AddEvent(
430 NetLogEventType::SSL_CONNECT_JOB_RESTART_WITH_ECH_CONFIG_LIST, [&] {
431 return base::Value::Dict().Set(
432 "bytes", NetLogBinaryValue(*ech_retry_configs_));
433 });
434
435 ResetStateForRestart();
436 next_state_ = GetInitialState(params_->GetConnectionType());
437 return OK;
438 }
439
440 if (is_ech_capable && ech_enabled) {
441 // These values are persisted to logs. Entries should not be renumbered
442 // and numeric values should never be reused.
443 enum class ECHResult {
444 // The connection succeeded on the initial connection.
445 kSuccessInitial = 0,
446 // The connection failed on the initial connection, without providing
447 // retry configs.
448 kErrorInitial = 1,
449 // The connection succeeded after getting retry configs.
450 kSuccessRetry = 2,
451 // The connection failed after getting retry configs.
452 kErrorRetry = 3,
453 // The connection succeeded after getting a rollback signal.
454 kSuccessRollback = 4,
455 // The connection failed after getting a rollback signal.
456 kErrorRollback = 5,
457 kMaxValue = kErrorRollback,
458 };
459 const bool is_ok = result == OK;
460 ECHResult ech_result;
461 if (!ech_retry_configs_.has_value()) {
462 ech_result =
463 is_ok ? ECHResult::kSuccessInitial : ECHResult::kErrorInitial;
464 } else if (ech_retry_configs_->empty()) {
465 ech_result =
466 is_ok ? ECHResult::kSuccessRollback : ECHResult::kErrorRollback;
467 } else {
468 ech_result = is_ok ? ECHResult::kSuccessRetry : ECHResult::kErrorRetry;
469 }
470 base::UmaHistogramEnumeration("Net.SSL.ECHResult", ech_result);
471 }
472
473 if (result == OK) {
474 DCHECK(!connect_timing_.ssl_start.is_null());
475 base::TimeDelta connect_duration =
476 connect_timing_.ssl_end - connect_timing_.ssl_start;
477 UMA_HISTOGRAM_CUSTOM_TIMES("Net.SSL_Connection_Latency_2", connect_duration,
478 base::Milliseconds(1), base::Minutes(1), 100);
479 if (is_ech_capable) {
480 UMA_HISTOGRAM_CUSTOM_TIMES("Net.SSL_Connection_Latency_ECH",
481 connect_duration, base::Milliseconds(1),
482 base::Minutes(1), 100);
483 }
484
485 SSLInfo ssl_info;
486 bool has_ssl_info = ssl_socket_->GetSSLInfo(&ssl_info);
487 DCHECK(has_ssl_info);
488
489 SSLVersion version =
490 SSLConnectionStatusToVersion(ssl_info.connection_status);
491 UMA_HISTOGRAM_ENUMERATION("Net.SSLVersion", version,
492 SSL_CONNECTION_VERSION_MAX);
493
494 uint16_t cipher_suite =
495 SSLConnectionStatusToCipherSuite(ssl_info.connection_status);
496 base::UmaHistogramSparse("Net.SSL_CipherSuite", cipher_suite);
497
498 if (ssl_info.key_exchange_group != 0) {
499 base::UmaHistogramSparse("Net.SSL_KeyExchange.ECDHE",
500 ssl_info.key_exchange_group);
501 }
502 }
503
504 base::UmaHistogramSparse("Net.SSL_Connection_Error", std::abs(result));
505 if (is_ech_capable) {
506 base::UmaHistogramSparse("Net.SSL_Connection_Error_ECH", std::abs(result));
507 }
508
509 if (result == OK || IsCertificateError(result)) {
510 SetSocket(std::move(ssl_socket_), std::move(dns_aliases_));
511 } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
512 ssl_cert_request_info_ = base::MakeRefCounted<SSLCertRequestInfo>();
513 ssl_socket_->GetSSLCertRequestInfo(ssl_cert_request_info_.get());
514 }
515
516 return result;
517 }
518
GetInitialState(SSLSocketParams::ConnectionType connection_type)519 SSLConnectJob::State SSLConnectJob::GetInitialState(
520 SSLSocketParams::ConnectionType connection_type) {
521 switch (connection_type) {
522 case SSLSocketParams::DIRECT:
523 return STATE_TRANSPORT_CONNECT;
524 case SSLSocketParams::HTTP_PROXY:
525 return STATE_TUNNEL_CONNECT;
526 case SSLSocketParams::SOCKS_PROXY:
527 return STATE_SOCKS_CONNECT;
528 }
529 NOTREACHED();
530 return STATE_NONE;
531 }
532
ConnectInternal()533 int SSLConnectJob::ConnectInternal() {
534 next_state_ = GetInitialState(params_->GetConnectionType());
535 return DoLoop(OK);
536 }
537
ResetStateForRestart()538 void SSLConnectJob::ResetStateForRestart() {
539 ResetTimer(base::TimeDelta());
540 nested_connect_job_ = nullptr;
541 nested_socket_ = nullptr;
542 ssl_socket_ = nullptr;
543 ssl_cert_request_info_ = nullptr;
544 ssl_negotiation_started_ = false;
545 resolve_error_info_ = ResolveErrorInfo();
546 server_address_ = IPEndPoint();
547 }
548
ChangePriorityInternal(RequestPriority priority)549 void SSLConnectJob::ChangePriorityInternal(RequestPriority priority) {
550 if (nested_connect_job_) {
551 nested_connect_job_->ChangePriority(priority);
552 }
553 }
554
555 } // namespace net
556