use crate::error::{NucleusError, Result};
use caps::{CapSet, Capability, CapsHashSet};
use tracing::{debug, info};
pub struct CapabilityManager {
dropped: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CapabilitySets {
pub bounding: Vec<Capability>,
pub permitted: Vec<Capability>,
pub effective: Vec<Capability>,
pub inheritable: Vec<Capability>,
pub ambient: Vec<Capability>,
}
impl CapabilityManager {
pub fn new() -> Self {
Self { dropped: false }
}
pub fn drop_all(&mut self) -> Result<()> {
if self.dropped {
debug!("Capabilities already dropped, skipping");
return Ok(());
}
info!("Dropping all capabilities");
caps::clear(None, CapSet::Permitted).map_err(|e| {
NucleusError::CapabilityError(format!("Failed to clear permitted caps: {}", e))
})?;
caps::clear(None, CapSet::Effective).map_err(|e| {
NucleusError::CapabilityError(format!("Failed to clear effective caps: {}", e))
})?;
caps::clear(None, CapSet::Inheritable).map_err(|e| {
NucleusError::CapabilityError(format!("Failed to clear inheritable caps: {}", e))
})?;
caps::clear(None, CapSet::Ambient).map_err(|e| {
NucleusError::CapabilityError(format!("Failed to clear ambient caps: {}", e))
})?;
for cap in caps::all() {
if let Err(e) = caps::drop(None, CapSet::Bounding, cap) {
debug!(
"Failed to drop bounding cap {:?}: {} (may not be present)",
cap, e
);
}
}
self.dropped = true;
info!("Successfully dropped all capabilities (including bounding set)");
Ok(())
}
pub fn drop_except(&mut self, keep: &[Capability]) -> Result<()> {
if self.dropped {
debug!("Capabilities already dropped, skipping");
return Ok(());
}
info!("Dropping capabilities except: {:?}", keep);
let all_caps = caps::all();
for cap in all_caps {
if !keep.contains(&cap) {
caps::drop(None, CapSet::Permitted, cap).map_err(|e| {
NucleusError::CapabilityError(format!("Failed to drop {cap:?}: {e}"))
})?;
caps::drop(None, CapSet::Effective, cap).map_err(|e| {
NucleusError::CapabilityError(format!("Failed to drop {cap:?}: {e}"))
})?;
caps::drop(None, CapSet::Inheritable, cap).map_err(|e| {
NucleusError::CapabilityError(format!("Failed to drop {cap:?}: {e}"))
})?;
if let Err(e) = caps::drop(None, CapSet::Bounding, cap) {
debug!(
"Failed to drop bounding cap {:?}: {} (may not be present)",
cap, e
);
}
}
}
caps::clear(None, CapSet::Ambient).map_err(|e| {
NucleusError::CapabilityError(format!("Failed to clear ambient caps: {}", e))
})?;
self.dropped = true;
info!("Successfully dropped capabilities");
Ok(())
}
pub fn apply_sets(&mut self, sets: &CapabilitySets) -> Result<()> {
if self.dropped {
debug!("Capabilities already dropped, skipping");
return Ok(());
}
info!("Applying explicit capability sets");
for cap in caps::all() {
if !sets.bounding.contains(&cap) {
if let Err(e) = caps::drop(None, CapSet::Bounding, cap) {
debug!(
"Failed to drop bounding cap {:?}: {} (may not be present)",
cap, e
);
}
}
}
caps::set(None, CapSet::Permitted, &to_caps_hash_set(&sets.permitted)).map_err(|e| {
NucleusError::CapabilityError(format!("Failed to set permitted caps: {}", e))
})?;
caps::set(
None,
CapSet::Inheritable,
&to_caps_hash_set(&sets.inheritable),
)
.map_err(|e| {
NucleusError::CapabilityError(format!("Failed to set inheritable caps: {}", e))
})?;
caps::set(None, CapSet::Ambient, &to_caps_hash_set(&sets.ambient)).map_err(|e| {
NucleusError::CapabilityError(format!("Failed to set ambient caps: {}", e))
})?;
caps::set(None, CapSet::Effective, &to_caps_hash_set(&sets.effective)).map_err(|e| {
NucleusError::CapabilityError(format!("Failed to set effective caps: {}", e))
})?;
self.dropped = true;
info!("Successfully applied capability sets");
Ok(())
}
pub fn is_dropped(&self) -> bool {
self.dropped
}
}
impl Default for CapabilityManager {
fn default() -> Self {
Self::new()
}
}
fn to_caps_hash_set(caps_list: &[Capability]) -> CapsHashSet {
caps_list.iter().copied().collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_capability_manager_initial_state() {
let mgr = CapabilityManager::new();
assert!(!mgr.is_dropped());
}
#[test]
fn test_drop_idempotent() {
let mut mgr = CapabilityManager::new();
let _ = mgr.drop_all();
assert!(mgr.is_dropped());
let result = mgr.drop_all();
assert!(result.is_ok());
assert!(mgr.is_dropped());
}
}