diff --git a/randd/.gitignore b/randd/.gitignore new file mode 100644 index 0000000000..b83d22266a --- /dev/null +++ b/randd/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/randd/Cargo.lock b/randd/Cargo.lock new file mode 100644 index 0000000000..f78bfbc45a --- /dev/null +++ b/randd/Cargo.lock @@ -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", +] diff --git a/randd/Cargo.toml b/randd/Cargo.toml new file mode 100644 index 0000000000..6d30d0c999 --- /dev/null +++ b/randd/Cargo.toml @@ -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" diff --git a/randd/LICENSE b/randd/LICENSE new file mode 100644 index 0000000000..5deeece4f4 --- /dev/null +++ b/randd/LICENSE @@ -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. diff --git a/randd/README.md b/randd/README.md new file mode 100644 index 0000000000..96c6b227ff --- /dev/null +++ b/randd/README.md @@ -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. diff --git a/randd/src/main.rs b/randd/src/main.rs new file mode 100644 index 0000000000..35399b8eb6 --- /dev/null +++ b/randd/src/main.rs @@ -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, // 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. + // + next_fd: Wrapping, +} + +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 { + // 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 { + // 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 { + // 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 { + // 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 { + // 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 { + // Just ignore this. + Ok(0) + } + + fn fevent(&mut self, _id: usize, _flags: EventFlags) -> Result { + Ok(EventFlags::EVENT_READ) + } + fn fpath(&mut self, _file: usize, buf: &mut [u8]) -> Result { + 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 { + // 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 { + // 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::::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"); +}