Support PR_SET/GET_SECUREBITS

This commit is contained in:
Chen Chengjun 2025-11-21 09:27:13 +00:00 committed by Tate, Hongliang Tian
parent 76c7752130
commit 74dd519a10
6 changed files with 246 additions and 16 deletions

View File

@ -1,10 +1,12 @@
// SPDX-License-Identifier: MPL-2.0
use core::sync::atomic::{AtomicBool, Ordering};
use core::sync::atomic::Ordering;
use ostd::sync::{PreemptDisabled, RwLockReadGuard, RwLockWriteGuard};
use super::{group::AtomicGid, user::AtomicUid, Gid, Uid};
use super::{
group::AtomicGid, secure_bits::AtomicSecureBits, user::AtomicUid, Gid, SecureBits, Uid,
};
use crate::{
prelude::*,
process::credentials::capabilities::{AtomicCapSet, CapSet},
@ -47,8 +49,8 @@ pub(super) struct Credentials_ {
/// Capability that we can actually use
effective_capset: AtomicCapSet,
/// Keep capabilities flag
keep_capabilities: AtomicBool,
/// Secure bits flags
securebits: AtomicSecureBits,
}
impl Credentials_ {
@ -70,7 +72,7 @@ impl Credentials_ {
inheritable_capset: AtomicCapSet::new(capset),
permitted_capset: AtomicCapSet::new(capset),
effective_capset: AtomicCapSet::new(capset),
keep_capabilities: AtomicBool::new(false),
securebits: AtomicSecureBits::new(SecureBits::new_empty()),
}
}
@ -97,7 +99,7 @@ impl Credentials_ {
}
pub(super) fn keep_capabilities(&self) -> bool {
self.keep_capabilities.load(Ordering::Relaxed)
self.securebits.load(Ordering::Relaxed).keep_capabilities()
}
pub(super) fn set_uid(&self, uid: Uid) {
@ -259,6 +261,13 @@ impl Credentials_ {
old_suid
};
// If the `SECBIT_NO_SETUID_FIXUP` bit is set, do not adjust capabilities.
// Reference: The "SECBIT_NO_SETUID_FIXUP" section in
// https://man7.org/linux/man-pages/man7/capabilities.7.html
if self.securebits().no_setuid_fixup() {
return;
}
// Begin to adjust capabilities.
// Reference: The "Effect of user ID changes on capabilities" section in
// https://man7.org/linux/man-pages/man7/capabilities.7.html
@ -397,9 +406,15 @@ impl Credentials_ {
self.sgid.store(sgid, Ordering::Relaxed);
}
pub(super) fn set_keep_capabilities(&self, keep_capabilities: bool) {
self.keep_capabilities
.store(keep_capabilities, Ordering::Relaxed);
pub(super) fn set_keep_capabilities(&self, keep_capabilities: bool) -> Result<()> {
let current_bits = self.securebits();
let stored_bits = if !keep_capabilities {
current_bits - SecureBits::KEEP_CAPS
} else {
current_bits | SecureBits::KEEP_CAPS
};
self.securebits.try_store(stored_bits, Ordering::Relaxed)
}
// For `setregid`, rgid can *NOT* be set to old sgid,
@ -503,6 +518,23 @@ impl Credentials_ {
self.effective_capset
.store(effective_capset, Ordering::Relaxed);
}
// ******* SecureBits methods *******
pub(super) fn securebits(&self) -> SecureBits {
self.securebits.load(Ordering::Relaxed)
}
pub(super) fn set_securebits(&self, securebits: SecureBits) -> Result<()> {
if !self.effective_capset().contains(CapSet::SETPCAP) {
return_errno_with_message!(
Errno::EPERM,
"only threads with CAP_SETPCAP can change securebits"
);
}
self.securebits.try_store(securebits, Ordering::Relaxed)
}
}
impl Clone for Credentials_ {
@ -520,7 +552,7 @@ impl Clone for Credentials_ {
inheritable_capset: self.inheritable_capset.clone(),
permitted_capset: self.permitted_capset.clone(),
effective_capset: self.effective_capset.clone(),
keep_capabilities: AtomicBool::new(self.keep_capabilities.load(Ordering::Relaxed)),
securebits: self.securebits.clone(),
}
}
}

View File

@ -4,12 +4,14 @@ pub mod c_types;
pub mod capabilities;
mod credentials_;
mod group;
mod secure_bits;
mod static_cap;
mod user;
use aster_rights::FullOp;
use credentials_::Credentials_;
pub use group::Gid;
pub use secure_bits::SecureBits;
pub use user::Uid;
use crate::prelude::*;

View File

@ -0,0 +1,154 @@
// SPDX-License-Identifier: MPL-2.0
use core::sync::atomic::{AtomicU16, Ordering};
use atomic_integer_wrapper::define_atomic_version_of_integer_like_type;
use bitflags::bitflags;
use crate::prelude::*;
bitflags! {
/// Represents the secure bits flags for a POSIX thread.
///
/// These flags control the behavior of capabilities when changing UIDs.
/// Reference: <https://man7.org/linux/man-pages/man7/capabilities.7.html>
pub struct SecureBits: u16 {
/// If set, the kernel does not grant capabilities when a set-user-ID-root program
/// is executed, or when a process with an effective or real UID of 0 calls `execve`.
const NOROOT = 1 << 0;
/// Make `NOROOT` bit immutable (irreversible).
const NOROOT_LOCKED = 1 << 1;
/// If set, the kernel does not adjust the process's permitted, effective, and
/// ambient capability sets when the UIDs are switched between zero and nonzero values.
const NO_SETUID_FIXUP = 1 << 2;
/// Make `NO_SETUID_FIXUP` bit immutable (irreversible).
const NO_SETUID_FIXUP_LOCKED = 1 << 3;
/// If set, the kernel preserves permitted capabilities across UID changes,
/// specifically when all UIDs transition from root (0) to non-root values.
const KEEP_CAPS = 1 << 4;
/// Make `KEEP_CAPS` bit immutable (irreversible).
const KEEP_CAPS_LOCKED = 1 << 5;
/// If set, the kernel will not permit raising ambient capabilities via the
/// prctl PR_CAP_AMBIENT_RAISE operation.
const NO_CAP_AMBIENT_RAISE = 1 << 6;
/// Make `NO_CAP_AMBIENT_RAISE` bit immutable (irreversible).
const NO_CAP_AMBIENT_RAISE_LOCKED = 1 << 7;
}
}
impl SecureBits {
/// Mask of all lock bits.
const LOCK_MASK: u16 = 0b10101010;
/// Mask of all valid bits.
const ALL_VALID_BITS: u16 = Self::LOCK_MASK | (Self::LOCK_MASK >> 1);
/// Creates a new `SecureBits` with default (empty) settings.
pub(super) const fn new_empty() -> Self {
SecureBits::empty()
}
pub(super) const fn locked_bits(&self) -> SecureBits {
Self::from_bits_truncate((self.bits & Self::LOCK_MASK) >> 1)
}
pub(super) fn keep_capabilities(&self) -> bool {
self.contains(SecureBits::KEEP_CAPS)
}
pub(super) fn no_setuid_fixup(&self) -> bool {
self.contains(SecureBits::NO_SETUID_FIXUP)
}
}
impl TryFrom<u16> for SecureBits {
type Error = Error;
fn try_from(value: u16) -> Result<Self> {
if value & !SecureBits::ALL_VALID_BITS != 0 {
return_errno_with_message!(Errno::EINVAL, "invalid SecureBits value");
}
#[cfg(debug_assertions)]
{
// Warn about unsupported bits in debug builds.
const DUMMY_IMPL_BITS: u16 =
SecureBits::NOROOT.bits() | SecureBits::NO_CAP_AMBIENT_RAISE.bits();
let dummy_bits = value & DUMMY_IMPL_BITS;
if dummy_bits != 0 {
warn!(
"Some SecureBits flags are unsupported currently: {:?}.",
SecureBits::from_bits_truncate(dummy_bits)
);
}
}
Ok(SecureBits { bits: value })
}
}
impl From<SecureBits> for u16 {
fn from(value: SecureBits) -> Self {
value.bits()
}
}
define_atomic_version_of_integer_like_type!(SecureBits, try_from = true, {
#[derive(Debug)]
struct AtomicSecureBitsInner(AtomicU16);
});
impl Clone for AtomicSecureBitsInner {
fn clone(&self) -> Self {
Self::new(self.load(Ordering::Relaxed))
}
}
/// An atomic wrapper around `SecureBits`.
#[derive(Debug, Clone)]
pub(super) struct AtomicSecureBits {
inner: AtomicSecureBitsInner,
}
impl AtomicSecureBits {
/// Creates a new `AtomicSecureBits`.
pub(super) fn new(bits: SecureBits) -> Self {
Self {
inner: AtomicSecureBitsInner::new(bits),
}
}
/// Loads the current `SecureBits` atomically.
pub(super) fn load(&self, ordering: Ordering) -> SecureBits {
self.inner.load(ordering)
}
/// Attempts to store `SecureBits` atomically.
///
/// Returning an error if one of the bits is locked.
pub(super) fn try_store(&self, bits: SecureBits, ordering: Ordering) -> Result<()> {
// A thread can only modify its own secure bits, so there are no
// race conditions and synchronization concerns.
let current = self.inner.load(Ordering::Relaxed);
let locked_bits = current.locked_bits();
if locked_bits & current != locked_bits & bits {
return_errno_with_message!(Errno::EPERM, "one or more SecureBits are locked");
}
if SecureBits::LOCK_MASK & current.bits() & !bits.bits() != 0 {
return_errno_with_message!(Errno::EPERM, "cannot unlock the lock bits");
}
self.inner.store(bits, ordering);
Ok(())
}
}

View File

@ -4,7 +4,7 @@ use aster_rights::{Dup, Read, TRights, Write};
use aster_rights_proc::require;
use ostd::sync::{PreemptDisabled, RwLockReadGuard, RwLockWriteGuard};
use super::{capabilities::CapSet, credentials_::Credentials_, Credentials, Gid, Uid};
use super::{capabilities::CapSet, credentials_::Credentials_, Credentials, Gid, SecureBits, Uid};
use crate::prelude::*;
impl<R: TRights> Credentials<R> {
@ -147,10 +147,12 @@ impl<R: TRights> Credentials<R> {
/// Sets keep capabilities flag.
///
/// If the [`SecureBits::KEEP_CAPS_LOCKED`] is set, this method will return an error.
///
/// This method requires the `Write` right.
#[require(R > Write)]
pub fn set_keep_capabilities(&self, keep_capabilities: bool) {
self.0.set_keep_capabilities(keep_capabilities);
pub fn set_keep_capabilities(&self, keep_capabilities: bool) -> Result<()> {
self.0.set_keep_capabilities(keep_capabilities)
}
// *********** Gid methods **********
@ -247,6 +249,27 @@ impl<R: TRights> Credentials<R> {
self.0.set_sgid(egid);
}
// *********** SecureBits methods **********
/// Gets the secure bits.
///
/// This method requires the `Read` right.
#[require(R > Read)]
pub fn securebits(&self) -> SecureBits {
self.0.securebits()
}
/// Sets the secure bits.
///
/// If the caller does not have the `CAP_SETPCAP` capability, or if it tries to set
/// locked bits, this method will return an error.
///
/// This method requires the `Write` right.
#[require(R > Write)]
pub fn set_securebits(&self, securebits: SecureBits) -> Result<()> {
self.0.set_securebits(securebits)
}
// *********** Supplementary group methods **********
/// Acquires the read lock of supplementary group ids.

View File

@ -243,7 +243,7 @@ fn apply_caps_from_exec(
let credentials = posix_thread.credentials_mut();
set_uid_from_elf(process, &credentials, elf_file)?;
set_gid_from_elf(process, &credentials, elf_file)?;
credentials.set_keep_capabilities(false);
credentials.set_keep_capabilities(false)?;
Ok(())
}

View File

@ -3,7 +3,9 @@
use super::SyscallReturn;
use crate::{
prelude::*,
process::{posix_thread::MAX_THREAD_NAME_LEN, signal::sig_num::SigNum},
process::{
credentials::SecureBits, posix_thread::MAX_THREAD_NAME_LEN, signal::sig_num::SigNum,
},
};
pub fn sys_prctl(
@ -58,7 +60,7 @@ pub fn sys_prctl(
return_errno!(Errno::EINVAL)
}
let credentials = ctx.posix_thread.credentials_mut();
credentials.set_keep_capabilities(keep_cap != 0);
credentials.set_keep_capabilities(keep_cap != 0)?;
}
PrctlCmd::PR_GET_NAME(write_to_addr) => {
let thread_name = ctx.posix_thread.thread_name().lock();
@ -87,6 +89,15 @@ pub fn sys_prctl(
ctx.user_space()
.write_val(write_addr, &(process.is_child_subreaper() as u32))?;
}
PrctlCmd::PR_GET_SECUREBITS => {
let credentials = ctx.posix_thread.credentials();
let securebits = credentials.securebits();
return Ok(SyscallReturn::Return(securebits.bits() as _));
}
PrctlCmd::PR_SET_SECUREBITS(securebits) => {
let credentials = ctx.posix_thread.credentials_mut();
credentials.set_securebits(securebits)?;
}
_ => todo!(),
}
Ok(SyscallReturn::Return(0))
@ -100,6 +111,8 @@ const PR_GET_KEEPCAPS: i32 = 7;
const PR_SET_KEEPCAPS: i32 = 8;
const PR_SET_NAME: i32 = 15;
const PR_GET_NAME: i32 = 16;
const PR_GET_SECUREBITS: i32 = 27;
const PR_SET_SECUREBITS: i32 = 28;
const PR_SET_TIMERSLACK: i32 = 29;
const PR_GET_TIMERSLACK: i32 = 30;
const PR_SET_CHILD_SUBREAPER: i32 = 36;
@ -122,6 +135,8 @@ pub enum PrctlCmd {
PR_GET_DUMPABLE,
PR_SET_CHILD_SUBREAPER(bool),
PR_GET_CHILD_SUBREAPER(Vaddr),
PR_GET_SECUREBITS,
PR_SET_SECUREBITS(SecureBits),
}
#[repr(u64)]
@ -150,6 +165,10 @@ impl PrctlCmd {
PR_SET_KEEPCAPS => Ok(PrctlCmd::PR_SET_KEEPCAPS(arg2 as _)),
PR_SET_CHILD_SUBREAPER => Ok(PrctlCmd::PR_SET_CHILD_SUBREAPER(arg2 > 0)),
PR_GET_CHILD_SUBREAPER => Ok(PrctlCmd::PR_GET_CHILD_SUBREAPER(arg2 as _)),
PR_GET_SECUREBITS => Ok(PrctlCmd::PR_GET_SECUREBITS),
PR_SET_SECUREBITS => Ok(PrctlCmd::PR_SET_SECUREBITS(SecureBits::try_from(
arg2 as u16,
)?)),
_ => {
debug!("prctl cmd number: {}", option);
return_errno_with_message!(Errno::EINVAL, "unsupported prctl command");