From 59f99619dac63869fc08f8b79956e648d4b3ec73 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Mon, 29 Aug 2022 06:30:17 +0200 Subject: [PATCH] Add global options --password and --password-command --- src/commands/helpers.rs | 52 +++++++++++++++++++++++++++-------------- src/commands/mod.rs | 33 ++++++++++++++++++++++++-- 2 files changed, 66 insertions(+), 19 deletions(-) diff --git a/src/commands/helpers.rs b/src/commands/helpers.rs index 12b8a5b..d45bf99 100644 --- a/src/commands/helpers.rs +++ b/src/commands/helpers.rs @@ -1,7 +1,7 @@ use std::fmt::Write; use std::fs::File; use std::io::BufReader; -use std::path::PathBuf; +use std::path::Path; use std::process::Command; use std::time::Duration; @@ -25,25 +25,43 @@ pub fn bytes(b: u64) -> String { ByteSize(b).to_string_as(true) } -pub async fn get_key(be: &impl ReadBackend, password_file: Option) -> Result { - match password_file { - None => { - for _i in 0..MAX_PASSWORD_RETRIES { - let pass = prompt_password("enter repository password: ")?; - if let Ok(key) = find_key_in_backend(be, &pass, None).await { - ve1!("password is correct"); +pub async fn get_key( + be: &impl ReadBackend, + password: Option<&str>, + password_file: Option<&Path>, + password_command: Option<&str>, +) -> Result { + let password = match (password, password_file, password_command) { + (Some(pwd), _, _) => Some(pwd.to_string()), + (_, Some(file), _) => { + let mut file = BufReader::new(File::open(file)?); + Some(read_password_from_bufread(&mut file)?) + } + (_, _, Some(command)) => { + let mut commands: Vec<_> = command.split(' ').collect(); + let output = Command::new(commands[0]) + .args(&mut commands[1..]) + .output()?; + + let mut pwd = BufReader::new(&*output.stdout); + Some(read_password_from_bufread(&mut pwd)?) + } + (None, None, None) => None, + }; + + for _ in 0..MAX_PASSWORD_RETRIES { + match &password { + // if password is given, directly return the result of find_key_in_backend and don't retry + Some(pass) => return find_key_in_backend(be, pass, None).await, + None => { + // TODO: Differentiate between wrong password and other error! + if let Ok(key) = + find_key_in_backend(be, &prompt_password("enter repository password: ")?, None) + .await + { return Ok(key); } } - bail!("tried too often...aborting!"); - } - Some(file) => { - let mut file = BufReader::new(File::open(file)?); - let pass = read_password_from_bufread(&mut file)?; - if let Ok(key) = find_key_in_backend(be, &pass, None).await { - ve1!("password is correct"); - return Ok(key); - } } } bail!("incorrect password!"); diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 6b4ebb1..7c2f6b4 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -52,6 +52,15 @@ struct Opts { )] repo_hot: Option, + /// Password of the repository - WARNING: Using --password can reveal the password in the process list! + #[clap( + long, + global = true, + env = "RUSTIC_PASSWORD", + help_heading = "GLOBAL OPTIONS" + )] + password: Option, + /// File to read the password from #[clap( short, @@ -59,10 +68,21 @@ struct Opts { global = true, parse(from_os_str), env = "RUSTIC_PASSWORD_FILE", - help_heading = "GLOBAL OPTIONS" + help_heading = "GLOBAL OPTIONS", + conflicts_with = "password" )] password_file: Option, + /// Command to read the password from + #[clap( + long, + global = true, + env = "RUSTIC_PASSWORD_COMMAND", + help_heading = "GLOBAL OPTIONS", + conflicts_with_all = &["password", "password-file"], + )] + password_command: Option, + /// Increase verbosity (can be used multiple times) #[clap( long, @@ -202,7 +222,16 @@ pub async fn execute() -> Result<()> { bail!("keys from repo and repo-hot do not match. Aborting."); } } - let key = get_key(&be, args.password_file).await?; + + let key = get_key( + &be, + args.password.as_deref(), + args.password_file.as_deref(), + args.password_command.as_deref(), + ) + .await?; + ve1!("password is correct."); + let dbe = DecryptBackend::new(&be, key.clone()); let config: ConfigFile = dbe.get_file(&config_ids[0]).await?; match (config.is_hot == Some(true), be_hot.is_some()) {