[plasma/ksshaskpass] /: Consider password incorrect when same SSH process asks the same prompt again

Kai Uwe Broulik <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit b438fa055c55bc3eb967229578263ca889c9f493 by Kai Uwe Broulik.
Committed on 19/08/2026 at 10:57.
Pushed by broulik into branch 'master'.

Consider password incorrect when same SSH process asks the same prompt again

There's no real way for ksshaskpass to know when the password was rejected,
all it gets is the "user@host's password: " (or similar) prompt.

Therefore, remember the last prompt and PID of the ssh process and when the
same is encountered again, consider the stored password incorrect and bring
up the prompt again. This avoids having a wrong password (or when it changes
and is no longer correct) lock you out unless you manually remove the password
from the keystore again.

M  +1    -0    .kde-ci.yml
M  +1    -0    CMakeLists.txt
M  +1    -0    src/CMakeLists.txt
M  +25   -1    src/main.cpp

https://invent.kde.org/plasma/ksshaskpass/-/commit/b438fa055c55bc3eb967229578263ca889c9f493

diff --git a/.kde-ci.yml b/.kde-ci.yml
index ae4e8b7..fb0ad9c 100644
--- a/.kde-ci.yml
+++ b/.kde-ci.yml
@@ -5,6 +5,7 @@ Dependencies:
  - 'on': ['@all']
    'require':
     'frameworks/extra-cmake-modules': '@latest-kf6'
+    'frameworks/kconfig': '@latest-kf6'
     'frameworks/kcoreaddons': '@latest-kf6'
     'frameworks/ki18n': '@latest-kf6'
     'third-party/qtkeychain': '@latest'
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 515e84c..779c3a0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -37,6 +37,7 @@ find_package(Qt6Keychain ${QT_KEYCHAIN_MIN_VERSION} REQUIRED)
 add_definitions(-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT)
 
 find_package(KF6 ${KF6_MIN_VERSION} REQUIRED COMPONENTS
+    Config
     CoreAddons
     I18n
     WidgetsAddons
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 22a7f85..d5160e3 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -18,6 +18,7 @@ target_link_libraries(ksshaskpass_prompt PUBLIC Qt6::Core)
 add_executable(ksshaskpass main.cpp)
 target_compile_definitions(ksshaskpass PRIVATE -DPROJECT_VERSION="${PROJECT_VERSION}")
 target_link_libraries(ksshaskpass
+    KF6::ConfigCore
     KF6::CoreAddons
     KF6::I18n
     KF6::WidgetsAddons
diff --git a/src/main.cpp b/src/main.cpp
index 088b0a2..245bf2b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -6,11 +6,14 @@
 
 #include <memory>
 #include <sys/resource.h>
+#include <unistd.h>
 
 #include <KAboutData>
+#include <KConfigGroup>
 #include <KLocalizedString>
 #include <KMessageBox>
 #include <KPasswordDialog>
+#include <KSharedConfig>
 
 #include <QApplication>
 #include <QCommandLineParser>
@@ -95,6 +98,7 @@ int main(int argc, char **argv)
     QString item;
     bool ignoreKeychain = false;
     DisplayType displayType = DisplayType::Password;
+    bool askAgain = false;
 
     // Parse commandline arguments
     if (!parser.positionalArguments().isEmpty()) {
@@ -102,6 +106,9 @@ int main(int argc, char **argv)
         parsePrompt(promptType, dialog, identifier, ignoreKeychain, displayType);
     }
 
+    auto cfg = KSharedConfig::openStateConfig();
+    KConfigGroup grp(cfg, QStringLiteral("Last Prompt"));
+
     if ((!ignoreKeychain) && (!identifier.isNull())) {
         QKeychain::ReadPasswordJob job(app.applicationName());
         job.setKey(identifier);
@@ -138,9 +145,18 @@ int main(int argc, char **argv)
                 }
             }
         }
+
+        if (!item.isEmpty() && grp.readEntry("PID", pid_t{-1}) == getppid() && grp.readEntry("Prompt", QString()) == dialog) {
+            qCInfo(LOG_KSSHASKPASS) << "SSH process" << getppid() << "asked for same prompt already, assuming stored password is incorrect";
+            askAgain = true;
+        }
     }
 
-    if (!item.isEmpty()) {
+    grp.deleteGroup();
+
+    if (!askAgain && !item.isEmpty()) {
+        grp.writeEntry("PID", getppid());
+        grp.writeEntry("Prompt", dialog);
         QTextStream(stdout) << item;
         return 0;
     }
@@ -219,6 +235,11 @@ int main(int argc, char **argv)
             ui.passwordLabel->hide();
         }
 
+        if (!item.isEmpty()) {
+            ui.lineEdit->setPassword(item);
+            ui.keepCheckBox->setChecked(true);
+        }
+
         if (dlg.exec() == QDialog::Accepted) {
             item = ui.lineEdit->password();
             remember = ui.keepCheckBox->isChecked();
@@ -232,6 +253,9 @@ int main(int argc, char **argv)
 
     if (!identifier.isEmpty() && remember.has_value()) {
         if (remember.value()) {
+            grp.writeEntry("PID", getppid());
+            grp.writeEntry("Prompt", dialog);
+
             QKeychain::WritePasswordJob job(app.applicationName());
             job.setKey(identifier);
             job.setTextData(item);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.