diff --git a/src/bin/passwd.rs b/src/bin/passwd.rs index ef88106d48..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,20 +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); - 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); - }), - }; + if uid == 0 { + let mut users = + AllUsers::authenticator(Config::default().writeable(true)).unwrap_or_exit(1); - if user.uid != uid && uid != 0 { + 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 { eprintln!( "passwd: you do not have permission to set the password of '{}'", user.user @@ -102,30 +113,64 @@ fn main() { stdout.write_all(&msg.as_bytes()).r#try(&mut stderr); stdout.flush().r#try(&mut stderr); - let mut verified = false; - if user.is_passwd_blank() { - verified = true; - } else if user.is_passwd_unset() && uid != 0 { - verified = false; - } else if user.uid == uid || uid != 0 { - 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}"), } } else { - verified = true; - } - - if !verified { eprintln!("passwd: incorrect current password"); exit(1); } + let new_password = ask_new_password(stdin, stdout, stderr); + + 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>( + 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<'_>, + 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 +192,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 } 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));