Commit 3ba6bb1b authored by Lefebvre, Jordan's avatar Lefebvre, Jordan
Browse files

#1. Finished separating sessionworker from session and channel.

parent 997d1cc0
Pipeline #98522 failed with stages
in 38 minutes and 38 seconds
......@@ -12,18 +12,18 @@
namespace rsm
{
void assert_ssh_session(ssh_session session, const std::string& error_message)
void assert_ssh_session(ssh_session session, const char* error_message)
{
if (session == nullptr)
{
throw std::runtime_error(error_message.c_str());
throw std::runtime_error(error_message);
}
}
void assert_ssh_channel(ssh_channel channel, const std::string& error_message)
void assert_ssh_channel(ssh_channel channel, const char* error_message)
{
if (channel == nullptr)
{
throw std::runtime_error(error_message.c_str());
throw std::runtime_error(error_message);
}
}
......@@ -93,26 +93,26 @@ QString Channel::readExecOut()
assert_ssh_channel(p->channel, "readExecOut() -- Channel is not allocated.");
char buffer[256];
int nbytes = ssh_channel_read(p->channel, buffer, sizeof(buffer), 0);
QByteArray output_buffer;
while (nbytes > 0)
if (nbytes > 0)
{
output_buffer.append(buffer, nbytes);
nbytes = ssh_channel_read(p->channel, buffer, sizeof(buffer), 0);
buffer[nbytes] = '\0';
QString result = buffer;
return result;
}
return output_buffer;
return QString("");
}
QString Channel::readExecErr()
{
assert_ssh_channel(p->channel, "readExecErr() -- Channel is not allocated.");
char buffer[256];
int nbytes = ssh_channel_read(p->channel, buffer, sizeof(buffer), 1);
QByteArray output_buffer;
while (nbytes > 0)
if (nbytes > 0)
{
output_buffer.append(buffer, nbytes);
nbytes = ssh_channel_read(p->channel, buffer, sizeof(buffer), 1);
buffer[nbytes] = '\0';
QString result = buffer;
return result;
}
return output_buffer;
return QString("");
}
class SessionImpl
......@@ -153,7 +153,7 @@ void Session::setHost(QString host)
ssh_options_set(p->session, SSH_OPTIONS_HOST, host.toStdString().c_str());
}
std::string Session::host() const
QString Session::host() const
{
// This may not be necessary
assert_ssh_session(p->session, "host() -- Session is not allocated.");
......@@ -161,13 +161,13 @@ std::string Session::host() const
int rv = ssh_options_get(p->session, SSH_OPTIONS_HOST, &host_name);
if (rv == SSH_OK)
{
std::string host = host_name;
QString host = host_name;
delete host_name;
return host;
}
else
{
return std::string("");
return QString("");
}
}
void Session::setLogVerbosity(SessionVerbosity level)
......@@ -188,7 +188,7 @@ void Session::setUser(QString name)
ssh_options_set(p->session, SSH_OPTIONS_USER, name.toStdString().c_str());
}
std::string Session::user() const
QString Session::user() const
{
// This may not be necessary
assert_ssh_session(p->session, "user() -- Session is not allocated.");
......@@ -196,13 +196,13 @@ std::string Session::user() const
int rv = ssh_options_get(p->session, SSH_OPTIONS_USER, &user_name);
if (rv == SSH_OK)
{
std::string user = user_name;
QString user = user_name;
delete user_name;
return user;
}
else
{
return std::string("");
return QString("");
}
}
......@@ -217,7 +217,7 @@ bool Session::connect()
{
assert_ssh_session(p->session, "connect() -- Session is not allocated.");
radix_tagged_line("connect()");
if (ssh_is_connected(p->session) == 1)
if (isConnected())
{
radix_tagged_line("session already connected. disconnectinng.");
ssh_disconnect(p->session);
......@@ -233,10 +233,10 @@ bool Session::connect()
bool Session::isConnected() const
{
assert_ssh_session(p->session, "isConnected() -- Session is not allocated.");
return (ssh_is_connected(p->session) != 0);
return (ssh_is_connected(p->session) == 1);
}
std::string Session::getError() const
QString Session::getError() const
{
assert_ssh_session(p->session, "getError() -- Session is not allocated.");
return ssh_get_error(p->session);
......@@ -332,6 +332,12 @@ int Session::authenticationMethodList() const
assert_ssh_session(p->session,
"authenticationMethodList() -- Session is not allocated.");
// required method to be called before auth list is called
int rc = ssh_userauth_none(p->session, nullptr);
if (rc == SSH_AUTH_ERROR)
{
return SSH_AUTH_METHOD_UNKNOWN;
}
// get acceptable methods
int method = ssh_userauth_list(p->session, nullptr);
return method;
......@@ -339,10 +345,20 @@ int Session::authenticationMethodList() const
SessionAuthState Session::authenticateWithPublicKey()
{
assert_ssh_session(
p->session, "authenticateWithPublicKey() -- Session is not allocated.");
int rc = ssh_userauth_publickey_auto(p->session, nullptr, nullptr);
return static_cast<SessionAuthState>(rc);
}
SessionAuthState Session::authenticateInteractively()
{
assert_ssh_session(
p->session, "authenticateInteractively() -- Session is not allocated.");
int rc = ssh_userauth_kbdint(p->session, nullptr, nullptr);
return static_cast<SessionAuthState>(rc);
}
QString Session::keyboardInteractiveName() const
{
assert_ssh_session(p->session,
......
......@@ -18,16 +18,13 @@ enum class SessionAuthState
ERROR = -1
};
enum class SessionAuthMethod
{
UNKNOWN = 0x0000u,
NONE = 0x0001u,
PASSWORD = 0x0002u,
PUBLICKEY = 0x0004u,
HOSTBASED = 0x0008u,
INTERACTIVE = 0x0010u,
GSSAPI_MIC = 0x0020u,
};
#define SessionAuthMethodUNKNOWN 0x0000u
#define SessionAuthMethodNONE 0x0001u
#define SessionAuthMethodPASSWORD 0x0002u
#define SessionAuthMethodPUBLICKEY 0x0004u
#define SessionAuthMethodHOSTBASED 0x0008u
#define SessionAuthMethodINTERACTIVE 0x0010u
#define SessionAuthMethodGSSAPI_MIC 0x0020u
/**
* @brief The SessionHostState enum
......@@ -148,7 +145,7 @@ class RSM_PUBLIC Session
/**
* @brief host Get the host name of the session
*/
std::string host() const;
QString host() const;
/**
* Set the Log verbosity
*/
......@@ -167,7 +164,7 @@ class RSM_PUBLIC Session
* @brief user get the username
* @return
*/
std::string user() const;
QString user() const;
/**
* Set the proxy command
......@@ -189,7 +186,7 @@ class RSM_PUBLIC Session
* @brief getError Retrieve error message
* @return
*/
std::string getError() const;
QString getError() const;
/**
* Disconnect ssh session
*/
......
#include "rsmcore/sessionworker.hh"
#include "radixbug/bug.hh"
#include <libssh/libssh.h>
#include "rsmcore/session.hh"
#include <cassert>
#include <sstream>
......@@ -12,50 +11,33 @@
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
{
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:
ssh_session session = nullptr;
Session* session = nullptr;
QByteArray output_buffer;
SessionWorkerImpl()
{
session = ssh_new();
assert_ssh_session(session, "Failed to allocate new ssh session.");
}
SessionWorkerImpl() { session = new Session(); }
~SessionWorkerImpl()
{
if (session != nullptr)
{
ssh_free(session);
ssh_finalize();
delete session;
}
}
};
}; // class SessionWorkerImpl
void SessionWorker::processPrompts()
{
QString instruction = ssh_userauth_kbdint_getname(p->session);
QString name = ssh_userauth_kbdint_getinstruction(p->session);
int num_prompts = ssh_userauth_kbdint_getnprompts(p->session);
QString instruction = p->session->keyboardInteractiveInstruction();
QString name = p->session->keyboardInteractiveName();
// build list of prompts
QStringList prompts;
char echo;
for (int i = 0; i < num_prompts; ++i)
{
QString prompt = ssh_userauth_kbdint_getprompt(
p->session, static_cast<unsigned int>(i), &echo);
radix_tagged_line(i << ". " << prompt.toStdString());
if (prompt.isEmpty()) break;
prompts << prompt;
}
QStringList prompts = p->session->keyboardInteractivePrompts();
emit interactiveAuthenticationRequested(instruction, name, prompts);
}
......@@ -79,162 +61,91 @@ SessionWorker::~SessionWorker()
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::setHost(QString host) { p->session->setHost(host); }
void SessionWorker::setLogVerbosity(SessionVerbosity level)
{
assert_ssh_session(p->session,
"setLogVerbosity() -- Session is not allocated.");
int verbosity = static_cast<int>(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());
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)
{
assert_ssh_session(p->session,
"setProxyCommand() -- Session is not allocated.");
ssh_options_set(p->session, SSH_OPTIONS_PROXYCOMMAND,
command.toStdString().c_str());
p->session->setProxyCommand(command);
}
void SessionWorker::connect()
{
assert_ssh_session(p->session, "connect() -- Session is not allocated.");
radix_tagged_line("connect()");
if (ssh_is_connected(p->session) == 1)
{
radix_tagged_line("session already connected. disconnectinng.");
ssh_disconnect(p->session);
}
if (p->session->isConnected()) p->session->disconnect();
bool rc = p->session->connect();
if (!rc)
{
radix_tagged_line("Attempting connection.");
// 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);
std::ostringstream os;
if (rv == SSH_OK) // if the error message retrieval was good
{
os << "Error connecting to " << message << ":";
delete message;
}
os << ssh_get_error(p->session);
emit connectionFailed(os.str().c_str());
return;
}
// 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()
{
assert_ssh_session(p->session, "disconnect() -- Session is not allocated.");
radix_tagged_line("disconnect()");
if (ssh_is_connected(p->session) != 0)
if (p->session->isConnected())
{
radix_tagged_line("Disconnecting session.");
ssh_disconnect(p->session);
// copy options in preparation for deletion
ssh_session new_session = ssh_new();
ssh_options_copy(p->session, &new_session);
ssh_free(p->session);
p->session = new_session;
p->session->disconnect();
emit disconnectSuccessful();
}
}
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;
SessionHostState state = p->session->verifyKnownHost();
QString qhexa;
radix_tagged_line("verifyKnownHost()");
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:
case SessionHostState::OK:
/* OK */
break;
case SSH_KNOWN_HOSTS_CHANGED:
hexa = ssh_get_hexa(hash, hlen);
qhexa = hexa;
ssh_clean_pubkey_hash(&hash);
case SessionHostState::CHANGED:
qhexa = p->session->getHexa();
radix_tagged_line("SSH_KNOWN_HOSTS_CHANGED: " << qhexa.toStdString());
emit hostPublicKeyChanged(qhexa);
break;
case SSH_KNOWN_HOSTS_OTHER:
ssh_clean_pubkey_hash(&hash);
case SessionHostState::OTHER:
radix_tagged_line("SSH_KNOWN_HOSTS_OTHER");
emit hostPublicKeyUnavailable();
break;
case SSH_KNOWN_HOSTS_NOT_FOUND:
case SessionHostState::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);
case SessionHostState::UNKNOWN:
qhexa = p->session->getHexa();
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);
case SessionHostState::ERROR:
QString message = p->session->getError();
radix_tagged_line("SSH_KNOWN_HOSTS_ERROR: " << message.toStdString());
ssh_clean_pubkey_hash(&hash);
emit knownHostError(message);
break;
}
emit verifyKnownHostSuccessful();
ssh_clean_pubkey_hash(&hash);
}
void SessionWorker::acceptHostPublicKeyUpdate()
{
assert_ssh_session(
p->session, "acceptHostPublicKeyUpdate() -- Session is not allocated.");
radix_tagged_line("authenticateHostPublicKeyUpdate()");
int rc = ssh_session_update_known_hosts(p->session);
if (rc != SSH_OK)
bool rc = p->session->updateKnownHosts();
if (!rc)
{
emit updateKnownHostsFailed();
}
......@@ -242,32 +153,22 @@ void SessionWorker::acceptHostPublicKeyUpdate()
void SessionWorker::authenticate()
{
assert_ssh_session(p->session, "authenticate() -- Session is not allocated.");
radix_tagged_line("Authenticate.");
// 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);
int method = p->session->authenticationMethodList();
// Try to authenticate with public key first
if (method & SSH_AUTH_METHOD_PUBLICKEY)
if (method & SessionAuthMethodPUBLICKEY)
{
rc = ssh_userauth_publickey_auto(p->session, nullptr, nullptr);
if (rc == SSH_AUTH_ERROR)
SessionAuthState state = p->session->authenticateWithPublicKey();
if (state == SessionAuthState::ERROR)
{
QString message = ssh_get_error(p->session);
QString message = p->session->getError();
emit authenticationError(message);
return;
}
else if (rc == SSH_AUTH_SUCCESS)
else if (state == SessionAuthState::SUCCESS)
{
radix_tagged_line("SSH_AUTH_METHOD_PUBLICKEY succeeded.");
emit authenticationSucceeded();
......@@ -277,12 +178,10 @@ void SessionWorker::authenticate()
} // public key authentication
// Try to authenticate with keyboard interactive";
if (method & SSH_AUTH_METHOD_INTERACTIVE)
if (method & SessionAuthMethodINTERACTIVE)
{
int err;
err = ssh_userauth_kbdint(p->session, nullptr, nullptr);
while (err == SSH_AUTH_INFO)
SessionAuthState state = p->session->authenticateInteractively();
while (state == SessionAuthState::INFO)
{
processPrompts();
return;
......@@ -290,71 +189,56 @@ void SessionWorker::authenticate()
}
// Try to authenticate with password
if (method & SSH_AUTH_METHOD_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)
{
assert_ssh_session(p->session,
"authenticateWithPassword() -- Session is not allocated.");
radix_tagged_line("Authenticate with password.");
int rc =
ssh_userauth_password(p->session, nullptr, pswd.toStdString().c_str());
if (rc == SSH_AUTH_ERROR)
SessionAuthState state = p->session->authenticateWithPassword(pswd);
if (state == SessionAuthState::ERROR)
{
QString message = ssh_get_error(p->session);
QString message = p->session->getError();
emit authenticationError(message);
}
else if (rc == SSH_AUTH_SUCCESS)
else if (state == SessionAuthState::SUCCESS)
{
radix_tagged_line("SSH_AUTH_METHOD_PASSWORD succeeded.");
emit authenticationSucceeded();
char* banner = ssh_get_issue_banner(p->session);
if (banner)
QString banner = p->session->loginIssueBanner();
if (!banner.isEmpty())
{
QString qbanner = banner;
emit loginBannerIssued(qbanner);
delete banner;
emit loginBannerIssued(banner);
}
}
}
void SessionWorker::authenticatePrompts(QStringList responses)
{
int err;
radix_tagged_line("authenticatePrompts()");
for (int i = 0; i < responses.size(); ++i)
{
radix_tagged_line("Setting response " << i);
const char* answer = responses.at(i).toStdString().c_str();
err = ssh_userauth_kbdint_setanswer(p->session,
static_cast<unsigned int>(i), answer);
if (err < 0)
{
emit authenticationError("Failed to authenticate prompts.");
ssh_disconnect(p->session);
return;
}
}
// check status
err = ssh_userauth_kbdint(p->session, nullptr, nullptr);
if (err == SSH_AUTH_DENIED)
SessionAuthState state = p->session->authenticatePrompts(responses);
if (state == SessionAuthState::DENIED)
{
emit authenticationError("Authentication denied.");
ssh_disconnect(p->session);
p->session->disconnect();
return;
}
if (err == SSH_AUTH_INFO)
if (state == SessionAuthState::INFO)
{
processPrompts();
return;
}