Blame src/capcsc.c

Packit cfa437
/*
Packit cfa437
 * Supply a vreader using the PC/SC interface.
Packit cfa437
 *
Packit cfa437
 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
Packit cfa437
 * See the COPYING.LIB file in the top-level directory.
Packit cfa437
 */
Packit cfa437
Packit cfa437
#include "glib-compat.h"
Packit cfa437
#include <string.h>
Packit cfa437
#include <stdio.h>
Packit cfa437
Packit cfa437
#include "vcard.h"
Packit cfa437
#include "card_7816.h"
Packit cfa437
#include "capcsc.h"
Packit cfa437
#include "vreader.h"
Packit cfa437
#include "vevent.h"
Packit cfa437
Packit cfa437
#include <PCSC/wintypes.h>
Packit cfa437
#include <PCSC/winscard.h>
Packit cfa437
Packit cfa437
Packit cfa437
typedef struct _PCSCContext PCSCContext;
Packit cfa437
Packit cfa437
typedef struct {
Packit cfa437
    PCSCContext *context;
Packit cfa437
    int index;
Packit cfa437
    char *name;
Packit cfa437
    DWORD protocol;
Packit cfa437
    DWORD state;
Packit cfa437
    SCARDHANDLE card;
Packit cfa437
    BYTE atr[MAX_ATR_SIZE];
Packit cfa437
    DWORD atrlen;
Packit cfa437
    int card_connected;
Packit cfa437
    unsigned long request_count;
Packit cfa437
} SCardReader;
Packit cfa437
Packit cfa437
typedef struct _PCSCContext {
Packit cfa437
    SCARDCONTEXT context;
Packit cfa437
    SCardReader readers[CAPCSC_MAX_READERS];
Packit cfa437
    int reader_count;
Packit cfa437
    int readers_changed;
Packit cfa437
    GThread *thread;
Packit cfa437
    CompatGMutex lock;
Packit cfa437
} PCSCContext;
Packit cfa437
Packit cfa437
Packit cfa437
static void delete_reader(PCSCContext *pc, int i)
Packit cfa437
{
Packit cfa437
    SCardReader *r = &pc->readers[i];
Packit cfa437
    g_free(r->name);
Packit cfa437
    r->name = NULL;
Packit cfa437
Packit cfa437
    if (i < (pc->reader_count - 1)) {
Packit cfa437
        int rem = pc->reader_count - i - 1;
Packit cfa437
        memmove(&pc->readers[i], &pc->readers[i + 1],
Packit cfa437
                sizeof(SCardReader) * rem);
Packit cfa437
    }
Packit cfa437
Packit cfa437
    pc->reader_count--;
Packit cfa437
}
Packit cfa437
Packit cfa437
static void delete_reader_cb(VReaderEmul *ve)
Packit cfa437
{
Packit cfa437
    SCardReader *r = (SCardReader *) ve;
Packit cfa437
Packit cfa437
    g_mutex_lock(&r->context->lock);
Packit cfa437
    delete_reader(r->context, r->index);
Packit cfa437
    g_mutex_unlock(&r->context->lock);
Packit cfa437
}
Packit cfa437
Packit cfa437
static int new_reader(PCSCContext *pc, const char *name, DWORD state)
Packit cfa437
{
Packit cfa437
    SCardReader *r;
Packit cfa437
    VReader *vreader;
Packit cfa437
Packit cfa437
    if (pc->reader_count >= CAPCSC_MAX_READERS - 1) {
Packit cfa437
        return 1;
Packit cfa437
    }
Packit cfa437
Packit cfa437
    r = &pc->readers[pc->reader_count];
Packit cfa437
    memset(r, 0, sizeof(*r));
Packit cfa437
    r->index = pc->reader_count++;
Packit cfa437
    r->context = pc;
Packit cfa437
    r->name = g_strdup(name);
Packit cfa437
Packit cfa437
    vreader = vreader_new(name, (VReaderEmul *) r, delete_reader_cb);
Packit cfa437
    vreader_add_reader(vreader);
Packit cfa437
    vreader_free(vreader);
Packit cfa437
Packit cfa437
    return 0;
Packit cfa437
}
Packit cfa437
Packit cfa437
static int find_reader(PCSCContext *pc, const char *name)
Packit cfa437
{
Packit cfa437
    int i;
Packit cfa437
    for (i = 0; i < pc->reader_count; i++)
Packit cfa437
        if (strcmp(pc->readers[i].name, name) == 0) {
Packit cfa437
            return i;
Packit cfa437
        }
Packit cfa437
Packit cfa437
    return -1;
Packit cfa437
}
Packit cfa437
Packit cfa437
Packit cfa437
static int scan_for_readers(PCSCContext *pc)
Packit cfa437
{
Packit cfa437
    LONG rc;
Packit cfa437
Packit cfa437
    int i;
Packit cfa437
    char buf[8192];
Packit cfa437
    DWORD buflen = sizeof(buf);
Packit cfa437
Packit cfa437
    char *p;
Packit cfa437
    int matches[CAPCSC_MAX_READERS] = { 0, };
Packit cfa437
Packit cfa437
    g_mutex_lock(&pc->lock);
Packit cfa437
Packit cfa437
    pc->readers_changed = 1;
Packit cfa437
    memset(buf, 0, sizeof(buf));
Packit cfa437
    rc = SCardListReaders(pc->context, NULL, buf, &buflen);
Packit cfa437
    if (rc == SCARD_E_NO_READERS_AVAILABLE) {
Packit cfa437
        rc = 0;
Packit cfa437
        goto exit;
Packit cfa437
    }
Packit cfa437
Packit cfa437
    if (rc != SCARD_S_SUCCESS) {
Packit cfa437
        fprintf(stderr, "SCardListReaders failed: %s (0x%lX)\n",
Packit cfa437
            pcsc_stringify_error(rc), rc);
Packit cfa437
        goto exit;
Packit cfa437
    }
Packit cfa437
Packit cfa437
    for (p = buf; p && p < buf + sizeof(buf); p += (strlen(p) + 1)) {
Packit cfa437
        if (strlen(p) > 0) {
Packit cfa437
            i = find_reader(pc, p);
Packit cfa437
            if (i >= 0) {
Packit cfa437
                matches[i]++;
Packit cfa437
            } else {
Packit cfa437
                if (!new_reader(pc, p, SCARD_STATE_UNAWARE)) {
Packit cfa437
                    matches[pc->reader_count - 1]++;
Packit cfa437
                }
Packit cfa437
            }
Packit cfa437
        }
Packit cfa437
    }
Packit cfa437
Packit cfa437
    rc = 0;
Packit cfa437
Packit cfa437
exit:
Packit cfa437
    i = pc->reader_count - 1;
Packit cfa437
    g_mutex_unlock(&pc->lock);
Packit cfa437
Packit cfa437
    for (; i >= 0; i--) {
Packit cfa437
        if (!matches[i]) {
Packit cfa437
            VReader *reader = vreader_get_reader_by_name(pc->readers[i].name);
Packit cfa437
            if (reader) {
Packit cfa437
                vreader_free(reader);
Packit cfa437
                vreader_remove_reader(reader);
Packit cfa437
            }
Packit cfa437
        }
Packit cfa437
    }
Packit cfa437
Packit cfa437
Packit cfa437
    return rc;
Packit cfa437
}
Packit cfa437
Packit cfa437
static int init_pcsc(PCSCContext *pc)
Packit cfa437
{
Packit cfa437
    LONG rc;
Packit cfa437
Packit cfa437
    memset(pc, 0, sizeof(*pc));
Packit cfa437
Packit cfa437
    rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &pc->context);
Packit cfa437
    if (rc != SCARD_S_SUCCESS) {
Packit cfa437
        fprintf(stderr, "SCardEstablishContext: "
Packit cfa437
                        "Cannot Connect to Resource Manager %lX\n", rc);
Packit cfa437
        return rc;
Packit cfa437
    }
Packit cfa437
Packit cfa437
    return 0;
Packit cfa437
}
Packit cfa437
Packit cfa437
Packit cfa437
static void prepare_reader_states(PCSCContext *pc, SCARD_READERSTATE **states,
Packit cfa437
                                  DWORD *reader_count)
Packit cfa437
{
Packit cfa437
    SCARD_READERSTATE *state;
Packit cfa437
    int i;
Packit cfa437
Packit cfa437
    if (*states) {
Packit cfa437
        g_free(*states);
Packit cfa437
    }
Packit cfa437
Packit cfa437
    *reader_count = pc->reader_count;
Packit cfa437
Packit cfa437
    (*reader_count)++;
Packit cfa437
    *states = g_malloc((*reader_count) * sizeof(**states));
Packit cfa437
    memset(*states, 0, sizeof((*reader_count) * sizeof(**states)));
Packit cfa437
Packit cfa437
    for (i = 0, state = *states; i < pc->reader_count; i++, state++) {
Packit cfa437
        state->szReader = pc->readers[i].name;
Packit cfa437
        state->dwCurrentState = pc->readers[i].state;
Packit cfa437
    }
Packit cfa437
Packit cfa437
    /* Leave a space to be notified of new readers */
Packit cfa437
    state->szReader = "\\\\?PnP?\\Notification";
Packit cfa437
    state->dwCurrentState = SCARD_STATE_UNAWARE;
Packit cfa437
}
Packit cfa437
Packit cfa437
static int connect_card(SCardReader *r)
Packit cfa437
{
Packit cfa437
    LONG rc;
Packit cfa437
Packit cfa437
    r->protocol = -1;
Packit cfa437
    rc = SCardConnect(r->context->context, r->name, SCARD_SHARE_SHARED,
Packit cfa437
                        SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
Packit cfa437
                        &r->card, &r->protocol);
Packit cfa437
    if (rc != SCARD_S_SUCCESS) {
Packit cfa437
        fprintf(stderr, "Failed to connect to a card reader: %s (0x%lX)\n",
Packit cfa437
            pcsc_stringify_error(rc), rc);
Packit cfa437
        return rc;
Packit cfa437
    }
Packit cfa437
Packit cfa437
    r->card_connected = 1;
Packit cfa437
    r->request_count = 0;
Packit cfa437
Packit cfa437
    return 0;
Packit cfa437
}
Packit cfa437
Packit cfa437
static LONG send_receive(SCardReader *r, BYTE *transmit, DWORD transmit_len,
Packit cfa437
                 BYTE *receive, DWORD *receive_len)
Packit cfa437
{
Packit cfa437
    const SCARD_IO_REQUEST *send_header;
Packit cfa437
    SCARD_IO_REQUEST receive_header;
Packit cfa437
    LONG rc;
Packit cfa437
Packit cfa437
    if (!r->card_connected) {
Packit cfa437
        rc = connect_card(r);
Packit cfa437
        if (rc) {
Packit cfa437
            return rc;
Packit cfa437
        }
Packit cfa437
    }
Packit cfa437
Packit cfa437
    if (r->protocol == SCARD_PROTOCOL_T0) {
Packit cfa437
        send_header = SCARD_PCI_T0;
Packit cfa437
    } else if (r->protocol == SCARD_PROTOCOL_T1) {
Packit cfa437
        send_header = SCARD_PCI_T1;
Packit cfa437
    } else {
Packit cfa437
        fprintf(stderr, "Unknown protocol %lX\n", r->protocol);
Packit cfa437
        return 1;
Packit cfa437
    }
Packit cfa437
Packit cfa437
    rc = SCardTransmit(r->card, send_header, transmit, transmit_len,
Packit cfa437
                        &receive_header, receive, receive_len);
Packit cfa437
    if (rc != SCARD_S_SUCCESS) {
Packit cfa437
        fprintf(stderr, "Failed to transmit %ld bytes: %s (0x%lX)\n",
Packit cfa437
            transmit_len, pcsc_stringify_error(rc), rc);
Packit cfa437
        return rc;
Packit cfa437
    }
Packit cfa437
Packit cfa437
    return 0;
Packit cfa437
}
Packit cfa437
Packit cfa437
Packit cfa437
static VCardStatus apdu_cb(VCard *card, VCardAPDU *apdu,
Packit cfa437
                           VCardResponse **response)
Packit cfa437
{
Packit cfa437
    VCardStatus ret = VCARD_DONE;
Packit cfa437
    SCardReader *r = (SCardReader *) vcard_get_private(card);
Packit cfa437
    BYTE outbuf[4096];
Packit cfa437
    DWORD outlen = sizeof(outbuf);
Packit cfa437
    LONG rc;
Packit cfa437
Packit cfa437
    rc = send_receive(r, apdu->a_data, apdu->a_len, outbuf, &outlen);
Packit cfa437
    if (rc || outlen < 2) {
Packit cfa437
        ret = VCARD_FAIL;
Packit cfa437
    } else {
Packit cfa437
        *response = vcard_response_new_data(outbuf, outlen - 2);
Packit cfa437
        if (*response == NULL) {
Packit cfa437
            return VCARD_FAIL;
Packit cfa437
        }
Packit cfa437
        vcard_response_set_status_bytes(*response, outbuf[outlen - 2],
Packit cfa437
                                                   outbuf[outlen - 1]);
Packit cfa437
    }
Packit cfa437
Packit cfa437
    return ret;
Packit cfa437
}
Packit cfa437
Packit cfa437
static VCardStatus reset_cb(VCard *card, int channel)
Packit cfa437
{
Packit cfa437
    SCardReader *r = (SCardReader *) vcard_get_private(card);
Packit cfa437
    LONG rc;
Packit cfa437
Packit cfa437
    /* vreader_power_on is a bit too free with it's resets.
Packit cfa437
       And a reconnect is expensive; as much as 10-20 seconds.
Packit cfa437
       Hence, we discard any initial reconnect request. */
Packit cfa437
    if (r->request_count++ == 0) {
Packit cfa437
        return VCARD_DONE;
Packit cfa437
    }
Packit cfa437
Packit cfa437
    rc = SCardReconnect(r->card, SCARD_SHARE_SHARED,
Packit cfa437
                        SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
Packit cfa437
                        SCARD_RESET_CARD, &r->protocol);
Packit cfa437
    if (rc != SCARD_S_SUCCESS) {
Packit cfa437
        fprintf(stderr, "Failed to reconnect to a card reader: %s (0x%lX)\n",
Packit cfa437
            pcsc_stringify_error(rc), rc);
Packit cfa437
        return VCARD_FAIL;
Packit cfa437
    }
Packit cfa437
    return VCARD_DONE;
Packit cfa437
}
Packit cfa437
Packit cfa437
static void get_atr_cb(VCard *card, unsigned char *atr, int *atr_len)
Packit cfa437
{
Packit cfa437
    SCardReader *r = (SCardReader *) vcard_get_private(card);
Packit cfa437
    *atr_len = r->atrlen;
Packit cfa437
    if (atr) {
Packit cfa437
        memcpy(atr, r->atr, r->atrlen);
Packit cfa437
    }
Packit cfa437
}
Packit cfa437
Packit cfa437
static void delete_card_cb(VCardEmul *ve)
Packit cfa437
{
Packit cfa437
    fprintf(stderr, "TODO, got a delete_card_cb\n");
Packit cfa437
}
Packit cfa437
Packit cfa437
static void insert_card(SCardReader *r, SCARD_READERSTATE *s)
Packit cfa437
{
Packit cfa437
    VReader *reader;
Packit cfa437
    VCardApplet *applet;
Packit cfa437
    VCard *card;
Packit cfa437
Packit cfa437
    memcpy(r->atr, s->rgbAtr, MIN(sizeof(r->atr), sizeof(s->rgbAtr)));
Packit cfa437
    r->atrlen = s->cbAtr;
Packit cfa437
Packit cfa437
    reader = vreader_get_reader_by_name(r->name);
Packit cfa437
    if (!reader) {
Packit cfa437
        return;
Packit cfa437
    }
Packit cfa437
Packit cfa437
    if (connect_card(r)) {
Packit cfa437
        return;
Packit cfa437
    }
Packit cfa437
Packit cfa437
    applet =
Packit cfa437
        vcard_new_applet(apdu_cb,
Packit cfa437
                         reset_cb,
Packit cfa437
                         (const unsigned char *)CAPCSC_APPLET,
Packit cfa437
                         strlen(CAPCSC_APPLET));
Packit cfa437
    if (!applet) {
Packit cfa437
        return;
Packit cfa437
    }
Packit cfa437
Packit cfa437
    card = vcard_new((VCardEmul *) r, delete_card_cb);
Packit cfa437
    if (!card) {
Packit cfa437
        vcard_delete_applet(applet);
Packit cfa437
        vreader_free(reader);
Packit cfa437
        return;
Packit cfa437
    }
Packit cfa437
Packit cfa437
    vcard_set_type(card, VCARD_DIRECT);
Packit cfa437
    vcard_set_atr_func(card, get_atr_cb);
Packit cfa437
    vcard_add_applet(card, applet);
Packit cfa437
Packit cfa437
    vreader_insert_card(reader, card);
Packit cfa437
    vreader_free(reader);
Packit cfa437
}
Packit cfa437
Packit cfa437
static void remove_card(SCardReader *r)
Packit cfa437
{
Packit cfa437
    LONG rc;
Packit cfa437
    VReader *reader;
Packit cfa437
Packit cfa437
    memset(r->atr, 0, sizeof(r->atr));
Packit cfa437
    r->atrlen = 0;
Packit cfa437
Packit cfa437
    rc = SCardDisconnect(r->card, SCARD_LEAVE_CARD);
Packit cfa437
    if (rc != SCARD_S_SUCCESS) {
Packit cfa437
        fprintf(stderr, "Non fatal info:"
Packit cfa437
                        "failed to disconnect card reader: %s (0x%lX)\n",
Packit cfa437
            pcsc_stringify_error(rc), rc);
Packit cfa437
    }
Packit cfa437
    r->card_connected = 0;
Packit cfa437
Packit cfa437
    reader = vreader_get_reader_by_name(r->name);
Packit cfa437
    if (!reader) {
Packit cfa437
        return;
Packit cfa437
    }
Packit cfa437
Packit cfa437
    vreader_insert_card(reader, NULL);
Packit cfa437
    vreader_free(reader);
Packit cfa437
}
Packit cfa437
Packit cfa437
static void process_reader_change(SCardReader *r, SCARD_READERSTATE *s)
Packit cfa437
{
Packit cfa437
    if (s->dwEventState & SCARD_STATE_PRESENT) {
Packit cfa437
        insert_card(r, s);
Packit cfa437
    } else if (s->dwEventState & SCARD_STATE_EMPTY) {
Packit cfa437
        remove_card(r);
Packit cfa437
    } else {
Packit cfa437
        fprintf(stderr, "Unexpected card state change from %lx to %lx:\n",
Packit cfa437
                        r->state, s->dwEventState);
Packit cfa437
    }
Packit cfa437
Packit cfa437
    r->state = s->dwEventState & ~SCARD_STATE_CHANGED;
Packit cfa437
}
Packit cfa437
Packit cfa437
/*
Packit cfa437
 * This thread looks for card and reader insertions and puts events on the
Packit cfa437
 * event queue.
Packit cfa437
 */
Packit cfa437
static gpointer event_thread(gpointer arg)
Packit cfa437
{
Packit cfa437
    PCSCContext *pc = (PCSCContext *) arg;
Packit cfa437
    DWORD reader_count = 0;
Packit cfa437
    SCARD_READERSTATE *reader_states = NULL;
Packit cfa437
    LONG rc;
Packit cfa437
Packit cfa437
    scan_for_readers(pc);
Packit cfa437
Packit cfa437
    do {
Packit cfa437
        DWORD i;
Packit cfa437
        DWORD timeout = INFINITE;
Packit cfa437
Packit cfa437
        g_mutex_lock(&pc->lock);
Packit cfa437
        if (pc->readers_changed) {
Packit cfa437
            prepare_reader_states(pc, &reader_states, &reader_count);
Packit cfa437
            timeout = 0;
Packit cfa437
        } else if (reader_count > 1) {
Packit cfa437
            timeout = 0;
Packit cfa437
        }
Packit cfa437
Packit cfa437
        pc->readers_changed = 0;
Packit cfa437
        g_mutex_unlock(&pc->lock);
Packit cfa437
Packit cfa437
        rc = SCardGetStatusChange(pc->context, timeout, reader_states,
Packit cfa437
                                  reader_count);
Packit cfa437
Packit cfa437
        /* If we have a new reader, or an unknown reader,
Packit cfa437
           rescan and go back and do it again */
Packit cfa437
        if ((rc == SCARD_S_SUCCESS && (reader_states[reader_count - 1].dwEventState & SCARD_STATE_CHANGED))
Packit cfa437
                      ||
Packit cfa437
             rc == SCARD_E_UNKNOWN_READER) {
Packit cfa437
            scan_for_readers(pc);
Packit cfa437
            continue;
Packit cfa437
        }
Packit cfa437
Packit cfa437
        if (rc != SCARD_S_SUCCESS && rc != SCARD_E_TIMEOUT) {
Packit cfa437
            fprintf(stderr, "Unexpected SCardGetStatusChange ret %lx(%s)\n",
Packit cfa437
                            rc, pcsc_stringify_error(rc));
Packit cfa437
            continue;
Packit cfa437
        }
Packit cfa437
Packit cfa437
        g_mutex_lock(&pc->lock);
Packit cfa437
Packit cfa437
        for (i = 0; i < reader_count; i++) {
Packit cfa437
            if (reader_states[i].dwEventState & SCARD_STATE_CHANGED) {
Packit cfa437
                process_reader_change(&pc->readers[i], &reader_states[i]);
Packit cfa437
                pc->readers_changed++;
Packit cfa437
            }
Packit cfa437
Packit cfa437
        }
Packit cfa437
        g_mutex_unlock(&pc->lock);
Packit cfa437
Packit cfa437
        /* libpcsclite is only thread safe at a high level.  If we constantly
Packit cfa437
           hold long calls into SCardGetStatusChange, we'll starve any running
Packit cfa437
           clients.  So, if we have an active session, and nothing has changed
Packit cfa437
           on our front, we just idle.  */
Packit cfa437
        if (!pc->readers_changed && reader_count > 1) {
Packit cfa437
            g_usleep(CAPCSC_POLL_TIME * 1000);
Packit cfa437
        }
Packit cfa437
Packit cfa437
Packit cfa437
    } while (1);
Packit cfa437
Packit cfa437
    return NULL;
Packit cfa437
}
Packit cfa437
Packit cfa437
/*
Packit cfa437
 * We poll the PC/SC interface, looking for device changes
Packit cfa437
 */
Packit cfa437
static int new_event_thread(PCSCContext *pc)
Packit cfa437
{
Packit cfa437
    pc->thread = g_thread_new("capcsc_event_thread", event_thread, pc);
Packit cfa437
    return pc->thread == NULL;
Packit cfa437
}
Packit cfa437
Packit cfa437
Packit cfa437
static PCSCContext context;
Packit cfa437
Packit cfa437
int capcsc_init(void)
Packit cfa437
{
Packit cfa437
    g_mutex_init(&context.lock);
Packit cfa437
Packit cfa437
    if (init_pcsc(&context)) {
Packit cfa437
        return -1;
Packit cfa437
    }
Packit cfa437
Packit cfa437
    if (new_event_thread(&context)) {
Packit cfa437
        return -1;
Packit cfa437
    }
Packit cfa437
Packit cfa437
    return 0;
Packit cfa437
}