Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/usb/backend/fido_backend/fido_passthrough.rs
5394 views
1
// Copyright 2024 The ChromiumOS Authors
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
use std::collections::VecDeque;
6
use std::io::Error as IOError;
7
use std::io::Read;
8
use std::sync::Arc;
9
use std::sync::RwLock;
10
11
use base::debug;
12
use base::error;
13
use base::AsRawDescriptor;
14
use base::Event;
15
use base::RawDescriptor;
16
use base::WorkerThread;
17
use sync::Mutex;
18
use usb_util::parse_usbfs_descriptors;
19
use usb_util::ConfigDescriptorTree;
20
use usb_util::ControlRequestDataPhaseTransferDirection;
21
use usb_util::ControlRequestRecipient;
22
use usb_util::ControlRequestType;
23
use usb_util::DescriptorType;
24
use usb_util::DeviceDescriptorTree;
25
use usb_util::DeviceSpeed;
26
use usb_util::EndpointDirection;
27
use usb_util::EndpointType;
28
use usb_util::Error as UsbUtilError;
29
use usb_util::TransferBuffer;
30
use usb_util::TransferStatus;
31
use usb_util::UsbRequestSetup;
32
use zerocopy::FromBytes;
33
use zerocopy::IntoBytes;
34
35
use crate::usb::backend::device::BackendDevice;
36
use crate::usb::backend::device::DeviceState;
37
use crate::usb::backend::endpoint::ControlEndpointState;
38
use crate::usb::backend::endpoint::UsbEndpoint;
39
use crate::usb::backend::error::Error as BackendError;
40
use crate::usb::backend::error::Result as BackendResult;
41
use crate::usb::backend::fido_backend::constants;
42
use crate::usb::backend::fido_backend::error::Error;
43
use crate::usb::backend::fido_backend::error::Result;
44
use crate::usb::backend::fido_backend::fido_device::FidoDevice;
45
use crate::usb::backend::fido_backend::poll_thread::poll_for_pending_packets;
46
use crate::usb::backend::fido_backend::transfer::FidoTransfer;
47
use crate::usb::backend::fido_backend::transfer::FidoTransferHandle;
48
use crate::usb::backend::transfer::BackendTransferHandle;
49
use crate::usb::backend::transfer::BackendTransferType;
50
use crate::usb::backend::transfer::ControlTransferState;
51
use crate::usb::backend::transfer::GenericTransferHandle;
52
use crate::usb::xhci::xhci_backend_device::BackendType;
53
use crate::usb::xhci::xhci_backend_device::UsbDeviceAddress;
54
use crate::usb::xhci::xhci_backend_device::XhciBackendDevice;
55
use crate::utils::AsyncJobQueue;
56
use crate::utils::EventLoop;
57
58
/// Host-level fido passthrough device that handles USB operations and relays them to the
59
/// appropriate virtual fido device.
60
pub struct FidoPassthroughDevice {
61
/// The virtual FIDO device implementation.
62
device: Arc<Mutex<FidoDevice>>,
63
/// The state of the device as seen by the backend provider.
64
state: Arc<RwLock<DeviceState>>,
65
/// The state of the control transfer exchange with the xhci layer.
66
control_transfer_state: Arc<RwLock<ControlTransferState>>,
67
transfer_job_queue: Arc<AsyncJobQueue>,
68
kill_evt: Event,
69
worker_thread: Option<WorkerThread<()>>,
70
pending_in_transfers:
71
Arc<Mutex<VecDeque<(FidoTransferHandle, Arc<Mutex<Option<FidoTransfer>>>)>>>,
72
}
73
74
impl FidoPassthroughDevice {
75
pub fn new(
76
device: Arc<Mutex<FidoDevice>>,
77
state: DeviceState,
78
event_loop: Arc<EventLoop>,
79
) -> Result<Self> {
80
let control_transfer_state = ControlTransferState {
81
ctl_ep_state: ControlEndpointState::SetupStage,
82
control_request_setup: UsbRequestSetup::new(0, 0, 0, 0, 0),
83
executed: false,
84
};
85
let job_queue = AsyncJobQueue::init(&event_loop).map_err(Error::StartAsyncFidoQueue)?;
86
Ok(FidoPassthroughDevice {
87
device,
88
state: Arc::new(RwLock::new(state)),
89
control_transfer_state: Arc::new(RwLock::new(control_transfer_state)),
90
transfer_job_queue: job_queue,
91
kill_evt: Event::new().unwrap(),
92
worker_thread: None,
93
pending_in_transfers: Arc::new(Mutex::new(VecDeque::new())),
94
})
95
}
96
97
/// This function is called from the low-level event handler when the monitored `fd` is ready
98
/// to transmit data from the host to the guest.
99
pub fn read_hidraw_file(&mut self) -> Result<()> {
100
let mut device = self.device.lock();
101
// Device has already stopped working, just return early.
102
if device.is_device_lost {
103
return Ok(());
104
}
105
if !device.is_active {
106
// We should NEVER be polling on the fd and wake up if no transactions have been
107
// initiated from the guest first.
108
error!("Fido device received fd poll event from inactive device. This is a bug.");
109
return Err(Error::InconsistentFidoDeviceState);
110
}
111
112
let mut packet = vec![0; constants::U2FHID_PACKET_SIZE * 2];
113
114
if device.guest_key.lock().pending_in_packets.len() >= constants::U2FHID_MAX_IN_PENDING {
115
return Err(Error::PendingInQueueFull);
116
}
117
118
let read_result = device.fd.lock().read(&mut packet);
119
match read_result {
120
Ok(n) => {
121
// We read too much, the device is misbehaving
122
if n != constants::U2FHID_PACKET_SIZE {
123
return Err(Error::ReadHidrawDevice(IOError::other(format!(
124
"Read too many bytes ({n}), the hidraw device is misbehaving."
125
))));
126
}
127
// This is safe because we just checked the size of n is exactly U2FHID_PACKET_SIZE
128
device
129
.recv_from_host(&packet[..constants::U2FHID_PACKET_SIZE].try_into().unwrap())?;
130
}
131
Err(e) => {
132
error!("U2F hidraw read error: {e:#}, resetting and detaching device",);
133
device.set_active(false);
134
device.is_device_lost = true;
135
return Err(Error::ReadHidrawDevice(e));
136
}
137
}
138
Ok(())
139
}
140
141
/// This function is called by a queued job to handle all communication related to USB control
142
/// transfer packets between the guest and the virtual security key.
143
pub fn handle_control(
144
transfer: &mut FidoTransfer,
145
device: &Arc<Mutex<FidoDevice>>,
146
) -> Result<()> {
147
transfer.actual_length = 0;
148
let request_setup = match &transfer.buffer {
149
TransferBuffer::Vector(v) => {
150
UsbRequestSetup::read_from_prefix(v)
151
.map_err(|_| Error::InvalidDataBufferSize)?
152
.0
153
}
154
_ => {
155
return Err(Error::UnsupportedTransferBufferType);
156
}
157
};
158
159
let mut request_setup_out = request_setup.as_bytes().to_vec();
160
let is_device_to_host =
161
request_setup.get_direction() == ControlRequestDataPhaseTransferDirection::DeviceToHost;
162
let descriptor_type = (request_setup.value >> 8) as u8;
163
164
// Get Device Descriptor request
165
if descriptor_type == (DescriptorType::Device as u8) && is_device_to_host {
166
// If the descriptor is larger than the actual requested data, we only allocate space
167
// for the request size. This is common for USB3 control setup to request only the
168
// initial 8 bytes instead of the full descriptor.
169
let buf_size = std::cmp::min(
170
request_setup.length.into(),
171
constants::U2FHID_DEVICE_DESC.len(),
172
);
173
let mut buffer: Vec<u8> = constants::U2FHID_DEVICE_DESC[..buf_size].to_vec();
174
transfer.actual_length = buffer.len();
175
request_setup_out.append(&mut buffer);
176
}
177
178
if request_setup.get_recipient() == ControlRequestRecipient::Interface {
179
// It's a request for the HID report descriptor
180
if is_device_to_host && descriptor_type == constants::HID_GET_REPORT_DESC {
181
let mut buffer: Vec<u8> = constants::HID_REPORT_DESC.to_vec();
182
transfer.actual_length = buffer.len();
183
request_setup_out.append(&mut buffer);
184
}
185
}
186
187
if request_setup.get_type() == ControlRequestType::Class {
188
match request_setup.request {
189
constants::HID_GET_IDLE => {
190
let mut buffer: Vec<u8> = vec![0u8, 1];
191
buffer[0] = device.lock().guest_key.lock().idle;
192
transfer.actual_length = 1;
193
request_setup_out.append(&mut buffer);
194
}
195
constants::HID_SET_IDLE => {
196
device.lock().guest_key.lock().idle = (request_setup.value >> 8) as u8;
197
}
198
_ => {
199
debug!(
200
"Received unsupported setup request code of Class type: {}",
201
request_setup.request
202
);
203
}
204
}
205
}
206
207
// Store the response
208
transfer.buffer = TransferBuffer::Vector(request_setup_out);
209
Ok(())
210
}
211
212
/// This function is called by a queued job to handle all USB OUT requests from the guest down
213
/// to the host by writing the given `FidoTransfer` data into the hidraw file.
214
pub fn handle_interrupt_out(
215
transfer: &mut FidoTransfer,
216
device: &Arc<Mutex<FidoDevice>>,
217
) -> Result<()> {
218
let mut packet = [0u8; constants::U2FHID_PACKET_SIZE];
219
let buffer = match &transfer.buffer {
220
TransferBuffer::Vector(v) => v,
221
_ => {
222
return Err(Error::UnsupportedTransferBufferType);
223
}
224
};
225
if buffer.len() > constants::U2FHID_PACKET_SIZE {
226
error!(
227
"Buffer size is bigger than u2f-hid packet size: {}",
228
buffer.len()
229
);
230
return Err(Error::InvalidDataBufferSize);
231
}
232
packet.copy_from_slice(buffer);
233
let written = device.lock().recv_from_guest(&packet)?;
234
transfer.actual_length = written;
235
Ok(())
236
}
237
}
238
239
impl Drop for FidoPassthroughDevice {
240
fn drop(&mut self) {
241
self.device.lock().is_device_lost = true;
242
if let Err(e) = self.kill_evt.signal() {
243
error!(
244
"Failed to send signal to stop poll worker thread, \
245
it might have already stopped. {e:#}"
246
);
247
}
248
}
249
}
250
251
impl AsRawDescriptor for FidoPassthroughDevice {
252
fn as_raw_descriptor(&self) -> RawDescriptor {
253
self.device.lock().as_raw_descriptor()
254
}
255
}
256
257
impl BackendDevice for FidoPassthroughDevice {
258
fn submit_backend_transfer(
259
&mut self,
260
transfer: BackendTransferType,
261
) -> BackendResult<BackendTransferHandle> {
262
let transfer = match transfer {
263
BackendTransferType::FidoDevice(transfer) => transfer,
264
_ => return Err(BackendError::MalformedBackendTransfer),
265
};
266
267
let endpoint = transfer.endpoint;
268
let arc_transfer = Arc::new(Mutex::new(Some(transfer)));
269
let cancel_handle = FidoTransferHandle {
270
weak_transfer: Arc::downgrade(&arc_transfer),
271
};
272
273
match endpoint {
274
constants::U2FHID_CONTROL_ENDPOINT => {
275
let arc_transfer_local = arc_transfer.clone();
276
let fido_device = self.device.clone();
277
self.transfer_job_queue
278
.queue_job(move || {
279
let mut lock = arc_transfer_local.lock();
280
match lock.take() {
281
Some(mut transfer) => {
282
if let Err(e) = FidoPassthroughDevice::handle_control(
283
&mut transfer,
284
&fido_device,
285
) {
286
error!(
287
"Fido device handle control failed, cancelling transfer:\
288
{e:#}"
289
);
290
drop(lock);
291
if let Err(e) = cancel_handle.cancel() {
292
error!(
293
"Failed to cancel transfer, dropping request: {e:#}"
294
);
295
return;
296
}
297
}
298
transfer.complete_transfer();
299
}
300
None => {
301
error!(
302
"USB transfer disappeared in handle_control. Dropping request."
303
);
304
}
305
}
306
})
307
.map_err(BackendError::QueueAsyncJob)?;
308
}
309
constants::U2FHID_OUT_ENDPOINT => {
310
let arc_transfer_local = arc_transfer.clone();
311
let fido_device = self.device.clone();
312
self.transfer_job_queue
313
.queue_job(move || {
314
let mut lock = arc_transfer_local.lock();
315
match lock.take() {
316
Some(mut transfer) => {
317
if let Err(e) = FidoPassthroughDevice::handle_interrupt_out(
318
&mut transfer,
319
&fido_device,
320
) {
321
error!(
322
"Fido device handle interrupt out failed,\
323
cancelling transfer: {e:#}"
324
);
325
drop(lock);
326
if let Err(e) = cancel_handle.cancel() {
327
error!(
328
"Failed to cancel transfer, dropping request: {e:#}"
329
);
330
return;
331
}
332
}
333
transfer.complete_transfer();
334
}
335
None => {
336
error!("Interrupt out transfer disappeared. Dropping request.");
337
}
338
}
339
})
340
.map_err(BackendError::QueueAsyncJob)?;
341
}
342
constants::U2FHID_IN_ENDPOINT => {
343
let handle = FidoTransferHandle {
344
weak_transfer: Arc::downgrade(&arc_transfer.clone()),
345
};
346
self.pending_in_transfers
347
.lock()
348
.push_back((handle, arc_transfer.clone()));
349
350
// Make sure to arm the timer for both transfer and host packet polling as we wait
351
// for transaction requests to be fulfilled by the host or xhci transfer to time
352
// out.
353
if let Err(e) = self.device.lock().guest_key.lock().timer.arm() {
354
error!("Unable to start U2F guest key timer. U2F packets may be lost. {e:#}");
355
}
356
if let Err(e) = self.device.lock().transfer_timer.arm() {
357
error!("Unable to start transfer poll timer. Transfers might stall. {e:#}");
358
}
359
}
360
_ => {
361
error!("Wrong endpoint requested: {endpoint}");
362
return Err(BackendError::MalformedBackendTransfer);
363
}
364
}
365
366
// Start the worker thread if it hasn't been created yet
367
if self.worker_thread.is_none()
368
&& (endpoint == constants::U2FHID_IN_ENDPOINT
369
|| endpoint == constants::U2FHID_OUT_ENDPOINT)
370
{
371
let device = self.device.clone();
372
let pending_in_transfers = self.pending_in_transfers.clone();
373
self.worker_thread = Some(WorkerThread::start("fido poll thread", move |kill_evt| {
374
if let Err(e) = poll_for_pending_packets(device, pending_in_transfers, kill_evt) {
375
error!("Poll worker thread errored: {e:#}");
376
}
377
}));
378
}
379
380
let cancel_handle = FidoTransferHandle {
381
weak_transfer: Arc::downgrade(&arc_transfer),
382
};
383
Ok(BackendTransferHandle::new(cancel_handle))
384
}
385
386
fn detach_event_handler(&self, _event_loop: &Arc<EventLoop>) -> BackendResult<()> {
387
self.device.lock().set_active(false);
388
Ok(())
389
}
390
391
fn request_transfer_buffer(&mut self, size: usize) -> TransferBuffer {
392
TransferBuffer::Vector(vec![0u8; size])
393
}
394
395
fn build_bulk_transfer(
396
&mut self,
397
_ep_addr: u8,
398
_transfer_buffer: TransferBuffer,
399
_stream_id: Option<u16>,
400
) -> BackendResult<BackendTransferType> {
401
// Fido devices don't support bulk transfer requests
402
Err(BackendError::MalformedBackendTransfer)
403
}
404
405
fn build_interrupt_transfer(
406
&mut self,
407
ep_addr: u8,
408
transfer_buffer: TransferBuffer,
409
) -> BackendResult<BackendTransferType> {
410
Ok(BackendTransferType::FidoDevice(FidoTransfer::new(
411
ep_addr,
412
transfer_buffer,
413
)))
414
}
415
416
fn get_control_transfer_state(&mut self) -> Arc<RwLock<ControlTransferState>> {
417
self.control_transfer_state.clone()
418
}
419
420
fn get_device_state(&mut self) -> Arc<RwLock<DeviceState>> {
421
self.state.clone()
422
}
423
424
fn get_active_config_descriptor(&mut self) -> BackendResult<ConfigDescriptorTree> {
425
// There is only a config descriptor for u2f virtual keys.
426
self.get_config_descriptor_by_index(0)
427
}
428
429
fn get_config_descriptor(&mut self, config: u8) -> BackendResult<ConfigDescriptorTree> {
430
let device_descriptor = self.get_device_descriptor_tree()?;
431
if let Some(config_descriptor) = device_descriptor.get_config_descriptor(config) {
432
return Ok(config_descriptor.clone());
433
}
434
Err(BackendError::GetConfigDescriptor(
435
UsbUtilError::DescriptorParse,
436
))
437
}
438
439
fn get_config_descriptor_by_index(
440
&mut self,
441
config_index: u8,
442
) -> BackendResult<ConfigDescriptorTree> {
443
let device_descriptor = self.get_device_descriptor_tree()?;
444
if let Some(config_descriptor) =
445
device_descriptor.get_config_descriptor_by_index(config_index)
446
{
447
return Ok(config_descriptor.clone());
448
}
449
Err(BackendError::GetConfigDescriptor(
450
UsbUtilError::DescriptorParse,
451
))
452
}
453
454
fn get_device_descriptor_tree(&mut self) -> BackendResult<DeviceDescriptorTree> {
455
// Skip the first two fields of length and descriptor type as we don't need them in our
456
// DeviceDescriptor structure.
457
let mut descbuf: Vec<u8> = constants::U2FHID_DEVICE_DESC.to_vec();
458
let mut configbuf: Vec<u8> = constants::U2FHID_CONFIG_DESC.to_vec();
459
descbuf.append(&mut configbuf);
460
parse_usbfs_descriptors(&descbuf).map_err(BackendError::GetDeviceDescriptor)
461
}
462
463
fn get_active_configuration(&mut self) -> BackendResult<u8> {
464
let descriptor_tree = self.get_device_descriptor_tree()?;
465
if descriptor_tree.bNumConfigurations != 1 {
466
error!(
467
"Fido devices should only have one configuration, found {}",
468
descriptor_tree.bNumConfigurations
469
);
470
} else if let Some(config_descriptor) = descriptor_tree.get_config_descriptor_by_index(0) {
471
return Ok(config_descriptor.bConfigurationValue);
472
}
473
Err(BackendError::GetActiveConfig(UsbUtilError::DescriptorParse))
474
}
475
476
fn set_active_configuration(&mut self, config: u8) -> BackendResult<()> {
477
// Fido devices only have one configuration so we should do nothing here.
478
// Return an error if the configuration number is unexpected.
479
if config != 0 {
480
error!(
481
"Requested to set fido active configuration of {config}, but only 0 is allowed."
482
);
483
return Err(BackendError::BadBackendProviderState);
484
}
485
Ok(())
486
}
487
488
fn clear_feature(&mut self, _value: u16, _index: u16) -> BackendResult<TransferStatus> {
489
// Nothing to do here, just return.
490
Ok(TransferStatus::Completed)
491
}
492
493
fn create_endpoints(&mut self, _config_descriptor: &ConfigDescriptorTree) -> BackendResult<()> {
494
let mut endpoints = Vec::new();
495
let device_state = self.get_device_state();
496
// We ignore the config descriptor because u2f-hid endpoints are already defined by the
497
// protocol and are unchanging.
498
// Endpoint 1 (OUT)
499
endpoints.push(UsbEndpoint::new(
500
device_state.read().unwrap().fail_handle.clone(),
501
device_state.read().unwrap().job_queue.clone(),
502
1,
503
EndpointDirection::HostToDevice,
504
EndpointType::Interrupt,
505
));
506
// Endpoint 1 (IN)
507
endpoints.push(UsbEndpoint::new(
508
device_state.read().unwrap().fail_handle.clone(),
509
device_state.read().unwrap().job_queue.clone(),
510
1,
511
EndpointDirection::DeviceToHost,
512
EndpointType::Interrupt,
513
));
514
device_state.write().unwrap().endpoints = endpoints;
515
Ok(())
516
}
517
}
518
519
impl XhciBackendDevice for FidoPassthroughDevice {
520
fn get_backend_type(&self) -> BackendType {
521
BackendType::Usb2
522
}
523
524
fn get_vid(&self) -> u16 {
525
// Google vendor ID
526
0x18d1
527
}
528
529
fn get_pid(&self) -> u16 {
530
// Unique Product ID
531
0xf1d0
532
}
533
534
fn set_address(&mut self, _address: UsbDeviceAddress) {
535
// Nothing to do here
536
}
537
538
fn reset(&mut self) -> BackendResult<()> {
539
let mut device_lock = self.device.lock();
540
device_lock.set_active(false);
541
device_lock.guest_key.lock().reset();
542
device_lock.transaction_manager.lock().reset();
543
Ok(())
544
}
545
546
fn get_speed(&self) -> Option<DeviceSpeed> {
547
Some(DeviceSpeed::Full)
548
}
549
550
fn alloc_streams(&self, _ep: u8, _num_streams: u16) -> BackendResult<()> {
551
// FIDO devices don't support bulk/streams so we ignore this request.
552
Ok(())
553
}
554
555
fn free_streams(&self, _ep: u8) -> BackendResult<()> {
556
// FIDO devices don't support bulk/streams so we ignore this request.
557
Ok(())
558
}
559
560
fn stop(&mut self) {
561
// Transition the FIDO device into inactive mode and mark device as lost.
562
// The FIDO device cannot error on reset so we can unwrap safely.
563
self.reset().unwrap();
564
self.device.lock().is_device_lost = true;
565
}
566
}
567
568