OSDN Git Service

It's 2011 now.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / coreplugin / ssh / sshincomingpacket.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
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
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
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.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "sshincomingpacket_p.h"
35
36 #include "sshcapabilities_p.h"
37
38 namespace Core {
39 namespace Internal {
40
41 const QByteArray SshIncomingPacket::ExitStatusType("exit-status");
42 const QByteArray SshIncomingPacket::ExitSignalType("exit-signal");
43
44 SshIncomingPacket::SshIncomingPacket() : m_serverSeqNr(0) { }
45
46 quint32 SshIncomingPacket::cipherBlockSize() const
47 {
48     return qMax(m_decrypter.cipherBlockSize(), 8U);
49 }
50
51 quint32 SshIncomingPacket::macLength() const
52 {
53     return m_decrypter.macLength();
54 }
55
56 void SshIncomingPacket::recreateKeys(const SshKeyExchange &keyExchange)
57 {
58     m_decrypter.recreateKeys(keyExchange);
59 }
60
61 void SshIncomingPacket::reset()
62 {
63     clear();
64     m_serverSeqNr = 0;
65     m_decrypter.clearKeys();
66 }
67
68 void SshIncomingPacket::consumeData(QByteArray &newData)
69 {
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());
73 #endif
74
75     if (isComplete() || newData.isEmpty())
76         return;
77
78     /*
79      * Until we have reached the minimum packet size, we cannot decrypt the
80      * length field.
81      */
82     const quint32 minSize = minPacketSize();
83     if (currentDataSize() < minSize) {
84         const int bytesToTake
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);
89 #endif
90         if (currentDataSize() < minSize)
91             return;
92     }
93
94     const int bytesToTake
95         = qMin<quint32>(length() + 4 + macLength() - currentDataSize(),
96               newData.size());
97     moveFirstBytes(m_data, newData, bytesToTake);
98 #ifdef CREATOR_SSH_DEBUG
99     qDebug("Took %d bytes from new data", bytesToTake);
100 #endif
101     if (isComplete()) {
102 #ifdef CREATOR_SSH_DEBUG
103         qDebug("Message complete. Overall size: %u, payload size: %u",
104             m_data.size(), m_length - paddingLength() - 1);
105 #endif
106         decrypt();
107         ++m_serverSeqNr;
108     }
109 }
110
111 void SshIncomingPacket::decrypt()
112 {
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.");
121     }
122 }
123
124 void SshIncomingPacket::moveFirstBytes(QByteArray &target, QByteArray &source,
125     int n)
126 {
127     target.append(source.left(n));
128     source.remove(0, n);
129 }
130
131 SshKeyExchangeInit SshIncomingPacket::extractKeyExchangeInitData() const
132 {
133     Q_ASSERT(isComplete());
134     Q_ASSERT(type() == SSH_MSG_KEXINIT);
135
136     SshKeyExchangeInit exchangeData;
137     try {
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.");
167     }
168     return exchangeData;
169 }
170
171 SshKeyExchangeReply SshIncomingPacket::extractKeyExchangeReply(const QByteArray &pubKeyAlgo) const
172 {
173     Q_ASSERT(isComplete());
174     Q_ASSERT(type() == SSH_MSG_KEXDH_REPLY);
175
176     try {
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();
186
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);
190
191         // g and y
192         if (pubKeyAlgo == SshCapabilities::PubKeyDss) {
193             replyData.parameters << SshPacketParser::asBigInt(m_data, &offset);
194             replyData.parameters << SshPacketParser::asBigInt(m_data, &offset);
195         }
196
197         replyData.f = SshPacketParser::asBigInt(m_data, &offset);
198         offset += 4;
199         if (SshPacketParser::asString(m_data, &offset) != pubKeyAlgo)
200             throw SshPacketParseException();
201         replyData.signatureBlob = SshPacketParser::asString(m_data, &offset);
202         return replyData;
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.");
207     }
208 }
209
210 SshDisconnect SshIncomingPacket::extractDisconnect() const
211 {
212     Q_ASSERT(isComplete());
213     Q_ASSERT(type() == SSH_MSG_DISCONNECT);
214
215     SshDisconnect msg;
216     try {
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.");
224     }
225
226     return msg;
227 }
228
229 SshUserAuthBanner SshIncomingPacket::extractUserAuthBanner() const
230 {
231     Q_ASSERT(isComplete());
232     Q_ASSERT(type() == SSH_MSG_USERAUTH_BANNER);
233
234     try {
235         SshUserAuthBanner msg;
236         quint32 offset = TypeOffset + 1;
237         msg.message = SshPacketParser::asUserString(m_data, &offset);
238         msg.language = SshPacketParser::asString(m_data, &offset);
239         return msg;
240     } catch (SshPacketParseException &) {
241         throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
242             "Invalid SSH_MSG_USERAUTH_BANNER.");
243     }
244 }
245
246 SshDebug SshIncomingPacket::extractDebug() const
247 {
248     Q_ASSERT(isComplete());
249     Q_ASSERT(type() == SSH_MSG_DEBUG);
250
251     try {
252         SshDebug msg;
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);
257         return msg;
258     } catch (SshPacketParseException &) {
259         throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
260             "Invalid SSH_MSG_USERAUTH_BANNER.");
261     }
262 }
263
264 SshChannelOpenFailure SshIncomingPacket::extractChannelOpenFailure() const
265 {
266     Q_ASSERT(isComplete());
267     Q_ASSERT(type() == SSH_MSG_CHANNEL_OPEN_FAILURE);
268
269     SshChannelOpenFailure openFailure;
270     try {
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.");
279     }
280     return openFailure;
281 }
282
283 SshChannelOpenConfirmation SshIncomingPacket::extractChannelOpenConfirmation() const
284 {
285     Q_ASSERT(isComplete());
286     Q_ASSERT(type() == SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
287
288     SshChannelOpenConfirmation confirmation;
289     try {
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.");
298     }
299     return confirmation;
300 }
301
302 SshChannelWindowAdjust SshIncomingPacket::extractWindowAdjust() const
303 {
304     Q_ASSERT(isComplete());
305     Q_ASSERT(type() == SSH_MSG_CHANNEL_WINDOW_ADJUST);
306
307     SshChannelWindowAdjust adjust;
308     try {
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.");
315     }
316     return adjust;
317 }
318
319 SshChannelData SshIncomingPacket::extractChannelData() const
320 {
321     Q_ASSERT(isComplete());
322     Q_ASSERT(type() == SSH_MSG_CHANNEL_DATA);
323
324     SshChannelData data;
325     try {
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.");
332     }
333     return data;
334 }
335
336 SshChannelExtendedData SshIncomingPacket::extractChannelExtendedData() const
337 {
338     Q_ASSERT(isComplete());
339     Q_ASSERT(type() == SSH_MSG_CHANNEL_EXTENDED_DATA);
340
341     SshChannelExtendedData data;
342     try {
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.");
350     }
351     return data;
352 }
353
354 SshChannelExitStatus SshIncomingPacket::extractChannelExitStatus() const
355 {
356     Q_ASSERT(isComplete());
357     Q_ASSERT(type() == SSH_MSG_CHANNEL_REQUEST);
358
359     SshChannelExitStatus exitStatus;
360     try {
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.");
371     }
372     return exitStatus;
373 }
374
375 SshChannelExitSignal SshIncomingPacket::extractChannelExitSignal() const
376 {
377     Q_ASSERT(isComplete());
378     Q_ASSERT(type() == SSH_MSG_CHANNEL_REQUEST);
379
380     SshChannelExitSignal exitSignal;
381     try {
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.");
395     }
396     return exitSignal;
397 }
398
399 quint32 SshIncomingPacket::extractRecipientChannel() const
400 {
401     Q_ASSERT(isComplete());
402
403     try {
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.");
409     }
410 }
411
412 QByteArray SshIncomingPacket::extractChannelRequestType() const
413 {
414     Q_ASSERT(isComplete());
415     Q_ASSERT(type() == SSH_MSG_CHANNEL_REQUEST);
416
417     try {
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.");
424     }
425 }
426
427 void SshIncomingPacket::calculateLength() const
428 {
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);
433 #endif
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));
438 #endif
439     m_length = SshPacketParser::asUint32(m_data, static_cast<quint32>(0));
440 #ifdef CREATOR_SSH_DEBUG
441     qDebug("decrypted length is %u", m_length);
442 #endif
443 }
444
445 } // namespace Internal
446 } // namespace Core