cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

ccid-card-passthru.c (13317B)


      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)