Accepting request 1100023 from KDE:Qt6
Qt 6.5.2 OBS-URL: https://build.opensuse.org/request/show/1100023 OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/qt6-base?expand=0&rev=36
This commit is contained in:
commit
811da9dd7e
@ -1,291 +0,0 @@
|
||||
From ada2c573c1a25f8d96577734968fe317ddfa292a Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= <marten.nordheim@qt.io>
|
||||
Date: Wed, 10 May 2023 16:43:41 +0200
|
||||
Subject: [PATCH] Schannel: Reject certificate not signed by a configured CA
|
||||
certificate
|
||||
|
||||
Not entirely clear why, but when building the certificate chain for a
|
||||
peer the system certificate store is searched for root certificates.
|
||||
General expectation is that after calling
|
||||
`sslConfiguration.setCaCertificates()` the system certificates will
|
||||
not be taken into consideration.
|
||||
|
||||
To work around this behavior, we do a manual check that the root of the
|
||||
chain is part of the configured CA certificates.
|
||||
|
||||
Pick-to: 6.5 6.2 5.15
|
||||
Change-Id: I03666a4d9b0eac39ae97e150b4743120611a11b3
|
||||
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
|
||||
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
|
||||
---
|
||||
src/plugins/tls/schannel/qtls_schannel.cpp | 21 ++++
|
||||
.../network/ssl/client-auth/CMakeLists.txt | 24 ++++
|
||||
.../network/ssl/client-auth/certs/.gitignore | 4 +
|
||||
.../client-auth/certs/accepted-client.conf | 14 +++
|
||||
.../network/ssl/client-auth/certs/generate.sh | 33 +++++
|
||||
.../tst_manual_ssl_client_auth.cpp | 118 ++++++++++++++++++
|
||||
6 files changed, 214 insertions(+)
|
||||
create mode 100644 tests/manual/network/ssl/client-auth/CMakeLists.txt
|
||||
create mode 100644 tests/manual/network/ssl/client-auth/certs/.gitignore
|
||||
create mode 100644 tests/manual/network/ssl/client-auth/certs/accepted-client.conf
|
||||
create mode 100755 tests/manual/network/ssl/client-auth/certs/generate.sh
|
||||
create mode 100644 tests/manual/network/ssl/client-auth/tst_manual_ssl_client_auth.cpp
|
||||
|
||||
diff --git a/src/plugins/tls/schannel/qtls_schannel.cpp b/src/plugins/tls/schannel/qtls_schannel.cpp
|
||||
index d72aaa5a36..ae9ff06eae 100644
|
||||
--- a/src/plugins/tls/schannel/qtls_schannel.cpp
|
||||
+++ b/src/plugins/tls/schannel/qtls_schannel.cpp
|
||||
@@ -2066,6 +2066,27 @@ bool TlsCryptographSchannel::verifyCertContext(CERT_CONTEXT *certContext)
|
||||
verifyDepth = DWORD(q->peerVerifyDepth());
|
||||
|
||||
const auto &caCertificates = q->sslConfiguration().caCertificates();
|
||||
+
|
||||
+ if (!rootCertOnDemandLoadingAllowed()
|
||||
+ && !(chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN)
|
||||
+ && (q->peerVerifyMode() == QSslSocket::VerifyPeer
|
||||
+ || (isClient && q->peerVerifyMode() == QSslSocket::AutoVerifyPeer))) {
|
||||
+ // When verifying a peer Windows "helpfully" builds a chain that
|
||||
+ // may include roots from the system store. But we don't want that if
|
||||
+ // the user has set their own CA certificates.
|
||||
+ // Since Windows claims this is not a partial chain the root is included
|
||||
+ // and we have to check that it is one of our configured CAs.
|
||||
+ CERT_CHAIN_ELEMENT *element = chain->rgpElement[chain->cElement - 1];
|
||||
+ QSslCertificate certificate = getCertificateFromChainElement(element);
|
||||
+ if (!caCertificates.contains(certificate)) {
|
||||
+ auto error = QSslError(QSslError::CertificateUntrusted, certificate);
|
||||
+ sslErrors += error;
|
||||
+ emit q->peerVerifyError(error);
|
||||
+ if (q->state() != QAbstractSocket::ConnectedState)
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
QList<QSslCertificate> peerCertificateChain;
|
||||
for (DWORD i = 0; i < verifyDepth; i++) {
|
||||
CERT_CHAIN_ELEMENT *element = chain->rgpElement[i];
|
||||
diff --git a/tests/manual/network/ssl/client-auth/CMakeLists.txt b/tests/manual/network/ssl/client-auth/CMakeLists.txt
|
||||
new file mode 100644
|
||||
index 0000000000..67ecc20bf4
|
||||
--- /dev/null
|
||||
+++ b/tests/manual/network/ssl/client-auth/CMakeLists.txt
|
||||
@@ -0,0 +1,24 @@
|
||||
+# Copyright (C) 2023 The Qt Company Ltd.
|
||||
+# SPDX-License-Identifier: BSD-3-Clause
|
||||
+
|
||||
+qt_internal_add_manual_test(tst_manual_ssl_client_auth
|
||||
+ SOURCES
|
||||
+ tst_manual_ssl_client_auth.cpp
|
||||
+ LIBRARIES
|
||||
+ Qt::Network
|
||||
+)
|
||||
+
|
||||
+qt_internal_add_resource(tst_manual_ssl_client_auth "tst_manual_ssl_client_auth"
|
||||
+ PREFIX
|
||||
+ "/"
|
||||
+ FILES
|
||||
+ "certs/127.0.0.1.pem"
|
||||
+ "certs/127.0.0.1-key.pem"
|
||||
+ "certs/127.0.0.1-client.pem"
|
||||
+ "certs/127.0.0.1-client-key.pem"
|
||||
+ "certs/accepted-client.pem"
|
||||
+ "certs/accepted-client-key.pem"
|
||||
+ "certs/rootCA.pem"
|
||||
+ BASE
|
||||
+ "certs"
|
||||
+)
|
||||
diff --git a/tests/manual/network/ssl/client-auth/certs/.gitignore b/tests/manual/network/ssl/client-auth/certs/.gitignore
|
||||
new file mode 100644
|
||||
index 0000000000..5866f7b609
|
||||
--- /dev/null
|
||||
+++ b/tests/manual/network/ssl/client-auth/certs/.gitignore
|
||||
@@ -0,0 +1,4 @@
|
||||
+*
|
||||
+!/.gitignore
|
||||
+!/generate.sh
|
||||
+!/accepted-client.conf
|
||||
diff --git a/tests/manual/network/ssl/client-auth/certs/accepted-client.conf b/tests/manual/network/ssl/client-auth/certs/accepted-client.conf
|
||||
new file mode 100644
|
||||
index 0000000000..a88b276efe
|
||||
--- /dev/null
|
||||
+++ b/tests/manual/network/ssl/client-auth/certs/accepted-client.conf
|
||||
@@ -0,0 +1,14 @@
|
||||
+[req]
|
||||
+default_md = sha512
|
||||
+basicConstraints = CA:FALSE
|
||||
+extendedKeyUsage = clientAuth
|
||||
+[req]
|
||||
+distinguished_name = client_distinguished_name
|
||||
+prompt = no
|
||||
+[client_distinguished_name]
|
||||
+C = NO
|
||||
+ST = Oslo
|
||||
+L = Oslo
|
||||
+O = The Qt Project
|
||||
+OU = The Qt Project
|
||||
+CN = Fake Qt Project Client Certificate
|
||||
diff --git a/tests/manual/network/ssl/client-auth/certs/generate.sh b/tests/manual/network/ssl/client-auth/certs/generate.sh
|
||||
new file mode 100755
|
||||
index 0000000000..5dbe3b3712
|
||||
--- /dev/null
|
||||
+++ b/tests/manual/network/ssl/client-auth/certs/generate.sh
|
||||
@@ -0,0 +1,33 @@
|
||||
+#!/bin/bash
|
||||
+# Copyright (C) 2023 The Qt Company Ltd.
|
||||
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
+
|
||||
+# Requires mkcert and openssl
|
||||
+
|
||||
+warn () { echo "$@" >&2; }
|
||||
+die () { warn "$@"; exit 1; }
|
||||
+
|
||||
+
|
||||
+command -v mkcert 1>/dev/null 2>&1 || die "Failed to find mkcert"
|
||||
+command -v openssl 1>/dev/null 2>&1 || die "Failed to find openssl"
|
||||
+
|
||||
+SCRIPT=$(realpath "$0")
|
||||
+SCRIPTPATH=$(dirname "$SCRIPT")
|
||||
+
|
||||
+pushd "$SCRIPTPATH" || die "Unable to pushd to $SCRIPTPATH"
|
||||
+mkcert 127.0.0.1
|
||||
+mkcert -client 127.0.0.1
|
||||
+warn "Remember to run mkcert -install if you haven't already"
|
||||
+
|
||||
+# Generate CA
|
||||
+openssl genrsa -out ca-key.pem 2048
|
||||
+openssl req -new -x509 -noenc -days 365 -key ca-key.pem -out rootCA.pem
|
||||
+
|
||||
+# Generate accepted client certificate
|
||||
+openssl genrsa -out accepted-client-key.pem 2048
|
||||
+openssl req -new -sha512 -nodes -key accepted-client-key.pem -out accepted-client.csr -config accepted-client.conf
|
||||
+openssl x509 -req -sha512 -days 45 -in accepted-client.csr -CA rootCA.pem -CAkey ca-key.pem -CAcreateserial -out accepted-client.pem
|
||||
+rm accepted-client.csr
|
||||
+rm rootCA.srl
|
||||
+
|
||||
+popd || die "Unable to popd"
|
||||
diff --git a/tests/manual/network/ssl/client-auth/tst_manual_ssl_client_auth.cpp b/tests/manual/network/ssl/client-auth/tst_manual_ssl_client_auth.cpp
|
||||
new file mode 100644
|
||||
index 0000000000..2307cbb191
|
||||
--- /dev/null
|
||||
+++ b/tests/manual/network/ssl/client-auth/tst_manual_ssl_client_auth.cpp
|
||||
@@ -0,0 +1,118 @@
|
||||
+// Copyright (C) 2023 The Qt Company Ltd.
|
||||
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
+
|
||||
+#include <QtCore/qcoreapplication.h>
|
||||
+
|
||||
+#include <QtCore/qthread.h>
|
||||
+#include <QtCore/qfile.h>
|
||||
+#include <QtCore/qdir.h>
|
||||
+
|
||||
+#include <QtNetwork/qsslsocket.h>
|
||||
+#include <QtNetwork/qsslserver.h>
|
||||
+#include <QtNetwork/qsslconfiguration.h>
|
||||
+#include <QtNetwork/qsslkey.h>
|
||||
+
|
||||
+// Client and/or server presents a certificate signed by a system-trusted CA
|
||||
+// but the other side presents a certificate signed by a different CA.
|
||||
+constexpr bool TestServerPresentsIncorrectCa = false;
|
||||
+constexpr bool TestClientPresentsIncorrectCa = true;
|
||||
+
|
||||
+class ServerThread : public QThread
|
||||
+{
|
||||
+ Q_OBJECT
|
||||
+public:
|
||||
+ void run() override
|
||||
+ {
|
||||
+ QSslServer server;
|
||||
+
|
||||
+ QSslConfiguration config = server.sslConfiguration();
|
||||
+ QList<QSslCertificate> certs = QSslCertificate::fromPath(QStringLiteral(":/rootCA.pem"));
|
||||
+ config.setCaCertificates(certs);
|
||||
+ config.setLocalCertificate(QSslCertificate::fromPath(QStringLiteral(":/127.0.0.1.pem"))
|
||||
+ .first());
|
||||
+ QFile keyFile(QStringLiteral(":/127.0.0.1-key.pem"));
|
||||
+ if (!keyFile.open(QIODevice::ReadOnly))
|
||||
+ qFatal("Failed to open key file");
|
||||
+ config.setPrivateKey(QSslKey(&keyFile, QSsl::Rsa));
|
||||
+ config.setPeerVerifyMode(QSslSocket::VerifyPeer);
|
||||
+ server.setSslConfiguration(config);
|
||||
+
|
||||
+ connect(&server, &QSslServer::pendingConnectionAvailable, [&server]() {
|
||||
+ QSslSocket *socket = static_cast<QSslSocket *>(server.nextPendingConnection());
|
||||
+ qDebug() << "[s] newConnection" << socket->peerAddress() << socket->peerPort();
|
||||
+ socket->disconnectFromHost();
|
||||
+ qApp->quit();
|
||||
+ });
|
||||
+ connect(&server, &QSslServer::startedEncryptionHandshake, [](QSslSocket *socket) {
|
||||
+ qDebug() << "[s] new handshake" << socket->peerAddress() << socket->peerPort();
|
||||
+ });
|
||||
+ connect(&server, &QSslServer::errorOccurred,
|
||||
+ [](QSslSocket *socket, QAbstractSocket::SocketError error) {
|
||||
+ qDebug() << "[s] errorOccurred" << socket->peerAddress() << socket->peerPort()
|
||||
+ << error << socket->errorString();
|
||||
+ });
|
||||
+ connect(&server, &QSslServer::peerVerifyError,
|
||||
+ [](QSslSocket *socket, const QSslError &error) {
|
||||
+ qDebug() << "[s] peerVerifyError" << socket->peerAddress() << socket->peerPort()
|
||||
+ << error;
|
||||
+ });
|
||||
+ server.listen(QHostAddress::LocalHost, 24242);
|
||||
+
|
||||
+ exec();
|
||||
+
|
||||
+ server.close();
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+int main(int argc, char **argv)
|
||||
+{
|
||||
+ QCoreApplication app(argc, argv);
|
||||
+
|
||||
+ using namespace Qt::StringLiterals;
|
||||
+
|
||||
+ if (!QFileInfo(u":/rootCA.pem"_s).exists())
|
||||
+ qFatal("rootCA.pem not found. Did you run generate.sh in the certs directory?");
|
||||
+
|
||||
+ ServerThread serverThread;
|
||||
+ serverThread.start();
|
||||
+
|
||||
+ QSslSocket socket;
|
||||
+ QSslConfiguration config = socket.sslConfiguration();
|
||||
+ QString certificatePath;
|
||||
+ QString keyFileName;
|
||||
+ if constexpr (TestClientPresentsIncorrectCa) { // true: Present cert signed with incorrect CA: should fail
|
||||
+ certificatePath = u":/127.0.0.1-client.pem"_s;
|
||||
+ keyFileName = u":/127.0.0.1-client-key.pem"_s;
|
||||
+ } else { // false: Use correct CA: should succeed
|
||||
+ certificatePath = u":/accepted-client.pem"_s;
|
||||
+ keyFileName = u":/accepted-client-key.pem"_s;
|
||||
+ }
|
||||
+ config.setLocalCertificate(QSslCertificate::fromPath(certificatePath).first());
|
||||
+ if (TestServerPresentsIncorrectCa) // true: Verify server using incorrect CA: should fail
|
||||
+ config.setCaCertificates(QSslCertificate::fromPath(u":/rootCA.pem"_s));
|
||||
+ QFile keyFile(keyFileName);
|
||||
+ if (!keyFile.open(QIODevice::ReadOnly))
|
||||
+ qFatal("Failed to open key file");
|
||||
+ config.setPrivateKey(QSslKey(&keyFile, QSsl::Rsa));
|
||||
+ socket.setSslConfiguration(config);
|
||||
+
|
||||
+ QObject::connect(&socket, &QSslSocket::encrypted, []() { qDebug() << "[c] encrypted"; });
|
||||
+ QObject::connect(&socket, &QSslSocket::errorOccurred,
|
||||
+ [&socket](QAbstractSocket::SocketError error) {
|
||||
+ qDebug() << "[c] errorOccurred" << error << socket.errorString();
|
||||
+ qApp->quit();
|
||||
+ });
|
||||
+ QObject::connect(&socket, &QSslSocket::sslErrors, [](const QList<QSslError> &errors) {
|
||||
+ qDebug() << "[c] sslErrors" << errors;
|
||||
+ });
|
||||
+ QObject::connect(&socket, &QSslSocket::connected, []() { qDebug() << "[c] connected"; });
|
||||
+
|
||||
+ socket.connectToHostEncrypted(QStringLiteral("127.0.0.1"), 24242);
|
||||
+
|
||||
+ const int res = app.exec();
|
||||
+ serverThread.quit();
|
||||
+ serverThread.wait();
|
||||
+ return res;
|
||||
+}
|
||||
+
|
||||
+#include "tst_manual_ssl_client_auth.moc"
|
||||
--
|
||||
2.40.1
|
||||
|
@ -1,112 +0,0 @@
|
||||
From 57ba6260c0801055b7188fdaa1818b940590f5f1 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= <marten.nordheim@qt.io>
|
||||
Date: Thu, 25 May 2023 14:40:29 +0200
|
||||
Subject: [PATCH] Ssl: Copy the on-demand cert loading bool from default config
|
||||
|
||||
Otherwise individual sockets will still load system certificates when
|
||||
a chain doesn't match against the configured CA certificates.
|
||||
That's not intended behavior, since specifically setting the CA
|
||||
certificates means you don't want the system certificates to be used.
|
||||
|
||||
Follow-up to/amends ada2c573c1a25f8d96577734968fe317ddfa292a
|
||||
|
||||
This is potentially a breaking change because now, if you ever add a
|
||||
CA to the default config, it will disable loading system certificates
|
||||
on demand for all sockets. And the only way to re-enable it is to
|
||||
create a null-QSslConfiguration and set it as the new default.
|
||||
|
||||
Pick-to: 6.5 6.2 5.15
|
||||
Change-Id: Ic3b2ab125c0cdd58ad654af1cb36173960ce2d1e
|
||||
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
|
||||
---
|
||||
src/network/ssl/qsslsocket.cpp | 5 ++++
|
||||
.../tst_manual_ssl_client_auth.cpp | 24 ++++++++++++++++---
|
||||
2 files changed, 26 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
|
||||
index 4eefe43929..0563fd0663 100644
|
||||
--- a/src/network/ssl/qsslsocket.cpp
|
||||
+++ b/src/network/ssl/qsslsocket.cpp
|
||||
@@ -1973,6 +1973,10 @@ QSslSocketPrivate::QSslSocketPrivate()
|
||||
, flushTriggered(false)
|
||||
{
|
||||
QSslConfigurationPrivate::deepCopyDefaultConfiguration(&configuration);
|
||||
+ // If the global configuration doesn't allow root certificates to be loaded
|
||||
+ // on demand then we have to disable it for this socket as well.
|
||||
+ if (!configuration.allowRootCertOnDemandLoading)
|
||||
+ allowRootCertOnDemandLoading = false;
|
||||
|
||||
const auto *tlsBackend = tlsBackendInUse();
|
||||
if (!tlsBackend) {
|
||||
@@ -2281,6 +2285,7 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri
|
||||
ptr->sessionProtocol = global->sessionProtocol;
|
||||
ptr->ciphers = global->ciphers;
|
||||
ptr->caCertificates = global->caCertificates;
|
||||
+ ptr->allowRootCertOnDemandLoading = global->allowRootCertOnDemandLoading;
|
||||
ptr->protocol = global->protocol;
|
||||
ptr->peerVerifyMode = global->peerVerifyMode;
|
||||
ptr->peerVerifyDepth = global->peerVerifyDepth;
|
||||
diff --git a/tests/manual/network/ssl/client-auth/tst_manual_ssl_client_auth.cpp b/tests/manual/network/ssl/client-auth/tst_manual_ssl_client_auth.cpp
|
||||
index 2307cbb191..4d4aaca7e3 100644
|
||||
--- a/tests/manual/network/ssl/client-auth/tst_manual_ssl_client_auth.cpp
|
||||
+++ b/tests/manual/network/ssl/client-auth/tst_manual_ssl_client_auth.cpp
|
||||
@@ -16,6 +16,9 @@
|
||||
// but the other side presents a certificate signed by a different CA.
|
||||
constexpr bool TestServerPresentsIncorrectCa = false;
|
||||
constexpr bool TestClientPresentsIncorrectCa = true;
|
||||
+// Decides whether or not to put the root CA into the global ssl configuration
|
||||
+// or into the socket's specific ssl configuration.
|
||||
+constexpr bool UseGlobalConfiguration = true;
|
||||
|
||||
class ServerThread : public QThread
|
||||
{
|
||||
@@ -26,8 +29,10 @@ public:
|
||||
QSslServer server;
|
||||
|
||||
QSslConfiguration config = server.sslConfiguration();
|
||||
- QList<QSslCertificate> certs = QSslCertificate::fromPath(QStringLiteral(":/rootCA.pem"));
|
||||
- config.setCaCertificates(certs);
|
||||
+ if (!UseGlobalConfiguration) {
|
||||
+ QList<QSslCertificate> certs = QSslCertificate::fromPath(QStringLiteral(":/rootCA.pem"));
|
||||
+ config.setCaCertificates(certs);
|
||||
+ }
|
||||
config.setLocalCertificate(QSslCertificate::fromPath(QStringLiteral(":/127.0.0.1.pem"))
|
||||
.first());
|
||||
QFile keyFile(QStringLiteral(":/127.0.0.1-key.pem"));
|
||||
@@ -73,6 +78,12 @@ int main(int argc, char **argv)
|
||||
if (!QFileInfo(u":/rootCA.pem"_s).exists())
|
||||
qFatal("rootCA.pem not found. Did you run generate.sh in the certs directory?");
|
||||
|
||||
+ if (UseGlobalConfiguration) {
|
||||
+ QSslConfiguration config = QSslConfiguration::defaultConfiguration();
|
||||
+ config.setCaCertificates(QSslCertificate::fromPath(u":/rootCA.pem"_s));
|
||||
+ QSslConfiguration::setDefaultConfiguration(config);
|
||||
+ }
|
||||
+
|
||||
ServerThread serverThread;
|
||||
serverThread.start();
|
||||
|
||||
@@ -88,12 +99,19 @@ int main(int argc, char **argv)
|
||||
keyFileName = u":/accepted-client-key.pem"_s;
|
||||
}
|
||||
config.setLocalCertificate(QSslCertificate::fromPath(certificatePath).first());
|
||||
- if (TestServerPresentsIncorrectCa) // true: Verify server using incorrect CA: should fail
|
||||
+ if (!UseGlobalConfiguration && TestServerPresentsIncorrectCa) {
|
||||
+ // Verify server using incorrect CA: should fail
|
||||
config.setCaCertificates(QSslCertificate::fromPath(u":/rootCA.pem"_s));
|
||||
+ } else if (UseGlobalConfiguration && !TestServerPresentsIncorrectCa) {
|
||||
+ // Verify server using correct CA, we need to explicitly set the
|
||||
+ // system CAs when the global config is overridden.
|
||||
+ config.setCaCertificates(QSslConfiguration::systemCaCertificates());
|
||||
+ }
|
||||
QFile keyFile(keyFileName);
|
||||
if (!keyFile.open(QIODevice::ReadOnly))
|
||||
qFatal("Failed to open key file");
|
||||
config.setPrivateKey(QSslKey(&keyFile, QSsl::Rsa));
|
||||
+
|
||||
socket.setSslConfiguration(config);
|
||||
|
||||
QObject::connect(&socket, &QSslSocket::encrypted, []() { qDebug() << "[c] encrypted"; });
|
||||
--
|
||||
2.40.1
|
||||
|
@ -1,99 +0,0 @@
|
||||
From 2a7da1b3c8c4096d7c2b09f3fcc58e9cf47867cd Mon Sep 17 00:00:00 2001
|
||||
From: Volker Hilsheimer <volker.hilsheimer@qt.io>
|
||||
Date: Mon, 5 Jun 2023 17:10:00 +0200
|
||||
Subject: QTabBar: recalculate scroll offset when showing
|
||||
|
||||
If an application sets the current index and resizes the tab widget
|
||||
before showing it, then the scroll offset might be calculated based on
|
||||
an old size. Since after ca15f650a1a914bb9a41131109c46c4e52c5ebb1,
|
||||
resizing explicitly avoids scrolling, this could result in tabs ending
|
||||
up scrolled outside of the tab bar when showing the tab widget.
|
||||
|
||||
Fix that by explicitly making the current tab visible in the tab bar's
|
||||
showEvent handler, which recalculates the scroll offset based on the
|
||||
actual size.
|
||||
|
||||
This is only reproducible with a tab widget, which lays out the tab bar
|
||||
for each change and resets the tab bar's layoutDirty flag. Add a test
|
||||
case there.
|
||||
|
||||
Pick-to: 6.5 6.6
|
||||
Fixes: QTBUG-114204
|
||||
Change-Id: I1e9506b9dde1dd892291d108dd2c7b675ef99509
|
||||
Reviewed-by: Axel Spoerl <axel.spoerl@qt.io>
|
||||
Reviewed-by: Jonas Kvinge <jonas@jkvinge.net>
|
||||
---
|
||||
src/widgets/widgets/qtabbar.cpp | 2 ++
|
||||
.../widgets/widgets/qtabwidget/tst_qtabwidget.cpp | 37 ++++++++++++++++++++++
|
||||
2 files changed, 39 insertions(+)
|
||||
|
||||
diff --git a/src/widgets/widgets/qtabbar.cpp b/src/widgets/widgets/qtabbar.cpp
|
||||
index 75cb6dd4ea..ea408ec7f6 100644
|
||||
--- a/src/widgets/widgets/qtabbar.cpp
|
||||
+++ b/src/widgets/widgets/qtabbar.cpp
|
||||
@@ -1656,6 +1656,8 @@ void QTabBar::showEvent(QShowEvent *)
|
||||
d->refresh();
|
||||
if (!d->validIndex(d->currentIndex))
|
||||
setCurrentIndex(0);
|
||||
+ else
|
||||
+ d->makeVisible(d->currentIndex);
|
||||
d->updateMacBorderMetrics();
|
||||
}
|
||||
|
||||
diff --git a/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp b/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp
|
||||
index 00cb26c2d3..eb29933fd1 100644
|
||||
--- a/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp
|
||||
+++ b/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp
|
||||
@@ -79,6 +79,9 @@ private slots:
|
||||
void moveCurrentTab();
|
||||
void autoHide();
|
||||
|
||||
+ void setCurrentBeforeShow_data();
|
||||
+ void setCurrentBeforeShow();
|
||||
+
|
||||
private:
|
||||
int addPage();
|
||||
void removePage(int index);
|
||||
@@ -750,5 +753,39 @@ void tst_QTabWidget::autoHide()
|
||||
QVERIFY(heightForWidth1 > tabWidget.heightForWidth(20));
|
||||
}
|
||||
|
||||
+void tst_QTabWidget::setCurrentBeforeShow_data()
|
||||
+{
|
||||
+ QTest::addColumn<QTabWidget::TabPosition>("tabPosition");
|
||||
+ QTest::newRow("West") << QTabWidget::West;
|
||||
+ QTest::newRow("North") << QTabWidget::North;
|
||||
+ QTest::newRow("East") << QTabWidget::East;
|
||||
+ QTest::newRow("South") << QTabWidget::South;
|
||||
+}
|
||||
+
|
||||
+void tst_QTabWidget::setCurrentBeforeShow()
|
||||
+{
|
||||
+ QFETCH(QTabWidget::TabPosition, tabPosition);
|
||||
+
|
||||
+ QTabWidget tabWidget;
|
||||
+ tabWidget.setTabPosition(tabPosition);
|
||||
+
|
||||
+ QPixmap pm(50, 50);
|
||||
+ pm.fill(Qt::red);
|
||||
+ const QIcon icon(pm);
|
||||
+ for (int i = 0; i < 4; ++i)
|
||||
+ tabWidget.addTab(new QWidget, icon, QString("Tab %1").arg(i));
|
||||
+
|
||||
+ // the tab widget has space for the entire tab bar
|
||||
+ tabWidget.resize(tabWidget.tabBar()->sizeHint() + QSize(50, 50));
|
||||
+ tabWidget.setCurrentIndex(2);
|
||||
+ tabWidget.show();
|
||||
+ QVERIFY(QTest::qWaitForWindowExposed(&tabWidget));
|
||||
+
|
||||
+ QCOMPARE_GE(tabWidget.tabBar()->tabRect(0).x(), 0);
|
||||
+ QCOMPARE_GE(tabWidget.tabBar()->tabRect(0).y(), 0);
|
||||
+
|
||||
+ QTest::qWait(2000);
|
||||
+}
|
||||
+
|
||||
QTEST_MAIN(tst_QTabWidget)
|
||||
#include "tst_qtabwidget.moc"
|
||||
--
|
||||
cgit v1.2.3
|
||||
|
388
CVE-2023-38197-qtbase-6.5.diff
Normal file
388
CVE-2023-38197-qtbase-6.5.diff
Normal file
@ -0,0 +1,388 @@
|
||||
From c216c3d9859a20b3aeec985512e89316423fc3a8 Mon Sep 17 00:00:00 2001
|
||||
From: Axel Spoerl <axel.spoerl@qt.io>
|
||||
Date: Fri, 30 Jun 2023 12:43:59 +0200
|
||||
Subject: [PATCH] QXmlStreamReader: Raise error on unexpected tokens
|
||||
|
||||
QXmlStreamReader accepted multiple DOCTYPE elements, containing DTD
|
||||
fragments in the XML prolog, and in the XML body.
|
||||
Well-formed but invalid XML files - with multiple DTD fragments in
|
||||
prolog and body, combined with recursive entity expansions - have
|
||||
caused infinite loops in QXmlStreamReader.
|
||||
|
||||
This patch implements a token check in QXmlStreamReader.
|
||||
A stream is allowed to start with an XML prolog. StartDocument
|
||||
and DOCTYPE elements are only allowed in this prolog, which
|
||||
may also contain ProcessingInstruction and Comment elements.
|
||||
As soon as anything else is seen, the prolog ends.
|
||||
After that, the prolog-specific elements are treated as unexpected.
|
||||
Furthermore, the prolog can contain at most one DOCTYPE element.
|
||||
|
||||
Update the documentation to reflect the new behavior.
|
||||
Add an autotest that checks the new error cases are correctly detected,
|
||||
and no error is raised for legitimate input.
|
||||
|
||||
The original OSS-Fuzz files (see bug reports) are not included in this
|
||||
patch for file size reasons. They have been tested manually. Each of
|
||||
them has more than one DOCTYPE element, causing infinite loops in
|
||||
recursive entity expansions. The newly implemented functionality
|
||||
detects those invalid DTD fragments. By raising an error, it aborts
|
||||
stream reading before an infinite loop occurs.
|
||||
|
||||
Thanks to OSS-Fuzz for finding this.
|
||||
|
||||
Fixes: QTBUG-92113
|
||||
Fixes: QTBUG-95188
|
||||
Change-Id: I0a082b9188b2eee50b396c4d5b1c9e1fd237bbdd
|
||||
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
|
||||
(cherry picked from commit c4301be7d5f94852e1b17f2c2989d5ca807855d4)
|
||||
---
|
||||
|
||||
diff --git a/src/corelib/serialization/qxmlstream.cpp b/src/corelib/serialization/qxmlstream.cpp
|
||||
index 6e34d4d..cf46d69 100644
|
||||
--- a/src/corelib/serialization/qxmlstream.cpp
|
||||
+++ b/src/corelib/serialization/qxmlstream.cpp
|
||||
@@ -185,7 +185,7 @@
|
||||
addData() or by waiting for it to arrive on the device().
|
||||
|
||||
\value UnexpectedElementError The parser encountered an element
|
||||
- that was different to those it expected.
|
||||
+ or token that was different to those it expected.
|
||||
|
||||
*/
|
||||
|
||||
@@ -322,13 +322,34 @@
|
||||
|
||||
QXmlStreamReader is a well-formed XML 1.0 parser that does \e not
|
||||
include external parsed entities. As long as no error occurs, the
|
||||
- application code can thus be assured that the data provided by the
|
||||
- stream reader satisfies the W3C's criteria for well-formed XML. For
|
||||
- example, you can be certain that all tags are indeed nested and
|
||||
- closed properly, that references to internal entities have been
|
||||
- replaced with the correct replacement text, and that attributes have
|
||||
- been normalized or added according to the internal subset of the
|
||||
- DTD.
|
||||
+ application code can thus be assured, that
|
||||
+ \list
|
||||
+ \li the data provided by the stream reader satisfies the W3C's
|
||||
+ criteria for well-formed XML,
|
||||
+ \li tokens are provided in a valid order.
|
||||
+ \endlist
|
||||
+
|
||||
+ Unless QXmlStreamReader raises an error, it guarantees the following:
|
||||
+ \list
|
||||
+ \li All tags are nested and closed properly.
|
||||
+ \li References to internal entities have been replaced with the
|
||||
+ correct replacement text.
|
||||
+ \li Attributes have been normalized or added according to the
|
||||
+ internal subset of the \l DTD.
|
||||
+ \li Tokens of type \l StartDocument happen before all others,
|
||||
+ aside from comments and processing instructions.
|
||||
+ \li At most one DOCTYPE element (a token of type \l DTD) is present.
|
||||
+ \li If present, the DOCTYPE appears before all other elements,
|
||||
+ aside from StartDocument, comments and processing instructions.
|
||||
+ \endlist
|
||||
+
|
||||
+ In particular, once any token of type \l StartElement, \l EndElement,
|
||||
+ \l Characters, \l EntityReference or \l EndDocument is seen, no
|
||||
+ tokens of type StartDocument or DTD will be seen. If one is present in
|
||||
+ the input stream, out of order, an error is raised.
|
||||
+
|
||||
+ \note The token types \l Comment and \l ProcessingInstruction may appear
|
||||
+ anywhere in the stream.
|
||||
|
||||
If an error occurs while parsing, atEnd() and hasError() return
|
||||
true, and error() returns the error that occurred. The functions
|
||||
@@ -659,6 +680,7 @@
|
||||
d->token = -1;
|
||||
return readNext();
|
||||
}
|
||||
+ d->checkToken();
|
||||
return d->type;
|
||||
}
|
||||
|
||||
@@ -743,6 +765,11 @@
|
||||
"ProcessingInstruction"
|
||||
);
|
||||
|
||||
+static constexpr auto QXmlStreamReader_XmlContextString = qOffsetStringArray(
|
||||
+ "Prolog",
|
||||
+ "Body"
|
||||
+);
|
||||
+
|
||||
/*!
|
||||
\property QXmlStreamReader::namespaceProcessing
|
||||
\brief the namespace-processing flag of the stream reader.
|
||||
@@ -777,6 +804,15 @@
|
||||
return QLatin1StringView(QXmlStreamReader_tokenTypeString.at(d->type));
|
||||
}
|
||||
|
||||
+/*!
|
||||
+ \internal
|
||||
+ \return \param loc (Prolog/Body) as a string.
|
||||
+ */
|
||||
+static constexpr QLatin1StringView contextString(QXmlStreamReaderPrivate::XmlContext ctxt)
|
||||
+{
|
||||
+ return QLatin1StringView(QXmlStreamReader_XmlContextString.at(static_cast<int>(ctxt)));
|
||||
+}
|
||||
+
|
||||
#endif // QT_NO_XMLSTREAMREADER
|
||||
|
||||
QXmlStreamPrivateTagStack::QXmlStreamPrivateTagStack()
|
||||
@@ -864,6 +900,8 @@
|
||||
|
||||
type = QXmlStreamReader::NoToken;
|
||||
error = QXmlStreamReader::NoError;
|
||||
+ currentContext = XmlContext::Prolog;
|
||||
+ foundDTD = false;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3838,6 +3876,97 @@
|
||||
}
|
||||
}
|
||||
|
||||
+static constexpr bool isTokenAllowedInContext(QXmlStreamReader::TokenType type,
|
||||
+ QXmlStreamReaderPrivate::XmlContext loc)
|
||||
+{
|
||||
+ switch (type) {
|
||||
+ case QXmlStreamReader::StartDocument:
|
||||
+ case QXmlStreamReader::DTD:
|
||||
+ return loc == QXmlStreamReaderPrivate::XmlContext::Prolog;
|
||||
+
|
||||
+ case QXmlStreamReader::StartElement:
|
||||
+ case QXmlStreamReader::EndElement:
|
||||
+ case QXmlStreamReader::Characters:
|
||||
+ case QXmlStreamReader::EntityReference:
|
||||
+ case QXmlStreamReader::EndDocument:
|
||||
+ return loc == QXmlStreamReaderPrivate::XmlContext::Body;
|
||||
+
|
||||
+ case QXmlStreamReader::Comment:
|
||||
+ case QXmlStreamReader::ProcessingInstruction:
|
||||
+ return true;
|
||||
+
|
||||
+ case QXmlStreamReader::NoToken:
|
||||
+ case QXmlStreamReader::Invalid:
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
|
||||
+#if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
|
||||
+ Q_UNREACHABLE_RETURN(false);
|
||||
+#else
|
||||
+ return false;
|
||||
+#endif
|
||||
+}
|
||||
+
|
||||
+/*!
|
||||
+ \internal
|
||||
+ \brief QXmlStreamReader::isValidToken
|
||||
+ \return \c true if \param type is a valid token type.
|
||||
+ \return \c false if \param type is an unexpected token,
|
||||
+ which indicates a non-well-formed or invalid XML stream.
|
||||
+ */
|
||||
+bool QXmlStreamReaderPrivate::isValidToken(QXmlStreamReader::TokenType type)
|
||||
+{
|
||||
+ // Don't change currentContext, if Invalid or NoToken occur in the prolog
|
||||
+ if (type == QXmlStreamReader::Invalid || type == QXmlStreamReader::NoToken)
|
||||
+ return false;
|
||||
+
|
||||
+ // If a token type gets rejected in the body, there is no recovery
|
||||
+ const bool result = isTokenAllowedInContext(type, currentContext);
|
||||
+ if (result || currentContext == XmlContext::Body)
|
||||
+ return result;
|
||||
+
|
||||
+ // First non-Prolog token observed => switch context to body and check again.
|
||||
+ currentContext = XmlContext::Body;
|
||||
+ return isTokenAllowedInContext(type, currentContext);
|
||||
+}
|
||||
+
|
||||
+/*!
|
||||
+ \internal
|
||||
+ Checks token type and raises an error, if it is invalid
|
||||
+ in the current context (prolog/body).
|
||||
+ */
|
||||
+void QXmlStreamReaderPrivate::checkToken()
|
||||
+{
|
||||
+ Q_Q(QXmlStreamReader);
|
||||
+
|
||||
+ // The token type must be consumed, to keep track if the body has been reached.
|
||||
+ const XmlContext context = currentContext;
|
||||
+ const bool ok = isValidToken(type);
|
||||
+
|
||||
+ // Do nothing if an error has been raised already (going along with an unexpected token)
|
||||
+ if (error != QXmlStreamReader::Error::NoError)
|
||||
+ return;
|
||||
+
|
||||
+ if (!ok) {
|
||||
+ raiseError(QXmlStreamReader::UnexpectedElementError,
|
||||
+ QObject::tr("Unexpected token type %1 in %2.")
|
||||
+ .arg(q->tokenString(), contextString(context)));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (type != QXmlStreamReader::DTD)
|
||||
+ return;
|
||||
+
|
||||
+ // Raise error on multiple DTD tokens
|
||||
+ if (foundDTD) {
|
||||
+ raiseError(QXmlStreamReader::UnexpectedElementError,
|
||||
+ QObject::tr("Found second DTD token in %1.").arg(contextString(context)));
|
||||
+ } else {
|
||||
+ foundDTD = true;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/*!
|
||||
\fn bool QXmlStreamAttributes::hasAttribute(QAnyStringView qualifiedName) const
|
||||
|
||||
diff --git a/src/corelib/serialization/qxmlstream_p.h b/src/corelib/serialization/qxmlstream_p.h
|
||||
index 070424a..f09adaa 100644
|
||||
--- a/src/corelib/serialization/qxmlstream_p.h
|
||||
+++ b/src/corelib/serialization/qxmlstream_p.h
|
||||
@@ -297,6 +297,17 @@
|
||||
QStringDecoder decoder;
|
||||
bool atEnd;
|
||||
|
||||
+ enum class XmlContext
|
||||
+ {
|
||||
+ Prolog,
|
||||
+ Body,
|
||||
+ };
|
||||
+
|
||||
+ XmlContext currentContext = XmlContext::Prolog;
|
||||
+ bool foundDTD = false;
|
||||
+ bool isValidToken(QXmlStreamReader::TokenType type);
|
||||
+ void checkToken();
|
||||
+
|
||||
/*!
|
||||
\sa setType()
|
||||
*/
|
||||
diff --git a/tests/auto/corelib/serialization/qxmlstream/tokenError/dtdInBody.xml b/tests/auto/corelib/serialization/qxmlstream/tokenError/dtdInBody.xml
|
||||
new file mode 100644
|
||||
index 0000000..1c3ca4e
|
||||
--- /dev/null
|
||||
+++ b/tests/auto/corelib/serialization/qxmlstream/tokenError/dtdInBody.xml
|
||||
@@ -0,0 +1,20 @@
|
||||
+<!DOCTYPE TEST [
|
||||
+ <!ELEMENT TESTATTRIBUTE (CASE+)>
|
||||
+ <!ELEMENT CASE (CLASS, FUNCTION)>
|
||||
+ <!ELEMENT CLASS (#PCDATA)>
|
||||
+
|
||||
+ <!-- adding random ENTITY statement, as this is typical DTD content -->
|
||||
+ <!ENTITY unite "∪">
|
||||
+
|
||||
+ <!ATTLIST CASE CLASS CDATA #REQUIRED>
|
||||
+]>
|
||||
+<TEST>
|
||||
+ <CASE>
|
||||
+ <CLASS>tst_QXmlStream</CLASS>
|
||||
+ </CASE>
|
||||
+ <!-- invalid DTD in XML body follows -->
|
||||
+ <!DOCTYPE DTDTEST [
|
||||
+ <!ELEMENT RESULT (CASE+)>
|
||||
+ <!ATTLIST RESULT OUTPUT CDATA #REQUIRED>
|
||||
+ ]>
|
||||
+</TEST>
|
||||
diff --git a/tests/auto/corelib/serialization/qxmlstream/tokenError/multipleDtd.xml b/tests/auto/corelib/serialization/qxmlstream/tokenError/multipleDtd.xml
|
||||
new file mode 100644
|
||||
index 0000000..cd398c0
|
||||
--- /dev/null
|
||||
+++ b/tests/auto/corelib/serialization/qxmlstream/tokenError/multipleDtd.xml
|
||||
@@ -0,0 +1,20 @@
|
||||
+<!DOCTYPE TEST [
|
||||
+ <!ELEMENT TESTATTRIBUTE (CASE+)>
|
||||
+ <!ELEMENT CASE (CLASS, FUNCTION, DATASET, COMMENTS)>
|
||||
+ <!ELEMENT CLASS (#PCDATA)>
|
||||
+
|
||||
+ <!-- adding random ENTITY statements, as this is typical DTD content -->
|
||||
+ <!ENTITY iff "⇔">
|
||||
+
|
||||
+ <!ATTLIST CASE CLASS CDATA #REQUIRED>
|
||||
+]>
|
||||
+<!-- invalid second DTD follows -->
|
||||
+<!DOCTYPE SECOND [
|
||||
+ <!ELEMENT SECONDATTRIBUTE (#PCDATA)>
|
||||
+ <!ENTITY on "∘">
|
||||
+]>
|
||||
+<TEST>
|
||||
+ <CASE>
|
||||
+ <CLASS>tst_QXmlStream</CLASS>
|
||||
+ </CASE>
|
||||
+</TEST>
|
||||
diff --git a/tests/auto/corelib/serialization/qxmlstream/tokenError/wellFormed.xml b/tests/auto/corelib/serialization/qxmlstream/tokenError/wellFormed.xml
|
||||
new file mode 100644
|
||||
index 0000000..1b61a3f
|
||||
--- /dev/null
|
||||
+++ b/tests/auto/corelib/serialization/qxmlstream/tokenError/wellFormed.xml
|
||||
@@ -0,0 +1,15 @@
|
||||
+<!DOCTYPE TEST [
|
||||
+ <!ELEMENT TESTATTRIBUTE (CASE+)>
|
||||
+ <!ELEMENT CASE (CLASS, FUNCTION, DATASET, COMMENTS)>
|
||||
+ <!ELEMENT CLASS (#PCDATA)>
|
||||
+
|
||||
+ <!-- adding random ENTITY statements, as this is typical DTD content -->
|
||||
+ <!ENTITY unite "∪">
|
||||
+
|
||||
+ <!ATTLIST CASE CLASS CDATA #REQUIRED>
|
||||
+]>
|
||||
+<TEST>
|
||||
+ <CASE>
|
||||
+ <CLASS>tst_QXmlStream</CLASS>
|
||||
+ </CASE>
|
||||
+</TEST>
|
||||
diff --git a/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp b/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp
|
||||
index 2a340e1..30f5499 100644
|
||||
--- a/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp
|
||||
+++ b/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp
|
||||
@@ -590,6 +590,9 @@
|
||||
|
||||
void entityExpansionLimit() const;
|
||||
|
||||
+ void tokenErrorHandling_data() const;
|
||||
+ void tokenErrorHandling() const;
|
||||
+
|
||||
private:
|
||||
static QByteArray readFile(const QString &filename);
|
||||
|
||||
@@ -1855,5 +1858,41 @@
|
||||
QCOMPARE(reader.error(), errorType);
|
||||
}
|
||||
|
||||
+void tst_QXmlStream::tokenErrorHandling_data() const
|
||||
+{
|
||||
+ QTest::addColumn<QString>("fileName");
|
||||
+ QTest::addColumn<QXmlStreamReader::Error>("expectedError");
|
||||
+ QTest::addColumn<QString>("errorKeyWord");
|
||||
+
|
||||
+ constexpr auto invalid = QXmlStreamReader::Error::UnexpectedElementError;
|
||||
+ constexpr auto valid = QXmlStreamReader::Error::NoError;
|
||||
+ QTest::newRow("DtdInBody") << "dtdInBody.xml" << invalid << "DTD";
|
||||
+ QTest::newRow("multipleDTD") << "multipleDtd.xml" << invalid << "second DTD";
|
||||
+ QTest::newRow("wellFormed") << "wellFormed.xml" << valid << "";
|
||||
+}
|
||||
+
|
||||
+void tst_QXmlStream::tokenErrorHandling() const
|
||||
+{
|
||||
+ QFETCH(const QString, fileName);
|
||||
+ QFETCH(const QXmlStreamReader::Error, expectedError);
|
||||
+ QFETCH(const QString, errorKeyWord);
|
||||
+
|
||||
+ const QDir dir(QFINDTESTDATA("tokenError"));
|
||||
+ QFile file(dir.absoluteFilePath(fileName));
|
||||
+
|
||||
+ // Cross-compiling: File will be on host only
|
||||
+ if (!file.exists())
|
||||
+ QSKIP("Testfile not found.");
|
||||
+
|
||||
+ file.open(QIODevice::ReadOnly);
|
||||
+ QXmlStreamReader reader(&file);
|
||||
+ while (!reader.atEnd())
|
||||
+ reader.readNext();
|
||||
+
|
||||
+ QCOMPARE(reader.error(), expectedError);
|
||||
+ if (expectedError != QXmlStreamReader::Error::NoError)
|
||||
+ QVERIFY(reader.errorString().contains(errorKeyWord));
|
||||
+}
|
||||
+
|
||||
#include "tst_qxmlstream.moc"
|
||||
// vim: et:ts=4:sw=4:sts=4
|
@ -1,3 +1,17 @@
|
||||
-------------------------------------------------------------------
|
||||
Thu Jul 20 08:34:44 UTC 2023 - Christophe Marin <christophe@krop.fr>
|
||||
|
||||
- Use a mirror to download sources for all Qt packages. Upstream
|
||||
servers are very slow since a couple weeks.
|
||||
- Update to 6.5.2
|
||||
* https://www.qt.io/blog/qt-6.5.2-released-1
|
||||
- Drop patches, merged upstream:
|
||||
* 0001-Schannel-Reject-certificate-not-signed-by-a-configur.patch
|
||||
* 0001-Ssl-Copy-the-on-demand-cert-loading-bool-from-defaul.patch
|
||||
* 0001-tabbar-fix.patch
|
||||
- Add patch:
|
||||
* CVE-2023-38197-qtbase-6.5.diff (boo#1213326, CVE-2023-38197)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Fri Jun 30 15:13:07 UTC 2023 - Christophe Marin <christophe@krop.fr>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
#
|
||||
|
||||
|
||||
%define real_version 6.5.1
|
||||
%define real_version 6.5.2
|
||||
%define short_version 6.5
|
||||
%define tar_name qtbase-everywhere-src
|
||||
%define tar_suffix %{nil}
|
||||
@ -30,18 +30,16 @@
|
||||
%global with_gles 1
|
||||
%endif
|
||||
Name: qt6-base%{?pkg_suffix}
|
||||
Version: 6.5.1
|
||||
Version: 6.5.2
|
||||
Release: 0
|
||||
Summary: Qt 6 core components (Core, Gui, Widgets, Network...)
|
||||
# Legal: qtpaths is BSD-3-Clause
|
||||
License: LGPL-2.1-with-Qt-Company-Qt-exception-1.1 OR LGPL-3.0-only
|
||||
URL: https://www.qt.io
|
||||
Source: https://download.qt.io/official_releases/qt/%{short_version}/%{real_version}%{tar_suffix}/submodules/%{tar_name}-%{real_version}%{tar_suffix}.tar.xz
|
||||
Source: https://www.nic.funet.fi/pub/mirrors/download.qt-project.org/official_releases/qt/%{short_version}/%{real_version}%{tar_suffix}/submodules/%{tar_name}-%{real_version}%{tar_suffix}.tar.xz
|
||||
Source99: qt6-base-rpmlintrc
|
||||
# Patches 0-100 are upstream patches #
|
||||
Patch0: 0001-Schannel-Reject-certificate-not-signed-by-a-configur.patch
|
||||
Patch1: 0001-Ssl-Copy-the-on-demand-cert-loading-bool-from-defaul.patch
|
||||
Patch2: 0001-tabbar-fix.patch
|
||||
Patch0: CVE-2023-38197-qtbase-6.5.diff
|
||||
# Patches 100-200 are openSUSE and/or non-upstream(able) patches #
|
||||
Patch100: 0001-Tell-the-truth-about-private-API.patch
|
||||
# No need to pollute the library dir with object files, install them in the qt6 subfolder
|
||||
|
@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:db56fa1f4303a1189fe33418d25d1924931c7aef237f89eea9de58e858eebfed
|
||||
size 48287392
|
BIN
qtbase-everywhere-src-6.5.2.tar.xz
(Stored with Git LFS)
Normal file
BIN
qtbase-everywhere-src-6.5.2.tar.xz
(Stored with Git LFS)
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user