diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/authenticator/src/netbsd | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/authenticator/src/netbsd')
-rw-r--r-- | third_party/rust/authenticator/src/netbsd/device.rs | 159 | ||||
-rw-r--r-- | third_party/rust/authenticator/src/netbsd/fd.rs | 47 | ||||
-rw-r--r-- | third_party/rust/authenticator/src/netbsd/mod.rs | 10 | ||||
-rw-r--r-- | third_party/rust/authenticator/src/netbsd/monitor.rs | 87 | ||||
-rw-r--r-- | third_party/rust/authenticator/src/netbsd/transaction.rs | 53 | ||||
-rw-r--r-- | third_party/rust/authenticator/src/netbsd/uhid.rs | 77 |
6 files changed, 433 insertions, 0 deletions
diff --git a/third_party/rust/authenticator/src/netbsd/device.rs b/third_party/rust/authenticator/src/netbsd/device.rs new file mode 100644 index 0000000000..92e7c22ea1 --- /dev/null +++ b/third_party/rust/authenticator/src/netbsd/device.rs @@ -0,0 +1,159 @@ +/* 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 std::io; +use std::io::Read; +use std::io::Write; +use std::mem; + +use crate::consts::CID_BROADCAST; +use crate::consts::MAX_HID_RPT_SIZE; +use crate::platform::fd::Fd; +use crate::platform::uhid; +use crate::u2ftypes::{U2FDevice, U2FDeviceInfo}; +use crate::util::io_err; + +#[derive(Debug)] +pub struct Device { + fd: Fd, + cid: [u8; 4], + dev_info: Option<U2FDeviceInfo>, +} + +impl Device { + pub fn new(fd: Fd) -> io::Result<Self> { + Ok(Self { + fd, + cid: CID_BROADCAST, + dev_info: None, + }) + } + + pub fn is_u2f(&mut self) -> bool { + if !uhid::is_u2f_device(&self.fd) { + return false; + } + // This step is not strictly necessary -- NetBSD puts fido + // devices into raw mode automatically by default, but in + // principle that might change, and this serves as a test to + // verify that we're running on a kernel with support for raw + // mode at all so we don't get confused issuing writes that try + // to set the report descriptor rather than transfer data on + // the output interrupt pipe as we need. + match uhid::hid_set_raw(&self.fd, true) { + Ok(_) => (), + Err(_) => return false, + } + if let Err(_) = self.ping() { + return false; + } + true + } + + fn ping(&mut self) -> io::Result<()> { + for i in 0..10 { + let mut buf = vec![0u8; 1 + MAX_HID_RPT_SIZE]; + + buf[0] = 0; // report number + buf[1] = 0xff; // CID_BROADCAST + buf[2] = 0xff; + buf[3] = 0xff; + buf[4] = 0xff; + buf[5] = 0x81; // ping + buf[6] = 0; + buf[7] = 1; // one byte + + self.write(&buf[..])?; + + // Wait for response + let mut pfd: libc::pollfd = unsafe { mem::zeroed() }; + pfd.fd = self.fd.fileno; + pfd.events = libc::POLLIN; + let nfds = unsafe { libc::poll(&mut pfd, 1, 100) }; + if nfds == -1 { + return Err(io::Error::last_os_error()); + } + if nfds == 0 { + debug!("device timeout {}", i); + continue; + } + + // Read response + self.read(&mut buf[..])?; + + return Ok(()); + } + + Err(io_err("no response from device")) + } +} + +impl PartialEq for Device { + fn eq(&self, other: &Device) -> bool { + self.fd == other.fd + } +} + +impl Read for Device { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + let bufp = buf.as_mut_ptr() as *mut libc::c_void; + let nread = unsafe { libc::read(self.fd.fileno, bufp, buf.len()) }; + if nread == -1 { + return Err(io::Error::last_os_error()); + } + Ok(nread as usize) + } +} + +impl Write for Device { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + // Always skip the first byte (report number) + let data = &buf[1..]; + let data_ptr = data.as_ptr() as *const libc::c_void; + let nwrit = unsafe { libc::write(self.fd.fileno, data_ptr, data.len()) }; + if nwrit == -1 { + return Err(io::Error::last_os_error()); + } + // Pretend we wrote the report number byte + Ok(nwrit as usize + 1) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl U2FDevice for Device { + fn get_cid<'a>(&'a self) -> &'a [u8; 4] { + &self.cid + } + + fn set_cid(&mut self, cid: [u8; 4]) { + self.cid = cid; + } + + fn in_rpt_size(&self) -> usize { + MAX_HID_RPT_SIZE + } + + fn out_rpt_size(&self) -> usize { + MAX_HID_RPT_SIZE + } + + fn get_property(&self, _prop_name: &str) -> io::Result<String> { + 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); + } +} diff --git a/third_party/rust/authenticator/src/netbsd/fd.rs b/third_party/rust/authenticator/src/netbsd/fd.rs new file mode 100644 index 0000000000..c011b7fcc8 --- /dev/null +++ b/third_party/rust/authenticator/src/netbsd/fd.rs @@ -0,0 +1,47 @@ +/* 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 std::ffi::CString; +use std::io; +use std::mem; +use std::os::raw::c_int; +use std::os::unix::io::RawFd; + +#[derive(Debug)] +pub struct Fd { + pub fileno: RawFd, +} + +impl Fd { + pub fn open(path: &str, flags: c_int) -> io::Result<Fd> { + let cpath = CString::new(path.as_bytes())?; + let rv = unsafe { libc::open(cpath.as_ptr(), flags) }; + if rv == -1 { + return Err(io::Error::last_os_error()); + } + Ok(Fd { fileno: rv }) + } +} + +impl Drop for Fd { + fn drop(&mut self) { + unsafe { libc::close(self.fileno) }; + } +} + +impl PartialEq for Fd { + fn eq(&self, other: &Fd) -> bool { + let mut st: libc::stat = unsafe { mem::zeroed() }; + let mut sto: libc::stat = unsafe { mem::zeroed() }; + if unsafe { libc::fstat(self.fileno, &mut st) } == -1 { + return false; + } + if unsafe { libc::fstat(other.fileno, &mut sto) } == -1 { + return false; + } + (st.st_dev == sto.st_dev) & (st.st_ino == sto.st_ino) + } +} diff --git a/third_party/rust/authenticator/src/netbsd/mod.rs b/third_party/rust/authenticator/src/netbsd/mod.rs new file mode 100644 index 0000000000..a0eabb6e06 --- /dev/null +++ b/third_party/rust/authenticator/src/netbsd/mod.rs @@ -0,0 +1,10 @@ +/* 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 fd; +mod monitor; +mod uhid; diff --git a/third_party/rust/authenticator/src/netbsd/monitor.rs b/third_party/rust/authenticator/src/netbsd/monitor.rs new file mode 100644 index 0000000000..c78cff6ee1 --- /dev/null +++ b/third_party/rust/authenticator/src/netbsd/monitor.rs @@ -0,0 +1,87 @@ +/* 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 std::collections::HashMap; +use std::ffi::OsString; +use std::io; +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +use runloop::RunLoop; + +use crate::platform::fd::Fd; + +// XXX Should use drvctl, but it doesn't do pubsub properly yet so +// DRVGETEVENT requires write access to /dev/drvctl. Instead, for now, +// just poll every 500ms. +const POLL_TIMEOUT: u64 = 500; + +pub struct Monitor<F> +where + F: Fn(Fd, &dyn Fn() -> bool) + Send + Sync + 'static, +{ + runloops: HashMap<OsString, RunLoop>, + new_device_cb: Arc<F>, +} + +impl<F> Monitor<F> +where + F: Fn(Fd, &dyn Fn() -> bool) + Send + Sync + 'static, +{ + pub fn new(new_device_cb: F) -> Self { + Self { + runloops: HashMap::new(), + new_device_cb: Arc::new(new_device_cb), + } + } + + pub fn run(&mut self, alive: &dyn Fn() -> bool) -> io::Result<()> { + while alive() { + for n in 0..100 { + let uhidpath = format!("/dev/uhid{}", n); + match Fd::open(&uhidpath, libc::O_RDWR | libc::O_CLOEXEC) { + Ok(uhid) => { + self.add_device(uhid, OsString::from(&uhidpath)); + } + Err(ref err) => match err.raw_os_error() { + Some(libc::EBUSY) => continue, + Some(libc::ENOENT) => break, + _ => self.remove_device(OsString::from(&uhidpath)), + }, + } + } + thread::sleep(Duration::from_millis(POLL_TIMEOUT)); + } + self.remove_all_devices(); + Ok(()) + } + + fn add_device(&mut self, fd: Fd, path: OsString) { + let f = self.new_device_cb.clone(); + + let runloop = RunLoop::new(move |alive| { + if alive() { + f(fd, alive); + } + }); + + if let Ok(runloop) = runloop { + self.runloops.insert(path.clone(), runloop); + } + } + + fn remove_device(&mut self, path: OsString) { + 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/netbsd/transaction.rs b/third_party/rust/authenticator/src/netbsd/transaction.rs new file mode 100644 index 0000000000..21ac212569 --- /dev/null +++ b/third_party/rust/authenticator/src/netbsd/transaction.rs @@ -0,0 +1,53 @@ +/* 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::platform::fd::Fd; +use crate::platform::monitor::Monitor; +use crate::statecallback::StateCallback; +use runloop::RunLoop; + +pub struct Transaction { + // Handle to the thread loop. + thread: Option<RunLoop>, +} + +impl Transaction { + pub fn new<F, T>( + timeout: u64, + callback: StateCallback<crate::Result<T>>, + new_device_cb: F, + ) -> crate::Result<Self> + where + F: Fn(Fd, &dyn Fn() -> bool) + Sync + Send + 'static, + T: 'static, + { + let thread = RunLoop::new_with_timeout( + move |alive| { + // Create a new device monitor. + let mut monitor = Monitor::new(new_device_cb); + + // 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: Some(thread), + }) + } + + pub fn cancel(&mut self) { + // This must never be None. + self.thread.take().unwrap().cancel(); + } +} diff --git a/third_party/rust/authenticator/src/netbsd/uhid.rs b/third_party/rust/authenticator/src/netbsd/uhid.rs new file mode 100644 index 0000000000..f8d711553d --- /dev/null +++ b/third_party/rust/authenticator/src/netbsd/uhid.rs @@ -0,0 +1,77 @@ +/* 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 std::io; +use std::mem; +use std::os::raw::c_int; +use std::os::raw::c_uchar; + +use crate::hidproto::has_fido_usage; +use crate::hidproto::ReportDescriptor; +use crate::platform::fd::Fd; +use crate::util::io_err; + +/* sys/ioccom.h */ + +const IOCPARM_MASK: u32 = 0x1fff; +const IOCPARM_SHIFT: u32 = 16; +const IOCGROUP_SHIFT: u32 = 8; + +//const IOC_VOID: u32 = 0x20000000; +const IOC_OUT: u32 = 0x40000000; +const IOC_IN: u32 = 0x80000000; +//const IOC_INOUT: u32 = IOC_IN|IOC_OUT; + +macro_rules! ioctl { + ($dir:expr, $name:ident, $group:expr, $nr:expr, $ty:ty) => { + unsafe fn $name(fd: libc::c_int, val: *mut $ty) -> io::Result<libc::c_int> { + let ioc = ($dir as u32) + | ((mem::size_of::<$ty>() as u32 & IOCPARM_MASK) << IOCPARM_SHIFT) + | (($group as u32) << IOCGROUP_SHIFT) + | ($nr as u32); + let rv = libc::ioctl(fd, ioc as libc::c_ulong, val); + if rv == -1 { + return Err(io::Error::last_os_error()); + } + Ok(rv) + } + }; +} + +#[allow(non_camel_case_types)] +#[repr(C)] +struct usb_ctl_report_desc { + ucrd_size: c_int, + ucrd_data: [c_uchar; 1024], +} + +ioctl!(IOC_OUT, usb_get_report_desc, b'U', 21, usb_ctl_report_desc); + +fn read_report_descriptor(fd: &Fd) -> io::Result<ReportDescriptor> { + let mut desc = unsafe { mem::zeroed() }; + unsafe { usb_get_report_desc(fd.fileno, &mut desc) }?; + if desc.ucrd_size < 0 { + return Err(io_err("negative report descriptor size")); + } + let size = desc.ucrd_size as usize; + let value = Vec::from(&desc.ucrd_data[..size]); + Ok(ReportDescriptor { value }) +} + +pub fn is_u2f_device(fd: &Fd) -> bool { + match read_report_descriptor(fd) { + Ok(desc) => has_fido_usage(desc), + Err(_) => false, + } +} + +ioctl!(IOC_IN, usb_hid_set_raw_ioctl, b'h', 2, c_int); + +pub fn hid_set_raw(fd: &Fd, raw: bool) -> io::Result<()> { + let mut raw_int: c_int = if raw { 1 } else { 0 }; + unsafe { usb_hid_set_raw_ioctl(fd.fileno, &mut raw_int) }?; + Ok(()) +} |