| OLD | NEW |
| 1 | 1 |
| 2 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 // Use of this source code is governed by a BSD-style license that can be | 3 // Use of this source code is governed by a BSD-style license that can be |
| 4 // found in the LICENSE file. | 4 // found in the LICENSE file. |
| 5 // | 5 // |
| 6 // This file implements the Windows service controlling Me2Me host processes | 6 // This file implements the Windows service controlling Me2Me host processes |
| 7 // running within user sessions. | 7 // running within user sessions. |
| 8 | 8 |
| 9 #include "remoting/host/win/unprivileged_process_delegate.h" | 9 #include "remoting/host/win/unprivileged_process_delegate.h" |
| 10 | 10 |
| 11 #include <sddl.h> | 11 #include <sddl.h> |
| 12 | 12 |
| 13 #include "base/command_line.h" | 13 #include "base/command_line.h" |
| 14 #include "base/logging.h" | 14 #include "base/logging.h" |
| 15 #include "base/process_util.h" | 15 #include "base/process_util.h" |
| 16 #include "base/rand_util.h" | 16 #include "base/rand_util.h" |
| 17 #include "base/single_thread_task_runner.h" | 17 #include "base/single_thread_task_runner.h" |
| 18 #include "base/string16.h" | 18 #include "base/string16.h" |
| 19 #include "base/stringprintf.h" | 19 #include "base/stringprintf.h" |
| 20 #include "base/synchronization/lock.h" | 20 #include "base/synchronization/lock.h" |
| 21 #include "base/utf_string_conversions.h" | 21 #include "base/utf_string_conversions.h" |
| 22 #include "base/win/scoped_handle.h" | 22 #include "base/win/scoped_handle.h" |
| 23 #include "base/win/windows_version.h" | 23 #include "base/win/windows_version.h" |
| 24 #include "ipc/ipc_channel.h" | 24 #include "ipc/ipc_channel.h" |
| 25 #include "ipc/ipc_channel_proxy.h" | 25 #include "ipc/ipc_channel_proxy.h" |
| 26 #include "ipc/ipc_message.h" | 26 #include "ipc/ipc_message.h" |
| 27 #include "remoting/base/typed_buffer.h" | 27 #include "remoting/base/typed_buffer.h" |
| 28 #include "remoting/host/host_exit_codes.h" | |
| 29 #include "remoting/host/ipc_constants.h" | 28 #include "remoting/host/ipc_constants.h" |
| 30 #include "remoting/host/win/launch_process_with_token.h" | 29 #include "remoting/host/win/launch_process_with_token.h" |
| 31 #include "remoting/host/win/security_descriptor.h" | 30 #include "remoting/host/win/security_descriptor.h" |
| 32 #include "remoting/host/win/window_station_and_desktop.h" | 31 #include "remoting/host/win/window_station_and_desktop.h" |
| 33 #include "sandbox/win/src/restricted_token.h" | 32 #include "sandbox/win/src/restricted_token.h" |
| 34 | 33 |
| 35 using base::win::ScopedHandle; | 34 using base::win::ScopedHandle; |
| 36 | 35 |
| 37 namespace remoting { | 36 namespace remoting { |
| 38 | 37 |
| (...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 207 return false; | 206 return false; |
| 208 } | 207 } |
| 209 | 208 |
| 210 handles.Swap(*handles_out); | 209 handles.Swap(*handles_out); |
| 211 return true; | 210 return true; |
| 212 } | 211 } |
| 213 | 212 |
| 214 } // namespace | 213 } // namespace |
| 215 | 214 |
| 216 UnprivilegedProcessDelegate::UnprivilegedProcessDelegate( | 215 UnprivilegedProcessDelegate::UnprivilegedProcessDelegate( |
| 217 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, | |
| 218 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, | 216 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, |
| 219 scoped_ptr<CommandLine> target_command) | 217 scoped_ptr<CommandLine> target_command) |
| 220 : main_task_runner_(main_task_runner), | 218 : io_task_runner_(io_task_runner), |
| 221 io_task_runner_(io_task_runner), | 219 event_handler_(NULL), |
| 222 target_command_(target_command.Pass()) { | 220 target_command_(target_command.Pass()) { |
| 223 } | 221 } |
| 224 | 222 |
| 225 UnprivilegedProcessDelegate::~UnprivilegedProcessDelegate() { | 223 UnprivilegedProcessDelegate::~UnprivilegedProcessDelegate() { |
| 226 KillProcess(CONTROL_C_EXIT); | 224 DCHECK(CalledOnValidThread()); |
| 225 DCHECK(!channel_); |
| 226 DCHECK(!worker_process_.IsValid()); |
| 227 } | 227 } |
| 228 | 228 |
| 229 bool UnprivilegedProcessDelegate::Send(IPC::Message* message) { | 229 void UnprivilegedProcessDelegate::LaunchProcess( |
| 230 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 230 WorkerProcessLauncher* event_handler) { |
| 231 DCHECK(CalledOnValidThread()); |
| 232 DCHECK(!event_handler_); |
| 231 | 233 |
| 232 return channel_->Send(message); | 234 event_handler_ = event_handler; |
| 233 } | |
| 234 | |
| 235 void UnprivilegedProcessDelegate::CloseChannel() { | |
| 236 DCHECK(main_task_runner_->BelongsToCurrentThread()); | |
| 237 | |
| 238 channel_.reset(); | |
| 239 } | |
| 240 | |
| 241 DWORD UnprivilegedProcessDelegate::GetProcessId() const { | |
| 242 if (worker_process_.IsValid()) { | |
| 243 return ::GetProcessId(worker_process_); | |
| 244 } else { | |
| 245 return 0; | |
| 246 } | |
| 247 } | |
| 248 | |
| 249 bool UnprivilegedProcessDelegate::IsPermanentError(int failure_count) const { | |
| 250 // Get exit code of the worker process if it is available. | |
| 251 DWORD exit_code = CONTROL_C_EXIT; | |
| 252 if (worker_process_.IsValid()) { | |
| 253 if (!::GetExitCodeProcess(worker_process_, &exit_code)) { | |
| 254 LOG_GETLASTERROR(INFO) | |
| 255 << "Failed to query the exit code of the worker process"; | |
| 256 exit_code = CONTROL_C_EXIT; | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 // Stop trying to restart the worker process if it exited due to | |
| 261 // misconfiguration. | |
| 262 return (kMinPermanentErrorExitCode <= exit_code && | |
| 263 exit_code <= kMaxPermanentErrorExitCode); | |
| 264 } | |
| 265 | |
| 266 void UnprivilegedProcessDelegate::KillProcess(DWORD exit_code) { | |
| 267 DCHECK(main_task_runner_->BelongsToCurrentThread()); | |
| 268 | |
| 269 channel_.reset(); | |
| 270 | |
| 271 if (worker_process_.IsValid()) { | |
| 272 TerminateProcess(worker_process_, exit_code); | |
| 273 } | |
| 274 } | |
| 275 | |
| 276 bool UnprivilegedProcessDelegate::LaunchProcess( | |
| 277 IPC::Listener* delegate, | |
| 278 ScopedHandle* process_exit_event_out) { | |
| 279 DCHECK(main_task_runner_->BelongsToCurrentThread()); | |
| 280 | 235 |
| 281 scoped_ptr<IPC::ChannelProxy> server; | 236 scoped_ptr<IPC::ChannelProxy> server; |
| 282 | 237 |
| 283 // Generate a unique name for the channel. | 238 // Generate a unique name for the channel. |
| 284 std::string channel_name = IPC::Channel::GenerateUniqueRandomChannelID(); | 239 std::string channel_name = IPC::Channel::GenerateUniqueRandomChannelID(); |
| 285 | 240 |
| 286 // Create a restricted token that will be used to run the worker process. | 241 // Create a restricted token that will be used to run the worker process. |
| 287 ScopedHandle token; | 242 ScopedHandle token; |
| 288 if (!CreateRestrictedToken(&token)) { | 243 if (!CreateRestrictedToken(&token)) { |
| 289 LOG_GETLASTERROR(ERROR) | 244 LOG_GETLASTERROR(ERROR) |
| 290 << "Failed to create a restricted LocalService token"; | 245 << "Failed to create a restricted LocalService token"; |
| 291 return false; | 246 ReportFatalError(); |
| 247 return; |
| 292 } | 248 } |
| 293 | 249 |
| 294 // Determine our logon SID, so we can grant it access to our window station | 250 // Determine our logon SID, so we can grant it access to our window station |
| 295 // and desktop. | 251 // and desktop. |
| 296 ScopedSid logon_sid = GetLogonSid(token); | 252 ScopedSid logon_sid = GetLogonSid(token); |
| 297 if (!logon_sid) { | 253 if (!logon_sid) { |
| 298 LOG_GETLASTERROR(ERROR) << "Failed to retrieve the logon SID"; | 254 LOG_GETLASTERROR(ERROR) << "Failed to retrieve the logon SID"; |
| 299 return false; | 255 ReportFatalError(); |
| 256 return; |
| 300 } | 257 } |
| 301 | 258 |
| 302 // Create the process and thread security descriptors. | 259 // Create the process and thread security descriptors. |
| 303 ScopedSd process_sd = ConvertSddlToSd(kWorkerProcessSd); | 260 ScopedSd process_sd = ConvertSddlToSd(kWorkerProcessSd); |
| 304 ScopedSd thread_sd = ConvertSddlToSd(kWorkerThreadSd); | 261 ScopedSd thread_sd = ConvertSddlToSd(kWorkerThreadSd); |
| 305 if (!process_sd || !thread_sd) { | 262 if (!process_sd || !thread_sd) { |
| 306 LOG_GETLASTERROR(ERROR) << "Failed to create a security descriptor"; | 263 LOG_GETLASTERROR(ERROR) << "Failed to create a security descriptor"; |
| 307 return false; | 264 ReportFatalError(); |
| 265 return; |
| 308 } | 266 } |
| 309 | 267 |
| 310 SECURITY_ATTRIBUTES process_attributes; | 268 SECURITY_ATTRIBUTES process_attributes; |
| 311 process_attributes.nLength = sizeof(process_attributes); | 269 process_attributes.nLength = sizeof(process_attributes); |
| 312 process_attributes.lpSecurityDescriptor = process_sd.get(); | 270 process_attributes.lpSecurityDescriptor = process_sd.get(); |
| 313 process_attributes.bInheritHandle = FALSE; | 271 process_attributes.bInheritHandle = FALSE; |
| 314 | 272 |
| 315 SECURITY_ATTRIBUTES thread_attributes; | 273 SECURITY_ATTRIBUTES thread_attributes; |
| 316 thread_attributes.nLength = sizeof(thread_attributes); | 274 thread_attributes.nLength = sizeof(thread_attributes); |
| 317 thread_attributes.lpSecurityDescriptor = thread_sd.get(); | 275 thread_attributes.lpSecurityDescriptor = thread_sd.get(); |
| 318 thread_attributes.bInheritHandle = FALSE; | 276 thread_attributes.bInheritHandle = FALSE; |
| 319 | 277 |
| 278 ScopedHandle worker_process; |
| 320 { | 279 { |
| 321 // Take a lock why any inheritable handles are open to make sure that only | 280 // Take a lock why any inheritable handles are open to make sure that only |
| 322 // one process inherits them. | 281 // one process inherits them. |
| 323 base::AutoLock lock(g_inherit_handles_lock.Get()); | 282 base::AutoLock lock(g_inherit_handles_lock.Get()); |
| 324 | 283 |
| 325 // Create a connected IPC channel. | 284 // Create a connected IPC channel. |
| 326 ScopedHandle client; | 285 ScopedHandle client; |
| 327 if (!CreateConnectedIpcChannel(channel_name, kDaemonIpcSd, io_task_runner_, | 286 if (!CreateConnectedIpcChannel(channel_name, kDaemonIpcSd, io_task_runner_, |
| 328 delegate, &client, &server)) { | 287 this, &client, &server)) { |
| 329 return false; | 288 ReportFatalError(); |
| 289 return; |
| 330 } | 290 } |
| 331 | 291 |
| 332 // Convert the handle value into a decimal integer. Handle values are 32bit | 292 // Convert the handle value into a decimal integer. Handle values are 32bit |
| 333 // even on 64bit platforms. | 293 // even on 64bit platforms. |
| 334 std::string pipe_handle = base::StringPrintf( | 294 std::string pipe_handle = base::StringPrintf( |
| 335 "%d", reinterpret_cast<ULONG_PTR>(client.Get())); | 295 "%d", reinterpret_cast<ULONG_PTR>(client.Get())); |
| 336 | 296 |
| 337 // Pass the IPC channel via the command line. | 297 // Pass the IPC channel via the command line. |
| 338 CommandLine command_line(target_command_->argv()); | 298 CommandLine command_line(target_command_->argv()); |
| 339 command_line.AppendSwitchASCII(kDaemonPipeSwitchName, pipe_handle); | 299 command_line.AppendSwitchASCII(kDaemonPipeSwitchName, pipe_handle); |
| 340 | 300 |
| 341 // Create our own window station and desktop accessible by |logon_sid|. | 301 // Create our own window station and desktop accessible by |logon_sid|. |
| 342 WindowStationAndDesktop handles; | 302 WindowStationAndDesktop handles; |
| 343 if (!CreateWindowStationAndDesktop(logon_sid.Pass(), &handles)) { | 303 if (!CreateWindowStationAndDesktop(logon_sid.Pass(), &handles)) { |
| 344 LOG_GETLASTERROR(ERROR) | 304 LOG_GETLASTERROR(ERROR) |
| 345 << "Failed to create a window station and desktop"; | 305 << "Failed to create a window station and desktop"; |
| 346 return false; | 306 ReportFatalError(); |
| 307 return; |
| 347 } | 308 } |
| 348 | 309 |
| 349 // Try to launch the worker process. The launched process inherits | 310 // Try to launch the worker process. The launched process inherits |
| 350 // the window station, desktop and pipe handles, created above. | 311 // the window station, desktop and pipe handles, created above. |
| 351 ScopedHandle worker_thread; | 312 ScopedHandle worker_thread; |
| 352 worker_process_.Close(); | |
| 353 if (!LaunchProcessWithToken(command_line.GetProgram(), | 313 if (!LaunchProcessWithToken(command_line.GetProgram(), |
| 354 command_line.GetCommandLineString(), | 314 command_line.GetCommandLineString(), |
| 355 token, | 315 token, |
| 356 &process_attributes, | 316 &process_attributes, |
| 357 &thread_attributes, | 317 &thread_attributes, |
| 358 true, | 318 true, |
| 359 0, | 319 0, |
| 360 NULL, | 320 NULL, |
| 361 &worker_process_, | 321 &worker_process, |
| 362 &worker_thread)) { | 322 &worker_thread)) { |
| 363 return false; | 323 ReportFatalError(); |
| 324 return; |
| 364 } | 325 } |
| 365 } | 326 } |
| 366 | 327 |
| 367 // Return a handle that the caller can wait on to get notified when | 328 channel_ = server.Pass(); |
| 368 // the process terminates. | 329 ReportProcessLaunched(worker_process.Pass()); |
| 369 ScopedHandle process_exit_event; | 330 } |
| 331 |
| 332 void UnprivilegedProcessDelegate::Send(IPC::Message* message) { |
| 333 DCHECK(CalledOnValidThread()); |
| 334 |
| 335 if (channel_) { |
| 336 channel_->Send(message); |
| 337 } else { |
| 338 delete message; |
| 339 } |
| 340 } |
| 341 |
| 342 void UnprivilegedProcessDelegate::CloseChannel() { |
| 343 DCHECK(CalledOnValidThread()); |
| 344 |
| 345 channel_.reset(); |
| 346 } |
| 347 |
| 348 void UnprivilegedProcessDelegate::KillProcess() { |
| 349 DCHECK(CalledOnValidThread()); |
| 350 |
| 351 channel_.reset(); |
| 352 |
| 353 if (worker_process_.IsValid()) { |
| 354 TerminateProcess(worker_process_, CONTROL_C_EXIT); |
| 355 worker_process_.Close(); |
| 356 } |
| 357 } |
| 358 |
| 359 bool UnprivilegedProcessDelegate::OnMessageReceived( |
| 360 const IPC::Message& message) { |
| 361 DCHECK(CalledOnValidThread()); |
| 362 |
| 363 return event_handler_->OnMessageReceived(message); |
| 364 } |
| 365 |
| 366 void UnprivilegedProcessDelegate::OnChannelConnected(int32 peer_pid) { |
| 367 DCHECK(CalledOnValidThread()); |
| 368 |
| 369 DWORD pid = GetProcessId(worker_process_); |
| 370 if (pid != static_cast<DWORD>(peer_pid)) { |
| 371 LOG(ERROR) << "The actual client PID " << pid |
| 372 << " does not match the one reported by the client: " |
| 373 << peer_pid; |
| 374 ReportFatalError(); |
| 375 return; |
| 376 } |
| 377 |
| 378 event_handler_->OnChannelConnected(peer_pid); |
| 379 } |
| 380 |
| 381 void UnprivilegedProcessDelegate::OnChannelError() { |
| 382 DCHECK(CalledOnValidThread()); |
| 383 |
| 384 event_handler_->OnChannelError(); |
| 385 } |
| 386 |
| 387 void UnprivilegedProcessDelegate::ReportFatalError() { |
| 388 DCHECK(CalledOnValidThread()); |
| 389 |
| 390 channel_.reset(); |
| 391 |
| 392 WorkerProcessLauncher* event_handler = event_handler_; |
| 393 event_handler_ = NULL; |
| 394 event_handler->OnFatalError(); |
| 395 } |
| 396 |
| 397 void UnprivilegedProcessDelegate::ReportProcessLaunched( |
| 398 base::win::ScopedHandle worker_process) { |
| 399 DCHECK(CalledOnValidThread()); |
| 400 DCHECK(!worker_process_.IsValid()); |
| 401 |
| 402 worker_process_ = worker_process.Pass(); |
| 403 |
| 404 // Report a handle that can be used to wait for the worker process completion, |
| 405 // query information about the process and duplicate handles. |
| 406 DWORD desired_access = |
| 407 SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION; |
| 408 ScopedHandle limited_handle; |
| 370 if (!DuplicateHandle(GetCurrentProcess(), | 409 if (!DuplicateHandle(GetCurrentProcess(), |
| 371 worker_process_, | 410 worker_process_, |
| 372 GetCurrentProcess(), | 411 GetCurrentProcess(), |
| 373 process_exit_event.Receive(), | 412 limited_handle.Receive(), |
| 374 SYNCHRONIZE, | 413 desired_access, |
| 375 FALSE, | 414 FALSE, |
| 376 0)) { | 415 0)) { |
| 377 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle"; | 416 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle"; |
| 378 KillProcess(CONTROL_C_EXIT); | 417 ReportFatalError(); |
| 379 return false; | 418 return; |
| 380 } | 419 } |
| 381 | 420 |
| 382 channel_ = server.Pass(); | 421 event_handler_->OnProcessLaunched(limited_handle.Pass()); |
| 383 *process_exit_event_out = process_exit_event.Pass(); | |
| 384 return true; | |
| 385 } | 422 } |
| 386 | 423 |
| 387 } // namespace remoting | 424 } // namespace remoting |
| OLD | NEW |