xref: /illumos-gate/usr/src/lib/libpcsc/common/libpcsc.c (revision fc910014e8a32a65612105835a10995f2c13d942)
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 *
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 *
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
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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 	 * The semantics of pcsc are that this operation does not block, but
780 	 * instead fails if we cannot grab it immediately.
781 	 */
782 	bzero(&txn, sizeof (uccid_cmd_txn_begin_t));
783 	txn.uct_version = UCCID_CURRENT_VERSION;
784 	txn.uct_flags = UCCID_TXN_DONT_BLOCK;
785 
786 	if (ioctl(card->pcc_fd, UCCID_CMD_TXN_BEGIN, &txn) != 0) {
787 		VERIFY3S(errno, !=, EFAULT);
788 		switch (errno) {
789 		case ENODEV:
790 			return (SCARD_E_READER_UNAVAILABLE);
791 		case EEXIST:
792 			/*
793 			 * This is an odd case. It's used to tell us that we
794 			 * already have it. For now, just treat it as success.
795 			 */
796 			return (SCARD_S_SUCCESS);
797 		case EBUSY:
798 			return (SCARD_E_SHARING_VIOLATION);
799 		/*
800 		 * EINPROGRESS is a weird case. It means that we were trying to
801 		 * grab a hold while another instance using the same handle was.
802 		 * For now, treat it as an unknown error.
803 		 */
804 		case EINPROGRESS:
805 		case EINTR:
806 		default:
807 			return (SCARD_F_UNKNOWN_ERROR);
808 		}
809 	}
810 	return (SCARD_S_SUCCESS);
811 }
812 
813 LONG
814 SCardEndTransaction(SCARDHANDLE arg, DWORD state)
815 {
816 	uccid_cmd_txn_end_t txn;
817 	pcsc_card_t *card = arg;
818 
819 	if (card == NULL) {
820 		return (SCARD_E_INVALID_HANDLE);
821 	}
822 
823 	bzero(&txn, sizeof (uccid_cmd_txn_end_t));
824 	txn.uct_version = UCCID_CURRENT_VERSION;
825 
826 	switch (state) {
827 	case SCARD_LEAVE_CARD:
828 		txn.uct_flags = UCCID_TXN_END_RELEASE;
829 		break;
830 	case SCARD_RESET_CARD:
831 		txn.uct_flags = UCCID_TXN_END_RESET;
832 		break;
833 	case SCARD_UNPOWER_CARD:
834 	case SCARD_EJECT_CARD:
835 	default:
836 		return (SCARD_E_INVALID_VALUE);
837 	}
838 
839 	if (ioctl(card->pcc_fd, UCCID_CMD_TXN_END, &txn) != 0) {
840 		VERIFY3S(errno, !=, EFAULT);
841 		switch (errno) {
842 		case ENODEV:
843 			return (SCARD_E_READER_UNAVAILABLE);
844 		case ENXIO:
845 			return (SCARD_E_SHARING_VIOLATION);
846 		default:
847 			return (SCARD_F_UNKNOWN_ERROR);
848 		}
849 	}
850 	return (SCARD_S_SUCCESS);
851 }
852 
853 LONG
854 SCardReconnect(SCARDHANDLE arg, DWORD mode, DWORD prots, DWORD init,
855     LPDWORD protp)
856 {
857 	uccid_cmd_status_t ucs;
858 	pcsc_card_t *card = arg;
859 	LONG ret;
860 
861 	if (card == NULL) {
862 		return (SCARD_E_INVALID_HANDLE);
863 	}
864 
865 	if (protp == NULL) {
866 		return (SCARD_E_INVALID_PARAMETER);
867 	}
868 
869 	if (mode != SCARD_SHARE_SHARED) {
870 		return (SCARD_E_INVALID_VALUE);
871 	}
872 
873 	if ((prots & ~(SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1 |
874 	    SCARD_PROTOCOL_RAW | SCARD_PROTOCOL_T15)) != 0) {
875 		return (SCARD_E_INVALID_VALUE);
876 	}
877 
878 	if ((prots & (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)) == 0) {
879 		return (SCARD_E_UNSUPPORTED_FEATURE);
880 	}
881 
882 	if (init != SCARD_LEAVE_CARD) {
883 		return (SCARD_E_INVALID_VALUE);
884 	}
885 
886 	if ((ret = uccid_status_helper(card->pcc_fd, prots, &ucs)) != 0)
887 		return (ret);
888 
889 	*protp = ucs.ucs_prot;
890 	return (SCARD_S_SUCCESS);
891 }
892 
893 LONG
894 SCardTransmit(SCARDHANDLE arg, const SCARD_IO_REQUEST *sendreq,
895     LPCBYTE sendbuf, DWORD sendlen, SCARD_IO_REQUEST *recvreq, LPBYTE recvbuf,
896     LPDWORD recvlenp)
897 {
898 	int len;
899 	ssize_t ret;
900 	pcsc_card_t *card = arg;
901 
902 	if (card == NULL) {
903 		return (SCARD_E_INVALID_HANDLE);
904 	}
905 
906 	/*
907 	 * Ignore sendreq / recvreq.
908 	 */
909 	if (sendbuf == NULL || recvbuf == NULL || recvlenp == NULL) {
910 		return (SCARD_E_INVALID_PARAMETER);
911 	}
912 
913 	/*
914 	 * The CCID write will always consume all data or none.
915 	 */
916 	ret = write(card->pcc_fd, sendbuf, sendlen);
917 	if (ret == -1) {
918 		switch (errno) {
919 		case E2BIG:
920 			return (SCARD_E_INVALID_PARAMETER);
921 		case ENODEV:
922 			return (SCARD_E_READER_UNAVAILABLE);
923 		case EACCES:
924 		case EBUSY:
925 			return (SCARD_E_SHARING_VIOLATION);
926 		case ENXIO:
927 			return (SCARD_W_REMOVED_CARD);
928 		case EFAULT:
929 			return (SCARD_E_INVALID_PARAMETER);
930 		case ENOMEM:
931 		default:
932 			return (SCARD_F_UNKNOWN_ERROR);
933 		}
934 	}
935 	ASSERT3S(ret, ==, sendlen);
936 
937 	/*
938 	 * Now, we should be able to block in read.
939 	 */
940 	ret = read(card->pcc_fd, recvbuf, *recvlenp);
941 	if (ret == -1) {
942 		switch (errno) {
943 		case EINVAL:
944 		case EOVERFLOW:
945 			/*
946 			 * This means that we need to update len with the real
947 			 * one.
948 			 */
949 			if (ioctl(card->pcc_fd, FIONREAD, &len) != 0) {
950 				return (SCARD_F_UNKNOWN_ERROR);
951 			}
952 			*recvlenp = len;
953 			return (SCARD_E_INSUFFICIENT_BUFFER);
954 		case ENODEV:
955 			return (SCARD_E_READER_UNAVAILABLE);
956 		case EACCES:
957 		case EBUSY:
958 			return (SCARD_E_SHARING_VIOLATION);
959 		case EFAULT:
960 			return (SCARD_E_INVALID_PARAMETER);
961 		case ENODATA:
962 		default:
963 			return (SCARD_F_UNKNOWN_ERROR);
964 		}
965 	}
966 
967 	*recvlenp = ret;
968 
969 	return (SCARD_S_SUCCESS);
970 }
971