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::ErrorKind; 8 use std::io::Read; 9 use std::sync::Arc; 10 use std::sync::RwLock; 11 12 use base::debug; 13 use base::error; 14 use base::AsRawDescriptor; 15 use base::Event; 16 use base::RawDescriptor; 17 use base::WorkerThread; 18 use sync::Mutex; 19 use usb_util::parse_usbfs_descriptors; 20 use usb_util::ConfigDescriptorTree; 21 use usb_util::ControlRequestDataPhaseTransferDirection; 22 use usb_util::ControlRequestRecipient; 23 use usb_util::ControlRequestType; 24 use usb_util::DescriptorType; 25 use usb_util::DeviceDescriptorTree; 26 use usb_util::DeviceSpeed; 27 use usb_util::EndpointDirection; 28 use usb_util::EndpointType; 29 use usb_util::Error as UsbUtilError; 30 use usb_util::TransferBuffer; 31 use usb_util::TransferStatus; 32 use usb_util::UsbRequestSetup; 33 use zerocopy::AsBytes; 34 use zerocopy::FromBytes; 35 36 use crate::usb::backend::device::BackendDevice; 37 use crate::usb::backend::device::DeviceState; 38 use crate::usb::backend::endpoint::ControlEndpointState; 39 use crate::usb::backend::endpoint::UsbEndpoint; 40 use crate::usb::backend::error::Error as BackendError; 41 use crate::usb::backend::error::Result as BackendResult; 42 use crate::usb::backend::fido_backend::constants; 43 use crate::usb::backend::fido_backend::error::Error; 44 use crate::usb::backend::fido_backend::error::Result; 45 use crate::usb::backend::fido_backend::fido_device::FidoDevice; 46 use crate::usb::backend::fido_backend::poll_thread::poll_for_pending_packets; 47 use crate::usb::backend::fido_backend::transfer::FidoTransfer; 48 use crate::usb::backend::fido_backend::transfer::FidoTransferHandle; 49 use crate::usb::backend::transfer::BackendTransferHandle; 50 use crate::usb::backend::transfer::BackendTransferType; 51 use crate::usb::backend::transfer::ControlTransferState; 52 use crate::usb::backend::transfer::GenericTransferHandle; 53 use crate::usb::xhci::xhci_backend_device::BackendType; 54 use crate::usb::xhci::xhci_backend_device::UsbDeviceAddress; 55 use crate::usb::xhci::xhci_backend_device::XhciBackendDevice; 56 use crate::utils::AsyncJobQueue; 57 use crate::utils::EventLoop; 58 59 /// Host-level fido passthrough device that handles USB operations and relays them to the 60 /// appropriate virtual fido device. 61 pub struct FidoPassthroughDevice { 62 /// The virtual FIDO device implementation. 63 device: Arc<Mutex<FidoDevice>>, 64 /// The state of the device as seen by the backend provider. 65 state: Arc<RwLock<DeviceState>>, 66 /// The state of the control transfer exchange with the xhci layer. 67 control_transfer_state: Arc<RwLock<ControlTransferState>>, 68 transfer_job_queue: Arc<AsyncJobQueue>, 69 kill_evt: Event, 70 worker_thread: Option<WorkerThread<()>>, 71 pending_in_transfers: 72 Arc<Mutex<VecDeque<(FidoTransferHandle, Arc<Mutex<Option<FidoTransfer>>>)>>>, 73 } 74 75 impl FidoPassthroughDevice { new( device: Arc<Mutex<FidoDevice>>, state: DeviceState, event_loop: Arc<EventLoop>, ) -> Result<Self>76 pub fn new( 77 device: Arc<Mutex<FidoDevice>>, 78 state: DeviceState, 79 event_loop: Arc<EventLoop>, 80 ) -> Result<Self> { 81 let control_transfer_state = ControlTransferState { 82 ctl_ep_state: ControlEndpointState::SetupStage, 83 control_request_setup: UsbRequestSetup::new(0, 0, 0, 0, 0), 84 executed: false, 85 }; 86 let job_queue = AsyncJobQueue::init(&event_loop).map_err(Error::StartAsyncFidoQueue)?; 87 Ok(FidoPassthroughDevice { 88 device, 89 state: Arc::new(RwLock::new(state)), 90 control_transfer_state: Arc::new(RwLock::new(control_transfer_state)), 91 transfer_job_queue: job_queue, 92 kill_evt: Event::new().unwrap(), 93 worker_thread: None, 94 pending_in_transfers: Arc::new(Mutex::new(VecDeque::new())), 95 }) 96 } 97 98 /// This function is called from the low-level event handler when the monitored `fd` is ready 99 /// to transmit data from the host to the guest. read_hidraw_file(&mut self) -> Result<()>100 pub fn read_hidraw_file(&mut self) -> Result<()> { 101 let mut device = self.device.lock(); 102 // Device has already stopped working, just return early. 103 if device.is_device_lost { 104 return Ok(()); 105 } 106 if !device.is_active { 107 // We should NEVER be polling on the fd and wake up if no transactions have been 108 // initiated from the guest first. 109 error!("Fido device received fd poll event from inactive device. This is a bug."); 110 return Err(Error::InconsistentFidoDeviceState); 111 } 112 113 let mut packet = vec![0; constants::U2FHID_PACKET_SIZE * 2]; 114 115 if device.guest_key.lock().pending_in_packets.len() >= constants::U2FHID_MAX_IN_PENDING { 116 return Err(Error::PendingInQueueFull); 117 } 118 119 let read_result = device.fd.lock().read(&mut packet); 120 match read_result { 121 Ok(n) => { 122 // We read too much, the device is misbehaving 123 if n != constants::U2FHID_PACKET_SIZE { 124 return Err(Error::ReadHidrawDevice(IOError::new( 125 ErrorKind::Other, 126 format!("Read too many bytes ({n}), the hidraw device is misbehaving."), 127 ))); 128 } 129 // This is safe because we just checked the size of n is exactly U2FHID_PACKET_SIZE 130 device 131 .recv_from_host(packet[..constants::U2FHID_PACKET_SIZE].try_into().unwrap())?; 132 } 133 Err(e) => { 134 error!("U2F hidraw read error: {e:#}, resetting and detaching device",); 135 device.set_active(false); 136 device.is_device_lost = true; 137 return Err(Error::ReadHidrawDevice(e)); 138 } 139 } 140 Ok(()) 141 } 142 143 /// This function is called by a queued job to handle all communication related to USB control 144 /// transfer packets between the guest and the virtual security key. handle_control( transfer: &mut FidoTransfer, device: &Arc<Mutex<FidoDevice>>, ) -> Result<()>145 pub fn handle_control( 146 transfer: &mut FidoTransfer, 147 device: &Arc<Mutex<FidoDevice>>, 148 ) -> Result<()> { 149 transfer.actual_length = 0; 150 let request_setup = match &transfer.buffer { 151 TransferBuffer::Vector(v) => { 152 UsbRequestSetup::read_from_prefix(v).ok_or_else(|| Error::InvalidDataBufferSize)? 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. handle_interrupt_out( transfer: &mut FidoTransfer, device: &Arc<Mutex<FidoDevice>>, ) -> Result<()>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 { drop(&mut self)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 { as_raw_descriptor(&self) -> RawDescriptor252 fn as_raw_descriptor(&self) -> RawDescriptor { 253 self.device.lock().as_raw_descriptor() 254 } 255 } 256 257 impl BackendDevice for FidoPassthroughDevice { submit_backend_transfer( &mut self, transfer: BackendTransferType, ) -> BackendResult<BackendTransferHandle>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 detach_event_handler(&self, _event_loop: &Arc<EventLoop>) -> BackendResult<()>386 fn detach_event_handler(&self, _event_loop: &Arc<EventLoop>) -> BackendResult<()> { 387 self.device.lock().set_active(false); 388 Ok(()) 389 } 390 request_transfer_buffer(&mut self, size: usize) -> TransferBuffer391 fn request_transfer_buffer(&mut self, size: usize) -> TransferBuffer { 392 TransferBuffer::Vector(vec![0u8; size]) 393 } 394 build_bulk_transfer( &mut self, _ep_addr: u8, _transfer_buffer: TransferBuffer, _stream_id: Option<u16>, ) -> BackendResult<BackendTransferType>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 build_interrupt_transfer( &mut self, ep_addr: u8, transfer_buffer: TransferBuffer, ) -> BackendResult<BackendTransferType>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 get_control_transfer_state(&mut self) -> Arc<RwLock<ControlTransferState>>416 fn get_control_transfer_state(&mut self) -> Arc<RwLock<ControlTransferState>> { 417 self.control_transfer_state.clone() 418 } 419 get_device_state(&mut self) -> Arc<RwLock<DeviceState>>420 fn get_device_state(&mut self) -> Arc<RwLock<DeviceState>> { 421 self.state.clone() 422 } 423 get_active_config_descriptor(&mut self) -> BackendResult<ConfigDescriptorTree>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 get_config_descriptor(&mut self, config: u8) -> BackendResult<ConfigDescriptorTree>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 get_config_descriptor_by_index( &mut self, config_index: u8, ) -> BackendResult<ConfigDescriptorTree>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 get_device_descriptor_tree(&mut self) -> BackendResult<DeviceDescriptorTree>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 get_active_configuration(&mut self) -> BackendResult<u8>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 set_active_configuration(&mut self, config: u8) -> BackendResult<()>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 clear_feature(&mut self, _value: u16, _index: u16) -> BackendResult<TransferStatus>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 create_endpoints(&mut self, _config_descriptor: &ConfigDescriptorTree) -> BackendResult<()>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 { get_backend_type(&self) -> BackendType520 fn get_backend_type(&self) -> BackendType { 521 BackendType::Usb2 522 } 523 get_vid(&self) -> u16524 fn get_vid(&self) -> u16 { 525 // Google vendor ID 526 0x18d1 527 } 528 get_pid(&self) -> u16529 fn get_pid(&self) -> u16 { 530 // Unique Product ID 531 0xf1d0 532 } 533 set_address(&mut self, _address: UsbDeviceAddress)534 fn set_address(&mut self, _address: UsbDeviceAddress) { 535 // Nothing to do here 536 } 537 reset(&mut self) -> BackendResult<()>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 get_speed(&self) -> Option<DeviceSpeed>546 fn get_speed(&self) -> Option<DeviceSpeed> { 547 Some(DeviceSpeed::Full) 548 } 549 alloc_streams(&self, _ep: u8, _num_streams: u16) -> BackendResult<()>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 free_streams(&self, _ep: u8) -> BackendResult<()>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 stop(&mut self)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