mirror of
https://github.com/bitwarden/browser
synced 2026-02-06 11:43:51 +00:00
209 lines
6.3 KiB
Rust
209 lines
6.3 KiB
Rust
#[cfg(target_os = "linux")]
|
|
use std::{fs, process::Command, sync::Arc};
|
|
|
|
#[cfg(target_os = "linux")]
|
|
use ssh_agent::{
|
|
agent::{
|
|
ui_requester::{UiRequestMessage, UiRequester},
|
|
BitwardenDesktopAgent,
|
|
},
|
|
memory::UnlockedSshItem,
|
|
protocol::types::{KeyPair, PrivateKey},
|
|
transport::unix_listener_stream::UnixListenerStream,
|
|
};
|
|
#[cfg(target_os = "linux")]
|
|
use tokio::{
|
|
sync::{broadcast, mpsc, Mutex},
|
|
task,
|
|
};
|
|
#[cfg(target_os = "linux")]
|
|
use tracing::info;
|
|
|
|
#[cfg(target_os = "linux")]
|
|
#[tokio::test]
|
|
#[test_log::test]
|
|
async fn ssh_agent_auth() {
|
|
let dir = homedir::my_home().unwrap().unwrap();
|
|
let dir = dir.join(".cache");
|
|
let dir = dir.join("ssh_agent_integration_test");
|
|
let dir = dir.to_string_lossy().into_owned();
|
|
|
|
fs::remove_dir_all(&dir).unwrap_or(());
|
|
// Prepare test run directory
|
|
fs::create_dir_all(&dir).unwrap();
|
|
|
|
let config = format!(
|
|
"Port 2222
|
|
HostKey {}/ssh_host_rsa_key
|
|
HostKey {}/ssh_host_ecdsa_key
|
|
HostKey {}/ssh_host_ed25519_key
|
|
AuthorizedKeysFile {}/authorized_keys
|
|
",
|
|
dir, dir, dir, dir
|
|
);
|
|
fs::write(format!("{}/sshd_config", dir), config).unwrap();
|
|
|
|
let keys = make_keys(&dir);
|
|
|
|
// Start ssh server
|
|
let dir_clone = dir.clone();
|
|
std::thread::spawn(move || {
|
|
Command::new("/usr/bin/sshd")
|
|
.args(["-f", &format!("{}/sshd_config", &dir_clone), "-D", "-e"])
|
|
.status()
|
|
.expect("failed to execute process");
|
|
});
|
|
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
|
|
|
let ui_requester = mock_channels();
|
|
let desktop_agent = BitwardenDesktopAgent::new(ui_requester);
|
|
desktop_agent.set_keys(keys);
|
|
|
|
let dir_clone = dir.clone();
|
|
task::spawn(async move {
|
|
info!(target: "ssh-agent", "Listening on {}", format!("{}/ssh-agent.sock", dir_clone));
|
|
UnixListenerStream::listen(format!("{}/ssh-agent.sock", dir_clone), desktop_agent)
|
|
.await
|
|
.unwrap();
|
|
});
|
|
|
|
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
|
|
|
|
// Test listing keys
|
|
let dir_clone = dir.clone();
|
|
let jh1 = std::thread::spawn(move || {
|
|
Command::new("ssh-add")
|
|
.env("SSH_AUTH_SOCK", format!("{}/ssh-agent.sock", dir_clone))
|
|
.args(["-L"])
|
|
.status()
|
|
.expect("failed to execute process");
|
|
});
|
|
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
|
jh1.join().unwrap();
|
|
|
|
// Test ssh connection
|
|
let dir_clone = dir.clone();
|
|
let jh1 = std::thread::spawn(move || {
|
|
Command::new("ssh")
|
|
.env("SSH_AUTH_SOCK", format!("{}/ssh-agent.sock", dir_clone))
|
|
.args([
|
|
"-o",
|
|
"StrictHostKeyChecking=no",
|
|
"-o",
|
|
"UserKnownHostsFile=/dev/null",
|
|
"-p",
|
|
"2222",
|
|
"localhost",
|
|
"echo",
|
|
"Success",
|
|
])
|
|
.status()
|
|
.expect("failed to execute process");
|
|
});
|
|
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
|
jh1.join().unwrap();
|
|
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
|
|
|
// Cleanup
|
|
fs::remove_dir_all(dir).unwrap();
|
|
std::process::exit(0);
|
|
}
|
|
|
|
#[cfg(target_os = "linux")]
|
|
fn make_keys(dir: &str) -> Vec<UnlockedSshItem> {
|
|
Command::new("ssh-keygen")
|
|
.args([
|
|
"-f",
|
|
&format!("{}/ssh_host_rsa_key", dir),
|
|
"-N",
|
|
"",
|
|
"-t",
|
|
"rsa",
|
|
])
|
|
.status()
|
|
.expect("failed to execute process");
|
|
Command::new("ssh-keygen")
|
|
.args([
|
|
"-f",
|
|
&format!("{}/ssh_host_ecdsa_key", dir),
|
|
"-N",
|
|
"",
|
|
"-t",
|
|
"ecdsa",
|
|
])
|
|
.status()
|
|
.expect("failed to execute process");
|
|
Command::new("ssh-keygen")
|
|
.args([
|
|
"-f",
|
|
&format!("{}/ssh_host_ed25519_key", dir),
|
|
"-N",
|
|
"",
|
|
"-t",
|
|
"ed25519",
|
|
])
|
|
.status()
|
|
.expect("failed to execute process");
|
|
// // Make user key
|
|
Command::new("ssh-keygen")
|
|
.args([
|
|
"-f",
|
|
&format!("{}/id_ed25519", dir),
|
|
"-N",
|
|
"",
|
|
"-t",
|
|
"ed25519",
|
|
])
|
|
.status()
|
|
.expect("failed to execute process");
|
|
Command::new("ssh-keygen")
|
|
.args(["-f", &format!("{}/ssh_rsa", dir), "-N", "", "-t", "rsa"])
|
|
.status()
|
|
.expect("failed to execute process");
|
|
let pubkey1 =
|
|
fs::read_to_string(format!("{}/id_ed25519.pub", dir)).expect("failed to read public key");
|
|
let pubkey2 =
|
|
fs::read_to_string(format!("{}/ssh_rsa.pub", dir)).expect("failed to read public key");
|
|
fs::write(
|
|
format!("{}/authorized_keys", dir),
|
|
format!("{}{}", pubkey1, pubkey2),
|
|
)
|
|
.expect("failed to write authorized_keys");
|
|
let privkey1 = fs::read_to_string(format!("{}/id_ed25519", dir)).expect("key is valid");
|
|
let key1 = KeyPair::new(
|
|
PrivateKey::try_from(privkey1).expect("key is valid"),
|
|
"ed25519-key".to_string(),
|
|
);
|
|
let privkey2 = fs::read_to_string(format!("{}/ssh_rsa", dir)).expect("key is valid");
|
|
let key2 = KeyPair::new(
|
|
PrivateKey::try_from(privkey2).expect("key is valid"),
|
|
"rsa-key".to_string(),
|
|
);
|
|
let unlocked_items = vec![
|
|
UnlockedSshItem::new(key1, "cipher1".to_string()),
|
|
UnlockedSshItem::new(key2, "cipher2".to_string()),
|
|
];
|
|
unlocked_items
|
|
}
|
|
|
|
#[cfg(target_os = "linux")]
|
|
fn mock_channels() -> UiRequester {
|
|
let (show_ui_request_tx, mut show_ui_request_rx) = mpsc::channel::<UiRequestMessage>(10);
|
|
|
|
// Create mock broadcast channel for responses
|
|
let (response_tx, response_rx) = broadcast::channel::<(u32, bool)>(10);
|
|
let get_ui_response_rx = Arc::new(Mutex::new(response_rx));
|
|
|
|
// Spawn a task to automatically send back "true" responses
|
|
let response_tx_clone = response_tx.clone();
|
|
let _handle = task::spawn(async move {
|
|
while let Some(req) = show_ui_request_rx.recv().await {
|
|
info!("Mock UI requester received request: {:?}", req);
|
|
let _ = response_tx_clone.send((req.id(), true));
|
|
}
|
|
info!("Mock UI requester task ending");
|
|
});
|
|
|
|
UiRequester::new(show_ui_request_tx, get_ui_response_rx)
|
|
}
|