From 1ecef476fac3461e041e3085a09f16a97664abfe Mon Sep 17 00:00:00 2001 From: Jason Cooper Date: Thu, 4 Apr 2024 19:39:32 +0000 Subject: [PATCH] auth: Fix multiple 'sk' keys configured, fails on first When a user has configured two or more 'sk' keys, eg Yubikeys, pam-ssh-agent will fail if the first configured key isn't currently plugged in. Standard SSH utilities gracefully try the next key in this situation, so we should too. This is very helpful for users attempting to dogfood their backup key. ;-) We correct this failure by catching the error, and trying the next key IFF the failed key was of the 'sk' type. Signed-off-by: Jason Cooper --- src/auth.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/auth.rs b/src/auth.rs index cd7d4b2..45a25df 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -3,6 +3,7 @@ use crate::log::Log; use anyhow::{Context, Result}; use getrandom::getrandom; use signature::Verifier; +use ssh_agent_client_rs::Error as SACError; use ssh_key::public::KeyData; use ssh_key::{AuthorizedKeys, PublicKey}; use std::collections::HashSet; @@ -28,13 +29,30 @@ pub fn authenticate( "found a matching key: {}", key.fingerprint(Default::default()) ))?; - return sign_and_verify(key, agent); + // Allow sign_and_verify() to return RemoteFailure for hardware keys, + // as the hardware might not be plugged in, eg when the user + // has configured multiple hardware keys + match sign_and_verify(&key, &mut agent) { + Ok(res) => return Ok(res), + Err(e) => { + if let Some(SACError::RemoteFailure) = e.downcast_ref::() { + let data = key.key_data(); + if data.is_sk_ed25519() || data.is_sk_ecdsa_p256() { + log.info(format!( + "SSHAgent: RemoteFailure, keytype is 'sk'; trying next key" + ))?; + continue; + } + } + return Err(e); + } + } } } Ok(false) } -fn sign_and_verify(key: PublicKey, mut agent: impl SSHAgent) -> Result { +fn sign_and_verify(key: &PublicKey, agent: &mut impl SSHAgent) -> Result { let mut data: [u8; CHALLENGE_SIZE] = [0_u8; CHALLENGE_SIZE]; getrandom(data.as_mut_slice())?; let sig = agent.sign(&key, data.as_ref())?;