1 /**************************************************************************
2 *   Copyright (C) 2005-2020 by Oleksandr Shneyder                         *
3 *                              <o.shneyder@phoca-gmbh.de>                 *
4 *                                                                         *
5 *   This program is free software; you can redistribute it and/or modify  *
6 *   it under the terms of the GNU General Public License as published by  *
7 *   the Free Software Foundation; either version 2 of the License, or     *
8 *   (at your option) any later version.                                   *
9 *   This program is distributed in the hope that it will be useful,       *
10 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12 *   GNU General Public License for more details.                          *
13 *                                                                         *
14 *   You should have received a copy of the GNU General Public License     *
15 *   along with this program.  If not, see <https://www.gnu.org/licenses/>. *
16 ***************************************************************************/
17 
18 #include "httpbrokerclient.h"
19 #include <QNetworkAccessManager>
20 #include <QUrl>
21 #include <QNetworkRequest>
22 #include <QNetworkReply>
23 #include <QUuid>
24 #include <QTextStream>
25 #include <QFile>
26 #include <QDir>
27 #include <QSslSocket>
28 #include "x2gologdebug.h"
29 #include <QMessageBox>
30 #include <QDateTime>
31 #include "onmainwindow.h"
32 #include "x2gosettings.h"
33 #include <QDesktopWidget>
34 #include <QTimer>
35 #include "SVGFrame.h"
36 #include "onmainwindow.h"
37 #include <QTemporaryFile>
38 #include <QInputDialog>
39 #include "InteractionDialog.h"
40 
41 
HttpBrokerClient(ONMainWindow * wnd,ConfigFile * cfg)42 HttpBrokerClient::HttpBrokerClient ( ONMainWindow* wnd, ConfigFile* cfg )
43 {
44     config=cfg;
45     mainWindow=wnd;
46     sshConnection=0;
47     sessionsRequest=0l;
48     selSessRequest=0l;
49     chPassRequest=0l;
50     testConRequest=0l;
51     eventRequest=0l;
52     QUrl lurl ( config->brokerurl );
53     if(lurl.userName().length()>0)
54         config->brokerUser=lurl.userName();
55     nextAuthId=config->brokerUserId;
56 
57     if(config->brokerurl.indexOf("ssh://")==0)
58     {
59         sshBroker=true;
60         x2goDebug<<"host:"<<lurl.host();
61         x2goDebug<<"port:"<<lurl.port(22);
62         x2goDebug<<"uname:"<<lurl.userName();
63         x2goDebug<<"path:"<<lurl.path();
64         config->sshBrokerBin=lurl.path();
65     }
66     else
67     {
68         sshBroker=false;
69 
70         if ((config->brokerCaCertFile.length() >0) && (QFile::exists(config->brokerCaCertFile))) {
71             QSslSocket::addDefaultCaCertificates(config->brokerCaCertFile, QSsl::Pem);
72             x2goDebug<<"Custom CA certificate file loaded into HTTPS broker client: "<<config->brokerCaCertFile;
73         }
74 
75         http=new QNetworkAccessManager ( this );
76         x2goDebug<<"Setting up connection to broker: "<<config->brokerurl;
77 
78         connect ( http, SIGNAL ( sslErrors ( QNetworkReply*, const QList<QSslError>& ) ),this,
79                   SLOT ( slotSslErrors ( QNetworkReply*, const QList<QSslError>& ) ) );
80 
81         connect ( http,SIGNAL ( finished (QNetworkReply*) ),this,
82                   SLOT ( slotRequestFinished (QNetworkReply*) ) );
83     }
84 }
85 
86 
~HttpBrokerClient()87 HttpBrokerClient::~HttpBrokerClient()
88 {
89 }
90 
createSshConnection()91 void HttpBrokerClient::createSshConnection()
92 {
93     QUrl lurl ( config->brokerurl );
94     sshConnection=new SshMasterConnection (this, lurl.host(), lurl.port(22),mainWindow->getAcceptRSA(),
95                                            config->brokerUser, config->brokerPass,config->brokerSshKey,config->brokerAutologin,
96                                            config->brokerKrbLogin, false);
97 
98     qRegisterMetaType<SshMasterConnection::passphrase_types> ("SshMasterConnection::passphrase_types");
99 
100     connect ( sshConnection, SIGNAL ( connectionOk(QString)), this, SLOT ( slotSshConnectionOk() ) );
101     connect ( sshConnection, SIGNAL ( serverAuthError ( int,QString, SshMasterConnection* ) ),this,
102               SLOT ( slotSshServerAuthError ( int,QString, SshMasterConnection* ) ) );
103     connect ( sshConnection, SIGNAL ( needPassPhrase(SshMasterConnection*, SshMasterConnection::passphrase_types)),this,
104               SLOT ( slotSshServerAuthPassphrase(SshMasterConnection*, SshMasterConnection::passphrase_types)) );
105     connect ( sshConnection, SIGNAL ( userAuthError ( QString ) ),this,SLOT ( slotSshUserAuthError ( QString ) ) );
106     connect ( sshConnection, SIGNAL ( connectionError(QString,QString)), this,
107               SLOT ( slotSshConnectionError ( QString,QString ) ) );
108     connect ( sshConnection, SIGNAL(ioErr(SshProcess*,QString,QString)), this,
109               SLOT(slotSshIoErr(SshProcess*,QString,QString)));
110 
111 
112     connect ( sshConnection, SIGNAL(startInteraction(SshMasterConnection*,QString)),mainWindow,
113               SLOT(slotSshInteractionStart(SshMasterConnection*,QString)) );
114     connect ( sshConnection, SIGNAL(updateInteraction(SshMasterConnection*,QString)),mainWindow,
115               SLOT(slotSshInteractionUpdate(SshMasterConnection*,QString)) );
116     connect ( sshConnection, SIGNAL(finishInteraction(SshMasterConnection*)),mainWindow,
117               SLOT(slotSshInteractionFinish(SshMasterConnection*)));
118     connect ( mainWindow->getInteractionDialog(), SIGNAL(textEntered(QString)), sshConnection,
119               SLOT(interactionTextEnter(QString)));
120     connect ( mainWindow->getInteractionDialog(), SIGNAL(interrupt()), sshConnection, SLOT(interactionInterruptSlot()));
121 
122     sshConnection->start();
123 }
124 
slotSshConnectionError(QString message,QString lastSessionError)125 void HttpBrokerClient::slotSshConnectionError(QString message, QString lastSessionError)
126 {
127     if ( sshConnection )
128     {
129         sshConnection->wait();
130         delete sshConnection;
131         sshConnection=0l;
132     }
133 
134     QMessageBox::critical ( 0l,message,lastSessionError,
135                             QMessageBox::Ok,
136                             QMessageBox::NoButton );
137 }
138 
slotSshConnectionOk()139 void HttpBrokerClient::slotSshConnectionOk()
140 {
141     mainWindow->getInteractionDialog()->hide();
142     getUserSessions();
143 }
144 
slotSshServerAuthError(int error,QString sshMessage,SshMasterConnection * connection)145 void HttpBrokerClient::slotSshServerAuthError(int error, QString sshMessage, SshMasterConnection* connection)
146 {
147     QString errMsg;
148     switch ( error )
149     {
150     case SSH_SERVER_KNOWN_CHANGED:
151         errMsg=tr ( "Host key for server changed.\nIt is now: " ) +sshMessage+"\n"+
152                tr ( "For security reasons, the connection attempt will be aborted." );
153         connection->writeKnownHosts(false);
154         connection->wait();
155         if(sshConnection && sshConnection !=connection)
156         {
157             sshConnection->wait();
158             delete sshConnection;
159         }
160         sshConnection=0;
161         slotSshUserAuthError ( errMsg );
162         return;
163 
164     case SSH_SERVER_FOUND_OTHER:
165         errMsg=tr ( "The host key for this server was not found but another "
166                     "type of key exists. An attacker might have changed the default server key to "
167                     "trick your client into thinking the key does not exist yet." );
168         connection->writeKnownHosts(false);
169         connection->wait();
170         if(sshConnection && sshConnection !=connection)
171         {
172             sshConnection->wait();
173             delete sshConnection;
174         }
175         sshConnection=0;
176         slotSshUserAuthError ( errMsg );
177         return ;
178 
179     case SSH_SERVER_ERROR:
180         connection->writeKnownHosts(false);
181         connection->wait();
182         if(sshConnection && sshConnection !=connection)
183         {
184             sshConnection->wait();
185             delete sshConnection;
186         }
187         sshConnection=0;
188         slotSshUserAuthError ( sshMessage );
189         return ;
190     case SSH_SERVER_FILE_NOT_FOUND:
191         errMsg=tr ( "Could not find known hosts file. "
192                     "If you accept the host key here, the file will be automatically created." );
193         break;
194 
195     case SSH_SERVER_NOT_KNOWN:
196         errMsg=tr ( "The server is unknown. Do you trust the host key?\nPublic key hash: " ) +sshMessage;
197         break;
198     }
199 
200     if ( QMessageBox::warning ( 0, tr ( "Host key verification failed." ),errMsg,tr ( "Yes" ), tr ( "No" ) ) !=0 )
201     {
202         connection->writeKnownHosts(false);
203         connection->wait();
204         if(sshConnection && sshConnection !=connection)
205         {
206             sshConnection->wait();
207             delete sshConnection;
208         }
209         sshConnection=0;
210         slotSshUserAuthError ( tr ( "Host key verification failed." ) );
211         return;
212     }
213     connection->writeKnownHosts(true);
214     connection->wait();
215     connection->start();
216 
217 }
218 
slotSshServerAuthPassphrase(SshMasterConnection * connection,SshMasterConnection::passphrase_types passphrase_type)219 void HttpBrokerClient::slotSshServerAuthPassphrase(SshMasterConnection* connection, SshMasterConnection::passphrase_types passphrase_type)
220 {
221     bool ok;
222     QString message;
223 
224     switch (passphrase_type) {
225         case SshMasterConnection::PASSPHRASE_PRIVKEY:
226                                                         message = tr ("Enter passphrase to decrypt a key");
227                                                         ok = true;
228                                                         break;
229         case SshMasterConnection::PASSPHRASE_CHALLENGE:
230                                                         message = tr ("Verification code:");
231                                                         ok = true;
232                                                         break;
233         case SshMasterConnection::PASSPHRASE_PASSWORD:
234                                                         message = tr ("Enter user account password:");
235                                                         ok = true;
236                                                         break;
237         default:
238                                                         x2goDebug << "Unknown passphrase type requested! Was: " << passphrase_type << endl;
239                                                         ok = false;
240                                                         break;
241     }
242 
243     if (ok) {
244         QString phrase = QInputDialog::getText (0, connection->getUser () + "@" + connection->getHost () + ":" + QString::number (connection->getPort ()),
245                                                 message, QLineEdit::Password, QString (""), &ok);
246         if (!ok) {
247             phrase = QString ("");
248         }
249         connection->setKeyPhrase (phrase);
250     }
251 }
252 
closeSSHInteractionDialog()253 void HttpBrokerClient::closeSSHInteractionDialog()
254 {
255     slotSshUserAuthError("NO_ERROR");
256 }
257 
258 
slotSshUserAuthError(QString error)259 void HttpBrokerClient::slotSshUserAuthError(QString error)
260 {
261     if ( sshConnection )
262     {
263         sshConnection->wait();
264         delete sshConnection;
265         sshConnection=0l;
266     }
267     mainWindow->getInteractionDialog()->hide();
268 
269     if(error!="NO_ERROR")
270 
271         QMessageBox::critical ( 0l,tr ( "Authentication failed." ),error,
272                                 QMessageBox::Ok,
273                                 QMessageBox::NoButton );
274     emit authFailed();
275     return;
276 }
277 
278 
getUserSessions()279 void HttpBrokerClient::getUserSessions()
280 {
281     QString brokerUser=config->brokerUser;
282     // Otherwise, after logout from the session, we will be connected by a previous user without a password by authid.
283     if (config->brokerAutologoff) {
284         nextAuthId=config->brokerUserId;
285     }
286     x2goDebug<<"Called getUserSessions: brokeruser: "<<brokerUser<<" authid: "<<nextAuthId;
287     if(mainWindow->getUsePGPCard())
288         brokerUser=mainWindow->getCardLogin();
289     config->sessiondata=QString::null;
290     if(!sshBroker)
291     {
292         QString req;
293         QTextStream ( &req ) <<
294                              "task=listsessions&"<<
295                              "user="<<QUrl::toPercentEncoding(brokerUser)<<"&"<<
296                              "password="<<QUrl::toPercentEncoding(config->brokerPass)<<"&"<<
297                              "authid="<<nextAuthId;
298 
299         x2goDebug << "sending request: "<< scramblePwd(req.toUtf8());
300         QNetworkRequest request(QUrl(config->brokerurl));
301         request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
302         sessionsRequest=http->post (request, req.toUtf8() );
303     }
304     else
305     {
306         if(!sshConnection)
307         {
308             createSshConnection();
309             return;
310         }
311         if (nextAuthId.length() > 0) {
312             sshConnection->executeCommand ( config->sshBrokerBin+" --user "+ brokerUser +" --authid "+nextAuthId+ " --task listsessions",
313                                             this, SLOT ( slotListSessions ( bool, QString,int ) ));
314         } else {
315             sshConnection->executeCommand ( config->sshBrokerBin+" --user "+ brokerUser +" --task listsessions",
316                                             this, SLOT ( slotListSessions ( bool, QString,int ) ));
317         }
318     }
319 }
320 
selectUserSession(const QString & session,const QString & loginName)321 void HttpBrokerClient::selectUserSession(const QString& session, const QString& loginName)
322 {
323     x2goDebug<<"Called selectUserSession for session "<<session<<", "<<"loginName "<<loginName;
324     QString brokerUser=config->brokerUser;
325     if(mainWindow->getUsePGPCard())
326         brokerUser=mainWindow->getCardLogin();
327 
328     if(!sshBroker)
329     {
330         QString req;
331         QTextStream ( &req ) <<
332                              "task=selectsession&"<<
333                              "sid="<<session<<"&"<<
334                              "user="<<QUrl::toPercentEncoding(brokerUser)<<"&"<<
335                              "password="<<QUrl::toPercentEncoding(config->brokerPass)<<"&"<<
336                              "authid="<<nextAuthId;
337         if(loginName.length()>0)
338         {
339             QTextStream ( &req ) <<"&login="<<QUrl::toPercentEncoding(loginName);
340         }
341         x2goDebug << "sending request: "<< scramblePwd(req.toUtf8());
342         QNetworkRequest request(QUrl(config->brokerurl));
343         request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
344         selSessRequest=http->post (request, req.toUtf8() );
345 
346     }
347     else
348     {
349         QString sshCmd=config->sshBrokerBin+" --user "+ brokerUser + " --task selectsession --sid \""+session+"\"";
350         if(nextAuthId.length() > 0)
351         {
352             sshCmd+=" --authid "+nextAuthId;
353         }
354         if(loginName.length() > 0)
355         {
356             sshCmd+=" --login " + loginName;
357         }
358         sshConnection->executeCommand (sshCmd, this,SLOT ( slotSelectSession(bool,QString,int)));
359     }
360 
361 }
362 
sendEvent(const QString & ev,const QString & id,const QString & server,const QString & client,const QString & login,const QString & cmd,const QString & display,const QString & start,uint connectionTime)363 void HttpBrokerClient::sendEvent(const QString& ev, const QString& id, const QString& server, const QString& client,
364                                  const QString& login, const QString& cmd,
365                                  const QString& display, const QString& start, uint connectionTime)
366 {
367     x2goDebug<<"Called sendEvent.";
368     QString brokerUser=config->brokerUser;
369     if(mainWindow->getUsePGPCard())
370         brokerUser=mainWindow->getCardLogin();
371 
372     if(!sshBroker)
373     {
374         QString req;
375         QTextStream ( &req ) <<
376                              "task=clientevent&"<<
377                              "user="<<QUrl::toPercentEncoding(brokerUser)<<"&"<<
378                              "password="<<QUrl::toPercentEncoding(config->brokerPass)<<"&"<<
379                              "sid="<<id<<"&"<<
380                              "event="<<ev<<"&"<<
381                              "server="<<QUrl::toPercentEncoding(server)<<"&"<<
382                              "client="<<QUrl::toPercentEncoding(client)<<"&"<<
383                              "login="<<QUrl::toPercentEncoding(login)<<"&"<<
384                              "cmd="<<QUrl::toPercentEncoding(cmd)<<"&"<<
385                              "display="<<QUrl::toPercentEncoding(display)<<"&"<<
386                              "start="<<QUrl::toPercentEncoding(start)<<"&"<<
387                              "elapsed="<<QString::number(connectionTime)<<"&"<<
388                              "authid="<<nextAuthId;
389         x2goDebug << "sending request: "<< scramblePwd(req.toUtf8());
390         QNetworkRequest request(QUrl(config->brokerurl));
391         request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
392         eventRequest=http->post (request, req.toUtf8() );
393 
394     }
395     else
396     {
397         if (nextAuthId.length() > 0) {
398             sshConnection->executeCommand ( config->sshBrokerBin+" --user "+ brokerUser +" --authid "+nextAuthId+
399             " --task clientevent --sid \""+id+"\" --event "+ev+" --server \""+server+"\" --client \""+client+"\" --login "+"\""+
400             login+"\" --cmd \""+cmd+"\" --display \""+display+"\" --start \""+start+"\" --elapsed "+QString::number(connectionTime),
401             this,SLOT ( slotEventSent(bool,QString,int)));
402         } else {
403             sshConnection->executeCommand ( config->sshBrokerBin+" --user "+ brokerUser +
404             " --task clientevent --sid \""+id+"\" --event "+ev+" --server \""+server+"\" --client \""+client+"\" --login "+"\""+
405             login+"\" --cmd \""+cmd+"\" --display \""+display+"\" --start \""+start+"\" --elapsed "+QString::number(connectionTime),
406             this,SLOT ( slotEventSent(bool,QString,int)));
407         }
408     }
409 }
410 
411 
slotEventSent(bool success,QString answer,int)412 void HttpBrokerClient::slotEventSent(bool success, QString answer, int)
413 {
414     if(!success)
415     {
416         x2goDebug<<answer;
417         QMessageBox::critical(0,tr("Error"),answer);
418         emit fatalHttpError();
419         return;
420     }
421     if(!checkAccess(answer))
422         return;
423     x2goDebug<<"event sent:"<<answer;
424     if(answer.indexOf("SUSPEND")!=-1)
425     {
426         QString sid=answer.split("SUSPEND ")[1].simplified();
427         x2goDebug<<"broker asks to suspend "<<sid;
428         mainWindow->suspendFromBroker(sid);
429     }
430     if(answer.indexOf("TERMINATE")!=-1)
431     {
432         QString sid=answer.split("TERMINATE ")[1].simplified();
433         x2goDebug<<"broker asks to terminate "<<sid;
434         mainWindow->terminateFromBroker(sid);
435     }
436 }
437 
438 
changePassword(QString newPass)439 void HttpBrokerClient::changePassword(QString newPass)
440 {
441     newBrokerPass=newPass;
442     QString brokerUser=config->brokerUser;
443     if(mainWindow->getUsePGPCard())
444         brokerUser=mainWindow->getCardLogin();
445 
446     if(!sshBroker)
447     {
448         QString req;
449         QTextStream ( &req ) <<
450                              "task=setpass&"<<
451                              "newpass="<<QUrl::toPercentEncoding(newPass)<<"&"<<
452                              "user="<<QUrl::toPercentEncoding(brokerUser)<<"&"<<
453                              "password="<<QUrl::toPercentEncoding(config->brokerPass)<<"&"<<
454                              "authid="<<nextAuthId;
455         x2goDebug << "sending request: "<< scramblePwd(req.toUtf8());
456         QNetworkRequest request(QUrl(config->brokerurl));
457         request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
458         chPassRequest=http->post (request, req.toUtf8() );
459     }
460     else
461     {
462         if (nextAuthId.length() > 0) {
463             sshConnection->executeCommand ( config->sshBrokerBin+" --user "+ brokerUser +" --authid "+nextAuthId+ " --task setpass --newpass "+newPass, this,
464                                             SLOT ( slotPassChanged(bool,QString,int)));
465         } else {
466             sshConnection->executeCommand ( config->sshBrokerBin+" --user "+ brokerUser +" --task setpass --newpass "+newPass, this,
467                                             SLOT ( slotPassChanged(bool,QString,int)));
468         }
469     }
470 }
471 
testConnection()472 void HttpBrokerClient::testConnection()
473 {
474     x2goDebug<<"Called testConnection.";
475     if(!sshBroker)
476     {
477         QString req;
478         QTextStream ( &req ) <<
479                              "task=testcon";
480         x2goDebug << "sending request: "<< scramblePwd(req.toUtf8());
481         QNetworkRequest request(QUrl(config->brokerurl));
482         request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
483         testConRequest=http->post (request, req.toUtf8() );
484     }
485     else
486     {
487         if (nextAuthId.length() > 0) {
488             sshConnection->executeCommand(config->sshBrokerBin+" --authid "+nextAuthId+ " --task testcon",
489                                           this, SLOT ( slotSelectSession(bool,QString,int)));
490         } else {
491             sshConnection->executeCommand(config->sshBrokerBin+" --task testcon",
492                                           this, SLOT ( slotSelectSession(bool,QString,int)));
493         }
494     }
495 }
496 
processClientConfig(const QString & raw_content)497 void HttpBrokerClient::processClientConfig(const QString& raw_content)
498 {
499     X2goSettings st(raw_content, QSettings::IniFormat);
500     mainWindow->config.brokerEvents=st.setting()->value("events",false).toBool();
501     mainWindow->config.brokerLiveEventsTimeout=st.setting()->value("liveevent",false).toUInt();
502     if(mainWindow->config.brokerEvents)
503     {
504         x2goDebug<<"sending client events to broker";
505         if(mainWindow->config.brokerLiveEventsTimeout)
506         {
507             x2goDebug<<"sending alive events to broker every "<<mainWindow->config.brokerLiveEventsTimeout<<" seconds";
508         }
509     }
510 }
511 
512 
createIniFile(const QString & raw_content)513 void HttpBrokerClient::createIniFile(const QString& raw_content)
514 {
515     QString content;
516     content = raw_content;
517     content.replace("<br>","\n");
518     x2goDebug<<"Inifile content: "<<content<<endl;
519     QString cont;
520     QStringList lines=content.split("START_USER_SESSIONS\n");
521     if (lines.count()>1)
522     {
523         cont=lines[1];
524         cont=cont.split("END_USER_SESSIONS\n")[0];
525     }
526     mainWindow->config.iniFile=cont;
527     lines=content.split("START_CLIENT_CONFIG\n");
528     if (lines.count()>1)
529     {
530         cont=lines[1];
531         cont=cont.split("END_CLIENT_CONFIG\n")[0];
532         processClientConfig(cont);
533     }
534     else
535     {
536         x2goDebug<<"no client config from broker";
537     }
538 }
539 
540 
checkAccess(QString answer)541 bool HttpBrokerClient::checkAccess(QString answer )
542 {
543     x2goDebug<<"Called checkAccess - answer was: "<<answer;
544     if (answer.indexOf("Access granted")==-1)
545     {
546         QMessageBox::critical (
547             0,tr ( "Error" ),
548             tr ( "Login failed!<br>"
549                  "Please try again." ) );
550         emit authFailed();
551         return false;
552     }
553     config->brokerAuthenticated=true;
554     emit (enableBrokerLogoutButton ());
555     int authBegin=answer.indexOf("AUTHID:");
556     if (authBegin!=-1)
557     {
558         nextAuthId=answer.mid(authBegin+7, answer.indexOf("\n",authBegin)-authBegin-7);
559     }
560     return true;
561 }
562 
563 
slotConnectionTest(bool success,QString answer,int)564 void HttpBrokerClient::slotConnectionTest(bool success, QString answer, int)
565 {
566     x2goDebug<<"Called slotConnectionTest.";
567     if(!success)
568     {
569         x2goDebug<<answer;
570         QMessageBox::critical(0,tr("Error"),answer);
571         emit fatalHttpError();
572         return;
573     }
574     if(!checkAccess(answer))
575         return;
576     if(!sshBroker)
577     {
578         x2goDebug<<"Elapsed: "<<requestTime.elapsed()<<"; received:"<<answer.size()<<endl;
579         emit connectionTime(requestTime.elapsed(),answer.size());
580     }
581     return;
582 
583 }
584 
slotListSessions(bool success,QString answer,int)585 void HttpBrokerClient::slotListSessions(bool success, QString answer, int)
586 {
587     if(!success)
588     {
589         x2goDebug<<answer;
590         QMessageBox::critical(0,tr("Error"),answer);
591         emit fatalHttpError();
592         return;
593     }
594     if(!checkAccess(answer))
595         return;
596     createIniFile(answer);
597     emit sessionsLoaded();
598 }
599 
slotPassChanged(bool success,QString answer,int)600 void HttpBrokerClient::slotPassChanged(bool success, QString answer, int)
601 {
602     if(!success)
603     {
604         x2goDebug<<answer;
605         QMessageBox::critical(0,tr("Error"),answer);
606         emit fatalHttpError();
607         return;
608     }
609     if(!checkAccess(answer))
610         return;
611 
612 }
613 
slotSelectSession(bool success,QString answer,int)614 void HttpBrokerClient::slotSelectSession(bool success, QString answer, int)
615 {
616     if(!success)
617     {
618         x2goDebug<<answer;
619         QMessageBox::critical(0,tr("Error"),answer);
620         emit fatalHttpError();
621         return;
622     }
623     if(!checkAccess(answer))
624         return;
625     x2goDebug<<"parsing "<<answer;
626     parseSession(answer);
627 }
628 
629 
slotRequestFinished(QNetworkReply * reply)630 void HttpBrokerClient::slotRequestFinished ( QNetworkReply*  reply )
631 {
632     if(reply->error() != QNetworkReply::NoError)
633     {
634         x2goDebug<<"Broker HTTP request failed with error: "<<reply->errorString();
635         if(reply == eventRequest)
636         {
637             reply->deleteLater();
638             eventRequest=0l;
639             //do not exit, just return the function
640             return;
641         }
642         QMessageBox::critical(0,tr("Error"),reply->errorString());
643         emit fatalHttpError();
644         return;
645     }
646 
647     QString answer ( reply->readAll() );
648     x2goDebug<<"A http request returned. Result was: "<<answer;
649     if (reply == testConRequest)
650     {
651         testConRequest=0l;
652         slotConnectionTest(true,answer,0);
653     }
654     else if (reply == sessionsRequest)
655     {
656         sessionsRequest=0l;
657         slotListSessions(true, answer,0);
658     }
659     else if (reply == selSessRequest)
660     {
661         selSessRequest=0l;
662         slotSelectSession(true,answer,0);
663     }
664     else if (reply == chPassRequest)
665     {
666         chPassRequest=0l;
667         slotPassChanged(true,answer,0);
668     }
669     else if (reply == eventRequest)
670     {
671         eventRequest=0l;
672         slotEventSent(true,answer,0);
673     }
674 
675     // We receive ownership of the reply object
676     // and therefore need to handle deletion.
677     reply->deleteLater();
678 }
679 
parseSession(QString sinfo)680 void HttpBrokerClient::parseSession(QString sinfo)
681 {
682     config->sessiondata="";
683     suspendedSession.clear();
684     x2goDebug<<"Starting parser.";
685     QStringList lst=sinfo.split("SERVER:",QString::SkipEmptyParts);
686     int keyStartPos=sinfo.indexOf("-----BEGIN DSA PRIVATE KEY-----");
687     if(keyStartPos==-1)
688         keyStartPos=sinfo.indexOf("-----BEGIN RSA PRIVATE KEY-----");
689     QString endStr="-----END DSA PRIVATE KEY-----";
690     int keyEndPos=sinfo.indexOf(endStr);
691     if(keyEndPos==-1)
692     {
693         endStr="-----END RSA PRIVATE KEY-----";
694         keyEndPos=sinfo.indexOf(endStr);
695     }
696     if (! (keyEndPos == -1 || keyStartPos == -1 || lst.size()==0))
697         config->key=sinfo.mid(keyStartPos, keyEndPos+endStr.length()-keyStartPos);
698     QString serverLine=(lst[1].split("\n"))[0];
699     QStringList words=serverLine.split(":",QString::SkipEmptyParts);
700     config->serverIp=words[0];
701     if (words.count()>1)
702         config->sshport=words[1];
703     x2goDebug<<"Server IP address: "<<config->serverIp;
704     x2goDebug<<"Server port: "<<config->sshport;
705     if (sinfo.indexOf("SESSION_INFO")!=-1)
706     {
707         QStringList lst=sinfo.split("SESSION_INFO:",QString::SkipEmptyParts);
708         //config->sessiondata=lst[1];
709         x2goDebug<<"Session data: "<<lst[1]<<"\n";
710         suspendedSession=lst[1].trimmed().split ( '\n', QString::SkipEmptyParts );
711         mainWindow->selectSession(suspendedSession);
712     }
713     else
714     {
715         emit sessionSelected();
716     }
717     x2goDebug<<"Parsing has finished.";
718 }
719 
resumeSession(const QString & id,const QString & server)720 void HttpBrokerClient::resumeSession(const QString& id, const QString& server)
721 {
722     x2goDebug<<"Resuming session with id:"<<id<<"on:"<<server;
723     foreach (QString sline, suspendedSession)
724     {
725         if(sline.indexOf(id)!=-1)
726         {
727             config->sessiondata=sline;
728             config->serverIp=server;
729             emit sessionSelected();
730             break;
731         }
732     }
733 }
734 
735 
slotSslErrors(QNetworkReply * netReply,const QList<QSslError> & errors)736 void HttpBrokerClient::slotSslErrors ( QNetworkReply* netReply, const QList<QSslError> & errors )
737 {
738     QStringList err;
739     QSslCertificate cert;
740     for ( int i=0; i<errors.count(); ++i )
741     {
742         x2goDebug<<"sslError, code:"<<errors[i].error() <<":";
743         err<<errors[i].errorString();
744         if ( !errors[i].certificate().isNull() )
745             cert=errors[i].certificate();
746     }
747 
748 
749     QString md5=getHexVal ( cert.digest() );
750     QString fname=md5;
751     fname=fname.replace(":","_");
752     QUrl lurl ( config->brokerurl );
753     QString homeDir=mainWindow->getHomeDirectory();
754     if ( QFile::exists ( homeDir+"/.x2go/ssl/exceptions/"+
755                          lurl.host() +"/"+fname ) )
756     {
757         QFile fl ( homeDir+"/.x2go/ssl/exceptions/"+
758                    lurl.host() +"/"+fname );
759         fl.open ( QIODevice::ReadOnly | QIODevice::Text );
760         QSslCertificate mcert ( &fl );
761         if ( mcert==cert )
762         {
763             netReply->ignoreSslErrors();
764             requestTime.restart();
765             return;
766         }
767     }
768 
769     QString text=tr ( "<br><b>Server uses an invalid "
770                       "security certificate.</b><br><br>" );
771     text+=err.join ( "<br>" );
772     text+=tr ( "<p style='background:#FFFFDC;'>"
773                "You should not add an exception "
774                "if you are using an internet connection "
775                "that you do not trust completely or if you are "
776                "not used to seeing a warning for this server.</p>" );
777     QMessageBox mb ( QMessageBox::Warning,tr ( "Secure connection failed." ),
778                      text );
779     text=QString::null;
780     QTextStream ( &text ) <<err.join ( "\n" ) <<"\n"<<
781                           "------------\n"<<
782                           tr ( "Issued to:\n" ) <<
783                           tr ( "Common Name(CN)\t" ) <<
784 #if QT_VERSION >= 0x050000
785                           cert.issuerInfo ( QSslCertificate::CommonName ).join("; ")
786 #else
787                           cert.issuerInfo ( QSslCertificate::CommonName )
788 #endif
789                           <<endl<<
790                           tr ( "Organization(O)\t" ) <<
791 #if QT_VERSION >= 0x050000
792                           cert.issuerInfo ( QSslCertificate::Organization ).join("; ")
793 #else
794                           cert.issuerInfo ( QSslCertificate::Organization )
795 #endif
796                           <<endl<<
797                           tr ( "Organizational Unit(OU)\t" ) <<
798 #if QT_VERSION >= 0x050000
799                           cert.issuerInfo ( QSslCertificate::OrganizationalUnitName ).join("; ")
800 #else
801                           cert.issuerInfo ( QSslCertificate::OrganizationalUnitName )
802 #endif
803                           <<endl<<
804                           tr ( "Serial Number\t" ) <<getHexVal ( cert.serialNumber() )
805                           <<endl<<endl<<
806                           tr ( "Issued by:\n" ) <<
807                           tr ( "Common Name(CN)\t" ) <<
808 #if QT_VERSION >= 0x050000
809                           cert.subjectInfo ( QSslCertificate::CommonName ).join("; ")
810 #else
811                           cert.subjectInfo ( QSslCertificate::CommonName )
812 #endif
813                           <<endl<<
814                           tr ( "Organization(O)\t" ) <<
815 #if QT_VERSION >= 0x050000
816                           cert.subjectInfo ( QSslCertificate::Organization ).join("; ")
817 #else
818                           cert.subjectInfo ( QSslCertificate::Organization )
819 #endif
820                           <<endl<<
821                           tr ( "Organizational Unit(OU)\t" ) <<
822 #if QT_VERSION >= 0x050000
823                           cert.subjectInfo ( QSslCertificate::OrganizationalUnitName ).join("; ")
824 #else
825                           cert.subjectInfo ( QSslCertificate::OrganizationalUnitName )
826 #endif
827                           <<endl<<endl<<
828 
829                           tr ( "Validity:\n" ) <<
830                           tr ( "Issued on\t" ) <<cert.effectiveDate().toString() <<endl<<
831                           tr ( "expires on\t" ) <<cert.expiryDate().toString() <<endl<<endl<<
832                           tr ( "Fingerprints:\n" ) <<
833                           tr ( "SHA1\t" ) <<
834                           getHexVal ( cert.digest ( QCryptographicHash::Sha1 ) ) <<endl<<
835                           tr ( "MD5\t" ) <<md5;
836 
837 
838 
839     mb.setDetailedText ( text );
840     mb.setEscapeButton (
841         ( QAbstractButton* ) mb.addButton ( tr ( "Exit X2Go Client" ),
842                                             QMessageBox::RejectRole ) );
843     QPushButton *okButton=mb.addButton ( tr ( "Add exception" ),
844                                          QMessageBox::AcceptRole );
845     mb.setDefaultButton ( okButton );
846 
847     mb.exec();
848     if ( mb.clickedButton() == ( QAbstractButton* ) okButton )
849     {
850         x2goDebug<<"User accepted certificate.";
851         QDir dr;
852         dr.mkpath ( homeDir+"/.x2go/ssl/exceptions/"+lurl.host() +"/" );
853         QFile fl ( homeDir+"/.x2go/ssl/exceptions/"+
854                    lurl.host() +"/"+fname );
855         fl.open ( QIODevice::WriteOnly | QIODevice::Text );
856         QTextStream ( &fl ) <<cert.toPem();
857         fl.close();
858         netReply->ignoreSslErrors();
859         x2goDebug<<"Storing certificate in "<<homeDir+"/.x2go/ssl/exceptions/"+
860                  lurl.host() +"/"+fname;
861         requestTime.restart();
862     }
863     else
864         emit fatalHttpError();
865 }
866 
867 
getHexVal(const QByteArray & ba)868 QString HttpBrokerClient::getHexVal ( const QByteArray& ba )
869 {
870     QStringList val;
871     for ( int i=0; i<ba.size(); ++i )
872     {
873         QString bt;
874         bt.sprintf ( "%02X", ( unsigned char ) ba[i] );
875         val<<bt;
876     }
877     return val.join ( ":" );
878 }
879 
slotSshIoErr(SshProcess * caller,QString error,QString lastSessionError)880 void HttpBrokerClient::slotSshIoErr(SshProcess* caller, QString error, QString lastSessionError)
881 {
882     x2goDebug<<"Brocker SSH Connection IO Error, reconnect session\n";
883     if ( sshConnection )
884     {
885         delete sshConnection;
886         sshConnection=0l;
887     }
888     createSshConnection();
889 }
890 
scramblePwd(const QString & req)891 QString HttpBrokerClient::scramblePwd(const QString& req)
892 {
893     QString scrambled=req;
894     int startPos=scrambled.indexOf("password=");
895     if(startPos!=-1)
896     {
897         startPos+=9;
898         int endPos=scrambled.indexOf("&",startPos);
899         int plength;
900         if(endPos==-1)
901         {
902             plength=scrambled.length()-startPos;
903         }
904         else
905         {
906             plength=endPos-startPos;
907         }
908         scrambled.remove(startPos, plength);
909         // Hardcode a value of 8 here - the length of the string "password".
910         scrambled.insert(startPos, QString ('*').repeated (8));
911     }
912     return scrambled;
913 }
914