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..efdd8f50ab141f2aa1fc681655345edcdc497498 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,168 @@ 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, Direct_ClientAuthCertCache_NoFalseStart) { |
wtc
2011/01/10 23:40:58
Nit: name the unit tests with "ClientAuthCertCache
|
+ SessionDependencies session_deps; |
+ |
+ // [ssl_]data1 represents the first TLS handshake. The handshake will fail |
+ // because a client certificate is requested |
+ // (net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED). A client certificate (NULL, in |
+ // this case) will then be supplied, and [ssl_]data2 will represent the |
+ // second handshake. Here, the handshake will fail because the |
+ // hypothetical server requires an actual certificate (or if this were a |
+ // real client certificate, a different certificate). Because the TLSv1 |
+ // handshake failed, HttpNetworkTransaction then tries an SSLv3 fallback, |
+ // [ssl_]data3, to try to distinguish if the reason for the failure was |
+ // client authentication (if so, it should fail again) or due to TLS |
+ // intolerance (in which case, it should succeed). Once the fallback |
+ // handshake fails, HttpNetworkTransaction will return the status to the |
+ // caller. |
+ SSLSocketDataProvider ssl_data1(true /* async */, |
+ net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED); |
+ SSLSocketDataProvider ssl_data2(true /* async */, |
+ net::ERR_SSL_PROTOCOL_ERROR); |
+ SSLSocketDataProvider ssl_data3(true /* async */, |
+ net::ERR_SSL_PROTOCOL_ERROR); |
+ scoped_refptr<SSLCertRequestInfo> cert_request(new SSLCertRequestInfo()); |
+ cert_request->host_and_port = "www.example.com:443"; |
+ ssl_data1.cert_request_info = cert_request.get(); |
+ ssl_data2.cert_request_info = cert_request.get(); |
+ ssl_data3.cert_request_info = cert_request.get(); |
+ |
+ // Not particularly interested in the individual reads/writes, as the |
+ // SSL client auth cache is what is under inspection. |
+ net::StaticSocketDataProvider data1(NULL, 0, NULL, 0); |
+ net::StaticSocketDataProvider data2(NULL, 0, NULL, 0); |
+ net::StaticSocketDataProvider data3(NULL, 0, NULL, 0); |
+ |
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data1); |
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data2); |
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data3); |
+ session_deps.socket_factory.AddSocketDataProvider(&data1); |
+ session_deps.socket_factory.AddSocketDataProvider(&data2); |
+ 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; |
+ |
+ TestCompletionCallback callback; |
+ int rv = trans->Start(&request_info, &callback, net::BoundNetLog()); |
+ ASSERT_EQ(net::ERR_IO_PENDING, rv); |
+ rv = callback.WaitForResult(); |
+ ASSERT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED, rv); |
+ |
+ // Supply a dummy certificate. The SSLClientAuthCertCache tests ensure |
+ // this test should behave the same as if this test used a real cert. |
+ 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()); |
+ rv = callback.WaitForResult(); |
+ |
+ // Now that the new handshake has failed, ensure that the client |
+ // certificate was removed from the client auth cache. |
+ ASSERT_EQ(net::ERR_SSL_PROTOCOL_ERROR, rv); |
+ 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, Direct_ClientAuthCertCache_FalseStart) { |
+ SessionDependencies session_deps; |
+ |
+ // See Direct_ClientAuthCertCache_NoFalseStart for the explanation of |
+ // [ssl_]data[1-3]. |
+ // When False Start is used, the SSL Connect() calls artificially succeed, |
+ // because handshake errors involving client auth are not detected until |
+ // after the server has had a chance to process the client's Certificate |
+ // message and sends a response. With False Start, the response is |
+ // processed during the first Read() call on the SSL socket. |
+ SSLSocketDataProvider ssl_data1(true /* async */, net::OK); |
+ SSLSocketDataProvider ssl_data2(true /* async */, net::OK); |
+ SSLSocketDataProvider ssl_data3(true /* async */, net::OK); |
+ |
+ scoped_refptr<SSLCertRequestInfo> cert_request(new SSLCertRequestInfo()); |
+ cert_request->host_and_port = "www.example.com:443"; |
+ ssl_data1.cert_request_info = cert_request.get(); |
+ ssl_data2.cert_request_info = cert_request.get(); |
+ ssl_data3.cert_request_info = cert_request.get(); |
+ |
+ // When TLS False Start is used, the SSL handshake is completed by the |
+ // first attempt to Read from the socket. Ensure that even if the error is |
+ // delayed until Read, the client cert is still evicted from the cache. |
+ net::MockRead data1_reads[] = { |
+ net::MockRead(true /* async */, net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED), |
+ }; |
+ net::MockRead data2_reads[] = { |
+ net::MockRead(true /* async */, net::ERR_SSL_PROTOCOL_ERROR), |
+ }; |
+ net::StaticSocketDataProvider data1( |
+ data1_reads, arraysize(data1_reads), NULL, 0); |
+ net::StaticSocketDataProvider data2( |
+ data2_reads, arraysize(data2_reads), NULL, 0); |
+ net::StaticSocketDataProvider data3( |
+ data2_reads, arraysize(data2_reads), NULL, 0); |
+ |
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data1); |
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data2); |
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data3); |
+ session_deps.socket_factory.AddSocketDataProvider(&data1); |
+ session_deps.socket_factory.AddSocketDataProvider(&data2); |
+ 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; |
+ |
+ TestCompletionCallback callback; |
+ int rv = trans->Start(&request_info, &callback, net::BoundNetLog()); |
+ ASSERT_EQ(net::ERR_IO_PENDING, rv); |
+ rv = callback.WaitForResult(); |
+ ASSERT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED, rv); |
+ |
+ // Supply a dummy certificate. The SSLClientAuthCertCache tests ensure |
+ // this test should behave the same as if this test used a real cert. |
+ 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()); |
+ rv = callback.WaitForResult(); |
+ |
+ // Now that the new handshake has failed, ensure that the client |
+ // certificate was removed from the client auth cache. |
+ ASSERT_EQ(net::ERR_SSL_PROTOCOL_ERROR, rv); |
+ ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443", |
+ &client_cert)); |
+} |
+ |
} // namespace net |