From 51a3c8d7b8140f0bf6912d14a58bcd0092b868a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Wed, 10 May 2023 16:43:41 +0200 Subject: [PATCH 1/2] 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 Reviewed-by: Volker Hilsheimer (cherry picked from commit ada2c573c1a25f8d96577734968fe317ddfa292a) --- src/network/ssl/qsslsocket_schannel.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/network/ssl/qsslsocket_schannel.cpp b/src/network/ssl/qsslsocket_schannel.cpp index c956ce3c2b..d1b23af29b 100644 --- a/src/network/ssl/qsslsocket_schannel.cpp +++ b/src/network/ssl/qsslsocket_schannel.cpp @@ -1880,6 +1880,28 @@ bool QSslSocketBackendPrivate::verifyCertContext(CERT_CONTEXT *certContext) if (configuration.peerVerifyDepth > 0 && DWORD(configuration.peerVerifyDepth) < verifyDepth) verifyDepth = DWORD(configuration.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; + } + } + for (DWORD i = 0; i < verifyDepth; i++) { CERT_CHAIN_ELEMENT *element = chain->rgpElement[i]; QSslCertificate certificate = getCertificateFromChainElement(element); -- 2.41.0 From a933f89e1f69b97ccb9d1e5f82d9a619c02afcd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Thu, 25 May 2023 14:40:29 +0200 Subject: [PATCH 2/2] 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 (cherry picked from commit 57ba6260c0801055b7188fdaa1818b940590f5f1) --- src/network/ssl/qsslsocket.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 5bb6e7ee4a..2a0b3a4f1d 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -2221,6 +2221,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; } /*! @@ -2470,6 +2474,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; -- 2.41.0