pub mod errors;
pub mod runtime;
pub mod securebits;
mod ambient;
mod base;
mod bounding;
mod nr;
use nix::errno::Errno;
use crate::caps::errors::CapsError;
#[derive(Debug, Clone, Copy)]
pub enum CapSet {
Ambient,
Bounding,
Effective,
Inheritable,
Permitted,
}
#[cfg(feature = "oci")]
use oci_spec::runtime::Capability as SpecCapability;
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
pub struct Capability(u8);
impl Capability {
pub const CAP_CHOWN: Self = Self(nr::CAP_CHOWN);
pub const CAP_DAC_OVERRIDE: Self = Self(nr::CAP_DAC_OVERRIDE);
pub const CAP_DAC_READ_SEARCH: Self = Self(nr::CAP_DAC_READ_SEARCH);
pub const CAP_FOWNER: Self = Self(nr::CAP_FOWNER);
pub const CAP_FSETID: Self = Self(nr::CAP_FSETID);
pub const CAP_KILL: Self = Self(nr::CAP_KILL);
pub const CAP_SETGID: Self = Self(nr::CAP_SETGID);
pub const CAP_SETUID: Self = Self(nr::CAP_SETUID);
pub const CAP_SETPCAP: Self = Self(nr::CAP_SETPCAP);
pub const CAP_LINUX_IMMUTABLE: Self = Self(nr::CAP_LINUX_IMMUTABLE);
pub const CAP_NET_BIND_SERVICE: Self = Self(nr::CAP_NET_BIND_SERVICE);
pub const CAP_NET_BROADCAST: Self = Self(nr::CAP_NET_BROADCAST);
pub const CAP_NET_ADMIN: Self = Self(nr::CAP_NET_ADMIN);
pub const CAP_NET_RAW: Self = Self(nr::CAP_NET_RAW);
pub const CAP_IPC_LOCK: Self = Self(nr::CAP_IPC_LOCK);
pub const CAP_IPC_OWNER: Self = Self(nr::CAP_IPC_OWNER);
pub const CAP_SYS_MODULE: Self = Self(nr::CAP_SYS_MODULE);
pub const CAP_SYS_RAWIO: Self = Self(nr::CAP_SYS_RAWIO);
pub const CAP_SYS_CHROOT: Self = Self(nr::CAP_SYS_CHROOT);
pub const CAP_SYS_PTRACE: Self = Self(nr::CAP_SYS_PTRACE);
pub const CAP_SYS_PACCT: Self = Self(nr::CAP_SYS_PACCT);
pub const CAP_SYS_ADMIN: Self = Self(nr::CAP_SYS_ADMIN);
pub const CAP_SYS_BOOT: Self = Self(nr::CAP_SYS_BOOT);
pub const CAP_SYS_NICE: Self = Self(nr::CAP_SYS_NICE);
pub const CAP_SYS_RESOURCE: Self = Self(nr::CAP_SYS_RESOURCE);
pub const CAP_SYS_TIME: Self = Self(nr::CAP_SYS_TIME);
pub const CAP_SYS_TTY_CONFIG: Self = Self(nr::CAP_SYS_TTY_CONFIG);
pub const CAP_MKNOD: Self = Self(nr::CAP_MKNOD);
pub const CAP_LEASE: Self = Self(nr::CAP_LEASE);
pub const CAP_AUDIT_WRITE: Self = Self(nr::CAP_AUDIT_WRITE);
pub const CAP_AUDIT_CONTROL: Self = Self(nr::CAP_AUDIT_CONTROL);
pub const CAP_SETFCAP: Self = Self(nr::CAP_SETFCAP);
pub const CAP_MAC_OVERRIDE: Self = Self(nr::CAP_MAC_OVERRIDE);
pub const CAP_MAC_ADMIN: Self = Self(nr::CAP_MAC_ADMIN);
pub const CAP_SYSLOG: Self = Self(nr::CAP_SYSLOG);
pub const CAP_WAKE_ALARM: Self = Self(nr::CAP_WAKE_ALARM);
pub const CAP_BLOCK_SUSPEND: Self = Self(nr::CAP_BLOCK_SUSPEND);
pub const CAP_AUDIT_READ: Self = Self(nr::CAP_AUDIT_READ);
pub const CAP_PERFMON: Self = Self(nr::CAP_PERFMON);
pub const CAP_BPF: Self = Self(nr::CAP_BPF);
pub const CAP_CHECKPOINT_RESTORE: Self = Self(nr::CAP_CHECKPOINT_RESTORE);
pub const fn from_index(index: u8) -> Self {
Self(index)
}
}
impl std::fmt::Display for Capability {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let name = match *self {
Capability::CAP_CHOWN => "cap_chown",
Capability::CAP_DAC_OVERRIDE => "cap_dac_override",
Capability::CAP_DAC_READ_SEARCH => "cap_dac_read_search",
Capability::CAP_FOWNER => "cap_fowner",
Capability::CAP_FSETID => "cap_fsetid",
Capability::CAP_KILL => "cap_kill",
Capability::CAP_SETGID => "cap_setgid",
Capability::CAP_SETUID => "cap_setuid",
Capability::CAP_SETPCAP => "cap_setpcap",
Capability::CAP_LINUX_IMMUTABLE => "cap_linux_immutable",
Capability::CAP_NET_BIND_SERVICE => "cap_net_bind_service",
Capability::CAP_NET_BROADCAST => "cap_net_broadcast",
Capability::CAP_NET_ADMIN => "cap_net_admin",
Capability::CAP_NET_RAW => "cap_net_raw",
Capability::CAP_IPC_LOCK => "cap_ipc_lock",
Capability::CAP_IPC_OWNER => "cap_ipc_owner",
Capability::CAP_SYS_MODULE => "cap_sys_module",
Capability::CAP_SYS_RAWIO => "cap_sys_rawio",
Capability::CAP_SYS_CHROOT => "cap_sys_chroot",
Capability::CAP_SYS_PTRACE => "cap_sys_ptrace",
Capability::CAP_SYS_PACCT => "cap_sys_pacct",
Capability::CAP_SYS_ADMIN => "cap_sys_admin",
Capability::CAP_SYS_BOOT => "cap_sys_boot",
Capability::CAP_SYS_NICE => "cap_sys_nice",
Capability::CAP_SYS_RESOURCE => "cap_sys_resource",
Capability::CAP_SYS_TIME => "cap_sys_time",
Capability::CAP_SYS_TTY_CONFIG => "cap_sys_tty_config",
Capability::CAP_MKNOD => "cap_mknod",
Capability::CAP_LEASE => "cap_lease",
Capability::CAP_AUDIT_WRITE => "cap_audit_write",
Capability::CAP_AUDIT_CONTROL => "cap_audit_control",
Capability::CAP_SETFCAP => "cap_setfcap",
Capability::CAP_MAC_OVERRIDE => "cap_mac_override",
Capability::CAP_MAC_ADMIN => "cap_mac_admin",
Capability::CAP_SYSLOG => "cap_syslog",
Capability::CAP_WAKE_ALARM => "cap_wake_alarm",
Capability::CAP_BLOCK_SUSPEND => "cap_block_suspend",
Capability::CAP_AUDIT_READ => "cap_audit_read",
Capability::CAP_PERFMON => "cap_perfmon",
Capability::CAP_BPF => "cap_bpf",
Capability::CAP_CHECKPOINT_RESTORE => "cap_checkpoint_restore",
_ => return write!(f, "cap_{}", self.0),
};
write!(f, "{name}")
}
}
impl std::str::FromStr for Capability {
type Err = CapsError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"cap_chown" => Ok(Capability::CAP_CHOWN),
"cap_dac_override" => Ok(Capability::CAP_DAC_OVERRIDE),
"cap_dac_read_search" => Ok(Capability::CAP_DAC_READ_SEARCH),
"cap_fowner" => Ok(Capability::CAP_FOWNER),
"cap_fsetid" => Ok(Capability::CAP_FSETID),
"cap_kill" => Ok(Capability::CAP_KILL),
"cap_setgid" => Ok(Capability::CAP_SETGID),
"cap_setuid" => Ok(Capability::CAP_SETUID),
"cap_setpcap" => Ok(Capability::CAP_SETPCAP),
"cap_linux_immutable" => Ok(Capability::CAP_LINUX_IMMUTABLE),
"cap_net_bind_service" => Ok(Capability::CAP_NET_BIND_SERVICE),
"cap_net_broadcast" => Ok(Capability::CAP_NET_BROADCAST),
"cap_net_admin" => Ok(Capability::CAP_NET_ADMIN),
"cap_net_raw" => Ok(Capability::CAP_NET_RAW),
"cap_ipc_lock" => Ok(Capability::CAP_IPC_LOCK),
"cap_ipc_owner" => Ok(Capability::CAP_IPC_OWNER),
"cap_sys_module" => Ok(Capability::CAP_SYS_MODULE),
"cap_sys_rawio" => Ok(Capability::CAP_SYS_RAWIO),
"cap_sys_chroot" => Ok(Capability::CAP_SYS_CHROOT),
"cap_sys_ptrace" => Ok(Capability::CAP_SYS_PTRACE),
"cap_sys_pacct" => Ok(Capability::CAP_SYS_PACCT),
"cap_sys_admin" => Ok(Capability::CAP_SYS_ADMIN),
"cap_sys_boot" => Ok(Capability::CAP_SYS_BOOT),
"cap_sys_nice" => Ok(Capability::CAP_SYS_NICE),
"cap_sys_resource" => Ok(Capability::CAP_SYS_RESOURCE),
"cap_sys_time" => Ok(Capability::CAP_SYS_TIME),
"cap_sys_tty_config" => Ok(Capability::CAP_SYS_TTY_CONFIG),
"cap_mknod" => Ok(Capability::CAP_MKNOD),
"cap_lease" => Ok(Capability::CAP_LEASE),
"cap_audit_write" => Ok(Capability::CAP_AUDIT_WRITE),
"cap_audit_control" => Ok(Capability::CAP_AUDIT_CONTROL),
"cap_setfcap" => Ok(Capability::CAP_SETFCAP),
"cap_mac_override" => Ok(Capability::CAP_MAC_OVERRIDE),
"cap_mac_admin" => Ok(Capability::CAP_MAC_ADMIN),
"cap_syslog" => Ok(Capability::CAP_SYSLOG),
"cap_wake_alarm" => Ok(Capability::CAP_WAKE_ALARM),
"cap_block_suspend" => Ok(Capability::CAP_BLOCK_SUSPEND),
"cap_audit_read" => Ok(Capability::CAP_AUDIT_READ),
"cap_perfmon" => Ok(Capability::CAP_PERFMON),
"cap_bpf" => Ok(Capability::CAP_BPF),
"cap_checkpoint_restore" => Ok(Capability::CAP_CHECKPOINT_RESTORE),
_ => Err(CapsError(Errno::EINVAL)),
}
}
}
#[cfg(feature = "oci")]
impl Capability {
pub fn spec(&self) -> SpecCapability {
match *self {
Self::CAP_CHOWN => SpecCapability::Chown,
Self::CAP_DAC_OVERRIDE => SpecCapability::DacOverride,
Self::CAP_DAC_READ_SEARCH => SpecCapability::DacReadSearch,
Self::CAP_FOWNER => SpecCapability::Fowner,
Self::CAP_FSETID => SpecCapability::Fsetid,
Self::CAP_KILL => SpecCapability::Kill,
Self::CAP_SETGID => SpecCapability::Setgid,
Self::CAP_SETUID => SpecCapability::Setuid,
Self::CAP_SETPCAP => SpecCapability::Setpcap,
Self::CAP_LINUX_IMMUTABLE => SpecCapability::LinuxImmutable,
Self::CAP_NET_BIND_SERVICE => SpecCapability::NetBindService,
Self::CAP_NET_BROADCAST => SpecCapability::NetBroadcast,
Self::CAP_NET_ADMIN => SpecCapability::NetAdmin,
Self::CAP_NET_RAW => SpecCapability::NetRaw,
Self::CAP_IPC_LOCK => SpecCapability::IpcLock,
Self::CAP_IPC_OWNER => SpecCapability::IpcOwner,
Self::CAP_SYS_MODULE => SpecCapability::SysModule,
Self::CAP_SYS_RAWIO => SpecCapability::SysRawio,
Self::CAP_SYS_CHROOT => SpecCapability::SysChroot,
Self::CAP_SYS_PTRACE => SpecCapability::SysPtrace,
Self::CAP_SYS_PACCT => SpecCapability::SysPacct,
Self::CAP_SYS_ADMIN => SpecCapability::SysAdmin,
Self::CAP_SYS_BOOT => SpecCapability::SysBoot,
Self::CAP_SYS_NICE => SpecCapability::SysNice,
Self::CAP_SYS_RESOURCE => SpecCapability::SysResource,
Self::CAP_SYS_TIME => SpecCapability::SysTime,
Self::CAP_SYS_TTY_CONFIG => SpecCapability::SysTtyConfig,
Self::CAP_MKNOD => SpecCapability::Mknod,
Self::CAP_LEASE => SpecCapability::Lease,
Self::CAP_AUDIT_WRITE => SpecCapability::AuditWrite,
Self::CAP_AUDIT_CONTROL => SpecCapability::AuditControl,
Self::CAP_SETFCAP => SpecCapability::Setfcap,
Self::CAP_MAC_OVERRIDE => SpecCapability::MacOverride,
Self::CAP_MAC_ADMIN => SpecCapability::MacAdmin,
Self::CAP_SYSLOG => SpecCapability::Syslog,
Self::CAP_WAKE_ALARM => SpecCapability::WakeAlarm,
Self::CAP_BLOCK_SUSPEND => SpecCapability::BlockSuspend,
Self::CAP_AUDIT_READ => SpecCapability::AuditRead,
Self::CAP_PERFMON => SpecCapability::Perfmon,
Self::CAP_BPF => SpecCapability::Bpf,
Self::CAP_CHECKPOINT_RESTORE => SpecCapability::CheckpointRestore,
_ => unreachable!("BUG: Undefined capability CAP_{}, report a bug!", self.0),
}
}
}
impl TryFrom<Capabilities> for Capability {
type Error = CapsError;
fn try_from(caps: Capabilities) -> Result<Self, Self::Error> {
let bits = caps.bits();
if bits == 0 || (bits & (bits - 1)) != 0 {
return Err(CapsError(Errno::EINVAL));
}
let idx = u8::try_from(bits.trailing_zeros()).or(Err(CapsError(Errno::EINVAL)))?;
Ok(Capability(idx))
}
}
impl From<Capability> for Capabilities {
#[inline]
fn from(c: Capability) -> Self {
Capabilities::from_bits_retain(c.bitmask())
}
}
impl Capability {
pub const fn bitmask(self) -> u64 {
1u64 << self.0
}
pub const fn index(self) -> u8 {
self.0
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
pub struct Capabilities {
val: u64,
}
impl Capabilities {
pub const CAP_CHOWN: Self = Self {
val: Capability::CAP_CHOWN.bitmask(),
};
pub const CAP_DAC_OVERRIDE: Self = Self {
val: Capability::CAP_DAC_OVERRIDE.bitmask(),
};
pub const CAP_DAC_READ_SEARCH: Self = Self {
val: Capability::CAP_DAC_READ_SEARCH.bitmask(),
};
pub const CAP_FOWNER: Self = Self {
val: Capability::CAP_FOWNER.bitmask(),
};
pub const CAP_FSETID: Self = Self {
val: Capability::CAP_FSETID.bitmask(),
};
pub const CAP_KILL: Self = Self {
val: Capability::CAP_KILL.bitmask(),
};
pub const CAP_SETGID: Self = Self {
val: Capability::CAP_SETGID.bitmask(),
};
pub const CAP_SETUID: Self = Self {
val: Capability::CAP_SETUID.bitmask(),
};
pub const CAP_SETPCAP: Self = Self {
val: Capability::CAP_SETPCAP.bitmask(),
};
pub const CAP_LINUX_IMMUTABLE: Self = Self {
val: Capability::CAP_LINUX_IMMUTABLE.bitmask(),
};
pub const CAP_NET_BIND_SERVICE: Self = Self {
val: Capability::CAP_NET_BIND_SERVICE.bitmask(),
};
pub const CAP_NET_BROADCAST: Self = Self {
val: Capability::CAP_NET_BROADCAST.bitmask(),
};
pub const CAP_NET_ADMIN: Self = Self {
val: Capability::CAP_NET_ADMIN.bitmask(),
};
pub const CAP_NET_RAW: Self = Self {
val: Capability::CAP_NET_RAW.bitmask(),
};
pub const CAP_IPC_LOCK: Self = Self {
val: Capability::CAP_IPC_LOCK.bitmask(),
};
pub const CAP_IPC_OWNER: Self = Self {
val: Capability::CAP_IPC_OWNER.bitmask(),
};
pub const CAP_SYS_MODULE: Self = Self {
val: Capability::CAP_SYS_MODULE.bitmask(),
};
pub const CAP_SYS_RAWIO: Self = Self {
val: Capability::CAP_SYS_RAWIO.bitmask(),
};
pub const CAP_SYS_CHROOT: Self = Self {
val: Capability::CAP_SYS_CHROOT.bitmask(),
};
pub const CAP_SYS_PTRACE: Self = Self {
val: Capability::CAP_SYS_PTRACE.bitmask(),
};
pub const CAP_SYS_PACCT: Self = Self {
val: Capability::CAP_SYS_PACCT.bitmask(),
};
pub const CAP_SYS_ADMIN: Self = Self {
val: Capability::CAP_SYS_ADMIN.bitmask(),
};
pub const CAP_SYS_BOOT: Self = Self {
val: Capability::CAP_SYS_BOOT.bitmask(),
};
pub const CAP_SYS_NICE: Self = Self {
val: Capability::CAP_SYS_NICE.bitmask(),
};
pub const CAP_SYS_RESOURCE: Self = Self {
val: Capability::CAP_SYS_RESOURCE.bitmask(),
};
pub const CAP_SYS_TIME: Self = Self {
val: Capability::CAP_SYS_TIME.bitmask(),
};
pub const CAP_SYS_TTY_CONFIG: Self = Self {
val: Capability::CAP_SYS_TTY_CONFIG.bitmask(),
};
pub const CAP_MKNOD: Self = Self {
val: Capability::CAP_MKNOD.bitmask(),
};
pub const CAP_LEASE: Self = Self {
val: Capability::CAP_LEASE.bitmask(),
};
pub const CAP_AUDIT_WRITE: Self = Self {
val: Capability::CAP_AUDIT_WRITE.bitmask(),
};
pub const CAP_AUDIT_CONTROL: Self = Self {
val: Capability::CAP_AUDIT_CONTROL.bitmask(),
};
pub const CAP_SETFCAP: Self = Self {
val: Capability::CAP_SETFCAP.bitmask(),
};
pub const CAP_MAC_OVERRIDE: Self = Self {
val: Capability::CAP_MAC_OVERRIDE.bitmask(),
};
pub const CAP_MAC_ADMIN: Self = Self {
val: Capability::CAP_MAC_ADMIN.bitmask(),
};
pub const CAP_SYSLOG: Self = Self {
val: Capability::CAP_SYSLOG.bitmask(),
};
pub const CAP_WAKE_ALARM: Self = Self {
val: Capability::CAP_WAKE_ALARM.bitmask(),
};
pub const CAP_BLOCK_SUSPEND: Self = Self {
val: Capability::CAP_BLOCK_SUSPEND.bitmask(),
};
pub const CAP_AUDIT_READ: Self = Self {
val: Capability::CAP_AUDIT_READ.bitmask(),
};
pub const CAP_PERFMON: Self = Self {
val: Capability::CAP_PERFMON.bitmask(),
};
pub const CAP_BPF: Self = Self {
val: Capability::CAP_BPF.bitmask(),
};
pub const CAP_CHECKPOINT_RESTORE: Self = Self {
val: Capability::CAP_CHECKPOINT_RESTORE.bitmask(),
};
}
impl Capabilities {
pub const fn empty() -> Self {
Self { val: 0 }
}
pub fn all() -> Result<Self, CapsError> {
runtime::procfs_all_supported(None).or_else(|_| runtime::thread_all_supported())
}
pub const fn bits(self) -> u64 {
self.val
}
pub const fn from_bits_retain(bits: u64) -> Self {
Self { val: bits }
}
pub const fn is_empty(self) -> bool {
self.val == 0
}
pub const fn contains(self, other: Self) -> bool {
self.val & other.val == other.val
}
pub const fn intersects(self, other: Self) -> bool {
self.val & other.val != 0
}
pub fn insert(&mut self, other: Self) {
self.val |= other.val;
}
pub fn remove(&mut self, other: Self) {
self.val &= !other.val;
}
pub fn iter(self) -> CapabilitiesIter {
CapabilitiesIter { val: self.val }
}
}
pub struct CapabilitiesIter {
val: u64,
}
impl Iterator for CapabilitiesIter {
type Item = Capabilities;
fn next(&mut self) -> Option<Capabilities> {
if self.val == 0 {
return None;
}
let bit = self.val & self.val.wrapping_neg();
self.val &= !bit;
Some(Capabilities { val: bit })
}
}
impl IntoIterator for Capabilities {
type Item = Capabilities;
type IntoIter = CapabilitiesIter;
fn into_iter(self) -> CapabilitiesIter {
self.iter()
}
}
impl std::ops::BitAnd for Capabilities {
type Output = Self;
fn bitand(self, rhs: Self) -> Self {
Self {
val: self.val & rhs.val,
}
}
}
impl std::ops::BitOr for Capabilities {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self {
val: self.val | rhs.val,
}
}
}
impl std::ops::BitXor for Capabilities {
type Output = Self;
fn bitxor(self, rhs: Self) -> Self {
Self {
val: self.val ^ rhs.val,
}
}
}
impl std::ops::Not for Capabilities {
type Output = Self;
fn not(self) -> Self {
Self { val: !self.val }
}
}
impl std::ops::BitOrAssign for Capabilities {
fn bitor_assign(&mut self, rhs: Self) {
self.val |= rhs.val;
}
}
impl std::ops::BitAndAssign for Capabilities {
fn bitand_assign(&mut self, rhs: Self) {
self.val &= rhs.val;
}
}
pub fn has_cap(tid: Option<i32>, cset: CapSet, cap: Capability) -> Result<bool, CapsError> {
let t = tid.unwrap_or(0);
match cset {
CapSet::Ambient if t == 0 => ambient::has_cap(cap),
CapSet::Bounding if t == 0 => bounding::has_cap(cap),
CapSet::Effective | CapSet::Inheritable | CapSet::Permitted => base::has_cap(t, cset, cap),
_ => Err(CapsError(Errno::EOPNOTSUPP)),
}
}
pub fn read(tid: Option<i32>, cset: CapSet) -> Result<Capabilities, CapsError> {
let t = tid.unwrap_or(0);
match cset {
CapSet::Ambient if t == 0 => ambient::read(),
CapSet::Bounding if t == 0 => bounding::read(),
CapSet::Effective | CapSet::Inheritable | CapSet::Permitted => base::read(t, cset),
_ => Err(CapsError(Errno::EOPNOTSUPP)),
}
}
pub fn set(tid: Option<i32>, cset: CapSet, value: Capabilities) -> Result<(), CapsError> {
let t = tid.unwrap_or(0);
match cset {
CapSet::Ambient if t == 0 => ambient::set(value),
CapSet::Effective | CapSet::Inheritable | CapSet::Permitted => base::set(t, cset, value),
_ => Err(CapsError(Errno::EOPNOTSUPP)),
}
}
pub fn set_all(
tid: Option<i32>,
eff: Capabilities,
perm: Capabilities,
inh: Capabilities,
) -> Result<(), CapsError> {
base::set_epi(tid.unwrap_or(0), eff, perm, inh)
}
pub fn clear(tid: Option<i32>, cset: CapSet) -> Result<(), CapsError> {
let t = tid.unwrap_or(0);
match cset {
CapSet::Ambient if t == 0 => ambient::clear(),
CapSet::Bounding if t == 0 => bounding::clear(),
CapSet::Effective | CapSet::Permitted | CapSet::Inheritable => base::clear(t, cset),
_ => Err(CapsError(Errno::EOPNOTSUPP)),
}
}
pub fn raise(tid: Option<i32>, cset: CapSet, cap: Capability) -> Result<(), CapsError> {
let t = tid.unwrap_or(0);
match cset {
CapSet::Ambient if t == 0 => ambient::raise(cap),
CapSet::Effective | CapSet::Permitted | CapSet::Inheritable => base::raise(t, cset, cap),
_ => Err(CapsError(Errno::EOPNOTSUPP)),
}
}
pub fn drop(tid: Option<i32>, cset: CapSet, cap: Capability) -> Result<(), CapsError> {
let t = tid.unwrap_or(0);
match cset {
CapSet::Ambient if t == 0 => ambient::drop(cap),
CapSet::Bounding if t == 0 => bounding::drop(cap),
CapSet::Effective | CapSet::Permitted | CapSet::Inheritable => base::drop(t, cset, cap),
_ => Err(CapsError(Errno::EOPNOTSUPP)),
}
}
pub fn to_canonical(name: &str) -> String {
let uppername = name.to_ascii_uppercase();
if uppername.starts_with("CAP_") {
uppername
} else {
["CAP_", &uppername].concat()
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use super::*;
#[test]
fn test_caps_1() {
let last = Capability::CAP_CHECKPOINT_RESTORE.index();
assert!(last > 0);
for idx in 0..=last {
let cap = Capability::from_index(idx);
let name = cap.to_string();
let parsed: Capability = name.parse().unwrap();
assert_eq!(cap, parsed);
}
}
#[test]
fn test_caps_2() {
let mut caps = Capabilities::empty();
assert!(caps.is_empty());
assert!(!caps.contains(Capabilities::CAP_CHOWN));
caps.insert(Capabilities::CAP_CHOWN);
caps.insert(Capabilities::CAP_NET_RAW);
assert!(!caps.is_empty());
assert!(caps.contains(Capabilities::CAP_CHOWN));
assert!(caps.contains(Capabilities::CAP_CHOWN | Capabilities::CAP_NET_RAW));
assert!(!caps.contains(Capabilities::CAP_SYS_ADMIN));
assert!(caps.intersects(Capabilities::CAP_CHOWN | Capabilities::CAP_SYS_ADMIN));
caps.remove(Capabilities::CAP_CHOWN);
assert!(!caps.contains(Capabilities::CAP_CHOWN));
assert!(caps.contains(Capabilities::CAP_NET_RAW));
}
#[test]
fn test_caps_3() {
let a = Capabilities::CAP_CHOWN | Capabilities::CAP_KILL;
let b = Capabilities::CAP_KILL | Capabilities::CAP_SYS_ADMIN;
assert_eq!((a & b).bits(), Capabilities::CAP_KILL.bits());
assert_eq!((a ^ b).bits(), a.bits() ^ b.bits());
assert_eq!((a & !b).bits(), Capabilities::CAP_CHOWN.bits());
assert_eq!(Capabilities::from_bits_retain(a.bits()), a);
let mut got: Vec<u8> = a
.iter()
.filter_map(|c| Capability::try_from(c).ok())
.map(|c| c.index())
.collect();
got.sort_unstable();
assert_eq!(
got,
vec![Capability::CAP_CHOWN.index(), Capability::CAP_KILL.index()]
);
assert_eq!(Capabilities::empty().iter().count(), 0);
}
#[test]
fn test_caps_4() {
let p1 = Capability::from_str("CAP_FOO");
let p1_err = p1.unwrap_err();
assert_eq!(p1_err.0, Errno::EINVAL);
let p2: Result<Capability, CapsError> = "CAP_BAR".parse();
assert!(p2.is_err());
}
#[test]
fn test_caps_5() {
let p1 = "foo";
assert!(Capability::from_str(&to_canonical(p1)).is_err());
let p2 = "sys_admin";
assert!(Capability::from_str(&to_canonical(p2)).is_ok());
let p3 = "CAP_SYS_CHROOT";
assert!(Capability::from_str(&to_canonical(p3)).is_ok());
}
#[test]
fn test_caps_6() {
assert_eq!(to_canonical("net_admin"), "CAP_NET_ADMIN");
}
#[test]
fn test_caps_7() {
assert_eq!(to_canonical("CAP_NET_ADMIN"), "CAP_NET_ADMIN");
}
#[test]
fn test_caps_8() {
assert_eq!(to_canonical("cap_sys_chroot"), "CAP_SYS_CHROOT");
}
#[test]
fn test_caps_9() {
assert_eq!(
Capability::from_str("CAP_CHOWN").unwrap(),
Capability::CAP_CHOWN
);
}
#[test]
fn test_caps_10() {
assert_eq!(
Capability::from_str("CAP_NET_RAW").unwrap(),
Capability::CAP_NET_RAW
);
}
#[test]
fn test_caps_11() {
assert!(Capability::from_str("INVALID").is_err());
}
#[test]
fn test_caps_12() {
assert_eq!(Capability::CAP_CHOWN.to_string(), "cap_chown");
}
#[test]
fn test_caps_13() {
assert_eq!(Capability::CAP_NET_RAW.to_string(), "cap_net_raw");
}
#[test]
fn test_caps_14() {
assert_eq!(Capability::CAP_SYS_ADMIN.to_string(), "cap_sys_admin");
}
#[test]
fn test_caps_15() {
assert_eq!(Capability::CAP_CHOWN.bitmask(), 1u64 << 0);
}
#[test]
fn test_caps_16() {
assert_eq!(Capability::CAP_DAC_OVERRIDE.bitmask(), 1u64 << 1);
}
#[test]
fn test_caps_17() {
assert_eq!(Capability::CAP_KILL.bitmask(), 1u64 << 5);
}
#[test]
fn test_caps_18() {
assert_eq!(Capability::CAP_CHOWN.index(), 0u8);
}
#[test]
fn test_caps_19() {
assert_eq!(Capability::CAP_NET_RAW.index(), 13u8);
}
#[test]
fn test_caps_20() {
assert_eq!(Capability::CAP_SYS_ADMIN.index(), 21u8);
}
#[test]
fn test_caps_21() {
let caps = Capabilities::CAP_CHOWN;
let cap: Capability = caps.try_into().unwrap();
assert_eq!(cap, Capability::CAP_CHOWN);
}
#[test]
fn test_caps_22() {
let caps = Capabilities::empty();
let result: Result<Capability, _> = caps.try_into();
assert!(result.is_err());
}
#[test]
fn test_caps_23() {
let caps = Capabilities::CAP_CHOWN | Capabilities::CAP_DAC_OVERRIDE;
let result: Result<Capability, _> = caps.try_into();
assert!(result.is_err());
}
#[test]
fn test_caps_24() {
let caps: Capabilities = Capability::CAP_SYS_ADMIN.into();
assert!(caps.contains(Capabilities::CAP_SYS_ADMIN));
}
}