diff --git a/src/include/winpty_constants.h b/src/include/winpty_constants.h index 11e34cf1..83b5644f 100755 --- a/src/include/winpty_constants.h +++ b/src/include/winpty_constants.h @@ -76,11 +76,18 @@ * See https://github.com/rprichard/winpty/issues/58. */ #define WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION 0x8ull + /* Create agent process from the thread token instead of current process. + * This is useful when winpty.dll is called by Windows Service program to + * impersonate to the original user from a remote process. + * See https://github.com/rprichard/winpty/issues/132. */ +#define WINPTY_FLAG_IMPERSONATE_THREAD 0x10ull + #define WINPTY_FLAG_MASK (0ull \ | WINPTY_FLAG_CONERR \ | WINPTY_FLAG_PLAIN_OUTPUT \ | WINPTY_FLAG_COLOR_ESCAPES \ | WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION \ + | WINPTY_FLAG_IMPERSONATE_THREAD \ ) /* QuickEdit mode is initially disabled, and the agent does not send mouse diff --git a/src/libwinpty/winpty.cc b/src/libwinpty/winpty.cc index 3d977498..0c6c1960 100644 --- a/src/libwinpty/winpty.cc +++ b/src/libwinpty/winpty.cc @@ -470,6 +470,7 @@ static bool shouldSpecifyHideFlag() { } static OwnedHandle startAgentProcess( + const winpty_config_t *cfg, const std::wstring &desktop, const std::wstring &controlPipeName, const std::wstring ¶ms, @@ -494,24 +495,93 @@ static OwnedHandle startAgentProcess( sui.dwFlags |= STARTF_USESHOWWINDOW; sui.wShowWindow = SW_HIDE; } + PROCESS_INFORMATION pi = {}; - const BOOL success = - CreateProcessW(exePath.c_str(), - cmdlineV.data(), - nullptr, nullptr, - /*bInheritHandles=*/FALSE, - /*dwCreationFlags=*/creationFlags, - nullptr, nullptr, - &sui, &pi); - if (!success) { - const DWORD lastError = GetLastError(); - const auto errStr = - (WStringBuilder(256) - << L"winpty-agent CreateProcess failed: cmdline='" << cmdline - << L"' err=0x" << whexOfInt(lastError)).str_moved(); - throw LibWinptyException( - WINPTY_ERROR_AGENT_CREATION_FAILED, errStr.c_str()); + if (cfg->flags & WINPTY_FLAG_IMPERSONATE_THREAD) { + HRESULT hr; + HANDLE token = nullptr; + if (!OpenThreadToken( + GetCurrentThread(), + TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, + FALSE, + &token)) + { + const DWORD lastError = GetLastError(); + const auto errStr = + (WStringBuilder(256) + << L"winpty-agent OpenThreadToken failed: cmdline='" << cmdline + << L"' err=0x" << whexOfInt(lastError)).str_moved(); + throw LibWinptyException( + WINPTY_ERROR_AGENT_CREATION_FAILED, errStr.c_str()); + } + + HANDLE dupToken = nullptr; + if (!DuplicateTokenEx( + token, + TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, + NULL, + SecurityImpersonation, + TokenPrimary, + &dupToken)) + { + CloseHandle(token); + const DWORD lastError = GetLastError(); + const auto errStr = + (WStringBuilder(256) + << L"winpty-agent DuplicateTokenEx failed: cmdline='" << cmdline + << L"' err=0x" << whexOfInt(lastError)).str_moved(); + throw LibWinptyException( + WINPTY_ERROR_AGENT_CREATION_FAILED, errStr.c_str()); + } + + creationFlags = CREATE_NEW_CONSOLE | CREATE_DEFAULT_ERROR_MODE | CREATE_BREAKAWAY_FROM_JOB | NORMAL_PRIORITY_CLASS; + DWORD exitCode = 0; + const BOOL success = CreateProcessAsUser( + dupToken, + exePath.c_str(), + cmdlineV.data(), + nullptr, + nullptr, + FALSE, + creationFlags, + nullptr, + nullptr, + &sui, + &pi); + if (!success) { + CloseHandle(token); + CloseHandle(dupToken); + const DWORD lastError = GetLastError(); + const auto errStr = + (WStringBuilder(256) + << L"winpty-agent CreateProcessAsUser failed: cmdline='" << cmdline + << L"' err=0x" << whexOfInt(lastError)).str_moved(); + throw LibWinptyException( + WINPTY_ERROR_AGENT_CREATION_FAILED, errStr.c_str()); + } + + CloseHandle(token); + CloseHandle(dupToken); + } else { + const BOOL success = + CreateProcessW(exePath.c_str(), + cmdlineV.data(), + nullptr, nullptr, + /*bInheritHandles=*/FALSE, + /*dwCreationFlags=*/creationFlags, + nullptr, nullptr, + &sui, &pi); + if (!success) { + const DWORD lastError = GetLastError(); + const auto errStr = + (WStringBuilder(256) + << L"winpty-agent CreateProcess failed: cmdline='" << cmdline + << L"' err=0x" << whexOfInt(lastError)).str_moved(); + throw LibWinptyException( + WINPTY_ERROR_AGENT_CREATION_FAILED, errStr.c_str()); + } } + CloseHandle(pi.hThread); TRACE("Created agent successfully, pid=%u, cmdline=%s", static_cast(pi.dwProcessId), @@ -556,7 +626,7 @@ createAgentSession(const winpty_config_t *cfg, DWORD agentPid = 0; wp->agentProcess = startAgentProcess( - desktop, pipeName, params, creationFlags, agentPid); + cfg, desktop, pipeName, params, creationFlags, agentPid); connectControlPipe(*wp.get()); verifyPipeClientPid(wp->controlPipe.get(), agentPid);