From 24f1e02b26c5bd6f0f2b352b796ad6a30c05b52a Mon Sep 17 00:00:00 2001 From: Fabing Li Date: Thu, 5 Dec 2024 07:49:43 +0000 Subject: [PATCH] Support prctl(PR_SET_KEEPCAPS/PR_GET_KEEPCAPS) --- .../src/process/credentials/credentials_.rs | 30 ++++++++++++++++++- kernel/src/process/credentials/static_cap.rs | 16 ++++++++++ kernel/src/syscall/execve.rs | 1 + kernel/src/syscall/prctl.rs | 25 ++++++++++++++++ test/syscall_test/Makefile | 2 +- .../syscall_test/blocklists/prctl_setuid_test | 1 + 6 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 test/syscall_test/blocklists/prctl_setuid_test diff --git a/kernel/src/process/credentials/credentials_.rs b/kernel/src/process/credentials/credentials_.rs index 5583c15c7..fddc6caa5 100644 --- a/kernel/src/process/credentials/credentials_.rs +++ b/kernel/src/process/credentials/credentials_.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 -use core::sync::atomic::Ordering; +use core::sync::atomic::{AtomicBool, Ordering}; use ostd::sync::{PreemptDisabled, RwLockReadGuard, RwLockWriteGuard}; @@ -46,6 +46,9 @@ pub(super) struct Credentials_ { /// Capability that we can actually use effective_capset: AtomicCapSet, + + /// Keep capabilities flag + keep_capabilities: AtomicBool, } impl Credentials_ { @@ -67,6 +70,7 @@ impl Credentials_ { inheritable_capset: AtomicCapSet::new(capset), permitted_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) } + pub(super) fn keep_capabilities(&self) -> bool { + self.keep_capabilities.load(Ordering::Relaxed) + } + pub(super) fn set_uid(&self, uid: Uid) { if self.is_privileged() { self.ruid.store(uid, Ordering::Relaxed); @@ -99,9 +107,23 @@ impl Credentials_ { self.suid.store(uid, Ordering::Relaxed); self.fsuid.store(uid, Ordering::Relaxed); } 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.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, euid: Option) -> Result<()> { @@ -326,6 +348,11 @@ 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); + } + // For `setregid`, rgid can *NOT* be set to old sgid, // while for `setresgid`, ruid can be set to old sgid. fn check_gid_perm( @@ -444,6 +471,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)), } } } diff --git a/kernel/src/process/credentials/static_cap.rs b/kernel/src/process/credentials/static_cap.rs index 5ddbc5441..f6a6b63b8 100644 --- a/kernel/src/process/credentials/static_cap.rs +++ b/kernel/src/process/credentials/static_cap.rs @@ -79,6 +79,14 @@ impl Credentials { 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`, /// Otherwise, sets effective user id as `uid`. /// @@ -139,6 +147,14 @@ impl Credentials { 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 ********** /// Gets real group id. diff --git a/kernel/src/syscall/execve.rs b/kernel/src/syscall/execve.rs index f6367d378..0b56e5942 100644 --- a/kernel/src/syscall/execve.rs +++ b/kernel/src/syscall/execve.rs @@ -129,6 +129,7 @@ fn do_execve( let credentials = ctx.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); // set executable path process.set_executable_path(new_executable_path); diff --git a/kernel/src/syscall/prctl.rs b/kernel/src/syscall/prctl.rs index 5bf3389ae..ac6667fb5 100644 --- a/kernel/src/syscall/prctl.rs +++ b/kernel/src/syscall/prctl.rs @@ -41,6 +41,25 @@ pub fn sys_prctl( // 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) => { let thread_name = ctx.posix_thread.thread_name().lock(); 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_DUMPABLE: i32 = 3; 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_GET_NAME: i32 = 16; const PR_SET_TIMERSLACK: i32 = 29; @@ -82,6 +103,8 @@ pub enum PrctlCmd { PR_GET_PDEATHSIG(Vaddr), PR_SET_NAME(Vaddr), PR_GET_NAME(Vaddr), + PR_GET_KEEPCAPS, + PR_SET_KEEPCAPS(u32), #[allow(dead_code)] PR_SET_TIMERSLACK(u64), #[allow(dead_code)] @@ -112,6 +135,8 @@ impl PrctlCmd { PR_GET_NAME => Ok(PrctlCmd::PR_GET_NAME(arg2 as _)), PR_GET_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); return_errno_with_message!(Errno::EINVAL, "unsupported prctl command"); diff --git a/test/syscall_test/Makefile b/test/syscall_test/Makefile index 5675f413c..24193acbb 100644 --- a/test/syscall_test/Makefile +++ b/test/syscall_test/Makefile @@ -10,7 +10,6 @@ TESTS ?= \ alarm_test \ chmod_test \ chown_test \ - chroot_test \ creat_test \ dup_test \ epoll_test \ @@ -29,6 +28,7 @@ TESTS ?= \ mount_test \ open_create_test \ open_test \ + prctl_setuid_test \ pread64_test \ preadv2_test \ proc_test \ diff --git a/test/syscall_test/blocklists/prctl_setuid_test b/test/syscall_test/blocklists/prctl_setuid_test new file mode 100644 index 000000000..2d6d33b88 --- /dev/null +++ b/test/syscall_test/blocklists/prctl_setuid_test @@ -0,0 +1 @@ +PrctlKeepCapsSetuidTest.NoKeepCapsAfterNewUserNamespace \ No newline at end of file