149ab747fSPaolo Bonzini /* 249ab747fSPaolo Bonzini * CCID Card Device. Emulated card. 349ab747fSPaolo Bonzini * 449ab747fSPaolo Bonzini * Copyright (c) 2011 Red Hat. 549ab747fSPaolo Bonzini * Written by Alon Levy. 649ab747fSPaolo Bonzini * 749ab747fSPaolo Bonzini * This code is licensed under the GNU LGPL, version 2 or later. 849ab747fSPaolo Bonzini */ 949ab747fSPaolo Bonzini 1049ab747fSPaolo Bonzini /* 1149ab747fSPaolo Bonzini * It can be used to provide access to the local hardware in a non exclusive 1249ab747fSPaolo Bonzini * way, or it can use certificates. It requires the usb-ccid bus. 1349ab747fSPaolo Bonzini * 1449ab747fSPaolo Bonzini * Usage 1: standard, mirror hardware reader+card: 1549ab747fSPaolo Bonzini * qemu .. -usb -device usb-ccid -device ccid-card-emulated 1649ab747fSPaolo Bonzini * 1749ab747fSPaolo Bonzini * Usage 2: use certificates, no hardware required 1849ab747fSPaolo Bonzini * one time: create the certificates: 1949ab747fSPaolo Bonzini * for i in 1 2 3; do 2049ab747fSPaolo Bonzini * certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=user$i" -n user$i 2149ab747fSPaolo Bonzini * done 2249ab747fSPaolo Bonzini * qemu .. -usb -device usb-ccid \ 2349ab747fSPaolo Bonzini * -device ccid-card-emulated,cert1=user1,cert2=user2,cert3=user3 2449ab747fSPaolo Bonzini * 2549ab747fSPaolo Bonzini * If you use a non default db for the certificates you can specify it using 2649ab747fSPaolo Bonzini * the db parameter. 2749ab747fSPaolo Bonzini */ 2849ab747fSPaolo Bonzini 2949ab747fSPaolo Bonzini #include <eventt.h> 3049ab747fSPaolo Bonzini #include <vevent.h> 3149ab747fSPaolo Bonzini #include <vreader.h> 3249ab747fSPaolo Bonzini #include <vcard_emul.h> 3349ab747fSPaolo Bonzini 3449ab747fSPaolo Bonzini #include "qemu/thread.h" 35dccfcd0eSPaolo Bonzini #include "sysemu/char.h" 3647b43a1fSPaolo Bonzini #include "ccid.h" 3749ab747fSPaolo Bonzini 3849ab747fSPaolo Bonzini #define DPRINTF(card, lvl, fmt, ...) \ 3949ab747fSPaolo Bonzini do {\ 4049ab747fSPaolo Bonzini if (lvl <= card->debug) {\ 4149ab747fSPaolo Bonzini printf("ccid-card-emul: %s: " fmt , __func__, ## __VA_ARGS__);\ 4249ab747fSPaolo Bonzini } \ 4349ab747fSPaolo Bonzini } while (0) 4449ab747fSPaolo Bonzini 4549ab747fSPaolo Bonzini #define EMULATED_DEV_NAME "ccid-card-emulated" 4649ab747fSPaolo Bonzini 4749ab747fSPaolo Bonzini #define BACKEND_NSS_EMULATED_NAME "nss-emulated" 4849ab747fSPaolo Bonzini #define BACKEND_CERTIFICATES_NAME "certificates" 4949ab747fSPaolo Bonzini 5049ab747fSPaolo Bonzini enum { 5149ab747fSPaolo Bonzini BACKEND_NSS_EMULATED = 1, 5249ab747fSPaolo Bonzini BACKEND_CERTIFICATES 5349ab747fSPaolo Bonzini }; 5449ab747fSPaolo Bonzini 5549ab747fSPaolo Bonzini #define DEFAULT_BACKEND BACKEND_NSS_EMULATED 5649ab747fSPaolo Bonzini 5749ab747fSPaolo Bonzini typedef struct EmulatedState EmulatedState; 5849ab747fSPaolo Bonzini 5949ab747fSPaolo Bonzini enum { 6049ab747fSPaolo Bonzini EMUL_READER_INSERT = 0, 6149ab747fSPaolo Bonzini EMUL_READER_REMOVE, 6249ab747fSPaolo Bonzini EMUL_CARD_INSERT, 6349ab747fSPaolo Bonzini EMUL_CARD_REMOVE, 6449ab747fSPaolo Bonzini EMUL_GUEST_APDU, 6549ab747fSPaolo Bonzini EMUL_RESPONSE_APDU, 6649ab747fSPaolo Bonzini EMUL_ERROR, 6749ab747fSPaolo Bonzini }; 6849ab747fSPaolo Bonzini 6949ab747fSPaolo Bonzini static const char *emul_event_to_string(uint32_t emul_event) 7049ab747fSPaolo Bonzini { 7149ab747fSPaolo Bonzini switch (emul_event) { 7249ab747fSPaolo Bonzini case EMUL_READER_INSERT: 7349ab747fSPaolo Bonzini return "EMUL_READER_INSERT"; 7449ab747fSPaolo Bonzini case EMUL_READER_REMOVE: 7549ab747fSPaolo Bonzini return "EMUL_READER_REMOVE"; 7649ab747fSPaolo Bonzini case EMUL_CARD_INSERT: 7749ab747fSPaolo Bonzini return "EMUL_CARD_INSERT"; 7849ab747fSPaolo Bonzini case EMUL_CARD_REMOVE: 7949ab747fSPaolo Bonzini return "EMUL_CARD_REMOVE"; 8049ab747fSPaolo Bonzini case EMUL_GUEST_APDU: 8149ab747fSPaolo Bonzini return "EMUL_GUEST_APDU"; 8249ab747fSPaolo Bonzini case EMUL_RESPONSE_APDU: 8349ab747fSPaolo Bonzini return "EMUL_RESPONSE_APDU"; 8449ab747fSPaolo Bonzini case EMUL_ERROR: 8549ab747fSPaolo Bonzini return "EMUL_ERROR"; 8649ab747fSPaolo Bonzini } 8749ab747fSPaolo Bonzini return "UNKNOWN"; 8849ab747fSPaolo Bonzini } 8949ab747fSPaolo Bonzini 9049ab747fSPaolo Bonzini typedef struct EmulEvent { 9149ab747fSPaolo Bonzini QSIMPLEQ_ENTRY(EmulEvent) entry; 9249ab747fSPaolo Bonzini union { 9349ab747fSPaolo Bonzini struct { 9449ab747fSPaolo Bonzini uint32_t type; 9549ab747fSPaolo Bonzini } gen; 9649ab747fSPaolo Bonzini struct { 9749ab747fSPaolo Bonzini uint32_t type; 9849ab747fSPaolo Bonzini uint64_t code; 9949ab747fSPaolo Bonzini } error; 10049ab747fSPaolo Bonzini struct { 10149ab747fSPaolo Bonzini uint32_t type; 10249ab747fSPaolo Bonzini uint32_t len; 10349ab747fSPaolo Bonzini uint8_t data[]; 10449ab747fSPaolo Bonzini } data; 10549ab747fSPaolo Bonzini } p; 10649ab747fSPaolo Bonzini } EmulEvent; 10749ab747fSPaolo Bonzini 10849ab747fSPaolo Bonzini #define MAX_ATR_SIZE 40 10949ab747fSPaolo Bonzini struct EmulatedState { 11049ab747fSPaolo Bonzini CCIDCardState base; 11149ab747fSPaolo Bonzini uint8_t debug; 11249ab747fSPaolo Bonzini char *backend_str; 11349ab747fSPaolo Bonzini uint32_t backend; 11449ab747fSPaolo Bonzini char *cert1; 11549ab747fSPaolo Bonzini char *cert2; 11649ab747fSPaolo Bonzini char *cert3; 11749ab747fSPaolo Bonzini char *db; 11849ab747fSPaolo Bonzini uint8_t atr[MAX_ATR_SIZE]; 11949ab747fSPaolo Bonzini uint8_t atr_length; 12049ab747fSPaolo Bonzini QSIMPLEQ_HEAD(event_list, EmulEvent) event_list; 12149ab747fSPaolo Bonzini QemuMutex event_list_mutex; 12249ab747fSPaolo Bonzini QemuThread event_thread_id; 12349ab747fSPaolo Bonzini VReader *reader; 12449ab747fSPaolo Bonzini QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list; 12549ab747fSPaolo Bonzini QemuMutex vreader_mutex; /* and guest_apdu_list mutex */ 12649ab747fSPaolo Bonzini QemuMutex handle_apdu_mutex; 12749ab747fSPaolo Bonzini QemuCond handle_apdu_cond; 128c1129f6bSPaolo Bonzini EventNotifier notifier; 12949ab747fSPaolo Bonzini int quit_apdu_thread; 13049ab747fSPaolo Bonzini QemuThread apdu_thread_id; 13149ab747fSPaolo Bonzini }; 13249ab747fSPaolo Bonzini 13349ab747fSPaolo Bonzini static void emulated_apdu_from_guest(CCIDCardState *base, 13449ab747fSPaolo Bonzini const uint8_t *apdu, uint32_t len) 13549ab747fSPaolo Bonzini { 13649ab747fSPaolo Bonzini EmulatedState *card = DO_UPCAST(EmulatedState, base, base); 13749ab747fSPaolo Bonzini EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len); 13849ab747fSPaolo Bonzini 13949ab747fSPaolo Bonzini assert(event); 14049ab747fSPaolo Bonzini event->p.data.type = EMUL_GUEST_APDU; 14149ab747fSPaolo Bonzini event->p.data.len = len; 14249ab747fSPaolo Bonzini memcpy(event->p.data.data, apdu, len); 14349ab747fSPaolo Bonzini qemu_mutex_lock(&card->vreader_mutex); 14449ab747fSPaolo Bonzini QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry); 14549ab747fSPaolo Bonzini qemu_mutex_unlock(&card->vreader_mutex); 14649ab747fSPaolo Bonzini qemu_mutex_lock(&card->handle_apdu_mutex); 14749ab747fSPaolo Bonzini qemu_cond_signal(&card->handle_apdu_cond); 14849ab747fSPaolo Bonzini qemu_mutex_unlock(&card->handle_apdu_mutex); 14949ab747fSPaolo Bonzini } 15049ab747fSPaolo Bonzini 15149ab747fSPaolo Bonzini static const uint8_t *emulated_get_atr(CCIDCardState *base, uint32_t *len) 15249ab747fSPaolo Bonzini { 15349ab747fSPaolo Bonzini EmulatedState *card = DO_UPCAST(EmulatedState, base, base); 15449ab747fSPaolo Bonzini 15549ab747fSPaolo Bonzini *len = card->atr_length; 15649ab747fSPaolo Bonzini return card->atr; 15749ab747fSPaolo Bonzini } 15849ab747fSPaolo Bonzini 15949ab747fSPaolo Bonzini static void emulated_push_event(EmulatedState *card, EmulEvent *event) 16049ab747fSPaolo Bonzini { 16149ab747fSPaolo Bonzini qemu_mutex_lock(&card->event_list_mutex); 16249ab747fSPaolo Bonzini QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry); 16349ab747fSPaolo Bonzini qemu_mutex_unlock(&card->event_list_mutex); 164c1129f6bSPaolo Bonzini event_notifier_set(&card->notifier); 16549ab747fSPaolo Bonzini } 16649ab747fSPaolo Bonzini 16749ab747fSPaolo Bonzini static void emulated_push_type(EmulatedState *card, uint32_t type) 16849ab747fSPaolo Bonzini { 169*98f34339SMarkus Armbruster EmulEvent *event = g_new(EmulEvent, 1); 17049ab747fSPaolo Bonzini 17149ab747fSPaolo Bonzini assert(event); 17249ab747fSPaolo Bonzini event->p.gen.type = type; 17349ab747fSPaolo Bonzini emulated_push_event(card, event); 17449ab747fSPaolo Bonzini } 17549ab747fSPaolo Bonzini 17649ab747fSPaolo Bonzini static void emulated_push_error(EmulatedState *card, uint64_t code) 17749ab747fSPaolo Bonzini { 178*98f34339SMarkus Armbruster EmulEvent *event = g_new(EmulEvent, 1); 17949ab747fSPaolo Bonzini 18049ab747fSPaolo Bonzini assert(event); 18149ab747fSPaolo Bonzini event->p.error.type = EMUL_ERROR; 18249ab747fSPaolo Bonzini event->p.error.code = code; 18349ab747fSPaolo Bonzini emulated_push_event(card, event); 18449ab747fSPaolo Bonzini } 18549ab747fSPaolo Bonzini 18649ab747fSPaolo Bonzini static void emulated_push_data_type(EmulatedState *card, uint32_t type, 18749ab747fSPaolo Bonzini const uint8_t *data, uint32_t len) 18849ab747fSPaolo Bonzini { 18949ab747fSPaolo Bonzini EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len); 19049ab747fSPaolo Bonzini 19149ab747fSPaolo Bonzini assert(event); 19249ab747fSPaolo Bonzini event->p.data.type = type; 19349ab747fSPaolo Bonzini event->p.data.len = len; 19449ab747fSPaolo Bonzini memcpy(event->p.data.data, data, len); 19549ab747fSPaolo Bonzini emulated_push_event(card, event); 19649ab747fSPaolo Bonzini } 19749ab747fSPaolo Bonzini 19849ab747fSPaolo Bonzini static void emulated_push_reader_insert(EmulatedState *card) 19949ab747fSPaolo Bonzini { 20049ab747fSPaolo Bonzini emulated_push_type(card, EMUL_READER_INSERT); 20149ab747fSPaolo Bonzini } 20249ab747fSPaolo Bonzini 20349ab747fSPaolo Bonzini static void emulated_push_reader_remove(EmulatedState *card) 20449ab747fSPaolo Bonzini { 20549ab747fSPaolo Bonzini emulated_push_type(card, EMUL_READER_REMOVE); 20649ab747fSPaolo Bonzini } 20749ab747fSPaolo Bonzini 20849ab747fSPaolo Bonzini static void emulated_push_card_insert(EmulatedState *card, 20949ab747fSPaolo Bonzini const uint8_t *atr, uint32_t len) 21049ab747fSPaolo Bonzini { 21149ab747fSPaolo Bonzini emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len); 21249ab747fSPaolo Bonzini } 21349ab747fSPaolo Bonzini 21449ab747fSPaolo Bonzini static void emulated_push_card_remove(EmulatedState *card) 21549ab747fSPaolo Bonzini { 21649ab747fSPaolo Bonzini emulated_push_type(card, EMUL_CARD_REMOVE); 21749ab747fSPaolo Bonzini } 21849ab747fSPaolo Bonzini 21949ab747fSPaolo Bonzini static void emulated_push_response_apdu(EmulatedState *card, 22049ab747fSPaolo Bonzini const uint8_t *apdu, uint32_t len) 22149ab747fSPaolo Bonzini { 22249ab747fSPaolo Bonzini emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len); 22349ab747fSPaolo Bonzini } 22449ab747fSPaolo Bonzini 22549ab747fSPaolo Bonzini #define APDU_BUF_SIZE 270 22649ab747fSPaolo Bonzini static void *handle_apdu_thread(void* arg) 22749ab747fSPaolo Bonzini { 22849ab747fSPaolo Bonzini EmulatedState *card = arg; 22949ab747fSPaolo Bonzini uint8_t recv_data[APDU_BUF_SIZE]; 23049ab747fSPaolo Bonzini int recv_len; 23149ab747fSPaolo Bonzini VReaderStatus reader_status; 23249ab747fSPaolo Bonzini EmulEvent *event; 23349ab747fSPaolo Bonzini 23449ab747fSPaolo Bonzini while (1) { 23549ab747fSPaolo Bonzini qemu_mutex_lock(&card->handle_apdu_mutex); 23649ab747fSPaolo Bonzini qemu_cond_wait(&card->handle_apdu_cond, &card->handle_apdu_mutex); 23749ab747fSPaolo Bonzini qemu_mutex_unlock(&card->handle_apdu_mutex); 23849ab747fSPaolo Bonzini if (card->quit_apdu_thread) { 23949ab747fSPaolo Bonzini card->quit_apdu_thread = 0; /* debugging */ 24049ab747fSPaolo Bonzini break; 24149ab747fSPaolo Bonzini } 24249ab747fSPaolo Bonzini qemu_mutex_lock(&card->vreader_mutex); 24349ab747fSPaolo Bonzini while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) { 24449ab747fSPaolo Bonzini event = QSIMPLEQ_FIRST(&card->guest_apdu_list); 24549ab747fSPaolo Bonzini assert((unsigned long)event > 1000); 24649ab747fSPaolo Bonzini QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry); 24749ab747fSPaolo Bonzini if (event->p.data.type != EMUL_GUEST_APDU) { 24849ab747fSPaolo Bonzini DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n"); 24949ab747fSPaolo Bonzini g_free(event); 25049ab747fSPaolo Bonzini continue; 25149ab747fSPaolo Bonzini } 25249ab747fSPaolo Bonzini if (card->reader == NULL) { 25349ab747fSPaolo Bonzini DPRINTF(card, 1, "reader is NULL\n"); 25449ab747fSPaolo Bonzini g_free(event); 25549ab747fSPaolo Bonzini continue; 25649ab747fSPaolo Bonzini } 25749ab747fSPaolo Bonzini recv_len = sizeof(recv_data); 25849ab747fSPaolo Bonzini reader_status = vreader_xfr_bytes(card->reader, 25949ab747fSPaolo Bonzini event->p.data.data, event->p.data.len, 26049ab747fSPaolo Bonzini recv_data, &recv_len); 26149ab747fSPaolo Bonzini DPRINTF(card, 2, "got back apdu of length %d\n", recv_len); 26249ab747fSPaolo Bonzini if (reader_status == VREADER_OK) { 26349ab747fSPaolo Bonzini emulated_push_response_apdu(card, recv_data, recv_len); 26449ab747fSPaolo Bonzini } else { 26549ab747fSPaolo Bonzini emulated_push_error(card, reader_status); 26649ab747fSPaolo Bonzini } 26749ab747fSPaolo Bonzini g_free(event); 26849ab747fSPaolo Bonzini } 26949ab747fSPaolo Bonzini qemu_mutex_unlock(&card->vreader_mutex); 27049ab747fSPaolo Bonzini } 27149ab747fSPaolo Bonzini return NULL; 27249ab747fSPaolo Bonzini } 27349ab747fSPaolo Bonzini 27449ab747fSPaolo Bonzini static void *event_thread(void *arg) 27549ab747fSPaolo Bonzini { 27649ab747fSPaolo Bonzini int atr_len = MAX_ATR_SIZE; 27749ab747fSPaolo Bonzini uint8_t atr[MAX_ATR_SIZE]; 27849ab747fSPaolo Bonzini VEvent *event = NULL; 27949ab747fSPaolo Bonzini EmulatedState *card = arg; 28049ab747fSPaolo Bonzini 28149ab747fSPaolo Bonzini while (1) { 28249ab747fSPaolo Bonzini const char *reader_name; 28349ab747fSPaolo Bonzini 28449ab747fSPaolo Bonzini event = vevent_wait_next_vevent(); 28549ab747fSPaolo Bonzini if (event == NULL || event->type == VEVENT_LAST) { 28649ab747fSPaolo Bonzini break; 28749ab747fSPaolo Bonzini } 28849ab747fSPaolo Bonzini if (event->type != VEVENT_READER_INSERT) { 28949ab747fSPaolo Bonzini if (card->reader == NULL && event->reader != NULL) { 29049ab747fSPaolo Bonzini /* Happens after device_add followed by card remove or insert. 29149ab747fSPaolo Bonzini * XXX: create synthetic add_reader events if vcard_emul_init 29249ab747fSPaolo Bonzini * already called, which happens if device_del and device_add 29349ab747fSPaolo Bonzini * are called */ 29449ab747fSPaolo Bonzini card->reader = vreader_reference(event->reader); 29549ab747fSPaolo Bonzini } else { 29649ab747fSPaolo Bonzini if (event->reader != card->reader) { 29749ab747fSPaolo Bonzini fprintf(stderr, 29849ab747fSPaolo Bonzini "ERROR: wrong reader: quiting event_thread\n"); 29949ab747fSPaolo Bonzini break; 30049ab747fSPaolo Bonzini } 30149ab747fSPaolo Bonzini } 30249ab747fSPaolo Bonzini } 30349ab747fSPaolo Bonzini switch (event->type) { 30449ab747fSPaolo Bonzini case VEVENT_READER_INSERT: 30549ab747fSPaolo Bonzini /* TODO: take a specific reader. i.e. track which reader 30649ab747fSPaolo Bonzini * we are seeing here, check it is the one we want (the first, 30749ab747fSPaolo Bonzini * or by a particular name), and ignore if we don't want it. 30849ab747fSPaolo Bonzini */ 30949ab747fSPaolo Bonzini reader_name = vreader_get_name(event->reader); 31049ab747fSPaolo Bonzini if (card->reader != NULL) { 31149ab747fSPaolo Bonzini DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n", 31249ab747fSPaolo Bonzini vreader_get_name(card->reader), reader_name); 31349ab747fSPaolo Bonzini qemu_mutex_lock(&card->vreader_mutex); 31449ab747fSPaolo Bonzini vreader_free(card->reader); 31549ab747fSPaolo Bonzini qemu_mutex_unlock(&card->vreader_mutex); 31649ab747fSPaolo Bonzini emulated_push_reader_remove(card); 31749ab747fSPaolo Bonzini } 31849ab747fSPaolo Bonzini qemu_mutex_lock(&card->vreader_mutex); 31949ab747fSPaolo Bonzini DPRINTF(card, 2, "READER INSERT %s\n", reader_name); 32049ab747fSPaolo Bonzini card->reader = vreader_reference(event->reader); 32149ab747fSPaolo Bonzini qemu_mutex_unlock(&card->vreader_mutex); 32249ab747fSPaolo Bonzini emulated_push_reader_insert(card); 32349ab747fSPaolo Bonzini break; 32449ab747fSPaolo Bonzini case VEVENT_READER_REMOVE: 32549ab747fSPaolo Bonzini DPRINTF(card, 2, " READER REMOVE: %s\n", 32649ab747fSPaolo Bonzini vreader_get_name(event->reader)); 32749ab747fSPaolo Bonzini qemu_mutex_lock(&card->vreader_mutex); 32849ab747fSPaolo Bonzini vreader_free(card->reader); 32949ab747fSPaolo Bonzini card->reader = NULL; 33049ab747fSPaolo Bonzini qemu_mutex_unlock(&card->vreader_mutex); 33149ab747fSPaolo Bonzini emulated_push_reader_remove(card); 33249ab747fSPaolo Bonzini break; 33349ab747fSPaolo Bonzini case VEVENT_CARD_INSERT: 33449ab747fSPaolo Bonzini /* get the ATR (intended as a response to a power on from the 33549ab747fSPaolo Bonzini * reader */ 33649ab747fSPaolo Bonzini atr_len = MAX_ATR_SIZE; 33749ab747fSPaolo Bonzini vreader_power_on(event->reader, atr, &atr_len); 33849ab747fSPaolo Bonzini card->atr_length = (uint8_t)atr_len; 33949ab747fSPaolo Bonzini DPRINTF(card, 2, " CARD INSERT\n"); 34049ab747fSPaolo Bonzini emulated_push_card_insert(card, atr, atr_len); 34149ab747fSPaolo Bonzini break; 34249ab747fSPaolo Bonzini case VEVENT_CARD_REMOVE: 34349ab747fSPaolo Bonzini DPRINTF(card, 2, " CARD REMOVE\n"); 34449ab747fSPaolo Bonzini emulated_push_card_remove(card); 34549ab747fSPaolo Bonzini break; 34649ab747fSPaolo Bonzini case VEVENT_LAST: /* quit */ 34749ab747fSPaolo Bonzini vevent_delete(event); 34849ab747fSPaolo Bonzini return NULL; 34949ab747fSPaolo Bonzini break; 35049ab747fSPaolo Bonzini default: 35149ab747fSPaolo Bonzini break; 35249ab747fSPaolo Bonzini } 35349ab747fSPaolo Bonzini vevent_delete(event); 35449ab747fSPaolo Bonzini } 35549ab747fSPaolo Bonzini return NULL; 35649ab747fSPaolo Bonzini } 35749ab747fSPaolo Bonzini 358c1129f6bSPaolo Bonzini static void card_event_handler(EventNotifier *notifier) 35949ab747fSPaolo Bonzini { 360c1129f6bSPaolo Bonzini EmulatedState *card = container_of(notifier, EmulatedState, notifier); 36149ab747fSPaolo Bonzini EmulEvent *event, *next; 36249ab747fSPaolo Bonzini 363c1129f6bSPaolo Bonzini event_notifier_test_and_clear(&card->notifier); 36449ab747fSPaolo Bonzini qemu_mutex_lock(&card->event_list_mutex); 36549ab747fSPaolo Bonzini QSIMPLEQ_FOREACH_SAFE(event, &card->event_list, entry, next) { 36649ab747fSPaolo Bonzini DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type)); 36749ab747fSPaolo Bonzini switch (event->p.gen.type) { 36849ab747fSPaolo Bonzini case EMUL_RESPONSE_APDU: 36949ab747fSPaolo Bonzini ccid_card_send_apdu_to_guest(&card->base, event->p.data.data, 37049ab747fSPaolo Bonzini event->p.data.len); 37149ab747fSPaolo Bonzini break; 37249ab747fSPaolo Bonzini case EMUL_READER_INSERT: 37349ab747fSPaolo Bonzini ccid_card_ccid_attach(&card->base); 37449ab747fSPaolo Bonzini break; 37549ab747fSPaolo Bonzini case EMUL_READER_REMOVE: 37649ab747fSPaolo Bonzini ccid_card_ccid_detach(&card->base); 37749ab747fSPaolo Bonzini break; 37849ab747fSPaolo Bonzini case EMUL_CARD_INSERT: 37949ab747fSPaolo Bonzini assert(event->p.data.len <= MAX_ATR_SIZE); 38049ab747fSPaolo Bonzini card->atr_length = event->p.data.len; 38149ab747fSPaolo Bonzini memcpy(card->atr, event->p.data.data, card->atr_length); 38249ab747fSPaolo Bonzini ccid_card_card_inserted(&card->base); 38349ab747fSPaolo Bonzini break; 38449ab747fSPaolo Bonzini case EMUL_CARD_REMOVE: 38549ab747fSPaolo Bonzini ccid_card_card_removed(&card->base); 38649ab747fSPaolo Bonzini break; 38749ab747fSPaolo Bonzini case EMUL_ERROR: 38849ab747fSPaolo Bonzini ccid_card_card_error(&card->base, event->p.error.code); 38949ab747fSPaolo Bonzini break; 39049ab747fSPaolo Bonzini default: 39149ab747fSPaolo Bonzini DPRINTF(card, 2, "unexpected event\n"); 39249ab747fSPaolo Bonzini break; 39349ab747fSPaolo Bonzini } 39449ab747fSPaolo Bonzini g_free(event); 39549ab747fSPaolo Bonzini } 39649ab747fSPaolo Bonzini QSIMPLEQ_INIT(&card->event_list); 39749ab747fSPaolo Bonzini qemu_mutex_unlock(&card->event_list_mutex); 39849ab747fSPaolo Bonzini } 39949ab747fSPaolo Bonzini 400c1129f6bSPaolo Bonzini static int init_event_notifier(EmulatedState *card) 40149ab747fSPaolo Bonzini { 402c1129f6bSPaolo Bonzini if (event_notifier_init(&card->notifier, false) < 0) { 403c1129f6bSPaolo Bonzini DPRINTF(card, 2, "event notifier creation failed\n"); 40449ab747fSPaolo Bonzini return -1; 40549ab747fSPaolo Bonzini } 406c1129f6bSPaolo Bonzini event_notifier_set_handler(&card->notifier, card_event_handler); 40749ab747fSPaolo Bonzini return 0; 40849ab747fSPaolo Bonzini } 40949ab747fSPaolo Bonzini 41049ab747fSPaolo Bonzini #define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb" 41149ab747fSPaolo Bonzini #define CERTIFICATES_ARGS_TEMPLATE\ 41249ab747fSPaolo Bonzini "db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)" 41349ab747fSPaolo Bonzini 41449ab747fSPaolo Bonzini static int wrap_vcard_emul_init(VCardEmulOptions *options) 41549ab747fSPaolo Bonzini { 41649ab747fSPaolo Bonzini static int called; 41749ab747fSPaolo Bonzini static int options_was_null; 41849ab747fSPaolo Bonzini 41949ab747fSPaolo Bonzini if (called) { 42049ab747fSPaolo Bonzini if ((options == NULL) != options_was_null) { 42149ab747fSPaolo Bonzini printf("%s: warning: running emulated with certificates" 42249ab747fSPaolo Bonzini " and emulated side by side is not supported\n", 42349ab747fSPaolo Bonzini __func__); 42449ab747fSPaolo Bonzini return VCARD_EMUL_FAIL; 42549ab747fSPaolo Bonzini } 42649ab747fSPaolo Bonzini vcard_emul_replay_insertion_events(); 42749ab747fSPaolo Bonzini return VCARD_EMUL_OK; 42849ab747fSPaolo Bonzini } 42949ab747fSPaolo Bonzini options_was_null = (options == NULL); 43049ab747fSPaolo Bonzini called = 1; 43149ab747fSPaolo Bonzini return vcard_emul_init(options); 43249ab747fSPaolo Bonzini } 43349ab747fSPaolo Bonzini 43449ab747fSPaolo Bonzini static int emulated_initialize_vcard_from_certificates(EmulatedState *card) 43549ab747fSPaolo Bonzini { 43649ab747fSPaolo Bonzini char emul_args[200]; 43749ab747fSPaolo Bonzini VCardEmulOptions *options = NULL; 43849ab747fSPaolo Bonzini 43949ab747fSPaolo Bonzini snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE, 44049ab747fSPaolo Bonzini card->db ? card->db : CERTIFICATES_DEFAULT_DB, 44149ab747fSPaolo Bonzini card->cert1, card->cert2, card->cert3); 44249ab747fSPaolo Bonzini options = vcard_emul_options(emul_args); 44349ab747fSPaolo Bonzini if (options == NULL) { 44449ab747fSPaolo Bonzini printf("%s: warning: not using certificates due to" 44549ab747fSPaolo Bonzini " initialization error\n", __func__); 44649ab747fSPaolo Bonzini } 44749ab747fSPaolo Bonzini return wrap_vcard_emul_init(options); 44849ab747fSPaolo Bonzini } 44949ab747fSPaolo Bonzini 45049ab747fSPaolo Bonzini typedef struct EnumTable { 45149ab747fSPaolo Bonzini const char *name; 45249ab747fSPaolo Bonzini uint32_t value; 45349ab747fSPaolo Bonzini } EnumTable; 45449ab747fSPaolo Bonzini 455d18c7117SJim Meyering static const EnumTable backend_enum_table[] = { 45649ab747fSPaolo Bonzini {BACKEND_NSS_EMULATED_NAME, BACKEND_NSS_EMULATED}, 45749ab747fSPaolo Bonzini {BACKEND_CERTIFICATES_NAME, BACKEND_CERTIFICATES}, 45849ab747fSPaolo Bonzini {NULL, 0}, 45949ab747fSPaolo Bonzini }; 46049ab747fSPaolo Bonzini 46149ab747fSPaolo Bonzini static uint32_t parse_enumeration(char *str, 462d18c7117SJim Meyering const EnumTable *table, uint32_t not_found_value) 46349ab747fSPaolo Bonzini { 46449ab747fSPaolo Bonzini uint32_t ret = not_found_value; 46549ab747fSPaolo Bonzini 466d0ebd788SMarc-André Lureau if (str == NULL) 467d0ebd788SMarc-André Lureau return 0; 468d0ebd788SMarc-André Lureau 46949ab747fSPaolo Bonzini while (table->name != NULL) { 47049ab747fSPaolo Bonzini if (strcmp(table->name, str) == 0) { 47149ab747fSPaolo Bonzini ret = table->value; 47249ab747fSPaolo Bonzini break; 47349ab747fSPaolo Bonzini } 47449ab747fSPaolo Bonzini table++; 47549ab747fSPaolo Bonzini } 47649ab747fSPaolo Bonzini return ret; 47749ab747fSPaolo Bonzini } 47849ab747fSPaolo Bonzini 47949ab747fSPaolo Bonzini static int emulated_initfn(CCIDCardState *base) 48049ab747fSPaolo Bonzini { 48149ab747fSPaolo Bonzini EmulatedState *card = DO_UPCAST(EmulatedState, base, base); 48249ab747fSPaolo Bonzini VCardEmulError ret; 483d18c7117SJim Meyering const EnumTable *ptable; 48449ab747fSPaolo Bonzini 48549ab747fSPaolo Bonzini QSIMPLEQ_INIT(&card->event_list); 48649ab747fSPaolo Bonzini QSIMPLEQ_INIT(&card->guest_apdu_list); 48749ab747fSPaolo Bonzini qemu_mutex_init(&card->event_list_mutex); 48849ab747fSPaolo Bonzini qemu_mutex_init(&card->vreader_mutex); 48949ab747fSPaolo Bonzini qemu_mutex_init(&card->handle_apdu_mutex); 49049ab747fSPaolo Bonzini qemu_cond_init(&card->handle_apdu_cond); 49149ab747fSPaolo Bonzini card->reader = NULL; 49249ab747fSPaolo Bonzini card->quit_apdu_thread = 0; 493c1129f6bSPaolo Bonzini if (init_event_notifier(card) < 0) { 49449ab747fSPaolo Bonzini return -1; 49549ab747fSPaolo Bonzini } 496ae12e3a6SCole Robinson 497ae12e3a6SCole Robinson card->backend = 0; 498ae12e3a6SCole Robinson if (card->backend_str) { 499ae12e3a6SCole Robinson card->backend = parse_enumeration(card->backend_str, 500ae12e3a6SCole Robinson backend_enum_table, 0); 501ae12e3a6SCole Robinson } 502ae12e3a6SCole Robinson 50349ab747fSPaolo Bonzini if (card->backend == 0) { 504ae12e3a6SCole Robinson printf("backend must be one of:\n"); 50549ab747fSPaolo Bonzini for (ptable = backend_enum_table; ptable->name != NULL; ++ptable) { 50649ab747fSPaolo Bonzini printf("%s\n", ptable->name); 50749ab747fSPaolo Bonzini } 50849ab747fSPaolo Bonzini return -1; 50949ab747fSPaolo Bonzini } 51049ab747fSPaolo Bonzini 51149ab747fSPaolo Bonzini /* TODO: a passthru backened that works on local machine. third card type?*/ 51249ab747fSPaolo Bonzini if (card->backend == BACKEND_CERTIFICATES) { 51349ab747fSPaolo Bonzini if (card->cert1 != NULL && card->cert2 != NULL && card->cert3 != NULL) { 51449ab747fSPaolo Bonzini ret = emulated_initialize_vcard_from_certificates(card); 51549ab747fSPaolo Bonzini } else { 51649ab747fSPaolo Bonzini printf("%s: you must provide all three certs for" 51749ab747fSPaolo Bonzini " certificates backend\n", EMULATED_DEV_NAME); 51849ab747fSPaolo Bonzini return -1; 51949ab747fSPaolo Bonzini } 52049ab747fSPaolo Bonzini } else { 52149ab747fSPaolo Bonzini if (card->backend != BACKEND_NSS_EMULATED) { 52249ab747fSPaolo Bonzini printf("%s: bad backend specified. The options are:\n%s (default)," 52349ab747fSPaolo Bonzini " %s.\n", EMULATED_DEV_NAME, BACKEND_NSS_EMULATED_NAME, 52449ab747fSPaolo Bonzini BACKEND_CERTIFICATES_NAME); 52549ab747fSPaolo Bonzini return -1; 52649ab747fSPaolo Bonzini } 52749ab747fSPaolo Bonzini if (card->cert1 != NULL || card->cert2 != NULL || card->cert3 != NULL) { 52849ab747fSPaolo Bonzini printf("%s: unexpected cert parameters to nss emulated backend\n", 52949ab747fSPaolo Bonzini EMULATED_DEV_NAME); 53049ab747fSPaolo Bonzini return -1; 53149ab747fSPaolo Bonzini } 53249ab747fSPaolo Bonzini /* default to mirroring the local hardware readers */ 53349ab747fSPaolo Bonzini ret = wrap_vcard_emul_init(NULL); 53449ab747fSPaolo Bonzini } 53549ab747fSPaolo Bonzini if (ret != VCARD_EMUL_OK) { 53649ab747fSPaolo Bonzini printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME); 53749ab747fSPaolo Bonzini return -1; 53849ab747fSPaolo Bonzini } 5394900116eSDr. David Alan Gilbert qemu_thread_create(&card->event_thread_id, "ccid/event", event_thread, 5404900116eSDr. David Alan Gilbert card, QEMU_THREAD_JOINABLE); 5414900116eSDr. David Alan Gilbert qemu_thread_create(&card->apdu_thread_id, "ccid/apdu", handle_apdu_thread, 5424900116eSDr. David Alan Gilbert card, QEMU_THREAD_JOINABLE); 54349ab747fSPaolo Bonzini return 0; 54449ab747fSPaolo Bonzini } 54549ab747fSPaolo Bonzini 54649ab747fSPaolo Bonzini static int emulated_exitfn(CCIDCardState *base) 54749ab747fSPaolo Bonzini { 54849ab747fSPaolo Bonzini EmulatedState *card = DO_UPCAST(EmulatedState, base, base); 54949ab747fSPaolo Bonzini VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL); 55049ab747fSPaolo Bonzini 55149ab747fSPaolo Bonzini vevent_queue_vevent(vevent); /* stop vevent thread */ 55249ab747fSPaolo Bonzini qemu_thread_join(&card->event_thread_id); 55349ab747fSPaolo Bonzini 55449ab747fSPaolo Bonzini card->quit_apdu_thread = 1; /* stop handle_apdu thread */ 55549ab747fSPaolo Bonzini qemu_cond_signal(&card->handle_apdu_cond); 55649ab747fSPaolo Bonzini qemu_thread_join(&card->apdu_thread_id); 55749ab747fSPaolo Bonzini 55849ab747fSPaolo Bonzini /* threads exited, can destroy all condvars/mutexes */ 55949ab747fSPaolo Bonzini qemu_cond_destroy(&card->handle_apdu_cond); 56049ab747fSPaolo Bonzini qemu_mutex_destroy(&card->handle_apdu_mutex); 56149ab747fSPaolo Bonzini qemu_mutex_destroy(&card->vreader_mutex); 56249ab747fSPaolo Bonzini qemu_mutex_destroy(&card->event_list_mutex); 56349ab747fSPaolo Bonzini return 0; 56449ab747fSPaolo Bonzini } 56549ab747fSPaolo Bonzini 56649ab747fSPaolo Bonzini static Property emulated_card_properties[] = { 56749ab747fSPaolo Bonzini DEFINE_PROP_STRING("backend", EmulatedState, backend_str), 56849ab747fSPaolo Bonzini DEFINE_PROP_STRING("cert1", EmulatedState, cert1), 56949ab747fSPaolo Bonzini DEFINE_PROP_STRING("cert2", EmulatedState, cert2), 57049ab747fSPaolo Bonzini DEFINE_PROP_STRING("cert3", EmulatedState, cert3), 57149ab747fSPaolo Bonzini DEFINE_PROP_STRING("db", EmulatedState, db), 57249ab747fSPaolo Bonzini DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0), 57349ab747fSPaolo Bonzini DEFINE_PROP_END_OF_LIST(), 57449ab747fSPaolo Bonzini }; 57549ab747fSPaolo Bonzini 57649ab747fSPaolo Bonzini static void emulated_class_initfn(ObjectClass *klass, void *data) 57749ab747fSPaolo Bonzini { 57849ab747fSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass); 57949ab747fSPaolo Bonzini CCIDCardClass *cc = CCID_CARD_CLASS(klass); 58049ab747fSPaolo Bonzini 58149ab747fSPaolo Bonzini cc->initfn = emulated_initfn; 58249ab747fSPaolo Bonzini cc->exitfn = emulated_exitfn; 58349ab747fSPaolo Bonzini cc->get_atr = emulated_get_atr; 58449ab747fSPaolo Bonzini cc->apdu_from_guest = emulated_apdu_from_guest; 585125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_INPUT, dc->categories); 58649ab747fSPaolo Bonzini dc->desc = "emulated smartcard"; 58749ab747fSPaolo Bonzini dc->props = emulated_card_properties; 58849ab747fSPaolo Bonzini } 58949ab747fSPaolo Bonzini 59049ab747fSPaolo Bonzini static const TypeInfo emulated_card_info = { 59149ab747fSPaolo Bonzini .name = EMULATED_DEV_NAME, 59249ab747fSPaolo Bonzini .parent = TYPE_CCID_CARD, 59349ab747fSPaolo Bonzini .instance_size = sizeof(EmulatedState), 59449ab747fSPaolo Bonzini .class_init = emulated_class_initfn, 59549ab747fSPaolo Bonzini }; 59649ab747fSPaolo Bonzini 59749ab747fSPaolo Bonzini static void ccid_card_emulated_register_types(void) 59849ab747fSPaolo Bonzini { 59949ab747fSPaolo Bonzini type_register_static(&emulated_card_info); 60049ab747fSPaolo Bonzini } 60149ab747fSPaolo Bonzini 60249ab747fSPaolo Bonzini type_init(ccid_card_emulated_register_types) 603