summaryrefslogtreecommitdiffstats
path: root/remoting/host
diff options
context:
space:
mode:
authorpsj@chromium.org <psj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-24 02:33:35 +0000
committerpsj@chromium.org <psj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-24 02:33:35 +0000
commitf66432db0ee32950f0dd04ce8bf3895daae7370e (patch)
tree1b269337e54deae960100fe1e5e8b5c8cb59256b /remoting/host
parentc091d360da97bdeb6f7d025245991b34ac28c273 (diff)
downloadchromium_src-f66432db0ee32950f0dd04ce8bf3895daae7370e.zip
chromium_src-f66432db0ee32950f0dd04ce8bf3895daae7370e.tar.gz
chromium_src-f66432db0ee32950f0dd04ce8bf3895daae7370e.tar.bz2
Do minimal processing of gnubby data. Add request timeouts and send error
responses as necessary. Requires gnubbyd 0.8.37 or later. BUG=134250 Review URL: https://codereview.chromium.org/205493005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@258844 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/host')
-rw-r--r--remoting/host/gnubby_auth_handler_posix.cc198
-rw-r--r--remoting/host/gnubby_auth_handler_posix.h28
-rw-r--r--remoting/host/gnubby_auth_handler_posix_unittest.cc49
-rw-r--r--remoting/host/gnubby_socket.cc116
-rw-r--r--remoting/host/gnubby_socket.h80
-rw-r--r--remoting/host/gnubby_util.cc347
-rw-r--r--remoting/host/gnubby_util.h30
-rw-r--r--remoting/host/gnubby_util_unittest.cc95
8 files changed, 373 insertions, 570 deletions
diff --git a/remoting/host/gnubby_auth_handler_posix.cc b/remoting/host/gnubby_auth_handler_posix.cc
index c797cce..77938e0 100644
--- a/remoting/host/gnubby_auth_handler_posix.cc
+++ b/remoting/host/gnubby_auth_handler_posix.cc
@@ -6,7 +6,6 @@
#include <unistd.h>
#include <utility>
-#include <vector>
#include "base/bind.h"
#include "base/file_util.h"
@@ -17,7 +16,7 @@
#include "base/values.h"
#include "net/socket/unix_domain_socket_posix.h"
#include "remoting/base/logging.h"
-#include "remoting/host/gnubby_util.h"
+#include "remoting/host/gnubby_socket.h"
#include "remoting/proto/control.pb.h"
#include "remoting/protocol/client_stub.h"
@@ -25,15 +24,14 @@ namespace remoting {
namespace {
-const int kMaxRequestLength = 4096;
-
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 kJSONMessage[] = "jsonMessage";
const char kMessageType[] = "type";
// The name of the socket to listen for gnubby requests on.
@@ -45,9 +43,8 @@ class CompareSocket {
public:
explicit CompareSocket(net::StreamListenSocket* socket) : socket_(socket) {}
- bool operator()(const std::pair<int, net::StreamListenSocket*> element)
- const {
- return socket_ == element.second;
+ bool operator()(const std::pair<int, GnubbySocket*> element) const {
+ return element.second->IsSocket(socket_);
}
private:
@@ -63,25 +60,28 @@ bool MatchUid(uid_t user_id, gid_t) {
return allowed;
}
-// Returns the request data length from the first four data bytes.
-int GetRequestLength(const char* data) {
- return ((data[0] & 255) << 24) + ((data[1] & 255) << 16) +
- ((data[2] & 255) << 8) + (data[3] & 255) + 4;
-}
-
-// Returns true if the request data is complete (has at least as many bytes as
-// indicated by the size in the first four bytes plus four for the first bytes).
-bool IsRequestComplete(const char* data, int data_len) {
- if (data_len < 4)
- return false;
- return GetRequestLength(data) <= data_len;
+// 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]);
}
-// Returns true if the request data size is bigger than the threshold.
-bool IsRequestTooLarge(const char* data, int data_len, int max_len) {
- if (data_len < 4)
- return false;
- return GetRequestLength(data) > max_len;
+// 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
@@ -129,22 +129,28 @@ void GnubbyAuthHandlerPosix::DeliverClientMessage(const std::string& message) {
LOG(ERROR) << "Invalid gnubby-auth control option";
}
} else if (type == kDataMessage) {
- int connection_id;
- std::string json_message;
- if (client_message->GetInteger(kConnectionId, &connection_id) &&
- client_message->GetString(kJSONMessage, &json_message)) {
- ActiveSockets::iterator iter = active_sockets_.find(connection_id);
- if (iter != active_sockets_.end()) {
- HOST_LOG << "Sending gnubby response";
-
- std::string response;
- GetGnubbyResponseFromJson(json_message, &response);
- iter->second->Send(response);
+ 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) << "Received gnubby-auth data for unknown connection";
+ LOG(ERROR) << "Invalid gnubby data";
+ SendErrorAndCloseActiveSocket(iter);
}
} else {
- LOG(ERROR) << "Invalid gnubby-auth data message";
+ 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;
@@ -152,15 +158,20 @@ void GnubbyAuthHandlerPosix::DeliverClientMessage(const std::string& message) {
}
}
-void GnubbyAuthHandlerPosix::DeliverHostDataMessage(int connection_id,
- const std::string& data)
- const {
+void GnubbyAuthHandlerPosix::DeliverHostDataMessage(
+ int connection_id,
+ const std::string& data) const {
DCHECK(CalledOnValidThread());
base::DictionaryValue request;
request.SetString(kMessageType, kDataMessage);
request.SetInteger(kConnectionId, connection_id);
- request.SetString(kJSONMessage, data);
+
+ 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);
std::string request_json;
if (!base::JSONWriter::Write(&request, &request_json)) {
@@ -182,12 +193,31 @@ bool GnubbyAuthHandlerPosix::HasActiveSocketForTesting(
CompareSocket(socket)) != active_sockets_.end();
}
+int GnubbyAuthHandlerPosix::GetConnectionIdForTesting(
+ net::StreamListenSocket* socket) const {
+ ActiveSockets::const_iterator iter = std::find_if(
+ active_sockets_.begin(), active_sockets_.end(), CompareSocket(socket));
+ return iter->first;
+}
+
+GnubbySocket* GnubbyAuthHandlerPosix::GetGnubbySocketForTesting(
+ net::StreamListenSocket* socket) const {
+ ActiveSockets::const_iterator iter = std::find_if(
+ active_sockets_.begin(), active_sockets_.end(), CompareSocket(socket));
+ return iter->second;
+}
+
void GnubbyAuthHandlerPosix::DidAccept(
net::StreamListenSocket* server,
scoped_ptr<net::StreamListenSocket> socket) {
DCHECK(CalledOnValidThread());
- active_sockets_[++last_connection_id_] = socket.release();
+ int connection_id = ++last_connection_id_;
+ active_sockets_[connection_id] =
+ new GnubbySocket(socket.Pass(),
+ base::Bind(&GnubbyAuthHandlerPosix::RequestTimedOut,
+ base::Unretained(this),
+ connection_id));
}
void GnubbyAuthHandlerPosix::DidRead(net::StreamListenSocket* socket,
@@ -195,39 +225,20 @@ void GnubbyAuthHandlerPosix::DidRead(net::StreamListenSocket* socket,
int len) {
DCHECK(CalledOnValidThread());
- ActiveSockets::iterator socket_iter = std::find_if(
+ ActiveSockets::iterator iter = std::find_if(
active_sockets_.begin(), active_sockets_.end(), CompareSocket(socket));
- if (socket_iter != active_sockets_.end()) {
- int connection_id = socket_iter->first;
-
- ActiveRequests::iterator request_iter =
- active_requests_.find(connection_id);
- if (request_iter != active_requests_.end()) {
- std::vector<char>& saved_vector = request_iter->second;
- if (IsRequestTooLarge(
- saved_vector.data(), saved_vector.size(), kMaxRequestLength)) {
- // We can't close a StreamListenSocket; throw away everything but the
- // size bytes.
- saved_vector.resize(4);
- return;
- }
- saved_vector.insert(saved_vector.end(), data, data + len);
-
- if (IsRequestComplete(saved_vector.data(), saved_vector.size())) {
- ProcessGnubbyRequest(
- connection_id, saved_vector.data(), saved_vector.size());
- active_requests_.erase(request_iter);
- }
- } else if (IsRequestComplete(data, len)) {
- ProcessGnubbyRequest(connection_id, data, len);
- } else {
- if (IsRequestTooLarge(data, len, kMaxRequestLength)) {
- // Only save the size bytes.
- active_requests_[connection_id] = std::vector<char>(data, data + 4);
- } else {
- active_requests_[connection_id] = std::vector<char>(data, data + len);
- }
+ if (iter != active_sockets_.end()) {
+ GnubbySocket* gnubby_socket = iter->second;
+ gnubby_socket->AddRequestData(data, len);
+ if (gnubby_socket->IsRequestTooLarge()) {
+ SendErrorAndCloseActiveSocket(iter);
+ } else if (gnubby_socket->IsRequestComplete()) {
+ std::string request_data;
+ gnubby_socket->GetAndClearRequestData(&request_data);
+ ProcessGnubbyRequest(iter->first, request_data);
}
+ } else {
+ LOG(ERROR) << "Received data for unknown connection";
}
}
@@ -237,8 +248,6 @@ void GnubbyAuthHandlerPosix::DidClose(net::StreamListenSocket* socket) {
ActiveSockets::iterator iter = std::find_if(
active_sockets_.begin(), active_sockets_.end(), CompareSocket(socket));
if (iter != active_sockets_.end()) {
- active_requests_.erase(iter->first);
-
delete iter->second;
active_sockets_.erase(iter);
}
@@ -264,16 +273,35 @@ void GnubbyAuthHandlerPosix::CreateAuthorizationSocket() {
}
}
-void GnubbyAuthHandlerPosix::ProcessGnubbyRequest(int connection_id,
- const char* data,
- int data_len) {
- std::string json;
- if (GetJsonFromGnubbyRequest(data, data_len, &json)) {
- HOST_LOG << "Received gnubby request";
- DeliverHostDataMessage(connection_id, json);
- } else {
- LOG(ERROR) << "Could not decode gnubby request";
+void GnubbyAuthHandlerPosix::ProcessGnubbyRequest(
+ int connection_id,
+ const std::string& request_data) {
+ HOST_LOG << "Received gnubby request: " << GetCommandCode(request_data);
+ DeliverHostDataMessage(connection_id, request_data);
+}
+
+GnubbyAuthHandlerPosix::ActiveSockets::iterator
+GnubbyAuthHandlerPosix::GetSocketForMessage(base::DictionaryValue* message) {
+ int connection_id;
+ if (message->GetInteger(kConnectionId, &connection_id)) {
+ return active_sockets_.find(connection_id);
}
+ return active_sockets_.end();
+}
+
+void GnubbyAuthHandlerPosix::SendErrorAndCloseActiveSocket(
+ const ActiveSockets::iterator& iter) {
+ iter->second->SendSshError();
+
+ delete iter->second;
+ active_sockets_.erase(iter);
+}
+
+void GnubbyAuthHandlerPosix::RequestTimedOut(int connection_id) {
+ HOST_LOG << "Gnubby request timed out";
+ ActiveSockets::iterator iter = active_sockets_.find(connection_id);
+ if (iter != active_sockets_.end())
+ SendErrorAndCloseActiveSocket(iter);
}
} // namespace remoting
diff --git a/remoting/host/gnubby_auth_handler_posix.h b/remoting/host/gnubby_auth_handler_posix.h
index ea0e3b61..db38886 100644
--- a/remoting/host/gnubby_auth_handler_posix.h
+++ b/remoting/host/gnubby_auth_handler_posix.h
@@ -7,19 +7,24 @@
#include <map>
#include <string>
-#include <vector>
#include "base/memory/scoped_ptr.h"
#include "base/threading/non_thread_safe.h"
#include "net/socket/stream_listen_socket.h"
#include "remoting/host/gnubby_auth_handler.h"
+namespace base {
+class DictionaryValue;
+} // namespace base
+
namespace remoting {
namespace protocol {
class ClientStub;
} // namespace protocol
+class GnubbySocket;
+
class GnubbyAuthHandlerPosix : public GnubbyAuthHandler,
public base::NonThreadSafe,
public net::StreamListenSocket::Delegate {
@@ -28,8 +33,13 @@ class GnubbyAuthHandlerPosix : public GnubbyAuthHandler,
virtual ~GnubbyAuthHandlerPosix();
bool HasActiveSocketForTesting(net::StreamListenSocket* socket) const;
+ int GetConnectionIdForTesting(net::StreamListenSocket* socket) const;
+ GnubbySocket* GetGnubbySocketForTesting(
+ net::StreamListenSocket* socket) const;
private:
+ typedef std::map<int, GnubbySocket*> ActiveSockets;
+
// GnubbyAuthHandler interface.
virtual void DeliverClientMessage(const std::string& message) OVERRIDE;
virtual void DeliverHostDataMessage(int connection_id,
@@ -47,7 +57,16 @@ class GnubbyAuthHandlerPosix : public GnubbyAuthHandler,
void CreateAuthorizationSocket();
// Process a gnubby request.
- void ProcessGnubbyRequest(int connection_id, const char* data, int data_len);
+ 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);
+
+ // Send an error and close an active socket.
+ void SendErrorAndCloseActiveSocket(const ActiveSockets::iterator& iter);
+
+ // A request timed out.
+ void RequestTimedOut(int connection_id);
// Interface through which communication with the client occurs.
protocol::ClientStub* client_stub_;
@@ -59,13 +78,8 @@ class GnubbyAuthHandlerPosix : public GnubbyAuthHandler,
int last_connection_id_;
// Sockets by connection id used to process gnubbyd requests.
- typedef std::map<int, net::StreamListenSocket*> ActiveSockets;
ActiveSockets active_sockets_;
- // Partial gnubbyd request data by connection id.
- typedef std::map<int, std::vector<char> > ActiveRequests;
- ActiveRequests active_requests_;
-
DISALLOW_COPY_AND_ASSIGN(GnubbyAuthHandlerPosix);
};
diff --git a/remoting/host/gnubby_auth_handler_posix_unittest.cc b/remoting/host/gnubby_auth_handler_posix_unittest.cc
index c3a8c9f..1c9bd86 100644
--- a/remoting/host/gnubby_auth_handler_posix_unittest.cc
+++ b/remoting/host/gnubby_auth_handler_posix_unittest.cc
@@ -2,9 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/message_loop/message_loop.h"
#include "base/strings/stringprintf.h"
+#include "base/timer/mock_timer.h"
#include "net/socket/stream_listen_socket.h"
#include "remoting/host/gnubby_auth_handler_posix.h"
+#include "remoting/host/gnubby_socket.h"
#include "remoting/protocol/protocol_mock_objects.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -66,6 +69,8 @@ class GnubbyAuthHandlerPosixTest : public testing::Test {
// Mock client stub.
MockClientStub client_stub_;
+ base::MessageLoop message_loop_;
+
private:
void OnConnect(int result);
};
@@ -78,17 +83,19 @@ void GnubbyAuthHandlerPosixTest::SetUp() {
MATCHER_P2(EqualsDataMessage, id, data, "") {
std::string connection_id = base::StringPrintf("\"connectionId\":%d", id);
- std::string json_message = base::StringPrintf("\"jsonMessage\":\"%s\"", data);
+ std::string data_message = base::StringPrintf("\"data\":%s", data);
return (arg.type() == "gnubby-auth" &&
arg.data().find("\"type\":\"data\"") != std::string::npos &&
arg.data().find(connection_id) != std::string::npos &&
- arg.data().find(json_message) != std::string::npos);
+ arg.data().find(data_message) != std::string::npos);
}
TEST_F(GnubbyAuthHandlerPosixTest, HostDataMessageDelivered) {
+ // Expects a JSON array of the ASCII character codes for "test_msg".
EXPECT_CALL(client_stub_,
- DeliverHostMessage(EqualsDataMessage(42, "test_msg")));
+ DeliverHostMessage(
+ EqualsDataMessage(42, "[116,101,115,116,95,109,115,103]")));
auth_handler_->DeliverHostDataMessage(42, "test_msg");
}
@@ -109,7 +116,8 @@ TEST_F(GnubbyAuthHandlerPosixTest, DidRead) {
net::StreamListenSocket* socket = new MockStreamListenSocket(delegate_);
delegate_->DidAccept(NULL, make_scoped_ptr(socket));
- delegate_->DidRead(socket, reinterpret_cast<const char*>(request_data),
+ delegate_->DidRead(socket,
+ reinterpret_cast<const char*>(request_data),
sizeof(request_data));
}
@@ -120,9 +128,38 @@ TEST_F(GnubbyAuthHandlerPosixTest, DidReadByteByByte) {
delegate_->DidAccept(NULL, make_scoped_ptr(socket));
for (unsigned int i = 0; i < sizeof(request_data); ++i) {
- delegate_->DidRead(socket,
- reinterpret_cast<const char *>(request_data + i), 1);
+ delegate_->DidRead(
+ socket, reinterpret_cast<const char*>(request_data + i), 1);
}
}
+TEST_F(GnubbyAuthHandlerPosixTest, DidReadTimeout) {
+ net::StreamListenSocket* socket = new MockStreamListenSocket(delegate_);
+
+ delegate_->DidAccept(NULL, make_scoped_ptr(socket));
+ ASSERT_TRUE(auth_handler_posix_->HasActiveSocketForTesting(socket));
+
+ base::MockTimer* mock_timer = new base::MockTimer(false, false);
+ auth_handler_posix_->GetGnubbySocketForTesting(socket)
+ ->SetTimerForTesting(scoped_ptr<base::Timer>(mock_timer));
+ delegate_->DidRead(socket, reinterpret_cast<const char*>(request_data), 1);
+ mock_timer->Fire();
+
+ ASSERT_FALSE(auth_handler_posix_->HasActiveSocketForTesting(socket));
+}
+
+TEST_F(GnubbyAuthHandlerPosixTest, ClientErrorMessageDelivered) {
+ net::StreamListenSocket* socket = new MockStreamListenSocket(delegate_);
+
+ delegate_->DidAccept(NULL, make_scoped_ptr(socket));
+
+ std::string error_json = base::StringPrintf(
+ "{\"type\":\"error\",\"connectionId\":%d}",
+ auth_handler_posix_->GetConnectionIdForTesting(socket));
+
+ ASSERT_TRUE(auth_handler_posix_->HasActiveSocketForTesting(socket));
+ auth_handler_->DeliverClientMessage(error_json);
+ ASSERT_FALSE(auth_handler_posix_->HasActiveSocketForTesting(socket));
+}
+
} // namespace remoting
diff --git a/remoting/host/gnubby_socket.cc b/remoting/host/gnubby_socket.cc
new file mode 100644
index 0000000..09d5d30
--- /dev/null
+++ b/remoting/host/gnubby_socket.cc
@@ -0,0 +1,116 @@
+// Copyright 2014 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/gnubby_socket.h"
+
+#include "base/timer/timer.h"
+#include "net/socket/stream_listen_socket.h"
+
+namespace remoting {
+
+namespace {
+
+const size_t kRequestSizeBytes = 4;
+const size_t kMaxRequestLength = 16384;
+const unsigned int kRequestTimeoutSeconds = 60;
+
+// SSH Failure Code
+const char kSshError[] = {0x05};
+
+} // namespace
+
+GnubbySocket::GnubbySocket(scoped_ptr<net::StreamListenSocket> socket,
+ const base::Closure& timeout_callback)
+ : socket_(socket.Pass()) {
+ timer_.reset(new base::Timer(false, false));
+ timer_->Start(FROM_HERE,
+ base::TimeDelta::FromSeconds(kRequestTimeoutSeconds),
+ timeout_callback);
+}
+
+GnubbySocket::~GnubbySocket() {}
+
+void GnubbySocket::AddRequestData(const char* data, int data_len) {
+ DCHECK(CalledOnValidThread());
+
+ request_data_.insert(request_data_.end(), data, data + data_len);
+ ResetTimer();
+}
+
+void GnubbySocket::GetAndClearRequestData(std::string* data_out) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(IsRequestComplete() && !IsRequestTooLarge());
+
+ // The request size is not part of the data; don't send it.
+ data_out->assign(request_data_.begin() + kRequestSizeBytes,
+ request_data_.end());
+ request_data_.clear();
+}
+
+bool GnubbySocket::IsRequestComplete() const {
+ DCHECK(CalledOnValidThread());
+
+ if (request_data_.size() < kRequestSizeBytes)
+ return false;
+ return GetRequestLength() <= request_data_.size();
+}
+
+bool GnubbySocket::IsRequestTooLarge() const {
+ DCHECK(CalledOnValidThread());
+
+ if (request_data_.size() < kRequestSizeBytes)
+ return false;
+ return GetRequestLength() > kMaxRequestLength;
+}
+
+void GnubbySocket::SendResponse(const std::string& response_data) {
+ DCHECK(CalledOnValidThread());
+
+ socket_->Send(GetResponseLengthAsBytes(response_data));
+ socket_->Send(response_data);
+ ResetTimer();
+}
+
+void GnubbySocket::SendSshError() {
+ DCHECK(CalledOnValidThread());
+
+ SendResponse(kSshError);
+}
+
+bool GnubbySocket::IsSocket(net::StreamListenSocket* socket) const {
+ return socket == socket_.get();
+}
+
+void GnubbySocket::SetTimerForTesting(scoped_ptr<base::Timer> timer) {
+ timer->Start(FROM_HERE, timer_->GetCurrentDelay(), timer_->user_task());
+ timer_ = timer.Pass();
+}
+
+size_t GnubbySocket::GetRequestLength() const {
+ DCHECK(request_data_.size() >= kRequestSizeBytes);
+
+ return ((request_data_[0] & 255) << 24) + ((request_data_[1] & 255) << 16) +
+ ((request_data_[2] & 255) << 8) + (request_data_[3] & 255) +
+ kRequestSizeBytes;
+}
+
+std::string GnubbySocket::GetResponseLengthAsBytes(
+ const std::string& response) const {
+ std::string response_len;
+ int len = response.size();
+
+ response_len.push_back((len >> 24) & 255);
+ response_len.push_back((len >> 16) & 255);
+ response_len.push_back((len >> 8) & 255);
+ response_len.push_back(len & 255);
+
+ return response_len;
+}
+
+void GnubbySocket::ResetTimer() {
+ if (timer_->IsRunning())
+ timer_->Reset();
+}
+
+} // namespace remoting
diff --git a/remoting/host/gnubby_socket.h b/remoting/host/gnubby_socket.h
new file mode 100644
index 0000000..d119762
--- /dev/null
+++ b/remoting/host/gnubby_socket.h
@@ -0,0 +1,80 @@
+// Copyright 2014 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_GNUBBY_SOCKET_H_
+#define REMOTING_HOST_GNUBBY_SOCKET_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/non_thread_safe.h"
+
+namespace base {
+class Timer;
+} // namespace base
+
+namespace net {
+class StreamListenSocket;
+} // namespace net
+
+namespace remoting {
+
+// Class that manages a socket used for gnubby requests.
+class GnubbySocket : public base::NonThreadSafe {
+ public:
+ GnubbySocket(scoped_ptr<net::StreamListenSocket> socket,
+ const base::Closure& timeout_callback);
+ ~GnubbySocket();
+
+ // Adds data to the current request.
+ void AddRequestData(const char* data, int data_len);
+
+ // Gets the current request data and clears it.
+ void GetAndClearRequestData(std::string* data_out);
+
+ // Returns true if the current request is complete.
+ bool IsRequestComplete() const;
+
+ // Returns true if the stated request size is larger than the allowed maximum.
+ bool IsRequestTooLarge() const;
+
+ // Sends response data to the socket.
+ void SendResponse(const std::string& data);
+
+ // Sends an SSH error code to the socket.
+ void SendSshError();
+
+ // Returns true if |socket| is the same one owned by this object.
+ bool IsSocket(net::StreamListenSocket* socket) const;
+
+ // Sets a timer for testing.
+ void SetTimerForTesting(scoped_ptr<base::Timer> timer);
+
+ private:
+ // Returns the stated request length.
+ size_t GetRequestLength() const;
+
+ // Returns the response length bytes.
+ std::string GetResponseLengthAsBytes(const std::string& response) const;
+
+ // Resets the socket activity timer.
+ void ResetTimer();
+
+ // The socket.
+ scoped_ptr<net::StreamListenSocket> socket_;
+
+ // Request data.
+ std::vector<char> request_data_;
+
+ // The activity timer.
+ scoped_ptr<base::Timer> timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(GnubbySocket);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_GNUBBY_SOCKET_H_
diff --git a/remoting/host/gnubby_util.cc b/remoting/host/gnubby_util.cc
deleted file mode 100644
index 126584b..0000000
--- a/remoting/host/gnubby_util.cc
+++ /dev/null
@@ -1,347 +0,0 @@
-// Copyright 2014 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/gnubby_util.h"
-
-#include <algorithm>
-#include <vector>
-
-#include "base/base64.h"
-#include "base/json/json_reader.h"
-#include "base/json/json_writer.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/values.h"
-#include "remoting/base/logging.h"
-
-namespace remoting {
-
-namespace {
-
-// Failure code to use when the code from the webapp response isn't available.
-const int kGnubbyResponseFail = 1;
-
-const int kSsh2AgentcGnubbySignRequest = 101; // 0x65
-const int kSsh2AgentcGnubbySignResponse = 102; // 0x66
-
-const char kAppIdHash[] = "appIdHash";
-const char kChallengeHash[] = "challengeHash";
-const char kCode[] = "code";
-const char kKeyHandle[] = "keyHandle";
-const char kResponseData[] = "responseData";
-const char kSignData[] = "signData";
-const char kSignReply[] = "sign_helper_reply";
-const char kSignRequest[] = "sign_helper_request";
-const char kSignatureData[] = "signatureData";
-const char kTimeout[] = "timeout";
-const char kType[] = "type";
-const char kVersion[] = "version";
-
-void WebSafeBase64Encode(const std::string& data, std::string* encoded_data) {
- base::Base64Encode(data, encoded_data);
-
- std::replace(encoded_data->begin(), encoded_data->end(), '+', '-');
- std::replace(encoded_data->begin(), encoded_data->end(), '/', '_');
- encoded_data->erase(
- std::remove(encoded_data->begin(), encoded_data->end(), '='),
- encoded_data->end());
-}
-
-void WebSafeBase64Decode(const std::string& encoded_data, std::string* data) {
- std::string temp(encoded_data);
- std::replace(temp.begin(), temp.end(), '-', '+');
- std::replace(temp.begin(), temp.end(), '_', '/');
-
- int num_equals = temp.length() % 3;
- temp.append(num_equals, '=');
-
- base::Base64Decode(temp, data);
-}
-
-bool DecodeDataFromDictionary(const base::DictionaryValue& dictionary,
- const std::string& path,
- std::string* data) {
- std::string encoded_data;
- bool result = dictionary.GetString(path, &encoded_data);
- if (result) {
- WebSafeBase64Decode(encoded_data, data);
- } else {
- LOG(ERROR) << "Failed to get dictionary value " << path;
- data->erase();
- }
- return result;
-}
-
-// Class to read gnubby blob data.
-class BlobReader {
- public:
- // Create a blob with the given data. Does not take ownership of the memory.
- BlobReader(const uint8_t* data, size_t data_len);
- virtual ~BlobReader();
-
- // Read a byte from the blob. Returns true on success.
- bool ReadByte(uint8_t* value);
-
- // Read a four byte size from the blob. Returns true on success.
- bool ReadSize(size_t* value);
-
- // Read a size-prefixed blob. Returns true on success.
- bool ReadBlobReader(scoped_ptr<BlobReader>* value);
-
- // Read a size-prefixed string from the blob. Returns true on success.
- bool ReadString(std::string* value);
-
- private:
- // The blob data.
- const uint8_t* data_;
-
- // The length of the blob data.
- size_t data_len_;
-
- // The current read index.
- size_t index_;
-
- DISALLOW_COPY_AND_ASSIGN(BlobReader);
-};
-
-// Class to write gnubby blob data.
-class BlobWriter {
- public:
- BlobWriter();
- virtual ~BlobWriter();
-
- // Write a byte to the blob.
- void WriteByte(uint8_t value);
-
- // Write a four byte size to the blob.
- void WriteSize(size_t value);
-
- // Write a size-prefixed blob to the blob.
- void WriteBlobWriter(const BlobWriter& value);
-
- // Write a size-prefixed string to the blob.
- void WriteString(const std::string& value);
-
- // Returns the blob data.
- std::string GetData() const;
-
- private:
- // The blob data.
- std::vector<uint8_t> data_;
-
- DISALLOW_COPY_AND_ASSIGN(BlobWriter);
-};
-
-BlobReader::BlobReader(const uint8_t* data, size_t data_len)
- : data_(data), data_len_(data_len), index_(0) {}
-
-BlobReader::~BlobReader() {}
-
-bool BlobReader::ReadByte(uint8_t* value) {
- if (data_len_ < index_) {
- *value = 0;
- return false;
- }
- *value = data_[index_++];
- return true;
-}
-
-bool BlobReader::ReadSize(size_t* value) {
- if (data_len_ < (index_ + 4)) {
- *value = 0;
- return false;
- }
- *value = ((data_[index_] & 255) << 24) + ((data_[index_ + 1] & 255) << 16) +
- ((data_[index_ + 2] & 255) << 8) + (data_[index_ + 3] & 255);
- index_ += 4;
- return true;
-}
-
-bool BlobReader::ReadBlobReader(scoped_ptr<BlobReader>* value) {
- size_t blob_size;
- if (!ReadSize(&blob_size) || data_len_ < (index_ + blob_size)) {
- value->reset();
- return 0;
- }
- value->reset(new BlobReader(data_ + index_, blob_size));
- index_ += blob_size;
- return true;
-}
-
-bool BlobReader::ReadString(std::string* value) {
- size_t length;
- if (!ReadSize(&length) || data_len_ < (index_ + length)) {
- value->erase();
- return 0;
- }
- value->assign(reinterpret_cast<const char*>(data_ + index_), length);
- index_ += length;
- return true;
-}
-
-BlobWriter::BlobWriter() {}
-
-BlobWriter::~BlobWriter() {}
-
-void BlobWriter::WriteByte(uint8_t value) { data_.push_back(value); }
-
-void BlobWriter::WriteSize(size_t value) {
- data_.push_back((value & 0xff000000) >> 24);
- data_.push_back((value & 0xff0000) >> 16);
- data_.push_back((value & 0xff00) >> 8);
- data_.push_back(value & 0xff);
-}
-
-void BlobWriter::WriteBlobWriter(const BlobWriter& value) {
- WriteString(value.GetData());
-}
-
-void BlobWriter::WriteString(const std::string& value) {
- WriteSize(value.length());
- data_.insert(data_.end(), value.begin(), value.end());
-}
-
-std::string BlobWriter::GetData() const {
- return std::string(reinterpret_cast<const char*>(data_.data()), data_.size());
-}
-
-} // namespace
-
-bool GetJsonFromGnubbyRequest(const char* data,
- int data_len,
- std::string* json) {
- json->empty();
-
- BlobReader ssh_request(reinterpret_cast<const uint8_t*>(data), data_len);
- scoped_ptr<BlobReader> blob;
- if (!ssh_request.ReadBlobReader(&blob))
- return false;
-
- uint8_t cmd = 0;
- uint8_t timeout = 0;
- size_t request_count = 0;
- bool result = blob->ReadByte(&cmd);
- result = result && blob->ReadByte(&timeout);
- result = result && blob->ReadSize(&request_count);
- if (!result || cmd != kSsh2AgentcGnubbySignRequest)
- return false;
-
- base::DictionaryValue request;
- request.SetString(kType, kSignRequest);
- request.SetInteger(kTimeout, timeout);
-
- base::ListValue* sign_requests = new base::ListValue();
- request.Set(kSignData, sign_requests);
-
- for (unsigned int i = 0; i < request_count; ++i) {
- scoped_ptr<BlobReader> sign_request;
- std::string version;
- std::string challenge_hash;
- std::string origin_hash;
- std::string key_handle;
-
- if (!(blob->ReadBlobReader(&sign_request) &&
- sign_request->ReadString(&version) &&
- sign_request->ReadString(&challenge_hash) &&
- sign_request->ReadString(&origin_hash) &&
- sign_request->ReadString(&key_handle)))
- return false;
-
- std::string encoded_origin_hash;
- std::string encoded_challenge_hash;
- std::string encoded_key_handle;
-
- WebSafeBase64Encode(origin_hash, &encoded_origin_hash);
- WebSafeBase64Encode(challenge_hash, &encoded_challenge_hash);
- WebSafeBase64Encode(key_handle, &encoded_key_handle);
-
- base::DictionaryValue* request = new base::DictionaryValue();
- request->SetString(kAppIdHash, encoded_origin_hash);
- request->SetString(kChallengeHash, encoded_challenge_hash);
- request->SetString(kKeyHandle, encoded_key_handle);
- request->SetString(kVersion, version);
- sign_requests->Append(request);
- }
-
- base::JSONWriter::Write(&request, json);
- return true;
-}
-
-void GetGnubbyResponseFromJson(const std::string& json, std::string* data) {
- data->erase();
-
- scoped_ptr<base::Value> json_value(base::JSONReader::Read(json));
- base::DictionaryValue* reply;
- if (json_value && json_value->GetAsDictionary(&reply)) {
- BlobWriter response;
- response.WriteByte(kSsh2AgentcGnubbySignResponse);
-
- int code;
- if (reply->GetInteger(kCode, &code) && code == 0) {
- response.WriteSize(code);
-
- std::string type;
- if (!(reply->GetString(kType, &type) && type == kSignReply)) {
- LOG(ERROR) << "Invalid type";
- return;
- }
-
- base::DictionaryValue* reply_data;
- if (!reply->GetDictionary(kResponseData, &reply_data)) {
- LOG(ERROR) << "Invalid response data";
- return;
- }
-
- BlobWriter tmp;
- std::string version;
- if (reply_data->GetString(kVersion, &version)) {
- tmp.WriteString(version);
- } else {
- tmp.WriteSize(0);
- }
-
- std::string challenge_hash;
- if (!DecodeDataFromDictionary(
- *reply_data, kChallengeHash, &challenge_hash)) {
- LOG(ERROR) << "Invalid challenge hash";
- return;
- }
- tmp.WriteString(challenge_hash);
-
- std::string app_id_hash;
- if (!DecodeDataFromDictionary(*reply_data, kAppIdHash, &app_id_hash)) {
- LOG(ERROR) << "Invalid app id hash";
- return;
- }
- tmp.WriteString(app_id_hash);
-
- std::string key_handle;
- if (!DecodeDataFromDictionary(*reply_data, kKeyHandle, &key_handle)) {
- LOG(ERROR) << "Invalid key handle";
- return;
- }
- tmp.WriteString(key_handle);
-
- std::string signature_data;
- if (!DecodeDataFromDictionary(
- *reply_data, kSignatureData, &signature_data)) {
- LOG(ERROR) << "Invalid signature data";
- return;
- }
- tmp.WriteString(signature_data);
-
- response.WriteBlobWriter(tmp);
- } else {
- response.WriteSize(kGnubbyResponseFail);
- }
-
- BlobWriter ssh_response;
- ssh_response.WriteBlobWriter(response);
- data->assign(ssh_response.GetData());
- } else {
- LOG(ERROR) << "Could not parse json: " << json;
- }
-}
-
-} // namespace remoting
diff --git a/remoting/host/gnubby_util.h b/remoting/host/gnubby_util.h
deleted file mode 100644
index 0d888d6..0000000
--- a/remoting/host/gnubby_util.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2014 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_GNUBBY_UTIL_H_
-#define REMOTING_HOST_GNUBBY_UTIL_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-
-namespace base {
-
-class DictionaryValue;
-
-} // namespace base
-
-namespace remoting {
-
-// Get a gnubbyd sign request json string from a request blob.
-bool GetJsonFromGnubbyRequest(const char* data,
- int data_len,
- std::string* json);
-
-// Get response blob data from a gnubbyd sign reply json string.
-void GetGnubbyResponseFromJson(const std::string& json, std::string* data);
-
-} // namespace remoting
-
-#endif // REMOTING_HOST_GNUBBY_UTIL_H_
diff --git a/remoting/host/gnubby_util_unittest.cc b/remoting/host/gnubby_util_unittest.cc
deleted file mode 100644
index e788788..0000000
--- a/remoting/host/gnubby_util_unittest.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2014 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/gnubby_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace remoting {
-
-namespace {
-
-// Test gnubby request data.
-const unsigned char request_data[] = {
- 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};
-
-// Expected json for gnubby request data.
-const char expected_json[] =
- "{\"signData\":[{\"appIdHash\":\"ZEZHL99u7Xvzwzcg8jZnbDbhtF6-BIXbiaPN_dJL1p"
- "8\",\"challengeHash\":\"YJAkcfjy5d9_gcdJxKNYXPbMQBQoDKD6Axg42H13Kzo\",\"ke"
- "yHandle\":\"ODUFdR0TbrNrHSmu00PmhI-jnWVOL1fj9uYgPADG4XM04iOZxPqRwtWXwYvQPB"
- "O68Ndeo7wCW-zkS64O8r3Iqg\",\"version\":\"\"}],\"timeout\":30,\"type\":\"si"
- "gn_helper_request\"}";
-
-// Test json response.
-const char response_json[] =
- "{\"type\":\"sign_helper_reply\",\"code\":0,\"responseData\":{\"appIdHash\""
- ":\"ZEZHL99u7Xvzwzcg8jZnbDbhtF6-BIXbiaPN_dJL1p8\",\"challengeHash\":\"YJAkc"
- "fjy5d9_gcdJxKNYXPbMQBQoDKD6Axg42H13Kzo\",\"keyHandle\":\"ODUFdR0TbrNrHSmu0"
- "0PmhI-jnWVOL1fj9uYgPADG4XM04iOZxPqRwtWXwYvQPBO68Ndeo7wCW-zkS64O8r3Iqg\",\""
- "signatureData\":\"AeW_wWGxdN0LGtX_E7s5CsMAH95-my58SN1HW8qrCygF257EWRzbsOpO"
- "mTtM9N71lGd3B3hTVPASkcpQZzhQcQNIObY_vjO-aliOHb937DWU\"}}";
-
-// Expected data for json response.
-const unsigned char expected_data[] = {
- 0x00, 0x00, 0x00, 0xee, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xe5, 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, 0x00, 0x00, 0x00, 0x51, 0x01, 0xe5, 0xbf, 0xc1, 0x61, 0xb1, 0x74,
- 0xdd, 0x0b, 0x1a, 0xd5, 0xff, 0x13, 0xbb, 0x39, 0x0a, 0xc3, 0x00, 0x1f,
- 0xde, 0x7e, 0x9b, 0x2e, 0x7c, 0x48, 0xdd, 0x47, 0x5b, 0xca, 0xab, 0x0b,
- 0x28, 0x05, 0xdb, 0x9e, 0xc4, 0x59, 0x1c, 0xdb, 0xb0, 0xea, 0x4e, 0x99,
- 0x3b, 0x4c, 0xf4, 0xde, 0xf5, 0x94, 0x67, 0x77, 0x07, 0x78, 0x53, 0x54,
- 0xf0, 0x12, 0x91, 0xca, 0x50, 0x67, 0x38, 0x50, 0x71, 0x03, 0x48, 0x39,
- 0xb6, 0x3f, 0xbe, 0x33, 0xbe, 0x6a, 0x58, 0x8e, 0x1d, 0xbf, 0x77, 0xec,
- 0x35, 0x94};
-
-} // namespace
-
-class GnubbyUtilsTest : public testing::Test {
- public:
- GnubbyUtilsTest() {}
-};
-
-TEST_F(GnubbyUtilsTest, DataToJson) {
- std::string actual_json;
- GetJsonFromGnubbyRequest(reinterpret_cast<const char*>(request_data),
- sizeof(request_data),
- &actual_json);
-
- EXPECT_EQ(expected_json, actual_json);
-}
-
-TEST_F(GnubbyUtilsTest, JsonToData) {
- std::string actual_data;
- GetGnubbyResponseFromJson(response_json, &actual_data);
-
- EXPECT_EQ(std::string(reinterpret_cast<const char*>(expected_data),
- sizeof(expected_data)),
- actual_data);
-}
-
-} // namespace remoting