Coverage Report

Created: 2026-10-15 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/channels/smartcard/client/smartcard_main.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Smartcard Device Service Virtual Channel
4
 *
5
 * Copyright 2011 O.S. Systems Software Ltda.
6
 * Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
7
 * Copyright 2011 Anthony Tong <atong@trustedcs.com>
8
 * Copyright 2015 Thincast Technologies GmbH
9
 * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
10
 * Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.com>
11
 *
12
 * Licensed under the Apache License, Version 2.0 (the "License");
13
 * you may not use this file except in compliance with the License.
14
 * You may obtain a copy of the License at
15
 *
16
 *     http://www.apache.org/licenses/LICENSE-2.0
17
 *
18
 * Unless required by applicable law or agreed to in writing, software
19
 * distributed under the License is distributed on an "AS IS" BASIS,
20
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
 * See the License for the specific language governing permissions and
22
 * limitations under the License.
23
 */
24
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
0
#define CAST_FROM_DEVICE(device) cast_device_from(device, __func__, __FILE__, __LINE__)
41
42
typedef struct
43
{
44
  SMARTCARD_OPERATION operation;
45
  IRP* irp;
46
} scard_irp_queue_element;
47
48
static void smartcard_context_free(void* pCtx);
49
50
static UINT smartcard_complete_irp(SMARTCARD_DEVICE* smartcard, IRP* irp, BOOL* handled);
51
52
static SMARTCARD_DEVICE* cast_device_from(DEVICE* device, const char* fkt, const char* file,
53
                                          size_t line)
54
0
{
55
0
  if (!device)
56
0
  {
57
0
    WLog_ERR(TAG, "%s [%s:%" PRIuz "] Called smartcard channel with nullptr device", fkt, file,
58
0
             line);
59
0
    return nullptr;
60
0
  }
61
62
0
  if (device->type != RDPDR_DTYP_SMARTCARD)
63
0
  {
64
0
    WLog_ERR(TAG,
65
0
             "%s [%s:%" PRIuz "] Called smartcard channel with invalid device of type %" PRIx32,
66
0
             fkt, file, line, device->type);
67
0
    return nullptr;
68
0
  }
69
70
0
  return (SMARTCARD_DEVICE*)device;
71
0
}
72
73
static DWORD smartcard_context_queue_loop(SMARTCARD_CONTEXT* pContext, wMessageQueue* queue)
74
0
{
75
0
  DWORD nCount = 0;
76
0
  DWORD waitStatus = 0;
77
0
  HANDLE hEvents[2] = WINPR_C_ARRAY_INIT;
78
0
  wMessage message = WINPR_C_ARRAY_INIT;
79
0
  SMARTCARD_DEVICE* smartcard = nullptr;
80
0
  UINT error = CHANNEL_RC_OK;
81
0
  smartcard = pContext->smartcard;
82
83
0
  hEvents[nCount++] = MessageQueue_Event(queue);
84
85
0
  while (1)
86
0
  {
87
0
    waitStatus = WaitForMultipleObjects(nCount, hEvents, FALSE, INFINITE);
88
89
0
    if (waitStatus == WAIT_FAILED)
90
0
    {
91
0
      error = GetLastError();
92
0
      WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "!", error);
93
0
      break;
94
0
    }
95
96
0
    waitStatus = WaitForSingleObject(MessageQueue_Event(queue), 0);
97
98
0
    if (waitStatus == WAIT_FAILED)
99
0
    {
100
0
      error = GetLastError();
101
0
      WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
102
0
      break;
103
0
    }
104
105
0
    if (waitStatus == WAIT_OBJECT_0)
106
0
    {
107
0
      scard_irp_queue_element* element = nullptr;
108
109
0
      if (!MessageQueue_Peek(queue, &message, TRUE))
110
0
      {
111
0
        WLog_ERR(TAG, "MessageQueue_Peek failed!");
112
0
        error = ERROR_INTERNAL_ERROR;
113
0
        break;
114
0
      }
115
116
0
      if (message.id == WMQ_QUIT)
117
0
        break;
118
119
0
      element = (scard_irp_queue_element*)message.wParam;
120
121
0
      if (element)
122
0
      {
123
0
        BOOL handled = FALSE;
124
0
        WINPR_ASSERT(smartcard);
125
126
0
        const LONG status =
127
0
            smartcard_irp_device_control_call(smartcard->callctx, element->irp->output,
128
0
                                              &element->irp->IoStatus, &element->operation);
129
0
        if (status)
130
0
        {
131
0
          element->irp->Discard(element->irp);
132
0
          smartcard_operation_free(&element->operation, TRUE);
133
0
          WLog_ERR(TAG,
134
0
                   "smartcard_irp_device_control_call failed with error %s [%" PRId32 "]",
135
0
                   NtStatus2Tag(status), status);
136
0
          error = (UINT)status;
137
0
          break;
138
0
        }
139
140
0
        error = smartcard_complete_irp(smartcard, element->irp, &handled);
141
0
        if (!handled)
142
0
          element->irp->Discard(element->irp);
143
0
        smartcard_operation_free(&element->operation, TRUE);
144
145
0
        if (error)
146
0
        {
147
0
          WLog_ERR(TAG, "smartcard_complete_irp failed with %s [%" PRIu32 "]",
148
0
                   WTSErrorToString(error), error);
149
0
          break;
150
0
        }
151
0
      }
152
0
    }
153
0
  }
154
155
0
  if (error && smartcard->rdpcontext)
156
0
    setChannelError(smartcard->rdpcontext, error, "smartcard_context_thread reported an error");
157
158
0
  return error;
159
0
}
160
161
static DWORD WINAPI smartcard_context_thread(LPVOID arg)
162
0
{
163
0
  SMARTCARD_CONTEXT* pContext = (SMARTCARD_CONTEXT*)arg;
164
0
  const DWORD error = smartcard_context_queue_loop(pContext, pContext->IrpQueue);
165
0
  ExitThread(error);
166
0
  return error;
167
0
}
168
169
static DWORD WINAPI smartcard_context_tx_thread(LPVOID arg)
170
0
{
171
0
  SMARTCARD_CONTEXT* pContext = (SMARTCARD_CONTEXT*)arg;
172
0
  const DWORD error = smartcard_context_queue_loop(pContext, pContext->TxQueue);
173
0
  ExitThread(error);
174
0
  return error;
175
0
}
176
177
static void smartcard_operation_queue_free(void* obj)
178
0
{
179
0
  wMessage* msg = obj;
180
0
  if (!msg)
181
0
    return;
182
0
  if (msg->id != 0)
183
0
    return;
184
185
0
  scard_irp_queue_element* element = (scard_irp_queue_element*)msg->wParam;
186
0
  if (!element)
187
0
    return;
188
0
  WINPR_ASSERT(element->irp);
189
0
  WINPR_ASSERT(element->irp->Discard);
190
0
  element->irp->Discard(element->irp);
191
0
  smartcard_operation_free(&element->operation, TRUE);
192
0
}
193
194
static void* smartcard_context_new(void* smartcard, SCARDCONTEXT hContext)
195
0
{
196
0
  SMARTCARD_CONTEXT* pContext = (SMARTCARD_CONTEXT*)calloc(1, sizeof(SMARTCARD_CONTEXT));
197
198
0
  WLog_VRB(TAG, "smartcard context create %p", (const void*)pContext);
199
0
  if (!pContext)
200
0
  {
201
0
    WLog_ERR(TAG, "calloc failed!");
202
0
    return pContext;
203
0
  }
204
205
0
  pContext->smartcard = smartcard;
206
0
  pContext->hContext = hContext;
207
0
  pContext->IrpQueue = MessageQueue_New(nullptr);
208
209
0
  if (!pContext->IrpQueue)
210
0
  {
211
0
    WLog_ERR(TAG, "MessageQueue_New failed!");
212
0
    goto fail;
213
0
  }
214
215
0
  {
216
0
    wObject* obj = MessageQueue_Object(pContext->IrpQueue);
217
0
    WINPR_ASSERT(obj);
218
0
    obj->fnObjectFree = smartcard_operation_queue_free;
219
0
  }
220
221
0
  pContext->thread = CreateThread(nullptr, 0, smartcard_context_thread, pContext, 0, nullptr);
222
223
0
  if (!pContext->thread)
224
0
  {
225
0
    WLog_ERR(TAG, "CreateThread failed!");
226
0
    goto fail;
227
0
  }
228
229
0
  pContext->TxQueue = MessageQueue_New(nullptr);
230
231
0
  if (!pContext->TxQueue)
232
0
  {
233
0
    WLog_ERR(TAG, "MessageQueue_New failed!");
234
0
    goto fail;
235
0
  }
236
237
0
  {
238
0
    wObject* obj = MessageQueue_Object(pContext->TxQueue);
239
0
    WINPR_ASSERT(obj);
240
0
    obj->fnObjectFree = smartcard_operation_queue_free;
241
0
  }
242
243
0
  pContext->txThread =
244
0
      CreateThread(nullptr, 0, smartcard_context_tx_thread, pContext, 0, nullptr);
245
246
0
  if (!pContext->txThread)
247
0
  {
248
0
    WLog_ERR(TAG, "CreateThread failed!");
249
0
    goto fail;
250
0
  }
251
252
0
  return pContext;
253
0
fail:
254
0
  smartcard_context_free(pContext);
255
0
  return nullptr;
256
0
}
257
258
void smartcard_context_free(void* pCtx)
259
0
{
260
0
  SMARTCARD_CONTEXT* pContext = pCtx;
261
262
0
  WLog_VRB(TAG, "smartcard context destroy %p", pCtx);
263
0
  if (!pContext)
264
0
    return;
265
266
  /* cancel blocking calls like SCardGetStatusChange */
267
0
  WINPR_ASSERT(pContext->smartcard);
268
0
  smartcard_call_cancel_context(pContext->smartcard->callctx, pContext->hContext);
269
270
0
  if (pContext->IrpQueue)
271
0
  {
272
0
    if (MessageQueue_PostQuit(pContext->IrpQueue, 0))
273
0
    {
274
0
      if (WaitForSingleObject(pContext->thread, INFINITE) == WAIT_FAILED)
275
0
        WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", GetLastError());
276
277
0
      (void)CloseHandle(pContext->thread);
278
0
    }
279
0
    MessageQueue_Free(pContext->IrpQueue);
280
0
  }
281
0
  if (pContext->TxQueue)
282
0
  {
283
0
    if (pContext->txThread && MessageQueue_PostQuit(pContext->TxQueue, 0))
284
0
    {
285
0
      if (WaitForSingleObject(pContext->txThread, INFINITE) == WAIT_FAILED)
286
0
        WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", GetLastError());
287
288
0
      (void)CloseHandle(pContext->txThread);
289
0
    }
290
0
    MessageQueue_Free(pContext->TxQueue);
291
0
  }
292
0
  smartcard_call_release_context(pContext->smartcard->callctx, pContext->hContext);
293
0
  free(pContext);
294
0
}
295
296
static UINT smartcard_free_(SMARTCARD_DEVICE* smartcard)
297
0
{
298
0
  if (!smartcard)
299
0
    return CHANNEL_RC_OK;
300
301
0
  if (smartcard->IrpQueue)
302
0
  {
303
0
    MessageQueue_Free(smartcard->IrpQueue);
304
0
    (void)CloseHandle(smartcard->thread);
305
0
  }
306
307
0
  Stream_Free(smartcard->device.data, TRUE);
308
0
  ListDictionary_Free(smartcard->rgOutstandingMessages);
309
310
0
  smartcard_call_context_free(smartcard->callctx);
311
312
0
  free(smartcard);
313
0
  return CHANNEL_RC_OK;
314
0
}
315
/**
316
 * Function description
317
 *
318
 * @return 0 on success, otherwise a Win32 error code
319
 */
320
static UINT smartcard_free(DEVICE* device)
321
0
{
322
0
  SMARTCARD_DEVICE* smartcard = CAST_FROM_DEVICE(device);
323
324
0
  WLog_VRB(TAG, "smartcard device destroy: %p", (const void*)smartcard);
325
0
  if (!smartcard)
326
0
    return ERROR_INVALID_PARAMETER;
327
328
  /**
329
   * Calling smartcard_release_all_contexts to unblock all operations waiting for transactions
330
   * to unlock.
331
   */
332
0
  smartcard_call_cancel_all_context(smartcard->callctx);
333
334
  /* Stopping all threads and cancelling all IRPs */
335
336
0
  if (smartcard->IrpQueue)
337
0
  {
338
0
    if (MessageQueue_PostQuit(smartcard->IrpQueue, 0) &&
339
0
        (WaitForSingleObject(smartcard->thread, INFINITE) == WAIT_FAILED))
340
0
    {
341
0
      DWORD error = GetLastError();
342
0
      WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
343
0
      return error;
344
0
    }
345
0
  }
346
347
0
  return smartcard_free_(smartcard);
348
0
}
349
350
/**
351
 * Initialization occurs when the protocol server sends a device announce message.
352
 * At that time, we need to cancel all outstanding IRPs.
353
 */
354
355
/**
356
 * Function description
357
 *
358
 * @return 0 on success, otherwise a Win32 error code
359
 */
360
static UINT smartcard_init(DEVICE* device)
361
0
{
362
0
  SMARTCARD_DEVICE* smartcard = CAST_FROM_DEVICE(device);
363
364
0
  WLog_VRB(TAG, "smartcard device init %p", (const void*)smartcard);
365
0
  if (!smartcard)
366
0
    return ERROR_INVALID_PARAMETER;
367
368
0
  smartcard_call_cancel_all_context(smartcard->callctx);
369
0
  return CHANNEL_RC_OK;
370
0
}
371
372
/**
373
 * Function description
374
 *
375
 * @return 0 on success, otherwise a Win32 error code
376
 */
377
UINT smartcard_complete_irp(SMARTCARD_DEVICE* smartcard, IRP* irp, BOOL* handled)
378
0
{
379
0
  WINPR_ASSERT(smartcard);
380
0
  WINPR_ASSERT(irp);
381
0
  WINPR_ASSERT(handled);
382
383
0
  uintptr_t key = (uintptr_t)irp->CompletionId + 1;
384
0
  ListDictionary_Remove(smartcard->rgOutstandingMessages, (void*)key);
385
386
0
  WINPR_ASSERT(irp->Complete);
387
0
  *handled = TRUE;
388
0
  return irp->Complete(irp);
389
0
}
390
391
/**
392
 * Multiple threads and SCardGetStatusChange:
393
 * http://musclecard.996296.n3.nabble.com/Multiple-threads-and-SCardGetStatusChange-td4430.html
394
 */
395
396
/**
397
 * Function description
398
 *
399
 * @return 0 on success, otherwise a Win32 error code
400
 */
401
static UINT smartcard_process_irp(SMARTCARD_DEVICE* smartcard, IRP* irp, BOOL* handled)
402
0
{
403
0
  LONG status = 0;
404
0
  BOOL asyncIrp = FALSE;
405
0
  SMARTCARD_CONTEXT* pContext = nullptr;
406
407
0
  WINPR_ASSERT(smartcard);
408
0
  WINPR_ASSERT(handled);
409
0
  WINPR_ASSERT(irp);
410
0
  WINPR_ASSERT(irp->Complete);
411
412
0
  uintptr_t key = (uintptr_t)irp->CompletionId + 1;
413
414
0
  if (!ListDictionary_Add(smartcard->rgOutstandingMessages, (void*)key, irp))
415
0
  {
416
0
    WLog_ERR(TAG, "ListDictionary_Add failed!");
417
0
    return ERROR_INTERNAL_ERROR;
418
0
  }
419
420
0
  if (irp->MajorFunction == IRP_MJ_DEVICE_CONTROL)
421
0
  {
422
0
    scard_irp_queue_element* element = calloc(1, sizeof(scard_irp_queue_element));
423
0
    if (!element)
424
0
      return ERROR_OUTOFMEMORY;
425
426
0
    element->irp = irp;
427
0
    element->operation.completionID = irp->CompletionId;
428
429
0
    status = smartcard_irp_device_control_decode_request(irp->input, irp->CompletionId,
430
0
                                                         irp->FileId, &element->operation);
431
432
0
    if (status != SCARD_S_SUCCESS)
433
0
    {
434
0
      UINT error = 0;
435
436
0
      smartcard_operation_free(&element->operation, TRUE);
437
0
      irp->IoStatus = STATUS_UNSUCCESSFUL;
438
439
0
      if ((error = smartcard_complete_irp(smartcard, irp, handled)))
440
0
      {
441
0
        WLog_ERR(TAG, "Queue_Enqueue failed!");
442
0
        return error;
443
0
      }
444
445
0
      return CHANNEL_RC_OK;
446
0
    }
447
448
0
    asyncIrp = TRUE;
449
450
0
    switch (element->operation.ioControlCode)
451
0
    {
452
0
      case SCARD_IOCTL_ESTABLISHCONTEXT:
453
0
      case SCARD_IOCTL_RELEASECONTEXT:
454
0
      case SCARD_IOCTL_ISVALIDCONTEXT:
455
0
      case SCARD_IOCTL_CANCEL:
456
0
      case SCARD_IOCTL_ACCESSSTARTEDEVENT:
457
0
      case SCARD_IOCTL_RELEASETARTEDEVENT:
458
0
        asyncIrp = FALSE;
459
0
        break;
460
461
0
      case SCARD_IOCTL_LISTREADERGROUPSA:
462
0
      case SCARD_IOCTL_LISTREADERGROUPSW:
463
0
      case SCARD_IOCTL_LISTREADERSA:
464
0
      case SCARD_IOCTL_LISTREADERSW:
465
0
      case SCARD_IOCTL_INTRODUCEREADERGROUPA:
466
0
      case SCARD_IOCTL_INTRODUCEREADERGROUPW:
467
0
      case SCARD_IOCTL_FORGETREADERGROUPA:
468
0
      case SCARD_IOCTL_FORGETREADERGROUPW:
469
0
      case SCARD_IOCTL_INTRODUCEREADERA:
470
0
      case SCARD_IOCTL_INTRODUCEREADERW:
471
0
      case SCARD_IOCTL_FORGETREADERA:
472
0
      case SCARD_IOCTL_FORGETREADERW:
473
0
      case SCARD_IOCTL_ADDREADERTOGROUPA:
474
0
      case SCARD_IOCTL_ADDREADERTOGROUPW:
475
0
      case SCARD_IOCTL_REMOVEREADERFROMGROUPA:
476
0
      case SCARD_IOCTL_REMOVEREADERFROMGROUPW:
477
0
      case SCARD_IOCTL_LOCATECARDSA:
478
0
      case SCARD_IOCTL_LOCATECARDSW:
479
0
      case SCARD_IOCTL_LOCATECARDSBYATRA:
480
0
      case SCARD_IOCTL_LOCATECARDSBYATRW:
481
0
      case SCARD_IOCTL_READCACHEA:
482
0
      case SCARD_IOCTL_READCACHEW:
483
0
      case SCARD_IOCTL_WRITECACHEA:
484
0
      case SCARD_IOCTL_WRITECACHEW:
485
0
      case SCARD_IOCTL_GETREADERICON:
486
0
      case SCARD_IOCTL_GETDEVICETYPEID:
487
0
      case SCARD_IOCTL_GETSTATUSCHANGEA:
488
0
      case SCARD_IOCTL_GETSTATUSCHANGEW:
489
0
      case SCARD_IOCTL_CONNECTA:
490
0
      case SCARD_IOCTL_CONNECTW:
491
0
      case SCARD_IOCTL_RECONNECT:
492
0
      case SCARD_IOCTL_DISCONNECT:
493
0
      case SCARD_IOCTL_BEGINTRANSACTION:
494
0
      case SCARD_IOCTL_ENDTRANSACTION:
495
0
      case SCARD_IOCTL_STATE:
496
0
      case SCARD_IOCTL_STATUSA:
497
0
      case SCARD_IOCTL_STATUSW:
498
0
      case SCARD_IOCTL_TRANSMIT:
499
0
      case SCARD_IOCTL_CONTROL:
500
0
      case SCARD_IOCTL_GETATTRIB:
501
0
      case SCARD_IOCTL_SETATTRIB:
502
0
      case SCARD_IOCTL_GETTRANSMITCOUNT:
503
0
        asyncIrp = TRUE;
504
0
        break;
505
0
      default:
506
0
        break;
507
0
    }
508
509
0
    pContext = smartcard_call_get_context(smartcard->callctx, element->operation.hContext);
510
511
0
    if (!pContext)
512
0
      asyncIrp = FALSE;
513
514
0
    if (!asyncIrp)
515
0
    {
516
0
      UINT error = 0;
517
518
0
      status =
519
0
          smartcard_irp_device_control_call(smartcard->callctx, element->irp->output,
520
0
                                            &element->irp->IoStatus, &element->operation);
521
0
      smartcard_operation_free(&element->operation, TRUE);
522
523
0
      if (status)
524
0
      {
525
0
        WLog_ERR(TAG, "smartcard_irp_device_control_call failed with error %" PRId32 "!",
526
0
                 status);
527
0
        return (UINT32)status;
528
0
      }
529
530
0
      if ((error = smartcard_complete_irp(smartcard, irp, handled)))
531
0
      {
532
0
        WLog_ERR(TAG, "Queue_Enqueue failed!");
533
0
        return error;
534
0
      }
535
0
    }
536
0
    else
537
0
    {
538
0
      if (pContext)
539
0
      {
540
0
        wMessageQueue* queue = pContext->IrpQueue;
541
542
0
        switch (element->operation.ioControlCode)
543
0
        {
544
          /* may wait for the transaction of another handle */
545
0
          case SCARD_IOCTL_BEGINTRANSACTION:
546
0
          case SCARD_IOCTL_CONNECTA:
547
0
          case SCARD_IOCTL_CONNECTW:
548
0
          case SCARD_IOCTL_RECONNECT:
549
0
            queue = pContext->TxQueue;
550
0
            break;
551
0
          default:
552
0
            break;
553
0
        }
554
555
0
        if (!MessageQueue_Post(queue, nullptr, 0, (void*)element, nullptr))
556
0
        {
557
0
          smartcard_operation_free(&element->operation, TRUE);
558
0
          WLog_ERR(TAG, "MessageQueue_Post failed!");
559
0
          return ERROR_INTERNAL_ERROR;
560
0
        }
561
0
        *handled = TRUE;
562
0
      }
563
0
    }
564
0
  }
565
0
  else
566
0
  {
567
0
    UINT ustatus = 0;
568
0
    WLog_ERR(TAG, "Unexpected SmartCard IRP: MajorFunction %s, MinorFunction: 0x%08" PRIX32 "",
569
0
             rdpdr_irp_string(irp->MajorFunction), irp->MinorFunction);
570
0
    irp->IoStatus = STATUS_NOT_SUPPORTED;
571
572
0
    if ((ustatus = smartcard_complete_irp(smartcard, irp, handled)))
573
0
    {
574
0
      WLog_ERR(TAG, "Queue_Enqueue failed!");
575
0
      return ustatus;
576
0
    }
577
0
  }
578
579
0
  return CHANNEL_RC_OK;
580
0
}
581
582
static DWORD WINAPI smartcard_thread_func(LPVOID arg)
583
0
{
584
0
  IRP* irp = nullptr;
585
0
  DWORD nCount = 0;
586
0
  DWORD status = 0;
587
0
  HANDLE hEvents[1] = WINPR_C_ARRAY_INIT;
588
0
  wMessage message = WINPR_C_ARRAY_INIT;
589
0
  UINT error = CHANNEL_RC_OK;
590
0
  SMARTCARD_DEVICE* smartcard = CAST_FROM_DEVICE(arg);
591
592
0
  if (!smartcard)
593
0
    return ERROR_INVALID_PARAMETER;
594
595
0
  hEvents[nCount++] = MessageQueue_Event(smartcard->IrpQueue);
596
597
0
  while (1)
598
0
  {
599
0
    status = WaitForMultipleObjects(nCount, hEvents, FALSE, INFINITE);
600
601
0
    if (status == WAIT_FAILED)
602
0
    {
603
0
      error = GetLastError();
604
0
      WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "!", error);
605
0
      break;
606
0
    }
607
608
0
    if (status == WAIT_OBJECT_0)
609
0
    {
610
0
      if (!MessageQueue_Peek(smartcard->IrpQueue, &message, TRUE))
611
0
      {
612
0
        WLog_ERR(TAG, "MessageQueue_Peek failed!");
613
0
        error = ERROR_INTERNAL_ERROR;
614
0
        break;
615
0
      }
616
617
0
      if (message.id == WMQ_QUIT)
618
0
        break;
619
620
0
      irp = (IRP*)message.wParam;
621
622
0
      if (irp)
623
0
      {
624
0
        BOOL handled = FALSE;
625
0
        if ((error = smartcard_process_irp(smartcard, irp, &handled)))
626
0
        {
627
0
          WLog_ERR(TAG, "smartcard_process_irp failed with error %" PRIu32 "!", error);
628
0
          goto out;
629
0
        }
630
0
        if (!handled)
631
0
        {
632
0
          WINPR_ASSERT(irp->Discard);
633
0
          irp->Discard(irp);
634
0
        }
635
0
      }
636
0
    }
637
0
  }
638
639
0
out:
640
641
0
  if (error && smartcard->rdpcontext)
642
0
    setChannelError(smartcard->rdpcontext, error, "smartcard_thread_func reported an error");
643
644
0
  ExitThread(error);
645
0
  return error;
646
0
}
647
648
/**
649
 * Function description
650
 *
651
 * @return 0 on success, otherwise a Win32 error code
652
 */
653
static UINT smartcard_irp_request(DEVICE* device, IRP* irp)
654
0
{
655
0
  SMARTCARD_DEVICE* smartcard = CAST_FROM_DEVICE(device);
656
657
0
  if (!smartcard)
658
0
  {
659
0
    irp->Discard(irp);
660
0
    return ERROR_INVALID_PARAMETER;
661
0
  }
662
663
0
  if (!MessageQueue_Post(smartcard->IrpQueue, nullptr, 0, (void*)irp, nullptr))
664
0
  {
665
0
    WLog_ERR(TAG, "MessageQueue_Post failed!");
666
0
    irp->Discard(irp);
667
0
    return ERROR_INTERNAL_ERROR;
668
0
  }
669
670
0
  return CHANNEL_RC_OK;
671
0
}
672
673
static void smartcard_free_irp(void* obj)
674
0
{
675
0
  wMessage* msg = obj;
676
0
  if (!msg)
677
0
    return;
678
0
  if (msg->id != 0)
679
0
    return;
680
681
0
  IRP* irp = (IRP*)msg->wParam;
682
0
  if (!irp)
683
0
    return;
684
0
  WINPR_ASSERT(irp->Discard);
685
0
  irp->Discard(irp);
686
0
}
687
688
/* smartcard is always built-in */
689
#define DeviceServiceEntry smartcard_DeviceServiceEntry
690
691
/**
692
 * Function description
693
 *
694
 * @return 0 on success, otherwise a Win32 error code
695
 */
696
FREERDP_ENTRY_POINT(UINT VCAPITYPE DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
697
0
{
698
0
  BOOL registered = FALSE;
699
0
  UINT error = CHANNEL_RC_NO_MEMORY;
700
701
0
  SMARTCARD_DEVICE* smartcard = (SMARTCARD_DEVICE*)calloc(1, sizeof(SMARTCARD_DEVICE));
702
0
  WLog_VRB(TAG, "smartcard device create: %p", (const void*)smartcard);
703
0
  if (!smartcard)
704
0
  {
705
0
    WLog_ERR(TAG, "calloc failed!");
706
0
    return CHANNEL_RC_NO_MEMORY;
707
0
  }
708
709
0
  smartcard->device.type = RDPDR_DTYP_SMARTCARD;
710
0
  smartcard->device.name = "SCARD";
711
0
  smartcard->device.IRPRequest = smartcard_irp_request;
712
0
  smartcard->device.Init = smartcard_init;
713
0
  smartcard->device.Free = smartcard_free;
714
0
  smartcard->rdpcontext = pEntryPoints->rdpcontext;
715
0
  smartcard->IrpQueue = MessageQueue_New(nullptr);
716
717
0
  if (!smartcard->IrpQueue)
718
0
  {
719
0
    WLog_ERR(TAG, "MessageQueue_New failed!");
720
0
    goto fail;
721
0
  }
722
723
0
  wObject* obj = MessageQueue_Object(smartcard->IrpQueue);
724
0
  WINPR_ASSERT(obj);
725
0
  obj->fnObjectFree = smartcard_free_irp;
726
727
0
  smartcard->rgOutstandingMessages = ListDictionary_New(TRUE);
728
729
0
  if (!smartcard->rgOutstandingMessages)
730
0
  {
731
0
    WLog_ERR(TAG, "ListDictionary_New failed!");
732
0
    goto fail;
733
0
  }
734
735
0
  smartcard->callctx = smartcard_call_context_new_with_context(smartcard->rdpcontext);
736
0
  if (!smartcard->callctx)
737
0
    goto fail;
738
739
0
  if (!smarcard_call_set_callbacks(smartcard->callctx, smartcard, smartcard_context_new,
740
0
                                   smartcard_context_free))
741
0
    goto fail;
742
743
0
  error = pEntryPoints->RegisterDevice(pEntryPoints->devman, &smartcard->device);
744
0
  if (error)
745
0
  {
746
0
    WLog_ERR(TAG, "RegisterDevice failed!");
747
0
    goto fail;
748
0
  }
749
750
0
  registered = TRUE;
751
0
  smartcard->thread =
752
0
      CreateThread(nullptr, 0, smartcard_thread_func, smartcard, CREATE_SUSPENDED, nullptr);
753
754
0
  if (!smartcard->thread)
755
0
  {
756
0
    WLog_ERR(TAG, "CreateThread failed!");
757
0
    error = ERROR_INTERNAL_ERROR;
758
0
    goto fail;
759
0
  }
760
761
0
  ResumeThread(smartcard->thread);
762
763
0
  if (pEntryPoints->device->Name)
764
0
  {
765
0
    if (!smartcard_call_context_add(smartcard->callctx, pEntryPoints->device->Name))
766
0
      goto fail;
767
0
  }
768
769
0
  return CHANNEL_RC_OK;
770
0
fail:
771
0
  if (!registered)
772
0
    smartcard_free_(smartcard);
773
0
  return error;
774
0
}