Skip to content

Commit

Permalink
rust/check_linux_pass: drop error-chain
Browse files Browse the repository at this point in the history
error-chain seems to be deprecated
(rust-lang-deprecated/failure#181), but its
"successor", failure, is not in good shape either. Following guidance
from https://blog.burntsushi.net/rust-error-handling/, use Rust's
standard library error managment system.
  • Loading branch information
fishilico committed Aug 25, 2019
1 parent 5763d86 commit f63031a
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 29 deletions.
3 changes: 1 addition & 2 deletions rust/check_linux_pass/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = "~2.33"
error-chain = "0.12"
clap = {version = "2.33", default-features = false}
pwhash = "0.3"
termios = "0.3"
users = "0.9"
105 changes: 78 additions & 27 deletions rust/check_linux_pass/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,90 @@ extern crate pwhash;
extern crate termios;
extern crate users;

#[macro_use]
extern crate error_chain;

use clap::{App, Arg};
use std::error;
use std::fmt;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Write;

error_chain! {
foreign_links {
PwHash(pwhash::error::Error);
Io(std::io::Error);
use std::io::{self, BufRead, BufReader, Write};
use std::path::Path;
use termios::{Termios, tcsetattr};

#[derive(Debug)]
enum Error {
Io(io::Error),
PwHash(pwhash::error::Error),
Str(String),
}

impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::Io(err)
}
}

impl From<pwhash::error::Error> for Error {
fn from(err: pwhash::error::Error) -> Error {
Error::PwHash(err)
}
}

impl From<String> for Error {
fn from(err: String) -> Error {
Error::Str(err)
}
}

impl From<&str> for Error {
fn from(err: &str) -> Error {
Error::Str(err.to_owned())
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Io(ref err) => write!(f, "IO error: {}", err),
Error::PwHash(ref err) => write!(f, "PwHash error: {}", err),
Error::Str(ref err) => f.write_str(err),
}
}
}

impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::Io(ref err) => err.description(),
Error::PwHash(ref err) => error::Error::description(err),
Error::Str(ref err) => err,
}
}

fn cause(&self) -> Option<&dyn error::Error> {
match *self {
Error::Io(ref err) => Some(err),
Error::PwHash(ref err) => Some(err),
Error::Str(_) => None,
}
}
}

/// Prompt the password of a user
fn prompt_password() -> Result<String> {
fn prompt_password() -> Result<String, Error> {
// Disable ECHO but echo the new line character
let initial_term = termios::Termios::from_fd(0)?;
let initial_term = Termios::from_fd(0)?;
let mut term = initial_term;
term.c_lflag &= !termios::ECHO;
term.c_lflag |= termios::ECHONL;
termios::tcsetattr(0, termios::TCSANOW, &term)?;
tcsetattr(0, termios::TCSANOW, &term)?;

let mut password_line = String::new();
eprint!("Password: ");
let result = std::io::stderr().flush().and_then(|_| {
std::io::stdin().read_line(&mut password_line)
let result = io::stderr().flush().and_then(|_| {
io::stdin().read_line(&mut password_line)
});

// Reset the initial terminal before returning a failure
termios::tcsetattr(0, termios::TCSANOW, &initial_term)?;
tcsetattr(0, termios::TCSANOW, &initial_term)?;

result?;

Expand All @@ -47,11 +98,11 @@ fn prompt_password() -> Result<String> {
}

/// Check a password using a `/etc/shadow` file
fn check_password_in_shadow(
shadow_path: &str,
fn check_password_in_shadow<P: AsRef<Path>>(
shadow_path: P,
user: &str,
password_opt: Option<&str>,
) -> Result<()> {
) -> Result<(), Error> {
let mut is_found = false;
let mut prompted_password = None;
let file = File::open(shadow_path)?;
Expand Down Expand Up @@ -87,17 +138,17 @@ fn check_password_in_shadow(
}
}
if !is_found {
bail!("user not found in shadow file")
return Err(Error::Str("user not found in shadow file".to_owned()));
} else {
bail!("incorrect password")
return Err(Error::Str("incorrect password".to_owned()));
}
}

/// Check a password using `unix_chkpwd` helper
///
/// The source code of the helper is
/// [on GitHub](https://github.com/linux-pam/linux-pam/blob/v1.3.1/modules/pam_unix/unix_chkpwd.c)
fn check_password_with_helper(user: &str, password_opt: Option<&str>) -> Result<()> {
fn check_password_with_helper(user: &str, password_opt: Option<&str>) -> Result<(), Error> {
// Find unix_chkpwd
let mut unix_chkpwd_path_opt = None;
for path_dir in &["/bin", "/sbin", "/usr/bin", "/usr/sbin"] {
Expand Down Expand Up @@ -134,16 +185,16 @@ fn check_password_with_helper(user: &str, password_opt: Option<&str>) -> Result<
let exit_status = child.wait()?;
if !exit_status.success() {
if exit_status.code() == Some(7) {
bail!("incorrect password")
return Err(Error::Str("incorrect password".to_owned()));
} else {
bail!("unknown exit status ({})", exit_status)
return Err(Error::Str(format!("unknown exit status ({})", exit_status)));
}
}
println!("The password is correct :)");
Ok(())
}

fn main_with_result() -> Result<()> {
fn main_with_result() -> Result<(), Error> {
let matches = App::new("CheckLinuxPass")
.version("0.1.0")
.author("Nicolas Iooss")
Expand Down Expand Up @@ -192,7 +243,7 @@ fn main_with_result() -> Result<()> {

fn main() {
if let Err(err) = main_with_result() {
println!("Error: {}", err);
eprintln!("Error: {}", err);
std::process::exit(1);
}
}

0 comments on commit f63031a

Please sign in to comment.