Index: net/socket/ssl_client_socket_unittest.cc |
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc |
index 8d5512d0a381648dec849b58a5f10fa8d176a618..6e9f4b49afc47af7eb40c4611601d2156b80e186 100644 |
--- a/net/socket/ssl_client_socket_unittest.cc |
+++ b/net/socket/ssl_client_socket_unittest.cc |
@@ -290,8 +290,8 @@ void ReadBufferingStreamSocket::OnReadCompleted(int result) { |
// Simulates synchronously receiving an error during Read() or Write() |
class SynchronousErrorStreamSocket : public WrappedStreamSocket { |
public: |
- explicit SynchronousErrorStreamSocket( |
- std::unique_ptr<StreamSocket> transport); |
+ explicit SynchronousErrorStreamSocket(std::unique_ptr<StreamSocket> transport) |
+ : WrappedStreamSocket(std::move(transport)) {} |
~SynchronousErrorStreamSocket() override {} |
// Socket implementation: |
@@ -323,23 +323,15 @@ class SynchronousErrorStreamSocket : public WrappedStreamSocket { |
} |
private: |
- bool have_read_error_; |
- int pending_read_error_; |
+ bool have_read_error_ = false; |
+ int pending_read_error_ = OK; |
- bool have_write_error_; |
- int pending_write_error_; |
+ bool have_write_error_ = false; |
+ int pending_write_error_ = OK; |
DISALLOW_COPY_AND_ASSIGN(SynchronousErrorStreamSocket); |
}; |
-SynchronousErrorStreamSocket::SynchronousErrorStreamSocket( |
- std::unique_ptr<StreamSocket> transport) |
- : WrappedStreamSocket(std::move(transport)), |
- have_read_error_(false), |
- pending_read_error_(OK), |
- have_write_error_(false), |
- pending_write_error_(OK) {} |
- |
int SynchronousErrorStreamSocket::Read(IOBuffer* buf, |
int buf_len, |
const CompletionCallback& callback) { |
@@ -362,7 +354,8 @@ int SynchronousErrorStreamSocket::Write(IOBuffer* buf, |
// semantics). |
class FakeBlockingStreamSocket : public WrappedStreamSocket { |
public: |
- explicit FakeBlockingStreamSocket(std::unique_ptr<StreamSocket> transport); |
+ explicit FakeBlockingStreamSocket(std::unique_ptr<StreamSocket> transport) |
+ : WrappedStreamSocket(std::move(transport)) {} |
~FakeBlockingStreamSocket() override {} |
// Socket implementation: |
@@ -383,6 +376,10 @@ class FakeBlockingStreamSocket : public WrappedStreamSocket { |
void BlockReadResult(); |
void UnblockReadResult(); |
+ // Replaces the pending read with |data|. Returns true on success or false if |
+ // the caller's reads were too small. |
+ bool ReplaceReadResult(const std::string& data); |
+ |
// Waits for the blocked Read() call to be complete at the underlying |
// transport. |
void WaitForReadResult(); |
@@ -402,24 +399,30 @@ class FakeBlockingStreamSocket : public WrappedStreamSocket { |
// Handles completion from the underlying transport read. |
void OnReadCompleted(int result); |
+ // Finishes the current read. |
+ void ReturnReadResult(); |
+ |
// True if read callbacks are blocked. |
- bool should_block_read_; |
+ bool should_block_read_ = false; |
// The buffer for the pending read, or NULL if not consumed. |
scoped_refptr<IOBuffer> pending_read_buf_; |
+ // The size of the pending read buffer, or -1 if not set. |
+ int pending_read_buf_len_ = -1; |
+ |
// The user callback for the pending read call. |
CompletionCallback pending_read_callback_; |
// The result for the blocked read callback, or ERR_IO_PENDING if not |
// completed. |
- int pending_read_result_; |
+ int pending_read_result_ = ERR_IO_PENDING; |
// WaitForReadResult() wait loop. |
std::unique_ptr<base::RunLoop> read_loop_; |
// True if write calls are blocked. |
- bool should_block_write_; |
+ bool should_block_write_ = false; |
// The buffer for the pending write, or NULL if not scheduled. |
scoped_refptr<IOBuffer> pending_write_buf_; |
@@ -428,20 +431,12 @@ class FakeBlockingStreamSocket : public WrappedStreamSocket { |
CompletionCallback pending_write_callback_; |
// The length for the pending write, or -1 if not scheduled. |
- int pending_write_len_; |
+ int pending_write_len_ = -1; |
// WaitForWrite() wait loop. |
std::unique_ptr<base::RunLoop> write_loop_; |
}; |
-FakeBlockingStreamSocket::FakeBlockingStreamSocket( |
- std::unique_ptr<StreamSocket> transport) |
- : WrappedStreamSocket(std::move(transport)), |
- should_block_read_(false), |
- pending_read_result_(ERR_IO_PENDING), |
- should_block_write_(false), |
- pending_write_len_(-1) {} |
- |
int FakeBlockingStreamSocket::Read(IOBuffer* buf, |
int len, |
const CompletionCallback& callback) { |
@@ -452,16 +447,16 @@ int FakeBlockingStreamSocket::Read(IOBuffer* buf, |
int rv = transport_->Read(buf, len, base::Bind( |
&FakeBlockingStreamSocket::OnReadCompleted, base::Unretained(this))); |
- if (rv == ERR_IO_PENDING) { |
+ if (rv == ERR_IO_PENDING || should_block_read_) { |
// Save the callback to be called later. |
pending_read_buf_ = buf; |
+ pending_read_buf_len_ = len; |
pending_read_callback_ = callback; |
- } else if (should_block_read_) { |
- // Save the callback and read result to be called later. |
- pending_read_buf_ = buf; |
- pending_read_callback_ = callback; |
- OnReadCompleted(rv); |
- rv = ERR_IO_PENDING; |
+ // Save the read result. |
+ if (rv != ERR_IO_PENDING) { |
+ OnReadCompleted(rv); |
+ rv = ERR_IO_PENDING; |
+ } |
} |
return rv; |
} |
@@ -499,15 +494,23 @@ void FakeBlockingStreamSocket::UnblockReadResult() { |
DCHECK(should_block_read_); |
should_block_read_ = false; |
- // If the operation is still pending in the underlying transport, immediately |
- // return - OnReadCompleted() will handle invoking the callback once the |
- // transport has completed. |
- if (pending_read_result_ == ERR_IO_PENDING) |
- return; |
- int result = pending_read_result_; |
- pending_read_buf_ = nullptr; |
- pending_read_result_ = ERR_IO_PENDING; |
- base::ResetAndReturn(&pending_read_callback_).Run(result); |
+ // If the operation has since completed, return the result to the caller. |
+ if (pending_read_result_ != ERR_IO_PENDING) |
+ ReturnReadResult(); |
+} |
+ |
+bool FakeBlockingStreamSocket::ReplaceReadResult(const std::string& data) { |
+ DCHECK(should_block_read_); |
+ DCHECK_NE(ERR_IO_PENDING, pending_read_result_); |
+ DCHECK(pending_read_buf_); |
+ DCHECK_NE(-1, pending_read_buf_len_); |
+ |
+ if (static_cast<size_t>(pending_read_buf_len_) < data.size()) |
+ return false; |
+ |
+ memcpy(pending_read_buf_->data(), data.data(), data.size()); |
+ pending_read_result_ = data.size(); |
+ return true; |
} |
void FakeBlockingStreamSocket::WaitForReadResult() { |
@@ -563,20 +566,24 @@ void FakeBlockingStreamSocket::OnReadCompleted(int result) { |
DCHECK_EQ(ERR_IO_PENDING, pending_read_result_); |
DCHECK(!pending_read_callback_.is_null()); |
- if (should_block_read_) { |
- // Store the result so that the callback can be invoked once Unblock() is |
- // called. |
- pending_read_result_ = result; |
+ pending_read_result_ = result; |
- // Stop the WaitForReadResult() call if any. |
+ if (should_block_read_) { |
+ // Defer the result until UnblockReadResult is called. |
if (read_loop_) |
read_loop_->Quit(); |
- } else { |
- // Either the Read() was never blocked or UnblockReadResult() was called |
- // before the Read() completed. Either way, return the result to the caller. |
- pending_read_buf_ = nullptr; |
- base::ResetAndReturn(&pending_read_callback_).Run(result); |
+ return; |
} |
+ |
+ ReturnReadResult(); |
+} |
+ |
+void FakeBlockingStreamSocket::ReturnReadResult() { |
+ int result = pending_read_result_; |
+ pending_read_result_ = ERR_IO_PENDING; |
+ pending_read_buf_ = nullptr; |
+ pending_read_buf_len_ = -1; |
+ base::ResetAndReturn(&pending_read_callback_).Run(result); |
} |
// CountingStreamSocket wraps an existing StreamSocket and maintains a count of |
@@ -1023,6 +1030,25 @@ class SSLClientSocketChannelIDTest : public SSLClientSocketTest { |
std::unique_ptr<ChannelIDService> channel_id_service_; |
}; |
+// Returns a serialized unencrypted TLS 1.2 alert record for the given alert |
+// value. |
+std::string FormatTLS12Alert(uint8_t alert) { |
+ std::string ret; |
+ // ContentType.alert |
+ ret.push_back(21); |
+ // Record-layer version. Assume TLS 1.2. |
+ ret.push_back(0x03); |
+ ret.push_back(0x03); |
+ // Record length. |
+ ret.push_back(0); |
+ ret.push_back(2); |
+ // AlertLevel.fatal. |
+ ret.push_back(2); |
+ // The alert itself. |
+ ret.push_back(alert); |
+ return ret; |
+} |
+ |
} // namespace |
TEST_F(SSLClientSocketTest, Connect) { |
@@ -3240,9 +3266,6 @@ TEST_F(SSLClientSocketTest, SendGoodCert) { |
SSLConfig ssl_config; |
ssl_config.send_client_cert = true; |
ssl_config.client_cert = ImportCertFromFile(certs_dir, "client_1.pem"); |
- |
- // This is required to ensure that signing works with the client |
- // certificate's private key. |
ssl_config.client_private_key = |
LoadPrivateKeyOpenSSL(certs_dir.AppendASCII("client_1.key")); |
@@ -3433,4 +3456,279 @@ TEST_F(SSLClientSocketTest, PKPMoreImportantThanCT) { |
EXPECT_TRUE(sock_->IsConnected()); |
} |
+// Test that handshake_failure alerts at the ServerHello are mapped to |
+// ERR_SSL_VERSION_OR_CIPHER_MISMATCH. |
+TEST_F(SSLClientSocketTest, HandshakeFailureServerHello) { |
+ ASSERT_TRUE(StartTestServer(SpawnedTestServer::SSLOptions())); |
+ |
+ TestCompletionCallback callback; |
+ std::unique_ptr<StreamSocket> real_transport( |
+ new TCPClientSocket(addr(), NULL, NULL, NetLog::Source())); |
+ std::unique_ptr<FakeBlockingStreamSocket> transport( |
+ new FakeBlockingStreamSocket(std::move(real_transport))); |
+ FakeBlockingStreamSocket* raw_transport = transport.get(); |
+ int rv = callback.GetResult(transport->Connect(callback.callback())); |
+ ASSERT_THAT(rv, IsOk()); |
+ |
+ std::unique_ptr<SSLClientSocket> sock(CreateSSLClientSocket( |
+ std::move(transport), spawned_test_server()->host_port_pair(), |
+ SSLConfig())); |
+ |
+ // Connect. Stop before the client processes ServerHello. |
+ raw_transport->BlockReadResult(); |
+ rv = sock->Connect(callback.callback()); |
+ ASSERT_THAT(rv, IsError(ERR_IO_PENDING)); |
+ raw_transport->WaitForReadResult(); |
+ |
+ // Replace it with an alert. |
+ raw_transport->ReplaceReadResult( |
+ FormatTLS12Alert(40 /* AlertDescription.handshake_failure */)); |
+ raw_transport->UnblockReadResult(); |
+ |
+ rv = callback.GetResult(rv); |
+ EXPECT_THAT(rv, IsError(ERR_SSL_VERSION_OR_CIPHER_MISMATCH)); |
+} |
+ |
+// Test that handshake_failure alerts after the ServerHello but without a |
+// CertificateRequest are mapped to ERR_SSL_PROTOCOL_ERROR. |
+TEST_F(SSLClientSocketTest, HandshakeFailureNoClientCerts) { |
+ ASSERT_TRUE(StartTestServer(SpawnedTestServer::SSLOptions())); |
+ |
+ TestCompletionCallback callback; |
+ std::unique_ptr<StreamSocket> real_transport( |
+ new TCPClientSocket(addr(), NULL, NULL, NetLog::Source())); |
+ std::unique_ptr<FakeBlockingStreamSocket> transport( |
+ new FakeBlockingStreamSocket(std::move(real_transport))); |
+ FakeBlockingStreamSocket* raw_transport = transport.get(); |
+ int rv = callback.GetResult(transport->Connect(callback.callback())); |
+ ASSERT_THAT(rv, IsOk()); |
+ |
+ std::unique_ptr<SSLClientSocket> sock(CreateSSLClientSocket( |
+ std::move(transport), spawned_test_server()->host_port_pair(), |
+ SSLConfig())); |
+ |
+ // Connect. Stop before the client processes ServerHello. |
+ raw_transport->BlockReadResult(); |
+ rv = sock->Connect(callback.callback()); |
+ ASSERT_THAT(rv, IsError(ERR_IO_PENDING)); |
+ raw_transport->WaitForReadResult(); |
+ |
+ // Release the ServerHello and wait for the client to write its second flight. |
+ raw_transport->BlockWrite(); |
+ raw_transport->UnblockReadResult(); |
+ raw_transport->WaitForWrite(); |
+ |
+ // Wait for the server's final flight. |
+ raw_transport->BlockReadResult(); |
+ raw_transport->UnblockWrite(); |
+ raw_transport->WaitForReadResult(); |
+ |
+ // Replace it with an alert. |
+ raw_transport->ReplaceReadResult( |
+ FormatTLS12Alert(40 /* AlertDescription.handshake_failure */)); |
+ raw_transport->UnblockReadResult(); |
+ |
+ rv = callback.GetResult(rv); |
+ EXPECT_THAT(rv, IsError(ERR_SSL_PROTOCOL_ERROR)); |
+} |
+ |
+// Test that handshake_failure alerts after the ServerHello map to |
+// ERR_BAD_SSL_CLIENT_AUTH_CERT if a client certificate was requested but not |
+// supplied. TLS does not have an alert for this case, so handshake_failure is |
+// common. See https://crbug.com/646567. |
+TEST_F(SSLClientSocketTest, LateHandshakeFailureMissingClientCerts) { |
+ // Request a client certificate. |
+ SpawnedTestServer::SSLOptions ssl_options; |
+ ssl_options.request_client_certificate = true; |
+ ASSERT_TRUE(StartTestServer(ssl_options)); |
+ |
+ TestCompletionCallback callback; |
+ std::unique_ptr<StreamSocket> real_transport( |
+ new TCPClientSocket(addr(), NULL, NULL, NetLog::Source())); |
+ std::unique_ptr<FakeBlockingStreamSocket> transport( |
+ new FakeBlockingStreamSocket(std::move(real_transport))); |
+ FakeBlockingStreamSocket* raw_transport = transport.get(); |
+ int rv = callback.GetResult(transport->Connect(callback.callback())); |
+ ASSERT_THAT(rv, IsOk()); |
+ |
+ // Send no client certificate. |
+ SSLConfig config; |
+ config.send_client_cert = true; |
+ std::unique_ptr<SSLClientSocket> sock(CreateSSLClientSocket( |
+ std::move(transport), spawned_test_server()->host_port_pair(), config)); |
+ |
+ // Connect. Stop before the client processes ServerHello. |
+ raw_transport->BlockReadResult(); |
+ rv = sock->Connect(callback.callback()); |
+ ASSERT_THAT(rv, IsError(ERR_IO_PENDING)); |
+ raw_transport->WaitForReadResult(); |
+ |
+ // Release the ServerHello and wait for the client to write its second flight. |
+ raw_transport->BlockWrite(); |
+ raw_transport->UnblockReadResult(); |
+ raw_transport->WaitForWrite(); |
+ |
+ // Wait for the server's final flight. |
+ raw_transport->BlockReadResult(); |
+ raw_transport->UnblockWrite(); |
+ raw_transport->WaitForReadResult(); |
+ |
+ // Replace it with an alert. |
+ raw_transport->ReplaceReadResult( |
+ FormatTLS12Alert(40 /* AlertDescription.handshake_failure */)); |
+ raw_transport->UnblockReadResult(); |
+ |
+ rv = callback.GetResult(rv); |
+ EXPECT_THAT(rv, IsError(ERR_BAD_SSL_CLIENT_AUTH_CERT)); |
+} |
+ |
+// Test that handshake_failure alerts after the ServerHello map to |
+// ERR_SSL_PROTOCOL_ERROR if received after sending a client certificate. It is |
+// assumed servers will send a more appropriate alert in this case. |
+TEST_F(SSLClientSocketTest, LateHandshakeFailureSendClientCerts) { |
+ // Request a client certificate. |
+ SpawnedTestServer::SSLOptions ssl_options; |
+ ssl_options.request_client_certificate = true; |
+ ASSERT_TRUE(StartTestServer(ssl_options)); |
+ |
+ TestCompletionCallback callback; |
+ std::unique_ptr<StreamSocket> real_transport( |
+ new TCPClientSocket(addr(), NULL, NULL, NetLog::Source())); |
+ std::unique_ptr<FakeBlockingStreamSocket> transport( |
+ new FakeBlockingStreamSocket(std::move(real_transport))); |
+ FakeBlockingStreamSocket* raw_transport = transport.get(); |
+ int rv = callback.GetResult(transport->Connect(callback.callback())); |
+ ASSERT_THAT(rv, IsOk()); |
+ |
+ // Send a client certificate. |
+ base::FilePath certs_dir = GetTestCertsDirectory(); |
+ SSLConfig config; |
+ config.send_client_cert = true; |
+ config.client_cert = ImportCertFromFile(certs_dir, "client_1.pem"); |
+ config.client_private_key = |
+ LoadPrivateKeyOpenSSL(certs_dir.AppendASCII("client_1.key")); |
+ std::unique_ptr<SSLClientSocket> sock(CreateSSLClientSocket( |
+ std::move(transport), spawned_test_server()->host_port_pair(), config)); |
+ |
+ // Connect. Stop before the client processes ServerHello. |
+ raw_transport->BlockReadResult(); |
+ rv = sock->Connect(callback.callback()); |
+ ASSERT_THAT(rv, IsError(ERR_IO_PENDING)); |
+ raw_transport->WaitForReadResult(); |
+ |
+ // Release the ServerHello and wait for the client to write its second flight. |
+ raw_transport->BlockWrite(); |
+ raw_transport->UnblockReadResult(); |
+ raw_transport->WaitForWrite(); |
+ |
+ // Wait for the server's final flight. |
+ raw_transport->BlockReadResult(); |
+ raw_transport->UnblockWrite(); |
+ raw_transport->WaitForReadResult(); |
+ |
+ // Replace it with an alert. |
+ raw_transport->ReplaceReadResult( |
+ FormatTLS12Alert(40 /* AlertDescription.handshake_failure */)); |
+ raw_transport->UnblockReadResult(); |
+ |
+ rv = callback.GetResult(rv); |
+ EXPECT_THAT(rv, IsError(ERR_SSL_PROTOCOL_ERROR)); |
+} |
+ |
+// Test that access_denied alerts are mapped to ERR_SSL_PROTOCOL_ERROR if |
+// received on a connection not requesting client certificates. This is an |
+// incorrect use of the alert but is common. See https://crbug.com/630883. |
+TEST_F(SSLClientSocketTest, AccessDeniedNoClientCerts) { |
+ ASSERT_TRUE(StartTestServer(SpawnedTestServer::SSLOptions())); |
+ |
+ TestCompletionCallback callback; |
+ std::unique_ptr<StreamSocket> real_transport( |
+ new TCPClientSocket(addr(), NULL, NULL, NetLog::Source())); |
+ std::unique_ptr<FakeBlockingStreamSocket> transport( |
+ new FakeBlockingStreamSocket(std::move(real_transport))); |
+ FakeBlockingStreamSocket* raw_transport = transport.get(); |
+ int rv = callback.GetResult(transport->Connect(callback.callback())); |
+ ASSERT_THAT(rv, IsOk()); |
+ |
+ std::unique_ptr<SSLClientSocket> sock(CreateSSLClientSocket( |
+ std::move(transport), spawned_test_server()->host_port_pair(), |
+ SSLConfig())); |
+ |
+ // Connect. Stop before the client processes ServerHello. |
+ raw_transport->BlockReadResult(); |
+ rv = sock->Connect(callback.callback()); |
+ ASSERT_THAT(rv, IsError(ERR_IO_PENDING)); |
+ raw_transport->WaitForReadResult(); |
+ |
+ // Release the ServerHello and wait for the client to write its second flight. |
+ raw_transport->BlockWrite(); |
+ raw_transport->UnblockReadResult(); |
+ raw_transport->WaitForWrite(); |
+ |
+ // Wait for the server's final flight. |
+ raw_transport->BlockReadResult(); |
+ raw_transport->UnblockWrite(); |
+ raw_transport->WaitForReadResult(); |
+ |
+ // Replace it with an alert. |
+ raw_transport->ReplaceReadResult( |
+ FormatTLS12Alert(49 /* AlertDescription.access_denied */)); |
+ raw_transport->UnblockReadResult(); |
+ |
+ rv = callback.GetResult(rv); |
+ EXPECT_THAT(rv, IsError(ERR_SSL_PROTOCOL_ERROR)); |
+} |
+ |
+// Test that access_denied alerts are mapped to ERR_BAD_SSL_CLIENT_AUTH_CERT if |
+// received on a connection requesting client certificates. |
+TEST_F(SSLClientSocketTest, AccessDeniedClientCerts) { |
+ // Request a client certificate. |
+ SpawnedTestServer::SSLOptions ssl_options; |
+ ssl_options.request_client_certificate = true; |
+ ASSERT_TRUE(StartTestServer(ssl_options)); |
+ |
+ TestCompletionCallback callback; |
+ std::unique_ptr<StreamSocket> real_transport( |
+ new TCPClientSocket(addr(), NULL, NULL, NetLog::Source())); |
+ std::unique_ptr<FakeBlockingStreamSocket> transport( |
+ new FakeBlockingStreamSocket(std::move(real_transport))); |
+ FakeBlockingStreamSocket* raw_transport = transport.get(); |
+ int rv = callback.GetResult(transport->Connect(callback.callback())); |
+ ASSERT_THAT(rv, IsOk()); |
+ |
+ // Send a client certificate. |
+ base::FilePath certs_dir = GetTestCertsDirectory(); |
+ SSLConfig config; |
+ config.send_client_cert = true; |
+ config.client_cert = ImportCertFromFile(certs_dir, "client_1.pem"); |
+ config.client_private_key = |
+ LoadPrivateKeyOpenSSL(certs_dir.AppendASCII("client_1.key")); |
+ std::unique_ptr<SSLClientSocket> sock(CreateSSLClientSocket( |
+ std::move(transport), spawned_test_server()->host_port_pair(), config)); |
+ |
+ // Connect. Stop before the client processes ServerHello. |
+ raw_transport->BlockReadResult(); |
+ rv = sock->Connect(callback.callback()); |
+ ASSERT_THAT(rv, IsError(ERR_IO_PENDING)); |
+ raw_transport->WaitForReadResult(); |
+ |
+ // Release the ServerHello and wait for the client to write its second flight. |
+ raw_transport->BlockWrite(); |
+ raw_transport->UnblockReadResult(); |
+ raw_transport->WaitForWrite(); |
+ |
+ // Wait for the server's final flight. |
+ raw_transport->BlockReadResult(); |
+ raw_transport->UnblockWrite(); |
+ raw_transport->WaitForReadResult(); |
+ |
+ // Replace it with an alert. |
+ raw_transport->ReplaceReadResult( |
+ FormatTLS12Alert(49 /* AlertDescription.access_denied */)); |
+ raw_transport->UnblockReadResult(); |
+ |
+ rv = callback.GetResult(rv); |
+ EXPECT_THAT(rv, IsError(ERR_BAD_SSL_CLIENT_AUTH_CERT)); |
+} |
+ |
} // namespace net |