use nix::errno::Errno;
use crate::caps::{errors::CapsError, nr, CapSet, Capabilities, Capability};
#[expect(clippy::unreadable_literal)]
const CAPS_V3: u32 = 0x20080522;
fn capget(hdr: &mut CapUserHeader, data: &mut CapUserData) -> Result<(), CapsError> {
Errno::result(unsafe { libc::syscall(nr::CAPGET, hdr, data) })
.map(std::mem::drop)
.map_err(CapsError)
}
fn capset(hdr: &mut CapUserHeader, data: &CapUserData) -> Result<(), CapsError> {
Errno::result(unsafe { libc::syscall(nr::CAPSET, hdr, data) })
.map(std::mem::drop)
.map_err(CapsError)
}
pub fn has_cap(tid: i32, cset: CapSet, cap: Capability) -> Result<bool, CapsError> {
let mut hdr = CapUserHeader {
version: CAPS_V3,
pid: tid,
};
let mut data: CapUserData = CapUserData::default();
capget(&mut hdr, &mut data)?;
let caps: u64 = match cset {
CapSet::Effective => (u64::from(data.effective_s1) << 32) + u64::from(data.effective_s0),
CapSet::Inheritable => {
(u64::from(data.inheritable_s1) << 32) + u64::from(data.inheritable_s0)
}
CapSet::Permitted => (u64::from(data.permitted_s1) << 32) + u64::from(data.permitted_s0),
CapSet::Bounding | CapSet::Ambient => return Err(CapsError(Errno::EINVAL)),
};
let has_cap = (caps & cap.bitmask()) != 0;
Ok(has_cap)
}
pub fn clear(tid: i32, cset: CapSet) -> Result<(), CapsError> {
let mut hdr = CapUserHeader {
version: CAPS_V3,
pid: tid,
};
let mut data: CapUserData = CapUserData::default();
capget(&mut hdr, &mut data)?;
match cset {
CapSet::Effective => {
data.effective_s0 = 0;
data.effective_s1 = 0;
}
CapSet::Inheritable => {
data.inheritable_s0 = 0;
data.inheritable_s1 = 0;
}
CapSet::Permitted => {
data.effective_s0 = 0;
data.effective_s1 = 0;
data.permitted_s0 = 0;
data.permitted_s1 = 0;
}
CapSet::Bounding | CapSet::Ambient => return Err(CapsError(Errno::EINVAL)),
}
capset(&mut hdr, &data)
}
pub fn read(tid: i32, cset: CapSet) -> Result<Capabilities, CapsError> {
let mut hdr = CapUserHeader {
version: CAPS_V3,
pid: tid,
};
let mut data: CapUserData = CapUserData::default();
capget(&mut hdr, &mut data)?;
let caps: u64 = match cset {
CapSet::Effective => (u64::from(data.effective_s1) << 32) + u64::from(data.effective_s0),
CapSet::Inheritable => {
(u64::from(data.inheritable_s1) << 32) + u64::from(data.inheritable_s0)
}
CapSet::Permitted => (u64::from(data.permitted_s1) << 32) + u64::from(data.permitted_s0),
CapSet::Bounding | CapSet::Ambient => return Err(CapsError(Errno::EINVAL)),
};
Ok(Capabilities::from_bits_truncate(caps) & Capabilities::all())
}
pub fn set(tid: i32, cset: CapSet, value: Capabilities) -> Result<(), CapsError> {
let mut hdr = CapUserHeader {
version: CAPS_V3,
pid: tid,
};
let mut data: CapUserData = CapUserData::default();
capget(&mut hdr, &mut data)?;
{
let (s1, s0) = match cset {
CapSet::Effective => (&mut data.effective_s1, &mut data.effective_s0),
CapSet::Inheritable => (&mut data.inheritable_s1, &mut data.inheritable_s0),
CapSet::Permitted => (&mut data.permitted_s1, &mut data.permitted_s0),
CapSet::Bounding | CapSet::Ambient => return Err(CapsError(Errno::EINVAL)),
};
let bits = (value & super::Capabilities::all()).bits();
*s0 = (bits & 0xFFFF_FFFF) as u32;
*s1 = (bits >> 32) as u32;
}
capset(&mut hdr, &data)
}
pub fn set_epi(
tid: i32,
eff: Capabilities,
perm: Capabilities,
inh: Capabilities,
) -> Result<(), CapsError> {
let mut hdr = CapUserHeader {
version: CAPS_V3,
pid: tid,
};
let mut data: CapUserData = CapUserData::default();
capget(&mut hdr, &mut data)?;
let mask = |c: Capabilities| (c & super::Capabilities::all()).bits();
let eb = mask(eff);
let pb = mask(perm);
let ib = mask(inh);
data.effective_s0 = (eb & 0xFFFF_FFFF) as u32;
data.effective_s1 = (eb >> 32) as u32;
data.permitted_s0 = (pb & 0xFFFF_FFFF) as u32;
data.permitted_s1 = (pb >> 32) as u32;
data.inheritable_s0 = (ib & 0xFFFF_FFFF) as u32;
data.inheritable_s1 = (ib >> 32) as u32;
capset(&mut hdr, &data)?;
Ok(())
}
pub fn drop(tid: i32, cset: CapSet, cap: Capability) -> Result<(), CapsError> {
let mut caps = read(tid, cset)?;
let flag: Capabilities = cap.into();
if caps.contains(flag) {
caps.remove(flag);
set(tid, cset, caps)?;
}
Ok(())
}
pub fn raise(tid: i32, cset: CapSet, cap: Capability) -> Result<(), CapsError> {
let mut caps = read(tid, cset)?;
let flag: Capabilities = cap.into();
if !caps.contains(flag) {
caps.insert(flag);
set(tid, cset, caps)?;
}
Ok(())
}
#[derive(Debug)]
#[repr(C)]
struct CapUserHeader {
version: u32,
pid: i32,
}
#[derive(Debug, Default, Clone)]
#[repr(C)]
struct CapUserData {
effective_s0: u32,
permitted_s0: u32,
inheritable_s0: u32,
effective_s1: u32,
permitted_s1: u32,
inheritable_s1: u32,
}