capo 0.0.1

Linux capabilities interface
Documentation
// Copyright (C) 2020 - Will Glozer. All rights reserved.

use std::os::raw::c_int;
use crate::Error;
use crate::ffi::{self, Data, Header};
use crate::set::Set;
use crate::sys::{Cmd, ambient, bounding, capget, capset};

#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Cap {
    Chown          = ffi::CAP_CHOWN,
    DacOverride    = ffi::CAP_DAC_OVERRIDE,
    DacReadSearch  = ffi::CAP_DAC_READ_SEARCH,
    FOwner         = ffi::CAP_FOWNER,
    FSetId         = ffi::CAP_FSETID,
    Kill           = ffi::CAP_KILL,
    SetGid         = ffi::CAP_SETGID,
    SetUid         = ffi::CAP_SETUID,
    SetPcap        = ffi::CAP_SETPCAP,
    LinuxImmutable = ffi::CAP_LINUX_IMMUTABLE,
    NetBindService = ffi::CAP_NET_BIND_SERVICE,
    NetBroadcast   = ffi::CAP_NET_BROADCAST,
    NetAdmin       = ffi::CAP_NET_ADMIN,
    NetRaw         = ffi::CAP_NET_RAW,
    IpcLock        = ffi::CAP_IPC_LOCK,
    IpcOwner       = ffi::CAP_IPC_OWNER,
    SysModule      = ffi::CAP_SYS_MODULE,
    SysRawIO       = ffi::CAP_SYS_RAWIO,
    SysChroot      = ffi::CAP_SYS_CHROOT,
    SysPtrace      = ffi::CAP_SYS_PTRACE,
    SysPacct       = ffi::CAP_SYS_PACCT,
    SysAdmin       = ffi::CAP_SYS_ADMIN,
    SysBoot        = ffi::CAP_SYS_BOOT,
    SysNice        = ffi::CAP_SYS_NICE,
    SysResource    = ffi::CAP_SYS_RESOURCE,
    SysTime        = ffi::CAP_SYS_TIME,
    SysTTYConfig   = ffi::CAP_SYS_TTY_CONFIG,
    Mknod          = ffi::CAP_MKNOD,
    Lease          = ffi::CAP_LEASE,
    AuditWrite     = ffi::CAP_AUDIT_WRITE,
    AuditControl   = ffi::CAP_AUDIT_CONTROL,
    Setfcap        = ffi::CAP_SETFCAP,
    MacOverride    = ffi::CAP_MAC_OVERRIDE,
    MacAdmin       = ffi::CAP_MAC_ADMIN,
    Syslog         = ffi::CAP_SYSLOG,
    WakeAlarm      = ffi::CAP_WAKE_ALARM,
    BlockSuspend   = ffi::CAP_BLOCK_SUSPEND,
    AuditRead      = ffi::CAP_AUDIT_READ,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Caps {
    pub effective:   Set,
    pub permitted:   Set,
    pub inheritable: Set,
}

pub struct Ambient;
pub struct Bounding;

impl Caps {
    pub fn empty() -> Self {
        Self {
            effective:   Set::empty(),
            permitted:   Set::empty(),
            inheritable: Set::empty(),
        }
    }

    pub fn get() -> Result<Self, Error> {
        Self::fetch(0)
    }

    pub fn set(&self) -> Result<(), Error> {
        self.apply(0)
    }

    pub fn fetch(pid: c_int) -> Result<Self, Error> {
        let head = Header::v3(pid);
        let mut data = data();
        capget(&head, &mut data)?;
        Ok(Self::from(data))
    }

    pub fn apply(&self, pid: c_int) -> Result<(), Error> {
        let head = Header::v3(pid);
        let mut data = data();
        self.shift(&mut data);
        Ok(capset(&head, &data)?)
    }

    pub fn shift(&self, [low, high]: &mut [Data; 2]) {
        self.effective.shift(&mut high.effective, &mut low.effective);
        self.permitted.shift(&mut high.permitted, &mut low.permitted);
        self.inheritable.shift(&mut high.inheritable, &mut low.inheritable);
    }
}

impl Ambient {
    pub fn raise(cap: Cap) -> Result<(), Error> {
        ambient(Cmd::AMBIENT_RAISE, cap)?;
        Ok(())
    }

    pub fn lower(cap: Cap) -> Result<(), Error> {
        ambient(Cmd::AMBIENT_LOWER, cap)?;
        Ok(())
    }

    pub fn is_set(cap: Cap) -> Result<bool, Error> {
        Ok(ambient(Cmd::AMBIENT_IS_SET, cap)? == 1)
    }

    pub fn clear_all() -> Result<(), Error> {
        ambient(Cmd::AMBIENT_CLEAR_ALL, 0)?;
        Ok(())
    }
}

impl Bounding  {
    pub fn read(cap: Cap) -> Result<bool, Error> {
        Ok(bounding(Cmd::BOUNDING_READ, cap)? == 1)
    }

    pub fn drop(cap: Cap) -> Result<(), Error> {
        bounding(Cmd::BOUNDING_DROP, cap)?;
        Ok(())
    }
}

impl From<Cap> for u64 {
    fn from(cap: Cap) -> u64 {
        1 << u64::from(cap as u8)
    }
}

impl From<Cap> for c_int {
    fn from(cap: Cap) -> c_int {
        c_int::from(cap as u8)
    }
}

impl From<[Data; 2]> for Caps {
    fn from([low, high]: [Data; 2]) -> Self {
        Self {
            effective:   Set::from((high.effective,   low.effective)),
            permitted:   Set::from((high.permitted,   low.permitted)),
            inheritable: Set::from((high.inheritable, low.inheritable)),
        }
    }
}

fn data() -> [Data; 2] {
    Default::default()
}

#[test]
fn test() -> Result<(), Error> {
    let effective = Caps::get()?.effective;
    assert!(!effective.contains(Cap::SysAdmin));
    assert!(!Ambient::is_set(Cap::SysAdmin)?);
    assert!(Bounding::read(Cap::SysAdmin)?);
    Ok(())
}