Index: net/socket/ssl_client_socket_nss.cc |
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc |
index 4397ad65e82c5371d265e0014ffc89a1510f334d..16d09aaae4675bb0aac65f0dca7c44b8de50ddb0 100644 |
--- a/net/socket/ssl_client_socket_nss.cc |
+++ b/net/socket/ssl_client_socket_nss.cc |
@@ -3473,6 +3473,40 @@ int SSLClientSocketNSS::DoVerifyCertComplete(int result) { |
} |
#endif |
+ // Persist the negotiated SSL version if it is newer than the minimum |
+ // version. This is used to prevent SSLv3 fallback for domains known |
+ // to support TLS. |
+ |
+ // Note: this is effective only for non-preloaded HSTS hosts. |
+ |
+ // TODO(thaidn): determine if the negotiated version is authenticated |
+ // at this point. Chrome must not use it if it isn't; otherwise MITM |
+ // attackers can perform a permanent DoS attack against users by setting |
+ // the version to TLSv1.2 for non-TLSv1.2 servers. |
+ if (transport_security_state_ && |
+ result == OK) { |
+ bool sni_available = |
+ ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1 || |
+ ssl_config_.version_fallback; |
+ const std::string& host = host_and_port_.host(); |
+ TransportSecurityState::DomainState domain_state; |
+ |
+ if (transport_security_state_->GetDomainState(host, sni_available, |
+ &domain_state)) { |
+ // Update the minimum version of existing |domain_state| if necessary. |
+ int ssl_version = SSLConnectionStatusToVersion( |
+ core_->state().ssl_connection_status); |
+ if (ssl_version > SSL_CONNECTION_VERSION_UNKNOWN && |
+ ssl_version < SSL_CONNECTION_VERSION_MAX && |
+ ssl_version > domain_state.ssl_version_min) { |
+ domain_state.ssl_version_min = |
+ static_cast<SSL_CONNECTION_VERSION>(ssl_version); |
+ } |
+ } |
+ // Note: the default value of |domain_state.ssl_version_min| is SSLv3. |
+ transport_security_state_->EnableHost(host, domain_state); |
+ } |
+ |
// Exit DoHandshakeLoop and return the result to the caller to Connect. |
DCHECK_EQ(STATE_NONE, next_handshake_state_); |
return result; |