Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/include/winpty_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
104 changes: 87 additions & 17 deletions src/libwinpty/winpty.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 &params,
Expand All @@ -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);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer this code use RAII (i.e. OwnedHandle), but I can fix that up myself after merging the change.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left as is.

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<unsigned int>(pi.dwProcessId),
Expand Down Expand Up @@ -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);

Expand Down