diff options
author | joedow <joedow@chromium.org> | 2016-03-14 14:34:07 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-14 21:35:19 +0000 |
commit | ae74137e7cfee907d23ec69cc6ba2eb490b8fa6c (patch) | |
tree | 44068b18dea3e0cd06bf99a800fde2a5f35a7010 /remoting | |
parent | eea1fa26c3d6fa0bd6e088980f1e55b15660ada9 (diff) | |
download | chromium_src-ae74137e7cfee907d23ec69cc6ba2eb490b8fa6c.zip chromium_src-ae74137e7cfee907d23ec69cc6ba2eb490b8fa6c.tar.gz chromium_src-ae74137e7cfee907d23ec69cc6ba2eb490b8fa6c.tar.bz2 |
Implement GnubbyAuthHandlerWin class and tests.
This change adds the GnubbyAuthHandlerWin class which services IPC clients that
want to forward security key messages to the remote client. This class creates
a new RemoteSecurityKeyIpcServer instance for each client so that multiple
security key requests can be serviced simultaneously.
BUG=584463
Review URL: https://codereview.chromium.org/1765743002
Cr-Commit-Position: refs/heads/master@{#381077}
Diffstat (limited to 'remoting')
12 files changed, 1033 insertions, 5 deletions
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn index 822141d..eec5d97 100644 --- a/remoting/host/BUILD.gn +++ b/remoting/host/BUILD.gn @@ -218,6 +218,8 @@ if (is_mac) { # TODO(GYP) Mac build of remoting host. "host_mock_objects.cc", "security_key/fake_remote_security_key_ipc_client.cc", "security_key/fake_remote_security_key_ipc_client.h", + "security_key/fake_remote_security_key_ipc_server.cc", + "security_key/fake_remote_security_key_ipc_server.h", "setup/mock_oauth_client.cc", "setup/mock_oauth_client.h", ] @@ -286,6 +288,7 @@ if (is_mac) { # TODO(GYP) Mac build of remoting host. "resources_unittest.cc", "screen_resolution_unittest.cc", "security_key/gnubby_auth_handler_linux_unittest.cc", + "security_key/gnubby_auth_handler_win_unittest.cc", "security_key/gnubby_extension_session_unittest.cc", "security_key/remote_security_key_ipc_server_unittest.cc", "server_log_entry_host_unittest.cc", 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 index ba5219f..f1068d5 100644 --- a/remoting/host/security_key/fake_remote_security_key_ipc_client.cc +++ b/remoting/host/security_key/fake_remote_security_key_ipc_client.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 "remoting/host/security_key/fake_remote_security_key_ipc_client.h" #include "base/callback.h" 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 index 24305ea..2c5f5bb 100644 --- a/remoting/host/security_key/fake_remote_security_key_ipc_client.h +++ b/remoting/host/security_key/fake_remote_security_key_ipc_client.h @@ -15,7 +15,7 @@ namespace IPC { class Channel; class Message; -} +} // IPC namespace remoting { diff --git a/remoting/host/security_key/fake_remote_security_key_ipc_server.cc b/remoting/host/security_key/fake_remote_security_key_ipc_server.cc new file mode 100644 index 0000000..16fa018 --- /dev/null +++ b/remoting/host/security_key/fake_remote_security_key_ipc_server.cc @@ -0,0 +1,98 @@ +// 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_server.h" + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "ipc/ipc_channel.h" +#include "ipc/ipc_listener.h" +#include "ipc/ipc_message.h" +#include "remoting/host/security_key/gnubby_auth_handler.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace remoting { + +FakeRemoteSecurityKeyIpcServer::FakeRemoteSecurityKeyIpcServer( + int connection_id, + base::TimeDelta initial_connect_timeout, + const GnubbyAuthHandler::SendMessageCallback& send_message_callback, + const base::Closure& channel_closed_callback) + : connection_id_(connection_id), + send_message_callback_(send_message_callback), + channel_closed_callback_(channel_closed_callback), + weak_factory_(this) {} + +FakeRemoteSecurityKeyIpcServer::~FakeRemoteSecurityKeyIpcServer() {} + +void FakeRemoteSecurityKeyIpcServer::SendRequest( + const std::string& message_data) { + send_message_callback_.Run(connection_id_, message_data); +} + +void FakeRemoteSecurityKeyIpcServer::CloseChannel() { + channel_closed_callback_.Run(); +} + +base::WeakPtr<FakeRemoteSecurityKeyIpcServer> +FakeRemoteSecurityKeyIpcServer::AsWeakPtr() { + return weak_factory_.GetWeakPtr(); +} + +bool FakeRemoteSecurityKeyIpcServer::OnMessageReceived( + const IPC::Message& message) { + return false; +} + +void FakeRemoteSecurityKeyIpcServer::OnChannelConnected(int32_t peer_pid) {} + +void FakeRemoteSecurityKeyIpcServer::OnChannelError() {} + +bool FakeRemoteSecurityKeyIpcServer::CreateChannel( + const std::string& channel_name, + base::TimeDelta request_timeout) { + channel_name_ = channel_name; + return true; +} + +bool FakeRemoteSecurityKeyIpcServer::SendResponse( + const std::string& message_data) { + last_message_received_ = message_data; + + send_response_callback_.Run(); + + return true; +} + +FakeRemoteSecurityKeyIpcServerFactory::FakeRemoteSecurityKeyIpcServerFactory() { + RemoteSecurityKeyIpcServer::SetFactoryForTest(this); +} + +FakeRemoteSecurityKeyIpcServerFactory:: + ~FakeRemoteSecurityKeyIpcServerFactory() { + RemoteSecurityKeyIpcServer::SetFactoryForTest(nullptr); +} + +scoped_ptr<RemoteSecurityKeyIpcServer> +FakeRemoteSecurityKeyIpcServerFactory::Create( + int connection_id, + base::TimeDelta initial_connect_timeout, + const GnubbyAuthHandler::SendMessageCallback& send_message_callback, + const base::Closure& done_callback) { + scoped_ptr<FakeRemoteSecurityKeyIpcServer> fake_ipc_server( + new FakeRemoteSecurityKeyIpcServer(connection_id, initial_connect_timeout, + send_message_callback, done_callback)); + + ipc_server_map_[connection_id] = fake_ipc_server->AsWeakPtr(); + + return make_scoped_ptr(fake_ipc_server.release()); +} + +base::WeakPtr<FakeRemoteSecurityKeyIpcServer> +FakeRemoteSecurityKeyIpcServerFactory::GetIpcServerObject(int connection_id) { + return ipc_server_map_[connection_id]; +} + +} // namespace remoting diff --git a/remoting/host/security_key/fake_remote_security_key_ipc_server.h b/remoting/host/security_key/fake_remote_security_key_ipc_server.h new file mode 100644 index 0000000..7df05ec --- /dev/null +++ b/remoting/host/security_key/fake_remote_security_key_ipc_server.h @@ -0,0 +1,129 @@ +// 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_SERVER_H_ +#define REMOTING_HOST_SECURITY_KEY_FAKE_SECURITY_KEY_IPC_SERVER_H_ + +#include "remoting/host/security_key/remote_security_key_ipc_server.h" + +#include <map> +#include <string> + +#include "base/callback_forward.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "ipc/ipc_listener.h" + +namespace IPC { +class Channel; +class Message; +} // IPC + +namespace remoting { + +// Used to send/receive security key messages for testing. It provides a +// WeakPtr reference to itself which allows tests to verify its lifetime is +// managed properly without interfering with it. +class FakeRemoteSecurityKeyIpcServer : public RemoteSecurityKeyIpcServer, + public IPC::Listener { + public: + FakeRemoteSecurityKeyIpcServer( + int connection_id, + base::TimeDelta initial_connect_timeout, + const GnubbyAuthHandler::SendMessageCallback& send_message_callback, + const base::Closure& channel_closed_callback); + ~FakeRemoteSecurityKeyIpcServer() override; + + // Simulates receipt of a security key request message. + void SendRequest(const std::string& message_data); + + // Simulates the IPC channel being closed. + void CloseChannel(); + + // Returns a WeakPtr reference to this instance. + base::WeakPtr<FakeRemoteSecurityKeyIpcServer> AsWeakPtr(); + + // Returns the payload for the last message received. + const std::string& last_message_received() const { + return last_message_received_; + } + + // Returns the name of the IPC channel this instance was told to create. + const std::string& channel_name() const { return channel_name_; } + + // Sets a callback which will be signaled when a security key response message + // is received. + void set_send_response_callback(const base::Closure& send_response_callback) { + send_response_callback_ = send_response_callback; + } + + private: + // IPC::Listener interface. + bool OnMessageReceived(const IPC::Message& message) override; + void OnChannelConnected(int32_t peer_pid) override; + void OnChannelError() override; + + // RemoteSecurityKeyIpcServer interface. + bool CreateChannel(const std::string& channel_name, + base::TimeDelta request_timeout) override; + bool SendResponse(const std::string& message_data) override; + + // The id assigned to this IPC connection. + int connection_id_; + + // Name of the IPC channel this instance was told to connect to. + std::string channel_name_; + + // The payload for the last message received. + std::string last_message_received_; + + // Used to forward security key requests to the remote client. + GnubbyAuthHandler::SendMessageCallback send_message_callback_; + + // Signaled when the IPC channel is closed. + base::Closure channel_closed_callback_; + + // Signaled when a security key response message is received. + base::Closure send_response_callback_; + + // NOTE: Weak pointers must be invalidated before all other member variables. + base::WeakPtrFactory<FakeRemoteSecurityKeyIpcServer> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(FakeRemoteSecurityKeyIpcServer); +}; + +// Used to create FakeRemoteSecurityKeyIpcServer instances for testing. +// Provides a method which will return a WeakPtr reference to each instance +// this factory creates. This allows tests to inject/retrieve messages and +// verify the backing instance is destroyed at the appropriate time. +class FakeRemoteSecurityKeyIpcServerFactory + : public RemoteSecurityKeyIpcServerFactory { + public: + FakeRemoteSecurityKeyIpcServerFactory(); + ~FakeRemoteSecurityKeyIpcServerFactory() override; + + // RemoteSecurityKeyIpcServerFactory implementation. + scoped_ptr<RemoteSecurityKeyIpcServer> Create( + int connection_id, + base::TimeDelta initial_connect_timeout, + const GnubbyAuthHandler::SendMessageCallback& message_callback, + const base::Closure& done_callback) override; + + // Provide a WeakPtr reference to the FakeRemoteSecurityKeyIpcServer object + // created for the |connection_id| IPC channel. + base::WeakPtr<FakeRemoteSecurityKeyIpcServer> GetIpcServerObject( + int connection_id); + + private: + // Tracks each FakeRemoteSecurityKeyIpcServer instance created by this + // factory which allows them to be retrieved and queried for tests. + std::map<int, base::WeakPtr<FakeRemoteSecurityKeyIpcServer>> ipc_server_map_; + + DISALLOW_COPY_AND_ASSIGN(FakeRemoteSecurityKeyIpcServerFactory); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_SECURITY_KEY_FAKE_SECURITY_KEY_IPC_SERVER_H_
\ No newline at end of file diff --git a/remoting/host/security_key/gnubby_auth_handler_linux.cc b/remoting/host/security_key/gnubby_auth_handler_linux.cc index fdde958..b53f823 100644 --- a/remoting/host/security_key/gnubby_auth_handler_linux.cc +++ b/remoting/host/security_key/gnubby_auth_handler_linux.cc @@ -2,6 +2,8 @@ // 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/gnubby_auth_handler.h" + #include <stdint.h> #include <unistd.h> @@ -19,7 +21,6 @@ #include "net/socket/stream_socket.h" #include "net/socket/unix_domain_server_socket_posix.h" #include "remoting/base/logging.h" -#include "remoting/host/security_key/gnubby_auth_handler.h" #include "remoting/host/security_key/gnubby_socket.h" namespace { @@ -140,7 +141,7 @@ void GnubbyAuthHandlerLinux::CreateGnubbyConnection() { // socket below. Consider moving this class to a different thread if this // causes any problems. See crbug.com/509807. // TODO(joedow): Since this code now runs as a host extension, we should - // perform our IO on a separate thread. + // perform our IO on a separate thread: crbug.com/591739 base::ThreadRestrictions::ScopedAllowIO allow_io; // If the file already exists, a socket in use error is returned. diff --git a/remoting/host/security_key/gnubby_auth_handler_win.cc b/remoting/host/security_key/gnubby_auth_handler_win.cc index e0d092b..4a3c98b6 100644 --- a/remoting/host/security_key/gnubby_auth_handler_win.cc +++ b/remoting/host/security_key/gnubby_auth_handler_win.cc @@ -4,13 +4,251 @@ #include "remoting/host/security_key/gnubby_auth_handler.h" +#include <map> +#include <string> + +#include "base/bind.h" #include "base/logging.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_channel.h" +#include "ipc/ipc_listener.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_constants.h" +#include "remoting/host/security_key/remote_security_key_ipc_server.h" + +namespace { + +// The timeout used to disconnect a client from the IPC Server channel if it +// forgets to do so. This ensures the server channel is not blocked forever. +const int kInitialRequestTimeoutSeconds = 5; + +// This value represents the amount of time to wait for a gnubby request from +// the client before terminating the connection. +const int kGnubbyRequestTimeoutSeconds = 60; + +} // namespace namespace remoting { +// Creates an IPC server channel which services IPC clients that want to start +// a security key forwarding session. Once an IPC Client connects to the +// server, the GnubbyAuthHandlerWin class will create a new +// RemoteSecurityKeyIpcServer instance that will service that request. The new +// instance will exist for the lifetime of the security key request and will be +// assigned a unique IPC channel name and connection id. The channel name is +// sent to the client which should disconnect the IPC server channel and +// connect to the security key forwarding session IPC channel to send/receive +// security key messages. The IPC server channel will then be reset so it can +// can service the next client/request. This system allows multiple security +// key forwarding sessions to occur concurrently. +// TODO(joedow): Update GnubbyAuthHandler impls to run on a separate IO thread +// instead of the thread it was created on: crbug.com/591739 +class GnubbyAuthHandlerWin : public GnubbyAuthHandler, public IPC::Listener { + public: + GnubbyAuthHandlerWin(); + ~GnubbyAuthHandlerWin() override; + + private: + typedef std::map<int, scoped_ptr<RemoteSecurityKeyIpcServer>> ActiveChannels; + + // GnubbyAuthHandler interface. + void CreateGnubbyConnection() override; + bool IsValidConnectionId(int gnubby_connection_id) const override; + void SendClientResponse(int gnubby_connection_id, + const std::string& response) override; + void SendErrorAndCloseConnection(int gnubby_connection_id) override; + void SetSendMessageCallback(const SendMessageCallback& callback) override; + size_t GetActiveConnectionCountForTest() const override; + void SetRequestTimeoutForTest(base::TimeDelta timeout) override; + + // IPC::Listener implementation. + bool OnMessageReceived(const IPC::Message& message) override; + void OnChannelConnected(int32_t peer_pid) override; + void OnChannelError() override; + + // Creates the IPC server channel and waits for a connection using + // |ipc_server_channel_name_|. + void StartIpcServerChannel(); + + // Restarts the IPC server channel to prepare for another connection. + void RecreateIpcServerChannel(); + + // Closes the IPC channel created for a security key forwarding session. + void CloseSecurityKeyRequestIpcChannel(int connection_id); + + // Returns the IPC Channel instance created for |connection_id|. + ActiveChannels::const_iterator GetChannelForConnectionId( + int connection_id) const; + + // Creates a unique name based on the well-known IPC channel name. + std::string GenerateUniqueChannelName(); + + // Represents the last id assigned to a new security key request IPC channel. + int last_connection_id_ = 0; + + // Sends a gnubby extension messages to the remote client when called. + SendMessageCallback send_message_callback_; + + // Tracks the IPC channel created for each security key forwarding session. + ActiveChannels active_channels_; + + // The amount of time to wait for a client to process the connection details + // message and disconnect from the IPC server channel before disconnecting it. + base::TimeDelta disconnect_timeout_; + + // Used to recreate the IPC server channel if a client forgets to disconnect. + base::OneShotTimer timer_; + + // IPC Clients connect to this channel first to receive their own IPC + // channel to start a security key forwarding session on. + scoped_ptr<IPC::Channel> ipc_server_channel_; + + // Ensures GnubbyAuthHandlerWin methods are called on the same thread. + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(GnubbyAuthHandlerWin); +}; + scoped_ptr<GnubbyAuthHandler> GnubbyAuthHandler::Create( - const GnubbyAuthHandler::SendMessageCallback& callback) { - return nullptr; + const SendMessageCallback& callback) { + scoped_ptr<GnubbyAuthHandler> auth_handler(new GnubbyAuthHandlerWin()); + auth_handler->SetSendMessageCallback(callback); + return auth_handler; +} + +GnubbyAuthHandlerWin::GnubbyAuthHandlerWin() + : disconnect_timeout_( + base::TimeDelta::FromSeconds(kInitialRequestTimeoutSeconds)) {} + +GnubbyAuthHandlerWin::~GnubbyAuthHandlerWin() {} + +void GnubbyAuthHandlerWin::CreateGnubbyConnection() { + DCHECK(thread_checker_.CalledOnValidThread()); + StartIpcServerChannel(); +} + +bool GnubbyAuthHandlerWin::IsValidConnectionId(int connection_id) const { + DCHECK(thread_checker_.CalledOnValidThread()); + return (GetChannelForConnectionId(connection_id) != active_channels_.end()); +} + +void GnubbyAuthHandlerWin::SendClientResponse( + int connection_id, + const std::string& response_data) { + DCHECK(thread_checker_.CalledOnValidThread()); + + ActiveChannels::const_iterator iter = + GetChannelForConnectionId(connection_id); + if (iter == active_channels_.end()) { + HOST_LOG << "Invalid gnubby connection ID received: " << connection_id; + return; + } + + if (!iter->second->SendResponse(response_data)) { + CloseSecurityKeyRequestIpcChannel(connection_id); + } +} + +void GnubbyAuthHandlerWin::SendErrorAndCloseConnection(int connection_id) { + DCHECK(thread_checker_.CalledOnValidThread()); + + SendClientResponse(connection_id, kRemoteSecurityKeyConnectionError); + CloseSecurityKeyRequestIpcChannel(connection_id); +} + +void GnubbyAuthHandlerWin::SetSendMessageCallback( + const SendMessageCallback& callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + send_message_callback_ = callback; +} + +size_t GnubbyAuthHandlerWin::GetActiveConnectionCountForTest() const { + return active_channels_.size(); +} + +void GnubbyAuthHandlerWin::SetRequestTimeoutForTest(base::TimeDelta timeout) { + disconnect_timeout_ = timeout; +} + +void GnubbyAuthHandlerWin::StartIpcServerChannel() { + DCHECK(thread_checker_.CalledOnValidThread()); + + ipc_server_channel_ = IPC::Channel::CreateNamedServer( + IPC::ChannelHandle(remoting::GetRemoteSecurityKeyIpcChannelName()), this); + CHECK(ipc_server_channel_->Connect()); +} + +void GnubbyAuthHandlerWin::RecreateIpcServerChannel() { + DCHECK(thread_checker_.CalledOnValidThread()); + + timer_.Stop(); + ipc_server_channel_.reset(); + + StartIpcServerChannel(); +} + +void GnubbyAuthHandlerWin::CloseSecurityKeyRequestIpcChannel( + int connection_id) { + active_channels_.erase(connection_id); +} + +GnubbyAuthHandlerWin::ActiveChannels::const_iterator +GnubbyAuthHandlerWin::GetChannelForConnectionId(int connection_id) const { + return active_channels_.find(connection_id); +} + +std::string GnubbyAuthHandlerWin::GenerateUniqueChannelName() { + return GetRemoteSecurityKeyIpcChannelName() + "." + + IPC::Channel::GenerateUniqueRandomChannelID(); +} + +bool GnubbyAuthHandlerWin::OnMessageReceived(const IPC::Message& message) { + DCHECK(thread_checker_.CalledOnValidThread()); + // This class does handle any IPC messages sent by the client. + return false; +} + +void GnubbyAuthHandlerWin::OnChannelConnected(int32_t peer_pid) { + DCHECK(thread_checker_.CalledOnValidThread()); + + timer_.Start(FROM_HERE, disconnect_timeout_, + base::Bind(&GnubbyAuthHandlerWin::OnChannelError, + base::Unretained(this))); + + // TODO(joedow): Use |peer_pid| to determine the originating session + // using ProcessIdToSessionId() and verify it is the one we created. + // Tracked via crbug.com/591746 + + int new_connection_id = ++last_connection_id_; + scoped_ptr<RemoteSecurityKeyIpcServer> ipc_server( + RemoteSecurityKeyIpcServer::Create( + new_connection_id, disconnect_timeout_, send_message_callback_, + base::Bind(&GnubbyAuthHandlerWin::CloseSecurityKeyRequestIpcChannel, + base::Unretained(this), new_connection_id))); + + std::string unique_channel_name = GenerateUniqueChannelName(); + if (ipc_server->CreateChannel( + unique_channel_name, + base::TimeDelta::FromSeconds(kGnubbyRequestTimeoutSeconds))) { + active_channels_[new_connection_id] = std::move(ipc_server); + ipc_server_channel_->Send( + new ChromotingNetworkToRemoteSecurityKeyMsg_ConnectionDetails( + unique_channel_name)); + } +} + +void GnubbyAuthHandlerWin::OnChannelError() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Could be an error, most likely the client disconnected though. Either way + // we should restart the server to prepare for the next connection. + RecreateIpcServerChannel(); } } // namespace remoting diff --git a/remoting/host/security_key/gnubby_auth_handler_win_unittest.cc b/remoting/host/security_key/gnubby_auth_handler_win_unittest.cc new file mode 100644 index 0000000..0de39df --- /dev/null +++ b/remoting/host/security_key/gnubby_auth_handler_win_unittest.cc @@ -0,0 +1,492 @@ +// 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/gnubby_auth_handler.h" + +#include <string> + +#include "base/bind.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "ipc/ipc_channel.h" +#include "ipc/ipc_listener.h" +#include "ipc/ipc_message.h" +#include "ipc/ipc_message_macros.h" +#include "remoting/host/security_key/fake_remote_security_key_ipc_client.h" +#include "remoting/host/security_key/fake_remote_security_key_ipc_server.h" +#include "remoting/host/security_key/remote_security_key_ipc_constants.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +const int kConnectionId1 = 1; +const int kConnectionId2 = 2; +} // namespace + +namespace remoting { + +class GnubbyAuthHandlerWinTest : public testing::Test { + public: + GnubbyAuthHandlerWinTest(); + ~GnubbyAuthHandlerWinTest() 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: + // Waits until the current |run_loop_| instance is signaled, then resets it. + void WaitForOperationComplete(); + + // Used as a callback given to the object under test, expected to be called + // back when a security key request is received by it. + void SendMessageToClient(int connection_id, const std::string& data); + + // Creates a new gnubby connection on the object under test. + void CreateGnubbyConnection(const std::string& channel_name); + + // Uses |fake_ipc_client| to connect to the initial IPC server channel, it + // then validates internal state of the object under test and closes the + // connection based on |close_connection|. + void EstablishInitialIpcConnection( + FakeRemoteSecurityKeyIpcClient* fake_ipc_client, + int expected_connection_id, + const std::string& channel_name, + bool close_connection); + + // Sends a security key response message using |fake_ipc_server| and + // validates the state of the object under test. + void SendRequestToGnubbyAuthHandler( + const base::WeakPtr<FakeRemoteSecurityKeyIpcServer>& fake_ipc_server, + int connection_id, + const std::string& request_payload); + + // Sends a security key response message to |fake_ipc_server| and validates + // the state of the object under test. + void SendResponseViaGnubbyAuthHandler( + const base::WeakPtr<FakeRemoteSecurityKeyIpcServer>& fake_ipc_server, + int connection_id, + const std::string& response_payload); + + // Closes a security key session IPC channel and validates state. + void CloseSecurityKeySessionIpcChannel( + const base::WeakPtr<FakeRemoteSecurityKeyIpcServer>& fake_ipc_server, + int connection_id); + + // Returns a unique IPC channel name which prevents conflicts when running + // tests concurrently. + std::string GetUniqueTestChannelName(); + + // 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<GnubbyAuthHandler> auth_handler_; + + // Set as the default factory to create RemoteSecurityKeyIpcServerFactory + // instances, this class will track each objects creation and allow the tests + // to access it and use it for driving tests and validate state. + FakeRemoteSecurityKeyIpcServerFactory ipc_server_factory_; + + // 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(GnubbyAuthHandlerWinTest); +}; + +GnubbyAuthHandlerWinTest::GnubbyAuthHandlerWinTest() + : run_loop_(new base::RunLoop()) { + auth_handler_ = remoting::GnubbyAuthHandler::Create(base::Bind( + &GnubbyAuthHandlerWinTest::SendMessageToClient, base::Unretained(this))); +} + +GnubbyAuthHandlerWinTest::~GnubbyAuthHandlerWinTest() {} + +void GnubbyAuthHandlerWinTest::OperationComplete() { + run_loop_->Quit(); +} + +void GnubbyAuthHandlerWinTest::WaitForOperationComplete() { + run_loop_->Run(); + run_loop_.reset(new base::RunLoop()); +} + +void GnubbyAuthHandlerWinTest::SendMessageToClient(int connection_id, + const std::string& data) { + last_connection_id_received_ = connection_id; + last_message_received_ = data; + OperationComplete(); +} + +void GnubbyAuthHandlerWinTest::CreateGnubbyConnection( + const std::string& channel_name) { + ASSERT_EQ(0u, auth_handler_->GetActiveConnectionCountForTest()); + + remoting::SetRemoteSecurityKeyIpcChannelNameForTest(channel_name); + + // Create a new Gnubby IPC Server connection. + auth_handler_->CreateGnubbyConnection(); + ASSERT_TRUE(IPC::Channel::IsNamedServerInitialized(channel_name)); +} + +void GnubbyAuthHandlerWinTest::EstablishInitialIpcConnection( + FakeRemoteSecurityKeyIpcClient* fake_ipc_client, + int expected_connection_id, + const std::string& channel_name, + bool close_connection) { + size_t expected_connection_count = + auth_handler_->GetActiveConnectionCountForTest() + 1; + + ASSERT_TRUE(fake_ipc_client->Connect(channel_name)); + // Client and Server will each signal us once when OnChannelConenect() is + // called so we wait on complete twice. The order in which each is signaled + // is not important. + WaitForOperationComplete(); + WaitForOperationComplete(); + + // Verify the connection details have been passed to the client. + std::string new_channel_name = fake_ipc_client->last_message_received(); + ASSERT_FALSE(new_channel_name.empty()); + + // Verify the internal state of the GnubbyAuthHandler is correct. + ASSERT_TRUE(auth_handler_->IsValidConnectionId(expected_connection_id)); + ASSERT_EQ(expected_connection_count, + auth_handler_->GetActiveConnectionCountForTest()); + + if (close_connection) { + fake_ipc_client->CloseChannel(); + WaitForOperationComplete(); + } +} + +void GnubbyAuthHandlerWinTest::SendRequestToGnubbyAuthHandler( + const base::WeakPtr<FakeRemoteSecurityKeyIpcServer>& fake_ipc_server, + int connection_id, + const std::string& request_payload) { + size_t expected_connection_count = + auth_handler_->GetActiveConnectionCountForTest(); + // Send a gnubby request using the fake IPC server. + fake_ipc_server->SendRequest(request_payload); + WaitForOperationComplete(); + + // Verify the FakeRemoteSecurityKeyIpcServer instance was not destroyed. + ASSERT_TRUE(fake_ipc_server.get()); + + // Verify the request was received. + ASSERT_EQ(connection_id, last_connection_id_received_); + ASSERT_EQ(request_payload, last_message_received_); + + // Verify the internal state of the GnubbyAuthHandler is still correct. + ASSERT_TRUE(auth_handler_->IsValidConnectionId(connection_id)); + ASSERT_EQ(expected_connection_count, + auth_handler_->GetActiveConnectionCountForTest()); +} + +void GnubbyAuthHandlerWinTest::SendResponseViaGnubbyAuthHandler( + const base::WeakPtr<FakeRemoteSecurityKeyIpcServer>& fake_ipc_server, + int connection_id, + const std::string& response_payload) { + size_t expected_connection_count = + auth_handler_->GetActiveConnectionCountForTest(); + + // Send a gnubby response using the new IPC channel. + auth_handler_->SendClientResponse(connection_id, response_payload); + WaitForOperationComplete(); + + // Verify the gnubby response was received. + ASSERT_EQ(response_payload, fake_ipc_server->last_message_received()); + + // Verify the internal state of the GnubbyAuthHandler is still correct. + ASSERT_TRUE(auth_handler_->IsValidConnectionId(connection_id)); + ASSERT_EQ(expected_connection_count, + auth_handler_->GetActiveConnectionCountForTest()); +} + +void GnubbyAuthHandlerWinTest::CloseSecurityKeySessionIpcChannel( + const base::WeakPtr<FakeRemoteSecurityKeyIpcServer>& fake_ipc_server, + int connection_id) { + size_t expected_connection_count = + auth_handler_->GetActiveConnectionCountForTest() - 1; + + fake_ipc_server->CloseChannel(); + + // Verify the internal state has been updated. + ASSERT_FALSE(auth_handler_->IsValidConnectionId(connection_id)); + ASSERT_EQ(expected_connection_count, + auth_handler_->GetActiveConnectionCountForTest()); + + // Verify the FakeRemoteSecurityKeyIpcServer instance was destroyed. + ASSERT_FALSE(fake_ipc_server.get()); +} + +std::string GnubbyAuthHandlerWinTest::GetUniqueTestChannelName() { + std::string channel_name("Uber_Awesome_Super_Mega_Test_Channel."); + channel_name.append(IPC::Channel::GenerateUniqueRandomChannelID()); + + return channel_name; +} + +TEST_F(GnubbyAuthHandlerWinTest, HandleSingleGnubbyRequest) { + std::string channel_name(GetUniqueTestChannelName()); + CreateGnubbyConnection(channel_name); + + // Create a fake client and connect to the IPC server channel. + FakeRemoteSecurityKeyIpcClient fake_ipc_client(base::Bind( + &GnubbyAuthHandlerWinTest::OperationComplete, base::Unretained(this))); + EstablishInitialIpcConnection(&fake_ipc_client, kConnectionId1, channel_name, + /*close_connection=*/true); + + // Connect to the private IPC server channel created for this client. + std::string new_channel_name = fake_ipc_client.last_message_received(); + + // Retrieve the IPC server instance created when the client connected. + base::WeakPtr<FakeRemoteSecurityKeyIpcServer> fake_ipc_server = + ipc_server_factory_.GetIpcServerObject(kConnectionId1); + ASSERT_TRUE(fake_ipc_server.get()); + ASSERT_EQ(new_channel_name, fake_ipc_server->channel_name()); + + fake_ipc_server->set_send_response_callback(base::Bind( + &GnubbyAuthHandlerWinTest::OperationComplete, base::Unretained(this))); + + // Send a gnubby request using the fake IPC server. + SendRequestToGnubbyAuthHandler(fake_ipc_server, kConnectionId1, "0123456789"); + + // Send a gnubby response using the new IPC channel. + SendResponseViaGnubbyAuthHandler(fake_ipc_server, kConnectionId1, + "9876543210"); + + CloseSecurityKeySessionIpcChannel(fake_ipc_server, kConnectionId1); +} + +TEST_F(GnubbyAuthHandlerWinTest, HandleConcurrentGnubbyRequests) { + std::string channel_name(GetUniqueTestChannelName()); + CreateGnubbyConnection(channel_name); + + // Create fake clients and connect each to the IPC server channel. + FakeRemoteSecurityKeyIpcClient fake_ipc_client_1(base::Bind( + &GnubbyAuthHandlerWinTest::OperationComplete, base::Unretained(this))); + FakeRemoteSecurityKeyIpcClient fake_ipc_client_2(base::Bind( + &GnubbyAuthHandlerWinTest::OperationComplete, base::Unretained(this))); + + EstablishInitialIpcConnection(&fake_ipc_client_1, kConnectionId1, + channel_name, + /*close_connection=*/true); + EstablishInitialIpcConnection(&fake_ipc_client_2, kConnectionId2, + channel_name, + /*close_connection=*/true); + + // Verify the connection details have been passed to the client. + std::string channel_name_1 = fake_ipc_client_1.last_message_received(); + std::string channel_name_2 = fake_ipc_client_2.last_message_received(); + ASSERT_NE(channel_name_1, channel_name_2); + + base::WeakPtr<FakeRemoteSecurityKeyIpcServer> fake_ipc_server_1 = + ipc_server_factory_.GetIpcServerObject(kConnectionId1); + ASSERT_TRUE(fake_ipc_server_1.get()); + ASSERT_EQ(channel_name_1, fake_ipc_server_1->channel_name()); + + base::WeakPtr<FakeRemoteSecurityKeyIpcServer> fake_ipc_server_2 = + ipc_server_factory_.GetIpcServerObject(kConnectionId2); + ASSERT_TRUE(fake_ipc_server_2.get()); + ASSERT_EQ(channel_name_2, fake_ipc_server_2->channel_name()); + + fake_ipc_server_1->set_send_response_callback(base::Bind( + &GnubbyAuthHandlerWinTest::OperationComplete, base::Unretained(this))); + fake_ipc_server_2->set_send_response_callback(base::Bind( + &GnubbyAuthHandlerWinTest::OperationComplete, base::Unretained(this))); + + // Connect and send a gnubby request using the first IPC channel. + SendRequestToGnubbyAuthHandler(fake_ipc_server_1, kConnectionId1, + "aaaaaaaaaa"); + + // Send a gnubby request using the second IPC channel. + SendRequestToGnubbyAuthHandler(fake_ipc_server_1, kConnectionId1, + "bbbbbbbbbb"); + + // Send a gnubby response using the first IPC channel. + SendResponseViaGnubbyAuthHandler(fake_ipc_server_2, kConnectionId2, + "cccccccccc"); + + // Send a gnubby response using the second IPC channel. + SendResponseViaGnubbyAuthHandler(fake_ipc_server_2, kConnectionId2, + "dddddddddd"); + + // Close the IPC channels. + CloseSecurityKeySessionIpcChannel(fake_ipc_server_1, kConnectionId1); + CloseSecurityKeySessionIpcChannel(fake_ipc_server_2, kConnectionId2); +} + +TEST_F(GnubbyAuthHandlerWinTest, HandleSequentialGnubbyRequests) { + std::string channel_name(GetUniqueTestChannelName()); + CreateGnubbyConnection(channel_name); + + // Create fake clients to connect to the IPC server channel. + FakeRemoteSecurityKeyIpcClient fake_ipc_client_1(base::Bind( + &GnubbyAuthHandlerWinTest::OperationComplete, base::Unretained(this))); + + EstablishInitialIpcConnection(&fake_ipc_client_1, kConnectionId1, + channel_name, + /*close_connection=*/true); + + // Verify the connection details have been passed to the client. + std::string channel_name_1 = fake_ipc_client_1.last_message_received(); + + base::WeakPtr<FakeRemoteSecurityKeyIpcServer> fake_ipc_server_1 = + ipc_server_factory_.GetIpcServerObject(kConnectionId1); + ASSERT_TRUE(fake_ipc_server_1.get()); + ASSERT_EQ(channel_name_1, fake_ipc_server_1->channel_name()); + + fake_ipc_server_1->set_send_response_callback(base::Bind( + &GnubbyAuthHandlerWinTest::OperationComplete, base::Unretained(this))); + + // Send a gnubby request using the first IPC channel. + SendRequestToGnubbyAuthHandler(fake_ipc_server_1, kConnectionId1, + "aaaaaaaaaa"); + + // Send a gnubby response using the first IPC channel. + SendResponseViaGnubbyAuthHandler(fake_ipc_server_1, kConnectionId1, + "cccccccccc"); + + // Close the IPC channel. + CloseSecurityKeySessionIpcChannel(fake_ipc_server_1, kConnectionId1); + + // Now connect with a second client. + FakeRemoteSecurityKeyIpcClient fake_ipc_client_2(base::Bind( + &GnubbyAuthHandlerWinTest::OperationComplete, base::Unretained(this))); + EstablishInitialIpcConnection(&fake_ipc_client_2, kConnectionId2, + channel_name, + /*close_connection=*/true); + + std::string channel_name_2 = fake_ipc_client_2.last_message_received(); + ASSERT_NE(channel_name_1, channel_name_2); + + base::WeakPtr<FakeRemoteSecurityKeyIpcServer> fake_ipc_server_2 = + ipc_server_factory_.GetIpcServerObject(kConnectionId2); + ASSERT_TRUE(fake_ipc_server_2.get()); + ASSERT_EQ(channel_name_2, fake_ipc_server_2->channel_name()); + + fake_ipc_server_2->set_send_response_callback(base::Bind( + &GnubbyAuthHandlerWinTest::OperationComplete, base::Unretained(this))); + + // Send a gnubby request using the second IPC channel. + SendRequestToGnubbyAuthHandler(fake_ipc_server_2, kConnectionId2, + "bbbbbbbbbb"); + + // Send a gnubby response using the second IPC channel. + SendResponseViaGnubbyAuthHandler(fake_ipc_server_2, kConnectionId2, + "dddddddddd"); + + // Close the IPC channel. + CloseSecurityKeySessionIpcChannel(fake_ipc_server_2, kConnectionId2); +} + +TEST_F(GnubbyAuthHandlerWinTest, ClientNeverDisconnectsFromInitialIpcChannel) { + const int kLowConnectionTimeoutInMs = 25; + auth_handler_->SetRequestTimeoutForTest( + base::TimeDelta::FromMilliseconds(kLowConnectionTimeoutInMs)); + + std::string channel_name(GetUniqueTestChannelName()); + CreateGnubbyConnection(channel_name); + + // Create a fake client and connect to the IPC server channel. + FakeRemoteSecurityKeyIpcClient fake_ipc_client(base::Bind( + &GnubbyAuthHandlerWinTest::OperationComplete, base::Unretained(this))); + EstablishInitialIpcConnection(&fake_ipc_client, kConnectionId1, channel_name, + /*close_connection=*/false); + + // Don't close the channel here, instead wait for the GnubbyAuthHandler to + // close the connection due to the timeout. + WaitForOperationComplete(); + + // Verify the connection that was set up still exists. + ASSERT_TRUE(auth_handler_->IsValidConnectionId(kConnectionId1)); + ASSERT_EQ(1u, auth_handler_->GetActiveConnectionCountForTest()); + + // Attempt to connect again after the error. + EstablishInitialIpcConnection(&fake_ipc_client, kConnectionId2, channel_name, + /*close_connection=*/true); +} + +TEST_F(GnubbyAuthHandlerWinTest, HandleGnubbyRequestTimeout) { + std::string channel_name(GetUniqueTestChannelName()); + CreateGnubbyConnection(channel_name); + + // Create a fake client and connect to the IPC server channel. + FakeRemoteSecurityKeyIpcClient fake_ipc_client(base::Bind( + &GnubbyAuthHandlerWinTest::OperationComplete, base::Unretained(this))); + EstablishInitialIpcConnection(&fake_ipc_client, kConnectionId1, channel_name, + /*close_connection=*/true); + + // Connect to the private IPC server channel created for this client. + std::string new_channel_name = fake_ipc_client.last_message_received(); + + // Retrieve the IPC server instance created when the client connected. + base::WeakPtr<FakeRemoteSecurityKeyIpcServer> fake_ipc_server = + ipc_server_factory_.GetIpcServerObject(kConnectionId1); + ASSERT_TRUE(fake_ipc_server.get()); + ASSERT_EQ(new_channel_name, fake_ipc_server->channel_name()); + + fake_ipc_server->set_send_response_callback(base::Bind( + &GnubbyAuthHandlerWinTest::OperationComplete, base::Unretained(this))); + + // Simulate a timeout and verify the IPC server is cleaned up. + CloseSecurityKeySessionIpcChannel(fake_ipc_server, kConnectionId1); + + // Attempt to connect again after the error. + EstablishInitialIpcConnection(&fake_ipc_client, kConnectionId2, channel_name, + /*close_connection=*/true); +} + +TEST_F(GnubbyAuthHandlerWinTest, HandleGnubbyErrorResponse) { + std::string channel_name(GetUniqueTestChannelName()); + CreateGnubbyConnection(channel_name); + + // Create a fake client and connect to the IPC server channel. + FakeRemoteSecurityKeyIpcClient fake_ipc_client(base::Bind( + &GnubbyAuthHandlerWinTest::OperationComplete, base::Unretained(this))); + EstablishInitialIpcConnection(&fake_ipc_client, kConnectionId1, channel_name, + /*close_connection=*/true); + + // Connect to the private IPC server channel created for this client. + std::string new_channel_name = fake_ipc_client.last_message_received(); + + // Retrieve the IPC server instance created when the client connected. + base::WeakPtr<FakeRemoteSecurityKeyIpcServer> fake_ipc_server = + ipc_server_factory_.GetIpcServerObject(kConnectionId1); + ASSERT_TRUE(fake_ipc_server.get()); + ASSERT_EQ(new_channel_name, fake_ipc_server->channel_name()); + + fake_ipc_server->set_send_response_callback(base::Bind( + &GnubbyAuthHandlerWinTest::OperationComplete, base::Unretained(this))); + + // Send a gnubby request using the fake IPC server. + SendRequestToGnubbyAuthHandler(fake_ipc_server, kConnectionId1, "0123456789"); + + // Simulate a gnubby error from the client. + auth_handler_->SendErrorAndCloseConnection(kConnectionId1); + // Wait for the ipc server channel to be torn down. + WaitForOperationComplete(); + + // Verify the connection was cleaned up. + ASSERT_FALSE(fake_ipc_server.get()); + ASSERT_FALSE(auth_handler_->IsValidConnectionId(kConnectionId1)); + ASSERT_EQ(0u, auth_handler_->GetActiveConnectionCountForTest()); + + // Attempt to connect again after the error. + EstablishInitialIpcConnection(&fake_ipc_client, kConnectionId2, channel_name, + /*close_connection=*/true); +} + +} // namespace remoting diff --git a/remoting/host/security_key/remote_security_key_ipc_constants.cc b/remoting/host/security_key/remote_security_key_ipc_constants.cc new file mode 100644 index 0000000..227b284 --- /dev/null +++ b/remoting/host/security_key/remote_security_key_ipc_constants.cc @@ -0,0 +1,37 @@ +// 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_constants.h" + +#include "base/lazy_instance.h" + +namespace { +base::LazyInstance<std::string> g_remote_security_key_ipc_channel_name = + LAZY_INSTANCE_INITIALIZER; + +const char kRemoteSecurityKeyIpcChannelName[] = + "remote_security_key_ipc_channel"; + +} // namespace + +namespace remoting { + +extern const char kRemoteSecurityKeyConnectionError[] = + "remote_ssh_connection_error"; + +const std::string& GetRemoteSecurityKeyIpcChannelName() { + if (g_remote_security_key_ipc_channel_name.Get().empty()) { + g_remote_security_key_ipc_channel_name.Get() = + kRemoteSecurityKeyIpcChannelName; + } + + return g_remote_security_key_ipc_channel_name.Get(); +} + +void SetRemoteSecurityKeyIpcChannelNameForTest( + const std::string& channel_name) { + g_remote_security_key_ipc_channel_name.Get() = channel_name; +} + +} // namespace remoting diff --git a/remoting/host/security_key/remote_security_key_ipc_constants.h b/remoting/host/security_key/remote_security_key_ipc_constants.h new file mode 100644 index 0000000..4952735 --- /dev/null +++ b/remoting/host/security_key/remote_security_key_ipc_constants.h @@ -0,0 +1,24 @@ +// 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_CONSTANTS_H_ +#define REMOTING_HOST_SECURITY_KEY_REMOTE_SECURITY_KEY_IPC_CONSTANTS_H_ + +#include <string> + +namespace remoting { + +// Used to indicate an error during remote security key forwarding session. +extern const char kRemoteSecurityKeyConnectionError[]; + +// Returns the name of the well-known IPC server channel used to initiate a +// remote security key forwarding session. +const std::string& GetRemoteSecurityKeyIpcChannelName(); + +// Sets the name of the well-known IPC server channel for testing purposes. +void SetRemoteSecurityKeyIpcChannelNameForTest(const std::string& channel_name); + +} // namespace remoting + +#endif // REMOTING_HOST_SECURITY_KEY_REMOTE_SECURITY_KEY_IPC_CONSTANTS_H_ diff --git a/remoting/remoting_host_srcs.gypi b/remoting/remoting_host_srcs.gypi index 31126a2c..33f0df0 100644 --- a/remoting/remoting_host_srcs.gypi +++ b/remoting/remoting_host_srcs.gypi @@ -222,6 +222,8 @@ 'host/security_key/gnubby_extension_session.h', 'host/security_key/gnubby_socket.cc', 'host/security_key/gnubby_socket.h', + 'host/security_key/remote_security_key_ipc_constants.cc', + 'host/security_key/remote_security_key_ipc_constants.h', 'host/security_key/remote_security_key_ipc_server.cc', 'host/security_key/remote_security_key_ipc_server.h', 'host/security_key/remote_security_key_ipc_server_impl.cc', diff --git a/remoting/remoting_test.gypi b/remoting/remoting_test.gypi index 589f2c7..982fe0c 100644 --- a/remoting/remoting_test.gypi +++ b/remoting/remoting_test.gypi @@ -33,6 +33,8 @@ 'host/fake_oauth_token_getter.h', 'host/security_key/fake_remote_security_key_ipc_client.cc', 'host/security_key/fake_remote_security_key_ipc_client.h', + 'host/security_key/fake_remote_security_key_ipc_server.cc', + 'host/security_key/fake_remote_security_key_ipc_server.h', 'protocol/fake_authenticator.cc', 'protocol/fake_authenticator.h', 'protocol/fake_connection_to_client.cc', @@ -293,6 +295,7 @@ 'host/resources_unittest.cc', 'host/screen_resolution_unittest.cc', 'host/security_key/gnubby_auth_handler_linux_unittest.cc', + 'host/security_key/gnubby_auth_handler_win_unittest.cc', 'host/security_key/gnubby_extension_session_unittest.cc', 'host/security_key/remote_security_key_ipc_server_unittest.cc', 'host/server_log_entry_host_unittest.cc', |