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