FreeRDP/server/proxy/channels/pf_channel_rdpdr.c

2018 lines
63 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2021 Armin Novak <armin.novak@thincast.com>
* Copyright 2021 Thincast Technologies GmbH
*
* 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.
*/
#include <freerdp/config.h>
#include <winpr/assert.h>
#include <winpr/string.h>
#include <winpr/print.h>
#include "pf_channel_rdpdr.h"
#include "pf_channel_smartcard.h"
#include <freerdp/server/proxy/proxy_log.h>
#include <freerdp/channels/rdpdr.h>
#include <freerdp/channels/channels.h>
#include <freerdp/utils/rdpdr_utils.h>
#define RTAG PROXY_TAG("channel.rdpdr")
#define SCARD_DEVICE_ID UINT32_MAX
typedef struct
{
InterceptContextMapEntry base;
wStream* s;
wStream* buffer;
UINT16 versionMajor;
UINT16 versionMinor;
UINT32 clientID;
UINT32 computerNameLen;
BOOL computerNameUnicode;
union
{
WCHAR* wc;
char* c;
void* v;
} computerName;
UINT32 SpecialDeviceCount;
UINT32 capabilityVersions[6];
} pf_channel_common_context;
typedef enum
{
STATE_CLIENT_EXPECT_SERVER_ANNOUNCE_REQUEST = 0x01,
STATE_CLIENT_EXPECT_SERVER_CORE_CAPABILITY_REQUEST = 0x02,
STATE_CLIENT_EXPECT_SERVER_CLIENT_ID_CONFIRM = 0x04,
STATE_CLIENT_CHANNEL_RUNNING = 0x10
} pf_channel_client_state;
typedef struct
{
pf_channel_common_context common;
pf_channel_client_state state;
UINT32 flags;
UINT16 maxMajorVersion;
UINT16 maxMinorVersion;
wQueue* queue;
wLog* log;
} pf_channel_client_context;
typedef enum
{
STATE_SERVER_INITIAL,
STATE_SERVER_EXPECT_CLIENT_ANNOUNCE_REPLY,
STATE_SERVER_EXPECT_CLIENT_NAME_REQUEST,
STATE_SERVER_EXPECT_EXPECT_CLIENT_CAPABILITY_RESPONE,
STATE_SERVER_CHANNEL_RUNNING
} pf_channel_server_state;
typedef struct
{
pf_channel_common_context common;
pf_channel_server_state state;
DWORD SessionId;
HANDLE handle;
wArrayList* blockedDevices;
wLog* log;
} pf_channel_server_context;
#define proxy_client "[proxy<-->client]"
#define proxy_server "[proxy<-->server]"
#define proxy_client_rx proxy_client " receive"
#define proxy_client_tx proxy_client " send"
#define proxy_server_rx proxy_server " receive"
#define proxy_server_tx proxy_server " send"
#define SERVER_RX_LOG(log, lvl, fmt, ...) WLog_Print(log, lvl, proxy_client_rx fmt, ##__VA_ARGS__)
#define CLIENT_RX_LOG(log, lvl, fmt, ...) WLog_Print(log, lvl, proxy_server_rx fmt, ##__VA_ARGS__)
#define SERVER_TX_LOG(log, lvl, fmt, ...) WLog_Print(log, lvl, proxy_client_tx fmt, ##__VA_ARGS__)
#define CLIENT_TX_LOG(log, lvl, fmt, ...) WLog_Print(log, lvl, proxy_server_tx fmt, ##__VA_ARGS__)
#define RX_LOG(srv, lvl, fmt, ...) \
do \
{ \
if (srv) \
{ \
SERVER_RX_LOG(lvl, fmt, ##__VA_ARGS__); \
} \
else \
{ \
CLIENT_RX_LOG(lvl, fmt, ##__VA_ARGS__); \
} \
} while (0)
#define SERVER_RXTX_LOG(send, log, lvl, fmt, ...) \
do \
{ \
if (send) \
{ \
SERVER_TX_LOG(log, lvl, fmt, ##__VA_ARGS__); \
} \
else \
{ \
SERVER_RX_LOG(log, lvl, fmt, ##__VA_ARGS__); \
} \
} while (0)
#define Stream_CheckAndLogRequiredLengthSrv(log, s, len) \
Stream_CheckAndLogRequiredLengthWLogEx(log, WLOG_WARN, s, len, 1, \
proxy_client_rx " %s(%s:%" PRIuz ")", __func__, \
__FILE__, (size_t)__LINE__)
#define Stream_CheckAndLogRequiredLengthClient(log, s, len) \
Stream_CheckAndLogRequiredLengthWLogEx(log, WLOG_WARN, s, len, 1, \
proxy_server_rx " %s(%s:%" PRIuz ")", __func__, \
__FILE__, (size_t)__LINE__)
#define Stream_CheckAndLogRequiredLengthRx(srv, log, s, len) \
Stream_CheckAndLogRequiredLengthRx_(srv, log, s, len, 1, __func__, __FILE__, __LINE__)
static BOOL Stream_CheckAndLogRequiredLengthRx_(BOOL srv, wLog* log, wStream* s, size_t nmemb,
size_t size, const char* fkt, const char* file,
size_t line)
{
const char* fmt =
srv ? proxy_server_rx " %s(%s:%" PRIuz ")" : proxy_client_rx " %s(%s:%" PRIuz ")";
return Stream_CheckAndLogRequiredLengthWLogEx(log, WLOG_WARN, s, nmemb, size, fmt, fkt, file,
line);
}
static const char* rdpdr_server_state_to_string(pf_channel_server_state state)
{
switch (state)
{
case STATE_SERVER_INITIAL:
return "STATE_SERVER_INITIAL";
case STATE_SERVER_EXPECT_CLIENT_ANNOUNCE_REPLY:
return "STATE_SERVER_EXPECT_CLIENT_ANNOUNCE_REPLY";
case STATE_SERVER_EXPECT_CLIENT_NAME_REQUEST:
return "STATE_SERVER_EXPECT_CLIENT_NAME_REQUEST";
case STATE_SERVER_EXPECT_EXPECT_CLIENT_CAPABILITY_RESPONE:
return "STATE_SERVER_EXPECT_EXPECT_CLIENT_CAPABILITY_RESPONE";
case STATE_SERVER_CHANNEL_RUNNING:
return "STATE_SERVER_CHANNEL_RUNNING";
default:
return "STATE_SERVER_UNKNOWN";
}
}
static const char* rdpdr_client_state_to_string(pf_channel_client_state state)
{
switch (state)
{
case STATE_CLIENT_EXPECT_SERVER_ANNOUNCE_REQUEST:
return "STATE_CLIENT_EXPECT_SERVER_ANNOUNCE_REQUEST";
case STATE_CLIENT_EXPECT_SERVER_CORE_CAPABILITY_REQUEST:
return "STATE_CLIENT_EXPECT_SERVER_CORE_CAPABILITY_REQUEST";
case STATE_CLIENT_EXPECT_SERVER_CLIENT_ID_CONFIRM:
return "STATE_CLIENT_EXPECT_SERVER_CLIENT_ID_CONFIRM";
case STATE_CLIENT_CHANNEL_RUNNING:
return "STATE_CLIENT_CHANNEL_RUNNING";
default:
return "STATE_CLIENT_UNKNOWN";
}
}
static wStream* rdpdr_get_send_buffer(pf_channel_common_context* rdpdr, UINT16 component,
UINT16 PacketID, size_t capacity)
{
WINPR_ASSERT(rdpdr);
WINPR_ASSERT(rdpdr->s);
if (!Stream_SetPosition(rdpdr->s, 0))
return NULL;
if (!Stream_EnsureCapacity(rdpdr->s, capacity + 4))
return NULL;
Stream_Write_UINT16(rdpdr->s, component);
Stream_Write_UINT16(rdpdr->s, PacketID);
return rdpdr->s;
}
static wStream* rdpdr_client_get_send_buffer(pf_channel_client_context* rdpdr, UINT16 component,
UINT16 PacketID, size_t capacity)
{
WINPR_ASSERT(rdpdr);
return rdpdr_get_send_buffer(&rdpdr->common, component, PacketID, capacity);
}
static wStream* rdpdr_server_get_send_buffer(pf_channel_server_context* rdpdr, UINT16 component,
UINT16 PacketID, size_t capacity)
{
WINPR_ASSERT(rdpdr);
return rdpdr_get_send_buffer(&rdpdr->common, component, PacketID, capacity);
}
static UINT rdpdr_client_send(wLog* log, pClientContext* pc, wStream* s)
{
UINT16 channelId = 0;
WINPR_ASSERT(log);
WINPR_ASSERT(pc);
WINPR_ASSERT(s);
WINPR_ASSERT(pc->context.instance);
if (!pc->connected)
{
CLIENT_TX_LOG(log, WLOG_WARN, "Ignoring channel %s message, not connected!",
RDPDR_SVC_CHANNEL_NAME);
return CHANNEL_RC_OK;
}
channelId = freerdp_channels_get_id_by_name(pc->context.instance, RDPDR_SVC_CHANNEL_NAME);
/* Ignore unmappable channels. Might happen when the channel was already down and
* some delayed message is tried to be sent. */
if ((channelId == 0) || (channelId == UINT16_MAX))
return ERROR_INTERNAL_ERROR;
Stream_SealLength(s);
rdpdr_dump_send_packet(log, WLOG_TRACE, s, proxy_server_tx);
WINPR_ASSERT(pc->context.instance->SendChannelData);
if (!pc->context.instance->SendChannelData(pc->context.instance, channelId, Stream_Buffer(s),
Stream_Length(s)))
return ERROR_EVT_CHANNEL_NOT_FOUND;
return CHANNEL_RC_OK;
}
static UINT rdpdr_seal_send_free_request(pf_channel_server_context* context, wStream* s)
{
BOOL status = 0;
size_t len = 0;
WINPR_ASSERT(context);
WINPR_ASSERT(context->handle);
WINPR_ASSERT(s);
Stream_SealLength(s);
len = Stream_Length(s);
WINPR_ASSERT(len <= UINT32_MAX);
rdpdr_dump_send_packet(context->log, WLOG_TRACE, s, proxy_client_tx);
status = WTSVirtualChannelWrite(context->handle, (char*)Stream_Buffer(s), (ULONG)len, NULL);
return (status) ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
}
static BOOL rdpdr_process_server_header(BOOL server, wLog* log, wStream* s, UINT16 component,
UINT16 PacketId, size_t expect)
{
UINT16 rpacketid = 0;
UINT16 rcomponent = 0;
WINPR_ASSERT(s);
if (!Stream_CheckAndLogRequiredLengthRx(server, log, s, 4))
{
RX_LOG(server, log, WLOG_WARN, "RDPDR_HEADER[%s | %s]: expected length 4, got %" PRIuz,
rdpdr_component_string(component), rdpdr_packetid_string(PacketId),
Stream_GetRemainingLength(s));
return FALSE;
}
Stream_Read_UINT16(s, rcomponent);
Stream_Read_UINT16(s, rpacketid);
if (rcomponent != component)
{
RX_LOG(server, log, WLOG_WARN, "RDPDR_HEADER[%s | %s]: got component %s",
rdpdr_component_string(component), rdpdr_packetid_string(PacketId),
rdpdr_component_string(rcomponent));
return FALSE;
}
if (rpacketid != PacketId)
{
RX_LOG(server, log, WLOG_WARN, "RDPDR_HEADER[%s | %s]: got PacketID %s",
rdpdr_component_string(component), rdpdr_packetid_string(PacketId),
rdpdr_packetid_string(rpacketid));
return FALSE;
}
if (!Stream_CheckAndLogRequiredLengthRx(server, log, s, expect))
{
RX_LOG(server, log, WLOG_WARN,
"RDPDR_HEADER[%s | %s] not enought data, expected %" PRIuz ", "
"got %" PRIuz,
rdpdr_component_string(component), rdpdr_packetid_string(PacketId), expect,
Stream_GetRemainingLength(s));
return ERROR_INVALID_DATA;
}
return TRUE;
}
static BOOL rdpdr_check_version(BOOL server, wLog* log, UINT16 versionMajor, UINT16 versionMinor,
UINT16 component, UINT16 PacketId)
{
if (versionMajor != RDPDR_VERSION_MAJOR)
{
RX_LOG(server, log, WLOG_WARN, "[%s | %s] expected MajorVersion %" PRIu16 ", got %" PRIu16,
rdpdr_component_string(component), rdpdr_packetid_string(PacketId),
RDPDR_VERSION_MAJOR, versionMajor);
return FALSE;
}
switch (versionMinor)
{
case RDPDR_VERSION_MINOR_RDP50:
case RDPDR_VERSION_MINOR_RDP51:
case RDPDR_VERSION_MINOR_RDP52:
case RDPDR_VERSION_MINOR_RDP6X:
case RDPDR_VERSION_MINOR_RDP10X:
break;
default:
{
RX_LOG(server, log, WLOG_WARN, "[%s | %s] unsupported MinorVersion %" PRIu16,
rdpdr_component_string(component), rdpdr_packetid_string(PacketId),
versionMinor);
return FALSE;
}
}
return TRUE;
}
static UINT rdpdr_process_server_announce_request(pf_channel_client_context* rdpdr, wStream* s)
{
const UINT16 component = RDPDR_CTYP_CORE;
const UINT16 packetid = PAKID_CORE_SERVER_ANNOUNCE;
WINPR_ASSERT(rdpdr);
WINPR_ASSERT(s);
if (!rdpdr_process_server_header(FALSE, rdpdr->log, s, component, packetid, 8))
return ERROR_INVALID_DATA;
Stream_Read_UINT16(s, rdpdr->common.versionMajor);
Stream_Read_UINT16(s, rdpdr->common.versionMinor);
if (!rdpdr_check_version(FALSE, rdpdr->log, rdpdr->common.versionMajor,
rdpdr->common.versionMinor, component, packetid))
return ERROR_INVALID_DATA;
/* Limit maximum channel protocol version to the one set by proxy server */
if (rdpdr->common.versionMajor > rdpdr->maxMajorVersion)
{
rdpdr->common.versionMajor = rdpdr->maxMajorVersion;
rdpdr->common.versionMinor = rdpdr->maxMinorVersion;
}
else if (rdpdr->common.versionMinor > rdpdr->maxMinorVersion)
rdpdr->common.versionMinor = rdpdr->maxMinorVersion;
Stream_Read_UINT32(s, rdpdr->common.clientID);
return CHANNEL_RC_OK;
}
static UINT rdpdr_server_send_announce_request(pf_channel_server_context* context)
{
wStream* s =
rdpdr_server_get_send_buffer(context, RDPDR_CTYP_CORE, PAKID_CORE_SERVER_ANNOUNCE, 8);
if (!s)
return CHANNEL_RC_NO_MEMORY;
Stream_Write_UINT16(s, context->common.versionMajor); /* VersionMajor (2 bytes) */
Stream_Write_UINT16(s, context->common.versionMinor); /* VersionMinor (2 bytes) */
Stream_Write_UINT32(s, context->common.clientID); /* ClientId (4 bytes) */
return rdpdr_seal_send_free_request(context, s);
}
static UINT rdpdr_process_client_announce_reply(pf_channel_server_context* rdpdr, wStream* s)
{
const UINT16 component = RDPDR_CTYP_CORE;
const UINT16 packetid = PAKID_CORE_CLIENTID_CONFIRM;
UINT16 versionMajor = 0;
UINT16 versionMinor = 0;
UINT32 clientID = 0;
WINPR_ASSERT(rdpdr);
WINPR_ASSERT(s);
if (!rdpdr_process_server_header(TRUE, rdpdr->log, s, component, packetid, 8))
return ERROR_INVALID_DATA;
Stream_Read_UINT16(s, versionMajor);
Stream_Read_UINT16(s, versionMinor);
if (!rdpdr_check_version(TRUE, rdpdr->log, versionMajor, versionMinor, component, packetid))
return ERROR_INVALID_DATA;
if ((rdpdr->common.versionMajor != versionMajor) ||
(rdpdr->common.versionMinor != versionMinor))
{
SERVER_RX_LOG(
rdpdr->log, WLOG_WARN,
"[%s | %s] downgrading version from %" PRIu16 ".%" PRIu16 " to %" PRIu16 ".%" PRIu16,
rdpdr_component_string(component), rdpdr_packetid_string(packetid),
rdpdr->common.versionMajor, rdpdr->common.versionMinor, versionMajor, versionMinor);
rdpdr->common.versionMajor = versionMajor;
rdpdr->common.versionMinor = versionMinor;
}
Stream_Read_UINT32(s, clientID);
if (rdpdr->common.clientID != clientID)
{
SERVER_RX_LOG(rdpdr->log, WLOG_WARN,
"[%s | %s] changing clientID 0x%08" PRIu32 " to 0x%08" PRIu32,
rdpdr_component_string(component), rdpdr_packetid_string(packetid),
rdpdr->common.clientID, clientID);
rdpdr->common.clientID = clientID;
}
return CHANNEL_RC_OK;
}
static UINT rdpdr_send_client_announce_reply(pClientContext* pc, pf_channel_client_context* rdpdr)
{
wStream* s =
rdpdr_client_get_send_buffer(rdpdr, RDPDR_CTYP_CORE, PAKID_CORE_CLIENTID_CONFIRM, 8);
if (!s)
return CHANNEL_RC_NO_MEMORY;
Stream_Write_UINT16(s, rdpdr->common.versionMajor);
Stream_Write_UINT16(s, rdpdr->common.versionMinor);
Stream_Write_UINT32(s, rdpdr->common.clientID);
return rdpdr_client_send(rdpdr->log, pc, s);
}
static UINT rdpdr_process_client_name_request(pf_channel_server_context* rdpdr, wStream* s,
pClientContext* pc)
{
UINT32 unicodeFlag = 0;
UINT32 codePage = 0;
WINPR_ASSERT(rdpdr);
WINPR_ASSERT(s);
WINPR_ASSERT(pc);
if (!rdpdr_process_server_header(TRUE, rdpdr->log, s, RDPDR_CTYP_CORE, PAKID_CORE_CLIENT_NAME,
12))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, unicodeFlag);
rdpdr->common.computerNameUnicode = (unicodeFlag & 1);
Stream_Read_UINT32(s, codePage);
WINPR_UNUSED(codePage); /* Field is ignored */
Stream_Read_UINT32(s, rdpdr->common.computerNameLen);
if (!Stream_CheckAndLogRequiredLengthSrv(rdpdr->log, s, rdpdr->common.computerNameLen))
{
SERVER_RX_LOG(
rdpdr->log, WLOG_WARN, "[%s | %s]: missing data, got %" PRIu32 ", expected %" PRIu32,
rdpdr_component_string(RDPDR_CTYP_CORE), rdpdr_packetid_string(PAKID_CORE_CLIENT_NAME),
Stream_GetRemainingLength(s), rdpdr->common.computerNameLen);
return ERROR_INVALID_DATA;
}
void* tmp = realloc(rdpdr->common.computerName.v, rdpdr->common.computerNameLen);
if (!tmp)
return CHANNEL_RC_NO_MEMORY;
rdpdr->common.computerName.v = tmp;
Stream_Read(s, rdpdr->common.computerName.v, rdpdr->common.computerNameLen);
pc->computerNameLen = rdpdr->common.computerNameLen;
pc->computerNameUnicode = rdpdr->common.computerNameUnicode;
tmp = realloc(pc->computerName.v, pc->computerNameLen);
if (!tmp)
return CHANNEL_RC_NO_MEMORY;
pc->computerName.v = tmp;
memcpy(pc->computerName.v, rdpdr->common.computerName.v, pc->computerNameLen);
return CHANNEL_RC_OK;
}
static UINT rdpdr_send_client_name_request(pClientContext* pc, pf_channel_client_context* rdpdr)
{
wStream* s = NULL;
WINPR_ASSERT(rdpdr);
WINPR_ASSERT(pc);
{
void* tmp = realloc(rdpdr->common.computerName.v, pc->computerNameLen);
if (!tmp)
return CHANNEL_RC_NO_MEMORY;
rdpdr->common.computerName.v = tmp;
rdpdr->common.computerNameLen = pc->computerNameLen;
rdpdr->common.computerNameUnicode = pc->computerNameUnicode;
memcpy(rdpdr->common.computerName.v, pc->computerName.v, pc->computerNameLen);
}
s = rdpdr_client_get_send_buffer(rdpdr, RDPDR_CTYP_CORE, PAKID_CORE_CLIENT_NAME,
12U + rdpdr->common.computerNameLen);
if (!s)
return CHANNEL_RC_NO_MEMORY;
Stream_Write_UINT32(s, rdpdr->common.computerNameUnicode
? 1
: 0); /* unicodeFlag, 0 for ASCII and 1 for Unicode */
Stream_Write_UINT32(s, 0); /* codePage, must be set to zero */
Stream_Write_UINT32(s, rdpdr->common.computerNameLen);
Stream_Write(s, rdpdr->common.computerName.v, rdpdr->common.computerNameLen);
return rdpdr_client_send(rdpdr->log, pc, s);
}
#define rdpdr_ignore_capset(srv, log, s, header) \
rdpdr_ignore_capset_((srv), (log), (s), header, __func__)
static UINT rdpdr_ignore_capset_(BOOL srv, wLog* log, wStream* s,
const RDPDR_CAPABILITY_HEADER* header, const char* fkt)
{
WINPR_ASSERT(s);
WINPR_ASSERT(header);
Stream_Seek(s, header->CapabilityLength);
return CHANNEL_RC_OK;
}
static UINT rdpdr_client_process_general_capset(pf_channel_client_context* rdpdr, wStream* s,
const RDPDR_CAPABILITY_HEADER* header)
{
WINPR_UNUSED(rdpdr);
return rdpdr_ignore_capset(FALSE, rdpdr->log, s, header);
}
static UINT rdpdr_process_printer_capset(pf_channel_client_context* rdpdr, wStream* s,
const RDPDR_CAPABILITY_HEADER* header)
{
WINPR_UNUSED(rdpdr);
return rdpdr_ignore_capset(FALSE, rdpdr->log, s, header);
}
static UINT rdpdr_process_port_capset(pf_channel_client_context* rdpdr, wStream* s,
const RDPDR_CAPABILITY_HEADER* header)
{
WINPR_UNUSED(rdpdr);
return rdpdr_ignore_capset(FALSE, rdpdr->log, s, header);
}
static UINT rdpdr_process_drive_capset(pf_channel_client_context* rdpdr, wStream* s,
const RDPDR_CAPABILITY_HEADER* header)
{
WINPR_UNUSED(rdpdr);
return rdpdr_ignore_capset(FALSE, rdpdr->log, s, header);
}
static UINT rdpdr_process_smartcard_capset(pf_channel_client_context* rdpdr, wStream* s,
const RDPDR_CAPABILITY_HEADER* header)
{
WINPR_UNUSED(rdpdr);
return rdpdr_ignore_capset(FALSE, rdpdr->log, s, header);
}
static UINT rdpdr_process_server_core_capability_request(pf_channel_client_context* rdpdr,
wStream* s)
{
UINT status = CHANNEL_RC_OK;
UINT16 numCapabilities = 0;
WINPR_ASSERT(rdpdr);
if (!rdpdr_process_server_header(FALSE, rdpdr->log, s, RDPDR_CTYP_CORE,
PAKID_CORE_SERVER_CAPABILITY, 4))
return ERROR_INVALID_DATA;
Stream_Read_UINT16(s, numCapabilities);
Stream_Seek(s, 2); /* pad (2 bytes) */
for (UINT16 i = 0; i < numCapabilities; i++)
{
RDPDR_CAPABILITY_HEADER header = { 0 };
UINT error = rdpdr_read_capset_header(rdpdr->log, s, &header);
if (error != CHANNEL_RC_OK)
return error;
if (header.CapabilityType < ARRAYSIZE(rdpdr->common.capabilityVersions))
{
if (rdpdr->common.capabilityVersions[header.CapabilityType] > header.Version)
rdpdr->common.capabilityVersions[header.CapabilityType] = header.Version;
WLog_Print(rdpdr->log, WLOG_TRACE,
"capability %s got version %" PRIu32 ", will use version %" PRIu32,
rdpdr_cap_type_string(header.CapabilityType), header.Version,
rdpdr->common.capabilityVersions[header.CapabilityType]);
}
switch (header.CapabilityType)
{
case CAP_GENERAL_TYPE:
status = rdpdr_client_process_general_capset(rdpdr, s, &header);
break;
case CAP_PRINTER_TYPE:
status = rdpdr_process_printer_capset(rdpdr, s, &header);
break;
case CAP_PORT_TYPE:
status = rdpdr_process_port_capset(rdpdr, s, &header);
break;
case CAP_DRIVE_TYPE:
status = rdpdr_process_drive_capset(rdpdr, s, &header);
break;
case CAP_SMARTCARD_TYPE:
status = rdpdr_process_smartcard_capset(rdpdr, s, &header);
break;
default:
WLog_Print(rdpdr->log, WLOG_WARN,
"unknown capability 0x%04" PRIx16 ", length %" PRIu16
", version %" PRIu32,
header.CapabilityType, header.CapabilityLength, header.Version);
Stream_Seek(s, header.CapabilityLength);
break;
}
if (status != CHANNEL_RC_OK)
return status;
}
return CHANNEL_RC_OK;
}
static BOOL rdpdr_write_general_capset(wLog* log, pf_channel_common_context* rdpdr, wStream* s)
{
WINPR_ASSERT(rdpdr);
WINPR_ASSERT(s);
const RDPDR_CAPABILITY_HEADER header = { CAP_GENERAL_TYPE, 44,
rdpdr->capabilityVersions[CAP_GENERAL_TYPE] };
if (rdpdr_write_capset_header(log, s, &header) != CHANNEL_RC_OK)
return FALSE;
Stream_Write_UINT32(s, 0); /* osType, ignored on receipt */
Stream_Write_UINT32(s, 0); /* osVersion, should be ignored */
Stream_Write_UINT16(s, rdpdr->versionMajor); /* protocolMajorVersion, must be set to 1 */
Stream_Write_UINT16(s, rdpdr->versionMinor); /* protocolMinorVersion */
Stream_Write_UINT32(s, 0x0000FFFF); /* ioCode1 */
Stream_Write_UINT32(s, 0); /* ioCode2, must be set to zero, reserved for future use */
Stream_Write_UINT32(s, RDPDR_DEVICE_REMOVE_PDUS | RDPDR_CLIENT_DISPLAY_NAME_PDU |
RDPDR_USER_LOGGEDON_PDU); /* extendedPDU */
Stream_Write_UINT32(s, ENABLE_ASYNCIO); /* extraFlags1 */
Stream_Write_UINT32(s, 0); /* extraFlags2, must be set to zero, reserved for future use */
Stream_Write_UINT32(s, rdpdr->SpecialDeviceCount); /* SpecialTypeDeviceCap, number of special
devices to be redirected before logon */
return TRUE;
}
static BOOL rdpdr_write_printer_capset(wLog* log, pf_channel_common_context* rdpdr, wStream* s)
{
WINPR_ASSERT(rdpdr);
WINPR_ASSERT(s);
const RDPDR_CAPABILITY_HEADER header = { CAP_PRINTER_TYPE, 8,
rdpdr->capabilityVersions[CAP_PRINTER_TYPE] };
if (rdpdr_write_capset_header(log, s, &header) != CHANNEL_RC_OK)
return FALSE;
return TRUE;
}
static BOOL rdpdr_write_port_capset(wLog* log, pf_channel_common_context* rdpdr, wStream* s)
{
WINPR_ASSERT(rdpdr);
WINPR_ASSERT(s);
const RDPDR_CAPABILITY_HEADER header = { CAP_PORT_TYPE, 8,
rdpdr->capabilityVersions[CAP_PORT_TYPE] };
if (rdpdr_write_capset_header(log, s, &header) != CHANNEL_RC_OK)
return FALSE;
return TRUE;
}
static BOOL rdpdr_write_drive_capset(wLog* log, pf_channel_common_context* rdpdr, wStream* s)
{
WINPR_ASSERT(rdpdr);
WINPR_ASSERT(s);
const RDPDR_CAPABILITY_HEADER header = { CAP_DRIVE_TYPE, 8,
rdpdr->capabilityVersions[CAP_DRIVE_TYPE] };
if (rdpdr_write_capset_header(log, s, &header) != CHANNEL_RC_OK)
return FALSE;
return TRUE;
}
static BOOL rdpdr_write_smartcard_capset(wLog* log, pf_channel_common_context* rdpdr, wStream* s)
{
WINPR_ASSERT(rdpdr);
WINPR_ASSERT(s);
const RDPDR_CAPABILITY_HEADER header = { CAP_SMARTCARD_TYPE, 8,
rdpdr->capabilityVersions[CAP_SMARTCARD_TYPE] };
if (rdpdr_write_capset_header(log, s, &header) != CHANNEL_RC_OK)
return FALSE;
return TRUE;
}
static UINT rdpdr_send_server_capability_request(pf_channel_server_context* rdpdr)
{
wStream* s =
rdpdr_server_get_send_buffer(rdpdr, RDPDR_CTYP_CORE, PAKID_CORE_SERVER_CAPABILITY, 8);
if (!s)
return CHANNEL_RC_NO_MEMORY;
Stream_Write_UINT16(s, 5); /* numCapabilities */
Stream_Write_UINT16(s, 0); /* pad */
if (!rdpdr_write_general_capset(rdpdr->log, &rdpdr->common, s))
return CHANNEL_RC_NO_MEMORY;
if (!rdpdr_write_printer_capset(rdpdr->log, &rdpdr->common, s))
return CHANNEL_RC_NO_MEMORY;
if (!rdpdr_write_port_capset(rdpdr->log, &rdpdr->common, s))
return CHANNEL_RC_NO_MEMORY;
if (!rdpdr_write_drive_capset(rdpdr->log, &rdpdr->common, s))
return CHANNEL_RC_NO_MEMORY;
if (!rdpdr_write_smartcard_capset(rdpdr->log, &rdpdr->common, s))
return CHANNEL_RC_NO_MEMORY;
return rdpdr_seal_send_free_request(rdpdr, s);
}
static UINT rdpdr_process_client_capability_response(pf_channel_server_context* rdpdr, wStream* s)
{
const UINT16 component = RDPDR_CTYP_CORE;
const UINT16 packetid = PAKID_CORE_CLIENT_CAPABILITY;
UINT status = CHANNEL_RC_OK;
UINT16 numCapabilities = 0;
WINPR_ASSERT(rdpdr);
if (!rdpdr_process_server_header(TRUE, rdpdr->log, s, component, packetid, 4))
return ERROR_INVALID_DATA;
Stream_Read_UINT16(s, numCapabilities);
Stream_Seek_UINT16(s); /* padding */
for (UINT16 x = 0; x < numCapabilities; x++)
{
RDPDR_CAPABILITY_HEADER header = { 0 };
UINT error = rdpdr_read_capset_header(rdpdr->log, s, &header);
if (error != CHANNEL_RC_OK)
return error;
if (header.CapabilityType < ARRAYSIZE(rdpdr->common.capabilityVersions))
{
if (rdpdr->common.capabilityVersions[header.CapabilityType] > header.Version)
rdpdr->common.capabilityVersions[header.CapabilityType] = header.Version;
WLog_Print(rdpdr->log, WLOG_TRACE,
"capability %s got version %" PRIu32 ", will use version %" PRIu32,
rdpdr_cap_type_string(header.CapabilityType), header.Version,
rdpdr->common.capabilityVersions[header.CapabilityType]);
}
switch (header.CapabilityType)
{
case CAP_GENERAL_TYPE:
status = rdpdr_ignore_capset(TRUE, rdpdr->log, s, &header);
break;
case CAP_PRINTER_TYPE:
status = rdpdr_ignore_capset(TRUE, rdpdr->log, s, &header);
break;
case CAP_PORT_TYPE:
status = rdpdr_ignore_capset(TRUE, rdpdr->log, s, &header);
break;
case CAP_DRIVE_TYPE:
status = rdpdr_ignore_capset(TRUE, rdpdr->log, s, &header);
break;
case CAP_SMARTCARD_TYPE:
status = rdpdr_ignore_capset(TRUE, rdpdr->log, s, &header);
break;
default:
SERVER_RX_LOG(rdpdr->log, WLOG_WARN,
"[%s | %s] invalid capability type 0x%04" PRIx16,
rdpdr_component_string(component), rdpdr_packetid_string(packetid),
header.CapabilityType);
status = ERROR_INVALID_DATA;
break;
}
if (status != CHANNEL_RC_OK)
break;
}
return status;
}
static UINT rdpdr_send_client_capability_response(pClientContext* pc,
pf_channel_client_context* rdpdr)
{
wStream* s = NULL;
WINPR_ASSERT(rdpdr);
s = rdpdr_client_get_send_buffer(rdpdr, RDPDR_CTYP_CORE, PAKID_CORE_CLIENT_CAPABILITY, 4);
if (!s)
return CHANNEL_RC_NO_MEMORY;
Stream_Write_UINT16(s, 5); /* numCapabilities */
Stream_Write_UINT16(s, 0); /* pad */
if (!rdpdr_write_general_capset(rdpdr->log, &rdpdr->common, s))
return CHANNEL_RC_NO_MEMORY;
if (!rdpdr_write_printer_capset(rdpdr->log, &rdpdr->common, s))
return CHANNEL_RC_NO_MEMORY;
if (!rdpdr_write_port_capset(rdpdr->log, &rdpdr->common, s))
return CHANNEL_RC_NO_MEMORY;
if (!rdpdr_write_drive_capset(rdpdr->log, &rdpdr->common, s))
return CHANNEL_RC_NO_MEMORY;
if (!rdpdr_write_smartcard_capset(rdpdr->log, &rdpdr->common, s))
return CHANNEL_RC_NO_MEMORY;
return rdpdr_client_send(rdpdr->log, pc, s);
}
static UINT rdpdr_send_server_clientid_confirm(pf_channel_server_context* rdpdr)
{
wStream* s = NULL;
s = rdpdr_server_get_send_buffer(rdpdr, RDPDR_CTYP_CORE, PAKID_CORE_CLIENTID_CONFIRM, 8);
if (!s)
return CHANNEL_RC_NO_MEMORY;
Stream_Write_UINT16(s, rdpdr->common.versionMajor);
Stream_Write_UINT16(s, rdpdr->common.versionMinor);
Stream_Write_UINT32(s, rdpdr->common.clientID);
return rdpdr_seal_send_free_request(rdpdr, s);
}
static UINT rdpdr_process_server_clientid_confirm(pf_channel_client_context* rdpdr, wStream* s)
{
UINT16 versionMajor = 0;
UINT16 versionMinor = 0;
UINT32 clientID = 0;
WINPR_ASSERT(rdpdr);
WINPR_ASSERT(s);
if (!rdpdr_process_server_header(FALSE, rdpdr->log, s, RDPDR_CTYP_CORE,
PAKID_CORE_CLIENTID_CONFIRM, 8))
return ERROR_INVALID_DATA;
Stream_Read_UINT16(s, versionMajor);
Stream_Read_UINT16(s, versionMinor);
if (!rdpdr_check_version(FALSE, rdpdr->log, versionMajor, versionMinor, RDPDR_CTYP_CORE,
PAKID_CORE_CLIENTID_CONFIRM))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, clientID);
if ((versionMajor != rdpdr->common.versionMajor) ||
(versionMinor != rdpdr->common.versionMinor))
{
CLIENT_RX_LOG(rdpdr->log, WLOG_WARN,
"[%s | %s] Version mismatch, sent %" PRIu16 ".%" PRIu16
", downgraded to %" PRIu16 ".%" PRIu16,
rdpdr_component_string(RDPDR_CTYP_CORE),
rdpdr_packetid_string(PAKID_CORE_CLIENTID_CONFIRM),
rdpdr->common.versionMajor, rdpdr->common.versionMinor, versionMajor,
versionMinor);
rdpdr->common.versionMajor = versionMajor;
rdpdr->common.versionMinor = versionMinor;
}
if (clientID != rdpdr->common.clientID)
{
CLIENT_RX_LOG(rdpdr->log, WLOG_WARN,
"[%s | %s] clientID mismatch, sent 0x%08" PRIx32 ", changed to 0x%08" PRIx32,
rdpdr_component_string(RDPDR_CTYP_CORE),
rdpdr_packetid_string(PAKID_CORE_CLIENTID_CONFIRM), rdpdr->common.clientID,
clientID);
rdpdr->common.clientID = clientID;
}
return CHANNEL_RC_OK;
}
static BOOL
rdpdr_process_server_capability_request_or_clientid_confirm(pf_channel_client_context* rdpdr,
wStream* s)
{
const UINT32 mask = STATE_CLIENT_EXPECT_SERVER_CLIENT_ID_CONFIRM |
STATE_CLIENT_EXPECT_SERVER_CORE_CAPABILITY_REQUEST;
const UINT16 rcomponent = RDPDR_CTYP_CORE;
UINT16 component = 0;
UINT16 packetid = 0;
WINPR_ASSERT(rdpdr);
WINPR_ASSERT(s);
if ((rdpdr->flags & mask) == mask)
{
CLIENT_RX_LOG(rdpdr->log, WLOG_WARN, "already past this state, abort!");
return FALSE;
}
if (!Stream_CheckAndLogRequiredLengthClient(rdpdr->log, s, 4))
return FALSE;
Stream_Read_UINT16(s, component);
if (rcomponent != component)
{
CLIENT_RX_LOG(rdpdr->log, WLOG_WARN, "got component %s, expected %s",
rdpdr_component_string(component), rdpdr_component_string(rcomponent));
return FALSE;
}
Stream_Read_UINT16(s, packetid);
Stream_Rewind(s, 4);
switch (packetid)
{
case PAKID_CORE_SERVER_CAPABILITY:
if (rdpdr->flags & STATE_CLIENT_EXPECT_SERVER_CORE_CAPABILITY_REQUEST)
{
CLIENT_RX_LOG(rdpdr->log, WLOG_WARN, "got duplicate packetid %s",
rdpdr_packetid_string(packetid));
return FALSE;
}
rdpdr->flags |= STATE_CLIENT_EXPECT_SERVER_CORE_CAPABILITY_REQUEST;
return rdpdr_process_server_core_capability_request(rdpdr, s) == CHANNEL_RC_OK;
case PAKID_CORE_CLIENTID_CONFIRM:
default:
if (rdpdr->flags & STATE_CLIENT_EXPECT_SERVER_CLIENT_ID_CONFIRM)
{
CLIENT_RX_LOG(rdpdr->log, WLOG_WARN, "got duplicate packetid %s",
rdpdr_packetid_string(packetid));
return FALSE;
}
rdpdr->flags |= STATE_CLIENT_EXPECT_SERVER_CLIENT_ID_CONFIRM;
return rdpdr_process_server_clientid_confirm(rdpdr, s) == CHANNEL_RC_OK;
}
}
#if defined(WITH_PROXY_EMULATE_SMARTCARD)
static UINT rdpdr_send_emulated_scard_device_list_announce_request(pClientContext* pc,
pf_channel_client_context* rdpdr)
{
wStream* s = NULL;
s = rdpdr_client_get_send_buffer(rdpdr, RDPDR_CTYP_CORE, PAKID_CORE_DEVICELIST_ANNOUNCE, 24);
if (!s)
return CHANNEL_RC_NO_MEMORY;
Stream_Write_UINT32(s, 1); /* deviceCount -> our emulated smartcard only */
Stream_Write_UINT32(s, RDPDR_DTYP_SMARTCARD); /* deviceType */
Stream_Write_UINT32(
s, SCARD_DEVICE_ID); /* deviceID -> reserve highest value for the emulated smartcard */
Stream_Write(s, "SCARD\0\0\0", 8);
Stream_Write_UINT32(s, 6);
Stream_Write(s, "SCARD\0", 6);
return rdpdr_client_send(rdpdr->log, pc, s);
}
static UINT rdpdr_send_emulated_scard_device_remove(pClientContext* pc,
pf_channel_client_context* rdpdr)
{
wStream* s = NULL;
s = rdpdr_client_get_send_buffer(rdpdr, RDPDR_CTYP_CORE, PAKID_CORE_DEVICELIST_REMOVE, 24);
if (!s)
return CHANNEL_RC_NO_MEMORY;
Stream_Write_UINT32(s, 1); /* deviceCount -> our emulated smartcard only */
Stream_Write_UINT32(
s, SCARD_DEVICE_ID); /* deviceID -> reserve highest value for the emulated smartcard */
return rdpdr_client_send(rdpdr->log, pc, s);
}
static UINT rdpdr_process_server_device_announce_response(pf_channel_client_context* rdpdr,
wStream* s)
{
const UINT16 component = RDPDR_CTYP_CORE;
const UINT16 packetid = PAKID_CORE_DEVICE_REPLY;
UINT32 deviceID = 0;
UINT32 resultCode = 0;
WINPR_ASSERT(rdpdr);
WINPR_ASSERT(s);
if (!rdpdr_process_server_header(TRUE, rdpdr->log, s, component, packetid, 8))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, deviceID);
Stream_Read_UINT32(s, resultCode);
if (deviceID != SCARD_DEVICE_ID)
{
CLIENT_RX_LOG(rdpdr->log, WLOG_WARN,
"[%s | %s] deviceID mismatch, sent 0x%08" PRIx32 ", changed to 0x%08" PRIx32,
rdpdr_component_string(component), rdpdr_packetid_string(packetid),
SCARD_DEVICE_ID, deviceID);
}
else if (resultCode != 0)
{
CLIENT_RX_LOG(rdpdr->log, WLOG_WARN,
"[%s | %s] deviceID 0x%08" PRIx32 " resultCode=0x%08" PRIx32,
rdpdr_component_string(component), rdpdr_packetid_string(packetid), deviceID,
resultCode);
}
else
CLIENT_RX_LOG(rdpdr->log, WLOG_DEBUG,
"[%s | %s] deviceID 0x%08" PRIx32 " resultCode=0x%08" PRIx32
" -> emulated smartcard redirected!",
rdpdr_component_string(component), rdpdr_packetid_string(packetid), deviceID,
resultCode);
return CHANNEL_RC_OK;
}
#endif
static BOOL pf_channel_rdpdr_rewrite_device_list_to(wStream* s, UINT32 fromVersion,
UINT32 toVersion)
{
BOOL rc = FALSE;
if (fromVersion == toVersion)
return TRUE;
const size_t cap = Stream_GetRemainingLength(s);
wStream* clone = Stream_New(NULL, cap);
if (!clone)
goto fail;
const size_t pos = Stream_GetPosition(s);
Stream_Copy(s, clone, cap);
Stream_SealLength(clone);
Stream_SetPosition(clone, 0);
Stream_SetPosition(s, pos);
/* Skip device count */
if (!Stream_SafeSeek(s, 4))
goto fail;
UINT32 count = 0;
if (Stream_GetRemainingLength(clone) < 4)
goto fail;
Stream_Read_UINT32(clone, count);
for (UINT32 x = 0; x < count; x++)
{
RdpdrDevice device = { 0 };
const size_t charCount = ARRAYSIZE(device.PreferredDosName);
if (Stream_GetRemainingLength(clone) < 20)
goto fail;
Stream_Read_UINT32(clone, device.DeviceType); /* DeviceType (4 bytes) */
Stream_Read_UINT32(clone, device.DeviceId); /* DeviceId (4 bytes) */
Stream_Read(clone, device.PreferredDosName, charCount); /* PreferredDosName (8 bytes) */
Stream_Read_UINT32(clone, device.DeviceDataLength); /* DeviceDataLength (4 bytes) */
device.DeviceData = Stream_Pointer(clone);
if (!Stream_SafeSeek(clone, device.DeviceDataLength))
goto fail;
if (!Stream_EnsureRemainingCapacity(s, 20))
goto fail;
Stream_Write_UINT32(s, device.DeviceType);
Stream_Write_UINT32(s, device.DeviceId);
Stream_Write(s, device.PreferredDosName, charCount);
if (device.DeviceType == RDPDR_DTYP_FILESYSTEM)
{
if (toVersion == DRIVE_CAPABILITY_VERSION_01)
Stream_Write_UINT32(s, 0); /* No unicode name */
else
{
const size_t datalen = charCount * sizeof(WCHAR);
if (!Stream_EnsureRemainingCapacity(s, datalen + sizeof(UINT32)))
goto fail;
Stream_Write_UINT32(s, datalen);
const SSIZE_T rcw = Stream_Write_UTF16_String_From_UTF8(
s, charCount, device.PreferredDosName, charCount - 1, TRUE);
if (rcw < 0)
goto fail;
}
}
else
{
Stream_Write_UINT32(s, device.DeviceDataLength);
if (!Stream_EnsureRemainingCapacity(s, device.DeviceDataLength))
goto fail;
Stream_Write(s, device.DeviceData, device.DeviceDataLength);
}
}
Stream_SealLength(s);
rc = TRUE;
fail:
Stream_Free(clone, TRUE);
return rc;
}
static BOOL pf_channel_rdpdr_rewrite_device_list(pf_channel_client_context* rdpdr,
pServerContext* ps, wStream* s, BOOL toServer)
{
WINPR_ASSERT(rdpdr);
WINPR_ASSERT(ps);
const size_t pos = Stream_GetPosition(s);
UINT16 component = 0;
UINT16 packetid = 0;
Stream_SetPosition(s, 0);
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 4))
return FALSE;
Stream_Read_UINT16(s, component);
Stream_Read_UINT16(s, packetid);
if ((component != RDPDR_CTYP_CORE) || (packetid != PAKID_CORE_DEVICELIST_ANNOUNCE))
{
Stream_SetPosition(s, pos);
return TRUE;
}
const pf_channel_server_context* srv =
HashTable_GetItemValue(ps->interceptContextMap, RDPDR_SVC_CHANNEL_NAME);
if (!srv)
{
WLog_Print(rdpdr->log, WLOG_ERROR, "No channel %s in intercep map", RDPDR_SVC_CHANNEL_NAME);
return FALSE;
}
UINT32 from = srv->common.capabilityVersions[CAP_DRIVE_TYPE];
UINT32 to = rdpdr->common.capabilityVersions[CAP_DRIVE_TYPE];
if (toServer)
{
from = rdpdr->common.capabilityVersions[CAP_DRIVE_TYPE];
to = srv->common.capabilityVersions[CAP_DRIVE_TYPE];
}
if (!pf_channel_rdpdr_rewrite_device_list_to(s, from, to))
return FALSE;
Stream_SetPosition(s, pos);
return TRUE;
}
static BOOL pf_channel_rdpdr_client_send_to_server(pf_channel_client_context* rdpdr,
pServerContext* ps, wStream* s)
{
WINPR_ASSERT(rdpdr);
if (ps)
{
UINT16 server_channel_id = WTSChannelGetId(ps->context.peer, RDPDR_SVC_CHANNEL_NAME);
/* Ignore messages for channels that can not be mapped.
* The client might not have enabled support for this specific channel,
* so just drop the message. */
if (server_channel_id == 0)
return TRUE;
if (!pf_channel_rdpdr_rewrite_device_list(rdpdr, ps, s, TRUE))
return FALSE;
size_t len = Stream_Length(s);
Stream_SetPosition(s, len);
rdpdr_dump_send_packet(rdpdr->log, WLOG_TRACE, s, proxy_client_tx);
WINPR_ASSERT(ps->context.peer);
WINPR_ASSERT(ps->context.peer->SendChannelData);
return ps->context.peer->SendChannelData(ps->context.peer, server_channel_id,
Stream_Buffer(s), len);
}
return TRUE;
}
static BOOL pf_channel_send_client_queue(pClientContext* pc, pf_channel_client_context* rdpdr);
#if defined(WITH_PROXY_EMULATE_SMARTCARD)
static BOOL rdpdr_process_server_loggedon_request(pServerContext* ps, pClientContext* pc,
pf_channel_client_context* rdpdr, wStream* s,
UINT16 component, UINT16 packetid)
{
WINPR_ASSERT(rdpdr);
WLog_Print(rdpdr->log, WLOG_DEBUG, "[%s | %s]", rdpdr_component_string(component),
rdpdr_packetid_string(packetid));
if (rdpdr_send_emulated_scard_device_remove(pc, rdpdr) != CHANNEL_RC_OK)
return FALSE;
if (rdpdr_send_emulated_scard_device_list_announce_request(pc, rdpdr) != CHANNEL_RC_OK)
return FALSE;
return pf_channel_rdpdr_client_send_to_server(rdpdr, ps, s);
}
static BOOL filter_smartcard_io_requests(pf_channel_client_context* rdpdr, wStream* s,
UINT16* pPacketid)
{
BOOL rc = FALSE;
UINT16 component = 0;
UINT16 packetid = 0;
UINT32 deviceID = 0;
size_t pos = 0;
WINPR_ASSERT(rdpdr);
WINPR_ASSERT(pPacketid);
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 4))
return FALSE;
pos = Stream_GetPosition(s);
Stream_Read_UINT16(s, component);
Stream_Read_UINT16(s, packetid);
if (Stream_GetRemainingLength(s) >= 4)
Stream_Read_UINT32(s, deviceID);
WLog_Print(rdpdr->log, WLOG_DEBUG, "got: [%s | %s]: [0x%08" PRIx32 "]",
rdpdr_component_string(component), rdpdr_packetid_string(packetid), deviceID);
if (component != RDPDR_CTYP_CORE)
goto fail;
switch (packetid)
{
case PAKID_CORE_SERVER_ANNOUNCE:
case PAKID_CORE_CLIENTID_CONFIRM:
case PAKID_CORE_CLIENT_NAME:
case PAKID_CORE_DEVICELIST_ANNOUNCE:
case PAKID_CORE_DEVICELIST_REMOVE:
case PAKID_CORE_SERVER_CAPABILITY:
case PAKID_CORE_CLIENT_CAPABILITY:
WLog_Print(rdpdr->log, WLOG_WARN, "Filtering client -> server message [%s | %s]",
rdpdr_component_string(component), rdpdr_packetid_string(packetid));
*pPacketid = packetid;
break;
case PAKID_CORE_USER_LOGGEDON:
*pPacketid = packetid;
break;
case PAKID_CORE_DEVICE_REPLY:
case PAKID_CORE_DEVICE_IOREQUEST:
if (deviceID != SCARD_DEVICE_ID)
goto fail;
*pPacketid = packetid;
break;
default:
if (deviceID != SCARD_DEVICE_ID)
goto fail;
WLog_Print(rdpdr->log, WLOG_WARN,
"Got [%s | %s] for deviceID 0x%08" PRIx32 ", TODO: Not handled!",
rdpdr_component_string(component), rdpdr_packetid_string(packetid),
deviceID);
goto fail;
}
rc = TRUE;
fail:
Stream_SetPosition(s, pos);
return rc;
}
#endif
BOOL pf_channel_send_client_queue(pClientContext* pc, pf_channel_client_context* rdpdr)
{
UINT16 channelId = 0;
WINPR_ASSERT(pc);
WINPR_ASSERT(rdpdr);
if (rdpdr->state != STATE_CLIENT_CHANNEL_RUNNING)
return FALSE;
channelId = freerdp_channels_get_id_by_name(pc->context.instance, RDPDR_SVC_CHANNEL_NAME);
if ((channelId == 0) || (channelId == UINT16_MAX))
return TRUE;
Queue_Lock(rdpdr->queue);
while (Queue_Count(rdpdr->queue) > 0)
{
wStream* s = Queue_Dequeue(rdpdr->queue);
if (!s)
continue;
size_t len = Stream_Length(s);
Stream_SetPosition(s, len);
rdpdr_dump_send_packet(rdpdr->log, WLOG_TRACE, s, proxy_server_tx " (queue) ");
WINPR_ASSERT(pc->context.instance->SendChannelData);
if (!pc->context.instance->SendChannelData(pc->context.instance, channelId,
Stream_Buffer(s), len))
{
CLIENT_TX_LOG(rdpdr->log, WLOG_ERROR, "xxxxxx TODO: Failed to send data!");
}
Stream_Free(s, TRUE);
}
Queue_Unlock(rdpdr->queue);
return TRUE;
}
static BOOL rdpdr_handle_server_announce_request(pClientContext* pc,
pf_channel_client_context* rdpdr, wStream* s)
{
WINPR_ASSERT(pc);
WINPR_ASSERT(rdpdr);
WINPR_ASSERT(s);
if (rdpdr_process_server_announce_request(rdpdr, s) != CHANNEL_RC_OK)
return FALSE;
if (rdpdr_send_client_announce_reply(pc, rdpdr) != CHANNEL_RC_OK)
return FALSE;
if (rdpdr_send_client_name_request(pc, rdpdr) != CHANNEL_RC_OK)
return FALSE;
rdpdr->state = STATE_CLIENT_EXPECT_SERVER_CORE_CAPABILITY_REQUEST;
return TRUE;
}
BOOL pf_channel_rdpdr_client_handle(pClientContext* pc, UINT16 channelId, const char* channel_name,
const BYTE* xdata, size_t xsize, UINT32 flags, size_t totalSize)
{
pf_channel_client_context* rdpdr = NULL;
pServerContext* ps = NULL;
wStream* s = NULL;
#if defined(WITH_PROXY_EMULATE_SMARTCARD)
UINT16 packetid = 0;
#endif
WINPR_ASSERT(pc);
WINPR_ASSERT(pc->pdata);
WINPR_ASSERT(pc->interceptContextMap);
WINPR_ASSERT(channel_name);
WINPR_ASSERT(xdata);
ps = pc->pdata->ps;
rdpdr = HashTable_GetItemValue(pc->interceptContextMap, channel_name);
if (!rdpdr)
{
CLIENT_RX_LOG(WLog_Get(RTAG), WLOG_ERROR,
"Channel %s [0x%04" PRIx16 "] missing context in interceptContextMap",
channel_name, channelId);
return FALSE;
}
s = rdpdr->common.buffer;
if (flags & CHANNEL_FLAG_FIRST)
Stream_SetPosition(s, 0);
if (!Stream_EnsureRemainingCapacity(s, xsize))
{
CLIENT_RX_LOG(rdpdr->log, WLOG_ERROR,
"Channel %s [0x%04" PRIx16 "] not enough memory [need %" PRIuz "]",
channel_name, channelId, xsize);
return FALSE;
}
Stream_Write(s, xdata, xsize);
if ((flags & CHANNEL_FLAG_LAST) == 0)
return TRUE;
Stream_SealLength(s);
Stream_SetPosition(s, 0);
if (Stream_Length(s) != totalSize)
{
CLIENT_RX_LOG(rdpdr->log, WLOG_WARN,
"Received invalid %s channel data (server -> proxy), expected %" PRIuz
"bytes, got %" PRIuz,
channel_name, totalSize, Stream_Length(s));
return FALSE;
}
rdpdr_dump_received_packet(rdpdr->log, WLOG_TRACE, s, proxy_server_rx);
switch (rdpdr->state)
{
case STATE_CLIENT_EXPECT_SERVER_ANNOUNCE_REQUEST:
if (!rdpdr_handle_server_announce_request(pc, rdpdr, s))
return FALSE;
break;
case STATE_CLIENT_EXPECT_SERVER_CORE_CAPABILITY_REQUEST:
if (!rdpdr_process_server_capability_request_or_clientid_confirm(rdpdr, s))
return FALSE;
rdpdr->state = STATE_CLIENT_EXPECT_SERVER_CLIENT_ID_CONFIRM;
break;
case STATE_CLIENT_EXPECT_SERVER_CLIENT_ID_CONFIRM:
if (!rdpdr_process_server_capability_request_or_clientid_confirm(rdpdr, s))
return FALSE;
if (rdpdr_send_client_capability_response(pc, rdpdr) != CHANNEL_RC_OK)
return FALSE;
#if defined(WITH_PROXY_EMULATE_SMARTCARD)
if (pf_channel_smartcard_client_emulate(pc))
{
if (rdpdr_send_emulated_scard_device_list_announce_request(pc, rdpdr) !=
CHANNEL_RC_OK)
return FALSE;
rdpdr->state = STATE_CLIENT_CHANNEL_RUNNING;
}
else
#endif
{
rdpdr->state = STATE_CLIENT_CHANNEL_RUNNING;
pf_channel_send_client_queue(pc, rdpdr);
}
break;
case STATE_CLIENT_CHANNEL_RUNNING:
#if defined(WITH_PROXY_EMULATE_SMARTCARD)
if (!pf_channel_smartcard_client_emulate(pc) ||
!filter_smartcard_io_requests(rdpdr, s, &packetid))
return pf_channel_rdpdr_client_send_to_server(rdpdr, ps, s);
else
{
switch (packetid)
{
case PAKID_CORE_USER_LOGGEDON:
return rdpdr_process_server_loggedon_request(ps, pc, rdpdr, s,
RDPDR_CTYP_CORE, packetid);
case PAKID_CORE_DEVICE_IOREQUEST:
{
wStream* out = rdpdr_client_get_send_buffer(
rdpdr, RDPDR_CTYP_CORE, PAKID_CORE_DEVICE_IOCOMPLETION, 0);
WINPR_ASSERT(out);
if (!rdpdr_process_server_header(FALSE, rdpdr->log, s, RDPDR_CTYP_CORE,
PAKID_CORE_DEVICE_IOREQUEST, 20))
return FALSE;
if (!pf_channel_smartcard_client_handle(rdpdr->log, pc, s, out,
rdpdr_client_send))
return FALSE;
}
break;
case PAKID_CORE_SERVER_ANNOUNCE:
pf_channel_rdpdr_client_reset(pc);
if (!rdpdr_handle_server_announce_request(pc, rdpdr, s))
return FALSE;
break;
case PAKID_CORE_SERVER_CAPABILITY:
rdpdr->state = STATE_CLIENT_EXPECT_SERVER_CORE_CAPABILITY_REQUEST;
rdpdr->flags = 0;
return pf_channel_rdpdr_client_handle(pc, channelId, channel_name, xdata,
xsize, flags, totalSize);
case PAKID_CORE_DEVICE_REPLY:
break;
default:
CLIENT_RX_LOG(
rdpdr->log, WLOG_ERROR,
"Channel %s [0x%04" PRIx16
"] we´ve reached an impossible state %s! [%s] aliens invaded!",
channel_name, channelId, rdpdr_client_state_to_string(rdpdr->state),
rdpdr_packetid_string(packetid));
return FALSE;
}
}
break;
#else
return pf_channel_rdpdr_client_send_to_server(rdpdr, ps, s);
#endif
default:
CLIENT_RX_LOG(rdpdr->log, WLOG_ERROR,
"Channel %s [0x%04" PRIx16
"] we´ve reached an impossible state %s! aliens invaded!",
channel_name, channelId, rdpdr_client_state_to_string(rdpdr->state));
return FALSE;
}
return TRUE;
}
static void pf_channel_rdpdr_common_context_free(pf_channel_common_context* common)
{
if (!common)
return;
free(common->computerName.v);
Stream_Free(common->s, TRUE);
Stream_Free(common->buffer, TRUE);
}
static void pf_channel_rdpdr_client_context_free(InterceptContextMapEntry* base)
{
pf_channel_client_context* entry = (pf_channel_client_context*)base;
if (!entry)
return;
pf_channel_rdpdr_common_context_free(&entry->common);
Queue_Free(entry->queue);
free(entry);
}
static BOOL pf_channel_rdpdr_common_context_new(pf_channel_common_context* common,
void (*fkt)(InterceptContextMapEntry*))
{
if (!common)
return FALSE;
common->base.free = fkt;
common->s = Stream_New(NULL, 1024);
if (!common->s)
return FALSE;
common->buffer = Stream_New(NULL, 1024);
if (!common->buffer)
return FALSE;
common->computerNameUnicode = 1;
common->computerName.v = NULL;
common->versionMajor = RDPDR_VERSION_MAJOR;
common->versionMinor = RDPDR_VERSION_MINOR_RDP10X;
common->clientID = SCARD_DEVICE_ID;
const UINT32 versions[] = { 0,
GENERAL_CAPABILITY_VERSION_02,
PRINT_CAPABILITY_VERSION_01,
PORT_CAPABILITY_VERSION_01,
DRIVE_CAPABILITY_VERSION_02,
SMARTCARD_CAPABILITY_VERSION_01 };
memcpy(common->capabilityVersions, versions, sizeof(common->capabilityVersions));
return TRUE;
}
static BOOL pf_channel_rdpdr_client_pass_message(pServerContext* ps, pClientContext* pc,
UINT16 channelId, const char* channel_name,
wStream* s)
{
pf_channel_client_context* rdpdr = NULL;
WINPR_ASSERT(ps);
WINPR_ASSERT(pc);
rdpdr = HashTable_GetItemValue(pc->interceptContextMap, channel_name);
if (!rdpdr)
return TRUE; /* Ignore data for channels not available on proxy -> server connection */
WINPR_ASSERT(rdpdr->queue);
if (!pf_channel_rdpdr_rewrite_device_list(rdpdr, ps, s, FALSE))
return FALSE;
if (!Queue_Enqueue(rdpdr->queue, s))
return FALSE;
pf_channel_send_client_queue(pc, rdpdr);
return TRUE;
}
#if defined(WITH_PROXY_EMULATE_SMARTCARD)
static BOOL filter_smartcard_device_list_remove(pf_channel_server_context* rdpdr, wStream* s)
{
size_t pos = 0;
UINT32 count = 0;
WINPR_ASSERT(rdpdr);
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, sizeof(UINT32)))
return TRUE;
pos = Stream_GetPosition(s);
Stream_Read_UINT32(s, count);
if (count == 0)
return TRUE;
if (!Stream_CheckAndLogRequiredLengthOfSizeWLog(rdpdr->log, s, count, sizeof(UINT32)))
return TRUE;
for (UINT32 x = 0; x < count; x++)
{
UINT32 deviceID = 0;
BYTE* dst = Stream_Pointer(s);
Stream_Read_UINT32(s, deviceID);
if (deviceID == SCARD_DEVICE_ID)
{
ArrayList_Remove(rdpdr->blockedDevices, (void*)(size_t)deviceID);
/* This is the only device, filter it! */
if (count == 1)
return TRUE;
/* Remove this device from the list */
memmove(dst, Stream_ConstPointer(s), (count - x - 1) * sizeof(UINT32));
count--;
Stream_SetPosition(s, pos);
Stream_Write_UINT32(s, count);
return FALSE;
}
}
return FALSE;
}
static BOOL filter_smartcard_device_io_request(pf_channel_server_context* rdpdr, wStream* s)
{
UINT32 DeviceID = 0;
WINPR_ASSERT(rdpdr);
WINPR_ASSERT(s);
Stream_Read_UINT32(s, DeviceID);
return ArrayList_Contains(rdpdr->blockedDevices, (void*)(size_t)DeviceID);
}
static BOOL filter_smartcard_device_list_announce(pf_channel_server_context* rdpdr, wStream* s)
{
UINT32 count = 0;
WINPR_ASSERT(rdpdr);
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, sizeof(UINT32)))
return TRUE;
const size_t pos = Stream_GetPosition(s);
Stream_Read_UINT32(s, count);
if (count == 0)
return TRUE;
for (UINT32 x = 0; x < count; x++)
{
UINT32 DeviceType = 0;
UINT32 DeviceId = 0;
char PreferredDosName[8];
UINT32 DeviceDataLength = 0;
BYTE* dst = Stream_Pointer(s);
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 20))
return TRUE;
Stream_Read_UINT32(s, DeviceType);
Stream_Read_UINT32(s, DeviceId);
Stream_Read(s, PreferredDosName, ARRAYSIZE(PreferredDosName));
Stream_Read_UINT32(s, DeviceDataLength);
if (!Stream_SafeSeek(s, DeviceDataLength))
return TRUE;
if (DeviceType == RDPDR_DTYP_SMARTCARD)
{
ArrayList_Append(rdpdr->blockedDevices, (void*)(size_t)DeviceId);
if (count == 1)
return TRUE;
WLog_Print(rdpdr->log, WLOG_INFO, "Filtering smartcard device 0x%08" PRIx32 "",
DeviceId);
memmove(dst, Stream_ConstPointer(s), Stream_GetRemainingLength(s));
Stream_SetPosition(s, pos);
Stream_Write_UINT32(s, count - 1);
return FALSE;
}
}
return FALSE;
}
static BOOL filter_smartcard_device_list_announce_request(pf_channel_server_context* rdpdr,
wStream* s)
{
BOOL rc = TRUE;
size_t pos = 0;
UINT16 component = 0;
UINT16 packetid = 0;
WINPR_ASSERT(rdpdr);
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 8))
return FALSE;
pos = Stream_GetPosition(s);
Stream_Read_UINT16(s, component);
Stream_Read_UINT16(s, packetid);
if (component != RDPDR_CTYP_CORE)
goto fail;
switch (packetid)
{
case PAKID_CORE_DEVICELIST_ANNOUNCE:
if (filter_smartcard_device_list_announce(rdpdr, s))
goto fail;
break;
case PAKID_CORE_DEVICELIST_REMOVE:
if (filter_smartcard_device_list_remove(rdpdr, s))
goto fail;
break;
case PAKID_CORE_DEVICE_IOREQUEST:
if (filter_smartcard_device_io_request(rdpdr, s))
goto fail;
break;
case PAKID_CORE_SERVER_ANNOUNCE:
case PAKID_CORE_CLIENTID_CONFIRM:
case PAKID_CORE_CLIENT_NAME:
case PAKID_CORE_DEVICE_REPLY:
case PAKID_CORE_SERVER_CAPABILITY:
case PAKID_CORE_CLIENT_CAPABILITY:
case PAKID_CORE_USER_LOGGEDON:
WLog_Print(rdpdr->log, WLOG_WARN, "Filtering client -> server message [%s | %s]",
rdpdr_component_string(component), rdpdr_packetid_string(packetid));
goto fail;
default:
break;
}
rc = FALSE;
fail:
Stream_SetPosition(s, pos);
return rc;
}
#endif
static void* stream_copy(const void* obj)
{
const wStream* src = obj;
wStream* dst = Stream_New(NULL, Stream_Capacity(src));
if (!dst)
return NULL;
memcpy(Stream_Buffer(dst), Stream_ConstBuffer(src), Stream_Capacity(dst));
Stream_SetLength(dst, Stream_Length(src));
Stream_SetPosition(dst, Stream_GetPosition(src));
return dst;
}
static void stream_free(void* obj)
{
wStream* s = obj;
Stream_Free(s, TRUE);
}
static const char* pf_channel_rdpdr_client_context(void* arg)
{
pClientContext* pc = arg;
if (!pc)
return "pc=null";
if (!pc->pdata)
return "pc->pdata=null";
return pc->pdata->session_id;
}
BOOL pf_channel_rdpdr_client_new(pClientContext* pc)
{
wObject* obj = NULL;
pf_channel_client_context* rdpdr = NULL;
WINPR_ASSERT(pc);
WINPR_ASSERT(pc->interceptContextMap);
rdpdr = calloc(1, sizeof(pf_channel_client_context));
if (!rdpdr)
return FALSE;
rdpdr->log = WLog_Get(RTAG);
WINPR_ASSERT(rdpdr->log);
WLog_SetContext(rdpdr->log, pf_channel_rdpdr_client_context, pc);
if (!pf_channel_rdpdr_common_context_new(&rdpdr->common, pf_channel_rdpdr_client_context_free))
goto fail;
rdpdr->maxMajorVersion = RDPDR_VERSION_MAJOR;
rdpdr->maxMinorVersion = RDPDR_VERSION_MINOR_RDP10X;
rdpdr->state = STATE_CLIENT_EXPECT_SERVER_ANNOUNCE_REQUEST;
rdpdr->queue = Queue_New(TRUE, 0, 0);
if (!rdpdr->queue)
goto fail;
obj = Queue_Object(rdpdr->queue);
WINPR_ASSERT(obj);
obj->fnObjectNew = stream_copy;
obj->fnObjectFree = stream_free;
if (!HashTable_Insert(pc->interceptContextMap, RDPDR_SVC_CHANNEL_NAME, rdpdr))
goto fail;
// NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert takes ownership of rdpdr
return TRUE;
fail:
pf_channel_rdpdr_client_context_free(&rdpdr->common.base);
return FALSE;
}
void pf_channel_rdpdr_client_free(pClientContext* pc)
{
WINPR_ASSERT(pc);
WINPR_ASSERT(pc->interceptContextMap);
HashTable_Remove(pc->interceptContextMap, RDPDR_SVC_CHANNEL_NAME);
}
static void pf_channel_rdpdr_server_context_free(InterceptContextMapEntry* base)
{
pf_channel_server_context* entry = (pf_channel_server_context*)base;
if (!entry)
return;
(void)WTSVirtualChannelClose(entry->handle);
pf_channel_rdpdr_common_context_free(&entry->common);
ArrayList_Free(entry->blockedDevices);
free(entry);
}
static const char* pf_channel_rdpdr_server_context(void* arg)
{
pServerContext* ps = arg;
if (!ps)
return "ps=null";
if (!ps->pdata)
return "ps->pdata=null";
return ps->pdata->session_id;
}
BOOL pf_channel_rdpdr_server_new(pServerContext* ps)
{
pf_channel_server_context* rdpdr = NULL;
PULONG pSessionId = NULL;
DWORD BytesReturned = 0;
WINPR_ASSERT(ps);
WINPR_ASSERT(ps->interceptContextMap);
rdpdr = calloc(1, sizeof(pf_channel_server_context));
if (!rdpdr)
return FALSE;
rdpdr->log = WLog_Get(RTAG);
WINPR_ASSERT(rdpdr->log);
WLog_SetContext(rdpdr->log, pf_channel_rdpdr_server_context, ps);
if (!pf_channel_rdpdr_common_context_new(&rdpdr->common, pf_channel_rdpdr_server_context_free))
goto fail;
rdpdr->state = STATE_SERVER_INITIAL;
rdpdr->blockedDevices = ArrayList_New(FALSE);
if (!rdpdr->blockedDevices)
goto fail;
rdpdr->SessionId = WTS_CURRENT_SESSION;
if (WTSQuerySessionInformationA(ps->vcm, WTS_CURRENT_SESSION, WTSSessionId, (LPSTR*)&pSessionId,
&BytesReturned))
{
rdpdr->SessionId = (DWORD)*pSessionId;
WTSFreeMemory(pSessionId);
}
rdpdr->handle = WTSVirtualChannelOpenEx(rdpdr->SessionId, RDPDR_SVC_CHANNEL_NAME, 0);
if (rdpdr->handle == 0)
goto fail;
if (!HashTable_Insert(ps->interceptContextMap, RDPDR_SVC_CHANNEL_NAME, rdpdr))
goto fail;
// NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert takes ownership of rdpdr
return TRUE;
fail:
pf_channel_rdpdr_server_context_free(&rdpdr->common.base);
return FALSE;
}
void pf_channel_rdpdr_server_free(pServerContext* ps)
{
WINPR_ASSERT(ps);
WINPR_ASSERT(ps->interceptContextMap);
HashTable_Remove(ps->interceptContextMap, RDPDR_SVC_CHANNEL_NAME);
}
static pf_channel_server_context* get_channel(pServerContext* ps, BOOL send)
{
pf_channel_server_context* rdpdr = NULL;
WINPR_ASSERT(ps);
WINPR_ASSERT(ps->interceptContextMap);
rdpdr = HashTable_GetItemValue(ps->interceptContextMap, RDPDR_SVC_CHANNEL_NAME);
if (!rdpdr)
{
SERVER_RXTX_LOG(send, WLog_Get(RTAG), WLOG_ERROR,
"Channel %s missing context in interceptContextMap",
RDPDR_SVC_CHANNEL_NAME);
return NULL;
}
return rdpdr;
}
BOOL pf_channel_rdpdr_server_handle(pServerContext* ps, UINT16 channelId, const char* channel_name,
const BYTE* xdata, size_t xsize, UINT32 flags, size_t totalSize)
{
wStream* s = NULL;
pClientContext* pc = NULL;
pf_channel_server_context* rdpdr = get_channel(ps, FALSE);
if (!rdpdr)
return FALSE;
WINPR_ASSERT(ps->pdata);
pc = ps->pdata->pc;
s = rdpdr->common.buffer;
if (flags & CHANNEL_FLAG_FIRST)
Stream_SetPosition(s, 0);
if (!Stream_EnsureRemainingCapacity(s, xsize))
return FALSE;
Stream_Write(s, xdata, xsize);
if ((flags & CHANNEL_FLAG_LAST) == 0)
return TRUE;
Stream_SealLength(s);
Stream_SetPosition(s, 0);
if (Stream_Length(s) != totalSize)
{
SERVER_RX_LOG(rdpdr->log, WLOG_WARN,
"Received invalid %s channel data (client -> proxy), expected %" PRIuz
"bytes, got %" PRIuz,
channel_name, totalSize, Stream_Length(s));
return FALSE;
}
rdpdr_dump_received_packet(rdpdr->log, WLOG_TRACE, s, proxy_client_rx);
switch (rdpdr->state)
{
case STATE_SERVER_EXPECT_CLIENT_ANNOUNCE_REPLY:
if (rdpdr_process_client_announce_reply(rdpdr, s) != CHANNEL_RC_OK)
return FALSE;
rdpdr->state = STATE_SERVER_EXPECT_CLIENT_NAME_REQUEST;
break;
case STATE_SERVER_EXPECT_CLIENT_NAME_REQUEST:
if (rdpdr_process_client_name_request(rdpdr, s, pc) != CHANNEL_RC_OK)
return FALSE;
if (rdpdr_send_server_capability_request(rdpdr) != CHANNEL_RC_OK)
return FALSE;
if (rdpdr_send_server_clientid_confirm(rdpdr) != CHANNEL_RC_OK)
return FALSE;
rdpdr->state = STATE_SERVER_EXPECT_EXPECT_CLIENT_CAPABILITY_RESPONE;
break;
case STATE_SERVER_EXPECT_EXPECT_CLIENT_CAPABILITY_RESPONE:
if (rdpdr_process_client_capability_response(rdpdr, s) != CHANNEL_RC_OK)
return FALSE;
rdpdr->state = STATE_SERVER_CHANNEL_RUNNING;
break;
case STATE_SERVER_CHANNEL_RUNNING:
#if defined(WITH_PROXY_EMULATE_SMARTCARD)
if (!pf_channel_smartcard_client_emulate(pc) ||
!filter_smartcard_device_list_announce_request(rdpdr, s))
{
if (!pf_channel_rdpdr_client_pass_message(ps, pc, channelId, channel_name, s))
return FALSE;
}
else
return pf_channel_smartcard_server_handle(ps, s);
#else
if (!pf_channel_rdpdr_client_pass_message(ps, pc, channelId, channel_name, s))
return FALSE;
#endif
break;
default:
case STATE_SERVER_INITIAL:
SERVER_RX_LOG(rdpdr->log, WLOG_WARN, "Invalid state %s",
rdpdr_server_state_to_string(rdpdr->state));
return FALSE;
}
return TRUE;
}
BOOL pf_channel_rdpdr_server_announce(pServerContext* ps)
{
pf_channel_server_context* rdpdr = get_channel(ps, TRUE);
if (!rdpdr)
return FALSE;
WINPR_ASSERT(rdpdr->state == STATE_SERVER_INITIAL);
if (rdpdr_server_send_announce_request(rdpdr) != CHANNEL_RC_OK)
return FALSE;
rdpdr->state = STATE_SERVER_EXPECT_CLIENT_ANNOUNCE_REPLY;
return TRUE;
}
BOOL pf_channel_rdpdr_client_reset(pClientContext* pc)
{
pf_channel_client_context* rdpdr = NULL;
WINPR_ASSERT(pc);
WINPR_ASSERT(pc->pdata);
WINPR_ASSERT(pc->interceptContextMap);
rdpdr = HashTable_GetItemValue(pc->interceptContextMap, RDPDR_SVC_CHANNEL_NAME);
if (!rdpdr)
return TRUE;
Queue_Clear(rdpdr->queue);
rdpdr->flags = 0;
rdpdr->state = STATE_CLIENT_EXPECT_SERVER_ANNOUNCE_REQUEST;
return TRUE;
}
static PfChannelResult pf_rdpdr_back_data(proxyData* pdata,
const pServerStaticChannelContext* channel,
const BYTE* xdata, size_t xsize, UINT32 flags,
size_t totalSize)
{
WINPR_ASSERT(pdata);
WINPR_ASSERT(channel);
if (!pf_channel_rdpdr_client_handle(pdata->pc, channel->back_channel_id, channel->channel_name,
xdata, xsize, flags, totalSize))
return PF_CHANNEL_RESULT_ERROR;
#if defined(WITH_PROXY_EMULATE_SMARTCARD)
if (pf_channel_smartcard_client_emulate(pdata->pc))
return PF_CHANNEL_RESULT_DROP;
#endif
return PF_CHANNEL_RESULT_DROP;
}
static PfChannelResult pf_rdpdr_front_data(proxyData* pdata,
const pServerStaticChannelContext* channel,
const BYTE* xdata, size_t xsize, UINT32 flags,
size_t totalSize)
{
WINPR_ASSERT(pdata);
WINPR_ASSERT(channel);
if (!pf_channel_rdpdr_server_handle(pdata->ps, channel->front_channel_id, channel->channel_name,
xdata, xsize, flags, totalSize))
return PF_CHANNEL_RESULT_ERROR;
#if defined(WITH_PROXY_EMULATE_SMARTCARD)
if (pf_channel_smartcard_client_emulate(pdata->pc))
return PF_CHANNEL_RESULT_DROP;
#endif
return PF_CHANNEL_RESULT_DROP;
}
BOOL pf_channel_setup_rdpdr(pServerContext* ps, pServerStaticChannelContext* channel)
{
channel->onBackData = pf_rdpdr_back_data;
channel->onFrontData = pf_rdpdr_front_data;
if (!pf_channel_rdpdr_server_new(ps))
return FALSE;
if (!pf_channel_rdpdr_server_announce(ps))
return FALSE;
return TRUE;
}