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:
Joan Torres
2023-03-07 11:58:28 +01:00
committed by akallabeth
parent 49f44303b1
commit 7c24da917e
13 changed files with 1133 additions and 22 deletions

View File

@@ -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 */

View File

@@ -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;

View File

@@ -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" },

View File

@@ -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})

View File

@@ -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);

View File

@@ -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

View File

@@ -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,

View File

@@ -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
View 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
View 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 */

View File

@@ -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) ||

View File

@@ -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)

View File

@@ -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);