summaryrefslogtreecommitdiffstats
path: root/third_party/rust/authenticator/src/openbsd
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/authenticator/src/openbsd
parentInitial commit. (diff)
downloadfirefox-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/openbsd')
-rw-r--r--third_party/rust/authenticator/src/openbsd/device.rs148
-rw-r--r--third_party/rust/authenticator/src/openbsd/mod.rs8
-rw-r--r--third_party/rust/authenticator/src/openbsd/monitor.rs111
-rw-r--r--third_party/rust/authenticator/src/openbsd/transaction.rs52
4 files changed, 319 insertions, 0 deletions
diff --git a/third_party/rust/authenticator/src/openbsd/device.rs b/third_party/rust/authenticator/src/openbsd/device.rs
new file mode 100644
index 0000000000..2238e034e2
--- /dev/null
+++ b/third_party/rust/authenticator/src/openbsd/device.rs
@@ -0,0 +1,148 @@
+/* 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::OsString;
+use std::io;
+use std::io::{Read, Result, Write};
+use std::mem;
+
+use crate::consts::{CID_BROADCAST, MAX_HID_RPT_SIZE};
+use crate::platform::monitor::FidoDev;
+use crate::u2ftypes::{U2FDevice, U2FDeviceInfo};
+use crate::util::{from_unix_result, io_err};
+
+#[derive(Debug)]
+pub struct Device {
+ path: OsString,
+ fd: libc::c_int,
+ cid: [u8; 4],
+ out_len: usize,
+ dev_info: Option<U2FDeviceInfo>,
+}
+
+impl Device {
+ pub fn new(fido: FidoDev) -> Result<Self> {
+ debug!("device found: {:?}", fido);
+ Ok(Self {
+ path: fido.os_path,
+ fd: fido.fd,
+ cid: CID_BROADCAST,
+ out_len: 64,
+ dev_info: None,
+ })
+ }
+
+ pub 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 ping(&mut self) -> Result<()> {
+ let capacity = 256;
+
+ for _ in 0..10 {
+ let mut data = vec![0u8; capacity];
+
+ // Send 1 byte ping
+ 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 Read for Device {
+ fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
+ 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]) -> 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 rv = unsafe { libc::write(self.fd, data_ptr, data.len()) };
+ Ok(from_unix_result(rv as usize)? + 1)
+ }
+
+ fn flush(&mut self) -> Result<()> {
+ Ok(())
+ }
+}
+
+impl U2FDevice for Device {
+ 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 {
+ 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/openbsd/mod.rs b/third_party/rust/authenticator/src/openbsd/mod.rs
new file mode 100644
index 0000000000..fa02132e67
--- /dev/null
+++ b/third_party/rust/authenticator/src/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/openbsd/monitor.rs b/third_party/rust/authenticator/src/openbsd/monitor.rs
new file mode 100644
index 0000000000..2f3930497f
--- /dev/null
+++ b/third_party/rust/authenticator/src/openbsd/monitor.rs
@@ -0,0 +1,111 @@
+/* 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::{CString, OsString};
+use std::io;
+use std::os::unix::ffi::OsStrExt;
+use std::os::unix::io::RawFd;
+use std::path::PathBuf;
+use std::sync::Arc;
+use std::thread;
+use std::time::Duration;
+
+use crate::util::from_unix_result;
+use runloop::RunLoop;
+
+const POLL_TIMEOUT: u64 = 500;
+
+#[derive(Debug)]
+pub struct FidoDev {
+ pub fd: RawFd,
+ pub os_path: OsString,
+}
+
+pub struct Monitor<F>
+where
+ F: Fn(FidoDev, &dyn Fn() -> bool) + Sync,
+{
+ runloops: HashMap<OsString, RunLoop>,
+ new_device_cb: Arc<F>,
+}
+
+impl<F> Monitor<F>
+where
+ F: Fn(FidoDev, &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<()> {
+ // 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.
+ self.add_device(FidoDev { 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: FidoDev) {
+ if !self.runloops.contains_key(&fido.os_path) {
+ let f = self.new_device_cb.clone();
+ let key = fido.os_path.clone();
+
+ let runloop = RunLoop::new(move |alive| {
+ if alive() {
+ f(fido, alive);
+ }
+ });
+
+ if let Ok(runloop) = runloop {
+ self.runloops.insert(key, 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/openbsd/transaction.rs b/third_party/rust/authenticator/src/openbsd/transaction.rs
new file mode 100644
index 0000000000..4b85db2785
--- /dev/null
+++ b/third_party/rust/authenticator/src/openbsd/transaction.rs
@@ -0,0 +1,52 @@
+/* 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::monitor::{FidoDev, 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(FidoDev, &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();
+ }
+}