mirror of
https://github.com/FreeRDP/FreeRDP.git
synced 2025-06-03 00:00:20 +00:00
[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:
parent
20f09b2d34
commit
061148f856
@ -31,6 +31,7 @@
|
||||
#include <winpr/sspi.h>
|
||||
#include <winpr/ntlm.h>
|
||||
#include <winpr/winsock.h>
|
||||
#include <winpr/secapi.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
@ -42,60 +43,64 @@ extern "C"
|
||||
|
||||
typedef BOOL (*psPeerInitialize)(freerdp_peer* peer);
|
||||
#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,
|
||||
int* rcount);)
|
||||
#endif
|
||||
typedef HANDLE (*psPeerGetEventHandle)(freerdp_peer* peer);
|
||||
typedef DWORD (*psPeerGetEventHandles)(freerdp_peer* peer, HANDLE* events, DWORD count);
|
||||
typedef HANDLE (*psPeerGetReceiveEventHandle)(freerdp_peer* peer);
|
||||
typedef BOOL (*psPeerCheckFileDescriptor)(freerdp_peer* peer);
|
||||
typedef BOOL (*psPeerIsWriteBlocked)(freerdp_peer* peer);
|
||||
typedef int (*psPeerDrainOutputBuffer)(freerdp_peer* peer);
|
||||
typedef BOOL (*psPeerHasMoreToRead)(freerdp_peer* peer);
|
||||
typedef BOOL (*psPeerClose)(freerdp_peer* peer);
|
||||
typedef void (*psPeerDisconnect)(freerdp_peer* peer);
|
||||
typedef BOOL (*psPeerCapabilities)(freerdp_peer* peer);
|
||||
typedef BOOL (*psPeerPostConnect)(freerdp_peer* peer);
|
||||
typedef BOOL (*psPeerActivate)(freerdp_peer* peer);
|
||||
typedef BOOL (*psPeerLogon)(freerdp_peer* peer, const SEC_WINNT_AUTH_IDENTITY* identity,
|
||||
typedef HANDLE (*psPeerGetEventHandle)(freerdp_peer* peer);
|
||||
typedef DWORD (*psPeerGetEventHandles)(freerdp_peer* peer, HANDLE* events, DWORD count);
|
||||
typedef HANDLE (*psPeerGetReceiveEventHandle)(freerdp_peer* peer);
|
||||
typedef BOOL (*psPeerCheckFileDescriptor)(freerdp_peer* peer);
|
||||
typedef BOOL (*psPeerIsWriteBlocked)(freerdp_peer* peer);
|
||||
typedef int (*psPeerDrainOutputBuffer)(freerdp_peer* peer);
|
||||
typedef BOOL (*psPeerHasMoreToRead)(freerdp_peer* peer);
|
||||
typedef BOOL (*psPeerClose)(freerdp_peer* peer);
|
||||
typedef void (*psPeerDisconnect)(freerdp_peer* peer);
|
||||
typedef BOOL (*psPeerRemoteCredentials)(freerdp_peer* peer, KERB_TICKET_LOGON* logonCreds,
|
||||
MSV1_0_SUPPLEMENTAL_CREDENTIAL* suppCreds);
|
||||
typedef BOOL (*psPeerCapabilities)(freerdp_peer* peer);
|
||||
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);
|
||||
typedef BOOL (*psPeerSendServerRedirection)(freerdp_peer* peer, const rdpRedirection* redirection);
|
||||
typedef BOOL (*psPeerAdjustMonitorsLayout)(freerdp_peer* peer);
|
||||
typedef BOOL (*psPeerClientCapabilities)(freerdp_peer* peer);
|
||||
typedef BOOL (*psPeerSendServerRedirection)(freerdp_peer* peer,
|
||||
const rdpRedirection* redirection);
|
||||
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);
|
||||
typedef BOOL (*psPeerSendChannelPacket)(freerdp_peer* client, UINT16 channelId, size_t totalSize,
|
||||
UINT32 flags, const BYTE* data, size_t chunkSize);
|
||||
typedef BOOL (*psPeerReceiveChannelData)(freerdp_peer* peer, UINT16 channelId, const BYTE* data,
|
||||
typedef BOOL (*psPeerSendChannelPacket)(freerdp_peer* client, UINT16 channelId,
|
||||
size_t totalSize, UINT32 flags, 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);
|
||||
|
||||
typedef HANDLE (*psPeerVirtualChannelOpen)(freerdp_peer* peer, const char* name, UINT32 flags);
|
||||
typedef BOOL (*psPeerVirtualChannelClose)(freerdp_peer* peer, HANDLE hChannel);
|
||||
typedef int (*psPeerVirtualChannelRead)(freerdp_peer* peer, HANDLE hChannel, BYTE* buffer,
|
||||
typedef HANDLE (*psPeerVirtualChannelOpen)(freerdp_peer* peer, const char* name, UINT32 flags);
|
||||
typedef BOOL (*psPeerVirtualChannelClose)(freerdp_peer* peer, HANDLE hChannel);
|
||||
typedef int (*psPeerVirtualChannelRead)(freerdp_peer* peer, HANDLE hChannel, BYTE* buffer,
|
||||
UINT32 length);
|
||||
typedef int (*psPeerVirtualChannelWrite)(freerdp_peer* peer, HANDLE hChannel, const BYTE* buffer,
|
||||
UINT32 length);
|
||||
typedef void* (*psPeerVirtualChannelGetData)(freerdp_peer* peer, HANDLE hChannel);
|
||||
typedef int (*psPeerVirtualChannelSetData)(freerdp_peer* peer, HANDLE hChannel, void* data);
|
||||
typedef BOOL (*psPeerSetState)(freerdp_peer* peer, CONNECTION_STATE state);
|
||||
typedef BOOL (*psPeerReachedState)(freerdp_peer* peer, CONNECTION_STATE state);
|
||||
typedef int (*psPeerVirtualChannelWrite)(freerdp_peer* peer, HANDLE hChannel,
|
||||
const BYTE* buffer, UINT32 length);
|
||||
typedef void* (*psPeerVirtualChannelGetData)(freerdp_peer* peer, HANDLE hChannel);
|
||||
typedef int (*psPeerVirtualChannelSetData)(freerdp_peer* peer, HANDLE hChannel, void* data);
|
||||
typedef BOOL (*psPeerSetState)(freerdp_peer* peer, CONNECTION_STATE state);
|
||||
typedef BOOL (*psPeerReachedState)(freerdp_peer* peer, CONNECTION_STATE state);
|
||||
|
||||
/** @brief the result of the license callback */
|
||||
typedef enum
|
||||
{
|
||||
/** @brief the result of the license callback */
|
||||
typedef enum
|
||||
{
|
||||
LICENSE_CB_INTERNAL_ERROR, /** an internal error happened in the callback */
|
||||
LICENSE_CB_ABORT, /** licensing process failed, abort the connection */
|
||||
LICENSE_CB_IN_PROGRESS, /** incoming packet has been treated, we're waiting for further packets
|
||||
to complete the workflow */
|
||||
LICENSE_CB_IN_PROGRESS, /** incoming packet has been treated, we're waiting for further
|
||||
packets to complete the workflow */
|
||||
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 int sockfd;
|
||||
@ -104,7 +109,8 @@ struct rdp_freerdp_peer
|
||||
#if defined(WITH_FREERDP_DEPRECATED)
|
||||
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::autodetect instead", ALIGN64 rdpAutoDetect* autodetect;)
|
||||
WINPR_DEPRECATED_VAR("Use rdpContext::autodetect instead",
|
||||
ALIGN64 rdpAutoDetect* autodetect;)
|
||||
#else
|
||||
UINT64 reservedX[3];
|
||||
#endif
|
||||
@ -181,7 +187,13 @@ struct rdp_freerdp_peer
|
||||
ALIGN64 psPeerSetState SetState;
|
||||
ALIGN64 psPeerReachedState ReachedState;
|
||||
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_ex(freerdp_peer* client, const rdpSettings* settings);
|
||||
|
@ -651,6 +651,7 @@ extern "C"
|
||||
#define FreeRDP_RdstlsSecurity (1111)
|
||||
#define FreeRDP_AadSecurity (1112)
|
||||
#define FreeRDP_WinSCardModule (1113)
|
||||
#define FreeRDP_RemoteCredentialGuard (1114)
|
||||
#define FreeRDP_MstscCookieMode (1152)
|
||||
#define FreeRDP_CookieMaxLength (1153)
|
||||
#define FreeRDP_PreconnectionId (1154)
|
||||
@ -1185,7 +1186,8 @@ extern "C"
|
||||
ALIGN64 BOOL RdstlsSecurity; /* 1111 */
|
||||
ALIGN64 BOOL AadSecurity; /* 1112 */
|
||||
ALIGN64 char* WinSCardModule; /* 1113 */
|
||||
UINT64 padding1152[1152 - 1114]; /* 1114 */
|
||||
ALIGN64 BOOL RemoteCredentialGuard; /* 1114 */
|
||||
UINT64 padding1152[1152 - 1115]; /* 1115 */
|
||||
|
||||
/* Connection Cookie */
|
||||
ALIGN64 BOOL MstscCookieMode; /* 1152 */
|
||||
|
@ -465,6 +465,9 @@ BOOL freerdp_settings_get_bool(const rdpSettings* settings, size_t id)
|
||||
case FreeRDP_RemoteConsoleAudio:
|
||||
return settings->RemoteConsoleAudio;
|
||||
|
||||
case FreeRDP_RemoteCredentialGuard:
|
||||
return settings->RemoteCredentialGuard;
|
||||
|
||||
case FreeRDP_RemoteFxCodec:
|
||||
return settings->RemoteFxCodec;
|
||||
|
||||
@ -1182,6 +1185,10 @@ BOOL freerdp_settings_set_bool(rdpSettings* settings, size_t id, BOOL val)
|
||||
settings->RemoteConsoleAudio = cnv.c;
|
||||
break;
|
||||
|
||||
case FreeRDP_RemoteCredentialGuard:
|
||||
settings->RemoteCredentialGuard = cnv.c;
|
||||
break;
|
||||
|
||||
case FreeRDP_RemoteFxCodec:
|
||||
settings->RemoteFxCodec = cnv.c;
|
||||
break;
|
||||
|
@ -192,6 +192,7 @@ static const struct settings_str_entry settings_map[] = {
|
||||
{ FreeRDP_RemoteAssistanceRequestControl, FREERDP_SETTINGS_TYPE_BOOL,
|
||||
"FreeRDP_RemoteAssistanceRequestControl" },
|
||||
{ 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_RemoteFxImageCodec, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_RemoteFxImageCodec" },
|
||||
{ FreeRDP_RemoteFxOnly, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_RemoteFxOnly" },
|
||||
|
@ -60,6 +60,9 @@ struct rdp_nego
|
||||
BOOL NegotiateSecurityLayer;
|
||||
BOOL EnabledProtocols[32];
|
||||
BOOL RestrictedAdminModeRequired;
|
||||
BOOL RemoteCredsGuardRequired;
|
||||
BOOL RemoteCredsGuardActive;
|
||||
BOOL RemoteCredsGuardSupported;
|
||||
BOOL GatewayEnabled;
|
||||
BOOL GatewayBypassLocal;
|
||||
BOOL ConnectChildSession;
|
||||
@ -1112,6 +1115,9 @@ BOOL nego_send_negotiation_request(rdpNego* nego)
|
||||
if (nego->RestrictedAdminModeRequired)
|
||||
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, flags);
|
||||
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)
|
||||
{
|
||||
WLog_ERR(TAG, "RDP_NEG_REQ::flags REDIRECTED_AUTHENTICATION_MODE_REQUIRED: FreeRDP does "
|
||||
"not support Remote Credential Guard");
|
||||
if (!nego->RemoteCredsGuardSupported)
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"RDP_NEG_REQ::flags REDIRECTED_AUTHENTICATION_MODE_REQUIRED but disabled");
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
WLog_INFO(TAG, "RDP_NEG_REQ::flags REDIRECTED_AUTHENTICATION_MODE_REQUIRED");
|
||||
}
|
||||
nego->RemoteCredsGuardActive = TRUE;
|
||||
}
|
||||
|
||||
Stream_Read_UINT16(s, length);
|
||||
if (length != 8)
|
||||
@ -1418,6 +1432,12 @@ BOOL nego_send_negotiation_response(rdpNego* nego)
|
||||
if (freerdp_settings_get_bool(settings, FreeRDP_SupportGraphicsPipeline))
|
||||
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 */
|
||||
Stream_Write_UINT8(s, TYPE_RDP_NEG_RSP);
|
||||
Stream_Write_UINT8(s, flags); /* flags */
|
||||
@ -1648,6 +1668,28 @@ void nego_set_restricted_admin_mode_required(rdpNego* nego, BOOL RestrictedAdmin
|
||||
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)
|
||||
{
|
||||
WINPR_ASSERT(nego);
|
||||
|
@ -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_restricted_admin_mode_required(rdpNego* nego,
|
||||
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_gateway_enabled(rdpNego* nego, BOOL GatewayEnabled);
|
||||
FREERDP_LOCAL void nego_set_gateway_bypass_local(rdpNego* nego, BOOL GatewayBypassLocal);
|
||||
|
@ -40,8 +40,11 @@
|
||||
#include <winpr/cred.h>
|
||||
#include <winpr/debug.h>
|
||||
#include <winpr/asn1.h>
|
||||
#include <winpr/secapi.h>
|
||||
|
||||
#include "../crypto/tls.h"
|
||||
#include "nego.h"
|
||||
#include "rdp.h"
|
||||
#include "nla.h"
|
||||
#include "utils.h"
|
||||
#include "credssp_auth.h"
|
||||
@ -336,6 +339,9 @@ static BOOL nla_client_setup_identity(rdpNla* nla)
|
||||
if ((settings->PasswordHash) && (strlen(settings->PasswordHash) > 0))
|
||||
PromptPassword = FALSE;
|
||||
}
|
||||
|
||||
if (settings->RemoteCredentialGuard)
|
||||
PromptPassword = FALSE;
|
||||
}
|
||||
#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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
WinPrAsn1Decoder dec = { 0 };
|
||||
@ -1141,6 +1237,7 @@ static BOOL nla_read_ts_credentials(rdpNla* nla, SecBuffer* data)
|
||||
WinPrAsn1_OctetString credentials = { 0 };
|
||||
BOOL error = FALSE;
|
||||
WinPrAsn1_INTEGER credType = -1;
|
||||
BOOL ret = true;
|
||||
|
||||
WINPR_ASSERT(nla);
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
case 1:
|
||||
case TSCREDS_USER_PASSWD:
|
||||
{
|
||||
/* TSPasswordCreds */
|
||||
if (!WinPrAsn1DecReadSequence(&dec, &dec2))
|
||||
@ -1184,7 +1287,7 @@ static BOOL nla_read_ts_credentials(rdpNla* nla, SecBuffer* data)
|
||||
/* password [2] OCTET STRING */
|
||||
return set_creds_octetstring_to_settings(&dec, 2, FALSE, FreeRDP_Password, settings);
|
||||
}
|
||||
case 2:
|
||||
case TSCREDS_SMARTCARD:
|
||||
{
|
||||
/* TSSmartCardCreds */
|
||||
if (!WinPrAsn1DecReadSequence(&dec, &dec2))
|
||||
@ -1210,12 +1313,78 @@ static BOOL nla_read_ts_credentials(rdpNla* nla, SecBuffer* data)
|
||||
/* domainHint [3] OCTET STRING OPTIONAL */
|
||||
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:
|
||||
WLog_DBG(TAG, "TSCredentials type " PRIu32 " not supported for now", credType);
|
||||
ret = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1231,7 +1400,7 @@ static BOOL nla_encode_ts_credentials(rdpNla* nla)
|
||||
WinPrAsn1Encoder* enc = NULL;
|
||||
size_t length = 0;
|
||||
wStream s = { 0 };
|
||||
int credType = -1;
|
||||
TsCredentialsType credType;
|
||||
|
||||
WINPR_ASSERT(nla);
|
||||
WINPR_ASSERT(nla->rdpcontext);
|
||||
@ -1239,6 +1408,13 @@ static BOOL nla_encode_ts_credentials(rdpNla* nla)
|
||||
rdpSettings* settings = nla->rdpcontext->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);
|
||||
if (!enc)
|
||||
return FALSE;
|
||||
@ -1248,15 +1424,16 @@ static BOOL nla_encode_ts_credentials(rdpNla* nla)
|
||||
goto out;
|
||||
|
||||
/* credType [0] INTEGER */
|
||||
credType = settings->SmartcardLogon ? 2 : 1;
|
||||
if (!WinPrAsn1EncContextualInteger(enc, 0, credType))
|
||||
if (!WinPrAsn1EncContextualInteger(enc, 0, (WinPrAsn1_INTEGER)credType))
|
||||
goto out;
|
||||
|
||||
/* credentials [1] OCTET STRING */
|
||||
if (!WinPrAsn1EncContextualOctetStringContainer(enc, 1))
|
||||
goto out;
|
||||
|
||||
if (settings->SmartcardLogon)
|
||||
switch (credType)
|
||||
{
|
||||
case TSCREDS_SMARTCARD:
|
||||
{
|
||||
struct
|
||||
{
|
||||
@ -1287,8 +1464,8 @@ static BOOL nla_encode_ts_credentials(rdpNla* nla)
|
||||
goto out;
|
||||
|
||||
/* keySpec [0] INTEGER */
|
||||
if (!WinPrAsn1EncContextualInteger(enc, 0,
|
||||
freerdp_settings_get_uint32(settings, FreeRDP_KeySpec)))
|
||||
if (!WinPrAsn1EncContextualInteger(
|
||||
enc, 0, freerdp_settings_get_uint32(settings, FreeRDP_KeySpec)))
|
||||
goto out;
|
||||
|
||||
for (size_t i = 0; i < ARRAYSIZE(cspData_fields); i++)
|
||||
@ -1315,8 +1492,9 @@ static BOOL nla_encode_ts_credentials(rdpNla* nla)
|
||||
/* End TSSmartCardCreds */
|
||||
if (!WinPrAsn1EncEndContainer(enc))
|
||||
goto out;
|
||||
break;
|
||||
}
|
||||
else
|
||||
case TSCREDS_USER_PASSWD:
|
||||
{
|
||||
WinPrAsn1_OctetString username = { 0 };
|
||||
WinPrAsn1_OctetString domain = { 0 };
|
||||
@ -1348,6 +1526,11 @@ static BOOL nla_encode_ts_credentials(rdpNla* nla)
|
||||
/* End TSPasswordCreds */
|
||||
if (!WinPrAsn1EncEndContainer(enc))
|
||||
goto out;
|
||||
break;
|
||||
}
|
||||
case TSCREDS_REMOTEGUARD:
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* End credentials | End TSCredentials */
|
||||
@ -1364,8 +1547,8 @@ static BOOL nla_encode_ts_credentials(rdpNla* nla)
|
||||
}
|
||||
|
||||
Stream_StaticInit(&s, (BYTE*)nla->tsCredentials.pvBuffer, length);
|
||||
if (WinPrAsn1EncToStream(enc, &s))
|
||||
ret = TRUE;
|
||||
|
||||
ret = WinPrAsn1EncToStream(enc, &s);
|
||||
|
||||
out:
|
||||
WinPrAsn1Encoder_Free(&enc);
|
||||
|
@ -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))
|
||||
return FALSE;
|
||||
|
||||
|
@ -139,6 +139,7 @@ static const size_t bool_list_indices[] = {
|
||||
FreeRDP_RemoteAssistanceMode,
|
||||
FreeRDP_RemoteAssistanceRequestControl,
|
||||
FreeRDP_RemoteConsoleAudio,
|
||||
FreeRDP_RemoteCredentialGuard,
|
||||
FreeRDP_RemoteFxCodec,
|
||||
FreeRDP_RemoteFxImageCodec,
|
||||
FreeRDP_RemoteFxOnly,
|
||||
|
@ -56,6 +56,8 @@ int main(int argc, char** argv)
|
||||
"Select rectangle within monitor to share" },
|
||||
{ "auth", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL,
|
||||
"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,
|
||||
"Clients may view without prompt" },
|
||||
{ "may-interact", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
|
||||
|
@ -303,6 +303,10 @@ int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** a
|
||||
{
|
||||
server->authentication = arg->Value ? TRUE : FALSE;
|
||||
}
|
||||
CommandLineSwitchCase(arg, "remote-guard")
|
||||
{
|
||||
settings->RemoteCredentialGuard = arg->Value ? TRUE : FALSE;
|
||||
}
|
||||
CommandLineSwitchCase(arg, "sec")
|
||||
{
|
||||
if (strcmp("rdp", arg->Value) == 0) /* Standard RDP */
|
||||
|
78
winpr/include/winpr/secapi.h
Normal file
78
winpr/include/winpr/secapi.h
Normal 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_ */
|
Loading…
Reference in New Issue
Block a user