Support PR_SET/GET_SECUREBITS
This commit is contained in:
parent
76c7752130
commit
74dd519a10
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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::*;
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
Loading…
Reference in New Issue