Merge branch 'su_no_setuid' into 'master'

Use the sudo daemon rather than setuid for su

See merge request redox-os/userutils!53
This commit is contained in:
Jeremy Soller
2025-04-20 15:22:33 +00:00
3 changed files with 34 additions and 17 deletions
+13 -9
View File
@@ -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}"),
}
}
}
+20 -7
View File
@@ -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<OpenResult> {
// 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));
+1 -1
View File
@@ -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<auth::Full>) -> IoResult<i32> {
pub fn spawn_shell<T: Default>(user: &User<T>) -> IoResult<i32> {
let mut command = user.shell_cmd();
let mut child = command.spawn()?;