rdesktop/orders.c

1426 lines
33 KiB
C
Raw Normal View History

/* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client.
RDP order processing
Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "rdesktop.h"
#include "orders.h"
extern size_t g_next_packet;
static RDP_ORDER_STATE g_order_state;
extern RDP_VERSION g_rdp_version;
/* Read field indicating which parameters are present */
static void rdp_in_present(STREAM s, uint32 *present, uint8 flags, int size)
{
uint8 bits;
int i;
if (flags & RDP_ORDER_SMALL)
{
size--;
}
if (flags & RDP_ORDER_TINY)
{
if (size < 2)
size = 0;
else
size -= 2;
}
*present = 0;
for (i = 0; i < size; i++)
{
in_uint8(s, bits);
*present |= bits << (i * 8);
}
}
/* Read a co-ordinate (16-bit, or 8-bit delta) */
static void rdp_in_coord(STREAM s, sint16 *coord, RD_BOOL delta)
{
sint8 change;
if (delta)
{
in_uint8(s, change);
*coord += change;
}
else
{
in_uint16_le(s, *coord);
}
}
/* Parse a delta co-ordinate in polyline/polygon order form */
static int parse_delta(uint8 *buffer, int *offset)
{
int value = buffer[(*offset)++];
int two_byte = value & 0x80;
if (value & 0x40) /* sign bit */
value |= ~0x3f;
else
value &= 0x3f;
if (two_byte)
value = (value << 8) | buffer[(*offset)++];
return value;
}
/* Read a colour entry */
static void rdp_in_colour(STREAM s, uint32 *colour)
{
uint32 i;
in_uint8(s, i);
*colour = i;
in_uint8(s, i);
*colour |= i << 8;
in_uint8(s, i);
*colour |= i << 16;
}
/* Parse bounds information */
static void rdp_parse_bounds(STREAM s, BOUNDS *bounds)
{
uint8 present;
in_uint8(s, present);
if (present & 1)
rdp_in_coord(s, &bounds->left, False);
else if (present & 16)
rdp_in_coord(s, &bounds->left, True);
if (present & 2)
rdp_in_coord(s, &bounds->top, False);
else if (present & 32)
rdp_in_coord(s, &bounds->top, True);
if (present & 4)
rdp_in_coord(s, &bounds->right, False);
else if (present & 64)
rdp_in_coord(s, &bounds->right, True);
if (present & 8)
rdp_in_coord(s, &bounds->bottom, False);
else if (present & 128)
rdp_in_coord(s, &bounds->bottom, True);
}
/* Parse a pen */
static void rdp_parse_pen(STREAM s, PEN *pen, uint32 present)
{
if (present & 1)
in_uint8(s, pen->style);
if (present & 2)
in_uint8(s, pen->width);
if (present & 4)
rdp_in_colour(s, &pen->colour);
}
static void setup_brush(BRUSH *out_brush, BRUSH *in_brush)
{
BRUSHDATA *brush_data;
uint8 cache_idx;
uint8 colour_code;
memcpy(out_brush, in_brush, sizeof(BRUSH));
if (out_brush->style & 0x80)
{
colour_code = out_brush->style & 0x0f;
cache_idx = out_brush->pattern[0];
brush_data = cache_get_brush_data(colour_code, cache_idx);
if ((brush_data == NULL) || (brush_data->data == NULL))
{
logger(Graphics, Error, "setup_brush(), error getting brush data, style %x", out_brush->style);
out_brush->bd = NULL;
memset(out_brush->pattern, 0, 8);
}
else
{
out_brush->bd = brush_data;
}
out_brush->style = 3;
}
}
/* Parse a brush */
static void rdp_parse_brush(STREAM s, BRUSH *brush, uint32 present)
{
if (present & 1)
in_uint8(s, brush->xorigin);
if (present & 2)
in_uint8(s, brush->yorigin);
if (present & 4)
in_uint8(s, brush->style);
if (present & 8)
in_uint8(s, brush->pattern[0]);
if (present & 16)
in_uint8a(s, &brush->pattern[1], 7);
}
/* Process a destination blt order */
static void process_destblt(STREAM s, DESTBLT_ORDER *os, uint32 present, RD_BOOL delta)
{
if (present & 0x01)
rdp_in_coord(s, &os->x, delta);
if (present & 0x02)
rdp_in_coord(s, &os->y, delta);
if (present & 0x04)
rdp_in_coord(s, &os->cx, delta);
if (present & 0x08)
rdp_in_coord(s, &os->cy, delta);
if (present & 0x10)
in_uint8(s, os->opcode);
logger(Graphics, Debug, "process_destblt(), op=0x%x, x=%d, y=%d, cx=%d, cy=%d",
os->opcode, os->x, os->y, os->cx, os->cy);
ui_destblt(ROP2_S(os->opcode), os->x, os->y, os->cx, os->cy);
}
/* Process a pattern blt order */
static void process_patblt(STREAM s, PATBLT_ORDER *os, uint32 present, RD_BOOL delta)
{
BRUSH brush;
if (present & 0x0001)
rdp_in_coord(s, &os->x, delta);
if (present & 0x0002)
rdp_in_coord(s, &os->y, delta);
if (present & 0x0004)
rdp_in_coord(s, &os->cx, delta);
if (present & 0x0008)
rdp_in_coord(s, &os->cy, delta);
if (present & 0x0010)
in_uint8(s, os->opcode);
if (present & 0x0020)
rdp_in_colour(s, &os->bgcolour);
if (present & 0x0040)
rdp_in_colour(s, &os->fgcolour);
rdp_parse_brush(s, &os->brush, present >> 7);
logger(Graphics, Debug,
"process_patblt(), op=0x%x, x=%d, y=%d, cx=%d, cy=%d, bs=%d, bg=0x%x, fg=0x%x)",
os->opcode, os->x, os->y, os->cx, os->cy, os->brush.style, os->bgcolour, os->fgcolour);
setup_brush(&brush, &os->brush);
ui_patblt(ROP2_P(os->opcode), os->x, os->y, os->cx, os->cy, &brush, os->bgcolour, os->fgcolour);
}
/* Process a screen blt order */
static void process_screenblt(STREAM s, SCREENBLT_ORDER *os, uint32 present, RD_BOOL delta)
{
if (present & 0x0001)
rdp_in_coord(s, &os->x, delta);
if (present & 0x0002)
rdp_in_coord(s, &os->y, delta);
if (present & 0x0004)
rdp_in_coord(s, &os->cx, delta);
if (present & 0x0008)
rdp_in_coord(s, &os->cy, delta);
if (present & 0x0010)
in_uint8(s, os->opcode);
if (present & 0x0020)
rdp_in_coord(s, &os->srcx, delta);
if (present & 0x0040)
rdp_in_coord(s, &os->srcy, delta);
logger(Graphics, Debug,
"process_screenblt(), op=0x%x, x=%d, y=%d, cx=%d, cy=%d, srcx=%d, srcy=%d)",
os->opcode, os->x, os->y, os->cx, os->cy, os->srcx, os->srcy);
ui_screenblt(ROP2_S(os->opcode), os->x, os->y, os->cx, os->cy, os->srcx, os->srcy);
}
/* Process a line order */
static void process_line(STREAM s, LINE_ORDER *os, uint32 present, RD_BOOL delta)
{
if (present & 0x0001)
in_uint16_le(s, os->mixmode);
if (present & 0x0002)
rdp_in_coord(s, &os->startx, delta);
if (present & 0x0004)
rdp_in_coord(s, &os->starty, delta);
if (present & 0x0008)
rdp_in_coord(s, &os->endx, delta);
if (present & 0x0010)
rdp_in_coord(s, &os->endy, delta);
if (present & 0x0020)
rdp_in_colour(s, &os->bgcolour);
if (present & 0x0040)
in_uint8(s, os->opcode);
rdp_parse_pen(s, &os->pen, present >> 7);
logger(Graphics, Debug, "process_line(), op=0x%x, sx=%d, sy=%d, dx=%d, dy=%d, fg=0x%x)",
os->opcode, os->startx, os->starty, os->endx, os->endy, os->pen.colour);
if (os->opcode < 0x01 || os->opcode > 0x10)
{
logger(Graphics, Error, "process_line(), bad ROP2 0x%x", os->opcode);
return;
}
ui_line(os->opcode - 1, os->startx, os->starty, os->endx, os->endy, &os->pen);
}
/* Process an opaque rectangle order */
static void process_rect(STREAM s, RECT_ORDER *os, uint32 present, RD_BOOL delta)
{
uint32 i;
if (present & 0x01)
rdp_in_coord(s, &os->x, delta);
if (present & 0x02)
rdp_in_coord(s, &os->y, delta);
if (present & 0x04)
rdp_in_coord(s, &os->cx, delta);
if (present & 0x08)
rdp_in_coord(s, &os->cy, delta);
if (present & 0x10)
{
in_uint8(s, i);
os->colour = (os->colour & 0xffffff00) | i;
}
if (present & 0x20)
{
in_uint8(s, i);
os->colour = (os->colour & 0xffff00ff) | (i << 8);
}
if (present & 0x40)
{
in_uint8(s, i);
os->colour = (os->colour & 0xff00ffff) | (i << 16);
}
logger(Graphics, Debug, "process_rect(), x=%d, y=%d, cx=%d, cy=%d, fg=0x%x",
os->x, os->y, os->cx, os->cy, os->colour);
ui_rect(os->x, os->y, os->cx, os->cy, os->colour);
}
/* Process a desktop save order */
static void process_desksave(STREAM s, DESKSAVE_ORDER *os, uint32 present, RD_BOOL delta)
{
int width, height;
if (present & 0x01)
in_uint32_le(s, os->offset);
if (present & 0x02)
rdp_in_coord(s, &os->left, delta);
if (present & 0x04)
rdp_in_coord(s, &os->top, delta);
if (present & 0x08)
rdp_in_coord(s, &os->right, delta);
if (present & 0x10)
rdp_in_coord(s, &os->bottom, delta);
if (present & 0x20)
in_uint8(s, os->action);
logger(Graphics, Debug, "process_desksave(), l=%d, t=%d, r=%d, b=%d, off=%d, op=%d",
os->left, os->top, os->right, os->bottom, os->offset, os->action);
width = os->right - os->left + 1;
height = os->bottom - os->top + 1;
if (os->action == 0)
ui_desktop_save(os->offset, os->left, os->top, width, height);
else
ui_desktop_restore(os->offset, os->left, os->top, width, height);
}
/* Process a memory blt order */
static void process_memblt(STREAM s, MEMBLT_ORDER *os, uint32 present, RD_BOOL delta)
{
RD_HBITMAP bitmap;
if (present & 0x0001)
{
in_uint8(s, os->cache_id);
in_uint8(s, os->colour_table);
}
if (present & 0x0002)
rdp_in_coord(s, &os->x, delta);
if (present & 0x0004)
rdp_in_coord(s, &os->y, delta);
if (present & 0x0008)
rdp_in_coord(s, &os->cx, delta);
if (present & 0x0010)
rdp_in_coord(s, &os->cy, delta);
if (present & 0x0020)
in_uint8(s, os->opcode);
if (present & 0x0040)
rdp_in_coord(s, &os->srcx, delta);
if (present & 0x0080)
rdp_in_coord(s, &os->srcy, delta);
if (present & 0x0100)
in_uint16_le(s, os->cache_idx);
logger(Graphics, Debug,
"process_memblt(), op=0x%x, x=%d, y=%d, cx=%d, cy=%d, id=%d, idx=%d", os->opcode,
os->x, os->y, os->cx, os->cy, os->cache_id, os->cache_idx);
bitmap = cache_get_bitmap(os->cache_id, os->cache_idx);
if (bitmap == NULL)
return;
ui_memblt(ROP2_S(os->opcode), os->x, os->y, os->cx, os->cy, bitmap, os->srcx, os->srcy);
}
/* Process a 3-way blt order */
static void process_triblt(STREAM s, TRIBLT_ORDER *os, uint32 present, RD_BOOL delta)
{
RD_HBITMAP bitmap;
BRUSH brush;
if (present & 0x000001)
{
in_uint8(s, os->cache_id);
in_uint8(s, os->colour_table);
}
if (present & 0x000002)
rdp_in_coord(s, &os->x, delta);
if (present & 0x000004)
rdp_in_coord(s, &os->y, delta);
if (present & 0x000008)
rdp_in_coord(s, &os->cx, delta);
if (present & 0x000010)
rdp_in_coord(s, &os->cy, delta);
if (present & 0x000020)
in_uint8(s, os->opcode);
if (present & 0x000040)
rdp_in_coord(s, &os->srcx, delta);
if (present & 0x000080)
rdp_in_coord(s, &os->srcy, delta);
if (present & 0x000100)
rdp_in_colour(s, &os->bgcolour);
if (present & 0x000200)
rdp_in_colour(s, &os->fgcolour);
rdp_parse_brush(s, &os->brush, present >> 10);
if (present & 0x008000)
in_uint16_le(s, os->cache_idx);
if (present & 0x010000)
in_uint16_le(s, os->unknown);
logger(Graphics, Debug,
"process_triblt(), op=0x%x, x=%d, y=%d, cx=%d, cy=%d, id=%d, idx=%d, bs=%d, bg=0x%x, fg=0x%x",
os->opcode, os->x, os->y, os->cx, os->cy, os->cache_id, os->cache_idx,
os->brush.style, os->bgcolour, os->fgcolour);
bitmap = cache_get_bitmap(os->cache_id, os->cache_idx);
if (bitmap == NULL)
return;
setup_brush(&brush, &os->brush);
ui_triblt(os->opcode, os->x, os->y, os->cx, os->cy, bitmap, os->srcx, os->srcy, &brush, os->bgcolour, os->fgcolour);
}
/* Process a polygon order */
static void process_polygon(STREAM s, POLYGON_ORDER *os, uint32 present, RD_BOOL delta)
{
int index, data, next;
uint8 flags = 0;
RD_POINT *points;
if (present & 0x01)
rdp_in_coord(s, &os->x, delta);
if (present & 0x02)
rdp_in_coord(s, &os->y, delta);
if (present & 0x04)
in_uint8(s, os->opcode);
if (present & 0x08)
in_uint8(s, os->fillmode);
if (present & 0x10)
rdp_in_colour(s, &os->fgcolour);
if (present & 0x20)
in_uint8(s, os->npoints);
if (present & 0x40)
{
in_uint8(s, os->datasize);
in_uint8a(s, os->data, os->datasize);
}
logger(Graphics, Debug,
"process_polygon(), x=%d, y=%d, op=0x%x, fm=%d, fg=0x%x, n=%d, sz=%d", os->x, os->y,
os->opcode, os->fillmode, os->fgcolour, os->npoints, os->datasize);
if (os->opcode < 0x01 || os->opcode > 0x10)
{
logger(Graphics, Error, "process_polygon(), bad ROP2 0x%x", os->opcode);
return;
}
points = (RD_POINT *)xmalloc((os->npoints + 1) * sizeof(RD_POINT));
memset(points, 0, (os->npoints + 1) * sizeof(RD_POINT));
points[0].x = os->x;
points[0].y = os->y;
index = 0;
data = ((os->npoints - 1) / 4) + 1;
for (next = 1; (next <= os->npoints) && (next < 256) && (data < os->datasize); next++)
{
if ((next - 1) % 4 == 0)
flags = os->data[index++];
if (~flags & 0x80)
points[next].x = parse_delta(os->data, &data);
if (~flags & 0x40)
points[next].y = parse_delta(os->data, &data);
flags <<= 2;
}
if (next - 1 == os->npoints)
ui_polygon(os->opcode - 1, os->fillmode, points, os->npoints + 1, NULL, 0,
os->fgcolour);
else
logger(Graphics, Error, "process_polygon(), polygon parse error");
xfree(points);
}
/* Process a polygon2 order */
static void process_polygon2(STREAM s, POLYGON2_ORDER *os, uint32 present, RD_BOOL delta)
{
int index, data, next;
uint8 flags = 0;
RD_POINT *points;
BRUSH brush;
if (present & 0x0001)
rdp_in_coord(s, &os->x, delta);
if (present & 0x0002)
rdp_in_coord(s, &os->y, delta);
if (present & 0x0004)
in_uint8(s, os->opcode);
if (present & 0x0008)
in_uint8(s, os->fillmode);
if (present & 0x0010)
rdp_in_colour(s, &os->bgcolour);
if (present & 0x0020)
rdp_in_colour(s, &os->fgcolour);
rdp_parse_brush(s, &os->brush, present >> 6);
if (present & 0x0800)
in_uint8(s, os->npoints);
if (present & 0x1000)
{
in_uint8(s, os->datasize);
in_uint8a(s, os->data, os->datasize);
}
logger(Graphics, Debug,
"process_polygon2(), x=%d, y=%d, op=0x%x, fm=%d, bs=%d, bg=0x%x, fg=0x%x, n=%d, sz=%d)",
os->x, os->y, os->opcode, os->fillmode, os->brush.style, os->bgcolour, os->fgcolour,
os->npoints, os->datasize);
if (os->opcode < 0x01 || os->opcode > 0x10)
{
logger(Graphics, Error, "process_polygon2(), bad ROP2 0x%x", os->opcode);
return;
}
setup_brush(&brush, &os->brush);
points = (RD_POINT *)xmalloc((os->npoints + 1) * sizeof(RD_POINT));
memset(points, 0, (os->npoints + 1) * sizeof(RD_POINT));
points[0].x = os->x;
points[0].y = os->y;
index = 0;
data = ((os->npoints - 1) / 4) + 1;
for (next = 1; (next <= os->npoints) && (next < 256) && (data < os->datasize); next++)
{
if ((next - 1) % 4 == 0)
flags = os->data[index++];
if (~flags & 0x80)
points[next].x = parse_delta(os->data, &data);
if (~flags & 0x40)
points[next].y = parse_delta(os->data, &data);
flags <<= 2;
}
if (next - 1 == os->npoints)
ui_polygon(os->opcode - 1, os->fillmode, points, os->npoints + 1,
&brush, os->bgcolour, os->fgcolour);
else
logger(Graphics, Error, "process_polygon2(), polygon parse error");
xfree(points);
}
/* Process a polyline order */
static void process_polyline(STREAM s, POLYLINE_ORDER *os, uint32 present, RD_BOOL delta)
{
int index, next, data;
uint8 flags = 0;
PEN pen;
RD_POINT *points;
if (present & 0x01)
rdp_in_coord(s, &os->x, delta);
if (present & 0x02)
rdp_in_coord(s, &os->y, delta);
if (present & 0x04)
in_uint8(s, os->opcode);
if (present & 0x10)
rdp_in_colour(s, &os->fgcolour);
if (present & 0x20)
in_uint8(s, os->lines);
if (present & 0x40)
{
in_uint8(s, os->datasize);
in_uint8a(s, os->data, os->datasize);
}
logger(Graphics, Debug, "process_polyline(), x=%d, y=%d, op=0x%x, fg=0x%x, n=%d, sz=%d)",
os->x, os->y, os->opcode, os->fgcolour, os->lines, os->datasize);
if (os->opcode < 0x01 || os->opcode > 0x10)
{
logger(Graphics, Error, "process_polyline(), bad ROP2 0x%x", os->opcode);
return;
}
points = (RD_POINT *)xmalloc((os->lines + 1) * sizeof(RD_POINT));
memset(points, 0, (os->lines + 1) * sizeof(RD_POINT));
points[0].x = os->x;
points[0].y = os->y;
pen.style = pen.width = 0;
pen.colour = os->fgcolour;
index = 0;
data = ((os->lines - 1) / 4) + 1;
for (next = 1; (next <= os->lines) && (data < os->datasize); next++)
{
if ((next - 1) % 4 == 0)
flags = os->data[index++];
if (~flags & 0x80)
points[next].x = parse_delta(os->data, &data);
if (~flags & 0x40)
points[next].y = parse_delta(os->data, &data);
flags <<= 2;
}
if (next - 1 == os->lines)
ui_polyline(os->opcode - 1, points, os->lines + 1, &pen);
else
logger(Graphics, Error, "process_polyline(), parse error");
xfree(points);
}
/* Process an ellipse order */
static void process_ellipse(STREAM s, ELLIPSE_ORDER *os, uint32 present, RD_BOOL delta)
{
if (present & 0x01)
rdp_in_coord(s, &os->left, delta);
if (present & 0x02)
rdp_in_coord(s, &os->top, delta);
if (present & 0x04)
rdp_in_coord(s, &os->right, delta);
if (present & 0x08)
rdp_in_coord(s, &os->bottom, delta);
if (present & 0x10)
in_uint8(s, os->opcode);
if (present & 0x20)
in_uint8(s, os->fillmode);
if (present & 0x40)
rdp_in_colour(s, &os->fgcolour);
logger(Graphics, Debug,
"process_ellipse(), l=%d, t=%d, r=%d, b=%d, op=0x%x, fm=%d, fg=0x%x", os->left,
os->top, os->right, os->bottom, os->opcode, os->fillmode, os->fgcolour);
ui_ellipse(os->opcode - 1, os->fillmode, os->left, os->top, os->right - os->left,
os->bottom - os->top, NULL, 0, os->fgcolour);
}
/* Process an ellipse2 order */
static void process_ellipse2(STREAM s, ELLIPSE2_ORDER *os, uint32 present, RD_BOOL delta)
{
BRUSH brush;
if (present & 0x0001)
rdp_in_coord(s, &os->left, delta);
if (present & 0x0002)
rdp_in_coord(s, &os->top, delta);
if (present & 0x0004)
rdp_in_coord(s, &os->right, delta);
if (present & 0x0008)
rdp_in_coord(s, &os->bottom, delta);
if (present & 0x0010)
in_uint8(s, os->opcode);
if (present & 0x0020)
in_uint8(s, os->fillmode);
if (present & 0x0040)
rdp_in_colour(s, &os->bgcolour);
if (present & 0x0080)
rdp_in_colour(s, &os->fgcolour);
rdp_parse_brush(s, &os->brush, present >> 8);
logger(Graphics, Debug,
"process_ellipse2(), l=%d, t=%d, r=%d, b=%d, op=0x%x, fm=%d, bs=%d, bg=0x%x, fg=0x%x",
os->left, os->top, os->right, os->bottom, os->opcode, os->fillmode, os->brush.style,
os->bgcolour, os->fgcolour);
setup_brush(&brush, &os->brush);
ui_ellipse(os->opcode - 1, os->fillmode, os->left, os->top, os->right - os->left,
os->bottom - os->top, &brush, os->bgcolour, os->fgcolour);
}
/* Process a text order */
static void process_text2(STREAM s, TEXT2_ORDER *os, uint32 present, RD_BOOL delta)
{
2017-10-20 11:05:52 +02:00
UNUSED(delta);
BRUSH brush;
if (present & 0x000001)
in_uint8(s, os->font);
if (present & 0x000002)
in_uint8(s, os->flags);
if (present & 0x000004)
in_uint8(s, os->opcode);
if (present & 0x000008)
in_uint8(s, os->mixmode);
if (present & 0x000010)
rdp_in_colour(s, &os->fgcolour);
if (present & 0x000020)
rdp_in_colour(s, &os->bgcolour);
if (present & 0x000040)
in_uint16_le(s, os->clipleft);
if (present & 0x000080)
in_uint16_le(s, os->cliptop);
if (present & 0x000100)
in_uint16_le(s, os->clipright);
if (present & 0x000200)
in_uint16_le(s, os->clipbottom);
if (present & 0x000400)
in_uint16_le(s, os->boxleft);
if (present & 0x000800)
in_uint16_le(s, os->boxtop);
if (present & 0x001000)
in_uint16_le(s, os->boxright);
if (present & 0x002000)
in_uint16_le(s, os->boxbottom);
rdp_parse_brush(s, &os->brush, present >> 14);
if (present & 0x080000)
in_uint16_le(s, os->x);
if (present & 0x100000)
in_uint16_le(s, os->y);
if (present & 0x200000)
{
in_uint8(s, os->length);
in_uint8a(s, os->text, os->length);
}
logger(Graphics, Debug,
"process_text2(), x=%d, y=%d, cl=%d, ct=%d, cr=%d, cb=%d, bl=%d, bt=%d, br=%d, bb=%d, bs=%d, bg=0x%x, fg=0x%x, font=%d, fl=0x%x, op=0x%x, mix=%d, n=%d",
os->x, os->y, os->clipleft, os->cliptop, os->clipright, os->clipbottom, os->boxleft,
os->boxtop, os->boxright, os->boxbottom, os->brush.style, os->bgcolour, os->fgcolour,
os->font, os->flags, os->opcode, os->mixmode, os->length);
setup_brush(&brush, &os->brush);
ui_draw_text(os->font, os->flags, os->opcode - 1, os->mixmode, os->x, os->y,
os->clipleft, os->cliptop, os->clipright - os->clipleft,
os->clipbottom - os->cliptop, os->boxleft, os->boxtop,
os->boxright - os->boxleft, os->boxbottom - os->boxtop,
&brush, os->bgcolour, os->fgcolour, os->text, os->length);
}
/* Process a raw bitmap cache order */
static void process_raw_bmpcache(STREAM s)
{
RD_HBITMAP bitmap;
uint16 cache_idx, bufsize;
uint8 cache_id, width, height, bpp, Bpp;
uint8 *data, *inverted;
int y;
in_uint8(s, cache_id);
in_uint8s(s, 1); /* pad */
in_uint8(s, width);
in_uint8(s, height);
in_uint8(s, bpp);
Bpp = (bpp + 7) / 8;
in_uint16_le(s, bufsize);
in_uint16_le(s, cache_idx);
in_uint8p(s, data, bufsize);
logger(Graphics, Debug, "process_raw_bpmcache(), cx=%d, cy=%d, id=%d, idx=%d", width,
height, cache_id, cache_idx);
inverted = (uint8 *)xmalloc(width * height * Bpp);
for (y = 0; y < height; y++)
{
memcpy(&inverted[(height - y - 1) * (width * Bpp)], &data[y * (width * Bpp)],
width * Bpp);
}
bitmap = ui_create_bitmap(width, height, inverted);
xfree(inverted);
cache_put_bitmap(cache_id, cache_idx, bitmap);
}
/* Process a bitmap cache order */
static void process_bmpcache(STREAM s)
{
RD_HBITMAP bitmap;
uint16 cache_idx, size;
uint8 cache_id, width, height, bpp, Bpp;
uint8 *data, *bmpdata;
uint16 bufsize, pad2, row_size, final_size;
uint8 pad1;
pad2 = row_size = final_size = 0xffff; /* Shut the compiler up */
in_uint8(s, cache_id);
in_uint8(s, pad1); /* pad */
in_uint8(s, width);
in_uint8(s, height);
in_uint8(s, bpp);
Bpp = (bpp + 7) / 8;
in_uint16_le(s, bufsize); /* bufsize */
in_uint16_le(s, cache_idx);
if (g_rdp_version >= RDP_V5)
{
size = bufsize;
}
else
{
/* Begin compressedBitmapData */
in_uint16_le(s, pad2); /* pad */
in_uint16_le(s, size);
/* in_uint8s(s, 4); *//* row_size, final_size */
in_uint16_le(s, row_size);
in_uint16_le(s, final_size);
}
in_uint8p(s, data, size);
logger(Graphics, Debug,
"process_bmpcache(), cx=%d, cy=%d, id=%d, idx=%d, bpp=%d, size=%d, pad1=%d, bufsize=%d, pad2=%d, rs=%d, fs=%d",
width, height, cache_id, cache_idx, bpp, size, pad1, bufsize, pad2, row_size,
final_size);
bmpdata = (uint8 *)xmalloc(width * height * Bpp);
if (bitmap_decompress(bmpdata, width, height, data, size, Bpp))
{
bitmap = ui_create_bitmap(width, height, bmpdata);
cache_put_bitmap(cache_id, cache_idx, bitmap);
}
else
{
logger(Graphics, Error, "process_bmpcache(), Failed to decompress bitmap data");
}
xfree(bmpdata);
}
/* Process a bitmap cache v2 order */
static void process_bmpcache2(STREAM s, uint16 flags, RD_BOOL compressed)
{
RD_HBITMAP bitmap;
int y;
uint8 cache_id, cache_idx_low, width, height, Bpp;
uint16 cache_idx, bufsize;
uint8 *data, *bmpdata, *bitmap_id;
bitmap_id = NULL; /* prevent compiler warning */
cache_id = flags & ID_MASK;
Bpp = ((flags & MODE_MASK) >> MODE_SHIFT) - 2;
if (flags & PERSIST)
{
in_uint8p(s, bitmap_id, 8);
}
if (flags & SQUARE)
{
in_uint8(s, width);
height = width;
}
else
{
in_uint8(s, width);
in_uint8(s, height);
}
in_uint16_be(s, bufsize);
bufsize &= BUFSIZE_MASK;
in_uint8(s, cache_idx);
if (cache_idx & LONG_FORMAT)
{
in_uint8(s, cache_idx_low);
cache_idx = ((cache_idx ^ LONG_FORMAT) << 8) + cache_idx_low;
}
in_uint8p(s, data, bufsize);
logger(Graphics, Debug,
"process_bmpcache2(), compr=%d, flags=%x, cx=%d, cy=%d, id=%d, idx=%d, Bpp=%d, bs=%d",
compressed, flags, width, height, cache_id, cache_idx, Bpp, bufsize);
bmpdata = (uint8 *)xmalloc(width * height * Bpp);
if (compressed)
{
if (!bitmap_decompress(bmpdata, width, height, data, bufsize, Bpp))
{
logger(Graphics, Error,
"process_bmpcache2(), failed to decompress bitmap data");
xfree(bmpdata);
return;
}
}
else
{
for (y = 0; y < height; y++)
memcpy(&bmpdata[(height - y - 1) * (width * Bpp)],
&data[y * (width * Bpp)], width * Bpp);
}
bitmap = ui_create_bitmap(width, height, bmpdata);
if (bitmap)
{
cache_put_bitmap(cache_id, cache_idx, bitmap);
if (flags & PERSIST)
pstcache_save_bitmap(cache_id, cache_idx, bitmap_id, width, height,
width * height * Bpp, bmpdata);
}
else
{
logger(Graphics, Error, "process_bmpcache2(), ui_create_bitmap(), failed");
}
xfree(bmpdata);
}
/* Process a colourmap cache order */
static void process_colcache(STREAM s)
{
COLOURENTRY *entry;
COLOURMAP map;
RD_HCOLOURMAP hmap;
uint8 cache_id;
int i;
in_uint8(s, cache_id);
in_uint16_le(s, map.ncolours);
map.colours = (COLOURENTRY *)xmalloc(sizeof(COLOURENTRY) * map.ncolours);
for (i = 0; i < map.ncolours; i++)
{
entry = &map.colours[i];
in_uint8(s, entry->blue);
in_uint8(s, entry->green);
in_uint8(s, entry->red);
in_uint8s(s, 1); /* pad */
}
logger(Graphics, Debug, "process_colcache(), id=%d, n=%d", cache_id, map.ncolours);
hmap = ui_create_colourmap(&map);
if (cache_id)
ui_set_colourmap(hmap);
xfree(map.colours);
}
/* Process a font cache order */
static void process_fontcache(STREAM s)
{
RD_HGLYPH bitmap;
uint8 font, nglyphs;
uint16 character, offset, baseline, width, height;
int i, datasize;
uint8 *data;
in_uint8(s, font);
in_uint8(s, nglyphs);
logger(Graphics, Debug, "process_fontcache(), font=%d, n=%d", font, nglyphs);
for (i = 0; i < nglyphs; i++)
{
in_uint16_le(s, character);
in_uint16_le(s, offset);
in_uint16_le(s, baseline);
in_uint16_le(s, width);
in_uint16_le(s, height);
datasize = (height * ((width + 7) / 8) + 3) & ~3;
in_uint8p(s, data, datasize);
bitmap = ui_create_glyph(width, height, data);
cache_put_font(font, character, offset, baseline, width, height, bitmap);
}
}
static void process_compressed_8x8_brush_data(uint8 *in, uint8 *out, int Bpp)
{
int x, y, pal_index, in_index, shift, do2, i;
uint8 *pal;
in_index = 0;
pal = in + 16;
/* read it bottom up */
for (y = 7; y >= 0; y--)
{
/* 2 bytes per row */
x = 0;
for (do2 = 0; do2 < 2; do2++)
{
/* 4 pixels per byte */
shift = 6;
while (shift >= 0)
{
pal_index = (in[in_index] >> shift) & 3;
/* size of palette entries depends on Bpp */
for (i = 0; i < Bpp; i++)
{
out[(y * 8 + x) * Bpp + i] = pal[pal_index * Bpp + i];
}
x++;
shift -= 2;
}
in_index++;
}
}
}
/* Process a brush cache order */
static void process_brushcache(STREAM s, uint16 flags)
{
2017-10-20 11:05:52 +02:00
UNUSED(flags);
BRUSHDATA brush_data;
uint8 cache_idx, colour_code, width, height, size, type;
uint8 *comp_brush;
int index;
int Bpp;
in_uint8(s, cache_idx);
in_uint8(s, colour_code);
in_uint8(s, width);
in_uint8(s, height);
in_uint8(s, type); /* type, 0x8x = cached */
in_uint8(s, size);
logger(Graphics, Debug, "process_brushcache(), idx=%d, wd=%d, ht=%d, type=0x%x sz=%d",
cache_idx, width, height, type, size);
if ((width == 8) && (height == 8))
{
if (colour_code == 1)
{
brush_data.colour_code = 1;
brush_data.data_size = 8;
brush_data.data = xmalloc(8);
if (size == 8)
{
/* read it bottom up */
for (index = 7; index >= 0; index--)
{
in_uint8(s, brush_data.data[index]);
}
}
else
{
logger(Graphics, Warning,
"process_brushcache(), incompatible brush, colour_code %d size %d",
colour_code, size);
}
cache_put_brush_data(1, cache_idx, &brush_data);
}
else if ((colour_code >= 3) && (colour_code <= 6))
{
Bpp = colour_code - 2;
brush_data.colour_code = colour_code;
brush_data.data_size = 8 * 8 * Bpp;
brush_data.data = xmalloc(8 * 8 * Bpp);
if (size == 16 + 4 * Bpp)
{
in_uint8p(s, comp_brush, 16 + 4 * Bpp);
process_compressed_8x8_brush_data(comp_brush, brush_data.data, Bpp);
}
else
{
in_uint8a(s, brush_data.data, 8 * 8 * Bpp);
}
cache_put_brush_data(colour_code, cache_idx, &brush_data);
}
else
{
logger(Graphics, Warning,
"process_brushcache(), incompatible brush, colour_code %d size %d",
colour_code, size);
}
}
else
{
logger(Graphics, Warning,
"process_brushcache(), incompatible brush, width height %d %d", width,
height);
}
}
/* Process a secondary order */
static void process_secondary_order(STREAM s)
{
/* The length isn't calculated correctly by the server.
* For very compact orders the length becomes negative
* so a signed integer must be used. */
sint16 length;
uint16 flags;
uint8 type;
size_t next_order;
Malicious RDP server security fixes This commit includes fixes for a set of 21 vulnerabilities in rdesktop when a malicious RDP server is used. All vulnerabilities was identified and reported by Eyal Itkin. * Add rdp_protocol_error function that is used in several fixes * Refactor of process_bitmap_updates * Fix possible integer overflow in s_check_rem() on 32bit arch * Fix memory corruption in process_bitmap_data - CVE-2018-8794 * Fix remote code execution in process_bitmap_data - CVE-2018-8795 * Fix remote code execution in process_plane - CVE-2018-8797 * Fix Denial of Service in mcs_recv_connect_response - CVE-2018-20175 * Fix Denial of Service in mcs_parse_domain_params - CVE-2018-20175 * Fix Denial of Service in sec_parse_crypt_info - CVE-2018-20176 * Fix Denial of Service in sec_recv - CVE-2018-20176 * Fix minor information leak in rdpdr_process - CVE-2018-8791 * Fix Denial of Service in cssp_read_tsrequest - CVE-2018-8792 * Fix remote code execution in cssp_read_tsrequest - CVE-2018-8793 * Fix Denial of Service in process_bitmap_data - CVE-2018-8796 * Fix minor information leak in rdpsnd_process_ping - CVE-2018-8798 * Fix Denial of Service in process_secondary_order - CVE-2018-8799 * Fix remote code execution in in ui_clip_handle_data - CVE-2018-8800 * Fix major information leak in ui_clip_handle_data - CVE-2018-20174 * Fix memory corruption in rdp_in_unistr - CVE-2018-20177 * Fix Denial of Service in process_demand_active - CVE-2018-20178 * Fix remote code execution in lspci_process - CVE-2018-20179 * Fix remote code execution in rdpsnddbg_process - CVE-2018-20180 * Fix remote code execution in seamless_process - CVE-2018-20181 * Fix remote code execution in seamless_process_line - CVE-2018-20182
2019-01-16 10:45:33 +01:00
struct stream packet = *s;
in_uint16_le(s, length);
in_uint16_le(s, flags); /* used by bmpcache2 */
in_uint8(s, type);
length += 13; /* MS-RDPEGDI is ridiculous and says that you need to add 13 to this
field to get the total packet length. "For historical reasons". */
length -= 6; /* Subtract six bytes of headers and you'll get the size of the remaining
order data. */
if (!s_check_rem(s, length))
Malicious RDP server security fixes This commit includes fixes for a set of 21 vulnerabilities in rdesktop when a malicious RDP server is used. All vulnerabilities was identified and reported by Eyal Itkin. * Add rdp_protocol_error function that is used in several fixes * Refactor of process_bitmap_updates * Fix possible integer overflow in s_check_rem() on 32bit arch * Fix memory corruption in process_bitmap_data - CVE-2018-8794 * Fix remote code execution in process_bitmap_data - CVE-2018-8795 * Fix remote code execution in process_plane - CVE-2018-8797 * Fix Denial of Service in mcs_recv_connect_response - CVE-2018-20175 * Fix Denial of Service in mcs_parse_domain_params - CVE-2018-20175 * Fix Denial of Service in sec_parse_crypt_info - CVE-2018-20176 * Fix Denial of Service in sec_recv - CVE-2018-20176 * Fix minor information leak in rdpdr_process - CVE-2018-8791 * Fix Denial of Service in cssp_read_tsrequest - CVE-2018-8792 * Fix remote code execution in cssp_read_tsrequest - CVE-2018-8793 * Fix Denial of Service in process_bitmap_data - CVE-2018-8796 * Fix minor information leak in rdpsnd_process_ping - CVE-2018-8798 * Fix Denial of Service in process_secondary_order - CVE-2018-8799 * Fix remote code execution in in ui_clip_handle_data - CVE-2018-8800 * Fix major information leak in ui_clip_handle_data - CVE-2018-20174 * Fix memory corruption in rdp_in_unistr - CVE-2018-20177 * Fix Denial of Service in process_demand_active - CVE-2018-20178 * Fix remote code execution in lspci_process - CVE-2018-20179 * Fix remote code execution in rdpsnddbg_process - CVE-2018-20180 * Fix remote code execution in seamless_process - CVE-2018-20181 * Fix remote code execution in seamless_process_line - CVE-2018-20182
2019-01-16 10:45:33 +01:00
{
rdp_protocol_error("next order pointer would overrun stream", &packet);
Malicious RDP server security fixes This commit includes fixes for a set of 21 vulnerabilities in rdesktop when a malicious RDP server is used. All vulnerabilities was identified and reported by Eyal Itkin. * Add rdp_protocol_error function that is used in several fixes * Refactor of process_bitmap_updates * Fix possible integer overflow in s_check_rem() on 32bit arch * Fix memory corruption in process_bitmap_data - CVE-2018-8794 * Fix remote code execution in process_bitmap_data - CVE-2018-8795 * Fix remote code execution in process_plane - CVE-2018-8797 * Fix Denial of Service in mcs_recv_connect_response - CVE-2018-20175 * Fix Denial of Service in mcs_parse_domain_params - CVE-2018-20175 * Fix Denial of Service in sec_parse_crypt_info - CVE-2018-20176 * Fix Denial of Service in sec_recv - CVE-2018-20176 * Fix minor information leak in rdpdr_process - CVE-2018-8791 * Fix Denial of Service in cssp_read_tsrequest - CVE-2018-8792 * Fix remote code execution in cssp_read_tsrequest - CVE-2018-8793 * Fix Denial of Service in process_bitmap_data - CVE-2018-8796 * Fix minor information leak in rdpsnd_process_ping - CVE-2018-8798 * Fix Denial of Service in process_secondary_order - CVE-2018-8799 * Fix remote code execution in in ui_clip_handle_data - CVE-2018-8800 * Fix major information leak in ui_clip_handle_data - CVE-2018-20174 * Fix memory corruption in rdp_in_unistr - CVE-2018-20177 * Fix Denial of Service in process_demand_active - CVE-2018-20178 * Fix remote code execution in lspci_process - CVE-2018-20179 * Fix remote code execution in rdpsnddbg_process - CVE-2018-20180 * Fix remote code execution in seamless_process - CVE-2018-20181 * Fix remote code execution in seamless_process_line - CVE-2018-20182
2019-01-16 10:45:33 +01:00
}
next_order = s_tell(s) + length;
switch (type)
{
case RDP_ORDER_RAW_BMPCACHE:
process_raw_bmpcache(s);
break;
case RDP_ORDER_COLCACHE:
process_colcache(s);
break;
case RDP_ORDER_BMPCACHE:
process_bmpcache(s);
break;
case RDP_ORDER_FONTCACHE:
process_fontcache(s);
break;
case RDP_ORDER_RAW_BMPCACHE2:
process_bmpcache2(s, flags, False); /* uncompressed */
break;
case RDP_ORDER_BMPCACHE2:
process_bmpcache2(s, flags, True); /* compressed */
break;
case RDP_ORDER_BRUSHCACHE:
process_brushcache(s, flags);
break;
default:
logger(Graphics, Warning,
"process_secondary_order(), unhandled secondary order %d", type);
}
s_seek(s, next_order);
}
/* Process an order PDU */
void process_orders(STREAM s, uint16 num_orders)
{
RDP_ORDER_STATE *os = &g_order_state;
uint32 present;
uint8 order_flags;
int size, processed = 0;
RD_BOOL delta;
while (processed < num_orders)
{
in_uint8(s, order_flags);
if (!(order_flags & RDP_ORDER_STANDARD))
{
logger(Graphics, Error, "process_orders(), order parsing failed");
break;
}
if (order_flags & RDP_ORDER_SECONDARY)
{
process_secondary_order(s);
}
else
{
if (order_flags & RDP_ORDER_CHANGE)
{
in_uint8(s, os->order_type);
}
switch (os->order_type)
{
case RDP_ORDER_TRIBLT:
case RDP_ORDER_TEXT2:
size = 3;
break;
case RDP_ORDER_PATBLT:
case RDP_ORDER_MEMBLT:
case RDP_ORDER_LINE:
case RDP_ORDER_POLYGON2:
case RDP_ORDER_ELLIPSE2:
size = 2;
break;
default:
size = 1;
}
rdp_in_present(s, &present, order_flags, size);
if (order_flags & RDP_ORDER_BOUNDS)
{
if (!(order_flags & RDP_ORDER_LASTBOUNDS))
rdp_parse_bounds(s, &os->bounds);
ui_set_clip(os->bounds.left,
os->bounds.top,
os->bounds.right - os->bounds.left + 1,
os->bounds.bottom - os->bounds.top + 1);
}
delta = order_flags & RDP_ORDER_DELTA;
switch (os->order_type)
{
case RDP_ORDER_DESTBLT:
process_destblt(s, &os->destblt, present, delta);
break;
case RDP_ORDER_PATBLT:
process_patblt(s, &os->patblt, present, delta);
break;
case RDP_ORDER_SCREENBLT:
process_screenblt(s, &os->screenblt, present, delta);
break;
case RDP_ORDER_LINE:
process_line(s, &os->line, present, delta);
break;
case RDP_ORDER_RECT:
process_rect(s, &os->rect, present, delta);
break;
case RDP_ORDER_DESKSAVE:
process_desksave(s, &os->desksave, present, delta);
break;
case RDP_ORDER_MEMBLT:
process_memblt(s, &os->memblt, present, delta);
break;
case RDP_ORDER_TRIBLT:
process_triblt(s, &os->triblt, present, delta);
break;
case RDP_ORDER_POLYGON:
process_polygon(s, &os->polygon, present, delta);
break;
case RDP_ORDER_POLYGON2:
process_polygon2(s, &os->polygon2, present, delta);
break;
case RDP_ORDER_POLYLINE:
process_polyline(s, &os->polyline, present, delta);
break;
case RDP_ORDER_ELLIPSE:
process_ellipse(s, &os->ellipse, present, delta);
break;
case RDP_ORDER_ELLIPSE2:
process_ellipse2(s, &os->ellipse2, present, delta);
break;
case RDP_ORDER_TEXT2:
process_text2(s, &os->text2, present, delta);
break;
default:
logger(Graphics, Warning,
"process_orders(), unhandled order type %d",
os->order_type);
return;
}
if (order_flags & RDP_ORDER_BOUNDS)
ui_reset_clip();
}
processed++;
}
#if 0
/* not true when RDP_COMPRESSION is set */
if (s_tell(s) != g_next_packet)
logger(Graphics, Error, "process_orders(), %d bytes remaining",
(int)(g_next_packet - s_tell(s)));
#endif
}
/* Reset order state */
void reset_order_state(void)
{
memset(&g_order_state, 0, sizeof(g_order_state));
g_order_state.order_type = RDP_ORDER_PATBLT;
}