aboutsummaryrefslogtreecommitdiff
path: root/deskutils/kdeconnect-kde/files/patch-CVE-2020-26164
diff options
context:
space:
mode:
Diffstat (limited to 'deskutils/kdeconnect-kde/files/patch-CVE-2020-26164')
-rw-r--r--deskutils/kdeconnect-kde/files/patch-CVE-2020-26164377
1 files changed, 377 insertions, 0 deletions
diff --git a/deskutils/kdeconnect-kde/files/patch-CVE-2020-26164 b/deskutils/kdeconnect-kde/files/patch-CVE-2020-26164
new file mode 100644
index 000000000000..267f90b078d2
--- /dev/null
+++ b/deskutils/kdeconnect-kde/files/patch-CVE-2020-26164
@@ -0,0 +1,377 @@
+KDE Connect 20.08.2 patches several code paths that could result in a DoS.
+You can apply these patches on top of 20.08.1:
+ https://invent.kde.org/network/kdeconnect-kde/-/commit/f183b5447bad47655c21af87214579f03bf3a163
+ https://invent.kde.org/network/kdeconnect-kde/-/commit/b279c52101d3f7cc30a26086d58de0b5f1c547fa
+ https://invent.kde.org/network/kdeconnect-kde/-/commit/d35b88c1b25fe13715f9170f18674d476ca9acdc
+ https://invent.kde.org/network/kdeconnect-kde/-/commit/b496e66899e5bc9547b6537a7f44ab44dd0aaf38
+ https://invent.kde.org/network/kdeconnect-kde/-/commit/5310eae85dbdf92fba30375238a2481f2e34943e
+ https://invent.kde.org/network/kdeconnect-kde/-/commit/721ba9faafb79aac73973410ee1dd3624ded97a5
+ https://invent.kde.org/network/kdeconnect-kde/-/commit/ae58b9dec49c809b85b5404cee17946116f8a706
+ https://invent.kde.org/network/kdeconnect-kde/-/commit/66c768aa9e7fba30b119c8b801efd49ed1270b0a
+ https://invent.kde.org/network/kdeconnect-kde/-/commit/85b691e40f525e22ca5cc4ebe79c361d71d7dc05
+ https://invent.kde.org/network/kdeconnect-kde/-/commit/48180b46552d40729a36b7431e97bbe2b5379306
+
+
+From 6a3aa96fc0fa8a6f8d92afa2c603a71db061482f Mon Sep 17 00:00:00 2001
+From: Albert Vaca Cintora <albertvaka@gmail.com>
+Date: Thu, 24 Sep 2020 16:59:22 +0200
+Subject: [PATCH] Do not ignore SSL errors, except for self-signed cert errors.
+
+Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
+
+Do not leak the local user in the device name.
+
+Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
+
+Fix use after free in LanLinkProvider::connectError()
+
+If QSslSocket::connectToHost() hasn't finished running.
+
+Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
+
+Limit identity packets to 8KiB
+
+Healthy identity packages shouldn't be that big and we don't want to
+allow systems around us to send us ever humongous packages that will
+just leave us without any memory.
+
+Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
+
+Do not let lanlink connections stay open for long without authenticating
+
+If there's no information received, close the socket to try again.
+
+Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
+
+Don't brute-force reading the socket
+
+The package will arrive eventually, and dataReceived will be emitted.
+Otherwise we just end up calling dataReceived to no end.
+
+Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
+
+Limit number of connected sockets from unpaired devices
+
+Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
+
+Do not remember more than a few identity packets at a time
+
+To prevent the kdeconnect process from using too much memory.
+
+Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
+
+Limit the ports we try to connect to to the port range of KDE Connect
+
+So we can't trigger connections to other services.
+
+Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
+
+Do not replace connections for a given deviceId if the certs have changed
+
+Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
+---
+ core/backends/lan/landevicelink.cpp | 5 ++
+ core/backends/lan/landevicelink.h | 1 +
+ core/backends/lan/lanlinkprovider.cpp | 79 ++++++++++++++++++++++----
+ core/backends/lan/socketlinereader.cpp | 8 ---
+ core/kdeconnectconfig.cpp | 8 +--
+ tests/testsocketlinereader.cpp | 31 +++++++++-
+ 6 files changed, 103 insertions(+), 29 deletions(-)
+
+diff --git core/backends/lan/landevicelink.cpp core/backends/lan/landevicelink.cpp
+index 8a65fb92..41af6f0e 100644
+--- core/backends/lan/landevicelink.cpp
++++ core/backends/lan/landevicelink.cpp
+@@ -192,3 +192,8 @@ bool LanDeviceLink::linkShouldBeKeptAlive() {
+ //return (mConnectionSource == ConnectionStarted::Remotely || pairStatus() == Paired);
+
+ }
++
++QSslCertificate LanDeviceLink::certificate() const
++{
++ return m_socketLineReader->peerCertificate();
++}
+diff --git core/backends/lan/landevicelink.h core/backends/lan/landevicelink.h
+index 28f63db2..485c58b5 100644
+--- core/backends/lan/landevicelink.h
++++ core/backends/lan/landevicelink.h
+@@ -56,6 +56,7 @@ public:
+ bool linkShouldBeKeptAlive() override;
+
+ QHostAddress hostAddress() const;
++ QSslCertificate certificate() const;
+
+ private Q_SLOTS:
+ void dataReceived();
+diff --git core/backends/lan/lanlinkprovider.cpp core/backends/lan/lanlinkprovider.cpp
+index d9a7d8fa..372cdc8f 100644
+--- core/backends/lan/lanlinkprovider.cpp
++++ core/backends/lan/lanlinkprovider.cpp
+@@ -46,6 +46,9 @@
+
+ #define MIN_VERSION_WITH_SSL_SUPPORT 6
+
++static const int MAX_UNPAIRED_CONNECTIONS = 42;
++static const int MAX_REMEMBERED_IDENTITY_PACKETS = 42;
++
+ LanLinkProvider::LanLinkProvider(
+ bool testMode,
+ quint16 udpBroadcastPort,
+@@ -220,9 +223,20 @@ void LanLinkProvider::udpBroadcastReceived()
+ }
+
+ int tcpPort = receivedPacket->get<int>(QStringLiteral("tcpPort"));
++ if (tcpPort < MIN_TCP_PORT || tcpPort > MAX_TCP_PORT) {
++ qCDebug(KDECONNECT_CORE) << "TCP port outside of kdeconnect's range";
++ delete receivedPacket;
++ continue;
++ }
+
+ //qCDebug(KDECONNECT_CORE) << "Received Udp identity packet from" << sender << " asking for a tcp connection on port " << tcpPort;
+
++ if (m_receivedIdentityPackets.size() > MAX_REMEMBERED_IDENTITY_PACKETS) {
++ qCWarning(KDECONNECT_CORE) << "Too many remembered identities, ignoring" << receivedPacket->get<QString>(QStringLiteral("deviceId")) << "received via UDP";
++ delete receivedPacket;
++ continue;
++ }
++
+ QSslSocket* socket = new QSslSocket(this);
+ socket->setProxy(QNetworkProxy::NoProxy);
+ m_receivedIdentityPackets[socket].np = receivedPacket;
+@@ -252,7 +266,7 @@ void LanLinkProvider::connectError(QAbstractSocket::SocketError socketError)
+ //The socket we created didn't work, and we didn't manage
+ //to create a LanDeviceLink from it, deleting everything.
+ delete m_receivedIdentityPackets.take(socket).np;
+- delete socket;
++ socket->deleteLater();
+ }
+
+ //We received a UDP packet and answered by connecting to them by TCP. This gets called on a successful connection.
+@@ -297,9 +311,7 @@ void LanLinkProvider::tcpSocketConnected()
+
+ connect(socket, &QSslSocket::encrypted, this, &LanLinkProvider::encrypted);
+
+- if (isDeviceTrusted) {
+- connect(socket, QOverload<const QList<QSslError> &>::of(&QSslSocket::sslErrors), this, &LanLinkProvider::sslErrors);
+- }
++ connect(socket, QOverload<const QList<QSslError> &>::of(&QSslSocket::sslErrors), this, &LanLinkProvider::sslErrors);
+
+ socket->startServerEncryption();
+
+@@ -326,8 +338,6 @@ void LanLinkProvider::encrypted()
+
+ QSslSocket* socket = qobject_cast<QSslSocket*>(sender());
+ if (!socket) return;
+- // TODO delete me?
+- disconnect(socket, QOverload<const QList<QSslError> &>::of(&QSslSocket::sslErrors), this, &LanLinkProvider::sslErrors);
+
+ Q_ASSERT(socket->mode() != QSslSocket::UnencryptedMode);
+ LanDeviceLink::ConnectionStarted connectionOrigin = (socket->mode() == QSslSocket::SslClientMode)? LanDeviceLink::Locally : LanDeviceLink::Remotely;
+@@ -335,6 +345,12 @@ void LanLinkProvider::encrypted()
+ NetworkPacket* receivedPacket = m_receivedIdentityPackets[socket].np;
+ const QString& deviceId = receivedPacket->get<QString>(QStringLiteral("deviceId"));
+
++ if (m_links.contains(deviceId) && m_links[deviceId]->certificate() != socket->peerCertificate()) {
++ socket->disconnectFromHost();
++ qCWarning(KDECONNECT_CORE) << "Got connection for the same deviceId but certificates don't match. Ignoring " << deviceId;
++ return;
++ }
++
+ addLink(deviceId, socket, receivedPacket, connectionOrigin);
+
+ // Copied from tcpSocketConnected slot, now delete received packet
+@@ -346,14 +362,20 @@ void LanLinkProvider::sslErrors(const QList<QSslError>& errors)
+ QSslSocket* socket = qobject_cast<QSslSocket*>(sender());
+ if (!socket) return;
+
+- qCDebug(KDECONNECT_CORE) << "Failing due to " << errors;
+- Device* device = Daemon::instance()->getDevice(socket->peerVerifyName());
+- if (device) {
+- device->unpair();
++ bool fatal = false;
++ for (const QSslError& error : errors) {
++ if (error.error() != QSslError::SelfSignedCertificate) {
++ qCCritical(KDECONNECT_CORE) << "Disconnecting due to fatal SSL Error: " << error;
++ fatal = true;
++ } else {
++ qCDebug(KDECONNECT_CORE) << "Ignoring self-signed cert error";
++ }
+ }
+
+- delete m_receivedIdentityPackets.take(socket).np;
+- // Socket disconnects itself on ssl error and will be deleted by deleteLater slot, no need to delete manually
++ if (fatal) {
++ socket->disconnectFromHost();
++ delete m_receivedIdentityPackets.take(socket).np;
++ }
+ }
+
+ //I'm the new device and this is the answer to my UDP identity packet (no data received yet). They are connecting to us through TCP, and they should send an identity.
+@@ -372,6 +394,16 @@ void LanLinkProvider::newConnection()
+ connect(socket, &QIODevice::readyRead,
+ this, &LanLinkProvider::dataReceived);
+
++ QTimer* timer = new QTimer(socket);
++ timer->setSingleShot(true);
++ timer->setInterval(1000);
++ connect(socket, &QSslSocket::encrypted,
++ timer, &QObject::deleteLater);
++ connect(timer, &QTimer::timeout, socket, [socket] {
++ qCWarning(KDECONNECT_CORE) << "LanLinkProvider/newConnection: Host timed out without sending any identity." << socket->peerAddress();
++ socket->disconnectFromHost();
++ });
++ timer->start();
+ }
+ }
+
+@@ -379,6 +411,14 @@ void LanLinkProvider::newConnection()
+ void LanLinkProvider::dataReceived()
+ {
+ QSslSocket* socket = qobject_cast<QSslSocket*>(sender());
++ //the size here is arbitrary and is now at 8192 bytes. It needs to be considerably long as it includes the capabilities but there needs to be a limit
++ //Tested between my systems and I get around 2000 per identity package.
++ if (socket->bytesAvailable() > 8192) {
++ qCWarning(KDECONNECT_CORE) << "LanLinkProvider/newConnection: Suspiciously long identity package received. Closing connection." << socket->peerAddress() << socket->bytesAvailable();
++ socket->disconnectFromHost();
++ return;
++ }
++
+ #if QT_VERSION < QT_VERSION_CHECK(5,7,0)
+ if (!socket->canReadLine())
+ return;
+@@ -413,6 +453,12 @@ void LanLinkProvider::dataReceived()
+ return;
+ }
+
++ if (m_receivedIdentityPackets.size() > MAX_REMEMBERED_IDENTITY_PACKETS) {
++ qCWarning(KDECONNECT_CORE) << "Too many remembered identities, ignoring" << np->get<QString>(QStringLiteral("deviceId")) << "received via TCP";
++ delete np;
++ return;
++ }
++
+ // Needed in "encrypted" if ssl is used, similar to "tcpSocketConnected"
+ m_receivedIdentityPackets[socket].np = np;
+
+@@ -535,6 +581,15 @@ void LanLinkProvider::addLink(const QString& deviceId, QSslSocket* socket, Netwo
+ deviceLink->reset(socket, connectionOrigin);
+ } else {
+ deviceLink = new LanDeviceLink(deviceId, this, socket, connectionOrigin);
++ // Socket disconnection will now be handled by LanDeviceLink
++ disconnect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater);
++ bool isDeviceTrusted = KdeConnectConfig::instance().trustedDevices().contains(deviceId);
++ if (!isDeviceTrusted && m_links.size() > MAX_UNPAIRED_CONNECTIONS) {
++ qCWarning(KDECONNECT_CORE) << "Too many unpaired devices to remember them all. Ignoring " << deviceId;
++ socket->disconnectFromHost();
++ socket->deleteLater();
++ return;
++ }
+ connect(deviceLink, &QObject::destroyed, this, &LanLinkProvider::deviceLinkDestroyed);
+ m_links[deviceId] = deviceLink;
+ if (m_pairingHandlers.contains(deviceId)) {
+diff --git core/backends/lan/socketlinereader.cpp core/backends/lan/socketlinereader.cpp
+index f67fdf3f..da77052a 100644
+--- core/backends/lan/socketlinereader.cpp
++++ core/backends/lan/socketlinereader.cpp
+@@ -38,14 +38,6 @@ void SocketLineReader::dataReceived()
+ }
+ }
+
+- //If we still have things to read from the socket, call dataReceived again
+- //We do this manually because we do not trust readyRead to be emitted again
+- //So we call this method again just in case.
+- if (m_socket->bytesAvailable() > 0) {
+- QMetaObject::invokeMethod(this, "dataReceived", Qt::QueuedConnection);
+- return;
+- }
+-
+ //If we have any packets, tell it to the world.
+ if (!m_packets.isEmpty()) {
+ Q_EMIT readyRead();
+diff --git core/kdeconnectconfig.cpp core/kdeconnectconfig.cpp
+index 91719303..a8dbcf5c 100644
+--- core/kdeconnectconfig.cpp
++++ core/kdeconnectconfig.cpp
+@@ -90,13 +90,7 @@ KdeConnectConfig::KdeConnectConfig()
+
+ QString KdeConnectConfig::name()
+ {
+- QString username;
+- #ifdef Q_OS_WIN
+- username = QString::fromLatin1(qgetenv("USERNAME"));
+- #else
+- username = QString::fromLatin1(qgetenv("USER"));
+- #endif
+- QString defaultName = username + QStringLiteral("@") + QHostInfo::localHostName();
++ QString defaultName = QHostInfo::localHostName();
+ QString name = d->m_config->value(QStringLiteral("name"), defaultName).toString();
+ return name;
+ }
+diff --git tests/testsocketlinereader.cpp tests/testsocketlinereader.cpp
+index 75584556..b6425b03 100644
+--- tests/testsocketlinereader.cpp
++++ tests/testsocketlinereader.cpp
+@@ -25,16 +25,19 @@
+ #include <QProcess>
+ #include <QEventLoop>
+ #include <QTimer>
++#include <QSignalSpy>
+
+ class TestSocketLineReader : public QObject
+ {
+ Q_OBJECT
+ public Q_SLOTS:
+- void initTestCase();
++ void init();
++ void cleanup() { delete m_server; }
+ void newPacket();
+
+ private Q_SLOTS:
+ void socketLineReader();
++ void badData();
+
+ private:
+ QTimer m_timer;
+@@ -45,8 +48,9 @@ private:
+ SocketLineReader* m_reader;
+ };
+
+-void TestSocketLineReader::initTestCase()
++void TestSocketLineReader::init()
+ {
++ m_packets.clear();
+ m_server = new Server(this);
+
+ QVERIFY2(m_server->listen(QHostAddress::LocalHost, 8694), "Failed to create local tcp server");
+@@ -97,6 +101,29 @@ void TestSocketLineReader::socketLineReader()
+ }
+ }
+
++void TestSocketLineReader::badData()
++{
++ const QList<QByteArray> dataToSend = { "data1\n", "data" }; //does not end in a \n
++ for (const QByteArray& line : qAsConst(dataToSend)) {
++ m_conn->write(line);
++ }
++ m_conn->flush();
++
++ QSignalSpy spy(m_server, &QTcpServer::newConnection);
++ QVERIFY(m_server->hasPendingConnections() || spy.wait(1000));
++ QSslSocket* sock = m_server->nextPendingConnection();
++
++ QVERIFY2(sock != nullptr, "Could not open a connection to the client");
++
++ m_reader = new SocketLineReader(sock, this);
++ connect(m_reader, &SocketLineReader::readyRead, this, &TestSocketLineReader::newPacket);
++ m_timer.start();
++ m_loop.exec();
++
++ QCOMPARE(m_packets.count(), 1);
++ QCOMPARE(m_packets[0], dataToSend[0]);
++}
++
+ void TestSocketLineReader::newPacket()
+ {
+ if (!m_reader->bytesAvailable()) {
+--
+2.28.0
+