sessionworker.cc 7.66 KB
Newer Older
1
2
3
4
5
6

#include "rsmcore/sessionworker.hh"
#include "radixbug/bug.hh"

#include <cassert>
#include <sstream>
7
8
9
10

#include <QByteArray>
#include <QString>

11
12
13
14
namespace rsm
{
class SessionWorkerImpl
{
15
16
17
 private:
  // privatize copy of this object to prevent their use.
  // would be nice to use c++14 '= delete' but c'est la vie.
18
19
  SessionWorkerImpl(const SessionWorkerImpl& orig) { void(sizeof(orig)); }
  void operator=(const SessionWorkerImpl& orig) { void(sizeof(orig)); }
20

21
 public:
22
  Session* session = nullptr;
23
  QByteArray output_buffer;
24
  SessionWorkerImpl() { session = new Session(); }
25
26
27
28
  ~SessionWorkerImpl()
  {
    if (session != nullptr)
    {
29
      delete session;
30
31
    }
  }
32
};  // class SessionWorkerImpl
33

34
35
void SessionWorker::processPrompts()
{
36
37
  QString instruction = p->session->keyboardInteractiveInstruction();
  QString name        = p->session->keyboardInteractiveName();
38
  // build list of prompts
39
  QStringList prompts = p->session->keyboardInteractivePrompts();
40
41
42
  emit interactiveAuthenticationRequested(instruction, name, prompts);
}

43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
SessionWorker::SessionWorker(QObject* parent)
    : QObject(parent)
{
  p = new SessionWorkerImpl();
}

SessionWorker::SessionWorker(QString host, QObject* parent)
    : QObject(parent)
{
  p = new SessionWorkerImpl();
  setHost(host);
}

SessionWorker::~SessionWorker()
{
  if (p != nullptr) delete p;
}

61
62
QByteArray SessionWorker::readExecOutput() { return p->output_buffer; }

63
64
Session* SessionWorker::session() { return p->session; }

65
void SessionWorker::setHost(QString host) { p->session->setHost(host); }
66
void SessionWorker::setLogVerbosity(SessionVerbosity level)
67
{
68
  p->session->setLogVerbosity(level);
69
}
70
71
void SessionWorker::setPort(int port) { p->session->setPort(port); }
void SessionWorker::setUser(QString name) { p->session->setUser(name); }
72
73
74

void SessionWorker::setProxyCommand(QString command)
{
75
  p->session->setProxyCommand(command);
76
}
77
78
void SessionWorker::connect()
{
79
  radix_tagged_line("connect()");
80
81
82
  if (p->session->isConnected()) p->session->disconnect();
  bool rc = p->session->connect();
  if (!rc)
83
  {
84
85
86
87
88
89
90
    // get error message
    QString host    = p->session->host();
    QString error   = p->session->getError();
    QString message = "Error connecting to ";
    message.append(host).append(": ").append(error);
    emit connectionFailed(message);
    return;
91
  }
92
  emit connectionSuccessful();
93
94
95
}
void SessionWorker::disconnect()
{
96
  radix_tagged_line("disconnect()");
97
  if (p->session->isConnected())
98
99
  {
    radix_tagged_line("Disconnecting session.");
100
    p->session->disconnect();
101
    emit disconnectSuccessful();
102
103
104
105
106
  }
}

void SessionWorker::verifyKnownHost()
{
107
  SessionHostState state = p->session->verifyKnownHost();
108
109
110
111
  QString qhexa;

  switch (state)
  {
112
    case SessionHostState::OK:
113
114
115
      /* OK */

      break;
116
117
118
    case SessionHostState::CHANGED:

      qhexa = p->session->getHexa();
119
120
121
      radix_tagged_line("SSH_KNOWN_HOSTS_CHANGED: " << qhexa.toStdString());
      emit hostPublicKeyChanged(qhexa);
      break;
122
    case SessionHostState::OTHER:
123
124
125
      radix_tagged_line("SSH_KNOWN_HOSTS_OTHER");
      emit hostPublicKeyUnavailable();
      break;
126
    case SessionHostState::NOT_FOUND:
127
128
129
      /* FALL THROUGH to SSH_SERVER_NOT_KNOWN behavior */
      radix_tagged_line("SSH_KNOWN_HOSTS_NOT_FOUND");

130
131
    case SessionHostState::UNKNOWN:
      qhexa = p->session->getHexa();
132
133
134
      radix_tagged_line("SSH_KNOWN_HOSTS_UNKNOWN: " << qhexa.toStdString());
      emit hostUnknown(qhexa);
      break;
135
    case SessionHostState::ERROR_STATE:
136
      QString message = p->session->getError();
137
138
139
140
      radix_tagged_line("SSH_KNOWN_HOSTS_ERROR: " << message.toStdString());
      emit knownHostError(message);
      break;
  }
141
  emit verifyKnownHostSuccessful();
142
143
144
145
}

void SessionWorker::acceptHostPublicKeyUpdate()
{
146
  radix_tagged_line("authenticateHostPublicKeyUpdate()");
147
148
149
  bool rc = p->session->updateKnownHosts();

  if (!rc)
150
151
152
153
154
155
156
  {
    emit updateKnownHostsFailed();
  }
}

void SessionWorker::authenticate()
{
157
  radix_tagged_line("Authenticate.");
158
159

  // get acceptable methods
160
  int method = p->session->authenticationMethodList();
161
162

  // Try to authenticate with public key first
163
  if (method & SessionAuthMethodPUBLICKEY)
164
  {
165
    SessionAuthState state = p->session->authenticateWithPublicKey();
166
    if (state == SessionAuthState::ERROR_STATE)
167
    {
168
      QString message = p->session->getError();
169
170
171
      emit authenticationError(message);
      return;
    }
172
    else if (state == SessionAuthState::SUCCESS)
173
174
175
176
177
178
179
    {
      radix_tagged_line("SSH_AUTH_METHOD_PUBLICKEY succeeded.");
      emit authenticationSucceeded();
      return;
    }
    radix_tagged_line("SSH_AUTH_METHOD_PUBLICKEY didn't work.");
  }  // public key authentication
180
181

  // Try to authenticate with keyboard interactive";
182
  if (method & SessionAuthMethodINTERACTIVE)
183
  {
184
185
    SessionAuthState state = p->session->authenticateInteractively();
    while (state == SessionAuthState::INFO)
186
    {
187
      processPrompts();
188
189
190
191
      return;
    }
  }

192
  // Try to authenticate with password
193
  if (method & SessionAuthMethodPASSWORD)
194
195
196
197
198
  {
    radix_tagged_line("SSH_AUTH_METHOD_PASSWORD required.");
    emit passwordRequested();
    return;
  }
199
200
201
202
203
  if (method & SessionAuthMethodUNKNOWN)
  {
    radix_tagged_line("SSH_AUTH_METHOD_UNKNOWN.");
    emit authenticationError(p->session->getError());
  }
204
205
206
207
}

void SessionWorker::authenticateWithPassword(QString pswd)
{
208
  radix_tagged_line("Authenticate with password.");
209
  SessionAuthState state = p->session->authenticateWithPassword(pswd);
210
211
  if (state == SessionAuthState::ERROR_STATE ||
      state == SessionAuthState::DENIED)
212
  {
213
    QString message = p->session->getError();
214
215
    emit authenticationError(message);
  }
216
  else if (state == SessionAuthState::SUCCESS)
217
218
219
  {
    radix_tagged_line("SSH_AUTH_METHOD_PASSWORD succeeded.");
    emit authenticationSucceeded();
220
221
    QString banner = p->session->loginIssueBanner();
    if (!banner.isEmpty())
222
    {
223
      emit loginBannerIssued(banner);
224
225
226
227
    }
  }
}

228
229
void SessionWorker::authenticatePrompts(QStringList responses)
{
230
  radix_tagged_line("authenticatePrompts()");
231
232
  SessionAuthState state = p->session->authenticatePrompts(responses);
  if (state == SessionAuthState::DENIED)
233
234
  {
    emit authenticationError("Authentication denied.");
235
    p->session->disconnect();
236
237
    return;
  }
238
  if (state == SessionAuthState::INFO)
239
240
241
242
  {
    processPrompts();
    return;
  }
243
  if (state == SessionAuthState::SUCCESS)
244
245
246
247
248
249
  {
    emit authenticationSucceeded();
    return;
  }
}

250
251
void SessionWorker::requestExec(QString command)
{
252
  radix_tagged_line("requestExec(" << command.toStdString() << ")");
253
  Channel* channel = p->session->newChannel();
254
255
  // clear any previous buffer
  p->output_buffer.clear();
256
  bool rc = channel->open();
257
  if (!rc)
258
  {
259
    radix_tagged_line("Failed to open channel");
260
    emit execFailed("Failed to open a channel on the session.");
Norby, Tom's avatar
Norby, Tom committed
261
    delete channel;
262
263
264
    return;
  }

265
  rc = channel->exec(command);
266
  if (!rc)
267
  {
268
269
    radix_tagged_line("Failed to request exec.");
    emit execFailed("Failed to execute remote command.");
270
    channel->close();
Norby, Tom's avatar
Norby, Tom committed
271
    delete channel;
272
273
    return;
  }
274

275
  // TODO: should we use timeout or non-blocking version of this call?
276
  // read stdout
277
  QString bytes = channel->readExecOut();
278
  while (!bytes.isEmpty())
279
  {
280
    p->output_buffer.append(bytes);
281
    bytes = channel->readExecOut();
282
  }
283

284
  // read stderr
285
  bytes = channel->readExecErr();
286
  while (!bytes.isEmpty())
287
  {
288
    p->output_buffer.append(bytes);
289
    bytes = channel->readExecErr();
290
  }
291
  emit execOutputReady();
292
  radix_tagged_line("nbytes=" << p->output_buffer.size());
293
  radix_tagged_line("Finished reading response\n" << p->output_buffer.data());
294
295
  channel->close();
  delete channel;
296
297
  radix_tagged_line("'" << command.toStdString() << "' finished.");
  emit execFinished();
298
299
}

300
}  // namespace rsm