summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMavridis Philippe <mavridisf@gmail.com>2022-06-24 14:28:04 +0300
committerMavridis Philippe <mavridisf@gmail.com>2022-07-15 13:12:20 +0300
commit7f277bc5e1504110b80bdef6da99d21ba4ea2a8c (patch)
tree7c65abc8b1708699546e6c94ace56c9acf5b8df2
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 <mavridisf@gmail.com>
-rw-r--r--ConfigureChecks.cmake10
-rw-r--r--tdeioslave/sftp/CMakeL10n.txt3
-rw-r--r--tdeioslave/sftp/CMakeLists.txt4
-rw-r--r--tdeioslave/sftp/sftp.protocol2
-rw-r--r--tdeioslave/sftp/tdeio_sftp.cpp229
-rw-r--r--tdeioslave/sftp/tdeio_sftp.h8
6 files changed, 163 insertions, 93 deletions
diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake
index b60dd5dc0..a637df137 100644
--- a/ConfigureChecks.cmake
+++ b/ConfigureChecks.cmake
@@ -89,11 +89,19 @@ endif( )
# sys/time.h (tdeioslave/sftp, ksmserver, ksplashml)
-if( BUILD_KSMSERVER OR BUILD_KSPLASHML OR BUILD_TDEIOSLAVES)
+if( BUILD_KSMSERVER OR BUILD_KSPLASHML OR BUILD_TDEIOSLAVES )
check_include_file( sys/time.h HAVE_SYS_TIME_H )
check_include_files( "sys/time.h;time.h" TIME_WITH_SYS_TIME )
endif( )
+# libssh (tdeioslave/sftp)
+if( BUILD_TDEIOSLAVES )
+ pkg_search_module( LIBSSH libssh )
+ if( NOT LIBSSH_FOUND )
+ tde_message_fatal( "LibSSH is required, but was not found on your system" )
+ endif( )
+endif( )
+
# pam
if( WITH_PAM AND (BUILD_KCHECKPASS OR BUILD_TDM) )
diff --git a/tdeioslave/sftp/CMakeL10n.txt b/tdeioslave/sftp/CMakeL10n.txt
new file mode 100644
index 000000000..f6ee3ab31
--- /dev/null
+++ b/tdeioslave/sftp/CMakeL10n.txt
@@ -0,0 +1,3 @@
+##### create translation templates ##############
+
+tde_l10n_create_template( "tdeio_sftp" )
diff --git a/tdeioslave/sftp/CMakeLists.txt b/tdeioslave/sftp/CMakeLists.txt
index 8967d1ccd..8074ea76e 100644
--- a/tdeioslave/sftp/CMakeLists.txt
+++ b/tdeioslave/sftp/CMakeLists.txt
@@ -23,7 +23,7 @@ link_directories(
tde_create_translated_desktop(
SOURCE sftp.protocol
DESTINATION ${SERVICES_INSTALL_DIR}
- #PO_DIR tdeioslave-desktops
+ PO_DIR tdeioslave-desktops
)
@@ -33,6 +33,6 @@ set( target tdeio_sftp )
tde_add_kpart( ${target} AUTOMOC
SOURCES tdeio_sftp.cpp
- LINK tdeio-shared
+ LINK tdeio-shared ssh
DESTINATION ${PLUGIN_INSTALL_DIR}
)
diff --git a/tdeioslave/sftp/sftp.protocol b/tdeioslave/sftp/sftp.protocol
index a317beb25..931ee7890 100644
--- a/tdeioslave/sftp/sftp.protocol
+++ b/tdeioslave/sftp/sftp.protocol
@@ -12,7 +12,7 @@ makedir=true
deleting=true
moving=true
Icon=ftp
-Description=A new tdeioslave for sftp
+Description=A tdeioslave for sftp
X-DocPath=tdeioslave/sftp/index.html
Icon=ftp
Class=:internet
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 <mavridisf@gmail.com>
* Trinity port
*
+ * Portions Copyright (c) 2020-2021 Harald Sitter <sitter@kde.org>
+ *
* 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;
+ }
+}
diff --git a/tdeioslave/sftp/tdeio_sftp.h b/tdeioslave/sftp/tdeio_sftp.h
index 88ddaecf1..82bbc7a43 100644
--- a/tdeioslave/sftp/tdeio_sftp.h
+++ b/tdeioslave/sftp/tdeio_sftp.h
@@ -70,12 +70,13 @@ public:
// libssh authentication callback (note that this is called by the
// global ::auth_callback() call.
int auth_callback(const char *prompt, char *buf, size_t len,
- int echo, int verify, void *userdata);
+ int echo, int verify, void *userdata);
// libssh logging callback (note that this is called by the
// global ::log_callback() call.
void log_callback(ssh_session session, int priority, const char *message,
- void *userdata);
+ void *userdata);
+
private: // Private variables
void statMime(const KURL &url);
@@ -119,9 +120,12 @@ private: // Private variables
// TQString text;
//};
+ TDEIO::AuthInfo *pubKeyInfo;
+
private: // private methods
int authenticateKeyboardInteractive(TDEIO::AuthInfo &info);
+ void clearPubKeyAuthInfo();
void reportError(const KURL &url, const int err);