qemu/hw/usb/u2f-passthru.c
<<
>>
Prefs
   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}
 538
 539static const TypeInfo u2f_key_passthru_info = {
 540    .name = TYPE_U2F_PASSTHRU,
 541    .parent = TYPE_U2F_KEY,
 542    .instance_size = sizeof(U2FPassthruState),
 543    .class_init = u2f_passthru_class_init
 544};
 545
 546static void u2f_key_passthru_register_types(void)
 547{
 548    type_register_static(&u2f_key_passthru_info);
 549}
 550
 551type_init(u2f_key_passthru_register_types)
 552