fix(linux): gtk sudo shows no false "Sorry, try again." and retries with sudo-rs (#16652)

* fix(linux): no false "Sorry, try again." in gtk sudo after a PAM info line

The gtk sudo dialog takes the password before sudo starts and drops it
when sudo prints a non-prompt line first, such as pam_fprintd's "Place
your finger on the fingerprint reader". The real password prompt then
came up labelled as a retry, "Sorry, try again.", although no password
had been sent, so users took it as a rejected password.

Treat the next prompt as a first prompt when the typed password is
dropped. The dialog comes back prefilled and without the error.

Keeping the password and sending it to the next prompt instead is not
safe: when sudo authenticates without a prompt (fingerprint, NOPASSWD),
use_pty, the default since sudo 1.9.14, holds the tty in raw mode while
the command runs, so any output line ending in ':' would receive it.

Fixes #16647

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(linux): let gtk sudo retry after sudo-rs rejects a password

sudo-rs reports a rejected password with "sudo: Authentication failed,
try again." and then asks again. The line starts with "sudo:", so
ui_parent treated it as fatal: it showed it in an error dialog and
exited instead of offering a retry, as it does after classic sudo's
"Sorry, try again." (the second log in #16647).

Exempt exactly that line from the fatal check. It then goes through
the ordinary non-prompt branch, so the held-password drop rule still
applies. sudo-rs's final "maximum N incorrect authentication attempts"
stays fatal, and so does every other sudo:/su: line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
RustDeskandClaude Opus 5.5 authored and GitHub committed 2026-10-22 18:27:35 +08:00
1 parent d97351ad73
commit 3ec041644d
1 file changed
+5 -1
+5 -1
View File
@@ -351,7 +351,10 @@ fn ui_parent(
let last_line = buf.lines().last().unwrap_or(&buf).trim().to_string();
log::info!("read from child: {}", buf);
if last_line.starts_with("sudo:") || last_line.starts_with("su:") {
// sudo-rs's notice for a rejected password, not an error: it asks again.
if (last_line.starts_with("sudo:") || last_line.starts_with("su:"))
&& last_line != "sudo: Authentication failed, try again."
{
if let Err(e) = tx_to_ui.send(Message::ErrorDialog(last_line)) {
log::error!("Channel error: {e:?}");
kill_child(child);
@@ -419,6 +422,7 @@ fn ui_parent(
if !last_line.is_empty() && initial_password.is_some() {
log::error!("received not empty line: {last_line}, clear initial password");
initial_password = None;
first = true;
}
}
}