Skip to content
Draft
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
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,27 @@ MsRdpEx processes additional .RDP file options that are not normally supported b
| EnableHardwareMode:i:value | Disable DirectX client presenter (force GDI client presenter) | 0/1 | 1 |
| ClearTextPassword:s:value | Target RDP server password - use for testing only | Insecure password | - |
| GatewayPassword:s:value | RD Gateway server password - use for testing only | Insecure password | - |
| KerbCertificateLogon:i:value | Force smart card credentials into a KERB_CERTIFICATE_LOGON buffer (see below) | 0/1 | 0 |

### Smart card certificate logon (KerbCertificateLogon)

When a smart card certificate thumbprint and PIN are both pre-supplied (for example by a
connection manager that stores them and sets `PasswordContainsSCardPin:i:1`), the CredSSP/SSPI
credential that reaches LSAS can take a code path in `tspkg` that calls
`CryptAcquireCertificatePrivateKey` without `CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG`, which fails for
keys backed by a CNG/NCrypt key storage provider (the common case for smart cards). The interactive
Windows credential prompt avoids this because it builds a packed `KERB_CERTIFICATE_LOGON` structure.

Setting `KerbCertificateLogon:i:1` opts the session in to a client-side workaround: MsRdpEx hooks
`AcquireCredentialsHandleW` and, only for the matching session, rewrites a smart card CredSSP
credential into the equivalent `KERB_CERTIFICATE_LOGON`-shaped buffer
(`CredsspCertificateCreds`) before it is handed to LSAS. The rewrite is strictly opt-in per session
and only activates when the existing credential is a marshaled certificate credential; in every
other case the original credential is passed through unchanged. No PINs, passwords, or certificate
bytes are ever logged, and PIN-bearing buffers are zeroed before they are freed.

Set the `MSRDPEX_SSPI_SMARTCARD_DEBUG=1` environment variable to emit additional (secret-free)
CredSSP credential metadata to the log while diagnosing smart card logon issues.

## Extended RDP client logs

Expand Down
3 changes: 3 additions & 0 deletions dll/MsRdpClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <MsRdpEx/RdpProcess.h>
#include <MsRdpEx/RdpInstance.h>
#include <MsRdpEx/RdpSettings.h>
#include <MsRdpEx/Sspi.h>
#include <MsRdpEx/NameResolver.h>

#include "TSObjects.h"
Expand Down Expand Up @@ -502,7 +503,9 @@ class CMsRdpClient : public IMsRdpClient10
m_pMsRdpExtendedSettings->PrepareVideoRecorder();
m_pMsRdpExtendedSettings->PrepareExtraSystemMenu();

MsRdpEx_Sspi_BeginSession(&m_sessionId);
hr = m_pMsTscAx->raw_Connect();
MsRdpEx_Sspi_EndSession(&m_sessionId);

return hr;
}
Expand Down
48 changes: 48 additions & 0 deletions dll/RdpSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,38 @@ HRESULT __stdcall CMsRdpExtendedSettings::put_Property(BSTR bstrPropertyName, VA
delete[] propValue;
hr = S_OK;
}
else if (MsRdpEx_StringEquals(propName, "KerbCertificateLogon") ||
MsRdpEx_StringEquals(propName, "KerbCertificateLogonEnabled"))
{
if ((pValue->vt != VT_BOOL) && (pValue->vt != VT_I4) && (pValue->vt != VT_UI4))
goto end;

if (pValue->vt == VT_BOOL)
m_KerbCertificateLogonEnabled = pValue->boolVal ? true : false;
else if (pValue->vt == VT_I4)
m_KerbCertificateLogonEnabled = pValue->intVal ? true : false;
else
m_KerbCertificateLogonEnabled = pValue->uintVal ? true : false;

hr = S_OK;
}
else if (MsRdpEx_StringEquals(propName, "PasswordContainsSCardPin"))
{
if ((pValue->vt != VT_BOOL) && (pValue->vt != VT_I4) && (pValue->vt != VT_UI4))
goto end;

if (pValue->vt == VT_BOOL)
m_PasswordContainsSCardPin = pValue->boolVal ? true : false;
else if (pValue->vt == VT_I4)
m_PasswordContainsSCardPin = pValue->intVal ? true : false;
else
m_PasswordContainsSCardPin = pValue->uintVal ? true : false;

if (m_CoreProps)
hr = m_CoreProps->put_Property(bstrPropertyName, pValue);
else
hr = S_OK;
}
else if (MsRdpEx_StringEquals(propName, "EnableMouseJiggler"))
{
if (pValue->vt != VT_BOOL)
Expand Down Expand Up @@ -1349,8 +1381,14 @@ HRESULT CMsRdpExtendedSettings::ApplyRdpFile(void* rdpFilePtr)
else if (MsRdpEx_RdpFileEntry_IsMatch(entry, 's', "ClearTextPassword")) {
pMsRdpExtendedSettings->SetTargetPassword(entry->value);
}
else if (MsRdpEx_RdpFileEntry_IsMatch(entry, 'i', "KerbCertificateLogon")) {
if (MsRdpEx_RdpFileEntry_GetVBoolValue(entry, &value)) {
m_KerbCertificateLogonEnabled = value.boolVal ? true : false;
}
}
else if (MsRdpEx_RdpFileEntry_IsMatch(entry, 'i', "PasswordContainsSCardPin")) {
if (MsRdpEx_RdpFileEntry_GetVBoolValue(entry, &value)) {
m_PasswordContainsSCardPin = value.boolVal ? true : false;
bstr_t propName = _com_util::ConvertStringToBSTR(entry->name);
pMsRdpExtendedSettings->put_CoreProperty(propName, &value);
}
Expand Down Expand Up @@ -1617,6 +1655,16 @@ char* CMsRdpExtendedSettings::GetKdcProxyName()
return MsRdpEx_KdcProxyUrlToName(m_KdcProxyUrl);
}

bool CMsRdpExtendedSettings::GetKerbCertificateLogonEnabled()
{
return m_KerbCertificateLogonEnabled;
}

bool CMsRdpExtendedSettings::GetPasswordContainsSCardPin()
{
return m_PasswordContainsSCardPin;
}

bool CMsRdpExtendedSettings::GetMouseJigglerEnabled()
{
return m_MouseJigglerEnabled;
Expand Down
Loading
Loading