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

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 job_state_(kJobUninitialized) {
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 // Job object initalization has to be performed on the I/O thread because
88 // MessageLoopForIO::RegisterJobObject() can only be called via
89 // MessageLoopForIO::current().
Wez 2012/08/15 23:22:20 If that's the only limitation then why not move Re
alexeypa (please no reviews) 2012/08/15 23:55:50 It does not belong there (and to MessageLoopProxy
Wez 2012/08/16 00:06:33 OK, so the comment should state that job initializ
alexeypa (please no reviews) 2012/08/16 00:34:10 Done.
90 ipc_message_loop_->PostTask(FROM_HERE, base::Bind(
91 &WtsSessionProcessLauncher::InitializeJob,
92 base::Unretained(this)));
77 } 93 }
78 94
79 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() { 95 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() {
80 monitor_->RemoveWtsConsoleObserver(this); 96 monitor_->RemoveWtsConsoleObserver(this);
81 97
82 DCHECK(!attached_); 98 // Failing this check means that the object hasn't been shutdown correctly and
83 DCHECK(!timer_.IsRunning()); 99 // there still could be pending tasks posted to it.
Wez 2012/08/15 23:22:20 nit: You're checking this here, in addition to the
alexeypa (please no reviews) 2012/08/15 23:55:50 I'm duplicating this check for a reason. First, Wt
Wez 2012/08/16 00:06:33 Right, so the comment should simply clarify that w
alexeypa (please no reviews) 2012/08/16 00:34:10 Done.
100 CHECK_EQ(stoppable_state(), Stoppable::kStopped);
Wez 2012/08/15 23:22:20 nit: Doesn't this check belong before the RemoveWt
alexeypa (please no reviews) 2012/08/15 23:55:50 It does not really matter.
Wez 2012/08/16 00:06:33 Please move it before the RemoveWtsConsoleObserver
alexeypa (please no reviews) 2012/08/16 00:34:10 Done.
101
102 CHECK(!attached_);
103 CHECK(!timer_.IsRunning());
84 } 104 }
85 105
86 void WtsSessionProcessLauncher::LaunchProcess() { 106 void WtsSessionProcessLauncher::LaunchProcess() {
87 DCHECK(main_message_loop_->BelongsToCurrentThread()); 107 DCHECK(main_message_loop_->BelongsToCurrentThread());
88 DCHECK(attached_); 108 DCHECK(attached_);
89 DCHECK(launcher_.get() == NULL); 109 DCHECK(launcher_.get() == NULL);
90 DCHECK(!timer_.IsRunning()); 110 DCHECK(!timer_.IsRunning());
91 DCHECK(!worker_process_.IsValid()); 111 DCHECK(!worker_process_.IsValid());
92 112
93 launch_time_ = base::Time::Now(); 113 launch_time_ = base::Time::Now();
94 launcher_.reset(new WorkerProcessLauncher( 114 launcher_.reset(new WorkerProcessLauncher(
95 this, 115 this,
96 base::Bind(&WtsSessionProcessLauncher::OnLauncherStopped, 116 base::Bind(&WtsSessionProcessLauncher::OnLauncherStopped,
97 base::Unretained(this)), 117 base::Unretained(this)),
98 main_message_loop_, 118 main_message_loop_,
99 ipc_message_loop_)); 119 ipc_message_loop_));
100 launcher_->Start(kChromotingChannelSecurityDescriptor); 120 launcher_->Start(kChromotingChannelSecurityDescriptor);
101 } 121 }
102 122
103 void WtsSessionProcessLauncher::OnLauncherStopped() { 123 void WtsSessionProcessLauncher::OnLauncherStopped() {
104 DCHECK(main_message_loop_->BelongsToCurrentThread()); 124 DCHECK(main_message_loop_->BelongsToCurrentThread());
105 125
106 DWORD exit_code; 126 DWORD exit_code = CONTROL_C_EXIT;
107 if (!::GetExitCodeProcess(worker_process_, &exit_code)) { 127 if (worker_process_.IsValid()) {
108 LOG_GETLASTERROR(INFO) 128 if (!::GetExitCodeProcess(worker_process_, &exit_code)) {
109 << "Failed to query the exit code of the worker process"; 129 LOG_GETLASTERROR(INFO)
110 exit_code = CONTROL_C_EXIT; 130 << "Failed to query the exit code of the worker process";
131 exit_code = CONTROL_C_EXIT;
132 }
133
134 worker_process_.Close();
111 } 135 }
112 136
113 launcher_.reset(NULL); 137 launcher_.reset(NULL);
114 worker_process_.Close();
115 138
116 // Do not relaunch the worker process if the caller has asked us to stop. 139 // Do not relaunch the worker process if the caller has asked us to stop.
117 if (stoppable_state() != Stoppable::kRunning) { 140 if (stoppable_state() != Stoppable::kRunning) {
118 CompleteStopping(); 141 Stop();
119 return; 142 return;
120 } 143 }
121 144
122 // Stop trying to restart the worker process if its process exited due to 145 // Stop trying to restart the worker process if its process exited due to
123 // misconfiguration. 146 // misconfiguration.
124 if (kMinPermanentErrorExitCode <= exit_code && 147 if (kMinPermanentErrorExitCode <= exit_code &&
125 exit_code <= kMaxPermanentErrorExitCode) { 148 exit_code <= kMaxPermanentErrorExitCode) {
126 Stop(); 149 Stop();
127 return; 150 return;
128 } 151 }
(...skipping 12 matching lines...) Expand all
141 launch_backoff_ = std::min( 164 launch_backoff_ = std::min(
142 launch_backoff_, TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)); 165 launch_backoff_, TimeDelta::FromSeconds(kMaxLaunchDelaySeconds));
143 } 166 }
144 167
145 // Try to launch the worker process. 168 // Try to launch the worker process.
146 timer_.Start(FROM_HERE, launch_backoff_, 169 timer_.Start(FROM_HERE, launch_backoff_,
147 this, &WtsSessionProcessLauncher::LaunchProcess); 170 this, &WtsSessionProcessLauncher::LaunchProcess);
148 } 171 }
149 } 172 }
150 173
174 void WtsSessionProcessLauncher::OnIOCompleted(
175 base::MessagePumpForIO::IOContext* context,
176 DWORD bytes_transferred,
177 DWORD error) {
178 DCHECK(ipc_message_loop_->BelongsToCurrentThread());
179
180 // |bytes_transferred| is used in job object notifications to supply
181 // the message ID.
182 if (bytes_transferred == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO)
183 CHECK(SetEvent(process_exit_event_));
184 }
185
151 bool WtsSessionProcessLauncher::DoLaunchProcess( 186 bool WtsSessionProcessLauncher::DoLaunchProcess(
152 const std::string& channel_name, 187 const std::string& channel_name,
153 ScopedHandle* process_exit_event_out) { 188 ScopedHandle* process_exit_event_out) {
154 DCHECK(main_message_loop_->BelongsToCurrentThread()); 189 DCHECK(main_message_loop_->BelongsToCurrentThread());
155 DCHECK(!worker_process_.IsValid()); 190 DCHECK(!worker_process_.IsValid());
156 191
192 // The job object is not ready. Retry starting the host process later.
193 if (!job_.IsValid()) {
194 return false;
195 }
196
157 // Construct the host binary name. 197 // Construct the host binary name.
158 FilePath dir_path; 198 FilePath dir_path;
159 if (!PathService::Get(base::DIR_EXE, &dir_path)) { 199 if (!PathService::Get(base::DIR_EXE, &dir_path)) {
160 LOG(ERROR) << "Failed to get the executable file name."; 200 LOG(ERROR) << "Failed to get the executable file name.";
161 return false; 201 return false;
162 } 202 }
163 FilePath host_binary = dir_path.Append(kMe2meHostBinaryName); 203 FilePath host_binary = dir_path.Append(kMe2meHostBinaryName);
204 FilePath service_binary = dir_path.Append(kMe2meServiceBinaryName);
164 205
165 // Create the host process command line passing the name of the IPC channel 206 // 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. 207 // to use and copying known switches from the service's command line.
167 CommandLine command_line(host_binary); 208 CommandLine command_line(service_binary);
209 command_line.AppendSwitchPath(kElevateSwitchName, host_binary);
168 command_line.AppendSwitchNative(kChromotingIpcSwitchName, 210 command_line.AppendSwitchNative(kChromotingIpcSwitchName,
169 UTF8ToWide(channel_name)); 211 UTF8ToWide(channel_name));
170 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(), 212 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
171 kCopiedSwitchNames, 213 kCopiedSwitchNames,
172 _countof(kCopiedSwitchNames)); 214 _countof(kCopiedSwitchNames));
173 215
216 CHECK(ResetEvent(process_exit_event_));
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. Generic |CONTROL_C_EXIT| will be used if the process
Wez 2012/08/15 23:22:20 nit: What does "will be used" mean here? You mean
alexeypa (please no reviews) 2012/08/15 23:55:50 OpenProcess can fail. I explain why this error is
Wez 2012/08/16 00:06:33 My point is just that the explanation is not clear
alexeypa (please no reviews) 2012/08/16 00:34:10 Done.
216 Stop(); 276 // cannot be opened.
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 (job_state_ == kJobRunning) {
352 job_state_ = kJobStopping;
353 ipc_message_loop_->PostTask(FROM_HERE, base::Bind(
354 &WtsSessionProcessLauncher::DrainJobNotifications,
355 base::Unretained(this)));
356 }
357
358 // Don't complete shutdown if |launcher_| is not completely stopped.
359 if (launcher_.get() != NULL) {
360 return;
361 }
362
363 // Don't complete shutdown if the completion queue hasn't been drained.
364 if (job_state_ != kJobUninitialized && job_state_ != kJobStopped) {
365 return;
366 }
367
368 CompleteStopping();
369 }
370
371 void WtsSessionProcessLauncher::DrainJobNotifications() {
372 DCHECK(ipc_message_loop_->BelongsToCurrentThread());
373
374 // DrainJobNotifications() is posted after the job object is destroyed, so
375 // by this time all notifications from the job object have been processed
376 // already. Let the main thread know that the queue has been drained.
377 main_message_loop_->PostTask(FROM_HERE, base::Bind(
378 &WtsSessionProcessLauncher::DrainJobNotificationsCompleted,
379 base::Unretained(this)));
380 }
381
382 void WtsSessionProcessLauncher::DrainJobNotificationsCompleted() {
383 DCHECK(main_message_loop_->BelongsToCurrentThread());
384 DCHECK_EQ(job_state_, kJobStopping);
385
386 job_state_ = kJobStopped;
387 Stop();
388 }
389
390 void WtsSessionProcessLauncher::InitializeJob() {
391 DCHECK(ipc_message_loop_->BelongsToCurrentThread());
392
393 ScopedHandle job;
394 job.Set(CreateJobObject(NULL, NULL));
395 if (!job.IsValid()) {
396 LOG_GETLASTERROR(ERROR) << "Failed to create a job object";
397 return;
398 }
399
400 // Limit the number of active processes in the job to two (the process
401 // performing elevation and the host) and make sure that all processes will be
402 // killed once the job object is destroyed.
403 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
404 memset(&info, 0, sizeof(info));
405 info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_ACTIVE_PROCESS |
406 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
407 info.BasicLimitInformation.ActiveProcessLimit = 2;
408 if (!SetInformationJobObject(job,
409 JobObjectExtendedLimitInformation,
410 &info,
411 sizeof(info))) {
412 LOG_GETLASTERROR(ERROR) << "Failed to set limits on the job object";
413 return;
414 }
415
416 // Register the job object with the completion port in the I/O thread to
417 // receive job notifications.
418 if (!MessageLoopForIO::current()->RegisterJobObject(job, this)) {
419 LOG_GETLASTERROR(ERROR)
420 << "Failed to associate the job object with a completion port";
421 return;
422 }
423
424 // ScopedHandle is not compatible with base::Passed, so we wrap it to a scoped
425 // pointer.
426 scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle());
427 *job_wrapper = job.Pass();
428
429 // Let the main thread know that initialization is complete.
430 main_message_loop_->PostTask(FROM_HERE, base::Bind(
431 &WtsSessionProcessLauncher::InitializeJobCompleted,
432 base::Unretained(this), base::Passed(&job_wrapper)));
433 }
434
435 void WtsSessionProcessLauncher::InitializeJobCompleted(
436 scoped_ptr<ScopedHandle> job) {
437 DCHECK(main_message_loop_->BelongsToCurrentThread());
438 DCHECK(!job_.IsValid());
439 DCHECK_EQ(job_state_, kJobUninitialized);
440
441 if (stoppable_state() == Stoppable::kRunning) {
442 job_ = job->Pass();
443 job_state_ = kJobRunning;
289 } 444 }
290 } 445 }
291 446
292 } // namespace remoting 447 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698