diff options
Diffstat (limited to 'src/libs/ssh')
82 files changed, 1307 insertions, 11872 deletions
diff --git a/src/libs/ssh/opensshkeyfilereader.cpp b/src/libs/ssh/opensshkeyfilereader.cpp deleted file mode 100644 index 1a5cb2ece68..00000000000 --- a/src/libs/ssh/opensshkeyfilereader.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "opensshkeyfilereader_p.h" - -#include "sshcapabilities_p.h" -#include "ssherrors.h" -#include "sshexception_p.h" -#include "sshlogging_p.h" -#include "sshpacketparser_p.h" -#include "ssh_global.h" - -#include <botan/dl_group.h> -#include <botan/dsa.h> -#include <botan/ecdsa.h> -#include <botan/rsa.h> - -namespace QSsh { -namespace Internal { - -using namespace Botan; - -bool OpenSshKeyFileReader::parseKey(const QByteArray &privKeyFileContents) -{ - static const QByteArray magicPrefix = "-----BEGIN OPENSSH PRIVATE KEY-----\n"; - static const QByteArray magicSuffix = "-----END OPENSSH PRIVATE KEY-----\n"; - if (!privKeyFileContents.startsWith(magicPrefix)) { - qCDebug(sshLog) << "not an OpenSSH key file: prefix does not match"; - return false; - } - if (!privKeyFileContents.endsWith(magicSuffix)) - throwException(SSH_TR("Unexpected end-of-file marker.")); - const QByteArray payload = QByteArray::fromBase64 - (privKeyFileContents.mid(magicPrefix.size(), privKeyFileContents.size() - - magicPrefix.size() - magicSuffix.size())); - doParse(payload); - return true; -} - -std::unique_ptr<Private_Key> OpenSshKeyFileReader::privateKey() const -{ - if (m_keyType == SshCapabilities::PubKeyRsa) { - QSSH_ASSERT_AND_RETURN_VALUE(m_parameters.size() == 5, nullptr); - const BigInt &e = m_parameters.at(0); - const BigInt &n = m_parameters.at(1); - const BigInt &p = m_parameters.at(2); - const BigInt &q = m_parameters.at(3); - const BigInt &d = m_parameters.at(4); - return std::make_unique<RSA_PrivateKey>(p, q, e, d, n); - } else if (m_keyType == SshCapabilities::PubKeyDss) { - QSSH_ASSERT_AND_RETURN_VALUE(m_parameters.size() == 5, nullptr); - const BigInt &p = m_parameters.at(0); - const BigInt &q = m_parameters.at(1); - const BigInt &g = m_parameters.at(2); - const BigInt &x = m_parameters.at(4); - return std::make_unique<DSA_PrivateKey>(m_rng, DL_Group(p, q, g), x); - } else if (m_keyType.startsWith(SshCapabilities::PubKeyEcdsaPrefix)) { - QSSH_ASSERT_AND_RETURN_VALUE(m_parameters.size() == 1, nullptr); - const BigInt &value = m_parameters.first(); - const EC_Group group(SshCapabilities::oid(m_keyType)); - return std::make_unique<ECDSA_PrivateKey>(m_rng, group, value); - } - QSSH_ASSERT_AND_RETURN_VALUE(false, nullptr); -} - -QList<BigInt> OpenSshKeyFileReader::publicParameters() const -{ - if (m_keyType == SshCapabilities::PubKeyRsa) - return m_parameters.mid(0, 2); - if (m_keyType == SshCapabilities::PubKeyDss) - return m_parameters.mid(0, 4); - if (m_keyType.startsWith(SshCapabilities::PubKeyEcdsaPrefix)) - return QList<BigInt>(); - QSSH_ASSERT_AND_RETURN_VALUE(false, QList<BigInt>()); -} - -void OpenSshKeyFileReader::doParse(const QByteArray &payload) -{ - // See PROTOCOL.key in OpenSSH sources. - static const QByteArray magicString = "openssh-key-v1"; - if (!payload.startsWith(magicString)) - throwException(SSH_TR("Unexpected magic string.")); - try { - quint32 offset = magicString.size() + 1; // null byte - m_cipherName = SshPacketParser::asString(payload, &offset); - qCDebug(sshLog) << "cipher:" << m_cipherName; - m_kdf = SshPacketParser::asString(payload, &offset); - qCDebug(sshLog) << "kdf:" << m_kdf; - parseKdfOptions(SshPacketParser::asString(payload, &offset)); - const quint32 keyCount = SshPacketParser::asUint32(payload, &offset); - if (keyCount != 1) { - qCWarning(sshLog) << "more than one key found in OpenSSH private key file, ignoring " - "all but the first one"; - } - for (quint32 i = 0; i < keyCount; ++i) // Skip the public key blob(s). - SshPacketParser::asString(payload, &offset); - m_privateKeyList = SshPacketParser::asString(payload, &offset); - decryptPrivateKeyList(); - parsePrivateKeyList(); - } catch (const SshPacketParseException &) { - throwException(SSH_TR("Parse error.")); - } catch (const Exception &e) { - throwException(QLatin1String(e.what())); - } -} - -void OpenSshKeyFileReader::parseKdfOptions(const QByteArray &kdfOptions) -{ - if (m_cipherName == "none") - return; - quint32 offset = 0; - m_salt = SshPacketParser::asString(kdfOptions, &offset); - if (m_salt.size() != 16) - throwException(SSH_TR("Invalid salt size %1.").arg(m_salt.size())); - m_rounds = SshPacketParser::asUint32(kdfOptions, &offset); - qCDebug(sshLog) << "salt:" << m_salt.toHex(); - qCDebug(sshLog) << "rounds:" << m_rounds; -} - -void OpenSshKeyFileReader::decryptPrivateKeyList() -{ - if (m_cipherName == "none") - return; - if (m_kdf != "bcrypt") { - throwException(SSH_TR("Unexpected key derivation function '%1'.") - .arg(QLatin1String(m_kdf))); - } - - // OpenSSH uses a proprietary algorithm for the key derivation. We'd basically have to - // copy the code. - // TODO: If the lower-level operations (hashing primitives, blowfish stuff) can be taken - // over by Botan, that might be feasible. Investigate. - throwException(SSH_TR("Encrypted keys are currently not supported in this format.")); -} - -void OpenSshKeyFileReader::parsePrivateKeyList() -{ - quint32 offset = 0; - const quint32 checkInt1 = SshPacketParser::asUint32(m_privateKeyList, &offset); - const quint32 checkInt2 = SshPacketParser::asUint32(m_privateKeyList, &offset); - if (checkInt1 != checkInt2) - throwException(SSH_TR("Verification failed.")); - m_keyType = SshPacketParser::asString(m_privateKeyList, &offset); - qCDebug(sshLog) << "key type:" << m_keyType; - if (m_keyType == SshCapabilities::PubKeyRsa) { - const BigInt n = SshPacketParser::asBigInt(m_privateKeyList, &offset); - const BigInt e = SshPacketParser::asBigInt(m_privateKeyList, &offset); - const BigInt d = SshPacketParser::asBigInt(m_privateKeyList, &offset); - SshPacketParser::asBigInt(m_privateKeyList, &offset); // iqmp - const BigInt p = SshPacketParser::asBigInt(m_privateKeyList, &offset); - const BigInt q = SshPacketParser::asBigInt(m_privateKeyList, &offset); - m_parameters = QList<BigInt>{e, n, p, q, d}; - } else if (m_keyType == SshCapabilities::PubKeyDss) { - const BigInt p = SshPacketParser::asBigInt(m_privateKeyList, &offset); - const BigInt q = SshPacketParser::asBigInt(m_privateKeyList, &offset); - const BigInt g = SshPacketParser::asBigInt(m_privateKeyList, &offset); - const BigInt y = SshPacketParser::asBigInt(m_privateKeyList, &offset); - const BigInt x = SshPacketParser::asBigInt(m_privateKeyList, &offset); - m_parameters = QList<BigInt>{p, q, g, y, x}; - } else if (m_keyType.startsWith(SshCapabilities::PubKeyEcdsaPrefix)) { - SshPacketParser::asString(m_privateKeyList, &offset); // name - SshPacketParser::asString(m_privateKeyList, &offset); // pubkey representation - m_parameters = {SshPacketParser::asBigInt(m_privateKeyList, &offset)}; - } else { - throwException(SSH_TR("Private key type '%1' is not supported.") - .arg(QString::fromLatin1(m_keyType))); - } - const QByteArray comment = SshPacketParser::asString(m_privateKeyList, &offset); - qCDebug(sshLog) << "comment:" << comment; -} - -void OpenSshKeyFileReader::throwException(const QString &reason) -{ - throw SshClientException(SshKeyFileError, - SSH_TR("Processing OpenSSH private key file failed: %1").arg(reason)); -} - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/opensshkeyfilereader_p.h b/src/libs/ssh/opensshkeyfilereader_p.h deleted file mode 100644 index 0ea0eb5c925..00000000000 --- a/src/libs/ssh/opensshkeyfilereader_p.h +++ /dev/null @@ -1,73 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include <QByteArray> -#include <QList> - -#include <botan/bigint.h> - -#include <memory> - -namespace Botan { -class Private_Key; -class RandomNumberGenerator; -} - -namespace QSsh { -namespace Internal { - -class OpenSshKeyFileReader -{ -public: - OpenSshKeyFileReader(Botan::RandomNumberGenerator &rng) : m_rng(rng) {} - - bool parseKey(const QByteArray &privKeyFileContents); - QByteArray keyType() const { return m_keyType; } - std::unique_ptr<Botan::Private_Key> privateKey() const; - QList<Botan::BigInt> allParameters() const { return m_parameters; } - QList<Botan::BigInt> publicParameters() const; - -private: - void doParse(const QByteArray &payload); - void parseKdfOptions(const QByteArray &kdfOptions); - void decryptPrivateKeyList(); - void parsePrivateKeyList(); - [[noreturn]] void throwException(const QString &reason); - - Botan::RandomNumberGenerator &m_rng; - QByteArray m_keyType; - QList<Botan::BigInt> m_parameters; - QByteArray m_cipherName; - QByteArray m_kdf; - QByteArray m_salt; - quint32 m_rounds; - QByteArray m_privateKeyList; -}; - -} // namespace Internal -} // namespace QSsh - diff --git a/src/libs/ssh/sftpchannel.cpp b/src/libs/ssh/sftpchannel.cpp deleted file mode 100644 index 59c6ea66ec4..00000000000 --- a/src/libs/ssh/sftpchannel.cpp +++ /dev/null @@ -1,976 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sftpchannel.h" -#include "sftpchannel_p.h" - -#include "sshexception_p.h" -#include "sshincomingpacket_p.h" -#include "sshlogging_p.h" -#include "sshsendfacility_p.h" - -#include <QDir> -#include <QFile> - -/*! - \class QSsh::SftpChannel - - \brief The SftpChannel class provides SFTP operations. - - Objects are created via SshConnection::createSftpChannel(). - The channel needs to be initialized with - a call to initialize() and is closed via closeChannel(). After closing - a channel, no more operations are possible. It cannot be re-opened - using initialize(); use SshConnection::createSftpChannel() if you need - a new one. - - After the initialized() signal has been emitted, operations can be started. - All SFTP operations are asynchronous (non-blocking) and can be in-flight - simultaneously (though callers must ensure that concurrently running jobs - are independent of each other, e.g. they must not write to the same file). - Operations are identified by their job id, which is returned by - the respective member function. If the function can right away detect that - the operation cannot succeed, it returns SftpInvalidJob. If an error occurs - later, the finished() signal is emitted for the respective job with a - non-empty error string. - - Note that directory names must not have a trailing slash. -*/ - -namespace QSsh { -namespace Internal { -namespace { - const quint32 ProtocolVersion = 3; - - QString errorMessage(const QString &serverMessage, - const QString &alternativeMessage) - { - return serverMessage.isEmpty() ? alternativeMessage : serverMessage; - } - - QString errorMessage(const SftpStatusResponse &response, - const QString &alternativeMessage) - { - return response.status == SSH_FX_OK ? QString() - : errorMessage(response.errorString, alternativeMessage); - } -} // anonymous namespace -} // namespace Internal - -SftpChannel::SftpChannel(quint32 channelId, - Internal::SshSendFacility &sendFacility) - : d(new Internal::SftpChannelPrivate(channelId, sendFacility, this)) -{ - connect(d, &Internal::SftpChannelPrivate::initialized, - this, &SftpChannel::initialized, Qt::QueuedConnection); - connect(d, &Internal::SftpChannelPrivate::channelError, - this, &SftpChannel::channelError, Qt::QueuedConnection); - connect(d, &Internal::SftpChannelPrivate::dataAvailable, - this, &SftpChannel::dataAvailable, Qt::QueuedConnection); - connect(d, &Internal::SftpChannelPrivate::fileInfoAvailable, - this, &SftpChannel::fileInfoAvailable, Qt::QueuedConnection); - connect(d, &Internal::SftpChannelPrivate::finished, - this, &SftpChannel::finished, Qt::QueuedConnection); - connect(d, &Internal::SftpChannelPrivate::closed, - this, &SftpChannel::closed, Qt::QueuedConnection); -} - -SftpChannel::State SftpChannel::state() const -{ - switch (d->channelState()) { - case Internal::AbstractSshChannel::Inactive: - return Uninitialized; - case Internal::AbstractSshChannel::SessionRequested: - return Initializing; - case Internal::AbstractSshChannel::CloseRequested: - return Closing; - case Internal::AbstractSshChannel::Closed: - return Closed; - case Internal::AbstractSshChannel::SessionEstablished: - return d->m_sftpState == Internal::SftpChannelPrivate::Initialized - ? Initialized : Initializing; - default: - Q_ASSERT(!"Oh no, we forgot to handle a channel state!"); - return Closed; // For the compiler. - } -} - -void SftpChannel::initialize() -{ - d->requestSessionStart(); - d->m_sftpState = Internal::SftpChannelPrivate::SubsystemRequested; -} - -void SftpChannel::closeChannel() -{ - d->closeChannel(); -} - -SftpJobId SftpChannel::statFile(const QString &path) -{ - return d->createJob(Internal::SftpStatFile::Ptr( - new Internal::SftpStatFile(++d->m_nextJobId, path))); -} - -SftpJobId SftpChannel::listDirectory(const QString &path) -{ - return d->createJob(Internal::SftpListDir::Ptr( - new Internal::SftpListDir(++d->m_nextJobId, path))); -} - -SftpJobId SftpChannel::createDirectory(const QString &path) -{ - return d->createJob(Internal::SftpMakeDir::Ptr( - new Internal::SftpMakeDir(++d->m_nextJobId, path))); -} - -SftpJobId SftpChannel::removeDirectory(const QString &path) -{ - return d->createJob(Internal::SftpRmDir::Ptr( - new Internal::SftpRmDir(++d->m_nextJobId, path))); -} - -SftpJobId SftpChannel::removeFile(const QString &path) -{ - return d->createJob(Internal::SftpRm::Ptr( - new Internal::SftpRm(++d->m_nextJobId, path))); -} - -SftpJobId SftpChannel::renameFileOrDirectory(const QString &oldPath, - const QString &newPath) -{ - return d->createJob(Internal::SftpRename::Ptr( - new Internal::SftpRename(++d->m_nextJobId, oldPath, newPath))); -} - -SftpJobId SftpChannel::createLink(const QString &filePath, const QString &target) -{ - return d->createJob(Internal::SftpCreateLink::Ptr( - new Internal::SftpCreateLink(++d->m_nextJobId, filePath, target))); -} - -SftpJobId SftpChannel::createFile(const QString &path, SftpOverwriteMode mode) -{ - return d->createJob(Internal::SftpCreateFile::Ptr( - new Internal::SftpCreateFile(++d->m_nextJobId, path, mode))); -} - -SftpJobId SftpChannel::uploadFile(const QString &localFilePath, - const QString &remoteFilePath, SftpOverwriteMode mode) -{ - QSharedPointer<QFile> localFile(new QFile(localFilePath)); - if (!localFile->open(QIODevice::ReadOnly)) - return SftpInvalidJob; - return d->createJob(Internal::SftpUploadFile::Ptr( - new Internal::SftpUploadFile(++d->m_nextJobId, remoteFilePath, localFile, mode))); -} - -SftpJobId SftpChannel::downloadFile(const QString &remoteFilePath, - const QString &localFilePath, SftpOverwriteMode mode) -{ - QSharedPointer<QFile> localFile(new QFile(localFilePath)); - if (mode == SftpSkipExisting && localFile->exists()) - return SftpInvalidJob; - QIODevice::OpenMode openMode = QIODevice::WriteOnly; - if (mode == SftpOverwriteExisting) - openMode |= QIODevice::Truncate; - else if (mode == SftpAppendToExisting) - openMode |= QIODevice::Append; - if (!localFile->open(openMode)) - return SftpInvalidJob; - return d->createJob(Internal::SftpDownload::Ptr( - new Internal::SftpDownload(++d->m_nextJobId, remoteFilePath, localFile))); -} - -SftpJobId SftpChannel::uploadDir(const QString &localDirPath, - const QString &remoteParentDirPath) -{ - if (state() != Initialized) - return SftpInvalidJob; - const QDir localDir(localDirPath); - if (!localDir.exists() || !localDir.isReadable()) - return SftpInvalidJob; - const Internal::SftpUploadDir::Ptr uploadDirOp( - new Internal::SftpUploadDir(++d->m_nextJobId)); - const QString remoteDirPath - = remoteParentDirPath + QLatin1Char('/') + localDir.dirName(); - const Internal::SftpMakeDir::Ptr mkdirOp( - new Internal::SftpMakeDir(++d->m_nextJobId, remoteDirPath, uploadDirOp)); - uploadDirOp->mkdirsInProgress.insert(mkdirOp, - Internal::SftpUploadDir::Dir(localDirPath, remoteDirPath)); - d->createJob(mkdirOp); - return uploadDirOp->jobId; -} - -SftpChannel::~SftpChannel() -{ - delete d; -} - - -namespace Internal { - -SftpChannelPrivate::SftpChannelPrivate(quint32 channelId, - SshSendFacility &sendFacility, SftpChannel *sftp) - : AbstractSshChannel(channelId, sendFacility), - m_nextJobId(0), m_sftpState(Inactive), m_sftp(sftp) -{ -} - -SftpJobId SftpChannelPrivate::createJob(const AbstractSftpOperation::Ptr &job) -{ - if (m_sftp->state() != SftpChannel::Initialized) - return SftpInvalidJob; - m_jobs.insert(job->jobId, job); - sendData(job->initialPacket(m_outgoingPacket).rawData()); - return job->jobId; -} - -void SftpChannelPrivate::handleChannelSuccess() -{ - if (channelState() == CloseRequested) - return; - qCDebug(sshLog, "sftp subsystem initialized"); - sendData(m_outgoingPacket.generateInit(ProtocolVersion).rawData()); - m_sftpState = InitSent; -} - -void SftpChannelPrivate::handleChannelFailure() -{ - if (channelState() == CloseRequested) - return; - - if (m_sftpState != SubsystemRequested) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_MSG_CHANNEL_FAILURE packet."); - } - emit channelError(tr("Server could not start SFTP subsystem.")); - closeChannel(); -} - -void SftpChannelPrivate::handleChannelDataInternal(const QByteArray &data) -{ - if (channelState() == CloseRequested) - return; - - m_incomingData += data; - m_incomingPacket.consumeData(m_incomingData); - while (m_incomingPacket.isComplete()) { - handleCurrentPacket(); - m_incomingPacket.clear(); - m_incomingPacket.consumeData(m_incomingData); - } -} - -void SftpChannelPrivate::handleChannelExtendedDataInternal(quint32 type, - const QByteArray &data) -{ - qCWarning(sshLog, "Unexpected extended data '%s' of type %d on SFTP channel.", - data.data(), type); -} - -void SftpChannelPrivate::handleExitStatus(const SshChannelExitStatus &exitStatus) -{ - qCDebug(sshLog, "Remote SFTP service exited with exit code %d", exitStatus.exitStatus); - - if (channelState() == CloseRequested || channelState() == Closed) - return; - - emit channelError(tr("The SFTP server finished unexpectedly with exit code %1.") - .arg(exitStatus.exitStatus)); - - // Note: According to the specs, the server must close the channel after this happens, - // but OpenSSH doesn't do that, so we need to initiate the closing procedure ourselves. - closeChannel(); -} - -void SftpChannelPrivate::handleExitSignal(const SshChannelExitSignal &signal) -{ - emit channelError(tr("The SFTP server crashed: %1.").arg(signal.error)); - closeChannel(); // See above. -} - -void SftpChannelPrivate::handleCurrentPacket() -{ - qCDebug(sshLog, "Handling SFTP packet of type %d", m_incomingPacket.type()); - switch (m_incomingPacket.type()) { - case SSH_FXP_VERSION: - handleServerVersion(); - break; - case SSH_FXP_HANDLE: - handleHandle(); - break; - case SSH_FXP_NAME: - handleName(); - break; - case SSH_FXP_STATUS: - handleStatus(); - break; - case SSH_FXP_DATA: - handleReadData(); - break; - case SSH_FXP_ATTRS: - handleAttrs(); - break; - default: - throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected packet.", - tr("Unexpected packet of type %1.").arg(m_incomingPacket.type())); - } -} - -void SftpChannelPrivate::handleServerVersion() -{ - checkChannelActive(); - if (m_sftpState != InitSent) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_FXP_VERSION packet."); - } - - qCDebug(sshLog, "sftp init received"); - const quint32 serverVersion = m_incomingPacket.extractServerVersion(); - if (serverVersion != ProtocolVersion) { - emit channelError(tr("Protocol version mismatch: Expected %1, got %2") - .arg(serverVersion).arg(ProtocolVersion)); - closeChannel(); - } else { - m_sftpState = Initialized; - emit initialized(); - } -} - -void SftpChannelPrivate::handleHandle() -{ - const SftpHandleResponse &response = m_incomingPacket.asHandleResponse(); - JobMap::Iterator it = lookupJob(response.requestId); - const QSharedPointer<AbstractSftpOperationWithHandle> job - = it.value().dynamicCast<AbstractSftpOperationWithHandle>(); - if (job.isNull()) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_FXP_HANDLE packet."); - } - if (job->state != AbstractSftpOperationWithHandle::OpenRequested) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_FXP_HANDLE packet."); - } - job->remoteHandle = response.handle; - job->state = AbstractSftpOperationWithHandle::Open; - - switch (it.value()->type()) { - case AbstractSftpOperation::ListDir: - handleLsHandle(it); - break; - case AbstractSftpOperation::CreateFile: - handleCreateFileHandle(it); - break; - case AbstractSftpOperation::Download: - handleGetHandle(it); - break; - case AbstractSftpOperation::UploadFile: - handlePutHandle(it); - break; - default: - Q_ASSERT(!"Oh no, I forgot to handle an SFTP operation type!"); - } -} - -void SftpChannelPrivate::handleLsHandle(const JobMap::Iterator &it) -{ - SftpListDir::Ptr op = it.value().staticCast<SftpListDir>(); - sendData(m_outgoingPacket.generateReadDir(op->remoteHandle, - op->jobId).rawData()); -} - -void SftpChannelPrivate::handleCreateFileHandle(const JobMap::Iterator &it) -{ - SftpCreateFile::Ptr op = it.value().staticCast<SftpCreateFile>(); - sendData(m_outgoingPacket.generateCloseHandle(op->remoteHandle, - op->jobId).rawData()); -} - -void SftpChannelPrivate::handleGetHandle(const JobMap::Iterator &it) -{ - SftpDownload::Ptr op = it.value().staticCast<SftpDownload>(); - sendData(m_outgoingPacket.generateFstat(op->remoteHandle, - op->jobId).rawData()); - op->statRequested = true; -} - -void SftpChannelPrivate::handlePutHandle(const JobMap::Iterator &it) -{ - SftpUploadFile::Ptr op = it.value().staticCast<SftpUploadFile>(); - if (op->parentJob && op->parentJob->hasError) - sendTransferCloseHandle(op, it.key()); - - // OpenSSH does not implement the RFC's append functionality, so we - // have to emulate it. - if (op->mode == SftpAppendToExisting) { - sendData(m_outgoingPacket.generateFstat(op->remoteHandle, - op->jobId).rawData()); - op->statRequested = true; - } else { - spawnWriteRequests(it); - } -} - -void SftpChannelPrivate::handleStatus() -{ - const SftpStatusResponse &response = m_incomingPacket.asStatusResponse(); - qCDebug(sshLog, "%s: status = %d", Q_FUNC_INFO, response.status); - JobMap::Iterator it = lookupJob(response.requestId); - switch (it.value()->type()) { - case AbstractSftpOperation::ListDir: - handleLsStatus(it, response); - break; - case AbstractSftpOperation::Download: - handleGetStatus(it, response); - break; - case AbstractSftpOperation::UploadFile: - handlePutStatus(it, response); - break; - case AbstractSftpOperation::MakeDir: - handleMkdirStatus(it, response); - break; - case AbstractSftpOperation::StatFile: - case AbstractSftpOperation::RmDir: - case AbstractSftpOperation::Rm: - case AbstractSftpOperation::Rename: - case AbstractSftpOperation::CreateFile: - case AbstractSftpOperation::CreateLink: - handleStatusGeneric(it, response); - break; - } -} - -void SftpChannelPrivate::handleStatusGeneric(const JobMap::Iterator &it, - const SftpStatusResponse &response) -{ - AbstractSftpOperation::Ptr op = it.value(); - const QString error = errorMessage(response, tr("Unknown error.")); - emit finished(op->jobId, error); - m_jobs.erase(it); -} - -void SftpChannelPrivate::handleMkdirStatus(const JobMap::Iterator &it, - const SftpStatusResponse &response) -{ - SftpMakeDir::Ptr op = it.value().staticCast<SftpMakeDir>(); - QSharedPointer<SftpUploadDir> parentJob = op->parentJob; - if (parentJob == SftpUploadDir::Ptr()) { - handleStatusGeneric(it, response); - return; - } - if (parentJob->hasError) { - m_jobs.erase(it); - return; - } - - typedef QMap<SftpMakeDir::Ptr, SftpUploadDir::Dir>::Iterator DirIt; - DirIt dirIt = parentJob->mkdirsInProgress.find(op); - Q_ASSERT(dirIt != parentJob->mkdirsInProgress.end()); - const QString &remoteDir = dirIt.value().remoteDir; - if (response.status == SSH_FX_OK) { - emit dataAvailable(parentJob->jobId, - tr("Created remote directory \"%1\".").arg(remoteDir)); - } else if (response.status == SSH_FX_FAILURE) { - emit dataAvailable(parentJob->jobId, - tr("Remote directory \"%1\" already exists.").arg(remoteDir)); - } else { - parentJob->setError(); - emit finished(parentJob->jobId, - tr("Error creating directory \"%1\": %2") - .arg(remoteDir, response.errorString)); - m_jobs.erase(it); - return; - } - - QDir localDir(dirIt.value().localDir); - const QFileInfoList &dirInfos - = localDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); - foreach (const QFileInfo &dirInfo, dirInfos) { - const QString remoteSubDir = remoteDir + QLatin1Char('/') + dirInfo.fileName(); - const SftpMakeDir::Ptr mkdirOp( - new SftpMakeDir(++m_nextJobId, remoteSubDir, parentJob)); - parentJob->mkdirsInProgress.insert(mkdirOp, - SftpUploadDir::Dir(dirInfo.absoluteFilePath(), remoteSubDir)); - createJob(mkdirOp); - } - - const QFileInfoList &fileInfos = localDir.entryInfoList(QDir::Files); - foreach (const QFileInfo &fileInfo, fileInfos) { - QSharedPointer<QFile> localFile(new QFile(fileInfo.absoluteFilePath())); - if (!localFile->open(QIODevice::ReadOnly)) { - parentJob->setError(); - emit finished(parentJob->jobId, - tr("Could not open local file \"%1\": %2") - .arg(fileInfo.absoluteFilePath(), localFile->errorString())); - m_jobs.erase(it); - return; - } - - const QString remoteFilePath = remoteDir + QLatin1Char('/') + fileInfo.fileName(); - SftpUploadFile::Ptr uploadFileOp(new SftpUploadFile(++m_nextJobId, - remoteFilePath, localFile, SftpOverwriteExisting, parentJob)); - createJob(uploadFileOp); - parentJob->uploadsInProgress.append(uploadFileOp); - } - - parentJob->mkdirsInProgress.erase(dirIt); - if (parentJob->mkdirsInProgress.isEmpty() - && parentJob->uploadsInProgress.isEmpty()) - emit finished(parentJob->jobId); - m_jobs.erase(it); -} - -void SftpChannelPrivate::handleLsStatus(const JobMap::Iterator &it, - const SftpStatusResponse &response) -{ - SftpListDir::Ptr op = it.value().staticCast<SftpListDir>(); - switch (op->state) { - case SftpListDir::OpenRequested: - emit finished(op->jobId, errorMessage(response.errorString, - tr("Remote directory could not be opened for reading."))); - m_jobs.erase(it); - break; - case SftpListDir::Open: - if (response.status != SSH_FX_EOF) - reportRequestError(op, errorMessage(response.errorString, - tr("Failed to list remote directory contents."))); - op->state = SftpListDir::CloseRequested; - sendData(m_outgoingPacket.generateCloseHandle(op->remoteHandle, - op->jobId).rawData()); - break; - case SftpListDir::CloseRequested: - if (!op->hasError) { - const QString error = errorMessage(response, - tr("Failed to close remote directory.")); - emit finished(op->jobId, error); - } - m_jobs.erase(it); - break; - default: - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_FXP_STATUS packet."); - } -} - -void SftpChannelPrivate::handleGetStatus(const JobMap::Iterator &it, - const SftpStatusResponse &response) -{ - SftpDownload::Ptr op = it.value().staticCast<SftpDownload>(); - switch (op->state) { - case SftpDownload::OpenRequested: - emit finished(op->jobId, - errorMessage(response.errorString, - tr("Failed to open remote file for reading."))); - m_jobs.erase(it); - break; - case SftpDownload::Open: - if (op->statRequested) { - reportRequestError(op, errorMessage(response.errorString, - tr("Failed to retrieve information on the remote file ('stat' failed)."))); - sendTransferCloseHandle(op, response.requestId); - } else { - if ((response.status != SSH_FX_EOF || response.requestId != op->eofId) - && !op->hasError) - reportRequestError(op, errorMessage(response.errorString, - tr("Failed to read remote file."))); - finishTransferRequest(it); - } - break; - case SftpDownload::CloseRequested: - Q_ASSERT(op->inFlightCount == 1); - if (!op->hasError) { - if (response.status == SSH_FX_OK) - emit finished(op->jobId); - else - reportRequestError(op, errorMessage(response.errorString, - tr("Failed to close remote file."))); - } - removeTransferRequest(it); - break; - default: - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_FXP_STATUS packet."); - } -} - -void SftpChannelPrivate::handlePutStatus(const JobMap::Iterator &it, - const SftpStatusResponse &response) -{ - SftpUploadFile::Ptr job = it.value().staticCast<SftpUploadFile>(); - switch (job->state) { - case SftpUploadFile::OpenRequested: { - bool emitError = false; - if (job->parentJob) { - if (!job->parentJob->hasError) { - job->parentJob->setError(); - emitError = true; - } - } else { - emitError = true; - } - - if (emitError) { - emit finished(job->jobId, - errorMessage(response.errorString, - tr("Failed to open remote file for writing."))); - } - m_jobs.erase(it); - break; - } - case SftpUploadFile::Open: - if (job->hasError || (job->parentJob && job->parentJob->hasError)) { - job->hasError = true; - finishTransferRequest(it); - return; - } - - if (response.status == SSH_FX_OK) { - sendWriteRequest(it); - } else { - if (job->parentJob) - job->parentJob->setError(); - reportRequestError(job, errorMessage(response.errorString, - tr("Failed to write remote file."))); - finishTransferRequest(it); - } - break; - case SftpUploadFile::CloseRequested: - Q_ASSERT(job->inFlightCount == 1); - if (job->hasError || (job->parentJob && job->parentJob->hasError)) { - m_jobs.erase(it); - return; - } - - if (response.status == SSH_FX_OK) { - if (job->parentJob) { - job->parentJob->uploadsInProgress.removeOne(job); - if (job->parentJob->mkdirsInProgress.isEmpty() - && job->parentJob->uploadsInProgress.isEmpty()) - emit finished(job->parentJob->jobId); - } else { - emit finished(job->jobId); - } - } else { - const QString error = errorMessage(response.errorString, - tr("Failed to close remote file.")); - if (job->parentJob) { - job->parentJob->setError(); - emit finished(job->parentJob->jobId, error); - } else { - emit finished(job->jobId, error); - } - } - m_jobs.erase(it); - break; - default: - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_FXP_STATUS packet."); - } -} - -void SftpChannelPrivate::handleName() -{ - const SftpNameResponse &response = m_incomingPacket.asNameResponse(); - JobMap::Iterator it = lookupJob(response.requestId); - switch (it.value()->type()) { - case AbstractSftpOperation::ListDir: { - SftpListDir::Ptr op = it.value().staticCast<SftpListDir>(); - if (op->state != SftpListDir::Open) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_FXP_NAME packet."); - } - - QList<SftpFileInfo> fileInfoList; - for (int i = 0; i < response.files.count(); ++i) { - const SftpFile &file = response.files.at(i); - - SftpFileInfo fileInfo; - fileInfo.name = file.fileName; - attributesToFileInfo(file.attributes, fileInfo); - fileInfoList << fileInfo; - } - emit fileInfoAvailable(op->jobId, fileInfoList); - sendData(m_outgoingPacket.generateReadDir(op->remoteHandle, - op->jobId).rawData()); - break; - } - default: - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_FXP_NAME packet."); - } -} - -void SftpChannelPrivate::handleReadData() -{ - const SftpDataResponse &response = m_incomingPacket.asDataResponse(); - JobMap::Iterator it = lookupJob(response.requestId); - if (it.value()->type() != AbstractSftpOperation::Download) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_FXP_DATA packet."); - } - - SftpDownload::Ptr op = it.value().staticCast<SftpDownload>(); - if (op->hasError) { - finishTransferRequest(it); - return; - } - - if (!op->localFile->seek(op->offsets[response.requestId])) { - reportRequestError(op, op->localFile->errorString()); - finishTransferRequest(it); - return; - } - - if (op->localFile->write(response.data) != response.data.size()) { - reportRequestError(op, op->localFile->errorString()); - finishTransferRequest(it); - return; - } - - if (op->offset >= op->fileSize && op->fileSize != 0) - finishTransferRequest(it); - else - sendReadRequest(op, response.requestId); -} - -void SftpChannelPrivate::handleAttrs() -{ - const SftpAttrsResponse &response = m_incomingPacket.asAttrsResponse(); - JobMap::Iterator it = lookupJob(response.requestId); - - SftpStatFile::Ptr statOp = it.value().dynamicCast<SftpStatFile>(); - if (statOp) { - SftpFileInfo fileInfo; - fileInfo.name = QFileInfo(statOp->path).fileName(); - attributesToFileInfo(response.attrs, fileInfo); - emit fileInfoAvailable(it.key(), QList<SftpFileInfo>() << fileInfo); - emit finished(it.key()); - m_jobs.erase(it); - return; - } - - AbstractSftpTransfer::Ptr transfer - = it.value().dynamicCast<AbstractSftpTransfer>(); - if (!transfer || transfer->state != AbstractSftpTransfer::Open - || !transfer->statRequested) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_FXP_ATTRS packet."); - } - Q_ASSERT(transfer->type() == AbstractSftpOperation::UploadFile - || transfer->type() == AbstractSftpOperation::Download); - - if (transfer->type() == AbstractSftpOperation::Download) { - SftpDownload::Ptr op = transfer.staticCast<SftpDownload>(); - if (response.attrs.sizePresent) { - op->fileSize = response.attrs.size; - } else { - op->fileSize = 0; - op->eofId = op->jobId; - } - op->statRequested = false; - spawnReadRequests(op); - } else { - SftpUploadFile::Ptr op = transfer.staticCast<SftpUploadFile>(); - if (op->parentJob && op->parentJob->hasError) { - op->hasError = true; - sendTransferCloseHandle(op, op->jobId); - return; - } - - if (response.attrs.sizePresent) { - op->offset = response.attrs.size; - spawnWriteRequests(it); - } else { - if (op->parentJob) - op->parentJob->setError(); - reportRequestError(op, tr("Cannot append to remote file: " - "Server does not support the file size attribute.")); - sendTransferCloseHandle(op, op->jobId); - } - } -} - -SftpChannelPrivate::JobMap::Iterator SftpChannelPrivate::lookupJob(SftpJobId id) -{ - JobMap::Iterator it = m_jobs.find(id); - if (it == m_jobs.end()) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid request id in SFTP packet."); - } - return it; -} - -void SftpChannelPrivate::closeHook() -{ - for (JobMap::ConstIterator it = m_jobs.constBegin(); it != m_jobs.constEnd(); ++it) - emit finished(it.key(), tr("SFTP channel closed unexpectedly.")); - m_jobs.clear(); - m_incomingData.clear(); - m_incomingPacket.clear(); - emit closed(); -} - -void SftpChannelPrivate::handleOpenSuccessInternal() -{ - qCDebug(sshLog, "SFTP session started"); - m_sendFacility.sendSftpPacket(remoteChannel()); - m_sftpState = SubsystemRequested; -} - -void SftpChannelPrivate::handleOpenFailureInternal(const QString &reason) -{ - if (channelState() != SessionRequested) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE packet."); - } - emit channelError(tr("Server could not start session: %1").arg(reason)); -} - -void SftpChannelPrivate::sendReadRequest(const SftpDownload::Ptr &job, - quint32 requestId) -{ - Q_ASSERT(job->eofId == SftpInvalidJob); - sendData(m_outgoingPacket.generateReadFile(job->remoteHandle, job->offset, - AbstractSftpPacket::MaxDataSize, requestId).rawData()); - job->offsets[requestId] = job->offset; - job->offset += AbstractSftpPacket::MaxDataSize; - if (job->offset >= job->fileSize) - job->eofId = requestId; -} - -void SftpChannelPrivate::reportRequestError(const AbstractSftpOperationWithHandle::Ptr &job, - const QString &error) -{ - emit finished(job->jobId, error); - job->hasError = true; -} - -void SftpChannelPrivate::finishTransferRequest(const JobMap::Iterator &it) -{ - AbstractSftpTransfer::Ptr job = it.value().staticCast<AbstractSftpTransfer>(); - if (job->inFlightCount == 1) - sendTransferCloseHandle(job, it.key()); - else - removeTransferRequest(it); -} - -void SftpChannelPrivate::sendTransferCloseHandle(const AbstractSftpTransfer::Ptr &job, - quint32 requestId) -{ - sendData(m_outgoingPacket.generateCloseHandle(job->remoteHandle, - requestId).rawData()); - job->state = SftpDownload::CloseRequested; -} - -void SftpChannelPrivate::attributesToFileInfo(const SftpFileAttributes &attributes, - SftpFileInfo &fileInfo) const -{ - if (attributes.sizePresent) { - fileInfo.sizeValid = true; - fileInfo.size = attributes.size; - } - if (attributes.permissionsPresent) { - if (attributes.permissions & 0x8000) // S_IFREG - fileInfo.type = FileTypeRegular; - else if (attributes.permissions & 0x4000) // S_IFDIR - fileInfo.type = FileTypeDirectory; - else - fileInfo.type = FileTypeOther; - fileInfo.permissionsValid = true; - fileInfo.permissions = 0; - if (attributes.permissions & 00001) // S_IXOTH - fileInfo.permissions |= QFile::ExeOther; - if (attributes.permissions & 00002) // S_IWOTH - fileInfo.permissions |= QFile::WriteOther; - if (attributes.permissions & 00004) // S_IROTH - fileInfo.permissions |= QFile::ReadOther; - if (attributes.permissions & 00010) // S_IXGRP - fileInfo.permissions |= QFile::ExeGroup; - if (attributes.permissions & 00020) // S_IWGRP - fileInfo.permissions |= QFile::WriteGroup; - if (attributes.permissions & 00040) // S_IRGRP - fileInfo.permissions |= QFile::ReadGroup; - if (attributes.permissions & 00100) // S_IXUSR - fileInfo.permissions |= QFile::ExeUser | QFile::ExeOwner; - if (attributes.permissions & 00200) // S_IWUSR - fileInfo.permissions |= QFile::WriteUser | QFile::WriteOwner; - if (attributes.permissions & 00400) // S_IRUSR - fileInfo.permissions |= QFile::ReadUser | QFile::ReadOwner; - } - if (attributes.timesPresent) { - fileInfo.mtime = attributes.mtime; - fileInfo.mtimeValid = true; - } -} - -void SftpChannelPrivate::removeTransferRequest(const JobMap::Iterator &it) -{ - --it.value().staticCast<AbstractSftpTransfer>()->inFlightCount; - m_jobs.erase(it); -} - -void SftpChannelPrivate::sendWriteRequest(const JobMap::Iterator &it) -{ - SftpUploadFile::Ptr job = it.value().staticCast<SftpUploadFile>(); - QByteArray data = job->localFile->read(AbstractSftpPacket::MaxDataSize); - if (job->localFile->error() != QFile::NoError) { - if (job->parentJob) - job->parentJob->setError(); - reportRequestError(job, tr("Error reading local file: %1") - .arg(job->localFile->errorString())); - finishTransferRequest(it); - } else if (data.isEmpty()) { - finishTransferRequest(it); - } else { - sendData(m_outgoingPacket.generateWriteFile(job->remoteHandle, - job->offset, data, it.key()).rawData()); - job->offset += AbstractSftpPacket::MaxDataSize; - } -} - -void SftpChannelPrivate::spawnWriteRequests(const JobMap::Iterator &it) -{ - SftpUploadFile::Ptr op = it.value().staticCast<SftpUploadFile>(); - op->calculateInFlightCount(AbstractSftpPacket::MaxDataSize); - sendWriteRequest(it); - for (int i = 1; !op->hasError && i < op->inFlightCount; ++i) - sendWriteRequest(m_jobs.insert(++m_nextJobId, op)); -} - -void SftpChannelPrivate::spawnReadRequests(const SftpDownload::Ptr &job) -{ - job->calculateInFlightCount(AbstractSftpPacket::MaxDataSize); - sendReadRequest(job, job->jobId); - for (int i = 1; i < job->inFlightCount; ++i) { - const quint32 requestId = ++m_nextJobId; - m_jobs.insert(requestId, job); - sendReadRequest(job, requestId); - } -} - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sftpchannel.h b/src/libs/ssh/sftpchannel.h deleted file mode 100644 index 78857459ee7..00000000000 --- a/src/libs/ssh/sftpchannel.h +++ /dev/null @@ -1,103 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sftpdefs.h" -#include "sftpincomingpacket_p.h" - -#include "ssh_global.h" - -#include <QByteArray> -#include <QObject> -#include <QSharedPointer> -#include <QString> - -namespace QSsh { - -namespace Internal { -class SftpChannelPrivate; -class SshChannelManager; -class SshSendFacility; -} // namespace Internal - -class QSSH_EXPORT SftpChannel : public QObject -{ - Q_OBJECT - - friend class Internal::SftpChannelPrivate; - friend class Internal::SshChannelManager; -public: - typedef QSharedPointer<SftpChannel> Ptr; - - enum State { Uninitialized, Initializing, Initialized, Closing, Closed }; - State state() const; - - void initialize(); - void closeChannel(); - - SftpJobId statFile(const QString &path); - SftpJobId listDirectory(const QString &dirPath); - SftpJobId createDirectory(const QString &dirPath); - SftpJobId removeDirectory(const QString &dirPath); - SftpJobId removeFile(const QString &filePath); - SftpJobId renameFileOrDirectory(const QString &oldPath, - const QString &newPath); - SftpJobId createFile(const QString &filePath, SftpOverwriteMode mode); - SftpJobId createLink(const QString &filePath, const QString &target); - SftpJobId uploadFile(const QString &localFilePath, - const QString &remoteFilePath, SftpOverwriteMode mode); - SftpJobId downloadFile(const QString &remoteFilePath, - const QString &localFilePath, SftpOverwriteMode mode); - SftpJobId uploadDir(const QString &localDirPath, - const QString &remoteParentDirPath); - - ~SftpChannel(); - -signals: - void initialized(); - void channelError(const QString &reason); - void closed(); - - // error.isEmpty <=> finished successfully - void finished(QSsh::SftpJobId job, const QString &error = QString()); - - // TODO: Also emit for each file copied by uploadDir(). - void dataAvailable(QSsh::SftpJobId job, const QString &data); - - /* - * This signal is emitted as a result of: - * - statFile() (with the list having exactly one element) - * - listDirectory() (potentially more than once) - */ - void fileInfoAvailable(QSsh::SftpJobId job, const QList<QSsh::SftpFileInfo> &fileInfoList); - -private: - SftpChannel(quint32 channelId, Internal::SshSendFacility &sendFacility); - - Internal::SftpChannelPrivate *d; -}; - -} // namespace QSsh diff --git a/src/libs/ssh/sftpchannel_p.h b/src/libs/ssh/sftpchannel_p.h deleted file mode 100644 index 6ab1c3a2dba..00000000000 --- a/src/libs/ssh/sftpchannel_p.h +++ /dev/null @@ -1,124 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sftpdefs.h" -#include "sftpincomingpacket_p.h" -#include "sftpoperation_p.h" -#include "sftpoutgoingpacket_p.h" -#include "sshchannel_p.h" - -#include <QByteArray> -#include <QMap> - -namespace QSsh { -class SftpChannel; -namespace Internal { - -class SftpChannelPrivate : public AbstractSshChannel -{ - Q_OBJECT - friend class QSsh::SftpChannel; -public: - enum SftpState { Inactive, SubsystemRequested, InitSent, Initialized }; - -signals: - void initialized(); - void channelError(const QString &reason); - void closed(); - void finished(QSsh::SftpJobId job, const QString &error = QString()); - void dataAvailable(QSsh::SftpJobId job, const QString &data); - void fileInfoAvailable(QSsh::SftpJobId job, const QList<QSsh::SftpFileInfo> &fileInfoList); - -private: - typedef QMap<SftpJobId, AbstractSftpOperation::Ptr> JobMap; - - SftpChannelPrivate(quint32 channelId, SshSendFacility &sendFacility, - SftpChannel *sftp); - SftpJobId createJob(const AbstractSftpOperation::Ptr &job); - - virtual void handleChannelSuccess(); - virtual void handleChannelFailure(); - - virtual void handleOpenSuccessInternal(); - virtual void handleOpenFailureInternal(const QString &reason); - virtual void handleChannelDataInternal(const QByteArray &data); - virtual void handleChannelExtendedDataInternal(quint32 type, - const QByteArray &data); - virtual void handleExitStatus(const SshChannelExitStatus &exitStatus); - virtual void handleExitSignal(const SshChannelExitSignal &signal); - - virtual void closeHook(); - - void handleCurrentPacket(); - void handleServerVersion(); - void handleHandle(); - void handleStatus(); - void handleName(); - void handleReadData(); - void handleAttrs(); - - void handleStatusGeneric(const JobMap::Iterator &it, - const SftpStatusResponse &response); - void handleMkdirStatus(const JobMap::Iterator &it, - const SftpStatusResponse &response); - void handleLsStatus(const JobMap::Iterator &it, - const SftpStatusResponse &response); - void handleGetStatus(const JobMap::Iterator &it, - const SftpStatusResponse &response); - void handlePutStatus(const JobMap::Iterator &it, - const SftpStatusResponse &response); - - void handleLsHandle(const JobMap::Iterator &it); - void handleCreateFileHandle(const JobMap::Iterator &it); - void handleGetHandle(const JobMap::Iterator &it); - void handlePutHandle(const JobMap::Iterator &it); - - void spawnReadRequests(const SftpDownload::Ptr &job); - void spawnWriteRequests(const JobMap::Iterator &it); - void sendReadRequest(const SftpDownload::Ptr &job, quint32 requestId); - void sendWriteRequest(const JobMap::Iterator &it); - void finishTransferRequest(const JobMap::Iterator &it); - void removeTransferRequest(const JobMap::Iterator &it); - void reportRequestError(const AbstractSftpOperationWithHandle::Ptr &job, - const QString &error); - void sendTransferCloseHandle(const AbstractSftpTransfer::Ptr &job, - quint32 requestId); - - void attributesToFileInfo(const SftpFileAttributes &attributes, SftpFileInfo &fileInfo) const; - - JobMap::Iterator lookupJob(SftpJobId id); - JobMap m_jobs; - SftpOutgoingPacket m_outgoingPacket; - SftpIncomingPacket m_incomingPacket; - QByteArray m_incomingData; - SftpJobId m_nextJobId; - SftpState m_sftpState; - SftpChannel *m_sftp; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sftpdefs.h b/src/libs/ssh/sftpdefs.h index b996c38ca4f..e3d9be9b974 100644 --- a/src/libs/ssh/sftpdefs.h +++ b/src/libs/ssh/sftpdefs.h @@ -28,10 +28,32 @@ #include "ssh_global.h" #include <QFile> +#include <QList> #include <QString> +#include <memory> + namespace QSsh { +class SftpTransfer; +using SftpTransferPtr = std::unique_ptr<SftpTransfer>; +class SftpSession; +using SftpSessionPtr = std::unique_ptr<SftpSession>; + +enum class FileTransferErrorHandling { Abort, Ignore }; + +class FileToTransfer +{ +public: + FileToTransfer(const QString &source, const QString &target) + : sourceFile(source), targetFile(target) {} + QString sourceFile; + QString targetFile; +}; +using FilesToTransfer = QList<FileToTransfer>; + +namespace Internal { enum class FileTransferType { Upload, Download }; } + typedef quint32 SftpJobId; QSSH_EXPORT extern const SftpJobId SftpInvalidJob; @@ -48,12 +70,6 @@ public: SftpFileType type = FileTypeUnknown; quint64 size = 0; QFile::Permissions permissions; - quint32 mtime = 0; - - // The RFC allows an SFTP server not to support any file attributes beyond the name. - bool sizeValid = false; - bool permissionsValid = false; - bool mtimeValid = false; }; } // namespace QSsh diff --git a/src/libs/ssh/sftpfilesystemmodel.cpp b/src/libs/ssh/sftpfilesystemmodel.cpp index b1a173759c6..555796b812e 100644 --- a/src/libs/ssh/sftpfilesystemmodel.cpp +++ b/src/libs/ssh/sftpfilesystemmodel.cpp @@ -25,10 +25,12 @@ #include "sftpfilesystemmodel.h" -#include "sftpchannel.h" +#include "sftpsession.h" #include "sshconnection.h" #include "sshconnectionmanager.h" +#include <utils/qtcassert.h> + #include <QFileInfo> #include <QHash> #include <QIcon> @@ -71,7 +73,7 @@ SftpFileNode *indexToFileNode(const QModelIndex &index) SftpDirNode *indexToDirNode(const QModelIndex &index) { SftpFileNode * const fileNode = indexToFileNode(index); - QSSH_ASSERT(fileNode); + QTC_CHECK(fileNode); return dynamic_cast<SftpDirNode *>(fileNode); } @@ -81,7 +83,7 @@ class SftpFileSystemModelPrivate { public: SshConnection *sshConnection; - SftpChannel::Ptr sftpChannel; + SftpSessionPtr sftpSession; QString rootDirectory; SftpFileNode *rootNode; SftpJobId statJobId; @@ -109,9 +111,9 @@ SftpFileSystemModel::~SftpFileSystemModel() void SftpFileSystemModel::setSshConnection(const SshConnectionParameters &sshParams) { - QSSH_ASSERT_AND_RETURN(!d->sshConnection); + QTC_ASSERT(!d->sshConnection, return); d->sshConnection = QSsh::acquireConnection(sshParams); - connect(d->sshConnection, &SshConnection::error, + connect(d->sshConnection, &SshConnection::errorOccurred, this, &SftpFileSystemModel::handleSshConnectionFailure); if (d->sshConnection->state() == SshConnection::Connected) { handleSshConnectionEstablished(); @@ -142,12 +144,11 @@ QString SftpFileSystemModel::rootDirectory() const SftpJobId SftpFileSystemModel::downloadFile(const QModelIndex &index, const QString &targetFilePath) { - QSSH_ASSERT_AND_RETURN_VALUE(d->rootNode, SftpInvalidJob); + QTC_ASSERT(d->rootNode, return SftpInvalidJob); const SftpFileNode * const fileNode = indexToFileNode(index); - QSSH_ASSERT_AND_RETURN_VALUE(fileNode, SftpInvalidJob); - QSSH_ASSERT_AND_RETURN_VALUE(fileNode->fileInfo.type == FileTypeRegular, SftpInvalidJob); - const SftpJobId jobId = d->sftpChannel->downloadFile(fileNode->path, targetFilePath, - SftpOverwriteExisting); + QTC_ASSERT(fileNode, return SftpInvalidJob); + QTC_ASSERT(fileNode->fileInfo.type == FileTypeRegular, return SftpInvalidJob); + const SftpJobId jobId = d->sftpSession->downloadFile(fileNode->path, targetFilePath); if (jobId != SftpInvalidJob) d->externalJobs << jobId; return jobId; @@ -211,8 +212,8 @@ QModelIndex SftpFileSystemModel::index(int row, int column, const QModelIndex &p if (!parent.isValid()) return createIndex(row, column, d->rootNode); const SftpDirNode * const parentNode = indexToDirNode(parent); - QSSH_ASSERT_AND_RETURN_VALUE(parentNode, QModelIndex()); - QSSH_ASSERT_AND_RETURN_VALUE(row < parentNode->children.count(), QModelIndex()); + QTC_ASSERT(parentNode, return QModelIndex()); + QTC_ASSERT(row < parentNode->children.count(), return QModelIndex()); SftpFileNode * const childNode = parentNode->children.at(row); return createIndex(row, column, childNode); } @@ -223,14 +224,14 @@ QModelIndex SftpFileSystemModel::parent(const QModelIndex &child) const return QModelIndex(); const SftpFileNode * const childNode = indexToFileNode(child); - QSSH_ASSERT_AND_RETURN_VALUE(childNode, QModelIndex()); + QTC_ASSERT(childNode, return QModelIndex()); if (childNode == d->rootNode) return QModelIndex(); SftpDirNode * const parentNode = childNode->parent; if (parentNode == d->rootNode) return createIndex(0, 0, d->rootNode); const SftpDirNode * const grandParentNode = parentNode->parent; - QSSH_ASSERT_AND_RETURN_VALUE(grandParentNode, QModelIndex()); + QTC_ASSERT(grandParentNode, return QModelIndex()); return createIndex(grandParentNode->children.indexOf(parentNode), 0, parentNode); } @@ -247,22 +248,22 @@ int SftpFileSystemModel::rowCount(const QModelIndex &parent) const return 0; if (dirNode->lsState != SftpDirNode::LsNotYetCalled) return dirNode->children.count(); - d->lsOps.insert(d->sftpChannel->listDirectory(dirNode->path), dirNode); + d->lsOps.insert(d->sftpSession->ls(dirNode->path), dirNode); dirNode->lsState = SftpDirNode::LsRunning; return 0; } void SftpFileSystemModel::statRootDirectory() { - d->statJobId = d->sftpChannel->statFile(d->rootDirectory); + d->statJobId = d->sftpSession->ls(d->rootDirectory); } void SftpFileSystemModel::shutDown() { - if (d->sftpChannel) { - disconnect(d->sftpChannel.data(), 0, this, 0); - d->sftpChannel->closeChannel(); - d->sftpChannel.clear(); + if (d->sftpSession) { + disconnect(d->sftpSession.get(), 0, this, 0); + d->sftpSession->quit(); + d->sftpSession.release()->deleteLater(); } if (d->sshConnection) { disconnect(d->sshConnection, 0, this, 0); @@ -283,25 +284,24 @@ void SftpFileSystemModel::handleSshConnectionFailure() void SftpFileSystemModel::handleSftpChannelInitialized() { - connect(d->sftpChannel.data(), - &SftpChannel::fileInfoAvailable, + connect(d->sftpSession.get(), &SftpSession::fileInfoAvailable, this, &SftpFileSystemModel::handleFileInfo); - connect(d->sftpChannel.data(), &SftpChannel::finished, + connect(d->sftpSession.get(), &SftpSession::commandFinished, this, &SftpFileSystemModel::handleSftpJobFinished); statRootDirectory(); } void SftpFileSystemModel::handleSshConnectionEstablished() { - d->sftpChannel = d->sshConnection->createSftpChannel(); - connect(d->sftpChannel.data(), &SftpChannel::initialized, + d->sftpSession = d->sshConnection->createSftpSession(); + connect(d->sftpSession.get(), &SftpSession::started, this, &SftpFileSystemModel::handleSftpChannelInitialized); - connect(d->sftpChannel.data(), &SftpChannel::channelError, - this, &SftpFileSystemModel::handleSftpChannelError); - d->sftpChannel->initialize(); + connect(d->sftpSession.get(), &SftpSession::done, + this, &SftpFileSystemModel::handleSftpSessionClosed); + d->sftpSession->start(); } -void SftpFileSystemModel::handleSftpChannelError(const QString &reason) +void SftpFileSystemModel::handleSftpSessionClosed(const QString &reason) { emit connectionError(reason); beginResetModel(); @@ -312,18 +312,18 @@ void SftpFileSystemModel::handleSftpChannelError(const QString &reason) void SftpFileSystemModel::handleFileInfo(SftpJobId jobId, const QList<SftpFileInfo> &fileInfoList) { if (jobId == d->statJobId) { - QSSH_ASSERT_AND_RETURN(!d->rootNode); + QTC_ASSERT(!d->rootNode, return); beginInsertRows(QModelIndex(), 0, 0); d->rootNode = new SftpDirNode; d->rootNode->path = d->rootDirectory; - d->rootNode->fileInfo = fileInfoList.first(); + d->rootNode->fileInfo.type = FileTypeDirectory; d->rootNode->fileInfo.name = d->rootDirectory == QLatin1String("/") ? d->rootDirectory : QFileInfo(d->rootDirectory).fileName(); endInsertRows(); return; } SftpDirNode * const parentNode = d->lsOps.value(jobId); - QSSH_ASSERT_AND_RETURN(parentNode); + QTC_ASSERT(parentNode, return); QList<SftpFileInfo> filteredList; foreach (const SftpFileInfo &fi, fileInfoList) { if (fi.name != QLatin1String(".") && fi.name != QLatin1String("..")) @@ -358,14 +358,14 @@ void SftpFileSystemModel::handleSftpJobFinished(SftpJobId jobId, const QString & if (jobId == d->statJobId) { d->statJobId = SftpInvalidJob; if (!errorMessage.isEmpty()) - emit sftpOperationFailed(tr("Error getting \"stat\" info about \"%1\": %2") + emit sftpOperationFailed(tr("Error listing root directory \"%1\": %2") .arg(rootDirectory(), errorMessage)); return; } DirNodeHash::Iterator it = d->lsOps.find(jobId); if (it != d->lsOps.end()) { - QSSH_ASSERT(it.value()->lsState == SftpDirNode::LsRunning); + QTC_ASSERT(it.value()->lsState == SftpDirNode::LsRunning, return); it.value()->lsState = SftpDirNode::LsFinished; if (!errorMessage.isEmpty()) emit sftpOperationFailed(tr("Error listing contents of directory \"%1\": %2") @@ -375,7 +375,7 @@ void SftpFileSystemModel::handleSftpJobFinished(SftpJobId jobId, const QString & } const int jobIndex = d->externalJobs.indexOf(jobId); - QSSH_ASSERT_AND_RETURN(jobIndex != -1); + QTC_ASSERT(jobIndex != -1, return); d->externalJobs.removeAt(jobIndex); emit sftpOperationFinished(jobId, errorMessage); } diff --git a/src/libs/ssh/sftpfilesystemmodel.h b/src/libs/ssh/sftpfilesystemmodel.h index 99f26ee1e29..091dfabb4c3 100644 --- a/src/libs/ssh/sftpfilesystemmodel.h +++ b/src/libs/ssh/sftpfilesystemmodel.h @@ -80,7 +80,7 @@ private: void handleSshConnectionEstablished(); void handleSshConnectionFailure(); void handleSftpChannelInitialized(); - void handleSftpChannelError(const QString &reason); + void handleSftpSessionClosed(const QString &reason); void handleFileInfo(QSsh::SftpJobId jobId, const QList<QSsh::SftpFileInfo> &fileInfoList); void handleSftpJobFinished(QSsh::SftpJobId jobId, const QString &errorMessage); diff --git a/src/libs/ssh/sftpincomingpacket.cpp b/src/libs/ssh/sftpincomingpacket.cpp deleted file mode 100644 index 4f580a16c2e..00000000000 --- a/src/libs/ssh/sftpincomingpacket.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sftpincomingpacket_p.h" - -#include "sshexception_p.h" -#include "sshlogging_p.h" -#include "sshpacketparser_p.h" - -namespace QSsh { -namespace Internal { - -SftpIncomingPacket::SftpIncomingPacket() : m_length(0) -{ -} - -void SftpIncomingPacket::consumeData(QByteArray &newData) -{ - qCDebug(sshLog, "%s: current data size = %d, new data size = %d", Q_FUNC_INFO, - m_data.size(), newData.size()); - - if (isComplete() || dataSize() + newData.size() < sizeof m_length) - return; - - if (dataSize() < sizeof m_length) { - moveFirstBytes(m_data, newData, sizeof m_length - m_data.size()); - m_length = SshPacketParser::asUint32(m_data, static_cast<quint32>(0)); - if (m_length < static_cast<quint32>(TypeOffset + 1) - || m_length > MaxPacketSize) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid length field in SFTP packet."); - } - } - - moveFirstBytes(m_data, newData, - qMin<quint32>(m_length - dataSize() + 4, newData.size())); -} - -void SftpIncomingPacket::moveFirstBytes(QByteArray &target, QByteArray &source, - int n) -{ - target.append(source.left(n)); - source.remove(0, n); -} - -bool SftpIncomingPacket::isComplete() const -{ - return m_length == dataSize() - 4; -} - -void SftpIncomingPacket::clear() -{ - m_data.clear(); - m_length = 0; -} - -quint32 SftpIncomingPacket::extractServerVersion() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_FXP_VERSION); - try { - return SshPacketParser::asUint32(m_data, TypeOffset + 1); - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid SSH_FXP_VERSION packet."); - } -} - -SftpHandleResponse SftpIncomingPacket::asHandleResponse() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_FXP_HANDLE); - try { - SftpHandleResponse response; - quint32 offset = RequestIdOffset; - response.requestId = SshPacketParser::asUint32(m_data, &offset); - response.handle = SshPacketParser::asString(m_data, &offset); - return response; - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid SSH_FXP_HANDLE packet"); - } -} - -SftpStatusResponse SftpIncomingPacket::asStatusResponse() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_FXP_STATUS); - try { - SftpStatusResponse response; - quint32 offset = RequestIdOffset; - response.requestId = SshPacketParser::asUint32(m_data, &offset); - response.status = static_cast<SftpStatusCode>(SshPacketParser::asUint32(m_data, &offset)); - response.errorString = SshPacketParser::asUserString(m_data, &offset); - response.language = SshPacketParser::asString(m_data, &offset); - return response; - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid SSH_FXP_STATUS packet."); - } -} - -SftpNameResponse SftpIncomingPacket::asNameResponse() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_FXP_NAME); - try { - SftpNameResponse response; - quint32 offset = RequestIdOffset; - response.requestId = SshPacketParser::asUint32(m_data, &offset); - const quint32 count = SshPacketParser::asUint32(m_data, &offset); - for (quint32 i = 0; i < count; ++i) - response.files << asFile(offset); - return response; - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid SSH_FXP_NAME packet."); - } -} - -SftpDataResponse SftpIncomingPacket::asDataResponse() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_FXP_DATA); - try { - SftpDataResponse response; - quint32 offset = RequestIdOffset; - response.requestId = SshPacketParser::asUint32(m_data, &offset); - response.data = SshPacketParser::asString(m_data, &offset); - return response; - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid SSH_FXP_DATA packet."); - } -} - -SftpAttrsResponse SftpIncomingPacket::asAttrsResponse() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_FXP_ATTRS); - try { - SftpAttrsResponse response; - quint32 offset = RequestIdOffset; - response.requestId = SshPacketParser::asUint32(m_data, &offset); - response.attrs = asFileAttributes(offset); - return response; - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid SSH_FXP_ATTRS packet."); - } -} - -SftpFile SftpIncomingPacket::asFile(quint32 &offset) const -{ - SftpFile file; - file.fileName - = QString::fromUtf8(SshPacketParser::asString(m_data, &offset)); - file.longName - = QString::fromUtf8(SshPacketParser::asString(m_data, &offset)); - file.attributes = asFileAttributes(offset); - return file; -} - -SftpFileAttributes SftpIncomingPacket::asFileAttributes(quint32 &offset) const -{ - SftpFileAttributes attributes; - const quint32 flags = SshPacketParser::asUint32(m_data, &offset); - attributes.sizePresent = flags & SSH_FILEXFER_ATTR_SIZE; - attributes.timesPresent = flags & SSH_FILEXFER_ATTR_ACMODTIME; - attributes.uidAndGidPresent = flags & SSH_FILEXFER_ATTR_UIDGID; - attributes.permissionsPresent = flags & SSH_FILEXFER_ATTR_PERMISSIONS; - if (attributes.sizePresent) - attributes.size = SshPacketParser::asUint64(m_data, &offset); - if (attributes.uidAndGidPresent) { - attributes.uid = SshPacketParser::asUint32(m_data, &offset); - attributes.gid = SshPacketParser::asUint32(m_data, &offset); - } - if (attributes.permissionsPresent) - attributes.permissions = SshPacketParser::asUint32(m_data, &offset); - if (attributes.timesPresent) { - attributes.atime = SshPacketParser::asUint32(m_data, &offset); - attributes.mtime = SshPacketParser::asUint32(m_data, &offset); - } - if (flags & SSH_FILEXFER_ATTR_EXTENDED) { - const quint32 count = SshPacketParser::asUint32(m_data, &offset); - for (quint32 i = 0; i < count; ++i) { - SshPacketParser::asString(m_data, &offset); - SshPacketParser::asString(m_data, &offset); - } - } - return attributes; -} - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sftpincomingpacket_p.h b/src/libs/ssh/sftpincomingpacket_p.h deleted file mode 100644 index fe372e30fbc..00000000000 --- a/src/libs/ssh/sftpincomingpacket_p.h +++ /dev/null @@ -1,104 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sftppacket_p.h" - -namespace QSsh { -namespace Internal { - -struct SftpHandleResponse { - quint32 requestId; - QByteArray handle; -}; - -struct SftpStatusResponse { - quint32 requestId; - SftpStatusCode status; - QString errorString; - QByteArray language; -}; - -struct SftpFileAttributes { - bool sizePresent; - bool timesPresent; - bool uidAndGidPresent; - bool permissionsPresent; - quint64 size; - quint32 uid; - quint32 gid; - quint32 permissions; - quint32 atime; - quint32 mtime; -}; - -struct SftpFile { - QString fileName; - QString longName; // Not present in later RFCs, so we don't expose this to the user. - SftpFileAttributes attributes; -}; - -struct SftpNameResponse { - quint32 requestId; - QList<SftpFile> files; -}; - -struct SftpDataResponse { - quint32 requestId; - QByteArray data; -}; - -struct SftpAttrsResponse { - quint32 requestId; - SftpFileAttributes attrs; -}; - -class SftpIncomingPacket : public AbstractSftpPacket -{ -public: - SftpIncomingPacket(); - - void consumeData(QByteArray &data); - void clear(); - bool isComplete() const; - quint32 extractServerVersion() const; - SftpHandleResponse asHandleResponse() const; - SftpStatusResponse asStatusResponse() const; - SftpNameResponse asNameResponse() const; - SftpDataResponse asDataResponse() const; - SftpAttrsResponse asAttrsResponse() const; - -private: - void moveFirstBytes(QByteArray &target, QByteArray &source, int n); - - SftpFileAttributes asFileAttributes(quint32 &offset) const; - SftpFile asFile(quint32 &offset) const; - - quint32 m_length; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sftpoperation.cpp b/src/libs/ssh/sftpoperation.cpp deleted file mode 100644 index 257a8515429..00000000000 --- a/src/libs/ssh/sftpoperation.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sftpoperation_p.h" - -#include "sftpoutgoingpacket_p.h" - -#include <QFile> - -namespace QSsh { -namespace Internal { - -AbstractSftpOperation::AbstractSftpOperation(SftpJobId jobId) : jobId(jobId) -{ -} - -AbstractSftpOperation::~AbstractSftpOperation() { } - - -SftpStatFile::SftpStatFile(SftpJobId jobId, const QString &path) - : AbstractSftpOperation(jobId), path(path) -{ -} - -SftpOutgoingPacket &SftpStatFile::initialPacket(SftpOutgoingPacket &packet) -{ - return packet.generateStat(path, jobId); -} - -SftpMakeDir::SftpMakeDir(SftpJobId jobId, const QString &path, - const SftpUploadDir::Ptr &parentJob) - : AbstractSftpOperation(jobId), parentJob(parentJob), remoteDir(path) -{ -} - -SftpOutgoingPacket &SftpMakeDir::initialPacket(SftpOutgoingPacket &packet) -{ - return packet.generateMkDir(remoteDir, jobId); -} - - -SftpRmDir::SftpRmDir(SftpJobId id, const QString &path) - : AbstractSftpOperation(id), remoteDir(path) -{ -} - -SftpOutgoingPacket &SftpRmDir::initialPacket(SftpOutgoingPacket &packet) -{ - return packet.generateRmDir(remoteDir, jobId); -} - - -SftpRm::SftpRm(SftpJobId jobId, const QString &path) - : AbstractSftpOperation(jobId), remoteFile(path) {} - -SftpOutgoingPacket &SftpRm::initialPacket(SftpOutgoingPacket &packet) -{ - return packet.generateRm(remoteFile, jobId); -} - - -SftpRename::SftpRename(SftpJobId jobId, const QString &oldPath, - const QString &newPath) - : AbstractSftpOperation(jobId), oldPath(oldPath), newPath(newPath) -{ -} - -SftpOutgoingPacket &SftpRename::initialPacket(SftpOutgoingPacket &packet) -{ - return packet.generateRename(oldPath, newPath, jobId); -} - - -SftpCreateLink::SftpCreateLink(SftpJobId jobId, const QString &filePath, const QString &target) - : AbstractSftpOperation(jobId), filePath(filePath), target(target) -{ -} - -SftpOutgoingPacket &SftpCreateLink::initialPacket(SftpOutgoingPacket &packet) -{ - return packet.generateCreateLink(filePath, target, jobId); -} - - -AbstractSftpOperationWithHandle::AbstractSftpOperationWithHandle(SftpJobId jobId, - const QString &remotePath) - : AbstractSftpOperation(jobId), - remotePath(remotePath), state(Inactive), hasError(false) -{ -} - -AbstractSftpOperationWithHandle::~AbstractSftpOperationWithHandle() { } - - -SftpListDir::SftpListDir(SftpJobId jobId, const QString &path) - : AbstractSftpOperationWithHandle(jobId, path) -{ -} - -SftpOutgoingPacket &SftpListDir::initialPacket(SftpOutgoingPacket &packet) -{ - state = OpenRequested; - return packet.generateOpenDir(remotePath, jobId); -} - - -SftpCreateFile::SftpCreateFile(SftpJobId jobId, const QString &path, - SftpOverwriteMode mode) - : AbstractSftpOperationWithHandle(jobId, path), mode(mode) -{ -} - -SftpOutgoingPacket & SftpCreateFile::initialPacket(SftpOutgoingPacket &packet) -{ - state = OpenRequested; - return packet.generateOpenFileForWriting(remotePath, mode, - SftpOutgoingPacket::DefaultPermissions, jobId); -} - - -const int AbstractSftpTransfer::MaxInFlightCount = 10; // Experimentally found to be enough. - -AbstractSftpTransfer::AbstractSftpTransfer(SftpJobId jobId, const QString &remotePath, - const QSharedPointer<QFile> &localFile) - : AbstractSftpOperationWithHandle(jobId, remotePath), - localFile(localFile), fileSize(0), offset(0), inFlightCount(0), - statRequested(false) -{ -} - -AbstractSftpTransfer::~AbstractSftpTransfer() {} - -void AbstractSftpTransfer::calculateInFlightCount(quint32 chunkSize) -{ - if (fileSize == 0) { - inFlightCount = 1; - } else { - inFlightCount = fileSize / chunkSize; - if (fileSize % chunkSize) - ++inFlightCount; - if (inFlightCount > MaxInFlightCount) - inFlightCount = MaxInFlightCount; - } -} - - -SftpDownload::SftpDownload(SftpJobId jobId, const QString &remotePath, - const QSharedPointer<QFile> &localFile) - : AbstractSftpTransfer(jobId, remotePath, localFile), eofId(SftpInvalidJob) -{ -} - -SftpOutgoingPacket &SftpDownload::initialPacket(SftpOutgoingPacket &packet) -{ - state = OpenRequested; - return packet.generateOpenFileForReading(remotePath, jobId); -} - - -SftpUploadFile::SftpUploadFile(SftpJobId jobId, const QString &remotePath, - const QSharedPointer<QFile> &localFile, SftpOverwriteMode mode, - const SftpUploadDir::Ptr &parentJob) - : AbstractSftpTransfer(jobId, remotePath, localFile), - parentJob(parentJob), mode(mode) -{ - fileSize = localFile->size(); -} - -SftpOutgoingPacket &SftpUploadFile::initialPacket(SftpOutgoingPacket &packet) -{ - state = OpenRequested; - quint32 permissions = 0; - const QFile::Permissions &qtPermissions = localFile->permissions(); - if (qtPermissions & QFile::ExeOther) - permissions |= 1 << 0; - if (qtPermissions & QFile::WriteOther) - permissions |= 1 << 1; - if (qtPermissions & QFile::ReadOther) - permissions |= 1 << 2; - if (qtPermissions & QFile::ExeGroup) - permissions |= 1<< 3; - if (qtPermissions & QFile::WriteGroup) - permissions |= 1<< 4; - if (qtPermissions & QFile::ReadGroup) - permissions |= 1<< 5; - if (qtPermissions & QFile::ExeOwner) - permissions |= 1<< 6; - if (qtPermissions & QFile::WriteOwner) - permissions |= 1<< 7; - if (qtPermissions & QFile::ReadOwner) - permissions |= 1<< 8; - return packet.generateOpenFileForWriting(remotePath, mode, permissions, jobId); -} - -SftpUploadDir::~SftpUploadDir() {} - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sftpoperation_p.h b/src/libs/ssh/sftpoperation_p.h deleted file mode 100644 index 3ee703f6665..00000000000 --- a/src/libs/ssh/sftpoperation_p.h +++ /dev/null @@ -1,244 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sftpdefs.h" - -#include <QByteArray> -#include <QList> -#include <QMap> -#include <QSharedPointer> - -QT_BEGIN_NAMESPACE -class QFile; -QT_END_NAMESPACE - -namespace QSsh { -namespace Internal { - -class SftpOutgoingPacket; - -struct AbstractSftpOperation -{ - typedef QSharedPointer<AbstractSftpOperation> Ptr; - enum Type { - StatFile, ListDir, MakeDir, RmDir, Rm, Rename, CreateLink, CreateFile, Download, UploadFile - }; - - AbstractSftpOperation(SftpJobId jobId); - virtual ~AbstractSftpOperation(); - virtual Type type() const = 0; - virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet) = 0; - - const SftpJobId jobId; - -private: - AbstractSftpOperation(const AbstractSftpOperation &); - AbstractSftpOperation &operator=(const AbstractSftpOperation &); -}; - -struct SftpUploadDir; - -struct SftpStatFile : public AbstractSftpOperation -{ - typedef QSharedPointer<SftpStatFile> Ptr; - - SftpStatFile(SftpJobId jobId, const QString &path); - virtual Type type() const { return StatFile; } - virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet); - - const QString path; -}; - -struct SftpMakeDir : public AbstractSftpOperation -{ - typedef QSharedPointer<SftpMakeDir> Ptr; - - SftpMakeDir(SftpJobId jobId, const QString &path, - const QSharedPointer<SftpUploadDir> &parentJob = QSharedPointer<SftpUploadDir>()); - virtual Type type() const { return MakeDir; } - virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet); - - const QSharedPointer<SftpUploadDir> parentJob; - const QString remoteDir; -}; - -struct SftpRmDir : public AbstractSftpOperation -{ - typedef QSharedPointer<SftpRmDir> Ptr; - - SftpRmDir(SftpJobId id, const QString &path); - virtual Type type() const { return RmDir; } - virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet); - - const QString remoteDir; -}; - -struct SftpRm : public AbstractSftpOperation -{ - typedef QSharedPointer<SftpRm> Ptr; - - SftpRm(SftpJobId jobId, const QString &path); - virtual Type type() const { return Rm; } - virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet); - - const QString remoteFile; -}; - -struct SftpRename : public AbstractSftpOperation -{ - typedef QSharedPointer<SftpRename> Ptr; - - SftpRename(SftpJobId jobId, const QString &oldPath, const QString &newPath); - virtual Type type() const { return Rename; } - virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet); - - const QString oldPath; - const QString newPath; -}; - -struct SftpCreateLink : public AbstractSftpOperation -{ - typedef QSharedPointer<SftpCreateLink> Ptr; - - SftpCreateLink(SftpJobId jobId, const QString &filePath, const QString &target); - virtual Type type() const { return CreateLink; } - virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet); - - const QString filePath; - const QString target; -}; - - -struct AbstractSftpOperationWithHandle : public AbstractSftpOperation -{ - typedef QSharedPointer<AbstractSftpOperationWithHandle> Ptr; - enum State { Inactive, OpenRequested, Open, CloseRequested }; - - AbstractSftpOperationWithHandle(SftpJobId jobId, const QString &remotePath); - ~AbstractSftpOperationWithHandle(); - - const QString remotePath; - QByteArray remoteHandle; - State state; - bool hasError; -}; - - -struct SftpListDir : public AbstractSftpOperationWithHandle -{ - typedef QSharedPointer<SftpListDir> Ptr; - - SftpListDir(SftpJobId jobId, const QString &path); - virtual Type type() const { return ListDir; } - virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet); -}; - - -struct SftpCreateFile : public AbstractSftpOperationWithHandle -{ - typedef QSharedPointer<SftpCreateFile> Ptr; - - SftpCreateFile(SftpJobId jobId, const QString &path, SftpOverwriteMode mode); - virtual Type type() const { return CreateFile; } - virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet); - - const SftpOverwriteMode mode; -}; - -struct AbstractSftpTransfer : public AbstractSftpOperationWithHandle -{ - typedef QSharedPointer<AbstractSftpTransfer> Ptr; - - AbstractSftpTransfer(SftpJobId jobId, const QString &remotePath, - const QSharedPointer<QFile> &localFile); - ~AbstractSftpTransfer(); - void calculateInFlightCount(quint32 chunkSize); - - static const int MaxInFlightCount; - - const QSharedPointer<QFile> localFile; - quint64 fileSize; - quint64 offset; - int inFlightCount; - bool statRequested; -}; - -struct SftpDownload : public AbstractSftpTransfer -{ - typedef QSharedPointer<SftpDownload> Ptr; - SftpDownload(SftpJobId jobId, const QString &remotePath, - const QSharedPointer<QFile> &localFile); - virtual Type type() const { return Download; } - virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet); - - QMap<quint32, quint64> offsets; - SftpJobId eofId; -}; - -struct SftpUploadFile : public AbstractSftpTransfer -{ - typedef QSharedPointer<SftpUploadFile> Ptr; - - SftpUploadFile(SftpJobId jobId, const QString &remotePath, - const QSharedPointer<QFile> &localFile, SftpOverwriteMode mode, - const QSharedPointer<SftpUploadDir> &parentJob = QSharedPointer<SftpUploadDir>()); - virtual Type type() const { return UploadFile; } - virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet); - - const QSharedPointer<SftpUploadDir> parentJob; - SftpOverwriteMode mode; -}; - -// Composite operation. -struct SftpUploadDir -{ - typedef QSharedPointer<SftpUploadDir> Ptr; - - struct Dir { - Dir(const QString &l, const QString &r) : localDir(l), remoteDir(r) {} - QString localDir; - QString remoteDir; - }; - - SftpUploadDir(SftpJobId jobId) : jobId(jobId), hasError(false) {} - ~SftpUploadDir(); - - void setError() - { - hasError = true; - uploadsInProgress.clear(); - mkdirsInProgress.clear(); - } - - const SftpJobId jobId; - bool hasError; - QList<SftpUploadFile::Ptr> uploadsInProgress; - QMap<SftpMakeDir::Ptr, Dir> mkdirsInProgress; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sftpoutgoingpacket.cpp b/src/libs/ssh/sftpoutgoingpacket.cpp deleted file mode 100644 index 69c3a544ac3..00000000000 --- a/src/libs/ssh/sftpoutgoingpacket.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sftpoutgoingpacket_p.h" - -#include "sshlogging_p.h" -#include "sshpacket_p.h" - -#include <QtEndian> - -#include <limits> - -namespace QSsh { -namespace Internal { - -namespace { - const quint32 DefaultAttributes = 0; - const quint32 SSH_FXF_READ = 0x00000001; - const quint32 SSH_FXF_WRITE = 0x00000002; - const quint32 SSH_FXF_APPEND = 0x00000004; - const quint32 SSH_FXF_CREAT = 0x00000008; - const quint32 SSH_FXF_TRUNC = 0x00000010; - const quint32 SSH_FXF_EXCL = 0x00000020; -} - -SftpOutgoingPacket::SftpOutgoingPacket() -{ -} - -SftpOutgoingPacket &SftpOutgoingPacket::generateInit(quint32 version) -{ - return init(SSH_FXP_INIT, 0).appendInt(version).finalize(); -} - -SftpOutgoingPacket &SftpOutgoingPacket::generateStat(const QString &path, quint32 requestId) -{ - return init(SSH_FXP_LSTAT, requestId).appendString(path).finalize(); -} - -SftpOutgoingPacket &SftpOutgoingPacket::generateOpenDir(const QString &path, - quint32 requestId) -{ - return init(SSH_FXP_OPENDIR, requestId).appendString(path).finalize(); -} - -SftpOutgoingPacket &SftpOutgoingPacket::generateReadDir(const QByteArray &handle, - quint32 requestId) -{ - return init(SSH_FXP_READDIR, requestId).appendString(handle).finalize(); -} - -SftpOutgoingPacket &SftpOutgoingPacket::generateCloseHandle(const QByteArray &handle, - quint32 requestId) -{ - return init(SSH_FXP_CLOSE, requestId).appendString(handle).finalize(); -} - -SftpOutgoingPacket &SftpOutgoingPacket::generateMkDir(const QString &path, - quint32 requestId) -{ - return init(SSH_FXP_MKDIR, requestId).appendString(path) - .appendInt(DefaultAttributes).finalize(); -} - -SftpOutgoingPacket &SftpOutgoingPacket::generateRmDir(const QString &path, - quint32 requestId) -{ - return init(SSH_FXP_RMDIR, requestId).appendString(path).finalize(); -} - -SftpOutgoingPacket &SftpOutgoingPacket::generateRm(const QString &path, - quint32 requestId) -{ - return init(SSH_FXP_REMOVE, requestId).appendString(path).finalize(); -} - -SftpOutgoingPacket &SftpOutgoingPacket::generateRename(const QString &oldPath, - const QString &newPath, quint32 requestId) -{ - return init(SSH_FXP_RENAME, requestId).appendString(oldPath) - .appendString(newPath).finalize(); -} - -SftpOutgoingPacket &SftpOutgoingPacket::generateOpenFileForWriting(const QString &path, - SftpOverwriteMode mode, quint32 permissions, quint32 requestId) -{ - QList<quint32> attributes; - if (permissions != DefaultPermissions) - attributes << SSH_FILEXFER_ATTR_PERMISSIONS << permissions; - else - attributes << DefaultAttributes; - return generateOpenFile(path, Write, mode, attributes, requestId); -} - -SftpOutgoingPacket &SftpOutgoingPacket::generateOpenFileForReading(const QString &path, - quint32 requestId) -{ - // Note: Overwrite mode is irrelevant and will be ignored. - return generateOpenFile(path, Read, SftpSkipExisting, QList<quint32>() << DefaultAttributes, - requestId); -} - -SftpOutgoingPacket &SftpOutgoingPacket::generateReadFile(const QByteArray &handle, - quint64 offset, quint32 length, quint32 requestId) -{ - return init(SSH_FXP_READ, requestId).appendString(handle).appendInt64(offset) - .appendInt(length).finalize(); -} - -SftpOutgoingPacket &SftpOutgoingPacket::generateFstat(const QByteArray &handle, - quint32 requestId) -{ - return init(SSH_FXP_FSTAT, requestId).appendString(handle).finalize(); -} - -SftpOutgoingPacket &SftpOutgoingPacket::generateWriteFile(const QByteArray &handle, - quint64 offset, const QByteArray &data, quint32 requestId) -{ - return init(SSH_FXP_WRITE, requestId).appendString(handle) - .appendInt64(offset).appendString(data).finalize(); -} - -SftpOutgoingPacket &SftpOutgoingPacket::generateCreateLink(const QString &filePath, - const QString &target, quint32 requestId) -{ - return init(SSH_FXP_SYMLINK, requestId).appendString(filePath).appendString(target).finalize(); -} - -SftpOutgoingPacket &SftpOutgoingPacket::generateOpenFile(const QString &path, - OpenType openType, SftpOverwriteMode mode, const QList<quint32> &attributes, quint32 requestId) -{ - quint32 pFlags = 0; - switch (openType) { - case Read: - pFlags = SSH_FXF_READ; - break; - case Write: - pFlags = SSH_FXF_WRITE | SSH_FXF_CREAT; - switch (mode) { - case SftpOverwriteExisting: pFlags |= SSH_FXF_TRUNC; break; - case SftpAppendToExisting: pFlags |= SSH_FXF_APPEND; break; - case SftpSkipExisting: pFlags |= SSH_FXF_EXCL; break; - } - break; - } - - init(SSH_FXP_OPEN, requestId).appendString(path).appendInt(pFlags); - foreach (const quint32 attribute, attributes) - appendInt(attribute); - return finalize(); -} - -SftpOutgoingPacket &SftpOutgoingPacket::init(SftpPacketType type, - quint32 requestId) -{ - m_data.resize(TypeOffset + 1); - m_data[TypeOffset] = type; - if (type != SSH_FXP_INIT) { - appendInt(requestId); - qCDebug(sshLog, "Generating SFTP packet of type %d with request id %u", type, requestId); - } - return *this; -} - -SftpOutgoingPacket &SftpOutgoingPacket::appendInt(quint32 val) -{ - m_data.append(AbstractSshPacket::encodeInt(val)); - return *this; -} - -SftpOutgoingPacket &SftpOutgoingPacket::appendInt64(quint64 value) -{ - m_data.append(AbstractSshPacket::encodeInt(value)); - return *this; -} - -SftpOutgoingPacket &SftpOutgoingPacket::appendString(const QString &string) -{ - m_data.append(AbstractSshPacket::encodeString(string.toUtf8())); - return *this; -} - -SftpOutgoingPacket &SftpOutgoingPacket::appendString(const QByteArray &string) -{ - m_data += AbstractSshPacket::encodeString(string); - return *this; -} - -SftpOutgoingPacket &SftpOutgoingPacket::finalize() -{ - AbstractSshPacket::setLengthField(m_data); - return *this; -} - -const quint32 SftpOutgoingPacket::DefaultPermissions = std::numeric_limits<quint32>::max(); - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sftpoutgoingpacket_p.h b/src/libs/ssh/sftpoutgoingpacket_p.h deleted file mode 100644 index 0fcc67c1514..00000000000 --- a/src/libs/ssh/sftpoutgoingpacket_p.h +++ /dev/null @@ -1,84 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sftppacket_p.h" -#include "sftpdefs.h" - -namespace QSsh { -namespace Internal { - -class SftpOutgoingPacket : public AbstractSftpPacket -{ -public: - SftpOutgoingPacket(); - SftpOutgoingPacket &generateInit(quint32 version); - SftpOutgoingPacket &generateStat(const QString &path, quint32 requestId); - SftpOutgoingPacket &generateOpenDir(const QString &path, quint32 requestId); - SftpOutgoingPacket &generateReadDir(const QByteArray &handle, - quint32 requestId); - SftpOutgoingPacket &generateCloseHandle(const QByteArray &handle, - quint32 requestId); - SftpOutgoingPacket &generateMkDir(const QString &path, quint32 requestId); - SftpOutgoingPacket &generateRmDir(const QString &path, quint32 requestId); - SftpOutgoingPacket &generateRm(const QString &path, quint32 requestId); - SftpOutgoingPacket &generateRename(const QString &oldPath, - const QString &newPath, quint32 requestId); - SftpOutgoingPacket &generateOpenFileForWriting(const QString &path, - SftpOverwriteMode mode, quint32 permissions, quint32 requestId); - SftpOutgoingPacket &generateOpenFileForReading(const QString &path, - quint32 requestId); - SftpOutgoingPacket &generateReadFile(const QByteArray &handle, - quint64 offset, quint32 length, quint32 requestId); - SftpOutgoingPacket &generateFstat(const QByteArray &handle, - quint32 requestId); - SftpOutgoingPacket &generateWriteFile(const QByteArray &handle, - quint64 offset, const QByteArray &data, quint32 requestId); - - // Note: OpenSSH's SFTP server has a bug that reverses the filePath and target - // arguments, so this operation is not portable. - SftpOutgoingPacket &generateCreateLink(const QString &filePath, const QString &target, - quint32 requestId); - - static const quint32 DefaultPermissions; - -private: - static QByteArray encodeString(const QString &string); - - enum OpenType { Read, Write }; - SftpOutgoingPacket &generateOpenFile(const QString &path, OpenType openType, - SftpOverwriteMode mode, const QList<quint32> &attributes, quint32 requestId); - - SftpOutgoingPacket &init(SftpPacketType type, quint32 requestId); - SftpOutgoingPacket &appendInt(quint32 value); - SftpOutgoingPacket &appendInt64(quint64 value); - SftpOutgoingPacket &appendString(const QString &string); - SftpOutgoingPacket &appendString(const QByteArray &string); - SftpOutgoingPacket &finalize(); -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sftppacket.cpp b/src/libs/ssh/sftppacket.cpp deleted file mode 100644 index 4efaa35d7c7..00000000000 --- a/src/libs/ssh/sftppacket.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sftppacket_p.h" - -#include "sshpacketparser_p.h" - -namespace QSsh { -namespace Internal { - -const quint32 AbstractSftpPacket::MaxDataSize = 32000; -const quint32 AbstractSftpPacket::MaxPacketSize = 34000; -const int AbstractSftpPacket::TypeOffset = 4; -const int AbstractSftpPacket::RequestIdOffset = TypeOffset + 1; -const int AbstractSftpPacket::PayloadOffset = RequestIdOffset + 4; - -AbstractSftpPacket::AbstractSftpPacket() -{ -} - -quint32 AbstractSftpPacket::requestId() const -{ - return SshPacketParser::asUint32(m_data, RequestIdOffset); -} - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sftppacket_p.h b/src/libs/ssh/sftppacket_p.h deleted file mode 100644 index b99f7b36d67..00000000000 --- a/src/libs/ssh/sftppacket_p.h +++ /dev/null @@ -1,109 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include <QByteArray> -#include <QList> -#include <QString> - -namespace QSsh { -namespace Internal { - -enum SftpPacketType { - SSH_FXP_INIT = 1, - SSH_FXP_VERSION = 2, - SSH_FXP_OPEN = 3, - SSH_FXP_CLOSE = 4, - SSH_FXP_READ = 5, - SSH_FXP_WRITE = 6, - SSH_FXP_LSTAT = 7, - SSH_FXP_FSTAT = 8, - SSH_FXP_SETSTAT = 9, - SSH_FXP_FSETSTAT = 10, - SSH_FXP_OPENDIR = 11, - SSH_FXP_READDIR = 12, - SSH_FXP_REMOVE = 13, - SSH_FXP_MKDIR = 14, - SSH_FXP_RMDIR = 15, - SSH_FXP_REALPATH = 16, - SSH_FXP_STAT = 17, - SSH_FXP_RENAME = 18, - SSH_FXP_READLINK = 19, - SSH_FXP_SYMLINK = 20, // Removed from later protocol versions. Try not to use. - - SSH_FXP_STATUS = 101, - SSH_FXP_HANDLE = 102, - SSH_FXP_DATA = 103, - SSH_FXP_NAME = 104, - SSH_FXP_ATTRS = 105, - - SSH_FXP_EXTENDED = 200, - SSH_FXP_EXTENDED_REPLY = 201 -}; - -enum SftpStatusCode { - SSH_FX_OK = 0, - SSH_FX_EOF = 1, - SSH_FX_NO_SUCH_FILE = 2, - SSH_FX_PERMISSION_DENIED = 3, - SSH_FX_FAILURE = 4, - SSH_FX_BAD_MESSAGE = 5, - SSH_FX_NO_CONNECTION = 6, - SSH_FX_CONNECTION_LOST = 7, - SSH_FX_OP_UNSUPPORTED = 8 -}; - -enum SftpAttributeType { - SSH_FILEXFER_ATTR_SIZE = 0x00000001, - SSH_FILEXFER_ATTR_UIDGID = 0x00000002, - SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004, - SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008, - SSH_FILEXFER_ATTR_EXTENDED = 0x80000000 -}; - -class AbstractSftpPacket -{ -public: - AbstractSftpPacket(); - quint32 requestId() const; - const QByteArray &rawData() const { return m_data; } - SftpPacketType type() const { return static_cast<SftpPacketType>(m_data.at(TypeOffset)); } - - static const quint32 MaxDataSize; // "Pure" data size per read/writepacket. - static const quint32 MaxPacketSize; - -protected: - quint32 dataSize() const { return static_cast<quint32>(m_data.size()); } - - static const int TypeOffset; - static const int RequestIdOffset; - static const int PayloadOffset; - - QByteArray m_data; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sftpsession.cpp b/src/libs/ssh/sftpsession.cpp new file mode 100644 index 00000000000..2ba5db778aa --- /dev/null +++ b/src/libs/ssh/sftpsession.cpp @@ -0,0 +1,332 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "sftpsession.h" + +#include "sshlogging_p.h" +#include "sshprocess_p.h" +#include "sshsettings.h" + +#include <utils/fileutils.h> +#include <utils/qtcassert.h> +#include <utils/qtcprocess.h> + +#include <QByteArrayList> +#include <QFileInfo> +#include <QQueue> +#include <QTimer> + +using namespace Utils; + +namespace QSsh { +using namespace Internal; + +enum class CommandType { Ls, Mkdir, Rmdir, Rm, Rename, Ln, Put, Get, None }; + +struct Command +{ + Command() = default; + Command(CommandType t, const QStringList &p, SftpJobId id) : type(t), paths(p), jobId(id) {} + bool isValid() const { return type != CommandType::None; } + + CommandType type = CommandType::None; + QStringList paths; + SftpJobId jobId = SftpInvalidJob; +}; + +struct SftpSession::SftpSessionPrivate +{ + SshProcess sftpProc; + QStringList connectionArgs; + QByteArray output; + QQueue<Command> pendingCommands; + Command activeCommand; + SftpJobId nextJobId = 1; + SftpSession::State state = SftpSession::State::Inactive; + + QByteArray commandString(CommandType command) const + { + switch (command) { + case CommandType::Ls: return "ls -n"; + case CommandType::Mkdir: return "mkdir"; + case CommandType::Rmdir: return "rmdir"; + case CommandType::Rm: return "rm"; + case CommandType::Rename: return "rename"; + case CommandType::Ln: return "ln -s"; + case CommandType::Put: return "put"; + case CommandType::Get: return "get"; + default: QTC_ASSERT(false, return QByteArray()); + } + } + + SftpJobId queueCommand(CommandType command, const QStringList &paths) + { + const SftpJobId jobId = nextJobId++; + pendingCommands.enqueue(Command(command, paths, jobId)); + runNextCommand(); + return jobId; + } + + void runNextCommand() + { + if (activeCommand.isValid()) + return; + if (pendingCommands.empty()) + return; + QTC_ASSERT(sftpProc.state() == QProcess::Running, return); + activeCommand = pendingCommands.dequeue(); + + // The second newline forces the prompt to appear after the command has finished. + sftpProc.write(commandString(activeCommand.type) + ' ' + + QtcProcess::Arguments::createUnixArgs(activeCommand.paths) + .toString().toLocal8Bit() + "\n\n"); + } +}; + +static QByteArray prompt() { return "sftp> "; } + +SftpSession::SftpSession(const QStringList &connectionArgs) : d(new SftpSessionPrivate) +{ + d->connectionArgs = connectionArgs; + connect(&d->sftpProc, &QProcess::started, [this] { + d->sftpProc.write("\n"); // Force initial prompt. + }); + connect(&d->sftpProc, &QProcess::errorOccurred, [this](QProcess::ProcessError error) { + if (error == QProcess::FailedToStart) { + d->state = State::Inactive; + emit done(tr("sftp failed to start: %1").arg(d->sftpProc.errorString())); + } + }); + connect(&d->sftpProc, static_cast<void (QProcess::*)(int)>(&QProcess::finished), [this] { + d->state = State::Inactive; + if (d->sftpProc.exitStatus() != QProcess::NormalExit) { + emit done(tr("sftp crashed.")); + return; + } + if (d->sftpProc.exitCode() != 0) { + emit done(QString::fromLocal8Bit(d->sftpProc.readAllStandardError())); + return; + } + emit done(QString()); + }); + connect(&d->sftpProc, &QProcess::readyReadStandardOutput, this, &SftpSession::handleStdout); +} + +void SftpSession::doStart() +{ + if (d->state != State::Starting) + return; + const FileName sftpBinary = SshSettings::sftpFilePath(); + if (!sftpBinary.exists()) { + d->state = State::Inactive; + emit done(tr("Cannot establish SFTP session: sftp binary \"%1\" does not exist.") + .arg(sftpBinary.toUserOutput())); + return; + } + d->activeCommand = Command(); + const QStringList args = QStringList{"-q"} << d->connectionArgs; + qCDebug(sshLog) << "starting sftp session:" << sftpBinary.toUserOutput() << args; + d->sftpProc.start(sftpBinary.toString(), args); +} + +void SftpSession::handleStdout() +{ + if (state() == State::Running && !d->activeCommand.isValid()) { + qCWarning(sshLog) << "ignoring unexpected sftp output:" + << d->sftpProc.readAllStandardOutput(); + return; + } + + d->output += d->sftpProc.readAllStandardOutput(); + qCDebug(sshLog) << "accumulated sftp output:" << d->output; + const int firstPromptOffset = d->output.indexOf(prompt()); + if (firstPromptOffset == -1) + return; + if (state() == State::Starting) { + d->state = State::Running; + d->output.clear(); + d->sftpProc.readAllStandardError(); // The "connected" message goes to stderr. + emit started(); + return; + } + const int secondPromptOffset = d->output.indexOf(prompt(), firstPromptOffset + prompt().size()); + if (secondPromptOffset == -1) + return; + const Command command = d->activeCommand; + d->activeCommand = Command(); + const QByteArray commandOutput = d->output.mid( + firstPromptOffset + prompt().size(), + secondPromptOffset - firstPromptOffset - prompt().size()); + d->output = d->output.mid(secondPromptOffset + prompt().size()); + if (command.type == CommandType::Ls) + handleLsOutput(command.jobId, commandOutput); + const QByteArray stdErr = d->sftpProc.readAllStandardError(); + emit commandFinished(command.jobId, QString::fromLocal8Bit(stdErr)); + d->runNextCommand(); +} + +static SftpFileType typeFromLsOutput(char c) +{ + if (c == '-') + return FileTypeRegular; + if (c == 'd') + return FileTypeDirectory; + return FileTypeOther; +} + +static QFile::Permissions permissionsFromLsOutput(const QByteArray &output) +{ + QFile::Permissions perms; + if (output.at(0) == 'r') + perms |= QFile::ReadOwner; + if (output.at(1) == 'w') + perms |= QFile::WriteOwner; + if (output.at(2) == 'x') + perms |= QFile::ExeOwner; + if (output.at(3) == 'r') + perms |= QFile::ReadGroup; + if (output.at(4) == 'w') + perms |= QFile::WriteGroup; + if (output.at(5) == 'x') + perms |= QFile::ExeGroup; + if (output.at(6) == 'r') + perms |= QFile::ReadOther; + if (output.at(7) == 'w') + perms |= QFile::WriteOther; + if (output.at(8) == 'x') + perms |= QFile::ExeOther; + return perms; +} + +void SftpSession::handleLsOutput(SftpJobId jobId, const QByteArray &output) +{ + QList<SftpFileInfo> allFileInfo; + for (const QByteArray &line : output.split('\n')) { + if (line.startsWith("ls") || line.isEmpty()) + continue; + const QByteArrayList components = line.simplified().split(' '); + if (components.size() < 9) { + qCWarning(sshLog) << "Don't know how to parse sftp ls output:" << line; + continue; + } + const QByteArray typeAndPermissions = components.first(); + if (typeAndPermissions.size() != 10) { + qCWarning(sshLog) << "Don't know how to parse sftp ls output:" << line; + continue; + } + SftpFileInfo fileInfo; + fileInfo.type = typeFromLsOutput(typeAndPermissions.at(0)); + fileInfo.permissions = permissionsFromLsOutput(QByteArray::fromRawData( + typeAndPermissions.constData() + 1, + typeAndPermissions.size() - 1)); + bool isNumber; + fileInfo.size = components.at(4).toULongLong(&isNumber); + if (!isNumber) { + qCWarning(sshLog) << "Don't know how to parse sftp ls output:" << line; + continue; + } + // TODO: This will not work for file names with weird whitespace combinations + fileInfo.name = QFileInfo(QString::fromUtf8(components.mid(8).join(' '))).fileName(); + allFileInfo << fileInfo; + } + emit fileInfoAvailable(jobId, allFileInfo); +} + +SftpSession::~SftpSession() +{ + quit(); + delete d; +} + +void SftpSession::start() +{ + QTC_ASSERT(d->state == State::Inactive, return); + d->state = State::Starting; + QTimer::singleShot(0, this, &SftpSession::doStart); +} + +void SftpSession::quit() +{ + switch (state()) { + case State::Starting: + case State::Closing: + d->state = State::Closing; + d->sftpProc.kill(); + break; + case State::Running: + d->state = State::Closing; + d->sftpProc.write("bye\n"); + break; + case State::Inactive: + break; + } +} + +SftpJobId SftpSession::ls(const QString &path) +{ + return d->queueCommand(CommandType::Ls, QStringList(path)); +} + +SftpJobId SftpSession::createDirectory(const QString &path) +{ + return d->queueCommand(CommandType::Mkdir, QStringList(path)); +} + +SftpJobId SftpSession::removeDirectory(const QString &path) +{ + return d->queueCommand(CommandType::Rmdir, QStringList(path)); +} + +SftpJobId SftpSession::removeFile(const QString &path) +{ + return d->queueCommand(CommandType::Rm, QStringList(path)); +} + +SftpJobId SftpSession::rename(const QString &oldPath, const QString &newPath) +{ + return d->queueCommand(CommandType::Rename, QStringList{oldPath, newPath}); +} + +SftpJobId SftpSession::createSoftLink(const QString &filePath, const QString &target) +{ + return d->queueCommand(CommandType::Ln, QStringList{filePath, target}); +} + +SftpJobId SftpSession::uploadFile(const QString &localFilePath, const QString &remoteFilePath) +{ + return d->queueCommand(CommandType::Put, QStringList{localFilePath, remoteFilePath}); +} + +SftpJobId SftpSession::downloadFile(const QString &remoteFilePath, const QString &localFilePath) +{ + return d->queueCommand(CommandType::Get, QStringList{remoteFilePath, localFilePath}); +} + +SftpSession::State SftpSession::state() const +{ + return d->state; +} + +} // namespace QSsh diff --git a/src/libs/ssh/sshtcpipforwardserver.h b/src/libs/ssh/sftpsession.h index 2a0ed7e32b6..89d0eec145d 100644 --- a/src/libs/ssh/sshtcpipforwardserver.h +++ b/src/libs/ssh/sftpsession.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -25,57 +25,49 @@ #pragma once +#include "sftpdefs.h" #include "ssh_global.h" -#include "sshforwardedtcpiptunnel.h" + #include <QObject> namespace QSsh { +class SshConnection; -namespace Internal { -class SshChannelManager; -class SshTcpIpForwardServerPrivate; -class SshSendFacility; -class SshConnectionPrivate; -} // namespace Internal - -class QSSH_EXPORT SshTcpIpForwardServer : public QObject +class QSSH_EXPORT SftpSession : public QObject { + friend class SshConnection; Q_OBJECT - friend class Internal::SshChannelManager; - friend class Internal::SshConnectionPrivate; - public: - enum State { - Inactive, - Initializing, - Listening, - Closing - }; + ~SftpSession() override; + void start(); + void quit(); - typedef QSharedPointer<SshTcpIpForwardServer> Ptr; - ~SshTcpIpForwardServer(); + SftpJobId ls(const QString &path); + SftpJobId createDirectory(const QString &path); + SftpJobId removeDirectory(const QString &path); + SftpJobId removeFile(const QString &path); + SftpJobId rename(const QString &oldPath, const QString &newPath); + SftpJobId createSoftLink(const QString &filePath, const QString &target); + SftpJobId uploadFile(const QString &localFilePath, const QString &remoteFilePath); + SftpJobId downloadFile(const QString &remoteFilePath, const QString &localFilePath); - const QString &bindAddress() const; - quint16 port() const; + enum class State { Inactive, Starting, Running, Closing }; State state() const; - void initialize(); - void close(); - - SshForwardedTcpIpTunnel::Ptr nextPendingConnection(); signals: - void error(const QString &reason); - void newConnection(); - void stateChanged(State state); + void started(); + void done(const QString &error); + void commandFinished(SftpJobId job, const QString &error); + void fileInfoAvailable(SftpJobId job, const QList<SftpFileInfo> &fileInfoList); private: - SshTcpIpForwardServer(const QString &bindAddress, quint16 bindPort, - Internal::SshSendFacility &sendFacility); - void setListening(quint16 port); - void setClosed(); - void setNewConnection(const SshForwardedTcpIpTunnel::Ptr &connection); + SftpSession(const QStringList &connectionArgs); + void doStart(); + void handleStdout(); + void handleLsOutput(SftpJobId jobId, const QByteArray &output); - Internal::SshTcpIpForwardServerPrivate * const d; + struct SftpSessionPrivate; + SftpSessionPrivate * const d; }; } // namespace QSsh diff --git a/src/libs/ssh/sftptransfer.cpp b/src/libs/ssh/sftptransfer.cpp new file mode 100644 index 00000000000..3bfc89713d7 --- /dev/null +++ b/src/libs/ssh/sftptransfer.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "sftptransfer.h" + +#include "sshprocess_p.h" +#include "sshsettings.h" + +#include <QDir> +#include <QFileInfo> +#include <QStringList> +#include <QTemporaryFile> +#include <QTimer> + +#include <utils/algorithm.h> +#include <utils/qtcprocess.h> + +using namespace Utils; + +namespace QSsh { + +struct SftpTransfer::SftpTransferPrivate +{ + Internal::SshProcess sftpProc; + FilesToTransfer files; + Internal::FileTransferType transferType; + FileTransferErrorHandling errorHandlingMode; + QStringList connectionArgs; + QTemporaryFile batchFile; + + QStringList dirsToCreate() const + { + QStringList dirs; + for (const FileToTransfer &f : qAsConst(files)) { + QString parentDir = QFileInfo(f.targetFile).path(); + while (true) { + if (dirs.contains(parentDir) || !parentDir.startsWith('/')) + break; + dirs << parentDir; + parentDir = QFileInfo(parentDir).path(); + } + } + sort(dirs, [](const QString &d1, const QString &d2) { + if (d1 == "/" && d2 != "/") + return true; + return d1.count('/') < d2.count('/'); + }); + return dirs; + } + QByteArray transferCommand(bool link) const + { + QByteArray command; + switch (transferType) { + case Internal::FileTransferType::Upload: + command = link ? "ln -s" : "put"; + break; + case Internal::FileTransferType::Download: + command = "get"; + break; + } + if (errorHandlingMode == FileTransferErrorHandling::Ignore) + command.prepend('-'); + return command; + } +}; + +SftpTransfer::~SftpTransfer() +{ + delete d; +} + +void SftpTransfer::start() +{ + QTimer::singleShot(0, this, &SftpTransfer::doStart); +} + +void SftpTransfer::stop() +{ + d->sftpProc.terminate(); +} + +SftpTransfer::SftpTransfer(const FilesToTransfer &files, Internal::FileTransferType type, + FileTransferErrorHandling errorHandlingMode, + const QStringList &connectionArgs) + : d(new SftpTransferPrivate) +{ + d->files = files; + d->transferType = type; + d->errorHandlingMode = errorHandlingMode; + d->connectionArgs = connectionArgs; + connect(&d->sftpProc, &QProcess::errorOccurred, [this](QProcess::ProcessError error) { + if (error == QProcess::FailedToStart) + emitError(tr("sftp failed to start: %1").arg(d->sftpProc.errorString())); + }); + connect(&d->sftpProc, static_cast<void (QProcess::*)(int)>(&QProcess::finished), [this] { + if (d->sftpProc.exitStatus() != QProcess::NormalExit) { + emitError(tr("sftp crashed.")); + return; + } + if (d->sftpProc.exitCode() != 0) { + emitError(QString::fromLocal8Bit(d->sftpProc.readAllStandardError())); + return; + } + emit done(QString()); + }); + connect(&d->sftpProc, &QProcess::readyReadStandardOutput, [this] { + emit progress(QString::fromLocal8Bit(d->sftpProc.readAllStandardOutput())); + }); +} + +void SftpTransfer::doStart() +{ + const FileName sftpBinary = SshSettings::sftpFilePath(); + if (!sftpBinary.exists()) { + emitError(tr("sftp binary \"%1\" does not exist.").arg(sftpBinary.toUserOutput())); + return; + } + if (!d->batchFile.isOpen() && !d->batchFile.open()) { + emitError(tr("Could not create temporary file: %1").arg(d->batchFile.errorString())); + return; + } + d->batchFile.resize(0); + for (const QString &dir : d->dirsToCreate()) { + switch (d->transferType) { + case Internal::FileTransferType::Upload: + d->batchFile.write("-mkdir " + QtcProcess::quoteArgUnix(dir).toLocal8Bit() + '\n'); + break; + case Internal::FileTransferType::Download: + if (!QDir::root().mkdir(dir)) { + emitError(tr("Failed to create local directory \"%1\"") + .arg(QDir::toNativeSeparators(dir))); + return; + } + break; + } + } + for (const FileToTransfer &f : d->files) { + bool link = false; + if (d->transferType == Internal::FileTransferType::Upload) { + QFileInfo fi(f.sourceFile); + if (fi.isSymLink()) { + link = true; + const QString target = fi.dir().relativeFilePath(fi.symLinkTarget()); // see QTBUG-5817. + d->batchFile.write("-rm " + QtcProcess::quoteArgUnix(f.targetFile).toLocal8Bit() + + '\n'); + } + } + d->batchFile.write(d->transferCommand(link) + ' ' + + QtcProcess::quoteArgUnix(f.sourceFile).toLocal8Bit() + ' ' + + QtcProcess::quoteArgUnix(f.targetFile).toLocal8Bit() + '\n'); + } + d->batchFile.flush(); + d->sftpProc.start(sftpBinary.toString(), + QStringList{"-b", d->batchFile.fileName()} << d->connectionArgs); + emit started(); +} + +void SftpTransfer::emitError(const QString &details) +{ + emit done(tr("File transfer failed: %1").arg(details)); +} + +} // namespace QSsh diff --git a/src/libs/ssh/sshx11inforetriever_p.h b/src/libs/ssh/sftptransfer.h index fc27672f4f1..4c1c9cb6364 100644 --- a/src/libs/ssh/sshx11inforetriever_p.h +++ b/src/libs/ssh/sftptransfer.h @@ -25,41 +25,38 @@ #pragma once +#include "sftpdefs.h" #include "ssh_global.h" #include <QObject> -#include <QString> - -QT_BEGIN_NAMESPACE -class QByteArray; -class QProcess; -class QTemporaryFile; -QT_END_NAMESPACE namespace QSsh { -namespace Internal { -class X11DisplayInfo; +class SshConnection; -class QSSH_AUTOTEST_EXPORT SshX11InfoRetriever : public QObject +class QSSH_EXPORT SftpTransfer : public QObject { + friend class SshConnection; Q_OBJECT public: - SshX11InfoRetriever(const QString &displayName, QObject *parent = nullptr); + ~SftpTransfer(); + void start(); + void stop(); signals: - void failure(const QString &message); - void success(const X11DisplayInfo &displayInfo); + void started(); + void done(const QString &error); + void progress(const QString &output); private: - void emitFailure(const QString &reason); - - const QString m_displayName; - QProcess * const m_xauthProc; - QTemporaryFile * const m_xauthFile; - - enum class State { Inactive, RunningGenerate, RunningList } m_state = State::Inactive; + SftpTransfer(const FilesToTransfer &files, Internal::FileTransferType type, + FileTransferErrorHandling errorHandlingMode, + const QStringList &connectionArgs); + void doStart(); + void emitError(const QString &details); + + struct SftpTransferPrivate; + SftpTransferPrivate * const d; }; -} // namespace Internal } // namespace QSsh diff --git a/src/libs/ssh/ssh.pro b/src/libs/ssh/ssh.pro index 26f8a2ab54c..2dc628c49ea 100644 --- a/src/libs/ssh/ssh.pro +++ b/src/libs/ssh/ssh.pro @@ -3,100 +3,35 @@ DEFINES += QTCSSH_LIBRARY include(../../qtcreatorlibrary.pri) -SOURCES = $$PWD/sshsendfacility.cpp \ - $$PWD/sshremoteprocess.cpp \ - $$PWD/sshpacketparser.cpp \ - $$PWD/sshpacket.cpp \ - $$PWD/sshoutgoingpacket.cpp \ - $$PWD/sshkeygenerator.cpp \ - $$PWD/sshkeyexchange.cpp \ - $$PWD/sshincomingpacket.cpp \ - $$PWD/sshcryptofacility.cpp \ - $$PWD/sshconnection.cpp \ - $$PWD/sshchannelmanager.cpp \ - $$PWD/sshchannel.cpp \ - $$PWD/sshcapabilities.cpp \ - $$PWD/sftppacket.cpp \ - $$PWD/sftpoutgoingpacket.cpp \ - $$PWD/sftpoperation.cpp \ - $$PWD/sftpincomingpacket.cpp \ - $$PWD/sftpdefs.cpp \ - $$PWD/sftpchannel.cpp \ - $$PWD/sshremoteprocessrunner.cpp \ - $$PWD/sshconnectionmanager.cpp \ - $$PWD/sshkeypasswordretriever.cpp \ - $$PWD/sftpfilesystemmodel.cpp \ - $$PWD/sshkeycreationdialog.cpp \ - $$PWD/sshdirecttcpiptunnel.cpp \ - $$PWD/sshlogging.cpp \ - $$PWD/sshhostkeydatabase.cpp \ - $$PWD/sshtcpipforwardserver.cpp \ - $$PWD/sshtcpiptunnel.cpp \ - $$PWD/sshforwardedtcpiptunnel.cpp \ - $$PWD/sshagent.cpp \ - $$PWD/opensshkeyfilereader.cpp \ - $$PWD/sshx11channel.cpp \ - $$PWD/sshx11inforetriever.cpp +SOURCES = \ + sftpdefs.cpp \ + sftpfilesystemmodel.cpp \ + sftpsession.cpp \ + sftptransfer.cpp \ + sshconnection.cpp \ + sshconnectionmanager.cpp \ + sshkeycreationdialog.cpp \ + sshlogging.cpp \ + sshprocess.cpp \ + sshremoteprocess.cpp \ + sshremoteprocessrunner.cpp \ + sshsettings.cpp -HEADERS = $$PWD/sshsendfacility_p.h \ - $$PWD/sshremoteprocess.h \ - $$PWD/sshremoteprocess_p.h \ - $$PWD/sshpacketparser_p.h \ - $$PWD/sshpacket_p.h \ - $$PWD/sshoutgoingpacket_p.h \ - $$PWD/sshkeygenerator.h \ - $$PWD/sshkeyexchange_p.h \ - $$PWD/sshincomingpacket_p.h \ - $$PWD/sshexception_p.h \ - $$PWD/ssherrors.h \ - $$PWD/sshcryptofacility_p.h \ - $$PWD/sshconnection.h \ - $$PWD/sshconnection_p.h \ - $$PWD/sshchannelmanager_p.h \ - $$PWD/sshchannel_p.h \ - $$PWD/sshcapabilities_p.h \ - $$PWD/sshbotanconversions_p.h \ - $$PWD/sftppacket_p.h \ - $$PWD/sftpoutgoingpacket_p.h \ - $$PWD/sftpoperation_p.h \ - $$PWD/sftpincomingpacket_p.h \ - $$PWD/sftpdefs.h \ - $$PWD/sftpchannel.h \ - $$PWD/sftpchannel_p.h \ - $$PWD/sshremoteprocessrunner.h \ - $$PWD/sshconnectionmanager.h \ - $$PWD/sshpseudoterminal.h \ - $$PWD/sshkeypasswordretriever_p.h \ - $$PWD/sftpfilesystemmodel.h \ - $$PWD/sshkeycreationdialog.h \ - $$PWD/ssh_global.h \ - $$PWD/sshdirecttcpiptunnel_p.h \ - $$PWD/sshdirecttcpiptunnel.h \ - $$PWD/sshlogging_p.h \ - $$PWD/sshhostkeydatabase.h \ - $$PWD/sshtcpipforwardserver.h \ - $$PWD/sshtcpipforwardserver_p.h \ - $$PWD/sshtcpiptunnel_p.h \ - $$PWD/sshforwardedtcpiptunnel.h \ - $$PWD/sshforwardedtcpiptunnel_p.h \ - $$PWD/sshagent_p.h \ - $$PWD/opensshkeyfilereader_p.h \ - $$PWD/sshx11channel_p.h \ - $$PWD/sshx11displayinfo_p.h \ - $$PWD/sshx11inforetriever_p.h +HEADERS = \ + sftpdefs.h \ + sftpfilesystemmodel.h \ + sftpsession.h \ + sftptransfer.h \ + sshconnection.h \ + sshconnectionmanager.h \ + sshkeycreationdialog.h \ + sshlogging_p.h \ + sshprocess_p.h \ + sshremoteprocess.h \ + sshremoteprocessrunner.h \ + sshsettings.h \ + ssh_global.h FORMS = $$PWD/sshkeycreationdialog.ui RESOURCES += $$PWD/ssh.qrc - -include(../botan/botan.pri) -use_system_botan { - CONFIG += link_pkgconfig - PKGCONFIG += botan-2 -} else { - BOTAN_BUILD_DIR = $$OUT_PWD/../botan/$$BOTAN_BUILD_DIR - INCLUDEPATH += $$BOTAN_BUILD_DIR/build/include - LIBS += $$BOTAN_BUILD_DIR/$$BOTAN_FULL_NAME - win32: LIBS += -ladvapi32 -luser32 -lws2_32 -} -msvc:QMAKE_CXXFLAGS += /wd4250 diff --git a/src/libs/ssh/ssh.qbs b/src/libs/ssh/ssh.qbs index 27b3368d0e2..4999d5da262 100644 --- a/src/libs/ssh/ssh.qbs +++ b/src/libs/ssh/ssh.qbs @@ -1,5 +1,4 @@ import qbs 1.0 -import qbs.Environment Project { name: "QtcSsh" @@ -8,120 +7,40 @@ Project { QtcLibrary { cpp.defines: base.concat("QTCSSH_LIBRARY") - cpp.includePaths: botanIncludes - cpp.dynamicLibraries: botanLibs cpp.enableExceptions: true - Properties { - condition: qbs.toolchain.contains("msvc") - cpp.cxxFlags: base.concat("/wd4250"); - } - Depends { name: "Qt"; submodules: ["widgets", "network" ] } - Depends { name: "Botan"; condition: !qtc.useSystemBotan } + Depends { name: "Utils" } files: [ - "opensshkeyfilereader.cpp", - "opensshkeyfilereader_p.h", - "sftpchannel.h", - "sftpchannel_p.h", - "sftpchannel.cpp", "sftpdefs.cpp", "sftpdefs.h", "sftpfilesystemmodel.cpp", "sftpfilesystemmodel.h", - "sftpincomingpacket.cpp", - "sftpincomingpacket_p.h", - "sftpoperation.cpp", - "sftpoperation_p.h", - "sftpoutgoingpacket.cpp", - "sftpoutgoingpacket_p.h", - "sftppacket.cpp", - "sftppacket_p.h", + "sftpsession.cpp", + "sftpsession.h", + "sftptransfer.cpp", + "sftptransfer.h", "ssh.qrc", - "sshagent.cpp", - "sshagent_p.h", - "sshbotanconversions_p.h", - "sshcapabilities_p.h", - "sshcapabilities.cpp", - "sshchannel.cpp", - "sshchannel_p.h", - "sshchannelmanager.cpp", - "sshchannelmanager_p.h", "sshconnection.h", - "sshconnection_p.h", "sshconnection.cpp", "sshconnectionmanager.cpp", "sshconnectionmanager.h", - "sshcryptofacility.cpp", - "sshcryptofacility_p.h", - "sshdirecttcpiptunnel.h", - "sshdirecttcpiptunnel_p.h", - "sshdirecttcpiptunnel.cpp", - "ssherrors.h", - "sshexception_p.h", - "sshforwardedtcpiptunnel.cpp", - "sshforwardedtcpiptunnel.h", - "sshforwardedtcpiptunnel_p.h", - "sshhostkeydatabase.cpp", - "sshhostkeydatabase.h", - "sshincomingpacket_p.h", - "sshincomingpacket.cpp", "sshkeycreationdialog.cpp", "sshkeycreationdialog.h", "sshkeycreationdialog.ui", - "sshkeyexchange.cpp", - "sshkeyexchange_p.h", - "sshkeygenerator.cpp", - "sshkeygenerator.h", - "sshkeypasswordretriever.cpp", - "sshkeypasswordretriever_p.h", "sshlogging.cpp", "sshlogging_p.h", - "sshoutgoingpacket.cpp", - "sshoutgoingpacket_p.h", - "sshpacket.cpp", - "sshpacket_p.h", - "sshpacketparser.cpp", - "sshpacketparser_p.h", - "sshpseudoterminal.h", + "sshprocess.cpp", + "sshprocess_p.h", "sshremoteprocess.cpp", "sshremoteprocess.h", - "sshremoteprocess_p.h", "sshremoteprocessrunner.cpp", "sshremoteprocessrunner.h", - "sshsendfacility.cpp", - "sshsendfacility_p.h", - "sshtcpipforwardserver.cpp", - "sshtcpipforwardserver.h", - "sshtcpipforwardserver_p.h", - "sshtcpiptunnel.cpp", - "sshtcpiptunnel_p.h", - "sshx11channel.cpp", - "sshx11channel_p.h", - "sshx11displayinfo_p.h", - "sshx11inforetriever.cpp", - "sshx11inforetriever_p.h", + "sshsettings.cpp", + "sshsettings.h", ] - property var botanIncludes: qtc.useSystemBotan ? ["/usr/include/botan-2"] : [] - property var botanLibs: { - var result = []; - if (qtc.useSystemBotan) - result.push("botan-2") - if (qbs.targetOS.contains("windows")) - result.push("advapi32", "user32", "ws2_32") - else if (qbs.targetOS.contains("linux")) - result.push("rt", "dl"); - else if (qbs.targetOS.contains("macos")) - result.push("dl"); - else if (qbs.targetOS.contains("unix")) - result.push("rt"); - return result - } - - Export { - Depends { name: "Qt"; submodules: ["widgets", "network"] } - } + Export { Depends { name: "Qt.network" } } } } diff --git a/src/libs/ssh/ssh_dependencies.pri b/src/libs/ssh/ssh_dependencies.pri index ccd896a82cb..edca1c112c1 100644 --- a/src/libs/ssh/ssh_dependencies.pri +++ b/src/libs/ssh/ssh_dependencies.pri @@ -1 +1,2 @@ QTC_LIB_NAME = QtcSsh +QTC_LIB_DEPENDS += utils diff --git a/src/libs/ssh/ssh_global.h b/src/libs/ssh/ssh_global.h index 3941ffbfcd8..437fb67f148 100644 --- a/src/libs/ssh/ssh_global.h +++ b/src/libs/ssh/ssh_global.h @@ -38,8 +38,3 @@ #else # define QSSH_AUTOTEST_EXPORT #endif - -#define QSSH_PRINT_WARNING qWarning("Soft assert at %s:%d", __FILE__, __LINE__) -#define QSSH_ASSERT(cond) do { if (!(cond)) { QSSH_PRINT_WARNING; } } while (false) -#define QSSH_ASSERT_AND_RETURN(cond) do { if (!(cond)) { QSSH_PRINT_WARNING; return; } } while (false) -#define QSSH_ASSERT_AND_RETURN_VALUE(cond, value) do { if (!(cond)) { QSSH_PRINT_WARNING; return value; } } while (false) diff --git a/src/libs/ssh/sshagent.cpp b/src/libs/ssh/sshagent.cpp deleted file mode 100644 index 4e581fac0a0..00000000000 --- a/src/libs/ssh/sshagent.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ -#include "sshagent_p.h" - -#include "sshlogging_p.h" -#include "sshpacket_p.h" -#include "sshpacketparser_p.h" -#include "ssh_global.h" - -#include <QTimer> -#include <QtEndian> - -#include <algorithm> - -namespace QSsh { -namespace Internal { - -// https://github.com/openssh/openssh-portable/blob/V_7_2/PROTOCOL.agent -enum PacketType { - SSH_AGENT_FAILURE = 5, - SSH2_AGENTC_REQUEST_IDENTITIES = 11, - SSH2_AGENTC_SIGN_REQUEST = 13, - SSH2_AGENT_IDENTITIES_ANSWER = 12, - SSH2_AGENT_SIGN_RESPONSE = 14, -}; - -// TODO: Remove once we require 5.7, where the endianness functions have a sane input type. -template<typename T> static T fromBigEndian(const QByteArray &ba) -{ - return qFromBigEndian<T>(reinterpret_cast<const uchar *>(ba.constData())); -} - -void SshAgent::refreshKeysImpl() -{ - if (state() != Connected) - return; - const auto keysRequestIt = std::find_if(m_pendingRequests.constBegin(), - m_pendingRequests.constEnd(), [](const Request &r) { return r.isKeysRequest(); }); - if (keysRequestIt != m_pendingRequests.constEnd()) { - qCDebug(sshLog) << "keys request already pending, not adding another one"; - return; - } - qCDebug(sshLog) << "queueing keys request"; - m_pendingRequests << Request(); - sendNextRequest(); -} - -void SshAgent::requestSignatureImpl(const QByteArray &key, uint token) -{ - if (state() != Connected) - return; - const QByteArray data = m_dataToSign.take(qMakePair(key, token)); - QSSH_ASSERT(!data.isEmpty()); - qCDebug(sshLog) << "queueing signature request"; - m_pendingRequests.enqueue(Request(key, data, token)); - sendNextRequest(); -} - -void SshAgent::sendNextRequest() -{ - if (m_pendingRequests.isEmpty()) - return; - if (m_outgoingPacket.isComplete()) - return; - if (hasError()) - return; - const Request &request = m_pendingRequests.head(); - m_outgoingPacket = request.isKeysRequest() ? generateKeysPacket() : generateSigPacket(request); - sendPacket(); -} - -SshAgent::Packet SshAgent::generateKeysPacket() -{ - qCDebug(sshLog) << "requesting keys from agent"; - Packet p; - p.size = 1; - p.data += char(SSH2_AGENTC_REQUEST_IDENTITIES); - return p; -} - -SshAgent::Packet SshAgent::generateSigPacket(const SshAgent::Request &request) -{ - qCDebug(sshLog) << "requesting signature from agent for key" << request.key << "and token" - << request.token; - Packet p; - p.data += char(SSH2_AGENTC_SIGN_REQUEST); - p.data += AbstractSshPacket::encodeString(request.key); - p.data += AbstractSshPacket::encodeString(request.dataToSign); - p.data += AbstractSshPacket::encodeInt(quint32(0)); - p.size = p.data.count(); - return p; -} - -SshAgent::~SshAgent() -{ - m_agentSocket.disconnect(this); -} - -void SshAgent::storeDataToSign(const QByteArray &key, const QByteArray &data, uint token) -{ - instance().m_dataToSign.insert(qMakePair(key, token), data); -} - -void SshAgent::removeDataToSign(const QByteArray &key, uint token) -{ - instance().m_dataToSign.remove(qMakePair(key, token)); -} - -SshAgent &QSsh::Internal::SshAgent::instance() -{ - static SshAgent agent; - return agent; -} - -SshAgent::SshAgent() -{ - connect(&m_agentSocket, &QLocalSocket::connected, this, &SshAgent::handleConnected); - connect(&m_agentSocket, &QLocalSocket::disconnected, this, &SshAgent::handleDisconnected); - connect(&m_agentSocket, - static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error), - this, &SshAgent::handleSocketError); - connect(&m_agentSocket, &QLocalSocket::readyRead, this, &SshAgent::handleIncomingData); - QTimer::singleShot(0, this, &SshAgent::connectToServer); -} - -void SshAgent::connectToServer() -{ - const QByteArray serverAddress = qgetenv("SSH_AUTH_SOCK"); - if (serverAddress.isEmpty()) { - qCDebug(sshLog) << "agent failure: socket address unknown"; - m_error = tr("Cannot connect to ssh-agent: SSH_AUTH_SOCK is not set."); - emit errorOccurred(); - return; - } - qCDebug(sshLog) << "connecting to ssh-agent socket" << serverAddress; - m_state = Connecting; - m_agentSocket.connectToServer(QString::fromLocal8Bit(serverAddress)); -} - -void SshAgent::handleConnected() -{ - m_state = Connected; - qCDebug(sshLog) << "connection to ssh-agent established"; - refreshKeys(); -} - -void SshAgent::handleDisconnected() -{ - qCDebug(sshLog) << "lost connection to ssh-agent"; - m_error = tr("Lost connection to ssh-agent for unknown reason."); - setDisconnected(); -} - -void SshAgent::handleSocketError() -{ - qCDebug(sshLog) << "agent socket error" << m_agentSocket.error(); - m_error = m_agentSocket.errorString(); - setDisconnected(); -} - -void SshAgent::handleIncomingData() -{ - qCDebug(sshLog) << "getting data from agent"; - m_incomingData += m_agentSocket.readAll(); - while (!hasError() && !m_incomingData.isEmpty()) { - if (m_incomingPacket.size == 0) { - if (m_incomingData.count() < int(sizeof m_incomingPacket.size)) - break; - m_incomingPacket.size = fromBigEndian<quint32>(m_incomingData); - m_incomingData.remove(0, sizeof m_incomingPacket.size); - } - const int bytesToTake = qMin<quint32>(m_incomingPacket.size - m_incomingPacket.data.count(), - m_incomingData.count()); - m_incomingPacket.data += m_incomingData.left(bytesToTake); - m_incomingData.remove(0, bytesToTake); - if (m_incomingPacket.isComplete()) - handleIncomingPacket(); - else - break; - } -} - -void SshAgent::handleIncomingPacket() -{ - try { - qCDebug(sshLog) << "received packet from agent:" << m_incomingPacket.data.toHex(); - const char messageType = m_incomingPacket.data.at(0); - switch (messageType) { - case SSH2_AGENT_IDENTITIES_ANSWER: - handleIdentitiesPacket(); - break; - case SSH2_AGENT_SIGN_RESPONSE: - handleSignaturePacket(); - break; - case SSH_AGENT_FAILURE: - if (m_pendingRequests.isEmpty()) { - qCWarning(sshLog) << "unexpected failure message from agent"; - } else { - const Request request = m_pendingRequests.dequeue(); - if (request.isSignatureRequest()) { - qCWarning(sshLog) << "agent failed to sign message for key" - << request.key.toHex(); - emit signatureAvailable(request.key, QByteArray(), request.token); - } else { - qCWarning(sshLog) << "agent failed to retrieve key list"; - if (m_keys.isEmpty()) { - m_error = tr("ssh-agent failed to retrieve keys."); - setDisconnected(); - } - } - } - break; - default: - qCWarning(sshLog) << "unexpected message type from agent:" << messageType; - } - } catch (const SshPacketParseException &) { - qCWarning(sshLog()) << "received malformed packet from agent"; - handleProtocolError(); - } - m_incomingPacket.invalidate(); - m_incomingPacket.size = 0; - m_outgoingPacket.invalidate(); - sendNextRequest(); -} - -void SshAgent::handleIdentitiesPacket() -{ - qCDebug(sshLog) << "got keys packet from agent"; - if (m_pendingRequests.isEmpty() || !m_pendingRequests.dequeue().isKeysRequest()) { - qCDebug(sshLog) << "packet was not requested"; - handleProtocolError(); - return; - } - quint32 offset = 1; - const auto keyCount = SshPacketParser::asUint32(m_incomingPacket.data, &offset); - qCDebug(sshLog) << "packet contains" << keyCount << "keys"; - QList<QByteArray> newKeys; - for (quint32 i = 0; i < keyCount; ++i) { - const QByteArray key = SshPacketParser::asString(m_incomingPacket.data, &offset); - quint32 keyOffset = 0; - const QByteArray algoName = SshPacketParser::asString(key, &keyOffset); - SshPacketParser::asString(key, &keyOffset); // rest of key blob - SshPacketParser::asString(m_incomingPacket.data, &offset); // comment - qCDebug(sshLog) << "adding key of type" << algoName; - newKeys << key; - } - - m_keys = newKeys; - emit keysUpdated(); -} - -void SshAgent::handleSignaturePacket() -{ - qCDebug(sshLog) << "got signature packet from agent"; - if (m_pendingRequests.isEmpty()) { - qCDebug(sshLog) << "signature packet was not requested"; - handleProtocolError(); - return; - } - const Request request = m_pendingRequests.dequeue(); - if (!request.isSignatureRequest()) { - qCDebug(sshLog) << "signature packet was not requested"; - handleProtocolError(); - return; - } - const QByteArray signature = SshPacketParser::asString(m_incomingPacket.data, 1); - qCDebug(sshLog) << "signature for key" << request.key.toHex() << "is" << signature.toHex(); - emit signatureAvailable(request.key, signature, request.token); -} - -void SshAgent::handleProtocolError() -{ - m_error = tr("Protocol error when talking to ssh-agent."); - setDisconnected(); -} - -void SshAgent::setDisconnected() -{ - m_state = Unconnected; - m_agentSocket.disconnect(this); - emit errorOccurred(); -} - -void SshAgent::sendPacket() -{ - const quint32 sizeMsb = qToBigEndian(m_outgoingPacket.size); - m_agentSocket.write(reinterpret_cast<const char *>(&sizeMsb), sizeof sizeMsb); - m_agentSocket.write(m_outgoingPacket.data); -} - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshagent_p.h b/src/libs/ssh/sshagent_p.h deleted file mode 100644 index f30867b9343..00000000000 --- a/src/libs/ssh/sshagent_p.h +++ /dev/null @@ -1,125 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include <QByteArray> -#include <QHash> -#include <QList> -#include <QLocalSocket> -#include <QObject> -#include <QPair> -#include <QQueue> -#include <QString> - -namespace QSsh { -namespace Internal { - -class SshAgent : public QObject -{ - Q_OBJECT -public: - enum State { Unconnected, Connecting, Connected, }; - - ~SshAgent(); - static State state() { return instance().m_state; } - static bool hasError() { return !instance().m_error.isEmpty(); } - static QString errorString() { return instance().m_error; } - static QList<QByteArray> publicKeys() { return instance().m_keys; } - - static void refreshKeys() { instance().refreshKeysImpl(); } - static void storeDataToSign(const QByteArray &key, const QByteArray &data, uint token); - static void removeDataToSign(const QByteArray &key, uint token); - static void requestSignature(const QByteArray &key, uint token) { - instance().requestSignatureImpl(key, token); - } - - static SshAgent &instance(); - -signals: - void errorOccurred(); - void keysUpdated(); - - // Empty signature means signing failure. - void signatureAvailable(const QByteArray &key, const QByteArray &signature, uint token); - -private: - struct Request { - Request() { } - Request(const QByteArray &k, const QByteArray &d, uint t) - : key(k), dataToSign(d), token(t) { } - - bool isKeysRequest() const { return !isSignatureRequest(); } - bool isSignatureRequest() const { return !key.isEmpty(); } - - QByteArray key; - QByteArray dataToSign; - uint token = 0; - }; - - struct Packet { - bool isComplete() const { return size != 0 && int(size) == data.count(); } - void invalidate() { size = 0; data.clear(); } - - quint32 size = 0; - QByteArray data; - }; - - SshAgent(); - void connectToServer(); - void refreshKeysImpl(); - void requestSignatureImpl(const QByteArray &key, uint token); - - void sendNextRequest(); - Packet generateKeysPacket(); - Packet generateSigPacket(const Request &request); - - void handleConnected(); - void handleDisconnected(); - void handleSocketError(); - void handleIncomingData(); - void handleIncomingPacket(); - void handleIdentitiesPacket(); - void handleSignaturePacket(); - - void handleProtocolError(); - void setDisconnected(); - - void sendPacket(); - - State m_state = Unconnected; - QString m_error; - QList<QByteArray> m_keys; - QHash<QPair<QByteArray, uint>, QByteArray> m_dataToSign; - QLocalSocket m_agentSocket; - QByteArray m_incomingData; - Packet m_incomingPacket; - Packet m_outgoingPacket; - - QQueue<Request> m_pendingRequests; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshbotanconversions_p.h b/src/libs/ssh/sshbotanconversions_p.h deleted file mode 100644 index 335a02323c9..00000000000 --- a/src/libs/ssh/sshbotanconversions_p.h +++ /dev/null @@ -1,138 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sshcapabilities_p.h" -#include "sshexception_p.h" - -#include <botan/secmem.h> -#include <botan/types.h> - -namespace QSsh { -namespace Internal { - -inline const Botan::byte *convertByteArray(const QByteArray &a) -{ - return reinterpret_cast<const Botan::byte *>(a.constData()); -} - -inline Botan::byte *convertByteArray(QByteArray &a) -{ - return reinterpret_cast<Botan::byte *>(a.data()); -} - -inline QByteArray convertByteArray(const Botan::SecureVector<Botan::byte> &v) -{ - return QByteArray(reinterpret_cast<const char *>(&v.front()), static_cast<int>(v.size())); -} - -inline QByteArray convertByteArray(const std::vector<std::uint8_t> &v) -{ - return QByteArray(reinterpret_cast<const char *>(&v.front()), static_cast<int>(v.size())); -} - -inline const char *botanKeyExchangeAlgoName(const QByteArray &rfcAlgoName) -{ - if (rfcAlgoName == SshCapabilities::DiffieHellmanGroup1Sha1) - return "modp/ietf/1024"; - if (rfcAlgoName == SshCapabilities::DiffieHellmanGroup14Sha1) - return "modp/ietf/2048"; - if (rfcAlgoName == SshCapabilities::EcdhNistp256) - return "secp256r1"; - if (rfcAlgoName == SshCapabilities::EcdhNistp384) - return "secp384r1"; - if (rfcAlgoName == SshCapabilities::EcdhNistp521) - return "secp521r1"; - throw SshClientException(SshInternalError, SSH_TR("Unexpected key exchange algorithm \"%1\"") - .arg(QString::fromLatin1(rfcAlgoName))); -} - -inline const char *botanCryptAlgoName(const QByteArray &rfcAlgoName) -{ - if (rfcAlgoName == SshCapabilities::CryptAlgoAes128Cbc - || rfcAlgoName == SshCapabilities::CryptAlgoAes128Ctr) { - return "AES-128"; - } - if (rfcAlgoName == SshCapabilities::CryptAlgo3DesCbc - || rfcAlgoName == SshCapabilities::CryptAlgo3DesCtr) { - return "TripleDES"; - } - if (rfcAlgoName == SshCapabilities::CryptAlgoAes192Ctr) { - return "AES-192"; - } - if (rfcAlgoName == SshCapabilities::CryptAlgoAes256Ctr) { - return "AES-256"; - } - throw SshClientException(SshInternalError, SSH_TR("Unexpected cipher \"%1\"") - .arg(QString::fromLatin1(rfcAlgoName))); -} - -inline const char *botanEmsaAlgoName(const QByteArray &rfcAlgoName) -{ - if (rfcAlgoName == SshCapabilities::PubKeyDss) - return "EMSA1(SHA-1)"; - if (rfcAlgoName == SshCapabilities::PubKeyRsa) - return "EMSA3(SHA-1)"; - if (rfcAlgoName == SshCapabilities::PubKeyEcdsa256) - return "EMSA1(SHA-256)"; - if (rfcAlgoName == SshCapabilities::PubKeyEcdsa384) - return "EMSA1(SHA-384)"; - if (rfcAlgoName == SshCapabilities::PubKeyEcdsa521) - return "EMSA1(SHA-512)"; - throw SshClientException(SshInternalError, SSH_TR("Unexpected host key algorithm \"%1\"") - .arg(QString::fromLatin1(rfcAlgoName))); -} - -inline const char *botanHMacAlgoName(const QByteArray &rfcAlgoName) -{ - if (rfcAlgoName == SshCapabilities::HMacSha1) - return "SHA-1"; - if (rfcAlgoName == SshCapabilities::HMacSha256) - return "SHA-256"; - if (rfcAlgoName == SshCapabilities::HMacSha384) - return "SHA-384"; - if (rfcAlgoName == SshCapabilities::HMacSha512) - return "SHA-512"; - throw SshClientException(SshInternalError, SSH_TR("Unexpected hashing algorithm \"%1\"") - .arg(QString::fromLatin1(rfcAlgoName))); -} - -inline quint32 botanHMacKeyLen(const QByteArray &rfcAlgoName) -{ - if (rfcAlgoName == SshCapabilities::HMacSha1) - return 20; - if (rfcAlgoName == SshCapabilities::HMacSha256) - return 32; - if (rfcAlgoName == SshCapabilities::HMacSha384) - return 48; - if (rfcAlgoName == SshCapabilities::HMacSha512) - return 64; - throw SshClientException(SshInternalError, SSH_TR("Unexpected hashing algorithm \"%1\"") - .arg(QString::fromLatin1(rfcAlgoName))); -} - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshcapabilities.cpp b/src/libs/ssh/sshcapabilities.cpp deleted file mode 100644 index ac0b4a55c2c..00000000000 --- a/src/libs/ssh/sshcapabilities.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshcapabilities_p.h" - -#include "sshexception_p.h" - -#include <QCoreApplication> -#include <QString> - -namespace QSsh { -namespace Internal { - -namespace { - QByteArray listAsByteArray(const QList<QByteArray> &list) - { - QByteArray array; - foreach (const QByteArray &elem, list) - array += elem + ','; - if (!array.isEmpty()) - array.remove(array.count() - 1, 1); - return array; - } -} // anonymous namspace - -const QByteArray SshCapabilities::DiffieHellmanGroup1Sha1("diffie-hellman-group1-sha1"); -const QByteArray SshCapabilities::DiffieHellmanGroup14Sha1("diffie-hellman-group14-sha1"); -const QByteArray SshCapabilities::EcdhKexNamePrefix("ecdh-sha2-nistp"); -const QByteArray SshCapabilities::EcdhNistp256 = EcdhKexNamePrefix + "256"; -const QByteArray SshCapabilities::EcdhNistp384 = EcdhKexNamePrefix + "384"; -const QByteArray SshCapabilities::EcdhNistp521 = EcdhKexNamePrefix + "521"; -const QList<QByteArray> SshCapabilities::KeyExchangeMethods = QList<QByteArray>() - << SshCapabilities::EcdhNistp256 - << SshCapabilities::EcdhNistp384 - << SshCapabilities::EcdhNistp521 - << SshCapabilities::DiffieHellmanGroup1Sha1 - << SshCapabilities::DiffieHellmanGroup14Sha1; - -const QByteArray SshCapabilities::PubKeyDss("ssh-dss"); -const QByteArray SshCapabilities::PubKeyRsa("ssh-rsa"); -const QByteArray SshCapabilities::PubKeyEcdsaPrefix("ecdsa-sha2-nistp"); -const QByteArray SshCapabilities::PubKeyEcdsa256 = SshCapabilities::PubKeyEcdsaPrefix + "256"; -const QByteArray SshCapabilities::PubKeyEcdsa384 = SshCapabilities::PubKeyEcdsaPrefix + "384"; -const QByteArray SshCapabilities::PubKeyEcdsa521 = SshCapabilities::PubKeyEcdsaPrefix + "521"; -const QList<QByteArray> SshCapabilities::PublicKeyAlgorithms = QList<QByteArray>() - << SshCapabilities::PubKeyEcdsa256 - << SshCapabilities::PubKeyEcdsa384 - << SshCapabilities::PubKeyEcdsa521 - << SshCapabilities::PubKeyRsa - << SshCapabilities::PubKeyDss; - -const QByteArray SshCapabilities::CryptAlgo3DesCbc("3des-cbc"); -const QByteArray SshCapabilities::CryptAlgo3DesCtr("3des-ctr"); -const QByteArray SshCapabilities::CryptAlgoAes128Cbc("aes128-cbc"); -const QByteArray SshCapabilities::CryptAlgoAes128Ctr("aes128-ctr"); -const QByteArray SshCapabilities::CryptAlgoAes192Ctr("aes192-ctr"); -const QByteArray SshCapabilities::CryptAlgoAes256Ctr("aes256-ctr"); -const QList<QByteArray> SshCapabilities::EncryptionAlgorithms - = QList<QByteArray>() << SshCapabilities::CryptAlgoAes256Ctr - << SshCapabilities::CryptAlgoAes192Ctr - << SshCapabilities::CryptAlgoAes128Ctr - << SshCapabilities::CryptAlgo3DesCtr - << SshCapabilities::CryptAlgoAes128Cbc - << SshCapabilities::CryptAlgo3DesCbc; - -const QByteArray SshCapabilities::HMacSha1("hmac-sha1"); -const QByteArray SshCapabilities::HMacSha196("hmac-sha1-96"); -const QByteArray SshCapabilities::HMacSha256("hmac-sha2-256"); -const QByteArray SshCapabilities::HMacSha384("hmac-sha2-384"); -const QByteArray SshCapabilities::HMacSha512("hmac-sha2-512"); -const QList<QByteArray> SshCapabilities::MacAlgorithms - = QList<QByteArray>() /* << SshCapabilities::HMacSha196 */ - << SshCapabilities::HMacSha256 - << SshCapabilities::HMacSha384 - << SshCapabilities::HMacSha512 - << SshCapabilities::HMacSha1; - -const QList<QByteArray> SshCapabilities::CompressionAlgorithms - = QList<QByteArray>() << "none"; - -const QByteArray SshCapabilities::SshConnectionService("ssh-connection"); - -QList<QByteArray> SshCapabilities::commonCapabilities(const QList<QByteArray> &myCapabilities, - const QList<QByteArray> &serverCapabilities) -{ - QList<QByteArray> capabilities; - foreach (const QByteArray &myCapability, myCapabilities) { - if (serverCapabilities.contains(myCapability)) - capabilities << myCapability; - } - - if (!capabilities.isEmpty()) - return capabilities; - - throw SshServerException(SSH_DISCONNECT_KEY_EXCHANGE_FAILED, - "Server and client capabilities do not match.", - QCoreApplication::translate("SshConnection", - "Server and client capabilities don't match. " - "Client list was: %1.\nServer list was %2.") - .arg(QString::fromLocal8Bit(listAsByteArray(myCapabilities).data())) - .arg(QString::fromLocal8Bit(listAsByteArray(serverCapabilities).data()))); - -} - -QByteArray SshCapabilities::findBestMatch(const QList<QByteArray> &myCapabilities, - const QList<QByteArray> &serverCapabilities) -{ - return commonCapabilities(myCapabilities, serverCapabilities).first(); -} - -int SshCapabilities::ecdsaIntegerWidthInBytes(const QByteArray &ecdsaAlgo) -{ - if (ecdsaAlgo == PubKeyEcdsa256) - return 32; - if (ecdsaAlgo == PubKeyEcdsa384) - return 48; - if (ecdsaAlgo == PubKeyEcdsa521) - return 66; - throw SshClientException(SshInternalError, SSH_TR("Unexpected ecdsa algorithm \"%1\"") - .arg(QString::fromLatin1(ecdsaAlgo))); -} - -QByteArray SshCapabilities::ecdsaPubKeyAlgoForKeyWidth(int keyWidthInBytes) -{ - if (keyWidthInBytes <= 32) - return PubKeyEcdsa256; - if (keyWidthInBytes <= 48) - return PubKeyEcdsa384; - if (keyWidthInBytes <= 66) - return PubKeyEcdsa521; - throw SshClientException(SshInternalError, SSH_TR("Unexpected ecdsa key size (%1 bytes)") - .arg(keyWidthInBytes)); -} - -const char *SshCapabilities::oid(const QByteArray &ecdsaAlgo) -{ - if (ecdsaAlgo == PubKeyEcdsa256) - return "secp256r1"; - if (ecdsaAlgo == PubKeyEcdsa384) - return "secp384r1"; - if (ecdsaAlgo == PubKeyEcdsa521) - return "secp521r1"; - throw SshClientException(SshInternalError, SSH_TR("Unexpected ecdsa algorithm \"%1\"") - .arg(QString::fromLatin1(ecdsaAlgo))); -} - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshcapabilities_p.h b/src/libs/ssh/sshcapabilities_p.h deleted file mode 100644 index 46341681b1b..00000000000 --- a/src/libs/ssh/sshcapabilities_p.h +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include <QByteArray> -#include <QList> - -namespace QSsh { -namespace Internal { - -class SshCapabilities -{ -public: - static const QByteArray DiffieHellmanGroup1Sha1; - static const QByteArray DiffieHellmanGroup14Sha1; - static const QByteArray EcdhKexNamePrefix; - static const QByteArray EcdhNistp256; - static const QByteArray EcdhNistp384; - static const QByteArray EcdhNistp521; // sic - static const QList<QByteArray> KeyExchangeMethods; - - static const QByteArray PubKeyDss; - static const QByteArray PubKeyRsa; - static const QByteArray PubKeyEcdsaPrefix; - static const QByteArray PubKeyEcdsa256; - static const QByteArray PubKeyEcdsa384; - static const QByteArray PubKeyEcdsa521; - static const QList<QByteArray> PublicKeyAlgorithms; - - static const QByteArray CryptAlgo3DesCbc; - static const QByteArray CryptAlgo3DesCtr; - static const QByteArray CryptAlgoAes128Cbc; - static const QByteArray CryptAlgoAes128Ctr; - static const QByteArray CryptAlgoAes192Ctr; - static const QByteArray CryptAlgoAes256Ctr; - static const QList<QByteArray> EncryptionAlgorithms; - - static const QByteArray HMacSha1; - static const QByteArray HMacSha196; - static const QByteArray HMacSha256; - static const QByteArray HMacSha384; - static const QByteArray HMacSha512; - static const QList<QByteArray> MacAlgorithms; - - static const QList<QByteArray> CompressionAlgorithms; - - static const QByteArray SshConnectionService; - - static QList<QByteArray> commonCapabilities(const QList<QByteArray> &myCapabilities, - const QList<QByteArray> &serverCapabilities); - static QByteArray findBestMatch(const QList<QByteArray> &myCapabilities, - const QList<QByteArray> &serverCapabilities); - - static int ecdsaIntegerWidthInBytes(const QByteArray &ecdsaAlgo); - static QByteArray ecdsaPubKeyAlgoForKeyWidth(int keyWidthInBytes); - static const char *oid(const QByteArray &ecdsaAlgo); -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshchannel.cpp b/src/libs/ssh/sshchannel.cpp deleted file mode 100644 index a1a15bf0a7a..00000000000 --- a/src/libs/ssh/sshchannel.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshchannel_p.h" - -#include "sshincomingpacket_p.h" -#include "sshlogging_p.h" -#include "sshsendfacility_p.h" - -#include <QTimer> - -namespace QSsh { -namespace Internal { - -const quint32 NoChannel = 0xffffffffu; - -AbstractSshChannel::AbstractSshChannel(quint32 channelId, - SshSendFacility &sendFacility) - : m_sendFacility(sendFacility), - m_localChannel(channelId), m_remoteChannel(NoChannel), - m_localWindowSize(initialWindowSize()), m_remoteWindowSize(0), - m_state(Inactive) -{ - m_timeoutTimer.setSingleShot(true); - connect(&m_timeoutTimer, &QTimer::timeout, this, &AbstractSshChannel::timeout); -} - -AbstractSshChannel::~AbstractSshChannel() -{ - -} - -void AbstractSshChannel::setChannelState(ChannelState state) -{ - m_state = state; - if (state == Closed) - closeHook(); -} - -void AbstractSshChannel::requestSessionStart() -{ - // Note: We are just being paranoid here about the Botan exceptions, - // which are extremely unlikely to happen, because if there was a problem - // with our cryptography stuff, it would have hit us before, on - // establishing the connection. - try { - m_sendFacility.sendSessionPacket(m_localChannel, initialWindowSize(), maxPacketSize()); - setChannelState(SessionRequested); - m_timeoutTimer.start(ReplyTimeout); - } catch (const std::exception &e) { - qCWarning(sshLog, "Botan error: %s", e.what()); - closeChannel(); - } -} - -void AbstractSshChannel::sendData(const QByteArray &data) -{ - try { - m_sendBuffer += data; - flushSendBuffer(); - } catch (const std::exception &e) { - qCWarning(sshLog, "Botan error: %s", e.what()); - closeChannel(); - } -} - -quint32 AbstractSshChannel::initialWindowSize() -{ - return maxPacketSize(); -} - -quint32 AbstractSshChannel::maxPacketSize() -{ - return 16 * 1024 * 1024; -} - -void AbstractSshChannel::handleWindowAdjust(quint32 bytesToAdd) -{ - checkChannelActive(); - - const quint64 newValue = m_remoteWindowSize + bytesToAdd; - if (newValue > 0xffffffffu) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Illegal window size requested."); - } - - m_remoteWindowSize = newValue; - flushSendBuffer(); -} - -void AbstractSshChannel::flushSendBuffer() -{ - while (true) { - const quint32 bytesToSend = qMin(m_remoteMaxPacketSize, - qMin<quint32>(m_remoteWindowSize, m_sendBuffer.size())); - if (bytesToSend == 0) - break; - const QByteArray &data = m_sendBuffer.left(bytesToSend); - m_sendFacility.sendChannelDataPacket(m_remoteChannel, data); - m_sendBuffer.remove(0, bytesToSend); - m_remoteWindowSize -= bytesToSend; - } -} - -void AbstractSshChannel::handleOpenSuccess(quint32 remoteChannelId, - quint32 remoteWindowSize, quint32 remoteMaxPacketSize) -{ - const ChannelState oldState = m_state; - switch (oldState) { - case CloseRequested: // closeChannel() was called while we were in SessionRequested state - case SessionRequested: - break; // Ok, continue. - default: - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION packet."); - } - - m_timeoutTimer.stop(); - - qCDebug(sshLog, "Channel opened. remote channel id: %u, remote window size: %u, " - "remote max packet size: %u", - remoteChannelId, remoteWindowSize, remoteMaxPacketSize); - m_remoteChannel = remoteChannelId; - m_remoteWindowSize = remoteWindowSize; - m_remoteMaxPacketSize = remoteMaxPacketSize; - setChannelState(SessionEstablished); - if (oldState == CloseRequested) - closeChannel(); - else - handleOpenSuccessInternal(); -} - -void AbstractSshChannel::handleOpenFailure(const QString &reason) -{ - switch (m_state) { - case SessionRequested: - break; // Ok, continue. - case CloseRequested: - return; // Late server reply; we requested a channel close in the meantime. - default: - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE packet."); - } - - m_timeoutTimer.stop(); - - qCDebug(sshLog, "Channel open request failed for channel %u", m_localChannel); - handleOpenFailureInternal(reason); -} - -void AbstractSshChannel::handleChannelEof() -{ - if (m_state == Inactive || m_state == Closed) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_MSG_CHANNEL_EOF message."); - } - m_localWindowSize = 0; - emit eof(); -} - -void AbstractSshChannel::handleChannelClose() -{ - qCDebug(sshLog, "Receiving CLOSE for channel %u", m_localChannel); - if (channelState() == Inactive || channelState() == Closed) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_MSG_CHANNEL_CLOSE message."); - } - closeChannel(); - setChannelState(Closed); -} - -void AbstractSshChannel::handleChannelData(const QByteArray &data) -{ - const int bytesToDeliver = handleChannelOrExtendedChannelData(data); - handleChannelDataInternal(bytesToDeliver == data.size() - ? data : data.left(bytesToDeliver)); -} - -void AbstractSshChannel::handleChannelExtendedData(quint32 type, const QByteArray &data) -{ - const int bytesToDeliver = handleChannelOrExtendedChannelData(data); - handleChannelExtendedDataInternal(type, bytesToDeliver == data.size() - ? data : data.left(bytesToDeliver)); -} - -void AbstractSshChannel::handleChannelRequest(const SshIncomingPacket &packet) -{ - checkChannelActive(); - const QByteArray &requestType = packet.extractChannelRequestType(); - if (requestType == SshIncomingPacket::ExitStatusType) - handleExitStatus(packet.extractChannelExitStatus()); - else if (requestType == SshIncomingPacket::ExitSignalType) - handleExitSignal(packet.extractChannelExitSignal()); - else if (requestType != "[email protected]") // Suppress warning for this one, as it's sent all the time. - qCWarning(sshLog, "Ignoring unknown request type '%s'", requestType.data()); -} - -int AbstractSshChannel::handleChannelOrExtendedChannelData(const QByteArray &data) -{ - checkChannelActive(); - - const int bytesToDeliver = qMin<quint32>(data.size(), maxDataSize()); - if (bytesToDeliver != data.size()) - qCWarning(sshLog, "Misbehaving server does not respect local window, clipping."); - - m_localWindowSize -= bytesToDeliver; - if (m_localWindowSize < maxPacketSize()) { - m_localWindowSize += maxPacketSize(); - m_sendFacility.sendWindowAdjustPacket(m_remoteChannel, maxPacketSize()); - } - return bytesToDeliver; -} - -void AbstractSshChannel::closeChannel() -{ - if (m_state == CloseRequested) { - m_timeoutTimer.stop(); - } else if (m_state != Closed) { - if (m_state == Inactive) { - setChannelState(Closed); - } else { - const ChannelState oldState = m_state; - setChannelState(CloseRequested); - if (m_remoteChannel != NoChannel) { - m_sendFacility.sendChannelEofPacket(m_remoteChannel); - m_sendFacility.sendChannelClosePacket(m_remoteChannel); - } else { - QSSH_ASSERT(oldState == SessionRequested); - } - } - } -} - -void AbstractSshChannel::checkChannelActive() -{ - if (channelState() == Inactive || channelState() == Closed) - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Channel not open."); -} - -quint32 AbstractSshChannel::maxDataSize() const -{ - return qMin(m_localWindowSize, maxPacketSize()); -} - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshchannel_p.h b/src/libs/ssh/sshchannel_p.h deleted file mode 100644 index a55567708fe..00000000000 --- a/src/libs/ssh/sshchannel_p.h +++ /dev/null @@ -1,117 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include <QByteArray> -#include <QObject> -#include <QString> -#include <QTimer> - -namespace QSsh { -namespace Internal { - -struct SshChannelExitSignal; -struct SshChannelExitStatus; -class SshIncomingPacket; -class SshSendFacility; - -class AbstractSshChannel : public QObject -{ - Q_OBJECT -public: - enum ChannelState { - Inactive, SessionRequested, SessionEstablished, CloseRequested, Closed - }; - - quint32 localChannelId() const { return m_localChannel; } - quint32 remoteChannel() const { return m_remoteChannel; } - - virtual void handleChannelSuccess() = 0; - virtual void handleChannelFailure() = 0; - - void handleOpenSuccess(quint32 remoteChannelId, quint32 remoteWindowSize, - quint32 remoteMaxPacketSize); - void handleOpenFailure(const QString &reason); - void handleWindowAdjust(quint32 bytesToAdd); - void handleChannelEof(); - void handleChannelClose(); - void handleChannelData(const QByteArray &data); - void handleChannelExtendedData(quint32 type, const QByteArray &data); - void handleChannelRequest(const SshIncomingPacket &packet); - - void closeChannel(); - - virtual ~AbstractSshChannel(); - - static const int ReplyTimeout = 10000; // milli seconds - ChannelState channelState() const { return m_state; } - -signals: - void timeout(); - void eof(); - -protected: - AbstractSshChannel(quint32 channelId, SshSendFacility &sendFacility); - - void setChannelState(ChannelState state); - - void requestSessionStart(); - void sendData(const QByteArray &data); - - static quint32 initialWindowSize(); - static quint32 maxPacketSize(); - - quint32 maxDataSize() const; - void checkChannelActive(); - - SshSendFacility &m_sendFacility; - QTimer m_timeoutTimer; - -private: - virtual void handleOpenSuccessInternal() = 0; - virtual void handleOpenFailureInternal(const QString &reason) = 0; - virtual void handleChannelDataInternal(const QByteArray &data) = 0; - virtual void handleChannelExtendedDataInternal(quint32 type, - const QByteArray &data) = 0; - virtual void handleExitStatus(const SshChannelExitStatus &exitStatus) = 0; - virtual void handleExitSignal(const SshChannelExitSignal &signal) = 0; - - virtual void closeHook() = 0; - - void flushSendBuffer(); - int handleChannelOrExtendedChannelData(const QByteArray &data); - - const quint32 m_localChannel; - quint32 m_remoteChannel; - quint32 m_localWindowSize; - quint32 m_remoteWindowSize; - quint32 m_remoteMaxPacketSize; - ChannelState m_state; - QByteArray m_sendBuffer; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshchannelmanager.cpp b/src/libs/ssh/sshchannelmanager.cpp deleted file mode 100644 index f4bfb8cf129..00000000000 --- a/src/libs/ssh/sshchannelmanager.cpp +++ /dev/null @@ -1,407 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshchannelmanager_p.h" - -#include "sftpchannel.h" -#include "sftpchannel_p.h" -#include "sshdirecttcpiptunnel.h" -#include "sshdirecttcpiptunnel_p.h" -#include "sshforwardedtcpiptunnel.h" -#include "sshforwardedtcpiptunnel_p.h" -#include "sshincomingpacket_p.h" -#include "sshlogging_p.h" -#include "sshremoteprocess.h" -#include "sshremoteprocess_p.h" -#include "sshsendfacility_p.h" -#include "sshtcpipforwardserver.h" -#include "sshtcpipforwardserver_p.h" -#include "sshx11channel_p.h" -#include "sshx11inforetriever_p.h" - -#include <QList> - -namespace QSsh { -namespace Internal { - -SshChannelManager::SshChannelManager(SshSendFacility &sendFacility, - QObject *parent) - : QObject(parent), m_sendFacility(sendFacility), m_nextLocalChannelId(0) -{ -} - -void SshChannelManager::handleChannelRequest(const SshIncomingPacket &packet) -{ - lookupChannel(packet.extractRecipientChannel()) - ->handleChannelRequest(packet); -} - -void SshChannelManager::handleChannelOpen(const SshIncomingPacket &packet) -{ - const SshChannelOpenGeneric channelOpen = packet.extractChannelOpen(); - if (channelOpen.channelType == SshIncomingPacket::ForwardedTcpIpType) { - handleChannelOpenForwardedTcpIp(channelOpen); - return; - } - if (channelOpen.channelType == "x11") { - handleChannelOpenX11(channelOpen); - return; - } - try { - m_sendFacility.sendChannelOpenFailurePacket(channelOpen.commonData.remoteChannel, - SSH_OPEN_UNKNOWN_CHANNEL_TYPE, QByteArray()); - } catch (const std::exception &e) { - qCWarning(sshLog, "Botan error: %s", e.what()); - } -} - -void SshChannelManager::handleChannelOpenFailure(const SshIncomingPacket &packet) -{ - const SshChannelOpenFailure &failure = packet.extractChannelOpenFailure(); - ChannelIterator it = lookupChannelAsIterator(failure.localChannel); - try { - it.value()->handleOpenFailure(failure.reasonString); - } catch (const SshServerException &e) { - removeChannel(it); - throw e; - } - removeChannel(it); -} - -void SshChannelManager::handleChannelOpenConfirmation(const SshIncomingPacket &packet) -{ - const SshChannelOpenConfirmation &confirmation - = packet.extractChannelOpenConfirmation(); - lookupChannel(confirmation.localChannel)->handleOpenSuccess(confirmation.remoteChannel, - confirmation.remoteWindowSize, confirmation.remoteMaxPacketSize); -} - -void SshChannelManager::handleChannelSuccess(const SshIncomingPacket &packet) -{ - lookupChannel(packet.extractRecipientChannel())->handleChannelSuccess(); -} - -void SshChannelManager::handleChannelFailure(const SshIncomingPacket &packet) -{ - lookupChannel(packet.extractRecipientChannel())->handleChannelFailure(); -} - -void SshChannelManager::handleChannelWindowAdjust(const SshIncomingPacket &packet) -{ - const SshChannelWindowAdjust adjust = packet.extractWindowAdjust(); - lookupChannel(adjust.localChannel)->handleWindowAdjust(adjust.bytesToAdd); -} - -void SshChannelManager::handleChannelData(const SshIncomingPacket &packet) -{ - const SshChannelData &data = packet.extractChannelData(); - lookupChannel(data.localChannel)->handleChannelData(data.data); -} - -void SshChannelManager::handleChannelExtendedData(const SshIncomingPacket &packet) -{ - const SshChannelExtendedData &data = packet.extractChannelExtendedData(); - lookupChannel(data.localChannel)->handleChannelExtendedData(data.type, data.data); -} - -void SshChannelManager::handleChannelEof(const SshIncomingPacket &packet) -{ - AbstractSshChannel * const channel - = lookupChannel(packet.extractRecipientChannel(), true); - if (channel) - channel->handleChannelEof(); -} - -void SshChannelManager::handleChannelClose(const SshIncomingPacket &packet) -{ - const quint32 channelId = packet.extractRecipientChannel(); - - ChannelIterator it = lookupChannelAsIterator(channelId, true); - if (it != m_channels.end()) { - it.value()->handleChannelClose(); - removeChannel(it); - } -} - -void SshChannelManager::handleRequestSuccess(const SshIncomingPacket &packet) -{ - if (m_waitingForwardServers.isEmpty()) { - throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected request success packet.", - tr("Unexpected request success packet.")); - } - SshTcpIpForwardServer::Ptr server = m_waitingForwardServers.takeFirst(); - if (server->state() == SshTcpIpForwardServer::Closing) { - server->setClosed(); - } else if (server->state() == SshTcpIpForwardServer::Initializing) { - quint16 port = server->port(); - if (port == 0) - port = packet.extractRequestSuccess().bindPort; - server->setListening(port); - m_listeningForwardServers.append(server); - } else { - QSSH_ASSERT(false); - } -} - -void SshChannelManager::handleRequestFailure(const SshIncomingPacket &packet) -{ - Q_UNUSED(packet); - if (m_waitingForwardServers.isEmpty()) { - throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected request failure packet.", - tr("Unexpected request failure packet.")); - } - SshTcpIpForwardServer::Ptr tunnel = m_waitingForwardServers.takeFirst(); - tunnel->setClosed(); -} - -SshChannelManager::ChannelIterator SshChannelManager::lookupChannelAsIterator(quint32 channelId, - bool allowNotFound) -{ - ChannelIterator it = m_channels.find(channelId); - if (it == m_channels.end() && !allowNotFound) { - throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid channel id.", - tr("Invalid channel id %1").arg(channelId)); - } - return it; -} - -AbstractSshChannel *SshChannelManager::lookupChannel(quint32 channelId, - bool allowNotFound) -{ - ChannelIterator it = lookupChannelAsIterator(channelId, allowNotFound); - return it == m_channels.end() ? 0 : it.value(); -} - -QSsh::SshRemoteProcess::Ptr SshChannelManager::createRemoteProcess(const QByteArray &command) -{ - SshRemoteProcess::Ptr proc(new SshRemoteProcess(command, m_nextLocalChannelId++, m_sendFacility)); - insertChannel(proc->d, proc); - connect(proc->d, &SshRemoteProcessPrivate::destroyed, this, [this] { - m_x11ForwardingRequests.removeOne(static_cast<SshRemoteProcessPrivate *>(sender())); - }); - connect(proc->d, &SshRemoteProcessPrivate::x11ForwardingRequested, this, - [this, proc = proc->d](const QString &displayName) { - if (!x11DisplayName().isEmpty()) { - if (x11DisplayName() != displayName) { - proc->failToStart(tr("Cannot forward to display %1 on SSH connection that is " - "already forwarding to display %2.") - .arg(displayName, x11DisplayName())); - return; - } - if (!m_x11DisplayInfo.cookie.isEmpty()) - proc->startProcess(m_x11DisplayInfo); - else - m_x11ForwardingRequests << proc; - return; - } - m_x11DisplayInfo.displayName = displayName; - m_x11ForwardingRequests << proc; - auto * const x11InfoRetriever = new SshX11InfoRetriever(displayName, this); - const auto failureHandler = [this](const QString &errorMessage) { - for (SshRemoteProcessPrivate * const proc : qAsConst(m_x11ForwardingRequests)) - proc->failToStart(errorMessage); - m_x11ForwardingRequests.clear(); - }; - connect(x11InfoRetriever, &SshX11InfoRetriever::failure, this, failureHandler); - const auto successHandler = [this](const X11DisplayInfo &displayInfo) { - m_x11DisplayInfo = displayInfo; - for (SshRemoteProcessPrivate * const proc : qAsConst(m_x11ForwardingRequests)) - proc->startProcess(displayInfo); - m_x11ForwardingRequests.clear(); - }; - connect(x11InfoRetriever, &SshX11InfoRetriever::success, this, successHandler); - qCDebug(sshLog) << "starting x11 info retriever"; - x11InfoRetriever->start(); - }); - return proc; -} - -QSsh::SshRemoteProcess::Ptr SshChannelManager::createRemoteShell() -{ - SshRemoteProcess::Ptr proc(new SshRemoteProcess(m_nextLocalChannelId++, m_sendFacility)); - insertChannel(proc->d, proc); - return proc; -} - -QSsh::SftpChannel::Ptr SshChannelManager::createSftpChannel() -{ - SftpChannel::Ptr sftp(new SftpChannel(m_nextLocalChannelId++, m_sendFacility)); - insertChannel(sftp->d, sftp); - return sftp; -} - -SshDirectTcpIpTunnel::Ptr SshChannelManager::createDirectTunnel(const QString &originatingHost, - quint16 originatingPort, const QString &remoteHost, quint16 remotePort) -{ - SshDirectTcpIpTunnel::Ptr tunnel(new SshDirectTcpIpTunnel(m_nextLocalChannelId++, - originatingHost, originatingPort, remoteHost, remotePort, m_sendFacility)); - insertChannel(tunnel->d, tunnel); - return tunnel; -} - -SshTcpIpForwardServer::Ptr SshChannelManager::createForwardServer(const QString &remoteHost, - quint16 remotePort) -{ - SshTcpIpForwardServer::Ptr server(new SshTcpIpForwardServer(remoteHost, remotePort, - m_sendFacility)); - connect(server.data(), &SshTcpIpForwardServer::stateChanged, - this, [this, server](SshTcpIpForwardServer::State state) { - switch (state) { - case SshTcpIpForwardServer::Closing: - m_listeningForwardServers.removeOne(server); - // fall through - case SshTcpIpForwardServer::Initializing: - m_waitingForwardServers.append(server); - break; - case SshTcpIpForwardServer::Listening: - case SshTcpIpForwardServer::Inactive: - break; - } - }); - return server; -} - -void SshChannelManager::insertChannel(AbstractSshChannel *priv, - const QSharedPointer<QObject> &pub) -{ - connect(priv, &AbstractSshChannel::timeout, this, &SshChannelManager::timeout); - m_channels.insert(priv->localChannelId(), priv); - m_sessions.insert(priv, pub); -} - -void SshChannelManager::handleChannelOpenForwardedTcpIp( - const SshChannelOpenGeneric &channelOpenGeneric) -{ - const SshChannelOpenForwardedTcpIp channelOpen - = SshIncomingPacket::extractChannelOpenForwardedTcpIp(channelOpenGeneric); - - SshTcpIpForwardServer::Ptr server; - - foreach (const SshTcpIpForwardServer::Ptr &candidate, m_listeningForwardServers) { - if (candidate->port() == channelOpen.remotePort - && candidate->bindAddress().toUtf8() == channelOpen.remoteAddress) { - server = candidate; - break; - } - }; - - - if (server.isNull()) { - // Apparently the server knows a remoteAddress we are not aware of. There are plenty of ways - // to make that happen: /etc/hosts on the server, different writings for localhost, - // different DNS servers, ... - // Rather than trying to figure that out, we just use the first listening forwarder with the - // same port. - foreach (const SshTcpIpForwardServer::Ptr &candidate, m_listeningForwardServers) { - if (candidate->port() == channelOpen.remotePort) { - server = candidate; - break; - } - }; - } - - if (server.isNull()) { - try { - m_sendFacility.sendChannelOpenFailurePacket(channelOpen.common.remoteChannel, - SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, - QByteArray()); - } catch (const std::exception &e) { - qCWarning(sshLog, "Botan error: %s", e.what()); - } - return; - } - - SshForwardedTcpIpTunnel::Ptr tunnel(new SshForwardedTcpIpTunnel(m_nextLocalChannelId++, - m_sendFacility)); - tunnel->d->handleOpenSuccess(channelOpen.common.remoteChannel, - channelOpen.common.remoteWindowSize, - channelOpen.common.remoteMaxPacketSize); - tunnel->open(QIODevice::ReadWrite); - server->setNewConnection(tunnel); - insertChannel(tunnel->d, tunnel); -} - -void SshChannelManager::handleChannelOpenX11(const SshChannelOpenGeneric &channelOpenGeneric) -{ - qCDebug(sshLog) << "incoming X11 channel open request"; - const SshChannelOpenX11 channelOpen - = SshIncomingPacket::extractChannelOpenX11(channelOpenGeneric); - if (m_x11DisplayInfo.cookie.isEmpty()) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Server attempted to open an unrequested X11 channel."); - } - SshX11Channel * const x11Channel = new SshX11Channel(m_x11DisplayInfo, - m_nextLocalChannelId++, - m_sendFacility); - x11Channel->setParent(this); - x11Channel->handleOpenSuccess(channelOpen.common.remoteChannel, - channelOpen.common.remoteWindowSize, - channelOpen.common.remoteMaxPacketSize); - insertChannel(x11Channel, QSharedPointer<QObject>()); -} - -int SshChannelManager::closeAllChannels(CloseAllMode mode) -{ - int count = 0; - for (ChannelIterator it = m_channels.begin(); it != m_channels.end(); ++it) { - AbstractSshChannel * const channel = it.value(); - QSSH_ASSERT(channel->channelState() != AbstractSshChannel::Closed); - if (channel->channelState() != AbstractSshChannel::CloseRequested) { - channel->closeChannel(); - ++count; - } - } - if (mode == CloseAllAndReset) { - m_channels.clear(); - m_sessions.clear(); - } - return count; -} - -int SshChannelManager::channelCount() const -{ - return m_channels.count(); -} - -void SshChannelManager::removeChannel(ChannelIterator it) -{ - if (it == m_channels.end()) { - throw SshClientException(SshInternalError, - QLatin1String("Internal error: Unexpected channel lookup failure")); - } - const int removeCount = m_sessions.remove(it.value()); - if (removeCount != 1) { - throw SshClientException(SshInternalError, - QString::fromLatin1("Internal error: Unexpected session count %1 for channel.") - .arg(removeCount)); - } - m_channels.erase(it); -} - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshchannelmanager_p.h b/src/libs/ssh/sshchannelmanager_p.h deleted file mode 100644 index 10e31e7d9e9..00000000000 --- a/src/libs/ssh/sshchannelmanager_p.h +++ /dev/null @@ -1,109 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sshx11displayinfo_p.h" - -#include <QHash> -#include <QObject> -#include <QSharedPointer> - -namespace QSsh { -class SftpChannel; -class SshDirectTcpIpTunnel; -class SshRemoteProcess; -class SshTcpIpForwardServer; - -namespace Internal { - -class AbstractSshChannel; -struct SshChannelOpenGeneric; -class SshIncomingPacket; -class SshSendFacility; -class SshRemoteProcessPrivate; - -class SshChannelManager : public QObject -{ - Q_OBJECT -public: - SshChannelManager(SshSendFacility &sendFacility, QObject *parent); - - QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command); - QSharedPointer<SshRemoteProcess> createRemoteShell(); - QSharedPointer<SftpChannel> createSftpChannel(); - QSharedPointer<SshDirectTcpIpTunnel> createDirectTunnel(const QString &originatingHost, - quint16 originatingPort, const QString &remoteHost, quint16 remotePort); - QSharedPointer<SshTcpIpForwardServer> createForwardServer(const QString &remoteHost, - quint16 remotePort); - - int channelCount() const; - enum CloseAllMode { CloseAllRegular, CloseAllAndReset }; - int closeAllChannels(CloseAllMode mode); - QString x11DisplayName() const { return m_x11DisplayInfo.displayName; } - - void handleChannelRequest(const SshIncomingPacket &packet); - void handleChannelOpen(const SshIncomingPacket &packet); - void handleChannelOpenFailure(const SshIncomingPacket &packet); - void handleChannelOpenConfirmation(const SshIncomingPacket &packet); - void handleChannelSuccess(const SshIncomingPacket &packet); - void handleChannelFailure(const SshIncomingPacket &packet); - void handleChannelWindowAdjust(const SshIncomingPacket &packet); - void handleChannelData(const SshIncomingPacket &packet); - void handleChannelExtendedData(const SshIncomingPacket &packet); - void handleChannelEof(const SshIncomingPacket &packet); - void handleChannelClose(const SshIncomingPacket &packet); - void handleRequestSuccess(const SshIncomingPacket &packet); - void handleRequestFailure(const SshIncomingPacket &packet); - -signals: - void timeout(); - -private: - typedef QHash<quint32, AbstractSshChannel *>::Iterator ChannelIterator; - - ChannelIterator lookupChannelAsIterator(quint32 channelId, - bool allowNotFound = false); - AbstractSshChannel *lookupChannel(quint32 channelId, - bool allowNotFound = false); - void removeChannel(ChannelIterator it); - void insertChannel(AbstractSshChannel *priv, - const QSharedPointer<QObject> &pub); - - void handleChannelOpenForwardedTcpIp(const SshChannelOpenGeneric &channelOpenGeneric); - void handleChannelOpenX11(const SshChannelOpenGeneric &channelOpenGeneric); - - SshSendFacility &m_sendFacility; - QHash<quint32, AbstractSshChannel *> m_channels; - QHash<AbstractSshChannel *, QSharedPointer<QObject> > m_sessions; - quint32 m_nextLocalChannelId; - QList<QSharedPointer<SshTcpIpForwardServer>> m_waitingForwardServers; - QList<QSharedPointer<SshTcpIpForwardServer>> m_listeningForwardServers; - QList<SshRemoteProcessPrivate *> m_x11ForwardingRequests; - X11DisplayInfo m_x11DisplayInfo; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshconnection.cpp b/src/libs/ssh/sshconnection.cpp index 8495da64c42..6dd2e49f9d4 100644 --- a/src/libs/ssh/sshconnection.cpp +++ b/src/libs/ssh/sshconnection.cpp @@ -24,48 +24,49 @@ ****************************************************************************/ #include "sshconnection.h" -#include "sshconnection_p.h" - -#include "sftpchannel.h" -#include "sshagent_p.h" -#include "sshcapabilities_p.h" -#include "sshchannelmanager_p.h" -#include "sshcryptofacility_p.h" -#include "sshdirecttcpiptunnel.h" -#include "sshtcpipforwardserver.h" -#include "sshexception_p.h" -#include "sshkeyexchange_p.h" + +#include "sftpsession.h" +#include "sftptransfer.h" #include "sshlogging_p.h" +#include "sshprocess_p.h" #include "sshremoteprocess.h" +#include "sshsettings.h" + +#include <utils/filesystemwatcher.h> +#include <utils/fileutils.h> +#include <utils/hostosinfo.h> +#include <utils/qtcassert.h> +#include <utils/temporarydirectory.h> + +#include <QByteArrayList> +#include <QDir> +#include <QFileInfo> +#include <QTemporaryDir> +#include <QTimer> -#include <QFile> -#include <QMutex> -#include <QMutexLocker> -#include <QNetworkProxy> -#include <QRegExp> -#include <QTcpSocket> +#include <memory> /*! \class QSsh::SshConnection - \brief The SshConnection class provides an SSH connection, implementing - protocol version 2.0. + \brief The SshConnection class provides an SSH connection via an OpenSSH client + running in master mode. - It can spawn channels for remote execution and SFTP operations (version 3). It operates asynchronously (non-blocking) and is not thread-safe. + + If connection sharing is turned off, the class operates as a simple factory + for processes etc and "connecting" always succeeds. The actual connection + is then established later, e.g. when starting the remote process. + */ namespace QSsh { +using namespace Internal; +using namespace Utils; -const QByteArray ClientId("SSH-2.0-QtCreator\r\n"); - -SshConnectionParameters::SshConnectionParameters() : - timeout(0), authenticationType(AuthenticationTypePublicKey), - hostKeyCheckingMode(SshHostKeyCheckingNone) +SshConnectionParameters::SshConnectionParameters() { url.setPort(0); - options |= SshIgnoreDefaultProxy; - options |= SshEnableStrictConformanceChecks; } static inline bool equals(const SshConnectionParameters &p1, const SshConnectionParameters &p2) @@ -74,6 +75,7 @@ static inline bool equals(const SshConnectionParameters &p1, const SshConnection && p1.authenticationType == p2.authenticationType && p1.privateKeyFile == p2.privateKeyFile && p1.hostKeyCheckingMode == p2.hostKeyCheckingMode + && p1.x11DisplayName == p2.x11DisplayName && p1.timeout == p2.timeout; } @@ -87,929 +89,301 @@ bool operator!=(const SshConnectionParameters &p1, const SshConnectionParameters return !equals(p1, p2); } - -SshConnection::SshConnection(const SshConnectionParameters &serverInfo, QObject *parent) - : QObject(parent) -{ - qRegisterMetaType<QSsh::SshError>("QSsh::SshError"); - qRegisterMetaType<QSsh::SftpJobId>("QSsh::SftpJobId"); - qRegisterMetaType<QSsh::SftpFileInfo>("QSsh::SftpFileInfo"); - qRegisterMetaType<QList <QSsh::SftpFileInfo> >("QList<QSsh::SftpFileInfo>"); - - d = new Internal::SshConnectionPrivate(this, serverInfo); - connect(d, &Internal::SshConnectionPrivate::connected, this, &SshConnection::connected, - Qt::QueuedConnection); - connect(d, &Internal::SshConnectionPrivate::dataAvailable, this, - &SshConnection::dataAvailable, Qt::QueuedConnection); - connect(d, &Internal::SshConnectionPrivate::disconnected, this, &SshConnection::disconnected, - Qt::QueuedConnection); - connect(d, &Internal::SshConnectionPrivate::error, this, - &SshConnection::error, Qt::QueuedConnection); -} - -void SshConnection::connectToHost() -{ - d->connectToHost(); -} - -void SshConnection::disconnectFromHost() -{ - d->closeConnection(Internal::SSH_DISCONNECT_BY_APPLICATION, SshNoError, "", - QString()); -} - -SshConnection::State SshConnection::state() const -{ - switch (d->state()) { - case Internal::SocketUnconnected: - return Unconnected; - case Internal::ConnectionEstablished: - return Connected; - default: - return Connecting; - } -} - -SshError SshConnection::errorState() const -{ - return d->errorState(); -} - -QString SshConnection::errorString() const -{ - return d->errorString(); -} - -SshConnectionParameters SshConnection::connectionParameters() const -{ - return d->m_connParams; -} - -SshConnectionInfo SshConnection::connectionInfo() const -{ - QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, SshConnectionInfo()); - - return SshConnectionInfo(d->m_socket->localAddress(), d->m_socket->localPort(), - d->m_socket->peerAddress(), d->m_socket->peerPort()); -} - -SshConnection::~SshConnection() -{ - disconnect(); - disconnectFromHost(); - delete d; -} - -QSharedPointer<SshRemoteProcess> SshConnection::createRemoteProcess(const QByteArray &command) -{ - QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, QSharedPointer<SshRemoteProcess>()); - return d->createRemoteProcess(command); -} - -QSharedPointer<SshRemoteProcess> SshConnection::createRemoteShell() -{ - QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, QSharedPointer<SshRemoteProcess>()); - return d->createRemoteShell(); -} - -QSharedPointer<SftpChannel> SshConnection::createSftpChannel() -{ - QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, QSharedPointer<SftpChannel>()); - return d->createSftpChannel(); -} - -SshDirectTcpIpTunnel::Ptr SshConnection::createDirectTunnel(const QString &originatingHost, - quint16 originatingPort, const QString &remoteHost, quint16 remotePort) -{ - QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, SshDirectTcpIpTunnel::Ptr()); - return d->createDirectTunnel(originatingHost, originatingPort, remoteHost, remotePort); -} - -QSharedPointer<SshTcpIpForwardServer> SshConnection::createForwardServer(const QString &remoteHost, - quint16 remotePort) -{ - QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, SshTcpIpForwardServer::Ptr()); - return d->createForwardServer(remoteHost, remotePort); -} - -int SshConnection::closeAllChannels() +struct SshConnection::SshConnectionPrivate { - try { - return d->m_channelManager->closeAllChannels(Internal::SshChannelManager::CloseAllRegular); - } catch (const std::exception &e) { - qCWarning(Internal::sshLog, "%s: %s", Q_FUNC_INFO, e.what()); - return -1; + QString fullProcessError() + { + QString error; + if (masterProcess.exitStatus() != QProcess::NormalExit) + error = masterProcess.errorString(); + const QByteArray stdErr = masterProcess.readAllStandardError(); + if (!stdErr.isEmpty()) { + if (!error.isEmpty()) + error.append('\n'); + error.append(QString::fromLocal8Bit(stdErr)); + } + return error; } -} - -int SshConnection::channelCount() const -{ - return d->m_channelManager->channelCount(); -} - -QString SshConnection::x11DisplayName() const -{ - return d->m_channelManager->x11DisplayName(); -} - -namespace Internal { - -SshConnectionPrivate::SshConnectionPrivate(SshConnection *conn, - const SshConnectionParameters &serverInfo) - : m_socket(new QTcpSocket(this)), m_state(SocketUnconnected), - m_sendFacility(m_socket), - m_channelManager(new SshChannelManager(m_sendFacility, this)), - m_connParams(serverInfo), m_error(SshNoError), m_ignoreNextPacket(false), - m_conn(conn) -{ - setupPacketHandlers(); - m_socket->setProxy((m_connParams.options & SshIgnoreDefaultProxy) - ? QNetworkProxy::NoProxy : QNetworkProxy::DefaultProxy); - m_timeoutTimer.setSingleShot(true); - m_timeoutTimer.setInterval(m_connParams.timeout * 1000); - m_keepAliveTimer.setSingleShot(true); - m_keepAliveTimer.setInterval(10000); - connect(m_channelManager, &SshChannelManager::timeout, - this, &SshConnectionPrivate::handleTimeout); -} - -SshConnectionPrivate::~SshConnectionPrivate() -{ - disconnect(); -} -void SshConnectionPrivate::setupPacketHandlers() -{ - typedef SshConnectionPrivate This; - - setupPacketHandler(SSH_MSG_KEXINIT, StateList() << SocketConnected - << ConnectionEstablished, &This::handleKeyExchangeInitPacket); - setupPacketHandler(SSH_MSG_KEXDH_REPLY, StateList() << SocketConnected - << ConnectionEstablished, &This::handleKeyExchangeReplyPacket); - - setupPacketHandler(SSH_MSG_NEWKEYS, StateList() << SocketConnected - << ConnectionEstablished, &This::handleNewKeysPacket); - setupPacketHandler(SSH_MSG_SERVICE_ACCEPT, - StateList() << UserAuthServiceRequested, - &This::handleServiceAcceptPacket); - if (m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypePassword - || m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods) { - setupPacketHandler(SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, - StateList() << UserAuthRequested, &This::handlePasswordExpiredPacket); - } - setupPacketHandler(SSH_MSG_GLOBAL_REQUEST, - StateList() << ConnectionEstablished, &This::handleGlobalRequest); - - const StateList authReqList = StateList() << UserAuthRequested; - setupPacketHandler(SSH_MSG_USERAUTH_BANNER, authReqList, - &This::handleUserAuthBannerPacket); - setupPacketHandler(SSH_MSG_USERAUTH_SUCCESS, authReqList, - &This::handleUserAuthSuccessPacket); - setupPacketHandler(SSH_MSG_USERAUTH_FAILURE, authReqList, - &This::handleUserAuthFailurePacket); - if (m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypeKeyboardInteractive - || m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods) { - setupPacketHandler(SSH_MSG_USERAUTH_INFO_REQUEST, authReqList, - &This::handleUserAuthInfoRequestPacket); + QString socketFilePath() const + { + QTC_ASSERT(masterSocketDir, return QString()); + return masterSocketDir->path() + "/control_socket"; } - setupPacketHandler(SSH_MSG_USERAUTH_PK_OK, authReqList, &This::handleUserAuthKeyOkPacket); - - const StateList connectedList - = StateList() << ConnectionEstablished; - setupPacketHandler(SSH_MSG_CHANNEL_REQUEST, connectedList, - &This::handleChannelRequest); - setupPacketHandler(SSH_MSG_CHANNEL_OPEN, connectedList, - &This::handleChannelOpen); - setupPacketHandler(SSH_MSG_CHANNEL_OPEN_FAILURE, connectedList, - &This::handleChannelOpenFailure); - setupPacketHandler(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, connectedList, - &This::handleChannelOpenConfirmation); - setupPacketHandler(SSH_MSG_CHANNEL_SUCCESS, connectedList, - &This::handleChannelSuccess); - setupPacketHandler(SSH_MSG_CHANNEL_FAILURE, connectedList, - &This::handleChannelFailure); - setupPacketHandler(SSH_MSG_CHANNEL_WINDOW_ADJUST, connectedList, - &This::handleChannelWindowAdjust); - setupPacketHandler(SSH_MSG_CHANNEL_DATA, connectedList, - &This::handleChannelData); - setupPacketHandler(SSH_MSG_CHANNEL_EXTENDED_DATA, connectedList, - &This::handleChannelExtendedData); - - const StateList connectedOrClosedList - = StateList() << SocketUnconnected << ConnectionEstablished; - setupPacketHandler(SSH_MSG_CHANNEL_EOF, connectedOrClosedList, - &This::handleChannelEof); - setupPacketHandler(SSH_MSG_CHANNEL_CLOSE, connectedOrClosedList, - &This::handleChannelClose); - - setupPacketHandler(SSH_MSG_DISCONNECT, StateList() << SocketConnected << WaitingForAgentKeys - << UserAuthServiceRequested << UserAuthRequested - << ConnectionEstablished, &This::handleDisconnect); - - setupPacketHandler(SSH_MSG_UNIMPLEMENTED, - StateList() << ConnectionEstablished, &This::handleUnimplementedPacket); - - setupPacketHandler(SSH_MSG_REQUEST_SUCCESS, connectedList, - &This::handleRequestSuccess); - setupPacketHandler(SSH_MSG_REQUEST_FAILURE, connectedList, - &This::handleRequestFailure); -} -void SshConnectionPrivate::setupPacketHandler(SshPacketType type, - const SshConnectionPrivate::StateList &states, - SshConnectionPrivate::PacketHandler handler) -{ - m_packetHandlers.insert(type, HandlerInStates(states, handler)); -} + QStringList connectionArgs() const + { + QString hostKeyCheckingString; + switch (connParams.hostKeyCheckingMode) { + case SshHostKeyCheckingNone: + case SshHostKeyCheckingAllowNoMatch: + // There is "accept-new" as well, but only since 7.6. + hostKeyCheckingString = "no"; + break; + case SshHostKeyCheckingStrict: + hostKeyCheckingString = "yes"; + break; + } + QStringList args{"-o", "StrictHostKeyChecking=" + hostKeyCheckingString, + "-o", "User=" + connParams.userName(), + "-o", "Port=" + QString::number(connParams.port())}; + const bool keyOnly = connParams.authenticationType == + SshConnectionParameters::AuthenticationTypeSpecificKey; + if (keyOnly) + args << "-i" << connParams.privateKeyFile; + if (keyOnly || SshSettings::askpassFilePath().isEmpty()) + args << "-o" << "BatchMode=yes"; + if (sharingEnabled) + args << "-o" << ("ControlPath=" + socketFilePath()); + if (connParams.timeout != 0) + args << "-o" << ("ConnectTimeout=" + QString::number(connParams.timeout)); + return args << connParams.host(); + } + + SshConnectionParameters connParams; + SshConnectionInfo connInfo; + SshProcess masterProcess; + QString errorString; + QTimer socketWatcherTimer; + std::unique_ptr<TemporaryDirectory> masterSocketDir; + FileSystemWatcher *socketWatcher = nullptr; + State state = Unconnected; + const bool sharingEnabled = SshSettings::connectionSharingEnabled(); +}; -void SshConnectionPrivate::handleSocketConnected() -{ - m_state = SocketConnected; - sendData(ClientId); -} -void SshConnectionPrivate::handleIncomingData() +SshConnection::SshConnection(const SshConnectionParameters &serverInfo, QObject *parent) + : QObject(parent), d(new SshConnectionPrivate) { - if (m_state == SocketUnconnected) - return; // For stuff queued in the event loop after we've called closeConnection(); - - try { - if (!canUseSocket()) + qRegisterMetaType<QSsh::SftpFileInfo>("QSsh::SftpFileInfo"); + qRegisterMetaType<QList <QSsh::SftpFileInfo> >("QList<QSsh::SftpFileInfo>"); + d->connParams = serverInfo; + d->socketWatcher = new FileSystemWatcher(this); + connect(&d->masterProcess, &QProcess::started, [this] { + QFileInfo socketInfo(d->socketFilePath()); + if (socketInfo.exists()) { + emitConnected(); return; - m_incomingData += m_socket->readAll(); - qCDebug(sshLog, "state = %d, remote data size = %d", m_state, m_incomingData.count()); - if (m_serverId.isEmpty()) - handleServerId(); - handlePackets(); - } catch (const SshServerException &e) { - closeConnection(e.error, SshProtocolError, e.errorStringServer, - tr("SSH Protocol error: %1").arg(e.errorStringUser)); - } catch (const SshClientException &e) { - closeConnection(SSH_DISCONNECT_BY_APPLICATION, e.error, "", - e.errorString); - } catch (const std::exception &e) { - closeConnection(SSH_DISCONNECT_BY_APPLICATION, SshInternalError, "", - tr("Botan library exception: %1").arg(QString::fromLatin1(e.what()))); - } -} - -// RFC 4253, 4.2. -void SshConnectionPrivate::handleServerId() -{ - qCDebug(sshLog, "%s: incoming data size = %d, incoming data = '%s'", - Q_FUNC_INFO, m_incomingData.count(), m_incomingData.data()); - const int newLinePos = m_incomingData.indexOf('\n'); - if (newLinePos == -1) - return; // Not enough data yet. - - // Lines not starting with "SSH-" are ignored. - if (!m_incomingData.startsWith("SSH-")) { - m_incomingData.remove(0, newLinePos + 1); - m_serverHasSentDataBeforeId = true; - return; - } - - if (newLinePos > 255 - 1) { - throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, - "Identification string too long.", - tr("Server identification string is %n characters long, but the maximum " - "allowed length is 255.", 0, newLinePos + 1)); - } - - const bool hasCarriageReturn = m_incomingData.at(newLinePos - 1) == '\r'; - m_serverId = m_incomingData.left(newLinePos); - if (hasCarriageReturn) - m_serverId.chop(1); - m_incomingData.remove(0, newLinePos + 1); - - if (m_serverId.contains('\0')) { - throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, - "Identification string contains illegal NUL character.", - tr("Server identification string contains illegal NUL character.")); - } - - // "printable US-ASCII characters, with the exception of whitespace characters - // and the minus sign" - QString legalString = QLatin1String("[]!\"#$!&'()*+,./0-9:;<=>?@A-Z[\\\\^_`a-z{|}~]+"); - const QRegExp versionIdpattern(QString::fromLatin1("SSH-(%1)-%1(?: .+)?").arg(legalString)); - if (!versionIdpattern.exactMatch(QString::fromLatin1(m_serverId))) { - throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, - "Identification string is invalid.", - tr("Server Identification string \"%1\" is invalid.") - .arg(QString::fromLatin1(m_serverId))); - } - const QString serverProtoVersion = versionIdpattern.cap(1); - if (serverProtoVersion != QLatin1String("2.0") && serverProtoVersion != QLatin1String("1.99")) { - throw SshServerException(SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED, - "Invalid protocol version.", - tr("Server protocol version is \"%1\", but needs to be 2.0 or 1.99.") - .arg(serverProtoVersion)); - } - - if (m_connParams.options & SshEnableStrictConformanceChecks) { - if (serverProtoVersion == QLatin1String("2.0") && !hasCarriageReturn) { - throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, - "Identification string is invalid.", - tr("Server identification string is invalid (missing carriage return).")); } - - if (serverProtoVersion == QLatin1String("1.99") && m_serverHasSentDataBeforeId) { - throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, - "No extra data preceding identification string allowed for 1.99.", - tr("Server reports protocol version 1.99, but sends data " - "before the identification string, which is not allowed.")); + const auto socketFileChecker = [this] { + if (!QFileInfo::exists(d->socketFilePath())) + return; + d->socketWatcher->disconnect(); + d->socketWatcher->removeDirectory(QFileInfo(d->socketFilePath()).path()); + d->socketWatcherTimer.disconnect(); + d->socketWatcherTimer.stop(); + emitConnected(); + }; + connect(d->socketWatcher, &FileSystemWatcher::directoryChanged, socketFileChecker); + d->socketWatcher->addDirectory(socketInfo.path(), FileSystemWatcher::WatchAllChanges); + if (HostOsInfo::isMacHost()) { + // QTBUG-72455 + d->socketWatcherTimer.setInterval(1000); + connect(&d->socketWatcherTimer, &QTimer::timeout, socketFileChecker); + d->socketWatcherTimer.start(); } + }); + connect(&d->masterProcess, &QProcess::errorOccurred, [this] (QProcess::ProcessError error) { + switch (error) { + case QProcess::FailedToStart: + emitError(tr("Cannot establish SSH connection: Control process failed to start: %1") + .arg(d->fullProcessError())); + break; + case QProcess::Crashed: // Handled by finished() handler. + case QProcess::Timedout: + case QProcess::ReadError: + case QProcess::WriteError: + case QProcess::UnknownError: + break; // Cannot happen. + } + }); + connect(&d->masterProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished), [this] { + if (d->state == Disconnecting) { + emitDisconnected(); + return; + } + const QString procError = d->fullProcessError(); + QString errorMsg = tr("SSH connection failure."); + if (!procError.isEmpty()) + errorMsg.append('\n').append(procError); + emitError(errorMsg); + }); + if (!d->connParams.x11DisplayName.isEmpty()) { + QProcessEnvironment env = d->masterProcess.processEnvironment(); + env.insert("DISPLAY", d->connParams.x11DisplayName); + d->masterProcess.setProcessEnvironment(env); } - - m_keyExchange.reset(new SshKeyExchange(m_connParams, m_sendFacility)); - m_keyExchange->sendKexInitPacket(m_serverId); - m_keyExchangeState = KexInitSent; -} - -void SshConnectionPrivate::handlePackets() -{ - m_incomingPacket.consumeData(m_incomingData); - while (m_incomingPacket.isComplete()) { - handleCurrentPacket(); - m_incomingPacket.clear(); - m_incomingPacket.consumeData(m_incomingData); - } -} - -void SshConnectionPrivate::handleCurrentPacket() -{ - Q_ASSERT(m_incomingPacket.isComplete()); - Q_ASSERT(m_keyExchangeState == DhInitSent || !m_ignoreNextPacket); - - if (m_ignoreNextPacket) { - m_ignoreNextPacket = false; - return; - } - - QHash<SshPacketType, HandlerInStates>::ConstIterator it - = m_packetHandlers.constFind(m_incomingPacket.type()); - if (it == m_packetHandlers.constEnd()) { - m_sendFacility.sendMsgUnimplementedPacket(m_incomingPacket.serverSeqNr()); - return; - } - if (!it.value().first.contains(m_state)) { - handleUnexpectedPacket(); - return; - } - (this->*it.value().second)(); -} - -void SshConnectionPrivate::handleKeyExchangeInitPacket() -{ - if (m_keyExchangeState != NoKeyExchange - && m_keyExchangeState != KexInitSent) { - throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected packet.", tr("Unexpected packet of type %1.") - .arg(m_incomingPacket.type())); - } - - // Server-initiated re-exchange. - if (m_keyExchangeState == NoKeyExchange) { - m_keyExchange.reset(new SshKeyExchange(m_connParams, m_sendFacility)); - m_keyExchange->sendKexInitPacket(m_serverId); - } - - // If the server sends a guessed packet, the guess must be wrong, - // because the algorithms we support require us to initiate the - // key exchange. - if (m_keyExchange->sendDhInitPacket(m_incomingPacket)) - m_ignoreNextPacket = true; - - m_keyExchangeState = DhInitSent; -} - -void SshConnectionPrivate::handleKeyExchangeReplyPacket() -{ - if (m_keyExchangeState != DhInitSent) { - throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected packet.", tr("Unexpected packet of type %1.") - .arg(m_incomingPacket.type())); - } - - m_keyExchange->sendNewKeysPacket(m_incomingPacket, - ClientId.left(ClientId.size() - 2)); - m_sendFacility.recreateKeys(*m_keyExchange); - m_keyExchangeState = NewKeysSent; } -void SshConnectionPrivate::handleNewKeysPacket() +void SshConnection::connectToHost() { - if (m_keyExchangeState != NewKeysSent) { - throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected packet.", tr("Unexpected packet of type %1.") - .arg(m_incomingPacket.type())); - } - - m_incomingPacket.recreateKeys(*m_keyExchange); - m_keyExchange.reset(); - m_keyExchangeState = NoKeyExchange; - - if (m_state == SocketConnected) { - m_sendFacility.sendUserAuthServiceRequestPacket(); - m_state = UserAuthServiceRequested; - } + d->state = Connecting; + QTimer::singleShot(0, this, &SshConnection::doConnectToHost); } -void SshConnectionPrivate::handleServiceAcceptPacket() +void SshConnection::disconnectFromHost() { - switch (m_connParams.authenticationType) { - case SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods: - m_triedAllPasswordBasedMethods = false; - // Fall-through. - case SshConnectionParameters::AuthenticationTypePassword: - m_sendFacility.sendUserAuthByPasswordRequestPacket(m_connParams.userName().toUtf8(), - SshCapabilities::SshConnectionService, m_connParams.password().toUtf8()); - break; - case SshConnectionParameters::AuthenticationTypeKeyboardInteractive: - m_sendFacility.sendUserAuthByKeyboardInteractiveRequestPacket(m_connParams.userName().toUtf8(), - SshCapabilities::SshConnectionService); - break; - case SshConnectionParameters::AuthenticationTypePublicKey: - authenticateWithPublicKey(); - break; - case SshConnectionParameters::AuthenticationTypeAgent: - if (SshAgent::publicKeys().isEmpty()) { - if (m_agentKeysUpToDate) - throw SshClientException(SshAuthenticationError, tr("ssh-agent has no keys.")); - qCDebug(sshLog) << "agent has no keys yet, waiting"; - m_state = WaitingForAgentKeys; + switch (d->state) { + case Connecting: + case Connected: + if (!d->sharingEnabled) { + emitDisconnected(); return; - } else { - tryAllAgentKeys(); } + d->state = Disconnecting; + if (HostOsInfo::isWindowsHost()) + d->masterProcess.kill(); + else + d->masterProcess.terminate(); + break; + case Unconnected: + case Disconnecting: break; } - m_state = UserAuthRequested; -} - -void SshConnectionPrivate::handlePasswordExpiredPacket() -{ - if (m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods - && m_triedAllPasswordBasedMethods) { - // This means we just tried to authorize via "keyboard-interactive", in which case - // this type of packet is not allowed. - handleUnexpectedPacket(); - return; - } - throw SshClientException(SshAuthenticationError, tr("Password expired.")); -} - -void SshConnectionPrivate::handleUserAuthInfoRequestPacket() -{ - if (m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods - && !m_triedAllPasswordBasedMethods) { - // This means we just tried to authorize via "password", in which case - // this type of packet is not allowed. - handleUnexpectedPacket(); - return; - } - - const SshUserAuthInfoRequestPacket requestPacket - = m_incomingPacket.extractUserAuthInfoRequest(); - QStringList responses; - responses.reserve(requestPacket.prompts.count()); - - // Not very interactive, admittedly, but we don't want to be for now. - for (int i = 0; i < requestPacket.prompts.count(); ++i) - responses << m_connParams.password(); - m_sendFacility.sendUserAuthInfoResponsePacket(responses); -} - -void SshConnectionPrivate::handleUserAuthBannerPacket() -{ - emit dataAvailable(m_incomingPacket.extractUserAuthBanner().message); -} - -void SshConnectionPrivate::handleUnexpectedPacket() -{ - throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected packet.", tr("Unexpected packet of type %1.") - .arg(m_incomingPacket.type())); } -void SshConnectionPrivate::handleGlobalRequest() +SshConnection::State SshConnection::state() const { - m_sendFacility.sendRequestFailurePacket(); + return d->state; } -void SshConnectionPrivate::handleUserAuthSuccessPacket() +QString SshConnection::errorString() const { - m_state = ConnectionEstablished; - m_timeoutTimer.stop(); - emit connected(); - m_lastInvalidMsgSeqNr = InvalidSeqNr; - connect(&m_keepAliveTimer, &QTimer::timeout, this, &SshConnectionPrivate::sendKeepAlivePacket); - m_keepAliveTimer.start(); + return d->errorString; } -void SshConnectionPrivate::handleUserAuthFailurePacket() +SshConnectionParameters SshConnection::connectionParameters() const { - if (!m_pendingKeyChecks.isEmpty()) { - const QByteArray key = m_pendingKeyChecks.dequeue(); - SshAgent::removeDataToSign(key, tokenForAgent()); - qCDebug(sshLog) << "server rejected one of the keys supplied by the agent," - << m_pendingKeyChecks.count() << "keys remaining"; - if (m_pendingKeyChecks.isEmpty() && m_agentKeyToUse.isEmpty()) { - throw SshClientException(SshAuthenticationError, tr("The server rejected all keys " - "known to the ssh-agent.")); - } - return; - } - - // TODO: Evaluate "authentications that can continue" field and act on it. - if (m_connParams.authenticationType - == SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods - && !m_triedAllPasswordBasedMethods) { - m_triedAllPasswordBasedMethods = true; - m_sendFacility.sendUserAuthByKeyboardInteractiveRequestPacket( - m_connParams.userName().toUtf8(), - SshCapabilities::SshConnectionService); - return; - } - - m_timeoutTimer.stop(); - QString errorMsg; - switch (m_connParams.authenticationType) { - case SshConnectionParameters::AuthenticationTypePublicKey: - case SshConnectionParameters::AuthenticationTypeAgent: - errorMsg = tr("Server rejected key."); - break; - default: - errorMsg = tr("Server rejected password."); - break; - } - throw SshClientException(SshAuthenticationError, errorMsg); + return d->connParams; } -void SshConnectionPrivate::handleUserAuthKeyOkPacket() +SshConnectionInfo SshConnection::connectionInfo() const { - const SshUserAuthPkOkPacket &msg = m_incomingPacket.extractUserAuthPkOk(); - qCDebug(sshLog) << "server accepted key of type" << msg.algoName; - - if (m_pendingKeyChecks.isEmpty()) { - throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, "Unexpected packet", - tr("Server sent unexpected SSH_MSG_USERAUTH_PK_OK packet.")); + QTC_ASSERT(state() == Connected, return SshConnectionInfo()); + if (d->connInfo.isValid()) + return d->connInfo; + QProcess p; + p.start(SshSettings::sshFilePath().toString(), d->connectionArgs() << "echo" << "-n" + << "$SSH_CLIENT"); + if (!p.waitForStarted() || !p.waitForFinished()) { + qCWarning(Internal::sshLog) << "failed to retrieve connection info:" << p.errorString(); + return SshConnectionInfo(); } - const QByteArray key = m_pendingKeyChecks.dequeue(); - if (key != msg.keyBlob) { - // The server must answer the requests in the order we sent them. - throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, "Unexpected packet content", - tr("Server sent unexpected key in SSH_MSG_USERAUTH_PK_OK packet.")); + const QByteArrayList data = p.readAllStandardOutput().split(' '); + if (data.size() != 3) { + qCWarning(Internal::sshLog) << "failed to retrieve connection info: unexpected output"; + return SshConnectionInfo(); } - const uint token = tokenForAgent(); - if (!m_agentKeyToUse.isEmpty()) { - qCDebug(sshLog) << "another key has already been accepted, ignoring this one"; - SshAgent::removeDataToSign(key, token); - return; + d->connInfo.localPort = data.at(1).toInt(); + if (d->connInfo.localPort == 0) { + qCWarning(Internal::sshLog) << "failed to retrieve connection info: unexpected output"; + return SshConnectionInfo(); } - m_agentKeyToUse = key; - qCDebug(sshLog) << "requesting signature from agent"; - SshAgent::requestSignature(key, token); -} - -void SshConnectionPrivate::handleDebugPacket() -{ - const SshDebug &msg = m_incomingPacket.extractDebug(); - if (msg.display) - emit dataAvailable(msg.message); -} - -void SshConnectionPrivate::handleUnimplementedPacket() -{ - const SshUnimplemented &msg = m_incomingPacket.extractUnimplemented(); - if (msg.invalidMsgSeqNr != m_lastInvalidMsgSeqNr) { - throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected packet", tr("The server sent an unexpected SSH packet " - "of type SSH_MSG_UNIMPLEMENTED.")); + if (!d->connInfo.localAddress.setAddress(QString::fromLatin1(data.first()))) { + qCWarning(Internal::sshLog) << "failed to retrieve connection info: unexpected output"; + return SshConnectionInfo(); } - m_lastInvalidMsgSeqNr = InvalidSeqNr; - m_timeoutTimer.stop(); - m_keepAliveTimer.start(); -} - -void SshConnectionPrivate::handleChannelRequest() -{ - m_channelManager->handleChannelRequest(m_incomingPacket); -} - -void SshConnectionPrivate::handleChannelOpen() -{ - m_channelManager->handleChannelOpen(m_incomingPacket); -} - -void SshConnectionPrivate::handleChannelOpenFailure() -{ - m_channelManager->handleChannelOpenFailure(m_incomingPacket); -} - -void SshConnectionPrivate::handleChannelOpenConfirmation() -{ - m_channelManager->handleChannelOpenConfirmation(m_incomingPacket); -} - -void SshConnectionPrivate::handleChannelSuccess() -{ - m_channelManager->handleChannelSuccess(m_incomingPacket); -} - -void SshConnectionPrivate::handleChannelFailure() -{ - m_channelManager->handleChannelFailure(m_incomingPacket); -} - -void SshConnectionPrivate::handleChannelWindowAdjust() -{ - m_channelManager->handleChannelWindowAdjust(m_incomingPacket); + d->connInfo.peerPort = d->connParams.port(); + d->connInfo.peerAddress.setAddress(d->connParams.host()); + return d->connInfo; } -void SshConnectionPrivate::handleChannelData() +bool SshConnection::sharingEnabled() const { - m_channelManager->handleChannelData(m_incomingPacket); + return d->sharingEnabled; } -void SshConnectionPrivate::handleChannelExtendedData() -{ - m_channelManager->handleChannelExtendedData(m_incomingPacket); -} - -void SshConnectionPrivate::handleChannelEof() -{ - m_channelManager->handleChannelEof(m_incomingPacket); -} - -void SshConnectionPrivate::handleChannelClose() -{ - m_channelManager->handleChannelClose(m_incomingPacket); -} - -void SshConnectionPrivate::handleDisconnect() -{ - const SshDisconnect msg = m_incomingPacket.extractDisconnect(); - throw SshServerException(SSH_DISCONNECT_CONNECTION_LOST, - "", tr("Server closed connection: %1").arg(msg.description)); -} - -void SshConnectionPrivate::handleRequestSuccess() -{ - m_channelManager->handleRequestSuccess(m_incomingPacket); -} - -void SshConnectionPrivate::handleRequestFailure() +SshConnection::~SshConnection() { - m_channelManager->handleRequestFailure(m_incomingPacket); + disconnect(); + disconnectFromHost(); + delete d; } -void SshConnectionPrivate::sendData(const QByteArray &data) +SshRemoteProcessPtr SshConnection::createRemoteProcess(const QByteArray &command) { - if (canUseSocket()) - m_socket->write(data); + QTC_ASSERT(state() == Connected, return SshRemoteProcessPtr()); + return SshRemoteProcessPtr(new SshRemoteProcess(command, d->connectionArgs())); } -uint SshConnectionPrivate::tokenForAgent() const +SshRemoteProcessPtr SshConnection::createRemoteShell() { - return qHash(m_sendFacility.sessionId()); + return createRemoteProcess(QByteArray()); } -void SshConnectionPrivate::handleSocketDisconnected() +SftpTransferPtr SshConnection::createUpload(const FilesToTransfer &files, + FileTransferErrorHandling errorHandlingMode) { - closeConnection(SSH_DISCONNECT_CONNECTION_LOST, SshClosedByServerError, - "Connection closed unexpectedly.", - tr("Connection closed unexpectedly.")); + return setupTransfer(files, Internal::FileTransferType::Upload, errorHandlingMode); } -void SshConnectionPrivate::handleSocketError() +SftpTransferPtr SshConnection::createDownload(const FilesToTransfer &files, + FileTransferErrorHandling errorHandlingMode) { - if (m_error == SshNoError) { - closeConnection(SSH_DISCONNECT_CONNECTION_LOST, SshSocketError, - "Network error", m_socket->errorString()); - } + return setupTransfer(files, Internal::FileTransferType::Download, errorHandlingMode); } -void SshConnectionPrivate::handleTimeout() +SftpSessionPtr SshConnection::createSftpSession() { - const QString errorMessage = m_state == WaitingForAgentKeys - ? tr("Timeout waiting for keys from ssh-agent.") - : tr("Timeout waiting for reply from server."); - closeConnection(SSH_DISCONNECT_BY_APPLICATION, SshTimeoutError, "", errorMessage); + QTC_ASSERT(state() == Connected, return SftpSessionPtr()); + return SftpSessionPtr(new SftpSession(d->connectionArgs())); } -void SshConnectionPrivate::sendKeepAlivePacket() +void SshConnection::doConnectToHost() { - // This type of message is not allowed during key exchange. - if (m_keyExchangeState != NoKeyExchange) { - m_keepAliveTimer.start(); + if (d->state != Connecting) return; - } - - Q_ASSERT(m_lastInvalidMsgSeqNr == InvalidSeqNr); - m_lastInvalidMsgSeqNr = m_sendFacility.nextClientSeqNr(); - m_sendFacility.sendInvalidPacket(); - m_timeoutTimer.start(); -} - -void SshConnectionPrivate::handleAgentKeysUpdated() -{ - m_agentKeysUpToDate = true; - if (m_state == WaitingForAgentKeys) { - m_state = UserAuthRequested; - tryAllAgentKeys(); - } -} - -void SshConnectionPrivate::handleSignatureFromAgent(const QByteArray &key, - const QByteArray &signature, uint token) -{ - if (token != tokenForAgent()) { - qCDebug(sshLog) << "signature is for different connection, ignoring"; + const FileName sshBinary = SshSettings::sshFilePath(); + if (!sshBinary.exists()) { + emitError(tr("Cannot establish SSH connection: ssh binary \"%1\" does not exist.") + .arg(sshBinary.toUserOutput())); return; } - QSSH_ASSERT(key == m_agentKeyToUse); - m_agentSignature = signature; - authenticateWithPublicKey(); -} - -void SshConnectionPrivate::tryAllAgentKeys() -{ - const QList<QByteArray> &keys = SshAgent::publicKeys(); - if (keys.isEmpty()) - throw SshClientException(SshAuthenticationError, tr("ssh-agent has no keys.")); - qCDebug(sshLog) << "trying authentication with" << keys.count() - << "public keys received from agent"; - foreach (const QByteArray &key, keys) { - m_sendFacility.sendQueryPublicKeyPacket(m_connParams.userName().toUtf8(), - SshCapabilities::SshConnectionService, key); - m_pendingKeyChecks.enqueue(key); - } -} - -void SshConnectionPrivate::authenticateWithPublicKey() -{ - qCDebug(sshLog) << "sending actual authentication request"; - - QByteArray key; - QByteArray signature; - if (m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypeAgent) { - // Agent is not needed anymore after this point. - disconnect(&SshAgent::instance(), 0, this, 0); - - key = m_agentKeyToUse; - signature = m_agentSignature; - } - - m_sendFacility.sendUserAuthByPublicKeyRequestPacket(m_connParams.userName().toUtf8(), - SshCapabilities::SshConnectionService, key, signature); -} - -void SshConnectionPrivate::setAgentError() -{ - m_error = SshAgentError; - m_errorString = SshAgent::errorString(); - emit error(m_error); -} - -void SshConnectionPrivate::connectToHost() -{ - QSSH_ASSERT_AND_RETURN(m_state == SocketUnconnected); - - m_incomingData.clear(); - m_incomingPacket.reset(); - m_sendFacility.reset(); - m_error = SshNoError; - m_ignoreNextPacket = false; - m_errorString.clear(); - m_serverId.clear(); - m_serverHasSentDataBeforeId = false; - m_agentSignature.clear(); - m_agentKeysUpToDate = false; - m_pendingKeyChecks.clear(); - m_agentKeyToUse.clear(); - - switch (m_connParams.authenticationType) { - case SshConnectionParameters::AuthenticationTypePublicKey: - try { - createPrivateKey(); - break; - } catch (const SshClientException &ex) { - m_error = ex.error; - m_errorString = ex.errorString; - emit error(m_error); - return; - } - case SshConnectionParameters::AuthenticationTypeAgent: - if (SshAgent::hasError()) { - setAgentError(); - return; - } - connect(&SshAgent::instance(), &SshAgent::errorOccurred, - this, &SshConnectionPrivate::setAgentError); - connect(&SshAgent::instance(), &SshAgent::keysUpdated, - this, &SshConnectionPrivate::handleAgentKeysUpdated); - SshAgent::refreshKeys(); - connect(&SshAgent::instance(), &SshAgent::signatureAvailable, - this, &SshConnectionPrivate::handleSignatureFromAgent); - break; - default: - break; - } - - connect(m_socket, &QAbstractSocket::connected, - this, &SshConnectionPrivate::handleSocketConnected); - connect(m_socket, &QIODevice::readyRead, - this, &SshConnectionPrivate::handleIncomingData); - connect(m_socket, - static_cast<void (QAbstractSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error), - this, &SshConnectionPrivate::handleSocketError); - connect(m_socket, &QAbstractSocket::disconnected, - this, &SshConnectionPrivate::handleSocketDisconnected); - connect(&m_timeoutTimer, &QTimer::timeout, this, &SshConnectionPrivate::handleTimeout); - m_state = SocketConnecting; - m_keyExchangeState = NoKeyExchange; - m_timeoutTimer.start(); - m_socket->connectToHost(m_connParams.host(), m_connParams.port()); -} - -void SshConnectionPrivate::closeConnection(SshErrorCode sshError, - SshError userError, const QByteArray &serverErrorString, - const QString &userErrorString) -{ - // Prevent endless loops by recursive exceptions. - if (m_state == SocketUnconnected || m_error != SshNoError) + if (!d->sharingEnabled) + emitConnected(); + QTC_ASSERT(TemporaryDirectory::masterTemporaryDirectory(), return); + d->masterSocketDir.reset(new TemporaryDirectory("qtc-ssh-XXXXXX")); + if (!d->masterSocketDir->isValid()) { + emitError(tr("Cannot establish SSH connection: Failed to create temporary " + "directory for control socket: %1") + .arg(d->masterSocketDir->errorString())); return; - - m_error = userError; - m_errorString = userErrorString; - m_timeoutTimer.stop(); - disconnect(m_socket, 0, this, 0); - disconnect(&m_timeoutTimer, 0, this, 0); - m_keepAliveTimer.stop(); - disconnect(&m_keepAliveTimer, 0, this, 0); - if (m_state != SocketConnected) { - try { - m_channelManager->closeAllChannels(SshChannelManager::CloseAllAndReset); - m_sendFacility.sendDisconnectPacket(sshError, serverErrorString); - } catch (...) {} // Nothing sensible to be done here. } - if (m_error != SshNoError) - emit error(userError); - if (m_state == ConnectionEstablished) - emit disconnected(); - if (canUseSocket()) - m_socket->disconnectFromHost(); - m_state = SocketUnconnected; + QStringList args = QStringList{"-M", "-N", "-o", "ControlPersist=no"} << d->connectionArgs(); + if (!d->connParams.x11DisplayName.isEmpty()) + args.prepend("-X"); + qCDebug(sshLog) << "establishing connection:" << sshBinary.toUserOutput() << args; + d->masterProcess.start(sshBinary.toString(), args); } -bool SshConnectionPrivate::canUseSocket() const +void SshConnection::emitError(const QString &reason) { - return m_socket->isValid() - && m_socket->state() == QAbstractSocket::ConnectedState; -} - -void SshConnectionPrivate::createPrivateKey() -{ - if (m_connParams.privateKeyFile.isEmpty()) - throw SshClientException(SshKeyFileError, tr("No private key file given.")); - QFile keyFile(m_connParams.privateKeyFile); - if (!keyFile.open(QIODevice::ReadOnly)) { - throw SshClientException(SshKeyFileError, - tr("Private key file error: %1").arg(keyFile.errorString())); - } - m_sendFacility.createAuthenticationKey(keyFile.readAll()); + const State oldState = d->state; + d->state = Unconnected; + d->errorString = reason; + emit errorOccurred(); + if (oldState == Connected) + emitDisconnected(); } -QSharedPointer<SshRemoteProcess> SshConnectionPrivate::createRemoteProcess(const QByteArray &command) +void SshConnection::emitConnected() { - return m_channelManager->createRemoteProcess(command); -} - -QSharedPointer<SshRemoteProcess> SshConnectionPrivate::createRemoteShell() -{ - return m_channelManager->createRemoteShell(); -} - -QSharedPointer<SftpChannel> SshConnectionPrivate::createSftpChannel() -{ - return m_channelManager->createSftpChannel(); + d->state = Connected; + emit connected(); } -SshDirectTcpIpTunnel::Ptr SshConnectionPrivate::createDirectTunnel(const QString &originatingHost, - quint16 originatingPort, const QString &remoteHost, quint16 remotePort) +void SshConnection::emitDisconnected() { - return m_channelManager->createDirectTunnel(originatingHost, originatingPort, remoteHost, - remotePort); + d->state = Unconnected; + emit disconnected(); } -SshTcpIpForwardServer::Ptr SshConnectionPrivate::createForwardServer(const QString &bindAddress, - quint16 bindPort) +SftpTransferPtr SshConnection::setupTransfer( + const FilesToTransfer &files, Internal::FileTransferType type, + FileTransferErrorHandling errorHandlingMode) { - return m_channelManager->createForwardServer(bindAddress, bindPort); + QTC_ASSERT(state() == Connected, return SftpTransferPtr()); + return SftpTransferPtr(new SftpTransfer(files, type, errorHandlingMode, d->connectionArgs())); } -const quint64 SshConnectionPrivate::InvalidSeqNr = static_cast<quint64>(-1); - -} // namespace Internal } // namespace QSsh diff --git a/src/libs/ssh/sshconnection.h b/src/libs/ssh/sshconnection.h index ca1e3a461a6..09151795d03 100644 --- a/src/libs/ssh/sshconnection.h +++ b/src/libs/ssh/sshconnection.h @@ -25,54 +25,34 @@ #pragma once -#include "ssherrors.h" -#include "sshhostkeydatabase.h" - +#include "sftpdefs.h" #include "ssh_global.h" #include <QByteArray> #include <QFlags> #include <QMetaType> #include <QObject> -#include <QSharedPointer> #include <QString> #include <QHostAddress> #include <QUrl> +#include <memory> + namespace QSsh { -class SftpChannel; -class SshDirectTcpIpTunnel; class SshRemoteProcess; -class SshTcpIpForwardServer; - -namespace Internal { class SshConnectionPrivate; } - -enum SshConnectionOption { - SshIgnoreDefaultProxy = 0x1, - SshEnableStrictConformanceChecks = 0x2 -}; - -Q_DECLARE_FLAGS(SshConnectionOptions, SshConnectionOption) enum SshHostKeyCheckingMode { SshHostKeyCheckingNone, SshHostKeyCheckingStrict, SshHostKeyCheckingAllowNoMatch, - SshHostKeyCheckingAllowMismatch }; class QSSH_EXPORT SshConnectionParameters { public: enum AuthenticationType { - AuthenticationTypePassword, - AuthenticationTypePublicKey, - AuthenticationTypeKeyboardInteractive, - - // Some servers disable "password", others disable "keyboard-interactive". - AuthenticationTypeTryAllPasswordBasedMethods, - - AuthenticationTypeAgent, + AuthenticationTypeAll, + AuthenticationTypeSpecificKey, }; SshConnectionParameters(); @@ -80,19 +60,16 @@ public: QString host() const { return url.host(); } quint16 port() const { return url.port(); } QString userName() const { return url.userName(); } - QString password() const { return url.password(); } void setHost(const QString &host) { url.setHost(host); } void setPort(int port) { url.setPort(port); } void setUserName(const QString &name) { url.setUserName(name); } - void setPassword(const QString &password) { url.setPassword(password); } QUrl url; QString privateKeyFile; - int timeout; // In seconds. - AuthenticationType authenticationType; - SshConnectionOptions options; - SshHostKeyCheckingMode hostKeyCheckingMode; - SshHostKeyDatabasePtr hostKeyDatabase; + QString x11DisplayName; + int timeout = 0; // In seconds. + AuthenticationType authenticationType = AuthenticationTypeAll; + SshHostKeyCheckingMode hostKeyCheckingMode = SshHostKeyCheckingAllowNoMatch; }; QSSH_EXPORT bool operator==(const SshConnectionParameters &p1, const SshConnectionParameters &p2); @@ -101,57 +78,62 @@ QSSH_EXPORT bool operator!=(const SshConnectionParameters &p1, const SshConnecti class QSSH_EXPORT SshConnectionInfo { public: - SshConnectionInfo() : localPort(0), peerPort(0) {} + SshConnectionInfo() = default; SshConnectionInfo(const QHostAddress &la, quint16 lp, const QHostAddress &pa, quint16 pp) : localAddress(la), localPort(lp), peerAddress(pa), peerPort(pp) {} + bool isValid() const { return peerPort != 0; } + QHostAddress localAddress; - quint16 localPort; + quint16 localPort = 0; QHostAddress peerAddress; - quint16 peerPort; + quint16 peerPort = 0; }; +using SshRemoteProcessPtr = std::unique_ptr<SshRemoteProcess>; + class QSSH_EXPORT SshConnection : public QObject { Q_OBJECT public: - enum State { Unconnected, Connecting, Connected }; + enum State { Unconnected, Connecting, Connected, Disconnecting }; explicit SshConnection(const SshConnectionParameters &serverInfo, QObject *parent = 0); void connectToHost(); void disconnectFromHost(); State state() const; - SshError errorState() const; QString errorString() const; SshConnectionParameters connectionParameters() const; SshConnectionInfo connectionInfo() const; + bool sharingEnabled() const; ~SshConnection(); - QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command); - QSharedPointer<SshRemoteProcess> createRemoteShell(); - QSharedPointer<SftpChannel> createSftpChannel(); - QSharedPointer<SshDirectTcpIpTunnel> createDirectTunnel(const QString &originatingHost, - quint16 originatingPort, const QString &remoteHost, quint16 remotePort); - QSharedPointer<SshTcpIpForwardServer> createForwardServer(const QString &remoteHost, - quint16 remotePort); - - // -1 if an error occurred, number of channels closed otherwise. - int closeAllChannels(); - - int channelCount() const; - - QString x11DisplayName() const; + SshRemoteProcessPtr createRemoteProcess(const QByteArray &command); + SshRemoteProcessPtr createRemoteShell(); + SftpTransferPtr createUpload(const FilesToTransfer &files, + FileTransferErrorHandling errorHandlingMode); + SftpTransferPtr createDownload(const FilesToTransfer &files, + FileTransferErrorHandling errorHandlingMode); + SftpSessionPtr createSftpSession(); signals: void connected(); void disconnected(); void dataAvailable(const QString &message); - void error(QSsh::SshError); + void errorOccurred(); private: - Internal::SshConnectionPrivate *d; + void doConnectToHost(); + void emitError(const QString &reason); + void emitConnected(); + void emitDisconnected(); + SftpTransferPtr setupTransfer(const FilesToTransfer &files, Internal::FileTransferType type, + FileTransferErrorHandling errorHandlingMode); + + struct SshConnectionPrivate; + SshConnectionPrivate * const d; }; } // namespace QSsh diff --git a/src/libs/ssh/sshconnection_p.h b/src/libs/ssh/sshconnection_p.h deleted file mode 100644 index 9a7afe35528..00000000000 --- a/src/libs/ssh/sshconnection_p.h +++ /dev/null @@ -1,194 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sshconnection.h" -#include "sshexception_p.h" -#include "sshincomingpacket_p.h" -#include "sshsendfacility_p.h" - -#include <QHash> -#include <QList> -#include <QQueue> -#include <QObject> -#include <QPair> -#include <QScopedPointer> -#include <QTimer> - -QT_BEGIN_NAMESPACE -class QTcpSocket; -QT_END_NAMESPACE - -namespace QSsh { -class SftpChannel; -class SshRemoteProcess; -class SshDirectTcpIpTunnel; -class SshTcpIpForwardServer; - -namespace Internal { -class SshChannelManager; - -// NOTE: When you add stuff here, don't forget to update m_packetHandlers. -enum SshStateInternal { - SocketUnconnected, // initial and after disconnect - SocketConnecting, // After connectToHost() - SocketConnected, // After socket's connected() signal - UserAuthServiceRequested, - WaitingForAgentKeys, - UserAuthRequested, - ConnectionEstablished // After service has been started - // ... -}; - -enum SshKeyExchangeState { - NoKeyExchange, - KexInitSent, - DhInitSent, - NewKeysSent, - KeyExchangeSuccess // After server's DH_REPLY message -}; - -class SshConnectionPrivate : public QObject -{ - Q_OBJECT - friend class QSsh::SshConnection; -public: - SshConnectionPrivate(SshConnection *conn, - const SshConnectionParameters &serverInfo); - ~SshConnectionPrivate(); - - void connectToHost(); - void closeConnection(SshErrorCode sshError, SshError userError, - const QByteArray &serverErrorString, const QString &userErrorString); - QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command); - QSharedPointer<SshRemoteProcess> createRemoteShell(); - QSharedPointer<SftpChannel> createSftpChannel(); - QSharedPointer<SshDirectTcpIpTunnel> createDirectTunnel(const QString &originatingHost, - quint16 originatingPort, const QString &remoteHost, quint16 remotePort); - QSharedPointer<SshTcpIpForwardServer> createForwardServer(const QString &remoteHost, - quint16 remotePort); - - SshStateInternal state() const { return m_state; } - SshError errorState() const { return m_error; } - QString errorString() const { return m_errorString; } - -signals: - void connected(); - void disconnected(); - void dataAvailable(const QString &message); - void error(QSsh::SshError); - -private: - void handleSocketConnected(); - void handleIncomingData(); - void handleSocketError(); - void handleSocketDisconnected(); - void handleTimeout(); - void sendKeepAlivePacket(); - - void handleAgentKeysUpdated(); - void handleSignatureFromAgent(const QByteArray &key, const QByteArray &signature, uint token); - void tryAllAgentKeys(); - void authenticateWithPublicKey(); - void setAgentError(); - - void handleServerId(); - void handlePackets(); - void handleCurrentPacket(); - void handleKeyExchangeInitPacket(); - void handleKeyExchangeReplyPacket(); - void handleNewKeysPacket(); - void handleServiceAcceptPacket(); - void handlePasswordExpiredPacket(); - void handleUserAuthInfoRequestPacket(); - void handleUserAuthSuccessPacket(); - void handleUserAuthFailurePacket(); - void handleUserAuthKeyOkPacket(); - void handleUserAuthBannerPacket(); - void handleUnexpectedPacket(); - void handleGlobalRequest(); - void handleDebugPacket(); - void handleUnimplementedPacket(); - void handleChannelRequest(); - void handleChannelOpen(); - void handleChannelOpenFailure(); - void handleChannelOpenConfirmation(); - void handleChannelSuccess(); - void handleChannelFailure(); - void handleChannelWindowAdjust(); - void handleChannelData(); - void handleChannelExtendedData(); - void handleChannelEof(); - void handleChannelClose(); - void handleDisconnect(); - void handleRequestSuccess(); - void handleRequestFailure(); - - bool canUseSocket() const; - void createPrivateKey(); - - void sendData(const QByteArray &data); - - uint tokenForAgent() const; - - typedef void (SshConnectionPrivate::*PacketHandler)(); - typedef QList<SshStateInternal> StateList; - void setupPacketHandlers(); - void setupPacketHandler(SshPacketType type, const StateList &states, - PacketHandler handler); - - typedef QPair<StateList, PacketHandler> HandlerInStates; - QHash<SshPacketType, HandlerInStates> m_packetHandlers; - - static const quint64 InvalidSeqNr; - - QTcpSocket *m_socket; - SshStateInternal m_state; - SshKeyExchangeState m_keyExchangeState; - SshIncomingPacket m_incomingPacket; - SshSendFacility m_sendFacility; - SshChannelManager * const m_channelManager; - const SshConnectionParameters m_connParams; - QByteArray m_incomingData; - SshError m_error; - QString m_errorString; - QScopedPointer<SshKeyExchange> m_keyExchange; - QTimer m_timeoutTimer; - QTimer m_keepAliveTimer; - bool m_ignoreNextPacket; - SshConnection *m_conn; - quint64 m_lastInvalidMsgSeqNr; - QByteArray m_serverId; - QByteArray m_agentSignature; - QQueue<QByteArray> m_pendingKeyChecks; - QByteArray m_agentKeyToUse; - bool m_serverHasSentDataBeforeId; - bool m_triedAllPasswordBasedMethods; - bool m_agentKeysUpToDate; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshconnectionmanager.cpp b/src/libs/ssh/sshconnectionmanager.cpp index c6a35bd4b8a..c3bb1821eee 100644 --- a/src/libs/ssh/sshconnectionmanager.cpp +++ b/src/libs/ssh/sshconnectionmanager.cpp @@ -26,6 +26,9 @@ #include "sshconnectionmanager.h" #include "sshconnection.h" +#include "sshsettings.h" + +#include <utils/qtcassert.h> #include <QCoreApplication> #include <QList> @@ -61,7 +64,7 @@ public: moveToThread(QCoreApplication::instance()->thread()); connect(&m_removalTimer, &QTimer::timeout, this, &SshConnectionManager::removeInactiveConnections); - m_removalTimer.start(150000); // For a total timeout of five minutes. + m_removalTimer.start(SshSettings::connectionSharingTimeout() * 1000 * 60 / 2); } ~SshConnectionManager() @@ -71,8 +74,8 @@ public: delete connection.connection; } - QSSH_ASSERT(m_acquiredConnections.isEmpty()); - QSSH_ASSERT(m_deprecatedConnections.isEmpty()); + QTC_CHECK(m_acquiredConnections.isEmpty()); + QTC_CHECK(m_deprecatedConnections.isEmpty()); } SshConnection *acquireConnection(const SshConnectionParameters &sshParams) @@ -87,6 +90,9 @@ public: if (connection->thread() != QThread::currentThread()) continue; + if (connection->sharingEnabled() != SshSettings::connectionSharingEnabled()) + continue; + if (m_deprecatedConnections.contains(connection)) // we were asked to no longer use this one... continue; @@ -102,8 +108,6 @@ public: continue; if (connection->thread() != QThread::currentThread()) { - if (connection->channelCount() != 0) - continue; QMetaObject::invokeMethod(this, "switchToCallerThread", Qt::BlockingQueuedConnection, Q_ARG(SshConnection *, connection), @@ -129,7 +133,7 @@ public: QMutexLocker locker(&m_listMutex); const bool wasAquired = m_acquiredConnections.removeOne(connection); - QSSH_ASSERT_AND_RETURN(wasAquired); + QTC_ASSERT(wasAquired, return); if (m_acquiredConnections.contains(connection)) return; @@ -139,7 +143,7 @@ public: || connection->state() != SshConnection::Connected) { doDelete = true; } else { - QSSH_ASSERT_AND_RETURN(!m_unacquiredConnections.contains(UnaquiredConnection(connection))); + QTC_ASSERT(!m_unacquiredConnections.contains(UnaquiredConnection(connection)), return); // It can happen that two or more connections with the same parameters were acquired // if the clients were running in different threads. Only keep one of them in @@ -151,12 +155,10 @@ public: break; } } - if (!haveConnection) { - connection->closeAllChannels(); // Clean up after neglectful clients. + if (!haveConnection) m_unacquiredConnections.append(UnaquiredConnection(connection)); - } else { + else doDelete = true; - } } if (doDelete) { diff --git a/src/libs/ssh/sshcryptofacility.cpp b/src/libs/ssh/sshcryptofacility.cpp deleted file mode 100644 index 28c72da1861..00000000000 --- a/src/libs/ssh/sshcryptofacility.cpp +++ /dev/null @@ -1,453 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshcryptofacility_p.h" - -#include "opensshkeyfilereader_p.h" -#include "sshbotanconversions_p.h" -#include "sshcapabilities_p.h" -#include "sshexception_p.h" -#include "sshkeyexchange_p.h" -#include "sshkeypasswordretriever_p.h" -#include "sshlogging_p.h" -#include "sshpacket_p.h" - -#include <botan/ber_dec.h> -#include <botan/ctr.h> -#include <botan/dsa.h> -#include <botan/ec_group.h> -#include <botan/ecdsa.h> -#include <botan/filters.h> -#include <botan/pkcs8.h> -#include <botan/point_gfp.h> -#include <botan/pubkey.h> -#include <botan/rsa.h> - -#include <QDebug> -#include <QList> - -#include <string> - -using namespace Botan; - -namespace QSsh { -namespace Internal { - -SshAbstractCryptoFacility::SshAbstractCryptoFacility() - : m_cipherBlockSize(0), m_macLength(0) -{ -} - -SshAbstractCryptoFacility::~SshAbstractCryptoFacility() {} - -void SshAbstractCryptoFacility::clearKeys() -{ - m_cipherBlockSize = 0; - m_macLength = 0; - m_sessionId.clear(); - m_pipe.reset(0); - m_hMac.reset(0); -} - -SshAbstractCryptoFacility::Mode SshAbstractCryptoFacility::getMode(const QByteArray &algoName) -{ - if (algoName.endsWith("-ctr")) - return CtrMode; - if (algoName.endsWith("-cbc")) - return CbcMode; - throw SshClientException(SshInternalError, SSH_TR("Unexpected cipher \"%1\"") - .arg(QString::fromLatin1(algoName))); -} - -void SshAbstractCryptoFacility::recreateKeys(const SshKeyExchange &kex) -{ - checkInvariant(); - - if (m_sessionId.isEmpty()) - m_sessionId = kex.h(); - const QByteArray &rfcCryptAlgoName = cryptAlgoName(kex); - std::unique_ptr<BlockCipher> cipher - = BlockCipher::create_or_throw(botanCryptAlgoName(rfcCryptAlgoName)); - m_cipherBlockSize = static_cast<quint32>(cipher->block_size()); - const QByteArray ivData = generateHash(kex, ivChar(), m_cipherBlockSize); - const InitializationVector iv(convertByteArray(ivData), m_cipherBlockSize); - - const quint32 keySize = static_cast<quint32>(cipher->key_spec().maximum_keylength()); - const QByteArray cryptKeyData = generateHash(kex, keyChar(), keySize); - SymmetricKey cryptKey(convertByteArray(cryptKeyData), keySize); - Keyed_Filter * const cipherMode - = makeCipherMode(cipher.release(), getMode(rfcCryptAlgoName), iv, cryptKey); - m_pipe.reset(new Pipe(cipherMode)); - - m_macLength = botanHMacKeyLen(hMacAlgoName(kex)); - const QByteArray hMacKeyData = generateHash(kex, macChar(), macLength()); - SymmetricKey hMacKey(convertByteArray(hMacKeyData), macLength()); - std::unique_ptr<HashFunction> hashFunc - = HashFunction::create_or_throw(botanHMacAlgoName(hMacAlgoName(kex))); - m_hMac.reset(new HMAC(hashFunc.release())); - m_hMac->set_key(hMacKey); -} - -void SshAbstractCryptoFacility::convert(QByteArray &data, quint32 offset, - quint32 dataSize) const -{ - Q_ASSERT(offset + dataSize <= static_cast<quint32>(data.size())); - checkInvariant(); - - // Session id empty => No key exchange has happened yet. - if (dataSize == 0 || m_sessionId.isEmpty()) - return; - - if (dataSize % cipherBlockSize() != 0) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid packet size"); - } - m_pipe->process_msg(reinterpret_cast<const byte *>(data.constData()) + offset, - dataSize); - // Can't use Pipe::LAST_MESSAGE because of a VC bug. - quint32 bytesRead = static_cast<quint32>(m_pipe->read( - reinterpret_cast<byte *>(data.data()) + offset, dataSize, m_pipe->message_count() - 1)); - if (bytesRead != dataSize) { - throw SshClientException(SshInternalError, - QLatin1String("Internal error: Botan::Pipe::read() returned unexpected value")); - } -} - -Keyed_Filter *SshAbstractCryptoFacility::makeCtrCipherMode(BlockCipher *cipher, - const InitializationVector &iv, const SymmetricKey &key) -{ - StreamCipher_Filter * const filter = new StreamCipher_Filter(new CTR_BE(cipher)); - filter->set_key(key); - filter->set_iv(iv); - return filter; -} - -QByteArray SshAbstractCryptoFacility::generateMac(const QByteArray &data, - quint32 dataSize) const -{ - return m_sessionId.isEmpty() - ? QByteArray() - : convertByteArray(m_hMac->process(reinterpret_cast<const byte *>(data.constData()), - dataSize)); -} - -QByteArray SshAbstractCryptoFacility::generateHash(const SshKeyExchange &kex, - char c, quint32 length) -{ - const QByteArray &k = kex.k(); - const QByteArray &h = kex.h(); - QByteArray data(k); - data.append(h).append(c).append(m_sessionId); - SecureVector<byte> key - = kex.hash()->process(convertByteArray(data), data.size()); - while (key.size() < length) { - SecureVector<byte> tmpKey; - tmpKey.insert(tmpKey.end(), k.cbegin(), k.cend()); - tmpKey.insert(tmpKey.end(), h.cbegin(), h.cend()); - tmpKey += key; - key += kex.hash()->process(tmpKey); - } - return QByteArray(reinterpret_cast<const char *>(&key.front()), length); -} - -void SshAbstractCryptoFacility::checkInvariant() const -{ - Q_ASSERT(m_sessionId.isEmpty() == !m_pipe); -} - - -const QByteArray SshEncryptionFacility::PrivKeyFileStartLineRsa("-----BEGIN RSA PRIVATE KEY-----"); -const QByteArray SshEncryptionFacility::PrivKeyFileStartLineDsa("-----BEGIN DSA PRIVATE KEY-----"); -const QByteArray SshEncryptionFacility::PrivKeyFileEndLineRsa("-----END RSA PRIVATE KEY-----"); -const QByteArray SshEncryptionFacility::PrivKeyFileEndLineDsa("-----END DSA PRIVATE KEY-----"); -const QByteArray SshEncryptionFacility::PrivKeyFileStartLineEcdsa("-----BEGIN EC PRIVATE KEY-----"); -const QByteArray SshEncryptionFacility::PrivKeyFileEndLineEcdsa("-----END EC PRIVATE KEY-----"); - -QByteArray SshEncryptionFacility::cryptAlgoName(const SshKeyExchange &kex) const -{ - return kex.encryptionAlgo(); -} - -QByteArray SshEncryptionFacility::hMacAlgoName(const SshKeyExchange &kex) const -{ - return kex.hMacAlgoClientToServer(); -} - -Keyed_Filter *SshEncryptionFacility::makeCipherMode(BlockCipher *cipher, Mode mode, - const InitializationVector &iv, const SymmetricKey &key) -{ - switch (mode) { - case CbcMode: - return get_cipher(cipher->name() + "/CBC/NoPadding", key, iv, ENCRYPTION); - case CtrMode: - return makeCtrCipherMode(cipher, iv, key); - } - return 0; // For dumb compilers. -} - -void SshEncryptionFacility::encrypt(QByteArray &data) const -{ - convert(data, 0, data.size()); -} - -void SshEncryptionFacility::createAuthenticationKey(const QByteArray &privKeyFileContents) -{ - if (privKeyFileContents == m_cachedPrivKeyContents) - return; - - m_authKeyAlgoName.clear(); - qCDebug(sshLog, "%s: Key not cached, reading", Q_FUNC_INFO); - QList<BigInt> pubKeyParams; - QList<BigInt> allKeyParams; - QString error1; - QString error2; - OpenSshKeyFileReader openSshReader(m_rng); - if (openSshReader.parseKey(privKeyFileContents)) { - m_authKeyAlgoName = openSshReader.keyType(); - m_authKey.reset(openSshReader.privateKey().release()); - pubKeyParams = openSshReader.publicParameters(); - allKeyParams = openSshReader.allParameters(); - } else if (!createAuthenticationKeyFromPKCS8(privKeyFileContents, pubKeyParams, allKeyParams, - error1) - && !createAuthenticationKeyFromOpenSSL(privKeyFileContents, pubKeyParams, allKeyParams, - error2)) { - qCDebug(sshLog, "%s: %s\n\t%s\n", Q_FUNC_INFO, qPrintable(error1), qPrintable(error2)); - throw SshClientException(SshKeyFileError, SSH_TR("Decoding of private key file failed: " - "Format not understood.")); - } - - foreach (const BigInt &b, allKeyParams) { - if (b.is_zero()) { - throw SshClientException(SshKeyFileError, - SSH_TR("Decoding of private key file failed: Invalid zero parameter.")); - } - } - - m_authPubKeyBlob = AbstractSshPacket::encodeString(m_authKeyAlgoName); - auto * const ecdsaKey = dynamic_cast<ECDSA_PrivateKey *>(m_authKey.data()); - if (ecdsaKey) { - m_authPubKeyBlob += AbstractSshPacket::encodeString(m_authKeyAlgoName.mid(11)); // Without "ecdsa-sha2-" prefix. - m_authPubKeyBlob += AbstractSshPacket::encodeString( - convertByteArray(ecdsaKey->public_point().encode(PointGFp::UNCOMPRESSED))); - } else { - foreach (const BigInt &b, pubKeyParams) - m_authPubKeyBlob += AbstractSshPacket::encodeMpInt(b); - } - m_cachedPrivKeyContents = privKeyFileContents; -} - -bool SshEncryptionFacility::createAuthenticationKeyFromPKCS8(const QByteArray &privKeyFileContents, - QList<BigInt> &pubKeyParams, QList<BigInt> &allKeyParams, QString &error) -{ - try { - Pipe pipe; - pipe.process_msg(convertByteArray(privKeyFileContents), privKeyFileContents.size()); - m_authKey.reset(PKCS8::load_key(pipe, m_rng, &get_passphrase)); - if (auto * const dsaKey = dynamic_cast<DSA_PrivateKey *>(m_authKey.data())) { - m_authKeyAlgoName = SshCapabilities::PubKeyDss; - pubKeyParams << dsaKey->group_p() << dsaKey->group_q() - << dsaKey->group_g() << dsaKey->get_y(); - allKeyParams << pubKeyParams << dsaKey->get_x(); - } else if (auto * const rsaKey = dynamic_cast<RSA_PrivateKey *>(m_authKey.data())) { - m_authKeyAlgoName = SshCapabilities::PubKeyRsa; - pubKeyParams << rsaKey->get_e() << rsaKey->get_n(); - allKeyParams << pubKeyParams << rsaKey->get_p() << rsaKey->get_q() - << rsaKey->get_d(); - } else if (auto * const ecdsaKey = dynamic_cast<ECDSA_PrivateKey *>(m_authKey.data())) { - const BigInt value = ecdsaKey->private_value(); - m_authKeyAlgoName = SshCapabilities::ecdsaPubKeyAlgoForKeyWidth( - static_cast<int>(value.bytes())); - pubKeyParams << ecdsaKey->public_point().get_affine_x() - << ecdsaKey->public_point().get_affine_y(); - allKeyParams << pubKeyParams << value; - } else { - qCWarning(sshLog, "%s: Unexpected code flow, expected success or exception.", - Q_FUNC_INFO); - return false; - } - } catch (const std::exception &ex) { - error = QLatin1String(ex.what()); - return false; - } - - return true; -} - -bool SshEncryptionFacility::createAuthenticationKeyFromOpenSSL(const QByteArray &privKeyFileContents, - QList<BigInt> &pubKeyParams, QList<BigInt> &allKeyParams, QString &error) -{ - try { - bool syntaxOk = true; - QList<QByteArray> lines = privKeyFileContents.split('\n'); - while (lines.last().isEmpty()) - lines.removeLast(); - if (lines.count() < 3) { - syntaxOk = false; - } else if (lines.first() == PrivKeyFileStartLineRsa) { - if (lines.last() != PrivKeyFileEndLineRsa) - syntaxOk = false; - else - m_authKeyAlgoName = SshCapabilities::PubKeyRsa; - } else if (lines.first() == PrivKeyFileStartLineDsa) { - if (lines.last() != PrivKeyFileEndLineDsa) - syntaxOk = false; - else - m_authKeyAlgoName = SshCapabilities::PubKeyDss; - } else if (lines.first() == PrivKeyFileStartLineEcdsa) { - if (lines.last() != PrivKeyFileEndLineEcdsa) - syntaxOk = false; - // m_authKeyAlgoName set below, as we don't know the size yet. - } else { - syntaxOk = false; - } - if (!syntaxOk) { - error = SSH_TR("Unexpected format."); - return false; - } - - QByteArray privateKeyBlob; - for (int i = 1; i < lines.size() - 1; ++i) - privateKeyBlob += lines.at(i); - privateKeyBlob = QByteArray::fromBase64(privateKeyBlob); - - BER_Decoder decoder(convertByteArray(privateKeyBlob), privateKeyBlob.size()); - BER_Decoder sequence = decoder.start_cons(SEQUENCE); - size_t version; - sequence.decode (version); - const size_t expectedVersion = m_authKeyAlgoName.isEmpty() ? 1 : 0; - if (version != expectedVersion) { - error = SSH_TR("Key encoding has version %1, expected %2.") - .arg(version).arg(expectedVersion); - return false; - } - - if (m_authKeyAlgoName == SshCapabilities::PubKeyDss) { - BigInt p, q, g, y, x; - sequence.decode (p).decode (q).decode (g).decode (y).decode (x); - DSA_PrivateKey * const dsaKey = new DSA_PrivateKey(m_rng, DL_Group(p, q, g), x); - m_authKey.reset(dsaKey); - pubKeyParams << p << q << g << y; - allKeyParams << pubKeyParams << x; - } else if (m_authKeyAlgoName == SshCapabilities::PubKeyRsa) { - BigInt p, q, e, d, n; - sequence.decode(n).decode(e).decode(d).decode(p).decode(q); - m_authKey.reset(new RSA_PrivateKey(p, q, e, d, n)); - pubKeyParams << e << n; - allKeyParams << pubKeyParams << p << q << d; - } else { - BigInt privKey; - sequence.decode_octet_string_bigint(privKey); - m_authKeyAlgoName = SshCapabilities::ecdsaPubKeyAlgoForKeyWidth( - static_cast<int>(privKey.bytes())); - const EC_Group group(SshCapabilities::oid(m_authKeyAlgoName)); - auto * const key = new ECDSA_PrivateKey(m_rng, group, privKey); - m_authKey.reset(key); - pubKeyParams << key->public_point().get_affine_x() - << key->public_point().get_affine_y(); - allKeyParams << pubKeyParams << privKey; - } - - sequence.discard_remaining(); - sequence.verify_end(); - } catch (const std::exception &ex) { - error = QLatin1String(ex.what()); - return false; - } - return true; -} - -QByteArray SshEncryptionFacility::authenticationAlgorithmName() const -{ - Q_ASSERT(m_authKey); - return m_authKeyAlgoName; -} - -QByteArray SshEncryptionFacility::authenticationKeySignature(const QByteArray &data) const -{ - Q_ASSERT(m_authKey); - - QScopedPointer<PK_Signer> signer(new PK_Signer(*m_authKey, m_rng, - botanEmsaAlgoName(m_authKeyAlgoName))); - QByteArray dataToSign = AbstractSshPacket::encodeString(sessionId()) + data; - QByteArray signature - = convertByteArray(signer->sign_message(convertByteArray(dataToSign), - dataToSign.size(), m_rng)); - if (m_authKeyAlgoName.startsWith(SshCapabilities::PubKeyEcdsaPrefix)) { - // The Botan output is not quite in the format that SSH defines. - const int halfSize = signature.count() / 2; - const BigInt r = BigInt::decode(convertByteArray(signature), halfSize); - const BigInt s = BigInt::decode(convertByteArray(signature.mid(halfSize)), halfSize); - signature = AbstractSshPacket::encodeMpInt(r) + AbstractSshPacket::encodeMpInt(s); - } - return AbstractSshPacket::encodeString(m_authKeyAlgoName) - + AbstractSshPacket::encodeString(signature); -} - -QByteArray SshEncryptionFacility::getRandomNumbers(int count) const -{ - QByteArray data; - data.resize(count); - m_rng.randomize(convertByteArray(data), count); - return data; -} - -SshEncryptionFacility::~SshEncryptionFacility() {} - - -QByteArray SshDecryptionFacility::cryptAlgoName(const SshKeyExchange &kex) const -{ - return kex.decryptionAlgo(); -} - -QByteArray SshDecryptionFacility::hMacAlgoName(const SshKeyExchange &kex) const -{ - return kex.hMacAlgoServerToClient(); -} - -Keyed_Filter *SshDecryptionFacility::makeCipherMode(BlockCipher *cipher, Mode mode, const InitializationVector &iv, - const SymmetricKey &key) -{ - switch (mode) { - case CbcMode: - return get_cipher(cipher->name() + "/CBC/NoPadding", key, iv, DECRYPTION); - case CtrMode: - return makeCtrCipherMode(cipher, iv, key); - } - return 0; // For dumb compilers. -} - -void SshDecryptionFacility::decrypt(QByteArray &data, quint32 offset, - quint32 dataSize) const -{ - convert(data, offset, dataSize); - qCDebug(sshLog, "Decrypted data:"); - const char * const start = data.constData() + offset; - const char * const end = start + dataSize; - for (const char *c = start; c < end; ++c) - qCDebug(sshLog, ) << "'" << *c << "' (0x" << (static_cast<int>(*c) & 0xff) << ")"; -} - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshcryptofacility_p.h b/src/libs/ssh/sshcryptofacility_p.h deleted file mode 100644 index 86df75764d8..00000000000 --- a/src/libs/ssh/sshcryptofacility_p.h +++ /dev/null @@ -1,144 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include <botan/auto_rng.h> -#include <botan/bigint.h> -#include <botan/block_cipher.h> -#include <botan/hmac.h> -#include <botan/key_filt.h> -#include <botan/pipe.h> -#include <botan/pk_keys.h> - -#include <QByteArray> -#include <QScopedPointer> - -namespace QSsh { -namespace Internal { - -class SshKeyExchange; - -class SshAbstractCryptoFacility -{ -public: - virtual ~SshAbstractCryptoFacility(); - - void clearKeys(); - void recreateKeys(const SshKeyExchange &kex); - QByteArray generateMac(const QByteArray &data, quint32 dataSize) const; - quint32 cipherBlockSize() const { return m_cipherBlockSize; } - quint32 macLength() const { return m_macLength; } - QByteArray sessionId() const { return m_sessionId; } - -protected: - enum Mode { CbcMode, CtrMode }; - - SshAbstractCryptoFacility(); - void convert(QByteArray &data, quint32 offset, quint32 dataSize) const; - Botan::Keyed_Filter *makeCtrCipherMode(Botan::BlockCipher *cipher, - const Botan::InitializationVector &iv, const Botan::SymmetricKey &key); - -private: - SshAbstractCryptoFacility(const SshAbstractCryptoFacility &); - SshAbstractCryptoFacility &operator=(const SshAbstractCryptoFacility &); - - virtual QByteArray cryptAlgoName(const SshKeyExchange &kex) const = 0; - virtual QByteArray hMacAlgoName(const SshKeyExchange &kex) const = 0; - virtual Botan::Keyed_Filter *makeCipherMode(Botan::BlockCipher *cipher, - Mode mode, const Botan::InitializationVector &iv, const Botan::SymmetricKey &key) = 0; - virtual char ivChar() const = 0; - virtual char keyChar() const = 0; - virtual char macChar() const = 0; - - QByteArray generateHash(const SshKeyExchange &kex, char c, quint32 length); - void checkInvariant() const; - static Mode getMode(const QByteArray &algoName); - - QByteArray m_sessionId; - QScopedPointer<Botan::Pipe> m_pipe; - QScopedPointer<Botan::HMAC> m_hMac; - quint32 m_cipherBlockSize; - quint32 m_macLength; -}; - -class SshEncryptionFacility : public SshAbstractCryptoFacility -{ -public: - void encrypt(QByteArray &data) const; - - void createAuthenticationKey(const QByteArray &privKeyFileContents); - QByteArray authenticationAlgorithmName() const; - QByteArray authenticationPublicKey() const { return m_authPubKeyBlob; } - QByteArray authenticationKeySignature(const QByteArray &data) const; - QByteArray getRandomNumbers(int count) const; - - ~SshEncryptionFacility(); - -private: - virtual QByteArray cryptAlgoName(const SshKeyExchange &kex) const; - virtual QByteArray hMacAlgoName(const SshKeyExchange &kex) const; - virtual Botan::Keyed_Filter *makeCipherMode(Botan::BlockCipher *cipher, - Mode mode, const Botan::InitializationVector &iv, const Botan::SymmetricKey &key); - virtual char ivChar() const { return 'A'; } - virtual char keyChar() const { return 'C'; } - virtual char macChar() const { return 'E'; } - - bool createAuthenticationKeyFromPKCS8(const QByteArray &privKeyFileContents, - QList<Botan::BigInt> &pubKeyParams, QList<Botan::BigInt> &allKeyParams, QString &error); - bool createAuthenticationKeyFromOpenSSL(const QByteArray &privKeyFileContents, - QList<Botan::BigInt> &pubKeyParams, QList<Botan::BigInt> &allKeyParams, QString &error); - - static const QByteArray PrivKeyFileStartLineRsa; - static const QByteArray PrivKeyFileStartLineDsa; - static const QByteArray PrivKeyFileEndLineRsa; - static const QByteArray PrivKeyFileEndLineDsa; - static const QByteArray PrivKeyFileStartLineEcdsa; - static const QByteArray PrivKeyFileEndLineEcdsa; - - QByteArray m_authKeyAlgoName; - QByteArray m_authPubKeyBlob; - QByteArray m_cachedPrivKeyContents; - QScopedPointer<Botan::Private_Key> m_authKey; - mutable Botan::AutoSeeded_RNG m_rng; -}; - -class SshDecryptionFacility : public SshAbstractCryptoFacility -{ -public: - void decrypt(QByteArray &data, quint32 offset, quint32 dataSize) const; - -private: - virtual QByteArray cryptAlgoName(const SshKeyExchange &kex) const; - virtual QByteArray hMacAlgoName(const SshKeyExchange &kex) const; - virtual Botan::Keyed_Filter *makeCipherMode(Botan::BlockCipher *cipher, - Mode mode, const Botan::InitializationVector &iv, const Botan::SymmetricKey &key); - virtual char ivChar() const { return 'B'; } - virtual char keyChar() const { return 'D'; } - virtual char macChar() const { return 'F'; } -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshdirecttcpiptunnel.cpp b/src/libs/ssh/sshdirecttcpiptunnel.cpp deleted file mode 100644 index 9700bd99c66..00000000000 --- a/src/libs/ssh/sshdirecttcpiptunnel.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshdirecttcpiptunnel.h" -#include "sshdirecttcpiptunnel_p.h" - -#include "sshincomingpacket_p.h" -#include "sshlogging_p.h" -#include "sshsendfacility_p.h" - -#include <QTimer> - -namespace QSsh { -namespace Internal { - -SshDirectTcpIpTunnelPrivate::SshDirectTcpIpTunnelPrivate(quint32 channelId, - const QString &originatingHost, quint16 originatingPort, const QString &remoteHost, - quint16 remotePort, SshSendFacility &sendFacility) - : SshTcpIpTunnelPrivate(channelId, sendFacility), - m_originatingHost(originatingHost), - m_originatingPort(originatingPort), - m_remoteHost(remoteHost), - m_remotePort(remotePort) -{ -} - -void SshDirectTcpIpTunnelPrivate::handleOpenSuccessInternal() -{ - emit initialized(); -} - -} // namespace Internal - -using namespace Internal; - -SshDirectTcpIpTunnel::SshDirectTcpIpTunnel(quint32 channelId, const QString &originatingHost, - quint16 originatingPort, const QString &remoteHost, quint16 remotePort, - SshSendFacility &sendFacility) - : d(new SshDirectTcpIpTunnelPrivate(channelId, originatingHost, originatingPort, remoteHost, - remotePort, sendFacility)) -{ - d->init(this); - connect(d, &SshDirectTcpIpTunnelPrivate::initialized, - this, &SshDirectTcpIpTunnel::initialized, Qt::QueuedConnection); -} - -SshDirectTcpIpTunnel::~SshDirectTcpIpTunnel() -{ - delete d; -} - -bool SshDirectTcpIpTunnel::atEnd() const -{ - return QIODevice::atEnd() && d->m_data.isEmpty(); -} - -qint64 SshDirectTcpIpTunnel::bytesAvailable() const -{ - return QIODevice::bytesAvailable() + d->m_data.count(); -} - -bool SshDirectTcpIpTunnel::canReadLine() const -{ - return QIODevice::canReadLine() || d->m_data.contains('\n'); -} - -void SshDirectTcpIpTunnel::close() -{ - d->closeChannel(); - QIODevice::close(); -} - -void SshDirectTcpIpTunnel::initialize() -{ - QSSH_ASSERT_AND_RETURN(d->channelState() == AbstractSshChannel::Inactive); - - try { - QIODevice::open(QIODevice::ReadWrite); - d->m_sendFacility.sendDirectTcpIpPacket(d->localChannelId(), d->initialWindowSize(), - d->maxPacketSize(), d->m_remoteHost.toUtf8(), d->m_remotePort, - d->m_originatingHost.toUtf8(), d->m_originatingPort); - d->setChannelState(AbstractSshChannel::SessionRequested); - d->m_timeoutTimer.start(d->ReplyTimeout); - } catch (const std::exception &e) { // Won't happen, but let's play it safe. - qCWarning(sshLog, "Botan error: %s", e.what()); - d->closeChannel(); - } -} - -qint64 SshDirectTcpIpTunnel::readData(char *data, qint64 maxlen) -{ - return d->readData(data, maxlen); -} - -qint64 SshDirectTcpIpTunnel::writeData(const char *data, qint64 len) -{ - return d->writeData(data, len); -} - -} // namespace QSsh diff --git a/src/libs/ssh/sshdirecttcpiptunnel.h b/src/libs/ssh/sshdirecttcpiptunnel.h deleted file mode 100644 index 5d23f1cfdc3..00000000000 --- a/src/libs/ssh/sshdirecttcpiptunnel.h +++ /dev/null @@ -1,79 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "ssh_global.h" - -#include <QIODevice> -#include <QSharedPointer> - -namespace QSsh { - -namespace Internal { -class SshChannelManager; -class SshDirectTcpIpTunnelPrivate; -class SshSendFacility; -class SshTcpIpTunnelPrivate; -} // namespace Internal - -class QSSH_EXPORT SshDirectTcpIpTunnel : public QIODevice -{ - Q_OBJECT - - friend class Internal::SshChannelManager; - friend class Internal::SshTcpIpTunnelPrivate; - -public: - typedef QSharedPointer<SshDirectTcpIpTunnel> Ptr; - - ~SshDirectTcpIpTunnel(); - - // QIODevice stuff - bool atEnd() const; - qint64 bytesAvailable() const; - bool canReadLine() const; - void close(); - bool isSequential() const { return true; } - - void initialize(); - -signals: - void initialized(); - void error(const QString &reason); - -private: - SshDirectTcpIpTunnel(quint32 channelId, const QString &originatingHost, - quint16 originatingPort, const QString &remoteHost, quint16 remotePort, - Internal::SshSendFacility &sendFacility); - - // QIODevice stuff - qint64 readData(char *data, qint64 maxlen); - qint64 writeData(const char *data, qint64 len); - - Internal::SshDirectTcpIpTunnelPrivate * const d; -}; - -} // namespace QSsh diff --git a/src/libs/ssh/sshdirecttcpiptunnel_p.h b/src/libs/ssh/sshdirecttcpiptunnel_p.h deleted file mode 100644 index a2a4ac1cc5e..00000000000 --- a/src/libs/ssh/sshdirecttcpiptunnel_p.h +++ /dev/null @@ -1,59 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sshtcpiptunnel_p.h" - -namespace QSsh { -class SshDirectTcpIpTunnel; - -namespace Internal { - -class SshDirectTcpIpTunnelPrivate : public SshTcpIpTunnelPrivate -{ - Q_OBJECT - - friend class QSsh::SshDirectTcpIpTunnel; - -public: - explicit SshDirectTcpIpTunnelPrivate(quint32 channelId, const QString &originatingHost, - quint16 originatingPort, const QString &remoteHost, quint16 remotePort, - SshSendFacility &sendFacility); - -signals: - void initialized(); - -private: - void handleOpenSuccessInternal(); - - const QString m_originatingHost; - const quint16 m_originatingPort; - const QString m_remoteHost; - const quint16 m_remotePort; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/ssherrors.h b/src/libs/ssh/ssherrors.h deleted file mode 100644 index f8a9e2fd954..00000000000 --- a/src/libs/ssh/ssherrors.h +++ /dev/null @@ -1,41 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once -#define SSHERRORS_P_H - -#include <QMetaType> - -namespace QSsh { - -enum SshError { - SshNoError, SshSocketError, SshTimeoutError, SshProtocolError, - SshHostKeyError, SshKeyFileError, SshAuthenticationError, - SshClosedByServerError, SshAgentError, SshInternalError -}; - -} // namespace QSsh - -Q_DECLARE_METATYPE(QSsh::SshError) diff --git a/src/libs/ssh/sshexception_p.h b/src/libs/ssh/sshexception_p.h deleted file mode 100644 index 94697df19ed..00000000000 --- a/src/libs/ssh/sshexception_p.h +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "ssherrors.h" - -#include <QByteArray> -#include <QCoreApplication> -#include <QString> - -#include <exception> - -namespace QSsh { -namespace Internal { - -enum SshErrorCode { - SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1, - SSH_DISCONNECT_PROTOCOL_ERROR = 2, - SSH_DISCONNECT_KEY_EXCHANGE_FAILED = 3, - SSH_DISCONNECT_RESERVED = 4, - SSH_DISCONNECT_MAC_ERROR = 5, - SSH_DISCONNECT_COMPRESSION_ERROR = 6, - SSH_DISCONNECT_SERVICE_NOT_AVAILABLE = 7, - SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8, - SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE = 9, - SSH_DISCONNECT_CONNECTION_LOST = 10, - SSH_DISCONNECT_BY_APPLICATION = 11, - SSH_DISCONNECT_TOO_MANY_CONNECTIONS = 12, - SSH_DISCONNECT_AUTH_CANCELLED_BY_USER = 13, - SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14, - SSH_DISCONNECT_ILLEGAL_USER_NAME = 15 -}; - -#define SSH_TR(string) QCoreApplication::translate("SshConnection", string) - -#define SSH_SERVER_EXCEPTION(error, errorString) \ - SshServerException((error), (errorString), SSH_TR(errorString)) - -struct SshServerException : public std::exception -{ - SshServerException(SshErrorCode error, const QByteArray &errorStringServer, - const QString &errorStringUser) - : error(error), errorStringServer(errorStringServer), - errorStringUser(errorStringUser) {} - const char *what() const noexcept override { return errorStringServer.constData(); } - - const SshErrorCode error; - const QByteArray errorStringServer; - const QString errorStringUser; -}; - -struct SshClientException : public std::exception -{ - SshClientException(SshError error, const QString &errorString) - : error(error), errorString(errorString), errorStringPrintable(errorString.toLocal8Bit()) {} - const char *what() const noexcept override { return errorStringPrintable.constData(); } - - const SshError error; - const QString errorString; - const QByteArray errorStringPrintable; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshforwardedtcpiptunnel.cpp b/src/libs/ssh/sshforwardedtcpiptunnel.cpp deleted file mode 100644 index 22442f7c80c..00000000000 --- a/src/libs/ssh/sshforwardedtcpiptunnel.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshforwardedtcpiptunnel.h" -#include "sshforwardedtcpiptunnel_p.h" -#include "sshlogging_p.h" -#include "sshsendfacility_p.h" - -namespace QSsh { - -namespace Internal { -SshForwardedTcpIpTunnelPrivate::SshForwardedTcpIpTunnelPrivate(quint32 channelId, - SshSendFacility &sendFacility) : - SshTcpIpTunnelPrivate(channelId, sendFacility) -{ - setChannelState(SessionRequested); -} - -void SshForwardedTcpIpTunnelPrivate::handleOpenSuccessInternal() -{ - QSSH_ASSERT_AND_RETURN(channelState() == AbstractSshChannel::SessionEstablished); - - try { - m_sendFacility.sendChannelOpenConfirmationPacket(remoteChannel(), localChannelId(), - initialWindowSize(), maxPacketSize()); - } catch (const std::exception &e) { // Won't happen, but let's play it safe. - qCWarning(sshLog, "Botan error: %s", e.what()); - closeChannel(); - } -} - -} // namespace Internal - -using namespace Internal; - -SshForwardedTcpIpTunnel::SshForwardedTcpIpTunnel(quint32 channelId, SshSendFacility &sendFacility) : - d(new SshForwardedTcpIpTunnelPrivate(channelId, sendFacility)) -{ - d->init(this); -} - -SshForwardedTcpIpTunnel::~SshForwardedTcpIpTunnel() -{ - delete d; -} - -bool SshForwardedTcpIpTunnel::atEnd() const -{ - return QIODevice::atEnd() && d->m_data.isEmpty(); -} - -qint64 SshForwardedTcpIpTunnel::bytesAvailable() const -{ - return QIODevice::bytesAvailable() + d->m_data.count(); -} - -bool SshForwardedTcpIpTunnel::canReadLine() const -{ - return QIODevice::canReadLine() || d->m_data.contains('\n'); -} - -void SshForwardedTcpIpTunnel::close() -{ - d->closeChannel(); - QIODevice::close(); -} - -qint64 SshForwardedTcpIpTunnel::readData(char *data, qint64 maxlen) -{ - return d->readData(data, maxlen); -} - -qint64 SshForwardedTcpIpTunnel::writeData(const char *data, qint64 len) -{ - return d->writeData(data, len); -} - -} // namespace QSsh diff --git a/src/libs/ssh/sshforwardedtcpiptunnel.h b/src/libs/ssh/sshforwardedtcpiptunnel.h deleted file mode 100644 index cb33c30446a..00000000000 --- a/src/libs/ssh/sshforwardedtcpiptunnel.h +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ -#pragma once - -#include "ssh_global.h" -#include <QIODevice> -#include <QSharedPointer> - -namespace QSsh { - -namespace Internal { -class SshChannelManager; -class SshForwardedTcpIpTunnelPrivate; -class SshSendFacility; -class SshTcpIpTunnelPrivate; -} // namespace Internal - -class QSSH_EXPORT SshForwardedTcpIpTunnel : public QIODevice -{ - Q_OBJECT - friend class Internal::SshChannelManager; - friend class Internal::SshTcpIpTunnelPrivate; - -public: - typedef QSharedPointer<SshForwardedTcpIpTunnel> Ptr; - ~SshForwardedTcpIpTunnel() override; - - // QIODevice stuff - bool atEnd() const override; - qint64 bytesAvailable() const override; - bool canReadLine() const override; - void close() override; - bool isSequential() const override { return true; } - -signals: - void error(const QString &reason); - -private: - SshForwardedTcpIpTunnel(quint32 channelId, Internal::SshSendFacility &sendFacility); - - // QIODevice stuff - qint64 readData(char *data, qint64 maxlen) override; - qint64 writeData(const char *data, qint64 len) override; - - Internal::SshForwardedTcpIpTunnelPrivate * const d; -}; - -} // namespace QSsh diff --git a/src/libs/ssh/sshforwardedtcpiptunnel_p.h b/src/libs/ssh/sshforwardedtcpiptunnel_p.h deleted file mode 100644 index 7b5043dac87..00000000000 --- a/src/libs/ssh/sshforwardedtcpiptunnel_p.h +++ /dev/null @@ -1,44 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sshforwardedtcpiptunnel.h" -#include "sshtcpiptunnel_p.h" - -namespace QSsh { -namespace Internal { - -class SshForwardedTcpIpTunnelPrivate : public SshTcpIpTunnelPrivate -{ - Q_OBJECT - friend class QSsh::SshForwardedTcpIpTunnel; -public: - SshForwardedTcpIpTunnelPrivate(quint32 channelId, SshSendFacility &sendFacility); - void handleOpenSuccessInternal() override; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshhostkeydatabase.cpp b/src/libs/ssh/sshhostkeydatabase.cpp deleted file mode 100644 index ebf1c643771..00000000000 --- a/src/libs/ssh/sshhostkeydatabase.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshhostkeydatabase.h" - -#include "sshlogging_p.h" - -#include <QByteArray> -#include <QCoreApplication> -#include <QDir> -#include <QFile> -#include <QHash> -#include <QString> - -namespace QSsh { - -class SshHostKeyDatabase::SshHostKeyDatabasePrivate -{ -public: - QHash<QString, QByteArray> hostKeys; -}; - -SshHostKeyDatabase::SshHostKeyDatabase() : d(new SshHostKeyDatabasePrivate) -{ -} - -SshHostKeyDatabase::~SshHostKeyDatabase() -{ - delete d; -} - -bool SshHostKeyDatabase::load(const QString &filePath, QString *error) -{ - QFile file(filePath); - if (!file.open(QIODevice::ReadOnly)) { - if (error) { - *error = QCoreApplication::translate("QSsh::Ssh", - "Failed to open key file \"%1\" for reading: %2") - .arg(QDir::toNativeSeparators(filePath), file.errorString()); - } - return false; - } - - d->hostKeys.clear(); - const QByteArray content = file.readAll().trimmed(); - if (content.isEmpty()) - return true; - foreach (const QByteArray &line, content.split('\n')) { - const QList<QByteArray> &lineData = line.trimmed().split(' '); - if (lineData.count() != 2) { - qCDebug(Internal::sshLog, "Unexpected line \"%s\" in file \"%s\".", line.constData(), - qPrintable(filePath)); - continue; - } - d->hostKeys.insert(QString::fromUtf8(lineData.first()), - QByteArray::fromHex(lineData.last())); - } - - return true; -} - -bool SshHostKeyDatabase::store(const QString &filePath, QString *error) const -{ - QFile file(filePath); - if (!file.open(QIODevice::WriteOnly)) { - if (error) { - *error = QCoreApplication::translate("QSsh::Ssh", - "Failed to open key file \"%1\" for writing: %2") - .arg(QDir::toNativeSeparators(filePath), file.errorString()); - } - return false; - } - - file.resize(0); - for (auto it = d->hostKeys.constBegin(); it != d->hostKeys.constEnd(); ++it) - file.write(it.key().toUtf8() + ' ' + it.value().toHex() + '\n'); - return true; -} - -SshHostKeyDatabase::KeyLookupResult SshHostKeyDatabase::matchHostKey(const QString &hostName, - const QByteArray &key) const -{ - auto it = d->hostKeys.constFind(hostName); - if (it == d->hostKeys.constEnd()) - return KeyLookupNoMatch; - if (it.value() == key) - return KeyLookupMatch; - return KeyLookupMismatch; -} - -void SshHostKeyDatabase::insertHostKey(const QString &hostName, const QByteArray &key) -{ - d->hostKeys.insert(hostName, key); -} - -} // namespace QSsh diff --git a/src/libs/ssh/sshincomingpacket.cpp b/src/libs/ssh/sshincomingpacket.cpp deleted file mode 100644 index aa3beedf6b6..00000000000 --- a/src/libs/ssh/sshincomingpacket.cpp +++ /dev/null @@ -1,604 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshincomingpacket_p.h" - -#include "ssh_global.h" -#include "sshbotanconversions_p.h" -#include "sshcapabilities_p.h" -#include "sshlogging_p.h" - -namespace QSsh { -namespace Internal { - -const QByteArray SshIncomingPacket::ExitStatusType("exit-status"); -const QByteArray SshIncomingPacket::ExitSignalType("exit-signal"); -const QByteArray SshIncomingPacket::ForwardedTcpIpType("forwarded-tcpip"); - -SshIncomingPacket::SshIncomingPacket() : m_serverSeqNr(0) { } - -quint32 SshIncomingPacket::cipherBlockSize() const -{ - return qMax(m_decrypter.cipherBlockSize(), 8U); -} - -quint32 SshIncomingPacket::macLength() const -{ - return m_decrypter.macLength(); -} - -void SshIncomingPacket::recreateKeys(const SshKeyExchange &keyExchange) -{ - m_decrypter.recreateKeys(keyExchange); -} - -void SshIncomingPacket::reset() -{ - clear(); - m_serverSeqNr = 0; - m_decrypter.clearKeys(); -} - -void SshIncomingPacket::consumeData(QByteArray &newData) -{ - qCDebug(sshLog, "%s: current data size = %d, new data size = %d", - Q_FUNC_INFO, m_data.size(), newData.size()); - - if (isComplete() || newData.isEmpty()) - return; - - /* - * Until we have reached the minimum packet size, we cannot decrypt the - * length field. - */ - const quint32 minSize = minPacketSize(); - if (currentDataSize() < minSize) { - const int bytesToTake - = qMin<quint32>(minSize - currentDataSize(), newData.size()); - moveFirstBytes(m_data, newData, bytesToTake); - qCDebug(sshLog, "Took %d bytes from new data", bytesToTake); - if (currentDataSize() < minSize) - return; - } - - if (4 + length() + macLength() < currentDataSize()) - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, "Server sent invalid packet."); - - const int bytesToTake - = qMin<quint32>(length() + 4 + macLength() - currentDataSize(), - newData.size()); - moveFirstBytes(m_data, newData, bytesToTake); - qCDebug(sshLog, "Took %d bytes from new data", bytesToTake); - if (isComplete()) { - qCDebug(sshLog, "Message complete. Overall size: %u, payload size: %u", - m_data.size(), m_length - paddingLength() - 1); - decrypt(); - ++m_serverSeqNr; - } -} - -void SshIncomingPacket::decrypt() -{ - Q_ASSERT(isComplete()); - const quint32 netDataLength = length() + 4; - m_decrypter.decrypt(m_data, cipherBlockSize(), - netDataLength - cipherBlockSize()); - const QByteArray &mac = m_data.mid(netDataLength, macLength()); - if (mac != generateMac(m_decrypter, m_serverSeqNr)) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_MAC_ERROR, - "Message authentication failed."); - } -} - -void SshIncomingPacket::moveFirstBytes(QByteArray &target, QByteArray &source, - int n) -{ - target.append(source.left(n)); - source.remove(0, n); -} - -SshKeyExchangeInit SshIncomingPacket::extractKeyExchangeInitData() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_MSG_KEXINIT); - - SshKeyExchangeInit exchangeData; - try { - quint32 offset = TypeOffset + 1; - std::memcpy(exchangeData.cookie, &m_data.constData()[offset], - sizeof exchangeData.cookie); - offset += sizeof exchangeData.cookie; - exchangeData.keyAlgorithms - = SshPacketParser::asNameList(m_data, &offset); - exchangeData.serverHostKeyAlgorithms - = SshPacketParser::asNameList(m_data, &offset); - exchangeData.encryptionAlgorithmsClientToServer - = SshPacketParser::asNameList(m_data, &offset); - exchangeData.encryptionAlgorithmsServerToClient - = SshPacketParser::asNameList(m_data, &offset); - exchangeData.macAlgorithmsClientToServer - = SshPacketParser::asNameList(m_data, &offset); - exchangeData.macAlgorithmsServerToClient - = SshPacketParser::asNameList(m_data, &offset); - exchangeData.compressionAlgorithmsClientToServer - = SshPacketParser::asNameList(m_data, &offset); - exchangeData.compressionAlgorithmsServerToClient - = SshPacketParser::asNameList(m_data, &offset); - exchangeData.languagesClientToServer - = SshPacketParser::asNameList(m_data, &offset); - exchangeData.languagesServerToClient - = SshPacketParser::asNameList(m_data, &offset); - exchangeData.firstKexPacketFollows - = SshPacketParser::asBool(m_data, &offset); - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_KEY_EXCHANGE_FAILED, - "Key exchange failed: Server sent invalid SSH_MSG_KEXINIT packet."); - } - return exchangeData; -} - -static void getHostKeySpecificReplyData(SshKeyExchangeReply &replyData, - const QByteArray &hostKeyAlgo, const QByteArray &input) -{ - quint32 offset = 0; - if (hostKeyAlgo == SshCapabilities::PubKeyDss || hostKeyAlgo == SshCapabilities::PubKeyRsa) { - // DSS: p and q, RSA: e and n - replyData.hostKeyParameters << SshPacketParser::asBigInt(input, &offset); - replyData.hostKeyParameters << SshPacketParser::asBigInt(input, &offset); - - // g and y - if (hostKeyAlgo == SshCapabilities::PubKeyDss) { - replyData.hostKeyParameters << SshPacketParser::asBigInt(input, &offset); - replyData.hostKeyParameters << SshPacketParser::asBigInt(input, &offset); - } - } else { - QSSH_ASSERT_AND_RETURN(hostKeyAlgo.startsWith(SshCapabilities::PubKeyEcdsaPrefix)); - if (SshPacketParser::asString(input, &offset) - != hostKeyAlgo.mid(11)) { // Without "ecdsa-sha2-" prefix. - throw SshPacketParseException(); - } - replyData.q = SshPacketParser::asString(input, &offset); - } -} - -static QByteArray &padToWidth(QByteArray &data, int targetWidth) -{ - return data.prepend(QByteArray(targetWidth - data.count(), 0)); -} - -SshKeyExchangeReply SshIncomingPacket::extractKeyExchangeReply(const QByteArray &kexAlgo, - const QByteArray &hostKeyAlgo) const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_MSG_KEXDH_REPLY); - - try { - SshKeyExchangeReply replyData; - quint32 topLevelOffset = TypeOffset + 1; - replyData.k_s = SshPacketParser::asString(m_data, &topLevelOffset); - quint32 k_sOffset = 0; - if (SshPacketParser::asString(replyData.k_s, &k_sOffset) != hostKeyAlgo) - throw SshPacketParseException(); - getHostKeySpecificReplyData(replyData, hostKeyAlgo, replyData.k_s.mid(k_sOffset)); - - if (kexAlgo == SshCapabilities::DiffieHellmanGroup1Sha1 - || kexAlgo == SshCapabilities::DiffieHellmanGroup14Sha1) { - replyData.f = SshPacketParser::asBigInt(m_data, &topLevelOffset); - } else { - QSSH_ASSERT_AND_RETURN_VALUE(kexAlgo.startsWith(SshCapabilities::EcdhKexNamePrefix), - SshKeyExchangeReply()); - replyData.q_s = SshPacketParser::asString(m_data, &topLevelOffset); - } - const QByteArray fullSignature = SshPacketParser::asString(m_data, &topLevelOffset); - quint32 sigOffset = 0; - if (SshPacketParser::asString(fullSignature, &sigOffset) != hostKeyAlgo) - throw SshPacketParseException(); - replyData.signatureBlob = SshPacketParser::asString(fullSignature, &sigOffset); - if (hostKeyAlgo.startsWith(SshCapabilities::PubKeyEcdsaPrefix)) { - // Botan's PK_Verifier wants the signature in this format. - quint32 blobOffset = 0; - const Botan::BigInt r = SshPacketParser::asBigInt(replyData.signatureBlob, &blobOffset); - const Botan::BigInt s = SshPacketParser::asBigInt(replyData.signatureBlob, &blobOffset); - const int width = SshCapabilities::ecdsaIntegerWidthInBytes(hostKeyAlgo); - QByteArray encodedR = convertByteArray(Botan::BigInt::encode(r)); - replyData.signatureBlob = padToWidth(encodedR, width); - QByteArray encodedS = convertByteArray(Botan::BigInt::encode(s)); - replyData.signatureBlob += padToWidth(encodedS, width); - } - replyData.k_s.prepend(m_data.mid(TypeOffset + 1, 4)); - return replyData; - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_KEY_EXCHANGE_FAILED, - "Key exchange failed: " - "Server sent invalid key exchange reply packet."); - } -} - -SshDisconnect SshIncomingPacket::extractDisconnect() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_MSG_DISCONNECT); - - SshDisconnect msg; - try { - quint32 offset = TypeOffset + 1; - msg.reasonCode = SshPacketParser::asUint32(m_data, &offset); - msg.description = SshPacketParser::asUserString(m_data, &offset); - msg.language = SshPacketParser::asString(m_data, &offset); - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid SSH_MSG_DISCONNECT."); - } - - return msg; -} - -SshUserAuthBanner SshIncomingPacket::extractUserAuthBanner() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_MSG_USERAUTH_BANNER); - - try { - SshUserAuthBanner msg; - quint32 offset = TypeOffset + 1; - msg.message = SshPacketParser::asUserString(m_data, &offset); - msg.language = SshPacketParser::asString(m_data, &offset); - return msg; - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid SSH_MSG_USERAUTH_BANNER."); - } -} - -SshUserAuthInfoRequestPacket SshIncomingPacket::extractUserAuthInfoRequest() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_MSG_USERAUTH_INFO_REQUEST); - - try { - SshUserAuthInfoRequestPacket msg; - quint32 offset = TypeOffset + 1; - msg.name = SshPacketParser::asUserString(m_data, &offset); - msg.instruction = SshPacketParser::asUserString(m_data, &offset); - msg.languageTag = SshPacketParser::asString(m_data, &offset); - const quint32 promptCount = SshPacketParser::asUint32(m_data, &offset); - msg.prompts.reserve(promptCount); - msg.echos.reserve(promptCount); - for (quint32 i = 0; i < promptCount; ++i) { - msg.prompts << SshPacketParser::asUserString(m_data, &offset); - msg.echos << SshPacketParser::asBool(m_data, &offset); - } - return msg; - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid SSH_MSG_USERAUTH_INFO_REQUEST."); - } -} - -SshUserAuthPkOkPacket SshIncomingPacket::extractUserAuthPkOk() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_MSG_USERAUTH_PK_OK); - - try { - SshUserAuthPkOkPacket msg; - quint32 offset = TypeOffset + 1; - msg.algoName= SshPacketParser::asString(m_data, &offset); - msg.keyBlob = SshPacketParser::asString(m_data, &offset); - return msg; - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid SSH_MSG_USERAUTH_PK_OK."); - } -} - -SshDebug SshIncomingPacket::extractDebug() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_MSG_DEBUG); - - try { - SshDebug msg; - quint32 offset = TypeOffset + 1; - msg.display = SshPacketParser::asBool(m_data, &offset); - msg.message = SshPacketParser::asUserString(m_data, &offset); - msg.language = SshPacketParser::asString(m_data, &offset); - return msg; - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid SSH_MSG_DEBUG."); - } -} - -SshRequestSuccess SshIncomingPacket::extractRequestSuccess() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_MSG_REQUEST_SUCCESS); - - try { - SshRequestSuccess msg; - quint32 offset = TypeOffset + 1; - msg.bindPort = SshPacketParser::asUint32(m_data, &offset); - return msg; - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid SSH_MSG_REQUEST_SUCCESS."); - } -} - -SshUnimplemented SshIncomingPacket::extractUnimplemented() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_MSG_UNIMPLEMENTED); - - try { - SshUnimplemented msg; - quint32 offset = TypeOffset + 1; - msg.invalidMsgSeqNr = SshPacketParser::asUint32(m_data, &offset); - return msg; - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid SSH_MSG_UNIMPLEMENTED."); - } -} - -SshChannelOpenGeneric SshIncomingPacket::extractChannelOpen() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_MSG_CHANNEL_OPEN); - - try { - SshChannelOpenGeneric channelOpen; - quint32 offset = TypeOffset + 1; - channelOpen.channelType = SshPacketParser::asString(m_data, &offset); - channelOpen.commonData.remoteChannel = SshPacketParser::asUint32(m_data, &offset); - channelOpen.commonData.remoteWindowSize = SshPacketParser::asUint32(m_data, &offset); - channelOpen.commonData.remoteMaxPacketSize = SshPacketParser::asUint32(m_data, &offset); - channelOpen.typeSpecificData = m_data.mid(offset, length() - paddingLength() - offset - + int(sizeof m_length)); - return channelOpen; - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Server sent invalid SSH_MSG_CHANNEL_OPEN packet."); - } -} - -SshChannelOpenForwardedTcpIp SshIncomingPacket::extractChannelOpenForwardedTcpIp( - const SshChannelOpenGeneric &genericData) -{ - try { - SshChannelOpenForwardedTcpIp specificData; - specificData.common = genericData.commonData; - quint32 offset = 0; - specificData.remoteAddress = SshPacketParser::asString(genericData.typeSpecificData, - &offset); - specificData.remotePort = SshPacketParser::asUint32(genericData.typeSpecificData, &offset); - specificData.originatorAddress = SshPacketParser::asString(genericData.typeSpecificData, - &offset); - specificData.originatorPort = SshPacketParser::asUint32(genericData.typeSpecificData, - &offset); - return specificData; - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Server sent invalid SSH_MSG_CHANNEL_OPEN packet."); - } -} - -SshChannelOpenX11 SshIncomingPacket::extractChannelOpenX11(const SshChannelOpenGeneric &genericData) -{ - try { - SshChannelOpenX11 specificData; - specificData.common = genericData.commonData; - quint32 offset = 0; - specificData.originatorAddress = SshPacketParser::asString(genericData.typeSpecificData, - &offset); - specificData.originatorPort = SshPacketParser::asUint32(genericData.typeSpecificData, - &offset); - return specificData; - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Server sent invalid SSH_MSG_CHANNEL_OPEN packet."); - } -} - -SshChannelOpenFailure SshIncomingPacket::extractChannelOpenFailure() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_MSG_CHANNEL_OPEN_FAILURE); - - SshChannelOpenFailure openFailure; - try { - quint32 offset = TypeOffset + 1; - openFailure.localChannel = SshPacketParser::asUint32(m_data, &offset); - openFailure.reasonCode = SshPacketParser::asUint32(m_data, &offset); - openFailure.reasonString = QString::fromLocal8Bit(SshPacketParser::asString(m_data, &offset)); - openFailure.language = SshPacketParser::asString(m_data, &offset); - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Server sent invalid SSH_MSG_CHANNEL_OPEN_FAILURE packet."); - } - return openFailure; -} - -SshChannelOpenConfirmation SshIncomingPacket::extractChannelOpenConfirmation() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_MSG_CHANNEL_OPEN_CONFIRMATION); - - SshChannelOpenConfirmation confirmation; - try { - quint32 offset = TypeOffset + 1; - confirmation.localChannel = SshPacketParser::asUint32(m_data, &offset); - confirmation.remoteChannel = SshPacketParser::asUint32(m_data, &offset); - confirmation.remoteWindowSize = SshPacketParser::asUint32(m_data, &offset); - confirmation.remoteMaxPacketSize = SshPacketParser::asUint32(m_data, &offset); - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Server sent invalid SSH_MSG_CHANNEL_OPEN_CONFIRMATION packet."); - } - return confirmation; -} - -SshChannelWindowAdjust SshIncomingPacket::extractWindowAdjust() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_MSG_CHANNEL_WINDOW_ADJUST); - - SshChannelWindowAdjust adjust; - try { - quint32 offset = TypeOffset + 1; - adjust.localChannel = SshPacketParser::asUint32(m_data, &offset); - adjust.bytesToAdd = SshPacketParser::asUint32(m_data, &offset); - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid SSH_MSG_CHANNEL_WINDOW_ADJUST packet."); - } - return adjust; -} - -SshChannelData SshIncomingPacket::extractChannelData() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_MSG_CHANNEL_DATA); - - SshChannelData data; - try { - quint32 offset = TypeOffset + 1; - data.localChannel = SshPacketParser::asUint32(m_data, &offset); - data.data = SshPacketParser::asString(m_data, &offset); - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid SSH_MSG_CHANNEL_DATA packet."); - } - return data; -} - -SshChannelExtendedData SshIncomingPacket::extractChannelExtendedData() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_MSG_CHANNEL_EXTENDED_DATA); - - SshChannelExtendedData data; - try { - quint32 offset = TypeOffset + 1; - data.localChannel = SshPacketParser::asUint32(m_data, &offset); - data.type = SshPacketParser::asUint32(m_data, &offset); - data.data = SshPacketParser::asString(m_data, &offset); - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid SSH_MSG_CHANNEL_EXTENDED_DATA packet."); - } - return data; -} - -SshChannelExitStatus SshIncomingPacket::extractChannelExitStatus() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_MSG_CHANNEL_REQUEST); - - SshChannelExitStatus exitStatus; - try { - quint32 offset = TypeOffset + 1; - exitStatus.localChannel = SshPacketParser::asUint32(m_data, &offset); - const QByteArray &type = SshPacketParser::asString(m_data, &offset); - Q_ASSERT(type == ExitStatusType); - Q_UNUSED(type); - if (SshPacketParser::asBool(m_data, &offset)) - throw SshPacketParseException(); - exitStatus.exitStatus = SshPacketParser::asUint32(m_data, &offset); - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid exit-status packet."); - } - return exitStatus; -} - -SshChannelExitSignal SshIncomingPacket::extractChannelExitSignal() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_MSG_CHANNEL_REQUEST); - - SshChannelExitSignal exitSignal; - try { - quint32 offset = TypeOffset + 1; - exitSignal.localChannel = SshPacketParser::asUint32(m_data, &offset); - const QByteArray &type = SshPacketParser::asString(m_data, &offset); - Q_ASSERT(type == ExitSignalType); - Q_UNUSED(type); - if (SshPacketParser::asBool(m_data, &offset)) - throw SshPacketParseException(); - exitSignal.signal = SshPacketParser::asString(m_data, &offset); - exitSignal.coreDumped = SshPacketParser::asBool(m_data, &offset); - exitSignal.error = SshPacketParser::asUserString(m_data, &offset); - exitSignal.language = SshPacketParser::asString(m_data, &offset); - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid exit-signal packet."); - } - return exitSignal; -} - -quint32 SshIncomingPacket::extractRecipientChannel() const -{ - Q_ASSERT(isComplete()); - - try { - quint32 offset = TypeOffset + 1; - return SshPacketParser::asUint32(m_data, &offset); - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Server sent invalid packet."); - } -} - -QByteArray SshIncomingPacket::extractChannelRequestType() const -{ - Q_ASSERT(isComplete()); - Q_ASSERT(type() == SSH_MSG_CHANNEL_REQUEST); - - try { - quint32 offset = TypeOffset + 1; - SshPacketParser::asUint32(m_data, &offset); - return SshPacketParser::asString(m_data, &offset); - } catch (const SshPacketParseException &) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Invalid SSH_MSG_CHANNEL_REQUEST packet."); - } -} - -void SshIncomingPacket::calculateLength() const -{ - Q_ASSERT(currentDataSize() >= minPacketSize()); - qCDebug(sshLog, "Length field before decryption: %d-%d-%d-%d", m_data.at(0) & 0xff, - m_data.at(1) & 0xff, m_data.at(2) & 0xff, m_data.at(3) & 0xff); - m_decrypter.decrypt(m_data, 0, cipherBlockSize()); - qCDebug(sshLog, "Length field after decryption: %d-%d-%d-%d", m_data.at(0) & 0xff, m_data.at(1) & 0xff, m_data.at(2) & 0xff, m_data.at(3) & 0xff); - qCDebug(sshLog, "message type = %d", m_data.at(TypeOffset)); - m_length = SshPacketParser::asUint32(m_data, static_cast<quint32>(0)); - qCDebug(sshLog, "decrypted length is %u", m_length); -} - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshincomingpacket_p.h b/src/libs/ssh/sshincomingpacket_p.h deleted file mode 100644 index d495f9ca95f..00000000000 --- a/src/libs/ssh/sshincomingpacket_p.h +++ /dev/null @@ -1,244 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sshpacket_p.h" - -#include "sshcryptofacility_p.h" -#include "sshpacketparser_p.h" - -#include <QStringList> - -namespace QSsh { -namespace Internal { - -class SshKeyExchange; - -struct SshKeyExchangeInit -{ - char cookie[16]; - SshNameList keyAlgorithms; - SshNameList serverHostKeyAlgorithms; - SshNameList encryptionAlgorithmsClientToServer; - SshNameList encryptionAlgorithmsServerToClient; - SshNameList macAlgorithmsClientToServer; - SshNameList macAlgorithmsServerToClient; - SshNameList compressionAlgorithmsClientToServer; - SshNameList compressionAlgorithmsServerToClient; - SshNameList languagesClientToServer; - SshNameList languagesServerToClient; - bool firstKexPacketFollows; -}; - -struct SshKeyExchangeReply -{ - QByteArray k_s; - QList<Botan::BigInt> hostKeyParameters; // DSS: p, q, g, y. RSA: e, n. - QByteArray q; // For ECDSA host keys only. - Botan::BigInt f; // For DH only. - QByteArray q_s; // For ECDH only. - QByteArray signatureBlob; -}; - -struct SshDisconnect -{ - quint32 reasonCode; - QString description; - QByteArray language; -}; - -struct SshUserAuthBanner -{ - QString message; - QByteArray language; -}; - -struct SshUserAuthPkOkPacket -{ - QByteArray algoName; - QByteArray keyBlob; -}; - -struct SshUserAuthInfoRequestPacket -{ - QString name; - QString instruction; - QByteArray languageTag; - QStringList prompts; - QList<bool> echos; -}; - -struct SshDebug -{ - bool display; - QString message; - QByteArray language; -}; - -struct SshUnimplemented -{ - quint32 invalidMsgSeqNr; -}; - -struct SshRequestSuccess -{ - quint32 bindPort; -}; - -struct SshChannelOpenCommon -{ - quint32 remoteChannel; - quint32 remoteWindowSize; - quint32 remoteMaxPacketSize; -}; - -struct SshChannelOpenGeneric -{ - QByteArray channelType; - SshChannelOpenCommon commonData; - QByteArray typeSpecificData; -}; - -struct SshChannelOpenForwardedTcpIp -{ - SshChannelOpenCommon common; - QByteArray remoteAddress; - quint32 remotePort; - QByteArray originatorAddress; - quint32 originatorPort; -}; - -struct SshChannelOpenX11 -{ - SshChannelOpenCommon common; - QByteArray originatorAddress; - quint32 originatorPort; -}; - -struct SshChannelOpenFailure -{ - quint32 localChannel; - quint32 reasonCode; - QString reasonString; - QByteArray language; -}; - -struct SshChannelOpenConfirmation -{ - quint32 localChannel; - quint32 remoteChannel; - quint32 remoteWindowSize; - quint32 remoteMaxPacketSize; -}; - -struct SshChannelWindowAdjust -{ - quint32 localChannel; - quint32 bytesToAdd; -}; - -struct SshChannelData -{ - quint32 localChannel; - QByteArray data; -}; - -struct SshChannelExtendedData -{ - quint32 localChannel; - quint32 type; - QByteArray data; -}; - -struct SshChannelExitStatus -{ - quint32 localChannel; - quint32 exitStatus; -}; - -struct SshChannelExitSignal -{ - quint32 localChannel; - QByteArray signal; - bool coreDumped; - QString error; - QByteArray language; -}; - -class SshIncomingPacket : public AbstractSshPacket -{ -public: - SshIncomingPacket(); - - void consumeData(QByteArray &data); - void recreateKeys(const SshKeyExchange &keyExchange); - void reset(); - - SshKeyExchangeInit extractKeyExchangeInitData() const; - SshKeyExchangeReply extractKeyExchangeReply(const QByteArray &kexAlgo, - const QByteArray &hostKeyAlgo) const; - SshDisconnect extractDisconnect() const; - SshUserAuthBanner extractUserAuthBanner() const; - SshUserAuthInfoRequestPacket extractUserAuthInfoRequest() const; - SshUserAuthPkOkPacket extractUserAuthPkOk() const; - SshDebug extractDebug() const; - SshRequestSuccess extractRequestSuccess() const; - SshUnimplemented extractUnimplemented() const; - - SshChannelOpenGeneric extractChannelOpen() const; - static SshChannelOpenForwardedTcpIp extractChannelOpenForwardedTcpIp( - const SshChannelOpenGeneric &genericData); - static SshChannelOpenX11 extractChannelOpenX11(const SshChannelOpenGeneric &genericData); - SshChannelOpenFailure extractChannelOpenFailure() const; - SshChannelOpenConfirmation extractChannelOpenConfirmation() const; - SshChannelWindowAdjust extractWindowAdjust() const; - SshChannelData extractChannelData() const; - SshChannelExtendedData extractChannelExtendedData() const; - SshChannelExitStatus extractChannelExitStatus() const; - SshChannelExitSignal extractChannelExitSignal() const; - quint32 extractRecipientChannel() const; - QByteArray extractChannelRequestType() const; - - quint32 serverSeqNr() const { return m_serverSeqNr; } - - static const QByteArray ExitStatusType; - static const QByteArray ExitSignalType; - static const QByteArray ForwardedTcpIpType; - -private: - virtual quint32 cipherBlockSize() const; - virtual quint32 macLength() const; - virtual void calculateLength() const; - - void decrypt(); - void moveFirstBytes(QByteArray &target, QByteArray &source, int n); - - quint32 m_serverSeqNr; - SshDecryptionFacility m_decrypter; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshkeycreationdialog.cpp b/src/libs/ssh/sshkeycreationdialog.cpp index 21d51145994..9c4fe8db2b2 100644 --- a/src/libs/ssh/sshkeycreationdialog.cpp +++ b/src/libs/ssh/sshkeycreationdialog.cpp @@ -26,20 +26,23 @@ #include "sshkeycreationdialog.h" #include "ui_sshkeycreationdialog.h" -#include "sshkeygenerator.h" +#include "sshsettings.h" +#include <utils/fileutils.h> + +#include <QApplication> #include <QDir> #include <QFile> #include <QFileDialog> #include <QFileInfo> -#include <QApplication> #include <QMessageBox> +#include <QProcess> #include <QStandardPaths> namespace QSsh { SshKeyCreationDialog::SshKeyCreationDialog(QWidget *parent) - : QDialog(parent), m_keyGenerator(0), m_ui(new Ui::SshKeyCreationDialog) + : QDialog(parent), m_ui(new Ui::SshKeyCreationDialog) { m_ui->setupUi(this); // Not using Utils::PathChooser::browseButtonLabel to avoid dependency @@ -63,7 +66,6 @@ SshKeyCreationDialog::SshKeyCreationDialog(QWidget *parent) SshKeyCreationDialog::~SshKeyCreationDialog() { - delete m_keyGenerator; delete m_ui; } @@ -83,24 +85,33 @@ void SshKeyCreationDialog::keyTypeChanged() void SshKeyCreationDialog::generateKeys() { - if (userForbidsOverwriting()) + if (SshSettings::keygenFilePath().isEmpty()) { + showError(tr("The ssh-keygen tool was not found.")); return; - - const SshKeyGenerator::KeyType keyType = m_ui->rsa->isChecked() - ? SshKeyGenerator::Rsa : SshKeyGenerator::Ecdsa; - - if (!m_keyGenerator) - m_keyGenerator = new SshKeyGenerator; - + } + if (QFileInfo::exists(privateKeyFilePath())) { + showError(tr("Refusing to overwrite existing private key file \"%1\".") + .arg(QDir::toNativeSeparators(privateKeyFilePath()))); + return; + } + const QString keyTypeString = QLatin1String(m_ui->rsa->isChecked() ? "rsa": "ecdsa"); QApplication::setOverrideCursor(Qt::BusyCursor); - const bool success = m_keyGenerator->generateKeys(keyType, SshKeyGenerator::Mixed, - m_ui->comboBox->currentText().toUShort()); + QProcess keygen; + const QStringList args{"-t", keyTypeString, "-b", m_ui->comboBox->currentText(), + "-N", QString(), "-f", privateKeyFilePath()}; + QString errorMsg; + keygen.start(SshSettings::keygenFilePath().toString(), args); + keygen.closeWriteChannel(); + if (!keygen.waitForStarted() || !keygen.waitForFinished()) + errorMsg = keygen.errorString(); + else if (keygen.exitCode() != 0) + errorMsg = QString::fromLocal8Bit(keygen.readAllStandardError()); + if (!errorMsg.isEmpty()) { + showError(tr("The ssh-keygen tool at \"%1\" failed: %2") + .arg(SshSettings::keygenFilePath().toUserOutput(), errorMsg)); + } QApplication::restoreOverrideCursor(); - - if (success) - saveKeys(); - else - QMessageBox::critical(this, tr("Key Generation Failed"), m_keyGenerator->error()); + accept(); } void SshKeyCreationDialog::handleBrowseButtonClicked() @@ -117,43 +128,9 @@ void SshKeyCreationDialog::setPrivateKeyFile(const QString &filePath) m_ui->publicKeyFileLabel->setText(filePath + QLatin1String(".pub")); } -void SshKeyCreationDialog::saveKeys() -{ - const QString parentDir = QFileInfo(privateKeyFilePath()).dir().path(); - if (!QDir::root().mkpath(parentDir)) { - QMessageBox::critical(this, tr("Cannot Save Key File"), - tr("Failed to create directory: \"%1\".").arg(parentDir)); - return; - } - - QFile privateKeyFile(privateKeyFilePath()); - if (!privateKeyFile.open(QIODevice::WriteOnly) - || !privateKeyFile.write(m_keyGenerator->privateKey())) { - QMessageBox::critical(this, tr("Cannot Save Private Key File"), - tr("The private key file could not be saved: %1").arg(privateKeyFile.errorString())); - return; - } - QFile::setPermissions(privateKeyFilePath(), QFile::ReadOwner | QFile::WriteOwner); - - QFile publicKeyFile(publicKeyFilePath()); - if (!publicKeyFile.open(QIODevice::WriteOnly) - || !publicKeyFile.write(m_keyGenerator->publicKey())) { - QMessageBox::critical(this, tr("Cannot Save Public Key File"), - tr("The public key file could not be saved: %1").arg(publicKeyFile.errorString())); - return; - } - - accept(); -} - -bool SshKeyCreationDialog::userForbidsOverwriting() +void SshKeyCreationDialog::showError(const QString &details) { - if (!QFileInfo::exists(privateKeyFilePath()) && !QFileInfo::exists(publicKeyFilePath())) - return false; - const QMessageBox::StandardButton reply = QMessageBox::question(this, tr("File Exists"), - tr("There already is a file of that name. Do you want to overwrite it?"), - QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - return reply != QMessageBox::Yes; + QMessageBox::critical(this, tr("Key Generation Failed"), details); } QString SshKeyCreationDialog::privateKeyFilePath() const diff --git a/src/libs/ssh/sshkeycreationdialog.h b/src/libs/ssh/sshkeycreationdialog.h index 31059c0213e..a73fb3b3c2d 100644 --- a/src/libs/ssh/sshkeycreationdialog.h +++ b/src/libs/ssh/sshkeycreationdialog.h @@ -30,7 +30,6 @@ #include <QDialog> namespace QSsh { -class SshKeyGenerator; namespace Ui { class SshKeyCreationDialog; } @@ -49,11 +48,9 @@ private: void generateKeys(); void handleBrowseButtonClicked(); void setPrivateKeyFile(const QString &filePath); - void saveKeys(); - bool userForbidsOverwriting(); + void showError(const QString &details); private: - SshKeyGenerator *m_keyGenerator; Ui::SshKeyCreationDialog *m_ui; }; diff --git a/src/libs/ssh/sshkeyexchange.cpp b/src/libs/ssh/sshkeyexchange.cpp deleted file mode 100644 index 3eb013f282a..00000000000 --- a/src/libs/ssh/sshkeyexchange.cpp +++ /dev/null @@ -1,285 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshkeyexchange_p.h" - -#include "ssh_global.h" -#include "sshbotanconversions_p.h" -#include "sshcapabilities_p.h" -#include "sshlogging_p.h" -#include "sshsendfacility_p.h" -#include "sshexception_p.h" -#include "sshincomingpacket_p.h" - -#include <botan/auto_rng.h> -#include <botan/dh.h> -#include <botan/dl_group.h> -#include <botan/dsa.h> -#include <botan/ec_group.h> -#include <botan/ecdh.h> -#include <botan/ecdsa.h> -#include <botan/lookup.h> -#include <botan/point_gfp.h> -#include <botan/pubkey.h> -#include <botan/rsa.h> -#include <botan/types.h> - -#include <string> - -using namespace Botan; - -namespace QSsh { -namespace Internal { - -namespace { - - // For debugging - void printNameList(const char *listName, const SshNameList &list) - { - qCDebug(sshLog, "%s:", listName); - foreach (const QByteArray &name, list.names) - qCDebug(sshLog, "%s", name.constData()); - } - - void printData(const char *name, const QByteArray &data) - { - qCDebug(sshLog, "The client thinks the %s has length %d and is: %s", name, data.count(), - data.toHex().constData()); - } - -} // anonymous namespace - -SshKeyExchange::SshKeyExchange(const SshConnectionParameters &connParams, - SshSendFacility &sendFacility) - : m_connParams(connParams), m_sendFacility(sendFacility) -{ -} - -SshKeyExchange::~SshKeyExchange() {} - -void SshKeyExchange::sendKexInitPacket(const QByteArray &serverId) -{ - m_serverId = serverId; - m_clientKexInitPayload = m_sendFacility.sendKeyExchangeInitPacket(); -} - -bool SshKeyExchange::sendDhInitPacket(const SshIncomingPacket &serverKexInit) -{ - qCDebug(sshLog, "server requests key exchange"); - serverKexInit.printRawBytes(); - SshKeyExchangeInit kexInitParams - = serverKexInit.extractKeyExchangeInitData(); - - printNameList("Key Algorithms", kexInitParams.keyAlgorithms); - printNameList("Server Host Key Algorithms", kexInitParams.serverHostKeyAlgorithms); - printNameList("Encryption algorithms client to server", kexInitParams.encryptionAlgorithmsClientToServer); - printNameList("Encryption algorithms server to client", kexInitParams.encryptionAlgorithmsServerToClient); - printNameList("MAC algorithms client to server", kexInitParams.macAlgorithmsClientToServer); - printNameList("MAC algorithms server to client", kexInitParams.macAlgorithmsServerToClient); - printNameList("Compression algorithms client to server", kexInitParams.compressionAlgorithmsClientToServer); - printNameList("Compression algorithms client to server", kexInitParams.compressionAlgorithmsClientToServer); - printNameList("Languages client to server", kexInitParams.languagesClientToServer); - printNameList("Languages server to client", kexInitParams.languagesServerToClient); - qCDebug(sshLog, "First packet follows: %d", kexInitParams.firstKexPacketFollows); - - m_kexAlgoName = SshCapabilities::findBestMatch(SshCapabilities::KeyExchangeMethods, - kexInitParams.keyAlgorithms.names); - m_serverHostKeyAlgo = SshCapabilities::findBestMatch(SshCapabilities::PublicKeyAlgorithms, - kexInitParams.serverHostKeyAlgorithms.names); - determineHashingAlgorithm(kexInitParams, true); - determineHashingAlgorithm(kexInitParams, false); - - m_encryptionAlgo - = SshCapabilities::findBestMatch(SshCapabilities::EncryptionAlgorithms, - kexInitParams.encryptionAlgorithmsClientToServer.names); - m_decryptionAlgo - = SshCapabilities::findBestMatch(SshCapabilities::EncryptionAlgorithms, - kexInitParams.encryptionAlgorithmsServerToClient.names); - SshCapabilities::findBestMatch(SshCapabilities::CompressionAlgorithms, - kexInitParams.compressionAlgorithmsClientToServer.names); - SshCapabilities::findBestMatch(SshCapabilities::CompressionAlgorithms, - kexInitParams.compressionAlgorithmsServerToClient.names); - - AutoSeeded_RNG rng; - if (m_kexAlgoName.startsWith(SshCapabilities::EcdhKexNamePrefix)) { - m_ecdhKey.reset(new ECDH_PrivateKey(rng, EC_Group(botanKeyExchangeAlgoName(m_kexAlgoName)))); - m_sendFacility.sendKeyEcdhInitPacket(convertByteArray(m_ecdhKey->public_value())); - } else { - m_dhKey.reset(new DH_PrivateKey(rng, DL_Group(botanKeyExchangeAlgoName(m_kexAlgoName)))); - m_sendFacility.sendKeyDhInitPacket(m_dhKey->get_y()); - } - - m_serverKexInitPayload = serverKexInit.payLoad(); - return kexInitParams.firstKexPacketFollows; -} - -void SshKeyExchange::sendNewKeysPacket(const SshIncomingPacket &dhReply, - const QByteArray &clientId) -{ - - const SshKeyExchangeReply &reply - = dhReply.extractKeyExchangeReply(m_kexAlgoName, m_serverHostKeyAlgo); - if (m_dhKey && (reply.f <= 0 || reply.f >= m_dhKey->group_p())) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_KEY_EXCHANGE_FAILED, - "Server sent invalid f."); - } - - QByteArray concatenatedData = AbstractSshPacket::encodeString(clientId); - concatenatedData += AbstractSshPacket::encodeString(m_serverId); - concatenatedData += AbstractSshPacket::encodeString(m_clientKexInitPayload); - concatenatedData += AbstractSshPacket::encodeString(m_serverKexInitPayload); - concatenatedData += reply.k_s; - - printData("Client Id", AbstractSshPacket::encodeString(clientId)); - printData("Server Id", AbstractSshPacket::encodeString(m_serverId)); - printData("Client Payload", AbstractSshPacket::encodeString(m_clientKexInitPayload)); - printData("Server payload", AbstractSshPacket::encodeString(m_serverKexInitPayload)); - printData("K_S", reply.k_s); - - SecureVector<byte> encodedK; - Botan::AutoSeeded_RNG rng; - if (m_dhKey) { - concatenatedData += AbstractSshPacket::encodeMpInt(m_dhKey->get_y()); - concatenatedData += AbstractSshPacket::encodeMpInt(reply.f); - Botan::PK_Key_Agreement dhOp(*m_dhKey, rng, "Raw"); - const std::vector<std::uint8_t> encodedF = BigInt::encode(reply.f); - encodedK = dhOp.derive_key(m_dhKey->group_p().bytes(), encodedF).bits_of(); - printData("y", AbstractSshPacket::encodeMpInt(m_dhKey->get_y())); - printData("f", AbstractSshPacket::encodeMpInt(reply.f)); - m_dhKey.reset(nullptr); - } else { - Q_ASSERT(m_ecdhKey); - concatenatedData // Q_C. - += AbstractSshPacket::encodeString(convertByteArray(m_ecdhKey->public_value())); - concatenatedData += AbstractSshPacket::encodeString(reply.q_s); - Botan::PK_Key_Agreement ecdhOp(*m_ecdhKey, rng, "Raw"); - encodedK = ecdhOp.derive_key(m_ecdhKey->domain().get_p().bytes(), - convertByteArray(reply.q_s), reply.q_s.count()).bits_of(); - m_ecdhKey.reset(nullptr); - } - - const BigInt k = BigInt::decode(encodedK); - m_k = AbstractSshPacket::encodeMpInt(k); // Roundtrip, as Botan encodes BigInts somewhat differently. - printData("K", m_k); - concatenatedData += m_k; - printData("Concatenated data", concatenatedData); - - m_hash = HashFunction::create_or_throw(botanHMacAlgoName(hashAlgoForKexAlgo())); - const SecureVector<byte> &hashResult = m_hash->process(convertByteArray(concatenatedData), - concatenatedData.size()); - m_h = convertByteArray(hashResult); - printData("H", m_h); - - QScopedPointer<Public_Key> sigKey; - if (m_serverHostKeyAlgo == SshCapabilities::PubKeyDss) { - const DL_Group group(reply.hostKeyParameters.at(0), reply.hostKeyParameters.at(1), - reply.hostKeyParameters.at(2)); - DSA_PublicKey * const dsaKey - = new DSA_PublicKey(group, reply.hostKeyParameters.at(3)); - sigKey.reset(dsaKey); - } else if (m_serverHostKeyAlgo == SshCapabilities::PubKeyRsa) { - RSA_PublicKey * const rsaKey - = new RSA_PublicKey(reply.hostKeyParameters.at(1), reply.hostKeyParameters.at(0)); - sigKey.reset(rsaKey); - } else { - QSSH_ASSERT_AND_RETURN(m_serverHostKeyAlgo.startsWith(SshCapabilities::PubKeyEcdsaPrefix)); - const EC_Group domain(SshCapabilities::oid(m_serverHostKeyAlgo)); - const PointGFp point = domain.OS2ECP(convertByteArray(reply.q), reply.q.count()); - ECDSA_PublicKey * const ecdsaKey = new ECDSA_PublicKey(domain, point); - sigKey.reset(ecdsaKey); - } - - const byte * const botanH = convertByteArray(m_h); - const Botan::byte * const botanSig = convertByteArray(reply.signatureBlob); - PK_Verifier verifier(*sigKey, botanEmsaAlgoName(m_serverHostKeyAlgo)); - if (!verifier.verify_message(botanH, m_h.size(), botanSig, reply.signatureBlob.size())) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_KEY_EXCHANGE_FAILED, - "Invalid signature in key exchange reply packet."); - } - - checkHostKey(reply.k_s); - - m_sendFacility.sendNewKeysPacket(); -} - -QByteArray SshKeyExchange::hashAlgoForKexAlgo() const -{ - if (m_kexAlgoName == SshCapabilities::EcdhNistp256) - return SshCapabilities::HMacSha256; - if (m_kexAlgoName == SshCapabilities::EcdhNistp384) - return SshCapabilities::HMacSha384; - if (m_kexAlgoName == SshCapabilities::EcdhNistp521) - return SshCapabilities::HMacSha512; - return SshCapabilities::HMacSha1; -} - -void SshKeyExchange::determineHashingAlgorithm(const SshKeyExchangeInit &kexInit, - bool serverToClient) -{ - QByteArray * const algo = serverToClient ? &m_s2cHMacAlgo : &m_c2sHMacAlgo; - const QList<QByteArray> &serverCapabilities = serverToClient - ? kexInit.macAlgorithmsServerToClient.names - : kexInit.macAlgorithmsClientToServer.names; - *algo = SshCapabilities::findBestMatch(SshCapabilities::MacAlgorithms, serverCapabilities); -} - -void SshKeyExchange::checkHostKey(const QByteArray &hostKey) -{ - if (m_connParams.hostKeyCheckingMode == SshHostKeyCheckingNone) { - if (m_connParams.hostKeyDatabase) - m_connParams.hostKeyDatabase->insertHostKey(m_connParams.host(), hostKey); - return; - } - - if (!m_connParams.hostKeyDatabase) { - throw SshClientException(SshInternalError, - SSH_TR("Host key database must exist " - "if host key checking is enabled.")); - } - - switch (m_connParams.hostKeyDatabase->matchHostKey(m_connParams.host(), hostKey)) { - case SshHostKeyDatabase::KeyLookupMatch: - return; // Nothing to do. - case SshHostKeyDatabase::KeyLookupMismatch: - if (m_connParams.hostKeyCheckingMode != SshHostKeyCheckingAllowMismatch) - throwHostKeyException(); - break; - case SshHostKeyDatabase::KeyLookupNoMatch: - if (m_connParams.hostKeyCheckingMode == SshHostKeyCheckingStrict) - throwHostKeyException(); - break; - } - m_connParams.hostKeyDatabase->insertHostKey(m_connParams.host(), hostKey); -} - -void SshKeyExchange::throwHostKeyException() -{ - throw SshServerException(SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE, "Host key changed", - SSH_TR("Host key of machine \"%1\" has changed.") - .arg(m_connParams.host())); -} - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshkeyexchange_p.h b/src/libs/ssh/sshkeyexchange_p.h deleted file mode 100644 index de5828292e2..00000000000 --- a/src/libs/ssh/sshkeyexchange_p.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sshconnection.h" - -#include <QByteArray> -#include <QScopedPointer> - -#include <memory> - -namespace Botan { -class DH_PrivateKey; -class ECDH_PrivateKey; -class HashFunction; -} - -namespace QSsh { -namespace Internal { - -struct SshKeyExchangeInit; -class SshSendFacility; -class SshIncomingPacket; - -class SshKeyExchange -{ -public: - SshKeyExchange(const SshConnectionParameters &connParams, SshSendFacility &sendFacility); - ~SshKeyExchange(); - - void sendKexInitPacket(const QByteArray &serverId); - - // Returns true <=> the server sends a guessed package. - bool sendDhInitPacket(const SshIncomingPacket &serverKexInit); - - void sendNewKeysPacket(const SshIncomingPacket &dhReply, - const QByteArray &clientId); - - QByteArray k() const { return m_k; } - QByteArray h() const { return m_h; } - Botan::HashFunction *hash() const { return m_hash.get(); } - QByteArray encryptionAlgo() const { return m_encryptionAlgo; } - QByteArray decryptionAlgo() const { return m_decryptionAlgo; } - QByteArray hMacAlgoClientToServer() const { return m_c2sHMacAlgo; } - QByteArray hMacAlgoServerToClient() const { return m_s2cHMacAlgo; } - -private: - QByteArray hashAlgoForKexAlgo() const; - void determineHashingAlgorithm(const SshKeyExchangeInit &kexInit, bool serverToClient); - void checkHostKey(const QByteArray &hostKey); - Q_NORETURN void throwHostKeyException(); - - QByteArray m_serverId; - QByteArray m_clientKexInitPayload; - QByteArray m_serverKexInitPayload; - QScopedPointer<Botan::DH_PrivateKey> m_dhKey; - QScopedPointer<Botan::ECDH_PrivateKey> m_ecdhKey; - QByteArray m_kexAlgoName; - QByteArray m_k; - QByteArray m_h; - QByteArray m_serverHostKeyAlgo; - QByteArray m_encryptionAlgo; - QByteArray m_decryptionAlgo; - QByteArray m_c2sHMacAlgo; - QByteArray m_s2cHMacAlgo; - std::unique_ptr<Botan::HashFunction> m_hash; - const SshConnectionParameters m_connParams; - SshSendFacility &m_sendFacility; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshkeygenerator.cpp b/src/libs/ssh/sshkeygenerator.cpp deleted file mode 100644 index 38828ee97f4..00000000000 --- a/src/libs/ssh/sshkeygenerator.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshkeygenerator.h" - -#include "sshbotanconversions_p.h" -#include "sshcapabilities_p.h" -#include "sshlogging_p.h" -#include "ssh_global.h" -#include "sshpacket_p.h" - -#include <botan/auto_rng.h> -#include <botan/der_enc.h> -#include <botan/dsa.h> -#include <botan/ecdsa.h> -#include <botan/numthry.h> -#include <botan/pem.h> -#include <botan/pipe.h> -#include <botan/pkcs8.h> -#include <botan/rsa.h> -#include <botan/x509_key.h> - -#include <QDateTime> -#include <QInputDialog> - -#include <string> - -namespace QSsh { - -using namespace Botan; -using namespace Internal; - -SshKeyGenerator::SshKeyGenerator() : m_type(Rsa) -{ -} - -bool SshKeyGenerator::generateKeys(KeyType type, PrivateKeyFormat format, int keySize, - EncryptionMode encryptionMode) -{ - m_type = type; - m_encryptionMode = encryptionMode; - - try { - AutoSeeded_RNG rng; - KeyPtr key; - switch (m_type) { - case Rsa: - key = KeyPtr(new RSA_PrivateKey(rng, keySize)); - break; - case Dsa: - key = KeyPtr(new DSA_PrivateKey(rng, DL_Group(rng, DL_Group::DSA_Kosherizer, keySize))); - break; - case Ecdsa: { - const QByteArray algo = SshCapabilities::ecdsaPubKeyAlgoForKeyWidth(keySize / 8); - key = KeyPtr(new ECDSA_PrivateKey(rng, EC_Group(SshCapabilities::oid(algo)))); - break; - } - } - switch (format) { - case Pkcs8: - generatePkcs8KeyStrings(key, rng); - break; - case OpenSsl: - generateOpenSslKeyStrings(key); - break; - case Mixed: - default: - generatePkcs8KeyString(key, true, rng); - generateOpenSslPublicKeyString(key); - } - return true; - } catch (const std::exception &e) { - m_error = tr("Error generating key: %1").arg(QString::fromLatin1(e.what())); - return false; - } -} - -void SshKeyGenerator::generatePkcs8KeyStrings(const KeyPtr &key, RandomNumberGenerator &rng) -{ - generatePkcs8KeyString(key, false, rng); - generatePkcs8KeyString(key, true, rng); -} - -void SshKeyGenerator::generatePkcs8KeyString(const KeyPtr &key, bool privateKey, - RandomNumberGenerator &rng) -{ - Pipe pipe; - pipe.start_msg(); - QByteArray *keyData; - if (privateKey) { - QString password; - if (m_encryptionMode == DoOfferEncryption) - password = getPassword(); - if (!password.isEmpty()) - pipe.write(PKCS8::PEM_encode(*key, rng, password.toLocal8Bit().data())); - else - pipe.write(PKCS8::PEM_encode(*key)); - keyData = &m_privateKey; - } else { - pipe.write(X509::PEM_encode(*key)); - keyData = &m_publicKey; - } - pipe.end_msg(); - keyData->resize(static_cast<int>(pipe.remaining(pipe.message_count() - 1))); - if (pipe.read(convertByteArray(*keyData), keyData->size(), pipe.message_count() - 1) - != std::size_t(keyData->size())) { - qCWarning(sshLog) << "short read from botan pipe"; - } -} - -void SshKeyGenerator::generateOpenSslKeyStrings(const KeyPtr &key) -{ - generateOpenSslPublicKeyString(key); - generateOpenSslPrivateKeyString(key); -} - -void SshKeyGenerator::generateOpenSslPublicKeyString(const KeyPtr &key) -{ - QList<BigInt> params; - QByteArray keyId; - QByteArray q; - switch (m_type) { - case Rsa: { - const QSharedPointer<RSA_PrivateKey> rsaKey = key.dynamicCast<RSA_PrivateKey>(); - params << rsaKey->get_e() << rsaKey->get_n(); - keyId = SshCapabilities::PubKeyRsa; - break; - } - case Dsa: { - const QSharedPointer<DSA_PrivateKey> dsaKey = key.dynamicCast<DSA_PrivateKey>(); - params << dsaKey->group_p() << dsaKey->group_q() << dsaKey->group_g() << dsaKey->get_y(); - keyId = SshCapabilities::PubKeyDss; - break; - } - case Ecdsa: { - const auto ecdsaKey = key.dynamicCast<ECDSA_PrivateKey>(); - q = convertByteArray(ecdsaKey->public_point().encode(PointGFp::UNCOMPRESSED)); - keyId = SshCapabilities::ecdsaPubKeyAlgoForKeyWidth( - static_cast<int>(ecdsaKey->private_value().bytes())); - break; - } - } - - QByteArray publicKeyBlob = AbstractSshPacket::encodeString(keyId); - foreach (const BigInt &b, params) - publicKeyBlob += AbstractSshPacket::encodeMpInt(b); - if (!q.isEmpty()) { - publicKeyBlob += AbstractSshPacket::encodeString(keyId.mid(11)); // Without "ecdsa-sha2-" prefix. - publicKeyBlob += AbstractSshPacket::encodeString(q); - } - publicKeyBlob = publicKeyBlob.toBase64(); - const QByteArray id = "QtCreator/" - + QDateTime::currentDateTime().toString(Qt::ISODate).toUtf8(); - m_publicKey = keyId + ' ' + publicKeyBlob + ' ' + id; -} - -void SshKeyGenerator::generateOpenSslPrivateKeyString(const KeyPtr &key) -{ - QList<BigInt> params; - const char *label = ""; - switch (m_type) { - case Rsa: { - const QSharedPointer<RSA_PrivateKey> rsaKey - = key.dynamicCast<RSA_PrivateKey>(); - params << rsaKey->get_n() << rsaKey->get_e() << rsaKey->get_d() << rsaKey->get_p() - << rsaKey->get_q(); - const BigInt dmp1 = rsaKey->get_d() % (rsaKey->get_p() - 1); - const BigInt dmq1 = rsaKey->get_d() % (rsaKey->get_q() - 1); - const BigInt iqmp = inverse_mod(rsaKey->get_q(), rsaKey->get_p()); - params << dmp1 << dmq1 << iqmp; - label = "RSA PRIVATE KEY"; - break; - } - case Dsa: { - const QSharedPointer<DSA_PrivateKey> dsaKey = key.dynamicCast<DSA_PrivateKey>(); - params << dsaKey->group_p() << dsaKey->group_q() << dsaKey->group_g() << dsaKey->get_y() - << dsaKey->get_x(); - label = "DSA PRIVATE KEY"; - break; - } - case Ecdsa: - params << key.dynamicCast<ECDSA_PrivateKey>()->private_value(); - label = "EC PRIVATE KEY"; - break; - } - - DER_Encoder encoder; - encoder.start_cons(SEQUENCE).encode(size_t(0)); - foreach (const BigInt &b, params) - encoder.encode(b); - encoder.end_cons(); - m_privateKey = QByteArray(PEM_Code::encode (encoder.get_contents(), label).c_str()); -} - -QString SshKeyGenerator::getPassword() const -{ - QInputDialog d; - d.setInputMode(QInputDialog::TextInput); - d.setTextEchoMode(QLineEdit::Password); - d.setWindowTitle(tr("Password for Private Key")); - d.setLabelText(tr("It is recommended that you secure your private key\n" - "with a password, which you can enter below.")); - d.setOkButtonText(tr("Encrypt Key File")); - d.setCancelButtonText(tr("Do Not Encrypt Key File")); - int result = QDialog::Accepted; - QString password; - while (result == QDialog::Accepted && password.isEmpty()) { - result = d.exec(); - password = d.textValue(); - } - return result == QDialog::Accepted ? password : QString(); -} - -} // namespace QSsh diff --git a/src/libs/ssh/sshkeygenerator.h b/src/libs/ssh/sshkeygenerator.h deleted file mode 100644 index 483614429e5..00000000000 --- a/src/libs/ssh/sshkeygenerator.h +++ /dev/null @@ -1,75 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "ssh_global.h" - -#include <QCoreApplication> -#include <QSharedPointer> - -namespace Botan { - class Private_Key; - class RandomNumberGenerator; -} - -namespace QSsh { - -class QSSH_EXPORT SshKeyGenerator -{ - Q_DECLARE_TR_FUNCTIONS(SshKeyGenerator) -public: - enum KeyType { Rsa, Dsa, Ecdsa }; - enum PrivateKeyFormat { Pkcs8, OpenSsl, Mixed }; - enum EncryptionMode { DoOfferEncryption, DoNotOfferEncryption }; // Only relevant for Pkcs8 format. - - SshKeyGenerator(); - bool generateKeys(KeyType type, PrivateKeyFormat format, int keySize, - EncryptionMode encryptionMode = DoOfferEncryption); - - QString error() const { return m_error; } - QByteArray privateKey() const { return m_privateKey; } - QByteArray publicKey() const { return m_publicKey; } - KeyType type() const { return m_type; } - -private: - typedef QSharedPointer<Botan::Private_Key> KeyPtr; - - void generatePkcs8KeyStrings(const KeyPtr &key, Botan::RandomNumberGenerator &rng); - void generatePkcs8KeyString(const KeyPtr &key, bool privateKey, - Botan::RandomNumberGenerator &rng); - void generateOpenSslKeyStrings(const KeyPtr &key); - void generateOpenSslPrivateKeyString(const KeyPtr &key); - void generateOpenSslPublicKeyString(const KeyPtr &key); - QString getPassword() const; - - QString m_error; - QByteArray m_publicKey; - QByteArray m_privateKey; - KeyType m_type; - EncryptionMode m_encryptionMode; -}; - -} // namespace QSsh diff --git a/src/libs/ssh/sshkeypasswordretriever.cpp b/src/libs/ssh/sshkeypasswordretriever.cpp deleted file mode 100644 index 6631404a038..00000000000 --- a/src/libs/ssh/sshkeypasswordretriever.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshkeypasswordretriever_p.h" - -#include <QString> -#include <QApplication> -#include <QInputDialog> - -#include <iostream> - -namespace QSsh { -namespace Internal { - -std::string get_passphrase() -{ - const bool hasGui = dynamic_cast<QApplication *>(QApplication::instance()); - if (hasGui) { - const QString &password = QInputDialog::getText(0, - QCoreApplication::translate("QSsh::Ssh", "Password Required"), - QCoreApplication::translate("QSsh::Ssh", "Please enter the password for your private key."), - QLineEdit::Password, QString()); - return std::string(password.toLocal8Bit().data()); - } else { - std::string password; - std::cout << "Please enter the password for your private key (set echo off beforehand!): " << std::flush; - std::cin >> password; - return password; - } -} - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshoutgoingpacket.cpp b/src/libs/ssh/sshoutgoingpacket.cpp deleted file mode 100644 index ac25e0c9800..00000000000 --- a/src/libs/ssh/sshoutgoingpacket.cpp +++ /dev/null @@ -1,415 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshoutgoingpacket_p.h" - -#include "sshagent_p.h" -#include "sshcapabilities_p.h" -#include "sshcryptofacility_p.h" -#include "sshlogging_p.h" -#include "sshpacketparser_p.h" - -#include <QtEndian> - -namespace QSsh { -namespace Internal { - -SshOutgoingPacket::SshOutgoingPacket(const SshEncryptionFacility &encrypter, - const quint32 &seqNr) : m_encrypter(encrypter), m_seqNr(seqNr) -{ -} - -quint32 SshOutgoingPacket::cipherBlockSize() const -{ - return qMax(m_encrypter.cipherBlockSize(), 4U); -} - -quint32 SshOutgoingPacket::macLength() const -{ - return m_encrypter.macLength(); -} - -QByteArray SshOutgoingPacket::generateKeyExchangeInitPacket() -{ - const QByteArray &supportedkeyExchangeMethods - = encodeNameList(SshCapabilities::KeyExchangeMethods); - const QByteArray &supportedPublicKeyAlgorithms - = encodeNameList(SshCapabilities::PublicKeyAlgorithms); - const QByteArray &supportedEncryptionAlgorithms - = encodeNameList(SshCapabilities::EncryptionAlgorithms); - const QByteArray &supportedMacAlgorithms - = encodeNameList(SshCapabilities::MacAlgorithms); - const QByteArray &supportedCompressionAlgorithms - = encodeNameList(SshCapabilities::CompressionAlgorithms); - const QByteArray &supportedLanguages = encodeNameList(QList<QByteArray>()); - - init(SSH_MSG_KEXINIT); - m_data += m_encrypter.getRandomNumbers(16); - m_data.append(supportedkeyExchangeMethods); - m_data.append(supportedPublicKeyAlgorithms); - m_data.append(supportedEncryptionAlgorithms) - .append(supportedEncryptionAlgorithms); - m_data.append(supportedMacAlgorithms).append(supportedMacAlgorithms); - m_data.append(supportedCompressionAlgorithms) - .append(supportedCompressionAlgorithms); - m_data.append(supportedLanguages).append(supportedLanguages); - appendBool(false); // No guessed packet. - m_data.append(QByteArray(4, 0)); // Reserved. - QByteArray payload = m_data.mid(PayloadOffset); - finalize(); - return payload; -} - -void SshOutgoingPacket::generateKeyDhInitPacket(const Botan::BigInt &e) -{ - init(SSH_MSG_KEXDH_INIT).appendMpInt(e).finalize(); -} - -void SshOutgoingPacket::generateKeyEcdhInitPacket(const QByteArray &clientQ) -{ - init(SSH_MSG_KEX_ECDH_INIT).appendString(clientQ).finalize(); -} - -void SshOutgoingPacket::generateNewKeysPacket() -{ - init(SSH_MSG_NEWKEYS).finalize(); -} - -void SshOutgoingPacket::generateUserAuthServiceRequestPacket() -{ - generateServiceRequest("ssh-userauth"); -} - -void SshOutgoingPacket::generateServiceRequest(const QByteArray &service) -{ - init(SSH_MSG_SERVICE_REQUEST).appendString(service).finalize(); -} - -void SshOutgoingPacket::generateUserAuthByPasswordRequestPacket(const QByteArray &user, - const QByteArray &service, const QByteArray &pwd) -{ - init(SSH_MSG_USERAUTH_REQUEST).appendString(user).appendString(service); - if (pwd.isEmpty()) - appendString("none"); // RFC 4252, 5.2 - else - appendString("password").appendBool(false).appendString(pwd); - finalize(); -} - -void SshOutgoingPacket::generateUserAuthByPublicKeyRequestPacket(const QByteArray &user, - const QByteArray &service, const QByteArray &key, const QByteArray &signature) -{ - init(SSH_MSG_USERAUTH_REQUEST).appendString(user).appendString(service) - .appendString("publickey").appendBool(true); - if (!key.isEmpty()) { - appendString(SshPacketParser::asString(key, quint32(0))); - appendString(key); - appendString(signature); - } else { - appendString(m_encrypter.authenticationAlgorithmName()); - appendString(m_encrypter.authenticationPublicKey()); - const QByteArray &dataToSign = m_data.mid(PayloadOffset); - appendString(m_encrypter.authenticationKeySignature(dataToSign)); - } - finalize(); -} - -void SshOutgoingPacket::generateQueryPublicKeyPacket(const QByteArray &user, - const QByteArray &service, const QByteArray &publicKey) -{ - // Name extraction cannot fail, we already verified this when receiving the key - // from the agent. - const QByteArray algoName = SshPacketParser::asString(publicKey, quint32(0)); - SshOutgoingPacket packetToSign(m_encrypter, m_seqNr); - packetToSign.init(SSH_MSG_USERAUTH_REQUEST).appendString(user).appendString(service) - .appendString("publickey").appendBool(true).appendString(algoName) - .appendString(publicKey); - const QByteArray &dataToSign - = encodeString(m_encrypter.sessionId()) + packetToSign.m_data.mid(PayloadOffset); - SshAgent::storeDataToSign(publicKey, dataToSign, qHash(m_encrypter.sessionId())); - init(SSH_MSG_USERAUTH_REQUEST).appendString(user).appendString(service) - .appendString("publickey").appendBool(false).appendString(algoName) - .appendString(publicKey).finalize(); -} - -void SshOutgoingPacket::generateUserAuthByKeyboardInteractiveRequestPacket(const QByteArray &user, - const QByteArray &service) -{ - // RFC 4256, 3.1 - init(SSH_MSG_USERAUTH_REQUEST).appendString(user).appendString(service) - .appendString("keyboard-interactive") - .appendString(QByteArray()) // Language tag. Deprecated and should be empty - .appendString(QByteArray()) // Submethods. - .finalize(); -} - -void SshOutgoingPacket::generateUserAuthInfoResponsePacket(const QStringList &responses) -{ - // RFC 4256, 3.4 - init(SSH_MSG_USERAUTH_INFO_RESPONSE).appendInt(responses.count()); - foreach (const QString &response, responses) - appendString(response.toUtf8()); - finalize(); -} - -void SshOutgoingPacket::generateRequestFailurePacket() -{ - init(SSH_MSG_REQUEST_FAILURE).finalize(); -} - -void SshOutgoingPacket::generateIgnorePacket() -{ - init(SSH_MSG_IGNORE).finalize(); -} - -void SshOutgoingPacket::generateInvalidMessagePacket() -{ - init(SSH_MSG_INVALID).finalize(); -} - -void SshOutgoingPacket::generateSessionPacket(quint32 channelId, - quint32 windowSize, quint32 maxPacketSize) -{ - init(SSH_MSG_CHANNEL_OPEN).appendString("session").appendInt(channelId) - .appendInt(windowSize).appendInt(maxPacketSize).finalize(); -} - -void SshOutgoingPacket::generateDirectTcpIpPacket(quint32 channelId, quint32 windowSize, - quint32 maxPacketSize, const QByteArray &remoteHost, quint32 remotePort, - const QByteArray &localIpAddress, quint32 localPort) -{ - init(SSH_MSG_CHANNEL_OPEN).appendString("direct-tcpip").appendInt(channelId) - .appendInt(windowSize).appendInt(maxPacketSize).appendString(remoteHost) - .appendInt(remotePort).appendString(localIpAddress).appendInt(localPort).finalize(); -} - -void SshOutgoingPacket::generateTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort) -{ - init(SSH_MSG_GLOBAL_REQUEST).appendString("tcpip-forward").appendBool(true) - .appendString(bindAddress).appendInt(bindPort).finalize(); -} - -void SshOutgoingPacket::generateCancelTcpIpForwardPacket(const QByteArray &bindAddress, - quint32 bindPort) -{ - init(SSH_MSG_GLOBAL_REQUEST).appendString("cancel-tcpip-forward").appendBool(true) - .appendString(bindAddress).appendInt(bindPort).finalize(); -} - -void SshOutgoingPacket::generateEnvPacket(quint32 remoteChannel, - const QByteArray &var, const QByteArray &value) -{ - init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel).appendString("env") - .appendBool(false).appendString(var).appendString(value).finalize(); -} - -void SshOutgoingPacket::generateX11ForwardingPacket(quint32 remoteChannel, - const QByteArray &protocol, const QByteArray &cookie, quint32 screenNumber) -{ - init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel).appendString("x11-req") - .appendBool(false).appendBool(false).appendString(protocol) - .appendString(cookie).appendInt(screenNumber).finalize(); -} - -void SshOutgoingPacket::generatePtyRequestPacket(quint32 remoteChannel, - const SshPseudoTerminal &terminal) -{ - init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel) - .appendString("pty-req").appendBool(false) - .appendString(terminal.termType).appendInt(terminal.columnCount) - .appendInt(terminal.rowCount).appendInt(0).appendInt(0); - QByteArray modeString; - for (SshPseudoTerminal::ModeMap::ConstIterator it = terminal.modes.constBegin(); - it != terminal.modes.constEnd(); ++it) { - modeString += char(it.key()); - modeString += encodeInt(it.value()); - } - modeString += char(0); // TTY_OP_END - appendString(modeString).finalize(); -} - -void SshOutgoingPacket::generateExecPacket(quint32 remoteChannel, - const QByteArray &command) -{ - init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel).appendString("exec") - .appendBool(true).appendString(command).finalize(); -} - -void SshOutgoingPacket::generateShellPacket(quint32 remoteChannel) -{ - init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel).appendString("shell") - .appendBool(true).finalize(); -} - -void SshOutgoingPacket::generateSftpPacket(quint32 remoteChannel) -{ - init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel) - .appendString("subsystem").appendBool(true).appendString("sftp") - .finalize(); -} - -void SshOutgoingPacket::generateWindowAdjustPacket(quint32 remoteChannel, - quint32 bytesToAdd) -{ - init(SSH_MSG_CHANNEL_WINDOW_ADJUST).appendInt(remoteChannel) - .appendInt(bytesToAdd).finalize(); -} - -void SshOutgoingPacket::generateChannelDataPacket(quint32 remoteChannel, - const QByteArray &data) -{ - init(SSH_MSG_CHANNEL_DATA).appendInt(remoteChannel).appendString(data) - .finalize(); -} - -void SshOutgoingPacket::generateChannelSignalPacket(quint32 remoteChannel, - const QByteArray &signalName) -{ - init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel) - .appendString("signal").appendBool(false).appendString(signalName) - .finalize(); -} - -void SshOutgoingPacket::generateChannelEofPacket(quint32 remoteChannel) -{ - init(SSH_MSG_CHANNEL_EOF).appendInt(remoteChannel).finalize(); -} - -void SshOutgoingPacket::generateChannelClosePacket(quint32 remoteChannel) -{ - init(SSH_MSG_CHANNEL_CLOSE).appendInt(remoteChannel).finalize(); -} - -void SshOutgoingPacket::generateChannelOpenConfirmationPacket(quint32 remoteChannel, - quint32 localChannel, - quint32 localWindowSize, - quint32 maxPacketSize) -{ - init(SSH_MSG_CHANNEL_OPEN_CONFIRMATION).appendInt(remoteChannel).appendInt(localChannel) - .appendInt(localWindowSize).appendInt(maxPacketSize).finalize(); -} - -void SshOutgoingPacket::generateChannelOpenFailurePacket(quint32 remoteChannel, quint32 reason, - const QByteArray &reasonString) -{ - init(SSH_MSG_CHANNEL_OPEN_FAILURE).appendInt(remoteChannel).appendInt(reason) - .appendString(reasonString).appendString(QByteArray()).finalize(); -} - -void SshOutgoingPacket::generateDisconnectPacket(SshErrorCode reason, - const QByteArray &reasonString) -{ - init(SSH_MSG_DISCONNECT).appendInt(reason).appendString(reasonString) - .appendString(QByteArray()).finalize(); -} - -void SshOutgoingPacket::generateMsgUnimplementedPacket(quint32 serverSeqNr) -{ - init(SSH_MSG_UNIMPLEMENTED).appendInt(serverSeqNr).finalize(); -} - -SshOutgoingPacket &SshOutgoingPacket::appendInt(quint32 val) -{ - m_data.append(encodeInt(val)); - return *this; -} - -SshOutgoingPacket &SshOutgoingPacket::appendMpInt(const Botan::BigInt &number) -{ - m_data.append(encodeMpInt(number)); - return *this; -} - -SshOutgoingPacket &SshOutgoingPacket::appendBool(bool b) -{ - m_data += static_cast<char>(b); - return *this; -} - -SshOutgoingPacket &SshOutgoingPacket::appendString(const QByteArray &string) -{ - m_data.append(encodeString(string)); - return *this; -} - -SshOutgoingPacket &SshOutgoingPacket::init(SshPacketType type) -{ - m_data.resize(TypeOffset + 1); - m_data[TypeOffset] = type; - return *this; -} - -SshOutgoingPacket &SshOutgoingPacket::setPadding() -{ - m_data += m_encrypter.getRandomNumbers(MinPaddingLength); - int padLength = MinPaddingLength; - const int divisor = sizeDivisor(); - const int mod = m_data.size() % divisor; - padLength += divisor - mod; - m_data += m_encrypter.getRandomNumbers(padLength - MinPaddingLength); - m_data[PaddingLengthOffset] = padLength; - return *this; -} - -SshOutgoingPacket &SshOutgoingPacket::encrypt() -{ - const QByteArray &mac - = generateMac(m_encrypter, m_seqNr); - m_encrypter.encrypt(m_data); - m_data += mac; - return *this; -} - -void SshOutgoingPacket::finalize() -{ - setPadding(); - setLengthField(m_data); - m_length = m_data.size() - 4; - qCDebug(sshLog, "Encrypting packet of type %u", m_data.at(TypeOffset)); - encrypt(); - qCDebug(sshLog, "Sending packet of size %d", rawData().count()); - Q_ASSERT(isComplete()); -} - -int SshOutgoingPacket::sizeDivisor() const -{ - return qMax(cipherBlockSize(), 8U); -} - -QByteArray SshOutgoingPacket::encodeNameList(const QList<QByteArray> &list) -{ - QByteArray data; - data.resize(4); - for (int i = 0; i < list.count(); ++i) { - if (i > 0) - data.append(','); - data.append(list.at(i)); - } - AbstractSshPacket::setLengthField(data); - return data; -} - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshoutgoingpacket_p.h b/src/libs/ssh/sshoutgoingpacket_p.h deleted file mode 100644 index b9392279aa9..00000000000 --- a/src/libs/ssh/sshoutgoingpacket_p.h +++ /dev/null @@ -1,117 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sshpacket_p.h" - -#include "sshpseudoterminal.h" - -#include <QStringList> - -namespace QSsh { -namespace Internal { - -class SshEncryptionFacility; - -class SshOutgoingPacket : public AbstractSshPacket -{ -public: - SshOutgoingPacket(const SshEncryptionFacility &encrypter, - const quint32 &seqNr); - - QByteArray generateKeyExchangeInitPacket(); // Returns payload. - void generateKeyDhInitPacket(const Botan::BigInt &e); - void generateKeyEcdhInitPacket(const QByteArray &clientQ); - void generateNewKeysPacket(); - void generateDisconnectPacket(SshErrorCode reason, - const QByteArray &reasonString); - void generateMsgUnimplementedPacket(quint32 serverSeqNr); - void generateUserAuthServiceRequestPacket(); - void generateUserAuthByPasswordRequestPacket(const QByteArray &user, - const QByteArray &service, const QByteArray &pwd); - void generateUserAuthByPublicKeyRequestPacket(const QByteArray &user, - const QByteArray &service, const QByteArray &key, const QByteArray &signature); - void generateQueryPublicKeyPacket(const QByteArray &user, const QByteArray &service, - const QByteArray &publicKey); - void generateUserAuthByKeyboardInteractiveRequestPacket(const QByteArray &user, - const QByteArray &service); - void generateUserAuthInfoResponsePacket(const QStringList &responses); - void generateRequestFailurePacket(); - void generateIgnorePacket(); - void generateInvalidMessagePacket(); - void generateSessionPacket(quint32 channelId, quint32 windowSize, - quint32 maxPacketSize); - void generateDirectTcpIpPacket(quint32 channelId, quint32 windowSize, - quint32 maxPacketSize, const QByteArray &remoteHost, quint32 remotePort, - const QByteArray &localIpAddress, quint32 localPort); - void generateTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort); - void generateCancelTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort); - void generateEnvPacket(quint32 remoteChannel, const QByteArray &var, - const QByteArray &value); - void generateX11ForwardingPacket(quint32 remoteChannel, const QByteArray &protocol, - const QByteArray &cookie, quint32 screenNumber); - void generatePtyRequestPacket(quint32 remoteChannel, - const SshPseudoTerminal &terminal); - void generateExecPacket(quint32 remoteChannel, const QByteArray &command); - void generateShellPacket(quint32 remoteChannel); - void generateSftpPacket(quint32 remoteChannel); - void generateWindowAdjustPacket(quint32 remoteChannel, quint32 bytesToAdd); - void generateChannelDataPacket(quint32 remoteChannel, - const QByteArray &data); - void generateChannelSignalPacket(quint32 remoteChannel, - const QByteArray &signalName); - void generateChannelEofPacket(quint32 remoteChannel); - void generateChannelClosePacket(quint32 remoteChannel); - void generateChannelOpenConfirmationPacket(quint32 remoteChannel, quint32 localChannel, - quint32 localWindowSize, quint32 maxPackeSize); - void generateChannelOpenFailurePacket(quint32 remoteChannel, quint32 reason, - const QByteArray &reasonString); - -private: - virtual quint32 cipherBlockSize() const; - virtual quint32 macLength() const; - - static QByteArray encodeNameList(const QList<QByteArray> &list); - - void generateServiceRequest(const QByteArray &service); - - SshOutgoingPacket &init(SshPacketType type); - SshOutgoingPacket &setPadding(); - SshOutgoingPacket &encrypt(); - void finalize(); - - SshOutgoingPacket &appendInt(quint32 val); - SshOutgoingPacket &appendString(const QByteArray &string); - SshOutgoingPacket &appendMpInt(const Botan::BigInt &number); - SshOutgoingPacket &appendBool(bool b); - int sizeDivisor() const; - - const SshEncryptionFacility &m_encrypter; - const quint32 &m_seqNr; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshpacket.cpp b/src/libs/ssh/sshpacket.cpp deleted file mode 100644 index 1b35037fb0e..00000000000 --- a/src/libs/ssh/sshpacket.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshpacket_p.h" - -#include "sshcapabilities_p.h" -#include "sshcryptofacility_p.h" -#include "sshexception_p.h" -#include "sshlogging_p.h" -#include "sshpacketparser_p.h" - -#include <QDebug> - -#include <cctype> - -namespace QSsh { -namespace Internal { - -const quint32 AbstractSshPacket::PaddingLengthOffset = 4; -const quint32 AbstractSshPacket::PayloadOffset = PaddingLengthOffset + 1; -const quint32 AbstractSshPacket::TypeOffset = PayloadOffset; -const quint32 AbstractSshPacket::MinPaddingLength = 4; - -static void printByteArray(const QByteArray &data) -{ - qCDebug(sshLog, "%s", data.toHex().constData()); -} - - -AbstractSshPacket::AbstractSshPacket() : m_length(0) { } -AbstractSshPacket::~AbstractSshPacket() {} - -bool AbstractSshPacket::isComplete() const -{ - if (currentDataSize() < minPacketSize()) - return false; - return 4 + length() + macLength() == currentDataSize(); -} - -void AbstractSshPacket::clear() -{ - m_data.clear(); - m_length = 0; -} - -SshPacketType AbstractSshPacket::type() const -{ - Q_ASSERT(isComplete()); - return static_cast<SshPacketType>(m_data.at(TypeOffset)); -} - -QByteArray AbstractSshPacket::payLoad() const -{ - return QByteArray(m_data.constData() + PayloadOffset, - length() - paddingLength() - 1); -} - -void AbstractSshPacket::printRawBytes() const -{ - printByteArray(m_data); -} - -QByteArray AbstractSshPacket::encodeString(const QByteArray &string) -{ - QByteArray data; - data.resize(4); - data += string; - setLengthField(data); - return data; -} - -QByteArray AbstractSshPacket::encodeMpInt(const Botan::BigInt &number) -{ - if (number.is_zero()) - return QByteArray(4, 0); - - int stringLength = static_cast<int>(number.bytes()); - const bool positiveAndMsbSet = number.sign() == Botan::BigInt::Positive - && (number.byte_at(stringLength - 1) & 0x80); - if (positiveAndMsbSet) - ++stringLength; - QByteArray data; - data.resize(4 + stringLength); - int pos = 4; - if (positiveAndMsbSet) - data[pos++] = '\0'; - number.binary_encode(reinterpret_cast<Botan::byte *>(data.data()) + pos); - setLengthField(data); - return data; -} - -int AbstractSshPacket::paddingLength() const -{ - return m_data[PaddingLengthOffset]; -} - -quint32 AbstractSshPacket::length() const -{ - //Q_ASSERT(currentDataSize() >= minPacketSize()); - if (m_length == 0) - calculateLength(); - return m_length; -} - -void AbstractSshPacket::calculateLength() const -{ - m_length = SshPacketParser::asUint32(m_data, static_cast<quint32>(0)); -} - -QByteArray AbstractSshPacket::generateMac(const SshAbstractCryptoFacility &crypt, - quint32 seqNr) const -{ - const quint32 seqNrBe = qToBigEndian(seqNr); - QByteArray data(reinterpret_cast<const char *>(&seqNrBe), sizeof seqNrBe); - data += QByteArray(m_data.constData(), length() + 4); - return crypt.generateMac(data, data.size()); -} - -quint32 AbstractSshPacket::minPacketSize() const -{ - return qMax<quint32>(cipherBlockSize(), 16) + macLength(); -} - -void AbstractSshPacket::setLengthField(QByteArray &data) -{ - const quint32 length = qToBigEndian(data.size() - 4); - data.replace(0, 4, reinterpret_cast<const char *>(&length), 4); -} - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshpacket_p.h b/src/libs/ssh/sshpacket_p.h deleted file mode 100644 index 4a94a432d1f..00000000000 --- a/src/libs/ssh/sshpacket_p.h +++ /dev/null @@ -1,147 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sshexception_p.h" - -#include <QtEndian> -#include <QByteArray> -#include <QList> - -namespace Botan { class BigInt; } - -namespace QSsh { -namespace Internal { - -enum SshPacketType { - SSH_MSG_DISCONNECT = 1, - SSH_MSG_IGNORE = 2, - SSH_MSG_UNIMPLEMENTED = 3, - SSH_MSG_DEBUG = 4, - SSH_MSG_SERVICE_REQUEST = 5, - SSH_MSG_SERVICE_ACCEPT = 6, - - SSH_MSG_KEXINIT = 20, - SSH_MSG_NEWKEYS = 21, - SSH_MSG_KEXDH_INIT = 30, - SSH_MSG_KEX_ECDH_INIT = 30, - SSH_MSG_KEXDH_REPLY = 31, - SSH_MSG_KEX_ECDH_REPLY = 31, - - SSH_MSG_USERAUTH_REQUEST = 50, - SSH_MSG_USERAUTH_FAILURE = 51, - SSH_MSG_USERAUTH_SUCCESS = 52, - SSH_MSG_USERAUTH_BANNER = 53, - SSH_MSG_USERAUTH_PK_OK = 60, - SSH_MSG_USERAUTH_PASSWD_CHANGEREQ = 60, - SSH_MSG_USERAUTH_INFO_REQUEST = 60, - SSH_MSG_USERAUTH_INFO_RESPONSE = 61, - - SSH_MSG_GLOBAL_REQUEST = 80, - SSH_MSG_REQUEST_SUCCESS = 81, - SSH_MSG_REQUEST_FAILURE = 82, - - // TODO: We currently take no precautions against sending these messages - // during a key re-exchange, which is not allowed. - SSH_MSG_CHANNEL_OPEN = 90, - SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91, - SSH_MSG_CHANNEL_OPEN_FAILURE = 92, - SSH_MSG_CHANNEL_WINDOW_ADJUST = 93, - SSH_MSG_CHANNEL_DATA = 94, - SSH_MSG_CHANNEL_EXTENDED_DATA = 95, - SSH_MSG_CHANNEL_EOF = 96, - SSH_MSG_CHANNEL_CLOSE = 97, - SSH_MSG_CHANNEL_REQUEST = 98, - SSH_MSG_CHANNEL_SUCCESS = 99, - SSH_MSG_CHANNEL_FAILURE = 100, - - // Not completely safe, since the server may actually understand this - // message type as an extension. Switch to a different value in that case - // (between 128 and 191). - SSH_MSG_INVALID = 128 -}; - -enum SshOpenFailureType { - SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1, - SSH_OPEN_CONNECT_FAILED = 2, - SSH_OPEN_UNKNOWN_CHANNEL_TYPE = 3, - SSH_OPEN_RESOURCE_SHORTAGE = 4 -}; - -enum SshExtendedDataType { SSH_EXTENDED_DATA_STDERR = 1 }; - -class SshAbstractCryptoFacility; - -class AbstractSshPacket -{ -public: - virtual ~AbstractSshPacket(); - - void clear(); - bool isComplete() const; - SshPacketType type() const; - - static QByteArray encodeString(const QByteArray &string); - static QByteArray encodeMpInt(const Botan::BigInt &number); - template<typename T> static QByteArray encodeInt(T value) - { - const T valMsb = qToBigEndian(value); - return QByteArray(reinterpret_cast<const char *>(&valMsb), sizeof valMsb); - } - - static void setLengthField(QByteArray &data); - - void printRawBytes() const; // For Debugging. - - const QByteArray &rawData() const { return m_data; } - - QByteArray payLoad() const; - -protected: - AbstractSshPacket(); - - virtual quint32 cipherBlockSize() const = 0; - virtual quint32 macLength() const = 0; - virtual void calculateLength() const; - - quint32 length() const; - int paddingLength() const; - quint32 minPacketSize() const; - quint32 currentDataSize() const { return m_data.size(); } - QByteArray generateMac(const SshAbstractCryptoFacility &crypt, - quint32 seqNr) const; - - static const quint32 PaddingLengthOffset; - static const quint32 PayloadOffset; - static const quint32 TypeOffset; - static const quint32 MinPaddingLength; - - mutable QByteArray m_data; - mutable quint32 m_length; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshpacketparser.cpp b/src/libs/ssh/sshpacketparser.cpp deleted file mode 100644 index 6251f60244f..00000000000 --- a/src/libs/ssh/sshpacketparser.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshpacketparser_p.h" - -#include <cctype> - -namespace QSsh { -namespace Internal { - -namespace { quint32 size(const QByteArray &data) { return data.size(); } } - -QString SshPacketParser::asUserString(const QByteArray &rawString) -{ - QByteArray filteredString; - filteredString.resize(rawString.size()); - for (int i = 0; i < rawString.size(); ++i) { - const char c = rawString.at(i); - filteredString[i] - = std::isprint(c) || c == '\n' || c == '\r' || c == '\t' ? c : '?'; - } - return QString::fromUtf8(filteredString); -} - -bool SshPacketParser::asBool(const QByteArray &data, quint32 offset) -{ - if (size(data) <= offset) - throw SshPacketParseException(); - return data.at(offset); -} - -bool SshPacketParser::asBool(const QByteArray &data, quint32 *offset) -{ - bool b = asBool(data, *offset); - ++(*offset); - return b; -} - - -quint32 SshPacketParser::asUint32(const QByteArray &data, quint32 offset) -{ - if (size(data) < offset + 4) - throw SshPacketParseException(); - const quint32 value = ((data.at(offset) & 0xff) << 24) - + ((data.at(offset + 1) & 0xff) << 16) - + ((data.at(offset + 2) & 0xff) << 8) + (data.at(offset + 3) & 0xff); - return value; -} - -quint32 SshPacketParser::asUint32(const QByteArray &data, quint32 *offset) -{ - const quint32 v = asUint32(data, *offset); - *offset += 4; - return v; -} - -quint64 SshPacketParser::asUint64(const QByteArray &data, quint32 offset) -{ - if (size(data) < offset + 8) - throw SshPacketParseException(); - const quint64 value = (static_cast<quint64>(data.at(offset) & 0xff) << 56) - + (static_cast<quint64>(data.at(offset + 1) & 0xff) << 48) - + (static_cast<quint64>(data.at(offset + 2) & 0xff) << 40) - + (static_cast<quint64>(data.at(offset + 3) & 0xff) << 32) - + ((data.at(offset + 4) & 0xff) << 24) - + ((data.at(offset + 5) & 0xff) << 16) - + ((data.at(offset + 6) & 0xff) << 8) - + (data.at(offset + 7) & 0xff); - return value; -} - -quint64 SshPacketParser::asUint64(const QByteArray &data, quint32 *offset) -{ - const quint64 val = asUint64(data, *offset); - *offset += 8; - return val; -} - -QByteArray SshPacketParser::asString(const QByteArray &data, quint32 offset) -{ - return asString(data, &offset); -} - -QByteArray SshPacketParser::asString(const QByteArray &data, quint32 *offset) -{ - const quint32 length = asUint32(data, offset); - if (size(data) < *offset + length) - throw SshPacketParseException(); - const QByteArray &string = data.mid(*offset, length); - *offset += length; - return string; -} - -QString SshPacketParser::asUserString(const QByteArray &data, quint32 *offset) -{ - return asUserString(asString(data, offset)); -} - -SshNameList SshPacketParser::asNameList(const QByteArray &data, quint32 *offset) -{ - const quint32 length = asUint32(data, offset); - const int listEndPos = *offset + length; - if (data.size() < listEndPos) - throw SshPacketParseException(); - SshNameList names(length + 4); - int nextNameOffset = *offset; - int nextCommaOffset = data.indexOf(',', nextNameOffset); - while (nextNameOffset > 0 && nextNameOffset < listEndPos) { - const int stringEndPos = nextCommaOffset == -1 - || nextCommaOffset > listEndPos ? listEndPos : nextCommaOffset; - names.names << QByteArray(data.constData() + nextNameOffset, - stringEndPos - nextNameOffset); - nextNameOffset = nextCommaOffset + 1; - nextCommaOffset = data.indexOf(',', nextNameOffset); - } - *offset += length; - return names; -} - -Botan::BigInt SshPacketParser::asBigInt(const QByteArray &data, quint32 *offset) -{ - const quint32 length = asUint32(data, offset); - if (length == 0) - return Botan::BigInt(); - const Botan::byte *numberStart - = reinterpret_cast<const Botan::byte *>(data.constData() + *offset); - *offset += length; - return Botan::BigInt::decode(numberStart, length); -} - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshpacketparser_p.h b/src/libs/ssh/sshpacketparser_p.h deleted file mode 100644 index db63f0a1b9c..00000000000 --- a/src/libs/ssh/sshpacketparser_p.h +++ /dev/null @@ -1,75 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include <botan/bigint.h> - -#include <QByteArray> -#include <QList> -#include <QString> - -namespace QSsh { -namespace Internal { - -struct SshNameList -{ - SshNameList() : originalLength(0) {} - SshNameList(quint32 originalLength) : originalLength(originalLength) {} - quint32 originalLength; - QList<QByteArray> names; -}; - -class SshPacketParseException { }; - -// This class's functions try to read a byte array at a certain offset -// as the respective chunk of data as specified in the SSH RFCs. -// If they succeed, they update the offset, so they can easily -// be called in succession by client code. -// For convenience, some have also versions that don't update the offset, -// so they can be called with rvalues if the new value is not needed. -// If they fail, they throw an SshPacketParseException. -class SshPacketParser -{ -public: - static bool asBool(const QByteArray &data, quint32 offset); - static bool asBool(const QByteArray &data, quint32 *offset); - static quint16 asUint16(const QByteArray &data, quint32 offset); - static quint16 asUint16(const QByteArray &data, quint32 *offset); - static quint64 asUint64(const QByteArray &data, quint32 offset); - static quint64 asUint64(const QByteArray &data, quint32 *offset); - static quint32 asUint32(const QByteArray &data, quint32 offset); - static quint32 asUint32(const QByteArray &data, quint32 *offset); - static QByteArray asString(const QByteArray &data, quint32 offset); - static QByteArray asString(const QByteArray &data, quint32 *offset); - static QString asUserString(const QByteArray &data, quint32 *offset); - static SshNameList asNameList(const QByteArray &data, quint32 *offset); - static Botan::BigInt asBigInt(const QByteArray &data, quint32 *offset); - - static QString asUserString(const QByteArray &rawString); -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshx11displayinfo_p.h b/src/libs/ssh/sshprocess.cpp index 48ba05218a6..9e29f0b88d3 100644 --- a/src/libs/ssh/sshx11displayinfo_p.h +++ b/src/libs/ssh/sshprocess.cpp @@ -23,27 +23,47 @@ ** ****************************************************************************/ -#pragma once +#include "sshprocess_p.h" -#include "ssh_global.h" +#include "sshsettings.h" -#include <QString> -#include <QByteArray> +#include <utils/environment.h> + +#ifdef Q_OS_UNIX +#include <sys/types.h> +#include <unistd.h> +#endif namespace QSsh { namespace Internal { -class QSSH_AUTOTEST_EXPORT X11DisplayInfo +SshProcess::SshProcess() +{ + Utils::Environment env = Utils::Environment::systemEnvironment(); + if (SshSettings::askpassFilePath().exists()) + env.set("SSH_ASKPASS", SshSettings::askpassFilePath().toUserOutput()); + setProcessEnvironment(env.toProcessEnvironment()); +} + +SshProcess::~SshProcess() +{ + if (state() == QProcess::NotRunning) + return; + disconnect(); + terminate(); + waitForFinished(1000); + if (state() == QProcess::NotRunning) + return; + kill(); + waitForFinished(1000); +} + +void SshProcess::setupChildProcess() { -public: - QString displayName; - QString hostName; - QByteArray protocol; - QByteArray cookie; - QByteArray randomCookie; - int display = 0; - int screen = 0; -}; +#ifdef Q_OS_UNIX + setsid(); // Otherwise, ssh will ignore SSH_ASKPASS and read from /dev/tty directly. +#endif +} } // namespace Internal } // namespace QSsh diff --git a/src/libs/ssh/sshkeypasswordretriever_p.h b/src/libs/ssh/sshprocess_p.h index 7d7bc76e698..836dc62f96a 100644 --- a/src/libs/ssh/sshkeypasswordretriever_p.h +++ b/src/libs/ssh/sshprocess_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -25,12 +25,20 @@ #pragma once -#include <string> +#include <QProcess> namespace QSsh { namespace Internal { -std::string get_passphrase(); +class SshProcess : public QProcess +{ +public: + SshProcess(); + ~SshProcess() override; + +private: + void setupChildProcess() override; +}; } // namespace Internal } // namespace QSsh diff --git a/src/libs/ssh/sshpseudoterminal.h b/src/libs/ssh/sshpseudoterminal.h deleted file mode 100644 index 827d6449e2d..00000000000 --- a/src/libs/ssh/sshpseudoterminal.h +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "ssh_global.h" - -#include <QByteArray> -#include <QHash> - -namespace QSsh { - -class QSSH_EXPORT SshPseudoTerminal -{ - public: - explicit SshPseudoTerminal(const QByteArray &termType = "vt100", - int rowCount = 24, int columnCount = 80) - : termType(termType), rowCount(rowCount), columnCount(columnCount) {} - - QByteArray termType; - int rowCount; - int columnCount; - - enum Mode { - VINTR = 1, // Interrupt character. - VQUIT = 2, // The quit character (sends SIGQUIT signal on POSIX systems). - VERASE = 3, // Erase the character to left of the cursor. - VKILL = 4, // Kill the current input line. - VEOF = 5, // End-of-file character (sends EOF from the terminal). - VEOL = 6, // End-of-line character in addition to carriage return and/or linefeed. - VEOL2 = 7, // Additional end-of-line character. - VSTART = 8, // Continues paused output (normally control-Q). - VSTOP = 9, // Pauses output (normally control-S). - VSUSP = 10, // Suspends the current program. - VDSUSP = 11, // Another suspend character. - VREPRINT = 12, // Reprints the current input line. - VWERASE = 13, // Erases a word left of cursor. - VLNEXT = 14, // Enter the next character typed literally, even if it is a special character. - VFLUSH = 15, // Character to flush output. - VSWTCH = 16, // Switch to a different shell layer. - VSTATUS = 17, // Prints system status line (load, command, pid, etc). - VDISCARD = 18, // Toggles the flushing of terminal output. - - IGNPAR = 30, // The ignore parity flag. The parameter SHOULD be 0 if this flag is FALSE, and 1 if it is TRUE. - PARMRK = 31, // Mark parity and framing errors. - INPCK = 32, // Enable checking of parity errors. - ISTRIP = 33, // Strip 8th bit off characters. - INLCR = 34, // Map NL into CR on input. - IGNCR = 35, // Ignore CR on input. - ICRNL = 36, // Map CR to NL on input. - IUCLC = 37, // Translate uppercase characters to lowercase. - IXON = 38, // Enable output flow control. - IXANY = 39, // Any char will restart after stop. - IXOFF = 40, // Enable input flow control. - IMAXBEL = 41, // Ring bell on input queue full. - ISIG = 50, // Enable signals INTR, QUIT, [D]SUSP. - ICANON = 51, // Canonicalize input lines. - XCASE = 52, // Enable input and output of uppercase characters by preceding their lowercase equivalents with "\". - ECHO = 53, // Enable echoing. - ECHOE = 54, // Visually erase chars. - ECHOK = 55, // Kill character discards current line. - ECHONL = 56, // Echo NL even if ECHO is off. - NOFLSH = 57, // Don't flush after interrupt. - TOSTOP = 58, // Stop background jobs from output. - IEXTEN = 59, // Enable extensions. - ECHOCTL = 60, // Echo control characters as ^(Char). - ECHOKE = 61, // Visual erase for line kill. - PENDIN = 62, // Retype pending input. - OPOST = 70, // Enable output processing. - OLCUC = 71, // Convert lowercase to uppercase. - ONLCR = 72, // Map NL to CR-NL. - OCRNL = 73, // Translate carriage return to newline (output). - ONOCR = 74, // Translate newline to carriage return-newline (output). - ONLRET = 75, // Newline performs a carriage return (output). - CS7 = 90, // 7 bit mode. - CS8 = 91, // 8 bit mode. - PARENB = 92, // Parity enable. - PARODD = 93, // Odd parity, else even. - - TTY_OP_ISPEED = 128, // Specifies the input baud rate in bits per second. - TTY_OP_OSPEED = 129 // Specifies the output baud rate in bits per second. - }; - - typedef QHash<Mode, quint32> ModeMap; - ModeMap modes; -}; - -} // namespace QSsh diff --git a/src/libs/ssh/sshremoteprocess.cpp b/src/libs/ssh/sshremoteprocess.cpp index f4c43bdce24..22e28131505 100644 --- a/src/libs/ssh/sshremoteprocess.cpp +++ b/src/libs/ssh/sshremoteprocess.cpp @@ -24,13 +24,12 @@ ****************************************************************************/ #include "sshremoteprocess.h" -#include "sshremoteprocess_p.h" -#include "ssh_global.h" -#include "sshincomingpacket_p.h" #include "sshlogging_p.h" -#include "sshsendfacility_p.h" -#include "sshx11displayinfo_p.h" +#include "sshsettings.h" + +#include <utils/fileutils.h> +#include <utils/qtcassert.h> #include <QTimer> @@ -47,362 +46,78 @@ The process is started via the start() member function. If the process needs a pseudo terminal, you can request one via requestTerminal() before calling start(). - Note that this class does not support QIODevice's waitFor*() functions, i.e. it has - no synchronous mode. */ namespace QSsh { +using namespace Internal; -const struct { - SshRemoteProcess::Signal signalEnum; - const char * const signalString; -} signalMap[] = { - {SshRemoteProcess::AbrtSignal, "ABRT"}, {SshRemoteProcess::AlrmSignal, "ALRM"}, - {SshRemoteProcess::FpeSignal, "FPE"}, {SshRemoteProcess::HupSignal, "HUP"}, - {SshRemoteProcess::IllSignal, "ILL"}, {SshRemoteProcess::IntSignal, "INT"}, - {SshRemoteProcess::KillSignal, "KILL"}, {SshRemoteProcess::PipeSignal, "PIPE"}, - {SshRemoteProcess::QuitSignal, "QUIT"}, {SshRemoteProcess::SegvSignal, "SEGV"}, - {SshRemoteProcess::TermSignal, "TERM"}, {SshRemoteProcess::Usr1Signal, "USR1"}, - {SshRemoteProcess::Usr2Signal, "USR2"} -}; - -SshRemoteProcess::SshRemoteProcess(const QByteArray &command, quint32 channelId, - Internal::SshSendFacility &sendFacility) - : d(new Internal::SshRemoteProcessPrivate(command, channelId, sendFacility, this)) +struct SshRemoteProcess::SshRemoteProcessPrivate { - init(); -} + QByteArray remoteCommand; + QStringList connectionArgs; + QString displayName; + bool useTerminal = false; +}; -SshRemoteProcess::SshRemoteProcess(quint32 channelId, Internal::SshSendFacility &sendFacility) - : d(new Internal::SshRemoteProcessPrivate(channelId, sendFacility, this)) -{ - init(); +SshRemoteProcess::SshRemoteProcess(const QByteArray &command, const QStringList &connectionArgs) + : d(new SshRemoteProcessPrivate) +{ + d->remoteCommand = command; + d->connectionArgs = connectionArgs; + + connect(this, + static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), + [this] { + emit done(exitStatus() == QProcess::NormalExit ? QString() : errorString()); + }); + connect(this, &QProcess::errorOccurred, [this](QProcess::ProcessError error) { + if (error == QProcess::FailedToStart) + emit done(errorString()); + }); +} + +void SshRemoteProcess::doStart() +{ + QTC_ASSERT(!isRunning(), return); + QStringList args = QStringList("-q") << d->connectionArgs; + if (d->useTerminal) + args.prepend("-tt"); + if (!d->displayName.isEmpty()) { + args.prepend("-X"); + QProcessEnvironment env = processEnvironment(); + env.insert("DISPLAY", d->displayName); + setProcessEnvironment(env); + } + if (!d->remoteCommand.isEmpty()) + args << QLatin1String(d->remoteCommand); + qCDebug(sshLog) << "starting remote process:" << SshSettings::sshFilePath().toUserOutput() + << args; + QProcess::start(SshSettings::sshFilePath().toString(), args); } SshRemoteProcess::~SshRemoteProcess() { - QSSH_ASSERT(d->channelState() != Internal::AbstractSshChannel::SessionEstablished); - close(); delete d; } -bool SshRemoteProcess::atEnd() const -{ - return QIODevice::atEnd() && d->data().isEmpty(); -} - -qint64 SshRemoteProcess::bytesAvailable() const -{ - return QIODevice::bytesAvailable() + d->data().count(); -} - -bool SshRemoteProcess::canReadLine() const -{ - return QIODevice::canReadLine() || d->data().contains('\n'); -} - -QByteArray SshRemoteProcess::readAllStandardOutput() +void SshRemoteProcess::requestTerminal() { - return readAllFromChannel(QProcess::StandardOutput); -} - -QByteArray SshRemoteProcess::readAllStandardError() -{ - return readAllFromChannel(QProcess::StandardError); -} - -QByteArray SshRemoteProcess::readAllFromChannel(QProcess::ProcessChannel channel) -{ - const QProcess::ProcessChannel currentReadChannel = readChannel(); - setReadChannel(channel); - const QByteArray &data = readAll(); - setReadChannel(currentReadChannel); - return data; -} - -void SshRemoteProcess::close() -{ - d->closeChannel(); - QIODevice::close(); -} - -qint64 SshRemoteProcess::readData(char *data, qint64 maxlen) -{ - const qint64 bytesRead = qMin(qint64(d->data().count()), maxlen); - memcpy(data, d->data().constData(), bytesRead); - d->data().remove(0, bytesRead); - return bytesRead; -} - -qint64 SshRemoteProcess::writeData(const char *data, qint64 len) -{ - if (isRunning()) { - d->sendData(QByteArray(data, len)); - return len; - } - return 0; -} - -QProcess::ProcessChannel SshRemoteProcess::readChannel() const -{ - return d->m_readChannel; -} - -void SshRemoteProcess::setReadChannel(QProcess::ProcessChannel channel) -{ - d->m_readChannel = channel; -} - -void SshRemoteProcess::init() -{ - connect(d, &Internal::SshRemoteProcessPrivate::started, - this, &SshRemoteProcess::started, Qt::QueuedConnection); - connect(d, &Internal::SshRemoteProcessPrivate::readyReadStandardOutput, - this, &SshRemoteProcess::readyReadStandardOutput, Qt::QueuedConnection); - connect(d, &Internal::SshRemoteProcessPrivate::readyRead, - this, &SshRemoteProcess::readyRead, Qt::QueuedConnection); - connect(d, &Internal::SshRemoteProcessPrivate::readyReadStandardError, - this, &SshRemoteProcess::readyReadStandardError, Qt::QueuedConnection); - connect(d, &Internal::SshRemoteProcessPrivate::closed, - this, &SshRemoteProcess::closed, Qt::QueuedConnection); - connect(d, &Internal::SshRemoteProcessPrivate::eof, - this, &SshRemoteProcess::readChannelFinished, Qt::QueuedConnection); -} - -void SshRemoteProcess::addToEnvironment(const QByteArray &var, const QByteArray &value) -{ - if (d->channelState() == Internal::SshRemoteProcessPrivate::Inactive) - d->m_env << qMakePair(var, value); // Cached locally and sent on start() -} - -void SshRemoteProcess::clearEnvironment() -{ - d->m_env.clear(); -} - -void SshRemoteProcess::requestTerminal(const SshPseudoTerminal &terminal) -{ - QSSH_ASSERT_AND_RETURN(d->channelState() == Internal::SshRemoteProcessPrivate::Inactive); - d->m_useTerminal = true; - d->m_terminal = terminal; + d->useTerminal = true; } void SshRemoteProcess::requestX11Forwarding(const QString &displayName) { - QSSH_ASSERT_AND_RETURN(d->channelState() == Internal::SshRemoteProcessPrivate::Inactive); - d->m_x11DisplayName = displayName; + d->displayName = displayName; } void SshRemoteProcess::start() { - if (d->channelState() == Internal::SshRemoteProcessPrivate::Inactive) { - qCDebug(Internal::sshLog, "process start requested, channel id = %u", d->localChannelId()); - QIODevice::open(QIODevice::ReadWrite); - d->requestSessionStart(); - } -} - -void SshRemoteProcess::sendSignal(Signal signal) -{ - try { - if (isRunning()) { - const char *signalString = 0; - for (size_t i = 0; i < sizeof signalMap/sizeof *signalMap && !signalString; ++i) { - if (signalMap[i].signalEnum == signal) - signalString = signalMap[i].signalString; - } - QSSH_ASSERT_AND_RETURN(signalString); - d->m_sendFacility.sendChannelSignalPacket(d->remoteChannel(), signalString); - } - } catch (const std::exception &e) { - setErrorString(QString::fromLatin1(e.what())); - d->closeChannel(); - } + QTimer::singleShot(0, this, &SshRemoteProcess::doStart); } bool SshRemoteProcess::isRunning() const { - return d->m_procState == Internal::SshRemoteProcessPrivate::Running; -} - -int SshRemoteProcess::exitCode() const { return d->m_exitCode; } - -SshRemoteProcess::Signal SshRemoteProcess::exitSignal() const -{ - return static_cast<SshRemoteProcess::Signal>(d->m_signal); -} - -namespace Internal { - -void SshRemoteProcessPrivate::failToStart(const QString &reason) -{ - if (m_procState != NotYetStarted) - return; - m_proc->setErrorString(reason); - setProcState(StartFailed); -} - -SshRemoteProcessPrivate::SshRemoteProcessPrivate(const QByteArray &command, - quint32 channelId, SshSendFacility &sendFacility, SshRemoteProcess *proc) - : AbstractSshChannel(channelId, sendFacility), - m_command(command), - m_isShell(false), - m_useTerminal(false), - m_proc(proc) -{ - init(); -} - -SshRemoteProcessPrivate::SshRemoteProcessPrivate(quint32 channelId, SshSendFacility &sendFacility, - SshRemoteProcess *proc) - : AbstractSshChannel(channelId, sendFacility), - m_isShell(true), - m_useTerminal(true), - m_proc(proc) -{ - init(); -} - -void SshRemoteProcessPrivate::init() -{ - m_procState = NotYetStarted; - m_wasRunning = false; - m_exitCode = 0; - m_readChannel = QProcess::StandardOutput; - m_signal = SshRemoteProcess::NoSignal; -} - -void SshRemoteProcessPrivate::setProcState(ProcessState newState) -{ - qCDebug(sshLog, "channel: old state = %d,new state = %d", m_procState, newState); - m_procState = newState; - if (newState == StartFailed) { - emit closed(SshRemoteProcess::FailedToStart); - } else if (newState == Running) { - m_wasRunning = true; - emit started(); - } -} - -QByteArray &SshRemoteProcessPrivate::data() -{ - return m_readChannel == QProcess::StandardOutput ? m_stdout : m_stderr; -} - -void SshRemoteProcessPrivate::closeHook() -{ - if (m_wasRunning) { - if (m_signal != SshRemoteProcess::NoSignal) - emit closed(SshRemoteProcess::CrashExit); - else - emit closed(SshRemoteProcess::NormalExit); - } -} - -void SshRemoteProcessPrivate::handleOpenSuccessInternal() -{ - if (m_x11DisplayName.isEmpty()) - startProcess(X11DisplayInfo()); - else - emit x11ForwardingRequested(m_x11DisplayName); -} - -void SshRemoteProcessPrivate::startProcess(const X11DisplayInfo &displayInfo) -{ - if (m_procState != NotYetStarted) - return; - - foreach (const EnvVar &envVar, m_env) { - m_sendFacility.sendEnvPacket(remoteChannel(), envVar.first, - envVar.second); - } - - if (!m_x11DisplayName.isEmpty()) { - m_sendFacility.sendX11ForwardingPacket(remoteChannel(), displayInfo.protocol, - displayInfo.randomCookie.toHex(), 0); - } - - if (m_useTerminal) - m_sendFacility.sendPtyRequestPacket(remoteChannel(), m_terminal); - - if (m_isShell) - m_sendFacility.sendShellPacket(remoteChannel()); - else - m_sendFacility.sendExecPacket(remoteChannel(), m_command); - setProcState(ExecRequested); - m_timeoutTimer.start(ReplyTimeout); -} - -void SshRemoteProcessPrivate::handleOpenFailureInternal(const QString &reason) -{ - failToStart(reason); -} - -void SshRemoteProcessPrivate::handleChannelSuccess() -{ - if (m_procState != ExecRequested) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_MSG_CHANNEL_SUCCESS message."); - } - m_timeoutTimer.stop(); - setProcState(Running); -} - -void SshRemoteProcessPrivate::handleChannelFailure() -{ - if (m_procState != ExecRequested) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_MSG_CHANNEL_FAILURE message."); - } - m_timeoutTimer.stop(); - setProcState(StartFailed); - closeChannel(); -} - -void SshRemoteProcessPrivate::handleChannelDataInternal(const QByteArray &data) -{ - m_stdout += data; - emit readyReadStandardOutput(); - if (m_readChannel == QProcess::StandardOutput) - emit readyRead(); -} - -void SshRemoteProcessPrivate::handleChannelExtendedDataInternal(quint32 type, - const QByteArray &data) -{ - if (type != SSH_EXTENDED_DATA_STDERR) { - qCWarning(sshLog, "Unknown extended data type %u", type); - } else { - m_stderr += data; - emit readyReadStandardError(); - if (m_readChannel == QProcess::StandardError) - emit readyRead(); - } -} - -void SshRemoteProcessPrivate::handleExitStatus(const SshChannelExitStatus &exitStatus) -{ - qCDebug(sshLog, "Process exiting with exit code %d", exitStatus.exitStatus); - m_exitCode = exitStatus.exitStatus; - m_procState = Exited; -} - -void SshRemoteProcessPrivate::handleExitSignal(const SshChannelExitSignal &signal) -{ - qCDebug(sshLog, "Exit due to signal %s", signal.signal.data()); - - for (size_t i = 0; i < sizeof signalMap/sizeof *signalMap; ++i) { - if (signalMap[i].signalString == signal.signal) { - m_signal = signalMap[i].signalEnum; - m_procState = Exited; - m_proc->setErrorString(tr("Process killed by signal")); - return; - } - } - - throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, "Invalid signal", - tr("Server sent invalid signal \"%1\"").arg(QString::fromUtf8(signal.signal))); + return state() == QProcess::Running; } -} // namespace Internal } // namespace QSsh diff --git a/src/libs/ssh/sshremoteprocess.h b/src/libs/ssh/sshremoteprocess.h index 85f80182274..958847fcd81 100644 --- a/src/libs/ssh/sshremoteprocess.h +++ b/src/libs/ssh/sshremoteprocess.h @@ -26,97 +26,40 @@ #pragma once #include "ssh_global.h" +#include "sshprocess_p.h" -#include <QProcess> -#include <QSharedPointer> +#include <QStringList> QT_BEGIN_NAMESPACE class QByteArray; QT_END_NAMESPACE namespace QSsh { -class SshPseudoTerminal; -namespace Internal { -class SshChannelManager; -class SshRemoteProcessPrivate; -class SshSendFacility; -} // namespace Internal +class SshConnection; -// TODO: ProcessChannel -class QSSH_EXPORT SshRemoteProcess : public QIODevice +class QSSH_EXPORT SshRemoteProcess : public Internal::SshProcess { Q_OBJECT - friend class Internal::SshChannelManager; - friend class Internal::SshRemoteProcessPrivate; - + friend class SshConnection; public: - typedef QSharedPointer<SshRemoteProcess> Ptr; - enum ExitStatus { FailedToStart, CrashExit, NormalExit }; - enum Signal { - AbrtSignal, AlrmSignal, FpeSignal, HupSignal, IllSignal, IntSignal, KillSignal, PipeSignal, - QuitSignal, SegvSignal, TermSignal, Usr1Signal, Usr2Signal, NoSignal - }; - ~SshRemoteProcess(); - // QIODevice stuff - bool atEnd() const; - qint64 bytesAvailable() const; - bool canReadLine() const; - void close(); - bool isSequential() const { return true; } - - QProcess::ProcessChannel readChannel() const; - void setReadChannel(QProcess::ProcessChannel channel); - - /* - * Note that this is of limited value in practice, because servers are - * usually configured to ignore such requests for security reasons. - */ - void addToEnvironment(const QByteArray &var, const QByteArray &value); - void clearEnvironment(); - - void requestTerminal(const SshPseudoTerminal &terminal); + void requestTerminal(); void requestX11Forwarding(const QString &displayName); void start(); bool isRunning() const; - int exitCode() const; - Signal exitSignal() const; - - QByteArray readAllStandardOutput(); - QByteArray readAllStandardError(); - - // Note: This is ignored by the OpenSSH server. - void sendSignal(Signal signal); - void kill() { sendSignal(KillSignal); } signals: - void started(); - - void readyReadStandardOutput(); - void readyReadStandardError(); - - /* - * Parameter is of type ExitStatus, but we use int because of - * signal/slot awkwardness (full namespace required). - */ - void closed(int exitStatus); + void done(const QString &error); private: - SshRemoteProcess(const QByteArray &command, quint32 channelId, - Internal::SshSendFacility &sendFacility); - SshRemoteProcess(quint32 channelId, Internal::SshSendFacility &sendFacility); - - // QIODevice stuff - qint64 readData(char *data, qint64 maxlen); - qint64 writeData(const char *data, qint64 len); - - void init(); - QByteArray readAllFromChannel(QProcess::ProcessChannel channel); + SshRemoteProcess(const QByteArray &command, const QStringList &connectionArgs); + void doStart(); - Internal::SshRemoteProcessPrivate *d; + struct SshRemoteProcessPrivate; + SshRemoteProcessPrivate * const d; }; } // namespace QSsh diff --git a/src/libs/ssh/sshremoteprocess_p.h b/src/libs/ssh/sshremoteprocess_p.h deleted file mode 100644 index 459da8c593a..00000000000 --- a/src/libs/ssh/sshremoteprocess_p.h +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sshpseudoterminal.h" - -#include "sshchannel_p.h" - -#include <QList> -#include <QPair> -#include <QProcess> - -namespace QSsh { -class SshRemoteProcess; - -namespace Internal { -class SshSendFacility; -class X11DisplayInfo; - -class SshRemoteProcessPrivate : public AbstractSshChannel -{ - Q_OBJECT - friend class QSsh::SshRemoteProcess; -public: - enum ProcessState { - NotYetStarted, ExecRequested, StartFailed, Running, Exited - }; - - void failToStart(const QString &reason); - void startProcess(const X11DisplayInfo &displayInfo); - -signals: - void started(); - void readyRead(); - void readyReadStandardOutput(); - void readyReadStandardError(); - void closed(int exitStatus); - void x11ForwardingRequested(const QString &display); - -private: - SshRemoteProcessPrivate(const QByteArray &command, quint32 channelId, - SshSendFacility &sendFacility, SshRemoteProcess *proc); - SshRemoteProcessPrivate(quint32 channelId, SshSendFacility &sendFacility, - SshRemoteProcess *proc); - - virtual void handleChannelSuccess(); - virtual void handleChannelFailure(); - - virtual void handleOpenSuccessInternal(); - virtual void handleOpenFailureInternal(const QString &reason); - virtual void handleChannelDataInternal(const QByteArray &data); - virtual void handleChannelExtendedDataInternal(quint32 type, - const QByteArray &data); - virtual void handleExitStatus(const SshChannelExitStatus &exitStatus); - virtual void handleExitSignal(const SshChannelExitSignal &signal); - - virtual void closeHook(); - - void init(); - void setProcState(ProcessState newState); - QByteArray &data(); - - QProcess::ProcessChannel m_readChannel; - - ProcessState m_procState; - bool m_wasRunning; - int m_signal; - int m_exitCode; - - const QByteArray m_command; - const bool m_isShell; - - typedef QPair<QByteArray, QByteArray> EnvVar; - QList<EnvVar> m_env; - bool m_useTerminal; - SshPseudoTerminal m_terminal; - - QString m_x11DisplayName; - - QByteArray m_stdout; - QByteArray m_stderr; - - SshRemoteProcess *m_proc; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshremoteprocessrunner.cpp b/src/libs/ssh/sshremoteprocessrunner.cpp index b3f55257e72..11b75f6a254 100644 --- a/src/libs/ssh/sshremoteprocessrunner.cpp +++ b/src/libs/ssh/sshremoteprocessrunner.cpp @@ -26,8 +26,8 @@ #include "sshremoteprocessrunner.h" #include "sshconnectionmanager.h" -#include "sshpseudoterminal.h" +#include <utils/qtcassert.h> /*! \class QSsh::SshRemoteProcessRunner @@ -47,15 +47,12 @@ class SshRemoteProcessRunnerPrivate public: SshRemoteProcessRunnerPrivate() : m_state(Inactive) {} - SshRemoteProcess::Ptr m_process; + SshRemoteProcessPtr m_process; SshConnection *m_connection; bool m_runInTerminal; - SshPseudoTerminal m_terminal; QByteArray m_command; - QSsh::SshError m_lastConnectionError; QString m_lastConnectionErrorString; - SshRemoteProcess::ExitStatus m_exitStatus; - SshRemoteProcess::Signal m_exitSignal; + QProcess::ExitStatus m_exitStatus; QByteArray m_stdout; QByteArray m_stderr; int m_exitCode; @@ -82,16 +79,15 @@ SshRemoteProcessRunner::~SshRemoteProcessRunner() void SshRemoteProcessRunner::run(const QByteArray &command, const SshConnectionParameters &sshParams) { - QSSH_ASSERT_AND_RETURN(d->m_state == Inactive); + QTC_ASSERT(d->m_state == Inactive, return); d->m_runInTerminal = false; runInternal(command, sshParams); } void SshRemoteProcessRunner::runInTerminal(const QByteArray &command, - const SshPseudoTerminal &terminal, const SshConnectionParameters &sshParams) + const SshConnectionParameters &sshParams) { - d->m_terminal = terminal; d->m_runInTerminal = true; runInternal(command, sshParams); } @@ -101,14 +97,12 @@ void SshRemoteProcessRunner::runInternal(const QByteArray &command, { setState(Connecting); - d->m_lastConnectionError = SshNoError; d->m_lastConnectionErrorString.clear(); d->m_processErrorString.clear(); - d->m_exitSignal = SshRemoteProcess::NoSignal; d->m_exitCode = -1; d->m_command = command; d->m_connection = QSsh::acquireConnection(sshParams); - connect(d->m_connection, &SshConnection::error, + connect(d->m_connection, &SshConnection::errorOccurred, this, &SshRemoteProcessRunner::handleConnectionError); connect(d->m_connection, &SshConnection::disconnected, this, &SshRemoteProcessRunner::handleDisconnected); @@ -123,26 +117,25 @@ void SshRemoteProcessRunner::runInternal(const QByteArray &command, void SshRemoteProcessRunner::handleConnected() { - QSSH_ASSERT_AND_RETURN(d->m_state == Connecting); + QTC_ASSERT(d->m_state == Connecting, return); setState(Connected); d->m_process = d->m_connection->createRemoteProcess(d->m_command); - connect(d->m_process.data(), &SshRemoteProcess::started, + connect(d->m_process.get(), &SshRemoteProcess::started, this, &SshRemoteProcessRunner::handleProcessStarted); - connect(d->m_process.data(), &SshRemoteProcess::closed, + connect(d->m_process.get(), &SshRemoteProcess::done, this, &SshRemoteProcessRunner::handleProcessFinished); - connect(d->m_process.data(), &SshRemoteProcess::readyReadStandardOutput, + connect(d->m_process.get(), &SshRemoteProcess::readyReadStandardOutput, this, &SshRemoteProcessRunner::handleStdout); - connect(d->m_process.data(), &SshRemoteProcess::readyReadStandardError, + connect(d->m_process.get(), &SshRemoteProcess::readyReadStandardError, this, &SshRemoteProcessRunner::handleStderr); if (d->m_runInTerminal) - d->m_process->requestTerminal(d->m_terminal); + d->m_process->requestTerminal(); d->m_process->start(); } -void SshRemoteProcessRunner::handleConnectionError(QSsh::SshError error) +void SshRemoteProcessRunner::handleConnectionError() { - d->m_lastConnectionError = error; d->m_lastConnectionErrorString = d->m_connection->errorString(); handleDisconnected(); emit connectionError(); @@ -150,40 +143,26 @@ void SshRemoteProcessRunner::handleConnectionError(QSsh::SshError error) void SshRemoteProcessRunner::handleDisconnected() { - QSSH_ASSERT_AND_RETURN(d->m_state == Connecting || d->m_state == Connected - || d->m_state == ProcessRunning); + QTC_ASSERT(d->m_state == Connecting || d->m_state == Connected || d->m_state == ProcessRunning, + return); setState(Inactive); } void SshRemoteProcessRunner::handleProcessStarted() { - QSSH_ASSERT_AND_RETURN(d->m_state == Connected); + QTC_ASSERT(d->m_state == Connected, return); setState(ProcessRunning); emit processStarted(); } -void SshRemoteProcessRunner::handleProcessFinished(int exitStatus) +void SshRemoteProcessRunner::handleProcessFinished(const QString &error) { - d->m_exitStatus = static_cast<SshRemoteProcess::ExitStatus>(exitStatus); - switch (d->m_exitStatus) { - case SshRemoteProcess::FailedToStart: - QSSH_ASSERT_AND_RETURN(d->m_state == Connected); - break; - case SshRemoteProcess::CrashExit: - QSSH_ASSERT_AND_RETURN(d->m_state == ProcessRunning); - d->m_exitSignal = d->m_process->exitSignal(); - break; - case SshRemoteProcess::NormalExit: - QSSH_ASSERT_AND_RETURN(d->m_state == ProcessRunning); - d->m_exitCode = d->m_process->exitCode(); - break; - default: - Q_ASSERT_X(false, Q_FUNC_INFO, "Impossible exit status."); - } - d->m_processErrorString = d->m_process->errorString(); + d->m_exitStatus = d->m_process->exitStatus(); + d->m_exitCode = d->m_process->exitCode(); + d->m_processErrorString = error; setState(Inactive); - emit processClosed(exitStatus); + emit processClosed(d->m_processErrorString); } void SshRemoteProcessRunner::handleStdout() @@ -206,9 +185,9 @@ void SshRemoteProcessRunner::setState(int newState) d->m_state = static_cast<State>(newState); if (d->m_state == Inactive) { if (d->m_process) { - disconnect(d->m_process.data(), 0, this, 0); - d->m_process->close(); - d->m_process.clear(); + disconnect(d->m_process.get(), nullptr, this, nullptr); + d->m_process->terminate(); + d->m_process.release()->deleteLater(); } if (d->m_connection) { disconnect(d->m_connection, 0, this, 0); @@ -219,7 +198,6 @@ void SshRemoteProcessRunner::setState(int newState) } QByteArray SshRemoteProcessRunner::command() const { return d->m_command; } -SshError SshRemoteProcessRunner::lastConnectionError() const { return d->m_lastConnectionError; } QString SshRemoteProcessRunner::lastConnectionErrorString() const { return d->m_lastConnectionErrorString; } @@ -231,19 +209,13 @@ bool SshRemoteProcessRunner::isProcessRunning() const SshRemoteProcess::ExitStatus SshRemoteProcessRunner::processExitStatus() const { - QSSH_ASSERT(!isProcessRunning()); + QTC_CHECK(!isProcessRunning()); return d->m_exitStatus; } -SshRemoteProcess::Signal SshRemoteProcessRunner::processExitSignal() const -{ - QSSH_ASSERT(processExitStatus() == SshRemoteProcess::CrashExit); - return d->m_exitSignal; -} - int SshRemoteProcessRunner::processExitCode() const { - QSSH_ASSERT(processExitStatus() == SshRemoteProcess::NormalExit); + QTC_CHECK(processExitStatus() == SshRemoteProcess::NormalExit); return d->m_exitCode; } @@ -268,16 +240,10 @@ QByteArray SshRemoteProcessRunner::readAllStandardError() void SshRemoteProcessRunner::writeDataToProcess(const QByteArray &data) { - QSSH_ASSERT(isProcessRunning()); + QTC_CHECK(isProcessRunning()); d->m_process->write(data); } -void SshRemoteProcessRunner::sendSignalToProcess(SshRemoteProcess::Signal signal) -{ - QSSH_ASSERT(isProcessRunning()); - d->m_process->sendSignal(signal); -} - void SshRemoteProcessRunner::cancel() { setState(Inactive); diff --git a/src/libs/ssh/sshremoteprocessrunner.h b/src/libs/ssh/sshremoteprocessrunner.h index 0a2ee578e9f..029fb6bb3df 100644 --- a/src/libs/ssh/sshremoteprocessrunner.h +++ b/src/libs/ssh/sshremoteprocessrunner.h @@ -40,19 +40,15 @@ public: ~SshRemoteProcessRunner(); void run(const QByteArray &command, const SshConnectionParameters &sshParams); - void runInTerminal(const QByteArray &command, const SshPseudoTerminal &terminal, - const SshConnectionParameters &sshParams); + void runInTerminal(const QByteArray &command, const SshConnectionParameters &sshParams); QByteArray command() const; - QSsh::SshError lastConnectionError() const; QString lastConnectionErrorString() const; bool isProcessRunning() const; void writeDataToProcess(const QByteArray &data); - void sendSignalToProcess(SshRemoteProcess::Signal signal); // No effect with OpenSSH server. - void cancel(); // Does not stop remote process, just frees SSH-related process resources. + void cancel(); SshRemoteProcess::ExitStatus processExitStatus() const; - SshRemoteProcess::Signal processExitSignal() const; int processExitCode() const; QString processErrorString() const; QByteArray readAllStandardOutput(); @@ -63,14 +59,14 @@ signals: void processStarted(); void readyReadStandardOutput(); void readyReadStandardError(); - void processClosed(int exitStatus); // values are of type SshRemoteProcess::ExitStatus + void processClosed(const QString &error); private: void handleConnected(); - void handleConnectionError(QSsh::SshError error); + void handleConnectionError(); void handleDisconnected(); void handleProcessStarted(); - void handleProcessFinished(int exitStatus); + void handleProcessFinished(const QString &error); void handleStdout(); void handleStderr(); void runInternal(const QByteArray &command, const QSsh::SshConnectionParameters &sshParams); diff --git a/src/libs/ssh/sshsendfacility.cpp b/src/libs/ssh/sshsendfacility.cpp deleted file mode 100644 index f92a1220fb4..00000000000 --- a/src/libs/ssh/sshsendfacility.cpp +++ /dev/null @@ -1,283 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshsendfacility_p.h" - -#include "sshkeyexchange_p.h" -#include "sshlogging_p.h" -#include "sshoutgoingpacket_p.h" - -#include <QTcpSocket> - -namespace QSsh { -namespace Internal { - -SshSendFacility::SshSendFacility(QTcpSocket *socket) - : m_clientSeqNr(0), m_socket(socket), - m_outgoingPacket(m_encrypter, m_clientSeqNr) -{ -} - -void SshSendFacility::sendPacket() -{ - qCDebug(sshLog, "Sending packet, client seq nr is %u", m_clientSeqNr); - if (m_socket->isValid() - && m_socket->state() == QAbstractSocket::ConnectedState) { - m_socket->write(m_outgoingPacket.rawData()); - ++m_clientSeqNr; - } -} - -void SshSendFacility::reset() -{ - m_clientSeqNr = 0; - m_encrypter.clearKeys(); -} - -void SshSendFacility::recreateKeys(const SshKeyExchange &keyExchange) -{ - m_encrypter.recreateKeys(keyExchange); -} - -void SshSendFacility::createAuthenticationKey(const QByteArray &privKeyFileContents) -{ - m_encrypter.createAuthenticationKey(privKeyFileContents); -} - -QByteArray SshSendFacility::sendKeyExchangeInitPacket() -{ - const QByteArray &payLoad = m_outgoingPacket.generateKeyExchangeInitPacket(); - sendPacket(); - return payLoad; -} - -void SshSendFacility::sendKeyDhInitPacket(const Botan::BigInt &e) -{ - m_outgoingPacket.generateKeyDhInitPacket(e); - sendPacket(); -} - -void SshSendFacility::sendKeyEcdhInitPacket(const QByteArray &clientQ) -{ - m_outgoingPacket.generateKeyEcdhInitPacket(clientQ); - sendPacket(); -} - -void SshSendFacility::sendNewKeysPacket() -{ - m_outgoingPacket.generateNewKeysPacket(); - sendPacket(); -} - -void SshSendFacility::sendDisconnectPacket(SshErrorCode reason, - const QByteArray &reasonString) -{ - m_outgoingPacket.generateDisconnectPacket(reason, reasonString); - sendPacket(); - } - -void SshSendFacility::sendMsgUnimplementedPacket(quint32 serverSeqNr) -{ - m_outgoingPacket.generateMsgUnimplementedPacket(serverSeqNr); - sendPacket(); -} - -void SshSendFacility::sendUserAuthServiceRequestPacket() -{ - m_outgoingPacket.generateUserAuthServiceRequestPacket(); - sendPacket(); -} - -void SshSendFacility::sendUserAuthByPasswordRequestPacket(const QByteArray &user, - const QByteArray &service, const QByteArray &pwd) -{ - m_outgoingPacket.generateUserAuthByPasswordRequestPacket(user, service, pwd); - sendPacket(); - } - -void SshSendFacility::sendUserAuthByPublicKeyRequestPacket(const QByteArray &user, - const QByteArray &service, const QByteArray &key, const QByteArray &signature) -{ - m_outgoingPacket.generateUserAuthByPublicKeyRequestPacket(user, service, key, signature); - sendPacket(); -} - -void SshSendFacility::sendQueryPublicKeyPacket(const QByteArray &user, const QByteArray &service, - const QByteArray &publicKey) -{ - m_outgoingPacket.generateQueryPublicKeyPacket(user, service, publicKey); - sendPacket(); -} - -void SshSendFacility::sendUserAuthByKeyboardInteractiveRequestPacket(const QByteArray &user, - const QByteArray &service) -{ - m_outgoingPacket.generateUserAuthByKeyboardInteractiveRequestPacket(user, service); - sendPacket(); -} - -void SshSendFacility::sendUserAuthInfoResponsePacket(const QStringList &responses) -{ - m_outgoingPacket.generateUserAuthInfoResponsePacket(responses); - sendPacket(); -} - -void SshSendFacility::sendRequestFailurePacket() -{ - m_outgoingPacket.generateRequestFailurePacket(); - sendPacket(); -} - -void SshSendFacility::sendIgnorePacket() -{ - m_outgoingPacket.generateIgnorePacket(); - sendPacket(); -} - -void SshSendFacility::sendInvalidPacket() -{ - m_outgoingPacket.generateInvalidMessagePacket(); - sendPacket(); -} - -void SshSendFacility::sendSessionPacket(quint32 channelId, quint32 windowSize, - quint32 maxPacketSize) -{ - m_outgoingPacket.generateSessionPacket(channelId, windowSize, - maxPacketSize); - sendPacket(); -} - -void SshSendFacility::sendDirectTcpIpPacket(quint32 channelId, quint32 windowSize, - quint32 maxPacketSize, const QByteArray &remoteHost, quint32 remotePort, - const QByteArray &localIpAddress, quint32 localPort) -{ - m_outgoingPacket.generateDirectTcpIpPacket(channelId, windowSize, maxPacketSize, remoteHost, - remotePort, localIpAddress, localPort); - sendPacket(); -} - -void SshSendFacility::sendTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort) -{ - m_outgoingPacket.generateTcpIpForwardPacket(bindAddress, bindPort); - sendPacket(); -} - -void SshSendFacility::sendCancelTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort) -{ - m_outgoingPacket.generateCancelTcpIpForwardPacket(bindAddress, bindPort); - sendPacket(); -} - -void SshSendFacility::sendPtyRequestPacket(quint32 remoteChannel, - const SshPseudoTerminal &terminal) -{ - m_outgoingPacket.generatePtyRequestPacket(remoteChannel, terminal); - sendPacket(); -} - -void SshSendFacility::sendEnvPacket(quint32 remoteChannel, - const QByteArray &var, const QByteArray &value) -{ - m_outgoingPacket.generateEnvPacket(remoteChannel, var, value); - sendPacket(); -} - -void SshSendFacility::sendX11ForwardingPacket(quint32 remoteChannel, const QByteArray &protocol, - const QByteArray &cookie, quint32 screenNumber) -{ - m_outgoingPacket.generateX11ForwardingPacket(remoteChannel, protocol, cookie, screenNumber); - sendPacket(); -} - -void SshSendFacility::sendExecPacket(quint32 remoteChannel, - const QByteArray &command) -{ - m_outgoingPacket.generateExecPacket(remoteChannel, command); - sendPacket(); -} - -void SshSendFacility::sendShellPacket(quint32 remoteChannel) -{ - m_outgoingPacket.generateShellPacket(remoteChannel); - sendPacket(); -} - -void SshSendFacility::sendSftpPacket(quint32 remoteChannel) -{ - m_outgoingPacket.generateSftpPacket(remoteChannel); - sendPacket(); -} - -void SshSendFacility::sendWindowAdjustPacket(quint32 remoteChannel, - quint32 bytesToAdd) -{ - m_outgoingPacket.generateWindowAdjustPacket(remoteChannel, bytesToAdd); - sendPacket(); -} - -void SshSendFacility::sendChannelDataPacket(quint32 remoteChannel, - const QByteArray &data) -{ - m_outgoingPacket.generateChannelDataPacket(remoteChannel, data); - sendPacket(); -} - -void SshSendFacility::sendChannelSignalPacket(quint32 remoteChannel, - const QByteArray &signalName) -{ - m_outgoingPacket.generateChannelSignalPacket(remoteChannel, signalName); - sendPacket(); -} - -void SshSendFacility::sendChannelEofPacket(quint32 remoteChannel) -{ - m_outgoingPacket.generateChannelEofPacket(remoteChannel); - sendPacket(); -} - -void SshSendFacility::sendChannelClosePacket(quint32 remoteChannel) -{ - m_outgoingPacket.generateChannelClosePacket(remoteChannel); - sendPacket(); -} - -void SshSendFacility::sendChannelOpenConfirmationPacket(quint32 remoteChannel, quint32 localChannel, - quint32 localWindowSize, quint32 maxPacketSize) -{ - m_outgoingPacket.generateChannelOpenConfirmationPacket(remoteChannel, localChannel, - localWindowSize, maxPacketSize); - sendPacket(); -} - -void SshSendFacility::sendChannelOpenFailurePacket(quint32 remoteChannel, quint32 reason, - const QByteArray &reasonString) -{ - m_outgoingPacket.generateChannelOpenFailurePacket(remoteChannel, reason, reasonString); - sendPacket(); -} - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshsendfacility_p.h b/src/libs/ssh/sshsendfacility_p.h deleted file mode 100644 index b3e0e44e1ff..00000000000 --- a/src/libs/ssh/sshsendfacility_p.h +++ /dev/null @@ -1,112 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sshcryptofacility_p.h" -#include "sshoutgoingpacket_p.h" - -#include <QStringList> - -QT_BEGIN_NAMESPACE -class QTcpSocket; -QT_END_NAMESPACE - - -namespace QSsh { -class SshPseudoTerminal; - -namespace Internal { -class SshKeyExchange; - -class SshSendFacility -{ -public: - SshSendFacility(QTcpSocket *socket); - void reset(); - void recreateKeys(const SshKeyExchange &keyExchange); - void createAuthenticationKey(const QByteArray &privKeyFileContents); - - QByteArray sessionId() const { return m_encrypter.sessionId(); } - - QByteArray sendKeyExchangeInitPacket(); - void sendKeyDhInitPacket(const Botan::BigInt &e); - void sendKeyEcdhInitPacket(const QByteArray &clientQ); - void sendNewKeysPacket(); - void sendDisconnectPacket(SshErrorCode reason, - const QByteArray &reasonString); - void sendMsgUnimplementedPacket(quint32 serverSeqNr); - void sendUserAuthServiceRequestPacket(); - void sendUserAuthByPasswordRequestPacket(const QByteArray &user, - const QByteArray &service, const QByteArray &pwd); - void sendUserAuthByPublicKeyRequestPacket(const QByteArray &user, - const QByteArray &service, const QByteArray &key, const QByteArray &signature); - void sendQueryPublicKeyPacket(const QByteArray &user, const QByteArray &service, - const QByteArray &publicKey); - void sendUserAuthByKeyboardInteractiveRequestPacket(const QByteArray &user, - const QByteArray &service); - void sendUserAuthInfoResponsePacket(const QStringList &responses); - void sendRequestFailurePacket(); - void sendIgnorePacket(); - void sendInvalidPacket(); - void sendSessionPacket(quint32 channelId, quint32 windowSize, - quint32 maxPacketSize); - void sendDirectTcpIpPacket(quint32 channelId, quint32 windowSize, quint32 maxPacketSize, - const QByteArray &remoteHost, quint32 remotePort, const QByteArray &localIpAddress, - quint32 localPort); - void sendTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort); - void sendCancelTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort); - void sendPtyRequestPacket(quint32 remoteChannel, - const SshPseudoTerminal &terminal); - void sendEnvPacket(quint32 remoteChannel, const QByteArray &var, - const QByteArray &value); - void sendX11ForwardingPacket(quint32 remoteChannel, const QByteArray &protocol, - const QByteArray &cookie, quint32 screenNumber); - void sendExecPacket(quint32 remoteChannel, const QByteArray &command); - void sendShellPacket(quint32 remoteChannel); - void sendSftpPacket(quint32 remoteChannel); - void sendWindowAdjustPacket(quint32 remoteChannel, quint32 bytesToAdd); - void sendChannelDataPacket(quint32 remoteChannel, const QByteArray &data); - void sendChannelSignalPacket(quint32 remoteChannel, - const QByteArray &signalName); - void sendChannelEofPacket(quint32 remoteChannel); - void sendChannelClosePacket(quint32 remoteChannel); - void sendChannelOpenConfirmationPacket(quint32 remoteChannel, quint32 localChannel, - quint32 localWindowSize, quint32 maxPackeSize); - void sendChannelOpenFailurePacket(quint32 remoteChannel, quint32 reason, - const QByteArray &reasonString); - quint32 nextClientSeqNr() const { return m_clientSeqNr; } - -private: - void sendPacket(); - - quint32 m_clientSeqNr; - SshEncryptionFacility m_encrypter; - QTcpSocket *m_socket; - SshOutgoingPacket m_outgoingPacket; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshsettings.cpp b/src/libs/ssh/sshsettings.cpp new file mode 100644 index 00000000000..d1d9e8c0997 --- /dev/null +++ b/src/libs/ssh/sshsettings.cpp @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "sshsettings.h" + +#include <utils/environment.h> + +#include <QSettings> + +using namespace Utils; + +namespace QSsh { +namespace Internal { + +struct SshSettings +{ + bool useConnectionSharing = true; + int connectionSharingTimeOutInMinutes = 10; + FileName sshFilePath; + FileName sftpFilePath; + FileName askpassFilePath; + FileName keygenFilePath; + QSsh::SshSettings::SearchPathRetriever searchPathRetriever = [] { return FileNameList(); }; +}; + +} // namespace Internal + +Q_GLOBAL_STATIC(QSsh::Internal::SshSettings, sshSettings) + + +class AccessSettingsGroup +{ +public: + AccessSettingsGroup(QSettings *settings) : m_settings(settings) + { + settings->beginGroup("SshSettings"); + } + ~AccessSettingsGroup() { m_settings->endGroup(); } +private: + QSettings * const m_settings; +}; + +static QString connectionSharingKey() { return "UseConnectionSharing"; } +static QString connectionSharingTimeoutKey() { return "ConnectionSharingTimeout"; } +static QString sshFilePathKey() { return "SshFilePath"; } +static QString sftpFilePathKey() { return "SftpFilePath"; } +static QString askPassFilePathKey() { return "AskpassFilePath"; } +static QString keygenFilePathKey() { return "KeygenFilePath"; } + +void SshSettings::loadSettings(QSettings *settings) +{ + AccessSettingsGroup g(settings); + QVariant value = settings->value(connectionSharingKey()); + if (value.isValid()) + sshSettings->useConnectionSharing = value.toBool(); + value = settings->value(connectionSharingTimeoutKey()); + if (value.isValid()) + sshSettings->connectionSharingTimeOutInMinutes = value.toInt(); + sshSettings->sshFilePath = FileName::fromString(settings->value(sshFilePathKey()).toString()); + sshSettings->sftpFilePath = FileName::fromString(settings->value(sftpFilePathKey()).toString()); + sshSettings->askpassFilePath = FileName::fromString( + settings->value(askPassFilePathKey()).toString()); + sshSettings->keygenFilePath = FileName::fromString( + settings->value(keygenFilePathKey()).toString()); +} + +void SshSettings::storeSettings(QSettings *settings) +{ + AccessSettingsGroup g(settings); + settings->setValue(connectionSharingKey(), sshSettings->useConnectionSharing); + settings->setValue(connectionSharingTimeoutKey(), + sshSettings->connectionSharingTimeOutInMinutes); + settings->setValue(sshFilePathKey(), sshSettings->sshFilePath.toString()); + settings->setValue(sftpFilePathKey(), sshSettings->sftpFilePath.toString()); + settings->setValue(askPassFilePathKey(), sshSettings->askpassFilePath.toString()); + settings->setValue(keygenFilePathKey(), sshSettings->keygenFilePath.toString()); +} + +void SshSettings::setConnectionSharingEnabled(bool share) +{ + sshSettings->useConnectionSharing = share; +} +bool SshSettings::connectionSharingEnabled() { return sshSettings->useConnectionSharing; } + +void SshSettings::setConnectionSharingTimeout(int timeInMinutes) +{ + sshSettings->connectionSharingTimeOutInMinutes = timeInMinutes; +} +int SshSettings::connectionSharingTimeout() +{ + return sshSettings->connectionSharingTimeOutInMinutes; +} + +static FileName filePathValue(const FileName &value, const QString &defaultFileName) +{ + return !value.isEmpty() + ? value + : Environment::systemEnvironment().searchInPath(defaultFileName, + sshSettings->searchPathRetriever()); +} + +void SshSettings::setSshFilePath(const FileName &ssh) { sshSettings->sshFilePath = ssh; } +FileName SshSettings::sshFilePath() { return filePathValue(sshSettings->sshFilePath, "ssh"); } + +void SshSettings::setSftpFilePath(const FileName &sftp) { sshSettings->sftpFilePath = sftp; } +FileName SshSettings::sftpFilePath() { return filePathValue(sshSettings->sftpFilePath, "sftp"); } + +void SshSettings::setAskpassFilePath(const FileName &askPass) +{ + sshSettings->askpassFilePath = askPass; +} + +FileName SshSettings::askpassFilePath() +{ + FileName candidate; + candidate = sshSettings->askpassFilePath; + if (candidate.isEmpty()) + candidate = FileName::fromString(Environment::systemEnvironment().value("SSH_ASKPASS")); + return filePathValue(candidate, "ssh-askpass"); +} + +void SshSettings::setKeygenFilePath(const FileName &keygen) +{ + sshSettings->keygenFilePath = keygen; +} + +FileName SshSettings::keygenFilePath() +{ + return filePathValue(sshSettings->keygenFilePath, "ssh-keygen"); +} + +void SshSettings::setExtraSearchPathRetriever(const SearchPathRetriever &pathRetriever) +{ + sshSettings->searchPathRetriever = pathRetriever; +} + +} // namespace QSsh diff --git a/src/libs/ssh/sshhostkeydatabase.h b/src/libs/ssh/sshsettings.h index bd58ab86b5e..dc29ead6065 100644 --- a/src/libs/ssh/sshhostkeydatabase.h +++ b/src/libs/ssh/sshsettings.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -27,40 +27,42 @@ #include "ssh_global.h" -#include <QSharedPointer> +#include <utils/fileutils.h> + +#include <functional> QT_BEGIN_NAMESPACE -class QByteArray; -class QString; +class QSettings; QT_END_NAMESPACE namespace QSsh { -class SshHostKeyDatabase; -typedef QSharedPointer<SshHostKeyDatabase> SshHostKeyDatabasePtr; -class QSSH_EXPORT SshHostKeyDatabase +class QSSH_EXPORT SshSettings { - friend class QSharedPointer<SshHostKeyDatabase>; // To give create() access to our constructor. - public: - enum KeyLookupResult { - KeyLookupMatch, - KeyLookupNoMatch, - KeyLookupMismatch - }; + static void loadSettings(QSettings *settings); + static void storeSettings(QSettings *settings); + + static void setConnectionSharingEnabled(bool share); + static bool connectionSharingEnabled(); + + static void setConnectionSharingTimeout(int timeInMinutes); + static int connectionSharingTimeout(); + + static void setSshFilePath(const Utils::FileName &ssh); + static Utils::FileName sshFilePath(); - ~SshHostKeyDatabase(); + static void setSftpFilePath(const Utils::FileName &sftp); + static Utils::FileName sftpFilePath(); - bool load(const QString &filePath, QString *error = 0); - bool store(const QString &filePath, QString *error = 0) const; - KeyLookupResult matchHostKey(const QString &hostName, const QByteArray &key) const; - void insertHostKey(const QString &hostName, const QByteArray &key); + static void setAskpassFilePath(const Utils::FileName &askPass); + static Utils::FileName askpassFilePath(); -private: - SshHostKeyDatabase(); + static void setKeygenFilePath(const Utils::FileName &keygen); + static Utils::FileName keygenFilePath(); - class SshHostKeyDatabasePrivate; - SshHostKeyDatabasePrivate * const d; + using SearchPathRetriever = std::function<Utils::FileNameList()>; + static void setExtraSearchPathRetriever(const SearchPathRetriever &pathRetriever); }; } // namespace QSsh diff --git a/src/libs/ssh/sshtcpipforwardserver.cpp b/src/libs/ssh/sshtcpipforwardserver.cpp deleted file mode 100644 index 5501ea930b9..00000000000 --- a/src/libs/ssh/sshtcpipforwardserver.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshtcpipforwardserver.h" -#include "sshtcpipforwardserver_p.h" - -#include "sshlogging_p.h" -#include "sshsendfacility_p.h" - -namespace QSsh { -namespace Internal { - -SshTcpIpForwardServerPrivate::SshTcpIpForwardServerPrivate(const QString &bindAddress, - quint16 bindPort, SshSendFacility &sendFacility) - : m_sendFacility(sendFacility), - m_bindAddress(bindAddress), - m_bindPort(bindPort), - m_state(SshTcpIpForwardServer::Inactive) -{ -} - -} // namespace Internal - -using namespace Internal; - -SshTcpIpForwardServer::SshTcpIpForwardServer(const QString &bindAddress, quint16 bindPort, - SshSendFacility &sendFacility) - : d(new SshTcpIpForwardServerPrivate(bindAddress, bindPort, sendFacility)) -{ - connect(&d->m_timeoutTimer, &QTimer::timeout, this, &SshTcpIpForwardServer::setClosed); -} - -void SshTcpIpForwardServer::setListening(quint16 port) -{ - QSSH_ASSERT_AND_RETURN(d->m_state != Listening); - d->m_bindPort = port; - d->m_state = Listening; - emit stateChanged(Listening); -} - -void SshTcpIpForwardServer::setClosed() -{ - QSSH_ASSERT_AND_RETURN(d->m_state != Inactive); - d->m_state = Inactive; - emit stateChanged(Inactive); -} - -void SshTcpIpForwardServer::setNewConnection(const SshForwardedTcpIpTunnel::Ptr &connection) -{ - d->m_pendingConnections.append(connection); - emit newConnection(); -} - -SshTcpIpForwardServer::~SshTcpIpForwardServer() -{ - delete d; -} - -void SshTcpIpForwardServer::initialize() -{ - if (d->m_state == Inactive || d->m_state == Closing) { - try { - d->m_state = Initializing; - emit stateChanged(Initializing); - d->m_sendFacility.sendTcpIpForwardPacket(d->m_bindAddress.toUtf8(), d->m_bindPort); - d->m_timeoutTimer.start(d->ReplyTimeout); - } catch (const std::exception &e) { - qCWarning(sshLog, "Botan error: %s", e.what()); - d->m_timeoutTimer.stop(); - setClosed(); - } - } -} - -void SshTcpIpForwardServer::close() -{ - d->m_timeoutTimer.stop(); - - if (d->m_state == Initializing || d->m_state == Listening) { - try { - d->m_state = Closing; - emit stateChanged(Closing); - d->m_sendFacility.sendCancelTcpIpForwardPacket(d->m_bindAddress.toUtf8(), - d->m_bindPort); - d->m_timeoutTimer.start(d->ReplyTimeout); - } catch (const std::exception &e) { - qCWarning(sshLog, "Botan error: %s", e.what()); - d->m_timeoutTimer.stop(); - setClosed(); - } - } -} - -const QString &SshTcpIpForwardServer::bindAddress() const -{ - return d->m_bindAddress; -} - -quint16 SshTcpIpForwardServer::port() const -{ - return d->m_bindPort; -} - -SshTcpIpForwardServer::State SshTcpIpForwardServer::state() const -{ - return d->m_state; -} - -SshForwardedTcpIpTunnel::Ptr SshTcpIpForwardServer::nextPendingConnection() -{ - return d->m_pendingConnections.takeFirst(); -} - -} // namespace QSsh diff --git a/src/libs/ssh/sshtcpipforwardserver_p.h b/src/libs/ssh/sshtcpipforwardserver_p.h deleted file mode 100644 index 9f4775bee78..00000000000 --- a/src/libs/ssh/sshtcpipforwardserver_p.h +++ /dev/null @@ -1,54 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sshtcpipforwardserver.h" -#include <QList> -#include <QTimer> - -namespace QSsh { -namespace Internal { - -class SshTcpIpForwardServerPrivate -{ -public: - static const int ReplyTimeout = 10000; // milli seconds - - SshTcpIpForwardServerPrivate(const QString &bindAddress, quint16 bindPort, - SshSendFacility &sendFacility); - - SshSendFacility &m_sendFacility; - QTimer m_timeoutTimer; - - const QString m_bindAddress; - quint16 m_bindPort; - SshTcpIpForwardServer::State m_state; - - QList<SshForwardedTcpIpTunnel::Ptr> m_pendingConnections; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshtcpiptunnel.cpp b/src/libs/ssh/sshtcpiptunnel.cpp deleted file mode 100644 index ac7bf5208b6..00000000000 --- a/src/libs/ssh/sshtcpiptunnel.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshsendfacility_p.h" -#include "sshtcpiptunnel_p.h" - -#include "sshincomingpacket_p.h" -#include "sshexception_p.h" -#include "sshlogging_p.h" - -namespace QSsh { - -namespace Internal { -SshTcpIpTunnelPrivate::SshTcpIpTunnelPrivate(quint32 channelId, SshSendFacility &sendFacility) - : AbstractSshChannel(channelId, sendFacility) -{ - connect(this, &AbstractSshChannel::eof, this, &SshTcpIpTunnelPrivate::handleEof); -} - -SshTcpIpTunnelPrivate::~SshTcpIpTunnelPrivate() -{ - closeChannel(); -} - - - -void SshTcpIpTunnelPrivate::handleChannelSuccess() -{ - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_MSG_CHANNEL_SUCCESS message."); -} - -void SshTcpIpTunnelPrivate::handleChannelFailure() -{ - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_MSG_CHANNEL_FAILURE message."); -} - -void SshTcpIpTunnelPrivate::handleOpenFailureInternal(const QString &reason) -{ - emit error(reason); - closeChannel(); -} - -void SshTcpIpTunnelPrivate::handleChannelDataInternal(const QByteArray &data) -{ - m_data += data; - emit readyRead(); -} - -void SshTcpIpTunnelPrivate::handleChannelExtendedDataInternal(quint32 type, - const QByteArray &data) -{ - qCWarning(sshLog, "%s: Unexpected extended channel data. Type is %u, content is '%s'.", - Q_FUNC_INFO, type, data.constData()); -} - -void SshTcpIpTunnelPrivate::handleExitStatus(const SshChannelExitStatus &exitStatus) -{ - qCWarning(sshLog, "%s: Unexpected exit status %d.", Q_FUNC_INFO, exitStatus.exitStatus); -} - -void SshTcpIpTunnelPrivate::handleExitSignal(const SshChannelExitSignal &signal) -{ - qCWarning(sshLog, "%s: Unexpected exit signal %s.", Q_FUNC_INFO, signal.signal.constData()); -} - -void SshTcpIpTunnelPrivate::closeHook() -{ - emit closed(); -} - -void SshTcpIpTunnelPrivate::handleEof() -{ - /* - * For some reason, the OpenSSH server only sends EOF when the remote port goes away, - * but does not close the channel, even though it becomes useless in that case. - * So we close it ourselves. - */ - closeChannel(); -} - -qint64 SshTcpIpTunnelPrivate::readData(char *data, qint64 maxlen) -{ - const qint64 bytesRead = qMin(qint64(m_data.count()), maxlen); - memcpy(data, m_data.constData(), bytesRead); - m_data.remove(0, bytesRead); - return bytesRead; -} - -qint64 SshTcpIpTunnelPrivate::writeData(const char *data, qint64 len) -{ - QSSH_ASSERT_AND_RETURN_VALUE(channelState() == AbstractSshChannel::SessionEstablished, 0); - - sendData(QByteArray(data, len)); - return len; -} - -} // namespace Internal - -} // namespace QSsh diff --git a/src/libs/ssh/sshtcpiptunnel_p.h b/src/libs/ssh/sshtcpiptunnel_p.h deleted file mode 100644 index a9f2844cbc6..00000000000 --- a/src/libs/ssh/sshtcpiptunnel_p.h +++ /dev/null @@ -1,82 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sshchannel_p.h" -#include <QIODevice> -#include <QByteArray> - -namespace QSsh { -namespace Internal { - -class SshTcpIpTunnelPrivate : public AbstractSshChannel -{ - Q_OBJECT - -public: - SshTcpIpTunnelPrivate(quint32 channelId, SshSendFacility &sendFacility); - ~SshTcpIpTunnelPrivate(); - - template<class SshTcpIpTunnel> - void init(SshTcpIpTunnel *q) - { - connect(this, &SshTcpIpTunnelPrivate::closed, - q, &SshTcpIpTunnel::close, Qt::QueuedConnection); - connect(this, &SshTcpIpTunnelPrivate::readyRead, - q, &SshTcpIpTunnel::readyRead, Qt::QueuedConnection); - connect(this, &SshTcpIpTunnelPrivate::error, q, [q](const QString &reason) { - q->setErrorString(reason); - emit q->error(reason); - }, Qt::QueuedConnection); - } - - void handleChannelSuccess() override; - void handleChannelFailure() override; - - qint64 readData(char *data, qint64 maxlen); - qint64 writeData(const char *data, qint64 len); - -signals: - void readyRead(); - void error(const QString &reason); - void closed(); - -protected: - void handleOpenFailureInternal(const QString &reason) override; - void handleChannelDataInternal(const QByteArray &data) override; - void handleChannelExtendedDataInternal(quint32 type, const QByteArray &data) override; - void handleExitStatus(const SshChannelExitStatus &exitStatus) override; - void handleExitSignal(const SshChannelExitSignal &signal) override; - void closeHook() override; - - QByteArray m_data; - -private: - void handleEof(); -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshx11channel.cpp b/src/libs/ssh/sshx11channel.cpp deleted file mode 100644 index d531142cebd..00000000000 --- a/src/libs/ssh/sshx11channel.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshx11channel_p.h" - -#include "sshincomingpacket_p.h" -#include "sshlogging_p.h" -#include "sshsendfacility_p.h" - -#include <QFileInfo> -#include <QLocalSocket> -#include <QTcpSocket> - -namespace QSsh { -namespace Internal { - -class X11Socket : public QObject -{ - Q_OBJECT -public: - X11Socket(QObject *parent) : QObject(parent) { } - - void establishConnection(const X11DisplayInfo &displayInfo) - { - const bool hostNameIsPath = displayInfo.hostName.startsWith('/'); // macOS - const bool hasActualHostName = !displayInfo.hostName.isEmpty() - && displayInfo.hostName != "unix" && !displayInfo.hostName.endsWith("/unix") - && !hostNameIsPath; - if (hasActualHostName) { - QTcpSocket * const socket = new QTcpSocket(this); - connect(socket, &QTcpSocket::connected, this, &X11Socket::connected); - connect(socket, - static_cast<void(QTcpSocket::*)(QTcpSocket::SocketError)>(&QTcpSocket::error), - [this, socket] { - emit error(socket->errorString()); - }); - socket->connectToHost(displayInfo.hostName, 6000 + displayInfo.display); - m_socket = socket; - } else { - const QString serverBasePath = hostNameIsPath ? QString(displayInfo.hostName + ':') - : "/tmp/.X11-unix/X"; - QLocalSocket * const socket = new QLocalSocket(this); - connect(socket, &QLocalSocket::connected, this, &X11Socket::connected); - connect(socket, - static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error), - [this, socket] { - emit error(socket->errorString()); - }); - socket->connectToServer(serverBasePath + QString::number(displayInfo.display)); - m_socket = socket; - } - connect(m_socket, &QIODevice::readyRead, - [this] { emit dataAvailable(m_socket->readAll()); }); - } - - void closeConnection() - { - m_socket->disconnect(); - if (localSocket()) - localSocket()->disconnectFromServer(); - else - tcpSocket()->disconnectFromHost(); - } - - void write(const QByteArray &data) - { - m_socket->write(data); - } - - bool hasError() const - { - return (localSocket() && localSocket()->error() != QLocalSocket::UnknownSocketError) - || (tcpSocket() && tcpSocket()->error() != QTcpSocket::UnknownSocketError); - } - - bool isConnected() const - { - return (localSocket() && localSocket()->state() == QLocalSocket::ConnectedState) - || (tcpSocket() && tcpSocket()->state() == QTcpSocket::ConnectedState); - } - -signals: - void connected(); - void error(const QString &message); - void dataAvailable(const QByteArray &data); - -private: - QLocalSocket *localSocket() const { return qobject_cast<QLocalSocket *>(m_socket); } - QTcpSocket *tcpSocket() const { return qobject_cast<QTcpSocket *>(m_socket); } - - QIODevice *m_socket = nullptr; -}; - -SshX11Channel::SshX11Channel(const X11DisplayInfo &displayInfo, quint32 channelId, - SshSendFacility &sendFacility) - : AbstractSshChannel (channelId, sendFacility), - m_x11Socket(new X11Socket(this)), - m_displayInfo(displayInfo) -{ - setChannelState(SessionRequested); // Invariant for parent class. -} - -void SshX11Channel::handleChannelSuccess() -{ - qCWarning(sshLog) << "unexpected channel success message for X11 channel"; -} - -void SshX11Channel::handleChannelFailure() -{ - qCWarning(sshLog) << "unexpected channel failure message for X11 channel"; -} - -void SshX11Channel::handleOpenSuccessInternal() -{ - m_sendFacility.sendChannelOpenConfirmationPacket(remoteChannel(), localChannelId(), - initialWindowSize(), maxPacketSize()); - connect(m_x11Socket, &X11Socket::connected, [this] { - qCDebug(sshLog) << "x11 socket connected for channel" << localChannelId(); - if (!m_queuedRemoteData.isEmpty()) - handleRemoteData(QByteArray()); - }); - connect(m_x11Socket, &X11Socket::error, - [this](const QString &msg) { emit error(tr("X11 socket error: %1").arg(msg)); }); - connect(m_x11Socket, &X11Socket::dataAvailable, [this](const QByteArray &data) { - qCDebug(sshLog) << "sending " << data.size() << "bytes from x11 socket to remote side " - "in channel" << localChannelId(); - sendData(data); - }); - m_x11Socket->establishConnection(m_displayInfo); -} - -void SshX11Channel::handleOpenFailureInternal(const QString &reason) -{ - qCWarning(sshLog) << "unexpected channel open failure message for X11 channel:" << reason; -} - -void SshX11Channel::handleChannelDataInternal(const QByteArray &data) -{ - handleRemoteData(data); -} - -void SshX11Channel::handleChannelExtendedDataInternal(quint32 type, const QByteArray &data) -{ - qCWarning(sshLog) << "unexpected extended data for X11 channel" << type << data; -} - -void SshX11Channel::handleExitStatus(const SshChannelExitStatus &exitStatus) -{ - qCWarning(sshLog) << "unexpected exit status message on X11 channel" << exitStatus.exitStatus; - closeChannel(); -} - -void SshX11Channel::handleExitSignal(const SshChannelExitSignal &signal) -{ - qCWarning(sshLog) << "unexpected exit signal message on X11 channel" << signal.error; - closeChannel(); -} - -void SshX11Channel::closeHook() -{ - m_x11Socket->disconnect(); - m_x11Socket->closeConnection(); -} - -void SshX11Channel::handleRemoteData(const QByteArray &data) -{ - if (m_x11Socket->hasError()) - return; - qCDebug(sshLog) << "received" << data.size() << "bytes from remote side in x11 channel" - << localChannelId(); - if (!m_x11Socket->isConnected()) { - qCDebug(sshLog) << "x11 socket not yet connected, queueing data"; - m_queuedRemoteData += data; - return; - } - if (m_haveReplacedRandomCookie) { - qCDebug(sshLog) << "forwarding data to x11 socket"; - m_x11Socket->write(data); - return; - } - m_queuedRemoteData += data; - const int randomCookieOffset = m_queuedRemoteData.indexOf(m_displayInfo.randomCookie); - if (randomCookieOffset == -1) { - qCDebug(sshLog) << "random cookie has not appeared in remote data yet, queueing data"; - return; - } - m_queuedRemoteData.replace(randomCookieOffset, m_displayInfo.cookie.size(), - m_displayInfo.cookie); - qCDebug(sshLog) << "found and replaced random cookie, forwarding data to x11 socket"; - m_x11Socket->write(m_queuedRemoteData); - m_queuedRemoteData.clear(); - m_haveReplacedRandomCookie = true; -} - -} // namespace Internal -} // namespace QSsh - -#include <sshx11channel.moc> diff --git a/src/libs/ssh/sshx11channel_p.h b/src/libs/ssh/sshx11channel_p.h deleted file mode 100644 index 918129172e0..00000000000 --- a/src/libs/ssh/sshx11channel_p.h +++ /dev/null @@ -1,73 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sshchannel_p.h" - -#include "sshx11displayinfo_p.h" - -#include <QByteArray> - -namespace QSsh { -namespace Internal { -class SshChannelManager; -class SshSendFacility; -class X11Socket; - -class SshX11Channel : public AbstractSshChannel -{ - Q_OBJECT - - friend class Internal::SshChannelManager; - -signals: - void error(const QString &message); - -private: - SshX11Channel(const X11DisplayInfo &displayInfo, quint32 channelId, - SshSendFacility &sendFacility); - - void handleChannelSuccess() override; - void handleChannelFailure() override; - - void handleOpenSuccessInternal() override; - void handleOpenFailureInternal(const QString &reason) override; - void handleChannelDataInternal(const QByteArray &data) override; - void handleChannelExtendedDataInternal(quint32 type, const QByteArray &data) override; - void handleExitStatus(const SshChannelExitStatus &exitStatus) override; - void handleExitSignal(const SshChannelExitSignal &signal) override; - void closeHook() override; - - void handleRemoteData(const QByteArray &data); - - X11Socket * const m_x11Socket; - const X11DisplayInfo m_displayInfo; - QByteArray m_queuedRemoteData; - bool m_haveReplacedRandomCookie = false; -}; - -} // namespace Internal -} // namespace QSsh diff --git a/src/libs/ssh/sshx11inforetriever.cpp b/src/libs/ssh/sshx11inforetriever.cpp deleted file mode 100644 index 819ad428afe..00000000000 --- a/src/libs/ssh/sshx11inforetriever.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshx11inforetriever_p.h" - -#include "sshlogging_p.h" -#include "sshx11displayinfo_p.h" - -#include <QByteArrayList> -#include <QProcess> -#include <QTemporaryFile> -#include <QTimer> - -#include <botan/auto_rng.h> - -namespace QSsh { -namespace Internal { - -static QByteArray xauthProtocol() { return "MIT-MAGIC-COOKIE-1"; } - -SshX11InfoRetriever::SshX11InfoRetriever(const QString &displayName, QObject *parent) - : QObject(parent), - m_displayName(displayName), - m_xauthProc(new QProcess(this)), - m_xauthFile(new QTemporaryFile(this)) -{ - connect(m_xauthProc, &QProcess::errorOccurred, [this] { - if (m_xauthProc->error() == QProcess::FailedToStart) { - emitFailure(tr("Could not start xauth: %1").arg(m_xauthProc->errorString())); - } - }); - connect(m_xauthProc, - static_cast<void (QProcess::*)(int,QProcess::ExitStatus)>(&QProcess::finished), - [this] { - if (m_xauthProc->exitStatus() != QProcess::NormalExit) { - emitFailure(tr("xauth crashed: %1").arg(m_xauthProc->errorString())); - return; - } - if (m_xauthProc->exitCode() != 0) { - emitFailure(tr("xauth failed with exit code %1.").arg(m_xauthProc->exitCode())); - return; - } - switch (m_state) { - case State::RunningGenerate: - m_state = State::RunningList; - m_xauthProc->start("xauth", QStringList{"-f", m_xauthFile->fileName(), "list", - m_displayName}); - break; - case State::RunningList: { - const QByteArrayList outputLines = m_xauthProc->readAllStandardOutput().split('\n'); - if (outputLines.empty()) { - emitFailure(tr("Unexpected xauth output.")); - return; - } - const QByteArrayList data = outputLines.first().simplified().split(' '); - if (data.size() < 3 || data.at(1) != xauthProtocol() || data.at(2).isEmpty()) { - emitFailure(tr("Unexpected xauth output.")); - return; - } - X11DisplayInfo displayInfo; - displayInfo.displayName = m_displayName; - const int colonIndex = m_displayName.indexOf(':'); - if (colonIndex == -1) { - emitFailure(tr("Invalid display name \"%1\"").arg(m_displayName)); - return; - } - displayInfo.hostName = m_displayName.mid(0, colonIndex); - const int dotIndex = m_displayName.indexOf('.', colonIndex + 1); - const QString display = m_displayName.mid(colonIndex + 1, - dotIndex == -1 ? -1 - : dotIndex - colonIndex - 1); - if (display.isEmpty()) { - emitFailure(tr("Invalid display name \"%1\"").arg(m_displayName)); - return; - } - bool ok; - displayInfo.display = display.toInt(&ok); - if (!ok) { - emitFailure(tr("Invalid display name \"%1\"").arg(m_displayName)); - return; - } - if (dotIndex != -1) { - displayInfo.screen = m_displayName.mid(dotIndex + 1).toInt(&ok); - if (!ok) { - emitFailure(tr("Invalid display name \"%1\"").arg(m_displayName)); - return; - } - } - displayInfo.protocol = data.at(1); - displayInfo.cookie = QByteArray::fromHex(data.at(2)); - displayInfo.randomCookie.resize(displayInfo.cookie.size()); - try { - Botan::AutoSeeded_RNG rng; - rng.randomize(reinterpret_cast<Botan::uint8_t *>(displayInfo.randomCookie.data()), - displayInfo.randomCookie.size()); - } catch (const std::exception &ex) { - emitFailure(tr("Failed to generate random cookie: %1") - .arg(QLatin1String(ex.what()))); - return; - } - emit success(displayInfo); - deleteLater(); - break; - } - default: - emitFailure(tr("Internal error")); - } - }); -} - -void SshX11InfoRetriever::start() -{ - if (!m_xauthFile->open()) { - emitFailure(tr("Could not create temporary file: %1").arg(m_xauthFile->errorString())); - return; - } - m_state = State::RunningGenerate; - m_xauthProc->start("xauth", QStringList{"-f", m_xauthFile->fileName(), "generate", - m_displayName, QString::fromLatin1(xauthProtocol())}); -} - -void SshX11InfoRetriever::emitFailure(const QString &reason) -{ - QTimer::singleShot(0, this, [this, reason] { - emit failure(tr("Could not retrieve X11 authentication cookie: %1").arg(reason)); - deleteLater(); - }); -} - -} // namespace Internal -} // namespace QSsh |