FreeRDP/channels/cliprdr/client/cliprdr_format.c
Norbert Federa ef4b29e5b3 ConvertFromUnicode fixes and misc hardening
- Added missing ConvertFromUnicode checks
- If ConvertToUnicode allocates memory, guarantee the null termination
  similar to ConvertFromUnicode's implementation
- Fixed some TestUnicodeConversion.c CTest return values
- Added some CTests for ConvertFromUnicode and ConvertToUnicode
- Misc code and protocol hardening fixes in the surrounding code regions
  that have been touched
2016-03-03 16:56:19 +01:00

339 lines
8.5 KiB
C

/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Clipboard Virtual Channel
*
* Copyright 2009-2011 Jay Sorg
* Copyright 2010-2011 Vic Lee
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 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 <winpr/print.h>
#include <freerdp/types.h>
#include <freerdp/constants.h>
#include <freerdp/client/cliprdr.h>
#include "cliprdr_main.h"
#include "cliprdr_format.h"
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
{
UINT32 index;
UINT32 position;
BOOL asciiNames;
int formatNameLength;
char* szFormatName;
WCHAR* wszFormatName;
CLIPRDR_FORMAT* formats = NULL;
CLIPRDR_FORMAT_LIST formatList;
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
UINT error = CHANNEL_RC_OK;
if (!context->custom)
{
WLog_ERR(TAG, "context->custom not set!");
return ERROR_INTERNAL_ERROR;
}
asciiNames = (msgFlags & CB_ASCII_NAMES) ? TRUE : FALSE;
formatList.msgType = CB_FORMAT_LIST;
formatList.msgFlags = msgFlags;
formatList.dataLen = dataLen;
index = 0;
formatList.numFormats = 0;
position = Stream_GetPosition(s);
if (!formatList.dataLen)
{
/* empty format list */
formatList.formats = NULL;
formatList.numFormats = 0;
}
else if (!cliprdr->useLongFormatNames)
{
formatList.numFormats = (dataLen / 36);
if ((formatList.numFormats * 36) != dataLen)
{
WLog_ERR(TAG, "Invalid short format list length: %d", dataLen);
return ERROR_INTERNAL_ERROR;
}
if (formatList.numFormats)
formats = (CLIPRDR_FORMAT*) calloc(formatList.numFormats, sizeof(CLIPRDR_FORMAT));
if (!formats)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
formatList.formats = formats;
while (dataLen)
{
Stream_Read_UINT32(s, formats[index].formatId); /* formatId (4 bytes) */
dataLen -= 4;
formats[index].formatName = NULL;
/* According to MS-RDPECLIP 2.2.3.1.1.1 formatName is "a 32-byte block containing
* the *null-terminated* name assigned to the Clipboard Format: (32 ASCII 8 characters
* or 16 Unicode characters)"
* However, both Windows RDSH and mstsc violate this specs as seen in the following
* example of a transferred short format name string: [R.i.c.h. .T.e.x.t. .F.o.r.m.a.t.]
* These are 16 unicode charaters - *without* terminating null !
*/
if (asciiNames)
{
szFormatName = (char*) Stream_Pointer(s);
if (szFormatName[0])
{
/* ensure null termination */
formats[index].formatName = (char*) malloc(32 + 1);
if (!formats[index].formatName)
{
WLog_ERR(TAG, "malloc failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
CopyMemory(formats[index].formatName, szFormatName, 32);
formats[index].formatName[32] = '\0';
}
}
else
{
wszFormatName = (WCHAR*) Stream_Pointer(s);
if (wszFormatName[0])
{
/* ConvertFromUnicode always returns a null-terminated
* string on success, even if the source string isn't.
*/
if (ConvertFromUnicode(CP_UTF8, 0, wszFormatName, 16,
&(formats[index].formatName), 0, NULL, NULL) < 1)
{
WLog_ERR(TAG, "failed to convert short clipboard format name");
error = ERROR_INTERNAL_ERROR;
goto error_out;
}
}
}
Stream_Seek(s, 32);
dataLen -= 32;
index++;
}
}
else
{
while (dataLen)
{
Stream_Seek(s, 4); /* formatId (4 bytes) */
dataLen -= 4;
wszFormatName = (WCHAR*) Stream_Pointer(s);
if (!wszFormatName[0])
formatNameLength = 0;
else
formatNameLength = _wcslen(wszFormatName);
Stream_Seek(s, (formatNameLength + 1) * 2);
dataLen -= ((formatNameLength + 1) * 2);
formatList.numFormats++;
}
dataLen = formatList.dataLen;
Stream_SetPosition(s, position);
if (formatList.numFormats)
formats = (CLIPRDR_FORMAT*) calloc(formatList.numFormats, sizeof(CLIPRDR_FORMAT));
if (!formats)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
formatList.formats = formats;
while (dataLen)
{
Stream_Read_UINT32(s, formats[index].formatId); /* formatId (4 bytes) */
dataLen -= 4;
formats[index].formatName = NULL;
wszFormatName = (WCHAR*) Stream_Pointer(s);
if (!wszFormatName[0])
formatNameLength = 0;
else
formatNameLength = _wcslen(wszFormatName);
if (formatNameLength)
{
if (ConvertFromUnicode(CP_UTF8, 0, wszFormatName, -1,
&(formats[index].formatName), 0, NULL, NULL) < 1)
{
WLog_ERR(TAG, "failed to convert long clipboard format name");
error = ERROR_INTERNAL_ERROR;
goto error_out;
}
}
Stream_Seek(s, (formatNameLength + 1) * 2);
dataLen -= ((formatNameLength + 1) * 2);
index++;
}
}
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatList: numFormats: %d",
formatList.numFormats);
if (context->ServerFormatList)
{
if ((error = context->ServerFormatList(context, &formatList)))
WLog_ERR(TAG, "ServerFormatList failed with error %d", error);
}
error_out:
if (formats)
{
for (index = 0; index < formatList.numFormats; index++)
{
free(formats[index].formatName);
}
free(formats);
}
return error;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
{
CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse;
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
UINT error = CHANNEL_RC_OK;
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatListResponse");
if (!context->custom)
{
WLog_ERR(TAG, "context->custom not set!");
return ERROR_INTERNAL_ERROR;
}
formatListResponse.msgType = CB_FORMAT_LIST_RESPONSE;
formatListResponse.msgFlags = msgFlags;
formatListResponse.dataLen = dataLen;
IFCALLRET(context->ServerFormatListResponse, error, context, &formatListResponse);
if (error)
WLog_ERR(TAG, "ServerFormatListResponse failed with error %lu!", error);
return error;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
{
CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest;
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
UINT error = CHANNEL_RC_OK;
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatDataRequest");
if (!context->custom)
{
WLog_ERR(TAG, "context->custom not set!");
return ERROR_INTERNAL_ERROR;
}
formatDataRequest.msgType = CB_FORMAT_DATA_REQUEST;
formatDataRequest.msgFlags = msgFlags;
formatDataRequest.dataLen = dataLen;
Stream_Read_UINT32(s, formatDataRequest.requestedFormatId); /* requestedFormatId (4 bytes) */
IFCALLRET(context->ServerFormatDataRequest, error, context, &formatDataRequest);
if (error)
WLog_ERR(TAG, "ServerFormatDataRequest failed with error %lu!", error);
return error;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
{
CLIPRDR_FORMAT_DATA_RESPONSE formatDataResponse;
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
UINT error = CHANNEL_RC_OK;
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatDataResponse");
if (!context->custom)
{
WLog_ERR(TAG, "context->custom not set!");
return ERROR_INTERNAL_ERROR;
}
formatDataResponse.msgType = CB_FORMAT_DATA_RESPONSE;
formatDataResponse.msgFlags = msgFlags;
formatDataResponse.dataLen = dataLen;
formatDataResponse.requestedFormatData = NULL;
if (dataLen)
formatDataResponse.requestedFormatData = (BYTE*) Stream_Pointer(s);
IFCALLRET(context->ServerFormatDataResponse, error, context, &formatDataResponse);
if (error)
WLog_ERR(TAG, "ServerFormatDataResponse failed with error %lu!", error);
return error;
}