sessionworker.cc 7.62 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.");
261 262 263
    return;
  }

264
  rc = channel->exec(command);
265
  if (!rc)
266
  {
267 268
    radix_tagged_line("Failed to request exec.");
    emit execFailed("Failed to execute remote command.");
269
    channel->close();
270 271
    return;
  }
272

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

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

298
}  // namespace rsm