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 smartcard_context_queue_loop(SMARTCARD_CONTEXT* pContext, wMessageQueue* queue)
74{
75 DWORD nCount = 0;
76 DWORD waitStatus = 0;
77 HANDLE hEvents[2] = WINPR_C_ARRAY_INIT;
78 wMessage message = WINPR_C_ARRAY_INIT;
79 SMARTCARD_DEVICE* smartcard = nullptr;
80 UINT error = CHANNEL_RC_OK;
81 smartcard = pContext->smartcard;
82
83 hEvents[nCount++] = MessageQueue_Event(queue);
84
85 while (1)
86 {
87 waitStatus = WaitForMultipleObjects(nCount, hEvents, FALSE, INFINITE);
88
89 if (waitStatus == WAIT_FAILED)
90 {
91 error = GetLastError();
92 WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "!", error);
93 break;
94 }
95
96 waitStatus = WaitForSingleObject(MessageQueue_Event(queue), 0);
97
98 if (waitStatus == WAIT_FAILED)
99 {
100 error = GetLastError();
101 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
102 break;
103 }
104
105 if (waitStatus == WAIT_OBJECT_0)
106 {
107 scard_irp_queue_element* element = nullptr;
108
109 if (!MessageQueue_Peek(queue, &message, TRUE))
110 {
111 WLog_ERR(TAG, "MessageQueue_Peek failed!");
112 error = ERROR_INTERNAL_ERROR;
113 break;
114 }
115
116 if (message.id == WMQ_QUIT)
117 break;
118
119 element = (scard_irp_queue_element*)message.wParam;
120
121 if (element)
122 {
123 BOOL handled = FALSE;
124 WINPR_ASSERT(smartcard);
125
126 const LONG status =
127 smartcard_irp_device_control_call(smartcard->callctx, element->irp->output,
128 &element->irp->IoStatus, &element->operation);
129 if (status)
130 {
131 element->irp->Discard(element->irp);
132 smartcard_operation_free(&element->operation, TRUE);
133 WLog_ERR(TAG,
134 "smartcard_irp_device_control_call failed with error %s [%" PRId32 "]",
135 NtStatus2Tag(status), status);
136 error = (UINT)status;
137 break;
138 }
139
140 error = smartcard_complete_irp(smartcard, element->irp, &handled);
141 if (!handled)
142 element->irp->Discard(element->irp);
143 smartcard_operation_free(&element->operation, TRUE);
144
145 if (error)
146 {
147 WLog_ERR(TAG, "smartcard_complete_irp failed with %s [%" PRIu32 "]",
148 WTSErrorToString(error), error);
149 break;
150 }
151 }
152 }
153 }
154
155 if (error && smartcard->rdpcontext)
156 setChannelError(smartcard->rdpcontext, error, "smartcard_context_thread reported an error");
157
158 return error;
159}
160
161static DWORD WINAPI smartcard_context_thread(LPVOID arg)
162{
163 SMARTCARD_CONTEXT* pContext = (SMARTCARD_CONTEXT*)arg;
164 const DWORD error = smartcard_context_queue_loop(pContext, pContext->IrpQueue);
165 ExitThread(error);
166 return error;
167}
168
169static DWORD WINAPI smartcard_context_tx_thread(LPVOID arg)
170{
171 SMARTCARD_CONTEXT* pContext = (SMARTCARD_CONTEXT*)arg;
172 const DWORD error = smartcard_context_queue_loop(pContext, pContext->TxQueue);
173 ExitThread(error);
174 return error;
175}
176
177static void smartcard_operation_queue_free(void* obj)
178{
179 wMessage* msg = obj;
180 if (!msg)
181 return;
182 if (msg->id != 0)
183 return;
184
185 scard_irp_queue_element* element = (scard_irp_queue_element*)msg->wParam;
186 if (!element)
187 return;
188 WINPR_ASSERT(element->irp);
189 WINPR_ASSERT(element->irp->Discard);
190 element->irp->Discard(element->irp);
191 smartcard_operation_free(&element->operation, TRUE);
192}
193
194static void* smartcard_context_new(void* smartcard, SCARDCONTEXT hContext)
195{
196 SMARTCARD_CONTEXT* pContext = (SMARTCARD_CONTEXT*)calloc(1, sizeof(SMARTCARD_CONTEXT));
197
198 WLog_VRB(TAG, "smartcard context create %p", (const void*)pContext);
199 if (!pContext)
200 {
201 WLog_ERR(TAG, "calloc failed!");
202 return pContext;
203 }
204
205 pContext->smartcard = smartcard;
206 pContext->hContext = hContext;
207 pContext->IrpQueue = MessageQueue_New(nullptr);
208
209 if (!pContext->IrpQueue)
210 {
211 WLog_ERR(TAG, "MessageQueue_New failed!");
212 goto fail;
213 }
214
215 {
216 wObject* obj = MessageQueue_Object(pContext->IrpQueue);
217 WINPR_ASSERT(obj);
218 obj->fnObjectFree = smartcard_operation_queue_free;
219 }
220
221 pContext->thread = CreateThread(nullptr, 0, smartcard_context_thread, pContext, 0, nullptr);
222
223 if (!pContext->thread)
224 {
225 WLog_ERR(TAG, "CreateThread failed!");
226 goto fail;
227 }
228
229 pContext->TxQueue = MessageQueue_New(nullptr);
230
231 if (!pContext->TxQueue)
232 {
233 WLog_ERR(TAG, "MessageQueue_New failed!");
234 goto fail;
235 }
236
237 {
238 wObject* obj = MessageQueue_Object(pContext->TxQueue);
239 WINPR_ASSERT(obj);
240 obj->fnObjectFree = smartcard_operation_queue_free;
241 }
242
243 pContext->txThread =
244 CreateThread(nullptr, 0, smartcard_context_tx_thread, pContext, 0, nullptr);
245
246 if (!pContext->txThread)
247 {
248 WLog_ERR(TAG, "CreateThread failed!");
249 goto fail;
250 }
251
252 return pContext;
253fail:
254 smartcard_context_free(pContext);
255 return nullptr;
256}
257
258void smartcard_context_free(void* pCtx)
259{
260 SMARTCARD_CONTEXT* pContext = pCtx;
261
262 WLog_VRB(TAG, "smartcard context destroy %p", pCtx);
263 if (!pContext)
264 return;
265
266 /* cancel blocking calls like SCardGetStatusChange */
267 WINPR_ASSERT(pContext->smartcard);
268 smartcard_call_cancel_context(pContext->smartcard->callctx, pContext->hContext);
269
270 if (pContext->IrpQueue)
271 {
272 if (MessageQueue_PostQuit(pContext->IrpQueue, 0))
273 {
274 if (WaitForSingleObject(pContext->thread, INFINITE) == WAIT_FAILED)
275 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", GetLastError());
276
277 (void)CloseHandle(pContext->thread);
278 }
279 MessageQueue_Free(pContext->IrpQueue);
280 }
281 if (pContext->TxQueue)
282 {
283 if (pContext->txThread && MessageQueue_PostQuit(pContext->TxQueue, 0))
284 {
285 if (WaitForSingleObject(pContext->txThread, INFINITE) == WAIT_FAILED)
286 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", GetLastError());
287
288 (void)CloseHandle(pContext->txThread);
289 }
290 MessageQueue_Free(pContext->TxQueue);
291 }
292 smartcard_call_release_context(pContext->smartcard->callctx, pContext->hContext);
293 free(pContext);
294}
295
296static UINT smartcard_free_(SMARTCARD_DEVICE* smartcard)
297{
298 if (!smartcard)
299 return CHANNEL_RC_OK;
300
301 if (smartcard->IrpQueue)
302 {
303 MessageQueue_Free(smartcard->IrpQueue);
304 (void)CloseHandle(smartcard->thread);
305 }
306
307 Stream_Free(smartcard->device.data, TRUE);
308 ListDictionary_Free(smartcard->rgOutstandingMessages);
309
310 smartcard_call_context_free(smartcard->callctx);
311
312 free(smartcard);
313 return CHANNEL_RC_OK;
314}
320static UINT smartcard_free(DEVICE* device)
321{
322 SMARTCARD_DEVICE* smartcard = CAST_FROM_DEVICE(device);
323
324 WLog_VRB(TAG, "smartcard device destroy: %p", (const void*)smartcard);
325 if (!smartcard)
326 return ERROR_INVALID_PARAMETER;
327
332 smartcard_call_cancel_all_context(smartcard->callctx);
333
334 /* Stopping all threads and cancelling all IRPs */
335
336 if (smartcard->IrpQueue)
337 {
338 if (MessageQueue_PostQuit(smartcard->IrpQueue, 0) &&
339 (WaitForSingleObject(smartcard->thread, INFINITE) == WAIT_FAILED))
340 {
341 DWORD error = GetLastError();
342 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
343 return error;
344 }
345 }
346
347 return smartcard_free_(smartcard);
348}
349
360static UINT smartcard_init(DEVICE* device)
361{
362 SMARTCARD_DEVICE* smartcard = CAST_FROM_DEVICE(device);
363
364 WLog_VRB(TAG, "smartcard device init %p", (const void*)smartcard);
365 if (!smartcard)
366 return ERROR_INVALID_PARAMETER;
367
368 smartcard_call_cancel_all_context(smartcard->callctx);
369 return CHANNEL_RC_OK;
370}
371
377UINT smartcard_complete_irp(SMARTCARD_DEVICE* smartcard, IRP* irp, BOOL* handled)
378{
379 WINPR_ASSERT(smartcard);
380 WINPR_ASSERT(irp);
381 WINPR_ASSERT(handled);
382
383 uintptr_t key = (uintptr_t)irp->CompletionId + 1;
384 ListDictionary_Remove(smartcard->rgOutstandingMessages, (void*)key);
385
386 WINPR_ASSERT(irp->Complete);
387 *handled = TRUE;
388 return irp->Complete(irp);
389}
390
401static UINT smartcard_process_irp(SMARTCARD_DEVICE* smartcard, IRP* irp, BOOL* handled)
402{
403 LONG status = 0;
404 BOOL asyncIrp = FALSE;
405 SMARTCARD_CONTEXT* pContext = nullptr;
406
407 WINPR_ASSERT(smartcard);
408 WINPR_ASSERT(handled);
409 WINPR_ASSERT(irp);
410 WINPR_ASSERT(irp->Complete);
411
412 uintptr_t key = (uintptr_t)irp->CompletionId + 1;
413
414 if (!ListDictionary_Add(smartcard->rgOutstandingMessages, (void*)key, irp))
415 {
416 WLog_ERR(TAG, "ListDictionary_Add failed!");
417 return ERROR_INTERNAL_ERROR;
418 }
419
420 if (irp->MajorFunction == IRP_MJ_DEVICE_CONTROL)
421 {
422 scard_irp_queue_element* element = calloc(1, sizeof(scard_irp_queue_element));
423 if (!element)
424 return ERROR_OUTOFMEMORY;
425
426 element->irp = irp;
427 element->operation.completionID = irp->CompletionId;
428
429 status = smartcard_irp_device_control_decode_request(irp->input, irp->CompletionId,
430 irp->FileId, &element->operation);
431
432 if (status != SCARD_S_SUCCESS)
433 {
434 UINT error = 0;
435
436 smartcard_operation_free(&element->operation, TRUE);
437 irp->IoStatus = STATUS_UNSUCCESSFUL;
438
439 if ((error = smartcard_complete_irp(smartcard, irp, handled)))
440 {
441 WLog_ERR(TAG, "Queue_Enqueue failed!");
442 return error;
443 }
444
445 return CHANNEL_RC_OK;
446 }
447
448 asyncIrp = TRUE;
449
450 switch (element->operation.ioControlCode)
451 {
452 case SCARD_IOCTL_ESTABLISHCONTEXT:
453 case SCARD_IOCTL_RELEASECONTEXT:
454 case SCARD_IOCTL_ISVALIDCONTEXT:
455 case SCARD_IOCTL_CANCEL:
456 case SCARD_IOCTL_ACCESSSTARTEDEVENT:
457 case SCARD_IOCTL_RELEASETARTEDEVENT:
458 asyncIrp = FALSE;
459 break;
460
461 case SCARD_IOCTL_LISTREADERGROUPSA:
462 case SCARD_IOCTL_LISTREADERGROUPSW:
463 case SCARD_IOCTL_LISTREADERSA:
464 case SCARD_IOCTL_LISTREADERSW:
465 case SCARD_IOCTL_INTRODUCEREADERGROUPA:
466 case SCARD_IOCTL_INTRODUCEREADERGROUPW:
467 case SCARD_IOCTL_FORGETREADERGROUPA:
468 case SCARD_IOCTL_FORGETREADERGROUPW:
469 case SCARD_IOCTL_INTRODUCEREADERA:
470 case SCARD_IOCTL_INTRODUCEREADERW:
471 case SCARD_IOCTL_FORGETREADERA:
472 case SCARD_IOCTL_FORGETREADERW:
473 case SCARD_IOCTL_ADDREADERTOGROUPA:
474 case SCARD_IOCTL_ADDREADERTOGROUPW:
475 case SCARD_IOCTL_REMOVEREADERFROMGROUPA:
476 case SCARD_IOCTL_REMOVEREADERFROMGROUPW:
477 case SCARD_IOCTL_LOCATECARDSA:
478 case SCARD_IOCTL_LOCATECARDSW:
479 case SCARD_IOCTL_LOCATECARDSBYATRA:
480 case SCARD_IOCTL_LOCATECARDSBYATRW:
481 case SCARD_IOCTL_READCACHEA:
482 case SCARD_IOCTL_READCACHEW:
483 case SCARD_IOCTL_WRITECACHEA:
484 case SCARD_IOCTL_WRITECACHEW:
485 case SCARD_IOCTL_GETREADERICON:
486 case SCARD_IOCTL_GETDEVICETYPEID:
487 case SCARD_IOCTL_GETSTATUSCHANGEA:
488 case SCARD_IOCTL_GETSTATUSCHANGEW:
489 case SCARD_IOCTL_CONNECTA:
490 case SCARD_IOCTL_CONNECTW:
491 case SCARD_IOCTL_RECONNECT:
492 case SCARD_IOCTL_DISCONNECT:
493 case SCARD_IOCTL_BEGINTRANSACTION:
494 case SCARD_IOCTL_ENDTRANSACTION:
495 case SCARD_IOCTL_STATE:
496 case SCARD_IOCTL_STATUSA:
497 case SCARD_IOCTL_STATUSW:
498 case SCARD_IOCTL_TRANSMIT:
499 case SCARD_IOCTL_CONTROL:
500 case SCARD_IOCTL_GETATTRIB:
501 case SCARD_IOCTL_SETATTRIB:
502 case SCARD_IOCTL_GETTRANSMITCOUNT:
503 asyncIrp = TRUE;
504 break;
505 default:
506 break;
507 }
508
509 pContext = smartcard_call_get_context(smartcard->callctx, element->operation.hContext);
510
511 if (!pContext)
512 asyncIrp = FALSE;
513
514 if (!asyncIrp)
515 {
516 UINT error = 0;
517
518 status =
519 smartcard_irp_device_control_call(smartcard->callctx, element->irp->output,
520 &element->irp->IoStatus, &element->operation);
521 smartcard_operation_free(&element->operation, TRUE);
522
523 if (status)
524 {
525 WLog_ERR(TAG, "smartcard_irp_device_control_call failed with error %" PRId32 "!",
526 status);
527 return (UINT32)status;
528 }
529
530 if ((error = smartcard_complete_irp(smartcard, irp, handled)))
531 {
532 WLog_ERR(TAG, "Queue_Enqueue failed!");
533 return error;
534 }
535 }
536 else
537 {
538 if (pContext)
539 {
540 wMessageQueue* queue = pContext->IrpQueue;
541
542 switch (element->operation.ioControlCode)
543 {
544 /* may wait for the transaction of another handle */
545 case SCARD_IOCTL_BEGINTRANSACTION:
546 case SCARD_IOCTL_CONNECTA:
547 case SCARD_IOCTL_CONNECTW:
548 case SCARD_IOCTL_RECONNECT:
549 queue = pContext->TxQueue;
550 break;
551 default:
552 break;
553 }
554
555 if (!MessageQueue_Post(queue, nullptr, 0, (void*)element, nullptr))
556 {
557 smartcard_operation_free(&element->operation, TRUE);
558 WLog_ERR(TAG, "MessageQueue_Post failed!");
559 return ERROR_INTERNAL_ERROR;
560 }
561 *handled = TRUE;
562 }
563 }
564 }
565 else
566 {
567 UINT ustatus = 0;
568 WLog_ERR(TAG, "Unexpected SmartCard IRP: MajorFunction %s, MinorFunction: 0x%08" PRIX32 "",
569 rdpdr_irp_string(irp->MajorFunction), irp->MinorFunction);
570 irp->IoStatus = STATUS_NOT_SUPPORTED;
571
572 if ((ustatus = smartcard_complete_irp(smartcard, irp, handled)))
573 {
574 WLog_ERR(TAG, "Queue_Enqueue failed!");
575 return ustatus;
576 }
577 }
578
579 return CHANNEL_RC_OK;
580}
581
582static DWORD WINAPI smartcard_thread_func(LPVOID arg)
583{
584 IRP* irp = nullptr;
585 DWORD nCount = 0;
586 DWORD status = 0;
587 HANDLE hEvents[1] = WINPR_C_ARRAY_INIT;
588 wMessage message = WINPR_C_ARRAY_INIT;
589 UINT error = CHANNEL_RC_OK;
590 SMARTCARD_DEVICE* smartcard = CAST_FROM_DEVICE(arg);
591
592 if (!smartcard)
593 return ERROR_INVALID_PARAMETER;
594
595 hEvents[nCount++] = MessageQueue_Event(smartcard->IrpQueue);
596
597 while (1)
598 {
599 status = WaitForMultipleObjects(nCount, hEvents, FALSE, INFINITE);
600
601 if (status == WAIT_FAILED)
602 {
603 error = GetLastError();
604 WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "!", error);
605 break;
606 }
607
608 if (status == WAIT_OBJECT_0)
609 {
610 if (!MessageQueue_Peek(smartcard->IrpQueue, &message, TRUE))
611 {
612 WLog_ERR(TAG, "MessageQueue_Peek failed!");
613 error = ERROR_INTERNAL_ERROR;
614 break;
615 }
616
617 if (message.id == WMQ_QUIT)
618 break;
619
620 irp = (IRP*)message.wParam;
621
622 if (irp)
623 {
624 BOOL handled = FALSE;
625 if ((error = smartcard_process_irp(smartcard, irp, &handled)))
626 {
627 WLog_ERR(TAG, "smartcard_process_irp failed with error %" PRIu32 "!", error);
628 goto out;
629 }
630 if (!handled)
631 {
632 WINPR_ASSERT(irp->Discard);
633 irp->Discard(irp);
634 }
635 }
636 }
637 }
638
639out:
640
641 if (error && smartcard->rdpcontext)
642 setChannelError(smartcard->rdpcontext, error, "smartcard_thread_func reported an error");
643
644 ExitThread(error);
645 return error;
646}
647
653static UINT smartcard_irp_request(DEVICE* device, IRP* irp)
654{
655 SMARTCARD_DEVICE* smartcard = CAST_FROM_DEVICE(device);
656
657 if (!smartcard)
658 {
659 irp->Discard(irp);
660 return ERROR_INVALID_PARAMETER;
661 }
662
663 if (!MessageQueue_Post(smartcard->IrpQueue, nullptr, 0, (void*)irp, nullptr))
664 {
665 WLog_ERR(TAG, "MessageQueue_Post failed!");
666 irp->Discard(irp);
667 return ERROR_INTERNAL_ERROR;
668 }
669
670 return CHANNEL_RC_OK;
671}
672
673static void smartcard_free_irp(void* obj)
674{
675 wMessage* msg = obj;
676 if (!msg)
677 return;
678 if (msg->id != 0)
679 return;
680
681 IRP* irp = (IRP*)msg->wParam;
682 if (!irp)
683 return;
684 WINPR_ASSERT(irp->Discard);
685 irp->Discard(irp);
686}
687
688/* smartcard is always built-in */
689#define DeviceServiceEntry smartcard_DeviceServiceEntry
690
696FREERDP_ENTRY_POINT(UINT VCAPITYPE DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
697{
698 BOOL registered = FALSE;
699 UINT error = CHANNEL_RC_NO_MEMORY;
700
701 SMARTCARD_DEVICE* smartcard = (SMARTCARD_DEVICE*)calloc(1, sizeof(SMARTCARD_DEVICE));
702 WLog_VRB(TAG, "smartcard device create: %p", (const void*)smartcard);
703 if (!smartcard)
704 {
705 WLog_ERR(TAG, "calloc failed!");
706 return CHANNEL_RC_NO_MEMORY;
707 }
708
709 smartcard->device.type = RDPDR_DTYP_SMARTCARD;
710 smartcard->device.name = "SCARD";
711 smartcard->device.IRPRequest = smartcard_irp_request;
712 smartcard->device.Init = smartcard_init;
713 smartcard->device.Free = smartcard_free;
714 smartcard->rdpcontext = pEntryPoints->rdpcontext;
715 smartcard->IrpQueue = MessageQueue_New(nullptr);
716
717 if (!smartcard->IrpQueue)
718 {
719 WLog_ERR(TAG, "MessageQueue_New failed!");
720 goto fail;
721 }
722
723 wObject* obj = MessageQueue_Object(smartcard->IrpQueue);
724 WINPR_ASSERT(obj);
725 obj->fnObjectFree = smartcard_free_irp;
726
727 smartcard->rgOutstandingMessages = ListDictionary_New(TRUE);
728
729 if (!smartcard->rgOutstandingMessages)
730 {
731 WLog_ERR(TAG, "ListDictionary_New failed!");
732 goto fail;
733 }
734
735 smartcard->callctx = smartcard_call_context_new_with_context(smartcard->rdpcontext);
736 if (!smartcard->callctx)
737 goto fail;
738
739 if (!smarcard_call_set_callbacks(smartcard->callctx, smartcard, smartcard_context_new,
740 smartcard_context_free))
741 goto fail;
742
743 error = pEntryPoints->RegisterDevice(pEntryPoints->devman, &smartcard->device);
744 if (error)
745 {
746 WLog_ERR(TAG, "RegisterDevice failed!");
747 goto fail;
748 }
749
750 registered = TRUE;
751 smartcard->thread =
752 CreateThread(nullptr, 0, smartcard_thread_func, smartcard, CREATE_SUSPENDED, nullptr);
753
754 if (!smartcard->thread)
755 {
756 WLog_ERR(TAG, "CreateThread failed!");
757 error = ERROR_INTERNAL_ERROR;
758 goto fail;
759 }
760
761 ResumeThread(smartcard->thread);
762
763 if (pEntryPoints->device->Name)
764 {
765 if (!smartcard_call_context_add(smartcard->callctx, pEntryPoints->device->Name))
766 goto fail;
767 }
768
769 return CHANNEL_RC_OK;
770fail:
771 if (!registered)
772 smartcard_free_(smartcard);
773 return error;
774}
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59