| Index: remoting/host/win/launch_process_with_token.cc
|
| diff --git a/remoting/host/win/launch_process_with_token.cc b/remoting/host/win/launch_process_with_token.cc
|
| index 04a6660dfabace20b536c7a4940491b1cd249e27..5a921d6a64065508ae3aa96d70bf8d09fa938f4a 100644
|
| --- a/remoting/host/win/launch_process_with_token.cc
|
| +++ b/remoting/host/win/launch_process_with_token.cc
|
| @@ -5,123 +5,17 @@
|
| #include "remoting/host/win/launch_process_with_token.h"
|
|
|
| #include <windows.h>
|
| -#include <stddef.h>
|
| -#include <winternl.h>
|
|
|
| -#include <limits>
|
| -#include <memory>
|
| #include <utility>
|
|
|
| #include "base/logging.h"
|
| -#include "base/rand_util.h"
|
| -#include "base/scoped_native_library.h"
|
| -#include "base/strings/string16.h"
|
| -#include "base/strings/stringprintf.h"
|
| -#include "base/strings/utf_string_conversions.h"
|
| #include "base/win/scoped_handle.h"
|
| #include "base/win/scoped_process_information.h"
|
| -#include "base/win/windows_version.h"
|
|
|
| using base::win::ScopedHandle;
|
|
|
| namespace {
|
|
|
| -const char kCreateProcessDefaultPipeNameFormat[] =
|
| - "\\\\.\\Pipe\\TerminalServer\\SystemExecSrvr\\%d";
|
| -
|
| -// Undocumented WINSTATIONINFOCLASS value causing
|
| -// winsta!WinStationQueryInformationW() to return the name of the pipe for
|
| -// requesting cross-session process creation.
|
| -const WINSTATIONINFOCLASS kCreateProcessPipeNameClass =
|
| - static_cast<WINSTATIONINFOCLASS>(0x21);
|
| -
|
| -const int kPipeBusyWaitTimeoutMs = 2000;
|
| -const int kPipeConnectMaxAttempts = 3;
|
| -
|
| -// Terminates the process and closes process and thread handles in
|
| -// |process_information| structure.
|
| -void CloseHandlesAndTerminateProcess(PROCESS_INFORMATION* process_information) {
|
| - if (process_information->hThread) {
|
| - CloseHandle(process_information->hThread);
|
| - process_information->hThread = nullptr;
|
| - }
|
| -
|
| - if (process_information->hProcess) {
|
| - TerminateProcess(process_information->hProcess, CONTROL_C_EXIT);
|
| - CloseHandle(process_information->hProcess);
|
| - process_information->hProcess = nullptr;
|
| - }
|
| -}
|
| -
|
| -// Connects to the executor server corresponding to |session_id|.
|
| -bool ConnectToExecutionServer(uint32_t session_id,
|
| - base::win::ScopedHandle* pipe_out) {
|
| - base::string16 pipe_name;
|
| -
|
| - // Use winsta!WinStationQueryInformationW() to determine the process creation
|
| - // pipe name for the session.
|
| - base::FilePath winsta_path(
|
| - base::GetNativeLibraryName(base::UTF8ToUTF16("winsta")));
|
| - base::ScopedNativeLibrary winsta(winsta_path);
|
| - if (winsta.is_valid()) {
|
| - PWINSTATIONQUERYINFORMATIONW win_station_query_information =
|
| - reinterpret_cast<PWINSTATIONQUERYINFORMATIONW>(
|
| - winsta.GetFunctionPointer("WinStationQueryInformationW"));
|
| - if (win_station_query_information) {
|
| - wchar_t name[MAX_PATH];
|
| - ULONG name_length;
|
| - if (win_station_query_information(0,
|
| - session_id,
|
| - kCreateProcessPipeNameClass,
|
| - name,
|
| - sizeof(name),
|
| - &name_length)) {
|
| - pipe_name.assign(name);
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Use the default pipe name if we couldn't query its name.
|
| - if (pipe_name.empty()) {
|
| - pipe_name = base::UTF8ToUTF16(
|
| - base::StringPrintf(kCreateProcessDefaultPipeNameFormat, session_id));
|
| - }
|
| -
|
| - // Try to connect to the named pipe.
|
| - base::win::ScopedHandle pipe;
|
| - for (int i = 0; i < kPipeConnectMaxAttempts; ++i) {
|
| - pipe.Set(CreateFile(pipe_name.c_str(),
|
| - GENERIC_READ | GENERIC_WRITE,
|
| - 0,
|
| - nullptr,
|
| - OPEN_EXISTING,
|
| - 0,
|
| - nullptr));
|
| - if (pipe.IsValid()) {
|
| - break;
|
| - }
|
| -
|
| - // Cannot continue retrying if error is something other than
|
| - // ERROR_PIPE_BUSY.
|
| - if (GetLastError() != ERROR_PIPE_BUSY) {
|
| - break;
|
| - }
|
| -
|
| - // Cannot continue retrying if wait on pipe fails.
|
| - if (!WaitNamedPipe(pipe_name.c_str(), kPipeBusyWaitTimeoutMs)) {
|
| - break;
|
| - }
|
| - }
|
| -
|
| - if (!pipe.IsValid()) {
|
| - PLOG(ERROR) << "Failed to connect to '" << pipe_name << "'";
|
| - return false;
|
| - }
|
| -
|
| - *pipe_out = std::move(pipe);
|
| - return true;
|
| -}
|
| -
|
| // Copies the process token making it a primary impersonation token.
|
| // The returned handle will have |desired_access| rights.
|
| bool CopyProcessToken(DWORD desired_access, ScopedHandle* token_out) {
|
| @@ -177,230 +71,6 @@ bool CreatePrivilegedToken(ScopedHandle* token_out) {
|
| return true;
|
| }
|
|
|
| -// Fills the process and thread handles in the passed |process_information|
|
| -// structure and resume the process if the caller didn't want to suspend it.
|
| -bool ProcessCreateProcessResponse(DWORD creation_flags,
|
| - PROCESS_INFORMATION* process_information) {
|
| - // The execution server does not return handles to the created process and
|
| - // thread.
|
| - if (!process_information->hProcess) {
|
| - // N.B. PROCESS_ALL_ACCESS is different in XP and Vista+ versions of
|
| - // the SDK. |desired_access| below is effectively PROCESS_ALL_ACCESS from
|
| - // the XP version of the SDK.
|
| - DWORD desired_access =
|
| - STANDARD_RIGHTS_REQUIRED |
|
| - SYNCHRONIZE |
|
| - PROCESS_TERMINATE |
|
| - PROCESS_CREATE_THREAD |
|
| - PROCESS_SET_SESSIONID |
|
| - PROCESS_VM_OPERATION |
|
| - PROCESS_VM_READ |
|
| - PROCESS_VM_WRITE |
|
| - PROCESS_DUP_HANDLE |
|
| - PROCESS_CREATE_PROCESS |
|
| - PROCESS_SET_QUOTA |
|
| - PROCESS_SET_INFORMATION |
|
| - PROCESS_QUERY_INFORMATION |
|
| - PROCESS_SUSPEND_RESUME;
|
| - process_information->hProcess =
|
| - OpenProcess(desired_access,
|
| - FALSE,
|
| - process_information->dwProcessId);
|
| - if (!process_information->hProcess) {
|
| - PLOG(ERROR) << "Failed to open the process "
|
| - << process_information->dwProcessId;
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - if (!process_information->hThread) {
|
| - // N.B. THREAD_ALL_ACCESS is different in XP and Vista+ versions of
|
| - // the SDK. |desired_access| below is effectively THREAD_ALL_ACCESS from
|
| - // the XP version of the SDK.
|
| - DWORD desired_access =
|
| - STANDARD_RIGHTS_REQUIRED |
|
| - SYNCHRONIZE |
|
| - THREAD_TERMINATE |
|
| - THREAD_SUSPEND_RESUME |
|
| - THREAD_GET_CONTEXT |
|
| - THREAD_SET_CONTEXT |
|
| - THREAD_QUERY_INFORMATION |
|
| - THREAD_SET_INFORMATION |
|
| - THREAD_SET_THREAD_TOKEN |
|
| - THREAD_IMPERSONATE |
|
| - THREAD_DIRECT_IMPERSONATION;
|
| - process_information->hThread =
|
| - OpenThread(desired_access,
|
| - FALSE,
|
| - process_information->dwThreadId);
|
| - if (!process_information->hThread) {
|
| - PLOG(ERROR) << "Failed to open the thread "
|
| - << process_information->dwThreadId;
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - // Resume the thread if the caller didn't want to suspend the process.
|
| - if ((creation_flags & CREATE_SUSPENDED) == 0) {
|
| - if (!ResumeThread(process_information->hThread)) {
|
| - PLOG(ERROR) << "Failed to resume the thread "
|
| - << process_information->dwThreadId;
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -// Receives the response to a remote process create request.
|
| -bool ReceiveCreateProcessResponse(
|
| - HANDLE pipe,
|
| - PROCESS_INFORMATION* process_information_out) {
|
| - struct CreateProcessResponse {
|
| - DWORD size;
|
| - BOOL success;
|
| - DWORD last_error;
|
| - PROCESS_INFORMATION process_information;
|
| - };
|
| -
|
| - DWORD bytes;
|
| - CreateProcessResponse response;
|
| - if (!ReadFile(pipe, &response, sizeof(response), &bytes, nullptr)) {
|
| - PLOG(ERROR) << "Failed to receive CreateProcessAsUser response";
|
| - return false;
|
| - }
|
| -
|
| - // The server sends the data in one chunk so if we didn't received a complete
|
| - // answer something bad happend and there is no point in retrying.
|
| - if (bytes != sizeof(response)) {
|
| - SetLastError(ERROR_RECEIVE_PARTIAL);
|
| - return false;
|
| - }
|
| -
|
| - if (!response.success) {
|
| - SetLastError(response.last_error);
|
| - return false;
|
| - }
|
| -
|
| - *process_information_out = response.process_information;
|
| - return true;
|
| -}
|
| -
|
| -// Sends a remote process create request to the execution server.
|
| -bool SendCreateProcessRequest(
|
| - HANDLE pipe,
|
| - const base::FilePath::StringType& application_name,
|
| - const base::CommandLine::StringType& command_line,
|
| - DWORD creation_flags,
|
| - const base::char16* desktop_name) {
|
| - // |CreateProcessRequest| structure passes the same parameters to
|
| - // the execution server as CreateProcessAsUser() function does. Strings are
|
| - // stored as wide strings immediately after the structure. String pointers are
|
| - // represented as byte offsets to string data from the beginning of
|
| - // the structure.
|
| - struct CreateProcessRequest {
|
| - DWORD size;
|
| - DWORD process_id;
|
| - BOOL use_default_token;
|
| - HANDLE token;
|
| - LPWSTR application_name;
|
| - LPWSTR command_line;
|
| - SECURITY_ATTRIBUTES process_attributes;
|
| - SECURITY_ATTRIBUTES thread_attributes;
|
| - BOOL inherit_handles;
|
| - DWORD creation_flags;
|
| - LPVOID environment;
|
| - LPWSTR current_directory;
|
| - STARTUPINFOW startup_info;
|
| - PROCESS_INFORMATION process_information;
|
| - };
|
| -
|
| - base::string16 desktop;
|
| - if (desktop_name)
|
| - desktop = desktop_name;
|
| -
|
| - // Allocate a large enough buffer to hold the CreateProcessRequest structure
|
| - // and three nullptr-terminated string parameters.
|
| - size_t size = sizeof(CreateProcessRequest) + sizeof(wchar_t) *
|
| - (application_name.size() + command_line.size() + desktop.size() + 3);
|
| - std::unique_ptr<char[]> buffer(new char[size]);
|
| - memset(buffer.get(), 0, size);
|
| -
|
| - // Marshal the input parameters.
|
| - CreateProcessRequest* request =
|
| - reinterpret_cast<CreateProcessRequest*>(buffer.get());
|
| - request->size = size;
|
| - request->process_id = GetCurrentProcessId();
|
| - request->use_default_token = TRUE;
|
| - // Always pass CREATE_SUSPENDED to avoid a race between the created process
|
| - // exiting too soon and OpenProcess() call below.
|
| - request->creation_flags = creation_flags | CREATE_SUSPENDED;
|
| - request->startup_info.cb = sizeof(request->startup_info);
|
| -
|
| - size_t buffer_offset = sizeof(CreateProcessRequest);
|
| -
|
| - request->application_name = reinterpret_cast<LPWSTR>(buffer_offset);
|
| - std::copy(application_name.begin(),
|
| - application_name.end(),
|
| - reinterpret_cast<wchar_t*>(buffer.get() + buffer_offset));
|
| - buffer_offset += (application_name.size() + 1) * sizeof(wchar_t);
|
| -
|
| - request->command_line = reinterpret_cast<LPWSTR>(buffer_offset);
|
| - std::copy(command_line.begin(),
|
| - command_line.end(),
|
| - reinterpret_cast<wchar_t*>(buffer.get() + buffer_offset));
|
| - buffer_offset += (command_line.size() + 1) * sizeof(wchar_t);
|
| -
|
| - request->startup_info.lpDesktop =
|
| - reinterpret_cast<LPWSTR>(buffer_offset);
|
| - std::copy(desktop.begin(),
|
| - desktop.end(),
|
| - reinterpret_cast<wchar_t*>(buffer.get() + buffer_offset));
|
| -
|
| - // Pass the request to create a process in the target session.
|
| - DWORD bytes;
|
| - if (!WriteFile(pipe, buffer.get(), size, &bytes, nullptr)) {
|
| - PLOG(ERROR) << "Failed to send CreateProcessAsUser request";
|
| - return false;
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -// Requests the execution server to create a process in the specified session
|
| -// using the default (i.e. Winlogon) token. This routine relies on undocumented
|
| -// OS functionality and will likely not work on anything but XP or W2K3.
|
| -bool CreateRemoteSessionProcess(
|
| - uint32_t session_id,
|
| - const base::FilePath::StringType& application_name,
|
| - const base::CommandLine::StringType& command_line,
|
| - DWORD creation_flags,
|
| - const base::char16* desktop_name,
|
| - PROCESS_INFORMATION* process_information_out) {
|
| - DCHECK_LT(base::win::GetVersion(), base::win::VERSION_VISTA);
|
| -
|
| - base::win::ScopedHandle pipe;
|
| - if (!ConnectToExecutionServer(session_id, &pipe))
|
| - return false;
|
| -
|
| - if (!SendCreateProcessRequest(pipe.Get(), application_name, command_line,
|
| - creation_flags, desktop_name)) {
|
| - return false;
|
| - }
|
| -
|
| - PROCESS_INFORMATION process_information;
|
| - if (!ReceiveCreateProcessResponse(pipe.Get(), &process_information))
|
| - return false;
|
| -
|
| - if (!ProcessCreateProcessResponse(creation_flags, &process_information)) {
|
| - CloseHandlesAndTerminateProcess(&process_information);
|
| - return false;
|
| - }
|
| -
|
| - *process_information_out = process_information;
|
| - return true;
|
| -}
|
| -
|
| } // namespace
|
|
|
| namespace remoting {
|
| @@ -480,36 +150,6 @@ bool LaunchProcessWithToken(const base::FilePath& binary,
|
| &startup_info,
|
| &temp_process_info);
|
|
|
| - // CreateProcessAsUser will fail on XP and W2K3 with ERROR_PIPE_NOT_CONNECTED
|
| - // if the user hasn't logged to the target session yet. In such a case
|
| - // we try to talk to the execution server directly emulating what
|
| - // the undocumented and not-exported advapi32!CreateRemoteSessionProcessW()
|
| - // function does. The created process will run under Winlogon'a token instead
|
| - // of |user_token|. Since Winlogon runs as SYSTEM, this suits our needs.
|
| - if (!result &&
|
| - GetLastError() == ERROR_PIPE_NOT_CONNECTED &&
|
| - base::win::GetVersion() < base::win::VERSION_VISTA) {
|
| - DWORD session_id;
|
| - DWORD return_length;
|
| - result = GetTokenInformation(user_token,
|
| - TokenSessionId,
|
| - &session_id,
|
| - sizeof(session_id),
|
| - &return_length);
|
| - if (result && session_id != 0) {
|
| - result = CreateRemoteSessionProcess(session_id,
|
| - application_name,
|
| - command_line,
|
| - creation_flags,
|
| - desktop_name,
|
| - &temp_process_info);
|
| - } else {
|
| - // Restore the error status returned by CreateProcessAsUser().
|
| - result = FALSE;
|
| - SetLastError(ERROR_PIPE_NOT_CONNECTED);
|
| - }
|
| - }
|
| -
|
| if (!result) {
|
| PLOG(ERROR) << "Failed to launch a process with a user token";
|
| return false;
|
|
|