This is an automated email from the git hooks/post-receive script. x2go pushed a commit to branch master in repository x2goclient. commit 68bbf328132125eaad5c53b0ac82490bf818e42e Author: Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> Date: Wed May 10 15:22:11 2017 +0200 Interaction with SSH server (for example for changing expired password). Fixes: #592. --- debian/changelog | 2 + src/InteractionDialog.cpp | 132 ++++++++++++++++++++++++++++++++++++ src/InteractionDialog.h | 59 ++++++++++++++++ src/onmainwindow.cpp | 59 ++++++++++++++-- src/onmainwindow.h | 6 ++ src/onmainwindow_privat.h | 1 + src/sshmasterconnection.cpp | 160 +++++++++++++++++++++++++++++++++++++++++++- src/sshmasterconnection.h | 11 +++ src/x2goclient.cpp | 1 + x2goclient.pro | 2 + 10 files changed, 428 insertions(+), 5 deletions(-) diff --git a/debian/changelog b/debian/changelog index 57e48d8..40e262f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -155,6 +155,8 @@ x2goclient (4.1.0.1-0x2go1) UNRELEASED; urgency=medium - Disable sound button on direct RDP and XDMCP sessions. Set for direct XDMCP session autologin=true. Set for direct XDMCP session username=XDM. + - Interaction with SSH server (for example for changing + expired password). Fixes: #592. [ Robert Parts ] * New upstream version (4.1.0.1): diff --git a/src/InteractionDialog.cpp b/src/InteractionDialog.cpp new file mode 100644 index 0000000..eac7a23 --- /dev/null +++ b/src/InteractionDialog.cpp @@ -0,0 +1,132 @@ +/************************************************************************** +* Copyright (C) 2005-2017 by Oleksandr Shneyder * +* o.shneyder@phoca-gmbh.de * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see <http://www.gnu.org/licenses/>. * +***************************************************************************/ + +#include "InteractionDialog.h" +#include "x2goclientconfig.h" +#include "onmainwindow.h" +#include <QTextEdit> +#include <QVBoxLayout> +#include <QPushButton> +#include <QLabel> +#include <QLineEdit> + +InteractionDialog::InteractionDialog(QWidget* parent): SVGFrame(":/img/svg/passform.svg", + false,parent ) +{ + mw=(ONMainWindow*)parent; + mw->setWidgetStyle(this); + + if ( !mw->retMiniMode() ) + setFixedSize ( this->sizeHint().width(),this->sizeHint().height()*1.5 ); + else + setFixedSize ( 310,280 ); + + + QPalette pal=this->palette(); + pal.setBrush ( QPalette::Window, QColor ( 255,255,255,0 ) ); + pal.setColor ( QPalette::Active, QPalette::WindowText, QPalette::Mid ); + pal.setColor ( QPalette::Active, QPalette::ButtonText, QPalette::Mid ); + pal.setColor ( QPalette::Active, QPalette::Text, QPalette::Mid ); + pal.setColor ( QPalette::Inactive, QPalette::WindowText, QPalette::Mid ); + pal.setColor ( QPalette::Inactive, QPalette::ButtonText, QPalette::Mid ); + pal.setColor ( QPalette::Inactive, QPalette::Text, QPalette::Mid ); + + this->setPalette ( pal ); + + pal.setColor ( QPalette::Button, QColor ( 255,255,255,0 ) ); + pal.setColor ( QPalette::Window, QColor ( 255,255,255,255 ) ); + pal.setColor ( QPalette::Base, QColor ( 255,255,255,255 ) ); + + QFont fnt=this->font(); + if ( mw->retMiniMode() ) +#ifdef Q_WS_HILDON + fnt.setPointSize ( 10 ); +#else + fnt.setPointSize ( 9 ); +#endif + this->setFont ( fnt ); + this->hide(); + + textEdit=new QTextEdit(this); + QVBoxLayout* lay=new QVBoxLayout(this); + lay->addWidget(new QLabel(tr("Terminal output:"))); + lay->addWidget(textEdit); + + textEntry=new QLineEdit(this); + textEntry->setEchoMode(QLineEdit::NoEcho); + lay->addWidget(textEntry); + mw->setWidgetStyle(textEntry); + + cancelButton=new QPushButton(tr("Cancel"),this); + lay->addWidget(cancelButton); + mw->setWidgetStyle(textEdit); + textEdit->setReadOnly(true); + mw->setWidgetStyle(textEdit->viewport()); + mw->setWidgetStyle((QWidget*)textEdit->verticalScrollBar()); + mw->setWidgetStyle(cancelButton); + connect(textEntry,SIGNAL(returnPressed()),this,SLOT(slotTextEntered())); + connect(cancelButton, SIGNAL(clicked(bool)),this,SLOT(slotButtonPressed())); +} + +InteractionDialog::~InteractionDialog() +{ +// qDebug()<<"Iter dlg destruct\n"; +} + +void InteractionDialog::appendText(QString txt) +{ + textEntry->setEnabled(true); + textEdit->append(txt); + textEntry->setFocus(); + interrupted=false; + display=false; + cancelButton->setText(tr("Cancel")); +} + +void InteractionDialog::reset() +{ + textEdit->clear(); +} + +void InteractionDialog::slotTextEntered() +{ + QString text=textEntry->text()+"\n"; + textEntry->clear(); + emit textEntered(text); +} + +void InteractionDialog::slotButtonPressed() +{ + if(!display) + { + emit interrupt(); + interrupted=true; + } + else + { + qDebug()<<"reconnect"; + emit closeInterractionDialog(); + } +} + +void InteractionDialog::setDisplayMode() +{ + cancelButton->setText(tr("Reconnect")); + textEntry->setEnabled(false); + display=true; +} + diff --git a/src/InteractionDialog.h b/src/InteractionDialog.h new file mode 100644 index 0000000..e2ddf43 --- /dev/null +++ b/src/InteractionDialog.h @@ -0,0 +1,59 @@ +/************************************************************************** +* Copyright (C) 2005-2017 by Oleksandr Shneyder * +* o.shneyder@phoca-gmbh.de * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see <http://www.gnu.org/licenses/>. * +***************************************************************************/ + +#ifndef INTERACTIONDIALOG_H +#define INTERACTIONDIALOG_H +#include "x2goclientconfig.h" + + +#include "SVGFrame.h" + +class ONMainWindow; +class QTextEdit; +class QLineEdit; +class QPushButton; +class InteractionDialog: public SVGFrame +{ + + Q_OBJECT +public: + InteractionDialog ( QWidget* parent=0); + virtual ~InteractionDialog(); + void reset(); + void appendText(QString txt); + bool isInterrupted() { + return interrupted; + } + void setDisplayMode(); +private: + ONMainWindow* mw; + QTextEdit* textEdit; + QPushButton* cancelButton; + QLineEdit* textEntry; + bool interrupted; + bool display; +private slots: + void slotTextEntered(); + void slotButtonPressed(); +signals: + void textEntered(QString text); + void interrupt(); + void closeInterractionDialog(); +}; + +#endif + diff --git a/src/onmainwindow.cpp b/src/onmainwindow.cpp index 6107c76..d346af3 100644 --- a/src/onmainwindow.cpp +++ b/src/onmainwindow.cpp @@ -446,6 +446,9 @@ ONMainWindow::ONMainWindow ( QWidget *parent ) :QMainWindow ( parent ) initPassDlg(); initSelectSessDlg(); initStatusDlg(); + interDlg=new InteractionDialog(bgFrame); + connect(interDlg, SIGNAL(closeInterractionDialog()), this, SLOT(slotCloseInteractionDialog())); + username->addWidget ( interDlg ); #if defined(CFGPLUGIN) && defined(Q_OS_LINUX) @@ -2920,6 +2923,13 @@ SshMasterConnection* ONMainWindow::startSshConnection ( QString host, QString po connect ( con, SIGNAL ( userAuthError ( QString ) ),this,SLOT ( slotSshUserAuthError ( QString ) ) ); connect ( con, SIGNAL ( connectionError ( QString,QString ) ), this, SLOT ( slotSshConnectionError ( QString,QString ) ) ); + connect ( con, SIGNAL(startInteraction(SshMasterConnection*,QString)),this, + SLOT(slotSshInteractionStart(SshMasterConnection*,QString)) ); + connect ( con, SIGNAL(updateInteraction(SshMasterConnection*,QString)),this, + SLOT(slotSshInteractionUpdate(SshMasterConnection*,QString)) ); + connect (con, SIGNAL(finishInteraction(SshMasterConnection*)),this, SLOT(slotSshInteractionFinish(SshMasterConnection*))); + connect ( interDlg, SIGNAL(textEntered(QString)), con, SLOT(interactionTextEnter(QString))); + connect ( interDlg, SIGNAL(interrupt()), con, SLOT(interactionInterruptSlot())); con->start(); return con; } @@ -3008,6 +3018,44 @@ void ONMainWindow::slotServSshConnectionOk(QString server) con->executeCommand( "export HOSTNAME && x2golistsessions", this, SLOT (slotListAllSessions ( bool,QString,int ) )); } +void ONMainWindow::slotSshInteractionFinish(SshMasterConnection* connection) +{ + if(interDlg->isInterrupted()) + { + slotCloseInteractionDialog(); + } + else + { + interDlg->setDisplayMode(); + } +} + +void ONMainWindow::slotCloseInteractionDialog() +{ + slotSshUserAuthError("NO_ERROR"); +} + + + +void ONMainWindow::slotSshInteractionStart(SshMasterConnection* connection, QString prompt) +{ + sessionStatusDlg->hide(); + interDlg->show(); + interDlg->reset(); + interDlg->appendText(prompt); + + setEnabled(true); + interDlg->setEnabled(true); + x2goDebug<<"SSH Session prompt:"<<prompt; + +} + +void ONMainWindow::slotSshInteractionUpdate(SshMasterConnection* connection, QString output) +{ + interDlg->appendText(output); + x2goDebug<<"SSH Interaction update:"<<output; +} + void ONMainWindow::slotSshServerAuthPassphrase(SshMasterConnection* connection, bool verificationCode) { bool ok; @@ -3069,6 +3117,7 @@ void ONMainWindow::slotSshServerAuthChallengeResponse(SshMasterConnection* conne void ONMainWindow::slotSshServerAuthError ( int error, QString sshMessage, SshMasterConnection* connection ) { + interDlg->hide(); if ( startHidden ) { startHidden=false; @@ -3179,6 +3228,7 @@ void ONMainWindow::slotSshServerAuthError ( int error, QString sshMessage, SshMa void ONMainWindow::slotSshUserAuthError ( QString error ) { + interDlg->hide(); if ( sshConnection ) { sshConnection->wait(); @@ -3201,9 +3251,10 @@ void ONMainWindow::slotSshUserAuthError ( QString error ) trayQuit(); } - QMessageBox::critical (0l, tr ("Authentication failed."), - error, QMessageBox::Ok, - QMessageBox::NoButton); + if(error != "NO_ERROR") + QMessageBox::critical (0l, tr ("Authentication failed."), + error, QMessageBox::Ok, + QMessageBox::NoButton); setEnabled ( true ); passForm->setEnabled ( true ); slotShowPassForm(); @@ -3752,6 +3803,7 @@ bool ONMainWindow::startSession ( const QString& sid ) void ONMainWindow::slotListSessions ( bool result,QString output, int ) { + interDlg->hide(); x2goDebug<<output; if ( result==false ) { @@ -12318,7 +12370,6 @@ void ONMainWindow::initSelectSessDlg() } - void ONMainWindow::printSshDError_startupFailure() { if ( closeEventSent ) diff --git a/src/onmainwindow.h b/src/onmainwindow.h index d2e5399..c38e50c 100644 --- a/src/onmainwindow.h +++ b/src/onmainwindow.h @@ -88,6 +88,7 @@ class QStandardItemModel; class HttpBrokerClient; class QMenu; class QComboBox; +class InteractionDialog; class SessionExplorer; struct user @@ -577,6 +578,7 @@ public: private: + InteractionDialog* interDlg; QString m_x2goconfig; QStringList _internApplicationsNames; QStringList _transApplicationsNames; @@ -1022,7 +1024,11 @@ private slots: void slotSshConnectionError ( QString message, QString lastSessionError ); void slotSshServerAuthError ( int error, QString sshMessage, SshMasterConnection* connection ); void slotSshServerAuthPassphrase ( SshMasterConnection* connection, bool verificationCode ); + void slotSshInteractionStart ( SshMasterConnection* connection, QString prompt ); + void slotSshInteractionUpdate ( SshMasterConnection* connection, QString output ); + void slotSshInteractionFinish ( SshMasterConnection* connection); void slotSshServerAuthChallengeResponse( SshMasterConnection* connection, QString Challenge ); + void slotCloseInteractionDialog(); void slotSshUserAuthError ( QString error ); void slotSshConnectionOk(); void slotServSshConnectionOk(QString server); diff --git a/src/onmainwindow_privat.h b/src/onmainwindow_privat.h index 817ee0b..92a3c36 100644 --- a/src/onmainwindow_privat.h +++ b/src/onmainwindow_privat.h @@ -33,6 +33,7 @@ #include "printprocess.h" #include "helpdialog.h" #include "appdialog.h" +#include "InteractionDialog.h" #include <QDesktopServices> #include <QApplication> #include <QScrollBar> diff --git a/src/sshmasterconnection.cpp b/src/sshmasterconnection.cpp index f2db295..e3ef249 100644 --- a/src/sshmasterconnection.cpp +++ b/src/sshmasterconnection.cpp @@ -724,7 +724,23 @@ void SshMasterConnection::run() #ifdef DEBUG x2goDebug<<"User authentication OK."; #endif - emit connectionOk(host); + if(checkLogin()) + { + x2goDebug<<"Login Check - OK"; + emit connectionOk(host); + } + else + { + x2goDebug<<"Login Check - Failed"; +// if(!interactionInterrupt) + { + emit finishInteraction(this); + } + ssh_disconnect ( my_ssh_session ); + ssh_free ( my_ssh_session ); + quit(); + return; + } } else { @@ -1502,6 +1518,145 @@ bool SshMasterConnection::userAuthKrb() } +void SshMasterConnection::interactionTextEnter(QString text) +{ + interactionInputMutex.lock(); + interactionInputText=text; + interactionInputMutex.unlock(); +} + +void SshMasterConnection::interactionInterruptSlot() +{ + interactionInputMutex.lock(); + interactionInterrupt=true; + interactionInputMutex.unlock(); +} + +bool SshMasterConnection::checkLogin() +{ + interactionInterrupt=false; + interactionInputText=QString::null; + + + ssh_channel channel = ssh_channel_new ( my_ssh_session ); + + if (!channel) { + QString err = ssh_get_error (my_ssh_session); + QString error_msg = tr ("%1 failed.").arg ("ssh_channel_new"); + +#ifdef DEBUG + x2goDebug << error_msg.left (error_msg.size () - 1) << ": " << err << endl; +#endif + return false; + } + if ( ssh_channel_open_session ( channel ) !=SSH_OK ) + { + QString err=ssh_get_error ( my_ssh_session ); + QString errorMsg=tr ( "%1 failed." ).arg ("ssh_channel_open_session"); +#ifdef DEBUG + x2goDebug<<errorMsg.left (errorMsg.size () - 1)<<": "<<err<<endl; +#endif + return false; + } + if (ssh_channel_request_pty(channel)!=SSH_OK) + { + QString err=ssh_get_error ( my_ssh_session ); + QString errorMsg=tr ( "%1 failed." ).arg ("ssh_channel_request_pty"); +#ifdef DEBUG + x2goDebug<<errorMsg.left (errorMsg.size () - 1)<<": "<<err<<endl; +#endif + return false; + } + if(ssh_channel_change_pty_size(channel, 80, 24)!=SSH_OK) + { + QString err=ssh_get_error ( my_ssh_session ); + QString errorMsg=tr ( "%1 failed." ).arg ("ssh_channel_change_pty_size"); +#ifdef DEBUG + x2goDebug<<errorMsg.left (errorMsg.size () - 1)<<": "<<err<<endl; +#endif + return false; + } + if ( ssh_channel_request_exec ( channel, "echo \"LOGIN OK\"" ) != SSH_OK ) + { + QString err=ssh_get_error ( my_ssh_session ); + QString errorMsg=tr ( "%1 failed." ).arg ("ssh_channel_request_exec"); +#ifdef DEBUG + x2goDebug<<errorMsg.left (errorMsg.size () - 1)<<": "<<err<<endl; +#endif + } + else + { + char buffer[1024*512]; //512K buffer + bool hasInterraction=true; + bool interactionStarted=false; +// x2goDebug<<"CHECK LOGIN channel created."<<endl; + while (ssh_channel_is_open(channel) && + !ssh_channel_is_eof(channel)) + { + int nbytes = ssh_channel_read_nonblocking(channel, buffer, sizeof(buffer), 0); + if (nbytes < 0) + return false; + if (nbytes > 0) + { + QString inf=QByteArray ( buffer,nbytes ); + x2goDebug<<"LOGIN CHECK:"<<inf; + if(inf.indexOf("LOGIN OK")!=-1) + { + x2goDebug<<"don't have interaction"; + hasInterraction=false; + break; + } + else + { + if(!interactionStarted) + { + interactionStarted=true; + emit startInteraction(this, inf); + } + else + emit updateInteraction(this,inf); + } + } + bool interrupt; + interactionInputMutex.lock(); + interrupt=interactionInterrupt; + QString textToSend=interactionInputText; + interactionInputText=QString::null; + interactionInputMutex.unlock(); + if(textToSend.length()>0) + { +// x2goDebug<<"SEND Input to SERVER"; + ssh_channel_write(channel, textToSend.toLocal8Bit().constData(), textToSend.length()); + } + if(interrupt) + { + break; + } + this->usleep(30); + } + + x2goDebug<<"LOOP FINISHED"; + bool retVal=false; + if(!hasInterraction) + { + x2goDebug<<"No interaction needed, continue session"; + retVal=true; + } + else + { + sshProcErrString=tr("Reconnect session"); + x2goDebug<<"Reconnect session"; + } + ssh_channel_close(channel); + ssh_channel_send_eof(channel); + ssh_channel_free(channel); + return retVal; + + } + return false; + +} + bool SshMasterConnection::userAuth() { if (kerberos) @@ -2059,3 +2214,6 @@ void SshMasterConnection::finalize ( int item ) emit channelClosed ( proc, uuid ); } + + + diff --git a/src/sshmasterconnection.h b/src/sshmasterconnection.h index 13499e6..f95f653 100644 --- a/src/sshmasterconnection.h +++ b/src/sshmasterconnection.h @@ -122,6 +122,7 @@ private: bool userAuthAuto(); bool userAuthWithKey(); bool userChallengeAuth(); + bool checkLogin(); bool userAuth(); bool userAuthKrb(); void channelLoop(); @@ -147,6 +148,9 @@ private slots: void slotSshProxyTunnelOk(int); void slotSshProxyTunnelFailed(bool result, QString output, int); +public slots: + void interactionTextEnter(QString text); + void interactionInterruptSlot(); private: ssh_session my_ssh_session; @@ -158,6 +162,9 @@ private: QMutex disconnectFlagMutex; QMutex writeHostKeyMutex; QMutex reverseTunnelRequestMutex; + QMutex interactionInputMutex; + QString interactionInputText; + bool interactionInterrupt; bool writeHostKey; bool writeHostKeyReady; int nextPid; @@ -219,7 +226,11 @@ signals: void needPassPhrase(SshMasterConnection*, bool verificationCode); void needChallengeResponse(SshMasterConnection*, QString Challenge); + void startInteraction(SshMasterConnection*, QString prompt); + void finishInteraction(SshMasterConnection*); + void updateInteraction(SshMasterConnection*, QString output); }; #endif // SSHMASTERCONNECTION_H + diff --git a/src/x2goclient.cpp b/src/x2goclient.cpp index 9879142..0aea78f 100644 --- a/src/x2goclient.cpp +++ b/src/x2goclient.cpp @@ -92,6 +92,7 @@ int fork_helper (int argc, char **argv) { #endif /* defined (Q_OS_UNIX) */ int main (int argc, char **argv) { + return (x2goMain(argc, argv)); #ifdef Q_OS_UNIX /* Scan program arguments for --unixhelper flag. */ bool unix_helper_request = 0; diff --git a/x2goclient.pro b/x2goclient.pro index 6cc8fd5..a1174f2 100644 --- a/x2goclient.pro +++ b/x2goclient.pro @@ -47,6 +47,7 @@ HEADERS += src/configdialog.h \ src/sshmasterconnection.h \ src/sshprocess.h \ src/SVGFrame.h \ + src/InteractionDialog.h \ src/userbutton.h \ src/x2goclientconfig.h \ src/x2gologdebug.h \ @@ -101,6 +102,7 @@ SOURCES += src/sharewidget.cpp \ src/sshmasterconnection.cpp \ src/sshprocess.cpp \ src/SVGFrame.cpp \ + src/InteractionDialog.cpp \ src/userbutton.cpp \ src/x2gologdebug.cpp \ src/printprocess.cpp \ -- Alioth's /srv/git/code.x2go.org/x2goclient.git//..//_hooks_/post-receive-email on /srv/git/code.x2go.org/x2goclient.git