sshconnection.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035
  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 "sshconnection.h"
  31. #include "sshconnection_p.h"
  32. #include "sftpchannel.h"
  33. #include "sshagent_p.h"
  34. #include "sshcapabilities_p.h"
  35. #include "sshchannelmanager_p.h"
  36. #include "sshcryptofacility_p.h"
  37. #include "sshdirecttcpiptunnel.h"
  38. #include "sshtcpipforwardserver.h"
  39. #include "sshexception_p.h"
  40. #include "sshkeyexchange_p.h"
  41. #include "sshremoteprocess.h"
  42. #include "sshlogging_p.h"
  43. #include <QFile>
  44. #include <QMutex>
  45. #include <QMutexLocker>
  46. #include <QNetworkProxy>
  47. #include <QRegularExpression>
  48. #include <QTcpSocket>
  49. namespace QSsh {
  50. namespace {
  51. const QByteArray ClientId("SSH-2.0-QtCreator\r\n");
  52. }
  53. SshConnectionParameters::SshConnectionParameters() :
  54. timeout(0), authenticationType(AuthenticationTypePublicKey),
  55. hostKeyCheckingMode(SshHostKeyCheckingNone)
  56. {
  57. url.setPort(0);
  58. options |= SshIgnoreDefaultProxy;
  59. hostKeyDatabase = SshHostKeyDatabasePtr(new SshHostKeyDatabase);
  60. }
  61. static inline bool equals(const SshConnectionParameters &p1, const SshConnectionParameters &p2)
  62. {
  63. return p1.url == p2.url
  64. && p1.authenticationType == p2.authenticationType
  65. && p1.privateKeyFile == p2.privateKeyFile
  66. && p1.hostKeyCheckingMode == p2.hostKeyCheckingMode
  67. && p1.timeout == p2.timeout;
  68. }
  69. bool operator==(const SshConnectionParameters &p1, const SshConnectionParameters &p2)
  70. {
  71. return equals(p1, p2);
  72. }
  73. bool operator!=(const SshConnectionParameters &p1, const SshConnectionParameters &p2)
  74. {
  75. return !equals(p1, p2);
  76. }
  77. SshConnection::SshConnection(const SshConnectionParameters &serverInfo, QObject *parent)
  78. : QObject(parent)
  79. {
  80. qRegisterMetaType<QSsh::SshError>("QSsh::SshError");
  81. qRegisterMetaType<QSsh::SftpJobId>("QSsh::SftpJobId");
  82. qRegisterMetaType<QSsh::SftpFileInfo>("QSsh::SftpFileInfo");
  83. qRegisterMetaType<QSsh::SftpError>("QSsh::SftpError");
  84. qRegisterMetaType<QSsh::SftpError>("SftpError");
  85. qRegisterMetaType<QList <QSsh::SftpFileInfo> >("QList<QSsh::SftpFileInfo>");
  86. d = new Internal::SshConnectionPrivate(this, serverInfo);
  87. connect(d, &Internal::SshConnectionPrivate::connected, this, &SshConnection::connected,
  88. Qt::QueuedConnection);
  89. connect(d, &Internal::SshConnectionPrivate::dataAvailable, this,
  90. &SshConnection::dataAvailable, Qt::QueuedConnection);
  91. connect(d, &Internal::SshConnectionPrivate::disconnected, this, &SshConnection::disconnected,
  92. Qt::QueuedConnection);
  93. connect(d, &Internal::SshConnectionPrivate::error, this,
  94. &SshConnection::error, Qt::QueuedConnection);
  95. }
  96. const QByteArray &SshConnection::hostKeyFingerprint() const
  97. {
  98. return d->hostKeyFingerprint();
  99. }
  100. void SshConnection::connectToHost()
  101. {
  102. d->connectToHost();
  103. }
  104. void SshConnection::disconnectFromHost()
  105. {
  106. d->closeConnection(Internal::SSH_DISCONNECT_BY_APPLICATION, SshNoError, "",
  107. QString());
  108. }
  109. SshConnection::State SshConnection::state() const
  110. {
  111. switch (d->state()) {
  112. case Internal::SocketUnconnected:
  113. return Unconnected;
  114. case Internal::ConnectionEstablished:
  115. return Connected;
  116. default:
  117. return Connecting;
  118. }
  119. }
  120. SshError SshConnection::errorState() const
  121. {
  122. return d->errorState();
  123. }
  124. QString SshConnection::errorString() const
  125. {
  126. return d->errorString();
  127. }
  128. SshConnectionParameters SshConnection::connectionParameters() const
  129. {
  130. return d->m_connParams;
  131. }
  132. SshConnectionInfo SshConnection::connectionInfo() const
  133. {
  134. QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, SshConnectionInfo());
  135. return SshConnectionInfo(d->m_socket->localAddress(), d->m_socket->localPort(),
  136. d->m_socket->peerAddress(), d->m_socket->peerPort());
  137. }
  138. SshConnection::~SshConnection()
  139. {
  140. disconnect();
  141. disconnectFromHost();
  142. delete d;
  143. }
  144. QSharedPointer<SshRemoteProcess> SshConnection::createRemoteProcess(const QByteArray &command)
  145. {
  146. QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, QSharedPointer<SshRemoteProcess>());
  147. return d->createRemoteProcess(command);
  148. }
  149. QSharedPointer<SshRemoteProcess> SshConnection::createRemoteShell()
  150. {
  151. QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, QSharedPointer<SshRemoteProcess>());
  152. return d->createRemoteShell();
  153. }
  154. QSharedPointer<SftpChannel> SshConnection::createSftpChannel()
  155. {
  156. QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, QSharedPointer<SftpChannel>());
  157. return d->createSftpChannel();
  158. }
  159. SshDirectTcpIpTunnel::Ptr SshConnection::createDirectTunnel(const QString &originatingHost,
  160. quint16 originatingPort, const QString &remoteHost, quint16 remotePort)
  161. {
  162. QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, SshDirectTcpIpTunnel::Ptr());
  163. return d->createDirectTunnel(originatingHost, originatingPort, remoteHost, remotePort);
  164. }
  165. QSharedPointer<SshTcpIpForwardServer> SshConnection::createForwardServer(const QString &remoteHost,
  166. quint16 remotePort)
  167. {
  168. QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, SshTcpIpForwardServer::Ptr());
  169. return d->createForwardServer(remoteHost, remotePort);
  170. }
  171. int SshConnection::closeAllChannels()
  172. {
  173. try {
  174. return d->m_channelManager->closeAllChannels(Internal::SshChannelManager::CloseAllRegular);
  175. } catch (const std::exception &e) {
  176. qCWarning(Internal::sshLog, "%s: %s", Q_FUNC_INFO, e.what());
  177. return -1;
  178. }
  179. }
  180. int SshConnection::channelCount() const
  181. {
  182. return d->m_channelManager->channelCount();
  183. }
  184. QString SshConnection::x11DisplayName() const
  185. {
  186. return d->m_channelManager->x11DisplayName();
  187. }
  188. namespace Internal {
  189. SshConnectionPrivate::SshConnectionPrivate(SshConnection *conn,
  190. const SshConnectionParameters &serverInfo)
  191. : m_socket(new QTcpSocket(this)), m_state(SocketUnconnected),
  192. m_sendFacility(m_socket),
  193. m_channelManager(new SshChannelManager(m_sendFacility, this)),
  194. m_connParams(serverInfo), m_error(SshNoError), m_ignoreNextPacket(false),
  195. m_conn(conn)
  196. {
  197. setupPacketHandlers();
  198. if (m_connParams.options & SshLowDelaySocket) {
  199. m_socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
  200. }
  201. m_socket->setProxy((m_connParams.options & SshIgnoreDefaultProxy)
  202. ? QNetworkProxy::NoProxy : QNetworkProxy::DefaultProxy);
  203. m_timeoutTimer.setTimerType(Qt::VeryCoarseTimer);
  204. m_timeoutTimer.setSingleShot(true);
  205. m_timeoutTimer.setInterval(m_connParams.timeout * 1000);
  206. m_keepAliveTimer.setTimerType(Qt::VeryCoarseTimer);
  207. m_keepAliveTimer.setSingleShot(true);
  208. m_keepAliveTimer.setInterval(10000);
  209. connect(m_channelManager, &SshChannelManager::timeout,
  210. this, &SshConnectionPrivate::handleTimeout);
  211. }
  212. SshConnectionPrivate::~SshConnectionPrivate()
  213. {
  214. disconnect();
  215. }
  216. void SshConnectionPrivate::setupPacketHandlers()
  217. {
  218. typedef SshConnectionPrivate This;
  219. setupPacketHandler(SSH_MSG_KEXINIT, StateList() << SocketConnected
  220. << ConnectionEstablished, &This::handleKeyExchangeInitPacket);
  221. setupPacketHandler(SSH_MSG_KEXDH_REPLY, StateList() << SocketConnected
  222. << ConnectionEstablished, &This::handleKeyExchangeReplyPacket);
  223. setupPacketHandler(SSH_MSG_NEWKEYS, StateList() << SocketConnected
  224. << ConnectionEstablished, &This::handleNewKeysPacket);
  225. setupPacketHandler(SSH_MSG_SERVICE_ACCEPT,
  226. StateList() << UserAuthServiceRequested,
  227. &This::handleServiceAcceptPacket);
  228. if (m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypePassword
  229. || m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods) {
  230. setupPacketHandler(SSH_MSG_USERAUTH_PASSWD_CHANGEREQ,
  231. StateList() << UserAuthRequested, &This::handlePasswordExpiredPacket);
  232. }
  233. setupPacketHandler(SSH_MSG_GLOBAL_REQUEST,
  234. StateList() << ConnectionEstablished, &This::handleGlobalRequest);
  235. const StateList authReqList = StateList() << UserAuthRequested;
  236. setupPacketHandler(SSH_MSG_USERAUTH_BANNER, authReqList,
  237. &This::handleUserAuthBannerPacket);
  238. setupPacketHandler(SSH_MSG_USERAUTH_SUCCESS, authReqList,
  239. &This::handleUserAuthSuccessPacket);
  240. setupPacketHandler(SSH_MSG_USERAUTH_FAILURE, authReqList,
  241. &This::handleUserAuthFailurePacket);
  242. if (m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypeKeyboardInteractive
  243. || m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods) {
  244. setupPacketHandler(SSH_MSG_USERAUTH_INFO_REQUEST, authReqList,
  245. &This::handleUserAuthInfoRequestPacket);
  246. }
  247. setupPacketHandler(SSH_MSG_USERAUTH_PK_OK, authReqList, &This::handleUserAuthKeyOkPacket);
  248. const StateList connectedList
  249. = StateList() << ConnectionEstablished;
  250. setupPacketHandler(SSH_MSG_CHANNEL_REQUEST, connectedList,
  251. &This::handleChannelRequest);
  252. setupPacketHandler(SSH_MSG_CHANNEL_OPEN, connectedList,
  253. &This::handleChannelOpen);
  254. setupPacketHandler(SSH_MSG_CHANNEL_OPEN_FAILURE, connectedList,
  255. &This::handleChannelOpenFailure);
  256. setupPacketHandler(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, connectedList,
  257. &This::handleChannelOpenConfirmation);
  258. setupPacketHandler(SSH_MSG_CHANNEL_SUCCESS, connectedList,
  259. &This::handleChannelSuccess);
  260. setupPacketHandler(SSH_MSG_CHANNEL_FAILURE, connectedList,
  261. &This::handleChannelFailure);
  262. setupPacketHandler(SSH_MSG_CHANNEL_WINDOW_ADJUST, connectedList,
  263. &This::handleChannelWindowAdjust);
  264. setupPacketHandler(SSH_MSG_CHANNEL_DATA, connectedList,
  265. &This::handleChannelData);
  266. setupPacketHandler(SSH_MSG_CHANNEL_EXTENDED_DATA, connectedList,
  267. &This::handleChannelExtendedData);
  268. const StateList connectedOrClosedList
  269. = StateList() << SocketUnconnected << ConnectionEstablished;
  270. setupPacketHandler(SSH_MSG_CHANNEL_EOF, connectedOrClosedList,
  271. &This::handleChannelEof);
  272. setupPacketHandler(SSH_MSG_CHANNEL_CLOSE, connectedOrClosedList,
  273. &This::handleChannelClose);
  274. setupPacketHandler(SSH_MSG_DISCONNECT, StateList() << SocketConnected << WaitingForAgentKeys
  275. << UserAuthServiceRequested << UserAuthRequested
  276. << ConnectionEstablished, &This::handleDisconnect);
  277. setupPacketHandler(SSH_MSG_UNIMPLEMENTED,
  278. StateList() << ConnectionEstablished, &This::handleUnimplementedPacket);
  279. setupPacketHandler(SSH_MSG_REQUEST_SUCCESS, connectedList,
  280. &This::handleRequestSuccess);
  281. setupPacketHandler(SSH_MSG_REQUEST_FAILURE, connectedList,
  282. &This::handleRequestFailure);
  283. }
  284. void SshConnectionPrivate::setupPacketHandler(SshPacketType type,
  285. const SshConnectionPrivate::StateList &states,
  286. SshConnectionPrivate::PacketHandler handler)
  287. {
  288. m_packetHandlers.insert(type, HandlerInStates(states, handler));
  289. }
  290. void SshConnectionPrivate::handleSocketConnected()
  291. {
  292. m_state = SocketConnected;
  293. sendData(ClientId);
  294. }
  295. void SshConnectionPrivate::handleIncomingData()
  296. {
  297. if (m_state == SocketUnconnected)
  298. return; // For stuff queued in the event loop after we've called closeConnection();
  299. try {
  300. if (!canUseSocket())
  301. return;
  302. m_incomingData += m_socket->readAll();
  303. qCDebug(sshLog, "state = %d, remote data size = %d", int(m_state), int(m_incomingData.count()));
  304. if (m_serverId.isEmpty())
  305. handleServerId();
  306. handlePackets();
  307. } catch (const SshServerException &e) {
  308. closeConnection(e.error, SshProtocolError, e.errorStringServer,
  309. tr("SSH Protocol error: %1").arg(e.errorStringUser));
  310. } catch (const SshClientException &e) {
  311. closeConnection(SSH_DISCONNECT_BY_APPLICATION, e.error, "",
  312. e.errorString);
  313. } catch (const std::exception &e) {
  314. closeConnection(SSH_DISCONNECT_BY_APPLICATION, SshInternalError, "",
  315. tr("Botan library exception: %1").arg(QString::fromLocal8Bit(e.what())));
  316. }
  317. }
  318. // RFC 4253, 4.2.
  319. void SshConnectionPrivate::handleServerId()
  320. {
  321. qCDebug(sshLog, "%s: incoming data size = %d, incoming data = '%s'",
  322. Q_FUNC_INFO, int(m_incomingData.count()), m_incomingData.data());
  323. const int newLinePos = m_incomingData.indexOf('\n');
  324. if (newLinePos == -1)
  325. return; // Not enough data yet.
  326. // Lines not starting with "SSH-" are ignored.
  327. if (!m_incomingData.startsWith("SSH-")) {
  328. m_incomingData.remove(0, newLinePos + 1);
  329. m_serverHasSentDataBeforeId = true;
  330. return;
  331. }
  332. if (newLinePos > 255 - 1) {
  333. throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
  334. "Identification string too long.",
  335. tr("Server identification string is %1 characters long, but the maximum "
  336. "allowed length is 255.").arg(newLinePos + 1));
  337. }
  338. const bool hasCarriageReturn = m_incomingData.at(newLinePos - 1) == '\r';
  339. m_serverId = m_incomingData.left(newLinePos);
  340. if (hasCarriageReturn)
  341. m_serverId.chop(1);
  342. m_incomingData.remove(0, newLinePos + 1);
  343. if (m_serverId.contains('\0')) {
  344. throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
  345. "Identification string contains illegal NUL character.",
  346. tr("Server identification string contains illegal NUL character."));
  347. }
  348. // "printable US-ASCII characters, with the exception of whitespace characters
  349. // and the minus sign"
  350. QString legalString = QLatin1String("[]!\"#$!&'()*+,./0-9:;<=>?@A-Z[\\\\^_`a-z{|}~]+");
  351. const QRegularExpression versionIdpattern(QString::fromLatin1("^SSH-(%1)-%1(?: .+)?.*$").arg(legalString));
  352. const QRegularExpressionMatch match = versionIdpattern.match(QString::fromLatin1(m_serverId));
  353. if (!match.hasMatch()) {
  354. throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
  355. "Identification string is invalid.",
  356. tr("Server Identification string \"%1\" is invalid.")
  357. .arg(QString::fromLatin1(m_serverId)));
  358. }
  359. const QString serverProtoVersion = match.captured(1);
  360. if (serverProtoVersion != QLatin1String("2.0") && serverProtoVersion != QLatin1String("1.99")) {
  361. throw SshServerException(SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
  362. "Invalid protocol version.",
  363. tr("Server protocol version is \"%1\", but needs to be 2.0 or 1.99.")
  364. .arg(serverProtoVersion));
  365. }
  366. if (serverProtoVersion == QLatin1String("2.0") && !hasCarriageReturn) {
  367. if (m_connParams.options & SshEnableStrictConformanceChecks) {
  368. throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
  369. "Identification string is invalid.",
  370. tr("Server identification string is invalid (missing carriage return)."));
  371. } else {
  372. qCWarning(Internal::sshLog, "Server identification string is invalid (missing carriage return).");
  373. }
  374. }
  375. if (serverProtoVersion == QLatin1String("1.99") && m_serverHasSentDataBeforeId) {
  376. if (m_connParams.options & SshEnableStrictConformanceChecks) {
  377. throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
  378. "No extra data preceding identification string allowed for 1.99.",
  379. tr("Server reports protocol version 1.99, but sends data "
  380. "before the identification string, which is not allowed."));
  381. } else {
  382. qCWarning(Internal::sshLog, "Server reports protocol version 1.99, but sends data "
  383. "before the identification string, which is not allowed.");
  384. }
  385. }
  386. m_keyExchange.reset(new SshKeyExchange(m_connParams, m_sendFacility));
  387. m_keyExchange->sendKexInitPacket(m_serverId);
  388. m_keyExchangeState = KexInitSent;
  389. }
  390. void SshConnectionPrivate::handlePackets()
  391. {
  392. m_incomingPacket.consumeData(m_incomingData);
  393. while (m_incomingPacket.isComplete()) {
  394. handleCurrentPacket();
  395. m_incomingPacket.clear();
  396. m_incomingPacket.consumeData(m_incomingData);
  397. }
  398. }
  399. void SshConnectionPrivate::handleCurrentPacket()
  400. {
  401. Q_ASSERT(m_incomingPacket.isComplete());
  402. Q_ASSERT(m_keyExchangeState == DhInitSent || !m_ignoreNextPacket);
  403. if (m_ignoreNextPacket) {
  404. m_ignoreNextPacket = false;
  405. return;
  406. }
  407. QHash<SshPacketType, HandlerInStates>::ConstIterator it
  408. = m_packetHandlers.constFind(m_incomingPacket.type());
  409. if (it == m_packetHandlers.constEnd()) {
  410. m_sendFacility.sendMsgUnimplementedPacket(m_incomingPacket.serverSeqNr());
  411. return;
  412. }
  413. if (!it.value().first.contains(m_state)) {
  414. handleUnexpectedPacket();
  415. return;
  416. }
  417. (this->*it.value().second)();
  418. }
  419. void SshConnectionPrivate::handleKeyExchangeInitPacket()
  420. {
  421. if (m_keyExchangeState != NoKeyExchange
  422. && m_keyExchangeState != KexInitSent) {
  423. throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
  424. "Unexpected packet.", tr("Unexpected packet of type %1.")
  425. .arg(m_incomingPacket.type()));
  426. }
  427. // Server-initiated re-exchange.
  428. if (m_keyExchangeState == NoKeyExchange) {
  429. m_keyExchange.reset(new SshKeyExchange(m_connParams, m_sendFacility));
  430. m_keyExchange->sendKexInitPacket(m_serverId);
  431. }
  432. // If the server sends a guessed packet, the guess must be wrong,
  433. // because the algorithms we support require us to initiate the
  434. // key exchange.
  435. if (m_keyExchange->sendDhInitPacket(m_incomingPacket)) {
  436. m_ignoreNextPacket = true;
  437. }
  438. m_keyExchangeState = DhInitSent;
  439. }
  440. void SshConnectionPrivate::handleKeyExchangeReplyPacket()
  441. {
  442. if (m_keyExchangeState != DhInitSent) {
  443. throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
  444. "Unexpected packet.", tr("Unexpected packet of type %1.")
  445. .arg(m_incomingPacket.type()));
  446. }
  447. m_keyExchange->sendNewKeysPacket(m_incomingPacket,
  448. ClientId.left(ClientId.size() - 2));
  449. m_hostFingerprint = m_keyExchange->hostKeyFingerprint();
  450. m_sendFacility.recreateKeys(*m_keyExchange);
  451. m_keyExchangeState = NewKeysSent;
  452. }
  453. void SshConnectionPrivate::handleNewKeysPacket()
  454. {
  455. if (m_keyExchangeState != NewKeysSent) {
  456. throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
  457. "Unexpected packet.", tr("Unexpected packet of type %1.")
  458. .arg(m_incomingPacket.type()));
  459. }
  460. m_incomingPacket.recreateKeys(*m_keyExchange);
  461. m_keyExchange.reset();
  462. m_keyExchangeState = NoKeyExchange;
  463. if (m_state == SocketConnected) {
  464. m_sendFacility.sendUserAuthServiceRequestPacket();
  465. m_state = UserAuthServiceRequested;
  466. }
  467. }
  468. void SshConnectionPrivate::handleServiceAcceptPacket()
  469. {
  470. switch (m_connParams.authenticationType) {
  471. case SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods:
  472. m_triedAllPasswordBasedMethods = false;
  473. Q_FALLTHROUGH();
  474. case SshConnectionParameters::AuthenticationTypePassword:
  475. m_sendFacility.sendUserAuthByPasswordRequestPacket(m_connParams.userName().toUtf8(),
  476. SshCapabilities::SshConnectionService, m_connParams.password().toUtf8());
  477. break;
  478. case SshConnectionParameters::AuthenticationTypeKeyboardInteractive:
  479. m_sendFacility.sendUserAuthByKeyboardInteractiveRequestPacket(m_connParams.userName().toUtf8(),
  480. SshCapabilities::SshConnectionService);
  481. break;
  482. case SshConnectionParameters::AuthenticationTypePublicKey:
  483. authenticateWithPublicKey();
  484. break;
  485. case SshConnectionParameters::AuthenticationTypeAgent:
  486. if (SshAgent::publicKeys().isEmpty()) {
  487. if (m_agentKeysUpToDate)
  488. throw SshClientException(SshAuthenticationError, tr("ssh-agent has no keys."));
  489. qCDebug(sshLog) << "agent has no keys yet, waiting";
  490. m_state = WaitingForAgentKeys;
  491. return;
  492. } else {
  493. tryAllAgentKeys();
  494. }
  495. break;
  496. }
  497. m_state = UserAuthRequested;
  498. }
  499. void SshConnectionPrivate::handlePasswordExpiredPacket()
  500. {
  501. if (m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods
  502. && m_triedAllPasswordBasedMethods) {
  503. // This means we just tried to authorize via "keyboard-interactive", in which case
  504. // this type of packet is not allowed.
  505. handleUnexpectedPacket();
  506. return;
  507. }
  508. throw SshClientException(SshAuthenticationError, tr("Password expired."));
  509. }
  510. void SshConnectionPrivate::handleUserAuthInfoRequestPacket()
  511. {
  512. if (m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods
  513. && !m_triedAllPasswordBasedMethods) {
  514. // This means we just tried to authorize via "password", in which case
  515. // this type of packet is not allowed.
  516. handleUnexpectedPacket();
  517. return;
  518. }
  519. const SshUserAuthInfoRequestPacket requestPacket
  520. = m_incomingPacket.extractUserAuthInfoRequest();
  521. QStringList responses;
  522. responses.reserve(requestPacket.prompts.count());
  523. // Not very interactive, admittedly, but we don't want to be for now.
  524. for (int i = 0; i < requestPacket.prompts.count(); ++i)
  525. responses << m_connParams.password();
  526. m_sendFacility.sendUserAuthInfoResponsePacket(responses);
  527. }
  528. void SshConnectionPrivate::handleUserAuthBannerPacket()
  529. {
  530. emit dataAvailable(m_incomingPacket.extractUserAuthBanner().message);
  531. }
  532. void SshConnectionPrivate::handleUnexpectedPacket()
  533. {
  534. throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
  535. "Unexpected packet.", tr("Unexpected packet of type %1.")
  536. .arg(m_incomingPacket.type()));
  537. }
  538. void SshConnectionPrivate::handleGlobalRequest()
  539. {
  540. m_sendFacility.sendRequestFailurePacket();
  541. }
  542. void SshConnectionPrivate::handleUserAuthSuccessPacket()
  543. {
  544. m_state = ConnectionEstablished;
  545. m_timeoutTimer.stop();
  546. emit connected();
  547. m_lastInvalidMsgSeqNr = InvalidSeqNr;
  548. connect(&m_keepAliveTimer, &QTimer::timeout, this, &SshConnectionPrivate::sendKeepAlivePacket);
  549. m_keepAliveTimer.start();
  550. }
  551. void SshConnectionPrivate::handleUserAuthFailurePacket()
  552. {
  553. if (!m_pendingKeyChecks.isEmpty()) {
  554. const QByteArray key = m_pendingKeyChecks.dequeue();
  555. SshAgent::removeDataToSign(key, tokenForAgent());
  556. qCDebug(sshLog) << "server rejected one of the keys supplied by the agent,"
  557. << m_pendingKeyChecks.count() << "keys remaining";
  558. if (m_pendingKeyChecks.isEmpty() && m_agentKeyToUse.isEmpty()) {
  559. throw SshClientException(SshAuthenticationError, tr("The server rejected all keys "
  560. "known to the ssh-agent."));
  561. }
  562. return;
  563. }
  564. // TODO: Evaluate "authentications that can continue" field and act on it.
  565. if (m_connParams.authenticationType
  566. == SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods
  567. && !m_triedAllPasswordBasedMethods) {
  568. m_triedAllPasswordBasedMethods = true;
  569. m_sendFacility.sendUserAuthByKeyboardInteractiveRequestPacket(
  570. m_connParams.userName().toUtf8(),
  571. SshCapabilities::SshConnectionService);
  572. return;
  573. }
  574. m_timeoutTimer.stop();
  575. QString errorMsg;
  576. switch (m_connParams.authenticationType) {
  577. case SshConnectionParameters::AuthenticationTypePublicKey:
  578. case SshConnectionParameters::AuthenticationTypeAgent:
  579. errorMsg = tr("Server rejected key.");
  580. break;
  581. default:
  582. errorMsg = tr("Server rejected password.");
  583. break;
  584. }
  585. throw SshClientException(SshAuthenticationError, errorMsg);
  586. }
  587. void SshConnectionPrivate::handleUserAuthKeyOkPacket()
  588. {
  589. const SshUserAuthPkOkPacket &msg = m_incomingPacket.extractUserAuthPkOk();
  590. qCDebug(sshLog) << "server accepted key of type" << msg.algoName;
  591. if (m_pendingKeyChecks.isEmpty()) {
  592. throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, "Unexpected packet",
  593. tr("Server sent unexpected SSH_MSG_USERAUTH_PK_OK packet."));
  594. }
  595. const QByteArray key = m_pendingKeyChecks.dequeue();
  596. if (key != msg.keyBlob) {
  597. // The server must answer the requests in the order we sent them.
  598. throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, "Unexpected packet content",
  599. tr("Server sent unexpected key in SSH_MSG_USERAUTH_PK_OK packet."));
  600. }
  601. const uint token = tokenForAgent();
  602. if (!m_agentKeyToUse.isEmpty()) {
  603. qCDebug(sshLog) << "another key has already been accepted, ignoring this one";
  604. SshAgent::removeDataToSign(key, token);
  605. return;
  606. }
  607. m_agentKeyToUse = key;
  608. qCDebug(sshLog) << "requesting signature from agent";
  609. SshAgent::requestSignature(key, token);
  610. }
  611. void SshConnectionPrivate::handleDebugPacket()
  612. {
  613. const SshDebug &msg = m_incomingPacket.extractDebug();
  614. if (msg.display)
  615. emit dataAvailable(msg.message);
  616. }
  617. void SshConnectionPrivate::handleUnimplementedPacket()
  618. {
  619. const SshUnimplemented &msg = m_incomingPacket.extractUnimplemented();
  620. if (msg.invalidMsgSeqNr != m_lastInvalidMsgSeqNr) {
  621. throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
  622. "Unexpected packet", tr("The server sent an unexpected SSH packet "
  623. "of type SSH_MSG_UNIMPLEMENTED."));
  624. }
  625. m_lastInvalidMsgSeqNr = InvalidSeqNr;
  626. m_timeoutTimer.stop();
  627. m_keepAliveTimer.start();
  628. }
  629. void SshConnectionPrivate::handleChannelRequest()
  630. {
  631. m_channelManager->handleChannelRequest(m_incomingPacket);
  632. }
  633. void SshConnectionPrivate::handleChannelOpen()
  634. {
  635. m_channelManager->handleChannelOpen(m_incomingPacket);
  636. }
  637. void SshConnectionPrivate::handleChannelOpenFailure()
  638. {
  639. m_channelManager->handleChannelOpenFailure(m_incomingPacket);
  640. }
  641. void SshConnectionPrivate::handleChannelOpenConfirmation()
  642. {
  643. m_channelManager->handleChannelOpenConfirmation(m_incomingPacket);
  644. }
  645. void SshConnectionPrivate::handleChannelSuccess()
  646. {
  647. m_channelManager->handleChannelSuccess(m_incomingPacket);
  648. }
  649. void SshConnectionPrivate::handleChannelFailure()
  650. {
  651. m_channelManager->handleChannelFailure(m_incomingPacket);
  652. }
  653. void SshConnectionPrivate::handleChannelWindowAdjust()
  654. {
  655. m_channelManager->handleChannelWindowAdjust(m_incomingPacket);
  656. }
  657. void SshConnectionPrivate::handleChannelData()
  658. {
  659. m_channelManager->handleChannelData(m_incomingPacket);
  660. }
  661. void SshConnectionPrivate::handleChannelExtendedData()
  662. {
  663. m_channelManager->handleChannelExtendedData(m_incomingPacket);
  664. }
  665. void SshConnectionPrivate::handleChannelEof()
  666. {
  667. m_channelManager->handleChannelEof(m_incomingPacket);
  668. }
  669. void SshConnectionPrivate::handleChannelClose()
  670. {
  671. m_channelManager->handleChannelClose(m_incomingPacket);
  672. }
  673. void SshConnectionPrivate::handleDisconnect()
  674. {
  675. const SshDisconnect msg = m_incomingPacket.extractDisconnect();
  676. throw SshServerException(SSH_DISCONNECT_CONNECTION_LOST,
  677. "", tr("Server closed connection: %1").arg(msg.description));
  678. }
  679. void SshConnectionPrivate::handleRequestSuccess()
  680. {
  681. m_channelManager->handleRequestSuccess(m_incomingPacket);
  682. }
  683. void SshConnectionPrivate::handleRequestFailure()
  684. {
  685. m_channelManager->handleRequestFailure(m_incomingPacket);
  686. }
  687. void SshConnectionPrivate::sendData(const QByteArray &data)
  688. {
  689. if (canUseSocket())
  690. m_socket->write(data);
  691. }
  692. uint SshConnectionPrivate::tokenForAgent() const
  693. {
  694. return qHash(m_sendFacility.sessionId());
  695. }
  696. void SshConnectionPrivate::handleSocketDisconnected()
  697. {
  698. closeConnection(SSH_DISCONNECT_CONNECTION_LOST, SshClosedByServerError,
  699. "Connection closed unexpectedly.",
  700. tr("Connection closed unexpectedly."));
  701. }
  702. void SshConnectionPrivate::handleSocketError()
  703. {
  704. if (m_error == SshNoError) {
  705. closeConnection(SSH_DISCONNECT_CONNECTION_LOST, SshSocketError,
  706. "Network error", m_socket->errorString());
  707. }
  708. }
  709. void SshConnectionPrivate::handleTimeout()
  710. {
  711. const QString errorMessage = m_state == WaitingForAgentKeys
  712. ? tr("Timeout waiting for keys from ssh-agent.")
  713. : tr("Timeout waiting for reply from server.");
  714. closeConnection(SSH_DISCONNECT_BY_APPLICATION, SshTimeoutError, "", errorMessage);
  715. }
  716. void SshConnectionPrivate::sendKeepAlivePacket()
  717. {
  718. // This type of message is not allowed during key exchange.
  719. if (m_keyExchangeState != NoKeyExchange) {
  720. m_keepAliveTimer.start();
  721. return;
  722. }
  723. Q_ASSERT(m_lastInvalidMsgSeqNr == InvalidSeqNr);
  724. m_lastInvalidMsgSeqNr = m_sendFacility.nextClientSeqNr();
  725. m_sendFacility.sendInvalidPacket();
  726. m_timeoutTimer.start();
  727. }
  728. void SshConnectionPrivate::handleAgentKeysUpdated()
  729. {
  730. m_agentKeysUpToDate = true;
  731. if (m_state == WaitingForAgentKeys) {
  732. m_state = UserAuthRequested;
  733. tryAllAgentKeys();
  734. }
  735. }
  736. void SshConnectionPrivate::handleSignatureFromAgent(const QByteArray &key,
  737. const QByteArray &signature, uint token)
  738. {
  739. if (token != tokenForAgent()) {
  740. qCDebug(sshLog) << "signature is for different connection, ignoring";
  741. return;
  742. }
  743. QSSH_ASSERT(key == m_agentKeyToUse);
  744. m_agentSignature = signature;
  745. authenticateWithPublicKey();
  746. }
  747. void SshConnectionPrivate::tryAllAgentKeys()
  748. {
  749. const QList<QByteArray> &keys = SshAgent::publicKeys();
  750. if (keys.isEmpty())
  751. throw SshClientException(SshAuthenticationError, tr("ssh-agent has no keys."));
  752. qCDebug(sshLog) << "trying authentication with" << keys.count()
  753. << "public keys received from agent";
  754. for (const QByteArray &key : keys) {
  755. m_sendFacility.sendQueryPublicKeyPacket(m_connParams.userName().toUtf8(),
  756. SshCapabilities::SshConnectionService, key);
  757. m_pendingKeyChecks.enqueue(key);
  758. }
  759. }
  760. void SshConnectionPrivate::authenticateWithPublicKey()
  761. {
  762. qCDebug(sshLog) << "sending actual authentication request";
  763. QByteArray key;
  764. QByteArray signature;
  765. if (m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypeAgent) {
  766. // Agent is not needed anymore after this point.
  767. disconnect(&SshAgent::instance(), nullptr, this, nullptr);
  768. key = m_agentKeyToUse;
  769. signature = m_agentSignature;
  770. }
  771. m_sendFacility.sendUserAuthByPublicKeyRequestPacket(m_connParams.userName().toUtf8(),
  772. SshCapabilities::SshConnectionService, key, signature);
  773. }
  774. void SshConnectionPrivate::setAgentError()
  775. {
  776. m_error = SshAgentError;
  777. m_errorString = SshAgent::errorString();
  778. emit error(m_error);
  779. }
  780. void SshConnectionPrivate::connectToHost()
  781. {
  782. QSSH_ASSERT_AND_RETURN(m_state == SocketUnconnected);
  783. m_incomingData.clear();
  784. m_incomingPacket.reset();
  785. m_sendFacility.reset();
  786. m_error = SshNoError;
  787. m_ignoreNextPacket = false;
  788. m_errorString.clear();
  789. m_serverId.clear();
  790. m_serverHasSentDataBeforeId = false;
  791. m_agentSignature.clear();
  792. m_agentKeysUpToDate = false;
  793. m_pendingKeyChecks.clear();
  794. m_agentKeyToUse.clear();
  795. switch (m_connParams.authenticationType) {
  796. case SshConnectionParameters::AuthenticationTypePublicKey:
  797. try {
  798. createPrivateKey();
  799. break;
  800. } catch (const SshClientException &ex) {
  801. m_error = ex.error;
  802. m_errorString = ex.errorString;
  803. emit error(m_error);
  804. return;
  805. }
  806. case SshConnectionParameters::AuthenticationTypeAgent:
  807. if (SshAgent::hasError()) {
  808. setAgentError();
  809. return;
  810. }
  811. connect(&SshAgent::instance(), &SshAgent::errorOccurred,
  812. this, &SshConnectionPrivate::setAgentError);
  813. connect(&SshAgent::instance(), &SshAgent::keysUpdated,
  814. this, &SshConnectionPrivate::handleAgentKeysUpdated);
  815. SshAgent::refreshKeys();
  816. connect(&SshAgent::instance(), &SshAgent::signatureAvailable,
  817. this, &SshConnectionPrivate::handleSignatureFromAgent);
  818. break;
  819. default:
  820. break;
  821. }
  822. connect(m_socket, &QAbstractSocket::connected,
  823. this, &SshConnectionPrivate::handleSocketConnected);
  824. connect(m_socket, &QIODevice::readyRead,
  825. this, &SshConnectionPrivate::handleIncomingData);
  826. // connect(m_socket, &QAbstractSocket::errorOccurred, this, &SshConnectionPrivate::handleSocketError);
  827. connect(m_socket, &QAbstractSocket::disconnected,
  828. this, &SshConnectionPrivate::handleSocketDisconnected);
  829. connect(&m_timeoutTimer, &QTimer::timeout, this, &SshConnectionPrivate::handleTimeout);
  830. m_state = SocketConnecting;
  831. m_keyExchangeState = NoKeyExchange;
  832. m_timeoutTimer.start();
  833. m_socket->connectToHost(m_connParams.host(), m_connParams.port());
  834. }
  835. void SshConnectionPrivate::closeConnection(SshErrorCode sshError,
  836. SshError userError, const QByteArray &serverErrorString,
  837. const QString &userErrorString)
  838. {
  839. // Prevent endless loops by recursive exceptions.
  840. if (m_state == SocketUnconnected || m_error != SshNoError)
  841. return;
  842. m_error = userError;
  843. m_errorString = userErrorString;
  844. m_timeoutTimer.stop();
  845. disconnect(m_socket, nullptr, this, nullptr);
  846. disconnect(&m_timeoutTimer, nullptr, this, nullptr);
  847. m_keepAliveTimer.stop();
  848. disconnect(&m_keepAliveTimer, nullptr, this, nullptr);
  849. try {
  850. m_channelManager->closeAllChannels(SshChannelManager::CloseAllAndReset);
  851. // Crypto initialization failed
  852. if (m_sendFacility.encrypterIsValid()) {
  853. m_sendFacility.sendDisconnectPacket(sshError, serverErrorString);
  854. }
  855. } catch (...) {} // Nothing sensible to be done here.
  856. if (m_error != SshNoError)
  857. emit error(userError);
  858. if (m_state == ConnectionEstablished)
  859. emit disconnected();
  860. if (canUseSocket())
  861. m_socket->disconnectFromHost();
  862. m_state = SocketUnconnected;
  863. }
  864. bool SshConnectionPrivate::canUseSocket() const
  865. {
  866. return m_socket->isValid()
  867. && m_socket->state() == QAbstractSocket::ConnectedState;
  868. }
  869. void SshConnectionPrivate::createPrivateKey()
  870. {
  871. if (m_connParams.privateKeyFile.isEmpty())
  872. throw SshClientException(SshKeyFileError, tr("No private key file given."));
  873. QFile keyFile(m_connParams.privateKeyFile);
  874. if (!keyFile.open(QIODevice::ReadOnly)) {
  875. throw SshClientException(SshKeyFileError,
  876. tr("Private key file error: %1").arg(keyFile.errorString()));
  877. }
  878. m_sendFacility.createAuthenticationKey(keyFile.readAll());
  879. }
  880. QSharedPointer<SshRemoteProcess> SshConnectionPrivate::createRemoteProcess(const QByteArray &command)
  881. {
  882. return m_channelManager->createRemoteProcess(command);
  883. }
  884. QSharedPointer<SshRemoteProcess> SshConnectionPrivate::createRemoteShell()
  885. {
  886. return m_channelManager->createRemoteShell();
  887. }
  888. QSharedPointer<SftpChannel> SshConnectionPrivate::createSftpChannel()
  889. {
  890. return m_channelManager->createSftpChannel();
  891. }
  892. SshDirectTcpIpTunnel::Ptr SshConnectionPrivate::createDirectTunnel(const QString &originatingHost,
  893. quint16 originatingPort, const QString &remoteHost, quint16 remotePort)
  894. {
  895. return m_channelManager->createDirectTunnel(originatingHost, originatingPort, remoteHost,
  896. remotePort);
  897. }
  898. SshTcpIpForwardServer::Ptr SshConnectionPrivate::createForwardServer(const QString &bindAddress,
  899. quint16 bindPort)
  900. {
  901. return m_channelManager->createForwardServer(bindAddress, bindPort);
  902. }
  903. const quint64 SshConnectionPrivate::InvalidSeqNr = static_cast<quint64>(-1);
  904. } // namespace Internal
  905. } // namespace QSsh