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 "x2goclientconfig.h"
19 #include "x2gologdebug.h"
20 #include "sshmasterconnection.h"
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <stddef.h>
24 #include "sshprocess.h"
25 
26 
27 #include <QStringList>
28 #include <QFile>
29 #include <QDir>
30 #include <QTemporaryFile>
31 #ifndef Q_OS_WIN
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 #endif
37 #include <math.h>
38 
39 #include <QUuid>
40 
41 #ifndef Q_OS_WIN
42 #include <sys/socket.h> /* for socket(), connect(), send(), and recv() */
43 #include <arpa/inet.h>  /* for sockaddr_in and inet_addr() */
44 #include <arpa/inet.h>
45 #include <netinet/tcp.h>
46 #include <QtNetwork/qabstractsocket.h>
47 #endif
48 
49 
50 #include "onmainwindow.h"
51 
52 
53 #define PROXYTUNNELPORT 44444
54 
55 const QString SshMasterConnection::challenge_auth_code_prompts_[] = {
56   "Verification code:",            // GA      (http://github.com/google/google-authenticator)
57   "One-time password (OATH) for",  // OATH    (http://www.nongnu.org/oath-toolkit/pam_oath.html)
58   "passcode:",                     // MOTP    (http://motp.sourceforge.net)
59   "Enter PASSCODE:",               // SecurID
60   "YubiKey for"                    // YubiKey (https://en.wikipedia.org/wiki/YubiKey)
61 };
62 
63 
64 #ifdef Q_OS_WIN
65 #include <QSettings>
66 // parse known_hosts file from libssh and export keys in registry to use with plink.exe
parseKnownHosts()67 void SshMasterConnection::parseKnownHosts()
68 {
69     QFile fl(mainWnd->getHomeDirectory()+"/ssh/known_hosts");
70     if (!fl.open(QFile::ReadOnly))
71         return;
72     QSettings settings("HKEY_CURRENT_USER\\Software\\SimonTatham\\PuTTY\\SshHostKeys",
73                        QSettings::NativeFormat);
74     while (!fl.atEnd())
75     {
76         QString line=fl.readLine();
77         QStringList parts=line.split(' ',QString::SkipEmptyParts);
78         if (parts.count()!=3)
79             continue;
80 
81         //lines in known_hosts have format:
82         //[host]:port <ssh-rsa|ssh-dss> <key>
83         //we proceeding only lines from libssh - no hashed hostnames
84         //or patterns are allowed
85 
86         QString type="unknown";
87         QString port="22";
88         if (parts[1]=="ssh-dss")
89             type="dss";
90         if (parts[1]=="ssh-rsa")
91             type="rsa2";
92 
93 
94         QStringList hostParts=parts[0].split(":",QString::SkipEmptyParts);
95         if (hostParts.count()>1)
96             port=hostParts[1];
97         hostParts[0].replace("[","");
98         hostParts[0].replace("]","");
99 
100         QString keyName=type+"@"+port+":"+hostParts[0];
101 
102         QByteArray bytes=QByteArray::fromBase64(parts[2].toLatin1());
103         QStringList fields;
104 
105         //key is a set of data fields:
106         //[size][data][size][data].....[size][data]
107 
108         for (int i=0; i<bytes.count();)
109         {
110             int size=0;
111             //first 4 bytes are for size of data field (big-endian)
112             for (int j=0; j<4; ++j)
113             {
114                 size+=((uchar)(bytes[i])) * pow((float)256,3-j);
115                 i++;
116             }
117             QByteArray data;
118             data=bytes.mid(i,size);
119             QString hex;
120 
121             for (int j=0; j<data.count(); ++j)
122             {
123                 QString byte;
124                 byte.sprintf("%02x",(uchar)(data[j]));
125                 hex+=byte;
126             }
127             //remove leading '0'
128             for (;;)
129             {
130                 if (hex.length()==0)
131                     break;
132                 if (hex[0]=='0')
133                     hex.remove(0,1);
134                 else
135                     break;
136             }
137             hex="0x"+hex;
138             fields<<hex;
139             i+=size;
140         }
141         //first element is a type of key, we don't need it
142         fields.removeFirst();
143         settings.setValue(keyName,fields.join(","));
144         x2goDebug<<"Writing key to registry: HKEY_CURRENT_USER\\Software\\SimonTatham\\PuTTY\\SshHostKeys"<<endl;
145         x2goDebug<<keyName<<"="<<fields.join(",")<<endl;
146     }
147     settings.sync();
148 }
149 #endif
150 
151 
SshMasterConnection(QObject * parent,QString host,int port,bool acceptUnknownServers,QString user,QString pass,QString key,bool autologin,bool krblogin,bool useproxy,ProxyType type,QString proxyserver,quint16 proxyport,QString proxylogin,QString proxypassword,QString proxykey,bool proxyautologin,bool proxyKrbLogin)152 SshMasterConnection::SshMasterConnection (QObject* parent, QString host, int port, bool acceptUnknownServers, QString user,
153         QString pass, QString key, bool autologin, bool krblogin,
154         bool useproxy, ProxyType type, QString proxyserver, quint16 proxyport,
155         QString proxylogin, QString proxypassword, QString proxykey,
156         bool proxyautologin, bool proxyKrbLogin ) : QThread ( parent )
157 {
158 #if defined ( Q_OS_DARWIN )
159     // Mac OS X provides only 512KB stack space for secondary threads.
160     // As we put a 512KB buffer on the stack later on, we need a bigger stack space.
161     setStackSize (sizeof (char) * 1024 * 1024 * 2);
162 #endif
163     tcpProxySocket = NULL;
164     tcpNetworkProxy = NULL;
165     sshProxy= NULL;
166     sshProxyReady=false;
167     nextPid=0;
168 
169     breakLoop=false;
170     kerberosDelegation=false;
171     x2goDebug << "SshMasterConnection, host " << host << "; port " << port << "; user " << user
172               << "; useproxy " << useproxy << "; proxyserver " << proxyserver
173               << "; proxyport " << proxyport;
174     this->host=host;
175     // If the hostname starts with "!" do not perform loginCheck() for this connection
176     if (this->host.indexOf("!") == 0)
177     {
178         this->loginCheck=false;
179         this->host.remove(0, 1);
180     }
181     else
182     {
183         this->loginCheck=true;
184     }
185     this->port=port;
186     this->user=user;
187     this->pass=pass;
188     this->key=key;
189     this->autologin=autologin;
190     this->acceptUnknownServers=acceptUnknownServers;
191     this->useproxy=useproxy;
192     this->proxytype=type;
193     this->proxyautologin=proxyautologin;
194     this->proxykey=proxykey;
195     this->proxyserver=proxyserver;
196     this->proxyport=proxyport;
197     this->proxylogin=proxylogin;
198     this->proxypassword=proxypassword;
199     this->proxyKrbLogin=proxyKrbLogin;
200     mainWnd=(ONMainWindow*) parent;
201     kerberos=krblogin;
202     challengeAuthVerificationCode=QString::null;
203 
204 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
205     if (this->user.isEmpty ()) {
206         /* We might have a config file request pending, honor this. */
207         ssh_session tmp_session = ssh_new ();
208 
209         if (!tmp_session) {
210             QString error_msg = tr ("Cannot create SSH session.");
211             error_msg += " " + tr ("Using environment-provided username.");
212             x2goDebug << error_msg;
213         }
214         else {
215             QByteArray tmp_BA;
216 
217             tmp_BA = this->host.toLocal8Bit ();
218 
219             ssh_options_set (tmp_session, SSH_OPTIONS_HOST, tmp_BA.data ());
220 
221             if (ssh_options_parse_config (tmp_session, NULL) < 0) {
222                 x2goDebug << "Warning: unable to parse the SSH config file.";
223             }
224 
225             char *inferred_username = NULL;
226             ssh_options_get (tmp_session, SSH_OPTIONS_USER, &inferred_username);
227             x2goDebug << "Temporary session user name after config file parse: " << inferred_username;
228 
229             this->user = QString::fromLocal8Bit (inferred_username);
230 
231             ssh_string_free_char (inferred_username);
232             ssh_free (tmp_session);
233         }
234     }
235 #endif
236 
237     if (this->user.isEmpty ()) {
238 #ifdef Q_OS_WIN
239         this->user = getenv ("USERNAME");
240 #else
241         this->user = getenv ("USER");
242 #endif
243     }
244 
245     if (kerberos)
246     {
247         x2goDebug<<"Starting SSH connection with Kerberos authentication.";
248     }
249     else
250     {
251         x2goDebug<<"Starting SSH connection without Kerberos authentication.";
252     }
253     x2goDebug<<"SshMasterConnection, instance "<<this<<" created.";
254 }
255 
256 
slotSshProxyConnectionOk()257 void SshMasterConnection::slotSshProxyConnectionOk()
258 {
259     x2goDebug<<"SSH proxy connected.";
260 
261 
262     localProxyPort=PROXYTUNNELPORT;
263     while ( ONMainWindow::isServerRunning ( localProxyPort ) )
264         ++localProxyPort;
265 
266     sshProxy->startTunnel ( host, port, "127.0.0.1",localProxyPort,false,this, SLOT ( slotSshProxyTunnelOk(int)),
267                             SLOT ( slotSshProxyTunnelFailed(bool,QString,int)));
268 
269 }
270 
271 
copyFile(const QString & src,const QString dst,QObject * receiver,const char * slotFinished)272 int SshMasterConnection::copyFile(const QString& src, const QString dst, QObject* receiver, const char* slotFinished)
273 {
274     SshProcess* proc=new SshProcess(this, nextPid++);
275     if(receiver && slotFinished)
276     {
277         connect(proc, SIGNAL(sshFinished(bool,QString,int)), receiver, slotFinished);
278     }
279     proc->start_cp(src,dst);
280     processes<<proc;
281     return proc->pid;
282 }
283 
executeCommand(const QString & command,QObject * receiver,const char * slotFinished,bool overridePath)284 int SshMasterConnection::executeCommand(const QString& command, QObject* receiver, const char* slotFinished, bool overridePath)
285 {
286     SshProcess* proc=new SshProcess(this, nextPid++);
287     if(receiver && slotFinished)
288     {
289         connect(proc, SIGNAL(sshFinished(bool,QString,int)), receiver, slotFinished);
290     }
291     proc->startNormal(command, overridePath);
292     processes<<proc;
293     return proc->pid;
294 
295 }
296 
getSourceFile(int pid)297 QString SshMasterConnection::getSourceFile(int pid)
298 {
299     foreach (SshProcess* proc, processes)
300     {
301         if(proc->pid==pid)
302             return proc->getSource();
303     }
304     return QString ::null;
305 }
306 
307 
addReverseTunnelConnections()308 void SshMasterConnection::addReverseTunnelConnections()
309 {
310     reverseTunnelRequestMutex.lock();
311     for(int i=0; i<reverseTunnelRequest.count(); ++i)
312     {
313         if(!reverseTunnelRequest[i].listen)
314         {
315             reverseTunnelRequest[i].listen=true;
316 
317             int rc = SSH_AGAIN;
318 
319 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 7, 0)
320             /* Non-blocking mode may return SSH_AGAIN, so try again if neceassary. */
321             while (SSH_AGAIN == rc) {
322                 rc = ssh_channel_listen_forward(my_ssh_session, NULL, reverseTunnelRequest[i].forwardPort, NULL);
323 #else
324             {
325                 rc = ssh_forward_listen(my_ssh_session, NULL, reverseTunnelRequest[i].forwardPort, NULL);
326 #endif
327 
328                 if(rc==SSH_OK)
329                 {
330                     emit reverseTunnelOk(reverseTunnelRequest[i].creator);
331                     x2goDebug<<"Listening for TCP/IP connections on "<<reverseTunnelRequest[i].forwardPort;
332                 }
333                 if(rc==SSH_ERROR)
334                 {
335                     QString err=ssh_get_error(my_ssh_session);
336                     x2goDebug<<"Forward port "<<reverseTunnelRequest[i].forwardPort<<" failed:"<<err;
337                     emit reverseTunnelFailed(reverseTunnelRequest[i].creator, err);
338                 }
339             }
340         }
341     }
342     reverseTunnelRequestMutex.unlock();
343 }
344 
345 void SshMasterConnection::checkReverseTunnelConnections()
346 {
347     int port;
348     ssh_channel chan=ssh_channel_accept_forward(my_ssh_session, 0, &port);
349     if(chan)
350     {
351         x2goDebug<<"New reverse connection on port "<<port;
352 
353         reverseTunnelRequestMutex.lock();
354         for(int i=0; i<reverseTunnelRequest.count(); ++i)
355         {
356             ReverseTunnelRequest req=reverseTunnelRequest[i];
357             if (static_cast<int> (req.forwardPort) == port)
358             {
359                 x2goDebug<<"Creating new channel for reverse tunnel "<<port;
360                 int sock=socket ( AF_INET, SOCK_STREAM,0 );
361 #ifndef Q_OS_WIN
362                 const int y=1;
363 #else
364                 const char y=1;
365 #endif
366                 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,&y, sizeof(int));
367 
368                 struct sockaddr_in address;
369                 address.sin_family=AF_INET;
370                 address.sin_port=htons ( req.localPort );
371                 x2goDebug<<"Connecting to "<<req.localHost<<":"<<req.localPort<<endl;
372 #ifndef Q_OS_WIN
373                 inet_aton ( req.localHost.toLatin1(), &address.sin_addr );
374 #else
375                 address.sin_addr.s_addr=inet_addr (
376                                             req.localHost.toLatin1() );
377 #endif
378 
379                 if ( ::connect ( sock, ( struct sockaddr * ) &address,sizeof ( address ) ) !=0 )
380                 {
381                     QString errMsg=tr ( "Cannot connect to " ) +
382                                    req.localHost+":"+QString::number ( req.localPort );
383                     x2goDebug<<errMsg<<endl;
384                     emit ioErr ( req.creator, errMsg, "" );
385                     break;
386                 }
387 
388                 ChannelConnection con;
389                 con.channel=chan;
390                 con.sock=sock;
391                 con.creator=req.creator;
392                 channelConnectionsMutex.lock();
393                 channelConnections<<con;
394                 channelConnectionsMutex.unlock();
395                 x2goDebug<<"New channel created";
396                 break;
397             }
398         }
399         reverseTunnelRequestMutex.unlock();
400 
401 
402     }
403 }
404 
405 
406 int SshMasterConnection::startTunnel(const QString& forwardHost, uint forwardPort, const QString& localHost, uint localPort, bool reverse,
407                                      QObject* receiver, const char* slotTunnelOk, const char* slotFinished)
408 {
409     SshProcess* proc=new SshProcess(this, nextPid++);
410     if(receiver && slotFinished)
411     {
412         connect(proc, SIGNAL(sshFinished(bool,QString,int)), receiver, slotFinished);
413     }
414     if(receiver && slotTunnelOk)
415     {
416         connect(proc, SIGNAL(sshTunnelOk(int)), receiver, slotTunnelOk);
417     }
418     proc->startTunnel(forwardHost, forwardPort, localHost, localPort, reverse);
419     if(reverse && !kerberos)
420     {
421         connect(this, SIGNAL(reverseTunnelOk(SshProcess*)), proc, SLOT(slotReverseTunnelOk(SshProcess*)));
422         connect(this, SIGNAL(reverseTunnelFailed(SshProcess*,QString)), proc, SLOT(slotReverseTunnelFailed(SshProcess*,QString)));
423         ReverseTunnelRequest req;
424         req.creator=proc;
425         req.localPort=localPort;
426         req.localHost=localHost;
427         req.forwardPort=forwardPort;
428         req.listen=false;
429         x2goDebug<<"Requesting reverse tunnel from port "<<forwardPort<< " to "<<localPort;
430         reverseTunnelRequestMutex.lock();
431         reverseTunnelRequest<<req;
432         reverseTunnelRequestMutex.unlock();
433     }
434     processes<<proc;
435     return proc->pid;
436 }
437 
438 void SshMasterConnection::slotSshProxyInteractionFinish(SshMasterConnection* connection)
439 {
440     x2goDebug<<"SSH proxy interaction finished";
441     slotSshProxyUserAuthError("NO_ERROR");
442 }
443 
444 void SshMasterConnection::slotSshProxyInteractionStart(SshMasterConnection* connection, QString prompt)
445 {
446     emit startInteraction(this, prompt);
447 }
448 
449 void SshMasterConnection::slotSshProxyInteractionUpdate(SshMasterConnection* connection, QString output)
450 {
451     emit updateInteraction(this, output);
452 }
453 
454 
455 void SshMasterConnection::slotSshProxyConnectionError(QString err1, QString err2)
456 {
457     breakLoop=true;
458     emit connectionError(tr("SSH proxy connection error."),err1+" "+err2);
459 }
460 
461 void SshMasterConnection::slotSshProxyServerAuthError(int errCode, QString err, SshMasterConnection* con)
462 {
463     emit serverAuthError(errCode, tr("SSH proxy connection error: ")+err, con);
464 }
465 
466 void SshMasterConnection::slotSshProxyUserAuthError(QString err)
467 {
468     breakLoop=true;
469     if(err=="NO_ERROR" || err=="NO_PROXY_ERROR")
470       emit userAuthError(err);
471     else
472       emit userAuthError(tr("SSH proxy connection error: ")+err);
473 }
474 
475 
476 void SshMasterConnection::slotSshProxyTunnelOk(int)
477 {
478     x2goDebug<<"SSH proxy tunnel established.";
479     sshProxyReady=true;
480 }
481 
482 void SshMasterConnection::slotSshProxyTunnelFailed(bool ,  QString output,
483         int)
484 {
485     breakLoop=true;
486     emit connectionError(tr("Failed to create SSH proxy tunnel."), output);
487 }
488 
489 
490 
491 void SshMasterConnection::slotSshProxyServerAuthAborted()
492 {
493     breakLoop=true;
494 }
495 
496 void SshMasterConnection::run()
497 {
498     x2goDebug<<"SshMasterConnection, instance "<<this<<" entering thread.";
499     if(useproxy && proxytype==PROXYSSH)
500     {
501         x2goDebug << "proxyserver: " << proxyserver << "; proxyport: " << proxyport << "; proxylogin: " << proxylogin;
502         sshProxy=new SshMasterConnection (0, proxyserver, proxyport,acceptUnknownServers,
503                                           proxylogin, proxypassword, proxykey, proxyautologin, proxyKrbLogin, false);
504 
505         qRegisterMetaType<SshMasterConnection::passphrase_types> ("SshMasterConnection::passphrase_types");
506 
507         connect ( sshProxy, SIGNAL ( connectionOk(QString) ), this, SLOT ( slotSshProxyConnectionOk() ) );
508 
509         connect ( sshProxy, SIGNAL ( serverAuthError ( int,QString,SshMasterConnection* ) ),this,
510                   SLOT ( slotSshProxyServerAuthError ( int,QString, SshMasterConnection* ) ) );
511         connect ( sshProxy, SIGNAL ( needPassPhrase(SshMasterConnection*, SshMasterConnection::passphrase_types)),this,
512                   SIGNAL ( needPassPhrase(SshMasterConnection*, SshMasterConnection::passphrase_types)) );
513         connect ( sshProxy, SIGNAL ( serverAuthAborted()),this,
514                   SLOT ( slotSshProxyServerAuthAborted()) );
515         connect ( sshProxy, SIGNAL ( userAuthError ( QString ) ),this,SLOT ( slotSshProxyUserAuthError ( QString ) ) );
516         connect ( sshProxy, SIGNAL ( connectionError ( QString,QString ) ), this,
517                   SLOT ( slotSshProxyConnectionError ( QString,QString ) ) );
518 
519         connect ( sshProxy, SIGNAL(startInteraction(SshMasterConnection*,QString)),this,
520                   SLOT(slotSshProxyInteractionStart(SshMasterConnection*,QString)) );
521         connect ( sshProxy, SIGNAL(updateInteraction(SshMasterConnection*,QString)),this,
522                   SLOT(slotSshProxyInteractionUpdate(SshMasterConnection*,QString)) );
523         connect ( sshProxy, SIGNAL(finishInteraction(SshMasterConnection*)),this,
524 		  SLOT(slotSshProxyInteractionFinish(SshMasterConnection*)));
525 //         connect ( interDlg, SIGNAL(textEntered(QString)), con, SLOT(interactionTextEnter(QString)));
526 //         connect ( interDlg, SIGNAL(interrupt()), con, SLOT(interactionInterruptSlot()));
527 
528         sshProxyReady=false;
529         sshProxy->start();
530 
531         while(! sshProxyReady)
532         {
533             if(breakLoop)
534             {
535                 quit();
536                 return;
537             }
538             this->usleep(200);
539         }
540     }
541     disconnectSessionFlag=false;
542 
543     int verbosity = SSH_LOG_NOLOG;
544 
545     if (ONMainWindow::libssh_debugging) {
546 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
547       verbosity = SSH_LOG_INFO;
548 #else
549       verbosity = SSH_LOG_PROTOCOL;
550 #endif
551     }
552 
553     if (ONMainWindow::libssh_packetlog) {
554 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
555       verbosity = SSH_LOG_DEBUG;
556 #else
557       verbosity = SSH_LOG_PACKET;
558 #endif
559     }
560 
561     long timeout = 60;
562 
563     my_ssh_session = ssh_new();
564     if ( my_ssh_session == NULL )
565     {
566         QString err=tr ( "Cannot create SSH session." );
567         x2goDebug<<err<<endl;
568         emit connectionError ( err,"" );
569         quit();
570         return;
571     }
572 
573 #ifdef Q_OS_WIN
574     {
575         QByteArray tmp_BA = (mainWnd->getHomeDirectory () + "/ssh").toLocal8Bit ();
576 
577         ssh_options_set ( my_ssh_session, SSH_OPTIONS_SSH_DIR, tmp_BA.data ());
578         x2goDebug << "Setting SSH directory to " << tmp_BA.data ();
579         if (kerberos)
580         {
581             parseKnownHosts();
582         }
583     }
584 #endif
585 
586     ssh_options_set(my_ssh_session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
587 
588     ssh_options_set(my_ssh_session, SSH_OPTIONS_TIMEOUT, &timeout);
589 
590     if (useproxy && proxytype == PROXYHTTP)
591     {
592         socket_t proxysocket = SSH_INVALID_SOCKET;
593 
594         tcpNetworkProxy = new QNetworkProxy( QNetworkProxy::HttpProxy,
595                                              proxyserver, proxyport, proxylogin, proxypassword);
596         tcpProxySocket = new QTcpSocket();
597         tcpProxySocket->setProxy( *tcpNetworkProxy );
598         tcpProxySocket->connectToHost(host, port);
599         if (!tcpProxySocket->waitForConnected(30000))
600         {
601             QString message=tr ( "Cannot connect to proxy server." );
602             x2goDebug<<message<<endl;
603             emit connectionError ( "Proxy", message );
604             ssh_free ( my_ssh_session );
605             quit();
606             return;
607         }
608         proxysocket = tcpProxySocket->socketDescriptor();
609         x2goDebug << "Created HTTP proxy socket: " << proxysocket << endl;
610         ssh_options_set( my_ssh_session, SSH_OPTIONS_FD, &proxysocket);
611         ssh_set_fd_toread( my_ssh_session);
612         x2goDebug<<"Connected to HTTP proxy server: " << proxyserver << ":"
613                  << proxyport <<endl;
614     }
615 
616     if ( !sshConnect() )
617     {
618         if(disconnectSessionFlag)
619         {
620             x2goDebug<<"Session is already disconnected, exiting."<<endl;
621             return;
622         }
623         QString err=ssh_get_error ( my_ssh_session );
624         QString message=tr ( "Cannot connect to " ) +host+":"+QString::number ( port );
625         x2goDebug<<message<<" - "<<err;
626         emit connectionError ( message, err );
627         ssh_free ( my_ssh_session );
628         quit();
629         return;
630     }
631     if(disconnectSessionFlag)
632     {
633         x2goDebug<<"Session is already disconnected, exiting."<<endl;
634         return;
635     }
636     QString errMsg;
637     int state=serverAuth ( errMsg );
638     if ( state != SSH_SERVER_KNOWN_OK )
639     {
640         if(disconnectSessionFlag)
641         {
642             x2goDebug<<"Session is already disconnected, exiting."<<endl;
643             return;
644         }
645         writeHostKey=writeHostKeyReady=false;
646         emit serverAuthError ( state,errMsg, this );
647         for(;;)
648         {
649             this->usleep(100);
650             writeHostKeyMutex.lock();
651             if(writeHostKeyReady)
652             {
653                 if(writeHostKey)
654                     ssh_write_knownhost(my_ssh_session);
655                 writeHostKeyMutex.unlock();
656                 break;
657             }
658             writeHostKeyMutex.unlock();
659         }
660         ssh_disconnect ( my_ssh_session );
661         ssh_free ( my_ssh_session );
662         return;
663     }
664 
665     if(disconnectSessionFlag)
666     {
667         x2goDebug<<"Session is already disconnected, exiting."<<endl;
668         return;
669     }
670 
671     QByteArray tmp_BA = user.toLocal8Bit ();
672     ssh_options_set ( my_ssh_session, SSH_OPTIONS_USER, tmp_BA.data () );
673 
674 #ifdef Q_OS_WIN
675     {
676         QByteArray tmp_BA = (mainWnd->getHomeDirectory () + "/ssh").toLocal8Bit ();
677 
678         ssh_options_set ( my_ssh_session, SSH_OPTIONS_SSH_DIR, tmp_BA.data () );
679         x2goDebug << "Setting SSH directory to " << tmp_BA.data ();
680     }
681 #endif
682 
683     if ( userAuth() )
684     {
685         if(disconnectSessionFlag)
686         {
687             x2goDebug<<"Session is already disconnected, exiting."<<endl;
688             return;
689         }
690         x2goDebug<<"User authentication OK.";
691         // checkLogin() is currently specific to libssh.
692         if(kerberos)
693         {
694             emit connectionOk(host);
695         }
696         else if(this->loginCheck == false)
697         {
698             x2goDebug<<"Skipping Login Check as requested by configuration";
699             emit connectionOk(host);
700         }
701         else
702         {
703             if(checkLogin())
704             {
705                 x2goDebug<<"Login Check - OK";
706                 emit connectionOk(host);
707             }
708             else
709             {
710                 x2goDebug<<"Login Check - Failed";
711 //          if(!interactionInterrupt)
712                 {
713                   emit finishInteraction(this);
714                 }
715                 ssh_disconnect ( my_ssh_session );
716                 ssh_free ( my_ssh_session );
717                 quit();
718                 return;
719             }
720         }
721     }
722     else
723     {
724         if(disconnectSessionFlag)
725         {
726             x2goDebug<<"Session is already disconnected, exiting."<<endl;
727             return;
728         }
729         QString err;
730         if (!kerberos)
731         {
732             err=ssh_get_error ( my_ssh_session );
733         }
734         else
735         {
736             err=sshProcErrString;
737         }
738         QString message=tr ( "Authentication failed." );
739         x2goDebug<<message<<" - "<<err;
740         emit userAuthError ( authErrors.join ( "\n" ) );
741         ssh_disconnect ( my_ssh_session );
742         ssh_free ( my_ssh_session );
743         quit();
744         return;
745     }
746 
747 
748 #ifndef Q_OS_WIN
749     const int y=1;
750 #else
751     const char y=1;
752 #endif
753     socket_t session_sock=ssh_get_fd(my_ssh_session);
754     setsockopt(session_sock, IPPROTO_TCP, TCP_NODELAY,&y, sizeof(int));
755 
756 
757     channelLoop();
758 }
759 
760 
761 SshMasterConnection::~SshMasterConnection()
762 {
763 
764     disconnectFlagMutex.lock();
765     disconnectSessionFlag=true;
766     disconnectFlagMutex.unlock();
767     x2goDebug<<"SshMasterConnection, instance "<<this<<" waiting for thread to finish.";
768     wait(15000);
769     x2goDebug<<"SshMasterConnection, instance "<<this<<" thread finished.";
770     for(int i=processes.count()-1; i>=0; --i)
771     {
772         delete processes[i];
773     }
774     x2goDebug<<"SshMasterConnection, instance "<<this<<" finished destructor.";
775 }
776 
777 
778 bool SshMasterConnection::sshConnect()
779 {
780     int rc;
781     QByteArray tmpBA = host.toLocal8Bit();
782     if(useproxy && proxytype==PROXYSSH)
783     {
784         ssh_options_set ( my_ssh_session, SSH_OPTIONS_HOST, "127.0.0.1" );
785         if (localProxyPort) {
786             ssh_options_set ( my_ssh_session, SSH_OPTIONS_PORT, &localProxyPort );
787         }
788     }
789     else
790     {
791         ssh_options_set ( my_ssh_session, SSH_OPTIONS_HOST, tmpBA.data() );
792         if (port) {
793             ssh_options_set ( my_ssh_session, SSH_OPTIONS_PORT, &port );
794         }
795     }
796 
797 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
798     unsigned int cur_port = 0;
799     ssh_options_get_port (my_ssh_session, &cur_port);
800     x2goDebug << "Session port before config file parse: " << cur_port;
801 #endif
802 
803     /* Parse ~/.ssh/config. */
804     if (ssh_options_parse_config (my_ssh_session, NULL) < 0) {
805         x2goDebug << "Warning: unable to parse the SSH config file.";
806     }
807 
808 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
809     ssh_options_get_port (my_ssh_session, &cur_port);
810     x2goDebug << "Session port after config file parse: " << cur_port;
811 #endif
812 
813     rc = ssh_connect ( my_ssh_session );
814     if ( rc != SSH_OK )
815     {
816         return false;
817     }
818 //set values for remote host for proper server authentication
819     if(useproxy && proxytype==PROXYSSH)
820     {
821         x2goDebug << "Connected via proxy, resetting connection values on session to " << tmpBA.data() << ":" << port;
822         ssh_options_set ( my_ssh_session, SSH_OPTIONS_HOST, tmpBA.data() );
823 
824         /*
825          * The SSH port might be 0, which indicates to use the default port
826          * or a custom one specified in the config file.
827          * We need to fetch the latter and then set the port unconditionally.
828          *
829          * The tricky part is that we already set a port before (in this case our proxy port.)
830          * There's no way to reset the port for this session to its default value of 0 again,
831          * so we'll need to create a new session, set the hostname
832          * and fetch the inferred port value from there.
833          *
834          * Failure to do so will trigger funny bugs like connecting to the correct remote host,
835          * but at a proxied port value.
836          */
837         int work_port = port;
838 
839         /* Oh, yeah, and we don't really support port values of 0 for pre-0.6.0 libssh. Sorry. */
840 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
841         if (!work_port) {
842             ssh_session tmp_session = ssh_new ();
843 
844             if (!tmp_session) {
845                 QString error_msg = tr ("Cannot create SSH session.");
846                 x2goDebug << error_msg;
847                 return (false);
848             }
849             else {
850                 ssh_options_set (tmp_session, SSH_OPTIONS_HOST, tmpBA.data ());
851 
852                 /* Parse ~/.ssh/config. */
853                 if (ssh_options_parse_config (tmp_session, NULL) < 0) {
854                     x2goDebug << "Warning: unable to parse the SSH config file.";
855                 }
856 
857                 unsigned int inferred_port = 0;
858                 ssh_options_get_port (tmp_session, &inferred_port);
859                 x2goDebug << "Fetched inferred session port: " << inferred_port;
860 
861                 work_port = inferred_port & 0xFFFF;
862 
863                 ssh_free (tmp_session);
864             }
865         }
866 #endif
867 
868         ssh_options_set ( my_ssh_session, SSH_OPTIONS_PORT, &work_port );
869     }
870 
871 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
872     ssh_options_get_port (my_ssh_session, &cur_port);
873     x2goDebug << "Session port before config file parse (part 2): " << cur_port;
874 #endif
875 
876     /* Parse ~/.ssh/config. */
877     if (ssh_options_parse_config (my_ssh_session, NULL) < 0) {
878         x2goDebug << "Warning: unable to parse the SSH config file.";
879     }
880 
881 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
882     ssh_options_get_port (my_ssh_session, &cur_port);
883     x2goDebug << "Session port after config file parse (part 2): " << cur_port;
884 #endif
885 
886     return true;
887 }
888 
889 
890 void SshMasterConnection::writeKnownHosts(bool write)
891 {
892     writeHostKeyMutex.lock();
893     writeHostKeyReady=true;
894     writeHostKey=write;
895     if(!write)
896     {
897         breakLoop=true;
898         emit serverAuthAborted();
899     }
900     writeHostKeyMutex.unlock();
901 
902 }
903 
904 
905 int SshMasterConnection::serverAuth ( QString& errorMsg )
906 {
907     x2goDebug<<"cserverAuth";
908 
909     int state = SSH_SERVER_ERROR;
910     unsigned char *hash = NULL;
911     char *hexa = NULL;
912 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
913     ssh_key srv_pubkey = { 0 };
914     int rc = SSH_ERROR;
915     size_t hlen = 0;
916 #else
917     int hlen = 0;
918 #endif
919 
920     state = ssh_is_server_known ( my_ssh_session );
921 
922 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
923 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 8, 0)
924     rc = ssh_get_server_publickey (my_ssh_session, &srv_pubkey);
925 #else /* LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 8, 0) */
926     rc = ssh_get_publickey (my_ssh_session, &srv_pubkey);
927 #endif /* LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 8, 0) */
928 
929     if (SSH_OK != rc) {
930         return (SSH_SERVER_ERROR);
931     }
932 
933     rc = ssh_get_publickey_hash (srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, &hash, &hlen);
934     ssh_key_free (srv_pubkey);
935 
936     if (0 != rc) {
937         return (SSH_SERVER_ERROR);
938     }
939 #else
940     hlen = ssh_get_pubkey_hash ( my_ssh_session, &hash );
941 #endif
942 
943     if ( 0 >= hlen )
944         return SSH_SERVER_ERROR;
945 
946     x2goDebug<<"state: "<<state<<endl;
947 
948     switch ( state )
949     {
950     case SSH_SERVER_KNOWN_OK:
951         break; /* ok */
952 
953     case SSH_SERVER_KNOWN_CHANGED:
954         hexa = ssh_get_hexa ( hash, hlen );
955         errorMsg=host+":"+QString::number(port)+" - "+hexa;
956         ssh_string_free_char ( hexa );
957         break;
958     case SSH_SERVER_FOUND_OTHER:
959         break;
960     case SSH_SERVER_FILE_NOT_FOUND:
961     case SSH_SERVER_NOT_KNOWN:
962         if ( !acceptUnknownServers )
963         {
964             hexa = ssh_get_hexa ( hash, hlen );
965             errorMsg=host+":"+QString::number(port)+" - "+hexa;
966             ssh_string_free_char ( hexa );
967             break;
968         }
969         ssh_write_knownhost ( my_ssh_session );
970         state=SSH_SERVER_KNOWN_OK;
971         break;
972 
973     case SSH_SERVER_ERROR:
974         errorMsg=host+":"+QString::number(port)+" - "+ssh_get_error ( my_ssh_session );
975         break;
976     }
977     ssh_clean_pubkey_hash ( &hash );
978     return state;
979 }
980 
981 void SshMasterConnection::setVerficationCode(QString code)
982 {
983     challengeAuthVerificationCode=code;
984 }
985 
986 
987 
988 bool SshMasterConnection::userAuthKeyboardInteractive(QString prompt)
989 {
990     x2goDebug<<"Open Interaction dialog to complete authentication";
991     emit startInteraction(this, prompt);
992     interactionInterrupt=false;
993     interactionInputText=QString::null;
994     int rez=SSH_AUTH_INFO;
995     bool firstLoop=true;
996     int prompts=1;
997     QString instruction;
998     while (rez==SSH_AUTH_INFO)
999     {
1000 
1001         if(firstLoop)
1002         {
1003             firstLoop=false;
1004         }
1005         else
1006         {
1007             prompts=ssh_userauth_kbdint_getnprompts(my_ssh_session);
1008             if(prompts>0)
1009                 emit updateInteraction(this, ssh_userauth_kbdint_getprompt(my_ssh_session,0,NULL));
1010 
1011             QString name= ssh_userauth_kbdint_getname(my_ssh_session);
1012             instruction = ssh_userauth_kbdint_getinstruction(my_ssh_session);
1013             x2goDebug<<"Have prompts: "<<prompts<<endl;
1014             x2goDebug<<"Name: "<<name<<endl;
1015             x2goDebug<<"Instruction: "<<instruction<<endl;
1016         }
1017         if(prompts>0)
1018         {
1019             while(true)
1020             {
1021                 bool interrupt;
1022                 interactionInputMutex.lock();
1023                 interrupt=interactionInterrupt;
1024                 QString textToSend=interactionInputText;
1025                 interactionInputText=QString::null;
1026                 interactionInputMutex.unlock();
1027                 if(textToSend.length()>0)
1028                 {
1029                     x2goDebug<<"SEND Input to SERVER";
1030                     textToSend.replace("\n","");
1031                     ssh_userauth_kbdint_setanswer(my_ssh_session,0,textToSend.toLocal8Bit());
1032                     break;
1033                 }
1034                 if(interrupt)
1035                 {
1036                     x2goDebug<<"Keyboard authentication failed";
1037 //         QString err=ssh_get_error ( my_ssh_session );
1038                     authErrors<<"NO_ERROR";
1039                     emit finishInteraction(this);
1040 
1041                     return false;
1042                 }
1043                 this->usleep(30);
1044             }
1045         }
1046 
1047         rez=ssh_userauth_kbdint(my_ssh_session, NULL, NULL);
1048 
1049     }
1050     if(rez==SSH_AUTH_SUCCESS)
1051     {
1052         x2goDebug<<"Keyboard authentication successful";
1053         emit finishInteraction(this);
1054         return true;
1055     }
1056     if(rez==SSH_AUTH_DENIED)
1057     {
1058         x2goDebug<<"Keyboard authentication failed";
1059         QString err=ssh_get_error ( my_ssh_session );
1060         if(instruction.length()>0)
1061         {
1062             authErrors<<instruction;
1063         }
1064         authErrors<<err;
1065         emit finishInteraction(this);
1066 
1067         return false;
1068     }
1069 
1070     QString err=ssh_get_error ( my_ssh_session );
1071     authErrors<<err;
1072 
1073     return false;
1074 
1075 }
1076 
1077 
1078 
1079 bool SshMasterConnection::userChallengeAuth()
1080 {
1081     int rez=ssh_userauth_kbdint(my_ssh_session, NULL, NULL);
1082     int prompts;
1083 
1084     switch( rez)
1085     {
1086     case SSH_AUTH_INFO:
1087         prompts=ssh_userauth_kbdint_getnprompts(my_ssh_session);
1088         x2goDebug<<"Have prompts: "<<prompts<<endl;
1089         if(prompts)
1090         {
1091             const char *prompt= ssh_userauth_kbdint_getprompt(my_ssh_session,0,NULL);
1092             x2goDebug<<"Prompt[0]: |"<<prompt<<"|"<<endl;
1093             QString pr=prompt;
1094             if(pr.startsWith ("Password:"))
1095             {
1096                 x2goDebug<<"Password request"<<endl;
1097                 ssh_userauth_kbdint_setanswer(my_ssh_session,0,pass.toLatin1());
1098                 return userChallengeAuth();
1099             }
1100 
1101             bool has_challenge_auth_code_prompt = false;
1102             bool need_to_display_auth_code_prompt = false;
1103             const std::size_t challenge_auth_code_prompts_size = (sizeof (challenge_auth_code_prompts_)/sizeof (*challenge_auth_code_prompts_));
1104 
1105             if (pr.contains ("challenge", Qt::CaseInsensitive)) {
1106               x2goDebug << "prompt contains 'challenge': " << pr << endl;
1107               has_challenge_auth_code_prompt = true;
1108               need_to_display_auth_code_prompt = true;
1109             }
1110             else {
1111               for (std::size_t i = 0; i < challenge_auth_code_prompts_size; ++i) {
1112                 x2goDebug << "Checking against known prompt #" << i << ": " << challenge_auth_code_prompts_[i] << endl;
1113 
1114                 /* Ignore "garbage" at the start of the string, but require at least one line to start with a known prompt. */
1115                 QStringList tmp_str_list = pr.split ("\n", QString::SkipEmptyParts);
1116 
1117                 for (QStringList::const_iterator cit = tmp_str_list.constBegin (); cit != tmp_str_list.constEnd (); ++cit) {
1118                   if ((*cit).startsWith (challenge_auth_code_prompts_[i])) {
1119                     has_challenge_auth_code_prompt = true;
1120                     break;
1121                   }
1122                 }
1123 
1124                 /* Skip over other challenge auth code prompts if we found one already. */
1125                 if (has_challenge_auth_code_prompt) {
1126                   break;
1127                 }
1128               }
1129             }
1130 
1131             if (has_challenge_auth_code_prompt) {
1132                 x2goDebug<<"Verification code request"<<endl;
1133 
1134                 challengeAuthPasswordAccepted=true;
1135                 if(challengeAuthVerificationCode == QString::null)
1136                 {
1137                     keyPhraseReady=false;
1138                     if (need_to_display_auth_code_prompt) {
1139                       emit needChallengeResponse(this, pr);
1140                     } else {
1141                       emit needPassPhrase(this, PASSPHRASE_CHALLENGE);
1142                     }
1143                     for(;;)
1144                     {
1145                         bool ready=false;
1146                         this->usleep(200);
1147                         keyPhraseMutex.lock();
1148                         if(keyPhraseReady)
1149                             ready=true;
1150                         keyPhraseMutex.unlock();
1151                         if(ready)
1152                             break;
1153                     }
1154                     challengeAuthVerificationCode=keyPhrase;
1155                     if(challengeAuthVerificationCode==QString::null)
1156                     {
1157                         authErrors<<tr("Authentication failed.");
1158                         return false;
1159                     }
1160                 }
1161                 ssh_userauth_kbdint_setanswer(my_ssh_session,0,challengeAuthVerificationCode.toLatin1());
1162                 return userChallengeAuth();
1163             }
1164             else
1165 	    {
1166 	        return userAuthKeyboardInteractive(prompt);
1167 	    }
1168         }
1169         else
1170         {
1171             return userChallengeAuth();
1172         }
1173     case SSH_AUTH_SUCCESS:
1174         x2goDebug<<"Challenge authentication OK."<<endl;
1175         return true;
1176     case SSH_AUTH_DENIED:
1177         if(!challengeAuthPasswordAccepted )
1178         {
1179             QString err=ssh_get_error ( my_ssh_session );
1180             authErrors<<err;
1181             return false;
1182         }
1183         else
1184         {
1185             challengeAuthVerificationCode=QString::null;
1186             //try with another verification code
1187             return userChallengeAuth();
1188         }
1189     default:
1190         QString err=ssh_get_error ( my_ssh_session );
1191         authErrors<<err;
1192 
1193         return false;
1194     }
1195     return false;
1196 
1197 }
1198 
1199 
1200 bool SshMasterConnection::userAuthWithPass()
1201 {
1202     bool ret = false;
1203 
1204     // Populate the userauth_list
1205     ssh_userauth_none(my_ssh_session, NULL);
1206 
1207     int method = ssh_userauth_list(my_ssh_session, NULL);
1208 
1209     if (method & SSH_AUTH_METHOD_INTERACTIVE) {
1210         x2goDebug << "Challenge authentication requested." << endl;
1211 
1212         challengeAuthPasswordAccepted = false;
1213         ret = userChallengeAuth ();
1214 
1215         if (!ret) {
1216             x2goDebug << "Challenge authentication failed." << endl;
1217         }
1218     }
1219 
1220     if (!ret) {
1221         x2goDebug << "Trying password mechanism if available." << endl;
1222     }
1223 
1224     if ((!ret) && (method & SSH_AUTH_METHOD_PASSWORD)) {
1225         x2goDebug << "Password mechanism available. Continuing." << endl;
1226 
1227         QString auth_password = pass;
1228 
1229         if (auth_password.isEmpty ()) {
1230             keyPhraseReady = false;
1231             emit needPassPhrase (this, PASSPHRASE_PASSWORD);
1232 
1233             for (bool ready = false; !ready;) {
1234                 this->usleep (200);
1235 
1236                 keyPhraseMutex.lock ();
1237                 ready = keyPhraseReady;
1238                 keyPhraseMutex.unlock ();
1239             }
1240 
1241             if (keyPhrase.isNull ()) {
1242                 authErrors << "No password provided.";
1243                 return (ret);
1244             }
1245             else {
1246                 auth_password = keyPhrase;
1247             }
1248         }
1249         int rc = ssh_userauth_password (my_ssh_session, NULL, auth_password.toLatin1 ());
1250         if ( rc != SSH_AUTH_SUCCESS )
1251         {
1252             QString err=ssh_get_error ( my_ssh_session );
1253             authErrors<<err;
1254             x2goDebug << "Password authentication failed: " << err << endl;
1255         }
1256         else {
1257             ret = true;
1258         }
1259     }
1260 
1261     if ((!ret) && (method & ~SSH_AUTH_METHOD_PASSWORD)) {
1262         /* In case password auth is disabled, make sure the error message is not empty. */
1263         QString err = ssh_get_error (my_ssh_session);
1264         authErrors << err;
1265         x2goDebug << "Password authentication not available: " << err << endl;
1266     }
1267 
1268     return (ret);
1269 }
1270 
1271 
1272 bool SshMasterConnection::userAuthAuto()
1273 {
1274     int rc = ssh_userauth_autopubkey ( my_ssh_session, "" );
1275     int i=0;
1276     while(rc != SSH_AUTH_SUCCESS)
1277     {
1278         if (SSH_AUTH_DENIED == rc) {
1279           /* No need to continue, all keys have been rejected by the server. */
1280           break;
1281         }
1282 
1283         /* This section should only be executed if rc is SSH_AUTH_ERROR. */
1284         keyPhraseReady=false;
1285         emit needPassPhrase(this, PASSPHRASE_PRIVKEY);
1286         for(;;)
1287         {
1288             bool ready=false;
1289             this->usleep(200);
1290             keyPhraseMutex.lock();
1291             if(keyPhraseReady)
1292                 ready=true;
1293             keyPhraseMutex.unlock();
1294             if(ready)
1295                 break;
1296         }
1297         if(keyPhrase==QString::null)
1298             break;
1299         rc = ssh_userauth_autopubkey ( my_ssh_session, keyPhrase.toLatin1() );
1300         if(i++==2)
1301         {
1302             break;
1303         }
1304     }
1305 
1306     if ( rc != SSH_AUTH_SUCCESS )
1307     {
1308         QString err=ssh_get_error ( my_ssh_session );
1309         authErrors<<err;
1310         x2goDebug << "userAuthAuto failed:" << err << " (code " << rc << ")" << endl;
1311         return false;
1312     }
1313     return true;
1314 }
1315 
1316 void SshMasterConnection::setKeyPhrase(QString phrase)
1317 {
1318     keyPhraseMutex.lock();
1319     keyPhrase=phrase;
1320     keyPhraseReady=true;
1321     keyPhraseMutex.unlock();
1322 }
1323 
1324 
1325 bool SshMasterConnection::userAuthWithKey()
1326 {
1327     x2goDebug<<"Trying to authenticate user with private key.";
1328     QString keyName=key;
1329     bool autoRemove=false;
1330     if ( key.indexOf ( "PRIVATE KEY" ) !=-1 )
1331     {
1332         QDir dr;
1333         QString keyPath=mainWnd->getHomeDirectory() +"/.x2go/ssh/gen";
1334         dr.mkpath ( keyPath );
1335         QTemporaryFile fl ( keyPath+"/key" );
1336         fl.open();
1337         keyName=fl.fileName();
1338         fl.setAutoRemove ( false );
1339         QTextStream out ( &fl );
1340         out<<key;
1341         fl.close();
1342         autoRemove=true;
1343         x2goDebug<<"Temporarily saved key in "<<keyName;
1344     }
1345 
1346     QByteArray tmp_ba = keyName.toLocal8Bit ();
1347 
1348 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
1349     ssh_key priv_key = { 0 };
1350 
1351     /*
1352      * Passing an empty string as a passphrase parameter is a workaround for inconsistent
1353      * behavior in libssh:
1354      *   - compiled with OpenSSL, libssh lets OpenSSL query the passphrase if the
1355      *     application has a controlling terminal connected
1356      *   - compiled with libgcrypt, this never happens
1357      *
1358      * We do not want to break user experience by having libssh/OpenSSL query for the
1359      * passphrase on a terminal (and the client not reacting to any input while this
1360      * happens), so work around this inconsistency by providing an empty passphrase.
1361      */
1362     int rc = ssh_pki_import_privkey_file (tmp_ba.data (), "", NULL, NULL, &priv_key);
1363 
1364     if (SSH_EOF == rc) {
1365         x2goDebug << "Failed to get private key from " << keyName << "; file does not exist.";
1366 
1367         ssh_key_free (priv_key);
1368 
1369         return (false);
1370     }
1371     else if (SSH_OK != rc) {
1372         x2goDebug << "Failed to get private key from " << keyName << "; trying to query passphrase.";
1373 
1374         ssh_key_free (priv_key);
1375         priv_key = NULL;
1376     }
1377 #else
1378     /* This is TOCTU, but forced upon us by libssh's legacy function. */
1379     {
1380       QFile tmp_file (keyName);
1381       if (tmp_file.open (QIODevice::ReadOnly)) {
1382         tmp_file.close ();
1383       }
1384       else {
1385         /* Don't pass invalid files to privatekey_from_file () - it crashes in this case. */
1386         return (false);
1387       }
1388     }
1389     ssh_private_key priv_key = privatekey_from_file (my_ssh_session, tmp_ba.data (), 0, NULL);
1390 #endif
1391 
1392     int i=0;
1393 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
1394     while (SSH_OK != rc)
1395 #else
1396     while (!priv_key)
1397 #endif
1398     {
1399         keyPhraseReady=false;
1400         emit needPassPhrase(this, PASSPHRASE_PRIVKEY);
1401         for(;;)
1402         {
1403             bool ready=false;
1404             this->usleep(200);
1405             keyPhraseMutex.lock();
1406             if(keyPhraseReady)
1407                 ready=true;
1408             keyPhraseMutex.unlock();
1409             if(ready)
1410                 break;
1411         }
1412         if(keyPhrase==QString::null)
1413             break;
1414 
1415         QByteArray tmp_ba_passphrase = keyPhrase.toLocal8Bit ();
1416 
1417 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
1418         rc = ssh_pki_import_privkey_file (tmp_ba.data (), tmp_ba_passphrase.data (), NULL, NULL, &priv_key);
1419 
1420         if (SSH_OK != rc) {
1421             ssh_key_free (priv_key);
1422             priv_key = NULL;
1423         }
1424 #else
1425         priv_key = privatekey_from_file (my_ssh_session, tmp_ba.data (), 0, tmp_ba_passphrase.data ());
1426 #endif
1427 
1428         if(i++==2)
1429         {
1430             break;
1431         }
1432     }
1433 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
1434     if (SSH_OK != rc)
1435 #else
1436     if (!priv_key)
1437 #endif
1438     {
1439         x2goDebug<<"Failed to get private key from "<<keyName;
1440         if ( autoRemove )
1441             QFile::remove ( keyName );
1442         return false;
1443     }
1444 
1445 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
1446     ssh_key pub_key = { 0 };
1447 
1448     rc = ssh_pki_export_privkey_to_pubkey (priv_key, &pub_key);
1449 #else
1450     ssh_public_key pub_key = publickey_from_privatekey (priv_key);
1451 #endif
1452 
1453 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
1454     if (SSH_OK != rc)
1455 #else
1456     if (!pub_key)
1457 #endif
1458     {
1459         x2goDebug<<"Failed to get public key from private key.";
1460 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
1461         ssh_key_free (priv_key);
1462         priv_key = NULL;
1463 
1464         ssh_key_free (pub_key);
1465         pub_key = NULL;
1466 #else
1467         privatekey_free(priv_key);
1468 #endif
1469         if ( autoRemove )
1470             QFile::remove ( keyName );
1471         return false;
1472     }
1473 
1474 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
1475     do {
1476         rc = ssh_userauth_try_publickey (my_ssh_session, NULL, pub_key);
1477     } while (SSH_AUTH_AGAIN == rc);
1478 
1479     ssh_key_free (pub_key);
1480     pub_key = NULL;
1481 
1482     /* FIXME: handle SSH_AUTH_PARTIAL correctly! */
1483     if (SSH_AUTH_SUCCESS != rc) {
1484         x2goDebug << "Unable to authenticate with public key.";
1485 
1486         ssh_key_free (priv_key);
1487         priv_key = NULL;
1488 
1489         if (autoRemove) {
1490             QFile::remove (keyName);
1491         }
1492 
1493         return (false);
1494     }
1495 
1496     do {
1497         rc = ssh_userauth_publickey (my_ssh_session, NULL, priv_key);
1498     } while (SSH_AUTH_AGAIN == rc);
1499 
1500     ssh_key_free (priv_key);
1501     priv_key = NULL;
1502 #else
1503     ssh_string pubkeyStr=publickey_to_string(pub_key);
1504     publickey_free(pub_key);
1505     int rc=ssh_userauth_pubkey(my_ssh_session, NULL, pubkeyStr, priv_key);
1506     privatekey_free(priv_key);
1507     ssh_string_free(pubkeyStr);
1508 #endif
1509 
1510     x2goDebug<<"Authenticating with key: "<<rc<<endl;
1511 
1512     if ( autoRemove )
1513         QFile::remove ( keyName );
1514 
1515     /* FIXME: handle SSH_AUTH_PARTIAL correctly! */
1516     if ( rc != SSH_AUTH_SUCCESS )
1517     {
1518         QString err=ssh_get_error ( my_ssh_session );
1519         authErrors<<err;
1520 
1521         x2goDebug<<"userAuthWithKey failed:" <<err<<endl;
1522 
1523         return false;
1524     }
1525     return true;
1526 }
1527 
1528 bool SshMasterConnection::userAuthKrb()
1529 {
1530     QProcess ssh;
1531 
1532     QUuid uuid = QUuid::createUuid();
1533     QString uuidStr = uuid.toString().mid(1, 36).toLower();
1534 
1535     /* On Windows, arguments are automatically wrapped in double quotes.
1536      * Additionally, QProcess automatically doubles escape characters before
1537      * double quotes and inserts an escape character before any non-escaped
1538      * double quotes.
1539      * Thus, we don't escape double quotes here and let Qt handle this stuff.
1540      *
1541      * On UNIX-like platforms, likewise, we MUST NOT escape double quotes,
1542      * as there is no preceding "outer double quote" the whole argument
1543      * is wrapped in.
1544      */
1545     QString shcmd = "bash -l -c 'echo \"X2GODATABEGIN:" + uuidStr + "\"; export TERM=\"dumb\"; whoami; echo \"X2GODATAEND:" + uuidStr + "\";'";
1546 
1547     QString local_cmd = "";
1548     QStringList local_args;
1549 
1550 #ifdef Q_OS_WIN
1551     local_cmd = "plink";
1552 
1553     /* General options. */
1554     local_args << "-batch";
1555 
1556     /* Port option. Must be the last one added! */
1557     local_args << "-P";
1558 #else
1559     local_cmd = "ssh";
1560 
1561     /* Kerberos options. */
1562     local_args << "-o" << "GSSApiAuthentication=yes"
1563                << "-o" << "PasswordAuthentication=no"
1564                << "-o" << "PubkeyAuthentication=no";
1565 
1566     /* Port option. Must be the last one added! */
1567     local_args << "-p";
1568 #endif
1569     local_args << QString::number (port)
1570                << "-l" << user
1571                << host;
1572 
1573     /* On Windows, arguments are automatically wrapped in double quotes.
1574      * This means we do not have to wrap shcmd ourselves.
1575      *
1576      * On UNIX-like platforms, we likewise MUST NOT wrap the command in
1577      * double quotes, as each entry in the arguments list is passed as
1578      * one entry in argv.
1579      */
1580     local_args << shcmd;
1581 
1582     x2goDebug << "Starting ssh:" << local_cmd << " " << local_args.join (" ") << endl;
1583     ssh.start (local_cmd, local_args);
1584 
1585 
1586     if (!ssh.waitForStarted(5000))
1587     {
1588         sshProcErrString=ssh.errorString();
1589         authErrors<<sshProcErrString;
1590         x2goDebug<<"SSH start failed:" <<sshProcErrString<<endl;
1591         return false;
1592     }
1593     if (!ssh.waitForFinished(20000))
1594     {
1595         sshProcErrString=ssh.errorString();
1596         authErrors<<tr("Failed to start SSH client. Please check your installation and GSSApi configuration.");
1597         authErrors<<sshProcErrString;
1598         x2goDebug<<"SSH did not finish:" <<sshProcErrString<<endl;
1599 
1600         return false;
1601     }
1602     QString outp=ssh.readAllStandardOutput();
1603     QString err=ssh.readAllStandardError();
1604     x2goDebug<<"SSH exited.";
1605     x2goDebug<<"stdout: "<<outp<<endl;
1606     x2goDebug<<"stderr: "<<err<<endl;
1607     x2goDebug<<"Exit code: "<<ssh.exitCode()<<"; status: "<<ssh.exitStatus()<<endl;
1608 
1609     QString begin_marker = "X2GODATABEGIN:"+uuidStr+"\n";
1610     QString end_marker = "X2GODATAEND:"+uuidStr+"\n";
1611     int output_begin=outp.indexOf(begin_marker) + begin_marker.length();
1612     int output_end=outp.indexOf(end_marker);
1613     outp = outp.mid(output_begin, output_end-output_begin);
1614     outp.replace("\n","");
1615 
1616     if (ssh.exitCode() == 0 && ssh.exitStatus() == 0 && outp== user)
1617         return true;
1618     authErrors<<tr("Check your GSSApi configuration or choose another authentication method.");
1619     return false;
1620 }
1621 
1622 
1623 void SshMasterConnection::interactionTextEnter(QString text)
1624 {
1625     if(sshProxy && ! sshProxyReady)
1626     {
1627         sshProxy->interactionTextEnter(text);
1628 	return;
1629     }
1630     interactionInputMutex.lock();
1631     interactionInputText=text;
1632     interactionInputMutex.unlock();
1633 }
1634 
1635 void SshMasterConnection::interactionInterruptSlot()
1636 {
1637     if(sshProxy && ! sshProxyReady)
1638     {
1639         sshProxy->interactionInterruptSlot();
1640 	return;
1641     }
1642     interactionInputMutex.lock();
1643     interactionInterrupt=true;
1644     interactionInputMutex.unlock();
1645 }
1646 
1647 bool SshMasterConnection::checkLogin()
1648 {
1649     interactionInterrupt=false;
1650     interactionInputText=QString::null;
1651 
1652 
1653     ssh_channel channel = ssh_channel_new ( my_ssh_session );
1654 
1655     if (!channel) {
1656         QString err = ssh_get_error (my_ssh_session);
1657         QString error_msg = tr ("%1 failed.").arg ("ssh_channel_new");
1658 
1659         x2goDebug << error_msg.left (error_msg.size () - 1) << ": " << err << endl;
1660         return false;
1661     }
1662     if ( ssh_channel_open_session ( channel ) !=SSH_OK )
1663     {
1664         QString err=ssh_get_error ( my_ssh_session );
1665         QString errorMsg=tr ( "%1 failed." ).arg ("ssh_channel_open_session");
1666         x2goDebug<<errorMsg.left (errorMsg.size () - 1)<<": "<<err<<endl;
1667         ssh_channel_free(channel);
1668         return false;
1669     }
1670     if (ssh_channel_request_pty(channel)!=SSH_OK)
1671     {
1672         QString err=ssh_get_error ( my_ssh_session );
1673         QString errorMsg=tr ( "%1 failed." ).arg ("ssh_channel_request_pty");
1674         x2goDebug<<errorMsg.left (errorMsg.size () - 1)<<": "<<err<<endl;
1675         ssh_channel_free(channel);
1676         return false;
1677     }
1678     if(ssh_channel_change_pty_size(channel, 80, 24)!=SSH_OK)
1679     {
1680         QString err=ssh_get_error ( my_ssh_session );
1681         QString errorMsg=tr ( "%1 failed." ).arg ("ssh_channel_change_pty_size");
1682         x2goDebug<<errorMsg.left (errorMsg.size () - 1)<<": "<<err<<endl;
1683         ssh_channel_free(channel);
1684         return false;
1685     }
1686     if ( ssh_channel_request_exec ( channel, "echo \"LOGIN OK\"" ) != SSH_OK )
1687     {
1688         QString err=ssh_get_error ( my_ssh_session );
1689         QString errorMsg=tr ( "%1 failed." ).arg ("ssh_channel_request_exec");
1690         x2goDebug<<errorMsg.left (errorMsg.size () - 1)<<": "<<err<<endl;
1691         ssh_channel_free(channel);
1692     }
1693     else
1694     {
1695         char buffer[1024*512]; //512K buffer
1696         bool hasInterraction=true;
1697 	bool interactionStarted=false;
1698 //         x2goDebug<<"CHECK LOGIN channel created."<<endl;
1699         while (ssh_channel_is_open(channel) &&
1700                 !ssh_channel_is_eof(channel))
1701         {
1702             int nbytes = ssh_channel_read_nonblocking(channel, buffer, sizeof(buffer), 0);
1703             if (nbytes < 0)
1704             {
1705                 ssh_channel_free(channel);
1706                 return false;
1707             }
1708             if (nbytes > 0)
1709             {
1710                 QString inf=QByteArray ( buffer,nbytes );
1711                 x2goDebug<<"LOGIN CHECK:"<<inf;
1712                 if(inf.indexOf("LOGIN OK")!=-1)
1713                 {
1714 		    x2goDebug<<"don't have interaction";
1715                     hasInterraction=false;
1716 		    break;
1717                 }
1718                 else
1719 		{
1720 		  if(!interactionStarted)
1721 		  {
1722 		     interactionStarted=true;
1723 		     emit startInteraction(this, inf);
1724 		  }
1725 		  else
1726 		     emit updateInteraction(this,inf);
1727 		}
1728             }
1729             bool interrupt;
1730             interactionInputMutex.lock();
1731 	    interrupt=interactionInterrupt;
1732 	    QString textToSend=interactionInputText;
1733 	    interactionInputText=QString::null;
1734 	    interactionInputMutex.unlock();
1735 	    if(textToSend.length()>0)
1736 	    {
1737 // 	        x2goDebug<<"SEND Input to SERVER";
1738 	        ssh_channel_write(channel, textToSend.toLocal8Bit().constData(), textToSend.length());
1739 	    }
1740 	    if(interrupt)
1741 	    {
1742 	        break;
1743 	    }
1744             this->usleep(30);
1745         }
1746 
1747         x2goDebug<<"LOOP FINISHED";
1748 	bool retVal=false;
1749         if(!hasInterraction)
1750 	{
1751 	    x2goDebug<<"No interaction needed, continue session";
1752             retVal=true;
1753 	}
1754 	else
1755         {
1756             sshProcErrString=tr("Reconnect session");
1757             x2goDebug<<"Reconnect session";
1758         }
1759         ssh_channel_close(channel);
1760         ssh_channel_send_eof(channel);
1761         ssh_channel_free(channel);
1762 	return retVal;
1763 
1764     }
1765     return false;
1766 
1767 }
1768 
1769 bool SshMasterConnection::userAuth()
1770 {
1771     if (kerberos)
1772         return userAuthKrb();
1773     if ( autologin && key=="" )
1774         if ( userAuthAuto() )
1775             return true;
1776     if ( key!="" )
1777     {
1778         if ( userAuthWithKey() )
1779             return true;
1780     }
1781     return userAuthWithPass();
1782 }
1783 
1784 
1785 void SshMasterConnection::addChannelConnection ( SshProcess* creator, int sock, QString forwardHost, int forwardPort,
1786         QString localHost, int localPort, void* channel )
1787 {
1788     ChannelConnection con;
1789     con.channel= ( ssh_channel ) channel;
1790     con.sock=sock;
1791     con.creator=creator;
1792     con.forwardHost=forwardHost;
1793     con.forwardPort=forwardPort;
1794     con.localHost=localHost;
1795     con.localPort=localPort;
1796 
1797     channelConnectionsMutex.lock();
1798     channelConnections<<con;
1799     channelConnectionsMutex.unlock();
1800 }
1801 
1802 
1803 void SshMasterConnection::addChannelConnection ( SshProcess* creator, QString uuid, QString cmd )
1804 {
1805 
1806     ChannelConnection con;
1807     con.channel=NULL;
1808     con.sock=-1;
1809     con.creator=creator;
1810     con.command=cmd;
1811     con.uuid=uuid;
1812 
1813     x2goDebug << "Locking SSH channel connection MUTEX.";
1814     channelConnectionsMutex.lock();
1815     x2goDebug << "Passing new channel connection object to channelConnections.";
1816     channelConnections<<con;
1817     x2goDebug << "Unlocking SSH channel connection MUTEX.";
1818     channelConnectionsMutex.unlock();
1819 }
1820 
1821 
1822 void SshMasterConnection::addCopyRequest ( SshProcess* creator, QString src, QString dst )
1823 {
1824     CopyRequest req;
1825     req.src=src;
1826     req.dst=dst;
1827     req.creator=creator;
1828     copyRequestMutex.lock();
1829     copyRequests<<req;
1830     copyRequestMutex.unlock();
1831 }
1832 
1833 
1834 void SshMasterConnection::copy()
1835 {
1836 
1837     for ( int i=copyRequests.size()-1; i>=0; --i )
1838     {
1839         QStringList lst=copyRequests[i].dst.split ( "/" );
1840         QString dstFile=lst.last();
1841         lst.removeLast();
1842         QString dstPath=lst.join ( "/" );
1843         x2goDebug<<"SSH Master Connection copy - dst path:"<<dstPath<<" file:"<<dstFile<<endl;
1844         ssh_scp scp=ssh_scp_new ( my_ssh_session, SSH_SCP_WRITE|SSH_SCP_RECURSIVE, dstPath.toLatin1() );
1845         if ( scp == NULL )
1846         {
1847             x2goDebug<<"Error allocating SCP session: "<< ssh_get_error ( my_ssh_session ) <<endl;
1848             return;
1849         }
1850         int rc = ssh_scp_init ( scp );
1851         if ( rc != SSH_OK )
1852         {
1853             x2goDebug<<"Error initializing SCP session: "<< ssh_get_error ( my_ssh_session ) <<endl;
1854             ssh_scp_free ( scp );
1855             return;
1856         }
1857         QFile file ( copyRequests[i].src );
1858         if ( !file.open ( QIODevice::ReadOnly ) )
1859         {
1860             QString errMsg=tr ( "Cannot open file " ) +copyRequests[i].src;
1861             emit copyErr ( copyRequests[i].creator, errMsg, "" );
1862             copyRequests.removeAt ( i );
1863             ssh_scp_close ( scp );
1864             ssh_scp_free ( scp );
1865             continue;
1866         }
1867         QByteArray arr=file.readAll();
1868         file.close();
1869         rc=ssh_scp_push_file ( scp,dstFile.toLatin1(),arr.size(), 0600 );
1870         if ( rc != SSH_OK )
1871         {
1872             QString errMsg=tr ( "Cannot create remote file " ) +copyRequests[i].dst;
1873             QString serr=ssh_get_error ( my_ssh_session );
1874             x2goDebug<<errMsg<<" - "<<serr<<endl;
1875             emit copyErr ( copyRequests[i].creator, errMsg, serr );
1876             copyRequests.removeAt ( i );
1877             ssh_scp_close ( scp );
1878             ssh_scp_free ( scp );
1879             continue;
1880         }
1881         rc=ssh_scp_write ( scp,arr.data(),arr.size() );
1882         if ( rc != SSH_OK )
1883         {
1884             QString serr=ssh_get_error ( my_ssh_session );
1885             QString errMsg=tr ( "Cannot write to remote file " ) +copyRequests[i].dst;
1886             x2goDebug<<errMsg<<" - "<<serr<<endl;
1887             emit copyErr ( copyRequests[i].creator, errMsg, serr );
1888             copyRequests.removeAt ( i );
1889             ssh_scp_close ( scp );
1890             ssh_scp_free ( scp );
1891             continue;
1892         }
1893         emit copyOk ( copyRequests[i].creator );
1894         x2goDebug<<"scp ok: "<<copyRequests[i].src<<" -> "<<user<<"@"<<host<<":"<<copyRequests[i].dst<<endl;
1895         copyRequests.removeAt ( i );
1896         ssh_scp_close ( scp );
1897         ssh_scp_free ( scp );
1898     }
1899 }
1900 
1901 void SshMasterConnection::channelLoop()
1902 {
1903     forever
1904     {
1905         disconnectFlagMutex.lock();
1906         bool disconnect=disconnectSessionFlag;
1907         disconnectFlagMutex.unlock();
1908 
1909         if ( disconnect )
1910         {
1911             x2goDebug<<"Disconnecting ..."<<endl;
1912 
1913             if (useproxy && proxytype==PROXYSSH&&sshProxy)
1914             {
1915                 delete sshProxy;
1916                 sshProxy=0;
1917             }
1918 
1919             channelConnectionsMutex.lock();
1920             x2goDebug<<"Deleting channel connections."<<endl;
1921             for ( int i=0; i<channelConnections.size(); ++i )
1922             {
1923                 finalize ( i );
1924             }
1925             channelConnectionsMutex.unlock();
1926             x2goDebug<<"Disconnecting session."<<endl;
1927             ssh_disconnect ( my_ssh_session );
1928             ssh_free ( my_ssh_session );
1929 
1930             x2goDebug<<"Deleting sockets."<<endl;
1931             if (tcpProxySocket != NULL)
1932                 delete tcpProxySocket;
1933             if (tcpNetworkProxy != NULL)
1934                 delete tcpNetworkProxy;
1935             x2goDebug<<"All channels closed and session disconnected. Quitting session loop."<<endl;
1936             quit();
1937             return;
1938         }
1939         addReverseTunnelConnections();
1940         checkReverseTunnelConnections();
1941         copyRequestMutex.lock();
1942         if ( copyRequests.size() >0 )
1943             copy();
1944         copyRequestMutex.unlock();
1945 
1946         char buffer[1024*512]; //512K buffer
1947         int nbytes;
1948         fd_set rfds;
1949 
1950         struct timeval tv;
1951 
1952         tv.tv_sec = 0;
1953         tv.tv_usec = 500000;
1954 
1955         int retval;
1956         int maxsock=-1;
1957 
1958 
1959         channelConnectionsMutex.lock();
1960         if ( channelConnections.size() <=0 )
1961         {
1962             //             x2goDebug<<"no channel connections, waiting\n";
1963             channelConnectionsMutex.unlock();
1964             usleep ( 500 );
1965             continue;
1966         }
1967         ssh_channel* read_chan=new ssh_channel[channelConnections.size() +1];
1968         ssh_channel* out_chan=new ssh_channel[channelConnections.size() +1];
1969         read_chan[channelConnections.size() ]=NULL;
1970 
1971         FD_ZERO ( &rfds );
1972 
1973         for ( int i=0; i<channelConnections.size(); ++i )
1974         {
1975             int tcpSocket=channelConnections.at ( i ).sock;
1976             if ( tcpSocket>0 )
1977                 FD_SET ( tcpSocket, &rfds );
1978             if ( channelConnections.at ( i ).channel==0l )
1979             {
1980                 x2goDebug<<"Creating new channel."<<endl;
1981                 ssh_channel channel = ssh_channel_new ( my_ssh_session );
1982 
1983                 if (!channel) {
1984                     QString err = ssh_get_error (my_ssh_session);
1985                     /*: Argument in this context will be a function name. */
1986                     QString error_msg = tr ("%1 failed.").arg ("ssh_channel_new");
1987                     emit ioErr (channelConnections[i].creator, error_msg, err);
1988 
1989                     x2goDebug << error_msg.left (error_msg.size () - 1) << ": " << err << endl;
1990 
1991                     continue;
1992                 }
1993                 x2goDebug<<"New channel:"<<channel<<endl;
1994                 channelConnections[i].channel=channel;
1995                 if ( tcpSocket>0 )
1996                 {
1997                     x2goDebug << "Forwarding parameters: from remote (" << channelConnections.at (i).forwardHost << ":"
1998                               << channelConnections.at (i).forwardPort << ") to local ("
1999                               << channelConnections.at (i).localHost << ":" << channelConnections.at (i).localPort
2000                               << ")";
2001 
2002                     /*
2003                      * Cannot support config file parsing here with pre-0.6.0 libssh versions.
2004                      * There's just no way to get the inferred host and port values.
2005                      */
2006 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT (0, 6, 0)
2007                     ssh_session tmp_session = ssh_new ();
2008 
2009                     if (!tmp_session) {
2010                         QString error_msg = tr ("Cannot create SSH session.");
2011                         x2goDebug << error_msg;
2012                         emit ioErr (channelConnections[i].creator, error_msg, "");
2013                     }
2014                     else {
2015                         QByteArray tmp_BA = channelConnections.at (i).forwardHost.toLocal8Bit ();
2016                         const int tmp_port = channelConnections.at (i).forwardPort;
2017 
2018                         ssh_options_set (tmp_session, SSH_OPTIONS_HOST, tmp_BA.data ());
2019 
2020                         if (tmp_port) {
2021                             ssh_options_set (tmp_session, SSH_OPTIONS_PORT, &tmp_port);
2022                         }
2023 
2024                         /* The host and port might be a shorthand and zero, so fetch the actual data. */
2025                         if (ssh_options_parse_config (tmp_session, NULL) < 0) {
2026                             x2goDebug << "Warning: unable to parse the SSH config file.";
2027                         }
2028 
2029                         unsigned int inferred_port = 0;
2030                         ssh_options_get_port (tmp_session, &inferred_port);
2031                         x2goDebug << "Temporary session port after config file parse: " << inferred_port;
2032 
2033                         char *inferred_host = NULL;
2034                         ssh_options_get (tmp_session, SSH_OPTIONS_HOST, &inferred_host);
2035                         x2goDebug << "Temporary session host after config file parse: " << inferred_host;
2036 
2037                         channelConnections[i].forwardHost = QString (inferred_host);
2038                         channelConnections[i].forwardPort = static_cast<int> (inferred_port);
2039 
2040                         ssh_string_free_char (inferred_host);
2041                         ssh_free (tmp_session);
2042                     }
2043 #endif
2044 
2045                     {
2046                         QByteArray tmp_BA_forward = channelConnections.at (i).forwardHost.toLocal8Bit ();
2047                         QByteArray tmp_BA_local = channelConnections.at (i).localHost.toLocal8Bit ();
2048 
2049                         if ( ssh_channel_open_forward ( channel,
2050                                                         tmp_BA_forward.data (),
2051                                                         channelConnections.at ( i ).forwardPort,
2052                                                         tmp_BA_local.data (),
2053                                                         channelConnections.at ( i ).localPort ) != SSH_OK )
2054                         {
2055                             QString err=ssh_get_error ( my_ssh_session );
2056                             QString errorMsg=tr ( "%1 failed." ).arg ("ssh_channel_open_forward");
2057                             emit ioErr ( channelConnections[i].creator, errorMsg, err );
2058                             x2goDebug<<errorMsg.left (errorMsg.size () - 1)<<": "<<err<<endl;
2059                         }
2060                         else
2061                         {
2062                             x2goDebug<<"New channel forwarded."<<endl;
2063                         }
2064                     }
2065                 }
2066                 else
2067                 {
2068                     x2goDebug<<"Executing remote: "<<channelConnections.at ( i ).command<<endl;
2069                     if ( ssh_channel_open_session ( channel ) !=SSH_OK )
2070                     {
2071                         QString err=ssh_get_error ( my_ssh_session );
2072                         QString errorMsg=tr ( "%1 failed." ).arg ("ssh_channel_open_session");
2073                         emit ioErr ( channelConnections[i].creator, errorMsg, err );
2074                         x2goDebug<<errorMsg.left (errorMsg.size () - 1)<<": "<<err<<endl;
2075                     }
2076                     else if ( ssh_channel_request_exec ( channel, channelConnections[i].command.toLatin1() ) != SSH_OK )
2077                     {
2078                         QString err=ssh_get_error ( my_ssh_session );
2079                         QString errorMsg=tr ( "%1 failed." ).arg ("ssh_channel_request_exec");
2080                         emit ioErr ( channelConnections[i].creator, errorMsg, err );
2081                         x2goDebug<<errorMsg.left (errorMsg.size () - 1)<<": "<<err<<endl;
2082                     }
2083                     else
2084                     {
2085                         x2goDebug<<"New exec channel created."<<endl;
2086                     }
2087                 }
2088 
2089             }
2090             read_chan[i]=channelConnections.at ( i ).channel;
2091             if ( tcpSocket>maxsock )
2092                 maxsock=tcpSocket;
2093         }
2094         channelConnectionsMutex.unlock();
2095         retval=ssh_select ( read_chan,out_chan,maxsock+1,&rfds,&tv );
2096         delete [] read_chan;
2097         delete [] out_chan;
2098 
2099         if ( retval == -1 )
2100         {
2101             x2goDebug<<"select() error.";
2102             continue;
2103         }
2104 
2105         //         x2goDebug<<"select exited"<<endl;
2106 
2107         channelConnectionsMutex.lock();
2108         for ( int i=channelConnections.size()-1; i>=0; --i )
2109         {
2110             int tcpSocket=channelConnections.at ( i ).sock;
2111             ssh_channel channel=channelConnections.at ( i ).channel;
2112             if ( channel==0l )
2113                 continue;
2114             if ( ssh_channel_poll ( channel,1 ) > 0 )
2115             {
2116                 //              x2goDebug<<"read err data from channel\n";
2117                 nbytes = ssh_channel_read ( channel, buffer, sizeof ( buffer )-1, 1 );
2118                 emit stdErr ( channelConnections[i].creator, QByteArray ( buffer,nbytes ) );
2119                 //              x2goDebug<<nbytes<<" err from channel"<<endl;
2120             }
2121             int rez = ssh_channel_poll ( channel, 0 );
2122             if ( rez==SSH_EOF )
2123             {
2124                 x2goDebug<<"EOF on channel "<<channel<<"; SshProcess object: "<<channelConnections[i].creator->pid;
2125 
2126                 //////Finished////////
2127                 finalize ( i );
2128                 continue;
2129             }
2130             if ( rez>0 )
2131             {
2132                 //                  x2goDebug<<"read data from channel "<<channel<<endl;
2133                 nbytes = ssh_channel_read ( channel, buffer, sizeof ( buffer )-1, 0 );
2134                 //                  x2goDebug<<nbytes<<" from channel "<<channel<<endl;
2135                 if ( nbytes > 0 )
2136                 {
2137                     if ( tcpSocket>0 )
2138                     {
2139                         if ( send ( tcpSocket,buffer, nbytes,0 ) != nbytes )
2140                         {
2141                             QString errMsg=tr ( "Error writing to socket." );
2142                             x2goDebug<<"Error writing "<<nbytes<<" to TCP socket"<<tcpSocket<<endl;
2143                             emit ioErr ( channelConnections[i].creator,errMsg,"" );
2144                             finalize ( i );
2145                             continue;
2146                         }
2147                         //                      x2goDebug<<"wrote "<<nbytes<<" to tcp socket "<<tcpSocket<<endl;
2148                     }
2149                     else
2150                     {
2151                         emit stdOut ( channelConnections[i].creator, QByteArray ( buffer,nbytes ) );
2152                     }
2153                 }
2154 
2155                 if ( nbytes < 0 )
2156                 {
2157                     //////ERROR!!!!!////////
2158                     QString err=ssh_get_error ( my_ssh_session );
2159                     QString errorMsg=tr ( "Error reading channel." );
2160                     emit ioErr ( channelConnections[i].creator, errorMsg, err );
2161                     x2goDebug<<errorMsg<<" - "<<err<<endl;
2162                     finalize ( i );
2163                     continue;
2164                 }
2165 
2166                 if ( ssh_channel_is_eof ( channel ) )
2167                 {
2168                     x2goDebug<<"EOF on channel "<<channel<<"; SshProcess object: "<<channelConnections[i].creator->pid;
2169 
2170                     //////Finished////////
2171                     finalize ( i );
2172                     continue;
2173                 }
2174             }
2175             if ( tcpSocket<=0 )
2176             {
2177                 continue;
2178             }
2179             if ( FD_ISSET ( tcpSocket,&rfds ) )
2180             {
2181                 nbytes = recv ( tcpSocket, buffer, sizeof ( buffer )-1,0 );
2182                 //                  x2goDebug<<nbytes<<" bytes from tcp socket "<<tcpSocket<<endl;
2183                 if ( nbytes > 0 )
2184                 {
2185                     if ( ssh_channel_write ( channel, buffer, nbytes ) !=nbytes )
2186                     {
2187                         QString err=ssh_get_error ( my_ssh_session );
2188                         QString errorMsg=tr ( "%1 failed." ).arg ("ssh_channel_write");
2189                         emit ioErr ( channelConnections[i].creator, errorMsg, err );
2190                         x2goDebug<<errorMsg.left (errorMsg.size () - 1)<<": "<<err<<endl;
2191                         finalize ( i );
2192                         continue;
2193                     }
2194                     //                      x2goDebug<<nbytes<<" bytes wrote to channel"<<channel<<endl;
2195                 }
2196                 if ( nbytes < 0 )
2197                 {
2198                     //////ERROR!!!!!////////
2199                     QString err="";
2200                     QString errorMsg=tr ( "Error reading from TCP socket." );
2201                     emit ioErr ( channelConnections[i].creator, errorMsg, err );
2202                     x2goDebug<<errorMsg<<" - "<<err<<endl;
2203                     finalize ( i );
2204                     continue;
2205                 }
2206                 if ( nbytes==0 )
2207                 {
2208                     x2goDebug<<"Socket "<<tcpSocket<<" closed."<<endl;
2209                     finalize ( i );
2210                     continue;
2211                 }
2212             }
2213         }
2214         channelConnectionsMutex.unlock();
2215     }
2216 }
2217 
2218 void SshMasterConnection::finalize ( int item )
2219 {
2220     int tcpSocket=channelConnections.at ( item ).sock;
2221     ssh_channel channel=channelConnections.at ( item ).channel;
2222     if ( channel )
2223     {
2224         ssh_channel_send_eof ( channel );
2225         x2goDebug<<"EOF sent.";
2226         ssh_channel_close ( channel );
2227         x2goDebug<<"Channel closed.";
2228         ssh_channel_free ( channel );
2229     }
2230     if ( tcpSocket>0 )
2231     {
2232 #ifndef Q_OS_WIN
2233         shutdown(tcpSocket, SHUT_RDWR);
2234         close ( tcpSocket );
2235 #else
2236         shutdown(tcpSocket, SD_BOTH);
2237         closesocket ( tcpSocket );
2238 #endif
2239     }
2240     SshProcess* proc=channelConnections[item].creator;
2241     QString uuid=channelConnections[item].uuid;
2242     channelConnections.removeAt ( item );
2243     emit channelClosed ( proc, uuid );
2244 }
2245 
2246 
2247 
2248 
2249