diff options
Diffstat (limited to 'remoting/host/wts_session_process_launcher_win.cc')
-rw-r--r-- | remoting/host/wts_session_process_launcher_win.cc | 167 |
1 files changed, 153 insertions, 14 deletions
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; } |