tdeioslave/sftp: get rid of goto in openConnection()

Signed-off-by: Alexander Golubev <fatzer2@gmail.com>
(cherry picked from commit 3a4538b4c3)
pull/478/head
Alexander Golubev 9 months ago committed by Michele Calgaro
parent e55cf59eba
commit e41e8b9e6a
Signed by: MicheleC
GPG Key ID: 2A75B7CA8ADED5CF

@ -984,8 +984,6 @@ int sftpProtocol::initializeConnection() {
return rc; return rc;
} }
ExitGuard connectionCloser([this](){ closeConnection(); });
kdDebug(TDEIO_SFTP_DB) << "Getting the SSH server hash" << endl; kdDebug(TDEIO_SFTP_DB) << "Getting the SSH server hash" << endl;
/* get the hash */ /* get the hash */
@ -1079,8 +1077,6 @@ int sftpProtocol::initializeConnection() {
kdDebug(TDEIO_SFTP_DB) << "Trying to authenticate with the server" << endl; kdDebug(TDEIO_SFTP_DB) << "Trying to authenticate with the server" << endl;
connectionCloser.abort();
return SSH_OK; return SSH_OK;
} }
@ -1121,135 +1117,140 @@ void sftpProtocol::openConnection() {
PasswordPurger pwPurger{mPassword}; PasswordPurger pwPurger{mPassword};
int rc; int rc;
connection_restart:
// Start the ssh connection.
if (initializeConnection() < 0) {
return;
}
ExitGuard connectionCloser([this](){ closeConnection(); }); ExitGuard connectionCloser([this](){ closeConnection(); });
// Try to authenticate (this required before calling ssh_auth_list()) do { // A loop to restart connection when needed
rc = ssh_userauth_none(mSession, NULL); // Start the ssh connection.
if (rc == SSH_AUTH_ERROR) { if (initializeConnection() < 0) {
error(TDEIO::ERR_COULD_NOT_LOGIN, sshError(i18n("Authentication failed (method: %1).") return;
.arg(i18n("none")))); }
return;
}
// Preinit the list of supported auth methods
static const auto authMethodsNormal = [](){
std::vector<std::unique_ptr<SSHAuthMethod>> rv;
rv.emplace_back(std::make_unique<PublicKeyAuth>());
rv.emplace_back(std::make_unique<KeyboardInteractiveAuth>());
rv.emplace_back(std::make_unique<PasswordAuth>());
return rv;
}();
const static int supportedMethods = std::accumulate( // Try to authenticate (this required before calling ssh_auth_list())
authMethodsNormal.begin(), authMethodsNormal.end(), rc = ssh_userauth_none(mSession, NULL);
SSH_AUTH_METHOD_NONE, //< none is supported by default if (rc == SSH_AUTH_ERROR) {
[](int acc, const auto &m){ return acc |= m->flag(); }); error(TDEIO::ERR_COULD_NOT_LOGIN, sshError(i18n("Authentication failed (method: %1).")
.arg(i18n("none"))));
return;
}
unsigned attemptedMethods = 0; // Preinit the list of supported auth methods
static const auto authMethodsNormal = [](){
std::vector<std::unique_ptr<SSHAuthMethod>> rv;
rv.emplace_back(std::make_unique<PublicKeyAuth>());
rv.emplace_back(std::make_unique<KeyboardInteractiveAuth>());
rv.emplace_back(std::make_unique<PasswordAuth>());
return rv;
}();
const static int supportedMethods = std::accumulate(
authMethodsNormal.begin(), authMethodsNormal.end(),
SSH_AUTH_METHOD_NONE, //< none is supported by default
[](int acc, const auto &m){ return acc |= m->flag(); });
unsigned attemptedMethods = 0;
// Backup of the value of the SlaveBase::s_seqNr. This is used to query different data values
// with openPassDlg() with the same seqNr. Otherwise it will result in the prompting of the pass
// dialog to the user in cases the values should be recovered from the cache.
// This is a bit hacky but necessary
long current_seqNr = SlaveBase::s_seqNr;
while (rc != SSH_AUTH_SUCCESS) {
// Note this loop can rerun in case of multistage ssh authentication e.g. "password,publickey"
// which will require user to provide a valid password at first and then a valid public key.
// see AuthenticationMethods in man 5 sshd_config for more info
bool wasCanceled = false;
unsigned availableMethodes = ssh_auth_list(mSession);
// Backup of the value of the SlaveBase::s_seqNr. This is used to query different data values SlaveBase::s_seqNr = current_seqNr;
// with openPassDlg() with the same seqNr. Otherwise it will result in the prompting of the pass
// dialog to the user in cases the values should be recovered from the cache.
// This is a bit hacky but necessary
long current_seqNr = SlaveBase::s_seqNr;
while (rc != SSH_AUTH_SUCCESS) { if (!availableMethodes) {
// Note this loop can rerun in case of multistage ssh authentication e.g. "password,publickey" // Technically libssh docs suggest that the server merely MAY send auth methods, but it's
// which will require user to provide a valid password at first and then a valid public key. // highly unclear what we should do in such case and it looks like openssh doesn't have an
// see AuthenticationMethods in man 5 sshd_config for more info // option for that, so let's just consider this server a jerk and don't talk to him anymore.
bool wasCanceled = false; error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Authentication failed.\n"
unsigned availableMethodes = ssh_auth_list(mSession); "The server did not send any authentication methods!"));
return;
SlaveBase::s_seqNr = current_seqNr; } else if (!(availableMethodes & supportedMethods)) {
error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Authentication failed.\n"
if (!availableMethodes) { "The server sent only unsupported authentication methods (%1)!")
// Technically libssh docs suggest that the server merely MAY send auth methods, but it's .arg(SSHAuthMethod::bitsetToStr(availableMethodes).join(", ")));
// highly unclear what we should do in such case and it looks like openssh doesn't have an return;
// option for that, so let's just consider this server a jerk and don't talk to him anymore. }
error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Authentication failed.\n"
"The server did not send any authentication methods!"));
return;
} else if (!(availableMethodes & supportedMethods)) {
error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Authentication failed.\n"
"The server sent only unsupported authentication methods (%1)!")
.arg(SSHAuthMethod::bitsetToStr(availableMethodes).join(", ")));
return;
}
const auto *authMethods = &authMethodsNormal; const auto *authMethods = &authMethodsNormal;
// If we have cached password we want try to use it before public key // If we have cached password we want try to use it before public key
if(!mPassword.isEmpty()) { if(!mPassword.isEmpty()) {
static const auto authMethodsWithPassword = []() { static const auto authMethodsWithPassword = []() {
std::vector<std::unique_ptr<SSHAuthMethod>> rv; std::vector<std::unique_ptr<SSHAuthMethod>> rv;
rv.emplace_back(std::make_unique<KeyboardInteractiveAuth>(/* noPasswordQuery = */true)); rv.emplace_back(std::make_unique<KeyboardInteractiveAuth>(/* noPasswordQuery = */true));
rv.emplace_back(std::make_unique<PasswordAuth>(/* noPasswordQuery = */true)); rv.emplace_back(std::make_unique<PasswordAuth>(/* noPasswordQuery = */true));
for (const auto &m: authMethodsNormal) { rv.emplace_back(m->clone()); } for (const auto &m: authMethodsNormal) { rv.emplace_back(m->clone()); }
return rv; return rv;
}(); }();
authMethods = &authMethodsWithPassword; authMethods = &authMethodsWithPassword;
} }
// Actually iterate over the list of methods and try them out // Actually iterate over the list of methods and try them out
for (const auto &method: *authMethods) { for (const auto &method: *authMethods) {
if (!(availableMethodes & method->flag())) { continue; } if (!(availableMethodes & method->flag())) { continue; }
rc = method->authenticate( this ); rc = method->authenticate( this );
attemptedMethods |= method->flag(); attemptedMethods |= method->flag();
if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_PARTIAL) { if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_PARTIAL) {
kdDebug(TDEIO_SFTP_DB) << "method=" << method->name() << ": auth " kdDebug(TDEIO_SFTP_DB) << "method=" << method->name() << ": auth "
<< (rc == SSH_AUTH_SUCCESS ? "success" : "partial") << endl; << (rc == SSH_AUTH_SUCCESS ? "success" : "partial") << endl;
break; // either next auth method or continue on with the connect break; // either next auth method or continue on with the connect
} else if (rc == SSH_AUTH_ERROR || rc == SSH_AUTH_AGAIN) { } else if (rc == SSH_AUTH_ERROR || rc == SSH_AUTH_AGAIN) {
TQString errMsg = i18n("Authentication failed (method: %1).").arg(method->name()); TQString errMsg = i18n("Authentication failed (method: %1).").arg(method->name());
// SSH_AUTH_AGAIN returned in case of some errors when server hangs up unexpectedly like // SSH_AUTH_AGAIN returned in case of some errors when server hangs up unexpectedly like
// in case there were too many failed authentication attempts // in case there were too many failed authentication attempts
if (rc == SSH_AUTH_AGAIN) { if (rc == SSH_AUTH_AGAIN) {
errMsg.append("\n").append(i18n("Server is slow to respond or hung up unexpectedly.")); errMsg.append("\n").append(i18n("Server is slow to respond or hung up unexpectedly."));
}
error(TDEIO::ERR_COULD_NOT_LOGIN, sshError(errMsg));
return;
} else if (rc == SSH_AUTH_CANCELED) {
kdDebug(TDEIO_SFTP_DB) << "method=" << method->name() << " was canceled by user" << endl;
// don't quit immediately due to that the user might have canceled one method to use another
wasCanceled = true;
} else if (rc == SSH_AUTH_NEED_RECONNECT) {
kdDebug(TDEIO_SFTP_DB) << "method=" << method->name() << " requested reconnection" << endl;
break;
} else if (rc == SSH_AUTH_DENIED) {
kdDebug(TDEIO_SFTP_DB) << "Auth for method=" << method->name() << " was denied" << endl;
// do nothing, just proceed with next auth method
} else {
// Shouldn't happen, but to be on the safe side better handle it
error(TDEIO::ERR_UNKNOWN, sshError(i18n("Authentication failed unexpectedly")));
return;
} }
error(TDEIO::ERR_COULD_NOT_LOGIN, sshError(errMsg)); }
// At this point rc values should be one of:
// SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED, SSH_AUTH_CANCELED or SSH_AUTH_NEED_RECONNECT
if(rc == SSH_AUTH_NEED_RECONNECT) {
closeConnection(); //< have to do it manually
break;
} else if (wasCanceled && (rc == SSH_AUTH_CANCELED || rc == SSH_AUTH_DENIED)) {
error(TDEIO::ERR_USER_CANCELED, TQString::null);
return; return;
} else if (rc == SSH_AUTH_CANCELED) { } else if (rc != SSH_AUTH_SUCCESS && rc != SSH_AUTH_PARTIAL) {
kdDebug(TDEIO_SFTP_DB) << "method=" << method->name() << " was canceled by user" << endl; TQString errMsg = i18n("Authentication denied (attempted methods: %1).")
// don't quit immediately due to that the user might have canceled one method to use another .arg(SSHAuthMethod::bitsetToStr(attemptedMethods).join(", "));
wasCanceled = true; if (availableMethodes & ~supportedMethods) {
} else if (rc == SSH_AUTH_NEED_RECONNECT) { errMsg.append("\n")
kdDebug(TDEIO_SFTP_DB) << "method=" << method->name() << " requested reconnection" << endl; .append(i18n("Note: server also declares some unsupported authentication methods (%1)")
goto connection_restart; .arg(SSHAuthMethod::bitsetToStr(availableMethodes & ~supportedMethods).join(", ")));
} else if (rc == SSH_AUTH_DENIED) { }
kdDebug(TDEIO_SFTP_DB) << "Auth for method=" << method->name() << " was denied" << endl; error(TDEIO::ERR_COULD_NOT_LOGIN, errMsg);
// do nothing, just proceed with next auth method
} else {
// Shouldn't happen, but to be on the safe side better handle it
error(TDEIO::ERR_UNKNOWN, sshError(i18n("Authentication failed unexpectedly")));
return; return;
} }
} } // while (rc != SSH_AUTH_SUCCESS)
} while(rc == SSH_AUTH_NEED_RECONNECT);
// At this point rc values should be one of:
// SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED or SSH_AUTH_CANCELED
if (wasCanceled && (rc == SSH_AUTH_CANCELED || rc == SSH_AUTH_DENIED)) {
error(TDEIO::ERR_USER_CANCELED, TQString::null);
return;
} else if (rc != SSH_AUTH_SUCCESS && rc != SSH_AUTH_PARTIAL) {
TQString errMsg = i18n("Authentication denied (attempted methods: %1).")
.arg(SSHAuthMethod::bitsetToStr(attemptedMethods).join(", "));
if (availableMethodes & ~supportedMethods) {
errMsg.append("\n")
.append(i18n("Note: server also declares some unsupported authentication methods (%1)")
.arg(SSHAuthMethod::bitsetToStr(availableMethodes & ~supportedMethods).join(", ")));
}
error(TDEIO::ERR_COULD_NOT_LOGIN, errMsg);
return;
}
} // while (rc != SSH_AUTH_SUCCESS)
// start sftp session // start sftp session
kdDebug(TDEIO_SFTP_DB) << "Trying to request the sftp session" << endl; kdDebug(TDEIO_SFTP_DB) << "Trying to request the sftp session" << endl;

Loading…
Cancel
Save