21 #include <winpr/assert.h>
22 #include <winpr/string.h>
24 #include <winpr/smartcard.h>
25 #include <winpr/pool.h>
27 #include <freerdp/server/proxy/proxy_log.h>
28 #include <freerdp/emulate/scard/smartcard_emulate.h>
29 #include <freerdp/channels/scard.h>
30 #include <freerdp/channels/rdpdr.h>
31 #include <freerdp/utils/rdpdr_utils.h>
33 #include <freerdp/utils/smartcard_operations.h>
34 #include <freerdp/utils/smartcard_call.h>
36 #include "pf_channel_smartcard.h"
37 #include "pf_channel_rdpdr.h"
39 #define TAG PROXY_TAG("channel.scard")
41 #define SCARD_SVC_CHANNEL_NAME "SCARD"
46 scard_call_context* callctx;
49 wArrayList* workObjects;
50 } pf_channel_client_context;
58 pf_scard_send_fkt_t send_fkt;
59 } pf_channel_client_queue_element;
61 static pf_channel_client_context* scard_get_client_context(pClientContext* pc)
63 pf_channel_client_context* scard = NULL;
66 WINPR_ASSERT(pc->interceptContextMap);
68 scard = HashTable_GetItemValue(pc->interceptContextMap, SCARD_SVC_CHANNEL_NAME);
70 WLog_WARN(TAG,
"[%s] missing in pc->interceptContextMap", SCARD_SVC_CHANNEL_NAME);
86 pos = Stream_GetPosition(out);
87 Stream_SetPosition(out, 0);
88 if (!Stream_CheckAndLogRequiredLength(TAG, out, 16))
91 Stream_Read_UINT16(out, component);
92 Stream_Read_UINT16(out, packetid);
94 Stream_Read_UINT32(out, dID);
95 Stream_Read_UINT32(out, cID);
97 WINPR_ASSERT(component == RDPDR_CTYP_CORE);
98 WINPR_ASSERT(packetid == PAKID_CORE_DEVICE_IOCOMPLETION);
99 WINPR_ASSERT(dID == op->deviceID);
100 WINPR_ASSERT(cID == op->completionID);
102 Stream_Write_UINT32(out, ioStatus);
103 Stream_SetPosition(out, pos);
109 pf_channel_client_context* scard;
110 pf_channel_client_queue_element* e;
113 static void queue_free(
void* obj);
114 static void* queue_copy(
const void* obj);
116 static VOID irp_thread(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WORK Work)
118 struct thread_arg* arg = Context;
119 pf_channel_client_context* scard = arg->scard;
122 LONG rc = smartcard_irp_device_control_call(arg->scard->callctx, arg->e->out, &ioStatus,
124 if (rc == CHANNEL_RC_OK)
126 if (pf_channel_client_write_iostatus(arg->e->out, &arg->e->op, ioStatus))
127 arg->e->send_fkt(arg->e->log, arg->e->pc, arg->e->out);
132 ArrayList_Remove(scard->workObjects, Work);
135 static BOOL start_irp_thread(pf_channel_client_context* scard,
136 const pf_channel_client_queue_element* e)
138 PTP_WORK work = NULL;
139 struct thread_arg* arg = calloc(1,
sizeof(
struct thread_arg));
143 arg->e = queue_copy(e);
147 work = CreateThreadpoolWork(irp_thread, arg, &scard->ThreadPoolEnv);
150 ArrayList_Append(scard->workObjects, work);
151 SubmitThreadpoolWork(work);
162 BOOL pf_channel_smartcard_client_handle(wLog* log, pClientContext* pc,
wStream* s,
wStream* out,
163 pf_scard_send_fkt_t send_fkt)
168 UINT32 CompletionId = 0;
170 pf_channel_client_queue_element e = { 0 };
171 pf_channel_client_context* scard = scard_get_client_context(pc);
174 WINPR_ASSERT(send_fkt);
183 e.send_fkt = send_fkt;
186 if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
191 UINT32 MajorFunction = 0;
192 UINT32 MinorFunction = 0;
194 Stream_Read_UINT32(s, DeviceId);
195 Stream_Read_UINT32(s, FileId);
196 Stream_Read_UINT32(s, CompletionId);
197 Stream_Read_UINT32(s, MajorFunction);
198 Stream_Read_UINT32(s, MinorFunction);
200 if (MajorFunction != IRP_MJ_DEVICE_CONTROL)
202 WLog_WARN(TAG,
"[%s] Invalid IRP received, expected %s, got %2", SCARD_SVC_CHANNEL_NAME,
203 rdpdr_irp_string(IRP_MJ_DEVICE_CONTROL), rdpdr_irp_string(MajorFunction));
206 e.op.completionID = CompletionId;
207 e.op.deviceID = DeviceId;
209 if (!rdpdr_write_iocompletion_header(out, DeviceId, CompletionId, 0))
213 status = smartcard_irp_device_control_decode(s, CompletionId, FileId, &e.op);
217 switch (e.op.ioControlCode)
219 case SCARD_IOCTL_LISTREADERGROUPSA:
220 case SCARD_IOCTL_LISTREADERGROUPSW:
221 case SCARD_IOCTL_LISTREADERSA:
222 case SCARD_IOCTL_LISTREADERSW:
223 case SCARD_IOCTL_LOCATECARDSA:
224 case SCARD_IOCTL_LOCATECARDSW:
225 case SCARD_IOCTL_LOCATECARDSBYATRA:
226 case SCARD_IOCTL_LOCATECARDSBYATRW:
227 case SCARD_IOCTL_GETSTATUSCHANGEA:
228 case SCARD_IOCTL_GETSTATUSCHANGEW:
229 case SCARD_IOCTL_CONNECTA:
230 case SCARD_IOCTL_CONNECTW:
231 case SCARD_IOCTL_RECONNECT:
232 case SCARD_IOCTL_DISCONNECT:
233 case SCARD_IOCTL_BEGINTRANSACTION:
234 case SCARD_IOCTL_ENDTRANSACTION:
235 case SCARD_IOCTL_STATE:
236 case SCARD_IOCTL_STATUSA:
237 case SCARD_IOCTL_STATUSW:
238 case SCARD_IOCTL_TRANSMIT:
239 case SCARD_IOCTL_CONTROL:
240 case SCARD_IOCTL_GETATTRIB:
241 case SCARD_IOCTL_SETATTRIB:
242 if (!start_irp_thread(scard, &e))
247 status = smartcard_irp_device_control_call(scard->callctx, out, &ioStatus, &e.op);
250 if (!pf_channel_client_write_iostatus(out, &e.op, ioStatus))
255 rc = send_fkt(log, pc, out) == CHANNEL_RC_OK;
258 smartcard_operation_free(&e.op, FALSE);
262 BOOL pf_channel_smartcard_server_handle(pServerContext* ps,
wStream* s)
264 WLog_ERR(TAG,
"TODO: unimplemented");
268 static void channel_stop_and_wait(pf_channel_client_context* scard, BOOL reset)
271 smartcard_call_context_signal_stop(scard->callctx, FALSE);
273 while (ArrayList_Count(scard->workObjects) > 0)
275 PTP_WORK work = ArrayList_GetItem(scard->workObjects, 0);
278 WaitForThreadpoolWorkCallbacks(work, TRUE);
281 smartcard_call_context_signal_stop(scard->callctx, reset);
286 pf_channel_client_context* entry = (pf_channel_client_context*)base;
293 channel_stop_and_wait(entry, FALSE);
294 ArrayList_Free(entry->workObjects);
295 CloseThreadpool(entry->ThreadPool);
296 DestroyThreadpoolEnvironment(&entry->ThreadPoolEnv);
298 smartcard_call_context_free(entry->callctx);
302 static void queue_free(
void* obj)
304 pf_channel_client_queue_element* element = obj;
307 smartcard_operation_free(&element->op, FALSE);
308 Stream_Free(element->out, TRUE);
312 static void* queue_copy(
const void* obj)
314 const pf_channel_client_queue_element* other = obj;
315 pf_channel_client_queue_element* copy = NULL;
318 copy = calloc(1,
sizeof(pf_channel_client_queue_element));
323 copy->out = Stream_New(NULL, Stream_Capacity(other->out));
326 Stream_Write(copy->out, Stream_Buffer(other->out), Stream_GetPosition(other->out));
333 static void work_object_free(
void* arg)
336 CloseThreadpoolWork(work);
339 BOOL pf_channel_smartcard_client_new(pClientContext* pc)
341 pf_channel_client_context* scard = NULL;
345 WINPR_ASSERT(pc->interceptContextMap);
347 scard = calloc(1,
sizeof(pf_channel_client_context));
350 scard->base.free = pf_channel_scard_client_context_free;
351 scard->callctx = smartcard_call_context_new(pc->context.settings);
355 scard->workObjects = ArrayList_New(TRUE);
356 if (!scard->workObjects)
358 obj = ArrayList_Object(scard->workObjects);
360 obj->fnObjectFree = work_object_free;
362 scard->ThreadPool = CreateThreadpool(NULL);
363 if (!scard->ThreadPool)
365 InitializeThreadpoolEnvironment(&scard->ThreadPoolEnv);
366 SetThreadpoolCallbackPool(&scard->ThreadPoolEnv, scard->ThreadPool);
368 return HashTable_Insert(pc->interceptContextMap, SCARD_SVC_CHANNEL_NAME, scard);
370 pf_channel_scard_client_context_free(&scard->base);
374 void pf_channel_smartcard_client_free(pClientContext* pc)
377 WINPR_ASSERT(pc->interceptContextMap);
378 HashTable_Remove(pc->interceptContextMap, SCARD_SVC_CHANNEL_NAME);
381 BOOL pf_channel_smartcard_client_emulate(pClientContext* pc)
383 pf_channel_client_context* scard = scard_get_client_context(pc);
386 return smartcard_call_is_configured(scard->callctx);
389 BOOL pf_channel_smartcard_client_reset(pClientContext* pc)
391 pf_channel_client_context* scard = scard_get_client_context(pc);
395 channel_stop_and_wait(scard, TRUE);
This struct contains function pointer to initialize/free objects.