OLD | NEW |
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 <limits> | 10 #include <limits> |
(...skipping 23 matching lines...) Expand all Loading... |
34 static_cast<WINSTATIONINFOCLASS>(0x21); | 34 static_cast<WINSTATIONINFOCLASS>(0x21); |
35 | 35 |
36 const int kPipeBusyWaitTimeoutMs = 2000; | 36 const int kPipeBusyWaitTimeoutMs = 2000; |
37 const int kPipeConnectMaxAttempts = 3; | 37 const int kPipeConnectMaxAttempts = 3; |
38 | 38 |
39 // Terminates the process and closes process and thread handles in | 39 // Terminates the process and closes process and thread handles in |
40 // |process_information| structure. | 40 // |process_information| structure. |
41 void CloseHandlesAndTerminateProcess(PROCESS_INFORMATION* process_information) { | 41 void CloseHandlesAndTerminateProcess(PROCESS_INFORMATION* process_information) { |
42 if (process_information->hThread) { | 42 if (process_information->hThread) { |
43 CloseHandle(process_information->hThread); | 43 CloseHandle(process_information->hThread); |
44 process_information->hThread = NULL; | 44 process_information->hThread = nullptr; |
45 } | 45 } |
46 | 46 |
47 if (process_information->hProcess) { | 47 if (process_information->hProcess) { |
48 TerminateProcess(process_information->hProcess, CONTROL_C_EXIT); | 48 TerminateProcess(process_information->hProcess, CONTROL_C_EXIT); |
49 CloseHandle(process_information->hProcess); | 49 CloseHandle(process_information->hProcess); |
50 process_information->hProcess = NULL; | 50 process_information->hProcess = nullptr; |
51 } | 51 } |
52 } | 52 } |
53 | 53 |
54 // Connects to the executor server corresponding to |session_id|. | 54 // Connects to the executor server corresponding to |session_id|. |
55 bool ConnectToExecutionServer(uint32 session_id, | 55 bool ConnectToExecutionServer(uint32 session_id, |
56 base::win::ScopedHandle* pipe_out) { | 56 base::win::ScopedHandle* pipe_out) { |
57 base::string16 pipe_name; | 57 base::string16 pipe_name; |
58 | 58 |
59 // Use winsta!WinStationQueryInformationW() to determine the process creation | 59 // Use winsta!WinStationQueryInformationW() to determine the process creation |
60 // pipe name for the session. | 60 // pipe name for the session. |
(...skipping 23 matching lines...) Expand all Loading... |
84 pipe_name = base::UTF8ToUTF16( | 84 pipe_name = base::UTF8ToUTF16( |
85 base::StringPrintf(kCreateProcessDefaultPipeNameFormat, session_id)); | 85 base::StringPrintf(kCreateProcessDefaultPipeNameFormat, session_id)); |
86 } | 86 } |
87 | 87 |
88 // Try to connect to the named pipe. | 88 // Try to connect to the named pipe. |
89 base::win::ScopedHandle pipe; | 89 base::win::ScopedHandle pipe; |
90 for (int i = 0; i < kPipeConnectMaxAttempts; ++i) { | 90 for (int i = 0; i < kPipeConnectMaxAttempts; ++i) { |
91 pipe.Set(CreateFile(pipe_name.c_str(), | 91 pipe.Set(CreateFile(pipe_name.c_str(), |
92 GENERIC_READ | GENERIC_WRITE, | 92 GENERIC_READ | GENERIC_WRITE, |
93 0, | 93 0, |
94 NULL, | 94 nullptr, |
95 OPEN_EXISTING, | 95 OPEN_EXISTING, |
96 0, | 96 0, |
97 NULL)); | 97 nullptr)); |
98 if (pipe.IsValid()) { | 98 if (pipe.IsValid()) { |
99 break; | 99 break; |
100 } | 100 } |
101 | 101 |
102 // Cannot continue retrying if error is something other than | 102 // Cannot continue retrying if error is something other than |
103 // ERROR_PIPE_BUSY. | 103 // ERROR_PIPE_BUSY. |
104 if (GetLastError() != ERROR_PIPE_BUSY) { | 104 if (GetLastError() != ERROR_PIPE_BUSY) { |
105 break; | 105 break; |
106 } | 106 } |
107 | 107 |
(...skipping 19 matching lines...) Expand all Loading... |
127 if (!OpenProcessToken(GetCurrentProcess(), | 127 if (!OpenProcessToken(GetCurrentProcess(), |
128 TOKEN_DUPLICATE | desired_access, | 128 TOKEN_DUPLICATE | desired_access, |
129 &temp_handle)) { | 129 &temp_handle)) { |
130 PLOG(ERROR) << "Failed to open process token"; | 130 PLOG(ERROR) << "Failed to open process token"; |
131 return false; | 131 return false; |
132 } | 132 } |
133 ScopedHandle process_token(temp_handle); | 133 ScopedHandle process_token(temp_handle); |
134 | 134 |
135 if (!DuplicateTokenEx(process_token.Get(), | 135 if (!DuplicateTokenEx(process_token.Get(), |
136 desired_access, | 136 desired_access, |
137 NULL, | 137 nullptr, |
138 SecurityImpersonation, | 138 SecurityImpersonation, |
139 TokenPrimary, | 139 TokenPrimary, |
140 &temp_handle)) { | 140 &temp_handle)) { |
141 PLOG(ERROR) << "Failed to duplicate the process token"; | 141 PLOG(ERROR) << "Failed to duplicate the process token"; |
142 return false; | 142 return false; |
143 } | 143 } |
144 | 144 |
145 token_out->Set(temp_handle); | 145 token_out->Set(temp_handle); |
146 return true; | 146 return true; |
147 } | 147 } |
148 | 148 |
149 // Creates a copy of the current process with SE_TCB_NAME privilege enabled. | 149 // Creates a copy of the current process with SE_TCB_NAME privilege enabled. |
150 bool CreatePrivilegedToken(ScopedHandle* token_out) { | 150 bool CreatePrivilegedToken(ScopedHandle* token_out) { |
151 ScopedHandle privileged_token; | 151 ScopedHandle privileged_token; |
152 DWORD desired_access = TOKEN_ADJUST_PRIVILEGES | TOKEN_IMPERSONATE | | 152 DWORD desired_access = TOKEN_ADJUST_PRIVILEGES | TOKEN_IMPERSONATE | |
153 TOKEN_DUPLICATE | TOKEN_QUERY; | 153 TOKEN_DUPLICATE | TOKEN_QUERY; |
154 if (!CopyProcessToken(desired_access, &privileged_token)) { | 154 if (!CopyProcessToken(desired_access, &privileged_token)) { |
155 return false; | 155 return false; |
156 } | 156 } |
157 | 157 |
158 // Get the LUID for the SE_TCB_NAME privilege. | 158 // Get the LUID for the SE_TCB_NAME privilege. |
159 TOKEN_PRIVILEGES state; | 159 TOKEN_PRIVILEGES state; |
160 state.PrivilegeCount = 1; | 160 state.PrivilegeCount = 1; |
161 state.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; | 161 state.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; |
162 if (!LookupPrivilegeValue(NULL, SE_TCB_NAME, &state.Privileges[0].Luid)) { | 162 if (!LookupPrivilegeValue(nullptr, SE_TCB_NAME, &state.Privileges[0].Luid)) { |
163 PLOG(ERROR) << "Failed to lookup the LUID for the SE_TCB_NAME privilege"; | 163 PLOG(ERROR) << "Failed to lookup the LUID for the SE_TCB_NAME privilege"; |
164 return false; | 164 return false; |
165 } | 165 } |
166 | 166 |
167 // Enable the SE_TCB_NAME privilege. | 167 // Enable the SE_TCB_NAME privilege. |
168 if (!AdjustTokenPrivileges(privileged_token.Get(), FALSE, &state, 0, NULL, | 168 if (!AdjustTokenPrivileges(privileged_token.Get(), FALSE, &state, 0, nullptr, |
169 0)) { | 169 0)) { |
170 PLOG(ERROR) << "Failed to enable SE_TCB_NAME privilege in a token"; | 170 PLOG(ERROR) << "Failed to enable SE_TCB_NAME privilege in a token"; |
171 return false; | 171 return false; |
172 } | 172 } |
173 | 173 |
174 *token_out = privileged_token.Pass(); | 174 *token_out = privileged_token.Pass(); |
175 return true; | 175 return true; |
176 } | 176 } |
177 | 177 |
178 // Fills the process and thread handles in the passed |process_information| | 178 // Fills the process and thread handles in the passed |process_information| |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
256 PROCESS_INFORMATION* process_information_out) { | 256 PROCESS_INFORMATION* process_information_out) { |
257 struct CreateProcessResponse { | 257 struct CreateProcessResponse { |
258 DWORD size; | 258 DWORD size; |
259 BOOL success; | 259 BOOL success; |
260 DWORD last_error; | 260 DWORD last_error; |
261 PROCESS_INFORMATION process_information; | 261 PROCESS_INFORMATION process_information; |
262 }; | 262 }; |
263 | 263 |
264 DWORD bytes; | 264 DWORD bytes; |
265 CreateProcessResponse response; | 265 CreateProcessResponse response; |
266 if (!ReadFile(pipe, &response, sizeof(response), &bytes, NULL)) { | 266 if (!ReadFile(pipe, &response, sizeof(response), &bytes, nullptr)) { |
267 PLOG(ERROR) << "Failed to receive CreateProcessAsUser response"; | 267 PLOG(ERROR) << "Failed to receive CreateProcessAsUser response"; |
268 return false; | 268 return false; |
269 } | 269 } |
270 | 270 |
271 // The server sends the data in one chunk so if we didn't received a complete | 271 // The server sends the data in one chunk so if we didn't received a complete |
272 // answer something bad happend and there is no point in retrying. | 272 // answer something bad happend and there is no point in retrying. |
273 if (bytes != sizeof(response)) { | 273 if (bytes != sizeof(response)) { |
274 SetLastError(ERROR_RECEIVE_PARTIAL); | 274 SetLastError(ERROR_RECEIVE_PARTIAL); |
275 return false; | 275 return false; |
276 } | 276 } |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
311 LPWSTR current_directory; | 311 LPWSTR current_directory; |
312 STARTUPINFOW startup_info; | 312 STARTUPINFOW startup_info; |
313 PROCESS_INFORMATION process_information; | 313 PROCESS_INFORMATION process_information; |
314 }; | 314 }; |
315 | 315 |
316 base::string16 desktop; | 316 base::string16 desktop; |
317 if (desktop_name) | 317 if (desktop_name) |
318 desktop = desktop_name; | 318 desktop = desktop_name; |
319 | 319 |
320 // Allocate a large enough buffer to hold the CreateProcessRequest structure | 320 // Allocate a large enough buffer to hold the CreateProcessRequest structure |
321 // and three NULL-terminated string parameters. | 321 // and three nullptr-terminated string parameters. |
322 size_t size = sizeof(CreateProcessRequest) + sizeof(wchar_t) * | 322 size_t size = sizeof(CreateProcessRequest) + sizeof(wchar_t) * |
323 (application_name.size() + command_line.size() + desktop.size() + 3); | 323 (application_name.size() + command_line.size() + desktop.size() + 3); |
324 scoped_ptr<char[]> buffer(new char[size]); | 324 scoped_ptr<char[]> buffer(new char[size]); |
325 memset(buffer.get(), 0, size); | 325 memset(buffer.get(), 0, size); |
326 | 326 |
327 // Marshal the input parameters. | 327 // Marshal the input parameters. |
328 CreateProcessRequest* request = | 328 CreateProcessRequest* request = |
329 reinterpret_cast<CreateProcessRequest*>(buffer.get()); | 329 reinterpret_cast<CreateProcessRequest*>(buffer.get()); |
330 request->size = size; | 330 request->size = size; |
331 request->process_id = GetCurrentProcessId(); | 331 request->process_id = GetCurrentProcessId(); |
(...skipping 18 matching lines...) Expand all Loading... |
350 buffer_offset += (command_line.size() + 1) * sizeof(wchar_t); | 350 buffer_offset += (command_line.size() + 1) * sizeof(wchar_t); |
351 | 351 |
352 request->startup_info.lpDesktop = | 352 request->startup_info.lpDesktop = |
353 reinterpret_cast<LPWSTR>(buffer_offset); | 353 reinterpret_cast<LPWSTR>(buffer_offset); |
354 std::copy(desktop.begin(), | 354 std::copy(desktop.begin(), |
355 desktop.end(), | 355 desktop.end(), |
356 reinterpret_cast<wchar_t*>(buffer.get() + buffer_offset)); | 356 reinterpret_cast<wchar_t*>(buffer.get() + buffer_offset)); |
357 | 357 |
358 // Pass the request to create a process in the target session. | 358 // Pass the request to create a process in the target session. |
359 DWORD bytes; | 359 DWORD bytes; |
360 if (!WriteFile(pipe, buffer.get(), size, &bytes, NULL)) { | 360 if (!WriteFile(pipe, buffer.get(), size, &bytes, nullptr)) { |
361 PLOG(ERROR) << "Failed to send CreateProcessAsUser request"; | 361 PLOG(ERROR) << "Failed to send CreateProcessAsUser request"; |
362 return false; | 362 return false; |
363 } | 363 } |
364 | 364 |
365 return true; | 365 return true; |
366 } | 366 } |
367 | 367 |
368 // Requests the execution server to create a process in the specified session | 368 // Requests the execution server to create a process in the specified session |
369 // using the default (i.e. Winlogon) token. This routine relies on undocumented | 369 // using the default (i.e. Winlogon) token. This routine relies on undocumented |
370 // OS functionality and will likely not work on anything but XP or W2K3. | 370 // OS functionality and will likely not work on anything but XP or W2K3. |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
466 startup_info.lpDesktop = const_cast<base::char16*>(desktop_name); | 466 startup_info.lpDesktop = const_cast<base::char16*>(desktop_name); |
467 | 467 |
468 PROCESS_INFORMATION temp_process_info = {}; | 468 PROCESS_INFORMATION temp_process_info = {}; |
469 BOOL result = CreateProcessAsUser(user_token, | 469 BOOL result = CreateProcessAsUser(user_token, |
470 application_name.c_str(), | 470 application_name.c_str(), |
471 const_cast<LPWSTR>(command_line.c_str()), | 471 const_cast<LPWSTR>(command_line.c_str()), |
472 process_attributes, | 472 process_attributes, |
473 thread_attributes, | 473 thread_attributes, |
474 inherit_handles, | 474 inherit_handles, |
475 creation_flags, | 475 creation_flags, |
476 NULL, | 476 nullptr, |
477 NULL, | 477 nullptr, |
478 &startup_info, | 478 &startup_info, |
479 &temp_process_info); | 479 &temp_process_info); |
480 | 480 |
481 // CreateProcessAsUser will fail on XP and W2K3 with ERROR_PIPE_NOT_CONNECTED | 481 // CreateProcessAsUser will fail on XP and W2K3 with ERROR_PIPE_NOT_CONNECTED |
482 // if the user hasn't logged to the target session yet. In such a case | 482 // if the user hasn't logged to the target session yet. In such a case |
483 // we try to talk to the execution server directly emulating what | 483 // we try to talk to the execution server directly emulating what |
484 // the undocumented and not-exported advapi32!CreateRemoteSessionProcessW() | 484 // the undocumented and not-exported advapi32!CreateRemoteSessionProcessW() |
485 // function does. The created process will run under Winlogon'a token instead | 485 // function does. The created process will run under Winlogon'a token instead |
486 // of |user_token|. Since Winlogon runs as SYSTEM, this suits our needs. | 486 // of |user_token|. Since Winlogon runs as SYSTEM, this suits our needs. |
487 if (!result && | 487 if (!result && |
(...skipping 27 matching lines...) Expand all Loading... |
515 | 515 |
516 base::win::ScopedProcessInformation process_info(temp_process_info); | 516 base::win::ScopedProcessInformation process_info(temp_process_info); |
517 | 517 |
518 CHECK(process_info.IsValid()); | 518 CHECK(process_info.IsValid()); |
519 process_out->Set(process_info.TakeProcessHandle()); | 519 process_out->Set(process_info.TakeProcessHandle()); |
520 thread_out->Set(process_info.TakeThreadHandle()); | 520 thread_out->Set(process_info.TakeThreadHandle()); |
521 return true; | 521 return true; |
522 } | 522 } |
523 | 523 |
524 } // namespace remoting | 524 } // namespace remoting |
OLD | NEW |