2024-07-03 06:33:58 -04:00
|
|
|
use std::io::ErrorKind;
|
|
|
|
|
|
|
|
use futures::SinkExt;
|
2024-06-16 07:08:10 -04:00
|
|
|
use signature::Signer;
|
2024-07-03 06:33:58 -04:00
|
|
|
use ssh_agent_lib::agent::MessageCodec;
|
|
|
|
use ssh_agent_lib::proto::message::{
|
|
|
|
Message,
|
|
|
|
Identity,
|
|
|
|
SignRequest,
|
|
|
|
};
|
|
|
|
use tokio::net::{UnixListener, UnixStream};
|
|
|
|
use tauri::{
|
|
|
|
AppHandle,
|
|
|
|
Manager,
|
|
|
|
async_runtime as rt,
|
|
|
|
};
|
|
|
|
use tokio_util::codec::Framed;
|
|
|
|
use tokio_stream::StreamExt;
|
|
|
|
use tokio::sync::oneshot;
|
|
|
|
|
|
|
|
use crate::clientinfo;
|
|
|
|
use crate::errors::*;
|
|
|
|
use crate::ipc::{Approval, RequestNotification};
|
|
|
|
use crate::state::AppState;
|
|
|
|
|
|
|
|
|
|
|
|
pub struct Agent {
|
|
|
|
listener: UnixListener,
|
|
|
|
app_handle: AppHandle,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Agent {
|
|
|
|
pub fn start(app_handle: AppHandle) -> std::io::Result<()> {
|
|
|
|
match std::fs::remove_file("/tmp/creddy-agent.sock") {
|
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) if e.kind() == ErrorKind::NotFound => (),
|
|
|
|
Err(e) => return Err(e),
|
|
|
|
}
|
|
|
|
|
|
|
|
let listener = UnixListener::bind("/tmp/creddy-agent.sock")?;
|
|
|
|
let srv = Agent { listener, app_handle };
|
|
|
|
rt::spawn(srv.serve());
|
|
|
|
Ok(())
|
|
|
|
}
|
2024-06-16 07:08:10 -04:00
|
|
|
|
2024-07-03 06:33:58 -04:00
|
|
|
async fn serve(self) {
|
|
|
|
loop {
|
|
|
|
self.try_serve()
|
|
|
|
.await
|
|
|
|
.error_print_prefix("Error accepting request: ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn try_serve(&self) -> Result<(), HandlerError> {
|
|
|
|
let (stream, _addr) = self.listener.accept().await?;
|
|
|
|
let new_handle = self.app_handle.clone();
|
|
|
|
let client_pid = get_client_pid(&stream)?;
|
|
|
|
rt::spawn(async move {
|
|
|
|
let adapter = Framed::new(stream, MessageCodec);
|
|
|
|
handle_framed(adapter, new_handle, client_pid)
|
|
|
|
.await
|
|
|
|
.error_print_prefix("Error responding to request: ");
|
|
|
|
});
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
2024-06-16 07:08:10 -04:00
|
|
|
|
|
|
|
|
2024-07-03 06:33:58 -04:00
|
|
|
async fn handle_framed(
|
|
|
|
mut adapter: Framed<UnixStream, MessageCodec>,
|
|
|
|
app_handle: AppHandle,
|
|
|
|
client_pid: u32,
|
|
|
|
) -> Result<(), HandlerError> {
|
|
|
|
while let Some(message) = adapter.try_next().await? {
|
|
|
|
let resp = match message {
|
|
|
|
Message::RequestIdentities => list_identities(app_handle.clone()).await?,
|
|
|
|
Message::SignRequest(req) => sign_request(req, app_handle.clone(), client_pid).await?,
|
|
|
|
_ => Message::Failure,
|
|
|
|
};
|
|
|
|
|
|
|
|
adapter.send(resp).await?;
|
2024-06-16 07:08:10 -04:00
|
|
|
}
|
2024-07-03 06:33:58 -04:00
|
|
|
|
|
|
|
Ok(())
|
2024-06-16 07:08:10 -04:00
|
|
|
}
|
|
|
|
|
2024-07-03 06:33:58 -04:00
|
|
|
|
|
|
|
async fn list_identities(app_handle: AppHandle) -> Result<Message, HandlerError> {
|
|
|
|
let state = app_handle.state::<AppState>();
|
|
|
|
let identities: Vec<Identity> = state.list_ssh_identities().await?;
|
|
|
|
Ok(Message::IdentitiesAnswer(identities))
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async fn sign_request(req: SignRequest, app_handle: AppHandle, client_pid: u32) -> Result<Message, HandlerError> {
|
|
|
|
let state = app_handle.state::<AppState>();
|
|
|
|
let rehide_ms = {
|
|
|
|
let config = state.config.read().await;
|
|
|
|
config.rehide_ms
|
|
|
|
};
|
|
|
|
let client = clientinfo::get_client(client_pid, false)?;
|
|
|
|
let lease = state.acquire_visibility_lease(rehide_ms).await
|
|
|
|
.map_err(|_e| HandlerError::NoMainWindow)?;
|
|
|
|
|
|
|
|
let (chan_send, chan_recv) = oneshot::channel();
|
|
|
|
let request_id = state.register_request(chan_send).await;
|
|
|
|
|
|
|
|
let proceed = async {
|
|
|
|
let key_name = state.ssh_name_from_pubkey(&req.pubkey_blob).await?;
|
|
|
|
let notification = RequestNotification::new_ssh(request_id, client, key_name.clone());
|
|
|
|
app_handle.emit("credential-request", ¬ification)?;
|
|
|
|
|
|
|
|
let response = chan_recv.await?;
|
|
|
|
if let Approval::Denied = response.approval {
|
|
|
|
return Ok(Message::Failure);
|
2024-06-16 07:08:10 -04:00
|
|
|
}
|
2024-07-03 06:33:58 -04:00
|
|
|
|
|
|
|
let key = state.sshkey_by_name(&key_name).await?;
|
|
|
|
let sig = Signer::sign(&key.private_key, &req.data);
|
|
|
|
let key_type = key.algorithm.as_str().as_bytes();
|
|
|
|
|
|
|
|
let payload_len = key_type.len() + sig.as_bytes().len() + 8;
|
|
|
|
let mut payload = Vec::with_capacity(payload_len);
|
|
|
|
encode_string(&mut payload, key.algorithm.as_str().as_bytes());
|
|
|
|
encode_string(&mut payload, sig.as_bytes());
|
|
|
|
|
|
|
|
Ok(Message::SignResponse(payload))
|
|
|
|
};
|
|
|
|
|
|
|
|
let res = proceed.await;
|
2024-07-03 06:47:25 -04:00
|
|
|
if let Err(_) = &res {
|
2024-07-03 06:33:58 -04:00
|
|
|
state.unregister_request(request_id).await;
|
2024-06-16 07:08:10 -04:00
|
|
|
}
|
2024-07-03 06:33:58 -04:00
|
|
|
|
|
|
|
lease.release();
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn get_client_pid(stream: &UnixStream) -> std::io::Result<u32> {
|
|
|
|
let cred = stream.peer_cred()?;
|
|
|
|
Ok(cred.pid().unwrap() as u32)
|
2024-06-16 07:08:10 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn encode_string(buf: &mut Vec<u8>, s: &[u8]) {
|
|
|
|
let len = s.len() as u32;
|
|
|
|
buf.extend(len.to_be_bytes());
|
|
|
|
buf.extend(s);
|
|
|
|
}
|