From 7f277bc5e1504110b80bdef6da99d21ba4ea2a8c Mon Sep 17 00:00:00 2001 From: Mavridis Philippe Date: Fri, 24 Jun 2022 14:28:04 +0300 Subject: 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 --- tdeioslave/sftp/CMakeL10n.txt | 3 + tdeioslave/sftp/CMakeLists.txt | 4 +- tdeioslave/sftp/sftp.protocol | 2 +- tdeioslave/sftp/tdeio_sftp.cpp | 229 +++++++++++++++++++++++++---------------- tdeioslave/sftp/tdeio_sftp.h | 8 +- 5 files changed, 154 insertions(+), 92 deletions(-) create mode 100644 tdeioslave/sftp/CMakeL10n.txt (limited to 'tdeioslave') 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 * Trinity port * + * Portions Copyright (c) 2020-2021 Harald Sitter + * * 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 buffer(bytes); ssize_t bytesRead = sftp_read(mOpenFile, buffer.data(), bytes); - ASSERT(bytesRead <= static_cast(bytes)); + Q_ASSERT(bytesRead <= static_cast(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(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 entryNames; + TQValueList 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); -- cgit v1.2.1