xref: /illumos-gate/usr/src/lib/libpcsc/common/libpcsc.c (revision a92282e44f968185a6bba094d1e5fece2da819cf)
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  */
15 
16 #include <stdlib.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <fts.h>
21 #include <errno.h>
22 #include <strings.h>
23 #include <unistd.h>
24 #include <sys/debug.h>
25 #include <sys/filio.h>
26 #include <sys/usb/clients/ccid/uccid.h>
27 
28 #include <winscard.h>
29 
30 /*
31  * Implementation of the PCSC library leveraging the uccid framework.
32  */
33 
34 /*
35  * The library handle is basically unused today. We keep this around such that
36  * consumers which expect to receive a non-NULL opaque handle have something
37  * they can use.
38  */
39 typedef struct pcsc_hdl {
40 	hrtime_t pcsc_create_time;
41 } pcsc_hdl_t;
42 
43 typedef struct pcsc_card {
44 	int pcc_fd;
45 } pcsc_card_t;
46 
47 /*
48  * Required globals
49  */
50 SCARD_IO_REQUEST g_rgSCardT0Pci = {
51 	SCARD_PROTOCOL_T0,
52 	0
53 };
54 
55 SCARD_IO_REQUEST g_rgSCardT1Pci = {
56 	SCARD_PROTOCOL_T1,
57 	0
58 };
59 
60 SCARD_IO_REQUEST g_rgSCardRawPci = {
61 	SCARD_PROTOCOL_RAW,
62 	0
63 };
64 
65 const char *
66 pcsc_stringify_error(const LONG err)
67 {
68 	switch (err) {
69 	case SCARD_S_SUCCESS:
70 		return ("no error");
71 	case SCARD_F_INTERNAL_ERROR:
72 		return ("internal error");
73 	case SCARD_E_CANCELLED:
74 		return ("request cancelled");
75 	case SCARD_E_INVALID_HANDLE:
76 		return ("invalid handle");
77 	case SCARD_E_INVALID_PARAMETER:
78 		return ("invalid parameter");
79 	case SCARD_E_NO_MEMORY:
80 		return ("no memory");
81 	case SCARD_E_INSUFFICIENT_BUFFER:
82 		return ("buffer was insufficiently sized");
83 	case SCARD_E_INVALID_VALUE:
84 		return ("invalid value passed");
85 	case SCARD_E_UNKNOWN_READER:
86 		return ("unknown reader");
87 	case SCARD_E_TIMEOUT:
88 		return ("timeout occurred");
89 	case SCARD_E_SHARING_VIOLATION:
90 		return ("sharing violation");
91 	case SCARD_E_NO_SMARTCARD:
92 		return ("no smartcard present");
93 	case SCARD_E_UNKNOWN_CARD:
94 		return ("unknown ICC");
95 	case SCARD_E_PROTO_MISMATCH:
96 		return ("protocol mismatch");
97 	case SCARD_F_COMM_ERROR:
98 		return ("communication error");
99 	case SCARD_F_UNKNOWN_ERROR:
100 		return ("unknown error");
101 	case SCARD_E_READER_UNAVAILABLE:
102 		return ("reader unavailable");
103 	case SCARD_E_NO_SERVICE:
104 		return ("service error");
105 	case SCARD_E_UNSUPPORTED_FEATURE:
106 		return ("ICC requires unsupported feature");
107 	case SCARD_E_NO_READERS_AVAILABLE:
108 		return ("no readers avaiable");
109 	case SCARD_W_UNSUPPORTED_CARD:
110 		return ("ICC unsupported");
111 	case SCARD_W_UNPOWERED_CARD:
112 		return ("ICC is not powered");
113 	case SCARD_W_RESET_CARD:
114 		return ("ICC was reset");
115 	case SCARD_W_REMOVED_CARD:
116 		return ("ICC has been removed");
117 	default:
118 		return ("unknown error");
119 	}
120 }
121 
122 
123 /*
124  * This is called when a caller wishes to open a new Library context.
125  */
126 LONG
127 SCardEstablishContext(DWORD scope, LPCVOID unused0, LPCVOID unused1,
128     LPSCARDCONTEXT outp)
129 {
130 	pcsc_hdl_t *hdl;
131 
132 	if (outp == NULL) {
133 		return (SCARD_E_INVALID_PARAMETER);
134 	}
135 
136 	if (scope != SCARD_SCOPE_SYSTEM) {
137 		return (SCARD_E_INVALID_VALUE);
138 	}
139 
140 	hdl = calloc(1, sizeof (pcsc_hdl_t));
141 	if (hdl == NULL) {
142 		return (SCARD_E_NO_MEMORY);
143 	}
144 
145 	hdl->pcsc_create_time = gethrtime();
146 	*outp = hdl;
147 	return (SCARD_S_SUCCESS);
148 }
149 
150 /*
151  * This is called to free a library context from a client.
152  */
153 LONG
154 SCardReleaseContext(SCARDCONTEXT hdl)
155 {
156 	free(hdl);
157 	return (SCARD_S_SUCCESS);
158 }
159 
160 /*
161  * This is called to release memory allocated by the library. No, it doesn't
162  * make sense to take a const pointer when being given memory to free. It just
163  * means we have to cast it, but remember: this isn't our API.
164  */
165 LONG
166 SCardFreeMemory(SCARDCONTEXT unused, LPCVOID mem)
167 {
168 	free((void *)mem);
169 	return (SCARD_S_SUCCESS);
170 }
171 
172 /*
173  * This is called by a caller to get a list of readers that exist in the system.
174  * If lenp is set to SCARD_AUTOALLOCATE, then we are responsible for dealing
175  * with this memory.
176  */
177 LONG
178 SCardListReaders(SCARDCONTEXT unused, LPCSTR groups, LPSTR bufp, LPDWORD lenp)
179 {
180 	FTS *fts;
181 	FTSENT *ent;
182 	char *const root[] = { "/dev/ccid", NULL };
183 	char *ubuf;
184 	char **readers;
185 	uint32_t len, ulen, npaths, nalloc, off, i;
186 	int ret;
187 
188 	if (groups != NULL || lenp == NULL) {
189 		return (SCARD_E_INVALID_PARAMETER);
190 	}
191 
192 	fts = fts_open(root, FTS_LOGICAL | FTS_NOCHDIR, NULL);
193 	if (fts == NULL) {
194 		switch (errno) {
195 		case ENOENT:
196 		case ENOTDIR:
197 			return (SCARD_E_NO_READERS_AVAILABLE);
198 		case ENOMEM:
199 		case EAGAIN:
200 			return (SCARD_E_NO_MEMORY);
201 		default:
202 			return (SCARD_E_NO_SERVICE);
203 		}
204 	}
205 
206 	npaths = nalloc = 0;
207 	/*
208 	 * Account for the NUL we'll have to place at the end of this.
209 	 */
210 	len = 1;
211 	readers = NULL;
212 	while ((ent = fts_read(fts)) != NULL) {
213 		size_t plen;
214 
215 		if (ent->fts_level != 2 || ent->fts_info == FTS_DP)
216 			continue;
217 
218 		if (ent->fts_info == FTS_ERR || ent->fts_info == FTS_NS)
219 			continue;
220 
221 		if (S_ISCHR(ent->fts_statp->st_mode) == 0)
222 			continue;
223 
224 		plen = strlen(ent->fts_path) + 1;
225 		if (UINT32_MAX - len <= plen) {
226 			/*
227 			 * I mean, it's true. But I wish I could just give you
228 			 * EOVERFLOW.
229 			 */
230 			ret = SCARD_E_INSUFFICIENT_BUFFER;
231 			goto out;
232 		}
233 
234 		if (npaths == nalloc) {
235 			char **tmp;
236 
237 			nalloc += 8;
238 			tmp = reallocarray(readers, nalloc, sizeof (char *));
239 			if (tmp == NULL) {
240 				ret = SCARD_E_NO_MEMORY;
241 				goto out;
242 			}
243 			readers = tmp;
244 		}
245 		readers[npaths] = strdup(ent->fts_path);
246 		npaths++;
247 		len += plen;
248 	}
249 
250 	if (npaths == 0) {
251 		ret = SCARD_E_NO_READERS_AVAILABLE;
252 		goto out;
253 	}
254 
255 	ulen = *lenp;
256 	*lenp = len;
257 	if (ulen != SCARD_AUTOALLOCATE) {
258 		if (bufp == NULL) {
259 			ret = SCARD_S_SUCCESS;
260 			goto out;
261 		}
262 
263 		if (ulen < len) {
264 			ret = SCARD_E_INSUFFICIENT_BUFFER;
265 			goto out;
266 		}
267 
268 		ubuf = bufp;
269 	} else {
270 		char **bufpp;
271 		if (bufp == NULL) {
272 			ret = SCARD_E_INVALID_PARAMETER;
273 			goto out;
274 		}
275 
276 		ubuf = malloc(ulen);
277 		if (ubuf == NULL) {
278 			ret = SCARD_E_NO_MEMORY;
279 			goto out;
280 		}
281 
282 		bufpp = (void *)bufp;
283 		*bufpp = ubuf;
284 	}
285 	ret = SCARD_S_SUCCESS;
286 
287 	for (off = 0, i = 0; i < npaths; i++) {
288 		size_t slen = strlen(readers[i]) + 1;
289 		bcopy(readers[i], ubuf + off, slen);
290 		off += slen;
291 		VERIFY3U(off, <=, len);
292 	}
293 	VERIFY3U(off, ==, len - 1);
294 	ubuf[off] = '\0';
295 out:
296 	for (i = 0; i < npaths; i++) {
297 		free(readers[i]);
298 	}
299 	free(readers);
300 	(void) fts_close(fts);
301 	return (ret);
302 }
303 
304 static LONG
305 uccid_status_helper(int fd, DWORD prots, uccid_cmd_status_t *ucs)
306 {
307 	/*
308 	 * Get the status of this slot and find out information about the slot.
309 	 * We need to see if there's an ICC present and if it matches the
310 	 * current protocol. If not, then we have to fail this.
311 	 */
312 	bzero(ucs, sizeof (uccid_cmd_status_t));
313 	ucs->ucs_version = UCCID_CURRENT_VERSION;
314 	if (ioctl(fd, UCCID_CMD_STATUS, ucs) != 0) {
315 		return (SCARD_F_UNKNOWN_ERROR);
316 	}
317 
318 	if ((ucs->ucs_status & UCCID_STATUS_F_CARD_PRESENT) == 0) {
319 		return (SCARD_W_REMOVED_CARD);
320 	}
321 
322 	if ((ucs->ucs_status & UCCID_STATUS_F_CARD_ACTIVE) == 0) {
323 		return (SCARD_W_UNPOWERED_CARD);
324 	}
325 
326 	if ((ucs->ucs_status & UCCID_STATUS_F_PARAMS_VALID) == 0) {
327 		return (SCARD_W_UNSUPPORTED_CARD);
328 	}
329 
330 	if ((ucs->ucs_prot & prots) == 0) {
331 		return (SCARD_E_PROTO_MISMATCH);
332 	}
333 
334 	return (0);
335 }
336 
337 LONG
338 SCardConnect(SCARDCONTEXT hdl, LPCSTR reader, DWORD mode, DWORD prots,
339     LPSCARDHANDLE iccp, LPDWORD protp)
340 {
341 	LONG ret;
342 	uccid_cmd_status_t ucs;
343 	pcsc_card_t *card;
344 
345 	if (reader == NULL) {
346 		return (SCARD_E_UNKNOWN_READER);
347 	}
348 
349 	if (iccp == NULL || protp == NULL) {
350 		return (SCARD_E_INVALID_PARAMETER);
351 	}
352 
353 	if (mode != SCARD_SHARE_SHARED) {
354 		return (SCARD_E_INVALID_VALUE);
355 	}
356 
357 	if ((prots & ~(SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1 |
358 	    SCARD_PROTOCOL_RAW | SCARD_PROTOCOL_T15)) != 0) {
359 		return (SCARD_E_INVALID_VALUE);
360 	}
361 
362 	if ((prots & (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)) == 0) {
363 		return (SCARD_E_UNSUPPORTED_FEATURE);
364 	}
365 
366 	if ((card = malloc(sizeof (*card))) == NULL) {
367 		return (SCARD_E_NO_MEMORY);
368 	}
369 
370 	if ((card->pcc_fd = open(reader, O_RDWR)) < 0) {
371 		free(card);
372 		switch (errno) {
373 		case ENOENT:
374 			return (SCARD_E_UNKNOWN_READER);
375 		default:
376 			return (SCARD_F_UNKNOWN_ERROR);
377 		}
378 	}
379 
380 	if ((ret = uccid_status_helper(card->pcc_fd, prots, &ucs)) != 0)
381 		goto cleanup;
382 
383 	*protp = ucs.ucs_prot;
384 	*iccp = card;
385 	return (SCARD_S_SUCCESS);
386 cleanup:
387 	(void) close(card->pcc_fd);
388 	free(card);
389 	return (ret);
390 }
391 
392 LONG
393 SCardDisconnect(SCARDHANDLE arg, DWORD disposition)
394 {
395 	pcsc_card_t *card = arg;
396 
397 	if (arg == NULL) {
398 		return (SCARD_E_INVALID_HANDLE);
399 	}
400 
401 	if (disposition != SCARD_LEAVE_CARD) {
402 		return (SCARD_E_INVALID_VALUE);
403 	}
404 
405 	if (close(card->pcc_fd) != 0) {
406 		return (SCARD_F_UNKNOWN_ERROR);
407 	}
408 
409 	free(card);
410 	return (SCARD_S_SUCCESS);
411 }
412 
413 LONG
414 SCardBeginTransaction(SCARDHANDLE arg)
415 {
416 	uccid_cmd_txn_begin_t txn;
417 	pcsc_card_t *card = arg;
418 
419 	if (card == NULL) {
420 		return (SCARD_E_INVALID_HANDLE);
421 	}
422 
423 	/*
424 	 * The semantics of pcsc are that this operation does not block, but
425 	 * instead fails if we cannot grab it immediately.
426 	 */
427 	bzero(&txn, sizeof (uccid_cmd_txn_begin_t));
428 	txn.uct_version = UCCID_CURRENT_VERSION;
429 	txn.uct_flags = UCCID_TXN_DONT_BLOCK;
430 
431 	if (ioctl(card->pcc_fd, UCCID_CMD_TXN_BEGIN, &txn) != 0) {
432 		VERIFY3S(errno, !=, EFAULT);
433 		switch (errno) {
434 		case ENODEV:
435 			return (SCARD_E_READER_UNAVAILABLE);
436 		case EEXIST:
437 			/*
438 			 * This is an odd case. It's used to tell us that we
439 			 * already have it. For now, just treat it as success.
440 			 */
441 			return (SCARD_S_SUCCESS);
442 		case EBUSY:
443 			return (SCARD_E_SHARING_VIOLATION);
444 		/*
445 		 * EINPROGRESS is a weird case. It means that we were trying to
446 		 * grab a hold while another instance using the same handle was.
447 		 * For now, treat it as an unknown error.
448 		 */
449 		case EINPROGRESS:
450 		case EINTR:
451 		default:
452 			return (SCARD_F_UNKNOWN_ERROR);
453 		}
454 	}
455 	return (SCARD_S_SUCCESS);
456 }
457 
458 LONG
459 SCardEndTransaction(SCARDHANDLE arg, DWORD state)
460 {
461 	uccid_cmd_txn_end_t txn;
462 	pcsc_card_t *card = arg;
463 
464 	if (card == NULL) {
465 		return (SCARD_E_INVALID_HANDLE);
466 	}
467 
468 	bzero(&txn, sizeof (uccid_cmd_txn_end_t));
469 	txn.uct_version = UCCID_CURRENT_VERSION;
470 
471 	switch (state) {
472 	case SCARD_LEAVE_CARD:
473 		txn.uct_flags = UCCID_TXN_END_RELEASE;
474 		break;
475 	case SCARD_RESET_CARD:
476 		txn.uct_flags = UCCID_TXN_END_RESET;
477 		break;
478 	case SCARD_UNPOWER_CARD:
479 	case SCARD_EJECT_CARD:
480 	default:
481 		return (SCARD_E_INVALID_VALUE);
482 	}
483 
484 	if (ioctl(card->pcc_fd, UCCID_CMD_TXN_END, &txn) != 0) {
485 		VERIFY3S(errno, !=, EFAULT);
486 		switch (errno) {
487 		case ENODEV:
488 			return (SCARD_E_READER_UNAVAILABLE);
489 		case ENXIO:
490 			return (SCARD_E_SHARING_VIOLATION);
491 		default:
492 			return (SCARD_F_UNKNOWN_ERROR);
493 		}
494 	}
495 	return (SCARD_S_SUCCESS);
496 }
497 
498 LONG
499 SCardReconnect(SCARDHANDLE arg, DWORD mode, DWORD prots, DWORD init,
500     LPDWORD protp)
501 {
502 	uccid_cmd_status_t ucs;
503 	pcsc_card_t *card = arg;
504 	LONG ret;
505 
506 	if (card == NULL) {
507 		return (SCARD_E_INVALID_HANDLE);
508 	}
509 
510 	if (protp == NULL) {
511 		return (SCARD_E_INVALID_PARAMETER);
512 	}
513 
514 	if (mode != SCARD_SHARE_SHARED) {
515 		return (SCARD_E_INVALID_VALUE);
516 	}
517 
518 	if ((prots & ~(SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1 |
519 	    SCARD_PROTOCOL_RAW | SCARD_PROTOCOL_T15)) != 0) {
520 		return (SCARD_E_INVALID_VALUE);
521 	}
522 
523 	if ((prots & (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)) == 0) {
524 		return (SCARD_E_UNSUPPORTED_FEATURE);
525 	}
526 
527 	if (init != SCARD_LEAVE_CARD) {
528 		return (SCARD_E_INVALID_VALUE);
529 	}
530 
531 	if ((ret = uccid_status_helper(card->pcc_fd, prots, &ucs)) != 0)
532 		return (ret);
533 
534 	*protp = ucs.ucs_prot;
535 	return (SCARD_S_SUCCESS);
536 }
537 
538 LONG
539 SCardTransmit(SCARDHANDLE arg, const SCARD_IO_REQUEST *sendreq,
540     LPCBYTE sendbuf, DWORD sendlen, SCARD_IO_REQUEST *recvreq, LPBYTE recvbuf,
541     LPDWORD recvlenp)
542 {
543 	int len;
544 	ssize_t ret;
545 	pcsc_card_t *card = arg;
546 
547 	if (card == NULL) {
548 		return (SCARD_E_INVALID_HANDLE);
549 	}
550 
551 	/*
552 	 * Ignore sendreq / recvreq.
553 	 */
554 	if (sendbuf == NULL || recvbuf == NULL || recvlenp == NULL) {
555 		return (SCARD_E_INVALID_PARAMETER);
556 	}
557 
558 	/*
559 	 * The CCID write will always consume all data or none.
560 	 */
561 	ret = write(card->pcc_fd, sendbuf, sendlen);
562 	if (ret == -1) {
563 		switch (errno) {
564 		case E2BIG:
565 			return (SCARD_E_INVALID_PARAMETER);
566 		case ENODEV:
567 			return (SCARD_E_READER_UNAVAILABLE);
568 		case EACCES:
569 		case EBUSY:
570 			return (SCARD_E_SHARING_VIOLATION);
571 		case ENXIO:
572 			return (SCARD_W_REMOVED_CARD);
573 		case EFAULT:
574 			return (SCARD_E_INVALID_PARAMETER);
575 		case ENOMEM:
576 		default:
577 			return (SCARD_F_UNKNOWN_ERROR);
578 		}
579 	}
580 	ASSERT3S(ret, ==, sendlen);
581 
582 	/*
583 	 * Now, we should be able to block in read.
584 	 */
585 	ret = read(card->pcc_fd, recvbuf, *recvlenp);
586 	if (ret == -1) {
587 		switch (errno) {
588 		case EINVAL:
589 		case EOVERFLOW:
590 			/*
591 			 * This means that we need to update len with the real
592 			 * one.
593 			 */
594 			if (ioctl(card->pcc_fd, FIONREAD, &len) != 0) {
595 				return (SCARD_F_UNKNOWN_ERROR);
596 			}
597 			*recvlenp = len;
598 			return (SCARD_E_INSUFFICIENT_BUFFER);
599 		case ENODEV:
600 			return (SCARD_E_READER_UNAVAILABLE);
601 		case EACCES:
602 		case EBUSY:
603 			return (SCARD_E_SHARING_VIOLATION);
604 		case EFAULT:
605 			return (SCARD_E_INVALID_PARAMETER);
606 		case ENODATA:
607 		default:
608 			return (SCARD_F_UNKNOWN_ERROR);
609 		}
610 	}
611 
612 	*recvlenp = ret;
613 
614 	return (SCARD_S_SUCCESS);
615 }
616