FreeRDP/libfreerdp/core/rdp.c

1714 lines
40 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <winpr/crt.h>
#include "rdp.h"
#include "info.h"
#include "redirection.h"
#include <freerdp/crypto/per.h>
#include <freerdp/log.h>
#define TAG FREERDP_TAG("core.rdp")
const char* DATA_PDU_TYPE_STRINGS[80] =
{
"?", "?", /* 0x00 - 0x01 */
"Update", /* 0x02 */
"?", "?", "?", "?", "?", "?", "?", "?", /* 0x03 - 0x0A */
"?", "?", "?", "?", "?", "?", "?", "?", "?", /* 0x0B - 0x13 */
"Control", /* 0x14 */
"?", "?", "?", "?", "?", "?", /* 0x15 - 0x1A */
"Pointer", /* 0x1B */
"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 */
"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 */
2016-03-17 12:44:11 +03:00
"Monitor Layout", /* 0x37 */
"FrameAcknowledge", "?", "?", /* 0x38 - 0x40 */
"?", "?", "?", "?", "?", "?" /* 0x41 - 0x46 */
};
/**
* Read RDP Security Header.\n
* @msdn{cc240579}
* @param s stream
* @param flags security flags
*/
BOOL rdp_read_security_header(wStream* s, UINT16* flags)
{
/* Basic Security Header */
if (Stream_GetRemainingLength(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 */
Stream_Seek(s, 2); /* flagsHi (unused) */
2013-01-12 03:43:16 +04:00
return TRUE;
}
/**
* Write RDP Security Header.\n
* @msdn{cc240579}
* @param s stream
* @param flags security flags
*/
void rdp_write_security_header(wStream* s, UINT16 flags)
{
/* Basic Security Header */
2013-05-09 00:09:16 +04:00
Stream_Write_UINT16(s, flags); /* flags */
Stream_Write_UINT16(s, 0); /* flagsHi (unused) */
}
BOOL rdp_read_share_control_header(wStream* s, UINT16* length, UINT16* type, UINT16* channel_id)
{
if (Stream_GetRemainingLength(s) < 2)
2013-01-12 03:43:16 +04:00
return FALSE;
/* Share Control Header */
2013-05-09 00:09:16 +04:00
Stream_Read_UINT16(s, *length); /* totalLength */
2012-02-07 01:31:41 +04:00
/* 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 (*length == 0x8000)
{
rdp_read_flow_control_pdu(s, type);
2014-11-17 02:05:12 +03:00
*channel_id = 0;
*length = 8; /* Flow control PDU is 8 bytes */
return TRUE;
}
2014-02-11 07:12:13 +04:00
if (((size_t) *length - 2) > Stream_GetRemainingLength(s))
return FALSE;
2012-02-07 01:31:41 +04:00
2013-05-09 00:09:16 +04:00
Stream_Read_UINT16(s, *type); /* pduType */
*type &= 0x0F; /* type is in the 4 least significant bits */
2011-08-21 11:52:44 +04:00
if (*length > 4)
2013-05-09 00:09:16 +04:00
Stream_Read_UINT16(s, *channel_id); /* pduSource */
else
*channel_id = 0; /* Windows XP can send such short DEACTIVATE_ALL PDUs. */
return TRUE;
}
void rdp_write_share_control_header(wStream* s, UINT16 length, UINT16 type, UINT16 channel_id)
{
length -= RDP_PACKET_HEADER_MAX_LENGTH;
/* Share Control Header */
2013-05-09 00:09:16 +04:00
Stream_Write_UINT16(s, length); /* totalLength */
Stream_Write_UINT16(s, type | 0x10); /* pduType */
Stream_Write_UINT16(s, channel_id); /* pduSource */
}
BOOL rdp_read_share_data_header(wStream* s, UINT16* length, BYTE* type, UINT32* shareId,
BYTE* compressedType, UINT16* compressedLength)
{
if (Stream_GetRemainingLength(s) < 12)
return FALSE;
2011-08-21 18:52:37 +04:00
/* Share Data Header */
Stream_Read_UINT32(s, *shareId); /* shareId (4 bytes) */
2013-05-09 00:27:21 +04:00
Stream_Seek_UINT8(s); /* pad1 (1 byte) */
Stream_Seek_UINT8(s); /* streamId (1 byte) */
2013-05-09 00:09:16 +04:00
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;
}
void rdp_write_share_data_header(wStream* s, UINT16 length, BYTE type, UINT32 share_id)
{
length -= RDP_PACKET_HEADER_MAX_LENGTH;
length -= RDP_SHARE_CONTROL_HEADER_LENGTH;
length -= RDP_SHARE_DATA_HEADER_LENGTH;
/* Share Data Header */
2013-05-09 00:09:16 +04:00
Stream_Write_UINT32(s, share_id); /* shareId (4 bytes) */
Stream_Write_UINT8(s, 0); /* pad1 (1 byte) */
Stream_Write_UINT8(s, STREAM_LOW); /* streamId (1 byte) */
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) */
}
static int rdp_security_stream_init(rdpRdp* rdp, wStream* s, BOOL sec_header)
2011-09-13 10:40:27 +04:00
{
if (rdp->do_crypt)
{
Stream_Seek(s, 12);
2012-12-12 08:34:51 +04:00
if (rdp->settings->EncryptionMethods == ENCRYPTION_METHOD_FIPS)
Stream_Seek(s, 4);
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
{
Stream_Seek(s, 4);
2011-09-13 10:40:27 +04:00
}
2012-12-12 08:34:51 +04:00
2011-09-13 10:40:27 +04:00
return 0;
}
int rdp_init_stream(rdpRdp* rdp, wStream* s)
{
Stream_Seek(s, RDP_PACKET_HEADER_MAX_LENGTH);
return rdp_security_stream_init(rdp, s, FALSE);
}
wStream* rdp_send_stream_init(rdpRdp* rdp)
{
wStream* s;
s = transport_send_stream_init(rdp->transport, 2048);
rdp_init_stream(rdp, s);
return s;
}
int rdp_init_stream_pdu(rdpRdp* rdp, wStream* s)
{
Stream_Seek(s, RDP_PACKET_HEADER_MAX_LENGTH);
rdp_security_stream_init(rdp, s, FALSE);
Stream_Seek(s, RDP_SHARE_CONTROL_HEADER_LENGTH);
return 0;
}
int rdp_init_stream_data_pdu(rdpRdp* rdp, wStream* s)
{
Stream_Seek(s, RDP_PACKET_HEADER_MAX_LENGTH);
rdp_security_stream_init(rdp, s, FALSE);
Stream_Seek(s, RDP_SHARE_CONTROL_HEADER_LENGTH);
Stream_Seek(s, RDP_SHARE_DATA_HEADER_LENGTH);
return 0;
}
wStream* rdp_data_pdu_init(rdpRdp* rdp)
{
wStream* s;
s = transport_send_stream_init(rdp->transport, 2048);
if (!s)
return NULL;
rdp_init_stream_data_pdu(rdp, s);
return s;
}
BOOL rdp_set_error_info(rdpRdp* rdp, UINT32 errorInfo)
{
rdp->errorInfo = errorInfo;
if (rdp->errorInfo != ERRINFO_SUCCESS)
{
rdpContext* context = rdp->context;
rdp_print_errinfo(rdp->errorInfo);
if (context)
{
context->LastError = MAKE_FREERDP_ERROR(ERRINFO, errorInfo);
if (context->pubSub)
{
ErrorInfoEventArgs e;
EventArgsInit(&e, "freerdp");
e.code = rdp->errorInfo;
PubSub_OnErrorInfo(context->pubSub, context, &e);
}
}
}
else
{
rdp->context->LastError = FREERDP_ERROR_SUCCESS;
}
return TRUE;
}
wStream* rdp_message_channel_pdu_init(rdpRdp* rdp)
{
wStream* s;
s = transport_send_stream_init(rdp->transport, 2048);
if (!s)
return NULL;
Stream_Seek(s, RDP_PACKET_HEADER_MAX_LENGTH);
rdp_security_stream_init(rdp, s, TRUE);
return s;
}
/**
* 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;
enum DomainMCSPDU MCSPDU;
enum DomainMCSPDU domainMCSPDU;
MCSPDU = (rdp->settings->ServerMode) ? DomainMCSPDU_SendDataRequest : DomainMCSPDU_SendDataIndication;
*length = tpkt_read_header(s);
if (!tpdu_read_header(s, &code, &li))
return FALSE;
if (code != X224_TPDU_DATA)
{
if (code == X224_TPDU_DISCONNECT_REQUEST)
{
freerdp_abort_connect(rdp->instance);
return TRUE;
}
return FALSE;
}
if (!per_read_choice(s, &choice))
return FALSE;
domainMCSPDU = (enum DomainMCSPDU) (choice >> 2);
if (domainMCSPDU != MCSPDU)
{
if (domainMCSPDU != DomainMCSPDU_DisconnectProviderUltimatum)
return FALSE;
}
2012-02-07 01:31:41 +04:00
MCSPDU = domainMCSPDU;
2014-02-11 07:12:13 +04:00
if ((size_t) (*length - 8) > Stream_GetRemainingLength(s))
return FALSE;
if (MCSPDU == DomainMCSPDU_DisconnectProviderUltimatum)
{
int reason = 0;
2013-06-19 00:55:23 +04:00
TerminateEventArgs e;
rdpContext* context;
if (!mcs_recv_disconnect_provider_ultimatum(rdp->mcs, s, &reason))
return FALSE;
if (!rdp->instance)
return FALSE;
context = rdp->instance->context;
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 == MCS_Reason_provider_initiated)
rdp_set_error_info(rdp, ERRINFO_RPC_INITIATED_DISCONNECT);
else if (reason == MCS_Reason_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);
freerdp_abort_connect(rdp->instance);
2013-06-19 00:55:23 +04:00
EventArgsInit(&e, "freerdp");
e.code = 0;
PubSub_OnTerminate(context->pubSub, context, &e);
return TRUE;
}
if (Stream_GetRemainingLength(s) < 5)
2013-01-12 03:43:16 +04:00
return FALSE;
per_read_integer16(s, &initiator, MCS_BASE_CHANNEL_ID); /* initiator (UserId) */
per_read_integer16(s, channelId, 0); /* channelId */
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 (*length > Stream_GetRemainingLength(s))
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
*/
void rdp_write_header(rdpRdp* rdp, wStream* s, UINT16 length, UINT16 channelId)
{
2011-09-16 03:54:03 +04:00
int body_length;
enum DomainMCSPDU MCSPDU;
MCSPDU = (rdp->settings->ServerMode) ? DomainMCSPDU_SendDataIndication : DomainMCSPDU_SendDataRequest;
if ((rdp->sec_flags & SEC_ENCRYPT) && (rdp->settings->EncryptionMethods == ENCRYPTION_METHOD_FIPS))
2011-09-27 09:30:58 +04:00
{
2011-09-16 03:54:03 +04:00
int pad;
body_length = length - RDP_PACKET_HEADER_MAX_LENGTH - 16;
2011-09-16 03:54:03 +04:00
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;
}
mcs_write_domain_mcspdu_header(s, MCSPDU, length, 0);
per_write_integer16(s, rdp->mcs->userId, MCS_BASE_CHANNEL_ID); /* initiator */
per_write_integer16(s, channelId, 0); /* channelId */
2013-05-09 00:09:16 +04:00
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) */
}
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;
2011-09-13 10:40:27 +04:00
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;
2011-09-16 03:54:03 +04:00
length = length - (data - Stream_Buffer(s));
2013-05-09 00:09:16 +04:00
Stream_Write_UINT16(s, 0x10); /* length */
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)
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)
{
UINT32 pad;
UINT16 length;
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))
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_pdu(rdpRdp* rdp, wStream* s, UINT16 type, UINT16 channel_id)
{
UINT16 length;
2012-10-09 11:26:39 +04:00
UINT32 sec_bytes;
int sec_hold;
UINT32 pad;
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);
rdp_write_share_control_header(s, length - sec_bytes, type, channel_id);
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)
{
UINT16 length;
2012-10-09 11:26:39 +04:00
UINT32 sec_bytes;
int sec_hold;
UINT32 pad;
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);
rdp_write_share_control_header(s, length - sec_bytes, PDU_TYPE_DATA, channel_id);
rdp_write_share_data_header(s, length - sec_bytes, type, rdp->settings->ShareId);
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_message_channel_pdu(rdpRdp* rdp, wStream* s, UINT16 sec_flags)
{
UINT16 length;
UINT32 sec_bytes;
int sec_hold;
UINT32 pad;
length = Stream_GetPosition(s);
Stream_SetPosition(s, 0);
rdp_write_header(rdp, s, length, rdp->mcs->messageChannelId);
sec_bytes = rdp_get_sec_bytes(rdp, sec_flags);
sec_hold = Stream_GetPosition(s);
Stream_Seek(s, sec_bytes);
Stream_SetPosition(s, sec_hold);
if (!rdp_security_stream_out(rdp, s, length, sec_flags, &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_recv_server_shutdown_denied_pdu(rdpRdp* rdp, wStream* s)
{
return TRUE;
}
BOOL rdp_recv_server_set_keyboard_indicators_pdu(rdpRdp* rdp, wStream* s)
{
UINT16 unitId;
UINT16 ledFlags;
rdpContext* context = rdp->instance->context;
if (Stream_GetRemainingLength(s) < 4)
return FALSE;
Stream_Read_UINT16(s, unitId); /* unitId (2 bytes) */
Stream_Read_UINT16(s, ledFlags); /* ledFlags (2 bytes) */
IFCALL(context->update->SetKeyboardIndicators, context, ledFlags);
return TRUE;
}
BOOL rdp_recv_server_set_keyboard_ime_status_pdu(rdpRdp* rdp, wStream* s)
{
UINT16 unitId;
UINT32 imeState;
UINT32 imeConvMode;
if (Stream_GetRemainingLength(s) < 10)
return FALSE;
Stream_Read_UINT16(s, unitId); /* unitId (2 bytes) */
Stream_Read_UINT32(s, imeState); /* imeState (4 bytes) */
Stream_Read_UINT32(s, imeConvMode); /* imeConvMode (4 bytes) */
return TRUE;
}
BOOL rdp_recv_set_error_info_data_pdu(rdpRdp* rdp, wStream* s)
2011-07-22 00:20:41 +04:00
{
UINT32 errorInfo;
if (Stream_GetRemainingLength(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
}
BOOL rdp_recv_server_auto_reconnect_status_pdu(rdpRdp* rdp, wStream* s)
{
UINT32 arcStatus;
if (Stream_GetRemainingLength(s) < 4)
return FALSE;
Stream_Read_UINT32(s, arcStatus); /* arcStatus (4 bytes) */
2015-02-06 22:21:26 +03:00
WLog_WARN(TAG, "AutoReconnectStatus: 0x%08"PRIX32"", arcStatus);
2015-02-06 22:21:26 +03:00
return TRUE;
}
BOOL rdp_recv_server_status_info_pdu(rdpRdp* rdp, wStream* s)
{
UINT32 statusCode;
if (Stream_GetRemainingLength(s) < 4)
return FALSE;
Stream_Read_UINT32(s, statusCode); /* statusCode (4 bytes) */
return TRUE;
}
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_GetRemainingLength(s) < 4)
return FALSE;
Stream_Read_UINT32(s, monitorCount); /* monitorCount (4 bytes) */
if ((Stream_GetRemainingLength(s) / 20) < monitorCount)
return FALSE;
monitorDefArray = (MONITOR_DEF*) calloc(monitorCount, sizeof(MONITOR_DEF));
if (!monitorDefArray)
return FALSE;
for (monitor = monitorDefArray, index = 0; index < monitorCount; index++, monitor++)
{
Stream_Read_UINT32(s, monitor->left); /* left (4 bytes) */
Stream_Read_UINT32(s, monitor->top); /* top (4 bytes) */
Stream_Read_UINT32(s, monitor->right); /* right (4 bytes) */
Stream_Read_UINT32(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;
}
BOOL rdp_write_monitor_layout_pdu(wStream* s, UINT32 monitorCount, const rdpMonitor* monitorDefArray)
{
2014-02-11 07:12:13 +04:00
UINT32 index;
const rdpMonitor* monitor;
if (!Stream_EnsureRemainingCapacity(s, 4 + (monitorCount * 20)))
return FALSE;
Stream_Write_UINT32(s, monitorCount); /* monitorCount (4 bytes) */
for (index = 0, monitor = monitorDefArray; index < monitorCount; index++, monitor++)
{
Stream_Write_UINT32(s, monitor->x); /* left (4 bytes) */
Stream_Write_UINT32(s, monitor->y); /* top (4 bytes) */
Stream_Write_UINT32(s, monitor->x + monitor->width - 1); /* right (4 bytes) */
Stream_Write_UINT32(s, monitor->y + monitor->height - 1); /* bottom (4 bytes) */
Stream_Write_UINT32(s, monitor->is_primary ? 0x01 : 0x00); /* flags (4 bytes) */
}
return TRUE;
}
int 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
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");
2013-01-12 03:43:16 +04:00
return -1;
}
2011-07-22 00:20:41 +04:00
cs = s;
if (compressedType & PACKET_COMPRESSED)
{
UINT32 DstSize = 0;
BYTE* pDstData = NULL;
UINT32 SrcSize = compressedLength - 18;
if (Stream_GetRemainingLength(s) < (size_t) SrcSize)
{
WLog_ERR(TAG, "bulk_decompress: not enough bytes for compressedLength %"PRIu16"", compressedLength);
return -1;
}
if (bulk_decompress(rdp->bulk, Stream_Pointer(s), SrcSize, &pDstData, &DstSize, compressedType))
{
if (!(cs = StreamPool_Take(rdp->transport->ReceivePool, DstSize)))
{
2016-12-01 13:15:11 +03:00
WLog_ERR(TAG, "Couldn't take stream from pool");
return -1;
}
Stream_SetPosition(cs, 0);
Stream_Write(cs, pDstData, DstSize);
Stream_SealLength(cs);
Stream_SetPosition(cs, 0);
}
else
{
WLog_ERR(TAG, "bulk_decompress() failed");
return -1;
}
Stream_Seek(s, SrcSize);
}
WLog_DBG(TAG, "recv %s Data PDU (0x%02"PRIX8"), length: %"PRIu16"",
type < ARRAYSIZE(DATA_PDU_TYPE_STRINGS) ? DATA_PDU_TYPE_STRINGS[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_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
{
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
{
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
{
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
{
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
{
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
{
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
{
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
{
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:
break;
}
if (cs != s)
Stream_Release(cs);
return 0;
out_fail:
if (cs != s)
Stream_Release(cs);
return -1;
2011-07-22 00:20:41 +04:00
}
int rdp_recv_message_channel_pdu(rdpRdp* rdp, wStream* s, UINT16 securityFlags)
{
if (securityFlags & SEC_AUTODETECT_REQ)
{
/* Server Auto-Detect Request PDU */
return rdp_recv_autodetect_request_packet(rdp, s);
}
if (securityFlags & SEC_AUTODETECT_RSP)
{
/* Client Auto-Detect Response PDU */
return rdp_recv_autodetect_response_packet(rdp, 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)
{
/* Initiate Multitransport Request PDU */
return rdp_recv_multitransport_packet(rdp, s);
}
return -1;
}
int rdp_recv_out_of_sequence_pdu(rdpRdp* rdp, wStream* s)
{
UINT16 type;
UINT16 length;
UINT16 channelId;
if (!rdp_read_share_control_header(s, &length, &type, &channelId))
return -1;
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);
}
else if (type == PDU_TYPE_FLOW_RESPONSE ||
type == PDU_TYPE_FLOW_STOP ||
type == PDU_TYPE_FLOW_TEST)
{
return 0;
}
else
{
return -1;
}
}
void rdp_read_flow_control_pdu(wStream* s, UINT16* type)
{
/*
* 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;
Stream_Read_UINT8(s, pduType); /* pduTypeFlow */
*type = pduType;
Stream_Seek_UINT8(s); /* pad8bits */
Stream_Seek_UINT8(s); /* flowIdentifier */
Stream_Seek_UINT8(s); /* flowNumber */
Stream_Seek_UINT16(s); /* pduSource */
}
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, int length, UINT16 securityFlags)
2011-09-05 22:02:52 +04:00
{
BYTE cmac[8];
BYTE wmac[8];
BOOL status;
2011-09-15 01:14:50 +04:00
if (rdp->settings->EncryptionMethods == ENCRYPTION_METHOD_FIPS)
2011-09-16 03:54:03 +04:00
{
UINT16 len;
BYTE version, pad;
BYTE* sig;
2011-09-16 03:54:03 +04:00
if (Stream_GetRemainingLength(s) < 12)
2013-01-12 03:43:16 +04:00
return FALSE;
2013-05-09 00:09:16 +04:00
Stream_Read_UINT16(s, len); /* 0x10 */
Stream_Read_UINT8(s, version); /* 0x1 */
Stream_Read_UINT8(s, pad);
2011-09-16 03:54:03 +04:00
sig = Stream_Pointer(s);
Stream_Seek(s, 8); /* signature */
2011-09-16 03:54:03 +04:00
length -= 12;
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);
return TRUE;
2011-09-16 03:54:03 +04:00
}
if (Stream_GetRemainingLength(s) < 8)
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 (!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.
*/
//return FALSE;
}
return TRUE;
2011-09-05 22:02:52 +04:00
}
/**
* Process an RDP packet.\n
* @param rdp RDP module
* @param s stream
*/
static int rdp_recv_tpkt_pdu(rdpRdp* rdp, wStream* s)
{
UINT16 length;
UINT16 pduType;
UINT16 pduLength;
UINT16 pduSource;
2013-08-30 18:31:23 +04:00
UINT16 channelId = 0;
UINT16 securityFlags = 0;
int nextPosition;
if (!rdp_read_header(rdp, s, &length, &channelId))
{
WLog_ERR(TAG, "Incorrect RDP header.");
return -1;
}
if (freerdp_shall_disconnect(rdp->instance))
return 0;
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
{
2013-01-12 03:43:16 +04:00
if (!rdp_read_security_header(s, &securityFlags))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "rdp_recv_tpkt_pdu: rdp_read_security_header() fail");
2013-01-12 03:43:16 +04:00
return -1;
}
if (securityFlags & (SEC_ENCRYPT | SEC_REDIRECTION_PKT))
2011-09-05 22:02:52 +04:00
{
if (!rdp_decrypt(rdp, s, length - 4, securityFlags))
2011-09-05 22:02:52 +04:00
{
WLog_ERR(TAG, "rdp_decrypt failed");
return -1;
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);
return rdp_recv_enhanced_security_redirection_packet(rdp, s);
}
2011-09-05 22:02:52 +04:00
}
if (channelId == MCS_GLOBAL_CHANNEL_ID)
{
while (Stream_GetRemainingLength(s) > 3)
{
nextPosition = Stream_GetPosition(s);
2013-01-12 03:43:16 +04:00
if (!rdp_read_share_control_header(s, &pduLength, &pduType, &pduSource))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "rdp_recv_tpkt_pdu: rdp_read_share_control_header() fail");
2013-01-12 03:43:16 +04:00
return -1;
}
nextPosition += pduLength;
rdp->settings->PduSource = pduSource;
switch (pduType)
{
case PDU_TYPE_DATA:
if (rdp_recv_data_pdu(rdp, s) < 0)
{
WLog_ERR(TAG, "rdp_recv_data_pdu() failed");
return -1;
}
break;
case PDU_TYPE_DEACTIVATE_ALL:
if (!rdp_recv_deactivate_all(rdp, s))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "rdp_recv_tpkt_pdu: rdp_recv_deactivate_all() fail");
return -1;
}
break;
case PDU_TYPE_SERVER_REDIRECTION:
return rdp_recv_enhanced_security_redirection_packet(rdp, s);
break;
case PDU_TYPE_FLOW_RESPONSE:
case PDU_TYPE_FLOW_STOP:
case PDU_TYPE_FLOW_TEST:
break;
default:
WLog_ERR(TAG, "incorrect PDU type: 0x%04"PRIX16"", pduType);
break;
}
Stream_SetPosition(s, nextPosition);
}
}
else if (rdp->mcs->messageChannelId && channelId == rdp->mcs->messageChannelId)
{
if (!rdp->settings->UseRdpSecurityLayer)
if (!rdp_read_security_header(s, &securityFlags))
return -1;
return rdp_recv_message_channel_pdu(rdp, s, securityFlags);
}
else
{
if (!freerdp_channel_process(rdp->instance, s, channelId))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "rdp_recv_tpkt_pdu: freerdp_channel_process() fail");
return -1;
}
}
return 0;
}
static int rdp_recv_fastpath_pdu(rdpRdp* rdp, wStream* s)
{
UINT16 length;
2011-09-27 09:30:58 +04:00
rdpFastPath* fastpath;
2011-09-27 09:30:58 +04:00
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");
2013-01-14 02:37:50 +04:00
return -1;
}
2012-01-25 19:27:00 +04:00
if ((length == 0) || (length > Stream_GetRemainingLength(s)))
{
WLog_ERR(TAG, "incorrect FastPath PDU header length %"PRIu16"", length);
return -1;
}
if (rdp->autodetect->bandwidthMeasureStarted)
{
rdp->autodetect->bandwidthMeasureByteCount += length;
}
2011-09-27 09:30:58 +04:00
if (fastpath->encryptionFlags & FASTPATH_OUTPUT_ENCRYPTED)
2011-09-15 01:14:50 +04:00
{
2013-01-12 03:43:16 +04:00
UINT16 flags = (fastpath->encryptionFlags & FASTPATH_OUTPUT_SECURE_CHECKSUM) ? SEC_SECURE_CHECKSUM : 0;
2013-01-12 03:43:16 +04:00
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");
2013-01-12 03:43:16 +04:00
return -1;
}
2011-09-15 01:14:50 +04:00
}
2011-08-23 09:05:58 +04:00
return fastpath_recv_updates(rdp->fastpath, s);
}
static int rdp_recv_pdu(rdpRdp* rdp, wStream* s)
{
if (tpkt_verify_header(s))
2011-08-23 08:58:10 +04:00
return rdp_recv_tpkt_pdu(rdp, s);
else
2011-08-23 08:58:10 +04:00
return rdp_recv_fastpath_pdu(rdp, s);
}
int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra)
{
int status = 0;
rdpRdp* rdp = (rdpRdp*) extra;
/*
* At any point in the connection sequence between when all
* MCS channels have been joined and when the RDP connection
* enters the active state, an auto-detect PDU can be received
* on the MCS message channel.
*/
if ((rdp->state > CONNECTION_STATE_MCS_CHANNEL_JOIN) && (rdp->state < CONNECTION_STATE_ACTIVE))
{
if (rdp_client_connect_auto_detect(rdp, s))
return 0;
}
switch (rdp->state)
{
2015-02-16 00:04:59 +03:00
case CONNECTION_STATE_NLA:
if (rdp->nla->state < NLA_STATE_AUTH_INFO)
2015-05-02 06:26:08 +03:00
{
if (nla_recv_pdu(rdp->nla, s) < 1)
{
WLog_ERR(TAG, "rdp_recv_callback: CONNECTION_STATE_NLA - nla_recv_pdu() fail");
return -1;
}
}
else if (rdp->nla->state == NLA_STATE_POST_NEGO)
{
nego_recv(rdp->transport, s, (void*) rdp->nego);
if (rdp->nego->state != NEGO_STATE_FINAL)
{
WLog_ERR(TAG, "rdp_recv_callback: CONNECTION_STATE_NLA - nego_recv() fail");
return -1;
}
rdp->nla->state = NLA_STATE_FINAL;
}
2015-02-16 00:04:59 +03:00
if (rdp->nla->state == NLA_STATE_AUTH_INFO)
{
transport_set_nla_mode(rdp->transport, FALSE);
if (rdp->settings->VmConnectMode)
{
rdp->nego->state = NEGO_STATE_NLA;
rdp->nego->RequestedProtocols = PROTOCOL_NLA | PROTOCOL_TLS;
nego_send_negotiation_request(rdp->nego);
rdp->nla->state = NLA_STATE_POST_NEGO;
}
else
{
rdp->nla->state = NLA_STATE_FINAL;
}
}
if (rdp->nla->state == NLA_STATE_FINAL)
{
2015-02-16 00:04:59 +03:00
nla_free(rdp->nla);
rdp->nla = NULL;
if (!mcs_client_begin(rdp->mcs))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "rdp_recv_callback: CONNECTION_STATE_NLA - mcs_client_begin() fail");
2015-02-16 00:04:59 +03:00
return -1;
}
2015-02-16 00:04:59 +03:00
}
break;
case CONNECTION_STATE_MCS_CONNECT:
if (!mcs_recv_connect_response(rdp->mcs, s))
{
WLog_ERR(TAG, "mcs_recv_connect_response failure");
return -1;
}
if (!mcs_send_erect_domain_request(rdp->mcs))
{
WLog_ERR(TAG, "mcs_send_erect_domain_request failure");
return -1;
}
if (!mcs_send_attach_user_request(rdp->mcs))
{
WLog_ERR(TAG, "mcs_send_attach_user_request failure");
return -1;
}
rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_ATTACH_USER);
break;
case CONNECTION_STATE_MCS_ATTACH_USER:
if (!mcs_recv_attach_user_confirm(rdp->mcs, s))
{
WLog_ERR(TAG, "mcs_recv_attach_user_confirm failure");
return -1;
}
if (!mcs_send_channel_join_request(rdp->mcs, rdp->mcs->userId))
{
WLog_ERR(TAG, "mcs_send_channel_join_request failure");
return -1;
}
rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN);
break;
case CONNECTION_STATE_MCS_CHANNEL_JOIN:
if (!rdp_client_connect_mcs_channel_join_confirm(rdp, s))
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "rdp_recv_callback: CONNECTION_STATE_MCS_CHANNEL_JOIN - rdp_client_connect_mcs_channel_join_confirm() fail");
status = -1;
}
break;
case CONNECTION_STATE_LICENSING:
status = rdp_client_connect_license(rdp, s);
if (status < 0)
WLog_DBG(TAG, "CONNECTION_STATE_LICENSING - rdp_client_connect_license() - %i", status);
break;
case CONNECTION_STATE_CAPABILITIES_EXCHANGE:
status = rdp_client_connect_demand_active(rdp, s);
if (status < 0)
WLog_DBG(TAG, "CONNECTION_STATE_CAPABILITIES_EXCHANGE - rdp_client_connect_demand_active() - %i", status);
break;
case CONNECTION_STATE_FINALIZATION:
status = rdp_recv_pdu(rdp, s);
if ((status >= 0) && (rdp->finalize_sc_pdus == FINALIZE_SC_COMPLETE))
{
rdp_client_transition_to_state(rdp, CONNECTION_STATE_ACTIVE);
return 2;
}
if (status < 0)
WLog_DBG(TAG, "CONNECTION_STATE_FINALIZATION - rdp_recv_pdu() - %i", status);
break;
case CONNECTION_STATE_ACTIVE:
status = rdp_recv_pdu(rdp, s);
if (status < 0)
WLog_DBG(TAG, "CONNECTION_STATE_ACTIVE - rdp_recv_pdu() - %i", status);
break;
default:
WLog_ERR(TAG, "Invalid state %d", rdp->state);
status = -1;
break;
}
return status;
}
int rdp_send_channel_data(rdpRdp* rdp, UINT16 channelId, BYTE* data, int size)
{
return freerdp_channel_send(rdp, channelId, data, size);
}
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;
2015-02-02 19:50:56 +03:00
rdpTransport* transport = rdp->transport;
2015-02-02 19:50:56 +03:00
if (transport->tsg)
{
rdpTsg* tsg = transport->tsg;
status = tsg_check_event_handles(tsg);
2015-02-02 19:50:56 +03:00
if (status < 0)
2015-05-02 06:26:08 +03:00
{
WLog_ERR(TAG, "rdp_check_fds: tsg_check_event_handles() - %i", status);
2015-02-02 19:50:56 +03:00
return -1;
}
2015-02-02 19:50:56 +03:00
if (tsg->state != TSG_STATE_PIPE_CREATED)
return status;
}
status = transport_check_fds(transport);
if (status == 1)
{
status = rdp_client_redirect(rdp); /* session redirection */
}
if (status < 0)
WLog_DBG(TAG, "transport_check_fds() - %i", status);
return status;
}
/**
* Instantiate new RDP module.
* @return new RDP module
*/
rdpRdp* rdp_new(rdpContext* context)
{
rdpRdp* rdp;
DWORD flags;
2014-04-10 23:07:53 +04:00
BOOL newSettings = FALSE;
2014-04-10 23:07:53 +04:00
rdp = (rdpRdp*) calloc(1, sizeof(rdpRdp));
2014-04-10 23:07:53 +04:00
if (!rdp)
return NULL;
2014-04-10 23:07:53 +04:00
rdp->context = context;
rdp->instance = context->instance;
2014-04-10 23:07:53 +04:00
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 = freerdp_settings_new(flags);
if (!context->settings)
2014-04-10 23:07:53 +04:00
goto out_free;
newSettings = TRUE;
}
2014-04-10 23:07:53 +04:00
rdp->settings = context->settings;
if (context->instance)
{
rdp->settings->instance = context->instance;
2014-04-10 23:07:53 +04:00
context->instance->settings = rdp->settings;
}
else if (context->peer)
{
rdp->settings->instance = context->peer;
context->peer->settings = rdp->settings;
}
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 out_free_settings;
2014-04-10 23:07:53 +04:00
rdp->license = license_new(rdp);
if (!rdp->license)
goto out_free_transport;
rdp->input = input_new(rdp);
if (!rdp->input)
goto out_free_license;
rdp->update = update_new(rdp);
if (!rdp->update)
goto out_free_input;
rdp->fastpath = fastpath_new(rdp);
if (!rdp->fastpath)
goto out_free_update;
rdp->nego = nego_new(rdp->transport);
if (!rdp->nego)
goto out_free_fastpath;
rdp->mcs = mcs_new(rdp->transport);
if (!rdp->mcs)
goto out_free_nego;
rdp->redirection = redirection_new();
if (!rdp->redirection)
goto out_free_mcs;
rdp->autodetect = autodetect_new();
if (!rdp->autodetect)
goto out_free_redirection;
rdp->heartbeat = heartbeat_new();
if (!rdp->heartbeat)
goto out_free_autodetect;
rdp->multitransport = multitransport_new();
if (!rdp->multitransport)
goto out_free_heartbeat;
rdp->bulk = bulk_new(context);
if (!rdp->bulk)
goto out_free_multitransport;
return rdp;
2014-04-10 23:07:53 +04:00
out_free_multitransport:
multitransport_free(rdp->multitransport);
out_free_heartbeat:
heartbeat_free(rdp->heartbeat);
out_free_autodetect:
autodetect_free(rdp->autodetect);
out_free_redirection:
redirection_free(rdp->redirection);
out_free_mcs:
mcs_free(rdp->mcs);
out_free_nego:
nego_free(rdp->nego);
out_free_fastpath:
fastpath_free(rdp->fastpath);
out_free_update:
update_free(rdp->update);
out_free_input:
input_free(rdp->input);
out_free_license:
license_free(rdp->license);
out_free_transport:
transport_free(rdp->transport);
out_free_settings:
if (newSettings)
freerdp_settings_free(rdp->settings);
out_free:
free(rdp);
return NULL;
}
void rdp_reset(rdpRdp* rdp)
{
rdpContext* context;
rdpSettings* settings;
context = rdp->context;
settings = rdp->settings;
bulk_reset(rdp->bulk);
2015-02-06 22:21:26 +03:00
if (rdp->rc4_decrypt_key)
{
2016-02-28 13:12:17 +03:00
winpr_RC4_Free(rdp->rc4_decrypt_key);
2015-02-06 22:21:26 +03:00
rdp->rc4_decrypt_key = NULL;
}
if (rdp->rc4_encrypt_key)
{
2016-02-28 13:12:17 +03:00
winpr_RC4_Free(rdp->rc4_encrypt_key);
2015-02-06 22:21:26 +03:00
rdp->rc4_encrypt_key = NULL;
}
if (rdp->fips_encrypt)
{
2016-02-28 01:28:49 +03:00
winpr_Cipher_Free(rdp->fips_encrypt);
2015-02-06 22:21:26 +03:00
rdp->fips_encrypt = NULL;
}
if (rdp->fips_decrypt)
{
2016-02-28 01:28:49 +03:00
winpr_Cipher_Free(rdp->fips_decrypt);
2015-02-06 22:21:26 +03:00
rdp->fips_decrypt = NULL;
}
if (settings->ServerRandom)
{
free(settings->ServerRandom);
settings->ServerRandom = NULL;
settings->ServerRandomLength = 0;
}
if (settings->ServerCertificate)
{
free(settings->ServerCertificate);
settings->ServerCertificate = NULL;
}
if (settings->ClientAddress)
{
free(settings->ClientAddress);
settings->ClientAddress = NULL;
}
mcs_free(rdp->mcs);
nego_free(rdp->nego);
license_free(rdp->license);
transport_free(rdp->transport);
rdp->transport = transport_new(context);
rdp->license = license_new(rdp);
rdp->nego = nego_new(rdp->transport);
rdp->mcs = mcs_new(rdp->transport);
rdp->transport->layer = TRANSPORT_LAYER_TCP;
2015-01-12 15:47:04 +03:00
rdp->errorInfo = 0;
rdp->deactivation_reactivation = 0;
rdp->finalize_sc_pdus = 0;
}
/**
* Free RDP module.
* @param rdp RDP module to be freed
*/
void rdp_free(rdpRdp* rdp)
{
if (rdp)
{
2016-02-28 13:12:17 +03:00
winpr_RC4_Free(rdp->rc4_decrypt_key);
winpr_RC4_Free(rdp->rc4_encrypt_key);
2016-02-28 01:28:49 +03:00
winpr_Cipher_Free(rdp->fips_encrypt);
winpr_Cipher_Free(rdp->fips_decrypt);
freerdp_settings_free(rdp->settings);
transport_free(rdp->transport);
license_free(rdp->license);
input_free(rdp->input);
update_free(rdp->update);
fastpath_free(rdp->fastpath);
2011-08-25 09:45:43 +04:00
nego_free(rdp->nego);
mcs_free(rdp->mcs);
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);
}
}