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

Side by Side Diff: remoting/host/wts_session_process_launcher_win.cc

Issue 10832068: Moving Windows-only files: remoting/host -> remoting/host/win. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: 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
« no previous file with comments | « remoting/host/wts_session_process_launcher_win.h ('k') | remoting/remoting.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4 //
5 // This file implements the Windows service controlling Me2Me host processes
6 // running within user sessions.
7
8 #include "remoting/host/wts_session_process_launcher_win.h"
9
10 #include <windows.h>
11 #include <sddl.h>
12 #include <limits>
13
14 #include "base/base_switches.h"
15 #include "base/bind.h"
16 #include "base/bind_helpers.h"
17 #include "base/command_line.h"
18 #include "base/logging.h"
19 #include "base/message_loop_proxy.h"
20 #include "base/process_util.h"
21 #include "base/rand_util.h"
22 #include "base/stringprintf.h"
23 #include "base/win/scoped_handle.h"
24 #include "ipc/ipc_channel_proxy.h"
25 #include "ipc/ipc_message.h"
26 #include "ipc/ipc_message_macros.h"
27 #include "remoting/host/constants.h"
28 #include "remoting/host/chromoting_messages.h"
29 #include "remoting/host/launch_process_in_session_win.h"
30 #include "remoting/host/sas_injector.h"
31 #include "remoting/host/wts_console_monitor_win.h"
32
33 using base::win::ScopedHandle;
34 using base::TimeDelta;
35
36 namespace {
37
38 // The minimum and maximum delays between attempts to inject host process into
39 // a session.
40 const int kMaxLaunchDelaySeconds = 60;
41 const int kMinLaunchDelaySeconds = 1;
42
43 // Match the pipe name prefix used by Chrome IPC channels.
44 const wchar_t kChromePipeNamePrefix[] = L"\\\\.\\pipe\\chrome.";
45
46 // The IPC channel name is passed to the host in the command line.
47 const char kChromotingIpcSwitchName[] = "chromoting-ipc";
48
49 // The command line parameters that should be copied from the service's command
50 // line to the host process.
51 const char* kCopiedSwitchNames[] = {
52 "auth-config", "host-config", switches::kV, switches::kVModule };
53
54 // The security descriptor of the Chromoting IPC channel. It gives full access
55 // to LocalSystem and denies access by anyone else.
56 const wchar_t kChromotingChannelSecurityDescriptor[] =
57 L"O:SYG:SYD:(A;;GA;;;SY)";
58
59 // Takes the process token and makes a copy of it. The returned handle will have
60 // |desired_access| rights.
61 bool CopyProcessToken(DWORD desired_access,
62 ScopedHandle* token_out) {
63
64 HANDLE handle;
65 if (!OpenProcessToken(GetCurrentProcess(),
66 TOKEN_DUPLICATE | desired_access,
67 &handle)) {
68 LOG_GETLASTERROR(ERROR) << "Failed to open process token";
69 return false;
70 }
71
72 ScopedHandle process_token(handle);
73
74 if (!DuplicateTokenEx(process_token,
75 desired_access,
76 NULL,
77 SecurityImpersonation,
78 TokenPrimary,
79 &handle)) {
80 LOG_GETLASTERROR(ERROR) << "Failed to duplicate the process token";
81 return false;
82 }
83
84 token_out->Set(handle);
85 return true;
86 }
87
88 // Creates a copy of the current process with SE_TCB_NAME privilege enabled.
89 bool CreatePrivilegedToken(ScopedHandle* token_out) {
90 ScopedHandle privileged_token;
91 DWORD desired_access = TOKEN_ADJUST_PRIVILEGES | TOKEN_IMPERSONATE |
92 TOKEN_DUPLICATE | TOKEN_QUERY;
93 if (!CopyProcessToken(desired_access, &privileged_token)) {
94 return false;
95 }
96
97 // Get the LUID for the SE_TCB_NAME privilege.
98 TOKEN_PRIVILEGES state;
99 state.PrivilegeCount = 1;
100 state.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
101 if (!LookupPrivilegeValue(NULL, SE_TCB_NAME, &state.Privileges[0].Luid)) {
102 LOG_GETLASTERROR(ERROR) <<
103 "Failed to lookup the LUID for the SE_TCB_NAME privilege";
104 return false;
105 }
106
107 // Enable the SE_TCB_NAME privilege.
108 if (!AdjustTokenPrivileges(privileged_token, FALSE, &state, 0, NULL, 0)) {
109 LOG_GETLASTERROR(ERROR) <<
110 "Failed to enable SE_TCB_NAME privilege in a token";
111 return false;
112 }
113
114 token_out->Set(privileged_token.Take());
115 return true;
116 }
117
118 // Creates a copy of the current process token for the given |session_id| so
119 // it can be used to launch a process in that session.
120 bool CreateSessionToken(uint32 session_id,
121 ScopedHandle* token_out) {
122
123 ScopedHandle session_token;
124 DWORD desired_access = TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID |
125 TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY;
126 if (!CopyProcessToken(desired_access, &session_token)) {
127 return false;
128 }
129
130 // Change the session ID of the token.
131 DWORD new_session_id = session_id;
132 if (!SetTokenInformation(session_token,
133 TokenSessionId,
134 &new_session_id,
135 sizeof(new_session_id))) {
136 LOG_GETLASTERROR(ERROR) <<
137 "Failed to change session ID of a token";
138 return false;
139 }
140
141 token_out->Set(session_token.Take());
142 return true;
143 }
144
145 // Generates random channel ID.
146 // N.B. Stolen from src/content/common/child_process_host_impl.cc
147 std::wstring GenerateRandomChannelId(void* instance) {
148 return base::StringPrintf(L"%d.%p.%d",
149 base::GetCurrentProcId(), instance,
150 base::RandInt(0, std::numeric_limits<int>::max()));
151 }
152
153 // Creates the server end of the Chromoting IPC channel.
154 // N.B. This code is based on IPC::Channel's implementation.
155 bool CreatePipeForIpcChannel(void* instance,
156 std::wstring* channel_name_out,
157 ScopedHandle* pipe_out) {
158 // Create security descriptor for the channel.
159 SECURITY_ATTRIBUTES security_attributes;
160 security_attributes.nLength = sizeof(security_attributes);
161 security_attributes.bInheritHandle = FALSE;
162
163 ULONG security_descriptor_length = 0;
164 if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
165 kChromotingChannelSecurityDescriptor,
166 SDDL_REVISION_1,
167 reinterpret_cast<PSECURITY_DESCRIPTOR*>(
168 &security_attributes.lpSecurityDescriptor),
169 &security_descriptor_length)) {
170 LOG_GETLASTERROR(ERROR) <<
171 "Failed to create a security descriptor for the Chromoting IPC channel";
172 return false;
173 }
174
175 // Generate a random channel name.
176 std::wstring channel_name(GenerateRandomChannelId(instance));
177
178 // Convert it to the pipe name.
179 std::wstring pipe_name(kChromePipeNamePrefix);
180 pipe_name.append(channel_name);
181
182 // Create the server end of the pipe. This code should match the code in
183 // IPC::Channel with exception of passing a non-default security descriptor.
184 HANDLE pipe = CreateNamedPipeW(pipe_name.c_str(),
185 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
186 FILE_FLAG_FIRST_PIPE_INSTANCE,
187 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
188 1,
189 IPC::Channel::kReadBufferSize,
190 IPC::Channel::kReadBufferSize,
191 5000,
192 &security_attributes);
193 if (pipe == INVALID_HANDLE_VALUE) {
194 LOG_GETLASTERROR(ERROR) <<
195 "Failed to create the server end of the Chromoting IPC channel";
196 LocalFree(security_attributes.lpSecurityDescriptor);
197 return false;
198 }
199
200 LocalFree(security_attributes.lpSecurityDescriptor);
201
202 *channel_name_out = channel_name;
203 pipe_out->Set(pipe);
204 return true;
205 }
206
207 } // namespace
208
209 namespace remoting {
210
211 // Session id that does not represent any session.
212 const uint32 kInvalidSessionId = 0xffffffff;
213
214 WtsSessionProcessLauncher::WtsSessionProcessLauncher(
215 WtsConsoleMonitor* monitor,
216 const FilePath& host_binary,
217 scoped_refptr<base::MessageLoopProxy> main_message_loop,
218 scoped_refptr<base::MessageLoopProxy> ipc_message_loop)
219 : host_binary_(host_binary),
220 main_message_loop_(main_message_loop),
221 ipc_message_loop_(ipc_message_loop),
222 monitor_(monitor),
223 state_(StateDetached) {
224 monitor_->AddWtsConsoleObserver(this);
225 }
226
227 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() {
228 DCHECK(state_ == StateDetached);
229 DCHECK(!timer_.IsRunning());
230 DCHECK(process_.handle() == NULL);
231 DCHECK(process_watcher_.GetWatchedObject() == NULL);
232 DCHECK(chromoting_channel_.get() == NULL);
233 if (monitor_ != NULL) {
234 monitor_->RemoveWtsConsoleObserver(this);
235 }
236 }
237
238 void WtsSessionProcessLauncher::LaunchProcess() {
239 DCHECK(main_message_loop_->BelongsToCurrentThread());
240 DCHECK(state_ == StateStarting);
241 DCHECK(!timer_.IsRunning());
242 DCHECK(process_.handle() == NULL);
243 DCHECK(process_watcher_.GetWatchedObject() == NULL);
244 DCHECK(chromoting_channel_.get() == NULL);
245
246 launch_time_ = base::Time::Now();
247
248 std::wstring channel_name;
249 ScopedHandle pipe;
250 if (CreatePipeForIpcChannel(this, &channel_name, &pipe)) {
251 // Wrap the pipe into an IPC channel.
252 chromoting_channel_.reset(new IPC::ChannelProxy(
253 IPC::ChannelHandle(pipe.Get()),
254 IPC::Channel::MODE_SERVER,
255 this,
256 ipc_message_loop_));
257
258 // Create the host process command line passing the name of the IPC channel
259 // to use and copying known switches from the service's command line.
260 CommandLine command_line(host_binary_);
261 command_line.AppendSwitchNative(kChromotingIpcSwitchName, channel_name);
262 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
263 kCopiedSwitchNames,
264 _countof(kCopiedSwitchNames));
265
266 // Try to launch the process and attach an object watcher to the returned
267 // handle so that we get notified when the process terminates.
268 if (LaunchProcessInSession(host_binary_,
269 command_line.GetCommandLineString(),
270 session_token_,
271 &process_)) {
272 if (process_watcher_.StartWatching(process_.handle(), this)) {
273 state_ = StateAttached;
274 return;
275 } else {
276 LOG(ERROR) << "Failed to arm the process watcher.";
277 process_.Terminate(0);
278 process_.Close();
279 }
280 }
281
282 chromoting_channel_.reset();
283 }
284
285 // Something went wrong. Try to launch the host again later. The attempts rate
286 // is limited by exponential backoff.
287 launch_backoff_ = std::max(launch_backoff_ * 2,
288 TimeDelta::FromSeconds(kMinLaunchDelaySeconds));
289 launch_backoff_ = std::min(launch_backoff_,
290 TimeDelta::FromSeconds(kMaxLaunchDelaySeconds));
291 timer_.Start(FROM_HERE, launch_backoff_,
292 this, &WtsSessionProcessLauncher::LaunchProcess);
293 }
294
295 void WtsSessionProcessLauncher::OnObjectSignaled(HANDLE object) {
296 if (!main_message_loop_->BelongsToCurrentThread()) {
297 main_message_loop_->PostTask(
298 FROM_HERE, base::Bind(&WtsSessionProcessLauncher::OnObjectSignaled,
299 base::Unretained(this), object));
300 return;
301 }
302
303 // It is possible that OnObjectSignaled() task will be queued by another
304 // thread right before |process_watcher_| was stopped. It such a case it is
305 // safe to ignore this notification.
306 if (state_ != StateAttached) {
307 return;
308 }
309
310 DCHECK(!timer_.IsRunning());
311 DCHECK(process_.handle() != NULL);
312 DCHECK(process_watcher_.GetWatchedObject() == NULL);
313 DCHECK(chromoting_channel_.get() != NULL);
314
315 // Stop trying to restart the host if its process exited due to
316 // misconfiguration.
317 int exit_code;
318 bool stop_trying =
319 base::WaitForExitCodeWithTimeout(
320 process_.handle(), &exit_code, base::TimeDelta()) &&
321 kMinPermanentErrorExitCode <= exit_code &&
322 exit_code <= kMaxPermanentErrorExitCode;
323
324 // The host process has been terminated for some reason. The handle can now be
325 // closed.
326 process_.Close();
327 chromoting_channel_.reset();
328 state_ = StateStarting;
329
330 if (stop_trying) {
331 OnSessionDetached();
332
333 // N.B. The service will stop once the last observer is removed from
334 // the list.
335 monitor_->RemoveWtsConsoleObserver(this);
336 monitor_ = NULL;
337 return;
338 }
339
340 // Expand the backoff interval if the process has died quickly or reset it if
341 // it was up longer than the maximum backoff delay.
342 base::TimeDelta delta = base::Time::Now() - launch_time_;
343 if (delta < base::TimeDelta() ||
344 delta >= base::TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)) {
345 launch_backoff_ = base::TimeDelta();
346 } else {
347 launch_backoff_ = std::max(launch_backoff_ * 2,
348 TimeDelta::FromSeconds(kMinLaunchDelaySeconds));
349 launch_backoff_ = std::min(launch_backoff_,
350 TimeDelta::FromSeconds(kMaxLaunchDelaySeconds));
351 }
352
353 // Try to restart the host.
354 timer_.Start(FROM_HERE, launch_backoff_,
355 this, &WtsSessionProcessLauncher::LaunchProcess);
356 }
357
358 bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) {
359 bool handled = true;
360 IPC_BEGIN_MESSAGE_MAP(WtsSessionProcessLauncher, message)
361 IPC_MESSAGE_HANDLER(ChromotingHostMsg_SendSasToConsole,
362 OnSendSasToConsole)
363 IPC_MESSAGE_UNHANDLED(handled = false)
364 IPC_END_MESSAGE_MAP()
365 return handled;
366 }
367
368 void WtsSessionProcessLauncher::OnSendSasToConsole() {
369 if (!main_message_loop_->BelongsToCurrentThread()) {
370 main_message_loop_->PostTask(
371 FROM_HERE, base::Bind(&WtsSessionProcessLauncher::OnSendSasToConsole,
372 base::Unretained(this)));
373 return;
374 }
375
376 if (state_ == StateAttached) {
377 if (sas_injector_.get() == NULL) {
378 sas_injector_ = SasInjector::Create();
379 }
380
381 if (sas_injector_.get() != NULL) {
382 sas_injector_->InjectSas();
383 }
384 }
385 }
386
387 void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) {
388 DCHECK(main_message_loop_->BelongsToCurrentThread());
389 DCHECK(state_ == StateDetached);
390 DCHECK(!timer_.IsRunning());
391 DCHECK(process_.handle() == NULL);
392 DCHECK(process_watcher_.GetWatchedObject() == NULL);
393 DCHECK(chromoting_channel_.get() == NULL);
394
395 // Temporarily enable the SE_TCB_NAME privilege. The privileged token is
396 // created as needed and kept for later reuse.
397 if (privileged_token_.Get() == NULL) {
398 if (!CreatePrivilegedToken(&privileged_token_)) {
399 return;
400 }
401 }
402
403 if (!ImpersonateLoggedOnUser(privileged_token_)) {
404 LOG_GETLASTERROR(ERROR) <<
405 "Failed to impersonate the privileged token";
406 return;
407 }
408
409 // While the SE_TCB_NAME privilege is enabled, create a session token for
410 // the launched process.
411 bool result = CreateSessionToken(session_id, &session_token_);
412
413 // Revert to the default token. The default token is sufficient to call
414 // CreateProcessAsUser() successfully.
415 CHECK(RevertToSelf());
416
417 if (!result)
418 return;
419
420 // Now try to launch the host.
421 state_ = StateStarting;
422 LaunchProcess();
423 }
424
425 void WtsSessionProcessLauncher::OnSessionDetached() {
426 DCHECK(main_message_loop_->BelongsToCurrentThread());
427 DCHECK(state_ == StateDetached ||
428 state_ == StateStarting ||
429 state_ == StateAttached);
430
431 switch (state_) {
432 case StateDetached:
433 DCHECK(!timer_.IsRunning());
434 DCHECK(process_.handle() == NULL);
435 DCHECK(process_watcher_.GetWatchedObject() == NULL);
436 DCHECK(chromoting_channel_.get() == NULL);
437 break;
438
439 case StateStarting:
440 DCHECK(process_.handle() == NULL);
441 DCHECK(process_watcher_.GetWatchedObject() == NULL);
442 DCHECK(chromoting_channel_.get() == NULL);
443
444 timer_.Stop();
445 launch_backoff_ = base::TimeDelta();
446 state_ = StateDetached;
447 break;
448
449 case StateAttached:
450 DCHECK(!timer_.IsRunning());
451 DCHECK(process_.handle() != NULL);
452 DCHECK(process_watcher_.GetWatchedObject() != NULL);
453 DCHECK(chromoting_channel_.get() != NULL);
454
455 process_watcher_.StopWatching();
456 process_.Terminate(0);
457 process_.Close();
458 chromoting_channel_.reset();
459 state_ = StateDetached;
460 break;
461 }
462
463 session_token_.Close();
464 }
465
466 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/host/wts_session_process_launcher_win.h ('k') | remoting/remoting.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698