FreeRDP/libfreerdp/core/rdp.c

2414 lines
58 KiB
C
Raw Normal View History

/**
2012-10-09 07:02:04 +04:00
* FreeRDP: A Remote Desktop Protocol Implementation
* RDP Core
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2014 DI (FH) Martin Haimberger <martin.haimberger@thincast.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.
*/
2022-02-16 13:20:38 +03:00
#include <freerdp/config.h>
#include <winpr/crt.h>
2022-06-23 10:59:24 +03:00
#include <winpr/string.h>
#include <winpr/synch.h>
#include <winpr/assert.h>
#include "rdp.h"
#include "state.h"
#include "info.h"
#include "utils.h"
#include "mcs.h"
#include "redirection.h"
#include <freerdp/codec/bulk.h>
#include <freerdp/crypto/per.h>
#include <freerdp/log.h>
#define TAG FREERDP_TAG("core.rdp")
2020-11-19 15:42:28 +03:00
static const char* DATA_PDU_TYPE_STRINGS[80] = {
2019-11-06 17:24:51 +03:00
"?",
"?", /* 0x00 - 0x01 */
"Update", /* 0x02 */
2019-11-06 17:24:51 +03:00
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?", /* 0x03 - 0x0A */
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?", /* 0x0B - 0x13 */
"Control", /* 0x14 */
2019-11-06 17:24:51 +03:00
"?",
"?",
"?",
"?",
"?",
"?", /* 0x15 - 0x1A */
"Pointer", /* 0x1B */
2019-11-06 17:24:51 +03:00
"Input", /* 0x1C */
"?",
"?", /* 0x1D - 0x1E */
"Synchronize", /* 0x1F */
"?", /* 0x20 */
"Refresh Rect", /* 0x21 */
"Play Sound", /* 0x22 */
"Suppress Output", /* 0x23 */
"Shutdown Request", /* 0x24 */
"Shutdown Denied", /* 0x25 */
"Save Session Info", /* 0x26 */
"Font List", /* 0x27 */
"Font Map", /* 0x28 */
"Set Keyboard Indicators", /* 0x29 */
"?", /* 0x2A */
"Bitmap Cache Persistent List", /* 0x2B */
2019-11-06 17:24:51 +03:00
"Bitmap Cache Error", /* 0x2C */
"Set Keyboard IME Status", /* 0x2D */
"Offscreen Cache Error", /* 0x2E */
"Set Error Info", /* 0x2F */
"Draw Nine Grid Error", /* 0x30 */
"Draw GDI+ Error", /* 0x31 */
"ARC Status", /* 0x32 */
"?",
"?",
"?", /* 0x33 - 0x35 */
"Status Info", /* 0x36 */
"Monitor Layout", /* 0x37 */
2019-11-06 17:24:51 +03:00
"FrameAcknowledge",
"?",
"?", /* 0x38 - 0x40 */
"?",
"?",
"?",
"?",
"?",
"?" /* 0x41 - 0x46 */
};
2020-11-19 15:42:28 +03:00
const char* data_pdu_type_to_string(UINT8 type)
{
if (type >= ARRAYSIZE(DATA_PDU_TYPE_STRINGS))
2020-11-19 15:42:28 +03:00
return "???";
return DATA_PDU_TYPE_STRINGS[type];
}
static BOOL rdp_read_flow_control_pdu(wStream* s, UINT16* type, UINT16* channel_id);
static BOOL rdp_write_share_control_header(wStream* s, UINT16 length, UINT16 type,
2019-11-20 13:30:14 +03:00
UINT16 channel_id);
static BOOL rdp_write_share_data_header(wStream* s, UINT16 length, BYTE type, UINT32 share_id);
2019-11-20 13:30:14 +03:00
/**
* Read RDP Security Header.\n
* @msdn{cc240579}
* @param s stream
* @param flags security flags
*/
BOOL rdp_read_security_header(wStream* s, UINT16* flags, UINT16* length)
{
WINPR_ASSERT(s);
WINPR_ASSERT(flags);
/* Basic Security Header */
if ((length && (*length < 4)))
{
WLog_WARN(TAG, "invalid security header length, have %" PRIu16 ", must be >= 4", *length);
return FALSE;
}
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
2013-01-12 03:43:16 +04:00
return FALSE;
2013-05-09 00:09:16 +04:00
Stream_Read_UINT16(s, *flags); /* flags */
2019-11-06 17:24:51 +03:00
Stream_Seek(s, 2); /* flagsHi (unused) */
if (length)
*length -= 4;
2013-01-12 03:43:16 +04:00
return TRUE;
}
/**
* Write RDP Security Header.\n
* @msdn{cc240579}
* @param s stream
* @param flags security flags
*/
BOOL rdp_write_security_header(wStream* s, UINT16 flags)
{
WINPR_ASSERT(s);
if (Stream_GetRemainingCapacity(s) < 4)
return FALSE;
/* Basic Security Header */
2013-05-09 00:09:16 +04:00
Stream_Write_UINT16(s, flags); /* flags */
2019-11-06 17:24:51 +03:00
Stream_Write_UINT16(s, 0); /* flagsHi (unused) */
return TRUE;
}
BOOL rdp_read_share_control_header(wStream* s, UINT16* tpktLength, UINT16* remainingLength,
UINT16* type, UINT16* channel_id)
{
UINT16 len;
UINT16 tmp;
WINPR_ASSERT(s);
WINPR_ASSERT(type);
WINPR_ASSERT(channel_id);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
2013-01-12 03:43:16 +04:00
return FALSE;
/* Share Control Header */
Stream_Read_UINT16(s, len); /* totalLength */
/* If length is 0x8000 then we actually got a flow control PDU that we should ignore
http://msdn.microsoft.com/en-us/library/cc240576.aspx */
if (len == 0x8000)
{
if (!rdp_read_flow_control_pdu(s, type, channel_id))
return FALSE;
2014-11-17 02:05:12 +03:00
*channel_id = 0;
if (tpktLength)
*tpktLength = 8; /* Flow control PDU is 8 bytes */
if (remainingLength)
*remainingLength = 0;
return TRUE;
}
if (len < 4U)
{
WLog_ERR(TAG, "Invalid share control header, length is %" PRIu16 ", must be >4", len);
return FALSE;
}
2012-02-07 01:31:41 +04:00
if (!Stream_CheckAndLogRequiredLength(TAG, s, len - 2))
return FALSE;
if (tpktLength)
*tpktLength = len;
Stream_Read_UINT16(s, tmp); /* pduType */
*type = tmp & 0x0F; /* type is in the 4 least significant bits */
if (len > 5)
{
2013-05-09 00:09:16 +04:00
Stream_Read_UINT16(s, *channel_id); /* pduSource */
if (remainingLength)
*remainingLength = len - 6;
}
else
{
*channel_id = 0; /* Windows XP can send such short DEACTIVATE_ALL PDUs. */
if (remainingLength)
*remainingLength = len - 4;
}
return TRUE;
}
BOOL rdp_write_share_control_header(wStream* s, UINT16 length, UINT16 type, UINT16 channel_id)
{
WINPR_ASSERT(s);
if (length < RDP_PACKET_HEADER_MAX_LENGTH)
return FALSE;
if (Stream_GetRemainingCapacity(s) < 6)
return FALSE;
length -= RDP_PACKET_HEADER_MAX_LENGTH;
/* Share Control Header */
2019-11-06 17:24:51 +03:00
Stream_Write_UINT16(s, length); /* totalLength */
2013-05-09 00:09:16 +04:00
Stream_Write_UINT16(s, type | 0x10); /* pduType */
2019-11-06 17:24:51 +03:00
Stream_Write_UINT16(s, channel_id); /* pduSource */
return TRUE;
}
BOOL rdp_read_share_data_header(wStream* s, UINT16* length, BYTE* type, UINT32* shareId,
BYTE* compressedType, UINT16* compressedLength)
{
WINPR_ASSERT(s);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
return FALSE;
2011-08-21 18:52:37 +04:00
/* Share Data Header */
2019-11-06 17:24:51 +03:00
Stream_Read_UINT32(s, *shareId); /* shareId (4 bytes) */
Stream_Seek_UINT8(s); /* pad1 (1 byte) */
Stream_Seek_UINT8(s); /* streamId (1 byte) */
Stream_Read_UINT16(s, *length); /* uncompressedLength (2 bytes) */
Stream_Read_UINT8(s, *type); /* pduType2, Data PDU Type (1 byte) */
Stream_Read_UINT8(s, *compressedType); /* compressedType (1 byte) */
Stream_Read_UINT16(s, *compressedLength); /* compressedLength (2 bytes) */
return TRUE;
}
BOOL rdp_write_share_data_header(wStream* s, UINT16 length, BYTE type, UINT32 share_id)
{
const size_t headerLen = RDP_PACKET_HEADER_MAX_LENGTH + RDP_SHARE_CONTROL_HEADER_LENGTH +
RDP_SHARE_DATA_HEADER_LENGTH;
WINPR_ASSERT(s);
if (length < headerLen)
return FALSE;
length -= headerLen;
if (Stream_GetRemainingCapacity(s) < 12)
return FALSE;
/* Share Data Header */
2019-11-06 17:24:51 +03:00
Stream_Write_UINT32(s, share_id); /* shareId (4 bytes) */
Stream_Write_UINT8(s, 0); /* pad1 (1 byte) */
2013-05-09 00:09:16 +04:00
Stream_Write_UINT8(s, STREAM_LOW); /* streamId (1 byte) */
2019-11-06 17:24:51 +03:00
Stream_Write_UINT16(s, length); /* uncompressedLength (2 bytes) */
Stream_Write_UINT8(s, type); /* pduType2, Data PDU Type (1 byte) */
Stream_Write_UINT8(s, 0); /* compressedType (1 byte) */
Stream_Write_UINT16(s, 0); /* compressedLength (2 bytes) */
return TRUE;
}
static BOOL rdp_security_stream_init(rdpRdp* rdp, wStream* s, BOOL sec_header)
2011-09-13 10:40:27 +04:00
{
WINPR_ASSERT(rdp);
WINPR_ASSERT(s);
2011-09-13 10:40:27 +04:00
if (rdp->do_crypt)
{
if (!Stream_SafeSeek(s, 12))
return FALSE;
2012-12-12 08:34:51 +04:00
if (rdp->settings->EncryptionMethods == ENCRYPTION_METHOD_FIPS)
{
if (!Stream_SafeSeek(s, 4))
return FALSE;
}
2012-12-12 08:34:51 +04:00
2011-09-13 10:40:27 +04:00
rdp->sec_flags |= SEC_ENCRYPT;
2012-12-12 08:34:51 +04:00
if (rdp->do_secure_checksum)
rdp->sec_flags |= SEC_SECURE_CHECKSUM;
2011-09-13 10:40:27 +04:00
}
else if (rdp->sec_flags != 0 || sec_header)
2011-09-13 10:40:27 +04:00
{
if (!Stream_SafeSeek(s, 4))
return FALSE;
2011-09-13 10:40:27 +04:00
}
2012-12-12 08:34:51 +04:00
return TRUE;
}
wStream* rdp_send_stream_init(rdpRdp* rdp)
{
wStream* s;
WINPR_ASSERT(rdp);
WINPR_ASSERT(rdp->transport);
s = transport_send_stream_init(rdp->transport, 4096);
if (!s)
return NULL;
if (!Stream_SafeSeek(s, RDP_PACKET_HEADER_MAX_LENGTH))
goto fail;
if (!rdp_security_stream_init(rdp, s, FALSE))
goto fail;
return s;
fail:
Stream_Release(s);
return NULL;
}
wStream* rdp_send_stream_pdu_init(rdpRdp* rdp)
{
wStream* s = rdp_send_stream_init(rdp);
if (!s)
return NULL;
if (!Stream_SafeSeek(s, RDP_SHARE_CONTROL_HEADER_LENGTH))
goto fail;
return s;
fail:
Stream_Release(s);
return NULL;
}
wStream* rdp_data_pdu_init(rdpRdp* rdp)
{
wStream* s = rdp_send_stream_pdu_init(rdp);
if (!s)
return NULL;
if (!Stream_SafeSeek(s, RDP_SHARE_DATA_HEADER_LENGTH))
goto fail;
return s;
fail:
Stream_Release(s);
return NULL;
}
BOOL rdp_set_error_info(rdpRdp* rdp, UINT32 errorInfo)
{
WINPR_ASSERT(rdp);
rdp->errorInfo = errorInfo;
if (rdp->errorInfo != ERRINFO_SUCCESS)
{
rdpContext* context = rdp->context;
WINPR_ASSERT(context);
rdp_print_errinfo(rdp->errorInfo);
if (context)
{
freerdp_set_last_error_log(context, MAKE_FREERDP_ERROR(ERRINFO, errorInfo));
if (context->pubSub)
{
2022-07-06 10:36:53 +03:00
ErrorInfoEventArgs e = { 0 };
EventArgsInit(&e, "freerdp");
e.code = rdp->errorInfo;
PubSub_OnErrorInfo(context->pubSub, context, &e);
}
}
else
WLog_ERR(TAG, "%s missing context=%p", __FUNCTION__, context);
}
else
{
freerdp_set_last_error_log(rdp->context, FREERDP_ERROR_SUCCESS);
}
return TRUE;
}
wStream* rdp_message_channel_pdu_init(rdpRdp* rdp)
{
wStream* s;
WINPR_ASSERT(rdp);
s = transport_send_stream_init(rdp->transport, 4096);
if (!s)
return NULL;
if (!Stream_SafeSeek(s, RDP_PACKET_HEADER_MAX_LENGTH))
goto fail;
if (!rdp_security_stream_init(rdp, s, TRUE))
goto fail;
return s;
fail:
Stream_Release(s);
return NULL;
}
/**
* Read an RDP packet header.\n
* @param rdp rdp module
* @param s stream
* @param length RDP packet length
* @param channel_id channel id
*/
BOOL rdp_read_header(rdpRdp* rdp, wStream* s, UINT16* length, UINT16* channelId)
{
BYTE li;
BYTE byte;
BYTE code;
BYTE choice;
UINT16 initiator;
DomainMCSPDU MCSPDU;
DomainMCSPDU domainMCSPDU;
2022-03-29 12:12:52 +03:00
WINPR_ASSERT(rdp);
WINPR_ASSERT(rdp->settings);
WINPR_ASSERT(s);
2019-11-06 17:24:51 +03:00
MCSPDU = (rdp->settings->ServerMode) ? DomainMCSPDU_SendDataRequest
: DomainMCSPDU_SendDataIndication;
*channelId = 0; /* Initialize in case of early abort */
if (!tpkt_read_header(s, length))
return FALSE;
if (!tpdu_read_header(s, &code, &li, *length))
return FALSE;
if (code != X224_TPDU_DATA)
{
if (code == X224_TPDU_DISCONNECT_REQUEST)
{
utils_abort_connect(rdp);
return TRUE;
}
WLog_WARN(TAG, "Unexpected X224 TPDU type %s [%08" PRIx32 "] instead of %s",
tpdu_type_to_string(code), code, tpdu_type_to_string(X224_TPDU_DATA));
return FALSE;
}
if (!per_read_choice(s, &choice))
return FALSE;
domainMCSPDU = (DomainMCSPDU)(choice >> 2);
if (domainMCSPDU != MCSPDU)
{
if (domainMCSPDU != DomainMCSPDU_DisconnectProviderUltimatum)
{
WLog_WARN(TAG, "Received %s instead of %s", mcs_domain_pdu_string(domainMCSPDU),
mcs_domain_pdu_string(MCSPDU));
return FALSE;
}
}
2012-02-07 01:31:41 +04:00
MCSPDU = domainMCSPDU;
2019-02-07 16:22:28 +03:00
if (*length < 8U)
{
WLog_WARN(TAG, "TPDU invalid length, got %" PRIu16 ", expected at least 8", *length);
return FALSE;
}
if (!Stream_CheckAndLogRequiredLength(TAG, s, *length - 8))
return FALSE;
if (MCSPDU == DomainMCSPDU_DisconnectProviderUltimatum)
{
int reason = 0;
2022-07-06 10:36:53 +03:00
TerminateEventArgs e = { 0 };
rdpContext* context;
if (!mcs_recv_disconnect_provider_ultimatum(rdp->mcs, s, &reason))
return FALSE;
2022-03-29 12:12:52 +03:00
context = rdp->context;
context->disconnectUltimatum = reason;
if (rdp->errorInfo == ERRINFO_SUCCESS)
{
/**
* Some servers like Windows Server 2008 R2 do not send the error info pdu
* when the user logs off like they should. Map DisconnectProviderUltimatum
* to a ERRINFO_LOGOFF_BY_USER when the errinfo code is ERRINFO_SUCCESS.
*/
if (reason == Disconnect_Ultimatum_provider_initiated)
rdp_set_error_info(rdp, ERRINFO_RPC_INITIATED_DISCONNECT);
else if (reason == Disconnect_Ultimatum_user_requested)
rdp_set_error_info(rdp, ERRINFO_LOGOFF_BY_USER);
else
rdp_set_error_info(rdp, ERRINFO_RPC_INITIATED_DISCONNECT);
}
WLog_DBG(TAG, "DisconnectProviderUltimatum: reason: %d", reason);
utils_abort_connect(rdp);
2013-06-19 00:55:23 +04:00
EventArgsInit(&e, "freerdp");
e.code = 0;
2022-07-06 10:36:53 +03:00
PubSub_OnTerminate(rdp->pubSub, context, &e);
return TRUE;
}
if (!Stream_CheckAndLogRequiredLength(TAG, s, 5))
2013-01-12 03:43:16 +04:00
return FALSE;
if (!per_read_integer16(s, &initiator, MCS_BASE_CHANNEL_ID)) /* initiator (UserId) */
return FALSE;
if (!per_read_integer16(s, channelId, 0)) /* channelId */
return FALSE;
Stream_Read_UINT8(s, byte); /* dataPriority + Segmentation (0x70) */
if (!per_read_length(s, length)) /* userData (OCTET_STRING) */
2013-01-12 03:43:16 +04:00
return FALSE;
if (!Stream_CheckAndLogRequiredLength(TAG, s, *length))
return FALSE;
return TRUE;
}
/**
* Write an RDP packet header.\n
* @param rdp rdp module
* @param s stream
* @param length RDP packet length
* @param channel_id channel id
*/
BOOL rdp_write_header(rdpRdp* rdp, wStream* s, UINT16 length, UINT16 channelId)
{
DomainMCSPDU MCSPDU;
WINPR_ASSERT(rdp);
WINPR_ASSERT(rdp->settings);
WINPR_ASSERT(s);
WINPR_ASSERT(length >= RDP_PACKET_HEADER_MAX_LENGTH);
2019-11-06 17:24:51 +03:00
MCSPDU = (rdp->settings->ServerMode) ? DomainMCSPDU_SendDataIndication
: DomainMCSPDU_SendDataRequest;
2019-11-06 17:24:51 +03:00
if ((rdp->sec_flags & SEC_ENCRYPT) &&
(rdp->settings->EncryptionMethods == ENCRYPTION_METHOD_FIPS))
2011-09-27 09:30:58 +04:00
{
const UINT16 body_length = length - RDP_PACKET_HEADER_MAX_LENGTH;
const UINT16 pad = 8 - (body_length % 8);
2012-12-12 08:34:51 +04:00
2011-09-16 03:54:03 +04:00
if (pad != 8)
length += pad;
}
if (!mcs_write_domain_mcspdu_header(s, MCSPDU, length, 0))
return FALSE;
if (!per_write_integer16(s, rdp->mcs->userId, MCS_BASE_CHANNEL_ID)) /* initiator */
return FALSE;
if (!per_write_integer16(s, channelId, 0)) /* channelId */
return FALSE;
if (!Stream_EnsureRemainingCapacity(s, 3))
return FALSE;
Stream_Write_UINT8(s, 0x70); /* dataPriority + segmentation */
/*
* We always encode length in two bytes, even though we could use
* only one byte if length <= 0x7F. It is just easier that way,
* because we can leave room for fixed-length header, store all
* the data first and then store the header.
*/
length = (length - RDP_PACKET_HEADER_MAX_LENGTH) | 0x8000;
2013-05-09 00:09:16 +04:00
Stream_Write_UINT16_BE(s, length); /* userData (OCTET_STRING) */
return TRUE;
}
static BOOL rdp_security_stream_out(rdpRdp* rdp, wStream* s, int length, UINT32 sec_flags,
UINT32* pad)
2011-09-13 10:40:27 +04:00
{
BYTE* data;
BOOL status;
sec_flags |= rdp->sec_flags;
*pad = 0;
2011-09-13 10:40:27 +04:00
if (sec_flags != 0)
{
rdp_write_security_header(s, sec_flags);
2011-09-13 10:40:27 +04:00
if (sec_flags & SEC_ENCRYPT)
{
if (rdp->settings->EncryptionMethods == ENCRYPTION_METHOD_FIPS)
2011-09-16 03:54:03 +04:00
{
data = Stream_Pointer(s) + 12;
length = length - (data - Stream_Buffer(s));
2013-05-09 00:09:16 +04:00
Stream_Write_UINT16(s, 0x10); /* length */
2019-11-06 17:24:51 +03:00
Stream_Write_UINT8(s, 0x1); /* TSFIPS_VERSION 1*/
2011-09-16 03:54:03 +04:00
/* handle padding */
*pad = 8 - (length % 8);
if (*pad == 8)
2015-04-13 10:28:29 +03:00
*pad = 0;
if (*pad)
memset(data + length, 0, *pad);
Stream_Write_UINT8(s, *pad);
if (!security_hmac_signature(data, length, Stream_Pointer(s), rdp))
return FALSE;
Stream_Seek(s, 8);
security_fips_encrypt(data, length + *pad, rdp);
2011-09-16 03:54:03 +04:00
}
else
{
data = Stream_Pointer(s) + 8;
length = length - (data - Stream_Buffer(s));
if (sec_flags & SEC_SECURE_CHECKSUM)
2019-11-06 17:24:51 +03:00
status =
security_salted_mac_signature(rdp, data, length, TRUE, Stream_Pointer(s));
else
status = security_mac_signature(rdp, data, length, Stream_Pointer(s));
if (!status)
return FALSE;
Stream_Seek(s, 8);
2015-04-07 22:06:53 +03:00
if (!security_encrypt(Stream_Pointer(s), length, rdp))
return FALSE;
2011-09-16 03:54:03 +04:00
}
2011-09-13 10:40:27 +04:00
}
2011-09-13 10:40:27 +04:00
rdp->sec_flags = 0;
}
return TRUE;
2011-09-13 10:40:27 +04:00
}
static UINT32 rdp_get_sec_bytes(rdpRdp* rdp, UINT16 sec_flags)
2011-09-13 10:40:27 +04:00
{
2012-10-09 11:26:39 +04:00
UINT32 sec_bytes;
2011-09-13 10:40:27 +04:00
if (rdp->sec_flags & SEC_ENCRYPT)
{
2011-09-13 10:40:27 +04:00
sec_bytes = 12;
if (rdp->settings->EncryptionMethods == ENCRYPTION_METHOD_FIPS)
2011-09-16 03:54:03 +04:00
sec_bytes += 4;
}
else if (rdp->sec_flags != 0 || sec_flags != 0)
{
2011-09-13 10:40:27 +04:00
sec_bytes = 4;
}
2011-09-13 10:40:27 +04:00
else
{
2011-09-13 10:40:27 +04:00
sec_bytes = 0;
}
2011-09-13 10:40:27 +04:00
return sec_bytes;
}
/**
* Send an RDP packet.
* @param rdp RDP module
* @param s stream
* @param channel_id channel id
*/
BOOL rdp_send(rdpRdp* rdp, wStream* s, UINT16 channel_id)
{
BOOL rc = FALSE;
UINT32 pad;
UINT16 length;
if (!s)
return FALSE;
if (!rdp)
goto fail;
length = Stream_GetPosition(s);
Stream_SetPosition(s, 0);
rdp_write_header(rdp, s, length, channel_id);
if (!rdp_security_stream_out(rdp, s, length, 0, &pad))
goto fail;
length += pad;
Stream_SetPosition(s, length);
Stream_SealLength(s);
if (transport_write(rdp->transport, s) < 0)
goto fail;
rc = TRUE;
fail:
Stream_Release(s);
return rc;
}
BOOL rdp_send_pdu(rdpRdp* rdp, wStream* s, UINT16 type, UINT16 channel_id)
{
UINT16 length;
2012-10-09 11:26:39 +04:00
UINT32 sec_bytes;
size_t sec_hold;
UINT32 pad;
if (!rdp || !s)
return FALSE;
length = Stream_GetPosition(s);
Stream_SetPosition(s, 0);
rdp_write_header(rdp, s, length, MCS_GLOBAL_CHANNEL_ID);
sec_bytes = rdp_get_sec_bytes(rdp, 0);
sec_hold = Stream_GetPosition(s);
Stream_Seek(s, sec_bytes);
if (!rdp_write_share_control_header(s, length - sec_bytes, type, channel_id))
return FALSE;
Stream_SetPosition(s, sec_hold);
if (!rdp_security_stream_out(rdp, s, length, 0, &pad))
return FALSE;
length += pad;
Stream_SetPosition(s, length);
Stream_SealLength(s);
if (transport_write(rdp->transport, s) < 0)
return FALSE;
return TRUE;
}
BOOL rdp_send_data_pdu(rdpRdp* rdp, wStream* s, BYTE type, UINT16 channel_id)
{
BOOL rc = FALSE;
size_t length;
2012-10-09 11:26:39 +04:00
UINT32 sec_bytes;
size_t sec_hold;
UINT32 pad;
if (!s)
return FALSE;
if (!rdp)
goto fail;
length = Stream_GetPosition(s);
Stream_SetPosition(s, 0);
rdp_write_header(rdp, s, length, MCS_GLOBAL_CHANNEL_ID);
sec_bytes = rdp_get_sec_bytes(rdp, 0);
sec_hold = Stream_GetPosition(s);
Stream_Seek(s, sec_bytes);
if (!rdp_write_share_control_header(s, length - sec_bytes, PDU_TYPE_DATA, channel_id))
goto fail;
if (!rdp_write_share_data_header(s, length - sec_bytes, type, rdp->settings->ShareId))
goto fail;
Stream_SetPosition(s, sec_hold);
if (!rdp_security_stream_out(rdp, s, length, 0, &pad))
goto fail;
length += pad;
Stream_SetPosition(s, length);
Stream_SealLength(s);
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "%s: sending data (type=0x%x size=%" PRIuz " channelId=%" PRIu16 ")",
__FUNCTION__, type, Stream_Length(s), channel_id);
2019-10-23 00:35:18 +03:00
rdp->outPackets++;
if (transport_write(rdp->transport, s) < 0)
goto fail;
rc = TRUE;
fail:
Stream_Release(s);
return rc;
}
BOOL rdp_send_message_channel_pdu(rdpRdp* rdp, wStream* s, UINT16 sec_flags)
{
BOOL rc = FALSE;
UINT16 length;
UINT32 pad;
WINPR_ASSERT(rdp);
WINPR_ASSERT(s);
length = Stream_GetPosition(s);
Stream_SetPosition(s, 0);
rdp_write_header(rdp, s, length, rdp->mcs->messageChannelId);
if (!rdp_security_stream_out(rdp, s, length, sec_flags, &pad))
goto fail;
length += pad;
Stream_SetPosition(s, length);
Stream_SealLength(s);
if (transport_write(rdp->transport, s) < 0)
goto fail;
rc = TRUE;
fail:
Stream_Release(s);
return rc;
}
static BOOL rdp_recv_server_shutdown_denied_pdu(rdpRdp* rdp, wStream* s)
{
return TRUE;
}
static BOOL rdp_recv_server_set_keyboard_indicators_pdu(rdpRdp* rdp, wStream* s)
{
UINT16 unitId;
UINT16 ledFlags;
2022-03-29 12:12:52 +03:00
WINPR_ASSERT(rdp);
WINPR_ASSERT(s);
rdpContext* context = rdp->context;
WINPR_ASSERT(context);
WINPR_ASSERT(context->update);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return FALSE;
2019-11-06 17:24:51 +03:00
Stream_Read_UINT16(s, unitId); /* unitId (2 bytes) */
Stream_Read_UINT16(s, ledFlags); /* ledFlags (2 bytes) */
2022-03-29 12:12:52 +03:00
return IFCALLRESULT(TRUE, context->update->SetKeyboardIndicators, context, ledFlags);
}
static BOOL rdp_recv_server_set_keyboard_ime_status_pdu(rdpRdp* rdp, wStream* s)
{
UINT16 unitId;
UINT32 imeState;
UINT32 imeConvMode;
if (!rdp || !rdp->input)
return FALSE;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 10))
return FALSE;
2019-11-06 17:24:51 +03:00
Stream_Read_UINT16(s, unitId); /* unitId (2 bytes) */
Stream_Read_UINT32(s, imeState); /* imeState (4 bytes) */
Stream_Read_UINT32(s, imeConvMode); /* imeConvMode (4 bytes) */
IFCALL(rdp->update->SetKeyboardImeStatus, rdp->context, unitId, imeState, imeConvMode);
return TRUE;
}
static BOOL rdp_recv_set_error_info_data_pdu(rdpRdp* rdp, wStream* s)
2011-07-22 00:20:41 +04:00
{
UINT32 errorInfo;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
2013-01-12 03:43:16 +04:00
return FALSE;
Stream_Read_UINT32(s, errorInfo); /* errorInfo (4 bytes) */
2016-02-12 00:45:30 +03:00
return rdp_set_error_info(rdp, errorInfo);
2011-07-22 00:20:41 +04:00
}
static BOOL rdp_recv_server_auto_reconnect_status_pdu(rdpRdp* rdp, wStream* s)
{
UINT32 arcStatus;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return FALSE;
Stream_Read_UINT32(s, arcStatus); /* arcStatus (4 bytes) */
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "AutoReconnectStatus: 0x%08" PRIX32 "", arcStatus);
return TRUE;
}
static BOOL rdp_recv_server_status_info_pdu(rdpRdp* rdp, wStream* s)
{
UINT32 statusCode;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return FALSE;
Stream_Read_UINT32(s, statusCode); /* statusCode (4 bytes) */
if (rdp->update->ServerStatusInfo)
return rdp->update->ServerStatusInfo(rdp->context, statusCode);
return TRUE;
}
static BOOL rdp_recv_monitor_layout_pdu(rdpRdp* rdp, wStream* s)
{
2014-02-11 07:12:13 +04:00
UINT32 index;
UINT32 monitorCount;
MONITOR_DEF* monitor;
MONITOR_DEF* monitorDefArray;
BOOL ret = TRUE;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return FALSE;
Stream_Read_UINT32(s, monitorCount); /* monitorCount (4 bytes) */
if (!Stream_CheckAndLogRequiredLength(TAG, s, 20ull * monitorCount))
return FALSE;
2019-11-06 17:24:51 +03:00
monitorDefArray = (MONITOR_DEF*)calloc(monitorCount, sizeof(MONITOR_DEF));
if (!monitorDefArray)
return FALSE;
for (monitor = monitorDefArray, index = 0; index < monitorCount; index++, monitor++)
{
2022-06-23 08:57:38 +03:00
Stream_Read_INT32(s, monitor->left); /* left (4 bytes) */
Stream_Read_INT32(s, monitor->top); /* top (4 bytes) */
Stream_Read_INT32(s, monitor->right); /* right (4 bytes) */
Stream_Read_INT32(s, monitor->bottom); /* bottom (4 bytes) */
Stream_Read_UINT32(s, monitor->flags); /* flags (4 bytes) */
}
IFCALLRET(rdp->update->RemoteMonitors, ret, rdp->context, monitorCount, monitorDefArray);
free(monitorDefArray);
return ret;
}
state_run_t rdp_recv_data_pdu(rdpRdp* rdp, wStream* s)
2011-07-22 00:20:41 +04:00
{
BYTE type;
wStream* cs;
UINT16 length;
UINT32 shareId;
BYTE compressedType;
UINT16 compressedLength;
2011-07-22 00:20:41 +04:00
2019-11-06 17:24:51 +03:00
if (!rdp_read_share_data_header(s, &length, &type, &shareId, &compressedType,
&compressedLength))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "rdp_read_share_data_header() failed");
return STATE_RUN_FAILED;
}
2011-07-22 00:20:41 +04:00
cs = s;
if (compressedType & PACKET_COMPRESSED)
{
UINT32 DstSize = 0;
2022-02-01 12:49:40 +03:00
const BYTE* pDstData = NULL;
UINT16 SrcSize = compressedLength - 18;
if ((compressedLength < 18) || (!Stream_CheckAndLogRequiredLength(TAG, s, SrcSize)))
{
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG, "bulk_decompress: not enough bytes for compressedLength %" PRIu16 "",
compressedLength);
return STATE_RUN_FAILED;
}
2019-11-06 17:24:51 +03:00
if (bulk_decompress(rdp->bulk, Stream_Pointer(s), SrcSize, &pDstData, &DstSize,
compressedType))
{
2021-09-06 12:01:36 +03:00
cs = transport_take_from_pool(rdp->transport, DstSize);
if (!cs)
{
2016-12-01 13:15:11 +03:00
WLog_ERR(TAG, "Couldn't take stream from pool");
return STATE_RUN_FAILED;
}
Stream_SetPosition(cs, 0);
Stream_Write(cs, pDstData, DstSize);
Stream_SealLength(cs);
Stream_SetPosition(cs, 0);
}
else
{
WLog_ERR(TAG, "bulk_decompress() failed");
return STATE_RUN_FAILED;
}
Stream_Seek(s, SrcSize);
}
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "recv %s Data PDU (0x%02" PRIX8 "), length: %" PRIu16 "",
2020-11-19 15:42:28 +03:00
data_pdu_type_to_string(type), type, length);
2011-07-22 00:20:41 +04:00
switch (type)
{
case DATA_PDU_TYPE_UPDATE:
if (!update_recv(rdp->update, cs))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "DATA_PDU_TYPE_UPDATE - update_recv() failed");
2015-05-06 17:54:23 +03:00
goto out_fail;
}
break;
case DATA_PDU_TYPE_CONTROL:
if (!rdp_recv_server_control_pdu(rdp, cs))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "DATA_PDU_TYPE_CONTROL - rdp_recv_server_control_pdu() failed");
2015-05-06 17:54:23 +03:00
goto out_fail;
}
break;
case DATA_PDU_TYPE_POINTER:
if (!update_recv_pointer(rdp->update, cs))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "DATA_PDU_TYPE_POINTER - update_recv_pointer() failed");
2015-05-06 17:54:23 +03:00
goto out_fail;
}
break;
case DATA_PDU_TYPE_SYNCHRONIZE:
if (!rdp_recv_server_synchronize_pdu(rdp, cs))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "DATA_PDU_TYPE_SYNCHRONIZE - rdp_recv_synchronize_pdu() failed");
2015-05-06 17:54:23 +03:00
goto out_fail;
}
break;
case DATA_PDU_TYPE_PLAY_SOUND:
if (!update_recv_play_sound(rdp->update, cs))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "DATA_PDU_TYPE_PLAY_SOUND - update_recv_play_sound() failed");
2015-05-06 17:54:23 +03:00
goto out_fail;
}
break;
case DATA_PDU_TYPE_SHUTDOWN_DENIED:
if (!rdp_recv_server_shutdown_denied_pdu(rdp, cs))
2015-05-02 06:26:08 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_ERR(
TAG,
"DATA_PDU_TYPE_SHUTDOWN_DENIED - rdp_recv_server_shutdown_denied_pdu() failed");
2015-05-06 17:54:23 +03:00
goto out_fail;
}
break;
case DATA_PDU_TYPE_SAVE_SESSION_INFO:
if (!rdp_recv_save_session_info(rdp, cs))
2015-05-02 06:26:08 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG,
"DATA_PDU_TYPE_SAVE_SESSION_INFO - rdp_recv_save_session_info() failed");
2015-05-06 17:54:23 +03:00
goto out_fail;
}
break;
case DATA_PDU_TYPE_FONT_MAP:
if (!rdp_recv_font_map_pdu(rdp, cs))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "DATA_PDU_TYPE_FONT_MAP - rdp_recv_font_map_pdu() failed");
goto out_fail;
}
break;
case DATA_PDU_TYPE_SET_KEYBOARD_INDICATORS:
if (!rdp_recv_server_set_keyboard_indicators_pdu(rdp, cs))
2015-05-02 06:26:08 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG, "DATA_PDU_TYPE_SET_KEYBOARD_INDICATORS - "
"rdp_recv_server_set_keyboard_indicators_pdu() failed");
2015-05-06 17:54:23 +03:00
goto out_fail;
}
break;
case DATA_PDU_TYPE_SET_KEYBOARD_IME_STATUS:
if (!rdp_recv_server_set_keyboard_ime_status_pdu(rdp, cs))
2015-05-02 06:26:08 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG, "DATA_PDU_TYPE_SET_KEYBOARD_IME_STATUS - "
"rdp_recv_server_set_keyboard_ime_status_pdu() failed");
2015-05-06 17:54:23 +03:00
goto out_fail;
}
break;
2011-07-22 00:20:41 +04:00
case DATA_PDU_TYPE_SET_ERROR_INFO:
if (!rdp_recv_set_error_info_data_pdu(rdp, cs))
2015-05-02 06:26:08 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_ERR(
TAG,
"DATA_PDU_TYPE_SET_ERROR_INFO - rdp_recv_set_error_info_data_pdu() failed");
2015-05-06 17:54:23 +03:00
goto out_fail;
}
2011-07-22 00:20:41 +04:00
break;
case DATA_PDU_TYPE_ARC_STATUS:
if (!rdp_recv_server_auto_reconnect_status_pdu(rdp, cs))
2015-05-02 06:26:08 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG, "DATA_PDU_TYPE_ARC_STATUS - "
"rdp_recv_server_auto_reconnect_status_pdu() failed");
2015-05-06 17:54:23 +03:00
goto out_fail;
}
break;
case DATA_PDU_TYPE_STATUS_INFO:
if (!rdp_recv_server_status_info_pdu(rdp, cs))
2015-05-02 06:26:08 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG,
"DATA_PDU_TYPE_STATUS_INFO - rdp_recv_server_status_info_pdu() failed");
2015-05-06 17:54:23 +03:00
goto out_fail;
}
break;
case DATA_PDU_TYPE_MONITOR_LAYOUT:
if (!rdp_recv_monitor_layout_pdu(rdp, cs))
2015-05-02 06:26:08 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG,
"DATA_PDU_TYPE_MONITOR_LAYOUT - rdp_recv_monitor_layout_pdu() failed");
2015-05-06 17:54:23 +03:00
goto out_fail;
}
break;
2011-07-22 00:20:41 +04:00
default:
WLog_WARN(TAG, "[UNHANDLED] %s Data PDU (0x%02" PRIX8 "), length: %" PRIu16 "",
data_pdu_type_to_string(type), type, length);
2011-07-22 00:20:41 +04:00
break;
}
if (cs != s)
Stream_Release(cs);
return STATE_RUN_SUCCESS;
out_fail:
if (cs != s)
Stream_Release(cs);
return STATE_RUN_FAILED;
2011-07-22 00:20:41 +04:00
}
state_run_t rdp_recv_message_channel_pdu(rdpRdp* rdp, wStream* s, UINT16 securityFlags)
{
WINPR_ASSERT(rdp);
WINPR_ASSERT(s);
if (securityFlags & SEC_AUTODETECT_REQ)
{
/* Server Auto-Detect Request PDU */
return autodetect_recv_request_packet(rdp->autodetect, RDP_TRANSPORT_TCP, s);
}
if (securityFlags & SEC_AUTODETECT_RSP)
{
/* Client Auto-Detect Response PDU */
return autodetect_recv_response_packet(rdp->autodetect, RDP_TRANSPORT_TCP, s);
}
2014-01-24 07:23:47 +04:00
if (securityFlags & SEC_HEARTBEAT)
{
/* Heartbeat PDU */
return rdp_recv_heartbeat_packet(rdp, s);
}
if (securityFlags & SEC_TRANSPORT_REQ)
{
HRESULT hr = E_ABORT;
/* Initiate Multitransport Request PDU */
// TODO: This message is server -> client only
state_run_t rc = multitransport_client_recv_request(rdp->multitransport, s);
if (state_run_failed(rc))
return rc;
if (!multitransport_client_send_response(rdp->multitransport, hr))
return STATE_RUN_FAILED;
return STATE_RUN_SUCCESS;
}
if (securityFlags & SEC_TRANSPORT_RSP)
{
/* Initiate Multitransport Request PDU */
HRESULT hr; // TODO: Do something with this result
// TODO: This message is client -> server only
return multitransport_server_recv_response(rdp->multitransport, s, &hr) ? 0 : -1;
}
2022-10-21 10:45:01 +03:00
if (securityFlags & SEC_LICENSE_PKT)
{
return license_recv(rdp->license, s);
}
if (securityFlags & SEC_LICENSE_ENCRYPT_CS)
{
return license_recv(rdp->license, s);
}
if (securityFlags & SEC_LICENSE_ENCRYPT_SC)
{
return license_recv(rdp->license, s);
}
return STATE_RUN_SUCCESS;
}
state_run_t rdp_recv_out_of_sequence_pdu(rdpRdp* rdp, wStream* s)
{
UINT16 type;
UINT16 length;
UINT16 channelId;
if (!rdp_read_share_control_header(s, &length, NULL, &type, &channelId))
return STATE_RUN_FAILED;
if (type == PDU_TYPE_DATA)
{
return rdp_recv_data_pdu(rdp, s);
}
else if (type == PDU_TYPE_SERVER_REDIRECTION)
{
2013-01-12 03:43:16 +04:00
return rdp_recv_enhanced_security_redirection_packet(rdp, s);
}
2019-11-06 17:24:51 +03:00
else if (type == PDU_TYPE_FLOW_RESPONSE || type == PDU_TYPE_FLOW_STOP ||
type == PDU_TYPE_FLOW_TEST)
{
return STATE_RUN_SUCCESS;
}
else
{
return STATE_RUN_FAILED;
}
}
BOOL rdp_read_flow_control_pdu(wStream* s, UINT16* type, UINT16* channel_id)
{
/*
* Read flow control PDU - documented in FlowPDU section in T.128
* http://www.itu.int/rec/T-REC-T.128-199802-S/en
* The specification for the PDU has pad8bits listed BEFORE pduTypeFlow.
* However, so far pad8bits has always been observed to arrive AFTER pduTypeFlow.
* Switched the order of these two fields to match this observation.
*/
UINT8 pduType;
WINPR_ASSERT(s);
WINPR_ASSERT(type);
WINPR_ASSERT(channel_id);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
return FALSE;
2019-11-06 17:24:51 +03:00
Stream_Read_UINT8(s, pduType); /* pduTypeFlow */
*type = pduType;
Stream_Seek_UINT8(s); /* pad8bits */
Stream_Seek_UINT8(s); /* flowIdentifier */
Stream_Seek_UINT8(s); /* flowNumber */
Stream_Read_UINT16(s, *channel_id); /* pduSource */
return TRUE;
}
2011-09-05 22:02:52 +04:00
/**
* Decrypt an RDP packet.\n
* @param rdp RDP module
* @param s stream
* @param length int
*/
BOOL rdp_decrypt(rdpRdp* rdp, wStream* s, UINT16* pLength, UINT16 securityFlags)
2011-09-05 22:02:52 +04:00
{
BYTE cmac[8];
BYTE wmac[8];
BOOL status;
INT32 length;
2011-09-15 01:14:50 +04:00
WINPR_ASSERT(rdp);
WINPR_ASSERT(rdp->settings);
WINPR_ASSERT(s);
WINPR_ASSERT(pLength);
length = *pLength;
if (rdp->settings->EncryptionMethods == ENCRYPTION_METHOD_FIPS)
2011-09-16 03:54:03 +04:00
{
UINT16 len;
BYTE version, pad;
BYTE* sig;
INT64 padLength;
2011-09-16 03:54:03 +04:00
if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
2013-01-12 03:43:16 +04:00
return FALSE;
2019-11-06 17:24:51 +03:00
Stream_Read_UINT16(s, len); /* 0x10 */
2013-05-09 00:09:16 +04:00
Stream_Read_UINT8(s, version); /* 0x1 */
Stream_Read_UINT8(s, pad);
sig = Stream_Pointer(s);
2019-11-06 17:24:51 +03:00
Stream_Seek(s, 8); /* signature */
length -= 12;
padLength = length - pad;
if ((length <= 0) || (padLength <= 0) || (padLength > UINT16_MAX))
{
WLog_ERR(TAG, "FATAL: invalid pad length %" PRId32, padLength);
return FALSE;
}
2011-09-16 03:54:03 +04:00
if (!security_fips_decrypt(Stream_Pointer(s), length, rdp))
2011-09-16 03:54:03 +04:00
{
WLog_ERR(TAG, "FATAL: cannot decrypt");
return FALSE; /* TODO */
2011-09-16 03:54:03 +04:00
}
if (!security_fips_check_signature(Stream_Pointer(s), length - pad, sig, rdp))
2011-09-16 03:54:03 +04:00
{
WLog_ERR(TAG, "FATAL: invalid packet signature");
return FALSE; /* TODO */
2011-09-16 03:54:03 +04:00
}
2016-02-25 22:01:12 +03:00
Stream_SetLength(s, Stream_Length(s) - pad);
*pLength = (UINT16)padLength;
return TRUE;
2011-09-16 03:54:03 +04:00
}
if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(wmac)))
2013-01-12 03:43:16 +04:00
return FALSE;
2013-05-09 00:09:16 +04:00
Stream_Read(s, wmac, sizeof(wmac));
length -= sizeof(wmac);
if (length <= 0)
{
WLog_ERR(TAG, "FATAL: invalid length field");
return FALSE;
}
if (!security_decrypt(Stream_Pointer(s), length, rdp))
return FALSE;
if (securityFlags & SEC_SECURE_CHECKSUM)
status = security_salted_mac_signature(rdp, Stream_Pointer(s), length, FALSE, cmac);
else
status = security_mac_signature(rdp, Stream_Pointer(s), length, cmac);
if (!status)
return FALSE;
2012-01-25 19:26:32 +04:00
if (memcmp(wmac, cmac, sizeof(wmac)) != 0)
{
WLog_ERR(TAG, "WARNING: invalid packet signature");
/*
* Because Standard RDP Security is totally broken,
* and cannot protect against MITM, don't treat signature
* verification failure as critical. This at least enables
* us to work with broken RDP clients and servers that
* generate invalid signatures.
*/
2019-11-06 17:24:51 +03:00
// return FALSE;
}
*pLength = length;
return TRUE;
2011-09-05 22:02:52 +04:00
}
const char* pdu_type_to_str(UINT16 pduType)
{
static char buffer[1024] = { 0 };
switch (pduType)
{
case PDU_TYPE_DEMAND_ACTIVE:
return "PDU_TYPE_DEMAND_ACTIVE";
case PDU_TYPE_CONFIRM_ACTIVE:
return "PDU_TYPE_CONFIRM_ACTIVE";
case PDU_TYPE_DEACTIVATE_ALL:
return "PDU_TYPE_DEACTIVATE_ALL";
case PDU_TYPE_DATA:
return "PDU_TYPE_DATA";
case PDU_TYPE_SERVER_REDIRECTION:
return "PDU_TYPE_SERVER_REDIRECTION";
case PDU_TYPE_FLOW_TEST:
return "PDU_TYPE_FLOW_TEST";
case PDU_TYPE_FLOW_RESPONSE:
return "PDU_TYPE_FLOW_RESPONSE";
case PDU_TYPE_FLOW_STOP:
return "PDU_TYPE_FLOW_STOP";
default:
_snprintf(buffer, sizeof(buffer), "UNKNOWN %04" PRIx16, pduType);
return buffer;
}
}
/**
* Process an RDP packet.\n
* @param rdp RDP module
* @param s stream
*/
static state_run_t rdp_recv_tpkt_pdu(rdpRdp* rdp, wStream* s)
{
state_run_t rc = STATE_RUN_SUCCESS;
UINT16 length;
UINT16 pduType;
UINT16 pduSource;
2013-08-30 18:31:23 +04:00
UINT16 channelId = 0;
UINT16 securityFlags = 0;
2022-03-29 12:12:52 +03:00
freerdp* instance;
WINPR_ASSERT(rdp);
WINPR_ASSERT(rdp->context);
WINPR_ASSERT(s);
instance = rdp->context->instance;
WINPR_ASSERT(instance);
if (!rdp_read_header(rdp, s, &length, &channelId))
return STATE_RUN_FAILED;
if (freerdp_shall_disconnect_context(rdp->context))
return STATE_RUN_SUCCESS;
if (rdp->autodetect->bandwidthMeasureStarted)
{
rdp->autodetect->bandwidthMeasureByteCount += length;
}
Standard RDP Security Layer Levels/Method Overhaul [MS-RDPBCGR] Section 5.3 describes the encryption level and method values for standard RDP security. Looking at the current usage of these values in the FreeRDP code gives me reason to believe that there is a certain lack of understanding of how these values should be handled. The encryption level is only configured on the server side in the "Encryption Level" setting found in the Remote Desktop Session Host Configuration RDP-Tcp properties dialog and this value is never transferred from the client to the server over the wire. The possible options are "None", "Low", "Client Compatible", "High" and "FIPS Compliant". The client receices this value in the Server Security Data block (TS_UD_SC_SEC1), probably only for informational purposes and maybe to give the client the possibility to verify if the server's decision for the encryption method confirms to the server's encryption level. The possible encryption methods are "NONE", "40BIT", "56BIT", "128BIT" and "FIPS" and the RDP client advertises the ones it supports to the server in the Client Security Data block (TS_UD_CS_SEC). The server's configured encryption level value restricts the possible final encryption method. Something that I was not able to find in the documentation is the priority level of the individual encryption methods based on which the server makes its final method decision if there are several options. My analysis with Windows Servers reveiled that the order is 128, 56, 40, FIPS. The server only chooses FIPS if the level is "FIPS Comliant" or if it is the only method advertised by the client. Bottom line: * FreeRDP's client side does not need to set settings->EncryptionLevel (which was done quite frequently). * FreeRDP's server side does not have to set the supported encryption methods list in settings->EncryptionMethods Changes in this commit: Removed unnecessary/confusing changes of EncryptionLevel/Methods settings Refactor settings->DisableEncryption * This value actually means "Advanced RDP Encryption (NLA/TLS) is NOT used" * The old name caused lots of confusion among developers * Renamed it to "UseRdpSecurityLayer" (the compare logic stays untouched) Any client's setting of settings->EncryptionMethods were annihilated * All clients "want" to set all supported methods * Some clients forgot 56bit because 56bit was not supported at the time the code was written * settings->EncryptionMethods was overwritten anyways in nego_connect() * Removed all client side settings of settings->EncryptionMethods The default is "None" (0) * Changed nego_connect() to advertise all supported methods if settings->EncryptionMethods is 0 (None) * Added a commandline option /encryption-methods:comma separated list of the values "40", "56", "128", "FIPS". E.g. /encryption-methods:56,128 * Print warning if server chooses non-advertised method Verify received level and method in client's gcc_read_server_security_data * Only accept valid/known encryption methods * Verify encryption level/method combinations according to MS-RDPBCGR 5.3.2 Server implementations can now set settings->EncryptionLevel * The default for settings->EncryptionLevel is 0 (None) * nego_send_negotiation_response() changes it to ClientCompatible in that case * default to ClientCompatible if the server implementation set an invalid level Fix server's gcc_write_server_security_data * Verify server encryption level value set by server implementations * Choose rdp encryption method based on level and supported client methods * Moved FIPS to the lowest priority (only used if other methods are possible) Updated sample server * Support RDP Security (RdpKeyFile was not set) * Added commented sample code for setting the security level
2014-12-12 04:17:12 +03:00
if (rdp->settings->UseRdpSecurityLayer)
2011-09-05 22:02:52 +04:00
{
if (!rdp_read_security_header(s, &securityFlags, &length))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "rdp_recv_tpkt_pdu: rdp_read_security_header() fail");
return STATE_RUN_FAILED;
}
if (securityFlags & (SEC_ENCRYPT | SEC_REDIRECTION_PKT))
2011-09-05 22:02:52 +04:00
{
if (!rdp_decrypt(rdp, s, &length, securityFlags))
2011-09-05 22:02:52 +04:00
{
WLog_ERR(TAG, "rdp_decrypt failed");
return STATE_RUN_FAILED;
2011-09-05 22:02:52 +04:00
}
}
if (securityFlags & SEC_REDIRECTION_PKT)
{
/*
* [MS-RDPBCGR] 2.2.13.2.1
* - no share control header, nor the 2 byte pad
*/
Stream_Rewind(s, 2);
rdp->inPackets++;
rc = rdp_recv_enhanced_security_redirection_packet(rdp, s);
goto out;
}
2011-09-05 22:02:52 +04:00
}
if (channelId == MCS_GLOBAL_CHANNEL_ID)
{
while (Stream_GetRemainingLength(s) > 3)
{
wStream subbuffer;
wStream* sub;
size_t diff;
UINT16 remain;
if (!rdp_read_share_control_header(s, NULL, &remain, &pduType, &pduSource))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "rdp_recv_tpkt_pdu: rdp_read_share_control_header() fail");
return STATE_RUN_FAILED;
}
sub = Stream_StaticInit(&subbuffer, Stream_Pointer(s), remain);
if (!Stream_SafeSeek(s, remain))
return STATE_RUN_FAILED;
rdp->settings->PduSource = pduSource;
rdp->inPackets++;
switch (pduType)
{
case PDU_TYPE_DATA:
rc = rdp_recv_data_pdu(rdp, sub);
if (state_run_failed(rc))
return rc;
break;
case PDU_TYPE_DEACTIVATE_ALL:
if (!rdp_recv_deactivate_all(rdp, sub))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "rdp_recv_tpkt_pdu: rdp_recv_deactivate_all() fail");
return STATE_RUN_FAILED;
}
break;
case PDU_TYPE_SERVER_REDIRECTION:
return rdp_recv_enhanced_security_redirection_packet(rdp, sub);
case PDU_TYPE_FLOW_RESPONSE:
case PDU_TYPE_FLOW_STOP:
case PDU_TYPE_FLOW_TEST:
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "flow message 0x%04" PRIX16 "", pduType);
/* http://msdn.microsoft.com/en-us/library/cc240576.aspx */
if (!Stream_SafeSeek(sub, remain))
return STATE_RUN_FAILED;
break;
default:
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG, "incorrect PDU type: 0x%04" PRIX16 "", pduType);
break;
}
diff = Stream_GetRemainingLength(sub);
if (diff > 0)
{
WLog_WARN(TAG,
"pduType %s not properly parsed, %" PRIdz
" bytes remaining unhandled. Skipping.",
pdu_type_to_str(pduType), diff);
}
}
}
else if (rdp->mcs->messageChannelId && (channelId == rdp->mcs->messageChannelId))
{
if (!rdp->settings->UseRdpSecurityLayer)
if (!rdp_read_security_header(s, &securityFlags, NULL))
return STATE_RUN_FAILED;
rdp->inPackets++;
rc = rdp_recv_message_channel_pdu(rdp, s, securityFlags);
}
else
{
rdp->inPackets++;
2022-03-29 12:12:52 +03:00
if (!freerdp_channel_process(instance, s, channelId, length))
return STATE_RUN_FAILED;
}
out:
if (!tpkt_ensure_stream_consumed(s, length))
return STATE_RUN_FAILED;
return rc;
}
static state_run_t rdp_recv_fastpath_pdu(rdpRdp* rdp, wStream* s)
{
UINT16 length;
2011-09-27 09:30:58 +04:00
rdpFastPath* fastpath;
fastpath = rdp->fastpath;
2013-01-14 02:37:50 +04:00
if (!fastpath_read_header_rdp(fastpath, s, &length))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "rdp_recv_fastpath_pdu: fastpath_read_header_rdp() fail");
return STATE_RUN_FAILED;
}
2012-01-25 19:27:00 +04:00
if ((length == 0) || (!Stream_CheckAndLogRequiredLength(TAG, s, length)))
{
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG, "incorrect FastPath PDU header length %" PRIu16 "", length);
return STATE_RUN_FAILED;
}
if (rdp->autodetect->bandwidthMeasureStarted)
{
rdp->autodetect->bandwidthMeasureByteCount += length;
}
if (fastpath_get_encryption_flags(fastpath) & FASTPATH_OUTPUT_ENCRYPTED)
2011-09-15 01:14:50 +04:00
{
UINT16 flags = (fastpath_get_encryption_flags(fastpath) & FASTPATH_OUTPUT_SECURE_CHECKSUM)
? SEC_SECURE_CHECKSUM
: 0;
if (!rdp_decrypt(rdp, s, &length, flags))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "rdp_recv_fastpath_pdu: rdp_decrypt() fail");
return STATE_RUN_FAILED;
}
2011-09-15 01:14:50 +04:00
}
2011-08-23 09:05:58 +04:00
return fastpath_recv_updates(rdp->fastpath, s);
}
static state_run_t rdp_recv_pdu(rdpRdp* rdp, wStream* s)
{
const int rc = tpkt_verify_header(s);
if (rc > 0)
2011-08-23 08:58:10 +04:00
return rdp_recv_tpkt_pdu(rdp, s);
else if (rc == 0)
2011-08-23 08:58:10 +04:00
return rdp_recv_fastpath_pdu(rdp, s);
else
return STATE_RUN_FAILED;
}
static state_run_t rdp_recv_callback_int(rdpTransport* transport, wStream* s, void* extra)
{
const UINT32 mask = FINALIZE_SC_SYNCHRONIZE_PDU | FINALIZE_SC_CONTROL_COOPERATE_PDU |
FINALIZE_SC_CONTROL_GRANTED_PDU | FINALIZE_SC_FONT_MAP_PDU;
state_run_t status = STATE_RUN_SUCCESS;
2019-11-06 17:24:51 +03:00
rdpRdp* rdp = (rdpRdp*)extra;
WINPR_ASSERT(transport);
WINPR_ASSERT(rdp);
WINPR_ASSERT(s);
switch (rdp_get_state(rdp))
{
case CONNECTION_STATE_NEGO:
rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CREATE_REQUEST);
status = STATE_RUN_CONTINUE;
break;
2015-02-16 00:04:59 +03:00
case CONNECTION_STATE_NLA:
2018-11-20 18:48:59 +03:00
if (nla_get_state(rdp->nla) < NLA_STATE_AUTH_INFO)
2015-05-02 06:26:08 +03:00
{
if (nla_recv_pdu(rdp->nla, s) < 1)
{
WLog_ERR(TAG, "%s: %s - nla_recv_pdu() fail", __FUNCTION__,
rdp_get_state_string(rdp));
status = STATE_RUN_FAILED;
}
}
2018-11-20 18:48:59 +03:00
else if (nla_get_state(rdp->nla) == NLA_STATE_POST_NEGO)
{
2019-11-06 17:24:51 +03:00
nego_recv(rdp->transport, s, (void*)rdp->nego);
2018-11-20 18:38:06 +03:00
if (nego_get_state(rdp->nego) != NEGO_STATE_FINAL)
{
WLog_ERR(TAG, "%s: %s - nego_recv() fail", __FUNCTION__,
rdp_get_state_string(rdp));
status = STATE_RUN_FAILED;
}
else if (!nla_set_state(rdp->nla, NLA_STATE_FINAL))
status = STATE_RUN_FAILED;
}
2015-02-16 00:04:59 +03:00
if (state_run_success(status))
2015-02-16 00:04:59 +03:00
{
if (nla_get_state(rdp->nla) == NLA_STATE_AUTH_INFO)
{
transport_set_nla_mode(rdp->transport, FALSE);
2018-11-20 18:38:06 +03:00
if (rdp->settings->VmConnectMode)
{
if (!nego_set_state(rdp->nego, NEGO_STATE_NLA))
status = STATE_RUN_FAILED;
else if (!nego_set_requested_protocols(rdp->nego,
PROTOCOL_HYBRID | PROTOCOL_SSL))
status = STATE_RUN_FAILED;
else
{
if (!nego_send_negotiation_request(rdp->nego))
status = STATE_RUN_FAILED;
else if (!nla_set_state(rdp->nla, NLA_STATE_POST_NEGO))
status = STATE_RUN_FAILED;
}
}
else
{
if (!nla_set_state(rdp->nla, NLA_STATE_FINAL))
status = STATE_RUN_FAILED;
}
}
}
if (state_run_success(status))
{
2015-02-16 00:04:59 +03:00
if (nla_get_state(rdp->nla) == NLA_STATE_FINAL)
2015-05-02 06:26:08 +03:00
{
rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CREATE_REQUEST);
status = STATE_RUN_TRY_AGAIN;
}
2015-02-16 00:04:59 +03:00
}
break;
case CONNECTION_STATE_MCS_CREATE_REQUEST:
if (!mcs_client_begin(rdp->mcs))
{
WLog_ERR(TAG, "%s: %s - mcs_client_begin() fail", __FUNCTION__,
rdp_get_state_string(rdp));
status = STATE_RUN_FAILED;
}
else
{
rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CREATE_RESPONSE);
}
break;
case CONNECTION_STATE_MCS_CREATE_RESPONSE:
if (!mcs_recv_connect_response(rdp->mcs, s))
{
WLog_ERR(TAG, "mcs_recv_connect_response failure");
status = STATE_RUN_FAILED;
}
else
{
rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_ERECT_DOMAIN);
if (!mcs_send_erect_domain_request(rdp->mcs))
{
WLog_ERR(TAG, "mcs_send_erect_domain_request failure");
status = STATE_RUN_FAILED;
}
else
{
rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_ATTACH_USER);
if (!mcs_send_attach_user_request(rdp->mcs))
{
WLog_ERR(TAG, "mcs_send_attach_user_request failure");
status = STATE_RUN_FAILED;
}
else
rdp_client_transition_to_state(rdp,
CONNECTION_STATE_MCS_ATTACH_USER_CONFIRM);
}
}
break;
case CONNECTION_STATE_MCS_ATTACH_USER_CONFIRM:
if (!mcs_recv_attach_user_confirm(rdp->mcs, s))
{
WLog_ERR(TAG, "mcs_recv_attach_user_confirm failure");
status = STATE_RUN_FAILED;
}
else
{
rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_REQUEST);
if (!mcs_send_channel_join_request(rdp->mcs, rdp->mcs->userId))
{
WLog_ERR(TAG, "mcs_send_channel_join_request failure");
status = STATE_RUN_FAILED;
}
else
rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_RESPONSE);
}
break;
case CONNECTION_STATE_MCS_CHANNEL_JOIN_RESPONSE:
if (!rdp_client_connect_mcs_channel_join_confirm(rdp, s))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG,
"%s: %s - "
"rdp_client_connect_mcs_channel_join_confirm() fail",
__FUNCTION__, rdp_get_state_string(rdp));
status = STATE_RUN_FAILED;
}
break;
case CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT_REQUEST:
if (!rdp_client_connect_auto_detect(rdp, s))
{
rdp_client_transition_to_state(rdp, CONNECTION_STATE_LICENSING);
status = STATE_RUN_TRY_AGAIN;
}
break;
case CONNECTION_STATE_LICENSING:
status = rdp_client_connect_license(rdp, s);
if (state_run_failed(status))
{
char buffer[64] = { 0 };
WLog_DBG(TAG, "%s: %s - rdp_client_connect_license() - %s", __FUNCTION__,
rdp_get_state_string(rdp),
state_run_result_string(status, buffer, ARRAYSIZE(buffer)));
}
break;
case CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_REQUEST:
if (!rdp_client_connect_auto_detect(rdp, s))
{
rdp_client_transition_to_state(
rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE);
status = STATE_RUN_TRY_AGAIN;
}
break;
case CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE:
status = rdp_client_connect_demand_active(rdp, s);
if (state_run_failed(status))
{
char buffer[64] = { 0 };
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG,
"%s: %s - "
"rdp_client_connect_demand_active() - %s",
__FUNCTION__, rdp_get_state_string(rdp),
state_run_result_string(status, buffer, ARRAYSIZE(buffer)));
}
else if (status != STATE_RUN_REDIRECT)
{
if (!rdp->settings->SupportMonitorLayoutPdu)
{
rdp_client_transition_to_state(
rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_CONFIRM_ACTIVE);
status = STATE_RUN_TRY_AGAIN;
}
else
{
rdp_client_transition_to_state(
rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_MONITOR_LAYOUT);
}
}
break;
case CONNECTION_STATE_CAPABILITIES_EXCHANGE_MONITOR_LAYOUT:
status = rdp_recv_pdu(rdp, s);
if (state_run_success(status))
{
status = STATE_RUN_TRY_AGAIN;
rdp_client_transition_to_state(
rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_CONFIRM_ACTIVE);
}
break;
case CONNECTION_STATE_CAPABILITIES_EXCHANGE_CONFIRM_ACTIVE:
status = rdp_client_connect_confirm_active(rdp, s);
break;
case CONNECTION_STATE_FINALIZATION_CLIENT_SYNC:
{
const UINT32 flags = rdp->finalize_sc_pdus & mask;
status = rdp_recv_pdu(rdp, s);
if (state_run_success(status))
{
const UINT32 uflags = rdp->finalize_sc_pdus & mask;
if (flags != uflags)
rdp_client_transition_to_state(rdp,
CONNECTION_STATE_FINALIZATION_CLIENT_COOPERATE);
else
status = STATE_RUN_FAILED;
}
}
break;
case CONNECTION_STATE_FINALIZATION_CLIENT_COOPERATE:
{
const UINT32 flags = rdp->finalize_sc_pdus & mask;
status = rdp_recv_pdu(rdp, s);
if (state_run_success(status))
{
const UINT32 uflags = rdp->finalize_sc_pdus & mask;
if (flags != uflags)
rdp_client_transition_to_state(
rdp, CONNECTION_STATE_FINALIZATION_CLIENT_GRANTED_CONTROL);
else
status = STATE_RUN_FAILED;
}
}
break;
case CONNECTION_STATE_FINALIZATION_CLIENT_GRANTED_CONTROL:
{
const UINT32 flags = rdp->finalize_sc_pdus & mask;
status = rdp_recv_pdu(rdp, s);
if (state_run_success(status))
{
const UINT32 uflags = rdp->finalize_sc_pdus & mask;
if (flags != uflags)
rdp_client_transition_to_state(rdp,
CONNECTION_STATE_FINALIZATION_CLIENT_FONT_MAP);
else
status = STATE_RUN_FAILED;
}
}
break;
case CONNECTION_STATE_FINALIZATION_CLIENT_FONT_MAP:
{
const UINT32 flags = rdp->finalize_sc_pdus & mask;
status = rdp_recv_pdu(rdp, s);
if (state_run_success(status))
{
const UINT32 uflags = rdp->finalize_sc_pdus & mask;
if (flags == uflags)
WLog_WARN(TAG, "Did not receive a FINALIZE_SC_FONT_MAP_PDU");
{
rdp_client_transition_to_state(rdp, CONNECTION_STATE_ACTIVE);
status = STATE_RUN_ACTIVE;
}
}
if (state_run_failed(status))
{
char buffer[64] = { 0 };
WLog_DBG(TAG, "%s: %s - rdp_recv_pdu() - %s", __FUNCTION__,
rdp_get_state_string(rdp),
state_run_result_string(status, buffer, ARRAYSIZE(buffer)));
}
}
break;
case CONNECTION_STATE_ACTIVE:
status = rdp_recv_pdu(rdp, s);
if (state_run_failed(status))
{
char buffer[64] = { 0 };
WLog_DBG(TAG, "%s: %s - rdp_recv_pdu() - %s", __FUNCTION__,
rdp_get_state_string(rdp),
state_run_result_string(status, buffer, ARRAYSIZE(buffer)));
}
break;
default:
WLog_ERR(TAG, "%s: %s state %d", __FUNCTION__, rdp_get_state_string(rdp),
rdp_get_state(rdp));
status = STATE_RUN_FAILED;
break;
}
if (state_run_failed(status))
{
char buffer[64] = { 0 };
WLog_ERR(TAG, "%s: %s status %s", __FUNCTION__, rdp_get_state_string(rdp),
state_run_result_string(status, buffer, ARRAYSIZE(buffer)));
}
return status;
}
state_run_t rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra)
{
char buffer[64] = { 0 };
state_run_t rc = STATE_RUN_FAILED;
const size_t start = Stream_GetPosition(s);
const rdpContext* context = transport_get_context(transport);
WINPR_ASSERT(context);
do
{
const rdpRdp* rdp = context->rdp;
const char* old = rdp_get_state_string(rdp);
if (rc == STATE_RUN_TRY_AGAIN)
Stream_SetPosition(s, start);
rc = rdp_recv_callback_int(transport, s, extra);
WLog_VRB(TAG, "(client)[%s -> %s] current return %s", old, rdp_get_state_string(rdp),
state_run_result_string(rc, buffer, sizeof(buffer)));
} while ((rc == STATE_RUN_TRY_AGAIN) || (rc == STATE_RUN_CONTINUE));
return rc;
}
BOOL rdp_send_channel_data(rdpRdp* rdp, UINT16 channelId, const BYTE* data, size_t size)
{
return freerdp_channel_send(rdp, channelId, data, size);
}
BOOL rdp_channel_send_packet(rdpRdp* rdp, UINT16 channelId, size_t totalSize, UINT32 flags,
const BYTE* data, size_t chunkSize)
{
return freerdp_channel_send_packet(rdp, channelId, totalSize, flags, data, chunkSize);
}
BOOL rdp_send_error_info(rdpRdp* rdp)
{
wStream* s;
BOOL status;
if (rdp->errorInfo == ERRINFO_SUCCESS)
return TRUE;
s = rdp_data_pdu_init(rdp);
2016-02-12 00:45:30 +03:00
if (!s)
return FALSE;
Stream_Write_UINT32(s, rdp->errorInfo); /* error id (4 bytes) */
status = rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_SET_ERROR_INFO, 0);
return status;
}
int rdp_check_fds(rdpRdp* rdp)
{
int status;
2021-09-06 12:01:36 +03:00
rdpTsg* tsg;
rdpTransport* transport;
2021-09-06 12:01:36 +03:00
WINPR_ASSERT(rdp);
transport = rdp->transport;
2015-02-02 19:50:56 +03:00
2021-09-06 12:01:36 +03:00
tsg = transport_get_tsg(transport);
if (tsg)
{
2018-09-28 09:43:43 +03:00
if (!tsg_check_event_handles(tsg))
2015-05-02 06:26:08 +03:00
{
2018-09-28 09:43:43 +03:00
WLog_ERR(TAG, "rdp_check_fds: tsg_check_event_handles()");
2015-02-02 19:50:56 +03:00
return -1;
}
2015-02-02 19:50:56 +03:00
2018-09-28 09:43:43 +03:00
if (tsg_get_state(tsg) != TSG_STATE_PIPE_CREATED)
return 1;
2015-02-02 19:50:56 +03:00
}
status = transport_check_fds(transport);
if (status == 1)
{
if (!rdp_client_redirect(rdp)) /* session redirection */
return -1;
}
if (status < 0)
WLog_DBG(TAG, "transport_check_fds() - %i", status);
return status;
}
2019-11-06 17:24:51 +03:00
BOOL freerdp_get_stats(rdpRdp* rdp, UINT64* inBytes, UINT64* outBytes, UINT64* inPackets,
UINT64* outPackets)
2019-10-23 00:35:18 +03:00
{
if (!rdp)
return FALSE;
if (inBytes)
*inBytes = rdp->inBytes;
if (outBytes)
*outBytes = rdp->outBytes;
if (inPackets)
*inPackets = rdp->inPackets;
if (outPackets)
*outPackets = rdp->outPackets;
return TRUE;
}
/**
* Instantiate new RDP module.
* @return new RDP module
*/
rdpRdp* rdp_new(rdpContext* context)
{
rdpRdp* rdp;
DWORD flags;
2019-11-06 17:24:51 +03:00
rdp = (rdpRdp*)calloc(1, sizeof(rdpRdp));
2014-04-10 23:07:53 +04:00
if (!rdp)
return NULL;
InitializeCriticalSection(&rdp->critical);
2014-04-10 23:07:53 +04:00
rdp->context = context;
flags = 0;
2014-04-10 23:07:53 +04:00
if (context->ServerMode)
flags |= FREERDP_SETTINGS_SERVER_MODE;
2014-04-10 23:07:53 +04:00
if (!context->settings)
{
context->settings = rdp->settings = freerdp_settings_new(flags);
if (!rdp->settings)
goto fail;
}
else
rdp->settings = context->settings;
/* Keep a backup copy of settings for later comparisons */
freerdp_settings_free(rdp->originalSettings);
rdp->originalSettings = freerdp_settings_clone(rdp->settings);
if (!rdp->originalSettings)
return FALSE;
rdp->settings->instance = context->instance;
context->settings = rdp->settings;
2014-04-10 23:07:53 +04:00
if (context->instance)
context->settings->instance = context->instance;
else if (context->peer)
{
rdp->settings->instance = context->peer;
#if defined(WITH_FREERDP_DEPRECATED)
context->peer->settings = rdp->settings;
#endif
}
2014-04-10 23:07:53 +04:00
rdp->transport = transport_new(context);
2014-04-10 23:07:53 +04:00
if (!rdp->transport)
goto fail;
2014-04-10 23:07:53 +04:00
{
const rdpTransportIo* io = transport_get_io_callbacks(rdp->transport);
if (!io)
goto fail;
rdp->io = calloc(1, sizeof(rdpTransportIo));
if (!rdp->io)
goto fail;
*rdp->io = *io;
}
2014-04-10 23:07:53 +04:00
rdp->license = license_new(rdp);
2014-04-10 23:07:53 +04:00
if (!rdp->license)
goto fail;
2014-04-10 23:07:53 +04:00
rdp->input = input_new(rdp);
2014-04-10 23:07:53 +04:00
if (!rdp->input)
goto fail;
2014-04-10 23:07:53 +04:00
rdp->update = update_new(rdp);
2014-04-10 23:07:53 +04:00
if (!rdp->update)
goto fail;
2014-04-10 23:07:53 +04:00
rdp->fastpath = fastpath_new(rdp);
2014-04-10 23:07:53 +04:00
if (!rdp->fastpath)
goto fail;
2014-04-10 23:07:53 +04:00
rdp->nego = nego_new(rdp->transport);
2014-04-10 23:07:53 +04:00
if (!rdp->nego)
goto fail;
2014-04-10 23:07:53 +04:00
rdp->mcs = mcs_new(rdp->transport);
2014-04-10 23:07:53 +04:00
if (!rdp->mcs)
goto fail;
2014-04-10 23:07:53 +04:00
rdp->redirection = redirection_new();
2014-04-10 23:07:53 +04:00
if (!rdp->redirection)
goto fail;
2014-04-10 23:07:53 +04:00
rdp->autodetect = autodetect_new(rdp->context);
2014-04-10 23:07:53 +04:00
if (!rdp->autodetect)
goto fail;
2014-04-10 23:07:53 +04:00
rdp->heartbeat = heartbeat_new();
2014-04-10 23:07:53 +04:00
if (!rdp->heartbeat)
goto fail;
2014-04-10 23:07:53 +04:00
rdp->multitransport = multitransport_new(rdp, INITIATE_REQUEST_PROTOCOL_UDPFECL |
INITIATE_REQUEST_PROTOCOL_UDPFECR);
2014-04-10 23:07:53 +04:00
if (!rdp->multitransport)
goto fail;
2014-04-10 23:07:53 +04:00
rdp->bulk = bulk_new(context);
2014-04-10 23:07:53 +04:00
if (!rdp->bulk)
goto fail;
2014-04-10 23:07:53 +04:00
2022-07-06 10:36:53 +03:00
rdp->pubSub = PubSub_New(TRUE);
if (!rdp->pubSub)
goto fail;
rdp->abortEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!rdp->abortEvent)
goto fail;
return rdp;
fail:
rdp_free(rdp);
2014-04-10 23:07:53 +04:00
return NULL;
}
2021-09-17 10:25:30 +03:00
static void rdp_reset_free(rdpRdp* rdp)
{
WINPR_ASSERT(rdp);
winpr_RC4_Free(rdp->rc4_decrypt_key);
winpr_RC4_Free(rdp->rc4_encrypt_key);
winpr_Cipher_Free(rdp->fips_encrypt);
winpr_Cipher_Free(rdp->fips_decrypt);
rdp->rc4_decrypt_key = NULL;
rdp->rc4_encrypt_key = NULL;
rdp->fips_encrypt = NULL;
rdp->fips_decrypt = NULL;
mcs_free(rdp->mcs);
nego_free(rdp->nego);
license_free(rdp->license);
transport_free(rdp->transport);
fastpath_free(rdp->fastpath);
rdp->mcs = NULL;
rdp->nego = NULL;
rdp->license = NULL;
rdp->transport = NULL;
rdp->fastpath = NULL;
}
2021-09-07 11:03:36 +03:00
BOOL rdp_reset(rdpRdp* rdp)
{
2021-09-17 10:25:30 +03:00
BOOL rc = TRUE;
rdpContext* context;
rdpSettings* settings;
2021-09-17 10:25:30 +03:00
WINPR_ASSERT(rdp);
context = rdp->context;
2021-09-17 10:25:30 +03:00
WINPR_ASSERT(context);
settings = rdp->settings;
2021-09-17 10:25:30 +03:00
WINPR_ASSERT(settings);
bulk_reset(rdp->bulk);
2021-09-17 10:25:30 +03:00
rdp_reset_free(rdp);
2015-02-06 22:21:26 +03:00
2021-09-17 10:25:30 +03:00
if (!freerdp_settings_set_pointer_len(settings, FreeRDP_ServerRandom, NULL, 0))
rc = FALSE;
2015-02-06 22:21:26 +03:00
2021-09-17 10:25:30 +03:00
if (!freerdp_settings_set_pointer_len(settings, FreeRDP_ServerCertificate, NULL, 0))
rc = FALSE;
2015-02-06 22:21:26 +03:00
2021-09-17 10:25:30 +03:00
if (!freerdp_settings_set_string(settings, FreeRDP_ClientAddress, NULL))
rc = FALSE;
2015-02-06 22:21:26 +03:00
2021-09-17 10:25:30 +03:00
if (!rc)
goto fail;
2015-02-06 22:21:26 +03:00
2021-09-17 10:25:30 +03:00
rc = FALSE;
rdp->transport = transport_new(context);
if (!rdp->transport)
goto fail;
2015-02-06 22:21:26 +03:00
2021-09-17 10:25:30 +03:00
if (rdp->io)
2015-02-06 22:21:26 +03:00
{
2021-09-17 10:25:30 +03:00
if (!transport_set_io_callbacks(rdp->transport, rdp->io))
goto fail;
2015-02-06 22:21:26 +03:00
}
2021-09-17 10:25:30 +03:00
rdp->nego = nego_new(rdp->transport);
if (!rdp->nego)
goto fail;
2021-09-07 11:13:16 +03:00
2021-09-17 10:25:30 +03:00
rdp->mcs = mcs_new(rdp->transport);
if (!rdp->mcs)
goto fail;
2021-09-07 11:03:36 +03:00
2021-09-17 10:25:30 +03:00
if (!transport_set_layer(rdp->transport, TRANSPORT_LAYER_TCP))
goto fail;
2021-09-07 11:03:36 +03:00
rdp->license = license_new(rdp);
2021-09-17 10:25:30 +03:00
if (!rdp->license)
goto fail;
rdp->fastpath = fastpath_new(rdp);
2021-09-17 10:25:30 +03:00
if (!rdp->fastpath)
goto fail;
2015-01-12 15:47:04 +03:00
rdp->errorInfo = 0;
rc = rdp_finalize_reset_flags(rdp, TRUE);
2021-09-17 10:25:30 +03:00
fail:
return rc;
}
/**
* Free RDP module.
* @param rdp RDP module to be freed
*/
void rdp_free(rdpRdp* rdp)
{
if (rdp)
{
DeleteCriticalSection(&rdp->critical);
2021-09-17 10:25:30 +03:00
rdp_reset_free(rdp);
freerdp_settings_free(rdp->settings);
freerdp_settings_free(rdp->originalSettings);
2021-09-17 10:25:30 +03:00
input_free(rdp->input);
update_free(rdp->update);
2015-02-16 00:04:59 +03:00
nla_free(rdp->nla);
redirection_free(rdp->redirection);
autodetect_free(rdp->autodetect);
heartbeat_free(rdp->heartbeat);
multitransport_free(rdp->multitransport);
bulk_free(rdp->bulk);
free(rdp->io);
2022-07-06 10:36:53 +03:00
PubSub_Free(rdp->pubSub);
if (rdp->abortEvent)
CloseHandle(rdp->abortEvent);
free(rdp);
}
}
const rdpTransportIo* rdp_get_io_callbacks(rdpRdp* rdp)
{
if (!rdp)
return NULL;
return rdp->io;
}
BOOL rdp_set_io_callbacks(rdpRdp* rdp, const rdpTransportIo* io_callbacks)
{
if (!rdp)
return FALSE;
free(rdp->io);
rdp->io = NULL;
if (io_callbacks)
{
rdp->io = malloc(sizeof(rdpTransportIo));
if (!rdp->io)
return FALSE;
*rdp->io = *io_callbacks;
return transport_set_io_callbacks(rdp->transport, rdp->io);
}
return TRUE;
}
BOOL rdp_set_io_callback_context(rdpRdp* rdp, void* usercontext)
{
WINPR_ASSERT(rdp);
rdp->ioContext = usercontext;
return TRUE;
}
void* rdp_get_io_callback_context(rdpRdp* rdp)
{
WINPR_ASSERT(rdp);
return rdp->ioContext;
}
2022-06-23 10:59:24 +03:00
const char* rdp_finalize_flags_to_str(UINT32 flags, char* buffer, size_t size)
{
char number[32] = { 0 };
const UINT32 mask = ~(FINALIZE_SC_SYNCHRONIZE_PDU | FINALIZE_SC_CONTROL_COOPERATE_PDU |
FINALIZE_SC_CONTROL_GRANTED_PDU | FINALIZE_SC_FONT_MAP_PDU |
FINALIZE_CS_SYNCHRONIZE_PDU | FINALIZE_CS_CONTROL_COOPERATE_PDU |
FINALIZE_CS_CONTROL_REQUEST_PDU | FINALIZE_CS_PERSISTENT_KEY_LIST_PDU |
FINALIZE_CS_FONT_LIST_PDU | FINALIZE_DEACTIVATE_REACTIVATE);
if (flags & FINALIZE_SC_SYNCHRONIZE_PDU)
winpr_str_append("FINALIZE_SC_SYNCHRONIZE_PDU", buffer, size, "|");
if (flags & FINALIZE_SC_CONTROL_COOPERATE_PDU)
winpr_str_append("FINALIZE_SC_CONTROL_COOPERATE_PDU", buffer, size, "|");
if (flags & FINALIZE_SC_CONTROL_GRANTED_PDU)
winpr_str_append("FINALIZE_SC_CONTROL_GRANTED_PDU", buffer, size, "|");
if (flags & FINALIZE_SC_FONT_MAP_PDU)
winpr_str_append("FINALIZE_SC_FONT_MAP_PDU", buffer, size, "|");
if (flags & FINALIZE_CS_SYNCHRONIZE_PDU)
winpr_str_append("FINALIZE_CS_SYNCHRONIZE_PDU", buffer, size, "|");
if (flags & FINALIZE_CS_CONTROL_COOPERATE_PDU)
winpr_str_append("FINALIZE_CS_CONTROL_COOPERATE_PDU", buffer, size, "|");
if (flags & FINALIZE_CS_CONTROL_REQUEST_PDU)
winpr_str_append("FINALIZE_CS_CONTROL_REQUEST_PDU", buffer, size, "|");
if (flags & FINALIZE_CS_PERSISTENT_KEY_LIST_PDU)
winpr_str_append("FINALIZE_CS_PERSISTENT_KEY_LIST_PDU", buffer, size, "|");
if (flags & FINALIZE_CS_FONT_LIST_PDU)
winpr_str_append("FINALIZE_CS_FONT_LIST_PDU", buffer, size, "|");
if (flags & FINALIZE_DEACTIVATE_REACTIVATE)
winpr_str_append("FINALIZE_DEACTIVATE_REACTIVATE", buffer, size, "|");
if (flags & mask)
winpr_str_append("UNKNOWN_FLAG", buffer, size, "|");
if (flags == 0)
winpr_str_append("NO_FLAG_SET", buffer, size, "|");
_snprintf(number, sizeof(number), " [0x%04" PRIx16 "]", flags);
winpr_str_append(number, buffer, size, "|");
return buffer;
}
BOOL rdp_finalize_reset_flags(rdpRdp* rdp, BOOL clearAll)
{
WINPR_ASSERT(rdp);
WLog_DBG(TAG, "[%s] reset finalize_sc_pdus", rdp_get_state_string(rdp));
if (clearAll)
rdp->finalize_sc_pdus = 0;
else
rdp->finalize_sc_pdus &= FINALIZE_DEACTIVATE_REACTIVATE;
return TRUE;
}
BOOL rdp_finalize_set_flag(rdpRdp* rdp, UINT32 flag)
{
char buffer[1024] = { 0 };
WINPR_ASSERT(rdp);
WLog_DBG(TAG, "[%s] received flag %s", rdp_get_state_string(rdp),
rdp_finalize_flags_to_str(flag, buffer, sizeof(buffer)));
rdp->finalize_sc_pdus |= flag;
return TRUE;
}
BOOL rdp_finalize_is_flag_set(rdpRdp* rdp, UINT32 flag)
{
WINPR_ASSERT(rdp);
return (rdp->finalize_sc_pdus & flag) == flag;
}