qemu/hw/usb/ccid-card-passthru.c
<<
>>
Prefs
   1/*
   2 * CCID Passthru Card Device emulation
   3 *
   4 * Copyright (c) 2011 Red Hat.
   5 * Written by Alon Levy.
   6 *
   7 * This work is licensed under the terms of the GNU GPL, version 2.1 or later.
   8 * See the COPYING file in the top-level directory.
   9 */
  10
  11#include "qemu/osdep.h"
  12#include "qemu-common.h"
  13#include "qemu/units.h"
  14#include <libcacard.h>
  15#include "chardev/char-fe.h"
  16#include "hw/qdev-properties.h"
  17#include "hw/qdev-properties-system.h"
  18#include "migration/vmstate.h"
  19#include "qemu/error-report.h"
  20#include "qemu/module.h"
  21#include "qemu/sockets.h"
  22#include "ccid.h"
  23#include "qapi/error.h"
  24#include "qom/object.h"
  25
  26#define DPRINTF(card, lvl, fmt, ...)                    \
  27do {                                                    \
  28    if (lvl <= card->debug) {                           \
  29        printf("ccid-card-passthru: " fmt , ## __VA_ARGS__);     \
  30    }                                                   \
  31} while (0)
  32
  33#define D_WARN 1
  34#define D_INFO 2
  35#define D_MORE_INFO 3
  36#define D_VERBOSE 4
  37
  38/* TODO: do we still need this? */
  39static const uint8_t DEFAULT_ATR[] = {
  40/*
  41 * From some example somewhere
  42 * 0x3B, 0xB0, 0x18, 0x00, 0xD1, 0x81, 0x05, 0xB1, 0x40, 0x38, 0x1F, 0x03, 0x28
  43 */
  44
  45/* From an Athena smart card */
  46 0x3B, 0xD5, 0x18, 0xFF, 0x80, 0x91, 0xFE, 0x1F, 0xC3, 0x80, 0x73, 0xC8, 0x21,
  47 0x13, 0x08
  48};
  49
  50#define VSCARD_IN_SIZE      (64 * KiB)
  51
  52/* maximum size of ATR - from 7816-3 */
  53#define MAX_ATR_SIZE        40
  54
  55typedef struct PassthruState PassthruState;
  56
  57struct PassthruState {
  58    CCIDCardState base;
  59    CharBackend cs;
  60    uint8_t  vscard_in_data[VSCARD_IN_SIZE];
  61    uint32_t vscard_in_pos;
  62    uint32_t vscard_in_hdr;
  63    uint8_t  atr[MAX_ATR_SIZE];
  64    uint8_t  atr_length;
  65    uint8_t  debug;
  66};
  67
  68#define TYPE_CCID_PASSTHRU "ccid-card-passthru"
  69DECLARE_INSTANCE_CHECKER(PassthruState, PASSTHRU_CCID_CARD,
  70                         TYPE_CCID_PASSTHRU)
  71
  72/*
  73 * VSCard protocol over chardev
  74 * This code should not depend on the card type.
  75 */
  76
  77static void ccid_card_vscard_send_msg(PassthruState *s,
  78        VSCMsgType type, uint32_t reader_id,
  79        const uint8_t *payload, uint32_t length)
  80{
  81    VSCMsgHeader scr_msg_header;
  82
  83    scr_msg_header.type = htonl(type);
  84    scr_msg_header.reader_id = htonl(reader_id);
  85    scr_msg_header.length = htonl(length);
  86    /* XXX this blocks entire thread. Rewrite to use
  87     * qemu_chr_fe_write and background I/O callbacks */
  88    qemu_chr_fe_write_all(&s->cs, (uint8_t *)&scr_msg_header,
  89                          sizeof(VSCMsgHeader));
  90    qemu_chr_fe_write_all(&s->cs, payload, length);
  91}
  92
  93static void ccid_card_vscard_send_apdu(PassthruState *s,
  94    const uint8_t *apdu, uint32_t length)
  95{
  96    ccid_card_vscard_send_msg(
  97        s, VSC_APDU, VSCARD_MINIMAL_READER_ID, apdu, length);
  98}
  99
 100static void ccid_card_vscard_send_error(PassthruState *s,
 101                    uint32_t reader_id, VSCErrorCode code)
 102{
 103    VSCMsgError msg = {.code = htonl(code)};
 104
 105    ccid_card_vscard_send_msg(
 106        s, VSC_Error, reader_id, (uint8_t *)&msg, sizeof(msg));
 107}
 108
 109static void ccid_card_vscard_send_init(PassthruState *s)
 110{
 111    VSCMsgInit msg = {
 112        .version = htonl(VSCARD_VERSION),
 113        .magic = VSCARD_MAGIC,
 114        .capabilities = {0}
 115    };
 116
 117    ccid_card_vscard_send_msg(s, VSC_Init, VSCARD_UNDEFINED_READER_ID,
 118                         (uint8_t *)&msg, sizeof(msg));
 119}
 120
 121static int ccid_card_vscard_can_read(void *opaque)
 122{
 123    PassthruState *card = opaque;
 124
 125    return VSCARD_IN_SIZE >= card->vscard_in_pos ?
 126           VSCARD_IN_SIZE - card->vscard_in_pos : 0;
 127}
 128
 129static void ccid_card_vscard_handle_init(
 130    PassthruState *card, VSCMsgHeader *hdr, VSCMsgInit *init)
 131{
 132    uint32_t *capabilities;
 133    int num_capabilities;
 134    int i;
 135
 136    capabilities = init->capabilities;
 137    num_capabilities =
 138        1 + ((hdr->length - sizeof(VSCMsgInit)) / sizeof(uint32_t));
 139    init->version = ntohl(init->version);
 140    for (i = 0 ; i < num_capabilities; ++i) {
 141        capabilities[i] = ntohl(capabilities[i]);
 142    }
 143    if (init->magic != VSCARD_MAGIC) {
 144        error_report("wrong magic");
 145        /* we can't disconnect the chardev */
 146    }
 147    if (init->version != VSCARD_VERSION) {
 148        DPRINTF(card, D_WARN,
 149            "got version %d, have %d", init->version, VSCARD_VERSION);
 150    }
 151    /* future handling of capabilities, none exist atm */
 152    ccid_card_vscard_send_init(card);
 153}
 154
 155static int check_atr(PassthruState *card, uint8_t *data, int len)
 156{
 157    int historical_length, opt_bytes;
 158    int td_count = 0;
 159    int td;
 160
 161    if (len < 2) {
 162        return 0;
 163    }
 164    historical_length = data[1] & 0xf;
 165    opt_bytes = 0;
 166    if (data[0] != 0x3b && data[0] != 0x3f) {
 167        DPRINTF(card, D_WARN, "atr's T0 is 0x%X, not in {0x3b, 0x3f}\n",
 168                data[0]);
 169        return 0;
 170    }
 171    td_count = 0;
 172    td = data[1] >> 4;
 173    while (td && td_count < 2 && opt_bytes + historical_length + 2 < len) {
 174        td_count++;
 175        if (td & 0x1) {
 176            opt_bytes++;
 177        }
 178        if (td & 0x2) {
 179            opt_bytes++;
 180        }
 181        if (td & 0x4) {
 182            opt_bytes++;
 183        }
 184        if (td & 0x8) {
 185            opt_bytes++;
 186            td = data[opt_bytes + 2] >> 4;
 187        }
 188    }
 189    if (len < 2 + historical_length + opt_bytes) {
 190        DPRINTF(card, D_WARN,
 191            "atr too short: len %d, but historical_len %d, T1 0x%X\n",
 192            len, historical_length, data[1]);
 193        return 0;
 194    }
 195    if (len > 2 + historical_length + opt_bytes) {
 196        DPRINTF(card, D_WARN,
 197            "atr too long: len %d, but hist/opt %d/%d, T1 0x%X\n",
 198            len, historical_length, opt_bytes, data[1]);
 199        /* let it through */
 200    }
 201    DPRINTF(card, D_VERBOSE,
 202            "atr passes check: %d total length, %d historical, %d optional\n",
 203            len, historical_length, opt_bytes);
 204
 205    return 1;
 206}
 207
 208static void ccid_card_vscard_handle_message(PassthruState *card,
 209    VSCMsgHeader *scr_msg_header)
 210{
 211    uint8_t *data = (uint8_t *)&scr_msg_header[1];
 212
 213    switch (scr_msg_header->type) {
 214    case VSC_ATR:
 215        DPRINTF(card, D_INFO, "VSC_ATR %d\n", scr_msg_header->length);
 216        if (scr_msg_header->length > MAX_ATR_SIZE) {
 217            error_report("ATR size exceeds spec, ignoring");
 218            ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
 219                                        VSC_GENERAL_ERROR);
 220            break;
 221        }
 222        if (!check_atr(card, data, scr_msg_header->length)) {
 223            error_report("ATR is inconsistent, ignoring");
 224            ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
 225                                        VSC_GENERAL_ERROR);
 226            break;
 227        }
 228        memcpy(card->atr, data, scr_msg_header->length);
 229        card->atr_length = scr_msg_header->length;
 230        ccid_card_card_inserted(&card->base);
 231        ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
 232                                    VSC_SUCCESS);
 233        break;
 234    case VSC_APDU:
 235        ccid_card_send_apdu_to_guest(
 236            &card->base, data, scr_msg_header->length);
 237        break;
 238    case VSC_CardRemove:
 239        DPRINTF(card, D_INFO, "VSC_CardRemove\n");
 240        ccid_card_card_removed(&card->base);
 241        ccid_card_vscard_send_error(card,
 242            scr_msg_header->reader_id, VSC_SUCCESS);
 243        break;
 244    case VSC_Init:
 245        ccid_card_vscard_handle_init(
 246            card, scr_msg_header, (VSCMsgInit *)data);
 247        break;
 248    case VSC_Error:
 249        ccid_card_card_error(&card->base, *(uint32_t *)data);
 250        break;
 251    case VSC_ReaderAdd:
 252        if (ccid_card_ccid_attach(&card->base) < 0) {
 253            ccid_card_vscard_send_error(card, VSCARD_UNDEFINED_READER_ID,
 254                                      VSC_CANNOT_ADD_MORE_READERS);
 255        } else {
 256            ccid_card_vscard_send_error(card, VSCARD_MINIMAL_READER_ID,
 257                                        VSC_SUCCESS);
 258        }
 259        break;
 260    case VSC_ReaderRemove:
 261        ccid_card_ccid_detach(&card->base);
 262        ccid_card_vscard_send_error(card,
 263            scr_msg_header->reader_id, VSC_SUCCESS);
 264        break;
 265    default:
 266        printf("usb-ccid: chardev: unexpected message of type %X\n",
 267               scr_msg_header->type);
 268        ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
 269            VSC_GENERAL_ERROR);
 270    }
 271}
 272
 273static void ccid_card_vscard_drop_connection(PassthruState *card)
 274{
 275    qemu_chr_fe_deinit(&card->cs, true);
 276    card->vscard_in_pos = card->vscard_in_hdr = 0;
 277}
 278
 279static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size)
 280{
 281    PassthruState *card = opaque;
 282    VSCMsgHeader *hdr;
 283
 284    if (card->vscard_in_pos + size > VSCARD_IN_SIZE) {
 285        error_report("no room for data: pos %u +  size %d > %" PRId64 "."
 286                     " dropping connection.",
 287                     card->vscard_in_pos, size, VSCARD_IN_SIZE);
 288        ccid_card_vscard_drop_connection(card);
 289        return;
 290    }
 291    assert(card->vscard_in_pos < VSCARD_IN_SIZE);
 292    assert(card->vscard_in_hdr < VSCARD_IN_SIZE);
 293    memcpy(card->vscard_in_data + card->vscard_in_pos, buf, size);
 294    card->vscard_in_pos += size;
 295    hdr = (VSCMsgHeader *)(card->vscard_in_data + card->vscard_in_hdr);
 296
 297    while ((card->vscard_in_pos - card->vscard_in_hdr >= sizeof(VSCMsgHeader))
 298         &&(card->vscard_in_pos - card->vscard_in_hdr >=
 299                                  sizeof(VSCMsgHeader) + ntohl(hdr->length))) {
 300        hdr->reader_id = ntohl(hdr->reader_id);
 301        hdr->length = ntohl(hdr->length);
 302        hdr->type = ntohl(hdr->type);
 303        ccid_card_vscard_handle_message(card, hdr);
 304        card->vscard_in_hdr += hdr->length + sizeof(VSCMsgHeader);
 305        hdr = (VSCMsgHeader *)(card->vscard_in_data + card->vscard_in_hdr);
 306    }
 307    if (card->vscard_in_hdr == card->vscard_in_pos) {
 308        card->vscard_in_pos = card->vscard_in_hdr = 0;
 309    }
 310}
 311
 312static void ccid_card_vscard_event(void *opaque, QEMUChrEvent event)
 313{
 314    PassthruState *card = opaque;
 315
 316    switch (event) {
 317    case CHR_EVENT_BREAK:
 318        card->vscard_in_pos = card->vscard_in_hdr = 0;
 319        break;
 320    case CHR_EVENT_OPENED:
 321        DPRINTF(card, D_INFO, "%s: CHR_EVENT_OPENED\n", __func__);
 322        break;
 323    case CHR_EVENT_MUX_IN:
 324    case CHR_EVENT_MUX_OUT:
 325    case CHR_EVENT_CLOSED:
 326        /* Ignore */
 327        break;
 328    }
 329}
 330
 331/* End VSCard handling */
 332
 333static void passthru_apdu_from_guest(
 334    CCIDCardState *base, const uint8_t *apdu, uint32_t len)
 335{
 336    PassthruState *card = PASSTHRU_CCID_CARD(base);
 337
 338    if (!qemu_chr_fe_backend_connected(&card->cs)) {
 339        printf("ccid-passthru: no chardev, discarding apdu length %u\n", len);
 340        return;
 341    }
 342    ccid_card_vscard_send_apdu(card, apdu, len);
 343}
 344
 345static const uint8_t *passthru_get_atr(CCIDCardState *base, uint32_t *len)
 346{
 347    PassthruState *card = PASSTHRU_CCID_CARD(base);
 348
 349    *len = card->atr_length;
 350    return card->atr;
 351}
 352
 353static void passthru_realize(CCIDCardState *base, Error **errp)
 354{
 355    PassthruState *card = PASSTHRU_CCID_CARD(base);
 356
 357    card->vscard_in_pos = 0;
 358    card->vscard_in_hdr = 0;
 359    if (qemu_chr_fe_backend_connected(&card->cs)) {
 360        DPRINTF(card, D_INFO, "ccid-card-passthru: initing chardev");
 361        qemu_chr_fe_set_handlers(&card->cs,
 362            ccid_card_vscard_can_read,
 363            ccid_card_vscard_read,
 364            ccid_card_vscard_event, NULL, card, NULL, true);
 365        ccid_card_vscard_send_init(card);
 366    } else {
 367        error_setg(errp, "missing chardev");
 368        return;
 369    }
 370    card->debug = parse_debug_env("QEMU_CCID_PASSTHRU_DEBUG", D_VERBOSE,
 371                                  card->debug);
 372    assert(sizeof(DEFAULT_ATR) <= MAX_ATR_SIZE);
 373    memcpy(card->atr, DEFAULT_ATR, sizeof(DEFAULT_ATR));
 374    card->atr_length = sizeof(DEFAULT_ATR);
 375}
 376
 377static const VMStateDescription passthru_vmstate = {
 378    .name = "ccid-card-passthru",
 379    .version_id = 1,
 380    .minimum_version_id = 1,
 381    .fields = (VMStateField[]) {
 382        VMSTATE_BUFFER(vscard_in_data, PassthruState),
 383        VMSTATE_UINT32(vscard_in_pos, PassthruState),
 384        VMSTATE_UINT32(vscard_in_hdr, PassthruState),
 385        VMSTATE_BUFFER(atr, PassthruState),
 386        VMSTATE_UINT8(atr_length, PassthruState),
 387        VMSTATE_END_OF_LIST()
 388    }
 389};
 390
 391static Property passthru_card_properties[] = {
 392    DEFINE_PROP_CHR("chardev", PassthruState, cs),
 393    DEFINE_PROP_UINT8("debug", PassthruState, debug, 0),
 394    DEFINE_PROP_END_OF_LIST(),
 395};
 396
 397static void passthru_class_initfn(ObjectClass *klass, void *data)
 398{
 399    DeviceClass *dc = DEVICE_CLASS(klass);
 400    CCIDCardClass *cc = CCID_CARD_CLASS(klass);
 401
 402    cc->realize = passthru_realize;
 403    cc->get_atr = passthru_get_atr;
 404    cc->apdu_from_guest = passthru_apdu_from_guest;
 405    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
 406    dc->desc = "passthrough smartcard";
 407    dc->vmsd = &passthru_vmstate;
 408    device_class_set_props(dc, passthru_card_properties);
 409}
 410
 411static const TypeInfo passthru_card_info = {
 412    .name          = TYPE_CCID_PASSTHRU,
 413    .parent        = TYPE_CCID_CARD,
 414    .instance_size = sizeof(PassthruState),
 415    .class_init    = passthru_class_initfn,
 416};
 417module_obj(TYPE_CCID_PASSTHRU);
 418
 419static void ccid_card_passthru_register_types(void)
 420{
 421    type_register_static(&passthru_card_info);
 422}
 423
 424type_init(ccid_card_passthru_register_types)
 425