diff options
-rw-r--r-- | chrome/app/generated_resources.grd | 3 | ||||
-rw-r--r-- | chrome/browser/page_info_model.cc | 11 | ||||
-rw-r--r-- | net/base/connection_type_histograms.h | 5 | ||||
-rw-r--r-- | net/base/ssl_cipher_suite_names.cc | 26 | ||||
-rw-r--r-- | net/base/ssl_cipher_suite_names.h | 6 | ||||
-rw-r--r-- | net/base/ssl_connection_status_flags.h | 15 | ||||
-rw-r--r-- | net/base/ssl_info.h | 5 | ||||
-rw-r--r-- | net/http/http_response_info.cc | 1 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_nss.cc | 156 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_nss.h | 6 |
10 files changed, 175 insertions, 59 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 7a51308..a0a9d15 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -6029,6 +6029,9 @@ Keep your key file in a safe place. You will need it to create new versions of y <message name="IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_SENTENCE_LINK" desc="Linking 2 sentences in 1 paragraph."> <ph name="SENTENCE1">$1<ex>Your connection is encrypted.</ex></ph> <ph name="SENTENCE2">$2<ex>However, this page includes resources from other pages whose identity cannot be verified.</ex></ph> </message> + <message name="IDS_PAGE_INFO_SECURITY_TAB_SSL_VERSION" desc="This message gives the version of the SSL protocol used to protect the HTTPS connection."> + The connection uses <ph name="SSL_VERSION">$1<ex>TLS 1.0</ex></ph>. + </message> <message name="IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTION_DETAILS" desc="This message gives details of the cryptographic primitives used to protect the HTTPS connection."> The connection is encrypted using <ph name="CIPHER">$1<ex>AES_128</ex></ph>, with <ph name="MAC">$2<ex>SHA1</ex></ph> for message authentication and <ph name="KX">$3<ex>RSA</ex></ph> as the key exchange mechanism. </message> diff --git a/chrome/browser/page_info_model.cc b/chrome/browser/page_info_model.cc index 8b0d983..d0d7b74 100644 --- a/chrome/browser/page_info_model.cc +++ b/chrome/browser/page_info_model.cc @@ -195,6 +195,15 @@ PageInfoModel::PageInfoModel(Profile* profile, uint16 cipher_suite = net::SSLConnectionStatusToCipherSuite(ssl.connection_status()); if (ssl.security_bits() > 0 && cipher_suite) { + int ssl_version = + net::SSLConnectionStatusToVersion(ssl.connection_status()); + const char* ssl_version_str; + net::SSLVersionToString(&ssl_version_str, ssl_version); + description += ASCIIToUTF16("\n\n"); + description += l10n_util::GetStringFUTF16( + IDS_PAGE_INFO_SECURITY_TAB_SSL_VERSION, + ASCIIToUTF16(ssl_version_str)); + bool did_fallback = (ssl.connection_status() & net::SSL_CONNECTION_SSL3_FALLBACK) != 0; bool no_renegotiation = @@ -212,7 +221,7 @@ PageInfoModel::PageInfoModel(Profile* profile, uint8 compression_id = net::SSLConnectionStatusToCompression(ssl.connection_status()); if (compression_id) { - const char *compression; + const char* compression; net::SSLCompressionToString(&compression, compression_id); description += l10n_util::GetStringFUTF16( IDS_PAGE_INFO_SECURITY_TAB_COMPRESSION_DETAILS, diff --git a/net/base/connection_type_histograms.h b/net/base/connection_type_histograms.h index e6c2a59..e3e4a84 100644 --- a/net/base/connection_type_histograms.h +++ b/net/base/connection_type_histograms.h @@ -30,6 +30,11 @@ enum ConnectionType { // in the certificate chain (excluding root) CONNECTION_HTTP = 7, // An HTTP connection CONNECTION_SPDY = 8, // A SPDY connection + CONNECTION_SSL_SSL2 = 9, // An SSL connection that uses SSL 2.0 + CONNECTION_SSL_SSL3 = 10, // An SSL connection that uses SSL 3.0 + CONNECTION_SSL_TLS1 = 11, // An SSL connection that uses TLS 1.0 + CONNECTION_SSL_TLS1_1 = 12, // An SSL connection that uses TLS 1.1 + CONNECTION_SSL_TLS1_2 = 13, // An SSL connection that uses TLS 1.2 NUM_OF_CONNECTION_TYPES }; diff --git a/net/base/ssl_cipher_suite_names.cc b/net/base/ssl_cipher_suite_names.cc index 2db9a4b..39efd1c 100644 --- a/net/base/ssl_cipher_suite_names.cc +++ b/net/base/ssl_cipher_suite_names.cc @@ -6,6 +6,8 @@ #include <stdlib.h> +#include "base/logging.h" +#include "net/base/ssl_connection_status_flags.h" // Rather than storing the names of all the ciphersuites we eliminate the // redundancy and break each cipher suite into a key exchange method, cipher @@ -346,4 +348,28 @@ void SSLCompressionToString(const char** name, uint8 compresssion) { } } +void SSLVersionToString(const char** name, int ssl_version) { + switch (ssl_version) { + case SSL_CONNECTION_VERSION_SSL2: + *name = "SSL 2.0"; + break; + case SSL_CONNECTION_VERSION_SSL3: + *name = "SSL 3.0"; + break; + case SSL_CONNECTION_VERSION_TLS1: + *name = "TLS 1.0"; + break; + case SSL_CONNECTION_VERSION_TLS1_1: + *name = "TLS 1.1"; + break; + case SSL_CONNECTION_VERSION_TLS1_2: + *name = "TLS 1.2"; + break; + default: + NOTREACHED(); + *name = "???"; + break; + } +} + } // namespace net diff --git a/net/base/ssl_cipher_suite_names.h b/net/base/ssl_cipher_suite_names.h index cd61471..9241c1b 100644 --- a/net/base/ssl_cipher_suite_names.h +++ b/net/base/ssl_cipher_suite_names.h @@ -25,6 +25,12 @@ void SSLCipherSuiteToStrings(const char** key_exchange_str, // If the algorithm is unknown, |name| is set to "???". void SSLCompressionToString(const char** name, uint8 compression_method); +// SSLVersionToString returns the name of the SSL protocol version +// specified by |ssl_version|, which is defined in +// net/base/ssl_connection_status_flags.h. +// If the version is unknown, |name| is set to "???". +void SSLVersionToString(const char** name, int ssl_version); + } // namespace net #endif // NET_BASE_SSL_CIPHER_SUITE_NAMES_H_ diff --git a/net/base/ssl_connection_status_flags.h b/net/base/ssl_connection_status_flags.h index 1b7640c..51eb884 100644 --- a/net/base/ssl_connection_status_flags.h +++ b/net/base/ssl_connection_status_flags.h @@ -27,6 +27,16 @@ enum { // library that doesn't report it, like SChannel.) SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION = 1 << 19, + // The next three bits are reserved for the SSL version. + SSL_CONNECTION_VERSION_SHIFT = 20, + SSL_CONNECTION_VERSION_MASK = 7, + SSL_CONNECTION_VERSION_UNKNOWN = 0, // Unknown SSL version or SSL not used. + SSL_CONNECTION_VERSION_SSL2 = 1, + SSL_CONNECTION_VERSION_SSL3 = 2, + SSL_CONNECTION_VERSION_TLS1 = 3, + SSL_CONNECTION_VERSION_TLS1_1 = 4, + SSL_CONNECTION_VERSION_TLS1_2 = 5, + // 1 << 31 (the sign bit) is reserved so that the SSL connection status will // never be negative. }; @@ -41,6 +51,11 @@ inline int SSLConnectionStatusToCompression(int connection_status) { SSL_CONNECTION_COMPRESSION_MASK; } +inline int SSLConnectionStatusToVersion(int connection_status) { + return (connection_status >> SSL_CONNECTION_VERSION_SHIFT) & + SSL_CONNECTION_VERSION_MASK; +} + } // namespace net #endif // NET_BASE_SSL_CONNECTION_STATUS_FLAGS_H_ diff --git a/net/base/ssl_info.h b/net/base/ssl_info.h index 1786b58..4c68f06 100644 --- a/net/base/ssl_info.h +++ b/net/base/ssl_info.h @@ -42,9 +42,8 @@ class SSLInfo { int security_bits; // Information about the SSL connection itself. See - // ssl_connection_status_flags.h for values. The ciphersuite and compression - // in use are encoded within. - // TODO(agl): also encode the protocol version used. + // ssl_connection_status_flags.h for values. The protocol version, + // ciphersuite, and compression in use are encoded within. int connection_status; }; diff --git a/net/http/http_response_info.cc b/net/http/http_response_info.cc index dc90d1f..00bfb92 100644 --- a/net/http/http_response_info.cc +++ b/net/http/http_response_info.cc @@ -182,6 +182,7 @@ void HttpResponseInfo::Persist(Pickle* pickle, } if (ssl_info.security_bits != -1) flags |= RESPONSE_INFO_HAS_SECURITY_BITS; + // TODO(wtc): we should persist ssl_info.connection_status. if (vary_data.is_valid()) flags |= RESPONSE_INFO_HAS_VARY_DATA; if (response_truncated) diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc index 6d3f54f..f62c7b1 100644 --- a/net/socket/ssl_client_socket_nss.cc +++ b/net/socket/ssl_client_socket_nss.cc @@ -60,6 +60,7 @@ #include <sechash.h> #include <ssl.h> #include <sslerr.h> +#include <sslproto.h> #include <limits> @@ -76,6 +77,7 @@ #include "net/base/address_list.h" #include "net/base/cert_status_flags.h" #include "net/base/cert_verifier.h" +#include "net/base/connection_type_histograms.h" #include "net/base/dns_util.h" #include "net/base/dnsrr_resolver.h" #include "net/base/dnssec_chain_verifier.h" @@ -381,6 +383,7 @@ SSLClientSocketNSS::SSLClientSocketNSS(ClientSocketHandle* transport_socket, user_read_buf_len_(0), user_write_buf_len_(0), server_cert_nss_(NULL), + ssl_connection_status_(0), client_auth_cert_needed_(false), handshake_callback_called_(false), completed_handshake_(false), @@ -867,6 +870,7 @@ void SSLClientSocketNSS::Disconnect() { server_cert_nss_ = NULL; } server_cert_verify_result_.Reset(); + ssl_connection_status_ = 0; completed_handshake_ = false; pseudo_connected_ = false; eset_mitm_detected_ = false; @@ -1034,11 +1038,10 @@ bool SSLClientSocketNSS::SetSendBufferSize(int32 size) { return transport_->socket()->SetSendBufferSize(size); } - +// Sets server_cert_ and server_cert_nss_ if not yet set. +// Returns server_cert_. X509Certificate *SSLClientSocketNSS::UpdateServerCert() { - // We set the server_cert_ from HandshakeCallback(), but this handler - // does not necessarily get called if we are continuing a cached SSL - // session. + // We set the server_cert_ from HandshakeCallback(). if (server_cert_ == NULL) { server_cert_nss_ = SSL_PeerCertificate(nss_fd_); if (server_cert_nss_) { @@ -1069,21 +1072,61 @@ X509Certificate *SSLClientSocketNSS::UpdateServerCert() { return server_cert_; } -// Log an informational message if the server does not support secure -// renegotiation (RFC 5746). -void SSLClientSocketNSS::CheckSecureRenegotiation() const { +// Sets ssl_connection_status_. +void SSLClientSocketNSS::UpdateConnectionStatus() { + SSLChannelInfo channel_info; + SECStatus ok = SSL_GetChannelInfo(nss_fd_, + &channel_info, sizeof(channel_info)); + if (ok == SECSuccess && + channel_info.length == sizeof(channel_info) && + channel_info.cipherSuite) { + ssl_connection_status_ |= + (static_cast<int>(channel_info.cipherSuite) & + SSL_CONNECTION_CIPHERSUITE_MASK) << + SSL_CONNECTION_CIPHERSUITE_SHIFT; + + ssl_connection_status_ |= + (static_cast<int>(channel_info.compressionMethod) & + SSL_CONNECTION_COMPRESSION_MASK) << + SSL_CONNECTION_COMPRESSION_SHIFT; + + int version = SSL_CONNECTION_VERSION_UNKNOWN; + if (channel_info.protocolVersion < SSL_LIBRARY_VERSION_3_0) { + // All versions less than SSL_LIBRARY_VERSION_3_0 are treated as SSL + // version 2. + version = SSL_CONNECTION_VERSION_SSL2; + } else if (channel_info.protocolVersion == SSL_LIBRARY_VERSION_3_0) { + version = SSL_CONNECTION_VERSION_SSL3; + } else if (channel_info.protocolVersion == SSL_LIBRARY_VERSION_3_1_TLS) { + version = SSL_CONNECTION_VERSION_TLS1; + } + ssl_connection_status_ |= + (version & SSL_CONNECTION_VERSION_MASK) << + SSL_CONNECTION_VERSION_SHIFT; + } + // SSL_HandshakeNegotiatedExtension was added in NSS 3.12.6. // Since SSL_MAX_EXTENSIONS was added at the same time, we can test // SSL_MAX_EXTENSIONS for the presence of SSL_HandshakeNegotiatedExtension. #if defined(SSL_MAX_EXTENSIONS) - PRBool received_renego_info; - if (SSL_HandshakeNegotiatedExtension(nss_fd_, ssl_renegotiation_info_xtn, - &received_renego_info) == SECSuccess && - !received_renego_info) { - VLOG(1) << "The server " << hostname_ - << " does not support the TLS renegotiation_info extension."; + PRBool peer_supports_renego_ext; + ok = SSL_HandshakeNegotiatedExtension(nss_fd_, ssl_renegotiation_info_xtn, + &peer_supports_renego_ext); + if (ok == SECSuccess) { + if (!peer_supports_renego_ext) { + ssl_connection_status_ |= SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION; + // Log an informational message if the server does not support secure + // renegotiation (RFC 5746). + VLOG(1) << "The server " << hostname_ + << " does not support the TLS renegotiation_info extension."; + } + UMA_HISTOGRAM_ENUMERATION("Net.RenegotiationExtensionSupported", + peer_supports_renego_ext, 2); } #endif + + if (ssl_config_.ssl3_fallback) + ssl_connection_status_ |= SSL_CONNECTION_SSL3_FALLBACK; } void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) { @@ -1095,50 +1138,23 @@ void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) { return; } - SSLChannelInfo channel_info; - SECStatus ok = SSL_GetChannelInfo(nss_fd_, - &channel_info, sizeof(channel_info)); - if (ok == SECSuccess && - channel_info.length == sizeof(channel_info) && - channel_info.cipherSuite) { - SSLCipherSuiteInfo cipher_info; - ok = SSL_GetCipherSuiteInfo(channel_info.cipherSuite, - &cipher_info, sizeof(cipher_info)); - if (ok == SECSuccess) { - ssl_info->security_bits = cipher_info.effectiveKeyBits; - } else { - ssl_info->security_bits = -1; - LOG(DFATAL) << "SSL_GetCipherSuiteInfo returned " << PR_GetError() - << " for cipherSuite " << channel_info.cipherSuite; - } - ssl_info->connection_status |= - (((int)channel_info.cipherSuite) & SSL_CONNECTION_CIPHERSUITE_MASK) << - SSL_CONNECTION_CIPHERSUITE_SHIFT; - - ssl_info->connection_status |= - (((int)channel_info.compressionMethod) & - SSL_CONNECTION_COMPRESSION_MASK) << - SSL_CONNECTION_COMPRESSION_SHIFT; - - UpdateServerCert(); - } ssl_info->cert_status = server_cert_verify_result_.cert_status; DCHECK(server_cert_ != NULL); ssl_info->cert = server_cert_; + ssl_info->connection_status = ssl_connection_status_; - PRBool peer_supports_renego_ext; - ok = SSL_HandshakeNegotiatedExtension(nss_fd_, ssl_renegotiation_info_xtn, - &peer_supports_renego_ext); + PRUint16 cipher_suite = + SSLConnectionStatusToCipherSuite(ssl_connection_status_); + SSLCipherSuiteInfo cipher_info; + SECStatus ok = SSL_GetCipherSuiteInfo(cipher_suite, + &cipher_info, sizeof(cipher_info)); if (ok == SECSuccess) { - if (!peer_supports_renego_ext) - ssl_info->connection_status |= SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION; - UMA_HISTOGRAM_ENUMERATION("Net.RenegotiationExtensionSupported", - (int)peer_supports_renego_ext, 2); + ssl_info->security_bits = cipher_info.effectiveKeyBits; + } else { + ssl_info->security_bits = -1; + LOG(DFATAL) << "SSL_GetCipherSuiteInfo returned " << PR_GetError() + << " for cipherSuite " << cipher_suite; } - - if (ssl_config_.ssl3_fallback) - ssl_info->connection_status |= SSL_CONNECTION_SSL3_FALLBACK; - LeaveFunction(""); } @@ -1798,8 +1814,7 @@ void SSLClientSocketNSS::HandshakeCallback(PRFileDesc* socket, that->handshake_callback_called_ = true; that->UpdateServerCert(); - - that->CheckSecureRenegotiation(); + that->UpdateConnectionStatus(); } int SSLClientSocketNSS::DoSnapStartLoadInfo() { @@ -2218,6 +2233,9 @@ int SSLClientSocketNSS::DoVerifyCertComplete(int result) { result = OK; } + if (result == OK) + LogConnectionTypeMetrics(); + completed_handshake_ = true; // TODO(ukai): we may not need this call because it is now harmless to have a // session with a bad cert. @@ -2286,4 +2304,36 @@ int SSLClientSocketNSS::DoPayloadWrite() { return rv; } +void SSLClientSocketNSS::LogConnectionTypeMetrics() const { + UpdateConnectionTypeHistograms(CONNECTION_SSL); + if (server_cert_verify_result_.has_md5) + UpdateConnectionTypeHistograms(CONNECTION_SSL_MD5); + if (server_cert_verify_result_.has_md2) + UpdateConnectionTypeHistograms(CONNECTION_SSL_MD2); + if (server_cert_verify_result_.has_md4) + UpdateConnectionTypeHistograms(CONNECTION_SSL_MD4); + if (server_cert_verify_result_.has_md5_ca) + UpdateConnectionTypeHistograms(CONNECTION_SSL_MD5_CA); + if (server_cert_verify_result_.has_md2_ca) + UpdateConnectionTypeHistograms(CONNECTION_SSL_MD2_CA); + int ssl_version = SSLConnectionStatusToVersion(ssl_connection_status_); + switch (ssl_version) { + case SSL_CONNECTION_VERSION_SSL2: + UpdateConnectionTypeHistograms(CONNECTION_SSL_SSL2); + break; + case SSL_CONNECTION_VERSION_SSL3: + UpdateConnectionTypeHistograms(CONNECTION_SSL_SSL3); + break; + case SSL_CONNECTION_VERSION_TLS1: + UpdateConnectionTypeHistograms(CONNECTION_SSL_TLS1); + break; + case SSL_CONNECTION_VERSION_TLS1_1: + UpdateConnectionTypeHistograms(CONNECTION_SSL_TLS1_1); + break; + case SSL_CONNECTION_VERSION_TLS1_2: + UpdateConnectionTypeHistograms(CONNECTION_SSL_TLS1_2); + break; + }; +} + } // namespace net diff --git a/net/socket/ssl_client_socket_nss.h b/net/socket/ssl_client_socket_nss.h index ce5fef8..a0c79fb 100644 --- a/net/socket/ssl_client_socket_nss.h +++ b/net/socket/ssl_client_socket_nss.h @@ -50,7 +50,7 @@ class SSLClientSocketNSS : public SSLClientSocket { virtual void GetSSLInfo(SSLInfo* ssl_info); virtual void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info); virtual NextProtoStatus GetNextProto(std::string* proto); - virtual void UseDNSSEC(DNSSECProvider*); + virtual void UseDNSSEC(DNSSECProvider* provider); // ClientSocket methods: virtual int Connect(CompletionCallback* callback); @@ -83,7 +83,7 @@ class SSLClientSocketNSS : public SSLClientSocket { static X509Certificate::OSCertHandle CreateOSCert(const SECItem& der_cert); #endif X509Certificate* UpdateServerCert(); - void CheckSecureRenegotiation() const; + void UpdateConnectionStatus(); void DoReadCallback(int result); void DoWriteCallback(int result); void DoConnectCallback(int result); @@ -105,6 +105,7 @@ class SSLClientSocketNSS : public SSLClientSocket { int DoVerifyCertComplete(int result); int DoPayloadRead(); int DoPayloadWrite(); + void LogConnectionTypeMetrics() const; int Init(); void SaveSnapStartInfo(); bool LoadSnapStartInfo(); @@ -166,6 +167,7 @@ class SSLClientSocketNSS : public SSLClientSocket { scoped_refptr<X509Certificate> server_cert_; CERTCertificate* server_cert_nss_; CertVerifyResult server_cert_verify_result_; + int ssl_connection_status_; // Stores client authentication information between ClientAuthHandler and // GetSSLCertRequestInfo calls. |