Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions src/thread/libcap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,22 @@ use crate::{backend, io};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CapabilitySets {
/// `__user_cap_data_struct.effective`
pub effective: CapabilityFlags,
pub effective: CapabilitySet,
/// `__user_cap_data_struct.permitted`
pub permitted: CapabilityFlags,
pub permitted: CapabilitySet,
/// `__user_cap_data_struct.inheritable`
pub inheritable: CapabilityFlags,
pub inheritable: CapabilitySet,
}

/// Previous name of `CapabilitySet`.
#[deprecated(since = "1.1.0", note = "Renamed to CapabilitySet")]
pub type CapabilityFlags = CapabilitySet;

bitflags! {
/// `CAP_*` constants.
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct CapabilityFlags: u64 {
pub struct CapabilitySet: u64 {
/// `CAP_CHOWN`
const CHOWN = 1 << linux_raw_sys::general::CAP_CHOWN;
/// `CAP_DAC_OVERRIDE`
Expand Down Expand Up @@ -156,9 +160,9 @@ fn capget(pid: Option<Pid>) -> io::Result<CapabilitySets> {

// The kernel returns a partitioned bitset that we just combined above.
Ok(CapabilitySets {
effective: CapabilityFlags::from_bits_retain(effective),
permitted: CapabilityFlags::from_bits_retain(permitted),
inheritable: CapabilityFlags::from_bits_retain(inheritable),
effective: CapabilitySet::from_bits_retain(effective),
permitted: CapabilitySet::from_bits_retain(permitted),
inheritable: CapabilitySet::from_bits_retain(inheritable),
})
}

Expand Down
4 changes: 3 additions & 1 deletion src/thread/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ pub use clock::*;
#[cfg(linux_kernel)]
pub use id::*;
#[cfg(linux_kernel)]
pub use libcap::{capabilities, set_capabilities, CapabilityFlags, CapabilitySets};
// #[expect(deprecated, reason = "CapabilityFlags is deprecated")]
#[allow(deprecated)]
pub use libcap::{capabilities, set_capabilities, CapabilityFlags, CapabilitySet, CapabilitySets};
#[cfg(linux_kernel)]
pub use membarrier::*;
#[cfg(linux_kernel)]
Expand Down
103 changes: 95 additions & 8 deletions src/thread/prctl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ use crate::prctl::{
};
use crate::utils::as_ptr;

use super::CapabilitySet;

//
// PR_GET_KEEPCAPS/PR_SET_KEEPCAPS
//
Expand Down Expand Up @@ -178,6 +180,7 @@ pub fn set_secure_computing_mode(mode: SecureComputingMode) -> io::Result<()> {
const PR_CAPBSET_READ: c_int = 23;

/// Linux per-thread capability.
#[deprecated(since = "1.1.0", note = "Use CapabilitySet with a single bit instead")]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u32)]
#[non_exhaustive]
Expand Down Expand Up @@ -383,6 +386,75 @@ pub enum Capability {
CheckpointRestore = linux_raw_sys::general::CAP_CHECKPOINT_RESTORE,
}

mod private {
pub trait Sealed {}
pub struct Token;

#[allow(deprecated)]
impl Sealed for crate::thread::Capability {}
impl Sealed for crate::thread::CapabilitySet {}
}
/// Compatibility trait to keep existing code that uses the deprecated [`Capability`] type working.
///
/// This trait and its methods are sealed. It must not be used downstream.
pub trait CompatCapability: private::Sealed + Copy {
#[doc(hidden)]
fn as_capability_set(self, _: private::Token) -> CapabilitySet;
}
#[allow(deprecated)]
impl CompatCapability for Capability {
fn as_capability_set(self, _: private::Token) -> CapabilitySet {
match self {
Self::ChangeOwnership => CapabilitySet::CHOWN,
Self::DACOverride => CapabilitySet::DAC_OVERRIDE,
Self::DACReadSearch => CapabilitySet::DAC_READ_SEARCH,
Self::FileOwner => CapabilitySet::FOWNER,
Self::FileSetID => CapabilitySet::FSETID,
Self::Kill => CapabilitySet::KILL,
Self::SetGroupID => CapabilitySet::SETGID,
Self::SetUserID => CapabilitySet::SETUID,
Self::SetPermittedCapabilities => CapabilitySet::SETPCAP,
Self::LinuxImmutable => CapabilitySet::LINUX_IMMUTABLE,
Self::NetBindService => CapabilitySet::NET_BIND_SERVICE,
Self::NetBroadcast => CapabilitySet::NET_BROADCAST,
Self::NetAdmin => CapabilitySet::NET_ADMIN,
Self::NetRaw => CapabilitySet::NET_RAW,
Self::IPCLock => CapabilitySet::IPC_LOCK,
Self::IPCOwner => CapabilitySet::IPC_OWNER,
Self::SystemModule => CapabilitySet::SYS_MODULE,
Self::SystemRawIO => CapabilitySet::SYS_RAWIO,
Self::SystemChangeRoot => CapabilitySet::SYS_CHROOT,
Self::SystemProcessTrace => CapabilitySet::SYS_PTRACE,
Self::SystemProcessAccounting => CapabilitySet::SYS_PACCT,
Self::SystemAdmin => CapabilitySet::SYS_ADMIN,
Self::SystemBoot => CapabilitySet::SYS_BOOT,
Self::SystemNice => CapabilitySet::SYS_NICE,
Self::SystemResource => CapabilitySet::SYS_RESOURCE,
Self::SystemTime => CapabilitySet::SYS_TIME,
Self::SystemTTYConfig => CapabilitySet::SYS_TTY_CONFIG,
Self::MakeNode => CapabilitySet::MKNOD,
Self::Lease => CapabilitySet::LEASE,
Self::AuditWrite => CapabilitySet::AUDIT_WRITE,
Self::AuditControl => CapabilitySet::AUDIT_CONTROL,
Self::SetFileCapabilities => CapabilitySet::SETFCAP,
Self::MACOverride => CapabilitySet::MAC_OVERRIDE,
Self::MACAdmin => CapabilitySet::MAC_ADMIN,
Self::SystemLog => CapabilitySet::SYSLOG,
Self::WakeAlarm => CapabilitySet::WAKE_ALARM,
Self::BlockSuspend => CapabilitySet::BLOCK_SUSPEND,
Self::AuditRead => CapabilitySet::AUDIT_READ,
Self::PerformanceMonitoring => CapabilitySet::PERFMON,
Self::BerkeleyPacketFilters => CapabilitySet::BPF,
Self::CheckpointRestore => CapabilitySet::CHECKPOINT_RESTORE,
}
}
}
impl CompatCapability for CapabilitySet {
fn as_capability_set(self, _: private::Token) -> CapabilitySet {
self
}
}

/// Check if the specified capability is in the calling thread's capability
/// bounding set.
///
Expand All @@ -391,8 +463,14 @@ pub enum Capability {
///
/// [`prctl(PR_CAPBSET_READ,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html
#[inline]
pub fn capability_is_in_bounding_set(capability: Capability) -> io::Result<bool> {
unsafe { prctl_2args(PR_CAPBSET_READ, capability as usize as *mut _) }.map(|r| r != 0)
pub fn capability_is_in_bounding_set(capability: impl CompatCapability) -> io::Result<bool> {
unsafe {
prctl_2args(
PR_CAPBSET_READ,
capability.as_capability_set(private::Token).bits() as usize as *mut _,
)
}
.map(|r| r != 0)
}

const PR_CAPBSET_DROP: c_int = 24;
Expand All @@ -406,8 +484,14 @@ const PR_CAPBSET_DROP: c_int = 24;
///
/// [`prctl(PR_CAPBSET_DROP,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html
#[inline]
pub fn remove_capability_from_bounding_set(capability: Capability) -> io::Result<()> {
unsafe { prctl_2args(PR_CAPBSET_DROP, capability as usize as *mut _) }.map(|_r| ())
pub fn remove_capability_from_bounding_set(capability: impl CompatCapability) -> io::Result<()> {
unsafe {
prctl_2args(
PR_CAPBSET_DROP,
capability.as_capability_set(private::Token).bits() as usize as *mut _,
)
}
.map(|_r| ())
}

//
Expand Down Expand Up @@ -608,8 +692,8 @@ const PR_CAP_AMBIENT_IS_SET: usize = 1;
///
/// [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_IS_SET,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html
#[inline]
pub fn capability_is_in_ambient_set(capability: Capability) -> io::Result<bool> {
let cap = capability as usize as *mut _;
pub fn capability_is_in_ambient_set(capability: impl CompatCapability) -> io::Result<bool> {
let cap = capability.as_capability_set(private::Token).bits() as usize as *mut _;
unsafe { prctl_3args(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET as *mut _, cap) }.map(|r| r != 0)
}

Expand All @@ -636,13 +720,16 @@ const PR_CAP_AMBIENT_LOWER: usize = 3;
///
/// [`prctl(PR_CAP_AMBIENT,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html
#[inline]
pub fn configure_capability_in_ambient_set(capability: Capability, enable: bool) -> io::Result<()> {
pub fn configure_capability_in_ambient_set(
capability: impl CompatCapability,
enable: bool,
) -> io::Result<()> {
let sub_operation = if enable {
PR_CAP_AMBIENT_RAISE
} else {
PR_CAP_AMBIENT_LOWER
};
let cap = capability as usize as *mut _;
let cap = capability.as_capability_set(private::Token).bits() as usize as *mut _;

unsafe { prctl_3args(PR_CAP_AMBIENT, sub_operation as *mut _, cap) }.map(|_r| ())
}
Expand Down
10 changes: 5 additions & 5 deletions tests/process/prctl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use {

use rustix::process::*;
#[cfg(feature = "thread")]
use rustix::thread::Capability;
use rustix::thread::CapabilitySet;

#[test]
fn test_parent_process_death_signal() {
Expand Down Expand Up @@ -87,7 +87,7 @@ fn test_speculative_feature_state() {
#[cfg(feature = "thread")]
#[test]
fn test_is_io_flusher() {
if !thread_has_capability(Capability::SystemResource).unwrap() {
if !thread_has_capability(CapabilitySet::SYS_RESOURCE).unwrap() {
eprintln!("test_is_io_flusher: Test skipped due to missing capability: CAP_SYS_RESOURCE.");
return;
}
Expand All @@ -99,7 +99,7 @@ fn test_is_io_flusher() {
#[cfg(feature = "system")]
#[test]
fn test_virtual_memory_map_config_struct_size() {
if !thread_has_capability(Capability::SystemResource).unwrap() {
if !thread_has_capability(CapabilitySet::SYS_RESOURCE).unwrap() {
eprintln!(
"test_virtual_memory_map_config_struct_size: Test skipped due to missing capability: \
CAP_SYS_RESOURCE."
Expand Down Expand Up @@ -129,7 +129,7 @@ fn test_floating_point_emulation_control() {
//

#[cfg(feature = "thread")]
pub(crate) fn thread_has_capability(capability: Capability) -> io::Result<bool> {
pub(crate) fn thread_has_capability(capability: CapabilitySet) -> io::Result<bool> {
const _LINUX_CAPABILITY_VERSION_3: u32 = 0x2008_0522;

#[repr(C)]
Expand Down Expand Up @@ -175,7 +175,7 @@ pub(crate) fn thread_has_capability(capability: Capability) -> io::Result<bool>
return Err(io::Error::last_os_error());
}

let cap_index = capability as u32;
let cap_index = capability.bits() as u32;
let (data_index, cap_index) = if cap_index < 32 {
(0, cap_index)
} else {
Expand Down
6 changes: 3 additions & 3 deletions tests/system/reboot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
fn test_reboot() {
use rustix::io::Errno;
use rustix::system::{self, RebootCommand};
use rustix::thread::{self, CapabilityFlags};
use rustix::thread::{self, CapabilitySet};

let mut capabilities = thread::capabilities(None).expect("Failed to get capabilities");

capabilities.effective.set(CapabilityFlags::SYS_BOOT, false);
capabilities.effective.set(CapabilitySet::SYS_BOOT, false);

thread::set_capabilities(None, capabilities).expect("Failed to set capabilities");

// The reboot syscall requires the `CapabilityFlags::SYS_BOOT` permission
// The reboot syscall requires the `CapabilitySet::SYS_BOOT` permission
// to be called, otherwise [`Errno::PERM`] is returned
assert_eq!(system::reboot(RebootCommand::Restart), Err(Errno::PERM));
}
14 changes: 12 additions & 2 deletions tests/thread/prctl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ fn test_name() {

#[test]
fn test_capability_is_in_bounding_set() {
dbg!(capability_is_in_bounding_set(Capability::ChangeOwnership).unwrap());
dbg!(capability_is_in_bounding_set(CapabilitySet::CHOWN).unwrap());
dbg!(capability_is_in_bounding_set(
#[allow(deprecated)]
Capability::ChangeOwnership
)
.unwrap());
}

#[test]
Expand All @@ -38,7 +43,12 @@ fn test_no_new_privs() {

#[test]
fn test_capability_is_in_ambient_set() {
dbg!(capability_is_in_ambient_set(Capability::ChangeOwnership).unwrap());
dbg!(capability_is_in_ambient_set(CapabilitySet::CHOWN).unwrap());
dbg!(capability_is_in_ambient_set(
#[allow(deprecated)]
Capability::ChangeOwnership
)
.unwrap());
}

#[cfg(target_arch = "aarch64")]
Expand Down
Loading