FreeRDP
Loading...
Searching...
No Matches
TestSmartcardOperations.c
1
19#include <stdio.h>
20
21#include <winpr/crt.h>
22#include <winpr/stream.h>
23#include <winpr/smartcard.h>
24
25#include <freerdp/channels/rdpdr.h>
26#include <freerdp/channels/scard.h>
27#include <freerdp/utils/rdpdr_utils.h>
28#include <freerdp/utils/smartcard_operations.h>
29#include <freerdp/utils/smartcard_pack.h>
30
31static inline void fill_redir_context(REDIR_SCARDCONTEXT* ctx, SCARDCONTEXT val)
32{
33 smartcard_scard_context_native_to_redir(ctx, val);
34}
35
36static inline void fill_redir_handle(REDIR_SCARDHANDLE* handle, SCARDHANDLE val)
37{
38 smartcard_scard_handle_native_to_redir(handle, val);
39}
40
41WINPR_ATTR_MALLOC(Stream_Free, 1)
42static wStream* build_device_control_request(UINT32 ioControlCode, const void* inputBuffer,
43 size_t inputBufferLength)
44{
45 const char* name = scard_get_ioctl_string(ioControlCode, TRUE);
46 WINPR_ASSERT(name);
47
48 const size_t totalLength = RDPDR_DEVICE_IO_CONTROL_REQ_HDR_LENGTH + inputBufferLength;
49
50 wStream* s = Stream_New(nullptr, totalLength);
51 if (!s)
52 {
53 (void)fprintf(stderr, "build_device_control_request(%s): Stream_New(%" PRIuz ") failed\n",
54 name, totalLength);
55 return nullptr;
56 }
57
58 UINT32 inputLength = WINPR_ASSERTING_INT_CAST(UINT32, inputBufferLength);
59 Stream_Write_UINT32(s, 2048); /* OutputBufferLength */
60 Stream_Write_UINT32(s, inputLength); /* InputBufferLength */
61 Stream_Write_UINT32(s, ioControlCode); /* IoControlCode */
62 Stream_Zero(s, 20); /* Padding */
63
64 if (inputBufferLength > 0)
65 Stream_Write(s, inputBuffer, inputBufferLength);
66
67 Stream_SealLength(s);
68 if (!Stream_SetPosition(s, 0))
69 {
70 (void)fprintf(stderr, "build_device_control_request(%s): Stream_SetPosition failed\n",
71 name);
72 Stream_Release(s);
73 return nullptr;
74 }
75
76 return s;
77}
78
79WINPR_ATTR_NODISCARD
80static BOOL test_request_roundtrip(const SMARTCARD_OPERATION* opIn, SMARTCARD_OPERATION* opOut)
81{
82 const UINT32 ioControlCode = opIn->ioControlCode;
83 const char* name = scard_get_ioctl_string(ioControlCode, TRUE);
84 WINPR_ASSERT(name);
85
86 wStream* sEnc = Stream_New(nullptr, 4096);
87 if (!sEnc)
88 {
89 (void)fprintf(stderr, "%s: Stream_New for encode failed\n", name);
90 return FALSE;
91 }
92
93 LONG status = smartcard_irp_device_control_encode_request(sEnc, opIn);
94 if (status != SCARD_S_SUCCESS)
95 {
96 (void)fprintf(stderr, "%s: encode_request failed with 0x%08" PRIX32 "\n", name,
97 (UINT32)status);
98 Stream_Release(sEnc);
99 return FALSE;
100 }
101
102 wStream* sDec =
103 build_device_control_request(ioControlCode, Stream_Buffer(sEnc), Stream_Length(sEnc));
104 Stream_Release(sEnc);
105 if (!sDec)
106 return FALSE;
107
108 const SMARTCARD_OPERATION empty = WINPR_C_ARRAY_INIT;
109 *opOut = empty;
110 status = smartcard_irp_device_control_decode_request(sDec, 1, 0, opOut);
111 Stream_Release(sDec);
112 if (status != SCARD_S_SUCCESS)
113 {
114 (void)fprintf(stderr, "%s: decode_request failed with 0x%08" PRIX32 "\n", name,
115 (UINT32)status);
116 return FALSE;
117 }
118
119 if (opOut->ioControlCode != ioControlCode)
120 {
121 (void)fprintf(stderr, "%s: ioControlCode mismatch: 0x%08" PRIX32 " != 0x%08" PRIX32 "\n",
122 name, opOut->ioControlCode, ioControlCode);
123 smartcard_operation_free(opOut, FALSE);
124 return FALSE;
125 }
126
127 return TRUE;
128}
129
130WINPR_ATTR_NODISCARD
131static inline BOOL check_field(const char* name, const char* field, UINT32 a, UINT32 b)
132{
133 if (a != b)
134 {
135 (void)fprintf(stderr, "%s: %s mismatch: 0x%" PRIX32 " != 0x%" PRIX32 "\n", name, field, a,
136 b);
137 return FALSE;
138 }
139 return TRUE;
140}
141
142WINPR_ATTR_NODISCARD
143static inline BOOL check_bytes(const char* name, const char* field, const void* a, const void* b,
144 size_t len)
145{
146 if (!a || !b || memcmp(a, b, len) != 0)
147 {
148 (void)fprintf(stderr, "%s: %s mismatch (%" PRIuz " bytes)\n", name, field, len);
149 return FALSE;
150 }
151 return TRUE;
152}
153
154WINPR_ATTR_NODISCARD
155static inline BOOL check_redir_context(const char* name, const REDIR_SCARDCONTEXT* a,
156 const REDIR_SCARDCONTEXT* b)
157{
158 return check_field(name, "hContext.cbContext", a->cbContext, b->cbContext) &&
159 check_bytes(name, "hContext.pbContext", a->pbContext, b->pbContext, a->cbContext);
160}
161
162WINPR_ATTR_NODISCARD
163static inline BOOL check_redir_handle(const char* name, const REDIR_SCARDHANDLE* a,
164 const REDIR_SCARDHANDLE* b)
165{
166 return check_field(name, "hCard.cbHandle", a->cbHandle, b->cbHandle) &&
167 check_bytes(name, "hCard.pbHandle", a->pbHandle, b->pbHandle, a->cbHandle);
168}
169
170WINPR_ATTR_NODISCARD
171static BOOL test_establish_context_encode_decode_request(void)
172{
173 BOOL success = FALSE;
174 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
175 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
176 opIn.ioControlCode = SCARD_IOCTL_ESTABLISHCONTEXT;
177 opIn.call.establishContext.dwScope = SCARD_SCOPE_SYSTEM;
178
179 if (!test_request_roundtrip(&opIn, &opOut))
180 goto out;
181 if (!check_field("EstablishContext", "dwScope", opIn.call.establishContext.dwScope,
182 opOut.call.establishContext.dwScope))
183 goto out;
184
185 success = TRUE;
186out:
187 smartcard_operation_free(&opIn, FALSE);
188 smartcard_operation_free(&opOut, FALSE);
189 return success;
190}
191
192WINPR_ATTR_NODISCARD
193static BOOL test_release_context_encode_decode_request(void)
194{
195 BOOL success = FALSE;
196 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
197 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
198 opIn.ioControlCode = SCARD_IOCTL_RELEASECONTEXT;
199 fill_redir_context(&opIn.call.context.handles.hContext, 0x1234);
200
201 if (!test_request_roundtrip(&opIn, &opOut))
202 goto out;
203 if (!check_redir_context("ReleaseContext", &opIn.call.context.handles.hContext,
204 &opOut.call.context.handles.hContext))
205 goto out;
206
207 success = TRUE;
208out:
209 smartcard_operation_free(&opIn, FALSE);
210 smartcard_operation_free(&opOut, FALSE);
211 return success;
212}
213
214WINPR_ATTR_NODISCARD
215static BOOL test_is_valid_context_encode_decode_request(void)
216{
217 BOOL success = FALSE;
218 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
219 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
220 opIn.ioControlCode = SCARD_IOCTL_ISVALIDCONTEXT;
221 fill_redir_context(&opIn.call.context.handles.hContext, 0x5678);
222
223 if (!test_request_roundtrip(&opIn, &opOut))
224 goto out;
225 if (!check_redir_context("IsValidContext", &opIn.call.context.handles.hContext,
226 &opOut.call.context.handles.hContext))
227 goto out;
228
229 success = TRUE;
230out:
231 smartcard_operation_free(&opIn, FALSE);
232 smartcard_operation_free(&opOut, FALSE);
233 return success;
234}
235
236WINPR_ATTR_NODISCARD
237static BOOL test_list_reader_groups_encode_decode_request_impl(UINT32 ioControlCode)
238{
239 BOOL success = FALSE;
240 const char* n = scard_get_ioctl_string(ioControlCode, TRUE);
241 WINPR_ASSERT(n);
242
243 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
244 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
245 opIn.ioControlCode = ioControlCode;
246 fill_redir_context(&opIn.call.listReaderGroups.handles.hContext, 0x1234);
247 opIn.call.listReaderGroups.fmszGroupsIsNULL = 0;
248 opIn.call.listReaderGroups.cchGroups = SCARD_AUTOALLOCATE;
249
250 if (!test_request_roundtrip(&opIn, &opOut))
251 goto out;
252 if (!check_redir_context(n, &opIn.call.listReaderGroups.handles.hContext,
253 &opOut.call.listReaderGroups.handles.hContext))
254 goto out;
255 if (!check_field(n, "fmszGroupsIsNULL", opIn.call.listReaderGroups.fmszGroupsIsNULL,
256 opOut.call.listReaderGroups.fmszGroupsIsNULL))
257 goto out;
258 if (!check_field(n, "cchGroups", opIn.call.listReaderGroups.cchGroups,
259 opOut.call.listReaderGroups.cchGroups))
260 goto out;
261
262 success = TRUE;
263out:
264 smartcard_operation_free(&opIn, FALSE);
265 smartcard_operation_free(&opOut, FALSE);
266 return success;
267}
268
269WINPR_ATTR_NODISCARD
270static BOOL test_list_reader_groups_a_encode_decode_request(void)
271{
272 return test_list_reader_groups_encode_decode_request_impl(SCARD_IOCTL_LISTREADERGROUPSA);
273}
274
275WINPR_ATTR_NODISCARD
276static BOOL test_list_reader_groups_w_encode_decode_request(void)
277{
278 return test_list_reader_groups_encode_decode_request_impl(SCARD_IOCTL_LISTREADERGROUPSW);
279}
280
281WINPR_ATTR_NODISCARD
282static BOOL test_list_readers_encode_decode_request_impl(UINT32 ioControlCode)
283{
284 BOOL success = FALSE;
285 const char* n = scard_get_ioctl_string(ioControlCode, TRUE);
286 WINPR_ASSERT(n);
287
288 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
289 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
290 opIn.ioControlCode = ioControlCode;
291 fill_redir_context(&opIn.call.listReaders.handles.hContext, 0x1234);
292 opIn.call.listReaders.fmszReadersIsNULL = 0;
293 opIn.call.listReaders.cchReaders = SCARD_AUTOALLOCATE;
294
295 if (!test_request_roundtrip(&opIn, &opOut))
296 goto out;
297 if (!check_redir_context(n, &opIn.call.listReaders.handles.hContext,
298 &opOut.call.listReaders.handles.hContext))
299 goto out;
300 if (!check_field(n, "cchReaders", opIn.call.listReaders.cchReaders,
301 opOut.call.listReaders.cchReaders))
302 goto out;
303
304 success = TRUE;
305out:
306 smartcard_operation_free(&opIn, FALSE);
307 smartcard_operation_free(&opOut, FALSE);
308 return success;
309}
310
311WINPR_ATTR_NODISCARD
312static BOOL test_list_readers_a_encode_decode_request(void)
313{
314 return test_list_readers_encode_decode_request_impl(SCARD_IOCTL_LISTREADERSA);
315}
316
317WINPR_ATTR_NODISCARD
318static BOOL test_list_readers_w_encode_decode_request(void)
319{
320 return test_list_readers_encode_decode_request_impl(SCARD_IOCTL_LISTREADERSW);
321}
322
323WINPR_ATTR_NODISCARD
324static BOOL test_cancel_encode_decode_request(void)
325{
326 BOOL success = FALSE;
327 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
328 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
329 opIn.ioControlCode = SCARD_IOCTL_CANCEL;
330 fill_redir_context(&opIn.call.context.handles.hContext, 0xABCD);
331
332 if (!test_request_roundtrip(&opIn, &opOut))
333 goto out;
334 if (!check_redir_context("Cancel", &opIn.call.context.handles.hContext,
335 &opOut.call.context.handles.hContext))
336 goto out;
337
338 success = TRUE;
339out:
340 smartcard_operation_free(&opIn, FALSE);
341 smartcard_operation_free(&opOut, FALSE);
342 return success;
343}
344
345WINPR_ATTR_NODISCARD
346static BOOL test_connect_a_encode_decode_request(void)
347{
348 BOOL success = FALSE;
349 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
350 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
351 opIn.ioControlCode = SCARD_IOCTL_CONNECTA;
352 fill_redir_context(&opIn.call.connectA.Common.handles.hContext, 0x1234);
353 opIn.call.connectA.Common.dwShareMode = SCARD_SHARE_SHARED;
354 opIn.call.connectA.Common.dwPreferredProtocols = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
355 opIn.call.connectA.szReader = _strdup("TestReader");
356
357 if (!test_request_roundtrip(&opIn, &opOut))
358 goto out;
359 if (!check_redir_context("ConnectA", &opIn.call.connectA.Common.handles.hContext,
360 &opOut.call.connectA.Common.handles.hContext))
361 goto out;
362 if (!check_field("ConnectA", "dwShareMode", opIn.call.connectA.Common.dwShareMode,
363 opOut.call.connectA.Common.dwShareMode))
364 goto out;
365 if (!check_field("ConnectA", "dwPreferredProtocols",
366 opIn.call.connectA.Common.dwPreferredProtocols,
367 opOut.call.connectA.Common.dwPreferredProtocols))
368 goto out;
369 if (!check_bytes("ConnectA", "szReader", opIn.call.connectA.szReader,
370 opOut.call.connectA.szReader, strlen(opIn.call.connectA.szReader) + 1))
371 goto out;
372
373 success = TRUE;
374out:
375 smartcard_operation_free(&opIn, FALSE);
376 smartcard_operation_free(&opOut, FALSE);
377 return success;
378}
379
380WINPR_ATTR_NODISCARD
381static BOOL test_connect_w_encode_decode_request(void)
382{
383 BOOL success = FALSE;
384 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
385 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
386 opIn.ioControlCode = SCARD_IOCTL_CONNECTW;
387 fill_redir_context(&opIn.call.connectW.Common.handles.hContext, 0x1234);
388 opIn.call.connectW.Common.dwShareMode = SCARD_SHARE_SHARED;
389 opIn.call.connectW.Common.dwPreferredProtocols = SCARD_PROTOCOL_T0;
390 opIn.call.connectW.szReader = ConvertUtf8ToWCharAlloc("TestReader", nullptr);
391
392 if (!test_request_roundtrip(&opIn, &opOut))
393 goto out;
394 if (!check_redir_context("ConnectW", &opIn.call.connectW.Common.handles.hContext,
395 &opOut.call.connectW.Common.handles.hContext))
396 goto out;
397 if (!check_field("ConnectW", "dwShareMode", opIn.call.connectW.Common.dwShareMode,
398 opOut.call.connectW.Common.dwShareMode))
399 goto out;
400 if (!check_bytes("ConnectW", "szReader", opIn.call.connectW.szReader,
401 opOut.call.connectW.szReader,
402 (_wcslen(opIn.call.connectW.szReader) + 1) * sizeof(WCHAR)))
403 goto out;
404
405 success = TRUE;
406out:
407 smartcard_operation_free(&opIn, FALSE);
408 smartcard_operation_free(&opOut, FALSE);
409 return success;
410}
411
412WINPR_ATTR_NODISCARD
413static BOOL test_reconnect_encode_decode_request(void)
414{
415 BOOL success = FALSE;
416 const char* n = "Reconnect";
417 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
418 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
419 opIn.ioControlCode = SCARD_IOCTL_RECONNECT;
420 fill_redir_context(&opIn.call.reconnect.handles.hContext, 0x1234);
421 fill_redir_handle(&opIn.call.reconnect.handles.hCard, 0x5678);
422 opIn.call.reconnect.dwShareMode = SCARD_SHARE_EXCLUSIVE;
423 opIn.call.reconnect.dwPreferredProtocols = SCARD_PROTOCOL_T1;
424 opIn.call.reconnect.dwInitialization = SCARD_LEAVE_CARD;
425
426 if (!test_request_roundtrip(&opIn, &opOut))
427 goto out;
428 if (!check_redir_context(n, &opIn.call.reconnect.handles.hContext,
429 &opOut.call.reconnect.handles.hContext))
430 goto out;
431 if (!check_redir_handle(n, &opIn.call.reconnect.handles.hCard,
432 &opOut.call.reconnect.handles.hCard))
433 goto out;
434 if (!check_field(n, "dwShareMode", opIn.call.reconnect.dwShareMode,
435 opOut.call.reconnect.dwShareMode))
436 goto out;
437 if (!check_field(n, "dwPreferredProtocols", opIn.call.reconnect.dwPreferredProtocols,
438 opOut.call.reconnect.dwPreferredProtocols))
439 goto out;
440 if (!check_field(n, "dwInitialization", opIn.call.reconnect.dwInitialization,
441 opOut.call.reconnect.dwInitialization))
442 goto out;
443
444 success = TRUE;
445out:
446 smartcard_operation_free(&opIn, FALSE);
447 smartcard_operation_free(&opOut, FALSE);
448 return success;
449}
450
451WINPR_ATTR_NODISCARD
452static BOOL test_hcard_and_disposition_encode_decode_request_impl(UINT32 ioControlCode,
453 DWORD dwDisposition)
454{
455 BOOL success = FALSE;
456 const char* n = scard_get_ioctl_string(ioControlCode, TRUE);
457 WINPR_ASSERT(n);
458
459 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
460 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
461 opIn.ioControlCode = ioControlCode;
462 fill_redir_context(&opIn.call.hCardAndDisposition.handles.hContext, 0x1234);
463 fill_redir_handle(&opIn.call.hCardAndDisposition.handles.hCard, 0x5678);
464 opIn.call.hCardAndDisposition.dwDisposition = dwDisposition;
465
466 if (!test_request_roundtrip(&opIn, &opOut))
467 goto out;
468 if (!check_redir_context(n, &opIn.call.hCardAndDisposition.handles.hContext,
469 &opOut.call.hCardAndDisposition.handles.hContext))
470 goto out;
471 if (!check_redir_handle(n, &opIn.call.hCardAndDisposition.handles.hCard,
472 &opOut.call.hCardAndDisposition.handles.hCard))
473 goto out;
474 if (!check_field(n, "dwDisposition", opIn.call.hCardAndDisposition.dwDisposition,
475 opOut.call.hCardAndDisposition.dwDisposition))
476 goto out;
477
478 success = TRUE;
479out:
480 smartcard_operation_free(&opIn, FALSE);
481 smartcard_operation_free(&opOut, FALSE);
482 return success;
483}
484
485WINPR_ATTR_NODISCARD
486static BOOL test_disconnect_encode_decode_request(void)
487{
488 return test_hcard_and_disposition_encode_decode_request_impl(SCARD_IOCTL_DISCONNECT,
489 SCARD_LEAVE_CARD);
490}
491
492WINPR_ATTR_NODISCARD
493static BOOL test_begin_transaction_encode_decode_request(void)
494{
495 return test_hcard_and_disposition_encode_decode_request_impl(SCARD_IOCTL_BEGINTRANSACTION,
496 SCARD_LEAVE_CARD);
497}
498
499WINPR_ATTR_NODISCARD
500static BOOL test_end_transaction_encode_decode_request(void)
501{
502 return test_hcard_and_disposition_encode_decode_request_impl(SCARD_IOCTL_ENDTRANSACTION,
503 SCARD_RESET_CARD);
504}
505
506WINPR_ATTR_NODISCARD
507static BOOL test_status_encode_decode_request_impl(UINT32 ioControlCode)
508{
509 BOOL success = FALSE;
510 const char* n = scard_get_ioctl_string(ioControlCode, TRUE);
511 WINPR_ASSERT(n);
512
513 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
514 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
515 opIn.ioControlCode = ioControlCode;
516 fill_redir_context(&opIn.call.status.handles.hContext, 0x1234);
517 fill_redir_handle(&opIn.call.status.handles.hCard, 0x5678);
518 opIn.call.status.fmszReaderNamesIsNULL = 0;
519 opIn.call.status.cchReaderLen = SCARD_AUTOALLOCATE;
520 opIn.call.status.cbAtrLen = 36;
521
522 if (!test_request_roundtrip(&opIn, &opOut))
523 goto out;
524 if (!check_redir_context(n, &opIn.call.status.handles.hContext,
525 &opOut.call.status.handles.hContext))
526 goto out;
527 if (!check_redir_handle(n, &opIn.call.status.handles.hCard, &opOut.call.status.handles.hCard))
528 goto out;
529 if (!check_field(n, "fmszReaderNamesIsNULL", opIn.call.status.fmszReaderNamesIsNULL,
530 opOut.call.status.fmszReaderNamesIsNULL))
531 goto out;
532 if (!check_field(n, "cchReaderLen", opIn.call.status.cchReaderLen,
533 opOut.call.status.cchReaderLen))
534 goto out;
535 if (!check_field(n, "cbAtrLen", opIn.call.status.cbAtrLen, opOut.call.status.cbAtrLen))
536 goto out;
537
538 success = TRUE;
539out:
540 smartcard_operation_free(&opIn, FALSE);
541 smartcard_operation_free(&opOut, FALSE);
542 return success;
543}
544
545WINPR_ATTR_NODISCARD
546static BOOL test_status_a_encode_decode_request(void)
547{
548 return test_status_encode_decode_request_impl(SCARD_IOCTL_STATUSA);
549}
550
551WINPR_ATTR_NODISCARD
552static BOOL test_status_w_encode_decode_request(void)
553{
554 return test_status_encode_decode_request_impl(SCARD_IOCTL_STATUSW);
555}
556
557WINPR_ATTR_NODISCARD
558static BOOL test_transmit_encode_decode_request(void)
559{
560 BOOL success = FALSE;
561 BYTE sendBuf[] = { 0x00, 0xA4, 0x04, 0x00, 0x07 };
562 SCARD_IO_REQUEST pioSendPci = { .dwProtocol = SCARD_PROTOCOL_T0, .cbPciLength = 8 };
563 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
564 opIn.ioControlCode = SCARD_IOCTL_TRANSMIT;
565 fill_redir_context(&opIn.call.transmit.handles.hContext, 0x1234);
566 fill_redir_handle(&opIn.call.transmit.handles.hCard, 0x5678);
567 opIn.call.transmit.pioSendPci = &pioSendPci;
568 opIn.call.transmit.cbSendLength = sizeof(sendBuf);
569 opIn.call.transmit.pbSendBuffer = sendBuf;
570 opIn.call.transmit.fpbRecvBufferIsNULL = 0;
571 opIn.call.transmit.cbRecvLength = 256;
572
573 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
574 if (!test_request_roundtrip(&opIn, &opOut))
575 goto out;
576 if (!check_redir_context("Transmit", &opIn.call.transmit.handles.hContext,
577 &opOut.call.transmit.handles.hContext))
578 goto out;
579 if (!check_redir_handle("Transmit", &opIn.call.transmit.handles.hCard,
580 &opOut.call.transmit.handles.hCard))
581 goto out;
582 if (!check_field("Transmit", "cbSendLength", opIn.call.transmit.cbSendLength,
583 opOut.call.transmit.cbSendLength))
584 goto out;
585 if (!check_bytes("Transmit", "pbSendBuffer", opIn.call.transmit.pbSendBuffer,
586 opOut.call.transmit.pbSendBuffer, opIn.call.transmit.cbSendLength))
587 goto out;
588 if (!check_field("Transmit", "fpbRecvBufferIsNULL",
589 (UINT32)opIn.call.transmit.fpbRecvBufferIsNULL,
590 (UINT32)opOut.call.transmit.fpbRecvBufferIsNULL))
591 goto out;
592 if (!check_field("Transmit", "cbRecvLength", opIn.call.transmit.cbRecvLength,
593 opOut.call.transmit.cbRecvLength))
594 goto out;
595 if (!check_field("Transmit", "pioSendPci.dwProtocol", opIn.call.transmit.pioSendPci->dwProtocol,
596 opOut.call.transmit.pioSendPci->dwProtocol))
597 goto out;
598
599 success = TRUE;
600out:
601 opIn.call.transmit.pioSendPci = nullptr;
602 opIn.call.transmit.pbSendBuffer = nullptr;
603 smartcard_operation_free(&opIn, FALSE);
604 smartcard_operation_free(&opOut, FALSE);
605 return success;
606}
607
608WINPR_ATTR_NODISCARD
609static BOOL test_control_encode_decode_request(void)
610{
611 BOOL success = FALSE;
612 BYTE inBuf[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
613 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
614 opIn.ioControlCode = SCARD_IOCTL_CONTROL;
615 fill_redir_context(&opIn.call.control.handles.hContext, 0x1234);
616 fill_redir_handle(&opIn.call.control.handles.hCard, 0x5678);
617 opIn.call.control.dwControlCode = 0x42000001;
618 opIn.call.control.cbInBufferSize = sizeof(inBuf);
619 opIn.call.control.pvInBuffer = inBuf;
620 opIn.call.control.fpvOutBufferIsNULL = 0;
621 opIn.call.control.cbOutBufferSize = 256;
622
623 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
624 if (!test_request_roundtrip(&opIn, &opOut))
625 goto out;
626 if (!check_redir_context("Control", &opIn.call.control.handles.hContext,
627 &opOut.call.control.handles.hContext))
628 goto out;
629 if (!check_redir_handle("Control", &opIn.call.control.handles.hCard,
630 &opOut.call.control.handles.hCard))
631 goto out;
632 if (!check_field("Control", "dwControlCode", opIn.call.control.dwControlCode,
633 opOut.call.control.dwControlCode))
634 goto out;
635 if (!check_field("Control", "cbInBufferSize", opIn.call.control.cbInBufferSize,
636 opOut.call.control.cbInBufferSize))
637 goto out;
638 if (!check_bytes("Control", "pvInBuffer", opIn.call.control.pvInBuffer,
639 opOut.call.control.pvInBuffer, opIn.call.control.cbInBufferSize))
640 goto out;
641 if (!check_field("Control", "cbOutBufferSize", opIn.call.control.cbOutBufferSize,
642 opOut.call.control.cbOutBufferSize))
643 goto out;
644
645 success = TRUE;
646out:
647 opIn.call.control.pvInBuffer = nullptr;
648 smartcard_operation_free(&opIn, FALSE);
649 smartcard_operation_free(&opOut, FALSE);
650 return success;
651}
652
653WINPR_ATTR_NODISCARD
654static BOOL test_get_attrib_encode_decode_request(void)
655{
656 BOOL success = FALSE;
657 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
658 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
659 opIn.ioControlCode = SCARD_IOCTL_GETATTRIB;
660 fill_redir_context(&opIn.call.getAttrib.handles.hContext, 0x1234);
661 fill_redir_handle(&opIn.call.getAttrib.handles.hCard, 0x5678);
662 opIn.call.getAttrib.dwAttrId = SCARD_ATTR_ATR_STRING;
663 opIn.call.getAttrib.fpbAttrIsNULL = 0;
664 opIn.call.getAttrib.cbAttrLen = 36;
665
666 if (!test_request_roundtrip(&opIn, &opOut))
667 goto out;
668 if (!check_redir_context("GetAttrib", &opIn.call.getAttrib.handles.hContext,
669 &opOut.call.getAttrib.handles.hContext))
670 goto out;
671 if (!check_redir_handle("GetAttrib", &opIn.call.getAttrib.handles.hCard,
672 &opOut.call.getAttrib.handles.hCard))
673 goto out;
674 if (!check_field("GetAttrib", "dwAttrId", opIn.call.getAttrib.dwAttrId,
675 opOut.call.getAttrib.dwAttrId))
676 goto out;
677 if (!check_field("GetAttrib", "cbAttrLen", opIn.call.getAttrib.cbAttrLen,
678 opOut.call.getAttrib.cbAttrLen))
679 goto out;
680
681 success = TRUE;
682out:
683 smartcard_operation_free(&opIn, FALSE);
684 smartcard_operation_free(&opOut, FALSE);
685 return success;
686}
687
688WINPR_ATTR_NODISCARD
689static BOOL test_set_attrib_encode_decode_request(void)
690{
691 BOOL success = FALSE;
692 BYTE attrBuf[] = { 0x3B, 0x00 };
693 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
694 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
695 opIn.ioControlCode = SCARD_IOCTL_SETATTRIB;
696 fill_redir_context(&opIn.call.setAttrib.handles.hContext, 0x1234);
697 fill_redir_handle(&opIn.call.setAttrib.handles.hCard, 0x5678);
698 opIn.call.setAttrib.dwAttrId = SCARD_ATTR_ATR_STRING;
699 opIn.call.setAttrib.cbAttrLen = sizeof(attrBuf);
700 opIn.call.setAttrib.pbAttr = attrBuf;
701
702 if (!test_request_roundtrip(&opIn, &opOut))
703 goto out;
704 if (!check_redir_context("SetAttrib", &opIn.call.setAttrib.handles.hContext,
705 &opOut.call.setAttrib.handles.hContext))
706 goto out;
707 if (!check_redir_handle("SetAttrib", &opIn.call.setAttrib.handles.hCard,
708 &opOut.call.setAttrib.handles.hCard))
709 goto out;
710 if (!check_field("SetAttrib", "dwAttrId", opIn.call.setAttrib.dwAttrId,
711 opOut.call.setAttrib.dwAttrId))
712 goto out;
713 if (!check_field("SetAttrib", "cbAttrLen", opIn.call.setAttrib.cbAttrLen,
714 opOut.call.setAttrib.cbAttrLen))
715 goto out;
716 if (!check_bytes("SetAttrib", "pbAttr", opIn.call.setAttrib.pbAttr, opOut.call.setAttrib.pbAttr,
717 opIn.call.setAttrib.cbAttrLen))
718 goto out;
719
720 success = TRUE;
721out:
722 opIn.call.setAttrib.pbAttr = nullptr;
723 smartcard_operation_free(&opIn, FALSE);
724 smartcard_operation_free(&opOut, FALSE);
725 return success;
726}
727
728WINPR_ATTR_NODISCARD
729static BOOL test_encode_decode_requests(void)
730{
731 BOOL success = TRUE;
732
733 if (!test_establish_context_encode_decode_request())
734 success = FALSE;
735 if (!test_release_context_encode_decode_request())
736 success = FALSE;
737 if (!test_is_valid_context_encode_decode_request())
738 success = FALSE;
739 if (!test_list_reader_groups_a_encode_decode_request())
740 success = FALSE;
741 if (!test_list_reader_groups_w_encode_decode_request())
742 success = FALSE;
743 if (!test_list_readers_a_encode_decode_request())
744 success = FALSE;
745 if (!test_list_readers_w_encode_decode_request())
746 success = FALSE;
747 if (!test_cancel_encode_decode_request())
748 success = FALSE;
749 if (!test_connect_a_encode_decode_request())
750 success = FALSE;
751 if (!test_connect_w_encode_decode_request())
752 success = FALSE;
753 if (!test_reconnect_encode_decode_request())
754 success = FALSE;
755 if (!test_disconnect_encode_decode_request())
756 success = FALSE;
757 if (!test_begin_transaction_encode_decode_request())
758 success = FALSE;
759 if (!test_end_transaction_encode_decode_request())
760 success = FALSE;
761 if (!test_status_a_encode_decode_request())
762 success = FALSE;
763 if (!test_status_w_encode_decode_request())
764 success = FALSE;
765 if (!test_transmit_encode_decode_request())
766 success = FALSE;
767 if (!test_control_encode_decode_request())
768 success = FALSE;
769 if (!test_get_attrib_encode_decode_request())
770 success = FALSE;
771 if (!test_set_attrib_encode_decode_request())
772 success = FALSE;
773
774 return success;
775}
776
777WINPR_ATTR_NODISCARD
778static LONG encode_response_payload(wStream* s, UINT32 ioControlCode,
779 const SMARTCARD_OPERATION* opIn)
780{
781 switch (ioControlCode)
782 {
783 case SCARD_IOCTL_ESTABLISHCONTEXT:
784 return smartcard_pack_establish_context_return(s, &opIn->ret.establishContext);
785 case SCARD_IOCTL_RECONNECT:
786 return smartcard_pack_reconnect_return(s, &opIn->ret.reconnect);
787 case SCARD_IOCTL_CONNECTA:
788 case SCARD_IOCTL_CONNECTW:
789 return smartcard_pack_connect_return(s, &opIn->ret.connect);
790 case SCARD_IOCTL_CONTROL:
791 return smartcard_pack_control_return(s, &opIn->ret.control);
792 case SCARD_IOCTL_TRANSMIT:
793 return smartcard_pack_transmit_return(s, &opIn->ret.transmit);
794 case SCARD_IOCTL_GETATTRIB:
795 return smartcard_pack_get_attrib_return(s, &opIn->ret.getAttrib, 0,
796 opIn->ret.getAttrib.cbAttrLen);
797 case SCARD_IOCTL_GETSTATUSCHANGEA:
798 return smartcard_pack_get_status_change_return(s, &opIn->ret.getStatusChange, FALSE);
799 case SCARD_IOCTL_GETSTATUSCHANGEW:
800 return smartcard_pack_get_status_change_return(s, &opIn->ret.getStatusChange, TRUE);
801 case SCARD_IOCTL_LISTREADERGROUPSA:
802 return smartcard_pack_list_reader_groups_return(s, &opIn->ret.listReaders, FALSE);
803 case SCARD_IOCTL_LISTREADERGROUPSW:
804 return smartcard_pack_list_reader_groups_return(s, &opIn->ret.listReaders, TRUE);
805 case SCARD_IOCTL_LISTREADERSA:
806 return smartcard_pack_list_readers_return(s, &opIn->ret.listReaders, FALSE);
807 case SCARD_IOCTL_LISTREADERSW:
808 return smartcard_pack_list_readers_return(s, &opIn->ret.listReaders, TRUE);
809 case SCARD_IOCTL_STATUSA:
810 return smartcard_pack_status_return(s, &opIn->ret.status, FALSE);
811 case SCARD_IOCTL_STATUSW:
812 return smartcard_pack_status_return(s, &opIn->ret.status, TRUE);
813 case SCARD_IOCTL_RELEASECONTEXT:
814 case SCARD_IOCTL_ISVALIDCONTEXT:
815 case SCARD_IOCTL_CANCEL:
816 case SCARD_IOCTL_DISCONNECT:
817 case SCARD_IOCTL_BEGINTRANSACTION:
818 case SCARD_IOCTL_ENDTRANSACTION:
819 case SCARD_IOCTL_SETATTRIB:
820 return SCARD_S_SUCCESS;
821 default:
822 return SCARD_F_INTERNAL_ERROR;
823 }
824}
825
826WINPR_ATTR_MALLOC(Stream_Free, 1)
827static wStream* build_device_control_response(UINT32 ioControlCode, const SMARTCARD_OPERATION* opIn)
828{
829 const char* name = scard_get_ioctl_string(ioControlCode, TRUE);
830 WINPR_ASSERT(name);
831
832 wStream* payload = Stream_New(nullptr, 4096);
833 if (!payload)
834 {
835 (void)fprintf(stderr, "%s: Stream_New for payload failed\n", name);
836 return nullptr;
837 }
838
839 LONG rc = encode_response_payload(payload, ioControlCode, opIn);
840 if (rc != SCARD_S_SUCCESS)
841 {
842 (void)fprintf(stderr, "%s: encode_response_payload failed with 0x%08" PRIX32 "\n", name,
843 (UINT32)rc);
844 Stream_Release(payload);
845 return nullptr;
846 }
847 Stream_SealLength(payload);
848
849 wStream* s = Stream_New(nullptr, 256 + Stream_Length(payload));
850 if (!s)
851 {
852 (void)fprintf(stderr, "%s: Stream_New for response failed\n", name);
853 Stream_Release(payload);
854 return nullptr;
855 }
856
857 Stream_Seek(s, 4); /* Placeholder for outputBufferLength */
858 const size_t headerPos = Stream_GetPosition(s);
859 Stream_Seek(s, SMARTCARD_COMMON_TYPE_HEADER_LENGTH +
860 SMARTCARD_PRIVATE_TYPE_HEADER_LENGTH); /* Placeholder for headers */
861 Stream_Write_INT32(s, opIn->returnCode);
862 Stream_Write(s, Stream_Buffer(payload), Stream_Length(payload));
863 Stream_Release(payload);
864
865 const size_t payloadSize = Stream_GetPosition(s) - headerPos;
866 const size_t objectBufferLength =
867 payloadSize - SMARTCARD_COMMON_TYPE_HEADER_LENGTH - SMARTCARD_PRIVATE_TYPE_HEADER_LENGTH;
868
869 WINPR_UNUSED(smartcard_pack_write_size_align(s, payloadSize, 8));
870
871 const size_t endPos = Stream_GetPosition(s);
872 const size_t outputBufferLength = endPos - headerPos;
873
874 if (!Stream_SetPosition(s, 0))
875 {
876 (void)fprintf(stderr, "%s: Stream_SetPosition failed\n", name);
877 Stream_Release(s);
878 return nullptr;
879 }
880 UINT32 outputLength = WINPR_ASSERTING_INT_CAST(UINT32, outputBufferLength);
881 UINT32 objectLength = WINPR_ASSERTING_INT_CAST(UINT32, objectBufferLength);
882 Stream_Write_UINT32(s, outputLength);
883 smartcard_pack_common_type_header(s);
884 smartcard_pack_private_type_header(s, objectLength);
885
886 if (!Stream_SetPosition(s, endPos))
887 {
888 (void)fprintf(stderr, "%s: Stream_SetPosition failed\n", name);
889 Stream_Release(s);
890 return nullptr;
891 }
892 Stream_SealLength(s);
893 if (!Stream_SetPosition(s, 0))
894 {
895 (void)fprintf(stderr, "%s: Stream_SetPosition failed\n", name);
896 Stream_Release(s);
897 return nullptr;
898 }
899 return s;
900}
901
902WINPR_ATTR_NODISCARD
903static BOOL test_response_roundtrip(const SMARTCARD_OPERATION* opIn, SMARTCARD_OPERATION* opOut)
904{
905 const UINT32 ioControlCode = opIn->ioControlCode;
906 const char* name = scard_get_ioctl_string(ioControlCode, TRUE);
907 WINPR_ASSERT(name);
908
909 wStream* s = build_device_control_response(ioControlCode, opIn);
910 if (!s)
911 return FALSE;
912
913 *opOut = (SMARTCARD_OPERATION)WINPR_C_ARRAY_INIT;
914 LONG status = smartcard_irp_device_control_decode_response(s, ioControlCode, opOut);
915 Stream_Release(s);
916
917 if (status != SCARD_S_SUCCESS)
918 {
919 (void)fprintf(stderr, "%s: decode_response failed with 0x%08" PRIX32 "\n", name,
920 (UINT32)status);
921 return FALSE;
922 }
923
924 return TRUE;
925}
926
927WINPR_ATTR_NODISCARD
928static BOOL test_establish_context_decode_response(void)
929{
930 BOOL success = FALSE;
931 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
932 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
933 opIn.ioControlCode = SCARD_IOCTL_ESTABLISHCONTEXT;
934 fill_redir_context(&opIn.ret.establishContext.hContext, 0xABCD);
935 opIn.returnCode = SCARD_S_SUCCESS;
936
937 if (!test_response_roundtrip(&opIn, &opOut))
938 goto out;
939 if (!check_redir_context("EstablishContext", &opIn.ret.establishContext.hContext,
940 &opOut.ret.establishContext.hContext))
941 goto out;
942 if (!check_field("EstablishContext", "returnCode", (UINT32)opIn.returnCode,
943 (UINT32)opOut.returnCode))
944 goto out;
945
946 success = TRUE;
947out:
948 smartcard_operation_free(&opIn, FALSE);
949 smartcard_operation_free(&opOut, FALSE);
950 return success;
951}
952
953WINPR_ATTR_NODISCARD
954static BOOL test_release_context_decode_response(void)
955{
956 BOOL success = FALSE;
957 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
958 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
959 opIn.ioControlCode = SCARD_IOCTL_RELEASECONTEXT;
960 opIn.returnCode = SCARD_S_SUCCESS;
961
962 if (!test_response_roundtrip(&opIn, &opOut))
963 goto out;
964 if (!check_field("ReleaseContext", "returnCode", (UINT32)opIn.returnCode,
965 (UINT32)opOut.returnCode))
966 goto out;
967
968 success = TRUE;
969out:
970 smartcard_operation_free(&opIn, FALSE);
971 smartcard_operation_free(&opOut, FALSE);
972 return success;
973}
974
975WINPR_ATTR_NODISCARD
976static BOOL test_is_valid_context_decode_response(void)
977{
978 BOOL success = FALSE;
979 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
980 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
981 opIn.ioControlCode = SCARD_IOCTL_ISVALIDCONTEXT;
982 opIn.returnCode = SCARD_S_SUCCESS;
983
984 if (!test_response_roundtrip(&opIn, &opOut))
985 goto out;
986 if (!check_field("IsValidContext", "returnCode", (UINT32)opIn.returnCode,
987 (UINT32)opOut.returnCode))
988 goto out;
989
990 success = TRUE;
991out:
992 smartcard_operation_free(&opIn, FALSE);
993 smartcard_operation_free(&opOut, FALSE);
994 return success;
995}
996
997WINPR_ATTR_NODISCARD
998static BOOL test_list_reader_groups_decode_response_impl(UINT32 ioControlCode)
999{
1000 BOOL success = FALSE;
1001 const char* n = scard_get_ioctl_string(ioControlCode, TRUE);
1002 WINPR_ASSERT(n);
1003
1004 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
1005 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
1006 opIn.ioControlCode = ioControlCode;
1007 opIn.returnCode = SCARD_S_SUCCESS;
1008 BYTE groups[] = { 'G', 'r', 'p', '\0', '\0' };
1009 opIn.ret.listReaders.cBytes = sizeof(groups);
1010 opIn.ret.listReaders.msz = groups;
1011
1012 if (!test_response_roundtrip(&opIn, &opOut))
1013 goto out;
1014 if (!check_field(n, "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode))
1015 goto out;
1016 if (!check_field(n, "cBytes", opIn.ret.listReaders.cBytes, opOut.ret.listReaders.cBytes))
1017 goto out;
1018 if (!check_bytes(n, "msz", opIn.ret.listReaders.msz, opOut.ret.listReaders.msz,
1019 opIn.ret.listReaders.cBytes))
1020 goto out;
1021
1022 success = TRUE;
1023out:
1024 opIn.ret.listReaders.msz = nullptr;
1025 smartcard_operation_free(&opIn, FALSE);
1026 smartcard_operation_free(&opOut, FALSE);
1027 return success;
1028}
1029
1030WINPR_ATTR_NODISCARD
1031static BOOL test_list_reader_groups_a_decode_response(void)
1032{
1033 return test_list_reader_groups_decode_response_impl(SCARD_IOCTL_LISTREADERGROUPSA);
1034}
1035
1036WINPR_ATTR_NODISCARD
1037static BOOL test_list_reader_groups_w_decode_response(void)
1038{
1039 return test_list_reader_groups_decode_response_impl(SCARD_IOCTL_LISTREADERGROUPSW);
1040}
1041
1042WINPR_ATTR_NODISCARD
1043static BOOL test_list_readers_decode_response_impl(UINT32 ioControlCode)
1044{
1045 BOOL success = FALSE;
1046 const char* n = scard_get_ioctl_string(ioControlCode, TRUE);
1047 WINPR_ASSERT(n);
1048
1049 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
1050 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
1051 opIn.ioControlCode = ioControlCode;
1052 opIn.returnCode = SCARD_S_SUCCESS;
1053 BYTE readers[] = { 'R', 'd', 'r', '\0', '\0' };
1054 opIn.ret.listReaders.cBytes = sizeof(readers);
1055 opIn.ret.listReaders.msz = readers;
1056
1057 if (!test_response_roundtrip(&opIn, &opOut))
1058 goto out;
1059 if (!check_field(n, "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode))
1060 goto out;
1061 if (!check_field(n, "cBytes", opIn.ret.listReaders.cBytes, opOut.ret.listReaders.cBytes))
1062 goto out;
1063 if (!check_bytes(n, "msz", opIn.ret.listReaders.msz, opOut.ret.listReaders.msz,
1064 opIn.ret.listReaders.cBytes))
1065 goto out;
1066
1067 success = TRUE;
1068out:
1069 opIn.ret.listReaders.msz = nullptr;
1070 smartcard_operation_free(&opIn, FALSE);
1071 smartcard_operation_free(&opOut, FALSE);
1072 return success;
1073}
1074
1075WINPR_ATTR_NODISCARD
1076static BOOL test_list_readers_a_decode_response(void)
1077{
1078 return test_list_readers_decode_response_impl(SCARD_IOCTL_LISTREADERSA);
1079}
1080
1081WINPR_ATTR_NODISCARD
1082static BOOL test_list_readers_w_decode_response(void)
1083{
1084 return test_list_readers_decode_response_impl(SCARD_IOCTL_LISTREADERSW);
1085}
1086
1087WINPR_ATTR_NODISCARD
1088static BOOL test_get_status_change_decode_response_impl(UINT32 ioControlCode)
1089{
1090 BOOL success = FALSE;
1091 const char* n = scard_get_ioctl_string(ioControlCode, TRUE);
1092 WINPR_ASSERT(n);
1093
1094 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
1095 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
1096 opIn.ioControlCode = ioControlCode;
1097 opIn.returnCode = SCARD_S_SUCCESS;
1098 ReaderState_Return state = { .dwCurrentState = SCARD_STATE_PRESENT,
1099 .dwEventState = SCARD_STATE_CHANGED,
1100 .cbAtr = 2,
1101 .rgbAtr[0] = 0x3B,
1102 .rgbAtr[1] = 0x00 };
1103 opIn.ret.getStatusChange.cReaders = 1;
1104 opIn.ret.getStatusChange.rgReaderStates = &state;
1105
1106 if (!test_response_roundtrip(&opIn, &opOut))
1107 goto out;
1108 if (!check_field(n, "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode))
1109 goto out;
1110 if (!check_field(n, "cReaders", opIn.ret.getStatusChange.cReaders,
1111 opOut.ret.getStatusChange.cReaders))
1112 goto out;
1113 if (!check_field(n, "dwCurrentState", state.dwCurrentState,
1114 opOut.ret.getStatusChange.rgReaderStates[0].dwCurrentState))
1115 goto out;
1116 if (!check_field(n, "dwEventState", state.dwEventState,
1117 opOut.ret.getStatusChange.rgReaderStates[0].dwEventState))
1118 goto out;
1119 if (!check_field(n, "cbAtr", state.cbAtr, opOut.ret.getStatusChange.rgReaderStates[0].cbAtr))
1120 goto out;
1121 if (!check_bytes(n, "rgbAtr", state.rgbAtr, opOut.ret.getStatusChange.rgReaderStates[0].rgbAtr,
1122 sizeof(state.rgbAtr)))
1123 goto out;
1124
1125 success = TRUE;
1126out:
1127 opIn.ret.getStatusChange.rgReaderStates = nullptr;
1128 smartcard_operation_free(&opIn, FALSE);
1129 smartcard_operation_free(&opOut, FALSE);
1130 return success;
1131}
1132
1133WINPR_ATTR_NODISCARD
1134static BOOL test_get_status_change_a_decode_response(void)
1135{
1136 return test_get_status_change_decode_response_impl(SCARD_IOCTL_GETSTATUSCHANGEA);
1137}
1138
1139WINPR_ATTR_NODISCARD
1140static BOOL test_get_status_change_w_decode_response(void)
1141{
1142 return test_get_status_change_decode_response_impl(SCARD_IOCTL_GETSTATUSCHANGEW);
1143}
1144
1145WINPR_ATTR_NODISCARD
1146static BOOL test_cancel_decode_response(void)
1147{
1148 BOOL success = FALSE;
1149 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
1150 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
1151 opIn.ioControlCode = SCARD_IOCTL_CANCEL;
1152 opIn.returnCode = SCARD_S_SUCCESS;
1153
1154 if (!test_response_roundtrip(&opIn, &opOut))
1155 goto out;
1156 if (!check_field("Cancel", "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode))
1157 goto out;
1158
1159 success = TRUE;
1160out:
1161 smartcard_operation_free(&opIn, FALSE);
1162 smartcard_operation_free(&opOut, FALSE);
1163 return success;
1164}
1165
1166WINPR_ATTR_NODISCARD
1167static BOOL test_connect_decode_response_impl(UINT32 ioControlCode)
1168{
1169 BOOL success = FALSE;
1170 const char* n = scard_get_ioctl_string(ioControlCode, TRUE);
1171 WINPR_ASSERT(n);
1172
1173 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
1174 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
1175 opIn.ioControlCode = ioControlCode;
1176 opIn.returnCode = SCARD_S_SUCCESS;
1177 fill_redir_context(&opIn.ret.connect.hContext, 0x1234);
1178 fill_redir_handle(&opIn.ret.connect.hCard, 0x5678);
1179 opIn.ret.connect.dwActiveProtocol = SCARD_PROTOCOL_T0;
1180
1181 if (!test_response_roundtrip(&opIn, &opOut))
1182 goto out;
1183 if (!check_field(n, "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode))
1184 goto out;
1185 if (!check_redir_context(n, &opIn.ret.connect.hContext, &opOut.ret.connect.hContext))
1186 goto out;
1187 if (!check_redir_handle(n, &opIn.ret.connect.hCard, &opOut.ret.connect.hCard))
1188 goto out;
1189 if (!check_field(n, "dwActiveProtocol", opIn.ret.connect.dwActiveProtocol,
1190 opOut.ret.connect.dwActiveProtocol))
1191 goto out;
1192
1193 success = TRUE;
1194out:
1195 smartcard_operation_free(&opIn, FALSE);
1196 smartcard_operation_free(&opOut, FALSE);
1197 return success;
1198}
1199
1200WINPR_ATTR_NODISCARD
1201static BOOL test_connect_a_decode_response(void)
1202{
1203 return test_connect_decode_response_impl(SCARD_IOCTL_CONNECTA);
1204}
1205
1206WINPR_ATTR_NODISCARD
1207static BOOL test_connect_w_decode_response(void)
1208{
1209 return test_connect_decode_response_impl(SCARD_IOCTL_CONNECTW);
1210}
1211
1212WINPR_ATTR_NODISCARD
1213static BOOL test_reconnect_decode_response(void)
1214{
1215 BOOL success = FALSE;
1216 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
1217 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
1218 opIn.ioControlCode = SCARD_IOCTL_RECONNECT;
1219 opIn.returnCode = SCARD_S_SUCCESS;
1220 opIn.ret.reconnect.dwActiveProtocol = SCARD_PROTOCOL_T1;
1221
1222 if (!test_response_roundtrip(&opIn, &opOut))
1223 goto out;
1224 if (!check_field("Reconnect", "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode))
1225 goto out;
1226 if (!check_field("Reconnect", "dwActiveProtocol", opIn.ret.reconnect.dwActiveProtocol,
1227 opOut.ret.reconnect.dwActiveProtocol))
1228 goto out;
1229
1230 success = TRUE;
1231out:
1232 smartcard_operation_free(&opIn, FALSE);
1233 smartcard_operation_free(&opOut, FALSE);
1234 return success;
1235}
1236
1237WINPR_ATTR_NODISCARD
1238static BOOL test_disconnect_decode_response(void)
1239{
1240 BOOL success = FALSE;
1241 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
1242 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
1243 opIn.ioControlCode = SCARD_IOCTL_DISCONNECT;
1244 opIn.returnCode = SCARD_S_SUCCESS;
1245
1246 if (!test_response_roundtrip(&opIn, &opOut))
1247 goto out;
1248 if (!check_field("Disconnect", "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode))
1249 goto out;
1250
1251 success = TRUE;
1252out:
1253 smartcard_operation_free(&opIn, FALSE);
1254 smartcard_operation_free(&opOut, FALSE);
1255 return success;
1256}
1257
1258WINPR_ATTR_NODISCARD
1259static BOOL test_begin_transaction_decode_response(void)
1260{
1261 BOOL success = FALSE;
1262 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
1263 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
1264 opIn.ioControlCode = SCARD_IOCTL_BEGINTRANSACTION;
1265 opIn.returnCode = SCARD_S_SUCCESS;
1266
1267 if (!test_response_roundtrip(&opIn, &opOut))
1268 goto out;
1269 if (!check_field("BeginTransaction", "returnCode", (UINT32)opIn.returnCode,
1270 (UINT32)opOut.returnCode))
1271 goto out;
1272
1273 success = TRUE;
1274out:
1275 smartcard_operation_free(&opIn, FALSE);
1276 smartcard_operation_free(&opOut, FALSE);
1277 return success;
1278}
1279
1280WINPR_ATTR_NODISCARD
1281static BOOL test_end_transaction_decode_response(void)
1282{
1283 BOOL success = FALSE;
1284 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
1285 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
1286 opIn.ioControlCode = SCARD_IOCTL_ENDTRANSACTION;
1287 opIn.returnCode = SCARD_S_SUCCESS;
1288
1289 if (!test_response_roundtrip(&opIn, &opOut))
1290 goto out;
1291 if (!check_field("EndTransaction", "returnCode", (UINT32)opIn.returnCode,
1292 (UINT32)opOut.returnCode))
1293 goto out;
1294
1295 success = TRUE;
1296out:
1297 smartcard_operation_free(&opIn, FALSE);
1298 smartcard_operation_free(&opOut, FALSE);
1299 return success;
1300}
1301
1302WINPR_ATTR_NODISCARD
1303static BOOL test_status_decode_response_impl(UINT32 ioControlCode)
1304{
1305 BOOL success = FALSE;
1306 const char* n = scard_get_ioctl_string(ioControlCode, TRUE);
1307 WINPR_ASSERT(n);
1308
1309 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
1310 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
1311 opIn.ioControlCode = ioControlCode;
1312 opIn.returnCode = SCARD_S_SUCCESS;
1313 BYTE readerName[] = { 'R', 'd', 'r', '\0' };
1314 opIn.ret.status.cBytes = sizeof(readerName);
1315 opIn.ret.status.mszReaderNames = readerName;
1316 opIn.ret.status.dwState = SCARD_SPECIFIC;
1317 opIn.ret.status.dwProtocol = SCARD_PROTOCOL_T1;
1318 memset(opIn.ret.status.pbAtr, 0x3B, sizeof(opIn.ret.status.pbAtr));
1319 opIn.ret.status.cbAtrLen = 2;
1320
1321 if (!test_response_roundtrip(&opIn, &opOut))
1322 goto out;
1323 if (!check_field(n, "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode))
1324 goto out;
1325 if (!check_field(n, "dwState", opIn.ret.status.dwState, opOut.ret.status.dwState))
1326 goto out;
1327 if (!check_field(n, "dwProtocol", opIn.ret.status.dwProtocol, opOut.ret.status.dwProtocol))
1328 goto out;
1329 if (!check_field(n, "cbAtrLen", opIn.ret.status.cbAtrLen, opOut.ret.status.cbAtrLen))
1330 goto out;
1331 if (!check_bytes(n, "pbAtr", opIn.ret.status.pbAtr, opOut.ret.status.pbAtr,
1332 sizeof(opIn.ret.status.pbAtr)))
1333 goto out;
1334 if (!check_field(n, "cBytes", opIn.ret.status.cBytes, opOut.ret.status.cBytes))
1335 goto out;
1336 if (!check_bytes(n, "mszReaderNames", opIn.ret.status.mszReaderNames,
1337 opOut.ret.status.mszReaderNames, opIn.ret.status.cBytes))
1338 goto out;
1339
1340 success = TRUE;
1341out:
1342 opIn.ret.status.mszReaderNames = nullptr;
1343 smartcard_operation_free(&opIn, FALSE);
1344 smartcard_operation_free(&opOut, FALSE);
1345 return success;
1346}
1347
1348WINPR_ATTR_NODISCARD
1349static BOOL test_status_a_decode_response(void)
1350{
1351 return test_status_decode_response_impl(SCARD_IOCTL_STATUSA);
1352}
1353
1354WINPR_ATTR_NODISCARD
1355static BOOL test_status_w_decode_response(void)
1356{
1357 return test_status_decode_response_impl(SCARD_IOCTL_STATUSW);
1358}
1359
1360WINPR_ATTR_NODISCARD
1361static BOOL test_transmit_decode_response(void)
1362{
1363 BOOL success = FALSE;
1364 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
1365 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
1366 opIn.ioControlCode = SCARD_IOCTL_TRANSMIT;
1367 opIn.returnCode = SCARD_S_SUCCESS;
1368 BYTE recvBuf[] = { 0x90, 0x00 };
1369 opIn.ret.transmit.cbRecvLength = sizeof(recvBuf);
1370 opIn.ret.transmit.pbRecvBuffer = recvBuf;
1371 opIn.ret.transmit.pioRecvPci = nullptr;
1372
1373 if (!test_response_roundtrip(&opIn, &opOut))
1374 goto out;
1375 if (!check_field("Transmit", "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode))
1376 goto out;
1377 if (!check_field("Transmit", "cbRecvLength", opIn.ret.transmit.cbRecvLength,
1378 opOut.ret.transmit.cbRecvLength))
1379 goto out;
1380 if (!check_bytes("Transmit", "pbRecvBuffer", opIn.ret.transmit.pbRecvBuffer,
1381 opOut.ret.transmit.pbRecvBuffer, opIn.ret.transmit.cbRecvLength))
1382 goto out;
1383
1384 success = TRUE;
1385out:
1386 opIn.ret.transmit.pbRecvBuffer = nullptr;
1387 smartcard_operation_free(&opIn, FALSE);
1388 smartcard_operation_free(&opOut, FALSE);
1389 return success;
1390}
1391
1392WINPR_ATTR_NODISCARD
1393static BOOL test_control_decode_response(void)
1394{
1395 BOOL success = FALSE;
1396 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
1397 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
1398 opIn.ioControlCode = SCARD_IOCTL_CONTROL;
1399 opIn.returnCode = SCARD_S_SUCCESS;
1400 BYTE outBuf[] = { 0xAA, 0xBB, 0xCC };
1401 opIn.ret.control.cbOutBufferSize = sizeof(outBuf);
1402 opIn.ret.control.pvOutBuffer = outBuf;
1403
1404 if (!test_response_roundtrip(&opIn, &opOut))
1405 goto out;
1406 if (!check_field("Control", "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode))
1407 goto out;
1408 if (!check_field("Control", "cbOutBufferSize", opIn.ret.control.cbOutBufferSize,
1409 opOut.ret.control.cbOutBufferSize))
1410 goto out;
1411 if (!check_bytes("Control", "pvOutBuffer", opIn.ret.control.pvOutBuffer,
1412 opOut.ret.control.pvOutBuffer, opIn.ret.control.cbOutBufferSize))
1413 goto out;
1414
1415 success = TRUE;
1416out:
1417 opIn.ret.control.pvOutBuffer = nullptr;
1418 smartcard_operation_free(&opIn, FALSE);
1419 smartcard_operation_free(&opOut, FALSE);
1420 return success;
1421}
1422
1423WINPR_ATTR_NODISCARD
1424static BOOL test_get_attrib_decode_response(void)
1425{
1426 BOOL success = FALSE;
1427 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
1428 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
1429 opIn.ioControlCode = SCARD_IOCTL_GETATTRIB;
1430 opIn.returnCode = SCARD_S_SUCCESS;
1431 BYTE attr[] = { 0x3B, 0x90, 0x00 };
1432 opIn.ret.getAttrib.cbAttrLen = sizeof(attr);
1433 opIn.ret.getAttrib.pbAttr = attr;
1434
1435 if (!test_response_roundtrip(&opIn, &opOut))
1436 goto out;
1437 if (!check_field("GetAttrib", "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode))
1438 goto out;
1439 if (!check_field("GetAttrib", "cbAttrLen", opIn.ret.getAttrib.cbAttrLen,
1440 opOut.ret.getAttrib.cbAttrLen))
1441 goto out;
1442 if (!check_bytes("GetAttrib", "pbAttr", opIn.ret.getAttrib.pbAttr, opOut.ret.getAttrib.pbAttr,
1443 opIn.ret.getAttrib.cbAttrLen))
1444 goto out;
1445
1446 success = TRUE;
1447out:
1448 opIn.ret.getAttrib.pbAttr = nullptr;
1449 smartcard_operation_free(&opIn, FALSE);
1450 smartcard_operation_free(&opOut, FALSE);
1451 return success;
1452}
1453
1454WINPR_ATTR_NODISCARD
1455static BOOL test_set_attrib_decode_response(void)
1456{
1457 BOOL success = FALSE;
1458 SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT;
1459 SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT;
1460 opIn.ioControlCode = SCARD_IOCTL_SETATTRIB;
1461 opIn.returnCode = SCARD_S_SUCCESS;
1462
1463 if (!test_response_roundtrip(&opIn, &opOut))
1464 goto out;
1465 if (!check_field("SetAttrib", "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode))
1466 goto out;
1467
1468 success = TRUE;
1469out:
1470 smartcard_operation_free(&opIn, FALSE);
1471 smartcard_operation_free(&opOut, FALSE);
1472 return success;
1473}
1474
1475WINPR_ATTR_NODISCARD
1476static BOOL test_decode_responses(void)
1477{
1478 BOOL success = TRUE;
1479
1480 if (!test_establish_context_decode_response())
1481 success = FALSE;
1482 if (!test_release_context_decode_response())
1483 success = FALSE;
1484 if (!test_is_valid_context_decode_response())
1485 success = FALSE;
1486 if (!test_list_reader_groups_a_decode_response())
1487 success = FALSE;
1488 if (!test_list_reader_groups_w_decode_response())
1489 success = FALSE;
1490 if (!test_list_readers_a_decode_response())
1491 success = FALSE;
1492 if (!test_list_readers_w_decode_response())
1493 success = FALSE;
1494 if (!test_get_status_change_a_decode_response())
1495 success = FALSE;
1496 if (!test_get_status_change_w_decode_response())
1497 success = FALSE;
1498 if (!test_cancel_decode_response())
1499 success = FALSE;
1500 if (!test_connect_a_decode_response())
1501 success = FALSE;
1502 if (!test_connect_w_decode_response())
1503 success = FALSE;
1504 if (!test_reconnect_decode_response())
1505 success = FALSE;
1506 if (!test_disconnect_decode_response())
1507 success = FALSE;
1508 if (!test_begin_transaction_decode_response())
1509 success = FALSE;
1510 if (!test_end_transaction_decode_response())
1511 success = FALSE;
1512 if (!test_status_a_decode_response())
1513 success = FALSE;
1514 if (!test_status_w_decode_response())
1515 success = FALSE;
1516 if (!test_transmit_decode_response())
1517 success = FALSE;
1518 if (!test_control_decode_response())
1519 success = FALSE;
1520 if (!test_get_attrib_decode_response())
1521 success = FALSE;
1522 if (!test_set_attrib_decode_response())
1523 success = FALSE;
1524
1525 return success;
1526}
1527
1528int TestSmartcardOperations(int argc, char* argv[])
1529{
1530 WINPR_UNUSED(argc);
1531 WINPR_UNUSED(argv);
1532
1533 if (!test_encode_decode_requests())
1534 return -1;
1535
1536 if (!test_decode_responses())
1537 return -1;
1538
1539 return 0;
1540}
union SMARTCARD_OPERATION::@35 ret