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" 3549ab747fSPaolo Bonzini #include "char/char.h" 3649ab747fSPaolo Bonzini #include "monitor/monitor.h" 37*47b43a1fSPaolo Bonzini #include "ccid.h" 3849ab747fSPaolo Bonzini 3949ab747fSPaolo Bonzini #define DPRINTF(card, lvl, fmt, ...) \ 4049ab747fSPaolo Bonzini do {\ 4149ab747fSPaolo Bonzini if (lvl <= card->debug) {\ 4249ab747fSPaolo Bonzini printf("ccid-card-emul: %s: " fmt , __func__, ## __VA_ARGS__);\ 4349ab747fSPaolo Bonzini } \ 4449ab747fSPaolo Bonzini } while (0) 4549ab747fSPaolo Bonzini 4649ab747fSPaolo Bonzini #define EMULATED_DEV_NAME "ccid-card-emulated" 4749ab747fSPaolo Bonzini 4849ab747fSPaolo Bonzini #define BACKEND_NSS_EMULATED_NAME "nss-emulated" 4949ab747fSPaolo Bonzini #define BACKEND_CERTIFICATES_NAME "certificates" 5049ab747fSPaolo Bonzini 5149ab747fSPaolo Bonzini enum { 5249ab747fSPaolo Bonzini BACKEND_NSS_EMULATED = 1, 5349ab747fSPaolo Bonzini BACKEND_CERTIFICATES 5449ab747fSPaolo Bonzini }; 5549ab747fSPaolo Bonzini 5649ab747fSPaolo Bonzini #define DEFAULT_BACKEND BACKEND_NSS_EMULATED 5749ab747fSPaolo Bonzini 5849ab747fSPaolo Bonzini typedef struct EmulatedState EmulatedState; 5949ab747fSPaolo Bonzini 6049ab747fSPaolo Bonzini enum { 6149ab747fSPaolo Bonzini EMUL_READER_INSERT = 0, 6249ab747fSPaolo Bonzini EMUL_READER_REMOVE, 6349ab747fSPaolo Bonzini EMUL_CARD_INSERT, 6449ab747fSPaolo Bonzini EMUL_CARD_REMOVE, 6549ab747fSPaolo Bonzini EMUL_GUEST_APDU, 6649ab747fSPaolo Bonzini EMUL_RESPONSE_APDU, 6749ab747fSPaolo Bonzini EMUL_ERROR, 6849ab747fSPaolo Bonzini }; 6949ab747fSPaolo Bonzini 7049ab747fSPaolo Bonzini static const char *emul_event_to_string(uint32_t emul_event) 7149ab747fSPaolo Bonzini { 7249ab747fSPaolo Bonzini switch (emul_event) { 7349ab747fSPaolo Bonzini case EMUL_READER_INSERT: 7449ab747fSPaolo Bonzini return "EMUL_READER_INSERT"; 7549ab747fSPaolo Bonzini case EMUL_READER_REMOVE: 7649ab747fSPaolo Bonzini return "EMUL_READER_REMOVE"; 7749ab747fSPaolo Bonzini case EMUL_CARD_INSERT: 7849ab747fSPaolo Bonzini return "EMUL_CARD_INSERT"; 7949ab747fSPaolo Bonzini case EMUL_CARD_REMOVE: 8049ab747fSPaolo Bonzini return "EMUL_CARD_REMOVE"; 8149ab747fSPaolo Bonzini case EMUL_GUEST_APDU: 8249ab747fSPaolo Bonzini return "EMUL_GUEST_APDU"; 8349ab747fSPaolo Bonzini case EMUL_RESPONSE_APDU: 8449ab747fSPaolo Bonzini return "EMUL_RESPONSE_APDU"; 8549ab747fSPaolo Bonzini case EMUL_ERROR: 8649ab747fSPaolo Bonzini return "EMUL_ERROR"; 8749ab747fSPaolo Bonzini } 8849ab747fSPaolo Bonzini return "UNKNOWN"; 8949ab747fSPaolo Bonzini } 9049ab747fSPaolo Bonzini 9149ab747fSPaolo Bonzini typedef struct EmulEvent { 9249ab747fSPaolo Bonzini QSIMPLEQ_ENTRY(EmulEvent) entry; 9349ab747fSPaolo Bonzini union { 9449ab747fSPaolo Bonzini struct { 9549ab747fSPaolo Bonzini uint32_t type; 9649ab747fSPaolo Bonzini } gen; 9749ab747fSPaolo Bonzini struct { 9849ab747fSPaolo Bonzini uint32_t type; 9949ab747fSPaolo Bonzini uint64_t code; 10049ab747fSPaolo Bonzini } error; 10149ab747fSPaolo Bonzini struct { 10249ab747fSPaolo Bonzini uint32_t type; 10349ab747fSPaolo Bonzini uint32_t len; 10449ab747fSPaolo Bonzini uint8_t data[]; 10549ab747fSPaolo Bonzini } data; 10649ab747fSPaolo Bonzini } p; 10749ab747fSPaolo Bonzini } EmulEvent; 10849ab747fSPaolo Bonzini 10949ab747fSPaolo Bonzini #define MAX_ATR_SIZE 40 11049ab747fSPaolo Bonzini struct EmulatedState { 11149ab747fSPaolo Bonzini CCIDCardState base; 11249ab747fSPaolo Bonzini uint8_t debug; 11349ab747fSPaolo Bonzini char *backend_str; 11449ab747fSPaolo Bonzini uint32_t backend; 11549ab747fSPaolo Bonzini char *cert1; 11649ab747fSPaolo Bonzini char *cert2; 11749ab747fSPaolo Bonzini char *cert3; 11849ab747fSPaolo Bonzini char *db; 11949ab747fSPaolo Bonzini uint8_t atr[MAX_ATR_SIZE]; 12049ab747fSPaolo Bonzini uint8_t atr_length; 12149ab747fSPaolo Bonzini QSIMPLEQ_HEAD(event_list, EmulEvent) event_list; 12249ab747fSPaolo Bonzini QemuMutex event_list_mutex; 12349ab747fSPaolo Bonzini QemuThread event_thread_id; 12449ab747fSPaolo Bonzini VReader *reader; 12549ab747fSPaolo Bonzini QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list; 12649ab747fSPaolo Bonzini QemuMutex vreader_mutex; /* and guest_apdu_list mutex */ 12749ab747fSPaolo Bonzini QemuMutex handle_apdu_mutex; 12849ab747fSPaolo Bonzini QemuCond handle_apdu_cond; 12949ab747fSPaolo Bonzini int pipe[2]; 13049ab747fSPaolo Bonzini int quit_apdu_thread; 13149ab747fSPaolo Bonzini QemuThread apdu_thread_id; 13249ab747fSPaolo Bonzini }; 13349ab747fSPaolo Bonzini 13449ab747fSPaolo Bonzini static void emulated_apdu_from_guest(CCIDCardState *base, 13549ab747fSPaolo Bonzini const uint8_t *apdu, uint32_t len) 13649ab747fSPaolo Bonzini { 13749ab747fSPaolo Bonzini EmulatedState *card = DO_UPCAST(EmulatedState, base, base); 13849ab747fSPaolo Bonzini EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len); 13949ab747fSPaolo Bonzini 14049ab747fSPaolo Bonzini assert(event); 14149ab747fSPaolo Bonzini event->p.data.type = EMUL_GUEST_APDU; 14249ab747fSPaolo Bonzini event->p.data.len = len; 14349ab747fSPaolo Bonzini memcpy(event->p.data.data, apdu, len); 14449ab747fSPaolo Bonzini qemu_mutex_lock(&card->vreader_mutex); 14549ab747fSPaolo Bonzini QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry); 14649ab747fSPaolo Bonzini qemu_mutex_unlock(&card->vreader_mutex); 14749ab747fSPaolo Bonzini qemu_mutex_lock(&card->handle_apdu_mutex); 14849ab747fSPaolo Bonzini qemu_cond_signal(&card->handle_apdu_cond); 14949ab747fSPaolo Bonzini qemu_mutex_unlock(&card->handle_apdu_mutex); 15049ab747fSPaolo Bonzini } 15149ab747fSPaolo Bonzini 15249ab747fSPaolo Bonzini static const uint8_t *emulated_get_atr(CCIDCardState *base, uint32_t *len) 15349ab747fSPaolo Bonzini { 15449ab747fSPaolo Bonzini EmulatedState *card = DO_UPCAST(EmulatedState, base, base); 15549ab747fSPaolo Bonzini 15649ab747fSPaolo Bonzini *len = card->atr_length; 15749ab747fSPaolo Bonzini return card->atr; 15849ab747fSPaolo Bonzini } 15949ab747fSPaolo Bonzini 16049ab747fSPaolo Bonzini static void emulated_push_event(EmulatedState *card, EmulEvent *event) 16149ab747fSPaolo Bonzini { 16249ab747fSPaolo Bonzini qemu_mutex_lock(&card->event_list_mutex); 16349ab747fSPaolo Bonzini QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry); 16449ab747fSPaolo Bonzini qemu_mutex_unlock(&card->event_list_mutex); 16549ab747fSPaolo Bonzini if (write(card->pipe[1], card, 1) != 1) { 16649ab747fSPaolo Bonzini DPRINTF(card, 1, "write to pipe failed\n"); 16749ab747fSPaolo Bonzini } 16849ab747fSPaolo Bonzini } 16949ab747fSPaolo Bonzini 17049ab747fSPaolo Bonzini static void emulated_push_type(EmulatedState *card, uint32_t type) 17149ab747fSPaolo Bonzini { 17249ab747fSPaolo Bonzini EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent)); 17349ab747fSPaolo Bonzini 17449ab747fSPaolo Bonzini assert(event); 17549ab747fSPaolo Bonzini event->p.gen.type = type; 17649ab747fSPaolo Bonzini emulated_push_event(card, event); 17749ab747fSPaolo Bonzini } 17849ab747fSPaolo Bonzini 17949ab747fSPaolo Bonzini static void emulated_push_error(EmulatedState *card, uint64_t code) 18049ab747fSPaolo Bonzini { 18149ab747fSPaolo Bonzini EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent)); 18249ab747fSPaolo Bonzini 18349ab747fSPaolo Bonzini assert(event); 18449ab747fSPaolo Bonzini event->p.error.type = EMUL_ERROR; 18549ab747fSPaolo Bonzini event->p.error.code = code; 18649ab747fSPaolo Bonzini emulated_push_event(card, event); 18749ab747fSPaolo Bonzini } 18849ab747fSPaolo Bonzini 18949ab747fSPaolo Bonzini static void emulated_push_data_type(EmulatedState *card, uint32_t type, 19049ab747fSPaolo Bonzini const uint8_t *data, uint32_t len) 19149ab747fSPaolo Bonzini { 19249ab747fSPaolo Bonzini EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len); 19349ab747fSPaolo Bonzini 19449ab747fSPaolo Bonzini assert(event); 19549ab747fSPaolo Bonzini event->p.data.type = type; 19649ab747fSPaolo Bonzini event->p.data.len = len; 19749ab747fSPaolo Bonzini memcpy(event->p.data.data, data, len); 19849ab747fSPaolo Bonzini emulated_push_event(card, event); 19949ab747fSPaolo Bonzini } 20049ab747fSPaolo Bonzini 20149ab747fSPaolo Bonzini static void emulated_push_reader_insert(EmulatedState *card) 20249ab747fSPaolo Bonzini { 20349ab747fSPaolo Bonzini emulated_push_type(card, EMUL_READER_INSERT); 20449ab747fSPaolo Bonzini } 20549ab747fSPaolo Bonzini 20649ab747fSPaolo Bonzini static void emulated_push_reader_remove(EmulatedState *card) 20749ab747fSPaolo Bonzini { 20849ab747fSPaolo Bonzini emulated_push_type(card, EMUL_READER_REMOVE); 20949ab747fSPaolo Bonzini } 21049ab747fSPaolo Bonzini 21149ab747fSPaolo Bonzini static void emulated_push_card_insert(EmulatedState *card, 21249ab747fSPaolo Bonzini const uint8_t *atr, uint32_t len) 21349ab747fSPaolo Bonzini { 21449ab747fSPaolo Bonzini emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len); 21549ab747fSPaolo Bonzini } 21649ab747fSPaolo Bonzini 21749ab747fSPaolo Bonzini static void emulated_push_card_remove(EmulatedState *card) 21849ab747fSPaolo Bonzini { 21949ab747fSPaolo Bonzini emulated_push_type(card, EMUL_CARD_REMOVE); 22049ab747fSPaolo Bonzini } 22149ab747fSPaolo Bonzini 22249ab747fSPaolo Bonzini static void emulated_push_response_apdu(EmulatedState *card, 22349ab747fSPaolo Bonzini const uint8_t *apdu, uint32_t len) 22449ab747fSPaolo Bonzini { 22549ab747fSPaolo Bonzini emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len); 22649ab747fSPaolo Bonzini } 22749ab747fSPaolo Bonzini 22849ab747fSPaolo Bonzini #define APDU_BUF_SIZE 270 22949ab747fSPaolo Bonzini static void *handle_apdu_thread(void* arg) 23049ab747fSPaolo Bonzini { 23149ab747fSPaolo Bonzini EmulatedState *card = arg; 23249ab747fSPaolo Bonzini uint8_t recv_data[APDU_BUF_SIZE]; 23349ab747fSPaolo Bonzini int recv_len; 23449ab747fSPaolo Bonzini VReaderStatus reader_status; 23549ab747fSPaolo Bonzini EmulEvent *event; 23649ab747fSPaolo Bonzini 23749ab747fSPaolo Bonzini while (1) { 23849ab747fSPaolo Bonzini qemu_mutex_lock(&card->handle_apdu_mutex); 23949ab747fSPaolo Bonzini qemu_cond_wait(&card->handle_apdu_cond, &card->handle_apdu_mutex); 24049ab747fSPaolo Bonzini qemu_mutex_unlock(&card->handle_apdu_mutex); 24149ab747fSPaolo Bonzini if (card->quit_apdu_thread) { 24249ab747fSPaolo Bonzini card->quit_apdu_thread = 0; /* debugging */ 24349ab747fSPaolo Bonzini break; 24449ab747fSPaolo Bonzini } 24549ab747fSPaolo Bonzini qemu_mutex_lock(&card->vreader_mutex); 24649ab747fSPaolo Bonzini while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) { 24749ab747fSPaolo Bonzini event = QSIMPLEQ_FIRST(&card->guest_apdu_list); 24849ab747fSPaolo Bonzini assert((unsigned long)event > 1000); 24949ab747fSPaolo Bonzini QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry); 25049ab747fSPaolo Bonzini if (event->p.data.type != EMUL_GUEST_APDU) { 25149ab747fSPaolo Bonzini DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n"); 25249ab747fSPaolo Bonzini g_free(event); 25349ab747fSPaolo Bonzini continue; 25449ab747fSPaolo Bonzini } 25549ab747fSPaolo Bonzini if (card->reader == NULL) { 25649ab747fSPaolo Bonzini DPRINTF(card, 1, "reader is NULL\n"); 25749ab747fSPaolo Bonzini g_free(event); 25849ab747fSPaolo Bonzini continue; 25949ab747fSPaolo Bonzini } 26049ab747fSPaolo Bonzini recv_len = sizeof(recv_data); 26149ab747fSPaolo Bonzini reader_status = vreader_xfr_bytes(card->reader, 26249ab747fSPaolo Bonzini event->p.data.data, event->p.data.len, 26349ab747fSPaolo Bonzini recv_data, &recv_len); 26449ab747fSPaolo Bonzini DPRINTF(card, 2, "got back apdu of length %d\n", recv_len); 26549ab747fSPaolo Bonzini if (reader_status == VREADER_OK) { 26649ab747fSPaolo Bonzini emulated_push_response_apdu(card, recv_data, recv_len); 26749ab747fSPaolo Bonzini } else { 26849ab747fSPaolo Bonzini emulated_push_error(card, reader_status); 26949ab747fSPaolo Bonzini } 27049ab747fSPaolo Bonzini g_free(event); 27149ab747fSPaolo Bonzini } 27249ab747fSPaolo Bonzini qemu_mutex_unlock(&card->vreader_mutex); 27349ab747fSPaolo Bonzini } 27449ab747fSPaolo Bonzini return NULL; 27549ab747fSPaolo Bonzini } 27649ab747fSPaolo Bonzini 27749ab747fSPaolo Bonzini static void *event_thread(void *arg) 27849ab747fSPaolo Bonzini { 27949ab747fSPaolo Bonzini int atr_len = MAX_ATR_SIZE; 28049ab747fSPaolo Bonzini uint8_t atr[MAX_ATR_SIZE]; 28149ab747fSPaolo Bonzini VEvent *event = NULL; 28249ab747fSPaolo Bonzini EmulatedState *card = arg; 28349ab747fSPaolo Bonzini 28449ab747fSPaolo Bonzini while (1) { 28549ab747fSPaolo Bonzini const char *reader_name; 28649ab747fSPaolo Bonzini 28749ab747fSPaolo Bonzini event = vevent_wait_next_vevent(); 28849ab747fSPaolo Bonzini if (event == NULL || event->type == VEVENT_LAST) { 28949ab747fSPaolo Bonzini break; 29049ab747fSPaolo Bonzini } 29149ab747fSPaolo Bonzini if (event->type != VEVENT_READER_INSERT) { 29249ab747fSPaolo Bonzini if (card->reader == NULL && event->reader != NULL) { 29349ab747fSPaolo Bonzini /* Happens after device_add followed by card remove or insert. 29449ab747fSPaolo Bonzini * XXX: create synthetic add_reader events if vcard_emul_init 29549ab747fSPaolo Bonzini * already called, which happens if device_del and device_add 29649ab747fSPaolo Bonzini * are called */ 29749ab747fSPaolo Bonzini card->reader = vreader_reference(event->reader); 29849ab747fSPaolo Bonzini } else { 29949ab747fSPaolo Bonzini if (event->reader != card->reader) { 30049ab747fSPaolo Bonzini fprintf(stderr, 30149ab747fSPaolo Bonzini "ERROR: wrong reader: quiting event_thread\n"); 30249ab747fSPaolo Bonzini break; 30349ab747fSPaolo Bonzini } 30449ab747fSPaolo Bonzini } 30549ab747fSPaolo Bonzini } 30649ab747fSPaolo Bonzini switch (event->type) { 30749ab747fSPaolo Bonzini case VEVENT_READER_INSERT: 30849ab747fSPaolo Bonzini /* TODO: take a specific reader. i.e. track which reader 30949ab747fSPaolo Bonzini * we are seeing here, check it is the one we want (the first, 31049ab747fSPaolo Bonzini * or by a particular name), and ignore if we don't want it. 31149ab747fSPaolo Bonzini */ 31249ab747fSPaolo Bonzini reader_name = vreader_get_name(event->reader); 31349ab747fSPaolo Bonzini if (card->reader != NULL) { 31449ab747fSPaolo Bonzini DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n", 31549ab747fSPaolo Bonzini vreader_get_name(card->reader), reader_name); 31649ab747fSPaolo Bonzini qemu_mutex_lock(&card->vreader_mutex); 31749ab747fSPaolo Bonzini vreader_free(card->reader); 31849ab747fSPaolo Bonzini qemu_mutex_unlock(&card->vreader_mutex); 31949ab747fSPaolo Bonzini emulated_push_reader_remove(card); 32049ab747fSPaolo Bonzini } 32149ab747fSPaolo Bonzini qemu_mutex_lock(&card->vreader_mutex); 32249ab747fSPaolo Bonzini DPRINTF(card, 2, "READER INSERT %s\n", reader_name); 32349ab747fSPaolo Bonzini card->reader = vreader_reference(event->reader); 32449ab747fSPaolo Bonzini qemu_mutex_unlock(&card->vreader_mutex); 32549ab747fSPaolo Bonzini emulated_push_reader_insert(card); 32649ab747fSPaolo Bonzini break; 32749ab747fSPaolo Bonzini case VEVENT_READER_REMOVE: 32849ab747fSPaolo Bonzini DPRINTF(card, 2, " READER REMOVE: %s\n", 32949ab747fSPaolo Bonzini vreader_get_name(event->reader)); 33049ab747fSPaolo Bonzini qemu_mutex_lock(&card->vreader_mutex); 33149ab747fSPaolo Bonzini vreader_free(card->reader); 33249ab747fSPaolo Bonzini card->reader = NULL; 33349ab747fSPaolo Bonzini qemu_mutex_unlock(&card->vreader_mutex); 33449ab747fSPaolo Bonzini emulated_push_reader_remove(card); 33549ab747fSPaolo Bonzini break; 33649ab747fSPaolo Bonzini case VEVENT_CARD_INSERT: 33749ab747fSPaolo Bonzini /* get the ATR (intended as a response to a power on from the 33849ab747fSPaolo Bonzini * reader */ 33949ab747fSPaolo Bonzini atr_len = MAX_ATR_SIZE; 34049ab747fSPaolo Bonzini vreader_power_on(event->reader, atr, &atr_len); 34149ab747fSPaolo Bonzini card->atr_length = (uint8_t)atr_len; 34249ab747fSPaolo Bonzini DPRINTF(card, 2, " CARD INSERT\n"); 34349ab747fSPaolo Bonzini emulated_push_card_insert(card, atr, atr_len); 34449ab747fSPaolo Bonzini break; 34549ab747fSPaolo Bonzini case VEVENT_CARD_REMOVE: 34649ab747fSPaolo Bonzini DPRINTF(card, 2, " CARD REMOVE\n"); 34749ab747fSPaolo Bonzini emulated_push_card_remove(card); 34849ab747fSPaolo Bonzini break; 34949ab747fSPaolo Bonzini case VEVENT_LAST: /* quit */ 35049ab747fSPaolo Bonzini vevent_delete(event); 35149ab747fSPaolo Bonzini return NULL; 35249ab747fSPaolo Bonzini break; 35349ab747fSPaolo Bonzini default: 35449ab747fSPaolo Bonzini break; 35549ab747fSPaolo Bonzini } 35649ab747fSPaolo Bonzini vevent_delete(event); 35749ab747fSPaolo Bonzini } 35849ab747fSPaolo Bonzini return NULL; 35949ab747fSPaolo Bonzini } 36049ab747fSPaolo Bonzini 36149ab747fSPaolo Bonzini static void pipe_read(void *opaque) 36249ab747fSPaolo Bonzini { 36349ab747fSPaolo Bonzini EmulatedState *card = opaque; 36449ab747fSPaolo Bonzini EmulEvent *event, *next; 36549ab747fSPaolo Bonzini char dummy; 36649ab747fSPaolo Bonzini int len; 36749ab747fSPaolo Bonzini 36849ab747fSPaolo Bonzini do { 36949ab747fSPaolo Bonzini len = read(card->pipe[0], &dummy, sizeof(dummy)); 37049ab747fSPaolo Bonzini } while (len == sizeof(dummy)); 37149ab747fSPaolo Bonzini qemu_mutex_lock(&card->event_list_mutex); 37249ab747fSPaolo Bonzini QSIMPLEQ_FOREACH_SAFE(event, &card->event_list, entry, next) { 37349ab747fSPaolo Bonzini DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type)); 37449ab747fSPaolo Bonzini switch (event->p.gen.type) { 37549ab747fSPaolo Bonzini case EMUL_RESPONSE_APDU: 37649ab747fSPaolo Bonzini ccid_card_send_apdu_to_guest(&card->base, event->p.data.data, 37749ab747fSPaolo Bonzini event->p.data.len); 37849ab747fSPaolo Bonzini break; 37949ab747fSPaolo Bonzini case EMUL_READER_INSERT: 38049ab747fSPaolo Bonzini ccid_card_ccid_attach(&card->base); 38149ab747fSPaolo Bonzini break; 38249ab747fSPaolo Bonzini case EMUL_READER_REMOVE: 38349ab747fSPaolo Bonzini ccid_card_ccid_detach(&card->base); 38449ab747fSPaolo Bonzini break; 38549ab747fSPaolo Bonzini case EMUL_CARD_INSERT: 38649ab747fSPaolo Bonzini assert(event->p.data.len <= MAX_ATR_SIZE); 38749ab747fSPaolo Bonzini card->atr_length = event->p.data.len; 38849ab747fSPaolo Bonzini memcpy(card->atr, event->p.data.data, card->atr_length); 38949ab747fSPaolo Bonzini ccid_card_card_inserted(&card->base); 39049ab747fSPaolo Bonzini break; 39149ab747fSPaolo Bonzini case EMUL_CARD_REMOVE: 39249ab747fSPaolo Bonzini ccid_card_card_removed(&card->base); 39349ab747fSPaolo Bonzini break; 39449ab747fSPaolo Bonzini case EMUL_ERROR: 39549ab747fSPaolo Bonzini ccid_card_card_error(&card->base, event->p.error.code); 39649ab747fSPaolo Bonzini break; 39749ab747fSPaolo Bonzini default: 39849ab747fSPaolo Bonzini DPRINTF(card, 2, "unexpected event\n"); 39949ab747fSPaolo Bonzini break; 40049ab747fSPaolo Bonzini } 40149ab747fSPaolo Bonzini g_free(event); 40249ab747fSPaolo Bonzini } 40349ab747fSPaolo Bonzini QSIMPLEQ_INIT(&card->event_list); 40449ab747fSPaolo Bonzini qemu_mutex_unlock(&card->event_list_mutex); 40549ab747fSPaolo Bonzini } 40649ab747fSPaolo Bonzini 40749ab747fSPaolo Bonzini static int init_pipe_signaling(EmulatedState *card) 40849ab747fSPaolo Bonzini { 40949ab747fSPaolo Bonzini if (pipe(card->pipe) < 0) { 41049ab747fSPaolo Bonzini DPRINTF(card, 2, "pipe creation failed\n"); 41149ab747fSPaolo Bonzini return -1; 41249ab747fSPaolo Bonzini } 41349ab747fSPaolo Bonzini fcntl(card->pipe[0], F_SETFL, O_NONBLOCK); 41449ab747fSPaolo Bonzini fcntl(card->pipe[1], F_SETFL, O_NONBLOCK); 41549ab747fSPaolo Bonzini fcntl(card->pipe[0], F_SETOWN, getpid()); 41649ab747fSPaolo Bonzini qemu_set_fd_handler(card->pipe[0], pipe_read, NULL, card); 41749ab747fSPaolo Bonzini return 0; 41849ab747fSPaolo Bonzini } 41949ab747fSPaolo Bonzini 42049ab747fSPaolo Bonzini #define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb" 42149ab747fSPaolo Bonzini #define CERTIFICATES_ARGS_TEMPLATE\ 42249ab747fSPaolo Bonzini "db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)" 42349ab747fSPaolo Bonzini 42449ab747fSPaolo Bonzini static int wrap_vcard_emul_init(VCardEmulOptions *options) 42549ab747fSPaolo Bonzini { 42649ab747fSPaolo Bonzini static int called; 42749ab747fSPaolo Bonzini static int options_was_null; 42849ab747fSPaolo Bonzini 42949ab747fSPaolo Bonzini if (called) { 43049ab747fSPaolo Bonzini if ((options == NULL) != options_was_null) { 43149ab747fSPaolo Bonzini printf("%s: warning: running emulated with certificates" 43249ab747fSPaolo Bonzini " and emulated side by side is not supported\n", 43349ab747fSPaolo Bonzini __func__); 43449ab747fSPaolo Bonzini return VCARD_EMUL_FAIL; 43549ab747fSPaolo Bonzini } 43649ab747fSPaolo Bonzini vcard_emul_replay_insertion_events(); 43749ab747fSPaolo Bonzini return VCARD_EMUL_OK; 43849ab747fSPaolo Bonzini } 43949ab747fSPaolo Bonzini options_was_null = (options == NULL); 44049ab747fSPaolo Bonzini called = 1; 44149ab747fSPaolo Bonzini return vcard_emul_init(options); 44249ab747fSPaolo Bonzini } 44349ab747fSPaolo Bonzini 44449ab747fSPaolo Bonzini static int emulated_initialize_vcard_from_certificates(EmulatedState *card) 44549ab747fSPaolo Bonzini { 44649ab747fSPaolo Bonzini char emul_args[200]; 44749ab747fSPaolo Bonzini VCardEmulOptions *options = NULL; 44849ab747fSPaolo Bonzini 44949ab747fSPaolo Bonzini snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE, 45049ab747fSPaolo Bonzini card->db ? card->db : CERTIFICATES_DEFAULT_DB, 45149ab747fSPaolo Bonzini card->cert1, card->cert2, card->cert3); 45249ab747fSPaolo Bonzini options = vcard_emul_options(emul_args); 45349ab747fSPaolo Bonzini if (options == NULL) { 45449ab747fSPaolo Bonzini printf("%s: warning: not using certificates due to" 45549ab747fSPaolo Bonzini " initialization error\n", __func__); 45649ab747fSPaolo Bonzini } 45749ab747fSPaolo Bonzini return wrap_vcard_emul_init(options); 45849ab747fSPaolo Bonzini } 45949ab747fSPaolo Bonzini 46049ab747fSPaolo Bonzini typedef struct EnumTable { 46149ab747fSPaolo Bonzini const char *name; 46249ab747fSPaolo Bonzini uint32_t value; 46349ab747fSPaolo Bonzini } EnumTable; 46449ab747fSPaolo Bonzini 46549ab747fSPaolo Bonzini EnumTable backend_enum_table[] = { 46649ab747fSPaolo Bonzini {BACKEND_NSS_EMULATED_NAME, BACKEND_NSS_EMULATED}, 46749ab747fSPaolo Bonzini {BACKEND_CERTIFICATES_NAME, BACKEND_CERTIFICATES}, 46849ab747fSPaolo Bonzini {NULL, 0}, 46949ab747fSPaolo Bonzini }; 47049ab747fSPaolo Bonzini 47149ab747fSPaolo Bonzini static uint32_t parse_enumeration(char *str, 47249ab747fSPaolo Bonzini EnumTable *table, uint32_t not_found_value) 47349ab747fSPaolo Bonzini { 47449ab747fSPaolo Bonzini uint32_t ret = not_found_value; 47549ab747fSPaolo Bonzini 47649ab747fSPaolo Bonzini while (table->name != NULL) { 47749ab747fSPaolo Bonzini if (strcmp(table->name, str) == 0) { 47849ab747fSPaolo Bonzini ret = table->value; 47949ab747fSPaolo Bonzini break; 48049ab747fSPaolo Bonzini } 48149ab747fSPaolo Bonzini table++; 48249ab747fSPaolo Bonzini } 48349ab747fSPaolo Bonzini return ret; 48449ab747fSPaolo Bonzini } 48549ab747fSPaolo Bonzini 48649ab747fSPaolo Bonzini static int emulated_initfn(CCIDCardState *base) 48749ab747fSPaolo Bonzini { 48849ab747fSPaolo Bonzini EmulatedState *card = DO_UPCAST(EmulatedState, base, base); 48949ab747fSPaolo Bonzini VCardEmulError ret; 49049ab747fSPaolo Bonzini EnumTable *ptable; 49149ab747fSPaolo Bonzini 49249ab747fSPaolo Bonzini QSIMPLEQ_INIT(&card->event_list); 49349ab747fSPaolo Bonzini QSIMPLEQ_INIT(&card->guest_apdu_list); 49449ab747fSPaolo Bonzini qemu_mutex_init(&card->event_list_mutex); 49549ab747fSPaolo Bonzini qemu_mutex_init(&card->vreader_mutex); 49649ab747fSPaolo Bonzini qemu_mutex_init(&card->handle_apdu_mutex); 49749ab747fSPaolo Bonzini qemu_cond_init(&card->handle_apdu_cond); 49849ab747fSPaolo Bonzini card->reader = NULL; 49949ab747fSPaolo Bonzini card->quit_apdu_thread = 0; 50049ab747fSPaolo Bonzini if (init_pipe_signaling(card) < 0) { 50149ab747fSPaolo Bonzini return -1; 50249ab747fSPaolo Bonzini } 50349ab747fSPaolo Bonzini card->backend = parse_enumeration(card->backend_str, backend_enum_table, 0); 50449ab747fSPaolo Bonzini if (card->backend == 0) { 50549ab747fSPaolo Bonzini printf("unknown backend, must be one of:\n"); 50649ab747fSPaolo Bonzini for (ptable = backend_enum_table; ptable->name != NULL; ++ptable) { 50749ab747fSPaolo Bonzini printf("%s\n", ptable->name); 50849ab747fSPaolo Bonzini } 50949ab747fSPaolo Bonzini return -1; 51049ab747fSPaolo Bonzini } 51149ab747fSPaolo Bonzini 51249ab747fSPaolo Bonzini /* TODO: a passthru backened that works on local machine. third card type?*/ 51349ab747fSPaolo Bonzini if (card->backend == BACKEND_CERTIFICATES) { 51449ab747fSPaolo Bonzini if (card->cert1 != NULL && card->cert2 != NULL && card->cert3 != NULL) { 51549ab747fSPaolo Bonzini ret = emulated_initialize_vcard_from_certificates(card); 51649ab747fSPaolo Bonzini } else { 51749ab747fSPaolo Bonzini printf("%s: you must provide all three certs for" 51849ab747fSPaolo Bonzini " certificates backend\n", EMULATED_DEV_NAME); 51949ab747fSPaolo Bonzini return -1; 52049ab747fSPaolo Bonzini } 52149ab747fSPaolo Bonzini } else { 52249ab747fSPaolo Bonzini if (card->backend != BACKEND_NSS_EMULATED) { 52349ab747fSPaolo Bonzini printf("%s: bad backend specified. The options are:\n%s (default)," 52449ab747fSPaolo Bonzini " %s.\n", EMULATED_DEV_NAME, BACKEND_NSS_EMULATED_NAME, 52549ab747fSPaolo Bonzini BACKEND_CERTIFICATES_NAME); 52649ab747fSPaolo Bonzini return -1; 52749ab747fSPaolo Bonzini } 52849ab747fSPaolo Bonzini if (card->cert1 != NULL || card->cert2 != NULL || card->cert3 != NULL) { 52949ab747fSPaolo Bonzini printf("%s: unexpected cert parameters to nss emulated backend\n", 53049ab747fSPaolo Bonzini EMULATED_DEV_NAME); 53149ab747fSPaolo Bonzini return -1; 53249ab747fSPaolo Bonzini } 53349ab747fSPaolo Bonzini /* default to mirroring the local hardware readers */ 53449ab747fSPaolo Bonzini ret = wrap_vcard_emul_init(NULL); 53549ab747fSPaolo Bonzini } 53649ab747fSPaolo Bonzini if (ret != VCARD_EMUL_OK) { 53749ab747fSPaolo Bonzini printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME); 53849ab747fSPaolo Bonzini return -1; 53949ab747fSPaolo Bonzini } 54049ab747fSPaolo Bonzini qemu_thread_create(&card->event_thread_id, event_thread, card, 54149ab747fSPaolo Bonzini QEMU_THREAD_JOINABLE); 54249ab747fSPaolo Bonzini qemu_thread_create(&card->apdu_thread_id, handle_apdu_thread, card, 54349ab747fSPaolo Bonzini QEMU_THREAD_JOINABLE); 54449ab747fSPaolo Bonzini return 0; 54549ab747fSPaolo Bonzini } 54649ab747fSPaolo Bonzini 54749ab747fSPaolo Bonzini static int emulated_exitfn(CCIDCardState *base) 54849ab747fSPaolo Bonzini { 54949ab747fSPaolo Bonzini EmulatedState *card = DO_UPCAST(EmulatedState, base, base); 55049ab747fSPaolo Bonzini VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL); 55149ab747fSPaolo Bonzini 55249ab747fSPaolo Bonzini vevent_queue_vevent(vevent); /* stop vevent thread */ 55349ab747fSPaolo Bonzini qemu_thread_join(&card->event_thread_id); 55449ab747fSPaolo Bonzini 55549ab747fSPaolo Bonzini card->quit_apdu_thread = 1; /* stop handle_apdu thread */ 55649ab747fSPaolo Bonzini qemu_cond_signal(&card->handle_apdu_cond); 55749ab747fSPaolo Bonzini qemu_thread_join(&card->apdu_thread_id); 55849ab747fSPaolo Bonzini 55949ab747fSPaolo Bonzini /* threads exited, can destroy all condvars/mutexes */ 56049ab747fSPaolo Bonzini qemu_cond_destroy(&card->handle_apdu_cond); 56149ab747fSPaolo Bonzini qemu_mutex_destroy(&card->handle_apdu_mutex); 56249ab747fSPaolo Bonzini qemu_mutex_destroy(&card->vreader_mutex); 56349ab747fSPaolo Bonzini qemu_mutex_destroy(&card->event_list_mutex); 56449ab747fSPaolo Bonzini return 0; 56549ab747fSPaolo Bonzini } 56649ab747fSPaolo Bonzini 56749ab747fSPaolo Bonzini static Property emulated_card_properties[] = { 56849ab747fSPaolo Bonzini DEFINE_PROP_STRING("backend", EmulatedState, backend_str), 56949ab747fSPaolo Bonzini DEFINE_PROP_STRING("cert1", EmulatedState, cert1), 57049ab747fSPaolo Bonzini DEFINE_PROP_STRING("cert2", EmulatedState, cert2), 57149ab747fSPaolo Bonzini DEFINE_PROP_STRING("cert3", EmulatedState, cert3), 57249ab747fSPaolo Bonzini DEFINE_PROP_STRING("db", EmulatedState, db), 57349ab747fSPaolo Bonzini DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0), 57449ab747fSPaolo Bonzini DEFINE_PROP_END_OF_LIST(), 57549ab747fSPaolo Bonzini }; 57649ab747fSPaolo Bonzini 57749ab747fSPaolo Bonzini static void emulated_class_initfn(ObjectClass *klass, void *data) 57849ab747fSPaolo Bonzini { 57949ab747fSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass); 58049ab747fSPaolo Bonzini CCIDCardClass *cc = CCID_CARD_CLASS(klass); 58149ab747fSPaolo Bonzini 58249ab747fSPaolo Bonzini cc->initfn = emulated_initfn; 58349ab747fSPaolo Bonzini cc->exitfn = emulated_exitfn; 58449ab747fSPaolo Bonzini cc->get_atr = emulated_get_atr; 58549ab747fSPaolo Bonzini cc->apdu_from_guest = emulated_apdu_from_guest; 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