diff options
33 files changed, 1347 insertions, 5 deletions
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc index 506756a..cf778749 100644 --- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc +++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc @@ -267,6 +267,9 @@ const PolicyToPreferenceMapEntry kSimplePolicyMap[] = { { key::kRemoteAccessHostAllowClientPairing, prefs::kRemoteAccessHostAllowClientPairing, base::Value::TYPE_BOOLEAN }, + { key::kRemoteAccessHostAllowGnubbyAuth, + prefs::kRemoteAccessHostAllowGnubbyAuth, + base::Value::TYPE_BOOLEAN }, { key::kCloudPrintProxyEnabled, prefs::kCloudPrintProxyEnabled, base::Value::TYPE_BOOLEAN }, diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index ddb145a..685ba07 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -1993,6 +1993,10 @@ const char kRemoteAccessHostRequireCurtain[] = const char kRemoteAccessHostAllowClientPairing[] = "remote_access.host_allow_client_pairing"; +// Whether Chrome Remote Desktop can proxy gnubby authentication traffic. +const char kRemoteAccessHostAllowGnubbyAuth[] = + "remote_access.host_allow_gnubby_auth"; + // The last used printer and its settings. const char kPrintPreviewStickySettings[] = "printing.print_preview_sticky_settings"; diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index e38a6d3..1577741 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -680,6 +680,7 @@ extern const char kRemoteAccessHostDomain[]; extern const char kRemoteAccessHostTalkGadgetPrefix[]; extern const char kRemoteAccessHostRequireCurtain[]; extern const char kRemoteAccessHostAllowClientPairing[]; +extern const char kRemoteAccessHostAllowGnubbyAuth[]; extern const char kPrintPreviewStickySettings[]; extern const char kCloudPrintRoot[]; diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json index d21cdae..df4ea62 100644 --- a/chrome/test/data/policy/policy_test_cases.json +++ b/chrome/test/data/policy/policy_test_cases.json @@ -233,6 +233,14 @@ ] }, + "RemoteAccessHostAllowGnubbyAuth": { + "os": [], + "test_policy": { "RemoteAccessHostAllowGnubbyAuth": true }, + "pref_mappings": [ + { "pref": "remote_access.host_allow_gnubby_auth" } + ] + }, + "PrintingEnabled": { "os": ["win", "linux", "mac", "chromeos"], "test_policy": { "PrintingEnabled": false }, diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json index c038b55..efb02fe 100644 --- a/components/policy/resources/policy_templates.json +++ b/components/policy/resources/policy_templates.json @@ -118,7 +118,7 @@ # persistent IDs for all fields (but not for groups!) are needed. These are # specified by the 'id' keys of each policy. NEVER CHANGE EXISTING IDs, # because doing so would break the deployed wire format! -# For your editing convenience: highest ID currently used: 256 +# For your editing convenience: highest ID currently used: 257 # # Placeholders: # The following placeholder strings are automatically substituted: @@ -623,6 +623,22 @@ If this setting is disabled, then this feature will not be available.''', }, + { + 'name': 'RemoteAccessHostAllowGnubbyAuth', + 'type': 'main', + 'schema': { 'type': 'boolean' }, + 'supported_on': ['chrome.*:35-'], + 'features': { + 'dynamic_refresh': True, + 'per_profile': False, + }, + 'example_value': True, + 'id': 257, + 'caption': '''Allow gnubby authentication''', + 'desc': '''If this setting is enabled, then gnubby authentication requests will be proxied across a remote host connection. + + If this setting is disabled or not configured, gnubby authentication requests will not be proxied.''', + }, ], }, { diff --git a/remoting/host/basic_desktop_environment.cc b/remoting/host/basic_desktop_environment.cc index 7698ca8..011bf5c 100644 --- a/remoting/host/basic_desktop_environment.cc +++ b/remoting/host/basic_desktop_environment.cc @@ -9,6 +9,7 @@ #include "base/single_thread_task_runner.h" #include "remoting/host/audio_capturer.h" #include "remoting/host/client_session_control.h" +#include "remoting/host/gnubby_auth_handler.h" #include "remoting/host/input_injector.h" #include "remoting/host/screen_controls.h" #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" @@ -44,6 +45,11 @@ std::string BasicDesktopEnvironment::GetCapabilities() const { void BasicDesktopEnvironment::SetCapabilities(const std::string& capabilities) { } +scoped_ptr<GnubbyAuthHandler> BasicDesktopEnvironment::CreateGnubbyAuthHandler( + protocol::ClientStub* client_stub) { + return scoped_ptr<GnubbyAuthHandler>(); +} + scoped_ptr<webrtc::ScreenCapturer> BasicDesktopEnvironment::CreateVideoCapturer() { DCHECK(caller_task_runner_->BelongsToCurrentThread()); diff --git a/remoting/host/basic_desktop_environment.h b/remoting/host/basic_desktop_environment.h index dbc9822..ab38c49 100644 --- a/remoting/host/basic_desktop_environment.h +++ b/remoting/host/basic_desktop_environment.h @@ -15,6 +15,8 @@ namespace remoting { +class GnubbyAuthHandler; + // Used to create audio/video capturers and event executor that work with // the local console. class BasicDesktopEnvironment : public DesktopEnvironment { @@ -28,6 +30,8 @@ class BasicDesktopEnvironment : public DesktopEnvironment { virtual scoped_ptr<webrtc::ScreenCapturer> CreateVideoCapturer() OVERRIDE; virtual std::string GetCapabilities() const OVERRIDE; virtual void SetCapabilities(const std::string& capabilities) OVERRIDE; + virtual scoped_ptr<GnubbyAuthHandler> CreateGnubbyAuthHandler( + protocol::ClientStub* client_stub) OVERRIDE; protected: friend class BasicDesktopEnvironmentFactory; diff --git a/remoting/host/client_session.cc b/remoting/host/client_session.cc index 52feba7..6057f12 100644 --- a/remoting/host/client_session.cc +++ b/remoting/host/client_session.cc @@ -197,9 +197,15 @@ void ClientSession::DeliverClientMessage( reply.set_data(message.data().substr(0, 16)); connection_->client_stub()->DeliverHostMessage(reply); return; + } else if (message.type() == "gnubby-auth") { + if (gnubby_auth_handler_) { + gnubby_auth_handler_->DeliverClientMessage(message.data()); + } else { + HOST_LOG << "gnubby auth is not enabled"; + } + return; } } - // No messages are currently supported. HOST_LOG << "Unexpected message received: " << message.type() << ": " << message.data(); } @@ -288,6 +294,10 @@ void ClientSession::OnConnectionAuthenticated( audio_encoder.Pass(), connection_->audio_stub()); } + + // Create a GnubbyAuthHandler to proxy gnubbyd messages. + gnubby_auth_handler_ = desktop_environment_->CreateGnubbyAuthHandler( + connection_->client_stub()); } void ClientSession::OnConnectionChannelsConnected( @@ -412,6 +422,11 @@ void ClientSession::SetDisableInputs(bool disable_inputs) { disable_clipboard_filter_.set_enabled(!disable_inputs); } +void ClientSession::SetGnubbyAuthHandlerForTesting( + GnubbyAuthHandler* gnubby_auth_handler) { + gnubby_auth_handler_.reset(gnubby_auth_handler); +} + scoped_ptr<protocol::ClipboardStub> ClientSession::CreateClipboardProxy() { DCHECK(CalledOnValidThread()); diff --git a/remoting/host/client_session.h b/remoting/host/client_session.h index 76e0cbe..ef75b25 100644 --- a/remoting/host/client_session.h +++ b/remoting/host/client_session.h @@ -14,6 +14,7 @@ #include "base/time/time.h" #include "base/timer/timer.h" #include "remoting/host/client_session_control.h" +#include "remoting/host/gnubby_auth_handler.h" #include "remoting/host/mouse_clamping_filter.h" #include "remoting/host/remote_input_filter.h" #include "remoting/protocol/clipboard_echo_filter.h" @@ -134,6 +135,8 @@ class ClientSession const webrtc::DesktopVector& position) OVERRIDE; virtual void SetDisableInputs(bool disable_inputs) OVERRIDE; + void SetGnubbyAuthHandlerForTesting(GnubbyAuthHandler* gnubby_auth_handler); + protocol::ConnectionToClient* connection() const { return connection_.get(); } @@ -233,6 +236,9 @@ class ClientSession // The pairing registry for PIN-less authentication. scoped_refptr<protocol::PairingRegistry> pairing_registry_; + // Used to proxy gnubby auth traffic. + scoped_ptr<GnubbyAuthHandler> gnubby_auth_handler_; + DISALLOW_COPY_AND_ASSIGN(ClientSession); }; diff --git a/remoting/host/client_session_unittest.cc b/remoting/host/client_session_unittest.cc index 7cd21ee..f28050a 100644 --- a/remoting/host/client_session_unittest.cc +++ b/remoting/host/client_session_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/message_loop/message_loop.h" +#include "base/test/test_simple_task_runner.h" #include "remoting/base/auto_thread_task_runner.h" #include "remoting/base/constants.h" #include "remoting/host/audio_capturer.h" @@ -11,6 +12,7 @@ #include "remoting/host/host_mock_objects.h" #include "remoting/host/screen_capturer_fake.h" #include "remoting/protocol/protocol_mock_objects.h" +#include "testing/gmock/include/gmock/gmock-matchers.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" #include "third_party/webrtc/modules/desktop_capture/desktop_region.h" @@ -35,6 +37,8 @@ using testing::Expectation; using testing::Return; using testing::ReturnRef; using testing::Sequence; +using testing::StrEq; +using testing::StrictMock; namespace { @@ -55,7 +59,15 @@ ACTION_P2(LocalMouseMoved, client_session, event) { webrtc::DesktopVector(event.x(), event.y())); } -} // namespace +ACTION_P2(SetGnubbyAuthHandlerForTesting, client_session, gnubby_auth_handler) { + client_session->SetGnubbyAuthHandlerForTesting(gnubby_auth_handler); +} + +ACTION_P2(DeliverClientMessage, client_session, message) { + client_session->DeliverClientMessage(message); +} + +} class ClientSessionTest : public testing::Test { public: @@ -294,7 +306,7 @@ MATCHER_P2(EqualsMouseButtonEvent, button, down, "") { return arg.button() == button && arg.button_down() == down; } -} +} // namespace TEST_F(ClientSessionTest, InputStubFilter) { protocol::KeyEvent key_event1; @@ -539,4 +551,52 @@ TEST_F(ClientSessionTest, ClampMouseEvents) { message_loop_.Run(); } +TEST_F(ClientSessionTest, NoGnubbyAuth) { + protocol::ExtensionMessage message; + message.set_type("gnubby-auth"); + message.set_data("test"); + + Expectation authenticated = + EXPECT_CALL(session_event_handler_, OnSessionAuthenticated(_)) + .WillOnce(Return(true)); + EXPECT_CALL(*input_injector_, StartPtr(_)).After(authenticated); + EXPECT_CALL(session_event_handler_, OnSessionChannelsConnected(_)) + .After(authenticated) + .WillOnce(DoAll( + DeliverClientMessage(client_session_.get(), message), + InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession), + InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession))); + EXPECT_CALL(session_event_handler_, OnSessionClosed(_)); + + ConnectClientSession(); + message_loop_.Run(); +} + +TEST_F(ClientSessionTest, EnableGnubbyAuth) { + // Lifetime controlled by object under test. + MockGnubbyAuthHandler* gnubby_auth_handler = new MockGnubbyAuthHandler(); + + protocol::ExtensionMessage message; + message.set_type("gnubby-auth"); + message.set_data("test"); + + Expectation authenticated = + EXPECT_CALL(session_event_handler_, OnSessionAuthenticated(_)) + .WillOnce(Return(true)); + EXPECT_CALL(*input_injector_, StartPtr(_)).After(authenticated); + EXPECT_CALL(session_event_handler_, OnSessionChannelsConnected(_)) + .After(authenticated) + .WillOnce(DoAll( + SetGnubbyAuthHandlerForTesting(client_session_.get(), + gnubby_auth_handler), + DeliverClientMessage(client_session_.get(), message), + InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession), + InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession))); + EXPECT_CALL(*gnubby_auth_handler, DeliverClientMessage(_)); + EXPECT_CALL(session_event_handler_, OnSessionClosed(_)); + + ConnectClientSession(); + message_loop_.Run(); +} + } // namespace remoting diff --git a/remoting/host/desktop_environment.h b/remoting/host/desktop_environment.h index 1fa9ccd..c289891 100644 --- a/remoting/host/desktop_environment.h +++ b/remoting/host/desktop_environment.h @@ -23,8 +23,13 @@ class ScreenCapturer; namespace remoting { +namespace protocol { +class ClientStub; +} // namespace protocol + class AudioCapturer; class ClientSessionControl; +class GnubbyAuthHandler; class InputInjector; class ScreenControls; @@ -47,6 +52,11 @@ class DesktopEnvironment { // Passes the final set of capabilities negotiated between the client and host // to |this|. virtual void SetCapabilities(const std::string& capabilities) = 0; + + // Factory method to create a gnubby auth handler suitable for the particular + // desktop environment. + virtual scoped_ptr<GnubbyAuthHandler> CreateGnubbyAuthHandler( + protocol::ClientStub* client_stub) = 0; }; // Used to create |DesktopEnvironment| instances. @@ -67,6 +77,9 @@ class DesktopEnvironmentFactory { // Returns |true| if created |DesktopEnvironment| instances support audio // capture. virtual bool SupportsAudioCapture() const = 0; + + // Enables or disables gnubby authentication. + virtual void SetEnableGnubbyAuth(bool enable) {} }; } // namespace remoting diff --git a/remoting/host/gnubby_auth_handler.h b/remoting/host/gnubby_auth_handler.h new file mode 100644 index 0000000..70d8bae --- /dev/null +++ b/remoting/host/gnubby_auth_handler.h @@ -0,0 +1,46 @@ +// 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_AUTH_HANDLER_H_ +#define REMOTING_HOST_GNUBBY_AUTH_HANDLER_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" + +namespace base { +class FilePath; +} // namespace base + +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() {} + + // Creates a platform-specific GnubbyAuthHandler. + static scoped_ptr<GnubbyAuthHandler> Create( + protocol::ClientStub* client_stub); + + // Specify the name of the socket to listen to gnubby requests on. + static void SetGnubbySocketName(const base::FilePath& gnubby_socket_name); + + // A message was received from the client. + virtual void DeliverClientMessage(const std::string& message) = 0; + + // Send data to client. + virtual void DeliverHostDataMessage(int connection_id, + const std::string& data) const = 0; +}; + +} // namespace remoting + +#endif // REMOTING_HOST_GNUBBY_AUTH_HANDLER_H_ diff --git a/remoting/host/gnubby_auth_handler_posix.cc b/remoting/host/gnubby_auth_handler_posix.cc new file mode 100644 index 0000000..c797cce --- /dev/null +++ b/remoting/host/gnubby_auth_handler_posix.cc @@ -0,0 +1,279 @@ +// 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_auth_handler_posix.h" + +#include <unistd.h> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/file_util.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/lazy_instance.h" +#include "base/stl_util.h" +#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/proto/control.pb.h" +#include "remoting/protocol/client_stub.h" + +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 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. +base::LazyInstance<base::FilePath>::Leaky g_gnubby_socket_name = + LAZY_INSTANCE_INITIALIZER; + +// STL predicate to match by a StreamListenSocket pointer. +class CompareSocket { + public: + explicit CompareSocket(net::StreamListenSocket* socket) : socket_(socket) {} + + bool operator()(const std::pair<int, net::StreamListenSocket*> element) + const { + return socket_ == element.second; + } + + private: + net::StreamListenSocket* socket_; +}; + +// Socket authentication function that only allows connections from callers with +// the current uid. +bool MatchUid(uid_t user_id, gid_t) { + bool allowed = user_id == getuid(); + if (!allowed) + HOST_LOG << "Refused socket connection from uid " << user_id; + 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 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; +} + +} // namespace + +GnubbyAuthHandlerPosix::GnubbyAuthHandlerPosix( + protocol::ClientStub* client_stub) + : client_stub_(client_stub), last_connection_id_(0) { + DCHECK(client_stub_); +} + +GnubbyAuthHandlerPosix::~GnubbyAuthHandlerPosix() { + STLDeleteValues(&active_sockets_); +} + +// static +scoped_ptr<GnubbyAuthHandler> GnubbyAuthHandler::Create( + protocol::ClientStub* client_stub) { + return scoped_ptr<GnubbyAuthHandler>(new GnubbyAuthHandlerPosix(client_stub)); +} + +// static +void GnubbyAuthHandler::SetGnubbySocketName( + const base::FilePath& gnubby_socket_name) { + g_gnubby_socket_name.Get() = gnubby_socket_name; +} + +void GnubbyAuthHandlerPosix::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) { + 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); + } else { + LOG(ERROR) << "Received gnubby-auth data for unknown connection"; + } + } else { + LOG(ERROR) << "Invalid gnubby-auth data message"; + } + } else { + LOG(ERROR) << "Unknown gnubby-auth message type: " << type; + } + } +} + +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); + + std::string request_json; + if (!base::JSONWriter::Write(&request, &request_json)) { + LOG(ERROR) << "Failed to create request json"; + return; + } + + protocol::ExtensionMessage message; + message.set_type(kGnubbyAuthMessage); + message.set_data(request_json); + + client_stub_->DeliverHostMessage(message); +} + +bool GnubbyAuthHandlerPosix::HasActiveSocketForTesting( + net::StreamListenSocket* socket) const { + return std::find_if(active_sockets_.begin(), + active_sockets_.end(), + CompareSocket(socket)) != active_sockets_.end(); +} + +void GnubbyAuthHandlerPosix::DidAccept( + net::StreamListenSocket* server, + scoped_ptr<net::StreamListenSocket> socket) { + DCHECK(CalledOnValidThread()); + + active_sockets_[++last_connection_id_] = socket.release(); +} + +void GnubbyAuthHandlerPosix::DidRead(net::StreamListenSocket* socket, + const char* data, + int len) { + DCHECK(CalledOnValidThread()); + + ActiveSockets::iterator socket_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); + } + } + } +} + +void GnubbyAuthHandlerPosix::DidClose(net::StreamListenSocket* socket) { + DCHECK(CalledOnValidThread()); + + 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); + } +} + +void GnubbyAuthHandlerPosix::CreateAuthorizationSocket() { + DCHECK(CalledOnValidThread()); + + if (!g_gnubby_socket_name.Get().empty()) { + // 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_ = net::UnixDomainSocket::CreateAndListen( + g_gnubby_socket_name.Get().value(), this, base::Bind(MatchUid)); + if (!auth_socket_.get()) { + LOG(ERROR) << "Failed to open socket for gnubby requests"; + } + } else { + HOST_LOG << "No gnubby socket name specified"; + } +} + +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"; + } +} + +} // namespace remoting diff --git a/remoting/host/gnubby_auth_handler_posix.h b/remoting/host/gnubby_auth_handler_posix.h new file mode 100644 index 0000000..ea0e3b61 --- /dev/null +++ b/remoting/host/gnubby_auth_handler_posix.h @@ -0,0 +1,74 @@ +// 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_AUTH_HANDLER_POSIX_H_ +#define REMOTING_HOST_GNUBBY_AUTH_HANDLER_POSIX_H_ + +#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 remoting { + +namespace protocol { +class ClientStub; +} // namespace protocol + +class GnubbyAuthHandlerPosix : public GnubbyAuthHandler, + public base::NonThreadSafe, + public net::StreamListenSocket::Delegate { + public: + explicit GnubbyAuthHandlerPosix(protocol::ClientStub* client_stub); + virtual ~GnubbyAuthHandlerPosix(); + + bool HasActiveSocketForTesting(net::StreamListenSocket* socket) const; + + private: + // GnubbyAuthHandler interface. + virtual void DeliverClientMessage(const std::string& message) OVERRIDE; + virtual void DeliverHostDataMessage(int connection_id, + const std::string& data) const OVERRIDE; + + // StreamListenSocket::Delegate interface. + virtual void DidAccept(net::StreamListenSocket* server, + scoped_ptr<net::StreamListenSocket> socket) OVERRIDE; + virtual void DidRead(net::StreamListenSocket* socket, + const char* data, + int len) OVERRIDE; + virtual void DidClose(net::StreamListenSocket* socket) OVERRIDE; + + // Create socket for authorization. + void CreateAuthorizationSocket(); + + // Process a gnubby request. + void ProcessGnubbyRequest(int connection_id, const char* data, int data_len); + + // Interface through which communication with the client occurs. + protocol::ClientStub* client_stub_; + + // Socket used to listen for authorization requests. + scoped_ptr<net::StreamListenSocket> auth_socket_; + + // The last assigned gnubby connection id. + 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); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_GNUBBY_AUTH_HANDLER_POSIX_H_ diff --git a/remoting/host/gnubby_auth_handler_posix_unittest.cc b/remoting/host/gnubby_auth_handler_posix_unittest.cc new file mode 100644 index 0000000..60f32c8c --- /dev/null +++ b/remoting/host/gnubby_auth_handler_posix_unittest.cc @@ -0,0 +1,126 @@ +// 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 "base/strings/stringprintf.h" +#include "net/socket/stream_listen_socket.h" +#include "remoting/host/gnubby_auth_handler_posix.h" +#include "remoting/protocol/protocol_mock_objects.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace remoting { + +using protocol::MockClientStub; + +using testing::_; +using testing::Return; + +namespace { + +// Test gnubby request data. +const 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}; + +class MockStreamListenSocket : public net::StreamListenSocket { + public: + explicit MockStreamListenSocket(net::StreamListenSocket::Delegate* delegate) + : StreamListenSocket(net::kInvalidSocket, delegate) {} + + virtual void Accept() OVERRIDE { NOTREACHED(); } + + private: + virtual ~MockStreamListenSocket() {} +}; + +} // namespace + +class GnubbyAuthHandlerPosixTest : public testing::Test { + public: + GnubbyAuthHandlerPosixTest() {} + + virtual void SetUp() OVERRIDE; + + protected: + // Object under test. + scoped_ptr<GnubbyAuthHandlerPosix> auth_handler_posix_; + + // GnubbyAuthHandler interface for |auth_handler_posix_|. + GnubbyAuthHandler* auth_handler_; + + // Stream delegate interface for |auth_handler_posix_|. + net::StreamListenSocket::Delegate* delegate_; + + // Mock client stub. + MockClientStub client_stub_; + + private: + void OnConnect(int result); +}; + +void GnubbyAuthHandlerPosixTest::SetUp() { + auth_handler_posix_.reset(new GnubbyAuthHandlerPosix(&client_stub_)); + auth_handler_ = auth_handler_posix_.get(); + delegate_ = auth_handler_posix_.get(); +} + +MATCHER_P2(EqualsDataMessage, id, data, "") { + std::string connection_id = base::StringPrintf("\"connectionId\":%d", id); + std::string json_message = base::StringPrintf("\"jsonMessage\":\"%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); +} + +TEST_F(GnubbyAuthHandlerPosixTest, HostDataMessageDelivered) { + EXPECT_CALL(client_stub_, + DeliverHostMessage(EqualsDataMessage(42, "test_msg"))); + + auth_handler_->DeliverHostDataMessage(42, "test_msg"); +} + +TEST_F(GnubbyAuthHandlerPosixTest, DidClose) { + net::StreamListenSocket* socket = new MockStreamListenSocket(delegate_); + + delegate_->DidAccept(NULL, make_scoped_ptr(socket)); + ASSERT_TRUE(auth_handler_posix_->HasActiveSocketForTesting(socket)); + + delegate_->DidClose(socket); + ASSERT_FALSE(auth_handler_posix_->HasActiveSocketForTesting(socket)); +} + +TEST_F(GnubbyAuthHandlerPosixTest, DidRead) { + EXPECT_CALL(client_stub_, DeliverHostMessage(_)); + + net::StreamListenSocket* socket = new MockStreamListenSocket(delegate_); + + delegate_->DidAccept(NULL, make_scoped_ptr(socket)); + delegate_->DidRead(socket, request_data, sizeof(request_data)); +} + +TEST_F(GnubbyAuthHandlerPosixTest, DidReadByteByByte) { + EXPECT_CALL(client_stub_, DeliverHostMessage(_)); + + net::StreamListenSocket* socket = new MockStreamListenSocket(delegate_); + + delegate_->DidAccept(NULL, make_scoped_ptr(socket)); + for (unsigned int i = 0; i < sizeof(request_data); ++i) { + delegate_->DidRead(socket, request_data + i, 1); + } +} + +} // namespace remoting diff --git a/remoting/host/gnubby_auth_handler_win.cc b/remoting/host/gnubby_auth_handler_win.cc new file mode 100644 index 0000000..12731b3 --- /dev/null +++ b/remoting/host/gnubby_auth_handler_win.cc @@ -0,0 +1,47 @@ +// 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_auth_handler.h" + +#include "base/logging.h" + +namespace remoting { + +namespace { + +class GnubbyAuthHandlerWin : public GnubbyAuthHandler { + private: + // GnubbyAuthHandler interface. + virtual void DeliverClientMessage(const std::string& message) OVERRIDE; + virtual 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) { + return scoped_ptr<GnubbyAuthHandler>(); +} + +// 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/gnubby_util.cc b/remoting/host/gnubby_util.cc new file mode 100644 index 0000000..126584b --- /dev/null +++ b/remoting/host/gnubby_util.cc @@ -0,0 +1,347 @@ +// 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 new file mode 100644 index 0000000..0d888d6 --- /dev/null +++ b/remoting/host/gnubby_util.h @@ -0,0 +1,30 @@ +// 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 new file mode 100644 index 0000000..e788788 --- /dev/null +++ b/remoting/host/gnubby_util_unittest.cc @@ -0,0 +1,95 @@ +// 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 diff --git a/remoting/host/host_mock_objects.cc b/remoting/host/host_mock_objects.cc index 4bdd064..dcc2646 100644 --- a/remoting/host/host_mock_objects.cc +++ b/remoting/host/host_mock_objects.cc @@ -39,6 +39,12 @@ MockDesktopEnvironment::CreateVideoCapturer() { return scoped_ptr<webrtc::ScreenCapturer>(CreateVideoCapturerPtr()); } +scoped_ptr<GnubbyAuthHandler> +MockDesktopEnvironment::CreateGnubbyAuthHandler( + protocol::ClientStub* client_stub) { + return scoped_ptr<GnubbyAuthHandler>(CreateGnubbyAuthHandlerPtr(client_stub)); +} + MockDesktopEnvironmentFactory::MockDesktopEnvironmentFactory() {} MockDesktopEnvironmentFactory::~MockDesktopEnvironmentFactory() {} @@ -69,4 +75,8 @@ MockHostStatusObserver::MockHostStatusObserver() {} MockHostStatusObserver::~MockHostStatusObserver() {} +MockGnubbyAuthHandler::MockGnubbyAuthHandler() {} + +MockGnubbyAuthHandler::~MockGnubbyAuthHandler() {} + } // namespace remoting diff --git a/remoting/host/host_mock_objects.h b/remoting/host/host_mock_objects.h index b5f1db9..e8bad3e 100644 --- a/remoting/host/host_mock_objects.h +++ b/remoting/host/host_mock_objects.h @@ -5,11 +5,14 @@ #ifndef REMOTING_HOST_HOST_MOCK_OBJECTS_H_ #define REMOTING_HOST_HOST_MOCK_OBJECTS_H_ +#include <string> + #include "net/base/ip_endpoint.h" #include "remoting/host/chromoting_host_context.h" #include "remoting/host/client_session.h" #include "remoting/host/client_session_control.h" #include "remoting/host/desktop_environment.h" +#include "remoting/host/gnubby_auth_handler.h" #include "remoting/host/host_status_observer.h" #include "remoting/host/input_injector.h" #include "remoting/host/screen_controls.h" @@ -34,12 +37,16 @@ class MockDesktopEnvironment : public DesktopEnvironment { MOCK_METHOD0(CreateVideoCapturerPtr, webrtc::ScreenCapturer*()); MOCK_CONST_METHOD0(GetCapabilities, std::string()); MOCK_METHOD1(SetCapabilities, void(const std::string&)); + MOCK_METHOD1(CreateGnubbyAuthHandlerPtr, GnubbyAuthHandler*( + protocol::ClientStub* client_stub)); // DesktopEnvironment implementation. virtual scoped_ptr<AudioCapturer> CreateAudioCapturer() OVERRIDE; virtual scoped_ptr<InputInjector> CreateInputInjector() OVERRIDE; virtual scoped_ptr<ScreenControls> CreateScreenControls() OVERRIDE; virtual scoped_ptr<webrtc::ScreenCapturer> CreateVideoCapturer() OVERRIDE; + virtual scoped_ptr<GnubbyAuthHandler> CreateGnubbyAuthHandler( + protocol::ClientStub* client_stub) OVERRIDE; }; class MockClientSessionControl : public ClientSessionControl { @@ -126,6 +133,19 @@ class MockHostStatusObserver : public HostStatusObserver { MOCK_METHOD0(OnShutdown, void()); }; +class MockGnubbyAuthHandler : public GnubbyAuthHandler { + public: + MockGnubbyAuthHandler(); + virtual ~MockGnubbyAuthHandler(); + + MOCK_METHOD1(DeliverClientMessage, void(const std::string& message)); + MOCK_CONST_METHOD2(DeliverHostDataMessage, + void(int connection_id, const std::string& data)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockGnubbyAuthHandler); +}; + } // namespace remoting #endif // REMOTING_HOST_HOST_MOCK_OBJECTS_H_ diff --git a/remoting/host/ipc_desktop_environment.cc b/remoting/host/ipc_desktop_environment.cc index 6ec6676..5125494 100644 --- a/remoting/host/ipc_desktop_environment.cc +++ b/remoting/host/ipc_desktop_environment.cc @@ -16,6 +16,7 @@ #include "remoting/host/client_session_control.h" #include "remoting/host/desktop_session.h" #include "remoting/host/desktop_session_proxy.h" +#include "remoting/host/gnubby_auth_handler.h" #include "remoting/host/input_injector.h" #include "remoting/host/screen_controls.h" #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" @@ -69,6 +70,11 @@ void IpcDesktopEnvironment::SetCapabilities(const std::string& capabilities) { return desktop_session_proxy_->SetCapabilities(capabilities); } +scoped_ptr<GnubbyAuthHandler> IpcDesktopEnvironment::CreateGnubbyAuthHandler( + protocol::ClientStub* client_stub) { + return scoped_ptr<GnubbyAuthHandler>(); +} + IpcDesktopEnvironmentFactory::IpcDesktopEnvironmentFactory( scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner, scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner, diff --git a/remoting/host/ipc_desktop_environment.h b/remoting/host/ipc_desktop_environment.h index 2dee387..bb68df1 100644 --- a/remoting/host/ipc_desktop_environment.h +++ b/remoting/host/ipc_desktop_environment.h @@ -28,6 +28,7 @@ namespace remoting { class ClientSessionControl; class DesktopSessionProxy; +class GnubbyAuthHandler; class ScreenResolution; // A variant of desktop environment integrating with the desktop by means of @@ -54,6 +55,8 @@ class IpcDesktopEnvironment : public DesktopEnvironment { virtual scoped_ptr<webrtc::ScreenCapturer> CreateVideoCapturer() OVERRIDE; virtual std::string GetCapabilities() const OVERRIDE; virtual void SetCapabilities(const std::string& capabilities) OVERRIDE; + virtual scoped_ptr<GnubbyAuthHandler> CreateGnubbyAuthHandler( + protocol::ClientStub* client_stub) OVERRIDE; private: scoped_refptr<DesktopSessionProxy> desktop_session_proxy_; diff --git a/remoting/host/me2me_desktop_environment.cc b/remoting/host/me2me_desktop_environment.cc index f9f9113..abf9a99 100644 --- a/remoting/host/me2me_desktop_environment.cc +++ b/remoting/host/me2me_desktop_environment.cc @@ -6,9 +6,11 @@ #include "base/logging.h" #include "base/single_thread_task_runner.h" +#include "remoting/base/logging.h" #include "remoting/host/client_session_control.h" #include "remoting/host/curtain_mode.h" #include "remoting/host/desktop_resizer.h" +#include "remoting/host/gnubby_auth_handler.h" #include "remoting/host/host_window.h" #include "remoting/host/host_window.h" #include "remoting/host/host_window_proxy.h" @@ -58,10 +60,23 @@ Me2MeDesktopEnvironment::Me2MeDesktopEnvironment( scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) : BasicDesktopEnvironment(caller_task_runner, input_task_runner, - ui_task_runner) { + ui_task_runner), + gnubby_auth_enabled_(false) { DCHECK(caller_task_runner->BelongsToCurrentThread()); } +scoped_ptr<GnubbyAuthHandler> Me2MeDesktopEnvironment::CreateGnubbyAuthHandler( + protocol::ClientStub* client_stub) { + DCHECK(caller_task_runner()->BelongsToCurrentThread()); + + if (gnubby_auth_enabled_) + return scoped_ptr<GnubbyAuthHandler>( + GnubbyAuthHandler::Create(client_stub)); + + HOST_LOG << "gnubby auth is not enabled"; + return scoped_ptr<GnubbyAuthHandler>(); +} + bool Me2MeDesktopEnvironment::InitializeSecurity( base::WeakPtr<ClientSessionControl> client_session_control, bool curtain_enabled) { @@ -115,6 +130,10 @@ bool Me2MeDesktopEnvironment::InitializeSecurity( return true; } +void Me2MeDesktopEnvironment::SetEnableGnubbyAuth(bool gnubby_auth_enabled) { + gnubby_auth_enabled_ = gnubby_auth_enabled; +} + Me2MeDesktopEnvironmentFactory::Me2MeDesktopEnvironmentFactory( scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner, scoped_refptr<base::SingleThreadTaskRunner> input_task_runner, @@ -140,6 +159,7 @@ scoped_ptr<DesktopEnvironment> Me2MeDesktopEnvironmentFactory::Create( curtain_enabled_)) { return scoped_ptr<DesktopEnvironment>(); } + desktop_environment->SetEnableGnubbyAuth(gnubby_auth_enabled_); return desktop_environment.PassAs<DesktopEnvironment>(); } @@ -150,4 +170,9 @@ void Me2MeDesktopEnvironmentFactory::SetEnableCurtaining(bool enable) { curtain_enabled_ = enable; } +void Me2MeDesktopEnvironmentFactory::SetEnableGnubbyAuth( + bool gnubby_auth_enabled) { + gnubby_auth_enabled_ = gnubby_auth_enabled; +} + } // namespace remoting diff --git a/remoting/host/me2me_desktop_environment.h b/remoting/host/me2me_desktop_environment.h index f028664..9ee69a9 100644 --- a/remoting/host/me2me_desktop_environment.h +++ b/remoting/host/me2me_desktop_environment.h @@ -23,6 +23,8 @@ class Me2MeDesktopEnvironment : public BasicDesktopEnvironment { virtual scoped_ptr<ScreenControls> CreateScreenControls() OVERRIDE; virtual scoped_ptr<webrtc::ScreenCapturer> CreateVideoCapturer() OVERRIDE; virtual std::string GetCapabilities() const OVERRIDE; + virtual scoped_ptr<GnubbyAuthHandler> CreateGnubbyAuthHandler( + protocol::ClientStub* client_stub) OVERRIDE; protected: friend class Me2MeDesktopEnvironmentFactory; @@ -37,6 +39,8 @@ class Me2MeDesktopEnvironment : public BasicDesktopEnvironment { base::WeakPtr<ClientSessionControl> client_session_control, bool curtain_enabled); + void SetEnableGnubbyAuth(bool gnubby_auth_enabled); + private: // "Curtains" the session making sure it is disconnected from the local // console. @@ -48,6 +52,9 @@ class Me2MeDesktopEnvironment : public BasicDesktopEnvironment { // Notifies the client session about the local mouse movements. scoped_ptr<LocalInputMonitor> local_input_monitor_; + // True if gnubby auth is enabled. + bool gnubby_auth_enabled_; + DISALLOW_COPY_AND_ASSIGN(Me2MeDesktopEnvironment); }; @@ -64,6 +71,7 @@ class Me2MeDesktopEnvironmentFactory : public BasicDesktopEnvironmentFactory { virtual scoped_ptr<DesktopEnvironment> Create( base::WeakPtr<ClientSessionControl> client_session_control) OVERRIDE; virtual void SetEnableCurtaining(bool enable) OVERRIDE; + virtual void SetEnableGnubbyAuth(bool enable) OVERRIDE; protected: bool curtain_enabled() const { return curtain_enabled_; } @@ -72,6 +80,9 @@ class Me2MeDesktopEnvironmentFactory : public BasicDesktopEnvironmentFactory { // True if curtain mode is enabled. bool curtain_enabled_; + // True if gnubby auth is enabled. + bool gnubby_auth_enabled_; + DISALLOW_COPY_AND_ASSIGN(Me2MeDesktopEnvironmentFactory); }; diff --git a/remoting/host/policy_hack/policy_watcher.cc b/remoting/host/policy_hack/policy_watcher.cc index 11302f6..fd1a37b 100644 --- a/remoting/host/policy_hack/policy_watcher.cc +++ b/remoting/host/policy_hack/policy_watcher.cc @@ -104,6 +104,9 @@ const char PolicyWatcher::kHostTokenValidationCertIssuerPolicyName[] = const char PolicyWatcher::kHostAllowClientPairing[] = "RemoteAccessHostAllowClientPairing"; +const char PolicyWatcher::kHostAllowGnubbyAuthPolicyName[] = + "RemoteAccessHostAllowGnubbyAuth"; + const char PolicyWatcher::kHostDebugOverridePoliciesName[] = "RemoteAccessHostDebugOverridePolicies"; @@ -126,6 +129,7 @@ PolicyWatcher::PolicyWatcher( default_values_->SetString(kHostTokenValidationCertIssuerPolicyName, std::string()); default_values_->SetBoolean(kHostAllowClientPairing, true); + default_values_->SetBoolean(kHostAllowGnubbyAuthPolicyName, true); #if !defined(NDEBUG) default_values_->SetString(kHostDebugOverridePoliciesName, std::string()); #endif diff --git a/remoting/host/policy_hack/policy_watcher.h b/remoting/host/policy_hack/policy_watcher.h index 915d0ba..a3afee0 100644 --- a/remoting/host/policy_hack/policy_watcher.h +++ b/remoting/host/policy_hack/policy_watcher.h @@ -74,6 +74,9 @@ class PolicyWatcher { // The name of the policy for disabling PIN-less authentication. static const char kHostAllowClientPairing[]; + // The name of the policy for disabling gnubbyd forwarding. + static const char kHostAllowGnubbyAuthPolicyName[]; + // The name of the policy for overriding policies, for use in testing. static const char kHostDebugOverridePoliciesName[]; diff --git a/remoting/host/policy_hack/policy_watcher_unittest.cc b/remoting/host/policy_hack/policy_watcher_unittest.cc index 26aa8db..729a7bb 100644 --- a/remoting/host/policy_hack/policy_watcher_unittest.cc +++ b/remoting/host/policy_hack/policy_watcher_unittest.cc @@ -71,6 +71,10 @@ class PolicyWatcherTest : public testing::Test { kOverrideNatTraversalToFalse); pairing_true_.SetBoolean(PolicyWatcher::kHostAllowClientPairing, true); pairing_false_.SetBoolean(PolicyWatcher::kHostAllowClientPairing, false); + gnubby_auth_true_.SetBoolean(PolicyWatcher::kHostAllowGnubbyAuthPolicyName, + true); + gnubby_auth_false_.SetBoolean(PolicyWatcher::kHostAllowGnubbyAuthPolicyName, + false); #if !defined(NDEBUG) SetDefaults(nat_false_overridden_others_default_); nat_false_overridden_others_default_.SetBoolean( @@ -120,6 +124,8 @@ class PolicyWatcherTest : public testing::Test { base::DictionaryValue nat_false_overridden_others_default_; base::DictionaryValue pairing_true_; base::DictionaryValue pairing_false_; + base::DictionaryValue gnubby_auth_true_; + base::DictionaryValue gnubby_auth_false_; private: void SetDefaults(base::DictionaryValue& dict) { @@ -136,6 +142,7 @@ class PolicyWatcherTest : public testing::Test { dict.SetString(PolicyWatcher::kHostTokenValidationCertIssuerPolicyName, std::string()); dict.SetBoolean(PolicyWatcher::kHostAllowClientPairing, true); + dict.SetBoolean(PolicyWatcher::kHostAllowGnubbyAuthPolicyName, true); #if !defined(NDEBUG) dict.SetString(PolicyWatcher::kHostDebugOverridePoliciesName, ""); #endif @@ -332,5 +339,21 @@ TEST_F(PolicyWatcherTest, PairingFalseThenTrue) { StopWatching(); } +TEST_F(PolicyWatcherTest, GnubbyAuth) { + testing::InSequence sequence; + EXPECT_CALL(mock_policy_callback_, + OnPolicyUpdatePtr(IsPolicies(&nat_true_others_default_))); + EXPECT_CALL(mock_policy_callback_, + OnPolicyUpdatePtr(IsPolicies(&gnubby_auth_false_))); + EXPECT_CALL(mock_policy_callback_, + OnPolicyUpdatePtr(IsPolicies(&gnubby_auth_true_))); + + StartWatching(); + policy_watcher_->SetPolicies(&empty_); + policy_watcher_->SetPolicies(&gnubby_auth_false_); + policy_watcher_->SetPolicies(&gnubby_auth_true_); + StopWatching(); +} + } // namespace policy_hack } // namespace remoting diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc index 69b505b..aadd809 100644 --- a/remoting/host/remoting_me2me_host.cc +++ b/remoting/host/remoting_me2me_host.cc @@ -112,6 +112,10 @@ const char kApplicationName[] = "chromoting"; // The command line switch used to pass name of the pipe to capture audio on // linux. const char kAudioPipeSwitchName[] = "audio-pipe-name"; + +// The command line switch used to pass name of the unix domain socket used to +// listen for gnubby requests. +const char kAuthSocknameSwitchName[] = "ssh-auth-sockname"; #endif // defined(OS_LINUX) // The command line switch used by the parent to request the host to signal it @@ -229,6 +233,7 @@ class HostProcess const GURL& token_validation_url, const std::string& token_validation_cert_issuer); bool OnPairingPolicyUpdate(bool pairing_enabled); + bool OnGnubbyAuthPolicyUpdate(bool enable_gnubby_auth); void StartHost(); @@ -286,6 +291,7 @@ class HostProcess bool curtain_required_; ThirdPartyAuthConfig third_party_auth_config_; + bool enable_gnubby_auth_; scoped_ptr<OAuthTokenGetter> oauth_token_getter_; scoped_ptr<XmppSignalStrategy> signal_strategy_; @@ -319,6 +325,7 @@ HostProcess::HostProcess(scoped_ptr<ChromotingHostContext> context, allow_nat_traversal_(true), allow_pairing_(true), curtain_required_(false), + enable_gnubby_auth_(false), #if defined(REMOTING_MULTI_PROCESS) desktop_session_connector_(NULL), #endif // defined(REMOTING_MULTI_PROCESS) @@ -631,6 +638,11 @@ void HostProcess::StartOnUiThread() { remoting::AudioCapturerLinux::InitializePipeReader( context_->audio_task_runner(), audio_pipe_name); } + + base::FilePath gnubby_socket_name = CommandLine::ForCurrentProcess()-> + GetSwitchValuePath(kAuthSocknameSwitchName); + if (!gnubby_socket_name.empty()) + remoting::GnubbyAuthHandler::SetGnubbySocketName(gnubby_socket_name); #endif // defined(OS_LINUX) // Create a desktop environment factory appropriate to the build type & @@ -653,6 +665,7 @@ void HostProcess::StartOnUiThread() { #endif // !defined(OS_WIN) desktop_environment_factory_.reset(desktop_environment_factory); + desktop_environment_factory_->SetEnableGnubbyAuth(enable_gnubby_auth_); context_->network_task_runner()->PostTask( FROM_HERE, @@ -847,6 +860,10 @@ void HostProcess::OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies) { &bool_value)) { restart_required |= OnPairingPolicyUpdate(bool_value); } + if (policies->GetBoolean( + policy_hack::PolicyWatcher::kHostAllowGnubbyAuthPolicyName, + &bool_value)) + restart_required |= OnGnubbyAuthPolicyUpdate(bool_value); if (state_ == HOST_INITIALIZING) { StartHost(); @@ -1012,6 +1029,25 @@ bool HostProcess::OnPairingPolicyUpdate(bool allow_pairing) { return true; } +bool HostProcess::OnGnubbyAuthPolicyUpdate(bool enable_gnubby_auth) { + DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); + + if (enable_gnubby_auth_ == enable_gnubby_auth) + return false; + + if (enable_gnubby_auth) { + HOST_LOG << "Policy enables gnubby auth."; + } else { + HOST_LOG << "Policy disables gnubby auth."; + } + enable_gnubby_auth_ = enable_gnubby_auth; + + if (desktop_environment_factory_) + desktop_environment_factory_->SetEnableGnubbyAuth(enable_gnubby_auth); + + return true; +} + void HostProcess::StartHost() { DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); DCHECK(!host_); diff --git a/remoting/remoting_host.gypi b/remoting/remoting_host.gypi index 46b31ec..970e972 100644 --- a/remoting/remoting_host.gypi +++ b/remoting/remoting_host.gypi @@ -112,6 +112,12 @@ 'host/disconnect_window_win.cc', 'host/dns_blackhole_checker.cc', 'host/dns_blackhole_checker.h', + 'host/gnubby_auth_handler_posix.cc', + 'host/gnubby_auth_handler_posix.h', + 'host/gnubby_auth_handler_win.cc', + 'host/gnubby_auth_handler.h', + 'host/gnubby_util.cc', + 'host/gnubby_util.h', 'host/heartbeat_sender.cc', 'host/heartbeat_sender.h', 'host/host_change_notification_listener.cc', diff --git a/remoting/remoting_test.gypi b/remoting/remoting_test.gypi index 82ad008..0979c9b 100644 --- a/remoting/remoting_test.gypi +++ b/remoting/remoting_test.gypi @@ -74,6 +74,8 @@ 'host/daemon_process_unittest.cc', 'host/desktop_process_unittest.cc', 'host/desktop_shape_tracker_unittest.cc', + 'host/gnubby_auth_handler_posix_unittest.cc', + 'host/gnubby_util_unittest.cc', 'host/heartbeat_sender_unittest.cc', 'host/host_change_notification_listener_unittest.cc', 'host/host_mock_objects.cc', diff --git a/remoting/tools/me2me_virtual_host.py b/remoting/tools/me2me_virtual_host.py index 3cd1fc0..a275f3a 100755 --- a/remoting/tools/me2me_virtual_host.py +++ b/remoting/tools/me2me_virtual_host.py @@ -204,6 +204,7 @@ class Desktop: self.pulseaudio_pipe = None self.server_supports_exact_resize = False self.host_ready = False + self.ssh_auth_sockname = None g_desktops.append(self) @staticmethod @@ -304,6 +305,10 @@ class Desktop: return True + def _setup_gnubby(self): + self.ssh_auth_sockname = ("/tmp/chromoting.%s.ssh_auth_sock" % + os.environ["USER"]) + def _launch_x_server(self, extra_x_args): x_auth_file = os.path.expanduser("~/.Xauthority") self.child_env["XAUTHORITY"] = x_auth_file @@ -361,6 +366,10 @@ class Desktop: chrome_profile = os.path.join(CONFIG_DIR, "chrome-profile") self.child_env["CHROME_USER_DATA_DIR"] = chrome_profile + # Set SSH_AUTH_SOCK to the file name to listen on. + if self.ssh_auth_sockname: + self.child_env["SSH_AUTH_SOCK"] = self.ssh_auth_sockname + # Wait for X to be active. for _test in range(5): proc = subprocess.Popen("xdpyinfo", env=self.child_env, stdout=devnull) @@ -433,6 +442,7 @@ class Desktop: def launch_session(self, x_args): self._init_child_env() self._setup_pulseaudio() + self._setup_gnubby() self._launch_x_server(x_args) self._launch_x_session() @@ -443,6 +453,8 @@ class Desktop: args.append("--audio-pipe-name=%s" % self.pulseaudio_pipe) if self.server_supports_exact_resize: args.append("--server-supports-exact-resize") + if self.ssh_auth_sockname: + args.append("--ssh-auth-sockname=%s" % self.ssh_auth_sockname) # Have the host process use SIGUSR1 to signal a successful start. def sigusr1_handler(signum, frame): diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 1e27661..8325e88 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml @@ -24758,6 +24758,7 @@ other types of suffix sets. <int value="255" label="Enable on-screen keyboard"/> <int value="256" label="Set default state of the on-screen keyboard on the login screen"/> + <int value="257" label="Allow gnubby authentication"/> </enum> <enum name="EnterprisePolicyInvalidations" type="int"> |