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

u2f-passthru.c (15663B)


      1/*
      2 * U2F USB Passthru device.
      3 *
      4 * Copyright (c) 2020 César Belley <cesar.belley@lse.epita.fr>
      5 * Written by César Belley <cesar.belley@lse.epita.fr>
      6 *
      7 * Permission is hereby granted, free of charge, to any person obtaining a copy
      8 * of this software and associated documentation files (the "Software"), to deal
      9 * in the Software without restriction, including without limitation the rights
     10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     11 * copies of the Software, and to permit persons to whom the Software is
     12 * furnished to do so, subject to the following conditions:
     13 *
     14 * The above copyright notice and this permission notice shall be included in
     15 * all copies or substantial portions of the Software.
     16 *
     17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     23 * THE SOFTWARE.
     24 */
     25
     26#include "qemu/osdep.h"
     27#include "qemu/module.h"
     28#include "qemu/main-loop.h"
     29#include "qemu/error-report.h"
     30#include "qapi/error.h"
     31#include "hw/qdev-properties.h"
     32#include "hw/usb.h"
     33#include "migration/vmstate.h"
     34
     35#include "u2f.h"
     36
     37#ifdef CONFIG_LIBUDEV
     38#include <libudev.h>
     39#endif
     40#include <linux/hidraw.h>
     41#include <sys/ioctl.h>
     42
     43#define NONCE_SIZE 8
     44#define BROADCAST_CID 0xFFFFFFFF
     45#define TRANSACTION_TIMEOUT 120000
     46
     47struct transaction {
     48    uint32_t cid;
     49    uint16_t resp_bcnt;
     50    uint16_t resp_size;
     51
     52    /* Nonce for broadcast isolation */
     53    uint8_t nonce[NONCE_SIZE];
     54};
     55
     56typedef struct U2FPassthruState U2FPassthruState;
     57
     58#define CURRENT_TRANSACTIONS_NUM 4
     59
     60struct U2FPassthruState {
     61    U2FKeyState base;
     62
     63    /* Host device */
     64    char *hidraw;
     65    int hidraw_fd;
     66
     67    /* Current Transactions */
     68    struct transaction current_transactions[CURRENT_TRANSACTIONS_NUM];
     69    uint8_t current_transactions_start;
     70    uint8_t current_transactions_end;
     71    uint8_t current_transactions_num;
     72
     73    /* Transaction time checking */
     74    int64_t last_transaction_time;
     75    QEMUTimer timer;
     76};
     77
     78#define TYPE_U2F_PASSTHRU "u2f-passthru"
     79#define PASSTHRU_U2F_KEY(obj) \
     80    OBJECT_CHECK(U2FPassthruState, (obj), TYPE_U2F_PASSTHRU)
     81
     82/* Init packet sizes */
     83#define PACKET_INIT_HEADER_SIZE 7
     84#define PACKET_INIT_DATA_SIZE (U2FHID_PACKET_SIZE - PACKET_INIT_HEADER_SIZE)
     85
     86/* Cont packet sizes */
     87#define PACKET_CONT_HEADER_SIZE 5
     88#define PACKET_CONT_DATA_SIZE (U2FHID_PACKET_SIZE - PACKET_CONT_HEADER_SIZE)
     89
     90struct packet_init {
     91    uint32_t cid;
     92    uint8_t cmd;
     93    uint8_t bcnth;
     94    uint8_t bcntl;
     95    uint8_t data[PACKET_INIT_DATA_SIZE];
     96} QEMU_PACKED;
     97
     98static inline uint32_t packet_get_cid(const void *packet)
     99{
    100    return *((uint32_t *)packet);
    101}
    102
    103static inline bool packet_is_init(const void *packet)
    104{
    105    return ((uint8_t *)packet)[4] & (1 << 7);
    106}
    107
    108static inline uint16_t packet_init_get_bcnt(
    109        const struct packet_init *packet_init)
    110{
    111    uint16_t bcnt = 0;
    112    bcnt |= packet_init->bcnth << 8;
    113    bcnt |= packet_init->bcntl;
    114
    115    return bcnt;
    116}
    117
    118static void u2f_passthru_reset(U2FPassthruState *key)
    119{
    120    timer_del(&key->timer);
    121    qemu_set_fd_handler(key->hidraw_fd, NULL, NULL, key);
    122    key->last_transaction_time = 0;
    123    key->current_transactions_start = 0;
    124    key->current_transactions_end = 0;
    125    key->current_transactions_num = 0;
    126}
    127
    128static void u2f_timeout_check(void *opaque)
    129{
    130    U2FPassthruState *key = opaque;
    131    int64_t time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
    132
    133    if (time > key->last_transaction_time + TRANSACTION_TIMEOUT) {
    134        u2f_passthru_reset(key);
    135    } else {
    136        timer_mod(&key->timer, time + TRANSACTION_TIMEOUT / 4);
    137    }
    138}
    139
    140static int u2f_transaction_get_index(U2FPassthruState *key, uint32_t cid)
    141{
    142    for (int i = 0; i < key->current_transactions_num; ++i) {
    143        int index = (key->current_transactions_start + i)
    144            % CURRENT_TRANSACTIONS_NUM;
    145        if (cid == key->current_transactions[index].cid) {
    146            return index;
    147        }
    148    }
    149    return -1;
    150}
    151
    152static struct transaction *u2f_transaction_get(U2FPassthruState *key,
    153                                               uint32_t cid)
    154{
    155    int index = u2f_transaction_get_index(key, cid);
    156    if (index < 0) {
    157        return NULL;
    158    }
    159    return &key->current_transactions[index];
    160}
    161
    162static struct transaction *u2f_transaction_get_from_nonce(U2FPassthruState *key,
    163                                const uint8_t nonce[NONCE_SIZE])
    164{
    165    for (int i = 0; i < key->current_transactions_num; ++i) {
    166        int index = (key->current_transactions_start + i)
    167            % CURRENT_TRANSACTIONS_NUM;
    168        if (key->current_transactions[index].cid == BROADCAST_CID
    169            && memcmp(nonce, key->current_transactions[index].nonce,
    170                      NONCE_SIZE) == 0) {
    171            return &key->current_transactions[index];
    172        }
    173    }
    174    return NULL;
    175}
    176
    177static void u2f_transaction_close(U2FPassthruState *key, uint32_t cid)
    178{
    179    int index, next_index;
    180    index = u2f_transaction_get_index(key, cid);
    181    if (index < 0) {
    182        return;
    183    }
    184    next_index = (index + 1) % CURRENT_TRANSACTIONS_NUM;
    185
    186    /* Rearrange to ensure the oldest is at the start position */
    187    while (next_index != key->current_transactions_end) {
    188        memcpy(&key->current_transactions[index],
    189               &key->current_transactions[next_index],
    190               sizeof(struct transaction));
    191
    192        index = next_index;
    193        next_index = (index + 1) % CURRENT_TRANSACTIONS_NUM;
    194    }
    195
    196    key->current_transactions_end = index;
    197    --key->current_transactions_num;
    198
    199    if (key->current_transactions_num == 0) {
    200        u2f_passthru_reset(key);
    201    }
    202}
    203
    204static void u2f_transaction_add(U2FPassthruState *key, uint32_t cid,
    205                                const uint8_t nonce[NONCE_SIZE])
    206{
    207    uint8_t index;
    208    struct transaction *transaction;
    209
    210    if (key->current_transactions_num >= CURRENT_TRANSACTIONS_NUM) {
    211        /* Close the oldest transaction */
    212        index = key->current_transactions_start;
    213        transaction = &key->current_transactions[index];
    214        u2f_transaction_close(key, transaction->cid);
    215    }
    216
    217    /* Index */
    218    index = key->current_transactions_end;
    219    key->current_transactions_end = (index + 1) % CURRENT_TRANSACTIONS_NUM;
    220    ++key->current_transactions_num;
    221
    222    /* Transaction */
    223    transaction = &key->current_transactions[index];
    224    transaction->cid = cid;
    225    transaction->resp_bcnt = 0;
    226    transaction->resp_size = 0;
    227
    228    /* Nonce */
    229    if (nonce != NULL) {
    230        memcpy(transaction->nonce, nonce, NONCE_SIZE);
    231    }
    232}
    233
    234static void u2f_passthru_read(void *opaque);
    235
    236static void u2f_transaction_start(U2FPassthruState *key,
    237                                  const struct packet_init *packet_init)
    238{
    239    int64_t time;
    240
    241    /* Transaction */
    242    if (packet_init->cid == BROADCAST_CID) {
    243        u2f_transaction_add(key, packet_init->cid, packet_init->data);
    244    } else {
    245        u2f_transaction_add(key, packet_init->cid, NULL);
    246    }
    247
    248    /* Time */
    249    time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
    250    if (key->last_transaction_time == 0) {
    251        qemu_set_fd_handler(key->hidraw_fd, u2f_passthru_read, NULL, key);
    252        timer_init_ms(&key->timer, QEMU_CLOCK_VIRTUAL, u2f_timeout_check, key);
    253        timer_mod(&key->timer, time + TRANSACTION_TIMEOUT / 4);
    254    }
    255    key->last_transaction_time = time;
    256}
    257
    258static void u2f_passthru_recv_from_host(U2FPassthruState *key,
    259                                    const uint8_t packet[U2FHID_PACKET_SIZE])
    260{
    261    struct transaction *transaction;
    262    uint32_t cid;
    263
    264    /* Retrieve transaction */
    265    cid = packet_get_cid(packet);
    266    if (cid == BROADCAST_CID) {
    267        struct packet_init *packet_init;
    268        if (!packet_is_init(packet)) {
    269            return;
    270        }
    271        packet_init = (struct packet_init *)packet;
    272        transaction = u2f_transaction_get_from_nonce(key, packet_init->data);
    273    } else {
    274        transaction = u2f_transaction_get(key, cid);
    275    }
    276
    277    /* Ignore no started transaction */
    278    if (transaction == NULL) {
    279        return;
    280    }
    281
    282    if (packet_is_init(packet)) {
    283        struct packet_init *packet_init = (struct packet_init *)packet;
    284        transaction->resp_bcnt = packet_init_get_bcnt(packet_init);
    285        transaction->resp_size = PACKET_INIT_DATA_SIZE;
    286
    287        if (packet_init->cid == BROADCAST_CID) {
    288            /* Nonce checking for legitimate response */
    289            if (memcmp(transaction->nonce, packet_init->data, NONCE_SIZE)
    290                != 0) {
    291                return;
    292            }
    293        }
    294    } else {
    295        transaction->resp_size += PACKET_CONT_DATA_SIZE;
    296    }
    297
    298    /* Transaction end check */
    299    if (transaction->resp_size >= transaction->resp_bcnt) {
    300        u2f_transaction_close(key, cid);
    301    }
    302    u2f_send_to_guest(&key->base, packet);
    303}
    304
    305static void u2f_passthru_read(void *opaque)
    306{
    307    U2FPassthruState *key = opaque;
    308    U2FKeyState *base = &key->base;
    309    uint8_t packet[2 * U2FHID_PACKET_SIZE];
    310    int ret;
    311
    312    /* Full size base queue check */
    313    if (base->pending_in_num >= U2FHID_PENDING_IN_NUM) {
    314        return;
    315    }
    316
    317    ret = read(key->hidraw_fd, packet, sizeof(packet));
    318    if (ret < 0) {
    319        /* Detach */
    320        if (base->dev.attached) {
    321            usb_device_detach(&base->dev);
    322            u2f_passthru_reset(key);
    323        }
    324        return;
    325    }
    326    if (ret != U2FHID_PACKET_SIZE) {
    327        return;
    328    }
    329    u2f_passthru_recv_from_host(key, packet);
    330}
    331
    332static void u2f_passthru_recv_from_guest(U2FKeyState *base,
    333                                    const uint8_t packet[U2FHID_PACKET_SIZE])
    334{
    335    U2FPassthruState *key = PASSTHRU_U2F_KEY(base);
    336    uint8_t host_packet[U2FHID_PACKET_SIZE + 1];
    337    ssize_t written;
    338
    339    if (packet_is_init(packet)) {
    340        u2f_transaction_start(key, (struct packet_init *)packet);
    341    }
    342
    343    host_packet[0] = 0;
    344    memcpy(host_packet + 1, packet, U2FHID_PACKET_SIZE);
    345
    346    written = write(key->hidraw_fd, host_packet, sizeof(host_packet));
    347    if (written != sizeof(host_packet)) {
    348        error_report("%s: Bad written size (req 0x%zu, val 0x%zd)",
    349                     TYPE_U2F_PASSTHRU, sizeof(host_packet), written);
    350    }
    351}
    352
    353static bool u2f_passthru_is_u2f_device(int fd)
    354{
    355    int ret, rdesc_size;
    356    struct hidraw_report_descriptor rdesc;
    357    const uint8_t u2f_hid_report_desc_header[] = {
    358        0x06, 0xd0, 0xf1, /* Usage Page (FIDO) */
    359        0x09, 0x01,       /* Usage (FIDO) */
    360    };
    361
    362    /* Get report descriptor size */
    363    ret = ioctl(fd, HIDIOCGRDESCSIZE, &rdesc_size);
    364    if (ret < 0 || rdesc_size < sizeof(u2f_hid_report_desc_header)) {
    365        return false;
    366    }
    367
    368    /* Get report descriptor */
    369    memset(&rdesc, 0x0, sizeof(rdesc));
    370    rdesc.size = rdesc_size;
    371    ret = ioctl(fd, HIDIOCGRDESC, &rdesc);
    372    if (ret < 0) {
    373        return false;
    374    }
    375
    376    /* Header bytes cover specific U2F rdesc values */
    377    return memcmp(u2f_hid_report_desc_header, rdesc.value,
    378                  sizeof(u2f_hid_report_desc_header)) == 0;
    379}
    380
    381#ifdef CONFIG_LIBUDEV
    382static int u2f_passthru_open_from_device(struct udev_device *device)
    383{
    384    const char *devnode = udev_device_get_devnode(device);
    385
    386    int fd = qemu_open_old(devnode, O_RDWR);
    387    if (fd < 0) {
    388        return -1;
    389    } else if (!u2f_passthru_is_u2f_device(fd)) {
    390        qemu_close(fd);
    391        return -1;
    392    }
    393    return fd;
    394}
    395
    396static int u2f_passthru_open_from_enumerate(struct udev *udev,
    397                                            struct udev_enumerate *enumerate)
    398{
    399    struct udev_list_entry *devices, *entry;
    400    int ret, fd;
    401
    402    ret = udev_enumerate_scan_devices(enumerate);
    403    if (ret < 0) {
    404        return -1;
    405    }
    406
    407    devices = udev_enumerate_get_list_entry(enumerate);
    408    udev_list_entry_foreach(entry, devices) {
    409        struct udev_device *device;
    410        const char *syspath = udev_list_entry_get_name(entry);
    411
    412        if (syspath == NULL) {
    413            continue;
    414        }
    415
    416        device = udev_device_new_from_syspath(udev, syspath);
    417        if (device == NULL) {
    418            continue;
    419        }
    420
    421        fd = u2f_passthru_open_from_device(device);
    422        udev_device_unref(device);
    423        if (fd >= 0) {
    424            return fd;
    425        }
    426    }
    427    return -1;
    428}
    429
    430static int u2f_passthru_open_from_scan(void)
    431{
    432    struct udev *udev;
    433    struct udev_enumerate *enumerate;
    434    int ret, fd = -1;
    435
    436    udev = udev_new();
    437    if (udev == NULL) {
    438        return -1;
    439    }
    440
    441    enumerate = udev_enumerate_new(udev);
    442    if (enumerate == NULL) {
    443        udev_unref(udev);
    444        return -1;
    445    }
    446
    447    ret = udev_enumerate_add_match_subsystem(enumerate, "hidraw");
    448    if (ret >= 0) {
    449        fd = u2f_passthru_open_from_enumerate(udev, enumerate);
    450    }
    451
    452    udev_enumerate_unref(enumerate);
    453    udev_unref(udev);
    454
    455    return fd;
    456}
    457#endif
    458
    459static void u2f_passthru_unrealize(U2FKeyState *base)
    460{
    461    U2FPassthruState *key = PASSTHRU_U2F_KEY(base);
    462
    463    u2f_passthru_reset(key);
    464    qemu_close(key->hidraw_fd);
    465}
    466
    467static void u2f_passthru_realize(U2FKeyState *base, Error **errp)
    468{
    469    U2FPassthruState *key = PASSTHRU_U2F_KEY(base);
    470    int fd;
    471
    472    if (key->hidraw == NULL) {
    473#ifdef CONFIG_LIBUDEV
    474        fd = u2f_passthru_open_from_scan();
    475        if (fd < 0) {
    476            error_setg(errp, "%s: Failed to find a U2F USB device",
    477                       TYPE_U2F_PASSTHRU);
    478            return;
    479        }
    480#else
    481        error_setg(errp, "%s: Missing hidraw", TYPE_U2F_PASSTHRU);
    482        return;
    483#endif
    484    } else {
    485        fd = qemu_open_old(key->hidraw, O_RDWR);
    486        if (fd < 0) {
    487            error_setg(errp, "%s: Failed to open %s", TYPE_U2F_PASSTHRU,
    488                       key->hidraw);
    489            return;
    490        }
    491
    492        if (!u2f_passthru_is_u2f_device(fd)) {
    493            qemu_close(fd);
    494            error_setg(errp, "%s: Passed hidraw does not represent "
    495                       "a U2F HID device", TYPE_U2F_PASSTHRU);
    496            return;
    497        }
    498    }
    499    key->hidraw_fd = fd;
    500    u2f_passthru_reset(key);
    501}
    502
    503static int u2f_passthru_post_load(void *opaque, int version_id)
    504{
    505    U2FPassthruState *key = opaque;
    506    u2f_passthru_reset(key);
    507    return 0;
    508}
    509
    510static const VMStateDescription u2f_passthru_vmstate = {
    511    .name = "u2f-key-passthru",
    512    .version_id = 1,
    513    .minimum_version_id = 1,
    514    .post_load = u2f_passthru_post_load,
    515    .fields = (VMStateField[]) {
    516        VMSTATE_U2F_KEY(base, U2FPassthruState),
    517        VMSTATE_END_OF_LIST()
    518    }
    519};
    520
    521static Property u2f_passthru_properties[] = {
    522    DEFINE_PROP_STRING("hidraw", U2FPassthruState, hidraw),
    523    DEFINE_PROP_END_OF_LIST(),
    524};
    525
    526static void u2f_passthru_class_init(ObjectClass *klass, void *data)
    527{
    528    DeviceClass *dc = DEVICE_CLASS(klass);
    529    U2FKeyClass *kc = U2F_KEY_CLASS(klass);
    530
    531    kc->realize = u2f_passthru_realize;
    532    kc->unrealize = u2f_passthru_unrealize;
    533    kc->recv_from_guest = u2f_passthru_recv_from_guest;
    534    dc->desc = "QEMU U2F passthrough key";
    535    dc->vmsd = &u2f_passthru_vmstate;
    536    device_class_set_props(dc, u2f_passthru_properties);
    537    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
    538}
    539
    540static const TypeInfo u2f_key_passthru_info = {
    541    .name = TYPE_U2F_PASSTHRU,
    542    .parent = TYPE_U2F_KEY,
    543    .instance_size = sizeof(U2FPassthruState),
    544    .class_init = u2f_passthru_class_init
    545};
    546
    547static void u2f_key_passthru_register_types(void)
    548{
    549    type_register_static(&u2f_key_passthru_info);
    550}
    551
    552type_init(u2f_key_passthru_register_types)