//! Linux-compatible process capabilities.
//!
//! This module implements the five capability sets (`effective`, `permitted`,
//! `inheritable`, `ambient`, `bounding`) used by the kernel for
//! fine-grained privilege checks.

#![allow(missing_docs)]

use crate::error::{KernelError, Result};

// Linux-compatible capability flags.
//
// Each flag corresponds to a single Linux capability as defined in
// `include/uapi/linux/capability.h`.
bitflags::bitflags! {
    /// Linux-compatible capability flags.
    ///
    /// Each flag corresponds to a single Linux capability as defined in
    /// `include/uapi/linux/capability.h`.
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    pub struct CapabilitiesFlags: u64 {
        const CAP_CHOWN = 1 << 0;
        const CAP_DAC_OVERRIDE = 1 << 1;
        const CAP_DAC_READ_SEARCH = 1 << 2;
        const CAP_FOWNER = 1 << 3;
        const CAP_FSETID = 1 << 4;
        const CAP_KILL = 1 << 5;
        const CAP_SETGID = 1 << 6;
        const CAP_SETUID = 1 << 7;
        const CAP_SETPCAP = 1 << 8;
        const CAP_LINUX_IMMUTABLE = 1 << 9;
        const CAP_NET_BIND_SERVICE = 1 << 10;
        const CAP_NET_BROADCAST = 1 << 11;
        const CAP_NET_ADMIN = 1 << 12;
        const CAP_NET_RAW = 1 << 13;
        const CAP_IPC_LOCK = 1 << 14;
        const CAP_IPC_OWNER = 1 << 15;
        const CAP_SYS_MODULE = 1 << 16;
        const CAP_SYS_RAWIO = 1 << 17;
        const CAP_SYS_CHROOT = 1 << 18;
        const CAP_SYS_PTRACE = 1 << 19;
        const CAP_SYS_PACCT = 1 << 20;
        const CAP_SYS_ADMIN = 1 << 21;
        const CAP_SYS_BOOT = 1 << 22;
        const CAP_SYS_NICE = 1 << 23;
        const CAP_SYS_RESOURCE = 1 << 24;
        const CAP_SYS_TIME = 1 << 25;
        const CAP_SYS_TTY_CONFIG = 1 << 26;
        const CAP_MKNOD = 1 << 27;
        const CAP_LEASE = 1 << 28;
        const CAP_AUDIT_WRITE = 1 << 29;
        const CAP_AUDIT_CONTROL = 1 << 30;
        const CAP_SETFCAP = 1 << 31;
        const CAP_MAC_OVERRIDE = 1 << 32;
        const CAP_MAC_ADMIN = 1 << 33;
        const CAP_SYSLOG = 1 << 34;
        const CAP_WAKE_ALARM = 1 << 35;
        const CAP_BLOCK_SUSPEND = 1 << 36;
        const CAP_AUDIT_READ = 1 << 37;
        const CAP_PERFMON = 1 << 38;
        const CAP_BPF = 1 << 39;
        const CAP_CHECKPOINT_RESTORE = 1 << 40;
    }
}

/// The five capability sets associated with a process.
///
/// See `capabilities(7)` for the full semantics of each set.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Capabilities {
    effective: CapabilitiesFlags,
    permitted: CapabilitiesFlags,
    inheritable: CapabilitiesFlags,
    ambient: CapabilitiesFlags,
    bounding: CapabilitiesFlags,
}

impl Capabilities {
    /// Creates a new `Capabilities` with the given sets.
    pub fn new(
        effective: CapabilitiesFlags,
        permitted: CapabilitiesFlags,
        inheritable: CapabilitiesFlags,
        ambient: CapabilitiesFlags,
        bounding: CapabilitiesFlags,
    ) -> Self {
        Self {
            effective,
            permitted,
            inheritable,
            ambient,
            bounding,
        }
    }

    /// Creates a root (all-capable) set.
    pub fn new_root() -> Self {
        Self {
            effective: CapabilitiesFlags::all(),
            permitted: CapabilitiesFlags::all(),
            inheritable: CapabilitiesFlags::all(),
            ambient: CapabilitiesFlags::all(),
            bounding: CapabilitiesFlags::all(),
        }
    }

    /// Creates an empty (no capabilities) set.
    pub fn new_empty() -> Self {
        Self {
            effective: CapabilitiesFlags::empty(),
            permitted: CapabilitiesFlags::empty(),
            inheritable: CapabilitiesFlags::empty(),
            ambient: CapabilitiesFlags::empty(),
            bounding: CapabilitiesFlags::empty(),
        }
    }

    /// Convenience method for creating capabilities with a single capability
    pub fn new_cap(cap: CapabilitiesFlags) -> Self {
        Self {
            effective: cap,
            permitted: cap,
            inheritable: cap,
            ambient: cap,
            bounding: cap,
        }
    }

    /// Set the publicly mutable fields on capabilities
    pub fn set_public(
        &mut self,
        caller_caps: Capabilities,
        effective: CapabilitiesFlags,
        permitted: CapabilitiesFlags,
        inheritable: CapabilitiesFlags,
    ) -> Result<()> {
        // permitted should be a subset of self.permitted, and effective should be a subset of permitted
        // inheritable should be a subset of self.bounding, or caller's effective should contain CAP_SETPCAP
        if !self.permitted.contains(permitted)
            || !permitted.contains(effective)
            || (!self.bounding.contains(inheritable)
                && !caller_caps
                    .effective
                    .contains(CapabilitiesFlags::CAP_SETPCAP))
        {
            return Err(KernelError::NotPermitted);
        }
        self.effective = effective;
        self.permitted = permitted;
        self.inheritable = inheritable;
        Ok(())
    }

    /// Returns the effective capability flags.
    pub fn effective(&self) -> CapabilitiesFlags {
        self.effective
    }

    /// Returns the permitted capability flags.
    pub fn permitted(&self) -> CapabilitiesFlags {
        self.permitted
    }

    /// Returns the inheritable capability flags.
    pub fn inheritable(&self) -> CapabilitiesFlags {
        self.inheritable
    }

    /// Returns the ambient capability flags.
    pub fn ambient(&self) -> CapabilitiesFlags {
        self.ambient
    }

    /// Returns a mutable reference to the ambient capability flags.
    pub fn ambient_mut(&mut self) -> &mut CapabilitiesFlags {
        &mut self.ambient
    }

    /// Returns the bounding capability flags.
    pub fn bounding(&self) -> CapabilitiesFlags {
        self.bounding
    }

    /// Returns a mutable reference to the bounding capability flags.
    pub fn bounding_mut(&mut self) -> &mut CapabilitiesFlags {
        &mut self.bounding
    }

    /// Checks if a capability is effective, as in if it can be used.
    pub fn is_capable(&self, cap: CapabilitiesFlags) -> bool {
        self.effective.contains(cap)
    }

    /// Shortcut for returning EPERM if a capability is not effective.
    pub fn check_capable(&self, cap: CapabilitiesFlags) -> Result<()> {
        if !self.effective.contains(cap) {
            Err(KernelError::NotPermitted)
        } else {
            Ok(())
        }
    }
}
