Verify irreversible capability removal
This commit is contained in:
parent
18afbeaa91
commit
3171c41399
2 changed files with 44 additions and 1 deletions
|
|
@ -58,6 +58,33 @@ pub fn irreversibly_drop(capability: CapabilitySet) -> io::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Errors
|
||||||
|
/// Refuses if an absent capability can be restored or any verification fails.
|
||||||
|
pub fn require_regain_refused(capability: CapabilitySet) -> io::Result<()> {
|
||||||
|
let before = inspect(capability)?;
|
||||||
|
if before.sets.effective.intersects(capability)
|
||||||
|
|| before.sets.permitted.intersects(capability)
|
||||||
|
|| before.sets.inheritable.intersects(capability)
|
||||||
|
|| before.ambient
|
||||||
|
|| before.bounding
|
||||||
|
{
|
||||||
|
return Err(io::Error::other(
|
||||||
|
"capability remains available before regain verification",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let mut attempted = before.sets;
|
||||||
|
attempted.effective.insert(capability);
|
||||||
|
attempted.permitted.insert(capability);
|
||||||
|
attempted.inheritable.insert(capability);
|
||||||
|
if set_capabilities(None, attempted).is_ok()
|
||||||
|
|| configure_capability_in_ambient_set(capability, true).is_ok()
|
||||||
|
|| inspect(capability)? != before
|
||||||
|
{
|
||||||
|
return Err(io::Error::other("capability regain was not refused"));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn require_single_threaded_process() -> io::Result<()> {
|
fn require_single_threaded_process() -> io::Result<()> {
|
||||||
let mut tasks = std::fs::read_dir("/proc/self/task")?;
|
let mut tasks = std::fs::read_dir("/proc/self/task")?;
|
||||||
if tasks.next().transpose()?.is_some() && tasks.next().transpose()?.is_none() {
|
if tasks.next().transpose()?.is_some() && tasks.next().transpose()?.is_none() {
|
||||||
|
|
@ -73,7 +100,7 @@ fn require_single_threaded_process() -> io::Result<()> {
|
||||||
mod tests {
|
mod tests {
|
||||||
use rustix::thread::CapabilitySet;
|
use rustix::thread::CapabilitySet;
|
||||||
|
|
||||||
use super::{inspect, irreversibly_drop};
|
use super::{inspect, irreversibly_drop, require_regain_refused};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn inspects_current_capability_sets() {
|
fn inspects_current_capability_sets() {
|
||||||
|
|
@ -95,4 +122,17 @@ mod tests {
|
||||||
waiting.wait();
|
waiting.wait();
|
||||||
child.join().unwrap();
|
child.join().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn absent_capability_cannot_be_regained() {
|
||||||
|
let state = inspect(CapabilitySet::NET_ADMIN).unwrap();
|
||||||
|
if !state.sets.effective.intersects(CapabilitySet::NET_ADMIN)
|
||||||
|
&& !state.sets.permitted.intersects(CapabilitySet::NET_ADMIN)
|
||||||
|
&& !state.sets.inheritable.intersects(CapabilitySet::NET_ADMIN)
|
||||||
|
&& !state.ambient
|
||||||
|
&& !state.bounding
|
||||||
|
{
|
||||||
|
require_regain_refused(CapabilitySet::NET_ADMIN).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,9 @@ fn main() -> ExitCode {
|
||||||
if lazaret_linux::capability::irreversibly_drop(capability).is_err() {
|
if lazaret_linux::capability::irreversibly_drop(capability).is_err() {
|
||||||
return ExitCode::FAILURE;
|
return ExitCode::FAILURE;
|
||||||
}
|
}
|
||||||
|
if lazaret_linux::capability::require_regain_refused(capability).is_err() {
|
||||||
|
return ExitCode::FAILURE;
|
||||||
|
}
|
||||||
let Ok(observed) = lazaret_linux::capability::inspect(capability) else {
|
let Ok(observed) = lazaret_linux::capability::inspect(capability) else {
|
||||||
return ExitCode::FAILURE;
|
return ExitCode::FAILURE;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue