xref: /openbmc/qemu/hw/usb/u2f-passthru.c (revision 299976b0)
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 #define NONCE_SIZE 8
38 #define BROADCAST_CID 0xFFFFFFFF
39 #define TRANSACTION_TIMEOUT 120000
40 
41 struct transaction {
42     uint32_t cid;
43     uint16_t resp_bcnt;
44     uint16_t resp_size;
45 
46     /* Nonce for broadcast isolation */
47     uint8_t nonce[NONCE_SIZE];
48 };
49 
50 typedef struct U2FPassthruState U2FPassthruState;
51 
52 #define CURRENT_TRANSACTIONS_NUM 4
53 
54 struct U2FPassthruState {
55     U2FKeyState base;
56 
57     /* Host device */
58     char *hidraw;
59     int hidraw_fd;
60 
61     /* Current Transactions */
62     struct transaction current_transactions[CURRENT_TRANSACTIONS_NUM];
63     uint8_t current_transactions_start;
64     uint8_t current_transactions_end;
65     uint8_t current_transactions_num;
66 
67     /* Transaction time checking */
68     int64_t last_transaction_time;
69     QEMUTimer timer;
70 };
71 
72 #define TYPE_U2F_PASSTHRU "u2f-passthru"
73 #define PASSTHRU_U2F_KEY(obj) \
74     OBJECT_CHECK(U2FPassthruState, (obj), TYPE_U2F_PASSTHRU)
75 
76 /* Init packet sizes */
77 #define PACKET_INIT_HEADER_SIZE 7
78 #define PACKET_INIT_DATA_SIZE (U2FHID_PACKET_SIZE - PACKET_INIT_HEADER_SIZE)
79 
80 /* Cont packet sizes */
81 #define PACKET_CONT_HEADER_SIZE 5
82 #define PACKET_CONT_DATA_SIZE (U2FHID_PACKET_SIZE - PACKET_CONT_HEADER_SIZE)
83 
84 struct packet_init {
85     uint32_t cid;
86     uint8_t cmd;
87     uint8_t bcnth;
88     uint8_t bcntl;
89     uint8_t data[PACKET_INIT_DATA_SIZE];
90 } QEMU_PACKED;
91 
92 static inline uint32_t packet_get_cid(const void *packet)
93 {
94     return *((uint32_t *)packet);
95 }
96 
97 static inline bool packet_is_init(const void *packet)
98 {
99     return ((uint8_t *)packet)[4] & (1 << 7);
100 }
101 
102 static inline uint16_t packet_init_get_bcnt(
103         const struct packet_init *packet_init)
104 {
105     uint16_t bcnt = 0;
106     bcnt |= packet_init->bcnth << 8;
107     bcnt |= packet_init->bcntl;
108 
109     return bcnt;
110 }
111 
112 static void u2f_passthru_reset(U2FPassthruState *key)
113 {
114     timer_del(&key->timer);
115     qemu_set_fd_handler(key->hidraw_fd, NULL, NULL, key);
116     key->last_transaction_time = 0;
117     key->current_transactions_start = 0;
118     key->current_transactions_end = 0;
119     key->current_transactions_num = 0;
120 }
121 
122 static void u2f_timeout_check(void *opaque)
123 {
124     U2FPassthruState *key = opaque;
125     int64_t time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
126 
127     if (time > key->last_transaction_time + TRANSACTION_TIMEOUT) {
128         u2f_passthru_reset(key);
129     } else {
130         timer_mod(&key->timer, time + TRANSACTION_TIMEOUT / 4);
131     }
132 }
133 
134 static int u2f_transaction_get_index(U2FPassthruState *key, uint32_t cid)
135 {
136     for (int i = 0; i < key->current_transactions_num; ++i) {
137         int index = (key->current_transactions_start + i)
138             % CURRENT_TRANSACTIONS_NUM;
139         if (cid == key->current_transactions[index].cid) {
140             return index;
141         }
142     }
143     return -1;
144 }
145 
146 static struct transaction *u2f_transaction_get(U2FPassthruState *key,
147                                                uint32_t cid)
148 {
149     int index = u2f_transaction_get_index(key, cid);
150     if (index < 0) {
151         return NULL;
152     }
153     return &key->current_transactions[index];
154 }
155 
156 static struct transaction *u2f_transaction_get_from_nonce(U2FPassthruState *key,
157                                 const uint8_t nonce[NONCE_SIZE])
158 {
159     for (int i = 0; i < key->current_transactions_num; ++i) {
160         int index = (key->current_transactions_start + i)
161             % CURRENT_TRANSACTIONS_NUM;
162         if (key->current_transactions[index].cid == BROADCAST_CID
163             && memcmp(nonce, key->current_transactions[index].nonce,
164                       NONCE_SIZE) == 0) {
165             return &key->current_transactions[index];
166         }
167     }
168     return NULL;
169 }
170 
171 static void u2f_transaction_close(U2FPassthruState *key, uint32_t cid)
172 {
173     int index, next_index;
174     index = u2f_transaction_get_index(key, cid);
175     if (index < 0) {
176         return;
177     }
178     next_index = (index + 1) % CURRENT_TRANSACTIONS_NUM;
179 
180     /* Rearrange to ensure the oldest is at the start position */
181     while (next_index != key->current_transactions_end) {
182         memcpy(&key->current_transactions[index],
183                &key->current_transactions[next_index],
184                sizeof(struct transaction));
185 
186         index = next_index;
187         next_index = (index + 1) % CURRENT_TRANSACTIONS_NUM;
188     }
189 
190     key->current_transactions_end = index;
191     --key->current_transactions_num;
192 
193     if (key->current_transactions_num == 0) {
194         u2f_passthru_reset(key);
195     }
196 }
197 
198 static void u2f_transaction_add(U2FPassthruState *key, uint32_t cid,
199                                 const uint8_t nonce[NONCE_SIZE])
200 {
201     uint8_t index;
202     struct transaction *transaction;
203 
204     if (key->current_transactions_num >= CURRENT_TRANSACTIONS_NUM) {
205         /* Close the oldest transaction */
206         index = key->current_transactions_start;
207         transaction = &key->current_transactions[index];
208         u2f_transaction_close(key, transaction->cid);
209     }
210 
211     /* Index */
212     index = key->current_transactions_end;
213     key->current_transactions_end = (index + 1) % CURRENT_TRANSACTIONS_NUM;
214     ++key->current_transactions_num;
215 
216     /* Transaction */
217     transaction = &key->current_transactions[index];
218     transaction->cid = cid;
219     transaction->resp_bcnt = 0;
220     transaction->resp_size = 0;
221 
222     /* Nonce */
223     if (nonce != NULL) {
224         memcpy(transaction->nonce, nonce, NONCE_SIZE);
225     }
226 }
227 
228 static void u2f_passthru_read(void *opaque);
229 
230 static void u2f_transaction_start(U2FPassthruState *key,
231                                   const struct packet_init *packet_init)
232 {
233     int64_t time;
234 
235     /* Transaction */
236     if (packet_init->cid == BROADCAST_CID) {
237         u2f_transaction_add(key, packet_init->cid, packet_init->data);
238     } else {
239         u2f_transaction_add(key, packet_init->cid, NULL);
240     }
241 
242     /* Time */
243     time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
244     if (key->last_transaction_time == 0) {
245         qemu_set_fd_handler(key->hidraw_fd, u2f_passthru_read, NULL, key);
246         timer_init_ms(&key->timer, QEMU_CLOCK_VIRTUAL, u2f_timeout_check, key);
247         timer_mod(&key->timer, time + TRANSACTION_TIMEOUT / 4);
248     }
249     key->last_transaction_time = time;
250 }
251 
252 static void u2f_passthru_recv_from_host(U2FPassthruState *key,
253                                     const uint8_t packet[U2FHID_PACKET_SIZE])
254 {
255     struct transaction *transaction;
256     uint32_t cid;
257 
258     /* Retrieve transaction */
259     cid = packet_get_cid(packet);
260     if (cid == BROADCAST_CID) {
261         struct packet_init *packet_init;
262         if (!packet_is_init(packet)) {
263             return;
264         }
265         packet_init = (struct packet_init *)packet;
266         transaction = u2f_transaction_get_from_nonce(key, packet_init->data);
267     } else {
268         transaction = u2f_transaction_get(key, cid);
269     }
270 
271     /* Ignore no started transaction */
272     if (transaction == NULL) {
273         return;
274     }
275 
276     if (packet_is_init(packet)) {
277         struct packet_init *packet_init = (struct packet_init *)packet;
278         transaction->resp_bcnt = packet_init_get_bcnt(packet_init);
279         transaction->resp_size = PACKET_INIT_DATA_SIZE;
280 
281         if (packet_init->cid == BROADCAST_CID) {
282             /* Nonce checking for legitimate response */
283             if (memcmp(transaction->nonce, packet_init->data, NONCE_SIZE)
284                 != 0) {
285                 return;
286             }
287         }
288     } else {
289         transaction->resp_size += PACKET_CONT_DATA_SIZE;
290     }
291 
292     /* Transaction end check */
293     if (transaction->resp_size >= transaction->resp_bcnt) {
294         u2f_transaction_close(key, cid);
295     }
296     u2f_send_to_guest(&key->base, packet);
297 }
298 
299 static void u2f_passthru_read(void *opaque)
300 {
301     U2FPassthruState *key = opaque;
302     U2FKeyState *base = &key->base;
303     uint8_t packet[2 * U2FHID_PACKET_SIZE];
304     int ret;
305 
306     /* Full size base queue check */
307     if (base->pending_in_num >= U2FHID_PENDING_IN_NUM) {
308         return;
309     }
310 
311     ret = read(key->hidraw_fd, packet, sizeof(packet));
312     if (ret < 0) {
313         /* Detach */
314         if (base->dev.attached) {
315             usb_device_detach(&base->dev);
316             u2f_passthru_reset(key);
317         }
318         return;
319     }
320     if (ret != U2FHID_PACKET_SIZE) {
321         return;
322     }
323     u2f_passthru_recv_from_host(key, packet);
324 }
325 
326 static void u2f_passthru_recv_from_guest(U2FKeyState *base,
327                                     const uint8_t packet[U2FHID_PACKET_SIZE])
328 {
329     U2FPassthruState *key = PASSTHRU_U2F_KEY(base);
330     uint8_t host_packet[U2FHID_PACKET_SIZE + 1];
331     ssize_t written;
332 
333     if (packet_is_init(packet)) {
334         u2f_transaction_start(key, (struct packet_init *)packet);
335     }
336 
337     host_packet[0] = 0;
338     memcpy(host_packet + 1, packet, U2FHID_PACKET_SIZE);
339 
340     written = write(key->hidraw_fd, host_packet, sizeof(host_packet));
341     if (written != sizeof(host_packet)) {
342         error_report("%s: Bad written size (req 0x%zu, val 0x%zd)",
343                      TYPE_U2F_PASSTHRU, sizeof(host_packet), written);
344     }
345 }
346 
347 static void u2f_passthru_unrealize(U2FKeyState *base)
348 {
349     U2FPassthruState *key = PASSTHRU_U2F_KEY(base);
350 
351     u2f_passthru_reset(key);
352     qemu_close(key->hidraw_fd);
353 }
354 
355 static void u2f_passthru_realize(U2FKeyState *base, Error **errp)
356 {
357     U2FPassthruState *key = PASSTHRU_U2F_KEY(base);
358     int fd;
359 
360     if (key->hidraw == NULL) {
361         error_setg(errp, "%s: Missing hidraw", TYPE_U2F_PASSTHRU);
362         return;
363     }
364 
365     fd = qemu_open(key->hidraw, O_RDWR);
366     if (fd < 0) {
367         error_setg(errp, "%s: Failed to open %s", TYPE_U2F_PASSTHRU,
368                    key->hidraw);
369         return;
370     }
371     key->hidraw_fd = fd;
372     u2f_passthru_reset(key);
373 }
374 
375 static int u2f_passthru_post_load(void *opaque, int version_id)
376 {
377     U2FPassthruState *key = opaque;
378     u2f_passthru_reset(key);
379     return 0;
380 }
381 
382 static const VMStateDescription u2f_passthru_vmstate = {
383     .name = "u2f-key-passthru",
384     .version_id = 1,
385     .minimum_version_id = 1,
386     .post_load = u2f_passthru_post_load,
387     .fields = (VMStateField[]) {
388         VMSTATE_U2F_KEY(base, U2FPassthruState),
389         VMSTATE_END_OF_LIST()
390     }
391 };
392 
393 static Property u2f_passthru_properties[] = {
394     DEFINE_PROP_STRING("hidraw", U2FPassthruState, hidraw),
395     DEFINE_PROP_END_OF_LIST(),
396 };
397 
398 static void u2f_passthru_class_init(ObjectClass *klass, void *data)
399 {
400     DeviceClass *dc = DEVICE_CLASS(klass);
401     U2FKeyClass *kc = U2F_KEY_CLASS(klass);
402 
403     kc->realize = u2f_passthru_realize;
404     kc->unrealize = u2f_passthru_unrealize;
405     kc->recv_from_guest = u2f_passthru_recv_from_guest;
406     dc->desc = "QEMU U2F passthrough key";
407     dc->vmsd = &u2f_passthru_vmstate;
408     device_class_set_props(dc, u2f_passthru_properties);
409 }
410 
411 static const TypeInfo u2f_key_passthru_info = {
412     .name = TYPE_U2F_PASSTHRU,
413     .parent = TYPE_U2F_KEY,
414     .instance_size = sizeof(U2FPassthruState),
415     .class_init = u2f_passthru_class_init
416 };
417 
418 static void u2f_key_passthru_register_types(void)
419 {
420     type_register_static(&u2f_key_passthru_info);
421 }
422 
423 type_init(u2f_key_passthru_register_types)
424