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; |