use super::{Cap, CapSet};
#[inline]
pub fn raise(cap: Cap) -> crate::Result<()> {
unsafe {
crate::raw_prctl(
libc::PR_CAP_AMBIENT,
libc::PR_CAP_AMBIENT_RAISE as libc::c_ulong,
cap as libc::c_ulong,
0,
0,
)?;
}
Ok(())
}
#[inline]
pub fn lower(cap: Cap) -> crate::Result<()> {
unsafe {
crate::raw_prctl(
libc::PR_CAP_AMBIENT,
libc::PR_CAP_AMBIENT_LOWER as libc::c_ulong,
cap as libc::c_ulong,
0,
0,
)?;
}
Ok(())
}
#[inline]
pub fn is_set(cap: Cap) -> Option<bool> {
match unsafe {
crate::raw_prctl_opt(
libc::PR_CAP_AMBIENT,
libc::PR_CAP_AMBIENT_IS_SET as libc::c_ulong,
cap as libc::c_ulong,
0,
0,
)
} {
Some(res) => Some(res != 0),
None => {
#[cfg(not(feature = "sc"))]
debug_assert_eq!(unsafe { *libc::__errno_location() }, libc::EINVAL);
None
}
}
}
#[inline]
pub fn clear() -> crate::Result<()> {
unsafe {
crate::raw_prctl(
libc::PR_CAP_AMBIENT,
libc::PR_CAP_AMBIENT_CLEAR_ALL as libc::c_ulong,
0,
0,
0,
)?;
}
Ok(())
}
#[inline]
pub fn is_supported() -> bool {
is_set(Cap::CHOWN).is_some()
}
pub fn probe() -> Option<CapSet> {
let mut set = CapSet::empty();
for cap in Cap::iter() {
match is_set(cap) {
Some(true) => set.add(cap),
Some(false) => (),
None => {
if cap as u8 == 0 {
return None;
} else {
break;
}
}
}
}
Some(set)
}
pub fn clear_unknown() -> crate::Result<()> {
for cap in (super::CAP_MAX as libc::c_ulong + 1)..(super::CAP_MAX as libc::c_ulong * 2) {
match unsafe {
crate::raw_prctl(
libc::PR_CAP_AMBIENT,
libc::PR_CAP_AMBIENT_LOWER as libc::c_ulong,
cap,
0,
0,
)
} {
Ok(_) => (),
Err(e) if e.code() == libc::EINVAL => return Ok(()),
Err(e) => return Err(e),
}
}
Err(crate::Error::from_code(libc::E2BIG))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ambient_supported() {
if is_supported() {
let orig_caps = probe().unwrap();
let supported_caps = Cap::probe_supported();
for cap in Cap::iter() {
if supported_caps.has(cap) {
assert_eq!(is_set(cap), Some(orig_caps.has(cap)), "{:?}", cap);
} else {
assert_eq!(is_set(cap), None, "{:?}", cap);
}
}
clear_unknown().unwrap();
assert_eq!(probe().unwrap(), orig_caps);
clear().unwrap();
assert_eq!(probe().unwrap(), CapSet::empty());
let orig_state = crate::caps::CapState::get_current().unwrap();
let mut state = orig_state;
state.inheritable = state.permitted;
state.set_current().unwrap();
for cap in state.inheritable {
raise(cap).unwrap();
}
state.inheritable.clear();
state.set_current().unwrap();
assert_eq!(probe().unwrap(), CapSet::empty());
for cap in supported_caps {
assert_eq!(raise(cap).unwrap_err().code(), libc::EPERM);
}
orig_state.set_current().unwrap();
for cap in orig_caps.iter() {
raise(cap).unwrap();
}
for cap in (supported_caps - orig_caps).iter() {
lower(cap).unwrap();
}
for cap in !supported_caps {
assert_eq!(raise(cap).unwrap_err().code(), libc::EINVAL);
assert_eq!(lower(cap).unwrap_err().code(), libc::EINVAL);
}
} else {
assert_eq!(probe(), None);
assert_eq!(raise(Cap::CHOWN).unwrap_err().code(), libc::EINVAL);
assert_eq!(lower(Cap::CHOWN).unwrap_err().code(), libc::EINVAL);
assert_eq!(clear().unwrap_err().code(), libc::EINVAL);
assert_eq!(clear_unknown().unwrap_err().code(), libc::EINVAL);
}
}
}