| Index: net/http/http_network_transaction.cc
|
| diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
|
| index e0967c60da61f7b86887f153e4b46433bc05d65f..847a59146d81e4660de7d699c0038938620197b2 100644
|
| --- a/net/http/http_network_transaction.cc
|
| +++ b/net/http/http_network_transaction.cc
|
| @@ -17,6 +17,7 @@
|
| #include "base/string_number_conversions.h"
|
| #include "base/string_util.h"
|
| #include "base/stringprintf.h"
|
| +#include "base/utf_string_conversions.h"
|
| #include "build/build_config.h"
|
| #include "googleurl/src/gurl.h"
|
| #include "net/base/auth.h"
|
| @@ -197,6 +198,49 @@ int HttpNetworkTransaction::RestartWithCertificate(
|
| return rv;
|
| }
|
|
|
| +void HttpNetworkTransaction::SetTLSLoginAuthData(AuthData* auth_data) {
|
| + DCHECK(!stream_request_.get()) << "Can't set TLS login on existing stream";
|
| + DCHECK(!stream_.get());
|
| + DCHECK_EQ(STATE_NONE, next_state_);
|
| +
|
| + DCHECK(!auth_data->username.empty());
|
| + DCHECK(!auth_data->password.empty());
|
| +
|
| + tls_auth_data_ = auth_data;
|
| + ssl_config_.tls_username = UTF16ToUTF8(auth_data->username);
|
| + ssl_config_.tls_password = UTF16ToUTF8(auth_data->password);
|
| + ssl_config_.use_tls_auth = true;
|
| + ssl_config_.ssl3_enabled = false;
|
| + ssl_config_.tls1_enabled = true;
|
| +}
|
| +
|
| +int HttpNetworkTransaction::RestartWithTLSLogin(CompletionCallback* callback) {
|
| + DCHECK(!stream_request_.get());
|
| + DCHECK(!stream_.get());
|
| + DCHECK_EQ(STATE_NONE, next_state_);
|
| +
|
| + DCHECK(!ssl_config_.tls_username.empty());
|
| + DCHECK(!ssl_config_.tls_password.empty());
|
| + DCHECK(ssl_config_.use_tls_auth);
|
| + DCHECK(ssl_config_.tls1_enabled);
|
| + DCHECK(!ssl_config_.ssl3_enabled);
|
| +
|
| + DCHECK(response_.login_request_info.get());
|
| + DCHECK(tls_auth_data_.get());
|
| + session_->tls_client_login_cache()->Add(
|
| + WideToUTF8(response_.login_request_info->host_and_port),
|
| + tls_auth_data_);
|
| +
|
| + // Reset the other member variables.
|
| + // Note: this is necessary only with SSL renegotiation.
|
| + ResetStateForRestart();
|
| + next_state_ = STATE_CREATE_STREAM;
|
| + int rv = DoLoop(OK);
|
| + if (rv == ERR_IO_PENDING)
|
| + user_callback_ = callback;
|
| + return rv;
|
| +}
|
| +
|
| int HttpNetworkTransaction::RestartWithAuth(
|
| const string16& username,
|
| const string16& password,
|
| @@ -330,7 +374,9 @@ int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len,
|
|
|
| const HttpResponseInfo* HttpNetworkTransaction::GetResponseInfo() const {
|
| return ((headers_valid_ && response_.headers) || response_.ssl_info.cert ||
|
| - response_.cert_request_info) ? &response_ : NULL;
|
| + !response_.ssl_info.tls_username.empty() ||
|
| + response_.cert_request_info || response_.login_request_info) ?
|
| + &response_ : NULL;
|
| }
|
|
|
| LoadState HttpNetworkTransaction::GetLoadState() const {
|
| @@ -425,6 +471,14 @@ void HttpNetworkTransaction::OnNeedsClientAuth(
|
| OnIOComplete(ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
|
| }
|
|
|
| +void HttpNetworkTransaction::OnNeedsTLSLogin(
|
| + AuthChallengeInfo* login_info) {
|
| + DCHECK_EQ(STATE_CREATE_STREAM_COMPLETE, next_state_);
|
| +
|
| + response_.login_request_info = login_info;
|
| + OnIOComplete(ERR_TLS_CLIENT_LOGIN_NEEDED);
|
| +}
|
| +
|
| void HttpNetworkTransaction::OnHttpsProxyTunnelResponse(
|
| const HttpResponseInfo& response_info,
|
| HttpStream* stream) {
|
| @@ -542,6 +596,33 @@ int HttpNetworkTransaction::DoLoop(int result) {
|
| }
|
|
|
| int HttpNetworkTransaction::DoCreateStream() {
|
| + DCHECK(request_);
|
| +
|
| + // Set TLS login, if available.
|
| + if (ssl_config_.use_tls_auth) {
|
| + // TODO(sqs): implement some way of forcing TLS auth (as right below)
|
| + // if (...) ssl_config_.require_tls_auth = true;
|
| + if (ssl_config_.tls_username.empty()) {
|
| + scoped_refptr<AuthData> tls_auth_data;
|
| + bool found_login = session_->tls_client_login_cache()->Lookup(
|
| + net::GetHostAndPort(request_->url),
|
| + &tls_auth_data);
|
| + if (found_login) {
|
| + LOG(INFO) << "Got TLS login from cache";
|
| + SetTLSLoginAuthData(tls_auth_data);
|
| + DCHECK(!ssl_config_.tls_username.empty());
|
| + DCHECK(!ssl_config_.tls_password.empty());
|
| + } else if (ssl_config_.require_tls_auth) {
|
| + response_.login_request_info = new AuthChallengeInfo;
|
| + response_.login_request_info->host_and_port =
|
| + UTF8ToWide(net::GetHostAndPort(request_->url));
|
| + response_.login_request_info->scheme = ASCIIToWide(net::kTLSSRPScheme);
|
| + response_.login_request_info->over_protocol = AUTH_OVER_TLS;
|
| + return ERR_TLS_CLIENT_LOGIN_NEEDED;
|
| + }
|
| + }
|
| + }
|
| +
|
| next_state_ = STATE_CREATE_STREAM_COMPLETE;
|
|
|
| stream_request_.reset(
|
| @@ -1037,16 +1118,56 @@ int HttpNetworkTransaction::HandleSSLHandshakeError(int error) {
|
| GetHostAndPort(request_->url));
|
| }
|
|
|
| + // Check for TLS-SRP-related errors.
|
| + if (ssl_config_.use_tls_auth) {
|
| + if (ssl_config_.tls_username.empty() && ssl_config_.tls_password.empty()) {
|
| + if (error == ERR_SSL_UNKNOWN_PSK_IDENTITY_ALERT) {
|
| + // RFC 5054 unknown_psk_identity idiom: The Client Hello contained no
|
| + // SRP username, but the server wishes to connect using an SRP cipher
|
| + // suite.
|
| + error = ERR_TLS_CLIENT_LOGIN_NEEDED;
|
| + }
|
| + } else if (!ssl_config_.tls_username.empty() &&
|
| + ssl_config_.tls_password.empty()) {
|
| + LOG(WARNING) << "SSL handshake error: empty TLS password";
|
| + } else if (!ssl_config_.tls_username.empty() &&
|
| + !ssl_config_.tls_password.empty()) {
|
| + if (error == ERR_SSL_BAD_RECORD_MAC_ALERT ||
|
| + error == ERR_SSL_UNKNOWN_PSK_IDENTITY_ALERT) {
|
| + // The TLS-SRP login probably failed.
|
| + // TODO(sqs): also verify that we did attempt to use an SRP cipher suite
|
| + error = ERR_TLS_CLIENT_LOGIN_FAILED;
|
| + }
|
| + }
|
| +
|
| + if (error == ERR_TLS_CLIENT_LOGIN_FAILED)
|
| + session_->tls_client_login_cache()->Remove(GetHostAndPort(request_->url));
|
| +
|
| + // Handle TLS-SRP errors now.
|
| + if (error == ERR_TLS_CLIENT_LOGIN_FAILED ||
|
| + error == ERR_TLS_CLIENT_LOGIN_NEEDED) {
|
| + DCHECK(!response_.login_request_info.get());
|
| + response_.login_request_info = new AuthChallengeInfo;
|
| + response_.login_request_info->host_and_port =
|
| + UTF8ToWide(GetHostAndPort(request_->url));
|
| + response_.login_request_info->scheme = ASCIIToWide(net::kTLSSRPScheme);
|
| + response_.login_request_info->over_protocol = AUTH_OVER_TLS;
|
| + return error;
|
| + }
|
| + }
|
| +
|
| switch (error) {
|
| case ERR_SSL_PROTOCOL_ERROR:
|
| case ERR_SSL_VERSION_OR_CIPHER_MISMATCH:
|
| case ERR_SSL_DECOMPRESSION_FAILURE_ALERT:
|
| case ERR_SSL_BAD_RECORD_MAC_ALERT:
|
| if (ssl_config_.tls1_enabled &&
|
| - !SSLConfigService::IsKnownStrictTLSServer(request_->url.host())) {
|
| + !SSLConfigService::IsKnownStrictTLSServer(request_->url.host()) &&
|
| + !ssl_config_.require_tls_auth) {
|
| // This could be a TLS-intolerant server, an SSL 3.0 server that
|
| // chose a TLS-only cipher suite or a server with buggy DEFLATE
|
| // support. Turn off TLS 1.0, DEFLATE support and retry.
|
| + LOG(WARNING) << "Set server TLS intolerant: " << request_->url;
|
| session_->http_stream_factory()->AddTLSIntolerantServer(request_->url);
|
| ResetConnectionAndRequestForResend();
|
| error = OK;
|
|
|