mirror of
https://github.com/bitwarden/browser
synced 2026-02-03 10:13:31 +00:00
Fix
This commit is contained in:
174
apps/desktop/desktop_native/ssh_agent/examples/integration.rs
Normal file
174
apps/desktop/desktop_native/ssh_agent/examples/integration.rs
Normal file
@@ -0,0 +1,174 @@
|
||||
use std::{fs, process::Command, sync::Arc};
|
||||
|
||||
use ssh_agent::{agent::{ui_requester::{UiRequestMessage, UiRequester}, BitwardenDesktopAgent}, memory::UnlockedSshItem, protocol::types::{KeyPair, PrivateKey}, transport::unix_listener_stream::UnixListenerStream};
|
||||
use tokio::{sync::{broadcast, mpsc, Mutex}, task};
|
||||
use tracing::info;
|
||||
|
||||
const TEST_RUN_DIR: &str = "/home/quexten/test_run/";
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// set up tracing to stdout
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(tracing::Level::INFO)
|
||||
.with_thread_ids(true)
|
||||
.with_thread_names(true)
|
||||
.init();
|
||||
|
||||
fs::remove_dir_all(TEST_RUN_DIR).unwrap_or(());
|
||||
// Prepare test run directory
|
||||
fs::create_dir_all(TEST_RUN_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
|
||||
", TEST_RUN_DIR, TEST_RUN_DIR, TEST_RUN_DIR, TEST_RUN_DIR);
|
||||
fs::write(format!("{}/sshd_config", TEST_RUN_DIR), config).unwrap();
|
||||
|
||||
let keys = make_keys();
|
||||
|
||||
// Start ssh server
|
||||
std::thread::spawn(move || {
|
||||
Command::new("/usr/bin/sshd")
|
||||
.args(&["-f", &format!("{}/sshd_config", TEST_RUN_DIR), "-D", "-e"])
|
||||
.status()
|
||||
.expect("failed to execute process");
|
||||
});
|
||||
|
||||
let ui_requester = mock_channels();
|
||||
let desktop_agent = BitwardenDesktopAgent::new(ui_requester);
|
||||
desktop_agent.set_keys(keys);
|
||||
|
||||
task::spawn(async move {
|
||||
println!("Starting SSH Agent V2 socket...");
|
||||
info!(target: "ssh-agent", "Listening on {}", format!("{}/ssh-agent.sock", TEST_RUN_DIR));
|
||||
UnixListenerStream::listen(format!("{}/ssh-agent.sock", TEST_RUN_DIR), desktop_agent)
|
||||
.await
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
// run ssh-add -L
|
||||
Command::new("ssh-add")
|
||||
.env("SSH_AUTH_SOCK", format!("{}/ssh-agent.sock", TEST_RUN_DIR))
|
||||
.args(&["-L"])
|
||||
.status()
|
||||
.expect("failed to execute process");
|
||||
|
||||
// run ssh
|
||||
Command::new("ssh")
|
||||
.env("SSH_AUTH_SOCK", format!("{}/ssh-agent.sock", TEST_RUN_DIR))
|
||||
.args(&[
|
||||
"-o",
|
||||
"StrictHostKeyChecking=no",
|
||||
"-o",
|
||||
"UserKnownHostsFile=/dev/null",
|
||||
"-p",
|
||||
"2222",
|
||||
"localhost",
|
||||
"echo",
|
||||
"Hello, world!",
|
||||
])
|
||||
.status()
|
||||
.expect("failed to execute process");
|
||||
|
||||
// Cleanup
|
||||
fs::remove_dir_all(TEST_RUN_DIR).unwrap();
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
fn make_keys() -> Vec<UnlockedSshItem> {
|
||||
Command::new("ssh-keygen")
|
||||
.args(&[
|
||||
"-f",
|
||||
&format!("{}/ssh_host_rsa_key", TEST_RUN_DIR),
|
||||
"-N",
|
||||
"",
|
||||
"-t",
|
||||
"rsa",
|
||||
])
|
||||
.status()
|
||||
.expect("failed to execute process");
|
||||
Command::new("ssh-keygen")
|
||||
.args(&[
|
||||
"-f",
|
||||
&format!("{}/ssh_host_ecdsa_key", TEST_RUN_DIR),
|
||||
"-N",
|
||||
"",
|
||||
"-t",
|
||||
"ecdsa",
|
||||
])
|
||||
.status()
|
||||
.expect("failed to execute process");
|
||||
Command::new("ssh-keygen")
|
||||
.args(&[
|
||||
"-f",
|
||||
&format!("{}/ssh_host_ed25519_key", TEST_RUN_DIR),
|
||||
"-N",
|
||||
"",
|
||||
"-t",
|
||||
"ed25519",
|
||||
])
|
||||
.status()
|
||||
.expect("failed to execute process");
|
||||
// // Make user key
|
||||
Command::new("ssh-keygen")
|
||||
.args(&[
|
||||
"-f",
|
||||
&format!("{}/id_ed25519", TEST_RUN_DIR),
|
||||
"-N",
|
||||
"",
|
||||
"-t",
|
||||
"ed25519",
|
||||
])
|
||||
.status()
|
||||
.expect("failed to execute process");
|
||||
Command::new("ssh-keygen")
|
||||
.args(&[
|
||||
"-f",
|
||||
&format!("{}/ssh_rsa", TEST_RUN_DIR),
|
||||
"-N",
|
||||
"",
|
||||
"-t",
|
||||
"rsa",
|
||||
])
|
||||
.status()
|
||||
.expect("failed to execute process");
|
||||
let pubkey1 = fs::read_to_string(format!("{}/id_ed25519.pub", TEST_RUN_DIR)).unwrap();
|
||||
let pubkey2 = fs::read_to_string(format!("{}/ssh_rsa.pub", TEST_RUN_DIR)).unwrap();
|
||||
fs::write(
|
||||
format!("{}/authorized_keys", TEST_RUN_DIR),
|
||||
format!("{}{}", pubkey1, pubkey2),
|
||||
)
|
||||
.unwrap();
|
||||
let privkey1 = fs::read_to_string(format!("{}/id_ed25519", TEST_RUN_DIR)).unwrap();
|
||||
let key1 = KeyPair::new(PrivateKey::try_from(privkey1).unwrap(), "ed25519-key".to_string());
|
||||
let privkey2 = fs::read_to_string(format!("{}/ssh_rsa", TEST_RUN_DIR)).unwrap();
|
||||
let key2 = KeyPair::new(PrivateKey::try_from(privkey2).unwrap(), "rsa-key".to_string());
|
||||
let unlocked_items = vec![
|
||||
UnlockedSshItem::new(key1, "cipher1".to_string()),
|
||||
UnlockedSshItem::new(key2, "cipher2".to_string()),
|
||||
];
|
||||
unlocked_items
|
||||
}
|
||||
|
||||
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 _ = 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)
|
||||
}
|
||||
@@ -82,7 +82,6 @@ impl TryFrom<&[u8]> for Request {
|
||||
// Only support session bind for now
|
||||
let extension_request: SessionBindRequest = contents.as_slice().try_into()?;
|
||||
if !extension_request.verify_signature() {
|
||||
info!("Invalid session bind signature");
|
||||
return Err(anyhow::anyhow!("Invalid session bind signature"));
|
||||
}
|
||||
Ok(Request::SessionBind(extension_request))
|
||||
@@ -162,7 +161,7 @@ impl TryFrom<&[u8]> for SshSignRequest {
|
||||
.map_err(|e| anyhow::anyhow!("Failed to read flags from sign request: {e}"))?;
|
||||
|
||||
Ok(SshSignRequest {
|
||||
public_key: public_key_blob.try_into()?,
|
||||
public_key: PublicKey::from_blob(public_key_blob),
|
||||
payload_to_sign: data.clone(),
|
||||
parsed_sign_request: data.as_slice().try_into()?,
|
||||
flags,
|
||||
|
||||
@@ -269,12 +269,14 @@ impl PrivateKey {
|
||||
PrivateKey::Ecdsa(key) => ssh_key::private::PrivateKey::from(key.to_owned()),
|
||||
};
|
||||
|
||||
private_key
|
||||
let pubkey_bytes = BASE64_STANDARD.encode(private_key
|
||||
.public_key()
|
||||
.to_bytes()
|
||||
.map(PublicKey::try_from)
|
||||
.expect("Key is always valid")
|
||||
.expect("Key is always valid")
|
||||
.expect("Converting to public key bytes should always be possible"));
|
||||
let alg_str = private_key.algorithm();
|
||||
|
||||
PublicKey::try_from(format!("{} {}", alg_str.as_str(), BASE64_STANDARD.encode(&pubkey_bytes)))
|
||||
.expect("Parsing public key should always be possible")
|
||||
}
|
||||
|
||||
pub(crate) fn sign(
|
||||
@@ -345,10 +347,7 @@ impl PublicKey {
|
||||
&self,
|
||||
writer: &mut impl ssh_encoding::Writer,
|
||||
) -> Result<(), ssh_encoding::Error> {
|
||||
let mut buf = Vec::new();
|
||||
self.alg().as_bytes().encode(&mut buf)?;
|
||||
self.blob().encode(&mut buf)?;
|
||||
buf.encode(writer)?;
|
||||
self.blob().encode(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
fn try_read_from(mut bytes: &[u8]) -> Result<Self, anyhow::Error> {
|
||||
@@ -356,6 +355,13 @@ impl PublicKey {
|
||||
let blob = read_bytes(&mut bytes)?;
|
||||
Ok(PublicKey { alg, blob })
|
||||
}
|
||||
|
||||
pub fn from_blob(blob: Vec<u8>) -> Self {
|
||||
// Parse the blob to extract the algorithm
|
||||
let mut bytes = &blob[..];
|
||||
let alg = String::from_utf8_lossy(read_bytes(&mut bytes).unwrap().as_slice()).to_string();
|
||||
PublicKey { alg, blob }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PublicKey {
|
||||
|
||||
Reference in New Issue
Block a user