summaryrefslogtreecommitdiffstats
path: root/src/network/ssl/qx509_openssl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/ssl/qx509_openssl.cpp')
-rw-r--r--src/network/ssl/qx509_openssl.cpp927
1 files changed, 0 insertions, 927 deletions
diff --git a/src/network/ssl/qx509_openssl.cpp b/src/network/ssl/qx509_openssl.cpp
deleted file mode 100644
index 376b24ab37..0000000000
--- a/src/network/ssl/qx509_openssl.cpp
+++ /dev/null
@@ -1,927 +0,0 @@
-/****************************************************************************
-**
-** 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_openssl_p.h"
-#include "qtlskey_openssl_p.h"
-#include "qx509_openssl_p.h"
-
-#include "qsslsocket_openssl_symbols_p.h"
-
-#include "qsslsocket.h"
-
-#include <QtNetwork/qhostaddress.h>
-
-#include <QtCore/qvarlengtharray.h>
-#include <QtCore/qscopeguard.h>
-#include <QtCore/qdatetime.h>
-#include <QtCore/qiodevice.h>
-#include <QtCore/qendian.h>
-#include <QtCore/qhash.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace QSsl {
-
-namespace {
-
-// TLSTODO: These helper functions below were static member-functions of
-// QSslCertificatePrivate, if-defed with !QT_NO_OPENSSL, no need
-// for them to be exposed this way anymore. Remove this comment when
-// plugins are ready.
-QByteArray asn1ObjectId(ASN1_OBJECT *object)
-{
- if (!object)
- return {};
-
- 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 asn1ObjectName(ASN1_OBJECT *object)
-{
- if (!object)
- return {};
-
- const int nid = q_OBJ_obj2nid(object);
- if (nid != NID_undef)
- return QByteArray(q_OBJ_nid2sn(nid));
-
- return asn1ObjectId(object);
-}
-
-QMultiMap<QByteArray, QString> mapFromX509Name(X509_NAME *name)
-{
- if (!name)
- return {};
-
- QMultiMap<QByteArray, QString> 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 = 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));
- q_CRYPTO_free(data, nullptr, 0);
- }
-
- return info;
-}
-
-#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----"
-#define ENDCERTSTRING "-----END CERTIFICATE-----"
-
-QByteArray x509ToQByteArray(X509 *x509, QSsl::EncodingFormat format)
-{
- Q_ASSERT(x509);
-
- // Use i2d_X509 to convert the X509 to an array.
- const int length = q_i2d_X509(x509, nullptr);
- if (length <= 0) {
- QTlsBackendOpenSSL::logAndClearErrorQueue();
- return {};
- }
-
- 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 x509ToText(X509 *x509)
-{
- Q_ASSERT(x509);
-
- QByteArray result;
- BIO *bio = q_BIO_new(q_BIO_s_mem());
- if (!bio)
- return QString();
- const auto bioRaii = qScopeGuard([bio]{q_BIO_free(bio);});
-
- q_X509_print(bio, x509);
-
- QVarLengthArray<char, 16384> data;
- int count = q_BIO_read(bio, data.data(), 16384);
- if ( count > 0 )
- result = QByteArray( data.data(), count );
-
- return QString::fromLatin1(result);
-}
-
-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<X509V3_EXT_METHOD *>(q_X509V3_EXT_get(ext));
- if (!meth) {
- ASN1_OCTET_STRING *value = q_X509_EXTENSION_get_data(ext);
- Q_ASSERT(value);
- QByteArray result( reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(value)),
- q_ASN1_STRING_length(value));
- return result;
- }
-
- 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.
- */
-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<BASIC_CONSTRAINTS *>(q_X509V3_EXT_d2i(ext));
- if (!basic)
- return {};
- 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<AUTHORITY_INFO_ACCESS *>(q_X509V3_EXT_d2i(ext));
- if (!info)
- return {};
- 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<const char *>(q_ASN1_STRING_get0_data(name->d.uniformResourceIdentifier));
- const QString uri = QString::fromUtf8(uriStr, len);
-
- result[QString::fromUtf8(asn1ObjectName(ad->method))] = uri;
- } else {
- qCWarning(lcTlsBackend) << "Strange location type" << name->type;
- }
- }
-
- q_OPENSSL_sk_pop_free((OPENSSL_STACK*)info, reinterpret_cast<void(*)(void *)>(q_OPENSSL_sk_free));
- return result;
- }
- break;
- case NID_subject_key_identifier:
- {
- void *ext_internal = q_X509V3_EXT_d2i(ext);
- if (!ext_internal)
- return {};
- // 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<X509V3_EXT_METHOD *>(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<AUTHORITY_KEYID *>(q_X509V3_EXT_d2i(ext));
- if (!auth_key)
- return {};
- QVariantMap result;
-
- // keyid
- if (auth_key->keyid) {
- QByteArray keyid(reinterpret_cast<const char *>(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 {};
-}
-
-} // Unnamed namespace
-
-extern "C" int qt_X509Callback(int ok, X509_STORE_CTX *ctx)
-{
- if (!ok) {
- // Store the error and at which depth the error was detected.
- using ErrorListPtr = QList<QSslErrorEntry> *;
- ErrorListPtr errors = nullptr;
-
- // Error list is attached to either 'SSL' or 'X509_STORE'.
- if (X509_STORE *store = q_X509_STORE_CTX_get0_store(ctx)) // We try store first:
- errors = ErrorListPtr(q_X509_STORE_get_ex_data(store, 0));
-
- if (!errors) {
- // Not found on store? Try SSL and its external data then. According to the OpenSSL's
- // documentation:
- //
- // "Whenever a X509_STORE_CTX object is created for the verification of the
- // peer's certificate during a handshake, a pointer to the SSL object is
- // stored into the X509_STORE_CTX object to identify the connection affected.
- // To retrieve this pointer the X509_STORE_CTX_get_ex_data() function can be
- // used with the correct index."
-
- // TLSTODO: verification callback has to change as soon as TlsCryptographer is in place.
- // This is a temporary solution for now to ease the transition.
- const auto offset = QSslSocketBackendPrivate::s_indexForSSLExtraData
- + QSslSocketBackendPrivate::errorOffsetInExData;
- if (SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx())))
- errors = ErrorListPtr(q_SSL_get_ex_data(ssl, offset));
- }
-
- if (!errors) {
- qCWarning(lcTlsBackend, "Neither X509_STORE, nor SSL contains error list, verification failed");
- return 0;
- }
-
- errors->append(X509CertificateOpenSSL::errorEntryFromStoreContext(ctx));
- }
- // Always return OK to allow verification to continue. We handle the
- // errors gracefully after collecting all errors, after verification has
- // completed.
- return 1;
-}
-
-X509CertificateOpenSSL::X509CertificateOpenSSL() = default;
-
-X509CertificateOpenSSL::~X509CertificateOpenSSL()
-{
- if (x509)
- q_X509_free(x509);
-}
-
-bool X509CertificateOpenSSL::isEqual(const X509Certificate &rhs) const
-{
- //TLSTODO: to make it safe I'll check the backend type later.
- const auto &other = static_cast<const X509CertificateOpenSSL &>(rhs);
- if (x509 && other.x509) {
- const int ret = q_X509_cmp(x509, other.x509);
- if (ret >= -1 && ret <= 1)
- return ret == 0;
- QTlsBackendOpenSSL::logAndClearErrorQueue();
- }
-
- return false;
-}
-
-bool X509CertificateOpenSSL::isSelfSigned() const
-{
- if (!x509)
- return false;
-
- return q_X509_check_issued(x509, x509) == X509_V_OK;
-}
-
-QMultiMap<QSsl::AlternativeNameEntryType, QString>
-X509CertificateOpenSSL::subjectAlternativeNames() const
-{
- QMultiMap<QSsl::AlternativeNameEntryType, QString> result;
-
- if (!x509)
- return result;
-
- auto *altNames = static_cast<STACK_OF(GENERAL_NAME) *>(q_X509_get_ext_d2i(x509, NID_subject_alt_name,
- nullptr, nullptr));
- if (!altNames)
- return result;
-
- auto altName = [](ASN1_IA5STRING *ia5, int len) {
- const char *altNameStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(ia5));
- return QString::fromLatin1(altNameStr, len);
- };
-
- 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;
-
- const 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<quint32 *>(genName->d.iPAddress->data)));
- break;
- case 16: // IPv6
- ipAddress = QHostAddress(reinterpret_cast<quint8 *>(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<void(*)(void*)>(q_GENERAL_NAME_free));
-
- return result;
-}
-
-TlsKey *X509CertificateOpenSSL::publicKey() const
-{
- if (!x509)
- return {};
-
- return TlsKeyOpenSSL::publicKeyFromX509(x509);
-}
-
-QByteArray X509CertificateOpenSSL::toPem() const
-{
- if (!x509)
- return {};
-
- return x509ToQByteArray(x509, QSsl::Pem);
-}
-
-QByteArray X509CertificateOpenSSL::toDer() const
-{
- if (!x509)
- return {};
-
- return x509ToQByteArray(x509, QSsl::Der);
-
-}
-QString X509CertificateOpenSSL::toText() const
-{
- if (!x509)
- return {};
-
- return x509ToText(x509);
-}
-
-Qt::HANDLE X509CertificateOpenSSL::handle() const
-{
- return Qt::HANDLE(x509);
-}
-
-size_t X509CertificateOpenSSL::hash(size_t seed) const noexcept
-{
- if (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;
-}
-
-QSslCertificate X509CertificateOpenSSL::certificateFromX509(X509 *x509)
-{
- QSslCertificate certificate;
-
- auto *backend = QTlsBackend::backend<X509CertificateOpenSSL>(certificate);
- if (!backend || !x509)
- return certificate;
-
- ASN1_TIME *nbef = q_X509_getm_notBefore(x509);
- if (nbef)
- backend->notValidBefore = q_getTimeFromASN1(nbef);
-
- ASN1_TIME *naft = q_X509_getm_notAfter(x509);
- if (naft)
- backend->notValidAfter = q_getTimeFromASN1(naft);
-
- backend->null = false;
- backend->x509 = q_X509_dup(x509);
-
- backend->issuerInfoEntries = mapFromX509Name(q_X509_get_issuer_name(x509));
- backend->subjectInfoEntries = mapFromX509Name(q_X509_get_subject_name(x509));
- backend->versionString = QByteArray::number(qlonglong(q_X509_get_version(x509)) + 1);
-
- if (ASN1_INTEGER *serialNumber = q_X509_get_serialNumber(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);
- backend->serialNumberString = hexString;
- }
-
- backend->parseExtensions();
-
- return certificate;
-}
-
-QList<QSslCertificate> X509CertificateOpenSSL::stackOfX509ToQSslCertificates(STACK_OF(X509) *x509)
-{
- if (!x509)
- return {};
-
- QList<QSslCertificate> certificates;
- for (int i = 0; i < q_sk_X509_num(x509); ++i) {
- if (X509 *entry = q_sk_X509_value(x509, i))
- certificates << certificateFromX509(entry);
- }
-
- return certificates;
-}
-
-QSslErrorEntry X509CertificateOpenSSL::errorEntryFromStoreContext(X509_STORE_CTX *ctx)
-{
- Q_ASSERT(ctx);
-
- return {q_X509_STORE_CTX_get_error(ctx), q_X509_STORE_CTX_get_error_depth(ctx)};
-}
-
-QList<QSslError> X509CertificateOpenSSL::verify(const QList<QSslCertificate> &chain,
- const QString &hostName)
-{
- // This was previously QSslSocketPrivate::verify().
- auto roots = QSslConfiguration::defaultConfiguration().caCertificates();
-#ifndef Q_OS_WIN
- // On Windows, system CA certificates are already set as default ones.
- // No need to add them again (and again) and also, if the default configuration
- // has its own set of CAs, this probably should not be amended by the ones
- // from the 'ROOT' store, since it's not what an application chose to trust.
- if (QSslSocketPrivate::s_loadRootCertsOnDemand)
- roots.append(QSslSocketPrivate::systemCaCertificates());
-#endif // Q_OS_WIN
- return verify(roots, chain, hostName);
-}
-
-QList<QSslError> X509CertificateOpenSSL::verify(const QList<QSslCertificate> &caCertificates,
- const QList<QSslCertificate> &certificateChain,
- const QString &hostName)
-{
- // This was previously QSslSocketPrivate::verify().
- if (certificateChain.count() <= 0)
- return {QSslError(QSslError::UnspecifiedError)};
-
- QList<QSslError> errors;
- X509_STORE *certStore = q_X509_STORE_new();
- if (!certStore) {
- qCWarning(lcTlsBackend) << "Unable to create certificate store";
- errors << QSslError(QSslError::UnspecifiedError);
- return errors;
- }
- const std::unique_ptr<X509_STORE, decltype(&q_X509_STORE_free)> storeGuard(certStore, q_X509_STORE_free);
-
- const QDateTime now = QDateTime::currentDateTimeUtc();
- for (const QSslCertificate &caCertificate : caCertificates) {
- // From https://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html:
- //
- // If several CA certificates matching the name, key identifier, and
- // serial number condition are available, only the first one will be
- // examined. This may lead to unexpected results if the same CA
- // certificate is available with different expiration dates. If a
- // ``certificate expired'' verification error occurs, no other
- // certificate will be searched. Make sure to not have expired
- // certificates mixed with valid ones.
- //
- // See also: QSslContext::fromConfiguration()
- if (caCertificate.expiryDate() >= now) {
- q_X509_STORE_add_cert(certStore, reinterpret_cast<X509 *>(caCertificate.handle()));
- }
- }
-
- QList<QSslErrorEntry> lastErrors;
- if (!q_X509_STORE_set_ex_data(certStore, 0, &lastErrors)) {
- qCWarning(lcTlsBackend) << "Unable to attach external data (error list) to a store";
- errors << QSslError(QSslError::UnspecifiedError);
- return errors;
- }
-
- // Register a custom callback to get all verification errors.
- q_X509_STORE_set_verify_cb(certStore, qt_X509Callback);
-
- // Build the chain of intermediate certificates
- STACK_OF(X509) *intermediates = nullptr;
- if (certificateChain.length() > 1) {
- intermediates = (STACK_OF(X509) *) q_OPENSSL_sk_new_null();
-
- if (!intermediates) {
- errors << QSslError(QSslError::UnspecifiedError);
- return errors;
- }
-
- bool first = true;
- for (const QSslCertificate &cert : certificateChain) {
- if (first) {
- first = false;
- continue;
- }
-
- q_OPENSSL_sk_push((OPENSSL_STACK *)intermediates, reinterpret_cast<X509 *>(cert.handle()));
- }
- }
-
- X509_STORE_CTX *storeContext = q_X509_STORE_CTX_new();
- if (!storeContext) {
- errors << QSslError(QSslError::UnspecifiedError);
- return errors;
- }
- std::unique_ptr<X509_STORE_CTX, decltype(&q_X509_STORE_CTX_free)> ctxGuard(storeContext, q_X509_STORE_CTX_free);
-
- if (!q_X509_STORE_CTX_init(storeContext, certStore, reinterpret_cast<X509 *>(certificateChain[0].handle()), intermediates)) {
- errors << QSslError(QSslError::UnspecifiedError);
- return errors;
- }
-
- // Now we can actually perform the verification of the chain we have built.
- // We ignore the result of this function since we process errors via the
- // callback.
- (void) q_X509_verify_cert(storeContext);
- ctxGuard.reset();
- q_OPENSSL_sk_free((OPENSSL_STACK *)intermediates);
-
- // Now process the errors
-
- if (certificateChain[0].isBlacklisted())
- errors << QSslError(QSslError::CertificateBlacklisted, certificateChain[0]);
-
- // Check the certificate name against the hostname if one was specified
- if (!hostName.isEmpty() && !QSslSocketPrivate::isMatchingHostname(certificateChain[0], hostName)) {
- // No matches in common names or alternate names.
- QSslError error(QSslError::HostNameMismatch, certificateChain[0]);
- errors << error;
- }
-
- // Translate errors from the error list into QSslErrors.
- errors.reserve(errors.size() + lastErrors.size());
- for (const auto &error : qAsConst(lastErrors))
- errors << openSSLErrorToQSslError(error.code, certificateChain.value(error.depth));
-
- return errors;
-}
-
-QList<QSslCertificate> X509CertificateOpenSSL::certificatesFromPem(const QByteArray &pem, int count)
-{
- QList<QSslCertificate> 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));
- const unsigned char *data = (const unsigned char *)decoded.data();
-
- if (X509 *x509 = q_d2i_X509(nullptr, &data, decoded.size())) {
- certificates << certificateFromX509(x509);
- q_X509_free(x509);
- }
- }
-
- return certificates;
-}
-
-QList<QSslCertificate> X509CertificateOpenSSL::certificatesFromDer(const QByteArray &der, int count)
-{
- QList<QSslCertificate> certificates;
-
- 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 << certificateFromX509(x509);
- q_X509_free(x509);
- } else {
- break;
- }
- size -= ((const char *)data - der.data());
- }
-
- return certificates;
-}
-
-bool X509CertificateOpenSSL::importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert,
- QList<QSslCertificate> *caCertificates,
- const QByteArray &passPhrase)
-{
- // 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<char *>(pkcs12data.constData()), pkcs12data.size());
- if (!bio) {
- qCWarning(lcTlsBackend, "BIO_new_mem_buf returned null");
- return false;
- }
- const auto bioRaii = qScopeGuard([bio]{q_BIO_free(bio);});
-
- // Create the PKCS#12 object
- PKCS12 *p12 = q_d2i_PKCS12_bio(bio, nullptr);
- if (!p12) {
- qCWarning(lcTlsBackend, "Unable to read PKCS#12 structure, %s",
- q_ERR_error_string(q_ERR_get_error(), nullptr));
- return false;
- }
- const auto p12Raii = qScopeGuard([p12]{q_PKCS12_free(p12);});
-
- // Extract the data
- EVP_PKEY *pkey = nullptr;
- X509 *x509 = nullptr;
- STACK_OF(X509) *ca = nullptr;
-
- if (!q_PKCS12_parse(p12, passPhrase.constData(), &pkey, &x509, &ca)) {
- qCWarning(lcTlsBackend, "Unable to parse PKCS#12 structure, %s",
- q_ERR_error_string(q_ERR_get_error(), nullptr));
- return false;
- }
-
- const auto x509Raii = qScopeGuard([x509]{q_X509_free(x509);});
- const auto keyRaii = qScopeGuard([pkey]{q_EVP_PKEY_free(pkey);});
- const auto caRaii = qScopeGuard([ca] {
- q_OPENSSL_sk_pop_free(reinterpret_cast<OPENSSL_STACK *>(ca),
- reinterpret_cast<void (*)(void *)>(q_X509_free));
- });
-
- // Convert to Qt types
- auto *tlsKey = QTlsBackend::backend<TlsKeyOpenSSL>(*key);
- if (!tlsKey || !tlsKey->fromEVP_PKEY(pkey)) {
- qCWarning(lcTlsBackend, "Unable to convert private key");
- return false;
- }
-
- *cert = certificateFromX509(x509);
-
- if (caCertificates)
- *caCertificates = stackOfX509ToQSslCertificates(ca);
-
- return true;
-}
-
-QSslError X509CertificateOpenSSL::openSSLErrorToQSslError(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;
-}
-
-void X509CertificateOpenSSL::parseExtensions()
-{
- extensions.clear();
-
- if (!x509)
- return;
-
- int count = q_X509_get_ext_count(x509);
- if (count <= 0)
- return;
-
- extensions.reserve(count);
-
- for (int i = 0; i < count; i++) {
- X509_EXTENSION *ext = q_X509_get_ext(x509, i);
- if (!ext) {
- qCWarning(lcTlsBackend) << "Invalid (nullptr) extension at index" << i;
- continue;
- }
-
- extensions << convertExtension(ext);
- }
-
- // Converting an extension may result in an error(s), clean them up:
- QTlsBackendOpenSSL::clearErrorQueue();
-}
-
-X509CertificateBase::X509CertificateExtension X509CertificateOpenSSL::convertExtension(X509_EXTENSION *ext)
-{
- Q_ASSERT(ext);
-
- X509CertificateExtension result;
-
- ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(ext);
- if (!obj)
- return result;
-
- result.oid = QString::fromUtf8(asn1ObjectId(obj));
- result.name = QString::fromUtf8(asn1ObjectName(obj));
-
- result.critical = bool(q_X509_EXTENSION_get_critical(ext));
-
- // Lets see if we have custom support for this one
- QVariant extensionValue = x509ExtensionToValue(ext);
- if (extensionValue.isValid()) {
- result.value = extensionValue;
- result.supported = true;
- return result;
- }
-
- extensionValue = x509UnknownExtensionToValue(ext);
- if (extensionValue.isValid())
- result.value = extensionValue;
-
- result.supported = false;
-
- return result;
-}
-
-} // namespace QSsl
-
-QT_END_NAMESPACE