|
|
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;
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);
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: 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: }
1.1.1.2 ! root 404: g_free(event);
1.1 root 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)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.