From 495fefd20f69d2503ba58fb6181e5c067b2cbe97 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 2 May 2024 00:31:50 -0700 Subject: [PATCH] Unconditionally use libc::getrandom on Illumos and Solaris Also removes the use of `GRND_RANDOM`, which appears to be based on outdated staements about the RNGs. For Solaris, see [this blog post](https://blogs.oracle.com/solaris/post/solaris-new-system-calls-getentropy2-and-getrandom2). For Illumos, the algorithms are less clear, but I don't see a clear reason to continue using `GRND_RANDOM`. I updated the documentation in `getrandom.rs` to full document this decision and to have a common place listing when `getrandom(2)` became avalible on each platform. I also updated the main lib.rs docs to point to the correct man pages. Note that Solaris 11.3 has a maximum buffer length of 1024 bytes, while Illumos doesn't have this sort of issue. Signed-off-by: Joe Richey --- src/getrandom.rs | 40 +++++++++++++++++++++++++++++++++++++--- src/lib.rs | 14 +++++++------- src/solaris_illumos.rs | 41 ----------------------------------------- src/use_file.rs | 16 +--------------- 4 files changed, 45 insertions(+), 66 deletions(-) delete mode 100644 src/solaris_illumos.rs diff --git a/src/getrandom.rs b/src/getrandom.rs index 38100384..70c118e8 100644 --- a/src/getrandom.rs +++ b/src/getrandom.rs @@ -1,9 +1,43 @@ //! Implementation using libc::getrandom +//! +//! Available since: +//! - Linux Kernel 3.17, Glibc 2.25, Musl 1.1.20 +//! - Android API level 23 (Marshmallow) +//! - NetBSD 10.0 +//! - FreeBSD 12.0 +//! - Solaris 11.3 +//! - Illumos since Dec 2018 +//! - DragonFly 5.7 +//! - Hurd Glibc 2.31 +//! - shim-3ds since Feb 2022 +//! +//! For all platforms, we use the default randomness source (the one used +//! by /dev/urandom) rather than the /dev/random (GRND_RANDOM) source. For +//! more information see the linked man pages in lib.rs. +//! - On Linux, "/dev/urandom is preferred and sufficient in all use cases". +//! - On NetBSD, "there is no reason to ever use" GRND_RANDOM. +//! - On Illumos, the default source is used for getentropy() and the like: +//! https://github.com/illumos/illumos-gate/blob/89cf0c2ce8a47dcf555bb1596f9034f07b9467fa/usr/src/lib/libc/port/gen/getentropy.c#L33 +//! - On Solaris, both sources use FIPS 140-2 / NIST SP-900-90A DRBGs, see: +//! https://blogs.oracle.com/solaris/post/solaris-new-system-calls-getentropy2-and-getrandom2 +//! - On Redox, only /dev/urandom is provided. +//! - On AIX, /dev/urandom will "provide cryptographically secure output". +//! - On Haiku, QNX Neutrino, DragonFly, and FreeBSD, they are identical. use crate::{util_libc::sys_fill_exact, Error}; use core::mem::MaybeUninit; +// On Solaris 11.3, getrandom() will fail if bufsz > 1024 (bufsz > 133120 on Solaris 11.4). +// This issue is not present in Illumos's implementation of getrandom(). +#[cfg(target_os = "solaris")] +const MAX_BYTES: usize = 1024; +#[cfg(not(target_os = "solaris"))] +const MAX_BYTES: usize = usize::MAX; + pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { - sys_fill_exact(dest, |buf| unsafe { - libc::getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0) - }) + for chunk in dest.chunks_mut(MAX_BYTES) { + sys_fill_exact(chunk, |buf| unsafe { + libc::getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0) + })?; + } + Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 6823041f..d2666f15 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,8 @@ //! | OpenBSD | `*‑openbsd` | [`getentropy`][7] //! | NetBSD | `*‑netbsd` | [`getrandom`][16] if available, otherwise [`kern.arandom`][8] //! | Dragonfly BSD | `*‑dragonfly` | [`getrandom`][9] -//! | Solaris, illumos | `*‑solaris`, `*‑illumos` | [`getrandom`][11] if available, otherwise [`/dev/random`][12] +//! | Solaris | `*‑solaris` | [`getrandom`][11] +//! | Illumos | `*‑illumos` | [`getrandom`][12] //! | Fuchsia OS | `*‑fuchsia` | [`cprng_draw`] //! | Redox | `*‑redox` | `/dev/urandom` //! | Haiku | `*‑haiku` | `/dev/urandom` (identical to `/dev/random`) @@ -25,7 +26,7 @@ //! | WASI | `wasm32‑wasi` | [`random_get`] //! | Web Browser and Node.js | `wasm*‑*‑unknown` | [`Crypto.getRandomValues`] if available, then [`crypto.randomFillSync`] if on Node.js, see [WebAssembly support] //! | SOLID | `*-kmc-solid_*` | `SOLID_RNG_SampleRandomBytes` -//! | Nintendo 3DS | `armv6k-nintendo-3ds` | [`getrandom`][1] +//! | Nintendo 3DS | `*-nintendo-3ds` | [`getrandom`][18] //! | PS Vita | `*-vita-*` | [`getentropy`][13] //! | QNX Neutrino | `*‑nto-qnx*` | [`/dev/urandom`][14] (identical to `/dev/random`) //! | AIX | `*-ibm-aix` | [`/dev/urandom`][15] @@ -177,12 +178,13 @@ //! [8]: https://man.netbsd.org/sysctl.7 //! [9]: https://leaf.dragonflybsd.org/cgi/web-man?command=getrandom //! [11]: https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html -//! [12]: https://docs.oracle.com/cd/E86824_01/html/E54777/random-7d.html +//! [12]: https://illumos.org/man/2/getrandom //! [13]: https://github.com/emscripten-core/emscripten/pull/12240 //! [14]: https://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.utilities/topic/r/random.html //! [15]: https://www.ibm.com/docs/en/aix/7.3?topic=files-random-urandom-devices //! [16]: https://man.netbsd.org/getrandom.2 //! [17]: https://www.gnu.org/software/libc/manual/html_mono/libc.html#index-getrandom +//! [18]: https://github.com/rust3ds/shim-3ds/commit/b01d2568836dea2a65d05d662f8e5f805c64389d //! //! [`BCryptGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom //! [`Crypto.getRandomValues`]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues @@ -249,6 +251,8 @@ cfg_if! { target_os = "dragonfly", target_os = "freebsd", target_os = "hurd", + target_os = "illumos", + target_os = "solaris", // Check for target_arch = "arm" to only include the 3DS. Does not // include the Nintendo Switch (which is target_arch = "aarch64"). all(target_os = "horizon", target_arch = "arm"), @@ -302,10 +306,6 @@ cfg_if! { } else if #[cfg(any(target_os = "android", target_os = "linux"))] { mod util_libc; #[path = "linux_android.rs"] mod imp; - } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] { - mod util_libc; - mod use_file; - #[path = "solaris_illumos.rs"] mod imp; } else if #[cfg(target_os = "netbsd")] { mod util_libc; #[path = "netbsd.rs"] mod imp; diff --git a/src/solaris_illumos.rs b/src/solaris_illumos.rs deleted file mode 100644 index fbc23943..00000000 --- a/src/solaris_illumos.rs +++ /dev/null @@ -1,41 +0,0 @@ -//! Implementation for the Solaris family -//! -//! `/dev/random` uses the Hash_DRBG with SHA512 algorithm from NIST SP 800-90A. -//! `/dev/urandom` uses the FIPS 186-2 algorithm, which is considered less -//! secure. We choose to read from `/dev/random` (and use GRND_RANDOM). -//! -//! Solaris 11.3 and late-2018 illumos added the getrandom(2) libc function. -//! To make sure we can compile on both Solaris and its derivatives, as well as -//! function, we check for the existence of getrandom(2) in libc by calling -//! libc::dlsym. -use crate::{ - use_file, - util_libc::{sys_fill_exact, Weak}, - Error, -}; -use core::mem::{self, MaybeUninit}; - -static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") }; -type GetRandomFn = - unsafe extern "C" fn(*mut libc::c_void, libc::size_t, libc::c_uint) -> libc::ssize_t; - -pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { - if let Some(fptr) = GETRANDOM.ptr() { - let func: GetRandomFn = unsafe { mem::transmute(fptr) }; - // 256 bytes is the lowest common denominator across all the Solaris - // derived platforms for atomically obtaining random data. - for chunk in dest.chunks_mut(256) { - sys_fill_exact(chunk, |buf| unsafe { - // A cast is needed for the flags as libc uses the wrong type. - func( - buf.as_mut_ptr() as *mut libc::c_void, - buf.len(), - libc::GRND_RANDOM as libc::c_uint, - ) - })? - } - Ok(()) - } else { - use_file::getrandom_inner(dest) - } -} diff --git a/src/use_file.rs b/src/use_file.rs index 333325b5..cf2082ed 100644 --- a/src/use_file.rs +++ b/src/use_file.rs @@ -9,21 +9,7 @@ use core::{ sync::atomic::{AtomicUsize, Ordering::Relaxed}, }; -// We prefer using /dev/urandom and only use /dev/random if the OS -// documentation indicates that /dev/urandom is insecure. -// On Solaris/Illumos, see src/solaris_illumos.rs -// On Dragonfly, Haiku, and QNX Neutrino the devices are identical. -#[cfg(any(target_os = "solaris", target_os = "illumos"))] -const FILE_PATH: &str = "/dev/random\0"; -#[cfg(any( - target_os = "aix", - target_os = "android", - target_os = "linux", - target_os = "redox", - target_os = "dragonfly", - target_os = "haiku", - target_os = "nto", -))] +// We always use /dev/urandom, see the comment in getrandom.rs. const FILE_PATH: &str = "/dev/urandom\0"; const FD_UNINIT: usize = usize::max_value();