From 79138e41d6bf9ca60056a5c7fe0f35f3b67a9237 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Mon, 22 Feb 2021 13:45:07 +0100 Subject: Move QSslCertificate's details and cert-related code to the plugins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also since we have to properly support 'no-ssl' configure option (alas, we support QSslCertificate on such builds) - introduce a minimal crippled QTlsBackendCertOnly, which depends on X509CertificateGeneric. Fixes: QTBUG-90954 Task-number: QTBUG-65922 Change-Id: Ib9d62903f16b7c0eaaa23e319a822c24a7631dc6 Reviewed-by: Edward Welbourne Reviewed-by: MÃ¥rten Nordheim (cherry picked from commit 41fc143635c25f937a557f09890601f6c7d38736) Reviewed-by: Qt Cherry-pick Bot --- src/network/CMakeLists.txt | 15 +- src/network/ssl/qdtls_openssl.cpp | 15 +- src/network/ssl/qsslcertificate.cpp | 316 +++++++++-- src/network/ssl/qsslcertificate.h | 10 +- src/network/ssl/qsslcertificate_openssl.cpp | 799 --------------------------- src/network/ssl/qsslcertificate_p.h | 96 +--- src/network/ssl/qsslcertificate_qt.cpp | 580 ------------------- src/network/ssl/qsslcertificate_schannel.cpp | 62 --- src/network/ssl/qsslsocket_mac.cpp | 2 +- src/network/ssl/qsslsocket_mac_shared.cpp | 1 + src/network/ssl/qsslsocket_openssl.cpp | 160 +----- src/network/ssl/qsslsocket_openssl_p.h | 8 +- src/network/ssl/qsslsocket_schannel.cpp | 4 +- src/network/ssl/qtlsbackend.cpp | 43 +- src/network/ssl/qtlsbackend_cert.cpp | 96 ++++ src/network/ssl/qtlsbackend_cert_p.h | 80 +++ src/network/ssl/qtlsbackend_p.h | 12 +- src/network/ssl/qtlskey_generic_p.h | 22 - src/network/ssl/qx509_generic.cpp | 1 - src/network/ssl/qx509_schannel.cpp | 25 + src/network/ssl/qx509_schannel_p.h | 13 + 21 files changed, 587 insertions(+), 1773 deletions(-) delete mode 100644 src/network/ssl/qsslcertificate_openssl.cpp delete mode 100644 src/network/ssl/qsslcertificate_qt.cpp delete mode 100644 src/network/ssl/qsslcertificate_schannel.cpp create mode 100644 src/network/ssl/qtlsbackend_cert.cpp create mode 100644 src/network/ssl/qtlsbackend_cert_p.h (limited to 'src/network') diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index c9e1b2a20e..254b3e6643 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -47,6 +47,10 @@ qt_internal_add_module(Network ssl/qsslcertificate.cpp ssl/qsslcertificate.h ssl/qsslcertificate_p.h ssl/qsslcertificateextension.cpp ssl/qsslcertificateextension.h ssl/qsslcertificateextension_p.h ssl/qtls_utils_p.h + ssl/qtlsbackend.cpp ssl/qtlsbackend_p.h + ssl/qtlsbackend_cert.cpp ssl/qtlsbackend_cert_p.h + ssl/qx509_base.cpp ssl/qx509_base_p.h + ssl/qx509_generic.cpp ssl/qx509_generic_p.h DEFINES QT_NO_FOREACH QT_NO_USING_NAMESPACE @@ -310,11 +314,6 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_system_proxies QT_USE_SYSTEM_PROXIES ) -qt_internal_extend_target(Network CONDITION NOT QT_FEATURE_openssl - SOURCES - ssl/qsslcertificate_qt.cpp -) - qt_internal_extend_target(Network CONDITION QT_FEATURE_ssl SOURCES ssl/qocspresponse.cpp ssl/qocspresponse.h ssl/qocspresponse_p.h @@ -326,14 +325,11 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_ssl ssl/qsslkey.h ssl/qsslkey_p.cpp ssl/qsslkey_p.h ssl/qsslpresharedkeyauthenticator.cpp ssl/qsslpresharedkeyauthenticator.h ssl/qsslpresharedkeyauthenticator_p.h ssl/qsslsocket.cpp ssl/qsslsocket.h ssl/qsslsocket_p.h - ssl/qtlsbackend.cpp ssl/qtlsbackend_p.h ssl/qtlskey_base.cpp ssl/qtlskey_base_p.h - ssl/qx509_base.cpp ssl/qx509_base_p.h ) qt_internal_extend_target(Network CONDITION QT_FEATURE_schannel AND QT_FEATURE_ssl SOURCES - ssl/qsslcertificate_schannel.cpp ssl/qssldiffiehellmanparameters_dummy.cpp ssl/qsslellipticcurve_dummy.cpp ssl/qsslsocket_qt.cpp @@ -341,7 +337,6 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_schannel AND QT_FEATURE_s ssl/qtlsbackend_schannel_p.h ssl/qtlskey_generic.cpp ssl/qtlskey_generic_p.h ssl/qtlskey_schannel.cpp ssl/qtlskey_schannel_p.h - ssl/qx509_generic.cpp ssl/qx509_generic_p.h ssl/qx509_schannel.cpp ssl/qx509_schannel_p.h LIBRARIES Crypt32 @@ -360,7 +355,6 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_securetransport AND QT_FE ssl/qtlskey_generic.cpp ssl/qtlskey_generic_p.h ssl/qtlskey_st.cpp ssl/qtlskey_st_p.h ssl/qtlsbackend_st.cpp ssl/qtlsbackend_st_p.h - ssl/qx509_generic.cpp ssl/qx509_generic_p.h ssl/qx509_st.cpp ssl/qx509_st_p.h ) @@ -371,7 +365,6 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_dtls AND QT_FEATURE_ssl qt_internal_extend_target(Network CONDITION QT_FEATURE_openssl AND QT_FEATURE_ssl SOURCES - ssl/qsslcertificate_openssl.cpp ssl/qsslcontext_openssl.cpp ssl/qsslcontext_openssl_p.h ssl/qssldiffiehellmanparameters_openssl.cpp ssl/qsslellipticcurve_openssl.cpp diff --git a/src/network/ssl/qdtls_openssl.cpp b/src/network/ssl/qdtls_openssl.cpp index 3f04ab97cd..4fc2f0a4d6 100644 --- a/src/network/ssl/qdtls_openssl.cpp +++ b/src/network/ssl/qdtls_openssl.cpp @@ -47,6 +47,7 @@ #include "qsslsocket_openssl_p.h" #include "qsslcertificate_p.h" #include "qdtls_openssl_p.h" +#include "qx509_openssl_p.h" #include "qudpsocket.h" #include "qssl_p.h" @@ -258,7 +259,7 @@ extern "C" int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx) } auto dtls = static_cast(generic); - dtls->x509Errors.append(QSslErrorEntry::fromStoreContext(ctx)); + dtls->x509Errors.append(QSsl::X509CertificateOpenSSL::errorEntryFromStoreContext(ctx)); } // Always return 1 (OK) to allow verification to continue. We handle the @@ -1263,9 +1264,6 @@ unsigned QDtlsPrivateOpenSSL::pskServerCallback(const char *identity, unsigned c return pskLength; } -// The definition is located in qsslsocket_openssl.cpp. -QSslError _q_OpenSSL_to_QSslError(int errorCode, const QSslCertificate &cert); - bool QDtlsPrivateOpenSSL::verifyPeer() { // DTLSTODO: Windows-specific code for CA fetcher is not here yet. @@ -1300,10 +1298,11 @@ bool QDtlsPrivateOpenSSL::verifyPeer() } // Translate errors from the error list into QSslErrors + using CertClass = QSsl::X509CertificateOpenSSL; errors.reserve(errors.size() + opensslErrors.size()); for (const auto &error : qAsConst(opensslErrors)) { - errors << _q_OpenSSL_to_QSslError(error.code, - dtlsConfiguration.peerCertificateChain.value(error.depth)); + const auto value = dtlsConfiguration.peerCertificateChain.value(error.depth); + errors << CertClass::openSSLErrorToQSslError(error.code, value); } tlsErrors = errors; @@ -1318,11 +1317,11 @@ void QDtlsPrivateOpenSSL::storePeerCertificates() // peer certificate and the chain may be empty if the peer didn't present // any certificate. X509 *x509 = q_SSL_get_peer_certificate(dtls.tlsConnection.data()); - dtlsConfiguration.peerCertificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509); + dtlsConfiguration.peerCertificate = QSsl::X509CertificateOpenSSL::certificateFromX509(x509); q_X509_free(x509); if (dtlsConfiguration.peerCertificateChain.isEmpty()) { auto stack = q_SSL_get_peer_cert_chain(dtls.tlsConnection.data()); - dtlsConfiguration.peerCertificateChain = QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(stack); + dtlsConfiguration.peerCertificateChain = QSsl::X509CertificateOpenSSL::stackOfX509ToQSslCertificates(stack); if (!dtlsConfiguration.peerCertificate.isNull() && mode == QSslSocket::SslServerMode) dtlsConfiguration.peerCertificateChain.prepend(dtlsConfiguration.peerCertificate); } diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp index c63ef5d205..530a9b61ca 100644 --- a/src/network/ssl/qsslcertificate.cpp +++ b/src/network/ssl/qsslcertificate.cpp @@ -131,22 +131,18 @@ */ #include -#ifndef QT_NO_OPENSSL -#include "qsslsocket_openssl_symbols_p.h" -#endif -#ifdef QT_SECURETRANSPORT -#include "qsslsocket_mac_p.h" -#endif -#if QT_CONFIG(schannel) -#include "qsslsocket_schannel_p.h" -#endif + #if QT_CONFIG(regularexpression) #include "qregularexpression.h" #endif -#include "qssl_p.h" -#include "qsslcertificate.h" + +#include "qsslcertificateextension_p.h" #include "qsslcertificate_p.h" +#include "qsslcertificate.h" +#include "qssl_p.h" + #ifndef QT_NO_SSL +#include "qsslsocket_p.h" #include "qsslkey_p.h" #endif @@ -156,6 +152,21 @@ QT_BEGIN_NAMESPACE +QSslCertificatePrivate::QSslCertificatePrivate() +{ +#ifndef QT_NO_SSL + QSslSocketPrivate::ensureInitialized(); +#endif + + const QTlsBackend *tlsBackend = QTlsBackend::activeOrAnyBackend(); + if (tlsBackend) + backend.reset(tlsBackend->createCertificate()); + else + qCWarning(lcSsl, "No TLS backend is available"); +} + +QSslCertificatePrivate::~QSslCertificatePrivate() = default; + /*! Constructs a QSslCertificate by reading \a format encoded data from \a device and using the first certificate found. You can @@ -165,13 +176,25 @@ QT_BEGIN_NAMESPACE QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format) : d(new QSslCertificatePrivate) { -#ifndef QT_NO_OPENSSL - QSslSocketPrivate::ensureInitialized(); - if (device && QSslSocket::supportsSsl()) -#else - if (device) -#endif - d->init(device->readAll(), format); + if (device) { + const auto data = device->readAll(); + if (data.isEmpty()) + return; + + const auto *tlsBackend = QTlsBackend::activeOrAnyBackend(); + if (!tlsBackend) + return; + + auto *X509Reader = format == QSsl::Pem ? tlsBackend->X509PemReader() : tlsBackend->X509DerReader(); + if (!X509Reader) { + qCWarning(lcSsl, "Current TLS plugin does not support reading from PEM/DER"); + return; + } + + QList certs = X509Reader(data, 1); + if (!certs.isEmpty()) + d = certs.first().d; + } } /*! @@ -183,11 +206,22 @@ QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format) QSslCertificate::QSslCertificate(const QByteArray &data, QSsl::EncodingFormat format) : d(new QSslCertificatePrivate) { -#ifndef QT_NO_OPENSSL - QSslSocketPrivate::ensureInitialized(); - if (QSslSocket::supportsSsl()) -#endif - d->init(data, format); + if (data.isEmpty()) + return; + + const auto *tlsBackend = QTlsBackend::activeOrAnyBackend(); + if (!tlsBackend) + return; + + auto *X509Reader = format == QSsl::Pem ? tlsBackend->X509PemReader() : tlsBackend->X509DerReader(); + if (!X509Reader) { + qCWarning(lcSsl, "Current TLS plugin does not support reading from PEM/DER"); + return; + } + + QList certs = X509Reader(data, 1); + if (!certs.isEmpty()) + d = certs.first().d; } /*! @@ -229,6 +263,20 @@ QSslCertificate &QSslCertificate::operator=(const QSslCertificate &other) returns \c false. */ +bool QSslCertificate::operator==(const QSslCertificate &other) const +{ + if (d == other.d) + return true; + + if (isNull() && other.isNull()) + return true; + + if (d->backend.get() && other.d->backend.get()) + return d->backend->isEqual(*other.d->backend.get()); + + return false; +} + /*! \fn bool QSslCertificate::operator!=(const QSslCertificate &other) const @@ -246,6 +294,13 @@ QSslCertificate &QSslCertificate::operator=(const QSslCertificate &other) \sa clear() */ +bool QSslCertificate::isNull() const +{ + if (const auto *backend = d->backend.get()) + return backend->isNull(); + + return true; +} /*! Returns \c true if this certificate is blacklisted; otherwise @@ -268,6 +323,13 @@ bool QSslCertificate::isBlacklisted() const A certificate is considered self-signed its issuer and subject are identical. */ +bool QSslCertificate::isSelfSigned() const +{ + if (const auto *backend = d->backend.get()) + return backend->isSelfSigned(); + + return false; +} /*! Clears the contents of this certificate, making it a null @@ -286,12 +348,26 @@ void QSslCertificate::clear() \fn QByteArray QSslCertificate::version() const Returns the certificate's version string. */ +QByteArray QSslCertificate::version() const +{ + if (const auto *backend = d->backend.get()) + return backend->version(); + + return {}; +} /*! \fn QByteArray QSslCertificate::serialNumber() const Returns the certificate's serial number string in hexadecimal format. */ +QByteArray QSslCertificate::serialNumber() const +{ + if (const auto *backend = d->backend.get()) + return backend->serialNumber(); + + return {}; +} /*! Returns a cryptographic digest of this certificate. By default, @@ -313,6 +389,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa subjectInfo() */ +QStringList QSslCertificate::issuerInfo(SubjectInfo info) const +{ + if (const auto *backend = d->backend.get()) + return backend->issuerInfo(info); + + return {}; +} /*! \fn QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const @@ -323,6 +406,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa subjectInfo() */ +QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const +{ + if (const auto *backend = d->backend.get()) + return backend->issuerInfo(attribute); + + return {}; +} /*! \fn QString QSslCertificate::subjectInfo(SubjectInfo subject) const @@ -333,6 +423,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa issuerInfo() */ +QStringList QSslCertificate::subjectInfo(SubjectInfo info) const +{ + if (const auto *backend = d->backend.get()) + return backend->subjectInfo(info); + + return {}; +} /*! \fn QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const @@ -343,6 +440,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa issuerInfo() */ +QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const +{ + if (const auto *backend = d->backend.get()) + return backend->subjectInfo(attribute); + + return {}; +} /*! \fn QList QSslCertificate::subjectInfoAttributes() const @@ -356,6 +460,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa subjectInfo() */ +QList QSslCertificate::subjectInfoAttributes() const +{ + if (const auto *backend = d->backend.get()) + return backend->subjectInfoAttributes(); + + return {}; +} /*! \fn QList QSslCertificate::issuerInfoAttributes() const @@ -369,6 +480,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa subjectInfo() */ +QList QSslCertificate::issuerInfoAttributes() const +{ + if (const auto *backend = d->backend.get()) + return backend->issuerInfoAttributes(); + + return {}; +} /*! \fn QMultiMap QSslCertificate::subjectAlternativeNames() const @@ -385,6 +503,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa subjectInfo() */ +QMultiMap QSslCertificate::subjectAlternativeNames() const +{ + if (const auto *backend = d->backend.get()) + return backend->subjectAlternativeNames(); + + return {}; +} /*! \fn QDateTime QSslCertificate::effectiveDate() const @@ -394,6 +519,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa expiryDate() */ +QDateTime QSslCertificate::effectiveDate() const +{ + if (const auto *backend = d->backend.get()) + return backend->effectiveDate(); + + return {}; +} /*! \fn QDateTime QSslCertificate::expiryDate() const @@ -403,6 +535,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa effectiveDate() */ +QDateTime QSslCertificate::expiryDate() const +{ + if (const auto *backend = d->backend.get()) + return backend->expiryDate(); + + return {}; +} /*! \fn Qt::HANDLE QSslCertificate::handle() const @@ -416,11 +555,29 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons non-portable, and its return value may vary from platform to platform or change from minor release to minor release. */ +Qt::HANDLE QSslCertificate::handle() const +{ + if (const auto *backend = d->backend.get()) + return backend->handle(); + return {}; +} + +#ifndef QT_NO_SSL /*! \fn QSslKey QSslCertificate::publicKey() const Returns the certificate subject's public key. */ +QSslKey QSslCertificate::publicKey() const +{ + QSslKey key; + if (const auto *backend = d->backend.get()) + QTlsBackend::resetBackend(key, backend->publicKey()); + + return key; +} +#endif // QT_NO_SSL + /*! \fn QList QSslCertificate::extensions() const @@ -428,6 +585,10 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons Returns a list containing the X509 extensions of this certificate. \since 5.0 */ +QList QSslCertificate::extensions() const +{ + return d->extensions(); +} /*! \fn QByteArray QSslCertificate::toPem() const @@ -435,6 +596,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons Returns this certificate converted to a PEM (Base64) encoded representation. */ +QByteArray QSslCertificate::toPem() const +{ + if (const auto *backend = d->backend.get()) + return backend->toPem(); + + return {}; +} /*! \fn QByteArray QSslCertificate::toDer() const @@ -442,6 +610,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons Returns this certificate converted to a DER (binary) encoded representation. */ +QByteArray QSslCertificate::toDer() const +{ + if (const auto *backend = d->backend.get()) + return backend->toDer(); + + return {}; +} /*! \fn QString QSslCertificate::toText() const @@ -451,6 +626,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \since 5.0 */ +QString QSslCertificate::toText() const +{ + if (const auto *backend = d->backend.get()) + return backend->toText(); + + return {}; +} /*! \since 5.15 @@ -576,13 +758,22 @@ QList QSslCertificate::fromDevice(QIODevice *device, QSsl::Enco */ QList QSslCertificate::fromData(const QByteArray &data, QSsl::EncodingFormat format) { - return (format == QSsl::Pem) - ? QSslCertificatePrivate::certificatesFromPem(data) - : QSslCertificatePrivate::certificatesFromDer(data); + const auto *tlsBackend = QTlsBackend::activeOrAnyBackend(); + if (!tlsBackend) { + qCWarning(lcSsl, "No TLS backend is available"); + return {}; + } + + auto reader = format == QSsl::Pem ? tlsBackend->X509PemReader() : tlsBackend->X509DerReader(); + if (!reader) { + qCWarning(lcSsl, "The available TLS backend does not support reading PEM/DER"); + return {}; + } + + return reader(data, -1); } #ifndef QT_NO_SSL - /*! Verifies a certificate chain. The chain to be verified is passed in the \a certificateChain parameter. The first certificate in the list should @@ -597,13 +788,19 @@ QList QSslCertificate::fromData(const QByteArray &data, QSsl::E \since 5.0 */ -#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) QList QSslCertificate::verify(const QList &certificateChain, const QString &hostName) -#else -QList QSslCertificate::verify(QList certificateChain, const QString &hostName) -#endif { - return QSslSocketBackendPrivate::verify(certificateChain, hostName); + const auto *tlsBackend = QTlsBackend::activeOrAnyBackend(); + if (!tlsBackend) { + qCWarning(lcSsl, "No TLS backend is available"); + return {}; + } + auto verifyPtr = tlsBackend->X509Verifier(); + if (!verifyPtr) { + qCWarning(lcSsl, "Available TLS backend does not support manual certificate verification"); + return {}; + } + return verifyPtr(certificateChain, hostName); } /*! @@ -623,10 +820,43 @@ bool QSslCertificate::importPkcs12(QIODevice *device, QList *caCertificates, const QByteArray &passPhrase) { - return QSslSocketBackendPrivate::importPkcs12(device, key, certificate, caCertificates, passPhrase); + if (!device || !key || !certificate) + return false; + + const auto *tlsBackend = QTlsBackend::activeOrAnyBackend(); + if (!tlsBackend) { + qCWarning(lcSsl, "No TLS backend is available"); + return false; + } + + if (auto reader = tlsBackend->X509Pkcs12Reader()) + return reader(device, key, certificate, caCertificates, passPhrase); + + qCWarning(lcSsl, "Available TLS backend does not support PKCS12"); + + return false; } +#endif // QT_NO_SSL -#endif +QList QSslCertificatePrivate::extensions() const +{ + QList result; + + if (backend.get()) { + auto nExt = backend->numberOfExtensions(); + for (decltype (nExt) i = 0; i < nExt; ++i) { + QSslCertificateExtension ext; + ext.d->oid = backend->oidForExtension(i); + ext.d->name = backend->nameForExtension(i); + ext.d->value = backend->valueForExtension(i); + ext.d->critical = backend->isExtensionCritical(i); + ext.d->supported = backend->isExtensionSupported(i); + result << ext; + } + } + + return result; +} // These certificates are known to be fraudulent and were created during the comodo // compromise. See http://www.comodo.com/Comodo-Fraud-Incident-2011-03-23.html @@ -757,6 +987,16 @@ QString QSslCertificate::subjectDisplayName() const return QString(); } +/*! + \internal + + Returns X509 backend this QSslCertificate is using. +*/ +QSsl::X509Certificate *QSslCertificate::backendImplementation() const +{ + return d->backend.get(); +} + /*! \fn size_t qHash(const QSslCertificate &key, size_t seed) @@ -764,6 +1004,14 @@ QString QSslCertificate::subjectDisplayName() const \since 5.4 \relates QHash */ +size_t qHash(const QSslCertificate &key, size_t seed) noexcept +{ + if (const auto *backend = key.d->backend.get()) + return backend->hash(seed); + + return seed; + +} #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug debug, const QSslCertificate &certificate) diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h index a2ba90465f..ea9c99adda 100644 --- a/src/network/ssl/qsslcertificate.h +++ b/src/network/ssl/qsslcertificate.h @@ -148,12 +148,7 @@ public: const QByteArray &data, QSsl::EncodingFormat format = QSsl::Pem); #ifndef QT_NO_SSL -#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) static QList verify(const QList &certificateChain, const QString &hostName = QString()); -#else - static QList verify(QList certificateChain, const QString &hostName = QString()); -#endif - static bool importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert, QList *caCertificates = nullptr, @@ -163,10 +158,7 @@ public: Qt::HANDLE handle() const; private: - QSsl::X509Certificate *backendImplementation() const - { - return nullptr; // TLSTODO - } + QSsl::X509Certificate *backendImplementation() const; QExplicitlySharedDataPointer d; friend class QSslCertificatePrivate; friend class QSslSocketBackendPrivate; diff --git a/src/network/ssl/qsslcertificate_openssl.cpp b/src/network/ssl/qsslcertificate_openssl.cpp deleted file mode 100644 index 2d85171c75..0000000000 --- a/src/network/ssl/qsslcertificate_openssl.cpp +++ /dev/null @@ -1,799 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Copyright (C) 2016 Richard J. Moore -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qssl_p.h" -#include "qsslsocket_openssl_symbols_p.h" -#include "qsslcertificate_p.h" -#include "qsslkey_p.h" -#include "qsslcertificateextension_p.h" -#include "qtlsbackend_openssl_p.h" -#include "qtlskey_openssl_p.h" - -#include -#include - -QT_BEGIN_NAMESPACE - -constexpr int MutexPoolSize = 17; -static QBasicMutex mutexPool[MutexPoolSize]; -namespace QMutexPool { - static QBasicMutex *globalInstanceGet(const void *addr) - { - return mutexPool + (quintptr(addr) % MutexPoolSize); - } -} - -// forward declaration -static QMultiMap _q_mapFromX509Name(X509_NAME *name); - -bool QSslCertificate::operator==(const QSslCertificate &other) const -{ - if (d == other.d) - return true; - - if (d->null && other.d->null) - return true; - - if (d->x509 && other.d->x509) { - const int ret = q_X509_cmp(d->x509, other.d->x509); - if (ret >= -1 && ret <= 1) - return ret == 0; - QSslSocketBackendPrivate::logAndClearErrorQueue(); - } - - return false; -} - -size_t qHash(const QSslCertificate &key, size_t seed) noexcept -{ - if (X509 * const x509 = key.d->x509) { - const EVP_MD *sha1 = q_EVP_sha1(); - unsigned int len = 0; - unsigned char md[EVP_MAX_MD_SIZE]; - q_X509_digest(x509, sha1, md, &len); - return qHashBits(md, len, seed); - } - - return seed; -} - -bool QSslCertificate::isNull() const -{ - return d->null; -} - -bool QSslCertificate::isSelfSigned() const -{ - if (!d->x509) - return false; - - return (q_X509_check_issued(d->x509, d->x509) == X509_V_OK); -} - -QByteArray QSslCertificate::version() const -{ -#if QT_CONFIG(thread) - QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); -#endif - if (d->versionString.isEmpty() && d->x509) - d->versionString = QByteArray::number(qlonglong(q_X509_get_version(d->x509)) + 1); - - return d->versionString; -} - -QByteArray QSslCertificate::serialNumber() const -{ -#if QT_CONFIG(thread) - QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); -#endif - if (d->serialNumberString.isEmpty() && d->x509) { - ASN1_INTEGER *serialNumber = q_X509_get_serialNumber(d->x509); - QByteArray hexString; - hexString.reserve(serialNumber->length * 3); - for (int a = 0; a < serialNumber->length; ++a) { - hexString += QByteArray::number(serialNumber->data[a], 16).rightJustified(2, '0'); - hexString += ':'; - } - hexString.chop(1); - d->serialNumberString = hexString; - } - return d->serialNumberString; -} - -QStringList QSslCertificate::issuerInfo(SubjectInfo info) const -{ -#if QT_CONFIG(thread) - QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); -#endif - // lazy init - if (d->issuerInfo.isEmpty() && d->x509) - d->issuerInfo = - _q_mapFromX509Name(q_X509_get_issuer_name(d->x509)); - - return d->issuerInfo.values(d->subjectInfoToString(info)); -} - -QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const -{ -#if QT_CONFIG(thread) - QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); -#endif - // lazy init - if (d->issuerInfo.isEmpty() && d->x509) - d->issuerInfo = - _q_mapFromX509Name(q_X509_get_issuer_name(d->x509)); - - return d->issuerInfo.values(attribute); -} - -QStringList QSslCertificate::subjectInfo(SubjectInfo info) const -{ -#if QT_CONFIG(thread) - QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); -#endif - // lazy init - if (d->subjectInfo.isEmpty() && d->x509) - d->subjectInfo = - _q_mapFromX509Name(q_X509_get_subject_name(d->x509)); - - return d->subjectInfo.values(d->subjectInfoToString(info)); -} - -QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const -{ -#if QT_CONFIG(thread) - QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); -#endif - // lazy init - if (d->subjectInfo.isEmpty() && d->x509) - d->subjectInfo = - _q_mapFromX509Name(q_X509_get_subject_name(d->x509)); - - return d->subjectInfo.values(attribute); -} - -QList QSslCertificate::subjectInfoAttributes() const -{ -#if QT_CONFIG(thread) - QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); -#endif - // lazy init - if (d->subjectInfo.isEmpty() && d->x509) - d->subjectInfo = - _q_mapFromX509Name(q_X509_get_subject_name(d->x509)); - - return d->subjectInfo.uniqueKeys(); -} - -QList QSslCertificate::issuerInfoAttributes() const -{ -#if QT_CONFIG(thread) - QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); -#endif - // lazy init - if (d->issuerInfo.isEmpty() && d->x509) - d->issuerInfo = - _q_mapFromX509Name(q_X509_get_issuer_name(d->x509)); - - return d->issuerInfo.uniqueKeys(); -} - -QMultiMap QSslCertificate::subjectAlternativeNames() const -{ - QMultiMap result; - - if (!d->x509) - return result; - - STACK_OF(GENERAL_NAME) *altNames = (STACK_OF(GENERAL_NAME) *)q_X509_get_ext_d2i( - d->x509, NID_subject_alt_name, nullptr, nullptr); - - auto altName = [](ASN1_IA5STRING *ia5, int len) { - const char *altNameStr = reinterpret_cast(q_ASN1_STRING_get0_data(ia5)); - return QString::fromLatin1(altNameStr, len); - }; - if (altNames) { - for (int i = 0; i < q_sk_GENERAL_NAME_num(altNames); ++i) { - const GENERAL_NAME *genName = q_sk_GENERAL_NAME_value(altNames, i); - if (genName->type != GEN_DNS && genName->type != GEN_EMAIL && genName->type != GEN_IPADD) - continue; - - int len = q_ASN1_STRING_length(genName->d.ia5); - if (len < 0 || len >= 8192) { - // broken name - continue; - } - - switch (genName->type) { - case GEN_DNS: - result.insert(QSsl::DnsEntry, altName(genName->d.ia5, len)); - break; - case GEN_EMAIL: - result.insert(QSsl::EmailEntry, altName(genName->d.ia5, len)); - break; - case GEN_IPADD: { - QHostAddress ipAddress; - switch (len) { - case 4: // IPv4 - ipAddress = QHostAddress(qFromBigEndian(*reinterpret_cast(genName->d.iPAddress->data))); - break; - case 16: // IPv6 - ipAddress = QHostAddress(reinterpret_cast(genName->d.iPAddress->data)); - break; - default: // Unknown IP address format - break; - } - if (!ipAddress.isNull()) - result.insert(QSsl::IpAddressEntry, ipAddress.toString()); - break; - } - default: - break; - } - } - - q_OPENSSL_sk_pop_free((OPENSSL_STACK*)altNames, reinterpret_cast(q_GENERAL_NAME_free)); - } - - return result; -} - -QDateTime QSslCertificate::effectiveDate() const -{ - return d->notValidBefore; -} - -QDateTime QSslCertificate::expiryDate() const -{ - return d->notValidAfter; -} - -Qt::HANDLE QSslCertificate::handle() const -{ - return Qt::HANDLE(d->x509); -} - -QSslKey QSslCertificate::publicKey() const -{ - if (!d->x509) - return QSslKey(); - - QSslKey key; - - auto *tlsKey = QTlsBackend::backend(key); - tlsKey->keyType = QSsl::PublicKey; - - EVP_PKEY *pkey = q_X509_get_pubkey(d->x509); - Q_ASSERT(pkey); - const int keyType = q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey)); - - if (keyType == EVP_PKEY_RSA) { - tlsKey->rsa = q_EVP_PKEY_get1_RSA(pkey); - tlsKey->keyAlgorithm = QSsl::Rsa; - tlsKey->keyIsNull = false; - } else if (keyType == EVP_PKEY_DSA) { - tlsKey->dsa = q_EVP_PKEY_get1_DSA(pkey); - tlsKey->keyAlgorithm = QSsl::Dsa; - tlsKey->keyIsNull = false; -#ifndef OPENSSL_NO_EC - } else if (keyType == EVP_PKEY_EC) { - tlsKey->ec = q_EVP_PKEY_get1_EC_KEY(pkey); - tlsKey->keyAlgorithm = QSsl::Ec; - tlsKey->keyIsNull = false; -#endif - } else if (keyType == EVP_PKEY_DH) { - // DH unsupported - } else { - // error? - } - - q_EVP_PKEY_free(pkey); - return key; -} - -/* - * Convert unknown extensions to a QVariant. - */ -static QVariant x509UnknownExtensionToValue(X509_EXTENSION *ext) -{ - // Get the extension specific method object if available - // we cast away the const-ness here because some versions of openssl - // don't use const for the parameters in the functions pointers stored - // in the object. - Q_ASSERT(ext); - - X509V3_EXT_METHOD *meth = const_cast(q_X509V3_EXT_get(ext)); - if (!meth) { - ASN1_OCTET_STRING *value = q_X509_EXTENSION_get_data(ext); - Q_ASSERT(value); - QByteArray result( reinterpret_cast(q_ASN1_STRING_get0_data(value)), - q_ASN1_STRING_length(value)); - return result; - } - - //const unsigned char *data = ext->value->data; - void *ext_internal = q_X509V3_EXT_d2i(ext); - - // If this extension can be converted - if (meth->i2v && ext_internal) { - STACK_OF(CONF_VALUE) *val = meth->i2v(meth, ext_internal, nullptr); - - QVariantMap map; - QVariantList list; - bool isMap = false; - - for (int j = 0; j < q_SKM_sk_num(CONF_VALUE, val); j++) { - CONF_VALUE *nval = q_SKM_sk_value(CONF_VALUE, val, j); - if (nval->name && nval->value) { - isMap = true; - map[QString::fromUtf8(nval->name)] = QString::fromUtf8(nval->value); - } else if (nval->name) { - list << QString::fromUtf8(nval->name); - } else if (nval->value) { - list << QString::fromUtf8(nval->value); - } - } - - if (isMap) - return map; - else - return list; - } else if (meth->i2s && ext_internal) { - QVariant result(QString::fromUtf8(meth->i2s(meth, ext_internal))); - return result; - } else if (meth->i2r && ext_internal) { - QByteArray result; - - BIO *bio = q_BIO_new(q_BIO_s_mem()); - if (!bio) - return result; - - meth->i2r(meth, ext_internal, bio, 0); - - char *bio_buffer; - long bio_size = q_BIO_get_mem_data(bio, &bio_buffer); - result = QByteArray(bio_buffer, bio_size); - - q_BIO_free(bio); - return result; - } - - return QVariant(); -} - -/* - * Convert extensions to a variant. The naming of the keys of the map are - * taken from RFC 5280, however we decided the capitalisation in the RFC - * was too silly for the real world. - */ -static QVariant x509ExtensionToValue(X509_EXTENSION *ext) -{ - ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(ext); - int nid = q_OBJ_obj2nid(obj); - - switch (nid) { - case NID_basic_constraints: - { - BASIC_CONSTRAINTS *basic = reinterpret_cast(q_X509V3_EXT_d2i(ext)); - if (!basic) - return QVariant(); - - QVariantMap result; - result[QLatin1String("ca")] = basic->ca ? true : false; - if (basic->pathlen) - result[QLatin1String("pathLenConstraint")] = (qlonglong)q_ASN1_INTEGER_get(basic->pathlen); - - q_BASIC_CONSTRAINTS_free(basic); - return result; - } - break; - case NID_info_access: - { - AUTHORITY_INFO_ACCESS *info = reinterpret_cast(q_X509V3_EXT_d2i(ext)); - if (!info) - return QVariant(); - - QVariantMap result; - for (int i=0; i < q_SKM_sk_num(ACCESS_DESCRIPTION, info); i++) { - ACCESS_DESCRIPTION *ad = q_SKM_sk_value(ACCESS_DESCRIPTION, info, i); - - GENERAL_NAME *name = ad->location; - if (name->type == GEN_URI) { - int len = q_ASN1_STRING_length(name->d.uniformResourceIdentifier); - if (len < 0 || len >= 8192) { - // broken name - continue; - } - - const char *uriStr = reinterpret_cast(q_ASN1_STRING_get0_data(name->d.uniformResourceIdentifier)); - const QString uri = QString::fromUtf8(uriStr, len); - - result[QString::fromUtf8(QSslCertificatePrivate::asn1ObjectName(ad->method))] = uri; - } else { - qCWarning(lcSsl) << "Strange location type" << name->type; - } - } - - q_OPENSSL_sk_pop_free((OPENSSL_STACK*)info, reinterpret_cast(q_OPENSSL_sk_free)); - return result; - } - break; - case NID_subject_key_identifier: - { - void *ext_internal = q_X509V3_EXT_d2i(ext); - if (!ext_internal) - return QVariant(); - // we cast away the const-ness here because some versions of openssl - // don't use const for the parameters in the functions pointers stored - // in the object. - X509V3_EXT_METHOD *meth = const_cast(q_X509V3_EXT_get(ext)); - - return QVariant(QString::fromUtf8(meth->i2s(meth, ext_internal))); - } - break; - case NID_authority_key_identifier: - { - AUTHORITY_KEYID *auth_key = reinterpret_cast(q_X509V3_EXT_d2i(ext)); - if (!auth_key) - return QVariant(); - - QVariantMap result; - - // keyid - if (auth_key->keyid) { - QByteArray keyid(reinterpret_cast(auth_key->keyid->data), - auth_key->keyid->length); - result[QLatin1String("keyid")] = keyid.toHex(); - } - - // issuer - // TODO: GENERAL_NAMES - - // serial - if (auth_key->serial) - result[QLatin1String("serial")] = (qlonglong)q_ASN1_INTEGER_get(auth_key->serial); - - q_AUTHORITY_KEYID_free(auth_key); - return result; - } - break; - } - - return QVariant(); -} - -QSslCertificateExtension QSslCertificatePrivate::convertExtension(X509_EXTENSION *ext) -{ - Q_ASSERT(ext); - - QSslCertificateExtension result; - - ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(ext); - if (!obj) { - qCWarning(lcSsl, "Invalid (nullptr) ASN1_OBJECT"); - return result; - } - - QByteArray oid = QSslCertificatePrivate::asn1ObjectId(obj); - QByteArray name = QSslCertificatePrivate::asn1ObjectName(obj); - - result.d->oid = QString::fromUtf8(oid); - result.d->name = QString::fromUtf8(name); - - bool critical = q_X509_EXTENSION_get_critical(ext); - result.d->critical = critical; - - // Lets see if we have custom support for this one - QVariant extensionValue = x509ExtensionToValue(ext); - if (extensionValue.isValid()) { - result.d->value = extensionValue; - result.d->supported = true; - - return result; - } - - extensionValue = x509UnknownExtensionToValue(ext); - if (extensionValue.isValid()) { - result.d->value = extensionValue; - result.d->supported = false; - return result; - } - - return result; -} - -QList QSslCertificate::extensions() const -{ - QList result; - - if (!d->x509) - return result; - - int count = q_X509_get_ext_count(d->x509); - if (count <= 0) - return result; - - result.reserve(count); - - for (int i = 0; i < count; i++) { - X509_EXTENSION *ext = q_X509_get_ext(d->x509, i); - if (!ext) { - qCWarning(lcSsl) << "Invalid (nullptr) extension at index" << i; - continue; - } - result << QSslCertificatePrivate::convertExtension(ext); - } - - // Converting an extension may result in an error(s), clean them up. - Q_UNUSED(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); - - return result; -} - -QByteArray QSslCertificate::toPem() const -{ - if (!d->x509) - return QByteArray(); - return d->QByteArray_from_X509(d->x509, QSsl::Pem); -} - -QByteArray QSslCertificate::toDer() const -{ - if (!d->x509) - return QByteArray(); - return d->QByteArray_from_X509(d->x509, QSsl::Der); -} - -QString QSslCertificate::toText() const -{ - if (!d->x509) - return QString(); - return d->text_from_X509(d->x509); -} - -#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----" -#define ENDCERTSTRING "-----END CERTIFICATE-----" - -void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format) -{ - if (!data.isEmpty()) { - const QList certs = (format == QSsl::Pem) - ? certificatesFromPem(data, 1) - : certificatesFromDer(data, 1); - if (!certs.isEmpty()) { - *this = *certs.first().d; - if (x509) - x509 = q_X509_dup(x509); - } - } -} - -// ### refactor against QSsl::pemFromDer() etc. (to avoid redundant implementations) -QByteArray QSslCertificatePrivate::QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format) -{ - if (!x509) { - qCWarning(lcSsl, "QSslSocketBackendPrivate::X509_to_QByteArray: null X509"); - return QByteArray(); - } - - // Use i2d_X509 to convert the X509 to an array. - int length = q_i2d_X509(x509, nullptr); - QByteArray array; - array.resize(length); - char *data = array.data(); - char **dataP = &data; - unsigned char **dataPu = (unsigned char **)dataP; - if (q_i2d_X509(x509, dataPu) < 0) - return QByteArray(); - - if (format == QSsl::Der) - return array; - - // Convert to Base64 - wrap at 64 characters. - array = array.toBase64(); - QByteArray tmp; - for (int i = 0; i <= array.size() - 64; i += 64) { - tmp += QByteArray::fromRawData(array.data() + i, 64); - tmp += '\n'; - } - if (int remainder = array.size() % 64) { - tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder); - tmp += '\n'; - } - - return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n"; -} - -QString QSslCertificatePrivate::text_from_X509(X509 *x509) -{ - if (!x509) { - qCWarning(lcSsl, "QSslSocketBackendPrivate::text_from_X509: null X509"); - return QString(); - } - - QByteArray result; - BIO *bio = q_BIO_new(q_BIO_s_mem()); - if (!bio) - return QString(); - - q_X509_print(bio, x509); - - QVarLengthArray data; - int count = q_BIO_read(bio, data.data(), 16384); - if ( count > 0 ) { - result = QByteArray( data.data(), count ); - } - - q_BIO_free(bio); - - return QString::fromLatin1(result); -} - -QByteArray QSslCertificatePrivate::asn1ObjectId(ASN1_OBJECT *object) -{ - char buf[80]; // The openssl docs a buffer length of 80 should be more than enough - q_OBJ_obj2txt(buf, sizeof(buf), object, 1); // the 1 says always use the oid not the long name - - return QByteArray(buf); -} - - -QByteArray QSslCertificatePrivate::asn1ObjectName(ASN1_OBJECT *object) -{ - int nid = q_OBJ_obj2nid(object); - if (nid != NID_undef) - return QByteArray(q_OBJ_nid2sn(nid)); - - return asn1ObjectId(object); -} - -static QMultiMap _q_mapFromX509Name(X509_NAME *name) -{ - QMultiMap info; - for (int i = 0; i < q_X509_NAME_entry_count(name); ++i) { - X509_NAME_ENTRY *e = q_X509_NAME_get_entry(name, i); - - QByteArray name = QSslCertificatePrivate::asn1ObjectName(q_X509_NAME_ENTRY_get_object(e)); - unsigned char *data = nullptr; - int size = q_ASN1_STRING_to_UTF8(&data, q_X509_NAME_ENTRY_get_data(e)); - info.insert(name, QString::fromUtf8((char*)data, size)); -#if QT_CONFIG(opensslv11) - q_CRYPTO_free(data, nullptr, 0); -#else - q_CRYPTO_free(data); -#endif - } - - return info; -} - -QSslCertificate QSslCertificatePrivate::QSslCertificate_from_X509(X509 *x509) -{ - QSslCertificate certificate; - if (!x509 || !QSslSocket::supportsSsl()) - return certificate; - - ASN1_TIME *nbef = q_X509_getm_notBefore(x509); - ASN1_TIME *naft = q_X509_getm_notAfter(x509); - - certificate.d->notValidBefore = q_getTimeFromASN1(nbef); - certificate.d->notValidAfter = q_getTimeFromASN1(naft); - certificate.d->null = false; - certificate.d->x509 = q_X509_dup(x509); - - return certificate; -} - -static bool matchLineFeed(const QByteArray &pem, int *offset) -{ - char ch = 0; - - // ignore extra whitespace at the end of the line - while (*offset < pem.size() && (ch = pem.at(*offset)) == ' ') - ++*offset; - - if (ch == '\n') { - *offset += 1; - return true; - } - if (ch == '\r' && pem.size() > (*offset + 1) && pem.at(*offset + 1) == '\n') { - *offset += 2; - return true; - } - return false; -} - -QList QSslCertificatePrivate::certificatesFromPem(const QByteArray &pem, int count) -{ - QList certificates; - QSslSocketPrivate::ensureInitialized(); - - int offset = 0; - while (count == -1 || certificates.size() < count) { - int startPos = pem.indexOf(BEGINCERTSTRING, offset); - if (startPos == -1) - break; - startPos += sizeof(BEGINCERTSTRING) - 1; - if (!matchLineFeed(pem, &startPos)) - break; - - int endPos = pem.indexOf(ENDCERTSTRING, startPos); - if (endPos == -1) - break; - - offset = endPos + sizeof(ENDCERTSTRING) - 1; - if (offset < pem.size() && !matchLineFeed(pem, &offset)) - break; - - QByteArray decoded = QByteArray::fromBase64( - QByteArray::fromRawData(pem.data() + startPos, endPos - startPos)); - const unsigned char *data = (const unsigned char *)decoded.data(); - - if (X509 *x509 = q_d2i_X509(nullptr, &data, decoded.size())) { - certificates << QSslCertificate_from_X509(x509); - q_X509_free(x509); - } - } - - return certificates; -} - -QList QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count) -{ - QList certificates; - QSslSocketPrivate::ensureInitialized(); - - const unsigned char *data = (const unsigned char *)der.data(); - int size = der.size(); - - while (size > 0 && (count == -1 || certificates.size() < count)) { - if (X509 *x509 = q_d2i_X509(nullptr, &data, size)) { - certificates << QSslCertificate_from_X509(x509); - q_X509_free(x509); - } else { - break; - } - size -= ((const char *)data - der.data()); - } - - return certificates; -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslcertificate_p.h b/src/network/ssl/qsslcertificate_p.h index 4588aa7d6f..33db88d8fa 100644 --- a/src/network/ssl/qsslcertificate_p.h +++ b/src/network/ssl/qsslcertificate_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -38,11 +38,8 @@ ****************************************************************************/ -#ifndef QSSLCERTIFICATE_OPENSSL_P_H -#define QSSLCERTIFICATE_OPENSSL_P_H - -#include -#include "qsslcertificate.h" +#ifndef QSSLCERTIFICATE_P_H +#define QSSLCERTIFICATE_P_H // // W A R N I N G @@ -55,99 +52,34 @@ // We mean it. // -#ifndef QT_NO_SSL -#include "qsslsocket_p.h" -#endif +#include + #include "qsslcertificateextension.h" -#include -#include +#include "qsslcertificate.h" +#include "qtlsbackend_p.h" -#ifndef QT_NO_OPENSSL -#include -#else -struct X509; -struct X509_EXTENSION; -struct ASN1_OBJECT; -#endif +#include -#if QT_CONFIG(schannel) -#include -#endif +#include QT_BEGIN_NAMESPACE -// forward declaration - class QSslCertificatePrivate { public: - QSslCertificatePrivate() - : null(true), x509(nullptr) - { -#ifndef QT_NO_SSL - QSslSocketPrivate::ensureInitialized(); -#endif - } - - ~QSslCertificatePrivate() - { -#ifndef QT_NO_OPENSSL - if (x509) - q_X509_free(x509); -#endif -#if QT_CONFIG(schannel) - if (certificateContext) - CertFreeCertificateContext(certificateContext); -#endif - } + QSslCertificatePrivate(); + ~QSslCertificatePrivate(); - bool null; - QByteArray versionString; - QByteArray serialNumberString; - - QMultiMap issuerInfo; - QMultiMap subjectInfo; - QDateTime notValidAfter; - QDateTime notValidBefore; - -#ifdef QT_NO_OPENSSL - bool subjectMatchesIssuer; - QSsl::KeyAlgorithm publicKeyAlgorithm; - QByteArray publicKeyDerData; - QMultiMap subjectAlternativeNames; - QList extensions; - - QByteArray derData; - - bool parse(const QByteArray &data); - bool parseExtension(const QByteArray &data, QSslCertificateExtension *extension); -#endif - X509 *x509; - - void init(const QByteArray &data, QSsl::EncodingFormat format); - - static QByteArray asn1ObjectId(ASN1_OBJECT *object); - static QByteArray asn1ObjectName(ASN1_OBJECT *object); - static QByteArray QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format); - static QString text_from_X509(X509 *x509); - static QSslCertificate QSslCertificate_from_X509(X509 *x509); - static QList certificatesFromPem(const QByteArray &pem, int count = -1); - static QList certificatesFromDer(const QByteArray &der, int count = -1); + QList extensions() const; static bool isBlacklisted(const QSslCertificate &certificate); - static QSslCertificateExtension convertExtension(X509_EXTENSION *ext); static QByteArray subjectInfoToString(QSslCertificate::SubjectInfo info); friend class QSslSocketBackendPrivate; QAtomicInt ref; - -#if QT_CONFIG(schannel) - const CERT_CONTEXT *certificateContext = nullptr; - - static QSslCertificate QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext); -#endif + std::unique_ptr backend; }; QT_END_NAMESPACE -#endif // QSSLCERTIFICATE_OPENSSL_P_H +#endif // QSSLCERTIFICATE_P_H diff --git a/src/network/ssl/qsslcertificate_qt.cpp b/src/network/ssl/qsslcertificate_qt.cpp deleted file mode 100644 index a8efecc3f6..0000000000 --- a/src/network/ssl/qsslcertificate_qt.cpp +++ /dev/null @@ -1,580 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsslcertificate.h" -#include "qsslcertificate_p.h" - -#include "qssl_p.h" -#ifndef QT_NO_SSL -#include "qsslkey.h" -#include "qsslkey_p.h" -#include "qtlskey_generic_p.h" -#include "qtlsbackend_p.h" -#endif -#include "qsslcertificateextension.h" -#include "qsslcertificateextension_p.h" -#include "qasn1element_p.h" - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -bool QSslCertificate::operator==(const QSslCertificate &other) const -{ - if (d == other.d) - return true; - if (d->null && other.d->null) - return true; - return d->derData == other.d->derData; -} - -size_t qHash(const QSslCertificate &key, size_t seed) noexcept -{ - // DER is the native encoding here, so toDer() is just "return d->derData": - return qHash(key.toDer(), seed); -} - -bool QSslCertificate::isNull() const -{ - return d->null; -} - -bool QSslCertificate::isSelfSigned() const -{ - if (d->null) - return false; - - qCWarning(lcSsl, - "QSslCertificate::isSelfSigned: This function does not check, whether the certificate " - "is actually signed. It just checks whether issuer and subject are identical"); - return d->subjectMatchesIssuer; -} - -QByteArray QSslCertificate::version() const -{ - return d->versionString; -} - -QByteArray QSslCertificate::serialNumber() const -{ - return d->serialNumberString; -} - -QStringList QSslCertificate::issuerInfo(SubjectInfo info) const -{ - return issuerInfo(QSslCertificatePrivate::subjectInfoToString(info)); -} - -QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const -{ - return d->issuerInfo.values(attribute); -} - -QStringList QSslCertificate::subjectInfo(SubjectInfo info) const -{ - return subjectInfo(QSslCertificatePrivate::subjectInfoToString(info)); -} - -QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const -{ - return d->subjectInfo.values(attribute); -} - -QList QSslCertificate::subjectInfoAttributes() const -{ - return d->subjectInfo.uniqueKeys(); -} - -QList QSslCertificate::issuerInfoAttributes() const -{ - return d->issuerInfo.uniqueKeys(); -} - -QMultiMap QSslCertificate::subjectAlternativeNames() const -{ - return d->subjectAlternativeNames; -} - -QDateTime QSslCertificate::effectiveDate() const -{ - return d->notValidBefore; -} - -QDateTime QSslCertificate::expiryDate() const -{ - return d->notValidAfter; -} - -#if !QT_CONFIG(schannel) // implemented in qsslcertificate_schannel.cpp -Qt::HANDLE QSslCertificate::handle() const -{ - Q_UNIMPLEMENTED(); - return nullptr; -} -#endif - -#ifndef QT_NO_SSL -QSslKey QSslCertificate::publicKey() const -{ - QSslKey key; - auto *tlsKey = QTlsBackend::backend(key); - if (!tlsKey) - return key; - - tlsKey->keyType = QSsl::PublicKey; - if (d->publicKeyAlgorithm != QSsl::Opaque) - tlsKey->decodeDer(QSsl::PublicKey, d->publicKeyAlgorithm, d->publicKeyDerData, {}, false); - - return key; -} -#endif - -QList QSslCertificate::extensions() const -{ - return d->extensions; -} - -#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----" -#define ENDCERTSTRING "-----END CERTIFICATE-----" - -QByteArray QSslCertificate::toPem() const -{ - QByteArray array = toDer(); - - // Convert to Base64 - wrap at 64 characters. - array = array.toBase64(); - QByteArray tmp; - for (int i = 0; i <= array.size() - 64; i += 64) { - tmp += QByteArray::fromRawData(array.data() + i, 64); - tmp += '\n'; - } - if (int remainder = array.size() % 64) { - tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder); - tmp += '\n'; - } - - return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n"; -} - -QByteArray QSslCertificate::toDer() const -{ - return d->derData; -} - -QString QSslCertificate::toText() const -{ - Q_UNIMPLEMENTED(); - return QString(); -} - -void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format) -{ - if (!data.isEmpty()) { - const QList certs = (format == QSsl::Pem) - ? certificatesFromPem(data, 1) - : certificatesFromDer(data, 1); - if (!certs.isEmpty()) { - *this = *certs.first().d; -#if QT_CONFIG(schannel) - if (certificateContext) - certificateContext = CertDuplicateCertificateContext(certificateContext); -#endif - } - } -} - -static bool matchLineFeed(const QByteArray &pem, int *offset) -{ - char ch = 0; - - // ignore extra whitespace at the end of the line - while (*offset < pem.size() && (ch = pem.at(*offset)) == ' ') - ++*offset; - - if (ch == '\n') { - *offset += 1; - return true; - } - if (ch == '\r' && pem.size() > (*offset + 1) && pem.at(*offset + 1) == '\n') { - *offset += 2; - return true; - } - return false; -} - -QList QSslCertificatePrivate::certificatesFromPem(const QByteArray &pem, int count) -{ - QList certificates; - int offset = 0; - while (count == -1 || certificates.size() < count) { - int startPos = pem.indexOf(BEGINCERTSTRING, offset); - if (startPos == -1) - break; - startPos += sizeof(BEGINCERTSTRING) - 1; - if (!matchLineFeed(pem, &startPos)) - break; - - int endPos = pem.indexOf(ENDCERTSTRING, startPos); - if (endPos == -1) - break; - - offset = endPos + sizeof(ENDCERTSTRING) - 1; - if (offset < pem.size() && !matchLineFeed(pem, &offset)) - break; - - QByteArray decoded = QByteArray::fromBase64( - QByteArray::fromRawData(pem.data() + startPos, endPos - startPos)); - certificates << certificatesFromDer(decoded, 1);; - } - - return certificates; -} - -QList QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count) -{ - QList certificates; - - QByteArray data = der; - while (count == -1 || certificates.size() < count) { - QSslCertificate cert; - if (!cert.d->parse(data)) - break; - - certificates << cert; - data.remove(0, cert.d->derData.size()); - } - - return certificates; -} - -static QByteArray colonSeparatedHex(const QByteArray &value) -{ - const int size = value.size(); - int i = 0; - while (i < size && !value.at(i)) // skip leading zeros - ++i; - - return value.mid(i).toHex(':'); -} - -bool QSslCertificatePrivate::parse(const QByteArray &data) -{ - QAsn1Element root; - - QDataStream dataStream(data); - if (!root.read(dataStream) || root.type() != QAsn1Element::SequenceType) - return false; - - QDataStream rootStream(root.value()); - QAsn1Element cert; - if (!cert.read(rootStream) || cert.type() != QAsn1Element::SequenceType) - return false; - - // version or serial number - QAsn1Element elem; - QDataStream certStream(cert.value()); - if (!elem.read(certStream)) - return false; - - if (elem.type() == QAsn1Element::Context0Type) { - QDataStream versionStream(elem.value()); - if (!elem.read(versionStream) - || elem.type() != QAsn1Element::IntegerType - || elem.value().isEmpty()) - return false; - - versionString = QByteArray::number(elem.value().at(0) + 1); - if (!elem.read(certStream)) - return false; - } else { - versionString = QByteArray::number(1); - } - - // serial number - if (elem.type() != QAsn1Element::IntegerType) - return false; - serialNumberString = colonSeparatedHex(elem.value()); - - // algorithm ID - if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) - return false; - - // issuer info - if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) - return false; - - QByteArray issuerDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length()); - issuerInfo = elem.toInfo(); - - // validity period - if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) - return false; - - QDataStream validityStream(elem.value()); - if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType)) - return false; - - notValidBefore = elem.toDateTime(); - if (!notValidBefore.isValid()) - return false; - - if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType)) - return false; - - notValidAfter = elem.toDateTime(); - if (!notValidAfter.isValid()) - return false; - - - // subject name - if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) - return false; - - QByteArray subjectDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length()); - subjectInfo = elem.toInfo(); - subjectMatchesIssuer = issuerDer == subjectDer; - - // public key - qint64 keyStart = certStream.device()->pos(); - if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) - return false; - - publicKeyDerData.resize(certStream.device()->pos() - keyStart); - QDataStream keyStream(elem.value()); - if (!elem.read(keyStream) || elem.type() != QAsn1Element::SequenceType) - return false; - - - // key algorithm - if (!elem.read(elem.value()) || elem.type() != QAsn1Element::ObjectIdentifierType) - return false; - - const QByteArray oid = elem.toObjectId(); - if (oid == RSA_ENCRYPTION_OID) - publicKeyAlgorithm = QSsl::Rsa; - else if (oid == DSA_ENCRYPTION_OID) - publicKeyAlgorithm = QSsl::Dsa; - else if (oid == EC_ENCRYPTION_OID) - publicKeyAlgorithm = QSsl::Ec; - else - publicKeyAlgorithm = QSsl::Opaque; - - certStream.device()->seek(keyStart); - certStream.readRawData(publicKeyDerData.data(), publicKeyDerData.size()); - - // extensions - while (elem.read(certStream)) { - if (elem.type() == QAsn1Element::Context3Type) { - if (elem.read(elem.value()) && elem.type() == QAsn1Element::SequenceType) { - QDataStream extStream(elem.value()); - while (elem.read(extStream) && elem.type() == QAsn1Element::SequenceType) { - QSslCertificateExtension extension; - if (!parseExtension(elem.value(), &extension)) - return false; - - if (extension.oid() == QLatin1String("2.5.29.17")) { - // subjectAltName - - // Note, parseExtension() returns true for this extensions, - // but considers it to be unsupported and assignes a useless - // value. OpenSSL also treats this extension as unsupported, - // but properly creates a map with 'name' and 'value' taken - // from the extension. We only support 'email', 'IP' and 'DNS', - // but this is what our subjectAlternativeNames map can contain - // anyway. - QVariantMap extValue; - QAsn1Element sanElem; - if (sanElem.read(extension.value().toByteArray()) && sanElem.type() == QAsn1Element::SequenceType) { - QDataStream nameStream(sanElem.value()); - QAsn1Element nameElem; - while (nameElem.read(nameStream)) { - switch (nameElem.type()) { - case QAsn1Element::Rfc822NameType: - subjectAlternativeNames.insert(QSsl::EmailEntry, nameElem.toString()); - extValue[QStringLiteral("email")] = nameElem.toString(); - break; - case QAsn1Element::DnsNameType: - subjectAlternativeNames.insert(QSsl::DnsEntry, nameElem.toString()); - extValue[QStringLiteral("DNS")] = nameElem.toString(); - break; - case QAsn1Element::IpAddressType: { - QHostAddress ipAddress; - QByteArray ipAddrValue = nameElem.value(); - switch (ipAddrValue.length()) { - case 4: // IPv4 - ipAddress = QHostAddress(qFromBigEndian(*reinterpret_cast(ipAddrValue.data()))); - break; - case 16: // IPv6 - ipAddress = QHostAddress(reinterpret_cast(ipAddrValue.data())); - break; - default: // Unknown IP address format - break; - } - if (!ipAddress.isNull()) { - subjectAlternativeNames.insert(QSsl::IpAddressEntry, ipAddress.toString()); - extValue[QStringLiteral("IP")] = ipAddress.toString(); - } - break; - } - default: - break; - } - } - extension.d->value = extValue; - extension.d->supported = true; - } - } - - extensions << extension; - } - } - } - } - - derData = data.left(dataStream.device()->pos()); - null = false; - return true; -} - -bool QSslCertificatePrivate::parseExtension(const QByteArray &data, QSslCertificateExtension *extension) -{ - bool ok; - bool critical = false; - QAsn1Element oidElem, valElem; - - QDataStream seqStream(data); - - // oid - if (!oidElem.read(seqStream) || oidElem.type() != QAsn1Element::ObjectIdentifierType) - return false; - const QByteArray oid = oidElem.toObjectId(); - - // critical and value - if (!valElem.read(seqStream)) - return false; - if (valElem.type() == QAsn1Element::BooleanType) { - critical = valElem.toBool(&ok); - if (!ok || !valElem.read(seqStream)) - return false; - } - if (valElem.type() != QAsn1Element::OctetStringType) - return false; - - // interpret value - QAsn1Element val; - bool supported = true; - QVariant value; - if (oid == "1.3.6.1.5.5.7.1.1") { - // authorityInfoAccess - if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType) - return false; - QVariantMap result; - const auto elems = val.toList(); - for (const QAsn1Element &el : elems) { - const auto items = el.toList(); - if (items.size() != 2) - return false; - const QString key = QString::fromLatin1(items.at(0).toObjectName()); - switch (items.at(1).type()) { - case QAsn1Element::Rfc822NameType: - case QAsn1Element::DnsNameType: - case QAsn1Element::UniformResourceIdentifierType: - result[key] = items.at(1).toString(); - break; - } - } - value = result; - } else if (oid == "2.5.29.14") { - // subjectKeyIdentifier - if (!val.read(valElem.value()) || val.type() != QAsn1Element::OctetStringType) - return false; - value = colonSeparatedHex(val.value()).toUpper(); - } else if (oid == "2.5.29.19") { - // basicConstraints - if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType) - return false; - - QVariantMap result; - const auto items = val.toList(); - if (items.size() > 0) { - result[QStringLiteral("ca")] = items.at(0).toBool(&ok); - if (!ok) - return false; - } else { - result[QStringLiteral("ca")] = false; - } - if (items.size() > 1) { - result[QStringLiteral("pathLenConstraint")] = items.at(1).toInteger(&ok); - if (!ok) - return false; - } - value = result; - } else if (oid == "2.5.29.35") { - // authorityKeyIdentifier - if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType) - return false; - QVariantMap result; - const auto elems = val.toList(); - for (const QAsn1Element &el : elems) { - if (el.type() == 0x80) { - const QString key = QStringLiteral("keyid"); - result[key] = el.value().toHex(); - } else if (el.type() == 0x82) { - const QString serial = QStringLiteral("serial"); - result[serial] = colonSeparatedHex(el.value()); - } - } - value = result; - } else { - supported = false; - value = valElem.value(); - } - - extension->d->critical = critical; - extension->d->supported = supported; - extension->d->oid = QString::fromLatin1(oid); - extension->d->name = QString::fromLatin1(oidElem.toObjectName()); - extension->d->value = value; - - return true; -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslcertificate_schannel.cpp b/src/network/ssl/qsslcertificate_schannel.cpp deleted file mode 100644 index 5ea713612a..0000000000 --- a/src/network/ssl/qsslcertificate_schannel.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsslcertificate.h" -#include "qsslcertificate_p.h" - -#include - -QT_BEGIN_NAMESPACE - -QSslCertificate QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext) -{ - QByteArray derData = QByteArray((const char *)certificateContext->pbCertEncoded, - certificateContext->cbCertEncoded); - - QSslCertificate certificate(derData, QSsl::Der); - certificate.d->certificateContext = CertDuplicateCertificateContext(certificateContext); - return certificate; -} - -Qt::HANDLE QSslCertificate::handle() const -{ - return Qt::HANDLE(d->certificateContext); -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp index 3747456bf7..9bcec5f7b3 100644 --- a/src/network/ssl/qsslsocket_mac.cpp +++ b/src/network/ssl/qsslsocket_mac.cpp @@ -1382,7 +1382,7 @@ bool QSslSocketBackendPrivate::verifyPeerTrust() // verify certificate chain QCFType certArray = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks); for (const QSslCertificate &cert : qAsConst(configuration.caCertificates)) { - QCFType certData = cert.d->derData.toCFData(); + QCFType certData = cert.toDer().toCFData(); if (QCFType secRef = SecCertificateCreateWithData(nullptr, certData)) CFArrayAppendValue(certArray, secRef); else diff --git a/src/network/ssl/qsslsocket_mac_shared.cpp b/src/network/ssl/qsslsocket_mac_shared.cpp index 09f283775b..c2ade1c702 100644 --- a/src/network/ssl/qsslsocket_mac_shared.cpp +++ b/src/network/ssl/qsslsocket_mac_shared.cpp @@ -43,6 +43,7 @@ #include "qssl_p.h" #include "qsslsocket.h" +#include "qsslsocket_p.h" #ifndef QT_NO_OPENSSL # include "qsslsocket_openssl_p.h" diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index bd3cf4d797..fe59d6dab2 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -65,6 +65,7 @@ #include "qsslellipticcurve.h" #include "qsslpresharedkeyauthenticator.h" #include "qsslpresharedkeyauthenticator_p.h" +#include "qtlsbackend_openssl_p.h" #include "qocspresponse_p.h" #include "qsslkey.h" #include "qtlsbackend_openssl_p.h" @@ -463,14 +464,6 @@ QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(const SSL_CIPHER return ciph; } -QSslErrorEntry QSslErrorEntry::fromStoreContext(X509_STORE_CTX *ctx) -{ - return { - q_X509_STORE_CTX_get_error(ctx), - q_X509_STORE_CTX_get_error_depth(ctx) - }; -} - #if QT_CONFIG(ocsp) QSslError::SslError qt_OCSP_response_status_to_SslError(long code) @@ -612,7 +605,7 @@ int q_X509Callback(int ok, X509_STORE_CTX *ctx) return 0; } - errors->append(QSslErrorEntry::fromStoreContext(ctx)); + errors->append(QSsl::X509CertificateOpenSSL::errorEntryFromStoreContext(ctx)); } // Always return OK to allow verification to continue. We handle the // errors gracefully after collecting all errors, after verification has @@ -1334,55 +1327,6 @@ void QSslSocketBackendPrivate::transmit() } while (ssl && transmitting); } -QSslError _q_OpenSSL_to_QSslError(int errorCode, const QSslCertificate &cert) -{ - QSslError error; - switch (errorCode) { - case X509_V_OK: - // X509_V_OK is also reported if the peer had no certificate. - break; - case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: - error = QSslError(QSslError::UnableToGetIssuerCertificate, cert); break; - case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: - error = QSslError(QSslError::UnableToDecryptCertificateSignature, cert); break; - case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: - error = QSslError(QSslError::UnableToDecodeIssuerPublicKey, cert); break; - case X509_V_ERR_CERT_SIGNATURE_FAILURE: - error = QSslError(QSslError::CertificateSignatureFailed, cert); break; - case X509_V_ERR_CERT_NOT_YET_VALID: - error = QSslError(QSslError::CertificateNotYetValid, cert); break; - case X509_V_ERR_CERT_HAS_EXPIRED: - error = QSslError(QSslError::CertificateExpired, cert); break; - case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: - error = QSslError(QSslError::InvalidNotBeforeField, cert); break; - case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: - error = QSslError(QSslError::InvalidNotAfterField, cert); break; - case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: - error = QSslError(QSslError::SelfSignedCertificate, cert); break; - case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: - error = QSslError(QSslError::SelfSignedCertificateInChain, cert); break; - case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: - error = QSslError(QSslError::UnableToGetLocalIssuerCertificate, cert); break; - case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: - error = QSslError(QSslError::UnableToVerifyFirstCertificate, cert); break; - case X509_V_ERR_CERT_REVOKED: - error = QSslError(QSslError::CertificateRevoked, cert); break; - case X509_V_ERR_INVALID_CA: - error = QSslError(QSslError::InvalidCaCertificate, cert); break; - case X509_V_ERR_PATH_LENGTH_EXCEEDED: - error = QSslError(QSslError::PathLengthExceeded, cert); break; - case X509_V_ERR_INVALID_PURPOSE: - error = QSslError(QSslError::InvalidPurpose, cert); break; - case X509_V_ERR_CERT_UNTRUSTED: - error = QSslError(QSslError::CertificateUntrusted, cert); break; - case X509_V_ERR_CERT_REJECTED: - error = QSslError(QSslError::CertificateRejected, cert); break; - default: - error = QSslError(QSslError::UnspecifiedError, cert); break; - } - return error; -} - QString QSslSocketBackendPrivate::msgErrorsDuringHandshake() { return QSslSocket::tr("Error during SSL handshake: %1") @@ -1422,7 +1366,7 @@ bool QSslSocketBackendPrivate::startHandshake() if (!errorsReportedFromCallback) { for (const auto ¤tError : qAsConst(lastErrors)) { - emit q->peerVerifyError(_q_OpenSSL_to_QSslError(currentError.code, + emit q->peerVerifyError(QSsl::X509CertificateOpenSSL::openSSLErrorToQSslError(currentError.code, configuration.peerCertificateChain.value(currentError.depth))); if (q->state() != QAbstractSocket::ConnectedState) break; @@ -1538,7 +1482,7 @@ bool QSslSocketBackendPrivate::startHandshake() // Translate errors from the error list into QSslErrors. errors.reserve(errors.size() + errorList.size()); for (const auto &error : qAsConst(errorList)) - errors << _q_OpenSSL_to_QSslError(error.code, configuration.peerCertificateChain.value(error.depth)); + errors << QSsl::X509CertificateOpenSSL::openSSLErrorToQSslError(error.code, configuration.peerCertificateChain.value(error.depth)); if (!errors.isEmpty()) { sslErrors = errors; @@ -1589,10 +1533,10 @@ void QSslSocketBackendPrivate::storePeerCertificates() // peer certificate and the chain may be empty if the peer didn't present // any certificate. X509 *x509 = q_SSL_get_peer_certificate(ssl); - configuration.peerCertificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509); + configuration.peerCertificate = QSsl::X509CertificateOpenSSL::certificateFromX509(x509); q_X509_free(x509); if (configuration.peerCertificateChain.isEmpty()) { - configuration.peerCertificateChain = STACKOFX509_to_QSslCertificates(q_SSL_get_peer_cert_chain(ssl)); + configuration.peerCertificateChain = QSsl::X509CertificateOpenSSL::stackOfX509ToQSslCertificates(q_SSL_get_peer_cert_chain(ssl)); if (!configuration.peerCertificate.isNull() && mode == QSslSocket::SslServerMode) configuration.peerCertificateChain.prepend(configuration.peerCertificate); } @@ -1932,7 +1876,7 @@ bool QSslSocketBackendPrivate::checkOcspStatus() matchFound = qt_OCSP_certificate_match(singleResponse, peerX509, issuer); if (matchFound) { if (q_X509_check_issued(issuer, peerX509) == X509_V_OK) { - dResponse->signerCert = QSslCertificatePrivate::QSslCertificate_from_X509(issuer); + dResponse->signerCert = QSsl::X509CertificateOpenSSL::certificateFromX509(issuer); break; } matchFound = false; @@ -2033,10 +1977,10 @@ int QSslSocketBackendPrivate::emitErrorFromCallback(X509_STORE_CTX *ctx) qCWarning(lcSsl, "Could not obtain the certificate (that failed to verify)"); return 0; } - const QSslCertificate certificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509); - const auto errorAndDepth = QSslErrorEntry::fromStoreContext(ctx); - const QSslError tlsError = _q_OpenSSL_to_QSslError(errorAndDepth.code, certificate); + const QSslCertificate certificate = QSsl::X509CertificateOpenSSL::certificateFromX509(x509); + const auto errorAndDepth = QSsl::X509CertificateOpenSSL::errorEntryFromStoreContext(ctx); + const QSslError tlsError = QSsl::X509CertificateOpenSSL::openSSLErrorToQSslError(errorAndDepth.code, certificate); errorsReportedFromCallback = true; handshakeInterrupted = true; @@ -2310,17 +2254,6 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded() #endif } -QList QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509) -{ - ensureInitialized(); - QList certificates; - for (int i = 0; i < q_sk_X509_num(x509); ++i) { - if (X509 *entry = q_sk_X509_value(x509, i)) - certificates << QSslCertificatePrivate::QSslCertificate_from_X509(entry); - } - return certificates; -} - QList QSslSocketBackendPrivate::verify(const QList &certificateChain, const QString &hostName) { @@ -2334,79 +2267,6 @@ QList QSslSocketBackendPrivate::verify(const QList & return QSsl::X509CertificateOpenSSL::verify(caCertificates, certificateChain, hostName); } -bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device, - QSslKey *key, QSslCertificate *cert, - QList *caCertificates, - const QByteArray &passPhrase) -{ - if (!supportsSsl()) - return false; - - // These are required - Q_ASSERT(device); - Q_ASSERT(key); - Q_ASSERT(cert); - - // Read the file into a BIO - QByteArray pkcs12data = device->readAll(); - if (pkcs12data.size() == 0) - return false; - - BIO *bio = q_BIO_new_mem_buf(const_cast(pkcs12data.constData()), pkcs12data.size()); - - // Create the PKCS#12 object - PKCS12 *p12 = q_d2i_PKCS12_bio(bio, nullptr); - if (!p12) { - qCWarning(lcSsl, "Unable to read PKCS#12 structure, %s", - q_ERR_error_string(q_ERR_get_error(), nullptr)); - q_BIO_free(bio); - return false; - } - - // Extract the data - EVP_PKEY *pkey = nullptr; - X509 *x509; - STACK_OF(X509) *ca = nullptr; - - if (!q_PKCS12_parse(p12, passPhrase.constData(), &pkey, &x509, &ca)) { - qCWarning(lcSsl, "Unable to parse PKCS#12 structure, %s", - q_ERR_error_string(q_ERR_get_error(), nullptr)); - q_PKCS12_free(p12); - q_BIO_free(bio); - return false; - } - - // Convert to Qt types - auto *tlsKey = QTlsBackend::backend(*key); - if (!tlsKey || !tlsKey->fromEVP_PKEY(pkey)) { - qCWarning(lcSsl, "Unable to convert private key"); - q_OPENSSL_sk_pop_free(reinterpret_cast(ca), - reinterpret_cast(q_X509_free)); - q_X509_free(x509); - q_EVP_PKEY_free(pkey); - q_PKCS12_free(p12); - q_BIO_free(bio); - - return false; - } - - *cert = QSslCertificatePrivate::QSslCertificate_from_X509(x509); - - if (caCertificates) - *caCertificates = QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(ca); - - // Clean up - q_OPENSSL_sk_pop_free(reinterpret_cast(ca), - reinterpret_cast(q_X509_free)); - - q_X509_free(x509); - q_EVP_PKEY_free(pkey); - q_PKCS12_free(p12); - q_BIO_free(bio); - - return true; -} - void QSslSocketPrivate::registerAdHocFactory() { // TLSTODO: this is a temporary solution, waiting for diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h index b45bbb0a30..1e79468958 100644 --- a/src/network/ssl/qsslsocket_openssl_p.h +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -110,9 +110,8 @@ QT_BEGIN_NAMESPACE struct QSslErrorEntry { int code; int depth; - - static QSslErrorEntry fromStoreContext(X509_STORE_CTX *ctx); }; + Q_DECLARE_TYPEINFO(QSslErrorEntry, Q_PRIMITIVE_TYPE); class QSslSocketBackendPrivate : public QSslSocketPrivate @@ -179,16 +178,11 @@ public: Q_AUTOTEST_EXPORT static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions); static QSslCipher QSslCipher_from_SSL_CIPHER(const SSL_CIPHER *cipher); - static QList STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509); static QList verify(const QList &certificateChain, const QString &hostName); static QList verify(const QList &cas, const QList &certificateChain, const QString &hostName); static QString getErrorsFromOpenSsl(); static void logAndClearErrorQueue(); - static bool importPkcs12(QIODevice *device, - QSslKey *key, QSslCertificate *cert, - QList *caCertificates, - const QByteArray &passPhrase); static QString msgErrorsDuringHandshake(); }; diff --git a/src/network/ssl/qsslsocket_schannel.cpp b/src/network/ssl/qsslsocket_schannel.cpp index 608032d3e2..982db74e11 100644 --- a/src/network/ssl/qsslsocket_schannel.cpp +++ b/src/network/ssl/qsslsocket_schannel.cpp @@ -630,7 +630,7 @@ QList QSslSocketPrivate::systemCaCertificates() PCCERT_CONTEXT pc = nullptr; while ((pc = CertFindCertificateInStore(hSystemStore.get(), X509_ASN_ENCODING, 0, CERT_FIND_ANY, nullptr, pc))) { - systemCerts.append(QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(pc)); + systemCerts.append(QSsl::X509CertificateSchannel::QSslCertificate_from_CERT_CONTEXT(pc)); } } return systemCerts; @@ -1991,7 +1991,7 @@ bool QSslSocketBackendPrivate::verifyCertContext(CERT_CONTEXT *certContext) return QSslCertificate(); const CERT_CONTEXT *certContext = element->pCertContext; - return QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(certContext); + return QSsl::X509CertificateSchannel::QSslCertificate_from_CERT_CONTEXT(certContext); }; // Pick a chain to use as the certificate chain, if multiple are available: diff --git a/src/network/ssl/qtlsbackend.cpp b/src/network/ssl/qtlsbackend.cpp index 1df462e362..dcffb0afd1 100644 --- a/src/network/ssl/qtlsbackend.cpp +++ b/src/network/ssl/qtlsbackend.cpp @@ -38,7 +38,15 @@ ****************************************************************************/ #include "qtlsbackend_p.h" + +#if QT_CONFIG(ssl) #include "qsslsocket_p.h" +#include "qsslkey_p.h" +#include "qsslkey.h" +#else +#include "qtlsbackend_cert_p.h" +#endif + #include "qssl_p.h" #include @@ -93,8 +101,13 @@ public: while (loader->instance(index)) ++index; - // TLSTODO: obviously, this one should go away: + // TLSTODO: obviously, these two below should + // disappear as soon as plugins are in place. +#if QT_CONFIG(ssl) QSslSocketPrivate::registerAdHocFactory(); +#else + static QTlsBackendCertOnly certGenerator; +#endif // QT_CONFIG(ssl) return loaded = true; } @@ -180,6 +193,12 @@ QByteArray TlsKey::pemFooter() const X509Certificate::~X509Certificate() = default; +TlsKey *X509Certificate::publicKey() const +{ + // 'no-ssl' build has no key support either. + return nullptr; +} + } // namespace QSsl const QString QTlsBackend::builtinBackendNames[] = { @@ -286,6 +305,9 @@ QString QTlsBackend::defaultBackendName() if (names.contains(name)) return name; + if (names.size()) + return names[0]; + return {}; } @@ -301,6 +323,15 @@ QTlsBackend *QTlsBackend::findBackend(const QString &backendName) return nullptr; } +QTlsBackend *QTlsBackend::activeOrAnyBackend() +{ +#if QT_CONFIG(ssl) + return QSslSocketPrivate::tlsBackendInUse(); +#else + return findBackend(defaultBackendName()); +#endif // QT_CONFIG(ssl) +} + QList QTlsBackend::supportedProtocols(const QString &backendName) { if (!backends()) @@ -334,4 +365,14 @@ QList QTlsBackend::implementedClasses(const QString &bac return {}; } +void QTlsBackend::resetBackend(QSslKey &key, QSsl::TlsKey *keyBackend) +{ +#if QT_CONFIG(ssl) + key.d->keyBackend.reset(keyBackend); +#else + Q_UNUSED(key); + Q_UNUSED(keyBackend); +#endif // QT_CONFIG(ssl) +} + QT_END_NAMESPACE diff --git a/src/network/ssl/qtlsbackend_cert.cpp b/src/network/ssl/qtlsbackend_cert.cpp new file mode 100644 index 0000000000..03451b2ad1 --- /dev/null +++ b/src/network/ssl/qtlsbackend_cert.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtlsbackend_cert_p.h" + +#ifdef QT_NO_SSL + +#include "qx509_generic_p.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.cert-only"); + +QString QTlsBackendCertOnly::backendName() const +{ + return QStringLiteral("cert-only"); +} + + +QList QTlsBackendCertOnly::supportedProtocols() const +{ + return {}; +} + +QList QTlsBackendCertOnly::supportedFeatures() const +{ + return {}; +} + +QList QTlsBackendCertOnly::implementedClasses() const +{ + QList classes; + classes << QSsl::ImplementedClass::Certificate; + + return classes; +} + +QSsl::X509Certificate *QTlsBackendCertOnly::createCertificate() const +{ + return new QSsl::X509CertificateGeneric; +} + +QSsl::X509PemReaderPtr QTlsBackendCertOnly::X509PemReader() const +{ + return QSsl::X509CertificateGeneric::certificatesFromPem; +} + +QSsl::X509DerReaderPtr QTlsBackendCertOnly::X509DerReader() const +{ + return QSsl::X509CertificateGeneric::certificatesFromDer; +} + +QT_END_NAMESPACE + +#endif // QT_NO_SSL + diff --git a/src/network/ssl/qtlsbackend_cert_p.h b/src/network/ssl/qtlsbackend_cert_p.h new file mode 100644 index 0000000000..86c93dc310 --- /dev/null +++ b/src/network/ssl/qtlsbackend_cert_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTLSBACKEND_CERT_P_H +#define QTLSBACKEND_CERT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include "qtlsbackend_p.h" + +#include + + +QT_BEGIN_NAMESPACE + +class QTlsBackendCertOnly final : public QTlsBackend +{ +public: +private: + QString backendName() const override; + + QList supportedProtocols() const override; + QList supportedFeatures() const override; + QList implementedClasses() const override; + + QSsl::X509Certificate *createCertificate() const override; + QSsl::X509PemReaderPtr X509PemReader() const override; + QSsl::X509DerReaderPtr X509DerReader() const override; +}; + +QT_END_NAMESPACE + +#endif // QTLSBACKEND_CERT_P_H diff --git a/src/network/ssl/qtlsbackend_p.h b/src/network/ssl/qtlsbackend_p.h index c1d5137ca3..d560288b5e 100644 --- a/src/network/ssl/qtlsbackend_p.h +++ b/src/network/ssl/qtlsbackend_p.h @@ -51,10 +51,9 @@ // We mean it. // -#include +#include -#include -#include +#include "qssl_p.h" #include #include @@ -76,6 +75,7 @@ QT_BEGIN_NAMESPACE class QByteArray; class QIODevice; +class QSslKey; namespace QSsl { @@ -147,7 +147,8 @@ public: virtual QMultiMap subjectAlternativeNames() const = 0; virtual QDateTime effectiveDate() const = 0; virtual QDateTime expiryDate() const = 0; - virtual TlsKey *publicKey() const = 0; + + virtual TlsKey *publicKey() const; // Extensions. Plugins do not expose internal representation // and cannot rely on QSslCertificate's internals. @@ -220,6 +221,7 @@ public: static QList availableBackendNames(); static QString defaultBackendName(); static QTlsBackend *findBackend(const QString &backendName); + static QTlsBackend *activeOrAnyBackend(); static QList supportedProtocols(const QString &backendName); static QList supportedFeatures(const QString &backendName); @@ -238,6 +240,8 @@ public: return static_cast(o.backendImplementation()); } + static void resetBackend(QSslKey &key, QSsl::TlsKey *keyBackend); + Q_DISABLE_COPY_MOVE(QTlsBackend) }; diff --git a/src/network/ssl/qtlskey_generic_p.h b/src/network/ssl/qtlskey_generic_p.h index acda061d08..6c2666bcf0 100644 --- a/src/network/ssl/qtlskey_generic_p.h +++ b/src/network/ssl/qtlskey_generic_p.h @@ -101,28 +101,6 @@ public: return pkcs8; } - QByteArray decrypt(Cipher cipher, const QByteArray &data, - const QByteArray &key, const QByteArray &iv) const override - { - // The real implementation is to be provided by Schannel or SecureTransport. - Q_UNUSED(cipher) - Q_UNUSED(data) - Q_UNUSED(key) - Q_UNUSED(iv) - - return {}; - } - QByteArray encrypt(Cipher cipher, const QByteArray &data, - const QByteArray &key, const QByteArray &iv) const override - { - // The real implementation is to be provided by Schannel or SecureTransport. - Q_UNUSED(cipher) - Q_UNUSED(data) - Q_UNUSED(key) - Q_UNUSED(iv) - - return {}; - } private: QByteArray decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase); diff --git a/src/network/ssl/qx509_generic.cpp b/src/network/ssl/qx509_generic.cpp index 9d982d31f5..535fbe5fd2 100644 --- a/src/network/ssl/qx509_generic.cpp +++ b/src/network/ssl/qx509_generic.cpp @@ -37,7 +37,6 @@ ** ****************************************************************************/ -#include "qtlskey_generic_p.h" #include "qx509_generic_p.h" #include "qasn1element_p.h" diff --git a/src/network/ssl/qx509_schannel.cpp b/src/network/ssl/qx509_schannel.cpp index 252bb470cb..6019fccb23 100644 --- a/src/network/ssl/qx509_schannel.cpp +++ b/src/network/ssl/qx509_schannel.cpp @@ -46,6 +46,14 @@ QT_BEGIN_NAMESPACE namespace QSsl { +X509CertificateSchannel::X509CertificateSchannel() = default; + +X509CertificateSchannel::~X509CertificateSchannel() +{ + if (certificateContext) + CertFreeCertificateContext(certificateContext); +} + TlsKey *X509CertificateSchannel::publicKey() const { auto key = std::make_unique(PublicKey); @@ -55,6 +63,23 @@ TlsKey *X509CertificateSchannel::publicKey() const return key.release(); } +Qt::HANDLE X509CertificateSchannel::handle() const +{ + return Qt::HANDLE(certificateContext); +} + +QSslCertificate X509CertificateSchannel::QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext) +{ + QByteArray derData = QByteArray((const char *)certificateContext->pbCertEncoded, + certificateContext->cbCertEncoded); + QSslCertificate certificate(derData, QSsl::Der); + + auto *certBackend = QTlsBackend::backend(certificate); + Q_ASSERT(certBackend); + certBackend->certificateContext = CertDuplicateCertificateContext(certificateContext); + return certificate; +} + } // namespace QSsl. QT_END_NAMESPACE diff --git a/src/network/ssl/qx509_schannel_p.h b/src/network/ssl/qx509_schannel_p.h index 5408ea5a8e..40fb292acf 100644 --- a/src/network/ssl/qx509_schannel_p.h +++ b/src/network/ssl/qx509_schannel_p.h @@ -57,6 +57,9 @@ #include +#include +#include + QT_BEGIN_NAMESPACE namespace QSsl { @@ -64,7 +67,17 @@ namespace QSsl { class X509CertificateSchannel final : public X509CertificateGeneric { public: + X509CertificateSchannel(); + ~X509CertificateSchannel(); + TlsKey *publicKey() const override; + 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); }; } // namespace QSsl. -- cgit v1.2.3