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 384fa9d3bf2e6ae14d608e61ec71d680e01ea027..f4718aff4b02c1262f58120c354986ea374c5844 100644 |
--- a/net/socket/ssl_client_socket_nss.cc |
+++ b/net/socket/ssl_client_socket_nss.cc |
@@ -75,6 +75,7 @@ |
#include "base/string_util.h" |
#include "base/stringprintf.h" |
#include "base/threading/thread_restrictions.h" |
+#include "base/utf_string_conversions.h" |
#include "base/values.h" |
#include "net/base/address_list.h" |
#include "net/base/cert_status_flags.h" |
@@ -469,6 +470,7 @@ SSLClientSocketNSS::SSLClientSocketNSS(ClientSocketHandle* transport_socket, |
completed_handshake_(false), |
pseudo_connected_(false), |
eset_mitm_detected_(false), |
+ server_cert_needed_(true), |
predicted_cert_chain_correct_(false), |
peername_initialized_(false), |
dnssec_provider_(NULL), |
@@ -498,15 +500,21 @@ void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) { |
EnterFunction(""); |
ssl_info->Reset(); |
- if (!server_cert_) { |
- LOG(DFATAL) << "!server_cert_"; |
+ if (!server_cert_ && server_cert_needed_) { |
+ LOG(DFATAL) << "!server_cert_ && server_cert_needed_"; |
return; |
} |
- 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_; |
+ if (server_cert_verify_result_) { |
+ ssl_info->cert_status = server_cert_verify_result_->cert_status; |
+ } |
+ if (server_cert_ != NULL) { |
+ ssl_info->cert = server_cert_; |
+ } |
+ if (ssl_config_.use_tls_auth && !authenticated_tls_username_.empty()) { |
+ ssl_info->tls_username = UTF8ToUTF16(authenticated_tls_username_); |
+ } |
PRUint16 cipher_suite = |
SSLConnectionStatusToCipherSuite(ssl_connection_status_); |
@@ -911,14 +919,6 @@ int SSLClientSocketNSS::InitializeSSLOptions() { |
return ERR_UNEXPECTED; |
} |
- for (std::vector<uint16>::const_iterator it = |
- ssl_config_.disabled_cipher_suites.begin(); |
- it != ssl_config_.disabled_cipher_suites.end(); ++it) { |
- // This will fail if the specified cipher is not implemented by NSS, but |
- // the failure is harmless. |
- SSL_CipherPrefSet(nss_fd_, *it, PR_FALSE); |
- } |
- |
#ifdef SSL_ENABLE_SESSION_TICKETS |
// Support RFC 5077 |
rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SESSION_TICKETS, PR_TRUE); |
@@ -1029,6 +1029,43 @@ int SSLClientSocketNSS::InitializeSSLOptions() { |
return ERR_UNEXPECTED; |
} |
+ if (ssl_config_.use_tls_auth) { |
+ // Enable SRP ciphers, and disable non-SRP ciphers if we *require* TLS auth. |
+ rv = SetCiphersForTLSAuth(true, ssl_config_.require_tls_auth); |
+ if (rv != OK) |
+ return rv; |
+ |
+ if (!ssl_config_.tls_username.empty() && |
+ !ssl_config_.tls_password.empty()) { |
+ LOG(WARNING) << "Using TLS-SRP as " << |
+ ssl_config_.tls_username << " / " << ssl_config_.tls_password; |
+ |
+ rv = SSL_SetUserLogin(nss_fd_, |
+ ssl_config_.tls_username.c_str(), |
+ ssl_config_.tls_password.c_str()); |
+ if (rv != SECSuccess) { |
+ LogFailedNSSFunction(net_log_, "SSL_SetUserLogin", ""); |
+ return ERR_UNEXPECTED; |
+ } |
+ } else |
+ LOG(INFO) << "Using TLS-SRP with no username/password"; |
+ } else { |
+ // Disable SRP ciphers. |
+ // TODO(sqs): this disable step should only be called if the SRP cipher |
+ // suites are on by default. |
+ rv = SetCiphersForTLSAuth(false, false); |
+ if (rv != OK) |
+ return rv; |
+ } |
+ |
+ for (std::vector<uint16>::const_iterator it = |
+ ssl_config_.disabled_cipher_suites.begin(); |
+ it != ssl_config_.disabled_cipher_suites.end(); ++it) { |
+ // This will fail if the specified cipher is not implemented by NSS, but |
+ // the failure is harmless. |
+ SSL_CipherPrefSet(nss_fd_, *it, PR_FALSE); |
+ } |
+ |
rv = SSL_HandshakeCallback(nss_fd_, HandshakeCallback, this); |
if (rv != SECSuccess) { |
LogFailedNSSFunction(net_log_, "SSL_HandshakeCallback", ""); |
@@ -1044,6 +1081,35 @@ int SSLClientSocketNSS::InitializeSSLOptions() { |
return OK; |
} |
+// Enables SRP ciphers if |set_srp_ciphers| is true; otherwise, disables SRP |
+// ciphers. If |disable_non_srp_ciphers| is true, then disable non-SRP ciphers; |
+// otherwise, leave them alone. |
+int SSLClientSocketNSS::SetCiphersForTLSAuth(bool set_srp_ciphers, |
+ bool disable_non_srp_ciphers) { |
+ int rv; |
+ const PRUint16 *cipherSuites = SSL_GetImplementedCiphers(); |
+ int numCiphers = SSL_GetNumImplementedCiphers(); |
+ SSLCipherSuiteInfo info; |
+ for (int i = 0; i < numCiphers; i++) { |
+ PRUint16 suite = cipherSuites[i]; |
+ rv = SSL_GetCipherSuiteInfo(suite, &info, sizeof(info)); |
+ if (rv != SECSuccess) { |
+ LogFailedNSSFunction(net_log_, "SSL_GetCipherSuiteInfo", ""); |
+ return ERR_UNEXPECTED; |
+ } |
+ if (IsNSSCipherKEATypeSRP(info.keaType)) { |
+ rv = SSL_CipherPrefSet(nss_fd_, suite, set_srp_ciphers); |
+ } else if (disable_non_srp_ciphers) { |
+ rv = SSL_CipherPrefSet(nss_fd_, suite, PR_FALSE); |
+ } |
+ if (rv != SECSuccess) { |
+ LogFailedNSSFunction(net_log_, "SSL_CipherPrefSet", ""); |
+ return ERR_UNEXPECTED; |
+ } |
+ } |
+ return OK; |
+} |
+ |
int SSLClientSocketNSS::InitializeSSLPeerName() { |
// Tell NSS who we're connected to |
AddressList peer_address; |
@@ -1096,6 +1162,43 @@ X509Certificate *SSLClientSocketNSS::UpdateServerCert() { |
return server_cert_; |
} |
+// Sets tls_username_ and server_cert_needed_. |
+void SSLClientSocketNSS::UpdateAuth() { |
+ if (ssl_config_.use_tls_auth) { |
+ // Get the username that we actually authenticated with. |
+ SECItem user; |
+ SECStatus rv = SSL_GetChannelUsername(nss_fd_, &user); |
+ if (rv != SECSuccess) { |
+ LogFailedNSSFunction(net_log_, "SSL_GetChannelUsername", ""); |
+ } else { |
+ authenticated_tls_username_.assign(const_cast<char *>((char *)user.data), |
+ (size_t)user.len); |
+ SECITEM_FreeItem(&user, PR_FALSE); |
+ } |
+ DCHECK(authenticated_tls_username_ == ssl_config_.tls_username); |
+ |
+ // See whether this SRP cipher suite uses certs. |
+ SSLChannelInfo channel_info; |
+ SSLCipherSuiteInfo info; |
+ rv = SSL_GetChannelInfo(nss_fd_, &channel_info, sizeof(channel_info)); |
+ if (rv != SECSuccess) { |
+ LogFailedNSSFunction(net_log_, "SSL_GetChannelInfo", ""); |
+ return; |
+ } |
+ if (!channel_info.cipherSuite) { |
+ LOG(WARNING) << "Couldn't get SSL channel cipher suite"; |
+ return; |
+ } |
+ rv = SSL_GetCipherSuiteInfo(channel_info.cipherSuite, &info, sizeof(info)); |
+ if (rv != SECSuccess) { |
+ LogFailedNSSFunction(net_log_, "SSL_GetCipherSuiteInfo", ""); |
+ return; |
+ } |
+ bool cipher_is_srp_no_certs = (info.keaType == ssl_kea_srp); |
+ server_cert_needed_ = !cipher_is_srp_no_certs; |
+ } |
+} |
+ |
// Sets ssl_connection_status_. |
void SSLClientSocketNSS::UpdateConnectionStatus() { |
SSLChannelInfo channel_info; |
@@ -1449,6 +1552,8 @@ int SSLClientSocketNSS::DoSnapStartWaitForWrite() { |
return OK; |
} |
+// TODO(sqs): this hangs if the server returns an unknown_psk_identity alert |
+// that is not fatal (that is a warning) |
int SSLClientSocketNSS::DoHandshake() { |
EnterFunction(""); |
int net_error = net::OK; |
@@ -1640,6 +1745,13 @@ int SSLClientSocketNSS::DoVerifyDNSSECComplete(int result) { |
} |
int SSLClientSocketNSS::DoVerifyCert(int result) { |
+ if (!server_cert_needed_) { |
+ DCHECK(ssl_config_.use_tls_auth); |
+ DCHECK(!ssl_config_.tls_username.empty()); |
+ GotoState(STATE_VERIFY_CERT_COMPLETE); |
+ return OK; |
+ } |
+ |
DCHECK(server_cert_); |
GotoState(STATE_VERIFY_CERT_COMPLETE); |
@@ -1809,16 +1921,20 @@ int SSLClientSocketNSS::DoPayloadWrite() { |
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); |
+ |
+ if (server_cert_verify_result_) { |
+ 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: |
@@ -1837,6 +1953,9 @@ void SSLClientSocketNSS::LogConnectionTypeMetrics() const { |
UpdateConnectionTypeHistograms(CONNECTION_SSL_TLS1_2); |
break; |
}; |
+ |
+ if (!authenticated_tls_username_.empty()) |
+ UpdateConnectionTypeHistograms(CONNECTION_TLS_PASSWORD_AUTH); |
} |
// SaveSnapStartInfo extracts the information needed to perform a Snap Start |
@@ -2495,6 +2614,7 @@ void SSLClientSocketNSS::HandshakeCallback(PRFileDesc* socket, |
that->handshake_callback_called_ = true; |
that->UpdateServerCert(); |
+ that->UpdateAuth(); |
that->UpdateConnectionStatus(); |
} |