mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-03 07:43:54 -06:00
libcacard: initial commit
libcacard emulates a Common Access Card (CAC) which is a standard for smartcards. It is used by the emulated ccid card introduced in a following patch. Docs are available in docs/libcacard.txt Signed-off-by: Alon Levy <alevy@redhat.com> --- changes from v24->v25: * Fix out of tree builds. * Fix build with linux-user targets. changes from v23->v24: (Jes Sorensen review 2) * Makefile.target: use obj-$(CONFIG_*) += * remove unrequired includes, include qemu-common before qemu-thread * required adding #define NO_NSPR_10_SUPPORT (harmless) changes from v22->v23: * configure fixes: (reported by Stefan Hajnoczi) * test a = b, not a == b (second isn't portable) * quote $source_path in case it contains spaces - this doesn't really help since there are many other places that need similar fixes, not introduced by this patch. changes from v21->v22: * fix configure to not link libcacard if nss not found (reported by Stefan Hajnoczi) * fix vscclient linkage with simpletrace backend (reported by Stefan Hajnoczi) * card_7816.c: add missing break in ERROR_DATA_NOT_FOUND (reported by William van de Velde) changes from v20->v21: (Jes Sorensen review) * use qemu infrastructure: qemu-thread, qemu-common (qemu_malloc and qemu_free), error_report * assert instead of ASSERT * cosmetic fixes * use strpbrk and isspace * add --disable-nss --enable-nss here, instead of in the final patch. * split vscclient, passthru and docs to following patches. changes from v19->v20: * checkpatch.pl changes from v15->v16: Build: * don't erase self with distclean * fix make clean after make distclean * Makefile: make vscclient link quiet Behavioral: * vcard_emul_nss: load coolkey in more situations * vscclient: * use hton,ntoh * send init on connect, only start vevent thread on response * read payload after header check, before type switch * remove Reconnect * update for vscard_common changes, empty Flush implementation Style/Whitespace: * fix wrong variable usage * remove unused variable * use only C style comments * add copyright header * fix tabulation Signed-off-by: Alon Levy <alevy@redhat.com> libcacard: fix out of tree builds
This commit is contained in:
parent
edbb21363f
commit
111a38b018
24 changed files with 4076 additions and 2 deletions
513
libcacard/vreader.c
Normal file
513
libcacard/vreader.c
Normal file
|
@ -0,0 +1,513 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu-thread.h"
|
||||
|
||||
#include "vcard.h"
|
||||
#include "vcard_emul.h"
|
||||
#include "card_7816.h"
|
||||
#include "vreader.h"
|
||||
#include "vevent.h"
|
||||
|
||||
struct VReaderStruct {
|
||||
int reference_count;
|
||||
VCard *card;
|
||||
char *name;
|
||||
vreader_id_t id;
|
||||
QemuMutex lock;
|
||||
VReaderEmul *reader_private;
|
||||
VReaderEmulFree reader_private_free;
|
||||
};
|
||||
|
||||
/* manage locking */
|
||||
static inline void
|
||||
vreader_lock(VReader *reader)
|
||||
{
|
||||
qemu_mutex_lock(&reader->lock);
|
||||
}
|
||||
|
||||
static inline void
|
||||
vreader_unlock(VReader *reader)
|
||||
{
|
||||
qemu_mutex_unlock(&reader->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* vreader constructor
|
||||
*/
|
||||
VReader *
|
||||
vreader_new(const char *name, VReaderEmul *private,
|
||||
VReaderEmulFree private_free)
|
||||
{
|
||||
VReader *reader;
|
||||
|
||||
reader = (VReader *)qemu_malloc(sizeof(VReader));
|
||||
qemu_mutex_init(&reader->lock);
|
||||
reader->reference_count = 1;
|
||||
reader->name = name ? strdup(name) : NULL;
|
||||
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);
|
||||
if (reader->card) {
|
||||
vcard_free(reader->card);
|
||||
}
|
||||
if (reader->name) {
|
||||
qemu_free(reader->name);
|
||||
}
|
||||
if (reader->reader_private_free) {
|
||||
reader->reader_private_free(reader->reader_private);
|
||||
}
|
||||
qemu_free(reader);
|
||||
return;
|
||||
}
|
||||
|
||||
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 {
|
||||
card_status = vcard_process_apdu(card, apdu, &response);
|
||||
}
|
||||
assert(card_status == VCARD_DONE);
|
||||
if (card_status == VCARD_DONE) {
|
||||
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 = (VReaderListEntry *)
|
||||
qemu_malloc(sizeof(VReaderListEntry));
|
||||
new_reader_list_entry->next = NULL;
|
||||
new_reader_list_entry->prev = NULL;
|
||||
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);
|
||||
qemu_free(entry);
|
||||
}
|
||||
|
||||
|
||||
static VReaderList *
|
||||
vreader_list_new(void)
|
||||
{
|
||||
VReaderList *new_reader_list;
|
||||
|
||||
new_reader_list = (VReaderList *)qemu_malloc(sizeof(VReaderList));
|
||||
new_reader_list->head = NULL;
|
||||
new_reader_list->tail = NULL;
|
||||
return new_reader_list;
|
||||
}
|
||||
|
||||
void
|
||||
vreader_list_delete(VReaderList *list)
|
||||
{
|
||||
VReaderListEntry *current_entry;
|
||||
VReaderListEntry *next_entry = NULL;
|
||||
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);
|
||||
}
|
||||
list->head = NULL;
|
||||
list->tail = NULL;
|
||||
qemu_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 QemuMutex vreader_list_mutex;
|
||||
|
||||
static void
|
||||
vreader_list_init(void)
|
||||
{
|
||||
vreader_list = vreader_list_new();
|
||||
qemu_mutex_init(&vreader_list_mutex);
|
||||
}
|
||||
|
||||
static void
|
||||
vreader_list_lock(void)
|
||||
{
|
||||
qemu_mutex_lock(&vreader_list_mutex);
|
||||
}
|
||||
|
||||
static void
|
||||
vreader_list_unlock(void)
|
||||
{
|
||||
qemu_mutex_unlock(&vreader_list_mutex);
|
||||
}
|
||||
|
||||
static VReaderList *
|
||||
vreader_copy_list(VReaderList *list)
|
||||
{
|
||||
VReaderList *new_list = NULL;
|
||||
VReaderListEntry *current_entry = NULL;
|
||||
|
||||
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 = NULL;
|
||||
|
||||
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 = 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 (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();
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue