Commit fd9bd198 authored by Harald Sitter's avatar Harald Sitter 🏳️‍🌈
Browse files

sftp: bump libssh requirement to at least 0.8.3

even that is very generous. it's 0.8.3 has been out for more than 3
years
parent fba3079e
......@@ -98,7 +98,7 @@ if(NOT WIN32)
)
endif()
find_package(libssh 0.7.0 MODULE)
find_package(libssh 0.8.3 MODULE)
set_package_properties(libssh PROPERTIES DESCRIPTION "the SSH library with SFTP support"
URL "https://www.libssh.org/"
TYPE OPTIONAL
......
......@@ -595,14 +595,12 @@ Result SFTPInternal::sftpOpenConnection(const AuthInfo &info)
return Result::fail(KIO::ERR_INTERNAL, i18n("Could not set a timeout."));
}
#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 8, 0)
// Disable Nagle's Algorithm (TCP_NODELAY). Usually faster for sftp.
bool nodelay = true;
rc = ssh_options_set(mSession, SSH_OPTIONS_NODELAY, &nodelay);
if (rc < 0) {
return Result::fail(KIO::ERR_INTERNAL, i18n("Could not disable Nagle's Algorithm."));
}
#endif // 0.8.0
// Don't use any compression
rc = ssh_options_set(mSession, SSH_OPTIONS_COMPRESSION_C_S, "none");
......@@ -669,7 +667,6 @@ Result SFTPInternal::sftpOpenConnection(const AuthInfo &info)
return Result::pass();
}
#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 8, 3)
Result SFTPInternal::openConnection()
{
if (mConnected) {
......@@ -987,301 +984,6 @@ Result SFTPInternal::openConnection()
return Result::pass();
}
#else // < 0.8.0
Result SFTPInternal::openConnection()
{
if (mConnected) {
return Result::pass();
}
if (mHost.isEmpty()) {
qCDebug(KIO_SFTP_LOG) << "openConnection(): Need hostname...";
return Result::fail(KIO::ERR_UNKNOWN_HOST);
}
AuthInfo info;
info.url.setScheme("sftp");
info.url.setHost(mHost);
if ( mPort > 0 && mPort != DEFAULT_SFTP_PORT ) {
info.url.setPort(mPort);
}
info.url.setUserName(mUsername);
info.username = mUsername;
// Check for cached authentication info if no password is specified...
if (mPassword.isEmpty()) {
qCDebug(KIO_SFTP_LOG) << "checking cache: info.username =" << info.username
<< ", info.url =" << info.url.toDisplayString();
q->checkCachedAuthentication(info);
} else {
info.password = mPassword;
}
// Start the ssh connection.
QString msg; // msg for dialog box
QString caption; // dialog box caption
unsigned char *hash = nullptr; // the server hash
ssh_key srv_pubkey;
char *hexa;
size_t hlen;
int rc, state;
// Attempt to start a ssh session and establish a connection with the server.
const auto openResult = sftpOpenConnection(info);
if (!openResult.success) {
return openResult;
}
qCDebug(KIO_SFTP_LOG) << "Getting the SSH server hash";
/* get the hash */
rc = ssh_get_publickey(mSession, &srv_pubkey);
if (rc < 0) {
const auto result = Result::fail(KIO::ERR_SLAVE_DEFINED, QString::fromUtf8(ssh_get_error(mSession)));
closeConnection();
return result;
}
rc = ssh_get_publickey_hash(srv_pubkey,
SSH_PUBLICKEY_HASH_SHA1,
&hash,
&hlen);
ssh_key_free(srv_pubkey);
if (rc < 0) {
closeConnection();
return Result::fail(KIO::ERR_SLAVE_DEFINED,
i18n("Could not create hash from server public key"));
}
qCDebug(KIO_SFTP_LOG) << "Checking if the SSH server is known";
/* check the server public key hash */
state = ssh_is_server_known(mSession);
switch (state) {
case SSH_SERVER_KNOWN_OK:
break;
case SSH_SERVER_FOUND_OTHER: {
ssh_string_free_char((char *)hash);
const QString errorString = i18n("The host key for this server was "
"not found, but another type of key exists.\n"
"An attacker might change the default server key to confuse your "
"client into thinking the key does not exist.\n"
"Please contact your system administrator.\n%1",
QString::fromUtf8(ssh_get_error(mSession)));
closeConnection();
return Result::fail(KIO::ERR_SLAVE_DEFINED, errorString);
}
case SSH_SERVER_KNOWN_CHANGED: {
hexa = ssh_get_hexa(hash, hlen);
ssh_string_free_char((char *)hash);
/* TODO print known_hosts file, port? */
const QString errorString = i18n("The host key for the server %1 has changed.\n"
"This could either mean that DNS SPOOFING is happening or the IP "
"address for the host and its host key have changed at the same time.\n"
"The fingerprint for the key sent by the remote host is:\n %2\n"
"Please contact your system administrator.\n%3",
mHost,
QString::fromUtf8(hexa),
QString::fromUtf8(ssh_get_error(mSession)));
ssh_string_free_char(hexa);
closeConnection();
return Result::fail(KIO::ERR_SLAVE_DEFINED, errorString);
}
case SSH_SERVER_FILE_NOT_FOUND:
case SSH_SERVER_NOT_KNOWN: {
hexa = ssh_get_hexa(hash, hlen);
ssh_string_free_char((char *)hash);
caption = i18n("Warning: Cannot verify host's identity.");
msg = i18n("The authenticity of host %1 cannot be established.\n"
"The key fingerprint is: %2\n"
"Are you sure you want to continue connecting?", mHost, hexa);
ssh_string_free_char(hexa);
if (KMessageBox::Yes != q->messageBox(SlaveBase::WarningYesNo, msg, caption)) {
closeConnection();
return Result::fail(KIO::ERR_USER_CANCELED);
}
/* write the known_hosts file */
qCDebug(KIO_SFTP_LOG) << "Adding server to known_hosts file.";
if (ssh_write_knownhost(mSession) < 0) {
const QString errorString = QString::fromUtf8(ssh_get_error(mSession));
closeConnection();
return Result::fail(KIO::ERR_USER_CANCELED, errorString);
}
break;
}
case SSH_SERVER_ERROR:
ssh_string_free_char((char *)hash);
return Result::fail(KIO::ERR_SLAVE_DEFINED, QString::fromUtf8(ssh_get_error(mSession)));
}
qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with the server";
// Try to login without authentication
rc = ssh_userauth_none(mSession, nullptr);
if (rc == SSH_AUTH_ERROR) {
closeConnection();
return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed."));
}
// This NEEDS to be called after ssh_userauth_none() !!!
int method = ssh_auth_list(mSession);
if (rc != SSH_AUTH_SUCCESS && method == 0) {
closeConnection();
return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed. The server "
"didn't send any authentication methods"));
}
// Try to authenticate with public key first
if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_PUBLICKEY)) {
qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with public key";
for(;;) {
rc = ssh_userauth_publickey_auto(mSession, nullptr, nullptr);
if (rc == SSH_AUTH_ERROR) {
qCDebug(KIO_SFTP_LOG) << "Public key authentication failed:" <<
QString::fromUtf8(ssh_get_error(mSession));
closeConnection();
clearPubKeyAuthInfo();
return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed."));
} else if (rc != SSH_AUTH_DENIED || !mPublicKeyAuthInfo || !mPublicKeyAuthInfo->isModified()) {
clearPubKeyAuthInfo();
break;
}
}
}
// Try to authenticate with GSSAPI
if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_GSSAPI_MIC)) {
qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with GSSAPI";
rc = ssh_userauth_gssapi(mSession);
if (rc == SSH_AUTH_ERROR) {
qCDebug(KIO_SFTP_LOG) << "Public key authentication failed:" <<
QString::fromUtf8(ssh_get_error(mSession));
closeConnection();
return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed."));
}
}
// Try to authenticate with keyboard interactive
if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_INTERACTIVE)) {
qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with keyboard interactive";
AuthInfo info2 (info);
rc = authenticateKeyboardInteractive(info2);
if (rc == SSH_AUTH_SUCCESS) {
info = info2;
} else if (rc == SSH_AUTH_ERROR) {
qCDebug(KIO_SFTP_LOG) << "Keyboard interactive authentication failed:"
<< QString::fromUtf8(ssh_get_error(mSession));
closeConnection();
return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed."));
}
}
// Try to authenticate with password
if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_PASSWORD)) {
qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with password";
info.caption = i18n("SFTP Login");
info.prompt = i18n("Please enter your username and password.");
info.comment = info.url.url();
info.commentLabel = i18n("Site:");
bool isFirstLoginAttempt = true;
for(;;) {
if (!isFirstLoginAttempt || info.password.isEmpty()) {
info.keepPassword = true; // make the "keep Password" check box visible to the user.
info.setModified(false);
QString username (info.username);
const QString errMsg(isFirstLoginAttempt ? QString() : i18n("Incorrect username or password"));
qCDebug(KIO_SFTP_LOG) << "Username:" << username << "first attempt?"
<< isFirstLoginAttempt << "error:" << errMsg;
// Handle user canceled or dialog failed to open...
int errCode = q->openPasswordDialogV2(info, errMsg);
if (errCode != KJob::NoError) {
qCDebug(KIO_SFTP_LOG) << "User canceled password/retry dialog";
closeConnection();
return Result::fail(errCode);
}
// If the user name changes, we have to restablish connection again
// since the user name must always be set before calling ssh_connect.
if (wasUsernameChanged(username, info)) {
qCDebug(KIO_SFTP_LOG) << "Username changed to" << info.username;
if (!info.url.userName().isEmpty()) {
info.url.setUserName(info.username);
}
closeConnection();
const auto result = sftpOpenConnection(info);
if (!result.success) {
return result;
}
}
}
rc = ssh_userauth_password(mSession, info.username.toUtf8().constData(), info.password.toUtf8().constData());
if (rc == SSH_AUTH_SUCCESS) {
break;
} else if (rc == SSH_AUTH_ERROR) {
qCDebug(KIO_SFTP_LOG) << "Password authentication failed:"
<< QString::fromUtf8(ssh_get_error(mSession));
closeConnection();
return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed."));
}
isFirstLoginAttempt = false; // failed attempt to login.
info.password.clear(); // clear the password after failed attempts.
}
}
// If we're still not authenticated then we need to leave.
if (rc != SSH_AUTH_SUCCESS) {
return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed."));
}
// start sftp session
qCDebug(KIO_SFTP_LOG) << "Trying to request the sftp session";
mSftp = sftp_new(mSession);
if (mSftp == nullptr) {
closeConnection();
return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Unable to request the SFTP subsystem. "
"Make sure SFTP is enabled on the server."));
}
qCDebug(KIO_SFTP_LOG) << "Trying to initialize the sftp session";
if (sftp_init(mSftp) < 0) {
closeConnection();
return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Could not initialize the SFTP session."));
}
// Login succeeded!
q->infoMessage(i18n("Successfully connected to %1", mHost));
if (info.keepPassword) {
qCDebug(KIO_SFTP_LOG) << "Caching info.username = " << info.username
<< ", info.url = " << info.url.toDisplayString();
q->cacheAuthentication(info);
}
// Update the original username in case it was changed!
if (!mUsername.isEmpty()) {
mUsername = info.username;
}
q->setTimeoutSpecialCommand(KIO_SFTP_SPECIAL_TIMEOUT);
mConnected = true;
q->connected();
info.password.fill('x');
info.password.clear();
return Result::pass();
}
#endif // 0.8.0
void SFTPInternal::closeConnection() {
qCDebug(KIO_SFTP_LOG);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment