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

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

Issue 11065016: Return the thread handle when launching a process in a different session via the execution server o… (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: CR feedback. Created 8 years, 2 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 | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 #include "remoting/host/win/launch_process_with_token.h" 5 #include "remoting/host/win/launch_process_with_token.h"
6 6
7 #include <windows.h> 7 #include <windows.h>
8 #include <winternl.h> 8 #include <winternl.h>
9 9
10 #include "base/logging.h" 10 #include "base/logging.h"
(...skipping 22 matching lines...) Expand all
33 const int kPipeConnectMaxAttempts = 3; 33 const int kPipeConnectMaxAttempts = 3;
34 34
35 // 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
36 // a session. 36 // a session.
37 const int kMaxLaunchDelaySeconds = 60; 37 const int kMaxLaunchDelaySeconds = 60;
38 const int kMinLaunchDelaySeconds = 1; 38 const int kMinLaunchDelaySeconds = 1;
39 39
40 // Name of the default session desktop. 40 // Name of the default session desktop.
41 const char kDefaultDesktopName[] = "winsta0\\default"; 41 const char kDefaultDesktopName[] = "winsta0\\default";
42 42
43 // Terminates the process and closes process and thread handles in
44 // |process_information| structure.
45 void CloseHandlesAndTerminateProcess(PROCESS_INFORMATION* process_information) {
46 if (process_information->hThread) {
47 CloseHandle(process_information->hThread);
48 process_information->hThread = NULL;
49 }
50
51 if (process_information->hProcess) {
52 TerminateProcess(process_information->hProcess, CONTROL_C_EXIT);
53 CloseHandle(process_information->hProcess);
54 process_information->hProcess = NULL;
55 }
56 }
57
58 // Connects to the executor server corresponding to |session_id|.
59 bool ConnectToExecutionServer(uint32 session_id,
60 base::win::ScopedHandle* pipe_out) {
61 string16 pipe_name;
62
63 // Use winsta!WinStationQueryInformationW() to determine the process creation
64 // pipe name for the session.
65 FilePath winsta_path(base::GetNativeLibraryName(UTF8ToUTF16("winsta")));
66 base::ScopedNativeLibrary winsta(winsta_path);
67 if (winsta.is_valid()) {
68 PWINSTATIONQUERYINFORMATIONW win_station_query_information =
69 static_cast<PWINSTATIONQUERYINFORMATIONW>(
70 winsta.GetFunctionPointer("WinStationQueryInformationW"));
71 if (win_station_query_information) {
72 wchar_t name[MAX_PATH];
73 ULONG name_length;
74 if (win_station_query_information(0,
75 session_id,
76 kCreateProcessPipeNameClass,
77 name,
78 sizeof(name),
79 &name_length)) {
80 pipe_name.assign(name);
81 }
82 }
83 }
84
85 // Use the default pipe name if we couldn't query its name.
86 if (pipe_name.empty()) {
87 pipe_name = UTF8ToUTF16(
88 StringPrintf(kCreateProcessDefaultPipeNameFormat, session_id));
89 }
90
91 // Try to connect to the named pipe.
92 base::win::ScopedHandle pipe;
93 for (int i = 0; i < kPipeConnectMaxAttempts; ++i) {
94 pipe.Set(CreateFile(pipe_name.c_str(),
95 GENERIC_READ | GENERIC_WRITE,
96 0,
97 NULL,
98 OPEN_EXISTING,
99 0,
100 NULL));
101 if (pipe.IsValid()) {
102 break;
103 }
104
105 // Cannot continue retrying if error is something other than
106 // ERROR_PIPE_BUSY.
107 if (GetLastError() != ERROR_PIPE_BUSY) {
108 break;
109 }
110
111 // Cannot continue retrying if wait on pipe fails.
112 if (!WaitNamedPipe(pipe_name.c_str(), kPipeBusyWaitTimeoutMs)) {
113 break;
114 }
115 }
116
117 if (!pipe.IsValid()) {
118 LOG_GETLASTERROR(ERROR) << "Failed to connect to '" << pipe_name << "'";
119 return false;
120 }
121
122 *pipe_out = pipe.Pass();
123 return true;
124 }
125
43 // Copies the process token making it a primary impersonation token. 126 // Copies the process token making it a primary impersonation token.
44 // The returned handle will have |desired_access| rights. 127 // The returned handle will have |desired_access| rights.
45 bool CopyProcessToken(DWORD desired_access, ScopedHandle* token_out) { 128 bool CopyProcessToken(DWORD desired_access, ScopedHandle* token_out) {
46 ScopedHandle process_token; 129 ScopedHandle process_token;
47 if (!OpenProcessToken(GetCurrentProcess(), 130 if (!OpenProcessToken(GetCurrentProcess(),
48 TOKEN_DUPLICATE | desired_access, 131 TOKEN_DUPLICATE | desired_access,
49 process_token.Receive())) { 132 process_token.Receive())) {
50 LOG_GETLASTERROR(ERROR) << "Failed to open process token"; 133 LOG_GETLASTERROR(ERROR) << "Failed to open process token";
51 return false; 134 return false;
52 } 135 }
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
89 if (!AdjustTokenPrivileges(privileged_token, FALSE, &state, 0, NULL, 0)) { 172 if (!AdjustTokenPrivileges(privileged_token, FALSE, &state, 0, NULL, 0)) {
90 LOG_GETLASTERROR(ERROR) << 173 LOG_GETLASTERROR(ERROR) <<
91 "Failed to enable SE_TCB_NAME privilege in a token"; 174 "Failed to enable SE_TCB_NAME privilege in a token";
92 return false; 175 return false;
93 } 176 }
94 177
95 *token_out = privileged_token.Pass(); 178 *token_out = privileged_token.Pass();
96 return true; 179 return true;
97 } 180 }
98 181
99 // Requests the execution server to create a process in the specified session 182 // Fills the process and thread handles in the passed |process_information|
100 // using the default (i.e. Winlogon) token. This routine relies on undocumented 183 // structure and resume the process if the caller didn't want to suspend it.
101 // OS functionality and will likely not work on anything but XP or W2K3. 184 bool ProcessCreateProcessResponse(DWORD creation_flags,
102 bool CreateRemoteSessionProcess( 185 PROCESS_INFORMATION* process_information) {
103 uint32 session_id, 186 // The execution server does not return handles to the created process and
104 const FilePath::StringType& application_name, 187 // thread.
105 const CommandLine::StringType& command_line, 188 if (!process_information->hProcess) {
106 DWORD creation_flags, 189 // N.B. PROCESS_ALL_ACCESS is different in XP and Vista+ versions of
107 PROCESS_INFORMATION* process_information_out) 190 // the SDK. |desired_access| below is effectively PROCESS_ALL_ACCESS from
108 { 191 // the XP version of the SDK.
109 DCHECK(base::win::GetVersion() == base::win::VERSION_XP); 192 DWORD desired_access =
110 193 STANDARD_RIGHTS_REQUIRED |
111 string16 pipe_name; 194 SYNCHRONIZE |
112 195 PROCESS_TERMINATE |
113 // Use winsta!WinStationQueryInformationW() to determine the process creation 196 PROCESS_CREATE_THREAD |
114 // pipe name for the session. 197 PROCESS_SET_SESSIONID |
115 FilePath winsta_path(base::GetNativeLibraryName(UTF8ToUTF16("winsta"))); 198 PROCESS_VM_OPERATION |
116 base::ScopedNativeLibrary winsta(winsta_path); 199 PROCESS_VM_READ |
117 if (winsta.is_valid()) { 200 PROCESS_VM_WRITE |
118 PWINSTATIONQUERYINFORMATIONW win_station_query_information = 201 PROCESS_DUP_HANDLE |
119 static_cast<PWINSTATIONQUERYINFORMATIONW>( 202 PROCESS_CREATE_PROCESS |
120 winsta.GetFunctionPointer("WinStationQueryInformationW")); 203 PROCESS_SET_QUOTA |
121 if (win_station_query_information) { 204 PROCESS_SET_INFORMATION |
122 wchar_t name[MAX_PATH]; 205 PROCESS_QUERY_INFORMATION |
123 ULONG name_length; 206 PROCESS_SUSPEND_RESUME;
124 if (win_station_query_information(0, 207 process_information->hProcess =
125 session_id, 208 OpenProcess(desired_access,
126 kCreateProcessPipeNameClass, 209 FALSE,
127 name, 210 process_information->dwProcessId);
128 sizeof(name), 211 if (!process_information->hProcess) {
129 &name_length)) { 212 LOG_GETLASTERROR(ERROR) << "Failed to open the process "
130 pipe_name.assign(name); 213 << process_information->dwProcessId;
131 } 214 return false;
132 } 215 }
133 } 216 }
134 217
135 // Use the default pipe name if we couldn't query its name. 218 if (!process_information->hThread) {
136 if (pipe_name.empty()) { 219 // N.B. THREAD_ALL_ACCESS is different in XP and Vista+ versions of
137 pipe_name = UTF8ToUTF16( 220 // the SDK. |desired_access| below is effectively THREAD_ALL_ACCESS from
138 StringPrintf(kCreateProcessDefaultPipeNameFormat, session_id)); 221 // the XP version of the SDK.
139 } 222 DWORD desired_access =
140 223 STANDARD_RIGHTS_REQUIRED |
141 // Try to connect to the named pipe. 224 SYNCHRONIZE |
142 base::win::ScopedHandle pipe; 225 THREAD_TERMINATE |
143 for (int i = 0; i < kPipeConnectMaxAttempts; ++i) { 226 THREAD_SUSPEND_RESUME |
144 pipe.Set(CreateFile(pipe_name.c_str(), 227 THREAD_GET_CONTEXT |
145 GENERIC_READ | GENERIC_WRITE, 228 THREAD_SET_CONTEXT |
146 0, 229 THREAD_QUERY_INFORMATION |
147 NULL, 230 THREAD_SET_INFORMATION |
148 OPEN_EXISTING, 231 THREAD_SET_THREAD_TOKEN |
149 0, 232 THREAD_IMPERSONATE |
150 NULL)); 233 THREAD_DIRECT_IMPERSONATION;
151 if (pipe.IsValid()) { 234 process_information->hThread =
152 break; 235 OpenThread(desired_access,
153 } 236 FALSE,
154 237 process_information->dwThreadId);
155 // Cannot continue retrying if error is something other than 238 if (!process_information->hThread) {
156 // ERROR_PIPE_BUSY. 239 LOG_GETLASTERROR(ERROR) << "Failed to open the thread "
157 if (GetLastError() != ERROR_PIPE_BUSY) { 240 << process_information->dwThreadId;
158 break; 241 return false;
159 }
160
161 // Cannot continue retrying if wait on pipe fails.
162 if (!WaitNamedPipe(pipe_name.c_str(), kPipeBusyWaitTimeoutMs)) {
163 break;
164 } 242 }
165 } 243 }
166 244
167 if (!pipe.IsValid()) { 245 // Resume the thread if the caller didn't want to suspend the process.
168 LOG_GETLASTERROR(ERROR) << "Failed to connect to '" << pipe_name << "'"; 246 if ((creation_flags & CREATE_SUSPENDED) == 0) {
247 if (!ResumeThread(process_information->hThread)) {
248 LOG_GETLASTERROR(ERROR) << "Failed to resume the thread "
249 << process_information->dwThreadId;
250 return false;
251 }
252 }
253
254 return true;
255 }
256
257 // Receives the response to a remote process create request.
258 bool ReceiveCreateProcessResponse(
259 HANDLE pipe,
260 PROCESS_INFORMATION* process_information_out) {
261 struct CreateProcessResponse {
262 DWORD size;
263 BOOL success;
264 DWORD last_error;
265 PROCESS_INFORMATION process_information;
266 };
267
268 DWORD bytes;
269 CreateProcessResponse response;
270 if (!ReadFile(pipe, &response, sizeof(response), &bytes, NULL)) {
271 LOG_GETLASTERROR(ERROR) << "Failed to receive CreateProcessAsUser response";
169 return false; 272 return false;
170 } 273 }
171 274
275 // The server sends the data in one chunk so if we didn't received a complete
276 // answer something bad happend and there is no point in retrying.
277 if (bytes != sizeof(response)) {
278 SetLastError(ERROR_RECEIVE_PARTIAL);
279 return false;
280 }
281
282 if (!response.success) {
283 SetLastError(response.last_error);
284 return false;
285 }
286
287 *process_information_out = response.process_information;
288 return true;
289 }
290
291 // Sends a remote process create request to the execution server.
292 bool SendCreateProcessRequest(
293 HANDLE pipe,
294 const FilePath::StringType& application_name,
295 const CommandLine::StringType& command_line,
296 DWORD creation_flags) {
172 string16 desktop_name(UTF8ToUTF16(kDefaultDesktopName)); 297 string16 desktop_name(UTF8ToUTF16(kDefaultDesktopName));
173 298
174 // |CreateProcessRequest| structure passes the same parameters to 299 // |CreateProcessRequest| structure passes the same parameters to
175 // the execution server as CreateProcessAsUser() function does. Strings are 300 // the execution server as CreateProcessAsUser() function does. Strings are
176 // stored as wide strings immediately after the structure. String pointers are 301 // stored as wide strings immediately after the structure. String pointers are
177 // represented as byte offsets to string data from the beginning of 302 // represented as byte offsets to string data from the beginning of
178 // the structure. 303 // the structure.
179 struct CreateProcessRequest { 304 struct CreateProcessRequest {
180 DWORD size; 305 DWORD size;
181 DWORD process_id; 306 DWORD process_id;
(...skipping 17 matching lines...) Expand all
199 (application_name.size() + command_line.size() + desktop_name.size() + 3); 324 (application_name.size() + command_line.size() + desktop_name.size() + 3);
200 scoped_array<char> buffer(new char[size]); 325 scoped_array<char> buffer(new char[size]);
201 memset(buffer.get(), 0, size); 326 memset(buffer.get(), 0, size);
202 327
203 // Marshal the input parameters. 328 // Marshal the input parameters.
204 CreateProcessRequest* request = 329 CreateProcessRequest* request =
205 reinterpret_cast<CreateProcessRequest*>(buffer.get()); 330 reinterpret_cast<CreateProcessRequest*>(buffer.get());
206 request->size = size; 331 request->size = size;
207 request->process_id = GetCurrentProcessId(); 332 request->process_id = GetCurrentProcessId();
208 request->use_default_token = TRUE; 333 request->use_default_token = TRUE;
209 request->creation_flags = creation_flags; 334 // Always pass CREATE_SUSPENDED to avoid a race between the created process
335 // exiting too soon and OpenProcess() call below.
336 request->creation_flags = creation_flags | CREATE_SUSPENDED;
210 request->startup_info.cb = sizeof(request->startup_info); 337 request->startup_info.cb = sizeof(request->startup_info);
211 338
212 size_t buffer_offset = sizeof(CreateProcessRequest); 339 size_t buffer_offset = sizeof(CreateProcessRequest);
213 340
214 request->application_name = reinterpret_cast<LPWSTR>(buffer_offset); 341 request->application_name = reinterpret_cast<LPWSTR>(buffer_offset);
215 std::copy(application_name.begin(), 342 std::copy(application_name.begin(),
216 application_name.end(), 343 application_name.end(),
217 reinterpret_cast<wchar_t*>(buffer.get() + buffer_offset)); 344 reinterpret_cast<wchar_t*>(buffer.get() + buffer_offset));
218 buffer_offset += (application_name.size() + 1) * sizeof(wchar_t); 345 buffer_offset += (application_name.size() + 1) * sizeof(wchar_t);
219 346
220 request->command_line = reinterpret_cast<LPWSTR>(buffer_offset); 347 request->command_line = reinterpret_cast<LPWSTR>(buffer_offset);
221 std::copy(command_line.begin(), 348 std::copy(command_line.begin(),
222 command_line.end(), 349 command_line.end(),
223 reinterpret_cast<wchar_t*>(buffer.get() + buffer_offset)); 350 reinterpret_cast<wchar_t*>(buffer.get() + buffer_offset));
224 buffer_offset += (command_line.size() + 1) * sizeof(wchar_t); 351 buffer_offset += (command_line.size() + 1) * sizeof(wchar_t);
225 352
226 request->startup_info.lpDesktop = 353 request->startup_info.lpDesktop =
227 reinterpret_cast<LPWSTR>(buffer_offset); 354 reinterpret_cast<LPWSTR>(buffer_offset);
228 std::copy(desktop_name.begin(), 355 std::copy(desktop_name.begin(),
229 desktop_name.end(), 356 desktop_name.end(),
230 reinterpret_cast<wchar_t*>(buffer.get() + buffer_offset)); 357 reinterpret_cast<wchar_t*>(buffer.get() + buffer_offset));
231 358
232 // Pass the request to create a process in the target session. 359 // Pass the request to create a process in the target session.
233 DWORD bytes; 360 DWORD bytes;
234 if (!WriteFile(pipe, buffer.get(), size, &bytes, NULL)) { 361 if (!WriteFile(pipe, buffer.get(), size, &bytes, NULL)) {
235 LOG_GETLASTERROR(ERROR) << "Failed to send CreateProcessAsUser request"; 362 LOG_GETLASTERROR(ERROR) << "Failed to send CreateProcessAsUser request";
236 return false; 363 return false;
237 } 364 }
238 365
239 // Receive the response. 366 return true;
240 struct CreateProcessResponse { 367 }
241 DWORD size;
242 BOOL success;
243 DWORD last_error;
244 PROCESS_INFORMATION process_information;
245 };
246 368
247 CreateProcessResponse response; 369 // Requests the execution server to create a process in the specified session
248 if (!ReadFile(pipe, &response, sizeof(response), &bytes, NULL)) { 370 // using the default (i.e. Winlogon) token. This routine relies on undocumented
249 LOG_GETLASTERROR(ERROR) << "Failed to receive CreateProcessAsUser response"; 371 // OS functionality and will likely not work on anything but XP or W2K3.
372 bool CreateRemoteSessionProcess(
373 uint32 session_id,
374 const FilePath::StringType& application_name,
375 const CommandLine::StringType& command_line,
376 DWORD creation_flags,
377 PROCESS_INFORMATION* process_information_out)
378 {
379 DCHECK(base::win::GetVersion() == base::win::VERSION_XP);
380
381 base::win::ScopedHandle pipe;
382 if (!ConnectToExecutionServer(session_id, &pipe))
383 return false;
384
385 if (!SendCreateProcessRequest(pipe, application_name, command_line,
386 creation_flags)) {
250 return false; 387 return false;
251 } 388 }
252 389
253 // The server sends the data in one chunk so if we didn't received a complete 390 PROCESS_INFORMATION process_information;
254 // answer something bad happend and there is no point in retrying. 391 if (!ReceiveCreateProcessResponse(pipe, &process_information))
255 if (bytes != sizeof(response)) { 392 return false;
256 SetLastError(ERROR_RECEIVE_PARTIAL); 393
394 if (!ProcessCreateProcessResponse(creation_flags, &process_information)) {
395 CloseHandlesAndTerminateProcess(&process_information);
257 return false; 396 return false;
258 } 397 }
259 398
260 if (!response.success) { 399 *process_information_out = process_information;
261 SetLastError(response.last_error);
262 return false;
263 }
264
265 // The execution server does not return handles to the created process and
266 // thread.
267 if (response.process_information.hProcess == NULL) {
268 // N.B. PROCESS_ALL_ACCESS is different in XP and Vista+ versions of
269 // the SDK. |desired_access| below is effectively PROCESS_ALL_ACCESS from
270 // the XP version of the SDK.
271 DWORD desired_access =
272 STANDARD_RIGHTS_REQUIRED |
273 SYNCHRONIZE |
274 PROCESS_TERMINATE |
275 PROCESS_CREATE_THREAD |
276 PROCESS_SET_SESSIONID |
277 PROCESS_VM_OPERATION |
278 PROCESS_VM_READ |
279 PROCESS_VM_WRITE |
280 PROCESS_DUP_HANDLE |
281 PROCESS_CREATE_PROCESS |
282 PROCESS_SET_QUOTA |
283 PROCESS_SET_INFORMATION |
284 PROCESS_QUERY_INFORMATION |
285 PROCESS_SUSPEND_RESUME;
286 response.process_information.hProcess =
287 OpenProcess(desired_access,
288 FALSE,
289 response.process_information.dwProcessId);
290 if (!response.process_information.hProcess) {
291 LOG_GETLASTERROR(ERROR) << "Failed to open process "
292 << response.process_information.dwProcessId;
293 return false;
294 }
295 }
296
297 *process_information_out = response.process_information;
298 return true; 400 return true;
299 } 401 }
300 402
301 } // namespace 403 } // namespace
302 404
303 namespace remoting { 405 namespace remoting {
304 406
305 // Creates a copy of the current process token for the given |session_id| so 407 // Creates a copy of the current process token for the given |session_id| so
306 // it can be used to launch a process in that session. 408 // it can be used to launch a process in that session.
307 bool CreateSessionToken(uint32 session_id, ScopedHandle* token_out) { 409 bool CreateSessionToken(uint32 session_id, ScopedHandle* token_out) {
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
408 return false; 510 return false;
409 } 511 }
410 512
411 CHECK(process_info.IsValid()); 513 CHECK(process_info.IsValid());
412 process_out->Set(process_info.TakeProcessHandle()); 514 process_out->Set(process_info.TakeProcessHandle());
413 thread_out->Set(process_info.TakeThreadHandle()); 515 thread_out->Set(process_info.TakeThreadHandle());
414 return true; 516 return true;
415 } 517 }
416 518
417 } // namespace remoting 519 } // namespace remoting
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698