pub(crate) const NO_NEW_PRIVS_ENV: &str = "CODEWHALE_NO_NEW_PRIVS";
fn is_no_new_privs_opt_out(value: &str) -> bool {
matches!(
value.trim().to_ascii_lowercase().as_str(),
"" | "0" | "false" | "no" | "off" | "disabled"
)
}
fn no_new_privs_opted_out() -> bool {
std::env::var_os(NO_NEW_PRIVS_ENV)
.is_some_and(|value| is_no_new_privs_opt_out(&value.to_string_lossy()))
}
pub fn apply_process_hardening() {
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
{
apply_linux_hardening();
}
#[cfg(not(all(target_os = "linux", not(target_env = "ohos"))))]
{
tracing::debug!("Process hardening skipped: not on Linux");
}
}
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
fn apply_linux_hardening() {
let result = unsafe { libc::prctl(libc::PR_SET_DUMPABLE, 0i64, 0i64, 0i64, 0i64) };
if result != 0 {
let err = std::io::Error::last_os_error();
tracing::warn!(
"PR_SET_DUMPABLE failed ({}); continuing without this hardening",
err
);
} else {
tracing::debug!("PR_SET_DUMPABLE=0 applied");
}
if no_new_privs_opted_out() {
tracing::info!(
target: "sandbox",
"PR_SET_NO_NEW_PRIVS skipped via {NO_NEW_PRIVS_ENV}: setuid/sudo escalation is \
allowed for this process tree"
);
} else {
let result = unsafe { libc::prctl(libc::PR_SET_NO_NEW_PRIVS, 1i64, 0i64, 0i64, 0i64) };
if result != 0 {
let err = std::io::Error::last_os_error();
tracing::warn!(
"PR_SET_NO_NEW_PRIVS failed ({}); continuing without this hardening",
err
);
} else {
tracing::debug!("PR_SET_NO_NEW_PRIVS=1 applied");
}
}
let rlim_core = libc::rlimit {
rlim_cur: 0,
rlim_max: 0,
};
let result = unsafe { libc::setrlimit(libc::RLIMIT_CORE, &raw const rlim_core) };
if result != 0 {
let err = std::io::Error::last_os_error();
tracing::warn!(
"RLIMIT_CORE failed ({}); continuing without this hardening",
err
);
} else {
tracing::debug!("RLIMIT_CORE=0 applied");
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_apply_process_hardening_does_not_panic() {
apply_process_hardening();
}
#[test]
fn no_new_privs_opt_out_accepts_exactly_the_falsey_values() {
for value in ["", "0", "false", "no", "off", "disabled"] {
assert!(
is_no_new_privs_opt_out(value),
"{value:?} should opt out of PR_SET_NO_NEW_PRIVS"
);
assert!(is_no_new_privs_opt_out(&format!(
" {} ",
value.to_uppercase()
)));
}
}
#[test]
fn no_new_privs_opt_out_rejects_truthy_and_garbage_values() {
for value in [
"1",
"true",
"yes",
"on",
"enabled",
"maybe",
"0x0",
"false-ish",
"off!",
] {
assert!(
!is_no_new_privs_opt_out(value),
"{value:?} should NOT opt out of PR_SET_NO_NEW_PRIVS"
);
}
}
#[test]
fn no_new_privs_env_wiring_reads_the_documented_variable() {
assert_eq!(NO_NEW_PRIVS_ENV, "CODEWHALE_NO_NEW_PRIVS");
let projected = std::env::var_os(NO_NEW_PRIVS_ENV)
.is_some_and(|value| is_no_new_privs_opt_out(&value.to_string_lossy()));
assert_eq!(projected, no_new_privs_opted_out());
}
}