mirror of
https://github.com/bitwarden/browser
synced 2026-02-04 10:43:47 +00:00
feat: add hash to process info
This commit is contained in:
@@ -6,7 +6,12 @@ use std::{
|
||||
use futures::{SinkExt, StreamExt, TryFutureExt};
|
||||
|
||||
use anyhow::Result;
|
||||
// Non-Unix uses interprocess local sockets
|
||||
#[cfg(not(unix))]
|
||||
use interprocess::local_socket::{tokio::prelude::*, GenericFilePath, ListenerOptions};
|
||||
// Unix uses tokio's UnixListener to access peer credentials
|
||||
#[cfg(unix)]
|
||||
use tokio::net::UnixListener;
|
||||
use tokio::{
|
||||
io::{AsyncRead, AsyncWrite},
|
||||
sync::{broadcast, mpsc},
|
||||
@@ -54,9 +59,15 @@ impl Server {
|
||||
let _ = std::fs::remove_file(path);
|
||||
}
|
||||
|
||||
let name = path.as_os_str().to_fs_name::<GenericFilePath>()?;
|
||||
let opts = ListenerOptions::new().name(name);
|
||||
let listener = opts.create_tokio()?;
|
||||
#[cfg(unix)]
|
||||
let listener = UnixListener::bind(path)?;
|
||||
|
||||
#[cfg(not(unix))]
|
||||
let listener = {
|
||||
let name = path.as_os_str().to_fs_name::<GenericFilePath>()?;
|
||||
let opts = ListenerOptions::new().name(name);
|
||||
opts.create_tokio()?
|
||||
};
|
||||
|
||||
// This broadcast channel is used for sending messages to all connected clients, and so the sender
|
||||
// will be stored in the server while the receiver will be cloned and passed to each client handler.
|
||||
@@ -74,7 +85,15 @@ impl Server {
|
||||
cancel_token: cancel_token.clone(),
|
||||
server_to_clients_send,
|
||||
};
|
||||
tokio::spawn(listen_incoming(
|
||||
#[cfg(unix)]
|
||||
tokio::spawn(listen_incoming_unix(
|
||||
listener,
|
||||
client_to_server_send,
|
||||
server_to_clients_recv,
|
||||
cancel_token,
|
||||
));
|
||||
#[cfg(not(unix))]
|
||||
tokio::spawn(listen_incoming_non_unix(
|
||||
listener,
|
||||
client_to_server_send,
|
||||
server_to_clients_recv,
|
||||
@@ -108,8 +127,78 @@ impl Drop for Server {
|
||||
}
|
||||
}
|
||||
|
||||
async fn listen_incoming(
|
||||
listener: LocalSocketListener,
|
||||
#[cfg(unix)]
|
||||
async fn listen_incoming_unix(
|
||||
listener: UnixListener,
|
||||
client_to_server_send: mpsc::Sender<Message>,
|
||||
server_to_clients_recv: broadcast::Receiver<String>,
|
||||
cancel_token: CancellationToken,
|
||||
) {
|
||||
// We use a simple incrementing ID for each client
|
||||
let mut next_client_id = 1_u32;
|
||||
|
||||
loop {
|
||||
use crate::ssh_agent::peerinfo::gather::get_peer_info;
|
||||
|
||||
tokio::select! {
|
||||
_ = cancel_token.cancelled() => {
|
||||
info!("IPC server cancelled.");
|
||||
break;
|
||||
},
|
||||
|
||||
// A new client connection has been established
|
||||
msg = listener.accept() => {
|
||||
match msg {
|
||||
Ok((client_stream, _addr)) => {
|
||||
let client_id = next_client_id;
|
||||
next_client_id += 1;
|
||||
|
||||
// Try to log peer credentials
|
||||
match client_stream.peer_cred() {
|
||||
Ok(peer) => {
|
||||
if let Some(pid) = peer.pid() {
|
||||
let peer_info = match get_peer_info(pid as u32) {
|
||||
Ok(info) => info,
|
||||
Err(_) => crate::ssh_agent::peerinfo::models::PeerInfo::unknown(),
|
||||
};
|
||||
info!(client_id, pid, uid = peer.uid(), gid = peer.gid(), peer_info = ?peer_info, "IPC client connected (peer credentials)");
|
||||
} else {
|
||||
info!(client_id, uid = peer.uid(), gid = peer.gid(), "IPC client connected (peer credentials, no pid)");
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!(client_id, error = %e, "Failed to get peer credentials");
|
||||
}
|
||||
}
|
||||
|
||||
let future = handle_connection(
|
||||
client_stream,
|
||||
client_to_server_send.clone(),
|
||||
// We resubscribe to the receiver here so this task can have it's own copy
|
||||
// Note that this copy will only receive messages sent after this point,
|
||||
// but that is okay, realistically we don't want any messages before we get a chance
|
||||
// to send the connected message to the client, which is done inside [`handle_connection`]
|
||||
server_to_clients_recv.resubscribe(),
|
||||
cancel_token.clone(),
|
||||
client_id
|
||||
);
|
||||
tokio::spawn(future.map_err(|e| {
|
||||
error!(error = %e, "Error handling connection")
|
||||
}));
|
||||
},
|
||||
Err(e) => {
|
||||
error!(error = %e, "Error accepting connection");
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
async fn listen_incoming_non_unix(
|
||||
listener: interprocess::local_socket::LocalSocketListener,
|
||||
client_to_server_send: mpsc::Sender<Message>,
|
||||
server_to_clients_recv: broadcast::Receiver<String>,
|
||||
cancel_token: CancellationToken,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::{fs::File, io::Read, path::Path};
|
||||
use sysinfo::{Pid, System};
|
||||
|
||||
use super::models::PeerInfo;
|
||||
@@ -12,12 +14,35 @@ pub fn get_peer_info(peer_pid: u32) -> Result<PeerInfo, String> {
|
||||
}
|
||||
};
|
||||
|
||||
let exe_hash = process.exe().and_then(|p| hash_file_if_exists(p));
|
||||
|
||||
return Ok(PeerInfo::new(
|
||||
peer_pid,
|
||||
process.pid().as_u32(),
|
||||
peer_process_name,
|
||||
exe_hash,
|
||||
));
|
||||
}
|
||||
|
||||
Err("Failed to get process".to_string())
|
||||
}
|
||||
|
||||
fn hash_file_if_exists(path: &Path) -> Option<String> {
|
||||
if path.as_os_str().is_empty() || !path.exists() {
|
||||
return None;
|
||||
}
|
||||
let mut f = File::open(path).ok()?;
|
||||
let mut hasher = Sha256::new();
|
||||
let mut buf = [0u8; 64 * 1024];
|
||||
loop {
|
||||
let n = match f.read(&mut buf) {
|
||||
Ok(n) => n,
|
||||
Err(_) => return None,
|
||||
};
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
hasher.update(&buf[..n]);
|
||||
}
|
||||
Some(format!("{:x}", hasher.finalize()))
|
||||
}
|
||||
|
||||
@@ -9,16 +9,18 @@ pub struct PeerInfo {
|
||||
uid: u32,
|
||||
pid: u32,
|
||||
process_name: String,
|
||||
exe_hash: Option<String>,
|
||||
is_forwarding: Arc<AtomicBool>,
|
||||
host_key: Arc<Mutex<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl PeerInfo {
|
||||
pub fn new(uid: u32, pid: u32, process_name: String) -> Self {
|
||||
pub fn new(uid: u32, pid: u32, process_name: String, exe_hash: Option<String>) -> Self {
|
||||
Self {
|
||||
uid,
|
||||
pid,
|
||||
process_name,
|
||||
exe_hash,
|
||||
is_forwarding: Arc::new(AtomicBool::new(false)),
|
||||
host_key: Arc::new(Mutex::new(Vec::new())),
|
||||
}
|
||||
@@ -29,6 +31,7 @@ impl PeerInfo {
|
||||
uid: 0,
|
||||
pid: 0,
|
||||
process_name: "Unknown application".to_string(),
|
||||
exe_hash: None,
|
||||
is_forwarding: Arc::new(AtomicBool::new(false)),
|
||||
host_key: Arc::new(Mutex::new(Vec::new())),
|
||||
}
|
||||
@@ -46,6 +49,10 @@ impl PeerInfo {
|
||||
&self.process_name
|
||||
}
|
||||
|
||||
pub fn exe_hash(&self) -> Option<&str> {
|
||||
self.exe_hash.as_deref()
|
||||
}
|
||||
|
||||
pub fn is_forwarding(&self) -> bool {
|
||||
self.is_forwarding
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
|
||||
Reference in New Issue
Block a user