From: Rafael Sadowski Subject: CVE-2025-5455 and CVE-2025-6338: qt6-qtbase-6.8.3 To: ports@openbsd.org Cc: tb@openbsd.org Date: Sun, 3 Aug 2025 10:00:30 +0200 Simple diff to fix CVE-2025-5455 and CVE-2025-6338 in qt6-qtbase-6.8.3. Tested on amd64. OK? Cheers Rafael Index: Makefile =================================================================== RCS file: /cvs/ports/x11/qt6/qtbase/Makefile,v diff -u -p -u -p -r1.43 Makefile --- Makefile 6 Jun 2025 05:12:03 -0000 1.43 +++ Makefile 3 Aug 2025 07:57:55 -0000 @@ -4,7 +4,7 @@ COMMENT-main = C++ general-purpose tool COMMENT-global = global Qt6 documentation internals COMMENT-mysql = MySQL plugin for Qt6 COMMENT-psql = PostgresSQL plugin for Qt6 -REVISION-main = 1 +REVISION-main = 2 PKGSPEC-main = qt6-qtbase-${QT6_PKGSPEC} Index: patches/patch-src_corelib_io_qdataurl_cpp =================================================================== RCS file: patches/patch-src_corelib_io_qdataurl_cpp diff -N patches/patch-src_corelib_io_qdataurl_cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-src_corelib_io_qdataurl_cpp 3 Aug 2025 07:57:55 -0000 @@ -0,0 +1,19 @@ +https://download.qt.io/official_releases/qt/6.8/CVE-2025-5455-qtbase-6.8.patch +Index: src/corelib/io/qdataurl.cpp +--- src/corelib/io/qdataurl.cpp.orig ++++ src/corelib/io/qdataurl.cpp +@@ -47,10 +47,10 @@ Q_CORE_EXPORT bool qDecodeDataUrl(const QUrl &uri, QSt + QLatin1StringView textPlain; + constexpr auto charset = "charset"_L1; + if (QLatin1StringView{data}.startsWith(charset, Qt::CaseInsensitive)) { +- qsizetype i = charset.size(); +- while (data.at(i) == ' ') +- ++i; +- if (data.at(i) == '=') ++ QByteArrayView copy = data.sliced(charset.size()); ++ while (copy.startsWith(' ')) ++ copy.slice(1); ++ if (copy.startsWith('=')) + textPlain = "text/plain;"_L1; + } + Index: patches/patch-src_plugins_tls_schannel_qtls_schannel_cpp =================================================================== RCS file: patches/patch-src_plugins_tls_schannel_qtls_schannel_cpp diff -N patches/patch-src_plugins_tls_schannel_qtls_schannel_cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-src_plugins_tls_schannel_qtls_schannel_cpp 3 Aug 2025 07:57:55 -0000 @@ -0,0 +1,225 @@ +https://ftp.fau.de/qtproject/archive/qt/6.8/CVE-2025-6338-qtbase-6.8.patch + +Index: src/plugins/tls/schannel/qtls_schannel.cpp +--- src/plugins/tls/schannel/qtls_schannel.cpp.orig ++++ src/plugins/tls/schannel/qtls_schannel.cpp +@@ -7,6 +7,7 @@ + #include "qtlskey_schannel_p.h" + #include "qx509_schannel_p.h" + #include "qtls_schannel_p.h" ++#include "../shared/qasn1element_p.h" + + #include + #include +@@ -126,8 +127,9 @@ using namespace Qt::StringLiterals; + Q_LOGGING_CATEGORY(lcTlsBackendSchannel, "qt.tlsbackend.schannel"); + + // Defined in qsslsocket_qt.cpp. +-QByteArray _q_makePkcs12(const QList &certs, const QSslKey &key, ++extern QByteArray _q_makePkcs12(const QList &certs, const QSslKey &key, + const QString &passPhrase); ++extern QAsn1Element _q_PKCS12_key(const QSslKey &key); + + namespace { + bool supportsTls13(); +@@ -1003,7 +1005,6 @@ TlsCryptographSchannel::~TlsCryptographSchannel() + closeCertificateStores(); + deallocateContext(); + freeCredentialsHandle(); +- CertFreeCertificateContext(localCertContext); + } + + void TlsCryptographSchannel::init(QSslSocket *qObj, QSslSocketPrivate *dObj) +@@ -1098,12 +1099,6 @@ bool TlsCryptographSchannel::acquireCredentialsHandle( + return false; + } + +- const CERT_CHAIN_CONTEXT *chainContext = nullptr; +- auto freeCertChain = qScopeGuard([&chainContext]() { +- if (chainContext) +- CertFreeCertificateChain(chainContext); +- }); +- + DWORD certsCount = 0; + // Set up our certificate stores before trying to use one... + initializeCertificateStores(); +@@ -1114,36 +1109,11 @@ bool TlsCryptographSchannel::acquireCredentialsHandle( + if (!configuration.localCertificateChain().isEmpty() && !localCertificateStore) + return true; // 'true' because "tst_QSslSocket::setEmptyKey" expects us to not disconnect + ++ PCCERT_CONTEXT localCertificate = nullptr; + if (localCertificateStore != nullptr) { +- CERT_CHAIN_FIND_BY_ISSUER_PARA findParam; +- ZeroMemory(&findParam, sizeof(findParam)); +- findParam.cbSize = sizeof(findParam); +- findParam.pszUsageIdentifier = isClient ? szOID_PKIX_KP_CLIENT_AUTH : szOID_PKIX_KP_SERVER_AUTH; +- +- // There should only be one chain in our store, so.. we grab that one. +- chainContext = CertFindChainInStore(localCertificateStore.get(), +- X509_ASN_ENCODING, +- 0, +- CERT_CHAIN_FIND_BY_ISSUER, +- &findParam, +- nullptr); +- if (!chainContext) { +- const QString message = isClient +- ? QSslSocket::tr("The certificate provided cannot be used for a client.") +- : QSslSocket::tr("The certificate provided cannot be used for a server."); +- setErrorAndEmit(d, QAbstractSocket::SocketError::SslInvalidUserDataError, message); +- return false; +- } +- Q_ASSERT(chainContext->cChain == 1); +- Q_ASSERT(chainContext->rgpChain[0]); +- Q_ASSERT(chainContext->rgpChain[0]->cbSize >= 1); +- Q_ASSERT(chainContext->rgpChain[0]->rgpElement[0]); +- Q_ASSERT(!localCertContext); +- localCertContext = CertDuplicateCertificateContext(chainContext->rgpChain[0] +- ->rgpElement[0] +- ->pCertContext); + certsCount = 1; +- Q_ASSERT(localCertContext); ++ localCertificate = static_cast(configuration.localCertificate().handle()); ++ Q_ASSERT(localCertificate); + } + + const QList ciphers = configuration.ciphers(); +@@ -1167,7 +1137,7 @@ bool TlsCryptographSchannel::acquireCredentialsHandle( + SCH_CREDENTIALS_VERSION, + 0, + certsCount, +- &localCertContext, ++ &localCertificate, + nullptr, + 0, + nullptr, +@@ -1724,9 +1694,6 @@ void TlsCryptographSchannel::reset() + connectionInfo = {}; + streamSizes = {}; + +- CertFreeCertificateContext(localCertContext); +- localCertContext = nullptr; +- + contextAttributes = 0; + intermediateBuffer.clear(); + schannelState = SchannelState::InitializeHandshake; +@@ -2236,6 +2203,70 @@ bool TlsCryptographSchannel::checkSslErrors() + return true; + } + ++static void attachPrivateKeyToCertificate(const QSslCertificate &certificate, ++ const QSslKey &privateKey) ++{ ++ QAsn1Element elem = _q_PKCS12_key(privateKey); ++ QByteArray buffer; ++ QDataStream stream(&buffer, QDataStream::WriteOnly); ++ elem.write(stream); ++ NCRYPT_PROV_HANDLE provider = 0; ++ SECURITY_STATUS status = NCryptOpenStorageProvider(&provider, MS_KEY_STORAGE_PROVIDER, 0); ++ if (status != SEC_E_OK) { ++ qCWarning(lcTlsBackendSchannel()) ++ << "Failed to open ncrypt storage provider:" << schannelErrorToString(status); ++ return; ++ } ++ const auto freeProvider = qScopeGuard([provider]() { NCryptFreeObject(provider); }); ++ ++ const QString certName = certificate.subjectInfo(QSslCertificate::CommonName).front(); ++ QSpan nameSpan(certName); ++ NCryptBuffer nbuffer{ ULONG(nameSpan.size_bytes() + sizeof(char16_t)), ++ NCRYPTBUFFER_PKCS_KEY_NAME, ++ const_reinterpret_cast(nameSpan.data()) }; ++ NCryptBufferDesc bufferDesc{ NCRYPTBUFFER_VERSION, 1, &nbuffer }; ++ NCRYPT_KEY_HANDLE ncryptKey = 0; ++ status = NCryptImportKey(provider, 0, NCRYPT_PKCS8_PRIVATE_KEY_BLOB, &bufferDesc, &ncryptKey, ++ PBYTE(buffer.data()), buffer.size(), 0); ++ if (status != SEC_E_OK) { ++ qCWarning(lcTlsBackendSchannel()) ++ << "Failed to import private key:" << schannelErrorToString(status); ++ return; ++ } ++ const auto freeKey = qScopeGuard([ncryptKey]() { NCryptFreeObject(ncryptKey); }); ++ ++ CERT_CONTEXT *context = PCERT_CONTEXT(certificate.handle()); ++ Q_ASSERT(context); ++ ++ CRYPT_DATA_BLOB keyBlob = { sizeof(ncryptKey), PBYTE(&ncryptKey) }; ++ BOOL ok = ++ CertSetCertificateContextProperty(context, CERT_NCRYPT_KEY_HANDLE_PROP_ID, 0, &keyBlob); ++ if (!ok) { ++ auto error = GetLastError(); ++ if (lcTlsBackendSchannel().isWarningEnabled()) ++ qErrnoWarning(int(error), "Failed to set ncrypt handle property."); ++ return; ++ } ++ ++ CRYPT_KEY_PROV_INFO provInfo{ ++ const_reinterpret_cast(certName.constData()), ++ const_cast(MS_KEY_STORAGE_PROVIDER), ++ 0, ++ CERT_SET_KEY_PROV_HANDLE_PROP_ID | CERT_SET_KEY_CONTEXT_PROP_ID, ++ 0, ++ nullptr, ++ 0, ++ }; ++ ok = CertSetCertificateContextProperty(context, CERT_KEY_PROV_INFO_PROP_ID, ++ CERT_SET_PROPERTY_INHIBIT_PERSIST_FLAG, &provInfo); ++ if (!ok) { ++ auto error = GetLastError(); ++ if (lcTlsBackendSchannel().isWarningEnabled()) ++ qErrnoWarning(int(error), "Failed to set key provider info property."); ++ return; ++ } ++} ++ + void TlsCryptographSchannel::initializeCertificateStores() + { + //// helper function which turns a chain into a certificate store +@@ -2252,7 +2283,10 @@ void TlsCryptographSchannel::initializeCertificateStor + CRYPT_DATA_BLOB pfxBlob; + pfxBlob.cbData = DWORD(pkcs12.length()); + pfxBlob.pbData = reinterpret_cast(pkcs12.data()); +- return QHCertStorePointer(PFXImportCertStore(&pfxBlob, passphrase, 0)); ++ // ALWAYS_CNG to import using "Cryptography API: Next Generation (CNG)" ++ // NO_PERSIST_KEY to request not persisting anything imported to disk ++ constexpr DWORD flags = PKCS12_ALWAYS_CNG_KSP | PKCS12_NO_PERSIST_KEY; ++ return QHCertStorePointer(PFXImportCertStore(&pfxBlob, passphrase, flags)); + }; + + if (!configuration.localCertificateChain().isEmpty()) { +@@ -2262,10 +2296,34 @@ void TlsCryptographSchannel::initializeCertificateStor + return; + } + if (localCertificateStore == nullptr) { +- localCertificateStore = createStoreFromCertificateChain(configuration.localCertificateChain(), +- configuration.privateKey()); +- if (localCertificateStore == nullptr) ++ localCertificateStore = ++ createStoreFromCertificateChain(configuration.localCertificateChain(), {}); ++ if (localCertificateStore) { ++ const CERT_CONTEXT *certificateContext = CertFindCertificateInStore( ++ localCertificateStore.get(), X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, ++ CERT_FIND_ANY, nullptr, nullptr); ++ if (certificateContext) { ++ auto *backend = QTlsBackend::backend( ++ configuration.localCertificate()); ++ backend->certificateContext.reset( ++ CertDuplicateCertificateContext(certificateContext)); ++ ++ DWORD keySpec = 0; ++ BOOL mustFree = FALSE; ++ NCRYPT_KEY_HANDLE testKey = 0; ++ BOOL ok = CryptAcquireCertificatePrivateKey( ++ certificateContext, CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG, nullptr, ++ &testKey, &keySpec, &mustFree); ++ if (mustFree) ++ NCryptFreeObject(testKey); ++ if (!ok) { ++ attachPrivateKeyToCertificate(configuration.localCertificate(), ++ configuration.privateKey()); ++ } ++ } ++ } else { + qCWarning(lcTlsBackendSchannel, "Failed to load certificate chain!"); ++ } + } + } + Index: patches/patch-src_plugins_tls_schannel_qtls_schannel_p_h =================================================================== RCS file: patches/patch-src_plugins_tls_schannel_qtls_schannel_p_h diff -N patches/patch-src_plugins_tls_schannel_qtls_schannel_p_h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-src_plugins_tls_schannel_qtls_schannel_p_h 3 Aug 2025 07:57:55 -0000 @@ -0,0 +1,14 @@ +https://ftp.fau.de/qtproject/archive/qt/6.8/CVE-2025-6338-qtbase-6.8.patch + +Index: src/plugins/tls/schannel/qtls_schannel_p.h +--- src/plugins/tls/schannel/qtls_schannel_p.h.orig ++++ src/plugins/tls/schannel/qtls_schannel_p.h +@@ -106,8 +106,6 @@ class TlsCryptographSchannel final : public TlsCryptog + QHCertStorePointer peerCertificateStore = nullptr; + QHCertStorePointer caCertificateStore = nullptr; + +- const CERT_CONTEXT *localCertContext = nullptr; +- + ULONG contextAttributes = 0; + qint64 missingData = 0; + Index: patches/patch-src_plugins_tls_shared_qsslsocket_qt_cpp =================================================================== RCS file: patches/patch-src_plugins_tls_shared_qsslsocket_qt_cpp diff -N patches/patch-src_plugins_tls_shared_qsslsocket_qt_cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-src_plugins_tls_shared_qsslsocket_qt_cpp 3 Aug 2025 07:57:55 -0000 @@ -0,0 +1,14 @@ +https://ftp.fau.de/qtproject/archive/qt/6.8/CVE-2025-6338-qtbase-6.8.patch + +Index: src/plugins/tls/shared/qsslsocket_qt.cpp +--- src/plugins/tls/shared/qsslsocket_qt.cpp.orig ++++ src/plugins/tls/shared/qsslsocket_qt.cpp +@@ -134,7 +134,7 @@ static QByteArray _q_PKCS12_certBag(const QSslCertific + return ba; + } + +-static QAsn1Element _q_PKCS12_key(const QSslKey &key) ++QAsn1Element _q_PKCS12_key(const QSslKey &key) + { + Q_ASSERT(key.algorithm() == QSsl::Rsa || key.algorithm() == QSsl::Dsa); +