From 5e24db4f2f3cae288b0c0b980faaa51e1846787b Mon Sep 17 00:00:00 2001
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
Date: Thu, 6 Nov 2014 04:30:26 +0100
Subject: [PATCH] Verify host key

---
 3rdparty/qssh/sshconnection.cpp  |  4 ++--
 3rdparty/qssh/sshconnection.h    |  1 +
 3rdparty/qssh/sshkeyexchange.cpp | 17 +++++++++++++++--
 3rdparty/qssh/sshkeyexchange_p.h |  3 ++-
 CertificateStore.cpp             |  2 ++
 5 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/3rdparty/qssh/sshconnection.cpp b/3rdparty/qssh/sshconnection.cpp
index 302ea82..6d01e17 100644
--- a/3rdparty/qssh/sshconnection.cpp
+++ b/3rdparty/qssh/sshconnection.cpp
@@ -411,7 +411,7 @@ void SshConnectionPrivate::handleServerId()
         }
     }
 
-    m_keyExchange.reset(new SshKeyExchange(m_sendFacility));
+    m_keyExchange.reset(new SshKeyExchange(m_sendFacility, m_connParams.expectedHostKey));
     m_keyExchange->sendKexInitPacket(m_serverId);
     m_keyExchangeState = KexInitSent;
 }
@@ -460,7 +460,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_sendFacility, m_connParams.expectedHostKey));
         m_keyExchange->sendKexInitPacket(m_serverId);
     }
 
diff --git a/3rdparty/qssh/sshconnection.h b/3rdparty/qssh/sshconnection.h
index 31f4953..8ba4bde 100644
--- a/3rdparty/qssh/sshconnection.h
+++ b/3rdparty/qssh/sshconnection.h
@@ -78,6 +78,7 @@ public:
     AuthenticationType authenticationType;
     quint16 port;
     SshConnectionOptions options;
+    QByteArray expectedHostKey;
 };
 
 QSSH_EXPORT bool operator==(const SshConnectionParameters &p1, const SshConnectionParameters &p2);
diff --git a/3rdparty/qssh/sshkeyexchange.cpp b/3rdparty/qssh/sshkeyexchange.cpp
index ec9dca1..d3b9766 100644
--- a/3rdparty/qssh/sshkeyexchange.cpp
+++ b/3rdparty/qssh/sshkeyexchange.cpp
@@ -76,8 +76,9 @@ namespace {
 
 } // anonymous namespace
 
-SshKeyExchange::SshKeyExchange(SshSendFacility &sendFacility)
-    : m_sendFacility(sendFacility)
+SshKeyExchange::SshKeyExchange(SshSendFacility &sendFacility, const QByteArray &expectedHostKey)
+    : m_sendFacility(sendFacility),
+      m_expectedHostKey(expectedHostKey)
 {
 }
 
@@ -186,21 +187,33 @@ void SshKeyExchange::sendNewKeysPacket(const SshIncomingPacket &dhReply,
 
     QScopedPointer<Public_Key> sigKey;
     QScopedPointer<PK_Verifier> verifier;
+    QByteArray discoveredHostKey;
     if (m_serverHostKeyAlgo == SshCapabilities::PubKeyDss) {
         const DL_Group group(reply.parameters.at(0), reply.parameters.at(1),
             reply.parameters.at(2));
         DSA_PublicKey * const dsaKey
             = new DSA_PublicKey(group, reply.parameters.at(3));
+        discoveredHostKey = AbstractSshPacket::encodeString("ssh-dss")
+                     + AbstractSshPacket::encodeMpInt(dsaKey->group_p())
+                     + AbstractSshPacket::encodeMpInt(dsaKey->group_q())
+                     + AbstractSshPacket::encodeMpInt(dsaKey->group_g())
+                     + AbstractSshPacket::encodeMpInt(dsaKey->get_y());
         sigKey.reset(dsaKey);
         verifier.reset(new PK_Verifier(*dsaKey, botanEmsaAlgoName(SshCapabilities::PubKeyDss)));
     } else if (m_serverHostKeyAlgo == SshCapabilities::PubKeyRsa) {
         RSA_PublicKey * const rsaKey
             = new RSA_PublicKey(reply.parameters.at(1), reply.parameters.at(0));
+        discoveredHostKey = AbstractSshPacket::encodeString("ssh-rsa")
+                     + AbstractSshPacket::encodeMpInt(rsaKey->get_e())
+                     + AbstractSshPacket::encodeMpInt(rsaKey->get_n());
         sigKey.reset(rsaKey);
         verifier.reset(new PK_Verifier(*rsaKey, botanEmsaAlgoName(SshCapabilities::PubKeyRsa)));
     } else {
         Q_ASSERT(!"Impossible: Neither DSS nor RSA!");
     }
+    if (!m_expectedHostKey.isEmpty() && m_expectedHostKey != discoveredHostKey)
+            throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE,
+                "Unrecognized server host key.");
     const byte * const botanH = convertByteArray(m_h);
     const Botan::byte * const botanSig
         = convertByteArray(reply.signatureBlob);
diff --git a/3rdparty/qssh/sshkeyexchange_p.h b/3rdparty/qssh/sshkeyexchange_p.h
index d84fc57..346ceb3 100644
--- a/3rdparty/qssh/sshkeyexchange_p.h
+++ b/3rdparty/qssh/sshkeyexchange_p.h
@@ -48,7 +48,7 @@ class SshIncomingPacket;
 class SshKeyExchange
 {
 public:
-    SshKeyExchange(SshSendFacility &sendFacility);
+    SshKeyExchange(SshSendFacility &sendFacility, const QByteArray &expectedHostKey = QByteArray());
     ~SshKeyExchange();
 
     void sendKexInitPacket(const QByteArray &serverId);
@@ -81,6 +81,7 @@ private:
     QByteArray m_s2cHMacAlgo;
     QScopedPointer<Botan::HashFunction> m_hash;
     SshSendFacility &m_sendFacility;
+    QByteArray m_expectedHostKey;
 };
 
 } // namespace Internal
-- 
2.1.3

