diff --git a/CVE-2025-6338-qtbase-6.5.patch b/CVE-2025-6338-qtbase-6.5.patch new file mode 100644 index 0000000000000000000000000000000000000000..0d48c15832e125742dc94891c1a8f36db15962d5 --- /dev/null +++ b/CVE-2025-6338-qtbase-6.5.patch @@ -0,0 +1,271 @@ +diff --git a/src/plugins/tls/schannel/qtls_schannel.cpp b/src/plugins/tls/schannel/qtls_schannel.cpp +index 807bb3926cf0..155f30b0350e 100644 +--- a/src/plugins/tls/schannel/qtls_schannel.cpp ++++ b/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 +@@ -130,8 +131,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 QTlsPrivate { + +@@ -700,7 +702,6 @@ TlsCryptographSchannel::~TlsCryptographSchannel() + closeCertificateStores(); + deallocateContext(); + freeCredentialsHandle(); +- CertFreeCertificateContext(localCertContext); + } + + void TlsCryptographSchannel::init(QSslSocket *qObj, QSslSocketPrivate *dObj) +@@ -795,12 +796,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(); +@@ -811,36 +806,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); + } + void *credentials = nullptr; + #ifdef SUPPORTS_TLS13 +@@ -857,7 +827,7 @@ bool TlsCryptographSchannel::acquireCredentialsHandle() + SCH_CREDENTIALS_VERSION, + 0, + certsCount, +- &localCertContext, ++ &localCertificate, + nullptr, + 0, + nullptr, +@@ -874,7 +844,7 @@ bool TlsCryptographSchannel::acquireCredentialsHandle() + SCHANNEL_CRED *cred = new SCHANNEL_CRED{ + SCHANNEL_CRED_VERSION, // dwVersion + certsCount, // cCreds +- &localCertContext, // paCred (certificate(s) containing a private key for authentication) ++ &localCertificate, // paCred (certificate(s) containing a private key for authentication) + nullptr, // hRootStore + + 0, // cMappers (reserved) +@@ -1445,9 +1415,6 @@ void TlsCryptographSchannel::reset() + connectionInfo = {}; + streamSizes = {}; + +- CertFreeCertificateContext(localCertContext); +- localCertContext = nullptr; +- + contextAttributes = 0; + intermediateBuffer.clear(); + schannelState = SchannelState::InitializeHandshake; +@@ -1941,6 +1908,69 @@ 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(); ++ NCryptBuffer nbuffer{ ULONG(certName.size() * sizeof(char16_t) + sizeof(char16_t)), ++ NCRYPTBUFFER_PKCS_KEY_NAME, ++ const_reinterpret_cast(certName.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 +@@ -1957,7 +1987,10 @@ void TlsCryptographSchannel::initializeCertificateStores() + 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()) { +@@ -1967,10 +2000,34 @@ void TlsCryptographSchannel::initializeCertificateStores() + 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!"); ++ } + } + } + +diff --git a/src/plugins/tls/schannel/qtls_schannel_p.h b/src/plugins/tls/schannel/qtls_schannel_p.h +index 4a8d77134f62..116f5b9ca412 100644 +--- a/src/plugins/tls/schannel/qtls_schannel_p.h ++++ b/src/plugins/tls/schannel/qtls_schannel_p.h +@@ -105,8 +105,6 @@ private: + QHCertStorePointer peerCertificateStore = nullptr; + QHCertStorePointer caCertificateStore = nullptr; + +- const CERT_CONTEXT *localCertContext = nullptr; +- + ULONG contextAttributes = 0; + qint64 missingData = 0; + +diff --git a/src/plugins/tls/schannel/qx509_schannel_p.h b/src/plugins/tls/schannel/qx509_schannel_p.h +index 4b225a6db86a..9e870ae15b04 100644 +--- a/src/plugins/tls/schannel/qx509_schannel_p.h ++++ b/src/plugins/tls/schannel/qx509_schannel_p.h +@@ -36,7 +36,7 @@ public: + Qt::HANDLE handle() const override; + + static QSslCertificate QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext); +-private: ++ + const CERT_CONTEXT *certificateContext = nullptr; + + Q_DISABLE_COPY_MOVE(X509CertificateSchannel); +diff --git a/src/plugins/tls/shared/qsslsocket_qt.cpp b/src/plugins/tls/shared/qsslsocket_qt.cpp +index 585511a88382..1f64b6255153 100644 +--- a/src/plugins/tls/shared/qsslsocket_qt.cpp ++++ b/src/plugins/tls/shared/qsslsocket_qt.cpp +@@ -134,7 +134,7 @@ static QByteArray _q_PKCS12_certBag(const QSslCertificate &cert) + 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); + diff --git a/qt6-qtbase.spec b/qt6-qtbase.spec index f41237cfacbcf764f117e8f27e132399c784007b..bf003199ec27a3458fc8bd7a58eff1d36fe7e70b 100644 --- a/qt6-qtbase.spec +++ b/qt6-qtbase.spec @@ -27,7 +27,7 @@ BuildRequires: pkgconfig(libsystemd) Name: qt6-qtbase Summary: Qt6 - QtBase components Version: 6.5.2 -Release: 11 +Release: 12 License: LGPL-3.0-only OR GPL-3.0-only WITH Qt-GPL-exception-1.0 Url: http://qt-project.org/ @@ -93,6 +93,7 @@ Patch6007:qtbase6.5.2-CVE-2023-45935.patch Patch6008:qtbase6.5.2-CVE-2024-25580.patch Patch6009:qtbase6.5.2-CVE-2025-30348.patch Patch6010:CVE-2025-5455-qtbase-6.5.patch +Patch6011:CVE-2025-6338-qtbase-6.5.patch # Do not check any files in %%{_qt6_plugindir}/platformthemes/ for requires. # Those themes are there for platform integration. If the required libraries are # not there, the platform to integrate with isn't either. Then Qt will just @@ -809,6 +810,9 @@ make check -k ||: %changelog +* Fri Jun 27 2025 Funda Wang - 6.5.2-12 +- fix CVE-2025-6338 + * Sat Jun 07 2025 Funda Wang - 6.5.2-11 - fix CVE-2025-5455