From f557417ebf5f1a251dff1a3350b09a58bf792632 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 20 Apr 2025 20:40:37 +0200 Subject: [PATCH 1/4] passwd: Extract ask_new_password --- src/bin/passwd.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/bin/passwd.rs b/src/bin/passwd.rs index ef88106d48..7acce687f6 100644 --- a/src/bin/passwd.rs +++ b/src/bin/passwd.rs @@ -126,6 +126,17 @@ fn main() { exit(1); } + let new_password = ask_new_password(stdin, stdout, stderr); + + user.set_passwd(&new_password).unwrap_or_exit(1); + users.save().unwrap_or_exit(1); +} + +fn ask_new_password( + mut stdin: io::StdinLock<'_>, + mut stdout: io::StdoutLock<'_>, + mut stderr: io::Stderr, +) -> String { stdout.write_all(b"new password: ").r#try(&mut stderr); stdout.flush().r#try(&mut stderr); let Some(new_password) = stdin.read_passwd(&mut stdout).r#try(&mut stderr) else { @@ -147,7 +158,5 @@ fn main() { eprintln!("passwd: new password does not match confirm password"); exit(1); } - - user.set_passwd(&new_password).unwrap_or_exit(1); - users.save().unwrap_or_exit(1); + new_password } From d514768aae1f15390c9cbec7cd927bedae7373a3 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 20 Apr 2025 20:48:14 +0200 Subject: [PATCH 2/4] passwd: Allow root to always change passwords Previously root would need to know its own password. On Linux root can change its own password without needing to enter its own password. It can do so on Redox OS already anyway by directly editing /etc/shadow. --- src/bin/passwd.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bin/passwd.rs b/src/bin/passwd.rs index 7acce687f6..daa86512f8 100644 --- a/src/bin/passwd.rs +++ b/src/bin/passwd.rs @@ -103,11 +103,13 @@ fn main() { stdout.flush().r#try(&mut stderr); let mut verified = false; - if user.is_passwd_blank() { + if uid == 0 { verified = true; - } else if user.is_passwd_unset() && uid != 0 { + } else if user.is_passwd_blank() { + verified = true; + } else if user.is_passwd_unset() { verified = false; - } else if user.uid == uid || uid != 0 { + } else { stdout.write_all(b"current password: ").r#try(&mut stderr); stdout.flush().r#try(&mut stderr); @@ -117,8 +119,6 @@ fn main() { verified = user.verify_passwd(&password) } - } else { - verified = true; } if !verified { From cacb0dd67e4dcb0b6412a6b242aad3d1fe6010b9 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 20 Apr 2025 20:54:00 +0200 Subject: [PATCH 3/4] passwd: Extract find_user --- src/bin/passwd.rs | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/bin/passwd.rs b/src/bin/passwd.rs index daa86512f8..4a8d063ab3 100644 --- a/src/bin/passwd.rs +++ b/src/bin/passwd.rs @@ -79,16 +79,7 @@ fn main() { let uid = get_uid().unwrap_or_exit(1); let mut users = AllUsers::authenticator(Config::default().writeable(true)).unwrap_or_exit(1); - let user = match args.value_of("LOGIN") { - Some(login) => users.get_mut_by_name(login).unwrap_or_else(|| { - eprintln!("passwd: user does not exist: {}", login); - exit(1); - }), - None => users.get_mut_by_id(uid).unwrap_or_else(|| { - eprintln!("passwd: you do not exist"); - exit(1); - }), - }; + let user = find_user(&args, &mut users); if user.uid != uid && uid != 0 { eprintln!( @@ -132,6 +123,23 @@ fn main() { users.save().unwrap_or_exit(1); } +fn find_user<'a, T: Default>( + args: &clap::ArgMatches<'_>, + users: &'a mut AllUsers, +) -> &'a mut redox_users::User { + let uid = get_uid().unwrap_or_exit(1); + match args.value_of("LOGIN") { + Some(login) => users.get_mut_by_name(login).unwrap_or_else(|| { + eprintln!("passwd: user does not exist: {}", login); + exit(1); + }), + None => users.get_mut_by_id(uid).unwrap_or_else(|| { + eprintln!("passwd: you do not exist"); + exit(1); + }), + } +} + fn ask_new_password( mut stdin: io::StdinLock<'_>, mut stdout: io::StdoutLock<'_>, From 4ed8323351c31e308b770b3afbd0ac19c8ed4bbf Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 20 Apr 2025 21:10:22 +0200 Subject: [PATCH 4/4] passwd: Remove suid usage though the sudo daemon --- src/bin/passwd.rs | 68 ++++++++++++++++++++++++++++++++--------------- src/bin/sudo.rs | 35 ++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 21 deletions(-) diff --git a/src/bin/passwd.rs b/src/bin/passwd.rs index 4a8d063ab3..b2102e7929 100644 --- a/src/bin/passwd.rs +++ b/src/bin/passwd.rs @@ -6,6 +6,7 @@ use std::io::Write; use std::process::exit; use extra::option::OptionalExt; +use libc::{EPERM, O_CLOEXEC}; use redox_users::{All, AllUsers, Config, get_uid}; use termion::input::TermRead; @@ -77,11 +78,30 @@ fn main() { } let uid = get_uid().unwrap_or_exit(1); - let mut users = AllUsers::authenticator(Config::default().writeable(true)).unwrap_or_exit(1); + + if uid == 0 { + let mut users = + AllUsers::authenticator(Config::default().writeable(true)).unwrap_or_exit(1); + + let user = find_user(&args, &mut users); + + let msg = format!("changing password for '{}' \n", user.user); + stdout.write_all(&msg.as_bytes()).r#try(&mut stderr); + stdout.flush().r#try(&mut stderr); + + let new_password = ask_new_password(stdin, stdout, stderr); + + user.set_passwd(&new_password).unwrap_or_exit(1); + users.save().unwrap_or_exit(1); + + return; + } + + let mut users = AllUsers::basic(Config::default()).unwrap_or_exit(1); let user = find_user(&args, &mut users); - if user.uid != uid && uid != 0 { + if user.uid != uid { eprintln!( "passwd: you do not have permission to set the password of '{}'", user.user @@ -93,34 +113,40 @@ fn main() { stdout.write_all(&msg.as_bytes()).r#try(&mut stderr); stdout.flush().r#try(&mut stderr); - let mut verified = false; - if uid == 0 { - verified = true; - } else if user.is_passwd_blank() { - verified = true; - } else if user.is_passwd_unset() { - verified = false; - } else { - stdout.write_all(b"current password: ").r#try(&mut stderr); + drop(users); // Unlock /etc/passwd + + stdout.write_all(b"current password: ").r#try(&mut stderr); + stdout.flush().r#try(&mut stderr); + + let file = libredox::call::open("/scheme/sudo/passwd", O_CLOEXEC, 0).unwrap(); + + if let Some(password) = stdin.read_passwd(&mut stdout).r#try(&mut stderr) { + stdout.write(b"\n").r#try(&mut stderr); stdout.flush().r#try(&mut stderr); - if let Some(password) = stdin.read_passwd(&mut stdout).r#try(&mut stderr) { - stdout.write(b"\n").r#try(&mut stderr); - stdout.flush().r#try(&mut stderr); - - verified = user.verify_passwd(&password) + match libredox::call::write(file, password.as_bytes()) { + Ok(_) => {} + Err(err) if err.errno() == EPERM => { + eprintln!("passwd: incorrect current password"); + exit(1); + } + Err(err) => panic!("{err}"), } - } - - if !verified { + } else { eprintln!("passwd: incorrect current password"); exit(1); } let new_password = ask_new_password(stdin, stdout, stderr); - user.set_passwd(&new_password).unwrap_or_exit(1); - users.save().unwrap_or_exit(1); + match libredox::call::write(file, new_password.as_bytes()) { + Ok(_) => {} + Err(err) if err.errno() == EPERM => { + eprintln!("passwd: invalid new password"); + exit(1); + } + Err(err) => panic!("{err}"), + } } fn find_user<'a, T: Default>( diff --git a/src/bin/sudo.rs b/src/bin/sudo.rs index 8e15b27dea..6c8cb6d5e5 100644 --- a/src/bin/sudo.rs +++ b/src/bin/sudo.rs @@ -152,6 +152,10 @@ enum Handle { AwaitingPassword { uid: u32 }, AwaitingRootPassword, AwaitingContextFd, + + AwaitingPasswordForPasswd { uid: u32 }, + AwaitingNewPassword { uid: u32 }, + Placeholder, } @@ -162,6 +166,7 @@ impl SchemeSync for Scheme { let handle = match path { "" => Handle::AwaitingPassword { uid: ctx.uid }, "su" => Handle::AwaitingRootPassword, + "passwd" => Handle::AwaitingPasswordForPasswd { uid: ctx.uid }, _ => return Err(Error::new(ENOENT)), }; self.handles.insert(fd, handle); @@ -222,6 +227,36 @@ impl SchemeSync for Scheme { return Err(Error::new(EINVAL)); } + Handle::AwaitingPasswordForPasswd { uid } => { + let users = + AllUsers::authenticator(Config::default()).map_err(|_| Error::new(ENOLCK))?; + let user = users.get_by_id(uid as usize).ok_or(Error::new(EEXIST))?; + + let password = validate_utf8(buf)?; + if user.verify_passwd(&password) { + *handle = Handle::AwaitingNewPassword { uid } + } else { + *handle = Handle::AwaitingPasswordForPasswd { uid }; + return Err(Error::new(EPERM)); + } + } + Handle::AwaitingNewPassword { uid } => { + let mut users = AllUsers::authenticator(Config::default().writeable(true)) + .map_err(|_| Error::new(ENOLCK))?; + let user = users + .get_mut_by_id(uid as usize) + .ok_or(Error::new(EEXIST))?; + + let new_password = validate_utf8(buf)?; + if user.set_passwd(&new_password).is_ok() { + users.save().map_err(|_| Error::new(ENOLCK))?; + *handle = Handle::Placeholder + } else { + *handle = Handle::AwaitingNewPassword { uid }; + return Err(Error::new(EPERM)); + } + } + Handle::Placeholder => { eprintln!("sudo: found placeholder handle with ID {id}"); return Err(Error::new(EBADFD));