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
20pub struct AgentClient<S: AgentStream> {
22 stream: S,
23 buf: CryptoVec,
24}
25
26impl<S: AgentStream + Send + Unpin + 'static> AgentClient<S> {
27 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
41impl<S: AgentStream + Unpin> AgentClient<S> {
43 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 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 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 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 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 self.stream.write_all(&self.buf).await?;
118 self.stream.flush().await?;
119
120 self.buf.clear();
122 self.buf.resize(4);
123 self.stream.read_exact(&mut self.buf).await?;
124
125 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 pub async fn add_identity(
146 &mut self,
147 key: &PrivateKey,
148 constraints: &[Constraint],
149 ) -> Result<(), Error> {
150 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)?; 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 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 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 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)] BigEndian::write_u32(&mut self.buf[..], len as u32);
253 self.read_response().await?;
254 Ok(())
255 }
256
257 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)] 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 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 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)] 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 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 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 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 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 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 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}