From 3bdd1d9a6e531bf80fbbf3f7f91a02c75a581a15 Mon Sep 17 00:00:00 2001 From: Ibuki Omatsu Date: Wed, 21 Jan 2026 03:58:27 +0000 Subject: [PATCH] Adapt sudo and getty to new namespace management, and enforce strict namespace at login. --- Cargo.lock | 148 +++++++++++++++++++++++++++++++++++++++++++---- Cargo.toml | 15 ++--- src/bin/login.rs | 98 ++++++++++++++++++++++++++++++- src/bin/sudo.rs | 35 ++++++++++- 4 files changed, 275 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 409c25da39..b43cd4a762 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,6 +108,12 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "extra" version = "0.1.0" @@ -116,7 +122,7 @@ source = "git+https://gitlab.redox-os.org/redox-os/libextra.git#cf213969493db866 [[package]] name = "generic-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc#250f072973b96ee2d2ab921b7c2114b5178ec4d1" +source = "git+https://gitlab.redox-os.org/redox-os/relibc#3a8f64aa143184dc11fe1927a4f9e55c33ae3052" [[package]] name = "getrandom" @@ -140,6 +146,12 @@ dependencies = [ "scroll", ] +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -149,6 +161,26 @@ dependencies = [ "libc", ] +[[package]] +name = "indexmap" +version = "2.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ioslice" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e571352c8a3b89074d12e3ee5173ffe162159105352aaaf1fc5764da747e31b" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "itertools" version = "0.13.0" @@ -172,11 +204,12 @@ checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" [[package]] name = "libredox" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ "bitflags 2.9.4", + "ioslice", "libc", "redox_syscall", ] @@ -244,11 +277,12 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" [[package]] name = "redox-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc#250f072973b96ee2d2ab921b7c2114b5178ec4d1" +source = "git+https://gitlab.redox-os.org/redox-os/relibc#3a8f64aa143184dc11fe1927a4f9e55c33ae3052" dependencies = [ "bitflags 2.9.4", "generic-rt", "goblin", + "ioslice", "plain", "redox-path", "redox_syscall", @@ -256,9 +290,9 @@ dependencies = [ [[package]] name = "redox-scheme" -version = "0.8.3" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cea2668a5932f878a4298a1d7f8950249bbbb77120fb263da252c589152f5ea" +checksum = "5eb0cee524a5c6e4db180250f699e101ee25f3f79ad2618527e539e028cb6722" dependencies = [ "libredox", "redox_syscall", @@ -266,9 +300,9 @@ dependencies = [ [[package]] name = "redox_event" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69609faa5d5992247a4ef379917bb3e39be281405d6a0ccd4f942429400b956f" +checksum = "c30b73c93693667b5a8e6f54bddcc5a57449ef17c99f4b55c12d5bae4ab79e82" dependencies = [ "bitflags 2.9.4", "libredox", @@ -289,8 +323,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.6.0" -source = "git+https://gitlab.redox-os.org/redox-os/syscall.git#2d11366fd160ec1dd3918164fa324275e1bab070" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" dependencies = [ "bitflags 2.9.4", ] @@ -369,6 +404,45 @@ dependencies = [ "version-compare", ] +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "strip-ansi-escapes" version = "0.2.1" @@ -436,6 +510,47 @@ dependencies = [ "syn", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "unicode-ident" version = "1.0.19" @@ -454,6 +569,8 @@ version = "0.1.0" dependencies = [ "clap", "extra", + "ioslice", + "libc", "libredox", "orbclient", "plain", @@ -464,7 +581,9 @@ dependencies = [ "redox_syscall", "redox_termios", "redox_users", + "serde", "termion", + "toml", ] [[package]] @@ -516,6 +635,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +dependencies = [ + "memchr", +] + [[package]] name = "zeroize" version = "1.8.2" diff --git a/Cargo.toml b/Cargo.toml index bccdad39a7..abfc7aedb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,19 +57,20 @@ extra = { git = "https://gitlab.redox-os.org/redox-os/libextra.git" } orbclient = "0.3.47" plain = "0.2.3" redox_liner = "0.5.2" -libredox = "0.1.3" +libredox = { version = "0.1.12", features = ["mkns"] } redox_termios = "0.1.3" -redox_event = "0.4" -redox-scheme = "0.8.3" -redox_syscall = "0.6" +redox_event = "0.4.3" +redox-scheme = "0.9.0" +redox_syscall = "0.7.0" redox_users = "0.4.6" termion = "4" +libc = "0.2" +serde = { version = "1.0.203", features = ["derive"] } +toml = "0.8.11" +ioslice = "0.6" [target.'cfg(target_os = "redox")'.dependencies] redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc", default-features = false } -[patch.crates-io] -redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" } - [profile.release] lto = true diff --git a/src/bin/login.rs b/src/bin/login.rs index 6bbd59cafa..08e178c7cb 100644 --- a/src/bin/login.rs +++ b/src/bin/login.rs @@ -1,12 +1,13 @@ #[macro_use] extern crate clap; +use libredox::error::Result; use std::fs::File; use std::io::{self, Write}; use std::str; use extra::option::OptionalExt; -use redox_users::{All, AllUsers, Config}; +use redox_users::{All, AllUsers, Config, User}; use termion::input::TermRead; use userutils::spawn_shell; @@ -33,6 +34,89 @@ AUTHOR const ISSUE_FILE: &'static str = "/etc/issue"; const MOTD_FILE: &'static str = "/etc/motd"; +// TODO: Move to redox_users once the definition solidifies. +const DEFAULT_SCHEMES: [&'static str; 26] = [ + // Kernel schemes + "debug", + "event", + "memory", + "pipe", + "serio", + "irq", + "time", + "sys", + // Base schemes + "rand", + "null", + "zero", + "log", + // Network schemes + "ip", + "icmp", + "tcp", + "udp", + // IPC schemes + "shm", + "chan", + "uds_stream", + "uds_dgram", + // File schemes + "file", + // Display schemes + "display.vesa", + "display*", + // Other schemes + "pty", + "sudo", + "audio", +]; +pub fn apply_login_schemes( + user: &User, + default_schemes: &[&str], +) -> Result { + let schemes = match load_config_schemes(user) { + Some(s) => s, + _ => default_schemes.iter().map(|s| s.to_string()).collect(), + }; + + let mut names: Vec = Vec::with_capacity(schemes.len()); + for scheme in schemes.iter() { + names.push(ioslice::IoSlice::new(scheme.as_bytes())); + } + + let ns_fd = libredox::call::mkns(&names)?; + let before_ns_fd = libredox::Fd::new(libredox::call::setns(ns_fd)?); + + Ok(before_ns_fd) +} + +fn load_config_schemes(user: &User) -> Option> { + use serde::{Deserialize, Serialize}; + use std::collections::BTreeMap; + use std::fs; + + const LOGIN_SCHEMES_FILE: &'static str = "/etc/login_schemes.toml"; + + #[derive(Debug, Clone, Serialize, Deserialize)] + struct UserSchemeConfig { + pub schemes: Vec, + } + + #[derive(Debug, Clone, Serialize, Deserialize)] + struct LoginConfig { + #[serde(rename = "user_schemes")] + pub user_schemes: BTreeMap, + } + + let config_str = fs::read_to_string(LOGIN_SCHEMES_FILE).ok()?; + let config: LoginConfig = toml::from_str(&config_str).ok()?; + + config + .user_schemes + .get(&user.user) + .map(|cfg| cfg.schemes.clone()) +} + pub fn main() { let mut stdout = io::stdout(); let mut stderr = io::stderr(); @@ -76,7 +160,19 @@ pub fn main() { stdout.flush().r#try(&mut stderr); } + let before_ns_fd = + apply_login_schemes(user, &DEFAULT_SCHEMES).unwrap_or_exit(1); + + let _ = syscall::fcntl( + before_ns_fd.raw(), + syscall::F_SETFD, + syscall::O_CLOEXEC, + ); spawn_shell(user).unwrap_or_exit(1); + let _ = syscall::fcntl(before_ns_fd.raw(), syscall::F_SETFD, 0); + let _ = libredox::call::close( + libredox::call::setns(before_ns_fd.into_raw()).unwrap_or_exit(1), + ); break; } diff --git a/src/bin/sudo.rs b/src/bin/sudo.rs index 1a7b8fe802..25ec3b04e5 100644 --- a/src/bin/sudo.rs +++ b/src/bin/sudo.rs @@ -9,7 +9,7 @@ use extra::option::OptionalExt; use libredox::flag::O_CLOEXEC; use redox_rt::protocol::ProcCall; use redox_rt::sys::proc_call; -use redox_scheme::scheme::SchemeSync; +use redox_scheme::scheme::{SchemeSync, register_sync_scheme}; use redox_scheme::{ CallerCtx, OpenResult, RequestKind, Response, SendFdRequest, SignalBehavior, Socket, }; @@ -157,10 +157,31 @@ enum Handle { AwaitingNewPassword { uid: u32 }, Placeholder, + + SchemeRoot, } impl SchemeSync for Scheme { - fn open(&mut self, path: &str, _flags: usize, ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + let fd = self.next_fd; + self.next_fd = self.next_fd.checked_add(1).ok_or(Error::new(EMFILE))?; + self.handles.insert(fd, Handle::SchemeRoot); + Ok(fd) + } + fn openat( + &mut self, + dirfd: usize, + path: &str, + _flags: usize, + _fcntl_flags: u32, + ctx: &CallerCtx, + ) -> Result { + if !matches!( + self.handles.get(&dirfd).ok_or(Error::new(EBADF))?, + Handle::SchemeRoot + ) { + return Err(Error::new(EACCES)); + } let fd = self.next_fd; self.next_fd = self.next_fd.checked_add(1).ok_or(Error::new(EMFILE))?; let handle = match path { @@ -261,6 +282,11 @@ impl SchemeSync for Scheme { eprintln!("sudo: found placeholder handle with ID {id}"); return Err(Error::new(EBADFD)); } + + Handle::SchemeRoot => { + eprintln!("sudo: found Scheme root handle with ID {id}"); + return Err(Error::new(EBADFD)); + } } Ok(buf.len()) } @@ -309,13 +335,16 @@ impl Scheme { fn daemon_main() -> ! { // TODO: Linux kernel audit-like logging? - let socket = Socket::create("sudo").expect("failed to open scheme socket"); + let socket = Socket::create().expect("failed to open scheme socket"); let mut scheme = Scheme { next_fd: 1, handles: HashMap::new(), }; + register_sync_scheme(&socket, "sudo", &mut scheme) + .expect("failed to register sudo scheme to namespace"); + loop { let Some(req) = socket .next_request(SignalBehavior::Restart)