diff --git a/Cargo.lock b/Cargo.lock index 25245035e7..4c660ebb37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,56 +6,96 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libc" -version = "0.2.65" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rand" -version = "0.3.23" +name = "block-buffer" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "rand" -version = "0.4.6" +name = "block-padding" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "rand_core" +name = "byte-tools" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "c2-chacha" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand_chacha" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_core" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "randd" version = "0.1.0" dependencies = [ - "rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "raw-cpuid 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -66,49 +106,42 @@ dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "redox_syscall" version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "winapi" -version = "0.3.8" +name = "sha2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "typenum" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" -"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" -"checksum rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" -"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" +"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" "checksum raw-cpuid 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13b844e4049605ff38fed943f5c7b2c691fad68d9d5bf074d2720554c4e48246" -"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" -"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" +"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" diff --git a/Cargo.toml b/Cargo.toml index 95c8a04049..c29ebdca85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,8 +3,10 @@ name = "randd" version = "0.1.0" [dependencies] -rand = "0.3" +rand_chacha = "0.2" +rand_core = "0.5" redox_syscall = "0.1" +sha2 = "0.8" [target.'cfg(target_arch = "x86_64")'.dependencies] raw-cpuid = "2.0" diff --git a/src/main.rs b/src/main.rs index 508326d484..1a40e6bc38 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,56 +2,369 @@ extern crate syscall; +extern crate rand_chacha; +extern crate rand_core; #[cfg(target_arch = "x86_64")] extern crate raw_cpuid; -extern crate rand; +extern crate sha2; use std::fs::File; use std::io::{Read, Write}; -use std::os::unix::io::AsRawFd; -use rand::chacha::ChaChaRng; -use rand::Rng; +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 syscall::{Error, Result, SchemeMut, EINVAL, MODE_CHR}; use syscall::data::{Packet, Stat}; use syscall::flag::EVENT_READ; +use syscall::{ + Error, Result, SchemeMut, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, EPERM, MODE_CHR, O_CLOEXEC, + O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_STAT, O_WRONLY, +}; -//TODO: Use a CSPRNG, allow write of entropy +// 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; + +// TODO consider adding blocking read based on available entropy as per linux /dev/random +// (compared to /dev/urandom which we are equivalent to) + +// 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" + : "={rax}"(rand) + : + : + : "intel", "volatile"); + } + + 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: File, - prng: ChaChaRng + 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: File) -> 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("/".as_bytes(), O_RDWR, 1, 1).is_err()); + assert!(scheme.open("/".as_bytes(), O_RDONLY, 1, 1).is_err()); + + scheme.prng_stat.st_mode = MODE_CHR | 0o400; + let mut fd = scheme.open("".as_bytes(), 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("".as_bytes(), O_WRONLY, 1, 1).is_err()); + assert!(scheme.open("".as_bytes(), O_RDWR, 1, 1).is_err()); + + scheme.prng_stat.st_mode = MODE_CHR | 0o600; + fd = scheme.open("".as_bytes(), 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("".as_bytes(), 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("".as_bytes(), 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("".as_bytes(), 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("".as_bytes(), 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("".as_bytes(), 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".as_bytes(), 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: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result { - Ok(0) + fn open(&mut self, path: &[u8], 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 != "".as_bytes() && path != "/urandom".as_bytes() { + 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) + } + + fn chmod(&mut self, path: &[u8], mode: u16, uid: u32, gid: u32) -> Result { + // Defer to fchmod + let fd = self.open(path, O_WRONLY, uid, gid)?; + self.fchmod(fd, mode) } fn dup(&mut self, file: usize, buf: &[u8]) -> Result { - if ! buf.is_empty() { + if !buf.is_empty() { return Err(Error::new(EINVAL)); } Ok(file) } + /* Resource operations */ + fn read(&mut self, file: usize, buf: &mut [u8]) -> Result { + // Check fd and permissions + self.can_perform_op_on_fd(file, MODE_READ)?; - fn read(&mut self, _file: usize, buf: &mut [u8]) -> Result { - let mut i = 0; - for chunk in buf.chunks_mut(8) { - let mut rand = self.prng.next_u64(); - for b in chunk.iter_mut() { - *b = rand as u8; - rand = rand >> 8; - i += 1; - } - } - Ok(i) + // 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]) -> 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: usize) -> Result { + Ok(EVENT_READ) + } fn fpath(&mut self, _file: usize, buf: &mut [u8]) -> Result { let mut i = 0; let scheme_path = b"rand"; @@ -62,68 +375,52 @@ impl SchemeMut for RandScheme { Ok(i) } - fn fstat(&mut self, _id: usize, stat: &mut Stat) -> Result { - *stat = Stat { - st_mode: MODE_CHR | 0o666, - ..Default::default() - }; + 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 fevent(&mut self, _id: usize, _flags: usize) -> Result { - Ok(EVENT_READ) - } - - fn fcntl(&mut self, _id: usize, _cmd: usize, _arg: usize) -> Result { - Ok(0) - } - - fn close(&mut self, _file: usize) -> Result { - 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 main(){ - let mut has_rdrand = false; - - #[cfg(target_arch = "x86_64")] - { - has_rdrand = CpuId::new().get_feature_info().unwrap().has_rdrand(); - } - +fn main() { // Daemonize if unsafe { syscall::clone(0).unwrap() } == 0 { let socket = File::create(":rand").expect("rand: failed to create rand scheme"); - let mut rng = ChaChaRng::new_unseeded(); - - if has_rdrand { - println!("rand: seeding with rdrand"); - let rand: u64; - unsafe { - asm!("rdrand rax" - : "={rax}"(rand) - : - : - : "intel", "volatile"); - } - rng.set_counter(0, rand); - } else { - println!("rand: unseeded"); - } - - let mut scheme = RandScheme{socket, prng: rng}; + let mut scheme = RandScheme::new(socket); syscall::setrens(0, 0).expect("randd: failed to enter null namespace"); loop { let mut packet = Packet::default(); - if scheme.socket.read(&mut packet).expect("rand: failed to read events from rand scheme") == 0 { - break; + match scheme.socket.read(&mut packet) { + Ok(s) => { + if s == 0 { + break; + } + scheme.handle(&mut packet); + match scheme.socket.write(&packet) { + Err(e) => println!("Error writing packet {}", e), + _ => {} + } + } + Err(e) => println!("Error reading packet {}", e), } - scheme.handle(&mut packet); - scheme.socket.write(&packet).expect("rand: failed to write responses to rand scheme"); + scheme + .socket + .write(&packet) + .expect("rand: failed to write responses to rand scheme"); } } }