Chromium Code Reviews| Index: net/socket/ssl_server_socket_unittest.cc |
| diff --git a/net/socket/ssl_server_socket_unittest.cc b/net/socket/ssl_server_socket_unittest.cc |
| index e4eb5d617eb1a762a5cd32d7c53b822dfac467ef..2649827f422ab1b2ed56d8c395903883f8f7191e 100644 |
| --- a/net/socket/ssl_server_socket_unittest.cc |
| +++ b/net/socket/ssl_server_socket_unittest.cc |
| @@ -73,7 +73,7 @@ const char kWrongClientCertFileName[] = "client_2.pem"; |
| const char kWrongClientPrivateKeyFileName[] = "client_2.pk8"; |
| const char kClientCertCAFileName[] = "client_1_ca.pem"; |
| -class FakeDataChannel { |
| +class FakeDataChannel : public base::RefCountedThreadSafe<FakeDataChannel> { |
|
davidben
2016/01/22 23:57:48
Reference-counted mutable classes are usually an i
ryanchung
2016/01/29 23:28:16
Done. Changed it back.
These were used by both the
|
| public: |
| FakeDataChannel() |
| : read_buf_len_(0), |
| @@ -134,6 +134,9 @@ class FakeDataChannel { |
| } |
| private: |
| + ~FakeDataChannel() {} |
| + friend class base::RefCountedThreadSafe<FakeDataChannel>; |
| + |
| void DoReadCallback() { |
| if (read_callback_.is_null() || data_.empty()) |
| return; |
| @@ -188,10 +191,10 @@ class FakeDataChannel { |
| class FakeSocket : public StreamSocket { |
| public: |
| - FakeSocket(FakeDataChannel* incoming_channel, |
| - FakeDataChannel* outgoing_channel) |
| - : incoming_(incoming_channel), |
| - outgoing_(outgoing_channel) { |
| + FakeSocket(const scoped_refptr<FakeDataChannel>& incoming_channel, |
| + const scoped_refptr<FakeDataChannel>& outgoing_channel) { |
| + incoming_ = incoming_channel; |
| + outgoing_ = outgoing_channel; |
| } |
| ~FakeSocket() override {} |
| @@ -269,8 +272,8 @@ class FakeSocket : public StreamSocket { |
| private: |
| BoundNetLog net_log_; |
| - FakeDataChannel* incoming_; |
| - FakeDataChannel* outgoing_; |
| + scoped_refptr<FakeDataChannel> incoming_; |
| + scoped_refptr<FakeDataChannel> outgoing_; |
| DISALLOW_COPY_AND_ASSIGN(FakeSocket); |
| }; |
| @@ -338,10 +341,10 @@ class TestSSLPrivateKey : public SSLPrivateKey { |
| // Verify the correctness of the test helper classes first. |
| TEST(FakeSocketTest, DataTransfer) { |
| // Establish channels between two sockets. |
| - FakeDataChannel channel_1; |
| - FakeDataChannel channel_2; |
| - FakeSocket client(&channel_1, &channel_2); |
| - FakeSocket server(&channel_2, &channel_1); |
| + scoped_refptr<FakeDataChannel> channel_1(new FakeDataChannel()); |
| + scoped_refptr<FakeDataChannel> channel_2(new FakeDataChannel()); |
| + FakeSocket client(channel_1, channel_2); |
| + FakeSocket server(channel_2, channel_1); |
| const char kTestData[] = "testing123"; |
| const int kTestDataSize = strlen(kTestData); |
| @@ -381,45 +384,56 @@ class SSLServerSocketTest : public PlatformTest { |
| : socket_factory_(ClientSocketFactory::GetDefaultFactory()), |
| cert_verifier_(new MockCertVerifier()), |
| client_cert_verifier_(new MockClientCertVerifier()), |
| - transport_security_state_(new TransportSecurityState) { |
| + transport_security_state_(new TransportSecurityState), |
| + initialized_(false) { |
| cert_verifier_->set_default_result(ERR_CERT_AUTHORITY_INVALID); |
| client_cert_verifier_->set_default_result(ERR_CERT_AUTHORITY_INVALID); |
| } |
| protected: |
| - void Initialize() { |
| + void Initialize(bool reinitialize_server_context = false) { |
|
davidben
2016/01/22 23:57:48
Style: no default arguments.
ryanchung
2016/01/29 23:28:16
Done.
|
| + scoped_refptr<FakeDataChannel> channel_1_ = new FakeDataChannel(); |
| + scoped_refptr<FakeDataChannel> channel_2_ = new FakeDataChannel(); |
| scoped_ptr<ClientSocketHandle> client_connection(new ClientSocketHandle); |
| client_connection->SetSocket( |
| - scoped_ptr<StreamSocket>(new FakeSocket(&channel_1_, &channel_2_))); |
| + scoped_ptr<StreamSocket>(new FakeSocket(channel_1_, channel_2_))); |
| scoped_ptr<StreamSocket> server_socket( |
| - new FakeSocket(&channel_2_, &channel_1_)); |
| + new FakeSocket(channel_2_, channel_1_)); |
| std::string server_cert_der; |
| - scoped_refptr<X509Certificate> server_cert( |
| - ReadTestCert("unittest.selfsigned.der", &server_cert_der)); |
| - scoped_ptr<crypto::RSAPrivateKey> server_private_key( |
| - ReadTestKey("unittest.key.bin")); |
| + if (!initialized_ || reinitialize_server_context) { |
| + scoped_refptr<X509Certificate> server_cert( |
| + ReadTestCert("unittest.selfsigned.der", &server_cert_der)); |
| + scoped_ptr<crypto::RSAPrivateKey> server_private_key( |
| + ReadTestKey("unittest.key.bin")); |
| + server_context_ = CreateSSLServerSocketContext( |
| + server_cert.get(), *server_private_key, server_ssl_config_); |
| + } |
| + |
| + if (!initialized_) { |
| + client_ssl_config_.false_start_enabled = false; |
| + client_ssl_config_.channel_id_enabled = false; |
| - client_ssl_config_.false_start_enabled = false; |
| - client_ssl_config_.channel_id_enabled = false; |
| + // Certificate provided by the host doesn't need authority. |
| + SSLConfig::CertAndStatus cert_and_status; |
| + cert_and_status.cert_status = CERT_STATUS_AUTHORITY_INVALID; |
| + cert_and_status.der_cert = server_cert_der; |
| + client_ssl_config_.allowed_bad_certs.push_back(cert_and_status); |
| - // Certificate provided by the host doesn't need authority. |
| - SSLConfig::CertAndStatus cert_and_status; |
| - cert_and_status.cert_status = CERT_STATUS_AUTHORITY_INVALID; |
| - cert_and_status.der_cert = server_cert_der; |
| - client_ssl_config_.allowed_bad_certs.push_back(cert_and_status); |
| + socket_factory_->ClearSSLSessionCache(); |
| + } |
| HostPortPair host_and_pair("unittest", 0); |
| SSLClientSocketContext context; |
| context.cert_verifier = cert_verifier_.get(); |
| context.transport_security_state = transport_security_state_.get(); |
| - socket_factory_->ClearSSLSessionCache(); |
| client_socket_ = socket_factory_->CreateSSLClientSocket( |
| std::move(client_connection), host_and_pair, client_ssl_config_, |
| context); |
| + |
| server_socket_ = |
| - CreateSSLServerSocket(std::move(server_socket), server_cert.get(), |
| - *server_private_key, server_ssl_config_); |
| + server_context_->CreateSSLServerSocket(std::move(server_socket)); |
| + initialized_ = true; |
| } |
| void ConfigureClientCertsForClient(const char* cert_file_name, |
| @@ -488,8 +502,6 @@ class SSLServerSocketTest : public PlatformTest { |
| return crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_vector); |
| } |
| - FakeDataChannel channel_1_; |
| - FakeDataChannel channel_2_; |
| SSLConfig client_ssl_config_; |
| SSLServerConfig server_ssl_config_; |
| scoped_ptr<SSLClientSocket> client_socket_; |
| @@ -498,7 +510,9 @@ class SSLServerSocketTest : public PlatformTest { |
| scoped_ptr<MockCertVerifier> cert_verifier_; |
| scoped_ptr<MockClientCertVerifier> client_cert_verifier_; |
| scoped_ptr<TransportSecurityState> transport_security_state_; |
| + scoped_ptr<SSLServerSocketContext> server_context_; |
| CertificateList trusted_certs_; |
| + bool initialized_; |
| }; |
| // This test only executes creation of client and server sockets. This is to |
| @@ -546,6 +560,102 @@ TEST_F(SSLServerSocketTest, Handshake) { |
| EXPECT_TRUE(is_aead); |
| } |
| +// This tests makes sure the session cache is working |
| +TEST_F(SSLServerSocketTest, HandshakeCached) { |
| + Initialize(); |
| + |
| + TestCompletionCallback handshake_callback; |
| + int server_ret = server_socket_->Handshake(handshake_callback.callback()); |
| + ASSERT_TRUE(server_ret == OK || server_ret == ERR_IO_PENDING); |
|
davidben
2016/01/22 23:57:48
I wouldn't bother with assertions like this one. T
ryanchung
2016/01/29 23:28:16
Done.
|
| + |
| + TestCompletionCallback connect_callback; |
| + int client_ret = client_socket_->Connect(connect_callback.callback()); |
| + ASSERT_TRUE(client_ret == OK || client_ret == ERR_IO_PENDING); |
| + |
| + client_ret = connect_callback.GetResult(client_ret); |
| + server_ret = handshake_callback.GetResult(server_ret); |
| + |
| + ASSERT_EQ(OK, client_ret); |
| + ASSERT_EQ(OK, server_ret); |
| + |
| + // Make sure the cert status is expected. |
| + SSLInfo ssl_info; |
| + ASSERT_TRUE(client_socket_->GetSSLInfo(&ssl_info)); |
| + EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, ssl_info.cert_status); |
| + EXPECT_EQ(ssl_info.handshake_type, SSLInfo::HANDSHAKE_FULL); |
| + |
| + // Make sure the second connection is cached |
| + Initialize(); |
| + |
| + TestCompletionCallback handshake_callback2; |
| + int server_ret2 = server_socket_->Handshake(handshake_callback2.callback()); |
| + ASSERT_TRUE(server_ret2 == OK || server_ret2 == ERR_IO_PENDING); |
| + |
| + TestCompletionCallback connect_callback2; |
| + int client_ret2 = client_socket_->Connect(connect_callback2.callback()); |
| + ASSERT_TRUE(client_ret2 == OK || client_ret2 == ERR_IO_PENDING); |
| + |
| + client_ret2 = connect_callback2.GetResult(client_ret2); |
| + server_ret2 = handshake_callback2.GetResult(server_ret2); |
| + |
| + ASSERT_EQ(OK, client_ret2); |
| + ASSERT_EQ(OK, server_ret2); |
| + |
| + // Make sure the cert status is expected. |
| + SSLInfo ssl_info2; |
| + ASSERT_TRUE(client_socket_->GetSSLInfo(&ssl_info2)); |
| + EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, ssl_info2.cert_status); |
| + EXPECT_EQ(ssl_info2.handshake_type, SSLInfo::HANDSHAKE_RESUME); |
| +} |
| + |
| +// This tests makes sure the session cache separates out by server context |
| +TEST_F(SSLServerSocketTest, HandshakeCachedContextSwitch) { |
| + Initialize(); |
| + |
| + TestCompletionCallback handshake_callback; |
| + int server_ret = server_socket_->Handshake(handshake_callback.callback()); |
| + ASSERT_TRUE(server_ret == OK || server_ret == ERR_IO_PENDING); |
| + |
| + TestCompletionCallback connect_callback; |
| + int client_ret = client_socket_->Connect(connect_callback.callback()); |
| + ASSERT_TRUE(client_ret == OK || client_ret == ERR_IO_PENDING); |
| + |
| + client_ret = connect_callback.GetResult(client_ret); |
| + server_ret = handshake_callback.GetResult(server_ret); |
| + |
| + ASSERT_EQ(OK, client_ret); |
| + ASSERT_EQ(OK, server_ret); |
| + |
| + // Make sure the cert status is expected. |
| + SSLInfo ssl_info; |
| + ASSERT_TRUE(client_socket_->GetSSLInfo(&ssl_info)); |
| + EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, ssl_info.cert_status); |
| + EXPECT_EQ(ssl_info.handshake_type, SSLInfo::HANDSHAKE_FULL); |
| + |
| + // Make sure the second connection is NOT cached when using a new context. |
| + Initialize(true); |
| + |
| + TestCompletionCallback handshake_callback2; |
| + int server_ret2 = server_socket_->Handshake(handshake_callback2.callback()); |
| + ASSERT_TRUE(server_ret2 == OK || server_ret2 == ERR_IO_PENDING); |
| + |
| + TestCompletionCallback connect_callback2; |
| + int client_ret2 = client_socket_->Connect(connect_callback2.callback()); |
| + ASSERT_TRUE(client_ret2 == OK || client_ret2 == ERR_IO_PENDING); |
| + |
| + client_ret2 = connect_callback2.GetResult(client_ret2); |
| + server_ret2 = handshake_callback2.GetResult(server_ret2); |
| + |
| + ASSERT_EQ(OK, client_ret2); |
| + ASSERT_EQ(OK, server_ret2); |
| + |
| + // Make sure the cert status is expected. |
| + SSLInfo ssl_info2; |
| + ASSERT_TRUE(client_socket_->GetSSLInfo(&ssl_info2)); |
| + EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, ssl_info2.cert_status); |
| + EXPECT_EQ(ssl_info.handshake_type, SSLInfo::HANDSHAKE_FULL); |
| +} |
| + |
| // NSS ports don't support client certificates |
| #if defined(USE_OPENSSL) |
| @@ -582,6 +692,69 @@ TEST_F(SSLServerSocketTest, HandshakeWithClientCert) { |
| EXPECT_TRUE(client_cert->Equals(ssl_info.cert.get())); |
| } |
| +// This test executes Connect() on SSLClientSocket and Handshake() twice on |
| +// SSLServerSocket to make sure handshaking between the two sockets is |
| +// completed successfully, using client certificate. The second connection is |
| +// expected to succeed through the session cache. |
| +TEST_F(SSLServerSocketTest, HandshakeWithClientCertCached) { |
| + scoped_refptr<X509Certificate> client_cert = |
| + ImportCertFromFile(GetTestCertsDirectory(), kClientCertFileName); |
| + ConfigureClientCertsForClient(kClientCertFileName, kClientPrivateKeyFileName); |
| + ConfigureClientCertsForServer(true); |
| + Initialize(); |
| + |
| + TestCompletionCallback handshake_callback; |
| + int server_ret = server_socket_->Handshake(handshake_callback.callback()); |
| + ASSERT_TRUE(server_ret == OK || server_ret == ERR_IO_PENDING); |
| + |
| + TestCompletionCallback connect_callback; |
| + int client_ret = client_socket_->Connect(connect_callback.callback()); |
| + ASSERT_TRUE(client_ret == OK || client_ret == ERR_IO_PENDING); |
| + |
| + client_ret = connect_callback.GetResult(client_ret); |
| + server_ret = handshake_callback.GetResult(server_ret); |
| + |
| + ASSERT_EQ(OK, client_ret); |
| + ASSERT_EQ(OK, server_ret); |
| + |
| + // Make sure the cert status is expected. |
| + SSLInfo ssl_info; |
| + client_socket_->GetSSLInfo(&ssl_info); |
| + EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, ssl_info.cert_status); |
| + server_socket_->GetSSLInfo(&ssl_info); |
| + EXPECT_TRUE(ssl_info.cert.get()); |
| + EXPECT_TRUE(client_cert->Equals(ssl_info.cert.get())); |
| + EXPECT_EQ(ssl_info.handshake_type, SSLInfo::HANDSHAKE_FULL); |
| + server_socket_->Disconnect(); |
| + client_socket_->Disconnect(); |
| + |
| + // Create the connection again |
| + Initialize(); |
| + |
| + TestCompletionCallback handshake_callback2; |
| + int server_ret2 = server_socket_->Handshake(handshake_callback2.callback()); |
| + ASSERT_TRUE(server_ret2 == OK || server_ret2 == ERR_IO_PENDING); |
| + |
| + TestCompletionCallback connect_callback2; |
| + int client_ret2 = client_socket_->Connect(connect_callback2.callback()); |
| + ASSERT_TRUE(client_ret2 == OK || client_ret2 == ERR_IO_PENDING); |
| + |
| + client_ret2 = connect_callback2.GetResult(client_ret2); |
| + server_ret2 = handshake_callback2.GetResult(server_ret2); |
| + |
| + ASSERT_EQ(OK, client_ret2); |
| + ASSERT_EQ(OK, server_ret2); |
| + |
| + // Make sure the cert status is expected. |
| + SSLInfo ssl_info2; |
| + client_socket_->GetSSLInfo(&ssl_info2); |
| + EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, ssl_info2.cert_status); |
| + server_socket_->GetSSLInfo(&ssl_info2); |
| + EXPECT_TRUE(ssl_info2.cert.get()); |
| + EXPECT_TRUE(client_cert->Equals(ssl_info2.cert.get())); |
| + EXPECT_EQ(ssl_info2.handshake_type, SSLInfo::HANDSHAKE_RESUME); |
| +} |
| + |
| TEST_F(SSLServerSocketTest, HandshakeWithClientCertRequiredNotSupplied) { |
| ConfigureClientCertsForServer(true); |
| Initialize(); |
| @@ -614,6 +787,62 @@ TEST_F(SSLServerSocketTest, HandshakeWithClientCertRequiredNotSupplied) { |
| EXPECT_EQ(ERR_FAILED, handshake_callback.GetResult(server_ret)); |
| } |
| +TEST_F(SSLServerSocketTest, HandshakeWithClientCertRequiredNotSuppliedCached) { |
| + ConfigureClientCertsForServer(true); |
| + Initialize(); |
| + // Use the default setting for the client socket, which is to not send |
| + // a client certificate. This will cause the client to receive an |
| + // ERR_SSL_CLIENT_AUTH_CERT_NEEDED error, and allow for inspecting the |
| + // requested cert_authorities from the CertificateRequest sent by the |
| + // server. |
| + |
| + TestCompletionCallback handshake_callback; |
| + int server_ret = server_socket_->Handshake(handshake_callback.callback()); |
| + ASSERT_EQ(server_ret, ERR_IO_PENDING); |
| + |
| + TestCompletionCallback connect_callback; |
| + EXPECT_EQ(ERR_SSL_CLIENT_AUTH_CERT_NEEDED, |
| + connect_callback.GetResult( |
| + client_socket_->Connect(connect_callback.callback()))); |
| + |
| + scoped_refptr<SSLCertRequestInfo> request_info = new SSLCertRequestInfo(); |
| + client_socket_->GetSSLCertRequestInfo(request_info.get()); |
| + |
| + // Check that the authority name that arrived in the CertificateRequest |
| + // handshake message is as expected. |
| + scoped_refptr<X509Certificate> client_cert = |
| + ImportCertFromFile(GetTestCertsDirectory(), kClientCertFileName); |
| + EXPECT_TRUE(client_cert->IsIssuedByEncoded(request_info->cert_authorities)); |
| + |
| + client_socket_->Disconnect(); |
| + |
| + EXPECT_EQ(ERR_FAILED, handshake_callback.GetResult(server_ret)); |
| + server_socket_->Disconnect(); |
| + |
| + // Below, check that the cache didn't store the result of a failed handshake |
| + Initialize(); |
| + |
| + TestCompletionCallback handshake_callback2; |
| + int server_ret2 = server_socket_->Handshake(handshake_callback2.callback()); |
| + ASSERT_EQ(server_ret2, ERR_IO_PENDING); |
| + |
| + TestCompletionCallback connect_callback2; |
| + EXPECT_EQ(ERR_SSL_CLIENT_AUTH_CERT_NEEDED, |
| + connect_callback2.GetResult( |
| + client_socket_->Connect(connect_callback2.callback()))); |
| + |
| + scoped_refptr<SSLCertRequestInfo> request_info2 = new SSLCertRequestInfo(); |
| + client_socket_->GetSSLCertRequestInfo(request_info2.get()); |
| + |
| + // Check that the authority name that arrived in the CertificateRequest |
| + // handshake message is as expected. |
| + EXPECT_TRUE(client_cert->IsIssuedByEncoded(request_info2->cert_authorities)); |
| + |
| + client_socket_->Disconnect(); |
| + |
| + EXPECT_EQ(ERR_FAILED, handshake_callback2.GetResult(server_ret2)); |
| +} |
| + |
| TEST_F(SSLServerSocketTest, HandshakeWithWrongClientCertSupplied) { |
| scoped_refptr<X509Certificate> client_cert = |
| ImportCertFromFile(GetTestCertsDirectory(), kClientCertFileName); |
| @@ -634,6 +863,45 @@ TEST_F(SSLServerSocketTest, HandshakeWithWrongClientCertSupplied) { |
| EXPECT_EQ(ERR_BAD_SSL_CLIENT_AUTH_CERT, |
| handshake_callback.GetResult(server_ret)); |
| } |
| + |
| +TEST_F(SSLServerSocketTest, HandshakeWithWrongClientCertSuppliedCached) { |
| + scoped_refptr<X509Certificate> client_cert = |
| + ImportCertFromFile(GetTestCertsDirectory(), kClientCertFileName); |
| + ConfigureClientCertsForClient(kWrongClientCertFileName, |
| + kWrongClientPrivateKeyFileName); |
| + ConfigureClientCertsForServer(true); |
| + Initialize(); |
| + |
| + TestCompletionCallback handshake_callback; |
| + int server_ret = server_socket_->Handshake(handshake_callback.callback()); |
| + EXPECT_EQ(server_ret, ERR_IO_PENDING); |
| + |
| + TestCompletionCallback connect_callback; |
| + int client_ret = client_socket_->Connect(connect_callback.callback()); |
| + |
| + EXPECT_EQ(ERR_BAD_SSL_CLIENT_AUTH_CERT, |
| + connect_callback.GetResult(client_ret)); |
| + EXPECT_EQ(ERR_BAD_SSL_CLIENT_AUTH_CERT, |
| + handshake_callback.GetResult(server_ret)); |
| + |
| + client_socket_->Disconnect(); |
| + server_socket_->Disconnect(); |
| + |
| + // Below, check that the cache didn't store the result of a failed handshake |
| + Initialize(); |
| + |
| + TestCompletionCallback handshake_callback2; |
| + int server_ret2 = server_socket_->Handshake(handshake_callback2.callback()); |
| + EXPECT_EQ(server_ret, ERR_IO_PENDING); |
| + |
| + TestCompletionCallback connect_callback2; |
| + int client_ret2 = client_socket_->Connect(connect_callback2.callback()); |
| + |
| + EXPECT_EQ(ERR_BAD_SSL_CLIENT_AUTH_CERT, |
| + connect_callback2.GetResult(client_ret2)); |
| + EXPECT_EQ(ERR_BAD_SSL_CLIENT_AUTH_CERT, |
| + handshake_callback2.GetResult(server_ret2)); |
| +} |
| #endif // defined(USE_OPENSSL) |
| TEST_F(SSLServerSocketTest, DataTransfer) { |