Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(682)

Unified Diff: net/http/http_network_transaction_unittest.cc

Issue 6120003: Unit test for testing SSL client auth with both a proxy and an SSL endpoint (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | net/socket/socket_test_util.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 f7614fffd8e1cd7bc355a05dc34985cc677c1820..909c8ec2cc8e505b56432d015b64ce226af57038 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -12,6 +12,7 @@
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/scoped_ptr.h"
+#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "net/base/auth.h"
#include "net/base/capturing_net_log.h"
@@ -8463,4 +8464,250 @@ TEST_F(HttpNetworkTransactionTest, Proxy_ClientAuthCertCache_Fail) {
}
}
+// Tests that the client certificate used to authenticate with an HTTPS proxy
+// is different than the client certificate used to authenticate to an SSL
+// endpoint, and that the HTTPS proxy certificate will not be sent to the SSL
+// endpoint.
+TEST_F(HttpNetworkTransactionTest, HTTPSProxyAuthAndSSLClientAuth) {
+ SessionDependencies session_deps(
+ ProxyService::CreateFixed("https://proxy:70"));
+ CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+ session_deps.net_log = log.bound().net_log();
+
+ // Certificate request from the HTTPS proxy.
+ scoped_refptr<SSLCertRequestInfo> proxy_cert_request(
+ new SSLCertRequestInfo());
+ proxy_cert_request->host_and_port = "proxy:70";
+
+ // The client certificate to use when authenticating with the proxy.
+ base::Time start_date = base::Time::Now();
+ base::Time expiration_date = start_date + base::TimeDelta::FromDays(1);
+ scoped_refptr<X509Certificate> proxy_cert(
+ new X509Certificate("proxy", "me", start_date, expiration_date));
+
+ // Certificate request from the SSL endpoint.
+ scoped_refptr<SSLCertRequestInfo> endpoint_cert_request(
+ new net::SSLCertRequestInfo());
+ endpoint_cert_request->host_and_port = "www.example.com:443";
+
+ // The client certificate to use when authenticating with the SSL endpoint.
+ scoped_refptr<X509Certificate> endpoint_cert(
+ new X509Certificate("endpoint", "you", start_date, expiration_date));
+
+ // ssl_data1 is the initial connection to the HTTPS proxy, which is
+ // requesting a client certificate. data1 is needed to initialize the
+ // underlying transport socket, but no data is exchanged due to the SSL
+ // handshake failure.
+ SSLSocketDataProvider ssl_data1(true /* async */,
+ net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
+ ssl_data1.cert_request_info = proxy_cert_request.get();
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data1);
+
+ StaticSocketDataProvider data1(NULL, 0, NULL, 0);
+ session_deps.socket_factory.AddSocketDataProvider(&data1);
+
+ // ssl_data2 is the re-connection to the HTTPS proxy, now with a valid
+ // client certificate. data2 contains the data exchanged with the proxy to
+ // set up the tunnel, and then the following reads/writes contain the data
+ // exchanged with the endpoint, which there should be none, due to the
+ // endpoint SSL handshake failure.
+ SSLSocketDataProvider ssl_data2(true /* async */, net::OK);
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data2);
+
+ MockWrite data_writes2[] = {
+ MockWrite("CONNECT www.example.com:443 HTTP/1.1\r\n"
+ "Host: www.example.com\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n"),
+ };
+ MockRead data_reads2[] = {
+ MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"),
+ };
+
+ StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+ data_writes2, arraysize(data_writes2));
+ session_deps.socket_factory.AddSocketDataProvider(&data2);
+
+ // ssl_data3 is the initial connection to the SSL endpoint, which is also
+ // requesting a client certificate. There is no data3, because ssl_data3's
+ // client socket handle comes from the proxy, which is using data2.
+ SSLSocketDataProvider ssl_data3(true /* async */,
+ net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
+ ssl_data3.cert_request_info = endpoint_cert_request.get();
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data3);
+
+ // ssl_data4 is the reconnection to the HTTPS proxy, after the connection
+ // to the SSL endpoint has failed due to requiring a client certificate.
+ // data4 contains the data exchanged with the proxy to set up the tunnel,
+ // and then the subsequent reads/writes contains the data exchanged with
+ // the endpoint.
+ SSLSocketDataProvider ssl_data4(true /* async */, net::OK);
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data4);
+
+ MockWrite data_writes4[] = {
+ MockWrite("CONNECT www.example.com:443 HTTP/1.1\r\n"
+ "Host: www.example.com\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n"),
+
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.example.com\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ };
+ MockRead data_reads4[] = {
+ MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"),
+
+ MockRead("HTTP/1.1 200 OK\r\n\r\n"),
+ MockRead("hello, world"),
+ MockRead(false, OK),
+ };
+ StaticSocketDataProvider data4(data_reads4, arraysize(data_reads4),
+ data_writes4, arraysize(data_writes4));
+ session_deps.socket_factory.AddSocketDataProvider(&data4);
+
+ // ssl_data5 is the SSL handshake with the endpoint, after the proxy has
+ // been negotiated. There is no data5, because ssl_data5's client socket
+ // handle comes from the proxy, which is using data4.
+ SSLSocketDataProvider ssl_data5(true /* async */, net::OK);
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data5);
+
+ net::HttpRequestInfo request;
+ request.url = GURL("https://www.example.com/");
+ request.method = "GET";
+ request.load_flags = net::LOAD_NORMAL;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+ scoped_ptr<HttpNetworkTransaction> trans(
+ new HttpNetworkTransaction(session));
+
+ // Make the initial connection to the HTTPS proxy.
+ TestCompletionCallback callback;
+ int rv = trans->Start(&request, &callback, net::BoundNetLog());
+ ASSERT_EQ(net::ERR_IO_PENDING, rv);
+ // Finish the SSL handshake to the HTTPS proxy (ssl_data1).
+ rv = callback.WaitForResult();
+ ASSERT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED, rv);
+
+ // Make sure that the certificate request matches that from ssl_data1.
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response);
+ ASSERT_TRUE(response->cert_request_info);
+ ASSERT_EQ("proxy:70", response->cert_request_info->host_and_port);
+
+ // Restart the connection to the HTTPS proxy, now with a client
+ // certificate (ssl_data2).
+ rv = trans->RestartWithCertificate(proxy_cert, &callback);
+ ASSERT_EQ(net::ERR_IO_PENDING, rv);
+
+ // Make sure that SSL client auth cache is only updated for the proxy and
+ // not for the endpoint.
+ scoped_refptr<X509Certificate> cached_cert;
+ ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup("proxy:70",
+ &cached_cert));
+ ASSERT_EQ(proxy_cert, cached_cert);
+ ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443",
+ &cached_cert));
+
+ // Utility class to interject ourselves into the handshake. |callback|,
+ // the TestCompletionCallback, can only wait until the
+ // HttpNetworkTransaction callback is invoked. However, on an SSL failure,
+ // the MockSSLClientSocket will be destroyed by the ClientSocketHandle,
+ // meaning the parameters that were passed in it's construction will no
+ // longer be available. This is just a hack-job substitute to demonstrate
+ // the problem without having to shave the Mock*Factory yak to allow us to
+ // break after creating a socket, and DOES NOT represent code I'd ever
+ // check in.
+ class WaitForSSLCreationObserver : public MessageLoop::TaskObserver {
+ public:
+ WaitForSSLCreationObserver(SessionDependencies* session_deps,
+ size_t expected_ssl_sockets)
+ : session_deps_(session_deps),
+ expected_ssl_sockets_(expected_ssl_sockets) {
+ }
+
+ // TaskObserver
+ virtual void WillProcessTask(const Task* task) OVERRIDE {}
+ virtual void DidProcessTask(const Task* task) OVERRIDE {
+ if (session_deps_->socket_factory.ssl_client_sockets().size() >=
+ expected_ssl_sockets_) {
+ MessageLoop::current()->QuitNow();
+ }
+ }
+ private:
+ SessionDependencies* session_deps_;
+ size_t expected_ssl_sockets_;
+ };
+
+ // Finish the SSL handshake with the HTTPS proxy and begin
+ // the SSL handshake with the endpoint (ssl_data3).
+ WaitForSSLCreationObserver wait_for_endpoint(&session_deps, 3);
+ MessageLoop::current()->AddTaskObserver(&wait_for_endpoint);
+ MessageLoop::current()->Run();
+ MessageLoop::current()->RemoveTaskObserver(&wait_for_endpoint);
+
+ // Make sure that the SSL handshake to the endpoint is NOT set to send the
+ // client certificate used for the proxy.
+ MockSSLClientSocket* endpoint_socket =
+ session_deps.socket_factory.GetMockSSLClientSocket(2);
+ ASSERT_TRUE(endpoint_socket);
+ ASSERT_EQ("www.example.com:443",
+ endpoint_socket->GetHostAndPort().ToString());
+ ASSERT_FALSE(endpoint_socket->GetSSLConfig().send_client_cert);
+ ASSERT_FALSE(endpoint_socket->GetSSLConfig().client_cert);
+
+ rv = callback.WaitForResult();
+ ASSERT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED, rv);
+
+ // Make sure that the certificate request matches that from ssl_data3.
+ response = trans->GetResponseInfo();
+ ASSERT_TRUE(response);
+ ASSERT_TRUE(response->cert_request_info);
+ ASSERT_EQ("www.example.com:443",
+ response->cert_request_info->host_and_port);
+
+ // Restart the SSL handshake with the endpoint. This will cause ssl_data4
+ // to be consumed, because |trans| has to first re-establish a connection
+ // with the proxy.
+ rv = trans->RestartWithCertificate(endpoint_cert, &callback);
+ ASSERT_EQ(net::ERR_IO_PENDING, rv);
+
+ // Ensure that the SSL socket for the HTTPS proxy is still configured to
+ // use the proxy client certificate, and NOT the client certificate for
+ // the endpoint.
+ MockSSLClientSocket* proxy_socket =
+ session_deps.socket_factory.GetMockSSLClientSocket(3);
+ ASSERT_TRUE(proxy_socket);
+ ASSERT_EQ("proxy:70", proxy_socket->GetHostAndPort().ToString());
+ ASSERT_TRUE(proxy_socket->GetSSLConfig().send_client_cert);
+ ASSERT_EQ(proxy_cert, proxy_socket->GetSSLConfig().client_cert);
+
+ // Finish the SSL handshake with HTTPS proxy and start the SSL handshake
+ // with the endpoint. This will consume ssl_data5.
+ WaitForSSLCreationObserver wait_for_endpoint_again(&session_deps, 5);
+ MessageLoop::current()->AddTaskObserver(&wait_for_endpoint_again);
+ MessageLoop::current()->Run();
+ MessageLoop::current()->RemoveTaskObserver(&wait_for_endpoint_again);
+
+ // Ensure that the SSL socket for the SSL endpoint is configured to use
+ // the endpoint client certificate, and NOT the client certificate for the
+ // HTTPS proxy.
+ endpoint_socket = session_deps.socket_factory.GetMockSSLClientSocket(4);
+ ASSERT_TRUE(endpoint_socket);
+ ASSERT_EQ("www.example.com:443",
+ endpoint_socket->GetHostAndPort().ToString());
+ ASSERT_TRUE(endpoint_socket->GetSSLConfig().send_client_cert);
+ ASSERT_EQ(endpoint_cert, endpoint_socket->GetSSLConfig().client_cert);
+
+ rv = callback.WaitForResult();
+ ASSERT_EQ(net::OK, rv);
+
+ // Make sure that the request was consumed from data4.
+ response = trans->GetResponseInfo();
+ ASSERT_TRUE(response);
+ EXPECT_EQ(200, response->headers->response_code());
+ EXPECT_EQ(HttpVersion(1, 1), response->headers->GetHttpVersion());
+ std::string response_data;
+ rv = ReadTransaction(trans.get(), &response_data);
+ EXPECT_EQ(net::OK, rv);
+ EXPECT_EQ("hello, world", response_data);
+}
+
} // namespace net
« no previous file with comments | « no previous file | net/socket/socket_test_util.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698