diff --git a/local/patches/kernel/P27-capability-bitmask.patch b/local/patches/kernel/P27-capability-bitmask.patch new file mode 100644 index 0000000000..38b12cf4d7 --- /dev/null +++ b/local/patches/kernel/P27-capability-bitmask.patch @@ -0,0 +1,123 @@ +diff --git a/src/context/context.rs b/src/context/context.rs +index 6d723f49..322cb30d 100644 +--- a/src/context/context.rs ++++ b/src/context/context.rs +@@ -149,0 +150 @@ pub struct Context { ++ pub caps: u64, +@@ -207,0 +209 @@ impl Context { ++ caps: crate::scheme::caps::CAP_ALL, +@@ -479,0 +482,3 @@ impl Context { ++ pub fn has_cap(&self, cap: u64) -> bool { ++ self.caps & cap != 0 ++ } +@@ -485,0 +491 @@ impl Context { ++ caps: self.caps, +diff --git a/src/scheme/acpi.rs b/src/scheme/acpi.rs +index 5d734691..1326ff57 100644 +--- a/src/scheme/acpi.rs ++++ b/src/scheme/acpi.rs +@@ -15,0 +16 @@ use crate::{ ++ scheme::caps, +@@ -142 +143 @@ impl KernelScheme for AcpiScheme { +- if ctx.uid != 0 { ++ if !ctx.has_cap(caps::CAP_ACPI) { +diff --git a/src/scheme/caps.rs b/src/scheme/caps.rs +new file mode 100644 +index 00000000..481f2369 +--- /dev/null ++++ b/src/scheme/caps.rs +@@ -0,0 +1,11 @@ ++pub const CAP_SCHEME_REGISTER: u64 = 1 << 0; ++pub const CAP_PHYS_MEM: u64 = 1 << 1; ++pub const CAP_IRQ: u64 = 1 << 2; ++pub const CAP_ACPI: u64 = 1 << 3; ++pub const CAP_SYS_DEBUG: u64 = 1 << 4; ++pub const CAP_SYS_WRITE: u64 = 1 << 5; ++pub const CAP_SYS_MSR: u64 = 1 << 6; ++pub const CAP_SERIO: u64 = 1 << 7; ++pub const CAP_CHOWN: u64 = 1 << 8; ++pub const CAP_PROC_ATTR: u64 = 1 << 9; ++pub const CAP_ALL: u64 = !0u64; +diff --git a/src/scheme/debug.rs b/src/scheme/debug.rs +index 4a23b3cf..2ab347d7 100644 +--- a/src/scheme/debug.rs ++++ b/src/scheme/debug.rs +@@ -76 +76 @@ impl KernelScheme for DebugScheme { +- if ctx.uid != 0 { ++ if !ctx.has_cap(caps::CAP_SYS_DEBUG) { +diff --git a/src/scheme/irq.rs b/src/scheme/irq.rs +index 42229609..b1b893ac 100644 +--- a/src/scheme/irq.rs ++++ b/src/scheme/irq.rs +@@ -20,0 +21 @@ use super::{CallerCtx, HandleMap, OpenResult, SchemeExt, StrOrBytes}; ++use super::caps; +@@ -259 +260 @@ impl crate::scheme::KernelScheme for IrqScheme { +- if ctx.uid != 0 { ++ if !ctx.has_cap(caps::CAP_IRQ) { +diff --git a/src/scheme/memory.rs b/src/scheme/memory.rs +index c2f9f474..29754518 100644 +--- a/src/scheme/memory.rs ++++ b/src/scheme/memory.rs +@@ -11,0 +12 @@ use crate::{ ++ scheme::caps, +@@ -235 +236 @@ impl KernelScheme for MemoryScheme { +- if ctx.uid != 0 ++ if !ctx.has_cap(caps::CAP_PHYS_MEM) +diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs +index 765e547f..68bb8247 100644 +--- a/src/scheme/mod.rs ++++ b/src/scheme/mod.rs +@@ -53,0 +54 @@ use self::{ ++pub mod caps; +@@ -356 +357 @@ impl KernelScheme for SchemeList { +- if caller.uid != 0 { ++ if !caller.has_cap(caps::CAP_SCHEME_REGISTER) { +@@ -813,0 +815 @@ pub struct CallerCtx { ++ pub caps: u64, +@@ -815,0 +818,3 @@ impl CallerCtx { ++ pub fn has_cap(&self, cap: u64) -> bool { ++ self.caps & cap != 0 ++ } +@@ -822,0 +828 @@ impl CallerCtx { ++ caps: self.caps, +diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs +index a9de02ea..ee033988 100644 +--- a/src/scheme/proc.rs ++++ b/src/scheme/proc.rs +@@ -1275,0 +1276 @@ impl ContextHandle { ++ guard.caps = if info.euid == 0 { crate::scheme::caps::CAP_ALL } else { 0 }; +diff --git a/src/scheme/serio.rs b/src/scheme/serio.rs +index 26505021..b4ceab42 100644 +--- a/src/scheme/serio.rs ++++ b/src/scheme/serio.rs +@@ -82 +82 @@ impl KernelScheme for SerioScheme { +- if ctx.uid != 0 { ++ if !ctx.has_cap(caps::CAP_SERIO) { +diff --git a/src/scheme/sys/mod.rs b/src/scheme/sys/mod.rs +index 9eb35644..beed2ad5 100644 +--- a/src/scheme/sys/mod.rs ++++ b/src/scheme/sys/mod.rs +@@ -26,0 +27 @@ use super::{CallerCtx, HandleMap, KernelScheme, OpenResult, StrOrBytes}; ++use super::caps; +@@ -144 +145 @@ impl KernelScheme for SysScheme { +- if ctx.uid != 0 { ++ if !ctx.has_cap(caps::CAP_SYS_MSR) { +@@ -170 +171 @@ impl KernelScheme for SysScheme { +- if matches!(entry.1, Wr(_)) && ctx.uid != 0 { ++ if matches!(entry.1, Wr(_)) && !ctx.has_cap(caps::CAP_SYS_WRITE) { +diff --git a/src/scheme/user.rs b/src/scheme/user.rs +index dfbf66b1..48fda536 100644 +--- a/src/scheme/user.rs ++++ b/src/scheme/user.rs +@@ -29 +29 @@ use crate::{ +- scheme::SchemeId, ++ scheme::{caps, SchemeId}, +@@ -1593 +1593 @@ impl KernelScheme for UserScheme { +- if cx.euid != 0 && (uid != cx.euid || gid != cx.egid) { ++ if !cx.has_cap(caps::CAP_CHOWN) && (uid != cx.euid || gid != cx.egid) { +diff --git a/src/startup/mod.rs b/src/startup/mod.rs +index 86aabc22..4af65a37 100644 +--- a/src/startup/mod.rs ++++ b/src/startup/mod.rs +@@ -190,0 +191 @@ pub(crate) fn kmain(bootstrap: Bootstrap) -> ! { ++ context.caps = crate::scheme::caps::CAP_ALL; diff --git a/recipes/core/kernel/recipe.toml b/recipes/core/kernel/recipe.toml index 2957124a8d..facaf1e7e3 100644 --- a/recipes/core/kernel/recipe.toml +++ b/recipes/core/kernel/recipe.toml @@ -49,6 +49,8 @@ patches = [ "../../../local/patches/kernel/P25-cpuidle-deep-cstates.patch", # P26: DebugDisplay proper scrolling — ptr::copy rows up instead of wrapping to top "../../../local/patches/kernel/P26-debug-display-scroll.patch", + # P27: Capability bitmask — replace uid==0 checks with has_cap() capability model + "../../../local/patches/kernel/P27-capability-bitmask.patch", ] [build] diff --git a/recipes/core/kernel/source/src/context/context.rs b/recipes/core/kernel/source/src/context/context.rs index 6d723f498f..322cb30d70 100644 --- a/recipes/core/kernel/source/src/context/context.rs +++ b/recipes/core/kernel/source/src/context/context.rs @@ -147,6 +147,7 @@ pub struct Context { // TODO: Temporary replacement for existing kernel logic, replace with capabilities! pub euid: u32, pub egid: u32, + pub caps: u64, pub pid: usize, /// Supplementary group IDs for access control decisions. pub groups: Vec, @@ -205,6 +206,7 @@ impl Context { euid: 0, egid: 0, + caps: crate::scheme::caps::CAP_ALL, pid: 0, groups: Vec::new(), @@ -477,12 +479,16 @@ impl Context { (for_thread, for_proc, sig) } + pub fn has_cap(&self, cap: u64) -> bool { + self.caps & cap != 0 + } pub fn caller_ctx(&self) -> CallerCtx { CallerCtx { uid: self.euid, gid: self.egid, pid: self.pid, groups: self.groups.clone(), + caps: self.caps, } } } diff --git a/recipes/core/kernel/source/src/scheme/acpi.rs b/recipes/core/kernel/source/src/scheme/acpi.rs index 5d734691a9..1326ff57fc 100644 --- a/recipes/core/kernel/source/src/scheme/acpi.rs +++ b/recipes/core/kernel/source/src/scheme/acpi.rs @@ -13,6 +13,7 @@ use crate::{ arch::sleep, context::file::InternalFlags, event, + scheme::caps, sync::{CleanLockToken, RwLock, WaitCondition, L1}, }; @@ -139,7 +140,7 @@ impl KernelScheme for AcpiScheme { .or(Err(Error::new(EINVAL)))? .trim_start_matches('/'); - if ctx.uid != 0 { + if !ctx.has_cap(caps::CAP_ACPI) { return Err(Error::new(EACCES)); } if flags & O_CREAT == O_CREAT { diff --git a/recipes/core/kernel/source/src/scheme/caps.rs b/recipes/core/kernel/source/src/scheme/caps.rs new file mode 100644 index 0000000000..481f236914 --- /dev/null +++ b/recipes/core/kernel/source/src/scheme/caps.rs @@ -0,0 +1,11 @@ +pub const CAP_SCHEME_REGISTER: u64 = 1 << 0; +pub const CAP_PHYS_MEM: u64 = 1 << 1; +pub const CAP_IRQ: u64 = 1 << 2; +pub const CAP_ACPI: u64 = 1 << 3; +pub const CAP_SYS_DEBUG: u64 = 1 << 4; +pub const CAP_SYS_WRITE: u64 = 1 << 5; +pub const CAP_SYS_MSR: u64 = 1 << 6; +pub const CAP_SERIO: u64 = 1 << 7; +pub const CAP_CHOWN: u64 = 1 << 8; +pub const CAP_PROC_ATTR: u64 = 1 << 9; +pub const CAP_ALL: u64 = !0u64; diff --git a/recipes/core/kernel/source/src/scheme/debug.rs b/recipes/core/kernel/source/src/scheme/debug.rs index 4a23b3cf4f..2ab347d777 100644 --- a/recipes/core/kernel/source/src/scheme/debug.rs +++ b/recipes/core/kernel/source/src/scheme/debug.rs @@ -73,7 +73,7 @@ impl KernelScheme for DebugScheme { } let path = user_buf.as_str().or(Err(Error::new(EINVAL)))?; - if ctx.uid != 0 { + if !ctx.has_cap(caps::CAP_SYS_DEBUG) { return Err(Error::new(EPERM)); } diff --git a/recipes/core/kernel/source/src/scheme/irq.rs b/recipes/core/kernel/source/src/scheme/irq.rs index 4222960986..b1b893acc4 100644 --- a/recipes/core/kernel/source/src/scheme/irq.rs +++ b/recipes/core/kernel/source/src/scheme/irq.rs @@ -18,6 +18,7 @@ use syscall::{ use crate::context::file::InternalFlags; use super::{CallerCtx, HandleMap, OpenResult, SchemeExt, StrOrBytes}; +use super::caps; #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] use crate::arch::device::{ioapic, local_apic::ApicId}; @@ -256,7 +257,7 @@ impl crate::scheme::KernelScheme for IrqScheme { } let path = user_buf.as_str().or(Err(Error::new(EINVAL)))?; - if ctx.uid != 0 { + if !ctx.has_cap(caps::CAP_IRQ) { return Err(Error::new(EACCES)); } diff --git a/recipes/core/kernel/source/src/scheme/memory.rs b/recipes/core/kernel/source/src/scheme/memory.rs index c2f9f4747e..297545185e 100644 --- a/recipes/core/kernel/source/src/scheme/memory.rs +++ b/recipes/core/kernel/source/src/scheme/memory.rs @@ -9,6 +9,7 @@ use crate::{ memory::{handle_notify_files, AddrSpace, AddrSpaceWrapper, Grant, PageSpan}, }, memory::{free_frames, used_frames, Frame, VirtualAddress, PAGE_SIZE}, + scheme::caps, sync::CleanLockToken, syscall::{ data::{Map, StatVfs}, @@ -232,7 +233,7 @@ impl KernelScheme for MemoryScheme { .ok_or(Error::new(ENOENT))?; // TODO: Support arches with other default memory types? - if ctx.uid != 0 + if !ctx.has_cap(caps::CAP_PHYS_MEM) && (!flags.is_empty() || !matches!( (handle_ty, mem_ty), diff --git a/recipes/core/kernel/source/src/scheme/mod.rs b/recipes/core/kernel/source/src/scheme/mod.rs index 765e547f77..68bb8247f0 100644 --- a/recipes/core/kernel/source/src/scheme/mod.rs +++ b/recipes/core/kernel/source/src/scheme/mod.rs @@ -51,6 +51,7 @@ use self::{ }; /// When compiled with the "acpi" feature - `acpi:` - allows drivers to read a limited set of ACPI tables. +pub mod caps; pub mod acpi; pub mod dtb; @@ -353,7 +354,7 @@ impl KernelScheme for SchemeList { return Err(Error::new(EINVAL)); } - if caller.uid != 0 { + if !caller.has_cap(caps::CAP_SCHEME_REGISTER) { return Err(Error::new(EACCES)); }; @@ -811,8 +812,12 @@ pub struct CallerCtx { pub uid: u32, pub gid: u32, pub groups: alloc::vec::Vec, + pub caps: u64, } impl CallerCtx { + pub fn has_cap(&self, cap: u64) -> bool { + self.caps & cap != 0 + } pub fn filter_uid_gid(self, euid: u32, egid: u32) -> Self { if self.uid == 0 && self.gid == 0 { Self { @@ -820,6 +825,7 @@ impl CallerCtx { uid: euid, gid: egid, groups: self.groups, + caps: self.caps, } } else { self diff --git a/recipes/core/kernel/source/src/scheme/proc.rs b/recipes/core/kernel/source/src/scheme/proc.rs index a9de02ea1a..ee0339885c 100644 --- a/recipes/core/kernel/source/src/scheme/proc.rs +++ b/recipes/core/kernel/source/src/scheme/proc.rs @@ -1273,6 +1273,7 @@ impl ContextHandle { guard.pid = info.pid as usize; guard.euid = info.euid; guard.egid = info.egid; + guard.caps = if info.euid == 0 { crate::scheme::caps::CAP_ALL } else { 0 }; guard.prio = (info.prio as usize).min(39); Ok(size_of::()) } diff --git a/recipes/core/kernel/source/src/scheme/serio.rs b/recipes/core/kernel/source/src/scheme/serio.rs index 26505021ad..b4ceab4285 100644 --- a/recipes/core/kernel/source/src/scheme/serio.rs +++ b/recipes/core/kernel/source/src/scheme/serio.rs @@ -79,7 +79,7 @@ impl KernelScheme for SerioScheme { } let path = user_buf.as_str().or(Err(Error::new(EINVAL)))?; - if ctx.uid != 0 { + if !ctx.has_cap(caps::CAP_SERIO) { return Err(Error::new(EPERM)); } diff --git a/recipes/core/kernel/source/src/scheme/sys/mod.rs b/recipes/core/kernel/source/src/scheme/sys/mod.rs index 9eb3564411..beed2ad56c 100644 --- a/recipes/core/kernel/source/src/scheme/sys/mod.rs +++ b/recipes/core/kernel/source/src/scheme/sys/mod.rs @@ -24,6 +24,7 @@ use crate::{ }; use super::{CallerCtx, HandleMap, KernelScheme, OpenResult, StrOrBytes}; +use super::caps; mod block; mod context; @@ -141,7 +142,7 @@ impl KernelScheme for SysScheme { } else if path.starts_with("msr/") { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { - if ctx.uid != 0 { + if !ctx.has_cap(caps::CAP_SYS_MSR) { return Err(Error::new(EPERM)); } let rest = &path[4..]; @@ -167,7 +168,7 @@ impl KernelScheme for SysScheme { .find(|(entry_path, _)| *entry_path == path) .ok_or(Error::new(ENOENT))?; - if matches!(entry.1, Wr(_)) && ctx.uid != 0 { + if matches!(entry.1, Wr(_)) && !ctx.has_cap(caps::CAP_SYS_WRITE) { return Err(Error::new(EPERM)); } diff --git a/recipes/core/kernel/source/src/scheme/user.rs b/recipes/core/kernel/source/src/scheme/user.rs index dfbf66b1b1..48fda5369e 100644 --- a/recipes/core/kernel/source/src/scheme/user.rs +++ b/recipes/core/kernel/source/src/scheme/user.rs @@ -26,7 +26,7 @@ use crate::{ }, event, memory::{Frame, Page, VirtualAddress, PAGE_SIZE}, - scheme::SchemeId, + scheme::{caps, SchemeId}, sync::{CleanLockToken, LockToken, Mutex, RwLock, WaitQueue, L1}, syscall::{ data::{Map, StdFsCallMeta}, @@ -1590,7 +1590,7 @@ impl KernelScheme for UserScheme { { let ctx = context::current(); let cx = &ctx.read(token.token()); - if cx.euid != 0 && (uid != cx.euid || gid != cx.egid) { + if !cx.has_cap(caps::CAP_CHOWN) && (uid != cx.euid || gid != cx.egid) { return Err(Error::new(EPERM)); } } diff --git a/recipes/core/kernel/source/src/startup/mod.rs b/recipes/core/kernel/source/src/startup/mod.rs index 86aabc227a..4af65a3737 100644 --- a/recipes/core/kernel/source/src/startup/mod.rs +++ b/recipes/core/kernel/source/src/startup/mod.rs @@ -188,6 +188,7 @@ pub(crate) fn kmain(bootstrap: Bootstrap) -> ! { // TODO: Remove these from kernel context.euid = 0; context.egid = 0; + context.caps = crate::scheme::caps::CAP_ALL; } Err(_err) => halt_boot("FATAL: failed to spawn first userspace process userspace_init\n"), }