mirror of
https://github.com/morgan9e/FreeRDP
synced 2026-04-15 00:44:19 +09:00
Add RDSTLS security protocol
The client tries to connect using RDSTLS only when it has received a server redirection PDU with LB_PASSWORD_IS_ENCRYPTED flag. The server exposes RDSTLS on negotiation if it has been configured on settings. Then authenticates a client using configured credentials from settings: RedirectionGuid, Username, Domain, Password.
This commit is contained in:
@@ -631,6 +631,7 @@ typedef struct
|
||||
#define FreeRDP_TLSMaxVersion (1108)
|
||||
#define FreeRDP_TlsSecretsFile (1109)
|
||||
#define FreeRDP_AuthenticationPackageList (1110)
|
||||
#define FreeRDP_RdstlsSecurity (1111)
|
||||
#define FreeRDP_MstscCookieMode (1152)
|
||||
#define FreeRDP_CookieMaxLength (1153)
|
||||
#define FreeRDP_PreconnectionId (1154)
|
||||
@@ -1142,7 +1143,8 @@ struct rdp_settings
|
||||
ALIGN64 UINT16 TLSMaxVersion; /* 1108 */
|
||||
ALIGN64 char* TlsSecretsFile; /* 1109 */
|
||||
ALIGN64 char* AuthenticationPackageList; /* 1110 */
|
||||
UINT64 padding1152[1152 - 1111]; /* 1111 */
|
||||
ALIGN64 BOOL RdstlsSecurity; /* 1111 */
|
||||
UINT64 padding1152[1152 - 1112]; /* 1112 */
|
||||
|
||||
/* Connection Cookie */
|
||||
ALIGN64 BOOL MstscCookieMode; /* 1152 */
|
||||
|
||||
@@ -399,6 +399,9 @@ BOOL freerdp_settings_get_bool(const rdpSettings* settings, size_t id)
|
||||
case FreeRDP_RdpSecurity:
|
||||
return settings->RdpSecurity;
|
||||
|
||||
case FreeRDP_RdstlsSecurity:
|
||||
return settings->RdstlsSecurity;
|
||||
|
||||
case FreeRDP_RedirectClipboard:
|
||||
return settings->RedirectClipboard;
|
||||
|
||||
@@ -1067,6 +1070,10 @@ BOOL freerdp_settings_set_bool(rdpSettings* settings, size_t id, BOOL val)
|
||||
settings->RdpSecurity = cnv.c;
|
||||
break;
|
||||
|
||||
case FreeRDP_RdstlsSecurity:
|
||||
settings->RdstlsSecurity = cnv.c;
|
||||
break;
|
||||
|
||||
case FreeRDP_RedirectClipboard:
|
||||
settings->RedirectClipboard = cnv.c;
|
||||
break;
|
||||
|
||||
@@ -168,6 +168,7 @@ static const struct settings_str_entry settings_map[] = {
|
||||
{ FreeRDP_PrintReconnectCookie, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_PrintReconnectCookie" },
|
||||
{ FreeRDP_PromptForCredentials, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_PromptForCredentials" },
|
||||
{ FreeRDP_RdpSecurity, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_RdpSecurity" },
|
||||
{ FreeRDP_RdstlsSecurity, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_RdstlsSecurity" },
|
||||
{ FreeRDP_RedirectClipboard, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_RedirectClipboard" },
|
||||
{ FreeRDP_RedirectDrives, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_RedirectDrives" },
|
||||
{ FreeRDP_RedirectHomeDrive, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_RedirectHomeDrive" },
|
||||
|
||||
@@ -132,7 +132,9 @@ set(${MODULE_PREFIX}_SRCS
|
||||
display.c
|
||||
display.h
|
||||
credssp_auth.c
|
||||
credssp_auth.h)
|
||||
credssp_auth.h
|
||||
rdstls.c
|
||||
rdstls.h)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} ${${MODULE_PREFIX}_GATEWAY_SRCS})
|
||||
|
||||
|
||||
@@ -392,6 +392,7 @@ BOOL rdp_client_connect(rdpRdp* rdp)
|
||||
nego_enable_tls(rdp->nego, settings->TlsSecurity);
|
||||
nego_enable_nla(rdp->nego, settings->NlaSecurity);
|
||||
nego_enable_ext(rdp->nego, settings->ExtSecurity);
|
||||
nego_enable_rdstls(rdp->nego, settings->RdstlsSecurity);
|
||||
|
||||
if (settings->MstscCookieMode)
|
||||
settings->CookieMaxLength = MSTSC_COOKIE_MAX_LENGTH;
|
||||
@@ -424,7 +425,8 @@ BOOL rdp_client_connect(rdpRdp* rdp)
|
||||
|
||||
SelectedProtocol = nego_get_selected_protocol(rdp->nego);
|
||||
|
||||
if ((SelectedProtocol & PROTOCOL_SSL) || (SelectedProtocol == PROTOCOL_RDP))
|
||||
if ((SelectedProtocol & PROTOCOL_SSL) || (SelectedProtocol == PROTOCOL_RDP) ||
|
||||
(SelectedProtocol == PROTOCOL_RDSTLS))
|
||||
{
|
||||
wStream s = { 0 };
|
||||
|
||||
@@ -658,6 +660,8 @@ BOOL rdp_client_redirect(rdpRdp* rdp)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
settings->RdstlsSecurity = settings->RedirectionFlags & LB_PASSWORD_IS_PK_ENCRYPTED;
|
||||
|
||||
WINPR_ASSERT(rdp->context);
|
||||
WINPR_ASSERT(rdp->context->instance);
|
||||
if (!IFCALLRESULT(TRUE, rdp->context->instance->Redirect, rdp->context->instance))
|
||||
@@ -1363,14 +1367,21 @@ BOOL rdp_server_accept_nego(rdpRdp* rdp, wStream* s)
|
||||
return FALSE;
|
||||
|
||||
RequestedProtocols = nego_get_requested_protocols(nego);
|
||||
WLog_INFO(TAG, "Client Security: NLA:%d TLS:%d RDP:%d",
|
||||
WLog_INFO(TAG, "Client Security: RDSTLS:%d NLA:%d TLS:%d RDP:%d",
|
||||
(RequestedProtocols & PROTOCOL_RDSTLS) ? 1 : 0,
|
||||
(RequestedProtocols & PROTOCOL_HYBRID) ? 1 : 0,
|
||||
(RequestedProtocols & PROTOCOL_SSL) ? 1 : 0,
|
||||
(RequestedProtocols == PROTOCOL_RDP) ? 1 : 0);
|
||||
WLog_INFO(TAG, "Server Security: NLA:%" PRId32 " TLS:%" PRId32 " RDP:%" PRId32 "",
|
||||
settings->NlaSecurity, settings->TlsSecurity, settings->RdpSecurity);
|
||||
WLog_INFO(TAG,
|
||||
"Server Security: RDSTLS:%" PRId32 " NLA:%" PRId32 " TLS:%" PRId32 " RDP:%" PRId32 "",
|
||||
settings->RdstlsSecurity, settings->NlaSecurity, settings->TlsSecurity,
|
||||
settings->RdpSecurity);
|
||||
|
||||
if ((settings->NlaSecurity) && (RequestedProtocols & PROTOCOL_HYBRID))
|
||||
if ((settings->RdstlsSecurity) && (RequestedProtocols & PROTOCOL_RDSTLS))
|
||||
{
|
||||
SelectedProtocol = PROTOCOL_RDSTLS;
|
||||
}
|
||||
else if ((settings->NlaSecurity) && (RequestedProtocols & PROTOCOL_HYBRID))
|
||||
{
|
||||
SelectedProtocol = PROTOCOL_HYBRID;
|
||||
}
|
||||
@@ -1414,7 +1425,8 @@ BOOL rdp_server_accept_nego(rdpRdp* rdp, wStream* s)
|
||||
|
||||
if (!(SelectedProtocol & PROTOCOL_FAILED_NEGO))
|
||||
{
|
||||
WLog_INFO(TAG, "Negotiated Security: NLA:%d TLS:%d RDP:%d",
|
||||
WLog_INFO(TAG, "Negotiated Security: RDSTLS:%d NLA:%d TLS:%d RDP:%d",
|
||||
(SelectedProtocol & PROTOCOL_RDSTLS) ? 1 : 0,
|
||||
(SelectedProtocol & PROTOCOL_HYBRID) ? 1 : 0,
|
||||
(SelectedProtocol & PROTOCOL_SSL) ? 1 : 0,
|
||||
(SelectedProtocol == PROTOCOL_RDP) ? 1 : 0);
|
||||
@@ -1429,7 +1441,9 @@ BOOL rdp_server_accept_nego(rdpRdp* rdp, wStream* s)
|
||||
SelectedProtocol = nego_get_selected_protocol(nego);
|
||||
status = FALSE;
|
||||
|
||||
if (SelectedProtocol & PROTOCOL_HYBRID)
|
||||
if (SelectedProtocol & PROTOCOL_RDSTLS)
|
||||
status = transport_accept_rdstls(rdp->transport);
|
||||
else if (SelectedProtocol & PROTOCOL_HYBRID)
|
||||
status = transport_accept_nla(rdp->transport);
|
||||
else if (SelectedProtocol & PROTOCOL_SSL)
|
||||
status = transport_accept_tls(rdp->transport);
|
||||
|
||||
@@ -67,10 +67,11 @@ struct rdp_nego
|
||||
|
||||
static const char* nego_state_string(NEGO_STATE state)
|
||||
{
|
||||
static const char* const NEGO_STATE_STRINGS[] = { "NEGO_STATE_INITIAL", "NEGO_STATE_EXT",
|
||||
"NEGO_STATE_NLA", "NEGO_STATE_TLS",
|
||||
"NEGO_STATE_RDP", "NEGO_STATE_FAIL",
|
||||
"NEGO_STATE_FINAL", "NEGO_STATE_INVALID" };
|
||||
static const char* const NEGO_STATE_STRINGS[] = { "NEGO_STATE_INITIAL", "NEGO_STATE_RDSTLS",
|
||||
"NEGO_STATE_EXT", "NEGO_STATE_NLA",
|
||||
"NEGO_STATE_TLS", "NEGO_STATE_RDP",
|
||||
"NEGO_STATE_FAIL", "NEGO_STATE_FINAL",
|
||||
"NEGO_STATE_INVALID" };
|
||||
if (state >= ARRAYSIZE(NEGO_STATE_STRINGS))
|
||||
return NEGO_STATE_STRINGS[ARRAYSIZE(NEGO_STATE_STRINGS) - 1];
|
||||
return NEGO_STATE_STRINGS[state];
|
||||
@@ -78,7 +79,7 @@ static const char* nego_state_string(NEGO_STATE state)
|
||||
|
||||
static const char* protocol_security_string(UINT32 security)
|
||||
{
|
||||
static const char* PROTOCOL_SECURITY_STRINGS[] = { "RDP", "TLS", "NLA", "UNK", "UNK",
|
||||
static const char* PROTOCOL_SECURITY_STRINGS[] = { "RDP", "TLS", "NLA", "UNK", "RDSTLS",
|
||||
"UNK", "UNK", "UNK", "EXT", "UNK" };
|
||||
if (security >= ARRAYSIZE(PROTOCOL_SECURITY_STRINGS))
|
||||
return PROTOCOL_SECURITY_STRINGS[ARRAYSIZE(PROTOCOL_SECURITY_STRINGS) - 1];
|
||||
@@ -115,7 +116,11 @@ BOOL nego_connect(rdpNego* nego)
|
||||
|
||||
if (nego_get_state(nego) == NEGO_STATE_INITIAL)
|
||||
{
|
||||
if (nego->EnabledProtocols[PROTOCOL_HYBRID_EX])
|
||||
if (nego->EnabledProtocols[PROTOCOL_RDSTLS])
|
||||
{
|
||||
nego_set_state(nego, NEGO_STATE_RDSTLS);
|
||||
}
|
||||
else if (nego->EnabledProtocols[PROTOCOL_HYBRID_EX])
|
||||
{
|
||||
nego_set_state(nego, NEGO_STATE_EXT);
|
||||
}
|
||||
@@ -146,9 +151,14 @@ BOOL nego_connect(rdpNego* nego)
|
||||
nego->EnabledProtocols[PROTOCOL_SSL] = FALSE;
|
||||
nego->EnabledProtocols[PROTOCOL_RDP] = FALSE;
|
||||
nego->EnabledProtocols[PROTOCOL_HYBRID_EX] = FALSE;
|
||||
nego->EnabledProtocols[PROTOCOL_RDSTLS] = FALSE;
|
||||
|
||||
switch (nego_get_state(nego))
|
||||
{
|
||||
case NEGO_STATE_RDSTLS:
|
||||
nego->EnabledProtocols[PROTOCOL_RDSTLS] = TRUE;
|
||||
nego->SelectedProtocol = PROTOCOL_RDSTLS;
|
||||
break;
|
||||
case NEGO_STATE_EXT:
|
||||
nego->EnabledProtocols[PROTOCOL_HYBRID_EX] = TRUE;
|
||||
nego->EnabledProtocols[PROTOCOL_HYBRID] = TRUE;
|
||||
@@ -262,7 +272,12 @@ BOOL nego_security_connect(rdpNego* nego)
|
||||
}
|
||||
else if (!nego->SecurityConnected)
|
||||
{
|
||||
if (nego->SelectedProtocol == PROTOCOL_HYBRID)
|
||||
if (nego->SelectedProtocol == PROTOCOL_RDSTLS)
|
||||
{
|
||||
WLog_DBG(TAG, "nego_security_connect with PROTOCOL_RDSTLS");
|
||||
nego->SecurityConnected = transport_connect_rdstls(nego->transport);
|
||||
}
|
||||
else if (nego->SelectedProtocol == PROTOCOL_HYBRID)
|
||||
{
|
||||
WLog_DBG(TAG, "nego_security_connect with PROTOCOL_HYBRID");
|
||||
nego->SecurityConnected = transport_connect_nla(nego->transport);
|
||||
@@ -442,6 +457,49 @@ BOOL nego_send_preconnection_pdu(rdpNego* nego)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void nego_attempt_rdstls(rdpNego* nego)
|
||||
{
|
||||
WINPR_ASSERT(nego);
|
||||
nego->RequestedProtocols = PROTOCOL_RDSTLS | PROTOCOL_SSL;
|
||||
WLog_DBG(TAG, "Attempting RDSTLS security");
|
||||
|
||||
if (!nego_transport_connect(nego))
|
||||
{
|
||||
nego_set_state(nego, NEGO_STATE_FAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nego_send_negotiation_request(nego))
|
||||
{
|
||||
nego_set_state(nego, NEGO_STATE_FAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nego_recv_response(nego))
|
||||
{
|
||||
nego_set_state(nego, NEGO_STATE_FAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
WLog_DBG(TAG, "state: %s", nego_state_string(nego_get_state(nego)));
|
||||
|
||||
if (nego_get_state(nego) != NEGO_STATE_FINAL)
|
||||
{
|
||||
nego_transport_disconnect(nego);
|
||||
|
||||
if (nego->EnabledProtocols[PROTOCOL_HYBRID_EX])
|
||||
nego_set_state(nego, NEGO_STATE_EXT);
|
||||
else if (nego->EnabledProtocols[PROTOCOL_HYBRID])
|
||||
nego_set_state(nego, NEGO_STATE_NLA);
|
||||
else if (nego->EnabledProtocols[PROTOCOL_SSL])
|
||||
nego_set_state(nego, NEGO_STATE_TLS);
|
||||
else if (nego->EnabledProtocols[PROTOCOL_RDP])
|
||||
nego_set_state(nego, NEGO_STATE_RDP);
|
||||
else
|
||||
nego_set_state(nego, NEGO_STATE_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
static void nego_attempt_ext(rdpNego* nego)
|
||||
{
|
||||
WINPR_ASSERT(nego);
|
||||
@@ -863,6 +921,9 @@ void nego_send(rdpNego* nego)
|
||||
|
||||
switch (nego_get_state(nego))
|
||||
{
|
||||
case NEGO_STATE_RDSTLS:
|
||||
nego_attempt_rdstls(nego);
|
||||
break;
|
||||
case NEGO_STATE_EXT:
|
||||
nego_attempt_ext(nego);
|
||||
break;
|
||||
@@ -1258,7 +1319,7 @@ BOOL nego_send_negotiation_response(rdpNego* nego)
|
||||
if (freerdp_settings_get_bool(settings, FreeRDP_SupportGraphicsPipeline))
|
||||
flags |= DYNVC_GFX_PROTOCOL_SUPPORTED;
|
||||
|
||||
/* RDP_NEG_DATA must be present for TLS, NLA, and RDP */
|
||||
/* 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 */
|
||||
Stream_Write_UINT16(s, 8); /* RDP_NEG_DATA length (8) */
|
||||
@@ -1339,6 +1400,8 @@ BOOL nego_send_negotiation_response(rdpNego* nego)
|
||||
return FALSE;
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
|
||||
return FALSE;
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_RdstlsSecurity, FALSE))
|
||||
return FALSE;
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
|
||||
return FALSE;
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, FALSE))
|
||||
@@ -1354,6 +1417,25 @@ BOOL nego_send_negotiation_response(rdpNego* nego)
|
||||
return FALSE;
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, TRUE))
|
||||
return FALSE;
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_RdstlsSecurity, FALSE))
|
||||
return FALSE;
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
|
||||
return FALSE;
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, FALSE))
|
||||
return FALSE;
|
||||
|
||||
if (!freerdp_settings_set_uint32(settings, FreeRDP_EncryptionLevel,
|
||||
ENCRYPTION_LEVEL_NONE))
|
||||
return FALSE;
|
||||
}
|
||||
else if (nego->SelectedProtocol == PROTOCOL_RDSTLS)
|
||||
{
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, TRUE))
|
||||
return FALSE;
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
|
||||
return FALSE;
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_RdstlsSecurity, TRUE))
|
||||
return FALSE;
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
|
||||
return FALSE;
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, FALSE))
|
||||
@@ -1512,6 +1594,19 @@ void nego_enable_nla(rdpNego* nego, BOOL enable_nla)
|
||||
nego->EnabledProtocols[PROTOCOL_HYBRID] = enable_nla;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable RDSTLS security protocol.
|
||||
* @param nego A pointer to the NEGO struct pointer to the negotiation structure
|
||||
* @param enable_rdstls whether to enable RDSTLS protocol (TRUE for enabled,
|
||||
* FALSE for disabled)
|
||||
*/
|
||||
|
||||
void nego_enable_rdstls(rdpNego* nego, BOOL enable_rdstls)
|
||||
{
|
||||
WLog_DBG(TAG, "Enabling RDSTLS security: %s", enable_rdstls ? "TRUE" : "FALSE");
|
||||
nego->EnabledProtocols[PROTOCOL_RDSTLS] = enable_rdstls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable NLA extended security protocol.
|
||||
* @param nego A pointer to the NEGO struct pointer to the negotiation structure
|
||||
|
||||
@@ -58,11 +58,12 @@ enum RDP_NEG_FAILURE_FAILURECODES
|
||||
typedef enum
|
||||
{
|
||||
NEGO_STATE_INITIAL,
|
||||
NEGO_STATE_EXT, /* Extended NLA (NLA + TLS implicit) */
|
||||
NEGO_STATE_NLA, /* Network Level Authentication (TLS implicit) */
|
||||
NEGO_STATE_TLS, /* TLS Encryption without NLA */
|
||||
NEGO_STATE_RDP, /* Standard Legacy RDP Encryption */
|
||||
NEGO_STATE_FAIL, /* Negotiation failure */
|
||||
NEGO_STATE_RDSTLS, /* RDSTLS (TLS implicit) */
|
||||
NEGO_STATE_EXT, /* Extended NLA (NLA + TLS implicit) */
|
||||
NEGO_STATE_NLA, /* Network Level Authentication (TLS implicit) */
|
||||
NEGO_STATE_TLS, /* TLS Encryption without NLA */
|
||||
NEGO_STATE_RDP, /* Standard Legacy RDP Encryption */
|
||||
NEGO_STATE_FAIL, /* Negotiation failure */
|
||||
NEGO_STATE_FINAL
|
||||
} NEGO_STATE;
|
||||
|
||||
@@ -120,6 +121,7 @@ FREERDP_LOCAL void nego_set_gateway_bypass_local(rdpNego* nego, BOOL GatewayBypa
|
||||
FREERDP_LOCAL void nego_enable_rdp(rdpNego* nego, BOOL enable_rdp);
|
||||
FREERDP_LOCAL void nego_enable_tls(rdpNego* nego, BOOL enable_tls);
|
||||
FREERDP_LOCAL void nego_enable_nla(rdpNego* nego, BOOL enable_nla);
|
||||
FREERDP_LOCAL void nego_enable_rdstls(rdpNego* nego, BOOL enable_rdstls);
|
||||
FREERDP_LOCAL void nego_enable_ext(rdpNego* nego, BOOL enable_ext);
|
||||
FREERDP_LOCAL const BYTE* nego_get_routing_token(rdpNego* nego, DWORD* RoutingTokenLength);
|
||||
FREERDP_LOCAL BOOL nego_set_routing_token(rdpNego* nego, const BYTE* RoutingToken,
|
||||
|
||||
@@ -822,6 +822,7 @@ static state_run_t peer_recv_callback_internal(rdpTransport* transport, wStream*
|
||||
else
|
||||
{
|
||||
SelectedProtocol = nego_get_selected_protocol(rdp->nego);
|
||||
settings->RdstlsSecurity = (SelectedProtocol & PROTOCOL_RDSTLS) ? TRUE : FALSE;
|
||||
settings->NlaSecurity = (SelectedProtocol & PROTOCOL_HYBRID) ? TRUE : FALSE;
|
||||
settings->TlsSecurity = (SelectedProtocol & PROTOCOL_SSL) ? TRUE : FALSE;
|
||||
settings->RdpSecurity = (SelectedProtocol == PROTOCOL_RDP) ? TRUE : FALSE;
|
||||
|
||||
785
libfreerdp/core/rdstls.c
Normal file
785
libfreerdp/core/rdstls.c
Normal file
@@ -0,0 +1,785 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* RDSTLS Security protocol
|
||||
*
|
||||
* Copyright 2023 Joan Torres <joan.torres@suse.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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#include <freerdp/error.h>
|
||||
#include <freerdp/settings.h>
|
||||
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/wlog.h>
|
||||
|
||||
#include "rdstls.h"
|
||||
#include "transport.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define TAG FREERDP_TAG("core.rdstls")
|
||||
|
||||
struct rdp_rdstls
|
||||
{
|
||||
BOOL server;
|
||||
RDSTLS_STATE state;
|
||||
rdpContext* context;
|
||||
rdpTransport* transport;
|
||||
|
||||
UINT32 resultCode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create new RDSTLS state machine.
|
||||
*
|
||||
* @param context A pointer to the rdp context to use
|
||||
*
|
||||
* @return new RDSTLS state machine.
|
||||
*/
|
||||
|
||||
rdpRdstls* rdstls_new(rdpContext* context, rdpTransport* transport)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(transport);
|
||||
|
||||
rdpSettings* settings = context->settings;
|
||||
WINPR_ASSERT(settings);
|
||||
|
||||
rdpRdstls* rdstls = (rdpRdstls*)calloc(1, sizeof(rdpRdstls));
|
||||
|
||||
if (!rdstls)
|
||||
return NULL;
|
||||
|
||||
rdstls->context = context;
|
||||
rdstls->transport = transport;
|
||||
rdstls->server = settings->ServerMode;
|
||||
|
||||
return rdstls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free RDSTLS state machine.
|
||||
* @param rdstls The RDSTLS instance to free
|
||||
*/
|
||||
|
||||
void rdstls_free(rdpRdstls* rdstls)
|
||||
{
|
||||
if (!rdstls)
|
||||
return;
|
||||
|
||||
free(rdstls);
|
||||
}
|
||||
|
||||
static const char* rdstls_get_state_str(RDSTLS_STATE state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case RDSTLS_STATE_CAPABILITIES:
|
||||
return "RDSTLS_STATE_CAPABILITIES";
|
||||
case RDSTLS_STATE_AUTH_REQ:
|
||||
return "RDSTLS_STATE_AUTH_REQ";
|
||||
case RDSTLS_STATE_AUTH_RSP:
|
||||
return "RDSTLS_STATE_AUTH_RSP";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static RDSTLS_STATE rdstls_get_state(rdpRdstls* rdstls)
|
||||
{
|
||||
WINPR_ASSERT(rdstls);
|
||||
return rdstls->state;
|
||||
}
|
||||
|
||||
static BOOL rdstls_set_state(rdpRdstls* rdstls, RDSTLS_STATE state)
|
||||
{
|
||||
WINPR_ASSERT(rdstls);
|
||||
|
||||
WLog_DBG(TAG, "-- %s\t--> %s", rdstls_get_state_str(rdstls->state),
|
||||
rdstls_get_state_str(state));
|
||||
rdstls->state = state;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdstls_write_capabilities(rdpRdstls* rdstls, wStream* s)
|
||||
{
|
||||
if (!Stream_EnsureRemainingCapacity(s, 6))
|
||||
return FALSE;
|
||||
|
||||
Stream_Write_UINT16(s, RDSTLS_TYPE_CAPABILITIES);
|
||||
Stream_Write_UINT16(s, RDSTLS_DATA_CAPABILITIES);
|
||||
Stream_Write_UINT16(s, RDSTLS_VERSION_1);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static SSIZE_T rdstls_write_string(wStream* s, const char* str)
|
||||
{
|
||||
const size_t pos = Stream_GetPosition(s);
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, 2))
|
||||
return -1;
|
||||
|
||||
if (!str)
|
||||
{
|
||||
/* Write unicode null */
|
||||
Stream_Write_UINT16(s, 2);
|
||||
Stream_Write_UINT16(s, 0);
|
||||
return (SSIZE_T)(Stream_GetPosition(s) - pos);
|
||||
}
|
||||
|
||||
const size_t length = (strlen(str) + 1);
|
||||
|
||||
Stream_Write_UINT16(s, (UINT16)length * sizeof(WCHAR));
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, length * sizeof(WCHAR)))
|
||||
return -1;
|
||||
|
||||
if (Stream_Write_UTF16_String_From_UTF8(s, length, str, length, TRUE) < 0)
|
||||
return -1;
|
||||
|
||||
return (SSIZE_T)(Stream_GetPosition(s) - pos);
|
||||
}
|
||||
|
||||
static BOOL rdstls_write_data(wStream* s, UINT32 length, const BYTE* data)
|
||||
{
|
||||
if (!Stream_EnsureRemainingCapacity(s, 2))
|
||||
return -1;
|
||||
|
||||
if (!data)
|
||||
{
|
||||
/* Write unicode null */
|
||||
Stream_Write_UINT16(s, 2);
|
||||
Stream_Write_UINT16(s, 0);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Stream_Write_UINT16(s, length);
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, length))
|
||||
return FALSE;
|
||||
|
||||
Stream_Write(s, data, length);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdstls_write_authentication_request_with_password(rdpRdstls* rdstls, wStream* s)
|
||||
{
|
||||
rdpSettings* settings = rdstls->context->settings;
|
||||
WINPR_ASSERT(settings);
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, 4))
|
||||
return FALSE;
|
||||
|
||||
Stream_Write_UINT16(s, RDSTLS_TYPE_AUTHREQ);
|
||||
Stream_Write_UINT16(s, RDSTLS_DATA_PASSWORD_CREDS);
|
||||
|
||||
if (!rdstls_write_data(s, settings->RedirectionGuidLength, settings->RedirectionGuid))
|
||||
return FALSE;
|
||||
|
||||
if (rdstls_write_string(s, settings->Username) < 0)
|
||||
return FALSE;
|
||||
|
||||
if (rdstls_write_string(s, settings->Domain) < 0)
|
||||
return FALSE;
|
||||
|
||||
if (!rdstls_write_data(s, settings->RedirectionPasswordLength, settings->RedirectionPassword))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdstls_write_authentication_request_with_cookie(rdpRdstls* rdstls, wStream* s)
|
||||
{
|
||||
// TODO
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL rdstls_write_authentication_response(rdpRdstls* rdstls, wStream* s)
|
||||
{
|
||||
if (!Stream_EnsureRemainingCapacity(s, 8))
|
||||
return FALSE;
|
||||
|
||||
Stream_Write_UINT16(s, RDSTLS_TYPE_AUTHRSP);
|
||||
Stream_Write_UINT16(s, RDSTLS_DATA_RESULT_CODE);
|
||||
Stream_Write_UINT32(s, rdstls->resultCode);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdstls_process_capabilities(rdpRdstls* rdstls, wStream* s)
|
||||
{
|
||||
UINT16 dataType;
|
||||
UINT16 supportedVersions;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
||||
return FALSE;
|
||||
|
||||
Stream_Read_UINT16(s, dataType);
|
||||
if (dataType != RDSTLS_DATA_CAPABILITIES)
|
||||
{
|
||||
WLog_ERR(TAG, "received invalid DataType=0x%04" PRIX16 ", expected 0x%04" PRIX16, dataType,
|
||||
RDSTLS_DATA_CAPABILITIES);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Stream_Read_UINT16(s, supportedVersions);
|
||||
if ((supportedVersions & RDSTLS_VERSION_1) == 0)
|
||||
{
|
||||
WLog_ERR(TAG, "received invalid supportedVersions=0x%04" PRIX16 ", expected 0x%04" PRIX16,
|
||||
supportedVersions, RDSTLS_VERSION_1);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdstls_read_unicode_string(wStream* s, char** str)
|
||||
{
|
||||
UINT16 length = 0;
|
||||
|
||||
WINPR_ASSERT(str);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
|
||||
return FALSE;
|
||||
|
||||
Stream_Read_UINT16(s, length);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
|
||||
return FALSE;
|
||||
|
||||
if (length <= 2)
|
||||
{
|
||||
Stream_Seek(s, length);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
*str = Stream_Read_UTF16_String_As_UTF8(s, length / sizeof(WCHAR), NULL);
|
||||
if (!*str)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdstls_read_data(wStream* s, UINT16* pLength, BYTE** pData)
|
||||
{
|
||||
UINT16 length = 0;
|
||||
|
||||
WINPR_ASSERT(pLength);
|
||||
WINPR_ASSERT(pData);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
|
||||
return FALSE;
|
||||
|
||||
Stream_Read_UINT16(s, length);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
|
||||
return FALSE;
|
||||
|
||||
if (length <= 2)
|
||||
{
|
||||
Stream_Seek(s, length);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
free(*pData);
|
||||
*pData = (BYTE*)malloc(length);
|
||||
if (!*pData)
|
||||
return FALSE;
|
||||
|
||||
Stream_Read(s, *pData, length);
|
||||
*pLength = length;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdstls_cmp_data(const char* field, const BYTE* serverData,
|
||||
const UINT32 serverDataLength, const BYTE* clientData,
|
||||
const UINT16 clientDataLength)
|
||||
{
|
||||
if (serverDataLength > 0)
|
||||
{
|
||||
if (clientDataLength == 0)
|
||||
{
|
||||
WLog_ERR(TAG, "expected %s", field);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (serverDataLength > UINT16_MAX || serverDataLength != clientDataLength ||
|
||||
memcmp(serverData, clientData, serverDataLength) != 0)
|
||||
{
|
||||
WLog_ERR(TAG, "%s verification failed", field);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdstls_cmp_str(const char* field, const char* serverStr, const char* clientStr)
|
||||
{
|
||||
if (!utils_str_is_empty(serverStr))
|
||||
{
|
||||
if (utils_str_is_empty(clientStr))
|
||||
{
|
||||
WLog_ERR(TAG, "expected %s", field);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (strcmp(serverStr, clientStr) != 0)
|
||||
{
|
||||
WLog_ERR(TAG, "%s verification failed", field);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdstls_process_authentication_request_with_password(rdpRdstls* rdstls, wStream* s)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
|
||||
BYTE* clientRedirectionGuid = NULL;
|
||||
UINT16 clientRedirectionGuidLength = 0;
|
||||
char* clientPassword = NULL;
|
||||
char* clientUsername = NULL;
|
||||
char* clientDomain = NULL;
|
||||
|
||||
const BYTE* serverRedirectionGuid = NULL;
|
||||
UINT16 serverRedirectionGuidLength = 0;
|
||||
const char* serverPassword = NULL;
|
||||
const char* serverUsername = NULL;
|
||||
const char* serverDomain = NULL;
|
||||
|
||||
rdpSettings* settings = rdstls->context->settings;
|
||||
WINPR_ASSERT(settings);
|
||||
|
||||
if (!rdstls_read_data(s, &clientRedirectionGuidLength, &clientRedirectionGuid))
|
||||
goto fail;
|
||||
|
||||
if (!rdstls_read_unicode_string(s, &clientUsername))
|
||||
goto fail;
|
||||
|
||||
if (!rdstls_read_unicode_string(s, &clientDomain))
|
||||
goto fail;
|
||||
|
||||
if (!rdstls_read_unicode_string(s, &clientPassword))
|
||||
goto fail;
|
||||
|
||||
serverRedirectionGuid = freerdp_settings_get_pointer(settings, FreeRDP_RedirectionGuid);
|
||||
serverRedirectionGuidLength =
|
||||
freerdp_settings_get_uint32(settings, FreeRDP_RedirectionGuidLength);
|
||||
serverUsername = freerdp_settings_get_string(settings, FreeRDP_Username);
|
||||
serverDomain = freerdp_settings_get_string(settings, FreeRDP_Domain);
|
||||
serverPassword = freerdp_settings_get_pointer(settings, FreeRDP_Password);
|
||||
|
||||
rdstls->resultCode = ERROR_SUCCESS;
|
||||
|
||||
if (!rdstls_cmp_data("RedirectionGuid", serverRedirectionGuid, serverRedirectionGuidLength,
|
||||
clientRedirectionGuid, clientRedirectionGuidLength))
|
||||
rdstls->resultCode = ERROR_LOGON_FAILURE;
|
||||
|
||||
if (!rdstls_cmp_str("UserName", serverUsername, clientUsername))
|
||||
rdstls->resultCode = ERROR_LOGON_FAILURE;
|
||||
|
||||
if (!rdstls_cmp_str("Domain", serverDomain, clientDomain))
|
||||
rdstls->resultCode = ERROR_LOGON_FAILURE;
|
||||
|
||||
if (!rdstls_cmp_str("Password", serverPassword, clientPassword))
|
||||
rdstls->resultCode = ERROR_LOGON_FAILURE;
|
||||
|
||||
rc = TRUE;
|
||||
fail:
|
||||
free(clientRedirectionGuid);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static BOOL rdstls_process_authentication_request_with_cookie(rdpRdstls* rdstls, wStream* s)
|
||||
{
|
||||
// TODO
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL rdstls_process_authentication_request(rdpRdstls* rdstls, wStream* s)
|
||||
{
|
||||
UINT16 dataType;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
|
||||
return FALSE;
|
||||
|
||||
Stream_Read_UINT16(s, dataType);
|
||||
switch (dataType)
|
||||
{
|
||||
case RDSTLS_DATA_PASSWORD_CREDS:
|
||||
if (!rdstls_process_authentication_request_with_password(rdstls, s))
|
||||
return FALSE;
|
||||
break;
|
||||
case RDSTLS_DATA_AUTORECONNECT_COOKIE:
|
||||
if (!rdstls_process_authentication_request_with_cookie(rdstls, s))
|
||||
return FALSE;
|
||||
break;
|
||||
default:
|
||||
WLog_ERR(TAG,
|
||||
"received invalid DataType=0x%04" PRIX16 ", expected 0x%04" PRIX16
|
||||
" or 0x%04" PRIX16,
|
||||
dataType, RDSTLS_DATA_PASSWORD_CREDS, RDSTLS_DATA_AUTORECONNECT_COOKIE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdstls_process_authentication_response(rdpRdstls* rdstls, wStream* s)
|
||||
{
|
||||
UINT16 dataType;
|
||||
UINT32 resultCode;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
|
||||
return FALSE;
|
||||
|
||||
Stream_Read_UINT16(s, dataType);
|
||||
if (dataType != RDSTLS_DATA_RESULT_CODE)
|
||||
{
|
||||
WLog_ERR(TAG, "received invalid DataType=0x%04" PRIX16 ", expected 0x%04" PRIX16, dataType,
|
||||
RDSTLS_DATA_RESULT_CODE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, resultCode);
|
||||
if (resultCode != ERROR_SUCCESS)
|
||||
{
|
||||
WLog_ERR(TAG, "resultCode: %s [0x%08" PRIX32 "] %s",
|
||||
freerdp_get_last_error_name(resultCode), resultCode,
|
||||
freerdp_get_last_error_string(resultCode));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdstls_send(rdpTransport* transport, wStream* s, void* extra)
|
||||
{
|
||||
rdpRdstls* rdstls = (rdpRdstls*)extra;
|
||||
rdpSettings* settings = NULL;
|
||||
|
||||
WINPR_ASSERT(transport);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(rdstls);
|
||||
|
||||
settings = rdstls->context->settings;
|
||||
WINPR_ASSERT(settings);
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, 2))
|
||||
return FALSE;
|
||||
|
||||
Stream_Write_UINT16(s, RDSTLS_VERSION_1);
|
||||
|
||||
switch (rdstls_get_state(rdstls))
|
||||
{
|
||||
case RDSTLS_STATE_CAPABILITIES:
|
||||
if (!rdstls_write_capabilities(rdstls, s))
|
||||
return FALSE;
|
||||
break;
|
||||
case RDSTLS_STATE_AUTH_REQ:
|
||||
if (settings->RedirectionFlags & LB_PASSWORD_IS_PK_ENCRYPTED)
|
||||
{
|
||||
if (!rdstls_write_authentication_request_with_password(rdstls, s))
|
||||
return FALSE;
|
||||
}
|
||||
else if (settings->ServerAutoReconnectCookie != NULL)
|
||||
{
|
||||
if (!rdstls_write_authentication_request_with_cookie(rdstls, s))
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
WLog_ERR(TAG, "cannot authenticate with password or auto-reconnect cookie");
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
case RDSTLS_STATE_AUTH_RSP:
|
||||
if (!rdstls_write_authentication_response(rdstls, s))
|
||||
return FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (transport_write(rdstls->transport, s) < 0)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int rdstls_recv(rdpTransport* transport, wStream* s, void* extra)
|
||||
{
|
||||
UINT16 length;
|
||||
UINT16 version;
|
||||
UINT16 pduType;
|
||||
rdpRdstls* rdstls = (rdpRdstls*)extra;
|
||||
|
||||
WINPR_ASSERT(transport);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(rdstls);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
||||
return FALSE;
|
||||
|
||||
Stream_Read_UINT16(s, version);
|
||||
if (version != RDSTLS_VERSION_1)
|
||||
{
|
||||
WLog_ERR(TAG, "received invalid RDSTLS Version=0x%04" PRIX16 ", expected 0x%04" PRIX16,
|
||||
version, RDSTLS_VERSION_1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Stream_Read_UINT16(s, pduType);
|
||||
switch (pduType)
|
||||
{
|
||||
case RDSTLS_TYPE_CAPABILITIES:
|
||||
if (!rdstls_process_capabilities(rdstls, s))
|
||||
return -1;
|
||||
break;
|
||||
case RDSTLS_TYPE_AUTHREQ:
|
||||
if (!rdstls_process_authentication_request(rdstls, s))
|
||||
return -1;
|
||||
break;
|
||||
case RDSTLS_TYPE_AUTHRSP:
|
||||
if (!rdstls_process_authentication_response(rdstls, s))
|
||||
return -1;
|
||||
break;
|
||||
default:
|
||||
WLog_ERR(TAG, "unknown RDSTLS PDU type");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static BOOL rdstls_send_capabilities(rdpRdstls* rdstls)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(rdstls);
|
||||
|
||||
if (rdstls_get_state(rdstls) != RDSTLS_STATE_CAPABILITIES)
|
||||
goto fail;
|
||||
|
||||
s = Stream_New(NULL, 512);
|
||||
if (!s)
|
||||
goto fail;
|
||||
|
||||
if (!rdstls_send(rdstls->transport, s, rdstls))
|
||||
goto fail;
|
||||
|
||||
rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_REQ);
|
||||
fail:
|
||||
Stream_Free(s, TRUE);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static BOOL rdstls_recv_authentication_request(rdpRdstls* rdstls)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
int status;
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(rdstls);
|
||||
|
||||
if (rdstls_get_state(rdstls) != RDSTLS_STATE_AUTH_REQ)
|
||||
goto fail;
|
||||
|
||||
s = Stream_New(NULL, 4096);
|
||||
if (!s)
|
||||
goto fail;
|
||||
|
||||
status = transport_read_pdu(rdstls->transport, s);
|
||||
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
|
||||
status = rdstls_recv(rdstls->transport, s, rdstls);
|
||||
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
|
||||
rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_RSP);
|
||||
fail:
|
||||
Stream_Free(s, TRUE);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static BOOL rdstls_send_authentication_response(rdpRdstls* rdstls)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(rdstls);
|
||||
|
||||
if (rdstls_get_state(rdstls) != RDSTLS_STATE_AUTH_RSP)
|
||||
goto fail;
|
||||
|
||||
s = Stream_New(NULL, 512);
|
||||
if (!s)
|
||||
goto fail;
|
||||
|
||||
if (!rdstls_send(rdstls->transport, s, rdstls))
|
||||
goto fail;
|
||||
|
||||
rc = TRUE;
|
||||
fail:
|
||||
Stream_Free(s, TRUE);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static BOOL rdstls_recv_capabilities(rdpRdstls* rdstls)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
int status;
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(rdstls);
|
||||
|
||||
if (rdstls_get_state(rdstls) != RDSTLS_STATE_CAPABILITIES)
|
||||
goto fail;
|
||||
|
||||
s = Stream_New(NULL, 512);
|
||||
if (!s)
|
||||
goto fail;
|
||||
|
||||
status = transport_read_pdu(rdstls->transport, s);
|
||||
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
|
||||
status = rdstls_recv(rdstls->transport, s, rdstls);
|
||||
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
|
||||
rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_REQ);
|
||||
fail:
|
||||
Stream_Free(s, TRUE);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static BOOL rdstls_send_authentication_request(rdpRdstls* rdstls)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(rdstls);
|
||||
|
||||
if (rdstls_get_state(rdstls) != RDSTLS_STATE_AUTH_REQ)
|
||||
goto fail;
|
||||
|
||||
s = Stream_New(NULL, 4096);
|
||||
if (!s)
|
||||
goto fail;
|
||||
|
||||
if (!rdstls_send(rdstls->transport, s, rdstls))
|
||||
goto fail;
|
||||
|
||||
rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_RSP);
|
||||
fail:
|
||||
Stream_Free(s, TRUE);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static BOOL rdstls_recv_authentication_response(rdpRdstls* rdstls)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
int status;
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(rdstls);
|
||||
|
||||
if (rdstls_get_state(rdstls) != RDSTLS_STATE_AUTH_RSP)
|
||||
goto fail;
|
||||
|
||||
s = Stream_New(NULL, 512);
|
||||
if (!s)
|
||||
goto fail;
|
||||
|
||||
status = transport_read_pdu(rdstls->transport, s);
|
||||
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
|
||||
status = rdstls_recv(rdstls->transport, s, rdstls);
|
||||
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
|
||||
rc = TRUE;
|
||||
fail:
|
||||
Stream_Free(s, TRUE);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int rdstls_server_authenticate(rdpRdstls* rdstls)
|
||||
{
|
||||
rdstls_set_state(rdstls, RDSTLS_STATE_CAPABILITIES);
|
||||
|
||||
if (!rdstls_send_capabilities(rdstls))
|
||||
return -1;
|
||||
|
||||
if (!rdstls_recv_authentication_request(rdstls))
|
||||
return -1;
|
||||
|
||||
if (!rdstls_send_authentication_response(rdstls))
|
||||
return -1;
|
||||
|
||||
if (rdstls->resultCode != 0)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int rdstls_client_authenticate(rdpRdstls* rdstls)
|
||||
{
|
||||
rdstls_set_state(rdstls, RDSTLS_STATE_CAPABILITIES);
|
||||
|
||||
if (!rdstls_recv_capabilities(rdstls))
|
||||
return -1;
|
||||
|
||||
if (!rdstls_send_authentication_request(rdstls))
|
||||
return -1;
|
||||
|
||||
if (!rdstls_recv_authentication_response(rdstls))
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate using RDSTLS.
|
||||
* @param rdstls The RDSTLS instance to use
|
||||
*
|
||||
* @return 1 if authentication is successful
|
||||
*/
|
||||
|
||||
int rdstls_authenticate(rdpRdstls* rdstls)
|
||||
{
|
||||
WINPR_ASSERT(rdstls);
|
||||
|
||||
if (rdstls->server)
|
||||
return rdstls_server_authenticate(rdstls);
|
||||
else
|
||||
return rdstls_client_authenticate(rdstls);
|
||||
}
|
||||
50
libfreerdp/core/rdstls.h
Normal file
50
libfreerdp/core/rdstls.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* RDSTLS Security protocol
|
||||
*
|
||||
* Copyright 2023 Joan Torres <joan.torres@suse.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 FREERDP_LIB_CORE_RDSTLS_H
|
||||
#define FREERDP_LIB_CORE_RDSTLS_H
|
||||
|
||||
typedef struct rdp_rdstls rdpRdstls;
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
#define RDSTLS_VERSION_1 0x01
|
||||
|
||||
#define RDSTLS_TYPE_CAPABILITIES 0x01
|
||||
#define RDSTLS_TYPE_AUTHREQ 0x02
|
||||
#define RDSTLS_TYPE_AUTHRSP 0x04
|
||||
|
||||
#define RDSTLS_DATA_CAPABILITIES 0x01
|
||||
#define RDSTLS_DATA_PASSWORD_CREDS 0x01
|
||||
#define RDSTLS_DATA_AUTORECONNECT_COOKIE 0x02
|
||||
#define RDSTLS_DATA_RESULT_CODE 0x01
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RDSTLS_STATE_CAPABILITIES,
|
||||
RDSTLS_STATE_AUTH_REQ,
|
||||
RDSTLS_STATE_AUTH_RSP,
|
||||
} RDSTLS_STATE;
|
||||
|
||||
FREERDP_LOCAL int rdstls_authenticate(rdpRdstls* rdstls);
|
||||
|
||||
FREERDP_LOCAL rdpRdstls* rdstls_new(rdpContext* context, rdpTransport* transport);
|
||||
FREERDP_LOCAL void rdstls_free(rdpRdstls* rdstls);
|
||||
|
||||
#endif /* FREERDP_LIB_CORE_RDSTLS_H */
|
||||
@@ -388,6 +388,7 @@ rdpSettings* freerdp_settings_new(DWORD flags)
|
||||
!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, TRUE) ||
|
||||
!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, TRUE) ||
|
||||
!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, TRUE) ||
|
||||
!freerdp_settings_set_bool(settings, FreeRDP_RdstlsSecurity, FALSE) ||
|
||||
!freerdp_settings_set_bool(settings, FreeRDP_NegotiateSecurityLayer, TRUE) ||
|
||||
!freerdp_settings_set_bool(settings, FreeRDP_RestrictedAdminModeRequired, FALSE) ||
|
||||
!freerdp_settings_set_bool(settings, FreeRDP_MstscCookieMode, FALSE) ||
|
||||
|
||||
@@ -73,6 +73,7 @@ struct rdp_transport
|
||||
wStreamPool* ReceivePool;
|
||||
HANDLE connectedEvent;
|
||||
BOOL NlaMode;
|
||||
BOOL RdstlsMode;
|
||||
BOOL blocking;
|
||||
BOOL GatewayEnabled;
|
||||
CRITICAL_SECTION ReadLock;
|
||||
@@ -367,6 +368,37 @@ BOOL transport_connect_nla(rdpTransport* transport)
|
||||
return rdp_client_transition_to_state(rdp, CONNECTION_STATE_NLA);
|
||||
}
|
||||
|
||||
BOOL transport_connect_rdstls(rdpTransport* transport)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
rdpRdstls* rdstls = NULL;
|
||||
rdpContext* context = NULL;
|
||||
|
||||
WINPR_ASSERT(transport);
|
||||
|
||||
context = transport_get_context(transport);
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (!transport_connect_tls(transport))
|
||||
goto fail;
|
||||
|
||||
rdstls = rdstls_new(context, transport);
|
||||
transport_set_rdstls_mode(transport, TRUE);
|
||||
|
||||
if (rdstls_authenticate(rdstls) < 0)
|
||||
{
|
||||
WLog_Print(transport->log, WLOG_ERROR, "RDSTLS authentication failed");
|
||||
freerdp_set_last_error_if_not(context, FREERDP_ERROR_AUTHENTICATION_FAILED);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
transport_set_rdstls_mode(transport, FALSE);
|
||||
rc = TRUE;
|
||||
fail:
|
||||
rdstls_free(rdstls);
|
||||
return rc;
|
||||
}
|
||||
|
||||
BOOL transport_connect(rdpTransport* transport, const char* hostname, UINT16 port, DWORD timeout)
|
||||
{
|
||||
int sockfd;
|
||||
@@ -542,6 +574,39 @@ BOOL transport_accept_nla(rdpTransport* transport)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL transport_accept_rdstls(rdpTransport* transport)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
rdpRdstls* rdstls = NULL;
|
||||
rdpContext* context = NULL;
|
||||
|
||||
WINPR_ASSERT(transport);
|
||||
|
||||
context = transport_get_context(transport);
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (!IFCALLRESULT(FALSE, transport->io.TLSAccept, transport))
|
||||
goto fail;
|
||||
|
||||
rdstls = rdstls_new(context, transport);
|
||||
transport_set_rdstls_mode(transport, TRUE);
|
||||
|
||||
if (rdstls_authenticate(rdstls) < 0)
|
||||
{
|
||||
WLog_Print(transport->log, WLOG_ERROR, "client authentication failure");
|
||||
freerdp_tls_set_alert_code(transport->tls, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_DESCRIPTION_ACCESS_DENIED);
|
||||
freerdp_tls_send_alert(transport->tls);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
transport_set_rdstls_mode(transport, FALSE);
|
||||
rc = TRUE;
|
||||
fail:
|
||||
rdstls_free(rdstls);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define WLog_ERR_BIO(transport, biofunc, bio) \
|
||||
transport_bio_error_log(transport, biofunc, bio, __FILE__, __FUNCTION__, __LINE__)
|
||||
|
||||
@@ -773,6 +838,82 @@ SSIZE_T transport_parse_pdu(rdpTransport* transport, wStream* s, BOOL* incomplet
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (transport->RdstlsMode)
|
||||
{
|
||||
UINT16 version = (header[1] << 8) | header[0];
|
||||
UINT16 pduType;
|
||||
UINT16 dataType;
|
||||
|
||||
if (version != RDSTLS_VERSION_1)
|
||||
{
|
||||
WLog_Print(transport->log, WLOG_ERROR, "invalid RDSTLS version");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (position < 4)
|
||||
return 0;
|
||||
|
||||
pduType = (header[3] << 8) | header[2];
|
||||
switch (pduType)
|
||||
{
|
||||
case RDSTLS_TYPE_CAPABILITIES:
|
||||
pduLength = 8;
|
||||
break;
|
||||
case RDSTLS_TYPE_AUTHREQ:
|
||||
if (position < 6)
|
||||
return 0;
|
||||
|
||||
dataType = (header[5] << 8 | header[4]);
|
||||
if (dataType == RDSTLS_DATA_PASSWORD_CREDS)
|
||||
{
|
||||
if (position < 8)
|
||||
return 0;
|
||||
|
||||
UINT16 redirGuidLength = (header[7] << 8 | header[6]);
|
||||
size_t usernamePos = 8 + redirGuidLength;
|
||||
|
||||
if (position < usernamePos + 2)
|
||||
return 0;
|
||||
|
||||
UINT16 usernameLength = (header[usernamePos + 1] << 8) | header[usernamePos];
|
||||
size_t domainPos = 8 + redirGuidLength + 2 + usernameLength;
|
||||
|
||||
if (position < domainPos + 2)
|
||||
return 0;
|
||||
|
||||
UINT16 domainLength = (header[domainPos + 1] << 8) | header[domainPos];
|
||||
size_t passwordPos =
|
||||
8 + redirGuidLength + 2 + usernameLength + 2 + domainLength;
|
||||
|
||||
if (position < passwordPos + 2)
|
||||
return 0;
|
||||
|
||||
UINT16 passwordLength = (header[passwordPos + 1] << 8) | header[passwordPos];
|
||||
|
||||
pduLength = 8 + redirGuidLength + 2 + usernameLength + 2 + domainLength + 2 +
|
||||
passwordLength;
|
||||
}
|
||||
else if (dataType == RDSTLS_DATA_AUTORECONNECT_COOKIE)
|
||||
{
|
||||
if (position < 12)
|
||||
return 0;
|
||||
|
||||
pduLength = 12 + ((header[11] << 8) | header[10]);
|
||||
}
|
||||
else
|
||||
{
|
||||
WLog_Print(transport->log, WLOG_ERROR, "invalid RDSLTS dataType");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case RDSTLS_TYPE_AUTHRSP:
|
||||
pduLength = 10;
|
||||
break;
|
||||
default:
|
||||
WLog_Print(transport->log, WLOG_ERROR, "invalid RDSTLS PDU type");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (header[0] == 0x03)
|
||||
@@ -1218,6 +1359,12 @@ void transport_set_nla_mode(rdpTransport* transport, BOOL NlaMode)
|
||||
transport->NlaMode = NlaMode;
|
||||
}
|
||||
|
||||
void transport_set_rdstls_mode(rdpTransport* transport, BOOL RdstlsMode)
|
||||
{
|
||||
WINPR_ASSERT(transport);
|
||||
transport->RdstlsMode = RdstlsMode;
|
||||
}
|
||||
|
||||
BOOL transport_disconnect(rdpTransport* transport)
|
||||
{
|
||||
if (!transport)
|
||||
|
||||
@@ -31,6 +31,7 @@ typedef enum
|
||||
|
||||
#include "tcp.h"
|
||||
#include "nla.h"
|
||||
#include "rdstls.h"
|
||||
|
||||
#include "gateway/tsg.h"
|
||||
#include "gateway/rdg.h"
|
||||
@@ -61,9 +62,11 @@ FREERDP_LOCAL BOOL transport_disconnect(rdpTransport* transport);
|
||||
FREERDP_LOCAL BOOL transport_connect_rdp(rdpTransport* transport);
|
||||
FREERDP_LOCAL BOOL transport_connect_tls(rdpTransport* transport);
|
||||
FREERDP_LOCAL BOOL transport_connect_nla(rdpTransport* transport);
|
||||
FREERDP_LOCAL BOOL transport_connect_rdstls(rdpTransport* transport);
|
||||
FREERDP_LOCAL BOOL transport_accept_rdp(rdpTransport* transport);
|
||||
FREERDP_LOCAL BOOL transport_accept_tls(rdpTransport* transport);
|
||||
FREERDP_LOCAL BOOL transport_accept_nla(rdpTransport* transport);
|
||||
FREERDP_LOCAL BOOL transport_accept_rdstls(rdpTransport* transport);
|
||||
|
||||
FREERDP_LOCAL int transport_read_pdu(rdpTransport* transport, wStream* s);
|
||||
FREERDP_LOCAL int transport_write(rdpTransport* transport, wStream* s);
|
||||
@@ -80,6 +83,7 @@ FREERDP_LOCAL HANDLE transport_get_front_bio(rdpTransport* transport);
|
||||
FREERDP_LOCAL BOOL transport_set_blocking_mode(rdpTransport* transport, BOOL blocking);
|
||||
FREERDP_LOCAL void transport_set_gateway_enabled(rdpTransport* transport, BOOL GatewayEnabled);
|
||||
FREERDP_LOCAL void transport_set_nla_mode(rdpTransport* transport, BOOL NlaMode);
|
||||
FREERDP_LOCAL void transport_set_rdstls_mode(rdpTransport* transport, BOOL RdstlsMode);
|
||||
FREERDP_LOCAL BOOL transport_is_write_blocked(rdpTransport* transport);
|
||||
FREERDP_LOCAL int transport_drain_output_buffer(rdpTransport* transport);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user