Coverage Report

Created: 2026-10-15 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/utils/smartcard_call.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Smartcard Device Service Virtual Channel
4
 *
5
 * Copyright (C) Alexi Volkov <alexi@myrealbox.com> 2006
6
 * Copyright 2011 O.S. Systems Software Ltda.
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 2017 Armin Novak <armin.novak@thincast.com>
11
 * Copyright 2017 Thincast Technologies GmbH
12
 *
13
 * Licensed under the Apache License, Version 2.0 (the "License");
14
 * you may not use this file except in compliance with the License.
15
 * You may obtain a copy of the License at
16
 *
17
 *     http://www.apache.org/licenses/LICENSE-2.0
18
 *
19
 * Unless required by applicable law or agreed to in writing, software
20
 * distributed under the License is distributed on an "AS IS" BASIS,
21
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22
 * See the License for the specific language governing permissions and
23
 * limitations under the License.
24
 */
25
26
#include <freerdp/config.h>
27
28
#include <winpr/assert.h>
29
30
#include <winpr/crt.h>
31
#include <winpr/print.h>
32
#include <winpr/stream.h>
33
#include <winpr/library.h>
34
#include <winpr/smartcard.h>
35
#include <winpr/synch.h>
36
#include <winpr/sysinfo.h>
37
#include <winpr/thread.h>
38
#include <winpr/collections.h>
39
40
#include <errno.h>
41
#include <stdlib.h>
42
43
#include <freerdp/freerdp.h>
44
#include <freerdp/channels/rdpdr.h>
45
#include <freerdp/channels/scard.h>
46
47
#include <freerdp/utils/rdpdr_utils.h>
48
#include <freerdp/utils/smartcard_pack.h>
49
#include <freerdp/utils/smartcard_call.h>
50
51
#include "smartcard_pack.h"
52
53
#include <freerdp/log.h>
54
0
#define SCARD_TAG FREERDP_TAG("utils.smartcard.call")
55
56
#if defined(WITH_SMARTCARD_EMULATE)
57
#include <freerdp/emulate/scard/smartcard_emulate.h>
58
59
/* The ternary is fully parenthesized: on _WIN32 wrap() below expands straight to wrap_raw(),
60
 * so an unparenthesized ternary would swallow any operator applied to the result. For example
61
 * `wrap(ctx, SCardIsValidContext, h) == SCARD_S_SUCCESS` would parse as
62
 * `useEmulatedCard ? Emulate_...(h) : (pfn...(h) == SCARD_S_SUCCESS)`, i.e. the comparison only
63
 * applies to the non-emulated branch. */
64
#define wrap_raw(ctx, fkt, ...)                                              \
65
0
  ((ctx)->useEmulatedCard ? Emulate_##fkt((ctx)->emulation, ##__VA_ARGS__) \
66
0
                          : (ctx)->pWinSCardApi->pfn##fkt(__VA_ARGS__))
67
0
#define wrap_ptr(ctx, fkt, ...) wrap_raw(ctx, fkt, ##__VA_ARGS__)
68
#else
69
#define wrap_raw(ctx, fkt, ...) \
70
  ((ctx)->useEmulatedCard ? SCARD_F_INTERNAL_ERROR : (ctx)->pWinSCardApi->pfn##fkt(__VA_ARGS__))
71
#define wrap_ptr(ctx, fkt, ...) \
72
  ((ctx)->useEmulatedCard ? nullptr : (ctx)->pWinSCardApi->pfn##fkt(__VA_ARGS__))
73
#endif
74
75
#if defined(_WIN32)
76
#define wrap(ctx, fkt, ...) wrap_raw(ctx, fkt, ##__VA_ARGS__)
77
#else
78
#define wrap(ctx, fkt, ...)                                               \
79
0
  __extension__({                                                       \
80
0
    LONG defstatus = wrap_raw(ctx, fkt, ##__VA_ARGS__);               \
81
0
    if (defstatus != SCARD_S_SUCCESS)                                 \
82
0
      WLog_Print(ctx->log, WLOG_TRACE, "[" #fkt "] failed with %s", \
83
0
                 SCardGetErrorString(defstatus));                   \
84
0
    defstatus;                                                        \
85
0
  })
86
#endif
87
88
struct s_scard_call_context
89
{
90
  BOOL useEmulatedCard;
91
  HANDLE StartedEvent;
92
  wLinkedList* names;
93
  wHashTable* rgSCardContextList;
94
#if defined(WITH_SMARTCARD_EMULATE)
95
  SmartcardEmulationContext* emulation;
96
#endif
97
  HANDLE hWinSCardLibrary;
98
  SCardApiFunctionTable WinSCardApi;
99
  const SCardApiFunctionTable* pWinSCardApi;
100
  HANDLE stopEvent;
101
  void* userdata;
102
103
  void* (*fn_new)(void*, SCARDCONTEXT);
104
  void (*fn_free)(void*);
105
  wLog* log;
106
  rdpContext* context;
107
108
  /* idle transaction watchdog */
109
  wHashTable* handleStates;
110
  HANDLE watchdogStopEvent;
111
  HANDLE watchdogThread;
112
  UINT64 transactionTimeout;
113
};
114
115
struct s_scard_context_element
116
{
117
  void* context;
118
  void (*fn_free)(void*);
119
};
120
121
static void context_free(void* arg);
122
123
/* Windows resets the card when a transaction is idle for 5 s, pcsc-lite does not.
124
 * FREERDP_SCARD_TRANSACTION_TIMEOUT_MS overrides the timeout, 0 disables it */
125
0
#define SCARD_TRANSACTION_IDLE_TIMEOUT_MS 5000
126
0
#define SCARD_TRANSACTION_WATCHDOG_INTERVAL_MS 250
127
#define SCARD_TRANSACTION_WATCHDOG_MAX_EXPIRED 32
128
129
typedef struct
130
{
131
  BOOL inTransaction;
132
  BOOL disconnected;
133
  UINT32 activeCalls;
134
  UINT32 pendingBegins;
135
  UINT32 abandonedBegins;
136
  UINT64 lastActivity;
137
} scard_handle_state;
138
139
typedef struct
140
{
141
  UINT64 now;
142
  UINT64 timeout;
143
  size_t count;
144
  SCARDHANDLE handles[SCARD_TRANSACTION_WATCHDOG_MAX_EXPIRED];
145
} scard_expired_transactions;
146
147
static void smartcard_handle_activity_begin(scard_call_context* ctx, UINT32 ioControlCode,
148
                                            SCARDHANDLE hCard)
149
0
{
150
0
  WINPR_ASSERT(ctx);
151
152
0
  if (!ctx->handleStates || !hCard)
153
0
    return;
154
155
0
  HashTable_Lock(ctx->handleStates);
156
0
  scard_handle_state* state = HashTable_GetItemValue(ctx->handleStates, (void*)hCard);
157
0
  if (!state)
158
0
  {
159
0
    state = calloc(1, sizeof(scard_handle_state));
160
0
    if (state && !HashTable_Insert(ctx->handleStates, (void*)hCard, state))
161
0
    {
162
0
      free(state);
163
0
      state = nullptr;
164
0
    }
165
0
  }
166
0
  if (state)
167
0
  {
168
0
    state->activeCalls++;
169
0
    state->lastActivity = GetTickCount64();
170
171
0
    switch (ioControlCode)
172
0
    {
173
0
      case SCARD_IOCTL_BEGINTRANSACTION:
174
0
        state->pendingBegins++;
175
0
        break;
176
0
      case SCARD_IOCTL_ENDTRANSACTION:
177
        /* the server gave up waiting, release it when granted */
178
0
        if (state->inTransaction)
179
0
          state->inTransaction = FALSE;
180
0
        else if (state->pendingBegins > state->abandonedBegins)
181
0
          state->abandonedBegins++;
182
0
        break;
183
0
      default:
184
0
        break;
185
0
    }
186
0
  }
187
  // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert owns state
188
0
  HashTable_Unlock(ctx->handleStates);
189
0
}
190
191
/* returns TRUE if a transaction the server gave up on was granted */
192
static BOOL smartcard_handle_activity_end(scard_call_context* ctx, UINT32 ioControlCode,
193
                                          SCARDHANDLE hCard, LONG result)
194
0
{
195
0
  BOOL abandoned = FALSE;
196
197
0
  WINPR_ASSERT(ctx);
198
199
0
  if (!ctx->handleStates || !hCard)
200
0
    return FALSE;
201
202
0
  HashTable_Lock(ctx->handleStates);
203
0
  scard_handle_state* state = HashTable_GetItemValue(ctx->handleStates, (void*)hCard);
204
0
  if (state)
205
0
  {
206
0
    if (state->activeCalls > 0)
207
0
      state->activeCalls--;
208
0
    state->lastActivity = GetTickCount64();
209
210
0
    switch (ioControlCode)
211
0
    {
212
0
      case SCARD_IOCTL_BEGINTRANSACTION:
213
0
        if (state->pendingBegins > 0)
214
0
          state->pendingBegins--;
215
0
        if (state->abandonedBegins > 0)
216
0
        {
217
0
          state->abandonedBegins--;
218
0
          abandoned = (result == SCARD_S_SUCCESS);
219
0
        }
220
0
        else if (result == SCARD_S_SUCCESS)
221
0
          state->inTransaction = TRUE;
222
0
        break;
223
0
      case SCARD_IOCTL_DISCONNECT:
224
0
        if (result == SCARD_S_SUCCESS)
225
0
        {
226
0
          state->inTransaction = FALSE;
227
0
          state->disconnected = TRUE;
228
0
        }
229
0
        break;
230
0
      default:
231
0
        break;
232
0
    }
233
234
0
    if (state->disconnected && (state->activeCalls == 0))
235
0
      HashTable_Remove(ctx->handleStates, (void*)hCard);
236
0
  }
237
0
  HashTable_Unlock(ctx->handleStates);
238
0
  return abandoned;
239
0
}
240
241
static BOOL smartcard_collect_expired_transaction(const void* key, void* value, void* arg)
242
0
{
243
0
  scard_handle_state* state = value;
244
0
  scard_expired_transactions* expired = arg;
245
246
0
  WINPR_ASSERT(state);
247
0
  WINPR_ASSERT(expired);
248
249
0
  if (!state->inTransaction || (state->activeCalls > 0))
250
0
    return TRUE;
251
0
  if (expired->now - state->lastActivity < expired->timeout)
252
0
    return TRUE;
253
0
  if (expired->count >= ARRAYSIZE(expired->handles))
254
0
    return FALSE; /* rest on the next round */
255
256
0
  state->inTransaction = FALSE;
257
0
  expired->handles[expired->count++] = (SCARDHANDLE)key;
258
0
  return TRUE;
259
0
}
260
261
static DWORD WINAPI smartcard_transaction_watchdog(LPVOID arg)
262
0
{
263
0
  scard_call_context* ctx = arg;
264
265
0
  WINPR_ASSERT(ctx);
266
267
0
  while (WaitForSingleObject(ctx->watchdogStopEvent, SCARD_TRANSACTION_WATCHDOG_INTERVAL_MS) ==
268
0
         WAIT_TIMEOUT)
269
0
  {
270
0
    scard_expired_transactions expired = WINPR_C_ARRAY_INIT;
271
0
    expired.now = GetTickCount64();
272
0
    expired.timeout = ctx->transactionTimeout;
273
274
0
    if (!HashTable_Foreach(ctx->handleStates, smartcard_collect_expired_transaction, &expired))
275
0
      WLog_Print(ctx->log, WLOG_DEBUG,
276
0
                 "more idle transactions than handled in one round, continuing next round");
277
278
    /* not locked, SCardEndTransaction may block */
279
0
    for (size_t x = 0; x < expired.count; x++)
280
0
    {
281
0
      const SCARDHANDLE hCard = expired.handles[x];
282
283
0
      WLog_Print(ctx->log, WLOG_WARN,
284
0
                 "transaction on hCard 0x%08" PRIxz " idle for more than %" PRIu64
285
0
                 " ms, ending it and resetting the card like Windows does",
286
0
                 (size_t)hCard, ctx->transactionTimeout);
287
288
0
      const LONG rc = wrap(ctx, SCardEndTransaction, hCard, SCARD_RESET_CARD);
289
0
      if (rc == SCARD_E_INVALID_HANDLE)
290
0
        HashTable_Remove(ctx->handleStates, (void*)hCard);
291
0
    }
292
0
  }
293
294
0
  ExitThread(0);
295
0
  return 0;
296
0
}
297
298
static UINT64 smartcard_transaction_timeout(void)
299
0
{
300
  // NOLINTNEXTLINE(concurrency-mt-unsafe)
301
0
  const char* value = getenv("FREERDP_SCARD_TRANSACTION_TIMEOUT_MS");
302
0
  if (!value || !*value)
303
0
    return SCARD_TRANSACTION_IDLE_TIMEOUT_MS;
304
305
0
  char* end = nullptr;
306
0
  errno = 0;
307
0
  const unsigned long long timeout = strtoull(value, &end, 10);
308
0
  if ((errno != 0) || !end || (*end != '\0'))
309
0
    return SCARD_TRANSACTION_IDLE_TIMEOUT_MS;
310
0
  return timeout;
311
0
}
312
313
static BOOL smartcard_transaction_watchdog_start(scard_call_context* ctx)
314
0
{
315
0
  WINPR_ASSERT(ctx);
316
317
0
  if (ctx->useEmulatedCard)
318
0
    return TRUE;
319
320
0
  ctx->handleStates = HashTable_New(TRUE);
321
0
  if (!ctx->handleStates)
322
0
    return FALSE;
323
324
0
  {
325
0
    wObject* obj = HashTable_ValueObject(ctx->handleStates);
326
0
    WINPR_ASSERT(obj);
327
0
    obj->fnObjectFree = free;
328
0
  }
329
330
0
  ctx->transactionTimeout = smartcard_transaction_timeout();
331
0
  if (ctx->transactionTimeout == 0)
332
0
  {
333
0
    WLog_Print(ctx->log, WLOG_DEBUG, "idle transaction watchdog disabled");
334
0
    return TRUE;
335
0
  }
336
337
0
  ctx->watchdogStopEvent = CreateEventA(nullptr, TRUE, FALSE, nullptr);
338
0
  if (!ctx->watchdogStopEvent)
339
0
    return FALSE;
340
341
0
  ctx->watchdogThread = CreateThread(nullptr, 0, smartcard_transaction_watchdog, ctx, 0, nullptr);
342
0
  if (!ctx->watchdogThread)
343
0
    return FALSE;
344
345
0
  WLog_Print(ctx->log, WLOG_DEBUG, "idle transaction watchdog started, timeout %" PRIu64 " ms",
346
0
             ctx->transactionTimeout);
347
0
  return TRUE;
348
0
}
349
350
static void smartcard_transaction_watchdog_stop(scard_call_context* ctx)
351
0
{
352
0
  WINPR_ASSERT(ctx);
353
354
0
  if (ctx->watchdogThread)
355
0
  {
356
0
    (void)SetEvent(ctx->watchdogStopEvent);
357
0
    (void)WaitForSingleObject(ctx->watchdogThread, INFINITE);
358
0
    (void)CloseHandle(ctx->watchdogThread);
359
0
    ctx->watchdogThread = nullptr;
360
0
  }
361
0
  if (ctx->watchdogStopEvent)
362
0
  {
363
0
    (void)CloseHandle(ctx->watchdogStopEvent);
364
0
    ctx->watchdogStopEvent = nullptr;
365
0
  }
366
0
}
367
368
WINPR_ATTR_NODISCARD WINPR_ATTR_NODISCARD static LONG
369
smartcard_EstablishContext_Call(scard_call_context* smartcard, wStream* out,
370
                                SMARTCARD_OPERATION* operation)
371
0
{
372
0
  SCARDCONTEXT hContext = WINPR_C_ARRAY_INIT;
373
0
  EstablishContext_Return ret = WINPR_C_ARRAY_INIT;
374
0
  EstablishContext_Call* call = &operation->call.establishContext;
375
0
  LONG status = ret.ReturnCode =
376
0
      wrap(smartcard, SCardEstablishContext, call->dwScope, nullptr, nullptr, &hContext);
377
378
0
  if (ret.ReturnCode == SCARD_S_SUCCESS)
379
0
  {
380
0
    const void* key = (void*)(size_t)hContext;
381
0
    struct s_scard_context_element* pContext =
382
0
        calloc(1, sizeof(struct s_scard_context_element));
383
0
    if (!pContext)
384
0
      return STATUS_NO_MEMORY;
385
386
0
    pContext->fn_free = smartcard->fn_free;
387
388
0
    if (smartcard->fn_new)
389
0
    {
390
0
      pContext->context = smartcard->fn_new(smartcard->userdata, hContext);
391
0
      if (!pContext->context)
392
0
      {
393
0
        free(pContext);
394
0
        return STATUS_NO_MEMORY;
395
0
      }
396
0
    }
397
398
0
    if (!HashTable_Insert(smartcard->rgSCardContextList, key, (void*)pContext))
399
0
    {
400
0
      WLog_Print(smartcard->log, WLOG_ERROR, "HashTable_Insert failed!");
401
0
      context_free(pContext);
402
0
      return STATUS_INTERNAL_ERROR;
403
0
    }
404
0
  }
405
0
  else
406
0
  {
407
0
    return scard_log_status_error_wlog(smartcard->log, "SCardEstablishContext", status);
408
0
  }
409
410
  // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert takes ownership of pContext
411
0
  smartcard_scard_context_native_to_redir(&(ret.hContext), hContext);
412
413
0
  status = smartcard_pack_establish_context_return(out, &ret);
414
0
  if (status != SCARD_S_SUCCESS)
415
0
  {
416
0
    return scard_log_status_error_wlog(smartcard->log,
417
0
                                       "smartcard_pack_establish_context_return", status);
418
0
  }
419
420
0
  return ret.ReturnCode;
421
0
}
422
423
WINPR_ATTR_NODISCARD static LONG smartcard_ReleaseContext_Call(scard_call_context* smartcard,
424
                                                               WINPR_ATTR_UNUSED wStream* out,
425
                                                               SMARTCARD_OPERATION* operation)
426
0
{
427
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
428
429
0
  WINPR_ASSERT(smartcard);
430
0
  WINPR_ASSERT(out);
431
0
  WINPR_ASSERT(operation);
432
433
0
  ret.ReturnCode = wrap(smartcard, SCardReleaseContext, operation->hContext);
434
435
0
  if (ret.ReturnCode == SCARD_S_SUCCESS)
436
0
    HashTable_Remove(smartcard->rgSCardContextList, (void*)operation->hContext);
437
0
  else
438
0
  {
439
0
    return scard_log_status_error_wlog(smartcard->log, "SCardReleaseContext", ret.ReturnCode);
440
0
  }
441
442
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "ReleaseContext");
443
0
  return ret.ReturnCode;
444
0
}
445
446
WINPR_ATTR_NODISCARD static LONG smartcard_IsValidContext_Call(scard_call_context* smartcard,
447
                                                               WINPR_ATTR_UNUSED wStream* out,
448
                                                               SMARTCARD_OPERATION* operation)
449
0
{
450
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
451
452
0
  WINPR_ASSERT(smartcard);
453
0
  WINPR_ASSERT(out);
454
0
  WINPR_ASSERT(operation);
455
456
0
  ret.ReturnCode = wrap(smartcard, SCardIsValidContext, operation->hContext);
457
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "IsValidContext");
458
0
  return ret.ReturnCode;
459
0
}
460
461
WINPR_ATTR_NODISCARD static LONG smartcard_ListReaderGroupsA_Call(scard_call_context* smartcard,
462
                                                                  wStream* out,
463
                                                                  SMARTCARD_OPERATION* operation)
464
0
{
465
0
  LONG status = 0;
466
0
  ListReaderGroups_Return ret = WINPR_C_ARRAY_INIT;
467
0
  LPSTR mszGroups = nullptr;
468
0
  DWORD cchGroups = 0;
469
470
0
  WINPR_ASSERT(smartcard);
471
0
  WINPR_ASSERT(out);
472
0
  WINPR_ASSERT(operation);
473
474
0
  cchGroups = SCARD_AUTOALLOCATE;
475
0
  ret.ReturnCode =
476
0
      wrap(smartcard, SCardListReaderGroupsA, operation->hContext, (LPSTR)&mszGroups, &cchGroups);
477
0
  if ((ret.ReturnCode == SCARD_S_SUCCESS) && (cchGroups == SCARD_AUTOALLOCATE))
478
0
    return SCARD_F_UNKNOWN_ERROR;
479
480
0
  ret.msz = (BYTE*)mszGroups;
481
0
  ret.cBytes = cchGroups;
482
483
0
  status = smartcard_pack_list_reader_groups_return(out, &ret, FALSE);
484
485
0
  if (status != SCARD_S_SUCCESS)
486
0
    return status;
487
488
0
  if (mszGroups)
489
0
    wrap(smartcard, SCardFreeMemory, operation->hContext, mszGroups);
490
491
0
  return ret.ReturnCode;
492
0
}
493
494
WINPR_ATTR_NODISCARD static LONG smartcard_ListReaderGroupsW_Call(scard_call_context* smartcard,
495
                                                                  wStream* out,
496
                                                                  SMARTCARD_OPERATION* operation)
497
0
{
498
0
  LONG status = 0;
499
0
  ListReaderGroups_Return ret = WINPR_C_ARRAY_INIT;
500
0
  LPWSTR mszGroups = nullptr;
501
0
  DWORD cchGroups = 0;
502
503
0
  WINPR_ASSERT(smartcard);
504
0
  WINPR_ASSERT(out);
505
0
  WINPR_ASSERT(operation);
506
507
0
  cchGroups = SCARD_AUTOALLOCATE;
508
0
  status = ret.ReturnCode = wrap(smartcard, SCardListReaderGroupsW, operation->hContext,
509
0
                                 (LPWSTR)&mszGroups, &cchGroups);
510
0
  if ((ret.ReturnCode == SCARD_S_SUCCESS) && (cchGroups == SCARD_AUTOALLOCATE))
511
0
    return SCARD_F_UNKNOWN_ERROR;
512
513
0
  ret.msz = (BYTE*)mszGroups;
514
515
0
  WINPR_ASSERT(cchGroups < SCARD_AUTOALLOCATE / sizeof(WCHAR));
516
0
  const size_t blen = sizeof(WCHAR) * cchGroups;
517
0
  WINPR_ASSERT(blen <= UINT32_MAX);
518
0
  ret.cBytes = (UINT32)blen;
519
520
0
  if (status != SCARD_S_SUCCESS)
521
0
    return status;
522
523
0
  status = smartcard_pack_list_reader_groups_return(out, &ret, TRUE);
524
525
0
  if (status != SCARD_S_SUCCESS)
526
0
    return status;
527
528
0
  if (mszGroups)
529
0
    wrap(smartcard, SCardFreeMemory, operation->hContext, mszGroups);
530
531
0
  return ret.ReturnCode;
532
0
}
533
534
WINPR_ATTR_NODISCARD
535
static BOOL filter_match(wLinkedList* list, LPCSTR reader, size_t readerLen)
536
0
{
537
0
  if (readerLen < 1)
538
0
    return FALSE;
539
540
0
  LinkedList_Enumerator_Reset(list);
541
542
0
  while (LinkedList_Enumerator_MoveNext(list))
543
0
  {
544
0
    const char* filter = LinkedList_Enumerator_Current(list);
545
546
0
    if (filter)
547
0
    {
548
0
      if (strstr(reader, filter) != nullptr)
549
0
        return TRUE;
550
0
    }
551
0
  }
552
553
0
  return FALSE;
554
0
}
555
556
WINPR_ATTR_NODISCARD
557
static DWORD filter_device_by_name_a(wLinkedList* list, LPSTR* mszReaders, DWORD cchReaders)
558
0
{
559
0
  size_t rpos = 0;
560
0
  size_t wpos = 0;
561
562
0
  if (*mszReaders == nullptr || LinkedList_Count(list) < 1)
563
0
    return cchReaders;
564
565
0
  do
566
0
  {
567
0
    LPCSTR rreader = &(*mszReaders)[rpos];
568
0
    LPSTR wreader = &(*mszReaders)[wpos];
569
0
    size_t readerLen = strnlen(rreader, cchReaders - rpos);
570
571
0
    rpos += readerLen + 1;
572
573
0
    if (filter_match(list, rreader, readerLen))
574
0
    {
575
0
      if (rreader != wreader)
576
0
        memmove(wreader, rreader, readerLen + 1);
577
578
0
      wpos += readerLen + 1;
579
0
    }
580
0
  } while (rpos < cchReaders);
581
582
  /* this string must be double 0 terminated */
583
0
  if (rpos != wpos)
584
0
  {
585
0
    if (wpos >= cchReaders)
586
0
      return 0;
587
588
0
    (*mszReaders)[wpos++] = '\0';
589
0
  }
590
591
0
  return (DWORD)wpos;
592
0
}
593
594
WINPR_ATTR_NODISCARD
595
static DWORD filter_device_by_name_w(wLinkedList* list, LPWSTR* mszReaders, DWORD cchReaders)
596
0
{
597
0
  DWORD rc = 0;
598
0
  LPSTR readers = nullptr;
599
600
0
  if (LinkedList_Count(list) < 1)
601
0
    return cchReaders;
602
603
0
  readers = ConvertMszWCharNToUtf8Alloc(*mszReaders, cchReaders, nullptr);
604
605
0
  if (!readers)
606
0
  {
607
0
    free(readers);
608
0
    return 0;
609
0
  }
610
611
0
  *mszReaders = nullptr;
612
0
  rc = filter_device_by_name_a(list, &readers, cchReaders);
613
614
0
  *mszReaders = ConvertMszUtf8NToWCharAlloc(readers, rc, nullptr);
615
0
  if (!*mszReaders)
616
0
    rc = 0;
617
618
0
  free(readers);
619
0
  return rc;
620
0
}
621
622
WINPR_ATTR_NODISCARD static LONG smartcard_ListReadersA_Call(scard_call_context* smartcard,
623
                                                             wStream* out,
624
                                                             SMARTCARD_OPERATION* operation)
625
0
{
626
0
  ListReaders_Return ret = WINPR_C_ARRAY_INIT;
627
0
  LPSTR mszReaders = nullptr;
628
629
0
  WINPR_ASSERT(smartcard);
630
0
  WINPR_ASSERT(out);
631
0
  WINPR_ASSERT(operation);
632
633
0
  ListReaders_Call* call = &operation->call.listReaders;
634
0
  DWORD cchReaders = SCARD_AUTOALLOCATE;
635
0
  LONG status = ret.ReturnCode = wrap(smartcard, SCardListReadersA, operation->hContext,
636
0
                                      (LPCSTR)call->mszGroups, (LPSTR)&mszReaders, &cchReaders);
637
0
  if (status == SCARD_S_SUCCESS)
638
0
  {
639
0
    if (cchReaders == SCARD_AUTOALLOCATE)
640
0
      status = SCARD_F_UNKNOWN_ERROR;
641
0
  }
642
643
0
  if (status != SCARD_S_SUCCESS)
644
0
  {
645
0
    (void)scard_log_status_error_wlog(smartcard->log, "SCardListReadersA", status);
646
0
    return smartcard_pack_list_readers_return(out, &ret, FALSE);
647
0
  }
648
649
0
  void* original = mszReaders;
650
0
  cchReaders = filter_device_by_name_a(smartcard->names, &mszReaders, cchReaders);
651
0
  ret.msz = (BYTE*)mszReaders;
652
0
  ret.cBytes = cchReaders;
653
654
0
  status = smartcard_pack_list_readers_return(out, &ret, FALSE);
655
0
  if (original)
656
0
    wrap(smartcard, SCardFreeMemory, operation->hContext, original);
657
658
0
  if (status != SCARD_S_SUCCESS)
659
0
    return scard_log_status_error_wlog(smartcard->log, "smartcard_pack_list_readers_return",
660
0
                                       status);
661
662
0
  return ret.ReturnCode;
663
0
}
664
665
WINPR_ATTR_NODISCARD static LONG smartcard_ListReadersW_Call(scard_call_context* smartcard,
666
                                                             wStream* out,
667
                                                             SMARTCARD_OPERATION* operation)
668
0
{
669
0
  LONG status = 0;
670
0
  ListReaders_Return ret = WINPR_C_ARRAY_INIT;
671
0
  DWORD cchReaders = 0;
672
0
  ListReaders_Call* call = nullptr;
673
0
  union
674
0
  {
675
0
    const BYTE* bp;
676
0
    const char* sz;
677
0
    const WCHAR* wz;
678
0
  } string;
679
0
  union
680
0
  {
681
0
    WCHAR** ppw;
682
0
    WCHAR* pw;
683
0
    CHAR* pc;
684
0
    BYTE* pb;
685
0
  } mszReaders;
686
687
0
  WINPR_ASSERT(smartcard);
688
0
  WINPR_ASSERT(operation);
689
690
0
  call = &operation->call.listReaders;
691
692
0
  string.bp = call->mszGroups;
693
0
  cchReaders = SCARD_AUTOALLOCATE;
694
0
  status = ret.ReturnCode = wrap(smartcard, SCardListReadersW, operation->hContext, string.wz,
695
0
                                 (LPWSTR)&mszReaders.pw, &cchReaders);
696
0
  if (status == SCARD_S_SUCCESS)
697
0
  {
698
0
    if (cchReaders == SCARD_AUTOALLOCATE)
699
0
      status = SCARD_F_UNKNOWN_ERROR;
700
0
  }
701
702
0
  if (status != SCARD_S_SUCCESS)
703
0
  {
704
0
    (void)scard_log_status_error_wlog(smartcard->log, "SCardListReadersW", status);
705
0
    return smartcard_pack_list_readers_return(out, &ret, TRUE);
706
0
  }
707
708
0
  void* original = mszReaders.pb;
709
0
  cchReaders = filter_device_by_name_w(smartcard->names, &mszReaders.pw, cchReaders);
710
0
  ret.msz = mszReaders.pb;
711
0
  ret.cBytes = cchReaders * sizeof(WCHAR);
712
0
  status = smartcard_pack_list_readers_return(out, &ret, TRUE);
713
714
0
  if (original)
715
0
    wrap(smartcard, SCardFreeMemory, operation->hContext, original);
716
717
0
  if (status != SCARD_S_SUCCESS)
718
0
    return status;
719
720
0
  return ret.ReturnCode;
721
0
}
722
723
WINPR_ATTR_NODISCARD static LONG
724
smartcard_IntroduceReaderGroupA_Call(scard_call_context* smartcard, WINPR_ATTR_UNUSED wStream* out,
725
                                     SMARTCARD_OPERATION* operation)
726
0
{
727
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
728
0
  ContextAndStringA_Call* call = nullptr;
729
730
0
  WINPR_ASSERT(smartcard);
731
0
  WINPR_ASSERT(out);
732
0
  WINPR_ASSERT(operation);
733
734
0
  call = &operation->call.contextAndStringA;
735
0
  ret.ReturnCode = wrap(smartcard, SCardIntroduceReaderGroupA, operation->hContext, call->sz);
736
0
  scard_log_status_error_wlog(smartcard->log, "SCardIntroduceReaderGroupA", ret.ReturnCode);
737
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "IntroduceReaderGroupA");
738
0
  return ret.ReturnCode;
739
0
}
740
741
WINPR_ATTR_NODISCARD static LONG
742
smartcard_IntroduceReaderGroupW_Call(scard_call_context* smartcard, WINPR_ATTR_UNUSED wStream* out,
743
                                     SMARTCARD_OPERATION* operation)
744
0
{
745
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
746
0
  ContextAndStringW_Call* call = nullptr;
747
748
0
  WINPR_ASSERT(smartcard);
749
0
  WINPR_ASSERT(out);
750
0
  WINPR_ASSERT(operation);
751
752
0
  call = &operation->call.contextAndStringW;
753
0
  ret.ReturnCode = wrap(smartcard, SCardIntroduceReaderGroupW, operation->hContext, call->sz);
754
0
  scard_log_status_error_wlog(smartcard->log, "SCardIntroduceReaderGroupW", ret.ReturnCode);
755
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "IntroduceReaderGroupW");
756
0
  return ret.ReturnCode;
757
0
}
758
759
WINPR_ATTR_NODISCARD static LONG smartcard_IntroduceReaderA_Call(scard_call_context* smartcard,
760
                                                                 WINPR_ATTR_UNUSED wStream* out,
761
                                                                 SMARTCARD_OPERATION* operation)
762
0
{
763
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
764
0
  ContextAndTwoStringA_Call* call = nullptr;
765
766
0
  WINPR_ASSERT(smartcard);
767
0
  WINPR_ASSERT(out);
768
0
  WINPR_ASSERT(operation);
769
770
0
  call = &operation->call.contextAndTwoStringA;
771
0
  ret.ReturnCode =
772
0
      wrap(smartcard, SCardIntroduceReaderA, operation->hContext, call->sz1, call->sz2);
773
0
  scard_log_status_error_wlog(smartcard->log, "SCardIntroduceReaderA", ret.ReturnCode);
774
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "IntroduceReaderA");
775
0
  return ret.ReturnCode;
776
0
}
777
778
WINPR_ATTR_NODISCARD static LONG smartcard_IntroduceReaderW_Call(scard_call_context* smartcard,
779
                                                                 WINPR_ATTR_UNUSED wStream* out,
780
                                                                 SMARTCARD_OPERATION* operation)
781
0
{
782
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
783
0
  ContextAndTwoStringW_Call* call = nullptr;
784
785
0
  WINPR_ASSERT(smartcard);
786
0
  WINPR_ASSERT(out);
787
0
  WINPR_ASSERT(operation);
788
789
0
  call = &operation->call.contextAndTwoStringW;
790
0
  ret.ReturnCode =
791
0
      wrap(smartcard, SCardIntroduceReaderW, operation->hContext, call->sz1, call->sz2);
792
0
  scard_log_status_error_wlog(smartcard->log, "SCardIntroduceReaderW", ret.ReturnCode);
793
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "IntroduceReaderW");
794
0
  return ret.ReturnCode;
795
0
}
796
797
WINPR_ATTR_NODISCARD static LONG smartcard_ForgetReaderA_Call(scard_call_context* smartcard,
798
                                                              WINPR_ATTR_UNUSED wStream* out,
799
                                                              SMARTCARD_OPERATION* operation)
800
0
{
801
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
802
0
  ContextAndStringA_Call* call = nullptr;
803
804
0
  WINPR_ASSERT(smartcard);
805
0
  WINPR_ASSERT(out);
806
0
  WINPR_ASSERT(operation);
807
808
0
  call = &operation->call.contextAndStringA;
809
0
  ret.ReturnCode = wrap(smartcard, SCardForgetReaderA, operation->hContext, call->sz);
810
0
  scard_log_status_error_wlog(smartcard->log, "SCardForgetReaderA", ret.ReturnCode);
811
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "SCardForgetReaderA");
812
0
  return ret.ReturnCode;
813
0
}
814
815
WINPR_ATTR_NODISCARD static LONG smartcard_ForgetReaderW_Call(scard_call_context* smartcard,
816
                                                              WINPR_ATTR_UNUSED wStream* out,
817
                                                              SMARTCARD_OPERATION* operation)
818
0
{
819
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
820
0
  ContextAndStringW_Call* call = nullptr;
821
822
0
  WINPR_ASSERT(smartcard);
823
0
  WINPR_ASSERT(out);
824
0
  WINPR_ASSERT(operation);
825
826
0
  call = &operation->call.contextAndStringW;
827
0
  ret.ReturnCode = wrap(smartcard, SCardForgetReaderW, operation->hContext, call->sz);
828
0
  scard_log_status_error_wlog(smartcard->log, "SCardForgetReaderW", ret.ReturnCode);
829
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "SCardForgetReaderW");
830
0
  return ret.ReturnCode;
831
0
}
832
833
WINPR_ATTR_NODISCARD static LONG smartcard_AddReaderToGroupA_Call(scard_call_context* smartcard,
834
                                                                  WINPR_ATTR_UNUSED wStream* out,
835
                                                                  SMARTCARD_OPERATION* operation)
836
0
{
837
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
838
0
  ContextAndTwoStringA_Call* call = nullptr;
839
840
0
  WINPR_ASSERT(smartcard);
841
0
  WINPR_ASSERT(out);
842
0
  WINPR_ASSERT(operation);
843
844
0
  call = &operation->call.contextAndTwoStringA;
845
0
  ret.ReturnCode =
846
0
      wrap(smartcard, SCardAddReaderToGroupA, operation->hContext, call->sz1, call->sz2);
847
0
  scard_log_status_error_wlog(smartcard->log, "SCardAddReaderToGroupA", ret.ReturnCode);
848
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "SCardAddReaderToGroupA");
849
0
  return ret.ReturnCode;
850
0
}
851
852
WINPR_ATTR_NODISCARD static LONG smartcard_AddReaderToGroupW_Call(scard_call_context* smartcard,
853
                                                                  WINPR_ATTR_UNUSED wStream* out,
854
                                                                  SMARTCARD_OPERATION* operation)
855
0
{
856
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
857
0
  ContextAndTwoStringW_Call* call = nullptr;
858
859
0
  WINPR_ASSERT(smartcard);
860
0
  WINPR_ASSERT(out);
861
0
  WINPR_ASSERT(operation);
862
863
0
  call = &operation->call.contextAndTwoStringW;
864
0
  ret.ReturnCode =
865
0
      wrap(smartcard, SCardAddReaderToGroupW, operation->hContext, call->sz1, call->sz2);
866
0
  scard_log_status_error_wlog(smartcard->log, "SCardAddReaderToGroupW", ret.ReturnCode);
867
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "SCardAddReaderToGroupA");
868
0
  return ret.ReturnCode;
869
0
}
870
871
WINPR_ATTR_NODISCARD static LONG
872
smartcard_RemoveReaderFromGroupA_Call(scard_call_context* smartcard, WINPR_ATTR_UNUSED wStream* out,
873
                                      SMARTCARD_OPERATION* operation)
874
0
{
875
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
876
0
  ContextAndTwoStringA_Call* call = nullptr;
877
878
0
  WINPR_ASSERT(smartcard);
879
0
  WINPR_ASSERT(out);
880
0
  WINPR_ASSERT(operation);
881
882
0
  call = &operation->call.contextAndTwoStringA;
883
0
  ret.ReturnCode =
884
0
      wrap(smartcard, SCardRemoveReaderFromGroupA, operation->hContext, call->sz1, call->sz2);
885
0
  scard_log_status_error_wlog(smartcard->log, "SCardRemoveReaderFromGroupA", ret.ReturnCode);
886
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "SCardRemoveReaderFromGroupA");
887
0
  return ret.ReturnCode;
888
0
}
889
890
WINPR_ATTR_NODISCARD static LONG
891
smartcard_RemoveReaderFromGroupW_Call(scard_call_context* smartcard, WINPR_ATTR_UNUSED wStream* out,
892
                                      SMARTCARD_OPERATION* operation)
893
0
{
894
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
895
0
  ContextAndTwoStringW_Call* call = nullptr;
896
897
0
  WINPR_ASSERT(smartcard);
898
0
  WINPR_ASSERT(out);
899
0
  WINPR_ASSERT(operation);
900
901
0
  call = &operation->call.contextAndTwoStringW;
902
0
  ret.ReturnCode =
903
0
      wrap(smartcard, SCardRemoveReaderFromGroupW, operation->hContext, call->sz1, call->sz2);
904
0
  scard_log_status_error_wlog(smartcard->log, "SCardRemoveReaderFromGroupW", ret.ReturnCode);
905
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "SCardRemoveReaderFromGroupW");
906
0
  return ret.ReturnCode;
907
0
}
908
909
WINPR_ATTR_NODISCARD static LONG smartcard_LocateCardsA_Call(scard_call_context* smartcard,
910
                                                             wStream* out,
911
                                                             SMARTCARD_OPERATION* operation)
912
0
{
913
0
  LONG status = 0;
914
0
  LocateCards_Return ret = WINPR_C_ARRAY_INIT;
915
0
  LocateCardsA_Call* call = nullptr;
916
917
0
  WINPR_ASSERT(smartcard);
918
0
  WINPR_ASSERT(operation);
919
920
0
  call = &operation->call.locateCardsA;
921
922
0
  ret.ReturnCode = wrap(smartcard, SCardLocateCardsA, operation->hContext, call->mszCards,
923
0
                        call->rgReaderStates, call->cReaders);
924
0
  scard_log_status_error_wlog(smartcard->log, "SCardLocateCardsA", ret.ReturnCode);
925
0
  ret.rgReaderStates = nullptr;
926
927
0
  if (call->cReaders > 0)
928
0
  {
929
0
    ret.rgReaderStates =
930
0
        (ReaderState_Return*)calloc(call->cReaders, sizeof(ReaderState_Return));
931
932
0
    if (!ret.rgReaderStates)
933
0
      return STATUS_NO_MEMORY;
934
0
    ret.cReaders = call->cReaders;
935
0
  }
936
937
0
  for (UINT32 x = 0; x < ret.cReaders; x++)
938
0
  {
939
0
    const LPSCARD_READERSTATEA cstate = &call->rgReaderStates[x];
940
0
    ReaderState_Return* cur = &ret.rgReaderStates[x];
941
942
0
    cur->dwCurrentState = cstate->dwCurrentState;
943
0
    cur->dwEventState = cstate->dwEventState;
944
0
    cur->cbAtr = cstate->cbAtr;
945
0
    CopyMemory(&(cur->rgbAtr), &(cstate->rgbAtr), sizeof(cur->rgbAtr));
946
0
    if (!smartcard_reader_state_return_is_valid(x, cur))
947
0
    {
948
0
      free(ret.rgReaderStates);
949
0
      return SCARD_E_INVALID_ATR;
950
0
    }
951
0
  }
952
953
0
  status = smartcard_pack_locate_cards_return(out, &ret);
954
0
  free(ret.rgReaderStates);
955
956
0
  if (status != SCARD_S_SUCCESS)
957
0
    return status;
958
959
0
  return ret.ReturnCode;
960
0
}
961
962
WINPR_ATTR_NODISCARD static LONG smartcard_LocateCardsW_Call(scard_call_context* smartcard,
963
                                                             wStream* out,
964
                                                             SMARTCARD_OPERATION* operation)
965
0
{
966
0
  LONG status = 0;
967
0
  LocateCards_Return ret = WINPR_C_ARRAY_INIT;
968
969
0
  WINPR_ASSERT(smartcard);
970
0
  WINPR_ASSERT(operation);
971
972
0
  LocateCardsW_Call* call = &operation->call.locateCardsW;
973
974
0
  ret.ReturnCode = wrap(smartcard, SCardLocateCardsW, operation->hContext, call->mszCards,
975
0
                        call->rgReaderStates, call->cReaders);
976
0
  scard_log_status_error_wlog(smartcard->log, "SCardLocateCardsW", ret.ReturnCode);
977
0
  ret.rgReaderStates = nullptr;
978
979
0
  if (call->cReaders > 0)
980
0
  {
981
0
    ret.rgReaderStates =
982
0
        (ReaderState_Return*)calloc(call->cReaders, sizeof(ReaderState_Return));
983
984
0
    if (!ret.rgReaderStates)
985
0
      return STATUS_NO_MEMORY;
986
0
    ret.cReaders = call->cReaders;
987
0
  }
988
989
0
  for (UINT32 x = 0; x < ret.cReaders; x++)
990
0
  {
991
0
    const LPSCARD_READERSTATEW cstate = &call->rgReaderStates[x];
992
0
    ReaderState_Return* cur = &ret.rgReaderStates[x];
993
994
0
    cur->dwCurrentState = cstate->dwCurrentState;
995
0
    cur->dwEventState = cstate->dwEventState;
996
0
    cur->cbAtr = cstate->cbAtr;
997
0
    CopyMemory(&(cur->rgbAtr), &(cstate->rgbAtr), sizeof(cur->rgbAtr));
998
0
    if (!smartcard_reader_state_return_is_valid(x, cur))
999
0
    {
1000
0
      free(ret.rgReaderStates);
1001
0
      return SCARD_E_INVALID_ATR;
1002
0
    }
1003
0
  }
1004
1005
0
  status = smartcard_pack_locate_cards_return(out, &ret);
1006
0
  free(ret.rgReaderStates);
1007
1008
0
  if (status != SCARD_S_SUCCESS)
1009
0
    return status;
1010
1011
0
  return ret.ReturnCode;
1012
0
}
1013
1014
WINPR_ATTR_NODISCARD static LONG smartcard_ReadCacheA_Call(scard_call_context* smartcard,
1015
                                                           wStream* out,
1016
                                                           SMARTCARD_OPERATION* operation)
1017
0
{
1018
0
  LONG status = 0;
1019
0
  BOOL autoalloc = 0;
1020
0
  ReadCache_Return ret = WINPR_C_ARRAY_INIT;
1021
0
  ReadCacheA_Call* call = nullptr;
1022
1023
0
  WINPR_ASSERT(smartcard);
1024
0
  WINPR_ASSERT(out);
1025
0
  WINPR_ASSERT(operation);
1026
1027
0
  call = &operation->call.readCacheA;
1028
0
  autoalloc = (call->Common.cbDataLen == SCARD_AUTOALLOCATE);
1029
1030
0
  if (!call->Common.fPbDataIsNULL)
1031
0
  {
1032
0
    if (!autoalloc)
1033
0
    {
1034
0
      ret.pbData = calloc(1, call->Common.cbDataLen);
1035
0
      if (!ret.pbData)
1036
0
        return SCARD_F_INTERNAL_ERROR;
1037
0
    }
1038
0
    ret.cbDataLen = call->Common.cbDataLen;
1039
0
  }
1040
1041
0
  if (autoalloc)
1042
0
    ret.ReturnCode = wrap(smartcard, SCardReadCacheA, operation->hContext,
1043
0
                          call->Common.CardIdentifier, call->Common.FreshnessCounter,
1044
0
                          call->szLookupName, (BYTE*)&ret.pbData, &ret.cbDataLen);
1045
0
  else
1046
0
    ret.ReturnCode =
1047
0
        wrap(smartcard, SCardReadCacheA, operation->hContext, call->Common.CardIdentifier,
1048
0
             call->Common.FreshnessCounter, call->szLookupName, ret.pbData, &ret.cbDataLen);
1049
1050
0
  WLog_Print(smartcard->log, WLOG_TRACE, "key=%s, length=%" PRIu32, call->szLookupName,
1051
0
             ret.cbDataLen);
1052
1053
0
  if ((ret.ReturnCode != SCARD_W_CACHE_ITEM_NOT_FOUND) &&
1054
0
      (ret.ReturnCode != SCARD_W_CACHE_ITEM_STALE))
1055
0
  {
1056
0
    scard_log_status_error_wlog(smartcard->log, "SCardReadCacheA", ret.ReturnCode);
1057
0
  }
1058
1059
0
  status = smartcard_pack_read_cache_return(out, &ret);
1060
0
  if (autoalloc)
1061
0
    wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbData);
1062
0
  else
1063
0
    free(ret.pbData);
1064
0
  if (status != SCARD_S_SUCCESS)
1065
0
    return status;
1066
1067
0
  return ret.ReturnCode;
1068
0
}
1069
1070
WINPR_ATTR_NODISCARD static LONG smartcard_ReadCacheW_Call(scard_call_context* smartcard,
1071
                                                           wStream* out,
1072
                                                           SMARTCARD_OPERATION* operation)
1073
0
{
1074
0
  LONG status = 0;
1075
0
  ReadCache_Return ret = WINPR_C_ARRAY_INIT;
1076
0
  ReadCacheW_Call* call = nullptr;
1077
1078
0
  WINPR_ASSERT(smartcard);
1079
0
  WINPR_ASSERT(out);
1080
0
  WINPR_ASSERT(operation);
1081
1082
0
  call = &operation->call.readCacheW;
1083
1084
0
  if (!call->Common.fPbDataIsNULL)
1085
0
    ret.cbDataLen = SCARD_AUTOALLOCATE;
1086
1087
0
  ret.ReturnCode =
1088
0
      wrap(smartcard, SCardReadCacheW, operation->hContext, call->Common.CardIdentifier,
1089
0
           call->Common.FreshnessCounter, call->szLookupName, (BYTE*)&ret.pbData, &ret.cbDataLen);
1090
1091
0
  if (WLog_IsLevelActive(smartcard->log, WLOG_TRACE))
1092
0
  {
1093
0
    char buffer[128] = WINPR_C_ARRAY_INIT;
1094
0
    (void)ConvertWCharToUtf8(call->szLookupName, buffer, sizeof(buffer));
1095
0
    WLog_Print(smartcard->log, WLOG_TRACE, "key=%s, length=%" PRIu32, buffer, ret.cbDataLen);
1096
0
  }
1097
0
  if ((ret.ReturnCode != SCARD_W_CACHE_ITEM_NOT_FOUND) &&
1098
0
      (ret.ReturnCode != SCARD_W_CACHE_ITEM_STALE))
1099
0
  {
1100
0
    scard_log_status_error_wlog(smartcard->log, "SCardReadCacheW", ret.ReturnCode);
1101
0
  }
1102
1103
0
  status = smartcard_pack_read_cache_return(out, &ret);
1104
1105
0
  wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbData);
1106
1107
0
  if (status != SCARD_S_SUCCESS)
1108
0
    return status;
1109
1110
0
  return ret.ReturnCode;
1111
0
}
1112
1113
WINPR_ATTR_NODISCARD static LONG smartcard_WriteCacheA_Call(scard_call_context* smartcard,
1114
                                                            WINPR_ATTR_UNUSED wStream* out,
1115
                                                            SMARTCARD_OPERATION* operation)
1116
0
{
1117
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
1118
0
  WriteCacheA_Call* call = nullptr;
1119
1120
0
  WINPR_ASSERT(smartcard);
1121
0
  WINPR_ASSERT(out);
1122
0
  WINPR_ASSERT(operation);
1123
1124
0
  call = &operation->call.writeCacheA;
1125
1126
0
  ret.ReturnCode = wrap(smartcard, SCardWriteCacheA, operation->hContext,
1127
0
                        call->Common.CardIdentifier, call->Common.FreshnessCounter,
1128
0
                        call->szLookupName, call->Common.pbData, call->Common.cbDataLen);
1129
0
  scard_log_status_error_wlog(smartcard->log, "SCardWriteCacheA", ret.ReturnCode);
1130
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "SCardWriteCacheA");
1131
0
  WLog_Print(smartcard->log, WLOG_TRACE, "key=%s, length=%" PRIu32, call->szLookupName,
1132
0
             call->Common.cbDataLen);
1133
0
  return ret.ReturnCode;
1134
0
}
1135
1136
WINPR_ATTR_NODISCARD static LONG smartcard_WriteCacheW_Call(scard_call_context* smartcard,
1137
                                                            WINPR_ATTR_UNUSED wStream* out,
1138
                                                            SMARTCARD_OPERATION* operation)
1139
0
{
1140
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
1141
0
  WriteCacheW_Call* call = nullptr;
1142
1143
0
  WINPR_ASSERT(smartcard);
1144
0
  WINPR_ASSERT(out);
1145
0
  WINPR_ASSERT(operation);
1146
1147
0
  call = &operation->call.writeCacheW;
1148
1149
0
  ret.ReturnCode = wrap(smartcard, SCardWriteCacheW, operation->hContext,
1150
0
                        call->Common.CardIdentifier, call->Common.FreshnessCounter,
1151
0
                        call->szLookupName, call->Common.pbData, call->Common.cbDataLen);
1152
0
  scard_log_status_error_wlog(smartcard->log, "SCardWriteCacheW", ret.ReturnCode);
1153
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "SCardWriteCacheW");
1154
1155
0
  if (WLog_IsLevelActive(smartcard->log, WLOG_TRACE))
1156
0
  {
1157
0
    char buffer[128] = WINPR_C_ARRAY_INIT;
1158
0
    (void)ConvertWCharToUtf8(call->szLookupName, buffer, sizeof(buffer));
1159
0
    WLog_Print(smartcard->log, WLOG_TRACE, "key=%s, length=%" PRIu32, buffer,
1160
0
               call->Common.cbDataLen);
1161
0
  }
1162
0
  return ret.ReturnCode;
1163
0
}
1164
1165
WINPR_ATTR_NODISCARD static LONG smartcard_GetTransmitCount_Call(scard_call_context* smartcard,
1166
                                                                 wStream* out,
1167
                                                                 SMARTCARD_OPERATION* operation)
1168
0
{
1169
0
  LONG status = 0;
1170
0
  GetTransmitCount_Return ret = WINPR_C_ARRAY_INIT;
1171
1172
0
  WINPR_ASSERT(smartcard);
1173
0
  WINPR_ASSERT(out);
1174
0
  WINPR_ASSERT(operation);
1175
1176
0
  ret.ReturnCode = wrap(smartcard, SCardGetTransmitCount, operation->hCard, &ret.cTransmitCount);
1177
0
  scard_log_status_error_wlog(smartcard->log, "SCardGetTransmitCount", ret.ReturnCode);
1178
0
  status = smartcard_pack_get_transmit_count_return(out, &ret);
1179
0
  if (status != SCARD_S_SUCCESS)
1180
0
    return status;
1181
1182
0
  return ret.ReturnCode;
1183
0
}
1184
1185
WINPR_ATTR_NODISCARD static LONG smartcard_ReleaseStartedEvent_Call(scard_call_context* smartcard,
1186
                                                                    wStream* out,
1187
                                                                    SMARTCARD_OPERATION* operation)
1188
0
{
1189
0
  WINPR_UNUSED(smartcard);
1190
0
  WINPR_UNUSED(out);
1191
0
  WINPR_UNUSED(operation);
1192
1193
0
  WLog_Print(smartcard->log, WLOG_WARN,
1194
0
             "According to [MS-RDPESC] 3.1.4 Message Processing Events and Sequencing Rules "
1195
0
             "this is not supported?!?");
1196
0
  return SCARD_E_UNSUPPORTED_FEATURE;
1197
0
}
1198
1199
WINPR_ATTR_NODISCARD static LONG smartcard_GetReaderIcon_Call(scard_call_context* smartcard,
1200
                                                              wStream* out,
1201
                                                              SMARTCARD_OPERATION* operation)
1202
0
{
1203
0
  LONG status = 0;
1204
0
  GetReaderIcon_Return ret = WINPR_C_ARRAY_INIT;
1205
0
  GetReaderIcon_Call* call = nullptr;
1206
1207
0
  WINPR_ASSERT(smartcard);
1208
0
  WINPR_ASSERT(out);
1209
0
  WINPR_ASSERT(operation);
1210
1211
0
  call = &operation->call.getReaderIcon;
1212
1213
0
  ret.cbDataLen = SCARD_AUTOALLOCATE;
1214
0
  ret.ReturnCode = wrap(smartcard, SCardGetReaderIconW, operation->hContext, call->szReaderName,
1215
0
                        (LPBYTE)&ret.pbData, &ret.cbDataLen);
1216
0
  scard_log_status_error_wlog(smartcard->log, "SCardGetReaderIconW", ret.ReturnCode);
1217
0
  if ((ret.ReturnCode == SCARD_S_SUCCESS) && (ret.cbDataLen == SCARD_AUTOALLOCATE))
1218
0
    return SCARD_F_UNKNOWN_ERROR;
1219
1220
0
  status = smartcard_pack_get_reader_icon_return(out, &ret);
1221
0
  wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbData);
1222
0
  if (status != SCARD_S_SUCCESS)
1223
0
    return status;
1224
1225
0
  return ret.ReturnCode;
1226
0
}
1227
1228
WINPR_ATTR_NODISCARD static LONG smartcard_GetDeviceTypeId_Call(scard_call_context* smartcard,
1229
                                                                wStream* out,
1230
                                                                SMARTCARD_OPERATION* operation)
1231
0
{
1232
0
  LONG status = 0;
1233
0
  GetDeviceTypeId_Return ret = WINPR_C_ARRAY_INIT;
1234
0
  GetDeviceTypeId_Call* call = nullptr;
1235
1236
0
  WINPR_ASSERT(smartcard);
1237
0
  WINPR_ASSERT(out);
1238
0
  WINPR_ASSERT(operation);
1239
1240
0
  call = &operation->call.getDeviceTypeId;
1241
1242
0
  ret.ReturnCode = wrap(smartcard, SCardGetDeviceTypeIdW, operation->hContext, call->szReaderName,
1243
0
                        &ret.dwDeviceId);
1244
0
  scard_log_status_error_wlog(smartcard->log, "SCardGetDeviceTypeIdW", ret.ReturnCode);
1245
1246
0
  status = smartcard_pack_device_type_id_return(out, &ret);
1247
0
  if (status != SCARD_S_SUCCESS)
1248
0
    return status;
1249
1250
0
  return ret.ReturnCode;
1251
0
}
1252
1253
WINPR_ATTR_NODISCARD
1254
static BOOL smartcard_context_was_aborted(scard_call_context* smartcard)
1255
0
{
1256
0
  WINPR_ASSERT(smartcard);
1257
1258
0
  HANDLE handles[] = { smartcard->stopEvent, freerdp_abort_event(smartcard->context) };
1259
0
  const DWORD rc = WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, 0);
1260
0
  return (rc >= WAIT_OBJECT_0) && (rc <= WAIT_OBJECT_0 + ARRAYSIZE(handles));
1261
0
}
1262
1263
/* wait in steps to notice an abort, stopping also cancels the wait */
1264
0
#define SCARD_STATUS_CHANGE_STEP_MS 1000
1265
1266
WINPR_ATTR_NODISCARD
1267
static DWORD smartcard_status_change_step(DWORD dwTimeOut, DWORD waited)
1268
0
{
1269
0
  if (dwTimeOut == INFINITE)
1270
0
    return SCARD_STATUS_CHANGE_STEP_MS;
1271
0
  if (waited >= dwTimeOut)
1272
0
    return 0;
1273
0
  return MIN(dwTimeOut - waited, SCARD_STATUS_CHANGE_STEP_MS);
1274
0
}
1275
1276
WINPR_ATTR_NODISCARD static LONG smartcard_GetStatusChangeA_Call(scard_call_context* smartcard,
1277
                                                                 wStream* out,
1278
                                                                 SMARTCARD_OPERATION* operation)
1279
0
{
1280
0
  LONG status = STATUS_NO_MEMORY;
1281
0
  DWORD dwTimeOut = 0;
1282
0
  GetStatusChange_Return ret = WINPR_C_ARRAY_INIT;
1283
0
  GetStatusChangeA_Call* call = nullptr;
1284
0
  LPSCARD_READERSTATEA rgReaderStates = nullptr;
1285
1286
0
  WINPR_ASSERT(smartcard);
1287
0
  WINPR_ASSERT(out);
1288
0
  WINPR_ASSERT(operation);
1289
1290
0
  call = &operation->call.getStatusChangeA;
1291
0
  dwTimeOut = call->dwTimeOut;
1292
1293
0
  if (call->cReaders > 0)
1294
0
  {
1295
0
    rgReaderStates = calloc(call->cReaders, sizeof(SCARD_READERSTATEA));
1296
0
    ret.rgReaderStates =
1297
0
        (ReaderState_Return*)calloc(call->cReaders, sizeof(ReaderState_Return));
1298
0
    if (!rgReaderStates || !ret.rgReaderStates)
1299
0
      goto fail;
1300
1301
0
    ret.cReaders = call->cReaders;
1302
0
  }
1303
1304
0
  const UINT64 start = GetTickCount64();
1305
0
  for (DWORD waited = 0;;)
1306
0
  {
1307
0
    const DWORD step = smartcard_status_change_step(dwTimeOut, waited);
1308
0
    if (call->cReaders > 0)
1309
0
      memcpy(rgReaderStates, call->rgReaderStates,
1310
0
             call->cReaders * sizeof(SCARD_READERSTATEA));
1311
    /* not wrap(), do not log every step */
1312
0
    ret.ReturnCode = wrap_raw(smartcard, SCardGetStatusChangeA, operation->hContext, step,
1313
0
                              rgReaderStates, call->cReaders);
1314
0
    if (ret.ReturnCode != SCARD_E_TIMEOUT)
1315
0
      break;
1316
0
    if (dwTimeOut != INFINITE)
1317
0
    {
1318
      /* the time really waited, each step also costs a pcscd round trip */
1319
0
      waited = (DWORD)MIN(GetTickCount64() - start, dwTimeOut);
1320
0
      if (waited >= dwTimeOut)
1321
0
        break;
1322
0
    }
1323
0
    if (smartcard_context_was_aborted(smartcard))
1324
0
      break;
1325
0
  }
1326
0
  scard_log_status_error_wlog(smartcard->log, "SCardGetStatusChangeA", ret.ReturnCode);
1327
1328
0
  status = SCARD_E_INVALID_ATR;
1329
0
  for (UINT32 index = 0; index < ret.cReaders; index++)
1330
0
  {
1331
0
    const SCARD_READERSTATEA* cur = &rgReaderStates[index];
1332
0
    ReaderState_Return* rout = &ret.rgReaderStates[index];
1333
1334
0
    rout->dwCurrentState = cur->dwCurrentState;
1335
0
    rout->dwEventState = cur->dwEventState;
1336
0
    rout->cbAtr = cur->cbAtr;
1337
0
    CopyMemory(&(rout->rgbAtr), cur->rgbAtr, sizeof(rout->rgbAtr));
1338
0
    if (!smartcard_reader_state_return_is_valid(index, rout))
1339
0
      goto fail;
1340
0
  }
1341
1342
0
  status = smartcard_pack_get_status_change_return(out, &ret, FALSE);
1343
0
fail:
1344
0
  free(ret.rgReaderStates);
1345
0
  free(rgReaderStates);
1346
0
  if (status != SCARD_S_SUCCESS)
1347
0
    return status;
1348
0
  return ret.ReturnCode;
1349
0
}
1350
1351
WINPR_ATTR_NODISCARD static LONG smartcard_GetStatusChangeW_Call(scard_call_context* smartcard,
1352
                                                                 wStream* out,
1353
                                                                 SMARTCARD_OPERATION* operation)
1354
0
{
1355
0
  LONG status = STATUS_NO_MEMORY;
1356
0
  DWORD dwTimeOut = 0;
1357
0
  GetStatusChange_Return ret = WINPR_C_ARRAY_INIT;
1358
0
  LPSCARD_READERSTATEW rgReaderStates = nullptr;
1359
1360
0
  WINPR_ASSERT(smartcard);
1361
0
  WINPR_ASSERT(out);
1362
0
  WINPR_ASSERT(operation);
1363
1364
0
  GetStatusChangeW_Call* call = &operation->call.getStatusChangeW;
1365
0
  dwTimeOut = call->dwTimeOut;
1366
1367
0
  if (call->cReaders > 0)
1368
0
  {
1369
0
    rgReaderStates = calloc(call->cReaders, sizeof(SCARD_READERSTATEW));
1370
0
    ret.rgReaderStates =
1371
0
        (ReaderState_Return*)calloc(call->cReaders, sizeof(ReaderState_Return));
1372
0
    if (!rgReaderStates || !ret.rgReaderStates)
1373
0
      goto fail;
1374
1375
0
    ret.cReaders = call->cReaders;
1376
0
  }
1377
1378
0
  const UINT64 start = GetTickCount64();
1379
0
  for (DWORD waited = 0;;)
1380
0
  {
1381
0
    const DWORD step = smartcard_status_change_step(dwTimeOut, waited);
1382
0
    if (call->cReaders > 0)
1383
0
      memcpy(rgReaderStates, call->rgReaderStates,
1384
0
             call->cReaders * sizeof(SCARD_READERSTATEW));
1385
    /* not wrap(), do not log every step */
1386
0
    ret.ReturnCode = wrap_raw(smartcard, SCardGetStatusChangeW, operation->hContext, step,
1387
0
                              rgReaderStates, call->cReaders);
1388
0
    if (ret.ReturnCode != SCARD_E_TIMEOUT)
1389
0
      break;
1390
0
    if (dwTimeOut != INFINITE)
1391
0
    {
1392
      /* the time really waited, each step also costs a pcscd round trip */
1393
0
      waited = (DWORD)MIN(GetTickCount64() - start, dwTimeOut);
1394
0
      if (waited >= dwTimeOut)
1395
0
        break;
1396
0
    }
1397
0
    if (smartcard_context_was_aborted(smartcard))
1398
0
      break;
1399
0
  }
1400
0
  scard_log_status_error_wlog(smartcard->log, "SCardGetStatusChangeW", ret.ReturnCode);
1401
1402
0
  status = SCARD_E_INVALID_ATR;
1403
0
  for (UINT32 index = 0; index < ret.cReaders; index++)
1404
0
  {
1405
0
    const SCARD_READERSTATEW* cur = &rgReaderStates[index];
1406
0
    ReaderState_Return* rout = &ret.rgReaderStates[index];
1407
1408
0
    rout->dwCurrentState = cur->dwCurrentState;
1409
0
    rout->dwEventState = cur->dwEventState;
1410
0
    rout->cbAtr = cur->cbAtr;
1411
0
    CopyMemory(&(rout->rgbAtr), cur->rgbAtr, sizeof(rout->rgbAtr));
1412
0
    if (!smartcard_reader_state_return_is_valid(index, rout))
1413
0
      goto fail;
1414
0
  }
1415
1416
0
  status = smartcard_pack_get_status_change_return(out, &ret, TRUE);
1417
0
fail:
1418
0
  free(ret.rgReaderStates);
1419
0
  free(rgReaderStates);
1420
0
  if (status != SCARD_S_SUCCESS)
1421
0
    return status;
1422
0
  return ret.ReturnCode;
1423
0
}
1424
1425
WINPR_ATTR_NODISCARD static LONG smartcard_Cancel_Call(scard_call_context* smartcard,
1426
                                                       WINPR_ATTR_UNUSED wStream* out,
1427
                                                       SMARTCARD_OPERATION* operation)
1428
0
{
1429
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
1430
1431
0
  WINPR_ASSERT(smartcard);
1432
0
  WINPR_ASSERT(out);
1433
0
  WINPR_ASSERT(operation);
1434
1435
0
  ret.ReturnCode = wrap(smartcard, SCardCancel, operation->hContext);
1436
0
  scard_log_status_error_wlog(smartcard->log, "SCardCancel", ret.ReturnCode);
1437
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "Cancel");
1438
0
  return ret.ReturnCode;
1439
0
}
1440
1441
WINPR_ATTR_NODISCARD static LONG
1442
smartcard_ConnectA_Call(scard_call_context* smartcard, wStream* out, SMARTCARD_OPERATION* operation)
1443
0
{
1444
0
  LONG status = 0;
1445
0
  SCARDHANDLE hCard = 0;
1446
0
  Connect_Return ret = WINPR_C_ARRAY_INIT;
1447
0
  ConnectA_Call* call = nullptr;
1448
1449
0
  WINPR_ASSERT(smartcard);
1450
0
  WINPR_ASSERT(out);
1451
0
  WINPR_ASSERT(operation);
1452
1453
0
  call = &operation->call.connectA;
1454
1455
0
  if ((call->Common.dwPreferredProtocols == SCARD_PROTOCOL_UNDEFINED) &&
1456
0
      (call->Common.dwShareMode != SCARD_SHARE_DIRECT))
1457
0
  {
1458
0
    call->Common.dwPreferredProtocols = SCARD_PROTOCOL_Tx;
1459
0
  }
1460
1461
0
  ret.ReturnCode = wrap(smartcard, SCardConnectA, operation->hContext, (char*)call->szReader,
1462
0
                        call->Common.dwShareMode, call->Common.dwPreferredProtocols, &hCard,
1463
0
                        &ret.dwActiveProtocol);
1464
0
  smartcard_scard_context_native_to_redir(&(ret.hContext), operation->hContext);
1465
0
  smartcard_scard_handle_native_to_redir(&(ret.hCard), hCard);
1466
1467
0
  status = smartcard_pack_connect_return(out, &ret);
1468
0
  if (status != SCARD_S_SUCCESS)
1469
0
    goto out_fail;
1470
1471
0
  status = ret.ReturnCode;
1472
0
out_fail:
1473
1474
0
  return status;
1475
0
}
1476
1477
WINPR_ATTR_NODISCARD static LONG
1478
smartcard_ConnectW_Call(scard_call_context* smartcard, wStream* out, SMARTCARD_OPERATION* operation)
1479
0
{
1480
0
  LONG status = 0;
1481
0
  SCARDHANDLE hCard = 0;
1482
0
  Connect_Return ret = WINPR_C_ARRAY_INIT;
1483
0
  ConnectW_Call* call = nullptr;
1484
1485
0
  WINPR_ASSERT(smartcard);
1486
0
  WINPR_ASSERT(out);
1487
0
  WINPR_ASSERT(operation);
1488
1489
0
  call = &operation->call.connectW;
1490
1491
0
  if ((call->Common.dwPreferredProtocols == SCARD_PROTOCOL_UNDEFINED) &&
1492
0
      (call->Common.dwShareMode != SCARD_SHARE_DIRECT))
1493
0
  {
1494
0
    call->Common.dwPreferredProtocols = SCARD_PROTOCOL_Tx;
1495
0
  }
1496
1497
0
  ret.ReturnCode = wrap(smartcard, SCardConnectW, operation->hContext, (WCHAR*)call->szReader,
1498
0
                        call->Common.dwShareMode, call->Common.dwPreferredProtocols, &hCard,
1499
0
                        &ret.dwActiveProtocol);
1500
0
  smartcard_scard_context_native_to_redir(&(ret.hContext), operation->hContext);
1501
0
  smartcard_scard_handle_native_to_redir(&(ret.hCard), hCard);
1502
1503
0
  status = smartcard_pack_connect_return(out, &ret);
1504
0
  if (status != SCARD_S_SUCCESS)
1505
0
    goto out_fail;
1506
1507
0
  status = ret.ReturnCode;
1508
0
out_fail:
1509
1510
0
  return status;
1511
0
}
1512
1513
WINPR_ATTR_NODISCARD static LONG smartcard_Reconnect_Call(scard_call_context* smartcard,
1514
                                                          wStream* out,
1515
                                                          SMARTCARD_OPERATION* operation)
1516
0
{
1517
0
  LONG status = 0;
1518
0
  Reconnect_Return ret = WINPR_C_ARRAY_INIT;
1519
0
  Reconnect_Call* call = nullptr;
1520
1521
0
  WINPR_ASSERT(smartcard);
1522
0
  WINPR_ASSERT(out);
1523
0
  WINPR_ASSERT(operation);
1524
1525
0
  call = &operation->call.reconnect;
1526
0
  ret.ReturnCode =
1527
0
      wrap(smartcard, SCardReconnect, operation->hCard, call->dwShareMode,
1528
0
           call->dwPreferredProtocols, call->dwInitialization, &ret.dwActiveProtocol);
1529
0
  scard_log_status_error_wlog(smartcard->log, "SCardReconnect", ret.ReturnCode);
1530
0
  status = smartcard_pack_reconnect_return(out, &ret);
1531
0
  if (status != SCARD_S_SUCCESS)
1532
0
    return status;
1533
1534
0
  return ret.ReturnCode;
1535
0
}
1536
1537
WINPR_ATTR_NODISCARD static LONG smartcard_Disconnect_Call(scard_call_context* smartcard,
1538
                                                           WINPR_ATTR_UNUSED wStream* out,
1539
                                                           SMARTCARD_OPERATION* operation)
1540
0
{
1541
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
1542
0
  HCardAndDisposition_Call* call = nullptr;
1543
1544
0
  WINPR_ASSERT(smartcard);
1545
0
  WINPR_ASSERT(out);
1546
0
  WINPR_ASSERT(operation);
1547
1548
0
  call = &operation->call.hCardAndDisposition;
1549
1550
0
  ret.ReturnCode = wrap(smartcard, SCardDisconnect, operation->hCard, call->dwDisposition);
1551
0
  scard_log_status_error_wlog(smartcard->log, "SCardDisconnect", ret.ReturnCode);
1552
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "Disconnect");
1553
1554
0
  return ret.ReturnCode;
1555
0
}
1556
1557
WINPR_ATTR_NODISCARD static LONG smartcard_BeginTransaction_Call(scard_call_context* smartcard,
1558
                                                                 WINPR_ATTR_UNUSED wStream* out,
1559
                                                                 SMARTCARD_OPERATION* operation)
1560
0
{
1561
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
1562
1563
0
  WINPR_ASSERT(smartcard);
1564
0
  WINPR_ASSERT(out);
1565
0
  WINPR_ASSERT(operation);
1566
1567
0
  ret.ReturnCode = wrap(smartcard, SCardBeginTransaction, operation->hCard);
1568
0
  scard_log_status_error_wlog(smartcard->log, "SCardBeginTransaction", ret.ReturnCode);
1569
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "BeginTransaction");
1570
0
  return ret.ReturnCode;
1571
0
}
1572
1573
WINPR_ATTR_NODISCARD static LONG smartcard_EndTransaction_Call(scard_call_context* smartcard,
1574
                                                               WINPR_ATTR_UNUSED wStream* out,
1575
                                                               SMARTCARD_OPERATION* operation)
1576
0
{
1577
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
1578
0
  HCardAndDisposition_Call* call = nullptr;
1579
1580
0
  WINPR_ASSERT(smartcard);
1581
0
  WINPR_ASSERT(out);
1582
0
  WINPR_ASSERT(operation);
1583
1584
0
  call = &operation->call.hCardAndDisposition;
1585
1586
0
  ret.ReturnCode = wrap(smartcard, SCardEndTransaction, operation->hCard, call->dwDisposition);
1587
0
  scard_log_status_error_wlog(smartcard->log, "SCardEndTransaction", ret.ReturnCode);
1588
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "EndTransaction");
1589
0
  return ret.ReturnCode;
1590
0
}
1591
1592
WINPR_ATTR_NODISCARD static LONG smartcard_State_Call(scard_call_context* smartcard, wStream* out,
1593
                                                      SMARTCARD_OPERATION* operation)
1594
0
{
1595
0
  LONG status = 0;
1596
0
  State_Return ret = WINPR_C_ARRAY_INIT;
1597
1598
0
  WINPR_ASSERT(smartcard);
1599
0
  WINPR_ASSERT(out);
1600
0
  WINPR_ASSERT(operation);
1601
1602
0
  ret.cbAtrLen = SCARD_ATR_LENGTH;
1603
0
  ret.ReturnCode = wrap(smartcard, SCardState, operation->hCard, &ret.dwState, &ret.dwProtocol,
1604
0
                        (BYTE*)&ret.rgAtr, &ret.cbAtrLen);
1605
1606
0
  scard_log_status_error_wlog(smartcard->log, "SCardState", ret.ReturnCode);
1607
0
  status = smartcard_pack_state_return(out, &ret);
1608
0
  if (status != SCARD_S_SUCCESS)
1609
0
    return status;
1610
1611
0
  return ret.ReturnCode;
1612
0
}
1613
1614
WINPR_ATTR_NODISCARD static LONG smartcard_StatusA_Call(scard_call_context* smartcard, wStream* out,
1615
                                                        SMARTCARD_OPERATION* operation)
1616
0
{
1617
0
  LONG status = 0;
1618
0
  Status_Return ret = WINPR_C_ARRAY_INIT;
1619
0
  DWORD cchReaderLen = 0;
1620
0
  DWORD cbAtrLen = 0;
1621
0
  LPSTR mszReaderNames = nullptr;
1622
0
  Status_Call* call = nullptr;
1623
1624
0
  WINPR_ASSERT(smartcard);
1625
0
  WINPR_ASSERT(out);
1626
0
  WINPR_ASSERT(operation);
1627
1628
0
  call = &operation->call.status;
1629
1630
0
  call->cbAtrLen = 32;
1631
0
  cbAtrLen = call->cbAtrLen;
1632
1633
0
  if (call->fmszReaderNamesIsNULL)
1634
0
    cchReaderLen = 0;
1635
0
  else
1636
0
    cchReaderLen = SCARD_AUTOALLOCATE;
1637
1638
0
  status = ret.ReturnCode =
1639
0
      wrap(smartcard, SCardStatusA, operation->hCard,
1640
0
           call->fmszReaderNamesIsNULL ? nullptr : (LPSTR)&mszReaderNames, &cchReaderLen,
1641
0
           &ret.dwState, &ret.dwProtocol, cbAtrLen ? (BYTE*)&ret.pbAtr : nullptr, &cbAtrLen);
1642
1643
0
  scard_log_status_error_wlog(smartcard->log, "SCardStatusA", status);
1644
0
  if ((ret.ReturnCode == SCARD_S_SUCCESS) && (cchReaderLen == SCARD_AUTOALLOCATE))
1645
0
    return SCARD_F_UNKNOWN_ERROR;
1646
1647
0
  if (status == SCARD_S_SUCCESS)
1648
0
  {
1649
0
    if (!call->fmszReaderNamesIsNULL)
1650
0
      ret.mszReaderNames = (BYTE*)mszReaderNames;
1651
1652
0
    ret.cBytes = cchReaderLen;
1653
1654
0
    if (call->cbAtrLen)
1655
0
      ret.cbAtrLen = cbAtrLen;
1656
0
  }
1657
1658
0
  status = smartcard_pack_status_return(out, &ret, FALSE);
1659
1660
0
  if (mszReaderNames)
1661
0
    wrap(smartcard, SCardFreeMemory, operation->hContext, mszReaderNames);
1662
1663
0
  if (status != SCARD_S_SUCCESS)
1664
0
    return status;
1665
0
  return ret.ReturnCode;
1666
0
}
1667
1668
WINPR_ATTR_NODISCARD static LONG smartcard_StatusW_Call(scard_call_context* smartcard, wStream* out,
1669
                                                        SMARTCARD_OPERATION* operation)
1670
0
{
1671
0
  LONG status = 0;
1672
0
  Status_Return ret = WINPR_C_ARRAY_INIT;
1673
0
  LPWSTR mszReaderNames = nullptr;
1674
0
  Status_Call* call = nullptr;
1675
0
  DWORD cbAtrLen = 0;
1676
1677
0
  WINPR_ASSERT(smartcard);
1678
0
  WINPR_ASSERT(out);
1679
0
  WINPR_ASSERT(operation);
1680
1681
0
  call = &operation->call.status;
1682
1683
  /**
1684
   * [MS-RDPESC]
1685
   * According to 2.2.2.18 Status_Call cbAtrLen is unused an must be ignored upon receipt.
1686
   */
1687
0
  cbAtrLen = call->cbAtrLen = 32;
1688
1689
0
  if (call->fmszReaderNamesIsNULL)
1690
0
    ret.cBytes = 0;
1691
0
  else
1692
0
    ret.cBytes = SCARD_AUTOALLOCATE;
1693
1694
0
  status = ret.ReturnCode =
1695
0
      wrap(smartcard, SCardStatusW, operation->hCard,
1696
0
           call->fmszReaderNamesIsNULL ? nullptr : (LPWSTR)&mszReaderNames, &ret.cBytes,
1697
0
           &ret.dwState, &ret.dwProtocol, (BYTE*)&ret.pbAtr, &cbAtrLen);
1698
0
  scard_log_status_error_wlog(smartcard->log, "SCardStatusW", status);
1699
0
  if ((ret.ReturnCode == SCARD_S_SUCCESS) && (ret.cBytes == SCARD_AUTOALLOCATE))
1700
0
    return SCARD_F_UNKNOWN_ERROR;
1701
1702
0
  size_t blen = 0;
1703
0
  if (status == SCARD_S_SUCCESS)
1704
0
  {
1705
0
    if (!call->fmszReaderNamesIsNULL)
1706
0
      ret.mszReaderNames = (BYTE*)mszReaderNames;
1707
1708
0
    ret.cbAtrLen = cbAtrLen;
1709
0
  }
1710
1711
0
  if (ret.cBytes != SCARD_AUTOALLOCATE)
1712
0
  {
1713
    /* SCardStatusW returns number of characters, we need number of bytes */
1714
0
    WINPR_ASSERT(ret.cBytes < SCARD_AUTOALLOCATE / sizeof(WCHAR));
1715
0
    blen = sizeof(WCHAR) * ret.cBytes;
1716
0
    WINPR_ASSERT(blen <= UINT32_MAX);
1717
0
    ret.cBytes = (UINT32)blen;
1718
0
  }
1719
1720
0
  status = smartcard_pack_status_return(out, &ret, TRUE);
1721
0
  if (status != SCARD_S_SUCCESS)
1722
0
    return status;
1723
1724
0
  if (mszReaderNames)
1725
0
    wrap(smartcard, SCardFreeMemory, operation->hContext, mszReaderNames);
1726
1727
0
  return ret.ReturnCode;
1728
0
}
1729
1730
WINPR_ATTR_NODISCARD static LONG
1731
smartcard_Transmit_Call(scard_call_context* smartcard, wStream* out, SMARTCARD_OPERATION* operation)
1732
0
{
1733
0
  LONG status = 0;
1734
0
  Transmit_Return ret = WINPR_C_ARRAY_INIT;
1735
0
  Transmit_Call* call = nullptr;
1736
1737
0
  WINPR_ASSERT(smartcard);
1738
0
  WINPR_ASSERT(out);
1739
0
  WINPR_ASSERT(operation);
1740
1741
0
  call = &operation->call.transmit;
1742
0
  ret.cbRecvLength = 0;
1743
0
  ret.pbRecvBuffer = nullptr;
1744
1745
0
  if (call->cbRecvLength && !call->fpbRecvBufferIsNULL)
1746
0
  {
1747
0
    if (call->cbRecvLength >= 66560)
1748
0
      call->cbRecvLength = 66560;
1749
1750
0
    const UINT32 cbRecvLength = call->cbRecvLength;
1751
0
    ret.pbRecvBuffer = (BYTE*)calloc(1, cbRecvLength);
1752
1753
0
    if (!ret.pbRecvBuffer)
1754
0
      return STATUS_NO_MEMORY;
1755
0
    ret.cbRecvLength = cbRecvLength;
1756
0
  }
1757
1758
0
  ret.pioRecvPci = call->pioRecvPci;
1759
0
  ret.ReturnCode =
1760
0
      wrap(smartcard, SCardTransmit, operation->hCard, call->pioSendPci, call->pbSendBuffer,
1761
0
           call->cbSendLength, ret.pioRecvPci, ret.pbRecvBuffer, &(ret.cbRecvLength));
1762
1763
0
  scard_log_status_error_wlog(smartcard->log, "SCardTransmit", ret.ReturnCode);
1764
1765
0
  status = smartcard_pack_transmit_return(out, &ret);
1766
0
  free(ret.pbRecvBuffer);
1767
1768
0
  if (status != SCARD_S_SUCCESS)
1769
0
    return status;
1770
0
  return ret.ReturnCode;
1771
0
}
1772
1773
WINPR_ATTR_NODISCARD static LONG smartcard_Control_Call(scard_call_context* smartcard, wStream* out,
1774
                                                        SMARTCARD_OPERATION* operation)
1775
0
{
1776
0
  LONG status = 0;
1777
0
  Control_Return ret = WINPR_C_ARRAY_INIT;
1778
0
  Control_Call* call = nullptr;
1779
1780
0
  WINPR_ASSERT(smartcard);
1781
0
  WINPR_ASSERT(out);
1782
0
  WINPR_ASSERT(operation);
1783
1784
0
  call = &operation->call.control;
1785
0
  ret.pvOutBuffer = (BYTE*)calloc(1, call->cbOutBufferSize);
1786
1787
0
  if (!ret.pvOutBuffer)
1788
0
    return SCARD_E_NO_MEMORY;
1789
0
  ret.cbOutBufferSize = call->cbOutBufferSize;
1790
1791
0
  ret.ReturnCode =
1792
0
      wrap(smartcard, SCardControl, operation->hCard, call->dwControlCode, call->pvInBuffer,
1793
0
           call->cbInBufferSize, ret.pvOutBuffer, call->cbOutBufferSize, &ret.cbOutBufferSize);
1794
0
  scard_log_status_error_wlog(smartcard->log, "SCardControl", ret.ReturnCode);
1795
0
  status = smartcard_pack_control_return(out, &ret);
1796
1797
0
  free(ret.pvOutBuffer);
1798
0
  if (status != SCARD_S_SUCCESS)
1799
0
    return status;
1800
0
  return ret.ReturnCode;
1801
0
}
1802
1803
WINPR_ATTR_NODISCARD static LONG smartcard_GetAttrib_Call(scard_call_context* smartcard,
1804
                                                          wStream* out,
1805
                                                          SMARTCARD_OPERATION* operation)
1806
0
{
1807
0
  BOOL autoAllocate = FALSE;
1808
0
  LONG status = 0;
1809
0
  DWORD cbAttrLen = 0;
1810
0
  LPBYTE pbAttr = nullptr;
1811
0
  GetAttrib_Return ret = WINPR_C_ARRAY_INIT;
1812
0
  const GetAttrib_Call* call = nullptr;
1813
1814
0
  WINPR_ASSERT(smartcard);
1815
0
  WINPR_ASSERT(operation);
1816
1817
0
  call = &operation->call.getAttrib;
1818
1819
0
  if (!call->fpbAttrIsNULL)
1820
0
  {
1821
0
    autoAllocate = (call->cbAttrLen == SCARD_AUTOALLOCATE);
1822
0
    cbAttrLen = call->cbAttrLen;
1823
0
    if (cbAttrLen && !autoAllocate)
1824
0
    {
1825
0
      ret.pbAttr = (BYTE*)calloc(1, cbAttrLen);
1826
1827
0
      if (!ret.pbAttr)
1828
0
        return SCARD_E_NO_MEMORY;
1829
0
    }
1830
1831
0
    pbAttr = autoAllocate ? (LPBYTE) & (ret.pbAttr) : ret.pbAttr;
1832
0
  }
1833
1834
0
  ret.ReturnCode =
1835
0
      wrap(smartcard, SCardGetAttrib, operation->hCard, call->dwAttrId, pbAttr, &cbAttrLen);
1836
0
  scard_log_status_error_wlog(smartcard->log, "SCardGetAttrib", ret.ReturnCode);
1837
0
  if ((ret.ReturnCode == SCARD_S_SUCCESS) && (cbAttrLen == SCARD_AUTOALLOCATE))
1838
0
    return SCARD_F_UNKNOWN_ERROR;
1839
1840
0
  ret.cbAttrLen = cbAttrLen;
1841
1842
0
  status = smartcard_pack_get_attrib_return(out, &ret, call->dwAttrId, call->cbAttrLen);
1843
1844
0
  if (autoAllocate)
1845
0
    wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbAttr);
1846
0
  else
1847
0
    free(ret.pbAttr);
1848
0
  return status;
1849
0
}
1850
1851
WINPR_ATTR_NODISCARD static LONG smartcard_SetAttrib_Call(scard_call_context* smartcard,
1852
                                                          WINPR_ATTR_UNUSED wStream* out,
1853
                                                          SMARTCARD_OPERATION* operation)
1854
0
{
1855
0
  Long_Return ret = WINPR_C_ARRAY_INIT;
1856
0
  SetAttrib_Call* call = nullptr;
1857
1858
0
  WINPR_ASSERT(smartcard);
1859
0
  WINPR_ASSERT(out);
1860
0
  WINPR_ASSERT(operation);
1861
1862
0
  call = &operation->call.setAttrib;
1863
1864
0
  ret.ReturnCode = wrap(smartcard, SCardSetAttrib, operation->hCard, call->dwAttrId, call->pbAttr,
1865
0
                        call->cbAttrLen);
1866
0
  scard_log_status_error_wlog(smartcard->log, "SCardSetAttrib", ret.ReturnCode);
1867
0
  smartcard_trace_long_return_int(smartcard->log, &ret, "SetAttrib");
1868
1869
0
  return ret.ReturnCode;
1870
0
}
1871
1872
WINPR_ATTR_NODISCARD static LONG smartcard_AccessStartedEvent_Call(scard_call_context* smartcard,
1873
                                                                   WINPR_ATTR_UNUSED wStream* out,
1874
                                                                   SMARTCARD_OPERATION* operation)
1875
0
{
1876
0
  LONG status = SCARD_S_SUCCESS;
1877
1878
0
  WINPR_ASSERT(smartcard);
1879
0
  WINPR_ASSERT(out);
1880
0
  WINPR_UNUSED(operation);
1881
1882
0
  if (!smartcard->StartedEvent)
1883
0
    smartcard->StartedEvent = wrap_ptr(smartcard, SCardAccessStartedEvent);
1884
1885
0
  if (!smartcard->StartedEvent)
1886
0
    status = SCARD_E_NO_SERVICE;
1887
1888
0
  return status;
1889
0
}
1890
1891
WINPR_ATTR_NODISCARD static LONG smartcard_LocateCardsByATRA_Call(scard_call_context* smartcard,
1892
                                                                  wStream* out,
1893
                                                                  SMARTCARD_OPERATION* operation)
1894
0
{
1895
0
  LONG status = SCARD_E_INVALID_HANDLE;
1896
0
  GetStatusChange_Return ret = WINPR_C_ARRAY_INIT;
1897
1898
0
  WINPR_ASSERT(smartcard);
1899
0
  WINPR_ASSERT(operation);
1900
1901
0
  LocateCardsByATRA_Call* call = &operation->call.locateCardsByATRA;
1902
0
  LPSCARD_READERSTATEA states =
1903
0
      (LPSCARD_READERSTATEA)calloc(call->cReaders, sizeof(SCARD_READERSTATEA));
1904
1905
0
  if (!states)
1906
0
    return STATUS_NO_MEMORY;
1907
1908
0
  for (UINT32 i = 0; i < call->cReaders; i++)
1909
0
  {
1910
0
    const LPSCARD_READERSTATEA cstate = &call->rgReaderStates[i];
1911
0
    LPSCARD_READERSTATEA state = &states[i];
1912
1913
0
    state->szReader = cstate->szReader;
1914
0
    state->dwCurrentState = cstate->dwCurrentState;
1915
0
    state->dwEventState = cstate->dwEventState;
1916
0
    state->cbAtr = cstate->cbAtr;
1917
1918
0
    if (state->cbAtr > sizeof(state->rgbAtr))
1919
0
    {
1920
0
      WLog_WARN(SCARD_TAG, "[%" PRIu32 "] val->cbAtr=%" PRIu32 ", max=%" PRIuz, i,
1921
0
                state->cbAtr, sizeof(state->rgbAtr));
1922
0
      goto fail;
1923
0
    }
1924
0
    CopyMemory(&(state->rgbAtr), &(cstate->rgbAtr), sizeof(cstate->rgbAtr));
1925
0
  }
1926
1927
0
  status = ret.ReturnCode = wrap(smartcard, SCardGetStatusChangeA, operation->hContext,
1928
0
                                 0x000001F4, states, call->cReaders);
1929
1930
0
  scard_log_status_error_wlog(smartcard->log, "SCardGetStatusChangeA", status);
1931
0
  for (UINT32 i = 0; i < call->cAtrs; i++)
1932
0
  {
1933
0
    for (UINT32 j = 0; j < call->cReaders; j++)
1934
0
    {
1935
0
      const LocateCards_ATRMask* mask = &call->rgAtrMasks[i];
1936
0
      for (UINT32 k = 0; k < winpr_Data_Get_UINT32(&mask->cbAtr); k++)
1937
0
      {
1938
0
        if ((mask->rgbAtr[k] & mask->rgbMask[k]) !=
1939
0
            (states[j].rgbAtr[k] & mask->rgbMask[k]))
1940
0
        {
1941
0
          break;
1942
0
        }
1943
1944
0
        states[j].dwEventState |= SCARD_STATE_ATRMATCH;
1945
0
      }
1946
0
    }
1947
0
  }
1948
1949
0
  ret.rgReaderStates = nullptr;
1950
1951
0
  if (call->cReaders > 0)
1952
0
    ret.rgReaderStates =
1953
0
        (ReaderState_Return*)calloc(call->cReaders, sizeof(ReaderState_Return));
1954
1955
0
  if (!ret.rgReaderStates)
1956
0
  {
1957
0
    status = STATUS_NO_MEMORY;
1958
0
    goto fail;
1959
0
  }
1960
1961
0
  ret.cReaders = call->cReaders;
1962
1963
0
  status = SCARD_E_INVALID_ATR;
1964
0
  for (UINT32 i = 0; i < ret.cReaders; i++)
1965
0
  {
1966
0
    const LPSCARD_READERSTATEA state = &states[i];
1967
0
    ReaderState_Return* cur = &ret.rgReaderStates[i];
1968
1969
0
    cur->dwCurrentState = state->dwCurrentState;
1970
0
    cur->dwEventState = state->dwEventState;
1971
0
    cur->cbAtr = state->cbAtr;
1972
0
    CopyMemory(&(cur->rgbAtr), &(state->rgbAtr), sizeof(cur->rgbAtr));
1973
1974
0
    if (!smartcard_reader_state_return_is_valid(i, cur))
1975
0
      goto fail;
1976
0
  }
1977
1978
0
  status = smartcard_pack_get_status_change_return(out, &ret, FALSE);
1979
1980
0
fail:
1981
0
  free(states);
1982
0
  free(ret.rgReaderStates);
1983
0
  if (status != SCARD_S_SUCCESS)
1984
0
    return status;
1985
0
  return ret.ReturnCode;
1986
0
}
1987
1988
WINPR_ATTR_NODISCARD static LONG smartcard_LocateCardsByATRW_Call(scard_call_context* smartcard,
1989
                                                                  wStream* out,
1990
                                                                  SMARTCARD_OPERATION* operation)
1991
0
{
1992
0
  LONG status = SCARD_E_INVALID_HANDLE;
1993
0
  GetStatusChange_Return ret = WINPR_C_ARRAY_INIT;
1994
1995
0
  WINPR_ASSERT(smartcard);
1996
0
  WINPR_ASSERT(operation);
1997
1998
0
  LocateCardsByATRW_Call* call = &operation->call.locateCardsByATRW;
1999
0
  LPSCARD_READERSTATEW states =
2000
0
      (LPSCARD_READERSTATEW)calloc(call->cReaders, sizeof(SCARD_READERSTATEW));
2001
2002
0
  if (!states)
2003
0
    return STATUS_NO_MEMORY;
2004
2005
0
  for (UINT32 i = 0; i < call->cReaders; i++)
2006
0
  {
2007
0
    const LPSCARD_READERSTATEW cstate = &call->rgReaderStates[i];
2008
0
    LPSCARD_READERSTATEW state = &states[i];
2009
2010
0
    state->szReader = cstate->szReader;
2011
0
    state->dwCurrentState = cstate->dwCurrentState;
2012
0
    state->dwEventState = cstate->dwEventState;
2013
0
    state->cbAtr = cstate->cbAtr;
2014
2015
0
    if (state->cbAtr > sizeof(state->rgbAtr))
2016
0
    {
2017
0
      WLog_WARN(SCARD_TAG, "[%" PRIu32 "] val->cbAtr=%" PRIu32 ", max=%" PRIuz, i,
2018
0
                state->cbAtr, sizeof(state->rgbAtr));
2019
0
      goto fail;
2020
0
    }
2021
0
    CopyMemory(&(state->rgbAtr), &(cstate->rgbAtr), sizeof(cstate->rgbAtr));
2022
0
  }
2023
2024
0
  status = ret.ReturnCode = wrap(smartcard, SCardGetStatusChangeW, operation->hContext,
2025
0
                                 0x000001F4, states, call->cReaders);
2026
2027
0
  scard_log_status_error_wlog(smartcard->log, "SCardGetStatusChangeW", status);
2028
0
  for (UINT32 i = 0; i < call->cAtrs; i++)
2029
0
  {
2030
0
    for (UINT32 j = 0; j < call->cReaders; j++)
2031
0
    {
2032
0
      const LocateCards_ATRMask* mask = &call->rgAtrMasks[i];
2033
0
      for (UINT32 k = 0; k < winpr_Data_Get_UINT32(&mask->cbAtr); k++)
2034
0
      {
2035
0
        if ((mask->rgbAtr[k] & mask->rgbMask[k]) !=
2036
0
            (states[j].rgbAtr[k] & mask->rgbMask[k]))
2037
0
        {
2038
0
          break;
2039
0
        }
2040
2041
0
        states[j].dwEventState |= SCARD_STATE_ATRMATCH;
2042
0
      }
2043
0
    }
2044
0
  }
2045
2046
0
  ret.rgReaderStates = nullptr;
2047
2048
0
  if (call->cReaders > 0)
2049
0
    ret.rgReaderStates =
2050
0
        (ReaderState_Return*)calloc(call->cReaders, sizeof(ReaderState_Return));
2051
2052
0
  if (!ret.rgReaderStates)
2053
0
  {
2054
0
    status = STATUS_NO_MEMORY;
2055
0
    goto fail;
2056
0
  }
2057
2058
0
  ret.cReaders = call->cReaders;
2059
2060
0
  status = SCARD_E_INVALID_ATR;
2061
0
  for (UINT32 i = 0; i < ret.cReaders; i++)
2062
0
  {
2063
0
    const LPSCARD_READERSTATEW state = &states[i];
2064
0
    ReaderState_Return* cur = &ret.rgReaderStates[i];
2065
2066
0
    cur->dwCurrentState = state->dwCurrentState;
2067
0
    cur->dwEventState = state->dwEventState;
2068
0
    cur->cbAtr = state->cbAtr;
2069
0
    CopyMemory(&(cur->rgbAtr), &(state->rgbAtr), sizeof(cur->rgbAtr));
2070
2071
0
    if (!smartcard_reader_state_return_is_valid(i, cur))
2072
0
      goto fail;
2073
0
  }
2074
2075
0
  status = smartcard_pack_get_status_change_return(out, &ret, FALSE);
2076
2077
0
fail:
2078
0
  free(states);
2079
0
  free(ret.rgReaderStates);
2080
0
  if (status != SCARD_S_SUCCESS)
2081
0
    return status;
2082
0
  return ret.ReturnCode;
2083
0
}
2084
2085
LONG smartcard_irp_device_control_call(scard_call_context* ctx, wStream* out, NTSTATUS* pIoStatus,
2086
                                       SMARTCARD_OPERATION* operation)
2087
0
{
2088
0
  LONG result = 0;
2089
0
  UINT32 offset = 0;
2090
0
  size_t objectBufferLength = 0;
2091
2092
0
  WINPR_ASSERT(ctx);
2093
0
  WINPR_ASSERT(out);
2094
0
  WINPR_ASSERT(pIoStatus);
2095
0
  WINPR_ASSERT(operation);
2096
2097
0
  const UINT32 ioControlCode = operation->ioControlCode;
2098
  /**
2099
   * [MS-RDPESC] 3.2.5.1: Sending Outgoing Messages:
2100
   * the output buffer length SHOULD be set to 2048
2101
   *
2102
   * Since it's a SHOULD and not a MUST, we don't care
2103
   * about it, but we still reserve at least 2048 bytes.
2104
   */
2105
0
  if (!Stream_EnsureRemainingCapacity(out, 2048ull))
2106
0
    return SCARD_E_NO_MEMORY;
2107
2108
  /* Device Control Response */
2109
0
  Stream_Write_UINT32(out, 0);                            /* OutputBufferLength (4 bytes) */
2110
0
  Stream_Zero(out, SMARTCARD_COMMON_TYPE_HEADER_LENGTH);  /* CommonTypeHeader (8 bytes) */
2111
0
  Stream_Zero(out, SMARTCARD_PRIVATE_TYPE_HEADER_LENGTH); /* PrivateTypeHeader (8 bytes) */
2112
0
  Stream_Write_UINT32(out, 0);                            /* Result (4 bytes) */
2113
2114
0
  smartcard_handle_activity_begin(ctx, ioControlCode, operation->hCard);
2115
2116
  /* Call */
2117
0
  switch (ioControlCode)
2118
0
  {
2119
0
    case SCARD_IOCTL_ESTABLISHCONTEXT:
2120
0
      result = smartcard_EstablishContext_Call(ctx, out, operation);
2121
0
      break;
2122
2123
0
    case SCARD_IOCTL_RELEASECONTEXT:
2124
0
      result = smartcard_ReleaseContext_Call(ctx, out, operation);
2125
0
      break;
2126
2127
0
    case SCARD_IOCTL_ISVALIDCONTEXT:
2128
0
      result = smartcard_IsValidContext_Call(ctx, out, operation);
2129
0
      break;
2130
2131
0
    case SCARD_IOCTL_LISTREADERGROUPSA:
2132
0
      result = smartcard_ListReaderGroupsA_Call(ctx, out, operation);
2133
0
      break;
2134
2135
0
    case SCARD_IOCTL_LISTREADERGROUPSW:
2136
0
      result = smartcard_ListReaderGroupsW_Call(ctx, out, operation);
2137
0
      break;
2138
2139
0
    case SCARD_IOCTL_LISTREADERSA:
2140
0
      result = smartcard_ListReadersA_Call(ctx, out, operation);
2141
0
      break;
2142
2143
0
    case SCARD_IOCTL_LISTREADERSW:
2144
0
      result = smartcard_ListReadersW_Call(ctx, out, operation);
2145
0
      break;
2146
2147
0
    case SCARD_IOCTL_INTRODUCEREADERGROUPA:
2148
0
      result = smartcard_IntroduceReaderGroupA_Call(ctx, out, operation);
2149
0
      break;
2150
2151
0
    case SCARD_IOCTL_INTRODUCEREADERGROUPW:
2152
0
      result = smartcard_IntroduceReaderGroupW_Call(ctx, out, operation);
2153
0
      break;
2154
2155
0
    case SCARD_IOCTL_FORGETREADERGROUPA:
2156
0
      result = smartcard_ForgetReaderA_Call(ctx, out, operation);
2157
0
      break;
2158
2159
0
    case SCARD_IOCTL_FORGETREADERGROUPW:
2160
0
      result = smartcard_ForgetReaderW_Call(ctx, out, operation);
2161
0
      break;
2162
2163
0
    case SCARD_IOCTL_INTRODUCEREADERA:
2164
0
      result = smartcard_IntroduceReaderA_Call(ctx, out, operation);
2165
0
      break;
2166
2167
0
    case SCARD_IOCTL_INTRODUCEREADERW:
2168
0
      result = smartcard_IntroduceReaderW_Call(ctx, out, operation);
2169
0
      break;
2170
2171
0
    case SCARD_IOCTL_FORGETREADERA:
2172
0
      result = smartcard_ForgetReaderA_Call(ctx, out, operation);
2173
0
      break;
2174
2175
0
    case SCARD_IOCTL_FORGETREADERW:
2176
0
      result = smartcard_ForgetReaderW_Call(ctx, out, operation);
2177
0
      break;
2178
2179
0
    case SCARD_IOCTL_ADDREADERTOGROUPA:
2180
0
      result = smartcard_AddReaderToGroupA_Call(ctx, out, operation);
2181
0
      break;
2182
2183
0
    case SCARD_IOCTL_ADDREADERTOGROUPW:
2184
0
      result = smartcard_AddReaderToGroupW_Call(ctx, out, operation);
2185
0
      break;
2186
2187
0
    case SCARD_IOCTL_REMOVEREADERFROMGROUPA:
2188
0
      result = smartcard_RemoveReaderFromGroupA_Call(ctx, out, operation);
2189
0
      break;
2190
2191
0
    case SCARD_IOCTL_REMOVEREADERFROMGROUPW:
2192
0
      result = smartcard_RemoveReaderFromGroupW_Call(ctx, out, operation);
2193
0
      break;
2194
2195
0
    case SCARD_IOCTL_LOCATECARDSA:
2196
0
      result = smartcard_LocateCardsA_Call(ctx, out, operation);
2197
0
      break;
2198
2199
0
    case SCARD_IOCTL_LOCATECARDSW:
2200
0
      result = smartcard_LocateCardsW_Call(ctx, out, operation);
2201
0
      break;
2202
2203
0
    case SCARD_IOCTL_GETSTATUSCHANGEA:
2204
0
      result = smartcard_GetStatusChangeA_Call(ctx, out, operation);
2205
0
      break;
2206
2207
0
    case SCARD_IOCTL_GETSTATUSCHANGEW:
2208
0
      result = smartcard_GetStatusChangeW_Call(ctx, out, operation);
2209
0
      break;
2210
2211
0
    case SCARD_IOCTL_CANCEL:
2212
0
      result = smartcard_Cancel_Call(ctx, out, operation);
2213
0
      break;
2214
2215
0
    case SCARD_IOCTL_CONNECTA:
2216
0
      result = smartcard_ConnectA_Call(ctx, out, operation);
2217
0
      break;
2218
2219
0
    case SCARD_IOCTL_CONNECTW:
2220
0
      result = smartcard_ConnectW_Call(ctx, out, operation);
2221
0
      break;
2222
2223
0
    case SCARD_IOCTL_RECONNECT:
2224
0
      result = smartcard_Reconnect_Call(ctx, out, operation);
2225
0
      break;
2226
2227
0
    case SCARD_IOCTL_DISCONNECT:
2228
0
      result = smartcard_Disconnect_Call(ctx, out, operation);
2229
0
      break;
2230
2231
0
    case SCARD_IOCTL_BEGINTRANSACTION:
2232
0
      result = smartcard_BeginTransaction_Call(ctx, out, operation);
2233
0
      break;
2234
2235
0
    case SCARD_IOCTL_ENDTRANSACTION:
2236
0
      result = smartcard_EndTransaction_Call(ctx, out, operation);
2237
0
      break;
2238
2239
0
    case SCARD_IOCTL_STATE:
2240
0
      result = smartcard_State_Call(ctx, out, operation);
2241
0
      break;
2242
2243
0
    case SCARD_IOCTL_STATUSA:
2244
0
      result = smartcard_StatusA_Call(ctx, out, operation);
2245
0
      break;
2246
2247
0
    case SCARD_IOCTL_STATUSW:
2248
0
      result = smartcard_StatusW_Call(ctx, out, operation);
2249
0
      break;
2250
2251
0
    case SCARD_IOCTL_TRANSMIT:
2252
0
      result = smartcard_Transmit_Call(ctx, out, operation);
2253
0
      break;
2254
2255
0
    case SCARD_IOCTL_CONTROL:
2256
0
      result = smartcard_Control_Call(ctx, out, operation);
2257
0
      break;
2258
2259
0
    case SCARD_IOCTL_GETATTRIB:
2260
0
      result = smartcard_GetAttrib_Call(ctx, out, operation);
2261
0
      break;
2262
2263
0
    case SCARD_IOCTL_SETATTRIB:
2264
0
      result = smartcard_SetAttrib_Call(ctx, out, operation);
2265
0
      break;
2266
2267
0
    case SCARD_IOCTL_ACCESSSTARTEDEVENT:
2268
0
      result = smartcard_AccessStartedEvent_Call(ctx, out, operation);
2269
0
      break;
2270
2271
0
    case SCARD_IOCTL_LOCATECARDSBYATRA:
2272
0
      result = smartcard_LocateCardsByATRA_Call(ctx, out, operation);
2273
0
      break;
2274
2275
0
    case SCARD_IOCTL_LOCATECARDSBYATRW:
2276
0
      result = smartcard_LocateCardsByATRW_Call(ctx, out, operation);
2277
0
      break;
2278
2279
0
    case SCARD_IOCTL_READCACHEA:
2280
0
      result = smartcard_ReadCacheA_Call(ctx, out, operation);
2281
0
      break;
2282
2283
0
    case SCARD_IOCTL_READCACHEW:
2284
0
      result = smartcard_ReadCacheW_Call(ctx, out, operation);
2285
0
      break;
2286
2287
0
    case SCARD_IOCTL_WRITECACHEA:
2288
0
      result = smartcard_WriteCacheA_Call(ctx, out, operation);
2289
0
      break;
2290
2291
0
    case SCARD_IOCTL_WRITECACHEW:
2292
0
      result = smartcard_WriteCacheW_Call(ctx, out, operation);
2293
0
      break;
2294
2295
0
    case SCARD_IOCTL_GETTRANSMITCOUNT:
2296
0
      result = smartcard_GetTransmitCount_Call(ctx, out, operation);
2297
0
      break;
2298
2299
0
    case SCARD_IOCTL_RELEASETARTEDEVENT:
2300
0
      result = smartcard_ReleaseStartedEvent_Call(ctx, out, operation);
2301
0
      break;
2302
2303
0
    case SCARD_IOCTL_GETREADERICON:
2304
0
      result = smartcard_GetReaderIcon_Call(ctx, out, operation);
2305
0
      break;
2306
2307
0
    case SCARD_IOCTL_GETDEVICETYPEID:
2308
0
      result = smartcard_GetDeviceTypeId_Call(ctx, out, operation);
2309
0
      break;
2310
2311
0
    default:
2312
0
      result = STATUS_UNSUCCESSFUL;
2313
0
      break;
2314
0
  }
2315
2316
0
  if (smartcard_handle_activity_end(ctx, ioControlCode, operation->hCard, result))
2317
0
  {
2318
0
    WLog_Print(ctx->log, WLOG_WARN,
2319
0
               "transaction on hCard 0x%08" PRIxz
2320
0
               " granted after the server ended it, releasing it",
2321
0
               (size_t)operation->hCard);
2322
0
    const LONG rc = wrap(ctx, SCardEndTransaction, operation->hCard, SCARD_LEAVE_CARD);
2323
0
    scard_log_status_error_wlog(ctx->log, "SCardEndTransaction", rc);
2324
0
    result = SCARD_E_CANCELLED;
2325
0
  }
2326
2327
  /**
2328
   * [MS-RPCE] 2.2.6.3 Primitive Type Serialization
2329
   * The type MUST be aligned on an 8-byte boundary. If the size of the
2330
   * primitive type is not a multiple of 8 bytes, the data MUST be padded.
2331
   */
2332
2333
0
  if ((ioControlCode != SCARD_IOCTL_ACCESSSTARTEDEVENT) &&
2334
0
      (ioControlCode != SCARD_IOCTL_RELEASETARTEDEVENT))
2335
0
  {
2336
0
    offset = (RDPDR_DEVICE_IO_RESPONSE_LENGTH + RDPDR_DEVICE_IO_CONTROL_RSP_HDR_LENGTH);
2337
0
    const LONG rc = smartcard_pack_write_size_align(out, Stream_GetPosition(out) - offset, 8);
2338
0
    if (rc != SCARD_S_SUCCESS)
2339
0
      result = rc;
2340
0
  }
2341
2342
0
  if ((result != SCARD_S_SUCCESS) && (result != SCARD_E_TIMEOUT) &&
2343
0
      (result != SCARD_E_NO_READERS_AVAILABLE) && (result != SCARD_E_NO_SERVICE) &&
2344
0
      (result != SCARD_W_CACHE_ITEM_NOT_FOUND) && (result != SCARD_W_CACHE_ITEM_STALE))
2345
0
  {
2346
0
    scard_log_status_error_wlog(ctx->log, "IRP failure: %s (0x%08" PRIX32 ")", result,
2347
0
                                scard_get_ioctl_string(ioControlCode, TRUE), ioControlCode);
2348
0
  }
2349
2350
0
  *pIoStatus = STATUS_SUCCESS;
2351
2352
0
  if ((result & 0xC0000000L) == 0xC0000000L)
2353
0
  {
2354
    /* NTSTATUS error */
2355
0
    *pIoStatus = result;
2356
2357
0
    scard_log_status_error_wlog(ctx->log, "IRP failure: %s (0x%08" PRIX32 ")", result,
2358
0
                                scard_get_ioctl_string(ioControlCode, TRUE), ioControlCode);
2359
0
  }
2360
2361
0
  Stream_SealLength(out);
2362
0
  size_t outputBufferLength = Stream_Length(out);
2363
0
  size_t dataEndPos = outputBufferLength;
2364
2365
0
  WINPR_ASSERT(outputBufferLength >= RDPDR_DEVICE_IO_RESPONSE_LENGTH + 4U);
2366
0
  outputBufferLength -= (RDPDR_DEVICE_IO_RESPONSE_LENGTH + 4U);
2367
0
  WINPR_ASSERT(outputBufferLength >= RDPDR_DEVICE_IO_RESPONSE_LENGTH);
2368
0
  objectBufferLength = outputBufferLength - RDPDR_DEVICE_IO_RESPONSE_LENGTH;
2369
0
  WINPR_ASSERT(outputBufferLength <= UINT32_MAX);
2370
0
  WINPR_ASSERT(objectBufferLength <= UINT32_MAX);
2371
0
  if (!Stream_SetPosition(out, RDPDR_DEVICE_IO_RESPONSE_LENGTH))
2372
0
    return SCARD_E_BAD_SEEK;
2373
2374
  /* [MS-RDPESC] 3.2.5.2 Processing Incoming Replies
2375
   *
2376
   * if the output buffer is too small, reply with STATUS_BUFFER_TOO_SMALL
2377
   * and a outputBufferLength of 0.
2378
   * The message should then be retransmitted from the server with a doubled
2379
   * buffer size.
2380
   */
2381
0
  if (outputBufferLength > operation->outputBufferLength)
2382
0
  {
2383
0
    WLog_Print(ctx->log, WLOG_DEBUG,
2384
0
               "IRP  warn: expected outputBufferLength %" PRIuz ", but current limit %" PRIu32
2385
0
               ", respond with STATUS_BUFFER_TOO_SMALL for retransmit with larger buffer",
2386
0
               outputBufferLength, operation->outputBufferLength);
2387
2388
0
    *pIoStatus = STATUS_BUFFER_TOO_SMALL;
2389
0
    result = *pIoStatus;
2390
0
    outputBufferLength = 0;
2391
0
    objectBufferLength = 0;
2392
0
  }
2393
0
  else
2394
0
  {
2395
0
    WLog_Print(ctx->log, WLOG_TRACE, "IRP trace: outputBufferLength %" PRIuz ", limit %" PRIu32,
2396
0
               outputBufferLength, operation->outputBufferLength);
2397
0
  }
2398
2399
  /* Device Control Response */
2400
0
  Stream_Write_UINT32(out, (UINT32)outputBufferLength); /* OutputBufferLength (4 bytes) */
2401
0
  smartcard_pack_common_type_header(out);               /* CommonTypeHeader (8 bytes) */
2402
0
  smartcard_pack_private_type_header(
2403
0
      out, (UINT32)objectBufferLength); /* PrivateTypeHeader (8 bytes) */
2404
0
  Stream_Write_INT32(out, result);      /* Result (4 bytes) */
2405
0
  if (result == STATUS_BUFFER_TOO_SMALL)
2406
0
    Stream_SealLength(out);
2407
0
  else if (!Stream_SetPosition(out, dataEndPos))
2408
0
    return SCARD_E_BAD_SEEK;
2409
0
  return SCARD_S_SUCCESS;
2410
0
}
2411
2412
void context_free(void* arg)
2413
0
{
2414
0
  struct s_scard_context_element* element = arg;
2415
0
  if (!arg)
2416
0
    return;
2417
2418
0
  if (element->fn_free)
2419
0
    element->fn_free(element->context);
2420
0
  free(element);
2421
0
}
2422
2423
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
2424
scard_call_context* smartcard_call_context_new(const rdpSettings* settings)
2425
0
{
2426
0
  const freerdp* inst = freerdp_settings_get_pointer(settings, FreeRDP_instance);
2427
0
  if (!inst || !inst->context)
2428
0
    return nullptr;
2429
0
  return smartcard_call_context_new_with_context(inst->context);
2430
0
}
2431
#endif
2432
2433
scard_call_context* smartcard_call_context_new_with_context(rdpContext* context)
2434
0
{
2435
0
  WINPR_ASSERT(context);
2436
0
  scard_call_context* ctx = calloc(1, sizeof(scard_call_context));
2437
0
  if (!ctx)
2438
0
    goto fail;
2439
2440
0
  ctx->context = context;
2441
2442
0
  const rdpSettings* settings = context->settings;
2443
0
  WINPR_ASSERT(settings);
2444
2445
0
  ctx->log = WLog_Get(SCARD_TAG);
2446
0
  WINPR_ASSERT(ctx->log);
2447
2448
0
  ctx->stopEvent = CreateEventA(nullptr, TRUE, FALSE, nullptr);
2449
0
  if (!ctx->stopEvent)
2450
0
    goto fail;
2451
2452
0
  ctx->names = LinkedList_New();
2453
0
  if (!ctx->names)
2454
0
    goto fail;
2455
2456
0
#if defined(WITH_SMARTCARD_EMULATE)
2457
0
  ctx->useEmulatedCard = freerdp_settings_get_bool(settings, FreeRDP_SmartcardEmulation);
2458
0
#endif
2459
2460
0
  if (ctx->useEmulatedCard)
2461
0
  {
2462
0
#if defined(WITH_SMARTCARD_EMULATE)
2463
0
    ctx->emulation = Emulate_New(settings);
2464
0
    if (!ctx->emulation)
2465
0
      goto fail;
2466
#else
2467
    WLog_Print(ctx->log, WLOG_ERROR, "Smartcard emulation requested, but not supported!");
2468
    goto fail;
2469
#endif
2470
0
  }
2471
0
  else
2472
0
  {
2473
0
    const char* WinSCardModule = freerdp_settings_get_string(settings, FreeRDP_WinSCardModule);
2474
0
    if (WinSCardModule)
2475
0
    {
2476
0
      ctx->hWinSCardLibrary = LoadLibraryX(WinSCardModule);
2477
2478
0
      if (!ctx->hWinSCardLibrary)
2479
0
      {
2480
0
        WLog_Print(ctx->log, WLOG_ERROR, "Failed to load WinSCard library: '%s'",
2481
0
                   WinSCardModule);
2482
0
        goto fail;
2483
0
      }
2484
2485
0
      if (!WinSCard_LoadApiTableFunctions(&ctx->WinSCardApi, ctx->hWinSCardLibrary))
2486
0
        goto fail;
2487
0
      ctx->pWinSCardApi = &ctx->WinSCardApi;
2488
0
    }
2489
0
    else
2490
0
    {
2491
0
      ctx->pWinSCardApi = WinPR_GetSCardApiFunctionTable();
2492
0
    }
2493
2494
0
    if (!ctx->pWinSCardApi)
2495
0
    {
2496
0
      WLog_Print(ctx->log, WLOG_ERROR, "Failed to load WinSCard API!");
2497
0
      goto fail;
2498
0
    }
2499
0
  }
2500
2501
0
  ctx->rgSCardContextList = HashTable_New(FALSE);
2502
0
  if (!ctx->rgSCardContextList)
2503
0
    goto fail;
2504
2505
0
  {
2506
0
    wObject* obj = HashTable_ValueObject(ctx->rgSCardContextList);
2507
0
    WINPR_ASSERT(obj);
2508
0
    obj->fnObjectFree = context_free;
2509
0
  }
2510
2511
0
  if (!smartcard_transaction_watchdog_start(ctx))
2512
0
  {
2513
0
    WLog_Print(ctx->log, WLOG_ERROR, "Failed to start idle transaction watchdog!");
2514
0
    goto fail;
2515
0
  }
2516
2517
0
  return ctx;
2518
0
fail:
2519
0
  WINPR_PRAGMA_DIAG_PUSH
2520
0
  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
2521
0
  smartcard_call_context_free(ctx);
2522
0
  WINPR_PRAGMA_DIAG_POP
2523
0
  return nullptr;
2524
0
}
2525
2526
void smartcard_call_context_free(scard_call_context* ctx)
2527
0
{
2528
0
  if (!ctx)
2529
0
    return;
2530
2531
0
  smartcard_call_context_signal_stop(ctx, FALSE);
2532
2533
  /* uses the WinSCard API */
2534
0
  smartcard_transaction_watchdog_stop(ctx);
2535
2536
0
  LinkedList_Free(ctx->names);
2537
0
  if (ctx->StartedEvent)
2538
0
  {
2539
0
    WINPR_ASSERT(ctx->useEmulatedCard || ctx->pWinSCardApi);
2540
0
    wrap_raw(ctx, SCardReleaseStartedEvent);
2541
0
  }
2542
2543
0
  if (ctx->useEmulatedCard)
2544
0
  {
2545
0
#ifdef WITH_SMARTCARD_EMULATE
2546
0
    if (ctx->emulation)
2547
0
    {
2548
0
      Emulate_Free(ctx->emulation);
2549
0
      ctx->emulation = nullptr;
2550
0
    }
2551
0
#endif
2552
0
  }
2553
2554
0
  if (ctx->hWinSCardLibrary)
2555
0
  {
2556
0
    ZeroMemory(&ctx->WinSCardApi, sizeof(SCardApiFunctionTable));
2557
0
    FreeLibrary(ctx->hWinSCardLibrary);
2558
0
    ctx->hWinSCardLibrary = nullptr;
2559
0
  }
2560
2561
0
  ctx->pWinSCardApi = nullptr;
2562
2563
0
  HashTable_Free(ctx->rgSCardContextList);
2564
0
  HashTable_Free(ctx->handleStates);
2565
0
  (void)CloseHandle(ctx->stopEvent);
2566
0
  free(ctx);
2567
0
}
2568
2569
BOOL smartcard_call_context_add(scard_call_context* ctx, const char* name)
2570
0
{
2571
0
  WINPR_ASSERT(ctx);
2572
0
  WINPR_ASSERT(name);
2573
0
  return LinkedList_AddLast(ctx->names, name);
2574
0
}
2575
2576
BOOL smartcard_call_cancel_context(scard_call_context* ctx, SCARDCONTEXT hContext)
2577
0
{
2578
0
  WINPR_ASSERT(ctx);
2579
0
  if (wrap(ctx, SCardIsValidContext, hContext) == SCARD_S_SUCCESS)
2580
0
  {
2581
0
    wrap(ctx, SCardCancel, hContext);
2582
0
  }
2583
0
  return TRUE;
2584
0
}
2585
2586
BOOL smartcard_call_release_context(scard_call_context* ctx, SCARDCONTEXT hContext)
2587
0
{
2588
0
  WINPR_ASSERT(ctx);
2589
0
  wrap(ctx, SCardReleaseContext, hContext);
2590
0
  return TRUE;
2591
0
}
2592
2593
BOOL smartcard_call_cancel_all_context(scard_call_context* ctx)
2594
0
{
2595
0
  if (!ctx)
2596
0
    return FALSE;
2597
2598
0
  HashTable_Clear(ctx->rgSCardContextList);
2599
0
  return TRUE;
2600
0
}
2601
2602
BOOL smarcard_call_set_callbacks(scard_call_context* ctx, void* userdata,
2603
                                 void* (*fn_new)(void*, SCARDCONTEXT), void (*fn_free)(void*))
2604
0
{
2605
0
  WINPR_ASSERT(ctx);
2606
0
  ctx->userdata = userdata;
2607
0
  ctx->fn_new = fn_new;
2608
0
  ctx->fn_free = fn_free;
2609
0
  return TRUE;
2610
0
}
2611
2612
void* smartcard_call_get_context(scard_call_context* ctx, SCARDCONTEXT hContext)
2613
0
{
2614
0
  struct s_scard_context_element* element = nullptr;
2615
2616
0
  WINPR_ASSERT(ctx);
2617
0
  element = HashTable_GetItemValue(ctx->rgSCardContextList, (void*)hContext);
2618
0
  if (!element)
2619
0
    return nullptr;
2620
0
  return element->context;
2621
0
}
2622
2623
BOOL smartcard_call_is_configured(scard_call_context* ctx)
2624
0
{
2625
0
  WINPR_ASSERT(ctx);
2626
2627
0
#if defined(WITH_SMARTCARD_EMULATE)
2628
0
  if (ctx->useEmulatedCard)
2629
0
    return Emulate_IsConfigured(ctx->emulation);
2630
0
#endif
2631
2632
0
  return FALSE;
2633
0
}
2634
2635
static BOOL smartcard_cancel_context_wait(const void* key, WINPR_ATTR_UNUSED void* value, void* arg)
2636
0
{
2637
0
  scard_call_context* ctx = arg;
2638
0
  WINPR_ASSERT(ctx);
2639
2640
0
  const LONG rc = wrap_raw(ctx, SCardCancel, (SCARDCONTEXT)key);
2641
0
  if (rc != SCARD_S_SUCCESS)
2642
0
    WLog_Print(ctx->log, WLOG_DEBUG, "SCardCancel failed with %s", SCardGetErrorString(rc));
2643
0
  return TRUE;
2644
0
}
2645
2646
BOOL smartcard_call_context_signal_stop(scard_call_context* ctx, BOOL reset)
2647
0
{
2648
0
  WINPR_ASSERT(ctx);
2649
2650
0
  if (!ctx->stopEvent)
2651
0
    return TRUE;
2652
2653
0
  if (reset)
2654
0
    return ResetEvent(ctx->stopEvent);
2655
2656
0
  if (!SetEvent(ctx->stopEvent))
2657
0
    return FALSE;
2658
2659
  /* end pending SCardGetStatusChange calls */
2660
0
  if (ctx->rgSCardContextList && (ctx->useEmulatedCard || ctx->pWinSCardApi))
2661
0
    return HashTable_Foreach(ctx->rgSCardContextList, smartcard_cancel_context_wait, ctx);
2662
0
  return TRUE;
2663
0
}