[qt-creator/f21] Add SSH host key verification to built-in SSH client (#1161655)
Sandro Mani
smani at fedoraproject.org
Mon Mar 9 22:01:41 UTC 2015
commit 527b3bba9cf7a4d4948f51a6e012c702888678f6
Author: Sandro Mani <manisandro at gmail.com>
Date: Mon Mar 9 23:01:34 2015 +0100
Add SSH host key verification to built-in SSH client (#1161655)
qt-creator.spec | 8 +-
..._62a83f911365eab71e7260484517ef6c739d5192.patch | 682 +++++++++++++++++++++
2 files changed, 689 insertions(+), 1 deletion(-)
---
diff --git a/qt-creator.spec b/qt-creator.spec
index 5773956..3818d37 100644
--- a/qt-creator.spec
+++ b/qt-creator.spec
@@ -2,7 +2,7 @@
Name: qt-creator
Version: 3.3.2
-Release: 1%{?pre:.%pre}%{?dist}
+Release: 2%{?pre:.%pre}%{?dist}
Summary: Cross-platform IDE for Qt
Group: Development/Tools
@@ -17,6 +17,8 @@ Patch1: 380acb5baa375806af0a081b56d6d1dccd87264f.patch
# Use absolute paths for the specified rpaths, not $ORIGIN-relative paths
# (to fix some /usr/bin/<binary> having rpath $ORIGIN/..)
Patch2: qt-creator_rpath.patch
+# Add SSH host key verification to built-in SSH client (#1161655)
+Patch3: qt-creator_62a83f911365eab71e7260484517ef6c739d5192.patch
# See #1074700
ExcludeArch: %{arm}
@@ -82,6 +84,7 @@ tailored to the needs of Qt developers.
%patch0 -p1
%patch1 -p1
%patch2 -p1
+%patch3 -p1
%build
export QTDIR="%{_qt5_prefix}"
@@ -160,6 +163,9 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :
%changelog
+* Mon Mar 09 2015 Sandro Mani <manisandro at gmail.com> - 3.3.2-2
+- Add SSH host key verification to built-in SSH client (#1161655)
+
* Thu Mar 05 2015 Sandro Mani <manisandro at gmail.com> - 3.3.2-1
- 3.3.2 release
diff --git a/qt-creator_62a83f911365eab71e7260484517ef6c739d5192.patch b/qt-creator_62a83f911365eab71e7260484517ef6c739d5192.patch
new file mode 100644
index 0000000..4d3f5d9
--- /dev/null
+++ b/qt-creator_62a83f911365eab71e7260484517ef6c739d5192.patch
@@ -0,0 +1,682 @@
+From 62a83f911365eab71e7260484517ef6c739d5192 Mon Sep 17 00:00:00 2001
+From: Christian Kandeler <christian.kandeler at theqtcompany.com>
+Date: Wed, 12 Nov 2014 16:50:04 +0100
+Subject: [PATCH] SSH: implement host key checking.
+
+Change-Id: I5f10bd801bb5cf43e58193c41e62d9ea2f9cb645
+Task-number: QTCREATORBUG-13339
+Reviewed-by: Joerg Bornemann <joerg.bornemann at theqtcompany.com>
+---
+ src/libs/ssh/ssh.pro | 6 +-
+ src/libs/ssh/ssh.qbs | 2 +
+ src/libs/ssh/sshconnection.cpp | 9 +-
+ src/libs/ssh/sshconnection.h | 10 ++
+ src/libs/ssh/sshhostkeydatabase.cpp | 120 ++++++++++++++++++++
+ src/libs/ssh/sshhostkeydatabase.h | 73 ++++++++++++
+ src/libs/ssh/sshkeyexchange.cpp | 43 +++++++-
+ src/libs/ssh/sshkeyexchange_p.h | 8 +-
+ .../devicesupport/devicemanager.cpp | 21 ++++
+ .../projectexplorer/devicesupport/devicemanager.h | 3 +
+ .../projectexplorer/devicesupport/idevice.cpp | 10 ++-
+ .../genericlinuxdeviceconfigurationwidget.cpp | 11 ++
+ .../genericlinuxdeviceconfigurationwidget.h | 1 +
+ .../genericlinuxdeviceconfigurationwidget.ui | 11 ++-
+ 14 files changed, 316 insertions(+), 12 deletions(-)
+ create mode 100644 src/libs/ssh/sshhostkeydatabase.cpp
+ create mode 100644 src/libs/ssh/sshhostkeydatabase.h
+
+diff --git a/src/libs/ssh/ssh.pro b/src/libs/ssh/ssh.pro
+index 511d31b..f2b8036 100644
+--- a/src/libs/ssh/ssh.pro
++++ b/src/libs/ssh/ssh.pro
+@@ -28,7 +28,8 @@ SOURCES = $$PWD/sshsendfacility.cpp \
+ $$PWD/sftpfilesystemmodel.cpp \
+ $$PWD/sshkeycreationdialog.cpp \
+ $$PWD/sshinit.cpp \
+- $$PWD/sshdirecttcpiptunnel.cpp
++ $$PWD/sshdirecttcpiptunnel.cpp \
++ $$PWD/sshhostkeydatabase.cpp
+
+ HEADERS = $$PWD/sshsendfacility_p.h \
+ $$PWD/sshremoteprocess.h \
+@@ -64,7 +65,8 @@ HEADERS = $$PWD/sshsendfacility_p.h \
+ $$PWD/ssh_global.h \
+ $$PWD/sshdirecttcpiptunnel_p.h \
+ $$PWD/sshinit_p.h \
+- $$PWD/sshdirecttcpiptunnel.h
++ $$PWD/sshdirecttcpiptunnel.h \
++ $$PWD/sshhostkeydatabase.h
+
+ FORMS = $$PWD/sshkeycreationdialog.ui
+
+diff --git a/src/libs/ssh/ssh.qbs b/src/libs/ssh/ssh.qbs
+index 486fa21..8062f1a 100644
+--- a/src/libs/ssh/ssh.qbs
++++ b/src/libs/ssh/ssh.qbs
+@@ -27,6 +27,8 @@ QtcLibrary {
+ "sshdirecttcpiptunnel.h", "sshdirecttcpiptunnel_p.h", "sshdirecttcpiptunnel.cpp",
+ "ssherrors.h",
+ "sshexception_p.h",
++ "sshhostkeydatabase.cpp",
++ "sshhostkeydatabase.h",
+ "sshincomingpacket_p.h", "sshincomingpacket.cpp",
+ "sshinit_p.h", "sshinit.cpp",
+ "sshkeycreationdialog.cpp", "sshkeycreationdialog.h", "sshkeycreationdialog.ui",
+diff --git a/src/libs/ssh/sshconnection.cpp b/src/libs/ssh/sshconnection.cpp
+index fd4e028..d4dac7a 100644
+--- a/src/libs/ssh/sshconnection.cpp
++++ b/src/libs/ssh/sshconnection.cpp
+@@ -65,7 +65,8 @@ namespace QSsh {
+ const QByteArray ClientId("SSH-2.0-QtCreator\r\n");
+
+ SshConnectionParameters::SshConnectionParameters() :
+- timeout(0), authenticationType(AuthenticationTypePublicKey), port(0)
++ timeout(0), authenticationType(AuthenticationTypePublicKey), port(0),
++ hostKeyCheckingMode(SshHostKeyCheckingNone)
+ {
+ options |= SshIgnoreDefaultProxy;
+ options |= SshEnableStrictConformanceChecks;
+@@ -77,6 +78,7 @@ static inline bool equals(const SshConnectionParameters &p1, const SshConnection
+ && p1.authenticationType == p2.authenticationType
+ && (p1.authenticationType == SshConnectionParameters::AuthenticationTypePassword ?
+ p1.password == p2.password : p1.privateKeyFile == p2.privateKeyFile)
++ && p1.hostKeyCheckingMode == p2.hostKeyCheckingMode
+ && p1.timeout == p2.timeout && p1.port == p2.port;
+ }
+
+@@ -90,7 +92,6 @@ bool operator!=(const SshConnectionParameters &p1, const SshConnectionParameters
+ return !equals(p1, p2);
+ }
+
+-// TODO: Mechanism for checking the host key. First connection to host: save, later: compare
+
+ SshConnection::SshConnection(const SshConnectionParameters &serverInfo, QObject *parent)
+ : QObject(parent)
+@@ -411,7 +412,7 @@ void SshConnectionPrivate::handleServerId()
+ }
+ }
+
+- m_keyExchange.reset(new SshKeyExchange(m_sendFacility));
++ m_keyExchange.reset(new SshKeyExchange(m_connParams, m_sendFacility));
+ m_keyExchange->sendKexInitPacket(m_serverId);
+ m_keyExchangeState = KexInitSent;
+ }
+@@ -460,7 +461,7 @@ void SshConnectionPrivate::handleKeyExchangeInitPacket()
+
+ // Server-initiated re-exchange.
+ if (m_keyExchangeState == NoKeyExchange) {
+- m_keyExchange.reset(new SshKeyExchange(m_sendFacility));
++ m_keyExchange.reset(new SshKeyExchange(m_connParams, m_sendFacility));
+ m_keyExchange->sendKexInitPacket(m_serverId);
+ }
+
+diff --git a/src/libs/ssh/sshconnection.h b/src/libs/ssh/sshconnection.h
+index 31f4953..aa56679 100644
+--- a/src/libs/ssh/sshconnection.h
++++ b/src/libs/ssh/sshconnection.h
+@@ -32,6 +32,7 @@
+ #define SSHCONNECTION_H
+
+ #include "ssherrors.h"
++#include "sshhostkeydatabase.h"
+
+ #include "ssh_global.h"
+
+@@ -56,6 +57,13 @@ enum SshConnectionOption {
+
+ Q_DECLARE_FLAGS(SshConnectionOptions, SshConnectionOption)
+
++enum SshHostKeyCheckingMode {
++ SshHostKeyCheckingNone,
++ SshHostKeyCheckingStrict,
++ SshHostKeyCheckingAllowNoMatch,
++ SshHostKeyCheckingAllowMismatch
++};
++
+ class QSSH_EXPORT SshConnectionParameters
+ {
+ public:
+@@ -78,6 +86,8 @@ public:
+ AuthenticationType authenticationType;
+ quint16 port;
+ SshConnectionOptions options;
++ SshHostKeyCheckingMode hostKeyCheckingMode;
++ SshHostKeyDatabasePtr hostKeyDatabase;
+ };
+
+ QSSH_EXPORT bool operator==(const SshConnectionParameters &p1, const SshConnectionParameters &p2);
+diff --git a/src/libs/ssh/sshhostkeydatabase.cpp b/src/libs/ssh/sshhostkeydatabase.cpp
+new file mode 100644
+index 0000000..c06edd2
+--- /dev/null
++++ b/src/libs/ssh/sshhostkeydatabase.cpp
+@@ -0,0 +1,120 @@
++/****************************************************************************
++**
++** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
++** Contact: http://www.qt-project.org/legal
++**
++** 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 Digia. For licensing terms and
++** conditions see http://www.qt.io/licensing. For further information
++** use the contact form at http://www.qt.io/contact-us.
++**
++** GNU Lesser General Public License Usage
++** Alternatively, this file may be used under the terms of the GNU Lesser
++** General Public License version 2.1 or version 3 as published by the Free
++** Software Foundation and appearing in the file LICENSE.LGPLv21 and
++** LICENSE.LGPLv3 included in the packaging of this file. Please review the
++** following information to ensure the GNU Lesser General Public License
++** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
++** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
++**
++** In addition, as a special exception, Digia gives you certain additional
++** rights. These rights are described in the Digia Qt LGPL Exception
++** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
++**
++****************************************************************************/
++#include "sshhostkeydatabase.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) {
++ qDebug("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.find(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/sshhostkeydatabase.h b/src/libs/ssh/sshhostkeydatabase.h
+new file mode 100644
+index 0000000..8e51953
+--- /dev/null
++++ b/src/libs/ssh/sshhostkeydatabase.h
+@@ -0,0 +1,73 @@
++/****************************************************************************
++**
++** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
++** Contact: http://www.qt-project.org/legal
++**
++** 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 Digia. For licensing terms and
++** conditions see http://www.qt.io/licensing. For further information
++** use the contact form at http://www.qt.io/contact-us.
++**
++** GNU Lesser General Public License Usage
++** Alternatively, this file may be used under the terms of the GNU Lesser
++** General Public License version 2.1 or version 3 as published by the Free
++** Software Foundation and appearing in the file LICENSE.LGPLv21 and
++** LICENSE.LGPLv3 included in the packaging of this file. Please review the
++** following information to ensure the GNU Lesser General Public License
++** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
++** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
++**
++** In addition, as a special exception, Digia gives you certain additional
++** rights. These rights are described in the Digia Qt LGPL Exception
++** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
++**
++****************************************************************************/
++#ifndef SSHHOSTKEYDATABASE_H
++#define SSHHOSTKEYDATABASE_H
++
++#include "ssh_global.h"
++
++#include <QSharedPointer>
++
++QT_BEGIN_NAMESPACE
++class QByteArray;
++class QString;
++QT_END_NAMESPACE
++
++namespace QSsh {
++class SshHostKeyDatabase;
++typedef QSharedPointer<SshHostKeyDatabase> SshHostKeyDatabasePtr;
++
++class QSSH_EXPORT SshHostKeyDatabase
++{
++ friend class QSharedPointer<SshHostKeyDatabase>; // To give create() access to our constructor.
++
++public:
++ enum KeyLookupResult {
++ KeyLookupMatch,
++ KeyLookupNoMatch,
++ KeyLookupMismatch
++ };
++
++ ~SshHostKeyDatabase();
++
++ 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);
++
++private:
++ SshHostKeyDatabase();
++
++ class SshHostKeyDatabasePrivate;
++ SshHostKeyDatabasePrivate * const d;
++};
++
++} // namespace QSsh
++
++#endif // Include guard.
+diff --git a/src/libs/ssh/sshkeyexchange.cpp b/src/libs/ssh/sshkeyexchange.cpp
+index ec9dca1..f427b12 100644
+--- a/src/libs/ssh/sshkeyexchange.cpp
++++ b/src/libs/ssh/sshkeyexchange.cpp
+@@ -76,8 +76,9 @@ namespace {
+
+ } // anonymous namespace
+
+-SshKeyExchange::SshKeyExchange(SshSendFacility &sendFacility)
+- : m_sendFacility(sendFacility)
++SshKeyExchange::SshKeyExchange(const SshConnectionParameters &connParams,
++ SshSendFacility &sendFacility)
++ : m_connParams(connParams), m_sendFacility(sendFacility)
+ {
+ }
+
+@@ -210,8 +211,46 @@ void SshKeyExchange::sendNewKeysPacket(const SshIncomingPacket &dhReply,
+ "Invalid signature in SSH_MSG_KEXDH_REPLY packet.");
+ }
+
++ checkHostKey(reply.k_s);
++
+ m_sendFacility.sendNewKeysPacket();
+ }
+
++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
+index d84fc57..440c3cd 100644
+--- a/src/libs/ssh/sshkeyexchange_p.h
++++ b/src/libs/ssh/sshkeyexchange_p.h
+@@ -31,6 +31,8 @@
+ #ifndef SSHKEYEXCHANGE_P_H
+ #define SSHKEYEXCHANGE_P_H
+
++#include "sshconnection.h"
++
+ #include <QByteArray>
+ #include <QScopedPointer>
+
+@@ -48,7 +50,7 @@ class SshIncomingPacket;
+ class SshKeyExchange
+ {
+ public:
+- SshKeyExchange(SshSendFacility &sendFacility);
++ SshKeyExchange(const SshConnectionParameters &connParams, SshSendFacility &sendFacility);
+ ~SshKeyExchange();
+
+ void sendKexInitPacket(const QByteArray &serverId);
+@@ -68,6 +70,9 @@ public:
+ QByteArray hMacAlgoServerToClient() const { return m_s2cHMacAlgo; }
+
+ private:
++ void checkHostKey(const QByteArray &hostKey);
++ Q_NORETURN void throwHostKeyException();
++
+ QByteArray m_serverId;
+ QByteArray m_clientKexInitPayload;
+ QByteArray m_serverKexInitPayload;
+@@ -80,6 +85,7 @@ private:
+ QByteArray m_c2sHMacAlgo;
+ QByteArray m_s2cHMacAlgo;
+ QScopedPointer<Botan::HashFunction> m_hash;
++ const SshConnectionParameters m_connParams;
+ SshSendFacility &m_sendFacility;
+ };
+
+diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
+index 54bc180..24106b2 100644
+--- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
++++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
+@@ -32,9 +32,11 @@
+ #include "idevicefactory.h"
+
+ #include <coreplugin/icore.h>
++#include <coreplugin/messagemanager.h>
+ #include <extensionsystem/pluginmanager.h>
+ #include <projectexplorer/project.h>
+ #include <projectexplorer/projectexplorerconstants.h>
++#include <ssh/sshhostkeydatabase.h>
+ #include <utils/qtcassert.h>
+ #include <utils/fileutils.h>
+ #include <utils/persistentsettings.h>
+@@ -75,6 +77,7 @@ public:
+ static DeviceManager *clonedInstance;
+ QList<IDevice::Ptr> devices;
+ QHash<Core::Id, Core::Id> defaultDevices;
++ QSsh::SshHostKeyDatabasePtr hostKeyDatabase;
+
+ Utils::PersistentSettingsWriter *writer;
+ };
+@@ -136,6 +139,7 @@ void DeviceManager::save()
+ QVariantMap data;
+ data.insert(QLatin1String(DeviceManagerKey), toMap());
+ d->writer->save(data, Core::ICore::mainWindow());
++ d->hostKeyDatabase->store(hostKeysFilePath());
+ }
+
+ void DeviceManager::load()
+@@ -308,6 +312,11 @@ bool DeviceManager::isLoaded() const
+ return d->writer;
+ }
+
++QSsh::SshHostKeyDatabasePtr DeviceManager::hostKeyDatabase() const
++{
++ return d->hostKeyDatabase;
++}
++
+ void DeviceManager::setDefaultDevice(Core::Id id)
+ {
+ QTC_ASSERT(this != instance(), return);
+@@ -343,6 +352,13 @@ DeviceManager::DeviceManager(bool isInstance) : d(new DeviceManagerPrivate)
+ if (isInstance) {
+ QTC_ASSERT(!m_instance, return);
+ m_instance = this;
++ d->hostKeyDatabase = QSsh::SshHostKeyDatabasePtr::create();
++ const QString keyFilePath = hostKeysFilePath();
++ if (QFileInfo(keyFilePath).exists()) {
++ QString error;
++ if (!d->hostKeyDatabase->load(keyFilePath, &error))
++ Core::MessageManager::write(error);
++ }
+ connect(Core::ICore::instance(), SIGNAL(saveSettingsRequested()), SLOT(save()));
+ }
+ }
+@@ -415,6 +431,11 @@ IDevice::ConstPtr DeviceManager::fromRawPointer(const IDevice *device) const
+ return fromRawPointer(const_cast<IDevice *>(device));
+ }
+
++QString DeviceManager::hostKeysFilePath()
++{
++ return settingsFilePath(QLatin1String("/ssh-hostkeys")).toString();
++}
++
+ } // namespace ProjectExplorer
+
+
+diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.h b/src/plugins/projectexplorer/devicesupport/devicemanager.h
+index 76dc912..51a9612 100644
+--- a/src/plugins/projectexplorer/devicesupport/devicemanager.h
++++ b/src/plugins/projectexplorer/devicesupport/devicemanager.h
+@@ -36,6 +36,7 @@
+
+ #include <QObject>
+
++namespace QSsh { class SshHostKeyDatabase; }
+ namespace Utils { class FileName; }
+
+ namespace ProjectExplorer {
+@@ -105,6 +106,8 @@ private:
+ IDevice::Ptr fromRawPointer(IDevice *device) const;
+ IDevice::ConstPtr fromRawPointer(const IDevice *device) const;
+
++ static QString hostKeysFilePath();
++ QSharedPointer<QSsh::SshHostKeyDatabase> hostKeyDatabase() const;
+ static Utils::FileName settingsFilePath(const QString &extension);
+ static Utils::FileName systemSettingsFilePath(const QString &deviceFileRelativePath);
+ static void copy(const DeviceManager *source, DeviceManager *target, bool deep);
+diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp
+index 366e6f5..a8d69f4 100644
+--- a/src/plugins/projectexplorer/devicesupport/idevice.cpp
++++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp
+@@ -123,6 +123,7 @@ const char AuthKey[] = "Authentication";
+ const char KeyFileKey[] = "KeyFile";
+ const char PasswordKey[] = "Password";
+ const char TimeoutKey[] = "Timeout";
++const char HostKeyCheckingKey[] = "HostKeyChecking";
+
+ const char DebugServerKey[] = "DebugServerKey";
+
+@@ -161,7 +162,9 @@ PortsGatheringMethod::~PortsGatheringMethod() { }
+ DeviceTester::DeviceTester(QObject *parent) : QObject(parent) { }
+
+ IDevice::IDevice() : d(new Internal::IDevicePrivate)
+-{ }
++{
++ d->sshParameters.hostKeyDatabase = DeviceManager::instance()->hostKeyDatabase();
++}
+
+ IDevice::IDevice(Core::Id type, Origin origin, MachineType machineType, Core::Id id)
+ : d(new Internal::IDevicePrivate)
+@@ -171,6 +174,7 @@ IDevice::IDevice(Core::Id type, Origin origin, MachineType machineType, Core::Id
+ d->machineType = machineType;
+ QTC_CHECK(origin == ManuallyAdded || id.isValid());
+ d->id = id.isValid() ? id : newId();
++ d->sshParameters.hostKeyDatabase = DeviceManager::instance()->hostKeyDatabase();
+ }
+
+ IDevice::IDevice(const IDevice &other) : d(new Internal::IDevicePrivate)
+@@ -322,6 +326,8 @@ void IDevice::fromMap(const QVariantMap &map)
+ d->sshParameters.password = map.value(QLatin1String(PasswordKey)).toString();
+ d->sshParameters.privateKeyFile = map.value(QLatin1String(KeyFileKey), defaultPrivateKeyFilePath()).toString();
+ d->sshParameters.timeout = map.value(QLatin1String(TimeoutKey), DefaultTimeout).toInt();
++ d->sshParameters.hostKeyCheckingMode = static_cast<QSsh::SshHostKeyCheckingMode>
++ (map.value(QLatin1String(HostKeyCheckingKey), QSsh::SshHostKeyCheckingNone).toInt());
+
+ d->freePorts = Utils::PortList::fromString(map.value(QLatin1String(PortsSpecKey),
+ QLatin1String("10000-10100")).toString());
+@@ -353,6 +359,7 @@ QVariantMap IDevice::toMap() const
+ map.insert(QLatin1String(PasswordKey), d->sshParameters.password);
+ map.insert(QLatin1String(KeyFileKey), d->sshParameters.privateKeyFile);
+ map.insert(QLatin1String(TimeoutKey), d->sshParameters.timeout);
++ map.insert(QLatin1String(HostKeyCheckingKey), d->sshParameters.hostKeyCheckingMode);
+
+ map.insert(QLatin1String(PortsSpecKey), d->freePorts.toString());
+ map.insert(QLatin1String(VersionKey), d->version);
+@@ -392,6 +399,7 @@ QSsh::SshConnectionParameters IDevice::sshParameters() const
+ void IDevice::setSshParameters(const QSsh::SshConnectionParameters &sshParameters)
+ {
+ d->sshParameters = sshParameters;
++ d->sshParameters.hostKeyDatabase = DeviceManager::instance()->hostKeyDatabase();
+ }
+
+ QString IDevice::qmlProfilerHost() const
+diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp
+index 36e51dd..de06668 100644
+--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp
++++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp
+@@ -63,6 +63,8 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget(
+ connect(m_ui->portsLineEdit, SIGNAL(editingFinished()), this, SLOT(handleFreePortsChanged()));
+ connect(m_ui->createKeyButton, SIGNAL(clicked()), SLOT(createNewKey()));
+ connect(m_ui->gdbServerLineEdit, SIGNAL(editingFinished()), SLOT(gdbServerEditingFinished()));
++ connect(m_ui->hostKeyCheckBox, &QCheckBox::toggled, this,
++ &GenericLinuxDeviceConfigurationWidget::hostKeyCheckingChanged);
+
+ initGui();
+ }
+@@ -158,6 +160,14 @@ void GenericLinuxDeviceConfigurationWidget::createNewKey()
+ setPrivateKey(dialog.privateKeyFilePath());
+ }
+
++void GenericLinuxDeviceConfigurationWidget::hostKeyCheckingChanged(bool doCheck)
++{
++ SshConnectionParameters sshParams = device()->sshParameters();
++ sshParams.hostKeyCheckingMode
++ = doCheck ? QSsh::SshHostKeyCheckingAllowNoMatch : QSsh::SshHostKeyCheckingNone;
++ device()->setSshParameters(sshParams);
++}
++
+ void GenericLinuxDeviceConfigurationWidget::updateDeviceFromUi()
+ {
+ hostNameEditingFinished();
+@@ -200,6 +210,7 @@ void GenericLinuxDeviceConfigurationWidget::initGui()
+ m_ui->timeoutSpinBox->setValue(sshParams.timeout);
+ m_ui->hostLineEdit->setEnabled(!device()->isAutoDetected());
+ m_ui->sshPortSpinBox->setEnabled(!device()->isAutoDetected());
++ m_ui->hostKeyCheckBox->setChecked(sshParams.hostKeyCheckingMode != SshHostKeyCheckingNone);
+
+ m_ui->hostLineEdit->setText(sshParams.host);
+ m_ui->sshPortSpinBox->setValue(sshParams.port);
+diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h
+index 7824454..65b7df3 100644
+--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h
++++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h
+@@ -62,6 +62,7 @@ private slots:
+ void handleFreePortsChanged();
+ void setPrivateKey(const QString &path);
+ void createNewKey();
++ void hostKeyCheckingChanged(bool doCheck);
+
+ private:
+ void updateDeviceFromUi();
+diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.ui b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.ui
+index 2b38ed1..3074e46 100644
+--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.ui
++++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.ui
+@@ -6,8 +6,8 @@
+ <rect>
+ <x>0</x>
+ <y>0</y>
+- <width>393</width>
+- <height>321</height>
++ <width>556</width>
++ <height>309</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+@@ -123,6 +123,13 @@
+ </property>
+ </widget>
+ </item>
++ <item>
++ <widget class="QCheckBox" name="hostKeyCheckBox">
++ <property name="text">
++ <string>&Check host key</string>
++ </property>
++ </widget>
++ </item>
+ </layout>
+ </item>
+ <item row="3" column="0">
+--
+1.7.1
+
More information about the scm-commits
mailing list