Add opt-in KERB_CERTIFICATE_LOGON SSPI rewrite for smart card logon#171
Draft
Marc-André Moreau (mamoreau-devolutions) wants to merge 1 commit into
Draft
Add opt-in KERB_CERTIFICATE_LOGON SSPI rewrite for smart card logon#171Marc-André Moreau (mamoreau-devolutions) wants to merge 1 commit into
Marc-André Moreau (mamoreau-devolutions) wants to merge 1 commit into
Conversation
Smart card logon via RDP fails when a certificate thumbprint and PIN are both pre-supplied: the CredSSP credential reaching LSASS is not a KERB_CERTIFICATE_LOGON, so tspkg calls CryptAcquireCertificatePrivateKey without CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG and fails for CNG-backed keys. Implement the client-side workaround by extending the existing SSPI API hooking instead of patching LSASS. The AcquireCredentialsHandleW detour now, strictly per session and only when KerbCertificateLogon:i:1 is set, rewrites a marshaled smart card certificate credential into the equivalent CredsspCertificateCreds / KERB_CERTIFICATE_LOGON packed buffer before it is handed to LSASS. CREDSSP_CRED_EX wrapping is preserved when present. Per-session isolation reuses the existing instance/session machinery: a new thread-local SSPI session scope is bracketed around CMsRdpClient::raw_Connect() (with a guarded, non-ambiguous global fallback) so the hook can resolve the owning CMsRdpExtendedSettings without affecting other concurrent sessions. Safety: rewrite is opt-in only, never triggered automatically by PasswordContainsSCardPin; unknown credential shapes pass through unchanged; no PINs, passwords, or certificate bytes are logged; PIN-bearing buffers are zeroed before free. Optional secret-free diagnostics are gated behind MSRDPEX_SSPI_SMARTCARD_DEBUG. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the client-side workaround for smart card certificate logon failures over RDP, entirely by extending MsRdpEx's existing SSPI API hooking — no LSASS patching and no internal
mstscaxoffset hooks.Problem
When a smart card certificate thumbprint and PIN are both pre-supplied (e.g. by a connection manager that stores them and sets
PasswordContainsSCardPin:i:1), the CredSSP credential reaching LSASS is not aKERB_CERTIFICATE_LOGON.tspkgthen callsCryptAcquireCertificatePrivateKeywithoutCRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, which fails for CNG/NCrypt-backed keys (the common smart card case). The interactive Windows prompt avoids this because it builds a packedKERB_CERTIFICATE_LOGON.Solution
When
KerbCertificateLogon:i:1is set on a session, theAcquireCredentialsHandleWdetour rewrites a marshaled smart card certificate + PIN CredSSP credential into the equivalentCredsspCertificateCreds/KERB_CERTIFICATE_LOGONpacked buffer before it is handed to LSASS.CREDSSP_CRED_EXwrapping is preserved when present.Key design points
CMsRdpClient::raw_Connect()(with a guarded, non-ambiguous global fallback), so the hook resolves the owningCMsRdpExtendedSettingswithout affecting other concurrent sessions.CredUnPackAuthenticationBufferW→ validate marshaledCertCredential→CredPackAuthenticationBufferW, verifying the first DWORD isKerbCertificateLogon.PasswordContainsSCardPin. Unknown / already-correct credential shapes pass through unchanged.MSRDPEX_SSPI_SMARTCARD_DEBUG=1.Files changed
dll/Sspi.cpp— session-scope infra, diagnostics, KERB_CERTIFICATE_LOGON builder, wired-in rewritedll/MsRdpClient.cpp— SSPI session begin/end aroundraw_Connect()dll/RdpSettings.cpp,include/MsRdpEx/RdpSettings.h—KerbCertificateLogonopt-in +PasswordContainsSCardPinstateinclude/MsRdpEx/Sspi.h— begin/end session declarationsREADME.md— documents the new RDP option, behavior, and debug env varValidation
MsRdpEx.dllRelease/x64 — links cleanly, no new warnings from the changes.MSRDPEX_SSPI_SMARTCARD_DEBUG=1.