qsslsocket_shared_mac: add more logging into certificate parsing

On macOS we observe strange CA certificates that are coming from
Security framework and which it cannot later parse from the DER
format we feed it in. Add some more debugging in order to understand,
which certificate gives such result.

Task-number: QTBUG-109135
Change-Id: I75cf4591e33c85db6fe80d37d84ede1456c56231
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
(cherry picked from commit 8a18466e38779b63c19e281e92031cebaedbcba5)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Timur Pocheptsov 2022-12-02 11:41:11 +01:00 committed by Qt Cherry-pick Bot
parent 95811fba5e
commit f2a6eeb978
2 changed files with 62 additions and 4 deletions

View File

@ -1126,8 +1126,6 @@ bool TlsCryptographSecureTransport::verifyPeerTrust()
QCFType<CFDataRef> certData = cert.toDer().toCFData(); QCFType<CFDataRef> certData = cert.toDer().toCFData();
if (QCFType<SecCertificateRef> secRef = SecCertificateCreateWithData(nullptr, certData)) if (QCFType<SecCertificateRef> secRef = SecCertificateCreateWithData(nullptr, certData))
CFArrayAppendValue(certArray, secRef); CFArrayAppendValue(certArray, secRef);
else
qCWarning(lcSecureTransport, "Failed to create SecCertificate from QSslCertificate");
} }
SecTrustSetAnchorCertificates(trust, certArray); SecTrustSetAnchorCertificates(trust, certArray);

View File

@ -6,6 +6,7 @@
#include <QtNetwork/qsslcertificate.h> #include <QtNetwork/qsslcertificate.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qglobal.h> #include <QtCore/qglobal.h>
#include <QtCore/qdebug.h> #include <QtCore/qdebug.h>
@ -21,6 +22,8 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcX509, "qt.mac.shared.x509");
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
namespace { namespace {
@ -74,6 +77,52 @@ bool isCaCertificateTrusted(SecCertificateRef cfCert, int domain)
return false; return false;
} }
bool canDERBeParsed(CFDataRef derData, const QSslCertificate &qtCert)
{
// We are observing certificates, that while accepted when we copy them
// from the keychain(s), later give us 'Failed to create SslCertificate
// from QSslCertificate'. It's interesting to know at what step the failure
// occurred. Let's check it and skip it below if it's not valid.
auto checkDer = [](CFDataRef derData, const char *source)
{
Q_ASSERT(source);
Q_ASSERT(derData);
const auto cfLength = CFDataGetLength(derData);
if (cfLength <= 0) {
qCWarning(lcX509) << source << "returned faulty DER data with invalid length.";
return false;
}
QCFType<SecCertificateRef> secRef = SecCertificateCreateWithData(nullptr, derData);
if (!secRef) {
qCWarning(lcX509) << source << "returned faulty DER data which cannot be parsed back.";
return false;
}
return true;
};
if (!checkDer(derData, "SecCertificateCopyData")) {
qCDebug(lcX509) << "Faulty QSslCertificate is:" << qtCert;// Just in case we managed to parse something.
return false;
}
// Generic parser failed?
if (qtCert.isNull()) {
qCWarning(lcX509, "QSslCertificate failed to parse DER");
return false;
}
const QCFType<CFDataRef> qtDerData = qtCert.toDer().toCFData();
if (!checkDer(qtDerData, "QSslCertificate")) {
qCWarning(lcX509) << "Faulty QSslCertificate is:" << qtCert;
return false;
}
return true;
}
} // unnamed namespace } // unnamed namespace
#endif // Q_OS_MACOS #endif // Q_OS_MACOS
@ -94,8 +143,19 @@ QList<QSslCertificate> systemCaCertificates()
SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i); SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i);
QCFType<CFDataRef> derData = SecCertificateCopyData(cfCert); QCFType<CFDataRef> derData = SecCertificateCopyData(cfCert);
if (isCaCertificateTrusted(cfCert, dom)) { if (isCaCertificateTrusted(cfCert, dom)) {
if (derData) if (derData) {
systemCerts << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der); const auto newCert = QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
if (!canDERBeParsed(derData, newCert)) {
// Last attempt to get some information about the certificate:
CFShow(cfCert);
continue;
}
systemCerts << newCert;
} else {
// "Returns NULL if the data passed in the certificate parameter
// is not a valid certificate object."
qCWarning(lcX509, "SecCertificateCopyData returned invalid DER data (nullptr).");
}
} }
} }
} }