xref: /openbmc/qemu/hw/usb/u2f-passthru.c (revision 00d8ba9e)
1299976b0SCésar Belley /*
2299976b0SCésar Belley  * U2F USB Passthru device.
3299976b0SCésar Belley  *
4299976b0SCésar Belley  * Copyright (c) 2020 César Belley <cesar.belley@lse.epita.fr>
5299976b0SCésar Belley  * Written by César Belley <cesar.belley@lse.epita.fr>
6299976b0SCésar Belley  *
7299976b0SCésar Belley  * Permission is hereby granted, free of charge, to any person obtaining a copy
8299976b0SCésar Belley  * of this software and associated documentation files (the "Software"), to deal
9299976b0SCésar Belley  * in the Software without restriction, including without limitation the rights
10299976b0SCésar Belley  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11299976b0SCésar Belley  * copies of the Software, and to permit persons to whom the Software is
12299976b0SCésar Belley  * furnished to do so, subject to the following conditions:
13299976b0SCésar Belley  *
14299976b0SCésar Belley  * The above copyright notice and this permission notice shall be included in
15299976b0SCésar Belley  * all copies or substantial portions of the Software.
16299976b0SCésar Belley  *
17299976b0SCésar Belley  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18299976b0SCésar Belley  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19299976b0SCésar Belley  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20299976b0SCésar Belley  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21299976b0SCésar Belley  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22299976b0SCésar Belley  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23299976b0SCésar Belley  * THE SOFTWARE.
24299976b0SCésar Belley  */
25299976b0SCésar Belley 
26299976b0SCésar Belley #include "qemu/osdep.h"
27299976b0SCésar Belley #include "qemu/module.h"
28299976b0SCésar Belley #include "qemu/main-loop.h"
29299976b0SCésar Belley #include "qemu/error-report.h"
30299976b0SCésar Belley #include "qapi/error.h"
31299976b0SCésar Belley #include "hw/qdev-properties.h"
32299976b0SCésar Belley #include "hw/usb.h"
33299976b0SCésar Belley #include "migration/vmstate.h"
34299976b0SCésar Belley 
35299976b0SCésar Belley #include "u2f.h"
36299976b0SCésar Belley 
374ee40a6bSCésar Belley #ifdef CONFIG_LIBUDEV
384ee40a6bSCésar Belley #include <libudev.h>
394ee40a6bSCésar Belley #endif
404ee40a6bSCésar Belley #include <linux/hidraw.h>
414ee40a6bSCésar Belley #include <sys/ioctl.h>
424ee40a6bSCésar Belley 
43299976b0SCésar Belley #define NONCE_SIZE 8
44299976b0SCésar Belley #define BROADCAST_CID 0xFFFFFFFF
45299976b0SCésar Belley #define TRANSACTION_TIMEOUT 120000
46299976b0SCésar Belley 
47299976b0SCésar Belley struct transaction {
48299976b0SCésar Belley     uint32_t cid;
49299976b0SCésar Belley     uint16_t resp_bcnt;
50299976b0SCésar Belley     uint16_t resp_size;
51299976b0SCésar Belley 
52299976b0SCésar Belley     /* Nonce for broadcast isolation */
53299976b0SCésar Belley     uint8_t nonce[NONCE_SIZE];
54299976b0SCésar Belley };
55299976b0SCésar Belley 
56299976b0SCésar Belley typedef struct U2FPassthruState U2FPassthruState;
57299976b0SCésar Belley 
58299976b0SCésar Belley #define CURRENT_TRANSACTIONS_NUM 4
59299976b0SCésar Belley 
60299976b0SCésar Belley struct U2FPassthruState {
61299976b0SCésar Belley     U2FKeyState base;
62299976b0SCésar Belley 
63299976b0SCésar Belley     /* Host device */
64299976b0SCésar Belley     char *hidraw;
65299976b0SCésar Belley     int hidraw_fd;
66299976b0SCésar Belley 
67299976b0SCésar Belley     /* Current Transactions */
68299976b0SCésar Belley     struct transaction current_transactions[CURRENT_TRANSACTIONS_NUM];
69299976b0SCésar Belley     uint8_t current_transactions_start;
70299976b0SCésar Belley     uint8_t current_transactions_end;
71299976b0SCésar Belley     uint8_t current_transactions_num;
72299976b0SCésar Belley 
73299976b0SCésar Belley     /* Transaction time checking */
74299976b0SCésar Belley     int64_t last_transaction_time;
75299976b0SCésar Belley     QEMUTimer timer;
76299976b0SCésar Belley };
77299976b0SCésar Belley 
78299976b0SCésar Belley #define TYPE_U2F_PASSTHRU "u2f-passthru"
79299976b0SCésar Belley #define PASSTHRU_U2F_KEY(obj) \
80299976b0SCésar Belley     OBJECT_CHECK(U2FPassthruState, (obj), TYPE_U2F_PASSTHRU)
81299976b0SCésar Belley 
82299976b0SCésar Belley /* Init packet sizes */
83299976b0SCésar Belley #define PACKET_INIT_HEADER_SIZE 7
84299976b0SCésar Belley #define PACKET_INIT_DATA_SIZE (U2FHID_PACKET_SIZE - PACKET_INIT_HEADER_SIZE)
85299976b0SCésar Belley 
86299976b0SCésar Belley /* Cont packet sizes */
87299976b0SCésar Belley #define PACKET_CONT_HEADER_SIZE 5
88299976b0SCésar Belley #define PACKET_CONT_DATA_SIZE (U2FHID_PACKET_SIZE - PACKET_CONT_HEADER_SIZE)
89299976b0SCésar Belley 
90299976b0SCésar Belley struct packet_init {
91299976b0SCésar Belley     uint32_t cid;
92299976b0SCésar Belley     uint8_t cmd;
93299976b0SCésar Belley     uint8_t bcnth;
94299976b0SCésar Belley     uint8_t bcntl;
95299976b0SCésar Belley     uint8_t data[PACKET_INIT_DATA_SIZE];
96299976b0SCésar Belley } QEMU_PACKED;
97299976b0SCésar Belley 
packet_get_cid(const void * packet)98299976b0SCésar Belley static inline uint32_t packet_get_cid(const void *packet)
99299976b0SCésar Belley {
100299976b0SCésar Belley     return *((uint32_t *)packet);
101299976b0SCésar Belley }
102299976b0SCésar Belley 
packet_is_init(const void * packet)103299976b0SCésar Belley static inline bool packet_is_init(const void *packet)
104299976b0SCésar Belley {
105299976b0SCésar Belley     return ((uint8_t *)packet)[4] & (1 << 7);
106299976b0SCésar Belley }
107299976b0SCésar Belley 
packet_init_get_bcnt(const struct packet_init * packet_init)108299976b0SCésar Belley static inline uint16_t packet_init_get_bcnt(
109299976b0SCésar Belley         const struct packet_init *packet_init)
110299976b0SCésar Belley {
111299976b0SCésar Belley     uint16_t bcnt = 0;
112299976b0SCésar Belley     bcnt |= packet_init->bcnth << 8;
113299976b0SCésar Belley     bcnt |= packet_init->bcntl;
114299976b0SCésar Belley 
115299976b0SCésar Belley     return bcnt;
116299976b0SCésar Belley }
117299976b0SCésar Belley 
u2f_passthru_reset(U2FPassthruState * key)118299976b0SCésar Belley static void u2f_passthru_reset(U2FPassthruState *key)
119299976b0SCésar Belley {
120299976b0SCésar Belley     timer_del(&key->timer);
121299976b0SCésar Belley     qemu_set_fd_handler(key->hidraw_fd, NULL, NULL, key);
122299976b0SCésar Belley     key->last_transaction_time = 0;
123299976b0SCésar Belley     key->current_transactions_start = 0;
124299976b0SCésar Belley     key->current_transactions_end = 0;
125299976b0SCésar Belley     key->current_transactions_num = 0;
126299976b0SCésar Belley }
127299976b0SCésar Belley 
u2f_timeout_check(void * opaque)128299976b0SCésar Belley static void u2f_timeout_check(void *opaque)
129299976b0SCésar Belley {
130299976b0SCésar Belley     U2FPassthruState *key = opaque;
131299976b0SCésar Belley     int64_t time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
132299976b0SCésar Belley 
133299976b0SCésar Belley     if (time > key->last_transaction_time + TRANSACTION_TIMEOUT) {
134299976b0SCésar Belley         u2f_passthru_reset(key);
135299976b0SCésar Belley     } else {
136299976b0SCésar Belley         timer_mod(&key->timer, time + TRANSACTION_TIMEOUT / 4);
137299976b0SCésar Belley     }
138299976b0SCésar Belley }
139299976b0SCésar Belley 
u2f_transaction_get_index(U2FPassthruState * key,uint32_t cid)140299976b0SCésar Belley static int u2f_transaction_get_index(U2FPassthruState *key, uint32_t cid)
141299976b0SCésar Belley {
142299976b0SCésar Belley     for (int i = 0; i < key->current_transactions_num; ++i) {
143299976b0SCésar Belley         int index = (key->current_transactions_start + i)
144299976b0SCésar Belley             % CURRENT_TRANSACTIONS_NUM;
145299976b0SCésar Belley         if (cid == key->current_transactions[index].cid) {
146299976b0SCésar Belley             return index;
147299976b0SCésar Belley         }
148299976b0SCésar Belley     }
149299976b0SCésar Belley     return -1;
150299976b0SCésar Belley }
151299976b0SCésar Belley 
u2f_transaction_get(U2FPassthruState * key,uint32_t cid)152299976b0SCésar Belley static struct transaction *u2f_transaction_get(U2FPassthruState *key,
153299976b0SCésar Belley                                                uint32_t cid)
154299976b0SCésar Belley {
155299976b0SCésar Belley     int index = u2f_transaction_get_index(key, cid);
156299976b0SCésar Belley     if (index < 0) {
157299976b0SCésar Belley         return NULL;
158299976b0SCésar Belley     }
159299976b0SCésar Belley     return &key->current_transactions[index];
160299976b0SCésar Belley }
161299976b0SCésar Belley 
u2f_transaction_get_from_nonce(U2FPassthruState * key,const uint8_t nonce[NONCE_SIZE])162299976b0SCésar Belley static struct transaction *u2f_transaction_get_from_nonce(U2FPassthruState *key,
163299976b0SCésar Belley                                 const uint8_t nonce[NONCE_SIZE])
164299976b0SCésar Belley {
165299976b0SCésar Belley     for (int i = 0; i < key->current_transactions_num; ++i) {
166299976b0SCésar Belley         int index = (key->current_transactions_start + i)
167299976b0SCésar Belley             % CURRENT_TRANSACTIONS_NUM;
168299976b0SCésar Belley         if (key->current_transactions[index].cid == BROADCAST_CID
169299976b0SCésar Belley             && memcmp(nonce, key->current_transactions[index].nonce,
170299976b0SCésar Belley                       NONCE_SIZE) == 0) {
171299976b0SCésar Belley             return &key->current_transactions[index];
172299976b0SCésar Belley         }
173299976b0SCésar Belley     }
174299976b0SCésar Belley     return NULL;
175299976b0SCésar Belley }
176299976b0SCésar Belley 
u2f_transaction_close(U2FPassthruState * key,uint32_t cid)177299976b0SCésar Belley static void u2f_transaction_close(U2FPassthruState *key, uint32_t cid)
178299976b0SCésar Belley {
179299976b0SCésar Belley     int index, next_index;
180299976b0SCésar Belley     index = u2f_transaction_get_index(key, cid);
181299976b0SCésar Belley     if (index < 0) {
182299976b0SCésar Belley         return;
183299976b0SCésar Belley     }
184299976b0SCésar Belley     next_index = (index + 1) % CURRENT_TRANSACTIONS_NUM;
185299976b0SCésar Belley 
186299976b0SCésar Belley     /* Rearrange to ensure the oldest is at the start position */
187299976b0SCésar Belley     while (next_index != key->current_transactions_end) {
188299976b0SCésar Belley         memcpy(&key->current_transactions[index],
189299976b0SCésar Belley                &key->current_transactions[next_index],
190299976b0SCésar Belley                sizeof(struct transaction));
191299976b0SCésar Belley 
192299976b0SCésar Belley         index = next_index;
193299976b0SCésar Belley         next_index = (index + 1) % CURRENT_TRANSACTIONS_NUM;
194299976b0SCésar Belley     }
195299976b0SCésar Belley 
196299976b0SCésar Belley     key->current_transactions_end = index;
197299976b0SCésar Belley     --key->current_transactions_num;
198299976b0SCésar Belley 
199299976b0SCésar Belley     if (key->current_transactions_num == 0) {
200299976b0SCésar Belley         u2f_passthru_reset(key);
201299976b0SCésar Belley     }
202299976b0SCésar Belley }
203299976b0SCésar Belley 
u2f_transaction_add(U2FPassthruState * key,uint32_t cid,const uint8_t nonce[NONCE_SIZE])204299976b0SCésar Belley static void u2f_transaction_add(U2FPassthruState *key, uint32_t cid,
205299976b0SCésar Belley                                 const uint8_t nonce[NONCE_SIZE])
206299976b0SCésar Belley {
207299976b0SCésar Belley     uint8_t index;
208299976b0SCésar Belley     struct transaction *transaction;
209299976b0SCésar Belley 
210299976b0SCésar Belley     if (key->current_transactions_num >= CURRENT_TRANSACTIONS_NUM) {
211299976b0SCésar Belley         /* Close the oldest transaction */
212299976b0SCésar Belley         index = key->current_transactions_start;
213299976b0SCésar Belley         transaction = &key->current_transactions[index];
214299976b0SCésar Belley         u2f_transaction_close(key, transaction->cid);
215299976b0SCésar Belley     }
216299976b0SCésar Belley 
217299976b0SCésar Belley     /* Index */
218299976b0SCésar Belley     index = key->current_transactions_end;
219299976b0SCésar Belley     key->current_transactions_end = (index + 1) % CURRENT_TRANSACTIONS_NUM;
220299976b0SCésar Belley     ++key->current_transactions_num;
221299976b0SCésar Belley 
222299976b0SCésar Belley     /* Transaction */
223299976b0SCésar Belley     transaction = &key->current_transactions[index];
224299976b0SCésar Belley     transaction->cid = cid;
225299976b0SCésar Belley     transaction->resp_bcnt = 0;
226299976b0SCésar Belley     transaction->resp_size = 0;
227299976b0SCésar Belley 
228299976b0SCésar Belley     /* Nonce */
229299976b0SCésar Belley     if (nonce != NULL) {
230299976b0SCésar Belley         memcpy(transaction->nonce, nonce, NONCE_SIZE);
231299976b0SCésar Belley     }
232299976b0SCésar Belley }
233299976b0SCésar Belley 
234299976b0SCésar Belley static void u2f_passthru_read(void *opaque);
235299976b0SCésar Belley 
u2f_transaction_start(U2FPassthruState * key,const struct packet_init * packet_init)236299976b0SCésar Belley static void u2f_transaction_start(U2FPassthruState *key,
237299976b0SCésar Belley                                   const struct packet_init *packet_init)
238299976b0SCésar Belley {
239299976b0SCésar Belley     int64_t time;
240299976b0SCésar Belley 
241299976b0SCésar Belley     /* Transaction */
242299976b0SCésar Belley     if (packet_init->cid == BROADCAST_CID) {
243299976b0SCésar Belley         u2f_transaction_add(key, packet_init->cid, packet_init->data);
244299976b0SCésar Belley     } else {
245299976b0SCésar Belley         u2f_transaction_add(key, packet_init->cid, NULL);
246299976b0SCésar Belley     }
247299976b0SCésar Belley 
248299976b0SCésar Belley     /* Time */
249299976b0SCésar Belley     time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
250299976b0SCésar Belley     if (key->last_transaction_time == 0) {
251299976b0SCésar Belley         qemu_set_fd_handler(key->hidraw_fd, u2f_passthru_read, NULL, key);
252299976b0SCésar Belley         timer_init_ms(&key->timer, QEMU_CLOCK_VIRTUAL, u2f_timeout_check, key);
253299976b0SCésar Belley         timer_mod(&key->timer, time + TRANSACTION_TIMEOUT / 4);
254299976b0SCésar Belley     }
255299976b0SCésar Belley     key->last_transaction_time = time;
256299976b0SCésar Belley }
257299976b0SCésar Belley 
u2f_passthru_recv_from_host(U2FPassthruState * key,const uint8_t packet[U2FHID_PACKET_SIZE])258299976b0SCésar Belley static void u2f_passthru_recv_from_host(U2FPassthruState *key,
259299976b0SCésar Belley                                     const uint8_t packet[U2FHID_PACKET_SIZE])
260299976b0SCésar Belley {
261299976b0SCésar Belley     struct transaction *transaction;
262299976b0SCésar Belley     uint32_t cid;
263299976b0SCésar Belley 
264299976b0SCésar Belley     /* Retrieve transaction */
265299976b0SCésar Belley     cid = packet_get_cid(packet);
266299976b0SCésar Belley     if (cid == BROADCAST_CID) {
267299976b0SCésar Belley         struct packet_init *packet_init;
268299976b0SCésar Belley         if (!packet_is_init(packet)) {
269299976b0SCésar Belley             return;
270299976b0SCésar Belley         }
271299976b0SCésar Belley         packet_init = (struct packet_init *)packet;
272299976b0SCésar Belley         transaction = u2f_transaction_get_from_nonce(key, packet_init->data);
273299976b0SCésar Belley     } else {
274299976b0SCésar Belley         transaction = u2f_transaction_get(key, cid);
275299976b0SCésar Belley     }
276299976b0SCésar Belley 
277299976b0SCésar Belley     /* Ignore no started transaction */
278299976b0SCésar Belley     if (transaction == NULL) {
279299976b0SCésar Belley         return;
280299976b0SCésar Belley     }
281299976b0SCésar Belley 
282299976b0SCésar Belley     if (packet_is_init(packet)) {
283299976b0SCésar Belley         struct packet_init *packet_init = (struct packet_init *)packet;
284299976b0SCésar Belley         transaction->resp_bcnt = packet_init_get_bcnt(packet_init);
285299976b0SCésar Belley         transaction->resp_size = PACKET_INIT_DATA_SIZE;
286299976b0SCésar Belley 
287299976b0SCésar Belley         if (packet_init->cid == BROADCAST_CID) {
288299976b0SCésar Belley             /* Nonce checking for legitimate response */
289299976b0SCésar Belley             if (memcmp(transaction->nonce, packet_init->data, NONCE_SIZE)
290299976b0SCésar Belley                 != 0) {
291299976b0SCésar Belley                 return;
292299976b0SCésar Belley             }
293299976b0SCésar Belley         }
294299976b0SCésar Belley     } else {
295299976b0SCésar Belley         transaction->resp_size += PACKET_CONT_DATA_SIZE;
296299976b0SCésar Belley     }
297299976b0SCésar Belley 
298299976b0SCésar Belley     /* Transaction end check */
299299976b0SCésar Belley     if (transaction->resp_size >= transaction->resp_bcnt) {
300299976b0SCésar Belley         u2f_transaction_close(key, cid);
301299976b0SCésar Belley     }
302299976b0SCésar Belley     u2f_send_to_guest(&key->base, packet);
303299976b0SCésar Belley }
304299976b0SCésar Belley 
u2f_passthru_read(void * opaque)305299976b0SCésar Belley static void u2f_passthru_read(void *opaque)
306299976b0SCésar Belley {
307299976b0SCésar Belley     U2FPassthruState *key = opaque;
308299976b0SCésar Belley     U2FKeyState *base = &key->base;
309299976b0SCésar Belley     uint8_t packet[2 * U2FHID_PACKET_SIZE];
310299976b0SCésar Belley     int ret;
311299976b0SCésar Belley 
312299976b0SCésar Belley     /* Full size base queue check */
313299976b0SCésar Belley     if (base->pending_in_num >= U2FHID_PENDING_IN_NUM) {
314299976b0SCésar Belley         return;
315299976b0SCésar Belley     }
316299976b0SCésar Belley 
317299976b0SCésar Belley     ret = read(key->hidraw_fd, packet, sizeof(packet));
318299976b0SCésar Belley     if (ret < 0) {
319299976b0SCésar Belley         /* Detach */
320299976b0SCésar Belley         if (base->dev.attached) {
321299976b0SCésar Belley             usb_device_detach(&base->dev);
322299976b0SCésar Belley             u2f_passthru_reset(key);
323299976b0SCésar Belley         }
324299976b0SCésar Belley         return;
325299976b0SCésar Belley     }
326299976b0SCésar Belley     if (ret != U2FHID_PACKET_SIZE) {
327299976b0SCésar Belley         return;
328299976b0SCésar Belley     }
329299976b0SCésar Belley     u2f_passthru_recv_from_host(key, packet);
330299976b0SCésar Belley }
331299976b0SCésar Belley 
u2f_passthru_recv_from_guest(U2FKeyState * base,const uint8_t packet[U2FHID_PACKET_SIZE])332299976b0SCésar Belley static void u2f_passthru_recv_from_guest(U2FKeyState *base,
333299976b0SCésar Belley                                     const uint8_t packet[U2FHID_PACKET_SIZE])
334299976b0SCésar Belley {
335299976b0SCésar Belley     U2FPassthruState *key = PASSTHRU_U2F_KEY(base);
336299976b0SCésar Belley     uint8_t host_packet[U2FHID_PACKET_SIZE + 1];
337299976b0SCésar Belley     ssize_t written;
338299976b0SCésar Belley 
339299976b0SCésar Belley     if (packet_is_init(packet)) {
340299976b0SCésar Belley         u2f_transaction_start(key, (struct packet_init *)packet);
341299976b0SCésar Belley     }
342299976b0SCésar Belley 
343299976b0SCésar Belley     host_packet[0] = 0;
344299976b0SCésar Belley     memcpy(host_packet + 1, packet, U2FHID_PACKET_SIZE);
345299976b0SCésar Belley 
346299976b0SCésar Belley     written = write(key->hidraw_fd, host_packet, sizeof(host_packet));
347299976b0SCésar Belley     if (written != sizeof(host_packet)) {
348299976b0SCésar Belley         error_report("%s: Bad written size (req 0x%zu, val 0x%zd)",
349299976b0SCésar Belley                      TYPE_U2F_PASSTHRU, sizeof(host_packet), written);
350299976b0SCésar Belley     }
351299976b0SCésar Belley }
352299976b0SCésar Belley 
u2f_passthru_is_u2f_device(int fd)3534ee40a6bSCésar Belley static bool u2f_passthru_is_u2f_device(int fd)
3544ee40a6bSCésar Belley {
3554ee40a6bSCésar Belley     int ret, rdesc_size;
3564ee40a6bSCésar Belley     struct hidraw_report_descriptor rdesc;
3574ee40a6bSCésar Belley     const uint8_t u2f_hid_report_desc_header[] = {
3584ee40a6bSCésar Belley         0x06, 0xd0, 0xf1, /* Usage Page (FIDO) */
3594ee40a6bSCésar Belley         0x09, 0x01,       /* Usage (FIDO) */
3604ee40a6bSCésar Belley     };
3614ee40a6bSCésar Belley 
3624ee40a6bSCésar Belley     /* Get report descriptor size */
3634ee40a6bSCésar Belley     ret = ioctl(fd, HIDIOCGRDESCSIZE, &rdesc_size);
3644ee40a6bSCésar Belley     if (ret < 0 || rdesc_size < sizeof(u2f_hid_report_desc_header)) {
3654ee40a6bSCésar Belley         return false;
3664ee40a6bSCésar Belley     }
3674ee40a6bSCésar Belley 
3684ee40a6bSCésar Belley     /* Get report descriptor */
3694ee40a6bSCésar Belley     memset(&rdesc, 0x0, sizeof(rdesc));
3704ee40a6bSCésar Belley     rdesc.size = rdesc_size;
3714ee40a6bSCésar Belley     ret = ioctl(fd, HIDIOCGRDESC, &rdesc);
3724ee40a6bSCésar Belley     if (ret < 0) {
3734ee40a6bSCésar Belley         return false;
3744ee40a6bSCésar Belley     }
3754ee40a6bSCésar Belley 
3764ee40a6bSCésar Belley     /* Header bytes cover specific U2F rdesc values */
3774ee40a6bSCésar Belley     return memcmp(u2f_hid_report_desc_header, rdesc.value,
3784ee40a6bSCésar Belley                   sizeof(u2f_hid_report_desc_header)) == 0;
3794ee40a6bSCésar Belley }
3804ee40a6bSCésar Belley 
381d7c1523fSCésar Belley #ifdef CONFIG_LIBUDEV
u2f_passthru_open_from_device(struct udev_device * device)382d7c1523fSCésar Belley static int u2f_passthru_open_from_device(struct udev_device *device)
383d7c1523fSCésar Belley {
384d7c1523fSCésar Belley     const char *devnode = udev_device_get_devnode(device);
385d7c1523fSCésar Belley 
386448058aaSDaniel P. Berrangé     int fd = qemu_open_old(devnode, O_RDWR);
387d7c1523fSCésar Belley     if (fd < 0) {
388d7c1523fSCésar Belley         return -1;
389d7c1523fSCésar Belley     } else if (!u2f_passthru_is_u2f_device(fd)) {
390d7c1523fSCésar Belley         qemu_close(fd);
391d7c1523fSCésar Belley         return -1;
392d7c1523fSCésar Belley     }
393d7c1523fSCésar Belley     return fd;
394d7c1523fSCésar Belley }
395d7c1523fSCésar Belley 
u2f_passthru_open_from_enumerate(struct udev * udev,struct udev_enumerate * enumerate)396d7c1523fSCésar Belley static int u2f_passthru_open_from_enumerate(struct udev *udev,
397d7c1523fSCésar Belley                                             struct udev_enumerate *enumerate)
398d7c1523fSCésar Belley {
399d7c1523fSCésar Belley     struct udev_list_entry *devices, *entry;
400d7c1523fSCésar Belley     int ret, fd;
401d7c1523fSCésar Belley 
402d7c1523fSCésar Belley     ret = udev_enumerate_scan_devices(enumerate);
403d7c1523fSCésar Belley     if (ret < 0) {
404d7c1523fSCésar Belley         return -1;
405d7c1523fSCésar Belley     }
406d7c1523fSCésar Belley 
407d7c1523fSCésar Belley     devices = udev_enumerate_get_list_entry(enumerate);
408d7c1523fSCésar Belley     udev_list_entry_foreach(entry, devices) {
409d7c1523fSCésar Belley         struct udev_device *device;
410d7c1523fSCésar Belley         const char *syspath = udev_list_entry_get_name(entry);
411d7c1523fSCésar Belley 
412d7c1523fSCésar Belley         if (syspath == NULL) {
413d7c1523fSCésar Belley             continue;
414d7c1523fSCésar Belley         }
415d7c1523fSCésar Belley 
416d7c1523fSCésar Belley         device = udev_device_new_from_syspath(udev, syspath);
417d7c1523fSCésar Belley         if (device == NULL) {
418d7c1523fSCésar Belley             continue;
419d7c1523fSCésar Belley         }
420d7c1523fSCésar Belley 
421d7c1523fSCésar Belley         fd = u2f_passthru_open_from_device(device);
422d7c1523fSCésar Belley         udev_device_unref(device);
423d7c1523fSCésar Belley         if (fd >= 0) {
424d7c1523fSCésar Belley             return fd;
425d7c1523fSCésar Belley         }
426d7c1523fSCésar Belley     }
427d7c1523fSCésar Belley     return -1;
428d7c1523fSCésar Belley }
429d7c1523fSCésar Belley 
u2f_passthru_open_from_scan(void)430d7c1523fSCésar Belley static int u2f_passthru_open_from_scan(void)
431d7c1523fSCésar Belley {
432d7c1523fSCésar Belley     struct udev *udev;
433d7c1523fSCésar Belley     struct udev_enumerate *enumerate;
434d7c1523fSCésar Belley     int ret, fd = -1;
435d7c1523fSCésar Belley 
436d7c1523fSCésar Belley     udev = udev_new();
437d7c1523fSCésar Belley     if (udev == NULL) {
438d7c1523fSCésar Belley         return -1;
439d7c1523fSCésar Belley     }
440d7c1523fSCésar Belley 
441d7c1523fSCésar Belley     enumerate = udev_enumerate_new(udev);
442d7c1523fSCésar Belley     if (enumerate == NULL) {
443d7c1523fSCésar Belley         udev_unref(udev);
444d7c1523fSCésar Belley         return -1;
445d7c1523fSCésar Belley     }
446d7c1523fSCésar Belley 
447d7c1523fSCésar Belley     ret = udev_enumerate_add_match_subsystem(enumerate, "hidraw");
448d7c1523fSCésar Belley     if (ret >= 0) {
449d7c1523fSCésar Belley         fd = u2f_passthru_open_from_enumerate(udev, enumerate);
450d7c1523fSCésar Belley     }
451d7c1523fSCésar Belley 
452d7c1523fSCésar Belley     udev_enumerate_unref(enumerate);
453d7c1523fSCésar Belley     udev_unref(udev);
454d7c1523fSCésar Belley 
455d7c1523fSCésar Belley     return fd;
456d7c1523fSCésar Belley }
457d7c1523fSCésar Belley #endif
458d7c1523fSCésar Belley 
u2f_passthru_unrealize(U2FKeyState * base)459299976b0SCésar Belley static void u2f_passthru_unrealize(U2FKeyState *base)
460299976b0SCésar Belley {
461299976b0SCésar Belley     U2FPassthruState *key = PASSTHRU_U2F_KEY(base);
462299976b0SCésar Belley 
463299976b0SCésar Belley     u2f_passthru_reset(key);
464299976b0SCésar Belley     qemu_close(key->hidraw_fd);
465299976b0SCésar Belley }
466299976b0SCésar Belley 
u2f_passthru_realize(U2FKeyState * base,Error ** errp)467299976b0SCésar Belley static void u2f_passthru_realize(U2FKeyState *base, Error **errp)
468299976b0SCésar Belley {
469299976b0SCésar Belley     U2FPassthruState *key = PASSTHRU_U2F_KEY(base);
470299976b0SCésar Belley     int fd;
471299976b0SCésar Belley 
472299976b0SCésar Belley     if (key->hidraw == NULL) {
473d7c1523fSCésar Belley #ifdef CONFIG_LIBUDEV
474d7c1523fSCésar Belley         fd = u2f_passthru_open_from_scan();
475d7c1523fSCésar Belley         if (fd < 0) {
476d7c1523fSCésar Belley             error_setg(errp, "%s: Failed to find a U2F USB device",
477d7c1523fSCésar Belley                        TYPE_U2F_PASSTHRU);
478299976b0SCésar Belley             return;
479299976b0SCésar Belley         }
480d7c1523fSCésar Belley #else
481d7c1523fSCésar Belley         error_setg(errp, "%s: Missing hidraw", TYPE_U2F_PASSTHRU);
482d7c1523fSCésar Belley         return;
483d7c1523fSCésar Belley #endif
484d7c1523fSCésar Belley     } else {
485448058aaSDaniel P. Berrangé         fd = qemu_open_old(key->hidraw, O_RDWR);
486299976b0SCésar Belley         if (fd < 0) {
487299976b0SCésar Belley             error_setg(errp, "%s: Failed to open %s", TYPE_U2F_PASSTHRU,
488299976b0SCésar Belley                        key->hidraw);
489299976b0SCésar Belley             return;
490299976b0SCésar Belley         }
4914ee40a6bSCésar Belley 
4924ee40a6bSCésar Belley         if (!u2f_passthru_is_u2f_device(fd)) {
4934ee40a6bSCésar Belley             qemu_close(fd);
4944ee40a6bSCésar Belley             error_setg(errp, "%s: Passed hidraw does not represent "
4954ee40a6bSCésar Belley                        "a U2F HID device", TYPE_U2F_PASSTHRU);
4964ee40a6bSCésar Belley             return;
4974ee40a6bSCésar Belley         }
498d7c1523fSCésar Belley     }
499299976b0SCésar Belley     key->hidraw_fd = fd;
500299976b0SCésar Belley     u2f_passthru_reset(key);
501299976b0SCésar Belley }
502299976b0SCésar Belley 
u2f_passthru_post_load(void * opaque,int version_id)503299976b0SCésar Belley static int u2f_passthru_post_load(void *opaque, int version_id)
504299976b0SCésar Belley {
505299976b0SCésar Belley     U2FPassthruState *key = opaque;
506299976b0SCésar Belley     u2f_passthru_reset(key);
507299976b0SCésar Belley     return 0;
508299976b0SCésar Belley }
509299976b0SCésar Belley 
510299976b0SCésar Belley static const VMStateDescription u2f_passthru_vmstate = {
511299976b0SCésar Belley     .name = "u2f-key-passthru",
512299976b0SCésar Belley     .version_id = 1,
513299976b0SCésar Belley     .minimum_version_id = 1,
514299976b0SCésar Belley     .post_load = u2f_passthru_post_load,
515299976b0SCésar Belley     .fields = (VMStateField[]) {
516299976b0SCésar Belley         VMSTATE_U2F_KEY(base, U2FPassthruState),
517299976b0SCésar Belley         VMSTATE_END_OF_LIST()
518299976b0SCésar Belley     }
519299976b0SCésar Belley };
520299976b0SCésar Belley 
521299976b0SCésar Belley static Property u2f_passthru_properties[] = {
522299976b0SCésar Belley     DEFINE_PROP_STRING("hidraw", U2FPassthruState, hidraw),
523299976b0SCésar Belley     DEFINE_PROP_END_OF_LIST(),
524299976b0SCésar Belley };
525299976b0SCésar Belley 
u2f_passthru_class_init(ObjectClass * klass,void * data)526299976b0SCésar Belley static void u2f_passthru_class_init(ObjectClass *klass, void *data)
527299976b0SCésar Belley {
528299976b0SCésar Belley     DeviceClass *dc = DEVICE_CLASS(klass);
529299976b0SCésar Belley     U2FKeyClass *kc = U2F_KEY_CLASS(klass);
530299976b0SCésar Belley 
531299976b0SCésar Belley     kc->realize = u2f_passthru_realize;
532299976b0SCésar Belley     kc->unrealize = u2f_passthru_unrealize;
533299976b0SCésar Belley     kc->recv_from_guest = u2f_passthru_recv_from_guest;
534299976b0SCésar Belley     dc->desc = "QEMU U2F passthrough key";
535299976b0SCésar Belley     dc->vmsd = &u2f_passthru_vmstate;
536299976b0SCésar Belley     device_class_set_props(dc, u2f_passthru_properties);
537*50bf0b3dSGan Qixin     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
538299976b0SCésar Belley }
539299976b0SCésar Belley 
540299976b0SCésar Belley static const TypeInfo u2f_key_passthru_info = {
541299976b0SCésar Belley     .name = TYPE_U2F_PASSTHRU,
542299976b0SCésar Belley     .parent = TYPE_U2F_KEY,
543299976b0SCésar Belley     .instance_size = sizeof(U2FPassthruState),
544299976b0SCésar Belley     .class_init = u2f_passthru_class_init
545299976b0SCésar Belley };
546299976b0SCésar Belley 
u2f_key_passthru_register_types(void)547299976b0SCésar Belley static void u2f_key_passthru_register_types(void)
548299976b0SCésar Belley {
549299976b0SCésar Belley     type_register_static(&u2f_key_passthru_info);
550299976b0SCésar Belley }
551299976b0SCésar Belley 
552299976b0SCésar Belley type_init(u2f_key_passthru_register_types)
553