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