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)