FreeRDP/libfreerdp/core/rdp.c

2883 lines
73 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>
#include <freerdp/buildflags.h>
#define RDP_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 */
};
#define rdp_check_monitor_layout_pdu_state(rdp, expected) \
rdp_check_monitor_layout_pdu_state_(rdp, expected, __FILE__, __func__, __LINE__)
static BOOL rdp_check_monitor_layout_pdu_state_(const rdpRdp* rdp, BOOL expected, const char* file,
const char* fkt, size_t line)
{
WINPR_ASSERT(rdp);
if (expected != rdp->monitor_layout_pdu)
{
const DWORD log_level = WLOG_ERROR;
if (WLog_IsLevelActive(rdp->log, log_level))
{
WLog_PrintMessage(rdp->log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt,
"Expected rdp->monitor_layout_pdu == %s",
expected ? "TRUE" : "FALSE");
}
return FALSE;
}
return TRUE;
}
#define rdp_set_monitor_layout_pdu_state(rdp, expected) \
rdp_set_monitor_layout_pdu_state_(rdp, expected, __FILE__, __func__, __LINE__)
static BOOL rdp_set_monitor_layout_pdu_state_(rdpRdp* rdp, BOOL value, const char* file,
const char* fkt, size_t line)
{
WINPR_ASSERT(rdp);
if (value && (value == rdp->monitor_layout_pdu))
{
const DWORD log_level = WLOG_WARN;
if (WLog_IsLevelActive(rdp->log, log_level))
{
WLog_PrintMessage(rdp->log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt,
"rdp->monitor_layout_pdu == %s, expected FALSE",
value ? "TRUE" : "FALSE");
}
return FALSE;
}
rdp->monitor_layout_pdu = value;
return TRUE;
}
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(rdpRdp* rdp, wStream* s, UINT16* type, UINT16* channel_id);
static BOOL rdp_write_share_control_header(rdpRdp* rdp, wStream* s, UINT16 length, UINT16 type,
2019-11-20 13:30:14 +03:00
UINT16 channel_id);
static BOOL rdp_write_share_data_header(rdpRdp* rdp, wStream* s, UINT16 length, BYTE type,
UINT32 share_id);
2019-11-20 13:30:14 +03:00
/**
2022-12-09 16:35:03 +03:00
* @brief Read RDP Security Header.
* msdn{cc240579}
*
* @param s stream
* @param flags security flags
2022-12-09 16:35:03 +03:00
*
* @return \b TRUE for success, \b FALSE otherwise
*/
BOOL rdp_read_security_header(rdpRdp* rdp, wStream* s, UINT16* flags, UINT16* length)
{
char buffer[256] = { 0 };
WINPR_ASSERT(s);
WINPR_ASSERT(flags);
WINPR_ASSERT(rdp);
/* Basic Security Header */
if ((length && (*length < 4)))
{
WLog_Print(rdp->log, WLOG_WARN,
"invalid security header length, have %" PRIu16 ", must be >= 4", *length);
return FALSE;
}
if (!Stream_CheckAndLogRequiredLengthWLog(rdp->log, 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) */
WLog_Print(rdp->log, WLOG_TRACE, "%s",
rdp_security_flag_string(*flags, buffer, sizeof(buffer)));
if (length)
*length -= 4;
2013-01-12 03:43:16 +04:00
return TRUE;
}
/**
2022-12-09 16:35:03 +03:00
* Write RDP Security Header.
* msdn{cc240579}
* @param s stream
* @param flags security flags
2022-12-09 16:35:03 +03:00
*
* @return \b TRUE for success, \b FALSE otherwise
*/
BOOL rdp_write_security_header(rdpRdp* rdp, wStream* s, UINT16 flags)
{
char buffer[256] = { 0 };
WINPR_ASSERT(s);
WINPR_ASSERT(rdp);
if (!Stream_CheckAndLogRequiredCapacityWLog(rdp->log, (s), 4))
return FALSE;
WLog_Print(rdp->log, WLOG_TRACE, "%s", rdp_security_flag_string(flags, buffer, sizeof(buffer)));
/* 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(rdpRdp* rdp, wStream* s, UINT16* tpktLength,
UINT16* remainingLength, UINT16* type, UINT16* channel_id)
{
2023-01-23 12:18:50 +03:00
UINT16 len = 0;
UINT16 tmp = 0;
WINPR_ASSERT(rdp);
WINPR_ASSERT(s);
WINPR_ASSERT(type);
WINPR_ASSERT(channel_id);
if (!Stream_CheckAndLogRequiredLengthWLog(rdp->log, 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(rdp, 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;
char buffer[128] = { 0 };
WLog_Print(rdp->log, WLOG_DEBUG,
"[Flow control PDU] type=%s, tpktLength=%" PRIuz ", remainingLength=%" PRIuz,
pdu_type_to_str(*type, buffer, sizeof(buffer)), *tpktLength, *remainingLength);
return TRUE;
}
if (len < 4U)
{
WLog_Print(rdp->log, WLOG_ERROR,
"Invalid share control header, length is %" PRIu16 ", must be >4", len);
return FALSE;
}
2012-02-07 01:31:41 +04:00
if (tpktLength)
*tpktLength = len;
if (!Stream_CheckAndLogRequiredLengthWLog(rdp->log, s, 2))
return FALSE;
Stream_Read_UINT16(s, tmp); /* pduType */
*type = tmp & 0x0F; /* type is in the 4 least significant bits */
size_t remLen = len - 4;
if (len > 5)
{
if (!Stream_CheckAndLogRequiredLengthWLog(rdp->log, s, 2))
return FALSE;
2013-05-09 00:09:16 +04:00
Stream_Read_UINT16(s, *channel_id); /* pduSource */
remLen = len - 6;
}
else
*channel_id = 0; /* Windows XP can send such short DEACTIVATE_ALL PDUs. */
char buffer[128] = { 0 };
WLog_Print(rdp->log, WLOG_DEBUG, "type=%s, tpktLength=%" PRIuz ", remainingLength=%" PRIuz,
pdu_type_to_str(*type, buffer, sizeof(buffer)), len, remLen);
if (remainingLength)
*remainingLength = remLen;
return Stream_CheckAndLogRequiredLengthWLog(rdp->log, s, remLen);
}
BOOL rdp_write_share_control_header(rdpRdp* rdp, wStream* s, UINT16 length, UINT16 type,
UINT16 channel_id)
{
WINPR_ASSERT(s);
WINPR_ASSERT(rdp);
if (length < RDP_PACKET_HEADER_MAX_LENGTH)
return FALSE;
if (!Stream_CheckAndLogRequiredCapacityWLog(rdp->log, (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(rdpRdp* rdp, wStream* s, UINT16* length, BYTE* type,
UINT32* shareId, BYTE* compressedType, UINT16* compressedLength)
{
WINPR_ASSERT(s);
WINPR_ASSERT(rdp);
if (!Stream_CheckAndLogRequiredLengthWLog(rdp->log, 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(rdpRdp* rdp, 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);
WINPR_ASSERT(rdp);
if (length < headerLen)
return FALSE;
length -= headerLen;
if (!Stream_CheckAndLogRequiredCapacityWLog(rdp->log, (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_Print(rdp->log, WLOG_ERROR, "missing context=%p", 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;
}
/**
2022-12-09 16:35:03 +03:00
* Read an RDP packet header.
* @param rdp rdp module
* @param s stream
* @param length RDP packet length
2022-12-09 16:35:03 +03:00
* @param channelId channel id
*
* @return \b TRUE for success, \b FALSE otherwise
*/
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)
{
WLog_Print(rdp->log, WLOG_WARN, "Received X224_TPDU_DISCONNECT_REQUEST, terminating");
utils_abort_connect(rdp);
return TRUE;
}
WLog_Print(rdp->log, WLOG_WARN,
"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_Print(rdp->log, WLOG_WARN, "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_Print(rdp->log, WLOG_WARN, "TPDU invalid length, got %" PRIu16 ", expected at least 8",
*length);
return FALSE;
}
if (!Stream_CheckAndLogRequiredLengthWLog(rdp->log, s, *length - 8))
return FALSE;
if (MCSPDU == DomainMCSPDU_DisconnectProviderUltimatum)
{
int reason = 0;
2022-07-06 10:36:53 +03:00
TerminateEventArgs e = { 0 };
if (!mcs_recv_disconnect_provider_ultimatum(rdp->mcs, s, &reason))
return FALSE;
2023-06-26 11:17:42 +03:00
rdpContext* context = rdp->context;
WINPR_ASSERT(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_Print(rdp->log, WLOG_DEBUG, "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_CheckAndLogRequiredLengthWLog(rdp->log, 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_CheckAndLogRequiredLengthWLog(rdp->log, s, *length))
return FALSE;
return TRUE;
}
/**
2022-12-09 16:35:03 +03:00
* Write an RDP packet header.
* @param rdp rdp module
* @param s stream
* @param length RDP packet length
2022-12-09 16:35:03 +03:00
* @param channelId channel id
*
* @return \b TRUE for success, \b FALSE otherwise
*/
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
{
BOOL status;
WINPR_ASSERT(rdp);
sec_flags |= rdp->sec_flags;
*pad = 0;
2011-09-13 10:40:27 +04:00
if (sec_flags != 0)
{
if (!rdp_write_security_header(rdp, s, sec_flags))
return FALSE;
2011-09-13 10:40:27 +04:00
if (sec_flags & SEC_ENCRYPT)
{
2023-01-28 15:31:33 +03:00
BOOL res = FALSE;
if (!security_lock(rdp))
return FALSE;
if (rdp->settings->EncryptionMethods == ENCRYPTION_METHOD_FIPS)
2011-09-16 03:54:03 +04:00
{
BYTE* data = Stream_PointerAs(s, BYTE) + 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 (!Stream_CheckAndLogRequiredCapacityWLog(rdp->log, s, 8))
goto unlock;
if (!security_hmac_signature(data, length, Stream_Pointer(s), 8, rdp))
2023-01-28 15:31:33 +03:00
goto unlock;
Stream_Seek(s, 8);
2023-01-28 15:31:33 +03:00
if (!security_fips_encrypt(data, length + *pad, rdp))
goto unlock;
2011-09-16 03:54:03 +04:00
}
else
{
const BYTE* data = Stream_PointerAs(s, const BYTE) + 8;
length = length - (data - Stream_Buffer(s));
if (!Stream_CheckAndLogRequiredCapacityWLog(rdp->log, s, 8))
goto unlock;
if (sec_flags & SEC_SECURE_CHECKSUM)
status = security_salted_mac_signature(rdp, data, length, TRUE,
Stream_Pointer(s), 8);
else
status =
security_mac_signature(rdp, data, length, Stream_PointerAs(s, BYTE), 8);
if (!status)
2023-01-28 15:31:33 +03:00
goto unlock;
Stream_Seek(s, 8);
2015-04-07 22:06:53 +03:00
if (!security_encrypt(Stream_Pointer(s), length, rdp))
2023-01-28 15:31:33 +03:00
goto unlock;
2011-09-16 03:54:03 +04:00
}
2023-01-28 15:31:33 +03:00
res = TRUE;
unlock:
if (!security_unlock(rdp))
return FALSE;
if (!res)
return FALSE;
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);
if (!rdp_write_header(rdp, s, length, channel_id))
goto fail;
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);
if (!rdp_write_header(rdp, s, length, MCS_GLOBAL_CHANNEL_ID))
return FALSE;
sec_bytes = rdp_get_sec_bytes(rdp, 0);
sec_hold = Stream_GetPosition(s);
Stream_Seek(s, sec_bytes);
if (!rdp_write_share_control_header(rdp, 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);
if (!rdp_write_header(rdp, s, length, MCS_GLOBAL_CHANNEL_ID))
goto fail;
sec_bytes = rdp_get_sec_bytes(rdp, 0);
sec_hold = Stream_GetPosition(s);
Stream_Seek(s, sec_bytes);
if (!rdp_write_share_control_header(rdp, s, length - sec_bytes, PDU_TYPE_DATA, channel_id))
goto fail;
if (!rdp_write_share_data_header(rdp, 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);
WLog_Print(rdp->log, WLOG_DEBUG,
"sending data (type=0x%x size=%" PRIuz " channelId=%" PRIu16 ")", 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);
if (!rdp_write_header(rdp, s, length, rdp->mcs->messageChannelId))
goto fail;
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_CheckAndLogRequiredLengthWLog(rdp->log, 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_CheckAndLogRequiredLengthWLog(rdp->log, 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_CheckAndLogRequiredLengthWLog(rdp->log, 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_CheckAndLogRequiredLengthWLog(rdp->log, s, 4))
return FALSE;
Stream_Read_UINT32(s, arcStatus); /* arcStatus (4 bytes) */
WLog_Print(rdp->log, WLOG_WARN, "AutoReconnectStatus: 0x%08" PRIX32 "", arcStatus);
return TRUE;
}
static BOOL rdp_recv_server_status_info_pdu(rdpRdp* rdp, wStream* s)
{
UINT32 statusCode;
if (!Stream_CheckAndLogRequiredLengthWLog(rdp->log, 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;
WINPR_ASSERT(rdp);
if (!Stream_CheckAndLogRequiredLengthWLog(rdp->log, s, 4))
return FALSE;
Stream_Read_UINT32(s, monitorCount); /* monitorCount (4 bytes) */
if (!Stream_CheckAndLogRequiredLengthOfSizeWLog(rdp->log, s, monitorCount, 20ull))
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 rdp_set_monitor_layout_pdu_state(rdp, TRUE);
}
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
WINPR_ASSERT(rdp);
if (!rdp_read_share_data_header(rdp, s, &length, &type, &shareId, &compressedType,
2019-11-06 17:24:51 +03:00
&compressedLength))
2015-05-02 06:26:08 +03:00
{
WLog_Print(rdp->log, WLOG_ERROR, "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_CheckAndLogRequiredLengthWLog(rdp->log, s, SrcSize)))
{
WLog_Print(rdp->log, WLOG_ERROR,
"bulk_decompress: not enough bytes for compressedLength %" PRIu16 "",
compressedLength);
return STATE_RUN_FAILED;
}
if (bulk_decompress(rdp->bulk, Stream_ConstPointer(s), SrcSize, &pDstData, &DstSize,
2019-11-06 17:24:51 +03:00
compressedType))
{
2021-09-06 12:01:36 +03:00
cs = transport_take_from_pool(rdp->transport, DstSize);
if (!cs)
{
WLog_Print(rdp->log, WLOG_ERROR, "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_Print(rdp->log, WLOG_ERROR, "bulk_decompress() failed");
return STATE_RUN_FAILED;
}
Stream_Seek(s, SrcSize);
}
WLog_Print(rdp->log, WLOG_DEBUG, "recv %s Data PDU (0x%02" PRIX8 "), length: %" PRIu16 "",
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_Print(rdp->log, WLOG_ERROR, "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_Print(rdp->log, WLOG_ERROR,
"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_Print(rdp->log, WLOG_ERROR,
"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_Print(rdp->log, WLOG_ERROR,
"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_Print(rdp->log, WLOG_ERROR,
"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
{
WLog_Print(
rdp->log, WLOG_ERROR,
2019-11-06 17:24:51 +03:00
"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
{
WLog_Print(rdp->log, WLOG_ERROR,
"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_Print(rdp->log, WLOG_ERROR,
"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
{
WLog_Print(rdp->log, WLOG_ERROR,
"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
{
WLog_Print(rdp->log, WLOG_ERROR,
"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
{
WLog_Print(
rdp->log, WLOG_ERROR,
2019-11-06 17:24:51 +03:00
"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
{
WLog_Print(rdp->log, WLOG_ERROR,
"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
{
WLog_Print(rdp->log, WLOG_ERROR,
"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
{
WLog_Print(rdp->log, WLOG_ERROR,
"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_Print(rdp->log, WLOG_WARN,
"[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)
{
return multitransport_recv_request(rdp->multitransport, s);
}
if (securityFlags & SEC_TRANSPORT_RSP)
{
return multitransport_recv_response(rdp->multitransport, s);
}
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;
WINPR_ASSERT(rdp);
if (!rdp_read_share_control_header(rdp, 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(rdpRdp* rdp, 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(rdp);
WINPR_ASSERT(s);
WINPR_ASSERT(type);
WINPR_ASSERT(channel_id);
if (!Stream_CheckAndLogRequiredLengthWLog(rdp->log, 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
/**
2022-12-09 16:35:03 +03:00
* Decrypt an RDP packet.
*
2011-09-05 22:02:52 +04:00
* @param rdp RDP module
* @param s stream
2022-12-09 16:35:03 +03:00
* @param pLength A pointer to the result variable, must not be NULL
* @param securityFlags the security flags to apply
*
* @return \b TRUE for success, \b FALSE otherwise
2011-09-05 22:02:52 +04:00
*/
BOOL rdp_decrypt(rdpRdp* rdp, wStream* s, UINT16* pLength, UINT16 securityFlags)
2011-09-05 22:02:52 +04:00
{
2023-01-28 15:23:13 +03:00
BOOL res = FALSE;
BYTE cmac[8] = { 0 };
BYTE wmac[8] = { 0 };
BOOL status = FALSE;
INT32 length = 0;
2011-09-15 01:14:50 +04:00
WINPR_ASSERT(rdp);
WINPR_ASSERT(rdp->settings);
WINPR_ASSERT(s);
WINPR_ASSERT(pLength);
2023-01-28 15:31:33 +03:00
if (!security_lock(rdp))
return FALSE;
length = *pLength;
if (rdp->settings->EncryptionMethods == ENCRYPTION_METHOD_NONE)
return TRUE;
if (rdp->settings->EncryptionMethods == ENCRYPTION_METHOD_FIPS)
2011-09-16 03:54:03 +04:00
{
if (!Stream_CheckAndLogRequiredLengthWLog(rdp->log, s, 12))
2023-01-28 15:23:13 +03:00
goto unlock;
UINT16 len = 0;
2019-11-06 17:24:51 +03:00
Stream_Read_UINT16(s, len); /* 0x10 */
if (len != 0x10)
WLog_Print(rdp->log, WLOG_WARN, "ENCRYPTION_METHOD_FIPS length %" PRIu16 " != 0x10",
len);
UINT16 version = 0;
2013-05-09 00:09:16 +04:00
Stream_Read_UINT8(s, version); /* 0x1 */
if (version != 1)
WLog_Print(rdp->log, WLOG_WARN, "ENCRYPTION_METHOD_FIPS version %" PRIu16 " != 1",
version);
BYTE pad = 0;
2013-05-09 00:09:16 +04:00
Stream_Read_UINT8(s, pad);
const BYTE* sig = Stream_ConstPointer(s);
2019-11-06 17:24:51 +03:00
Stream_Seek(s, 8); /* signature */
length -= 12;
const INT32 padLength = length - pad;
if ((length <= 0) || (padLength <= 0) || (padLength > UINT16_MAX))
{
WLog_Print(rdp->log, WLOG_ERROR, "FATAL: invalid pad length %" PRId32, padLength);
2023-01-28 15:23:13 +03:00
goto unlock;
}
2011-09-16 03:54:03 +04:00
if (!security_fips_decrypt(Stream_Pointer(s), length, rdp))
2023-01-28 15:23:13 +03:00
goto unlock;
2011-09-16 03:54:03 +04:00
if (!security_fips_check_signature(Stream_ConstPointer(s), length - pad, sig, 8, rdp))
2023-01-28 15:23:13 +03:00
goto unlock;
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;
2011-09-16 03:54:03 +04:00
}
2023-01-28 15:23:13 +03:00
else
{
if (!Stream_CheckAndLogRequiredLengthWLog(rdp->log, s, sizeof(wmac)))
2023-01-28 15:23:13 +03:00
goto unlock;
2011-09-16 03:54:03 +04:00
2023-01-28 15:23:13 +03:00
Stream_Read(s, wmac, sizeof(wmac));
length -= sizeof(wmac);
2013-01-12 03:43:16 +04:00
2023-01-28 15:23:13 +03:00
if (length <= 0)
{
WLog_Print(rdp->log, WLOG_ERROR, "FATAL: invalid length field");
2023-01-28 15:23:13 +03:00
goto unlock;
}
if (!security_decrypt(Stream_PointerAs(s, BYTE), length, rdp))
2023-01-28 15:23:13 +03:00
goto unlock;
2023-01-28 15:23:13 +03:00
if (securityFlags & SEC_SECURE_CHECKSUM)
status = security_salted_mac_signature(rdp, Stream_ConstPointer(s), length, FALSE, cmac,
sizeof(cmac));
2023-01-28 15:23:13 +03:00
else
status =
security_mac_signature(rdp, Stream_ConstPointer(s), length, cmac, sizeof(cmac));
2023-01-28 15:23:13 +03:00
if (!status)
goto unlock;
2023-01-28 15:23:13 +03:00
if (memcmp(wmac, cmac, sizeof(wmac)) != 0)
{
WLog_Print(rdp->log, WLOG_ERROR, "WARNING: invalid packet signature");
2023-01-28 15:23:13 +03:00
/*
* 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.
*/
// return FALSE;
}
2023-01-28 15:23:13 +03:00
*pLength = length;
}
2023-01-28 15:23:13 +03:00
res = TRUE;
unlock:
2023-01-28 15:31:33 +03:00
if (!security_unlock(rdp))
return FALSE;
2023-01-28 15:23:13 +03:00
return res;
2011-09-05 22:02:52 +04:00
}
2023-01-23 12:18:50 +03:00
const char* pdu_type_to_str(UINT16 pduType, char* buffer, size_t length)
{
2023-01-23 12:18:50 +03:00
const char* str;
switch (pduType)
{
case PDU_TYPE_DEMAND_ACTIVE:
2023-01-23 12:18:50 +03:00
str = "PDU_TYPE_DEMAND_ACTIVE";
break;
case PDU_TYPE_CONFIRM_ACTIVE:
2023-01-23 12:18:50 +03:00
str = "PDU_TYPE_CONFIRM_ACTIVE";
break;
case PDU_TYPE_DEACTIVATE_ALL:
2023-01-23 12:18:50 +03:00
str = "PDU_TYPE_DEACTIVATE_ALL";
break;
case PDU_TYPE_DATA:
2023-01-23 12:18:50 +03:00
str = "PDU_TYPE_DATA";
break;
case PDU_TYPE_SERVER_REDIRECTION:
2023-01-23 12:18:50 +03:00
str = "PDU_TYPE_SERVER_REDIRECTION";
break;
case PDU_TYPE_FLOW_TEST:
2023-01-23 12:18:50 +03:00
str = "PDU_TYPE_FLOW_TEST";
break;
case PDU_TYPE_FLOW_RESPONSE:
2023-01-23 12:18:50 +03:00
str = "PDU_TYPE_FLOW_RESPONSE";
break;
case PDU_TYPE_FLOW_STOP:
2023-01-23 12:18:50 +03:00
str = "PDU_TYPE_FLOW_STOP";
break;
default:
2023-06-12 11:43:49 +03:00
str = "PDU_TYPE_UNKNOWN";
2023-01-23 12:18:50 +03:00
break;
}
winpr_str_append(str, buffer, length, "");
{
char msg[32] = { 0 };
_snprintf(msg, sizeof(msg), "[0x%08" PRIx32 "]", pduType);
winpr_str_append(msg, buffer, length, "");
}
2023-01-23 12:18:50 +03:00
return buffer;
}
/**
2022-12-09 16:35:03 +03:00
* Process an RDP packet.
* @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(rdp, s, &securityFlags, &length))
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))
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(rdp, s, NULL, &remain, &pduType, &pduSource))
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_Print(rdp->log, WLOG_ERROR,
"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:
WLog_Print(rdp->log, WLOG_DEBUG, "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:
2023-01-23 12:18:50 +03:00
{
char buffer[256] = { 0 };
WLog_Print(rdp->log, WLOG_ERROR, "incorrect PDU type: %s",
pdu_type_to_str(pduType, buffer, sizeof(buffer)));
2023-01-23 12:18:50 +03:00
}
break;
}
diff = Stream_GetRemainingLength(sub);
if (diff > 0)
{
2023-01-23 12:18:50 +03:00
char buffer[256] = { 0 };
WLog_Print(rdp->log, WLOG_WARN,
"pduType %s not properly parsed, %" PRIdz
" bytes remaining unhandled. Skipping.",
pdu_type_to_str(pduType, buffer, sizeof(buffer)), diff);
}
}
}
else if (rdp->mcs->messageChannelId && (channelId == rdp->mcs->messageChannelId))
{
if (!rdp->settings->UseRdpSecurityLayer)
if (!rdp_read_security_header(rdp, 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 = 0;
WINPR_ASSERT(rdp);
rdpFastPath* 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_Print(rdp->log, WLOG_ERROR, "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_CheckAndLogRequiredLengthWLog(rdp->log, s, length)))
{
WLog_Print(rdp->log, WLOG_ERROR, "incorrect FastPath PDU header length %" PRIu16 "",
length);
return STATE_RUN_FAILED;
}
if (rdp->autodetect->bandwidthMeasureStarted)
{
rdp->autodetect->bandwidthMeasureByteCount += length;
}
if (!fastpath_decrypt(fastpath, s, &length))
return STATE_RUN_FAILED;
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_handle_sc_flags(rdpRdp* rdp, wStream* s, UINT32 flag,
CONNECTION_STATE nextState)
{
const UINT32 mask = FINALIZE_SC_SYNCHRONIZE_PDU | FINALIZE_SC_CONTROL_COOPERATE_PDU |
FINALIZE_SC_CONTROL_GRANTED_PDU | FINALIZE_SC_FONT_MAP_PDU;
WINPR_ASSERT(rdp);
state_run_t status = rdp_recv_pdu(rdp, s);
if (state_run_success(status))
{
const UINT32 flags = rdp->finalize_sc_pdus & mask;
if ((flags & flag) == flag)
{
if (!rdp_client_transition_to_state(rdp, nextState))
status = STATE_RUN_FAILED;
else
status = STATE_RUN_SUCCESS;
}
else
{
char flag_buffer[256] = { 0 };
char mask_buffer[256] = { 0 };
WLog_Print(rdp->log, WLOG_WARN,
"[%s] unexpected server message, expected flag %s [have %s]",
rdp_get_state_string(rdp),
rdp_finalize_flags_to_str(flag, flag_buffer, sizeof(flag_buffer)),
rdp_finalize_flags_to_str(flags, mask_buffer, sizeof(mask_buffer)));
}
}
return status;
}
static state_run_t rdp_client_exchange_monitor_layout(rdpRdp* rdp, wStream* s)
{
WINPR_ASSERT(rdp);
if (!rdp_check_monitor_layout_pdu_state(rdp, FALSE))
return FALSE;
/* We might receive unrelated messages from the server (channel traffic),
* so only proceed if some flag changed
*/
const UINT32 old = rdp->finalize_sc_pdus;
state_run_t status = rdp_recv_pdu(rdp, s);
const UINT32 now = rdp->finalize_sc_pdus;
const BOOL changed = (old != now) || rdp->monitor_layout_pdu;
/* This PDU is optional, so if we received a finalize PDU continue there */
if (state_run_success(status) && changed)
{
if (!rdp->monitor_layout_pdu)
{
if (!rdp_finalize_is_flag_set(rdp, FINALIZE_SC_SYNCHRONIZE_PDU))
return STATE_RUN_FAILED;
}
status = rdp_client_connect_finalize(rdp);
if (state_run_success(status) && !rdp->monitor_layout_pdu)
status = STATE_RUN_TRY_AGAIN;
}
return status;
}
static state_run_t rdp_recv_callback_int(rdpTransport* transport, wStream* s, void* extra)
{
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:
if (!rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CREATE_REQUEST))
status = STATE_RUN_FAILED;
else
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_Print(rdp->log, WLOG_ERROR, "%s - nla_recv_pdu() fail",
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_Print(rdp->log, WLOG_ERROR, "%s - nego_recv() fail",
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
{
if (!rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CREATE_REQUEST))
status = STATE_RUN_FAILED;
else
status = STATE_RUN_CONTINUE;
}
2015-02-16 00:04:59 +03:00
}
break;
case CONNECTION_STATE_AAD:
if (aad_recv(rdp->aad, s) < 1)
{
WLog_Print(rdp->log, WLOG_ERROR, "%s - aad_recv() fail", rdp_get_state_string(rdp));
status = STATE_RUN_FAILED;
}
if (state_run_success(status))
{
if (aad_get_state(rdp->aad) == AAD_STATE_FINAL)
{
transport_set_aad_mode(rdp->transport, FALSE);
if (!rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CREATE_REQUEST))
status = STATE_RUN_FAILED;
else
status = STATE_RUN_CONTINUE;
}
}
break;
case CONNECTION_STATE_MCS_CREATE_REQUEST:
if (!mcs_client_begin(rdp->mcs))
{
WLog_Print(rdp->log, WLOG_ERROR, "%s - mcs_client_begin() fail",
rdp_get_state_string(rdp));
status = STATE_RUN_FAILED;
}
else if (!rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CREATE_RESPONSE))
status = STATE_RUN_FAILED;
else if (Stream_GetRemainingLength(s) > 0)
status = STATE_RUN_CONTINUE;
break;
case CONNECTION_STATE_MCS_CREATE_RESPONSE:
if (!mcs_recv_connect_response(rdp->mcs, s))
{
WLog_Print(rdp->log, WLOG_ERROR, "mcs_recv_connect_response failure");
status = STATE_RUN_FAILED;
}
else
{
if (!rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_ERECT_DOMAIN))
status = STATE_RUN_FAILED;
else if (!mcs_send_erect_domain_request(rdp->mcs))
{
WLog_Print(rdp->log, WLOG_ERROR, "mcs_send_erect_domain_request failure");
status = STATE_RUN_FAILED;
}
else if (!rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_ATTACH_USER))
status = STATE_RUN_FAILED;
else if (!mcs_send_attach_user_request(rdp->mcs))
{
WLog_Print(rdp->log, WLOG_ERROR, "mcs_send_attach_user_request failure");
status = STATE_RUN_FAILED;
}
else if (!rdp_client_transition_to_state(rdp,
CONNECTION_STATE_MCS_ATTACH_USER_CONFIRM))
status = STATE_RUN_FAILED;
}
break;
case CONNECTION_STATE_MCS_ATTACH_USER_CONFIRM:
if (!mcs_recv_attach_user_confirm(rdp->mcs, s))
{
WLog_Print(rdp->log, WLOG_ERROR, "mcs_recv_attach_user_confirm failure");
status = STATE_RUN_FAILED;
}
2023-01-20 11:03:16 +03:00
else if (!freerdp_settings_get_bool(rdp->settings, FreeRDP_SupportSkipChannelJoin))
{
2023-01-20 11:03:16 +03:00
if (!rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_REQUEST))
status = STATE_RUN_FAILED;
else if (!mcs_send_channel_join_request(rdp->mcs, rdp->mcs->userId))
{
WLog_Print(rdp->log, WLOG_ERROR, "mcs_send_channel_join_request failure");
2023-01-20 11:03:16 +03:00
status = STATE_RUN_FAILED;
}
else if (!rdp_client_transition_to_state(
rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_RESPONSE))
status = STATE_RUN_FAILED;
}
else
{
/* SKIP_CHANNELJOIN is active, consider channels to be joined */
if (!rdp_client_skip_mcs_channel_join(rdp))
status = STATE_RUN_FAILED;
}
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_Print(rdp->log, WLOG_ERROR,
"%s - "
"rdp_client_connect_mcs_channel_join_confirm() fail",
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))
{
if (!rdp_client_transition_to_state(rdp, CONNECTION_STATE_LICENSING))
status = STATE_RUN_FAILED;
else
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_Print(rdp->log, WLOG_DEBUG, "%s - rdp_client_connect_license() - %s",
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 };
WLog_Print(rdp->log, WLOG_DEBUG,
"%s - "
"rdp_client_connect_demand_active() - %s",
rdp_get_state_string(rdp),
state_run_result_string(status, buffer, ARRAYSIZE(buffer)));
}
else if (status != STATE_RUN_REDIRECT)
{
if (!rdp_client_transition_to_state(
rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_CONFIRM_ACTIVE))
status = STATE_RUN_FAILED;
else
status = STATE_RUN_CONTINUE;
}
break;
case CONNECTION_STATE_CAPABILITIES_EXCHANGE_MONITOR_LAYOUT:
status = rdp_client_exchange_monitor_layout(rdp, s);
break;
case CONNECTION_STATE_CAPABILITIES_EXCHANGE_CONFIRM_ACTIVE:
status = rdp_client_connect_confirm_active(rdp, s);
break;
case CONNECTION_STATE_FINALIZATION_CLIENT_SYNC:
status = rdp_handle_sc_flags(rdp, s, FINALIZE_SC_SYNCHRONIZE_PDU,
CONNECTION_STATE_FINALIZATION_CLIENT_COOPERATE);
break;
case CONNECTION_STATE_FINALIZATION_CLIENT_COOPERATE:
status = rdp_handle_sc_flags(rdp, s, FINALIZE_SC_CONTROL_COOPERATE_PDU,
CONNECTION_STATE_FINALIZATION_CLIENT_GRANTED_CONTROL);
break;
case CONNECTION_STATE_FINALIZATION_CLIENT_GRANTED_CONTROL:
status = rdp_handle_sc_flags(rdp, s, FINALIZE_SC_CONTROL_GRANTED_PDU,
CONNECTION_STATE_FINALIZATION_CLIENT_FONT_MAP);
break;
case CONNECTION_STATE_FINALIZATION_CLIENT_FONT_MAP:
status = rdp_handle_sc_flags(rdp, s, FINALIZE_SC_FONT_MAP_PDU, CONNECTION_STATE_ACTIVE);
break;
case CONNECTION_STATE_ACTIVE:
status = rdp_recv_pdu(rdp, s);
if (state_run_failed(status))
{
char buffer[64] = { 0 };
WLog_Print(rdp->log, WLOG_DEBUG, "%s - rdp_recv_pdu() - %s",
rdp_get_state_string(rdp),
state_run_result_string(status, buffer, ARRAYSIZE(buffer)));
}
break;
default:
WLog_Print(rdp->log, WLOG_ERROR, "%s state %d", rdp_get_state_string(rdp),
rdp_get_state(rdp));
status = STATE_RUN_FAILED;
break;
}
if (state_run_failed(status))
{
char buffer[64] = { 0 };
WLog_Print(rdp->log, WLOG_ERROR, "%s status %s", 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;
WINPR_ASSERT(rdp);
if (rc == STATE_RUN_TRY_AGAIN)
Stream_SetPosition(s, start);
const char* old = rdp_get_state_string(rdp);
const size_t orem = Stream_GetRemainingLength(s);
rc = rdp_recv_callback_int(transport, s, extra);
const char* now = rdp_get_state_string(rdp);
const size_t rem = Stream_GetRemainingLength(s);
WLog_Print(rdp->log, WLOG_TRACE,
"(client)[%s -> %s] current return %s [feeding %" PRIuz " bytes, %" PRIuz
" bytes not processed]",
old, now, state_run_result_string(rc, buffer, sizeof(buffer)), orem, rem);
} 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
{
WLog_Print(rdp->log, WLOG_ERROR, "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_Print(rdp->log, WLOG_DEBUG, "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 = 0;
DWORD remoteFlags = 0;
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;
rdp->log = WLog_Get(RDP_TAG);
WINPR_ASSERT(rdp->log);
2023-07-06 09:42:31 +03:00
_snprintf(rdp->log_context, sizeof(rdp->log_context), "%p", context);
WLog_SetContext(rdp->log, NULL, rdp->log_context);
InitializeCriticalSection(&rdp->critical);
2014-04-10 23:07:53 +04:00
rdp->context = context;
2023-06-26 11:17:42 +03:00
WINPR_ASSERT(rdp->context);
2014-04-10 23:07:53 +04:00
if (context->ServerMode)
flags |= FREERDP_SETTINGS_SERVER_MODE;
else
remoteFlags |= 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 */
2023-01-17 14:39:29 +03:00
if (!rdp_set_backup_settings(rdp))
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;
}
rdp->aad = aad_new(context, rdp->transport);
if (!rdp->aad)
goto fail;
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);
rdp_free_rc4_decrypt_keys(rdp);
rdp_free_rc4_encrypt_keys(rdp);
2021-09-17 10:25:30 +03:00
winpr_Cipher_Free(rdp->fips_encrypt);
winpr_Cipher_Free(rdp->fips_decrypt);
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
}
aad_free(rdp->aad);
rdp->aad = aad_new(context, rdp->transport);
if (!rdp->aad)
goto fail;
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);
freerdp_settings_free(rdp->remoteSettings);
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);
aad_free(rdp->aad);
free(rdp);
}
}
BOOL rdp_io_callback_set_event(rdpRdp* rdp, BOOL set)
{
if (!rdp)
return FALSE;
return transport_io_callback_set_event(rdp->transport, set);
}
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, "|");
2023-03-14 12:51:38 +03:00
_snprintf(number, sizeof(number), " [0x%08" PRIx32 "]", flags);
winpr_str_append(number, buffer, size, "");
2022-06-23 10:59:24 +03:00
return buffer;
}
BOOL rdp_finalize_reset_flags(rdpRdp* rdp, BOOL clearAll)
{
WINPR_ASSERT(rdp);
WLog_Print(rdp->log, WLOG_DEBUG, "[%s] reset finalize_sc_pdus", rdp_get_state_string(rdp));
2022-06-23 10:59:24 +03:00
if (clearAll)
rdp->finalize_sc_pdus = 0;
else
rdp->finalize_sc_pdus &= FINALIZE_DEACTIVATE_REACTIVATE;
return rdp_set_monitor_layout_pdu_state(rdp, FALSE);
2022-06-23 10:59:24 +03:00
}
BOOL rdp_finalize_set_flag(rdpRdp* rdp, UINT32 flag)
{
char buffer[1024] = { 0 };
WINPR_ASSERT(rdp);
WLog_Print(rdp->log, WLOG_DEBUG, "[%s] received flag %s", rdp_get_state_string(rdp),
rdp_finalize_flags_to_str(flag, buffer, sizeof(buffer)));
2022-06-23 10:59:24 +03:00
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;
}
BOOL rdp_reset_rc4_encrypt_keys(rdpRdp* rdp)
{
WINPR_ASSERT(rdp);
rdp_free_rc4_encrypt_keys(rdp);
rdp->rc4_encrypt_key = winpr_RC4_New(rdp->encrypt_key, rdp->rc4_key_len);
rdp->encrypt_use_count = 0;
return rdp->rc4_encrypt_key != NULL;
}
void rdp_free_rc4_encrypt_keys(rdpRdp* rdp)
{
WINPR_ASSERT(rdp);
winpr_RC4_Free(rdp->rc4_encrypt_key);
rdp->rc4_encrypt_key = NULL;
}
void rdp_free_rc4_decrypt_keys(rdpRdp* rdp)
{
WINPR_ASSERT(rdp);
winpr_RC4_Free(rdp->rc4_decrypt_key);
rdp->rc4_decrypt_key = NULL;
}
BOOL rdp_reset_rc4_decrypt_keys(rdpRdp* rdp)
{
WINPR_ASSERT(rdp);
rdp_free_rc4_decrypt_keys(rdp);
rdp->rc4_decrypt_key = winpr_RC4_New(rdp->decrypt_key, rdp->rc4_key_len);
rdp->decrypt_use_count = 0;
return rdp->rc4_decrypt_key != NULL;
}
const char* rdp_security_flag_string(UINT32 securityFlags, char* buffer, size_t size)
{
if (securityFlags & SEC_EXCHANGE_PKT)
winpr_str_append("SEC_EXCHANGE_PKT", buffer, size, "|");
if (securityFlags & SEC_TRANSPORT_REQ)
winpr_str_append("SEC_TRANSPORT_REQ", buffer, size, "|");
if (securityFlags & SEC_TRANSPORT_RSP)
winpr_str_append("SEC_TRANSPORT_RSP", buffer, size, "|");
if (securityFlags & SEC_ENCRYPT)
winpr_str_append("SEC_ENCRYPT", buffer, size, "|");
if (securityFlags & SEC_RESET_SEQNO)
winpr_str_append("SEC_RESET_SEQNO", buffer, size, "|");
if (securityFlags & SEC_IGNORE_SEQNO)
winpr_str_append("SEC_IGNORE_SEQNO", buffer, size, "|");
if (securityFlags & SEC_INFO_PKT)
winpr_str_append("SEC_INFO_PKT", buffer, size, "|");
if (securityFlags & SEC_LICENSE_PKT)
winpr_str_append("SEC_LICENSE_PKT", buffer, size, "|");
if (securityFlags & SEC_LICENSE_ENCRYPT_CS)
winpr_str_append("SEC_LICENSE_ENCRYPT_CS", buffer, size, "|");
if (securityFlags & SEC_LICENSE_ENCRYPT_SC)
winpr_str_append("SEC_LICENSE_ENCRYPT_SC", buffer, size, "|");
if (securityFlags & SEC_REDIRECTION_PKT)
winpr_str_append("SEC_REDIRECTION_PKT", buffer, size, "|");
if (securityFlags & SEC_SECURE_CHECKSUM)
winpr_str_append("SEC_SECURE_CHECKSUM", buffer, size, "|");
if (securityFlags & SEC_AUTODETECT_REQ)
winpr_str_append("SEC_AUTODETECT_REQ", buffer, size, "|");
if (securityFlags & SEC_AUTODETECT_RSP)
winpr_str_append("SEC_AUTODETECT_RSP", buffer, size, "|");
if (securityFlags & SEC_HEARTBEAT)
winpr_str_append("SEC_HEARTBEAT", buffer, size, "|");
if (securityFlags & SEC_FLAGSHI_VALID)
winpr_str_append("SEC_FLAGSHI_VALID", buffer, size, "|");
{
char msg[32] = { 0 };
_snprintf(msg, sizeof(msg), "[0x%08" PRIx32 "]", securityFlags);
winpr_str_append(msg, buffer, size, "");
}
return buffer;
}
2023-01-17 14:39:29 +03:00
static BOOL rdp_reset_remote_settings(rdpRdp* rdp)
{
UINT32 flags = 0;
WINPR_ASSERT(rdp);
freerdp_settings_free(rdp->remoteSettings);
if (!freerdp_settings_get_bool(rdp->settings, FreeRDP_ServerMode))
flags |= FREERDP_SETTINGS_SERVER_MODE;
rdp->remoteSettings = freerdp_settings_new(flags);
return rdp->remoteSettings != NULL;
}
BOOL rdp_set_backup_settings(rdpRdp* rdp)
{
WINPR_ASSERT(rdp);
freerdp_settings_free(rdp->originalSettings);
rdp->originalSettings = freerdp_settings_clone(rdp->settings);
if (!rdp->originalSettings)
return FALSE;
return rdp_reset_remote_settings(rdp);
}
BOOL rdp_reset_runtime_settings(rdpRdp* rdp)
{
WINPR_ASSERT(rdp);
2023-06-26 11:17:42 +03:00
WINPR_ASSERT(rdp->context);
2023-01-17 14:39:29 +03:00
freerdp_settings_free(rdp->settings);
rdp->context->settings = rdp->settings = freerdp_settings_clone(rdp->originalSettings);
if (!rdp->settings)
return FALSE;
return rdp_reset_remote_settings(rdp);
}
static BOOL starts_with(const char* tok, const char* val)
{
const size_t len = strlen(val);
if (strncmp(tok, val, len) != 0)
return FALSE;
if (tok[len] != '=')
return FALSE;
return TRUE;
}
static BOOL option_equals(const char* what, const char* val)
{
return _stricmp(what, val) == 0;
}
static BOOL parse_on_off_option(const char* value)
{
WINPR_ASSERT(value);
const char* sep = strchr(value, '=');
if (!sep)
return TRUE;
if (option_equals("on", &sep[1]))
return TRUE;
if (option_equals("true", &sep[1]))
return TRUE;
if (option_equals("off", &sep[1]))
return FALSE;
if (option_equals("false", &sep[1]))
return FALSE;
errno = 0;
long val = strtol(value, NULL, 0);
if (errno == 0)
return val == 0 ? FALSE : TRUE;
return FALSE;
}
#define STR(x) #x
static BOOL option_is_experimental(wLog* log, const char* tok)
{
const char* experimental[] = { STR(WITH_DSP_EXPERIMENTAL), STR(WITH_VAAPI) };
for (size_t x = 0; x < ARRAYSIZE(experimental); x++)
{
const char* opt = experimental[x];
if (starts_with(tok, opt))
{
return parse_on_off_option(tok);
}
}
return FALSE;
}
static BOOL option_is_debug(wLog* log, const char* tok)
{
WINPR_ASSERT(log);
const char* debug[] = { STR(WITH_DEBUG_ALL),
STR(WITH_DEBUG_CERTIFICATE),
STR(WITH_DEBUG_CAPABILITIES),
STR(WITH_DEBUG_CHANNELS),
STR(WITH_DEBUG_CLIPRDR),
STR(WITH_DEBUG_CODECS),
STR(WITH_DEBUG_RDPGFX),
STR(WITH_DEBUG_DVC),
STR(WITH_DEBUG_TSMF),
STR(WITH_DEBUG_KBD),
STR(WITH_DEBUG_LICENSE),
STR(WITH_DEBUG_NEGO),
STR(WITH_DEBUG_NLA),
STR(WITH_DEBUG_TSG),
STR(WITH_DEBUG_RAIL),
STR(WITH_DEBUG_RDP),
STR(WITH_DEBUG_RDPEI),
STR(WITH_DEBUG_REDIR),
STR(WITH_DEBUG_RDPDR),
STR(WITH_DEBUG_RFX),
STR(WITH_DEBUG_SCARD),
STR(WITH_DEBUG_SND),
STR(WITH_DEBUG_SVC),
STR(WITH_DEBUG_TRANSPORT),
STR(WITH_DEBUG_TIMEZONE),
STR(WITH_DEBUG_WND),
STR(WITH_DEBUG_X11_CLIPRDR),
STR(WITH_DEBUG_X11_LOCAL_MOVESIZE),
STR(WITH_DEBUG_X11),
STR(WITH_DEBUG_XV),
STR(WITH_DEBUG_RINGBUFFER),
STR(WITH_DEBUG_SYMBOLS),
STR(WITH_DEBUG_EVENTS),
STR(WITH_DEBUG_MUTEX),
STR(WITH_DEBUG_NTLM),
STR(WITH_DEBUG_SDL_EVENTS),
STR(WITH_DEBUG_SDL_KBD_EVENTS),
STR(WITH_DEBUG_THREADS),
STR(WITH_DEBUG_URBDRC) };
for (size_t x = 0; x < ARRAYSIZE(debug); x++)
{
const char* opt = debug[x];
if (starts_with(tok, opt))
return parse_on_off_option(tok);
}
if (starts_with(tok, "WITH_DEBUG"))
{
WLog_Print(log, WLOG_WARN, "[BUG] Unmapped Debug-Build option '%s'.", tok);
return parse_on_off_option(tok);
}
return FALSE;
}
static void log_build_warn(rdpRdp* rdp, const char* what, const char* msg,
BOOL (*cmp)(wLog* log, const char* tok))
{
WINPR_ASSERT(rdp);
size_t len = sizeof(FREERDP_BUILD_CONFIG);
char* list = calloc(len, sizeof(char));
char* config = _strdup(FREERDP_BUILD_CONFIG);
if (config && list)
{
char* tok = strtok(config, " ");
while (tok)
{
if (cmp(rdp->log, tok))
winpr_str_append(tok, list, len, " ");
tok = strtok(NULL, " ");
}
}
free(config);
if (list)
{
if (strlen(list) > 0)
{
WLog_Print(rdp->log, WLOG_WARN, "*************************************************");
WLog_Print(rdp->log, WLOG_WARN, "This build is using [%s] build options:", what);
char* tok = strtok(list, " ");
while (tok)
{
WLog_Print(rdp->log, WLOG_WARN, "* '%s'", tok);
tok = strtok(NULL, " ");
}
WLog_Print(rdp->log, WLOG_WARN, "");
WLog_Print(rdp->log, WLOG_WARN, "[%s] build options %s", what, msg);
WLog_Print(rdp->log, WLOG_WARN, "*************************************************");
}
}
free(list);
}
void rdp_log_build_warnings(rdpRdp* rdp)
{
static unsigned count = 0;
WINPR_ASSERT(rdp);
/* Since this function is called in context creation routines stop logging
* this issue repetedly. This is required for proxy, which would otherwise
* spam the log with these. */
if (count > 0)
return;
count++;
log_build_warn(rdp, "experimental", "might crash the application", option_is_experimental);
log_build_warn(rdp, "debug", "might leak sensitive information (credentials, ...)",
option_is_debug);
}