[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v3 5/7] Add a VCARD_DIRECT implemention to the l
From: |
Marc-André Lureau |
Subject: |
Re: [Qemu-devel] [PATCH v3 5/7] Add a VCARD_DIRECT implemention to the libcacard smartcard support. |
Date: |
Wed, 25 Mar 2015 16:13:56 +0100 |
ACK.
Reviewed-by: Marc-André Lureau <address@hidden>
(the extra symbols should be shaved in a future libcacard update)
On Fri, Mar 13, 2015 at 8:45 PM, Jeremy White <address@hidden> wrote:
> This uses libpcsclite to provide direct communication with a smartcard.
>
> Signed-off-by: Jeremy White <address@hidden>
> ---
> Makefile.objs | 5 +
> libcacard/capcsc.c | 498
> ++++++++++++++++++++++++++++++++++++++++++++++
> libcacard/capcsc.h | 18 ++
> libcacard/card_7816.c | 2 +-
> libcacard/card_7816.h | 3 +
> libcacard/libcacard.syms | 2 +
> 6 files changed, 527 insertions(+), 1 deletion(-)
> create mode 100644 libcacard/capcsc.c
> create mode 100644 libcacard/capcsc.h
>
> diff --git a/Makefile.objs b/Makefile.objs
> index 28999d3..cc31b29 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -32,6 +32,11 @@ libcacard-y += libcacard/card_7816.o
> libcacard-y += libcacard/vcardt.o
> libcacard/vcard_emul_nss.o-cflags := $(NSS_CFLAGS)
> libcacard/vcard_emul_nss.o-libs := $(NSS_LIBS)
> +ifeq ($(CONFIG_SMARTCARD_PCSC),y)
> +libcacard-y += libcacard/capcsc.o
> +libcacard/capcsc.o-cflags := $(PCSC_CFLAGS)
> +libcacard/capcsc.o-libs := $(PCSC_LIBS)
> +endif
>
> ######################################################################
> # Target independent part of system emulation. The long term path is to
> diff --git a/libcacard/capcsc.c b/libcacard/capcsc.c
> new file mode 100644
> index 0000000..a1030df
> --- /dev/null
> +++ b/libcacard/capcsc.c
> @@ -0,0 +1,498 @@
> +/*
> + * Supply a vreader using the PC/SC interface.
> + *
> + * 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 <glib.h>
> +
> +#include "qemu-common.h"
> +
> +#include "vcard.h"
> +#include "card_7816.h"
> +#include "capcsc.h"
> +#include "vreader.h"
> +#include "vevent.h"
> +
> +#include <PCSC/wintypes.h>
> +#include <PCSC/winscard.h>
> +
> +
> +typedef struct _PCSCContext PCSCContext;
> +
> +typedef struct {
> + PCSCContext *context;
> + int index;
> + char *name;
> + DWORD protocol;
> + DWORD state;
> + SCARDHANDLE card;
> + BYTE atr[MAX_ATR_SIZE];
> + DWORD atrlen;
> + int card_connected;
> + unsigned long request_count;
> +} SCardReader;
> +
> +typedef struct _PCSCContext {
> + SCARDCONTEXT context;
> + SCardReader readers[CAPCSC_MAX_READERS];
> + int reader_count;
> + int readers_changed;
> + GThread *thread;
> + CompatGMutex lock;
> +} PCSCContext;
> +
> +
> +static void delete_reader(PCSCContext *pc, int i)
> +{
> + SCardReader *r = &pc->readers[i];
> + g_free(r->name);
> + r->name = NULL;
> +
> + if (i < (pc->reader_count - 1)) {
> + int rem = pc->reader_count - i - 1;
> + memmove(&pc->readers[i], &pc->readers[i + 1],
> + sizeof(SCardReader) * rem);
> + }
> +
> + pc->reader_count--;
> +}
> +
> +static void delete_reader_cb(VReaderEmul *ve)
> +{
> + SCardReader *r = (SCardReader *) ve;
> +
> + g_mutex_lock(&r->context->lock);
> + delete_reader(r->context, r->index);
> + g_mutex_unlock(&r->context->lock);
> +}
> +
> +static int new_reader(PCSCContext *pc, const char *name, DWORD state)
> +{
> + SCardReader *r;
> + VReader *vreader;
> +
> + if (pc->reader_count >= CAPCSC_MAX_READERS - 1) {
> + return 1;
> + }
> +
> + r = &pc->readers[pc->reader_count];
> + memset(r, 0, sizeof(*r));
> + r->index = pc->reader_count++;
> + r->context = pc;
> + r->name = g_strdup(name);
> +
> + vreader = vreader_new(name, (VReaderEmul *) r, delete_reader_cb);
> + vreader_add_reader(vreader);
> + vreader_free(vreader);
> +
> + return 0;
> +}
> +
> +static int find_reader(PCSCContext *pc, const char *name)
> +{
> + int i;
> + for (i = 0; i < pc->reader_count; i++)
> + if (strcmp(pc->readers[i].name, name) == 0) {
> + return i;
> + }
> +
> + return -1;
> +}
> +
> +
> +static int scan_for_readers(PCSCContext *pc)
> +{
> + LONG rc;
> +
> + int i;
> + char buf[8192];
> + DWORD buflen = sizeof(buf);
> +
> + char *p;
> + int matches[CAPCSC_MAX_READERS];
> +
> + g_mutex_lock(&pc->lock);
> +
> + for (i = 0; i < CAPCSC_MAX_READERS; i++) {
> + matches[i] = 0;
> + }
> +
> + pc->readers_changed = 1;
> + memset(buf, 0, sizeof(buf));
> + rc = SCardListReaders(pc->context, NULL, buf, &buflen);
> + if (rc == SCARD_E_NO_READERS_AVAILABLE) {
> + rc = 0;
> + goto exit;
> + }
> +
> + if (rc != SCARD_S_SUCCESS) {
> + fprintf(stderr, "SCardListReaders failed: %s (0x%lX)\n",
> + pcsc_stringify_error(rc), rc);
> + goto exit;
> + }
> +
> + for (p = buf; p && p < buf + sizeof(buf); p += (strlen(p) + 1)) {
> + if (strlen(p) > 0) {
> + i = find_reader(pc, p);
> + if (i >= 0) {
> + matches[i]++;
> + } else {
> + if (!new_reader(pc, p, SCARD_STATE_UNAWARE)) {
> + matches[pc->reader_count - 1]++;
> + }
> + }
> + }
> + }
> +
> + rc = 0;
> +
> +exit:
> + i = pc->reader_count - 1;
> + g_mutex_unlock(&pc->lock);
> +
> + for (; i >= 0; i--) {
> + if (!matches[i]) {
> + VReader *reader =
> vreader_get_reader_by_name(pc->readers[i].name);
> + if (reader) {
> + vreader_free(reader);
> + vreader_remove_reader(reader);
> + }
> + }
> + }
> +
> +
> + return rc;
> +}
> +
> +static int init_pcsc(PCSCContext *pc)
> +{
> + LONG rc;
> +
> + memset(pc, 0, sizeof(*pc));
> +
> + rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &pc->context);
> + if (rc != SCARD_S_SUCCESS) {
> + fprintf(stderr, "SCardEstablishContext: "
> + "Cannot Connect to Resource Manager %lX\n", rc);
> + return rc;
> + }
> +
> + return 0;
> +}
> +
> +
> +static void prepare_reader_states(PCSCContext *pc, SCARD_READERSTATE
> **states,
> + DWORD *reader_count)
> +{
> + SCARD_READERSTATE *state;
> + int i;
> +
> + if (*states) {
> + g_free(*states);
> + }
> +
> + *reader_count = pc->reader_count;
> +
> + (*reader_count)++;
> + *states = g_malloc((*reader_count) * sizeof(**states));
> + memset(*states, 0, sizeof((*reader_count) * sizeof(**states)));
> +
> + for (i = 0, state = *states; i < pc->reader_count; i++, state++) {
> + state->szReader = pc->readers[i].name;
> + state->dwCurrentState = pc->readers[i].state;
> + }
> +
> + /* Leave a space to be notified of new readers */
> + state->szReader = "\\\\?PnP?\\Notification";
> + state->dwCurrentState = SCARD_STATE_UNAWARE;
> +}
> +
> +static int connect_card(SCardReader *r)
> +{
> + LONG rc;
> +
> + r->protocol = -1;
> + rc = SCardConnect(r->context->context, r->name, SCARD_SHARE_SHARED,
> + SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
> + &r->card, &r->protocol);
> + if (rc != SCARD_S_SUCCESS) {
> + fprintf(stderr, "Failed to connect to a card reader: %s (0x%lX)\n",
> + pcsc_stringify_error(rc), rc);
> + return rc;
> + }
> +
> + r->card_connected = 1;
> + r->request_count = 0;
> +
> + return 0;
> +}
> +
> +static LONG send_receive(SCardReader *r, BYTE *transmit, DWORD transmit_len,
> + BYTE *receive, DWORD *receive_len)
> +{
> + const SCARD_IO_REQUEST *send_header;
> + SCARD_IO_REQUEST receive_header;
> + LONG rc;
> +
> + if (!r->card_connected) {
> + rc = connect_card(r);
> + if (rc) {
> + return rc;
> + }
> + }
> +
> + if (r->protocol == SCARD_PROTOCOL_T0) {
> + send_header = SCARD_PCI_T0;
> + } else if (r->protocol == SCARD_PROTOCOL_T1) {
> + send_header = SCARD_PCI_T1;
> + } else {
> + fprintf(stderr, "Unknown protocol %lX\n", r->protocol);
> + return 1;
> + }
> +
> + rc = SCardTransmit(r->card, send_header, transmit, transmit_len,
> + &receive_header, receive, receive_len);
> + if (rc != SCARD_S_SUCCESS) {
> + fprintf(stderr, "Failed to transmit %ld bytes: %s (0x%lX)\n",
> + transmit_len, pcsc_stringify_error(rc), rc);
> + return rc;
> + }
> +
> + return 0;
> +}
> +
> +
> +static VCardStatus apdu_cb(VCard *card, VCardAPDU *apdu,
> + VCardResponse **response)
> +{
> + VCardStatus ret = VCARD_DONE;
> + SCardReader *r = (SCardReader *) vcard_get_private(card);
> + BYTE outbuf[4096];
> + DWORD outlen = sizeof(outbuf);
> + LONG rc;
> +
> + rc = send_receive(r, apdu->a_data, apdu->a_len, outbuf, &outlen);
> + if (rc || outlen < 2) {
> + ret = VCARD_FAIL;
> + } else {
> + *response = vcard_response_new_data(outbuf, outlen - 2);
> + if (*response == NULL) {
> + return VCARD_FAIL;
> + }
> + vcard_response_set_status_bytes(*response, outbuf[outlen - 2],
> + outbuf[outlen - 1]);
> + }
> +
> + return ret;
> +}
> +
> +static VCardStatus reset_cb(VCard *card, int channel)
> +{
> + SCardReader *r = (SCardReader *) vcard_get_private(card);
> + LONG rc;
> +
> + /* vreader_power_on is a bit too free with it's resets.
> + And a reconnect is expensive; as much as 10-20 seconds.
> + Hence, we discard any initial reconnect request. */
> + if (r->request_count++ == 0) {
> + return VCARD_DONE;
> + }
> +
> + rc = SCardReconnect(r->card, SCARD_SHARE_SHARED,
> + SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
> + SCARD_RESET_CARD, &r->protocol);
> + if (rc != SCARD_S_SUCCESS) {
> + fprintf(stderr, "Failed to reconnect to a card reader: %s (0x%lX)\n",
> + pcsc_stringify_error(rc), rc);
> + return VCARD_FAIL;
> + }
> + return VCARD_DONE;
> +}
> +
> +static void get_atr_cb(VCard *card, unsigned char *atr, int *atr_len)
> +{
> + SCardReader *r = (SCardReader *) vcard_get_private(card);
> + *atr_len = r->atrlen;
> + if (atr) {
> + memcpy(atr, r->atr, r->atrlen);
> + }
> +}
> +
> +static void delete_card_cb(VCardEmul *ve)
> +{
> + fprintf(stderr, "TODO, got a delete_card_cb\n");
> +}
> +
> +static void insert_card(SCardReader *r, SCARD_READERSTATE *s)
> +{
> + memcpy(r->atr, s->rgbAtr, MIN(sizeof(r->atr), sizeof(s->rgbAtr)));
> + r->atrlen = s->cbAtr;
> +
> + VReader *reader = vreader_get_reader_by_name(r->name);
> + if (!reader) {
> + return;
> + }
> +
> + if (connect_card(r)) {
> + return;
> + }
> +
> + VCardApplet *applet =
> + vcard_new_applet(apdu_cb,
> + reset_cb,
> + (const unsigned char *) CAPCSC_APPLET,
> + strlen(CAPCSC_APPLET));
> + if (!applet) {
> + return;
> + }
> +
> + VCard * card = vcard_new((VCardEmul *) r, delete_card_cb);
> + if (!card) {
> + vcard_delete_applet(applet);
> + vreader_free(reader);
> + return;
> + }
> +
> + vcard_set_type(card, VCARD_DIRECT);
> + vcard_set_atr_func(card, get_atr_cb);
> + vcard_add_applet(card, applet);
> +
> + vreader_insert_card(reader, card);
> + vreader_free(reader);
> +}
> +
> +static void remove_card(SCardReader *r)
> +{
> + LONG rc;
> + memset(r->atr, 0, sizeof(r->atr));
> + r->atrlen = 0;
> +
> + rc = SCardDisconnect(r->card, SCARD_LEAVE_CARD);
> + if (rc != SCARD_S_SUCCESS) {
> + fprintf(stderr, "Non fatal info:"
> + "failed to disconnect card reader: %s (0x%lX)\n",
> + pcsc_stringify_error(rc), rc);
> + }
> + r->card_connected = 0;
> +
> + VReader *reader = vreader_get_reader_by_name(r->name);
> + if (!reader) {
> + return;
> + }
> +
> + vreader_insert_card(reader, NULL);
> + vreader_free(reader);
> +}
> +
> +static void process_reader_change(SCardReader *r, SCARD_READERSTATE *s)
> +{
> + if (s->dwEventState & SCARD_STATE_PRESENT) {
> + insert_card(r, s);
> + } else if (s->dwEventState & SCARD_STATE_EMPTY) {
> + remove_card(r);
> + } else {
> + fprintf(stderr, "Unexpected card state change from %lx to %lx:\n",
> + r->state, s->dwEventState);
> + }
> +
> + r->state = s->dwEventState & ~SCARD_STATE_CHANGED;
> +}
> +
> +/*
> + * This thread looks for card and reader insertions and puts events on the
> + * event queue.
> + */
> +static gpointer event_thread(gpointer arg)
> +{
> + PCSCContext *pc = (PCSCContext *) arg;
> + DWORD reader_count = 0;
> + SCARD_READERSTATE *reader_states = NULL;
> + LONG rc;
> +
> + scan_for_readers(pc);
> +
> + do {
> + int i;
> + DWORD timeout = INFINITE;
> +
> + g_mutex_lock(&pc->lock);
> + if (pc->readers_changed) {
> + prepare_reader_states(pc, &reader_states, &reader_count);
> + timeout = 0;
> + } else if (reader_count > 1) {
> + timeout = 0;
> + }
> +
> + pc->readers_changed = 0;
> + g_mutex_unlock(&pc->lock);
> +
> + rc = SCardGetStatusChange(pc->context, timeout, reader_states,
> + reader_count);
> +
> + /* If we have a new reader, or an unknown reader,
> + rescan and go back and do it again */
> + if ((rc == SCARD_S_SUCCESS && (reader_states[reader_count -
> 1].dwEventState & SCARD_STATE_CHANGED))
> + ||
> + rc == SCARD_E_UNKNOWN_READER) {
> + scan_for_readers(pc);
> + continue;
> + }
> +
> + if (rc != SCARD_S_SUCCESS && rc != SCARD_E_TIMEOUT) {
> + fprintf(stderr, "Unexpected SCardGetStatusChange ret %lx(%s)\n",
> + rc, pcsc_stringify_error(rc));
> + continue;
> + }
> +
> + g_mutex_lock(&pc->lock);
> + for (i = 0; i < reader_count; i++) {
> + if (reader_states[i].dwEventState & SCARD_STATE_CHANGED) {
> + process_reader_change(&pc->readers[i], &reader_states[i]);
> + pc->readers_changed++;
> + }
> +
> + }
> + g_mutex_unlock(&pc->lock);
> +
> + /* libpcsclite is only thread safe at a high level. If we constantly
> + hold long calls into SCardGetStatusChange, we'll starve any
> running
> + clients. So, if we have an active session, and nothing has
> changed
> + on our front, we just idle. */
> + if (!pc->readers_changed && reader_count > 1) {
> + usleep(CAPCSC_POLL_TIME * 1000);
> + }
> +
> +
> + } while (1);
> +
> + return NULL;
> +}
> +
> +/*
> + * We poll the PC/SC interface, looking for device changes
> + */
> +static int new_event_thread(PCSCContext *pc)
> +{
> + pc->thread = g_thread_new("capcsc_event_thread", event_thread, pc);
> + return pc->thread == NULL;
> +}
> +
> +
> +static PCSCContext context;
> +
> +int capcsc_init(void)
> +{
> + g_mutex_init(&context.lock);
> +
> + if (init_pcsc(&context)) {
> + return -1;
> + }
> +
> + if (new_event_thread(&context)) {
> + return -1;
> + }
> +
> + return 0;
> +}
> diff --git a/libcacard/capcsc.h b/libcacard/capcsc.h
> new file mode 100644
> index 0000000..bb59a4e
> --- /dev/null
> +++ b/libcacard/capcsc.h
> @@ -0,0 +1,18 @@
> +/*
> + * 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.
> + */
> +#ifndef CAPCSC_H
> +#define CAPCSC_H 1
> +
> +#define CAPCSC_POLL_TIME 50 /* ms - Time we will poll for */
> + /* card change when a */
> + /* reader is connected */
> +#define CAPCSC_MAX_READERS 16
> +
> +#define CAPCSC_APPLET "CAPCSC APPLET"
> +
> +int capcsc_init(void);
> +
> +
> +#endif
> diff --git a/libcacard/card_7816.c b/libcacard/card_7816.c
> index 814fa16..d84d26b 100644
> --- a/libcacard/card_7816.c
> +++ b/libcacard/card_7816.c
> @@ -31,7 +31,7 @@ vcard_response_set_status(VCardResponse *response,
> vcard_7816_status_t status)
> /*
> * set the status bytes in a response buffer
> */
> -static void
> +void
> vcard_response_set_status_bytes(VCardResponse *response,
> unsigned char sw1, unsigned char sw2)
> {
> diff --git a/libcacard/card_7816.h b/libcacard/card_7816.h
> index 4a01993..c6b3a6b 100644
> --- a/libcacard/card_7816.h
> +++ b/libcacard/card_7816.h
> @@ -31,6 +31,9 @@ VCardResponse *vcard_make_response(vcard_7816_status_t
> status);
> /* create a raw response (status has already been encoded */
> VCardResponse *vcard_response_new_data(unsigned char *buf, int len);
>
> +void
> +vcard_response_set_status_bytes(VCardResponse *response,
> + unsigned char sw1, unsigned char sw2);
>
>
>
> diff --git a/libcacard/libcacard.syms b/libcacard/libcacard.syms
> index 1697515..e17158f 100644
> --- a/libcacard/libcacard.syms
> +++ b/libcacard/libcacard.syms
> @@ -1,5 +1,6 @@
> cac_card_init
> cac_is_cac_card
> +capcsc_init
> vcard_add_applet
> vcard_apdu_delete
> vcard_apdu_new
> @@ -41,6 +42,7 @@ vcard_response_new
> vcard_response_new_bytes
> vcard_response_new_data
> vcard_response_new_status_bytes
> +vcard_response_set_status_bytes
> vcard_select_applet
> vcard_set_applet_private
> vcard_set_atr_func
> --
> 1.7.10.4
>
>
--
Marc-André Lureau
- [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard, Jeremy White, 2015/03/13
- [Qemu-devel] [PATCH v3 6/7] Enable support for passthru (e.g. direct to pcsc) smart cards in the emul_options entry point in libcacard., Jeremy White, 2015/03/13
- [Qemu-devel] [PATCH v3 3/7] Add a configure check for libpcsclite, and an option to enable or disable it., Jeremy White, 2015/03/13
- [Qemu-devel] [PATCH v3 4/7] Add error checking to vcard_emul_options., Jeremy White, 2015/03/13
- [Qemu-devel] [PATCH v3 5/7] Add a VCARD_DIRECT implemention to the libcacard smartcard support., Jeremy White, 2015/03/13
- Re: [Qemu-devel] [PATCH v3 5/7] Add a VCARD_DIRECT implemention to the libcacard smartcard support.,
Marc-André Lureau <=
- [Qemu-devel] [PATCH v3 7/7] Remove the (broken) passthru option., Jeremy White, 2015/03/13
- [Qemu-devel] [PATCH v3 1/7] Bug fix: delete the reader entry after queueing an event, not before., Jeremy White, 2015/03/13
- [Qemu-devel] [PATCH v3 2/7] Retrieve the correct TD byte when checking an ATR., Jeremy White, 2015/03/13
- Re: [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard, Patchew Tool, 2015/03/13
- Re: [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard, Marc-André Lureau, 2015/03/16