1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00

[BEEEP/PM-22958] Update russh version, and add sessionbind information (#14602)

* Update russh version, and add sessionbind information

* Cargo fmt

* Clean up to fix lint

* Attempt to fix windows

* Use expect instead of unwrap

* Fix cargo toml
This commit is contained in:
Bernd Schoolmann
2025-07-09 16:52:47 +02:00
committed by GitHub
parent 489cbd4856
commit e7d5cde105
7 changed files with 241 additions and 22 deletions

View File

@@ -377,6 +377,12 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "base16ct"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "base64"
version = "0.22.1"
@@ -427,11 +433,17 @@ checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
[[package]]
name = "bitwarden-russh"
version = "0.1.0"
source = "git+https://github.com/bitwarden/bitwarden-russh.git?rev=3d48f140fd506412d186203238993163a8c4e536#3d48f140fd506412d186203238993163a8c4e536"
source = "git+https://github.com/bitwarden/bitwarden-russh.git?rev=a641316227227f8777fdf56ac9fa2d6b5f7fe662#a641316227227f8777fdf56ac9fa2d6b5f7fe662"
dependencies = [
"anyhow",
"byteorder",
"ecdsa",
"ed25519-dalek",
"futures",
"p256",
"p384",
"p521",
"rsa",
"russh-cryptovec",
"ssh-encoding",
"ssh-key",
@@ -707,6 +719,18 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crypto-bigint"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
dependencies = [
"generic-array",
"rand_core 0.6.4",
"subtle",
"zeroize",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
@@ -750,6 +774,7 @@ dependencies = [
"fiat-crypto",
"rustc_version",
"subtle",
"zeroize",
]
[[package]]
@@ -1018,6 +1043,20 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]]
name = "ecdsa"
version = "0.16.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
dependencies = [
"der",
"digest",
"elliptic-curve",
"rfc6979",
"signature",
"spki",
]
[[package]]
name = "ed25519"
version = "2.2.3"
@@ -1036,8 +1075,32 @@ checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
dependencies = [
"curve25519-dalek",
"ed25519",
"serde",
"sha2",
"signature",
"subtle",
"zeroize",
]
[[package]]
name = "elliptic-curve"
version = "0.13.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
dependencies = [
"base16ct",
"crypto-bigint",
"digest",
"ff",
"generic-array",
"group",
"hkdf",
"pem-rfc7468",
"pkcs8",
"rand_core 0.6.4",
"sec1",
"subtle",
"zeroize",
]
[[package]]
@@ -1122,6 +1185,16 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "ff"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
dependencies = [
"rand_core 0.6.4",
"subtle",
]
[[package]]
name = "fiat-crypto"
version = "0.2.9"
@@ -1274,6 +1347,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
"zeroize",
]
[[package]]
@@ -1342,6 +1416,17 @@ dependencies = [
"scroll",
]
[[package]]
name = "group"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
dependencies = [
"ff",
"rand_core 0.6.4",
"subtle",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
@@ -2112,6 +2197,44 @@ dependencies = [
"log",
]
[[package]]
name = "p256"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
dependencies = [
"ecdsa",
"elliptic-curve",
"primeorder",
"sha2",
]
[[package]]
name = "p384"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6"
dependencies = [
"ecdsa",
"elliptic-curve",
"primeorder",
"sha2",
]
[[package]]
name = "p521"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2"
dependencies = [
"base16ct",
"ecdsa",
"elliptic-curve",
"primeorder",
"rand_core 0.6.4",
"sha2",
]
[[package]]
name = "parking"
version = "2.2.1"
@@ -2348,6 +2471,15 @@ dependencies = [
"zerocopy",
]
[[package]]
name = "primeorder"
version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
dependencies = [
"elliptic-curve",
]
[[package]]
name = "proc-macro-crate"
version = "3.3.0"
@@ -2504,6 +2636,16 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "rfc6979"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
dependencies = [
"hmac",
"subtle",
]
[[package]]
name = "rsa"
version = "0.9.6"
@@ -2640,6 +2782,20 @@ dependencies = [
"sha2",
]
[[package]]
name = "sec1"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
dependencies = [
"base16ct",
"der",
"generic-array",
"pkcs8",
"subtle",
"zeroize",
]
[[package]]
name = "security-framework"
version = "3.1.0"
@@ -2854,6 +3010,7 @@ dependencies = [
"num-bigint-dig",
"rand_core 0.6.4",
"rsa",
"sec1",
"sha2",
"signature",
"ssh-cipher",

View File

@@ -16,7 +16,7 @@ argon2 = "=0.5.3"
ashpd = "=0.11.0"
base64 = "=0.22.1"
bindgen = "=0.72.0"
bitwarden-russh = { git = "https://github.com/bitwarden/bitwarden-russh.git", rev = "3d48f140fd506412d186203238993163a8c4e536" }
bitwarden-russh = { git = "https://github.com/bitwarden/bitwarden-russh.git", rev = "a641316227227f8777fdf56ac9fa2d6b5f7fe662" }
byteorder = "=1.5.0"
bytes = "=1.10.1"
cbc = "=0.1.2"

View File

@@ -3,10 +3,14 @@ use std::sync::{
Arc,
};
use base64::{engine::general_purpose::STANDARD, Engine as _};
use tokio::sync::Mutex;
use tokio_util::sync::CancellationToken;
use bitwarden_russh::ssh_agent::{self, Key};
use bitwarden_russh::{
session_bind::SessionBindResult,
ssh_agent::{self, SshKey},
};
#[cfg_attr(target_os = "windows", path = "windows.rs")]
#[cfg_attr(target_os = "macos", path = "unix.rs")]
@@ -20,8 +24,8 @@ pub mod peerinfo;
mod request_parser;
#[derive(Clone)]
pub struct BitwardenDesktopAgent {
keystore: ssh_agent::KeyStore,
pub struct BitwardenDesktopAgent<Key> {
keystore: ssh_agent::KeyStore<Key>,
cancellation_token: CancellationToken,
show_ui_request_tx: tokio::sync::mpsc::Sender<SshAgentUIRequest>,
get_ui_response_rx: Arc<Mutex<tokio::sync::broadcast::Receiver<(u32, bool)>>>,
@@ -40,8 +44,47 @@ pub struct SshAgentUIRequest {
pub is_forwarding: bool,
}
impl ssh_agent::Agent<peerinfo::models::PeerInfo> for BitwardenDesktopAgent {
async fn confirm(&self, ssh_key: Key, data: &[u8], info: &peerinfo::models::PeerInfo) -> bool {
#[derive(Clone)]
pub struct BitwardenSshKey {
pub private_key: Option<ssh_key::private::PrivateKey>,
pub name: String,
pub cipher_uuid: String,
}
impl SshKey for BitwardenSshKey {
fn name(&self) -> &str {
&self.name
}
fn public_key_bytes(&self) -> Vec<u8> {
if let Some(ref private_key) = self.private_key {
private_key
.public_key()
.to_bytes()
.expect("Cipher private key is always correctly parsed")
} else {
Vec::new()
}
}
fn private_key(&self) -> Option<Box<dyn ssh_key::SigningKey>> {
if let Some(ref private_key) = self.private_key {
Some(Box::new(private_key.clone()))
} else {
None
}
}
}
impl ssh_agent::Agent<peerinfo::models::PeerInfo, BitwardenSshKey>
for BitwardenDesktopAgent<BitwardenSshKey>
{
async fn confirm(
&self,
ssh_key: BitwardenSshKey,
data: &[u8],
info: &peerinfo::models::PeerInfo,
) -> bool {
if !self.is_running() {
println!("[BitwardenDesktopAgent] Agent is not running, but tried to call confirm");
return false;
@@ -63,10 +106,11 @@ impl ssh_agent::Agent<peerinfo::models::PeerInfo> for BitwardenDesktopAgent {
};
println!(
"[SSH Agent] Confirming request from application: {}, is_forwarding: {}, namespace: {}",
"[SSH Agent] Confirming request from application: {}, is_forwarding: {}, namespace: {}, host_key: {}",
info.process_name(),
info.is_forwarding(),
namespace.clone().unwrap_or_default(),
STANDARD.encode(info.host_key())
);
let mut rx_channel = self.get_ui_response_rx.lock().await.resubscribe();
@@ -117,19 +161,24 @@ impl ssh_agent::Agent<peerinfo::models::PeerInfo> for BitwardenDesktopAgent {
false
}
async fn set_is_forwarding(
async fn set_sessionbind_info(
&self,
is_forwarding: bool,
session_bind_info_result: &SessionBindResult,
connection_info: &peerinfo::models::PeerInfo,
) {
// is_forwarding can only be added but never removed from a connection
if is_forwarding {
connection_info.set_forwarding(is_forwarding);
match session_bind_info_result {
SessionBindResult::Success(session_bind_info) => {
connection_info.set_forwarding(session_bind_info.is_forwarding);
connection_info.set_host_key(session_bind_info.host_key.clone());
}
SessionBindResult::SignatureFailure => {
println!("[BitwardenDesktopAgent] Session bind failure: Signature failure");
}
}
}
}
impl BitwardenDesktopAgent {
impl BitwardenDesktopAgent<BitwardenSshKey> {
pub fn stop(&self) {
if !self.is_running() {
println!("[BitwardenDesktopAgent] Tried to stop agent while it is not running");
@@ -170,7 +219,7 @@ impl BitwardenDesktopAgent {
.expect("Cipher private key is always correctly parsed");
keystore.0.write().expect("RwLock is not poisoned").insert(
public_key_bytes,
Key {
BitwardenSshKey {
private_key: Some(private_key),
name: name.clone(),
cipher_uuid: cipher_id.clone(),

View File

@@ -1,15 +1,16 @@
use std::sync::{atomic::AtomicBool, Arc};
use std::sync::{atomic::AtomicBool, Arc, Mutex};
/**
* Peerinfo represents the information of a peer process connecting over a socket.
* This can be later extended to include more information (icon, app name) for the corresponding application.
*/
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct PeerInfo {
uid: u32,
pid: u32,
process_name: String,
is_forwarding: Arc<AtomicBool>,
host_key: Arc<Mutex<Vec<u8>>>,
}
impl PeerInfo {
@@ -19,6 +20,7 @@ impl PeerInfo {
pid,
process_name,
is_forwarding: Arc::new(AtomicBool::new(false)),
host_key: Arc::new(Mutex::new(Vec::new())),
}
}
@@ -28,6 +30,7 @@ impl PeerInfo {
pid: 0,
process_name: "Unknown application".to_string(),
is_forwarding: Arc::new(AtomicBool::new(false)),
host_key: Arc::new(Mutex::new(Vec::new())),
}
}
@@ -52,4 +55,13 @@ impl PeerInfo {
self.is_forwarding
.store(value, std::sync::atomic::Ordering::Relaxed);
}
pub fn set_host_key(&self, host_key: Vec<u8>) {
let mut host_key_lock = self.host_key.lock().expect("Mutex is not poisoned");
*host_key_lock = host_key;
}
pub fn host_key(&self) -> Vec<u8> {
self.host_key.lock().expect("Mutex is not poisoned").clone()
}
}

View File

@@ -15,9 +15,9 @@ use tokio_util::sync::CancellationToken;
use crate::ssh_agent::peercred_unix_listener_stream::PeercredUnixListenerStream;
use super::{BitwardenDesktopAgent, SshAgentUIRequest};
use super::{BitwardenDesktopAgent, BitwardenSshKey, SshAgentUIRequest};
impl BitwardenDesktopAgent {
impl BitwardenDesktopAgent<BitwardenSshKey> {
pub async fn start_server(
auth_request_tx: tokio::sync::mpsc::Sender<SshAgentUIRequest>,
auth_response_rx: Arc<Mutex<tokio::sync::broadcast::Receiver<(u32, bool)>>>,

View File

@@ -11,9 +11,9 @@ use std::{
use tokio::sync::Mutex;
use tokio_util::sync::CancellationToken;
use super::{BitwardenDesktopAgent, SshAgentUIRequest};
use super::{BitwardenDesktopAgent, BitwardenSshKey, SshAgentUIRequest};
impl BitwardenDesktopAgent {
impl BitwardenDesktopAgent<BitwardenSshKey> {
pub async fn start_server(
auth_request_tx: tokio::sync::mpsc::Sender<SshAgentUIRequest>,
auth_response_rx: Arc<Mutex<tokio::sync::broadcast::Receiver<(u32, bool)>>>,

View File

@@ -166,6 +166,7 @@ pub mod clipboards {
pub mod sshagent {
use std::sync::Arc;
use desktop_core::ssh_agent::BitwardenSshKey;
use napi::{
bindgen_prelude::Promise,
threadsafe_function::{ErrorStrategy::CalleeHandled, ThreadsafeFunction},
@@ -174,7 +175,7 @@ pub mod sshagent {
#[napi]
pub struct SshAgentState {
state: desktop_core::ssh_agent::BitwardenDesktopAgent,
state: desktop_core::ssh_agent::BitwardenDesktopAgent<BitwardenSshKey>,
}
#[napi(object)]