Add syscall capget/capset

This commit is contained in:
StanPlatinum 2024-05-06 13:35:13 +08:00 committed by Tate, Hongliang Tian
parent 8e1aeaf578
commit 6e612fc730
15 changed files with 413 additions and 20 deletions

View File

@ -145,8 +145,8 @@ provided by Linux on x86-64 architecture.
| 122 | setfsuid | ✅ | | 122 | setfsuid | ✅ |
| 123 | setfsgid | ✅ | | 123 | setfsgid | ✅ |
| 124 | getsid | ✅ | | 124 | getsid | ✅ |
| 125 | capget | | | 125 | capget | |
| 126 | capset | | | 126 | capset | |
| 127 | rt_sigpending | ✅ | | 127 | rt_sigpending | ✅ |
| 128 | rt_sigtimedwait | ❌ | | 128 | rt_sigtimedwait | ❌ |
| 129 | rt_sigqueueinfo | ❌ | | 129 | rt_sigqueueinfo | ❌ |

View File

@ -0,0 +1,21 @@
// SPDX-License-Identifier: MPL-2.0
#![allow(non_camel_case_types)]
use crate::{prelude::*, process::Pid};
#[derive(Debug, Clone, Copy, Pod)]
#[repr(C)]
pub struct cap_user_header_t {
pub version: u32,
pub pid: Pid,
}
#[derive(Debug, Clone, Copy, Pod)]
#[repr(C)]
pub struct cap_user_data_t {
pub effective: u32,
pub permitted: u32,
pub inheritable: u32,
}
pub const LINUX_CAPABILITY_VERSION_3: u32 = 0x20080522;

View File

@ -0,0 +1,88 @@
// SPDX-License-Identifier: MPL-2.0
use core::sync::atomic::{AtomicU64, Ordering};
use bitflags::bitflags;
bitflags! {
/// Represents a set of Linux capabilities.
pub struct CapSet: u64 {
const CHOWN = 1 << 0;
const DAC_OVERRIDE = 1 << 1;
const DAC_READ_SEARCH = 1 << 2;
const FOWNER = 1 << 3;
const FSETID = 1 << 4;
const KILL = 1 << 5;
const SETGID = 1 << 6;
const SETUID = 1 << 7;
const SETPCAP = 1 << 8;
const LINUX_IMMUTABLE = 1 << 9;
const NET_BIND_SERVICE = 1 << 10;
const NET_BROADCAST = 1 << 11;
const NET_ADMIN = 1 << 12;
const NET_RAW = 1 << 13;
const IPC_LOCK = 1 << 14;
const IPC_OWNER = 1 << 15;
const SYS_MODULE = 1 << 16;
const SYS_RAWIO = 1 << 17;
const SYS_CHROOT = 1 << 18;
const SYS_PTRACE = 1 << 19;
const SYS_PACCT = 1 << 20;
const SYS_ADMIN = 1 << 21;
const SYS_BOOT = 1 << 22;
const SYS_NICE = 1 << 23;
const SYS_RESOURCE = 1 << 24;
const SYS_TIME = 1 << 25;
const SYS_TTY_CONFIG = 1 << 26;
const MKNOD = 1 << 27;
const LEASE = 1 << 28;
const AUDIT_WRITE = 1 << 29;
const AUDIT_CONTROL = 1 << 30;
const SETFCAP = 1 << 31;
const MAC_OVERRIDE = 1 << 32;
const MAC_ADMIN = 1 << 33;
const SYSLOG = 1 << 34;
const WAKE_ALARM = 1 << 35;
const BLOCK_SUSPEND = 1 << 36;
const AUDIT_READ = 1 << 37;
const PERFMON = 1 << 38;
const BPF = 1 << 39;
const CHECKPOINT_RESTORE = 1u64 << 40;
// ... include other capabilities as needed
}
}
impl CapSet {
/// Converts the capability set to a `u32`. The higher bits are truncated.
pub fn as_u32(&self) -> u32 {
self.bits() as u32
}
/// Creates a new `CapSet` with the `SYS_ADMIN` capability set, typically for a root user.
pub const fn new_root() -> Self {
CapSet::SYS_ADMIN
}
}
#[derive(Debug)]
pub(super) struct AtomicCapSet(AtomicU64);
impl AtomicCapSet {
pub const fn new(capset: CapSet) -> Self {
Self(AtomicU64::new(capset.bits))
}
pub fn set(&self, capset: CapSet) {
self.0.store(capset.bits(), Ordering::Relaxed);
}
pub fn get(&self) -> CapSet {
CapSet::from_bits_truncate(self.0.load(Ordering::Relaxed))
}
}
impl Clone for AtomicCapSet {
fn clone(&self) -> Self {
Self::new(self.get())
}
}

View File

@ -3,7 +3,10 @@
use aster_frame::sync::{RwLockReadGuard, RwLockWriteGuard}; use aster_frame::sync::{RwLockReadGuard, RwLockWriteGuard};
use super::{group::AtomicGid, user::AtomicUid, Gid, Uid}; use super::{group::AtomicGid, user::AtomicUid, Gid, Uid};
use crate::prelude::*; use crate::{
prelude::*,
process::credentials::capabilities::{AtomicCapSet, CapSet},
};
#[derive(Debug)] #[derive(Debug)]
pub(super) struct Credentials_ { pub(super) struct Credentials_ {
@ -25,13 +28,27 @@ pub(super) struct Credentials_ {
/// Group id used for file system checks. /// Group id used for file system checks.
fsgid: AtomicGid, fsgid: AtomicGid,
// A set of additional groups to which a process belongs. /// A set of additional groups to which a process belongs.
supplementary_gids: RwLock<BTreeSet<Gid>>, supplementary_gids: RwLock<BTreeSet<Gid>>,
/// The Linux capabilities.
/// This is not the capability (in static_cap.rs) enforced on rust objects.
/// Capability that child processes can inherit
inheritable_capset: AtomicCapSet,
/// Capabilities that a process can potentially be granted.
/// It defines the maximum set of privileges that the process could possibly have.
/// Even if the process is not currently using these privileges, it has the potential ability to enable them.
permitted_capset: AtomicCapSet,
/// Capability that we can actually use
effective_capset: AtomicCapSet,
} }
impl Credentials_ { impl Credentials_ {
/// Create a new credentials. ruid, euid, suid will be set as the same uid, and gid is the same. /// Create a new credentials. ruid, euid, suid will be set as the same uid, and gid is the same.
pub fn new(uid: Uid, gid: Gid) -> Self { pub fn new(uid: Uid, gid: Gid, capset: CapSet) -> Self {
let mut supplementary_gids = BTreeSet::new(); let mut supplementary_gids = BTreeSet::new();
supplementary_gids.insert(gid); supplementary_gids.insert(gid);
@ -45,6 +62,9 @@ impl Credentials_ {
sgid: AtomicGid::new(gid), sgid: AtomicGid::new(gid),
fsgid: AtomicGid::new(gid), fsgid: AtomicGid::new(gid),
supplementary_gids: RwLock::new(supplementary_gids), supplementary_gids: RwLock::new(supplementary_gids),
inheritable_capset: AtomicCapSet::new(capset),
permitted_capset: AtomicCapSet::new(capset),
effective_capset: AtomicCapSet::new(capset),
} }
} }
@ -364,6 +384,7 @@ impl Credentials_ {
} }
// ******* Supplementary groups methods ******* // ******* Supplementary groups methods *******
pub(super) fn groups(&self) -> RwLockReadGuard<BTreeSet<Gid>> { pub(super) fn groups(&self) -> RwLockReadGuard<BTreeSet<Gid>> {
self.supplementary_gids.read() self.supplementary_gids.read()
} }
@ -371,6 +392,32 @@ impl Credentials_ {
pub(super) fn groups_mut(&self) -> RwLockWriteGuard<BTreeSet<Gid>> { pub(super) fn groups_mut(&self) -> RwLockWriteGuard<BTreeSet<Gid>> {
self.supplementary_gids.write() self.supplementary_gids.write()
} }
// ******* Linux Capability methods *******
pub(super) fn inheritable_capset(&self) -> CapSet {
self.inheritable_capset.get()
}
pub(super) fn permitted_capset(&self) -> CapSet {
self.permitted_capset.get()
}
pub(super) fn effective_capset(&self) -> CapSet {
self.effective_capset.get()
}
pub(super) fn set_inheritable_capset(&self, inheritable_capset: CapSet) {
self.inheritable_capset.set(inheritable_capset);
}
pub(super) fn set_permitted_capset(&self, permitted_capset: CapSet) {
self.permitted_capset.set(permitted_capset);
}
pub(super) fn set_effective_capset(&self, effective_capset: CapSet) {
self.effective_capset.set(effective_capset);
}
} }
impl Clone for Credentials_ { impl Clone for Credentials_ {
@ -385,6 +432,9 @@ impl Clone for Credentials_ {
sgid: self.sgid.clone(), sgid: self.sgid.clone(),
fsgid: self.fsgid.clone(), fsgid: self.fsgid.clone(),
supplementary_gids: RwLock::new(self.supplementary_gids.read().clone()), supplementary_gids: RwLock::new(self.supplementary_gids.read().clone()),
inheritable_capset: self.inheritable_capset.clone(),
permitted_capset: self.permitted_capset.clone(),
effective_capset: self.effective_capset.clone(),
} }
} }
} }

View File

@ -1,5 +1,7 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
pub mod c_types;
pub mod capabilities;
mod credentials_; mod credentials_;
mod group; mod group;
mod static_cap; mod static_cap;
@ -20,7 +22,8 @@ use crate::prelude::*;
/// - effective user ID and group ID; /// - effective user ID and group ID;
/// - saved-set user ID and saved-set group ID; /// - saved-set user ID and saved-set group ID;
/// - file system user ID and group ID (Linux-specific); /// - file system user ID and group ID (Linux-specific);
/// - supplementary group IDs. /// - supplementary group IDs;
/// - Linux capabilities.
pub struct Credentials<R = FullOp>(Arc<Credentials_>, R); pub struct Credentials<R = FullOp>(Arc<Credentials_>, R);
/// Gets read-only credentials of current thread. /// Gets read-only credentials of current thread.

View File

@ -6,7 +6,7 @@ use aster_frame::sync::{RwLockReadGuard, RwLockWriteGuard};
use aster_rights::{Dup, Read, TRights, Write}; use aster_rights::{Dup, Read, TRights, Write};
use aster_rights_proc::require; use aster_rights_proc::require;
use super::{credentials_::Credentials_, Credentials, Gid, Uid}; use super::{capabilities::CapSet, credentials_::Credentials_, Credentials, Gid, Uid};
use crate::prelude::*; use crate::prelude::*;
impl<R: TRights> Credentials<R> { impl<R: TRights> Credentials<R> {
@ -14,7 +14,8 @@ impl<R: TRights> Credentials<R> {
pub fn new_root() -> Self { pub fn new_root() -> Self {
let uid = Uid::new_root(); let uid = Uid::new_root();
let gid = Gid::new_root(); let gid = Gid::new_root();
let credentials_ = Arc::new(Credentials_::new(uid, gid)); let cap = CapSet::new_root();
let credentials_ = Arc::new(Credentials_::new(uid, gid, cap));
Self(credentials_, R::new()) Self(credentials_, R::new())
} }
@ -249,4 +250,54 @@ impl<R: TRights> Credentials<R> {
pub fn groups_mut(&self) -> RwLockWriteGuard<BTreeSet<Gid>> { pub fn groups_mut(&self) -> RwLockWriteGuard<BTreeSet<Gid>> {
self.0.groups_mut() self.0.groups_mut()
} }
// *********** Linux Capability methods **********
/// Gets the capabilities that child process can inherit.
///
/// This method requies the `Read` right.
#[require(R > Read)]
pub fn inheritable_capset(&self) -> CapSet {
self.0.inheritable_capset()
}
/// Gets the capabilities that are permitted.
///
/// This method requies the `Read` right.
#[require(R > Read)]
pub fn permitted_capset(&self) -> CapSet {
self.0.permitted_capset()
}
/// Gets the capabilities that actually use.
///
/// This method requies the `Read` right.
#[require(R > Read)]
pub fn effective_capset(&self) -> CapSet {
self.0.effective_capset()
}
/// Sets the capabilities that child process can inherit.
///
/// This method requires the `Write` right.
#[require(R > Write)]
pub fn set_inheritable_capset(&self, inheritable_capset: CapSet) {
self.0.set_inheritable_capset(inheritable_capset);
}
/// Sets the capabilities that are permitted.
///
/// This method requires the `Write` right.
#[require(R > Write)]
pub fn set_permitted_capset(&self, permitted_capset: CapSet) {
self.0.set_permitted_capset(permitted_capset);
}
/// Sets the capabilities that actually use.
///
/// This method requires the `Write` right.
#[require(R > Write)]
pub fn set_effective_capset(&self, effective_capset: CapSet) {
self.0.set_effective_capset(effective_capset);
}
} }

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
mod clone; mod clone;
mod credentials; pub mod credentials;
mod exit; mod exit;
mod kill; mod kill;
pub mod posix_thread; pub mod posix_thread;

View File

@ -7,6 +7,8 @@ use crate::syscall::{
arch_prctl::sys_arch_prctl, arch_prctl::sys_arch_prctl,
bind::sys_bind, bind::sys_bind,
brk::sys_brk, brk::sys_brk,
capget::sys_capget,
capset::sys_capset,
chdir::{sys_chdir, sys_fchdir}, chdir::{sys_chdir, sys_fchdir},
chmod::{sys_chmod, sys_fchmod, sys_fchmodat}, chmod::{sys_chmod, sys_fchmod, sys_fchmodat},
chown::{sys_chown, sys_fchown, sys_fchownat, sys_lchown}, chown::{sys_chown, sys_fchown, sys_fchownat, sys_lchown},
@ -216,6 +218,8 @@ impl_syscall_nums_and_dispatch_fn! {
SYS_SETFSUID = 122 => sys_setfsuid(args[..1]); SYS_SETFSUID = 122 => sys_setfsuid(args[..1]);
SYS_SETFSGID = 123 => sys_setfsgid(args[..1]); SYS_SETFSGID = 123 => sys_setfsgid(args[..1]);
SYS_GETSID = 124 => sys_getsid(args[..1]); SYS_GETSID = 124 => sys_getsid(args[..1]);
SYS_CAPGET = 125 => sys_capget(args[..2]);
SYS_CAPSET = 126 => sys_capset(args[..2]);
SYS_RT_SIGPENDING = 127 => sys_rt_sigpending(args[..2]); SYS_RT_SIGPENDING = 127 => sys_rt_sigpending(args[..2]);
SYS_RT_SIGSUSPEND = 130 => sys_rt_sigsuspend(args[..2]); SYS_RT_SIGSUSPEND = 130 => sys_rt_sigsuspend(args[..2]);
SYS_SIGALTSTACK = 131 => sys_sigaltstack(args[..2]); SYS_SIGALTSTACK = 131 => sys_sigaltstack(args[..2]);

View File

@ -0,0 +1,47 @@
// SPDX-License-Identifier: MPL-2.0
use super::SyscallReturn;
use crate::{
prelude::*,
process::{
credentials,
credentials::c_types::{cap_user_data_t, cap_user_header_t, LINUX_CAPABILITY_VERSION_3},
},
util::{read_val_from_user, write_val_to_user},
};
pub fn sys_capget(cap_user_header_addr: Vaddr, cap_user_data_addr: Vaddr) -> Result<SyscallReturn> {
let cap_user_header: cap_user_header_t =
read_val_from_user::<cap_user_header_t>(cap_user_header_addr)?;
if cap_user_header.version != LINUX_CAPABILITY_VERSION_3 {
return_errno_with_message!(Errno::EINVAL, "not supported (capability version is not 3)");
};
// Extract target pid and validate whether it represents the current process.
let header_pid = cap_user_header.pid;
// Capget only query current process's credential. Namely, it only allows header->pid == 0
// or header->pid == getpid(), which are equivalent.
// See https://linux.die.net/man/2/capget (Section. With VFS capability support) for details.
if header_pid != 0 && header_pid != current!().pid() {
return_errno_with_message!(Errno::EINVAL, "invalid pid");
}
let credentials = credentials();
let inheritable_capset = credentials.inheritable_capset();
let permitted_capset = credentials.permitted_capset();
let effective_capset = credentials.effective_capset();
// Annoying legacy format with 64-bit capabilities exposed as two sets of 32-bit fields,
// so we need to split the capability values up.
let result = cap_user_data_t {
// Note we silently drop the upper capabilities here.
// This behavior is considered fail-safe behavior.
effective: effective_capset.as_u32(),
permitted: permitted_capset.as_u32(),
inheritable: inheritable_capset.as_u32(),
};
write_val_to_user(cap_user_data_addr, &result)?;
Ok(SyscallReturn::Return(0))
}

View File

@ -0,0 +1,51 @@
// SPDX-License-Identifier: MPL-2.0
use super::SyscallReturn;
use crate::{
prelude::*,
process::{
credentials::{
c_types::{cap_user_data_t, cap_user_header_t, LINUX_CAPABILITY_VERSION_3},
capabilities::CapSet,
},
credentials_mut,
},
util::read_val_from_user,
};
const CAP_LAST_CAP: u64 = 40; // Number of the last capability (CAP_CHECKPOINT_RESTORE)
const CAP_VALID_MASK: u64 = (1u64 << (CAP_LAST_CAP + 1)) - 1;
fn make_kernel_cap(low: u32, high: u32) -> u64 {
((low as u64) | ((high as u64) << 32)) & CAP_VALID_MASK
}
pub fn sys_capset(cap_user_header_addr: Vaddr, cap_user_data_addr: Vaddr) -> Result<SyscallReturn> {
let cap_user_header: cap_user_header_t =
read_val_from_user::<cap_user_header_t>(cap_user_header_addr)?;
if cap_user_header.version != LINUX_CAPABILITY_VERSION_3 {
return_errno_with_message!(Errno::EINVAL, "not supported (capability version is not 3)");
};
// The ability to set capabilities of any other process has been deprecated.
// See: https://elixir.bootlin.com/linux/v6.9.3/source/kernel/capability.c#L209 for more details.
let header_pid = cap_user_header.pid;
if header_pid != 0 && header_pid != current!().pid() {
return_errno_with_message!(Errno::EINVAL, "invalid pid");
}
// Convert the cap(u32) to u64
let cap_user_data: cap_user_data_t = read_val_from_user::<cap_user_data_t>(cap_user_data_addr)?;
let inheritable = make_kernel_cap(cap_user_data.inheritable, 0);
let permitted = make_kernel_cap(cap_user_data.permitted, 0);
let effective = make_kernel_cap(cap_user_data.effective, 0);
let credentials = credentials_mut();
credentials.set_inheritable_capset(CapSet::from_bits_truncate(inheritable));
credentials.set_permitted_capset(CapSet::from_bits_truncate(permitted));
credentials.set_effective_capset(CapSet::from_bits_truncate(effective));
Ok(SyscallReturn::Return(0))
}

View File

@ -14,6 +14,8 @@ mod arch;
mod arch_prctl; mod arch_prctl;
mod bind; mod bind;
mod brk; mod brk;
mod capget;
mod capset;
mod chdir; mod chdir;
mod chmod; mod chmod;
mod chown; mod chown;

View File

@ -11,6 +11,7 @@ REGRESSION_BUILD_DIR ?= $(INITRAMFS)/regression
# These test apps are sorted by name # These test apps are sorted by name
TEST_APPS := \ TEST_APPS := \
alarm \ alarm \
capability \
clone3 \ clone3 \
cpu_affinity \ cpu_affinity \
eventfd2 \ eventfd2 \

View File

@ -0,0 +1,5 @@
# SPDX-License-Identifier: MPL-2.0
include ../test_common.mk
EXTRA_C_FLAGS :=

View File

@ -0,0 +1,80 @@
// SPDX-License-Identifier: MPL-2.0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/capability.h>
int set_caps(__u32 target_pid, __u32 capabilities)
{
struct __user_cap_header_struct capheader;
struct __user_cap_data_struct capdata[2];
capheader.version = _LINUX_CAPABILITY_VERSION_3;
capheader.pid = target_pid;
memset(&capdata, 0, sizeof(capdata));
// Set specified capabilities
capdata[0].effective = capdata[0].permitted = capabilities;
capdata[0].inheritable = 0;
if (syscall(SYS_capset, &capheader, &capdata) < 0) {
perror("capset failed");
return 1;
}
printf("Process capabilities set successfully.\n");
return 0;
}
int check_caps(__u32 target_pid, __u32 capabilities)
{
struct __user_cap_header_struct capheader;
struct __user_cap_data_struct capdata[2];
memset(&capheader, 0, sizeof(capheader));
memset(&capdata, 0, sizeof(capdata));
capheader.version = _LINUX_CAPABILITY_VERSION_3;
capheader.pid = target_pid;
if (syscall(SYS_capget, &capheader, &capdata) == -1) {
perror("capget failed");
exit(EXIT_FAILURE);
}
printf("Process capabilities retrieved successfully.\n");
return (capdata[0].permitted & capabilities) &&
(capdata[0].effective & capabilities);
}
int main(void)
{
__u32 target_pid = getpid();
printf("Process Pid: %u.\n", target_pid);
__u32 caps_to_set =
(1 << CAP_NET_RAW) |
(1 << CAP_NET_ADMIN); // Define the desired capabilities.
// Try setting the specified capabilities.
if (set_caps(target_pid, caps_to_set) != 0) {
fprintf(stderr, "Failed to set capabilities.\n");
return 1;
}
// Check for CAP_NET_RAW among the process's capabilities.
if (check_caps(target_pid, 1 << CAP_NET_RAW)) {
printf("Process has CAP_NET_RAW capability.\n");
} else {
fprintf(stderr,
"Process does NOT have CAP_NET_RAW capability.\n");
return 1;
}
// Check for CAP_NET_ADMIN among the process's capabilities.
if (check_caps(target_pid, 1 << CAP_NET_ADMIN)) {
printf("Process has CAP_NET_ADMIN capability.\n");
} else {
fprintf(stderr,
"Process does NOT have CAP_NET_ADMIN capability.\n");
return 1;
}
return 0;
}

View File

@ -1,11 +1 @@
ChrootTest.Success ChrootTest.WithoutCapability
ChrootTest.PermissionDenied
ChrootTest.NotDir
ChrootTest.NotExist
ChrootTest.WithoutCapability
ChrootTest.CreatesNewRoot
ChrootTest.DotDotFromOpenFD
ChrootTest.ProcFdLinkResolutionInChroot
ChrootTest.ProcMemSelfFdsNoEscapeProcOpen
ChrootTest.ProcMemSelfMapsNoEscapeProcOpen
ChrootTest.ProcMountsMountinfoNoEscape