#include "rsmcore/sessionworker.hh" #include "radixbug/bug.hh" #include #include #include #include namespace rsm { class SessionWorkerImpl { private: // privatize copy of this object to prevent their use. // would be nice to use c++14 '= delete' but c'est la vie. SessionWorkerImpl(const SessionWorkerImpl& orig) { void(sizeof(orig)); } void operator=(const SessionWorkerImpl& orig) { void(sizeof(orig)); } public: Session* session = nullptr; QByteArray output_buffer; SessionWorkerImpl() { session = new Session(); } ~SessionWorkerImpl() { if (session != nullptr) { delete session; } } }; // class SessionWorkerImpl void SessionWorker::processPrompts() { QString instruction = p->session->keyboardInteractiveInstruction(); QString name = p->session->keyboardInteractiveName(); // build list of prompts QStringList prompts = p->session->keyboardInteractivePrompts(); emit interactiveAuthenticationRequested(instruction, name, prompts); } 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; } QByteArray SessionWorker::readExecOutput() { return p->output_buffer; } Session* SessionWorker::session() { return p->session; } void SessionWorker::setHost(QString host) { p->session->setHost(host); } void SessionWorker::setLogVerbosity(SessionVerbosity level) { p->session->setLogVerbosity(level); } void SessionWorker::setPort(int port) { p->session->setPort(port); } void SessionWorker::setUser(QString name) { p->session->setUser(name); } void SessionWorker::setProxyCommand(QString command) { p->session->setProxyCommand(command); } void SessionWorker::connect() { radix_tagged_line("connect()"); if (p->session->isConnected()) p->session->disconnect(); bool rc = p->session->connect(); if (!rc) { // 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; } emit connectionSuccessful(); } void SessionWorker::disconnect() { radix_tagged_line("disconnect()"); if (p->session->isConnected()) { radix_tagged_line("Disconnecting session."); p->session->disconnect(); emit disconnectSuccessful(); } } void SessionWorker::verifyKnownHost() { SessionHostState state = p->session->verifyKnownHost(); QString qhexa; switch (state) { case SessionHostState::OK: /* OK */ break; case SessionHostState::CHANGED: qhexa = p->session->getHexa(); radix_tagged_line("SSH_KNOWN_HOSTS_CHANGED: " << qhexa.toStdString()); emit hostPublicKeyChanged(qhexa); break; case SessionHostState::OTHER: radix_tagged_line("SSH_KNOWN_HOSTS_OTHER"); emit hostPublicKeyUnavailable(); break; case SessionHostState::NOT_FOUND: /* FALL THROUGH to SSH_SERVER_NOT_KNOWN behavior */ radix_tagged_line("SSH_KNOWN_HOSTS_NOT_FOUND"); case SessionHostState::UNKNOWN: qhexa = p->session->getHexa(); radix_tagged_line("SSH_KNOWN_HOSTS_UNKNOWN: " << qhexa.toStdString()); emit hostUnknown(qhexa); break; case SessionHostState::ERROR_STATE: QString message = p->session->getError(); radix_tagged_line("SSH_KNOWN_HOSTS_ERROR: " << message.toStdString()); emit knownHostError(message); break; } emit verifyKnownHostSuccessful(); } void SessionWorker::acceptHostPublicKeyUpdate() { radix_tagged_line("authenticateHostPublicKeyUpdate()"); bool rc = p->session->updateKnownHosts(); if (!rc) { emit updateKnownHostsFailed(); } } void SessionWorker::authenticate() { radix_tagged_line("Authenticate."); // get acceptable methods int method = p->session->authenticationMethodList(); // Try to authenticate with public key first if (method & SessionAuthMethodPUBLICKEY) { SessionAuthState state = p->session->authenticateWithPublicKey(); if (state == SessionAuthState::ERROR_STATE) { QString message = p->session->getError(); emit authenticationError(message); return; } else if (state == SessionAuthState::SUCCESS) { radix_tagged_line("SSH_AUTH_METHOD_PUBLICKEY succeeded."); emit authenticationSucceeded(); return; } radix_tagged_line("SSH_AUTH_METHOD_PUBLICKEY didn't work."); } // public key authentication // Try to authenticate with keyboard interactive"; if (method & SessionAuthMethodINTERACTIVE) { SessionAuthState state = p->session->authenticateInteractively(); while (state == SessionAuthState::INFO) { processPrompts(); return; } } // Try to authenticate with password if (method & SessionAuthMethodPASSWORD) { radix_tagged_line("SSH_AUTH_METHOD_PASSWORD required."); emit passwordRequested(); return; } if (method & SessionAuthMethodUNKNOWN) { radix_tagged_line("SSH_AUTH_METHOD_UNKNOWN."); emit authenticationError(p->session->getError()); } } void SessionWorker::authenticateWithPassword(QString pswd) { radix_tagged_line("Authenticate with password."); SessionAuthState state = p->session->authenticateWithPassword(pswd); if (state == SessionAuthState::ERROR_STATE || state == SessionAuthState::DENIED) { QString message = p->session->getError(); emit authenticationError(message); } else if (state == SessionAuthState::SUCCESS) { radix_tagged_line("SSH_AUTH_METHOD_PASSWORD succeeded."); emit authenticationSucceeded(); QString banner = p->session->loginIssueBanner(); if (!banner.isEmpty()) { emit loginBannerIssued(banner); } } } void SessionWorker::authenticatePrompts(QStringList responses) { radix_tagged_line("authenticatePrompts()"); SessionAuthState state = p->session->authenticatePrompts(responses); if (state == SessionAuthState::DENIED) { emit authenticationError("Authentication denied."); p->session->disconnect(); return; } if (state == SessionAuthState::INFO) { processPrompts(); return; } if (state == SessionAuthState::SUCCESS) { emit authenticationSucceeded(); return; } } void SessionWorker::requestExec(QString command) { radix_tagged_line("requestExec(" << command.toStdString() << ")"); Channel* channel = p->session->newChannel(); // clear any previous buffer p->output_buffer.clear(); bool rc = channel->open(); if (!rc) { radix_tagged_line("Failed to open channel"); emit execFailed("Failed to open a channel on the session."); delete channel; return; } rc = channel->exec(command); if (!rc) { radix_tagged_line("Failed to request exec."); emit execFailed("Failed to execute remote command."); channel->close(); delete channel; return; } // TODO: should we use timeout or non-blocking version of this call? // read stdout QString bytes = channel->readExecOut(); while (!bytes.isEmpty()) { p->output_buffer.append(bytes); bytes = channel->readExecOut(); } // read stderr bytes = channel->readExecErr(); while (!bytes.isEmpty()) { p->output_buffer.append(bytes); bytes = channel->readExecErr(); } emit execOutputReady(); radix_tagged_line("nbytes=" << p->output_buffer.size()); radix_tagged_line("Finished reading response\n" << p->output_buffer.data()); channel->close(); delete channel; radix_tagged_line("'" << command.toStdString() << "' finished."); emit execFinished(); } } // namespace rsm