Index: remoting/host/wts_session_process_launcher_win.cc |
diff --git a/remoting/host/wts_session_process_launcher_win.cc b/remoting/host/wts_session_process_launcher_win.cc |
index c5e42dacceff16e9e07833a4bae75cb937b79d27..fa8b2b284a2528c2a87baf4c8758a23849c743b2 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; |
} |