1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2019, Joyent, Inc.
14 * Copyright 2022 Oxide Computer Company
15 */
16
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <stdbool.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/list.h>
23 #include <fcntl.h>
24 #include <fts.h>
25 #include <errno.h>
26 #include <strings.h>
27 #include <unistd.h>
28 #include <upanic.h>
29 #include <sys/debug.h>
30 #include <sys/filio.h>
31 #include <sys/usb/clients/ccid/uccid.h>
32
33 #include <winscard.h>
34
35 /*
36 * Implementation of the PCSC library leveraging the uccid framework.
37 */
38
39 typedef struct pcsc_hdl {
40 hrtime_t pcsc_create_time;
41 list_t pcsc_autoalloc;
42 list_t pcsc_cards;
43 } pcsc_hdl_t;
44
45 typedef struct pcsc_card {
46 list_node_t pcc_link;
47 pcsc_hdl_t *pcc_hdl;
48 int pcc_fd;
49 char *pcc_name;
50 size_t pcc_namelen;
51 } pcsc_card_t;
52
53 typedef struct pcsc_mem {
54 list_node_t pcm_link;
55 void *pcm_buf;
56 } pcsc_mem_t;
57
58 /*
59 * Required globals
60 */
61 SCARD_IO_REQUEST g_rgSCardT0Pci = {
62 SCARD_PROTOCOL_T0,
63 0
64 };
65
66 SCARD_IO_REQUEST g_rgSCardT1Pci = {
67 SCARD_PROTOCOL_T1,
68 0
69 };
70
71 SCARD_IO_REQUEST g_rgSCardRawPci = {
72 SCARD_PROTOCOL_RAW,
73 0
74 };
75
76 const char *
pcsc_stringify_error(const LONG err)77 pcsc_stringify_error(const LONG err)
78 {
79 switch (err) {
80 case SCARD_S_SUCCESS:
81 return ("no error");
82 case SCARD_F_INTERNAL_ERROR:
83 return ("internal error");
84 case SCARD_E_CANCELLED:
85 return ("request cancelled");
86 case SCARD_E_INVALID_HANDLE:
87 return ("invalid handle");
88 case SCARD_E_INVALID_PARAMETER:
89 return ("invalid parameter");
90 case SCARD_E_NO_MEMORY:
91 return ("no memory");
92 case SCARD_E_INSUFFICIENT_BUFFER:
93 return ("buffer was insufficiently sized");
94 case SCARD_E_INVALID_VALUE:
95 return ("invalid value passed");
96 case SCARD_E_UNKNOWN_READER:
97 return ("unknown reader");
98 case SCARD_E_TIMEOUT:
99 return ("timeout occurred");
100 case SCARD_E_SHARING_VIOLATION:
101 return ("sharing violation");
102 case SCARD_E_NO_SMARTCARD:
103 return ("no smartcard present");
104 case SCARD_E_UNKNOWN_CARD:
105 return ("unknown ICC");
106 case SCARD_E_PROTO_MISMATCH:
107 return ("protocol mismatch");
108 case SCARD_F_COMM_ERROR:
109 return ("communication error");
110 case SCARD_F_UNKNOWN_ERROR:
111 return ("unknown error");
112 case SCARD_E_READER_UNAVAILABLE:
113 return ("reader unavailable");
114 case SCARD_E_NO_SERVICE:
115 return ("service error");
116 case SCARD_E_UNSUPPORTED_FEATURE:
117 return ("ICC requires unsupported feature");
118 case SCARD_E_NO_READERS_AVAILABLE:
119 return ("no readers avaiable");
120 case SCARD_W_UNSUPPORTED_CARD:
121 return ("ICC unsupported");
122 case SCARD_W_UNPOWERED_CARD:
123 return ("ICC is not powered");
124 case SCARD_W_RESET_CARD:
125 return ("ICC was reset");
126 case SCARD_W_REMOVED_CARD:
127 return ("ICC has been removed");
128 default:
129 return ("unknown error");
130 }
131 }
132
133 /*
134 * Allocate a buffer of size "len" for use with an SCARD_AUTOALLOCATE
135 * parameter. Each automatically allocated buffer must be appended to the
136 * context buffer list so that it can be freed during the call to
137 * SCardReleaseContext().
138 */
139 static void *
pcsc_mem_alloc(pcsc_hdl_t * hdl,size_t len)140 pcsc_mem_alloc(pcsc_hdl_t *hdl, size_t len)
141 {
142 pcsc_mem_t *mem;
143
144 if ((mem = malloc(sizeof (*mem))) == NULL) {
145 return (NULL);
146 }
147
148 if ((mem->pcm_buf = malloc(len)) == NULL) {
149 free(mem);
150 return (NULL);
151 }
152 list_link_init(&mem->pcm_link);
153
154 /*
155 * Put the buffer on the per-context list:
156 */
157 list_insert_tail(&hdl->pcsc_autoalloc, mem);
158
159 return (mem->pcm_buf);
160 }
161
162 static void
pcsc_mem_free(pcsc_hdl_t * hdl,void * buf)163 pcsc_mem_free(pcsc_hdl_t *hdl, void *buf)
164 {
165 for (pcsc_mem_t *mem = list_head(&hdl->pcsc_autoalloc); mem != NULL;
166 mem = list_next(&hdl->pcsc_autoalloc, mem)) {
167 if (mem->pcm_buf == buf) {
168 list_remove(&hdl->pcsc_autoalloc, mem);
169 free(mem->pcm_buf);
170 free(mem);
171 return;
172 }
173 }
174
175 char msg[512];
176 (void) snprintf(msg, sizeof (msg), "freed buffer %p not in context %p",
177 buf, hdl);
178 upanic(msg, strlen(msg));
179 }
180
181 static pcsc_card_t *
pcsc_card_alloc(pcsc_hdl_t * hdl,const char * reader)182 pcsc_card_alloc(pcsc_hdl_t *hdl, const char *reader)
183 {
184 pcsc_card_t *card;
185
186 if ((card = malloc(sizeof (*card))) == NULL) {
187 return (NULL);
188 }
189 card->pcc_hdl = hdl;
190 card->pcc_fd = -1;
191 list_link_init(&card->pcc_link);
192
193 /*
194 * The reader name is returned as a multi-string, which means we need
195 * the regular C string and then an additional null termination byte to
196 * end the list of strings:
197 */
198 card->pcc_namelen = strlen(reader) + 2;
199 if ((card->pcc_name = malloc(card->pcc_namelen)) == NULL) {
200 free(card);
201 return (NULL);
202 }
203 bcopy(reader, card->pcc_name, card->pcc_namelen - 1);
204 card->pcc_name[card->pcc_namelen - 1] = '\0';
205
206 /*
207 * Insert the card handle into the per-context list so that we can free
208 * them later during SCardReleaseContext().
209 */
210 list_insert_tail(&hdl->pcsc_cards, card);
211
212 return (card);
213 }
214
215 static void
pcsc_card_free(pcsc_card_t * card)216 pcsc_card_free(pcsc_card_t *card)
217 {
218 if (card == NULL) {
219 return;
220 }
221
222 if (card->pcc_fd >= 0) {
223 (void) close(card->pcc_fd);
224 }
225
226 /*
227 * Remove the card handle from the per-context list:
228 */
229 pcsc_hdl_t *hdl = card->pcc_hdl;
230 list_remove(&hdl->pcsc_cards, card);
231
232 free(card->pcc_name);
233 free(card);
234 }
235
236 /*
237 * This is called when a caller wishes to open a new Library context.
238 */
239 LONG
SCardEstablishContext(DWORD scope,LPCVOID unused0,LPCVOID unused1,LPSCARDCONTEXT outp)240 SCardEstablishContext(DWORD scope, LPCVOID unused0, LPCVOID unused1,
241 LPSCARDCONTEXT outp)
242 {
243 pcsc_hdl_t *hdl;
244
245 if (outp == NULL) {
246 return (SCARD_E_INVALID_PARAMETER);
247 }
248
249 if (scope != SCARD_SCOPE_SYSTEM) {
250 return (SCARD_E_INVALID_VALUE);
251 }
252
253 hdl = calloc(1, sizeof (pcsc_hdl_t));
254 if (hdl == NULL) {
255 return (SCARD_E_NO_MEMORY);
256 }
257 list_create(&hdl->pcsc_autoalloc, sizeof (pcsc_mem_t),
258 offsetof(pcsc_mem_t, pcm_link));
259 list_create(&hdl->pcsc_cards, sizeof (pcsc_card_t),
260 offsetof(pcsc_card_t, pcc_link));
261
262 hdl->pcsc_create_time = gethrtime();
263 *outp = hdl;
264 return (SCARD_S_SUCCESS);
265 }
266
267 bool
pcsc_valid_context(SCARDCONTEXT hdl)268 pcsc_valid_context(SCARDCONTEXT hdl)
269 {
270 /*
271 * On some other platforms, the context handle is a signed integer.
272 * Some software has been observed to use -1 as an invalid handle
273 * sentinel value, so we need to explicitly handle that here.
274 */
275 return (hdl != NULL && (uintptr_t)hdl != UINTPTR_MAX);
276 }
277
278 LONG
SCardIsValidContext(SCARDCONTEXT hdl)279 SCardIsValidContext(SCARDCONTEXT hdl)
280 {
281 if (!pcsc_valid_context(hdl)) {
282 return (SCARD_E_INVALID_HANDLE);
283 }
284
285 return (SCARD_S_SUCCESS);
286 }
287
288 /*
289 * This is called to free a library context from a client.
290 */
291 LONG
SCardReleaseContext(SCARDCONTEXT arg)292 SCardReleaseContext(SCARDCONTEXT arg)
293 {
294 if (!pcsc_valid_context(arg)) {
295 return (SCARD_E_INVALID_HANDLE);
296 }
297
298 /*
299 * Free any SCARD_AUTOALLOCATE memory now.
300 */
301 pcsc_hdl_t *hdl = arg;
302 pcsc_mem_t *mem;
303 while ((mem = list_head(&hdl->pcsc_autoalloc)) != NULL) {
304 pcsc_mem_free(hdl, mem->pcm_buf);
305 }
306 list_destroy(&hdl->pcsc_autoalloc);
307
308 /*
309 * Free any card handles that were not explicitly freed:
310 */
311 pcsc_card_t *card;
312 while ((card = list_head(&hdl->pcsc_cards)) != NULL) {
313 pcsc_card_free(card);
314 }
315 list_destroy(&hdl->pcsc_cards);
316
317 free(hdl);
318 return (SCARD_S_SUCCESS);
319 }
320
321 /*
322 * This is called to release memory allocated by the library. No, it doesn't
323 * make sense to take a const pointer when being given memory to free. It just
324 * means we have to cast it, but remember: this isn't our API.
325 */
326 LONG
SCardFreeMemory(SCARDCONTEXT hdl,LPCVOID mem)327 SCardFreeMemory(SCARDCONTEXT hdl, LPCVOID mem)
328 {
329 if (!pcsc_valid_context(hdl)) {
330 return (SCARD_E_INVALID_HANDLE);
331 }
332
333 pcsc_mem_free(hdl, (void *)mem);
334 return (SCARD_S_SUCCESS);
335 }
336
337 /*
338 * This is called by a caller to get a list of readers that exist in the system.
339 * If lenp is set to SCARD_AUTOALLOCATE, then we are responsible for dealing
340 * with this memory.
341 */
342 LONG
SCardListReaders(SCARDCONTEXT arg,LPCSTR groups,LPSTR bufp,LPDWORD lenp)343 SCardListReaders(SCARDCONTEXT arg, LPCSTR groups, LPSTR bufp, LPDWORD lenp)
344 {
345 pcsc_hdl_t *hdl = arg;
346 FTS *fts;
347 FTSENT *ent;
348 char *const root[] = { "/dev/ccid", NULL };
349 char *ubuf;
350 char **readers;
351 uint32_t len, ulen, npaths, nalloc, off, i;
352 int ret;
353
354 if (!pcsc_valid_context(hdl)) {
355 return (SCARD_E_INVALID_HANDLE);
356 }
357
358 if (groups != NULL || lenp == NULL) {
359 return (SCARD_E_INVALID_PARAMETER);
360 }
361
362 fts = fts_open(root, FTS_LOGICAL | FTS_NOCHDIR, NULL);
363 if (fts == NULL) {
364 switch (errno) {
365 case ENOENT:
366 case ENOTDIR:
367 return (SCARD_E_NO_READERS_AVAILABLE);
368 case ENOMEM:
369 case EAGAIN:
370 return (SCARD_E_NO_MEMORY);
371 default:
372 return (SCARD_E_NO_SERVICE);
373 }
374 }
375
376 npaths = nalloc = 0;
377 /*
378 * Account for the NUL we'll have to place at the end of this.
379 */
380 len = 1;
381 readers = NULL;
382 while ((ent = fts_read(fts)) != NULL) {
383 size_t plen;
384
385 if (ent->fts_level != 2 || ent->fts_info == FTS_DP)
386 continue;
387
388 if (ent->fts_info == FTS_ERR || ent->fts_info == FTS_NS)
389 continue;
390
391 if (S_ISCHR(ent->fts_statp->st_mode) == 0)
392 continue;
393
394 plen = strlen(ent->fts_path) + 1;
395 if (UINT32_MAX - len <= plen) {
396 /*
397 * I mean, it's true. But I wish I could just give you
398 * EOVERFLOW.
399 */
400 ret = SCARD_E_INSUFFICIENT_BUFFER;
401 goto out;
402 }
403
404 if (npaths == nalloc) {
405 char **tmp;
406
407 nalloc += 8;
408 tmp = reallocarray(readers, nalloc, sizeof (char *));
409 if (tmp == NULL) {
410 ret = SCARD_E_NO_MEMORY;
411 goto out;
412 }
413 readers = tmp;
414 }
415 readers[npaths] = strdup(ent->fts_path);
416 npaths++;
417 len += plen;
418 }
419
420 if (npaths == 0) {
421 ret = SCARD_E_NO_READERS_AVAILABLE;
422 goto out;
423 }
424
425 ulen = *lenp;
426 *lenp = len;
427 if (ulen != SCARD_AUTOALLOCATE) {
428 if (bufp == NULL) {
429 ret = SCARD_S_SUCCESS;
430 goto out;
431 }
432
433 if (ulen < len) {
434 ret = SCARD_E_INSUFFICIENT_BUFFER;
435 goto out;
436 }
437
438 ubuf = bufp;
439 } else {
440 char **bufpp;
441 if (bufp == NULL) {
442 ret = SCARD_E_INVALID_PARAMETER;
443 goto out;
444 }
445
446 if ((ubuf = pcsc_mem_alloc(hdl, ulen)) == NULL) {
447 ret = SCARD_E_NO_MEMORY;
448 goto out;
449 }
450
451 bufpp = (void *)bufp;
452 *bufpp = ubuf;
453 }
454 ret = SCARD_S_SUCCESS;
455
456 for (off = 0, i = 0; i < npaths; i++) {
457 size_t slen = strlen(readers[i]) + 1;
458 bcopy(readers[i], ubuf + off, slen);
459 off += slen;
460 VERIFY3U(off, <=, len);
461 }
462 VERIFY3U(off, ==, len - 1);
463 ubuf[off] = '\0';
464 out:
465 for (i = 0; i < npaths; i++) {
466 free(readers[i]);
467 }
468 free(readers);
469 (void) fts_close(fts);
470 return (ret);
471 }
472
473 static LONG
uccid_status_helper(int fd,DWORD prots,uccid_cmd_status_t * ucs)474 uccid_status_helper(int fd, DWORD prots, uccid_cmd_status_t *ucs)
475 {
476 /*
477 * Get the status of this slot and find out information about the slot.
478 * We need to see if there's an ICC present and if it matches the
479 * current protocol. If not, then we have to fail this.
480 */
481 bzero(ucs, sizeof (uccid_cmd_status_t));
482 ucs->ucs_version = UCCID_CURRENT_VERSION;
483 if (ioctl(fd, UCCID_CMD_STATUS, ucs) != 0) {
484 return (SCARD_F_UNKNOWN_ERROR);
485 }
486
487 if ((ucs->ucs_status & UCCID_STATUS_F_CARD_PRESENT) == 0) {
488 return (SCARD_W_REMOVED_CARD);
489 }
490
491 if ((ucs->ucs_status & UCCID_STATUS_F_CARD_ACTIVE) == 0) {
492 return (SCARD_W_UNPOWERED_CARD);
493 }
494
495 if ((ucs->ucs_status & UCCID_STATUS_F_PARAMS_VALID) == 0) {
496 return (SCARD_W_UNSUPPORTED_CARD);
497 }
498
499 if ((ucs->ucs_prot & prots) == 0) {
500 return (SCARD_E_PROTO_MISMATCH);
501 }
502
503 return (0);
504 }
505
506 LONG
SCardConnect(SCARDCONTEXT hdl,LPCSTR reader,DWORD mode,DWORD prots,LPSCARDHANDLE iccp,LPDWORD protp)507 SCardConnect(SCARDCONTEXT hdl, LPCSTR reader, DWORD mode, DWORD prots,
508 LPSCARDHANDLE iccp, LPDWORD protp)
509 {
510 LONG ret;
511 uccid_cmd_status_t ucs;
512 pcsc_card_t *card;
513
514 if (!pcsc_valid_context(hdl)) {
515 return (SCARD_E_INVALID_HANDLE);
516 }
517
518 if (reader == NULL) {
519 return (SCARD_E_UNKNOWN_READER);
520 }
521
522 if (iccp == NULL || protp == NULL) {
523 return (SCARD_E_INVALID_PARAMETER);
524 }
525
526 if (mode != SCARD_SHARE_SHARED) {
527 return (SCARD_E_INVALID_VALUE);
528 }
529
530 if ((prots & ~(SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1 |
531 SCARD_PROTOCOL_RAW | SCARD_PROTOCOL_T15)) != 0) {
532 return (SCARD_E_INVALID_VALUE);
533 }
534
535 if ((prots & (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)) == 0) {
536 return (SCARD_E_UNSUPPORTED_FEATURE);
537 }
538
539 if ((card = pcsc_card_alloc(hdl, reader)) == NULL) {
540 pcsc_card_free(card);
541 return (SCARD_E_NO_MEMORY);
542 }
543
544 if ((card->pcc_fd = open(reader, O_RDWR)) < 0) {
545 pcsc_card_free(card);
546 switch (errno) {
547 case ENOENT:
548 return (SCARD_E_UNKNOWN_READER);
549 default:
550 return (SCARD_F_UNKNOWN_ERROR);
551 }
552 }
553
554 if ((ret = uccid_status_helper(card->pcc_fd, prots, &ucs)) != 0) {
555 pcsc_card_free(card);
556 return (ret);
557 }
558
559 *protp = ucs.ucs_prot;
560 *iccp = card;
561 return (SCARD_S_SUCCESS);
562 }
563
564 /*
565 * The Windows documentation suggests that all of the input/output arguments
566 * (other than the handle) are effectively optional.
567 */
568 LONG
SCardStatus(SCARDHANDLE arg,LPSTR readerp,LPDWORD readerlenp,LPDWORD statep,LPDWORD protop,LPBYTE atrp,LPDWORD atrlenp)569 SCardStatus(SCARDHANDLE arg, LPSTR readerp, LPDWORD readerlenp,
570 LPDWORD statep, LPDWORD protop, LPBYTE atrp, LPDWORD atrlenp)
571 {
572 pcsc_card_t *card = arg;
573 pcsc_hdl_t *hdl = card->pcc_hdl;
574 LONG ret = SCARD_S_SUCCESS;
575
576 if (statep == NULL && protop == NULL && atrlenp == NULL) {
577 /*
578 * There is no need to perform the status ioctl.
579 */
580 goto name;
581 }
582
583 uccid_cmd_status_t ucs = { .ucs_version = UCCID_CURRENT_VERSION };
584 if (ioctl(card->pcc_fd, UCCID_CMD_STATUS, &ucs) != 0) {
585 VERIFY3S(errno, ==, ENODEV);
586 ret = SCARD_E_READER_UNAVAILABLE;
587 goto out;
588 }
589
590 if (statep != NULL) {
591 if (!(ucs.ucs_status & UCCID_STATUS_F_CARD_PRESENT)) {
592 *statep = SCARD_ABSENT;
593 } else if (ucs.ucs_status & UCCID_STATUS_F_CARD_ACTIVE) {
594 if (ucs.ucs_status & UCCID_STATUS_F_PARAMS_VALID) {
595 *statep = SCARD_SPECIFIC;
596 } else {
597 *statep = SCARD_POWERED;
598 }
599 } else {
600 *statep = SCARD_PRESENT;
601 }
602 }
603
604 if (protop != NULL) {
605 if (ucs.ucs_status & UCCID_STATUS_F_PARAMS_VALID) {
606 switch (ucs.ucs_prot) {
607 case UCCID_PROT_T0:
608 *protop = SCARD_PROTOCOL_T0;
609 break;
610 case UCCID_PROT_T1:
611 *protop = SCARD_PROTOCOL_T1;
612 break;
613 default:
614 *protop = SCARD_PROTOCOL_UNDEFINED;
615 break;
616 }
617 } else {
618 /*
619 * If SCARD_SPECIFIC is not returned as the card
620 * state, this value is not considered meaningful.
621 */
622 *protop = SCARD_PROTOCOL_UNDEFINED;
623 }
624 }
625
626 if (atrlenp != NULL) {
627 uint8_t *ubuf;
628 uint32_t len = *atrlenp;
629 if (len != SCARD_AUTOALLOCATE) {
630 if (len < ucs.ucs_atrlen) {
631 *atrlenp = ucs.ucs_atrlen;
632 ret = SCARD_E_INSUFFICIENT_BUFFER;
633 goto out;
634 }
635
636 if (atrp == NULL) {
637 ret = SCARD_E_INVALID_PARAMETER;
638 goto out;
639 }
640
641 ubuf = atrp;
642 } else {
643 if ((ubuf = pcsc_mem_alloc(hdl, ucs.ucs_atrlen)) ==
644 NULL) {
645 ret = SCARD_E_NO_MEMORY;
646 goto out;
647 }
648
649 *((LPBYTE *)atrp) = ubuf;
650 }
651
652 bcopy(ucs.ucs_atr, ubuf, ucs.ucs_atrlen);
653 *atrlenp = ucs.ucs_atrlen;
654 }
655
656 name:
657 if (readerlenp != NULL) {
658 char *ubuf;
659 uint32_t rlen = *readerlenp;
660 if (rlen != SCARD_AUTOALLOCATE) {
661 if (rlen < card->pcc_namelen) {
662 *readerlenp = card->pcc_namelen;
663 ret = SCARD_E_INSUFFICIENT_BUFFER;
664 goto out;
665 }
666
667 if (readerp == NULL) {
668 ret = SCARD_E_INVALID_PARAMETER;
669 goto out;
670 }
671
672 ubuf = readerp;
673 } else {
674 if ((ubuf = pcsc_mem_alloc(hdl, card->pcc_namelen)) ==
675 NULL) {
676 ret = SCARD_E_NO_MEMORY;
677 goto out;
678 }
679
680 *((LPSTR *)readerp) = ubuf;
681 }
682
683 /*
684 * We stored the reader name as a multi-string in
685 * pcsc_card_alloc(), so we can just copy out the whole value
686 * here without further modification:
687 */
688 bcopy(card->pcc_name, ubuf, card->pcc_namelen);
689 }
690
691 out:
692 return (ret);
693 }
694
695 LONG
SCardDisconnect(SCARDHANDLE arg,DWORD disposition)696 SCardDisconnect(SCARDHANDLE arg, DWORD disposition)
697 {
698 pcsc_card_t *card = arg;
699
700 if (arg == NULL) {
701 return (SCARD_E_INVALID_HANDLE);
702 }
703
704 switch (disposition) {
705 case SCARD_RESET_CARD: {
706 /*
707 * To reset the card, we first need to get exclusive access to
708 * the card.
709 */
710 uccid_cmd_txn_begin_t txnbegin = {
711 .uct_version = UCCID_CURRENT_VERSION,
712 };
713 if (ioctl(card->pcc_fd, UCCID_CMD_TXN_BEGIN, &txnbegin) != 0) {
714 VERIFY3S(errno, !=, EFAULT);
715
716 switch (errno) {
717 case ENODEV:
718 /*
719 * If the card is no longer present, we cannot
720 * reset it.
721 */
722 goto close;
723 case EEXIST:
724 break;
725 case EBUSY:
726 return (SCARD_E_SHARING_VIOLATION);
727 default:
728 return (SCARD_F_UNKNOWN_ERROR);
729 }
730 }
731
732 /*
733 * Once we have begun the transaction, we can end it
734 * immediately while requesting a reset before the next
735 * transaction.
736 */
737 uccid_cmd_txn_end_t txnend = {
738 .uct_version = UCCID_CURRENT_VERSION,
739 .uct_flags = UCCID_TXN_END_RESET,
740 };
741 if (ioctl(card->pcc_fd, UCCID_CMD_TXN_END, &txnend) != 0) {
742 VERIFY3S(errno, !=, EFAULT);
743
744 switch (errno) {
745 case ENODEV:
746 goto close;
747 default:
748 return (SCARD_F_UNKNOWN_ERROR);
749 }
750 }
751 }
752 case SCARD_LEAVE_CARD:
753 break;
754 default:
755 return (SCARD_E_INVALID_VALUE);
756 }
757
758 close:
759 if (close(card->pcc_fd) != 0) {
760 return (SCARD_F_UNKNOWN_ERROR);
761 }
762 card->pcc_fd = -1;
763
764 pcsc_card_free(card);
765 return (SCARD_S_SUCCESS);
766 }
767
768 LONG
SCardBeginTransaction(SCARDHANDLE arg)769 SCardBeginTransaction(SCARDHANDLE arg)
770 {
771 uccid_cmd_txn_begin_t txn;
772 pcsc_card_t *card = arg;
773
774 if (card == NULL) {
775 return (SCARD_E_INVALID_HANDLE);
776 }
777
778 /*
779 * According to the Windows documentation, this function "waits for the
780 * completion of all other transactions before it begins". Software in
781 * the wild has been observed to be confused by the
782 * SCARD_E_SHARING_VIOLATION condition we would previously return if
783 * the card was already in use. As a compatibility measure, we omit
784 * the UCCID_TXN_DONT_BLOCK flag so that the kernel will sleep until
785 * the card is no longer busy before returning.
786 */
787 bzero(&txn, sizeof (uccid_cmd_txn_begin_t));
788 txn.uct_version = UCCID_CURRENT_VERSION;
789
790 if (ioctl(card->pcc_fd, UCCID_CMD_TXN_BEGIN, &txn) != 0) {
791 VERIFY3S(errno, !=, EFAULT);
792 switch (errno) {
793 case ENODEV:
794 return (SCARD_E_READER_UNAVAILABLE);
795 case EEXIST:
796 /*
797 * This is an odd case. It's used to tell us that we
798 * already have it. For now, just treat it as success.
799 */
800 return (SCARD_S_SUCCESS);
801 case EBUSY:
802 return (SCARD_E_SHARING_VIOLATION);
803 /*
804 * EINPROGRESS is a weird case. It means that we were trying to
805 * grab a hold while another instance using the same handle was.
806 * For now, treat it as an unknown error.
807 */
808 case EINPROGRESS:
809 case EINTR:
810 default:
811 return (SCARD_F_UNKNOWN_ERROR);
812 }
813 }
814 return (SCARD_S_SUCCESS);
815 }
816
817 LONG
SCardEndTransaction(SCARDHANDLE arg,DWORD state)818 SCardEndTransaction(SCARDHANDLE arg, DWORD state)
819 {
820 uccid_cmd_txn_end_t txn;
821 pcsc_card_t *card = arg;
822
823 if (card == NULL) {
824 return (SCARD_E_INVALID_HANDLE);
825 }
826
827 bzero(&txn, sizeof (uccid_cmd_txn_end_t));
828 txn.uct_version = UCCID_CURRENT_VERSION;
829
830 switch (state) {
831 case SCARD_LEAVE_CARD:
832 txn.uct_flags = UCCID_TXN_END_RELEASE;
833 break;
834 case SCARD_RESET_CARD:
835 txn.uct_flags = UCCID_TXN_END_RESET;
836 break;
837 case SCARD_UNPOWER_CARD:
838 case SCARD_EJECT_CARD:
839 default:
840 return (SCARD_E_INVALID_VALUE);
841 }
842
843 if (ioctl(card->pcc_fd, UCCID_CMD_TXN_END, &txn) != 0) {
844 VERIFY3S(errno, !=, EFAULT);
845 switch (errno) {
846 case ENODEV:
847 return (SCARD_E_READER_UNAVAILABLE);
848 case ENXIO:
849 return (SCARD_E_SHARING_VIOLATION);
850 default:
851 return (SCARD_F_UNKNOWN_ERROR);
852 }
853 }
854 return (SCARD_S_SUCCESS);
855 }
856
857 LONG
SCardReconnect(SCARDHANDLE arg,DWORD mode,DWORD prots,DWORD init,LPDWORD protp)858 SCardReconnect(SCARDHANDLE arg, DWORD mode, DWORD prots, DWORD init,
859 LPDWORD protp)
860 {
861 uccid_cmd_status_t ucs;
862 pcsc_card_t *card = arg;
863 LONG ret;
864
865 if (card == NULL) {
866 return (SCARD_E_INVALID_HANDLE);
867 }
868
869 if (protp == NULL) {
870 return (SCARD_E_INVALID_PARAMETER);
871 }
872
873 if (mode != SCARD_SHARE_SHARED) {
874 return (SCARD_E_INVALID_VALUE);
875 }
876
877 if ((prots & ~(SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1 |
878 SCARD_PROTOCOL_RAW | SCARD_PROTOCOL_T15)) != 0) {
879 return (SCARD_E_INVALID_VALUE);
880 }
881
882 if ((prots & (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)) == 0) {
883 return (SCARD_E_UNSUPPORTED_FEATURE);
884 }
885
886 if (init != SCARD_LEAVE_CARD) {
887 return (SCARD_E_INVALID_VALUE);
888 }
889
890 if ((ret = uccid_status_helper(card->pcc_fd, prots, &ucs)) != 0)
891 return (ret);
892
893 *protp = ucs.ucs_prot;
894 return (SCARD_S_SUCCESS);
895 }
896
897 LONG
SCardTransmit(SCARDHANDLE arg,const SCARD_IO_REQUEST * sendreq,LPCBYTE sendbuf,DWORD sendlen,SCARD_IO_REQUEST * recvreq,LPBYTE recvbuf,LPDWORD recvlenp)898 SCardTransmit(SCARDHANDLE arg, const SCARD_IO_REQUEST *sendreq,
899 LPCBYTE sendbuf, DWORD sendlen, SCARD_IO_REQUEST *recvreq, LPBYTE recvbuf,
900 LPDWORD recvlenp)
901 {
902 int len;
903 ssize_t ret;
904 pcsc_card_t *card = arg;
905
906 if (card == NULL) {
907 return (SCARD_E_INVALID_HANDLE);
908 }
909
910 /*
911 * Ignore sendreq / recvreq.
912 */
913 if (sendbuf == NULL || recvbuf == NULL || recvlenp == NULL) {
914 return (SCARD_E_INVALID_PARAMETER);
915 }
916
917 /*
918 * The CCID write will always consume all data or none.
919 */
920 ret = write(card->pcc_fd, sendbuf, sendlen);
921 if (ret == -1) {
922 switch (errno) {
923 case E2BIG:
924 return (SCARD_E_INVALID_PARAMETER);
925 case ENODEV:
926 return (SCARD_E_READER_UNAVAILABLE);
927 case EACCES:
928 case EBUSY:
929 return (SCARD_E_SHARING_VIOLATION);
930 case ENXIO:
931 return (SCARD_W_REMOVED_CARD);
932 case EFAULT:
933 return (SCARD_E_INVALID_PARAMETER);
934 case ENOMEM:
935 default:
936 return (SCARD_F_UNKNOWN_ERROR);
937 }
938 }
939 ASSERT3S(ret, ==, sendlen);
940
941 /*
942 * Now, we should be able to block in read.
943 */
944 ret = read(card->pcc_fd, recvbuf, *recvlenp);
945 if (ret == -1) {
946 switch (errno) {
947 case EINVAL:
948 case EOVERFLOW:
949 /*
950 * This means that we need to update len with the real
951 * one.
952 */
953 if (ioctl(card->pcc_fd, FIONREAD, &len) != 0) {
954 return (SCARD_F_UNKNOWN_ERROR);
955 }
956 *recvlenp = len;
957 return (SCARD_E_INSUFFICIENT_BUFFER);
958 case ENODEV:
959 return (SCARD_E_READER_UNAVAILABLE);
960 case EACCES:
961 case EBUSY:
962 return (SCARD_E_SHARING_VIOLATION);
963 case EFAULT:
964 return (SCARD_E_INVALID_PARAMETER);
965 case ENODATA:
966 default:
967 return (SCARD_F_UNKNOWN_ERROR);
968 }
969 }
970
971 *recvlenp = ret;
972
973 return (SCARD_S_SUCCESS);
974 }
975