Using ConPTY with CreateProcessWithLogon and CreateProcessWithToken #16022

Open
opened 2026-01-31 04:55:07 +00:00 by claunia · 4 comments
Owner

Originally created by @sajagi on GitHub (Dec 2, 2021).

The documentation only covers same-user scenario with CreateProcess function. There is no mention whether CreatePseudoConsole also works with CreateProcessWithLogon, CreateProcessWithToken and similar functions.

It seems as if this scenario is not supported. CreateProcessWithLogon fails with ERROR_INVALID_PARAMETER (87) message. However, given how cryptic the message is, the underlying issue might be also elsewhere.

Originally created by @sajagi on GitHub (Dec 2, 2021). The documentation only covers same-user scenario with `CreateProcess` function. There is no mention whether `CreatePseudoConsole` also works with `CreateProcessWithLogon`, `CreateProcessWithToken` and similar functions. It seems as if this scenario is not supported. `CreateProcessWithLogon` fails with `ERROR_INVALID_PARAMETER` (87) message. However, given how cryptic the message is, the underlying issue might be also elsewhere.
claunia added the Issue-FeatureArea-ServerProduct-Conpty labels 2026-01-31 04:55:07 +00:00
Author
Owner

@zadjii-msft commented on GitHub (Dec 2, 2021):

You know, we wrote CreatePseudoConsoleAsUser
f2386de422/src/winconpty/winconpty.cpp (L360)

a while ago, but I don't think we ever added it to the public SDK. We should probably do that.

(this might also be an argument in favor of the conpty nuget package, #3577/#1130 so folks don't need to rely on SDK version updates)

@zadjii-msft commented on GitHub (Dec 2, 2021): You know, we wrote `CreatePseudoConsoleAsUser` https://github.com/microsoft/terminal/blob/f2386de422971488ee8d7cab6ae9afd0bc82b033/src/winconpty/winconpty.cpp#L360 a while ago, but I don't think we ever added it to the public SDK. We should probably do that. (this might also be an argument in favor of the conpty nuget package, #3577/#1130 so folks don't need to rely on SDK version updates)
Author
Owner

@sajagi commented on GitHub (Dec 2, 2021):

Thanks for the quick reply.

Do I read it correctly that such scenario is then not supported and the only way to host another user's process in pseudoconsole is to call CreatePseudoConsole and CreateProcess from a process already running under that user?

@sajagi commented on GitHub (Dec 2, 2021): Thanks for the quick reply. Do I read it correctly that such scenario is then not supported and the only way to host another user's process in pseudoconsole is to call `CreatePseudoConsole` and `CreateProcess` from a process already running under that user?
Author
Owner

@zadjii-msft commented on GitHub (Dec 2, 2021):

Right now, yea, it's not technically supported. All the code is there and ready to be used, we just never exposed the public function signature for it (mostly because we just forgot to).

If we do ever get around to shipping a conpty nuget, then we could ship the updated API surface out-of-band, without needing to wait for the OS to update. That might be a quicker and easier solution than trying to get this exposed in the OS.

@zadjii-msft commented on GitHub (Dec 2, 2021): Right now, yea, it's not technically supported. All the code is there and ready to be used, we just never exposed the public function signature for it (mostly because we just forgot to). If we do ever get around to shipping a conpty nuget, then we could ship the updated API surface out-of-band, without needing to wait for the OS to update. That might be a quicker and easier solution than trying to get this exposed in the OS.
Author
Owner

@eryksun commented on GitHub (Dec 2, 2021):

The linked code just calls CreateProcessAsUserW() to spawn the console host. But CreateProcessWithLogonW() and CreateProcessWithTokenW() are delegated to the Secondary Logon service (seclogon). Delegation is required in order to allow an unprivileged user to spawn a process as another user (e.g. via runas.exe).

Unless the new token is related to the caller's token (e.g. a restricted token, or UWP app token), directly calling CreateProcessAsUserW() requires SeAssignPrimaryTokenPrivilege and possibly SeIncreaseQuotaPrivilege. By default, even administrators don't have SeAssignPrimaryTokenPrivilege.

Also, for CreateProcessWithLogonW(), the Secondary Logon service enables access to the client's interactive window station and desktop by adding the client's logon-session group to the new token. (A logon-session group has the attribute SE_GROUP_LOGON_ID.) Without this access, the spawned process (e.g. conhost.exe) will fail with STATUS_DLL_INIT_FAILED (0xC0000142) when user32.dll loads, since it tries and fails to open the window station and desktop. Creating a token with the client's logon-session group requires calling LsaLogonUser() or LogonUserExExW() with SeTcbPrivilege (act as part of the OS) granted and enabled, which is why seclogon runs as a SYSTEM service. The alternative is to open the client's window station and desktop objects to add a DACL entry that grants all access to the new logon-session group.

@eryksun commented on GitHub (Dec 2, 2021): The linked code just calls `CreateProcessAsUserW()` to spawn the console host. But `CreateProcessWithLogonW()` and `CreateProcessWithTokenW()` are delegated to the Secondary Logon service (seclogon). Delegation is required in order to allow an unprivileged user to spawn a process as another user (e.g. via runas.exe). Unless the new token is related to the caller's token (e.g. a restricted token, or UWP app token), directly calling `CreateProcessAsUserW()` requires [SeAssignPrimaryTokenPrivilege](https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/replace-a-process-level-token) and possibly [SeIncreaseQuotaPrivilege](https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/adjust-memory-quotas-for-a-process). By default, even administrators don't have SeAssignPrimaryTokenPrivilege. Also, for `CreateProcessWithLogonW()`, the Secondary Logon service enables access to the client's interactive window station and desktop by adding the client's logon-session group to the new token. (A logon-session group has the attribute [`SE_GROUP_LOGON_ID`](https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-token_groups).) Without this access, the spawned process (e.g. conhost.exe) will fail with `STATUS_DLL_INIT_FAILED` (0xC0000142) when user32.dll loads, since it tries and fails to open the window station and desktop. Creating a token with the client's logon-session group requires calling `LsaLogonUser()` or `LogonUserExExW()` with SeTcbPrivilege (act as part of the OS) granted and enabled, which is why seclogon runs as a SYSTEM service. The alternative is to open the client's window station and desktop objects to add a DACL entry that grants all access to the new logon-session group.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#16022