diff options
Diffstat (limited to 'src/network/ssl/qx509_openssl.cpp')
-rw-r--r-- | src/network/ssl/qx509_openssl.cpp | 927 |
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 |