Handle insufficient server side buffer for smartcard operations

We should pay attention to OutputBufferLength of DR_CONTROL_REQ
and send STATUS_BUFFER_TOO_SMALL if it's insufficient for returned
result.
This commit is contained in:
Alexander Zakharov 2016-11-13 14:55:02 +03:00
parent d12204b424
commit 774a657975
7 changed files with 115 additions and 18 deletions

View File

@ -29,6 +29,8 @@
extern RDP_VERSION g_rdp_version;
extern RD_BOOL g_encryption;
uint32 vc_chunk_size = CHANNEL_CHUNK_LENGTH;
VCHANNEL g_channels[MAX_CHANNELS];
unsigned int g_num_channels;
@ -95,11 +97,16 @@ channel_send(STREAM s, VCHANNEL * channel)
logger(Protocol, Debug, "channel_send(), channel = %d, length = %d", channel->mcs_id,
length);
thislength = MIN(length, CHANNEL_CHUNK_LENGTH);
thislength = MIN(length, vc_chunk_size);
/* Note: In the original clipboard implementation, this number was
1592, not 1600. However, I don't remember the reason and 1600 seems
to work so.. This applies only to *this* length, not the length of
continuation or ending packets. */
/* Actually, CHANNEL_CHUNK_LENGTH (default value is 1600 bytes) is described
in MS-RDPBCGR (s. 2.2.6, s.3.1.5.2.1) and can be set by server only
in the optional field VCChunkSize of VC Caps) */
remaining = length - thislength;
flags = (remaining == 0) ? CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST : CHANNEL_FLAG_FIRST;
if (channel->flags & CHANNEL_OPTION_SHOW_PROTOCOL)
@ -114,7 +121,7 @@ channel_send(STREAM s, VCHANNEL * channel)
/* subsequent segments copied (otherwise would have to generate headers backwards) */
while (remaining > 0)
{
thislength = MIN(remaining, CHANNEL_CHUNK_LENGTH);
thislength = MIN(remaining, vc_chunk_size);
remaining -= thislength;
flags = (remaining == 0) ? CHANNEL_FLAG_LAST : 0;
if (channel->flags & CHANNEL_OPTION_SHOW_PROTOCOL)

View File

@ -398,6 +398,9 @@ enum RDP_INPUT_DEVICE
#define RDP_CAPSET_LARGE_POINTER 27
#define RDP_CAPLEN_LARGE_POINTER 6
#define RDP_CAPSET_VC 20
#define RDP_CAPLEN_VC 0x08
#define RDP_SOURCE "MSTSC"
/* Logon flags */
@ -520,6 +523,7 @@ enum RDP_INPUT_DEVICE
#define RD_STATUS_NO_SUCH_FILE 0xc000000f
#define RD_STATUS_INVALID_DEVICE_REQUEST 0xc0000010
#define RD_STATUS_ACCESS_DENIED 0xc0000022
#define RD_STATUS_BUFFER_TOO_SMALL 0xc0000023
#define RD_STATUS_OBJECT_NAME_COLLISION 0xc0000035
#define RD_STATUS_DISK_FULL 0xc000007f
#define RD_STATUS_FILE_IS_A_DIRECTORY 0xc00000ba

37
rdp.c
View File

@ -5,6 +5,7 @@
Copyright 2003-2011 Peter Astrand <astrand@cendio.se> for Cendio AB
Copyright 2011-2018 Henrik Andersson <hean01@cendio.se> for Cendio AB
Copyright 2017 Karl Mikaelsson <derfian@cendio.se> for Cendio AB
Copyright 2017 Alexander Zakharov <uglym8gmail.com>
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
@ -63,6 +64,8 @@ uint32 g_rdp_shareid;
extern RDPCOMP g_mppc_dict;
extern uint32 vc_chunk_size;
/* Session Directory support */
extern RD_BOOL g_redirect;
extern char *g_redirect_server;
@ -926,6 +929,28 @@ rdp_out_brushcache_caps(STREAM s)
out_uint32_le(s, 1); /* cache type */
}
/* 2.2.7.1.10 MS-RDPBCGR */
/* Output virtual channel capability set */
static void
rdp_out_virtchan_caps(STREAM s)
{
out_uint16_le(s, RDP_CAPSET_VC);
out_uint16_le(s, RDP_CAPLEN_VC);
/* VCCAPS_COMPR_SC */
out_uint32_le(s, 0x00000001); /* compression flags */
}
static void
rdp_process_virtchan_caps(STREAM s)
{
uint32 flags, chunk_size;
in_uint32_le(s, flags);
in_uint32_le(s, chunk_size);
vc_chunk_size = chunk_size;
}
/* Output Input Capability Set */
static void
rdp_out_ts_input_capabilityset(STREAM s)
@ -1043,7 +1068,8 @@ rdp_send_confirm_active(void)
RDP_CAPLEN_SOUND +
RDP_CAPLEN_GLYPHCACHE +
RDP_CAPLEN_MULTIFRAGMENTUPDATE +
RDP_CAPLEN_LARGE_POINTER + 4 /* w2k fix, sessionid */ ;
RDP_CAPLEN_LARGE_POINTER +
RDP_CAPLEN_VC + 4 /* w2k fix, sessionid */ ;
logger(Protocol, Debug, "%s()", __func__);
@ -1070,7 +1096,7 @@ rdp_send_confirm_active(void)
out_uint16_le(s, caplen);
out_uint8p(s, RDP_SOURCE, sizeof(RDP_SOURCE));
out_uint16_le(s, 16); /* num_caps */
out_uint16_le(s, 17); /* num_caps */
out_uint8s(s, 2); /* pad */
rdp_out_ts_general_capabilityset(s);
@ -1091,6 +1117,7 @@ rdp_send_confirm_active(void)
rdp_out_control_caps(s);
rdp_out_share_caps(s);
rdp_out_brushcache_caps(s);
rdp_out_virtchan_caps(s);
rdp_out_ts_input_capabilityset(s);
rdp_out_ts_sound_capabilityset(s);
@ -1212,6 +1239,12 @@ rdp_process_server_caps(STREAM s, uint16 length)
case RDP_CAPSET_BITMAP:
rdp_process_bitmap_caps(s);
break;
case RDP_CAPSET_VC:
/* Parse only if we got VCChunkSize */
if (capset_length > 8) {
rdp_process_virtchan_caps(s);
}
break;
}
s->p = next;

53
rdpdr.c
View File

@ -357,6 +357,50 @@ rdpdr_send_client_device_list_announce(void)
channel_send(s, rdpdr_channel);
}
void
rdpdr_send_scard_io_completion(uint32 device, uint32 id, uint32 status, uint32 result, uint8 * buffer,
uint32 srv_buf_len)
{
int i;
STREAM s;
#ifdef WITH_SCARD
scard_lock(SCARD_LOCK_RDPDR);
#endif
if (result > srv_buf_len) {
/*
* Not enough space has been allocated by server to store the result.
* Send STATUS_BUFFER_TOO_SMALL error as a IoStatus.
*/
result = 0;
status = RD_STATUS_BUFFER_TOO_SMALL;
}
s = channel_init(rdpdr_channel, 20 + result);
out_uint16_le(s, RDPDR_CTYP_CORE);
out_uint16_le(s, PAKID_CORE_DEVICE_IOCOMPLETION);
out_uint32_le(s, device);
out_uint32_le(s, id);
out_uint32_le(s, status);
out_uint32_le(s, result);
if (result)
out_uint8p(s, buffer, result);
s_mark_end(s);
/* JIF */
#ifdef WITH_DEBUG_RDP5
printf("--> rdpdr_send_scard_io_completion\n");
/* hexdump(s->channel_hdr + 8, s->end - s->channel_hdr - 8); */
#endif
channel_send(s, rdpdr_channel);
#ifdef WITH_SCARD
scard_unlock(SCARD_LOCK_RDPDR);
#endif
}
void
rdpdr_send_completion(uint32 device, uint32 id, uint32 status, uint32 result, uint8 * buffer,
uint32 length)
@ -730,11 +774,15 @@ rdpdr_process_irp(STREAM s)
break;
}
/* DR_CONTROL_REQ (2.2.1.4.5 of MS-RDPEFS) */
/* OutputBufferLength */
in_uint32_le(s, bytes_out);
in_uint8s(s, 4); /* skip bytes_in */
in_uint32_le(s, request);
/* Padding */
in_uint8s(s, 0x14);
/* TODO: Why do we need to increase length by padlen? Or is it hdr len? */
buffer = (uint8 *) xrealloc((void *) buffer, bytes_out + 0x14);
if (!buffer)
{
@ -743,7 +791,10 @@ rdpdr_process_irp(STREAM s)
}
out.data = out.p = buffer;
out.size = sizeof(buffer);
/* Guess, just a simple mistype. Check others */
//out.size = sizeof(buffer);
out.size = bytes_out + 0x14;
#ifdef WITH_SCARD
scardSetInfo(g_epoch, device, id, bytes_out + 0x14);

19
scard.c
View File

@ -1647,15 +1647,6 @@ TS_SCardTransmit(STREAM in, STREAM out)
myPioRecvPci, recvBuf, &myCbRecvLength);
cbRecvLength = myCbRecvLength;
/* FIXME: handle responses with length > 448 bytes */
if (cbRecvLength > 448)
{
logger(SmartCard, Warning,
"TS_SCardTransmit(), card response limit reached, %d truncated to 448 bytes",
cbRecvLength);
cbRecvLength = 448;
}
if (pioRecvPci)
{
/*
@ -2271,9 +2262,13 @@ scard_device_control(RD_NTHANDLE handle, uint32 request, STREAM in, STREAM out)
/* Processing request */
/* See MS-RSPESC 1.3 Overview for protocol flow */
/* Set CommonTypeHeader (MS-RPCE 2.2.6.1) */
out_uint32_le(out, 0x00081001); /* Header lines */
out_uint32_le(out, 0xCCCCCCCC);
psize = out->p;
/* Set PrivateTypeHeader (MS-RPCE 2.2.6.2) */
out_uint32_le(out, 0x00000000); /* Size of data portion */
out_uint32_le(out, 0x00000000); /* Zero bytes (may be useful) */
pStatusCode = out->p;
@ -2423,6 +2418,7 @@ scard_device_control(RD_NTHANDLE handle, uint32 request, STREAM in, STREAM out)
/* finish */
out->p = pend;
/* TODO: Check MS-RPCE 2.2.6.2 for alignment requirements (IIRC length must be integral multiple of 8) */
addToEnd = (pend - pStatusCode) % 16;
if (addToEnd < 16 && addToEnd > 0)
{
@ -2502,6 +2498,7 @@ SC_addToQueue(RD_NTHANDLE handle, uint32 request, STREAM in, STREAM out)
data->epoch = curEpoch;
data->handle = handle;
data->request = request;
data->srv_buf_len = curBytesOut - 0x14;
data->in = duplicateStream(&(data->memHandle), in, 0, SC_TRUE);
if (data->in == NULL)
{
@ -2576,8 +2573,8 @@ SC_deviceControl(PSCThreadData data)
back to server due to it's considered as abandoned.
*/
if (data->epoch == curEpoch)
rdpdr_send_completion(data->device, data->id, 0, buffer_len, data->out->data,
buffer_len);
rdpdr_send_scard_io_completion(data->device, data->id, 0, buffer_len, data->out->data,
data->srv_buf_len);
SC_destroyThreadData(data);
}

View File

@ -155,6 +155,7 @@ typedef struct _TSCThreadData
uint32 device;
uint32 id;
uint32 epoch;
uint32 srv_buf_len;
RD_NTHANDLE handle;
uint32 request;
STREAM in;

View File

@ -435,8 +435,12 @@ sec_out_mcs_connect_initial_pdu(STREAM s, uint32 selected_protocol)
out_uint16_le(s, g_requested_session_height); /* desktopHeight */
out_uint16_le(s, RNS_UD_COLOR_8BPP); /* colorDepth */
out_uint16_le(s, RNS_UD_SAS_DEL); /* SASSequence */
out_uint32_le(s, g_keylayout); /* keyboardLayout */
out_uint32_le(s, 2600); /* Client build. We are now 2600 compatible :-) */
out_uint32_le(s, g_keylayout); /* keyboardLayout */
/*
* According to s.1.7 of MS-RDPESC if the build number is at least 4,304,
* SCREDIR_VERSION_LONGHORN is assumed; otherwise SCREDIR_VERSIONXP is to be used
*/
out_uint32_le(s, 2600); /* Client build. We are now 2600 compatible :-) */
/* Unicode name of client, padded to 32 bytes */
out_utf16s_padded(s, g_hostname, 32, 0x00);