| Index: net/http/http_network_transaction_unittest.cc
|
| diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
|
| index 260de13f9a3cbe2fdbe1ade93d82454d1b82077a..5c9f0a29641ebba3235287e2fcfdd91760226689 100644
|
| --- a/net/http/http_network_transaction_unittest.cc
|
| +++ b/net/http/http_network_transaction_unittest.cc
|
| @@ -20,6 +20,7 @@
|
| #include "net/base/net_log.h"
|
| #include "net/base/net_log_unittest.h"
|
| #include "net/base/request_priority.h"
|
| +#include "net/base/ssl_cert_request_info.h"
|
| #include "net/base/ssl_config_service_defaults.h"
|
| #include "net/base/ssl_info.h"
|
| #include "net/base/test_completion_callback.h"
|
| @@ -8207,4 +8208,203 @@ TEST_F(HttpNetworkTransactionTest, NPNMispredict) {
|
| EXPECT_EQ("hello world", contents);
|
| }
|
|
|
| +// Ensure that a client certificate is removed from the SSL client auth
|
| +// cache when:
|
| +// 1) No proxy is involved.
|
| +// 2) TLS False Start is disabled.
|
| +// 3) The initial TLS handshake requests a client certificate.
|
| +// 4) The client supplies an invalid/unacceptable certificate.
|
| +TEST_F(HttpNetworkTransactionTest, ClientAuthCertCache_Direct_NoFalseStart) {
|
| + SessionDependencies session_deps;
|
| +
|
| + scoped_refptr<SSLCertRequestInfo> cert_request(new SSLCertRequestInfo());
|
| + cert_request->host_and_port = "www.example.com:443";
|
| +
|
| + // [ssl_]data1 contains the data for the first SSL handshake. When a
|
| + // CertificateRequest is received for the first time, the handshake will
|
| + // be aborted to allow the caller to provide a certificate.
|
| + SSLSocketDataProvider ssl_data1(true /* async */,
|
| + net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
|
| + ssl_data1.cert_request_info = cert_request.get();
|
| + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data1);
|
| + net::StaticSocketDataProvider data1(NULL, 0, NULL, 0);
|
| + session_deps.socket_factory.AddSocketDataProvider(&data1);
|
| +
|
| + // [ssl_]data2 contains the data for the second SSL handshake. When TLS
|
| + // False Start is not being used, the result of the SSL handshake will be
|
| + // returned as part of the SSLClientSocket::Connect() call. This test
|
| + // matches the result of a server sending a handshake_failure alert,
|
| + // rather than a Finished message, because it requires a client
|
| + // certificate and none was supplied.
|
| + SSLSocketDataProvider ssl_data2(true /* async */,
|
| + net::ERR_SSL_PROTOCOL_ERROR);
|
| + ssl_data2.cert_request_info = cert_request.get();
|
| + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data2);
|
| + net::StaticSocketDataProvider data2(NULL, 0, NULL, 0);
|
| + session_deps.socket_factory.AddSocketDataProvider(&data2);
|
| +
|
| + // [ssl_]data3 contains the data for the third SSL handshake. When a
|
| + // connection to a server fails during an SSL handshake,
|
| + // HttpNetworkTransaction will attempt to fallback to SSLv3 if the initial
|
| + // connection was attempted with TLSv1. This is transparent to the caller
|
| + // of the HttpNetworkTransaction. Because this test failure is due to
|
| + // requiring a client certificate, this fallback handshake should also
|
| + // fail.
|
| + SSLSocketDataProvider ssl_data3(true /* async */,
|
| + net::ERR_SSL_PROTOCOL_ERROR);
|
| + ssl_data3.cert_request_info = cert_request.get();
|
| + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data3);
|
| + net::StaticSocketDataProvider data3(NULL, 0, NULL, 0);
|
| + session_deps.socket_factory.AddSocketDataProvider(&data3);
|
| +
|
| + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
|
| + scoped_ptr<HttpNetworkTransaction> trans(new HttpNetworkTransaction(session));
|
| +
|
| + net::HttpRequestInfo request_info;
|
| + request_info.url = GURL("https://www.example.com/");
|
| + request_info.method = "GET";
|
| + request_info.load_flags = net::LOAD_NORMAL;
|
| +
|
| + // Begin the SSL handshake with the peer. This consumes ssl_data1.
|
| + TestCompletionCallback callback;
|
| + int rv = trans->Start(&request_info, &callback, net::BoundNetLog());
|
| + ASSERT_EQ(net::ERR_IO_PENDING, rv);
|
| +
|
| + // Complete the SSL handshake, which should abort due to requiring a
|
| + // client certificate.
|
| + rv = callback.WaitForResult();
|
| + ASSERT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED, rv);
|
| +
|
| + // Indicate that no certificate should be supplied. From the perspective
|
| + // of SSLClientCertCache, NULL is just as meaningful as a real
|
| + // certificate, so this is the same as supply a
|
| + // legitimate-but-unacceptable certificate.
|
| + rv = trans->RestartWithCertificate(NULL, &callback);
|
| + ASSERT_EQ(net::ERR_IO_PENDING, rv);
|
| +
|
| + // Ensure the certificate was added to the client auth cache before
|
| + // allowing the connection to continue restarting.
|
| + scoped_refptr<X509Certificate> client_cert;
|
| + ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup("www.example.com:443",
|
| + &client_cert));
|
| + ASSERT_EQ(NULL, client_cert.get());
|
| +
|
| + // Restart the handshake. This will consume ssl_data2, which fails, and
|
| + // then consume ssl_data3, which should also fail. The result code is
|
| + // checked against what ssl_data3 should return.
|
| + rv = callback.WaitForResult();
|
| + ASSERT_EQ(net::ERR_SSL_PROTOCOL_ERROR, rv);
|
| +
|
| + // Ensure that the client certificate is removed from the cache on a
|
| + // handshake failure.
|
| + ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443",
|
| + &client_cert));
|
| +}
|
| +
|
| +// Ensure that a client certificate is removed from the SSL client auth
|
| +// cache when:
|
| +// 1) No proxy is involved.
|
| +// 2) TLS False Start is enabled.
|
| +// 3) The initial TLS handshake requests a client certificate.
|
| +// 4) The client supplies an invalid/unacceptable certificate.
|
| +TEST_F(HttpNetworkTransactionTest, ClientAuthCertCache_Direct_FalseStart) {
|
| + SessionDependencies session_deps;
|
| +
|
| + scoped_refptr<SSLCertRequestInfo> cert_request(new SSLCertRequestInfo());
|
| + cert_request->host_and_port = "www.example.com:443";
|
| +
|
| + // When TLS False Start is used, SSLClientSocket::Connect() calls will
|
| + // return successfully after reading up to the peer's Certificate message.
|
| + // This is to allow the caller to call SSLClientSocket::Write(), which can
|
| + // enqueue application data to be sent in the same packet as the
|
| + // ChangeCipherSpec and Finished messages.
|
| + // The actual handshake will be finished when SSLClientSocket::Read() is
|
| + // called, which expects to process the peer's ChangeCipherSpec and
|
| + // Finished messages. If there was an error negotiating with the peer,
|
| + // such as due to the peer requiring a client certificate when none was
|
| + // supplied, the alert sent by the peer won't be processed until Read() is
|
| + // called.
|
| +
|
| + // Like the non-False Start case, when a client certificate is requested by
|
| + // the peer, the handshake is aborted during the Connect() call.
|
| + // [ssl_]data1 represents the initial SSL handshake with the peer.
|
| + SSLSocketDataProvider ssl_data1(true /* async */,
|
| + net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
|
| + ssl_data1.cert_request_info = cert_request.get();
|
| + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data1);
|
| + net::StaticSocketDataProvider data1(NULL, 0, NULL, 0);
|
| + session_deps.socket_factory.AddSocketDataProvider(&data1);
|
| +
|
| + // When a client certificate is supplied, Connect() will not be aborted
|
| + // when the peer requests the certificate. Instead, the handshake will
|
| + // artificially succeed, allowing the caller to write the HTTP request to
|
| + // the socket. The handshake messages are not processed until Read() is
|
| + // called, which then detects that the handshake was aborted, due to the
|
| + // peer sending a handshake_failure because it requires a client
|
| + // certificate.
|
| + SSLSocketDataProvider ssl_data2(true /* async */, net::OK);
|
| + ssl_data2.cert_request_info = cert_request.get();
|
| + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data2);
|
| + net::MockRead data2_reads[] = {
|
| + net::MockRead(true /* async */, net::ERR_SSL_PROTOCOL_ERROR),
|
| + };
|
| + net::StaticSocketDataProvider data2(
|
| + data2_reads, arraysize(data2_reads), NULL, 0);
|
| + session_deps.socket_factory.AddSocketDataProvider(&data2);
|
| +
|
| + // As described in ClientAuthCertCache_Direct_NoFalseStart, [ssl_]data3 is
|
| + // the data for the SSL handshake once the TLSv1 connection falls back to
|
| + // SSLv3. It has the same behaviour as [ssl_]data2.
|
| + SSLSocketDataProvider ssl_data3(true /* async */, net::OK);
|
| + ssl_data3.cert_request_info = cert_request.get();
|
| + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data3);
|
| + net::StaticSocketDataProvider data3(
|
| + data2_reads, arraysize(data2_reads), NULL, 0);
|
| + session_deps.socket_factory.AddSocketDataProvider(&data3);
|
| +
|
| + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
|
| + scoped_ptr<HttpNetworkTransaction> trans(new HttpNetworkTransaction(session));
|
| +
|
| + net::HttpRequestInfo request_info;
|
| + request_info.url = GURL("https://www.example.com/");
|
| + request_info.method = "GET";
|
| + request_info.load_flags = net::LOAD_NORMAL;
|
| +
|
| + // Begin the initial SSL handshake.
|
| + TestCompletionCallback callback;
|
| + int rv = trans->Start(&request_info, &callback, net::BoundNetLog());
|
| + ASSERT_EQ(net::ERR_IO_PENDING, rv);
|
| +
|
| + // Complete the SSL handshake, which should abort due to requiring a
|
| + // client certificate.
|
| + rv = callback.WaitForResult();
|
| + ASSERT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED, rv);
|
| +
|
| + // Indicate that no certificate should be supplied. From the perspective
|
| + // of SSLClientCertCache, NULL is just as meaningful as a real
|
| + // certificate, so this is the same as supply a
|
| + // legitimate-but-unacceptable certificate.
|
| + rv = trans->RestartWithCertificate(NULL, &callback);
|
| + ASSERT_EQ(net::ERR_IO_PENDING, rv);
|
| +
|
| + // Ensure the certificate was added to the client auth cache before
|
| + // allowing the connection to continue restarting.
|
| + scoped_refptr<X509Certificate> client_cert;
|
| + ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup("www.example.com:443",
|
| + &client_cert));
|
| + ASSERT_EQ(NULL, client_cert.get());
|
| +
|
| +
|
| + // Restart the handshake. This will consume ssl_data2, which fails, and
|
| + // then consume ssl_data3, which should also fail. The result code is
|
| + // checked against what ssl_data3 should return.
|
| + rv = callback.WaitForResult();
|
| + ASSERT_EQ(net::ERR_SSL_PROTOCOL_ERROR, rv);
|
| +
|
| + // Ensure that the client certificate is removed from the cache on a
|
| + // handshake failure.
|
| + ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443",
|
| + &client_cert));
|
| +}
|
| +
|
| } // namespace net
|
|
|