Statistics
| Branch: | Revision:

root / hw / ccid-card-emulated.c @ 8e31bf38

History | View | Annotate | Download (18.6 kB)

1
/*
2
 * CCID Card Device. Emulated card.
3
 *
4
 * Copyright (c) 2011 Red Hat.
5
 * Written by Alon Levy.
6
 *
7
 * This code is licensed under the GNU LGPL, version 2 or later.
8
 */
9

    
10
/*
11
 * It can be used to provide access to the local hardware in a non exclusive
12
 * way, or it can use certificates. It requires the usb-ccid bus.
13
 *
14
 * Usage 1: standard, mirror hardware reader+card:
15
 * qemu .. -usb -device usb-ccid -device ccid-card-emulated
16
 *
17
 * Usage 2: use certificates, no hardware required
18
 * one time: create the certificates:
19
 *  for i in 1 2 3; do
20
 *      certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=user$i" -n user$i
21
 *  done
22
 * qemu .. -usb -device usb-ccid \
23
 *  -device ccid-card-emulated,cert1=user1,cert2=user2,cert3=user3
24
 *
25
 * If you use a non default db for the certificates you can specify it using
26
 * the db parameter.
27
 */
28

    
29
#include <eventt.h>
30
#include <vevent.h>
31
#include <vreader.h>
32
#include <vcard_emul.h>
33

    
34
#include "qemu-thread.h"
35
#include "qemu-char.h"
36
#include "monitor.h"
37
#include "hw/ccid.h"
38

    
39
#define DPRINTF(card, lvl, fmt, ...) \
40
do {\
41
    if (lvl <= card->debug) {\
42
        printf("ccid-card-emul: %s: " fmt , __func__, ## __VA_ARGS__);\
43
    } \
44
} while (0)
45

    
46
#define EMULATED_DEV_NAME "ccid-card-emulated"
47

    
48
#define BACKEND_NSS_EMULATED_NAME "nss-emulated"
49
#define BACKEND_CERTIFICATES_NAME "certificates"
50

    
51
enum {
52
    BACKEND_NSS_EMULATED = 1,
53
    BACKEND_CERTIFICATES
54
};
55

    
56
#define DEFAULT_BACKEND BACKEND_NSS_EMULATED
57

    
58
typedef struct EmulatedState EmulatedState;
59

    
60
enum {
61
    EMUL_READER_INSERT = 0,
62
    EMUL_READER_REMOVE,
63
    EMUL_CARD_INSERT,
64
    EMUL_CARD_REMOVE,
65
    EMUL_GUEST_APDU,
66
    EMUL_RESPONSE_APDU,
67
    EMUL_ERROR,
68
};
69

    
70
static const char *emul_event_to_string(uint32_t emul_event)
71
{
72
    switch (emul_event) {
73
    case EMUL_READER_INSERT:
74
        return "EMUL_READER_INSERT";
75
    case EMUL_READER_REMOVE:
76
        return "EMUL_READER_REMOVE";
77
    case EMUL_CARD_INSERT:
78
        return "EMUL_CARD_INSERT";
79
    case EMUL_CARD_REMOVE:
80
        return "EMUL_CARD_REMOVE";
81
    case EMUL_GUEST_APDU:
82
        return "EMUL_GUEST_APDU";
83
    case EMUL_RESPONSE_APDU:
84
        return "EMUL_RESPONSE_APDU";
85
    case EMUL_ERROR:
86
        return "EMUL_ERROR";
87
    }
88
    return "UNKNOWN";
89
}
90

    
91
typedef struct EmulEvent {
92
    QSIMPLEQ_ENTRY(EmulEvent) entry;
93
    union {
94
        struct {
95
            uint32_t type;
96
        } gen;
97
        struct {
98
            uint32_t type;
99
            uint64_t code;
100
        } error;
101
        struct {
102
            uint32_t type;
103
            uint32_t len;
104
            uint8_t data[];
105
        } data;
106
    } p;
107
} EmulEvent;
108

    
109
#define MAX_ATR_SIZE 40
110
struct EmulatedState {
111
    CCIDCardState base;
112
    uint8_t  debug;
113
    char    *backend_str;
114
    uint32_t backend;
115
    char    *cert1;
116
    char    *cert2;
117
    char    *cert3;
118
    char    *db;
119
    uint8_t  atr[MAX_ATR_SIZE];
120
    uint8_t  atr_length;
121
    QSIMPLEQ_HEAD(event_list, EmulEvent) event_list;
122
    QemuMutex event_list_mutex;
123
    VReader *reader;
124
    QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list;
125
    QemuMutex vreader_mutex; /* and guest_apdu_list mutex */
126
    QemuMutex handle_apdu_mutex;
127
    QemuCond handle_apdu_cond;
128
    int      pipe[2];
129
    int      quit_apdu_thread;
130
    QemuMutex apdu_thread_quit_mutex;
131
    QemuCond apdu_thread_quit_cond;
132
};
133

    
134
static void emulated_apdu_from_guest(CCIDCardState *base,
135
    const uint8_t *apdu, uint32_t len)
136
{
137
    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
138
    EmulEvent *event = (EmulEvent *)qemu_malloc(sizeof(EmulEvent) + len);
139

    
140
    assert(event);
141
    event->p.data.type = EMUL_GUEST_APDU;
142
    event->p.data.len = len;
143
    memcpy(event->p.data.data, apdu, len);
144
    qemu_mutex_lock(&card->vreader_mutex);
145
    QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry);
146
    qemu_mutex_unlock(&card->vreader_mutex);
147
    qemu_mutex_lock(&card->handle_apdu_mutex);
148
    qemu_cond_signal(&card->handle_apdu_cond);
149
    qemu_mutex_unlock(&card->handle_apdu_mutex);
150
}
151

    
152
static const uint8_t *emulated_get_atr(CCIDCardState *base, uint32_t *len)
153
{
154
    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
155

    
156
    *len = card->atr_length;
157
    return card->atr;
158
}
159

    
160
static void emulated_push_event(EmulatedState *card, EmulEvent *event)
161
{
162
    qemu_mutex_lock(&card->event_list_mutex);
163
    QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry);
164
    qemu_mutex_unlock(&card->event_list_mutex);
165
    if (write(card->pipe[1], card, 1) != 1) {
166
        DPRINTF(card, 1, "write to pipe failed\n");
167
    }
168
}
169

    
170
static void emulated_push_type(EmulatedState *card, uint32_t type)
171
{
172
    EmulEvent *event = (EmulEvent *)qemu_malloc(sizeof(EmulEvent));
173

    
174
    assert(event);
175
    event->p.gen.type = type;
176
    emulated_push_event(card, event);
177
}
178

    
179
static void emulated_push_error(EmulatedState *card, uint64_t code)
180
{
181
    EmulEvent *event = (EmulEvent *)qemu_malloc(sizeof(EmulEvent));
182

    
183
    assert(event);
184
    event->p.error.type = EMUL_ERROR;
185
    event->p.error.code = code;
186
    emulated_push_event(card, event);
187
}
188

    
189
static void emulated_push_data_type(EmulatedState *card, uint32_t type,
190
    const uint8_t *data, uint32_t len)
191
{
192
    EmulEvent *event = (EmulEvent *)qemu_malloc(sizeof(EmulEvent) + len);
193

    
194
    assert(event);
195
    event->p.data.type = type;
196
    event->p.data.len = len;
197
    memcpy(event->p.data.data, data, len);
198
    emulated_push_event(card, event);
199
}
200

    
201
static void emulated_push_reader_insert(EmulatedState *card)
202
{
203
    emulated_push_type(card, EMUL_READER_INSERT);
204
}
205

    
206
static void emulated_push_reader_remove(EmulatedState *card)
207
{
208
    emulated_push_type(card, EMUL_READER_REMOVE);
209
}
210

    
211
static void emulated_push_card_insert(EmulatedState *card,
212
    const uint8_t *atr, uint32_t len)
213
{
214
    emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len);
215
}
216

    
217
static void emulated_push_card_remove(EmulatedState *card)
218
{
219
    emulated_push_type(card, EMUL_CARD_REMOVE);
220
}
221

    
222
static void emulated_push_response_apdu(EmulatedState *card,
223
    const uint8_t *apdu, uint32_t len)
224
{
225
    emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len);
226
}
227

    
228
#define APDU_BUF_SIZE 270
229
static void *handle_apdu_thread(void* arg)
230
{
231
    EmulatedState *card = arg;
232
    uint8_t recv_data[APDU_BUF_SIZE];
233
    int recv_len;
234
    VReaderStatus reader_status;
235
    EmulEvent *event;
236

    
237
    while (1) {
238
        qemu_mutex_lock(&card->handle_apdu_mutex);
239
        qemu_cond_wait(&card->handle_apdu_cond, &card->handle_apdu_mutex);
240
        qemu_mutex_unlock(&card->handle_apdu_mutex);
241
        if (card->quit_apdu_thread) {
242
            card->quit_apdu_thread = 0; /* debugging */
243
            break;
244
        }
245
        qemu_mutex_lock(&card->vreader_mutex);
246
        while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) {
247
            event = QSIMPLEQ_FIRST(&card->guest_apdu_list);
248
            assert((unsigned long)event > 1000);
249
            QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry);
250
            if (event->p.data.type != EMUL_GUEST_APDU) {
251
                DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n");
252
                qemu_free(event);
253
                continue;
254
            }
255
            if (card->reader == NULL) {
256
                DPRINTF(card, 1, "reader is NULL\n");
257
                qemu_free(event);
258
                continue;
259
            }
260
            recv_len = sizeof(recv_data);
261
            reader_status = vreader_xfr_bytes(card->reader,
262
                    event->p.data.data, event->p.data.len,
263
                    recv_data, &recv_len);
264
            DPRINTF(card, 2, "got back apdu of length %d\n", recv_len);
265
            if (reader_status == VREADER_OK) {
266
                emulated_push_response_apdu(card, recv_data, recv_len);
267
            } else {
268
                emulated_push_error(card, reader_status);
269
            }
270
            qemu_free(event);
271
        }
272
        qemu_mutex_unlock(&card->vreader_mutex);
273
    }
274
    qemu_mutex_lock(&card->apdu_thread_quit_mutex);
275
    qemu_cond_signal(&card->apdu_thread_quit_cond);
276
    qemu_mutex_unlock(&card->apdu_thread_quit_mutex);
277
    return NULL;
278
}
279

    
280
static void *event_thread(void *arg)
281
{
282
    int atr_len = MAX_ATR_SIZE;
283
    uint8_t atr[MAX_ATR_SIZE];
284
    VEvent *event = NULL;
285
    EmulatedState *card = arg;
286

    
287
    while (1) {
288
        const char *reader_name;
289

    
290
        event = vevent_wait_next_vevent();
291
        if (event == NULL || event->type == VEVENT_LAST) {
292
            break;
293
        }
294
        if (event->type != VEVENT_READER_INSERT) {
295
            if (card->reader == NULL && event->reader != NULL) {
296
                /* Happens after device_add followed by card remove or insert.
297
                 * XXX: create synthetic add_reader events if vcard_emul_init
298
                 * already called, which happens if device_del and device_add
299
                 * are called */
300
                card->reader = vreader_reference(event->reader);
301
            } else {
302
                if (event->reader != card->reader) {
303
                    fprintf(stderr,
304
                        "ERROR: wrong reader: quiting event_thread\n");
305
                    break;
306
                }
307
            }
308
        }
309
        switch (event->type) {
310
        case VEVENT_READER_INSERT:
311
            /* TODO: take a specific reader. i.e. track which reader
312
             * we are seeing here, check it is the one we want (the first,
313
             * or by a particular name), and ignore if we don't want it.
314
             */
315
            reader_name = vreader_get_name(event->reader);
316
            if (card->reader != NULL) {
317
                DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n",
318
                    vreader_get_name(card->reader), reader_name);
319
                qemu_mutex_lock(&card->vreader_mutex);
320
                vreader_free(card->reader);
321
                qemu_mutex_unlock(&card->vreader_mutex);
322
                emulated_push_reader_remove(card);
323
            }
324
            qemu_mutex_lock(&card->vreader_mutex);
325
            DPRINTF(card, 2, "READER INSERT %s\n", reader_name);
326
            card->reader = vreader_reference(event->reader);
327
            qemu_mutex_unlock(&card->vreader_mutex);
328
            emulated_push_reader_insert(card);
329
            break;
330
        case VEVENT_READER_REMOVE:
331
            DPRINTF(card, 2, " READER REMOVE: %s\n",
332
                    vreader_get_name(event->reader));
333
            qemu_mutex_lock(&card->vreader_mutex);
334
            vreader_free(card->reader);
335
            card->reader = NULL;
336
            qemu_mutex_unlock(&card->vreader_mutex);
337
            emulated_push_reader_remove(card);
338
            break;
339
        case VEVENT_CARD_INSERT:
340
            /* get the ATR (intended as a response to a power on from the
341
             * reader */
342
            atr_len = MAX_ATR_SIZE;
343
            vreader_power_on(event->reader, atr, &atr_len);
344
            card->atr_length = (uint8_t)atr_len;
345
            DPRINTF(card, 2, " CARD INSERT\n");
346
            emulated_push_card_insert(card, atr, atr_len);
347
            break;
348
        case VEVENT_CARD_REMOVE:
349
            DPRINTF(card, 2, " CARD REMOVE\n");
350
            emulated_push_card_remove(card);
351
            break;
352
        case VEVENT_LAST: /* quit */
353
            vevent_delete(event);
354
            return NULL;
355
            break;
356
        default:
357
            break;
358
        }
359
        vevent_delete(event);
360
    }
361
    return NULL;
362
}
363

    
364
static void pipe_read(void *opaque)
365
{
366
    EmulatedState *card = opaque;
367
    EmulEvent *event, *next;
368
    char dummy;
369
    int len;
370

    
371
    do {
372
        len = read(card->pipe[0], &dummy, sizeof(dummy));
373
    } while (len == sizeof(dummy));
374
    qemu_mutex_lock(&card->event_list_mutex);
375
    QSIMPLEQ_FOREACH_SAFE(event, &card->event_list, entry, next) {
376
        DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type));
377
        switch (event->p.gen.type) {
378
        case EMUL_RESPONSE_APDU:
379
            ccid_card_send_apdu_to_guest(&card->base, event->p.data.data,
380
                event->p.data.len);
381
            break;
382
        case EMUL_READER_INSERT:
383
            ccid_card_ccid_attach(&card->base);
384
            break;
385
        case EMUL_READER_REMOVE:
386
            ccid_card_ccid_detach(&card->base);
387
            break;
388
        case EMUL_CARD_INSERT:
389
            assert(event->p.data.len <= MAX_ATR_SIZE);
390
            card->atr_length = event->p.data.len;
391
            memcpy(card->atr, event->p.data.data, card->atr_length);
392
            ccid_card_card_inserted(&card->base);
393
            break;
394
        case EMUL_CARD_REMOVE:
395
            ccid_card_card_removed(&card->base);
396
            break;
397
        case EMUL_ERROR:
398
            ccid_card_card_error(&card->base, event->p.error.code);
399
            break;
400
        default:
401
            DPRINTF(card, 2, "unexpected event\n");
402
            break;
403
        }
404
        qemu_free(event);
405
    }
406
    QSIMPLEQ_INIT(&card->event_list);
407
    qemu_mutex_unlock(&card->event_list_mutex);
408
}
409

    
410
static int init_pipe_signaling(EmulatedState *card)
411
{
412
    if (pipe(card->pipe) < 0) {
413
        DPRINTF(card, 2, "pipe creation failed\n");
414
        return -1;
415
    }
416
    fcntl(card->pipe[0], F_SETFL, O_NONBLOCK);
417
    fcntl(card->pipe[1], F_SETFL, O_NONBLOCK);
418
    fcntl(card->pipe[0], F_SETOWN, getpid());
419
    qemu_set_fd_handler(card->pipe[0], pipe_read, NULL, card);
420
    return 0;
421
}
422

    
423
#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb"
424
#define CERTIFICATES_ARGS_TEMPLATE\
425
    "db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)"
426

    
427
static int wrap_vcard_emul_init(VCardEmulOptions *options)
428
{
429
    static int called;
430
    static int options_was_null;
431

    
432
    if (called) {
433
        if ((options == NULL) != options_was_null) {
434
            printf("%s: warning: running emulated with certificates"
435
                   " and emulated side by side is not supported\n",
436
                   __func__);
437
            return VCARD_EMUL_FAIL;
438
        }
439
        vcard_emul_replay_insertion_events();
440
        return VCARD_EMUL_OK;
441
    }
442
    options_was_null = (options == NULL);
443
    called = 1;
444
    return vcard_emul_init(options);
445
}
446

    
447
static int emulated_initialize_vcard_from_certificates(EmulatedState *card)
448
{
449
    char emul_args[200];
450
    VCardEmulOptions *options = NULL;
451

    
452
    snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE,
453
        card->db ? card->db : CERTIFICATES_DEFAULT_DB,
454
        card->cert1, card->cert2, card->cert3);
455
    options = vcard_emul_options(emul_args);
456
    if (options == NULL) {
457
        printf("%s: warning: not using certificates due to"
458
               " initialization error\n", __func__);
459
    }
460
    return wrap_vcard_emul_init(options);
461
}
462

    
463
typedef struct EnumTable {
464
    const char *name;
465
    uint32_t value;
466
} EnumTable;
467

    
468
EnumTable backend_enum_table[] = {
469
    {BACKEND_NSS_EMULATED_NAME, BACKEND_NSS_EMULATED},
470
    {BACKEND_CERTIFICATES_NAME, BACKEND_CERTIFICATES},
471
    {NULL, 0},
472
};
473

    
474
static uint32_t parse_enumeration(char *str,
475
    EnumTable *table, uint32_t not_found_value)
476
{
477
    uint32_t ret = not_found_value;
478

    
479
    while (table->name != NULL) {
480
        if (strcmp(table->name, str) == 0) {
481
            ret = table->value;
482
            break;
483
        }
484
        table++;
485
    }
486
    return ret;
487
}
488

    
489
static int emulated_initfn(CCIDCardState *base)
490
{
491
    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
492
    QemuThread thread_id;
493
    VCardEmulError ret;
494
    EnumTable *ptable;
495

    
496
    QSIMPLEQ_INIT(&card->event_list);
497
    QSIMPLEQ_INIT(&card->guest_apdu_list);
498
    qemu_mutex_init(&card->event_list_mutex);
499
    qemu_mutex_init(&card->vreader_mutex);
500
    qemu_mutex_init(&card->handle_apdu_mutex);
501
    qemu_cond_init(&card->handle_apdu_cond);
502
    card->reader = NULL;
503
    card->quit_apdu_thread = 0;
504
    if (init_pipe_signaling(card) < 0) {
505
        return -1;
506
    }
507
    card->backend = parse_enumeration(card->backend_str, backend_enum_table, 0);
508
    if (card->backend == 0) {
509
        printf("unknown backend, must be one of:\n");
510
        for (ptable = backend_enum_table; ptable->name != NULL; ++ptable) {
511
            printf("%s\n", ptable->name);
512
        }
513
        return -1;
514
    }
515

    
516
    /* TODO: a passthru backened that works on local machine. third card type?*/
517
    if (card->backend == BACKEND_CERTIFICATES) {
518
        if (card->cert1 != NULL && card->cert2 != NULL && card->cert3 != NULL) {
519
            ret = emulated_initialize_vcard_from_certificates(card);
520
        } else {
521
            printf("%s: you must provide all three certs for"
522
                   " certificates backend\n", EMULATED_DEV_NAME);
523
            return -1;
524
        }
525
    } else {
526
        if (card->backend != BACKEND_NSS_EMULATED) {
527
            printf("%s: bad backend specified. The options are:\n%s (default),"
528
                " %s.\n", EMULATED_DEV_NAME, BACKEND_NSS_EMULATED_NAME,
529
                BACKEND_CERTIFICATES_NAME);
530
            return -1;
531
        }
532
        if (card->cert1 != NULL || card->cert2 != NULL || card->cert3 != NULL) {
533
            printf("%s: unexpected cert parameters to nss emulated backend\n",
534
                   EMULATED_DEV_NAME);
535
            return -1;
536
        }
537
        /* default to mirroring the local hardware readers */
538
        ret = wrap_vcard_emul_init(NULL);
539
    }
540
    if (ret != VCARD_EMUL_OK) {
541
        printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME);
542
        return -1;
543
    }
544
    qemu_thread_create(&thread_id, event_thread, card);
545
    qemu_thread_create(&thread_id, handle_apdu_thread, card);
546
    return 0;
547
}
548

    
549
static int emulated_exitfn(CCIDCardState *base)
550
{
551
    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
552
    VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL);
553

    
554
    vevent_queue_vevent(vevent); /* stop vevent thread */
555
    qemu_mutex_lock(&card->apdu_thread_quit_mutex);
556
    card->quit_apdu_thread = 1; /* stop handle_apdu thread */
557
    qemu_cond_signal(&card->handle_apdu_cond);
558
    qemu_cond_wait(&card->apdu_thread_quit_cond,
559
                      &card->apdu_thread_quit_mutex);
560
    /* handle_apdu thread stopped, can destroy all of it's mutexes */
561
    qemu_cond_destroy(&card->handle_apdu_cond);
562
    qemu_cond_destroy(&card->apdu_thread_quit_cond);
563
    qemu_mutex_destroy(&card->apdu_thread_quit_mutex);
564
    qemu_mutex_destroy(&card->handle_apdu_mutex);
565
    qemu_mutex_destroy(&card->vreader_mutex);
566
    qemu_mutex_destroy(&card->event_list_mutex);
567
    return 0;
568
}
569

    
570
static CCIDCardInfo emulated_card_info = {
571
    .qdev.name = EMULATED_DEV_NAME,
572
    .qdev.desc = "emulated smartcard",
573
    .qdev.size = sizeof(EmulatedState),
574
    .initfn = emulated_initfn,
575
    .exitfn = emulated_exitfn,
576
    .get_atr = emulated_get_atr,
577
    .apdu_from_guest = emulated_apdu_from_guest,
578
    .qdev.unplug    = qdev_simple_unplug_cb,
579
    .qdev.props     = (Property[]) {
580
        DEFINE_PROP_STRING("backend", EmulatedState, backend_str),
581
        DEFINE_PROP_STRING("cert1", EmulatedState, cert1),
582
        DEFINE_PROP_STRING("cert2", EmulatedState, cert2),
583
        DEFINE_PROP_STRING("cert3", EmulatedState, cert3),
584
        DEFINE_PROP_STRING("db", EmulatedState, db),
585
        DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0),
586
        DEFINE_PROP_END_OF_LIST(),
587
    },
588
};
589

    
590
static void ccid_card_emulated_register_devices(void)
591
{
592
    ccid_card_qdev_register(&emulated_card_info);
593
}
594

    
595
device_init(ccid_card_emulated_register_devices)