summaryrefslogtreecommitdiffstats
path: root/tdeioslave/sftp/tdeio_sftp.cpp
diff options
context:
space:
mode:
authorMavridis Philippe <[email protected]>2022-06-24 14:28:04 +0300
committerMavridis Philippe <[email protected]>2022-07-15 13:12:20 +0300
commit7f277bc5e1504110b80bdef6da99d21ba4ea2a8c (patch)
tree7c65abc8b1708699546e6c94ace56c9acf5b8df2 /tdeioslave/sftp/tdeio_sftp.cpp
parentfd94618b638534cebf02eacfae104d147c361492 (diff)
downloadtdebase-7f277bc5e1504110b80bdef6da99d21ba4ea2a8c.tar.gz
tdebase-7f277bc5e1504110b80bdef6da99d21ba4ea2a8c.zip
SFTP: Various fixes and improvements
For details see PR #279. Portions of code borrowed from KDE5 SFTP ioslave: Source: https://invent.kde.org/network/kio-extras/-/blob/master/sftp/kio_sftp.cpp Licence: LGPLv2 or later Signed-off-by: Mavridis Philippe <[email protected]>
Diffstat (limited to 'tdeioslave/sftp/tdeio_sftp.cpp')
-rw-r--r--tdeioslave/sftp/tdeio_sftp.cpp229
1 files changed, 142 insertions, 87 deletions
diff --git a/tdeioslave/sftp/tdeio_sftp.cpp b/tdeioslave/sftp/tdeio_sftp.cpp
index 9203f8537..efdfa3e45 100644
--- a/tdeioslave/sftp/tdeio_sftp.cpp
+++ b/tdeioslave/sftp/tdeio_sftp.cpp
@@ -6,6 +6,8 @@
* Copyright (c) 2022 Mavridis Philippe <[email protected]>
* Trinity port
*
+ * Portions Copyright (c) 2020-2021 Harald Sitter <[email protected]>
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License (LGPL) as published by the Free Software Foundation;
@@ -66,9 +68,9 @@
using namespace TDEIO;
extern "C"
{
- int kdemain( int argc, char **argv )
+ int KDE_EXPORT kdemain( int argc, char **argv )
{
- TDEInstance instance( "tdeio_sftp" );
+ TDEInstance instance( "tdeio_sftp" );
kdDebug(TDEIO_SFTP_DB) << "*** Starting tdeio_sftp " << endl;
@@ -92,7 +94,8 @@ extern "C"
// The callback function for libssh
int auth_callback(const char *prompt, char *buf, size_t len,
- int echo, int verify, void *userdata) {
+ int echo, int verify, void *userdata)
+{
if (userdata == NULL) {
return -1;
}
@@ -117,39 +120,48 @@ void log_callback(ssh_session session, int priority, const char *message,
slave->log_callback(session, priority, message, userdata);
}
+// Public key authentication
int sftpProtocol::auth_callback(const char *prompt, char *buf, size_t len,
- int echo, int verify, void *userdata) {
- TQString i_prompt = TQString::fromUtf8(prompt);
-
+ int echo, int verify, void *userdata)
+{
// unused variables
(void) echo;
(void) verify;
(void) userdata;
- kdDebug(TDEIO_SFTP_DB) << "Entering authentication callback, prompt=" << i_prompt << endl;
+ kdDebug(TDEIO_SFTP_DB) << "Entering public key authentication callback" << endl;
- TDEIO::AuthInfo info;
+ if(!pubKeyInfo)
+ {
+ pubKeyInfo = new TDEIO::AuthInfo;
+ }
+ else
+ {
+ // TODO: inform user about incorrect password
+ }
- info.url.setProtocol("sftp");
- info.url.setHost(mHost);
- info.url.setPort(mPort);
- info.url.setUser(mUsername);
+ pubKeyInfo->url.setProtocol("sftp");
+ pubKeyInfo->url.setHost(mHost);
+ pubKeyInfo->url.setPort(mPort);
+ pubKeyInfo->url.setUser(mUsername);
- info.comment = "sftp://" + mUsername + "@" + mHost;
- info.username = i_prompt;
- info.readOnly = true;
- info.prompt = i_prompt;
- info.keepPassword = false; // don't save passwords for public key,
- // that's the task of ssh-agent.
+ pubKeyInfo->caption = i18n("SFTP Login");
+ pubKeyInfo->comment = "sftp://" + mUsername + "@" + mHost;
+ pubKeyInfo->username = mUsername;
+ pubKeyInfo->readOnly = false;
+ pubKeyInfo->prompt = TQString::fromUtf8(prompt);
+ pubKeyInfo->keepPassword = false; // don't save passwords for public key,
+ // that's the task of ssh-agent.
- if (!openPassDlg(info)) {
- kdDebug(TDEIO_SFTP_DB) << "Password dialog failed" << endl;
+ if (!openPassDlg(*pubKeyInfo)) {
+ kdDebug(TDEIO_SFTP_DB) << "User canceled entry of public key password." << endl;
return -1;
}
- strncpy(buf, info.password.utf8().data(), len - 1);
+ strncpy(buf, pubKeyInfo->password.utf8().data(), len - 1);
- info.password.fill('x');
+ pubKeyInfo->password.fill('x');
+ pubKeyInfo->password = "";
return 0;
}
@@ -224,7 +236,7 @@ int sftpProtocol::authenticateKeyboardInteractive(AuthInfo &info) {
}
break;
} else {
- if (prompt.lower() == "password") {
+ if (prompt.lower().startsWith("password")) {
answer = mPassword.utf8().data();
} else {
info.readOnly = true; // set username readonly
@@ -286,7 +298,7 @@ bool sftpProtocol::createUDSEntry(const TQString &filename, const TQByteArray &p
mode_t access;
char *link;
- ASSERT(entry.count() == 0);
+ Q_ASSERT(entry.count() == 0);
sftp_attributes sb = sftp_lstat(mSftp, path.data());
if (sb == NULL) {
@@ -531,6 +543,7 @@ void sftpProtocol::openConnection() {
<< ", info.url = " << info.url.prettyURL() << endl;
if (checkCachedAuthentication(info)) {
+ kdDebug() << "using cached" << endl;
mUsername = info.username;
mPassword = info.password;
}
@@ -708,78 +721,110 @@ void sftpProtocol::openConnection() {
rc = ssh_userauth_none(mSession, NULL);
if (rc == SSH_AUTH_ERROR) {
closeConnection();
- error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Authentication failed."));
+ error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Authentication failed (method: %1).")
+ .arg(i18n("none")));
return;
}
int method = ssh_auth_list(mSession);
+ if (!method && rc != SSH_AUTH_SUCCESS)
+ {
+ error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Authentication failed."
+ " The server did not send any authentication methods!"));
+ return;
+ }
+
bool firstTime = true;
bool dlgResult;
while (rc != SSH_AUTH_SUCCESS) {
-
// Try to authenticate with public key first
- kdDebug(TDEIO_SFTP_DB) << "Trying to authenticate public key" << endl;
- if (method & SSH_AUTH_METHOD_PUBLICKEY) {
- rc = ssh_userauth_autopubkey(mSession, NULL);
- if (rc == SSH_AUTH_ERROR) {
- closeConnection();
- error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Authentication failed."));
- return;
- } else if (rc == SSH_AUTH_SUCCESS) {
- break;
+ if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_PUBLICKEY) && !mPassword)
+ {
+ kdDebug(TDEIO_SFTP_DB) << "Trying to authenticate with public key" << endl;
+ for(;;)
+ {
+ rc = ssh_userauth_publickey_auto(mSession, nullptr, nullptr);
+ if (rc == SSH_AUTH_ERROR)
+ {
+ clearPubKeyAuthInfo();
+ closeConnection();
+ error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Authentication failed (method: %1).")
+ .arg(i18n("public key")));
+ return;
+ }
+ if (rc == SSH_AUTH_DENIED || !pubKeyInfo || !pubKeyInfo->isModified())
+ {
+ clearPubKeyAuthInfo();
+ break;
+ }
}
}
- info.caption = i18n("SFTP Login");
- info.readOnly = false;
- if (firstTime) {
- info.prompt = i18n("Please enter your username and password.");
- } else {
- info.prompt = i18n("Login failed.\nPlease confirm your username and password, and enter them again.");
- }
- dlgResult = openPassDlg(info);
-
- // Handle user canceled or dialog failed to open...
- if (!dlgResult) {
- kdDebug(TDEIO_SFTP_DB) << "User canceled, dlgResult = " << dlgResult << endl;
- closeConnection();
- error(TDEIO::ERR_USER_CANCELED, TQString());
- return;
- }
-
- firstTime = false;
-
- if (mUsername != info.username) {
- kdDebug(TDEIO_SFTP_DB) << "Username changed from " << mUsername
- << " to " << info.username << endl;
- }
- mUsername = info.username;
- mPassword = info.password;
-
// Try to authenticate with keyboard interactive
- kdDebug(TDEIO_SFTP_DB) << "Trying to authenticate with keyboard interactive" << endl;
- if (method & SSH_AUTH_METHOD_INTERACTIVE) {
- rc = authenticateKeyboardInteractive(info);
- if (rc == SSH_AUTH_ERROR) {
+ if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_INTERACTIVE))
+ {
+ kdDebug(TDEIO_SFTP_DB) << "Trying to authenticate with keyboard interactive" << endl;
+
+ TDEIO::AuthInfo tmpInfo(info);
+ rc = authenticateKeyboardInteractive(tmpInfo);
+ if (rc == SSH_AUTH_SUCCESS)
+ {
+ info = tmpInfo;
+ }
+ else if (rc == SSH_AUTH_ERROR)
+ {
closeConnection();
- error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Authentication failed."));
+ error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Authentication failed (method: %1).")
+ .arg(i18n("keyboard interactive")));
return;
- } else if (rc == SSH_AUTH_SUCCESS) {
- break;
}
- }
+ }
// Try to authenticate with password
- kdDebug(TDEIO_SFTP_DB) << "Trying to authenticate with password" << endl;
- if (method & SSH_AUTH_METHOD_PASSWORD) {
- rc = ssh_userauth_password(mSession, mUsername.utf8().data(),
- mPassword.utf8().data());
- if (rc == SSH_AUTH_ERROR) {
- closeConnection();
- error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Authentication failed."));
- return;
- } else if (rc == SSH_AUTH_SUCCESS) {
- break;
+ if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_PASSWORD))
+ {
+ kdDebug(TDEIO_SFTP_DB) << "Trying to authenticate with password" << endl;
+
+ info.keepPassword = true;
+ for(;;)
+ {
+ if(!firstTime || mPassword.isEmpty())
+ {
+ if (firstTime) {
+ info.prompt = i18n("Please enter your username and password.");
+ } else {
+ info.prompt = i18n("Login failed.\nPlease confirm your username and password, and enter them again.");
+ }
+ dlgResult = openPassDlg(info);
+
+ // Handle user canceled or dialog failed to open...
+ if (!dlgResult) {
+ kdDebug(TDEIO_SFTP_DB) << "User canceled, dlgResult = " << dlgResult << endl;
+ closeConnection();
+ error(TDEIO::ERR_USER_CANCELED, TQString());
+ return;
+ }
+
+ firstTime = false;
+ }
+
+ if (mUsername != info.username) {
+ kdDebug(TDEIO_SFTP_DB) << "Username changed from " << mUsername
+ << " to " << info.username << endl;
+ }
+ mUsername = info.username;
+ mPassword = info.password;
+
+ rc = ssh_userauth_password(mSession, mUsername.utf8().data(),
+ mPassword.utf8().data());
+ if (rc == SSH_AUTH_ERROR) {
+ closeConnection();
+ error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Authentication failed (method: %1).")
+ .arg(i18n("password")));
+ return;
+ } else if (rc == SSH_AUTH_SUCCESS) {
+ break;
+ }
}
}
}
@@ -947,12 +992,12 @@ void sftpProtocol::statMime(const KURL &url) {
void sftpProtocol::read(TDEIO::filesize_t bytes) {
kdDebug(TDEIO_SFTP_DB) << "read, offset = " << openOffset << ", bytes = " << bytes;
- ASSERT(mOpenFile != NULL);
+ Q_ASSERT(mOpenFile != NULL);
TQVarLengthArray<char> buffer(bytes);
ssize_t bytesRead = sftp_read(mOpenFile, buffer.data(), bytes);
- ASSERT(bytesRead <= static_cast<ssize_t>(bytes));
+ Q_ASSERT(bytesRead <= static_cast<ssize_t>(bytes));
if (bytesRead < 0) {
kdDebug(TDEIO_SFTP_DB) << "Could not read " << mOpenUrl;
@@ -968,7 +1013,7 @@ void sftpProtocol::read(TDEIO::filesize_t bytes) {
void sftpProtocol::write(const TQByteArray &data) {
kdDebug(TDEIO_SFTP_DB) << "write, offset = " << openOffset << ", bytes = " << data.size();
- ASSERT(mOpenFile != NULL);
+ Q_ASSERT(mOpenFile != NULL);
ssize_t bytesWritten = sftp_write(mOpenFile, data.data(), data.size());
if (bytesWritten < 0) {
@@ -984,7 +1029,7 @@ void sftpProtocol::write(const TQByteArray &data) {
void sftpProtocol::seek(TDEIO::filesize_t offset) {
kdDebug(TDEIO_SFTP_DB) << "seek, offset = " << offset;
- ASSERT(mOpenFile != NULL);
+ Q_ASSERT(mOpenFile != NULL);
if (sftp_seek64(mOpenFile, static_cast<uint64_t>(offset)) < 0) {
error(TDEIO::ERR_COULD_NOT_SEEK, mOpenUrl.path());
@@ -1193,7 +1238,8 @@ void sftpProtocol::put(const KURL& url, int permissions, bool overwrite, bool re
dataReq(); // Request for data
result = readData(buffer);
- if (result >= 0) {
+ if (result >= 0 && buffer.size()) {
+ kdDebug(TDEIO_SFTP_DB) << TQString("Got %1 bytes of data").arg(buffer.size()) << endl;
if (dest.isEmpty()) {
if (bMarkPartial) {
kdDebug(TDEIO_SFTP_DB) << "Appending .part extension to " << dest_orig << endl;
@@ -1253,6 +1299,7 @@ void sftpProtocol::put(const KURL& url, int permissions, bool overwrite, bool re
} // dest.isEmpty
ssize_t bytesWritten = sftp_write(file, buffer.data(), buffer.size());
+ kdDebug(TDEIO_SFTP_DB) << TQString("Written %1 bytes").arg(bytesWritten) << endl;
if (bytesWritten < 0) {
error(TDEIO::ERR_COULD_NOT_WRITE, dest_orig);
result = -1;
@@ -1270,7 +1317,7 @@ void sftpProtocol::put(const KURL& url, int permissions, bool overwrite, bool re
sftp_attributes attr = sftp_stat(mSftp, dest.data());
if (bMarkPartial && attr != NULL) {
- size_t size = config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE).toLong();
+ size_t size = config()->readLongNumEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE);
if (attr->size < size) {
sftp_unlink(mSftp, dest.data());
}
@@ -1466,7 +1513,7 @@ void sftpProtocol::listDir(const KURL& url) {
sftp_attributes dirent = NULL;
const TQString sDetails = metaData(TQString("details"));
const int details = sDetails.isEmpty() ? 2 : sDetails.toInt();
- TQList<TQByteArray> entryNames;
+ TQValueList<TQByteArray> entryNames;
UDSEntry entry;
kdDebug(TDEIO_SFTP_DB) << "readdir: " << path.data() << ", details: " << TQString::number(details) << endl;
@@ -1489,7 +1536,7 @@ void sftpProtocol::listDir(const KURL& url) {
entry.append(atom);
if (dirent->type == SSH_FILEXFER_TYPE_SYMLINK) {
- TQCString file = (TQString(path) + "/" + TQFile::decodeName(dirent->name)).utf8().data();
+ TQCString file = (TQString::fromUtf8(path) + "/" + TQFile::decodeName(dirent->name)).utf8().data();
atom.m_uds = UDS_FILE_TYPE;
atom.m_long = S_IFREG;
@@ -1771,3 +1818,11 @@ void sftpProtocol::slave_status() {
slaveStatus((mConnected ? mHost : TQString()), mConnected);
}
+void sftpProtocol::clearPubKeyAuthInfo()
+{
+ if (!pubKeyInfo)
+ {
+ delete pubKeyInfo;
+ pubKeyInfo = nullptr;
+ }
+}