Add RDP Smart Card Redirection Support
Resolves: RHEL-171999
This commit is contained in:
parent
6ecf4a031a
commit
b01e0672d5
551
channels-handle-CHANNEL_OPTION_SHOW_PROTOCOL-server-.patch
Normal file
551
channels-handle-CHANNEL_OPTION_SHOW_PROTOCOL-server-.patch
Normal file
@ -0,0 +1,551 @@
|
||||
From 2e1113794d125269f30fc7acf4d0f3d898d74d52 Mon Sep 17 00:00:00 2001
|
||||
From: David Fort <contact@hardening-consulting.com>
|
||||
Date: Mon, 15 Jun 2026 16:30:40 +0200
|
||||
Subject: [PATCH] [channels] handle CHANNEL_OPTION_SHOW_PROTOCOL server-side
|
||||
|
||||
With some static channels like rdpdr or clipdr there's no marking of packet
|
||||
length so in theory you can't handle packets that you don't know (you don't know
|
||||
how many bytes to skip) or that are badly encoded (WTSVirtualChannelRead linearizes
|
||||
the traffic so you can't know which bytes are part of the current PDU and which
|
||||
bytes are from the next one). To solve that this patch adds server-side the handling
|
||||
of a CHANNEL_OPTION_SHOW_PROTOCOL with WTSVirtualChannelOpen, when you set that
|
||||
option you get CHANNEL_PDU_HEADER before the packet payload, and so you can do the
|
||||
reassembly of a PDU. This allows to skip a PDU when you don't know the type, and
|
||||
go to the next one correctly if for instance the padding at the end of the PDU is
|
||||
incorrect (think of some smartcard commands invalidly padded by FreeRDP).
|
||||
|
||||
The patch also provides a ChannelPduTracker component that can do the reassembly
|
||||
for you.
|
||||
---
|
||||
include/freerdp/utils/channel_pdu_tracker.h | 65 +++++++++++
|
||||
libfreerdp/core/peer.c | 60 +++++-----
|
||||
libfreerdp/core/server.c | 82 ++++++++------
|
||||
libfreerdp/utils/CMakeLists.txt | 1 +
|
||||
libfreerdp/utils/channel_pdu_tracker.c | 119 ++++++++++++++++++++
|
||||
5 files changed, 259 insertions(+), 68 deletions(-)
|
||||
create mode 100644 include/freerdp/utils/channel_pdu_tracker.h
|
||||
create mode 100644 libfreerdp/utils/channel_pdu_tracker.c
|
||||
|
||||
diff --git a/include/freerdp/utils/channel_pdu_tracker.h b/include/freerdp/utils/channel_pdu_tracker.h
|
||||
new file mode 100644
|
||||
index 0000000..96c52b5
|
||||
--- /dev/null
|
||||
+++ b/include/freerdp/utils/channel_pdu_tracker.h
|
||||
@@ -0,0 +1,65 @@
|
||||
+/**
|
||||
+ * FreeRDP: A Remote Desktop Protocol Implementation
|
||||
+ * static channel utility functions
|
||||
+ *
|
||||
+ * Copyright 2026 David Fort <contact@hardening-consulting.com>
|
||||
+ *
|
||||
+ * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
+ * you may not use this file except in compliance with the License.
|
||||
+ * You may obtain a copy of the License at
|
||||
+ *
|
||||
+ * http://www.apache.org/licenses/LICENSE-2.0
|
||||
+ *
|
||||
+ * Unless required by applicable law or agreed to in writing, software
|
||||
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
+ * See the License for the specific language governing permissions and
|
||||
+ * limitations under the License.
|
||||
+ */
|
||||
+
|
||||
+#ifndef FREERDP_UTILS_CHANNEL_TRACKER_H_
|
||||
+#define FREERDP_UTILS_CHANNEL_TRACKER_H_
|
||||
+
|
||||
+#include <winpr/wtypes.h>
|
||||
+#include <winpr/stream.h>
|
||||
+
|
||||
+#include <freerdp/api.h>
|
||||
+
|
||||
+#ifdef __cplusplus
|
||||
+extern "C"
|
||||
+{
|
||||
+#endif
|
||||
+
|
||||
+ typedef struct ChannelPduTracker ChannelPduTracker;
|
||||
+
|
||||
+ /**
|
||||
+ * frees a ChannelPduTracker
|
||||
+ * @param tracker the tracker to release
|
||||
+ * @since version 3.28.0
|
||||
+ */
|
||||
+ FREERDP_API void ChannelPduTracker_free(ChannelPduTracker* tracker);
|
||||
+
|
||||
+ /**
|
||||
+ * Creates a ChannelPduTracker
|
||||
+ * @param vc the HANDLE of the channel
|
||||
+ * @return the created ChannelPduTracker, nullptr if something failed
|
||||
+ * @since version 3.28.0
|
||||
+ */
|
||||
+ WINPR_ATTR_MALLOC(ChannelPduTracker_free, 1)
|
||||
+ FREERDP_API ChannelPduTracker* ChannelPduTracker_new(HANDLE vc);
|
||||
+
|
||||
+ /**
|
||||
+ * Polls the channel and return a PDU if a whole PDU was reassembled
|
||||
+ * @param tracker the ChannelPduTracker to poll a packet from
|
||||
+ * @param ok a pointer to a status BOOL
|
||||
+ * @return the reassembled PDU if any
|
||||
+ * @since version 3.28.0
|
||||
+ */
|
||||
+ WINPR_ATTR_MALLOC(Stream_Release, 1)
|
||||
+ FREERDP_API wStream* ChannelPduTracker_poll(ChannelPduTracker* tracker, BOOL* ok);
|
||||
+
|
||||
+#ifdef __cplusplus
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
+#endif /* FREERDP_UTILS_CHANNEL_TRACKER_H_ */
|
||||
diff --git a/libfreerdp/core/peer.c b/libfreerdp/core/peer.c
|
||||
index 3e3f024..355c578 100644
|
||||
--- a/libfreerdp/core/peer.c
|
||||
+++ b/libfreerdp/core/peer.c
|
||||
@@ -48,17 +48,11 @@ static state_run_t peer_recv_pdu(freerdp_peer* client, wStream* s);
|
||||
static HANDLE freerdp_peer_virtual_channel_open(freerdp_peer* client, const char* name,
|
||||
UINT32 flags)
|
||||
{
|
||||
- UINT32 index = 0;
|
||||
- BOOL joined = FALSE;
|
||||
- rdpMcsChannel* mcsChannel = nullptr;
|
||||
- rdpPeerChannel* peerChannel = nullptr;
|
||||
- rdpMcs* mcs = nullptr;
|
||||
-
|
||||
WINPR_ASSERT(client);
|
||||
WINPR_ASSERT(client->context);
|
||||
WINPR_ASSERT(client->context->rdp);
|
||||
WINPR_ASSERT(name);
|
||||
- mcs = client->context->rdp->mcs;
|
||||
+ rdpMcs* mcs = client->context->rdp->mcs;
|
||||
WINPR_ASSERT(mcs);
|
||||
|
||||
if (flags & WTS_CHANNEL_OPTION_DYNAMIC)
|
||||
@@ -69,6 +63,9 @@ static HANDLE freerdp_peer_virtual_channel_open(freerdp_peer* client, const char
|
||||
if (length > 8)
|
||||
return nullptr; /* SVC maximum name length is 8 */
|
||||
|
||||
+ BOOL joined = FALSE;
|
||||
+ UINT32 index = 0;
|
||||
+ rdpMcsChannel* mcsChannel = nullptr;
|
||||
for (; index < mcs->channelCount; index++)
|
||||
{
|
||||
mcsChannel = &(mcs->channels[index]);
|
||||
@@ -86,7 +83,7 @@ static HANDLE freerdp_peer_virtual_channel_open(freerdp_peer* client, const char
|
||||
if (!joined)
|
||||
return nullptr; /* channel is not joined */
|
||||
|
||||
- peerChannel = (rdpPeerChannel*)mcsChannel->handle;
|
||||
+ rdpPeerChannel* peerChannel = (rdpPeerChannel*)mcsChannel->handle;
|
||||
|
||||
if (peerChannel)
|
||||
{
|
||||
@@ -1473,33 +1470,30 @@ freerdp_peer* freerdp_peer_new(int sockfd)
|
||||
(void)freerdp_tcp_set_nodelay(WLog_Get(TAG), WLOG_DEBUG, sockfd);
|
||||
}
|
||||
|
||||
- if (client)
|
||||
- {
|
||||
- client->sockfd = sockfd;
|
||||
- client->ContextSize = sizeof(rdpContext);
|
||||
- client->Initialize = freerdp_peer_initialize;
|
||||
+ client->sockfd = sockfd;
|
||||
+ client->ContextSize = sizeof(rdpContext);
|
||||
+ client->Initialize = freerdp_peer_initialize;
|
||||
#if defined(WITH_FREERDP_DEPRECATED)
|
||||
- client->GetFileDescriptor = freerdp_peer_get_fds;
|
||||
+ client->GetFileDescriptor = freerdp_peer_get_fds;
|
||||
#endif
|
||||
- client->GetEventHandle = freerdp_peer_get_event_handle;
|
||||
- client->GetEventHandles = freerdp_peer_get_event_handles;
|
||||
- client->CheckFileDescriptor = freerdp_peer_check_fds;
|
||||
- client->Close = freerdp_peer_close;
|
||||
- client->Disconnect = freerdp_peer_disconnect;
|
||||
- client->SendChannelData = freerdp_peer_send_channel_data;
|
||||
- client->SendChannelPacket = freerdp_peer_send_channel_packet;
|
||||
- client->SendServerRedirection = freerdp_peer_send_server_redirection_pdu;
|
||||
- client->IsWriteBlocked = freerdp_peer_is_write_blocked;
|
||||
- client->DrainOutputBuffer = freerdp_peer_drain_output_buffer;
|
||||
- client->HasMoreToRead = freerdp_peer_has_more_to_read;
|
||||
- client->VirtualChannelOpen = freerdp_peer_virtual_channel_open;
|
||||
- client->VirtualChannelClose = freerdp_peer_virtual_channel_close;
|
||||
- client->VirtualChannelWrite = freerdp_peer_virtual_channel_write;
|
||||
- client->VirtualChannelRead = nullptr; /* must be defined by server application */
|
||||
- client->VirtualChannelGetData = freerdp_peer_virtual_channel_get_data;
|
||||
- client->VirtualChannelSetData = freerdp_peer_virtual_channel_set_data;
|
||||
- client->SetState = freerdp_peer_set_state;
|
||||
- }
|
||||
+ client->GetEventHandle = freerdp_peer_get_event_handle;
|
||||
+ client->GetEventHandles = freerdp_peer_get_event_handles;
|
||||
+ client->CheckFileDescriptor = freerdp_peer_check_fds;
|
||||
+ client->Close = freerdp_peer_close;
|
||||
+ client->Disconnect = freerdp_peer_disconnect;
|
||||
+ client->SendChannelData = freerdp_peer_send_channel_data;
|
||||
+ client->SendChannelPacket = freerdp_peer_send_channel_packet;
|
||||
+ client->SendServerRedirection = freerdp_peer_send_server_redirection_pdu;
|
||||
+ client->IsWriteBlocked = freerdp_peer_is_write_blocked;
|
||||
+ client->DrainOutputBuffer = freerdp_peer_drain_output_buffer;
|
||||
+ client->HasMoreToRead = freerdp_peer_has_more_to_read;
|
||||
+ client->VirtualChannelOpen = freerdp_peer_virtual_channel_open;
|
||||
+ client->VirtualChannelClose = freerdp_peer_virtual_channel_close;
|
||||
+ client->VirtualChannelWrite = freerdp_peer_virtual_channel_write;
|
||||
+ client->VirtualChannelRead = nullptr; /* must be defined by server application */
|
||||
+ client->VirtualChannelGetData = freerdp_peer_virtual_channel_get_data;
|
||||
+ client->VirtualChannelSetData = freerdp_peer_virtual_channel_set_data;
|
||||
+ client->SetState = freerdp_peer_set_state;
|
||||
|
||||
return client;
|
||||
}
|
||||
diff --git a/libfreerdp/core/server.c b/libfreerdp/core/server.c
|
||||
index 3c998f0..7afcb62 100644
|
||||
--- a/libfreerdp/core/server.c
|
||||
+++ b/libfreerdp/core/server.c
|
||||
@@ -76,23 +76,27 @@ static rdpPeerChannel* wts_get_dvc_channel_by_id(WTSVirtualChannelManager* vcm,
|
||||
return HashTable_GetItemValue(vcm->dynamicVirtualChannels, &ChannelId);
|
||||
}
|
||||
|
||||
-static BOOL wts_queue_receive_data(rdpPeerChannel* channel, const BYTE* Buffer, UINT32 Length)
|
||||
+static BOOL wts_queue_receive_data(rdpPeerChannel* channel, const BYTE* Buffer1, UINT32 Length1,
|
||||
+ const BYTE* Buffer2, UINT32 Length2)
|
||||
{
|
||||
- BYTE* buffer = nullptr;
|
||||
- wtsChannelMessage* messageCtx = nullptr;
|
||||
-
|
||||
WINPR_ASSERT(channel);
|
||||
- messageCtx = (wtsChannelMessage*)malloc(sizeof(wtsChannelMessage) + Length);
|
||||
|
||||
+ wtsChannelMessage* messageCtx =
|
||||
+ (wtsChannelMessage*)malloc(sizeof(wtsChannelMessage) + Length1 + Length2);
|
||||
if (!messageCtx)
|
||||
return FALSE;
|
||||
|
||||
WINPR_ASSERT(channel->channelId <= UINT16_MAX);
|
||||
messageCtx->channelId = (UINT16)channel->channelId;
|
||||
- messageCtx->length = Length;
|
||||
+ messageCtx->length = Length1 + Length2;
|
||||
messageCtx->offset = 0;
|
||||
- buffer = (BYTE*)(messageCtx + 1);
|
||||
- CopyMemory(buffer, Buffer, Length);
|
||||
+ BYTE* buffer = (BYTE*)(messageCtx + 1);
|
||||
+ CopyMemory(buffer, Buffer1, Length1);
|
||||
+ if (Length2 && Buffer2)
|
||||
+ {
|
||||
+ buffer += Length1;
|
||||
+ CopyMemory(buffer, Buffer2, Length2);
|
||||
+ }
|
||||
return MessageQueue_Post(channel->queue, messageCtx, 0, nullptr, nullptr);
|
||||
}
|
||||
|
||||
@@ -252,7 +256,7 @@ static BOOL wts_read_drdynvc_data(rdpPeerChannel* channel, wStream* s, UINT32 le
|
||||
if (Stream_GetPosition(channel->receiveData) >= channel->dvc_total_length)
|
||||
{
|
||||
ret = wts_queue_receive_data(channel, Stream_Buffer(channel->receiveData),
|
||||
- channel->dvc_total_length);
|
||||
+ channel->dvc_total_length, nullptr, 0);
|
||||
channel->dvc_total_length = 0;
|
||||
}
|
||||
else
|
||||
@@ -260,7 +264,7 @@ static BOOL wts_read_drdynvc_data(rdpPeerChannel* channel, wStream* s, UINT32 le
|
||||
}
|
||||
else
|
||||
{
|
||||
- ret = wts_queue_receive_data(channel, Stream_ConstPointer(s), length);
|
||||
+ ret = wts_queue_receive_data(channel, Stream_ConstPointer(s), length, nullptr, 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -454,6 +458,17 @@ static BOOL WTSProcessChannelData(rdpPeerChannel* channel, UINT16 channelId, con
|
||||
WINPR_ASSERT(channel->vcm);
|
||||
WINPR_UNUSED(channelId);
|
||||
|
||||
+ if (channel->channelFlags & CHANNEL_OPTION_SHOW_PROTOCOL)
|
||||
+ {
|
||||
+ const CHANNEL_PDU_HEADER header = {
|
||||
+ .length = (UINT32)size,
|
||||
+ .flags = flags,
|
||||
+ };
|
||||
+
|
||||
+ return wts_queue_receive_data(channel, (const BYTE*)&header, sizeof(header), data,
|
||||
+ (UINT32)size);
|
||||
+ }
|
||||
+
|
||||
if (flags & CHANNEL_FLAG_FIRST)
|
||||
{
|
||||
Stream_ResetPosition(channel->receiveData);
|
||||
@@ -482,7 +497,7 @@ static BOOL WTSProcessChannelData(rdpPeerChannel* channel, UINT16 channelId, con
|
||||
ret = FALSE;
|
||||
else
|
||||
ret = wts_queue_receive_data(channel, Stream_Buffer(channel->receiveData),
|
||||
- (UINT32)pos);
|
||||
+ (UINT32)pos, nullptr, 0);
|
||||
}
|
||||
|
||||
Stream_ResetPosition(channel->receiveData);
|
||||
@@ -1353,7 +1368,7 @@ static void peer_channel_queue_free_message(void* obj)
|
||||
|
||||
static rdpPeerChannel* channel_new(WTSVirtualChannelManager* vcm, freerdp_peer* client,
|
||||
UINT32 ChannelId, UINT16 index, UINT16 type, size_t chunkSize,
|
||||
- const char* name)
|
||||
+ const char* name, UINT32 flags)
|
||||
{
|
||||
wObject queueCallbacks = WINPR_C_ARRAY_INIT;
|
||||
queueCallbacks.fnObjectFree = peer_channel_queue_free_message;
|
||||
@@ -1371,6 +1386,7 @@ static rdpPeerChannel* channel_new(WTSVirtualChannelManager* vcm, freerdp_peer*
|
||||
channel->channelType = type;
|
||||
channel->creationStatus =
|
||||
(type == RDP_PEER_CHANNEL_TYPE_SVC) ? ERROR_SUCCESS : ERROR_OPERATION_IN_PROGRESS;
|
||||
+ channel->channelFlags = flags;
|
||||
|
||||
return channel;
|
||||
fail:
|
||||
@@ -1378,44 +1394,35 @@ fail:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
-HANDLE WINAPI FreeRDP_WTSVirtualChannelOpen(HANDLE hServer, WINPR_ATTR_UNUSED DWORD SessionId,
|
||||
- LPSTR pVirtualName)
|
||||
+HANDLE WINAPI FreeRDP_WTSVirtualChannelOpenStatic(HANDLE hServer, WINPR_ATTR_UNUSED DWORD SessionId,
|
||||
+ LPSTR pVirtualName, UINT32 Flags)
|
||||
{
|
||||
- size_t length = 0;
|
||||
- rdpMcs* mcs = nullptr;
|
||||
- rdpMcsChannel* joined_channel = nullptr;
|
||||
- freerdp_peer* client = nullptr;
|
||||
- rdpPeerChannel* channel = nullptr;
|
||||
- WTSVirtualChannelManager* vcm = nullptr;
|
||||
- HANDLE hChannelHandle = nullptr;
|
||||
- rdpContext* context = nullptr;
|
||||
- vcm = (WTSVirtualChannelManager*)hServer;
|
||||
-
|
||||
+ WTSVirtualChannelManager* vcm = (WTSVirtualChannelManager*)hServer;
|
||||
if (!vcm)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_DATA);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
- client = vcm->client;
|
||||
+ freerdp_peer* client = vcm->client;
|
||||
WINPR_ASSERT(client);
|
||||
|
||||
- context = client->context;
|
||||
+ rdpContext* context = client->context;
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(context->rdp);
|
||||
WINPR_ASSERT(context->settings);
|
||||
|
||||
- mcs = context->rdp->mcs;
|
||||
+ rdpMcs* mcs = context->rdp->mcs;
|
||||
WINPR_ASSERT(mcs);
|
||||
|
||||
- length = strnlen(pVirtualName, CHANNEL_NAME_LEN + 1);
|
||||
-
|
||||
+ size_t length = strnlen(pVirtualName, CHANNEL_NAME_LEN + 1);
|
||||
if (length > CHANNEL_NAME_LEN)
|
||||
{
|
||||
SetLastError(ERROR_NOT_FOUND);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+ rdpMcsChannel* joined_channel = nullptr;
|
||||
UINT32 index = 0;
|
||||
for (; index < mcs->channelCount; index++)
|
||||
{
|
||||
@@ -1433,8 +1440,7 @@ HANDLE WINAPI FreeRDP_WTSVirtualChannelOpen(HANDLE hServer, WINPR_ATTR_UNUSED DW
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
- channel = (rdpPeerChannel*)joined_channel->handle;
|
||||
-
|
||||
+ rdpPeerChannel* channel = (rdpPeerChannel*)joined_channel->handle;
|
||||
if (!channel)
|
||||
{
|
||||
const UINT32 VCChunkSize =
|
||||
@@ -1442,7 +1448,7 @@ HANDLE WINAPI FreeRDP_WTSVirtualChannelOpen(HANDLE hServer, WINPR_ATTR_UNUSED DW
|
||||
|
||||
WINPR_ASSERT(index <= UINT16_MAX);
|
||||
channel = channel_new(vcm, client, joined_channel->ChannelId, (UINT16)index,
|
||||
- RDP_PEER_CHANNEL_TYPE_SVC, VCChunkSize, pVirtualName);
|
||||
+ RDP_PEER_CHANNEL_TYPE_SVC, VCChunkSize, pVirtualName, Flags);
|
||||
|
||||
if (!channel)
|
||||
goto fail;
|
||||
@@ -1450,7 +1456,7 @@ HANDLE WINAPI FreeRDP_WTSVirtualChannelOpen(HANDLE hServer, WINPR_ATTR_UNUSED DW
|
||||
joined_channel->handle = channel;
|
||||
}
|
||||
|
||||
- hChannelHandle = (HANDLE)channel;
|
||||
+ HANDLE hChannelHandle = (HANDLE)channel;
|
||||
return hChannelHandle;
|
||||
fail:
|
||||
channel_free(channel);
|
||||
@@ -1458,6 +1464,11 @@ fail:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+HANDLE WINAPI FreeRDP_WTSVirtualChannelOpen(HANDLE hServer, DWORD SessionId, LPSTR pVirtualName)
|
||||
+{
|
||||
+ return FreeRDP_WTSVirtualChannelOpenStatic(hServer, SessionId, pVirtualName, 0);
|
||||
+}
|
||||
+
|
||||
HANDLE WINAPI FreeRDP_WTSVirtualChannelOpenEx(DWORD SessionId, LPSTR pVirtualName, DWORD flags)
|
||||
{
|
||||
wStream* s = nullptr;
|
||||
@@ -1481,7 +1492,7 @@ HANDLE WINAPI FreeRDP_WTSVirtualChannelOpenEx(DWORD SessionId, LPSTR pVirtualNam
|
||||
if (!(flags & WTS_CHANNEL_OPTION_DYNAMIC))
|
||||
{
|
||||
HashTable_Unlock(g_ServerHandles);
|
||||
- return FreeRDP_WTSVirtualChannelOpen((HANDLE)vcm, SessionId, pVirtualName);
|
||||
+ return FreeRDP_WTSVirtualChannelOpenStatic((HANDLE)vcm, SessionId, pVirtualName, flags);
|
||||
}
|
||||
|
||||
freerdp_peer* client = vcm->client;
|
||||
@@ -1521,7 +1532,8 @@ HANDLE WINAPI FreeRDP_WTSVirtualChannelOpenEx(DWORD SessionId, LPSTR pVirtualNam
|
||||
|
||||
const UINT32 VCChunkSize =
|
||||
freerdp_settings_get_uint32(client->context->settings, FreeRDP_VCChunkSize);
|
||||
- channel = channel_new(vcm, client, 0, 0, RDP_PEER_CHANNEL_TYPE_DVC, VCChunkSize, pVirtualName);
|
||||
+ channel =
|
||||
+ channel_new(vcm, client, 0, 0, RDP_PEER_CHANNEL_TYPE_DVC, VCChunkSize, pVirtualName, flags);
|
||||
|
||||
if (!channel)
|
||||
{
|
||||
diff --git a/libfreerdp/utils/CMakeLists.txt b/libfreerdp/utils/CMakeLists.txt
|
||||
index 6f821a6..30b7ca9 100644
|
||||
--- a/libfreerdp/utils/CMakeLists.txt
|
||||
+++ b/libfreerdp/utils/CMakeLists.txt
|
||||
@@ -19,6 +19,7 @@ set(MODULE_NAME "freerdp-utils")
|
||||
set(MODULE_PREFIX "FREERDP_UTILS")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
+ channel_pdu_tracker.c
|
||||
encoded_types.c
|
||||
helpers.c
|
||||
passphrase.c
|
||||
diff --git a/libfreerdp/utils/channel_pdu_tracker.c b/libfreerdp/utils/channel_pdu_tracker.c
|
||||
new file mode 100644
|
||||
index 0000000..18e9aab
|
||||
--- /dev/null
|
||||
+++ b/libfreerdp/utils/channel_pdu_tracker.c
|
||||
@@ -0,0 +1,119 @@
|
||||
+/**
|
||||
+ * FreeRDP: A Remote Desktop Protocol Implementation
|
||||
+ * static channel utility functions
|
||||
+ *
|
||||
+ * Copyright 2026 David Fort <contact@hardening-consulting.com>
|
||||
+ *
|
||||
+ * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
+ * you may not use this file except in compliance with the License.
|
||||
+ * You may obtain a copy of the License at
|
||||
+ *
|
||||
+ * http://www.apache.org/licenses/LICENSE-2.0
|
||||
+ *
|
||||
+ * Unless required by applicable law or agreed to in writing, software
|
||||
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
+ * See the License for the specific language governing permissions and
|
||||
+ * limitations under the License.
|
||||
+ */
|
||||
+
|
||||
+#include <freerdp/utils/channel_pdu_tracker.h>
|
||||
+
|
||||
+#include <winpr/wtsapi.h>
|
||||
+
|
||||
+struct ChannelPduTracker
|
||||
+{
|
||||
+ HANDLE vc;
|
||||
+ wStream* currentPacket;
|
||||
+ BYTE buffer[CHANNEL_PDU_LENGTH];
|
||||
+ BYTE* writePtr;
|
||||
+};
|
||||
+
|
||||
+#define TAG "ChannelPduTracker"
|
||||
+
|
||||
+wStream* ChannelPduTracker_poll(ChannelPduTracker* tracker, BOOL* ok)
|
||||
+{
|
||||
+ WINPR_ASSERT(tracker);
|
||||
+ WINPR_ASSERT(ok);
|
||||
+
|
||||
+ ULONG sz = 0;
|
||||
+ *ok = FALSE;
|
||||
+ const ULONG readSz =
|
||||
+ WINPR_ASSERTING_INT_CAST(ULONG, CHANNEL_PDU_LENGTH - (tracker->writePtr - tracker->buffer));
|
||||
+ if (!WTSVirtualChannelRead(tracker->vc, INFINITE, (char*)tracker->writePtr, readSz, &sz))
|
||||
+ return nullptr;
|
||||
+
|
||||
+ tracker->writePtr += sz;
|
||||
+ const size_t recvSz = (tracker->writePtr - tracker->buffer);
|
||||
+ if (recvSz < sizeof(CHANNEL_PDU_HEADER))
|
||||
+ {
|
||||
+ *ok = TRUE;
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+
|
||||
+ CHANNEL_PDU_HEADER* header = (CHANNEL_PDU_HEADER*)tracker->buffer;
|
||||
+ if (header->length > CHANNEL_CHUNK_LENGTH)
|
||||
+ {
|
||||
+ WLog_ERR(TAG, "chunk size %" PRIu32 " is too big", header->length);
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+
|
||||
+ if (recvSz - sizeof(CHANNEL_PDU_HEADER) < header->length)
|
||||
+ {
|
||||
+ WLog_ERR(TAG, "chunk size %" PRIu32 " is too big", header->length);
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+
|
||||
+ tracker->writePtr = tracker->buffer;
|
||||
+ if (header->flags & CHANNEL_FLAG_FIRST)
|
||||
+ Stream_ResetPosition(tracker->currentPacket);
|
||||
+
|
||||
+ if (!Stream_EnsureRemainingCapacity(tracker->currentPacket, header->length))
|
||||
+ return nullptr;
|
||||
+
|
||||
+ Stream_Write(tracker->currentPacket, header + 1, header->length);
|
||||
+
|
||||
+ if (header->flags & CHANNEL_FLAG_LAST)
|
||||
+ {
|
||||
+ Stream_SealLength(tracker->currentPacket);
|
||||
+ Stream_ResetPosition(tracker->currentPacket);
|
||||
+ wStream* ret = tracker->currentPacket;
|
||||
+
|
||||
+ tracker->currentPacket = Stream_New(nullptr, 4096);
|
||||
+ if (!tracker->currentPacket)
|
||||
+ {
|
||||
+ Stream_Release(ret);
|
||||
+ WLog_ERR(TAG, "error allocating new currentPacket");
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ *ok = TRUE;
|
||||
+ return nullptr;
|
||||
+}
|
||||
+
|
||||
+void ChannelPduTracker_free(ChannelPduTracker* tracker)
|
||||
+{
|
||||
+ if (!tracker)
|
||||
+ return;
|
||||
+
|
||||
+ Stream_Release(tracker->currentPacket);
|
||||
+ free(tracker);
|
||||
+}
|
||||
+
|
||||
+ChannelPduTracker* ChannelPduTracker_new(HANDLE vc)
|
||||
+{
|
||||
+ ChannelPduTracker* ret = calloc(1, sizeof(*ret));
|
||||
+ if (!ret)
|
||||
+ return nullptr;
|
||||
+
|
||||
+ ret->currentPacket = Stream_New(nullptr, 4096);
|
||||
+ if (!ret->currentPacket)
|
||||
+ {
|
||||
+ free(ret);
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+ ret->vc = vc;
|
||||
+ ret->writePtr = ret->buffer;
|
||||
+ return ret;
|
||||
+}
|
||||
--
|
||||
2.54.0
|
||||
|
||||
26
channels-rdpdr-fix-off-by-one-scan-length-in-rdpdr_r.patch
Normal file
26
channels-rdpdr-fix-off-by-one-scan-length-in-rdpdr_r.patch
Normal file
@ -0,0 +1,26 @@
|
||||
From 8223c5a0abdf4d5eae40c66e1329d62766578ce1 Mon Sep 17 00:00:00 2001
|
||||
From: Sayed Kaif <metsw24@gmail.com>
|
||||
Date: Wed, 24 Jun 2026 17:44:53 +0530
|
||||
Subject: [PATCH] [channels,rdpdr] fix off-by-one scan length in
|
||||
rdpdr_read_ustring
|
||||
|
||||
---
|
||||
channels/rdpdr/server/rdpdr_main.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/channels/rdpdr/server/rdpdr_main.c b/channels/rdpdr/server/rdpdr_main.c
|
||||
index bbb3b7e..62e5b90 100644
|
||||
--- a/channels/rdpdr/server/rdpdr_main.c
|
||||
+++ b/channels/rdpdr/server/rdpdr_main.c
|
||||
@@ -222,7 +222,7 @@ static UINT32 g_ClientId = 0;
|
||||
|
||||
static const WCHAR* rdpdr_read_ustring(wLog* log, wStream* s, size_t bytelen)
|
||||
{
|
||||
- const size_t charlen = (bytelen + 1) / sizeof(WCHAR);
|
||||
+ const size_t charlen = bytelen / sizeof(WCHAR);
|
||||
const WCHAR* str = Stream_ConstPointer(s);
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(log, s, bytelen))
|
||||
return nullptr;
|
||||
--
|
||||
2.54.0
|
||||
|
||||
24
freerdp.spec
24
freerdp.spec
@ -30,7 +30,7 @@
|
||||
Name: freerdp
|
||||
Epoch: 2
|
||||
Version: 3.27.1
|
||||
Release: 1%{?dist}
|
||||
Release: 2%{?dist}
|
||||
Summary: Free implementation of the Remote Desktop Protocol (RDP)
|
||||
|
||||
# The effective license is Apache-2.0 but:
|
||||
@ -47,6 +47,24 @@ URL: http://www.freerdp.com/
|
||||
Source0: FreeRDP-%{version}-repack.tar.gz
|
||||
Source1: freerdp_download_and_repack.sh
|
||||
|
||||
# Add RDP Smart Card Redirection Support
|
||||
# https://github.com/FreeRDP/FreeRDP/pull/12928
|
||||
# https://github.com/FreeRDP/FreeRDP/commit/2a6d532a1d633145ae48e55ae471f2f2d8e56157
|
||||
# https://github.com/FreeRDP/FreeRDP/commit/5cdd46594ec792715a00ecb935dcac7aadde0fc3
|
||||
# https://github.com/FreeRDP/FreeRDP/commit/2e1113794d125269f30fc7acf4d0f3d898d74d52
|
||||
# https://github.com/FreeRDP/FreeRDP/commit/8223c5a0abdf4d5eae40c66e1329d62766578ce1
|
||||
# https://github.com/FreeRDP/FreeRDP/commit/1a15128e93705d5680c84b9e6368245d247fe893
|
||||
# https://github.com/FreeRDP/FreeRDP/commit/a41855e88ea37b92a4d506430bce4b09e76f7d76
|
||||
# https://github.com/FreeRDP/FreeRDP/pull/12756
|
||||
Patch: channels-handle-CHANNEL_OPTION_SHOW_PROTOCOL-server-.patch
|
||||
Patch: utils-smartcard-validate-cbAtr-against-rgbAtr-size-o.patch
|
||||
Patch: utils-scard-fix-endianess-issue.patch
|
||||
Patch: utils-test-replace-NULL.patch
|
||||
Patch: channels-rdpdr-fix-off-by-one-scan-length-in-rdpdr_r.patch
|
||||
Patch: utils-pdu-tracker-clean-up-warnings-fix-poll-return.patch
|
||||
Patch: utils-pdu-tracker-increase-capacity-for-fragmented-P.patch
|
||||
Patch: support-rdpdr-device-control-responses.patch
|
||||
|
||||
BuildRequires: gcc
|
||||
BuildRequires: gcc-c++
|
||||
BuildRequires: alsa-lib-devel
|
||||
@ -378,6 +396,10 @@ find %{buildroot} -name "*.a" -delete
|
||||
%{_libdir}/pkgconfig/winpr-tools3.pc
|
||||
|
||||
%changelog
|
||||
* Wed Jul 01 2026 Ondrej Holy <oholy@redhat.com> - 2:3.27.1-2
|
||||
- Add RDP Smart Card Redirection Support
|
||||
Resolves: RHEL-171999
|
||||
|
||||
* Wed Jul 01 2026 Ondrej Holy <oholy@redhat.com> - 2:3.27.1-1
|
||||
- Update to 3.27.1
|
||||
Resolves: RHEL-186097, RHEL-186938, RHEL-186960, RHEL-186965, RHEL-186980
|
||||
|
||||
14826
support-rdpdr-device-control-responses.patch
Normal file
14826
support-rdpdr-device-control-responses.patch
Normal file
File diff suppressed because it is too large
Load Diff
156
utils-pdu-tracker-clean-up-warnings-fix-poll-return.patch
Normal file
156
utils-pdu-tracker-clean-up-warnings-fix-poll-return.patch
Normal file
@ -0,0 +1,156 @@
|
||||
From 1a15128e93705d5680c84b9e6368245d247fe893 Mon Sep 17 00:00:00 2001
|
||||
From: Armin Novak <armin.novak@thincast.com>
|
||||
Date: Mon, 29 Jun 2026 10:23:37 +0200
|
||||
Subject: [PATCH] [utils,pdu-tracker] clean up warnings, fix poll return
|
||||
|
||||
---
|
||||
libfreerdp/utils/channel_pdu_tracker.c | 87 +++++++++++++++-----------
|
||||
1 file changed, 52 insertions(+), 35 deletions(-)
|
||||
|
||||
diff --git a/libfreerdp/utils/channel_pdu_tracker.c b/libfreerdp/utils/channel_pdu_tracker.c
|
||||
index 18e9aab..2b46a98 100644
|
||||
--- a/libfreerdp/utils/channel_pdu_tracker.c
|
||||
+++ b/libfreerdp/utils/channel_pdu_tracker.c
|
||||
@@ -21,16 +21,18 @@
|
||||
|
||||
#include <winpr/wtsapi.h>
|
||||
|
||||
+#include <freerdp/log.h>
|
||||
+#define TTAG FREERDP_TAG("utils.ChannelPduTracker")
|
||||
+
|
||||
struct ChannelPduTracker
|
||||
{
|
||||
HANDLE vc;
|
||||
wStream* currentPacket;
|
||||
- BYTE buffer[CHANNEL_PDU_LENGTH];
|
||||
- BYTE* writePtr;
|
||||
+ char buffer[CHANNEL_PDU_LENGTH];
|
||||
+ size_t offset;
|
||||
+ wLog* log;
|
||||
};
|
||||
|
||||
-#define TAG "ChannelPduTracker"
|
||||
-
|
||||
wStream* ChannelPduTracker_poll(ChannelPduTracker* tracker, BOOL* ok)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
@@ -38,58 +40,70 @@ wStream* ChannelPduTracker_poll(ChannelPduTracker* tracker, BOOL* ok)
|
||||
|
||||
ULONG sz = 0;
|
||||
*ok = FALSE;
|
||||
- const ULONG readSz =
|
||||
- WINPR_ASSERTING_INT_CAST(ULONG, CHANNEL_PDU_LENGTH - (tracker->writePtr - tracker->buffer));
|
||||
- if (!WTSVirtualChannelRead(tracker->vc, INFINITE, (char*)tracker->writePtr, readSz, &sz))
|
||||
+
|
||||
+ WINPR_ASSERT(tracker->offset <= CHANNEL_PDU_LENGTH);
|
||||
+ const ULONG readSz = WINPR_ASSERTING_INT_CAST(ULONG, CHANNEL_PDU_LENGTH - tracker->offset);
|
||||
+ if (!WTSVirtualChannelRead(tracker->vc, INFINITE, &tracker->buffer[tracker->offset], readSz,
|
||||
+ &sz))
|
||||
return nullptr;
|
||||
|
||||
- tracker->writePtr += sz;
|
||||
- const size_t recvSz = (tracker->writePtr - tracker->buffer);
|
||||
+ tracker->offset += sz;
|
||||
+ WINPR_ASSERT(tracker->offset <= CHANNEL_PDU_LENGTH);
|
||||
+
|
||||
+ const size_t recvSz = tracker->offset;
|
||||
if (recvSz < sizeof(CHANNEL_PDU_HEADER))
|
||||
{
|
||||
*ok = TRUE;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
- CHANNEL_PDU_HEADER* header = (CHANNEL_PDU_HEADER*)tracker->buffer;
|
||||
+ const CHANNEL_PDU_HEADER* header = (const CHANNEL_PDU_HEADER*)tracker->buffer;
|
||||
if (header->length > CHANNEL_CHUNK_LENGTH)
|
||||
{
|
||||
- WLog_ERR(TAG, "chunk size %" PRIu32 " is too big", header->length);
|
||||
+ WLog_Print(tracker->log, WLOG_ERROR, "chunk size %" PRIu32 " is too big", header->length);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
- if (recvSz - sizeof(CHANNEL_PDU_HEADER) < header->length)
|
||||
+ const size_t actual = recvSz - sizeof(CHANNEL_PDU_HEADER);
|
||||
+ if (actual != header->length)
|
||||
{
|
||||
- WLog_ERR(TAG, "chunk size %" PRIu32 " is too big", header->length);
|
||||
+ WLog_Print(tracker->log, WLOG_ERROR,
|
||||
+ "Expected chunk size %" PRIu32 " does not match received data size %" PRIuz,
|
||||
+ header->length, actual);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
- tracker->writePtr = tracker->buffer;
|
||||
- if (header->flags & CHANNEL_FLAG_FIRST)
|
||||
+ tracker->offset = 0;
|
||||
+ if ((header->flags & CHANNEL_FLAG_FIRST) != 0)
|
||||
Stream_ResetPosition(tracker->currentPacket);
|
||||
|
||||
- if (!Stream_EnsureRemainingCapacity(tracker->currentPacket, header->length))
|
||||
+ if (!Stream_CheckAndLogRequiredCapacityWLog(tracker->log, tracker->currentPacket,
|
||||
+ header->length))
|
||||
return nullptr;
|
||||
|
||||
- Stream_Write(tracker->currentPacket, header + 1, header->length);
|
||||
+ Stream_Write(tracker->currentPacket, &tracker->buffer[sizeof(CHANNEL_PDU_HEADER)],
|
||||
+ header->length);
|
||||
|
||||
- if (header->flags & CHANNEL_FLAG_LAST)
|
||||
+ if ((header->flags & CHANNEL_FLAG_LAST) == 0)
|
||||
{
|
||||
- Stream_SealLength(tracker->currentPacket);
|
||||
- Stream_ResetPosition(tracker->currentPacket);
|
||||
- wStream* ret = tracker->currentPacket;
|
||||
-
|
||||
- tracker->currentPacket = Stream_New(nullptr, 4096);
|
||||
- if (!tracker->currentPacket)
|
||||
- {
|
||||
- Stream_Release(ret);
|
||||
- WLog_ERR(TAG, "error allocating new currentPacket");
|
||||
- return nullptr;
|
||||
- }
|
||||
+ *ok = TRUE;
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+
|
||||
+ Stream_SealLength(tracker->currentPacket);
|
||||
+ Stream_ResetPosition(tracker->currentPacket);
|
||||
+ wStream* ret = tracker->currentPacket;
|
||||
+
|
||||
+ tracker->currentPacket = Stream_New(nullptr, 4096);
|
||||
+ if (!tracker->currentPacket)
|
||||
+ {
|
||||
+ Stream_Release(ret);
|
||||
+ WLog_Print(tracker->log, WLOG_ERROR, "error allocating new currentPacket");
|
||||
+ return nullptr;
|
||||
}
|
||||
|
||||
*ok = TRUE;
|
||||
- return nullptr;
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
void ChannelPduTracker_free(ChannelPduTracker* tracker)
|
||||
@@ -109,11 +123,14 @@ ChannelPduTracker* ChannelPduTracker_new(HANDLE vc)
|
||||
|
||||
ret->currentPacket = Stream_New(nullptr, 4096);
|
||||
if (!ret->currentPacket)
|
||||
- {
|
||||
- free(ret);
|
||||
- return nullptr;
|
||||
- }
|
||||
+ goto fail;
|
||||
+
|
||||
ret->vc = vc;
|
||||
- ret->writePtr = ret->buffer;
|
||||
+ ret->log = WLog_Get(TTAG);
|
||||
+ if (!ret->log)
|
||||
+ goto fail;
|
||||
return ret;
|
||||
+fail:
|
||||
+ ChannelPduTracker_free(ret);
|
||||
+ return nullptr;
|
||||
}
|
||||
--
|
||||
2.54.0
|
||||
|
||||
26
utils-pdu-tracker-increase-capacity-for-fragmented-P.patch
Normal file
26
utils-pdu-tracker-increase-capacity-for-fragmented-P.patch
Normal file
@ -0,0 +1,26 @@
|
||||
From a41855e88ea37b92a4d506430bce4b09e76f7d76 Mon Sep 17 00:00:00 2001
|
||||
From: Armin Novak <armin.novak@thincast.com>
|
||||
Date: Mon, 29 Jun 2026 15:50:18 +0200
|
||||
Subject: [PATCH] [utils,pdu-tracker] increase capacity for fragmented PDU
|
||||
|
||||
---
|
||||
libfreerdp/utils/channel_pdu_tracker.c | 3 +--
|
||||
1 file changed, 1 insertion(+), 2 deletions(-)
|
||||
|
||||
diff --git a/libfreerdp/utils/channel_pdu_tracker.c b/libfreerdp/utils/channel_pdu_tracker.c
|
||||
index 2b46a98..63c112d 100644
|
||||
--- a/libfreerdp/utils/channel_pdu_tracker.c
|
||||
+++ b/libfreerdp/utils/channel_pdu_tracker.c
|
||||
@@ -77,8 +77,7 @@ wStream* ChannelPduTracker_poll(ChannelPduTracker* tracker, BOOL* ok)
|
||||
if ((header->flags & CHANNEL_FLAG_FIRST) != 0)
|
||||
Stream_ResetPosition(tracker->currentPacket);
|
||||
|
||||
- if (!Stream_CheckAndLogRequiredCapacityWLog(tracker->log, tracker->currentPacket,
|
||||
- header->length))
|
||||
+ if (!Stream_EnsureRemainingCapacity(tracker->currentPacket, header->length))
|
||||
return nullptr;
|
||||
|
||||
Stream_Write(tracker->currentPacket, &tracker->buffer[sizeof(CHANNEL_PDU_HEADER)],
|
||||
--
|
||||
2.54.0
|
||||
|
||||
36
utils-scard-fix-endianess-issue.patch
Normal file
36
utils-scard-fix-endianess-issue.patch
Normal file
@ -0,0 +1,36 @@
|
||||
From 2a6d532a1d633145ae48e55ae471f2f2d8e56157 Mon Sep 17 00:00:00 2001
|
||||
From: Armin Novak <armin.novak@thincast.com>
|
||||
Date: Fri, 19 Jun 2026 20:15:15 +0200
|
||||
Subject: [PATCH] [utils,scard] fix endianess issue
|
||||
|
||||
---
|
||||
libfreerdp/utils/smartcard_pack.c | 9 +++++----
|
||||
1 file changed, 5 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/libfreerdp/utils/smartcard_pack.c b/libfreerdp/utils/smartcard_pack.c
|
||||
index bb977c4..83d1fbd 100644
|
||||
--- a/libfreerdp/utils/smartcard_pack.c
|
||||
+++ b/libfreerdp/utils/smartcard_pack.c
|
||||
@@ -319,14 +319,15 @@ static LONG smartcard_ndr_read_atrmask(wLog* log, wStream* s, LocateCards_ATRMas
|
||||
/* [MS-RDPESC] 2.2.1.5: cbAtr is range(0..36), the number of valid bytes in the fixed
|
||||
* rgbAtr/rgbMask arrays. A larger value walks past them when the mask is compared byte
|
||||
* by byte, so reject it here rather than trusting the wire value. */
|
||||
- const LocateCards_ATRMask* masks = *data;
|
||||
+ LocateCards_ATRMask* masks = *data;
|
||||
for (size_t x = 0; x < min; x++)
|
||||
{
|
||||
- if (masks[x].cbAtr > ARRAYSIZE(masks[x].rgbAtr))
|
||||
+ const UINT32 val = winpr_Data_Get_UINT32(&masks[x].cbAtr);
|
||||
+ if (val > ARRAYSIZE(masks[x].rgbAtr))
|
||||
{
|
||||
WLog_Print(log, WLOG_ERROR,
|
||||
- "LocateCards_ATRMask[%" PRIuz "]::cbAtr %" PRIu32 " exceeds %" PRIuz, x,
|
||||
- masks[x].cbAtr, (size_t)ARRAYSIZE(masks[x].rgbAtr));
|
||||
+ "LocateCards_ATRMask[%" PRIuz "]::cbAtr %" PRIu32 " exceeds %" PRIuz, x, val,
|
||||
+ (size_t)ARRAYSIZE(masks[x].rgbAtr));
|
||||
free(*data);
|
||||
*data = nullptr;
|
||||
return STATUS_DATA_ERROR;
|
||||
--
|
||||
2.54.0
|
||||
|
||||
189
utils-smartcard-validate-cbAtr-against-rgbAtr-size-o.patch
Normal file
189
utils-smartcard-validate-cbAtr-against-rgbAtr-size-o.patch
Normal file
@ -0,0 +1,189 @@
|
||||
From ececc5cbd0646ef012ca178322e6ef922a11f0c1 Mon Sep 17 00:00:00 2001
|
||||
From: metsw24-max <metsw24@gmail.com>
|
||||
Date: Fri, 19 Jun 2026 12:32:01 +0530
|
||||
Subject: [PATCH] [utils,smartcard] validate cbAtr against rgbAtr size on
|
||||
unpack
|
||||
|
||||
---
|
||||
libfreerdp/utils/smartcard_pack.c | 36 ++++++++-
|
||||
libfreerdp/utils/test/CMakeLists.txt | 2 +-
|
||||
libfreerdp/utils/test/TestSmartcardPack.c | 93 +++++++++++++++++++++++
|
||||
3 files changed, 129 insertions(+), 2 deletions(-)
|
||||
create mode 100644 libfreerdp/utils/test/TestSmartcardPack.c
|
||||
|
||||
diff --git a/libfreerdp/utils/smartcard_pack.c b/libfreerdp/utils/smartcard_pack.c
|
||||
index d8f9cab..bb977c4 100644
|
||||
--- a/libfreerdp/utils/smartcard_pack.c
|
||||
+++ b/libfreerdp/utils/smartcard_pack.c
|
||||
@@ -312,7 +312,27 @@ static LONG smartcard_ndr_read_atrmask(wLog* log, wStream* s, LocateCards_ATRMas
|
||||
BYTE** ppv;
|
||||
} u;
|
||||
u.ppc = data;
|
||||
- return smartcard_ndr_read(log, s, u.ppv, min, sizeof(LocateCards_ATRMask), type);
|
||||
+ const LONG status = smartcard_ndr_read(log, s, u.ppv, min, sizeof(LocateCards_ATRMask), type);
|
||||
+ if (status != SCARD_S_SUCCESS)
|
||||
+ return status;
|
||||
+
|
||||
+ /* [MS-RDPESC] 2.2.1.5: cbAtr is range(0..36), the number of valid bytes in the fixed
|
||||
+ * rgbAtr/rgbMask arrays. A larger value walks past them when the mask is compared byte
|
||||
+ * by byte, so reject it here rather than trusting the wire value. */
|
||||
+ const LocateCards_ATRMask* masks = *data;
|
||||
+ for (size_t x = 0; x < min; x++)
|
||||
+ {
|
||||
+ if (masks[x].cbAtr > ARRAYSIZE(masks[x].rgbAtr))
|
||||
+ {
|
||||
+ WLog_Print(log, WLOG_ERROR,
|
||||
+ "LocateCards_ATRMask[%" PRIuz "]::cbAtr %" PRIu32 " exceeds %" PRIuz, x,
|
||||
+ masks[x].cbAtr, (size_t)ARRAYSIZE(masks[x].rgbAtr));
|
||||
+ free(*data);
|
||||
+ *data = nullptr;
|
||||
+ return STATUS_DATA_ERROR;
|
||||
+ }
|
||||
+ }
|
||||
+ return SCARD_S_SUCCESS;
|
||||
}
|
||||
|
||||
static LONG smartcard_ndr_read_fixed_string_a(wLog* log, wStream* s, CHAR** data, size_t min,
|
||||
@@ -2454,6 +2474,13 @@ static LONG smartcard_unpack_reader_state_a(wLog* log, wStream* s, LPSCARD_READE
|
||||
Stream_Read_UINT32(s, readerState->dwCurrentState); /* dwCurrentState (4 bytes) */
|
||||
Stream_Read_UINT32(s, readerState->dwEventState); /* dwEventState (4 bytes) */
|
||||
Stream_Read_UINT32(s, readerState->cbAtr); /* cbAtr (4 bytes) */
|
||||
+ if (readerState->cbAtr > ARRAYSIZE(readerState->rgbAtr))
|
||||
+ {
|
||||
+ WLog_Print(log, WLOG_ERROR,
|
||||
+ "SCARD_READERSTATEA[%" PRIu32 "]::cbAtr %" PRIu32 " exceeds %" PRIuz, index,
|
||||
+ readerState->cbAtr, (size_t)ARRAYSIZE(readerState->rgbAtr));
|
||||
+ goto fail;
|
||||
+ }
|
||||
Stream_Read(s, readerState->rgbAtr, 36); /* rgbAtr [0..36] (36 bytes) */
|
||||
}
|
||||
|
||||
@@ -2528,6 +2555,13 @@ static LONG smartcard_unpack_reader_state_w(wLog* log, wStream* s, LPSCARD_READE
|
||||
Stream_Read_UINT32(s, readerState->dwCurrentState); /* dwCurrentState (4 bytes) */
|
||||
Stream_Read_UINT32(s, readerState->dwEventState); /* dwEventState (4 bytes) */
|
||||
Stream_Read_UINT32(s, readerState->cbAtr); /* cbAtr (4 bytes) */
|
||||
+ if (readerState->cbAtr > ARRAYSIZE(readerState->rgbAtr))
|
||||
+ {
|
||||
+ WLog_Print(log, WLOG_ERROR,
|
||||
+ "SCARD_READERSTATEW[%" PRIu32 "]::cbAtr %" PRIu32 " exceeds %" PRIuz, index,
|
||||
+ readerState->cbAtr, (size_t)ARRAYSIZE(readerState->rgbAtr));
|
||||
+ goto fail;
|
||||
+ }
|
||||
Stream_Read(s, readerState->rgbAtr, 36); /* rgbAtr [0..36] (36 bytes) */
|
||||
}
|
||||
|
||||
diff --git a/libfreerdp/utils/test/CMakeLists.txt b/libfreerdp/utils/test/CMakeLists.txt
|
||||
index b90c5f4..0c2bf84 100644
|
||||
--- a/libfreerdp/utils/test/CMakeLists.txt
|
||||
+++ b/libfreerdp/utils/test/CMakeLists.txt
|
||||
@@ -5,7 +5,7 @@ disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
|
||||
|
||||
-set(${MODULE_PREFIX}_TESTS TestRingBuffer.c TestPodArrays.c TestEncodedTypes.c)
|
||||
+set(${MODULE_PREFIX}_TESTS TestRingBuffer.c TestPodArrays.c TestEncodedTypes.c TestSmartcardPack.c)
|
||||
|
||||
create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} ${${MODULE_PREFIX}_TESTS})
|
||||
|
||||
diff --git a/libfreerdp/utils/test/TestSmartcardPack.c b/libfreerdp/utils/test/TestSmartcardPack.c
|
||||
new file mode 100644
|
||||
index 0000000..8c04600
|
||||
--- /dev/null
|
||||
+++ b/libfreerdp/utils/test/TestSmartcardPack.c
|
||||
@@ -0,0 +1,93 @@
|
||||
+/**
|
||||
+ * FreeRDP: A Remote Desktop Protocol Implementation
|
||||
+ *
|
||||
+ * 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 <stdio.h>
|
||||
+#include <string.h>
|
||||
+
|
||||
+#include <winpr/stream.h>
|
||||
+#include <freerdp/channels/scard.h>
|
||||
+#include <freerdp/utils/smartcard_pack.h>
|
||||
+
|
||||
+/* Build a LocateCardsByATRA call (cbContext=0, cAtrs=1, cReaders=0) holding a single
|
||||
+ * LocateCards_ATRMask whose cbAtr is set to the requested value. */
|
||||
+static wStream* build_locate_cards_by_atr_a(UINT32 cbAtr)
|
||||
+{
|
||||
+ wStream* s = Stream_New(NULL, 24 + 8 + sizeof(LocateCards_ATRMask));
|
||||
+ if (!s)
|
||||
+ return NULL;
|
||||
+
|
||||
+ Stream_Write_UINT32(s, 0); /* cbContext = 0 */
|
||||
+ Stream_Write_UINT32(s, 0); /* pbContextNdrPtr = 0 */
|
||||
+ Stream_Write_UINT32(s, 1); /* cAtrs = 1 */
|
||||
+ Stream_Write_UINT32(s, 0x00020000); /* rgAtrMasksNdrPtr */
|
||||
+ Stream_Write_UINT32(s, 0); /* cReaders = 0 */
|
||||
+ Stream_Write_UINT32(s, 0); /* rgReaderStatesNdrPtr = 0 */
|
||||
+ Stream_Write_UINT32(s, 1); /* conformant count = cAtrs */
|
||||
+ Stream_Write_UINT32(s, cbAtr); /* LocateCards_ATRMask::cbAtr */
|
||||
+ Stream_Zero(s, 36); /* rgbAtr[36] */
|
||||
+ Stream_Zero(s, 36); /* rgbMask[36] */
|
||||
+
|
||||
+ Stream_SealLength(s);
|
||||
+ if (!Stream_SetPosition(s, 0))
|
||||
+ {
|
||||
+ Stream_Free(s, TRUE);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ return s;
|
||||
+}
|
||||
+
|
||||
+static BOOL run_case(UINT32 cbAtr, BOOL expectAccept)
|
||||
+{
|
||||
+ wStream* s = build_locate_cards_by_atr_a(cbAtr);
|
||||
+ if (!s)
|
||||
+ return FALSE;
|
||||
+
|
||||
+ LocateCardsByATRA_Call call = { 0 };
|
||||
+ const LONG status = smartcard_unpack_locate_cards_by_atr_a_call(s, &call);
|
||||
+ Stream_Free(s, TRUE);
|
||||
+
|
||||
+ const BOOL accepted = (status == SCARD_S_SUCCESS) ? TRUE : FALSE;
|
||||
+ if (accepted != expectAccept)
|
||||
+ {
|
||||
+ printf("cbAtr=%" PRIu32 ": expected %s, unpack returned 0x%08" PRIX32 "\n", cbAtr,
|
||||
+ expectAccept ? "accept" : "reject", (UINT32)status);
|
||||
+ free(call.rgAtrMasks);
|
||||
+ return FALSE;
|
||||
+ }
|
||||
+
|
||||
+ free(call.rgAtrMasks);
|
||||
+ return TRUE;
|
||||
+}
|
||||
+
|
||||
+int TestSmartcardPack(int argc, char* argv[])
|
||||
+{
|
||||
+ WINPR_UNUSED(argc);
|
||||
+ WINPR_UNUSED(argv);
|
||||
+
|
||||
+ /* Valid: cbAtr within the fixed 36-byte rgbAtr/rgbMask arrays. */
|
||||
+ if (!run_case(0, TRUE))
|
||||
+ return -1;
|
||||
+ if (!run_case(36, TRUE))
|
||||
+ return -1;
|
||||
+
|
||||
+ /* Out of range: cbAtr larger than the buffers would later overrun them. */
|
||||
+ if (!run_case(37, FALSE))
|
||||
+ return -1;
|
||||
+ if (!run_case(0xFFFFFFFF, FALSE))
|
||||
+ return -1;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
--
|
||||
2.54.0
|
||||
|
||||
46
utils-test-replace-NULL.patch
Normal file
46
utils-test-replace-NULL.patch
Normal file
@ -0,0 +1,46 @@
|
||||
From 5cdd46594ec792715a00ecb935dcac7aadde0fc3 Mon Sep 17 00:00:00 2001
|
||||
From: Armin Novak <armin.novak@thincast.com>
|
||||
Date: Fri, 19 Jun 2026 20:58:44 +0200
|
||||
Subject: [PATCH] [utils,test] replace NULL
|
||||
|
||||
---
|
||||
libfreerdp/utils/test/TestSmartcardPack.c | 8 ++++----
|
||||
1 file changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/libfreerdp/utils/test/TestSmartcardPack.c b/libfreerdp/utils/test/TestSmartcardPack.c
|
||||
index 8c04600..4c465f1 100644
|
||||
--- a/libfreerdp/utils/test/TestSmartcardPack.c
|
||||
+++ b/libfreerdp/utils/test/TestSmartcardPack.c
|
||||
@@ -25,9 +25,9 @@
|
||||
* LocateCards_ATRMask whose cbAtr is set to the requested value. */
|
||||
static wStream* build_locate_cards_by_atr_a(UINT32 cbAtr)
|
||||
{
|
||||
- wStream* s = Stream_New(NULL, 24 + 8 + sizeof(LocateCards_ATRMask));
|
||||
+ wStream* s = Stream_New(nullptr, 24 + 8 + sizeof(LocateCards_ATRMask));
|
||||
if (!s)
|
||||
- return NULL;
|
||||
+ return nullptr;
|
||||
|
||||
Stream_Write_UINT32(s, 0); /* cbContext = 0 */
|
||||
Stream_Write_UINT32(s, 0); /* pbContextNdrPtr = 0 */
|
||||
@@ -44,7 +44,7 @@ static wStream* build_locate_cards_by_atr_a(UINT32 cbAtr)
|
||||
if (!Stream_SetPosition(s, 0))
|
||||
{
|
||||
Stream_Free(s, TRUE);
|
||||
- return NULL;
|
||||
+ return nullptr;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -59,7 +59,7 @@ static BOOL run_case(UINT32 cbAtr, BOOL expectAccept)
|
||||
const LONG status = smartcard_unpack_locate_cards_by_atr_a_call(s, &call);
|
||||
Stream_Free(s, TRUE);
|
||||
|
||||
- const BOOL accepted = (status == SCARD_S_SUCCESS) ? TRUE : FALSE;
|
||||
+ const BOOL accepted = status == SCARD_S_SUCCESS;
|
||||
if (accepted != expectAccept)
|
||||
{
|
||||
printf("cbAtr=%" PRIu32 ": expected %s, unpack returned 0x%08" PRIX32 "\n", cbAtr,
|
||||
--
|
||||
2.54.0
|
||||
|
||||
Loading…
Reference in New Issue
Block a user