Commit message: Fixed input sanitation in rdpgfx_recv_solid_fill_pdu

The input rectangle must be checked for plausibility.

Thanks to Sunglin and HuanGMz of the Knownsec 404 security team and pangzi of pwnzen
CVE: CVE-2020-15103
CWE: CWE-190
Vulnerability description: In FreeRDP less than or equal to 2.1.2, an integer overflow exists due to missing input sanitation in rdpegfx channel. All FreeRDP clients are affected. The input rectangles from the server are not checked against local surface coordinates and blindly accepted. A malicious server can send data that will crash the client later on (invalid length arguments to a `memcpy`) This has been fixed in 2.2.0. As a workaround, stop using command line arguments /gfx, /gfx-h264 and /network:auto

Project: FreeRDP
File: rdpgfx_common.c

UINT rdpgfx_read_header(wStream* s, RDPGFX_HEADER* header)
{
	if (Stream_GetRemainingLength(s) < 8)
	{
		WLog_ERR(TAG, "calloc failed!");
		return CHANNEL_RC_NO_MEMORY;
	}

	Stream_Read_UINT16(s, header->cmdId);     /* cmdId (2 bytes) */
	Stream_Read_UINT16(s, header->flags);     /* flags (2 bytes) */
	Stream_Read_UINT32(s, header->pduLength); /* pduLength (4 bytes) */
	return CHANNEL_RC_OK;
}

Project: FreeRDP
File: rdpgfx_common.c

UINT rdpgfx_write_header(wStream* s, const RDPGFX_HEADER* header)
{
	Stream_Write_UINT16(s, header->cmdId);     /* cmdId (2 bytes) */
	Stream_Write_UINT16(s, header->flags);     /* flags (2 bytes) */
	Stream_Write_UINT32(s, header->pduLength); /* pduLength (4 bytes) */
	return CHANNEL_RC_OK;
}

Project: FreeRDP
File: rdpgfx_common.c

const char* rdpgfx_get_cmd_id_string(UINT16 cmdId)
{
	if (cmdId <= RDPGFX_CMDID_MAPSURFACETOSCALEDWINDOW)
		return RDPGFX_CMDID_STRINGS[cmdId];
	else
		return "RDPGFX_CMDID_UNKNOWN";
}

Project: FreeRDP
File: rdpgfx_common.c

UINT rdpgfx_write_rect16(wStream* s, const RECTANGLE_16* rect16)
{
	Stream_Write_UINT16(s, rect16->left);   /* left (2 bytes) */
	Stream_Write_UINT16(s, rect16->top);    /* top (2 bytes) */
	Stream_Write_UINT16(s, rect16->right);  /* right (2 bytes) */
	Stream_Write_UINT16(s, rect16->bottom); /* bottom (2 bytes) */
	return CHANNEL_RC_OK;
}

Project: FreeRDP
File: rdpgfx_common.c

const char* rdpgfx_get_codec_id_string(UINT16 codecId)
{
	switch (codecId)
	{
		case RDPGFX_CODECID_UNCOMPRESSED:
			return "RDPGFX_CODECID_UNCOMPRESSED";

		case RDPGFX_CODECID_CAVIDEO:
			return "RDPGFX_CODECID_CAVIDEO";

		case RDPGFX_CODECID_CLEARCODEC:
			return "RDPGFX_CODECID_CLEARCODEC";

		case RDPGFX_CODECID_PLANAR:
			return "RDPGFX_CODECID_PLANAR";

		case RDPGFX_CODECID_AVC420:
			return "RDPGFX_CODECID_AVC420";

		case RDPGFX_CODECID_AVC444:
			return "RDPGFX_CODECID_AVC444";

		case RDPGFX_CODECID_AVC444v2:
			return "RDPGFX_CODECID_AVC444v2";

		case RDPGFX_CODECID_ALPHA:
			return "RDPGFX_CODECID_ALPHA";

		case RDPGFX_CODECID_CAPROGRESSIVE:
			return "RDPGFX_CODECID_CAPROGRESSIVE";

		case RDPGFX_CODECID_CAPROGRESSIVE_V2:
			return "RDPGFX_CODECID_CAPROGRESSIVE_V2";
	}

	return "RDPGFX_CODECID_UNKNOWN";
}

Project: FreeRDP
File: rdpgfx_common.c

UINT rdpgfx_write_color32(wStream* s, const RDPGFX_COLOR32* color32)
{
	Stream_Write_UINT8(s, color32->B);  /* B (1 byte) */
	Stream_Write_UINT8(s, color32->G);  /* G (1 byte) */
	Stream_Write_UINT8(s, color32->R);  /* R (1 byte) */
	Stream_Write_UINT8(s, color32->XA); /* XA (1 byte) */
	return CHANNEL_RC_OK;
}

Project: FreeRDP
File: rdpgfx_common.c

UINT rdpgfx_write_point16(wStream* s, const RDPGFX_POINT16* point16)
{
	Stream_Write_UINT16(s, point16->x); /* x (2 bytes) */
	Stream_Write_UINT16(s, point16->y); /* y (2 bytes) */
	return CHANNEL_RC_OK;
}

Project: FreeRDP
File: rdpgfx_common.c

UINT rdpgfx_read_color32(wStream* s, RDPGFX_COLOR32* color32)
{
	if (Stream_GetRemainingLength(s) < 4)
	{
		WLog_ERR(TAG, "not enough data!");
		return ERROR_INVALID_DATA;
	}

	Stream_Read_UINT8(s, color32->B);  /* B (1 byte) */
	Stream_Read_UINT8(s, color32->G);  /* G (1 byte) */
	Stream_Read_UINT8(s, color32->R);  /* R (1 byte) */
	Stream_Read_UINT8(s, color32->XA); /* XA (1 byte) */
	return CHANNEL_RC_OK;
}

Project: FreeRDP
File: rdpgfx_common.c

UINT rdpgfx_read_rect16(wStream* s, RECTANGLE_16* rect16)
{
	if (Stream_GetRemainingLength(s) < 8)
	{
		WLog_ERR(TAG, "not enough data!");
		return ERROR_INVALID_DATA;
	}

	Stream_Read_UINT16(s, rect16->left);   /* left (2 bytes) */
	Stream_Read_UINT16(s, rect16->top);    /* top (2 bytes) */
	Stream_Read_UINT16(s, rect16->right);  /* right (2 bytes) */
	Stream_Read_UINT16(s, rect16->bottom); /* bottom (2 bytes) */
	if (rect16->left >= rect16->right)
		return ERROR_INVALID_DATA;
	if (rect16->top >= rect16->bottom)
		return ERROR_INVALID_DATA;
	return CHANNEL_RC_OK;
}

Project: FreeRDP
File: rdpgfx_common.c

UINT rdpgfx_read_point16(wStream* s, RDPGFX_POINT16* pt16)
{
	if (Stream_GetRemainingLength(s) < 4)
	{
		WLog_ERR(TAG, "not enough data!");
		return ERROR_INVALID_DATA;
	}

	Stream_Read_UINT16(s, pt16->x); /* x (2 bytes) */
	Stream_Read_UINT16(s, pt16->y); /* y (2 bytes) */
	return CHANNEL_RC_OK;
}