devices/usb/backend/fido_backend/
fido_passthrough.rs1use std::collections::VecDeque;
6use std::io::Error as IOError;
7use std::io::Read;
8use std::sync::Arc;
9use std::sync::RwLock;
10
11use base::debug;
12use base::error;
13use base::AsRawDescriptor;
14use base::Event;
15use base::RawDescriptor;
16use base::WorkerThread;
17use sync::Mutex;
18use usb_util::parse_usbfs_descriptors;
19use usb_util::ConfigDescriptorTree;
20use usb_util::ControlRequestDataPhaseTransferDirection;
21use usb_util::ControlRequestRecipient;
22use usb_util::ControlRequestType;
23use usb_util::DescriptorType;
24use usb_util::DeviceDescriptorTree;
25use usb_util::DeviceSpeed;
26use usb_util::EndpointDirection;
27use usb_util::EndpointType;
28use usb_util::Error as UsbUtilError;
29use usb_util::TransferBuffer;
30use usb_util::TransferStatus;
31use usb_util::UsbRequestSetup;
32use zerocopy::FromBytes;
33use zerocopy::IntoBytes;
34
35use crate::usb::backend::device::BackendDevice;
36use crate::usb::backend::device::DeviceState;
37use crate::usb::backend::endpoint::ControlEndpointState;
38use crate::usb::backend::endpoint::UsbEndpoint;
39use crate::usb::backend::error::Error as BackendError;
40use crate::usb::backend::error::Result as BackendResult;
41use crate::usb::backend::fido_backend::constants;
42use crate::usb::backend::fido_backend::error::Error;
43use crate::usb::backend::fido_backend::error::Result;
44use crate::usb::backend::fido_backend::fido_device::FidoDevice;
45use crate::usb::backend::fido_backend::poll_thread::poll_for_pending_packets;
46use crate::usb::backend::fido_backend::transfer::FidoTransfer;
47use crate::usb::backend::fido_backend::transfer::FidoTransferHandle;
48use crate::usb::backend::transfer::BackendTransferHandle;
49use crate::usb::backend::transfer::BackendTransferType;
50use crate::usb::backend::transfer::ControlTransferState;
51use crate::usb::backend::transfer::GenericTransferHandle;
52use crate::usb::xhci::xhci_backend_device::BackendType;
53use crate::usb::xhci::xhci_backend_device::UsbDeviceAddress;
54use crate::usb::xhci::xhci_backend_device::XhciBackendDevice;
55use crate::utils::AsyncJobQueue;
56use crate::utils::EventLoop;
57
58pub struct FidoPassthroughDevice {
61 device: Arc<Mutex<FidoDevice>>,
63 state: Arc<RwLock<DeviceState>>,
65 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
74impl 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 data_stage_transfer: None,
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 pub fn read_hidraw_file(&mut self) -> Result<()> {
100 let mut device = self.device.lock();
101 if device.is_device_lost {
103 return Ok(());
104 }
105 if !device.is_active {
106 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 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 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 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 if descriptor_type == (DescriptorType::Device as u8) && is_device_to_host {
166 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 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 transfer.buffer = TransferBuffer::Vector(request_setup_out);
209 Ok(())
210 }
211
212 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
239impl 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
251impl AsRawDescriptor for FidoPassthroughDevice {
252 fn as_raw_descriptor(&self) -> RawDescriptor {
253 self.device.lock().as_raw_descriptor()
254 }
255}
256
257impl 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 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 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 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 build_isochronous_transfer(
417 &mut self,
418 _ep_addr: u8,
419 _transfer_buffer: TransferBuffer,
420 _packet_size: u32,
421 ) -> BackendResult<BackendTransferType> {
422 Err(BackendError::MalformedBackendTransfer)
424 }
425
426 fn get_control_transfer_state(&mut self) -> Arc<RwLock<ControlTransferState>> {
427 self.control_transfer_state.clone()
428 }
429
430 fn get_device_state(&mut self) -> Arc<RwLock<DeviceState>> {
431 self.state.clone()
432 }
433
434 fn get_active_config_descriptor(&mut self) -> BackendResult<ConfigDescriptorTree> {
435 self.get_config_descriptor_by_index(0)
437 }
438
439 fn get_config_descriptor(&mut self, config: u8) -> BackendResult<ConfigDescriptorTree> {
440 let device_descriptor = self.get_device_descriptor_tree()?;
441 if let Some(config_descriptor) = device_descriptor.get_config_descriptor(config) {
442 return Ok(config_descriptor.clone());
443 }
444 Err(BackendError::GetConfigDescriptor(
445 UsbUtilError::DescriptorParse,
446 ))
447 }
448
449 fn get_config_descriptor_by_index(
450 &mut self,
451 config_index: u8,
452 ) -> BackendResult<ConfigDescriptorTree> {
453 let device_descriptor = self.get_device_descriptor_tree()?;
454 if let Some(config_descriptor) =
455 device_descriptor.get_config_descriptor_by_index(config_index)
456 {
457 return Ok(config_descriptor.clone());
458 }
459 Err(BackendError::GetConfigDescriptor(
460 UsbUtilError::DescriptorParse,
461 ))
462 }
463
464 fn get_device_descriptor_tree(&mut self) -> BackendResult<DeviceDescriptorTree> {
465 let mut descbuf: Vec<u8> = constants::U2FHID_DEVICE_DESC.to_vec();
468 let mut configbuf: Vec<u8> = constants::U2FHID_CONFIG_DESC.to_vec();
469 descbuf.append(&mut configbuf);
470 parse_usbfs_descriptors(&descbuf).map_err(BackendError::GetDeviceDescriptor)
471 }
472
473 fn get_active_configuration(&mut self) -> BackendResult<u8> {
474 let descriptor_tree = self.get_device_descriptor_tree()?;
475 if descriptor_tree.bNumConfigurations != 1 {
476 error!(
477 "Fido devices should only have one configuration, found {}",
478 descriptor_tree.bNumConfigurations
479 );
480 } else if let Some(config_descriptor) = descriptor_tree.get_config_descriptor_by_index(0) {
481 return Ok(config_descriptor.bConfigurationValue);
482 }
483 Err(BackendError::GetActiveConfig(UsbUtilError::DescriptorParse))
484 }
485
486 fn set_active_configuration(&mut self, config: u8) -> BackendResult<()> {
487 if config != 0 {
490 error!(
491 "Requested to set fido active configuration of {config}, but only 0 is allowed."
492 );
493 return Err(BackendError::BadBackendProviderState);
494 }
495 Ok(())
496 }
497
498 fn clear_feature(&mut self, _value: u16, _index: u16) -> BackendResult<TransferStatus> {
499 Ok(TransferStatus::Completed)
501 }
502
503 fn create_endpoints(&mut self, _config_descriptor: &ConfigDescriptorTree) -> BackendResult<()> {
504 let mut endpoints = Vec::new();
505 let device_state = self.get_device_state();
506 endpoints.push(UsbEndpoint::new(
510 device_state.read().unwrap().fail_handle.clone(),
511 device_state.read().unwrap().job_queue.clone(),
512 1,
513 EndpointDirection::HostToDevice,
514 EndpointType::Interrupt,
515 ));
516 endpoints.push(UsbEndpoint::new(
518 device_state.read().unwrap().fail_handle.clone(),
519 device_state.read().unwrap().job_queue.clone(),
520 1,
521 EndpointDirection::DeviceToHost,
522 EndpointType::Interrupt,
523 ));
524 device_state.write().unwrap().endpoints = endpoints;
525 Ok(())
526 }
527}
528
529impl XhciBackendDevice for FidoPassthroughDevice {
530 fn get_backend_type(&self) -> BackendType {
531 BackendType::Usb2
532 }
533
534 fn get_vid(&self) -> u16 {
535 0x18d1
537 }
538
539 fn get_pid(&self) -> u16 {
540 0xf1d0
542 }
543
544 fn set_address(&mut self, _address: UsbDeviceAddress) {
545 }
547
548 fn reset(&mut self) -> BackendResult<()> {
549 let mut device_lock = self.device.lock();
550 device_lock.set_active(false);
551 device_lock.guest_key.lock().reset();
552 device_lock.transaction_manager.lock().reset();
553 Ok(())
554 }
555
556 fn get_speed(&self) -> Option<DeviceSpeed> {
557 Some(DeviceSpeed::Full)
558 }
559
560 fn alloc_streams(&self, _ep: u8, _num_streams: u16) -> BackendResult<()> {
561 Ok(())
563 }
564
565 fn free_streams(&self, _ep: u8) -> BackendResult<()> {
566 Ok(())
568 }
569
570 fn stop(&mut self) {
571 self.reset().unwrap();
574 self.device.lock().is_device_lost = true;
575 }
576}