[nla] initial server-side remote credential guard support

Adds support for server-side remote credential guard in NLA. When enabled that allows
the remote user to connect without shipping credentials in TSCred packets. Instead
it will send his TGT encoded with a TGS from the remote server. This way the server
is able to populate that TGT in a local credential cache without knowing the user's
password.

The patch only treats the NLA part and does not contain the associated RDPEAR channel
that allows to have the complete interaction to retrieve new access tokens.
This commit is contained in:
David Fort 2023-11-17 10:22:06 +01:00 committed by akallabeth
parent 20f09b2d34
commit 061148f856
12 changed files with 556 additions and 220 deletions

View File

@ -31,6 +31,7 @@
#include <winpr/sspi.h> #include <winpr/sspi.h>
#include <winpr/ntlm.h> #include <winpr/ntlm.h>
#include <winpr/winsock.h> #include <winpr/winsock.h>
#include <winpr/secapi.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
@ -42,60 +43,64 @@ extern "C"
typedef BOOL (*psPeerInitialize)(freerdp_peer* peer); typedef BOOL (*psPeerInitialize)(freerdp_peer* peer);
#if defined(WITH_FREERDP_DEPRECATED) #if defined(WITH_FREERDP_DEPRECATED)
WINPR_DEPRECATED_VAR("Use psPeerGetEventHandle instead", WINPR_DEPRECATED_VAR("Use psPeerGetEventHandle instead",
typedef BOOL (*psPeerGetFileDescriptor)(freerdp_peer* peer, void** rfds, typedef BOOL (*psPeerGetFileDescriptor)(freerdp_peer* peer, void** rfds,
int* rcount);) int* rcount);)
#endif #endif
typedef HANDLE (*psPeerGetEventHandle)(freerdp_peer* peer); typedef HANDLE (*psPeerGetEventHandle)(freerdp_peer* peer);
typedef DWORD (*psPeerGetEventHandles)(freerdp_peer* peer, HANDLE* events, DWORD count); typedef DWORD (*psPeerGetEventHandles)(freerdp_peer* peer, HANDLE* events, DWORD count);
typedef HANDLE (*psPeerGetReceiveEventHandle)(freerdp_peer* peer); typedef HANDLE (*psPeerGetReceiveEventHandle)(freerdp_peer* peer);
typedef BOOL (*psPeerCheckFileDescriptor)(freerdp_peer* peer); typedef BOOL (*psPeerCheckFileDescriptor)(freerdp_peer* peer);
typedef BOOL (*psPeerIsWriteBlocked)(freerdp_peer* peer); typedef BOOL (*psPeerIsWriteBlocked)(freerdp_peer* peer);
typedef int (*psPeerDrainOutputBuffer)(freerdp_peer* peer); typedef int (*psPeerDrainOutputBuffer)(freerdp_peer* peer);
typedef BOOL (*psPeerHasMoreToRead)(freerdp_peer* peer); typedef BOOL (*psPeerHasMoreToRead)(freerdp_peer* peer);
typedef BOOL (*psPeerClose)(freerdp_peer* peer); typedef BOOL (*psPeerClose)(freerdp_peer* peer);
typedef void (*psPeerDisconnect)(freerdp_peer* peer); typedef void (*psPeerDisconnect)(freerdp_peer* peer);
typedef BOOL (*psPeerCapabilities)(freerdp_peer* peer); typedef BOOL (*psPeerRemoteCredentials)(freerdp_peer* peer, KERB_TICKET_LOGON* logonCreds,
typedef BOOL (*psPeerPostConnect)(freerdp_peer* peer); MSV1_0_SUPPLEMENTAL_CREDENTIAL* suppCreds);
typedef BOOL (*psPeerActivate)(freerdp_peer* peer); typedef BOOL (*psPeerCapabilities)(freerdp_peer* peer);
typedef BOOL (*psPeerLogon)(freerdp_peer* peer, const SEC_WINNT_AUTH_IDENTITY* identity, typedef BOOL (*psPeerPostConnect)(freerdp_peer* peer);
typedef BOOL (*psPeerActivate)(freerdp_peer* peer);
typedef BOOL (*psPeerLogon)(freerdp_peer* peer, const SEC_WINNT_AUTH_IDENTITY* identity,
BOOL automatic); BOOL automatic);
typedef BOOL (*psPeerSendServerRedirection)(freerdp_peer* peer, const rdpRedirection* redirection); typedef BOOL (*psPeerSendServerRedirection)(freerdp_peer* peer,
typedef BOOL (*psPeerAdjustMonitorsLayout)(freerdp_peer* peer); const rdpRedirection* redirection);
typedef BOOL (*psPeerClientCapabilities)(freerdp_peer* peer); typedef BOOL (*psPeerAdjustMonitorsLayout)(freerdp_peer* peer);
typedef BOOL (*psPeerClientCapabilities)(freerdp_peer* peer);
typedef BOOL (*psPeerSendChannelData)(freerdp_peer* peer, UINT16 channelId, const BYTE* data, typedef BOOL (*psPeerSendChannelData)(freerdp_peer* peer, UINT16 channelId, const BYTE* data,
size_t size); size_t size);
typedef BOOL (*psPeerSendChannelPacket)(freerdp_peer* client, UINT16 channelId, size_t totalSize, typedef BOOL (*psPeerSendChannelPacket)(freerdp_peer* client, UINT16 channelId,
UINT32 flags, const BYTE* data, size_t chunkSize); size_t totalSize, UINT32 flags, const BYTE* data,
typedef BOOL (*psPeerReceiveChannelData)(freerdp_peer* peer, UINT16 channelId, const BYTE* data, size_t chunkSize);
typedef BOOL (*psPeerReceiveChannelData)(freerdp_peer* peer, UINT16 channelId, const BYTE* data,
size_t size, UINT32 flags, size_t totalSize); size_t size, UINT32 flags, size_t totalSize);
typedef HANDLE (*psPeerVirtualChannelOpen)(freerdp_peer* peer, const char* name, UINT32 flags); typedef HANDLE (*psPeerVirtualChannelOpen)(freerdp_peer* peer, const char* name, UINT32 flags);
typedef BOOL (*psPeerVirtualChannelClose)(freerdp_peer* peer, HANDLE hChannel); typedef BOOL (*psPeerVirtualChannelClose)(freerdp_peer* peer, HANDLE hChannel);
typedef int (*psPeerVirtualChannelRead)(freerdp_peer* peer, HANDLE hChannel, BYTE* buffer, typedef int (*psPeerVirtualChannelRead)(freerdp_peer* peer, HANDLE hChannel, BYTE* buffer,
UINT32 length); UINT32 length);
typedef int (*psPeerVirtualChannelWrite)(freerdp_peer* peer, HANDLE hChannel, const BYTE* buffer, typedef int (*psPeerVirtualChannelWrite)(freerdp_peer* peer, HANDLE hChannel,
UINT32 length); const BYTE* buffer, UINT32 length);
typedef void* (*psPeerVirtualChannelGetData)(freerdp_peer* peer, HANDLE hChannel); typedef void* (*psPeerVirtualChannelGetData)(freerdp_peer* peer, HANDLE hChannel);
typedef int (*psPeerVirtualChannelSetData)(freerdp_peer* peer, HANDLE hChannel, void* data); typedef int (*psPeerVirtualChannelSetData)(freerdp_peer* peer, HANDLE hChannel, void* data);
typedef BOOL (*psPeerSetState)(freerdp_peer* peer, CONNECTION_STATE state); typedef BOOL (*psPeerSetState)(freerdp_peer* peer, CONNECTION_STATE state);
typedef BOOL (*psPeerReachedState)(freerdp_peer* peer, CONNECTION_STATE state); typedef BOOL (*psPeerReachedState)(freerdp_peer* peer, CONNECTION_STATE state);
/** @brief the result of the license callback */ /** @brief the result of the license callback */
typedef enum typedef enum
{ {
LICENSE_CB_INTERNAL_ERROR, /** an internal error happened in the callback */ LICENSE_CB_INTERNAL_ERROR, /** an internal error happened in the callback */
LICENSE_CB_ABORT, /** licensing process failed, abort the connection */ LICENSE_CB_ABORT, /** licensing process failed, abort the connection */
LICENSE_CB_IN_PROGRESS, /** incoming packet has been treated, we're waiting for further packets LICENSE_CB_IN_PROGRESS, /** incoming packet has been treated, we're waiting for further
to complete the workflow */ packets to complete the workflow */
LICENSE_CB_COMPLETED /** the licensing workflow has completed, go to next step */ LICENSE_CB_COMPLETED /** the licensing workflow has completed, go to next step */
} LicenseCallbackResult; } LicenseCallbackResult;
typedef LicenseCallbackResult (*psPeerLicenseCallback)(freerdp_peer* peer, wStream* s); typedef LicenseCallbackResult (*psPeerLicenseCallback)(freerdp_peer* peer, wStream* s);
struct rdp_freerdp_peer struct rdp_freerdp_peer
{ {
ALIGN64 rdpContext* context; ALIGN64 rdpContext* context;
ALIGN64 int sockfd; ALIGN64 int sockfd;
@ -104,7 +109,8 @@ struct rdp_freerdp_peer
#if defined(WITH_FREERDP_DEPRECATED) #if defined(WITH_FREERDP_DEPRECATED)
WINPR_DEPRECATED_VAR("Use rdpContext::update instead", ALIGN64 rdpUpdate* update;) WINPR_DEPRECATED_VAR("Use rdpContext::update instead", ALIGN64 rdpUpdate* update;)
WINPR_DEPRECATED_VAR("Use rdpContext::settings instead", ALIGN64 rdpSettings* settings;) WINPR_DEPRECATED_VAR("Use rdpContext::settings instead", ALIGN64 rdpSettings* settings;)
WINPR_DEPRECATED_VAR("Use rdpContext::autodetect instead", ALIGN64 rdpAutoDetect* autodetect;) WINPR_DEPRECATED_VAR("Use rdpContext::autodetect instead",
ALIGN64 rdpAutoDetect* autodetect;)
#else #else
UINT64 reservedX[3]; UINT64 reservedX[3];
#endif #endif
@ -181,7 +187,13 @@ struct rdp_freerdp_peer
ALIGN64 psPeerSetState SetState; ALIGN64 psPeerSetState SetState;
ALIGN64 psPeerReachedState ReachedState; ALIGN64 psPeerReachedState ReachedState;
ALIGN64 psSspiNtlmHashCallback SspiNtlmHashCallback; ALIGN64 psSspiNtlmHashCallback SspiNtlmHashCallback;
}; /**
* @brief RemoteCredentials Function pointer that will be called when remote
* credentials guard are used by the peer and we receive the logonCreds (kerberos)
* and supplementary creds (NTLM).
*/
ALIGN64 psPeerRemoteCredentials RemoteCredentials;
};
FREERDP_API BOOL freerdp_peer_context_new(freerdp_peer* client); FREERDP_API BOOL freerdp_peer_context_new(freerdp_peer* client);
FREERDP_API BOOL freerdp_peer_context_new_ex(freerdp_peer* client, const rdpSettings* settings); FREERDP_API BOOL freerdp_peer_context_new_ex(freerdp_peer* client, const rdpSettings* settings);

View File

@ -651,6 +651,7 @@ extern "C"
#define FreeRDP_RdstlsSecurity (1111) #define FreeRDP_RdstlsSecurity (1111)
#define FreeRDP_AadSecurity (1112) #define FreeRDP_AadSecurity (1112)
#define FreeRDP_WinSCardModule (1113) #define FreeRDP_WinSCardModule (1113)
#define FreeRDP_RemoteCredentialGuard (1114)
#define FreeRDP_MstscCookieMode (1152) #define FreeRDP_MstscCookieMode (1152)
#define FreeRDP_CookieMaxLength (1153) #define FreeRDP_CookieMaxLength (1153)
#define FreeRDP_PreconnectionId (1154) #define FreeRDP_PreconnectionId (1154)
@ -1185,7 +1186,8 @@ extern "C"
ALIGN64 BOOL RdstlsSecurity; /* 1111 */ ALIGN64 BOOL RdstlsSecurity; /* 1111 */
ALIGN64 BOOL AadSecurity; /* 1112 */ ALIGN64 BOOL AadSecurity; /* 1112 */
ALIGN64 char* WinSCardModule; /* 1113 */ ALIGN64 char* WinSCardModule; /* 1113 */
UINT64 padding1152[1152 - 1114]; /* 1114 */ ALIGN64 BOOL RemoteCredentialGuard; /* 1114 */
UINT64 padding1152[1152 - 1115]; /* 1115 */
/* Connection Cookie */ /* Connection Cookie */
ALIGN64 BOOL MstscCookieMode; /* 1152 */ ALIGN64 BOOL MstscCookieMode; /* 1152 */

View File

@ -465,6 +465,9 @@ BOOL freerdp_settings_get_bool(const rdpSettings* settings, size_t id)
case FreeRDP_RemoteConsoleAudio: case FreeRDP_RemoteConsoleAudio:
return settings->RemoteConsoleAudio; return settings->RemoteConsoleAudio;
case FreeRDP_RemoteCredentialGuard:
return settings->RemoteCredentialGuard;
case FreeRDP_RemoteFxCodec: case FreeRDP_RemoteFxCodec:
return settings->RemoteFxCodec; return settings->RemoteFxCodec;
@ -1182,6 +1185,10 @@ BOOL freerdp_settings_set_bool(rdpSettings* settings, size_t id, BOOL val)
settings->RemoteConsoleAudio = cnv.c; settings->RemoteConsoleAudio = cnv.c;
break; break;
case FreeRDP_RemoteCredentialGuard:
settings->RemoteCredentialGuard = cnv.c;
break;
case FreeRDP_RemoteFxCodec: case FreeRDP_RemoteFxCodec:
settings->RemoteFxCodec = cnv.c; settings->RemoteFxCodec = cnv.c;
break; break;

View File

@ -192,6 +192,7 @@ static const struct settings_str_entry settings_map[] = {
{ FreeRDP_RemoteAssistanceRequestControl, FREERDP_SETTINGS_TYPE_BOOL, { FreeRDP_RemoteAssistanceRequestControl, FREERDP_SETTINGS_TYPE_BOOL,
"FreeRDP_RemoteAssistanceRequestControl" }, "FreeRDP_RemoteAssistanceRequestControl" },
{ FreeRDP_RemoteConsoleAudio, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_RemoteConsoleAudio" }, { FreeRDP_RemoteConsoleAudio, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_RemoteConsoleAudio" },
{ FreeRDP_RemoteCredentialGuard, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_RemoteCredentialGuard" },
{ FreeRDP_RemoteFxCodec, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_RemoteFxCodec" }, { FreeRDP_RemoteFxCodec, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_RemoteFxCodec" },
{ FreeRDP_RemoteFxImageCodec, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_RemoteFxImageCodec" }, { FreeRDP_RemoteFxImageCodec, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_RemoteFxImageCodec" },
{ FreeRDP_RemoteFxOnly, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_RemoteFxOnly" }, { FreeRDP_RemoteFxOnly, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_RemoteFxOnly" },

View File

@ -60,6 +60,9 @@ struct rdp_nego
BOOL NegotiateSecurityLayer; BOOL NegotiateSecurityLayer;
BOOL EnabledProtocols[32]; BOOL EnabledProtocols[32];
BOOL RestrictedAdminModeRequired; BOOL RestrictedAdminModeRequired;
BOOL RemoteCredsGuardRequired;
BOOL RemoteCredsGuardActive;
BOOL RemoteCredsGuardSupported;
BOOL GatewayEnabled; BOOL GatewayEnabled;
BOOL GatewayBypassLocal; BOOL GatewayBypassLocal;
BOOL ConnectChildSession; BOOL ConnectChildSession;
@ -1112,6 +1115,9 @@ BOOL nego_send_negotiation_request(rdpNego* nego)
if (nego->RestrictedAdminModeRequired) if (nego->RestrictedAdminModeRequired)
flags |= RESTRICTED_ADMIN_MODE_REQUIRED; flags |= RESTRICTED_ADMIN_MODE_REQUIRED;
if (nego->RemoteCredsGuardRequired)
flags |= REDIRECTED_AUTHENTICATION_MODE_REQUIRED;
Stream_Write_UINT8(s, TYPE_RDP_NEG_REQ); Stream_Write_UINT8(s, TYPE_RDP_NEG_REQ);
Stream_Write_UINT8(s, flags); Stream_Write_UINT8(s, flags);
Stream_Write_UINT16(s, 8); /* RDP_NEG_DATA length (8) */ Stream_Write_UINT16(s, 8); /* RDP_NEG_DATA length (8) */
@ -1221,10 +1227,18 @@ BOOL nego_process_negotiation_request(rdpNego* nego, wStream* s)
if (flags & REDIRECTED_AUTHENTICATION_MODE_REQUIRED) if (flags & REDIRECTED_AUTHENTICATION_MODE_REQUIRED)
{ {
WLog_ERR(TAG, "RDP_NEG_REQ::flags REDIRECTED_AUTHENTICATION_MODE_REQUIRED: FreeRDP does " if (!nego->RemoteCredsGuardSupported)
"not support Remote Credential Guard"); {
WLog_ERR(TAG,
"RDP_NEG_REQ::flags REDIRECTED_AUTHENTICATION_MODE_REQUIRED but disabled");
return FALSE; return FALSE;
} }
else
{
WLog_INFO(TAG, "RDP_NEG_REQ::flags REDIRECTED_AUTHENTICATION_MODE_REQUIRED");
}
nego->RemoteCredsGuardActive = TRUE;
}
Stream_Read_UINT16(s, length); Stream_Read_UINT16(s, length);
if (length != 8) if (length != 8)
@ -1418,6 +1432,12 @@ BOOL nego_send_negotiation_response(rdpNego* nego)
if (freerdp_settings_get_bool(settings, FreeRDP_SupportGraphicsPipeline)) if (freerdp_settings_get_bool(settings, FreeRDP_SupportGraphicsPipeline))
flags |= DYNVC_GFX_PROTOCOL_SUPPORTED; flags |= DYNVC_GFX_PROTOCOL_SUPPORTED;
if (freerdp_settings_get_bool(settings, FreeRDP_RestrictedAdminModeRequired))
flags |= RESTRICTED_ADMIN_MODE_SUPPORTED;
if (nego->RemoteCredsGuardSupported)
flags |= REDIRECTED_AUTHENTICATION_MODE_SUPPORTED;
/* RDP_NEG_DATA must be present for TLS, NLA, RDP and RDSTLS */ /* RDP_NEG_DATA must be present for TLS, NLA, RDP and RDSTLS */
Stream_Write_UINT8(s, TYPE_RDP_NEG_RSP); Stream_Write_UINT8(s, TYPE_RDP_NEG_RSP);
Stream_Write_UINT8(s, flags); /* flags */ Stream_Write_UINT8(s, flags); /* flags */
@ -1648,6 +1668,28 @@ void nego_set_restricted_admin_mode_required(rdpNego* nego, BOOL RestrictedAdmin
nego->RestrictedAdminModeRequired = RestrictedAdminModeRequired; nego->RestrictedAdminModeRequired = RestrictedAdminModeRequired;
} }
void nego_set_RCG_required(rdpNego* nego, BOOL enabled)
{
WINPR_ASSERT(nego);
WLog_DBG(TAG, "Enabling remoteCredentialGuards: %s", enabled ? "TRUE" : "FALSE");
nego->RemoteCredsGuardRequired = enabled;
}
void nego_set_RCG_supported(rdpNego* nego, BOOL enabled)
{
WINPR_ASSERT(nego);
nego->RemoteCredsGuardSupported = enabled;
}
BOOL nego_get_remoteCredentialGuard(rdpNego* nego)
{
WINPR_ASSERT(nego);
return nego->RemoteCredsGuardActive;
}
void nego_set_childsession_enabled(rdpNego* nego, BOOL ChildSessionEnabled) void nego_set_childsession_enabled(rdpNego* nego, BOOL ChildSessionEnabled)
{ {
WINPR_ASSERT(nego); WINPR_ASSERT(nego);

View File

@ -114,6 +114,9 @@ FREERDP_LOCAL BOOL nego_set_target(rdpNego* nego, const char* hostname, UINT16 p
FREERDP_LOCAL void nego_set_negotiation_enabled(rdpNego* nego, BOOL NegotiateSecurityLayer); FREERDP_LOCAL void nego_set_negotiation_enabled(rdpNego* nego, BOOL NegotiateSecurityLayer);
FREERDP_LOCAL void nego_set_restricted_admin_mode_required(rdpNego* nego, FREERDP_LOCAL void nego_set_restricted_admin_mode_required(rdpNego* nego,
BOOL RestrictedAdminModeRequired); BOOL RestrictedAdminModeRequired);
FREERDP_LOCAL void nego_set_RCG_required(rdpNego* nego, BOOL enabled);
FREERDP_LOCAL void nego_set_RCG_supported(rdpNego* nego, BOOL enabled);
FREERDP_LOCAL BOOL nego_get_remoteCredentialGuard(rdpNego* nego);
FREERDP_LOCAL void nego_set_childsession_enabled(rdpNego* nego, BOOL ChildSessionEnabled); FREERDP_LOCAL void nego_set_childsession_enabled(rdpNego* nego, BOOL ChildSessionEnabled);
FREERDP_LOCAL void nego_set_gateway_enabled(rdpNego* nego, BOOL GatewayEnabled); FREERDP_LOCAL void nego_set_gateway_enabled(rdpNego* nego, BOOL GatewayEnabled);
FREERDP_LOCAL void nego_set_gateway_bypass_local(rdpNego* nego, BOOL GatewayBypassLocal); FREERDP_LOCAL void nego_set_gateway_bypass_local(rdpNego* nego, BOOL GatewayBypassLocal);

View File

@ -40,8 +40,11 @@
#include <winpr/cred.h> #include <winpr/cred.h>
#include <winpr/debug.h> #include <winpr/debug.h>
#include <winpr/asn1.h> #include <winpr/asn1.h>
#include <winpr/secapi.h>
#include "../crypto/tls.h" #include "../crypto/tls.h"
#include "nego.h"
#include "rdp.h"
#include "nla.h" #include "nla.h"
#include "utils.h" #include "utils.h"
#include "credssp_auth.h" #include "credssp_auth.h"
@ -336,6 +339,9 @@ static BOOL nla_client_setup_identity(rdpNla* nla)
if ((settings->PasswordHash) && (strlen(settings->PasswordHash) > 0)) if ((settings->PasswordHash) && (strlen(settings->PasswordHash) > 0))
PromptPassword = FALSE; PromptPassword = FALSE;
} }
if (settings->RemoteCredentialGuard)
PromptPassword = FALSE;
} }
#endif #endif
@ -1134,6 +1140,96 @@ static BOOL nla_read_TSCspDataDetail(WinPrAsn1Decoder* dec, rdpSettings* setting
return set_creds_octetstring_to_settings(dec, 4, TRUE, FreeRDP_CspName, settings); return set_creds_octetstring_to_settings(dec, 4, TRUE, FreeRDP_CspName, settings);
} }
static BOOL nla_read_KERB_TICKET_LOGON(rdpNla* nla, wStream* s, KERB_TICKET_LOGON* ticket)
{
/* mysterious extra 16 bytes before TGS/TGT content */
if (!Stream_CheckAndLogRequiredLength(TAG, s, 16 + 16))
return FALSE;
Stream_Read_UINT32(s, ticket->MessageType);
Stream_Read_UINT32(s, ticket->Flags);
Stream_Read_UINT32(s, ticket->ServiceTicketLength);
Stream_Read_UINT32(s, ticket->TicketGrantingTicketLength);
if (ticket->MessageType != KerbTicketLogon)
{
WLog_ERR(TAG, "Not a KerbTicketLogon");
return FALSE;
}
if (!Stream_CheckAndLogRequiredLength(
TAG, s, 16ull + ticket->ServiceTicketLength + ticket->TicketGrantingTicketLength))
return FALSE;
/* mysterious 16 bytes in the way, maybe they would need to be interpreted... */
Stream_Seek(s, 16);
/*WLog_INFO(TAG, "TGS");
winpr_HexDump(TAG, WLOG_DEBUG, Stream_PointerAs(s, const BYTE), ticket->ServiceTicketLength);*/
ticket->ServiceTicket = Stream_PointerAs(s, UCHAR);
Stream_Seek(s, ticket->ServiceTicketLength);
/*WLog_INFO(TAG, "TGT");
winpr_HexDump(TAG, WLOG_DEBUG, Stream_PointerAs(s, const BYTE),
ticket->TicketGrantingTicketLength);*/
ticket->TicketGrantingTicket = Stream_PointerAs(s, UCHAR);
return TRUE;
}
/** @brief kind of RCG credentials */
typedef enum
{
RCG_TYPE_KERB,
RCG_TYPE_NTLM
} RemoteGuardPackageCredType;
static BOOL nla_read_TSRemoteGuardPackageCred(rdpNla* nla, WinPrAsn1Decoder* dec,
RemoteGuardPackageCredType* credsType,
wStream* payload)
{
WinPrAsn1_OctetString packageName = { 0 };
WinPrAsn1_OctetString credBuffer = { 0 };
BOOL error = FALSE;
char packageNameStr[100] = { 0 };
/* packageName [0] OCTET STRING */
if (!WinPrAsn1DecReadContextualOctetString(dec, 0, &error, &packageName, FALSE) || error)
return TRUE;
ConvertMszWCharNToUtf8((WCHAR*)packageName.data, packageName.len / sizeof(WCHAR),
packageNameStr, 100);
WLog_DBG(TAG, "TSRemoteGuardPackageCred(%s)", packageNameStr);
/* credBuffer [1] OCTET STRING, */
if (!WinPrAsn1DecReadContextualOctetString(dec, 1, &error, &credBuffer, FALSE) || error)
return TRUE;
if (_stricmp(packageNameStr, "Kerberos") == 0)
{
*credsType = RCG_TYPE_KERB;
}
else if (_stricmp(packageNameStr, "NTLM") == 0)
{
*credsType = RCG_TYPE_NTLM;
}
else
{
WLog_INFO(TAG, "TSRemoteGuardPackageCred package %s not handled", packageNameStr);
return FALSE;
}
Stream_StaticInit(payload, credBuffer.data, credBuffer.len);
return TRUE;
}
/** @brief kind of TSCreds */
typedef enum
{
TSCREDS_USER_PASSWD = 1,
TSCREDS_SMARTCARD = 2,
TSCREDS_REMOTEGUARD = 6
} TsCredentialsType;
static BOOL nla_read_ts_credentials(rdpNla* nla, SecBuffer* data) static BOOL nla_read_ts_credentials(rdpNla* nla, SecBuffer* data)
{ {
WinPrAsn1Decoder dec = { 0 }; WinPrAsn1Decoder dec = { 0 };
@ -1141,6 +1237,7 @@ static BOOL nla_read_ts_credentials(rdpNla* nla, SecBuffer* data)
WinPrAsn1_OctetString credentials = { 0 }; WinPrAsn1_OctetString credentials = { 0 };
BOOL error = FALSE; BOOL error = FALSE;
WinPrAsn1_INTEGER credType = -1; WinPrAsn1_INTEGER credType = -1;
BOOL ret = true;
WINPR_ASSERT(nla); WINPR_ASSERT(nla);
WINPR_ASSERT(data); WINPR_ASSERT(data);
@ -1163,10 +1260,16 @@ static BOOL nla_read_ts_credentials(rdpNla* nla, SecBuffer* data)
WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, credentials.data, credentials.len); WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, credentials.data, credentials.len);
rdpSettings* settings = nla->rdpcontext->settings; rdpSettings* settings = nla->rdpcontext->settings;
if (nego_get_remoteCredentialGuard(nla->rdpcontext->rdp->nego) &&
credType != TSCREDS_REMOTEGUARD)
{
WLog_ERR(TAG, "connecting with RCG but it's not TSRemoteGuard credentials");
return FALSE;
}
switch (credType) switch (credType)
{ {
case 1: case TSCREDS_USER_PASSWD:
{ {
/* TSPasswordCreds */ /* TSPasswordCreds */
if (!WinPrAsn1DecReadSequence(&dec, &dec2)) if (!WinPrAsn1DecReadSequence(&dec, &dec2))
@ -1184,7 +1287,7 @@ static BOOL nla_read_ts_credentials(rdpNla* nla, SecBuffer* data)
/* password [2] OCTET STRING */ /* password [2] OCTET STRING */
return set_creds_octetstring_to_settings(&dec, 2, FALSE, FreeRDP_Password, settings); return set_creds_octetstring_to_settings(&dec, 2, FALSE, FreeRDP_Password, settings);
} }
case 2: case TSCREDS_SMARTCARD:
{ {
/* TSSmartCardCreds */ /* TSSmartCardCreds */
if (!WinPrAsn1DecReadSequence(&dec, &dec2)) if (!WinPrAsn1DecReadSequence(&dec, &dec2))
@ -1210,12 +1313,78 @@ static BOOL nla_read_ts_credentials(rdpNla* nla, SecBuffer* data)
/* domainHint [3] OCTET STRING OPTIONAL */ /* domainHint [3] OCTET STRING OPTIONAL */
return set_creds_octetstring_to_settings(&dec, 3, TRUE, FreeRDP_Domain, settings); return set_creds_octetstring_to_settings(&dec, 3, TRUE, FreeRDP_Domain, settings);
} }
case TSCREDS_REMOTEGUARD:
{
/* TSRemoteGuardCreds */
if (!WinPrAsn1DecReadSequence(&dec, &dec2))
return FALSE;
/* logonCred[0] TSRemoteGuardPackageCred */
BOOL error = FALSE;
KERB_TICKET_LOGON kerbLogon;
WinPrAsn1Decoder logonCredsSeq = { 0 };
if (!WinPrAsn1DecReadContextualSequence(&dec2, 0, &error, &logonCredsSeq) || error)
return FALSE;
RemoteGuardPackageCredType logonCredsType;
wStream logonPayload;
if (!nla_read_TSRemoteGuardPackageCred(nla, &logonCredsSeq, &logonCredsType,
&logonPayload))
return FALSE;
if (logonCredsType != RCG_TYPE_KERB)
{
WLog_ERR(TAG, "logonCred must be some Kerberos creds");
return FALSE;
}
if (!nla_read_KERB_TICKET_LOGON(nla, &logonPayload, &kerbLogon))
{
WLog_ERR(TAG, "invalid KERB_TICKET_LOGON");
return FALSE;
}
/* supplementalCreds [1] SEQUENCE OF TSRemoteGuardPackageCred OPTIONAL, */
MSV1_0_SUPPLEMENTAL_CREDENTIAL ntlmCreds;
MSV1_0_SUPPLEMENTAL_CREDENTIAL* suppCreds = NULL;
WinPrAsn1Decoder suppCredsSeq = { 0 };
if (WinPrAsn1DecReadContextualSequence(&dec2, 1, &error, &suppCredsSeq))
{
WinPrAsn1Decoder ntlmCredsSeq = { 0 };
if (!WinPrAsn1DecReadSequence(&suppCredsSeq, &ntlmCredsSeq))
return FALSE;
RemoteGuardPackageCredType suppCredsType;
wStream ntlmPayload;
if (!nla_read_TSRemoteGuardPackageCred(nla, &ntlmCredsSeq, &suppCredsType,
&ntlmPayload))
return FALSE;
if (suppCredsType != RCG_TYPE_NTLM)
{
WLog_ERR(TAG, "supplementalCreds must be some NTLM creds");
return FALSE;
}
/* TODO: suppCreds = &ntlmCreds; and parse NTLM creds */
}
else if (error)
{
WLog_ERR(TAG, "invalid supplementalCreds");
return FALSE;
}
freerdp_peer* peer = nla->rdpcontext->peer;
ret = IFCALLRESULT(TRUE, peer->RemoteCredentials, peer, &kerbLogon, suppCreds);
break;
}
default: default:
WLog_DBG(TAG, "TSCredentials type " PRIu32 " not supported for now", credType); WLog_DBG(TAG, "TSCredentials type " PRIu32 " not supported for now", credType);
ret = FALSE;
break; break;
} }
return TRUE; return ret;
} }
/** /**
@ -1231,7 +1400,7 @@ static BOOL nla_encode_ts_credentials(rdpNla* nla)
WinPrAsn1Encoder* enc = NULL; WinPrAsn1Encoder* enc = NULL;
size_t length = 0; size_t length = 0;
wStream s = { 0 }; wStream s = { 0 };
int credType = -1; TsCredentialsType credType;
WINPR_ASSERT(nla); WINPR_ASSERT(nla);
WINPR_ASSERT(nla->rdpcontext); WINPR_ASSERT(nla->rdpcontext);
@ -1239,6 +1408,13 @@ static BOOL nla_encode_ts_credentials(rdpNla* nla)
rdpSettings* settings = nla->rdpcontext->settings; rdpSettings* settings = nla->rdpcontext->settings;
WINPR_ASSERT(settings); WINPR_ASSERT(settings);
if (settings->RemoteCredentialGuard)
credType = TSCREDS_REMOTEGUARD;
else if (settings->SmartcardLogon)
credType = TSCREDS_SMARTCARD;
else
credType = TSCREDS_USER_PASSWD;
enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER); enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
if (!enc) if (!enc)
return FALSE; return FALSE;
@ -1248,15 +1424,16 @@ static BOOL nla_encode_ts_credentials(rdpNla* nla)
goto out; goto out;
/* credType [0] INTEGER */ /* credType [0] INTEGER */
credType = settings->SmartcardLogon ? 2 : 1; if (!WinPrAsn1EncContextualInteger(enc, 0, (WinPrAsn1_INTEGER)credType))
if (!WinPrAsn1EncContextualInteger(enc, 0, credType))
goto out; goto out;
/* credentials [1] OCTET STRING */ /* credentials [1] OCTET STRING */
if (!WinPrAsn1EncContextualOctetStringContainer(enc, 1)) if (!WinPrAsn1EncContextualOctetStringContainer(enc, 1))
goto out; goto out;
if (settings->SmartcardLogon) switch (credType)
{
case TSCREDS_SMARTCARD:
{ {
struct struct
{ {
@ -1287,8 +1464,8 @@ static BOOL nla_encode_ts_credentials(rdpNla* nla)
goto out; goto out;
/* keySpec [0] INTEGER */ /* keySpec [0] INTEGER */
if (!WinPrAsn1EncContextualInteger(enc, 0, if (!WinPrAsn1EncContextualInteger(
freerdp_settings_get_uint32(settings, FreeRDP_KeySpec))) enc, 0, freerdp_settings_get_uint32(settings, FreeRDP_KeySpec)))
goto out; goto out;
for (size_t i = 0; i < ARRAYSIZE(cspData_fields); i++) for (size_t i = 0; i < ARRAYSIZE(cspData_fields); i++)
@ -1315,8 +1492,9 @@ static BOOL nla_encode_ts_credentials(rdpNla* nla)
/* End TSSmartCardCreds */ /* End TSSmartCardCreds */
if (!WinPrAsn1EncEndContainer(enc)) if (!WinPrAsn1EncEndContainer(enc))
goto out; goto out;
break;
} }
else case TSCREDS_USER_PASSWD:
{ {
WinPrAsn1_OctetString username = { 0 }; WinPrAsn1_OctetString username = { 0 };
WinPrAsn1_OctetString domain = { 0 }; WinPrAsn1_OctetString domain = { 0 };
@ -1348,6 +1526,11 @@ static BOOL nla_encode_ts_credentials(rdpNla* nla)
/* End TSPasswordCreds */ /* End TSPasswordCreds */
if (!WinPrAsn1EncEndContainer(enc)) if (!WinPrAsn1EncEndContainer(enc))
goto out; goto out;
break;
}
case TSCREDS_REMOTEGUARD:
default:
goto out;
} }
/* End credentials | End TSCredentials */ /* End credentials | End TSCredentials */
@ -1364,8 +1547,8 @@ static BOOL nla_encode_ts_credentials(rdpNla* nla)
} }
Stream_StaticInit(&s, (BYTE*)nla->tsCredentials.pvBuffer, length); Stream_StaticInit(&s, (BYTE*)nla->tsCredentials.pvBuffer, length);
if (WinPrAsn1EncToStream(enc, &s))
ret = TRUE; ret = WinPrAsn1EncToStream(enc, &s);
out: out:
WinPrAsn1Encoder_Free(&enc); WinPrAsn1Encoder_Free(&enc);

View File

@ -267,6 +267,7 @@ static BOOL freerdp_peer_initialize(freerdp_peer* client)
} }
} }
nego_set_RCG_supported(rdp->nego, settings->RemoteCredentialGuard);
if (!rdp_server_transition_to_state(rdp, CONNECTION_STATE_INITIAL)) if (!rdp_server_transition_to_state(rdp, CONNECTION_STATE_INITIAL))
return FALSE; return FALSE;

View File

@ -139,6 +139,7 @@ static const size_t bool_list_indices[] = {
FreeRDP_RemoteAssistanceMode, FreeRDP_RemoteAssistanceMode,
FreeRDP_RemoteAssistanceRequestControl, FreeRDP_RemoteAssistanceRequestControl,
FreeRDP_RemoteConsoleAudio, FreeRDP_RemoteConsoleAudio,
FreeRDP_RemoteCredentialGuard,
FreeRDP_RemoteFxCodec, FreeRDP_RemoteFxCodec,
FreeRDP_RemoteFxImageCodec, FreeRDP_RemoteFxImageCodec,
FreeRDP_RemoteFxOnly, FreeRDP_RemoteFxOnly,

View File

@ -56,6 +56,8 @@ int main(int argc, char** argv)
"Select rectangle within monitor to share" }, "Select rectangle within monitor to share" },
{ "auth", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, { "auth", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL,
"Clients must authenticate" }, "Clients must authenticate" },
{ "remote-guard", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL,
"Remote credential guard" },
{ "may-view", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, { "may-view", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
"Clients may view without prompt" }, "Clients may view without prompt" },
{ "may-interact", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, { "may-interact", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,

View File

@ -303,6 +303,10 @@ int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** a
{ {
server->authentication = arg->Value ? TRUE : FALSE; server->authentication = arg->Value ? TRUE : FALSE;
} }
CommandLineSwitchCase(arg, "remote-guard")
{
settings->RemoteCredentialGuard = arg->Value ? TRUE : FALSE;
}
CommandLineSwitchCase(arg, "sec") CommandLineSwitchCase(arg, "sec")
{ {
if (strcmp("rdp", arg->Value) == 0) /* Standard RDP */ if (strcmp("rdp", arg->Value) == 0) /* Standard RDP */

View File

@ -0,0 +1,78 @@
/**
* WinPR: Windows Portable Runtime
* Schannel Security Package
*
* Copyright 2023 David Fort <contact@hardening-consulting.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef WINPR_SECAPI_H_
#define WINPR_SECAPI_H_
#ifdef _WIN32
#define _NTDEF_
#include <ntsecapi.h>
#else
#include <winpr/wtypes.h>
typedef enum _KERB_LOGON_SUBMIT_TYPE
{
KerbInteractiveLogon = 2,
KerbSmartCardLogon = 6,
KerbWorkstationUnlockLogon = 7,
KerbSmartCardUnlockLogon = 8,
KerbProxyLogon = 9,
KerbTicketLogon = 10,
KerbTicketUnlockLogon = 11,
KerbS4ULogon = 12,
KerbCertificateLogon = 13,
KerbCertificateS4ULogon = 14,
KerbCertificateUnlockLogon = 15,
KerbNoElevationLogon = 83,
KerbLuidLogon = 84
} KERB_LOGON_SUBMIT_TYPE,
*PKERB_LOGON_SUBMIT_TYPE;
typedef struct _KERB_TICKET_LOGON
{
KERB_LOGON_SUBMIT_TYPE MessageType;
ULONG Flags;
ULONG ServiceTicketLength;
ULONG TicketGrantingTicketLength;
PUCHAR ServiceTicket;
PUCHAR TicketGrantingTicket;
} KERB_TICKET_LOGON, *PKERB_TICKET_LOGON;
#define KERB_LOGON_FLAG_ALLOW_EXPIRED_TICKET 0x1
#define MSV1_0_OWF_PASSWORD_LENGTH 16
typedef struct _MSV1_0_SUPPLEMENTAL_CREDENTIAL
{
ULONG Version;
ULONG Flags;
UCHAR LmPassword[MSV1_0_OWF_PASSWORD_LENGTH];
UCHAR NtPassword[MSV1_0_OWF_PASSWORD_LENGTH];
} MSV1_0_SUPPLEMENTAL_CREDENTIAL, *PMSV1_0_SUPPLEMENTAL_CREDENTIAL;
#define MSV1_0_CRED_VERSION_REMOTE 0xffff0002
#endif /* _WIN32 */
#ifndef KERB_LOGON_FLAG_REDIRECTED
#define KERB_LOGON_FLAG_REDIRECTED 0x2
#endif
#endif /* WINPR_SECAPI_H_ */