From 98fb56b767cc32478c1e68c4f22768fc17183914 Mon Sep 17 00:00:00 2001 From: akallabeth Date: Tue, 9 May 2023 12:51:01 +0200 Subject: [PATCH] [channels,cliprdr] implement client clipboard direction filter * Filter remote -> local or local -> remote clipboard depending on setting. * Filter remote -> local or local -> remote file clipboard depending on setting. --- channels/cliprdr/client/cliprdr_format.c | 87 +++++++++++++++++ channels/cliprdr/client/cliprdr_main.c | 115 ++++++++++++++++++++++- channels/cliprdr/client/cliprdr_main.h | 3 + channels/cliprdr/cliprdr_common.c | 2 +- 4 files changed, 205 insertions(+), 2 deletions(-) diff --git a/channels/cliprdr/client/cliprdr_format.c b/channels/cliprdr/client/cliprdr_format.c index 12eeb0cdc..4654e0f01 100644 --- a/channels/cliprdr/client/cliprdr_format.c +++ b/channels/cliprdr/client/cliprdr_format.c @@ -26,6 +26,8 @@ #include #include +#include +#include #include #include @@ -33,6 +35,70 @@ #include "cliprdr_format.h" #include "../cliprdr_common.h" +static BOOL cliprdr_filter_server_format_list(CLIPRDR_FORMAT_LIST* list, const UINT32 mask) +{ + const UINT32 all = CLIPRDR_FLAG_REMOTE_TO_LOCAL | CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES; + WINPR_ASSERT(list); + + if ((mask & all) == all) + return TRUE; + + if ((mask & CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES) != 0) + { + const CLIPRDR_FORMAT* files = NULL; + for (size_t x = 0; x < list->numFormats; x++) + { + CLIPRDR_FORMAT* format = &list->formats[x]; + + if (!format->formatName) + continue; + if (strcmp(format->formatName, type_FileGroupDescriptorW) == 0) + files = format; + else + { + free(format->formatName); + format->formatName = NULL; + } + } + + if (!files) + list->numFormats = 0; + else + { + list->numFormats = 1; + list->formats[0] = *files; + } + return TRUE; + } + + if ((mask & CLIPRDR_FLAG_REMOTE_TO_LOCAL) != 0) + { + BOOL move = FALSE; + for (size_t x = 0; x < list->numFormats; x++) + { + CLIPRDR_FORMAT* format = &list->formats[x]; + + if (move) + { + CLIPRDR_FORMAT* last = &list->formats[x - 1]; + *last = *format; + } + else if (!format->formatName) + continue; + else if (strcmp(format->formatName, type_FileGroupDescriptorW) == 0) + { + move = TRUE; + free(format->formatName); + format->formatName = NULL; + } + } + if (move) + list->numFormats -= 1; + return TRUE; + } + return FALSE; +} + /** * Function description * @@ -52,6 +118,11 @@ UINT cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 data if ((error = cliprdr_read_format_list(s, &formatList, cliprdr->useLongFormatNames))) goto error_out; + const UINT32 mask = + freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask); + if (!cliprdr_filter_server_format_list(&formatList, mask)) + goto error_out; + WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatList: numFormats: %" PRIu32 "", formatList.numFormats); @@ -112,6 +183,13 @@ UINT cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UIN if ((error = cliprdr_read_format_data_request(s, &formatDataRequest))) return error; + const UINT32 mask = + freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask); + if ((mask & (CLIPRDR_FLAG_LOCAL_TO_REMOTE | CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES)) == 0) + { + return cliprdr_send_error_response(cliprdr, CB_FORMAT_DATA_RESPONSE); + } + context->lastRequestedFormatId = formatDataRequest.requestedFormatId; IFCALLRET(context->ServerFormatDataRequest, error, context, &formatDataRequest); if (error) @@ -141,6 +219,15 @@ UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UI if ((error = cliprdr_read_format_data_response(s, &formatDataResponse))) return error; + const UINT32 mask = + freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask); + if ((mask & (CLIPRDR_FLAG_REMOTE_TO_LOCAL | CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES)) == 0) + { + WLog_WARN(TAG, + "Received ServerFormatDataResponse but remote -> local clipboard is disabled"); + return CHANNEL_RC_OK; + } + IFCALLRET(context->ServerFormatDataResponse, error, context, &formatDataResponse); if (error) WLog_ERR(TAG, "ServerFormatDataResponse failed with error %" PRIu32 "!", error); diff --git a/channels/cliprdr/client/cliprdr_main.c b/channels/cliprdr/client/cliprdr_main.c index e393a2a60..57ac68082 100644 --- a/channels/cliprdr/client/cliprdr_main.c +++ b/channels/cliprdr/client/cliprdr_main.c @@ -38,6 +38,8 @@ #include "cliprdr_format.h" #include "../cliprdr_common.h" +const char* type_FileGroupDescriptorW = "FileGroupDescriptorW"; + static const char* CB_MSG_TYPE_STRINGS(UINT32 type) { switch (type) @@ -124,6 +126,18 @@ static UINT cliprdr_packet_send(cliprdrPlugin* cliprdr, wStream* s) return status; } +UINT cliprdr_send_error_response(cliprdrPlugin* cliprdr, UINT16 type) +{ + wStream* s = cliprdr_packet_new(type, CB_RESPONSE_FAIL, 0); + if (!s) + { + WLog_ERR(TAG, "cliprdr_packet_new failed!"); + return ERROR_OUTOFMEMORY; + } + + return cliprdr_packet_send(cliprdr, s); +} + static void cliprdr_print_general_capability_flags(UINT32 flags) { WLog_DBG(TAG, "generalFlags (0x%08" PRIX32 ") {", flags); @@ -324,6 +338,13 @@ static UINT cliprdr_process_filecontents_request(cliprdrPlugin* cliprdr, wStream if ((error = cliprdr_read_file_contents_request(s, &request))) return error; + const UINT32 mask = + freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask); + if ((mask & (CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES)) == 0) + { + WLog_WARN(TAG, "local -> remote file copy disabled, ignoring request"); + return cliprdr_send_error_response(cliprdr, CB_FILECONTENTS_RESPONSE); + } IFCALLRET(context->ServerFileContentsRequest, error, context, &request); if (error) @@ -639,6 +660,66 @@ static UINT cliprdr_temp_directory(CliprdrClientContext* context, return cliprdr_packet_send(cliprdr, s); } +static CLIPRDR_FORMAT_LIST cliprdr_filter_local_format_list(const CLIPRDR_FORMAT_LIST* list, + const UINT32 mask) +{ + const UINT32 all = CLIPRDR_FLAG_LOCAL_TO_REMOTE | CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES; + WINPR_ASSERT(list); + + CLIPRDR_FORMAT_LIST filtered = { 0 }; + filtered.numFormats = list->numFormats; + filtered.formats = calloc(filtered.numFormats, sizeof(CLIPRDR_FORMAT_LIST)); + + size_t wpos = 0; + if ((mask & all) == all) + { + for (size_t x = 0; x < list->numFormats; x++) + { + const CLIPRDR_FORMAT* format = &list->formats[x]; + CLIPRDR_FORMAT* cur = &filtered.formats[x]; + cur->formatId = format->formatId; + if (format->formatName) + cur->formatName = _strdup(format->formatName); + wpos++; + } + } + else if ((mask & CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES) != 0) + { + for (size_t x = 0; x < list->numFormats; x++) + { + const CLIPRDR_FORMAT* format = &list->formats[x]; + CLIPRDR_FORMAT* cur = &filtered.formats[wpos]; + + if (!format->formatName) + continue; + if (strcmp(format->formatName, type_FileGroupDescriptorW) == 0) + { + cur->formatId = format->formatId; + cur->formatName = _strdup(format->formatName); + wpos++; + } + } + } + else if ((mask & CLIPRDR_FLAG_LOCAL_TO_REMOTE) != 0) + { + for (size_t x = 0; x < list->numFormats; x++) + { + const CLIPRDR_FORMAT* format = &list->formats[x]; + CLIPRDR_FORMAT* cur = &filtered.formats[wpos]; + + if (!format->formatName || (strcmp(format->formatName, type_FileGroupDescriptorW) != 0)) + { + cur->formatId = format->formatId; + if (format->formatName) + cur->formatName = _strdup(format->formatName); + wpos++; + } + } + } + filtered.numFormats = wpos; + return filtered; +} + /** * Function description * @@ -655,7 +736,15 @@ static UINT cliprdr_client_format_list(CliprdrClientContext* context, cliprdr = (cliprdrPlugin*)context->handle; WINPR_ASSERT(cliprdr); - s = cliprdr_packet_format_list_new(formatList, cliprdr->useLongFormatNames); + const UINT32 mask = + freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask); + CLIPRDR_FORMAT_LIST filterList = cliprdr_filter_local_format_list(formatList, mask); + if (filterList.numFormats == 0) + return CHANNEL_RC_OK; + + s = cliprdr_packet_format_list_new(&filterList, cliprdr->useLongFormatNames); + cliprdr_free_format_list(&filterList); + if (!s) { WLog_ERR(TAG, "cliprdr_packet_format_list_new failed!"); @@ -775,6 +864,13 @@ static UINT cliprdr_client_format_data_request(CliprdrClientContext* context, cliprdr = (cliprdrPlugin*)context->handle; WINPR_ASSERT(cliprdr); + const UINT32 mask = + freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask); + if ((mask & (CLIPRDR_FLAG_REMOTE_TO_LOCAL | CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES)) == 0) + { + WLog_WARN(TAG, "remote -> local copy disabled, ignoring request"); + return CHANNEL_RC_OK; + } s = cliprdr_packet_new(CB_FORMAT_DATA_REQUEST, 0, 4); if (!s) @@ -806,6 +902,10 @@ cliprdr_client_format_data_response(CliprdrClientContext* context, cliprdr = (cliprdrPlugin*)context->handle; WINPR_ASSERT(cliprdr); + const UINT32 mask = + freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask); + WINPR_ASSERT((mask & (CLIPRDR_FLAG_LOCAL_TO_REMOTE | CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES)) != 0); + s = cliprdr_packet_new(CB_FORMAT_DATA_RESPONSE, formatDataResponse->common.msgFlags, formatDataResponse->common.dataLen); @@ -835,6 +935,14 @@ cliprdr_client_file_contents_request(CliprdrClientContext* context, WINPR_ASSERT(context); WINPR_ASSERT(fileContentsRequest); + const UINT32 mask = + freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask); + if ((mask & CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES) == 0) + { + WLog_WARN(TAG, "remote -> local file copy disabled, ignoring request"); + return CHANNEL_RC_OK; + } + cliprdr = (cliprdrPlugin*)context->handle; if (!cliprdr) return ERROR_INTERNAL_ERROR; @@ -879,6 +987,11 @@ cliprdr_client_file_contents_response(CliprdrClientContext* context, cliprdr = (cliprdrPlugin*)context->handle; WINPR_ASSERT(cliprdr); + const UINT32 mask = + freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask); + if ((mask & CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES) == 0) + return cliprdr_send_error_response(cliprdr, CB_FILECONTENTS_RESPONSE); + s = cliprdr_packet_file_contents_response_new(fileContentsResponse); if (!s) diff --git a/channels/cliprdr/client/cliprdr_main.h b/channels/cliprdr/client/cliprdr_main.h index 7da245069..8763727e3 100644 --- a/channels/cliprdr/client/cliprdr_main.h +++ b/channels/cliprdr/client/cliprdr_main.h @@ -52,5 +52,8 @@ typedef struct } cliprdrPlugin; CliprdrClientContext* cliprdr_get_client_interface(cliprdrPlugin* cliprdr); +UINT cliprdr_send_error_response(cliprdrPlugin* cliprdr, UINT16 type); + +extern const char* type_FileGroupDescriptorW; #endif /* FREERDP_CHANNEL_CLIPRDR_CLIENT_MAIN_H */ diff --git a/channels/cliprdr/cliprdr_common.c b/channels/cliprdr/cliprdr_common.c index eab1d46eb..22b0314bd 100644 --- a/channels/cliprdr/cliprdr_common.c +++ b/channels/cliprdr/cliprdr_common.c @@ -72,7 +72,7 @@ wStream* cliprdr_packet_new(UINT16 msgType, UINT16 msgFlags, UINT32 dataLen) Stream_Write_UINT16(s, msgType); Stream_Write_UINT16(s, msgFlags); /* Write actual length after the entire packet has been constructed. */ - Stream_Seek(s, 4); + Stream_Write_UINT32(s, 0); return s; }