diff options
author | Mårten Nordheim <marten.nordheim@qt.io> | 2022-08-16 13:33:15 +0200 |
---|---|---|
committer | Mårten Nordheim <marten.nordheim@qt.io> | 2022-08-17 19:55:18 +0000 |
commit | 1b68e0b71786ce509cadf0771a1c4c1d71a7294b (patch) | |
tree | 0b427c5c091774c5cb34b23cc7a28e0265d078bf /src/network/ssl | |
parent | fb4123f36a88f6e890ed2e7f9b462665bfdcd6c0 (diff) |
QSslServer: Check that first byte is ClientHello
SecureTransport ignores any content that comes in until it is large
enough to be a handshake. So a plaintext client may be left hanging
while it is waiting for a response.
Pick-to: 6.4
Change-Id: I501ae61d89d516765c7ba5f0d916d9246fde5d4d
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
Diffstat (limited to 'src/network/ssl')
-rw-r--r-- | src/network/ssl/qsslserver.cpp | 59 | ||||
-rw-r--r-- | src/network/ssl/qsslserver_p.h | 25 |
2 files changed, 82 insertions, 2 deletions
diff --git a/src/network/ssl/qsslserver.cpp b/src/network/ssl/qsslserver.cpp index a26e79e315..1611046d0e 100644 --- a/src/network/ssl/qsslserver.cpp +++ b/src/network/ssl/qsslserver.cpp @@ -256,6 +256,8 @@ void QSslServer::incomingConnection(qintptr socket) pSslSocket->deleteLater(); }); connect(pSslSocket, &QSslSocket::encrypted, this, [this, pSslSocket]() { + Q_D(QSslServer); + d->removeSocketData(quintptr(pSslSocket)); pSslSocket->disconnect(this); addPendingConnection(pSslSocket); }); @@ -278,10 +280,63 @@ void QSslServer::incomingConnection(qintptr socket) Q_EMIT handshakeInterruptedOnError(pSslSocket, error); }); - Q_EMIT startedEncryptionHandshake(pSslSocket); + d_func()->initializeHandshakeProcess(pSslSocket); + } +} + +void QSslServerPrivate::initializeHandshakeProcess(QSslSocket *socket) +{ + Q_Q(QSslServer); + QMetaObject::Connection readyRead = QObject::connect( + socket, &QSslSocket::readyRead, q, [this]() { checkClientHelloAndContinue(); }); + + QMetaObject::Connection destroyed = + QObject::connect(socket, &QSslSocket::destroyed, q, [this](QObject *obj) { + // This cast is not safe to use since the socket is inside the + // QObject dtor, but we only use the pointer value! + removeSocketData(quintptr(obj)); + }); + socketData.emplace(quintptr(socket), readyRead, destroyed); +} + +// This function may be called while in the socket's QObject dtor, __never__ use +// the socket for anything other than a lookup! +void QSslServerPrivate::removeSocketData(quintptr socket) +{ + auto it = socketData.find(socket); + if (it != socketData.end()) { + it->disconnectSignals(); + socketData.erase(it); + } +} - pSslSocket->startServerEncryption(); +void QSslServerPrivate::checkClientHelloAndContinue() +{ + Q_Q(QSslServer); + QSslSocket *socket = qobject_cast<QSslSocket *>(q->sender()); + if (Q_UNLIKELY(!socket) || socket->bytesAvailable() <= 0) + return; + + char byte = '\0'; + if (socket->peek(&byte, 1) != 1) { + socket->deleteLater(); + return; } + + auto it = socketData.find(quintptr(socket)); + const bool foundData = it != socketData.end(); + if (foundData && it->readyReadConnection) + QObject::disconnect(std::exchange(it->readyReadConnection, {})); + + constexpr char CLIENT_HELLO = 0x16; + if (byte != CLIENT_HELLO) { + socket->disconnectFromHost(); + socket->deleteLater(); + return; + } + + socket->startServerEncryption(); + Q_EMIT q->startedEncryptionHandshake(socket); } QT_END_NAMESPACE diff --git a/src/network/ssl/qsslserver_p.h b/src/network/ssl/qsslserver_p.h index b4b6490ed4..3c7cce0355 100644 --- a/src/network/ssl/qsslserver_p.h +++ b/src/network/ssl/qsslserver_p.h @@ -16,8 +16,13 @@ // We mean it. // +#include <QtNetwork/private/qtnetworkglobal_p.h> + +#include <QtCore/qhash.h> + #include <QtNetwork/QSslConfiguration> #include <QtNetwork/private/qtcpserver_p.h> +#include <utility> QT_BEGIN_NAMESPACE @@ -27,6 +32,26 @@ public: Q_DECLARE_PUBLIC(QSslServer) QSslServerPrivate(); + void checkClientHelloAndContinue(); + void initializeHandshakeProcess(QSslSocket *socket); + void removeSocketData(quintptr socket); + + struct SocketData { + QMetaObject::Connection readyReadConnection; + QMetaObject::Connection destroyedConnection; + + SocketData(QMetaObject::Connection readyRead, QMetaObject::Connection destroyed) + : readyReadConnection(readyRead), destroyedConnection(destroyed) + { + } + + void disconnectSignals() + { + QObject::disconnect(std::exchange(readyReadConnection, {})); + QObject::disconnect(std::exchange(destroyedConnection, {})); + } + }; + QHash<quintptr, SocketData> socketData; QSslConfiguration sslConfiguration; }; |