diff --git a/src/thread/libcap.rs b/src/thread/libcap.rs index 0a0fbb4ca..af5ab58a8 100644 --- a/src/thread/libcap.rs +++ b/src/thread/libcap.rs @@ -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` @@ -156,9 +160,9 @@ fn capget(pid: Option) -> io::Result { // 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), }) } diff --git a/src/thread/mod.rs b/src/thread/mod.rs index e32b5945c..26d1de427 100644 --- a/src/thread/mod.rs +++ b/src/thread/mod.rs @@ -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)] diff --git a/src/thread/prctl.rs b/src/thread/prctl.rs index 4719b1eb4..2f439590e 100644 --- a/src/thread/prctl.rs +++ b/src/thread/prctl.rs @@ -27,6 +27,8 @@ use crate::prctl::{ }; use crate::utils::as_ptr; +use super::CapabilitySet; + // // PR_GET_KEEPCAPS/PR_SET_KEEPCAPS // @@ -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] @@ -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. /// @@ -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 { - 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 { + 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; @@ -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| ()) } // @@ -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 { - let cap = capability as usize as *mut _; +pub fn capability_is_in_ambient_set(capability: impl CompatCapability) -> io::Result { + 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) } @@ -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| ()) } diff --git a/tests/process/prctl.rs b/tests/process/prctl.rs index 248fbc6a6..25e4828ce 100644 --- a/tests/process/prctl.rs +++ b/tests/process/prctl.rs @@ -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() { @@ -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; } @@ -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." @@ -129,7 +129,7 @@ fn test_floating_point_emulation_control() { // #[cfg(feature = "thread")] -pub(crate) fn thread_has_capability(capability: Capability) -> io::Result { +pub(crate) fn thread_has_capability(capability: CapabilitySet) -> io::Result { const _LINUX_CAPABILITY_VERSION_3: u32 = 0x2008_0522; #[repr(C)] @@ -175,7 +175,7 @@ pub(crate) fn thread_has_capability(capability: Capability) -> io::Result 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 { diff --git a/tests/system/reboot.rs b/tests/system/reboot.rs index 2bc146c57..7369d757a 100644 --- a/tests/system/reboot.rs +++ b/tests/system/reboot.rs @@ -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)); } diff --git a/tests/thread/prctl.rs b/tests/thread/prctl.rs index 358ae5208..d9a98383b 100644 --- a/tests/thread/prctl.rs +++ b/tests/thread/prctl.rs @@ -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] @@ -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")]