diff options
25 files changed, 282 insertions, 60 deletions
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h index e22e11f..d0f91c4 100644 --- a/net/base/net_error_list.h +++ b/net/base/net_error_list.h @@ -106,6 +106,9 @@ NET_ERROR(PROXY_AUTH_REQUESTED, -115) // certificate error. NET_ERROR(CERT_ERROR_IN_SSL_RENEGOTIATION, -116) +// The SSL handshake failed because of a bad or missing client certificate. +NET_ERROR(BAD_SSL_CLIENT_AUTH_CERT, -117) + // Certificate error codes // // The values of certificate error codes must be consecutive. diff --git a/net/base/socket_test_util.cc b/net/base/socket_test_util.cc index fa7f63e..cd9bd77 100644 --- a/net/base/socket_test_util.cc +++ b/net/base/socket_test_util.cc @@ -24,6 +24,8 @@ class MockClientSocket : public net::SSLClientSocket { // SSLClientSocket methods: virtual void GetSSLInfo(net::SSLInfo* ssl_info); + virtual void GetSSLCertRequestInfo( + net::SSLCertRequestInfo* cert_request_info); virtual void Disconnect(); virtual bool IsConnected() const; virtual bool IsConnectedAndIdle() const; @@ -105,6 +107,11 @@ void MockClientSocket::GetSSLInfo(net::SSLInfo* ssl_info) { NOTREACHED(); } +void MockClientSocket::GetSSLCertRequestInfo( + net::SSLCertRequestInfo* cert_request_info) { + NOTREACHED(); +} + void MockClientSocket::Disconnect() { connected_ = false; callback_ = NULL; diff --git a/net/base/ssl_cert_request_info.h b/net/base/ssl_cert_request_info.h new file mode 100644 index 0000000..2529d87 --- /dev/null +++ b/net/base/ssl_cert_request_info.h @@ -0,0 +1,37 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_BASE_SSL_CERT_REQUEST_INFO_H_ +#define NET_BASE_SSL_CERT_REQUEST_INFO_H_ + +#include <string> +#include <vector> + +#include "base/ref_counted.h" + +namespace net { + +class X509Certificate; + +// The SSLCertRequestInfo class contains the info that allows a user to +// select a certificate to send to the SSL server for client authentication. +class SSLCertRequestInfo + : public base::RefCountedThreadSafe<SSLCertRequestInfo> { + public: + // The host and port of the SSL server that requested client authentication. + std::string host_and_port; + + // A list of client certificates that match the server's criteria in the + // SSL CertificateRequest message. In TLS 1.0, the CertificateRequest + // message is defined as: + // struct { + // ClientCertificateType certificate_types<1..2^8-1>; + // DistinguishedName certificate_authorities<3..2^16-1>; + // } CertificateRequest; + std::vector<scoped_refptr<X509Certificate> > client_certs; +}; + +} // namespace net + +#endif // NET_BASE_SSL_CERT_REQUEST_INFO_H_ diff --git a/net/base/ssl_client_socket.h b/net/base/ssl_client_socket.h index 8c9f05b..8b9cca6 100644 --- a/net/base/ssl_client_socket.h +++ b/net/base/ssl_client_socket.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -9,6 +9,7 @@ namespace net { +class SSLCertRequestInfo; class SSLInfo; // A client socket that uses SSL as the transport layer. @@ -21,6 +22,11 @@ class SSLClientSocket : public ClientSocket { public: // Gets the SSL connection information of the socket. virtual void GetSSLInfo(SSLInfo* ssl_info) = 0; + + // Gets the SSL CertificateRequest info of the socket after Connect failed + // with ERR_SSL_CLIENT_AUTH_CERT_NEEDED. + virtual void GetSSLCertRequestInfo( + SSLCertRequestInfo* cert_request_info) = 0; }; } // namespace net diff --git a/net/base/ssl_client_socket_mac.cc b/net/base/ssl_client_socket_mac.cc index 2148a2b..7528b13 100644 --- a/net/base/ssl_client_socket_mac.cc +++ b/net/base/ssl_client_socket_mac.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2008-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -418,6 +418,11 @@ void SSLClientSocketMac::GetSSLInfo(SSLInfo* ssl_info) { ssl_info->security_bits = KeySizeOfCipherSuite(suite); } +void SSLClientSocketMac::GetSSLCertRequestInfo( + SSLCertRequestInfo* cert_request_info) { + // TODO(wtc): implement this. +} + void SSLClientSocketMac::DoCallback(int rv) { DCHECK(rv != ERR_IO_PENDING); DCHECK(user_callback_); @@ -743,7 +748,6 @@ OSStatus SSLClientSocketMac::SSLWriteCallback(SSLConnectionRef connection, if (rv > 0) { us->send_buffer_.erase(us->send_buffer_.begin(), us->send_buffer_.begin() + rv); - } } while (rv > 0 && !us->send_buffer_.empty()); diff --git a/net/base/ssl_client_socket_mac.h b/net/base/ssl_client_socket_mac.h index 9d4dec0..6e088bb 100644 --- a/net/base/ssl_client_socket_mac.h +++ b/net/base/ssl_client_socket_mac.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -31,6 +31,7 @@ class SSLClientSocketMac : public SSLClientSocket { // SSLClientSocket methods: virtual void GetSSLInfo(SSLInfo* ssl_info); + virtual void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info); // ClientSocket methods: virtual int Connect(CompletionCallback* callback); diff --git a/net/base/ssl_client_socket_nss.cc b/net/base/ssl_client_socket_nss.cc index 6c8aeca..d64d791 100644 --- a/net/base/ssl_client_socket_nss.cc +++ b/net/base/ssl_client_socket_nss.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -490,6 +490,11 @@ void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) { LeaveFunction(""); } +void SSLClientSocketNSS::GetSSLCertRequestInfo( + SSLCertRequestInfo* cert_request_info) { + // TODO(wtc): implement this. +} + void SSLClientSocketNSS::DoCallback(int rv) { EnterFunction(rv); DCHECK(rv != ERR_IO_PENDING); diff --git a/net/base/ssl_client_socket_nss.h b/net/base/ssl_client_socket_nss.h index 71decf5..643051f 100644 --- a/net/base/ssl_client_socket_nss.h +++ b/net/base/ssl_client_socket_nss.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -40,6 +40,7 @@ class SSLClientSocketNSS : public SSLClientSocket { // SSLClientSocket methods: virtual void GetSSLInfo(SSLInfo* ssl_info); + virtual void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info); // ClientSocket methods: virtual int Connect(CompletionCallback* callback); diff --git a/net/base/ssl_client_socket_win.cc b/net/base/ssl_client_socket_win.cc index a839bb3..43641e3 100644 --- a/net/base/ssl_client_socket_win.cc +++ b/net/base/ssl_client_socket_win.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -232,7 +232,6 @@ SSLClientSocketWin::SSLClientSocketWin(ClientSocket* transport_socket, writing_first_token_(false), completed_handshake_(false), ignore_ok_result_(false), - no_client_cert_(false), renegotiating_(false) { memset(&stream_sizes_, 0, sizeof(stream_sizes_)); memset(in_buffers_, 0, sizeof(in_buffers_)); @@ -261,6 +260,11 @@ void SSLClientSocketWin::GetSSLInfo(SSLInfo* ssl_info) { } } +void SSLClientSocketWin::GetSSLCertRequestInfo( + SSLCertRequestInfo* cert_request_info) { + // TODO(wtc): implement this. +} + int SSLClientSocketWin::Connect(CompletionCallback* callback) { DCHECK(transport_.get()); DCHECK(next_state_ == STATE_NONE); @@ -544,14 +548,7 @@ int SSLClientSocketWin::DoHandshakeReadComplete(int result) { ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM; - // When InitializeSecurityContext returns SEC_I_INCOMPLETE_CREDENTIALS, - // John Banes (a Microsoft security developer) said we need to pass in the - // ISC_REQ_USE_SUPPLIED_CREDS flag if we skip finding a client certificate - // and just call InitializeSecurityContext again. (See - // (http://www.derkeiler.com/Newsgroups/microsoft.public.platformsdk.security/2004-08/0187.html.) - // My testing on XP SP2 and Vista SP1 shows that it still works without - // passing in this flag, but I pass it in to be safe. - if (no_client_cert_) + if (ssl_config_.send_client_cert) flags |= ISC_REQ_USE_SUPPLIED_CREDS; SecBufferDesc in_buffer_desc, out_buffer_desc; @@ -623,28 +620,21 @@ int SSLClientSocketWin::DidCallInitializeSecurityContext() { int result = MapSecurityError(isc_status_); // We told Schannel to not verify the server certificate // (SCH_CRED_MANUAL_CRED_VALIDATION), so any certificate error returned by - // InitializeSecurityContext must be referring to the (missing) client - // certificate. + // InitializeSecurityContext must be referring to the bad or missing + // client certificate. if (IsCertificateError(result)) { - // TODO(wtc): When we support SSL client authentication, we will need to - // add new error codes for client certificate errors reported by the - // server using SSL/TLS alert messages. See http://crbug.com/318. See - // also the MSDN page "Schannel Error Codes for TLS and SSL Alerts", - // which maps TLS alert messages to Windows error codes: + // TODO(wtc): Add new error codes for client certificate errors reported + // by the server using SSL/TLS alert messages. See the MSDN page + // "Schannel Error Codes for TLS and SSL Alerts", which maps TLS alert + // messages to Windows error codes: // http://msdn.microsoft.com/en-us/library/dd721886%28VS.85%29.aspx - return ERR_SSL_CLIENT_AUTH_CERT_NEEDED; + return ERR_BAD_SSL_CLIENT_AUTH_CERT; } return result; } - if (isc_status_ == SEC_I_INCOMPLETE_CREDENTIALS) { - // We don't support SSL client authentication yet. For now we just set - // no_client_cert_ to true and call InitializeSecurityContext again. - no_client_cert_ = true; - next_state_ = STATE_HANDSHAKE_READ_COMPLETE; - ignore_ok_result_ = true; // OK doesn't mean EOF. - return OK; - } + if (isc_status_ == SEC_I_INCOMPLETE_CREDENTIALS) + return ERR_SSL_CLIENT_AUTH_CERT_NEEDED; DCHECK(isc_status_ == SEC_I_CONTINUE_NEEDED); if (in_buffers_[1].BufferType == SECBUFFER_EXTRA) { @@ -989,7 +979,7 @@ int SSLClientSocketWin::DidCompleteHandshake() { SECURITY_STATUS status = QueryContextAttributes( &ctxt_, SECPKG_ATTR_STREAM_SIZES, &stream_sizes_); if (status != SEC_E_OK) { - DLOG(ERROR) << "QueryContextAttributes failed: " << status; + DLOG(ERROR) << "QueryContextAttributes (stream sizes) failed: " << status; return MapSecurityError(status); } DCHECK(!server_cert_ || renegotiating_); @@ -997,7 +987,7 @@ int SSLClientSocketWin::DidCompleteHandshake() { status = QueryContextAttributes( &ctxt_, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &server_cert_handle); if (status != SEC_E_OK) { - DLOG(ERROR) << "QueryContextAttributes failed: " << status; + DLOG(ERROR) << "QueryContextAttributes (remote cert) failed: " << status; return MapSecurityError(status); } if (renegotiating_ && diff --git a/net/base/ssl_client_socket_win.h b/net/base/ssl_client_socket_win.h index 35f1e9e..12bf18f 100644 --- a/net/base/ssl_client_socket_win.h +++ b/net/base/ssl_client_socket_win.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -36,6 +36,7 @@ class SSLClientSocketWin : public SSLClientSocket { // SSLClientSocket methods: virtual void GetSSLInfo(SSLInfo* ssl_info); + virtual void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info); // ClientSocket methods: virtual int Connect(CompletionCallback* callback); @@ -147,9 +148,6 @@ class SSLClientSocketWin : public SSLClientSocket { // to be interpreted as EOF. bool ignore_ok_result_; - // True if the user has no client certificate. - bool no_client_cert_; - // Renegotiation is in progress. bool renegotiating_; }; diff --git a/net/base/ssl_config_service.h b/net/base/ssl_config_service.h index dec6fdb..cf2c9bb 100644 --- a/net/base/ssl_config_service.h +++ b/net/base/ssl_config_service.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -18,7 +18,7 @@ struct SSLConfig { // Default to SSL 2.0 off, SSL 3.0 on, and TLS 1.0 on. SSLConfig() : rev_checking_enabled(false), ssl2_enabled(false), - ssl3_enabled(true), tls1_enabled(true) { + ssl3_enabled(true), tls1_enabled(true), send_client_cert(false) { } bool rev_checking_enabled; // True if server certificate revocation @@ -27,11 +27,19 @@ struct SSLConfig { bool ssl3_enabled; // True if SSL 3.0 is enabled. bool tls1_enabled; // True if TLS 1.0 is enabled. + // TODO(wtc): move the following members to a new SSLParams structure. They + // are not SSL configuration settings. + // Add any known-bad SSL certificates to allowed_bad_certs_ that should not // trigger an ERR_CERT_*_INVALID error when calling SSLClientSocket::Connect. // This would normally be done in response to the user explicitly accepting // the bad certificate. std::set<scoped_refptr<X509Certificate> > allowed_bad_certs_; + + // True if we should send client_cert to the server. + bool send_client_cert; + + scoped_refptr<X509Certificate> client_cert; }; // This class is responsible for getting and setting the SSL configuration. diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc index f87b306..4453480 100644 --- a/net/http/http_cache.cc +++ b/net/http/http_cache.cc @@ -20,6 +20,7 @@ #include "net/base/io_buffer.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" +#include "net/base/ssl_cert_request_info.h" #include "net/disk_cache/disk_cache.h" #include "net/http/http_network_layer.h" #include "net/http/http_network_session.h" @@ -166,6 +167,8 @@ class HttpCache::Transaction // HttpTransaction methods: virtual int Start(const HttpRequestInfo*, CompletionCallback*); virtual int RestartIgnoringLastError(CompletionCallback*); + virtual int RestartWithCertificate(X509Certificate* client_cert, + CompletionCallback* callback); virtual int RestartWithAuth(const std::wstring& username, const std::wstring& password, CompletionCallback* callback); @@ -249,6 +252,10 @@ class HttpCache::Transaction // error code. int RestartNetworkRequest(); + // Called to restart a network transaction with a client certificate. + // Returns network error code. + int RestartNetworkRequestWithCertificate(X509Certificate* client_cert); + // Called to restart a network transaction with authentication credentials. // Returns network error code. int RestartNetworkRequestWithAuth(const std::wstring& username, @@ -414,6 +421,25 @@ int HttpCache::Transaction::RestartIgnoringLastError( return rv; } +int HttpCache::Transaction::RestartWithCertificate( + X509Certificate* client_cert, + CompletionCallback* callback) { + DCHECK(callback); + + // ensure that we only have one asynchronous call at a time. + DCHECK(!callback_); + + if (revoked()) + return ERR_UNEXPECTED; + + int rv = RestartNetworkRequestWithCertificate(client_cert); + + if (rv == ERR_IO_PENDING) + callback_ = callback; + + return rv; +} + int HttpCache::Transaction::RestartWithAuth( const std::wstring& username, const std::wstring& password, @@ -499,7 +525,8 @@ const HttpResponseInfo* HttpCache::Transaction::GetResponseInfo() const { // Null headers means we encountered an error or haven't a response yet if (auth_response_.headers) return &auth_response_; - return (response_.headers || response_.ssl_info.cert) ? &response_ : NULL; + return (response_.headers || response_.ssl_info.cert || + response_.cert_request_info) ? &response_ : NULL; } LoadState HttpCache::Transaction::GetLoadState() const { @@ -805,6 +832,18 @@ int HttpCache::Transaction::RestartNetworkRequest() { return rv; } +int HttpCache::Transaction::RestartNetworkRequestWithCertificate( + X509Certificate* client_cert) { + DCHECK(mode_ & WRITE || mode_ == NONE); + DCHECK(network_trans_.get()); + + int rv = network_trans_->RestartWithCertificate(client_cert, + &network_info_callback_); + if (rv != ERR_IO_PENDING) + OnNetworkInfoAvailable(rv); + return rv; +} + int HttpCache::Transaction::RestartNetworkRequestWithAuth( const std::wstring& username, const std::wstring& password) { @@ -1092,6 +1131,10 @@ void HttpCache::Transaction::OnNetworkInfoAvailable(int result) { // so GetResponseInfo() should never returns NULL here. DCHECK(response); response_.ssl_info = response->ssl_info; + } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { + const HttpResponseInfo* response = network_trans_->GetResponseInfo(); + DCHECK(response); + response_.cert_request_info = response->cert_request_info; } HandleResult(result); } diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index dcdd5ec..4fbd5cb 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -17,6 +17,7 @@ #include "net/base/load_flags.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" +#include "net/base/ssl_cert_request_info.h" #include "net/base/ssl_client_socket.h" #include "net/base/upload_data_stream.h" #include "net/http/http_auth.h" @@ -189,6 +190,21 @@ int HttpNetworkTransaction::RestartIgnoringLastError( return rv; } +int HttpNetworkTransaction::RestartWithCertificate( + X509Certificate* client_cert, + CompletionCallback* callback) { + ssl_config_.client_cert = client_cert; + ssl_config_.send_client_cert = true; + next_state_ = STATE_INIT_CONNECTION; + // Reset the other member variables. + // Note: this is necessary only with SSL renegotiation. + ResetStateForRestart(); + int rv = DoLoop(OK); + if (rv == ERR_IO_PENDING) + user_callback_ = callback; + return rv; +} + int HttpNetworkTransaction::RestartWithAuth( const std::wstring& username, const std::wstring& password, @@ -346,7 +362,8 @@ int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len, } const HttpResponseInfo* HttpNetworkTransaction::GetResponseInfo() const { - return (response_.headers || response_.ssl_info.cert) ? &response_ : NULL; + return (response_.headers || response_.ssl_info.cert || + response_.cert_request_info) ? &response_ : NULL; } LoadState HttpNetworkTransaction::GetLoadState() const { @@ -603,6 +620,8 @@ int HttpNetworkTransaction::DoSSLConnectComplete(int result) { if (result == OK) { next_state_ = STATE_WRITE_HEADERS; + } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { + HandleCertificateRequest(); } else { result = HandleSSLHandshakeError(result); } @@ -746,13 +765,19 @@ int HttpNetworkTransaction::HandleConnectionClosedBeforeEndOfHeaders() { } int HttpNetworkTransaction::DoReadHeadersComplete(int result) { - if (using_ssl_ && IsCertificateError(result)) { - // We don't handle a certificate error during SSL renegotiation, so we - // have to return an error that's not in the certificate error range - // (-2xx). - LOG(ERROR) << "Got a server certificate with error " << result - << " during SSL renegotiation"; - result = ERR_CERT_ERROR_IN_SSL_RENEGOTIATION; + // We can get a certificate error or ERR_SSL_CLIENT_AUTH_CERT_NEEDED here + // due to SSL renegotiation. + if (using_ssl_) { + if (IsCertificateError(result)) { + // We don't handle a certificate error during SSL renegotiation, so we + // have to return an error that's not in the certificate error range + // (-2xx). + LOG(ERROR) << "Got a server certificate with error " << result + << " during SSL renegotiation"; + result = ERR_CERT_ERROR_IN_SSL_RENEGOTIATION; + } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { + HandleCertificateRequest(); + } } if (result < 0) @@ -1217,6 +1242,18 @@ int HttpNetworkTransaction::HandleCertificateError(int error) { return error; } +void HttpNetworkTransaction::HandleCertificateRequest() { + response_.cert_request_info = new SSLCertRequestInfo; + SSLClientSocket* ssl_socket = + reinterpret_cast<SSLClientSocket*>(connection_.socket()); + ssl_socket->GetSSLCertRequestInfo(response_.cert_request_info); + + // Close the connection while the user is selecting a certificate to send + // to the server. + connection_.socket()->Disconnect(); + connection_.Reset(); +} + int HttpNetworkTransaction::HandleSSLHandshakeError(int error) { switch (error) { case ERR_SSL_PROTOCOL_ERROR: diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h index fb21384..13d88b9 100644 --- a/net/http/http_network_transaction.h +++ b/net/http/http_network_transaction.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -44,6 +44,8 @@ class HttpNetworkTransaction : public HttpTransaction { virtual int Start(const HttpRequestInfo* request_info, CompletionCallback* callback); virtual int RestartIgnoringLastError(CompletionCallback* callback); + virtual int RestartWithCertificate(X509Certificate* client_cert, + CompletionCallback* callback); virtual int RestartWithAuth(const std::wstring& username, const std::wstring& password, CompletionCallback* callback); @@ -160,6 +162,9 @@ class HttpNetworkTransaction : public HttpTransaction { // returns the same error code. int HandleCertificateError(int error); + // Called to handle a client certificate request. + void HandleCertificateRequest(); + // Called to possibly recover from an SSL handshake error. Sets next_state_ // and returns OK if recovering from the error. Otherwise, the same error // code is returned. diff --git a/net/http/http_response_info.cc b/net/http/http_response_info.cc index bc2fbe3..0db95b5 100644 --- a/net/http/http_response_info.cc +++ b/net/http/http_response_info.cc @@ -3,6 +3,8 @@ // found in the LICENSE file. #include "net/http/http_response_info.h" + +#include "net/base/ssl_cert_request_info.h" #include "net/http/http_response_headers.h" namespace net { diff --git a/net/http/http_response_info.h b/net/http/http_response_info.h index 88f89e7..7cbe13c 100644 --- a/net/http/http_response_info.h +++ b/net/http/http_response_info.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -13,6 +13,7 @@ namespace net { class HttpResponseHeaders; +class SSLCertRequestInfo; class HttpResponseInfo { public: @@ -41,6 +42,12 @@ class HttpResponseInfo { // will contain additional information about the authentication challenge. scoped_refptr<AuthChallengeInfo> auth_challenge; + // The SSL client certificate request info. + // TODO(wtc): does this really belong in HttpResponseInfo? I put it here + // because it is similar to |auth_challenge|, but unlike HTTP authentication + // challenge, client certificate request is not part of an HTTP response. + scoped_refptr<SSLCertRequestInfo> cert_request_info; + // The SSL connection info (if HTTPS). SSLInfo ssl_info; diff --git a/net/http/http_transaction.h b/net/http/http_transaction.h index cc7dd92..14612e9 100644 --- a/net/http/http_transaction.h +++ b/net/http/http_transaction.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -13,6 +13,7 @@ namespace net { class HttpRequestInfo; class HttpResponseInfo; class IOBuffer; +class X509Certificate; // Represents a single HTTP transaction (i.e., a single request/response pair). // HTTP redirects are not followed and authentication challenges are not @@ -50,6 +51,10 @@ class HttpTransaction { // virtual int RestartIgnoringLastError(CompletionCallback* callback) = 0; + // Restarts the HTTP transaction with a client certificate. + virtual int RestartWithCertificate(X509Certificate* client_cert, + CompletionCallback* callback) = 0; + // Restarts the HTTP transaction with authentication credentials. virtual int RestartWithAuth(const std::wstring& username, const std::wstring& password, diff --git a/net/http/http_transaction_unittest.h b/net/http/http_transaction_unittest.h index d0a51b0..f72c305 100644 --- a/net/http/http_transaction_unittest.h +++ b/net/http/http_transaction_unittest.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -234,6 +234,11 @@ class MockNetworkTransaction : public net::HttpTransaction { return net::ERR_FAILED; } + virtual int RestartWithCertificate(net::X509Certificate* client_cert, + net::CompletionCallback* callback) { + return net::ERR_FAILED; + } + virtual int RestartWithAuth(const std::wstring& username, const std::wstring& password, net::CompletionCallback* callback) { diff --git a/net/net.gyp b/net/net.gyp index 08d197c..c6c92b5 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -116,6 +116,7 @@ 'base/sdch_manager.cc', 'base/sdch_manager.h', 'base/socket.h', + 'base/ssl_cert_request_info.h', 'base/ssl_client_socket.h', 'base/ssl_client_socket_mac.cc', 'base/ssl_client_socket_nss.cc', diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc index ebfc376..b80ebe0 100644 --- a/net/url_request/url_request.cc +++ b/net/url_request/url_request.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -367,6 +367,12 @@ void URLRequest::CancelAuth() { job_->CancelAuth(); } +void URLRequest::ContinueWithCertificate(net::X509Certificate* client_cert) { + DCHECK(job_); + + job_->ContinueWithCertificate(client_cert); +} + void URLRequest::ContinueDespiteLastError() { DCHECK(job_); diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h index 4f65f14..ed38cd7 100644 --- a/net/url_request/url_request.h +++ b/net/url_request/url_request.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -24,6 +24,7 @@ class Time; namespace net { class IOBuffer; +class SSLCertRequestInfo; class UploadData; class X509Certificate; } // namespace net @@ -145,6 +146,17 @@ class URLRequest { request->CancelAuth(); } + // Called when we receive an SSL CertificateRequest message for client + // authentication. The delegate should call + // request->ContinueWithCertificate() with the client certificate the user + // selected, or request->ContinueWithCertificate(NULL) to continue the SSL + // handshake without a client certificate. + virtual void OnCertificateRequested( + URLRequest* request, + net::SSLCertRequestInfo* cert_request_info) { + request->ContinueWithCertificate(NULL); + } + // Called when using SSL and the server responds with a certificate with // an error, for example, whose common name does not match the common name // we were expecting for that host. The delegate should either do the @@ -423,6 +435,11 @@ class URLRequest { void SetAuth(const std::wstring& username, const std::wstring& password); void CancelAuth(); + // This method can be called after the user selects a client certificate to + // instruct this URLRequest to continue with the request with the + // certificate. Pass NULL if the user doesn't have a client certificate. + void ContinueWithCertificate(net::X509Certificate* client_cert); + // This method can be called after some error notifications to instruct this // URLRequest to ignore the current error and continue with the request. To // cancel the request instead, call Cancel(). diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc index 6ad2dc5..da56c8b 100644 --- a/net/url_request/url_request_http_job.cc +++ b/net/url_request/url_request_http_job.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -20,6 +20,7 @@ #include "net/base/net_errors.h" #include "net/base/net_util.h" #include "net/base/sdch_manager.h" +#include "net/base/ssl_cert_request_info.h" #include "net/http/http_response_headers.h" #include "net/http/http_response_info.h" #include "net/http/http_transaction.h" @@ -384,6 +385,26 @@ void URLRequestHttpJob::CancelAuth() { this, &URLRequestHttpJob::OnStartCompleted, net::OK)); } +void URLRequestHttpJob::ContinueWithCertificate( + net::X509Certificate* client_cert) { + DCHECK(transaction_.get()); + + DCHECK(!response_info_) << "should not have a response yet"; + + // No matter what, we want to report our status as IO pending since we will + // be notifying our consumer asynchronously via OnStartCompleted. + SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); + + int rv = transaction_->RestartWithCertificate(client_cert, &start_callback_); + if (rv == net::ERR_IO_PENDING) + return; + + // The transaction started synchronously, but we need to notify the + // URLRequest delegate via the message loop. + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + this, &URLRequestHttpJob::OnStartCompleted, rv)); +} + void URLRequestHttpJob::ContinueDespiteLastError() { // If the transaction was destroyed, then the job was cancelled. if (!transaction_.get()) @@ -453,6 +474,9 @@ void URLRequestHttpJob::OnStartCompleted(int result) { // ssl_info. request_->delegate()->OnSSLCertificateError( request_, result, transaction_->GetResponseInfo()->ssl_info.cert); + } else if (result == net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { + request_->delegate()->OnCertificateRequested( + request_, transaction_->GetResponseInfo()->cert_request_info); } else { NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, result)); } diff --git a/net/url_request/url_request_http_job.h b/net/url_request/url_request_http_job.h index 7853f83..46078d4 100644 --- a/net/url_request/url_request_http_job.h +++ b/net/url_request/url_request_http_job.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -54,6 +54,7 @@ class URLRequestHttpJob : public URLRequestJob { virtual void SetAuth(const std::wstring& username, const std::wstring& password); virtual void CancelAuth(); + virtual void ContinueWithCertificate(net::X509Certificate* client_cert); virtual void ContinueDespiteLastError(); virtual bool GetMoreData(); virtual bool ReadRawData(net::IOBuffer* buf, int buf_size, int *bytes_read); diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc index 3d7df3e..8f4429d 100644 --- a/net/url_request/url_request_job.cc +++ b/net/url_request/url_request_job.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -90,6 +90,12 @@ void URLRequestJob::CancelAuth() { NOTREACHED(); } +void URLRequestJob::ContinueWithCertificate( + net::X509Certificate* client_cert) { + // The derived class should implement this! + NOTREACHED(); +} + void URLRequestJob::ContinueDespiteLastError() { // Implementations should know how to recover from errors they generate. // If this code was reached, we are trying to recover from an error that diff --git a/net/url_request/url_request_job.h b/net/url_request/url_request_job.h index 301bb3b..558d9ad 100644 --- a/net/url_request/url_request_job.h +++ b/net/url_request/url_request_job.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -19,6 +19,7 @@ class AuthChallengeInfo; class HttpResponseInfo; class IOBuffer; class UploadData; +class X509Certificate; } class GURL; @@ -176,6 +177,8 @@ class URLRequestJob : public base::RefCountedThreadSafe<URLRequestJob>, // Display the error page without asking for credentials again. virtual void CancelAuth(); + virtual void ContinueWithCertificate(net::X509Certificate* client_cert); + // Continue processing the request ignoring the last error. virtual void ContinueDespiteLastError(); |