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

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

Issue 9617027: Chromoting: Implemented security attention sequence (SAS) emulation on Windows. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebased + a bunch of renamings Created 8 years, 9 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/wts_session_process_launcher_win.h" 8 #include "remoting/host/wts_session_process_launcher_win.h"
9 9
10 #include <windows.h> 10 #include <windows.h>
11 #include <sddl.h>
12 #include <limits>
11 13
12 #include "base/logging.h" 14 #include "base/logging.h"
15 #include "base/process_util.h"
16 #include "base/rand_util.h"
17 #include "base/string16.h"
18 #include "base/stringprintf.h"
19 #include "base/threading/thread.h"
13 #include "base/utf_string_conversions.h" 20 #include "base/utf_string_conversions.h"
14 #include "base/win/scoped_handle.h" 21 #include "base/win/scoped_handle.h"
22 #include "ipc/ipc_channel_proxy.h"
23 #include "ipc/ipc_message.h"
24 #include "ipc/ipc_message_macros.h"
15 25
26 #include "remoting/host/chromoting_messages.h"
27 #include "remoting/host/sas_injector_win.h"
16 #include "remoting/host/wts_console_monitor_win.h" 28 #include "remoting/host/wts_console_monitor_win.h"
17 29
18 using base::win::ScopedHandle; 30 using base::win::ScopedHandle;
19 using base::TimeDelta; 31 using base::TimeDelta;
20 32
21 namespace { 33 namespace {
22 34
23 // The minimum and maximum delays between attempts to inject host process into 35 // The minimum and maximum delays between attempts to inject host process into
24 // a session. 36 // a session.
25 const int kMaxLaunchDelaySeconds = 60; 37 const int kMaxLaunchDelaySeconds = 60;
26 const int kMinLaunchDelaySeconds = 1; 38 const int kMinLaunchDelaySeconds = 1;
27 39
28 // Name of the default session desktop. 40 // Name of the default session desktop.
29 const char kDefaultDesktopName[] = "winsta0\\default"; 41 const char kDefaultDesktopName[] = "winsta0\\default";
30 42
43 const char kChromePipeNamePrefix[] = "\\\\.\\pipe\\chrome.";
Wez 2012/03/08 22:58:55 nit: Comment this.
alexeypa (please no reviews) 2012/03/09 01:13:54 Done.
44
45 // Generatesthe command line of the host process.
Wez 2012/03/08 22:58:55 typo: Generates the ...
alexeypa (please no reviews) 2012/03/09 01:13:54 Done.
46 const char kHostProcessCommandLineFormat[] = "\"%ls\" --channel=%ls";
47
48 // The security descriptor of the chromoting IPC channel. It gives full access
49 // to LocalSystem and denies access by anyone else.
50 const char kChromotingChannelSecurityDescriptor[] =
51 "O:SY" "G:SY" "D:(A;;GA;;;SY)";
52
31 // Takes the process token and makes a copy of it. The returned handle will have 53 // Takes the process token and makes a copy of it. The returned handle will have
32 // |desired_access| rights. 54 // |desired_access| rights.
33 bool CopyProcessToken(DWORD desired_access, 55 bool CopyProcessToken(DWORD desired_access,
34 ScopedHandle* token_out) { 56 ScopedHandle* token_out) {
35 57
36 HANDLE handle; 58 HANDLE handle;
37 if (!OpenProcessToken(GetCurrentProcess(), 59 if (!OpenProcessToken(GetCurrentProcess(),
38 TOKEN_DUPLICATE | desired_access, 60 TOKEN_DUPLICATE | desired_access,
39 &handle)) { 61 &handle)) {
40 LOG_GETLASTERROR(ERROR) << "Failed to open process token"; 62 LOG_GETLASTERROR(ERROR) << "Failed to open process token";
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
107 sizeof(new_session_id))) { 129 sizeof(new_session_id))) {
108 LOG_GETLASTERROR(ERROR) << 130 LOG_GETLASTERROR(ERROR) <<
109 "Failed to change session ID of a token"; 131 "Failed to change session ID of a token";
110 return false; 132 return false;
111 } 133 }
112 134
113 token_out->Set(session_token.Take()); 135 token_out->Set(session_token.Take());
114 return true; 136 return true;
115 } 137 }
116 138
139 // Generates random channel ID.
140 // N.B. Stolen from src/content/common/child_process_host_impl.cc
141 string16 GenerateRandomChannelId(void* instance) {
142 return base::StringPrintf(ASCIIToUTF16("%d.%p.%d").c_str(),
143 base::GetCurrentProcId(), instance,
144 base::RandInt(0, std::numeric_limits<int>::max()));
145 }
146
147 // Creates the server end of the Chromoting IPC channel.
Wez 2012/03/08 22:58:55 nit: Similar comment "based on IPC::Channel"?
alexeypa (please no reviews) 2012/03/09 01:13:54 Done.
148 bool CreatePipeForIpcChannel(void* instance,
149 string16* channel_name_out,
150 ScopedHandle* pipe_out) {
151 // Create security descriptor for the channel.
152 SECURITY_ATTRIBUTES security_attributes;
153 security_attributes.nLength = sizeof(security_attributes);
154 security_attributes.bInheritHandle = FALSE;
155
156 ULONG security_descriptor_length = 0;
157 if (!ConvertStringSecurityDescriptorToSecurityDescriptorA(
158 kChromotingChannelSecurityDescriptor,
159 SDDL_REVISION_1,
160 reinterpret_cast<PSECURITY_DESCRIPTOR*>(
161 &security_attributes.lpSecurityDescriptor),
162 &security_descriptor_length)) {
163 LOG_GETLASTERROR(ERROR) <<
164 "Failed to create a security descriptor for the Chromoting IPC channel";
165 return false;
166 }
167
168 // Generate a random channel name.
169 string16 channel_name(GenerateRandomChannelId(instance));
170
171 // Convert it to the pipe name.
172 string16 pipe_name(ASCIIToUTF16(kChromePipeNamePrefix));
173 pipe_name.append(channel_name);
174
175 // Create the server end of the pipe. This code should match the code in
176 // IPC::Channel with exception of passing a non-default security descriptor.
Wez 2012/03/08 22:58:55 Not for this CL, but would adding support for this
alexeypa (please no reviews) 2012/03/09 01:13:54 Probably.
177 HANDLE pipe = CreateNamedPipeW(pipe_name.c_str(),
178 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
179 FILE_FLAG_FIRST_PIPE_INSTANCE,
180 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
181 1,
182 IPC::Channel::kReadBufferSize,
183 IPC::Channel::kReadBufferSize,
184 5000,
185 &security_attributes);
186 if (pipe == INVALID_HANDLE_VALUE) {
187 LOG_GETLASTERROR(ERROR) <<
188 "Failed to create the server end of the Chromoting IPC channel";
189 LocalFree(security_attributes.lpSecurityDescriptor);
190 return false;
191 }
192
193 LocalFree(security_attributes.lpSecurityDescriptor);
194
195 *channel_name_out = channel_name;
196 pipe_out->Set(pipe);
197 return true;
198 }
199
117 // Launches |binary| in the security context of the supplied |user_token|. 200 // Launches |binary| in the security context of the supplied |user_token|.
118 bool LaunchProcessAsUser(const FilePath& binary, 201 bool LaunchProcessAsUser(const FilePath& binary,
202 const string16& command_line,
119 HANDLE user_token, 203 HANDLE user_token,
120 base::Process* process_out) { 204 base::Process* process_out) {
121 string16 command_line = binary.value(); 205 string16 application_name = binary.value();
122 string16 desktop = ASCIIToUTF16(kDefaultDesktopName); 206 string16 desktop = ASCIIToUTF16(kDefaultDesktopName);
123 207
124 PROCESS_INFORMATION process_info; 208 PROCESS_INFORMATION process_info;
125 STARTUPINFOW startup_info; 209 STARTUPINFOW startup_info;
126 210
127 memset(&startup_info, 0, sizeof(startup_info)); 211 memset(&startup_info, 0, sizeof(startup_info));
128 startup_info.cb = sizeof(startup_info); 212 startup_info.cb = sizeof(startup_info);
129 startup_info.lpDesktop = const_cast<LPWSTR>(desktop.c_str()); 213 startup_info.lpDesktop = const_cast<LPWSTR>(desktop.c_str());
130 214
131 if (!CreateProcessAsUserW(user_token, 215 if (!CreateProcessAsUserW(user_token,
132 command_line.c_str(), 216 application_name.c_str(),
133 const_cast<LPWSTR>(command_line.c_str()), 217 const_cast<LPWSTR>(command_line.c_str()),
134 NULL, 218 NULL,
135 NULL, 219 NULL,
136 FALSE, 220 FALSE,
137 0, 221 0,
138 NULL, 222 NULL,
139 NULL, 223 NULL,
140 &startup_info, 224 &startup_info,
141 &process_info)) { 225 &process_info)) {
142 LOG_GETLASTERROR(ERROR) << 226 LOG_GETLASTERROR(ERROR) <<
143 "Failed to launch a process with a user token"; 227 "Failed to launch a process with a user token";
144 return false; 228 return false;
145 } 229 }
146 230
147 CloseHandle(process_info.hThread); 231 CloseHandle(process_info.hThread);
148 process_out->set_handle(process_info.hProcess); 232 process_out->set_handle(process_info.hProcess);
149 return true; 233 return true;
150 } 234 }
151 235
152 } // namespace 236 } // namespace
153 237
154 namespace remoting { 238 namespace remoting {
155 239
156 WtsSessionProcessLauncher::WtsSessionProcessLauncher( 240 WtsSessionProcessLauncher::WtsSessionProcessLauncher(
157 WtsConsoleMonitor* monitor, 241 WtsConsoleMonitor* monitor,
158 const FilePath& host_binary) 242 const FilePath& host_binary,
243 base::Thread* io_thread)
159 : host_binary_(host_binary), 244 : host_binary_(host_binary),
245 io_thread_(io_thread),
160 monitor_(monitor), 246 monitor_(monitor),
161 state_(StateDetached) { 247 state_(StateDetached) {
162 monitor_->AddWtsConsoleObserver(this); 248 monitor_->AddWtsConsoleObserver(this);
163 } 249 }
164 250
165 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() { 251 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() {
166 DCHECK(state_ == StateDetached); 252 DCHECK(state_ == StateDetached);
167 DCHECK(!timer_.IsRunning()); 253 DCHECK(!timer_.IsRunning());
168 DCHECK(process_.handle() == NULL); 254 DCHECK(process_.handle() == NULL);
169 DCHECK(process_watcher_.GetWatchedObject() == NULL); 255 DCHECK(process_watcher_.GetWatchedObject() == NULL);
256 DCHECK(chromoting_channel_.get() == NULL);
170 257
171 monitor_->RemoveWtsConsoleObserver(this); 258 monitor_->RemoveWtsConsoleObserver(this);
172 } 259 }
173 260
174 void WtsSessionProcessLauncher::LaunchProcess() { 261 void WtsSessionProcessLauncher::LaunchProcess() {
175 DCHECK(state_ == StateStarting); 262 DCHECK(state_ == StateStarting);
176 DCHECK(!timer_.IsRunning()); 263 DCHECK(!timer_.IsRunning());
177 DCHECK(process_.handle() == NULL); 264 DCHECK(process_.handle() == NULL);
178 DCHECK(process_watcher_.GetWatchedObject() == NULL); 265 DCHECK(process_watcher_.GetWatchedObject() == NULL);
266 DCHECK(chromoting_channel_.get() == NULL);
179 267
180 // Try to launch the process and attach an object watcher to the returned
181 // handle so that we get notified when the process terminates.
182 launch_time_ = base::Time::Now(); 268 launch_time_ = base::Time::Now();
183 if (LaunchProcessAsUser(host_binary_, session_token_, &process_)) { 269
184 if (process_watcher_.StartWatching(process_.handle(), this)) { 270 string16 channel_name;
185 state_ = StateAttached; 271 ScopedHandle pipe;
186 return; 272 if (CreatePipeForIpcChannel(this, &channel_name, &pipe)) {
187 } else { 273 // Wrap the pipe into an IPC channel.
188 LOG(ERROR) << "Failed to arm the process watcher."; 274 chromoting_channel_.reset(new IPC::ChannelProxy(
189 process_.Terminate(0); 275 IPC::ChannelHandle(pipe.Get()),
Wez 2012/03/08 22:58:55 nit: This indentation looks weird; can you put the
alexeypa (please no reviews) 2012/03/09 01:13:54 Done.
190 process_.Close(); 276 IPC::Channel::MODE_SERVER,
277 this,
278 io_thread_->message_loop_proxy().get()));
279
280 string16 command_line =
281 base::StringPrintf(ASCIIToUTF16(kHostProcessCommandLineFormat).c_str(),
282 host_binary_.value().c_str(),
283 channel_name.c_str());
284
285 // Try to launch the process and attach an object watcher to the returned
286 // handle so that we get notified when the process terminates.
287 if (LaunchProcessAsUser(host_binary_, command_line, session_token_,
288 &process_)) {
289 if (process_watcher_.StartWatching(process_.handle(), this)) {
290 state_ = StateAttached;
291 return;
292 } else {
293 LOG(ERROR) << "Failed to arm the process watcher.";
294 process_.Terminate(0);
295 process_.Close();
296 }
191 } 297 }
298
299 chromoting_channel_.reset();
192 } 300 }
193 301
194 // Something went wrong. Try to launch the host again later. The attempts rate 302 // Something went wrong. Try to launch the host again later. The attempts rate
195 // is limited by exponential backoff. 303 // is limited by exponential backoff.
196 launch_backoff_ = std::max(launch_backoff_ * 2, 304 launch_backoff_ = std::max(launch_backoff_ * 2,
197 TimeDelta::FromSeconds(kMinLaunchDelaySeconds)); 305 TimeDelta::FromSeconds(kMinLaunchDelaySeconds));
198 launch_backoff_ = std::min(launch_backoff_, 306 launch_backoff_ = std::min(launch_backoff_,
199 TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)); 307 TimeDelta::FromSeconds(kMaxLaunchDelaySeconds));
200 timer_.Start(FROM_HERE, launch_backoff_, 308 timer_.Start(FROM_HERE, launch_backoff_,
201 this, &WtsSessionProcessLauncher::LaunchProcess); 309 this, &WtsSessionProcessLauncher::LaunchProcess);
202 } 310 }
203 311
204 void WtsSessionProcessLauncher::OnObjectSignaled(HANDLE object) { 312 void WtsSessionProcessLauncher::OnObjectSignaled(HANDLE object) {
205 DCHECK(state_ == StateAttached); 313 DCHECK(state_ == StateAttached);
206 DCHECK(!timer_.IsRunning()); 314 DCHECK(!timer_.IsRunning());
207 DCHECK(process_.handle() != NULL); 315 DCHECK(process_.handle() != NULL);
208 DCHECK(process_watcher_.GetWatchedObject() == NULL); 316 DCHECK(process_watcher_.GetWatchedObject() == NULL);
317 DCHECK(chromoting_channel_.get() != NULL);
209 318
210 // The host process has been terminated for some reason. The handle can now be 319 // The host process has been terminated for some reason. The handle can now be
211 // closed. 320 // closed.
212 process_.Close(); 321 process_.Close();
322 chromoting_channel_.reset();
213 323
214 // Expand the backoff interval if the process has died quickly or reset it if 324 // Expand the backoff interval if the process has died quickly or reset it if
215 // it was up longer than the maximum backoff delay. 325 // it was up longer than the maximum backoff delay.
216 base::TimeDelta delta = base::Time::Now() - launch_time_; 326 base::TimeDelta delta = base::Time::Now() - launch_time_;
217 if (delta < base::TimeDelta() || 327 if (delta < base::TimeDelta() ||
218 delta >= base::TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)) { 328 delta >= base::TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)) {
219 launch_backoff_ = base::TimeDelta(); 329 launch_backoff_ = base::TimeDelta();
220 } else { 330 } else {
221 launch_backoff_ = std::max(launch_backoff_ * 2, 331 launch_backoff_ = std::max(launch_backoff_ * 2,
222 TimeDelta::FromSeconds(kMinLaunchDelaySeconds)); 332 TimeDelta::FromSeconds(kMinLaunchDelaySeconds));
223 launch_backoff_ = std::min(launch_backoff_, 333 launch_backoff_ = std::min(launch_backoff_,
224 TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)); 334 TimeDelta::FromSeconds(kMaxLaunchDelaySeconds));
225 } 335 }
226 336
227 // Try to restart the host. 337 // Try to restart the host.
228 state_ = StateStarting; 338 state_ = StateStarting;
229 timer_.Start(FROM_HERE, launch_backoff_, 339 timer_.Start(FROM_HERE, launch_backoff_,
230 this, &WtsSessionProcessLauncher::LaunchProcess); 340 this, &WtsSessionProcessLauncher::LaunchProcess);
231 } 341 }
232 342
343 bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) {
344 bool handled = true;
345 IPC_BEGIN_MESSAGE_MAP(WtsSessionProcessLauncher, message)
346 IPC_MESSAGE_HANDLER(ChromotingHostMsg_SendSasToConsole,
347 OnSendSasToConsole)
348 IPC_MESSAGE_UNHANDLED(handled = false)
349 IPC_END_MESSAGE_MAP()
350 return handled;
351 }
352
353 void WtsSessionProcessLauncher::OnSendSasToConsole() {
354 if (state_ == StateAttached) {
355 if (sas_injector_.get() == NULL) {
356 sas_injector_ = SasInjector::Create();
357 }
358
359 if (sas_injector_.get() != NULL) {
360 sas_injector_->InjectSas();
361 }
362 }
363 }
364
233 void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) { 365 void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) {
234 DCHECK(state_ == StateDetached); 366 DCHECK(state_ == StateDetached);
235 DCHECK(!timer_.IsRunning()); 367 DCHECK(!timer_.IsRunning());
236 DCHECK(process_.handle() == NULL); 368 DCHECK(process_.handle() == NULL);
237 DCHECK(process_watcher_.GetWatchedObject() == NULL); 369 DCHECK(process_watcher_.GetWatchedObject() == NULL);
370 DCHECK(chromoting_channel_.get() == NULL);
238 371
239 // Temporarily enable the SE_TCB_NAME privilege. The privileged token is 372 // Temporarily enable the SE_TCB_NAME privilege. The privileged token is
240 // created as needed and kept for later reuse. 373 // created as needed and kept for later reuse.
241 if (privileged_token_.Get() == NULL) { 374 if (privileged_token_.Get() == NULL) {
242 if (!CreatePrivilegedToken(&privileged_token_)) { 375 if (!CreatePrivilegedToken(&privileged_token_)) {
243 return; 376 return;
244 } 377 }
245 } 378 }
246 379
247 if (!ImpersonateLoggedOnUser(privileged_token_)) { 380 if (!ImpersonateLoggedOnUser(privileged_token_)) {
248 LOG_GETLASTERROR(ERROR) << 381 LOG_GETLASTERROR(ERROR) <<
249 "Failed to impersonate the privileged token"; 382 "Failed to impersonate the privileged token";
250 return; 383 return;
251 } 384 }
252 385
253 // While the SE_TCB_NAME progolege is enabled, create a session token for 386 // While the SE_TCB_NAME privilege is enabled, create a session token for
254 // the launched process. 387 // the launched process.
255 bool result = CreateSessionToken(session_id, &session_token_); 388 bool result = CreateSessionToken(session_id, &session_token_);
256 389
257 // Revert to the default token. The default token is sufficient to call 390 // Revert to the default token. The default token is sufficient to call
258 // CreateProcessAsUser() successfully. 391 // CreateProcessAsUser() successfully.
259 CHECK(RevertToSelf()); 392 CHECK(RevertToSelf());
260 393
261 if (!result) 394 if (!result)
262 return; 395 return;
263 396
264 // Now try to launch the host. 397 // Now try to launch the host.
265 state_ = StateStarting; 398 state_ = StateStarting;
266 LaunchProcess(); 399 LaunchProcess();
267 } 400 }
268 401
269 void WtsSessionProcessLauncher::OnSessionDetached() { 402 void WtsSessionProcessLauncher::OnSessionDetached() {
270 DCHECK(state_ == StateDetached || 403 DCHECK(state_ == StateDetached ||
271 state_ == StateStarting || 404 state_ == StateStarting ||
272 state_ == StateAttached); 405 state_ == StateAttached);
273 406
274 switch (state_) { 407 switch (state_) {
275 case StateDetached: 408 case StateDetached:
276 DCHECK(!timer_.IsRunning()); 409 DCHECK(!timer_.IsRunning());
277 DCHECK(process_.handle() == NULL); 410 DCHECK(process_.handle() == NULL);
278 DCHECK(process_watcher_.GetWatchedObject() == NULL); 411 DCHECK(process_watcher_.GetWatchedObject() == NULL);
412 DCHECK(chromoting_channel_.get() == NULL);
279 break; 413 break;
280 414
281 case StateStarting: 415 case StateStarting:
282 DCHECK(timer_.IsRunning()); 416 DCHECK(timer_.IsRunning());
283 DCHECK(process_.handle() == NULL); 417 DCHECK(process_.handle() == NULL);
284 DCHECK(process_watcher_.GetWatchedObject() == NULL); 418 DCHECK(process_watcher_.GetWatchedObject() == NULL);
419 DCHECK(chromoting_channel_.get() == NULL);
285 420
286 timer_.Stop(); 421 timer_.Stop();
287 launch_backoff_ = base::TimeDelta(); 422 launch_backoff_ = base::TimeDelta();
288 state_ = StateDetached; 423 state_ = StateDetached;
289 break; 424 break;
290 425
291 case StateAttached: 426 case StateAttached:
292 DCHECK(!timer_.IsRunning()); 427 DCHECK(!timer_.IsRunning());
293 DCHECK(process_.handle() != NULL); 428 DCHECK(process_.handle() != NULL);
294 DCHECK(process_watcher_.GetWatchedObject() != NULL); 429 DCHECK(process_watcher_.GetWatchedObject() != NULL);
430 DCHECK(chromoting_channel_.get() != NULL);
295 431
296 process_watcher_.StopWatching(); 432 process_watcher_.StopWatching();
297 process_.Terminate(0); 433 process_.Terminate(0);
298 process_.Close(); 434 process_.Close();
435 chromoting_channel_.reset();
299 state_ = StateDetached; 436 state_ = StateDetached;
300 break; 437 break;
301 } 438 }
302 } 439 }
303 440
304 } // namespace remoting 441 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698