qemu

Форк
0
/
u2f-passthru.c 
550 строк · 15.2 Кб
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

47
struct 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

56
typedef struct U2FPassthruState U2FPassthruState;
57

58
#define CURRENT_TRANSACTIONS_NUM 4
59

60
struct 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

90
struct 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

98
static inline uint32_t packet_get_cid(const void *packet)
99
{
100
    return *((uint32_t *)packet);
101
}
102

103
static inline bool packet_is_init(const void *packet)
104
{
105
    return ((uint8_t *)packet)[4] & (1 << 7);
106
}
107

108
static 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

118
static 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

128
static 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

140
static 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

152
static 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

162
static 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

177
static 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

204
static 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

234
static void u2f_passthru_read(void *opaque);
235

236
static 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

258
static 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

305
static 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

332
static 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

353
static 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
382
static 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

396
static 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

430
static 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

459
static 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

467
static 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(key->hidraw, O_RDWR, errp);
486
        if (fd < 0) {
487
            return;
488
        }
489

490
        if (!u2f_passthru_is_u2f_device(fd)) {
491
            qemu_close(fd);
492
            error_setg(errp, "%s: Passed hidraw does not represent "
493
                       "a U2F HID device", TYPE_U2F_PASSTHRU);
494
            return;
495
        }
496
    }
497
    key->hidraw_fd = fd;
498
    u2f_passthru_reset(key);
499
}
500

501
static int u2f_passthru_post_load(void *opaque, int version_id)
502
{
503
    U2FPassthruState *key = opaque;
504
    u2f_passthru_reset(key);
505
    return 0;
506
}
507

508
static const VMStateDescription u2f_passthru_vmstate = {
509
    .name = "u2f-key-passthru",
510
    .version_id = 1,
511
    .minimum_version_id = 1,
512
    .post_load = u2f_passthru_post_load,
513
    .fields = (const VMStateField[]) {
514
        VMSTATE_U2F_KEY(base, U2FPassthruState),
515
        VMSTATE_END_OF_LIST()
516
    }
517
};
518

519
static Property u2f_passthru_properties[] = {
520
    DEFINE_PROP_STRING("hidraw", U2FPassthruState, hidraw),
521
    DEFINE_PROP_END_OF_LIST(),
522
};
523

524
static void u2f_passthru_class_init(ObjectClass *klass, void *data)
525
{
526
    DeviceClass *dc = DEVICE_CLASS(klass);
527
    U2FKeyClass *kc = U2F_KEY_CLASS(klass);
528

529
    kc->realize = u2f_passthru_realize;
530
    kc->unrealize = u2f_passthru_unrealize;
531
    kc->recv_from_guest = u2f_passthru_recv_from_guest;
532
    dc->desc = "QEMU U2F passthrough key";
533
    dc->vmsd = &u2f_passthru_vmstate;
534
    device_class_set_props(dc, u2f_passthru_properties);
535
    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
536
}
537

538
static const TypeInfo u2f_key_passthru_info = {
539
    .name = TYPE_U2F_PASSTHRU,
540
    .parent = TYPE_U2F_KEY,
541
    .instance_size = sizeof(U2FPassthruState),
542
    .class_init = u2f_passthru_class_init
543
};
544

545
static void u2f_key_passthru_register_types(void)
546
{
547
    type_register_static(&u2f_key_passthru_info);
548
}
549

550
type_init(u2f_key_passthru_register_types)
551

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.