Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(679)

Side by Side Diff: remoting/host/win/wts_session_process_launcher.cc

Issue 10831271: [Chromoting] Adding uiAccess='true' to the remoting_me2me_host.exe's manifest as it is required to … (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: CR feedback. Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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_launcher.h" 8 #include "remoting/host/win/wts_session_process_launcher.h"
9 9
10 #include <windows.h> 10 #include <windows.h>
(...skipping 29 matching lines...) Expand all
40 namespace { 40 namespace {
41 41
42 // The minimum and maximum delays between attempts to inject host process into 42 // The minimum and maximum delays between attempts to inject host process into
43 // a session. 43 // a session.
44 const int kMaxLaunchDelaySeconds = 60; 44 const int kMaxLaunchDelaySeconds = 60;
45 const int kMinLaunchDelaySeconds = 1; 45 const int kMinLaunchDelaySeconds = 1;
46 46
47 const FilePath::CharType kMe2meHostBinaryName[] = 47 const FilePath::CharType kMe2meHostBinaryName[] =
48 FILE_PATH_LITERAL("remoting_me2me_host.exe"); 48 FILE_PATH_LITERAL("remoting_me2me_host.exe");
49 49
50 const FilePath::CharType kMe2meServiceBinaryName[] =
51 FILE_PATH_LITERAL("remoting_service.exe");
52
50 // The IPC channel name is passed to the host in the command line. 53 // The IPC channel name is passed to the host in the command line.
51 const char kChromotingIpcSwitchName[] = "chromoting-ipc"; 54 const char kChromotingIpcSwitchName[] = "chromoting-ipc";
52 55
56 const char kElevateSwitchName[] = "elevate";
57
53 // The command line parameters that should be copied from the service's command 58 // The command line parameters that should be copied from the service's command
54 // line to the host process. 59 // line to the host process.
55 const char* kCopiedSwitchNames[] = { 60 const char* kCopiedSwitchNames[] = {
56 "auth-config", "host-config", switches::kV, switches::kVModule }; 61 "auth-config", "host-config", switches::kV, switches::kVModule };
57 62
58 // The security descriptor of the Chromoting IPC channel. It gives full access 63 // The security descriptor of the Chromoting IPC channel. It gives full access
59 // to LocalSystem and denies access by anyone else. 64 // to LocalSystem and denies access by anyone else.
60 const char kChromotingChannelSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)"; 65 const char kChromotingChannelSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)";
61 66
62 } // namespace 67 } // namespace
63 68
64 namespace remoting { 69 namespace remoting {
65 70
66 WtsSessionProcessLauncher::WtsSessionProcessLauncher( 71 WtsSessionProcessLauncher::WtsSessionProcessLauncher(
67 const base::Closure& stopped_callback, 72 const base::Closure& stopped_callback,
68 WtsConsoleMonitor* monitor, 73 WtsConsoleMonitor* monitor,
69 scoped_refptr<base::SingleThreadTaskRunner> main_message_loop, 74 scoped_refptr<base::SingleThreadTaskRunner> main_message_loop,
70 scoped_refptr<base::SingleThreadTaskRunner> ipc_message_loop) 75 scoped_refptr<base::SingleThreadTaskRunner> ipc_message_loop)
71 : Stoppable(main_message_loop, stopped_callback), 76 : Stoppable(main_message_loop, stopped_callback),
72 attached_(false), 77 attached_(false),
73 main_message_loop_(main_message_loop), 78 main_message_loop_(main_message_loop),
74 ipc_message_loop_(ipc_message_loop), 79 ipc_message_loop_(ipc_message_loop),
75 monitor_(monitor) { 80 monitor_(monitor),
81 sandbox_state_(kSandboxUninitialized) {
76 monitor_->AddWtsConsoleObserver(this); 82 monitor_->AddWtsConsoleObserver(this);
83
84 process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL));
85 CHECK(process_exit_event_.IsValid());
86
87 // Sandbox initalization has to be performed on the I/O thread.
88 ipc_message_loop_->PostTask(FROM_HERE, base::Bind(
89 &WtsSessionProcessLauncher::InitializeSandbox,
90 base::Unretained(this)));
77 } 91 }
78 92
79 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() { 93 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() {
80 monitor_->RemoveWtsConsoleObserver(this); 94 monitor_->RemoveWtsConsoleObserver(this);
81 95
82 DCHECK(!attached_); 96 DCHECK(!attached_);
83 DCHECK(!timer_.IsRunning()); 97 DCHECK(!timer_.IsRunning());
84 } 98 }
85 99
86 void WtsSessionProcessLauncher::LaunchProcess() { 100 void WtsSessionProcessLauncher::LaunchProcess() {
87 DCHECK(main_message_loop_->BelongsToCurrentThread()); 101 DCHECK(main_message_loop_->BelongsToCurrentThread());
88 DCHECK(attached_); 102 DCHECK(attached_);
89 DCHECK(launcher_.get() == NULL); 103 DCHECK(launcher_.get() == NULL);
90 DCHECK(!timer_.IsRunning()); 104 DCHECK(!timer_.IsRunning());
91 DCHECK(!worker_process_.IsValid()); 105 DCHECK(!worker_process_.IsValid());
92 106
93 launch_time_ = base::Time::Now(); 107 launch_time_ = base::Time::Now();
94 launcher_.reset(new WorkerProcessLauncher( 108 launcher_.reset(new WorkerProcessLauncher(
95 this, 109 this,
96 base::Bind(&WtsSessionProcessLauncher::OnLauncherStopped, 110 base::Bind(&WtsSessionProcessLauncher::OnLauncherStopped,
97 base::Unretained(this)), 111 base::Unretained(this)),
98 main_message_loop_, 112 main_message_loop_,
99 ipc_message_loop_)); 113 ipc_message_loop_));
100 launcher_->Start(kChromotingChannelSecurityDescriptor); 114 launcher_->Start(kChromotingChannelSecurityDescriptor);
101 } 115 }
102 116
103 void WtsSessionProcessLauncher::OnLauncherStopped() { 117 void WtsSessionProcessLauncher::OnLauncherStopped() {
104 DCHECK(main_message_loop_->BelongsToCurrentThread()); 118 DCHECK(main_message_loop_->BelongsToCurrentThread());
105 119
106 DWORD exit_code; 120 DWORD exit_code = CONTROL_C_EXIT;
107 if (!::GetExitCodeProcess(worker_process_, &exit_code)) { 121 if (worker_process_.IsValid()) {
108 LOG_GETLASTERROR(INFO) 122 if (!::GetExitCodeProcess(worker_process_, &exit_code)) {
109 << "Failed to query the exit code of the worker process"; 123 LOG_GETLASTERROR(INFO)
110 exit_code = CONTROL_C_EXIT; 124 << "Failed to query the exit code of the worker process";
125 exit_code = CONTROL_C_EXIT;
126 }
127
128 worker_process_.Close();
111 } 129 }
112 130
113 launcher_.reset(NULL); 131 launcher_.reset(NULL);
114 worker_process_.Close();
115 132
116 // Do not relaunch the worker process if the caller has asked us to stop. 133 // Do not relaunch the worker process if the caller has asked us to stop.
117 if (stoppable_state() != Stoppable::kRunning) { 134 if (stoppable_state() != Stoppable::kRunning) {
118 CompleteStopping(); 135 Stop();
119 return; 136 return;
120 } 137 }
121 138
122 // Stop trying to restart the worker process if its process exited due to 139 // Stop trying to restart the worker process if its process exited due to
123 // misconfiguration. 140 // misconfiguration.
124 if (kMinPermanentErrorExitCode <= exit_code && 141 if (kMinPermanentErrorExitCode <= exit_code &&
125 exit_code <= kMaxPermanentErrorExitCode) { 142 exit_code <= kMaxPermanentErrorExitCode) {
126 Stop(); 143 Stop();
127 return; 144 return;
128 } 145 }
(...skipping 12 matching lines...) Expand all
141 launch_backoff_ = std::min( 158 launch_backoff_ = std::min(
142 launch_backoff_, TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)); 159 launch_backoff_, TimeDelta::FromSeconds(kMaxLaunchDelaySeconds));
143 } 160 }
144 161
145 // Try to launch the worker process. 162 // Try to launch the worker process.
146 timer_.Start(FROM_HERE, launch_backoff_, 163 timer_.Start(FROM_HERE, launch_backoff_,
147 this, &WtsSessionProcessLauncher::LaunchProcess); 164 this, &WtsSessionProcessLauncher::LaunchProcess);
148 } 165 }
149 } 166 }
150 167
168 void WtsSessionProcessLauncher::OnIOCompleted(
169 base::MessagePumpForIO::IOContext* context,
170 DWORD bytes_transfered,
171 DWORD error) {
172 DCHECK(ipc_message_loop_->BelongsToCurrentThread());
173
174 // |bytes_transfered| is repurposed by the job object notifications to be
175 // the message ID.
176 BOOL result;
177 switch (bytes_transfered) {
178 case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
179 result = SetEvent(process_exit_event_);
180 CHECK(result);
181 break;
182 }
183 }
184
151 bool WtsSessionProcessLauncher::DoLaunchProcess( 185 bool WtsSessionProcessLauncher::DoLaunchProcess(
152 const std::string& channel_name, 186 const std::string& channel_name,
153 ScopedHandle* process_exit_event_out) { 187 ScopedHandle* process_exit_event_out) {
154 DCHECK(main_message_loop_->BelongsToCurrentThread()); 188 DCHECK(main_message_loop_->BelongsToCurrentThread());
155 DCHECK(!worker_process_.IsValid()); 189 DCHECK(!worker_process_.IsValid());
156 190
191 // Wait until the sandbox is ready.
192 if (!job_.IsValid()) {
193 return false;
194 }
195
157 // Construct the host binary name. 196 // Construct the host binary name.
158 FilePath dir_path; 197 FilePath dir_path;
159 if (!PathService::Get(base::DIR_EXE, &dir_path)) { 198 if (!PathService::Get(base::DIR_EXE, &dir_path)) {
160 LOG(ERROR) << "Failed to get the executable file name."; 199 LOG(ERROR) << "Failed to get the executable file name.";
161 return false; 200 return false;
162 } 201 }
163 FilePath host_binary = dir_path.Append(kMe2meHostBinaryName); 202 FilePath host_binary = dir_path.Append(kMe2meHostBinaryName);
203 FilePath service_binary = dir_path.Append(kMe2meServiceBinaryName);
164 204
165 // Create the host process command line passing the name of the IPC channel 205 // Create the host process command line passing the name of the IPC channel
166 // to use and copying known switches from the service's command line. 206 // to use and copying known switches from the service's command line.
167 CommandLine command_line(host_binary); 207 CommandLine command_line(service_binary);
208 command_line.AppendSwitchPath(kElevateSwitchName, host_binary);
168 command_line.AppendSwitchNative(kChromotingIpcSwitchName, 209 command_line.AppendSwitchNative(kChromotingIpcSwitchName,
169 UTF8ToWide(channel_name)); 210 UTF8ToWide(channel_name));
170 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(), 211 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
171 kCopiedSwitchNames, 212 kCopiedSwitchNames,
172 _countof(kCopiedSwitchNames)); 213 _countof(kCopiedSwitchNames));
173 214
215 BOOL result = ResetEvent(process_exit_event_);
216 CHECK(result);
217
174 // Try to launch the process and attach an object watcher to the returned 218 // Try to launch the process and attach an object watcher to the returned
175 // handle so that we get notified when the process terminates. 219 // handle so that we get notified when the process terminates.
176 if (!LaunchProcessWithToken(host_binary, 220 base::win::ScopedHandle worker_process;
221 base::win::ScopedHandle worker_thread;
222 if (!LaunchProcessWithToken(service_binary,
177 command_line.GetCommandLineString(), 223 command_line.GetCommandLineString(),
178 session_token_, 224 session_token_,
179 &worker_process_)) { 225 CREATE_SUSPENDED,
226 &worker_process,
227 &worker_thread)) {
228 return false;
229 }
230
231 if (!AssignProcessToJobObject(job_, worker_process)) {
232 LOG_GETLASTERROR(ERROR) << "Failed to assign the worker to the job object";
233 TerminateProcess(worker_process, CONTROL_C_EXIT);
234 return false;
235 }
236
237 if (!ResumeThread(worker_thread)) {
238 LOG_GETLASTERROR(ERROR) << "Failed to resume the worker thread";
239 DoKillProcess(CONTROL_C_EXIT);
180 return false; 240 return false;
181 } 241 }
182 242
183 ScopedHandle process_exit_event; 243 ScopedHandle process_exit_event;
184 if (!DuplicateHandle(GetCurrentProcess(), 244 if (!DuplicateHandle(GetCurrentProcess(),
185 worker_process_, 245 process_exit_event_,
186 GetCurrentProcess(), 246 GetCurrentProcess(),
187 process_exit_event.Receive(), 247 process_exit_event.Receive(),
188 SYNCHRONIZE, 248 SYNCHRONIZE,
189 FALSE, 249 FALSE,
190 0)) { 250 0)) {
191 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle"; 251 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
192 DoKillProcess(CONTROL_C_EXIT); 252 DoKillProcess(CONTROL_C_EXIT);
193 return false; 253 return false;
194 } 254 }
195 255
196 *process_exit_event_out = process_exit_event.Pass(); 256 *process_exit_event_out = process_exit_event.Pass();
197 return true; 257 return true;
198 } 258 }
199 259
200 void WtsSessionProcessLauncher::DoKillProcess(DWORD exit_code) { 260 void WtsSessionProcessLauncher::DoKillProcess(DWORD exit_code) {
201 DCHECK(main_message_loop_->BelongsToCurrentThread()); 261 DCHECK(main_message_loop_->BelongsToCurrentThread());
202 262
203 if (worker_process_.IsValid()) { 263 if (job_.IsValid()) {
204 TerminateProcess(worker_process_, exit_code); 264 TerminateJobObject(job_, exit_code);
205 } 265 }
206 } 266 }
207 267
208 void WtsSessionProcessLauncher::OnChannelConnected(DWORD peer_pid) { 268 void WtsSessionProcessLauncher::OnChannelConnected(DWORD peer_pid) {
209 DCHECK(main_message_loop_->BelongsToCurrentThread()); 269 DCHECK(main_message_loop_->BelongsToCurrentThread());
210 270
211 DWORD expected_pid = GetProcessId(worker_process_); 271 // Nothing to validate. Only processes running as SYSTEM can connect to
212 if (peer_pid != expected_pid) { 272 // the pipe, so if it was a malicious process the game would be over already.
213 LOG(ERROR) 273
214 << "Unexpected client connected: expected=" << expected_pid 274 // Get a handle to the peer process so we could query its exit code later.
215 << ", actual=" << peer_pid; 275 // Ignore the error. If the processes cannot be open the error code is generic
216 Stop(); 276 // |CONTROL_C_EXIT|.
217 } 277 worker_process_.Set(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, peer_pid));
218 } 278 }
219 279
220 bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) { 280 bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) {
221 DCHECK(main_message_loop_->BelongsToCurrentThread()); 281 DCHECK(main_message_loop_->BelongsToCurrentThread());
222 282
223 bool handled = true; 283 bool handled = true;
224 IPC_BEGIN_MESSAGE_MAP(WtsSessionProcessLauncher, message) 284 IPC_BEGIN_MESSAGE_MAP(WtsSessionProcessLauncher, message)
225 IPC_MESSAGE_HANDLER(ChromotingHostMsg_SendSasToConsole, 285 IPC_MESSAGE_HANDLER(ChromotingHostMsg_SendSasToConsole,
226 OnSendSasToConsole) 286 OnSendSasToConsole)
227 IPC_MESSAGE_UNHANDLED(handled = false) 287 IPC_MESSAGE_UNHANDLED(handled = false)
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
277 } 337 }
278 } 338 }
279 339
280 void WtsSessionProcessLauncher::DoStop() { 340 void WtsSessionProcessLauncher::DoStop() {
281 DCHECK(main_message_loop_->BelongsToCurrentThread()); 341 DCHECK(main_message_loop_->BelongsToCurrentThread());
282 342
283 if (attached_) { 343 if (attached_) {
284 OnSessionDetached(); 344 OnSessionDetached();
285 } 345 }
286 346
287 if (launcher_.get() == NULL) { 347 job_.Close();
288 CompleteStopping(); 348
349 // Drain the completion queue to make sure all job object notification have
350 // been received.
351 if (sandbox_state_ > kSandboxUninitialized) {
352 DCHECK_EQ(sandbox_state_, kSandboxRunning);
353 sandbox_state_ = kSandboxStopping;
354 ipc_message_loop_->PostTask(FROM_HERE, base::Bind(
355 &WtsSessionProcessLauncher::DrainSandboxNotifications,
356 base::Unretained(this)));
357 }
358
359 // Wait for |launcher_| to be completely stopped.
360 if (launcher_.get() != NULL) {
361 return;
362 }
363
364 // Wait for the completion queue to be drained.
365 if (sandbox_state_ != kSandboxUninitialized &&
366 sandbox_state_ != kSandboxStopped) {
367 return;
368 }
369
370 CompleteStopping();
371 }
372
373 void WtsSessionProcessLauncher::DrainSandboxNotifications() {
374 DCHECK(ipc_message_loop_->BelongsToCurrentThread());
375
376 // DrainSandboxNotifications() is posted after the job object is destroyed, so
377 // by this time all notifications from the job object have been processed
378 // already. Let the main thread know that the queue has been drained.
379 main_message_loop_->PostTask(FROM_HERE, base::Bind(
380 &WtsSessionProcessLauncher::DrainSandboxNotificationsCompleted,
381 base::Unretained(this)));
382 }
383
384 void WtsSessionProcessLauncher::DrainSandboxNotificationsCompleted() {
385 DCHECK(main_message_loop_->BelongsToCurrentThread());
386 DCHECK_EQ(sandbox_state_, kSandboxStopping);
387
388 sandbox_state_ = kSandboxStopped;
389 Stop();
390 }
391
392 void WtsSessionProcessLauncher::InitializeSandbox() {
393 DCHECK(ipc_message_loop_->BelongsToCurrentThread());
394
395 ScopedHandle job;
396 job.Set(CreateJobObject(NULL, NULL));
397 if (!job.IsValid()) {
398 LOG_GETLASTERROR(ERROR) << "Failed to create a job object";
399 return;
400 }
401
402 // Limit number of active processes in the job to two (the process performing
403 // elevation and the host) and make sure that all processes will be killed
404 // once the job object is destroyed.
405 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
406 memset(&info, 0, sizeof(info));
407 info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_ACTIVE_PROCESS |
408 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
409 info.BasicLimitInformation.ActiveProcessLimit = 2;
410 if (!SetInformationJobObject(job,
411 JobObjectExtendedLimitInformation,
412 &info,
413 sizeof(info))) {
414 LOG_GETLASTERROR(ERROR) << "Failed to set limits on the job object";
415 return;
416 }
417
418 // Register the job object with the completion port in the I/O thread to
419 // receive job notifications.
420 if (!MessageLoopForIO::current()->RegisterJobObject(job, this)) {
421 LOG_GETLASTERROR(ERROR)
422 << "Failed to associate the job object with a completion port";
423 return;
424 }
425
426 // ScopedHandle is not compatible with base::Passed, so we wrap it to a scoped
427 // pointer.
428 scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle());
429 *job_wrapper = job.Pass();
430
431 // Let the main thread know that initialization is complete.
432 main_message_loop_->PostTask(FROM_HERE, base::Bind(
433 &WtsSessionProcessLauncher::InitializeSandboxCompleted,
434 base::Unretained(this), base::Passed(&job_wrapper)));
435 }
436
437 void WtsSessionProcessLauncher::InitializeSandboxCompleted(
438 scoped_ptr<ScopedHandle> job) {
439 DCHECK(main_message_loop_->BelongsToCurrentThread());
440 DCHECK(!job_.IsValid());
441 DCHECK_EQ(sandbox_state_, kSandboxUninitialized);
442
443 if (stoppable_state() == Stoppable::kRunning) {
444 job_ = job->Pass();
445 sandbox_state_ = kSandboxRunning;
289 } 446 }
290 } 447 }
291 448
292 } // namespace remoting 449 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698