summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authordavidben@chromium.org <davidben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-07 01:10:02 +0000
committerdavidben@chromium.org <davidben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-07 01:10:02 +0000
commit8df162ae8daedfa09971f7b59420212df505a2b7 (patch)
treeb7e71c3ce4d3f4dae7735deb8c0b2d9f046ea582 /net
parent29810ee54b8cd8b3fd0177c48daffc1c85a94d65 (diff)
downloadchromium_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.cc50
-rw-r--r--net/test/test_server.cc7
-rw-r--r--net/test/test_server.h25
-rw-r--r--net/tools/testserver/testserver.py12
-rw-r--r--net/url_request/url_request_unittest.cc46
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;
{