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