diff --git a/src/config.rs b/src/config.rs index 8178ecf..8026838 100644 --- a/src/config.rs +++ b/src/config.rs @@ -70,6 +70,10 @@ pub struct ExecutableSpec { /// An optional set of process-specific resource limits. /// If this set is not provided, setrlimit(2) will not be called. pub process_limits: Option, + + /// If `true`, sets `PR_SET_NO_NEW_PRIVS` before + /// spawning the target executable. + pub no_new_privs: bool, } #[derive(Default, Debug, Serialize, Deserialize)] diff --git a/src/runner.rs b/src/runner.rs index d3614d5..61c46e6 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -79,6 +79,11 @@ impl AttachRequestBuilder { self } + pub fn set_no_new_privs(mut self, no_new_privs: bool) -> AttachRequestBuilder { + self.config.exec.no_new_privs = no_new_privs; + self + } + pub fn push_environment(mut self, key: &str, value: &str) -> AttachRequestBuilder { if self.config.exec.environment.is_none() { self.config.exec.environment = BTreeMap::new().into(); @@ -182,6 +187,11 @@ impl CreateRequestBuilder { self } + pub fn set_no_new_privs(mut self, no_new_privs: bool) -> CreateRequestBuilder { + self.config.exec.no_new_privs = no_new_privs; + self + } + pub fn set_hostname(mut self, hostname: &str) -> CreateRequestBuilder { self.config.hostname = hostname.to_string().into(); self diff --git a/src/wrap.rs b/src/wrap.rs index c9b984c..216519d 100644 --- a/src/wrap.rs +++ b/src/wrap.rs @@ -1,5 +1,3 @@ -use libc; - use std::env; use std::ffi::CString; use std::fs; @@ -22,7 +20,8 @@ use crate::signal; use crate::unshare::{setns, unshare}; use anyhow::{Result, anyhow, bail}; use libc::{ - PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, PR_CAP_AMBIENT_RAISE, PR_CAPBSET_DROP, c_int, prctl, + self, PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, PR_CAP_AMBIENT_RAISE, PR_CAPBSET_DROP, + PR_SET_NO_NEW_PRIVS, c_int, prctl, }; use log::{debug, error, warn}; @@ -569,6 +568,10 @@ impl ExecutableSpec { env::set_current_dir(wd.clone())?; } + if self.no_new_privs { + self.set_no_new_privs()?; + } + unsafe { if libc::execvpe( program_cstring.as_ptr(), @@ -611,6 +614,21 @@ impl ExecutableSpec { Ok(()) } + + // Note that `PR_SET_NO_NEW_PRIVS` is *not* a foolproof privilege escalation + // setting - it just "locks" the privilege set. If the process is granted + // CAP_ADMIN or similar elsewhere, it is trivial to escalate privs in spite of this flag. + fn set_no_new_privs(&self) -> Result<()> { + let error = unsafe { prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) }; + if error != 0 { + bail!( + "failed to set no_new_privs flag: {}", + Error::last_os_error() + ); + } + + Ok(()) + } } impl AttachRequest {