#include "rsmcore/sessionworker.hh" #include "radixbug/bug.hh" #include #include #include #include #include namespace rsm { void assert_ssh_session(ssh_session session, const std::string& error_message) { if (session == nullptr) { throw std::runtime_error(error_message.c_str()); } } class SessionWorkerImpl { public: ssh_session session = nullptr; QByteArray output_buffer; SessionWorkerImpl() { session = ssh_new(); assert_ssh_session(session, "Failed to allocate new ssh session."); } ~SessionWorkerImpl() { if (session != nullptr) { ssh_free(session); ssh_finalize(); } } }; 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; } void SessionWorker::setHost(QString host) { assert_ssh_session(p->session, "setHost() -- Session is not allocated."); ssh_options_set(p->session, SSH_OPTIONS_HOST, host.toStdString().c_str()); } void SessionWorker::setLogVerbosity(Verbosity level) { assert_ssh_session(p->session, "setLogVerbosity() -- Session is not allocated."); int verbosity = static_cast(level); ssh_options_set(p->session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); } void SessionWorker::setPort(int port) { assert_ssh_session(p->session, "setPort() -- Session is not allocated."); ssh_options_set(p->session, SSH_OPTIONS_PORT, &port); } void SessionWorker::setUser(QString name) { assert_ssh_session(p->session, "setName() -- Session is not allocated."); ssh_options_set(p->session, SSH_OPTIONS_USER, name.toStdString().c_str()); } void SessionWorker::connect() { assert_ssh_session(p->session, "connect() -- Session is not allocated."); if (ssh_is_connected(p->session) == 0) { // attempt a connection int rc = ssh_connect(p->session); if (rc != SSH_OK) { char* message; int rv = ssh_options_get(p->session, SSH_OPTIONS_HOST, &message); if (rv != SSH_OK) throw std::runtime_error("Error retrieving host."); std::ostringstream os; os << "Error connecting to " << message << ":" << ssh_get_error(p->session); delete message; throw std::runtime_error(os.str().c_str()); } } } void SessionWorker::disconnect() { assert_ssh_session(p->session, "disconnect() -- Session is not allocated."); if (ssh_is_connected(p->session) != 0) { radix_tagged_line("Disconnecting session."); ssh_disconnect(p->session); } } void SessionWorker::verifyKnownHost() { assert_ssh_session(p->session, "verifyKnownHost() -- Session is not allocated."); enum ssh_known_hosts_e state; unsigned char* hash = nullptr; ssh_key server_public_key = nullptr; char* hexa; size_t hlen; QString qhexa; int rc = ssh_get_server_publickey(p->session, &server_public_key); if (rc < 0) { emit getServerPublicKeyFailed(); return; } rc = ssh_get_publickey_hash(server_public_key, SSH_PUBLICKEY_HASH_SHA1, &hash, &hlen); ssh_key_free(server_public_key); if (rc < 0) { emit getServerPublicKeyFailed(); return; } state = ssh_session_is_known_server(p->session); switch (state) { case SSH_KNOWN_HOSTS_OK: /* OK */ break; case SSH_KNOWN_HOSTS_CHANGED: hexa = ssh_get_hexa(hash, hlen); qhexa = hexa; ssh_clean_pubkey_hash(&hash); radix_tagged_line("SSH_KNOWN_HOSTS_CHANGED: " << qhexa.toStdString()); emit hostPublicKeyChanged(qhexa); break; case SSH_KNOWN_HOSTS_OTHER: ssh_clean_pubkey_hash(&hash); radix_tagged_line("SSH_KNOWN_HOSTS_OTHER"); emit hostPublicKeyUnavailable(); break; case SSH_KNOWN_HOSTS_NOT_FOUND: /* FALL THROUGH to SSH_SERVER_NOT_KNOWN behavior */ radix_tagged_line("SSH_KNOWN_HOSTS_NOT_FOUND"); case SSH_KNOWN_HOSTS_UNKNOWN: hexa = ssh_get_hexa(hash, hlen); qhexa = hexa; ssh_string_free_char(hexa); ssh_clean_pubkey_hash(&hash); radix_tagged_line("SSH_KNOWN_HOSTS_UNKNOWN: " << qhexa.toStdString()); emit hostUnknown(qhexa); break; case SSH_KNOWN_HOSTS_ERROR: QString message = ssh_get_error(p->session); radix_tagged_line("SSH_KNOWN_HOSTS_ERROR: " << message.toStdString()); ssh_clean_pubkey_hash(&hash); emit knownHostError(message); break; } ssh_clean_pubkey_hash(&hash); } void SessionWorker::acceptHostPublicKeyUpdate() { assert_ssh_session( p->session, "acceptHostPublicKeyUpdate() -- Session is not allocated."); int rc = ssh_session_update_known_hosts(p->session); if (rc != SSH_OK) { emit updateKnownHostsFailed(); } } void SessionWorker::authenticate() { assert_ssh_session(p->session, "authenticate() -- Session is not allocated."); // Try authenticating with no credentials - rarely works int rc = ssh_userauth_none(p->session, nullptr); if (rc == SSH_AUTH_ERROR) { QString message = ssh_get_error(p->session); emit authenticationError(message); return; } // get acceptable methods int method = ssh_userauth_list(p->session, nullptr); // Try to authenticate with public key first if (method & SSH_AUTH_METHOD_PUBLICKEY) { rc = ssh_userauth_publickey_auto(p->session, nullptr, nullptr); if (rc == SSH_AUTH_ERROR) { QString message = ssh_get_error(p->session); emit authenticationError(message); return; } else if (rc == SSH_AUTH_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 password if (method & SSH_AUTH_METHOD_PASSWORD) { radix_tagged_line("SSH_AUTH_METHOD_PASSWORD required."); emit passwordRequested(); return; } } void SessionWorker::authenticateWithPassword(QString pswd) { assert_ssh_session(p->session, "authenticateWithPassword() -- Session is not allocated."); int rc = ssh_userauth_password(p->session, nullptr, pswd.toStdString().c_str()); if (rc == SSH_AUTH_ERROR) { QString message = ssh_get_error(p->session); emit authenticationError(message); } else if (rc == SSH_AUTH_SUCCESS) { radix_tagged_line("SSH_AUTH_METHOD_PASSWORD succeeded."); emit authenticationSucceeded(); char* banner = ssh_get_issue_banner(p->session); if (banner) { QString qbanner = banner; emit loginBannerIssued(qbanner); delete banner; } } } void SessionWorker::requestExec(QString command) { assert_ssh_session(p->session, "request_exec() -- Session is not allocated."); radix_tagged_line("requestExec(" << command.toStdString() << ")"); char buffer[256]; // clear any previous buffer p->output_buffer.clear(); ssh_channel channel = ssh_channel_new(p->session); if (channel == nullptr) { radix_tagged_line("Failed to create new channel"); emit execFailed("Failed to allocate a channel on the open session."); return; } int rc = ssh_channel_open_session(channel); if (rc < 0) { radix_tagged_line("Failed to create new channel"); emit execFailed("Failed to open a channel on the session."); return; } rc = ssh_channel_request_exec(channel, command.toStdString().c_str()); if (rc < 0) { radix_tagged_line("Failed to request exec."); emit execFailed("Failed to execute remote command."); ssh_channel_close(channel); ssh_channel_free(channel); return; } int nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); while (nbytes > 0) { p->output_buffer.append(buffer, nbytes); emit execOutputReady(); nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); } radix_tagged_line("nbytes=" << nbytes); radix_tagged_line("Finished reading response\n" << p->output_buffer.data()); if (nbytes == 0) { ssh_channel_send_eof(channel); } if (nbytes < 0) { emit execFailed("Failed to read response from remote command."); } ssh_channel_close(channel); ssh_channel_free(channel); radix_tagged_line("'" << command.toStdString() << "' finished."); emit execFinished(); } } // namespace rsm