qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

ccid-card-passthru.c (13338B)


      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/cutils.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, ...)                    \
     27 do {                                                    \
     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? */
     39 static 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 
     55 typedef struct PassthruState PassthruState;
     56 
     57 struct 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"
     69 DECLARE_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 
     77 static 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 
     93 static 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 
    100 static 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 
    109 static 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 
    121 static 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 
    129 static 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 
    155 static 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 
    208 static 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 
    273 static 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 
    279 static 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 
    312 static 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 
    333 static 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 
    345 static 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 
    353 static 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 
    377 static 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 
    391 static 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 
    397 static 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 
    411 static 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 };
    417 module_obj(TYPE_CCID_PASSTHRU);
    418 module_kconfig(USB);
    419 
    420 static void ccid_card_passthru_register_types(void)
    421 {
    422     type_register_static(&passthru_card_info);
    423 }
    424 
    425 type_init(ccid_card_passthru_register_types)