1
/**************************************************************************
2
* Copyright (C) 2005-2015 by Oleksandr Shneyder *
3
* o.shneyder@phoca-gmbh.de *
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. *
14
* You should have received a copy of the GNU General Public License *
15
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
16
***************************************************************************/
18
#include "x2goclientconfig.h"
19
#include "x2gologdebug.h"
20
#include "sshmasterconnection.h"
21
#include "sshprocess.h"
27
#include <arpa/inet.h>
28
#include <netinet/tcp.h>
34
#define KEEPALIVE_OPTION " -o ServerAliveInterval=60 "
36
SshProcess::SshProcess(SshMasterConnection* master, int pid): QObject(0)
40
connect(master,SIGNAL(stdErr(SshProcess*,QByteArray)),this,SLOT(slotStdErr(SshProcess*,QByteArray)));
41
connect(master,SIGNAL(ioErr(SshProcess*,QString,QString)),this,SLOT(slotIOerr(SshProcess*,QString,QString)));
49
SshProcess::~SshProcess()
52
x2goDebug<<"ssh process destructor";
59
disconnect(proc,SIGNAL(finished(int,QProcess::ExitStatus)),this,
60
SLOT(slotSshProcFinished(int,QProcess::ExitStatus)));
61
disconnect(proc,SIGNAL(readyReadStandardError()),this,SLOT(slotSshProcStdErr()));
62
disconnect(proc,SIGNAL(readyReadStandardOutput()),this,SLOT(slotSshProcStdOut()));
64
if (proc->state()==QProcess::Running && execProcess)
66
if(!proc->waitForFinished(3000))
71
if (proc->state()==QProcess::Running)
75
if(proc->state()!=QProcess::Running)
84
closesocket(serverSocket);
94
void SshProcess::slotCheckNewConnection()
103
FD_SET(serverSocket, &rfds);
105
if (select(serverSocket+1,&rfds,NULL,NULL,&tv)<=0)
109
x2goDebug<<"new tcp connection\n";
111
int tcpSocket=accept(serverSocket, (struct sockaddr*)&address,&addrlen);
114
x2goDebug<<"new socket:"<<tcpSocket<<endl;
116
masterCon->addChannelConnection(this, tcpSocket, forwardHost, forwardPort, localHost,
117
ntohs(address.sin_port));
121
void SshProcess::tunnelLoop()
124
serverSocket=socket(AF_INET, SOCK_STREAM, 0);
127
QString err=tr("Error creating socket");
128
x2goDebug<<err<<endl;
129
emit sshFinished(false,err,pid);
137
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR,&y, sizeof(int));
138
setsockopt(serverSocket, IPPROTO_TCP, TCP_NODELAY,&y, sizeof(int));
140
address.sin_family=AF_INET;
141
address.sin_addr.s_addr=INADDR_ANY;
142
address.sin_port=htons(localPort);
143
if (bind(serverSocket,(struct sockaddr*) &address,sizeof(address))!=0)
145
QString err=tr("Error binding ")+localHost+":"+QString::number(localPort);
146
x2goDebug<<err<<endl;
147
emit sshFinished(false,err,pid);
150
listen(serverSocket,5);
151
addrlen=sizeof(struct sockaddr_in);
152
QTimer* timer=new QTimer();
153
connect(timer,SIGNAL(timeout()),this,SLOT(slotCheckNewConnection()));
155
emit sshTunnelOk(pid);
157
x2goDebug<<"Direct tunnel: waiting for connections on "<<localHost<<":"<<localPort<<endl;
163
void SshProcess::addPuttyReg(QString host, QString uuidStr)
165
QSettings st("HKEY_CURRENT_USER\\Software\\SimonTatham\\PuTTY\\Sessions\\"+uuidStr,
166
QSettings::NativeFormat);
167
st.setValue("HostName", host);
168
st.setValue("GssapiFwd", (uint) 1);
172
void SshProcess::rmPuttyReg(QString uuidStr)
174
QSettings st("HKEY_CURRENT_USER\\Software\\SimonTatham\\PuTTY\\Sessions",
175
QSettings::NativeFormat);
181
void SshProcess::startNormal(const QString& cmd)
183
QUuid uuid = QUuid::createUuid();
184
QString uuidStr = uuid.toString().mid(1, 36).toLower();
188
// ONLY UNCOMMENT FOR TESTING, MIGHT REVEAL PASSWORD WHEN command=RDP
189
x2goDebug<<"executing remote command via SshProcess object ("<<pid<<"): "<<cmd<<endl;
191
if(!masterCon->useKerberos())
193
QString shcmd = "sh -c 'echo X2GODATABEGIN:" + uuidStr + "; PATH=/usr/local/bin:/usr/bin:/bin "+cmd+"; echo X2GODATAEND:" + uuidStr +";'";
194
masterCon->addChannelConnection(this, uuidStr, shcmd);
195
connect(masterCon,SIGNAL(stdOut(SshProcess*,QByteArray)),this,SLOT(slotStdOut(SshProcess*,QByteArray)));
196
connect(masterCon,SIGNAL(channelClosed(SshProcess*,QString)), this,SLOT(slotChannelClosed(SshProcess*,QString)));
200
QString host=masterCon->getHost();
201
QString shcmd = "sh -c 'echo X2GODATABEGIN:" + uuidStr + "; PATH=/usr/local/bin:/usr/bin:/bin "+cmd+"; echo X2GODATAEND:" + uuidStr +";'";
202
proc=new QProcess(this);
204
if(masterCon->get_kerberosDelegation())
206
addPuttyReg(host, uuidStr);
209
QString sshString="plink -batch -P "+
211
QString krbDelegOption=" -k ";
212
if(masterCon->get_kerberosDelegation())
214
krbDelegOption=" -K ";
216
QString sshString=QString::null+"ssh"+ KEEPALIVE_OPTION +krbDelegOption+" -o GSSApiAuthentication=yes -o PasswordAuthentication=no -o PubkeyAuthentication=no -p "+
218
QString::number(masterCon->getPort())+" -l "+
219
masterCon->getUser()+" "+ host + " \""+shcmd+"\"";
221
x2goDebug<<"evoking ssh command via SshProcess object ("<<pid<<"): "<<sshString<<endl;
223
proc->start(sshString);
225
if (!proc->waitForStarted(15000))
227
stdErrString=proc->errorString();
229
//x2goDebug<<"ssh start failed:" <<stdErrString<<endl;
231
slotChannelClosed(this, uuidStr);
234
connect(proc,SIGNAL(finished(int,QProcess::ExitStatus)),this,
235
SLOT(slotSshProcFinished(int,QProcess::ExitStatus)));
236
connect(proc,SIGNAL(readyReadStandardError()),this,SLOT(slotSshProcStdErr()));
237
connect(proc,SIGNAL(readyReadStandardOutput()),this,SLOT(slotSshProcStdOut()));
242
void SshProcess::start_cp(QString src, QString dst)
244
x2goDebug<<"copying file via SshProcess object ("<<pid<<"): "<<src<<" -> "<<dst<<endl;
247
if(!masterCon->useKerberos())
249
connect(masterCon, SIGNAL(copyErr(SshProcess*,QString,QString)), this,
250
SLOT(slotCopyErr(SshProcess*,QString,QString)));
251
connect(masterCon, SIGNAL(copyOk(SshProcess*)), this,SLOT(slotCopyOk(SshProcess*)));
252
masterCon->addCopyRequest(this,src,dst);
256
proc=new QProcess(this);
258
//pscp don't working with paths like "~user"
259
//I hope a home directories of your users are in /home/
260
dst.replace("~"+masterCon->getUser(),"/home/"+masterCon->getUser());
261
dst.replace("~","/home/"+masterCon->getUser());
263
QString sshString="pscp -batch -P "+
265
QString sshString="scp -o GSSApiAuthentication=yes -o PasswordAuthentication=no -o PubkeyAuthentication=no -P "+
267
QString::number(masterCon->getPort())+" "+src+" "+
268
masterCon->getUser()+"@"+ masterCon->getHost()+":"+dst;
270
x2goDebug<<"running scp:" <<sshString<<endl;
272
proc->start(sshString);
274
if (!proc->waitForStarted(15000))
276
stdErrString=proc->errorString();
278
x2goDebug<<"ssh start failed:" <<stdErrString<<endl;
280
slotChannelClosed(this,"");
283
connect(proc,SIGNAL(finished(int,QProcess::ExitStatus)),this,
284
SLOT(slotSshProcFinished(int,QProcess::ExitStatus)));
285
connect(proc,SIGNAL(readyReadStandardError()),this,SLOT(slotSshProcStdErr()));
286
connect(proc,SIGNAL(readyReadStandardOutput()),this,SLOT(slotSshProcStdOut()));
291
void SshProcess::startTunnel(const QString& forwardHost, uint forwardPort, const QString& localHost,
292
uint localPort, bool reverse)
294
x2goDebug<<"Starting tunnel via SshProcess object ("<<pid<<"): "<<forwardHost<<":"<<forwardPort<<" -> "<<localHost<<":"<<localPort<<endl;
297
tunnelOkEmited=false;
298
if(!masterCon->useKerberos())
300
this->forwardHost=forwardHost;
301
this->forwardPort=forwardPort;
302
this->localHost=localHost;
303
this->localPort=localPort;
309
proc=new QProcess(0);
311
QString sshString="plink -batch -P "+
313
QString sshString=QString::null+"ssh"+ KEEPALIVE_OPTION +"-o GSSApiAuthentication=yes -o PasswordAuthentication=no -o PubkeyAuthentication=no -p "+
315
QString::number(masterCon->getPort())+" "+
316
masterCon->getUser()+"@"+
317
masterCon->getHost() + " -N -v ";
319
sshString+=" -L " + QString::number(localPort)+":"+forwardHost+":"+QString::number(forwardPort);
321
sshString+=" -R "+ QString::number(forwardPort)+":"+forwardHost+":"+QString::number(localPort);
324
x2goDebug<<"TUNNEL: running ssh:" <<sshString<<endl;
326
proc->start(sshString);
328
if (!proc->waitForStarted(5000))
330
stdErrString=proc->errorString();
332
x2goDebug<<"ssh start failed:" <<stdErrString<<endl;
334
slotChannelClosed(this,"");
337
connect(proc,SIGNAL(finished(int,QProcess::ExitStatus)),this,
338
SLOT(slotSshProcFinished(int,QProcess::ExitStatus)));
339
connect(proc,SIGNAL(readyReadStandardError()),this,SLOT(slotSshProcStdErr()));
340
connect(proc,SIGNAL(readyReadStandardOutput()),this,SLOT(slotSshProcStdOut()));
344
void SshProcess::slotStdErr(SshProcess* creator, QByteArray data)
349
// x2goDebug<<"new err data:"<<data<<endl;
353
if(tunnel && !tunnelOkEmited)
356
if(stdErrString.indexOf("Access granted")!=-1)
358
if(stdErrString.indexOf("Entering interactive session")!=-1)
363
x2goDebug<<"Tunnel OK"<<endl;
365
emit sshTunnelOk(pid);
370
void SshProcess::slotStdOut(SshProcess* creator, QByteArray data)
374
// x2goDebug<<"new data"<<data<<endl;
378
void SshProcess::slotIOerr(SshProcess* creator, QString message, QString sshSessionErr)
382
if (sshSessionErr.length())
383
sshSessionErr = " - "+sshSessionErr;
385
x2goDebug<<"I/O error: "<<message<<sshSessionErr<<" ("<<pid<<")."<<endl;
388
abortString="I/O error: "+message+sshSessionErr;
391
void SshProcess::slotCopyErr(SshProcess* creator, QString message, QString sshSessionErr)
395
emit sshFinished(false, message+" - "+sshSessionErr, pid);
398
void SshProcess::slotCopyOk(SshProcess* creator)
402
emit sshFinished(true,"", pid);
405
void SshProcess::slotReverseTunnelOk(SshProcess* creator)
408
emit sshTunnelOk(pid);
411
void SshProcess::slotReverseTunnelFailed(SshProcess* creator, QString error)
414
emit sshFinished(false,error,pid);
419
void SshProcess::slotChannelClosed(SshProcess* creator, QString uuid)
427
if (output.length()<5)
434
QString begin_marker = "X2GODATABEGIN:"+uuid+"\n";
435
QString end_marker = "X2GODATAEND:"+uuid+"\n";
436
int output_begin=stdOutString.indexOf(begin_marker) + begin_marker.length();
437
int output_end=stdOutString.indexOf(end_marker);
438
output = stdOutString.mid(output_begin, output_end-output_begin);
439
if ( output.length()<=0 && stdErrString.length() >0 )
444
x2goDebug<<"have only stderr, something must be wrong"<<endl;
449
x2goDebug<<"ssh finished: "<<normalExited<<" - "<<output<<" ("<<pid<<")."<<endl;
451
emit sshFinished(normalExited, output, pid);
454
void SshProcess::slotSshProcFinished(int exitCode, QProcess::ExitStatus exitStatus)
457
if (exitCode==0 && exitStatus==QProcess::NormalExit)
460
x2goDebug<<"ssh process exit code :"<<exitStatus;
463
if(masterCon->useKerberos())
465
rmPuttyReg(procUuid);
468
slotChannelClosed(this,procUuid);
471
void SshProcess::slotSshProcStdErr()
473
slotStdErr(this, proc->readAllStandardError());
476
void SshProcess::slotSshProcStdOut()
478
slotStdOut(this, proc->readAllStandardOutput());