Subject: generalize OTP support for challenge/response style tokens
From: Tor Perkins <[email-masked]>
To: [email-masked]
Date: Wed, 13 Apr 2016 14:58:19 -0700

Package: x2goclient
Version: 4.0.5.2
Tags: patch

Hello,

I really like the new support for OTP (One Time Passwords).

This patch adds support for ANSI X9.9 OTP tokens (and probably others too).
These tokens look like tiny calculators.  Here's a link to a typical example:

  http://www.safenet-inc.com/multi-factor-authentication/authenticators/one-time-password-otp/gold-challenge-response-token/

Here's a diagram of the "user experience" when using these tokens:

  http://www.safenet-inc.com/uploadedImages/images/products/data-protection/authentication-images/GOLD-challenge-response-diagram.png

With this patch, if the SSH server sends a verification prompt to the X2Go
client that contains the string "challenge", then the client will prompt the
user for the verification code (as before), but this time it will show the
user the actual content of the server's prompt.

This enables the user to see what the "challenge" is.  This is not a
requirement for the time or event based algorithms currently supported.

The X9.9 standard was very popular before the newer algorithms came along and
there are many companies that use this style.

Thanks for your consideration!  X2Go rocks!

- Tor

diff --git a/src/onmainwindow.cpp b/src/onmainwindow.cpp
index c7fffd7..18ca368 100644
--- a/src/onmainwindow.cpp
+++ b/src/onmainwindow.cpp
@@ -2825,6 +2825,8 @@ SshMasterConnection* ONMainWindow::startSshConnection ( QString host, QString po
               SLOT ( slotSshServerAuthError ( int,QString, SshMasterConnection* ) ) );
     connect ( con, SIGNAL ( needPassPhrase(SshMasterConnection*, bool)),this,
               SLOT ( slotSshServerAuthPassphrase(SshMasterConnection*, bool)) );
+    connect ( con, SIGNAL ( needChallengeResponse(SshMasterConnection*, QString)),this,
+              SLOT ( slotSshServerAuthChallengeResponse(SshMasterConnection*, QString)) );
     connect ( con, SIGNAL ( userAuthError ( QString ) ),this,SLOT ( slotSshUserAuthError ( QString ) ) );
     connect ( con, SIGNAL ( connectionError ( QString,QString ) ), this,
               SLOT ( slotSshConnectionError ( QString,QString ) ) );
@@ -2948,6 +2950,33 @@ void ONMainWindow::slotSshServerAuthPassphrase(SshMasterConnection* connection,
 }

+void ONMainWindow::slotSshServerAuthChallengeResponse(SshMasterConnection* connection, QString Challenge)
+{
+    bool ok;
+    QString message;
+
+    message=Challenge;
+
+    QString phrase=QInputDialog::getText(0,connection->getUser()+"@"+connection->getHost()+":"+QString::number(connection->getPort()),
+                                         message,QLineEdit::Password,QString::null, &ok);
+    if(!ok)
+    {
+        phrase=QString::null;
+    }
+    else
+    {
+        if(phrase==QString::null)
+            phrase="";
+    }
+    connection->setKeyPhrase(phrase);
+    if(isHidden())
+    {
+        show();
+        QTimer::singleShot(1, this, SLOT(hide()));
+    }
+}
+
+
 void ONMainWindow::slotSshServerAuthError ( int error, QString sshMessage, SshMasterConnection* connection )
 {
     if ( startHidden )
diff --git a/src/onmainwindow.h b/src/onmainwindow.h
index 809fe5f..0962ac6 100644
--- a/src/onmainwindow.h
+++ b/src/onmainwindow.h
@@ -1036,6 +1036,7 @@ private slots:
     void slotSshConnectionError ( QString message, QString lastSessionError );
     void slotSshServerAuthError ( int error, QString sshMessage, SshMasterConnection* connection );
     void slotSshServerAuthPassphrase ( SshMasterConnection* connection, bool verificationCode );
+    void slotSshServerAuthChallengeResponse( SshMasterConnection* connection, QString Challenge );
     void slotSshUserAuthError ( QString error );
     void slotSshConnectionOk();
     void slotServSshConnectionOk(QString server);
diff --git a/src/sshmasterconnection.cpp b/src/sshmasterconnection.cpp
index 8ebac10..1d330a3 100644
--- a/src/sshmasterconnection.cpp
+++ b/src/sshmasterconnection.cpp
@@ -881,15 +881,21 @@ bool SshMasterConnection::userChallengeAuth()
             }
 
             bool has_challenge_auth_code_prompt = false;
+            bool need_to_display_auth_code_prompt = false;
             const std::size_t challenge_auth_code_prompts_size = (sizeof (challenge_auth_code_prompts_)/sizeof (*challenge_auth_code_prompts_));
 
-            for (std::size_t i = 0; i < challenge_auth_code_prompts_size; ++i) {
-                x2goDebug << "Checking against known prompt #" << i << ": " << challenge_auth_code_prompts_[i] << endl;
-
-                if (pr.startsWith (challenge_auth_code_prompts_[i])) {
-                    has_challenge_auth_code_prompt = true;
-                    break;
-                }
+            if( pr.contains("challenge", Qt::CaseInsensitive) ) {
+              x2goDebug << "prompt contains 'challenge': " << pr << endl;
+              has_challenge_auth_code_prompt = true;
+              need_to_display_auth_code_prompt = true;
+            } else {
+              for (std::size_t i = 0; i < challenge_auth_code_prompts_size; ++i) {
+                  x2goDebug << "Checking against known prompt #" << i << ": " << challenge_auth_code_prompts_[i] << endl;
+                  if (pr.startsWith (challenge_auth_code_prompts_[i])) {
+                      has_challenge_auth_code_prompt = true;
+                      break;
+                  }
+              }
             }
 
             if (has_challenge_auth_code_prompt) {
@@ -901,7 +907,11 @@ bool SshMasterConnection::userChallengeAuth()
                 if(challengeAuthVerificationCode == QString::null)
                 {
                     keyPhraseReady=false;
-                    emit needPassPhrase(this, true);
+                    if (need_to_display_auth_code_prompt) {
+                      emit needChallengeResponse(this, pr);
+                    } else {
+                      emit needPassPhrase(this, true);
+                    }
                     for(;;)
                     {
                         bool ready=false;
diff --git a/src/sshmasterconnection.h b/src/sshmasterconnection.h
index 0136ac4..79da49a 100644
--- a/src/sshmasterconnection.h
+++ b/src/sshmasterconnection.h
@@ -218,6 +218,7 @@ signals:
     void connectionOk( QString host);
 
     void needPassPhrase(SshMasterConnection*, bool verificationCode);
+    void needChallengeResponse(SshMasterConnection*, QString Challenge);
 };