| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 // | 4 // |
| 5 // This file implements the Windows service controlling Me2Me host processes | 5 // This file implements the Windows service controlling Me2Me host processes |
| 6 // running within user sessions. | 6 // running within user sessions. |
| 7 | 7 |
| 8 #include "remoting/host/win/wts_session_process_delegate.h" | 8 #include "remoting/host/win/wts_session_process_delegate.h" |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/bind_helpers.h" | |
| 12 #include "base/command_line.h" | 11 #include "base/command_line.h" |
| 13 #include "base/file_util.h" | |
| 14 #include "base/files/file_path.h" | 12 #include "base/files/file_path.h" |
| 15 #include "base/logging.h" | 13 #include "base/logging.h" |
| 16 #include "base/memory/scoped_ptr.h" | |
| 17 #include "base/message_loop.h" | 14 #include "base/message_loop.h" |
| 18 #include "base/single_thread_task_runner.h" | 15 #include "base/single_thread_task_runner.h" |
| 16 #include "base/thread_task_runner_handle.h" |
| 19 #include "base/utf_string_conversions.h" | 17 #include "base/utf_string_conversions.h" |
| 20 #include "base/win/scoped_handle.h" | 18 #include "base/win/scoped_handle.h" |
| 21 #include "base/win/windows_version.h" | 19 #include "base/win/windows_version.h" |
| 22 #include "ipc/ipc_channel.h" | 20 #include "ipc/ipc_channel.h" |
| 23 #include "ipc/ipc_channel_proxy.h" | 21 #include "ipc/ipc_channel_proxy.h" |
| 22 #include "ipc/ipc_listener.h" |
| 24 #include "ipc/ipc_message.h" | 23 #include "ipc/ipc_message.h" |
| 25 #include "remoting/host/host_exit_codes.h" | |
| 26 #include "remoting/host/host_main.h" | 24 #include "remoting/host/host_main.h" |
| 27 #include "remoting/host/ipc_constants.h" | 25 #include "remoting/host/ipc_constants.h" |
| 28 #include "remoting/host/win/launch_process_with_token.h" | 26 #include "remoting/host/win/launch_process_with_token.h" |
| 29 #include "remoting/host/win/worker_process_launcher.h" | 27 #include "remoting/host/win/worker_process_launcher.h" |
| 30 #include "remoting/host/win/wts_terminal_monitor.h" | 28 #include "remoting/host/win/wts_terminal_monitor.h" |
| 31 #include "remoting/host/worker_process_ipc_delegate.h" | 29 #include "remoting/host/worker_process_ipc_delegate.h" |
| 32 | 30 |
| 33 using base::win::ScopedHandle; | 31 using base::win::ScopedHandle; |
| 34 | 32 |
| 35 // Name of the default session desktop. | 33 // Name of the default session desktop. |
| 36 const char kDefaultDesktopName[] = "winsta0\\default"; | 34 const char kDefaultDesktopName[] = "winsta0\\default"; |
| 37 | 35 |
| 38 namespace remoting { | 36 namespace remoting { |
| 39 | 37 |
| 40 // A private class actually implementing the functionality provided by | 38 // A private class actually implementing the functionality provided by |
| 41 // |WtsSessionProcessDelegate|. This class is ref-counted and implements | 39 // |WtsSessionProcessDelegate|. This class is ref-counted and implements |
| 42 // asynchronous fire-and-forget shutdown. | 40 // asynchronous fire-and-forget shutdown. |
| 43 class WtsSessionProcessDelegate::Core | 41 class WtsSessionProcessDelegate::Core |
| 44 : public base::RefCountedThreadSafe<WtsSessionProcessDelegate::Core>, | 42 : public base::RefCountedThreadSafe<Core>, |
| 45 public base::MessagePumpForIO::IOHandler, | 43 public base::MessagePumpForIO::IOHandler, |
| 46 public WorkerProcessLauncher::Delegate { | 44 public IPC::Listener { |
| 47 public: | 45 public: |
| 48 // The caller must ensure that |delegate| remains valid at least until | 46 Core(scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, |
| 49 // Stop() method has been called. | 47 scoped_ptr<CommandLine> target, |
| 50 Core(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, | |
| 51 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, | |
| 52 scoped_ptr<CommandLine> target_command, | |
| 53 bool launch_elevated, | 48 bool launch_elevated, |
| 54 const std::string& channel_security); | 49 const std::string& channel_security); |
| 55 | 50 |
| 56 // base::MessagePumpForIO::IOHandler implementation. | |
| 57 virtual void OnIOCompleted(base::MessagePumpForIO::IOContext* context, | |
| 58 DWORD bytes_transferred, | |
| 59 DWORD error) OVERRIDE; | |
| 60 | |
| 61 // IPC::Sender implementation. | |
| 62 virtual bool Send(IPC::Message* message) OVERRIDE; | |
| 63 | |
| 64 // WorkerProcessLauncher::Delegate implementation. | |
| 65 virtual void CloseChannel() OVERRIDE; | |
| 66 virtual DWORD GetProcessId() const OVERRIDE; | |
| 67 virtual bool IsPermanentError(int failure_count) const OVERRIDE; | |
| 68 virtual void KillProcess(DWORD exit_code) OVERRIDE; | |
| 69 virtual bool LaunchProcess( | |
| 70 IPC::Listener* delegate, | |
| 71 base::win::ScopedHandle* process_exit_event_out) OVERRIDE; | |
| 72 | |
| 73 // Initializes the object returning true on success. | 51 // Initializes the object returning true on success. |
| 74 bool Initialize(uint32 session_id); | 52 bool Initialize(uint32 session_id); |
| 75 | 53 |
| 76 // Stops the object asynchronously. | 54 // Stops the object asynchronously. |
| 77 void Stop(); | 55 void Stop(); |
| 78 | 56 |
| 57 // Mirrors WorkerProcessLauncher::Delegate. |
| 58 void LaunchProcess(WorkerProcessLauncher* event_handler); |
| 59 void Send(IPC::Message* message); |
| 60 void CloseChannel(); |
| 61 void KillProcess(); |
| 62 |
| 79 private: | 63 private: |
| 80 friend class base::RefCountedThreadSafe<Core>; | 64 friend class base::RefCountedThreadSafe<Core>; |
| 81 virtual ~Core(); | 65 virtual ~Core(); |
| 82 | 66 |
| 67 // base::MessagePumpForIO::IOHandler implementation. |
| 68 virtual void OnIOCompleted(base::MessagePumpForIO::IOContext* context, |
| 69 DWORD bytes_transferred, |
| 70 DWORD error) OVERRIDE; |
| 71 |
| 72 // IPC::Listener implementation. |
| 73 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; |
| 74 virtual void OnChannelConnected(int32 peer_pid) OVERRIDE; |
| 75 virtual void OnChannelError() OVERRIDE; |
| 76 |
| 77 // The actual implementation of LaunchProcess() |
| 78 void DoLaunchProcess(); |
| 79 |
| 83 // Drains the completion port queue to make sure that all job object | 80 // Drains the completion port queue to make sure that all job object |
| 84 // notifications have been received. | 81 // notifications have been received. |
| 85 void DrainJobNotifications(); | 82 void DrainJobNotifications(); |
| 86 | 83 |
| 87 // Notified that the completion port queue has been drained. | 84 // Notified that the completion port queue has been drained. |
| 88 void DrainJobNotificationsCompleted(); | 85 void DrainJobNotificationsCompleted(); |
| 89 | 86 |
| 90 // Creates and initializes the job object that will sandbox the launched child | 87 // Creates and initializes the job object that will sandbox the launched child |
| 91 // processes. | 88 // processes. |
| 92 void InitializeJob(); | 89 void InitializeJob(scoped_ptr<base::win::ScopedHandle> job); |
| 93 | 90 |
| 94 // Notified that the job object initialization is complete. | 91 // Notified that the job object initialization is complete. |
| 95 void InitializeJobCompleted(scoped_ptr<base::win::ScopedHandle> job); | 92 void InitializeJobCompleted(scoped_ptr<base::win::ScopedHandle> job); |
| 96 | 93 |
| 97 // Called to process incoming job object notifications. | 94 // Called when the number of processes running in the job reaches zero. |
| 98 void OnJobNotification(DWORD message, DWORD pid); | 95 void OnActiveProcessZero(); |
| 96 |
| 97 void ReportFatalError(); |
| 98 void ReportProcessLaunched(base::win::ScopedHandle worker_process); |
| 99 | 99 |
| 100 // The task runner all public methods of this class should be called on. | 100 // The task runner all public methods of this class should be called on. |
| 101 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; | 101 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_; |
| 102 | 102 |
| 103 // The task runner serving job object notifications. | 103 // The task runner serving job object notifications. |
| 104 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; | 104 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; |
| 105 | 105 |
| 106 // Command line of the launched process. | |
| 107 scoped_ptr<CommandLine> target_command_; | |
| 108 | |
| 109 // The server end of the IPC channel used to communicate to the worker | 106 // The server end of the IPC channel used to communicate to the worker |
| 110 // process. | 107 // process. |
| 111 scoped_ptr<IPC::ChannelProxy> channel_; | 108 scoped_ptr<IPC::ChannelProxy> channel_; |
| 112 | 109 |
| 113 // Security descriptor (as SDDL) to be applied to |channel_|. | 110 // Security descriptor (as SDDL) to be applied to |channel_|. |
| 114 std::string channel_security_; | 111 std::string channel_security_; |
| 115 | 112 |
| 113 WorkerProcessLauncher* event_handler_; |
| 114 |
| 116 // Pointer to GetNamedPipeClientProcessId() API if it is available. | 115 // Pointer to GetNamedPipeClientProcessId() API if it is available. |
| 117 typedef BOOL (WINAPI * GetNamedPipeClientProcessIdFn)(HANDLE, DWORD*); | 116 typedef BOOL (WINAPI * GetNamedPipeClientProcessIdFn)(HANDLE, DWORD*); |
| 118 GetNamedPipeClientProcessIdFn get_named_pipe_client_pid_; | 117 GetNamedPipeClientProcessIdFn get_named_pipe_client_pid_; |
| 119 | 118 |
| 120 // The job object used to control the lifetime of child processes. | 119 // The job object used to control the lifetime of child processes. |
| 121 base::win::ScopedHandle job_; | 120 base::win::ScopedHandle job_; |
| 122 | 121 |
| 123 // True if the worker process should be launched elevated. | 122 // True if the worker process should be launched elevated. |
| 124 bool launch_elevated_; | 123 bool launch_elevated_; |
| 125 | 124 |
| 125 // True if a laucnh attemp is pending. |
| 126 bool launch_pending_; |
| 127 |
| 126 // The named pipe used as the transport by |channel_|. | 128 // The named pipe used as the transport by |channel_|. |
| 127 base::win::ScopedHandle pipe_; | 129 base::win::ScopedHandle pipe_; |
| 128 | 130 |
| 129 // A handle that becomes signalled once all processes associated with the job | |
| 130 // have been terminated. | |
| 131 base::win::ScopedHandle process_exit_event_; | |
| 132 | |
| 133 // The token to be used to launch a process in a different session. | 131 // The token to be used to launch a process in a different session. |
| 134 base::win::ScopedHandle session_token_; | 132 base::win::ScopedHandle session_token_; |
| 135 | 133 |
| 136 // True if Stop() has been called. | 134 // Command line of the launched process. |
| 137 bool stopping_; | 135 scoped_ptr<CommandLine> target_command_; |
| 138 | 136 |
| 139 // The handle of the worker process, if launched. | 137 // The handle of the worker process, if launched. |
| 140 base::win::ScopedHandle worker_process_; | 138 base::win::ScopedHandle worker_process_; |
| 141 | 139 |
| 142 DISALLOW_COPY_AND_ASSIGN(Core); | 140 DISALLOW_COPY_AND_ASSIGN(Core); |
| 143 }; | 141 }; |
| 144 | 142 |
| 145 WtsSessionProcessDelegate::Core::Core( | 143 WtsSessionProcessDelegate::Core::Core( |
| 146 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, | |
| 147 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, | 144 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, |
| 148 scoped_ptr<CommandLine> target_command, | 145 scoped_ptr<CommandLine> target_command, |
| 149 bool launch_elevated, | 146 bool launch_elevated, |
| 150 const std::string& channel_security) | 147 const std::string& channel_security) |
| 151 : main_task_runner_(main_task_runner), | 148 : caller_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| 152 io_task_runner_(io_task_runner), | 149 io_task_runner_(io_task_runner), |
| 153 target_command_(target_command.Pass()), | |
| 154 channel_security_(channel_security), | 150 channel_security_(channel_security), |
| 151 event_handler_(NULL), |
| 155 get_named_pipe_client_pid_(NULL), | 152 get_named_pipe_client_pid_(NULL), |
| 156 launch_elevated_(launch_elevated), | 153 launch_elevated_(launch_elevated), |
| 157 stopping_(false) { | 154 launch_pending_(false), |
| 158 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 155 target_command_(target_command.Pass()) { |
| 156 } |
| 157 |
| 158 bool WtsSessionProcessDelegate::Core::Initialize(uint32 session_id) { |
| 159 DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
| 160 |
| 161 if (base::win::GetVersion() < base::win::VERSION_VISTA) |
| 162 launch_elevated_ = false; |
| 163 |
| 164 if (launch_elevated_) { |
| 165 // GetNamedPipeClientProcessId() is available starting from Vista. |
| 166 HMODULE kernel32 = ::GetModuleHandle(L"kernel32.dll"); |
| 167 CHECK(kernel32 != NULL); |
| 168 |
| 169 get_named_pipe_client_pid_ = |
| 170 reinterpret_cast<GetNamedPipeClientProcessIdFn>( |
| 171 GetProcAddress(kernel32, "GetNamedPipeClientProcessId")); |
| 172 CHECK(get_named_pipe_client_pid_ != NULL); |
| 173 |
| 174 ScopedHandle job; |
| 175 job.Set(CreateJobObject(NULL, NULL)); |
| 176 if (!job.IsValid()) { |
| 177 LOG_GETLASTERROR(ERROR) << "Failed to create a job object"; |
| 178 return false; |
| 179 } |
| 180 |
| 181 // Limit the number of active processes in the job to two (the helper |
| 182 // process performing elevation and the worker process itself) and make sure |
| 183 // that all processes will be killed once the job object is destroyed. |
| 184 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; |
| 185 memset(&info, 0, sizeof(info)); |
| 186 info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_ACTIVE_PROCESS | |
| 187 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; |
| 188 info.BasicLimitInformation.ActiveProcessLimit = 2; |
| 189 if (!SetInformationJobObject(job, |
| 190 JobObjectExtendedLimitInformation, |
| 191 &info, |
| 192 sizeof(info))) { |
| 193 LOG_GETLASTERROR(ERROR) << "Failed to set limits on the job object"; |
| 194 return false; |
| 195 } |
| 196 |
| 197 // ScopedHandle is not compatible with base::Passed, so we wrap it to |
| 198 // a scoped pointer. |
| 199 scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle()); |
| 200 *job_wrapper = job.Pass(); |
| 201 |
| 202 // To receive job object notifications the job object is registered with |
| 203 // the completion port represented by |io_task_runner|. The registration has |
| 204 // to be done on the I/O thread because |
| 205 // MessageLoopForIO::RegisterJobObject() can only be called via |
| 206 // MessageLoopForIO::current(). |
| 207 io_task_runner_->PostTask( |
| 208 FROM_HERE, |
| 209 base::Bind(&Core::InitializeJob, this, base::Passed(&job_wrapper))); |
| 210 } |
| 211 |
| 212 // Create a session token for the launched process. |
| 213 return CreateSessionToken(session_id, &session_token_); |
| 214 } |
| 215 |
| 216 void WtsSessionProcessDelegate::Core::Stop() { |
| 217 DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
| 218 |
| 219 KillProcess(); |
| 220 |
| 221 // Drain the completion queue to make sure all job object notifications have |
| 222 // been received. |
| 223 DrainJobNotificationsCompleted(); |
| 224 } |
| 225 |
| 226 void WtsSessionProcessDelegate::Core::LaunchProcess( |
| 227 WorkerProcessLauncher* event_handler) { |
| 228 DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
| 229 DCHECK(!event_handler_); |
| 230 |
| 231 event_handler_ = event_handler; |
| 232 DoLaunchProcess(); |
| 233 } |
| 234 |
| 235 void WtsSessionProcessDelegate::Core::Send(IPC::Message* message) { |
| 236 DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
| 237 |
| 238 if (channel_) { |
| 239 channel_->Send(message); |
| 240 } else { |
| 241 delete message; |
| 242 } |
| 243 } |
| 244 |
| 245 void WtsSessionProcessDelegate::Core::CloseChannel() { |
| 246 DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
| 247 |
| 248 channel_.reset(); |
| 249 pipe_.Close(); |
| 250 } |
| 251 |
| 252 void WtsSessionProcessDelegate::Core::KillProcess() { |
| 253 DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
| 254 |
| 255 channel_.reset(); |
| 256 event_handler_ = NULL; |
| 257 launch_pending_ = false; |
| 258 pipe_.Close(); |
| 259 |
| 260 if (launch_elevated_) { |
| 261 if (job_.IsValid()) |
| 262 TerminateJobObject(job_, CONTROL_C_EXIT); |
| 263 } else { |
| 264 if (worker_process_.IsValid()) |
| 265 TerminateProcess(worker_process_, CONTROL_C_EXIT); |
| 266 } |
| 267 |
| 268 worker_process_.Close(); |
| 269 } |
| 270 |
| 271 WtsSessionProcessDelegate::Core::~Core() { |
| 272 DCHECK(!channel_); |
| 273 DCHECK(!event_handler_); |
| 274 DCHECK(!pipe_.IsValid()); |
| 275 DCHECK(!worker_process_.IsValid()); |
| 159 } | 276 } |
| 160 | 277 |
| 161 void WtsSessionProcessDelegate::Core::OnIOCompleted( | 278 void WtsSessionProcessDelegate::Core::OnIOCompleted( |
| 162 base::MessagePumpForIO::IOContext* context, | 279 base::MessagePumpForIO::IOContext* context, |
| 163 DWORD bytes_transferred, | 280 DWORD bytes_transferred, |
| 164 DWORD error) { | 281 DWORD error) { |
| 165 DCHECK(io_task_runner_->BelongsToCurrentThread()); | 282 DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| 166 | 283 |
| 167 // |bytes_transferred| is used in job object notifications to supply | 284 // |bytes_transferred| is used in job object notifications to supply |
| 168 // the message ID; |context| carries process ID. | 285 // the message ID; |context| carries process ID. |
| 169 main_task_runner_->PostTask(FROM_HERE, base::Bind( | 286 if (bytes_transferred == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO) { |
| 170 &Core::OnJobNotification, this, bytes_transferred, | 287 caller_task_runner_->PostTask(FROM_HERE, |
| 171 reinterpret_cast<DWORD>(context))); | 288 base::Bind(&Core::OnActiveProcessZero, this)); |
| 172 } | 289 } |
| 173 | 290 } |
| 174 bool WtsSessionProcessDelegate::Core::Send(IPC::Message* message) { | 291 |
| 175 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 292 bool WtsSessionProcessDelegate::Core::OnMessageReceived( |
| 176 | 293 const IPC::Message& message) { |
| 177 if (channel_) { | 294 DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
| 178 return channel_->Send(message); | 295 |
| 179 } else { | 296 return event_handler_->OnMessageReceived(message); |
| 180 delete message; | 297 } |
| 181 return false; | 298 |
| 182 } | 299 void WtsSessionProcessDelegate::Core::OnChannelConnected(int32 peer_pid) { |
| 183 } | 300 DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
| 184 | 301 |
| 185 void WtsSessionProcessDelegate::Core::CloseChannel() { | 302 // Report the worker PID now if the worker process is launched indirectly. |
| 186 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 303 // Note that in this case the pipe's security descriptor is the only |
| 187 | 304 // protection against a malicious processed connecting to the pipe. |
| 188 channel_.reset(); | 305 if (launch_elevated_) { |
| 189 pipe_.Close(); | 306 DWORD pid; |
| 190 } | 307 if (!get_named_pipe_client_pid_(pipe_, &pid)) { |
| 191 | 308 LOG_GETLASTERROR(ERROR) << "Failed to retrive PID of the client"; |
| 192 DWORD WtsSessionProcessDelegate::Core::GetProcessId() const { | 309 ReportFatalError(); |
| 193 DWORD pid = 0; | 310 return; |
| 194 if (launch_elevated_ && pipe_.IsValid() && | 311 } |
| 195 get_named_pipe_client_pid_(pipe_, &pid)) { | 312 |
| 196 return pid; | 313 if (pid != static_cast<DWORD>(peer_pid)) { |
| 197 } | 314 LOG(ERROR) << "The actual client PID " << pid |
| 198 | 315 << " does not match the one reported by the client: " |
| 199 if (worker_process_.IsValid()) | 316 << peer_pid; |
| 200 return ::GetProcessId(worker_process_); | 317 ReportFatalError(); |
| 201 | 318 return; |
| 202 return 0; | 319 } |
| 203 } | 320 |
| 204 | 321 DWORD desired_access = |
| 205 bool WtsSessionProcessDelegate::Core::IsPermanentError( | 322 SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION; |
| 206 int failure_count) const { | 323 ScopedHandle worker_process(OpenProcess(desired_access, false, pid)); |
| 207 // Get exit code of the worker process if it is available. | 324 if (!worker_process.IsValid()) { |
| 208 DWORD exit_code = CONTROL_C_EXIT; | 325 LOG_GETLASTERROR(ERROR) << "Failed to open process " << pid; |
| 209 if (worker_process_.IsValid()) { | 326 ReportFatalError(); |
| 210 if (!::GetExitCodeProcess(worker_process_, &exit_code)) { | 327 return; |
| 211 LOG_GETLASTERROR(INFO) | 328 } |
| 212 << "Failed to query the exit code of the worker process"; | 329 |
| 213 exit_code = CONTROL_C_EXIT; | 330 ReportProcessLaunched(worker_process.Pass()); |
| 214 } | 331 } |
| 215 } | 332 |
| 216 | 333 if (event_handler_) |
| 217 // Stop trying to restart the worker process if it exited due to | 334 event_handler_->OnChannelConnected(peer_pid); |
| 218 // misconfiguration. | 335 } |
| 219 return (kMinPermanentErrorExitCode <= exit_code && | 336 |
| 220 exit_code <= kMaxPermanentErrorExitCode); | 337 void WtsSessionProcessDelegate::Core::OnChannelError() { |
| 221 } | 338 DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
| 222 | 339 |
| 223 void WtsSessionProcessDelegate::Core::KillProcess(DWORD exit_code) { | 340 event_handler_->OnChannelError(); |
| 224 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 341 } |
| 225 | 342 |
| 226 channel_.reset(); | 343 void WtsSessionProcessDelegate::Core::DoLaunchProcess() { |
| 227 pipe_.Close(); | 344 DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
| 228 | 345 DCHECK(!channel_); |
| 229 if (launch_elevated_) { | 346 DCHECK(!pipe_.IsValid()); |
| 230 if (job_.IsValid()) { | 347 DCHECK(!worker_process_.IsValid()); |
| 231 TerminateJobObject(job_, exit_code); | |
| 232 } | |
| 233 } else { | |
| 234 if (worker_process_.IsValid()) { | |
| 235 TerminateProcess(worker_process_, exit_code); | |
| 236 } | |
| 237 } | |
| 238 } | |
| 239 | |
| 240 bool WtsSessionProcessDelegate::Core::LaunchProcess( | |
| 241 IPC::Listener* delegate, | |
| 242 ScopedHandle* process_exit_event_out) { | |
| 243 DCHECK(main_task_runner_->BelongsToCurrentThread()); | |
| 244 | 348 |
| 245 CommandLine command_line(target_command_->argv()); | 349 CommandLine command_line(target_command_->argv()); |
| 246 if (launch_elevated_) { | 350 if (launch_elevated_) { |
| 247 // The job object is not ready. Retry starting the host process later. | 351 // The job object is not ready. Retry starting the host process later. |
| 248 if (!job_.IsValid()) { | 352 if (!job_.IsValid()) { |
| 249 LOG(ERROR) << "The job object is not ready yet."; | 353 launch_pending_ = true; |
| 250 return false; | 354 return; |
| 251 } | 355 } |
| 252 | 356 |
| 253 // Construct the helper binary name. | 357 // Construct the helper binary name. |
| 254 base::FilePath helper_binary; | 358 base::FilePath helper_binary; |
| 255 if (!GetInstalledBinaryPath(kHostBinaryName, &helper_binary)) | 359 if (!GetInstalledBinaryPath(kHostBinaryName, &helper_binary)) { |
| 256 return false; | 360 ReportFatalError(); |
| 361 return; |
| 362 } |
| 257 | 363 |
| 258 // Create the command line passing the name of the IPC channel to use and | 364 // Create the command line passing the name of the IPC channel to use and |
| 259 // copying known switches from the caller's command line. | 365 // copying known switches from the caller's command line. |
| 260 command_line.SetProgram(helper_binary); | 366 command_line.SetProgram(helper_binary); |
| 261 command_line.AppendSwitchPath(kElevateSwitchName, | 367 command_line.AppendSwitchPath(kElevateSwitchName, |
| 262 target_command_->GetProgram()); | 368 target_command_->GetProgram()); |
| 263 | |
| 264 CHECK(ResetEvent(process_exit_event_)); | |
| 265 } | 369 } |
| 266 | 370 |
| 267 // Create the server end of the IPC channel. | 371 // Create the server end of the IPC channel. |
| 268 std::string channel_name = IPC::Channel::GenerateUniqueRandomChannelID(); | 372 std::string channel_name = IPC::Channel::GenerateUniqueRandomChannelID(); |
| 269 ScopedHandle pipe; | 373 ScopedHandle pipe; |
| 270 if (!CreateIpcChannel(channel_name, channel_security_, &pipe)) | 374 if (!CreateIpcChannel(channel_name, channel_security_, &pipe)) { |
| 271 return false; | 375 ReportFatalError(); |
| 376 return; |
| 377 } |
| 272 | 378 |
| 273 // Wrap the pipe into an IPC channel. | 379 // Wrap the pipe into an IPC channel. |
| 274 scoped_ptr<IPC::ChannelProxy> channel(new IPC::ChannelProxy( | 380 scoped_ptr<IPC::ChannelProxy> channel(new IPC::ChannelProxy( |
| 275 IPC::ChannelHandle(pipe), | 381 IPC::ChannelHandle(pipe), |
| 276 IPC::Channel::MODE_SERVER, | 382 IPC::Channel::MODE_SERVER, |
| 277 delegate, | 383 this, |
| 278 io_task_runner_)); | 384 io_task_runner_)); |
| 279 | 385 |
| 280 // Pass the name of the IPC channel to use. | 386 // Pass the name of the IPC channel to use. |
| 281 command_line.AppendSwitchNative(kDaemonPipeSwitchName, | 387 command_line.AppendSwitchNative(kDaemonPipeSwitchName, |
| 282 UTF8ToWide(channel_name)); | 388 UTF8ToWide(channel_name)); |
| 283 | 389 |
| 284 // Try to launch the process. | 390 // Try to launch the process. |
| 285 ScopedHandle worker_process; | 391 ScopedHandle worker_process; |
| 286 ScopedHandle worker_thread; | 392 ScopedHandle worker_thread; |
| 287 if (!LaunchProcessWithToken(command_line.GetProgram(), | 393 if (!LaunchProcessWithToken(command_line.GetProgram(), |
| 288 command_line.GetCommandLineString(), | 394 command_line.GetCommandLineString(), |
| 289 session_token_, | 395 session_token_, |
| 290 NULL, | 396 NULL, |
| 291 NULL, | 397 NULL, |
| 292 false, | 398 false, |
| 293 CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB, | 399 CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB, |
| 294 UTF8ToUTF16(kDefaultDesktopName).c_str(), | 400 UTF8ToUTF16(kDefaultDesktopName).c_str(), |
| 295 &worker_process, | 401 &worker_process, |
| 296 &worker_thread)) { | 402 &worker_thread)) { |
| 297 return false; | 403 ReportFatalError(); |
| 404 return; |
| 298 } | 405 } |
| 299 | 406 |
| 300 HANDLE local_process_exit_event; | |
| 301 if (launch_elevated_) { | 407 if (launch_elevated_) { |
| 302 if (!AssignProcessToJobObject(job_, worker_process)) { | 408 if (!AssignProcessToJobObject(job_, worker_process)) { |
| 303 LOG_GETLASTERROR(ERROR) | 409 LOG_GETLASTERROR(ERROR) |
| 304 << "Failed to assign the worker to the job object"; | 410 << "Failed to assign the worker to the job object"; |
| 305 TerminateProcess(worker_process, CONTROL_C_EXIT); | 411 ReportFatalError(); |
| 306 return false; | 412 return; |
| 307 } | 413 } |
| 308 | |
| 309 local_process_exit_event = process_exit_event_; | |
| 310 } else { | |
| 311 worker_process_ = worker_process.Pass(); | |
| 312 local_process_exit_event = worker_process_; | |
| 313 } | 414 } |
| 314 | 415 |
| 315 if (!ResumeThread(worker_thread)) { | 416 if (!ResumeThread(worker_thread)) { |
| 316 LOG_GETLASTERROR(ERROR) << "Failed to resume the worker thread"; | 417 LOG_GETLASTERROR(ERROR) << "Failed to resume the worker thread"; |
| 317 KillProcess(CONTROL_C_EXIT); | 418 ReportFatalError(); |
| 318 return false; | 419 return; |
| 319 } | |
| 320 | |
| 321 // Return a handle that the caller can wait on to get notified when | |
| 322 // the process terminates. | |
| 323 ScopedHandle process_exit_event; | |
| 324 if (!DuplicateHandle(GetCurrentProcess(), | |
| 325 local_process_exit_event, | |
| 326 GetCurrentProcess(), | |
| 327 process_exit_event.Receive(), | |
| 328 SYNCHRONIZE, | |
| 329 FALSE, | |
| 330 0)) { | |
| 331 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle"; | |
| 332 KillProcess(CONTROL_C_EXIT); | |
| 333 return false; | |
| 334 } | 420 } |
| 335 | 421 |
| 336 channel_ = channel.Pass(); | 422 channel_ = channel.Pass(); |
| 337 pipe_ = pipe.Pass(); | 423 pipe_ = pipe.Pass(); |
| 338 *process_exit_event_out = process_exit_event.Pass(); | |
| 339 return true; | |
| 340 } | |
| 341 | 424 |
| 342 bool WtsSessionProcessDelegate::Core::Initialize(uint32 session_id) { | 425 // Report success if the worker process is lauched directly. Otherwise, PID of |
| 343 if (base::win::GetVersion() < base::win::VERSION_VISTA) | 426 // the client connected to the pipe will be used later. See |
| 344 launch_elevated_ = false; | 427 // OnChannelConnected(). |
| 345 | 428 if (!launch_elevated_) |
| 346 if (launch_elevated_) { | 429 ReportProcessLaunched(worker_process.Pass()); |
| 347 // GetNamedPipeClientProcessId() is available starting from Vista. | |
| 348 HMODULE kernel32 = ::GetModuleHandle(L"kernel32.dll"); | |
| 349 CHECK(kernel32 != NULL); | |
| 350 | |
| 351 get_named_pipe_client_pid_ = | |
| 352 reinterpret_cast<GetNamedPipeClientProcessIdFn>( | |
| 353 GetProcAddress(kernel32, "GetNamedPipeClientProcessId")); | |
| 354 CHECK(get_named_pipe_client_pid_ != NULL); | |
| 355 | |
| 356 process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL)); | |
| 357 if (!process_exit_event_.IsValid()) { | |
| 358 LOG(ERROR) << "Failed to create a nameless event"; | |
| 359 return false; | |
| 360 } | |
| 361 | |
| 362 // To receive job object notifications the job object is registered with | |
| 363 // the completion port represented by |io_task_runner|. The registration has | |
| 364 // to be done on the I/O thread because | |
| 365 // MessageLoopForIO::RegisterJobObject() can only be called via | |
| 366 // MessageLoopForIO::current(). | |
| 367 io_task_runner_->PostTask(FROM_HERE, | |
| 368 base::Bind(&Core::InitializeJob, this)); | |
| 369 } | |
| 370 | |
| 371 // Create a session token for the launched process. | |
| 372 return CreateSessionToken(session_id, &session_token_); | |
| 373 } | |
| 374 | |
| 375 void WtsSessionProcessDelegate::Core::Stop() { | |
| 376 DCHECK(main_task_runner_->BelongsToCurrentThread()); | |
| 377 | |
| 378 if (!stopping_) { | |
| 379 stopping_ = true; | |
| 380 | |
| 381 // Drain the completion queue to make sure all job object notifications have | |
| 382 // been received. | |
| 383 DrainJobNotificationsCompleted(); | |
| 384 } | |
| 385 } | |
| 386 | |
| 387 WtsSessionProcessDelegate::Core::~Core() { | |
| 388 } | 430 } |
| 389 | 431 |
| 390 void WtsSessionProcessDelegate::Core::DrainJobNotifications() { | 432 void WtsSessionProcessDelegate::Core::DrainJobNotifications() { |
| 391 DCHECK(io_task_runner_->BelongsToCurrentThread()); | 433 DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| 392 | 434 |
| 393 // DrainJobNotifications() is posted after the job object is destroyed, so | 435 // DrainJobNotifications() is posted after the job object is destroyed, so |
| 394 // by this time all notifications from the job object have been processed | 436 // by this time all notifications from the job object have been processed |
| 395 // already. Let the main thread know that the queue has been drained. | 437 // already. Let the main thread know that the queue has been drained. |
| 396 main_task_runner_->PostTask(FROM_HERE, base::Bind( | 438 caller_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 397 &Core::DrainJobNotificationsCompleted, this)); | 439 &Core::DrainJobNotificationsCompleted, this)); |
| 398 } | 440 } |
| 399 | 441 |
| 400 void WtsSessionProcessDelegate::Core::DrainJobNotificationsCompleted() { | 442 void WtsSessionProcessDelegate::Core::DrainJobNotificationsCompleted() { |
| 401 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 443 DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
| 402 | 444 |
| 403 if (job_.IsValid()) { | 445 if (job_.IsValid()) { |
| 404 job_.Close(); | 446 job_.Close(); |
| 405 | 447 |
| 406 // Drain the completion queue to make sure all job object notification have | 448 // Drain the completion queue to make sure all job object notification have |
| 407 // been received. | 449 // been received. |
| 408 io_task_runner_->PostTask(FROM_HERE, base::Bind( | 450 io_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 409 &Core::DrainJobNotifications, this)); | 451 &Core::DrainJobNotifications, this)); |
| 410 } | 452 } |
| 411 } | 453 } |
| 412 | 454 |
| 413 void WtsSessionProcessDelegate::Core::InitializeJob() { | 455 void WtsSessionProcessDelegate::Core::InitializeJob( |
| 456 scoped_ptr<base::win::ScopedHandle> job) { |
| 414 DCHECK(io_task_runner_->BelongsToCurrentThread()); | 457 DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| 415 | 458 |
| 416 ScopedHandle job; | |
| 417 job.Set(CreateJobObject(NULL, NULL)); | |
| 418 if (!job.IsValid()) { | |
| 419 LOG_GETLASTERROR(ERROR) << "Failed to create a job object"; | |
| 420 return; | |
| 421 } | |
| 422 | |
| 423 // Limit the number of active processes in the job to two (the process | |
| 424 // performing elevation and the host) and make sure that all processes will be | |
| 425 // killed once the job object is destroyed. | |
| 426 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; | |
| 427 memset(&info, 0, sizeof(info)); | |
| 428 info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_ACTIVE_PROCESS | | |
| 429 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; | |
| 430 info.BasicLimitInformation.ActiveProcessLimit = 2; | |
| 431 if (!SetInformationJobObject(job, | |
| 432 JobObjectExtendedLimitInformation, | |
| 433 &info, | |
| 434 sizeof(info))) { | |
| 435 LOG_GETLASTERROR(ERROR) << "Failed to set limits on the job object"; | |
| 436 return; | |
| 437 } | |
| 438 | |
| 439 // Register to receive job notifications via the I/O thread's completion port. | 459 // Register to receive job notifications via the I/O thread's completion port. |
| 440 if (!base::MessageLoopForIO::current()->RegisterJobObject(job, this)) { | 460 if (!MessageLoopForIO::current()->RegisterJobObject(job->Get(), this)) { |
| 441 LOG_GETLASTERROR(ERROR) | 461 LOG_GETLASTERROR(ERROR) |
| 442 << "Failed to associate the job object with a completion port"; | 462 << "Failed to associate the job object with a completion port"; |
| 443 return; | 463 return; |
| 444 } | 464 } |
| 445 | 465 |
| 446 // ScopedHandle is not compatible with base::Passed, so we wrap it to a scoped | |
| 447 // pointer. | |
| 448 scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle()); | |
| 449 *job_wrapper = job.Pass(); | |
| 450 | |
| 451 // Let the main thread know that initialization is complete. | 466 // Let the main thread know that initialization is complete. |
| 452 main_task_runner_->PostTask(FROM_HERE, base::Bind( | 467 caller_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 453 &Core::InitializeJobCompleted, this, base::Passed(&job_wrapper))); | 468 &Core::InitializeJobCompleted, this, base::Passed(&job))); |
| 454 } | 469 } |
| 455 | 470 |
| 456 void WtsSessionProcessDelegate::Core::InitializeJobCompleted( | 471 void WtsSessionProcessDelegate::Core::InitializeJobCompleted( |
| 457 scoped_ptr<ScopedHandle> job) { | 472 scoped_ptr<ScopedHandle> job) { |
| 458 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 473 DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
| 459 DCHECK(!job_.IsValid()); | 474 DCHECK(!job_.IsValid()); |
| 460 | 475 |
| 461 job_ = job->Pass(); | 476 job_ = job->Pass(); |
| 477 |
| 478 if (launch_pending_) |
| 479 DoLaunchProcess(); |
| 462 } | 480 } |
| 463 | 481 |
| 464 void WtsSessionProcessDelegate::Core::OnJobNotification(DWORD message, | 482 void WtsSessionProcessDelegate::Core::OnActiveProcessZero() { |
| 465 DWORD pid) { | 483 DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
| 466 DCHECK(main_task_runner_->BelongsToCurrentThread()); | |
| 467 | 484 |
| 468 switch (message) { | 485 if (launch_pending_) { |
| 469 case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: | 486 LOG(ERROR) << "The worker process exited before connecting via IPC."; |
| 470 CHECK(SetEvent(process_exit_event_)); | 487 launch_pending_ = false; |
| 471 break; | 488 ReportFatalError(); |
| 472 | |
| 473 case JOB_OBJECT_MSG_NEW_PROCESS: | |
| 474 // We report the exit code of the worker process to be |CONTROL_C_EXIT| | |
| 475 // if we cannot get the actual exit code. So here we can safely ignore | |
| 476 // the error returned by OpenProcess(). | |
| 477 worker_process_.Set(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)); | |
| 478 break; | |
| 479 } | 489 } |
| 480 } | 490 } |
| 481 | 491 |
| 492 void WtsSessionProcessDelegate::Core::ReportFatalError() { |
| 493 DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
| 494 |
| 495 channel_.reset(); |
| 496 pipe_.Close(); |
| 497 |
| 498 WorkerProcessLauncher* event_handler = event_handler_; |
| 499 event_handler_ = NULL; |
| 500 event_handler->OnFatalError(); |
| 501 } |
| 502 |
| 503 void WtsSessionProcessDelegate::Core::ReportProcessLaunched( |
| 504 base::win::ScopedHandle worker_process) { |
| 505 DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
| 506 DCHECK(!worker_process_.IsValid()); |
| 507 |
| 508 worker_process_ = worker_process.Pass(); |
| 509 |
| 510 // Report a handle that can be used to wait for the worker process completion, |
| 511 // query information about the process and duplicate handles. |
| 512 DWORD desired_access = |
| 513 SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION; |
| 514 ScopedHandle limited_handle; |
| 515 if (!DuplicateHandle(GetCurrentProcess(), |
| 516 worker_process_, |
| 517 GetCurrentProcess(), |
| 518 limited_handle.Receive(), |
| 519 desired_access, |
| 520 FALSE, |
| 521 0)) { |
| 522 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle"; |
| 523 ReportFatalError(); |
| 524 return; |
| 525 } |
| 526 |
| 527 event_handler_->OnProcessLaunched(limited_handle.Pass()); |
| 528 } |
| 529 |
| 482 WtsSessionProcessDelegate::WtsSessionProcessDelegate( | 530 WtsSessionProcessDelegate::WtsSessionProcessDelegate( |
| 483 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, | |
| 484 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, | 531 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, |
| 485 scoped_ptr<CommandLine> target_command, | 532 scoped_ptr<CommandLine> target_command, |
| 486 uint32 session_id, | |
| 487 bool launch_elevated, | 533 bool launch_elevated, |
| 488 const std::string& channel_security) { | 534 const std::string& channel_security) { |
| 489 core_ = new Core(main_task_runner, io_task_runner, target_command.Pass(), | 535 core_ = new Core(io_task_runner, |
| 490 launch_elevated, channel_security); | 536 target_command.Pass(), |
| 491 if (!core_->Initialize(session_id)) { | 537 launch_elevated, |
| 492 core_->Stop(); | 538 channel_security); |
| 493 core_ = NULL; | |
| 494 } | |
| 495 } | 539 } |
| 496 | 540 |
| 497 WtsSessionProcessDelegate::~WtsSessionProcessDelegate() { | 541 WtsSessionProcessDelegate::~WtsSessionProcessDelegate() { |
| 498 if (core_) { | 542 core_->Stop(); |
| 499 core_->Stop(); | |
| 500 core_ = NULL; | |
| 501 } | |
| 502 } | 543 } |
| 503 | 544 |
| 504 bool WtsSessionProcessDelegate::Send(IPC::Message* message) { | 545 bool WtsSessionProcessDelegate::Initialize(uint32 session_id) { |
| 505 return core_->Send(message); | 546 return core_->Initialize(session_id); |
| 547 } |
| 548 |
| 549 void WtsSessionProcessDelegate::LaunchProcess( |
| 550 WorkerProcessLauncher* event_handler) { |
| 551 core_->LaunchProcess(event_handler); |
| 552 } |
| 553 |
| 554 void WtsSessionProcessDelegate::Send(IPC::Message* message) { |
| 555 core_->Send(message); |
| 506 } | 556 } |
| 507 | 557 |
| 508 void WtsSessionProcessDelegate::CloseChannel() { | 558 void WtsSessionProcessDelegate::CloseChannel() { |
| 509 if (core_) | 559 core_->CloseChannel(); |
| 510 core_->CloseChannel(); | |
| 511 } | 560 } |
| 512 | 561 |
| 513 DWORD WtsSessionProcessDelegate::GetProcessId() const { | 562 void WtsSessionProcessDelegate::KillProcess() { |
| 514 if (!core_) | 563 core_->KillProcess(); |
| 515 return 0; | |
| 516 | |
| 517 return core_->GetProcessId(); | |
| 518 } | |
| 519 | |
| 520 bool WtsSessionProcessDelegate::IsPermanentError(int failure_count) const { | |
| 521 if (!core_) | |
| 522 return false; | |
| 523 | |
| 524 return core_->IsPermanentError(failure_count); | |
| 525 } | |
| 526 | |
| 527 void WtsSessionProcessDelegate::KillProcess(DWORD exit_code) { | |
| 528 if (core_) | |
| 529 core_->KillProcess(exit_code); | |
| 530 } | |
| 531 | |
| 532 bool WtsSessionProcessDelegate::LaunchProcess( | |
| 533 IPC::Listener* delegate, | |
| 534 base::win::ScopedHandle* process_exit_event_out) { | |
| 535 if (!core_) | |
| 536 return false; | |
| 537 | |
| 538 return core_->LaunchProcess(delegate, process_exit_event_out); | |
| 539 } | 564 } |
| 540 | 565 |
| 541 } // namespace remoting | 566 } // namespace remoting |
| OLD | NEW |