FreeRDP
Loading...
Searching...
No Matches
smartcard_main.c
1
25#include <freerdp/config.h>
26
27#include <winpr/crt.h>
28#include <winpr/smartcard.h>
29#include <winpr/environment.h>
30
31#include <freerdp/freerdp.h>
32#include <freerdp/channels/rdpdr.h>
33#include <freerdp/channels/scard.h>
34#include <freerdp/utils/smartcard_call.h>
35#include <freerdp/utils/smartcard_operations.h>
36#include <freerdp/utils/rdpdr_utils.h>
37
38#include "smartcard_main.h"
39
40#define CAST_FROM_DEVICE(device) cast_device_from(device, __func__, __FILE__, __LINE__)
41
42typedef struct
43{
44 SMARTCARD_OPERATION operation;
45 IRP* irp;
46} scard_irp_queue_element;
47
48static void smartcard_context_free(void* pCtx);
49
50static UINT smartcard_complete_irp(SMARTCARD_DEVICE* smartcard, IRP* irp, BOOL* handled);
51
52static SMARTCARD_DEVICE* cast_device_from(DEVICE* device, const char* fkt, const char* file,
53 size_t line)
54{
55 if (!device)
56 {
57 WLog_ERR(TAG, "%s [%s:%" PRIuz "] Called smartcard channel with nullptr device", fkt, file,
58 line);
59 return nullptr;
60 }
61
62 if (device->type != RDPDR_DTYP_SMARTCARD)
63 {
64 WLog_ERR(TAG,
65 "%s [%s:%" PRIuz "] Called smartcard channel with invalid device of type %" PRIx32,
66 fkt, file, line, device->type);
67 return nullptr;
68 }
69
70 return (SMARTCARD_DEVICE*)device;
71}
72
73static DWORD WINAPI smartcard_context_thread(LPVOID arg)
74{
75 SMARTCARD_CONTEXT* pContext = (SMARTCARD_CONTEXT*)arg;
76 DWORD nCount = 0;
77 DWORD waitStatus = 0;
78 HANDLE hEvents[2] = WINPR_C_ARRAY_INIT;
79 wMessage message = WINPR_C_ARRAY_INIT;
80 SMARTCARD_DEVICE* smartcard = nullptr;
81 UINT error = CHANNEL_RC_OK;
82 smartcard = pContext->smartcard;
83
84 hEvents[nCount++] = MessageQueue_Event(pContext->IrpQueue);
85
86 while (1)
87 {
88 waitStatus = WaitForMultipleObjects(nCount, hEvents, FALSE, INFINITE);
89
90 if (waitStatus == WAIT_FAILED)
91 {
92 error = GetLastError();
93 WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "!", error);
94 break;
95 }
96
97 waitStatus = WaitForSingleObject(MessageQueue_Event(pContext->IrpQueue), 0);
98
99 if (waitStatus == WAIT_FAILED)
100 {
101 error = GetLastError();
102 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
103 break;
104 }
105
106 if (waitStatus == WAIT_OBJECT_0)
107 {
108 scard_irp_queue_element* element = nullptr;
109
110 if (!MessageQueue_Peek(pContext->IrpQueue, &message, TRUE))
111 {
112 WLog_ERR(TAG, "MessageQueue_Peek failed!");
113 error = ERROR_INTERNAL_ERROR;
114 break;
115 }
116
117 if (message.id == WMQ_QUIT)
118 break;
119
120 element = (scard_irp_queue_element*)message.wParam;
121
122 if (element)
123 {
124 BOOL handled = FALSE;
125 WINPR_ASSERT(smartcard);
126
127 const LONG status =
128 smartcard_irp_device_control_call(smartcard->callctx, element->irp->output,
129 &element->irp->IoStatus, &element->operation);
130 if (status)
131 {
132 element->irp->Discard(element->irp);
133 smartcard_operation_free(&element->operation, TRUE);
134 WLog_ERR(TAG,
135 "smartcard_irp_device_control_call failed with error %s [%" PRId32 "]",
136 NtStatus2Tag(status), status);
137 error = (UINT)status;
138 break;
139 }
140
141 error = smartcard_complete_irp(smartcard, element->irp, &handled);
142 if (!handled)
143 element->irp->Discard(element->irp);
144 smartcard_operation_free(&element->operation, TRUE);
145
146 if (error)
147 {
148 WLog_ERR(TAG, "smartcard_complete_irp failed with %s [%" PRIu32 "]",
149 WTSErrorToString(error), error);
150 break;
151 }
152 }
153 }
154 }
155
156 if (error && smartcard->rdpcontext)
157 setChannelError(smartcard->rdpcontext, error, "smartcard_context_thread reported an error");
158
159 ExitThread(error);
160 return error;
161}
162
163static void smartcard_operation_queue_free(void* obj)
164{
165 wMessage* msg = obj;
166 if (!msg)
167 return;
168 if (msg->id != 0)
169 return;
170
171 scard_irp_queue_element* element = (scard_irp_queue_element*)msg->wParam;
172 if (!element)
173 return;
174 WINPR_ASSERT(element->irp);
175 WINPR_ASSERT(element->irp->Discard);
176 element->irp->Discard(element->irp);
177 smartcard_operation_free(&element->operation, TRUE);
178}
179
180static void* smartcard_context_new(void* smartcard, SCARDCONTEXT hContext)
181{
182 SMARTCARD_CONTEXT* pContext = (SMARTCARD_CONTEXT*)calloc(1, sizeof(SMARTCARD_CONTEXT));
183
184 WLog_VRB(TAG, "smartcard context create %p", (const void*)pContext);
185 if (!pContext)
186 {
187 WLog_ERR(TAG, "calloc failed!");
188 return pContext;
189 }
190
191 pContext->smartcard = smartcard;
192 pContext->hContext = hContext;
193 pContext->IrpQueue = MessageQueue_New(nullptr);
194
195 if (!pContext->IrpQueue)
196 {
197 WLog_ERR(TAG, "MessageQueue_New failed!");
198 goto fail;
199 }
200
201 {
202 wObject* obj = MessageQueue_Object(pContext->IrpQueue);
203 WINPR_ASSERT(obj);
204 obj->fnObjectFree = smartcard_operation_queue_free;
205 }
206
207 pContext->thread = CreateThread(nullptr, 0, smartcard_context_thread, pContext, 0, nullptr);
208
209 if (!pContext->thread)
210 {
211 WLog_ERR(TAG, "CreateThread failed!");
212 goto fail;
213 }
214
215 return pContext;
216fail:
217 smartcard_context_free(pContext);
218 return nullptr;
219}
220
221void smartcard_context_free(void* pCtx)
222{
223 SMARTCARD_CONTEXT* pContext = pCtx;
224
225 WLog_VRB(TAG, "smartcard context destroy %p", pCtx);
226 if (!pContext)
227 return;
228
229 /* cancel blocking calls like SCardGetStatusChange */
230 WINPR_ASSERT(pContext->smartcard);
231 smartcard_call_cancel_context(pContext->smartcard->callctx, pContext->hContext);
232
233 if (pContext->IrpQueue)
234 {
235 if (MessageQueue_PostQuit(pContext->IrpQueue, 0))
236 {
237 if (WaitForSingleObject(pContext->thread, INFINITE) == WAIT_FAILED)
238 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", GetLastError());
239
240 (void)CloseHandle(pContext->thread);
241 }
242 MessageQueue_Free(pContext->IrpQueue);
243 }
244 smartcard_call_release_context(pContext->smartcard->callctx, pContext->hContext);
245 free(pContext);
246}
247
248static UINT smartcard_free_(SMARTCARD_DEVICE* smartcard)
249{
250 if (!smartcard)
251 return CHANNEL_RC_OK;
252
253 if (smartcard->IrpQueue)
254 {
255 MessageQueue_Free(smartcard->IrpQueue);
256 (void)CloseHandle(smartcard->thread);
257 }
258
259 Stream_Free(smartcard->device.data, TRUE);
260 ListDictionary_Free(smartcard->rgOutstandingMessages);
261
262 smartcard_call_context_free(smartcard->callctx);
263
264 free(smartcard);
265 return CHANNEL_RC_OK;
266}
272static UINT smartcard_free(DEVICE* device)
273{
274 SMARTCARD_DEVICE* smartcard = CAST_FROM_DEVICE(device);
275
276 WLog_VRB(TAG, "smartcard device destroy: %p", (const void*)smartcard);
277 if (!smartcard)
278 return ERROR_INVALID_PARAMETER;
279
284 smartcard_call_cancel_all_context(smartcard->callctx);
285
286 /* Stopping all threads and cancelling all IRPs */
287
288 if (smartcard->IrpQueue)
289 {
290 if (MessageQueue_PostQuit(smartcard->IrpQueue, 0) &&
291 (WaitForSingleObject(smartcard->thread, INFINITE) == WAIT_FAILED))
292 {
293 DWORD error = GetLastError();
294 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
295 return error;
296 }
297 }
298
299 return smartcard_free_(smartcard);
300}
301
312static UINT smartcard_init(DEVICE* device)
313{
314 SMARTCARD_DEVICE* smartcard = CAST_FROM_DEVICE(device);
315
316 WLog_VRB(TAG, "smartcard device init %p", (const void*)smartcard);
317 if (!smartcard)
318 return ERROR_INVALID_PARAMETER;
319
320 smartcard_call_cancel_all_context(smartcard->callctx);
321 return CHANNEL_RC_OK;
322}
323
329UINT smartcard_complete_irp(SMARTCARD_DEVICE* smartcard, IRP* irp, BOOL* handled)
330{
331 WINPR_ASSERT(smartcard);
332 WINPR_ASSERT(irp);
333 WINPR_ASSERT(handled);
334
335 uintptr_t key = (uintptr_t)irp->CompletionId + 1;
336 ListDictionary_Remove(smartcard->rgOutstandingMessages, (void*)key);
337
338 WINPR_ASSERT(irp->Complete);
339 *handled = TRUE;
340 return irp->Complete(irp);
341}
342
353static UINT smartcard_process_irp(SMARTCARD_DEVICE* smartcard, IRP* irp, BOOL* handled)
354{
355 LONG status = 0;
356 BOOL asyncIrp = FALSE;
357 SMARTCARD_CONTEXT* pContext = nullptr;
358
359 WINPR_ASSERT(smartcard);
360 WINPR_ASSERT(handled);
361 WINPR_ASSERT(irp);
362 WINPR_ASSERT(irp->Complete);
363
364 uintptr_t key = (uintptr_t)irp->CompletionId + 1;
365
366 if (!ListDictionary_Add(smartcard->rgOutstandingMessages, (void*)key, irp))
367 {
368 WLog_ERR(TAG, "ListDictionary_Add failed!");
369 return ERROR_INTERNAL_ERROR;
370 }
371
372 if (irp->MajorFunction == IRP_MJ_DEVICE_CONTROL)
373 {
374 scard_irp_queue_element* element = calloc(1, sizeof(scard_irp_queue_element));
375 if (!element)
376 return ERROR_OUTOFMEMORY;
377
378 element->irp = irp;
379 element->operation.completionID = irp->CompletionId;
380
381 status = smartcard_irp_device_control_decode(irp->input, irp->CompletionId, irp->FileId,
382 &element->operation);
383
384 if (status != SCARD_S_SUCCESS)
385 {
386 UINT error = 0;
387
388 smartcard_operation_free(&element->operation, TRUE);
389 irp->IoStatus = STATUS_UNSUCCESSFUL;
390
391 if ((error = smartcard_complete_irp(smartcard, irp, handled)))
392 {
393 WLog_ERR(TAG, "Queue_Enqueue failed!");
394 return error;
395 }
396
397 return CHANNEL_RC_OK;
398 }
399
400 asyncIrp = TRUE;
401
402 switch (element->operation.ioControlCode)
403 {
404 case SCARD_IOCTL_ESTABLISHCONTEXT:
405 case SCARD_IOCTL_RELEASECONTEXT:
406 case SCARD_IOCTL_ISVALIDCONTEXT:
407 case SCARD_IOCTL_CANCEL:
408 case SCARD_IOCTL_ACCESSSTARTEDEVENT:
409 case SCARD_IOCTL_RELEASETARTEDEVENT:
410 asyncIrp = FALSE;
411 break;
412
413 case SCARD_IOCTL_LISTREADERGROUPSA:
414 case SCARD_IOCTL_LISTREADERGROUPSW:
415 case SCARD_IOCTL_LISTREADERSA:
416 case SCARD_IOCTL_LISTREADERSW:
417 case SCARD_IOCTL_INTRODUCEREADERGROUPA:
418 case SCARD_IOCTL_INTRODUCEREADERGROUPW:
419 case SCARD_IOCTL_FORGETREADERGROUPA:
420 case SCARD_IOCTL_FORGETREADERGROUPW:
421 case SCARD_IOCTL_INTRODUCEREADERA:
422 case SCARD_IOCTL_INTRODUCEREADERW:
423 case SCARD_IOCTL_FORGETREADERA:
424 case SCARD_IOCTL_FORGETREADERW:
425 case SCARD_IOCTL_ADDREADERTOGROUPA:
426 case SCARD_IOCTL_ADDREADERTOGROUPW:
427 case SCARD_IOCTL_REMOVEREADERFROMGROUPA:
428 case SCARD_IOCTL_REMOVEREADERFROMGROUPW:
429 case SCARD_IOCTL_LOCATECARDSA:
430 case SCARD_IOCTL_LOCATECARDSW:
431 case SCARD_IOCTL_LOCATECARDSBYATRA:
432 case SCARD_IOCTL_LOCATECARDSBYATRW:
433 case SCARD_IOCTL_READCACHEA:
434 case SCARD_IOCTL_READCACHEW:
435 case SCARD_IOCTL_WRITECACHEA:
436 case SCARD_IOCTL_WRITECACHEW:
437 case SCARD_IOCTL_GETREADERICON:
438 case SCARD_IOCTL_GETDEVICETYPEID:
439 case SCARD_IOCTL_GETSTATUSCHANGEA:
440 case SCARD_IOCTL_GETSTATUSCHANGEW:
441 case SCARD_IOCTL_CONNECTA:
442 case SCARD_IOCTL_CONNECTW:
443 case SCARD_IOCTL_RECONNECT:
444 case SCARD_IOCTL_DISCONNECT:
445 case SCARD_IOCTL_BEGINTRANSACTION:
446 case SCARD_IOCTL_ENDTRANSACTION:
447 case SCARD_IOCTL_STATE:
448 case SCARD_IOCTL_STATUSA:
449 case SCARD_IOCTL_STATUSW:
450 case SCARD_IOCTL_TRANSMIT:
451 case SCARD_IOCTL_CONTROL:
452 case SCARD_IOCTL_GETATTRIB:
453 case SCARD_IOCTL_SETATTRIB:
454 case SCARD_IOCTL_GETTRANSMITCOUNT:
455 asyncIrp = TRUE;
456 break;
457 default:
458 break;
459 }
460
461 pContext = smartcard_call_get_context(smartcard->callctx, element->operation.hContext);
462
463 if (!pContext)
464 asyncIrp = FALSE;
465
466 if (!asyncIrp)
467 {
468 UINT error = 0;
469
470 status =
471 smartcard_irp_device_control_call(smartcard->callctx, element->irp->output,
472 &element->irp->IoStatus, &element->operation);
473 smartcard_operation_free(&element->operation, TRUE);
474
475 if (status)
476 {
477 WLog_ERR(TAG, "smartcard_irp_device_control_call failed with error %" PRId32 "!",
478 status);
479 return (UINT32)status;
480 }
481
482 if ((error = smartcard_complete_irp(smartcard, irp, handled)))
483 {
484 WLog_ERR(TAG, "Queue_Enqueue failed!");
485 return error;
486 }
487 }
488 else
489 {
490 if (pContext)
491 {
492 if (!MessageQueue_Post(pContext->IrpQueue, nullptr, 0, (void*)element, nullptr))
493 {
494 smartcard_operation_free(&element->operation, TRUE);
495 WLog_ERR(TAG, "MessageQueue_Post failed!");
496 return ERROR_INTERNAL_ERROR;
497 }
498 *handled = TRUE;
499 }
500 }
501 }
502 else
503 {
504 UINT ustatus = 0;
505 WLog_ERR(TAG, "Unexpected SmartCard IRP: MajorFunction %s, MinorFunction: 0x%08" PRIX32 "",
506 rdpdr_irp_string(irp->MajorFunction), irp->MinorFunction);
507 irp->IoStatus = STATUS_NOT_SUPPORTED;
508
509 if ((ustatus = smartcard_complete_irp(smartcard, irp, handled)))
510 {
511 WLog_ERR(TAG, "Queue_Enqueue failed!");
512 return ustatus;
513 }
514 }
515
516 return CHANNEL_RC_OK;
517}
518
519static DWORD WINAPI smartcard_thread_func(LPVOID arg)
520{
521 IRP* irp = nullptr;
522 DWORD nCount = 0;
523 DWORD status = 0;
524 HANDLE hEvents[1] = WINPR_C_ARRAY_INIT;
525 wMessage message = WINPR_C_ARRAY_INIT;
526 UINT error = CHANNEL_RC_OK;
527 SMARTCARD_DEVICE* smartcard = CAST_FROM_DEVICE(arg);
528
529 if (!smartcard)
530 return ERROR_INVALID_PARAMETER;
531
532 hEvents[nCount++] = MessageQueue_Event(smartcard->IrpQueue);
533
534 while (1)
535 {
536 status = WaitForMultipleObjects(nCount, hEvents, FALSE, INFINITE);
537
538 if (status == WAIT_FAILED)
539 {
540 error = GetLastError();
541 WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "!", error);
542 break;
543 }
544
545 if (status == WAIT_OBJECT_0)
546 {
547 if (!MessageQueue_Peek(smartcard->IrpQueue, &message, TRUE))
548 {
549 WLog_ERR(TAG, "MessageQueue_Peek failed!");
550 error = ERROR_INTERNAL_ERROR;
551 break;
552 }
553
554 if (message.id == WMQ_QUIT)
555 break;
556
557 irp = (IRP*)message.wParam;
558
559 if (irp)
560 {
561 BOOL handled = FALSE;
562 if ((error = smartcard_process_irp(smartcard, irp, &handled)))
563 {
564 WLog_ERR(TAG, "smartcard_process_irp failed with error %" PRIu32 "!", error);
565 goto out;
566 }
567 if (!handled)
568 {
569 WINPR_ASSERT(irp->Discard);
570 irp->Discard(irp);
571 }
572 }
573 }
574 }
575
576out:
577
578 if (error && smartcard->rdpcontext)
579 setChannelError(smartcard->rdpcontext, error, "smartcard_thread_func reported an error");
580
581 ExitThread(error);
582 return error;
583}
584
590static UINT smartcard_irp_request(DEVICE* device, IRP* irp)
591{
592 SMARTCARD_DEVICE* smartcard = CAST_FROM_DEVICE(device);
593
594 if (!smartcard)
595 {
596 irp->Discard(irp);
597 return ERROR_INVALID_PARAMETER;
598 }
599
600 if (!MessageQueue_Post(smartcard->IrpQueue, nullptr, 0, (void*)irp, nullptr))
601 {
602 WLog_ERR(TAG, "MessageQueue_Post failed!");
603 irp->Discard(irp);
604 return ERROR_INTERNAL_ERROR;
605 }
606
607 return CHANNEL_RC_OK;
608}
609
610static void smartcard_free_irp(void* obj)
611{
612 wMessage* msg = obj;
613 if (!msg)
614 return;
615 if (msg->id != 0)
616 return;
617
618 IRP* irp = (IRP*)msg->wParam;
619 if (!irp)
620 return;
621 WINPR_ASSERT(irp->Discard);
622 irp->Discard(irp);
623}
624
625/* smartcard is always built-in */
626#define DeviceServiceEntry smartcard_DeviceServiceEntry
627
633FREERDP_ENTRY_POINT(UINT VCAPITYPE DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
634{
635 size_t length = 0;
636 UINT error = CHANNEL_RC_NO_MEMORY;
637
638 SMARTCARD_DEVICE* smartcard = (SMARTCARD_DEVICE*)calloc(1, sizeof(SMARTCARD_DEVICE));
639 WLog_VRB(TAG, "smartcard device create: %p", (const void*)smartcard);
640 if (!smartcard)
641 {
642 WLog_ERR(TAG, "calloc failed!");
643 return CHANNEL_RC_NO_MEMORY;
644 }
645
646 smartcard->device.type = RDPDR_DTYP_SMARTCARD;
647 smartcard->device.name = "SCARD";
648 smartcard->device.IRPRequest = smartcard_irp_request;
649 smartcard->device.Init = smartcard_init;
650 smartcard->device.Free = smartcard_free;
651 smartcard->rdpcontext = pEntryPoints->rdpcontext;
652 length = strlen(smartcard->device.name);
653 smartcard->device.data = Stream_New(nullptr, length + 1);
654
655 if (!smartcard->device.data)
656 {
657 WLog_ERR(TAG, "Stream_New failed!");
658 goto fail;
659 }
660
661 Stream_Write(smartcard->device.data, "SCARD", 6);
662 smartcard->IrpQueue = MessageQueue_New(nullptr);
663
664 if (!smartcard->IrpQueue)
665 {
666 WLog_ERR(TAG, "MessageQueue_New failed!");
667 goto fail;
668 }
669
670 wObject* obj = MessageQueue_Object(smartcard->IrpQueue);
671 WINPR_ASSERT(obj);
672 obj->fnObjectFree = smartcard_free_irp;
673
674 smartcard->rgOutstandingMessages = ListDictionary_New(TRUE);
675
676 if (!smartcard->rgOutstandingMessages)
677 {
678 WLog_ERR(TAG, "ListDictionary_New failed!");
679 goto fail;
680 }
681
682 smartcard->callctx = smartcard_call_context_new_with_context(smartcard->rdpcontext);
683 if (!smartcard->callctx)
684 goto fail;
685
686 if (!smarcard_call_set_callbacks(smartcard->callctx, smartcard, smartcard_context_new,
687 smartcard_context_free))
688 goto fail;
689
690 if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, &smartcard->device)))
691 {
692 WLog_ERR(TAG, "RegisterDevice failed!");
693 goto fail;
694 }
695
696 smartcard->thread =
697 CreateThread(nullptr, 0, smartcard_thread_func, smartcard, CREATE_SUSPENDED, nullptr);
698
699 if (!smartcard->thread)
700 {
701 WLog_ERR(TAG, "CreateThread failed!");
702 error = ERROR_INTERNAL_ERROR;
703 goto fail;
704 }
705
706 ResumeThread(smartcard->thread);
707
708 if (pEntryPoints->device->Name)
709 {
710 if (!smartcard_call_context_add(smartcard->callctx, pEntryPoints->device->Name))
711 goto fail;
712 }
713
714 return CHANNEL_RC_OK;
715fail:
716 smartcard_free_(smartcard);
717 return error;
718}
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59