summaryrefslogtreecommitdiffstats
path: root/third_party/rust/authenticator/src/netbsd
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/netbsd
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/netbsd')
-rw-r--r--third_party/rust/authenticator/src/netbsd/device.rs159
-rw-r--r--third_party/rust/authenticator/src/netbsd/fd.rs47
-rw-r--r--third_party/rust/authenticator/src/netbsd/mod.rs10
-rw-r--r--third_party/rust/authenticator/src/netbsd/monitor.rs87
-rw-r--r--third_party/rust/authenticator/src/netbsd/transaction.rs53
-rw-r--r--third_party/rust/authenticator/src/netbsd/uhid.rs77
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(())
+}