From f66432db0ee32950f0dd04ce8bf3895daae7370e Mon Sep 17 00:00:00 2001 From: "psj@chromium.org" Date: Mon, 24 Mar 2014 02:33:35 +0000 Subject: 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 --- remoting/host/gnubby_auth_handler_posix.cc | 198 +++++++----- remoting/host/gnubby_auth_handler_posix.h | 28 +- .../host/gnubby_auth_handler_posix_unittest.cc | 49 ++- remoting/host/gnubby_socket.cc | 116 +++++++ remoting/host/gnubby_socket.h | 80 +++++ remoting/host/gnubby_util.cc | 347 --------------------- remoting/host/gnubby_util.h | 30 -- remoting/host/gnubby_util_unittest.cc | 95 ------ 8 files changed, 373 insertions(+), 570 deletions(-) create mode 100644 remoting/host/gnubby_socket.cc create mode 100644 remoting/host/gnubby_socket.h delete mode 100644 remoting/host/gnubby_util.cc delete mode 100644 remoting/host/gnubby_util.h delete mode 100644 remoting/host/gnubby_util_unittest.cc (limited to 'remoting/host') 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 #include -#include #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 element) - const { - return socket_ == element.second; + bool operator()(const std::pair 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(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(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(*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 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& 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(data, data + 4); - } else { - active_requests_[connection_id] = std::vector(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 #include -#include #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 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 ActiveSockets; ActiveSockets active_sockets_; - // Partial gnubbyd request data by connection id. - typedef std::map > 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(request_data), + delegate_->DidRead(socket, + reinterpret_cast(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(request_data + i), 1); + delegate_->DidRead( + socket, reinterpret_cast(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(mock_timer)); + delegate_->DidRead(socket, reinterpret_cast(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 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 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 +#include + +#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 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 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 socket_; + + // Request data. + std::vector request_data_; + + // The activity timer. + scoped_ptr 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 -#include - -#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* 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 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* 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(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(data_.data()), data_.size()); -} - -} // namespace - -bool GetJsonFromGnubbyRequest(const char* data, - int data_len, - std::string* json) { - json->empty(); - - BlobReader ssh_request(reinterpret_cast(data), data_len); - scoped_ptr 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 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 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 - -#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(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(expected_data), - sizeof(expected_data)), - actual_data); -} - -} // namespace remoting -- cgit v1.1