#![allow(dead_code)]
use crate::{
errors::{error, Errno},
gid_t, uapi, uid_t,
};
use bitflags::bitflags;
use std::ops;
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Capabilities {
mask: u64,
}
impl Capabilities {
pub fn empty() -> Self {
Self { mask: 0 }
}
pub fn all() -> Self {
Self { mask: u64::MAX }
}
pub fn union(&self, caps: Capabilities) -> Self {
let mut new_caps = *self;
new_caps.insert(caps);
new_caps
}
pub fn difference(&self, caps: Capabilities) -> Self {
let mut new_caps = *self;
new_caps.remove(caps);
new_caps
}
pub fn contains(self, caps: Capabilities) -> bool {
(self & caps) == caps
}
pub fn insert(&mut self, caps: Capabilities) {
*self |= caps;
}
pub fn remove(&mut self, caps: Capabilities) {
*self &= !caps;
}
pub fn as_abi_v1(self) -> u32 {
self.mask as u32
}
pub fn from_abi_v1(bits: u32) -> Self {
Self { mask: bits as u64 }
}
pub fn as_abi_v3(self) -> (u32, u32) {
(self.mask as u32, (self.mask >> 32) as u32)
}
pub fn from_abi_v3(u32s: (u32, u32)) -> Self {
Self { mask: u32s.0 as u64 | ((u32s.1 as u64) << 32) }
}
}
impl std::convert::TryFrom<u64> for Capabilities {
type Error = Errno;
fn try_from(capability_num: u64) -> Result<Self, Self::Error> {
match 1u64.checked_shl(capability_num as u32) {
Some(mask) => Ok(Self { mask }),
_ => error!(EINVAL),
}
}
}
impl ops::BitAnd for Capabilities {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
Self { mask: self.mask & rhs.mask }
}
}
impl ops::BitAndAssign for Capabilities {
fn bitand_assign(&mut self, rhs: Self) {
self.mask &= rhs.mask;
}
}
impl ops::BitOr for Capabilities {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self { mask: self.mask | rhs.mask }
}
}
impl ops::BitOrAssign for Capabilities {
fn bitor_assign(&mut self, rhs: Self) {
self.mask |= rhs.mask;
}
}
impl ops::Not for Capabilities {
type Output = Self;
fn not(self) -> Self::Output {
Self { mask: !self.mask }
}
}
impl std::fmt::Debug for Capabilities {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "Capabilities({:#x})", self.mask)
}
}
impl std::str::FromStr for Capabilities {
type Err = Errno;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"CHOWN" => CAP_CHOWN,
"DAC_OVERRIDE" => CAP_DAC_OVERRIDE,
"DAC_READ_SEARCH" => CAP_DAC_READ_SEARCH,
"FOWNER" => CAP_FOWNER,
"FSETID" => CAP_FSETID,
"KILL" => CAP_KILL,
"SETGID" => CAP_SETGID,
"SETUID" => CAP_SETUID,
"SETPCAP" => CAP_SETPCAP,
"LINUX_IMMUTABLE" => CAP_LINUX_IMMUTABLE,
"NET_BIND_SERVICE" => CAP_NET_BIND_SERVICE,
"NET_BROADCAST" => CAP_NET_BROADCAST,
"NET_ADMIN" => CAP_NET_ADMIN,
"NET_RAW" => CAP_NET_RAW,
"IPC_LOCK" => CAP_IPC_LOCK,
"IPC_OWNER" => CAP_IPC_OWNER,
"SYS_MODULE" => CAP_SYS_MODULE,
"SYS_RAWIO" => CAP_SYS_RAWIO,
"SYS_CHROOT" => CAP_SYS_CHROOT,
"SYS_PTRACE" => CAP_SYS_PTRACE,
"SYS_PACCT" => CAP_SYS_PACCT,
"SYS_ADMIN" => CAP_SYS_ADMIN,
"SYS_BOOT" => CAP_SYS_BOOT,
"SYS_NICE" => CAP_SYS_NICE,
"SYS_RESOURCE" => CAP_SYS_RESOURCE,
"SYS_TIME" => CAP_SYS_TIME,
"SYS_TTY_CONFIG" => CAP_SYS_TTY_CONFIG,
"MKNOD" => CAP_MKNOD,
"LEASE" => CAP_LEASE,
"AUDIT_WRITE" => CAP_AUDIT_WRITE,
"AUDIT_CONTROL" => CAP_AUDIT_CONTROL,
"SETFCAP" => CAP_SETFCAP,
"MAC_OVERRIDE" => CAP_MAC_OVERRIDE,
"MAC_ADMIN" => CAP_MAC_ADMIN,
"SYSLOG" => CAP_SYSLOG,
"WAKE_ALARM" => CAP_WAKE_ALARM,
"BLOCK_SUSPEND" => CAP_BLOCK_SUSPEND,
"AUDIT_READ" => CAP_AUDIT_READ,
"PERFMON" => CAP_PERFMON,
"BPF" => CAP_BPF,
"CHECKPOINT_RESTORE" => CAP_CHECKPOINT_RESTORE,
_ => return error!(EINVAL),
})
}
}
pub const CAP_CHOWN: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_CHOWN };
pub const CAP_DAC_OVERRIDE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_DAC_OVERRIDE };
pub const CAP_DAC_READ_SEARCH: Capabilities =
Capabilities { mask: 1u64 << uapi::CAP_DAC_READ_SEARCH };
pub const CAP_FOWNER: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_FOWNER };
pub const CAP_FSETID: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_FSETID };
pub const CAP_KILL: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_KILL };
pub const CAP_SETGID: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SETGID };
pub const CAP_SETUID: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SETUID };
pub const CAP_SETPCAP: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SETPCAP };
pub const CAP_LINUX_IMMUTABLE: Capabilities =
Capabilities { mask: 1u64 << uapi::CAP_LINUX_IMMUTABLE };
pub const CAP_NET_BIND_SERVICE: Capabilities =
Capabilities { mask: 1u64 << uapi::CAP_NET_BIND_SERVICE };
pub const CAP_NET_BROADCAST: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_NET_BROADCAST };
pub const CAP_NET_ADMIN: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_NET_ADMIN };
pub const CAP_NET_RAW: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_NET_RAW };
pub const CAP_IPC_LOCK: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_IPC_LOCK };
pub const CAP_IPC_OWNER: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_IPC_OWNER };
pub const CAP_SYS_MODULE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_MODULE };
pub const CAP_SYS_RAWIO: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_RAWIO };
pub const CAP_SYS_CHROOT: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_CHROOT };
pub const CAP_SYS_PTRACE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_PTRACE };
pub const CAP_SYS_PACCT: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_PACCT };
pub const CAP_SYS_ADMIN: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_ADMIN };
pub const CAP_SYS_BOOT: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_BOOT };
pub const CAP_SYS_NICE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_NICE };
pub const CAP_SYS_RESOURCE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_RESOURCE };
pub const CAP_SYS_TIME: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_TIME };
pub const CAP_SYS_TTY_CONFIG: Capabilities =
Capabilities { mask: 1u64 << uapi::CAP_SYS_TTY_CONFIG };
pub const CAP_MKNOD: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_MKNOD };
pub const CAP_LEASE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_LEASE };
pub const CAP_AUDIT_WRITE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_AUDIT_WRITE };
pub const CAP_AUDIT_CONTROL: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_AUDIT_CONTROL };
pub const CAP_SETFCAP: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SETFCAP };
pub const CAP_MAC_OVERRIDE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_MAC_OVERRIDE };
pub const CAP_MAC_ADMIN: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_MAC_ADMIN };
pub const CAP_SYSLOG: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYSLOG };
pub const CAP_WAKE_ALARM: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_WAKE_ALARM };
pub const CAP_BLOCK_SUSPEND: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_BLOCK_SUSPEND };
pub const CAP_AUDIT_READ: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_AUDIT_READ };
pub const CAP_PERFMON: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_PERFMON };
pub const CAP_BPF: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_BPF };
pub const CAP_CHECKPOINT_RESTORE: Capabilities =
Capabilities { mask: 1u64 << uapi::CAP_CHECKPOINT_RESTORE };
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PtraceAccessMode: u32 {
const READ = 1 << 0;
const ATTACH = 1 << 1;
const FSCREDS = 1 << 2;
const REALCREDS = 1 << 3;
const NOAUDIT = 1 << 4;
}
}
pub const PTRACE_MODE_READ: PtraceAccessMode = PtraceAccessMode::READ;
pub const PTRACE_MODE_ATTACH: PtraceAccessMode = PtraceAccessMode::ATTACH;
pub const PTRACE_MODE_FSCREDS: PtraceAccessMode = PtraceAccessMode::FSCREDS;
pub const PTRACE_MODE_REALCREDS: PtraceAccessMode = PtraceAccessMode::REALCREDS;
pub const PTRACE_MODE_READ_FSCREDS: PtraceAccessMode = PtraceAccessMode::from_bits_truncate(
PtraceAccessMode::READ.bits() | PtraceAccessMode::FSCREDS.bits(),
);
pub const PTRACE_MODE_READ_REALCREDS: PtraceAccessMode = PtraceAccessMode::from_bits_truncate(
PtraceAccessMode::READ.bits() | PtraceAccessMode::REALCREDS.bits(),
);
pub const PTRACE_MODE_ATTACH_FSCREDS: PtraceAccessMode = PtraceAccessMode::from_bits_truncate(
PtraceAccessMode::ATTACH.bits() | PtraceAccessMode::FSCREDS.bits(),
);
pub const PTRACE_MODE_ATTACH_REALCREDS: PtraceAccessMode = PtraceAccessMode::from_bits_truncate(
PtraceAccessMode::ATTACH.bits() | PtraceAccessMode::REALCREDS.bits(),
);
pub const PTRACE_MODE_NOAUDIT: PtraceAccessMode = PtraceAccessMode::NOAUDIT;
#[derive(Debug, Clone)]
pub struct Credentials {
pub uid: uid_t,
pub gid: gid_t,
pub euid: uid_t,
pub egid: gid_t,
pub saved_uid: uid_t,
pub saved_gid: gid_t,
pub groups: Vec<gid_t>,
pub fsuid: uid_t,
pub fsgid: gid_t,
pub cap_permitted: Capabilities,
pub cap_effective: Capabilities,
pub cap_inheritable: Capabilities,
pub cap_bounding: Capabilities,
pub cap_ambient: Capabilities,
pub securebits: SecureBits,
}
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SecureBits: u32 {
const KEEP_CAPS = 1 << uapi::SECURE_KEEP_CAPS;
const KEEP_CAPS_LOCKED = 1 << uapi::SECURE_KEEP_CAPS_LOCKED;
const NO_SETUID_FIXUP = 1 << uapi::SECURE_NO_SETUID_FIXUP;
const NO_SETUID_FIXUP_LOCKED = 1 << uapi::SECURE_NO_SETUID_FIXUP_LOCKED;
const NOROOT = 1 << uapi::SECURE_NOROOT;
const NOROOT_LOCKED = 1 << uapi::SECURE_NOROOT_LOCKED;
const NO_CAP_AMBIENT_RAISE = 1 << uapi::SECURE_NO_CAP_AMBIENT_RAISE;
const NO_CAP_AMBIENT_RAISE_LOCKED = 1 << uapi::SECURE_NO_CAP_AMBIENT_RAISE_LOCKED;
}
}
impl Credentials {
pub fn root() -> Self {
Self::with_ids(0, 0)
}
pub fn with_ids(uid: uid_t, gid: gid_t) -> Credentials {
let caps = if uid == 0 { Capabilities::all() } else { Capabilities::empty() };
Credentials {
uid,
gid,
euid: uid,
egid: gid,
saved_uid: uid,
saved_gid: gid,
groups: vec![],
fsuid: uid,
fsgid: gid,
cap_permitted: caps,
cap_effective: caps,
cap_inheritable: Capabilities::empty(),
cap_bounding: Capabilities::all(),
cap_ambient: Capabilities::empty(),
securebits: SecureBits::empty(),
}
}
pub fn has_same_uid(&self, other: &Credentials) -> bool {
self.euid == other.saved_uid
|| self.euid == other.uid
|| self.uid == other.uid
|| self.uid == other.saved_uid
}
pub fn is_superuser(&self) -> bool {
self.euid == 0
}
pub fn is_in_group(&self, gid: gid_t) -> bool {
self.egid == gid || self.groups.contains(&gid)
}
pub fn has_capability(&self, capability: Capabilities) -> bool {
self.cap_effective.contains(capability)
}
pub fn exec(&mut self) {
if self.uid == 0 && self.euid == 0 {
self.cap_permitted = self.cap_inheritable | self.cap_bounding;
} else {
self.cap_permitted = self.cap_inheritable | self.cap_ambient;
}
self.cap_effective = self.cap_permitted;
self.securebits.remove(SecureBits::KEEP_CAPS);
}
pub fn as_fscred(&self) -> FsCred {
FsCred { uid: self.fsuid, gid: self.fsgid }
}
pub fn euid_as_fscred(&self) -> FsCred {
FsCred { uid: self.euid, gid: self.egid }
}
pub fn uid_as_fscred(&self) -> FsCred {
FsCred { uid: self.uid, gid: self.gid }
}
pub fn update_capabilities(
&mut self,
prev_uid: uid_t,
prev_euid: uid_t,
prev_fsuid: uid_t,
prev_saved_uid: uid_t,
) {
if !self.securebits.contains(SecureBits::KEEP_CAPS)
&& !self.securebits.contains(SecureBits::NO_SETUID_FIXUP)
&& (prev_uid == 0 || prev_euid == 0 || prev_saved_uid == 0)
&& (self.uid != 0 && self.euid != 0 && self.saved_uid != 0)
{
self.cap_permitted = Capabilities::empty();
self.cap_effective = Capabilities::empty();
self.cap_ambient = Capabilities::empty();
}
if prev_euid == 0 && self.euid != 0 {
self.cap_effective = Capabilities::empty();
} else if prev_euid != 0 && self.euid == 0 {
self.cap_effective = self.cap_permitted;
}
let fs_capabilities = CAP_CHOWN
| CAP_DAC_OVERRIDE
| CAP_DAC_READ_SEARCH
| CAP_FOWNER
| CAP_FSETID
| CAP_LINUX_IMMUTABLE
| CAP_MAC_OVERRIDE
| CAP_MKNOD;
if prev_fsuid == 0 && self.fsuid != 0 {
self.cap_effective &= !fs_capabilities;
} else if prev_fsuid != 0 && self.fsuid == 0 {
self.cap_effective |= self.cap_permitted & fs_capabilities;
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct FsCred {
pub uid: uid_t,
pub gid: gid_t,
}
impl FsCred {
pub fn root() -> Self {
Self { uid: 0, gid: 0 }
}
}
impl From<Credentials> for FsCred {
fn from(c: Credentials) -> Self {
c.as_fscred()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[::fuchsia::test]
fn test_empty() {
assert_eq!(Capabilities::empty().mask, 0);
}
#[::fuchsia::test]
fn test_all() {
assert_eq!(Capabilities::all().mask, u64::MAX);
}
#[::fuchsia::test]
fn test_union() {
let expected = Capabilities { mask: CAP_BLOCK_SUSPEND.mask | CAP_AUDIT_READ.mask };
assert_eq!(CAP_BLOCK_SUSPEND.union(CAP_AUDIT_READ), expected);
assert_eq!(CAP_BLOCK_SUSPEND.union(CAP_BLOCK_SUSPEND), CAP_BLOCK_SUSPEND);
}
#[::fuchsia::test]
fn test_difference() {
let base = CAP_BPF | CAP_AUDIT_WRITE;
let expected = CAP_BPF;
assert_eq!(base.difference(CAP_AUDIT_WRITE), expected);
assert_eq!(base.difference(CAP_AUDIT_WRITE | CAP_BPF), Capabilities::empty());
}
#[::fuchsia::test]
fn test_contains() {
let base = CAP_BPF | CAP_AUDIT_WRITE;
assert!(base.contains(CAP_AUDIT_WRITE));
assert!(base.contains(CAP_BPF));
assert!(base.contains(CAP_AUDIT_WRITE | CAP_BPF));
assert!(!base.contains(CAP_AUDIT_CONTROL));
assert!(!base.contains(CAP_AUDIT_WRITE | CAP_BPF | CAP_AUDIT_CONTROL));
}
#[::fuchsia::test]
fn test_insert() {
let mut capabilities = CAP_BLOCK_SUSPEND;
capabilities.insert(CAP_BLOCK_SUSPEND);
assert_eq!(capabilities, CAP_BLOCK_SUSPEND);
capabilities.insert(CAP_AUDIT_READ);
let expected = Capabilities { mask: CAP_BLOCK_SUSPEND.mask | CAP_AUDIT_READ.mask };
assert_eq!(capabilities, expected);
}
#[::fuchsia::test]
fn test_remove() {
let mut capabilities = CAP_BLOCK_SUSPEND;
capabilities.remove(CAP_BLOCK_SUSPEND);
assert_eq!(capabilities, Capabilities::empty());
let mut capabilities = CAP_BLOCK_SUSPEND | CAP_AUDIT_READ;
capabilities.remove(CAP_AUDIT_READ);
assert_eq!(capabilities, CAP_BLOCK_SUSPEND);
}
#[::fuchsia::test]
fn test_try_from() {
let capabilities = CAP_BLOCK_SUSPEND;
assert_eq!(Capabilities::try_from(uapi::CAP_BLOCK_SUSPEND as u64), Ok(capabilities));
assert_eq!(Capabilities::try_from(200000), error!(EINVAL));
}
}