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