From a9fb2dcb9388b6b453bb55b0a41e9d95cb5bafbe Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 26 Dec 2024 20:06:59 +0100 Subject: [PATCH] Simplify proc scheme security. --- src/ptrace.rs | 2 +- src/scheme/mod.rs | 15 ++----- src/scheme/proc.rs | 103 +++++++++++++++++++++++-------------------- src/syscall/debug.rs | 15 ------- 4 files changed, 61 insertions(+), 74 deletions(-) diff --git a/src/ptrace.rs b/src/ptrace.rs index 4d86f42b8f..d555a5c02a 100644 --- a/src/ptrace.rs +++ b/src/ptrace.rs @@ -124,7 +124,7 @@ pub fn close_tracee(session: &Session) { /// Trigger a notification to the event: scheme fn proc_trigger_event(file_id: usize, flags: EventFlags) { - event::trigger(GlobalSchemes::ProcFull.scheme_id(), file_id, flags); + event::trigger(GlobalSchemes::Proc.scheme_id(), file_id, flags); } /// Dispatch an event to any tracer tracing `self`. This will cause diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs index d37bcc36cd..67ed54aa53 100644 --- a/src/scheme/mod.rs +++ b/src/scheme/mod.rs @@ -137,8 +137,7 @@ impl SchemeList { Irq, Time, Sys, - ProcFull, - ProcRestricted, + Proc, ]); #[cfg(feature = "acpi")] @@ -163,8 +162,6 @@ impl SchemeList { //anonymous mmap's are implemented self.insert_global(ns, "memory", GlobalSchemes::Memory) .unwrap(); - self.insert_global(ns, "thisproc", GlobalSchemes::ProcRestricted) - .unwrap(); self.insert_global(ns, "pipe", GlobalSchemes::Pipe).unwrap(); } @@ -209,9 +206,7 @@ impl SchemeList { self.insert_global(ns, "debug", GlobalSchemes::Debug) .unwrap(); self.insert_global(ns, "irq", GlobalSchemes::Irq).unwrap(); - self.insert_global(ns, "proc", GlobalSchemes::ProcFull) - .unwrap(); - self.insert_global(ns, "thisproc", GlobalSchemes::ProcRestricted) + self.insert_global(ns, "kernel.proc", GlobalSchemes::Proc) .unwrap(); self.insert_global(ns, "serio", GlobalSchemes::Serio) .unwrap(); @@ -538,8 +533,7 @@ pub enum GlobalSchemes { Irq, Time, Sys, - ProcFull, - ProcRestricted, + Proc, #[cfg(feature = "acpi")] Acpi, @@ -578,8 +572,7 @@ impl core::ops::Deref for GlobalSchemes { Self::Irq => &IrqScheme, Self::Time => &TimeScheme, Self::Sys => &SysScheme, - Self::ProcFull => &ProcScheme::, - Self::ProcRestricted => &ProcScheme::, + Self::Proc => &ProcScheme, #[cfg(feature = "acpi")] Self::Acpi => &AcpiScheme, #[cfg(dtb)] diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index fdecba0a7c..b629fb74c4 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -21,7 +21,7 @@ use crate::{ }; use super::{CallerCtx, GlobalSchemes, KernelSchemes, OpenResult}; -use ::syscall::{SigProcControl, Sigcontrol}; +use ::syscall::{ProcSchemeAttrs, SigProcControl, Sigcontrol}; use alloc::{ boxed::Box, collections::{btree_map::Entry, BTreeMap}, @@ -30,10 +30,10 @@ use alloc::{ vec::Vec, }; use core::{ - mem, + mem::{self, size_of}, num::NonZeroUsize, slice, str, - sync::atomic::{AtomicUsize, Ordering}, + sync::atomic::{AtomicBool, AtomicUsize, Ordering}, }; use spin::RwLock; use spinning_top::RwSpinlock; @@ -95,13 +95,12 @@ enum RegsKind { Env, } #[derive(Clone)] -enum ProcHandle { - Static { ty: &'static str, bytes: Box<[u8]> }, - SessionId, - Attr { attr: Attr }, -} -#[derive(Clone)] enum ContextHandle { + // Opened by the process manager, after which it is locked. This capability is used to open + // Attr handles, to set ens/euid/egid/pid. + Authority, + Attr, + Status, // writing usize::MAX causes exit Signal, // writing sends signal @@ -152,7 +151,7 @@ enum Attr { Gid, // TODO: namespace, tid, etc. } -pub struct ProcScheme; +pub struct ProcScheme; static NEXT_ID: AtomicUsize = AtomicUsize::new(1); // Using BTreeMap as hashbrown doesn't have a const constructor. @@ -181,9 +180,10 @@ fn new_handle((handle, fl): (Handle, InternalFlags)) -> Result<(usize, InternalF enum OpenTy { Ctxt(Arc>), + Auth, } -impl ProcScheme { +impl ProcScheme { fn openat_context( &self, path: &str, @@ -239,15 +239,27 @@ impl ProcScheme { flags: usize, ) -> Result<(usize, InternalFlags)> { let operation_name = operation_str.ok_or(Error::new(EINVAL))?; - let (mut handle, positioned) = { - let OpenTy::Ctxt(context) = ty; - if let Some((kind, positioned)) = + let (mut handle, positioned) = match ty { + OpenTy::Ctxt(context) => if let Some((kind, positioned)) = self.openat_context(operation_name, Arc::clone(&context))? { (Handle { context, kind }, positioned) } else { return Err(Error::new(EINVAL)); } + OpenTy::Auth => { + extern "C" fn ret() {} + let context = match operation_str.ok_or(Error::new(ENOENT))? { + "new-context" => context::spawn(true, ret)?, + "cur-context" => context::current(), + _ => return Err(Error::new(ENOENT)), + }; + + (Handle { + context, + kind: ContextHandle::OpenViaDup, + }, false) + } }; { @@ -301,34 +313,22 @@ impl ProcScheme { } } -fn new_context() -> Result>> { - let (euid, egid, ens) = match context::current().read() { - ref cur => (cur.euid, cur.egid, cur.ens), - }; - - let context = context::spawn(true, clone_handler)?; - { - let mut guard = context.write(); - guard.euid = euid; - guard.egid = egid; - guard.ens = ens; - } - Ok(context) -} - -impl KernelScheme for ProcScheme { +impl KernelScheme for ProcScheme { fn kopen(&self, path: &str, _flags: usize, _ctx: CallerCtx) -> Result { - let context = match path { - "new" => new_context()?, - "current" => context::current(), - _ => return Err(Error::new(ENOENT)), - }; + if path != "authority" { + return Err(Error::new(ENOENT)); + } + static LOCK: AtomicBool = AtomicBool::new(false); + if LOCK.swap(true, Ordering::Relaxed) { + return Err(Error::new(EEXIST)); + } let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); HANDLES.write().insert( id, Handle { - context, - kind: ContextHandle::OpenViaDup, + // TODO: placeholder + context: context::current(), + kind: ContextHandle::Authority, }, ); Ok(OpenResult::SchemeLocal(id, InternalFlags::empty())) @@ -545,6 +545,11 @@ impl KernelScheme for ProcScheme { let buf = &array[..raw_buf.len()]; new_handle(match info { + Handle { kind: ContextHandle::Authority, .. } => return self.open_inner(OpenTy::Auth, + Some(core::str::from_utf8(buf).map_err(|_| Error::new(EINVAL))?) + .filter(|s| !s.is_empty()), + O_RDWR | O_CLOEXEC, + ).map(|(r, fl)| OpenResult::SchemeLocal(r, fl)), Handle { kind: ContextHandle::OpenViaDup, context, @@ -643,11 +648,6 @@ impl KernelScheme for ProcScheme { .map(|(r, fl)| OpenResult::SchemeLocal(r, fl)) } } -extern "C" fn clone_handler() { - // This function will return to the syscall return assembly, and subsequently transition to - // usermode. -} - fn extract_scheme_number(fd: usize) -> Result<(KernelSchemes, usize)> { let (scheme_id, number) = match &*context::current() .read() @@ -668,7 +668,7 @@ fn extract_scheme_number(fd: usize) -> Result<(KernelSchemes, usize)> { fn verify_scheme(scheme: KernelSchemes) -> Result<()> { if !matches!( scheme, - KernelSchemes::Global(GlobalSchemes::ProcFull | GlobalSchemes::ProcRestricted) + KernelSchemes::Global(GlobalSchemes::Proc) ) { return Err(Error::new(EBADF)); } @@ -1002,9 +1002,7 @@ impl ContextHandle { */ Err(Error::new(EOPNOTSUPP)) } - Self::OpenViaDup - | Self::AwaitingAddrSpaceChange { .. } - | Self::AwaitingFiletableChange { .. } => Err(Error::new(EBADF)), + _ => Err(Error::new(EBADF)), } } fn kreadoff( @@ -1111,7 +1109,18 @@ impl ContextHandle { buf.copy_exactly(crate::cpu_set::mask_as_bytes(&mask))?; Ok(mem::size_of_val(&mask)) - } // TODO: Replace write() with SYS_DUP_FORWARD. + } // TODO: Replace write() with SYS_SENDFD? + ContextHandle::Attr => { + let (euid, egid, ens, pid) = match context.read() { + ref c => (c.euid, c.egid, c.ens.get() as u32, c.pid as u32), + }; + buf.copy_common_bytes_from_slice(&ProcSchemeAttrs { + pid, + euid, + egid, + ens, + }) + } // TODO: Find a better way to switch address spaces, since they also require switching // the instruction and stack pointer. Maybe remove `/regs` altogether and replace it diff --git a/src/syscall/debug.rs b/src/syscall/debug.rs index 6add4cc427..a912ce9bdc 100644 --- a/src/syscall/debug.rs +++ b/src/syscall/debug.rs @@ -156,7 +156,6 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) - SYS_CLOCK_GETTIME => format!("clock_gettime({}, {:?})", b, unsafe { read_struct::(c) }), - SYS_EXIT => format!("exit({})", b), SYS_FUTEX => format!( "futex({:#X} [{:?}], {}, {}, {}, {})", b, @@ -166,17 +165,7 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) - e, f ), - SYS_GETEGID => format!("getegid()"), - SYS_GETENS => format!("getens()"), - SYS_GETEUID => format!("geteuid()"), - SYS_GETGID => format!("getgid()"), - SYS_GETNS => format!("getns()"), - SYS_GETPGID => format!("getpgid()"), - SYS_GETPID => format!("getpid()"), - SYS_GETPPID => format!("getppid()"), - SYS_GETUID => format!("getuid()"), SYS_IOPL => format!("iopl({})", b), - SYS_KILL => format!("kill({}, {})", b, c), SYS_MKNS => format!( "mkns({:p} len: {})", // TODO: Print out all scheme names? @@ -194,10 +183,6 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) - d ), SYS_VIRTTOPHYS => format!("virttophys({:#X})", b), - SYS_SETREGID => format!("setregid({}, {})", b, c), - SYS_SETRENS => format!("setrens({}, {})", b, c), - SYS_SETREUID => format!("setreuid({}, {})", b, c), - SYS_WAITPID => format!("waitpid({}, {:#X}, {:?})", b, c, WaitFlags::from_bits(d)), SYS_YIELD => format!("yield()"), _ => format!( "UNKNOWN{} {:#X}({:#X}, {:#X}, {:#X}, {:#X}, {:#X})",