From d50863bbd84ca2d06a2674bc94148b2e8a61220a Mon Sep 17 00:00:00 2001 From: kossLAN Date: Fri, 29 May 2026 11:20:59 -0400 Subject: [PATCH] pr_caps: move process capabilities into workspace crate --- Cargo.lock | 11 ++ Cargo.toml | 2 + pr-caps/Cargo.toml | 12 ++ pr-caps/src/lib.rs | 264 ++++++++++++++++++++++++++++++++++++++++++++ src/pr_caps.rs | 267 +-------------------------------------------- 5 files changed, 290 insertions(+), 266 deletions(-) create mode 100644 pr-caps/Cargo.toml create mode 100644 pr-caps/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 834d271c..fb564714 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -704,6 +704,7 @@ dependencies = [ "jay-geometry", "jay-io-uring", "jay-layout-animation", + "jay-pr-caps", "jay-sighand", "jay-time", "jay-toml-config", @@ -857,6 +858,16 @@ dependencies = [ "jay-geometry", ] +[[package]] +name = "jay-pr-caps" +version = "0.1.0" +dependencies = [ + "jay-utils", + "opera", + "parking_lot", + "uapi", +] + [[package]] name = "jay-sighand" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 8b89a004..9da94883 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ members = [ "wheel", "cpu-worker", "sighand", + "pr-caps", "toml-config", "algorithms", "toml-spec", @@ -79,6 +80,7 @@ jay-eventfd-cache = { version = "0.1.0", path = "eventfd-cache" } jay-wheel = { version = "0.1.0", path = "wheel" } jay-cpu-worker = { version = "0.1.0", path = "cpu-worker" } jay-sighand = { version = "0.1.0", path = "sighand" } +jay-pr-caps = { version = "0.1.0", path = "pr-caps" } uapi = "0.2.13" thiserror = "2.0.11" diff --git a/pr-caps/Cargo.toml b/pr-caps/Cargo.toml new file mode 100644 index 00000000..76e5c2c5 --- /dev/null +++ b/pr-caps/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "jay-pr-caps" +version = "0.1.0" +edition = "2024" +license = "GPL-3.0-only" + +[dependencies] +jay-utils = { version = "0.1.0", path = "../utils" } + +opera = "1.0.1" +parking_lot = "0.12.1" +uapi = "0.2.13" diff --git a/pr-caps/src/lib.rs b/pr-caps/src/lib.rs new file mode 100644 index 00000000..256167b1 --- /dev/null +++ b/pr-caps/src/lib.rs @@ -0,0 +1,264 @@ +use { + crate::sys::{ + _LINUX_CAPABILITY_U32S_3, _LINUX_CAPABILITY_VERSION_3, CAP_SYS_NICE, cap_user_data_t, + cap_user_header_t, + }, + jay_utils::{bitflags::BitflagsExt, errorfmt::ErrorFmt, oserror::OsErrorExt}, + opera::PhantomNotSend, + parking_lot::{Condvar, Mutex}, + std::{ + mem, + sync::Arc, + thread::{self, JoinHandle}, + }, + uapi::{ + c::{SYS_capget, SYS_capset, syscall}, + map_err, + }, +}; + +pub struct PrCaps { + effective: u64, + permitted: u64, + inheritable: u64, +} + +pub struct PrCompCaps { + caps: PrCaps, +} + +pub struct PrCapsThread { + thread: Option>, + data: Arc, + _no_send: PhantomNotSend, +} + +#[derive(Default)] +struct ThreadData { + cond: Condvar, + mutex: Mutex, +} + +#[derive(Default)] +struct MutData { + exit: bool, + fun: Option>, +} + +pub fn pr_caps() -> PrCaps { + let mut hdr = cap_user_header_t { + version: _LINUX_CAPABILITY_VERSION_3, + pid: 0, + }; + let mut caps = [cap_user_data_t::default(); _LINUX_CAPABILITY_U32S_3]; + let ret = unsafe { syscall(SYS_capget, &mut hdr, &mut caps) }; + if let Err(e) = map_err!(ret).to_os_error() { + eprintln!("Could not get process capabilities: {}", ErrorFmt(e)); + return PrCaps { + effective: 0, + permitted: 0, + inheritable: 0, + }; + } + PrCaps { + effective: caps[0].effective as u64 | ((caps[1].effective as u64) << 32), + permitted: caps[0].permitted as u64 | ((caps[1].permitted as u64) << 32), + inheritable: caps[0].inheritable as u64 | ((caps[1].inheritable as u64) << 32), + } +} + +pub fn drop_all_pr_caps() { + let mut hdr = cap_user_header_t { + version: _LINUX_CAPABILITY_VERSION_3, + pid: 0, + }; + let caps = [cap_user_data_t::default(); _LINUX_CAPABILITY_U32S_3]; + let ret = unsafe { syscall(SYS_capset, &mut hdr, &caps) }; + if let Err(e) = map_err!(ret).to_os_error() { + eprintln!("Could not get drop capabilities: {}", ErrorFmt(e)); + } +} + +impl PrCaps { + pub fn into_comp(mut self) -> PrCompCaps { + let mut caps = 0; + macro_rules! add_cap { + ($name:ident) => { + if self.permitted.contains(1 << $name) { + caps |= 1 << $name; + } + }; + } + add_cap!(CAP_SYS_NICE); + let mut hdr = cap_user_header_t { + version: _LINUX_CAPABILITY_VERSION_3, + pid: 0, + }; + let caps_hi = (caps >> 32) as u32; + let caps_lo = caps as u32; + let mut data = [cap_user_data_t::default(); _LINUX_CAPABILITY_U32S_3]; + data[0].effective = caps_lo; + data[1].effective = caps_hi; + data[0].permitted = caps_lo; + data[1].permitted = caps_hi; + let ret = unsafe { syscall(SYS_capset, &mut hdr, &data) }; + if let Err(e) = map_err!(ret).to_os_error() { + eprintln!("Could not get set compositor capabilities: {}", ErrorFmt(e)); + return PrCompCaps { caps: self }; + } + self.effective = caps; + self.permitted = caps; + self.inheritable = 0; + PrCompCaps { caps: self } + } +} + +impl PrCompCaps { + pub fn has_nice(&self) -> bool { + self.caps.effective.contains(1 << CAP_SYS_NICE) + } + + pub fn into_thread(self) -> PrCapsThread { + let data = Arc::new(ThreadData::default()); + let data2 = data.clone(); + let jh = thread::Builder::new() + .name("SYS_nice thread".to_string()) + .spawn(move || { + let data2 = data2; + let mut lock = data2.mutex.lock(); + loop { + if lock.exit { + return; + } + if let Some(f) = lock.fun.take() { + f(); + } + data2.cond.wait(&mut lock); + } + }) + .expect("Could not spawn SYS_nice thread"); + PrCapsThread { + thread: Some(jh), + data, + _no_send: Default::default(), + } + } +} + +impl PrCapsThread { + pub unsafe fn run(&self, f: F) -> T + where + F: FnOnce() -> T, + { + struct AssertSend(T); + unsafe impl Send for AssertSend {} + struct Data { + cond: Condvar, + mutex: Mutex>>, + } + let data = Arc::new(Data { + cond: Default::default(), + mutex: Default::default(), + }); + let data2 = data.clone(); + let f = AssertSend(f); + let fun = Box::new(move || { + let f = f; + let t = f.0(); + *data2.mutex.lock() = Some(AssertSend(t)); + data2.cond.notify_all(); + }); + let fun = unsafe { + mem::transmute::, Box>(fun) + }; + self.data.mutex.lock().fun = Some(fun); + self.data.cond.notify_all(); + let mut lock = data.mutex.lock(); + loop { + if let Some(t) = lock.take() { + return t.0; + } + data.cond.wait(&mut lock); + } + } +} + +impl Drop for PrCaps { + fn drop(&mut self) { + drop_all_pr_caps(); + } +} + +impl Drop for PrCapsThread { + fn drop(&mut self) { + self.data.mutex.lock().exit = true; + self.data.cond.notify_all(); + let _ = self.thread.take().unwrap().join(); + } +} + +mod sys { + #![allow(dead_code)] + + use uapi::c::pid_t; + + pub const _LINUX_CAPABILITY_VERSION_3: u32 = 0x20080522; + pub const _LINUX_CAPABILITY_U32S_3: usize = 2; + + #[repr(C)] + #[derive(Copy, Clone, Debug)] + pub struct cap_user_header_t { + pub version: u32, + pub pid: pid_t, + } + + #[repr(C)] + #[derive(Copy, Clone, Debug, Default)] + pub struct cap_user_data_t { + pub effective: u32, + pub permitted: u32, + pub inheritable: u32, + } + + pub const CAP_CHOWN: u32 = 0; + pub const CAP_DAC_OVERRIDE: u32 = 1; + pub const CAP_DAC_READ_SEARCH: u32 = 2; + pub const CAP_FOWNER: u32 = 3; + pub const CAP_FSETID: u32 = 4; + pub const CAP_KILL: u32 = 5; + pub const CAP_SETGID: u32 = 6; + pub const CAP_SETUID: u32 = 7; + pub const CAP_SETPCAP: u32 = 8; + pub const CAP_LINUX_IMMUTABLE: u32 = 9; + pub const CAP_NET_BIND_SERVICE: u32 = 10; + pub const CAP_NET_BROADCAST: u32 = 11; + pub const CAP_NET_ADMIN: u32 = 12; + pub const CAP_NET_RAW: u32 = 13; + pub const CAP_IPC_LOCK: u32 = 14; + pub const CAP_IPC_OWNER: u32 = 15; + pub const CAP_SYS_MODULE: u32 = 16; + pub const CAP_SYS_RAWIO: u32 = 17; + pub const CAP_SYS_CHROOT: u32 = 18; + pub const CAP_SYS_PTRACE: u32 = 19; + pub const CAP_SYS_PACCT: u32 = 20; + pub const CAP_SYS_ADMIN: u32 = 21; + pub const CAP_SYS_BOOT: u32 = 22; + pub const CAP_SYS_NICE: u32 = 23; + pub const CAP_SYS_RESOURCE: u32 = 24; + pub const CAP_SYS_TIME: u32 = 25; + pub const CAP_SYS_TTY_CONFIG: u32 = 26; + pub const CAP_MKNOD: u32 = 27; + pub const CAP_LEASE: u32 = 28; + pub const CAP_AUDIT_WRITE: u32 = 29; + pub const CAP_AUDIT_CONTROL: u32 = 30; + pub const CAP_SETFCAP: u32 = 31; + pub const CAP_MAC_OVERRIDE: u32 = 32; + pub const CAP_MAC_ADMIN: u32 = 33; + pub const CAP_SYSLOG: u32 = 34; + pub const CAP_WAKE_ALARM: u32 = 35; + pub const CAP_BLOCK_SUSPEND: u32 = 36; + pub const CAP_AUDIT_READ: u32 = 37; + pub const CAP_PERFMON: u32 = 38; + pub const CAP_BPF: u32 = 39; + pub const CAP_CHECKPOINT_RESTORE: u32 = 40; +} diff --git a/src/pr_caps.rs b/src/pr_caps.rs index 1c262557..b80102fd 100644 --- a/src/pr_caps.rs +++ b/src/pr_caps.rs @@ -1,266 +1 @@ -use { - crate::{ - pr_caps::sys::{ - _LINUX_CAPABILITY_U32S_3, _LINUX_CAPABILITY_VERSION_3, CAP_SYS_NICE, cap_user_data_t, - cap_user_header_t, - }, - utils::{bitflags::BitflagsExt, errorfmt::ErrorFmt, oserror::OsErrorExt}, - }, - opera::PhantomNotSend, - parking_lot::{Condvar, Mutex}, - std::{ - mem, - sync::Arc, - thread::{self, JoinHandle}, - }, - uapi::{ - c::{SYS_capget, SYS_capset, syscall}, - map_err, - }, -}; - -pub struct PrCaps { - effective: u64, - permitted: u64, - inheritable: u64, -} - -pub struct PrCompCaps { - caps: PrCaps, -} - -pub struct PrCapsThread { - thread: Option>, - data: Arc, - _no_send: PhantomNotSend, -} - -#[derive(Default)] -struct ThreadData { - cond: Condvar, - mutex: Mutex, -} - -#[derive(Default)] -struct MutData { - exit: bool, - fun: Option>, -} - -pub fn pr_caps() -> PrCaps { - let mut hdr = cap_user_header_t { - version: _LINUX_CAPABILITY_VERSION_3, - pid: 0, - }; - let mut caps = [cap_user_data_t::default(); _LINUX_CAPABILITY_U32S_3]; - let ret = unsafe { syscall(SYS_capget, &mut hdr, &mut caps) }; - if let Err(e) = map_err!(ret).to_os_error() { - eprintln!("Could not get process capabilities: {}", ErrorFmt(e)); - return PrCaps { - effective: 0, - permitted: 0, - inheritable: 0, - }; - } - PrCaps { - effective: caps[0].effective as u64 | ((caps[1].effective as u64) << 32), - permitted: caps[0].permitted as u64 | ((caps[1].permitted as u64) << 32), - inheritable: caps[0].inheritable as u64 | ((caps[1].inheritable as u64) << 32), - } -} - -pub fn drop_all_pr_caps() { - let mut hdr = cap_user_header_t { - version: _LINUX_CAPABILITY_VERSION_3, - pid: 0, - }; - let caps = [cap_user_data_t::default(); _LINUX_CAPABILITY_U32S_3]; - let ret = unsafe { syscall(SYS_capset, &mut hdr, &caps) }; - if let Err(e) = map_err!(ret).to_os_error() { - eprintln!("Could not get drop capabilities: {}", ErrorFmt(e)); - } -} - -impl PrCaps { - pub fn into_comp(mut self) -> PrCompCaps { - let mut caps = 0; - macro_rules! add_cap { - ($name:ident) => { - if self.permitted.contains(1 << $name) { - caps |= 1 << $name; - } - }; - } - add_cap!(CAP_SYS_NICE); - let mut hdr = cap_user_header_t { - version: _LINUX_CAPABILITY_VERSION_3, - pid: 0, - }; - let caps_hi = (caps >> 32) as u32; - let caps_lo = caps as u32; - let mut data = [cap_user_data_t::default(); _LINUX_CAPABILITY_U32S_3]; - data[0].effective = caps_lo; - data[1].effective = caps_hi; - data[0].permitted = caps_lo; - data[1].permitted = caps_hi; - let ret = unsafe { syscall(SYS_capset, &mut hdr, &data) }; - if let Err(e) = map_err!(ret).to_os_error() { - eprintln!("Could not get set compositor capabilities: {}", ErrorFmt(e)); - return PrCompCaps { caps: self }; - } - self.effective = caps; - self.permitted = caps; - self.inheritable = 0; - PrCompCaps { caps: self } - } -} - -impl PrCompCaps { - pub fn has_nice(&self) -> bool { - self.caps.effective.contains(1 << CAP_SYS_NICE) - } - - pub fn into_thread(self) -> PrCapsThread { - let data = Arc::new(ThreadData::default()); - let data2 = data.clone(); - let jh = thread::Builder::new() - .name("SYS_nice thread".to_string()) - .spawn(move || { - let data2 = data2; - let mut lock = data2.mutex.lock(); - loop { - if lock.exit { - return; - } - if let Some(f) = lock.fun.take() { - f(); - } - data2.cond.wait(&mut lock); - } - }) - .expect("Could not spawn SYS_nice thread"); - PrCapsThread { - thread: Some(jh), - data, - _no_send: Default::default(), - } - } -} - -impl PrCapsThread { - pub unsafe fn run(&self, f: F) -> T - where - F: FnOnce() -> T, - { - struct AssertSend(T); - unsafe impl Send for AssertSend {} - struct Data { - cond: Condvar, - mutex: Mutex>>, - } - let data = Arc::new(Data { - cond: Default::default(), - mutex: Default::default(), - }); - let data2 = data.clone(); - let f = AssertSend(f); - let fun = Box::new(move || { - let f = f; - let t = f.0(); - *data2.mutex.lock() = Some(AssertSend(t)); - data2.cond.notify_all(); - }); - let fun = unsafe { - mem::transmute::, Box>(fun) - }; - self.data.mutex.lock().fun = Some(fun); - self.data.cond.notify_all(); - let mut lock = data.mutex.lock(); - loop { - if let Some(t) = lock.take() { - return t.0; - } - data.cond.wait(&mut lock); - } - } -} - -impl Drop for PrCaps { - fn drop(&mut self) { - drop_all_pr_caps(); - } -} - -impl Drop for PrCapsThread { - fn drop(&mut self) { - self.data.mutex.lock().exit = true; - self.data.cond.notify_all(); - let _ = self.thread.take().unwrap().join(); - } -} - -mod sys { - #![allow(dead_code)] - - use uapi::c::pid_t; - - pub const _LINUX_CAPABILITY_VERSION_3: u32 = 0x20080522; - pub const _LINUX_CAPABILITY_U32S_3: usize = 2; - - #[repr(C)] - #[derive(Copy, Clone, Debug)] - pub struct cap_user_header_t { - pub version: u32, - pub pid: pid_t, - } - - #[repr(C)] - #[derive(Copy, Clone, Debug, Default)] - pub struct cap_user_data_t { - pub effective: u32, - pub permitted: u32, - pub inheritable: u32, - } - - pub const CAP_CHOWN: u32 = 0; - pub const CAP_DAC_OVERRIDE: u32 = 1; - pub const CAP_DAC_READ_SEARCH: u32 = 2; - pub const CAP_FOWNER: u32 = 3; - pub const CAP_FSETID: u32 = 4; - pub const CAP_KILL: u32 = 5; - pub const CAP_SETGID: u32 = 6; - pub const CAP_SETUID: u32 = 7; - pub const CAP_SETPCAP: u32 = 8; - pub const CAP_LINUX_IMMUTABLE: u32 = 9; - pub const CAP_NET_BIND_SERVICE: u32 = 10; - pub const CAP_NET_BROADCAST: u32 = 11; - pub const CAP_NET_ADMIN: u32 = 12; - pub const CAP_NET_RAW: u32 = 13; - pub const CAP_IPC_LOCK: u32 = 14; - pub const CAP_IPC_OWNER: u32 = 15; - pub const CAP_SYS_MODULE: u32 = 16; - pub const CAP_SYS_RAWIO: u32 = 17; - pub const CAP_SYS_CHROOT: u32 = 18; - pub const CAP_SYS_PTRACE: u32 = 19; - pub const CAP_SYS_PACCT: u32 = 20; - pub const CAP_SYS_ADMIN: u32 = 21; - pub const CAP_SYS_BOOT: u32 = 22; - pub const CAP_SYS_NICE: u32 = 23; - pub const CAP_SYS_RESOURCE: u32 = 24; - pub const CAP_SYS_TIME: u32 = 25; - pub const CAP_SYS_TTY_CONFIG: u32 = 26; - pub const CAP_MKNOD: u32 = 27; - pub const CAP_LEASE: u32 = 28; - pub const CAP_AUDIT_WRITE: u32 = 29; - pub const CAP_AUDIT_CONTROL: u32 = 30; - pub const CAP_SETFCAP: u32 = 31; - pub const CAP_MAC_OVERRIDE: u32 = 32; - pub const CAP_MAC_ADMIN: u32 = 33; - pub const CAP_SYSLOG: u32 = 34; - pub const CAP_WAKE_ALARM: u32 = 35; - pub const CAP_BLOCK_SUSPEND: u32 = 36; - pub const CAP_AUDIT_READ: u32 = 37; - pub const CAP_PERFMON: u32 = 38; - pub const CAP_BPF: u32 = 39; - pub const CAP_CHECKPOINT_RESTORE: u32 = 40; -} +pub use jay_pr_caps::*;