1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "sshincomingpacket_p.h"
36 #include "sshcapabilities_p.h"
41 const QByteArray SshIncomingPacket::ExitStatusType("exit-status");
42 const QByteArray SshIncomingPacket::ExitSignalType("exit-signal");
44 SshIncomingPacket::SshIncomingPacket() : m_serverSeqNr(0) { }
46 quint32 SshIncomingPacket::cipherBlockSize() const
48 return qMax(m_decrypter.cipherBlockSize(), 8U);
51 quint32 SshIncomingPacket::macLength() const
53 return m_decrypter.macLength();
56 void SshIncomingPacket::recreateKeys(const SshKeyExchange &keyExchange)
58 m_decrypter.recreateKeys(keyExchange);
61 void SshIncomingPacket::reset()
65 m_decrypter.clearKeys();
68 void SshIncomingPacket::consumeData(QByteArray &newData)
70 #ifdef CREATOR_SSH_DEBUG
71 qDebug("%s: current data size = %d, new data size = %d",
72 Q_FUNC_INFO, m_data.size(), newData.size());
75 if (isComplete() || newData.isEmpty())
79 * Until we have reached the minimum packet size, we cannot decrypt the
82 const quint32 minSize = minPacketSize();
83 if (currentDataSize() < minSize) {
85 = qMin<quint32>(minSize - currentDataSize(), newData.size());
86 moveFirstBytes(m_data, newData, bytesToTake);
87 #ifdef CREATOR_SSH_DEBUG
88 qDebug("Took %d bytes from new data", bytesToTake);
90 if (currentDataSize() < minSize)
95 = qMin<quint32>(length() + 4 + macLength() - currentDataSize(),
97 moveFirstBytes(m_data, newData, bytesToTake);
98 #ifdef CREATOR_SSH_DEBUG
99 qDebug("Took %d bytes from new data", bytesToTake);
102 #ifdef CREATOR_SSH_DEBUG
103 qDebug("Message complete. Overall size: %u, payload size: %u",
104 m_data.size(), m_length - paddingLength() - 1);
111 void SshIncomingPacket::decrypt()
113 Q_ASSERT(isComplete());
114 const quint32 netDataLength = length() + 4;
115 m_decrypter.decrypt(m_data, cipherBlockSize(),
116 netDataLength - cipherBlockSize());
117 const QByteArray &mac = m_data.mid(netDataLength, macLength());
118 if (mac != generateMac(m_decrypter, m_serverSeqNr)) {
119 throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_MAC_ERROR,
120 "Message authentication failed.");
124 void SshIncomingPacket::moveFirstBytes(QByteArray &target, QByteArray &source,
127 target.append(source.left(n));
131 SshKeyExchangeInit SshIncomingPacket::extractKeyExchangeInitData() const
133 Q_ASSERT(isComplete());
134 Q_ASSERT(type() == SSH_MSG_KEXINIT);
136 SshKeyExchangeInit exchangeData;
138 quint32 offset = TypeOffset + 1;
139 std::memcpy(exchangeData.cookie, &m_data.constData()[offset],
140 sizeof exchangeData.cookie);
141 offset += sizeof exchangeData.cookie;
142 exchangeData.keyAlgorithms
143 = SshPacketParser::asNameList(m_data, &offset);
144 exchangeData.serverHostKeyAlgorithms
145 = SshPacketParser::asNameList(m_data, &offset);
146 exchangeData.encryptionAlgorithmsClientToServer
147 = SshPacketParser::asNameList(m_data, &offset);
148 exchangeData.encryptionAlgorithmsServerToClient
149 = SshPacketParser::asNameList(m_data, &offset);
150 exchangeData.macAlgorithmsClientToServer
151 = SshPacketParser::asNameList(m_data, &offset);
152 exchangeData.macAlgorithmsServerToClient
153 = SshPacketParser::asNameList(m_data, &offset);
154 exchangeData.compressionAlgorithmsClientToServer
155 = SshPacketParser::asNameList(m_data, &offset);
156 exchangeData.compressionAlgorithmsServerToClient
157 = SshPacketParser::asNameList(m_data, &offset);
158 exchangeData.languagesClientToServer
159 = SshPacketParser::asNameList(m_data, &offset);
160 exchangeData.languagesServerToClient
161 = SshPacketParser::asNameList(m_data, &offset);
162 exchangeData.firstKexPacketFollows
163 = SshPacketParser::asBool(m_data, &offset);
164 } catch (SshPacketParseException &) {
165 throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
166 "Key exchange failed: Server sent invalid SSH_MSG_KEXINIT packet.");
171 SshKeyExchangeReply SshIncomingPacket::extractKeyExchangeReply(const QByteArray &pubKeyAlgo) const
173 Q_ASSERT(isComplete());
174 Q_ASSERT(type() == SSH_MSG_KEXDH_REPLY);
177 SshKeyExchangeReply replyData;
178 quint32 offset = TypeOffset + 1;
179 const quint32 k_sLength
180 = SshPacketParser::asUint32(m_data, &offset);
181 if (offset + k_sLength > currentDataSize())
182 throw SshPacketParseException();
183 replyData.k_s = m_data.mid(offset - 4, k_sLength + 4);
184 if (SshPacketParser::asString(m_data, &offset) != pubKeyAlgo)
185 throw SshPacketParseException();
187 // DSS: p and q, RSA: e and n
188 replyData.parameters << SshPacketParser::asBigInt(m_data, &offset);
189 replyData.parameters << SshPacketParser::asBigInt(m_data, &offset);
192 if (pubKeyAlgo == SshCapabilities::PubKeyDss) {
193 replyData.parameters << SshPacketParser::asBigInt(m_data, &offset);
194 replyData.parameters << SshPacketParser::asBigInt(m_data, &offset);
197 replyData.f = SshPacketParser::asBigInt(m_data, &offset);
199 if (SshPacketParser::asString(m_data, &offset) != pubKeyAlgo)
200 throw SshPacketParseException();
201 replyData.signatureBlob = SshPacketParser::asString(m_data, &offset);
203 } catch (SshPacketParseException &) {
204 throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
205 "Key exchange failed: "
206 "Server sent invalid SSH_MSG_KEXDH_REPLY packet.");
210 SshDisconnect SshIncomingPacket::extractDisconnect() const
212 Q_ASSERT(isComplete());
213 Q_ASSERT(type() == SSH_MSG_DISCONNECT);
217 quint32 offset = TypeOffset + 1;
218 msg.reasonCode = SshPacketParser::asUint32(m_data, &offset);
219 msg.description = SshPacketParser::asUserString(m_data, &offset);
220 msg.language = SshPacketParser::asString(m_data, &offset);
221 } catch (SshPacketParseException &) {
222 throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
223 "Invalid SSH_MSG_DISCONNECT.");
229 SshUserAuthBanner SshIncomingPacket::extractUserAuthBanner() const
231 Q_ASSERT(isComplete());
232 Q_ASSERT(type() == SSH_MSG_USERAUTH_BANNER);
235 SshUserAuthBanner msg;
236 quint32 offset = TypeOffset + 1;
237 msg.message = SshPacketParser::asUserString(m_data, &offset);
238 msg.language = SshPacketParser::asString(m_data, &offset);
240 } catch (SshPacketParseException &) {
241 throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
242 "Invalid SSH_MSG_USERAUTH_BANNER.");
246 SshDebug SshIncomingPacket::extractDebug() const
248 Q_ASSERT(isComplete());
249 Q_ASSERT(type() == SSH_MSG_DEBUG);
253 quint32 offset = TypeOffset + 1;
254 msg.display = SshPacketParser::asBool(m_data, &offset);
255 msg.message = SshPacketParser::asUserString(m_data, &offset);
256 msg.language = SshPacketParser::asString(m_data, &offset);
258 } catch (SshPacketParseException &) {
259 throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
260 "Invalid SSH_MSG_USERAUTH_BANNER.");
264 SshChannelOpenFailure SshIncomingPacket::extractChannelOpenFailure() const
266 Q_ASSERT(isComplete());
267 Q_ASSERT(type() == SSH_MSG_CHANNEL_OPEN_FAILURE);
269 SshChannelOpenFailure openFailure;
271 quint32 offset = TypeOffset + 1;
272 openFailure.localChannel = SshPacketParser::asUint32(m_data, &offset);
273 openFailure.reasonCode = SshPacketParser::asUint32(m_data, &offset);
274 openFailure.reasonString = SshPacketParser::asString(m_data, &offset);
275 openFailure.language = SshPacketParser::asString(m_data, &offset);
276 } catch (SshPacketParseException &) {
277 throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
278 "Server sent invalid SSH_MSG_CHANNEL_OPEN_FAILURE packet.");
283 SshChannelOpenConfirmation SshIncomingPacket::extractChannelOpenConfirmation() const
285 Q_ASSERT(isComplete());
286 Q_ASSERT(type() == SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
288 SshChannelOpenConfirmation confirmation;
290 quint32 offset = TypeOffset + 1;
291 confirmation.localChannel = SshPacketParser::asUint32(m_data, &offset);
292 confirmation.remoteChannel = SshPacketParser::asUint32(m_data, &offset);
293 confirmation.remoteWindowSize = SshPacketParser::asUint32(m_data, &offset);
294 confirmation.remoteMaxPacketSize = SshPacketParser::asUint32(m_data, &offset);
295 } catch (SshPacketParseException &) {
296 throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
297 "Server sent invalid SSH_MSG_CHANNEL_OPEN_CONFIRMATION packet.");
302 SshChannelWindowAdjust SshIncomingPacket::extractWindowAdjust() const
304 Q_ASSERT(isComplete());
305 Q_ASSERT(type() == SSH_MSG_CHANNEL_WINDOW_ADJUST);
307 SshChannelWindowAdjust adjust;
309 quint32 offset = TypeOffset + 1;
310 adjust.localChannel = SshPacketParser::asUint32(m_data, &offset);
311 adjust.bytesToAdd = SshPacketParser::asUint32(m_data, &offset);
312 } catch (SshPacketParseException &) {
313 throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
314 "Invalid SSH_MSG_CHANNEL_WINDOW_ADJUST packet.");
319 SshChannelData SshIncomingPacket::extractChannelData() const
321 Q_ASSERT(isComplete());
322 Q_ASSERT(type() == SSH_MSG_CHANNEL_DATA);
326 quint32 offset = TypeOffset + 1;
327 data.localChannel = SshPacketParser::asUint32(m_data, &offset);
328 data.data = SshPacketParser::asString(m_data, &offset);
329 } catch (SshPacketParseException &) {
330 throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
331 "Invalid SSH_MSG_CHANNEL_DATA packet.");
336 SshChannelExtendedData SshIncomingPacket::extractChannelExtendedData() const
338 Q_ASSERT(isComplete());
339 Q_ASSERT(type() == SSH_MSG_CHANNEL_EXTENDED_DATA);
341 SshChannelExtendedData data;
343 quint32 offset = TypeOffset + 1;
344 data.localChannel = SshPacketParser::asUint32(m_data, &offset);
345 data.type = SshPacketParser::asUint32(m_data, &offset);
346 data.data = SshPacketParser::asString(m_data, &offset);
347 } catch (SshPacketParseException &) {
348 throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
349 "Invalid SSH_MSG_CHANNEL_EXTENDED_DATA packet.");
354 SshChannelExitStatus SshIncomingPacket::extractChannelExitStatus() const
356 Q_ASSERT(isComplete());
357 Q_ASSERT(type() == SSH_MSG_CHANNEL_REQUEST);
359 SshChannelExitStatus exitStatus;
361 quint32 offset = TypeOffset + 1;
362 exitStatus.localChannel = SshPacketParser::asUint32(m_data, &offset);
363 const QByteArray &type = SshPacketParser::asString(m_data, &offset);
364 Q_ASSERT(type == ExitStatusType);
365 if (SshPacketParser::asBool(m_data, &offset))
366 throw SshPacketParseException();
367 exitStatus.exitStatus = SshPacketParser::asUint32(m_data, &offset);
368 } catch (SshPacketParseException &) {
369 throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
370 "Invalid exit-status packet.");
375 SshChannelExitSignal SshIncomingPacket::extractChannelExitSignal() const
377 Q_ASSERT(isComplete());
378 Q_ASSERT(type() == SSH_MSG_CHANNEL_REQUEST);
380 SshChannelExitSignal exitSignal;
382 quint32 offset = TypeOffset + 1;
383 exitSignal.localChannel = SshPacketParser::asUint32(m_data, &offset);
384 const QByteArray &type = SshPacketParser::asString(m_data, &offset);
385 Q_ASSERT(type == ExitSignalType);
386 if (SshPacketParser::asBool(m_data, &offset))
387 throw SshPacketParseException();
388 exitSignal.signal = SshPacketParser::asString(m_data, &offset);
389 exitSignal.coreDumped = SshPacketParser::asBool(m_data, &offset);
390 exitSignal.error = SshPacketParser::asUserString(m_data, &offset);
391 exitSignal.language = SshPacketParser::asString(m_data, &offset);
392 } catch (SshPacketParseException &) {
393 throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
394 "Invalid exit-signal packet.");
399 quint32 SshIncomingPacket::extractRecipientChannel() const
401 Q_ASSERT(isComplete());
404 quint32 offset = TypeOffset + 1;
405 return SshPacketParser::asUint32(m_data, &offset);
406 } catch (SshPacketParseException &) {
407 throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
408 "Server sent invalid packet.");
412 QByteArray SshIncomingPacket::extractChannelRequestType() const
414 Q_ASSERT(isComplete());
415 Q_ASSERT(type() == SSH_MSG_CHANNEL_REQUEST);
418 quint32 offset = TypeOffset + 1;
419 SshPacketParser::asUint32(m_data, &offset);
420 return SshPacketParser::asString(m_data, &offset);
421 } catch (SshPacketParseException &) {
422 throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
423 "Invalid SSH_MSG_CHANNEL_REQUEST packet.");
427 void SshIncomingPacket::calculateLength() const
429 Q_ASSERT(currentDataSize() >= minPacketSize());
430 #ifdef CREATOR_SSH_DEBUG
431 qDebug("Length field before decryption: %d-%d-%d-%d", m_data.at(0) & 0xff,
432 m_data.at(1) & 0xff, m_data.at(2) & 0xff, m_data.at(3) & 0xff);
434 m_decrypter.decrypt(m_data, 0, cipherBlockSize());
435 #ifdef CREATOR_SSH_DEBUG
436 qDebug("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);
437 qDebug("message type = %d", m_data.at(TypeOffset));
439 m_length = SshPacketParser::asUint32(m_data, static_cast<quint32>(0));
440 #ifdef CREATOR_SSH_DEBUG
441 qDebug("decrypted length is %u", m_length);
445 } // namespace Internal