Make YubiKey::open() more robust (#504)

On systems with a physical card-reader, the previous implementation falsely
reports "multiple YubiKeys detected!", even if only one YubiKey is connected.
This change attempts to actually open each reader as a YubiKey, and only
reports "multiple YubiKeys" if it can actually open more than one.

Additionally, this change avoids resetting the YubiKeys in case we find more
than one.
This commit is contained in:
hko-s
2023-08-16 00:20:04 +02:00
committed by GitHub
parent 9932d05428
commit 485d49a6c8
+25 -8
View File
@@ -178,28 +178,45 @@ impl fmt::Debug for YubiKey {
impl YubiKey { impl YubiKey {
/// Open a connection to a YubiKey. /// Open a connection to a YubiKey.
/// ///
/// Returns an error if there is more than one YubiKey detected. /// Returns an error if more than one YubiKey is detected (or none at all).
///
/// NOTE: If multiple YubiKeys are connected, but we are only able to
/// open one of them (e.g. because the other one is in use, and the
/// connection doesn't allow sharing), the YubiKey that we were able to
/// open is returned.
/// ///
/// If you need to operate in environments with more than one YubiKey /// If you need to operate in environments with more than one YubiKey
/// attached to the same system, use [`YubiKey::open_by_serial`] or /// attached to the same system, use [`YubiKey::open_by_serial`] or
/// [`yubikey::reader::Context`][`Context`] to select from the available /// [`yubikey::reader::Context`][`Context`] to select from the available
/// PC/SC readers. /// PC/SC readers.
pub fn open() -> Result<Self> { pub fn open() -> Result<Self> {
let mut readers = Context::open()?; let mut yubikey: Option<Self> = None;
let mut reader_iter = readers.iter()?;
let mut readers = Context::open()?;
for reader in readers.iter()? {
if let Ok(yk_found) = reader.open() {
if let Some(yk_stored) = yubikey {
// We found two YubiKeys, so we won't use either.
// Don't reset them.
let _ = yk_stored.disconnect(pcsc::Disposition::LeaveCard);
let _ = yk_found.disconnect(pcsc::Disposition::LeaveCard);
if let Some(reader) = reader_iter.next() {
if reader_iter.next().is_some() {
error!("multiple YubiKeys detected!"); error!("multiple YubiKeys detected!");
return Err(Error::PcscError { inner: None }); return Err(Error::PcscError { inner: None });
} else {
yubikey = Some(yk_found);
}
}
} }
return reader.open(); if let Some(yubikey) = yubikey {
} // We found exactly one YubiKey that we could open, so we return it.
Ok(yubikey)
} else {
error!("no YubiKey detected!"); error!("no YubiKey detected!");
Err(Error::NotFound) Err(Error::NotFound)
} }
}
/// Open a YubiKey with a specific serial number. /// Open a YubiKey with a specific serial number.
pub fn open_by_serial(serial: Serial) -> Result<Self> { pub fn open_by_serial(serial: Serial) -> Result<Self> {