|
|
1.1 root 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;
1.1.1.3 ! root 123: QemuThread event_thread_id;
1.1 root 124: VReader *reader;
125: QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list;
126: QemuMutex vreader_mutex; /* and guest_apdu_list mutex */
127: QemuMutex handle_apdu_mutex;
128: QemuCond handle_apdu_cond;
129: int pipe[2];
130: int quit_apdu_thread;
1.1.1.3 ! root 131: QemuThread apdu_thread_id;
1.1 root 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);
1.1.1.2 root 138: EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len);
1.1 root 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: {
1.1.1.2 root 172: EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent));
1.1 root 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: {
1.1.1.2 root 181: EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent));
1.1 root 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: {
1.1.1.2 root 192: EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len);
1.1 root 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");
1.1.1.2 root 252: g_free(event);
1.1 root 253: continue;
254: }
255: if (card->reader == NULL) {
256: DPRINTF(card, 1, "reader is NULL\n");
1.1.1.2 root 257: g_free(event);
1.1 root 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: }
1.1.1.2 root 270: g_free(event);
1.1 root 271: }
272: qemu_mutex_unlock(&card->vreader_mutex);
273: }
274: return NULL;
275: }
276:
277: static void *event_thread(void *arg)
278: {
279: int atr_len = MAX_ATR_SIZE;
280: uint8_t atr[MAX_ATR_SIZE];
281: VEvent *event = NULL;
282: EmulatedState *card = arg;
283:
284: while (1) {
285: const char *reader_name;
286:
287: event = vevent_wait_next_vevent();
288: if (event == NULL || event->type == VEVENT_LAST) {
289: break;
290: }
291: if (event->type != VEVENT_READER_INSERT) {
292: if (card->reader == NULL && event->reader != NULL) {
293: /* Happens after device_add followed by card remove or insert.
294: * XXX: create synthetic add_reader events if vcard_emul_init
295: * already called, which happens if device_del and device_add
296: * are called */
297: card->reader = vreader_reference(event->reader);
298: } else {
299: if (event->reader != card->reader) {
300: fprintf(stderr,
301: "ERROR: wrong reader: quiting event_thread\n");
302: break;
303: }
304: }
305: }
306: switch (event->type) {
307: case VEVENT_READER_INSERT:
308: /* TODO: take a specific reader. i.e. track which reader
309: * we are seeing here, check it is the one we want (the first,
310: * or by a particular name), and ignore if we don't want it.
311: */
312: reader_name = vreader_get_name(event->reader);
313: if (card->reader != NULL) {
314: DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n",
315: vreader_get_name(card->reader), reader_name);
316: qemu_mutex_lock(&card->vreader_mutex);
317: vreader_free(card->reader);
318: qemu_mutex_unlock(&card->vreader_mutex);
319: emulated_push_reader_remove(card);
320: }
321: qemu_mutex_lock(&card->vreader_mutex);
322: DPRINTF(card, 2, "READER INSERT %s\n", reader_name);
323: card->reader = vreader_reference(event->reader);
324: qemu_mutex_unlock(&card->vreader_mutex);
325: emulated_push_reader_insert(card);
326: break;
327: case VEVENT_READER_REMOVE:
328: DPRINTF(card, 2, " READER REMOVE: %s\n",
329: vreader_get_name(event->reader));
330: qemu_mutex_lock(&card->vreader_mutex);
331: vreader_free(card->reader);
332: card->reader = NULL;
333: qemu_mutex_unlock(&card->vreader_mutex);
334: emulated_push_reader_remove(card);
335: break;
336: case VEVENT_CARD_INSERT:
337: /* get the ATR (intended as a response to a power on from the
338: * reader */
339: atr_len = MAX_ATR_SIZE;
340: vreader_power_on(event->reader, atr, &atr_len);
341: card->atr_length = (uint8_t)atr_len;
342: DPRINTF(card, 2, " CARD INSERT\n");
343: emulated_push_card_insert(card, atr, atr_len);
344: break;
345: case VEVENT_CARD_REMOVE:
346: DPRINTF(card, 2, " CARD REMOVE\n");
347: emulated_push_card_remove(card);
348: break;
349: case VEVENT_LAST: /* quit */
350: vevent_delete(event);
351: return NULL;
352: break;
353: default:
354: break;
355: }
356: vevent_delete(event);
357: }
358: return NULL;
359: }
360:
361: static void pipe_read(void *opaque)
362: {
363: EmulatedState *card = opaque;
364: EmulEvent *event, *next;
365: char dummy;
366: int len;
367:
368: do {
369: len = read(card->pipe[0], &dummy, sizeof(dummy));
370: } while (len == sizeof(dummy));
371: qemu_mutex_lock(&card->event_list_mutex);
372: QSIMPLEQ_FOREACH_SAFE(event, &card->event_list, entry, next) {
373: DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type));
374: switch (event->p.gen.type) {
375: case EMUL_RESPONSE_APDU:
376: ccid_card_send_apdu_to_guest(&card->base, event->p.data.data,
377: event->p.data.len);
378: break;
379: case EMUL_READER_INSERT:
380: ccid_card_ccid_attach(&card->base);
381: break;
382: case EMUL_READER_REMOVE:
383: ccid_card_ccid_detach(&card->base);
384: break;
385: case EMUL_CARD_INSERT:
386: assert(event->p.data.len <= MAX_ATR_SIZE);
387: card->atr_length = event->p.data.len;
388: memcpy(card->atr, event->p.data.data, card->atr_length);
389: ccid_card_card_inserted(&card->base);
390: break;
391: case EMUL_CARD_REMOVE:
392: ccid_card_card_removed(&card->base);
393: break;
394: case EMUL_ERROR:
395: ccid_card_card_error(&card->base, event->p.error.code);
396: break;
397: default:
398: DPRINTF(card, 2, "unexpected event\n");
399: break;
400: }
1.1.1.2 root 401: g_free(event);
1.1 root 402: }
403: QSIMPLEQ_INIT(&card->event_list);
404: qemu_mutex_unlock(&card->event_list_mutex);
405: }
406:
407: static int init_pipe_signaling(EmulatedState *card)
408: {
409: if (pipe(card->pipe) < 0) {
410: DPRINTF(card, 2, "pipe creation failed\n");
411: return -1;
412: }
413: fcntl(card->pipe[0], F_SETFL, O_NONBLOCK);
414: fcntl(card->pipe[1], F_SETFL, O_NONBLOCK);
415: fcntl(card->pipe[0], F_SETOWN, getpid());
416: qemu_set_fd_handler(card->pipe[0], pipe_read, NULL, card);
417: return 0;
418: }
419:
420: #define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb"
421: #define CERTIFICATES_ARGS_TEMPLATE\
422: "db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)"
423:
424: static int wrap_vcard_emul_init(VCardEmulOptions *options)
425: {
426: static int called;
427: static int options_was_null;
428:
429: if (called) {
430: if ((options == NULL) != options_was_null) {
431: printf("%s: warning: running emulated with certificates"
432: " and emulated side by side is not supported\n",
433: __func__);
434: return VCARD_EMUL_FAIL;
435: }
436: vcard_emul_replay_insertion_events();
437: return VCARD_EMUL_OK;
438: }
439: options_was_null = (options == NULL);
440: called = 1;
441: return vcard_emul_init(options);
442: }
443:
444: static int emulated_initialize_vcard_from_certificates(EmulatedState *card)
445: {
446: char emul_args[200];
447: VCardEmulOptions *options = NULL;
448:
449: snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE,
450: card->db ? card->db : CERTIFICATES_DEFAULT_DB,
451: card->cert1, card->cert2, card->cert3);
452: options = vcard_emul_options(emul_args);
453: if (options == NULL) {
454: printf("%s: warning: not using certificates due to"
455: " initialization error\n", __func__);
456: }
457: return wrap_vcard_emul_init(options);
458: }
459:
460: typedef struct EnumTable {
461: const char *name;
462: uint32_t value;
463: } EnumTable;
464:
465: EnumTable backend_enum_table[] = {
466: {BACKEND_NSS_EMULATED_NAME, BACKEND_NSS_EMULATED},
467: {BACKEND_CERTIFICATES_NAME, BACKEND_CERTIFICATES},
468: {NULL, 0},
469: };
470:
471: static uint32_t parse_enumeration(char *str,
472: EnumTable *table, uint32_t not_found_value)
473: {
474: uint32_t ret = not_found_value;
475:
476: while (table->name != NULL) {
477: if (strcmp(table->name, str) == 0) {
478: ret = table->value;
479: break;
480: }
481: table++;
482: }
483: return ret;
484: }
485:
486: static int emulated_initfn(CCIDCardState *base)
487: {
488: EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
489: VCardEmulError ret;
490: EnumTable *ptable;
491:
492: QSIMPLEQ_INIT(&card->event_list);
493: QSIMPLEQ_INIT(&card->guest_apdu_list);
494: qemu_mutex_init(&card->event_list_mutex);
495: qemu_mutex_init(&card->vreader_mutex);
496: qemu_mutex_init(&card->handle_apdu_mutex);
497: qemu_cond_init(&card->handle_apdu_cond);
498: card->reader = NULL;
499: card->quit_apdu_thread = 0;
500: if (init_pipe_signaling(card) < 0) {
501: return -1;
502: }
503: card->backend = parse_enumeration(card->backend_str, backend_enum_table, 0);
504: if (card->backend == 0) {
505: printf("unknown backend, must be one of:\n");
506: for (ptable = backend_enum_table; ptable->name != NULL; ++ptable) {
507: printf("%s\n", ptable->name);
508: }
509: return -1;
510: }
511:
512: /* TODO: a passthru backened that works on local machine. third card type?*/
513: if (card->backend == BACKEND_CERTIFICATES) {
514: if (card->cert1 != NULL && card->cert2 != NULL && card->cert3 != NULL) {
515: ret = emulated_initialize_vcard_from_certificates(card);
516: } else {
517: printf("%s: you must provide all three certs for"
518: " certificates backend\n", EMULATED_DEV_NAME);
519: return -1;
520: }
521: } else {
522: if (card->backend != BACKEND_NSS_EMULATED) {
523: printf("%s: bad backend specified. The options are:\n%s (default),"
524: " %s.\n", EMULATED_DEV_NAME, BACKEND_NSS_EMULATED_NAME,
525: BACKEND_CERTIFICATES_NAME);
526: return -1;
527: }
528: if (card->cert1 != NULL || card->cert2 != NULL || card->cert3 != NULL) {
529: printf("%s: unexpected cert parameters to nss emulated backend\n",
530: EMULATED_DEV_NAME);
531: return -1;
532: }
533: /* default to mirroring the local hardware readers */
534: ret = wrap_vcard_emul_init(NULL);
535: }
536: if (ret != VCARD_EMUL_OK) {
537: printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME);
538: return -1;
539: }
1.1.1.3 ! root 540: qemu_thread_create(&card->event_thread_id, event_thread, card,
! 541: QEMU_THREAD_JOINABLE);
! 542: qemu_thread_create(&card->apdu_thread_id, handle_apdu_thread, card,
! 543: QEMU_THREAD_JOINABLE);
1.1 root 544: return 0;
545: }
546:
547: static int emulated_exitfn(CCIDCardState *base)
548: {
549: EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
550: VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL);
551:
552: vevent_queue_vevent(vevent); /* stop vevent thread */
1.1.1.3 ! root 553: qemu_thread_join(&card->event_thread_id);
! 554:
1.1 root 555: card->quit_apdu_thread = 1; /* stop handle_apdu thread */
556: qemu_cond_signal(&card->handle_apdu_cond);
1.1.1.3 ! root 557: qemu_thread_join(&card->apdu_thread_id);
! 558:
! 559: /* threads exited, can destroy all condvars/mutexes */
1.1 root 560: qemu_cond_destroy(&card->handle_apdu_cond);
561: qemu_mutex_destroy(&card->handle_apdu_mutex);
562: qemu_mutex_destroy(&card->vreader_mutex);
563: qemu_mutex_destroy(&card->event_list_mutex);
564: return 0;
565: }
566:
1.1.1.3 ! root 567: static Property emulated_card_properties[] = {
! 568: DEFINE_PROP_STRING("backend", EmulatedState, backend_str),
! 569: DEFINE_PROP_STRING("cert1", EmulatedState, cert1),
! 570: DEFINE_PROP_STRING("cert2", EmulatedState, cert2),
! 571: DEFINE_PROP_STRING("cert3", EmulatedState, cert3),
! 572: DEFINE_PROP_STRING("db", EmulatedState, db),
! 573: DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0),
! 574: DEFINE_PROP_END_OF_LIST(),
! 575: };
! 576:
! 577: static void emulated_class_initfn(ObjectClass *klass, void *data)
! 578: {
! 579: DeviceClass *dc = DEVICE_CLASS(klass);
! 580: CCIDCardClass *cc = CCID_CARD_CLASS(klass);
! 581:
! 582: cc->initfn = emulated_initfn;
! 583: cc->exitfn = emulated_exitfn;
! 584: cc->get_atr = emulated_get_atr;
! 585: cc->apdu_from_guest = emulated_apdu_from_guest;
! 586: dc->desc = "emulated smartcard";
! 587: dc->props = emulated_card_properties;
! 588: }
! 589:
! 590: static TypeInfo emulated_card_info = {
! 591: .name = EMULATED_DEV_NAME,
! 592: .parent = TYPE_CCID_CARD,
! 593: .instance_size = sizeof(EmulatedState),
! 594: .class_init = emulated_class_initfn,
1.1 root 595: };
596:
1.1.1.3 ! root 597: static void ccid_card_emulated_register_types(void)
1.1 root 598: {
1.1.1.3 ! root 599: type_register_static(&emulated_card_info);
1.1 root 600: }
601:
1.1.1.3 ! root 602: type_init(ccid_card_emulated_register_types)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.