Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions src/bin/rbw-agent/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ async fn login_success(
async fn unlock_state(
state: std::sync::Arc<tokio::sync::Mutex<crate::state::State>>,
environment: &rbw::protocol::Environment,
password: Option<&rbw::locked::Password>,
) -> anyhow::Result<()> {
if state.lock().await.needs_unlock() {
let db = load_db().await?;
Expand Down Expand Up @@ -404,6 +405,26 @@ async fn unlock_state(

let email = config_email().await?;

if let Some(password) = password {
// Password was passed through stdin
match rbw::actions::unlock(
&email,
&password,
kdf,
iterations,
memory,
parallelism,
&protected_key,
&protected_private_key,
&db.protected_org_keys,
) {
Ok((keys, org_keys)) => {
return unlock_success(state, keys, org_keys).await
}
Err(e) => return Err(e).context("failed to unlock database"),
}
}

let mut err_msg = None;
for i in 1_u8..=3 {
let err = if i > 1 {
Expand Down Expand Up @@ -462,8 +483,9 @@ pub async fn unlock(
sock: &mut crate::sock::Sock,
state: std::sync::Arc<tokio::sync::Mutex<crate::state::State>>,
environment: &rbw::protocol::Environment,
password: Option<&rbw::locked::Password>,
) -> anyhow::Result<()> {
unlock_state(state, environment).await?;
unlock_state(state, environment, password).await?;

respond_ack(sock).await?;

Expand Down Expand Up @@ -858,7 +880,7 @@ pub async fn get_ssh_public_keys(
state.set_timeout();
state.last_environment().clone()
};
unlock_state(state.clone(), &environment).await?;
unlock_state(state.clone(), &environment, None).await?;

let db = load_db().await?;
let mut pubkeys = Vec::new();
Expand Down Expand Up @@ -894,7 +916,7 @@ pub async fn find_ssh_private_key(
state.set_timeout();
state.last_environment().clone()
};
unlock_state(state.clone(), &environment).await?;
unlock_state(state.clone(), &environment, None).await?;

let request_bytes = request_public_key.to_bytes();

Expand Down
30 changes: 22 additions & 8 deletions src/bin/rbw-agent/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ async fn handle_request(
}
};
let (action, environment) = req.into_parts();
let set_timeout = match &action {
let set_timeout = match action {
rbw::protocol::Action::Register => {
crate::actions::register(sock, &environment).await?;
true
Expand All @@ -127,8 +127,25 @@ async fn handle_request(
crate::actions::login(sock, state.clone(), &environment).await?;
true
}
rbw::protocol::Action::Unlock => {
crate::actions::unlock(sock, state.clone(), &environment).await?;
rbw::protocol::Action::Unlock { mut password } => {
// Copy the password into locked memory, then zeroize
// the original String
let locked_password = password.as_deref().map(|p| {
let mut v = rbw::locked::Vec::new();
v.extend(p.as_bytes().iter().copied());
rbw::locked::Password::new(v)
});
if let Some(ref mut p) = password {
zeroize::Zeroize::zeroize(p);
}

crate::actions::unlock(
sock,
state.clone(),
&environment,
locked_password.as_ref(),
)
.await?;
true
}
rbw::protocol::Action::CheckLock => {
Expand All @@ -148,9 +165,6 @@ async fn handle_request(
entry_key,
org_id,
} => {
let cipherstring = cipherstring.clone();
let entry_key = entry_key.clone();
let org_id = org_id.clone();
crate::actions::decrypt(
sock,
state.clone(),
Expand All @@ -166,14 +180,14 @@ async fn handle_request(
crate::actions::encrypt(
sock,
state.clone(),
plaintext,
&plaintext,
org_id.as_deref(),
)
.await?;
true
}
rbw::protocol::Action::ClipboardStore { text } => {
crate::actions::clipboard_store(sock, state.clone(), text)
crate::actions::clipboard_store(sock, state.clone(), &text)
.await?;
true
}
Expand Down
4 changes: 2 additions & 2 deletions src/bin/rbw/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ pub fn login() -> anyhow::Result<()> {
simple_action(rbw::protocol::Action::Login)
}

pub fn unlock() -> anyhow::Result<()> {
simple_action(rbw::protocol::Action::Unlock)
pub fn unlock(password: Option<String>) -> anyhow::Result<()> {
simple_action(rbw::protocol::Action::Unlock { password })
}

pub fn unlocked() -> anyhow::Result<()> {
Expand Down
22 changes: 11 additions & 11 deletions src/bin/rbw/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1333,10 +1333,10 @@ pub fn login() -> anyhow::Result<()> {
Ok(())
}

pub fn unlock() -> anyhow::Result<()> {
pub fn unlock(password: Option<String>) -> anyhow::Result<()> {
ensure_agent()?;
crate::actions::login()?;
crate::actions::unlock()?;
crate::actions::unlock(password)?;

Ok(())
}
Expand Down Expand Up @@ -1368,7 +1368,7 @@ pub fn list(fields: &[String], raw: bool) -> anyhow::Result<()> {
.collect::<anyhow::Result<_>>()?
};

unlock()?;
unlock(None)?;

let db = load_db()?;
let mut entries: Vec<DecryptedListCipher> = db
Expand All @@ -1395,7 +1395,7 @@ pub fn get(
ignore_case: bool,
list_fields: bool,
) -> anyhow::Result<()> {
unlock()?;
unlock(None)?;

let db = load_db()?;

Expand Down Expand Up @@ -1496,7 +1496,7 @@ pub fn search(
.collect::<anyhow::Result<_>>()?
};

unlock()?;
unlock(None)?;

let db = load_db()?;

Expand Down Expand Up @@ -1526,7 +1526,7 @@ pub fn code(
clipboard: bool,
ignore_case: bool,
) -> anyhow::Result<()> {
unlock()?;
unlock(None)?;

let db = load_db()?;

Expand Down Expand Up @@ -1561,7 +1561,7 @@ pub fn add(
uris: &[(String, Option<rbw::api::UriMatchType>)],
folder: Option<&str>,
) -> anyhow::Result<()> {
unlock()?;
unlock(None)?;

let mut db = load_db()?;
// unwrap is safe here because the call to unlock above is guaranteed to
Expand Down Expand Up @@ -1666,7 +1666,7 @@ pub fn generate(
println!("{password}");

if let Some(name) = name {
unlock()?;
unlock(None)?;

let mut db = load_db()?;
// unwrap is safe here because the call to unlock above is guaranteed
Expand Down Expand Up @@ -1756,7 +1756,7 @@ pub fn edit(
folder: Option<&str>,
ignore_case: bool,
) -> anyhow::Result<()> {
unlock()?;
unlock(None)?;

let mut db = load_db()?;
let access_token = db.access_token.as_ref().unwrap();
Expand Down Expand Up @@ -1881,7 +1881,7 @@ pub fn remove(
folder: Option<&str>,
ignore_case: bool,
) -> anyhow::Result<()> {
unlock()?;
unlock(None)?;

let mut db = load_db()?;
let access_token = db.access_token.as_ref().unwrap();
Expand Down Expand Up @@ -1914,7 +1914,7 @@ pub fn history(
folder: Option<&str>,
ignore_case: bool,
) -> anyhow::Result<()> {
unlock()?;
unlock(None)?;

let db = load_db()?;

Expand Down
21 changes: 18 additions & 3 deletions src/bin/rbw/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ enum Opt {
Login,

#[command(about = "Unlock the local Bitwarden database")]
Unlock,
Unlock {
#[arg(long, help = "Read the password from standard input")]
stdin: bool,
},

#[command(about = "Check if the local Bitwarden database is unlocked")]
Unlocked,
Expand Down Expand Up @@ -247,7 +250,7 @@ impl Opt {
}
Self::Register => "register".to_string(),
Self::Login => "login".to_string(),
Self::Unlock => "unlock".to_string(),
Self::Unlock { .. } => "unlock".to_string(),
Self::Unlocked => "unlocked".to_string(),
Self::Sync => "sync".to_string(),
Self::List { .. } => "list".to_string(),
Expand Down Expand Up @@ -334,7 +337,19 @@ fn main() {
},
Opt::Register => commands::register(),
Opt::Login => commands::login(),
Opt::Unlock => commands::unlock(),
Opt::Unlock { stdin } => {
let password = if stdin {
let mut buf = String::new();
let _ = std::io::stdin()
.read_line(&mut buf)
.context("failed to read password from stdin");
Some(buf.trim_end_matches('\n').to_string())
} else {
None
};

commands::unlock(password)
}
Opt::Unlocked => commands::unlocked(),
Opt::Sync => commands::sync(),
Opt::List { fields, raw } => commands::list(&fields, raw),
Expand Down
4 changes: 3 additions & 1 deletion src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,9 @@ impl Environment {
pub enum Action {
Login,
Register,
Unlock,
Unlock {
password: Option<String>,
},
CheckLock,
Lock,
Sync,
Expand Down
Loading