qemu/hw/usb/u2f-emulated.c
<<
>>
Prefs
   1/*
   2 * U2F USB Emulated 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/thread.h"
  29#include "qemu/main-loop.h"
  30#include "qapi/error.h"
  31#include "hw/usb.h"
  32#include "hw/qdev-properties.h"
  33
  34#include <u2f-emu/u2f-emu.h>
  35
  36#include "u2f.h"
  37
  38/* Counter which sync with a file */
  39struct synced_counter {
  40    /* Emulated device counter */
  41    struct u2f_emu_vdev_counter vdev_counter;
  42
  43    /* Private attributes */
  44    uint32_t value;
  45    FILE *fp;
  46};
  47
  48static void counter_increment(struct u2f_emu_vdev_counter *vdev_counter)
  49{
  50    struct synced_counter *counter = (struct synced_counter *)vdev_counter;
  51    ++counter->value;
  52
  53    /* Write back */
  54    if (fseek(counter->fp, 0, SEEK_SET) == -1) {
  55        return;
  56    }
  57    fprintf(counter->fp, "%u\n", counter->value);
  58}
  59
  60static uint32_t counter_read(struct u2f_emu_vdev_counter *vdev_counter)
  61{
  62    struct synced_counter *counter = (struct synced_counter *)vdev_counter;
  63    return counter->value;
  64}
  65
  66typedef struct U2FEmulatedState U2FEmulatedState;
  67
  68#define PENDING_OUT_NUM 32
  69
  70struct U2FEmulatedState {
  71    U2FKeyState base;
  72
  73    /* U2F virtual emulated device */
  74    u2f_emu_vdev *vdev;
  75    QemuMutex vdev_mutex;
  76
  77    /* Properties */
  78    char *dir;
  79    char *cert;
  80    char *privkey;
  81    char *entropy;
  82    char *counter;
  83    struct synced_counter synced_counter;
  84
  85    /* Pending packets received from the guest */
  86    uint8_t pending_out[PENDING_OUT_NUM][U2FHID_PACKET_SIZE];
  87    uint8_t pending_out_start;
  88    uint8_t pending_out_end;
  89    uint8_t pending_out_num;
  90    QemuMutex pending_out_mutex;
  91
  92    /* Emulation thread and sync */
  93    QemuCond key_cond;
  94    QemuMutex key_mutex;
  95    QemuThread key_thread;
  96    bool stop_thread;
  97    EventNotifier notifier;
  98};
  99
 100#define TYPE_U2F_EMULATED "u2f-emulated"
 101#define EMULATED_U2F_KEY(obj) \
 102    OBJECT_CHECK(U2FEmulatedState, (obj), TYPE_U2F_EMULATED)
 103
 104static void u2f_emulated_reset(U2FEmulatedState *key)
 105{
 106    key->pending_out_start = 0;
 107    key->pending_out_end = 0;
 108    key->pending_out_num = 0;
 109}
 110
 111static void u2f_pending_out_add(U2FEmulatedState *key,
 112                                const uint8_t packet[U2FHID_PACKET_SIZE])
 113{
 114    int index;
 115
 116    if (key->pending_out_num >= PENDING_OUT_NUM) {
 117        return;
 118    }
 119
 120    index = key->pending_out_end;
 121    key->pending_out_end = (index + 1) % PENDING_OUT_NUM;
 122    ++key->pending_out_num;
 123
 124    memcpy(&key->pending_out[index], packet, U2FHID_PACKET_SIZE);
 125}
 126
 127static uint8_t *u2f_pending_out_get(U2FEmulatedState *key)
 128{
 129    int index;
 130
 131    if (key->pending_out_num == 0) {
 132        return NULL;
 133    }
 134
 135    index  = key->pending_out_start;
 136    key->pending_out_start = (index + 1) % PENDING_OUT_NUM;
 137    --key->pending_out_num;
 138
 139    return key->pending_out[index];
 140}
 141
 142static void u2f_emulated_recv_from_guest(U2FKeyState *base,
 143                                    const uint8_t packet[U2FHID_PACKET_SIZE])
 144{
 145    U2FEmulatedState *key = EMULATED_U2F_KEY(base);
 146
 147    qemu_mutex_lock(&key->pending_out_mutex);
 148    u2f_pending_out_add(key, packet);
 149    qemu_mutex_unlock(&key->pending_out_mutex);
 150
 151    qemu_mutex_lock(&key->key_mutex);
 152    qemu_cond_signal(&key->key_cond);
 153    qemu_mutex_unlock(&key->key_mutex);
 154}
 155
 156static void *u2f_emulated_thread(void* arg)
 157{
 158    U2FEmulatedState *key = arg;
 159    uint8_t packet[U2FHID_PACKET_SIZE];
 160    uint8_t *packet_out = NULL;
 161
 162
 163    while (true) {
 164        /* Wait signal */
 165        qemu_mutex_lock(&key->key_mutex);
 166        qemu_cond_wait(&key->key_cond, &key->key_mutex);
 167        qemu_mutex_unlock(&key->key_mutex);
 168
 169        /* Exit thread check */
 170        if (key->stop_thread) {
 171            key->stop_thread = false;
 172            break;
 173        }
 174
 175        qemu_mutex_lock(&key->pending_out_mutex);
 176        packet_out = u2f_pending_out_get(key);
 177        if (packet_out == NULL) {
 178            qemu_mutex_unlock(&key->pending_out_mutex);
 179            continue;
 180        }
 181        memcpy(packet, packet_out, U2FHID_PACKET_SIZE);
 182        qemu_mutex_unlock(&key->pending_out_mutex);
 183
 184        qemu_mutex_lock(&key->vdev_mutex);
 185        u2f_emu_vdev_send(key->vdev, U2F_EMU_USB, packet,
 186                          U2FHID_PACKET_SIZE);
 187
 188        /* Notify response */
 189        if (u2f_emu_vdev_has_response(key->vdev, U2F_EMU_USB)) {
 190            event_notifier_set(&key->notifier);
 191        }
 192        qemu_mutex_unlock(&key->vdev_mutex);
 193    }
 194    return NULL;
 195}
 196
 197static ssize_t u2f_emulated_read(const char *path, char *buffer,
 198                                 size_t buffer_len)
 199{
 200    int fd;
 201    ssize_t ret;
 202
 203    fd = qemu_open_old(path, O_RDONLY);
 204    if (fd < 0) {
 205        return -1;
 206    }
 207
 208    ret = read(fd, buffer, buffer_len);
 209    close(fd);
 210
 211    return ret;
 212}
 213
 214static bool u2f_emulated_setup_counter(const char *path,
 215                                       struct synced_counter *counter)
 216{
 217    int fd, ret;
 218    FILE *fp;
 219
 220    fd = qemu_open_old(path, O_RDWR);
 221    if (fd < 0) {
 222        return false;
 223    }
 224    fp = fdopen(fd, "r+");
 225    if (fp == NULL) {
 226        close(fd);
 227        return false;
 228    }
 229    ret = fscanf(fp, "%u", &counter->value);
 230    if (ret == EOF) {
 231        fclose(fp);
 232        return false;
 233    }
 234    counter->fp = fp;
 235    counter->vdev_counter.counter_increment = counter_increment;
 236    counter->vdev_counter.counter_read = counter_read;
 237
 238    return true;
 239}
 240
 241static u2f_emu_rc u2f_emulated_setup_vdev_manualy(U2FEmulatedState *key)
 242{
 243    ssize_t ret;
 244    char cert_pem[4096], privkey_pem[2048];
 245    struct u2f_emu_vdev_setup setup_info;
 246
 247    /* Certificate */
 248    ret = u2f_emulated_read(key->cert, cert_pem, sizeof(cert_pem));
 249    if (ret < 0) {
 250        return -1;
 251    }
 252
 253    /* Private key */
 254    ret = u2f_emulated_read(key->privkey, privkey_pem, sizeof(privkey_pem));
 255    if (ret < 0) {
 256        return -1;
 257    }
 258
 259    /* Entropy */
 260    ret = u2f_emulated_read(key->entropy, (char *)&setup_info.entropy,
 261                            sizeof(setup_info.entropy));
 262    if (ret < 0) {
 263        return -1;
 264    }
 265
 266    /* Counter */
 267    if (!u2f_emulated_setup_counter(key->counter, &key->synced_counter)) {
 268        return -1;
 269    }
 270
 271    /* Setup */
 272    setup_info.certificate = cert_pem;
 273    setup_info.private_key = privkey_pem;
 274    setup_info.counter = (struct u2f_emu_vdev_counter *)&key->synced_counter;
 275
 276    return u2f_emu_vdev_new(&key->vdev, &setup_info);
 277}
 278
 279static void u2f_emulated_event_handler(EventNotifier *notifier)
 280{
 281    U2FEmulatedState *key = container_of(notifier, U2FEmulatedState, notifier);
 282    size_t packet_size;
 283    uint8_t *packet_in = NULL;
 284
 285    event_notifier_test_and_clear(&key->notifier);
 286    qemu_mutex_lock(&key->vdev_mutex);
 287    while (u2f_emu_vdev_has_response(key->vdev, U2F_EMU_USB)) {
 288        packet_size = u2f_emu_vdev_get_response(key->vdev, U2F_EMU_USB,
 289                                                &packet_in);
 290        if (packet_size == U2FHID_PACKET_SIZE) {
 291            u2f_send_to_guest(&key->base, packet_in);
 292        }
 293        u2f_emu_vdev_free_response(packet_in);
 294    }
 295    qemu_mutex_unlock(&key->vdev_mutex);
 296}
 297
 298static void u2f_emulated_realize(U2FKeyState *base, Error **errp)
 299{
 300    U2FEmulatedState *key = EMULATED_U2F_KEY(base);
 301    u2f_emu_rc rc;
 302
 303    if (key->cert != NULL || key->privkey != NULL || key->entropy != NULL
 304        || key->counter != NULL) {
 305        if (key->cert != NULL && key->privkey != NULL
 306            && key->entropy != NULL && key->counter != NULL) {
 307            rc = u2f_emulated_setup_vdev_manualy(key);
 308        } else {
 309            error_setg(errp, "%s: cert, priv, entropy and counter "
 310                       "parameters must be provided to manualy configure "
 311                       "the emulated device", TYPE_U2F_EMULATED);
 312            return;
 313        }
 314    } else if (key->dir != NULL) {
 315        rc = u2f_emu_vdev_new_from_dir(&key->vdev, key->dir);
 316    } else {
 317        rc = u2f_emu_vdev_new_ephemeral(&key->vdev);
 318    }
 319
 320    if (rc != U2F_EMU_OK) {
 321        error_setg(errp, "%s: Failed to setup the key", TYPE_U2F_EMULATED);
 322        return;
 323    }
 324
 325    if (event_notifier_init(&key->notifier, false) < 0) {
 326        error_setg(errp, "%s: Failed to initialize notifier",
 327                   TYPE_U2F_EMULATED);
 328        return;
 329    }
 330    /* Notifier */
 331    event_notifier_set_handler(&key->notifier, u2f_emulated_event_handler);
 332
 333    /* Synchronization */
 334    qemu_cond_init(&key->key_cond);
 335    qemu_mutex_init(&key->vdev_mutex);
 336    qemu_mutex_init(&key->pending_out_mutex);
 337    qemu_mutex_init(&key->key_mutex);
 338    u2f_emulated_reset(key);
 339
 340    /* Thread */
 341    key->stop_thread = false;
 342    qemu_thread_create(&key->key_thread, "u2f-key", u2f_emulated_thread,
 343                       key, QEMU_THREAD_JOINABLE);
 344}
 345
 346static void u2f_emulated_unrealize(U2FKeyState *base)
 347{
 348    U2FEmulatedState *key = EMULATED_U2F_KEY(base);
 349
 350    /* Thread */
 351    key->stop_thread = true;
 352    qemu_cond_signal(&key->key_cond);
 353    qemu_thread_join(&key->key_thread);
 354
 355    /* Notifier */
 356    event_notifier_set_handler(&key->notifier, NULL);
 357    event_notifier_cleanup(&key->notifier);
 358
 359    /* Synchronization */
 360    qemu_cond_destroy(&key->key_cond);
 361    qemu_mutex_destroy(&key->vdev_mutex);
 362    qemu_mutex_destroy(&key->key_mutex);
 363    qemu_mutex_destroy(&key->pending_out_mutex);
 364
 365    /* Vdev */
 366    u2f_emu_vdev_free(key->vdev);
 367    if (key->synced_counter.fp != NULL) {
 368        fclose(key->synced_counter.fp);
 369    }
 370}
 371
 372static Property u2f_emulated_properties[] = {
 373    DEFINE_PROP_STRING("dir", U2FEmulatedState, dir),
 374    DEFINE_PROP_STRING("cert", U2FEmulatedState, cert),
 375    DEFINE_PROP_STRING("privkey", U2FEmulatedState, privkey),
 376    DEFINE_PROP_STRING("entropy", U2FEmulatedState, entropy),
 377    DEFINE_PROP_STRING("counter", U2FEmulatedState, counter),
 378    DEFINE_PROP_END_OF_LIST(),
 379};
 380
 381static void u2f_emulated_class_init(ObjectClass *klass, void *data)
 382{
 383    DeviceClass *dc = DEVICE_CLASS(klass);
 384    U2FKeyClass *kc = U2F_KEY_CLASS(klass);
 385
 386    kc->realize = u2f_emulated_realize;
 387    kc->unrealize = u2f_emulated_unrealize;
 388    kc->recv_from_guest = u2f_emulated_recv_from_guest;
 389    dc->desc = "QEMU U2F emulated key";
 390    device_class_set_props(dc, u2f_emulated_properties);
 391}
 392
 393static const TypeInfo u2f_key_emulated_info = {
 394    .name = TYPE_U2F_EMULATED,
 395    .parent = TYPE_U2F_KEY,
 396    .instance_size = sizeof(U2FEmulatedState),
 397    .class_init = u2f_emulated_class_init
 398};
 399
 400static void u2f_key_emulated_register_types(void)
 401{
 402    type_register_static(&u2f_key_emulated_info);
 403}
 404
 405type_init(u2f_key_emulated_register_types)
 406