diff options
author | alexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-12 19:23:07 +0000 |
---|---|---|
committer | alexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-12 19:23:07 +0000 |
commit | c2b374409956cdad6d90ae6913f02251b09d8280 (patch) | |
tree | 7b0d440a0685ff903a8bbe846ff57917b5731901 | |
parent | 0ab8bf8880a7ede55591fefb0e1162dc1a6f33f0 (diff) | |
download | chromium_src-c2b374409956cdad6d90ae6913f02251b09d8280.zip chromium_src-c2b374409956cdad6d90ae6913f02251b09d8280.tar.gz chromium_src-c2b374409956cdad6d90ae6913f02251b09d8280.tar.bz2 |
Chromoting: Implemented security attention sequence (SAS) emulation on Windows.
The default SAS is Ctrl+Alt+Delete. It can neither be intercepted by the client (at least on Windows) nor it can be injected the same way other key strokes are injected. This CL makes the Windows host to interpret a double Scroll Lock as a SAS and invoke the Chromoting service to issue the actual SAS.
Review URL: http://codereview.chromium.org/9617027
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@126186 0039d316-1c4b-4281-b951-d872f2087c98
26 files changed, 781 insertions, 87 deletions
diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h index 912bc04..e8e3173 100644 --- a/ipc/ipc_message_utils.h +++ b/ipc/ipc_message_utils.h @@ -101,6 +101,7 @@ enum IPCMessageStart { ShellMsgStart, AccessibilityMsgStart, PrerenderMsgStart, + ChromotingMsgStart, LastIPCMsgStart // Must come last. }; diff --git a/remoting/host/chromoting_host_context.cc b/remoting/host/chromoting_host_context.cc index d66a4f2..783834f 100644 --- a/remoting/host/chromoting_host_context.cc +++ b/remoting/host/chromoting_host_context.cc @@ -13,10 +13,12 @@ namespace remoting { ChromotingHostContext::ChromotingHostContext( + base::MessageLoopProxy* io_message_loop, base::MessageLoopProxy* ui_message_loop) : main_thread_("ChromotingMainThread"), encode_thread_("ChromotingEncodeThread"), desktop_thread_("ChromotingDesktopThread"), + io_message_loop_(io_message_loop), ui_message_loop_(ui_message_loop) { } @@ -33,6 +35,10 @@ JingleThread* ChromotingHostContext::jingle_thread() { return &jingle_thread_; } +base::MessageLoopProxy* ChromotingHostContext::io_message_loop() { + return io_message_loop_; +} + base::MessageLoopProxy* ChromotingHostContext::ui_message_loop() { return ui_message_loop_; } diff --git a/remoting/host/chromoting_host_context.h b/remoting/host/chromoting_host_context.h index 92db811..d1b87ed 100644 --- a/remoting/host/chromoting_host_context.h +++ b/remoting/host/chromoting_host_context.h @@ -19,7 +19,8 @@ namespace remoting { class ChromotingHostContext { public: // Create a context. - explicit ChromotingHostContext(base::MessageLoopProxy* ui_message_loop); + ChromotingHostContext(base::MessageLoopProxy* io_message_loop, + base::MessageLoopProxy* ui_message_loop); virtual ~ChromotingHostContext(); // TODO(ajwong): Move the Start method out of this class. Then @@ -31,6 +32,7 @@ class ChromotingHostContext { virtual JingleThread* jingle_thread(); + virtual base::MessageLoopProxy* io_message_loop(); virtual base::MessageLoopProxy* ui_message_loop(); virtual MessageLoop* main_message_loop(); virtual MessageLoop* encode_message_loop(); @@ -53,6 +55,7 @@ class ChromotingHostContext { // This is NOT a Chrome-style UI thread. base::Thread desktop_thread_; + scoped_refptr<base::MessageLoopProxy> io_message_loop_; scoped_refptr<base::MessageLoopProxy> ui_message_loop_; DISALLOW_COPY_AND_ASSIGN(ChromotingHostContext); diff --git a/remoting/host/chromoting_host_context_unittest.cc b/remoting/host/chromoting_host_context_unittest.cc index 59a2ec5..2ada2ed 100644 --- a/remoting/host/chromoting_host_context_unittest.cc +++ b/remoting/host/chromoting_host_context_unittest.cc @@ -13,8 +13,7 @@ namespace remoting { // operates properly and all threads and message loops are valid. TEST(ChromotingHostContextTest, StartAndStop) { MessageLoop message_loop; - ChromotingHostContext context( - base::MessageLoopProxy::current()); + ChromotingHostContext context(NULL, base::MessageLoopProxy::current()); context.Start(); EXPECT_TRUE(context.jingle_thread()); diff --git a/remoting/host/chromoting_host_unittest.cc b/remoting/host/chromoting_host_unittest.cc index 185be71..f04be7c 100644 --- a/remoting/host/chromoting_host_unittest.cc +++ b/remoting/host/chromoting_host_unittest.cc @@ -88,10 +88,12 @@ class ChromotingHostTest : public testing::Test { EXPECT_CALL(context_, ui_message_loop()) .Times(AnyNumber()); - Capturer* capturer = new CapturerFake(); + scoped_ptr<Capturer> capturer(new CapturerFake()); event_executor_ = new MockEventExecutor(); - desktop_environment_.reset( - new DesktopEnvironment(&context_, capturer, event_executor_)); + desktop_environment_ = DesktopEnvironment::CreateFake( + &context_, + capturer.Pass(), + scoped_ptr<protocol::InputStub>(event_executor_)); host_ = new ChromotingHost( &context_, &signal_strategy_, desktop_environment_.get(), diff --git a/remoting/host/chromoting_messages.cc b/remoting/host/chromoting_messages.cc new file mode 100644 index 0000000..36c3cf4 --- /dev/null +++ b/remoting/host/chromoting_messages.cc @@ -0,0 +1,34 @@ +// Copyright (c) 2012 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. + +// Get basic type definitions. +#define IPC_MESSAGE_IMPL +#include "remoting/host/chromoting_messages.h" + +// Generate constructors. +#include "ipc/struct_constructor_macros.h" +#include "remoting/host/chromoting_messages.h" + +// Generate destructors. +#include "ipc/struct_destructor_macros.h" +#include "remoting/host/chromoting_messages.h" + +// Generate param traits write methods. +#include "ipc/param_traits_write_macros.h" +namespace IPC { +#include "remoting/host/chromoting_messages.h" +} // namespace IPC + +// Generate param traits read methods. +#include "ipc/param_traits_read_macros.h" +namespace IPC { +#include "remoting/host/chromoting_messages.h" +} // namespace IPC + +// Generate param traits log methods. +#include "ipc/param_traits_log_macros.h" +namespace IPC { +#include "remoting/host/chromoting_messages.h" +} // namespace IPC + diff --git a/remoting/host/chromoting_messages.h b/remoting/host/chromoting_messages.h new file mode 100644 index 0000000..0ec1339 --- /dev/null +++ b/remoting/host/chromoting_messages.h @@ -0,0 +1,17 @@ +// Copyright (c) 2012 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. + +// Defines IPC messages used by Chromoting components. + +// Multiply-included message file, no traditional include guard. +#include "ipc/ipc_message_macros.h" + +#define IPC_MESSAGE_START ChromotingMsgStart + +//----------------------------------------------------------------------------- +// The Chrmomoting session messages + +// Asks the service to send the Secure Attention Sequence (SAS) to the current +// console session. +IPC_MESSAGE_CONTROL0(ChromotingHostMsg_SendSasToConsole) diff --git a/remoting/host/desktop_environment.cc b/remoting/host/desktop_environment.cc index fbf4ffc..0007c8a 100644 --- a/remoting/host/desktop_environment.cc +++ b/remoting/host/desktop_environment.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -11,31 +11,57 @@ #include "remoting/host/chromoting_host_context.h" #include "remoting/host/event_executor.h" +#if defined(OS_WIN) +#include "remoting/host/session_event_executor_win.h" +#endif + namespace remoting { // static -DesktopEnvironment* DesktopEnvironment::Create(ChromotingHostContext* context) { +scoped_ptr<DesktopEnvironment> DesktopEnvironment::Create( + ChromotingHostContext* context) { scoped_ptr<Capturer> capturer(Capturer::Create()); - scoped_ptr<EventExecutor> event_executor( - EventExecutor::Create(context->desktop_message_loop(), capturer.get())); + scoped_ptr<protocol::InputStub> event_executor = + EventExecutor::Create(context->desktop_message_loop(), + capturer.get()); if (capturer.get() == NULL || event_executor.get() == NULL) { LOG(ERROR) << "Unable to create DesktopEnvironment"; - return NULL; + return scoped_ptr<DesktopEnvironment>(); } - return new DesktopEnvironment(context, - capturer.release(), - event_executor.release()); +#if defined(OS_WIN) + event_executor.reset(new SessionEventExecutorWin( + context->desktop_message_loop(), + context->io_message_loop(), + event_executor.Pass())); +#endif + + return scoped_ptr<DesktopEnvironment>( + new DesktopEnvironment(context, + capturer.Pass(), + event_executor.Pass())); +} + +// static +scoped_ptr<DesktopEnvironment> DesktopEnvironment::CreateFake( + ChromotingHostContext* context, + scoped_ptr<Capturer> capturer, + scoped_ptr<protocol::InputStub> event_executor) { + return scoped_ptr<DesktopEnvironment>( + new DesktopEnvironment(context, + capturer.Pass(), + event_executor.Pass())); } -DesktopEnvironment::DesktopEnvironment(ChromotingHostContext* context, - Capturer* capturer, - EventExecutor* event_executor) +DesktopEnvironment::DesktopEnvironment( + ChromotingHostContext* context, + scoped_ptr<Capturer> capturer, + scoped_ptr<protocol::InputStub> event_executor) : host_(NULL), context_(context), - capturer_(capturer), - event_executor_(event_executor) { + capturer_(capturer.Pass()), + event_executor_(event_executor.Pass()) { } DesktopEnvironment::~DesktopEnvironment() { diff --git a/remoting/host/desktop_environment.h b/remoting/host/desktop_environment.h index f95089b..5573cfc 100644 --- a/remoting/host/desktop_environment.h +++ b/remoting/host/desktop_environment.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -18,24 +18,31 @@ namespace remoting { class Capturer; class ChromotingHost; class ChromotingHostContext; -class EventExecutor; + +namespace protocol { +class InputStub; +}; class DesktopEnvironment { public: - static DesktopEnvironment* Create(ChromotingHostContext* context); + static scoped_ptr<DesktopEnvironment> Create(ChromotingHostContext* context); + static scoped_ptr<DesktopEnvironment> CreateFake( + ChromotingHostContext* context, + scoped_ptr<Capturer> capturer, + scoped_ptr<protocol::InputStub> event_executor); - // DesktopEnvironment takes ownership of all the objects passed in. - DesktopEnvironment(ChromotingHostContext* context, - Capturer* capturer, - EventExecutor* event_executor); virtual ~DesktopEnvironment(); void set_host(ChromotingHost* host) { host_ = host; } Capturer* capturer() const { return capturer_.get(); } - EventExecutor* event_executor() const { return event_executor_.get(); } + protocol::InputStub* event_executor() const { return event_executor_.get(); } private: + DesktopEnvironment(ChromotingHostContext* context, + scoped_ptr<Capturer> capturer, + scoped_ptr<protocol::InputStub> event_executor); + // The host that owns this DesktopEnvironment. ChromotingHost* host_; @@ -47,7 +54,7 @@ class DesktopEnvironment { scoped_ptr<Capturer> capturer_; // Executes input events received from the client. - scoped_ptr<EventExecutor> event_executor_; + scoped_ptr<protocol::InputStub> event_executor_; DISALLOW_COPY_AND_ASSIGN(DesktopEnvironment); }; diff --git a/remoting/host/event_executor.h b/remoting/host/event_executor.h index 6051b36..bcde0ba 100644 --- a/remoting/host/event_executor.h +++ b/remoting/host/event_executor.h @@ -1,10 +1,12 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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_EVENT_EXECUTOR_H_ #define REMOTING_HOST_EVENT_EXECUTOR_H_ +#include "base/memory/scoped_ptr.h" + #include "remoting/protocol/input_stub.h" class MessageLoop; @@ -17,8 +19,8 @@ class EventExecutor : public protocol::InputStub { public: // Creates default event executor for the current platform. // Does not take ownership of |message_loop| or |capturer|. - static EventExecutor* Create(MessageLoop* message_loop, - Capturer* capturer); + static scoped_ptr<protocol::InputStub> Create(MessageLoop* message_loop, + Capturer* capturer); }; } // namespace remoting diff --git a/remoting/host/event_executor_linux.cc b/remoting/host/event_executor_linux.cc index f2e68b8..7295c1a 100644 --- a/remoting/host/event_executor_linux.cc +++ b/remoting/host/event_executor_linux.cc @@ -426,14 +426,14 @@ void EventExecutorLinux::InjectMouseEvent(const MouseEvent& event) { } // namespace -EventExecutor* EventExecutor::Create(MessageLoop* message_loop, - Capturer* capturer) { - EventExecutorLinux* executor = new EventExecutorLinux(message_loop, capturer); +scoped_ptr<protocol::InputStub> EventExecutor::Create(MessageLoop* message_loop, + Capturer* capturer) { + scoped_ptr<EventExecutorLinux> executor( + new EventExecutorLinux(message_loop, capturer)); if (!executor->Init()) { - delete executor; - executor = NULL; + executor.reset(NULL); } - return executor; + return executor.PassAs<protocol::InputStub>(); } } // namespace remoting diff --git a/remoting/host/event_executor_mac.cc b/remoting/host/event_executor_mac.cc index cb65832..6ced472 100644 --- a/remoting/host/event_executor_mac.cc +++ b/remoting/host/event_executor_mac.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -289,9 +289,10 @@ void EventExecutorMac::InjectMouseEvent(const MouseEvent& event) { } // namespace -EventExecutor* EventExecutor::Create(MessageLoop* message_loop, - Capturer* capturer) { - return new EventExecutorMac(message_loop, capturer); +scoped_ptr<protocol::InputStub> EventExecutor::Create(MessageLoop* message_loop, + Capturer* capturer) { + return scoped_ptr<protocol::InputStub>( + new EventExecutorMac(message_loop, capturer)); } } // namespace remoting diff --git a/remoting/host/event_executor_win.cc b/remoting/host/event_executor_win.cc index b1f5842..af66225 100644 --- a/remoting/host/event_executor_win.cc +++ b/remoting/host/event_executor_win.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -167,9 +167,10 @@ void EventExecutorWin::HandleMouse(const MouseEvent& event) { } // namespace -EventExecutor* EventExecutor::Create(MessageLoop* message_loop, - Capturer* capturer) { - return new EventExecutorWin(message_loop, capturer); +scoped_ptr<protocol::InputStub> EventExecutor::Create(MessageLoop* message_loop, + Capturer* capturer) { + return scoped_ptr<protocol::InputStub>( + new EventExecutorWin(message_loop, capturer)); } } // namespace remoting diff --git a/remoting/host/host_mock_objects.cc b/remoting/host/host_mock_objects.cc index 2d34908..5144e7e 100644 --- a/remoting/host/host_mock_objects.cc +++ b/remoting/host/host_mock_objects.cc @@ -51,7 +51,7 @@ LocalInputMonitor* LocalInputMonitor::Create() { } MockChromotingHostContext::MockChromotingHostContext() - : ChromotingHostContext(base::MessageLoopProxy::current()) { + : ChromotingHostContext(NULL, base::MessageLoopProxy::current()) { } MockChromotingHostContext::~MockChromotingHostContext() {} diff --git a/remoting/host/host_mock_objects.h b/remoting/host/host_mock_objects.h index 9136f3f..967613f 100644 --- a/remoting/host/host_mock_objects.h +++ b/remoting/host/host_mock_objects.h @@ -84,6 +84,7 @@ class MockChromotingHostContext : public ChromotingHostContext { MOCK_METHOD0(Start, bool()); MOCK_METHOD0(Stop, void()); MOCK_METHOD0(jingle_thread, JingleThread*()); + MOCK_METHOD0(io_message_loop, base::MessageLoopProxy*()); MOCK_METHOD0(ui_message_loop, base::MessageLoopProxy*()); MOCK_METHOD0(main_message_loop, MessageLoop*()); MOCK_METHOD0(encode_message_loop, MessageLoop*()); diff --git a/remoting/host/host_service_win.cc b/remoting/host/host_service_win.cc index 66bfa09..a404e6e 100644 --- a/remoting/host/host_service_win.cc +++ b/remoting/host/host_service_win.cc @@ -20,6 +20,7 @@ #include "base/message_loop.h" #include "base/path_service.h" #include "base/stringprintf.h" +#include "base/threading/thread.h" #include "base/utf_string_conversions.h" #include "base/win/wrapped_window_proc.h" @@ -45,6 +46,8 @@ const DWORD kServiceStopTimeoutMs = 30 * 1000; // Session id that does not represent any session. const uint32 kInvalidSession = 0xffffffff; +const char kIoThreadName[] = "I/O thread"; + // A window class for the session change notifications window. static const char kSessionNotificationWindowClass[] = "Chromoting_SessionNotificationWindow"; @@ -351,7 +354,16 @@ int HostService::Run() { } void HostService::RunMessageLoop() { - WtsSessionProcessLauncher launcher(this, host_binary_); + // Launch the I/O thread. + base::Thread io_thread(kIoThreadName); + base::Thread::Options io_thread_options(MessageLoop::TYPE_IO, 0); + if (!io_thread.StartWithOptions(io_thread_options)) { + shutting_down_ = true; + stopped_event_.Signal(); + return; + } + + WtsSessionProcessLauncher launcher(this, host_binary_, &io_thread); // Run the service. message_loop_->Run(); diff --git a/remoting/host/plugin/host_script_object.cc b/remoting/host/plugin/host_script_object.cc index d25f89c..817e5a7 100644 --- a/remoting/host/plugin/host_script_object.cc +++ b/remoting/host/plugin/host_script_object.cc @@ -114,7 +114,8 @@ bool HostNPScriptObject::Init() { DCHECK(plugin_message_loop_proxy_->BelongsToCurrentThread()); VLOG(2) << "Init"; - host_context_.reset(new ChromotingHostContext(plugin_message_loop_proxy_)); + host_context_.reset(new ChromotingHostContext(NULL, + plugin_message_loop_proxy_)); if (!host_context_->Start()) { host_context_.reset(); return false; @@ -464,7 +465,7 @@ void HostNPScriptObject::FinishConnectMainThread( // TODO(sergeyu): Fix DesktopEnvironment so that it can be created // on either the UI or the network thread so that we can avoid // jumping to the main thread here. - desktop_environment_.reset(DesktopEnvironment::Create(host_context_.get())); + desktop_environment_ = DesktopEnvironment::Create(host_context_.get()); FinishConnectNetworkThread(uid, auth_token, auth_service); } diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc index d0479c7..0440303 100644 --- a/remoting/host/remoting_me2me_host.cc +++ b/remoting/host/remoting_me2me_host.cc @@ -18,6 +18,7 @@ #include "base/file_path.h" #include "base/file_util.h" #include "base/logging.h" +#include "base/memory/scoped_ptr.h" #include "base/message_loop.h" #include "base/path_service.h" #include "base/threading/thread.h" @@ -85,12 +86,15 @@ class HostProcess { HostProcess() : message_loop_(MessageLoop::TYPE_UI), file_io_thread_("FileIO"), - context_(message_loop_.message_loop_proxy()), allow_nat_traversal_(true), restarting_(false) { - context_.Start(); file_io_thread_.StartWithOptions( base::Thread::Options(MessageLoop::TYPE_IO, 0)); + + context_.reset(new ChromotingHostContext( + file_io_thread_.message_loop_proxy(), + message_loop_.message_loop_proxy())); + context_->Start(); network_change_notifier_.reset(net::NetworkChangeNotifier::Create()); } @@ -187,8 +191,8 @@ class HostProcess { } void OnNatPolicyUpdate(bool nat_traversal_enabled) { - if (!context_.network_message_loop()->BelongsToCurrentThread()) { - context_.network_message_loop()->PostTask(FROM_HERE, base::Bind( + if (!context_->network_message_loop()->BelongsToCurrentThread()) { + context_->network_message_loop()->PostTask(FROM_HERE, base::Bind( &HostProcess::OnNatPolicyUpdate, base::Unretained(this), nat_traversal_enabled)); return; @@ -209,19 +213,19 @@ class HostProcess { } void StartHost() { - DCHECK(context_.network_message_loop()->BelongsToCurrentThread()); + DCHECK(context_->network_message_loop()->BelongsToCurrentThread()); DCHECK(!host_); if (!signal_strategy_.get()) { signal_strategy_.reset( - new XmppSignalStrategy(context_.jingle_thread(), xmpp_login_, + new XmppSignalStrategy(context_->jingle_thread(), xmpp_login_, xmpp_auth_token_, xmpp_auth_service_)); signaling_connector_.reset( new SignalingConnector(signal_strategy_.get())); } if (!desktop_environment_.get()) - desktop_environment_.reset(DesktopEnvironment::Create(&context_)); + desktop_environment_ = DesktopEnvironment::Create(context_.get()); protocol::NetworkSettings network_settings(allow_nat_traversal_); if (!allow_nat_traversal_) { @@ -230,7 +234,7 @@ class HostProcess { } host_ = new ChromotingHost( - &context_, signal_strategy_.get(), desktop_environment_.get(), + context_.get(), signal_strategy_.get(), desktop_environment_.get(), network_settings); heartbeat_sender_.reset( @@ -251,7 +255,7 @@ class HostProcess { } void RestartHost() { - DCHECK(context_.network_message_loop()->BelongsToCurrentThread()); + DCHECK(context_->network_message_loop()->BelongsToCurrentThread()); if (restarting_) return; @@ -262,7 +266,7 @@ class HostProcess { } void RestartOnHostShutdown() { - DCHECK(context_.network_message_loop()->BelongsToCurrentThread()); + DCHECK(context_->network_message_loop()->BelongsToCurrentThread()); restarting_ = false; @@ -276,7 +280,7 @@ class HostProcess { MessageLoop message_loop_; base::Thread file_io_thread_; - ChromotingHostContext context_; + scoped_ptr<ChromotingHostContext> context_; scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_; FilePath auth_config_path_; diff --git a/remoting/host/sas_injector.h b/remoting/host/sas_injector.h new file mode 100644 index 0000000..2e3baaa --- /dev/null +++ b/remoting/host/sas_injector.h @@ -0,0 +1,28 @@ +// Copyright (c) 2012 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_SAS_INJECTOR_H_ +#define REMOTING_HOST_SAS_INJECTOR_H_ + +#include "base/memory/scoped_ptr.h" + +namespace remoting { + +// Provides a way to simulate a Secure Attention Sequence (SAS). The default +// sequence is Ctrl+Alt+Delete. +class SasInjector { + public: + virtual ~SasInjector() {} + + // Sends the Secure Attention Sequence to the console session. + virtual bool InjectSas() = 0; + + // Creates an instance of SasInjector if supported by the OS, otherwise + // returns NULL. + static scoped_ptr<SasInjector> Create(); +}; + +} // namespace remoting + +#endif REMOTING_HOST_SAS_INJECTOR_H_ diff --git a/remoting/host/sas_injector_win.cc b/remoting/host/sas_injector_win.cc new file mode 100644 index 0000000..3dd55f1 --- /dev/null +++ b/remoting/host/sas_injector_win.cc @@ -0,0 +1,186 @@ +// Copyright (c) 2012 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/sas_injector.h" + +#include <windows.h> +#include <string> + +#include "base/logging.h" +#include "base/file_path.h" +#include "base/native_library.h" +#include "base/path_service.h" +#include "base/utf_string_conversions.h" +#include "base/win/registry.h" +#include "base/win/windows_version.h" + +namespace remoting { + +namespace { + +// Names of the API and library implementing software SAS generation. +const FilePath::CharType kSasDllFileName[] = + FILE_PATH_LITERAL("sas.dll"); +const char kSendSasName[] = "SendSAS"; + +// The prototype of SendSAS(). +typedef VOID (WINAPI *SendSasFunc)(BOOL); + +// The registry key and value holding the policy controlling software SAS +// generation. +const char kSystemPolicyKeyName[] = + "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"; +const char kSoftwareSasValueName[] = "SoftwareSASGeneration"; + +const DWORD kEnableSoftwareSasByServices = 1; + +// Toggles the default software SAS generation policy to enable SAS generation +// by services. Non-default policy is not changed. +class ScopedSoftwareSasPolicy { + public: + ScopedSoftwareSasPolicy(); + ~ScopedSoftwareSasPolicy(); + + bool Apply(); + + private: + // The handle of the registry key were SoftwareSASGeneration policy is stored. + base::win::RegKey system_policy_; + + // Name of the registry value holding the policy. + string16 value_name_; + + // True if the policy needs to be restored. + bool restore_policy_; + + DISALLOW_COPY_AND_ASSIGN(ScopedSoftwareSasPolicy); +}; + +ScopedSoftwareSasPolicy::ScopedSoftwareSasPolicy() + : restore_policy_(false) { +} + +ScopedSoftwareSasPolicy::~ScopedSoftwareSasPolicy() { + // Restore the default policy by deleting the value that we have set. + if (restore_policy_) { + LONG result = system_policy_.DeleteValue(value_name_.c_str()); + if (result != ERROR_SUCCESS) { + SetLastError(result); + LOG_GETLASTERROR(ERROR) + << "Failed to restore the software SAS generation policy"; + } + } +} + +bool ScopedSoftwareSasPolicy::Apply() { + // Query the currently set SoftwareSASGeneration policy. + LONG result = system_policy_.Open(HKEY_LOCAL_MACHINE, + ASCIIToUTF16(kSystemPolicyKeyName).c_str(), + KEY_QUERY_VALUE | KEY_SET_VALUE | + KEY_WOW64_64KEY); + if (result != ERROR_SUCCESS) { + SetLastError(result); + LOG_GETLASTERROR(ERROR) << "Failed to open 'HKLM\\" + << kSystemPolicyKeyName << "'"; + return false; + } + + value_name_ = ASCIIToUTF16(kSoftwareSasValueName); + bool custom_policy = system_policy_.HasValue(value_name_.c_str()); + + // Override the default policy (i.e. there is no value in the registry) only. + if (!custom_policy) { + result = system_policy_.WriteValue(value_name_.c_str(), + kEnableSoftwareSasByServices); + if (result != ERROR_SUCCESS) { + SetLastError(result); + LOG_GETLASTERROR(ERROR) + << "Failed to enable software SAS generation by services"; + return false; + } else { + restore_policy_ = true; + } + } + + return true; +} + +} // namespace + +// Sends the Secure Attention Sequence using the SendSAS() function from +// sas.dll. This library is shipped starting from Win7/W2K8 R2 only. However +// Win7 SDK includes a redistributable verion of the same library that works on +// Vista/W2K8. We install the latter along with our binaries. +class SasInjectorWin : public SasInjector { + public: + SasInjectorWin(); + virtual ~SasInjectorWin(); + + // SasInjector implementation. + virtual bool InjectSas() OVERRIDE; + + private: + base::NativeLibrary sas_dll_; + SendSasFunc send_sas_; +}; + +SasInjectorWin::SasInjectorWin() : sas_dll_(NULL), send_sas_(NULL) { +} + +SasInjectorWin::~SasInjectorWin() { + if (sas_dll_ != NULL) { + base::UnloadNativeLibrary(sas_dll_); + } +} + +bool SasInjectorWin::InjectSas() { + // Load sas.dll. The library is expected to be in the same folder as this + // binary. + if (sas_dll_ == NULL) { + FilePath exe_path; + if (!PathService::Get(base::FILE_EXE, &exe_path)) { + LOG(ERROR) << "Failed to get the executable file name."; + return false; + } + + std::string error; + sas_dll_ = base::LoadNativeLibrary( + exe_path.DirName().Append(kSasDllFileName), + &error); + if (sas_dll_ == NULL) { + LOG(ERROR) << "Failed to load '" << kSasDllFileName << "'"; + return false; + } + } + + // Get the pointer to sas!SendSAS(). + if (send_sas_ == NULL) { + send_sas_ = reinterpret_cast<SendSasFunc>( + base::GetFunctionPointerFromNativeLibrary(sas_dll_, kSendSasName)); + if (send_sas_ == NULL) { + LOG(ERROR) << "Failed to retrieve the address of '" << kSendSasName + << "()'"; + return false; + } + } + + // Enable software SAS generation by services and send SAS. SAS can still fail + // if the policy does not allow services to generate software SAS. + ScopedSoftwareSasPolicy enable_sas; + if (!enable_sas.Apply()) + return false; + + (*send_sas_)(FALSE); + return true; +} + +scoped_ptr<SasInjector> SasInjector::Create() { + if (base::win::OSInfo::GetInstance()->version() >= base::win::VERSION_VISTA) { + return scoped_ptr<SasInjector>(new SasInjectorWin()); + } + + return scoped_ptr<SasInjector>(); +} + +} // namespace remoting diff --git a/remoting/host/session_event_executor_win.cc b/remoting/host/session_event_executor_win.cc new file mode 100644 index 0000000..36e5056 --- /dev/null +++ b/remoting/host/session_event_executor_win.cc @@ -0,0 +1,100 @@ +// Copyright (c) 2012 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/session_event_executor_win.h" + +#include <string> + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/compiler_specific.h" +#include "base/message_loop.h" +#include "ipc/ipc_channel.h" +#include "ipc/ipc_channel_proxy.h" +#include "remoting/proto/event.pb.h" +#include "ui/base/keycodes/keyboard_codes.h" + +#include "remoting/host/chromoting_messages.h" + +namespace { + +// The command line switch specifying the name of the session IPC channel. +const char kProcessChannelId[] = "channel"; + +} // namespace + +namespace remoting { + +using protocol::MouseEvent; +using protocol::KeyEvent; + +SessionEventExecutorWin::SessionEventExecutorWin( + MessageLoop* message_loop, + base::MessageLoopProxy* io_message_loop, + scoped_ptr<protocol::InputStub> nested_executor) + : nested_executor_(nested_executor.Pass()), + message_loop_(message_loop), + scroll_pressed_(false) { + std::string channel_name = + CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kProcessChannelId); + + // Connect to the Chromoting IPC channel if the name was passed in the command + // line. + if (!channel_name.empty()) { + chromoting_channel_.reset(new IPC::ChannelProxy( + channel_name, + IPC::Channel::MODE_CLIENT, + this, + io_message_loop)); + } +} + +SessionEventExecutorWin::~SessionEventExecutorWin() { +} + +void SessionEventExecutorWin::InjectKeyEvent(const KeyEvent& event) { + if (MessageLoop::current() != message_loop_) { + message_loop_->PostTask( + FROM_HERE, + base::Bind(&SessionEventExecutorWin::InjectKeyEvent, + base::Unretained(this), event)); + return; + } + + // Poor man's Ctrl+Alt+Delete emulation: a double Scroll Lock is converted to + // the Secure Attention Sequence. + // TODO(alexeypa): replace this with proper SAS handling. + if (chromoting_channel_.get() != NULL && event.keycode() == VK_SCROLL) { + if (event.pressed()) { + if (scroll_pressed_) { + chromoting_channel_->Send(new ChromotingHostMsg_SendSasToConsole()); + scroll_pressed_ = false; + } else { + scroll_pressed_ = true; + } + } + } else { + scroll_pressed_ = false; + } + + nested_executor_->InjectKeyEvent(event); +} + +void SessionEventExecutorWin::InjectMouseEvent(const MouseEvent& event) { + if (MessageLoop::current() != message_loop_) { + message_loop_->PostTask( + FROM_HERE, + base::Bind(&SessionEventExecutorWin::InjectMouseEvent, + base::Unretained(this), event)); + return; + } + + nested_executor_->InjectMouseEvent(event); +} + +bool SessionEventExecutorWin::OnMessageReceived(const IPC::Message& message) { + return false; +} + +} // namespace remoting diff --git a/remoting/host/session_event_executor_win.h b/remoting/host/session_event_executor_win.h new file mode 100644 index 0000000..3e33c44 --- /dev/null +++ b/remoting/host/session_event_executor_win.h @@ -0,0 +1,58 @@ +// Copyright (c) 2012 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_SESSION_EVENT_EXECUTOR_WIN_H_ +#define REMOTING_HOST_SESSION_EVENT_EXECUTOR_WIN_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "ipc/ipc_channel.h" + +#include "remoting/host/event_executor.h" +#include "remoting/protocol/input_stub.h" + +class MessageLoop; + +namespace base { +class MessageLoopProxy; +} // namespace base + +namespace IPC { +class ChannelProxy; +} // namespace IPC + +namespace remoting { + +class SessionEventExecutorWin : public protocol::InputStub, + public IPC::Channel::Listener { + public: + SessionEventExecutorWin(MessageLoop* message_loop, + base::MessageLoopProxy* io_message_loop, + scoped_ptr<protocol::InputStub> nested_executor); + ~SessionEventExecutorWin(); + + // protocol::InputStub implementation. + virtual void InjectKeyEvent(const protocol::KeyEvent& event) OVERRIDE; + virtual void InjectMouseEvent(const protocol::MouseEvent& event) OVERRIDE; + + // IPC::Channel::Listener implementation. + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + + private: + // Pointer to the next event executor. + scoped_ptr<protocol::InputStub> nested_executor_; + + MessageLoop* message_loop_; + + // The Chromoting IPC channel connecting the host with the service. + scoped_ptr<IPC::ChannelProxy> chromoting_channel_; + + bool scroll_pressed_; + + DISALLOW_COPY_AND_ASSIGN(SessionEventExecutorWin); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_SESSION_EVENT_EXECUTOR_WIN_H_ diff --git a/remoting/host/simple_host_process.cc b/remoting/host/simple_host_process.cc index ad454ae..16ed0a1 100644 --- a/remoting/host/simple_host_process.cc +++ b/remoting/host/simple_host_process.cc @@ -94,7 +94,7 @@ class SimpleHost { SimpleHost() : message_loop_(MessageLoop::TYPE_UI), file_io_thread_("FileIO"), - context_(message_loop_.message_loop_proxy()), + context_(NULL, message_loop_.message_loop_proxy()), fake_(false), is_it2me_(false) { context_.Start(); @@ -215,13 +215,14 @@ class SimpleHost { signaling_connector_.reset(new SignalingConnector(signal_strategy_.get())); if (fake_) { - Capturer* capturer = new CapturerFake(); - EventExecutor* event_executor = - EventExecutor::Create(context_.desktop_message_loop(), capturer); - desktop_environment_.reset( - new DesktopEnvironment(&context_, capturer, event_executor)); + scoped_ptr<Capturer> capturer(new CapturerFake()); + scoped_ptr<protocol::InputStub> event_executor = + EventExecutor::Create( + context_.desktop_message_loop(), capturer.get()); + desktop_environment_ = DesktopEnvironment::CreateFake( + &context_, capturer.Pass(), event_executor.Pass()); } else { - desktop_environment_.reset(DesktopEnvironment::Create(&context_)); + desktop_environment_ = DesktopEnvironment::Create(&context_); } host_ = new ChromotingHost(&context_, signal_strategy_.get(), diff --git a/remoting/host/wts_session_process_launcher_win.cc b/remoting/host/wts_session_process_launcher_win.cc index c5e42da..fa8b2b2 100644 --- a/remoting/host/wts_session_process_launcher_win.cc +++ b/remoting/host/wts_session_process_launcher_win.cc @@ -8,11 +8,23 @@ #include "remoting/host/wts_session_process_launcher_win.h" #include <windows.h> +#include <sddl.h> +#include <limits> #include "base/logging.h" +#include "base/process_util.h" +#include "base/rand_util.h" +#include "base/string16.h" +#include "base/stringprintf.h" +#include "base/threading/thread.h" #include "base/utf_string_conversions.h" #include "base/win/scoped_handle.h" +#include "ipc/ipc_channel_proxy.h" +#include "ipc/ipc_message.h" +#include "ipc/ipc_message_macros.h" +#include "remoting/host/chromoting_messages.h" +#include "remoting/host/sas_injector.h" #include "remoting/host/wts_console_monitor_win.h" using base::win::ScopedHandle; @@ -28,6 +40,17 @@ const int kMinLaunchDelaySeconds = 1; // Name of the default session desktop. const char kDefaultDesktopName[] = "winsta0\\default"; +// Match the pipe name prefix used by Chrome IPC channels. +const char kChromePipeNamePrefix[] = "\\\\.\\pipe\\chrome."; + +// Generates the command line of the host process. +const char kHostProcessCommandLineFormat[] = "\"%ls\" --channel=%ls"; + +// The security descriptor of the Chromoting IPC channel. It gives full access +// to LocalSystem and denies access by anyone else. +const char kChromotingChannelSecurityDescriptor[] = + "O:SY" "G:SY" "D:(A;;GA;;;SY)"; + // Takes the process token and makes a copy of it. The returned handle will have // |desired_access| rights. bool CopyProcessToken(DWORD desired_access, @@ -114,11 +137,74 @@ bool CreateSessionToken(uint32 session_id, return true; } +// Generates random channel ID. +// N.B. Stolen from src/content/common/child_process_host_impl.cc +string16 GenerateRandomChannelId(void* instance) { + return base::StringPrintf(ASCIIToUTF16("%d.%p.%d").c_str(), + base::GetCurrentProcId(), instance, + base::RandInt(0, std::numeric_limits<int>::max())); +} + +// Creates the server end of the Chromoting IPC channel. +// N.B. This code is based on IPC::Channel's implementation. +bool CreatePipeForIpcChannel(void* instance, + string16* channel_name_out, + ScopedHandle* pipe_out) { + // Create security descriptor for the channel. + SECURITY_ATTRIBUTES security_attributes; + security_attributes.nLength = sizeof(security_attributes); + security_attributes.bInheritHandle = FALSE; + + ULONG security_descriptor_length = 0; + if (!ConvertStringSecurityDescriptorToSecurityDescriptorA( + kChromotingChannelSecurityDescriptor, + SDDL_REVISION_1, + reinterpret_cast<PSECURITY_DESCRIPTOR*>( + &security_attributes.lpSecurityDescriptor), + &security_descriptor_length)) { + LOG_GETLASTERROR(ERROR) << + "Failed to create a security descriptor for the Chromoting IPC channel"; + return false; + } + + // Generate a random channel name. + string16 channel_name(GenerateRandomChannelId(instance)); + + // Convert it to the pipe name. + string16 pipe_name(ASCIIToUTF16(kChromePipeNamePrefix)); + pipe_name.append(channel_name); + + // Create the server end of the pipe. This code should match the code in + // IPC::Channel with exception of passing a non-default security descriptor. + HANDLE pipe = CreateNamedPipeW(pipe_name.c_str(), + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | + FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, + 1, + IPC::Channel::kReadBufferSize, + IPC::Channel::kReadBufferSize, + 5000, + &security_attributes); + if (pipe == INVALID_HANDLE_VALUE) { + LOG_GETLASTERROR(ERROR) << + "Failed to create the server end of the Chromoting IPC channel"; + LocalFree(security_attributes.lpSecurityDescriptor); + return false; + } + + LocalFree(security_attributes.lpSecurityDescriptor); + + *channel_name_out = channel_name; + pipe_out->Set(pipe); + return true; +} + // Launches |binary| in the security context of the supplied |user_token|. bool LaunchProcessAsUser(const FilePath& binary, + const string16& command_line, HANDLE user_token, base::Process* process_out) { - string16 command_line = binary.value(); + string16 application_name = binary.value(); string16 desktop = ASCIIToUTF16(kDefaultDesktopName); PROCESS_INFORMATION process_info; @@ -129,7 +215,7 @@ bool LaunchProcessAsUser(const FilePath& binary, startup_info.lpDesktop = const_cast<LPWSTR>(desktop.c_str()); if (!CreateProcessAsUserW(user_token, - command_line.c_str(), + application_name.c_str(), const_cast<LPWSTR>(command_line.c_str()), NULL, NULL, @@ -155,8 +241,10 @@ namespace remoting { WtsSessionProcessLauncher::WtsSessionProcessLauncher( WtsConsoleMonitor* monitor, - const FilePath& host_binary) + const FilePath& host_binary, + base::Thread* io_thread) : host_binary_(host_binary), + io_thread_(io_thread), monitor_(monitor), state_(StateDetached) { monitor_->AddWtsConsoleObserver(this); @@ -167,6 +255,7 @@ WtsSessionProcessLauncher::~WtsSessionProcessLauncher() { DCHECK(!timer_.IsRunning()); DCHECK(process_.handle() == NULL); DCHECK(process_watcher_.GetWatchedObject() == NULL); + DCHECK(chromoting_channel_.get() == NULL); monitor_->RemoveWtsConsoleObserver(this); } @@ -176,19 +265,40 @@ void WtsSessionProcessLauncher::LaunchProcess() { DCHECK(!timer_.IsRunning()); DCHECK(process_.handle() == NULL); DCHECK(process_watcher_.GetWatchedObject() == NULL); + DCHECK(chromoting_channel_.get() == NULL); - // Try to launch the process and attach an object watcher to the returned - // handle so that we get notified when the process terminates. launch_time_ = base::Time::Now(); - if (LaunchProcessAsUser(host_binary_, session_token_, &process_)) { - if (process_watcher_.StartWatching(process_.handle(), this)) { - state_ = StateAttached; - return; - } else { - LOG(ERROR) << "Failed to arm the process watcher."; - process_.Terminate(0); - process_.Close(); + + string16 channel_name; + ScopedHandle pipe; + if (CreatePipeForIpcChannel(this, &channel_name, &pipe)) { + // Wrap the pipe into an IPC channel. + chromoting_channel_.reset(new IPC::ChannelProxy( + IPC::ChannelHandle(pipe.Get()), + IPC::Channel::MODE_SERVER, + this, + io_thread_->message_loop_proxy().get())); + + string16 command_line = + base::StringPrintf(ASCIIToUTF16(kHostProcessCommandLineFormat).c_str(), + host_binary_.value().c_str(), + channel_name.c_str()); + + // Try to launch the process and attach an object watcher to the returned + // handle so that we get notified when the process terminates. + if (LaunchProcessAsUser(host_binary_, command_line, session_token_, + &process_)) { + if (process_watcher_.StartWatching(process_.handle(), this)) { + state_ = StateAttached; + return; + } else { + LOG(ERROR) << "Failed to arm the process watcher."; + process_.Terminate(0); + process_.Close(); + } } + + chromoting_channel_.reset(); } // Something went wrong. Try to launch the host again later. The attempts rate @@ -206,10 +316,12 @@ void WtsSessionProcessLauncher::OnObjectSignaled(HANDLE object) { DCHECK(!timer_.IsRunning()); DCHECK(process_.handle() != NULL); DCHECK(process_watcher_.GetWatchedObject() == NULL); + DCHECK(chromoting_channel_.get() != NULL); // The host process has been terminated for some reason. The handle can now be // closed. process_.Close(); + chromoting_channel_.reset(); // Expand the backoff interval if the process has died quickly or reset it if // it was up longer than the maximum backoff delay. @@ -230,11 +342,34 @@ void WtsSessionProcessLauncher::OnObjectSignaled(HANDLE object) { this, &WtsSessionProcessLauncher::LaunchProcess); } +bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(WtsSessionProcessLauncher, message) + IPC_MESSAGE_HANDLER(ChromotingHostMsg_SendSasToConsole, + OnSendSasToConsole) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void WtsSessionProcessLauncher::OnSendSasToConsole() { + if (state_ == StateAttached) { + if (sas_injector_.get() == NULL) { + sas_injector_ = SasInjector::Create(); + } + + if (sas_injector_.get() != NULL) { + sas_injector_->InjectSas(); + } + } +} + void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) { DCHECK(state_ == StateDetached); DCHECK(!timer_.IsRunning()); DCHECK(process_.handle() == NULL); DCHECK(process_watcher_.GetWatchedObject() == NULL); + DCHECK(chromoting_channel_.get() == NULL); // Temporarily enable the SE_TCB_NAME privilege. The privileged token is // created as needed and kept for later reuse. @@ -250,7 +385,7 @@ void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) { return; } - // While the SE_TCB_NAME progolege is enabled, create a session token for + // While the SE_TCB_NAME privilege is enabled, create a session token for // the launched process. bool result = CreateSessionToken(session_id, &session_token_); @@ -276,12 +411,14 @@ void WtsSessionProcessLauncher::OnSessionDetached() { DCHECK(!timer_.IsRunning()); DCHECK(process_.handle() == NULL); DCHECK(process_watcher_.GetWatchedObject() == NULL); + DCHECK(chromoting_channel_.get() == NULL); break; case StateStarting: DCHECK(timer_.IsRunning()); DCHECK(process_.handle() == NULL); DCHECK(process_watcher_.GetWatchedObject() == NULL); + DCHECK(chromoting_channel_.get() == NULL); timer_.Stop(); launch_backoff_ = base::TimeDelta(); @@ -292,10 +429,12 @@ void WtsSessionProcessLauncher::OnSessionDetached() { DCHECK(!timer_.IsRunning()); DCHECK(process_.handle() != NULL); DCHECK(process_watcher_.GetWatchedObject() != NULL); + DCHECK(chromoting_channel_.get() != NULL); process_watcher_.StopWatching(); process_.Terminate(0); process_.Close(); + chromoting_channel_.reset(); state_ = StateDetached; break; } diff --git a/remoting/host/wts_session_process_launcher_win.h b/remoting/host/wts_session_process_launcher_win.h index eaf9c67..efcb93a 100644 --- a/remoting/host/wts_session_process_launcher_win.h +++ b/remoting/host/wts_session_process_launcher_win.h @@ -15,29 +15,49 @@ #include "base/timer.h" #include "base/win/scoped_handle.h" #include "base/win/object_watcher.h" +#include "ipc/ipc_channel.h" #include "remoting/host/wts_console_observer_win.h" +namespace base { + +class Thread; + +} // namespace base + +namespace IPC { + +class ChannelProxy; +class Message; + +} // namespace IPC + namespace remoting { +class SasInjector; class WtsConsoleMonitor; class WtsSessionProcessLauncher : public base::win::ObjectWatcher::Delegate, + public IPC::Channel::Listener, public WtsConsoleObserver { public: - // Constructs a WtsSessionProcessLauncher object. |monitor| must outlive this - // class. |host_binary| is the name of the executable to be launched in - // the console session. + // Constructs a WtsSessionProcessLauncher object. |monitor| and |io_thread| + // must outlive this object. |host_binary| is the name of the executable to + // be launched in the console session. WtsSessionProcessLauncher(WtsConsoleMonitor* monitor, - const FilePath& host_binary); + const FilePath& host_binary, + base::Thread* io_thread); virtual ~WtsSessionProcessLauncher(); - // base::win::ObjectWatcher::Delegate implementation + // base::win::ObjectWatcher::Delegate implementation. virtual void OnObjectSignaled(HANDLE object) OVERRIDE; - // WtsConsoleObserver implementation + // IPC::Channel::Listener implementation. + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + + // WtsConsoleObserver implementation. virtual void OnSessionAttached(uint32 session_id) OVERRIDE; virtual void OnSessionDetached() OVERRIDE; @@ -47,6 +67,10 @@ class WtsSessionProcessLauncher // reason. void LaunchProcess(); + // Sends the Secure Attention Sequence to the session represented by + // |session_token_|. + void OnSendSasToConsole(); + // Name of the host executable. FilePath host_binary_; @@ -59,6 +83,10 @@ class WtsSessionProcessLauncher // Timer used to schedule the next attempt to launch the process. base::OneShotTimer<WtsSessionProcessLauncher> timer_; + // The I/O thread hosts the Chromoting IPC channel and any other code + // requiring an I/O message loop. + base::Thread* io_thread_; + // This pointer is used to unsubscribe from session attach and detach events. WtsConsoleMonitor* monitor_; @@ -84,6 +112,12 @@ class WtsSessionProcessLauncher // Current state of the process launcher. State state_; + // The Chromoting IPC channel connecting the service to the per-session + // process. + scoped_ptr<IPC::ChannelProxy> chromoting_channel_; + + scoped_ptr<SasInjector> sas_injector_; + DISALLOW_COPY_AND_ASSIGN(WtsSessionProcessLauncher); }; diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index 285ff03..14da577 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -234,6 +234,7 @@ '../base/base.gyp:base', '../base/base.gyp:base_i18n', '../media/media.gyp:media', + '../ipc/ipc.gyp:ipc', ], 'sources': [ 'host/host_event_logger_win.cc', @@ -279,13 +280,18 @@ 'dependencies': [ '../base/base.gyp:base', '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', + '../ipc/ipc.gyp:ipc', ], 'sources': [ 'base/scoped_sc_handle_win.h', + 'host/chromoting_messages.cc', + 'host/chromoting_messages.h', 'host/host_service.rc', 'host/host_service_resource.h', 'host/host_service_win.cc', 'host/host_service_win.h', + 'host/sas_injector.h', + 'host/sas_injector_win.cc', 'host/wts_console_monitor_win.h', 'host/wts_console_observer_win.h', 'host/wts_session_process_launcher_win.cc', @@ -404,6 +410,11 @@ }], ], # conditions }], # OS=="mac" + [ 'OS=="win"', { + 'dependencies': [ + '../ipc/ipc.gyp:ipc' + ], + }], ['OS!="win"', { 'sources!': [ 'host/plugin/host_plugin.def', @@ -641,6 +652,8 @@ 'host/screen_recorder.h', 'host/server_log_entry.cc', 'host/server_log_entry.h', + 'host/session_event_executor_win.cc', + 'host/session_event_executor_win.h', 'host/signaling_connector.cc', 'host/signaling_connector.h', 'host/ui_strings.cc', @@ -690,6 +703,12 @@ ], }, }], + ['OS=="win"', { + 'sources': [ + 'host/chromoting_messages.cc', + 'host/chromoting_messages.h', + ], + }], ], }, # end of target 'remoting_host' @@ -750,6 +769,13 @@ 'host/it2me_host_user_interface.h', 'host/simple_host_process.cc', ], + 'conditions': [ + [ 'OS=="win"', { + 'dependencies': [ + '../ipc/ipc.gyp:ipc' + ], + }], + ], }, # end of target 'remoting_simple_host' { @@ -1021,6 +1047,11 @@ 'run_all_unittests.cc', ], 'conditions': [ + [ 'OS=="win"', { + 'dependencies': [ + '../ipc/ipc.gyp:ipc' + ], + }], ['chromeos == 0', { 'dependencies': [ 'remoting_host' |