fix: wipe long-lived credential copies from memory

Session-only credentials (kept for a whole session so reconnects don't
re-prompt), credentials pending until a connection succeeds, and the
credential prompt's text buffer are now Zeroizing and wiped when
dropped, including when a prompt is cancelled or abandoned
(SEC-20261012-007, partial).

Still open: the values handed to the protocol clients are plain Strings
for the duration of authentication, egui keeps its own copies of typed
text, and no mlock/PR_SET_DUMPABLE is applied.
This commit is contained in:
Andy Dixon
2026-10-12 09:37:23 +01:00
parent c6ee9a8f36
commit 0d0b3a7c75
5 changed files with 29 additions and 13 deletions
+16 -3
View File
@@ -50,8 +50,21 @@ pub fn post_error_nonblocking(e: String) {
/// A credential or host-key question from a connecting session.
pub enum Prompt {
Secret { session: String, title: String, message: String, value: String, reply: mpsc::Sender<String> },
HostKey { host: String, key_type: String, fingerprint: String, stored: Option<String>, reply: mpsc::Sender<HostKeyDecision> },
/// `value` is wiped when the prompt is dropped (answered, cancelled or abandoned).
Secret {
session: String,
title: String,
message: String,
value: zeroize::Zeroizing<String>,
reply: mpsc::Sender<String>,
},
HostKey {
host: String,
key_type: String,
fingerprint: String,
stored: Option<String>,
reply: mpsc::Sender<HostKeyDecision>,
},
}
/// Modal dialogs owned by the main window.
@@ -507,7 +520,7 @@ impl App {
let _ = (session, title, message, reply.send(self.answers.remove(0)));
}
Event::SecretPrompt { session, title, message, reply } => {
self.prompts.push_back(Prompt::Secret { session, title, message, value: String::new(), reply });
self.prompts.push_back(Prompt::Secret { session, title, message, value: Default::default(), reply });
}
Event::HostKeyVerify { host, key_type, fingerprint, stored, reply, .. } => {
self.prompts.push_back(Prompt::HostKey { host, key_type, fingerprint, stored, reply });