diff options
author | davidben@chromium.org <davidben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-07 01:10:02 +0000 |
---|---|---|
committer | davidben@chromium.org <davidben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-07 01:10:02 +0000 |
commit | 8df162ae8daedfa09971f7b59420212df505a2b7 (patch) | |
tree | b7e71c3ce4d3f4dae7735deb8c0b2d9f046ea582 /net | |
parent | 29810ee54b8cd8b3fd0177c48daffc1c85a94d65 (diff) | |
download | chromium_src-8df162ae8daedfa09971f7b59420212df505a2b7.zip chromium_src-8df162ae8daedfa09971f7b59420212df505a2b7.tar.gz chromium_src-8df162ae8daedfa09971f7b59420212df505a2b7.tar.bz2 |
Add rudimentary support for client auth in testserver.py and unit tests
Nothing fancy for now. Just some tests that ERR_SSL_CLIENT_AUTH_CERT_NEEDED is
returned from the socket layer, and that URLRequest requests a certificate.
R=wtc
BUG=51132,51127
TEST=SSLClientSocketTest.ConnectClientAuthNoCert,HTTPRequestTest.ClientAuthTest
Review URL: http://codereview.chromium.org/3014055
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@55318 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/socket/ssl_client_socket_unittest.cc | 50 | ||||
-rw-r--r-- | net/test/test_server.cc | 7 | ||||
-rw-r--r-- | net/test/test_server.h | 25 | ||||
-rw-r--r-- | net/tools/testserver/testserver.py | 12 | ||||
-rw-r--r-- | net/url_request/url_request_unittest.cc | 46 |
5 files changed, 136 insertions, 4 deletions
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc index a62927e..c242db1 100644 --- a/net/socket/ssl_client_socket_unittest.cc +++ b/net/socket/ssl_client_socket_unittest.cc @@ -38,6 +38,11 @@ class SSLClientSocketTest : public PlatformTest { ASSERT_TRUE(success); } + void StartClientAuthServer() { + server_.set_ssl_client_auth(true); + StartOKServer(); + } + void StartMismatchedServer() { bool success = server_.Start(net::TestServerLauncher::ProtoHTTP, server_.kMismatchedHostName, server_.kOKHTTPSPort, @@ -194,6 +199,51 @@ TEST_F(SSLClientSocketTest, ConnectMismatched) { log.entries(), -1, net::NetLog::TYPE_SSL_CONNECT)); } +// TODO(davidben): Also test providing a certificate. +TEST_F(SSLClientSocketTest, ConnectClientAuthNoCert) { + StartClientAuthServer(); + + net::AddressList addr; + TestCompletionCallback callback; + + net::HostResolver::RequestInfo info(server_.kHostName, server_.kOKHTTPSPort); + int rv = resolver_->Resolve(info, &addr, NULL, NULL, net::BoundNetLog()); + EXPECT_EQ(net::OK, rv); + + net::CapturingNetLog log(net::CapturingNetLog::kUnbounded); + net::ClientSocket* transport = new net::TCPClientSocket(addr, &log); + rv = transport->Connect(&callback); + if (rv == net::ERR_IO_PENDING) + rv = callback.WaitForResult(); + EXPECT_EQ(net::OK, rv); + + scoped_ptr<net::SSLClientSocket> sock( + socket_factory_->CreateSSLClientSocket(transport, + server_.kHostName, kDefaultSSLConfig)); + + EXPECT_FALSE(sock->IsConnected()); + + rv = sock->Connect(&callback); + EXPECT_TRUE(net::LogContainsBeginEvent( + log.entries(), 5, net::NetLog::TYPE_SSL_CONNECT)); + if (rv != net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { + ASSERT_EQ(net::ERR_IO_PENDING, rv); + EXPECT_FALSE(sock->IsConnected()); + EXPECT_FALSE(net::LogContainsEndEvent( + log.entries(), -1, net::NetLog::TYPE_SSL_CONNECT)); + + rv = callback.WaitForResult(); + EXPECT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED, rv); + } + + // We cannot test sock->IsConnected(), as the NSS implementation disconnects + // the socket when it encounters an error, whereas other implementations + // leave it connected. + + EXPECT_TRUE(net::LogContainsEndEvent( + log.entries(), -1, net::NetLog::TYPE_SSL_CONNECT)); +} + // TODO(wtc): Add unit tests for IsConnectedAndIdle: // - Server closes an SSL connection (with a close_notify alert message). // - Server closes the underlying TCP connection directly. diff --git a/net/test/test_server.cc b/net/test/test_server.cc index 683cde2..7a2df0b 100644 --- a/net/test/test_server.cc +++ b/net/test/test_server.cc @@ -60,7 +60,8 @@ const int TestServerLauncher::kBadHTTPSPort = 9666; const wchar_t TestServerLauncher::kCertIssuerName[] = L"Test CA"; TestServerLauncher::TestServerLauncher() - : process_handle_(base::kNullProcessHandle) { + : process_handle_(base::kNullProcessHandle), + ssl_client_auth_(false) { InitCertPath(); } @@ -178,6 +179,8 @@ bool TestServerLauncher::Start(Protocol protocol, command_line.append(file_root_url); command_line.append(L"\""); } + if (ssl_client_auth_) + command_line.append(L" --ssl-client-auth"); if (!LaunchTestServerAsJob(command_line, true, @@ -196,6 +199,8 @@ bool TestServerLauncher::Start(Protocol protocol, command_line.push_back("-f"); if (!cert_path.value().empty()) command_line.push_back("--https=" + cert_path.value()); + if (ssl_client_auth_) + command_line.push_back("--ssl-client-auth"); base::file_handle_mapping_vector no_mappings; LOG(INFO) << "Trying to launch " << command_line[0] << " ..."; diff --git a/net/test/test_server.h b/net/test/test_server.h index 3b21339..989e38d 100644 --- a/net/test/test_server.h +++ b/net/test/test_server.h @@ -76,6 +76,13 @@ class TestServerLauncher { FilePath GetDocumentRootPath() { return document_root_dir_; } + // When Start is called, if protocol is HTTPS and ssl_client_auth_ is true, + // the server will request a client certificate on each connection. Must be + // called before Start to take effect. + void set_ssl_client_auth(bool ssl_client_auth) { + ssl_client_auth_ = ssl_client_auth; + } + // Issuer name of the root cert that should be trusted for the test to work. static const wchar_t kCertIssuerName[]; @@ -123,6 +130,8 @@ class TestServerLauncher { scoped_refptr<X509Certificate> cert_; #endif + bool ssl_client_auth_; + DISALLOW_COPY_AND_ASSIGN(TestServerLauncher); }; @@ -262,6 +271,22 @@ class HTTPSTestServer : public HTTPTestServer { return test_server; } + // Create a server which requests SSL client auth + static scoped_refptr<HTTPSTestServer> CreateClientAuthServer( + const std::wstring& document_root) { + scoped_refptr<HTTPSTestServer> test_server = new HTTPSTestServer(); + FilePath docroot = FilePath::FromWStringHack(document_root); + FilePath certpath = test_server->launcher_.GetOKCertPath(); + test_server->launcher_.set_ssl_client_auth(true); + if (!test_server->Start(net::TestServerLauncher::ProtoHTTP, + net::TestServerLauncher::kHostName, + net::TestServerLauncher::kOKHTTPSPort, + docroot, certpath, std::wstring())) { + return NULL; + } + return test_server; + } + // Create a server with an up to date certificate for the wrong hostname // for this host static scoped_refptr<HTTPSTestServer> CreateMismatchedServer( diff --git a/net/tools/testserver/testserver.py b/net/tools/testserver/testserver.py index e950ff8..0ad5d28 100644 --- a/net/tools/testserver/testserver.py +++ b/net/tools/testserver/testserver.py @@ -57,13 +57,15 @@ class StoppableHTTPServer(BaseHTTPServer.HTTPServer): class HTTPSServer(tlslite.api.TLSSocketServerMixIn, StoppableHTTPServer): """This is a specialization of StoppableHTTPerver that add https support.""" - def __init__(self, server_address, request_hander_class, cert_path): + def __init__(self, server_address, request_hander_class, cert_path, + ssl_client_auth): s = open(cert_path).read() x509 = tlslite.api.X509() x509.parse(s) self.cert_chain = tlslite.api.X509CertChain([x509]) s = open(cert_path).read() self.private_key = tlslite.api.parsePEMKey(s, private=True) + self.ssl_client_auth = ssl_client_auth self.session_cache = tlslite.api.SessionCache() StoppableHTTPServer.__init__(self, server_address, request_hander_class) @@ -73,7 +75,8 @@ class HTTPSServer(tlslite.api.TLSSocketServerMixIn, StoppableHTTPServer): try: tlsConnection.handshakeServer(certChain=self.cert_chain, privateKey=self.private_key, - sessionCache=self.session_cache) + sessionCache=self.session_cache, + reqCert=self.ssl_client_auth) tlsConnection.ignoreAbruptClose = True return True except tlslite.api.TLSError, error: @@ -1190,7 +1193,8 @@ def main(options, args): if not os.path.isfile(options.cert): print 'specified cert file not found: ' + options.cert + ' exiting...' return - server = HTTPSServer(('127.0.0.1', port), TestPageHandler, options.cert) + server = HTTPSServer(('127.0.0.1', port), TestPageHandler, options.cert, + options.ssl_client_auth) print 'HTTPS server started on port %d...' % port else: server = StoppableHTTPServer(('127.0.0.1', port), TestPageHandler) @@ -1255,6 +1259,8 @@ if __name__ == '__main__': help='Specify that https should be used, specify ' 'the path to the cert containing the private key ' 'the server should use.') + option_parser.add_option('', '--ssl-client-auth', action='store_true', + help='Require SSL client auth on every connection.') option_parser.add_option('', '--file-root-url', default='/files/', help='Specify a root URL for files served.') option_parser.add_option('', '--never-die', default=False, diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index 7398214..eb4a9bb 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc @@ -434,6 +434,52 @@ TEST_F(HTTPSRequestTest, HTTPSExpiredTest) { } } +namespace { + +class SSLClientAuthTestDelegate : public TestDelegate { + public: + SSLClientAuthTestDelegate() : on_certificate_requested_count_(0) { + } + virtual void OnCertificateRequested( + URLRequest* request, + net::SSLCertRequestInfo* cert_request_info) { + on_certificate_requested_count_++; + MessageLoop::current()->Quit(); + } + int on_certificate_requested_count() { + return on_certificate_requested_count_; + } + private: + int on_certificate_requested_count_; +}; + +} // namespace + +// TODO(davidben): Test the rest of the code. Specifically, +// - Filtering which certificates to select. +// - Sending a certificate back. +// - Getting a certificate request in an SSL renegotiation sending the +// HTTP request. +TEST_F(HTTPSRequestTest, ClientAuthTest) { + scoped_refptr<HTTPSTestServer> server = + HTTPSTestServer::CreateClientAuthServer(L"net/data/ssl"); + ASSERT_TRUE(NULL != server.get()); + + SSLClientAuthTestDelegate d; + { + TestURLRequest r(server->TestServerPage(""), &d); + + r.Start(); + EXPECT_TRUE(r.is_pending()); + + MessageLoop::current()->Run(); + + EXPECT_EQ(1, d.on_certificate_requested_count()); + EXPECT_FALSE(d.received_data_before_response()); + EXPECT_EQ(0, d.bytes_received()); + } +} + TEST_F(URLRequestTestHTTP, CancelTest) { TestDelegate d; { |