OSDN Git Service

SSH: Implement key re-exchange support.
authorChristian Kandeler <christian.kandeler@nokia.com>
Tue, 19 Apr 2011 08:57:12 +0000 (10:57 +0200)
committerChristian Kandeler <christian.kandeler@nokia.com>
Tue, 19 Apr 2011 12:40:40 +0000 (14:40 +0200)
Correct protocol-wise, but possibly buggy (fails with dropbear).

src/libs/utils/ssh/sshconnection.cpp
src/libs/utils/ssh/sshconnection_p.h

index 27501a6..4ff9c85 100644 (file)
@@ -215,13 +215,13 @@ void SshConnectionPrivate::setupPacketHandlers()
 {
     typedef SshConnectionPrivate This;
 
-    setupPacketHandler(SSH_MSG_KEXINIT, StateList() << SocketConnected,
-        &This::handleKeyExchangeInitPacket);
-    setupPacketHandler(SSH_MSG_KEXDH_REPLY, StateList() << KeyExchangeStarted,
-        &This::handleKeyExchangeReplyPacket);
+    setupPacketHandler(SSH_MSG_KEXINIT, StateList() << SocketConnected
+        << ConnectionEstablished, &This::handleKeyExchangeInitPacket);
+    setupPacketHandler(SSH_MSG_KEXDH_REPLY, StateList() << SocketConnected
+        << ConnectionEstablished, &This::handleKeyExchangeReplyPacket);
 
-    setupPacketHandler(SSH_MSG_NEWKEYS, StateList() << KeyExchangeSuccess,
-        &This::handleNewKeysPacket);
+    setupPacketHandler(SSH_MSG_NEWKEYS, StateList() << SocketConnected
+        << ConnectionEstablished, &This::handleNewKeysPacket);
     setupPacketHandler(SSH_MSG_SERVICE_ACCEPT,
         StateList() << UserAuthServiceRequested,
         &This::handleServiceAcceptPacket);
@@ -267,7 +267,6 @@ void SshConnectionPrivate::setupPacketHandlers()
         &This::handleChannelClose);
 
     setupPacketHandler(SSH_MSG_DISCONNECT, StateList() << SocketConnected
-        << KeyExchangeStarted << KeyExchangeSuccess
         << UserAuthServiceRequested << UserAuthRequested
         << ConnectionEstablished, &This::handleDisconnect);
 
@@ -341,7 +340,8 @@ void SshConnectionPrivate::handleServerId()
     }
 
     m_keyExchange.reset(new SshKeyExchange(m_sendFacility));
-    m_keyExchange->sendKexInitPacket(m_incomingData.left(endOffset));
+    m_serverId = m_incomingData.left(endOffset);
+    m_keyExchange->sendKexInitPacket(m_serverId);
     m_incomingData.remove(0, endOffset + 2);
 }
 
@@ -358,7 +358,7 @@ void SshConnectionPrivate::handlePackets()
 void SshConnectionPrivate::handleCurrentPacket()
 {
     Q_ASSERT(m_incomingPacket.isComplete());
-    Q_ASSERT(m_state == KeyExchangeStarted || !m_ignoreNextPacket);
+    Q_ASSERT(m_keyExchangeState == KeyExchangeStarted || !m_ignoreNextPacket);
 
     if (m_ignoreNextPacket) {
         m_ignoreNextPacket = false;
@@ -381,28 +381,58 @@ void SshConnectionPrivate::handleCurrentPacket()
 
 void SshConnectionPrivate::handleKeyExchangeInitPacket()
 {
+    if (m_keyExchangeState != NoKeyExchange) {
+        throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
+            "Unexpected packet.", tr("Unexpected packet of type %1.")
+            .arg(m_incomingPacket.type()));
+    }
+
+    // Server-initiated re-exchange.
+    if (m_state == ConnectionEstablished) {
+        m_keyExchange.reset(new SshKeyExchange(m_sendFacility));
+        m_keyExchange->sendKexInitPacket(m_serverId);
+    }
+
     // If the server sends a guessed packet, the guess must be wrong,
     // because the algorithms we support requires us to initiate the
     // key exchange.
-    if (m_keyExchange->sendDhInitPacket(m_incomingPacket))
+    if (m_keyExchange->sendDhInitPacket(m_incomingPacket)) {
         m_ignoreNextPacket = true;
-    m_state = KeyExchangeStarted;
+    }
+
+    m_keyExchangeState = KeyExchangeStarted;
 }
 
 void SshConnectionPrivate::handleKeyExchangeReplyPacket()
 {
+    if (m_keyExchangeState != KeyExchangeStarted) {
+        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_state = KeyExchangeSuccess;
+    m_keyExchangeState = KeyExchangeSuccess;
 }
 
 void SshConnectionPrivate::handleNewKeysPacket()
 {
+    if (m_keyExchangeState != KeyExchangeSuccess) {
+        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_sendFacility.sendUserAuthServiceRequestPacket();
-    m_state = UserAuthServiceRequested;
+    m_keyExchangeState = NoKeyExchange;
+
+    if (m_state == SocketConnected) {
+        m_sendFacility.sendUserAuthServiceRequestPacket();
+        m_state = UserAuthServiceRequested;
+    }
 }
 
 void SshConnectionPrivate::handleServiceAcceptPacket()
@@ -595,6 +625,7 @@ void SshConnectionPrivate::connectToHost()
         SLOT(handleSocketDisconnected()));
     connect(&m_timeoutTimer, SIGNAL(timeout()), this, SLOT(handleTimeout()));
     m_state = SocketConnecting;
+    m_keyExchangeState = NoKeyExchange;
     m_timeoutTimer.start(m_connParams.timeout * 1000);
     m_socket->connectToHost(m_connParams.host, m_connParams.port);
 }
index 3aaf643..352dd98 100644 (file)
@@ -63,15 +63,18 @@ enum SshStateInternal {
     SocketUnconnected, // initial and after disconnect
     SocketConnecting, // After connectToHost()
     SocketConnected, // After socket's connected() signal
-    KeyExchangeStarted, // After server's KEXINIT message
-    KeyExchangeSuccess, // After server's DH_REPLY message
     UserAuthServiceRequested,
     UserAuthRequested,
-
     ConnectionEstablished // After service has been started
     // ...
 };
 
+enum SshKeyExchangeState {
+    NoKeyExchange,
+    KeyExchangeStarted,    // After server's KEXINIT message
+    KeyExchangeSuccess     // After server's DH_REPLY message
+};
+
 class SshConnectionPrivate : public QObject
 {
     Q_OBJECT
@@ -147,6 +150,7 @@ private:
 
     QTcpSocket *m_socket;
     SshStateInternal m_state;
+    SshKeyExchangeState m_keyExchangeState;
     SshIncomingPacket m_incomingPacket;
     SshSendFacility m_sendFacility;
     SshChannelManager * const m_channelManager;
@@ -160,6 +164,7 @@ private:
     bool m_ignoreNextPacket;
     SshConnection *m_conn;
     quint64 m_lastInvalidMsgSeqNr;
+    QByteArray m_serverId;
 };
 
 } // namespace Internal