Mon Aug 17 06:58:32 2020 UTC ()
security/tor-browser: Add U2F support to NetBSD.

The webauthn API is disabled by default in the Tor Browser:

https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/26614

In order to use it, risking the consequences since the Tor Project
has not audited its anonymity properties, you have to explicitly
enable security.webauthn.webauthn=true in about:config.

So if you definitely want to log into a web site using U2F in spite
of that, with location privacy but not anonymity, then these patches
now enable it to work on NetBSD (with the caveat that enabling
security.webauthn.webauthn=true applies also to any web site that
tries to use the webauthn API, not just the ones you want to log
into).


(riastradh)
diff -r1.25 -r1.26 pkgsrc/security/tor-browser/distinfo
diff -r0 -r1.1 pkgsrc/security/tor-browser/patches/patch-dom_webauthn_u2f-hid-rs_src_lib.rs
diff -r0 -r1.1 pkgsrc/security/tor-browser/patches/patch-dom_webauthn_u2f-hid-rs_src_netbsd_device.rs
diff -r0 -r1.1 pkgsrc/security/tor-browser/patches/patch-dom_webauthn_u2f-hid-rs_src_netbsd_fd.rs
diff -r0 -r1.1 pkgsrc/security/tor-browser/patches/patch-dom_webauthn_u2f-hid-rs_src_netbsd_mod.rs
diff -r0 -r1.1 pkgsrc/security/tor-browser/patches/patch-dom_webauthn_u2f-hid-rs_src_netbsd_monitor.rs
diff -r0 -r1.1 pkgsrc/security/tor-browser/patches/patch-dom_webauthn_u2f-hid-rs_src_netbsd_transaction.rs
diff -r0 -r1.1 pkgsrc/security/tor-browser/patches/patch-dom_webauthn_u2f-hid-rs_src_netbsd_uhid.rs

cvs diff -r1.25 -r1.26 pkgsrc/security/tor-browser/distinfo (expand / switch to unified diff)

--- pkgsrc/security/tor-browser/distinfo 2020/07/29 07:46:37 1.25
+++ pkgsrc/security/tor-browser/distinfo 2020/08/17 06:58:32 1.26
@@ -1,31 +1,38 @@ @@ -1,31 +1,38 @@
1$NetBSD: distinfo,v 1.25 2020/07/29 07:46:37 wiz Exp $ 1$NetBSD: distinfo,v 1.26 2020/08/17 06:58:32 riastradh Exp $
2 2
3SHA1 (src-firefox-tor-browser-68.11.0esr-9.5-1-build1.tar.xz) = be69e804180d7cc1fb500b1a497f79ab41c4f0db 3SHA1 (src-firefox-tor-browser-68.11.0esr-9.5-1-build1.tar.xz) = be69e804180d7cc1fb500b1a497f79ab41c4f0db
4RMD160 (src-firefox-tor-browser-68.11.0esr-9.5-1-build1.tar.xz) = 625ab420108a64c3f50f2c482190ece3afcbd974 4RMD160 (src-firefox-tor-browser-68.11.0esr-9.5-1-build1.tar.xz) = 625ab420108a64c3f50f2c482190ece3afcbd974
5SHA512 (src-firefox-tor-browser-68.11.0esr-9.5-1-build1.tar.xz) = 8feb9e165f5b7ef80d944b950ec499c15414fd00b4684bca6d5f5019727d21eba46072d5ee416ab0f86b3cae24aa4ba894933a3efbf7b8818cbd956284c1e9ba 5SHA512 (src-firefox-tor-browser-68.11.0esr-9.5-1-build1.tar.xz) = 8feb9e165f5b7ef80d944b950ec499c15414fd00b4684bca6d5f5019727d21eba46072d5ee416ab0f86b3cae24aa4ba894933a3efbf7b8818cbd956284c1e9ba
6Size (src-firefox-tor-browser-68.11.0esr-9.5-1-build1.tar.xz) = 348642008 bytes 6Size (src-firefox-tor-browser-68.11.0esr-9.5-1-build1.tar.xz) = 348642008 bytes
7SHA1 (tor-browser-linux64-9.5.3_en-US.tar.xz) = 5f32789cb9c648cfdfeba93810d45b11c6f5402e 7SHA1 (tor-browser-linux64-9.5.3_en-US.tar.xz) = 5f32789cb9c648cfdfeba93810d45b11c6f5402e
8RMD160 (tor-browser-linux64-9.5.3_en-US.tar.xz) = 27f63a7d8672db669d58ce66b80116903a0c482c 8RMD160 (tor-browser-linux64-9.5.3_en-US.tar.xz) = 27f63a7d8672db669d58ce66b80116903a0c482c
9SHA512 (tor-browser-linux64-9.5.3_en-US.tar.xz) = 760673a0b40c905ec2866a030f4de9c33240cb9138e8af38a28d8527899d9477a8afbe436eda0ff72cc125cd8a5db75cb88efdc8027a0db4ee91f3be363eed90 9SHA512 (tor-browser-linux64-9.5.3_en-US.tar.xz) = 760673a0b40c905ec2866a030f4de9c33240cb9138e8af38a28d8527899d9477a8afbe436eda0ff72cc125cd8a5db75cb88efdc8027a0db4ee91f3be363eed90
10Size (tor-browser-linux64-9.5.3_en-US.tar.xz) = 79021184 bytes 10Size (tor-browser-linux64-9.5.3_en-US.tar.xz) = 79021184 bytes
11SHA1 (patch-.mozconfig) = 66fbb2f113091eee1f022cd656231f845b04b0f8 11SHA1 (patch-.mozconfig) = 66fbb2f113091eee1f022cd656231f845b04b0f8
12SHA1 (patch-aa) = 9f7200c411cd2217a80ec10a276c8877bc6b845c 12SHA1 (patch-aa) = 9f7200c411cd2217a80ec10a276c8877bc6b845c
13SHA1 (patch-browser_app_profile_000-tor-browser.js) = 84a0a15605fff0e22f3150bce901a296fc920280 13SHA1 (patch-browser_app_profile_000-tor-browser.js) = 84a0a15605fff0e22f3150bce901a296fc920280
14SHA1 (patch-browser_app_profile_firefox.js) = cf93582b68b8d4e72c3c25682ab9138e185418c8 14SHA1 (patch-browser_app_profile_firefox.js) = cf93582b68b8d4e72c3c25682ab9138e185418c8
15SHA1 (patch-build_moz.configure_rust.configure) = b57a9b1451dc426d75774f73d7c05fc98fe6e317 15SHA1 (patch-build_moz.configure_rust.configure) = b57a9b1451dc426d75774f73d7c05fc98fe6e317
16SHA1 (patch-config_gcc-stl-wrapper.template.h) = 11b45e0c7a9399c5b74b170648280a388dd67d89 16SHA1 (patch-config_gcc-stl-wrapper.template.h) = 11b45e0c7a9399c5b74b170648280a388dd67d89
17SHA1 (patch-dom_base_nsAttrName.h) = ac7ba441a3b27df2855cf2673eea36b1cb44ad49 17SHA1 (patch-dom_base_nsAttrName.h) = ac7ba441a3b27df2855cf2673eea36b1cb44ad49
18SHA1 (patch-dom_media_CubebUtils.cpp) = 3cd2c65ab281d802c56216565970450767a3fb24 18SHA1 (patch-dom_media_CubebUtils.cpp) = 3cd2c65ab281d802c56216565970450767a3fb24
 19SHA1 (patch-dom_webauthn_u2f-hid-rs_src_lib.rs) = c0dfe8b1e7ebbc7c1d6066c204030f13b063b8d7
 20SHA1 (patch-dom_webauthn_u2f-hid-rs_src_netbsd_device.rs) = 091ffab5bd6a15425acb2ab023cc26f6b23324c6
 21SHA1 (patch-dom_webauthn_u2f-hid-rs_src_netbsd_fd.rs) = 57f5c3c879b07375234e5cb0cbe0469b15105a6a
 22SHA1 (patch-dom_webauthn_u2f-hid-rs_src_netbsd_mod.rs) = 7160fc9fe6d197b42104856b997337f823d2a791
 23SHA1 (patch-dom_webauthn_u2f-hid-rs_src_netbsd_monitor.rs) = 527722bd4fbf0aca07d710e0a8b73f95b2adad40
 24SHA1 (patch-dom_webauthn_u2f-hid-rs_src_netbsd_transaction.rs) = aeafe7c1df614bb5e46cb7fb1cb351001f292caf
 25SHA1 (patch-dom_webauthn_u2f-hid-rs_src_netbsd_uhid.rs) = c1d2157350803fb3eaef6f7a00e7c81dd9cf708b
19SHA1 (patch-gfx_angle_checkout_src_common_third__party_smhasher_src_PMurHash.cpp) = e458c9c8dc66edc69c1874734af28a77fc5e3993 26SHA1 (patch-gfx_angle_checkout_src_common_third__party_smhasher_src_PMurHash.cpp) = e458c9c8dc66edc69c1874734af28a77fc5e3993
20SHA1 (patch-gfx_angle_checkout_src_compiler_translator_InfoSink.h) = 2f73c76c48852613e0c55c1680fcc2a9eb3cf4ef 27SHA1 (patch-gfx_angle_checkout_src_compiler_translator_InfoSink.h) = 2f73c76c48852613e0c55c1680fcc2a9eb3cf4ef
21SHA1 (patch-gfx_gl_GLContextProviderGLX.cpp) = 2c909a10a341e600392417240ad0c556f495d6ba 28SHA1 (patch-gfx_gl_GLContextProviderGLX.cpp) = 2c909a10a341e600392417240ad0c556f495d6ba
22SHA1 (patch-gfx_skia_skia_src_core_SkCpu.cpp) = 36218819254f3681b9c717d652ea78c9f20d49ad 29SHA1 (patch-gfx_skia_skia_src_core_SkCpu.cpp) = 36218819254f3681b9c717d652ea78c9f20d49ad
23SHA1 (patch-gfx_thebes_gfxPlatform.cpp) = e4a25e4a96055b1c42ec018b02d1828257a571de 30SHA1 (patch-gfx_thebes_gfxPlatform.cpp) = e4a25e4a96055b1c42ec018b02d1828257a571de
24SHA1 (patch-ipc_chromium_src_base_message__pump__libevent.cc) = 4a6606da590cfb8d855bde58b9c6f90e98d0870c 31SHA1 (patch-ipc_chromium_src_base_message__pump__libevent.cc) = 4a6606da590cfb8d855bde58b9c6f90e98d0870c
25SHA1 (patch-ipc_chromium_src_base_platform__thread__posix.cc) = 35d20981d33ccdb1d8ffb8039e48798777f11658 32SHA1 (patch-ipc_chromium_src_base_platform__thread__posix.cc) = 35d20981d33ccdb1d8ffb8039e48798777f11658
26SHA1 (patch-ipc_chromium_src_chrome_common_ipc__channel__posix.cc) = d634805bf3b02475081cb2f263e91e3f4c481a29 33SHA1 (patch-ipc_chromium_src_chrome_common_ipc__channel__posix.cc) = d634805bf3b02475081cb2f263e91e3f4c481a29
27SHA1 (patch-ipc_glue_GeckoChildProcessHost.cpp) = 260c29bacd8bf265951b7a412f850bf2b292c836 34SHA1 (patch-ipc_glue_GeckoChildProcessHost.cpp) = 260c29bacd8bf265951b7a412f850bf2b292c836
28SHA1 (patch-js_src_threading_posix_Thread.cpp) = 47e612a676e614fd6dd43b8a3140218a3fbdc7fa 35SHA1 (patch-js_src_threading_posix_Thread.cpp) = 47e612a676e614fd6dd43b8a3140218a3fbdc7fa
29SHA1 (patch-js_src_util_NativeStack.cpp) = 2c6f844d38343f40ebbc8fd665279256e4ae6d35 36SHA1 (patch-js_src_util_NativeStack.cpp) = 2c6f844d38343f40ebbc8fd665279256e4ae6d35
30SHA1 (patch-media_ffvpx_libavutil_arm_bswap.h) = de58daa0fd23d4fec50426602b65c9ea5862558a 37SHA1 (patch-media_ffvpx_libavutil_arm_bswap.h) = de58daa0fd23d4fec50426602b65c9ea5862558a
31SHA1 (patch-media_libcubeb_gtest_moz.build) = ea6dcc7ceeb76ce1fb9d508cf43080a2eef3a9e4 38SHA1 (patch-media_libcubeb_gtest_moz.build) = ea6dcc7ceeb76ce1fb9d508cf43080a2eef3a9e4

File Added: pkgsrc/security/tor-browser/patches/Attic/patch-dom_webauthn_u2f-hid-rs_src_lib.rs
$NetBSD: patch-dom_webauthn_u2f-hid-rs_src_lib.rs,v 1.1 2020/08/17 06:58:32 riastradh Exp $

Add NetBSD support for U2F.

--- dom/webauthn/u2f-hid-rs/src/lib.rs.orig	2020-06-22 22:55:03.000000000 +0000
+++ dom/webauthn/u2f-hid-rs/src/lib.rs
@@ -5,7 +5,7 @@
 #[macro_use]
 mod util;
 
-#[cfg(any(target_os = "linux", target_os = "freebsd"))]
+#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))]
 pub mod hidproto;
 
 #[cfg(any(target_os = "linux"))]
@@ -22,6 +22,10 @@ extern crate devd_rs;
 #[path = "freebsd/mod.rs"]
 pub mod platform;
 
+#[cfg(any(target_os = "netbsd"))]
+#[path = "netbsd/mod.rs"]
+pub mod platform;
+
 #[cfg(any(target_os = "macos"))]
 extern crate core_foundation;
 
@@ -36,6 +40,7 @@ pub mod platform;
 #[cfg(not(any(
     target_os = "linux",
     target_os = "freebsd",
+    target_os = "netbsd",
     target_os = "macos",
     target_os = "windows"
 )))]

File Added: pkgsrc/security/tor-browser/patches/Attic/patch-dom_webauthn_u2f-hid-rs_src_netbsd_device.rs
$NetBSD: patch-dom_webauthn_u2f-hid-rs_src_netbsd_device.rs,v 1.1 2020/08/17 06:58:32 riastradh Exp $

Add NetBSD support for U2F.

--- dom/webauthn/u2f-hid-rs/src/netbsd/device.rs.orig	2020-07-15 16:19:08.142403669 +0000
+++ dom/webauthn/u2f-hid-rs/src/netbsd/device.rs
@@ -0,0 +1,134 @@
+/* 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::mem;
+use std::io::Read;
+use std::io::Write;
+use std::io;
+
+use consts::CID_BROADCAST;
+use consts::HID_RPT_SIZE;
+use platform::fd::Fd;
+use platform::uhid;
+use u2ftypes::U2FDevice;
+use util::io_err;
+
+#[derive(Debug)]
+pub struct Device {
+    fd: Fd,
+    cid: [u8; 4],
+}
+
+impl Device {
+    pub fn new(fd: Fd) -> io::Result<Self> {
+        Ok(Self { fd, cid: CID_BROADCAST })
+    }
+
+    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 + 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;
+    }
+}

File Added: pkgsrc/security/tor-browser/patches/Attic/patch-dom_webauthn_u2f-hid-rs_src_netbsd_fd.rs
$NetBSD: patch-dom_webauthn_u2f-hid-rs_src_netbsd_fd.rs,v 1.1 2020/08/17 06:58:32 riastradh Exp $

Add NetBSD support for U2F.

--- dom/webauthn/u2f-hid-rs/src/netbsd/fd.rs.orig	2020-07-15 16:19:08.142740434 +0000
+++ dom/webauthn/u2f-hid-rs/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)
+    }
+}

File Added: pkgsrc/security/tor-browser/patches/Attic/patch-dom_webauthn_u2f-hid-rs_src_netbsd_mod.rs
$NetBSD: patch-dom_webauthn_u2f-hid-rs_src_netbsd_mod.rs,v 1.1 2020/08/17 06:58:32 riastradh Exp $

Add NetBSD support for U2F.

--- dom/webauthn/u2f-hid-rs/src/netbsd/mod.rs.orig	2020-07-15 16:19:08.143016295 +0000
+++ dom/webauthn/u2f-hid-rs/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;

File Added: pkgsrc/security/tor-browser/patches/Attic/patch-dom_webauthn_u2f-hid-rs_src_netbsd_monitor.rs
$NetBSD: patch-dom_webauthn_u2f-hid-rs_src_netbsd_monitor.rs,v 1.1 2020/08/17 06:58:32 riastradh Exp $

Add NetBSD support for U2F.

--- dom/webauthn/u2f-hid-rs/src/netbsd/monitor.rs.orig	2020-07-15 16:19:08.143281894 +0000
+++ dom/webauthn/u2f-hid-rs/src/netbsd/monitor.rs
@@ -0,0 +1,89 @@
+/* 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 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);
+        }
+    }
+}

File Added: pkgsrc/security/tor-browser/patches/Attic/patch-dom_webauthn_u2f-hid-rs_src_netbsd_transaction.rs
$NetBSD: patch-dom_webauthn_u2f-hid-rs_src_netbsd_transaction.rs,v 1.1 2020/08/17 06:58:32 riastradh Exp $

Add NetBSD support for U2F.

--- dom/webauthn/u2f-hid-rs/src/netbsd/transaction.rs.orig	2020-07-15 16:19:08.143583561 +0000
+++ dom/webauthn/u2f-hid-rs/src/netbsd/transaction.rs
@@ -0,0 +1,50 @@
+/* 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 runloop::RunLoop;
+use util::OnceCallback;
+
+use platform::fd::Fd;
+use platform::monitor::Monitor;
+
+pub struct Transaction {
+    // Handle to the thread loop.
+    thread: Option<RunLoop>,
+}
+
+impl Transaction {
+    pub fn new<F, T>(
+        timeout: u64,
+        callback: OnceCallback<T>,
+        new_device_cb: F,
+    ) -> Result<Self, ::Error>
+    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(::Error::Unknown)));
+
+                // Send an error, if the callback wasn't called already.
+                callback.call(Err(::Error::NotAllowed));
+            },
+            timeout,
+        )
+        .map_err(|_| ::Error::Unknown)?;
+
+        Ok(Self {
+            thread: Some(thread),
+        })
+    }
+
+    pub fn cancel(&mut self) {
+        // This must never be None.
+        self.thread.take().unwrap().cancel();
+    }
+}

File Added: pkgsrc/security/tor-browser/patches/Attic/patch-dom_webauthn_u2f-hid-rs_src_netbsd_uhid.rs
$NetBSD: patch-dom_webauthn_u2f-hid-rs_src_netbsd_uhid.rs,v 1.1 2020/08/17 06:58:32 riastradh Exp $

Add NetBSD support for U2F.

--- dom/webauthn/u2f-hid-rs/src/netbsd/uhid.rs.orig	2020-07-15 16:19:08.143860020 +0000
+++ dom/webauthn/u2f-hid-rs/src/netbsd/uhid.rs
@@ -0,0 +1,79 @@
+/* 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 hidproto::ReportDescriptor;
+use hidproto::has_fido_usage;
+use platform::fd::Fd;
+use 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(())
+}