ssh_agent_lib/
blocking.rs

1//! Blocking SSH agent client API.
2//!
3//! Blocking API is always enabled since it doesn't use additional
4//! dependencies over what is in the `proto` module and Rust standard
5//! library.
6//!
7//! # Examples
8//!
9//! ```no_run
10//! # #[cfg(unix)]
11//! # fn main() -> testresult::TestResult {
12//! use std::os::unix::net::UnixStream;
13//!
14//! use ssh_agent_lib::blocking::Client;
15//!
16//! let mut client = Client::new(UnixStream::connect(std::env::var("SSH_AUTH_SOCK")?)?);
17//!
18//! eprintln!(
19//!     "Identities that this agent knows of: {:#?}",
20//!     client.request_identities()?
21//! );
22//! # Ok(()) }
23//! # #[cfg(windows)] fn main() { }
24//! ```
25
26use std::io::{Read, Write};
27
28use byteorder::{BigEndian, ByteOrder};
29use ssh_encoding::{Decode, Encode};
30use ssh_key::Signature;
31
32use crate::{
33    error::AgentError,
34    proto::{
35        AddIdentity, AddIdentityConstrained, AddSmartcardKeyConstrained, Extension, Identity,
36        ProtoError, RemoveIdentity, Request, Response, SignRequest, SmartcardKey,
37    },
38};
39
40/// Blocking SSH agent client.
41#[derive(Debug)]
42pub struct Client<S: Read + Write> {
43    stream: S,
44}
45
46impl<S: Read + Write> Client<S> {
47    /// Construct a new SSH agent client for the given transport stream.
48    pub fn new(stream: S) -> Self {
49        Self { stream }
50    }
51
52    /// Extracts inner stream by consuming this object.
53    pub fn into_inner(self) -> S {
54        self.stream
55    }
56
57    fn handle(&mut self, request: Request) -> Result<Response, ProtoError> {
58        // send the request
59        let mut bytes = Vec::new();
60        let len = request.encoded_len()? as u32;
61        len.encode(&mut bytes)?;
62        request.encode(&mut bytes)?;
63        self.stream.write_all(&bytes)?;
64
65        // read the response
66        let mut len: [u8; 4] = [0; 4];
67        self.stream.read_exact(&mut len[..])?;
68        let len = BigEndian::read_u32(&len) as usize;
69        bytes.resize(len, 0);
70        self.stream.read_exact(&mut bytes)?;
71
72        Response::decode(&mut &bytes[..])
73    }
74
75    /// Request a list of keys managed by this session.
76    pub fn request_identities(&mut self) -> Result<Vec<Identity>, AgentError> {
77        if let Response::IdentitiesAnswer(identities) = self.handle(Request::RequestIdentities)? {
78            Ok(identities)
79        } else {
80            Err(ProtoError::UnexpectedResponse.into())
81        }
82    }
83
84    /// Perform a private key signature operation.
85    pub fn sign(&mut self, request: SignRequest) -> Result<Signature, AgentError> {
86        if let Response::SignResponse(response) = self.handle(Request::SignRequest(request))? {
87            Ok(response)
88        } else {
89            Err(ProtoError::UnexpectedResponse.into())
90        }
91    }
92
93    /// Add a private key to the agent.
94    pub fn add_identity(&mut self, identity: AddIdentity) -> Result<(), AgentError> {
95        if let Response::Success = self.handle(Request::AddIdentity(identity))? {
96            Ok(())
97        } else {
98            Err(ProtoError::UnexpectedResponse.into())
99        }
100    }
101
102    /// Add a private key to the agent with a set of constraints.
103    pub fn add_identity_constrained(
104        &mut self,
105        identity: AddIdentityConstrained,
106    ) -> Result<(), AgentError> {
107        if let Response::Success = self.handle(Request::AddIdConstrained(identity))? {
108            Ok(())
109        } else {
110            Err(ProtoError::UnexpectedResponse.into())
111        }
112    }
113
114    /// Remove private key from an agent.
115    pub fn remove_identity(&mut self, identity: RemoveIdentity) -> Result<(), AgentError> {
116        if let Response::Success = self.handle(Request::RemoveIdentity(identity))? {
117            Ok(())
118        } else {
119            Err(ProtoError::UnexpectedResponse.into())
120        }
121    }
122
123    /// Remove all keys from an agent.
124    pub fn remove_all_identities(&mut self) -> Result<(), AgentError> {
125        if let Response::Success = self.handle(Request::RemoveAllIdentities)? {
126            Ok(())
127        } else {
128            Err(ProtoError::UnexpectedResponse.into())
129        }
130    }
131
132    /// Add a key stored on a smartcard.
133    pub fn add_smartcard_key(&mut self, key: SmartcardKey) -> Result<(), AgentError> {
134        if let Response::Success = self.handle(Request::AddSmartcardKey(key))? {
135            Ok(())
136        } else {
137            Err(ProtoError::UnexpectedResponse.into())
138        }
139    }
140
141    /// Add a key stored on a smartcard with a set of constraints.
142    pub fn add_smartcard_key_constrained(
143        &mut self,
144        key: AddSmartcardKeyConstrained,
145    ) -> Result<(), AgentError> {
146        if let Response::Success = self.handle(Request::AddSmartcardKeyConstrained(key))? {
147            Ok(())
148        } else {
149            Err(ProtoError::UnexpectedResponse.into())
150        }
151    }
152
153    /// Remove a smartcard key from the agent.
154    pub fn remove_smartcard_key(&mut self, key: SmartcardKey) -> Result<(), AgentError> {
155        if let Response::Success = self.handle(Request::RemoveSmartcardKey(key))? {
156            Ok(())
157        } else {
158            Err(ProtoError::UnexpectedResponse.into())
159        }
160    }
161
162    /// Temporarily lock the agent with a password.
163    pub fn lock(&mut self, key: String) -> Result<(), AgentError> {
164        if let Response::Success = self.handle(Request::Lock(key))? {
165            Ok(())
166        } else {
167            Err(ProtoError::UnexpectedResponse.into())
168        }
169    }
170
171    /// Unlock the agent with a password.
172    pub fn unlock(&mut self, key: String) -> Result<(), AgentError> {
173        if let Response::Success = self.handle(Request::Unlock(key))? {
174            Ok(())
175        } else {
176            Err(ProtoError::UnexpectedResponse.into())
177        }
178    }
179
180    /// Invoke a custom, vendor-specific extension on the agent.
181    pub fn extension(&mut self, extension: Extension) -> Result<Option<Extension>, AgentError> {
182        match self.handle(Request::Extension(extension))? {
183            Response::Success => Ok(None),
184            Response::ExtensionResponse(response) => Ok(Some(response)),
185            _ => Err(ProtoError::UnexpectedResponse.into()),
186        }
187    }
188}