| 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();
 | 
|  }
 | 
|  
 | 
| 
 |