From 1e4c55890f278913b65b10e843f43a592782fe8a Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Tue, 24 Dec 2019 18:03:57 -0500 Subject: [PATCH] Refs #164 -- expose the kernel's CSPRNG, safely --- build.rs | 4 +++ src/bindings_helper.h | 1 + src/error.rs | 1 + src/lib.rs | 1 + src/random.rs | 32 ++++++++++++++++++++++++ tests/random/Cargo.toml | 18 ++++++++++++++ tests/random/src/lib.rs | 49 +++++++++++++++++++++++++++++++++++++ tests/random/tests/tests.rs | 19 ++++++++++++++ 8 files changed, 125 insertions(+) create mode 100644 src/random.rs create mode 100644 tests/random/Cargo.toml create mode 100644 tests/random/src/lib.rs create mode 100644 tests/random/tests/tests.rs diff --git a/build.rs b/build.rs index b81e2177..d3dac9c7 100644 --- a/build.rs +++ b/build.rs @@ -25,12 +25,16 @@ const INCLUDED_FUNCTIONS: &[&str] = &[ "_copy_from_user", "alloc_chrdev_region", "unregister_chrdev_region", + "wait_for_random_bytes", + "get_random_bytes", + "rng_is_initialized", ]; const INCLUDED_VARS: &[&str] = &[ "EINVAL", "ENOMEM", "ESPIPE", "EFAULT", + "EAGAIN", "__this_module", "FS_REQUIRES_DEV", "FS_BINARY_MOUNTDATA", diff --git a/src/bindings_helper.h b/src/bindings_helper.h index df0e387d..fd8ba5e8 100644 --- a/src/bindings_helper.h +++ b/src/bindings_helper.h @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/src/error.rs b/src/error.rs index 98081bcd..40b48d3e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,6 +10,7 @@ impl Error { pub const ENOMEM: Self = Error(-(bindings::ENOMEM as i32)); pub const EFAULT: Self = Error(-(bindings::EFAULT as i32)); pub const ESPIPE: Self = Error(-(bindings::ESPIPE as i32)); + pub const EAGAIN: Self = Error(-(bindings::EAGAIN as i32)); pub fn from_kernel_errno(errno: c_types::c_int) -> Error { Error(errno) diff --git a/src/lib.rs b/src/lib.rs index f9595036..e0ee4ccd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ mod error; pub mod file_operations; pub mod filesystem; pub mod printk; +pub mod random; pub mod sysctl; mod types; pub mod user_ptr; diff --git a/src/random.rs b/src/random.rs new file mode 100644 index 00000000..af6bdf85 --- /dev/null +++ b/src/random.rs @@ -0,0 +1,32 @@ +use core::convert::TryInto; + +use crate::{bindings, c_types, error}; + +/// Fills `dest` with random bytes generated from the kernel's CSPRNG. Ensures +/// that the CSPRNG has been seeded before generating any random bytes, and +/// will block until it's ready. +pub fn getrandom(dest: &mut [u8]) -> error::KernelResult<()> { + let res = unsafe { bindings::wait_for_random_bytes() }; + if res != 0 { + return Err(error::Error::from_kernel_errno(res)); + } + + unsafe { + bindings::get_random_bytes( + dest.as_mut_ptr() as *mut c_types::c_void, + dest.len().try_into()?, + ); + } + Ok(()) +} + +/// Fills `dest` with random bytes generated from the kernel's CSPRNG. If the +/// CSPRNG is not yet seeded, returns an `Err(EAGAIN)` immediately. Only +/// available on 4.19 and later kernels. +#[cfg(kernel_4_19_0_or_greater)] +pub fn getrandom_nonblock(dest: &mut [u8]) -> error::KernelResult<()> { + if !unsafe { bindings::rng_is_initialized() } { + return Err(error::Error::EAGAIN); + } + getrandom(dest) +} diff --git a/tests/random/Cargo.toml b/tests/random/Cargo.toml new file mode 100644 index 00000000..e8df867b --- /dev/null +++ b/tests/random/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "random-tests" +version = "0.1.0" +authors = ["Alex Gaynor ", "Geoffrey Thomas "] +edition = "2018" + +[lib] +crate-type = ["staticlib"] +test = false + +[features] +default = ["linux-kernel-module"] + +[dependencies] +linux-kernel-module = { path = "../..", optional = true } + +[dev-dependencies] +kernel-module-testlib = { path = "../../testlib" } diff --git a/tests/random/src/lib.rs b/tests/random/src/lib.rs new file mode 100644 index 00000000..251a1ece --- /dev/null +++ b/tests/random/src/lib.rs @@ -0,0 +1,49 @@ +#![no_std] + +use alloc::vec; + +use linux_kernel_module::sysctl::{Sysctl, SysctlStorage}; +use linux_kernel_module::{self, cstr, random, Mode}; + +struct EntropySource; + +impl SysctlStorage for EntropySource { + fn store_value(&self, _data: &[u8]) -> (usize, linux_kernel_module::KernelResult<()>) { + (0, Err(linux_kernel_module::Error::EINVAL)) + } + + fn read_value( + &self, + data: &mut linux_kernel_module::user_ptr::UserSlicePtrWriter, + ) -> (usize, linux_kernel_module::KernelResult<()>) { + let mut storage = vec![0; data.len()]; + if let Err(e) = random::getrandom(&mut storage) { + return (0, Err(e)); + } + (storage.len(), data.write(&storage)) + } +} + +struct RandomTestModule { + _sysctl_entropy: Sysctl, +} + +impl linux_kernel_module::KernelModule for RandomTestModule { + fn init() -> linux_kernel_module::KernelResult { + Ok(RandomTestModule { + _sysctl_entropy: Sysctl::register( + cstr!("rust/random-tests"), + cstr!("entropy"), + EntropySource, + Mode::from_int(0o444), + )?, + }) + } +} + +linux_kernel_module::kernel_module!( + RandomTestModule, + author: "Fish in a Barrel Contributors", + description: "A module for testing the CSPRNG", + license: "GPL" +); diff --git a/tests/random/tests/tests.rs b/tests/random/tests/tests.rs new file mode 100644 index 00000000..16473229 --- /dev/null +++ b/tests/random/tests/tests.rs @@ -0,0 +1,19 @@ +use std::collections::HashSet; +use std::fs; +use std::io::Read; + +use kernel_module_testlib::with_kernel_module; + +#[test] +fn test_random_entropy() { + with_kernel_module(|| { + let mut keys = HashSet::new(); + for _ in 0..1024 { + let mut key = [0; 16]; + let mut f = fs::File::open("/proc/sys/rust/random-tests/entropy").unwrap(); + f.read_exact(&mut key).unwrap(); + keys.insert(key); + } + assert_eq!(keys.len(), 1024); + }); +}