From 3597d963324137c33aba7454704312e72a8a3918 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sun, 1 Jan 2023 13:10:58 +0000 Subject: [PATCH] Correctly hunt agents in plugin mode --- CHANGELOG.md | 4 ++++ src/key.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec1d535..05461ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ to 0.3.0 are beta releases. - The "sharing violation" logic now also sends SIGHUP to any `yubikey-agent` that is running, to have them release any YubiKey locks they are holding. +### Fixed +- The "sharing violation" logic now runs during plugin mode as intended. In the + previous release it only ran during direct `age-plugin-yubikey` usage. + ## [0.3.1] - 2022-12-30 ### Changed - If a "sharing violation" error is encountered while opening a connection to a diff --git a/src/key.rs b/src/key.rs index b5b371f..e036a36 100644 --- a/src/key.rs +++ b/src/key.rs @@ -8,7 +8,7 @@ use age_core::{ use age_plugin::{identity, Callbacks}; use bech32::{ToBase32, Variant}; use dialoguer::Password; -use log::{debug, warn}; +use log::{debug, error, warn}; use std::fmt; use std::io; use std::iter; @@ -156,7 +156,49 @@ pub(crate) fn open_connection(reader: &Reader) -> Result Result { - open_sesame(|| YubiKey::open_by_serial(serial)) + // `YubiKey::open_by_serial` has a bug where it ignores all opening errors, even if + // it potentially could have found a matching YubiKey if not for an error, and thus + // returns `Error::NotFound` if another agent is holding exclusive access to the + // required YubiKey. This gives misleading UX behaviour where age-plugin-yubikey asks + // the user to insert a YubiKey they have already inserted. + // + // For now, we instead implement the correct behaviour manually. Once MSRV has been + // raised to 1.60, we can upstream this into the `yubikey` crate. + open_sesame(|| { + let mut readers = Context::open()?; + + let mut open_error = None; + + for reader in readers.iter()? { + let yubikey = match reader.open() { + Ok(yk) => yk, + Err(e) => { + // Save the first error we see that indicates we might have been able + // to find a matching YubiKey. + if open_error.is_none() { + if let yubikey::Error::PcscError { + inner: Some(pcsc::Error::SharingViolation), + } = e + { + open_error = Some(e); + } + } + continue; + } + }; + + if serial == yubikey.serial() { + return Ok(yubikey); + } + } + + Err(if let Some(e) = open_error { + e + } else { + error!("no YubiKey detected with serial: {}", serial); + yubikey::Error::NotFound + }) + }) } pub(crate) fn open(serial: Option) -> Result {