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, Certificate, HashAlg, PrivateKey, PublicKey, Signature};
8use tokio;
9use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
10
11use super::{AgentIdentity, Constraint, msg};
12use crate::CryptoVec;
13use crate::helpers::EncodedExt;
14use crate::keys::{Error, key};
15
16pub trait AgentStream: AsyncRead + AsyncWrite {}
17
18impl<S: AsyncRead + AsyncWrite> AgentStream for S {}
19
20const MAX_AGENT_FRAME_LEN: usize = 256 * 1024;
21
22pub struct AgentClient<S: AgentStream> {
24 stream: S,
25 buf: Vec<u8>,
26}
27
28impl<S: AgentStream + Send + Unpin + 'static> AgentClient<S> {
29 pub fn dynamic(self) -> AgentClient<Box<dyn AgentStream + Send + Unpin + 'static>> {
32 AgentClient {
33 stream: Box::new(self.stream),
34 buf: self.buf,
35 }
36 }
37
38 pub fn into_inner(self) -> Box<dyn AgentStream + Send + Unpin + 'static> {
39 Box::new(self.stream)
40 }
41}
42
43impl<S: AgentStream + Unpin> AgentClient<S> {
45 pub fn connect(stream: S) -> Self {
48 AgentClient {
49 stream,
50 buf: Vec::new(),
51 }
52 }
53}
54
55#[cfg(unix)]
56impl AgentClient<tokio::net::UnixStream> {
57 pub async fn connect_uds<P: AsRef<std::path::Path>>(path: P) -> Result<Self, Error> {
60 let stream = tokio::net::UnixStream::connect(path).await?;
61 Ok(AgentClient {
62 stream,
63 buf: Vec::new(),
64 })
65 }
66
67 pub async fn connect_env() -> Result<Self, Error> {
70 let var = if let Ok(var) = std::env::var("SSH_AUTH_SOCK") {
71 var
72 } else {
73 return Err(Error::EnvVar("SSH_AUTH_SOCK"));
74 };
75 match Self::connect_uds(var).await {
76 Err(Error::IO(io_err)) if io_err.kind() == std::io::ErrorKind::NotFound => {
77 Err(Error::BadAuthSock)
78 }
79 owise => owise,
80 }
81 }
82}
83
84#[cfg(windows)]
85const ERROR_PIPE_BUSY: u32 = 231u32;
86
87#[cfg(windows)]
88impl AgentClient<pageant::PageantStream> {
89 pub async fn connect_pageant() -> Result<Self, Error> {
91 Ok(Self::connect(pageant::PageantStream::new().await?))
92 }
93}
94
95#[cfg(windows)]
96impl AgentClient<tokio::net::windows::named_pipe::NamedPipeClient> {
97 pub async fn connect_named_pipe<P: AsRef<std::ffi::OsStr>>(path: P) -> Result<Self, Error> {
99 let stream = loop {
100 match tokio::net::windows::named_pipe::ClientOptions::new().open(path.as_ref()) {
101 Ok(client) => break client,
102 Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => (),
103 Err(e) => return Err(e.into()),
104 }
105
106 tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
107 };
108
109 Ok(AgentClient {
110 stream,
111 buf: Vec::new(),
112 })
113 }
114}
115
116impl<S: AgentStream + Unpin> AgentClient<S> {
117 async fn read_frame(&mut self) -> Result<(), Error> {
118 self.buf.clear();
119 self.buf.resize(4, 0);
120 self.stream.read_exact(&mut self.buf).await?;
121
122 let len = BigEndian::read_u32(&self.buf) as usize;
123 if len > MAX_AGENT_FRAME_LEN {
124 return Err(Error::AgentProtocolError);
125 }
126
127 self.buf.clear();
128 self.buf.resize(len, 0);
129 self.stream.read_exact(&mut self.buf).await?;
130 Ok(())
131 }
132
133 async fn read_response(&mut self) -> Result<(), Error> {
134 self.stream.write_all(&self.buf).await?;
136 self.stream.flush().await?;
137 self.read_frame().await
138 }
139
140 async fn read_success(&mut self) -> Result<(), Error> {
141 self.read_response().await?;
142 if self.buf.first() == Some(&msg::SUCCESS) {
143 Ok(())
144 } else {
145 Err(Error::AgentFailure)
146 }
147 }
148
149 pub async fn add_identity(
152 &mut self,
153 key: &PrivateKey,
154 constraints: &[Constraint],
155 ) -> Result<(), Error> {
156 self.buf.clear();
159 self.buf.resize(4, 0);
160 if constraints.is_empty() {
161 self.buf.push(msg::ADD_IDENTITY)
162 } else {
163 self.buf.push(msg::ADD_ID_CONSTRAINED)
164 }
165
166 key.key_data().encode(&mut self.buf)?;
167 "".encode(&mut self.buf)?; if !constraints.is_empty() {
170 for cons in constraints {
171 match *cons {
172 Constraint::KeyLifetime { seconds } => {
173 msg::CONSTRAIN_LIFETIME.encode(&mut self.buf)?;
174 seconds.encode(&mut self.buf)?;
175 }
176 Constraint::Confirm => self.buf.push(msg::CONSTRAIN_CONFIRM),
177 Constraint::Extensions {
178 ref name,
179 ref details,
180 } => {
181 msg::CONSTRAIN_EXTENSION.encode(&mut self.buf)?;
182 name.encode(&mut self.buf)?;
183 details.encode(&mut self.buf)?;
184 }
185 }
186 }
187 }
188 let len = self.buf.len() - 4;
189 BigEndian::write_u32(&mut self.buf[..], len as u32);
190
191 self.read_success().await?;
192 Ok(())
193 }
194
195 pub async fn add_smartcard_key(
198 &mut self,
199 id: &str,
200 pin: &[u8],
201 constraints: &[Constraint],
202 ) -> Result<(), Error> {
203 self.buf.clear();
204 self.buf.resize(4, 0);
205 if constraints.is_empty() {
206 self.buf.push(msg::ADD_SMARTCARD_KEY)
207 } else {
208 self.buf.push(msg::ADD_SMARTCARD_KEY_CONSTRAINED)
209 }
210 id.encode(&mut self.buf)?;
211 pin.encode(&mut self.buf)?;
212 if !constraints.is_empty() {
213 (constraints.len() as u32).encode(&mut self.buf)?;
214 for cons in constraints {
215 match *cons {
216 Constraint::KeyLifetime { seconds } => {
217 msg::CONSTRAIN_LIFETIME.encode(&mut self.buf)?;
218 seconds.encode(&mut self.buf)?;
219 }
220 Constraint::Confirm => self.buf.push(msg::CONSTRAIN_CONFIRM),
221 Constraint::Extensions {
222 ref name,
223 ref details,
224 } => {
225 msg::CONSTRAIN_EXTENSION.encode(&mut self.buf)?;
226 name.encode(&mut self.buf)?;
227 details.encode(&mut self.buf)?;
228 }
229 }
230 }
231 }
232 let len = self.buf.len() - 4;
233 BigEndian::write_u32(&mut self.buf[..], len as u32);
234 self.read_response().await?;
235 Ok(())
236 }
237
238 pub async fn lock(&mut self, passphrase: &[u8]) -> Result<(), Error> {
240 self.buf.clear();
241 self.buf.resize(4, 0);
242 self.buf.push(msg::LOCK);
243 passphrase.encode(&mut self.buf)?;
244 let len = self.buf.len() - 4;
245 BigEndian::write_u32(&mut self.buf[..], len as u32);
246 self.read_response().await?;
247 Ok(())
248 }
249
250 pub async fn unlock(&mut self, passphrase: &[u8]) -> Result<(), Error> {
252 self.buf.clear();
253 self.buf.resize(4, 0);
254 msg::UNLOCK.encode(&mut self.buf)?;
255 passphrase.encode(&mut self.buf)?;
256 let len = self.buf.len() - 4;
257 #[allow(clippy::indexing_slicing)] BigEndian::write_u32(&mut self.buf[..], len as u32);
259 self.read_response().await?;
260 Ok(())
261 }
262
263 pub async fn request_identities(&mut self) -> Result<Vec<AgentIdentity>, Error> {
265 self.buf.clear();
266 self.buf.resize(4, 0);
267 msg::REQUEST_IDENTITIES.encode(&mut self.buf)?;
268 let len = self.buf.len() - 4;
269 BigEndian::write_u32(&mut self.buf[..], len as u32);
270
271 self.read_response().await?;
272 debug!("identities: {:?}", &self.buf[..]);
273 let mut identities = Vec::new();
274
275 #[allow(clippy::indexing_slicing)] if let Some((&msg::IDENTITIES_ANSWER, mut r)) = self.buf.split_first() {
277 let n = u32::decode(&mut r)?;
278 for _ in 0..n {
279 let key_blob = Bytes::decode(&mut r)?;
280 let comment = String::decode(&mut r)?;
281
282 let identity = if Self::is_certificate_blob(&key_blob) {
286 match Certificate::decode(&mut key_blob.as_ref()) {
287 Ok(cert) => AgentIdentity::Certificate { certificate: cert, comment },
288 Err(_) => {
289 let key = key::parse_public_key(&key_blob)?;
291 AgentIdentity::PublicKey { key, comment }
292 }
293 }
294 } else {
295 let key = key::parse_public_key(&key_blob)?;
296 AgentIdentity::PublicKey { key, comment }
297 };
298 identities.push(identity);
299 }
300 }
301
302 Ok(identities)
303 }
304
305 fn is_certificate_blob(blob: &[u8]) -> bool {
308 let Some(len_bytes) = blob.get(..4) else {
311 return false;
312 };
313 let alg_len = BigEndian::read_u32(len_bytes) as usize;
314 let Some(alg_bytes) = blob.get(4..4 + alg_len) else {
315 return false;
316 };
317 if let Ok(alg_str) = str::from_utf8(alg_bytes) {
318 alg_str.ends_with("-cert-v01@openssh.com")
319 } else {
320 false
321 }
322 }
323
324 pub async fn sign_request(
326 &mut self,
327 identity: &AgentIdentity,
328 hash_alg: Option<HashAlg>,
329 data: Vec<u8>,
330 ) -> Result<Vec<u8>, Error> {
331 match identity {
332 AgentIdentity::PublicKey { key, .. } => self.sign_request_pk(key, hash_alg, data).await,
333 AgentIdentity::Certificate { certificate, .. } => {
334 self.sign_request_cert(certificate, hash_alg, data).await
335 }
336 }
337 }
338
339 async fn sign_request_pk(
340 &mut self,
341 public: &PublicKey,
342 hash_alg: Option<HashAlg>,
343 mut data: Vec<u8>,
344 ) -> Result<Vec<u8>, Error> {
345 debug!("sign_request: {data:?}");
346 let hash = self.prepare_sign_request(public, hash_alg, &data)?;
347
348 self.read_response().await?;
349
350 match self.buf.split_first() {
351 Some((&msg::SIGN_RESPONSE, mut r)) => {
352 self.write_signature(&mut r, hash, &mut data)?;
353 Ok(data)
354 }
355 Some((&msg::FAILURE, _)) => Err(Error::AgentFailure),
356 _ => {
357 debug!("self.buf = {:?}", &self.buf[..]);
358 Err(Error::AgentProtocolError)
359 }
360 }
361 }
362
363 async fn sign_request_cert(
370 &mut self,
371 cert: &Certificate,
372 hash_alg: Option<HashAlg>,
373 mut data: Vec<u8>,
374 ) -> Result<Vec<u8>, Error> {
375 debug!("sign_request_cert: {data:?}");
376
377 self.buf.clear();
378 self.buf.resize(4, 0);
379 msg::SIGN_REQUEST.encode(&mut self.buf)?;
380 cert.to_bytes()?.encode(&mut self.buf)?;
381 data.encode(&mut self.buf)?;
382
383 let hash = match cert.algorithm() {
385 Algorithm::Rsa { .. } => match hash_alg {
386 Some(HashAlg::Sha256) => 2,
387 Some(HashAlg::Sha512) => 4,
388 _ => 0,
389 },
390 _ => 0,
391 };
392
393 hash.encode(&mut self.buf)?;
394
395 let len = self.buf.len() - 4;
396 BigEndian::write_u32(&mut self.buf[..], len as u32);
397
398 self.read_response().await?;
399
400 match self.buf.split_first() {
401 Some((&msg::SIGN_RESPONSE, mut r)) => {
402 self.write_signature(&mut r, hash, &mut data)?;
403 Ok(data)
404 }
405 Some((&msg::FAILURE, _)) => Err(Error::AgentFailure),
406 _ => {
407 debug!("self.buf = {:?}", &self.buf[..]);
408 Err(Error::AgentProtocolError)
409 }
410 }
411 }
412
413 fn prepare_sign_request(
414 &mut self,
415 public: &ssh_key::PublicKey,
416 hash_alg: Option<HashAlg>,
417 data: &[u8],
418 ) -> Result<u32, Error> {
419 self.buf.clear();
420 self.buf.resize(4, 0);
421 msg::SIGN_REQUEST.encode(&mut self.buf)?;
422 public.key_data().encoded()?.encode(&mut self.buf)?;
423 data.encode(&mut self.buf)?;
424 debug!("public = {public:?}");
425
426 let hash = match public.algorithm() {
427 Algorithm::Rsa { .. } => match hash_alg {
428 Some(HashAlg::Sha256) => 2,
429 Some(HashAlg::Sha512) => 4,
430 _ => 0,
431 },
432 _ => 0,
433 };
434
435 hash.encode(&mut self.buf)?;
436 let len = self.buf.len() - 4;
437 BigEndian::write_u32(&mut self.buf[..], len as u32);
438 Ok(hash)
439 }
440
441 fn write_signature<R: Reader>(
442 &self,
443 r: &mut R,
444 hash: u32,
445 data: &mut Vec<u8>,
446 ) -> Result<(), Error> {
447 let mut resp = &Bytes::decode(r)?[..];
448 let t = String::decode(&mut resp)?;
449 if (hash == 2 && t == "rsa-sha2-256") || (hash == 4 && t == "rsa-sha2-512") || hash == 0 {
450 let sig = Bytes::decode(&mut resp)?;
451 let is_sk_signature = t.starts_with("sk-");
452 (t.len() + sig.len() + 8 + if is_sk_signature { 5 } else { 0 }).encode(data)?;
453 t.encode(data)?;
454 sig.encode(data)?;
455 if is_sk_signature {
456 let flags = u8::decode(&mut resp)?;
457 let counter = u32::decode(&mut resp)?;
458 flags.encode(data)?;
459 counter.encode(data)?;
460 }
461 Ok(())
462 } else {
463 error!("unexpected agent signature type: {t:?}");
464 Err(Error::AgentProtocolError)
465 }
466 }
467
468 pub fn sign_request_base64(
470 mut self,
471 public: &ssh_key::PublicKey,
472 hash_alg: Option<HashAlg>,
473 data: &[u8],
474 ) -> impl futures::Future<Output = (Self, Result<String, Error>)> {
475 debug!("sign_request: {data:?}");
476 let r = self.prepare_sign_request(public, hash_alg, data);
477 async move {
478 if let Err(e) = r {
479 return (self, Err(e));
480 }
481
482 let resp = self.read_response().await;
483 if let Err(e) = resp {
484 return (self, Err(e));
485 }
486
487 #[allow(clippy::indexing_slicing)] if !self.buf.is_empty() && self.buf[0] == msg::SIGN_RESPONSE {
489 let base64 = data_encoding::BASE64_NOPAD.encode(&self.buf[1..]);
490 (self, Ok(base64))
491 } else {
492 (self, Ok(String::new()))
493 }
494 }
495 }
496
497 pub async fn sign_request_signature(
499 &mut self,
500 public: &ssh_key::PublicKey,
501 hash_alg: Option<HashAlg>,
502 data: &[u8],
503 ) -> Result<Signature, Error> {
504 debug!("sign_request: {data:?}");
505
506 self.prepare_sign_request(public, hash_alg, data)?;
507 self.read_response().await?;
508
509 match self.buf.split_first() {
510 Some((&msg::SIGN_RESPONSE, mut r)) => {
511 let mut resp = &Bytes::decode(&mut r)?[..];
512 let sig = Signature::decode(&mut resp)?;
513 Ok(sig)
514 }
515 _ => Err(Error::AgentProtocolError),
516 }
517 }
518
519 pub async fn remove_identity(&mut self, public: &ssh_key::PublicKey) -> Result<(), Error> {
521 self.buf.clear();
522 self.buf.resize(4, 0);
523 self.buf.push(msg::REMOVE_IDENTITY);
524 public.key_data().encoded()?.encode(&mut self.buf)?;
525 let len = self.buf.len() - 4;
526 BigEndian::write_u32(&mut self.buf[..], len as u32);
527 self.read_response().await?;
528 Ok(())
529 }
530
531 pub async fn remove_smartcard_key(&mut self, id: &str, pin: &[u8]) -> Result<(), Error> {
533 self.buf.clear();
534 self.buf.resize(4, 0);
535 msg::REMOVE_SMARTCARD_KEY.encode(&mut self.buf)?;
536 id.encode(&mut self.buf)?;
537 pin.encode(&mut self.buf)?;
538 let len = self.buf.len() - 4;
539 BigEndian::write_u32(&mut self.buf[..], len as u32);
540 self.read_response().await?;
541 Ok(())
542 }
543
544 pub async fn remove_all_identities(&mut self) -> Result<(), Error> {
546 self.buf.clear();
547 self.buf.resize(4, 0);
548 msg::REMOVE_ALL_IDENTITIES.encode(&mut self.buf)?;
549 1u32.encode(&mut self.buf)?;
550 self.read_success().await?;
551 Ok(())
552 }
553
554 pub async fn extension(&mut self, typ: &[u8], ext: &[u8]) -> Result<(), Error> {
556 self.buf.clear();
557 self.buf.resize(4, 0);
558 msg::EXTENSION.encode(&mut self.buf)?;
559 typ.encode(&mut self.buf)?;
560 ext.encode(&mut self.buf)?;
561 let len = self.buf.len() - 4;
562 (len as u32).encode(&mut self.buf)?;
563 self.read_response().await?;
564 Ok(())
565 }
566
567 pub async fn query_extension(&mut self, typ: &[u8], mut ext: CryptoVec) -> Result<bool, Error> {
569 self.buf.clear();
570 self.buf.resize(4, 0);
571 msg::EXTENSION.encode(&mut self.buf)?;
572 typ.encode(&mut self.buf)?;
573 let len = self.buf.len() - 4;
574 (len as u32).encode(&mut self.buf)?;
575 self.read_response().await?;
576
577 match self.buf.split_first() {
578 Some((&msg::SUCCESS, mut r)) => {
579 ext.extend(&Bytes::decode(&mut r)?);
580 Ok(true)
581 }
582 _ => Ok(false),
583 }
584 }
585}
586
587#[cfg(test)]
588mod tests {
589 use byteorder::{BigEndian, ByteOrder};
590 use tokio::io::{AsyncReadExt, AsyncWriteExt};
591
592 use super::{AgentClient, MAX_AGENT_FRAME_LEN};
593 use crate::keys::Error;
594
595 #[test]
596 fn oversized_agent_response_is_rejected_before_allocation() -> std::io::Result<()> {
597 let runtime = tokio::runtime::Builder::new_current_thread()
598 .enable_all()
599 .build()?;
600
601 runtime.block_on(async {
602 let (mut writer, reader) = tokio::io::duplex(64);
603 let server = tokio::spawn(async move {
604 let mut frame = [0u8; 4];
605 writer.read_exact(&mut frame).await?;
606 let len = BigEndian::read_u32(&frame) as usize;
607 let mut body = vec![0; len];
608 writer.read_exact(&mut body).await?;
609
610 BigEndian::write_u32(&mut frame, (MAX_AGENT_FRAME_LEN + 1) as u32);
611 writer.write_all(&frame).await?;
612 Ok::<(), std::io::Error>(())
613 });
614
615 let mut client = AgentClient::connect(reader);
616 let err = client.request_identities().await.unwrap_err();
617 assert!(matches!(err, Error::AgentProtocolError));
618 server.await.expect("server task")?;
619 Ok::<(), std::io::Error>(())
620 })?;
621
622 Ok(())
623 }
624}