diff options
author | joedow <joedow@chromium.org> | 2016-03-14 13:12:43 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-14 20:14:08 +0000 |
commit | 99d59d48f1f9c205388657ba465bac11b8f9d747 (patch) | |
tree | 7f2fc6c8052b3c7c795e2151a2a3fd2cdd3bec71 /remoting/host/security_key | |
parent | ce3c4cbf6353f9953cc71edfc9f5e49ea4ecd248 (diff) | |
download | chromium_src-99d59d48f1f9c205388657ba465bac11b8f9d747.zip chromium_src-99d59d48f1f9c205388657ba465bac11b8f9d747.tar.gz chromium_src-99d59d48f1f9c205388657ba465bac11b8f9d747.tar.bz2 |
Implementing RemoteSecurityKeyIpcServer class and tests.
This change introduces the IPC Server class used to communicate with the
remote_security_key process for SK forwarding. It also includes unit
tests and some facilities that will be used in the subsequent change
which will implement the GnubbyAuthHandlerWin class.
BUG=584463
Review URL: https://codereview.chromium.org/1757873003
Cr-Commit-Position: refs/heads/master@{#381054}
Diffstat (limited to 'remoting/host/security_key')
12 files changed, 795 insertions, 9 deletions
diff --git a/remoting/host/security_key/fake_remote_security_key_ipc_client.cc b/remoting/host/security_key/fake_remote_security_key_ipc_client.cc new file mode 100644 index 0000000..ba5219f --- /dev/null +++ b/remoting/host/security_key/fake_remote_security_key_ipc_client.cc @@ -0,0 +1,94 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "remoting/host/security_key/fake_remote_security_key_ipc_client.h" + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/run_loop.h" +#include "base/thread_task_runner_handle.h" +#include "ipc/ipc_channel.h" +#include "ipc/ipc_message.h" +#include "ipc/ipc_message_macros.h" +#include "remoting/host/chromoting_messages.h" +#include "remoting/host/security_key/gnubby_auth_handler.h" + +namespace remoting { + +FakeRemoteSecurityKeyIpcClient::FakeRemoteSecurityKeyIpcClient( + base::Closure channel_event_callback) + : channel_event_callback_(channel_event_callback) { + DCHECK(!channel_event_callback_.is_null()); +} + +FakeRemoteSecurityKeyIpcClient::~FakeRemoteSecurityKeyIpcClient() {} + +bool FakeRemoteSecurityKeyIpcClient::Connect(const std::string& channel_name) { + // The retry loop is needed as the IPC Servers we connect to are reset (torn + // down and recreated) in some tests and we should be resilient in that case. + IPC::ChannelHandle channel_handle(channel_name); + for (int i = 0; i < 5; i++) { + client_channel_ = IPC::Channel::CreateClient(channel_handle, this); + if (client_channel_->Connect()) { + return true; + } + + base::RunLoop run_loop; + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, run_loop.QuitClosure(), + base::TimeDelta::FromMilliseconds(100)); + run_loop.Run(); + } + + return false; +} + +void FakeRemoteSecurityKeyIpcClient::CloseChannel() { + client_channel_.reset(); + channel_event_callback_.Run(); +} + +void FakeRemoteSecurityKeyIpcClient::SendRequest( + const std::string& request_payload) { + client_channel_->Send( + new ChromotingRemoteSecurityKeyToNetworkMsg_Request(request_payload)); +} + +bool FakeRemoteSecurityKeyIpcClient::OnMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(FakeRemoteSecurityKeyIpcClient, message) + IPC_MESSAGE_HANDLER( + ChromotingNetworkToRemoteSecurityKeyMsg_ConnectionDetails, + OnConnectionDetails) + IPC_MESSAGE_HANDLER(ChromotingNetworkToRemoteSecurityKeyMsg_Response, + OnSecurityKeyResponse) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + CHECK(handled) << "Received unexpected IPC type: " << message.type(); + return handled; +} + +void FakeRemoteSecurityKeyIpcClient::OnChannelConnected(int32_t peer_pid) { + channel_event_callback_.Run(); +} + +void FakeRemoteSecurityKeyIpcClient::OnChannelError() { + channel_event_callback_.Run(); +} + +void FakeRemoteSecurityKeyIpcClient::OnConnectionDetails( + const std::string& channel_name) { + last_message_received_ = channel_name; + channel_event_callback_.Run(); +} + +void FakeRemoteSecurityKeyIpcClient::OnSecurityKeyResponse( + const std::string& request_data) { + last_message_received_ = request_data; + channel_event_callback_.Run(); +} + +} // namespace remoting diff --git a/remoting/host/security_key/fake_remote_security_key_ipc_client.h b/remoting/host/security_key/fake_remote_security_key_ipc_client.h new file mode 100644 index 0000000..24305ea --- /dev/null +++ b/remoting/host/security_key/fake_remote_security_key_ipc_client.h @@ -0,0 +1,70 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_HOST_SECURITY_KEY_FAKE_SECURITY_KEY_IPC_CLIENT_H_ +#define REMOTING_HOST_SECURITY_KEY_FAKE_SECURITY_KEY_IPC_CLIENT_H_ + +#include <string> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "ipc/ipc_listener.h" + +namespace IPC { +class Channel; +class Message; +} + +namespace remoting { + +// Simulates the RemoteSecurityKeyIpcClient and provides access to data members +// for testing. +class FakeRemoteSecurityKeyIpcClient : public IPC::Listener { + public: + explicit FakeRemoteSecurityKeyIpcClient(base::Closure channel_event_callback); + ~FakeRemoteSecurityKeyIpcClient() override; + + // Connects as a client to the |channel_name| IPC Channel. + bool Connect(const std::string& channel_name); + + // Closes the |client_channel_| IPC channel. + void CloseChannel(); + + // Sends a security key request message via IPC through |client_channel_|. + void SendRequest(const std::string& request_data); + + // Provides access to |last_message_received_| for testing. + const std::string& last_message_received() const { + return last_message_received_; + } + + private: + // IPC::Listener implementation. + bool OnMessageReceived(const IPC::Message& message) override; + void OnChannelConnected(int32_t peer_pid) override; + void OnChannelError() override; + + // Handles the initial IPC message used to establish a side channel with this + // IPC Client instance. + void OnConnectionDetails(const std::string& request_data); + + // Handles security key response IPC messages. + void OnSecurityKeyResponse(const std::string& request_data); + + // Called when a change in the IPC channel state has occurred. + base::Closure channel_event_callback_; + + // Used for sending/receiving security key messages between processes. + scoped_ptr<IPC::Channel> client_channel_; + + // Provides the contents of the last IPC message received. + std::string last_message_received_; + + DISALLOW_COPY_AND_ASSIGN(FakeRemoteSecurityKeyIpcClient); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_SECURITY_KEY_FAKE_SECURITY_KEY_IPC_CLIENT_H_
\ No newline at end of file diff --git a/remoting/host/security_key/gnubby_auth_handler.h b/remoting/host/security_key/gnubby_auth_handler.h index 4c7ed25..fe6dff0 100644 --- a/remoting/host/security_key/gnubby_auth_handler.h +++ b/remoting/host/security_key/gnubby_auth_handler.h @@ -7,12 +7,12 @@ #include <string> -#include "base/callback_forward.h" +#include "base/callback.h" #include "base/memory/scoped_ptr.h" +#include "base/time/time.h" namespace base { class FilePath; -class TimeDelta; } // namespace base namespace remoting { @@ -59,7 +59,7 @@ class GnubbyAuthHandler { virtual size_t GetActiveConnectionCountForTest() const = 0; // Sets the timeout used when waiting for a gnubby response. - virtual void SetRequestTimeoutForTest(const base::TimeDelta& timeout) = 0; + virtual void SetRequestTimeoutForTest(base::TimeDelta timeout) = 0; }; } // namespace remoting diff --git a/remoting/host/security_key/gnubby_auth_handler_linux.cc b/remoting/host/security_key/gnubby_auth_handler_linux.cc index f036434..fdde958 100644 --- a/remoting/host/security_key/gnubby_auth_handler_linux.cc +++ b/remoting/host/security_key/gnubby_auth_handler_linux.cc @@ -1,6 +1,7 @@ // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. + #include <stdint.h> #include <unistd.h> @@ -64,7 +65,7 @@ class GnubbyAuthHandlerLinux : public GnubbyAuthHandler { void SendErrorAndCloseConnection(int gnubby_connection_id) override; void SetSendMessageCallback(const SendMessageCallback& callback) override; size_t GetActiveConnectionCountForTest() const override; - void SetRequestTimeoutForTest(const base::TimeDelta& timeout) override; + void SetRequestTimeoutForTest(base::TimeDelta timeout) override; // Starts listening for connection. void DoAccept(); @@ -200,8 +201,7 @@ size_t GnubbyAuthHandlerLinux::GetActiveConnectionCountForTest() const { return active_sockets_.size(); } -void GnubbyAuthHandlerLinux::SetRequestTimeoutForTest( - const base::TimeDelta& timeout) { +void GnubbyAuthHandlerLinux::SetRequestTimeoutForTest(base::TimeDelta timeout) { request_timeout_ = timeout; } diff --git a/remoting/host/security_key/gnubby_extension_session_unittest.cc b/remoting/host/security_key/gnubby_extension_session_unittest.cc index f763670..ceb76e1 100644 --- a/remoting/host/security_key/gnubby_extension_session_unittest.cc +++ b/remoting/host/security_key/gnubby_extension_session_unittest.cc @@ -71,7 +71,7 @@ class TestClientStub : public protocol::ClientStub { // protocol::CursorShapeStub implementation. void SetCursorShape(const protocol::CursorShapeInfo& cursor_shape) override {} - void WaitForDeliverHostMessage(const base::TimeDelta& max_timeout) { + void WaitForDeliverHostMessage(base::TimeDelta max_timeout) { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, run_loop_->QuitClosure(), max_timeout); run_loop_->Run(); diff --git a/remoting/host/security_key/gnubby_socket.cc b/remoting/host/security_key/gnubby_socket.cc index 0a24688..371a352 100644 --- a/remoting/host/security_key/gnubby_socket.cc +++ b/remoting/host/security_key/gnubby_socket.cc @@ -27,7 +27,7 @@ const char kSshError[] = {0x05}; } // namespace GnubbySocket::GnubbySocket(scoped_ptr<net::StreamSocket> socket, - const base::TimeDelta& timeout, + base::TimeDelta timeout, const base::Closure& timeout_callback) : socket_(std::move(socket)), read_completed_(false), diff --git a/remoting/host/security_key/gnubby_socket.h b/remoting/host/security_key/gnubby_socket.h index 39d5d05..f56bf33 100644 --- a/remoting/host/security_key/gnubby_socket.h +++ b/remoting/host/security_key/gnubby_socket.h @@ -34,7 +34,7 @@ namespace remoting { class GnubbySocket { public: GnubbySocket(scoped_ptr<net::StreamSocket> socket, - const base::TimeDelta& timeout, + base::TimeDelta timeout, const base::Closure& timeout_callback); ~GnubbySocket(); diff --git a/remoting/host/security_key/remote_security_key_ipc_server.cc b/remoting/host/security_key/remote_security_key_ipc_server.cc new file mode 100644 index 0000000..62d54c8 --- /dev/null +++ b/remoting/host/security_key/remote_security_key_ipc_server.cc @@ -0,0 +1,50 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "remoting/host/security_key/remote_security_key_ipc_server.h" + +#include <string> + +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "base/threading/thread_checker.h" +#include "base/timer/timer.h" +#include "ipc/ipc_channel.h" +#include "ipc/ipc_message.h" +#include "ipc/ipc_message_macros.h" +#include "remoting/base/logging.h" +#include "remoting/host/chromoting_messages.h" +#include "remoting/host/security_key/remote_security_key_ipc_server_impl.h" + +namespace { + +// Not thread safe, tests which set this value must do so on the same thread. +static remoting::RemoteSecurityKeyIpcServerFactory* g_factory = nullptr; + +} // namespace + +namespace remoting { + +void RemoteSecurityKeyIpcServer::SetFactoryForTest( + RemoteSecurityKeyIpcServerFactory* factory) { + g_factory = factory; +} + +scoped_ptr<RemoteSecurityKeyIpcServer> RemoteSecurityKeyIpcServer::Create( + int connection_id, + base::TimeDelta initial_connect_timeout, + const GnubbyAuthHandler::SendMessageCallback& message_callback, + const base::Closure& done_callback) { + scoped_ptr<RemoteSecurityKeyIpcServer> ipc_server = + g_factory + ? g_factory->Create(connection_id, initial_connect_timeout, + message_callback, done_callback) + : make_scoped_ptr(new RemoteSecurityKeyIpcServerImpl( + connection_id, initial_connect_timeout, message_callback, + done_callback)); + + return ipc_server; +} + +} // namespace remoting diff --git a/remoting/host/security_key/remote_security_key_ipc_server.h b/remoting/host/security_key/remote_security_key_ipc_server.h new file mode 100644 index 0000000..cd85541 --- /dev/null +++ b/remoting/host/security_key/remote_security_key_ipc_server.h @@ -0,0 +1,58 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_HOST_SECURITY_KEY_REMOTE_SECURITY_KEY_IPC_SERVER_H_ +#define REMOTING_HOST_SECURITY_KEY_REMOTE_SECURITY_KEY_IPC_SERVER_H_ + +#include <string> + +#include "base/callback_forward.h" +#include "base/memory/scoped_ptr.h" +#include "base/time/time.h" +#include "remoting/host/security_key/gnubby_auth_handler.h" + +namespace remoting { + +class RemoteSecurityKeyIpcServerFactory; + +// Responsible for handing the server end of the IPC channel between the +// network process (server) and the remote_security_key process (client). +class RemoteSecurityKeyIpcServer { + public: + virtual ~RemoteSecurityKeyIpcServer() {} + + // Creates a new RemoteSecurityKeyIpcServer instance. + static scoped_ptr<RemoteSecurityKeyIpcServer> Create( + int connection_id, + base::TimeDelta initial_connect_timeout, + const GnubbyAuthHandler::SendMessageCallback& message_callback, + const base::Closure& done_callback); + + // Used to set a Factory to generate fake/mock RemoteSecurityKeyIpcServer + // instances for testing. + static void SetFactoryForTest(RemoteSecurityKeyIpcServerFactory* factory); + + // Creates and starts listening on an IPC channel with the given name. + virtual bool CreateChannel(const std::string& channel_name, + base::TimeDelta request_timeout) = 0; + + // Sends a security key response IPC message via the IPC channel. + virtual bool SendResponse(const std::string& message_data) = 0; +}; + +// Used to allow for creating Fake/Mock RemoteSecurityKeyIpcServer for testing. +class RemoteSecurityKeyIpcServerFactory { + public: + virtual ~RemoteSecurityKeyIpcServerFactory() {} + + virtual scoped_ptr<RemoteSecurityKeyIpcServer> Create( + int connection_id, + base::TimeDelta connect_timeout, + const GnubbyAuthHandler::SendMessageCallback& message_callback, + const base::Closure& done_callback) = 0; +}; + +} // namespace remoting + +#endif // REMOTING_HOST_SECURITY_KEY_REMOTE_SECURITY_KEY_IPC_SERVER_H_ diff --git a/remoting/host/security_key/remote_security_key_ipc_server_impl.cc b/remoting/host/security_key/remote_security_key_ipc_server_impl.cc new file mode 100644 index 0000000..3d9aaff --- /dev/null +++ b/remoting/host/security_key/remote_security_key_ipc_server_impl.cc @@ -0,0 +1,130 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "remoting/host/security_key/remote_security_key_ipc_server_impl.h" + +#include <string> + +#include "base/callback.h" +#include "base/callback_helpers.h" +#include "base/memory/scoped_ptr.h" +#include "base/threading/thread_checker.h" +#include "base/timer/timer.h" +#include "ipc/ipc_channel.h" +#include "ipc/ipc_message.h" +#include "ipc/ipc_message_macros.h" +#include "remoting/base/logging.h" +#include "remoting/host/chromoting_messages.h" + +namespace { + +// Returns the command code (the first byte of the data) if it exists, or -1 if +// the data is empty. +unsigned int GetCommandCode(const std::string& data) { + return data.empty() ? -1 : static_cast<unsigned int>(data[0]); +} + +} // namespace + +namespace remoting { + +RemoteSecurityKeyIpcServerImpl::RemoteSecurityKeyIpcServerImpl( + int connection_id, + base::TimeDelta initial_connect_timeout, + const GnubbyAuthHandler::SendMessageCallback& message_callback, + const base::Closure& done_callback) + : connection_id_(connection_id), + initial_connect_timeout_(initial_connect_timeout), + done_callback_(done_callback), + message_callback_(message_callback) { + DCHECK_GT(connection_id_, 0); + DCHECK(!done_callback_.is_null()); + DCHECK(!message_callback_.is_null()); +} + +RemoteSecurityKeyIpcServerImpl::~RemoteSecurityKeyIpcServerImpl() {} + +bool RemoteSecurityKeyIpcServerImpl::CreateChannel( + const std::string& channel_name, + base::TimeDelta request_timeout) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!ipc_channel_); + security_key_request_timeout_ = request_timeout; + + ipc_channel_ = + IPC::Channel::CreateServer(IPC::ChannelHandle(channel_name), this); + if (!ipc_channel_->Connect()) { + ipc_channel_.reset(); + return false; + } + + // It is safe to use base::Unretained here as |timer_| will be stopped and + // this task will be removed when this instance is being destroyed. All + // methods must execute on the same thread (due to |thread_Checker_| so + // the posted task and D'Tor can not execute concurrently. + timer_.Start(FROM_HERE, initial_connect_timeout_, + base::Bind(&RemoteSecurityKeyIpcServerImpl::OnChannelError, + base::Unretained(this))); + return true; +} + +bool RemoteSecurityKeyIpcServerImpl::SendResponse(const std::string& response) { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Since we have received a response, we update the timer and wait + // for a subsequent request. + timer_.Start(FROM_HERE, initial_connect_timeout_, + base::Bind(&RemoteSecurityKeyIpcServerImpl::OnChannelError, + base::Unretained(this))); + + return ipc_channel_->Send( + new ChromotingNetworkToRemoteSecurityKeyMsg_Response(response)); +} + +bool RemoteSecurityKeyIpcServerImpl::OnMessageReceived( + const IPC::Message& message) { + DCHECK(thread_checker_.CalledOnValidThread()); + + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(RemoteSecurityKeyIpcServerImpl, message) + IPC_MESSAGE_HANDLER(ChromotingRemoteSecurityKeyToNetworkMsg_Request, + OnSecurityKeyRequest) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + CHECK(handled) << "Received unexpected IPC type: " << message.type(); + return handled; +} + +void RemoteSecurityKeyIpcServerImpl::OnChannelConnected(int32_t peer_pid) { + DCHECK(thread_checker_.CalledOnValidThread()); + // Reset the timer to give the client a chance to send the request. + timer_.Start(FROM_HERE, initial_connect_timeout_, + base::Bind(&RemoteSecurityKeyIpcServerImpl::OnChannelError, + base::Unretained(this))); +} + +void RemoteSecurityKeyIpcServerImpl::OnChannelError() { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (!done_callback_.is_null()) { + // Note: This callback may result in this object being torn down. + base::ResetAndReturn(&done_callback_).Run(); + } +} + +void RemoteSecurityKeyIpcServerImpl::OnSecurityKeyRequest( + const std::string& request_data) { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Reset the timer to give the client a chance to send the response. + timer_.Start(FROM_HERE, security_key_request_timeout_, + base::Bind(&RemoteSecurityKeyIpcServerImpl::OnChannelError, + base::Unretained(this))); + + HOST_LOG << "Received gnubby request: " << GetCommandCode(request_data); + message_callback_.Run(connection_id_, request_data); +} + +} // namespace remoting diff --git a/remoting/host/security_key/remote_security_key_ipc_server_impl.h b/remoting/host/security_key/remote_security_key_ipc_server_impl.h new file mode 100644 index 0000000..6810114 --- /dev/null +++ b/remoting/host/security_key/remote_security_key_ipc_server_impl.h @@ -0,0 +1,87 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_HOST_SECURITY_KEY_REMOTE_SECURITY_KEY_IPC_SERVER_IMPL_H_ +#define REMOTING_HOST_SECURITY_KEY_REMOTE_SECURITY_KEY_IPC_SERVER_IMPL_H_ + +#include "remoting/host/security_key/remote_security_key_ipc_server.h" + +#include <string> + +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "base/threading/thread_checker.h" +#include "base/time/time.h" +#include "base/timer/timer.h" +#include "ipc/ipc_listener.h" + +namespace base { +class TimeDelta; +} // base + +namespace IPC { +class Channel; +class Message; +} // IPC + +namespace remoting { + +// Responsible for handing the server end of the IPC channel between the +// the network process and the local remote_security_key process. +class RemoteSecurityKeyIpcServerImpl : public RemoteSecurityKeyIpcServer, + public IPC::Listener { + public: + RemoteSecurityKeyIpcServerImpl( + int connection_id, + base::TimeDelta initial_connect_timeout, + const GnubbyAuthHandler::SendMessageCallback& message_callback, + const base::Closure& done_callback); + ~RemoteSecurityKeyIpcServerImpl() override; + + // RemoteSecurityKeyIpcServer implementation. + bool CreateChannel(const std::string& channel_name, + base::TimeDelta request_timeout) override; + bool SendResponse(const std::string& message_data) override; + + private: + // IPC::Listener implementation. + bool OnMessageReceived(const IPC::Message& message) override; + void OnChannelConnected(int32_t peer_pid) override; + void OnChannelError() override; + + // Handles security key resquest IPC messages. + void OnSecurityKeyRequest(const std::string& request); + + // The value assigned to identify the current IPC channel. + int connection_id_; + + // Timeout for disconnecting the IPC channel if there is no client activity. + base::TimeDelta initial_connect_timeout_; + + // Timeout for disconnecting the IPC channel if there is no response from + // the remote client after a security key request. + base::TimeDelta security_key_request_timeout_; + + // Used to detect timeouts and disconnect the IPC channel. + base::OneShotTimer timer_; + + // Used to signal that the IPC channel should be disconnected. + base::Closure done_callback_; + + // Used to pass a security key request on to the remote client. + GnubbyAuthHandler::SendMessageCallback message_callback_; + + // Used for sending/receiving security key messages between processes. + scoped_ptr<IPC::Channel> ipc_channel_; + + // Ensures RemoteSecurityKeyIpcServerImpl methods are called on the same + // thread. + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(RemoteSecurityKeyIpcServerImpl); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_SECURITY_KEY_REMOTE_SECURITY_KEY_IPC_SERVER_IMPL_H_
\ No newline at end of file diff --git a/remoting/host/security_key/remote_security_key_ipc_server_unittest.cc b/remoting/host/security_key/remote_security_key_ipc_server_unittest.cc new file mode 100644 index 0000000..8afd68d --- /dev/null +++ b/remoting/host/security_key/remote_security_key_ipc_server_unittest.cc @@ -0,0 +1,297 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "remoting/host/security_key/remote_security_key_ipc_server.h" + +#include <string> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "ipc/ipc_channel.h" +#include "remoting/host/security_key/fake_remote_security_key_ipc_client.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +const int kTestConnectionId = 42; +const int kInitialConnectTimeoutMs = 250; +const int kConnectionTimeoutErrorDeltaMs = 100; +} // namespace + +namespace remoting { + +class RemoteSecurityKeyIpcServerTest : public testing::Test { + public: + RemoteSecurityKeyIpcServerTest(); + ~RemoteSecurityKeyIpcServerTest() override; + + // Passed to the object used for testing to be called back to signal + // completion of an IPC channel state change or reception of an IPC message. + void OperationComplete(); + + protected: + // Returns a unique IPC channel name which prevents conflicts when running + // tests concurrently. + std::string GetUniqueTestChannelName(); + + // Waits until the current |run_loop_| instance is signaled, then resets it. + void WaitForOperationComplete(); + + // Used as a callback to signal receipt of a security key request message. + void SendRequestToClient(int connection_id, const std::string& data); + + // IPC tests require a valid MessageLoop to run. + base::MessageLoopForIO message_loop_; + + // Used to allow |message_loop_| to run during tests. The instance is reset + // after each stage of the tests has been completed. + scoped_ptr<base::RunLoop> run_loop_; + + // The object under test. + scoped_ptr<RemoteSecurityKeyIpcServer> remote_security_key_ipc_server_; + + // Used to validate the object under test uses the correct ID when + // communicating over the IPC channel. + int last_connection_id_received_ = -1; + + // Stores the contents of the last IPC message received for validation. + std::string last_message_received_; + + private: + DISALLOW_COPY_AND_ASSIGN(RemoteSecurityKeyIpcServerTest); +}; + +RemoteSecurityKeyIpcServerTest::RemoteSecurityKeyIpcServerTest() + : run_loop_(new base::RunLoop()) { + remote_security_key_ipc_server_ = + remoting::RemoteSecurityKeyIpcServer::Create( + kTestConnectionId, + base::TimeDelta::FromMilliseconds(kInitialConnectTimeoutMs), + base::Bind(&RemoteSecurityKeyIpcServerTest::SendRequestToClient, + base::Unretained(this)), + base::Bind(&RemoteSecurityKeyIpcServerTest::OperationComplete, + base::Unretained(this))); +} + +RemoteSecurityKeyIpcServerTest::~RemoteSecurityKeyIpcServerTest() {} + +void RemoteSecurityKeyIpcServerTest::OperationComplete() { + run_loop_->Quit(); +} + +void RemoteSecurityKeyIpcServerTest::WaitForOperationComplete() { + run_loop_->Run(); + run_loop_.reset(new base::RunLoop()); +} + +void RemoteSecurityKeyIpcServerTest::SendRequestToClient( + int connection_id, + const std::string& data) { + last_connection_id_received_ = connection_id; + last_message_received_ = data; + OperationComplete(); +} + +std::string RemoteSecurityKeyIpcServerTest::GetUniqueTestChannelName() { + std::string channel_name("Super_Awesome_Test_Channel."); + channel_name.append(IPC::Channel::GenerateUniqueRandomChannelID()); + + return channel_name; +} + +TEST_F(RemoteSecurityKeyIpcServerTest, HandleSingleGnubbyRequest) { + std::string channel_name(GetUniqueTestChannelName()); + ASSERT_TRUE(remote_security_key_ipc_server_->CreateChannel( + channel_name, + /*request_timeout=*/base::TimeDelta::FromMilliseconds(500))); + + // Create a fake client and connect to the IPC server channel. + FakeRemoteSecurityKeyIpcClient fake_ipc_client( + base::Bind(&RemoteSecurityKeyIpcServerTest::OperationComplete, + base::Unretained(this))); + ASSERT_TRUE(fake_ipc_client.Connect(channel_name)); + WaitForOperationComplete(); + + // Send a request from the IPC client to the IPC server. + std::string request_data("Blergh!"); + fake_ipc_client.SendRequest(request_data); + WaitForOperationComplete(); + + // Verify the request was received. + ASSERT_EQ(kTestConnectionId, last_connection_id_received_); + ASSERT_EQ(request_data, last_message_received_); + + // Send a response from the IPC server to the IPC client. + std::string response_data("Blargh!"); + ASSERT_TRUE(remote_security_key_ipc_server_->SendResponse(response_data)); + WaitForOperationComplete(); + + // Verify the request was received. + ASSERT_EQ(response_data, fake_ipc_client.last_message_received()); + + // Typically the client will be the one to close the connection. + fake_ipc_client.CloseChannel(); +} + +TEST_F(RemoteSecurityKeyIpcServerTest, HandleMultipleGnubbyRequests) { + std::string channel_name(GetUniqueTestChannelName()); + ASSERT_TRUE(remote_security_key_ipc_server_->CreateChannel( + channel_name, + /*request_timeout=*/base::TimeDelta::FromMilliseconds(500))); + + // Create a fake client and connect to the IPC server channel. + FakeRemoteSecurityKeyIpcClient fake_ipc_client( + base::Bind(&RemoteSecurityKeyIpcServerTest::OperationComplete, + base::Unretained(this))); + ASSERT_TRUE(fake_ipc_client.Connect(channel_name)); + WaitForOperationComplete(); + + // Send a request from the IPC client to the IPC server. + std::string request_data_1("Blergh!"); + fake_ipc_client.SendRequest(request_data_1); + WaitForOperationComplete(); + + // Verify the request was received. + ASSERT_EQ(kTestConnectionId, last_connection_id_received_); + ASSERT_EQ(request_data_1, last_message_received_); + + // Send a response from the IPC server to the IPC client. + std::string response_data_1("Blargh!"); + ASSERT_TRUE(remote_security_key_ipc_server_->SendResponse(response_data_1)); + WaitForOperationComplete(); + + // Verify the response was received. + ASSERT_EQ(response_data_1, fake_ipc_client.last_message_received()); + + // Send a request from the IPC client to the IPC server. + std::string request_data_2("Bleh!"); + fake_ipc_client.SendRequest(request_data_2); + WaitForOperationComplete(); + + // Verify the request was received. + ASSERT_EQ(kTestConnectionId, last_connection_id_received_); + ASSERT_EQ(request_data_2, last_message_received_); + + // Send a response from the IPC server to the IPC client. + std::string response_data_2("Meh!"); + ASSERT_TRUE(remote_security_key_ipc_server_->SendResponse(response_data_2)); + WaitForOperationComplete(); + + // Verify the response was received. + ASSERT_EQ(response_data_2, fake_ipc_client.last_message_received()); + + // Typically the client will be the one to close the connection. + fake_ipc_client.CloseChannel(); +} + +TEST_F(RemoteSecurityKeyIpcServerTest, InitialIpcConnectionTimeout) { + // Create a channel, then wait for the done callback to be called indicating + // the connection was closed. This test simulates the IPC Server being + // created but the client failing to connect to it. + std::string channel_name(GetUniqueTestChannelName()); + ASSERT_TRUE(remote_security_key_ipc_server_->CreateChannel( + channel_name, + /*request_timeout=*/base::TimeDelta::FromMilliseconds(500))); + base::Time start_time(base::Time::NowFromSystemTime()); + WaitForOperationComplete(); + base::TimeDelta elapsed_time = base::Time::NowFromSystemTime() - start_time; + + ASSERT_NEAR(elapsed_time.InMilliseconds(), kInitialConnectTimeoutMs, + kConnectionTimeoutErrorDeltaMs); +} + +TEST_F(RemoteSecurityKeyIpcServerTest, NoGnubbyRequestTimeout) { + // Create a channel and connect to it via IPC but do not send a request. + // The channel should be closed and cleaned up if the IPC client does not + // issue a request within the specified timeout period. + std::string channel_name(GetUniqueTestChannelName()); + ASSERT_TRUE(remote_security_key_ipc_server_->CreateChannel( + channel_name, + /*request_timeout=*/base::TimeDelta::FromMilliseconds(500))); + + // Create a fake client and connect to the IPC server channel. + FakeRemoteSecurityKeyIpcClient fake_ipc_client( + base::Bind(&RemoteSecurityKeyIpcServerTest::OperationComplete, + base::Unretained(this))); + ASSERT_TRUE(fake_ipc_client.Connect(channel_name)); + WaitForOperationComplete(); + + // Now that a connection has been established, we wait for the timeout. + base::Time start_time(base::Time::NowFromSystemTime()); + WaitForOperationComplete(); + base::TimeDelta elapsed_time = base::Time::NowFromSystemTime() - start_time; + + ASSERT_NEAR(elapsed_time.InMilliseconds(), kInitialConnectTimeoutMs, + kConnectionTimeoutErrorDeltaMs); +} + +TEST_F(RemoteSecurityKeyIpcServerTest, GnubbyResponseTimeout) { + // Create a channel, connect to it via IPC, and issue a request, but do + // not send a response. This simulates a client-side timeout. + base::TimeDelta request_timeout(base::TimeDelta::FromMilliseconds(50)); + std::string channel_name(GetUniqueTestChannelName()); + ASSERT_TRUE(remote_security_key_ipc_server_->CreateChannel(channel_name, + request_timeout)); + + // Create a fake client and connect to the IPC server channel. + FakeRemoteSecurityKeyIpcClient fake_ipc_client( + base::Bind(&RemoteSecurityKeyIpcServerTest::OperationComplete, + base::Unretained(this))); + ASSERT_TRUE(fake_ipc_client.Connect(channel_name)); + WaitForOperationComplete(); + + // Now that a connection has been established, we issue a request and + // then wait for the timeout. + std::string request_data("I can haz Auth?"); + fake_ipc_client.SendRequest(request_data); + WaitForOperationComplete(); + + // Leave the request hanging until it times out... + base::Time start_time(base::Time::NowFromSystemTime()); + WaitForOperationComplete(); + base::TimeDelta elapsed_time = base::Time::NowFromSystemTime() - start_time; + + ASSERT_NEAR(elapsed_time.InMilliseconds(), request_timeout.InMilliseconds(), + kConnectionTimeoutErrorDeltaMs); +} + +TEST_F(RemoteSecurityKeyIpcServerTest, SendResponseTimeout) { + // Create a channel, connect to it via IPC, issue a request, and send + // a response, but do not close the channel after that. The connection + // should be terminated after the initial timeout period has elapsed. + std::string channel_name(GetUniqueTestChannelName()); + ASSERT_TRUE(remote_security_key_ipc_server_->CreateChannel( + channel_name, + /*request_timeout=*/base::TimeDelta::FromMilliseconds(500))); + + // Create a fake client and connect to the IPC server channel. + FakeRemoteSecurityKeyIpcClient fake_ipc_client( + base::Bind(&RemoteSecurityKeyIpcServerTest::OperationComplete, + base::Unretained(this))); + ASSERT_TRUE(fake_ipc_client.Connect(channel_name)); + WaitForOperationComplete(); + + // Issue a request. + std::string request_data("Auth me yo!"); + fake_ipc_client.SendRequest(request_data); + WaitForOperationComplete(); + + // Send a response from the IPC server to the IPC client. + std::string response_data("OK, the secret code is 1-2-3-4-5"); + ASSERT_TRUE(remote_security_key_ipc_server_->SendResponse(response_data)); + WaitForOperationComplete(); + + // Now wait for the timeout period for the connection to be torn down. + base::Time start_time(base::Time::NowFromSystemTime()); + WaitForOperationComplete(); + base::TimeDelta elapsed_time = base::Time::NowFromSystemTime() - start_time; + + ASSERT_NEAR(elapsed_time.InMilliseconds(), kInitialConnectTimeoutMs, + kConnectionTimeoutErrorDeltaMs); +} + +} // namespace remoting |