Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new config option to support thread token based agent process to … #133

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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 proces from the thread token instead of current process.
Copy link
Owner

Choose a reason for hiding this comment

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

s/proces/process

Copy link
Author

Choose a reason for hiding this comment

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

done

* This is usefull when winpty.dll is called by Windows Service program to
Copy link
Owner

Choose a reason for hiding this comment

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

s/usefull/useful

Copy link
Author

Choose a reason for hiding this comment

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

done

* 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
103 changes: 86 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,92 @@ 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());
}

DWORD exitCode = 0;
const BOOL success = CreateProcessAsUser(
dupToken,
exePath.c_str(),
cmdlineV.data(),
nullptr,
nullptr,
FALSE,
CREATE_DEFAULT_ERROR_MODE | CREATE_BREAKAWAY_FROM_JOB | CREATE_UNICODE_ENVIRONMENT | NORMAL_PRIORITY_CLASS,
Copy link
Owner

Choose a reason for hiding this comment

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

I'm curious about these creation flags.

  • startAgentProcess is already called with a creation flags parameter, which is currently only set to either DETACHED_PROCESS (for --create-desktop) or CREATE_NEW_CONSOLE (normal code path). This code doesn't ever use CREATE_NEW_CONSOLE, so I assume it reuses its parent's console, if it has one?
  • Regarding the specific flags:
    • CREATE_DEFAULT_ERROR_MODE: I wonder if this ought to be the default behavior for winpty -- there was a complaint a while ago about crash handling and winpty (When run in Terminal a programs unhandled exceptions don't produce debug dialog in win32  microsoft/vscode#32134), but I never looked into it. Maybe this creation flag fixes the problem?
    • CREATE_UNICODE_ENVIRONMENT: lpEnvironment is null; does this do anything?
    • CREATE_BREAKAWAY_FROM_JOB and NORMAL_PRIORITY_CLASS: I wonder if these flags should really be set by WINPTY_FLAG_IMPERSONATE_THREAD or whether they're orthogonal settings. Maybe winpty needs a way to specify arbitrary creation flags for the agent and/or the agent's child?

nullptr,
nullptr,
&sui,
&pi);
if (!success) {
CloseHandle(token);
Copy link
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
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 +625,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