Support prctl(PR_SET_KEEPCAPS/PR_GET_KEEPCAPS)

This commit is contained in:
Fabing Li
2024-12-05 07:49:43 +00:00
committed by Tate, Hongliang Tian
parent d72ce0351a
commit 24f1e02b26
6 changed files with 73 additions and 2 deletions

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use core::sync::atomic::Ordering; use core::sync::atomic::{AtomicBool, Ordering};
use ostd::sync::{PreemptDisabled, RwLockReadGuard, RwLockWriteGuard}; use ostd::sync::{PreemptDisabled, RwLockReadGuard, RwLockWriteGuard};
@ -46,6 +46,9 @@ pub(super) struct Credentials_ {
/// Capability that we can actually use /// Capability that we can actually use
effective_capset: AtomicCapSet, effective_capset: AtomicCapSet,
/// Keep capabilities flag
keep_capabilities: AtomicBool,
} }
impl Credentials_ { impl Credentials_ {
@ -67,6 +70,7 @@ impl Credentials_ {
inheritable_capset: AtomicCapSet::new(capset), inheritable_capset: AtomicCapSet::new(capset),
permitted_capset: AtomicCapSet::new(capset), permitted_capset: AtomicCapSet::new(capset),
effective_capset: AtomicCapSet::new(capset), effective_capset: AtomicCapSet::new(capset),
keep_capabilities: AtomicBool::new(false),
} }
} }
@ -92,6 +96,10 @@ impl Credentials_ {
self.fsuid.load(Ordering::Relaxed) self.fsuid.load(Ordering::Relaxed)
} }
pub(super) fn keep_capabilities(&self) -> bool {
self.keep_capabilities.load(Ordering::Relaxed)
}
pub(super) fn set_uid(&self, uid: Uid) { pub(super) fn set_uid(&self, uid: Uid) {
if self.is_privileged() { if self.is_privileged() {
self.ruid.store(uid, Ordering::Relaxed); self.ruid.store(uid, Ordering::Relaxed);
@ -99,9 +107,23 @@ impl Credentials_ {
self.suid.store(uid, Ordering::Relaxed); self.suid.store(uid, Ordering::Relaxed);
self.fsuid.store(uid, Ordering::Relaxed); self.fsuid.store(uid, Ordering::Relaxed);
} else { } else {
// Unprivileged processes can only switch between ruid, euid, suid
if uid != self.ruid.load(Ordering::Relaxed)
&& uid != self.euid.load(Ordering::Relaxed)
&& uid != self.suid.load(Ordering::Relaxed)
{
// No permission to set to this UID
return;
}
self.euid.store(uid, Ordering::Relaxed); self.euid.store(uid, Ordering::Relaxed);
self.fsuid.store(uid, Ordering::Relaxed); self.fsuid.store(uid, Ordering::Relaxed);
} }
if !self.keep_capabilities.load(Ordering::Relaxed) {
self.set_permitted_capset(CapSet::empty());
self.set_inheritable_capset(CapSet::empty());
}
// Always clear the effective capabilities when changing the UID
self.set_effective_capset(CapSet::empty());
} }
pub(super) fn set_reuid(&self, ruid: Option<Uid>, euid: Option<Uid>) -> Result<()> { pub(super) fn set_reuid(&self, ruid: Option<Uid>, euid: Option<Uid>) -> Result<()> {
@ -326,6 +348,11 @@ impl Credentials_ {
self.sgid.store(sgid, Ordering::Relaxed); self.sgid.store(sgid, Ordering::Relaxed);
} }
pub(super) fn set_keep_capabilities(&self, keep_capabilities: bool) {
self.keep_capabilities
.store(keep_capabilities, Ordering::Relaxed);
}
// For `setregid`, rgid can *NOT* be set to old sgid, // For `setregid`, rgid can *NOT* be set to old sgid,
// while for `setresgid`, ruid can be set to old sgid. // while for `setresgid`, ruid can be set to old sgid.
fn check_gid_perm( fn check_gid_perm(
@ -444,6 +471,7 @@ impl Clone for Credentials_ {
inheritable_capset: self.inheritable_capset.clone(), inheritable_capset: self.inheritable_capset.clone(),
permitted_capset: self.permitted_capset.clone(), permitted_capset: self.permitted_capset.clone(),
effective_capset: self.effective_capset.clone(), effective_capset: self.effective_capset.clone(),
keep_capabilities: AtomicBool::new(self.keep_capabilities.load(Ordering::Relaxed)),
} }
} }
} }

View File

@ -79,6 +79,14 @@ impl<R: TRights> Credentials<R> {
self.0.fsuid() self.0.fsuid()
} }
/// Gets keep capabilities flag.
///
/// This method requires the `Read` right.
#[require(R > Read)]
pub fn keep_capabilities(&self) -> bool {
self.0.keep_capabilities()
}
/// Sets uid. If self is privileged, sets the effective, real, saved-set user ids as `uid`, /// Sets uid. If self is privileged, sets the effective, real, saved-set user ids as `uid`,
/// Otherwise, sets effective user id as `uid`. /// Otherwise, sets effective user id as `uid`.
/// ///
@ -139,6 +147,14 @@ impl<R: TRights> Credentials<R> {
self.0.set_suid(euid); self.0.set_suid(euid);
} }
/// Sets keep capabilities flag.
///
/// 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);
}
// *********** Gid methods ********** // *********** Gid methods **********
/// Gets real group id. /// Gets real group id.

View File

@ -129,6 +129,7 @@ fn do_execve(
let credentials = ctx.posix_thread.credentials_mut(); let credentials = ctx.posix_thread.credentials_mut();
set_uid_from_elf(process, &credentials, &elf_file)?; set_uid_from_elf(process, &credentials, &elf_file)?;
set_gid_from_elf(process, &credentials, &elf_file)?; set_gid_from_elf(process, &credentials, &elf_file)?;
credentials.set_keep_capabilities(false);
// set executable path // set executable path
process.set_executable_path(new_executable_path); process.set_executable_path(new_executable_path);

View File

@ -41,6 +41,25 @@ pub fn sys_prctl(
// TODO: implement coredump // TODO: implement coredump
} }
PrctlCmd::PR_GET_KEEPCAPS => {
let keep_cap = {
let credentials = ctx.posix_thread.credentials();
if credentials.keep_capabilities() {
1
} else {
0
}
};
return Ok(SyscallReturn::Return(keep_cap as _));
}
PrctlCmd::PR_SET_KEEPCAPS(keep_cap) => {
if keep_cap > 1 {
return_errno!(Errno::EINVAL)
}
let credentials = ctx.posix_thread.credentials_mut();
credentials.set_keep_capabilities(keep_cap != 0);
}
PrctlCmd::PR_GET_NAME(write_to_addr) => { PrctlCmd::PR_GET_NAME(write_to_addr) => {
let thread_name = ctx.posix_thread.thread_name().lock(); let thread_name = ctx.posix_thread.thread_name().lock();
if let Some(thread_name) = &*thread_name { if let Some(thread_name) = &*thread_name {
@ -70,6 +89,8 @@ const PR_SET_PDEATHSIG: i32 = 1;
const PR_GET_PDEATHSIG: i32 = 2; const PR_GET_PDEATHSIG: i32 = 2;
const PR_GET_DUMPABLE: i32 = 3; const PR_GET_DUMPABLE: i32 = 3;
const PR_SET_DUMPABLE: i32 = 4; const PR_SET_DUMPABLE: i32 = 4;
const PR_GET_KEEPCAPS: i32 = 7;
const PR_SET_KEEPCAPS: i32 = 8;
const PR_SET_NAME: i32 = 15; const PR_SET_NAME: i32 = 15;
const PR_GET_NAME: i32 = 16; const PR_GET_NAME: i32 = 16;
const PR_SET_TIMERSLACK: i32 = 29; const PR_SET_TIMERSLACK: i32 = 29;
@ -82,6 +103,8 @@ pub enum PrctlCmd {
PR_GET_PDEATHSIG(Vaddr), PR_GET_PDEATHSIG(Vaddr),
PR_SET_NAME(Vaddr), PR_SET_NAME(Vaddr),
PR_GET_NAME(Vaddr), PR_GET_NAME(Vaddr),
PR_GET_KEEPCAPS,
PR_SET_KEEPCAPS(u32),
#[allow(dead_code)] #[allow(dead_code)]
PR_SET_TIMERSLACK(u64), PR_SET_TIMERSLACK(u64),
#[allow(dead_code)] #[allow(dead_code)]
@ -112,6 +135,8 @@ impl PrctlCmd {
PR_GET_NAME => Ok(PrctlCmd::PR_GET_NAME(arg2 as _)), PR_GET_NAME => Ok(PrctlCmd::PR_GET_NAME(arg2 as _)),
PR_GET_TIMERSLACK => todo!(), PR_GET_TIMERSLACK => todo!(),
PR_SET_TIMERSLACK => todo!(), PR_SET_TIMERSLACK => todo!(),
PR_GET_KEEPCAPS => Ok(PrctlCmd::PR_GET_KEEPCAPS),
PR_SET_KEEPCAPS => Ok(PrctlCmd::PR_SET_KEEPCAPS(arg2 as _)),
_ => { _ => {
debug!("prctl cmd number: {}", option); debug!("prctl cmd number: {}", option);
return_errno_with_message!(Errno::EINVAL, "unsupported prctl command"); return_errno_with_message!(Errno::EINVAL, "unsupported prctl command");

View File

@ -10,7 +10,6 @@ TESTS ?= \
alarm_test \ alarm_test \
chmod_test \ chmod_test \
chown_test \ chown_test \
chroot_test \
creat_test \ creat_test \
dup_test \ dup_test \
epoll_test \ epoll_test \
@ -29,6 +28,7 @@ TESTS ?= \
mount_test \ mount_test \
open_create_test \ open_create_test \
open_test \ open_test \
prctl_setuid_test \
pread64_test \ pread64_test \
preadv2_test \ preadv2_test \
proc_test \ proc_test \

View File

@ -0,0 +1 @@
PrctlKeepCapsSetuidTest.NoKeepCapsAfterNewUserNamespace