Disconnect without resetting YubiKeys if it is safe to do so
This enables the PIN caches to be preserved across age-plugin-yubikey processes, allowing PIN policies of "once" to become meaningful.
This commit is contained in:
+24
@@ -236,6 +236,23 @@ pub(crate) fn open(serial: Option<Serial>) -> Result<YubiKey, Error> {
|
||||
Ok(yubikey)
|
||||
}
|
||||
|
||||
/// Disconnect from the YubiKey without resetting it.
|
||||
///
|
||||
/// This can be used to preserve the YubiKey's PIN and touch caches. There are two cases
|
||||
/// where we want to do this:
|
||||
///
|
||||
/// - We connected to this YubiKey in a read-only context, so we have not made any changes
|
||||
/// to the YubiKey's state. However, we might have asked an agent to release the YubiKey
|
||||
/// in `key::open_connection`, and we want to allow any state it may have left behind
|
||||
/// (such as cached PINs or touches) to persist beyond our execution, for usability.
|
||||
/// - We opened this connection in a decryption context, so the only changes to the
|
||||
/// YubiKey's state were to potentially cache the PIN and/or touch (depending on the
|
||||
/// policies of the slot). We want to allow these to persist beyond our execution, for
|
||||
/// usability.
|
||||
pub(crate) fn disconnect_without_reset(yubikey: YubiKey) {
|
||||
let _ = yubikey.disconnect(pcsc::Disposition::LeaveCard);
|
||||
}
|
||||
|
||||
pub(crate) fn manage(yubikey: &mut YubiKey) -> Result<(), Error> {
|
||||
const DEFAULT_PIN: &str = "123456";
|
||||
const DEFAULT_PUK: &str = "12345678";
|
||||
@@ -674,6 +691,13 @@ impl Connection {
|
||||
Err(_) => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Close this connection without resetting the YubiKey.
|
||||
///
|
||||
/// This can be used to preserve the YubiKey's PIN and touch caches.
|
||||
pub(crate) fn disconnect_without_reset(self) {
|
||||
disconnect_without_reset(self.yubikey);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+18
-2
@@ -181,6 +181,13 @@ fn generate(flags: PluginFlags) -> Result<(), Error> {
|
||||
|
||||
util::print_identity(stub, recipient, metadata);
|
||||
|
||||
// We have written to the YubiKey, which means we've authenticated with the management
|
||||
// key. Out of an abundance of caution, we let the YubiKey be reset on disconnect,
|
||||
// which will clear its PIN and touch caches. This has as small negative UX effect,
|
||||
// but identity generation is a relatively infrequent occurrence, and users are more
|
||||
// likely to see their cached PINs reset due to switching applets (e.g. from PIV to
|
||||
// FIDO2).
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -200,6 +207,8 @@ fn print_single(
|
||||
|
||||
printer(stub, recipient, metadata);
|
||||
|
||||
key::disconnect_without_reset(yubikey);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -233,6 +242,8 @@ fn print_multiple(
|
||||
println!();
|
||||
}
|
||||
println!();
|
||||
|
||||
key::disconnect_without_reset(yubikey);
|
||||
}
|
||||
if printed > 1 {
|
||||
eprintln!("{}", fl!("printed-multiple", kind = kind, count = printed));
|
||||
@@ -360,11 +371,13 @@ fn main() -> Result<(), Error> {
|
||||
.iter()
|
||||
.map(|reader| {
|
||||
key::open_connection(reader).map(|yk| {
|
||||
fl!(
|
||||
let name = fl!(
|
||||
"cli-setup-yk-name",
|
||||
yubikey_name = reader.name(),
|
||||
yubikey_serial = yk.serial().to_string(),
|
||||
)
|
||||
);
|
||||
key::disconnect_without_reset(yk);
|
||||
name
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
@@ -457,8 +470,10 @@ fn main() -> Result<(), Error> {
|
||||
util::Metadata::extract(&mut yubikey, slot, key.certificate(), true)
|
||||
.unwrap();
|
||||
|
||||
key::disconnect_without_reset(yubikey);
|
||||
((stub, recipient, metadata), false)
|
||||
} else {
|
||||
key::disconnect_without_reset(yubikey);
|
||||
return Ok(());
|
||||
}
|
||||
} else {
|
||||
@@ -540,6 +555,7 @@ fn main() -> Result<(), Error> {
|
||||
true,
|
||||
)
|
||||
} else {
|
||||
key::disconnect_without_reset(yubikey);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,6 +249,8 @@ impl IdentityPluginV1 for IdentityPlugin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conn.disconnect_without_reset();
|
||||
}
|
||||
Ok(file_keys)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user