summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/policy/configuration_policy_handler_list_factory.cc3
-rw-r--r--chrome/common/pref_names.cc4
-rw-r--r--chrome/common/pref_names.h1
-rw-r--r--chrome/test/data/policy/policy_test_cases.json8
-rw-r--r--components/policy/resources/policy_templates.json18
-rw-r--r--remoting/host/basic_desktop_environment.cc6
-rw-r--r--remoting/host/basic_desktop_environment.h4
-rw-r--r--remoting/host/client_session.cc17
-rw-r--r--remoting/host/client_session.h6
-rw-r--r--remoting/host/client_session_unittest.cc64
-rw-r--r--remoting/host/desktop_environment.h13
-rw-r--r--remoting/host/gnubby_auth_handler.h46
-rw-r--r--remoting/host/gnubby_auth_handler_posix.cc279
-rw-r--r--remoting/host/gnubby_auth_handler_posix.h74
-rw-r--r--remoting/host/gnubby_auth_handler_posix_unittest.cc126
-rw-r--r--remoting/host/gnubby_auth_handler_win.cc47
-rw-r--r--remoting/host/gnubby_util.cc347
-rw-r--r--remoting/host/gnubby_util.h30
-rw-r--r--remoting/host/gnubby_util_unittest.cc95
-rw-r--r--remoting/host/host_mock_objects.cc10
-rw-r--r--remoting/host/host_mock_objects.h20
-rw-r--r--remoting/host/ipc_desktop_environment.cc6
-rw-r--r--remoting/host/ipc_desktop_environment.h3
-rw-r--r--remoting/host/me2me_desktop_environment.cc27
-rw-r--r--remoting/host/me2me_desktop_environment.h11
-rw-r--r--remoting/host/policy_hack/policy_watcher.cc4
-rw-r--r--remoting/host/policy_hack/policy_watcher.h3
-rw-r--r--remoting/host/policy_hack/policy_watcher_unittest.cc23
-rw-r--r--remoting/host/remoting_me2me_host.cc36
-rw-r--r--remoting/remoting_host.gypi6
-rw-r--r--remoting/remoting_test.gypi2
-rwxr-xr-xremoting/tools/me2me_virtual_host.py12
-rw-r--r--tools/metrics/histograms/histograms.xml1
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">