summaryrefslogtreecommitdiffstats
path: root/remoting/host/security_key
diff options
context:
space:
mode:
authorjoedow <joedow@chromium.org>2016-02-16 21:30:02 -0800
committerCommit bot <commit-bot@chromium.org>2016-02-17 05:30:58 +0000
commit709e40ef1af725648b40fe28e06801b3a56f7e14 (patch)
tree93c8d61e2c7bab3093b03c1b555404c10d1487be /remoting/host/security_key
parentcf186a3887a562701a0f52b216a4b05ac136d0f0 (diff)
downloadchromium_src-709e40ef1af725648b40fe28e06801b3a56f7e14.zip
chromium_src-709e40ef1af725648b40fe28e06801b3a56f7e14.tar.gz
chromium_src-709e40ef1af725648b40fe28e06801b3a56f7e14.tar.bz2
Refactoring platform-agnostic Gnubby Auth Handler logic into a Host Extension.
This change breaks the logic in the GnubbyAuthHandlerLinux class into platform-specific and platform-agnostic chunks. The platform-specific logic has remained in the GnubbyAuthHandlerLinux class whereas the platform-agnostic code was moved into a new HostExtension class (GnubbyHostExtensionSession). This allows us to add a Gnubby host extension if it is supported by policy and remove the logic and plumbing for gnubby message handling from ClientSession and the many DesktopEnvironment classes. BUG=584463 Review URL: https://codereview.chromium.org/1689003002 Cr-Commit-Position: refs/heads/master@{#375797}
Diffstat (limited to 'remoting/host/security_key')
-rw-r--r--remoting/host/security_key/gnubby_auth_handler.h34
-rw-r--r--remoting/host/security_key/gnubby_auth_handler_linux.cc271
-rw-r--r--remoting/host/security_key/gnubby_auth_handler_linux.h48
-rw-r--r--remoting/host/security_key/gnubby_auth_handler_linux_unittest.cc196
-rw-r--r--remoting/host/security_key/gnubby_auth_handler_mac.cc29
-rw-r--r--remoting/host/security_key/gnubby_auth_handler_win.cc29
-rw-r--r--remoting/host/security_key/gnubby_extension.cc34
-rw-r--r--remoting/host/security_key/gnubby_extension.h34
-rw-r--r--remoting/host/security_key/gnubby_extension_session.cc207
-rw-r--r--remoting/host/security_key/gnubby_extension_session.h65
-rw-r--r--remoting/host/security_key/gnubby_extension_session_unittest.cc377
-rw-r--r--remoting/host/security_key/gnubby_socket.cc20
-rw-r--r--remoting/host/security_key/gnubby_socket.h7
13 files changed, 978 insertions, 373 deletions
diff --git a/remoting/host/security_key/gnubby_auth_handler.h b/remoting/host/security_key/gnubby_auth_handler.h
index 606742b..8070398 100644
--- a/remoting/host/security_key/gnubby_auth_handler.h
+++ b/remoting/host/security_key/gnubby_auth_handler.h
@@ -7,6 +7,7 @@
#include <string>
+#include "base/callback_forward.h"
#include "base/memory/scoped_ptr.h"
namespace base {
@@ -15,29 +16,42 @@ class FilePath;
namespace remoting {
-namespace protocol {
-class ClientStub;
-} // namespace protocol
-
// Class responsible for proxying authentication data between a local gnubbyd
// and the client.
class GnubbyAuthHandler {
public:
virtual ~GnubbyAuthHandler() {}
+ // Used to send gnubby extension messages to the client.
+ typedef base::Callback<void(int connection_id, const std::string& data)>
+ SendMessageCallback;
+
// Creates a platform-specific GnubbyAuthHandler.
+ // All invocations of |callback| are guaranteed to occur before the underlying
+ // GnubbyAuthHandler object is destroyed. It is not safe to destroy the
+ // GnubbyAuthHandler object within the callback.
static scoped_ptr<GnubbyAuthHandler> Create(
- protocol::ClientStub* client_stub);
+ const SendMessageCallback& callback);
// Specify the name of the socket to listen to gnubby requests on.
+ // TODO(joedow): Move this to a linux specific class. see: crbug.com/587298
static void SetGnubbySocketName(const base::FilePath& gnubby_socket_name);
- // A message was received from the client.
- virtual void DeliverClientMessage(const std::string& message) = 0;
+ // Sets the callback used to send messages to the client.
+ virtual void SetSendMessageCallback(const SendMessageCallback& callback) = 0;
+
+ // Creates the platform specific connection to handle gnubby requests.
+ virtual void CreateGnubbyConnection() = 0;
+
+ // Returns true if |gnubby_connection_id| represents a valid connection.
+ virtual bool IsValidConnectionId(int gnubby_connection_id) const = 0;
+
+ // Sends the gnubby response from the client to the local gnubby agent.
+ virtual void SendClientResponse(int gnubby_connection_id,
+ const std::string& response) = 0;
- // Send data to client.
- virtual void DeliverHostDataMessage(int connection_id,
- const std::string& data) const = 0;
+ // Closes the gnubby connection represented by |gnubby_connection_id|.
+ virtual void SendErrorAndCloseConnection(int gnubby_connection_id) = 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 5bed3c9..8a50686 100644
--- a/remoting/host/security_key/gnubby_auth_handler_linux.cc
+++ b/remoting/host/security_key/gnubby_auth_handler_linux.cc
@@ -6,12 +6,9 @@
#include <stdint.h>
#include <unistd.h>
-#include <utility>
#include "base/bind.h"
#include "base/files/file_util.h"
-#include "base/json/json_reader.h"
-#include "base/json/json_writer.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/stl_util.h"
@@ -21,23 +18,9 @@
#include "net/socket/unix_domain_server_socket_posix.h"
#include "remoting/base/logging.h"
#include "remoting/host/security_key/gnubby_socket.h"
-#include "remoting/proto/control.pb.h"
-#include "remoting/protocol/client_stub.h"
-
-namespace remoting {
namespace {
-const char kConnectionId[] = "connectionId";
-const char kControlMessage[] = "control";
-const char kControlOption[] = "option";
-const char kDataMessage[] = "data";
-const char kDataPayload[] = "data";
-const char kErrorMessage[] = "error";
-const char kGnubbyAuthMessage[] = "gnubby-auth";
-const char kGnubbyAuthV1[] = "auth-v1";
-const char kMessageType[] = "type";
-
const int64_t kDefaultRequestTimeoutSeconds = 60;
// The name of the socket to listen for gnubby requests on.
@@ -59,127 +42,95 @@ unsigned int GetCommandCode(const std::string& data) {
return data.empty() ? -1 : static_cast<unsigned int>(data[0]);
}
-// Creates a string of byte data from a ListValue of numbers. Returns true if
-// all of the list elements are numbers.
-bool ConvertListValueToString(base::ListValue* bytes, std::string* out) {
- out->clear();
-
- unsigned int byte_count = bytes->GetSize();
- if (byte_count != 0) {
- out->reserve(byte_count);
- for (unsigned int i = 0; i < byte_count; i++) {
- int value;
- if (!bytes->GetInteger(i, &value))
- return false;
- out->push_back(static_cast<char>(value));
- }
- }
- return true;
-}
-
} // namespace
-GnubbyAuthHandlerLinux::GnubbyAuthHandlerLinux(
- protocol::ClientStub* client_stub)
- : client_stub_(client_stub),
- last_connection_id_(0),
- request_timeout_(
- base::TimeDelta::FromSeconds(kDefaultRequestTimeoutSeconds)) {
- DCHECK(client_stub_);
-}
-
-GnubbyAuthHandlerLinux::~GnubbyAuthHandlerLinux() {
- STLDeleteValues(&active_sockets_);
-}
+namespace remoting {
-// static
scoped_ptr<GnubbyAuthHandler> GnubbyAuthHandler::Create(
- protocol::ClientStub* client_stub) {
- return make_scoped_ptr(new GnubbyAuthHandlerLinux(client_stub));
+ const SendMessageCallback& callback) {
+ scoped_ptr<GnubbyAuthHandler> auth_handler(new GnubbyAuthHandlerLinux());
+ auth_handler->SetSendMessageCallback(callback);
+ return auth_handler;
}
-// static
void GnubbyAuthHandler::SetGnubbySocketName(
const base::FilePath& gnubby_socket_name) {
g_gnubby_socket_name.Get() = gnubby_socket_name;
}
-void GnubbyAuthHandlerLinux::DeliverClientMessage(const std::string& message) {
- DCHECK(CalledOnValidThread());
-
- scoped_ptr<base::Value> value = base::JSONReader::Read(message);
- base::DictionaryValue* client_message;
- if (value && value->GetAsDictionary(&client_message)) {
- std::string type;
- if (!client_message->GetString(kMessageType, &type)) {
- LOG(ERROR) << "Invalid gnubby-auth message";
- return;
- }
-
- if (type == kControlMessage) {
- std::string option;
- if (client_message->GetString(kControlOption, &option) &&
- option == kGnubbyAuthV1) {
- CreateAuthorizationSocket();
- } else {
- LOG(ERROR) << "Invalid gnubby-auth control option";
- }
- } else if (type == kDataMessage) {
- ActiveSockets::iterator iter = GetSocketForMessage(client_message);
- if (iter != active_sockets_.end()) {
- base::ListValue* bytes;
- std::string response;
- if (client_message->GetList(kDataPayload, &bytes) &&
- ConvertListValueToString(bytes, &response)) {
- HOST_LOG << "Sending gnubby response: " << GetCommandCode(response);
- iter->second->SendResponse(response);
- } else {
- LOG(ERROR) << "Invalid gnubby data";
- SendErrorAndCloseActiveSocket(iter);
- }
- } else {
- LOG(ERROR) << "Unknown gnubby-auth data connection";
- }
- } else if (type == kErrorMessage) {
- ActiveSockets::iterator iter = GetSocketForMessage(client_message);
- if (iter != active_sockets_.end()) {
- HOST_LOG << "Sending gnubby error";
- SendErrorAndCloseActiveSocket(iter);
- } else {
- LOG(ERROR) << "Unknown gnubby-auth error connection";
- }
- } else {
- LOG(ERROR) << "Unknown gnubby-auth message type: " << type;
- }
- }
+GnubbyAuthHandlerLinux::GnubbyAuthHandlerLinux()
+ : last_connection_id_(0),
+ request_timeout_(
+ base::TimeDelta::FromSeconds(kDefaultRequestTimeoutSeconds)) {}
+
+GnubbyAuthHandlerLinux::~GnubbyAuthHandlerLinux() {
+ STLDeleteValues(&active_sockets_);
}
-void GnubbyAuthHandlerLinux::DeliverHostDataMessage(
- int connection_id,
- const std::string& data) const {
- DCHECK(CalledOnValidThread());
+void GnubbyAuthHandlerLinux::CreateGnubbyConnection() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!g_gnubby_socket_name.Get().empty());
- base::DictionaryValue request;
- request.SetString(kMessageType, kDataMessage);
- request.SetInteger(kConnectionId, connection_id);
+ {
+ // DeleteFile() is a blocking operation, but so is creation of the unix
+ // 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.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
- base::ListValue* bytes = new base::ListValue();
- for (std::string::const_iterator i = data.begin(); i != data.end(); ++i) {
- bytes->AppendInteger(static_cast<unsigned char>(*i));
+ // If the file already exists, a socket in use error is returned.
+ base::DeleteFile(g_gnubby_socket_name.Get(), false);
}
- request.Set(kDataPayload, bytes);
- std::string request_json;
- if (!base::JSONWriter::Write(request, &request_json)) {
- LOG(ERROR) << "Failed to create request json";
+ HOST_LOG << "Listening for gnubby requests on "
+ << g_gnubby_socket_name.Get().value();
+
+ auth_socket_.reset(
+ new net::UnixDomainServerSocket(base::Bind(MatchUid), false));
+ int rv = auth_socket_->BindAndListen(g_gnubby_socket_name.Get().value(),
+ /*backlog=*/1);
+ if (rv != net::OK) {
+ LOG(ERROR) << "Failed to open socket for gnubby requests: '" << rv << "'";
return;
}
+ DoAccept();
+}
+
+bool GnubbyAuthHandlerLinux::IsValidConnectionId(
+ int gnubby_connection_id) const {
+ return GetSocketForConnectionId(gnubby_connection_id) !=
+ active_sockets_.end();
+}
+
+void GnubbyAuthHandlerLinux::SendClientResponse(int gnubby_connection_id,
+ const std::string& response) {
+ ActiveSockets::const_iterator iter =
+ GetSocketForConnectionId(gnubby_connection_id);
+ if (iter != active_sockets_.end()) {
+ iter->second->SendResponse(response);
+ } else {
+ LOG(WARNING) << "Unknown gnubby-auth data connection: '"
+ << gnubby_connection_id << "'";
+ }
+}
- protocol::ExtensionMessage message;
- message.set_type(kGnubbyAuthMessage);
- message.set_data(request_json);
+void GnubbyAuthHandlerLinux::SendErrorAndCloseConnection(
+ int gnubby_connection_id) {
+ ActiveSockets::const_iterator iter =
+ GetSocketForConnectionId(gnubby_connection_id);
+ if (iter != active_sockets_.end()) {
+ HOST_LOG << "Sending gnubby error";
+ SendErrorAndCloseActiveSocket(iter);
+ } else {
+ LOG(WARNING) << "Unknown gnubby-auth data connection: '"
+ << gnubby_connection_id << "'";
+ }
+}
- client_stub_->DeliverHostMessage(message);
+void GnubbyAuthHandlerLinux::SetSendMessageCallback(
+ const SendMessageCallback& callback) {
+ send_message_callback_ = callback;
}
size_t GnubbyAuthHandlerLinux::GetActiveSocketsMapSizeForTest() const {
@@ -200,7 +151,7 @@ void GnubbyAuthHandlerLinux::DoAccept() {
}
void GnubbyAuthHandlerLinux::OnAccepted(int result) {
- DCHECK(CalledOnValidThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_NE(net::ERR_IO_PENDING, result);
if (result < 0) {
@@ -208,93 +159,57 @@ void GnubbyAuthHandlerLinux::OnAccepted(int result) {
return;
}
- int connection_id = ++last_connection_id_;
- GnubbySocket* socket =
- new GnubbySocket(std::move(accept_socket_), request_timeout_,
- base::Bind(&GnubbyAuthHandlerLinux::RequestTimedOut,
- base::Unretained(this), connection_id));
- active_sockets_[connection_id] = socket;
+ int gnubby_connection_id = ++last_connection_id_;
+ GnubbySocket* socket = new GnubbySocket(
+ std::move(accept_socket_), request_timeout_,
+ base::Bind(&GnubbyAuthHandlerLinux::RequestTimedOut,
+ base::Unretained(this), gnubby_connection_id));
+ active_sockets_[gnubby_connection_id] = socket;
socket->StartReadingRequest(
base::Bind(&GnubbyAuthHandlerLinux::OnReadComplete,
- base::Unretained(this), connection_id));
+ base::Unretained(this), gnubby_connection_id));
// Continue accepting new connections.
DoAccept();
}
-void GnubbyAuthHandlerLinux::OnReadComplete(int connection_id) {
- DCHECK(CalledOnValidThread());
+void GnubbyAuthHandlerLinux::OnReadComplete(int gnubby_connection_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
- ActiveSockets::iterator iter = active_sockets_.find(connection_id);
+ ActiveSockets::const_iterator iter =
+ active_sockets_.find(gnubby_connection_id);
DCHECK(iter != active_sockets_.end());
std::string request_data;
if (!iter->second->GetAndClearRequestData(&request_data)) {
SendErrorAndCloseActiveSocket(iter);
return;
}
- ProcessGnubbyRequest(connection_id, request_data);
- iter->second->StartReadingRequest(
- base::Bind(&GnubbyAuthHandlerLinux::OnReadComplete,
- base::Unretained(this), connection_id));
-}
-
-void GnubbyAuthHandlerLinux::CreateAuthorizationSocket() {
- DCHECK(CalledOnValidThread());
-
- if (!g_gnubby_socket_name.Get().empty()) {
- {
- // DeleteFile() is a blocking operation, but so is creation of the unix
- // socket below. Consider moving this class to a different thread if this
- // causes any problems. See crbug.com/509807 .
- base::ThreadRestrictions::ScopedAllowIO allow_io;
-
- // If the file already exists, a socket in use error is returned.
- base::DeleteFile(g_gnubby_socket_name.Get(), false);
- }
-
- HOST_LOG << "Listening for gnubby requests on "
- << g_gnubby_socket_name.Get().value();
-
- auth_socket_.reset(
- new net::UnixDomainServerSocket(base::Bind(MatchUid), false));
- int rv = auth_socket_->BindAndListen(g_gnubby_socket_name.Get().value(),
- /*backlog=*/1);
- if (rv != net::OK) {
- LOG(ERROR) << "Failed to open socket for gnubby requests";
- return;
- }
- DoAccept();
- } else {
- HOST_LOG << "No gnubby socket name specified";
- }
-}
-void GnubbyAuthHandlerLinux::ProcessGnubbyRequest(
- int connection_id,
- const std::string& request_data) {
HOST_LOG << "Received gnubby request: " << GetCommandCode(request_data);
- DeliverHostDataMessage(connection_id, request_data);
+ send_message_callback_.Run(gnubby_connection_id, request_data);
+
+ iter->second->StartReadingRequest(
+ base::Bind(&GnubbyAuthHandlerLinux::OnReadComplete,
+ base::Unretained(this), gnubby_connection_id));
}
-GnubbyAuthHandlerLinux::ActiveSockets::iterator
-GnubbyAuthHandlerLinux::GetSocketForMessage(base::DictionaryValue* message) {
- int connection_id;
- if (message->GetInteger(kConnectionId, &connection_id)) {
- return active_sockets_.find(connection_id);
- }
- return active_sockets_.end();
+GnubbyAuthHandlerLinux::ActiveSockets::const_iterator
+GnubbyAuthHandlerLinux::GetSocketForConnectionId(
+ int gnubby_connection_id) const {
+ return active_sockets_.find(gnubby_connection_id);
}
void GnubbyAuthHandlerLinux::SendErrorAndCloseActiveSocket(
- const ActiveSockets::iterator& iter) {
+ const ActiveSockets::const_iterator& iter) {
iter->second->SendSshError();
delete iter->second;
active_sockets_.erase(iter);
}
-void GnubbyAuthHandlerLinux::RequestTimedOut(int connection_id) {
+void GnubbyAuthHandlerLinux::RequestTimedOut(int gnubby_connection_id) {
HOST_LOG << "Gnubby request timed out";
- ActiveSockets::iterator iter = active_sockets_.find(connection_id);
+ ActiveSockets::const_iterator iter =
+ active_sockets_.find(gnubby_connection_id);
if (iter != active_sockets_.end())
SendErrorAndCloseActiveSocket(iter);
}
diff --git a/remoting/host/security_key/gnubby_auth_handler_linux.h b/remoting/host/security_key/gnubby_auth_handler_linux.h
index a3c324e..ff27c20 100644
--- a/remoting/host/security_key/gnubby_auth_handler_linux.h
+++ b/remoting/host/security_key/gnubby_auth_handler_linux.h
@@ -12,31 +12,22 @@
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/threading/thread_checker.h"
#include "net/base/completion_callback.h"
#include "net/socket/stream_socket.h"
#include "remoting/host/security_key/gnubby_auth_handler.h"
-namespace base {
-class DictionaryValue;
-} // namespace base
-
namespace net {
class UnixDomainServerSocket;
} // namespace net
namespace remoting {
-namespace protocol {
-class ClientStub;
-} // namespace protocol
-
class GnubbySocket;
-class GnubbyAuthHandlerLinux : public GnubbyAuthHandler,
- public base::NonThreadSafe {
+class GnubbyAuthHandlerLinux : public GnubbyAuthHandler {
public:
- explicit GnubbyAuthHandlerLinux(protocol::ClientStub* client_stub);
+ GnubbyAuthHandlerLinux();
~GnubbyAuthHandlerLinux() override;
size_t GetActiveSocketsMapSizeForTest() const;
@@ -47,9 +38,12 @@ class GnubbyAuthHandlerLinux : public GnubbyAuthHandler,
typedef std::map<int, GnubbySocket*> ActiveSockets;
// GnubbyAuthHandler interface.
- void DeliverClientMessage(const std::string& message) override;
- void DeliverHostDataMessage(int connection_id,
- const std::string& data) const override;
+ 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;
// Starts listening for connection.
void DoAccept();
@@ -58,25 +52,20 @@ class GnubbyAuthHandlerLinux : public GnubbyAuthHandler,
void OnAccepted(int result);
// Called when a GnubbySocket has done reading.
- void OnReadComplete(int connection_id);
-
- // Create socket for authorization.
- void CreateAuthorizationSocket();
+ void OnReadComplete(int gnubby_connection_id);
- // Process a gnubby request.
- void ProcessGnubbyRequest(int connection_id, const std::string& request_data);
-
- // Gets an active socket iterator for the connection id in |message|.
- ActiveSockets::iterator GetSocketForMessage(base::DictionaryValue* message);
+ // Gets an active socket iterator for |gnubby_connection_id|.
+ ActiveSockets::const_iterator GetSocketForConnectionId(
+ int gnubby_connection_id) const;
// Send an error and closes an active socket.
- void SendErrorAndCloseActiveSocket(const ActiveSockets::iterator& iter);
+ void SendErrorAndCloseActiveSocket(const ActiveSockets::const_iterator& iter);
// A request timed out.
- void RequestTimedOut(int connection_id);
+ void RequestTimedOut(int gnubby_connection_id);
- // Interface through which communication with the client occurs.
- protocol::ClientStub* client_stub_;
+ // Ensures GnubbyAuthHandlerLinux methods are called on the same thread.
+ base::ThreadChecker thread_checker_;
// Socket used to listen for authorization requests.
scoped_ptr<net::UnixDomainServerSocket> auth_socket_;
@@ -84,6 +73,9 @@ class GnubbyAuthHandlerLinux : public GnubbyAuthHandler,
// A temporary holder for an accepted connection.
scoped_ptr<net::StreamSocket> accept_socket_;
+ // Used to pass gnubby extension messages to the client.
+ SendMessageCallback send_message_callback_;
+
// The last assigned gnubby connection id.
int last_connection_id_;
diff --git a/remoting/host/security_key/gnubby_auth_handler_linux_unittest.cc b/remoting/host/security_key/gnubby_auth_handler_linux_unittest.cc
index 2b73ffc..6a20326 100644
--- a/remoting/host/security_key/gnubby_auth_handler_linux_unittest.cc
+++ b/remoting/host/security_key/gnubby_auth_handler_linux_unittest.cc
@@ -6,7 +6,6 @@
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
-#include "base/json/json_writer.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
@@ -20,7 +19,6 @@
#include "remoting/host/security_key/gnubby_auth_handler_linux.h"
#include "remoting/host/security_key/gnubby_socket.h"
#include "remoting/proto/internal.pb.h"
-#include "remoting/protocol/client_stub.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace remoting {
@@ -48,59 +46,37 @@ const unsigned char kRequestData[] = {
} // namespace
-class TestClientStub : public protocol::ClientStub {
+class GnubbyAuthHandlerLinuxTest : public testing::Test {
public:
- TestClientStub() : loop_(new base::RunLoop) {}
- ~TestClientStub() override {}
-
- // protocol::ClientStub implementation.
- void SetCapabilities(const protocol::Capabilities& capabilities) override {}
-
- void SetPairingResponse(
- const protocol::PairingResponse& pairing_response) override {}
+ GnubbyAuthHandlerLinuxTest()
+ : run_loop_(new base::RunLoop()), last_connection_id_received_(-1) {
+ EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
+ socket_path_ = temp_dir_.path().Append(kSocketFilename);
- void DeliverHostMessage(const protocol::ExtensionMessage& message) override {
- message_ = message;
- loop_->Quit();
+ send_message_callback_ =
+ base::Bind(&GnubbyAuthHandlerLinuxTest::SendMessageToClient,
+ base::Unretained(this));
+ auth_handler_linux_.reset(new GnubbyAuthHandlerLinux());
+ auth_handler_ = auth_handler_linux_.get();
+ auth_handler_->SetSendMessageCallback(send_message_callback_);
+ auth_handler_->SetGnubbySocketName(socket_path_);
}
- // protocol::ClipboardStub implementation.
- void InjectClipboardEvent(const protocol::ClipboardEvent& event) override {}
-
- // protocol::CursorShapeStub implementation.
- void SetCursorShape(const protocol::CursorShapeInfo& cursor_shape) override {}
-
- void WaitForDeliverHostMessage() {
- loop_->Run();
- loop_.reset(new base::RunLoop);
+ void WaitForSendMessageToClient() {
+ run_loop_->Run();
+ run_loop_.reset(new base::RunLoop);
}
- void CheckHostDataMessage(int id, const std::string& data) {
- std::string connection_id = base::StringPrintf("\"connectionId\":%d", id);
- std::string data_message = base::StringPrintf("\"data\":%s", data.c_str());
-
- ASSERT_TRUE(message_.type() == "gnubby-auth" ||
- message_.type() == "auth-v1");
- ASSERT_NE(message_.data().find("\"type\":\"data\""), std::string::npos);
- ASSERT_NE(message_.data().find(connection_id), std::string::npos);
- ASSERT_NE(message_.data().find(data_message), std::string::npos);
+ void SendMessageToClient(int connection_id, const std::string& data) {
+ last_connection_id_received_ = connection_id;
+ last_message_received_ = data;
+ run_loop_->Quit();
}
- private:
- protocol::ExtensionMessage message_;
- scoped_ptr<base::RunLoop> loop_;
-
- DISALLOW_COPY_AND_ASSIGN(TestClientStub);
-};
-
-class GnubbyAuthHandlerLinuxTest : public testing::Test {
- public:
- GnubbyAuthHandlerLinuxTest() {
- EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
- socket_path_ = temp_dir_.path().Append(kSocketFilename);
- auth_handler_posix_.reset(new GnubbyAuthHandlerLinux(&client_stub_));
- auth_handler_ = auth_handler_posix_.get();
- auth_handler_->SetGnubbySocketName(socket_path_);
+ void CheckHostDataMessage(int id, const std::string& expected_data) {
+ ASSERT_EQ(id, last_connection_id_received_);
+ ASSERT_EQ(expected_data.length(), last_message_received_.length());
+ ASSERT_EQ(expected_data, last_message_received_);
}
void WriteRequestData(net::UnixDomainClientSocket* client_socket) {
@@ -125,45 +101,46 @@ class GnubbyAuthHandlerLinuxTest : public testing::Test {
ASSERT_EQ(request_len, bytes_written);
}
- void WaitForAndVerifyHostMessage() {
- client_stub_.WaitForDeliverHostMessage();
- base::ListValue expected_data;
- // Skip first four bytes.
+ void WaitForAndVerifyHostMessage(int connection_id) {
+ WaitForSendMessageToClient();
+ std::string expected_data;
+ expected_data.reserve(sizeof(kRequestData) - 4);
+
+ // Skip first four bytes and build up the response string.
for (size_t i = 4; i < sizeof(kRequestData); ++i) {
- expected_data.AppendInteger(kRequestData[i]);
+ expected_data.append(1, static_cast<unsigned char>(kRequestData[i]));
}
- std::string expected_data_json;
- base::JSONWriter::Write(expected_data, &expected_data_json);
- client_stub_.CheckHostDataMessage(1, expected_data_json);
+ CheckHostDataMessage(connection_id, expected_data);
}
protected:
+ base::MessageLoopForIO message_loop_;
+ scoped_ptr<base::RunLoop> run_loop_;
+
// Object under test.
- scoped_ptr<GnubbyAuthHandlerLinux> auth_handler_posix_;
+ scoped_ptr<GnubbyAuthHandlerLinux> auth_handler_linux_;
- // GnubbyAuthHandler interface for |auth_handler_posix_|.
+ // GnubbyAuthHandler interface for |auth_handler_linux_|.
GnubbyAuthHandler* auth_handler_;
- base::MessageLoopForIO message_loop_;
- TestClientStub client_stub_;
+ GnubbyAuthHandler::SendMessageCallback send_message_callback_;
+
+ int last_connection_id_received_;
+ std::string last_message_received_;
+
base::ScopedTempDir temp_dir_;
base::FilePath socket_path_;
base::Closure accept_callback_;
-};
-TEST_F(GnubbyAuthHandlerLinuxTest, HostDataMessageDelivered) {
- auth_handler_->DeliverHostDataMessage(42, "test_msg");
- client_stub_.WaitForDeliverHostMessage();
- // Expects a JSON array of the ASCII character codes for "test_msg".
- client_stub_.CheckHostDataMessage(42, "[116,101,115,116,95,109,115,103]");
-}
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GnubbyAuthHandlerLinuxTest);
+};
TEST_F(GnubbyAuthHandlerLinuxTest, NotClosedAfterRequest) {
- ASSERT_EQ(0u, auth_handler_posix_->GetActiveSocketsMapSizeForTest());
+ ASSERT_EQ(0u, auth_handler_linux_->GetActiveSocketsMapSizeForTest());
- const char message_json[] = "{\"type\":\"control\",\"option\":\"auth-v1\"}";
- auth_handler_->DeliverClientMessage(message_json);
+ auth_handler_->CreateGnubbyConnection();
net::UnixDomainClientSocket client_socket(socket_path_.value(), false);
net::TestCompletionCallback connect_callback;
@@ -173,17 +150,19 @@ TEST_F(GnubbyAuthHandlerLinuxTest, NotClosedAfterRequest) {
// Write the request and verify the response.
WriteRequestData(&client_socket);
- WaitForAndVerifyHostMessage();
+ WaitForAndVerifyHostMessage(1);
+
+ // Verify the connection is now valid.
+ ASSERT_TRUE(auth_handler_->IsValidConnectionId(1));
// Verify that completing a request/response cycle didn't close the socket.
- ASSERT_EQ(1u, auth_handler_posix_->GetActiveSocketsMapSizeForTest());
+ ASSERT_EQ(1u, auth_handler_linux_->GetActiveSocketsMapSizeForTest());
}
TEST_F(GnubbyAuthHandlerLinuxTest, HandleTwoRequests) {
- ASSERT_EQ(0u, auth_handler_posix_->GetActiveSocketsMapSizeForTest());
+ ASSERT_EQ(0u, auth_handler_linux_->GetActiveSocketsMapSizeForTest());
- const char message_json[] = "{\"type\":\"control\",\"option\":\"auth-v1\"}";
- auth_handler_->DeliverClientMessage(message_json);
+ auth_handler_->CreateGnubbyConnection();
net::UnixDomainClientSocket client_socket(socket_path_.value(), false);
net::TestCompletionCallback connect_callback;
@@ -193,42 +172,81 @@ TEST_F(GnubbyAuthHandlerLinuxTest, HandleTwoRequests) {
// Write the request and verify the response.
WriteRequestData(&client_socket);
- WaitForAndVerifyHostMessage();
+ WaitForAndVerifyHostMessage(1);
+
+ // Verify the connection is now valid.
+ ASSERT_TRUE(auth_handler_->IsValidConnectionId(1));
// Repeat the request/response cycle.
WriteRequestData(&client_socket);
- WaitForAndVerifyHostMessage();
+ WaitForAndVerifyHostMessage(1);
+
+ // Verify the connection is still valid.
+ ASSERT_TRUE(auth_handler_->IsValidConnectionId(1));
+
+ // Verify that completing two request/response cycles didn't close the
+ // socket.
+ ASSERT_EQ(1u, auth_handler_linux_->GetActiveSocketsMapSizeForTest());
+}
+
+TEST_F(GnubbyAuthHandlerLinuxTest, HandleTwoIndependentRequests) {
+ ASSERT_EQ(0u, auth_handler_linux_->GetActiveSocketsMapSizeForTest());
+
+ auth_handler_->CreateGnubbyConnection();
+
+ net::UnixDomainClientSocket client_socket(socket_path_.value(), false);
+ net::TestCompletionCallback connect_callback;
+
+ int rv = client_socket.Connect(connect_callback.callback());
+ ASSERT_EQ(net::OK, connect_callback.GetResult(rv));
+
+ // Write the request and verify the response.
+ WriteRequestData(&client_socket);
+ WaitForAndVerifyHostMessage(1);
+
+ // Verify the first connection is now valid.
+ ASSERT_TRUE(auth_handler_->IsValidConnectionId(1));
+
+ // Disconnect and establish a new connection.
+ client_socket.Disconnect();
+ rv = client_socket.Connect(connect_callback.callback());
+ ASSERT_EQ(net::OK, connect_callback.GetResult(rv));
+
+ // Repeat the request/response cycle.
+ WriteRequestData(&client_socket);
+ WaitForAndVerifyHostMessage(2);
+
+ // Verify the second connection is valid and the first is not.
+ ASSERT_TRUE(auth_handler_->IsValidConnectionId(2));
+ ASSERT_FALSE(auth_handler_->IsValidConnectionId(1));
- // Verify that completing two request/response cycles didn't close the socket.
- ASSERT_EQ(1u, auth_handler_posix_->GetActiveSocketsMapSizeForTest());
+ // Verify that the initial socket was released properly.
+ ASSERT_EQ(1u, auth_handler_linux_->GetActiveSocketsMapSizeForTest());
}
TEST_F(GnubbyAuthHandlerLinuxTest, DidReadTimeout) {
- std::string message_json = "{\"type\":\"control\",\"option\":\"auth-v1\"}";
+ ASSERT_EQ(0u, auth_handler_linux_->GetActiveSocketsMapSizeForTest());
+ auth_handler_->CreateGnubbyConnection();
- ASSERT_EQ(0u, auth_handler_posix_->GetActiveSocketsMapSizeForTest());
- auth_handler_->DeliverClientMessage(message_json);
net::UnixDomainClientSocket client_socket(socket_path_.value(), false);
net::TestCompletionCallback connect_callback;
int rv = client_socket.Connect(connect_callback.callback());
ASSERT_EQ(net::OK, connect_callback.GetResult(rv));
- auth_handler_posix_->SetRequestTimeoutForTest(base::TimeDelta());
- ASSERT_EQ(0u, auth_handler_posix_->GetActiveSocketsMapSizeForTest());
+ auth_handler_linux_->SetRequestTimeoutForTest(base::TimeDelta());
+ ASSERT_EQ(0u, auth_handler_linux_->GetActiveSocketsMapSizeForTest());
}
TEST_F(GnubbyAuthHandlerLinuxTest, ClientErrorMessageDelivered) {
- std::string message_json = "{\"type\":\"control\",\"option\":\"auth-v1\"}";
+ ASSERT_EQ(0u, auth_handler_linux_->GetActiveSocketsMapSizeForTest());
+ auth_handler_->CreateGnubbyConnection();
- ASSERT_EQ(0u, auth_handler_posix_->GetActiveSocketsMapSizeForTest());
- auth_handler_->DeliverClientMessage(message_json);
net::UnixDomainClientSocket client_socket(socket_path_.value(), false);
net::TestCompletionCallback connect_callback;
int rv = client_socket.Connect(connect_callback.callback());
ASSERT_EQ(net::OK, connect_callback.GetResult(rv));
- std::string error_json = "{\"type\":\"error\",\"connectionId\":1}";
- auth_handler_->DeliverClientMessage(error_json);
- ASSERT_EQ(0u, auth_handler_posix_->GetActiveSocketsMapSizeForTest());
+ auth_handler_->SendErrorAndCloseConnection(1);
+ ASSERT_EQ(0u, auth_handler_linux_->GetActiveSocketsMapSizeForTest());
}
} // namespace remoting
diff --git a/remoting/host/security_key/gnubby_auth_handler_mac.cc b/remoting/host/security_key/gnubby_auth_handler_mac.cc
index b76d77c..74e44aa 100644
--- a/remoting/host/security_key/gnubby_auth_handler_mac.cc
+++ b/remoting/host/security_key/gnubby_auth_handler_mac.cc
@@ -5,44 +5,17 @@
#include "remoting/host/security_key/gnubby_auth_handler.h"
#include "base/logging.h"
-#include "base/macros.h"
namespace remoting {
-namespace {
-
-class GnubbyAuthHandlerMac : public GnubbyAuthHandler {
- private:
- // GnubbyAuthHandler interface.
- void DeliverClientMessage(const std::string& message) override;
- void DeliverHostDataMessage(int connection_id,
- const std::string& data) const override;
-
- DISALLOW_COPY_AND_ASSIGN(GnubbyAuthHandlerMac);
-};
-
-} // namespace
-
-// static
scoped_ptr<GnubbyAuthHandler> GnubbyAuthHandler::Create(
- protocol::ClientStub* client_stub) {
+ const SendMessageCallback& callback) {
return nullptr;
}
-// static
void GnubbyAuthHandler::SetGnubbySocketName(
const base::FilePath& gnubby_socket_name) {
NOTIMPLEMENTED();
}
-void GnubbyAuthHandlerMac::DeliverClientMessage(const std::string& message) {
- NOTIMPLEMENTED();
-}
-
-void GnubbyAuthHandlerMac::DeliverHostDataMessage(
- int connection_id,
- const std::string& data) const {
- NOTIMPLEMENTED();
-}
-
} // namespace remoting
diff --git a/remoting/host/security_key/gnubby_auth_handler_win.cc b/remoting/host/security_key/gnubby_auth_handler_win.cc
index 83351ab..886b899 100644
--- a/remoting/host/security_key/gnubby_auth_handler_win.cc
+++ b/remoting/host/security_key/gnubby_auth_handler_win.cc
@@ -5,44 +5,17 @@
#include "remoting/host/security_key/gnubby_auth_handler.h"
#include "base/logging.h"
-#include "base/macros.h"
namespace remoting {
-namespace {
-
-class GnubbyAuthHandlerWin : public GnubbyAuthHandler {
- private:
- // GnubbyAuthHandler interface.
- void DeliverClientMessage(const std::string& message) override;
- void DeliverHostDataMessage(int connection_id,
- const std::string& data) const override;
-
- DISALLOW_COPY_AND_ASSIGN(GnubbyAuthHandlerWin);
-};
-
-} // namespace
-
-// static
scoped_ptr<GnubbyAuthHandler> GnubbyAuthHandler::Create(
- protocol::ClientStub* client_stub) {
+ const GnubbyAuthHandler::SendMessageCallback& callback) {
return nullptr;
}
-// static
void GnubbyAuthHandler::SetGnubbySocketName(
const base::FilePath& gnubby_socket_name) {
NOTIMPLEMENTED();
}
-void GnubbyAuthHandlerWin::DeliverClientMessage(const std::string& message) {
- NOTIMPLEMENTED();
-}
-
-void GnubbyAuthHandlerWin::DeliverHostDataMessage(
- int connection_id,
- const std::string& data) const {
- NOTIMPLEMENTED();
-}
-
} // namespace remoting
diff --git a/remoting/host/security_key/gnubby_extension.cc b/remoting/host/security_key/gnubby_extension.cc
new file mode 100644
index 0000000..d225f31
--- /dev/null
+++ b/remoting/host/security_key/gnubby_extension.cc
@@ -0,0 +1,34 @@
+// 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_extension.h"
+
+#include "remoting/host/security_key/gnubby_extension_session.h"
+
+namespace {
+// TODO(joedow): Update this once clients support sending a gnubby capabililty.
+// Tracked via: crbug.com/585835
+const char kCapability[] = "";
+}
+
+namespace remoting {
+
+GnubbyExtension::GnubbyExtension() {}
+
+GnubbyExtension::~GnubbyExtension() {}
+
+std::string GnubbyExtension::capability() const {
+ return kCapability;
+}
+
+scoped_ptr<HostExtensionSession> GnubbyExtension::CreateExtensionSession(
+ ClientSessionControl* client_session_control,
+ protocol::ClientStub* client_stub) {
+ // TODO(joedow): Update this mechanism to allow for multiple sessions. The
+ // extension will only send messages through the initial
+ // |client_stub| with the current design.
+ return make_scoped_ptr(new GnubbyExtensionSession(client_stub));
+}
+
+} // namespace remoting
diff --git a/remoting/host/security_key/gnubby_extension.h b/remoting/host/security_key/gnubby_extension.h
new file mode 100644
index 0000000..7cad095
--- /dev/null
+++ b/remoting/host/security_key/gnubby_extension.h
@@ -0,0 +1,34 @@
+// 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_GNUBBY_EXTENSION_H_
+#define REMOTING_HOST_SECURITY_KEY_GNUBBY_EXTENSION_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "remoting/host/host_extension.h"
+
+namespace remoting {
+
+// GnubbyExtension extends HostExtension to enable Security Key support.
+class GnubbyExtension : public HostExtension {
+ public:
+ GnubbyExtension();
+ ~GnubbyExtension() override;
+
+ // HostExtension interface.
+ std::string capability() const override;
+ scoped_ptr<HostExtensionSession> CreateExtensionSession(
+ ClientSessionControl* client_session_control,
+ protocol::ClientStub* client_stub) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GnubbyExtension);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_SECURITY_KEY_GNUBBY_EXTENSION_H_
diff --git a/remoting/host/security_key/gnubby_extension_session.cc b/remoting/host/security_key/gnubby_extension_session.cc
new file mode 100644
index 0000000..d53338c
--- /dev/null
+++ b/remoting/host/security_key/gnubby_extension_session.cc
@@ -0,0 +1,207 @@
+// 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_extension_session.h"
+
+#include "base/bind.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/values.h"
+#include "remoting/base/logging.h"
+#include "remoting/host/security_key/gnubby_auth_handler.h"
+#include "remoting/proto/control.pb.h"
+#include "remoting/protocol/client_stub.h"
+
+namespace {
+
+// Used as the type attribute of all Security Key protocol::ExtensionMessages.
+const char kExtensionMessageType[] = "gnubby-auth";
+
+// Gnubby extension message data members.
+const char kConnectionId[] = "connectionId";
+const char kControlMessage[] = "control";
+const char kControlOption[] = "option";
+const char kDataMessage[] = "data";
+const char kDataPayload[] = "data";
+const char kErrorMessage[] = "error";
+const char kGnubbyAuthV1[] = "auth-v1";
+const char kMessageType[] = "type";
+
+// 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]);
+}
+
+// Creates a string of byte data from a ListValue of numbers. Returns true if
+// all of the list elements are numbers.
+bool ConvertListValueToString(base::ListValue* bytes, std::string* out) {
+ out->clear();
+
+ unsigned int byte_count = bytes->GetSize();
+ if (byte_count != 0) {
+ out->reserve(byte_count);
+ for (unsigned int i = 0; i < byte_count; i++) {
+ int value;
+ if (!bytes->GetInteger(i, &value)) {
+ return false;
+ }
+ out->push_back(static_cast<char>(value));
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+namespace remoting {
+
+GnubbyExtensionSession::GnubbyExtensionSession(
+ protocol::ClientStub* client_stub)
+ : client_stub_(client_stub) {
+ DCHECK(client_stub_);
+
+ gnubby_auth_handler_ = remoting::GnubbyAuthHandler::Create(base::Bind(
+ &GnubbyExtensionSession::SendMessageToClient, base::Unretained(this)));
+}
+
+GnubbyExtensionSession::~GnubbyExtensionSession() {}
+
+// Returns true if the |message| is a Security Key ExtensionMessage.
+// This is done so the host does not pass |message| to other HostExtensions.
+// TODO(joedow): Use |client_session_control| to disconnect the session if we
+// receive an invalid extension message.
+bool GnubbyExtensionSession::OnExtensionMessage(
+ ClientSessionControl* client_session_control,
+ protocol::ClientStub* client_stub,
+ const protocol::ExtensionMessage& message) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (message.type() != kExtensionMessageType) {
+ return false;
+ }
+
+ scoped_ptr<base::Value> value = base::JSONReader::Read(message.data());
+ base::DictionaryValue* client_message;
+ if (!value || !value->GetAsDictionary(&client_message)) {
+ LOG(WARNING) << "Failed to retrieve data from gnubby-auth message.";
+ return true;
+ }
+
+ std::string type;
+ if (!client_message->GetString(kMessageType, &type)) {
+ LOG(WARNING) << "Invalid gnubby-auth message format.";
+ return true;
+ }
+
+ if (type == kControlMessage) {
+ ProcessControlMessage(client_message);
+ } else if (type == kDataMessage) {
+ ProcessDataMessage(client_message);
+ } else if (type == kErrorMessage) {
+ ProcessErrorMessage(client_message);
+ } else {
+ VLOG(2) << "Unknown gnubby-auth message type: " << type;
+ }
+
+ return true;
+}
+
+void GnubbyExtensionSession::ProcessControlMessage(
+ base::DictionaryValue* message_data) const {
+ std::string option;
+ if (!message_data->GetString(kControlOption, &option)) {
+ LOG(WARNING) << "Could not extract control option from message.";
+ return;
+ }
+
+ if (option == kGnubbyAuthV1) {
+ gnubby_auth_handler_->CreateGnubbyConnection();
+ } else {
+ VLOG(2) << "Invalid gnubby-auth control option: " << option;
+ }
+}
+
+void GnubbyExtensionSession::ProcessDataMessage(
+ base::DictionaryValue* message_data) const {
+ int connection_id;
+ if (!message_data->GetInteger(kConnectionId, &connection_id)) {
+ LOG(WARNING) << "Could not extract connection id from message.";
+ return;
+ }
+
+ if (!gnubby_auth_handler_->IsValidConnectionId(connection_id)) {
+ LOG(WARNING) << "Unknown gnubby-auth data connection: '" << connection_id
+ << "'";
+ return;
+ }
+
+ base::ListValue* bytes;
+ std::string response;
+ if (message_data->GetList(kDataPayload, &bytes) &&
+ ConvertListValueToString(bytes, &response)) {
+ HOST_LOG << "Sending gnubby response: " << GetCommandCode(response);
+ gnubby_auth_handler_->SendClientResponse(connection_id, response);
+ } else {
+ LOG(WARNING) << "Could not extract response data from message.";
+ gnubby_auth_handler_->SendErrorAndCloseConnection(connection_id);
+ return;
+ }
+}
+
+void GnubbyExtensionSession::ProcessErrorMessage(
+ base::DictionaryValue* message_data) const {
+ int connection_id;
+ if (!message_data->GetInteger(kConnectionId, &connection_id)) {
+ LOG(WARNING) << "Could not extract connection id from message.";
+ return;
+ }
+
+ if (gnubby_auth_handler_->IsValidConnectionId(connection_id)) {
+ HOST_LOG << "Sending gnubby error";
+ gnubby_auth_handler_->SendErrorAndCloseConnection(connection_id);
+ } else {
+ LOG(WARNING) << "Unknown gnubby-auth data connection: '" << connection_id
+ << "'";
+ }
+}
+
+void GnubbyExtensionSession::SendMessageToClient(
+ int connection_id,
+ const std::string& data) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(client_stub_);
+
+ base::DictionaryValue request;
+ request.SetString(kMessageType, kDataMessage);
+ request.SetInteger(kConnectionId, connection_id);
+
+ scoped_ptr<base::ListValue> bytes(new base::ListValue());
+ for (std::string::const_iterator i = data.begin(); i != data.end(); ++i) {
+ bytes->AppendInteger(static_cast<unsigned char>(*i));
+ }
+ request.Set(kDataPayload, bytes.release());
+
+ std::string request_json;
+ CHECK(base::JSONWriter::Write(request, &request_json));
+
+ protocol::ExtensionMessage message;
+ message.set_type(kExtensionMessageType);
+ message.set_data(request_json);
+
+ client_stub_->DeliverHostMessage(message);
+}
+
+void GnubbyExtensionSession::SetGnubbyAuthHandlerForTesting(
+ scoped_ptr<GnubbyAuthHandler> gnubby_auth_handler) {
+ DCHECK(gnubby_auth_handler);
+
+ gnubby_auth_handler_ = std::move(gnubby_auth_handler);
+ gnubby_auth_handler_->SetSendMessageCallback(base::Bind(
+ &GnubbyExtensionSession::SendMessageToClient, base::Unretained(this)));
+}
+
+} // namespace remoting
diff --git a/remoting/host/security_key/gnubby_extension_session.h b/remoting/host/security_key/gnubby_extension_session.h
new file mode 100644
index 0000000..82b6703
--- /dev/null
+++ b/remoting/host/security_key/gnubby_extension_session.h
@@ -0,0 +1,65 @@
+// 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_GNUBBY_EXTENSION_SESSION_H_
+#define REMOTING_HOST_SECURITY_KEY_GNUBBY_EXTENSION_SESSION_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "remoting/host/host_extension_session.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace remoting {
+
+class GnubbyAuthHandler;
+
+namespace protocol {
+class ClientStub;
+}
+
+// A HostExtensionSession implementation that enables Security Key support.
+class GnubbyExtensionSession : public HostExtensionSession {
+ public:
+ explicit GnubbyExtensionSession(protocol::ClientStub* client_stub);
+ ~GnubbyExtensionSession() override;
+
+ // HostExtensionSession interface.
+ bool OnExtensionMessage(ClientSessionControl* client_session_control,
+ protocol::ClientStub* client_stub,
+ const protocol::ExtensionMessage& message) override;
+
+ // Allows the underlying GnubbyAuthHandler to be overridden for unit testing.
+ void SetGnubbyAuthHandlerForTesting(
+ scoped_ptr<GnubbyAuthHandler> gnubby_auth_handler);
+
+ private:
+ // These methods process specific gnubby extension message types.
+ void ProcessControlMessage(base::DictionaryValue* message_data) const;
+ void ProcessDataMessage(base::DictionaryValue* message_data) const;
+ void ProcessErrorMessage(base::DictionaryValue* message_data) const;
+
+ void SendMessageToClient(int connection_id, const std::string& data) const;
+
+ // Ensures GnubbyExtensionSession methods are called on the same thread.
+ base::ThreadChecker thread_checker_;
+
+ // Interface through which messages can be sent to the client.
+ protocol::ClientStub* client_stub_ = nullptr;
+
+ // Handles platform specific gnubby operations.
+ scoped_ptr<GnubbyAuthHandler> gnubby_auth_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(GnubbyExtensionSession);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_SECURITY_KEY_GNUBBY_EXTENSION_SESSION_H_
diff --git a/remoting/host/security_key/gnubby_extension_session_unittest.cc b/remoting/host/security_key/gnubby_extension_session_unittest.cc
new file mode 100644
index 0000000..f763670
--- /dev/null
+++ b/remoting/host/security_key/gnubby_extension_session_unittest.cc
@@ -0,0 +1,377 @@
+// 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 <stddef.h>
+
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/json/json_writer.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/timer/mock_timer.h"
+#include "base/values.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/socket/unix_domain_client_socket_posix.h"
+#include "remoting/host/host_mock_objects.h"
+#include "remoting/host/security_key/gnubby_auth_handler.h"
+#include "remoting/host/security_key/gnubby_extension_session.h"
+#include "remoting/proto/internal.pb.h"
+#include "remoting/protocol/client_stub.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace remoting {
+
+namespace {
+
+// Test gnubby request data.
+const unsigned char kRequestData[] = {
+ 0x00, 0x00, 0x00, 0x9a, 0x65, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x60, 0x90,
+ 0x24, 0x71, 0xf8, 0xf2, 0xe5, 0xdf, 0x7f, 0x81, 0xc7, 0x49, 0xc4, 0xa3,
+ 0x58, 0x5c, 0xf6, 0xcc, 0x40, 0x14, 0x28, 0x0c, 0xa0, 0xfa, 0x03, 0x18,
+ 0x38, 0xd8, 0x7d, 0x77, 0x2b, 0x3a, 0x00, 0x00, 0x00, 0x20, 0x64, 0x46,
+ 0x47, 0x2f, 0xdf, 0x6e, 0xed, 0x7b, 0xf3, 0xc3, 0x37, 0x20, 0xf2, 0x36,
+ 0x67, 0x6c, 0x36, 0xe1, 0xb4, 0x5e, 0xbe, 0x04, 0x85, 0xdb, 0x89, 0xa3,
+ 0xcd, 0xfd, 0xd2, 0x4b, 0xd6, 0x9f, 0x00, 0x00, 0x00, 0x40, 0x38, 0x35,
+ 0x05, 0x75, 0x1d, 0x13, 0x6e, 0xb3, 0x6b, 0x1d, 0x29, 0xae, 0xd3, 0x43,
+ 0xe6, 0x84, 0x8f, 0xa3, 0x9d, 0x65, 0x4e, 0x2f, 0x57, 0xe3, 0xf6, 0xe6,
+ 0x20, 0x3c, 0x00, 0xc6, 0xe1, 0x73, 0x34, 0xe2, 0x23, 0x99, 0xc4, 0xfa,
+ 0x91, 0xc2, 0xd5, 0x97, 0xc1, 0x8b, 0xd0, 0x3c, 0x13, 0xba, 0xf0, 0xd7,
+ 0x5e, 0xa3, 0xbc, 0x02, 0x5b, 0xec, 0xe4, 0x4b, 0xae, 0x0e, 0xf2, 0xbd,
+ 0xc8, 0xaa};
+
+} // namespace
+
+class TestClientStub : public protocol::ClientStub {
+ public:
+ TestClientStub() : run_loop_(new base::RunLoop) {}
+ ~TestClientStub() override {}
+
+ // protocol::ClientStub implementation.
+ void SetCapabilities(const protocol::Capabilities& capabilities) override {}
+
+ void SetPairingResponse(
+ const protocol::PairingResponse& pairing_response) override {}
+
+ void DeliverHostMessage(const protocol::ExtensionMessage& message) override {
+ message_ = message;
+ run_loop_->Quit();
+ }
+
+ // protocol::ClipboardStub implementation.
+ void InjectClipboardEvent(const protocol::ClipboardEvent& event) override {}
+
+ // protocol::CursorShapeStub implementation.
+ void SetCursorShape(const protocol::CursorShapeInfo& cursor_shape) override {}
+
+ void WaitForDeliverHostMessage(const base::TimeDelta& max_timeout) {
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, run_loop_->QuitClosure(), max_timeout);
+ run_loop_->Run();
+ run_loop_.reset(new base::RunLoop);
+ }
+
+ void CheckHostDataMessage(int id, const std::string& data) {
+ std::string connection_id = base::StringPrintf("\"connectionId\":%d", id);
+ std::string data_message = base::StringPrintf("\"data\":%s", data.c_str());
+
+ ASSERT_TRUE(message_.type() == "gnubby-auth" ||
+ message_.type() == "auth-v1");
+ ASSERT_NE(message_.data().find("\"type\":\"data\""), std::string::npos);
+ ASSERT_NE(message_.data().find(connection_id), std::string::npos);
+ ASSERT_NE(message_.data().find(data_message), std::string::npos);
+ }
+
+ private:
+ protocol::ExtensionMessage message_;
+ scoped_ptr<base::RunLoop> run_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestClientStub);
+};
+
+class GnubbyExtensionSessionTest : public testing::Test {
+ public:
+ GnubbyExtensionSessionTest()
+ : gnubby_extension_session_(new GnubbyExtensionSession(&client_stub_)) {
+ // We want to retain ownership of mock object so we can use it to inject
+ // events into the extension session. The mock object should not be used
+ // once |gnubby_extension_session_| is destroyed.
+ mock_gnubby_auth_handler_ = new MockGnubbyAuthHandler();
+ gnubby_extension_session_->SetGnubbyAuthHandlerForTesting(
+ make_scoped_ptr(mock_gnubby_auth_handler_));
+ }
+
+ void WaitForAndVerifyHostMessage() {
+ client_stub_.WaitForDeliverHostMessage(
+ base::TimeDelta::FromMilliseconds(500));
+ base::ListValue expected_data;
+
+ // Skip first four bytes.
+ for (size_t i = 4; i < sizeof(kRequestData); ++i) {
+ expected_data.AppendInteger(kRequestData[i]);
+ }
+
+ std::string expected_data_json;
+ base::JSONWriter::Write(expected_data, &expected_data_json);
+ client_stub_.CheckHostDataMessage(1, expected_data_json);
+ }
+
+ void CreateGnubbyConnection() {
+ EXPECT_CALL(*mock_gnubby_auth_handler_, CreateGnubbyConnection()).Times(1);
+
+ protocol::ExtensionMessage message;
+ message.set_type("gnubby-auth");
+ message.set_data("{\"type\":\"control\",\"option\":\"auth-v1\"}");
+
+ ASSERT_TRUE(gnubby_extension_session_->OnExtensionMessage(nullptr, nullptr,
+ message));
+ }
+
+ protected:
+ base::MessageLoopForIO message_loop_;
+
+ // Object under test.
+ scoped_ptr<GnubbyExtensionSession> gnubby_extension_session_;
+
+ MockGnubbyAuthHandler* mock_gnubby_auth_handler_ = nullptr;
+
+ TestClientStub client_stub_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GnubbyExtensionSessionTest);
+};
+
+TEST_F(GnubbyExtensionSessionTest, GnubbyConnectionCreated_ValidMessage) {
+ CreateGnubbyConnection();
+}
+
+TEST_F(GnubbyExtensionSessionTest, NoGnubbyConnectionCreated_WrongMessageType) {
+ EXPECT_CALL(*mock_gnubby_auth_handler_, CreateGnubbyConnection()).Times(0);
+
+ protocol::ExtensionMessage message;
+ message.set_type("unsupported-gnubby-auth");
+ message.set_data("{\"type\":\"control\",\"option\":\"auth-v1\"}");
+
+ ASSERT_FALSE(
+ gnubby_extension_session_->OnExtensionMessage(nullptr, nullptr, message));
+}
+
+TEST_F(GnubbyExtensionSessionTest,
+ NoGnubbyConnectionCreated_InvalidMessageData) {
+ EXPECT_CALL(*mock_gnubby_auth_handler_, CreateGnubbyConnection()).Times(0);
+
+ // First try invalid JSON.
+ protocol::ExtensionMessage message;
+ message.set_type("gnubby-auth");
+ message.set_data("{\"type\":\"control\",\"option\":}");
+ // handled should still be true, even if the message payload is invalid.
+ ASSERT_TRUE(
+ gnubby_extension_session_->OnExtensionMessage(nullptr, nullptr, message));
+
+ // Now try an invalid message type.
+ message.set_type("gnubby-auth");
+ message.set_data("{\"type\":\"control\",\"option\":\"auth-v0\"}");
+ // handled should still be true, even if the message payload is invalid.
+ ASSERT_TRUE(
+ gnubby_extension_session_->OnExtensionMessage(nullptr, nullptr, message));
+
+ // Now try a message that is missing the option and auth type.
+ message.set_type("gnubby-auth");
+ message.set_data("{\"type\":\"control\"}");
+ // handled should still be true, even if the message payload is invalid.
+ ASSERT_TRUE(
+ gnubby_extension_session_->OnExtensionMessage(nullptr, nullptr, message));
+}
+
+TEST_F(GnubbyExtensionSessionTest, DataMessageProcessing_MissingConnectionId) {
+ CreateGnubbyConnection();
+
+ EXPECT_CALL(*mock_gnubby_auth_handler_,
+ SendClientResponse(testing::_, testing::_))
+ .Times(0);
+ EXPECT_CALL(*mock_gnubby_auth_handler_,
+ SendErrorAndCloseConnection(testing::_))
+ .Times(0);
+ EXPECT_CALL(*mock_gnubby_auth_handler_, IsValidConnectionId(testing::_))
+ .Times(0);
+
+ protocol::ExtensionMessage message;
+ message.set_type("gnubby-auth");
+ message.set_data("{\"type\":\"data\"}");
+
+ ASSERT_TRUE(
+ gnubby_extension_session_->OnExtensionMessage(nullptr, nullptr, message));
+}
+
+TEST_F(GnubbyExtensionSessionTest, DataMessageProcessing_InvalidConnectionId) {
+ CreateGnubbyConnection();
+
+ EXPECT_CALL(*mock_gnubby_auth_handler_,
+ SendClientResponse(testing::_, testing::_))
+ .Times(0);
+ EXPECT_CALL(*mock_gnubby_auth_handler_,
+ SendErrorAndCloseConnection(testing::_))
+ .Times(0);
+ EXPECT_CALL(*mock_gnubby_auth_handler_, IsValidConnectionId(1)).Times(1);
+
+ ON_CALL(*mock_gnubby_auth_handler_, IsValidConnectionId(testing::_))
+ .WillByDefault(testing::Return(false));
+
+ protocol::ExtensionMessage message;
+ message.set_type("gnubby-auth");
+ message.set_data("{\"type\":\"data\",\"connectionId\":1}");
+
+ ASSERT_TRUE(
+ gnubby_extension_session_->OnExtensionMessage(nullptr, nullptr, message));
+}
+
+TEST_F(GnubbyExtensionSessionTest, DataMessageProcessing_MissingPayload) {
+ CreateGnubbyConnection();
+
+ EXPECT_CALL(*mock_gnubby_auth_handler_, SendErrorAndCloseConnection(1))
+ .Times(1);
+ EXPECT_CALL(*mock_gnubby_auth_handler_,
+ SendClientResponse(testing::_, testing::_))
+ .Times(0);
+ EXPECT_CALL(*mock_gnubby_auth_handler_, IsValidConnectionId(1)).Times(1);
+
+ ON_CALL(*mock_gnubby_auth_handler_, IsValidConnectionId(testing::_))
+ .WillByDefault(testing::Return(true));
+
+ protocol::ExtensionMessage message;
+ message.set_type("gnubby-auth");
+ message.set_data("{\"type\":\"data\",\"connectionId\":1}");
+
+ ASSERT_TRUE(
+ gnubby_extension_session_->OnExtensionMessage(nullptr, nullptr, message));
+}
+
+TEST_F(GnubbyExtensionSessionTest, DataMessageProcessing_InvalidPayload) {
+ CreateGnubbyConnection();
+
+ EXPECT_CALL(*mock_gnubby_auth_handler_, SendErrorAndCloseConnection(1))
+ .Times(1);
+ EXPECT_CALL(*mock_gnubby_auth_handler_,
+ SendClientResponse(testing::_, testing::_))
+ .Times(0);
+ EXPECT_CALL(*mock_gnubby_auth_handler_, IsValidConnectionId(1)).Times(1);
+
+ ON_CALL(*mock_gnubby_auth_handler_, IsValidConnectionId(testing::_))
+ .WillByDefault(testing::Return(true));
+
+ protocol::ExtensionMessage message;
+ message.set_type("gnubby-auth");
+ message.set_data(
+ "{\"type\":\"data\",\"connectionId\":1,\"data\":[\"a\",\"-\",\"z\"]}");
+
+ ASSERT_TRUE(
+ gnubby_extension_session_->OnExtensionMessage(nullptr, nullptr, message));
+}
+
+TEST_F(GnubbyExtensionSessionTest, DataMessageProcessing_ValidData) {
+ CreateGnubbyConnection();
+
+ EXPECT_CALL(*mock_gnubby_auth_handler_,
+ SendClientResponse(1, "\x1\x2\x3\x4\x5"))
+ .Times(1);
+ EXPECT_CALL(*mock_gnubby_auth_handler_,
+ SendErrorAndCloseConnection(testing::_))
+ .Times(0);
+ EXPECT_CALL(*mock_gnubby_auth_handler_, IsValidConnectionId(1)).Times(1);
+
+ ON_CALL(*mock_gnubby_auth_handler_, IsValidConnectionId(testing::_))
+ .WillByDefault(testing::Return(true));
+
+ protocol::ExtensionMessage message;
+ message.set_type("gnubby-auth");
+ message.set_data(
+ "{\"type\":\"data\",\"connectionId\":1,\"data\":[1,2,3,4,5]}");
+
+ ASSERT_TRUE(
+ gnubby_extension_session_->OnExtensionMessage(nullptr, nullptr, message));
+}
+
+TEST_F(GnubbyExtensionSessionTest, ErrorMessageProcessing_MissingConnectionId) {
+ CreateGnubbyConnection();
+
+ EXPECT_CALL(*mock_gnubby_auth_handler_,
+ SendErrorAndCloseConnection(testing::_))
+ .Times(0);
+ EXPECT_CALL(*mock_gnubby_auth_handler_,
+ SendClientResponse(testing::_, testing::_))
+ .Times(0);
+ EXPECT_CALL(*mock_gnubby_auth_handler_, IsValidConnectionId(testing::_))
+ .Times(0);
+
+ protocol::ExtensionMessage message;
+ message.set_type("gnubby-auth");
+ message.set_data("{\"type\":\"error\"}");
+
+ ASSERT_TRUE(
+ gnubby_extension_session_->OnExtensionMessage(nullptr, nullptr, message));
+}
+
+TEST_F(GnubbyExtensionSessionTest, ErrorMessageProcessing_InvalidConnectionId) {
+ CreateGnubbyConnection();
+
+ EXPECT_CALL(*mock_gnubby_auth_handler_,
+ SendErrorAndCloseConnection(testing::_))
+ .Times(0);
+ EXPECT_CALL(*mock_gnubby_auth_handler_,
+ SendClientResponse(testing::_, testing::_))
+ .Times(0);
+ EXPECT_CALL(*mock_gnubby_auth_handler_, IsValidConnectionId(1)).Times(1);
+
+ ON_CALL(*mock_gnubby_auth_handler_, IsValidConnectionId(testing::_))
+ .WillByDefault(testing::Return(false));
+
+ protocol::ExtensionMessage message;
+ message.set_type("gnubby-auth");
+ message.set_data("{\"type\":\"error\",\"connectionId\":1}");
+
+ ASSERT_TRUE(
+ gnubby_extension_session_->OnExtensionMessage(nullptr, nullptr, message));
+}
+
+TEST_F(GnubbyExtensionSessionTest, ErrorMessageProcessing_ValidData) {
+ CreateGnubbyConnection();
+
+ EXPECT_CALL(*mock_gnubby_auth_handler_, SendErrorAndCloseConnection(1))
+ .Times(1);
+ EXPECT_CALL(*mock_gnubby_auth_handler_,
+ SendClientResponse(testing::_, testing::_))
+ .Times(0);
+ EXPECT_CALL(*mock_gnubby_auth_handler_, IsValidConnectionId(1)).Times(1);
+
+ ON_CALL(*mock_gnubby_auth_handler_, IsValidConnectionId(testing::_))
+ .WillByDefault(testing::Return(true));
+
+ protocol::ExtensionMessage message;
+ message.set_type("gnubby-auth");
+ message.set_data("{\"type\":\"error\",\"connectionId\":1}");
+
+ ASSERT_TRUE(
+ gnubby_extension_session_->OnExtensionMessage(nullptr, nullptr, message));
+}
+
+TEST_F(GnubbyExtensionSessionTest, SendMessageToClient_ValidData) {
+ CreateGnubbyConnection();
+
+ // Inject data into the SendMessageCallback to simulate a gnubby request.
+ mock_gnubby_auth_handler_->GetSendMessageCallback().Run(42, "test_msg");
+
+ client_stub_.WaitForDeliverHostMessage(
+ base::TimeDelta::FromMilliseconds(500));
+
+ // Expects a JSON array of the ASCII character codes for "test_msg".
+ client_stub_.CheckHostDataMessage(42, "[116,101,115,116,95,109,115,103]");
+}
+
+} // namespace remoting
diff --git a/remoting/host/security_key/gnubby_socket.cc b/remoting/host/security_key/gnubby_socket.cc
index cc54a22..0a24688 100644
--- a/remoting/host/security_key/gnubby_socket.cc
+++ b/remoting/host/security_key/gnubby_socket.cc
@@ -39,7 +39,7 @@ GnubbySocket::GnubbySocket(scoped_ptr<net::StreamSocket> socket,
GnubbySocket::~GnubbySocket() {}
bool GnubbySocket::GetAndClearRequestData(std::string* data_out) {
- DCHECK(CalledOnValidThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(read_completed_);
if (!read_completed_)
@@ -54,7 +54,7 @@ bool GnubbySocket::GetAndClearRequestData(std::string* data_out) {
}
void GnubbySocket::SendResponse(const std::string& response_data) {
- DCHECK(CalledOnValidThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!write_buffer_);
std::string response_length_string = GetResponseLengthAsBytes(response_data);
@@ -67,14 +67,14 @@ void GnubbySocket::SendResponse(const std::string& response_data) {
}
void GnubbySocket::SendSshError() {
- DCHECK(CalledOnValidThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
SendResponse(std::string(kSshError, arraysize(kSshError)));
}
void GnubbySocket::StartReadingRequest(
const base::Closure& request_received_callback) {
- DCHECK(CalledOnValidThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(request_received_callback_.is_null());
request_received_callback_ = request_received_callback;
@@ -82,7 +82,7 @@ void GnubbySocket::StartReadingRequest(
}
void GnubbySocket::OnDataWritten(int result) {
- DCHECK(CalledOnValidThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(write_buffer_);
if (result < 0) {
@@ -95,7 +95,7 @@ void GnubbySocket::OnDataWritten(int result) {
}
void GnubbySocket::DoWrite() {
- DCHECK(CalledOnValidThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(write_buffer_);
if (!write_buffer_->BytesRemaining()) {
@@ -110,7 +110,7 @@ void GnubbySocket::DoWrite() {
}
void GnubbySocket::OnDataRead(int result) {
- DCHECK(CalledOnValidThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
if (result <= 0) {
if (result < 0)
@@ -133,7 +133,7 @@ void GnubbySocket::OnDataRead(int result) {
}
void GnubbySocket::DoRead() {
- DCHECK(CalledOnValidThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
int result = socket_->Read(
read_buffer_.get(), kRequestReadBufferLength,
@@ -143,7 +143,7 @@ void GnubbySocket::DoRead() {
}
bool GnubbySocket::IsRequestComplete() const {
- DCHECK(CalledOnValidThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
if (request_data_.size() < kRequestSizeBytes)
return false;
@@ -151,7 +151,7 @@ bool GnubbySocket::IsRequestComplete() const {
}
bool GnubbySocket::IsRequestTooLarge() const {
- DCHECK(CalledOnValidThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
if (request_data_.size() < kRequestSizeBytes)
return false;
diff --git a/remoting/host/security_key/gnubby_socket.h b/remoting/host/security_key/gnubby_socket.h
index e37f64f..39d5d05 100644
--- a/remoting/host/security_key/gnubby_socket.h
+++ b/remoting/host/security_key/gnubby_socket.h
@@ -14,7 +14,7 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/threading/thread_checker.h"
namespace base {
class Timer;
@@ -31,7 +31,7 @@ namespace remoting {
// Class that manages reading requests and sending responses. The socket can
// only handle receiving one request at a time. It expects to receive no extra
// bytes over the wire, which is checked by IsRequestTooLarge method.
-class GnubbySocket : public base::NonThreadSafe {
+class GnubbySocket {
public:
GnubbySocket(scoped_ptr<net::StreamSocket> socket,
const base::TimeDelta& timeout,
@@ -82,6 +82,9 @@ class GnubbySocket : public base::NonThreadSafe {
// Resets the socket activity timer.
void ResetTimer();
+ // Ensures GnubbySocket methods are called on the same thread.
+ base::ThreadChecker thread_checker_;
+
// The socket.
scoped_ptr<net::StreamSocket> socket_;