diff options
author | alexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-15 07:28:22 +0000 |
---|---|---|
committer | alexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-15 07:28:22 +0000 |
commit | 5b731f3e83b2c6f6d69c887063ccc33de3db8ada (patch) | |
tree | 38de67e4a3e4c3e7d0afe02b32c54805ad98adf2 /remoting/host/desktop_session_win.cc | |
parent | 1e8e7b9cce00998ce06a9dd3dafe9bd71060482b (diff) | |
download | chromium_src-5b731f3e83b2c6f6d69c887063ccc33de3db8ada.zip chromium_src-5b731f3e83b2c6f6d69c887063ccc33de3db8ada.tar.gz chromium_src-5b731f3e83b2c6f6d69c887063ccc33de3db8ada.tar.bz2 |
Remote RDP sessions, rather than the console, if curtain-mode is configured.
This CL adds a new DesktopSession implementation that starts an RDP terminal and attaches to it. Attaching to the terminal launches the DesktopSessionAgent into the corresponding RDP session, and a new agent will be launched each time the terminal's active session is switched, e.g. if the user logs in to an existing session, in the same way as when remoting the host's physical console.
The terminal will be configured to match client dimensions as reported by the Chromoting host.
BUG=137696
Review URL: https://chromiumcodereview.appspot.com/12544020
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@188283 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/host/desktop_session_win.cc')
-rw-r--r-- | remoting/host/desktop_session_win.cc | 461 |
1 files changed, 416 insertions, 45 deletions
diff --git a/remoting/host/desktop_session_win.cc b/remoting/host/desktop_session_win.cc index fdefc28..ff863a4 100644 --- a/remoting/host/desktop_session_win.cc +++ b/remoting/host/desktop_session_win.cc @@ -4,67 +4,443 @@ #include "remoting/host/desktop_session_win.h" +#include <limits> +#include <sddl.h> + #include "base/base_switches.h" #include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" #include "base/path_service.h" +#include "base/threading/thread_checker.h" +#include "base/timer.h" +#include "base/utf_string_conversions.h" +#include "base/win/scoped_comptr.h" +#include "base/win/scoped_handle.h" #include "ipc/ipc_message_macros.h" +#include "ipc/ipc_platform_file.h" #include "net/base/ip_endpoint.h" #include "remoting/base/auto_thread_task_runner.h" +// MIDL-generated declarations and definitions. +#include "remoting/host/chromoting_lib.h" #include "remoting/host/chromoting_messages.h" #include "remoting/host/daemon_process.h" +#include "remoting/host/desktop_session.h" #include "remoting/host/host_main.h" #include "remoting/host/ipc_constants.h" #include "remoting/host/sas_injector.h" +#include "remoting/host/win/host_service.h" #include "remoting/host/win/worker_process_launcher.h" #include "remoting/host/win/wts_session_process_delegate.h" #include "remoting/host/win/wts_terminal_monitor.h" +#include "remoting/host/win/wts_terminal_observer.h" +#include "remoting/host/worker_process_ipc_delegate.h" using base::win::ScopedHandle; +namespace remoting { + namespace { // The security descriptor of the daemon IPC endpoint. It gives full access // to SYSTEM and denies access by anyone else. -const char kDaemonIpcSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)"; +const wchar_t kDaemonIpcSecurityDescriptor[] = + SDDL_OWNER L":" SDDL_LOCAL_SYSTEM + SDDL_GROUP L":" SDDL_LOCAL_SYSTEM + SDDL_DACL L":(" + SDDL_ACCESS_ALLOWED L";;" SDDL_GENERIC_ALL L";;;" SDDL_LOCAL_SYSTEM + L")"; // The command line parameters that should be copied from the service's command // line to the host process. const char* kCopiedSwitchNames[] = { switches::kV, switches::kVModule }; +// RDC 6.1 (W2K8) supports dimensions of up to 4096x2048. +const int kMaxRdpScreenWidth = 4096; +const int kMaxRdpScreenHeight = 2048; + +// The minimum effective screen dimensions supported by Windows are 800x600. +const int kMinRdpScreenWidth = 800; +const int kMinRdpScreenHeight = 600; + +// Default dots per inch is 96 DPI. +const int kDefaultDpiX = 96; +const int kDefaultDpiY = 96; + +// The session attach notification should arrive within 30 seconds. +const int kSessionAttachTimeoutSeconds = 30; + +// DesktopSession implementation which attaches to the host's physical console. +// Receives IPC messages from the desktop process, running in the console +// session, via |WorkerProcessIpcDelegate|, and monitors console session +// attach/detach events via |WtsConsoleObserer|. +class ConsoleSession : public DesktopSessionWin { + public: + // Same as DesktopSessionWin(). + ConsoleSession( + scoped_refptr<AutoThreadTaskRunner> caller_task_runner, + scoped_refptr<AutoThreadTaskRunner> io_task_runner, + DaemonProcess* daemon_process, + int id, + WtsTerminalMonitor* monitor); + virtual ~ConsoleSession(); + + protected: + // DesktopSessionWin overrides. + virtual void InjectSas() OVERRIDE; + + private: + scoped_ptr<SasInjector> sas_injector_; + + DISALLOW_COPY_AND_ASSIGN(ConsoleSession); +}; + +// DesktopSession implementation which attaches to virtual RDP console. +// Receives IPC messages from the desktop process, running in the console +// session, via |WorkerProcessIpcDelegate|, and monitors console session +// attach/detach events via |WtsConsoleObserer|. +class RdpSession : public DesktopSessionWin { + public: + // Same as DesktopSessionWin(). + RdpSession( + scoped_refptr<AutoThreadTaskRunner> caller_task_runner, + scoped_refptr<AutoThreadTaskRunner> io_task_runner, + DaemonProcess* daemon_process, + int id, + WtsTerminalMonitor* monitor); + virtual ~RdpSession(); + + // Performs the part of initialization that can fail. + bool Initialize(const DesktopSessionParams& params); + + // Mirrors IRdpDesktopSessionEventHandler. + void OnRdpConnected(const net::IPEndPoint& client_endpoint); + void OnRdpClosed(); + + protected: + // DesktopSessionWin overrides. + virtual void InjectSas() OVERRIDE; + + private: + // An implementation of IRdpDesktopSessionEventHandler interface that forwards + // notifications to the owning desktop session. + class EventHandler : public IRdpDesktopSessionEventHandler { + public: + explicit EventHandler(base::WeakPtr<RdpSession> desktop_session); + virtual ~EventHandler(); + + // IUnknown interface. + STDMETHOD_(ULONG, AddRef)() OVERRIDE; + STDMETHOD_(ULONG, Release)() OVERRIDE; + STDMETHOD(QueryInterface)(REFIID riid, void** ppv) OVERRIDE; + + // IRdpDesktopSessionEventHandler interface. + STDMETHOD(OnRdpConnected)(byte* client_endpoint, long length) OVERRIDE; + STDMETHOD(OnRdpClosed)() OVERRIDE; + + private: + ULONG ref_count_; + + // Points to the desktop session object receiving OnRdpXxx() notifications. + base::WeakPtr<RdpSession> desktop_session_; + + // This class must be used on a single thread. + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(EventHandler); + }; + + // Used to create an RDP desktop session. + base::win::ScopedComPtr<IRdpDesktopSession> rdp_desktop_session_; + + base::WeakPtrFactory<RdpSession> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(RdpSession); +}; + +ConsoleSession::ConsoleSession( + scoped_refptr<AutoThreadTaskRunner> caller_task_runner, + scoped_refptr<AutoThreadTaskRunner> io_task_runner, + DaemonProcess* daemon_process, + int id, + WtsTerminalMonitor* monitor) + : DesktopSessionWin(caller_task_runner, io_task_runner, daemon_process, id, + monitor) { + StartMonitoring(net::IPEndPoint()); +} + +ConsoleSession::~ConsoleSession() { +} + +void ConsoleSession::InjectSas() { + if (!sas_injector_) + sas_injector_ = SasInjector::Create(); + if (!sas_injector_->InjectSas()) + LOG(ERROR) << "Failed to inject Secure Attention Sequence."; +} + +RdpSession::RdpSession( + scoped_refptr<AutoThreadTaskRunner> caller_task_runner, + scoped_refptr<AutoThreadTaskRunner> io_task_runner, + DaemonProcess* daemon_process, + int id, + WtsTerminalMonitor* monitor) + : DesktopSessionWin(caller_task_runner, io_task_runner, daemon_process, id, + monitor), + ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { +} + +RdpSession::~RdpSession() { +} + +bool RdpSession::Initialize(const DesktopSessionParams& params) { + DCHECK(caller_task_runner()->BelongsToCurrentThread()); + + // Create the RDP wrapper object. + HRESULT result = rdp_desktop_session_.CreateInstance( + __uuidof(RdpDesktopSession)); + if (FAILED(result)) { + LOG(ERROR) << "Failed to create RdpSession object, 0x" + << std::hex << result << std::dec << "."; + return false; + } + + // DaemonProcess::CreateDesktopSession() varifies that |client_dpi_| and + // |client_size_| are positive. + DCHECK(params.client_dpi_.x() >= 0 && params.client_dpi_.y() >= 0); + DCHECK(params.client_size_.width() >= 0 && params.client_size_.height() >= 0); + + // Handle the default DPI. + SkIPoint client_dpi = params.client_dpi_; + if (!client_dpi.x()) + client_dpi.setX(kDefaultDpiX); + if (!client_dpi.y()) + client_dpi.setY(kDefaultDpiY); + + // Make sure there will be no integer overflow while scaling the client + // resolution. + SkISize client_size = SkISize::Make( + std::min(params.client_size_.width(), + std::numeric_limits<int32_t>::max() / kDefaultDpiX), + std::min(params.client_size_.height(), + std::numeric_limits<int32_t>::max() / kDefaultDpiY)); + + // Scale the client resolution assiming RDP gives us the default 96 DPI. + SkISize host_size = SkISize::Make( + client_size.width() * kDefaultDpiX / client_dpi.x(), + client_size.height() * kDefaultDpiY / client_dpi.y()); + + // Make sure that the host resolution is within the limits supported by RDP. + host_size = SkISize::Make( + std::min(kMaxRdpScreenWidth, + std::max(kMinRdpScreenWidth, host_size.width())), + std::min(kMaxRdpScreenHeight, + std::max(kMinRdpScreenHeight, host_size.height()))); + + // Create an RDP session. + base::win::ScopedComPtr<IRdpDesktopSessionEventHandler> event_handler( + new EventHandler(weak_factory_.GetWeakPtr())); + result = rdp_desktop_session_->Connect(host_size.width(), + host_size.height(), + event_handler); + if (FAILED(result)) { + LOG(ERROR) << "RdpSession::Create() failed, 0x" + << std::hex << result << std::dec << "."; + return false; + } + + return true; +} + +void RdpSession::OnRdpConnected(const net::IPEndPoint& client_endpoint) { + DCHECK(caller_task_runner()->BelongsToCurrentThread()); + + StopMonitoring(); + StartMonitoring(client_endpoint); +} + +void RdpSession::OnRdpClosed() { + DCHECK(caller_task_runner()->BelongsToCurrentThread()); + + OnPermanentError(); +} + +void RdpSession::InjectSas() { + DCHECK(caller_task_runner()->BelongsToCurrentThread()); + + NOTIMPLEMENTED(); +} + +RdpSession::EventHandler::EventHandler( + base::WeakPtr<RdpSession> desktop_session) + : ref_count_(0), + desktop_session_(desktop_session) { +} + +RdpSession::EventHandler::~EventHandler() { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (desktop_session_) + desktop_session_->OnRdpClosed(); +} + +ULONG STDMETHODCALLTYPE RdpSession::EventHandler::AddRef() { + DCHECK(thread_checker_.CalledOnValidThread()); + + return ++ref_count_; +} + +ULONG STDMETHODCALLTYPE RdpSession::EventHandler::Release() { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (--ref_count_ == 0) { + delete this; + return 0; + } + + return ref_count_; +} + +STDMETHODIMP RdpSession::EventHandler::QueryInterface(REFIID riid, void** ppv) { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (riid == IID_IUnknown || + riid == IID_IRdpDesktopSessionEventHandler) { + *ppv = static_cast<IRdpDesktopSessionEventHandler*>(this); + AddRef(); + return S_OK; + } + + *ppv = NULL; + return E_NOINTERFACE; +} + +STDMETHODIMP RdpSession::EventHandler::OnRdpConnected( + byte* client_endpoint, + long length) { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (!desktop_session_) + return S_OK; + + net::IPEndPoint endpoint; + if (!endpoint.FromSockAddr(reinterpret_cast<sockaddr*>(client_endpoint), + length)) { + LOG(ERROR) << "Failed to parse the endpoint passed to OnRdpConnected()."; + OnRdpClosed(); + return S_OK; + } + + desktop_session_->OnRdpConnected(endpoint); + return S_OK; +} + +STDMETHODIMP RdpSession::EventHandler::OnRdpClosed() { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (!desktop_session_) + return S_OK; + + base::WeakPtr<RdpSession> desktop_session = desktop_session_; + desktop_session_.reset(); + desktop_session->OnRdpClosed(); + return S_OK; +} + } // namespace -namespace remoting { +// static +scoped_ptr<DesktopSession> DesktopSessionWin::CreateForConsole( + scoped_refptr<AutoThreadTaskRunner> caller_task_runner, + scoped_refptr<AutoThreadTaskRunner> io_task_runner, + DaemonProcess* daemon_process, + int id, + const DesktopSessionParams& params) { + scoped_ptr<ConsoleSession> session(new ConsoleSession( + caller_task_runner, io_task_runner, daemon_process, id, + HostService::GetInstance())); + + return session.PassAs<DesktopSession>(); +} + +// static +scoped_ptr<DesktopSession> DesktopSessionWin::CreateForVirtualTerminal( + scoped_refptr<AutoThreadTaskRunner> caller_task_runner, + scoped_refptr<AutoThreadTaskRunner> io_task_runner, + DaemonProcess* daemon_process, + int id, + const DesktopSessionParams& params) { + scoped_ptr<RdpSession> session(new RdpSession( + caller_task_runner, io_task_runner, daemon_process, id, + HostService::GetInstance())); + if (!session->Initialize(params)) + return scoped_ptr<DesktopSession>(); + + return session.PassAs<DesktopSession>(); +} DesktopSessionWin::DesktopSessionWin( - scoped_refptr<AutoThreadTaskRunner> main_task_runner, + scoped_refptr<AutoThreadTaskRunner> caller_task_runner, scoped_refptr<AutoThreadTaskRunner> io_task_runner, DaemonProcess* daemon_process, int id, - const DesktopSessionParams& params, - bool virtual_terminal, WtsTerminalMonitor* monitor) : DesktopSession(daemon_process, id), - main_task_runner_(main_task_runner), + caller_task_runner_(caller_task_runner), io_task_runner_(io_task_runner), - monitor_(monitor) { - DCHECK(main_task_runner_->BelongsToCurrentThread()); - - monitor_->AddWtsTerminalObserver(net::IPEndPoint(), this); + monitor_(monitor), + monitoring_notifications_(false) { + DCHECK(caller_task_runner_->BelongsToCurrentThread()); } DesktopSessionWin::~DesktopSessionWin() { - DCHECK(main_task_runner_->BelongsToCurrentThread()); + DCHECK(caller_task_runner_->BelongsToCurrentThread()); - launcher_.reset(); - monitor_->RemoveWtsTerminalObserver(this); + StopMonitoring(); +} + +void DesktopSessionWin::OnSessionAttachTimeout() { + DCHECK(caller_task_runner_->BelongsToCurrentThread()); + + LOG(ERROR) << "Session attach notification didn't arrived within " + << kSessionAttachTimeoutSeconds << " seconds."; + OnPermanentError(); +} + +void DesktopSessionWin::StartMonitoring( + const net::IPEndPoint& client_endpoint) { + DCHECK(caller_task_runner_->BelongsToCurrentThread()); + DCHECK(!monitoring_notifications_); + DCHECK(!session_attach_timer_.IsRunning()); + + session_attach_timer_.Start( + FROM_HERE, base::TimeDelta::FromSeconds(kSessionAttachTimeoutSeconds), + this, &DesktopSessionWin::OnSessionAttachTimeout); + + monitoring_notifications_ = true; + monitor_->AddWtsTerminalObserver(client_endpoint, this); +} + +void DesktopSessionWin::StopMonitoring() { + DCHECK(caller_task_runner_->BelongsToCurrentThread()); + + if (monitoring_notifications_) { + monitoring_notifications_ = false; + monitor_->RemoveWtsTerminalObserver(this); + } + + session_attach_timer_.Stop(); + OnSessionDetached(); } void DesktopSessionWin::OnChannelConnected(int32 peer_pid) { - DCHECK(main_task_runner_->BelongsToCurrentThread()); + DCHECK(caller_task_runner_->BelongsToCurrentThread()); // Obtain the handle of the desktop process. It will be passed to the network - // process so it would be able to duplicate handles of shared memory objects - // from the desktop process. + // process to use to duplicate handles of shared memory objects from + // the desktop process. desktop_process_.Set(OpenProcess(PROCESS_DUP_HANDLE, false, peer_pid)); if (!desktop_process_.IsValid()) { CrashDesktopProcess(FROM_HERE); @@ -75,14 +451,14 @@ void DesktopSessionWin::OnChannelConnected(int32 peer_pid) { } bool DesktopSessionWin::OnMessageReceived(const IPC::Message& message) { - DCHECK(main_task_runner_->BelongsToCurrentThread()); + DCHECK(caller_task_runner_->BelongsToCurrentThread()); bool handled = true; IPC_BEGIN_MESSAGE_MAP(DesktopSessionWin, message) IPC_MESSAGE_HANDLER(ChromotingDesktopDaemonMsg_DesktopAttached, OnDesktopSessionAgentAttached) IPC_MESSAGE_HANDLER(ChromotingDesktopDaemonMsg_InjectSas, - OnInjectSas) + InjectSas) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() @@ -95,47 +471,56 @@ bool DesktopSessionWin::OnMessageReceived(const IPC::Message& message) { } void DesktopSessionWin::OnPermanentError() { - DCHECK(main_task_runner_->BelongsToCurrentThread()); + DCHECK(caller_task_runner_->BelongsToCurrentThread()); - launcher_.reset(); + StopMonitoring(); // This call will delete |this| so it should be at the very end of the method. daemon_process()->CloseDesktopSession(id()); } void DesktopSessionWin::OnSessionAttached(uint32 session_id) { - DCHECK(main_task_runner_->BelongsToCurrentThread()); - DCHECK(launcher_.get() == NULL); + DCHECK(caller_task_runner_->BelongsToCurrentThread()); + DCHECK(!launcher_); + DCHECK(monitoring_notifications_); - // Get the desktop binary name. + // Get the name of the executable the desktop process will run. base::FilePath desktop_binary; if (!GetInstalledBinaryPath(kDesktopBinaryName, &desktop_binary)) { OnPermanentError(); return; } + session_attach_timer_.Stop(); + scoped_ptr<CommandLine> target(new CommandLine(desktop_binary)); target->AppendSwitchASCII(kProcessTypeSwitchName, kProcessTypeDesktop); + // Copy the command line switches enabling verbose logging. target->CopySwitchesFrom(*CommandLine::ForCurrentProcess(), kCopiedSwitchNames, arraysize(kCopiedSwitchNames)); - // Create a delegate to launch a process into the session. + // Create a delegate capable of launching a process in a different session. scoped_ptr<WtsSessionProcessDelegate> delegate( - new WtsSessionProcessDelegate(main_task_runner_, io_task_runner_, - target.Pass(), session_id, - true, kDaemonIpcSecurityDescriptor)); + new WtsSessionProcessDelegate( + caller_task_runner_, io_task_runner_, target.Pass(), session_id, true, + WideToUTF8(kDaemonIpcSecurityDescriptor))); // Create a launcher for the desktop process, using the per-session delegate. launcher_.reset(new WorkerProcessLauncher( - main_task_runner_, delegate.Pass(), this)); + caller_task_runner_, delegate.Pass(), this)); } void DesktopSessionWin::OnSessionDetached() { - DCHECK(main_task_runner_->BelongsToCurrentThread()); - DCHECK(launcher_.get() != NULL); + DCHECK(caller_task_runner_->BelongsToCurrentThread()); launcher_.reset(); + + if (monitoring_notifications_) { + session_attach_timer_.Start( + FROM_HERE, base::TimeDelta::FromSeconds(kSessionAttachTimeoutSeconds), + this, &DesktopSessionWin::OnSessionAttachTimeout); + } } void DesktopSessionWin::OnDesktopSessionAgentAttached( @@ -147,23 +532,9 @@ void DesktopSessionWin::OnDesktopSessionAgentAttached( } } -void DesktopSessionWin::OnInjectSas() { - DCHECK(main_task_runner_->BelongsToCurrentThread()); - - // Do not try to inject SAS if the desktop process is not running. This can - // happen when the session has detached from the console for instance. - if (!launcher_) - return; - - if (!sas_injector_) - sas_injector_ = SasInjector::Create(); - if (!sas_injector_->InjectSas()) - LOG(ERROR) << "Failed to inject Secure Attention Sequence."; -} - void DesktopSessionWin::CrashDesktopProcess( const tracked_objects::Location& location) { - DCHECK(main_task_runner_->BelongsToCurrentThread()); + DCHECK(caller_task_runner_->BelongsToCurrentThread()); launcher_->Crash(location); } |