| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/socket/ssl_connect_job.h" |
| |
| #include <cstdlib> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/feature_list.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "net/base/connection_endpoint_metadata.h" |
| #include "net/base/features.h" |
| #include "net/base/host_port_pair.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/trace_constants.h" |
| #include "net/base/tracing.h" |
| #include "net/base/url_util.h" |
| #include "net/cert/x509_util.h" |
| #include "net/http/http_proxy_connect_job.h" |
| #include "net/log/net_log_source_type.h" |
| #include "net/log/net_log_values.h" |
| #include "net/log/net_log_with_source.h" |
| #include "net/socket/client_socket_factory.h" |
| #include "net/socket/client_socket_handle.h" |
| #include "net/socket/socks_connect_job.h" |
| #include "net/socket/ssl_client_socket.h" |
| #include "net/socket/transport_connect_job.h" |
| #include "net/ssl/ssl_cert_request_info.h" |
| #include "net/ssl/ssl_connection_status_flags.h" |
| #include "net/ssl/ssl_info.h" |
| #include "net/ssl/ssl_legacy_crypto_fallback.h" |
| #include "third_party/boringssl/src/include/openssl/pool.h" |
| #include "third_party/boringssl/src/include/openssl/ssl.h" |
| |
| namespace net { |
| |
| namespace { |
| |
| // Timeout for the SSL handshake portion of the connect. |
| constexpr base::TimeDelta kSSLHandshakeTimeout(base::Seconds(30)); |
| |
| } // namespace |
| |
| SSLSocketParams::SSLSocketParams( |
| scoped_refptr<TransportSocketParams> direct_params, |
| scoped_refptr<SOCKSSocketParams> socks_proxy_params, |
| scoped_refptr<HttpProxySocketParams> http_proxy_params, |
| const HostPortPair& host_and_port, |
| const SSLConfig& ssl_config, |
| PrivacyMode privacy_mode, |
| NetworkAnonymizationKey network_anonymization_key) |
| : direct_params_(std::move(direct_params)), |
| socks_proxy_params_(std::move(socks_proxy_params)), |
| http_proxy_params_(std::move(http_proxy_params)), |
| host_and_port_(host_and_port), |
| ssl_config_(ssl_config), |
| privacy_mode_(privacy_mode), |
| network_anonymization_key_(network_anonymization_key) { |
| // Only one set of lower level ConnectJob params should be non-NULL. |
| DCHECK((direct_params_ && !socks_proxy_params_ && !http_proxy_params_) || |
| (!direct_params_ && socks_proxy_params_ && !http_proxy_params_) || |
| (!direct_params_ && !socks_proxy_params_ && http_proxy_params_)); |
| } |
| |
| SSLSocketParams::~SSLSocketParams() = default; |
| |
| SSLSocketParams::ConnectionType SSLSocketParams::GetConnectionType() const { |
| if (direct_params_.get()) { |
| DCHECK(!socks_proxy_params_.get()); |
| DCHECK(!http_proxy_params_.get()); |
| return DIRECT; |
| } |
| |
| if (socks_proxy_params_.get()) { |
| DCHECK(!http_proxy_params_.get()); |
| return SOCKS_PROXY; |
| } |
| |
| DCHECK(http_proxy_params_.get()); |
| return HTTP_PROXY; |
| } |
| |
| const scoped_refptr<TransportSocketParams>& |
| SSLSocketParams::GetDirectConnectionParams() const { |
| DCHECK_EQ(GetConnectionType(), DIRECT); |
| return direct_params_; |
| } |
| |
| const scoped_refptr<SOCKSSocketParams>& |
| SSLSocketParams::GetSocksProxyConnectionParams() const { |
| DCHECK_EQ(GetConnectionType(), SOCKS_PROXY); |
| return socks_proxy_params_; |
| } |
| |
| const scoped_refptr<HttpProxySocketParams>& |
| SSLSocketParams::GetHttpProxyConnectionParams() const { |
| DCHECK_EQ(GetConnectionType(), HTTP_PROXY); |
| return http_proxy_params_; |
| } |
| |
| std::unique_ptr<SSLConnectJob> SSLConnectJob::Factory::Create( |
| RequestPriority priority, |
| const SocketTag& socket_tag, |
| const CommonConnectJobParams* common_connect_job_params, |
| scoped_refptr<SSLSocketParams> params, |
| ConnectJob::Delegate* delegate, |
| const NetLogWithSource* net_log) { |
| return std::make_unique<SSLConnectJob>(priority, socket_tag, |
| common_connect_job_params, |
| std::move(params), delegate, net_log); |
| } |
| |
| SSLConnectJob::SSLConnectJob( |
| RequestPriority priority, |
| const SocketTag& socket_tag, |
| const CommonConnectJobParams* common_connect_job_params, |
| scoped_refptr<SSLSocketParams> params, |
| ConnectJob::Delegate* delegate, |
| const NetLogWithSource* net_log) |
| : ConnectJob( |
| priority, |
| socket_tag, |
| // The SSLConnectJob's timer is only started during the SSL handshake. |
| base::TimeDelta(), |
| common_connect_job_params, |
| delegate, |
| net_log, |
| NetLogSourceType::SSL_CONNECT_JOB, |
| NetLogEventType::SSL_CONNECT_JOB_CONNECT), |
| params_(std::move(params)), |
| callback_(base::BindRepeating(&SSLConnectJob::OnIOComplete, |
| base::Unretained(this))) {} |
| |
| SSLConnectJob::~SSLConnectJob() { |
| // In the case the job was canceled, need to delete nested job first to |
| // correctly order NetLog events. |
| nested_connect_job_.reset(); |
| } |
| |
| LoadState SSLConnectJob::GetLoadState() const { |
| switch (next_state_) { |
| case STATE_TRANSPORT_CONNECT: |
| case STATE_SOCKS_CONNECT: |
| case STATE_TUNNEL_CONNECT: |
| return LOAD_STATE_IDLE; |
| case STATE_TRANSPORT_CONNECT_COMPLETE: |
| case STATE_SOCKS_CONNECT_COMPLETE: |
| return nested_connect_job_->GetLoadState(); |
| case STATE_TUNNEL_CONNECT_COMPLETE: |
| if (nested_socket_) |
| return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL; |
| return nested_connect_job_->GetLoadState(); |
| case STATE_SSL_CONNECT: |
| case STATE_SSL_CONNECT_COMPLETE: |
| return LOAD_STATE_SSL_HANDSHAKE; |
| default: |
| NOTREACHED(); |
| return LOAD_STATE_IDLE; |
| } |
| } |
| |
| bool SSLConnectJob::HasEstablishedConnection() const { |
| // If waiting on a nested ConnectJob, defer to that ConnectJob's state. |
| if (nested_connect_job_) |
| return nested_connect_job_->HasEstablishedConnection(); |
| // Otherwise, return true if a socket has been created. |
| return nested_socket_ || ssl_socket_; |
| } |
| |
| void SSLConnectJob::OnConnectJobComplete(int result, ConnectJob* job) { |
| DCHECK_EQ(job, nested_connect_job_.get()); |
| OnIOComplete(result); |
| } |
| |
| void SSLConnectJob::OnNeedsProxyAuth( |
| const HttpResponseInfo& response, |
| HttpAuthController* auth_controller, |
| base::OnceClosure restart_with_auth_callback, |
| ConnectJob* job) { |
| DCHECK_EQ(next_state_, STATE_TUNNEL_CONNECT_COMPLETE); |
| |
| // The timer shouldn't have started running yet, since the handshake only |
| // starts after a tunnel has been established through the proxy. |
| DCHECK(!TimerIsRunning()); |
| |
| // Just pass the callback up to the consumer. This class doesn't need to do |
| // anything once credentials are provided. |
| NotifyDelegateOfProxyAuth(response, auth_controller, |
| std::move(restart_with_auth_callback)); |
| } |
| |
| ConnectionAttempts SSLConnectJob::GetConnectionAttempts() const { |
| return connection_attempts_; |
| } |
| |
| ResolveErrorInfo SSLConnectJob::GetResolveErrorInfo() const { |
| return resolve_error_info_; |
| } |
| |
| bool SSLConnectJob::IsSSLError() const { |
| return ssl_negotiation_started_; |
| } |
| |
| scoped_refptr<SSLCertRequestInfo> SSLConnectJob::GetCertRequestInfo() { |
| return ssl_cert_request_info_; |
| } |
| |
| base::TimeDelta SSLConnectJob::HandshakeTimeoutForTesting() { |
| return kSSLHandshakeTimeout; |
| } |
| |
| void SSLConnectJob::OnIOComplete(int result) { |
| int rv = DoLoop(result); |
| if (rv != ERR_IO_PENDING) |
| NotifyDelegateOfCompletion(rv); // Deletes |this|. |
| } |
| |
| int SSLConnectJob::DoLoop(int result) { |
| TRACE_EVENT0(NetTracingCategory(), "SSLConnectJob::DoLoop"); |
| DCHECK_NE(next_state_, STATE_NONE); |
| |
| int rv = result; |
| do { |
| State state = next_state_; |
| next_state_ = STATE_NONE; |
| switch (state) { |
| case STATE_TRANSPORT_CONNECT: |
| DCHECK_EQ(OK, rv); |
| rv = DoTransportConnect(); |
| break; |
| case STATE_TRANSPORT_CONNECT_COMPLETE: |
| rv = DoTransportConnectComplete(rv); |
| break; |
| case STATE_SOCKS_CONNECT: |
| DCHECK_EQ(OK, rv); |
| rv = DoSOCKSConnect(); |
| break; |
| case STATE_SOCKS_CONNECT_COMPLETE: |
| rv = DoSOCKSConnectComplete(rv); |
| break; |
| case STATE_TUNNEL_CONNECT: |
| DCHECK_EQ(OK, rv); |
| rv = DoTunnelConnect(); |
| break; |
| case STATE_TUNNEL_CONNECT_COMPLETE: |
| rv = DoTunnelConnectComplete(rv); |
| break; |
| case STATE_SSL_CONNECT: |
| DCHECK_EQ(OK, rv); |
| rv = DoSSLConnect(); |
| break; |
| case STATE_SSL_CONNECT_COMPLETE: |
| rv = DoSSLConnectComplete(rv); |
| break; |
| default: |
| NOTREACHED() << "bad state"; |
| rv = ERR_FAILED; |
| break; |
| } |
| } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); |
| |
| return rv; |
| } |
| |
| int SSLConnectJob::DoTransportConnect() { |
| DCHECK(!nested_connect_job_); |
| DCHECK(params_->GetDirectConnectionParams()); |
| DCHECK(!TimerIsRunning()); |
| |
| next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE; |
| // If this is an ECH retry, connect to the same server as before. |
| absl::optional<TransportConnectJob::EndpointResultOverride> |
| endpoint_result_override; |
| if (ech_retry_configs_) { |
| DCHECK(ssl_client_context()->config().EncryptedClientHelloEnabled()); |
| DCHECK(endpoint_result_); |
| endpoint_result_override.emplace(*endpoint_result_, dns_aliases_); |
| } |
| nested_connect_job_ = std::make_unique<TransportConnectJob>( |
| priority(), socket_tag(), common_connect_job_params(), |
| params_->GetDirectConnectionParams(), this, &net_log(), |
| std::move(endpoint_result_override)); |
| return nested_connect_job_->Connect(); |
| } |
| |
| int SSLConnectJob::DoTransportConnectComplete(int result) { |
| resolve_error_info_ = nested_connect_job_->GetResolveErrorInfo(); |
| ConnectionAttempts connection_attempts = |
| nested_connect_job_->GetConnectionAttempts(); |
| connection_attempts_.insert(connection_attempts_.end(), |
| connection_attempts.begin(), |
| connection_attempts.end()); |
| if (result == OK) { |
| next_state_ = STATE_SSL_CONNECT; |
| nested_socket_ = nested_connect_job_->PassSocket(); |
| nested_socket_->GetPeerAddress(&server_address_); |
| dns_aliases_ = nested_socket_->GetDnsAliases(); |
| } |
| |
| return result; |
| } |
| |
| int SSLConnectJob::DoSOCKSConnect() { |
| DCHECK(!nested_connect_job_); |
| DCHECK(params_->GetSocksProxyConnectionParams()); |
| DCHECK(!TimerIsRunning()); |
| |
| next_state_ = STATE_SOCKS_CONNECT_COMPLETE; |
| nested_connect_job_ = std::make_unique<SOCKSConnectJob>( |
| priority(), socket_tag(), common_connect_job_params(), |
| params_->GetSocksProxyConnectionParams(), this, &net_log()); |
| return nested_connect_job_->Connect(); |
| } |
| |
| int SSLConnectJob::DoSOCKSConnectComplete(int result) { |
| resolve_error_info_ = nested_connect_job_->GetResolveErrorInfo(); |
| if (result == OK) { |
| next_state_ = STATE_SSL_CONNECT; |
| nested_socket_ = nested_connect_job_->PassSocket(); |
| } |
| |
| return result; |
| } |
| |
| int SSLConnectJob::DoTunnelConnect() { |
| DCHECK(!nested_connect_job_); |
| DCHECK(params_->GetHttpProxyConnectionParams()); |
| DCHECK(!TimerIsRunning()); |
| |
| next_state_ = STATE_TUNNEL_CONNECT_COMPLETE; |
| scoped_refptr<HttpProxySocketParams> http_proxy_params = |
| params_->GetHttpProxyConnectionParams(); |
| nested_connect_job_ = std::make_unique<HttpProxyConnectJob>( |
| priority(), socket_tag(), common_connect_job_params(), |
| params_->GetHttpProxyConnectionParams(), this, &net_log()); |
| return nested_connect_job_->Connect(); |
| } |
| |
| int SSLConnectJob::DoTunnelConnectComplete(int result) { |
| resolve_error_info_ = nested_connect_job_->GetResolveErrorInfo(); |
| nested_socket_ = nested_connect_job_->PassSocket(); |
| |
| if (result < 0) { |
| // Extract the information needed to prompt for appropriate proxy |
| // authentication so that when ClientSocketPoolBaseHelper calls |
| // |GetAdditionalErrorState|, we can easily set the state. |
| if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { |
| ssl_cert_request_info_ = nested_connect_job_->GetCertRequestInfo(); |
| } |
| return result; |
| } |
| |
| next_state_ = STATE_SSL_CONNECT; |
| return result; |
| } |
| |
| int SSLConnectJob::DoSSLConnect() { |
| TRACE_EVENT0(NetTracingCategory(), "SSLConnectJob::DoSSLConnect"); |
| DCHECK(!TimerIsRunning()); |
| |
| next_state_ = STATE_SSL_CONNECT_COMPLETE; |
| |
| // Set the timeout to just the time allowed for the SSL handshake. |
| ResetTimer(kSSLHandshakeTimeout); |
| |
| // Get the transport's connect start and DNS times. |
| const LoadTimingInfo::ConnectTiming& socket_connect_timing = |
| nested_connect_job_->connect_timing(); |
| |
| // Overwriting |connect_start| serves two purposes - it adjusts timing so |
| // |connect_start| doesn't include dns times, and it adjusts the time so |
| // as not to include time spent waiting for an idle socket. |
| connect_timing_.connect_start = socket_connect_timing.connect_start; |
| connect_timing_.domain_lookup_start = |
| socket_connect_timing.domain_lookup_start; |
| connect_timing_.domain_lookup_end = socket_connect_timing.domain_lookup_end; |
| |
| ssl_negotiation_started_ = true; |
| connect_timing_.ssl_start = base::TimeTicks::Now(); |
| |
| // Save the `HostResolverEndpointResult`. `nested_connect_job_` is destroyed |
| // at the end of this function. |
| endpoint_result_ = nested_connect_job_->GetHostResolverEndpointResult(); |
| |
| SSLConfig ssl_config = params_->ssl_config(); |
| ssl_config.network_anonymization_key = params_->network_anonymization_key(); |
| ssl_config.privacy_mode = params_->privacy_mode(); |
| // We do the fallback in both cases here to ensure we separate the effect of |
| // disabling sha1 from the effect of having a single automatic retry |
| // on a potentially unreliably network connection. |
| ssl_config.disable_sha1_server_signatures = |
| disable_legacy_crypto_with_fallback_ || |
| !ssl_client_context()->config().InsecureHashesInTLSHandshakesEnabled(); |
| |
| if (ssl_client_context()->config().EncryptedClientHelloEnabled()) { |
| if (ech_retry_configs_) { |
| ssl_config.ech_config_list = *ech_retry_configs_; |
| } else if (endpoint_result_) { |
| ssl_config.ech_config_list = endpoint_result_->metadata.ech_config_list; |
| } |
| if (!ssl_config.ech_config_list.empty()) { |
| // Overriding the DNS lookup only works for direct connections. We |
| // currently do not support ECH with other connection types. |
| DCHECK_EQ(params_->GetConnectionType(), SSLSocketParams::DIRECT); |
| } |
| } |
| |
| ssl_socket_ = client_socket_factory()->CreateSSLClientSocket( |
| ssl_client_context(), std::move(nested_socket_), params_->host_and_port(), |
| ssl_config); |
| nested_connect_job_.reset(); |
| return ssl_socket_->Connect(callback_); |
| } |
| |
| int SSLConnectJob::DoSSLConnectComplete(int result) { |
| connect_timing_.ssl_end = base::TimeTicks::Now(); |
| |
| if (result != OK && !server_address_.address().empty()) { |
| connection_attempts_.push_back(ConnectionAttempt(server_address_, result)); |
| server_address_ = IPEndPoint(); |
| } |
| |
| // Many servers which negotiate SHA-1 server signatures in TLS 1.2 actually |
| // support SHA-2 but preferentially sign SHA-1 if available. |
| // |
| // To get more accurate metrics, initially connect with SHA-1 disabled. If |
| // this fails, retry with them enabled. This keeps the legacy algorithms |
| // working for now, but they will only appear in metrics and DevTools if the |
| // site relies on them. |
| // |
| // See https://crbug.com/658905. |
| if (disable_legacy_crypto_with_fallback_ && |
| (result == ERR_CONNECTION_CLOSED || result == ERR_CONNECTION_RESET || |
| result == ERR_SSL_PROTOCOL_ERROR || |
| result == ERR_SSL_VERSION_OR_CIPHER_MISMATCH)) { |
| ResetStateForRestart(); |
| disable_legacy_crypto_with_fallback_ = false; |
| next_state_ = GetInitialState(params_->GetConnectionType()); |
| return OK; |
| } |
| |
| // We record metrics based on whether the server advertised ECH support in |
| // DNS. This allows the metrics to measure the same set of servers in both |
| // control and experiment group. |
| const bool is_ech_capable = |
| endpoint_result_ && !endpoint_result_->metadata.ech_config_list.empty(); |
| |
| if (!ech_retry_configs_ && result == ERR_ECH_NOT_NEGOTIATED && |
| ssl_client_context()->config().EncryptedClientHelloEnabled()) { |
| // We used ECH, and the server could not decrypt the ClientHello. However, |
| // it was able to handshake with the public name and send authenticated |
| // retry configs. If this is not the first time around, retry the connection |
| // with the new ECHConfigList, or with ECH disabled (empty retry configs), |
| // as directed. |
| // |
| // See |
| // https://www.ietf.org/archive/id/draft-ietf-tls-esni-13.html#section-6.1.6 |
| DCHECK(is_ech_capable); |
| ech_retry_configs_ = ssl_socket_->GetECHRetryConfigs(); |
| net_log().AddEvent( |
| NetLogEventType::SSL_CONNECT_JOB_RESTART_WITH_ECH_CONFIG_LIST, [&] { |
| base::Value::Dict dict; |
| dict.Set("bytes", NetLogBinaryValue(*ech_retry_configs_)); |
| return dict; |
| }); |
| |
| // TODO(https://crbug.com/1091403): Add histograms for how often this |
| // happens. |
| ResetStateForRestart(); |
| next_state_ = GetInitialState(params_->GetConnectionType()); |
| return OK; |
| } |
| |
| const std::string& host = params_->host_and_port().host(); |
| if (is_ech_capable && |
| base::FeatureList::IsEnabled(features::kEncryptedClientHello)) { |
| // These values are persisted to logs. Entries should not be renumbered |
| // and numeric values should never be reused. |
| enum class ECHResult { |
| // The connection succeeded on the initial connection. |
| kSuccessInitial = 0, |
| // The connection failed on the initial connection, without providing |
| // retry configs. |
| kErrorInitial = 1, |
| // The connection succeeded after getting retry configs. |
| kSuccessRetry = 2, |
| // The connection failed after getting retry configs. |
| kErrorRetry = 3, |
| // The connection succeeded after getting a rollback signal. |
| kSuccessRollback = 4, |
| // The connection failed after getting a rollback signal. |
| kErrorRollback = 5, |
| kMaxValue = kErrorRollback, |
| }; |
| const bool is_ok = result == OK; |
| ECHResult ech_result; |
| if (!ech_retry_configs_.has_value()) { |
| ech_result = |
| is_ok ? ECHResult::kSuccessInitial : ECHResult::kErrorInitial; |
| } else if (ech_retry_configs_->empty()) { |
| ech_result = |
| is_ok ? ECHResult::kSuccessRollback : ECHResult::kErrorRollback; |
| } else { |
| ech_result = is_ok ? ECHResult::kSuccessRetry : ECHResult::kErrorRetry; |
| } |
| base::UmaHistogramEnumeration("Net.SSL.ECHResult", ech_result); |
| } |
| |
| if (result == OK) { |
| DCHECK(!connect_timing_.ssl_start.is_null()); |
| base::TimeDelta connect_duration = |
| connect_timing_.ssl_end - connect_timing_.ssl_start; |
| UMA_HISTOGRAM_CUSTOM_TIMES("Net.SSL_Connection_Latency_2", connect_duration, |
| base::Milliseconds(1), base::Minutes(1), 100); |
| if (is_ech_capable) { |
| UMA_HISTOGRAM_CUSTOM_TIMES("Net.SSL_Connection_Latency_ECH", |
| connect_duration, base::Milliseconds(1), |
| base::Minutes(1), 100); |
| } |
| |
| SSLInfo ssl_info; |
| bool has_ssl_info = ssl_socket_->GetSSLInfo(&ssl_info); |
| DCHECK(has_ssl_info); |
| |
| SSLVersion version = |
| SSLConnectionStatusToVersion(ssl_info.connection_status); |
| UMA_HISTOGRAM_ENUMERATION("Net.SSLVersion", version, |
| SSL_CONNECTION_VERSION_MAX); |
| if (IsGoogleHost(host)) { |
| // Google hosts all support TLS 1.2, so any occurrences of TLS 1.0 or TLS |
| // 1.1 will be from an outdated insecure TLS MITM proxy, such as some |
| // antivirus configurations. TLS 1.0 and 1.1 are deprecated, so record |
| // these to see how prevalent they are. See https://crbug.com/896013. |
| UMA_HISTOGRAM_ENUMERATION("Net.SSLVersionGoogle", version, |
| SSL_CONNECTION_VERSION_MAX); |
| } |
| |
| uint16_t cipher_suite = |
| SSLConnectionStatusToCipherSuite(ssl_info.connection_status); |
| base::UmaHistogramSparse("Net.SSL_CipherSuite", cipher_suite); |
| |
| if (ssl_info.key_exchange_group != 0) { |
| base::UmaHistogramSparse("Net.SSL_KeyExchange.ECDHE", |
| ssl_info.key_exchange_group); |
| } |
| |
| // Classify whether the connection required the legacy crypto fallback. |
| SSLLegacyCryptoFallback fallback = SSLLegacyCryptoFallback::kNoFallback; |
| if (!disable_legacy_crypto_with_fallback_) { |
| // Some servers, though they do not negotiate SHA-1, still fail the |
| // connection when SHA-1 is not offered. We believe these are servers |
| // which match the sent certificates against the ClientHello and then |
| // are configured with a SHA-1 certificate. |
| // |
| // SHA-1 certificate chains are no longer accepted, however servers may |
| // send extra unused certificates, most commonly a copy of the trust |
| // anchor. We only need to check for RSASSA-PKCS1-v1_5 signatures, because |
| // other SHA-1 signature types have already been removed from the |
| // ClientHello. |
| bool sent_sha1_cert = ssl_info.unverified_cert && |
| x509_util::HasRsaPkcs1Sha1Signature( |
| ssl_info.unverified_cert->cert_buffer()); |
| if (!sent_sha1_cert && ssl_info.unverified_cert) { |
| for (const auto& cert : |
| ssl_info.unverified_cert->intermediate_buffers()) { |
| if (x509_util::HasRsaPkcs1Sha1Signature(cert.get())) { |
| sent_sha1_cert = true; |
| break; |
| } |
| } |
| } |
| if (ssl_info.peer_signature_algorithm == SSL_SIGN_RSA_PKCS1_SHA1) { |
| fallback = sent_sha1_cert |
| ? SSLLegacyCryptoFallback::kSentSHA1CertAndUsedSHA1 |
| : SSLLegacyCryptoFallback::kUsedSHA1; |
| } else { |
| fallback = sent_sha1_cert ? SSLLegacyCryptoFallback::kSentSHA1Cert |
| : SSLLegacyCryptoFallback::kUnknownReason; |
| } |
| } |
| UMA_HISTOGRAM_ENUMERATION("Net.SSLLegacyCryptoFallback2", fallback); |
| } |
| |
| base::UmaHistogramSparse("Net.SSL_Connection_Error", std::abs(result)); |
| if (is_ech_capable) { |
| base::UmaHistogramSparse("Net.SSL_Connection_Error_ECH", std::abs(result)); |
| } |
| |
| if (result == OK || IsCertificateError(result)) { |
| SetSocket(std::move(ssl_socket_), std::move(dns_aliases_)); |
| } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { |
| ssl_cert_request_info_ = base::MakeRefCounted<SSLCertRequestInfo>(); |
| ssl_socket_->GetSSLCertRequestInfo(ssl_cert_request_info_.get()); |
| } |
| |
| return result; |
| } |
| |
| SSLConnectJob::State SSLConnectJob::GetInitialState( |
| SSLSocketParams::ConnectionType connection_type) { |
| switch (connection_type) { |
| case SSLSocketParams::DIRECT: |
| return STATE_TRANSPORT_CONNECT; |
| case SSLSocketParams::HTTP_PROXY: |
| return STATE_TUNNEL_CONNECT; |
| case SSLSocketParams::SOCKS_PROXY: |
| return STATE_SOCKS_CONNECT; |
| } |
| NOTREACHED(); |
| return STATE_NONE; |
| } |
| |
| int SSLConnectJob::ConnectInternal() { |
| next_state_ = GetInitialState(params_->GetConnectionType()); |
| return DoLoop(OK); |
| } |
| |
| void SSLConnectJob::ResetStateForRestart() { |
| ResetTimer(base::TimeDelta()); |
| nested_connect_job_ = nullptr; |
| nested_socket_ = nullptr; |
| ssl_socket_ = nullptr; |
| ssl_cert_request_info_ = nullptr; |
| ssl_negotiation_started_ = false; |
| resolve_error_info_ = ResolveErrorInfo(); |
| server_address_ = IPEndPoint(); |
| } |
| |
| void SSLConnectJob::ChangePriorityInternal(RequestPriority priority) { |
| if (nested_connect_job_) |
| nested_connect_job_->ChangePriority(priority); |
| } |
| |
| } // namespace net |