#[allow(unused_imports)]
use std::ffi::OsString;
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
pub fn pre_main_hardening() {
#[cfg(any(target_os = "linux", target_os = "android"))]
pre_main_hardening_linux();
#[cfg(target_os = "macos")]
pre_main_hardening_macos();
#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
pre_main_hardening_bsd();
#[cfg(windows)]
pre_main_hardening_windows();
}
#[cfg(any(target_os = "linux", target_os = "android"))]
const PRCTL_FAILED_EXIT_CODE: i32 = 5;
#[cfg(target_os = "macos")]
const PTRACE_DENY_ATTACH_FAILED_EXIT_CODE: i32 = 6;
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "macos",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
const SET_RLIMIT_CORE_FAILED_EXIT_CODE: i32 = 7;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub(crate) fn pre_main_hardening_linux() {
let ret_code = unsafe { libc::prctl(libc::PR_SET_DUMPABLE, 0, 0, 0, 0) };
if ret_code != 0 {
eprintln!(
"ERROR: prctl(PR_SET_DUMPABLE, 0) failed: {}",
std::io::Error::last_os_error()
);
std::process::exit(PRCTL_FAILED_EXIT_CODE);
}
set_core_file_size_limit_to_zero();
let ld_keys = env_keys_with_prefix(std::env::vars_os(), b"LD_");
for key in ld_keys {
unsafe {
std::env::remove_var(key);
}
}
}
#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
pub(crate) fn pre_main_hardening_bsd() {
set_core_file_size_limit_to_zero();
let ld_keys = env_keys_with_prefix(std::env::vars_os(), b"LD_");
for key in ld_keys {
unsafe {
std::env::remove_var(key);
}
}
#[cfg(target_os = "openbsd")]
{
}
#[cfg(target_os = "freebsd")]
{
}
}
#[cfg(target_os = "macos")]
pub(crate) fn pre_main_hardening_macos() {
let ret_code = unsafe { libc::ptrace(libc::PT_DENY_ATTACH, 0, std::ptr::null_mut(), 0) };
if ret_code == -1 {
eprintln!(
"ERROR: ptrace(PT_DENY_ATTACH) failed: {}",
std::io::Error::last_os_error()
);
std::process::exit(PTRACE_DENY_ATTACH_FAILED_EXIT_CODE);
}
set_core_file_size_limit_to_zero();
let dyld_keys = env_keys_with_prefix(std::env::vars_os(), b"DYLD_");
for key in dyld_keys {
unsafe {
std::env::remove_var(key);
}
}
}
#[cfg(unix)]
fn set_core_file_size_limit_to_zero() {
let rlim = libc::rlimit {
rlim_cur: 0,
rlim_max: 0,
};
let ret_code = unsafe { libc::setrlimit(libc::RLIMIT_CORE, &rlim) };
if ret_code != 0 {
eprintln!(
"ERROR: setrlimit(RLIMIT_CORE) failed: {}",
std::io::Error::last_os_error()
);
std::process::exit(SET_RLIMIT_CORE_FAILED_EXIT_CODE);
}
}
#[cfg(windows)]
pub(crate) fn pre_main_hardening_windows() {
}
#[cfg(unix)]
fn env_keys_with_prefix<I>(vars: I, prefix: &[u8]) -> Vec<OsString>
where
I: IntoIterator<Item = (OsString, OsString)>,
{
vars.into_iter()
.filter_map(|(key, _)| {
key.as_os_str()
.as_bytes()
.starts_with(prefix)
.then_some(key)
})
.collect()
}
#[cfg(all(test, unix))]
mod tests {
use super::*;
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
#[test]
fn test_env_keys_with_prefix_filters_only_matching_keys() {
let ld_test_var = OsStr::from_bytes(b"LD_TEST");
let vars = vec![
(OsString::from("PATH"), OsString::from("/usr/bin")),
(ld_test_var.to_os_string(), OsString::from("1")),
(OsString::from("DYLD_FOO"), OsString::from("bar")),
];
let keys = env_keys_with_prefix(vars, b"LD_");
assert_eq!(keys.len(), 1);
assert_eq!(keys[0].as_os_str(), ld_test_var);
}
}