From ce28575b3a5e0441b715f2644181a538473eb786 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Sun, 5 Apr 2020 13:34:53 -0400 Subject: [PATCH] GUACAMOLE-1059: Use FreeRDP function for verifying Stream length before reading. --- .../rdpdr/rdpdr-fs-messages-file-info.c | 4 +++ .../rdp/channels/rdpdr/rdpdr-fs-messages.c | 30 +++++++++++++++++++ .../rdp/channels/rdpdr/rdpdr-messages.c | 18 +++++++++++ .../rdp/channels/rdpdr/rdpdr-printer.c | 3 ++ src/protocols/rdp/channels/rdpdr/rdpdr.c | 3 ++ .../rdp/channels/rdpsnd/rdpsnd-messages.c | 16 ++++++++++ src/protocols/rdp/channels/rdpsnd/rdpsnd.c | 14 ++++++++- .../plugins/guac-common-svc/guac-common-svc.c | 5 ++++ .../rdp/plugins/guacai/guacai-messages.c | 20 +++++++++++-- src/protocols/rdp/plugins/guacai/guacai.c | 5 +++- 10 files changed, 114 insertions(+), 4 deletions(-) diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-file-info.c b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-file-info.c index 0f453038..52201a27 100644 --- a/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-file-info.c +++ b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-file-info.c @@ -135,6 +135,10 @@ void guac_rdpdr_fs_process_set_rename_info(guac_rdp_common_svc* svc, wStream* output_stream; char destination_path[GUAC_RDP_FS_MAX_PATH]; + /* Check stream size prior to reading. */ + if (Stream_GetRemainingLength(input_stream) < 6) + return; + /* Read structure */ Stream_Seek_UINT8(input_stream); /* ReplaceIfExists */ Stream_Seek_UINT8(input_stream); /* RootDirectory */ diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages.c b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages.c index 529eea57..0e9581cc 100644 --- a/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages.c +++ b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages.c @@ -48,6 +48,10 @@ void guac_rdpdr_fs_process_create(guac_rdp_common_svc* svc, int create_disposition, create_options, path_length; char path[GUAC_RDP_FS_MAX_PATH]; + /* Check remaining stream data prior to reading. */ + if (Stream_GetRemainingLength(input_stream) < 32) + return; + /* Read "create" information */ Stream_Read_UINT32(input_stream, desired_access); Stream_Seek_UINT64(input_stream); /* allocation size */ @@ -123,6 +127,10 @@ void guac_rdpdr_fs_process_read(guac_rdp_common_svc* svc, wStream* output_stream; + /* Check remaining bytes before reading stream. */ + if (Stream_GetRemainingLength(input_stream) < 12) + return; + /* Read packet */ Stream_Read_UINT32(input_stream, length); Stream_Read_UINT64(input_stream, offset); @@ -172,6 +180,10 @@ void guac_rdpdr_fs_process_write(guac_rdp_common_svc* svc, wStream* output_stream; + /* Check remaining length. */ + if (Stream_GetRemainingLength(input_stream) < 32) + return; + /* Read packet */ Stream_Read_UINT32(input_stream, length); Stream_Read_UINT64(input_stream, offset); @@ -244,6 +256,10 @@ void guac_rdpdr_fs_process_volume_info(guac_rdp_common_svc* svc, int fs_information_class; + /* Check remaining length */ + if (Stream_GetRemainingLength(input_stream) < 4) + return; + Stream_Read_UINT32(input_stream, fs_information_class); /* Dispatch to appropriate class-specific handler */ @@ -282,6 +298,10 @@ void guac_rdpdr_fs_process_file_info(guac_rdp_common_svc* svc, int fs_information_class; + /* Check remaining length */ + if (Stream_GetRemainingLength(input_stream) < 4) + return; + Stream_Read_UINT32(input_stream, fs_information_class); /* Dispatch to appropriate class-specific handler */ @@ -328,6 +348,10 @@ void guac_rdpdr_fs_process_set_file_info(guac_rdp_common_svc* svc, int fs_information_class; int length; + /* Check remaining length */ + if (Stream_GetRemainingLength(input_stream) < 32) + return; + Stream_Read_UINT32(input_stream, fs_information_class); Stream_Read_UINT32(input_stream, length); /* Length */ Stream_Seek(input_stream, 24); /* Padding */ @@ -406,6 +430,9 @@ void guac_rdpdr_fs_process_query_directory(guac_rdp_common_svc* svc, if (file == NULL) return; + if (Stream_GetRemainingLength(input_stream) < 9) + return; + /* Read main header */ Stream_Read_UINT32(input_stream, fs_information_class); Stream_Read_UINT8(input_stream, initial_query); @@ -414,6 +441,9 @@ void guac_rdpdr_fs_process_query_directory(guac_rdp_common_svc* svc, /* If this is the first query, the path is included after padding */ if (initial_query) { + if (Stream_GetRemainingLength(input_stream) < 23) + return; + Stream_Seek(input_stream, 23); /* Padding */ /* Convert path to UTF-8 */ diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr-messages.c b/src/protocols/rdp/channels/rdpdr/rdpdr-messages.c index 28a27e67..76808fac 100644 --- a/src/protocols/rdp/channels/rdpdr/rdpdr-messages.c +++ b/src/protocols/rdp/channels/rdpdr/rdpdr-messages.c @@ -212,6 +212,9 @@ void guac_rdpdr_process_server_announce(guac_rdp_common_svc* svc, unsigned int major, minor, client_id; + if (Stream_GetRemainingLength(input_stream) < 8) + return; + Stream_Read_UINT16(input_stream, major); Stream_Read_UINT16(input_stream, minor); Stream_Read_UINT32(input_stream, client_id); @@ -243,6 +246,9 @@ void guac_rdpdr_process_device_reply(guac_rdp_common_svc* svc, unsigned int device_id, ntstatus; int severity, c, n, facility, code; + if (Stream_GetRemainingLength(input_stream) < 8) + return; + Stream_Read_UINT32(input_stream, device_id); Stream_Read_UINT32(input_stream, ntstatus); @@ -278,6 +284,9 @@ void guac_rdpdr_process_device_iorequest(guac_rdp_common_svc* svc, guac_rdpdr* rdpdr = (guac_rdpdr*) svc->data; guac_rdpdr_iorequest iorequest; + if (Stream_GetRemainingLength(input_stream) < 20) + return; + /* Read header */ Stream_Read_UINT32(input_stream, iorequest.device_id); Stream_Read_UINT32(input_stream, iorequest.file_id); @@ -306,6 +315,9 @@ void guac_rdpdr_process_server_capability(guac_rdp_common_svc* svc, int count; int i; + if (Stream_GetRemainingLength(input_stream) < 4) + return; + /* Read header */ Stream_Read_UINT16(input_stream, count); Stream_Seek(input_stream, 2); @@ -316,9 +328,15 @@ void guac_rdpdr_process_server_capability(guac_rdp_common_svc* svc, int type; int length; + if (Stream_GetRemainingLength(input_stream) < 4) + break; + Stream_Read_UINT16(input_stream, type); Stream_Read_UINT16(input_stream, length); + if (Stream_GetRemainingLength(input_stream) < (length - 4)) + break; + /* Ignore all for now */ guac_client_log(svc->client, GUAC_LOG_DEBUG, "Ignoring server capability set type=0x%04x, length=%i", type, length); Stream_Seek(input_stream, length - 4); diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr-printer.c b/src/protocols/rdp/channels/rdpdr/rdpdr-printer.c index d90116f5..fb0b1c95 100644 --- a/src/protocols/rdp/channels/rdpdr/rdpdr-printer.c +++ b/src/protocols/rdp/channels/rdpdr/rdpdr-printer.c @@ -67,6 +67,9 @@ void guac_rdpdr_process_print_job_write(guac_rdp_common_svc* svc, int length; int status; + if (Stream_GetRemainingLength(input_stream) < 32) + return; + /* Read buffer of print data */ Stream_Read_UINT32(input_stream, length); Stream_Seek(input_stream, 8); /* Offset */ diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr.c b/src/protocols/rdp/channels/rdpdr/rdpdr.c index e04bc9d7..5499e717 100644 --- a/src/protocols/rdp/channels/rdpdr/rdpdr.c +++ b/src/protocols/rdp/channels/rdpdr/rdpdr.c @@ -38,6 +38,9 @@ void guac_rdpdr_process_receive(guac_rdp_common_svc* svc, int component; int packet_id; + if (Stream_GetRemainingLength(input_stream) < 4) + return; + /* Read header */ Stream_Read_UINT16(input_stream, component); Stream_Read_UINT16(input_stream, packet_id); diff --git a/src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.c b/src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.c index 1d69e069..9e33c0e7 100644 --- a/src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.c +++ b/src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.c @@ -50,6 +50,9 @@ void guac_rdpsnd_formats_handler(guac_rdp_common_svc* svc, /* Reset own format count */ rdpsnd->format_count = 0; + if (Stream_GetRemainingLength(input_stream) < 20) + return; + /* Format header */ Stream_Seek(input_stream, 14); Stream_Read_UINT16(input_stream, server_format_count); @@ -96,6 +99,9 @@ void guac_rdpsnd_formats_handler(guac_rdp_common_svc* svc, /* Remember position in stream */ Stream_GetPointer(input_stream, format_start); + if (Stream_GetRemainingLength(input_stream) < 18) + return; + /* Read format */ Stream_Read_UINT16(input_stream, format_tag); Stream_Read_UINT16(input_stream, channels); @@ -106,6 +112,10 @@ void guac_rdpsnd_formats_handler(guac_rdp_common_svc* svc, /* Skip past extra data */ Stream_Read_UINT16(input_stream, body_size); + + if (Stream_GetRemainingLength(input_stream) < body_size) + return; + Stream_Seek(input_stream, body_size); /* If PCM, accept */ @@ -205,6 +215,9 @@ void guac_rdpsnd_training_handler(guac_rdp_common_svc* svc, guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data; + if (Stream_GetRemainingLength(input_stream) < 4) + return; + /* Read timestamp and data size */ Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp); Stream_Read_UINT16(input_stream, data_size); @@ -232,6 +245,9 @@ void guac_rdpsnd_wave_info_handler(guac_rdp_common_svc* svc, guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; guac_audio_stream* audio = rdp_client->audio; + if (Stream_GetRemainingLength(input_stream) < 12) + return; + /* Read wave information */ Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp); Stream_Read_UINT16(input_stream, format); diff --git a/src/protocols/rdp/channels/rdpsnd/rdpsnd.c b/src/protocols/rdp/channels/rdpsnd/rdpsnd.c index be6034d2..0873a946 100644 --- a/src/protocols/rdp/channels/rdpsnd/rdpsnd.c +++ b/src/protocols/rdp/channels/rdpsnd/rdpsnd.c @@ -35,11 +35,23 @@ void guac_rdpsnd_process_receive(guac_rdp_common_svc* svc, guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data; guac_rdpsnd_pdu_header header; + /* Check that we at least have a header. */ + if (Stream_GetRemainingLength(input_stream) < 4) + return; + /* Read RDPSND PDU header */ Stream_Read_UINT8(input_stream, header.message_type); Stream_Seek_UINT8(input_stream); Stream_Read_UINT16(input_stream, header.body_size); - + + if (Stream_GetRemainingLength(input_stream) < header.body_size) { + guac_client_log(svc->client, GUAC_LOG_DEBUG, "Not enough bytes in stream." + " Remaining: %d, Body size: %d", + Stream_GetRemainingLength(input_stream), + header.body_size); + return; + } + /* * If next PDU is SNDWAVE (due to receiving WaveInfo PDU previously), * ignore the header and parse as a Wave PDU. diff --git a/src/protocols/rdp/plugins/guac-common-svc/guac-common-svc.c b/src/protocols/rdp/plugins/guac-common-svc/guac-common-svc.c index 535cb453..91dee29b 100644 --- a/src/protocols/rdp/plugins/guac-common-svc/guac-common-svc.c +++ b/src/protocols/rdp/plugins/guac-common-svc/guac-common-svc.c @@ -116,6 +116,10 @@ static VOID guac_rdp_common_svc_handle_open_event(LPVOID user_param, svc->_input_stream = Stream_New(NULL, total_length); } + /* leave if we don't have a stream. */ + if (svc->_input_stream == NULL) + return; + /* Add chunk to buffer only if sufficient space remains */ if (Stream_EnsureRemainingCapacity(svc->_input_stream, data_length)) Stream_Write(svc->_input_stream, data, data_length); @@ -137,6 +141,7 @@ static VOID guac_rdp_common_svc_handle_open_event(LPVOID user_param, svc->_receive_handler(svc, svc->_input_stream); Stream_Free(svc->_input_stream, TRUE); + svc->_input_stream = NULL; } diff --git a/src/protocols/rdp/plugins/guacai/guacai-messages.c b/src/protocols/rdp/plugins/guacai/guacai-messages.c index 38f7a7c4..8641002c 100644 --- a/src/protocols/rdp/plugins/guacai/guacai-messages.c +++ b/src/protocols/rdp/plugins/guacai/guacai-messages.c @@ -39,6 +39,9 @@ static void guac_rdp_ai_read_format(wStream* stream, guac_rdp_ai_format* format) { + if (Stream_GetRemainingLength(stream) < 18) + return; + /* Read audio format into structure */ Stream_Read_UINT16(stream, format->tag); /* wFormatTag */ Stream_Read_UINT16(stream, format->channels); /* nChannels */ @@ -49,7 +52,8 @@ static void guac_rdp_ai_read_format(wStream* stream, Stream_Read_UINT16(stream, format->data_size); /* cbSize */ /* Read arbitrary data block (if applicable) */ - if (format->data_size != 0) { + if (format->data_size != 0 + && Stream_GetRemainingLength(stream) >= format->data_size) { format->data = Stream_Pointer(stream); /* data */ Stream_Seek(stream, format->data_size); } @@ -232,6 +236,12 @@ static void guac_rdp_ai_send_formatchange(IWTSVirtualChannel* channel, void guac_rdp_ai_process_version(guac_client* client, IWTSVirtualChannel* channel, wStream* stream) { + if (Stream_GetRemainingLength(stream) < 4) { + guac_client_log(client, GUAC_LOG_WARNING, + "Invalid value provided for AUDIO_INPUT version."); + return; + } + UINT32 version; Stream_Read_UINT32(stream, version); @@ -258,10 +268,13 @@ void guac_rdp_ai_process_formats(guac_client* client, guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; guac_rdp_audio_buffer* audio_buffer = rdp_client->audio_input; + if (Stream_GetRemainingLength(stream) < 8) + return; + UINT32 num_formats; Stream_Read_UINT32(stream, num_formats); /* NumFormats */ Stream_Seek_UINT32(stream); /* cbSizeFormatsPacket (MUST BE IGNORED) */ - + UINT32 index; for (index = 0; index < num_formats; index++) { @@ -306,6 +319,9 @@ void guac_rdp_ai_process_open(guac_client* client, guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; guac_rdp_audio_buffer* audio_buffer = rdp_client->audio_input; + if (Stream_GetRemainingLength(stream) < 8) + return; + UINT32 packet_frames; UINT32 initial_format; diff --git a/src/protocols/rdp/plugins/guacai/guacai.c b/src/protocols/rdp/plugins/guacai/guacai.c index 15de8655..5f2319d6 100644 --- a/src/protocols/rdp/plugins/guacai/guacai.c +++ b/src/protocols/rdp/plugins/guacai/guacai.c @@ -52,10 +52,13 @@ static void guac_rdp_ai_handle_data(guac_client* client, IWTSVirtualChannel* channel, wStream* stream) { + if (Stream_GetRemainingLength(stream) < 1) + return; + /* Read message ID from received PDU */ BYTE message_id; Stream_Read_UINT8(stream, message_id); - + /* Invoke appropriate message processor based on ID */ switch (message_id) {