bssh_russh/keys/agent/
client.rs

1use core::str;
2
3use byteorder::{BigEndian, ByteOrder};
4use bytes::Bytes;
5use log::{debug, error};
6use ssh_encoding::{Decode, Encode, Reader};
7use ssh_key::{Algorithm, HashAlg, PrivateKey, PublicKey, Signature};
8use tokio;
9use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
10
11use super::{msg, Constraint};
12use crate::helpers::EncodedExt;
13use crate::keys::{key, Error};
14use crate::CryptoVec;
15
16pub trait AgentStream: AsyncRead + AsyncWrite {}
17
18impl<S: AsyncRead + AsyncWrite> AgentStream for S {}
19
20/// SSH agent client.
21pub struct AgentClient<S: AgentStream> {
22    stream: S,
23    buf: CryptoVec,
24}
25
26impl<S: AgentStream + Send + Unpin + 'static> AgentClient<S> {
27    /// Wraps the internal stream in a Box<dyn _>, allowing different client
28    /// implementations to have the same type
29    pub fn dynamic(self) -> AgentClient<Box<dyn AgentStream + Send + Unpin + 'static>> {
30        AgentClient {
31            stream: Box::new(self.stream),
32            buf: self.buf,
33        }
34    }
35
36    pub fn into_inner(self) -> Box<dyn AgentStream + Send + Unpin + 'static> {
37        Box::new(self.stream)
38    }
39}
40
41// https://tools.ietf.org/html/draft-miller-ssh-agent-00#section-4.1
42impl<S: AgentStream + Unpin> AgentClient<S> {
43    /// Build a future that connects to an SSH agent via the provided
44    /// stream (on Unix, usually a Unix-domain socket).
45    pub fn connect(stream: S) -> Self {
46        AgentClient {
47            stream,
48            buf: CryptoVec::new(),
49        }
50    }
51}
52
53#[cfg(unix)]
54impl AgentClient<tokio::net::UnixStream> {
55    /// Connect to an SSH agent via the provided
56    /// stream (on Unix, usually a Unix-domain socket).
57    pub async fn connect_uds<P: AsRef<std::path::Path>>(path: P) -> Result<Self, Error> {
58        let stream = tokio::net::UnixStream::connect(path).await?;
59        Ok(AgentClient {
60            stream,
61            buf: CryptoVec::new(),
62        })
63    }
64
65    /// Connect to an SSH agent specified by the SSH_AUTH_SOCK
66    /// environment variable.
67    pub async fn connect_env() -> Result<Self, Error> {
68        let var = if let Ok(var) = std::env::var("SSH_AUTH_SOCK") {
69            var
70        } else {
71            return Err(Error::EnvVar("SSH_AUTH_SOCK"));
72        };
73        match Self::connect_uds(var).await {
74            Err(Error::IO(io_err)) if io_err.kind() == std::io::ErrorKind::NotFound => {
75                Err(Error::BadAuthSock)
76            }
77            owise => owise,
78        }
79    }
80}
81
82#[cfg(windows)]
83const ERROR_PIPE_BUSY: u32 = 231u32;
84
85#[cfg(windows)]
86impl AgentClient<pageant::PageantStream> {
87    /// Connect to a running Pageant instance
88    pub async fn connect_pageant() -> Result<Self, Error> {
89        Ok(Self::connect(pageant::PageantStream::new().await?))
90    }
91}
92
93#[cfg(windows)]
94impl AgentClient<tokio::net::windows::named_pipe::NamedPipeClient> {
95    /// Connect to an SSH agent via a Windows named pipe
96    pub async fn connect_named_pipe<P: AsRef<std::ffi::OsStr>>(path: P) -> Result<Self, Error> {
97        let stream = loop {
98            match tokio::net::windows::named_pipe::ClientOptions::new().open(path.as_ref()) {
99                Ok(client) => break client,
100                Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => (),
101                Err(e) => return Err(e.into()),
102            }
103
104            tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
105        };
106
107        Ok(AgentClient {
108            stream,
109            buf: CryptoVec::new(),
110        })
111    }
112}
113
114impl<S: AgentStream + Unpin> AgentClient<S> {
115    async fn read_response(&mut self) -> Result<(), Error> {
116        // Writing the message
117        self.stream.write_all(&self.buf).await?;
118        self.stream.flush().await?;
119
120        // Reading the length
121        self.buf.clear();
122        self.buf.resize(4);
123        self.stream.read_exact(&mut self.buf).await?;
124
125        // Reading the rest of the buffer
126        let len = BigEndian::read_u32(&self.buf) as usize;
127        self.buf.clear();
128        self.buf.resize(len);
129        self.stream.read_exact(&mut self.buf).await?;
130
131        Ok(())
132    }
133
134    async fn read_success(&mut self) -> Result<(), Error> {
135        self.read_response().await?;
136        if self.buf.first() == Some(&msg::SUCCESS) {
137            Ok(())
138        } else {
139            Err(Error::AgentFailure)
140        }
141    }
142
143    /// Send a key to the agent, with a (possibly empty) slice of
144    /// constraints to apply when using the key to sign.
145    pub async fn add_identity(
146        &mut self,
147        key: &PrivateKey,
148        constraints: &[Constraint],
149    ) -> Result<(), Error> {
150        // See IETF draft-miller-ssh-agent-13, section 3.2 for format.
151        // https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent
152        self.buf.clear();
153        self.buf.resize(4);
154        if constraints.is_empty() {
155            self.buf.push(msg::ADD_IDENTITY)
156        } else {
157            self.buf.push(msg::ADD_ID_CONSTRAINED)
158        }
159
160        key.key_data().encode(&mut self.buf)?;
161        "".encode(&mut self.buf)?; // comment field
162
163        if !constraints.is_empty() {
164            for cons in constraints {
165                match *cons {
166                    Constraint::KeyLifetime { seconds } => {
167                        msg::CONSTRAIN_LIFETIME.encode(&mut self.buf)?;
168                        seconds.encode(&mut self.buf)?;
169                    }
170                    Constraint::Confirm => self.buf.push(msg::CONSTRAIN_CONFIRM),
171                    Constraint::Extensions {
172                        ref name,
173                        ref details,
174                    } => {
175                        msg::CONSTRAIN_EXTENSION.encode(&mut self.buf)?;
176                        name.encode(&mut self.buf)?;
177                        details.encode(&mut self.buf)?;
178                    }
179                }
180            }
181        }
182        let len = self.buf.len() - 4;
183        BigEndian::write_u32(&mut self.buf[..], len as u32);
184
185        self.read_success().await?;
186        Ok(())
187    }
188
189    /// Add a smart card to the agent, with a (possibly empty) set of
190    /// constraints to apply when signing.
191    pub async fn add_smartcard_key(
192        &mut self,
193        id: &str,
194        pin: &[u8],
195        constraints: &[Constraint],
196    ) -> Result<(), Error> {
197        self.buf.clear();
198        self.buf.resize(4);
199        if constraints.is_empty() {
200            self.buf.push(msg::ADD_SMARTCARD_KEY)
201        } else {
202            self.buf.push(msg::ADD_SMARTCARD_KEY_CONSTRAINED)
203        }
204        id.encode(&mut self.buf)?;
205        pin.encode(&mut self.buf)?;
206        if !constraints.is_empty() {
207            (constraints.len() as u32).encode(&mut self.buf)?;
208            for cons in constraints {
209                match *cons {
210                    Constraint::KeyLifetime { seconds } => {
211                        msg::CONSTRAIN_LIFETIME.encode(&mut self.buf)?;
212                        seconds.encode(&mut self.buf)?;
213                    }
214                    Constraint::Confirm => self.buf.push(msg::CONSTRAIN_CONFIRM),
215                    Constraint::Extensions {
216                        ref name,
217                        ref details,
218                    } => {
219                        msg::CONSTRAIN_EXTENSION.encode(&mut self.buf)?;
220                        name.encode(&mut self.buf)?;
221                        details.encode(&mut self.buf)?;
222                    }
223                }
224            }
225        }
226        let len = self.buf.len() - 4;
227        BigEndian::write_u32(&mut self.buf[..], len as u32);
228        self.read_response().await?;
229        Ok(())
230    }
231
232    /// Lock the agent, making it refuse to sign until unlocked.
233    pub async fn lock(&mut self, passphrase: &[u8]) -> Result<(), Error> {
234        self.buf.clear();
235        self.buf.resize(4);
236        self.buf.push(msg::LOCK);
237        passphrase.encode(&mut self.buf)?;
238        let len = self.buf.len() - 4;
239        BigEndian::write_u32(&mut self.buf[..], len as u32);
240        self.read_response().await?;
241        Ok(())
242    }
243
244    /// Unlock the agent, allowing it to sign again.
245    pub async fn unlock(&mut self, passphrase: &[u8]) -> Result<(), Error> {
246        self.buf.clear();
247        self.buf.resize(4);
248        msg::UNLOCK.encode(&mut self.buf)?;
249        passphrase.encode(&mut self.buf)?;
250        let len = self.buf.len() - 4;
251        #[allow(clippy::indexing_slicing)] // static length
252        BigEndian::write_u32(&mut self.buf[..], len as u32);
253        self.read_response().await?;
254        Ok(())
255    }
256
257    /// Ask the agent for a list of the currently registered secret
258    /// keys.
259    pub async fn request_identities(&mut self) -> Result<Vec<PublicKey>, Error> {
260        self.buf.clear();
261        self.buf.resize(4);
262        msg::REQUEST_IDENTITIES.encode(&mut self.buf)?;
263        let len = self.buf.len() - 4;
264        BigEndian::write_u32(&mut self.buf[..], len as u32);
265
266        self.read_response().await?;
267        debug!("identities: {:?}", &self.buf[..]);
268        let mut keys = Vec::new();
269
270        #[allow(clippy::indexing_slicing)] // static length
271        if let Some((&msg::IDENTITIES_ANSWER, mut r)) = self.buf.split_first() {
272            let n = u32::decode(&mut r)?;
273            for _ in 0..n {
274                let key_blob = Bytes::decode(&mut r)?;
275                let comment = String::decode(&mut r)?;
276                let mut key = key::parse_public_key(&key_blob)?;
277                key.set_comment(comment);
278                keys.push(key);
279            }
280        }
281
282        Ok(keys)
283    }
284
285    /// Ask the agent to sign the supplied piece of data.
286    pub async fn sign_request(
287        &mut self,
288        public: &PublicKey,
289        hash_alg: Option<HashAlg>,
290        mut data: CryptoVec,
291    ) -> Result<CryptoVec, Error> {
292        debug!("sign_request: {data:?}");
293        let hash = self.prepare_sign_request(public, hash_alg, &data)?;
294
295        self.read_response().await?;
296
297        match self.buf.split_first() {
298            Some((&msg::SIGN_RESPONSE, mut r)) => {
299                self.write_signature(&mut r, hash, &mut data)?;
300                Ok(data)
301            }
302            Some((&msg::FAILURE, _)) => Err(Error::AgentFailure),
303            _ => {
304                debug!("self.buf = {:?}", &self.buf[..]);
305                Err(Error::AgentProtocolError)
306            }
307        }
308    }
309
310    fn prepare_sign_request(
311        &mut self,
312        public: &ssh_key::PublicKey,
313        hash_alg: Option<HashAlg>,
314        data: &[u8],
315    ) -> Result<u32, Error> {
316        self.buf.clear();
317        self.buf.resize(4);
318        msg::SIGN_REQUEST.encode(&mut self.buf)?;
319        public.key_data().encoded()?.encode(&mut self.buf)?;
320        data.encode(&mut self.buf)?;
321        debug!("public = {public:?}");
322
323        let hash = match public.algorithm() {
324            Algorithm::Rsa { .. } => match hash_alg {
325                Some(HashAlg::Sha256) => 2,
326                Some(HashAlg::Sha512) => 4,
327                _ => 0,
328            },
329            _ => 0,
330        };
331
332        hash.encode(&mut self.buf)?;
333        let len = self.buf.len() - 4;
334        BigEndian::write_u32(&mut self.buf[..], len as u32);
335        Ok(hash)
336    }
337
338    fn write_signature<R: Reader>(
339        &self,
340        r: &mut R,
341        hash: u32,
342        data: &mut CryptoVec,
343    ) -> Result<(), Error> {
344        let mut resp = &Bytes::decode(r)?[..];
345        let t = String::decode(&mut resp)?;
346        if (hash == 2 && t == "rsa-sha2-256") || (hash == 4 && t == "rsa-sha2-512") || hash == 0 {
347            let sig = Bytes::decode(&mut resp)?;
348            (t.len() + sig.len() + 8).encode(data)?;
349            t.encode(data)?;
350            sig.encode(data)?;
351            Ok(())
352        } else {
353            error!("unexpected agent signature type: {t:?}");
354            Err(Error::AgentProtocolError)
355        }
356    }
357
358    /// Ask the agent to sign the supplied piece of data.
359    pub fn sign_request_base64(
360        mut self,
361        public: &ssh_key::PublicKey,
362        hash_alg: Option<HashAlg>,
363        data: &[u8],
364    ) -> impl futures::Future<Output = (Self, Result<String, Error>)> {
365        debug!("sign_request: {data:?}");
366        let r = self.prepare_sign_request(public, hash_alg, data);
367        async move {
368            if let Err(e) = r {
369                return (self, Err(e));
370            }
371
372            let resp = self.read_response().await;
373            if let Err(e) = resp {
374                return (self, Err(e));
375            }
376
377            #[allow(clippy::indexing_slicing)] // length is checked
378            if !self.buf.is_empty() && self.buf[0] == msg::SIGN_RESPONSE {
379                let base64 = data_encoding::BASE64_NOPAD.encode(&self.buf[1..]);
380                (self, Ok(base64))
381            } else {
382                (self, Ok(String::new()))
383            }
384        }
385    }
386
387    /// Ask the agent to sign the supplied piece of data, and return a `Signature`.
388    pub async fn sign_request_signature(
389        &mut self,
390        public: &ssh_key::PublicKey,
391        hash_alg: Option<HashAlg>,
392        data: &[u8],
393    ) -> Result<Signature, Error> {
394        debug!("sign_request: {data:?}");
395
396        self.prepare_sign_request(public, hash_alg, data)?;
397        self.read_response().await?;
398
399        match self.buf.split_first() {
400            Some((&msg::SIGN_RESPONSE, mut r)) => {
401                let mut resp = &Bytes::decode(&mut r)?[..];
402                let sig = Signature::decode(&mut resp)?;
403                Ok(sig)
404            }
405            _ => Err(Error::AgentProtocolError),
406        }
407    }
408
409    /// Ask the agent to remove a key from its memory.
410    pub async fn remove_identity(&mut self, public: &ssh_key::PublicKey) -> Result<(), Error> {
411        self.buf.clear();
412        self.buf.resize(4);
413        self.buf.push(msg::REMOVE_IDENTITY);
414        public.key_data().encoded()?.encode(&mut self.buf)?;
415        let len = self.buf.len() - 4;
416        BigEndian::write_u32(&mut self.buf[..], len as u32);
417        self.read_response().await?;
418        Ok(())
419    }
420
421    /// Ask the agent to remove a smartcard from its memory.
422    pub async fn remove_smartcard_key(&mut self, id: &str, pin: &[u8]) -> Result<(), Error> {
423        self.buf.clear();
424        self.buf.resize(4);
425        msg::REMOVE_SMARTCARD_KEY.encode(&mut self.buf)?;
426        id.encode(&mut self.buf)?;
427        pin.encode(&mut self.buf)?;
428        let len = self.buf.len() - 4;
429        BigEndian::write_u32(&mut self.buf[..], len as u32);
430        self.read_response().await?;
431        Ok(())
432    }
433
434    /// Ask the agent to forget all known keys.
435    pub async fn remove_all_identities(&mut self) -> Result<(), Error> {
436        self.buf.clear();
437        self.buf.resize(4);
438        msg::REMOVE_ALL_IDENTITIES.encode(&mut self.buf)?;
439        1u32.encode(&mut self.buf)?;
440        self.read_success().await?;
441        Ok(())
442    }
443
444    /// Send a custom message to the agent.
445    pub async fn extension(&mut self, typ: &[u8], ext: &[u8]) -> Result<(), Error> {
446        self.buf.clear();
447        self.buf.resize(4);
448        msg::EXTENSION.encode(&mut self.buf)?;
449        typ.encode(&mut self.buf)?;
450        ext.encode(&mut self.buf)?;
451        let len = self.buf.len() - 4;
452        (len as u32).encode(&mut self.buf)?;
453        self.read_response().await?;
454        Ok(())
455    }
456
457    /// Ask the agent what extensions about supported extensions.
458    pub async fn query_extension(&mut self, typ: &[u8], mut ext: CryptoVec) -> Result<bool, Error> {
459        self.buf.clear();
460        self.buf.resize(4);
461        msg::EXTENSION.encode(&mut self.buf)?;
462        typ.encode(&mut self.buf)?;
463        let len = self.buf.len() - 4;
464        (len as u32).encode(&mut self.buf)?;
465        self.read_response().await?;
466
467        match self.buf.split_first() {
468            Some((&msg::SUCCESS, mut r)) => {
469                ext.extend(&Bytes::decode(&mut r)?);
470                Ok(true)
471            }
472            _ => Ok(false),
473        }
474    }
475}