use super::{Cap, CapSet};
#[inline]
pub fn drop(cap: Cap) -> crate::Result<()> {
unsafe { crate::raw_prctl(libc::PR_CAPBSET_DROP, cap as libc::c_ulong, 0, 0, 0) }?;
Ok(())
}
#[inline]
pub fn read(cap: Cap) -> Option<bool> {
read_raw(cap as _)
}
#[inline]
fn read_raw(cap: libc::c_ulong) -> Option<bool> {
match unsafe { crate::raw_prctl_opt(libc::PR_CAPBSET_READ, cap, 0, 0, 0) } {
Some(res) => Some(res != 0),
None => {
#[cfg(not(feature = "sc"))]
debug_assert_eq!(unsafe { *libc::__errno_location() }, libc::EINVAL);
None
}
}
}
#[deprecated(since = "0.2.1", note = "use `read()` instead")]
#[inline]
pub fn is_set(cap: Cap) -> Option<bool> {
read(cap)
}
pub fn probe() -> CapSet {
let mut set = CapSet::empty();
for cap in Cap::iter() {
match read(cap) {
Some(true) => set.add(cap),
Some(false) => (),
_ => break,
}
}
set
}
pub fn ensure_dropped(cap: Cap) -> crate::Result<()> {
ensure_dropped_raw(cap as _)
}
#[inline]
fn ensure_dropped_raw(cap: libc::c_ulong) -> crate::Result<()> {
match unsafe { crate::raw_prctl(libc::PR_CAPBSET_DROP, cap, 0, 0, 0) } {
Ok(_) => Ok(()),
Err(e) if e.code() == libc::EPERM => match read_raw(cap) {
Some(true) => Err(e),
Some(false) => Ok(()),
None => Err(crate::Error::from_code(libc::EINVAL)),
},
Err(e) => Err(e),
}
}
fn clear_from(low: libc::c_ulong) -> crate::Result<()> {
for cap in low..(super::CAP_MAX as libc::c_ulong * 2) {
match ensure_dropped_raw(cap) {
Ok(()) => (),
Err(e) if e.code() == libc::EINVAL && cap != 0 => return Ok(()),
Err(e) => return Err(e),
}
}
Err(crate::Error::from_code(libc::E2BIG))
}
#[inline]
pub fn clear() -> crate::Result<()> {
clear_from(0)
}
#[inline]
pub fn clear_unknown() -> crate::Result<()> {
clear_from((super::CAP_MAX + 1) as _)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bounding() {
probe();
read(Cap::CHOWN).unwrap();
}
#[test]
fn test_bounding_drop() {
if crate::caps::CapState::get_current()
.unwrap()
.effective
.has(crate::caps::Cap::SETPCAP)
{
assert!(read(crate::caps::Cap::SETPCAP).unwrap());
drop(crate::caps::Cap::SETPCAP).unwrap();
assert!(!read(crate::caps::Cap::SETPCAP).unwrap());
} else {
assert_eq!(
drop(crate::caps::Cap::SETPCAP).unwrap_err().code(),
libc::EPERM
);
}
}
#[test]
fn test_clear() {
let mut state = crate::caps::CapState::get_current().unwrap();
if state.effective.has(crate::caps::Cap::SETPCAP) || probe().is_empty() {
clear().unwrap();
assert_eq!(probe(), crate::caps::CapSet::empty());
clear().unwrap();
assert_eq!(probe(), crate::caps::CapSet::empty());
state.effective.drop(crate::caps::Cap::SETPCAP);
state.set_current().unwrap();
clear().unwrap();
assert_eq!(probe(), crate::caps::CapSet::empty());
} else {
assert_eq!(clear().unwrap_err().code(), libc::EPERM);
}
}
#[test]
fn test_clear_unknown() {
let mut state = crate::caps::CapState::get_current().unwrap();
if state.effective.has(crate::caps::Cap::SETPCAP)
|| read_raw((super::super::CAP_MAX + 1) as _).is_none()
{
let orig_caps = probe();
clear_unknown().unwrap();
assert_eq!(probe(), orig_caps);
clear_unknown().unwrap();
assert_eq!(probe(), orig_caps);
assert!(matches!(
read_raw((super::super::CAP_MAX + 1) as _),
Some(false) | None
));
state.effective.drop(crate::caps::Cap::SETPCAP);
state.set_current().unwrap();
clear_unknown().unwrap();
assert_eq!(probe(), orig_caps);
} else {
assert_eq!(clear_unknown().unwrap_err().code(), libc::EPERM);
}
}
}