Add 'randd/' from commit '80e27cb3d923e3a2c91b59122376e18233111a99'
git-subtree-dir: randd git-subtree-mainline:2179992ac3git-subtree-split:80e27cb3d9
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/target/
|
||||
Generated
+240
@@ -0,0 +1,240 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
|
||||
dependencies = [
|
||||
"block-padding",
|
||||
"byte-tools",
|
||||
"byteorder",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
|
||||
dependencies = [
|
||||
"byte-tools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byte-tools"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fake-simd"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.160"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0b21006cd1874ae9e650973c565615676dc4a274c965bb0a73796dac838ce4f"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
|
||||
[[package]]
|
||||
name = "randd"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libredox",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"raw-cpuid",
|
||||
"redox-daemon",
|
||||
"redox-scheme",
|
||||
"redox_syscall",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "raw-cpuid"
|
||||
version = "11.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox-daemon"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fbdeffdb03cf2961b211a2023e683d71f2c225ea93404da5dc34b0dc94f0341"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"libredox",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox-scheme"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95624e20d2c1808f7ca0820720d30aad9f5d2fc404e1ef379431ad7a790c3f7e"
|
||||
dependencies = [
|
||||
"libredox",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"digest",
|
||||
"fake-simd",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "randd"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
rand_chacha = "0.2"
|
||||
rand_core = "0.5"
|
||||
redox-daemon = "0.1.2"
|
||||
redox_syscall = "0.5"
|
||||
libredox = "0.1.3"
|
||||
sha2 = "0.8"
|
||||
redox-scheme = "0.2.1"
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||
raw-cpuid = "11.0.1"
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Redox OS
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,19 @@
|
||||
# randd
|
||||
|
||||
Random number generator daemon.
|
||||
|
||||
## How To Contribute
|
||||
|
||||
To learn how to contribute to this system component you need to read the following document:
|
||||
|
||||
- [CONTRIBUTING.md](https://gitlab.redox-os.org/redox-os/redox/-/blob/master/CONTRIBUTING.md)
|
||||
|
||||
## Development
|
||||
|
||||
To learn how to do development with this system component inside the Redox build system you need to read the [Build System](https://doc.redox-os.org/book/build-system-reference.html) and [Coding and Building](https://doc.redox-os.org/book/coding-and-building.html) pages.
|
||||
|
||||
### How To Build
|
||||
|
||||
To build this system component you need to download the Redox build system, you can learn how to do it on the [Building Redox](https://doc.redox-os.org/book/podman-build.html) page.
|
||||
|
||||
This is necessary because they only work with cross-compilation to a Redox virtual machine, but you can do some testing from Linux.
|
||||
@@ -0,0 +1,394 @@
|
||||
use std::process;
|
||||
|
||||
use std::arch::asm;
|
||||
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
use rand_core::RngCore;
|
||||
|
||||
pub const MODE_PERM: u16 = 0x0FFF;
|
||||
pub const MODE_EXEC: u16 = 0o1;
|
||||
pub const MODE_WRITE: u16 = 0o2;
|
||||
pub const MODE_READ: u16 = 0o4;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use raw_cpuid::CpuId;
|
||||
|
||||
use redox_scheme::{RequestKind, SchemeMut, SignalBehavior, Socket, V2};
|
||||
use syscall::data::Stat;
|
||||
use syscall::flag::EventFlags;
|
||||
use syscall::{
|
||||
Error, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, EPERM, MODE_CHR, O_CLOEXEC, O_CREAT,
|
||||
O_EXCL, O_RDONLY, O_RDWR, O_STAT, O_WRONLY,
|
||||
};
|
||||
|
||||
// Create an RNG Seed to create initial seed from the rdrand intel instruction
|
||||
use rand_core::SeedableRng;
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::collections::BTreeMap;
|
||||
use std::num::Wrapping;
|
||||
|
||||
// This Daemon implements a Cryptographically Secure Random Number Generator
|
||||
// that does not block on read - i.e. it is equivalent to linux /dev/urandom
|
||||
// We do not implement blocking reads as per linux /dev/random for the reasons outlined
|
||||
// here: https://www.2uo.de/myths-about-urandom/
|
||||
|
||||
// Default file access mode for PRNG
|
||||
const DEFAULT_PRNG_MODE: u16 = 0o644;
|
||||
// Rand crate recommends at least 256 bits of entropy to seed the RNG
|
||||
const SEED_BYTES: usize = 32;
|
||||
|
||||
/// Create a true random seed for the RNG from the Intel x64 rdrand instruction if present.
|
||||
/// Will seed with a zero (insecure) if rdrand not present.
|
||||
fn create_rdrand_seed() -> [u8; SEED_BYTES] {
|
||||
let mut rng = [0; SEED_BYTES];
|
||||
let mut have_seeded = false;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
if CpuId::new().get_feature_info().unwrap().has_rdrand() {
|
||||
for i in 0..SEED_BYTES / 8 {
|
||||
// We get 8 bytes at a time from rdrand instruction
|
||||
let rand: u64;
|
||||
unsafe {
|
||||
asm!("rdrand rax", out("rax") rand);
|
||||
}
|
||||
|
||||
rng[i * 8..(i * 8 + 8)].copy_from_slice(&rand.to_le_bytes());
|
||||
}
|
||||
have_seeded = true;
|
||||
}
|
||||
} // TODO integrate alternative entropy sources
|
||||
if !have_seeded {
|
||||
println!("randd: Seeding failed, no entropy source. Random numbers on this platform are NOT SECURE");
|
||||
}
|
||||
rng
|
||||
}
|
||||
|
||||
/// Contains information about an open file
|
||||
struct OpenFileInfo {
|
||||
o_flags: usize,
|
||||
/// Flags used when opening file.
|
||||
uid: u32,
|
||||
gid: u32,
|
||||
file_stat: Stat,
|
||||
}
|
||||
|
||||
impl OpenFileInfo {
|
||||
/// Tests if the current user has enough permissions to view the file, op is the operation,
|
||||
/// like read and write, these modes are MODE_EXEC, MODE_READ, and MODE_WRITE
|
||||
/// Copied from redoxfs
|
||||
fn permission(&self, op: u16) -> bool {
|
||||
let mut perm = self.file_stat.st_mode & 0o7;
|
||||
if self.uid == self.file_stat.st_uid {
|
||||
// If self.mode is 101100110, >> 6 would be 000000101
|
||||
// 0o7 is octal for 111, or, when expanded to 9 digits is 000000111
|
||||
perm |= (self.file_stat.st_mode >> 6) & 0o7;
|
||||
// Since we erased the GID and OTHER bits when >>6'ing, |= will keep those bits in place.
|
||||
}
|
||||
if self.gid == self.file_stat.st_gid || self.file_stat.st_gid == 0 {
|
||||
perm |= (self.file_stat.st_mode >> 3) & 0o7;
|
||||
}
|
||||
if self.uid == 0 {
|
||||
//set the `other` bits to 111
|
||||
perm |= 0o7;
|
||||
}
|
||||
perm & op == op
|
||||
}
|
||||
fn o_flag_set(&self, f: usize) -> bool {
|
||||
return (f & self.o_flags) == f;
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct to represent the rand scheme.
|
||||
struct RandScheme {
|
||||
socket: Socket,
|
||||
prng: ChaCha20Rng,
|
||||
// ChaCha20 is a Cryptographically Secure PRNG
|
||||
// https://docs.rs/rand/0.5.0/rand/prng/chacha/struct.ChaChaRng.html
|
||||
// Allows 2^64 streams of random numbers, which we will equate with file numbers
|
||||
prng_stat: Stat,
|
||||
open_descriptors: BTreeMap<usize, OpenFileInfo>, // Cannot use HashMap as the implementation
|
||||
// calls the system RNG (us) for entropy to protect against HashDOS attacks.
|
||||
// Trying to create a HashMap causes a system crash.
|
||||
// <file number, information about the open file>
|
||||
next_fd: Wrapping<usize>,
|
||||
}
|
||||
|
||||
impl RandScheme {
|
||||
/// Create new rand scheme from a message socket
|
||||
fn new(socket: Socket) -> RandScheme {
|
||||
RandScheme {
|
||||
socket,
|
||||
prng: ChaCha20Rng::from_seed(create_rdrand_seed()),
|
||||
prng_stat: Stat {
|
||||
st_mode: MODE_CHR | DEFAULT_PRNG_MODE,
|
||||
st_gid: 0,
|
||||
st_uid: 0,
|
||||
..Default::default()
|
||||
},
|
||||
open_descriptors: BTreeMap::new(),
|
||||
next_fd: Wrapping(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the open file info for a file descriptor if it is open - error otherwise.
|
||||
fn get_fd(&self, fd: usize) -> Result<&OpenFileInfo> {
|
||||
// Check we've got a valid file descriptor
|
||||
let file_info = match self.open_descriptors.get(&fd) {
|
||||
Some(m) => m,
|
||||
None => return Err(Error::new(EBADF)),
|
||||
};
|
||||
Ok(file_info)
|
||||
}
|
||||
/// Checks to see if the op (MODE_READ, MODE_WRITE) can be performed on the open file
|
||||
/// descriptor - Will return the open file info if successful, and error if the file
|
||||
/// descriptor is invalid, or the permission is denied.
|
||||
fn can_perform_op_on_fd(&self, fd: usize, op: u16) -> Result<&OpenFileInfo> {
|
||||
let file_info = self.get_fd(fd)?;
|
||||
if !file_info.permission(op) {
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
Ok(file_info)
|
||||
}
|
||||
/// Reseed the CSPRNG with the supplied entropy.
|
||||
/// TODO add this to an entropy pool and give a limited estimate to the amount of entropy
|
||||
/// TODO consider having trusted and untrusted entropy URIs, with different permissions.
|
||||
fn reseed_prng(&mut self, entropy: &[u8]) {
|
||||
// Need to fill a fixed size array for the from_seed, so we'll do 256 bit
|
||||
// array and has the entropy into it.
|
||||
let mut digest = Sha256::new();
|
||||
digest.input(entropy);
|
||||
let hash = digest.result();
|
||||
let mut entropy_array: [u8; SEED_BYTES] = [0; SEED_BYTES];
|
||||
entropy_array.copy_from_slice(hash.as_slice());
|
||||
self.prng = ChaCha20Rng::from_seed(entropy_array);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scheme_perms() {
|
||||
let mut scheme = RandScheme::new(File::open(".").unwrap());
|
||||
scheme.prng_stat.st_mode = MODE_CHR | 0o200;
|
||||
scheme.prng_stat.st_uid = 1;
|
||||
scheme.prng_stat.st_gid = 1;
|
||||
assert!(scheme.open("/", O_RDWR, 1, 1).is_err());
|
||||
assert!(scheme.open("/", O_RDONLY, 1, 1).is_err());
|
||||
|
||||
scheme.prng_stat.st_mode = MODE_CHR | 0o400;
|
||||
let mut fd = scheme.open("", O_RDONLY, 1, 1).unwrap();
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok());
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err());
|
||||
assert!(scheme.close(fd).is_ok());
|
||||
|
||||
assert!(scheme.open("", O_WRONLY, 1, 1).is_err());
|
||||
assert!(scheme.open("", O_RDWR, 1, 1).is_err());
|
||||
|
||||
scheme.prng_stat.st_mode = MODE_CHR | 0o600;
|
||||
fd = scheme.open("", O_RDWR, 1, 1).unwrap();
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok());
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok());
|
||||
assert!(scheme.close(fd).is_ok());
|
||||
|
||||
fd = scheme.open("", O_STAT, 2, 2).unwrap();
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_err());
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err());
|
||||
assert!(scheme.close(fd).is_ok());
|
||||
fd = scheme.open("", O_STAT | O_CLOEXEC, 2, 2).unwrap();
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_err());
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err());
|
||||
assert!(scheme.close(fd).is_ok());
|
||||
|
||||
// Try another user in group (no group perms)
|
||||
fd = scheme.open("", O_STAT | O_CLOEXEC, 2, 1).unwrap();
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_err());
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err());
|
||||
assert!(scheme.close(fd).is_ok());
|
||||
scheme.prng_stat.st_mode = MODE_CHR | 0o660;
|
||||
fd = scheme.open("", O_STAT | O_CLOEXEC, 2, 1).unwrap();
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok());
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok());
|
||||
assert!(scheme.close(fd).is_ok());
|
||||
|
||||
// Check root can do anything
|
||||
scheme.prng_stat.st_mode = MODE_CHR | 0o000;
|
||||
fd = scheme.open("", O_STAT | O_CLOEXEC, 0, 0).unwrap();
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok());
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok());
|
||||
assert!(scheme.close(fd).is_ok());
|
||||
|
||||
// Check the rand:/urandom URL (Equivalent to rand:/)
|
||||
scheme.prng_stat.st_mode = MODE_CHR | 0o660;
|
||||
fd = scheme.open("/urandom", O_STAT | O_CLOEXEC, 2, 1).unwrap();
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok());
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok());
|
||||
assert!(scheme.close(fd).is_ok());
|
||||
}
|
||||
|
||||
impl SchemeMut for RandScheme {
|
||||
fn open(&mut self, path: &str, flags: usize, uid: u32, gid: u32) -> Result<usize> {
|
||||
// We are only allowing
|
||||
// reads/writes from rand:/ and rand:/urandom - the root directory on its own is passed as an empty slice
|
||||
if path != "" && path != "/urandom" {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
if flags & (O_CREAT | O_EXCL) == O_CREAT | O_EXCL {
|
||||
return Err(Error::new(EEXIST));
|
||||
}
|
||||
|
||||
let fd = self.next_fd;
|
||||
let open_file_info = OpenFileInfo {
|
||||
o_flags: flags,
|
||||
file_stat: self.prng_stat,
|
||||
uid,
|
||||
gid,
|
||||
};
|
||||
|
||||
if (open_file_info.o_flag_set(O_RDONLY) || open_file_info.o_flag_set(O_RDWR))
|
||||
&& !open_file_info.permission(MODE_READ)
|
||||
{
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
if (open_file_info.o_flag_set(O_WRONLY) || open_file_info.o_flag_set(O_RDWR))
|
||||
&& !open_file_info.permission(MODE_WRITE)
|
||||
{
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
self.open_descriptors.insert(fd.0, open_file_info);
|
||||
// Get the next file descriptor
|
||||
self.next_fd += Wrapping(1);
|
||||
// If we've looped round there's a small chance that the file descriptor still exists, so loop till we get one that doesn't
|
||||
loop {
|
||||
if !self.open_descriptors.contains_key(&self.next_fd.0) {
|
||||
break;
|
||||
} else {
|
||||
self.next_fd += Wrapping(1);
|
||||
}
|
||||
}
|
||||
Ok(fd.0)
|
||||
}
|
||||
|
||||
/* Resource operations */
|
||||
fn read(&mut self, file: usize, buf: &mut [u8], _offset: u64, _flags: u32) -> Result<usize> {
|
||||
// Check fd and permissions
|
||||
self.can_perform_op_on_fd(file, MODE_READ)?;
|
||||
|
||||
// Setting the stream will ensure that if two clients are reading concurrently, they won't get the same numbers
|
||||
self.prng.set_stream(file as u64); // Should probably find a way to re-instate the counter for this stream, but
|
||||
// not doing so won't make the output any less 'random'
|
||||
self.prng.fill_bytes(buf);
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn write(&mut self, file: usize, buf: &[u8], _offset: u64, _flags: u32) -> Result<usize> {
|
||||
// Check fd and permissions
|
||||
self.can_perform_op_on_fd(file, MODE_WRITE)?;
|
||||
|
||||
// TODO - when we support other entropy sources, just add this to an entropy pool
|
||||
// TODO - consider having trusted and untrusted entropy writing paths
|
||||
// We have a healthy mistrust of the entropy we're being given, so we won't seed just with
|
||||
// that as the resulting numbers would be predictable based on this input
|
||||
// we'll take 512 bits (arbitrary) from the current PRNG, and seed with that
|
||||
// and the supplied data.
|
||||
|
||||
let mut rng_buf: [u8; SEED_BYTES] = [0; SEED_BYTES];
|
||||
self.prng.fill_bytes(&mut rng_buf);
|
||||
let mut rng_vec = Vec::new();
|
||||
rng_vec.extend(&rng_buf);
|
||||
rng_vec.extend(buf);
|
||||
self.reseed_prng(&rng_vec);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn fchmod(&mut self, file: usize, mode: u16) -> Result<usize> {
|
||||
// Check fd and permissions
|
||||
let file_info = self.get_fd(file)?;
|
||||
// only root and owner can chmod
|
||||
if file_info.uid != file_info.file_stat.st_uid && file_info.uid != 0 {
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
|
||||
self.prng_stat.st_mode = MODE_CHR | mode;
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fchown(&mut self, file: usize, uid: u32, gid: u32) -> Result<usize> {
|
||||
// Check fd and permissions
|
||||
let file_info = self.get_fd(file)?;
|
||||
// only root and owner can chmod
|
||||
if file_info.uid != file_info.file_stat.st_uid && file_info.uid != 0 {
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
|
||||
self.prng_stat.st_uid = uid;
|
||||
self.prng_stat.st_gid = gid;
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fcntl(&mut self, _id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
|
||||
// Just ignore this.
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fevent(&mut self, _id: usize, _flags: EventFlags) -> Result<EventFlags> {
|
||||
Ok(EventFlags::EVENT_READ)
|
||||
}
|
||||
fn fpath(&mut self, _file: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let mut i = 0;
|
||||
let scheme_path = b"rand";
|
||||
while i < buf.len() && i < scheme_path.len() {
|
||||
buf[i] = scheme_path[i];
|
||||
i += 1;
|
||||
}
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn fstat(&mut self, file: usize, stat: &mut Stat) -> Result<usize> {
|
||||
// Check fd and permissions
|
||||
self.can_perform_op_on_fd(file, MODE_READ)?;
|
||||
|
||||
*stat = self.prng_stat.clone();
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&mut self, file: usize) -> Result<usize> {
|
||||
// just remove the file descriptor from the open descriptors
|
||||
match self.open_descriptors.remove(&file) {
|
||||
Some(_) => Ok(0),
|
||||
None => Err(Error::new(EBADFD)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
let socket = Socket::<V2>::create("rand").expect("randd: failed to create rand scheme");
|
||||
|
||||
let mut scheme = RandScheme::new(socket);
|
||||
|
||||
daemon
|
||||
.ready()
|
||||
.expect("randd: failed to mark daemon as ready");
|
||||
|
||||
libredox::call::setrens(0, 0).expect("randd: failed to enter null namespace");
|
||||
|
||||
while let Some(request) = scheme
|
||||
.socket
|
||||
.next_request(SignalBehavior::Restart)
|
||||
.expect("error reading packet")
|
||||
{
|
||||
let RequestKind::Call(call) = request.kind() else {
|
||||
continue;
|
||||
};
|
||||
let response = call.handle_scheme_mut(&mut scheme);
|
||||
scheme
|
||||
.socket
|
||||
.write_responses(&[response], SignalBehavior::Restart)
|
||||
.expect("error writing packet");
|
||||
}
|
||||
|
||||
process::exit(0);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
redox_daemon::Daemon::new(daemon).expect("randd: failed to daemonize");
|
||||
}
|
||||
Reference in New Issue
Block a user