From 9c53a9a77516eb23efb0be60203568087045b2c1 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 20 Apr 2025 16:46:50 +0200 Subject: [PATCH] Use the sudo daemon rather than setuid for su --- src/bin/su.rs | 22 +++++++++++++--------- src/bin/sudo.rs | 27 ++++++++++++++++++++------- src/lib.rs | 2 +- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/bin/su.rs b/src/bin/su.rs index 51bc496a2f..cbf939e12d 100644 --- a/src/bin/su.rs +++ b/src/bin/su.rs @@ -6,7 +6,9 @@ use std::process::exit; use std::str; use extra::option::OptionalExt; +use libc::O_CLOEXEC; use redox_users::{All, AllUsers, Config, get_uid}; +use syscall::EPERM; use termion::input::TermRead; use userutils::spawn_shell; @@ -50,15 +52,17 @@ pub fn main() { let uid = get_uid().unwrap_or_exit(1); - let users = AllUsers::authenticator(Config::default()).unwrap_or_exit(1); + let users = AllUsers::basic(Config::default()).unwrap_or_exit(1); let user = users.get_by_name(&target_user).unwrap_or_exit(1); // If the user executing su is root, then they can do anything without a password. // Same if the user we're being asked to login as doesn't have a password. - if uid == 0 || user.is_passwd_blank() { + if uid == 0 { writeln!(stdout).unwrap_or_exit(1); exit(spawn_shell(user).unwrap_or_exit(1)); } else { + let file = libredox::call::open("/scheme/sudo/su", O_CLOEXEC, 0).unwrap(); + write!(stdout, "password: ").unwrap_or_exit(1); stdout.flush().unwrap_or_exit(1); @@ -68,13 +72,13 @@ pub fn main() { .r#try(&mut stderr) .unwrap_or(String::new()); - writeln!(stderr, "\n").unwrap_or_exit(1); - - if user.verify_passwd(&password) { - exit(spawn_shell(user).unwrap_or_exit(1)); - } else { - writeln!(stderr, "su: authentication failed").unwrap_or_exit(1); - exit(1); + match libredox::call::write(file, password.as_bytes()) { + Ok(_) => exit(spawn_shell(user).unwrap_or_exit(1)), + Err(err) if err.errno() == EPERM => { + writeln!(stderr, "su: authentication failed").unwrap_or_exit(1); + exit(1); + } + Err(err) => panic!("{err}"), } } } diff --git a/src/bin/sudo.rs b/src/bin/sudo.rs index 61e4392e6e..8e15b27dea 100644 --- a/src/bin/sudo.rs +++ b/src/bin/sudo.rs @@ -42,7 +42,7 @@ AUTHOR Written by Jeremy Soller, Jose Narvaez, bjorn3. "#; /* @MANEND */ -pub fn main() { +fn main() { if env::args().nth(1).as_deref() == Some("--daemon") { daemon_main(); } @@ -150,20 +150,21 @@ struct Scheme { } enum Handle { AwaitingPassword { uid: u32 }, + AwaitingRootPassword, AwaitingContextFd, Placeholder, } impl SchemeSync for Scheme { fn open(&mut self, path: &str, _flags: usize, ctx: &CallerCtx) -> Result { - // Path must be empty - if path.trim_start_matches('/') != "" { - return Err(Error::new(ENOENT)); - } let fd = self.next_fd; self.next_fd = self.next_fd.checked_add(1).ok_or(Error::new(EMFILE))?; - self.handles - .insert(fd, Handle::AwaitingPassword { uid: ctx.uid }); + let handle = match path { + "" => Handle::AwaitingPassword { uid: ctx.uid }, + "su" => Handle::AwaitingRootPassword, + _ => return Err(Error::new(ENOENT)), + }; + self.handles.insert(fd, handle); Ok(OpenResult::ThisScheme { number: fd, @@ -204,6 +205,18 @@ impl SchemeSync for Scheme { } } } + Handle::AwaitingRootPassword => { + let users = AllUsers::authenticator(Config::default()).unwrap_or_exit(1); + let user = users.get_by_id(0).unwrap_or_exit(1); + + let password = validate_utf8(buf)?; + if user.verify_passwd(&password) { + *handle = Handle::AwaitingContextFd + } else { + *handle = Handle::AwaitingRootPassword; + return Err(Error::new(EPERM)); + } + } Handle::AwaitingContextFd => { *handle = Handle::AwaitingContextFd; return Err(Error::new(EINVAL)); diff --git a/src/lib.rs b/src/lib.rs index c0a7fb5d47..2789547063 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,7 +71,7 @@ impl AllGroupsExt for AllGroups { /// let user = sys_users.get_by_name("goyox86"); /// spawn_shell(user).unwrap(); /// ``` -pub fn spawn_shell(user: &User) -> IoResult { +pub fn spawn_shell(user: &User) -> IoResult { let mut command = user.shell_cmd(); let mut child = command.spawn()?;