From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../authenticator/src/transport/openbsd/device.rs | 224 +++++++++++++++++++++ .../authenticator/src/transport/openbsd/mod.rs | 8 + .../authenticator/src/transport/openbsd/monitor.rs | 138 +++++++++++++ .../src/transport/openbsd/transaction.rs | 69 +++++++ 4 files changed, 439 insertions(+) create mode 100644 third_party/rust/authenticator/src/transport/openbsd/device.rs create mode 100644 third_party/rust/authenticator/src/transport/openbsd/mod.rs create mode 100644 third_party/rust/authenticator/src/transport/openbsd/monitor.rs create mode 100644 third_party/rust/authenticator/src/transport/openbsd/transaction.rs (limited to 'third_party/rust/authenticator/src/transport/openbsd') diff --git a/third_party/rust/authenticator/src/transport/openbsd/device.rs b/third_party/rust/authenticator/src/transport/openbsd/device.rs new file mode 100644 index 0000000000..f11ded7984 --- /dev/null +++ b/third_party/rust/authenticator/src/transport/openbsd/device.rs @@ -0,0 +1,224 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +extern crate libc; +use crate::consts::{Capability, CID_BROADCAST, MAX_HID_RPT_SIZE}; +use crate::ctap2::commands::get_info::AuthenticatorInfo; +use crate::transport::hid::HIDDevice; +use crate::transport::platform::monitor::WrappedOpenDevice; +use crate::transport::{FidoDevice, FidoProtocol, HIDError, SharedSecret}; +use crate::u2ftypes::U2FDeviceInfo; +use crate::util::{from_unix_result, io_err}; +use std::ffi::{CString, OsString}; +use std::hash::{Hash, Hasher}; +use std::io::{self, Read, Write}; +use std::mem; +use std::os::unix::ffi::OsStrExt; + +#[derive(Debug)] +pub struct Device { + path: OsString, + fd: libc::c_int, + in_rpt_size: usize, + out_rpt_size: usize, + cid: [u8; 4], + dev_info: Option, + secret: Option, + authenticator_info: Option, + protocol: FidoProtocol, +} + +impl Device { + fn ping(&mut self) -> io::Result<()> { + let capacity = 256; + + for _ in 0..10 { + let mut data = vec![0u8; capacity]; + + // Send 1 byte ping + // self.write_all requires Device to be mut. This can't be done at the moment, + // and this is a workaround anyways, so writing by hand instead. + self.write_all(&[0, 0xff, 0xff, 0xff, 0xff, 0x81, 0, 1])?; + + // Wait for response + let mut pfd: libc::pollfd = unsafe { mem::zeroed() }; + pfd.fd = self.fd; + pfd.events = libc::POLLIN; + if from_unix_result(unsafe { libc::poll(&mut pfd, 1, 100) })? == 0 { + debug!("device {:?} timeout", self.path); + continue; + } + + // Read response + self.read(&mut data[..])?; + + return Ok(()); + } + + Err(io_err("no response from device")) + } +} + +impl Drop for Device { + fn drop(&mut self) { + // Close the fd, ignore any errors. + let _ = unsafe { libc::close(self.fd) }; + debug!("device {:?} closed", self.path); + } +} + +impl PartialEq for Device { + fn eq(&self, other: &Device) -> bool { + self.path == other.path + } +} + +impl Eq for Device {} + +impl Hash for Device { + fn hash(&self, state: &mut H) { + // The path should be the only identifying member for a device + // If the path is the same, its the same device + self.path.hash(state); + } +} + +impl Read for Device { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let buf_ptr = buf.as_mut_ptr() as *mut libc::c_void; + let rv = unsafe { libc::read(self.fd, buf_ptr, buf.len()) }; + from_unix_result(rv as usize) + } +} + +impl Write for Device { + fn write(&mut self, buf: &[u8]) -> io::Result { + // Always skip the first byte (report number) + let data = &buf[1..]; + let data_ptr = data.as_ptr() as *const libc::c_void; + let rv = unsafe { libc::write(self.fd, data_ptr, data.len()) }; + Ok(from_unix_result(rv as usize)? + 1) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl HIDDevice for Device { + type BuildParameters = WrappedOpenDevice; + type Id = OsString; + + fn new(fido: WrappedOpenDevice) -> Result { + debug!("device found: {:?}", fido); + let mut res = Self { + path: fido.os_path, + fd: fido.fd, + in_rpt_size: MAX_HID_RPT_SIZE, + out_rpt_size: MAX_HID_RPT_SIZE, + cid: CID_BROADCAST, + dev_info: None, + secret: None, + authenticator_info: None, + protocol: FidoProtocol::CTAP2, + }; + if res.is_u2f() { + info!("new device {:?}", res.path); + Ok(res) + } else { + Err((HIDError::DeviceNotSupported, res.path.clone())) + } + } + + fn id(&self) -> Self::Id { + self.path.clone() + } + + fn get_cid(&self) -> &[u8; 4] { + &self.cid + } + + fn set_cid(&mut self, cid: [u8; 4]) { + self.cid = cid; + } + + fn in_rpt_size(&self) -> usize { + self.in_rpt_size + } + + fn out_rpt_size(&self) -> usize { + self.out_rpt_size + } + + fn get_property(&self, _prop_name: &str) -> io::Result { + Err(io::Error::new(io::ErrorKind::Other, "Not implemented")) + } + + fn get_device_info(&self) -> U2FDeviceInfo { + // unwrap is okay, as dev_info must have already been set, else + // a programmer error + self.dev_info.clone().unwrap() + } + + fn set_device_info(&mut self, dev_info: U2FDeviceInfo) { + self.dev_info = Some(dev_info); + } +} + +impl FidoDevice for Device { + fn pre_init(&mut self) -> Result<(), HIDError> { + HIDDevice::pre_init(self) + } + + fn should_try_ctap2(&self) -> bool { + HIDDevice::get_device_info(self) + .cap_flags + .contains(Capability::CBOR) + } + + fn initialized(&self) -> bool { + // During successful init, the broadcast channel id gets repplaced by an actual one + self.cid != CID_BROADCAST + } + + fn is_u2f(&mut self) -> bool { + debug!("device {:?} is U2F/FIDO", self.path); + + // From OpenBSD's libfido2 in 6.6-current: + // "OpenBSD (as of 201910) has a bug that causes it to lose + // track of the DATA0/DATA1 sequence toggle across uhid device + // open and close. This is a terrible hack to work around it." + match self.ping() { + Ok(_) => true, + Err(err) => { + debug!("device {:?} is not responding: {}", self.path, err); + false + } + } + } + + fn get_shared_secret(&self) -> Option<&SharedSecret> { + self.secret.as_ref() + } + + fn set_shared_secret(&mut self, secret: SharedSecret) { + self.secret = Some(secret); + } + + fn get_authenticator_info(&self) -> Option<&AuthenticatorInfo> { + self.authenticator_info.as_ref() + } + + fn set_authenticator_info(&mut self, authenticator_info: AuthenticatorInfo) { + self.authenticator_info = Some(authenticator_info); + } + + fn get_protocol(&self) -> FidoProtocol { + self.protocol + } + + fn downgrade_to_ctap1(&mut self) { + self.protocol = FidoProtocol::CTAP1; + } +} diff --git a/third_party/rust/authenticator/src/transport/openbsd/mod.rs b/third_party/rust/authenticator/src/transport/openbsd/mod.rs new file mode 100644 index 0000000000..fa02132e67 --- /dev/null +++ b/third_party/rust/authenticator/src/transport/openbsd/mod.rs @@ -0,0 +1,8 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +pub mod device; +pub mod transaction; + +mod monitor; diff --git a/third_party/rust/authenticator/src/transport/openbsd/monitor.rs b/third_party/rust/authenticator/src/transport/openbsd/monitor.rs new file mode 100644 index 0000000000..0ea5f3d0b8 --- /dev/null +++ b/third_party/rust/authenticator/src/transport/openbsd/monitor.rs @@ -0,0 +1,138 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::transport::device_selector::DeviceSelectorEvent; +use crate::util::from_unix_result; +use runloop::RunLoop; +use std::collections::HashMap; +use std::error::Error; +use std::ffi::{CString, OsString}; +use std::os::unix::ffi::OsStrExt; +use std::os::unix::io::RawFd; +use std::path::PathBuf; +use std::sync::{mpsc::Sender, Arc}; +use std::thread; +use std::time::Duration; + +const POLL_TIMEOUT: u64 = 500; + +#[derive(Debug)] +pub struct WrappedOpenDevice { + pub fd: RawFd, + pub os_path: OsString, +} + +pub struct Monitor +where + F: Fn( + WrappedOpenDevice, + Sender, + Sender, + &dyn Fn() -> bool, + ) + Sync, +{ + runloops: HashMap, + new_device_cb: Arc, + selector_sender: Sender, + status_sender: Sender, +} + +impl Monitor +where + F: Fn( + WrappedOpenDevice, + Sender, + Sender, + &dyn Fn() -> bool, + ) + Send + + Sync + + 'static, +{ + pub fn new( + new_device_cb: F, + selector_sender: Sender, + status_sender: Sender, + ) -> Self { + Self { + runloops: HashMap::new(), + new_device_cb: Arc::new(new_device_cb), + selector_sender, + status_sender, + } + } + + pub fn run(&mut self, alive: &dyn Fn() -> bool) -> Result<(), Box> { + // Loop until we're stopped by the controlling thread, or fail. + while alive() { + // Iterate the first 10 fido(4) devices. + for path in (0..10) + .map(|unit| PathBuf::from(&format!("/dev/fido/{}", unit))) + .filter(|path| path.exists()) + { + let os_path = path.as_os_str().to_os_string(); + let cstr = CString::new(os_path.as_bytes())?; + + // Try to open the device. + let fd = unsafe { libc::open(cstr.as_ptr(), libc::O_RDWR) }; + match from_unix_result(fd) { + Ok(fd) => { + // The device is available if it can be opened. + let _ = self + .selector_sender + .send(DeviceSelectorEvent::DevicesAdded(vec![os_path.clone()])); + self.add_device(WrappedOpenDevice { fd, os_path }); + } + Err(ref err) if err.raw_os_error() == Some(libc::EBUSY) => { + // The device is available but currently in use. + } + _ => { + // libc::ENODEV or any other error. + self.remove_device(os_path); + } + } + } + + thread::sleep(Duration::from_millis(POLL_TIMEOUT)); + } + + // Remove all tracked devices. + self.remove_all_devices(); + + Ok(()) + } + + fn add_device(&mut self, fido: WrappedOpenDevice) { + if !self.runloops.contains_key(&fido.os_path) { + let f = self.new_device_cb.clone(); + let key = fido.os_path.clone(); + let selector_sender = self.selector_sender.clone(); + let status_sender = self.status_sender.clone(); + let runloop = RunLoop::new(move |alive| { + if alive() { + f(fido, selector_sender, status_sender, alive); + } + }); + + if let Ok(runloop) = runloop { + self.runloops.insert(key, runloop); + } + } + } + + fn remove_device(&mut self, path: OsString) { + let _ = self + .selector_sender + .send(DeviceSelectorEvent::DeviceRemoved(path.clone())); + if let Some(runloop) = self.runloops.remove(&path) { + runloop.cancel(); + } + } + + fn remove_all_devices(&mut self) { + while !self.runloops.is_empty() { + let path = self.runloops.keys().next().unwrap().clone(); + self.remove_device(path); + } + } +} diff --git a/third_party/rust/authenticator/src/transport/openbsd/transaction.rs b/third_party/rust/authenticator/src/transport/openbsd/transaction.rs new file mode 100644 index 0000000000..6b15f6751a --- /dev/null +++ b/third_party/rust/authenticator/src/transport/openbsd/transaction.rs @@ -0,0 +1,69 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::errors; +use crate::statecallback::StateCallback; +use crate::transport::device_selector::{ + DeviceBuildParameters, DeviceSelector, DeviceSelectorEvent, +}; +use crate::transport::platform::monitor::Monitor; +use runloop::RunLoop; +use std::sync::mpsc::Sender; + +pub struct Transaction { + // Handle to the thread loop. + thread: RunLoop, + device_selector: DeviceSelector, +} + +impl Transaction { + pub fn new( + timeout: u64, + callback: StateCallback>, + status: Sender, + new_device_cb: F, + ) -> crate::Result + where + F: Fn( + DeviceBuildParameters, + Sender, + Sender, + &dyn Fn() -> bool, + ) + Sync + + Send + + 'static, + T: 'static, + { + let device_selector = DeviceSelector::run(); + let selector_sender = device_selector.clone_sender(); + let thread = RunLoop::new_with_timeout( + move |alive| { + // Create a new device monitor. + let mut monitor = Monitor::new(new_device_cb, selector_sender, status); + + // Start polling for new devices. + try_or!(monitor.run(alive), |_| callback + .call(Err(errors::AuthenticatorError::Platform))); + + // Send an error, if the callback wasn't called already. + callback.call(Err(errors::AuthenticatorError::U2FToken( + errors::U2FTokenError::NotAllowed, + ))); + }, + timeout, + ) + .map_err(|_| errors::AuthenticatorError::Platform)?; + + Ok(Self { + thread, + device_selector, + }) + } + + pub fn cancel(&mut self) { + info!("Transaction was cancelled."); + self.device_selector.stop(); + self.thread.cancel(); + } +} -- cgit v1.2.3