spiceccid.c revision d514b0f3
1/* 2 * Copyright (C) 2014 CodeWeavers, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 * 23 * Authors: 24 * Jeremy White <jwhite@codeweavers.com> 25 */ 26 27/*---------------------------------------------------------------------------- 28 Chip/Smart Card Interface Devices driver for Spice 29 30 This driver is built to interface to pcsc-lite as a serial smartcard 31 device. 32 It translates the IFD (Interface device) ABI into the Spice protocol. 33----------------------------------------------------------------------------*/ 34 35#include <stdio.h> 36#include <string.h> 37#include <stdlib.h> 38#include <sys/types.h> 39#include <sys/stat.h> 40#include <fcntl.h> 41#include <unistd.h> 42#include <pthread.h> 43#include <errno.h> 44#include <sys/socket.h> 45#include <sys/un.h> 46 47#include "vscard_common.h" 48#include "ifdhandler.h" 49#include <arpa/inet.h> 50 51typedef struct apdu_list { 52 void *data; 53 int len; 54 struct apdu_list *next; 55} apdu_t; 56 57#define MAX_LUNS 2 58typedef struct smartcard_ccid { 59 int fd; 60 int lun; 61 pthread_t tid; 62 int state; 63 char atr[36]; 64 int atr_len; 65 pthread_mutex_t apdu_lock; 66 apdu_t *apdu_list; 67} smartcard_ccid_t; 68 69#define STATE_OPEN 1 70#define STATE_READER_ADDED 2 71#define STATE_READER_REMOVED 4 72 73#if ! defined(MIN) 74#define MIN(x, y) (((x) < (y)) ? (x) : (y)) 75#endif 76 77 78smartcard_ccid_t luns[MAX_LUNS] = { { -1 }, { -1 } }; 79 80RESPONSECODE IFDHCloseChannel(DWORD Lun); 81 82static void push_apdu(smartcard_ccid_t *ccid, void *data, int len) 83{ 84 apdu_t *a = malloc(sizeof(*a)); 85 apdu_t **p; 86 87 a->data = malloc(len); 88 a->len = len; 89 a->next = NULL; 90 memcpy(a->data, data, len); 91 92 pthread_mutex_lock(&ccid->apdu_lock); 93 for (p = &ccid->apdu_list; *p; p = &(*p)->next) 94 ; 95 *p = a; 96 97 pthread_mutex_unlock(&ccid->apdu_lock); 98} 99 100static apdu_t * pop_apdu(smartcard_ccid_t *ccid) 101{ 102 apdu_t *p; 103 pthread_mutex_lock(&ccid->apdu_lock); 104 p = ccid->apdu_list; 105 if (ccid->apdu_list) 106 ccid->apdu_list = p->next; 107 pthread_mutex_unlock(&ccid->apdu_lock); 108 return p; 109} 110 111static void free_apdu(apdu_t *a) 112{ 113 free(a->data); 114 free(a); 115} 116 117static void send_reply(smartcard_ccid_t *ccid, uint32_t code) 118{ 119 uint32_t reply[4]; 120 121 reply[0] = htonl(VSC_Error); // type 122 reply[1] = htonl(ccid->lun); // reader id 123 reply[2] = htonl(sizeof(uint32_t)); // length 124 reply[3] = htonl(code); // Error code 125 126 if (write(ccid->fd, (char *) reply, sizeof(reply)) != sizeof(reply)) { 127 fprintf(stderr, "Error: lun %d fd %d write failed; errno %d\n", ccid->lun, ccid->fd, errno); 128 IFDHCloseChannel(ccid->lun); 129 } 130} 131 132static int send_tx_buffer(smartcard_ccid_t *ccid, void *data, int len) 133{ 134 uint32_t *reply, *p; 135 int write_len = sizeof(*reply) * 3 + len; 136 137 reply = malloc(write_len); 138 p = reply; 139 140 *p++ = htonl(VSC_APDU); // type 141 *p++ = htonl(ccid->lun); // reader id 142 *p++ = htonl(len); 143 memcpy(p, data, len); 144 145 if (write(ccid->fd, (char *) reply, write_len) != write_len) { 146 fprintf(stderr, "Error: lun %d fd %d write failed; errno %d\n", ccid->lun, ccid->fd, errno); 147 IFDHCloseChannel(ccid->lun); 148 free(reply); 149 return 0; 150 } 151 free(reply); 152 return 1; 153} 154 155static void process_reader_add(smartcard_ccid_t *ccid, VSCMsgHeader *h, char *data) 156{ 157 if (ccid->state & STATE_READER_ADDED) { 158 send_reply(ccid, VSC_GENERAL_ERROR); 159 return; 160 } 161 162 ccid->state |= STATE_READER_ADDED; 163 ccid->state &= ~STATE_READER_REMOVED; 164 165 pthread_mutex_init(&ccid->apdu_lock, NULL); 166 ccid->apdu_list = NULL; 167 168 send_reply(ccid, VSC_SUCCESS); 169} 170 171static void process_reader_remove(smartcard_ccid_t *ccid, VSCMsgHeader *h) 172{ 173 apdu_t *p; 174 175 if (ccid->state & STATE_READER_REMOVED) { 176 send_reply(ccid, VSC_GENERAL_ERROR); 177 return; 178 } 179 180 ccid->state |= STATE_READER_REMOVED; 181 ccid->state &= ~STATE_READER_ADDED; 182 183 while (p = pop_apdu(ccid)) 184 free_apdu(p); 185 186 pthread_mutex_destroy(&ccid->apdu_lock); 187 188 send_reply(ccid, VSC_SUCCESS); 189} 190 191static void process_atr(smartcard_ccid_t *ccid, VSCMsgHeader *h, char *data) 192{ 193 ccid->atr_len = h->length; 194 if (h->length > sizeof(ccid->atr)) { 195 fprintf(stderr, "Supplied ATR of length %d exceeds %d maximum\n", 196 h->length, sizeof(ccid->atr)); 197 send_reply(ccid, VSC_GENERAL_ERROR); 198 return; 199 } 200 201 memset(ccid->atr, 0, sizeof(ccid->atr)); 202 memcpy(ccid->atr, data, ccid->atr_len); 203 204 send_reply(ccid, VSC_SUCCESS); 205} 206 207static void process_apdu(smartcard_ccid_t *ccid, VSCMsgHeader *h, char *data) 208{ 209 if (ccid->state & STATE_READER_ADDED) 210 push_apdu(ccid, data, h->length); 211 else 212 fprintf(stderr, "apdu of length %d discarded; inactive reader\n", h->length); 213} 214 215static void process_card_remove(smartcard_ccid_t *ccid, VSCMsgHeader *h) 216{ 217 ccid->atr_len = 0; 218 memset(ccid->atr, 0, sizeof(ccid->atr)); 219 send_reply(ccid, VSC_SUCCESS); 220} 221 222static int process_message(smartcard_ccid_t *ccid, char *buf, int len) 223{ 224 VSCMsgHeader h; 225 uint32_t *p = (uint32_t *) buf; 226 227 h.type = ntohl(*p++); 228 h.reader_id = ntohl(*p++); 229 h.length = ntohl(*p++); 230 231 if (len < sizeof(h) || len < sizeof(h) + h.length) 232 return 0; 233 234 switch (h.type) { 235 case VSC_ReaderAdd: 236 process_reader_add(ccid, &h, h.length > 0 ? buf + sizeof(h) : NULL); 237 break; 238 239 case VSC_ReaderRemove: 240 process_reader_remove(ccid, &h); 241 break; 242 243 case VSC_ATR: 244 process_atr(ccid, &h, h.length > 0 ? buf + sizeof(h) : NULL); 245 break; 246 247 case VSC_CardRemove: 248 process_card_remove(ccid, &h); 249 break; 250 251 case VSC_APDU: 252 process_apdu(ccid, &h, h.length > 0 ? buf + sizeof(h) : NULL); 253 break; 254 255 default: 256 fprintf(stderr, "spiceccid %s: unknown smartcard message %d / %d\n", __FUNCTION__, h.type, sizeof(h) + h.length); 257 258 } 259 260 return(h.length + sizeof(h)); 261} 262 263static void * lun_thread(void *arg) 264{ 265 char buf[8096]; 266 int pos = 0; 267 smartcard_ccid_t *ccid = (smartcard_ccid_t *) arg; 268 int rc; 269 270 while (1) { 271 rc = read(ccid->fd, buf + pos, sizeof(buf) - pos); 272 if (rc == -1) 273 if (errno == EINTR) 274 continue; 275 else 276 break; 277 278 if (rc == 0) 279 break; 280 281 pos += rc; 282 283 do { 284 rc = process_message(ccid, buf, pos); 285 pos -= rc; 286 287 if (rc > 0 && pos > 0) 288 memmove(buf, buf + rc, pos); 289 } while (rc > 0 && pos > 0); 290 } 291 292 fprintf(stderr, "LUN %d thread exiting: %s\n", ccid->lun, 293 rc == 0 ? "normally" : strerror(errno)); 294 close(ccid->fd); 295 ccid->fd = -1; 296 ccid->lun = 0; 297 ccid->atr_len = 0; 298 ccid->state &= ~STATE_OPEN; 299 300 return NULL; 301} 302 303 304static void send_init(smartcard_ccid_t *ccid) 305{ 306 uint32_t msg[6]; 307 308 msg[0] = htonl(VSC_Init); // type 309 msg[1] = htonl(ccid->lun); // reader id 310 msg[2] = htonl(sizeof(uint32_t) * 3); // length 311 msg[3] = htonl(VSCARD_MAGIC); // VSCD 312 msg[4] = htonl(VSCARD_VERSION); // VSCD 313 msg[5] = 0; // capabilities 314 315 if (write(ccid->fd, (char *) msg, sizeof(msg)) != sizeof(msg)) { 316 fprintf(stderr, "Error: lun %d fd %d write failed; errno %d\n", ccid->lun, ccid->fd, errno); 317 IFDHCloseChannel(ccid->lun); 318 } 319} 320 321/*---------------------------------------------------------------------------- 322 IFDHCreateChannelByName 323 The pcsc daemon should invoke this function passing in the path name 324 configured in reader.conf. 325*/ 326RESPONSECODE IFDHCreateChannelByName(DWORD Lun, LPSTR DeviceName) 327{ 328 int i; 329 struct sockaddr_un addr; 330 331 for (i = 0; i < MAX_LUNS; i++) 332 if (luns[i].fd != -1 && luns[i].lun == Lun) 333 return IFD_COMMUNICATION_ERROR; 334 335 for (i = 0; i < MAX_LUNS; i++) 336 if (luns[i].fd == -1) 337 break; 338 339 if (i >= MAX_LUNS) 340 return IFD_COMMUNICATION_ERROR; 341 342 luns[i].fd = socket(AF_UNIX, SOCK_STREAM, 0); 343 if (luns[i].fd < 0) 344 return IFD_NO_SUCH_DEVICE; 345 346 memset(&addr, 0, sizeof(addr)); 347 addr.sun_family = AF_UNIX; 348 strncpy(addr.sun_path, DeviceName, sizeof(addr.sun_path) - 1); 349 if (connect(luns[i].fd, (struct sockaddr *) &addr, sizeof(addr))) { 350 close(luns[i].fd); 351 return IFD_COMMUNICATION_ERROR; 352 } 353 354 if (pthread_create(&luns[i].tid, NULL, &lun_thread, &luns[i])) { 355 close(luns[i].fd); 356 return IFD_COMMUNICATION_ERROR; 357 } 358 359 luns[i].lun = Lun; 360 luns[i].state = STATE_OPEN; 361 362 return IFD_SUCCESS; 363} 364 365RESPONSECODE IFDHCreateChannel(DWORD Lun, DWORD Channel) 366{ 367 fprintf(stderr, "spiceccid %s unsupported: Lun %ld, Channel %ld\n", __FUNCTION__, Lun, Channel); 368 return IFD_ERROR_NOT_SUPPORTED; 369} 370 371RESPONSECODE IFDHCloseChannel(DWORD Lun) 372{ 373 int i; 374 375 for (i = 0; i < MAX_LUNS; i++) { 376 if (luns[i].fd != -1 && luns[i].lun == Lun) { 377 pthread_cancel(luns[i].tid); 378 close(luns[i].fd); 379 luns[i].fd = -1; 380 luns[i].lun = 0; 381 luns[i].atr_len = 0; 382 luns[i].state &= ~STATE_OPEN; 383 break; 384 } 385 } 386 387 if (i == MAX_LUNS) 388 return IFD_NO_SUCH_DEVICE; 389 390 return IFD_SUCCESS; 391} 392 393RESPONSECODE IFDHGetCapabilities(DWORD Lun, DWORD Tag, PDWORD Length, PUCHAR Value) 394{ 395 fprintf(stderr, "spiceccid %s unsupported: Lun %ld, Tag %ld, Length %ld, Value %p\n", __FUNCTION__, Lun, Tag, *Length, Value); 396 /* TODO - explore supporting TAG_IFD_POLLING_THREAD */ 397 return IFD_ERROR_NOT_SUPPORTED; 398} 399 400RESPONSECODE IFDHSetCapabilities(DWORD Lun, DWORD Tag, DWORD Length, PUCHAR Value) 401{ 402 return IFD_ERROR_NOT_SUPPORTED; 403} 404 405RESPONSECODE IFDHPowerICC(DWORD Lun, DWORD Action, PUCHAR Atr, PDWORD AtrLength) 406{ 407 int i; 408 409 for (i = 0; i < MAX_LUNS; i++) 410 if (luns[i].fd != -1 && luns[i].lun == Lun) 411 if (Action == IFD_POWER_UP || Action == IFD_RESET) { 412 if (*AtrLength >= luns[i].atr_len) { 413 memcpy(Atr, luns[i].atr, luns[i].atr_len); 414 *AtrLength = luns[i].atr_len; 415 } 416 send_init(&luns[i]); 417 return IFD_SUCCESS; 418 } 419 420 fprintf(stderr, "spiceccid %s unsupported: Lun %ld, Action %ld\n", __FUNCTION__, Lun, Action); 421 return IFD_ERROR_NOT_SUPPORTED; 422} 423 424#define TX_MAX_SLEEP 5000 425#define TX_SLEEP_INTERVAL 1000 426RESPONSECODE IFDHTransmitToICC(DWORD Lun, SCARD_IO_HEADER SendPci, 427 PUCHAR TxBuffer, DWORD TxLength, PUCHAR RxBuffer, PDWORD 428 RxLength, PSCARD_IO_HEADER RecvPci) 429{ 430 apdu_t *p; 431 int i, j; 432 433 for (i = 0; i < MAX_LUNS; i++) 434 if (luns[i].fd != -1 && luns[i].lun == Lun) { 435 while (p = pop_apdu(&luns[i])) 436 free_apdu(p); 437 438 if (send_tx_buffer(&luns[i], TxBuffer, TxLength)) { 439 for (j = 0; j < TX_MAX_SLEEP; j++) 440 if (p = pop_apdu(&luns[i])) 441 break; 442 else 443 usleep(TX_SLEEP_INTERVAL); 444 445 if (p) { 446 memcpy(RxBuffer, p->data, MIN(p->len, *RxLength)); 447 *RxLength = MIN(p->len, *RxLength); 448 free_apdu(p); 449 return IFD_SUCCESS; 450 } 451 452 return IFD_RESPONSE_TIMEOUT; 453 } 454 } 455 return IFD_NO_SUCH_DEVICE; 456} 457 458RESPONSECODE IFDHICCPresence(DWORD Lun) 459{ 460 int i; 461 462 for (i = 0; i < MAX_LUNS; i++) 463 if (luns[i].fd != -1 && luns[i].lun == Lun) { 464 if (luns[i].atr_len > 0 && luns[i].state & STATE_READER_ADDED) 465 return IFD_SUCCESS; 466 467 return IFD_ICC_NOT_PRESENT; 468 } 469 470 return IFD_NO_SUCH_DEVICE; 471} 472 473RESPONSECODE IFDHSetProtocolParameters(DWORD Lun, DWORD Protocol, UCHAR Flags, 474 UCHAR PTS1, UCHAR PTS2, UCHAR PTS3) 475{ 476 if (Protocol == SCARD_PROTOCOL_T1) 477 return IFD_SUCCESS; 478 479 return IFD_NOT_SUPPORTED; 480} 481 482RESPONSECODE IFDHControl(DWORD Lun, DWORD dwControlCode, PUCHAR 483 TxBuffer, DWORD TxLength, PUCHAR RxBuffer, DWORD RxLength, 484 LPDWORD pdwBytesReturned) 485{ 486 fprintf(stderr, "spiceccid %s unsupported: Lun %ld\n", __FUNCTION__, Lun); 487 return IFD_ERROR_NOT_SUPPORTED; 488} 489