| 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 |