sshoutgoingpacket.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. /**************************************************************************
  2. **
  3. ** This file is part of Qt Creator
  4. **
  5. ** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
  6. **
  7. ** Contact: http://www.qt-project.org/
  8. **
  9. **
  10. ** GNU Lesser General Public License Usage
  11. **
  12. ** This file may be used under the terms of the GNU Lesser General Public
  13. ** License version 2.1 as published by the Free Software Foundation and
  14. ** appearing in the file LICENSE.LGPL included in the packaging of this file.
  15. ** Please review the following information to ensure the GNU Lesser General
  16. ** Public License version 2.1 requirements will be met:
  17. ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  18. **
  19. ** In addition, as a special exception, Nokia gives you certain additional
  20. ** rights. These rights are described in the Nokia Qt LGPL Exception
  21. ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
  22. **
  23. ** Other Usage
  24. **
  25. ** Alternatively, this file may be used in accordance with the terms and
  26. ** conditions contained in a signed written agreement between you and Nokia.
  27. **
  28. **
  29. **************************************************************************/
  30. #include "sshoutgoingpacket_p.h"
  31. #include "sshagent_p.h"
  32. #include "sshcapabilities_p.h"
  33. #include "sshcryptofacility_p.h"
  34. #include "sshlogging_p.h"
  35. #include "sshpacketparser_p.h"
  36. #include <QtEndian>
  37. namespace QSsh {
  38. namespace Internal {
  39. SshOutgoingPacket::SshOutgoingPacket(const SshEncryptionFacility &encrypter,
  40. const quint32 &seqNr) : m_encrypter(encrypter), m_seqNr(seqNr)
  41. {
  42. }
  43. quint32 SshOutgoingPacket::cipherBlockSize() const
  44. {
  45. return qMax(m_encrypter.cipherBlockSize(), 4U);
  46. }
  47. quint32 SshOutgoingPacket::macLength() const
  48. {
  49. return m_encrypter.macLength();
  50. }
  51. QByteArray SshOutgoingPacket::generateKeyExchangeInitPacket()
  52. {
  53. const QByteArray &supportedkeyExchangeMethods
  54. = encodeNameList(SshCapabilities::KeyExchangeMethods);
  55. const QByteArray &supportedPublicKeyAlgorithms
  56. = encodeNameList(SshCapabilities::PublicKeyAlgorithms);
  57. const QByteArray &supportedEncryptionAlgorithms
  58. = encodeNameList(SshCapabilities::EncryptionAlgorithms);
  59. const QByteArray &supportedMacAlgorithms
  60. = encodeNameList(SshCapabilities::MacAlgorithms);
  61. const QByteArray &supportedCompressionAlgorithms
  62. = encodeNameList(SshCapabilities::CompressionAlgorithms);
  63. const QByteArray &supportedLanguages = encodeNameList(QList<QByteArray>());
  64. init(SSH_MSG_KEXINIT);
  65. m_data += m_encrypter.getRandomNumbers(16);
  66. m_data.append(supportedkeyExchangeMethods);
  67. m_data.append(supportedPublicKeyAlgorithms);
  68. m_data.append(supportedEncryptionAlgorithms)
  69. .append(supportedEncryptionAlgorithms);
  70. m_data.append(supportedMacAlgorithms).append(supportedMacAlgorithms);
  71. m_data.append(supportedCompressionAlgorithms)
  72. .append(supportedCompressionAlgorithms);
  73. m_data.append(supportedLanguages).append(supportedLanguages);
  74. appendBool(false); // No guessed packet.
  75. m_data.append(QByteArray(4, 0)); // Reserved.
  76. QByteArray payload = m_data.mid(PayloadOffset);
  77. finalize();
  78. return payload;
  79. }
  80. void SshOutgoingPacket::generateKeyDhInitPacket(const Botan::BigInt &e)
  81. {
  82. init(SSH_MSG_KEXDH_INIT).appendMpInt(e).finalize();
  83. }
  84. void SshOutgoingPacket::generateKeyEcdhInitPacket(const QByteArray &clientQ)
  85. {
  86. init(SSH_MSG_KEX_ECDH_INIT).appendString(clientQ).finalize();
  87. }
  88. void SshOutgoingPacket::generateNewKeysPacket()
  89. {
  90. init(SSH_MSG_NEWKEYS).finalize();
  91. }
  92. void SshOutgoingPacket::generateUserAuthServiceRequestPacket()
  93. {
  94. generateServiceRequest("ssh-userauth");
  95. }
  96. void SshOutgoingPacket::generateServiceRequest(const QByteArray &service)
  97. {
  98. init(SSH_MSG_SERVICE_REQUEST).appendString(service).finalize();
  99. }
  100. void SshOutgoingPacket::generateUserAuthByPasswordRequestPacket(const QByteArray &user,
  101. const QByteArray &service, const QByteArray &pwd)
  102. {
  103. init(SSH_MSG_USERAUTH_REQUEST).appendString(user).appendString(service);
  104. if (pwd.isEmpty())
  105. appendString("none"); // RFC 4252, 5.2
  106. else
  107. appendString("password").appendBool(false).appendString(pwd);
  108. finalize();
  109. }
  110. void SshOutgoingPacket::generateUserAuthByPublicKeyRequestPacket(const QByteArray &user,
  111. const QByteArray &service, const QByteArray &key, const QByteArray &signature)
  112. {
  113. init(SSH_MSG_USERAUTH_REQUEST).appendString(user).appendString(service)
  114. .appendString("publickey").appendBool(true);
  115. if (!key.isEmpty()) {
  116. appendString(SshPacketParser::asString(key, quint32(0)));
  117. appendString(key);
  118. appendString(signature);
  119. } else {
  120. appendString(m_encrypter.authenticationAlgorithmName());
  121. appendString(m_encrypter.authenticationPublicKey());
  122. const QByteArray &dataToSign = m_data.mid(PayloadOffset);
  123. appendString(m_encrypter.authenticationKeySignature(dataToSign));
  124. }
  125. finalize();
  126. }
  127. void SshOutgoingPacket::generateQueryPublicKeyPacket(const QByteArray &user,
  128. const QByteArray &service, const QByteArray &publicKey)
  129. {
  130. // Name extraction cannot fail, we already verified this when receiving the key
  131. // from the agent.
  132. const QByteArray algoName = SshPacketParser::asString(publicKey, quint32(0));
  133. SshOutgoingPacket packetToSign(m_encrypter, m_seqNr);
  134. packetToSign.init(SSH_MSG_USERAUTH_REQUEST).appendString(user).appendString(service)
  135. .appendString("publickey").appendBool(true).appendString(algoName)
  136. .appendString(publicKey);
  137. const QByteArray &dataToSign
  138. = encodeString(m_encrypter.sessionId()) + packetToSign.m_data.mid(PayloadOffset);
  139. SshAgent::storeDataToSign(publicKey, dataToSign, qHash(m_encrypter.sessionId()));
  140. init(SSH_MSG_USERAUTH_REQUEST).appendString(user).appendString(service)
  141. .appendString("publickey").appendBool(false).appendString(algoName)
  142. .appendString(publicKey).finalize();
  143. }
  144. void SshOutgoingPacket::generateUserAuthByKeyboardInteractiveRequestPacket(const QByteArray &user,
  145. const QByteArray &service)
  146. {
  147. // RFC 4256, 3.1
  148. init(SSH_MSG_USERAUTH_REQUEST).appendString(user).appendString(service)
  149. .appendString("keyboard-interactive")
  150. .appendString(QByteArray()) // Language tag. Deprecated and should be empty
  151. .appendString(QByteArray()) // Submethods.
  152. .finalize();
  153. }
  154. void SshOutgoingPacket::generateUserAuthInfoResponsePacket(const QStringList &responses)
  155. {
  156. // RFC 4256, 3.4
  157. init(SSH_MSG_USERAUTH_INFO_RESPONSE).appendInt(responses.count());
  158. for (const QString &response : responses) {
  159. appendString(response.toUtf8());
  160. }
  161. finalize();
  162. }
  163. void SshOutgoingPacket::generateRequestFailurePacket()
  164. {
  165. init(SSH_MSG_REQUEST_FAILURE).finalize();
  166. }
  167. void SshOutgoingPacket::generateIgnorePacket()
  168. {
  169. init(SSH_MSG_IGNORE).finalize();
  170. }
  171. void SshOutgoingPacket::generateInvalidMessagePacket()
  172. {
  173. init(SSH_MSG_INVALID).finalize();
  174. }
  175. void SshOutgoingPacket::generateSessionPacket(quint32 channelId,
  176. quint32 windowSize, quint32 maxPacketSize)
  177. {
  178. init(SSH_MSG_CHANNEL_OPEN).appendString("session").appendInt(channelId)
  179. .appendInt(windowSize).appendInt(maxPacketSize).finalize();
  180. }
  181. void SshOutgoingPacket::generateDirectTcpIpPacket(quint32 channelId, quint32 windowSize,
  182. quint32 maxPacketSize, const QByteArray &remoteHost, quint32 remotePort,
  183. const QByteArray &localIpAddress, quint32 localPort)
  184. {
  185. init(SSH_MSG_CHANNEL_OPEN).appendString("direct-tcpip").appendInt(channelId)
  186. .appendInt(windowSize).appendInt(maxPacketSize).appendString(remoteHost)
  187. .appendInt(remotePort).appendString(localIpAddress).appendInt(localPort).finalize();
  188. }
  189. void SshOutgoingPacket::generateTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort)
  190. {
  191. init(SSH_MSG_GLOBAL_REQUEST).appendString("tcpip-forward").appendBool(true)
  192. .appendString(bindAddress).appendInt(bindPort).finalize();
  193. }
  194. void SshOutgoingPacket::generateCancelTcpIpForwardPacket(const QByteArray &bindAddress,
  195. quint32 bindPort)
  196. {
  197. init(SSH_MSG_GLOBAL_REQUEST).appendString("cancel-tcpip-forward").appendBool(true)
  198. .appendString(bindAddress).appendInt(bindPort).finalize();
  199. }
  200. void SshOutgoingPacket::generateEnvPacket(quint32 remoteChannel,
  201. const QByteArray &var, const QByteArray &value)
  202. {
  203. init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel).appendString("env")
  204. .appendBool(false).appendString(var).appendString(value).finalize();
  205. }
  206. void SshOutgoingPacket::generateX11ForwardingPacket(quint32 remoteChannel,
  207. const QByteArray &protocol, const QByteArray &cookie, quint32 screenNumber)
  208. {
  209. init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel).appendString("x11-req")
  210. .appendBool(false).appendBool(false).appendString(protocol)
  211. .appendString(cookie).appendInt(screenNumber).finalize();
  212. }
  213. void SshOutgoingPacket::generatePtyRequestPacket(quint32 remoteChannel,
  214. const SshPseudoTerminal &terminal)
  215. {
  216. init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel)
  217. .appendString("pty-req").appendBool(false)
  218. .appendString(terminal.termType).appendInt(terminal.columnCount)
  219. .appendInt(terminal.rowCount).appendInt(0).appendInt(0);
  220. QByteArray modeString;
  221. for (SshPseudoTerminal::ModeMap::ConstIterator it = terminal.modes.constBegin();
  222. it != terminal.modes.constEnd(); ++it) {
  223. modeString += char(it.key());
  224. modeString += encodeInt(it.value());
  225. }
  226. modeString += char(0); // TTY_OP_END
  227. appendString(modeString).finalize();
  228. }
  229. void SshOutgoingPacket::generateExecPacket(quint32 remoteChannel,
  230. const QByteArray &command)
  231. {
  232. init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel).appendString("exec")
  233. .appendBool(true).appendString(command).finalize();
  234. }
  235. void SshOutgoingPacket::generateShellPacket(quint32 remoteChannel)
  236. {
  237. init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel).appendString("shell")
  238. .appendBool(true).finalize();
  239. }
  240. void SshOutgoingPacket::generateSftpPacket(quint32 remoteChannel)
  241. {
  242. init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel)
  243. .appendString("subsystem").appendBool(true).appendString("sftp")
  244. .finalize();
  245. }
  246. void SshOutgoingPacket::generateWindowAdjustPacket(quint32 remoteChannel,
  247. quint32 bytesToAdd)
  248. {
  249. init(SSH_MSG_CHANNEL_WINDOW_ADJUST).appendInt(remoteChannel)
  250. .appendInt(bytesToAdd).finalize();
  251. }
  252. void SshOutgoingPacket::generateChannelDataPacket(quint32 remoteChannel,
  253. const QByteArray &data)
  254. {
  255. init(SSH_MSG_CHANNEL_DATA).appendInt(remoteChannel).appendString(data)
  256. .finalize();
  257. }
  258. void SshOutgoingPacket::generateChannelSignalPacket(quint32 remoteChannel,
  259. const QByteArray &signalName)
  260. {
  261. init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel)
  262. .appendString("signal").appendBool(false).appendString(signalName)
  263. .finalize();
  264. }
  265. void SshOutgoingPacket::generateChannelEofPacket(quint32 remoteChannel)
  266. {
  267. init(SSH_MSG_CHANNEL_EOF).appendInt(remoteChannel).finalize();
  268. }
  269. void SshOutgoingPacket::generateChannelClosePacket(quint32 remoteChannel)
  270. {
  271. init(SSH_MSG_CHANNEL_CLOSE).appendInt(remoteChannel).finalize();
  272. }
  273. void SshOutgoingPacket::generateChannelOpenConfirmationPacket(quint32 remoteChannel,
  274. quint32 localChannel,
  275. quint32 localWindowSize,
  276. quint32 maxPacketSize)
  277. {
  278. init(SSH_MSG_CHANNEL_OPEN_CONFIRMATION).appendInt(remoteChannel).appendInt(localChannel)
  279. .appendInt(localWindowSize).appendInt(maxPacketSize).finalize();
  280. }
  281. void SshOutgoingPacket::generateChannelOpenFailurePacket(quint32 remoteChannel, quint32 reason,
  282. const QByteArray &reasonString)
  283. {
  284. init(SSH_MSG_CHANNEL_OPEN_FAILURE).appendInt(remoteChannel).appendInt(reason)
  285. .appendString(reasonString).appendString(QByteArray()).finalize();
  286. }
  287. void SshOutgoingPacket::generateDisconnectPacket(SshErrorCode reason,
  288. const QByteArray &reasonString)
  289. {
  290. init(SSH_MSG_DISCONNECT).appendInt(reason).appendString(reasonString)
  291. .appendString(QByteArray()).finalize();
  292. }
  293. void SshOutgoingPacket::generateMsgUnimplementedPacket(quint32 serverSeqNr)
  294. {
  295. init(SSH_MSG_UNIMPLEMENTED).appendInt(serverSeqNr).finalize();
  296. }
  297. SshOutgoingPacket &SshOutgoingPacket::appendInt(quint32 val)
  298. {
  299. m_data.append(encodeInt(val));
  300. return *this;
  301. }
  302. SshOutgoingPacket &SshOutgoingPacket::appendMpInt(const Botan::BigInt &number)
  303. {
  304. m_data.append(encodeMpInt(number));
  305. return *this;
  306. }
  307. SshOutgoingPacket &SshOutgoingPacket::appendBool(bool b)
  308. {
  309. m_data += static_cast<char>(b);
  310. return *this;
  311. }
  312. SshOutgoingPacket &SshOutgoingPacket::appendString(const QByteArray &string)
  313. {
  314. m_data.append(encodeString(string));
  315. return *this;
  316. }
  317. SshOutgoingPacket &SshOutgoingPacket::init(SshPacketType type)
  318. {
  319. m_data.resize(TypeOffset + 1);
  320. m_data[TypeOffset] = type;
  321. return *this;
  322. }
  323. SshOutgoingPacket &SshOutgoingPacket::setPadding()
  324. {
  325. m_data += m_encrypter.getRandomNumbers(MinPaddingLength);
  326. int padLength = MinPaddingLength;
  327. const int divisor = sizeDivisor();
  328. const int mod = m_data.size() % divisor;
  329. padLength += divisor - mod;
  330. m_data += m_encrypter.getRandomNumbers(padLength - MinPaddingLength);
  331. m_data[PaddingLengthOffset] = padLength;
  332. return *this;
  333. }
  334. SshOutgoingPacket &SshOutgoingPacket::encrypt()
  335. {
  336. const QByteArray &mac
  337. = generateMac(m_encrypter, m_seqNr);
  338. m_encrypter.encrypt(m_data);
  339. m_data += mac;
  340. return *this;
  341. }
  342. void SshOutgoingPacket::finalize()
  343. {
  344. setPadding();
  345. setLengthField(m_data);
  346. m_length = m_data.size() - 4;
  347. qCDebug(sshLog) << "Encrypting packet of type" << int(m_data.at(TypeOffset));
  348. encrypt();
  349. qCDebug(sshLog) << "Sending packet of size" << rawData().count();
  350. Q_ASSERT(isComplete());
  351. }
  352. int SshOutgoingPacket::sizeDivisor() const
  353. {
  354. return qMax(cipherBlockSize(), 8U);
  355. }
  356. QByteArray SshOutgoingPacket::encodeNameList(const QList<QByteArray> &list)
  357. {
  358. QByteArray data;
  359. data.resize(4);
  360. for (int i = 0; i < list.count(); ++i) {
  361. if (i > 0)
  362. data.append(',');
  363. data.append(list.at(i));
  364. }
  365. AbstractSshPacket::setLengthField(data);
  366. return data;
  367. }
  368. } // namespace Internal
  369. } // namespace QSsh