summaryrefslogtreecommitdiffstats
path: root/qemu/libcacard/vreader.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/libcacard/vreader.c')
-rw-r--r--qemu/libcacard/vreader.c578
1 files changed, 578 insertions, 0 deletions
diff --git a/qemu/libcacard/vreader.c b/qemu/libcacard/vreader.c
new file mode 100644
index 000000000..9725f46a7
--- /dev/null
+++ b/qemu/libcacard/vreader.c
@@ -0,0 +1,578 @@
+/*
+ * emulate the reader
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifdef G_LOG_DOMAIN
+#undef G_LOG_DOMAIN
+#endif
+#define G_LOG_DOMAIN "libcacard"
+
+#include "glib-compat.h"
+
+#include <string.h>
+
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+#include "vreader.h"
+#include "vevent.h"
+#include "cac.h" /* just for debugging defines */
+
+#define LIBCACARD_LOG_DOMAIN "libcacard"
+
+struct VReaderStruct {
+ int reference_count;
+ VCard *card;
+ char *name;
+ vreader_id_t id;
+ CompatGMutex lock;
+ VReaderEmul *reader_private;
+ VReaderEmulFree reader_private_free;
+};
+
+/*
+ * Debug helpers
+ */
+
+static const char *
+apdu_ins_to_string(int ins)
+{
+ switch (ins) {
+ case VCARD7816_INS_MANAGE_CHANNEL:
+ return "manage channel";
+ case VCARD7816_INS_EXTERNAL_AUTHENTICATE:
+ return "external authenticate";
+ case VCARD7816_INS_GET_CHALLENGE:
+ return "get challenge";
+ case VCARD7816_INS_INTERNAL_AUTHENTICATE:
+ return "internal authenticate";
+ case VCARD7816_INS_ERASE_BINARY:
+ return "erase binary";
+ case VCARD7816_INS_READ_BINARY:
+ return "read binary";
+ case VCARD7816_INS_WRITE_BINARY:
+ return "write binary";
+ case VCARD7816_INS_UPDATE_BINARY:
+ return "update binary";
+ case VCARD7816_INS_READ_RECORD:
+ return "read record";
+ case VCARD7816_INS_WRITE_RECORD:
+ return "write record";
+ case VCARD7816_INS_UPDATE_RECORD:
+ return "update record";
+ case VCARD7816_INS_APPEND_RECORD:
+ return "append record";
+ case VCARD7816_INS_ENVELOPE:
+ return "envelope";
+ case VCARD7816_INS_PUT_DATA:
+ return "put data";
+ case VCARD7816_INS_GET_DATA:
+ return "get data";
+ case VCARD7816_INS_SELECT_FILE:
+ return "select file";
+ case VCARD7816_INS_VERIFY:
+ return "verify";
+ case VCARD7816_INS_GET_RESPONSE:
+ return "get response";
+ case CAC_GET_PROPERTIES:
+ return "get properties";
+ case CAC_GET_ACR:
+ return "get acr";
+ case CAC_READ_BUFFER:
+ return "read buffer";
+ case CAC_UPDATE_BUFFER:
+ return "update buffer";
+ case CAC_SIGN_DECRYPT:
+ return "sign decrypt";
+ case CAC_GET_CERTIFICATE:
+ return "get certificate";
+ }
+ return "unknown";
+}
+
+/* manage locking */
+static inline void
+vreader_lock(VReader *reader)
+{
+ g_mutex_lock(&reader->lock);
+}
+
+static inline void
+vreader_unlock(VReader *reader)
+{
+ g_mutex_unlock(&reader->lock);
+}
+
+/*
+ * vreader constructor
+ */
+VReader *
+vreader_new(const char *name, VReaderEmul *private,
+ VReaderEmulFree private_free)
+{
+ VReader *reader;
+
+ reader = g_new(VReader, 1);
+ g_mutex_init(&reader->lock);
+ reader->reference_count = 1;
+ reader->name = g_strdup(name);
+ reader->card = NULL;
+ reader->id = (vreader_id_t)-1;
+ reader->reader_private = private;
+ reader->reader_private_free = private_free;
+ return reader;
+}
+
+/* get a reference */
+VReader*
+vreader_reference(VReader *reader)
+{
+ if (reader == NULL) {
+ return NULL;
+ }
+ vreader_lock(reader);
+ reader->reference_count++;
+ vreader_unlock(reader);
+ return reader;
+}
+
+/* free a reference */
+void
+vreader_free(VReader *reader)
+{
+ if (reader == NULL) {
+ return;
+ }
+ vreader_lock(reader);
+ if (reader->reference_count-- > 1) {
+ vreader_unlock(reader);
+ return;
+ }
+ vreader_unlock(reader);
+ g_mutex_clear(&reader->lock);
+ if (reader->card) {
+ vcard_free(reader->card);
+ }
+ g_free(reader->name);
+ if (reader->reader_private_free) {
+ reader->reader_private_free(reader->reader_private);
+ }
+ g_free(reader);
+}
+
+static VCard *
+vreader_get_card(VReader *reader)
+{
+ VCard *card;
+
+ vreader_lock(reader);
+ card = vcard_reference(reader->card);
+ vreader_unlock(reader);
+ return card;
+}
+
+VReaderStatus
+vreader_card_is_present(VReader *reader)
+{
+ VCard *card = vreader_get_card(reader);
+
+ if (card == NULL) {
+ return VREADER_NO_CARD;
+ }
+ vcard_free(card);
+ return VREADER_OK;
+}
+
+vreader_id_t
+vreader_get_id(VReader *reader)
+{
+ if (reader == NULL) {
+ return (vreader_id_t)-1;
+ }
+ return reader->id;
+}
+
+VReaderStatus
+vreader_set_id(VReader *reader, vreader_id_t id)
+{
+ if (reader == NULL) {
+ return VREADER_NO_CARD;
+ }
+ reader->id = id;
+ return VREADER_OK;
+}
+
+const char *
+vreader_get_name(VReader *reader)
+{
+ if (reader == NULL) {
+ return NULL;
+ }
+ return reader->name;
+}
+
+VReaderEmul *
+vreader_get_private(VReader *reader)
+{
+ return reader->reader_private;
+}
+
+static VReaderStatus
+vreader_reset(VReader *reader, VCardPower power, unsigned char *atr, int *len)
+{
+ VCard *card = vreader_get_card(reader);
+
+ if (card == NULL) {
+ return VREADER_NO_CARD;
+ }
+ /*
+ * clean up our state
+ */
+ vcard_reset(card, power);
+ if (atr) {
+ vcard_get_atr(card, atr, len);
+ }
+ vcard_free(card); /* free our reference */
+ return VREADER_OK;
+}
+
+VReaderStatus
+vreader_power_on(VReader *reader, unsigned char *atr, int *len)
+{
+ return vreader_reset(reader, VCARD_POWER_ON, atr, len);
+}
+
+VReaderStatus
+vreader_power_off(VReader *reader)
+{
+ return vreader_reset(reader, VCARD_POWER_OFF, NULL, 0);
+}
+
+
+VReaderStatus
+vreader_xfr_bytes(VReader *reader,
+ unsigned char *send_buf, int send_buf_len,
+ unsigned char *receive_buf, int *receive_buf_len)
+{
+ VCardAPDU *apdu;
+ VCardResponse *response = NULL;
+ VCardStatus card_status;
+ unsigned short status;
+ VCard *card = vreader_get_card(reader);
+
+ if (card == NULL) {
+ return VREADER_NO_CARD;
+ }
+
+ apdu = vcard_apdu_new(send_buf, send_buf_len, &status);
+ if (apdu == NULL) {
+ response = vcard_make_response(status);
+ card_status = VCARD_DONE;
+ } else {
+ g_debug("%s: CLS=0x%x,INS=0x%x,P1=0x%x,P2=0x%x,Lc=%d,Le=%d %s",
+ __func__, apdu->a_cla, apdu->a_ins, apdu->a_p1, apdu->a_p2,
+ apdu->a_Lc, apdu->a_Le, apdu_ins_to_string(apdu->a_ins));
+ card_status = vcard_process_apdu(card, apdu, &response);
+ if (response) {
+ g_debug("%s: status=%d sw1=0x%x sw2=0x%x len=%d (total=%d)",
+ __func__, response->b_status, response->b_sw1,
+ response->b_sw2, response->b_len, response->b_total_len);
+ }
+ }
+ assert(card_status == VCARD_DONE && response);
+ int size = MIN(*receive_buf_len, response->b_total_len);
+ memcpy(receive_buf, response->b_data, size);
+ *receive_buf_len = size;
+ vcard_response_delete(response);
+ vcard_apdu_delete(apdu);
+ vcard_free(card); /* free our reference */
+ return VREADER_OK;
+}
+
+struct VReaderListStruct {
+ VReaderListEntry *head;
+ VReaderListEntry *tail;
+};
+
+struct VReaderListEntryStruct {
+ VReaderListEntry *next;
+ VReaderListEntry *prev;
+ VReader *reader;
+};
+
+
+static VReaderListEntry *
+vreader_list_entry_new(VReader *reader)
+{
+ VReaderListEntry *new_reader_list_entry;
+
+ new_reader_list_entry = g_new0(VReaderListEntry, 1);
+ new_reader_list_entry->reader = vreader_reference(reader);
+ return new_reader_list_entry;
+}
+
+static void
+vreader_list_entry_delete(VReaderListEntry *entry)
+{
+ if (entry == NULL) {
+ return;
+ }
+ vreader_free(entry->reader);
+ g_free(entry);
+}
+
+
+static VReaderList *
+vreader_list_new(void)
+{
+ VReaderList *new_reader_list;
+
+ new_reader_list = g_new0(VReaderList, 1);
+ return new_reader_list;
+}
+
+void
+vreader_list_delete(VReaderList *list)
+{
+ VReaderListEntry *current_entry;
+ VReaderListEntry *next_entry;
+ for (current_entry = vreader_list_get_first(list); current_entry;
+ current_entry = next_entry) {
+ next_entry = vreader_list_get_next(current_entry);
+ vreader_list_entry_delete(current_entry);
+ }
+ g_free(list);
+}
+
+
+VReaderListEntry *
+vreader_list_get_first(VReaderList *list)
+{
+ return list ? list->head : NULL;
+}
+
+VReaderListEntry *
+vreader_list_get_next(VReaderListEntry *current)
+{
+ return current ? current->next : NULL;
+}
+
+VReader *
+vreader_list_get_reader(VReaderListEntry *entry)
+{
+ return entry ? vreader_reference(entry->reader) : NULL;
+}
+
+static void
+vreader_queue(VReaderList *list, VReaderListEntry *entry)
+{
+ if (entry == NULL) {
+ return;
+ }
+ entry->next = NULL;
+ entry->prev = list->tail;
+ if (list->head) {
+ list->tail->next = entry;
+ } else {
+ list->head = entry;
+ }
+ list->tail = entry;
+}
+
+static void
+vreader_dequeue(VReaderList *list, VReaderListEntry *entry)
+{
+ if (entry == NULL) {
+ return;
+ }
+ if (entry->next == NULL) {
+ list->tail = entry->prev;
+ } else if (entry->prev == NULL) {
+ list->head = entry->next;
+ } else {
+ entry->prev->next = entry->next;
+ entry->next->prev = entry->prev;
+ }
+ if ((list->tail == NULL) || (list->head == NULL)) {
+ list->head = list->tail = NULL;
+ }
+ entry->next = entry->prev = NULL;
+}
+
+static VReaderList *vreader_list;
+static CompatGMutex vreader_list_mutex;
+
+static void
+vreader_list_init(void)
+{
+ vreader_list = vreader_list_new();
+}
+
+static void
+vreader_list_lock(void)
+{
+ g_mutex_lock(&vreader_list_mutex);
+}
+
+static void
+vreader_list_unlock(void)
+{
+ g_mutex_unlock(&vreader_list_mutex);
+}
+
+static VReaderList *
+vreader_copy_list(VReaderList *list)
+{
+ VReaderList *new_list;
+ VReaderListEntry *current_entry;
+
+ new_list = vreader_list_new();
+ if (new_list == NULL) {
+ return NULL;
+ }
+ for (current_entry = vreader_list_get_first(list); current_entry;
+ current_entry = vreader_list_get_next(current_entry)) {
+ VReader *reader = vreader_list_get_reader(current_entry);
+ VReaderListEntry *new_entry = vreader_list_entry_new(reader);
+
+ vreader_free(reader);
+ vreader_queue(new_list, new_entry);
+ }
+ return new_list;
+}
+
+VReaderList *
+vreader_get_reader_list(void)
+{
+ VReaderList *new_reader_list;
+
+ vreader_list_lock();
+ new_reader_list = vreader_copy_list(vreader_list);
+ vreader_list_unlock();
+ return new_reader_list;
+}
+
+VReader *
+vreader_get_reader_by_id(vreader_id_t id)
+{
+ VReader *reader = NULL;
+ VReaderListEntry *current_entry;
+
+ if (id == (vreader_id_t) -1) {
+ return NULL;
+ }
+
+ vreader_list_lock();
+ for (current_entry = vreader_list_get_first(vreader_list); current_entry;
+ current_entry = vreader_list_get_next(current_entry)) {
+ VReader *creader = vreader_list_get_reader(current_entry);
+ if (creader->id == id) {
+ reader = creader;
+ break;
+ }
+ vreader_free(creader);
+ }
+ vreader_list_unlock();
+ return reader;
+}
+
+VReader *
+vreader_get_reader_by_name(const char *name)
+{
+ VReader *reader = NULL;
+ VReaderListEntry *current_entry;
+
+ vreader_list_lock();
+ for (current_entry = vreader_list_get_first(vreader_list); current_entry;
+ current_entry = vreader_list_get_next(current_entry)) {
+ VReader *creader = vreader_list_get_reader(current_entry);
+ if (strcmp(creader->name, name) == 0) {
+ reader = creader;
+ break;
+ }
+ vreader_free(creader);
+ }
+ vreader_list_unlock();
+ return reader;
+}
+
+/* called from card_emul to initialize the readers */
+VReaderStatus
+vreader_add_reader(VReader *reader)
+{
+ VReaderListEntry *reader_entry;
+
+ reader_entry = vreader_list_entry_new(reader);
+ if (reader_entry == NULL) {
+ return VREADER_OUT_OF_MEMORY;
+ }
+ vreader_list_lock();
+ vreader_queue(vreader_list, reader_entry);
+ vreader_list_unlock();
+ vevent_queue_vevent(vevent_new(VEVENT_READER_INSERT, reader, NULL));
+ return VREADER_OK;
+}
+
+
+VReaderStatus
+vreader_remove_reader(VReader *reader)
+{
+ VReaderListEntry *current_entry;
+
+ vreader_list_lock();
+ for (current_entry = vreader_list_get_first(vreader_list); current_entry;
+ current_entry = vreader_list_get_next(current_entry)) {
+ if (current_entry->reader == reader) {
+ break;
+ }
+ }
+ vreader_dequeue(vreader_list, current_entry);
+ vreader_list_unlock();
+ vreader_list_entry_delete(current_entry);
+ vevent_queue_vevent(vevent_new(VEVENT_READER_REMOVE, reader, NULL));
+ return VREADER_OK;
+}
+
+/*
+ * Generate VEVENT_CARD_INSERT or VEVENT_CARD_REMOVE based on vreader
+ * state. Separated from vreader_insert_card to allow replaying events
+ * for a given state.
+ */
+void
+vreader_queue_card_event(VReader *reader)
+{
+ vevent_queue_vevent(vevent_new(
+ reader->card ? VEVENT_CARD_INSERT : VEVENT_CARD_REMOVE, reader,
+ reader->card));
+}
+
+/*
+ * insert/remove a new card. for removal, card == NULL
+ */
+VReaderStatus
+vreader_insert_card(VReader *reader, VCard *card)
+{
+ vreader_lock(reader);
+ if (reader->card) {
+ /* decrement reference count */
+ vcard_free(reader->card);
+ reader->card = NULL;
+ }
+ reader->card = vcard_reference(card);
+ vreader_unlock(reader);
+ vreader_queue_card_event(reader);
+ return VREADER_OK;
+}
+
+/*
+ * initialize all the static reader structures
+ */
+void
+vreader_init(void)
+{
+ vreader_list_init();
+}
+