passwd: Remove suid usage though the sudo daemon

This commit is contained in:
bjorn3
2025-04-20 21:10:22 +02:00
parent cacb0dd67e
commit 4ed8323351
2 changed files with 82 additions and 21 deletions
+47 -21
View File
@@ -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>(
+35
View File
@@ -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));