19#include <freerdp/config.h>
25#include <winpr/assert.h>
26#include <winpr/stream.h>
27#include <winpr/wlog.h>
28#include <winpr/print.h>
29#include <winpr/thread.h>
30#include <winpr/synch.h>
32#include "rdpewa_main.h"
33#include "rdpewa_cbor.h"
34#include "rdpewa_fido.h"
35#include <rdpewa-common.h>
37#include <freerdp/client/channels.h>
38#include <freerdp/channels/log.h>
39#include <freerdp/channels/rdpewa.h>
41#define TAG CHANNELS_TAG("rdpewa.client")
46 rdpContext* rdpContext;
48 IWTSVirtualChannel* channel;
51static DWORD WINAPI rdpewa_async_webauthn_thread(LPVOID arg)
53 RDPEWA_ASYNC_WORK* work = (RDPEWA_ASYNC_WORK*)arg;
56 switch (work->request.command)
58 case CTAPCBOR_RPC_COMMAND_WEB_AUTHN:
59 s = rdpewa_fido_webauthn(work->rdpContext, &work->request);
67 const size_t len = Stream_GetPosition(s);
68 WLog_DBG(TAG,
"Async: sending %" PRIuz
" byte response for command %" PRIu32, len,
69 work->request.command);
70 winpr_HexDump(TAG, WLOG_TRACE, Stream_Buffer(s), len > 512 ? 512 : len);
72 if (!freerdp_shall_disconnect_context(work->rdpContext))
75 work->channel->Write(work->channel, (ULONG)len, Stream_Buffer(s),
nullptr);
76 if (status != CHANNEL_RC_OK)
77 WLog_ERR(TAG,
"Async: Write failed with 0x%08" PRIx32, status);
82 WLog_ERR(TAG,
"Async: FIDO operation failed for command %" PRIu32, work->request.command);
86 free(work->request.request);
96static UINT rdpewa_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
wStream* data)
100 const BYTE* pBuffer = Stream_ConstPointer(data);
101 const size_t cbSize = Stream_GetRemainingLength(data);
105 WINPR_ASSERT(callback);
106 WINPR_ASSERT(callback->channel);
107 WINPR_ASSERT(callback->channel->Write);
109 WLog_DBG(TAG,
"Received %" PRIuz
" bytes from server", cbSize);
110 winpr_HexDump(TAG, WLOG_TRACE, pBuffer, cbSize > 2048 ? 2048 : cbSize);
112 if (!rdpewa_cbor_decode_request(pBuffer, cbSize, &request))
114 WLog_ERR(TAG,
"Failed to decode CBOR request");
115 free(request.request);
116 return ERROR_INVALID_DATA;
120 "Received command %" PRIu32
" flags 0x%08" PRIx32
" requestLen=%" PRIuz
122 request.command, request.flags, request.requestLen, request.timeout);
124 switch (request.command)
126 case CTAPCBOR_RPC_COMMAND_WEB_AUTHN:
131 RDPEWA_ASYNC_WORK* work = calloc(1,
sizeof(*work));
134 free(request.request);
135 return ERROR_INTERNAL_ERROR;
137 work->rdpContext = rdpewa->rdp_context;
138 work->request = request;
139 work->channel = callback->channel;
141 request.request =
nullptr;
145 if (ecb->workerThread)
147 WaitForSingleObject(ecb->workerThread, INFINITE);
148 CloseHandle(ecb->workerThread);
149 ecb->workerThread =
nullptr;
153 CreateThread(
nullptr, 0, rdpewa_async_webauthn_thread, work, 0,
nullptr);
154 if (!ecb->workerThread)
156 free(work->request.request);
158 return ERROR_INTERNAL_ERROR;
160 return CHANNEL_RC_OK;
163 case CTAPCBOR_RPC_COMMAND_IUVPAA:
164 response = rdpewa_fido_is_uvpaa();
167 case CTAPCBOR_RPC_COMMAND_CANCEL_CUR_OP:
169 response = rdpewa_cbor_encode_hresult_response(S_OK);
172 case CTAPCBOR_RPC_COMMAND_API_VERSION:
173 response = rdpewa_fido_api_version();
176 case CTAPCBOR_RPC_COMMAND_GET_CREDENTIALS:
179 response = rdpewa_fido_get_credentials(rdpewa->rdp_context, request.rpId);
183 case CTAPCBOR_RPC_COMMAND_GET_AUTHENTICATOR_LIST:
184 response = rdpewa_fido_get_authenticator_list();
188 WLog_WARN(TAG,
"Unsupported command %" PRIu32, request.command);
189 response = rdpewa_cbor_encode_hresult_response(E_NOTIMPL);
195 free(request.request);
196 return ERROR_INTERNAL_ERROR;
199 const size_t responseLen = Stream_GetPosition(response);
200 WLog_DBG(TAG,
"Sending %" PRIuz
" byte response for command %" PRIu32, responseLen,
202 winpr_HexDump(TAG, WLOG_TRACE, Stream_Buffer(response), responseLen > 512 ? 512 : responseLen);
203 UINT status = callback->channel->Write(callback->channel, (ULONG)responseLen,
204 Stream_Buffer(response),
nullptr);
205 if (status != CHANNEL_RC_OK)
206 WLog_ERR(TAG,
"Write failed with 0x%08" PRIx32, status);
207 Stream_Free(response, TRUE);
208 free(request.request);
217static UINT rdpewa_on_open(IWTSVirtualChannelCallback* pChannelCallback)
219 WINPR_UNUSED(pChannelCallback);
220 return CHANNEL_RC_OK;
228static UINT rdpewa_on_close(IWTSVirtualChannelCallback* pChannelCallback)
233 if (ecb->workerThread)
235 WLog_DBG(TAG,
"Waiting for worker thread to finish...");
236 WaitForSingleObject(ecb->workerThread, INFINITE);
237 CloseHandle(ecb->workerThread);
238 ecb->workerThread =
nullptr;
243 return CHANNEL_RC_OK;
246static const IWTSVirtualChannelCallback rdpewa_callbacks = { rdpewa_on_data_received,
247 rdpewa_on_open, rdpewa_on_close,
251 rdpSettings* settings)
254 WINPR_UNUSED(settings);
257 rdpewa->rdp_context = rcontext;
258 return CHANNEL_RC_OK;
266FREERDP_ENTRY_POINT(UINT VCAPITYPE rdpewa_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
268 return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, RDPEWA_DVC_CHANNEL_NAME,
270 &rdpewa_callbacks, rdpewa_init_plugin_cb,
nullptr);
Per-channel callback with async work tracking.
Decoded MS-RDPEWA request message.