initial commit
This commit is contained in:
commit
f3a453a88e
|
@ -0,0 +1 @@
|
||||||
|
/target
|
|
@ -0,0 +1,16 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fakerand"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.155"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "fakerand"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "0.2"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
|
@ -0,0 +1,23 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
=====================
|
||||||
|
|
||||||
|
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,11 @@
|
||||||
|
CARGO_BUILD=cargo build --release
|
||||||
|
TARGET_DIR=target/release
|
||||||
|
LIB_NAME=libfakerand.so
|
||||||
|
INSTALL_DIR=/usr/lib
|
||||||
|
|
||||||
|
all: build
|
||||||
|
|
||||||
|
build:
|
||||||
|
$(CARGO_BUILD)
|
||||||
|
|
||||||
|
.PHONY: all build
|
|
@ -0,0 +1,39 @@
|
||||||
|
# libfakerand
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
`libfakerand` intercepts various system calls that programs use to
|
||||||
|
retrieve entropy. It replaces the entropy which would otherwise be
|
||||||
|
used with the one supplied by the user. This means you can force
|
||||||
|
programs to use a fixed value instead of a random one.
|
||||||
|
|
||||||
|
`libfakerand` overrides the following functions:
|
||||||
|
* `rand()`
|
||||||
|
* `read()` function for `/dev/urandom` and `/dev/random`
|
||||||
|
* `getrandom()`
|
||||||
|
* `RAND_bytes()`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
You may use env variables `LD_PRELOAD` and `FAKERAND` along with a command:
|
||||||
|
```
|
||||||
|
LD_PRELOAD=./target/release/libfakerand.so FAKERAND=0 openssl rand -hex 16
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, set environment variables so that all programs running in that shell are effected:
|
||||||
|
```
|
||||||
|
export LD_PRELOAD=/usr/lib/fakerand/fakerand.so
|
||||||
|
export FAKERAND=42
|
||||||
|
```
|
||||||
|
|
||||||
|
If you will be using it this way, it may make sense to place the .so file in `/usr/lib/fakerand`
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
Statically linked libraries can't be overriden by `LD_PRELOAD`. For
|
||||||
|
this reason, `libfakerand` can't override cases such as:
|
||||||
|
|
||||||
|
```
|
||||||
|
FAKERAND=42 LD_PRELOAD=./target/release/libfakerand.so awk 'BEGIN{srand(); print rand()}' 2>/dev/null
|
||||||
|
```
|
||||||
|
|
||||||
|
This is because `awk` is statically linked.
|
|
@ -0,0 +1,178 @@
|
||||||
|
use std::env;
|
||||||
|
use std::mem;
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use libc::{
|
||||||
|
c_int,
|
||||||
|
c_uchar,
|
||||||
|
c_uint,
|
||||||
|
c_void,
|
||||||
|
dlsym,
|
||||||
|
getrandom as libc_getrandom,
|
||||||
|
RTLD_NEXT,
|
||||||
|
ssize_t
|
||||||
|
};
|
||||||
|
|
||||||
|
fn fakerand_value() -> Option<i32> {
|
||||||
|
env::var("FAKERAND").ok().and_then(|val| val.parse().ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn rand() -> c_int {
|
||||||
|
println!("rand() called");
|
||||||
|
if let Some(fakerand) = fakerand_value() {
|
||||||
|
println!("rand() value {}", fakerand);
|
||||||
|
return fakerand;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let symbol = dlsym(RTLD_NEXT, "rand\0".as_ptr() as *const _);
|
||||||
|
|
||||||
|
if !symbol.is_null() {
|
||||||
|
let original_rand: extern "C" fn() -> c_int = std::mem::transmute(symbol);
|
||||||
|
return original_rand();
|
||||||
|
} else {
|
||||||
|
eprintln!("Error: original rand() not found");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn read(fd: c_int, buf: *mut c_void, count: usize) -> ssize_t {
|
||||||
|
static mut ORIGINAL_READ: Option<extern "C" fn(c_int, *mut c_void, usize) -> ssize_t> = None;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if ORIGINAL_READ.is_none() {
|
||||||
|
let symbol = dlsym(RTLD_NEXT, "read\0".as_ptr() as *const _);
|
||||||
|
if symbol.is_null() {
|
||||||
|
eprintln!("Error: original read() not found");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
ORIGINAL_READ = Some(std::mem::transmute(symbol));
|
||||||
|
}
|
||||||
|
|
||||||
|
let original_read = ORIGINAL_READ.unwrap();
|
||||||
|
|
||||||
|
let path = format!("/proc/self/fd/{}", fd);
|
||||||
|
let mut actualpath = [0u8; 1024];
|
||||||
|
|
||||||
|
let len = libc::readlink(
|
||||||
|
path.as_ptr() as *const i8,
|
||||||
|
actualpath.as_mut_ptr() as *mut i8,
|
||||||
|
actualpath.len() as _
|
||||||
|
);
|
||||||
|
|
||||||
|
if len != -1 {
|
||||||
|
let actualpath = CStr::from_ptr(actualpath.as_ptr() as *const i8).to_str().unwrap();
|
||||||
|
if (actualpath == "/dev/urandom" || actualpath == "/dev/random") && fakerand_value().is_some() {
|
||||||
|
let fakerand = fakerand_value().unwrap();
|
||||||
|
*(buf as *mut c_int) = fakerand;
|
||||||
|
return std::mem::size_of::<c_int>() as ssize_t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
original_read(fd, buf, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn getrandom(buf: *mut c_void, buflen: usize, flags: c_uint) -> ssize_t {
|
||||||
|
if let Some(fakerand) = fakerand_value() {
|
||||||
|
unsafe {
|
||||||
|
*(buf as *mut c_int) = fakerand;
|
||||||
|
}
|
||||||
|
return std::mem::size_of::<c_int>() as ssize_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
libc_getrandom(buf, buflen, flags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn RAND_bytes(buf: *mut c_uchar, num: c_int) -> c_int {
|
||||||
|
if let Some(fakerand) = fakerand_value() {
|
||||||
|
unsafe {
|
||||||
|
for i in 0..num {
|
||||||
|
*buf.add(i as usize) = fakerand as c_uchar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let symbol = dlsym(RTLD_NEXT, "RAND_bytes\0".as_ptr() as *const _);
|
||||||
|
if !symbol.is_null() {
|
||||||
|
let original_rand_bytes: extern "C" fn(*mut c_uchar, c_int) -> c_int = mem::transmute(symbol);
|
||||||
|
return original_rand_bytes(buf, num);
|
||||||
|
} else {
|
||||||
|
eprintln!("Error: original RAND_bytes() not found");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rand_with_fakerand() {
|
||||||
|
env::set_var("FAKERAND", "42");
|
||||||
|
assert_eq!(rand(), 42);
|
||||||
|
env::remove_var("FAKERAND");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rand_without_fakerand() {
|
||||||
|
env::remove_var("FAKERAND");
|
||||||
|
let rand_value = rand();
|
||||||
|
// TODO: Does this mean the test will fail once in a while?
|
||||||
|
assert!(rand_value != 42);
|
||||||
|
assert!(rand_value >= 0 && rand_value <= libc::RAND_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_with_fakerand() {
|
||||||
|
env::set_var("FAKERAND", "42");
|
||||||
|
|
||||||
|
let file = OpenOptions::new().read(true).open("/dev/urandom").expect("Failed to open /dev/urandom");
|
||||||
|
let fd = file.as_raw_fd();
|
||||||
|
let mut buf = [0u8; 4];
|
||||||
|
let result = read(fd, buf.as_mut_ptr() as *mut c_void, buf.len());
|
||||||
|
|
||||||
|
assert_eq!(result, std::mem::size_of::<c_int>() as ssize_t);
|
||||||
|
assert_eq!(buf, [42, 0, 0, 0]);
|
||||||
|
|
||||||
|
env::remove_var("FAKERAND");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_without_fakerand() {
|
||||||
|
env::remove_var("FAKERAND");
|
||||||
|
|
||||||
|
let file = OpenOptions::new().read(true).open("/dev/urandom").expect("Failed to open /dev/urandom");
|
||||||
|
let fd = file.as_raw_fd();
|
||||||
|
let mut buf = [0u8; 4];
|
||||||
|
let result = read(fd, buf.as_mut_ptr() as *mut c_void, buf.len());
|
||||||
|
|
||||||
|
assert_eq!(result as usize, buf.len());
|
||||||
|
assert!(buf != [0, 0, 0, 0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rand_bytes_with_fakerand() {
|
||||||
|
env::set_var("FAKERAND", "42");
|
||||||
|
|
||||||
|
let mut buf = [0u8; 4];
|
||||||
|
let result = RAND_bytes(buf.as_mut_ptr(), buf.len() as c_int);
|
||||||
|
|
||||||
|
assert_eq!(result, 1);
|
||||||
|
assert_eq!(buf, [42, 42, 42, 42]);
|
||||||
|
|
||||||
|
env::remove_var("FAKERAND");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue