diff options
author | Timur Pocheptsov <timur.pocheptsov@qt.io> | 2021-03-25 12:41:08 +0100 |
---|---|---|
committer | Timur Pocheptsov <timur.pocheptsov@qt.io> | 2021-04-22 22:51:54 +0200 |
commit | d385158d5213ef568b7629e2aa4a818016bbffac (patch) | |
tree | 2c111b462fe39dffacb3c7f5cdd8db269f87ed6c /src/network/ssl | |
parent | 6b1a7341fed4b9456ea6bfa2de7412d45ef56c65 (diff) |
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Diffstat (limited to 'src/network/ssl')
58 files changed, 42 insertions, 18952 deletions
diff --git a/src/network/ssl/qasn1element.cpp b/src/network/ssl/qasn1element.cpp deleted file mode 100644 index 3df76c3774..0000000000 --- a/src/network/ssl/qasn1element.cpp +++ /dev/null @@ -1,400 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org> -** 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 "qasn1element_p.h" - -#include <QtCore/qdatastream.h> -#include <QtCore/qdatetime.h> -#include <QtCore/qlist.h> -#include <QDebug> - -#include <limits> - -QT_BEGIN_NAMESPACE - -typedef QMap<QByteArray, QByteArray> OidNameMap; -static OidNameMap createOidMap() -{ - OidNameMap oids; - // used by unit tests - oids.insert(oids.cend(), QByteArrayLiteral("0.9.2342.19200300.100.1.5"), QByteArrayLiteral("favouriteDrink")); - oids.insert(oids.cend(), QByteArrayLiteral("1.2.840.113549.1.9.1"), QByteArrayLiteral("emailAddress")); - oids.insert(oids.cend(), QByteArrayLiteral("1.3.6.1.5.5.7.1.1"), QByteArrayLiteral("authorityInfoAccess")); - oids.insert(oids.cend(), QByteArrayLiteral("1.3.6.1.5.5.7.48.1"), QByteArrayLiteral("OCSP")); - oids.insert(oids.cend(), QByteArrayLiteral("1.3.6.1.5.5.7.48.2"), QByteArrayLiteral("caIssuers")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.14"), QByteArrayLiteral("subjectKeyIdentifier")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.15"), QByteArrayLiteral("keyUsage")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.17"), QByteArrayLiteral("subjectAltName")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.19"), QByteArrayLiteral("basicConstraints")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.35"), QByteArrayLiteral("authorityKeyIdentifier")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.10"), QByteArrayLiteral("O")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.11"), QByteArrayLiteral("OU")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.12"), QByteArrayLiteral("title")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.13"), QByteArrayLiteral("description")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.17"), QByteArrayLiteral("postalCode")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.3"), QByteArrayLiteral("CN")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.4"), QByteArrayLiteral("SN")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.41"), QByteArrayLiteral("name")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.42"), QByteArrayLiteral("GN")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.43"), QByteArrayLiteral("initials")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.46"), QByteArrayLiteral("dnQualifier")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.5"), QByteArrayLiteral("serialNumber")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.6"), QByteArrayLiteral("C")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.7"), QByteArrayLiteral("L")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.8"), QByteArrayLiteral("ST")); - oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.9"), QByteArrayLiteral("street")); - return oids; -} -Q_GLOBAL_STATIC_WITH_ARGS(OidNameMap, oidNameMap, (createOidMap())) - -QAsn1Element::QAsn1Element(quint8 type, const QByteArray &value) - : mType(type) - , mValue(value) -{ -} - -bool QAsn1Element::read(QDataStream &stream) -{ - // type - quint8 tmpType; - stream >> tmpType; - if (!tmpType) - return false; - - // length - quint64 length = 0; - quint8 first; - stream >> first; - if (first & 0x80) { - // long form - const quint8 bytes = (first & 0x7f); - if (bytes > 7) - return false; - - quint8 b; - for (int i = 0; i < bytes; i++) { - stream >> b; - length = (length << 8) | b; - } - } else { - // short form - length = (first & 0x7f); - } - - if (length > quint64(std::numeric_limits<int>::max())) - return false; - - // read value in blocks to avoid being fooled by incorrect length - const int BUFFERSIZE = 4 * 1024; - QByteArray tmpValue; - int remainingLength = length; - while (remainingLength) { - char readBuffer[BUFFERSIZE]; - const int bytesToRead = qMin(remainingLength, BUFFERSIZE); - const int count = stream.readRawData(readBuffer, bytesToRead); - if (count != int(bytesToRead)) - return false; - tmpValue.append(readBuffer, bytesToRead); - remainingLength -= bytesToRead; - } - - mType = tmpType; - mValue.swap(tmpValue); - return true; -} - -bool QAsn1Element::read(const QByteArray &data) -{ - QDataStream stream(data); - return read(stream); -} - -void QAsn1Element::write(QDataStream &stream) const -{ - // type - stream << mType; - - // length - qint64 length = mValue.size(); - if (length >= 128) { - // long form - quint8 encodedLength = 0x80; - QByteArray ba; - while (length) { - ba.prepend(quint8((length & 0xff))); - length >>= 8; - encodedLength += 1; - } - stream << encodedLength; - stream.writeRawData(ba.data(), ba.size()); - } else { - // short form - stream << quint8(length); - } - - // value - stream.writeRawData(mValue.data(), mValue.size()); -} - -QAsn1Element QAsn1Element::fromBool(bool val) -{ - return QAsn1Element(QAsn1Element::BooleanType, - QByteArray(1, val ? 0xff : 0x00)); -} - -QAsn1Element QAsn1Element::fromInteger(unsigned int val) -{ - QAsn1Element elem(QAsn1Element::IntegerType); - while (val > 127) { - elem.mValue.prepend(val & 0xff); - val >>= 8; - } - elem.mValue.prepend(val & 0x7f); - return elem; -} - -QAsn1Element QAsn1Element::fromVector(const QList<QAsn1Element> &items) -{ - QAsn1Element seq; - seq.mType = SequenceType; - QDataStream stream(&seq.mValue, QDataStream::WriteOnly); - for (auto it = items.cbegin(), end = items.cend(); it != end; ++it) - it->write(stream); - return seq; -} - -QAsn1Element QAsn1Element::fromObjectId(const QByteArray &id) -{ - QAsn1Element elem; - elem.mType = ObjectIdentifierType; - const QList<QByteArray> bits = id.split('.'); - Q_ASSERT(bits.size() > 2); - elem.mValue += quint8((bits[0].toUInt() * 40 + bits[1].toUInt())); - for (int i = 2; i < bits.size(); ++i) { - char buffer[std::numeric_limits<unsigned int>::digits / 7 + 2]; - char *pBuffer = buffer + sizeof(buffer); - *--pBuffer = '\0'; - unsigned int node = bits[i].toUInt(); - *--pBuffer = quint8((node & 0x7f)); - node >>= 7; - while (node) { - *--pBuffer = quint8(((node & 0x7f) | 0x80)); - node >>= 7; - } - elem.mValue += pBuffer; - } - return elem; -} - -bool QAsn1Element::toBool(bool *ok) const -{ - if (*this == fromBool(true)) { - if (ok) - *ok = true; - return true; - } else if (*this == fromBool(false)) { - if (ok) - *ok = true; - return false; - } else { - if (ok) - *ok = false; - return false; - } -} - -QDateTime QAsn1Element::toDateTime() const -{ - QDateTime result; - - if (mValue.size() != 13 && mValue.size() != 15) - return result; - - // QDateTime::fromString is lenient and accepts +- signs in front - // of the year; but ASN.1 doesn't allow them. - const auto isAsciiDigit = [](char c) - { - return c >= '0' && c <= '9'; - }; - - if (!isAsciiDigit(mValue[0])) - return result; - - // Timezone must be present, and UTC - if (mValue.back() != 'Z') - return result; - - // In addition, check that we only have digits representing the - // date/time. This should not really be necessary (there's no such - // thing as negative months/days/etc.); it's a workaround for - // QTBUG-84349. - if (!std::all_of(mValue.begin(), mValue.end() - 1, isAsciiDigit)) - return result; - - if (mType == UtcTimeType && mValue.size() == 13) { - result = QDateTime::fromString(QString::fromLatin1(mValue), - QStringLiteral("yyMMddHHmmsst")); - if (!result.isValid()) - return result; - - Q_ASSERT(result.timeSpec() == Qt::UTC); - - QDate date = result.date(); - - // RFC 2459: - // Where YY is greater than or equal to 50, the year shall be - // interpreted as 19YY; and - // - // Where YY is less than 50, the year shall be interpreted as 20YY. - // - // QDateTime interprets the 'yy' format as 19yy, so we may need to adjust - // the year (bring it in the [1950, 2049] range). - if (date.year() < 1950) - result.setDate(date.addYears(100)); - - Q_ASSERT(result.date().year() >= 1950); - Q_ASSERT(result.date().year() <= 2049); - } else if (mType == GeneralizedTimeType && mValue.size() == 15) { - result = QDateTime::fromString(QString::fromLatin1(mValue), - QStringLiteral("yyyyMMddHHmmsst")); - } - - return result; -} - -QMultiMap<QByteArray, QString> QAsn1Element::toInfo() const -{ - QMultiMap<QByteArray, QString> info; - QAsn1Element elem; - QDataStream issuerStream(mValue); - while (elem.read(issuerStream) && elem.mType == QAsn1Element::SetType) { - QAsn1Element issuerElem; - QDataStream setStream(elem.mValue); - if (issuerElem.read(setStream) && issuerElem.mType == QAsn1Element::SequenceType) { - const auto elems = issuerElem.toList(); - if (elems.size() == 2) { - const QByteArray key = elems.front().toObjectName(); - if (!key.isEmpty()) - info.insert(key, elems.back().toString()); - } - } - } - return info; -} - -qint64 QAsn1Element::toInteger(bool *ok) const -{ - if (mType != QAsn1Element::IntegerType || mValue.isEmpty()) { - if (ok) - *ok = false; - return 0; - } - - // NOTE: - negative numbers are not handled - // - greater sizes would overflow - if (mValue.at(0) & 0x80 || mValue.size() > 8) { - if (ok) - *ok = false; - return 0; - } - - qint64 value = mValue.at(0) & 0x7f; - for (int i = 1; i < mValue.size(); ++i) - value = (value << 8) | quint8(mValue.at(i)); - - if (ok) - *ok = true; - return value; -} - -QList<QAsn1Element> QAsn1Element::toList() const -{ - QList<QAsn1Element> items; - if (mType == SequenceType) { - QAsn1Element elem; - QDataStream stream(mValue); - while (elem.read(stream)) - items << elem; - } - return items; -} - -QByteArray QAsn1Element::toObjectId() const -{ - QByteArray key; - if (mType == ObjectIdentifierType && !mValue.isEmpty()) { - quint8 b = mValue.at(0); - key += QByteArray::number(b / 40) + '.' + QByteArray::number (b % 40); - unsigned int val = 0; - for (int i = 1; i < mValue.size(); ++i) { - b = mValue.at(i); - val = (val << 7) | (b & 0x7f); - if (!(b & 0x80)) { - key += '.' + QByteArray::number(val); - val = 0; - } - } - } - return key; -} - -QByteArray QAsn1Element::toObjectName() const -{ - QByteArray key = toObjectId(); - return oidNameMap->value(key, key); -} - -QString QAsn1Element::toString() const -{ - // Detect embedded NULs and reject - if (qstrlen(mValue) < uint(mValue.size())) - return QString(); - - if (mType == PrintableStringType || mType == TeletexStringType - || mType == Rfc822NameType || mType == DnsNameType - || mType == UniformResourceIdentifierType) - return QString::fromLatin1(mValue, mValue.size()); - if (mType == Utf8StringType) - return QString::fromUtf8(mValue, mValue.size()); - - return QString(); -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qasn1element_p.h b/src/network/ssl/qasn1element_p.h deleted file mode 100644 index 48fe45c9a5..0000000000 --- a/src/network/ssl/qasn1element_p.h +++ /dev/null @@ -1,188 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org> -** 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 QASN1ELEMENT_P_H -#define QASN1ELEMENT_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 <QtNetwork/private/qtnetworkglobal_p.h> -#include <QtCore/qdatetime.h> -#include <QtCore/qmap.h> - -QT_BEGIN_NAMESPACE - -// General -#define RSADSI_OID "1.2.840.113549." - -#define RSA_ENCRYPTION_OID QByteArrayLiteral(RSADSI_OID "1.1.1") -#define DSA_ENCRYPTION_OID QByteArrayLiteral("1.2.840.10040.4.1") -#define EC_ENCRYPTION_OID QByteArrayLiteral("1.2.840.10045.2.1") -#define DH_ENCRYPTION_OID QByteArrayLiteral(RSADSI_OID "1.3.1") - -// These are mostly from the RFC for PKCS#5 -// PKCS#5: https://tools.ietf.org/html/rfc8018#appendix-B -#define PKCS5_OID RSADSI_OID "1.5." -// PKCS#12: https://tools.ietf.org/html/rfc7292#appendix-D) -#define PKCS12_OID RSADSI_OID "1.12." - -// -PBES1 -#define PKCS5_MD2_DES_CBC_OID QByteArrayLiteral(PKCS5_OID "1") // Not (yet) implemented -#define PKCS5_MD2_RC2_CBC_OID QByteArrayLiteral(PKCS5_OID "4") // Not (yet) implemented -#define PKCS5_MD5_DES_CBC_OID QByteArrayLiteral(PKCS5_OID "3") -#define PKCS5_MD5_RC2_CBC_OID QByteArrayLiteral(PKCS5_OID "6") -#define PKCS5_SHA1_DES_CBC_OID QByteArrayLiteral(PKCS5_OID "10") -#define PKCS5_SHA1_RC2_CBC_OID QByteArrayLiteral(PKCS5_OID "11") -#define PKCS12_SHA1_RC4_128_OID QByteArrayLiteral(PKCS12_OID "1.1") // Not (yet) implemented -#define PKCS12_SHA1_RC4_40_OID QByteArrayLiteral(PKCS12_OID "1.2") // Not (yet) implemented -#define PKCS12_SHA1_3KEY_3DES_CBC_OID QByteArrayLiteral(PKCS12_OID "1.3") -#define PKCS12_SHA1_2KEY_3DES_CBC_OID QByteArrayLiteral(PKCS12_OID "1.4") -#define PKCS12_SHA1_RC2_128_CBC_OID QByteArrayLiteral(PKCS12_OID "1.5") -#define PKCS12_SHA1_RC2_40_CBC_OID QByteArrayLiteral(PKCS12_OID "1.6") - -// -PBKDF2 -#define PKCS5_PBKDF2_ENCRYPTION_OID QByteArrayLiteral(PKCS5_OID "12") - -// -PBES2 -#define PKCS5_PBES2_ENCRYPTION_OID QByteArrayLiteral(PKCS5_OID "13") - -// Digest -#define DIGEST_ALGORITHM_OID RSADSI_OID "2." -// -HMAC-SHA-1 -#define HMAC_WITH_SHA1 QByteArrayLiteral(DIGEST_ALGORITHM_OID "7") -// -HMAC-SHA-2 -#define HMAC_WITH_SHA224 QByteArrayLiteral(DIGEST_ALGORITHM_OID "8") -#define HMAC_WITH_SHA256 QByteArrayLiteral(DIGEST_ALGORITHM_OID "9") -#define HMAC_WITH_SHA384 QByteArrayLiteral(DIGEST_ALGORITHM_OID "10") -#define HMAC_WITH_SHA512 QByteArrayLiteral(DIGEST_ALGORITHM_OID "11") -#define HMAC_WITH_SHA512_224 QByteArrayLiteral(DIGEST_ALGORITHM_OID "12") -#define HMAC_WITH_SHA512_256 QByteArrayLiteral(DIGEST_ALGORITHM_OID "13") - -// Encryption algorithms -#define ENCRYPTION_ALGORITHM_OID RSADSI_OID "3." -#define DES_CBC_ENCRYPTION_OID QByteArrayLiteral("1.3.14.3.2.7") -#define DES_EDE3_CBC_ENCRYPTION_OID QByteArrayLiteral(ENCRYPTION_ALGORITHM_OID "7") -#define RC2_CBC_ENCRYPTION_OID QByteArrayLiteral(ENCRYPTION_ALGORITHM_OID "2") -#define RC5_CBC_ENCRYPTION_OID QByteArrayLiteral(ENCRYPTION_ALGORITHM_OID "9") // Not (yet) implemented -#define AES_OID "2.16.840.1.101.3.4.1." -#define AES128_CBC_ENCRYPTION_OID QByteArrayLiteral(AES_OID "2") -#define AES192_CBC_ENCRYPTION_OID QByteArrayLiteral(AES_OID "22") // Not (yet) implemented -#define AES256_CBC_ENCRYPTION_OID QByteArrayLiteral(AES_OID "42") // Not (yet) implemented - -class Q_AUTOTEST_EXPORT QAsn1Element -{ -public: - enum ElementType { - // universal - BooleanType = 0x01, - IntegerType = 0x02, - BitStringType = 0x03, - OctetStringType = 0x04, - NullType = 0x05, - ObjectIdentifierType = 0x06, - Utf8StringType = 0x0c, - PrintableStringType = 0x13, - TeletexStringType = 0x14, - UtcTimeType = 0x17, - GeneralizedTimeType = 0x18, - SequenceType = 0x30, - SetType = 0x31, - - // GeneralNameTypes - Rfc822NameType = 0x81, - DnsNameType = 0x82, - UniformResourceIdentifierType = 0x86, - IpAddressType = 0x87, - - // context specific - Context0Type = 0xA0, - Context1Type = 0xA1, - Context3Type = 0xA3 - }; - - explicit QAsn1Element(quint8 type = 0, const QByteArray &value = QByteArray()); - bool read(QDataStream &data); - bool read(const QByteArray &data); - void write(QDataStream &data) const; - - static QAsn1Element fromBool(bool val); - static QAsn1Element fromInteger(unsigned int val); - static QAsn1Element fromVector(const QList<QAsn1Element> &items); - static QAsn1Element fromObjectId(const QByteArray &id); - - bool toBool(bool *ok = nullptr) const; - QDateTime toDateTime() const; - QMultiMap<QByteArray, QString> toInfo() const; - qint64 toInteger(bool *ok = nullptr) const; - QList<QAsn1Element> toList() const; - QByteArray toObjectId() const; - QByteArray toObjectName() const; - QString toString() const; - - quint8 type() const { return mType; } - QByteArray value() const { return mValue; } - - friend inline bool operator==(const QAsn1Element &, const QAsn1Element &); - friend inline bool operator!=(const QAsn1Element &, const QAsn1Element &); - -private: - quint8 mType; - QByteArray mValue; -}; -Q_DECLARE_TYPEINFO(QAsn1Element, Q_RELOCATABLE_TYPE); - -inline bool operator==(const QAsn1Element &e1, const QAsn1Element &e2) -{ return e1.mType == e2.mType && e1.mValue == e2.mValue; } - -inline bool operator!=(const QAsn1Element &e1, const QAsn1Element &e2) -{ return e1.mType != e2.mType || e1.mValue != e2.mValue; } - -QT_END_NAMESPACE - -#endif diff --git a/src/network/ssl/qdtls_base.cpp b/src/network/ssl/qdtls_base.cpp deleted file mode 100644 index 6a5979eb9e..0000000000 --- a/src/network/ssl/qdtls_base.cpp +++ /dev/null @@ -1,112 +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 "qdtls_base_p.h" - -QT_BEGIN_NAMESPACE - -void QDtlsBasePrivate::setDtlsError(QDtlsError code, const QString &description) -{ - errorCode = code; - errorDescription = description; -} - -QDtlsError QDtlsBasePrivate::error() const -{ - return errorCode; -} - -QString QDtlsBasePrivate::errorString() const -{ - return errorDescription; -} - -void QDtlsBasePrivate::clearDtlsError() -{ - errorCode = QDtlsError::NoError; - errorDescription.clear(); -} - -QSslConfiguration QDtlsBasePrivate::configuration() const -{ - return dtlsConfiguration; -} - -void QDtlsBasePrivate::setConfiguration(const QSslConfiguration &configuration) -{ - dtlsConfiguration = configuration; - clearDtlsError(); -} - -bool QDtlsBasePrivate::setCookieGeneratorParameters(const GenParams ¶ms) -{ - if (!params.secret.size()) { - setDtlsError(QDtlsError::InvalidInputParameters, - QDtls::tr("Invalid (empty) secret")); - return false; - } - - clearDtlsError(); - - hashAlgorithm = params.hash; - secret = params.secret; - - return true; -} - -QDtlsClientVerifier::GeneratorParameters -QDtlsBasePrivate::cookieGeneratorParameters() const -{ - return {hashAlgorithm, secret}; -} - -bool QDtlsBasePrivate::isDtlsProtocol(QSsl::SslProtocol protocol) -{ - switch (protocol) { - case QSsl::DtlsV1_0: - case QSsl::DtlsV1_0OrLater: - case QSsl::DtlsV1_2: - case QSsl::DtlsV1_2OrLater: - return true; - default: - return false; - } -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qdtls_base_p.h b/src/network/ssl/qdtls_base_p.h deleted file mode 100644 index df35f514a2..0000000000 --- a/src/network/ssl/qdtls_base_p.h +++ /dev/null @@ -1,115 +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$ -** -****************************************************************************/ - -#ifndef QDTLS_BASE_P_H -#define QDTLS_BASE_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 <private/qtnetworkglobal_p.h> - -QT_REQUIRE_CONFIG(dtls); - -#include "qsslconfiguration.h" -#include "qtlsbackend_p.h" -#include "qsslcipher.h" -#include "qsslsocket.h" -#include "qssl.h" - -#include <QtNetwork/qhostaddress.h> - -#include <QtCore/qcryptographichash.h> -#include <QtCore/qbytearray.h> -#include <QtCore/qglobal.h> -#include <QtCore/qstring.h> - -QT_BEGIN_NAMESPACE - -// This class exists to re-implement the shared error/cookie handling -// for both QDtls and QDtlsClientVerifier classes. Use it if/when -// you need it. Backend neutral. -class QDtlsBasePrivate : virtual public QTlsPrivate::DtlsBase -{ -public: - QDtlsBasePrivate(QSslSocket::SslMode m, const QByteArray &s) : mode(m), secret(s) {} - void setDtlsError(QDtlsError code, const QString &description) override; - QDtlsError error() const override; - QString errorString() const override; - void clearDtlsError() override; - - void setConfiguration(const QSslConfiguration &configuration) override; - QSslConfiguration configuration() const override; - - bool setCookieGeneratorParameters(const GenParams &) override; - GenParams cookieGeneratorParameters() const override; - - static bool isDtlsProtocol(QSsl::SslProtocol protocol); - - QHostAddress remoteAddress; - quint16 remotePort = 0; - quint16 mtuHint = 0; - - QDtlsError errorCode = QDtlsError::NoError; - QString errorDescription; - QSslConfiguration dtlsConfiguration; - QSslSocket::SslMode mode = QSslSocket::SslClientMode; - QSslCipher sessionCipher; - QSsl::SslProtocol sessionProtocol = QSsl::UnknownProtocol; - QString peerVfyName; - QByteArray secret; - -#ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 - QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha1; -#else - QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha256; -#endif -}; - -QT_END_NAMESPACE - -#endif // QDTLS_BASE_P_H diff --git a/src/network/ssl/qdtls_openssl.cpp b/src/network/ssl/qdtls_openssl.cpp deleted file mode 100644 index fe6c8013c7..0000000000 --- a/src/network/ssl/qdtls_openssl.cpp +++ /dev/null @@ -1,1451 +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$ -** -****************************************************************************/ - -#ifndef NOMINMAX -#define NOMINMAX -#endif // NOMINMAX -#include "private/qnativesocketengine_p.h" - -#include "qsslpresharedkeyauthenticator_p.h" -#include "qsslsocket_openssl_symbols_p.h" -#include "qsslcertificate_p.h" -#include "qdtls_openssl_p.h" -#include "qx509_openssl_p.h" -#include "qudpsocket.h" -#include "qssl_p.h" - -#include "qmessageauthenticationcode.h" -#include "qcryptographichash.h" - -#include "qdebug.h" - -#include <cstring> -#include <cstddef> - -QT_BEGIN_NAMESPACE - -#define QT_DTLS_VERBOSE 0 - -#if QT_DTLS_VERBOSE - -#define qDtlsWarning(arg) qWarning(arg) -#define qDtlsDebug(arg) qDebug(arg) - -#else - -#define qDtlsWarning(arg) -#define qDtlsDebug(arg) - -#endif // QT_DTLS_VERBOSE - -namespace dtlsutil -{ - -QByteArray cookie_for_peer(SSL *ssl) -{ - Q_ASSERT(ssl); - - // SSL_get_rbio does not increment the reference count - BIO *readBIO = q_SSL_get_rbio(ssl); - if (!readBIO) { - qCWarning(lcSsl, "No BIO (dgram) found in SSL object"); - return {}; - } - - auto listener = static_cast<dtlsopenssl::DtlsState *>(q_BIO_get_app_data(readBIO)); - if (!listener) { - qCWarning(lcSsl, "BIO_get_app_data returned invalid (nullptr) value"); - return {}; - } - - const QHostAddress peerAddress(listener->remoteAddress); - const quint16 peerPort(listener->remotePort); - QByteArray peerData; - if (peerAddress.protocol() == QAbstractSocket::IPv6Protocol) { - const Q_IPV6ADDR sin6_addr(peerAddress.toIPv6Address()); - peerData.resize(int(sizeof sin6_addr + sizeof peerPort)); - char *dst = peerData.data(); - std::memcpy(dst, &peerPort, sizeof peerPort); - dst += sizeof peerPort; - std::memcpy(dst, &sin6_addr, sizeof sin6_addr); - } else if (peerAddress.protocol() == QAbstractSocket::IPv4Protocol) { - const quint32 sin_addr(peerAddress.toIPv4Address()); - peerData.resize(int(sizeof sin_addr + sizeof peerPort)); - char *dst = peerData.data(); - std::memcpy(dst, &peerPort, sizeof peerPort); - dst += sizeof peerPort; - std::memcpy(dst, &sin_addr, sizeof sin_addr); - } else { - Q_UNREACHABLE(); - } - - return peerData; -} - -struct FallbackCookieSecret -{ - FallbackCookieSecret() - { - key.resize(32); - const int status = q_RAND_bytes(reinterpret_cast<unsigned char *>(key.data()), - key.size()); - if (status <= 0) - key.clear(); - } - - QByteArray key; - - Q_DISABLE_COPY_MOVE(FallbackCookieSecret) -}; - -QByteArray fallbackSecret() -{ - static const FallbackCookieSecret generator; - return generator.key; -} - -int next_timeoutMs(SSL *tlsConnection) -{ - Q_ASSERT(tlsConnection); - timeval timeLeft = {}; - q_DTLSv1_get_timeout(tlsConnection, &timeLeft); - return timeLeft.tv_sec * 1000; -} - - -void delete_connection(SSL *ssl) -{ - // The 'deleter' for QSharedPointer<SSL>. - if (ssl) - q_SSL_free(ssl); -} - -void delete_BIO_ADDR(BIO_ADDR *bio) -{ - // A deleter for QSharedPointer<BIO_ADDR> - if (bio) - q_BIO_ADDR_free(bio); -} - -void delete_bio_method(BIO_METHOD *method) -{ - // The 'deleter' for QSharedPointer<BIO_METHOD>. - if (method) - q_BIO_meth_free(method); -} - -// The path MTU discovery is non-trivial: it's a mix of getsockopt/setsockopt -// (IP_MTU/IP6_MTU/IP_MTU_DISCOVER) and fallback MTU values. It's not -// supported on all platforms, worse so - imposes specific requirements on -// underlying UDP socket etc. So for now, we either try a user-proposed MTU -// hint or rely on our own fallback value. As a fallback mtu OpenSSL uses 576 -// for IPv4 and 1280 for IPv6 (RFC 791, RFC 2460). To KIS we use 576. This -// rather small MTU value does not affect the size that can be read/written -// by QDtls, only a handshake (which is allowed to fragment). -enum class MtuGuess : long -{ - defaultMtu = 576 -}; - -} // namespace dtlsutil - -namespace dtlscallbacks -{ - -extern "C" int q_generate_cookie_callback(SSL *ssl, unsigned char *dst, - unsigned *cookieLength) -{ - if (!ssl || !dst || !cookieLength) { - qCWarning(lcSsl, - "Failed to generate cookie - invalid (nullptr) parameter(s)"); - return 0; - } - - void *generic = q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData); - if (!generic) { - qCWarning(lcSsl, "SSL_get_ex_data returned nullptr, cannot generate cookie"); - return 0; - } - - *cookieLength = 0; - - auto dtls = static_cast<dtlsopenssl::DtlsState *>(generic); - if (!dtls->secret.size()) - return 0; - - const QByteArray peerData(dtlsutil::cookie_for_peer(ssl)); - if (!peerData.size()) - return 0; - - QMessageAuthenticationCode hmac(dtls->hashAlgorithm, dtls->secret); - hmac.addData(peerData); - const QByteArray cookie = hmac.result(); - Q_ASSERT(cookie.size() >= 0); - // DTLS1_COOKIE_LENGTH is erroneously 256 bytes long, must be 255 - RFC 6347, 4.2.1. - *cookieLength = qMin(DTLS1_COOKIE_LENGTH - 1, cookie.size()); - std::memcpy(dst, cookie.constData(), *cookieLength); - - return 1; -} - -extern "C" int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie, - unsigned cookieLength) -{ - if (!ssl || !cookie || !cookieLength) { - qCWarning(lcSsl, "Could not verify cookie, invalid (nullptr or zero) parameters"); - return 0; - } - - unsigned char newCookie[DTLS1_COOKIE_LENGTH] = {}; - unsigned newCookieLength = 0; - if (q_generate_cookie_callback(ssl, newCookie, &newCookieLength) != 1) - return 0; - - return newCookieLength == cookieLength - && !std::memcmp(cookie, newCookie, cookieLength); -} - -extern "C" int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx) -{ - if (!ok) { - // Store the error and at which depth the error was detected. - SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx())); - if (!ssl) { - qCWarning(lcSsl, "X509_STORE_CTX_get_ex_data returned nullptr, handshake failure"); - return 0; - } - - void *generic = q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData); - if (!generic) { - qCWarning(lcSsl, "SSL_get_ex_data returned nullptr, handshake failure"); - return 0; - } - - auto dtls = static_cast<dtlsopenssl::DtlsState *>(generic); - dtls->x509Errors.append(QTlsPrivate::X509CertificateOpenSSL::errorEntryFromStoreContext(ctx)); - } - - // Always return 1 (OK) to allow verification to continue. We handle the - // errors gracefully after collecting all errors, after verification has - // completed. - return 1; -} - -extern "C" unsigned q_PSK_client_callback(SSL *ssl, const char *hint, char *identity, - unsigned max_identity_len, unsigned char *psk, - unsigned max_psk_len) -{ - auto *dtls = static_cast<dtlsopenssl::DtlsState *>(q_SSL_get_ex_data(ssl, - QTlsBackendOpenSSL::s_indexForSSLExtraData)); - if (!dtls) - return 0; - - Q_ASSERT(dtls->dtlsPrivate); - return dtls->dtlsPrivate->pskClientCallback(hint, identity, max_identity_len, psk, max_psk_len); -} - -extern "C" unsigned q_PSK_server_callback(SSL *ssl, const char *identity, unsigned char *psk, - unsigned max_psk_len) -{ - auto *dtls = static_cast<dtlsopenssl::DtlsState *>(q_SSL_get_ex_data(ssl, - QTlsBackendOpenSSL::s_indexForSSLExtraData)); - if (!dtls) - return 0; - - Q_ASSERT(dtls->dtlsPrivate); - return dtls->dtlsPrivate->pskServerCallback(identity, psk, max_psk_len); -} - -} // namespace dtlscallbacks - -namespace dtlsbio -{ - -extern "C" int q_dgram_read(BIO *bio, char *dst, int bytesToRead) -{ - if (!bio || !dst || bytesToRead <= 0) { - qCWarning(lcSsl, "invalid input parameter(s)"); - return 0; - } - - q_BIO_clear_retry_flags(bio); - - auto dtls = static_cast<dtlsopenssl::DtlsState *>(q_BIO_get_app_data(bio)); - // It's us who set data, if OpenSSL does too, the logic here is wrong - // then and we have to use BIO_set_app_data then! - Q_ASSERT(dtls); - int bytesRead = 0; - if (dtls->dgram.size()) { - bytesRead = qMin(dtls->dgram.size(), bytesToRead); - std::memcpy(dst, dtls->dgram.constData(), bytesRead); - - if (!dtls->peeking) - dtls->dgram = dtls->dgram.mid(bytesRead); - } else { - bytesRead = -1; - } - - if (bytesRead <= 0) - q_BIO_set_retry_read(bio); - - return bytesRead; -} - -extern "C" int q_dgram_write(BIO *bio, const char *src, int bytesToWrite) -{ - if (!bio || !src || bytesToWrite <= 0) { - qCWarning(lcSsl, "invalid input parameter(s)"); - return 0; - } - - q_BIO_clear_retry_flags(bio); - - auto dtls = static_cast<dtlsopenssl::DtlsState *>(q_BIO_get_app_data(bio)); - Q_ASSERT(dtls); - if (dtls->writeSuppressed) { - // See the comment in QDtls::startHandshake. - return bytesToWrite; - } - - QUdpSocket *udpSocket = dtls->udpSocket; - Q_ASSERT(udpSocket); - - const QByteArray dgram(QByteArray::fromRawData(src, bytesToWrite)); - qint64 bytesWritten = -1; - if (udpSocket->state() == QAbstractSocket::ConnectedState) { - bytesWritten = udpSocket->write(dgram); - } else { - bytesWritten = udpSocket->writeDatagram(dgram, dtls->remoteAddress, - dtls->remotePort); - } - - if (bytesWritten <= 0) - q_BIO_set_retry_write(bio); - - Q_ASSERT(bytesWritten <= std::numeric_limits<int>::max()); - return int(bytesWritten); -} - -extern "C" int q_dgram_puts(BIO *bio, const char *src) -{ - if (!bio || !src) { - qCWarning(lcSsl, "invalid input parameter(s)"); - return 0; - } - - return q_dgram_write(bio, src, int(std::strlen(src))); -} - -extern "C" long q_dgram_ctrl(BIO *bio, int cmd, long num, void *ptr) -{ - // This is our custom BIO_ctrl. bio.h defines a lot of BIO_CTRL_* - // and BIO_* constants and BIO_somename macros that expands to BIO_ctrl - // call with one of those constants as argument. What exactly BIO_ctrl - // does - depends on the 'cmd' and the type of BIO (so BIO_ctrl does - // not even have a single well-defined value meaning success or failure). - // We handle only the most generic commands - the ones documented for - // BIO_ctrl - and also DGRAM specific ones. And even for them - in most - // cases we do nothing but report a success or some non-error value. - // Documents also state: "Source/sink BIOs return an 0 if they do not - // recognize the BIO_ctrl() operation." - these are covered by 'default' - // label in the switch-statement below. Debug messages in the switch mean: - // 1) we got a command that is unexpected for dgram BIO, or: - // 2) we do not call any function that would lead to OpenSSL using this - // command. - - if (!bio) { - qDebug(lcSsl, "invalid 'bio' parameter (nullptr)"); - return -1; - } - - auto dtls = static_cast<dtlsopenssl::DtlsState *>(q_BIO_get_app_data(bio)); - Q_ASSERT(dtls); - - switch (cmd) { - // Let's start from the most generic ones, in the order in which they are - // documented (as BIO_ctrl): - case BIO_CTRL_RESET: - // BIO_reset macro. - // From documentation: - // "BIO_reset() normally returns 1 for success and 0 or -1 for failure. - // File BIOs are an exception, they return 0 for success and -1 for - // failure." - // We have nothing to reset and we are not file BIO. - return 1; - case BIO_C_FILE_SEEK: - case BIO_C_FILE_TELL: - qDtlsWarning("Unexpected cmd (BIO_C_FILE_SEEK/BIO_C_FILE_TELL)"); - // These are for BIO_seek, BIO_tell. We are not a file BIO. - // Non-negative return value means success. - return 0; - case BIO_CTRL_FLUSH: - // BIO_flush, nothing to do, we do not buffer any data. - // 0 or -1 means error, 1 - success. - return 1; - case BIO_CTRL_EOF: - qDtlsWarning("Unexpected cmd (BIO_CTRL_EOF)"); - // BIO_eof, 1 means EOF read. Makes no sense for us. - return 0; - case BIO_CTRL_SET_CLOSE: - // BIO_set_close with BIO_CLOSE/BIO_NOCLOSE flags. Documented as - // always returning 1. - // From the documentation: - // "Typically BIO_CLOSE is used in a source/sink BIO to indicate that - // the underlying I/O stream should be closed when the BIO is freed." - // - // QUdpSocket we work with is not BIO's business, ignoring. - return 1; - case BIO_CTRL_GET_CLOSE: - // BIO_get_close. No, never, see the comment above. - return 0; - case BIO_CTRL_PENDING: - qDtlsWarning("Unexpected cmd (BIO_CTRL_PENDING)"); - // BIO_pending. Not used by DTLS/OpenSSL (we are not buffering). - return 0; - case BIO_CTRL_WPENDING: - // No, we have nothing buffered. - return 0; - // The constants below are not documented as a part BIO_ctrl documentation, - // but they are also not type-specific. - case BIO_CTRL_DUP: - qDtlsWarning("Unexpected cmd (BIO_CTRL_DUP)"); - // BIO_dup_state, not used by DTLS (and socket-related BIOs in general). - // For some very specific BIO type this 'cmd' would copy some state - // from 'bio' to (BIO*)'ptr'. 1 means success. - return 0; - case BIO_CTRL_SET_CALLBACK: - qDtlsWarning("Unexpected cmd (BIO_CTRL_SET_CALLBACK)"); - // BIO_set_info_callback. We never call this, OpenSSL does not do this - // on its own (normally it's used if client code wants to have some - // debug information, for example, dumping handshake state via - // BIO_printf from SSL info_callback). - return 0; - case BIO_CTRL_GET_CALLBACK: - qDtlsWarning("Unexpected cmd (BIO_CTRL_GET_CALLBACK)"); - // BIO_get_info_callback. We never call this. - if (ptr) - *static_cast<bio_info_cb **>(ptr) = nullptr; - return 0; - case BIO_CTRL_SET: - case BIO_CTRL_GET: - qDtlsWarning("Unexpected cmd (BIO_CTRL_SET/BIO_CTRL_GET)"); - // Somewhat 'documented' as setting/getting IO type. Not used anywhere - // except BIO_buffer_get_num_lines (which contradics 'get IO type'). - // Ignoring. - return 0; - // DGRAM-specific operation, we have to return some reasonable value - // (so far, I've encountered only peek mode switching, connect). - case BIO_CTRL_DGRAM_CONNECT: - // BIO_ctrl_dgram_connect. Not needed. Our 'dtls' already knows - // the peer's address/port. Report success though. - return 1; - case BIO_CTRL_DGRAM_SET_CONNECTED: - qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_SET_CONNECTED)"); - // BIO_ctrl_dgram_set_connected. We never call it, OpenSSL does - // not call it on its own (so normally it's done by client code). - // Similar to BIO_CTRL_DGRAM_CONNECT, but it also informs the BIO - // that its UDP socket is connected. We never need it though. - return -1; - case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT: - qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_SET_RECV_TIMEOUT)"); - // Essentially setsockopt with SO_RCVTIMEO, not needed, our sockets - // are non-blocking. - return -1; - case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT: - qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_GET_RECV_TIMEOUT)"); - // getsockopt with SO_RCVTIMEO, not needed, our sockets are - // non-blocking. ptr is timeval *. - return -1; - case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT: - qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_SET_SEND_TIMEOUT)"); - // setsockopt, SO_SNDTIMEO, cannot happen. - return -1; - case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT: - qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_GET_SEND_TIMEOUT)"); - // getsockopt, SO_SNDTIMEO, cannot happen. - return -1; - case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP: - // BIO_dgram_recv_timedout. No, we are non-blocking. - return 0; - case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP: - // BIO_dgram_send_timedout. No, we are non-blocking. - return 0; - case BIO_CTRL_DGRAM_MTU_DISCOVER: - qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_MTU_DISCOVER)"); - // setsockopt, IP_MTU_DISCOVER/IP6_MTU_DISCOVER, to be done - // in QUdpSocket instead. OpenSSL never calls it, only client - // code. - return 1; - case BIO_CTRL_DGRAM_QUERY_MTU: - qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_QUERY_MTU)"); - // To be done in QUdpSocket instead. - return 1; - case BIO_CTRL_DGRAM_GET_FALLBACK_MTU: - qDtlsWarning("Unexpected command *BIO_CTRL_DGRAM_GET_FALLBACK_MTU)"); - // Without SSL_OP_NO_QUERY_MTU set on SSL, OpenSSL can request for - // fallback MTU after several re-transmissions. - // Should never happen in our case. - return long(dtlsutil::MtuGuess::defaultMtu); - case BIO_CTRL_DGRAM_GET_MTU: - qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_GET_MTU)"); - return -1; - case BIO_CTRL_DGRAM_SET_MTU: - qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_SET_MTU)"); - // Should not happen (we don't call BIO_ctrl with this parameter) - // and set MTU on SSL instead. - return -1; // num is mtu and it's a return value meaning success. - case BIO_CTRL_DGRAM_MTU_EXCEEDED: - qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_MTU_EXCEEDED)"); - return 0; - case BIO_CTRL_DGRAM_GET_PEER: - qDtlsDebug("BIO_CTRL_DGRAM_GET_PEER"); - // BIO_dgram_get_peer. We do not return a real address (DTLS is not - // using this address), but let's pretend a success. - switch (dtls->remoteAddress.protocol()) { - case QAbstractSocket::IPv6Protocol: - return sizeof(sockaddr_in6); - case QAbstractSocket::IPv4Protocol: - return sizeof(sockaddr_in); - default: - return -1; - } - case BIO_CTRL_DGRAM_SET_PEER: - // Similar to BIO_CTRL_DGRAM_CONNECTED. - return 1; - case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT: - // DTLSTODO: I'm not sure yet, how it's used by OpenSSL. - return 1; - case BIO_CTRL_DGRAM_SET_DONT_FRAG: - qDtlsDebug("BIO_CTRL_DGRAM_SET_DONT_FRAG"); - // To be done in QUdpSocket, it's about IP_DONTFRAG etc. - return 1; - case BIO_CTRL_DGRAM_GET_MTU_OVERHEAD: - // AFAIK it's 28 for IPv4 and 48 for IPv6, but let's pretend it's 0 - // so that OpenSSL does not start suddenly fragmenting the first - // client hello (which will result in DTLSv1_listen rejecting it). - return 0; - case BIO_CTRL_DGRAM_SET_PEEK_MODE: - dtls->peeking = num; - return 1; - default:; -#if QT_DTLS_VERBOSE - qWarning() << "Unexpected cmd (" << cmd << ")"; -#endif - } - - return 0; -} - -extern "C" int q_dgram_create(BIO *bio) -{ - - q_BIO_set_init(bio, 1); - // With a custom BIO you'd normally allocate some implementation-specific - // data and append it to this new BIO using BIO_set_data. We don't need - // it and thus q_dgram_destroy below is a noop. - return 1; -} - -extern "C" int q_dgram_destroy(BIO *bio) -{ - Q_UNUSED(bio); - return 1; -} - -const char * const qdtlsMethodName = "qdtlsbio"; - -} // namespace dtlsbio - -namespace dtlsopenssl -{ - -bool DtlsState::init(QDtlsBasePrivate *dtlsBase, QUdpSocket *socket, - const QHostAddress &remote, quint16 port, - const QByteArray &receivedMessage) -{ - Q_ASSERT(dtlsBase); - Q_ASSERT(socket); - - if (!tlsContext.data() && !initTls(dtlsBase)) - return false; - - udpSocket = socket; - - setLinkMtu(dtlsBase); - - dgram = receivedMessage; - remoteAddress = remote; - remotePort = port; - - // SSL_get_rbio does not increment a reference count. - BIO *bio = q_SSL_get_rbio(tlsConnection.data()); - Q_ASSERT(bio); - q_BIO_set_app_data(bio, this); - - return true; -} - -void DtlsState::reset() -{ - tlsConnection.reset(); - tlsContext.reset(); -} - -bool DtlsState::initTls(QDtlsBasePrivate *dtlsBase) -{ - if (tlsContext.data()) - return true; - - if (!QSslSocket::supportsSsl()) - return false; - - if (!initCtxAndConnection(dtlsBase)) - return false; - - if (!initBIO(dtlsBase)) { - tlsConnection.reset(); - tlsContext.reset(); - return false; - } - - return true; -} - -static QString msgFunctionFailed(const char *function) -{ - //: %1: Some function - return QDtls::tr("%1 failed").arg(QLatin1String(function)); -} - -bool DtlsState::initCtxAndConnection(QDtlsBasePrivate *dtlsBase) -{ - Q_ASSERT(dtlsBase); - Q_ASSERT(QSslSocket::supportsSsl()); - - if (dtlsBase->mode == QSslSocket::UnencryptedMode) { - dtlsBase->setDtlsError(QDtlsError::TlsInitializationError, - QDtls::tr("Invalid SslMode, SslServerMode or SslClientMode expected")); - return false; - } - - if (!QDtlsBasePrivate::isDtlsProtocol(dtlsBase->dtlsConfiguration.protocol())) { - dtlsBase->setDtlsError(QDtlsError::TlsInitializationError, - QDtls::tr("Invalid protocol version, DTLS protocol expected")); - return false; - } - - const bool rootsOnDemand = QTlsBackend::rootLoadingOnDemandAllowed(dtlsBase->dtlsConfiguration); - TlsContext newContext(QSslContext::sharedFromConfiguration(dtlsBase->mode, dtlsBase->dtlsConfiguration, - rootsOnDemand)); - - if (newContext->error() != QSslError::NoError) { - dtlsBase->setDtlsError(QDtlsError::TlsInitializationError, newContext->errorString()); - return false; - } - - TlsConnection newConnection(newContext->createSsl(), dtlsutil::delete_connection); - if (!newConnection.data()) { - dtlsBase->setDtlsError(QDtlsError::TlsInitializationError, - msgFunctionFailed("SSL_new")); - return false; - } - - const int set = q_SSL_set_ex_data(newConnection.data(), - QTlsBackendOpenSSL::s_indexForSSLExtraData, - this); - - if (set != 1 && dtlsBase->dtlsConfiguration.peerVerifyMode() != QSslSocket::VerifyNone) { - dtlsBase->setDtlsError(QDtlsError::TlsInitializationError, - msgFunctionFailed("SSL_set_ex_data")); - return false; - } - - if (dtlsBase->mode == QSslSocket::SslServerMode) { - if (dtlsBase->dtlsConfiguration.dtlsCookieVerificationEnabled()) - q_SSL_set_options(newConnection.data(), SSL_OP_COOKIE_EXCHANGE); - q_SSL_set_psk_server_callback(newConnection.data(), dtlscallbacks::q_PSK_server_callback); - } else { - q_SSL_set_psk_client_callback(newConnection.data(), dtlscallbacks::q_PSK_client_callback); - } - - tlsContext.swap(newContext); - tlsConnection.swap(newConnection); - - return true; -} - -bool DtlsState::initBIO(QDtlsBasePrivate *dtlsBase) -{ - Q_ASSERT(dtlsBase); - Q_ASSERT(tlsContext.data() && tlsConnection.data()); - - BioMethod customMethod(q_BIO_meth_new(BIO_TYPE_DGRAM, dtlsbio::qdtlsMethodName), - dtlsutil::delete_bio_method); - if (!customMethod.data()) { - dtlsBase->setDtlsError(QDtlsError::TlsInitializationError, - msgFunctionFailed("BIO_meth_new")); - return false; - } - - BIO_METHOD *biom = customMethod.data(); - q_BIO_meth_set_create(biom, dtlsbio::q_dgram_create); - q_BIO_meth_set_destroy(biom, dtlsbio::q_dgram_destroy); - q_BIO_meth_set_read(biom, dtlsbio::q_dgram_read); - q_BIO_meth_set_write(biom, dtlsbio::q_dgram_write); - q_BIO_meth_set_puts(biom, dtlsbio::q_dgram_puts); - q_BIO_meth_set_ctrl(biom, dtlsbio::q_dgram_ctrl); - - BIO *bio = q_BIO_new(biom); - if (!bio) { - dtlsBase->setDtlsError(QDtlsError::TlsInitializationError, - msgFunctionFailed("BIO_new")); - return false; - } - - q_SSL_set_bio(tlsConnection.data(), bio, bio); - - bioMethod.swap(customMethod); - - return true; -} - -void DtlsState::setLinkMtu(QDtlsBasePrivate *dtlsBase) -{ - Q_ASSERT(dtlsBase); - Q_ASSERT(udpSocket); - Q_ASSERT(tlsConnection.data()); - - long mtu = dtlsBase->mtuHint; - if (!mtu) { - // If the underlying QUdpSocket was connected, getsockopt with - // IP_MTU/IP6_MTU can give us some hint: - bool optionFound = false; - if (udpSocket->state() == QAbstractSocket::ConnectedState) { - const QVariant val(udpSocket->socketOption(QAbstractSocket::PathMtuSocketOption)); - if (val.isValid() && val.canConvert<int>()) - mtu = val.toInt(&optionFound); - } - - if (!optionFound || mtu <= 0) { - // OK, our own initial guess. - mtu = long(dtlsutil::MtuGuess::defaultMtu); - } - } - - // For now, we disable this option. - q_SSL_set_options(tlsConnection.data(), SSL_OP_NO_QUERY_MTU); - - q_DTLS_set_link_mtu(tlsConnection.data(), mtu); -} - -} // namespace dtlsopenssl - -QDtlsClientVerifierOpenSSL::QDtlsClientVerifierOpenSSL() - : QDtlsBasePrivate(QSslSocket::SslServerMode, dtlsutil::fallbackSecret()) -{ -} - -bool QDtlsClientVerifierOpenSSL::verifyClient(QUdpSocket *socket, const QByteArray &dgram, - const QHostAddress &address, quint16 port) -{ - Q_ASSERT(socket); - Q_ASSERT(dgram.size()); - Q_ASSERT(!address.isNull()); - Q_ASSERT(port); - - clearDtlsError(); - verifiedClientHello.clear(); - - if (!dtls.init(this, socket, address, port, dgram)) - return false; - - dtls.secret = secret; - dtls.hashAlgorithm = hashAlgorithm; - - Q_ASSERT(dtls.tlsConnection.data()); - QSharedPointer<BIO_ADDR> peer(q_BIO_ADDR_new(), dtlsutil::delete_BIO_ADDR); - if (!peer.data()) { - setDtlsError(QDtlsError::TlsInitializationError, - QDtlsClientVerifier::tr("BIO_ADDR_new failed, ignoring client hello")); - return false; - } - - const int ret = q_DTLSv1_listen(dtls.tlsConnection.data(), peer.data()); - if (ret < 0) { - // Since 1.1 - it's a fatal error (not so in 1.0.2 for non-blocking socket) - setDtlsError(QDtlsError::TlsFatalError, QTlsBackendOpenSSL::getErrorsFromOpenSsl()); - return false; - } - - if (ret > 0) { - verifiedClientHello = dgram; - return true; - } - - return false; -} - -QByteArray QDtlsClientVerifierOpenSSL::verifiedHello() const -{ - return verifiedClientHello; -} - -void QDtlsPrivateOpenSSL::TimeoutHandler::start(int hintMs) -{ - Q_ASSERT(timerId == -1); - timerId = startTimer(hintMs > 0 ? hintMs : timeoutMs, Qt::PreciseTimer); -} - -void QDtlsPrivateOpenSSL::TimeoutHandler::doubleTimeout() -{ - if (timeoutMs * 2 < 60000) - timeoutMs *= 2; - else - timeoutMs = 60000; -} - -void QDtlsPrivateOpenSSL::TimeoutHandler::stop() -{ - if (timerId != -1) { - killTimer(timerId); - timerId = -1; - } -} - -void QDtlsPrivateOpenSSL::TimeoutHandler::timerEvent(QTimerEvent *event) -{ - Q_UNUSED(event); - Q_ASSERT(timerId != -1); - - killTimer(timerId); - timerId = -1; - - Q_ASSERT(dtlsConnection); - dtlsConnection->reportTimeout(); -} - -QDtlsPrivateOpenSSL::QDtlsPrivateOpenSSL(QDtls *qObject, QSslSocket::SslMode side) - : QDtlsBasePrivate(side, dtlsutil::fallbackSecret()), q(qObject) -{ - Q_ASSERT(qObject); - - dtls.dtlsPrivate = this; -} - -QSslSocket::SslMode QDtlsPrivateOpenSSL::cryptographMode() const -{ - return mode; -} - -void QDtlsPrivateOpenSSL::setPeer(const QHostAddress &addr, quint16 port, const QString &name) -{ - remoteAddress = addr; - remotePort = port; - peerVfyName = name; -} - -QHostAddress QDtlsPrivateOpenSSL::peerAddress() const -{ - return remoteAddress; -} - -quint16 QDtlsPrivateOpenSSL::peerPort() const -{ - return remotePort; -} - -void QDtlsPrivateOpenSSL::setPeerVerificationName(const QString &name) -{ - peerVfyName = name; -} - -QString QDtlsPrivateOpenSSL::peerVerificationName() const -{ - return peerVfyName; -} - -void QDtlsPrivateOpenSSL::setDtlsMtuHint(quint16 mtu) -{ - mtuHint = mtu; -} - -quint16 QDtlsPrivateOpenSSL::dtlsMtuHint() const -{ - return mtuHint; -} - -QDtls::HandshakeState QDtlsPrivateOpenSSL::state() const -{ - return handshakeState; -} - -bool QDtlsPrivateOpenSSL::isConnectionEncrypted() const -{ - return connectionEncrypted; -} - -bool QDtlsPrivateOpenSSL::startHandshake(QUdpSocket *socket, const QByteArray &dgram) -{ - Q_ASSERT(socket); - Q_ASSERT(handshakeState == QDtls::HandshakeNotStarted); - - clearDtlsError(); - connectionEncrypted = false; - - if (!dtls.init(this, socket, remoteAddress, remotePort, dgram)) - return false; - - if (mode == QSslSocket::SslServerMode && dtlsConfiguration.dtlsCookieVerificationEnabled()) { - dtls.secret = secret; - dtls.hashAlgorithm = hashAlgorithm; - // Let's prepare the state machine so that message sequence 1 does not - // surprise DTLS/OpenSSL (such a message would be disregarded as - // 'stale or future' in SSL_accept otherwise): - int result = 0; - QSharedPointer<BIO_ADDR> peer(q_BIO_ADDR_new(), dtlsutil::delete_BIO_ADDR); - if (!peer.data()) { - setDtlsError(QDtlsError::TlsInitializationError, - QDtls::tr("BIO_ADD_new failed, cannot start handshake")); - return false; - } - - // If it's an invalid/unexpected ClientHello, we don't want to send - // VerifyClientRequest - it's a job of QDtlsClientVerifier - so we - // suppress any attempts to write into socket: - dtls.writeSuppressed = true; - result = q_DTLSv1_listen(dtls.tlsConnection.data(), peer.data()); - dtls.writeSuppressed = false; - - if (result <= 0) { - setDtlsError(QDtlsError::TlsFatalError, - QDtls::tr("Cannot start the handshake, verified client hello expected")); - dtls.reset(); - return false; - } - } - - handshakeState = QDtls::HandshakeInProgress; - opensslErrors.clear(); - tlsErrors.clear(); - - return continueHandshake(socket, dgram); -} - -bool QDtlsPrivateOpenSSL::continueHandshake(QUdpSocket *socket, const QByteArray &dgram) -{ - Q_ASSERT(socket); - - Q_ASSERT(handshakeState == QDtls::HandshakeInProgress); - - clearDtlsError(); - - if (timeoutHandler.data()) - timeoutHandler->stop(); - - if (!dtls.init(this, socket, remoteAddress, remotePort, dgram)) - return false; - - dtls.x509Errors.clear(); - - int result = 0; - if (mode == QSslSocket::SslServerMode) - result = q_SSL_accept(dtls.tlsConnection.data()); - else - result = q_SSL_connect(dtls.tlsConnection.data()); - - // DTLSTODO: Investigate/test if it makes sense - QSslSocket can emit - // peerVerifyError at this point (and thus potentially client code - // will close the underlying TCP connection immediately), but we are using - // QUdpSocket, no connection to close, our verification callback returns 1 - // (verified OK) and this probably means OpenSSL has already sent a reply - // to the server's hello/certificate. - - opensslErrors << dtls.x509Errors; - - if (result <= 0) { - const auto code = q_SSL_get_error(dtls.tlsConnection.data(), result); - switch (code) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - // DTLSTODO: to be tested - in principle, if it was the first call to - // continueHandshake and server for some reason discards the client - // hello message (even the verified one) - our 'this' will probably - // forever stay in this strange InProgress state? (the client - // will dully re-transmit the same hello and we discard it again?) - // SSL_get_state can provide more information about state - // machine and we can switch to NotStarted (since we have not - // replied with our hello ...) - if (!timeoutHandler.data()) { - timeoutHandler.reset(new TimeoutHandler); - timeoutHandler->dtlsConnection = this; - } else { - // Back to 1s. - timeoutHandler->resetTimeout(); - } - - timeoutHandler->start(); - - return true; // The handshake is not yet complete. - default: - storePeerCertificates(); - setDtlsError(QDtlsError::TlsFatalError, - QTlsBackendOpenSSL::msgErrorsDuringHandshake()); - dtls.reset(); - handshakeState = QDtls::HandshakeNotStarted; - return false; - } - } - - storePeerCertificates(); - fetchNegotiatedParameters(); - - const bool doVerifyPeer = dtlsConfiguration.peerVerifyMode() == QSslSocket::VerifyPeer - || (dtlsConfiguration.peerVerifyMode() == QSslSocket::AutoVerifyPeer - && mode == QSslSocket::SslClientMode); - - if (!doVerifyPeer || verifyPeer() || tlsErrorsWereIgnored()) { - connectionEncrypted = true; - handshakeState = QDtls::HandshakeComplete; - return true; - } - - setDtlsError(QDtlsError::PeerVerificationError, QDtls::tr("Peer verification failed")); - handshakeState = QDtls::PeerVerificationFailed; - return false; -} - - -bool QDtlsPrivateOpenSSL::handleTimeout(QUdpSocket *socket) -{ - Q_ASSERT(socket); - - Q_ASSERT(timeoutHandler.data()); - Q_ASSERT(dtls.tlsConnection.data()); - - clearDtlsError(); - - dtls.udpSocket = socket; - - if (q_DTLSv1_handle_timeout(dtls.tlsConnection.data()) > 0) { - timeoutHandler->doubleTimeout(); - timeoutHandler->start(); - } else { - timeoutHandler->start(dtlsutil::next_timeoutMs(dtls.tlsConnection.data())); - } - - return true; -} - -bool QDtlsPrivateOpenSSL::resumeHandshake(QUdpSocket *socket) -{ - Q_UNUSED(socket); - Q_ASSERT(socket); - Q_ASSERT(handshakeState == QDtls::PeerVerificationFailed); - - clearDtlsError(); - - if (tlsErrorsWereIgnored()) { - handshakeState = QDtls::HandshakeComplete; - connectionEncrypted = true; - tlsErrors.clear(); - tlsErrorsToIgnore.clear(); - return true; - } - - return false; -} - -void QDtlsPrivateOpenSSL::abortHandshake(QUdpSocket *socket) -{ - Q_ASSERT(socket); - Q_ASSERT(handshakeState == QDtls::PeerVerificationFailed - || handshakeState == QDtls::HandshakeInProgress); - - clearDtlsError(); - - if (handshakeState == QDtls::PeerVerificationFailed) { - // Yes, while peer verification failed, we were actually encrypted. - // Let's play it nice - inform our peer about connection shut down. - sendShutdownAlert(socket); - } else { - resetDtls(); - } -} - -void QDtlsPrivateOpenSSL::sendShutdownAlert(QUdpSocket *socket) -{ - Q_ASSERT(socket); - - clearDtlsError(); - - if (connectionEncrypted && !connectionWasShutdown) { - dtls.udpSocket = socket; - Q_ASSERT(dtls.tlsConnection.data()); - q_SSL_shutdown(dtls.tlsConnection.data()); - } - - resetDtls(); -} - -QList<QSslError> QDtlsPrivateOpenSSL::peerVerificationErrors() const -{ - return tlsErrors; -} - -void QDtlsPrivateOpenSSL::ignoreVerificationErrors(const QList<QSslError> &errorsToIgnore) -{ - tlsErrorsToIgnore = errorsToIgnore; -} - -QSslCipher QDtlsPrivateOpenSSL::dtlsSessionCipher() const -{ - return sessionCipher; -} - -QSsl::SslProtocol QDtlsPrivateOpenSSL::dtlsSessionProtocol() const -{ - return sessionProtocol; -} - -qint64 QDtlsPrivateOpenSSL::writeDatagramEncrypted(QUdpSocket *socket, - const QByteArray &dgram) -{ - Q_ASSERT(socket); - Q_ASSERT(dtls.tlsConnection.data()); - Q_ASSERT(connectionEncrypted); - - clearDtlsError(); - - dtls.udpSocket = socket; - const int written = q_SSL_write(dtls.tlsConnection.data(), - dgram.constData(), dgram.size()); - if (written > 0) - return written; - - const unsigned long errorCode = q_ERR_get_error(); - if (!dgram.size() && errorCode == SSL_ERROR_NONE) { - // With OpenSSL <= 1.1 this can happen. For example, DTLS client - // tries to reconnect (while re-using the same address/port) - - // DTLS server drops a message with unexpected epoch but says - no - // error. We leave to client code to resolve such problems until - // OpenSSL provides something better. - return 0; - } - - switch (errorCode) { - case SSL_ERROR_WANT_WRITE: - case SSL_ERROR_WANT_READ: - // We do not set any error/description ... a user can probably re-try - // sending a datagram. - break; - case SSL_ERROR_ZERO_RETURN: - connectionWasShutdown = true; - setDtlsError(QDtlsError::TlsFatalError, QDtls::tr("The DTLS connection has been closed")); - handshakeState = QDtls::HandshakeNotStarted; - dtls.reset(); - break; - case SSL_ERROR_SYSCALL: - case SSL_ERROR_SSL: - default: - // DTLSTODO: we don't know yet what to do. Tests needed - probably, - // some errors can be just ignored (it's UDP, not TCP after all). - // Unlike QSslSocket we do not abort though. - QString description(QTlsBackendOpenSSL::getErrorsFromOpenSsl()); - if (socket->error() != QAbstractSocket::UnknownSocketError && description.isEmpty()) { - setDtlsError(QDtlsError::UnderlyingSocketError, socket->errorString()); - } else { - setDtlsError(QDtlsError::TlsFatalError, - QDtls::tr("Error while writing: %1").arg(description)); - } - } - - return -1; -} - -QByteArray QDtlsPrivateOpenSSL::decryptDatagram(QUdpSocket *socket, const QByteArray &tlsdgram) -{ - Q_ASSERT(socket); - Q_ASSERT(tlsdgram.size()); - - Q_ASSERT(dtls.tlsConnection.data()); - Q_ASSERT(connectionEncrypted); - - dtls.dgram = tlsdgram; - dtls.udpSocket = socket; - - clearDtlsError(); - - QByteArray dgram; - dgram.resize(tlsdgram.size()); - const int read = q_SSL_read(dtls.tlsConnection.data(), dgram.data(), - dgram.size()); - - if (read > 0) { - dgram.resize(read); - return dgram; - } - - dgram.clear(); - unsigned long errorCode = q_ERR_get_error(); - if (errorCode == SSL_ERROR_NONE) { - const int shutdown = q_SSL_get_shutdown(dtls.tlsConnection.data()); - if (shutdown & SSL_RECEIVED_SHUTDOWN) - errorCode = SSL_ERROR_ZERO_RETURN; - else - return dgram; - } - - switch (errorCode) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - return dgram; - case SSL_ERROR_ZERO_RETURN: - // "The connection was shut down cleanly" ... hmm, whatever, - // needs testing (DTLSTODO). - connectionWasShutdown = true; - setDtlsError(QDtlsError::RemoteClosedConnectionError, - QDtls::tr("The DTLS connection has been shutdown")); - dtls.reset(); - connectionEncrypted = false; - handshakeState = QDtls::HandshakeNotStarted; - return dgram; - case SSL_ERROR_SYSCALL: // some IO error - case SSL_ERROR_SSL: // error in the SSL library - // DTLSTODO: Apparently, some errors can be ignored, for example, - // ECONNRESET etc. This all needs a lot of testing!!! - default: - setDtlsError(QDtlsError::TlsNonFatalError, - QDtls::tr("Error while reading: %1") - .arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl())); - return dgram; - } -} - -unsigned QDtlsPrivateOpenSSL::pskClientCallback(const char *hint, char *identity, - unsigned max_identity_len, - unsigned char *psk, - unsigned max_psk_len) -{ - // The code below is taken (with some modifications) from qsslsocket_openssl - // - alas, we cannot simply re-use it, it's in QSslSocketPrivate. - { - QSslPreSharedKeyAuthenticator authenticator; - // Fill in some read-only fields (for client code) - if (hint) { - identityHint.clear(); - identityHint.append(hint); - } - - QTlsBackend::setupClientPskAuth(&authenticator, hint ? identityHint.constData() : nullptr, - hint ? int(std::strlen(hint)) : 0, max_identity_len, max_psk_len); - pskAuthenticator.swap(authenticator); - } - - // Let the client provide the remaining bits... - emit q->pskRequired(&pskAuthenticator); - - // No PSK set? Return now to make the handshake fail - if (pskAuthenticator.preSharedKey().isEmpty()) - return 0; - - // Copy data back into OpenSSL - const int identityLength = qMin(pskAuthenticator.identity().length(), - pskAuthenticator.maximumIdentityLength()); - std::memcpy(identity, pskAuthenticator.identity().constData(), identityLength); - identity[identityLength] = 0; - - const int pskLength = qMin(pskAuthenticator.preSharedKey().length(), - pskAuthenticator.maximumPreSharedKeyLength()); - std::memcpy(psk, pskAuthenticator.preSharedKey().constData(), pskLength); - - return pskLength; -} - -unsigned QDtlsPrivateOpenSSL::pskServerCallback(const char *identity, unsigned char *psk, - unsigned max_psk_len) -{ - { - QSslPreSharedKeyAuthenticator authenticator; - // Fill in some read-only fields (for the user) - QTlsBackend::setupServerPskAuth(&authenticator, identity, dtlsConfiguration.preSharedKeyIdentityHint(), - max_psk_len); - pskAuthenticator.swap(authenticator); - } - - // Let the client provide the remaining bits... - emit q->pskRequired(&pskAuthenticator); - - // No PSK set? Return now to make the handshake fail - if (pskAuthenticator.preSharedKey().isEmpty()) - return 0; - - // Copy data back into OpenSSL - const int pskLength = qMin(pskAuthenticator.preSharedKey().length(), - pskAuthenticator.maximumPreSharedKeyLength()); - - std::memcpy(psk, pskAuthenticator.preSharedKey().constData(), pskLength); - - return pskLength; -} - -bool QDtlsPrivateOpenSSL::verifyPeer() -{ - QList<QSslError> errors; - - // Check the whole chain for blacklisting (including root, as we check for - // subjectInfo and issuer) - const auto &peerCertificateChain = dtlsConfiguration.peerCertificateChain(); - for (const QSslCertificate &cert : peerCertificateChain) { - if (QSslCertificatePrivate::isBlacklisted(cert)) - errors << QSslError(QSslError::CertificateBlacklisted, cert); - } - - const auto peerCertificate = dtlsConfiguration.peerCertificate(); - if (peerCertificate.isNull()) { - errors << QSslError(QSslError::NoPeerCertificate); - } else if (mode == QSslSocket::SslClientMode) { - // Check the peer certificate itself. First try the subject's common name - // (CN) as a wildcard, then try all alternate subject name DNS entries the - // same way. - - // QSslSocket has a rather twisted logic: if verificationPeerName - // is empty, we call QAbstractSocket::peerName(), which returns - // either peerName (can be set by setPeerName) or host name - // (can be set as a result of connectToHost). - QString name = peerVfyName; - if (name.isEmpty()) { - Q_ASSERT(dtls.udpSocket); - name = dtls.udpSocket->peerName(); - } - - if (!QTlsPrivate::TlsCryptograph::isMatchingHostname(peerCertificate, name)) - errors << QSslError(QSslError::HostNameMismatch, peerCertificate); - } - - // Translate errors from the error list into QSslErrors - using CertClass = QTlsPrivate::X509CertificateOpenSSL; - errors.reserve(errors.size() + opensslErrors.size()); - for (const auto &error : qAsConst(opensslErrors)) { - const auto value = peerCertificateChain.value(error.depth); - errors << CertClass::openSSLErrorToQSslError(error.code, value); - } - - tlsErrors = errors; - return tlsErrors.isEmpty(); -} - -void QDtlsPrivateOpenSSL::storePeerCertificates() -{ - Q_ASSERT(dtls.tlsConnection.data()); - // Store the peer certificate and chain. For clients, the peer certificate - // chain includes the peer certificate; for servers, it doesn't. Both the - // 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()); - const auto peerCertificate = QTlsPrivate::X509CertificateOpenSSL::certificateFromX509(x509); - QTlsBackend::storePeerCertificate(dtlsConfiguration, peerCertificate); - q_X509_free(x509); - - auto peerCertificateChain = dtlsConfiguration.peerCertificateChain(); - if (peerCertificateChain.isEmpty()) { - auto stack = q_SSL_get_peer_cert_chain(dtls.tlsConnection.data()); - peerCertificateChain = QTlsPrivate::X509CertificateOpenSSL::stackOfX509ToQSslCertificates(stack); - if (!peerCertificate.isNull() && mode == QSslSocket::SslServerMode) - peerCertificateChain.prepend(peerCertificate); - QTlsBackend::storePeerCertificateChain(dtlsConfiguration, peerCertificateChain); - } -} - -bool QDtlsPrivateOpenSSL::tlsErrorsWereIgnored() const -{ - // check whether the errors we got are all in the list of expected errors - // (applies only if the method QDtlsConnection::ignoreTlsErrors(const - // QList<QSslError> &errors) was called) - for (const QSslError &error : tlsErrors) { - if (!tlsErrorsToIgnore.contains(error)) - return false; - } - - return !tlsErrorsToIgnore.empty(); -} - -void QDtlsPrivateOpenSSL::fetchNegotiatedParameters() -{ - Q_ASSERT(dtls.tlsConnection.data()); - - if (const SSL_CIPHER *cipher = q_SSL_get_current_cipher(dtls.tlsConnection.data())) - sessionCipher = QTlsBackendOpenSSL::qt_OpenSSL_cipher_to_QSslCipher(cipher); - else - sessionCipher = {}; - - // Note: cipher's protocol version will be reported as either TLS 1.0 or - // TLS 1.2, that's how it's set by OpenSSL (and that's what they are?). - - switch (q_SSL_version(dtls.tlsConnection.data())) { - case DTLS1_VERSION: - sessionProtocol = QSsl::DtlsV1_0; - break; - case DTLS1_2_VERSION: - sessionProtocol = QSsl::DtlsV1_2; - break; - default: - qCWarning(lcSsl, "unknown protocol version"); - sessionProtocol = QSsl::UnknownProtocol; - } -} - -void QDtlsPrivateOpenSSL::reportTimeout() -{ - emit q->handshakeTimeout(); -} - -void QDtlsPrivateOpenSSL::resetDtls() -{ - dtls.reset(); - connectionEncrypted = false; - tlsErrors.clear(); - tlsErrorsToIgnore.clear(); - QTlsBackend::clearPeerCertificates(dtlsConfiguration); - connectionWasShutdown = false; - handshakeState = QDtls::HandshakeNotStarted; - sessionCipher = {}; - sessionProtocol = QSsl::UnknownProtocol; -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qdtls_openssl_p.h b/src/network/ssl/qdtls_openssl_p.h deleted file mode 100644 index 281e1133cd..0000000000 --- a/src/network/ssl/qdtls_openssl_p.h +++ /dev/null @@ -1,251 +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$ -** -****************************************************************************/ - -#ifndef QDTLS_OPENSSL_P_H -#define QDTLS_OPENSSL_P_H - -#include <private/qtnetworkglobal_p.h> - -#include <QtCore/qglobal.h> - -#include <openssl/ossl_typ.h> - -#include "qtlsbackend_openssl_p.h" -#include "qtls_openssl_p.h" -#include "qdtls_base_p.h" -#include "qdtls_p.h" - -#include <private/qsslcontext_openssl_p.h> -#include <private/qopenssl_p.h> - -#include <QtNetwork/qsslpresharedkeyauthenticator.h> -#include <QtNetwork/qhostaddress.h> - -#include <QtCore/qbytearray.h> -#include <QtCore/qglobal.h> -#include <QtCore/qlist.h> -#include <QtCore/qsharedpointer.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. -// - -QT_REQUIRE_CONFIG(openssl); -QT_REQUIRE_CONFIG(dtls); - -QT_BEGIN_NAMESPACE - -class QDtlsPrivateOpenSSL; -class QDtlsBasePrivate; -class QUdpSocket; - -namespace dtlsopenssl -{ - -class DtlsState -{ -public: - // Note, bioMethod _must_ outlive BIOs it was used to create. Thus - // the order of declarations here matters. - using BioMethod = QSharedPointer<BIO_METHOD>; - BioMethod bioMethod; - - using TlsContext = QSharedPointer<QSslContext>; - TlsContext tlsContext; - - using TlsConnection = QSharedPointer<SSL>; - TlsConnection tlsConnection; - - QByteArray dgram; - - QHostAddress remoteAddress; - quint16 remotePort = 0; - - QList<QSslErrorEntry> x509Errors; - - long peeking = false; - QUdpSocket *udpSocket = nullptr; - bool writeSuppressed = false; - - bool init(QDtlsBasePrivate *dtlsBase, QUdpSocket *socket, - const QHostAddress &remote, quint16 port, - const QByteArray &receivedMessage); - - void reset(); - - QDtlsPrivateOpenSSL *dtlsPrivate = nullptr; - QByteArray secret; - -#ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 - QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha1; -#else - QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha256; -#endif - -private: - - bool initTls(QDtlsBasePrivate *dtlsBase); - bool initCtxAndConnection(QDtlsBasePrivate *dtlsBase); - bool initBIO(QDtlsBasePrivate *dtlsBase); - void setLinkMtu(QDtlsBasePrivate *dtlsBase); -}; - -} // namespace dtlsopenssl - -// The trick with 'right' ancestor in the tree overriding (only once) some shared -// virtual functions is intentional. Too bad MSVC warns me about ... exactly the -// feature of C++ that I want to use. - -QT_WARNING_PUSH -QT_WARNING_DISABLE_MSVC(4250) - -class QDtlsClientVerifierOpenSSL : public QTlsPrivate::DtlsCookieVerifier, public QDtlsBasePrivate -{ -public: - QDtlsClientVerifierOpenSSL(); - - bool verifyClient(QUdpSocket *socket, const QByteArray &dgram, - const QHostAddress &address, quint16 port) override; - QByteArray verifiedHello() const override; - -private: - dtlsopenssl::DtlsState dtls; - QByteArray verifiedClientHello; -}; - -class QDtlsPrivateOpenSSL : public QTlsPrivate::DtlsCryptograph, public QDtlsBasePrivate -{ -public: - - QDtlsPrivateOpenSSL(QDtls *qObject, QSslSocket::SslMode mode); - -private: - - QSslSocket::SslMode cryptographMode() const override; - void setPeer(const QHostAddress &addr, quint16 port, const QString &name) override; - QHostAddress peerAddress() const override; - quint16 peerPort() const override; - void setPeerVerificationName(const QString &name) override; - QString peerVerificationName() const override; - - virtual void setDtlsMtuHint(quint16 mtu) override; - virtual quint16 dtlsMtuHint() const override; - - virtual QDtls::HandshakeState state() const override; - virtual bool isConnectionEncrypted() const override; - - bool startHandshake(QUdpSocket *socket, const QByteArray &datagram) override; - bool continueHandshake(QUdpSocket *socket, const QByteArray &datagram) override; - bool resumeHandshake(QUdpSocket *socket) override; - void abortHandshake(QUdpSocket *socket) override; - bool handleTimeout(QUdpSocket *socket) override; - void sendShutdownAlert(QUdpSocket *socket) override; - - QList<QSslError> peerVerificationErrors() const override; - void ignoreVerificationErrors(const QList<QSslError> &errorsToIgnore) override; - - QSslCipher dtlsSessionCipher() const override; - QSsl::SslProtocol dtlsSessionProtocol() const override; - - qint64 writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &datagram) override; - QByteArray decryptDatagram(QUdpSocket *socket, const QByteArray &tlsdgram) override; - -public: - unsigned pskClientCallback(const char *hint, char *identity, unsigned max_identity_len, - unsigned char *psk, unsigned max_psk_len); - unsigned pskServerCallback(const char *identity, unsigned char *psk, - unsigned max_psk_len); - -private: - - bool verifyPeer(); - void storePeerCertificates(); - bool tlsErrorsWereIgnored() const; - void fetchNegotiatedParameters(); - void reportTimeout(); - void resetDtls(); - - QList<QSslErrorEntry> opensslErrors; - dtlsopenssl::DtlsState dtls; - - // We have to externally handle timeouts since we have non-blocking - // sockets and OpenSSL(DTLS) with non-blocking UDP sockets does not - // know if a timeout has occurred. - struct TimeoutHandler : QObject - { - TimeoutHandler() = default; - - void start(int hintMs = 0); - void doubleTimeout(); - void resetTimeout() {timeoutMs = 1000;} - void stop(); - void timerEvent(QTimerEvent *event) override; - - int timerId = -1; - int timeoutMs = 1000; - - QDtlsPrivateOpenSSL *dtlsConnection = nullptr; - }; - - QDtls *q = nullptr; - QDtls::HandshakeState handshakeState = QDtls::HandshakeNotStarted; - - QList<QSslError> tlsErrors; - QList<QSslError> tlsErrorsToIgnore; - bool connectionEncrypted = false; - // We will initialize it 'lazily', just in case somebody wants to move - // QDtls to another thread. - QScopedPointer<TimeoutHandler> timeoutHandler; - bool connectionWasShutdown = false; - QSslPreSharedKeyAuthenticator pskAuthenticator; - QByteArray identityHint; -}; - -QT_WARNING_POP // C4250 - -QT_END_NAMESPACE - -#endif // QDTLS_OPENSSL_P_H diff --git a/src/network/ssl/qopenssl.cpp b/src/network/ssl/qopenssl.cpp deleted file mode 100644 index 1453b4d881..0000000000 --- a/src/network/ssl/qopenssl.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Copyright (C) 2014 Governikus GmbH & Co. KG -** 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$ -** -****************************************************************************/ - -/**************************************************************************** -** -** In addition, as a special exception, the copyright holders listed above give -** permission to link the code of its release of Qt with the OpenSSL project's -** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the -** same license as the original version), and distribute the linked executables. -** -** You must comply with the GNU General Public License version 2 in all -** respects for all of the code used other than the "OpenSSL" code. If you -** modify this file, you may extend this exception to your version of the file, -** but you are not obligated to do so. If you do not wish to do so, delete -** this exception statement from your version of this file. -** -****************************************************************************/ - -#include "qtlsbackend_openssl_p.h" -#include "qopenssl_p.h" - -QT_BEGIN_NAMESPACE - -Q_GLOBAL_STATIC(QTlsBackendOpenSSL, backendOpenSsl) - -void QSslSocketPrivate::registerAdHocFactory() -{ - // TLSTODO: this is a temporary solution, waiting for - // backends to move to ... plugins. - if (!backendOpenSsl()) - qCWarning(lcSsl, "Failed to create backend factory"); -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qopenssl_p.h b/src/network/ssl/qopenssl_p.h deleted file mode 100644 index fa3efb2c28..0000000000 --- a/src/network/ssl/qopenssl_p.h +++ /dev/null @@ -1,117 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 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$ -** -****************************************************************************/ - -/**************************************************************************** -** -** In addition, as a special exception, the copyright holders listed above give -** permission to link the code of its release of Qt with the OpenSSL project's -** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the -** same license as the original version), and distribute the linked executables. -** -** You must comply with the GNU General Public License version 2 in all -** respects for all of the code used other than the "OpenSSL" code. If you -** modify this file, you may extend this exception to your version of the file, -** but you are not obligated to do so. If you do not wish to do so, delete -** this exception statement from your version of this file. -** -****************************************************************************/ - -#ifndef QSSLSOCKET_OPENSSL_P_H -#define QSSLSOCKET_OPENSSL_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 <QtNetwork/private/qtnetworkglobal_p.h> - -#include "qsslsocket_p.h" -#include "qsslcipher.h" - -#ifdef Q_OS_WIN -#include <qt_windows.h> -#if defined(OCSP_RESPONSE) -#undef OCSP_RESPONSE -#endif -#if defined(X509_NAME) -#undef X509_NAME -#endif -#endif // Q_OS_WIN - -// This file is included in several *.cpp files and provides different -// openssl declarations where they are needed. -#include <openssl/asn1.h> -#include <openssl/bio.h> -#include <openssl/bn.h> -#include <openssl/err.h> -#include <openssl/evp.h> -#include <openssl/pem.h> -#include <openssl/pkcs12.h> -#include <openssl/pkcs7.h> -#include <openssl/rand.h> -#include <openssl/ssl.h> -#include <openssl/stack.h> -#include <openssl/x509.h> -#include <openssl/x509v3.h> -#include <openssl/x509_vfy.h> -#include <openssl/dsa.h> -#include <openssl/rsa.h> -#include <openssl/crypto.h> -#include <openssl/tls1.h> -#include <openssl/dh.h> - -QT_BEGIN_NAMESPACE - -struct QSslErrorEntry { - int code = 0; - int depth = 0; -}; - -Q_DECLARE_TYPEINFO(QSslErrorEntry, Q_PRIMITIVE_TYPE); - -QT_END_NAMESPACE - -#endif diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp deleted file mode 100644 index de7ef15d10..0000000000 --- a/src/network/ssl/qsslcontext_openssl.cpp +++ /dev/null @@ -1,824 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Copyright (C) 2014 BlackBerry Limited. All rights reserved. -** Copyright (C) 2014 Governikus GmbH & Co. KG. -** Copyright (C) 2016 Richard J. Moore <rich@kde.org> -** 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 <QtNetwork/qsslsocket.h> -#include <QtNetwork/qssldiffiehellmanparameters.h> - -#include "private/qopenssl_p.h" -#include "private/qssl_p.h" -#include "private/qsslsocket_p.h" -#include "private/qsslcontext_openssl_p.h" -#include "private/qsslsocket_openssl_symbols_p.h" -#include "private/qssldiffiehellmanparameters_p.h" -#include "private/qtlsbackend_openssl_p.h" - -#include <vector> - -QT_BEGIN_NAMESPACE - -Q_GLOBAL_STATIC(bool, forceSecurityLevel) - -Q_NETWORK_EXPORT void qt_ForceTlsSecurityLevel() -{ - *forceSecurityLevel() = true; -} - -namespace QTlsPrivate -{ -// These callback functions are defined in qtls_openssl.cpp. -extern "C" int q_X509Callback(int ok, X509_STORE_CTX *ctx); -extern "C" int q_X509CallbackDirect(int ok, X509_STORE_CTX *ctx); - -#if QT_CONFIG(ocsp) -extern "C" int qt_OCSP_status_server_callback(SSL *ssl, void *); -#endif // ocsp - -} // namespace QTlsPrivate - -#if QT_CONFIG(dtls) -// defined in qdtls_openssl.cpp: -namespace dtlscallbacks -{ -extern "C" int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx); -extern "C" int q_generate_cookie_callback(SSL *ssl, unsigned char *dst, - unsigned *cookieLength); -extern "C" int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie, - unsigned cookieLength); -} -#endif // dtls - -#ifdef TLS1_3_VERSION -extern "C" int q_ssl_sess_set_new_cb(SSL *context, SSL_SESSION *session); -#endif // TLS1_3_VERSION - -static inline QString msgErrorSettingBackendConfig(const QString &why) -{ - return QSslSocket::tr("Error when setting the OpenSSL configuration (%1)").arg(why); -} - -static inline QString msgErrorSettingEllipticCurves(const QString &why) -{ - return QSslSocket::tr("Error when setting the elliptic curves (%1)").arg(why); -} - -long QSslContext::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions) -{ - long options; - switch (protocol) { - case QSsl::SecureProtocols: - case QSsl::TlsV1_0OrLater: - options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; - break; - case QSsl::TlsV1_1OrLater: - options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1; - break; - case QSsl::TlsV1_2OrLater: - options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1; - break; - case QSsl::TlsV1_3OrLater: - options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2; - break; - default: - options = SSL_OP_ALL; - } - - // This option is disabled by default, so we need to be able to clear it - if (sslOptions & QSsl::SslOptionDisableEmptyFragments) - options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; - else - options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; - -#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION - // This option is disabled by default, so we need to be able to clear it - if (sslOptions & QSsl::SslOptionDisableLegacyRenegotiation) - options &= ~SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; - else - options |= SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; -#endif - -#ifdef SSL_OP_NO_TICKET - if (sslOptions & QSsl::SslOptionDisableSessionTickets) - options |= SSL_OP_NO_TICKET; -#endif -#ifdef SSL_OP_NO_COMPRESSION - if (sslOptions & QSsl::SslOptionDisableCompression) - options |= SSL_OP_NO_COMPRESSION; -#endif - - if (!(sslOptions & QSsl::SslOptionDisableServerCipherPreference)) - options |= SSL_OP_CIPHER_SERVER_PREFERENCE; - - return options; -} - -QSslContext::QSslContext() - : ctx(nullptr), - pkey(nullptr), - session(nullptr), - m_sessionTicketLifeTimeHint(-1) -{ -} - -QSslContext::~QSslContext() -{ - if (ctx) - // This will decrement the reference count by 1 and free the context eventually when possible - q_SSL_CTX_free(ctx); - - if (pkey) - q_EVP_PKEY_free(pkey); - - if (session) - q_SSL_SESSION_free(session); -} - -QSslContext* QSslContext::fromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration, bool allowRootCertOnDemandLoading) -{ - QSslContext *sslContext = new QSslContext(); - initSslContext(sslContext, mode, configuration, allowRootCertOnDemandLoading); - return sslContext; -} - -QSharedPointer<QSslContext> QSslContext::sharedFromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration, bool allowRootCertOnDemandLoading) -{ - QSharedPointer<QSslContext> sslContext = QSharedPointer<QSslContext>::create(); - initSslContext(sslContext.data(), mode, configuration, allowRootCertOnDemandLoading); - return sslContext; -} - -QSharedPointer<QSslContext> QSslContext::sharedFromPrivateConfiguration(QSslSocket::SslMode mode, QSslConfigurationPrivate *privConfiguration, - bool allowRootCertOnDemandLoading) -{ - return sharedFromConfiguration(mode, privConfiguration, allowRootCertOnDemandLoading); -} - -#ifndef OPENSSL_NO_NEXTPROTONEG - -static int next_proto_cb(SSL *, unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen, void *arg) -{ - QSslContext::NPNContext *ctx = reinterpret_cast<QSslContext::NPNContext *>(arg); - - // comment out to debug: -// QList<QByteArray> supportedVersions; -// for (unsigned int i = 0; i < inlen; ) { -// QByteArray version(reinterpret_cast<const char *>(&in[i+1]), in[i]); -// supportedVersions << version; -// i += in[i] + 1; -// } - - int proto = q_SSL_select_next_proto(out, outlen, in, inlen, ctx->data, ctx->len); - switch (proto) { - case OPENSSL_NPN_UNSUPPORTED: - ctx->status = QSslConfiguration::NextProtocolNegotiationNone; - break; - case OPENSSL_NPN_NEGOTIATED: - ctx->status = QSslConfiguration::NextProtocolNegotiationNegotiated; - break; - case OPENSSL_NPN_NO_OVERLAP: - ctx->status = QSslConfiguration::NextProtocolNegotiationUnsupported; - break; - default: - qCWarning(lcSsl, "OpenSSL sent unknown NPN status"); - } - - return SSL_TLSEXT_ERR_OK; -} - -QSslContext::NPNContext QSslContext::npnContext() const -{ - return m_npnContext; -} -#endif // !OPENSSL_NO_NEXTPROTONEG - - - -// Needs to be deleted by caller -SSL* QSslContext::createSsl() -{ - SSL* ssl = q_SSL_new(ctx); - q_SSL_clear(ssl); - - if (!session && !sessionASN1().isEmpty() - && !sslConfiguration.testSslOption(QSsl::SslOptionDisableSessionPersistence)) { - const unsigned char *data = reinterpret_cast<const unsigned char *>(m_sessionASN1.constData()); - session = q_d2i_SSL_SESSION(nullptr, &data, m_sessionASN1.size()); - // 'session' has refcount 1 already, set by the function above - } - - if (session) { - // Try to resume the last session we cached - if (!q_SSL_set_session(ssl, session)) { - qCWarning(lcSsl, "could not set SSL session"); - q_SSL_SESSION_free(session); - session = nullptr; - } - } - -#ifndef OPENSSL_NO_NEXTPROTONEG - QList<QByteArray> protocols = sslConfiguration.d.constData()->nextAllowedProtocols; - if (!protocols.isEmpty()) { - m_supportedNPNVersions.clear(); - for (int a = 0; a < protocols.count(); ++a) { - if (protocols.at(a).size() > 255) { - qCWarning(lcSsl) << "TLS NPN extension" << protocols.at(a) - << "is too long and will be ignored."; - continue; - } else if (protocols.at(a).isEmpty()) { - continue; - } - m_supportedNPNVersions.append(protocols.at(a).size()).append(protocols.at(a)); - } - if (m_supportedNPNVersions.size()) { - m_npnContext.data = reinterpret_cast<unsigned char *>(m_supportedNPNVersions.data()); - m_npnContext.len = m_supportedNPNVersions.count(); - m_npnContext.status = QSslConfiguration::NextProtocolNegotiationNone; - // Callback's type has a parameter 'const unsigned char ** out' - // since it was introduced in 1.0.2. Internally, OpenSSL's own code - // (tests/examples) cast it to unsigned char * (since it's 'out'). - // We just re-use our NPN callback and cast here: - typedef int (*alpn_callback_t) (SSL *, const unsigned char **, unsigned char *, - const unsigned char *, unsigned int, void *); - // With ALPN callback is for a server side only, for a client m_npnContext.status - // will stay in NextProtocolNegotiationNone. - q_SSL_CTX_set_alpn_select_cb(ctx, alpn_callback_t(next_proto_cb), &m_npnContext); - // Client: - q_SSL_set_alpn_protos(ssl, m_npnContext.data, m_npnContext.len); - // And in case our peer does not support ALPN, but supports NPN: - q_SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &m_npnContext); - } - } -#endif // !OPENSSL_NO_NEXTPROTONEG - - return ssl; -} - -// We cache exactly one session here -bool QSslContext::cacheSession(SSL* ssl) -{ - // don't cache the same session again - if (session && session == q_SSL_get_session(ssl)) - return true; - - // decrease refcount of currently stored session - // (this might happen if there are several concurrent handshakes in flight) - if (session) - q_SSL_SESSION_free(session); - - // cache the session the caller gave us and increase reference count - session = q_SSL_get1_session(ssl); - - if (session && !sslConfiguration.testSslOption(QSsl::SslOptionDisableSessionPersistence)) { - int sessionSize = q_i2d_SSL_SESSION(session, nullptr); - if (sessionSize > 0) { - m_sessionASN1.resize(sessionSize); - unsigned char *data = reinterpret_cast<unsigned char *>(m_sessionASN1.data()); - if (!q_i2d_SSL_SESSION(session, &data)) - qCWarning(lcSsl, "could not store persistent version of SSL session"); - m_sessionTicketLifeTimeHint = q_SSL_SESSION_get_ticket_lifetime_hint(session); - } - } - - return (session != nullptr); -} - -QByteArray QSslContext::sessionASN1() const -{ - return m_sessionASN1; -} - -void QSslContext::setSessionASN1(const QByteArray &session) -{ - m_sessionASN1 = session; -} - -int QSslContext::sessionTicketLifeTimeHint() const -{ - return m_sessionTicketLifeTimeHint; -} - -QSslError::SslError QSslContext::error() const -{ - return errorCode; -} - -QString QSslContext::errorString() const -{ - return errorStr; -} - -void QSslContext::initSslContext(QSslContext *sslContext, QSslSocket::SslMode mode, - const QSslConfiguration &configuration, - bool allowRootCertOnDemandLoading) -{ - sslContext->sslConfiguration = configuration; - sslContext->errorCode = QSslError::NoError; - - bool client = (mode == QSslSocket::SslClientMode); - - bool reinitialized = false; - bool unsupportedProtocol = false; - bool isDtls = false; -init_context: - switch (sslContext->sslConfiguration.protocol()) { - case QSsl::DtlsV1_0: - case QSsl::DtlsV1_0OrLater: - case QSsl::DtlsV1_2: - case QSsl::DtlsV1_2OrLater: -#if QT_CONFIG(dtls) - isDtls = true; - sslContext->ctx = q_SSL_CTX_new(client ? q_DTLS_client_method() : q_DTLS_server_method()); -#else // dtls - sslContext->ctx = nullptr; - unsupportedProtocol = true; - qCWarning(lcSsl, "DTLS protocol requested, but feature 'dtls' is disabled"); -#endif // dtls - break; - case QSsl::TlsV1_3: - case QSsl::TlsV1_3OrLater: -#if !defined(TLS1_3_VERSION) - qCWarning(lcSsl, "TLS 1.3 is not supported"); - sslContext->ctx = nullptr; - unsupportedProtocol = true; - break; -#endif // TLS1_3_VERSION - default: - // The ssl options will actually control the supported methods - sslContext->ctx = q_SSL_CTX_new(client ? q_TLS_client_method() : q_TLS_server_method()); - } - - if (!sslContext->ctx) { - // After stopping Flash 10 the SSL library loses its ciphers. Try re-adding them - // by re-initializing the library. - if (!reinitialized) { - reinitialized = true; - if (q_OPENSSL_init_ssl(0, nullptr) == 1) - goto init_context; - } - - sslContext->errorStr = QSslSocket::tr("Error creating SSL context (%1)").arg( - unsupportedProtocol ? QSslSocket::tr("unsupported protocol") : QTlsBackendOpenSSL::getErrorsFromOpenSsl() - ); - sslContext->errorCode = QSslError::UnspecifiedError; - return; - } - - // A nasty hacked OpenSSL using a level that will make our auto-tests fail: - if (q_SSL_CTX_get_security_level(sslContext->ctx) > 1 && *forceSecurityLevel()) - q_SSL_CTX_set_security_level(sslContext->ctx, 1); - - const long anyVersion = -#if QT_CONFIG(dtls) - isDtls ? DTLS_ANY_VERSION : TLS_ANY_VERSION; -#else - TLS_ANY_VERSION; -#endif // dtls - long minVersion = anyVersion; - long maxVersion = anyVersion; - - switch (sslContext->sslConfiguration.protocol()) { - case QSsl::TlsV1_0: - minVersion = TLS1_VERSION; - maxVersion = TLS1_VERSION; - break; - case QSsl::TlsV1_1: - minVersion = TLS1_1_VERSION; - maxVersion = TLS1_1_VERSION; - break; - case QSsl::TlsV1_2: - minVersion = TLS1_2_VERSION; - maxVersion = TLS1_2_VERSION; - break; - case QSsl::TlsV1_3: -#ifdef TLS1_3_VERSION - minVersion = TLS1_3_VERSION; - maxVersion = TLS1_3_VERSION; -#else - // This protocol is not supported by OpenSSL 1.1 and we handle - // it as an error (see the code above). - Q_UNREACHABLE(); -#endif // TLS1_3_VERSION - break; - // Ranges: - case QSsl::AnyProtocol: - case QSsl::SecureProtocols: - case QSsl::TlsV1_0OrLater: - minVersion = TLS1_VERSION; - maxVersion = 0; - break; - case QSsl::TlsV1_1OrLater: - minVersion = TLS1_1_VERSION; - maxVersion = 0; - break; - case QSsl::TlsV1_2OrLater: - minVersion = TLS1_2_VERSION; - maxVersion = 0; - break; - case QSsl::DtlsV1_0: - minVersion = DTLS1_VERSION; - maxVersion = DTLS1_VERSION; - break; - case QSsl::DtlsV1_0OrLater: - minVersion = DTLS1_VERSION; - maxVersion = DTLS_MAX_VERSION; - break; - case QSsl::DtlsV1_2: - minVersion = DTLS1_2_VERSION; - maxVersion = DTLS1_2_VERSION; - break; - case QSsl::DtlsV1_2OrLater: - minVersion = DTLS1_2_VERSION; - maxVersion = DTLS_MAX_VERSION; - break; - case QSsl::TlsV1_3OrLater: -#ifdef TLS1_3_VERSION - minVersion = TLS1_3_VERSION; - maxVersion = 0; - break; -#else - // This protocol is not supported by OpenSSL 1.1 and we handle - // it as an error (see the code above). - Q_UNREACHABLE(); - break; -#endif // TLS1_3_VERSION - case QSsl::UnknownProtocol: - break; - } - - if (minVersion != anyVersion - && !q_SSL_CTX_set_min_proto_version(sslContext->ctx, minVersion)) { - sslContext->errorStr = QSslSocket::tr("Error while setting the minimal protocol version"); - sslContext->errorCode = QSslError::UnspecifiedError; - return; - } - - if (maxVersion != anyVersion - && !q_SSL_CTX_set_max_proto_version(sslContext->ctx, maxVersion)) { - sslContext->errorStr = QSslSocket::tr("Error while setting the maximum protocol version"); - sslContext->errorCode = QSslError::UnspecifiedError; - return; - } - - // Enable bug workarounds. - const long options = setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions); - q_SSL_CTX_set_options(sslContext->ctx, options); - - // Tell OpenSSL to release memory early - // http://www.openssl.org/docs/ssl/SSL_CTX_set_mode.html - q_SSL_CTX_set_mode(sslContext->ctx, SSL_MODE_RELEASE_BUFFERS); - - auto filterCiphers = [](const QList<QSslCipher> &ciphers, bool selectTls13) - { - QByteArray cipherString; - - for (const QSslCipher &cipher : ciphers) { - const bool isTls13Cipher = cipher.protocol() == QSsl::TlsV1_3 || cipher.protocol() == QSsl::TlsV1_3OrLater; - if (selectTls13 != isTls13Cipher) - continue; - - if (cipherString.size()) - cipherString.append(':'); - cipherString.append(cipher.name().toLatin1()); - } - return cipherString; - }; - - // Initialize ciphers - QList<QSslCipher> ciphers = sslContext->sslConfiguration.ciphers(); - if (ciphers.isEmpty()) - ciphers = isDtls ? QTlsBackend::defaultDtlsCiphers() : QTlsBackend::defaultCiphers(); - - const QByteArray preTls13Ciphers = filterCiphers(ciphers, false); - - if (preTls13Ciphers.size()) { - if (!q_SSL_CTX_set_cipher_list(sslContext->ctx, preTls13Ciphers.data())) { - sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()); - sslContext->errorCode = QSslError::UnspecifiedError; - return; - } - } - - const QByteArray tls13Ciphers = filterCiphers(ciphers, true); -#ifdef TLS1_3_VERSION - if (tls13Ciphers.size()) { - if (!q_SSL_CTX_set_ciphersuites(sslContext->ctx, tls13Ciphers.data())) { - sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()); - sslContext->errorCode = QSslError::UnspecifiedError; - return; - } - } -#endif // TLS1_3_VERSION - if (!preTls13Ciphers.size() && !tls13Ciphers.size()) { - sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QStringLiteral("")); - sslContext->errorCode = QSslError::UnspecifiedError; - return; - } - - const QDateTime now = QDateTime::currentDateTimeUtc(); - - // Add all our CAs to this store. - const auto caCertificates = sslContext->sslConfiguration.caCertificates(); - 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: QSslSocketBackendPrivate::verify() - if (caCertificate.expiryDate() >= now) { - q_X509_STORE_add_cert(q_SSL_CTX_get_cert_store(sslContext->ctx), (X509 *)caCertificate.handle()); - } - } - - if (QSslSocketPrivate::rootCertOnDemandLoadingSupported() && allowRootCertOnDemandLoading) { - // tell OpenSSL the directories where to look up the root certs on demand - const QList<QByteArray> unixDirs = QSslSocketPrivate::unixRootCertDirectories(); - int success = 1; -#if OPENSSL_VERSION_MAJOR < 3 - for (const QByteArray &unixDir : unixDirs) { - if ((success = q_SSL_CTX_load_verify_locations(sslContext->ctx, nullptr, unixDir.constData())) != 1) - break; - } -#else - for (const QByteArray &unixDir : unixDirs) { - if ((success = q_SSL_CTX_load_verify_dir(sslContext->ctx, unixDir.constData())) != 1) - break; - } -#endif // OPENSSL_VERSION_MAJOR - if (success != 1) { - const auto qtErrors = QTlsBackendOpenSSL::getErrorsFromOpenSsl(); - qCWarning(lcSsl) << "An error encountered while to set root certificates location:" - << qtErrors; - } - } - - if (!sslContext->sslConfiguration.localCertificate().isNull()) { - // Require a private key as well. - if (sslContext->sslConfiguration.privateKey().isNull()) { - sslContext->errorStr = QSslSocket::tr("Cannot provide a certificate with no key"); - sslContext->errorCode = QSslError::UnspecifiedError; - return; - } - - // Load certificate - if (!q_SSL_CTX_use_certificate(sslContext->ctx, (X509 *)sslContext->sslConfiguration.localCertificate().handle())) { - sslContext->errorStr = QSslSocket::tr("Error loading local certificate, %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()); - sslContext->errorCode = QSslError::UnspecifiedError; - return; - } - - if (configuration.d->privateKey.algorithm() == QSsl::Opaque) { - sslContext->pkey = reinterpret_cast<EVP_PKEY *>(configuration.d->privateKey.handle()); - } else { - // Load private key - sslContext->pkey = q_EVP_PKEY_new(); - // before we were using EVP_PKEY_assign_R* functions and did not use EVP_PKEY_free. - // this lead to a memory leak. Now we use the *_set1_* functions which do not - // take ownership of the RSA/DSA key instance because the QSslKey already has ownership. - if (configuration.d->privateKey.algorithm() == QSsl::Rsa) - q_EVP_PKEY_set1_RSA(sslContext->pkey, reinterpret_cast<RSA *>(configuration.d->privateKey.handle())); - else if (configuration.d->privateKey.algorithm() == QSsl::Dsa) - q_EVP_PKEY_set1_DSA(sslContext->pkey, reinterpret_cast<DSA *>(configuration.d->privateKey.handle())); -#ifndef OPENSSL_NO_EC - else if (configuration.d->privateKey.algorithm() == QSsl::Ec) - q_EVP_PKEY_set1_EC_KEY(sslContext->pkey, reinterpret_cast<EC_KEY *>(configuration.d->privateKey.handle())); -#endif - } - auto pkey = sslContext->pkey; - if (configuration.d->privateKey.algorithm() == QSsl::Opaque) - sslContext->pkey = nullptr; // Don't free the private key, it belongs to QSslKey - - if (!q_SSL_CTX_use_PrivateKey(sslContext->ctx, pkey)) { - sslContext->errorStr = QSslSocket::tr("Error loading private key, %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()); - sslContext->errorCode = QSslError::UnspecifiedError; - return; - } - - // Check if the certificate matches the private key. - if (!q_SSL_CTX_check_private_key(sslContext->ctx)) { - sslContext->errorStr = QSslSocket::tr("Private key does not certify public key, %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()); - sslContext->errorCode = QSslError::UnspecifiedError; - return; - } - - // If we have any intermediate certificates then we need to add them to our chain - bool first = true; - for (const QSslCertificate &cert : qAsConst(configuration.d->localCertificateChain)) { - if (first) { - first = false; - continue; - } - q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_EXTRA_CHAIN_CERT, 0, - q_X509_dup(reinterpret_cast<X509 *>(cert.handle()))); - } - } - - // Initialize peer verification, different callbacks, TLS/DTLS verification first - // (note, all these set_some_callback do not have return value): - if (sslContext->sslConfiguration.peerVerifyMode() == QSslSocket::VerifyNone) { - q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_NONE, nullptr); - } else { - auto verificationCallback = - #if QT_CONFIG(dtls) - isDtls ? dtlscallbacks::q_X509DtlsCallback : - #endif // dtls - QTlsPrivate::q_X509Callback; - - if (!isDtls && configuration.handshakeMustInterruptOnError()) - verificationCallback = QTlsPrivate::q_X509CallbackDirect; - - auto verificationMode = SSL_VERIFY_PEER; - if (!isDtls && sslContext->sslConfiguration.missingCertificateIsFatal()) - verificationMode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; - - q_SSL_CTX_set_verify(sslContext->ctx, verificationMode, verificationCallback); - } - -#ifdef TLS1_3_VERSION - // NewSessionTicket callback: - if (mode == QSslSocket::SslClientMode && !isDtls) { - q_SSL_CTX_sess_set_new_cb(sslContext->ctx, q_ssl_sess_set_new_cb); - q_SSL_CTX_set_session_cache_mode(sslContext->ctx, SSL_SESS_CACHE_CLIENT); - } - -#endif // TLS1_3_VERSION - -#if QT_CONFIG(dtls) - // DTLS cookies: - if (mode == QSslSocket::SslServerMode && isDtls && configuration.dtlsCookieVerificationEnabled()) { - q_SSL_CTX_set_cookie_generate_cb(sslContext->ctx, dtlscallbacks::q_generate_cookie_callback); - q_SSL_CTX_set_cookie_verify_cb(sslContext->ctx, dtlscallbacks::q_verify_cookie_callback); - } -#endif // dtls - - // Set verification depth. - if (sslContext->sslConfiguration.peerVerifyDepth() != 0) - q_SSL_CTX_set_verify_depth(sslContext->ctx, sslContext->sslConfiguration.peerVerifyDepth()); - - // set persisted session if the user set it - if (!configuration.sessionTicket().isEmpty()) - sslContext->setSessionASN1(configuration.sessionTicket()); - - // Set temp DH params - QSslDiffieHellmanParameters dhparams = configuration.diffieHellmanParameters(); - - if (!dhparams.isValid()) { - sslContext->errorStr = QSslSocket::tr("Diffie-Hellman parameters are not valid"); - sslContext->errorCode = QSslError::UnspecifiedError; - return; - } - - if (!dhparams.isEmpty()) { - const QByteArray ¶ms = dhparams.d->derData; - const char *ptr = params.constData(); - DH *dh = q_d2i_DHparams(nullptr, reinterpret_cast<const unsigned char **>(&ptr), - params.length()); - if (dh == nullptr) - qFatal("q_d2i_DHparams failed to convert QSslDiffieHellmanParameters to DER form"); - q_SSL_CTX_set_tmp_dh(sslContext->ctx, dh); - q_DH_free(dh); - } - -#ifndef OPENSSL_NO_PSK - if (!client) - q_SSL_CTX_use_psk_identity_hint(sslContext->ctx, sslContext->sslConfiguration.preSharedKeyIdentityHint().constData()); -#endif // !OPENSSL_NO_PSK - - const auto qcurves = sslContext->sslConfiguration.ellipticCurves(); - if (!qcurves.isEmpty()) { -#ifdef OPENSSL_NO_EC - sslContext->errorStr = msgErrorSettingEllipticCurves(QSslSocket::tr("OpenSSL version with disabled elliptic curves")); - sslContext->errorCode = QSslError::UnspecifiedError; - return; -#else - // Set the curves to be used. - std::vector<int> curves; - curves.reserve(qcurves.size()); - for (const auto &sslCurve : qcurves) - curves.push_back(sslCurve.id); - if (!q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_SET_CURVES, long(curves.size()), &curves[0])) { - sslContext->errorStr = msgErrorSettingEllipticCurves(QTlsBackendOpenSSL::getErrorsFromOpenSsl()); - sslContext->errorCode = QSslError::UnspecifiedError; - return; - } -#endif - } - - applyBackendConfig(sslContext); -} - -void QSslContext::applyBackendConfig(QSslContext *sslContext) -{ - const QMap<QByteArray, QVariant> &conf = sslContext->sslConfiguration.backendConfiguration(); - if (conf.isEmpty()) - return; - -#if QT_CONFIG(ocsp) - auto ocspResponsePos = conf.find("Qt-OCSP-response"); - if (ocspResponsePos != conf.end()) { - // This is our private, undocumented configuration option, existing only for - // the purpose of testing OCSP status responses. We don't even check this - // callback was set. If no - the test must fail. - q_SSL_CTX_set_tlsext_status_cb(sslContext->ctx, QTlsPrivate::qt_OCSP_status_server_callback); - if (conf.size() == 1) - return; - } -#endif // ocsp - - QSharedPointer<SSL_CONF_CTX> cctx(q_SSL_CONF_CTX_new(), &q_SSL_CONF_CTX_free); - if (cctx) { - q_SSL_CONF_CTX_set_ssl_ctx(cctx.data(), sslContext->ctx); - q_SSL_CONF_CTX_set_flags(cctx.data(), SSL_CONF_FLAG_FILE); - - for (auto i = conf.constBegin(); i != conf.constEnd(); ++i) { - if (i.key() == "Qt-OCSP-response") // This never goes to SSL_CONF_cmd(). - continue; - - if (!i.value().canConvert(QMetaType(QMetaType::QByteArray))) { - sslContext->errorCode = QSslError::UnspecifiedError; - sslContext->errorStr = msgErrorSettingBackendConfig( - QSslSocket::tr("Expecting QByteArray for %1").arg( - QString::fromUtf8(i.key()))); - return; - } - - const QByteArray &value = i.value().toByteArray(); - const int result = q_SSL_CONF_cmd(cctx.data(), i.key().constData(), value.constData()); - if (result == 2) - continue; - - sslContext->errorCode = QSslError::UnspecifiedError; - switch (result) { - case 0: - sslContext->errorStr = msgErrorSettingBackendConfig( - QSslSocket::tr("An error occurred attempting to set %1 to %2").arg( - QString::fromUtf8(i.key()), QString::fromUtf8(value))); - return; - case 1: - sslContext->errorStr = msgErrorSettingBackendConfig( - QSslSocket::tr("Wrong value for %1 (%2)").arg( - QString::fromUtf8(i.key()), QString::fromUtf8(value))); - return; - default: - sslContext->errorStr = msgErrorSettingBackendConfig( - QSslSocket::tr("Unrecognized command %1 = %2").arg( - QString::fromUtf8(i.key()), QString::fromUtf8(value))); - return; - } - } - - if (q_SSL_CONF_CTX_finish(cctx.data()) == 0) { - sslContext->errorStr = msgErrorSettingBackendConfig(QSslSocket::tr("SSL_CONF_finish() failed")); - sslContext->errorCode = QSslError::UnspecifiedError; - } - } else { - sslContext->errorStr = msgErrorSettingBackendConfig(QSslSocket::tr("SSL_CONF_CTX_new() failed")); - sslContext->errorCode = QSslError::UnspecifiedError; - } -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslcontext_openssl_p.h b/src/network/ssl/qsslcontext_openssl_p.h deleted file mode 100644 index 4b343e7842..0000000000 --- a/src/network/ssl/qsslcontext_openssl_p.h +++ /dev/null @@ -1,132 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2014 BlackBerry Limited. All rights reserved. -** 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 QSSLCONTEXT_OPENSSL_P_H -#define QSSLCONTEXT_OPENSSL_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 <QtNetwork/private/qtnetworkglobal_p.h> -#include <QtCore/qvariant.h> -#include <QtNetwork/qsslcertificate.h> -#include <QtNetwork/qsslconfiguration.h> -#include <openssl/ssl.h> - -QT_BEGIN_NAMESPACE - -#ifndef QT_NO_SSL - -class QSslContext -{ -public: - - ~QSslContext(); - - static QSslContext* fromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration, - bool allowRootCertOnDemandLoading); - static QSharedPointer<QSslContext> sharedFromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration, - bool allowRootCertOnDemandLoading); - static QSharedPointer<QSslContext> sharedFromPrivateConfiguration(QSslSocket::SslMode mode, QSslConfigurationPrivate *privConfiguration, - bool allowRootCertOnDemandLoading); - static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions); - - QSslError::SslError error() const; - QString errorString() const; - - SSL* createSsl(); - bool cacheSession(SSL*); // should be called when handshake completed - - QByteArray sessionASN1() const; - void setSessionASN1(const QByteArray &sessionASN1); - int sessionTicketLifeTimeHint() const; - -#ifndef OPENSSL_NO_NEXTPROTONEG - // must be public because we want to use it from an OpenSSL callback - struct NPNContext { - NPNContext() : data(nullptr), - len(0), - status(QSslConfiguration::NextProtocolNegotiationNone) - { } - unsigned char *data; - unsigned short len; - QSslConfiguration::NextProtocolNegotiationStatus status; - }; - NPNContext npnContext() const; -#endif // !OPENSSL_NO_NEXTPROTONEG - -protected: - QSslContext(); - friend class QSharedPointer<QSslContext>; - -private: - static void initSslContext(QSslContext* sslContext, QSslSocket::SslMode mode, const QSslConfiguration &configuration, - bool allowRootCertOnDemandLoading); - static void applyBackendConfig(QSslContext *sslContext); - -private: - SSL_CTX* ctx; - EVP_PKEY *pkey; - SSL_SESSION *session; - QByteArray m_sessionASN1; - int m_sessionTicketLifeTimeHint; - QSslError::SslError errorCode; - QString errorStr; - QSslConfiguration sslConfiguration; -#ifndef OPENSSL_NO_NEXTPROTONEG - QByteArray m_supportedNPNVersions; - NPNContext m_npnContext; -#endif // !OPENSSL_NO_NEXTPROTONEG -}; - -#endif // QT_NO_SSL - -QT_END_NAMESPACE - -#endif // QSSLCONTEXT_OPENSSL_P_H diff --git a/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp b/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp deleted file mode 100644 index 8e0458de4e..0000000000 --- a/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk> -** Copyright (C) 2016 Richard J. Moore <rich@kde.org> -** 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 "qsslsocket_openssl_symbols_p.h" -#include "qtlsbackend_openssl_p.h" -#include "qsslsocket_p.h" - -#include <QtCore/qscopeguard.h> -#include <QtCore/qbytearray.h> -#include <QtCore/qiodevice.h> -#include <QtCore/qdebug.h> - -#include <openssl/bn.h> -#include <openssl/dh.h> - -QT_BEGIN_NAMESPACE - -namespace { - -#ifdef OPENSSL_NO_DEPRECATED_3_0 - -int q_DH_check(DH *dh, int *status) -{ - // DH_check was first deprecated in OpenSSL 3.0.0, as low-level - // API; the EVP_PKEY family of functions was advised as an alternative. - // As of now EVP_PKEY_params_check ends up calling ... DH_check, - // which is good enough. - - Q_ASSERT(dh); - Q_ASSERT(status); - - EVP_PKEY *key = q_EVP_PKEY_new(); - if (!key) { - qCWarning(lcSsl, "EVP_PKEY_new failed"); - QTlsBackendOpenSSL::logAndClearErrorQueue(); - return 0; - } - const auto keyDeleter = qScopeGuard([key](){ - q_EVP_PKEY_free(key); - }); - if (!q_EVP_PKEY_set1_DH(key, dh)) { - qCWarning(lcTlsBackend, "EVP_PKEY_set1_DH failed"); - QTlsBackendOpenSSL::logAndClearErrorQueue(); - return 0; - } - - EVP_PKEY_CTX *keyCtx = q_EVP_PKEY_CTX_new(key, nullptr); - if (!keyCtx) { - qCWarning(lcTlsBackend, "EVP_PKEY_CTX_new failed"); - QTlsBackendOpenSSL::logAndClearErrorQueue(); - return 0; - } - const auto ctxDeleter = qScopeGuard([keyCtx]{ - q_EVP_PKEY_CTX_free(keyCtx); - }); - - const int result = q_EVP_PKEY_param_check(keyCtx); - QTlsBackendOpenSSL::logAndClearErrorQueue(); - // Note: unlike DH_check, we cannot obtain the 'status', - // if the 'result' is 0 (actually the result is 1 only - // if this 'status' was 0). We could probably check the - // errors from the error queue, but it's not needed anyway - // - see the 'isSafeDH' below, how it returns immediately - // on 0. - Q_UNUSED(status); - - return result; -} -#endif // OPENSSL_NO_DEPRECATED_3_0 - -bool isSafeDH(DH *dh) -{ - int status = 0; - int bad = 0; - - // TLSTODO: check it's needed or if supportsSsl() - // is enough. - QSslSocketPrivate::ensureInitialized(); - - // From https://wiki.openssl.org/index.php/Diffie-Hellman_parameters: - // - // The additional call to BN_mod_word(dh->p, 24) - // (and unmasking of DH_NOT_SUITABLE_GENERATOR) - // is performed to ensure your program accepts - // IETF group parameters. OpenSSL checks the prime - // is congruent to 11 when g = 2; while the IETF's - // primes are congruent to 23 when g = 2. - // Without the test, the IETF parameters would - // fail validation. For details, see Diffie-Hellman - // Parameter Check (when g = 2, must p mod 24 == 11?). - // Mark p < 1024 bits as unsafe. - if (q_DH_bits(dh) < 1024) - return false; - - if (q_DH_check(dh, &status) != 1) - return false; - - const BIGNUM *p = nullptr; - const BIGNUM *q = nullptr; - const BIGNUM *g = nullptr; - q_DH_get0_pqg(dh, &p, &q, &g); - - if (q_BN_is_word(const_cast<BIGNUM *>(g), DH_GENERATOR_2)) { - const unsigned long residue = q_BN_mod_word(p, 24); - if (residue == 11 || residue == 23) - status &= ~DH_NOT_SUITABLE_GENERATOR; - } - - bad |= DH_CHECK_P_NOT_PRIME; - bad |= DH_CHECK_P_NOT_SAFE_PRIME; - bad |= DH_NOT_SUITABLE_GENERATOR; - - return !(status & bad); -} - -} // unnamed namespace - -int QTlsBackendOpenSSL::dhParametersFromDer(const QByteArray &der, QByteArray *derData) const -{ - Q_ASSERT(derData); - - if (der.isEmpty()) - return DHParams::InvalidInputDataError; - - const unsigned char *data = reinterpret_cast<const unsigned char *>(der.data()); - const int len = der.size(); - - // TLSTODO: check it's needed (loading ciphers and certs in - // addition to the library!) - QSslSocketPrivate::ensureInitialized(); - - DH *dh = q_d2i_DHparams(nullptr, &data, len); - if (dh) { - const auto dhRaii = qScopeGuard([dh] {q_DH_free(dh);}); - - if (isSafeDH(dh)) - *derData = der; - else - return DHParams::UnsafeParametersError; - } else { - return DHParams::InvalidInputDataError; - } - - return DHParams::NoError; -} - -int QTlsBackendOpenSSL::dhParametersFromPem(const QByteArray &pem, QByteArray *data) const -{ - Q_ASSERT(data); - - if (pem.isEmpty()) - return DHParams::InvalidInputDataError; - - // TLSTODO: check it was not a cargo-cult programming in case of - // DH ... - QSslSocketPrivate::ensureInitialized(); - - BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size()); - if (!bio) - return DHParams::InvalidInputDataError; - - const auto bioRaii = qScopeGuard([bio] - { - q_BIO_free(bio); - }); - - DH *dh = nullptr; - q_PEM_read_bio_DHparams(bio, &dh, nullptr, nullptr); - - if (dh) { - const auto dhGuard = qScopeGuard([dh] - { - q_DH_free(dh); - }); - - if (isSafeDH(dh)) { - char *buf = nullptr; - const int len = q_i2d_DHparams(dh, reinterpret_cast<unsigned char **>(&buf)); - if (len > 0) - *data = QByteArray(buf, len); - else - return DHParams::InvalidInputDataError; - } else { - return DHParams::UnsafeParametersError; - } - } else { - return DHParams::InvalidInputDataError; - } - - return DHParams::NoError; -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslkey_p.cpp b/src/network/ssl/qsslkey_p.cpp index 010f45fca9..8a5af25e75 100644 --- a/src/network/ssl/qsslkey_p.cpp +++ b/src/network/ssl/qsslkey_p.cpp @@ -57,12 +57,8 @@ #include "qssl_p.h" #include "qsslkey.h" #include "qsslkey_p.h" -#ifndef QT_NO_OPENSSL -#include "qsslsocket_openssl_symbols_p.h" -#endif #include "qsslsocket.h" #include "qsslsocket_p.h" -#include "qasn1element_p.h" #include "qtlsbackend_p.h" #include <QtCore/qatomic.h> diff --git a/src/network/ssl/qsslkey_p.h b/src/network/ssl/qsslkey_p.h index f5895561b4..6fea1ad66a 100644 --- a/src/network/ssl/qsslkey_p.h +++ b/src/network/ssl/qsslkey_p.h @@ -73,8 +73,8 @@ public: using Cipher = QTlsPrivate::Cipher; - Q_AUTOTEST_EXPORT static QByteArray decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv); - Q_AUTOTEST_EXPORT static QByteArray encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv); + Q_NETWORK_EXPORT static QByteArray decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv); + Q_NETWORK_EXPORT static QByteArray encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv); std::unique_ptr<QTlsPrivate::TlsKey> backend; QAtomicInt ref; diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 546c26272d..003bbf0787 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -1573,7 +1573,7 @@ QList<QString> QSslSocket::availableBackends() from the list of available backends. \note When selecting a default backend implicitly, QSslSocket prefers - native backends, such as SecureTransport on Darwin, or Schannel on Windows. + the OpenSSL backend if available. \sa setActiveBackend(), availableBackends() */ diff --git a/src/network/ssl/qsslsocket_mac_shared.cpp b/src/network/ssl/qsslsocket_mac_shared.cpp deleted file mode 100644 index 837ac4a4f6..0000000000 --- a/src/network/ssl/qsslsocket_mac_shared.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2015 ownCloud Inc -** 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 <QtCore/qglobal.h> - -#ifdef Q_OS_MACOS - -#include "qtlsbackend_p.h" - -#include <private/qcore_mac_p.h> - -#include <QtCore/qdebug.h> - -#include <CoreFoundation/CFArray.h> -#include <Security/Security.h> - -#endif // Q_OS_MACOS - -QT_BEGIN_NAMESPACE - -#ifdef Q_OS_MACOS -namespace { - -bool hasTrustedSslServerPolicy(SecPolicyRef policy, CFDictionaryRef props) { - QCFType<CFDictionaryRef> policyProps = SecPolicyCopyProperties(policy); - // only accept certificates with policies for SSL server validation for now - if (CFEqual(CFDictionaryGetValue(policyProps, kSecPolicyOid), kSecPolicyAppleSSL)) { - CFBooleanRef policyClient; - if (CFDictionaryGetValueIfPresent(policyProps, kSecPolicyClient, reinterpret_cast<const void**>(&policyClient)) && - CFEqual(policyClient, kCFBooleanTrue)) { - return false; // no client certs - } - if (!CFDictionaryContainsKey(props, kSecTrustSettingsResult)) { - // as per the docs, no trust settings result implies full trust - return true; - } - CFNumberRef number = static_cast<CFNumberRef>(CFDictionaryGetValue(props, kSecTrustSettingsResult)); - SecTrustSettingsResult settingsResult; - CFNumberGetValue(number, kCFNumberSInt32Type, &settingsResult); - switch (settingsResult) { - case kSecTrustSettingsResultTrustRoot: - case kSecTrustSettingsResultTrustAsRoot: - return true; - default: - return false; - } - } - return false; -} - -bool isCaCertificateTrusted(SecCertificateRef cfCert, int domain) -{ - QCFType<CFArrayRef> cfTrustSettings; - OSStatus status = SecTrustSettingsCopyTrustSettings(cfCert, SecTrustSettingsDomain(domain), &cfTrustSettings); - if (status == noErr) { - CFIndex size = CFArrayGetCount(cfTrustSettings); - // if empty, trust for everything (as per the Security Framework documentation) - if (size == 0) { - return true; - } else { - for (CFIndex i = 0; i < size; ++i) { - CFDictionaryRef props = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(cfTrustSettings, i)); - if (CFDictionaryContainsKey(props, kSecTrustSettingsPolicy)) { - if (hasTrustedSslServerPolicy((SecPolicyRef)CFDictionaryGetValue(props, kSecTrustSettingsPolicy), props)) - return true; - } - } - } - } else { - qCWarning(lcTlsBackend, "Error receiving trust for a CA certificate"); - } - return false; -} - -} // unnamed namespace -#endif // Q_OS_MACOS - -namespace QTlsPrivate { -QList<QSslCertificate> systemCaCertificates() -{ - QList<QSslCertificate> systemCerts; - // SecTrustSettingsCopyCertificates is not defined on iOS. -#ifdef Q_OS_MACOS - // iterate through all enum members, order: - // kSecTrustSettingsDomainUser, kSecTrustSettingsDomainAdmin, kSecTrustSettingsDomainSystem - for (int dom = kSecTrustSettingsDomainUser; dom <= int(kSecTrustSettingsDomainSystem); dom++) { - QCFType<CFArrayRef> cfCerts; - OSStatus status = SecTrustSettingsCopyCertificates(SecTrustSettingsDomain(dom), &cfCerts); - if (status == noErr) { - const CFIndex size = CFArrayGetCount(cfCerts); - for (CFIndex i = 0; i < size; ++i) { - SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i); - QCFType<CFDataRef> derData = SecCertificateCopyData(cfCert); - if (isCaCertificateTrusted(cfCert, dom)) { - if (derData == nullptr) { - qCWarning(lcSsl, "Error retrieving a CA certificate from the system store"); - } else { - systemCerts << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der); - } - } - } - } - } -#endif - return systemCerts; -} -} // namespace QTlsPrivate - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_openssl_android.cpp b/src/network/ssl/qsslsocket_openssl_android.cpp deleted file mode 100644 index 22884cb819..0000000000 --- a/src/network/ssl/qsslsocket_openssl_android.cpp +++ /dev/null @@ -1,93 +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$ -** -****************************************************************************/ - -/**************************************************************************** -** -** In addition, as a special exception, the copyright holders listed above give -** permission to link the code of its release of Qt with the OpenSSL project's -** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the -** same license as the original version), and distribute the linked executables. -** -** You must comply with the GNU General Public License version 2 in all -** respects for all of the code used other than the "OpenSSL" code. If you -** modify this file, you may extend this exception to your version of the file, -** but you are not obligated to do so. If you do not wish to do so, delete -** this exception statement from your version of this file. -** -****************************************************************************/ - -#include "qsslsocket_p.h" -#include <QtCore/QJniEnvironment> -#include <QtCore/QJniObject> - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -QList<QByteArray> fetchSslCertificateData() -{ - QList<QByteArray> certificateData; - - QJniObject certificates = QJniObject::callStaticObjectMethod("org/qtproject/qt/android/QtNative", - "getSSLCertificates", - "()[[B"); - if (!certificates.isValid()) - return certificateData; - - QJniEnvironment env; - jobjectArray jcertificates = static_cast<jobjectArray>(certificates.object()); - const jint nCertificates = env->GetArrayLength(jcertificates); - certificateData.reserve(static_cast<int>(nCertificates)); - - for (int i = 0; i < nCertificates; ++i) { - jbyteArray jCert = static_cast<jbyteArray>(env->GetObjectArrayElement(jcertificates, i)); - const uint sz = env->GetArrayLength(jCert); - jbyte *buffer = env->GetByteArrayElements(jCert, 0); - certificateData.append(QByteArray(reinterpret_cast<char*>(buffer), sz)); - - env->ReleaseByteArrayElements(jCert, buffer, JNI_ABORT); // don't copy back the elements - env->DeleteLocalRef(jCert); - } - - return certificateData; -} - -} // namespace QTlsPrivate - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp deleted file mode 100644 index 82429800f8..0000000000 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ /dev/null @@ -1,1234 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Copyright (C) 2014 BlackBerry Limited. All rights reserved. -** Copyright (C) 2016 Richard J. Moore <rich@kde.org> -** 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$ -** -****************************************************************************/ - -/**************************************************************************** -** -** In addition, as a special exception, the copyright holders listed above give -** permission to link the code of its release of Qt with the OpenSSL project's -** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the -** same license as the original version), and distribute the linked executables. -** -** You must comply with the GNU General Public License version 2 in all -** respects for all of the code used other than the "OpenSSL" code. If you -** modify this file, you may extend this exception to your version of the file, -** but you are not obligated to do so. If you do not wish to do so, delete -** this exception statement from your version of this file. -** -****************************************************************************/ - -#include "qssl_p.h" -#include "qsslsocket_openssl_symbols_p.h" - -#ifdef Q_OS_WIN -# include <private/qsystemlibrary_p.h> -#elif QT_CONFIG(library) -# include <QtCore/qlibrary.h> -#endif -#include <QtCore/qmutex.h> -#include <QtCore/qdatetime.h> -#if defined(Q_OS_UNIX) -#include <QtCore/qdir.h> -#endif -#include <QtCore/private/qduplicatetracker_p.h> -#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) -#include <link.h> -#endif -#ifdef Q_OS_DARWIN -#include "private/qcore_mac_p.h" -#endif - -#include <algorithm> - -QT_BEGIN_NAMESPACE - -/* - Note to maintainer: - ------------------- - - We load OpenSSL symbols dynamically. Because symbols are known to - disappear, and signatures sometimes change, between releases, we need to - be careful about how this is done. To ensure we don't end up dereferencing - null function pointers, and continue running even if certain functions are - missing, we define helper functions for each of the symbols we load from - OpenSSL, all prefixed with "q_" (declared in - qsslsocket_openssl_symbols_p.h). So instead of calling SSL_connect - directly, we call q_SSL_connect, which is a function that checks if the - actual SSL_connect fptr is null, and returns a failure if it is, or calls - SSL_connect if it isn't. - - This requires a somewhat tedious process of declaring each function we - want to call in OpenSSL thrice: once with the q_, in _p.h, once using the - DEFINEFUNC macros below, and once in the function that actually resolves - the symbols, below the DEFINEFUNC declarations below. - - There's one DEFINEFUNC macro declared for every number of arguments - exposed by OpenSSL (feel free to extend when needed). The easiest thing to - do is to find an existing entry that matches the arg count of the function - you want to import, and do the same. - - The first macro arg is the function return type. The second is the - verbatim name of the function/symbol. Then follows a list of N pairs of - argument types with a variable name, and just the variable name (char *a, - a, char *b, b, etc). Finally there's two arguments - a suitable return - statement for the error case (for an int function, return 0 or return -1 - is usually right). Then either just "return" or DUMMYARG, the latter being - for void functions. - - Note: Take into account that these macros and declarations are processed - at compile-time, and the result depends on the OpenSSL headers the - compiling host has installed, but the symbols are resolved at run-time, - possibly with a different version of OpenSSL. -*/ - -#ifndef QT_LINKED_OPENSSL - -namespace { -void qsslSocketUnresolvedSymbolWarning(const char *functionName) -{ - qCWarning(lcSsl, "QSslSocket: cannot call unresolved function %s", functionName); -} - -#if QT_CONFIG(library) -void qsslSocketCannotResolveSymbolWarning(const char *functionName) -{ - qCWarning(lcSsl, "QSslSocket: cannot resolve %s", functionName); -} -#endif - -} - -#endif // QT_LINKED_OPENSSL - -DEFINEFUNC(const unsigned char *, ASN1_STRING_get0_data, const ASN1_STRING *a, a, return nullptr, return) -DEFINEFUNC2(int, OPENSSL_init_ssl, uint64_t opts, opts, const OPENSSL_INIT_SETTINGS *settings, settings, return 0, return) -DEFINEFUNC2(int, OPENSSL_init_crypto, uint64_t opts, opts, const OPENSSL_INIT_SETTINGS *settings, settings, return 0, return) -DEFINEFUNC(BIO *, BIO_new, const BIO_METHOD *a, a, return nullptr, return) -DEFINEFUNC(const BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return nullptr, return) -DEFINEFUNC2(int, BN_is_word, BIGNUM *a, a, BN_ULONG w, w, return 0, return) -DEFINEFUNC(int, EVP_CIPHER_CTX_reset, EVP_CIPHER_CTX *c, c, return 0, return) -DEFINEFUNC(int, EVP_PKEY_up_ref, EVP_PKEY *a, a, return 0, return) -DEFINEFUNC2(EVP_PKEY_CTX *, EVP_PKEY_CTX_new, EVP_PKEY *pkey, pkey, ENGINE *e, e, return nullptr, return) -DEFINEFUNC(int, EVP_PKEY_param_check, EVP_PKEY_CTX *ctx, ctx, return 0, return) -DEFINEFUNC(void, EVP_PKEY_CTX_free, EVP_PKEY_CTX *ctx, ctx, return, return) -DEFINEFUNC(int, EVP_PKEY_base_id, EVP_PKEY *a, a, return NID_undef, return) -DEFINEFUNC(int, RSA_bits, RSA *a, a, return 0, return) -DEFINEFUNC(int, DSA_bits, DSA *a, a, return 0, return) -DEFINEFUNC(int, OPENSSL_sk_num, OPENSSL_STACK *a, a, return -1, return) -DEFINEFUNC2(void, OPENSSL_sk_pop_free, OPENSSL_STACK *a, a, void (*b)(void*), b, return, DUMMYARG) -DEFINEFUNC(OPENSSL_STACK *, OPENSSL_sk_new_null, DUMMYARG, DUMMYARG, return nullptr, return) -DEFINEFUNC2(void, OPENSSL_sk_push, OPENSSL_STACK *a, a, void *b, b, return, DUMMYARG) -DEFINEFUNC(void, OPENSSL_sk_free, OPENSSL_STACK *a, a, return, DUMMYARG) -DEFINEFUNC2(void *, OPENSSL_sk_value, OPENSSL_STACK *a, a, int b, b, return nullptr, return) -DEFINEFUNC(int, SSL_session_reused, SSL *a, a, return 0, return) -DEFINEFUNC2(unsigned long, SSL_CTX_set_options, SSL_CTX *ctx, ctx, unsigned long op, op, return 0, return) -using info_callback = void (*) (const SSL *ssl, int type, int val); -DEFINEFUNC2(void, SSL_set_info_callback, SSL *ssl, ssl, info_callback cb, cb, return, return) -DEFINEFUNC(const char *, SSL_alert_type_string, int value, value, return nullptr, return) -DEFINEFUNC(const char *, SSL_alert_desc_string_long, int value, value, return nullptr, return) -DEFINEFUNC(int, SSL_CTX_get_security_level, const SSL_CTX *ctx, ctx, return -1, return) -DEFINEFUNC2(void, SSL_CTX_set_security_level, SSL_CTX *ctx, ctx, int level, level, return, return) -#ifdef TLS1_3_VERSION -DEFINEFUNC2(int, SSL_CTX_set_ciphersuites, SSL_CTX *ctx, ctx, const char *str, str, return 0, return) -DEFINEFUNC2(void, SSL_set_psk_use_session_callback, SSL *ssl, ssl, q_SSL_psk_use_session_cb_func_t callback, callback, return, DUMMYARG) -DEFINEFUNC2(void, SSL_CTX_sess_set_new_cb, SSL_CTX *ctx, ctx, NewSessionCallback cb, cb, return, return) -DEFINEFUNC(int, SSL_SESSION_is_resumable, const SSL_SESSION *s, s, return 0, return) -#endif -DEFINEFUNC3(size_t, SSL_get_client_random, SSL *a, a, unsigned char *out, out, size_t outlen, outlen, return 0, return) -DEFINEFUNC3(size_t, SSL_SESSION_get_master_key, const SSL_SESSION *ses, ses, unsigned char *out, out, size_t outlen, outlen, return 0, return) -DEFINEFUNC6(int, CRYPTO_get_ex_new_index, int class_index, class_index, long argl, argl, void *argp, argp, CRYPTO_EX_new *new_func, new_func, CRYPTO_EX_dup *dup_func, dup_func, CRYPTO_EX_free *free_func, free_func, return -1, return) -DEFINEFUNC2(unsigned long, SSL_set_options, SSL *ssl, ssl, unsigned long op, op, return 0, return) - -DEFINEFUNC(const SSL_METHOD *, TLS_method, DUMMYARG, DUMMYARG, return nullptr, return) -DEFINEFUNC(const SSL_METHOD *, TLS_client_method, DUMMYARG, DUMMYARG, return nullptr, return) -DEFINEFUNC(const SSL_METHOD *, TLS_server_method, DUMMYARG, DUMMYARG, return nullptr, return) -DEFINEFUNC(void, X509_up_ref, X509 *a, a, return, DUMMYARG) -DEFINEFUNC(ASN1_TIME *, X509_getm_notBefore, X509 *a, a, return nullptr, return) -DEFINEFUNC(ASN1_TIME *, X509_getm_notAfter, X509 *a, a, return nullptr, return) -DEFINEFUNC(long, X509_get_version, X509 *a, a, return -1, return) -DEFINEFUNC(EVP_PKEY *, X509_get_pubkey, X509 *a, a, return nullptr, return) -DEFINEFUNC2(void, X509_STORE_set_verify_cb, X509_STORE *a, a, X509_STORE_CTX_verify_cb verify_cb, verify_cb, return, DUMMYARG) -DEFINEFUNC3(int, X509_STORE_set_ex_data, X509_STORE *a, a, int idx, idx, void *data, data, return 0, return) -DEFINEFUNC2(void *, X509_STORE_get_ex_data, X509_STORE *r, r, int idx, idx, return nullptr, return) -DEFINEFUNC(STACK_OF(X509) *, X509_STORE_CTX_get0_chain, X509_STORE_CTX *a, a, return nullptr, return) -DEFINEFUNC3(void, CRYPTO_free, void *str, str, const char *file, file, int line, line, return, DUMMYARG) -DEFINEFUNC(long, OpenSSL_version_num, void, DUMMYARG, return 0, return) -DEFINEFUNC(const char *, OpenSSL_version, int a, a, return nullptr, return) -DEFINEFUNC(unsigned long, SSL_SESSION_get_ticket_lifetime_hint, const SSL_SESSION *session, session, return 0, return) -DEFINEFUNC4(void, DH_get0_pqg, const DH *dh, dh, const BIGNUM **p, p, const BIGNUM **q, q, const BIGNUM **g, g, return, DUMMYARG) -DEFINEFUNC(int, DH_bits, DH *dh, dh, return 0, return) - -#if QT_CONFIG(dtls) -DEFINEFUNC2(int, DTLSv1_listen, SSL *s, s, BIO_ADDR *c, c, return -1, return) -DEFINEFUNC(BIO_ADDR *, BIO_ADDR_new, DUMMYARG, DUMMYARG, return nullptr, return) -DEFINEFUNC(void, BIO_ADDR_free, BIO_ADDR *ap, ap, return, DUMMYARG) -DEFINEFUNC2(BIO_METHOD *, BIO_meth_new, int type, type, const char *name, name, return nullptr, return) -DEFINEFUNC(void, BIO_meth_free, BIO_METHOD *biom, biom, return, DUMMYARG) -DEFINEFUNC2(int, BIO_meth_set_write, BIO_METHOD *biom, biom, DgramWriteCallback write, write, return 0, return) -DEFINEFUNC2(int, BIO_meth_set_read, BIO_METHOD *biom, biom, DgramReadCallback read, read, return 0, return) -DEFINEFUNC2(int, BIO_meth_set_puts, BIO_METHOD *biom, biom, DgramPutsCallback puts, puts, return 0, return) -DEFINEFUNC2(int, BIO_meth_set_ctrl, BIO_METHOD *biom, biom, DgramCtrlCallback ctrl, ctrl, return 0, return) -DEFINEFUNC2(int, BIO_meth_set_create, BIO_METHOD *biom, biom, DgramCreateCallback crt, crt, return 0, return) -DEFINEFUNC2(int, BIO_meth_set_destroy, BIO_METHOD *biom, biom, DgramDestroyCallback dtr, dtr, return 0, return) -#endif // dtls - -#if QT_CONFIG(ocsp) -DEFINEFUNC(const OCSP_CERTID *, OCSP_SINGLERESP_get0_id, const OCSP_SINGLERESP *x, x, return nullptr, return) -DEFINEFUNC3(OCSP_RESPONSE *, d2i_OCSP_RESPONSE, OCSP_RESPONSE **a, a, const unsigned char **in, in, long len, len, return nullptr, return) -DEFINEFUNC(void, OCSP_RESPONSE_free, OCSP_RESPONSE *rs, rs, return, DUMMYARG) -DEFINEFUNC(OCSP_BASICRESP *, OCSP_response_get1_basic, OCSP_RESPONSE *resp, resp, return nullptr, return) -DEFINEFUNC(void, OCSP_BASICRESP_free, OCSP_BASICRESP *bs, bs, return, DUMMYARG) -DEFINEFUNC(int, OCSP_response_status, OCSP_RESPONSE *resp, resp, return OCSP_RESPONSE_STATUS_INTERNALERROR, return) -DEFINEFUNC4(int, OCSP_basic_verify, OCSP_BASICRESP *bs, bs, STACK_OF(X509) *certs, certs, X509_STORE *st, st, unsigned long flags, flags, return -1, return) -DEFINEFUNC(int, OCSP_resp_count, OCSP_BASICRESP *bs, bs, return 0, return) -DEFINEFUNC2(OCSP_SINGLERESP *, OCSP_resp_get0, OCSP_BASICRESP *bs, bs, int idx, idx, return nullptr, return) -DEFINEFUNC5(int, OCSP_single_get0_status, OCSP_SINGLERESP *single, single, int *reason, reason, ASN1_GENERALIZEDTIME **revtime, revtime, - ASN1_GENERALIZEDTIME **thisupd, thisupd, ASN1_GENERALIZEDTIME **nextupd, nextupd, return -1, return) -DEFINEFUNC4(int, OCSP_check_validity, ASN1_GENERALIZEDTIME *thisupd, thisupd, ASN1_GENERALIZEDTIME *nextupd, nextupd, long nsec, nsec, long maxsec, maxsec, return 0, return) -DEFINEFUNC3(OCSP_CERTID *, OCSP_cert_to_id, const EVP_MD *dgst, dgst, X509 *subject, subject, X509 *issuer, issuer, return nullptr, return) -DEFINEFUNC(void, OCSP_CERTID_free, OCSP_CERTID *cid, cid, return, DUMMYARG) -DEFINEFUNC5(int, OCSP_id_get0_info, ASN1_OCTET_STRING **piNameHash, piNameHash, ASN1_OBJECT **pmd, pmd, - ASN1_OCTET_STRING **piKeyHash, piKeyHash, ASN1_INTEGER **pserial, pserial, OCSP_CERTID *cid, cid, - return 0, return) -DEFINEFUNC2(OCSP_RESPONSE *, OCSP_response_create, int status, status, OCSP_BASICRESP *bs, bs, return nullptr, return) -DEFINEFUNC(const STACK_OF(X509) *, OCSP_resp_get0_certs, const OCSP_BASICRESP *bs, bs, return nullptr, return) -DEFINEFUNC2(int, OCSP_id_cmp, OCSP_CERTID *a, a, OCSP_CERTID *b, b, return -1, return) -DEFINEFUNC7(OCSP_SINGLERESP *, OCSP_basic_add1_status, OCSP_BASICRESP *r, r, OCSP_CERTID *c, c, int s, s, - int re, re, ASN1_TIME *rt, rt, ASN1_TIME *t, t, ASN1_TIME *n, n, return nullptr, return) -DEFINEFUNC(OCSP_BASICRESP *, OCSP_BASICRESP_new, DUMMYARG, DUMMYARG, return nullptr, return) -DEFINEFUNC2(int, i2d_OCSP_RESPONSE, OCSP_RESPONSE *r, r, unsigned char **ppout, ppout, return 0, return) -DEFINEFUNC6(int, OCSP_basic_sign, OCSP_BASICRESP *br, br, X509 *signer, signer, EVP_PKEY *key, key, - const EVP_MD *dg, dg, STACK_OF(X509) *cs, cs, unsigned long flags, flags, return 0, return) -#endif // ocsp - -DEFINEFUNC2(void, BIO_set_data, BIO *a, a, void *ptr, ptr, return, DUMMYARG) -DEFINEFUNC(void *, BIO_get_data, BIO *a, a, return nullptr, return) -DEFINEFUNC2(void, BIO_set_init, BIO *a, a, int init, init, return, DUMMYARG) -DEFINEFUNC(int, BIO_get_shutdown, BIO *a, a, return -1, return) -DEFINEFUNC2(void, BIO_set_shutdown, BIO *a, a, int shut, shut, return, DUMMYARG) - -DEFINEFUNC(long, ASN1_INTEGER_get, ASN1_INTEGER *a, a, return 0, return) -DEFINEFUNC2(int, ASN1_INTEGER_cmp, const ASN1_INTEGER *a, a, const ASN1_INTEGER *b, b, return 1, return) -DEFINEFUNC(int, ASN1_STRING_length, ASN1_STRING *a, a, return 0, return) -DEFINEFUNC2(int, ASN1_STRING_to_UTF8, unsigned char **a, a, ASN1_STRING *b, b, return 0, return) -DEFINEFUNC2(int, ASN1_TIME_to_tm, const ASN1_TIME *s, s, struct tm *tm, tm, return 0, return) -DEFINEFUNC4(long, BIO_ctrl, BIO *a, a, int b, b, long c, c, void *d, d, return -1, return) -DEFINEFUNC(int, BIO_free, BIO *a, a, return 0, return) -DEFINEFUNC2(BIO *, BIO_new_mem_buf, void *a, a, int b, b, return nullptr, return) -DEFINEFUNC3(int, BIO_read, BIO *a, a, void *b, b, int c, c, return -1, return) - -DEFINEFUNC3(int, BIO_write, BIO *a, a, const void *b, b, int c, c, return -1, return) -DEFINEFUNC(int, BN_num_bits, const BIGNUM *a, a, return 0, return) -DEFINEFUNC2(BN_ULONG, BN_mod_word, const BIGNUM *a, a, BN_ULONG w, w, return static_cast<BN_ULONG>(-1), return) -#ifndef OPENSSL_NO_EC -DEFINEFUNC(const EC_GROUP*, EC_KEY_get0_group, const EC_KEY* k, k, return nullptr, return) -DEFINEFUNC(int, EC_GROUP_get_degree, const EC_GROUP* g, g, return 0, return) -#endif -DEFINEFUNC(DSA *, DSA_new, DUMMYARG, DUMMYARG, return nullptr, return) -DEFINEFUNC(void, DSA_free, DSA *a, a, return, DUMMYARG) -DEFINEFUNC3(X509 *, d2i_X509, X509 **a, a, const unsigned char **b, b, long c, c, return nullptr, return) -DEFINEFUNC2(char *, ERR_error_string, unsigned long a, a, char *b, b, return nullptr, return) -DEFINEFUNC3(void, ERR_error_string_n, unsigned long e, e, char *b, b, size_t len, len, return, DUMMYARG) -DEFINEFUNC(unsigned long, ERR_get_error, DUMMYARG, DUMMYARG, return 0, return) -DEFINEFUNC(EVP_CIPHER_CTX *, EVP_CIPHER_CTX_new, void, DUMMYARG, return nullptr, return) -DEFINEFUNC(void, EVP_CIPHER_CTX_free, EVP_CIPHER_CTX *a, a, return, DUMMYARG) -DEFINEFUNC4(int, EVP_CIPHER_CTX_ctrl, EVP_CIPHER_CTX *ctx, ctx, int type, type, int arg, arg, void *ptr, ptr, return 0, return) -DEFINEFUNC2(int, EVP_CIPHER_CTX_set_key_length, EVP_CIPHER_CTX *ctx, ctx, int keylen, keylen, return 0, return) -DEFINEFUNC5(int, EVP_CipherInit, EVP_CIPHER_CTX *ctx, ctx, const EVP_CIPHER *type, type, const unsigned char *key, key, const unsigned char *iv, iv, int enc, enc, return 0, return) -DEFINEFUNC6(int, EVP_CipherInit_ex, EVP_CIPHER_CTX *ctx, ctx, const EVP_CIPHER *cipher, cipher, ENGINE *impl, impl, const unsigned char *key, key, const unsigned char *iv, iv, int enc, enc, return 0, return) -DEFINEFUNC5(int, EVP_CipherUpdate, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, const unsigned char *in, in, int inl, inl, return 0, return) -DEFINEFUNC3(int, EVP_CipherFinal, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, return 0, return) -DEFINEFUNC(const EVP_MD *, EVP_get_digestbyname, const char *name, name, return nullptr, return) -#ifndef OPENSSL_NO_DES -DEFINEFUNC(const EVP_CIPHER *, EVP_des_cbc, DUMMYARG, DUMMYARG, return nullptr, return) -DEFINEFUNC(const EVP_CIPHER *, EVP_des_ede3_cbc, DUMMYARG, DUMMYARG, return nullptr, return) -#endif -#ifndef OPENSSL_NO_RC2 -DEFINEFUNC(const EVP_CIPHER *, EVP_rc2_cbc, DUMMYARG, DUMMYARG, return nullptr, return) -#endif -#ifndef OPENSSL_NO_AES -DEFINEFUNC(const EVP_CIPHER *, EVP_aes_128_cbc, DUMMYARG, DUMMYARG, return nullptr, return) -DEFINEFUNC(const EVP_CIPHER *, EVP_aes_192_cbc, DUMMYARG, DUMMYARG, return nullptr, return) -DEFINEFUNC(const EVP_CIPHER *, EVP_aes_256_cbc, DUMMYARG, DUMMYARG, return nullptr, return) -#endif -DEFINEFUNC(const EVP_MD *, EVP_sha1, DUMMYARG, DUMMYARG, return nullptr, return) -DEFINEFUNC3(int, EVP_PKEY_assign, EVP_PKEY *a, a, int b, b, void *r, r, return -1, return) -DEFINEFUNC2(int, EVP_PKEY_set1_RSA, EVP_PKEY *a, a, RSA *b, b, return -1, return) -DEFINEFUNC2(int, EVP_PKEY_set1_DSA, EVP_PKEY *a, a, DSA *b, b, return -1, return) -DEFINEFUNC2(int, EVP_PKEY_set1_DH, EVP_PKEY *a, a, DH *b, b, return -1, return) -#ifndef OPENSSL_NO_EC -DEFINEFUNC2(int, EVP_PKEY_set1_EC_KEY, EVP_PKEY *a, a, EC_KEY *b, b, return -1, return) -#endif -DEFINEFUNC2(int, EVP_PKEY_cmp, const EVP_PKEY *a, a, const EVP_PKEY *b, b, return -1, return) -DEFINEFUNC(void, EVP_PKEY_free, EVP_PKEY *a, a, return, DUMMYARG) -DEFINEFUNC(DSA *, EVP_PKEY_get1_DSA, EVP_PKEY *a, a, return nullptr, return) -DEFINEFUNC(RSA *, EVP_PKEY_get1_RSA, EVP_PKEY *a, a, return nullptr, return) -DEFINEFUNC(DH *, EVP_PKEY_get1_DH, EVP_PKEY *a, a, return nullptr, return) -#ifndef OPENSSL_NO_EC -DEFINEFUNC(EC_KEY *, EVP_PKEY_get1_EC_KEY, EVP_PKEY *a, a, return nullptr, return) -#endif -DEFINEFUNC(EVP_PKEY *, EVP_PKEY_new, DUMMYARG, DUMMYARG, return nullptr, return) -DEFINEFUNC(int, EVP_PKEY_type, int a, a, return NID_undef, return) -DEFINEFUNC2(int, i2d_X509, X509 *a, a, unsigned char **b, b, return -1, return) -DEFINEFUNC(const char *, OBJ_nid2sn, int a, a, return nullptr, return) -DEFINEFUNC(const char *, OBJ_nid2ln, int a, a, return nullptr, return) -DEFINEFUNC(int, OBJ_sn2nid, const char *s, s, return 0, return) -DEFINEFUNC(int, OBJ_ln2nid, const char *s, s, return 0, return) -DEFINEFUNC3(int, i2t_ASN1_OBJECT, char *a, a, int b, b, ASN1_OBJECT *c, c, return -1, return) -DEFINEFUNC4(int, OBJ_obj2txt, char *a, a, int b, b, ASN1_OBJECT *c, c, int d, d, return -1, return) -DEFINEFUNC(int, OBJ_obj2nid, const ASN1_OBJECT *a, a, return NID_undef, return) -DEFINEFUNC4(EVP_PKEY *, PEM_read_bio_PrivateKey, BIO *a, a, EVP_PKEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return) -DEFINEFUNC4(DSA *, PEM_read_bio_DSAPrivateKey, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return) -DEFINEFUNC4(RSA *, PEM_read_bio_RSAPrivateKey, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return) - -#ifndef OPENSSL_NO_EC -DEFINEFUNC4(EC_KEY *, PEM_read_bio_ECPrivateKey, BIO *a, a, EC_KEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return) -DEFINEFUNC7(int, PEM_write_bio_ECPrivateKey, BIO *a, a, EC_KEY *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return) -DEFINEFUNC4(EC_KEY *, PEM_read_bio_EC_PUBKEY, BIO *a, a, EC_KEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return) -DEFINEFUNC2(int, PEM_write_bio_EC_PUBKEY, BIO *a, a, EC_KEY *b, b, return 0, return) -#endif // OPENSSL_NO_EC - -DEFINEFUNC4(DH *, PEM_read_bio_DHparams, BIO *a, a, DH **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return) -DEFINEFUNC7(int, PEM_write_bio_DSAPrivateKey, BIO *a, a, DSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return) -DEFINEFUNC7(int, PEM_write_bio_RSAPrivateKey, BIO *a, a, RSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return) -DEFINEFUNC7(int, PEM_write_bio_PrivateKey, BIO *a, a, EVP_PKEY *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return) -DEFINEFUNC4(EVP_PKEY *, PEM_read_bio_PUBKEY, BIO *a, a, EVP_PKEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return) -DEFINEFUNC4(DSA *, PEM_read_bio_DSA_PUBKEY, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return) -DEFINEFUNC4(RSA *, PEM_read_bio_RSA_PUBKEY, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return) -DEFINEFUNC2(int, PEM_write_bio_DSA_PUBKEY, BIO *a, a, DSA *b, b, return 0, return) -DEFINEFUNC2(int, PEM_write_bio_RSA_PUBKEY, BIO *a, a, RSA *b, b, return 0, return) -DEFINEFUNC2(int, PEM_write_bio_PUBKEY, BIO *a, a, EVP_PKEY *b, b, return 0, return) -DEFINEFUNC2(void, RAND_seed, const void *a, a, int b, b, return, DUMMYARG) -DEFINEFUNC(int, RAND_status, void, DUMMYARG, return -1, return) -DEFINEFUNC2(int, RAND_bytes, unsigned char *b, b, int n, n, return 0, return) -DEFINEFUNC(RSA *, RSA_new, DUMMYARG, DUMMYARG, return nullptr, return) -DEFINEFUNC(void, RSA_free, RSA *a, a, return, DUMMYARG) -DEFINEFUNC(int, SSL_accept, SSL *a, a, return -1, return) -DEFINEFUNC(int, SSL_clear, SSL *a, a, return -1, return) -DEFINEFUNC3(char *, SSL_CIPHER_description, const SSL_CIPHER *a, a, char *b, b, int c, c, return nullptr, return) -DEFINEFUNC2(int, SSL_CIPHER_get_bits, const SSL_CIPHER *a, a, int *b, b, return 0, return) -DEFINEFUNC(BIO *, SSL_get_rbio, const SSL *s, s, return nullptr, return) -DEFINEFUNC(int, SSL_connect, SSL *a, a, return -1, return) -DEFINEFUNC(int, SSL_CTX_check_private_key, const SSL_CTX *a, a, return -1, return) -DEFINEFUNC4(long, SSL_CTX_ctrl, SSL_CTX *a, a, int b, b, long c, c, void *d, d, return -1, return) -DEFINEFUNC(void, SSL_CTX_free, SSL_CTX *a, a, return, DUMMYARG) -DEFINEFUNC(SSL_CTX *, SSL_CTX_new, const SSL_METHOD *a, a, return nullptr, return) -DEFINEFUNC2(int, SSL_CTX_set_cipher_list, SSL_CTX *a, a, const char *b, b, return -1, return) -DEFINEFUNC3(long, SSL_CTX_callback_ctrl, SSL_CTX *ctx, ctx, int dst, dst, GenericCallbackType cb, cb, return 0, return) -DEFINEFUNC(int, SSL_CTX_set_default_verify_paths, SSL_CTX *a, a, return -1, return) -DEFINEFUNC3(void, SSL_CTX_set_verify, SSL_CTX *a, a, int b, b, int (*c)(int, X509_STORE_CTX *), c, return, DUMMYARG) -DEFINEFUNC2(void, SSL_CTX_set_verify_depth, SSL_CTX *a, a, int b, b, return, DUMMYARG) -DEFINEFUNC2(int, SSL_CTX_use_certificate, SSL_CTX *a, a, X509 *b, b, return -1, return) -DEFINEFUNC3(int, SSL_CTX_use_certificate_file, SSL_CTX *a, a, const char *b, b, int c, c, return -1, return) -DEFINEFUNC2(int, SSL_CTX_use_PrivateKey, SSL_CTX *a, a, EVP_PKEY *b, b, return -1, return) -DEFINEFUNC2(int, SSL_CTX_use_RSAPrivateKey, SSL_CTX *a, a, RSA *b, b, return -1, return) -DEFINEFUNC3(int, SSL_CTX_use_PrivateKey_file, SSL_CTX *a, a, const char *b, b, int c, c, return -1, return) -DEFINEFUNC(X509_STORE *, SSL_CTX_get_cert_store, const SSL_CTX *a, a, return nullptr, return) -DEFINEFUNC(SSL_CONF_CTX *, SSL_CONF_CTX_new, DUMMYARG, DUMMYARG, return nullptr, return); -DEFINEFUNC(void, SSL_CONF_CTX_free, SSL_CONF_CTX *a, a, return ,return); -DEFINEFUNC2(void, SSL_CONF_CTX_set_ssl_ctx, SSL_CONF_CTX *a, a, SSL_CTX *b, b, return, return); -DEFINEFUNC2(unsigned int, SSL_CONF_CTX_set_flags, SSL_CONF_CTX *a, a, unsigned int b, b, return 0, return); -DEFINEFUNC(int, SSL_CONF_CTX_finish, SSL_CONF_CTX *a, a, return 0, return); -DEFINEFUNC3(int, SSL_CONF_cmd, SSL_CONF_CTX *a, a, const char *b, b, const char *c, c, return 0, return); -DEFINEFUNC(void, SSL_free, SSL *a, a, return, DUMMYARG) -DEFINEFUNC(STACK_OF(SSL_CIPHER) *, SSL_get_ciphers, const SSL *a, a, return nullptr, return) -DEFINEFUNC(const SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return nullptr, return) -DEFINEFUNC(int, SSL_version, const SSL *a, a, return 0, return) -DEFINEFUNC2(int, SSL_get_error, SSL *a, a, int b, b, return -1, return) -DEFINEFUNC(STACK_OF(X509) *, SSL_get_peer_cert_chain, SSL *a, a, return nullptr, return) -DEFINEFUNC(X509 *, SSL_get_peer_certificate, SSL *a, a, return nullptr, return) -DEFINEFUNC(long, SSL_get_verify_result, const SSL *a, a, return -1, return) -DEFINEFUNC(SSL *, SSL_new, SSL_CTX *a, a, return nullptr, return) -DEFINEFUNC(SSL_CTX *, SSL_get_SSL_CTX, SSL *a, a, return nullptr, return) -DEFINEFUNC4(long, SSL_ctrl, SSL *a, a, int cmd, cmd, long larg, larg, void *parg, parg, return -1, return) -DEFINEFUNC3(int, SSL_read, SSL *a, a, void *b, b, int c, c, return -1, return) -DEFINEFUNC3(void, SSL_set_bio, SSL *a, a, BIO *b, b, BIO *c, c, return, DUMMYARG) -DEFINEFUNC(void, SSL_set_accept_state, SSL *a, a, return, DUMMYARG) -DEFINEFUNC(void, SSL_set_connect_state, SSL *a, a, return, DUMMYARG) -DEFINEFUNC(int, SSL_shutdown, SSL *a, a, return -1, return) -DEFINEFUNC(int, SSL_in_init, const SSL *a, a, return 0, return) -DEFINEFUNC(int, SSL_get_shutdown, const SSL *ssl, ssl, return 0, return) -DEFINEFUNC2(int, SSL_set_session, SSL* to, to, SSL_SESSION *session, session, return -1, return) -DEFINEFUNC(void, SSL_SESSION_free, SSL_SESSION *ses, ses, return, DUMMYARG) -DEFINEFUNC(SSL_SESSION*, SSL_get1_session, SSL *ssl, ssl, return nullptr, return) -DEFINEFUNC(SSL_SESSION*, SSL_get_session, const SSL *ssl, ssl, return nullptr, return) -DEFINEFUNC3(int, SSL_set_ex_data, SSL *ssl, ssl, int idx, idx, void *arg, arg, return 0, return) -DEFINEFUNC2(void *, SSL_get_ex_data, const SSL *ssl, ssl, int idx, idx, return nullptr, return) - -#ifndef OPENSSL_NO_PSK -DEFINEFUNC2(void, SSL_set_psk_client_callback, SSL* ssl, ssl, q_psk_client_callback_t callback, callback, return, DUMMYARG) -DEFINEFUNC2(void, SSL_set_psk_server_callback, SSL* ssl, ssl, q_psk_server_callback_t callback, callback, return, DUMMYARG) -DEFINEFUNC2(int, SSL_CTX_use_psk_identity_hint, SSL_CTX* ctx, ctx, const char *hint, hint, return 0, return) -#endif // !OPENSSL_NO_PSK - -DEFINEFUNC3(int, SSL_write, SSL *a, a, const void *b, b, int c, c, return -1, return) -DEFINEFUNC2(int, X509_cmp, X509 *a, a, X509 *b, b, return -1, return) -DEFINEFUNC4(int, X509_digest, const X509 *x509, x509, const EVP_MD *type, type, unsigned char *md, md, unsigned int *len, len, return -1, return) -DEFINEFUNC(X509 *, X509_dup, X509 *a, a, return nullptr, return) -DEFINEFUNC2(void, X509_print, BIO *a, a, X509 *b, b, return, DUMMYARG); -DEFINEFUNC(ASN1_OBJECT *, X509_EXTENSION_get_object, X509_EXTENSION *a, a, return nullptr, return) -DEFINEFUNC(void, X509_free, X509 *a, a, return, DUMMYARG) -//Q_AUTOTEST_EXPORT ASN1_TIME *q_X509_gmtime_adj(ASN1_TIME *s, long adj); -DEFINEFUNC2(ASN1_TIME *, X509_gmtime_adj, ASN1_TIME *s, s, long adj, adj, return nullptr, return) -DEFINEFUNC(void, ASN1_TIME_free, ASN1_TIME *t, t, return, DUMMYARG) -DEFINEFUNC2(X509_EXTENSION *, X509_get_ext, X509 *a, a, int b, b, return nullptr, return) -DEFINEFUNC(int, X509_get_ext_count, X509 *a, a, return 0, return) -DEFINEFUNC4(void *, X509_get_ext_d2i, X509 *a, a, int b, b, int *c, c, int *d, d, return nullptr, return) -DEFINEFUNC(const X509V3_EXT_METHOD *, X509V3_EXT_get, X509_EXTENSION *a, a, return nullptr, return) -DEFINEFUNC(void *, X509V3_EXT_d2i, X509_EXTENSION *a, a, return nullptr, return) -DEFINEFUNC(int, X509_EXTENSION_get_critical, X509_EXTENSION *a, a, return 0, return) -DEFINEFUNC(ASN1_OCTET_STRING *, X509_EXTENSION_get_data, X509_EXTENSION *a, a, return nullptr, return) -DEFINEFUNC(void, BASIC_CONSTRAINTS_free, BASIC_CONSTRAINTS *a, a, return, DUMMYARG) -DEFINEFUNC(void, AUTHORITY_KEYID_free, AUTHORITY_KEYID *a, a, return, DUMMYARG) -DEFINEFUNC(void, GENERAL_NAME_free, GENERAL_NAME *a, a, return, DUMMYARG) -DEFINEFUNC2(int, ASN1_STRING_print, BIO *a, a, const ASN1_STRING *b, b, return 0, return) -DEFINEFUNC2(int, X509_check_issued, X509 *a, a, X509 *b, b, return -1, return) -DEFINEFUNC(X509_NAME *, X509_get_issuer_name, X509 *a, a, return nullptr, return) -DEFINEFUNC(X509_NAME *, X509_get_subject_name, X509 *a, a, return nullptr, return) -DEFINEFUNC(ASN1_INTEGER *, X509_get_serialNumber, X509 *a, a, return nullptr, return) -DEFINEFUNC(int, X509_verify_cert, X509_STORE_CTX *a, a, return -1, return) -DEFINEFUNC(int, X509_NAME_entry_count, X509_NAME *a, a, return 0, return) -DEFINEFUNC2(X509_NAME_ENTRY *, X509_NAME_get_entry, X509_NAME *a, a, int b, b, return nullptr, return) -DEFINEFUNC(ASN1_STRING *, X509_NAME_ENTRY_get_data, X509_NAME_ENTRY *a, a, return nullptr, return) -DEFINEFUNC(ASN1_OBJECT *, X509_NAME_ENTRY_get_object, X509_NAME_ENTRY *a, a, return nullptr, return) -DEFINEFUNC(EVP_PKEY *, X509_PUBKEY_get, X509_PUBKEY *a, a, return nullptr, return) -DEFINEFUNC(void, X509_STORE_free, X509_STORE *a, a, return, DUMMYARG) -DEFINEFUNC(X509_STORE *, X509_STORE_new, DUMMYARG, DUMMYARG, return nullptr, return) -DEFINEFUNC2(int, X509_STORE_add_cert, X509_STORE *a, a, X509 *b, b, return 0, return) -DEFINEFUNC(void, X509_STORE_CTX_free, X509_STORE_CTX *a, a, return, DUMMYARG) -DEFINEFUNC4(int, X509_STORE_CTX_init, X509_STORE_CTX *a, a, X509_STORE *b, b, X509 *c, c, STACK_OF(X509) *d, d, return -1, return) -DEFINEFUNC2(int, X509_STORE_CTX_set_purpose, X509_STORE_CTX *a, a, int b, b, return -1, return) -DEFINEFUNC(int, X509_STORE_CTX_get_error, X509_STORE_CTX *a, a, return -1, return) -DEFINEFUNC(int, X509_STORE_CTX_get_error_depth, X509_STORE_CTX *a, a, return -1, return) -DEFINEFUNC(X509 *, X509_STORE_CTX_get_current_cert, X509_STORE_CTX *a, a, return nullptr, return) -DEFINEFUNC(X509_STORE *, X509_STORE_CTX_get0_store, X509_STORE_CTX *ctx, ctx, return nullptr, return) -DEFINEFUNC(X509_STORE_CTX *, X509_STORE_CTX_new, DUMMYARG, DUMMYARG, return nullptr, return) -DEFINEFUNC2(void *, X509_STORE_CTX_get_ex_data, X509_STORE_CTX *ctx, ctx, int idx, idx, return nullptr, return) -DEFINEFUNC(int, SSL_get_ex_data_X509_STORE_CTX_idx, DUMMYARG, DUMMYARG, return -1, return) - -#if OPENSSL_VERSION_MAJOR < 3 -DEFINEFUNC3(int, SSL_CTX_load_verify_locations, SSL_CTX *ctx, ctx, const char *CAfile, CAfile, const char *CApath, CApath, return 0, return) -#else -DEFINEFUNC2(int, SSL_CTX_load_verify_dir, SSL_CTX *ctx, ctx, const char *CApath, CApath, return 0, return) -#endif // OPENSSL_VERSION_MAJOR - -DEFINEFUNC2(int, i2d_SSL_SESSION, SSL_SESSION *in, in, unsigned char **pp, pp, return 0, return) -DEFINEFUNC3(SSL_SESSION *, d2i_SSL_SESSION, SSL_SESSION **a, a, const unsigned char **pp, pp, long length, length, return nullptr, return) - -#ifndef OPENSSL_NO_NEXTPROTONEG -DEFINEFUNC6(int, SSL_select_next_proto, unsigned char **out, out, unsigned char *outlen, outlen, - const unsigned char *in, in, unsigned int inlen, inlen, - const unsigned char *client, client, unsigned int client_len, client_len, - return -1, return) -DEFINEFUNC3(void, SSL_CTX_set_next_proto_select_cb, SSL_CTX *s, s, - int (*cb) (SSL *ssl, unsigned char **out, - unsigned char *outlen, - const unsigned char *in, - unsigned int inlen, void *arg), cb, - void *arg, arg, return, DUMMYARG) -DEFINEFUNC3(void, SSL_get0_next_proto_negotiated, const SSL *s, s, - const unsigned char **data, data, unsigned *len, len, return, DUMMYARG) -DEFINEFUNC3(int, SSL_set_alpn_protos, SSL *s, s, const unsigned char *protos, protos, - unsigned protos_len, protos_len, return -1, return) -DEFINEFUNC3(void, SSL_CTX_set_alpn_select_cb, SSL_CTX *s, s, - int (*cb) (SSL *ssl, const unsigned char **out, - unsigned char *outlen, - const unsigned char *in, - unsigned int inlen, void *arg), cb, - void *arg, arg, return, DUMMYARG) -DEFINEFUNC3(void, SSL_get0_alpn_selected, const SSL *s, s, const unsigned char **data, data, - unsigned *len, len, return, DUMMYARG) -#endif // !OPENSSL_NO_NEXTPROTONEG - -// DTLS: -#if QT_CONFIG(dtls) -DEFINEFUNC2(void, SSL_CTX_set_cookie_generate_cb, SSL_CTX *ctx, ctx, CookieGenerateCallback cb, cb, return, DUMMYARG) -DEFINEFUNC2(void, SSL_CTX_set_cookie_verify_cb, SSL_CTX *ctx, ctx, CookieVerifyCallback cb, cb, return, DUMMYARG) -DEFINEFUNC(const SSL_METHOD *, DTLS_server_method, DUMMYARG, DUMMYARG, return nullptr, return) -DEFINEFUNC(const SSL_METHOD *, DTLS_client_method, DUMMYARG, DUMMYARG, return nullptr, return) -#endif // dtls -DEFINEFUNC2(void, BIO_set_flags, BIO *b, b, int flags, flags, return, DUMMYARG) -DEFINEFUNC2(void, BIO_clear_flags, BIO *b, b, int flags, flags, return, DUMMYARG) -DEFINEFUNC2(void *, BIO_get_ex_data, BIO *b, b, int idx, idx, return nullptr, return) -DEFINEFUNC3(int, BIO_set_ex_data, BIO *b, b, int idx, idx, void *data, data, return -1, return) - -DEFINEFUNC3(void *, CRYPTO_malloc, size_t num, num, const char *file, file, int line, line, return nullptr, return) -DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return nullptr, return) -DEFINEFUNC(void, DH_free, DH *dh, dh, return, DUMMYARG) -DEFINEFUNC3(DH *, d2i_DHparams, DH**a, a, const unsigned char **pp, pp, long length, length, return nullptr, return) -DEFINEFUNC2(int, i2d_DHparams, DH *a, a, unsigned char **p, p, return -1, return) -#ifndef OPENSSL_NO_DEPRECATED_3_0 -DEFINEFUNC2(int, DH_check, DH *dh, dh, int *codes, codes, return 0, return) -#endif // OPENSSL_NO_DEPRECATED_3_0 -DEFINEFUNC3(BIGNUM *, BN_bin2bn, const unsigned char *s, s, int len, len, BIGNUM *ret, ret, return nullptr, return) - -#ifndef OPENSSL_NO_EC -DEFINEFUNC(EC_KEY *, EC_KEY_dup, const EC_KEY *ec, ec, return nullptr, return) -DEFINEFUNC(EC_KEY *, EC_KEY_new_by_curve_name, int nid, nid, return nullptr, return) -DEFINEFUNC(void, EC_KEY_free, EC_KEY *ecdh, ecdh, return, DUMMYARG) -DEFINEFUNC2(size_t, EC_get_builtin_curves, EC_builtin_curve * r, r, size_t nitems, nitems, return 0, return) -DEFINEFUNC(int, EC_curve_nist2nid, const char *name, name, return 0, return) -#endif // OPENSSL_NO_EC - -DEFINEFUNC5(int, PKCS12_parse, PKCS12 *p12, p12, const char *pass, pass, EVP_PKEY **pkey, pkey, \ - X509 **cert, cert, STACK_OF(X509) **ca, ca, return 1, return); -DEFINEFUNC2(PKCS12 *, d2i_PKCS12_bio, BIO *bio, bio, PKCS12 **pkcs12, pkcs12, return nullptr, return); -DEFINEFUNC(void, PKCS12_free, PKCS12 *pkcs12, pkcs12, return, DUMMYARG) - -#define RESOLVEFUNC(func) \ - if (!(_q_##func = _q_PTR_##func(libs.ssl->resolve(#func))) \ - && !(_q_##func = _q_PTR_##func(libs.crypto->resolve(#func)))) \ - qsslSocketCannotResolveSymbolWarning(#func); - -#if !defined QT_LINKED_OPENSSL - -#if !QT_CONFIG(library) -bool q_resolveOpenSslSymbols() -{ - qCWarning(lcSsl, "QSslSocket: unable to resolve symbols. Qt is configured without the " - "'library' feature, which means runtime resolving of libraries won't work."); - qCWarning(lcSsl, "Either compile Qt statically or with support for runtime resolving " - "of libraries."); - return false; -} -#else - -# ifdef Q_OS_UNIX -struct NumericallyLess -{ - typedef bool result_type; - result_type operator()(QStringView lhs, QStringView rhs) const - { - bool ok = false; - int b = 0; - int a = lhs.toInt(&ok); - if (ok) - b = rhs.toInt(&ok); - if (ok) { - // both toInt succeeded - return a < b; - } else { - // compare as strings; - return lhs < rhs; - } - } -}; - -struct LibGreaterThan -{ - typedef bool result_type; - result_type operator()(QStringView lhs, QStringView rhs) const - { - const auto lhsparts = lhs.split(QLatin1Char('.')); - const auto rhsparts = rhs.split(QLatin1Char('.')); - Q_ASSERT(lhsparts.count() > 1 && rhsparts.count() > 1); - - // note: checking rhs < lhs, the same as lhs > rhs - return std::lexicographical_compare(rhsparts.begin() + 1, rhsparts.end(), - lhsparts.begin() + 1, lhsparts.end(), - NumericallyLess()); - } -}; - -#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) -static int dlIterateCallback(struct dl_phdr_info *info, size_t size, void *data) -{ - if (size < sizeof (info->dlpi_addr) + sizeof (info->dlpi_name)) - return 1; - QDuplicateTracker<QString> *paths = (QDuplicateTracker<QString> *)data; - QString path = QString::fromLocal8Bit(info->dlpi_name); - if (!path.isEmpty()) { - QFileInfo fi(path); - path = fi.absolutePath(); - if (!path.isEmpty()) - (void)paths->hasSeen(std::move(path)); - } - return 0; -} -#endif - -static QStringList libraryPathList() -{ - QStringList paths; -# ifdef Q_OS_DARWIN - paths = QString::fromLatin1(qgetenv("DYLD_LIBRARY_PATH")) - .split(QLatin1Char(':'), Qt::SkipEmptyParts); - - // search in .app/Contents/Frameworks - UInt32 packageType; - CFBundleGetPackageInfo(CFBundleGetMainBundle(), &packageType, nullptr); - if (packageType == FOUR_CHAR_CODE('APPL')) { - QUrl bundleUrl = QUrl::fromCFURL(QCFType<CFURLRef>(CFBundleCopyBundleURL(CFBundleGetMainBundle()))); - QUrl frameworksUrl = QUrl::fromCFURL(QCFType<CFURLRef>(CFBundleCopyPrivateFrameworksURL(CFBundleGetMainBundle()))); - paths << bundleUrl.resolved(frameworksUrl).path(); - } -# else - paths = QString::fromLatin1(qgetenv("LD_LIBRARY_PATH")) - .split(QLatin1Char(':'), Qt::SkipEmptyParts); -# endif - paths << QLatin1String("/lib") << QLatin1String("/usr/lib") << QLatin1String("/usr/local/lib"); - paths << QLatin1String("/lib64") << QLatin1String("/usr/lib64") << QLatin1String("/usr/local/lib64"); - paths << QLatin1String("/lib32") << QLatin1String("/usr/lib32") << QLatin1String("/usr/local/lib32"); - -#if defined(Q_OS_ANDROID) - paths << QLatin1String("/system/lib"); -#elif defined(Q_OS_LINUX) - // discover paths of already loaded libraries - QDuplicateTracker<QString> loadedPaths; - dl_iterate_phdr(dlIterateCallback, &loadedPaths); - std::move(loadedPaths).appendTo(paths); -#endif - - return paths; -} - -Q_NEVER_INLINE -static QStringList findAllLibs(QLatin1String filter) -{ - const QStringList paths = libraryPathList(); - QStringList found; - const QStringList filters((QString(filter))); - - for (const QString &path : paths) { - QDir dir(path); - QStringList entryList = dir.entryList(filters, QDir::Files); - - std::sort(entryList.begin(), entryList.end(), LibGreaterThan()); - for (const QString &entry : qAsConst(entryList)) - found << path + QLatin1Char('/') + entry; - } - - return found; -} - -static QStringList findAllLibSsl() -{ - return findAllLibs(QLatin1String("libssl.*")); -} - -static QStringList findAllLibCrypto() -{ - return findAllLibs(QLatin1String("libcrypto.*")); -} -# endif - -#ifdef Q_OS_WIN - -struct LoadedOpenSsl { - std::unique_ptr<QSystemLibrary> ssl, crypto; -}; - -static bool tryToLoadOpenSslWin32Library(QLatin1String ssleay32LibName, QLatin1String libeay32LibName, LoadedOpenSsl &result) -{ - auto ssleay32 = std::make_unique<QSystemLibrary>(ssleay32LibName); - if (!ssleay32->load(false)) { - return FALSE; - } - - auto libeay32 = std::make_unique<QSystemLibrary>(libeay32LibName); - if (!libeay32->load(false)) { - return FALSE; - } - - result.ssl = std::move(ssleay32); - result.crypto = std::move(libeay32); - return TRUE; -} - -static LoadedOpenSsl loadOpenSsl() -{ - LoadedOpenSsl result; - - // With OpenSSL 1.1 the names have changed to libssl-1_1 and libcrypto-1_1 for builds using - // MSVC and GCC, with architecture suffixes for non-x86 builds. - -#if defined(Q_PROCESSOR_X86_64) -#define QT_SSL_SUFFIX "-x64" -#elif defined(Q_PROCESSOR_ARM_64) -#define QT_SSL_SUFFIX "-arm64" -#elif defined(Q_PROCESSOR_ARM_32) -#define QT_SSL_SUFFIX "-arm" -#else -#define QT_SSL_SUFFIX -#endif - - tryToLoadOpenSslWin32Library(QLatin1String("libssl-1_1" QT_SSL_SUFFIX), - QLatin1String("libcrypto-1_1" QT_SSL_SUFFIX), result); - -#undef QT_SSL_SUFFIX - return result; -} -#else - -struct LoadedOpenSsl { - std::unique_ptr<QLibrary> ssl, crypto; -}; - -static LoadedOpenSsl loadOpenSsl() -{ - LoadedOpenSsl result = { std::make_unique<QLibrary>(), std::make_unique<QLibrary>() }; - -# if defined(Q_OS_UNIX) - QLibrary * const libssl = result.ssl.get(); - QLibrary * const libcrypto = result.crypto.get(); - - // Try to find the libssl library on the system. - // - // Up until Qt 4.3, this only searched for the "ssl" library at version -1, that - // is, libssl.so on most Unix systems. However, the .so file isn't present in - // user installations because it's considered a development file. - // - // The right thing to do is to load the library at the major version we know how - // to work with: the SHLIB_VERSION_NUMBER version (macro defined in opensslv.h) - // - // However, OpenSSL is a well-known case of binary-compatibility breakage. To - // avoid such problems, many system integrators and Linux distributions change - // the soname of the binary, letting the full version number be the soname. So - // we'll find libssl.so.0.9.7, libssl.so.0.9.8, etc. in the system. For that - // reason, we will search a few common paths (see findAllLibSsl() above) in hopes - // we find one that works. - // - // If that fails, for OpenSSL 1.0 we also try some fallbacks -- look up - // libssl.so with a hardcoded soname. The reason is QTBUG-68156: the binary - // builds of Qt happen (at the time of this writing) on RHEL machines, - // which change SHLIB_VERSION_NUMBER to a non-portable string. When running - // those binaries on the target systems, this code won't pick up - // libssl.so.MODIFIED_SHLIB_VERSION_NUMBER because it doesn't exist there. - // Given that the only 1.0 supported release (at the time of this writing) - // is 1.0.2, with soname "1.0.0", give that a try too. Note that we mandate - // OpenSSL >= 1.0.0 with a configure-time check, and OpenSSL has kept binary - // compatibility between 1.0.0 and 1.0.2. - // - // It is important, however, to try the canonical name and the unversioned name - // without going through the loop. By not specifying a path, we let the system - // dlopen(3) function determine it for us. This will include any DT_RUNPATH or - // DT_RPATH tags on our library header as well as other system-specific search - // paths. See the man page for dlopen(3) on your system for more information. - -#ifdef Q_OS_OPENBSD - libcrypto->setLoadHints(QLibrary::ExportExternalSymbolsHint); -#endif -#if defined(SHLIB_VERSION_NUMBER) && !defined(Q_OS_QNX) // on QNX, the libs are always libssl.so and libcrypto.so - // first attempt: the canonical name is libssl.so.<SHLIB_VERSION_NUMBER> - libssl->setFileNameAndVersion(QLatin1String("ssl"), QLatin1String(SHLIB_VERSION_NUMBER)); - libcrypto->setFileNameAndVersion(QLatin1String("crypto"), QLatin1String(SHLIB_VERSION_NUMBER)); - if (libcrypto->load() && libssl->load()) { - // libssl.so.<SHLIB_VERSION_NUMBER> and libcrypto.so.<SHLIB_VERSION_NUMBER> found - return result; - } else { - libssl->unload(); - libcrypto->unload(); - } -#endif - -#ifndef Q_OS_DARWIN - // second attempt: find the development files libssl.so and libcrypto.so - // - // disabled on macOS/iOS: - // macOS's /usr/lib/libssl.dylib, /usr/lib/libcrypto.dylib will be picked up in the third - // attempt, _after_ <bundle>/Contents/Frameworks has been searched. - // iOS does not ship a system libssl.dylib, libcrypto.dylib in the first place. -# if defined(Q_OS_ANDROID) - // OpenSSL 1.1.x must be suffixed otherwise it will use the system libcrypto.so libssl.so which on API-21 are OpenSSL 1.0 not 1.1 - auto openSSLSuffix = [](const QByteArray &defaultSuffix = {}) { - auto suffix = qgetenv("ANDROID_OPENSSL_SUFFIX"); - if (suffix.isEmpty()) - return defaultSuffix; - return suffix; - }; - - static QString suffix = QString::fromLatin1(openSSLSuffix("_1_1")); - - libssl->setFileNameAndVersion(QLatin1String("ssl") + suffix, -1); - libcrypto->setFileNameAndVersion(QLatin1String("crypto") + suffix, -1); -# else - libssl->setFileNameAndVersion(QLatin1String("ssl"), -1); - libcrypto->setFileNameAndVersion(QLatin1String("crypto"), -1); -# endif - if (libcrypto->load() && libssl->load()) { - // libssl.so.0 and libcrypto.so.0 found - return result; - } else { - libssl->unload(); - libcrypto->unload(); - } -#endif - - // third attempt: loop on the most common library paths and find libssl - const QStringList sslList = findAllLibSsl(); - const QStringList cryptoList = findAllLibCrypto(); - - for (const QString &crypto : cryptoList) { - libcrypto->setFileNameAndVersion(crypto, -1); - if (libcrypto->load()) { - QFileInfo fi(crypto); - QString version = fi.completeSuffix(); - - for (const QString &ssl : sslList) { - if (!ssl.endsWith(version)) - continue; - - libssl->setFileNameAndVersion(ssl, -1); - - if (libssl->load()) { - // libssl.so.x and libcrypto.so.x found - return result; - } else { - libssl->unload(); - } - } - } - libcrypto->unload(); - } - - // failed to load anything - result = {}; - return result; - -# else - // not implemented for this platform yet - return result; -# endif -} -#endif - -static QBasicMutex symbolResolveMutex; -static QBasicAtomicInt symbolsResolved = Q_BASIC_ATOMIC_INITIALIZER(false); -static bool triedToResolveSymbols = false; - -bool q_resolveOpenSslSymbols() -{ - if (symbolsResolved.loadAcquire()) - return true; - QMutexLocker locker(&symbolResolveMutex); - if (symbolsResolved.loadRelaxed()) - return true; - if (triedToResolveSymbols) - return false; - triedToResolveSymbols = true; - - LoadedOpenSsl libs = loadOpenSsl(); - if (!libs.ssl || !libs.crypto) - // failed to load them - return false; - - RESOLVEFUNC(OPENSSL_init_ssl) - RESOLVEFUNC(OPENSSL_init_crypto) - RESOLVEFUNC(ASN1_STRING_get0_data) - RESOLVEFUNC(EVP_CIPHER_CTX_reset) - RESOLVEFUNC(EVP_PKEY_up_ref) - RESOLVEFUNC(EVP_PKEY_CTX_new) - RESOLVEFUNC(EVP_PKEY_param_check) - RESOLVEFUNC(EVP_PKEY_CTX_free) - RESOLVEFUNC(EVP_PKEY_base_id) - RESOLVEFUNC(RSA_bits) - RESOLVEFUNC(OPENSSL_sk_new_null) - RESOLVEFUNC(OPENSSL_sk_push) - RESOLVEFUNC(OPENSSL_sk_free) - RESOLVEFUNC(OPENSSL_sk_num) - RESOLVEFUNC(OPENSSL_sk_pop_free) - RESOLVEFUNC(OPENSSL_sk_value) - RESOLVEFUNC(DH_get0_pqg) - RESOLVEFUNC(SSL_CTX_set_options) - RESOLVEFUNC(SSL_set_info_callback) - RESOLVEFUNC(SSL_alert_type_string) - RESOLVEFUNC(SSL_alert_desc_string_long) - RESOLVEFUNC(SSL_CTX_get_security_level) - RESOLVEFUNC(SSL_CTX_set_security_level) -#ifdef TLS1_3_VERSION - RESOLVEFUNC(SSL_CTX_set_ciphersuites) - RESOLVEFUNC(SSL_set_psk_use_session_callback) - RESOLVEFUNC(SSL_CTX_sess_set_new_cb) - RESOLVEFUNC(SSL_SESSION_is_resumable) -#endif // TLS 1.3 or OpenSSL > 1.1.1 - - RESOLVEFUNC(SSL_get_client_random) - RESOLVEFUNC(SSL_SESSION_get_master_key) - RESOLVEFUNC(SSL_session_reused) - RESOLVEFUNC(SSL_get_session) - RESOLVEFUNC(SSL_set_options) - RESOLVEFUNC(CRYPTO_get_ex_new_index) - RESOLVEFUNC(TLS_method) - RESOLVEFUNC(TLS_client_method) - RESOLVEFUNC(TLS_server_method) - RESOLVEFUNC(X509_up_ref) - RESOLVEFUNC(X509_STORE_CTX_get0_chain) - RESOLVEFUNC(X509_getm_notBefore) - RESOLVEFUNC(X509_getm_notAfter) - RESOLVEFUNC(X509_get_version) - RESOLVEFUNC(X509_get_pubkey) - RESOLVEFUNC(X509_STORE_set_verify_cb) - RESOLVEFUNC(X509_STORE_set_ex_data) - RESOLVEFUNC(X509_STORE_get_ex_data) - RESOLVEFUNC(CRYPTO_free) - RESOLVEFUNC(OpenSSL_version_num) - RESOLVEFUNC(OpenSSL_version) - - if (!_q_OpenSSL_version) { - // Apparently, we were built with OpenSSL 1.1 enabled but are now using - // a wrong library. - qCWarning(lcSsl, "Incompatible version of OpenSSL"); - return false; - } - - RESOLVEFUNC(SSL_SESSION_get_ticket_lifetime_hint) - RESOLVEFUNC(DH_bits) - RESOLVEFUNC(DSA_bits) - -#if QT_CONFIG(dtls) - RESOLVEFUNC(DTLSv1_listen) - RESOLVEFUNC(BIO_ADDR_new) - RESOLVEFUNC(BIO_ADDR_free) - RESOLVEFUNC(BIO_meth_new) - RESOLVEFUNC(BIO_meth_free) - RESOLVEFUNC(BIO_meth_set_write) - RESOLVEFUNC(BIO_meth_set_read) - RESOLVEFUNC(BIO_meth_set_puts) - RESOLVEFUNC(BIO_meth_set_ctrl) - RESOLVEFUNC(BIO_meth_set_create) - RESOLVEFUNC(BIO_meth_set_destroy) -#endif // dtls - -#if QT_CONFIG(ocsp) - RESOLVEFUNC(OCSP_SINGLERESP_get0_id) - RESOLVEFUNC(d2i_OCSP_RESPONSE) - RESOLVEFUNC(OCSP_RESPONSE_free) - RESOLVEFUNC(OCSP_response_status) - RESOLVEFUNC(OCSP_response_get1_basic) - RESOLVEFUNC(OCSP_BASICRESP_free) - RESOLVEFUNC(OCSP_basic_verify) - RESOLVEFUNC(OCSP_resp_count) - RESOLVEFUNC(OCSP_resp_get0) - RESOLVEFUNC(OCSP_single_get0_status) - RESOLVEFUNC(OCSP_check_validity) - RESOLVEFUNC(OCSP_cert_to_id) - RESOLVEFUNC(OCSP_id_get0_info) - RESOLVEFUNC(OCSP_resp_get0_certs) - RESOLVEFUNC(OCSP_basic_sign) - RESOLVEFUNC(OCSP_response_create) - RESOLVEFUNC(i2d_OCSP_RESPONSE) - RESOLVEFUNC(OCSP_basic_add1_status) - RESOLVEFUNC(OCSP_BASICRESP_new) - RESOLVEFUNC(OCSP_CERTID_free) - RESOLVEFUNC(OCSP_cert_to_id) - RESOLVEFUNC(OCSP_id_cmp) -#endif // ocsp - - RESOLVEFUNC(BIO_set_data) - RESOLVEFUNC(BIO_get_data) - RESOLVEFUNC(BIO_set_init) - RESOLVEFUNC(BIO_get_shutdown) - RESOLVEFUNC(BIO_set_shutdown) - RESOLVEFUNC(ASN1_INTEGER_get) - RESOLVEFUNC(ASN1_INTEGER_cmp) - RESOLVEFUNC(ASN1_STRING_length) - RESOLVEFUNC(ASN1_STRING_to_UTF8) - RESOLVEFUNC(ASN1_TIME_to_tm) - RESOLVEFUNC(BIO_ctrl) - RESOLVEFUNC(BIO_free) - RESOLVEFUNC(BIO_new) - RESOLVEFUNC(BIO_new_mem_buf) - RESOLVEFUNC(BIO_read) - RESOLVEFUNC(BIO_s_mem) - RESOLVEFUNC(BIO_write) - RESOLVEFUNC(BIO_set_flags) - RESOLVEFUNC(BIO_clear_flags) - RESOLVEFUNC(BIO_set_ex_data) - RESOLVEFUNC(BIO_get_ex_data) - -#ifndef OPENSSL_NO_EC - RESOLVEFUNC(EC_KEY_get0_group) - RESOLVEFUNC(EC_GROUP_get_degree) -#endif - RESOLVEFUNC(BN_num_bits) - RESOLVEFUNC(BN_is_word) - RESOLVEFUNC(BN_mod_word) - RESOLVEFUNC(DSA_new) - RESOLVEFUNC(DSA_free) - RESOLVEFUNC(ERR_error_string) - RESOLVEFUNC(ERR_error_string_n) - RESOLVEFUNC(ERR_get_error) - RESOLVEFUNC(EVP_CIPHER_CTX_new) - RESOLVEFUNC(EVP_CIPHER_CTX_free) - RESOLVEFUNC(EVP_CIPHER_CTX_ctrl) - RESOLVEFUNC(EVP_CIPHER_CTX_set_key_length) - RESOLVEFUNC(EVP_CipherInit) - RESOLVEFUNC(EVP_CipherInit_ex) - RESOLVEFUNC(EVP_CipherUpdate) - RESOLVEFUNC(EVP_CipherFinal) - RESOLVEFUNC(EVP_get_digestbyname) -#ifndef OPENSSL_NO_DES - RESOLVEFUNC(EVP_des_cbc) - RESOLVEFUNC(EVP_des_ede3_cbc) -#endif -#ifndef OPENSSL_NO_RC2 - RESOLVEFUNC(EVP_rc2_cbc) -#endif -#ifndef OPENSSL_NO_AES - RESOLVEFUNC(EVP_aes_128_cbc) - RESOLVEFUNC(EVP_aes_192_cbc) - RESOLVEFUNC(EVP_aes_256_cbc) -#endif - RESOLVEFUNC(EVP_sha1) - RESOLVEFUNC(EVP_PKEY_assign) - RESOLVEFUNC(EVP_PKEY_set1_RSA) - RESOLVEFUNC(EVP_PKEY_set1_DSA) - RESOLVEFUNC(EVP_PKEY_set1_DH) - -#ifndef OPENSSL_NO_EC - RESOLVEFUNC(EVP_PKEY_set1_EC_KEY) - RESOLVEFUNC(EVP_PKEY_get1_EC_KEY) - RESOLVEFUNC(PEM_read_bio_ECPrivateKey) - RESOLVEFUNC(PEM_write_bio_ECPrivateKey) - RESOLVEFUNC(PEM_read_bio_EC_PUBKEY) - RESOLVEFUNC(PEM_write_bio_EC_PUBKEY) -#endif // OPENSSL_NO_EC - - RESOLVEFUNC(EVP_PKEY_cmp) - RESOLVEFUNC(EVP_PKEY_free) - RESOLVEFUNC(EVP_PKEY_get1_DSA) - RESOLVEFUNC(EVP_PKEY_get1_RSA) - RESOLVEFUNC(EVP_PKEY_get1_DH) - RESOLVEFUNC(EVP_PKEY_new) - RESOLVEFUNC(EVP_PKEY_type) - RESOLVEFUNC(OBJ_nid2sn) - RESOLVEFUNC(OBJ_nid2ln) - RESOLVEFUNC(OBJ_sn2nid) - RESOLVEFUNC(OBJ_ln2nid) - RESOLVEFUNC(i2t_ASN1_OBJECT) - RESOLVEFUNC(OBJ_obj2txt) - RESOLVEFUNC(OBJ_obj2nid) - RESOLVEFUNC(PEM_read_bio_PrivateKey) - RESOLVEFUNC(PEM_read_bio_DSAPrivateKey) - RESOLVEFUNC(PEM_read_bio_RSAPrivateKey) - RESOLVEFUNC(PEM_read_bio_DHparams) - RESOLVEFUNC(PEM_write_bio_DSAPrivateKey) - RESOLVEFUNC(PEM_write_bio_RSAPrivateKey) - RESOLVEFUNC(PEM_write_bio_PrivateKey) - RESOLVEFUNC(PEM_read_bio_PUBKEY) - RESOLVEFUNC(PEM_read_bio_DSA_PUBKEY) - RESOLVEFUNC(PEM_read_bio_RSA_PUBKEY) - RESOLVEFUNC(PEM_write_bio_DSA_PUBKEY) - RESOLVEFUNC(PEM_write_bio_RSA_PUBKEY) - RESOLVEFUNC(PEM_write_bio_PUBKEY) - RESOLVEFUNC(RAND_seed) - RESOLVEFUNC(RAND_status) - RESOLVEFUNC(RAND_bytes) - RESOLVEFUNC(RSA_new) - RESOLVEFUNC(RSA_free) - RESOLVEFUNC(SSL_CIPHER_description) - RESOLVEFUNC(SSL_CIPHER_get_bits) - RESOLVEFUNC(SSL_get_rbio) - RESOLVEFUNC(SSL_CTX_check_private_key) - RESOLVEFUNC(SSL_CTX_ctrl) - RESOLVEFUNC(SSL_CTX_free) - RESOLVEFUNC(SSL_CTX_new) - RESOLVEFUNC(SSL_CTX_set_cipher_list) - RESOLVEFUNC(SSL_CTX_callback_ctrl) - RESOLVEFUNC(SSL_CTX_set_default_verify_paths) - RESOLVEFUNC(SSL_CTX_set_verify) - RESOLVEFUNC(SSL_CTX_set_verify_depth) - RESOLVEFUNC(SSL_CTX_use_certificate) - RESOLVEFUNC(SSL_CTX_use_certificate_file) - RESOLVEFUNC(SSL_CTX_use_PrivateKey) - RESOLVEFUNC(SSL_CTX_use_RSAPrivateKey) - RESOLVEFUNC(SSL_CTX_use_PrivateKey_file) - RESOLVEFUNC(SSL_CTX_get_cert_store); - RESOLVEFUNC(SSL_CONF_CTX_new); - RESOLVEFUNC(SSL_CONF_CTX_free); - RESOLVEFUNC(SSL_CONF_CTX_set_ssl_ctx); - RESOLVEFUNC(SSL_CONF_CTX_set_flags); - RESOLVEFUNC(SSL_CONF_CTX_finish); - RESOLVEFUNC(SSL_CONF_cmd); - RESOLVEFUNC(SSL_accept) - RESOLVEFUNC(SSL_clear) - RESOLVEFUNC(SSL_connect) - RESOLVEFUNC(SSL_free) - RESOLVEFUNC(SSL_get_ciphers) - RESOLVEFUNC(SSL_get_current_cipher) - RESOLVEFUNC(SSL_version) - RESOLVEFUNC(SSL_get_error) - RESOLVEFUNC(SSL_get_peer_cert_chain) - RESOLVEFUNC(SSL_get_peer_certificate) - RESOLVEFUNC(SSL_get_verify_result) - RESOLVEFUNC(SSL_new) - RESOLVEFUNC(SSL_get_SSL_CTX) - RESOLVEFUNC(SSL_ctrl) - RESOLVEFUNC(SSL_read) - RESOLVEFUNC(SSL_set_accept_state) - RESOLVEFUNC(SSL_set_bio) - RESOLVEFUNC(SSL_set_connect_state) - RESOLVEFUNC(SSL_shutdown) - RESOLVEFUNC(SSL_in_init) - RESOLVEFUNC(SSL_get_shutdown) - RESOLVEFUNC(SSL_set_session) - RESOLVEFUNC(SSL_SESSION_free) - RESOLVEFUNC(SSL_get1_session) - RESOLVEFUNC(SSL_get_session) - RESOLVEFUNC(SSL_set_ex_data) - RESOLVEFUNC(SSL_get_ex_data) - RESOLVEFUNC(SSL_get_ex_data_X509_STORE_CTX_idx) - -#ifndef OPENSSL_NO_PSK - RESOLVEFUNC(SSL_set_psk_client_callback) - RESOLVEFUNC(SSL_set_psk_server_callback) - RESOLVEFUNC(SSL_CTX_use_psk_identity_hint) -#endif // !OPENSSL_NO_PSK - - RESOLVEFUNC(SSL_write) - RESOLVEFUNC(X509_NAME_entry_count) - RESOLVEFUNC(X509_NAME_get_entry) - RESOLVEFUNC(X509_NAME_ENTRY_get_data) - RESOLVEFUNC(X509_NAME_ENTRY_get_object) - RESOLVEFUNC(X509_PUBKEY_get) - RESOLVEFUNC(X509_STORE_free) - RESOLVEFUNC(X509_STORE_new) - RESOLVEFUNC(X509_STORE_add_cert) - RESOLVEFUNC(X509_STORE_CTX_free) - RESOLVEFUNC(X509_STORE_CTX_init) - RESOLVEFUNC(X509_STORE_CTX_new) - RESOLVEFUNC(X509_STORE_CTX_set_purpose) - RESOLVEFUNC(X509_STORE_CTX_get_error) - RESOLVEFUNC(X509_STORE_CTX_get_error_depth) - RESOLVEFUNC(X509_STORE_CTX_get_current_cert) - RESOLVEFUNC(X509_STORE_CTX_get0_store) - RESOLVEFUNC(X509_cmp) - RESOLVEFUNC(X509_STORE_CTX_get_ex_data) - RESOLVEFUNC(X509_dup) - RESOLVEFUNC(X509_print) - RESOLVEFUNC(X509_digest) - RESOLVEFUNC(X509_EXTENSION_get_object) - RESOLVEFUNC(X509_free) - RESOLVEFUNC(X509_gmtime_adj) - RESOLVEFUNC(ASN1_TIME_free) - RESOLVEFUNC(X509_get_ext) - RESOLVEFUNC(X509_get_ext_count) - RESOLVEFUNC(X509_get_ext_d2i) - RESOLVEFUNC(X509V3_EXT_get) - RESOLVEFUNC(X509V3_EXT_d2i) - RESOLVEFUNC(X509_EXTENSION_get_critical) - RESOLVEFUNC(X509_EXTENSION_get_data) - RESOLVEFUNC(BASIC_CONSTRAINTS_free) - RESOLVEFUNC(AUTHORITY_KEYID_free) - RESOLVEFUNC(GENERAL_NAME_free) - RESOLVEFUNC(ASN1_STRING_print) - RESOLVEFUNC(X509_check_issued) - RESOLVEFUNC(X509_get_issuer_name) - RESOLVEFUNC(X509_get_subject_name) - RESOLVEFUNC(X509_get_serialNumber) - RESOLVEFUNC(X509_verify_cert) - RESOLVEFUNC(d2i_X509) - RESOLVEFUNC(i2d_X509) -#if OPENSSL_VERSION_MAJOR < 3 - RESOLVEFUNC(SSL_CTX_load_verify_locations) -#else - RESOLVEFUNC(SSL_CTX_load_verify_dir) -#endif // OPENSSL_VERSION_MAJOR - RESOLVEFUNC(i2d_SSL_SESSION) - RESOLVEFUNC(d2i_SSL_SESSION) - -#ifndef OPENSSL_NO_NEXTPROTONEG - RESOLVEFUNC(SSL_select_next_proto) - RESOLVEFUNC(SSL_CTX_set_next_proto_select_cb) - RESOLVEFUNC(SSL_get0_next_proto_negotiated) - RESOLVEFUNC(SSL_set_alpn_protos) - RESOLVEFUNC(SSL_CTX_set_alpn_select_cb) - RESOLVEFUNC(SSL_get0_alpn_selected) -#endif // !OPENSSL_NO_NEXTPROTONEG - -#if QT_CONFIG(dtls) - RESOLVEFUNC(SSL_CTX_set_cookie_generate_cb) - RESOLVEFUNC(SSL_CTX_set_cookie_verify_cb) - RESOLVEFUNC(DTLS_server_method) - RESOLVEFUNC(DTLS_client_method) -#endif // dtls - - RESOLVEFUNC(CRYPTO_malloc) - RESOLVEFUNC(DH_new) - RESOLVEFUNC(DH_free) - RESOLVEFUNC(d2i_DHparams) - RESOLVEFUNC(i2d_DHparams) -#ifndef OPENSSL_NO_DEPRECATED_3_0 - RESOLVEFUNC(DH_check) -#endif // OPENSSL_NO_DEPRECATED_3_0 - RESOLVEFUNC(BN_bin2bn) - -#ifndef OPENSSL_NO_EC - RESOLVEFUNC(EC_KEY_dup) - RESOLVEFUNC(EC_KEY_new_by_curve_name) - RESOLVEFUNC(EC_KEY_free) - RESOLVEFUNC(EC_get_builtin_curves) -#endif // OPENSSL_NO_EC - - RESOLVEFUNC(PKCS12_parse) - RESOLVEFUNC(d2i_PKCS12_bio) - RESOLVEFUNC(PKCS12_free) - - symbolsResolved.storeRelease(true); - return true; -} -#endif // QT_CONFIG(library) - -#else // !defined QT_LINKED_OPENSSL - -bool q_resolveOpenSslSymbols() -{ -#ifdef QT_NO_OPENSSL - return false; -#endif - return true; -} -#endif // !defined QT_LINKED_OPENSSL - -QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime) -{ - QDateTime result; - tm lTime; - - if (q_ASN1_TIME_to_tm(aTime, &lTime)) { - QDate resDate(lTime.tm_year + 1900, lTime.tm_mon + 1, lTime.tm_mday); - QTime resTime(lTime.tm_hour, lTime.tm_min, lTime.tm_sec); - result = QDateTime(resDate, resTime, Qt::UTC); - } - - return result; -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h deleted file mode 100644 index f733c64fe3..0000000000 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ /dev/null @@ -1,761 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Copyright (C) 2014 BlackBerry Limited. All rights reserved. -** 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$ -** -****************************************************************************/ - -/**************************************************************************** -** -** In addition, as a special exception, the copyright holders listed above give -** permission to link the code of its release of Qt with the OpenSSL project's -** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the -** same license as the original version), and distribute the linked executables. -** -** You must comply with the GNU General Public License version 2 in all -** respects for all of the code used other than the "OpenSSL" code. If you -** modify this file, you may extend this exception to your version of the file, -** but you are not obligated to do so. If you do not wish to do so, delete -** this exception statement from your version of this file. -** -****************************************************************************/ - -#ifndef QSSLSOCKET_OPENSSL_SYMBOLS_P_H -#define QSSLSOCKET_OPENSSL_SYMBOLS_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 <QtNetwork/private/qtnetworkglobal_p.h> -#include "qopenssl_p.h" -#include <QtCore/qglobal.h> - -#if QT_CONFIG(ocsp) -#include "qocsp_p.h" -#endif - -QT_BEGIN_NAMESPACE - -#define DUMMYARG - -#if !defined QT_LINKED_OPENSSL -// **************** Shared declarations ****************** -// ret func(arg) - -# define DEFINEFUNC(ret, func, arg, a, err, funcret) \ - typedef ret (*_q_PTR_##func)(arg); \ - static _q_PTR_##func _q_##func = nullptr; \ - ret q_##func(arg) { \ - if (Q_UNLIKELY(!_q_##func)) { \ - qsslSocketUnresolvedSymbolWarning(#func); \ - err; \ - } \ - funcret _q_##func(a); \ - } - -// ret func(arg1, arg2) -# define DEFINEFUNC2(ret, func, arg1, a, arg2, b, err, funcret) \ - typedef ret (*_q_PTR_##func)(arg1, arg2); \ - static _q_PTR_##func _q_##func = nullptr; \ - ret q_##func(arg1, arg2) { \ - if (Q_UNLIKELY(!_q_##func)) { \ - qsslSocketUnresolvedSymbolWarning(#func);\ - err; \ - } \ - funcret _q_##func(a, b); \ - } - -// ret func(arg1, arg2, arg3) -# define DEFINEFUNC3(ret, func, arg1, a, arg2, b, arg3, c, err, funcret) \ - typedef ret (*_q_PTR_##func)(arg1, arg2, arg3); \ - static _q_PTR_##func _q_##func = nullptr; \ - ret q_##func(arg1, arg2, arg3) { \ - if (Q_UNLIKELY(!_q_##func)) { \ - qsslSocketUnresolvedSymbolWarning(#func); \ - err; \ - } \ - funcret _q_##func(a, b, c); \ - } - -// ret func(arg1, arg2, arg3, arg4) -# define DEFINEFUNC4(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, err, funcret) \ - typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4); \ - static _q_PTR_##func _q_##func = nullptr; \ - ret q_##func(arg1, arg2, arg3, arg4) { \ - if (Q_UNLIKELY(!_q_##func)) { \ - qsslSocketUnresolvedSymbolWarning(#func); \ - err; \ - } \ - funcret _q_##func(a, b, c, d); \ - } - -// ret func(arg1, arg2, arg3, arg4, arg5) -# define DEFINEFUNC5(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, err, funcret) \ - typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5); \ - static _q_PTR_##func _q_##func = nullptr; \ - ret q_##func(arg1, arg2, arg3, arg4, arg5) { \ - if (Q_UNLIKELY(!_q_##func)) { \ - qsslSocketUnresolvedSymbolWarning(#func); \ - err; \ - } \ - funcret _q_##func(a, b, c, d, e); \ - } - -// ret func(arg1, arg2, arg3, arg4, arg6) -# define DEFINEFUNC6(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, err, funcret) \ - typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6); \ - static _q_PTR_##func _q_##func = nullptr; \ - ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6) { \ - if (Q_UNLIKELY(!_q_##func)) { \ - qsslSocketUnresolvedSymbolWarning(#func); \ - err; \ - } \ - funcret _q_##func(a, b, c, d, e, f); \ - } - -// ret func(arg1, arg2, arg3, arg4, arg6, arg7) -# define DEFINEFUNC7(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, err, funcret) \ - typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7); \ - static _q_PTR_##func _q_##func = nullptr; \ - ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { \ - if (Q_UNLIKELY(!_q_##func)) { \ - qsslSocketUnresolvedSymbolWarning(#func); \ - err; \ - } \ - funcret _q_##func(a, b, c, d, e, f, g); \ - } - -// ret func(arg1, arg2, arg3, arg4, arg6, arg7, arg8, arg9) -# define DEFINEFUNC9(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, arg8, h, arg9, i, err, funcret) \ - typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); \ - static _q_PTR_##func _q_##func = nullptr; \ - ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { \ - if (Q_UNLIKELY(!_q_##func)) { \ - qsslSocketUnresolvedSymbolWarning(#func); \ - err; \ - } \ - funcret _q_##func(a, b, c, d, e, f, g, h, i); \ - } -// **************** Shared declarations ****************** - -#else // !defined QT_LINKED_OPENSSL - -// **************** Static declarations ****************** - -// ret func(arg) -# define DEFINEFUNC(ret, func, arg, a, err, funcret) \ - ret q_##func(arg) { funcret func(a); } - -// ret func(arg1, arg2) -# define DEFINEFUNC2(ret, func, arg1, a, arg2, b, err, funcret) \ - ret q_##func(arg1, arg2) { funcret func(a, b); } - -// ret func(arg1, arg2, arg3) -# define DEFINEFUNC3(ret, func, arg1, a, arg2, b, arg3, c, err, funcret) \ - ret q_##func(arg1, arg2, arg3) { funcret func(a, b, c); } - -// ret func(arg1, arg2, arg3, arg4) -# define DEFINEFUNC4(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, err, funcret) \ - ret q_##func(arg1, arg2, arg3, arg4) { funcret func(a, b, c, d); } - -// ret func(arg1, arg2, arg3, arg4, arg5) -# define DEFINEFUNC5(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, err, funcret) \ - ret q_##func(arg1, arg2, arg3, arg4, arg5) { funcret func(a, b, c, d, e); } - -// ret func(arg1, arg2, arg3, arg4, arg6) -# define DEFINEFUNC6(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, err, funcret) \ - ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6) { funcret func(a, b, c, d, e, f); } - -// ret func(arg1, arg2, arg3, arg4, arg6, arg7) -# define DEFINEFUNC7(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, err, funcret) \ - ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { funcret func(a, b, c, d, e, f, g); } - -// ret func(arg1, arg2, arg3, arg4, arg6, arg7, arg8, arg9) -# define DEFINEFUNC9(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, arg8, h, arg9, i, err, funcret) \ - ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { funcret func(a, b, c, d, e, f, g, h, i); } - -// **************** Static declarations ****************** - -#endif // !defined QT_LINKED_OPENSSL - -// TODO: the following lines previously were a part of 1.1 - specific header. -// To reduce the amount of the change, I'm directly copying and pasting the -// content of the header here. Later, can be better sorted/split into groups, -// depending on the functionality. - -const unsigned char * q_ASN1_STRING_get0_data(const ASN1_STRING *x); - -Q_AUTOTEST_EXPORT BIO *q_BIO_new(const BIO_METHOD *a); -Q_AUTOTEST_EXPORT const BIO_METHOD *q_BIO_s_mem(); - -int q_DSA_bits(DSA *a); -int q_EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *c); -Q_AUTOTEST_EXPORT int q_EVP_PKEY_up_ref(EVP_PKEY *a); -EVP_PKEY_CTX *q_EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e); -void q_EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx); -int q_EVP_PKEY_param_check(EVP_PKEY_CTX *ctx); -int q_EVP_PKEY_base_id(EVP_PKEY *a); -int q_RSA_bits(RSA *a); -Q_AUTOTEST_EXPORT int q_OPENSSL_sk_num(OPENSSL_STACK *a); -Q_AUTOTEST_EXPORT void q_OPENSSL_sk_pop_free(OPENSSL_STACK *a, void (*b)(void *)); -Q_AUTOTEST_EXPORT OPENSSL_STACK *q_OPENSSL_sk_new_null(); -Q_AUTOTEST_EXPORT void q_OPENSSL_sk_push(OPENSSL_STACK *st, void *data); -Q_AUTOTEST_EXPORT void q_OPENSSL_sk_free(OPENSSL_STACK *a); -Q_AUTOTEST_EXPORT void * q_OPENSSL_sk_value(OPENSSL_STACK *a, int b); -int q_SSL_session_reused(SSL *a); -unsigned long q_SSL_CTX_set_options(SSL_CTX *ctx, unsigned long op); -int q_OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings); -size_t q_SSL_get_client_random(SSL *a, unsigned char *out, size_t outlen); -size_t q_SSL_SESSION_get_master_key(const SSL_SESSION *session, unsigned char *out, size_t outlen); -int q_CRYPTO_get_ex_new_index(int class_index, long argl, void *argp, CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func); -const SSL_METHOD *q_TLS_method(); -const SSL_METHOD *q_TLS_client_method(); -const SSL_METHOD *q_TLS_server_method(); -ASN1_TIME *q_X509_getm_notBefore(X509 *a); -ASN1_TIME *q_X509_getm_notAfter(X509 *a); - -Q_AUTOTEST_EXPORT void q_X509_up_ref(X509 *a); -long q_X509_get_version(X509 *a); -EVP_PKEY *q_X509_get_pubkey(X509 *a); -void q_X509_STORE_set_verify_cb(X509_STORE *ctx, X509_STORE_CTX_verify_cb verify_cb); -int q_X509_STORE_set_ex_data(X509_STORE *ctx, int idx, void *data); -void *q_X509_STORE_get_ex_data(X509_STORE *r, int idx); -STACK_OF(X509) *q_X509_STORE_CTX_get0_chain(X509_STORE_CTX *ctx); -void q_DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); -int q_DH_bits(DH *dh); - -# define q_SSL_load_error_strings() q_OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS \ - | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL) - -#define q_SKM_sk_num(st) q_OPENSSL_sk_num((OPENSSL_STACK *)st) -#define q_SKM_sk_value(type, st,i) (type *)q_OPENSSL_sk_value((OPENSSL_STACK *)st, i) - -#define q_OPENSSL_add_all_algorithms_conf() q_OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \ - | OPENSSL_INIT_ADD_ALL_DIGESTS \ - | OPENSSL_INIT_LOAD_CONFIG, NULL) -#define q_OPENSSL_add_all_algorithms_noconf() q_OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \ - | OPENSSL_INIT_ADD_ALL_DIGESTS, NULL) - -int q_OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings); -void q_CRYPTO_free(void *str, const char *file, int line); - -long q_OpenSSL_version_num(); -const char *q_OpenSSL_version(int type); - -unsigned long q_SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *session); -unsigned long q_SSL_set_options(SSL *s, unsigned long op); - -#ifdef TLS1_3_VERSION -int q_SSL_CTX_set_ciphersuites(SSL_CTX *ctx, const char *str); - -// The functions below do not really have to be ifdefed like this, but for now -// they only used in TLS 1.3 handshake (and probably future versions). -// Plus, 'is resumalbe' is OpenSSL 1.1.1-only (and again we need it for -// TLS 1.3-specific session management). - -extern "C" -{ -using NewSessionCallback = int (*)(SSL *, SSL_SESSION *); -} - -void q_SSL_CTX_sess_set_new_cb(SSL_CTX *ctx, NewSessionCallback cb); -int q_SSL_SESSION_is_resumable(const SSL_SESSION *s); - -#define q_SSL_CTX_set_session_cache_mode(ctx,m) \ - q_SSL_CTX_ctrl(ctx,SSL_CTRL_SET_SESS_CACHE_MODE,m,NULL) - -#endif - -#if QT_CONFIG(dtls) -// Functions and types required for DTLS support: -extern "C" -{ - -typedef int (*CookieVerifyCallback)(SSL *, const unsigned char *, unsigned); -typedef int (*DgramWriteCallback) (BIO *, const char *, int); -typedef int (*DgramReadCallback) (BIO *, char *, int); -typedef int (*DgramPutsCallback) (BIO *, const char *); -typedef long (*DgramCtrlCallback) (BIO *, int, long, void *); -typedef int (*DgramCreateCallback) (BIO *); -typedef int (*DgramDestroyCallback) (BIO *); - -} - -int q_DTLSv1_listen(SSL *s, BIO_ADDR *client); -BIO_ADDR *q_BIO_ADDR_new(); -void q_BIO_ADDR_free(BIO_ADDR *ap); - -// API we need for a custom dgram BIO: - -BIO_METHOD *q_BIO_meth_new(int type, const char *name); -void q_BIO_meth_free(BIO_METHOD *biom); -int q_BIO_meth_set_write(BIO_METHOD *biom, DgramWriteCallback); -int q_BIO_meth_set_read(BIO_METHOD *biom, DgramReadCallback); -int q_BIO_meth_set_puts(BIO_METHOD *biom, DgramPutsCallback); -int q_BIO_meth_set_ctrl(BIO_METHOD *biom, DgramCtrlCallback); -int q_BIO_meth_set_create(BIO_METHOD *biom, DgramCreateCallback); -int q_BIO_meth_set_destroy(BIO_METHOD *biom, DgramDestroyCallback); - -#endif // dtls - -void q_BIO_set_data(BIO *a, void *ptr); -void *q_BIO_get_data(BIO *a); -void q_BIO_set_init(BIO *a, int init); -int q_BIO_get_shutdown(BIO *a); -void q_BIO_set_shutdown(BIO *a, int shut); - -#if QT_CONFIG(ocsp) -const OCSP_CERTID *q_OCSP_SINGLERESP_get0_id(const OCSP_SINGLERESP *x); -#endif // ocsp - -#define q_SSL_CTX_set_min_proto_version(ctx, version) \ - q_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, version, nullptr) - -#define q_SSL_CTX_set_max_proto_version(ctx, version) \ - q_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, version, nullptr) - -extern "C" { -typedef int (*q_SSL_psk_use_session_cb_func_t)(SSL *, const EVP_MD *, const unsigned char **, size_t *, - SSL_SESSION **); -} -void q_SSL_set_psk_use_session_callback(SSL *s, q_SSL_psk_use_session_cb_func_t); -// Here the content of the 1.1 header ends. - -bool q_resolveOpenSslSymbols(); -long q_ASN1_INTEGER_get(ASN1_INTEGER *a); -int q_ASN1_INTEGER_cmp(const ASN1_INTEGER *x, const ASN1_INTEGER *y); -int q_ASN1_STRING_length(ASN1_STRING *a); -int q_ASN1_STRING_to_UTF8(unsigned char **a, ASN1_STRING *b); -int q_ASN1_TIME_to_tm(const ASN1_TIME *s, struct tm *tm); -long q_BIO_ctrl(BIO *a, int b, long c, void *d); -Q_AUTOTEST_EXPORT int q_BIO_free(BIO *a); -BIO *q_BIO_new_mem_buf(void *a, int b); -int q_BIO_read(BIO *a, void *b, int c); -Q_AUTOTEST_EXPORT int q_BIO_write(BIO *a, const void *b, int c); -int q_BN_num_bits(const BIGNUM *a); -int q_BN_is_word(BIGNUM *a, BN_ULONG w); -BN_ULONG q_BN_mod_word(const BIGNUM *a, BN_ULONG w); - -#ifndef OPENSSL_NO_EC -const EC_GROUP* q_EC_KEY_get0_group(const EC_KEY* k); -int q_EC_GROUP_get_degree(const EC_GROUP* g); -#endif // OPENSSL_NO_EC - -DSA *q_DSA_new(); -void q_DSA_free(DSA *a); -X509 *q_d2i_X509(X509 **a, const unsigned char **b, long c); -char *q_ERR_error_string(unsigned long a, char *b); -void q_ERR_error_string_n(unsigned long e, char *buf, size_t len); -unsigned long q_ERR_get_error(); -EVP_CIPHER_CTX *q_EVP_CIPHER_CTX_new(); -void q_EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *a); -int q_EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr); -int q_EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *x, int keylen); -int q_EVP_CipherInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, const unsigned char *key, const unsigned char *iv, int enc); -int q_EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ENGINE *impl, const unsigned char *key, const unsigned char *iv, int enc); -int q_EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl); -int q_EVP_CipherFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl); -const EVP_MD *q_EVP_get_digestbyname(const char *name); - -#ifndef OPENSSL_NO_DES -const EVP_CIPHER *q_EVP_des_cbc(); -const EVP_CIPHER *q_EVP_des_ede3_cbc(); -#endif // OPENSSL_NO_DES - -#ifndef OPENSSL_NO_RC2 -const EVP_CIPHER *q_EVP_rc2_cbc(); -#endif // OPENSSL_NO_RC2 - -#ifndef OPENSSL_NO_AES -const EVP_CIPHER *q_EVP_aes_128_cbc(); -const EVP_CIPHER *q_EVP_aes_192_cbc(); -const EVP_CIPHER *q_EVP_aes_256_cbc(); -#endif // OPENSSL_NO_AES - -Q_AUTOTEST_EXPORT const EVP_MD *q_EVP_sha1(); -int q_EVP_PKEY_assign(EVP_PKEY *a, int b, void *r); -Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_RSA(EVP_PKEY *a, RSA *b); -Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_DSA(EVP_PKEY *a, DSA *b); -Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_DH(EVP_PKEY *a, DH *b); - -#ifndef OPENSSL_NO_EC -Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_EC_KEY(EVP_PKEY *a, EC_KEY *b); -#endif - -Q_AUTOTEST_EXPORT int q_EVP_PKEY_cmp(const EVP_PKEY *a, const EVP_PKEY *b); -Q_AUTOTEST_EXPORT void q_EVP_PKEY_free(EVP_PKEY *a); -RSA *q_EVP_PKEY_get1_RSA(EVP_PKEY *a); -DSA *q_EVP_PKEY_get1_DSA(EVP_PKEY *a); -DH *q_EVP_PKEY_get1_DH(EVP_PKEY *a); -#ifndef OPENSSL_NO_EC -EC_KEY *q_EVP_PKEY_get1_EC_KEY(EVP_PKEY *a); -#endif -int q_EVP_PKEY_type(int a); -Q_AUTOTEST_EXPORT EVP_PKEY *q_EVP_PKEY_new(); -int q_i2d_X509(X509 *a, unsigned char **b); -const char *q_OBJ_nid2sn(int a); -const char *q_OBJ_nid2ln(int a); -int q_OBJ_sn2nid(const char *s); -int q_OBJ_ln2nid(const char *s); -int q_i2t_ASN1_OBJECT(char *buf, int buf_len, ASN1_OBJECT *obj); -int q_OBJ_obj2txt(char *buf, int buf_len, ASN1_OBJECT *obj, int no_name); -int q_OBJ_obj2nid(const ASN1_OBJECT *a); -#define q_EVP_get_digestbynid(a) q_EVP_get_digestbyname(q_OBJ_nid2sn(a)) -Q_AUTOTEST_EXPORT EVP_PKEY *q_PEM_read_bio_PrivateKey(BIO *a, EVP_PKEY **b, pem_password_cb *c, void *d); -DSA *q_PEM_read_bio_DSAPrivateKey(BIO *a, DSA **b, pem_password_cb *c, void *d); -RSA *q_PEM_read_bio_RSAPrivateKey(BIO *a, RSA **b, pem_password_cb *c, void *d); - -#ifndef OPENSSL_NO_EC -EC_KEY *q_PEM_read_bio_ECPrivateKey(BIO *a, EC_KEY **b, pem_password_cb *c, void *d); -int q_PEM_write_bio_ECPrivateKey(BIO *a, EC_KEY *b, const EVP_CIPHER *c, unsigned char *d, - int e, pem_password_cb *f, void *g); -EC_KEY *q_PEM_read_bio_EC_PUBKEY(BIO *a, EC_KEY **b, pem_password_cb *c, void *d); -int q_PEM_write_bio_EC_PUBKEY(BIO *a, EC_KEY *b); -#endif // OPENSSL_NO_EC - -DH *q_PEM_read_bio_DHparams(BIO *a, DH **b, pem_password_cb *c, void *d); -int q_PEM_write_bio_DSAPrivateKey(BIO *a, DSA *b, const EVP_CIPHER *c, unsigned char *d, - int e, pem_password_cb *f, void *g); -int q_PEM_write_bio_RSAPrivateKey(BIO *a, RSA *b, const EVP_CIPHER *c, unsigned char *d, - int e, pem_password_cb *f, void *g); -int q_PEM_write_bio_PrivateKey(BIO *a, EVP_PKEY *b, const EVP_CIPHER *c, unsigned char *d, - int e, pem_password_cb *f, void *g); -Q_AUTOTEST_EXPORT EVP_PKEY *q_PEM_read_bio_PUBKEY(BIO *a, EVP_PKEY **b, pem_password_cb *c, void *d); -DSA *q_PEM_read_bio_DSA_PUBKEY(BIO *a, DSA **b, pem_password_cb *c, void *d); -RSA *q_PEM_read_bio_RSA_PUBKEY(BIO *a, RSA **b, pem_password_cb *c, void *d); -int q_PEM_write_bio_DSA_PUBKEY(BIO *a, DSA *b); -int q_PEM_write_bio_RSA_PUBKEY(BIO *a, RSA *b); -int q_PEM_write_bio_PUBKEY(BIO *a, EVP_PKEY *b); - -void q_RAND_seed(const void *a, int b); -int q_RAND_status(); -int q_RAND_bytes(unsigned char *b, int n); -RSA *q_RSA_new(); -void q_RSA_free(RSA *a); -int q_SSL_accept(SSL *a); -int q_SSL_clear(SSL *a); -char *q_SSL_CIPHER_description(const SSL_CIPHER *a, char *b, int c); -int q_SSL_CIPHER_get_bits(const SSL_CIPHER *a, int *b); -BIO *q_SSL_get_rbio(const SSL *s); -int q_SSL_connect(SSL *a); -int q_SSL_CTX_check_private_key(const SSL_CTX *a); -long q_SSL_CTX_ctrl(SSL_CTX *a, int b, long c, void *d); -void q_SSL_CTX_free(SSL_CTX *a); -SSL_CTX *q_SSL_CTX_new(const SSL_METHOD *a); -int q_SSL_CTX_set_cipher_list(SSL_CTX *a, const char *b); -int q_SSL_CTX_set_default_verify_paths(SSL_CTX *a); -void q_SSL_CTX_set_verify(SSL_CTX *a, int b, int (*c)(int, X509_STORE_CTX *)); -void q_SSL_CTX_set_verify_depth(SSL_CTX *a, int b); -extern "C" { -typedef void (*GenericCallbackType)(); -} -long q_SSL_CTX_callback_ctrl(SSL_CTX *, int, GenericCallbackType); -int q_SSL_CTX_use_certificate(SSL_CTX *a, X509 *b); -int q_SSL_CTX_use_certificate_file(SSL_CTX *a, const char *b, int c); -int q_SSL_CTX_use_PrivateKey(SSL_CTX *a, EVP_PKEY *b); -int q_SSL_CTX_use_RSAPrivateKey(SSL_CTX *a, RSA *b); -int q_SSL_CTX_use_PrivateKey_file(SSL_CTX *a, const char *b, int c); -X509_STORE *q_SSL_CTX_get_cert_store(const SSL_CTX *a); -SSL_CONF_CTX *q_SSL_CONF_CTX_new(); -void q_SSL_CONF_CTX_free(SSL_CONF_CTX *a); -void q_SSL_CONF_CTX_set_ssl_ctx(SSL_CONF_CTX *a, SSL_CTX *b); -unsigned int q_SSL_CONF_CTX_set_flags(SSL_CONF_CTX *a, unsigned int b); -int q_SSL_CONF_CTX_finish(SSL_CONF_CTX *a); -int q_SSL_CONF_cmd(SSL_CONF_CTX *a, const char *b, const char *c); -void q_SSL_free(SSL *a); -STACK_OF(SSL_CIPHER) *q_SSL_get_ciphers(const SSL *a); -const SSL_CIPHER *q_SSL_get_current_cipher(SSL *a); -int q_SSL_version(const SSL *a); -int q_SSL_get_error(SSL *a, int b); -STACK_OF(X509) *q_SSL_get_peer_cert_chain(SSL *a); -X509 *q_SSL_get_peer_certificate(SSL *a); -long q_SSL_get_verify_result(const SSL *a); -SSL *q_SSL_new(SSL_CTX *a); -SSL_CTX *q_SSL_get_SSL_CTX(SSL *a); -long q_SSL_ctrl(SSL *ssl,int cmd, long larg, void *parg); -int q_SSL_read(SSL *a, void *b, int c); -void q_SSL_set_bio(SSL *a, BIO *b, BIO *c); -void q_SSL_set_accept_state(SSL *a); -void q_SSL_set_connect_state(SSL *a); -int q_SSL_shutdown(SSL *a); -int q_SSL_in_init(const SSL *s); -int q_SSL_get_shutdown(const SSL *ssl); -int q_SSL_set_session(SSL *to, SSL_SESSION *session); -void q_SSL_SESSION_free(SSL_SESSION *ses); -SSL_SESSION *q_SSL_get1_session(SSL *ssl); -SSL_SESSION *q_SSL_get_session(const SSL *ssl); -int q_SSL_set_ex_data(SSL *ssl, int idx, void *arg); -void *q_SSL_get_ex_data(const SSL *ssl, int idx); -#ifndef OPENSSL_NO_PSK -typedef unsigned int (*q_psk_client_callback_t)(SSL *ssl, const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len); -void q_SSL_set_psk_client_callback(SSL *ssl, q_psk_client_callback_t callback); -typedef unsigned int (*q_psk_server_callback_t)(SSL *ssl, const char *identity, unsigned char *psk, unsigned int max_psk_len); -void q_SSL_set_psk_server_callback(SSL *ssl, q_psk_server_callback_t callback); -int q_SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *hint); -#endif // !OPENSSL_NO_PSK -int q_SSL_write(SSL *a, const void *b, int c); -int q_X509_cmp(X509 *a, X509 *b); -X509 *q_X509_dup(X509 *a); -void q_X509_print(BIO *a, X509*b); -int q_X509_digest(const X509 *x509, const EVP_MD *type, unsigned char *md, unsigned int *len); -ASN1_OBJECT *q_X509_EXTENSION_get_object(X509_EXTENSION *a); -Q_AUTOTEST_EXPORT void q_X509_free(X509 *a); -Q_AUTOTEST_EXPORT ASN1_TIME *q_X509_gmtime_adj(ASN1_TIME *s, long adj); -Q_AUTOTEST_EXPORT void q_ASN1_TIME_free(ASN1_TIME *t); -X509_EXTENSION *q_X509_get_ext(X509 *a, int b); -int q_X509_get_ext_count(X509 *a); -void *q_X509_get_ext_d2i(X509 *a, int b, int *c, int *d); -const X509V3_EXT_METHOD *q_X509V3_EXT_get(X509_EXTENSION *a); -void *q_X509V3_EXT_d2i(X509_EXTENSION *a); -int q_X509_EXTENSION_get_critical(X509_EXTENSION *a); -ASN1_OCTET_STRING *q_X509_EXTENSION_get_data(X509_EXTENSION *a); -void q_BASIC_CONSTRAINTS_free(BASIC_CONSTRAINTS *a); -void q_AUTHORITY_KEYID_free(AUTHORITY_KEYID *a); -int q_ASN1_STRING_print(BIO *a, const ASN1_STRING *b); -int q_X509_check_issued(X509 *a, X509 *b); -X509_NAME *q_X509_get_issuer_name(X509 *a); -X509_NAME *q_X509_get_subject_name(X509 *a); -ASN1_INTEGER *q_X509_get_serialNumber(X509 *a); -int q_X509_verify_cert(X509_STORE_CTX *ctx); -int q_X509_NAME_entry_count(X509_NAME *a); -X509_NAME_ENTRY *q_X509_NAME_get_entry(X509_NAME *a,int b); -ASN1_STRING *q_X509_NAME_ENTRY_get_data(X509_NAME_ENTRY *a); -ASN1_OBJECT *q_X509_NAME_ENTRY_get_object(X509_NAME_ENTRY *a); -EVP_PKEY *q_X509_PUBKEY_get(X509_PUBKEY *a); -void q_X509_STORE_free(X509_STORE *store); -X509_STORE *q_X509_STORE_new(); -int q_X509_STORE_add_cert(X509_STORE *ctx, X509 *x); -void q_X509_STORE_CTX_free(X509_STORE_CTX *storeCtx); -int q_X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, - X509 *x509, STACK_OF(X509) *chain); -X509_STORE_CTX *q_X509_STORE_CTX_new(); -int q_X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose); -int q_X509_STORE_CTX_get_error(X509_STORE_CTX *ctx); -int q_X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx); -X509 *q_X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx); -X509_STORE *q_X509_STORE_CTX_get0_store(X509_STORE_CTX *ctx); - -// Diffie-Hellman support -DH *q_DH_new(); -void q_DH_free(DH *dh); -DH *q_d2i_DHparams(DH **a, const unsigned char **pp, long length); -int q_i2d_DHparams(DH *a, unsigned char **p); - -#ifndef OPENSSL_NO_DEPRECATED_3_0 -int q_DH_check(DH *dh, int *codes); -#endif // OPENSSL_NO_DEPRECATED_3_0 - -BIGNUM *q_BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret); -#define q_SSL_CTX_set_tmp_dh(ctx, dh) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_TMP_DH, 0, (char *)dh) - -#ifndef OPENSSL_NO_EC -// EC Diffie-Hellman support -EC_KEY *q_EC_KEY_dup(const EC_KEY *src); -EC_KEY *q_EC_KEY_new_by_curve_name(int nid); -void q_EC_KEY_free(EC_KEY *ecdh); -#define q_SSL_CTX_set_tmp_ecdh(ctx, ecdh) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_TMP_ECDH, 0, (char *)ecdh) - -// EC curves management -size_t q_EC_get_builtin_curves(EC_builtin_curve *r, size_t nitems); -int q_EC_curve_nist2nid(const char *name); -#endif // OPENSSL_NO_EC - -#define q_SSL_get_server_tmp_key(ssl, key) q_SSL_ctrl((ssl), SSL_CTRL_GET_SERVER_TMP_KEY, 0, (char *)key) - -// PKCS#12 support -int q_PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca); -PKCS12 *q_d2i_PKCS12_bio(BIO *bio, PKCS12 **pkcs12); -void q_PKCS12_free(PKCS12 *pkcs12); - -#define q_BIO_get_mem_data(b, pp) (int)q_BIO_ctrl(b,BIO_CTRL_INFO,0,(char *)pp) -#define q_BIO_pending(b) (int)q_BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL) -#define q_SSL_CTX_set_mode(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_MODE,(op),NULL) -#define q_sk_GENERAL_NAME_num(st) q_SKM_sk_num((st)) -#define q_sk_GENERAL_NAME_value(st, i) q_SKM_sk_value(GENERAL_NAME, (st), (i)) - -void q_GENERAL_NAME_free(GENERAL_NAME *a); - -#define q_sk_X509_num(st) q_SKM_sk_num((st)) -#define q_sk_X509_value(st, i) q_SKM_sk_value(X509, (st), (i)) -#define q_sk_SSL_CIPHER_num(st) q_SKM_sk_num((st)) -#define q_sk_SSL_CIPHER_value(st, i) q_SKM_sk_value(SSL_CIPHER, (st), (i)) -#define q_SSL_CTX_add_extra_chain_cert(ctx,x509) \ - q_SSL_CTX_ctrl(ctx,SSL_CTRL_EXTRA_CHAIN_CERT,0,(char *)x509) -#define q_EVP_PKEY_assign_RSA(pkey,rsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_RSA,\ - (char *)(rsa)) -#define q_EVP_PKEY_assign_DSA(pkey,dsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_DSA,\ - (char *)(dsa)) -#define q_OpenSSL_add_all_algorithms() q_OPENSSL_add_all_algorithms_conf() - -#if OPENSSL_VERSION_MAJOR < 3 -int q_SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath); -#else -int q_SSL_CTX_load_verify_dir(SSL_CTX *ctx, const char *CApath); -#endif // OPENSSL_VERSION_MAJOR - -int q_i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp); -SSL_SESSION *q_d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp, long length); - -#ifndef OPENSSL_NO_NEXTPROTONEG -int q_SSL_select_next_proto(unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen, - const unsigned char *client, unsigned int client_len); -void q_SSL_CTX_set_next_proto_select_cb(SSL_CTX *s, - int (*cb) (SSL *ssl, unsigned char **out, - unsigned char *outlen, - const unsigned char *in, - unsigned int inlen, void *arg), - void *arg); -void q_SSL_get0_next_proto_negotiated(const SSL *s, const unsigned char **data, - unsigned *len); -int q_SSL_set_alpn_protos(SSL *ssl, const unsigned char *protos, - unsigned protos_len); -void q_SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, - int (*cb) (SSL *ssl, - const unsigned char **out, - unsigned char *outlen, - const unsigned char *in, - unsigned int inlen, - void *arg), void *arg); -void q_SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, - unsigned *len); -#endif // !OPENSSL_NO_NEXTPROTONEG - - -#if QT_CONFIG(dtls) - -extern "C" -{ -typedef int (*CookieGenerateCallback)(SSL *, unsigned char *, unsigned *); -} - -void q_SSL_CTX_set_cookie_generate_cb(SSL_CTX *ctx, CookieGenerateCallback cb); -void q_SSL_CTX_set_cookie_verify_cb(SSL_CTX *ctx, CookieVerifyCallback cb); -const SSL_METHOD *q_DTLS_server_method(); -const SSL_METHOD *q_DTLS_client_method(); - -#endif // dtls - -void *q_X509_STORE_CTX_get_ex_data(X509_STORE_CTX *ctx, int idx); -int q_SSL_get_ex_data_X509_STORE_CTX_idx(); - -#if QT_CONFIG(dtls) -#define q_DTLS_set_link_mtu(ssl, mtu) q_SSL_ctrl((ssl), DTLS_CTRL_SET_LINK_MTU, (mtu), nullptr) -#define q_DTLSv1_get_timeout(ssl, arg) q_SSL_ctrl(ssl, DTLS_CTRL_GET_TIMEOUT, 0, arg) -#define q_DTLSv1_handle_timeout(ssl) q_SSL_ctrl(ssl, DTLS_CTRL_HANDLE_TIMEOUT, 0, nullptr) -#endif // dtls - -void q_BIO_set_flags(BIO *b, int flags); -void q_BIO_clear_flags(BIO *b, int flags); -void *q_BIO_get_ex_data(BIO *b, int idx); -int q_BIO_set_ex_data(BIO *b, int idx, void *data); - -#define q_BIO_set_retry_read(b) q_BIO_set_flags(b, (BIO_FLAGS_READ|BIO_FLAGS_SHOULD_RETRY)) -#define q_BIO_set_retry_write(b) q_BIO_set_flags(b, (BIO_FLAGS_WRITE|BIO_FLAGS_SHOULD_RETRY)) -#define q_BIO_clear_retry_flags(b) q_BIO_clear_flags(b, (BIO_FLAGS_RWS|BIO_FLAGS_SHOULD_RETRY)) -#define q_BIO_set_app_data(s,arg) q_BIO_set_ex_data(s,0,arg) -#define q_BIO_get_app_data(s) q_BIO_get_ex_data(s,0) - -// Helper function -class QDateTime; -QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime); - -#define q_SSL_set_tlsext_status_type(ssl, type) \ - q_SSL_ctrl((ssl), SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE, (type), nullptr) - -#if QT_CONFIG(ocsp) - -OCSP_RESPONSE *q_d2i_OCSP_RESPONSE(OCSP_RESPONSE **a, const unsigned char **in, long len); -Q_AUTOTEST_EXPORT int q_i2d_OCSP_RESPONSE(OCSP_RESPONSE *r, unsigned char **ppout); -Q_AUTOTEST_EXPORT OCSP_RESPONSE *q_OCSP_response_create(int status, OCSP_BASICRESP *bs); -Q_AUTOTEST_EXPORT void q_OCSP_RESPONSE_free(OCSP_RESPONSE *rs); -int q_OCSP_response_status(OCSP_RESPONSE *resp); -OCSP_BASICRESP *q_OCSP_response_get1_basic(OCSP_RESPONSE *resp); -Q_AUTOTEST_EXPORT OCSP_SINGLERESP *q_OCSP_basic_add1_status(OCSP_BASICRESP *rsp, OCSP_CERTID *cid, - int status, int reason, ASN1_TIME *revtime, - ASN1_TIME *thisupd, ASN1_TIME *nextupd); -Q_AUTOTEST_EXPORT int q_OCSP_basic_sign(OCSP_BASICRESP *brsp, X509 *signer, EVP_PKEY *key, const EVP_MD *dgst, - STACK_OF(X509) *certs, unsigned long flags); -Q_AUTOTEST_EXPORT OCSP_BASICRESP *q_OCSP_BASICRESP_new(); -Q_AUTOTEST_EXPORT void q_OCSP_BASICRESP_free(OCSP_BASICRESP *bs); -int q_OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs, X509_STORE *st, unsigned long flags); -int q_OCSP_resp_count(OCSP_BASICRESP *bs); -OCSP_SINGLERESP *q_OCSP_resp_get0(OCSP_BASICRESP *bs, int idx); -int q_OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason, ASN1_GENERALIZEDTIME **revtime, - ASN1_GENERALIZEDTIME **thisupd, ASN1_GENERALIZEDTIME **nextupd); -int q_OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd, long nsec, long maxsec); -int q_OCSP_id_get0_info(ASN1_OCTET_STRING **piNameHash, ASN1_OBJECT **pmd, ASN1_OCTET_STRING **pikeyHash, - ASN1_INTEGER **pserial, OCSP_CERTID *cid); - -const STACK_OF(X509) *q_OCSP_resp_get0_certs(const OCSP_BASICRESP *bs); -Q_AUTOTEST_EXPORT OCSP_CERTID *q_OCSP_cert_to_id(const EVP_MD *dgst, X509 *subject, X509 *issuer); -Q_AUTOTEST_EXPORT void q_OCSP_CERTID_free(OCSP_CERTID *cid); -int q_OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b); - -#define q_SSL_get_tlsext_status_ocsp_resp(ssl, arg) \ - q_SSL_ctrl(ssl, SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP, 0, arg) - -#define q_SSL_CTX_set_tlsext_status_cb(ssl, cb) \ - q_SSL_CTX_callback_ctrl(ssl, SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB, GenericCallbackType(cb)) - -# define q_SSL_set_tlsext_status_ocsp_resp(ssl, arg, arglen) \ - q_SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP, arglen, arg) - -#endif // ocsp - - -void *q_CRYPTO_malloc(size_t num, const char *file, int line); -#define q_OPENSSL_malloc(num) q_CRYPTO_malloc(num, "", 0) - -void q_SSL_set_info_callback(SSL *ssl, void (*cb) (const SSL *ssl, int type, int val)); -const char *q_SSL_alert_type_string(int value); -const char *q_SSL_alert_desc_string_long(int value); - -int q_SSL_CTX_get_security_level(const SSL_CTX *ctx); -void q_SSL_CTX_set_security_level(SSL_CTX *ctx, int level); - -QT_END_NAMESPACE - -#endif diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h index c90dfc8883..2816e62381 100644 --- a/src/network/ssl/qsslsocket_p.h +++ b/src/network/ssl/qsslsocket_p.h @@ -103,7 +103,7 @@ public: static bool s_loadRootCertsOnDemand; static bool supportsSsl(); - static void ensureInitialized(); + Q_NETWORK_EXPORT static void ensureInitialized(); static QList<QSslCipher> defaultCiphers(); static QList<QSslCipher> defaultDtlsCiphers(); @@ -117,7 +117,7 @@ public: static void resetDefaultEllipticCurves(); static QList<QSslCertificate> defaultCaCertificates(); - static QList<QSslCertificate> systemCaCertificates(); + Q_NETWORK_EXPORT static QList<QSslCertificate> systemCaCertificates(); static void setDefaultCaCertificates(const QList<QSslCertificate> &certs); static void addDefaultCaCertificate(const QSslCertificate &cert); static void addDefaultCaCertificates(const QList<QSslCertificate> &certs); @@ -168,7 +168,6 @@ public: Q_NETWORK_PRIVATE_EXPORT static void setRootCertOnDemandLoadingSupported(bool supported); static QTlsBackend *tlsBackendInUse(); - static void registerAdHocFactory(); // Needed by TlsCryptograph: Q_NETWORK_PRIVATE_EXPORT QSslSocket::SslMode tlsMode() const; diff --git a/src/network/ssl/qsslsocket_qt.cpp b/src/network/ssl/qsslsocket_qt.cpp deleted file mode 100644 index 13bbb76367..0000000000 --- a/src/network/ssl/qsslsocket_qt.cpp +++ /dev/null @@ -1,309 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org> -** 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 <QtCore/qbytearray.h> -#include <QtCore/qdatastream.h> -#include <QtCore/qmessageauthenticationcode.h> -#include <QtCore/qrandom.h> - -#include "qsslsocket_p.h" -#include "qasn1element_p.h" -#include "qsslkey_p.h" - -QT_BEGIN_NAMESPACE - -/* - PKCS12 helpers. -*/ - -static QAsn1Element wrap(quint8 type, const QAsn1Element &child) -{ - QByteArray value; - QDataStream stream(&value, QIODevice::WriteOnly); - child.write(stream); - return QAsn1Element(type, value); -} - -static QAsn1Element _q_PKCS7_data(const QByteArray &data) -{ - QList<QAsn1Element> items; - items << QAsn1Element::fromObjectId("1.2.840.113549.1.7.1"); - items << wrap(QAsn1Element::Context0Type, - QAsn1Element(QAsn1Element::OctetStringType, data)); - return QAsn1Element::fromVector(items); -} - -/*! - PKCS #12 key derivation. - - Some test vectors: - http://www.drh-consultancy.demon.co.uk/test.txt - \internal -*/ -static QByteArray _q_PKCS12_keygen(char id, const QByteArray &salt, const QString &passPhrase, int n, int r) -{ - const int u = 20; - const int v = 64; - - // password formatting - QByteArray passUnicode(passPhrase.size() * 2 + 2, '\0'); - char *p = passUnicode.data(); - for (int i = 0; i < passPhrase.size(); ++i) { - quint16 ch = passPhrase[i].unicode(); - *(p++) = (ch & 0xff00) >> 8; - *(p++) = (ch & 0xff); - } - - // prepare I - QByteArray D(64, id); - QByteArray S, P; - const int sSize = v * ((salt.size() + v - 1) / v); - S.resize(sSize); - for (int i = 0; i < sSize; ++i) - S[i] = salt[i % salt.size()]; - const int pSize = v * ((passUnicode.size() + v - 1) / v); - P.resize(pSize); - for (int i = 0; i < pSize; ++i) - P[i] = passUnicode[i % passUnicode.size()]; - QByteArray I = S + P; - - // apply hashing - const int c = (n + u - 1) / u; - QByteArray A; - QByteArray B; - B.resize(v); - QCryptographicHash hash(QCryptographicHash::Sha1); - for (int i = 0; i < c; ++i) { - // hash r iterations - QByteArray Ai = D + I; - for (int j = 0; j < r; ++j) { - hash.reset(); - hash.addData(Ai); - Ai = hash.result(); - } - - for (int j = 0; j < v; ++j) - B[j] = Ai[j % u]; - - // modify I as Ij = (Ij + B + 1) modulo 2^v - for (int p = 0; p < I.size(); p += v) { - quint8 carry = 1; - for (int j = v - 1; j >= 0; --j) { - quint16 v = quint8(I[p + j]) + quint8(B[j]) + carry; - I[p + j] = v & 0xff; - carry = (v & 0xff00) >> 8; - } - } - A += Ai; - } - return A.left(n); -} - -static QByteArray _q_PKCS12_salt() -{ - QByteArray salt; - salt.resize(8); - for (int i = 0; i < salt.size(); ++i) - salt[i] = (QRandomGenerator::global()->generate() & 0xff); - return salt; -} - -static QByteArray _q_PKCS12_certBag(const QSslCertificate &cert) -{ - QList<QAsn1Element> items; - items << QAsn1Element::fromObjectId("1.2.840.113549.1.12.10.1.3"); - - // certificate - QList<QAsn1Element> certItems; - certItems << QAsn1Element::fromObjectId("1.2.840.113549.1.9.22.1"); - certItems << wrap(QAsn1Element::Context0Type, - QAsn1Element(QAsn1Element::OctetStringType, cert.toDer())); - items << wrap(QAsn1Element::Context0Type, - QAsn1Element::fromVector(certItems)); - - // local key id - const QByteArray localKeyId = cert.digest(QCryptographicHash::Sha1); - QList<QAsn1Element> idItems; - idItems << QAsn1Element::fromObjectId("1.2.840.113549.1.9.21"); - idItems << wrap(QAsn1Element::SetType, - QAsn1Element(QAsn1Element::OctetStringType, localKeyId)); - items << wrap(QAsn1Element::SetType, QAsn1Element::fromVector(idItems)); - - // dump - QAsn1Element root = wrap(QAsn1Element::SequenceType, QAsn1Element::fromVector(items)); - QByteArray ba; - QDataStream stream(&ba, QIODevice::WriteOnly); - root.write(stream); - return ba; -} - -static QAsn1Element _q_PKCS12_key(const QSslKey &key) -{ - Q_ASSERT(key.algorithm() == QSsl::Rsa || key.algorithm() == QSsl::Dsa); - - QList<QAsn1Element> keyItems; - keyItems << QAsn1Element::fromInteger(0); - QList<QAsn1Element> algoItems; - if (key.algorithm() == QSsl::Rsa) - algoItems << QAsn1Element::fromObjectId(RSA_ENCRYPTION_OID); - else if (key.algorithm() == QSsl::Dsa) - algoItems << QAsn1Element::fromObjectId(DSA_ENCRYPTION_OID); - algoItems << QAsn1Element(QAsn1Element::NullType); - keyItems << QAsn1Element::fromVector(algoItems); - keyItems << QAsn1Element(QAsn1Element::OctetStringType, key.toDer()); - return QAsn1Element::fromVector(keyItems); -} - -static QByteArray _q_PKCS12_shroudedKeyBag(const QSslKey &key, const QString &passPhrase, const QByteArray &localKeyId) -{ - const int iterations = 2048; - QByteArray salt = _q_PKCS12_salt(); - QByteArray cKey = _q_PKCS12_keygen(1, salt, passPhrase, 24, iterations); - QByteArray cIv = _q_PKCS12_keygen(2, salt, passPhrase, 8, iterations); - - // prepare and encrypt data - QByteArray plain; - QDataStream plainStream(&plain, QIODevice::WriteOnly); - _q_PKCS12_key(key).write(plainStream); - QByteArray crypted = QSslKeyPrivate::encrypt(QTlsPrivate::Cipher::DesEde3Cbc, - plain, cKey, cIv); - - QList<QAsn1Element> items; - items << QAsn1Element::fromObjectId("1.2.840.113549.1.12.10.1.2"); - - // key - QList<QAsn1Element> keyItems; - QList<QAsn1Element> algoItems; - algoItems << QAsn1Element::fromObjectId("1.2.840.113549.1.12.1.3"); - QList<QAsn1Element> paramItems; - paramItems << QAsn1Element(QAsn1Element::OctetStringType, salt); - paramItems << QAsn1Element::fromInteger(iterations); - algoItems << QAsn1Element::fromVector(paramItems); - keyItems << QAsn1Element::fromVector(algoItems); - keyItems << QAsn1Element(QAsn1Element::OctetStringType, crypted); - items << wrap(QAsn1Element::Context0Type, - QAsn1Element::fromVector(keyItems)); - - // local key id - QList<QAsn1Element> idItems; - idItems << QAsn1Element::fromObjectId("1.2.840.113549.1.9.21"); - idItems << wrap(QAsn1Element::SetType, - QAsn1Element(QAsn1Element::OctetStringType, localKeyId)); - items << wrap(QAsn1Element::SetType, - QAsn1Element::fromVector(idItems)); - - // dump - QAsn1Element root = wrap(QAsn1Element::SequenceType, QAsn1Element::fromVector(items)); - QByteArray ba; - QDataStream stream(&ba, QIODevice::WriteOnly); - root.write(stream); - return ba; -} - -static QByteArray _q_PKCS12_bag(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase) -{ - QList<QAsn1Element> items; - - // certs - for (int i = 0; i < certs.size(); ++i) - items << _q_PKCS7_data(_q_PKCS12_certBag(certs[i])); - - // key - if (!key.isNull()) { - const QByteArray localKeyId = certs.first().digest(QCryptographicHash::Sha1); - items << _q_PKCS7_data(_q_PKCS12_shroudedKeyBag(key, passPhrase, localKeyId)); - } - - // dump - QAsn1Element root = QAsn1Element::fromVector(items); - QByteArray ba; - QDataStream stream(&ba, QIODevice::WriteOnly); - root.write(stream); - return ba; -} - -static QAsn1Element _q_PKCS12_mac(const QByteArray &data, const QString &passPhrase) -{ - const int iterations = 2048; - - // salt generation - QByteArray macSalt = _q_PKCS12_salt(); - QByteArray key = _q_PKCS12_keygen(3, macSalt, passPhrase, 20, iterations); - - // HMAC calculation - QMessageAuthenticationCode hmac(QCryptographicHash::Sha1, key); - hmac.addData(data); - - QList<QAsn1Element> algoItems; - algoItems << QAsn1Element::fromObjectId("1.3.14.3.2.26"); - algoItems << QAsn1Element(QAsn1Element::NullType); - - QList<QAsn1Element> digestItems; - digestItems << QAsn1Element::fromVector(algoItems); - digestItems << QAsn1Element(QAsn1Element::OctetStringType, hmac.result()); - - QList<QAsn1Element> macItems; - macItems << QAsn1Element::fromVector(digestItems); - macItems << QAsn1Element(QAsn1Element::OctetStringType, macSalt); - macItems << QAsn1Element::fromInteger(iterations); - return QAsn1Element::fromVector(macItems); -} - -QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase) -{ - QList<QAsn1Element> items; - - // version - items << QAsn1Element::fromInteger(3); - - // auth safe - const QByteArray data = _q_PKCS12_bag(certs, key, passPhrase); - items << _q_PKCS7_data(data); - - // HMAC - items << _q_PKCS12_mac(data, passPhrase); - - // dump - QAsn1Element root = QAsn1Element::fromVector(items); - QByteArray ba; - QDataStream stream(&ba, QIODevice::WriteOnly); - root.write(stream); - return ba; -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qtls_openssl.cpp b/src/network/ssl/qtls_openssl.cpp deleted file mode 100644 index 90561943f5..0000000000 --- a/src/network/ssl/qtls_openssl.cpp +++ /dev/null @@ -1,1837 +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 "qsslpresharedkeyauthenticator_p.h" -#include "qsslpresharedkeyauthenticator.h" -#include "qsslsocket_openssl_symbols_p.h" -#include "qsslcertificate_p.h" -#include "qx509_openssl_p.h" -#include "qocspresponse_p.h" -#include "qtls_openssl_p.h" -#include "qsslsocket_p.h" - -#ifdef Q_OS_WIN -#include "qwindowscarootfetcher_p.h" -#endif - -#include <QtCore/qscopedvaluerollback.h> -#include <QtCore/qscopeguard.h> - -#include <algorithm> -#include <cstring> - -QT_BEGIN_NAMESPACE - -namespace { - -QSsl::AlertLevel tlsAlertLevel(int value) -{ - using QSsl::AlertLevel; - - if (const char *typeString = q_SSL_alert_type_string(value)) { - // Documented to return 'W' for warning, 'F' for fatal, - // 'U' for unknown. - switch (typeString[0]) { - case 'W': - return AlertLevel::Warning; - case 'F': - return AlertLevel::Fatal; - default:; - } - } - - return AlertLevel::Unknown; -} - -QString tlsAlertDescription(int value) -{ - QString description = QLatin1String(q_SSL_alert_desc_string_long(value)); - if (!description.size()) - description = QLatin1String("no description provided"); - return description; -} - -QSsl::AlertType tlsAlertType(int value) -{ - // In case for some reason openssl gives us a value, - // which is not in our enum actually, we leave it to - // an application to handle (supposedly they have - // if or switch-statements). - return QSsl::AlertType(value & 0xff); -} - -#ifdef Q_OS_WIN - -QSslCertificate findCertificateToFetch(const QList<QSslError> &tlsErrors, bool checkAIA) -{ - QSslCertificate certToFetch; - - for (const auto &tlsError : tlsErrors) { - switch (tlsError.error()) { - case QSslError::UnableToGetLocalIssuerCertificate: // site presented intermediate cert, but root is unknown - case QSslError::SelfSignedCertificateInChain: // site presented a complete chain, but root is unknown - certToFetch = tlsError.certificate(); - break; - case QSslError::SelfSignedCertificate: - case QSslError::CertificateBlacklisted: - //With these errors, we know it will be untrusted so save time by not asking windows - return QSslCertificate{}; - default: -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << tlsError.errorString(); -#endif - //TODO - this part is strange. - break; - } - } - - if (checkAIA) { - const auto extensions = certToFetch.extensions(); - for (const auto &ext : extensions) { - if (ext.oid() == QStringLiteral("1.3.6.1.5.5.7.1.1")) // See RFC 4325 - return certToFetch; - } - //The only reason we check this extensions is because an application set trusted - //CA certificates explicitly, thus technically disabling CA fetch. So, if it's - //the case and an intermediate certificate is missing, and no extensions is - //present on the leaf certificate - we fail the handshake immediately. - return QSslCertificate{}; - } - - return certToFetch; -} - -#endif // Q_OS_WIN - -} // unnamed namespace - -namespace QTlsPrivate { - -extern "C" { - -int q_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." - const auto offset = QTlsBackendOpenSSL::s_indexForSSLExtraData - + TlsCryptographOpenSSL::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, handshake failure"); - 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; -} - -int q_X509CallbackDirect(int ok, X509_STORE_CTX *ctx) -{ - // Passed to SSL_CTX_set_verify() - // https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_verify.html - // Returns 0 to abort verification, 1 to continue. - - // This is a new, experimental verification callback, reporting - // errors immediately and returning 0 or 1 depending on an application - // either ignoring or not ignoring verification errors as they come. - if (!ctx) { - qCWarning(lcSsl, "Invalid store context (nullptr)"); - return 0; - } - - if (!ok) { - // "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." - SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx())); - if (!ssl) { - qCWarning(lcTlsBackend, "No external data (SSL) found in X509 store object"); - return 0; - } - - const auto offset = QTlsBackendOpenSSL::s_indexForSSLExtraData - + TlsCryptographOpenSSL::socketOffsetInExData; - auto crypto = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, offset)); - if (!crypto) { - qCWarning(lcTlsBackend, "No external data (TlsCryptographOpenSSL) found in SSL object"); - return 0; - } - - return crypto->emitErrorFromCallback(ctx); - } - return 1; -} - -#ifndef OPENSSL_NO_PSK -static unsigned q_ssl_psk_client_callback(SSL *ssl, const char *hint, char *identity, unsigned max_identity_len, - unsigned char *psk, unsigned max_psk_len) -{ - auto *tls = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData)); - return tls->pskClientTlsCallback(hint, identity, max_identity_len, psk, max_psk_len); -} - -static unsigned int q_ssl_psk_server_callback(SSL *ssl, const char *identity, unsigned char *psk, - unsigned int max_psk_len) -{ - auto *tls = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData)); - Q_ASSERT(tls); - return tls->pskServerTlsCallback(identity, psk, max_psk_len); -} - -#ifdef TLS1_3_VERSION -static unsigned q_ssl_psk_restore_client(SSL *ssl, const char *hint, char *identity, unsigned max_identity_len, - unsigned char *psk, unsigned max_psk_len) -{ - Q_UNUSED(hint); - Q_UNUSED(identity); - Q_UNUSED(max_identity_len); - Q_UNUSED(psk); - Q_UNUSED(max_psk_len); - -#ifdef QT_DEBUG - auto tls = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData)); - Q_ASSERT(tls); - Q_ASSERT(tls->d); - Q_ASSERT(tls->d->tlsMode() == QSslSocket::SslClientMode); -#endif - q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback); - - return 0; -} - -static int q_ssl_psk_use_session_callback(SSL *ssl, const EVP_MD *md, const unsigned char **id, - size_t *idlen, SSL_SESSION **sess) -{ - Q_UNUSED(ssl); - Q_UNUSED(md); - Q_UNUSED(id); - Q_UNUSED(idlen); - Q_UNUSED(sess); - -#ifdef QT_DEBUG - auto *tls = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData)); - Q_ASSERT(tls); - Q_ASSERT(tls->d); - Q_ASSERT(tls->d->tlsMode() == QSslSocket::SslClientMode); -#endif - - // Temporarily rebind the psk because it will be called next. The function will restore it. - q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_restore_client); - - return 1; // need to return 1 or else "the connection setup fails." -} - -int q_ssl_sess_set_new_cb(SSL *ssl, SSL_SESSION *session) -{ - if (!ssl) { - qCWarning(lcTlsBackend, "Invalid SSL (nullptr)"); - return 0; - } - if (!session) { - qCWarning(lcTlsBackend, "Invalid SSL_SESSION (nullptr)"); - return 0; - } - - auto *tls = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData)); - Q_ASSERT(tls); - return tls->handleNewSessionTicket(ssl); -} -#endif // TLS1_3_VERSION - -#endif // !OPENSSL_NO_PSK - -#if QT_CONFIG(ocsp) - -int qt_OCSP_status_server_callback(SSL *ssl, void *ocspRequest) -{ - Q_UNUSED(ocspRequest); - if (!ssl) - return SSL_TLSEXT_ERR_ALERT_FATAL; - - auto crypto = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData)); - if (!crypto) - return SSL_TLSEXT_ERR_ALERT_FATAL; - - Q_ASSERT(crypto->d); - Q_ASSERT(crypto->d->tlsMode() == QSslSocket::SslServerMode); - const QByteArray &response = crypto->ocspResponseDer; - Q_ASSERT(response.size()); - - unsigned char *derCopy = static_cast<unsigned char *>(q_OPENSSL_malloc(size_t(response.size()))); - if (!derCopy) - return SSL_TLSEXT_ERR_ALERT_FATAL; - - std::copy(response.data(), response.data() + response.size(), derCopy); - // We don't check the return value: internally OpenSSL simply assignes the - // pointer (it assumes it now owns this memory btw!) and the length. - q_SSL_set_tlsext_status_ocsp_resp(ssl, derCopy, response.size()); - - return SSL_TLSEXT_ERR_OK; -} - -#endif // ocsp - -void qt_AlertInfoCallback(const SSL *connection, int from, int value) -{ - // Passed to SSL_set_info_callback() - // https://www.openssl.org/docs/man1.1.1/man3/SSL_set_info_callback.html - - if (!connection) { -#ifdef QSSLSOCKET_DEBUG - qCWarning(lcTlsBackend, "Invalid 'connection' parameter (nullptr)"); -#endif // QSSLSOCKET_DEBUG - return; - } - - const auto offset = QTlsBackendOpenSSL::s_indexForSSLExtraData - + TlsCryptographOpenSSL::socketOffsetInExData; - auto crypto = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(connection, offset)); - if (!crypto) { - // SSL_set_ex_data can fail: -#ifdef QSSLSOCKET_DEBUG - qCWarning(lcTlsBackend, "No external data (socket backend) found for parameter 'connection'"); -#endif // QSSLSOCKET_DEBUG - return; - } - - if (!(from & SSL_CB_ALERT)) { - // We only want to know about alerts (at least for now). - return; - } - - if (from & SSL_CB_WRITE) - crypto->alertMessageSent(value); - else - crypto->alertMessageReceived(value); -} - -} // extern "C" - -#if QT_CONFIG(ocsp) -namespace { - -QSslError::SslError qt_OCSP_response_status_to_SslError(long code) -{ - switch (code) { - case OCSP_RESPONSE_STATUS_MALFORMEDREQUEST: - return QSslError::OcspMalformedRequest; - case OCSP_RESPONSE_STATUS_INTERNALERROR: - return QSslError::OcspInternalError; - case OCSP_RESPONSE_STATUS_TRYLATER: - return QSslError::OcspTryLater; - case OCSP_RESPONSE_STATUS_SIGREQUIRED: - return QSslError::OcspSigRequred; - case OCSP_RESPONSE_STATUS_UNAUTHORIZED: - return QSslError::OcspUnauthorized; - case OCSP_RESPONSE_STATUS_SUCCESSFUL: - default: - return {}; - } - Q_UNREACHABLE(); -} - -QOcspRevocationReason qt_OCSP_revocation_reason(int reason) -{ - switch (reason) { - case OCSP_REVOKED_STATUS_NOSTATUS: - return QOcspRevocationReason::None; - case OCSP_REVOKED_STATUS_UNSPECIFIED: - return QOcspRevocationReason::Unspecified; - case OCSP_REVOKED_STATUS_KEYCOMPROMISE: - return QOcspRevocationReason::KeyCompromise; - case OCSP_REVOKED_STATUS_CACOMPROMISE: - return QOcspRevocationReason::CACompromise; - case OCSP_REVOKED_STATUS_AFFILIATIONCHANGED: - return QOcspRevocationReason::AffiliationChanged; - case OCSP_REVOKED_STATUS_SUPERSEDED: - return QOcspRevocationReason::Superseded; - case OCSP_REVOKED_STATUS_CESSATIONOFOPERATION: - return QOcspRevocationReason::CessationOfOperation; - case OCSP_REVOKED_STATUS_CERTIFICATEHOLD: - return QOcspRevocationReason::CertificateHold; - case OCSP_REVOKED_STATUS_REMOVEFROMCRL: - return QOcspRevocationReason::RemoveFromCRL; - default: - return QOcspRevocationReason::None; - } - - Q_UNREACHABLE(); -} - -bool qt_OCSP_certificate_match(OCSP_SINGLERESP *singleResponse, X509 *peerCert, X509 *issuer) -{ - // OCSP_basic_verify does verify that the responder is legit, the response is - // correctly signed, CertID is correct. But it does not know which certificate - // we were presented with by our peer, so it does not check if it's a response - // for our peer's certificate. - Q_ASSERT(singleResponse && peerCert && issuer); - - const OCSP_CERTID *certId = q_OCSP_SINGLERESP_get0_id(singleResponse); // Does not increment refcount. - if (!certId) { - qCWarning(lcSsl, "A SingleResponse without CertID"); - return false; - } - - ASN1_OBJECT *md = nullptr; - ASN1_INTEGER *reportedSerialNumber = nullptr; - const int result = q_OCSP_id_get0_info(nullptr, &md, nullptr, &reportedSerialNumber, const_cast<OCSP_CERTID *>(certId)); - if (result != 1 || !md || !reportedSerialNumber) { - qCWarning(lcSsl, "Failed to extract a hash and serial number from CertID structure"); - return false; - } - - if (!q_X509_get_serialNumber(peerCert)) { - // Is this possible at all? But we have to check this, - // ASN1_INTEGER_cmp (called from OCSP_id_cmp) dereferences - // without any checks at all. - qCWarning(lcSsl, "No serial number in peer's ceritificate"); - return false; - } - - const int nid = q_OBJ_obj2nid(md); - if (nid == NID_undef) { - qCWarning(lcSsl, "Unknown hash algorithm in CertID"); - return false; - } - - const EVP_MD *digest = q_EVP_get_digestbynid(nid); // Does not increment refcount. - if (!digest) { - qCWarning(lcSsl) << "No digest for nid" << nid; - return false; - } - - OCSP_CERTID *recreatedId = q_OCSP_cert_to_id(digest, peerCert, issuer); - if (!recreatedId) { - qCWarning(lcSsl, "Failed to re-create CertID"); - return false; - } - const QSharedPointer<OCSP_CERTID> guard(recreatedId, q_OCSP_CERTID_free); - - if (q_OCSP_id_cmp(const_cast<OCSP_CERTID *>(certId), recreatedId)) { - qDebug(lcSsl, "Certificate ID mismatch"); - return false; - } - // Bingo! - return true; -} - -} // unnamed namespace -#endif // ocsp - -TlsCryptographOpenSSL::~TlsCryptographOpenSSL() -{ - destroySslContext(); -} - -void TlsCryptographOpenSSL::init(QSslSocket *qObj, QSslSocketPrivate *dObj) -{ - Q_ASSERT(qObj); - Q_ASSERT(dObj); - q = qObj; - d = dObj; - - ocspResponses.clear(); - ocspResponseDer.clear(); - - systemOrSslErrorDetected = false; - handshakeInterrupted = false; - - fetchAuthorityInformation = false; - caToFetch = QSslCertificate{}; -} - -void TlsCryptographOpenSSL::checkSettingSslContext(QSharedPointer<QSslContext> tlsContext) -{ - if (sslContextPointer.isNull()) - sslContextPointer = tlsContext; -} - -QSharedPointer<QSslContext> TlsCryptographOpenSSL::sslContext() const -{ - return sslContextPointer; -} - -QList<QSslError> TlsCryptographOpenSSL::tlsErrors() const -{ - return sslErrors; -} - -void TlsCryptographOpenSSL::startClientEncryption() -{ - if (!initSslContext()) { - Q_ASSERT(d); - d->setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Unable to init SSL Context: %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl())); - return; - } - - // Start connecting. This will place outgoing data in the BIO, so we - // follow up with calling transmit(). - startHandshake(); - transmit(); -} - -void TlsCryptographOpenSSL::startServerEncryption() -{ - if (!initSslContext()) { - Q_ASSERT(d); - d->setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Unable to init SSL Context: %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl())); - return; - } - - // Start connecting. This will place outgoing data in the BIO, so we - // follow up with calling transmit(). - startHandshake(); - transmit(); -} - -bool TlsCryptographOpenSSL::startHandshake() -{ - // Check if the connection has been established. Get all errors from the - // verification stage. - Q_ASSERT(q); - Q_ASSERT(d); - - using ScopedBool = QScopedValueRollback<bool>; - - if (inSetAndEmitError) - return false; - - const auto mode = d->tlsMode(); - - pendingFatalAlert = false; - errorsReportedFromCallback = false; - QList<QSslErrorEntry> lastErrors; - q_SSL_set_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData + errorOffsetInExData, &lastErrors); - - // SSL_set_ex_data can fail, but see the callback's code - we handle this there. - q_SSL_set_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData + socketOffsetInExData, this); - q_SSL_set_info_callback(ssl, qt_AlertInfoCallback); - - int result = (mode == QSslSocket::SslClientMode) ? q_SSL_connect(ssl) : q_SSL_accept(ssl); - q_SSL_set_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData + errorOffsetInExData, nullptr); - // Note, unlike errors as external data on SSL object, we do not unset - // a callback/ex-data if alert notifications are enabled: an alert can - // arrive after the handshake, for example, this happens when the server - // does not find a ClientCert or does not like it. - - if (!lastErrors.isEmpty() || errorsReportedFromCallback) - storePeerCertificates(); - - // storePeerCertificate() if called above - would update the - // configuration with peer's certificates. - auto configuration = q->sslConfiguration(); - if (!errorsReportedFromCallback) { - const auto &peerCertificateChain = configuration.peerCertificateChain(); - for (const auto ¤tError : qAsConst(lastErrors)) { - emit q->peerVerifyError(QTlsPrivate::X509CertificateOpenSSL::openSSLErrorToQSslError(currentError.code, - peerCertificateChain.value(currentError.depth))); - if (q->state() != QAbstractSocket::ConnectedState) - break; - } - } - - errorList << lastErrors; - - // Connection aborted during handshake phase. - if (q->state() != QAbstractSocket::ConnectedState) - return false; - - // Check if we're encrypted or not. - if (result <= 0) { - switch (q_SSL_get_error(ssl, result)) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - // The handshake is not yet complete. - break; - default: - QString errorString = QTlsBackendOpenSSL::msgErrorsDuringHandshake(); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::startHandshake: error!" << errorString; -#endif - { - const ScopedBool bg(inSetAndEmitError, true); - d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, errorString); - if (pendingFatalAlert) { - trySendFatalAlert(); - pendingFatalAlert = false; - } - } - q->abort(); - } - return false; - } - - // store peer certificate chain - storePeerCertificates(); - - // Start translating errors. - QList<QSslError> errors; - - // Note, the storePeerCerificates() probably updated the configuration at this point. - configuration = q->sslConfiguration(); - // Check the whole chain for blacklisting (including root, as we check for subjectInfo and issuer) - const auto &peerCertificateChain = configuration.peerCertificateChain(); - for (const QSslCertificate &cert : peerCertificateChain) { - if (QSslCertificatePrivate::isBlacklisted(cert)) { - QSslError error(QSslError::CertificateBlacklisted, cert); - errors << error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - } - - const bool doVerifyPeer = configuration.peerVerifyMode() == QSslSocket::VerifyPeer - || (configuration.peerVerifyMode() == QSslSocket::AutoVerifyPeer - && mode == QSslSocket::SslClientMode); - -#if QT_CONFIG(ocsp) - // For now it's always QSslSocket::SslClientMode - initSslContext() will bail out early, - // if it's enabled in QSslSocket::SslServerMode. This can change. - if (!configuration.peerCertificate().isNull() && configuration.ocspStaplingEnabled() && doVerifyPeer) { - if (!checkOcspStatus()) { - if (ocspErrors.isEmpty()) { - { - const ScopedBool bg(inSetAndEmitError, true); - d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, ocspErrorDescription); - } - q->abort(); - return false; - } - - for (const QSslError &error : ocspErrors) { - errors << error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - } - } -#endif // ocsp - - // Check the peer certificate itself. First try the subject's common name - // (CN) as a wildcard, then try all alternate subject name DNS entries the - // same way. - if (!configuration.peerCertificate().isNull()) { - // but only if we're a client connecting to a server - // if we're the server, don't check CN - const auto verificationPeerName = d->verificationName(); - if (mode == QSslSocket::SslClientMode) { - QString peerName = (verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName); - - if (!isMatchingHostname(configuration.peerCertificate(), peerName)) { - // No matches in common names or alternate names. - QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate()); - errors << error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - } - } else { - // No peer certificate presented. Report as error if the socket - // expected one. - if (doVerifyPeer) { - QSslError error(QSslError::NoPeerCertificate); - errors << error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - } - - // Translate errors from the error list into QSslErrors. - errors.reserve(errors.size() + errorList.size()); - for (const auto &error : qAsConst(errorList)) - errors << X509CertificateOpenSSL::openSSLErrorToQSslError(error.code, peerCertificateChain.value(error.depth)); - - if (!errors.isEmpty()) { - sslErrors = errors; -#ifdef Q_OS_WIN - const bool fetchEnabled = QSslSocketPrivate::rootCertOnDemandLoadingSupported() - && d->isRootsOnDemandAllowed(); - // !fetchEnabled is a special case scenario, when we potentially have a missing - // intermediate certificate and a recoverable chain, but on demand cert loading - // was disabled by setCaCertificates call. For this scenario we check if "Authority - // Information Access" is present - wincrypt can deal with such certificates. - QSslCertificate certToFetch; - if (doVerifyPeer && !d->verifyErrorsHaveBeenIgnored()) - certToFetch = findCertificateToFetch(sslErrors, !fetchEnabled); - - //Skip this if not using system CAs, or if the SSL errors are configured in advance to be ignorable - if (!certToFetch.isNull()) { - fetchAuthorityInformation = !fetchEnabled; - //Windows desktop versions starting from vista ship with minimal set of roots and download on demand - //from the windows update server CA roots that are trusted by MS. It also can fetch a missing intermediate - //in case "Authority Information Access" extension is present. - // - //However, this is only transparent if using WinINET - we have to trigger it - //ourselves. - fetchCaRootForCert(certToFetch); - return false; - } -#endif // Q_OS_WIN - if (!checkSslErrors()) - return false; - // A slot, attached to sslErrors signal can call - // abort/close/disconnetFromHost/etc; no need to - // continue handshake then. - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } else { - sslErrors.clear(); - } - - continueHandshake(); - return true; -} - -void TlsCryptographOpenSSL::enableHandshakeContinuation() -{ - handshakeInterrupted = false; -} - -void TlsCryptographOpenSSL::cancelCAFetch() -{ - fetchAuthorityInformation = false; - caToFetch = QSslCertificate{}; -} - -void TlsCryptographOpenSSL::continueHandshake() -{ - Q_ASSERT(q); - Q_ASSERT(d); - - auto *plainSocket = d->plainTcpSocket(); - Q_ASSERT(plainSocket); - - const auto mode = d->tlsMode(); - - // if we have a max read buffer size, reset the plain socket's to match - if (const auto maxSize = d->maxReadBufferSize()) - plainSocket->setReadBufferSize(maxSize); - - if (q_SSL_session_reused(ssl)) - QTlsBackend::setPeerSessionShared(d, true); - -#ifdef QT_DECRYPT_SSL_TRAFFIC - if (q_SSL_get_session(ssl)) { - size_t master_key_len = q_SSL_SESSION_get_master_key(q_SSL_get_session(ssl), nullptr, 0); - size_t client_random_len = q_SSL_get_client_random(ssl, nullptr, 0); - QByteArray masterKey(int(master_key_len), Qt::Uninitialized); // Will not overflow - QByteArray clientRandom(int(client_random_len), Qt::Uninitialized); // Will not overflow - - q_SSL_SESSION_get_master_key(q_SSL_get_session(ssl), - reinterpret_cast<unsigned char*>(masterKey.data()), - masterKey.size()); - q_SSL_get_client_random(ssl, reinterpret_cast<unsigned char *>(clientRandom.data()), - clientRandom.size()); - - QByteArray debugLineClientRandom("CLIENT_RANDOM "); - debugLineClientRandom.append(clientRandom.toHex().toUpper()); - debugLineClientRandom.append(" "); - debugLineClientRandom.append(masterKey.toHex().toUpper()); - debugLineClientRandom.append("\n"); - - QString sslKeyFile = QDir::tempPath() + QLatin1String("/qt-ssl-keys"); - QFile file(sslKeyFile); - if (!file.open(QIODevice::Append)) - qCWarning(lcSsl) << "could not open file" << sslKeyFile << "for appending"; - if (!file.write(debugLineClientRandom)) - qCWarning(lcSsl) << "could not write to file" << sslKeyFile; - file.close(); - } else { - qCWarning(lcTlsBackend, "could not decrypt SSL traffic"); - } -#endif // QT_DECRYPT_SSL_TRAFFIC - - const auto &configuration = q->sslConfiguration(); - // Cache this SSL session inside the QSslContext - if (!(configuration.testSslOption(QSsl::SslOptionDisableSessionSharing))) { - if (!sslContextPointer->cacheSession(ssl)) { - sslContextPointer.clear(); // we could not cache the session - } else { - // Cache the session for permanent usage as well - if (!(configuration.testSslOption(QSsl::SslOptionDisableSessionPersistence))) { - if (!sslContextPointer->sessionASN1().isEmpty()) - QTlsBackend::setSessionAsn1(d, sslContextPointer->sessionASN1()); - QTlsBackend::setSessionLifetimeHint(d, sslContextPointer->sessionTicketLifeTimeHint()); - } - } - } - -#if !defined(OPENSSL_NO_NEXTPROTONEG) - - QTlsBackend::setAlpnStatus(d, sslContextPointer->npnContext().status); - if (sslContextPointer->npnContext().status == QSslConfiguration::NextProtocolNegotiationUnsupported) { - // we could not agree -> be conservative and use HTTP/1.1 - // T.P.: I have to admit, this is a really strange notion of 'conservative', - // given the protocol-neutral nature of ALPN/NPN. - QTlsBackend::setNegotiatedProtocol(d, QByteArrayLiteral("http/1.1")); - } else { - const unsigned char *proto = nullptr; - unsigned int proto_len = 0; - - q_SSL_get0_alpn_selected(ssl, &proto, &proto_len); - if (proto_len && mode == QSslSocket::SslClientMode) { - // Client does not have a callback that sets it ... - QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNegotiated); - } - - if (!proto_len) { // Test if NPN was more lucky ... - q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len); - } - - if (proto_len) - QTlsBackend::setNegotiatedProtocol(d, QByteArray(reinterpret_cast<const char *>(proto), proto_len)); - else - QTlsBackend::setNegotiatedProtocol(d,{}); - } -#endif // !defined(OPENSSL_NO_NEXTPROTONEG) - - if (mode == QSslSocket::SslClientMode) { - EVP_PKEY *key; - if (q_SSL_get_server_tmp_key(ssl, &key)) - QTlsBackend::setEphemeralKey(d, QSslKey(key, QSsl::PublicKey)); - } - - d->setEncrypted(true); - emit q->encrypted(); - if (d->isAutoStartingHandshake() && d->isPendingClose()) { - d->setPendingClose(false); - q->disconnectFromHost(); - } -} - -void TlsCryptographOpenSSL::transmit() -{ - Q_ASSERT(q); - Q_ASSERT(d); - - using ScopedBool = QScopedValueRollback<bool>; - - if (inSetAndEmitError) - return; - - // If we don't have any SSL context, don't bother transmitting. - if (!ssl) - return; - - auto &writeBuffer = d->tlsWriteBuffer(); - auto &buffer = d->tlsBuffer(); - auto *plainSocket = d->plainTcpSocket(); - Q_ASSERT(plainSocket); - bool &emittedBytesWritten = d->tlsEmittedBytesWritten(); - - bool transmitting; - do { - transmitting = false; - - // If the connection is secure, we can transfer data from the write - // buffer (in plain text) to the write BIO through SSL_write. - if (q->isEncrypted() && !writeBuffer.isEmpty()) { - qint64 totalBytesWritten = 0; - int nextDataBlockSize; - while ((nextDataBlockSize = writeBuffer.nextDataBlockSize()) > 0) { - int writtenBytes = q_SSL_write(ssl, writeBuffer.readPointer(), nextDataBlockSize); - if (writtenBytes <= 0) { - int error = q_SSL_get_error(ssl, writtenBytes); - //write can result in a want_write_error - not an error - continue transmitting - if (error == SSL_ERROR_WANT_WRITE) { - transmitting = true; - break; - } else if (error == SSL_ERROR_WANT_READ) { - //write can result in a want_read error, possibly due to renegotiation - not an error - stop transmitting - transmitting = false; - break; - } else { - // ### Better error handling. - const ScopedBool bg(inSetAndEmitError, true); - d->setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Unable to write data: %1").arg( - QTlsBackendOpenSSL::getErrorsFromOpenSsl())); - return; - } - } -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: encrypted" << writtenBytes << "bytes"; -#endif - writeBuffer.free(writtenBytes); - totalBytesWritten += writtenBytes; - - if (writtenBytes < nextDataBlockSize) { - // break out of the writing loop and try again after we had read - transmitting = true; - break; - } - } - - if (totalBytesWritten > 0) { - // Don't emit bytesWritten() recursively. - if (!emittedBytesWritten) { - emittedBytesWritten = true; - emit q->bytesWritten(totalBytesWritten); - emittedBytesWritten = false; - } - emit q->channelBytesWritten(0, totalBytesWritten); - } - } - - // Check if we've got any data to be written to the socket. - QVarLengthArray<char, 4096> data; - int pendingBytes; - while (plainSocket->isValid() && (pendingBytes = q_BIO_pending(writeBio)) > 0 - && plainSocket->openMode() != QIODevice::NotOpen) { - // Read encrypted data from the write BIO into a buffer. - data.resize(pendingBytes); - int encryptedBytesRead = q_BIO_read(writeBio, data.data(), pendingBytes); - - // Write encrypted data from the buffer to the socket. - qint64 actualWritten = plainSocket->write(data.constData(), encryptedBytesRead); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: wrote" << encryptedBytesRead - << "encrypted bytes to the socket" << actualWritten << "actual."; -#endif - if (actualWritten < 0) { - //plain socket write fails if it was in the pending close state. - const ScopedBool bg(inSetAndEmitError, true); - d->setErrorAndEmit(plainSocket->error(), plainSocket->errorString()); - return; - } - transmitting = true; - } - - // Check if we've got any data to be read from the socket. - if (!q->isEncrypted() || !d->maxReadBufferSize() || buffer.size() < d->maxReadBufferSize()) - while ((pendingBytes = plainSocket->bytesAvailable()) > 0) { - // Read encrypted data from the socket into a buffer. - data.resize(pendingBytes); - // just peek() here because q_BIO_write could write less data than expected - int encryptedBytesRead = plainSocket->peek(data.data(), pendingBytes); - -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: read" << encryptedBytesRead << "encrypted bytes from the socket"; -#endif - // Write encrypted data from the buffer into the read BIO. - int writtenToBio = q_BIO_write(readBio, data.constData(), encryptedBytesRead); - - // Throw away the results. - if (writtenToBio > 0) { - plainSocket->skip(writtenToBio); - } else { - // ### Better error handling. - const ScopedBool bg(inSetAndEmitError, true); - d->setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Unable to decrypt data: %1") - .arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl())); - return; - } - - transmitting = true; - } - - // If the connection isn't secured yet, this is the time to retry the - // connect / accept. - if (!q->isEncrypted()) { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: testing encryption"; -#endif - if (startHandshake()) { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: encryption established"; -#endif - d->setEncrypted(true); - transmitting = true; - } else if (plainSocket->state() != QAbstractSocket::ConnectedState) { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: connection lost"; -#endif - break; - } else if (d->isPaused()) { - // just wait until the user continues - return; - } else { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: encryption not done yet"; -#endif - } - } - - // If the request is small and the remote host closes the transmission - // after sending, there's a chance that startHandshake() will already - // have triggered a shutdown. - if (!ssl) - continue; - - // We always read everything from the SSL decryption buffers, even if - // we have a readBufferMaxSize. There's no point in leaving data there - // just so that readBuffer.size() == readBufferMaxSize. - int readBytes = 0; - const int bytesToRead = 4096; - do { - if (q->readChannelCount() == 0) { - // The read buffer is deallocated, don't try resize or write to it. - break; - } - // Don't use SSL_pending(). It's very unreliable. - readBytes = q_SSL_read(ssl, buffer.reserve(bytesToRead), bytesToRead); - if (readBytes > 0) { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: decrypted" << readBytes << "bytes"; -#endif - buffer.chop(bytesToRead - readBytes); - - if (bool *readyReadEmittedPointer = d->readyReadPointer()) - *readyReadEmittedPointer = true; - emit q->readyRead(); - emit q->channelReadyRead(0); - transmitting = true; - continue; - } - buffer.chop(bytesToRead); - - // Error. - switch (q_SSL_get_error(ssl, readBytes)) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - // Out of data. - break; - case SSL_ERROR_ZERO_RETURN: - // The remote host closed the connection. -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: remote disconnect"; -#endif - shutdown = true; // the other side shut down, make sure we do not send shutdown ourselves - { - const ScopedBool bg(inSetAndEmitError, true); - d->setErrorAndEmit(QAbstractSocket::RemoteHostClosedError, - QSslSocket::tr("The TLS/SSL connection has been closed")); - } - return; - case SSL_ERROR_SYSCALL: // some IO error - case SSL_ERROR_SSL: // error in the SSL library - // we do not know exactly what the error is, nor whether we can recover from it, - // so just return to prevent an endless loop in the outer "while" statement - systemOrSslErrorDetected = true; - { - const ScopedBool bg(inSetAndEmitError, true); - d->setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Error while reading: %1") - .arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl())); - } - return; - default: - // SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: can only happen with a - // BIO_s_connect() or BIO_s_accept(), which we do not call. - // SSL_ERROR_WANT_X509_LOOKUP: can only happen with a - // SSL_CTX_set_client_cert_cb(), which we do not call. - // So this default case should never be triggered. - { - const ScopedBool bg(inSetAndEmitError, true); - d->setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Error while reading: %1") - .arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl())); - } - break; - } - } while (ssl && readBytes > 0); - } while (ssl && transmitting); -} - -void TlsCryptographOpenSSL::disconnectFromHost() -{ - if (ssl) { - if (!shutdown && !q_SSL_in_init(ssl) && !systemOrSslErrorDetected) { - if (q_SSL_shutdown(ssl) != 1) { - // Some error may be queued, clear it. - QTlsBackendOpenSSL::clearErrorQueue(); - } - shutdown = true; - transmit(); - } - } - Q_ASSERT(d); - auto *plainSocket = d->plainTcpSocket(); - Q_ASSERT(plainSocket); - plainSocket->disconnectFromHost(); -} - -void TlsCryptographOpenSSL::disconnected() -{ - Q_ASSERT(d); - auto *plainSocket = d->plainTcpSocket(); - Q_ASSERT(plainSocket); - - if (plainSocket->bytesAvailable() <= 0) { - destroySslContext(); - } else { - // Move all bytes into the plain buffer. - const qint64 tmpReadBufferMaxSize = d->maxReadBufferSize(); - // Reset temporarily, so the plain socket buffer is completely drained: - d->setMaxReadBufferSize(0); - transmit(); - d->setMaxReadBufferSize(tmpReadBufferMaxSize); - } - //if there is still buffered data in the plain socket, don't destroy the ssl context yet. - //it will be destroyed when the socket is deleted. -} - -QSslCipher TlsCryptographOpenSSL::sessionCipher() const -{ - if (!ssl) - return {}; - - const SSL_CIPHER *sessionCipher = q_SSL_get_current_cipher(ssl); - return sessionCipher ? QTlsBackendOpenSSL::qt_OpenSSL_cipher_to_QSslCipher(sessionCipher) : QSslCipher{}; -} - -QSsl::SslProtocol TlsCryptographOpenSSL::sessionProtocol() const -{ - if (!ssl) - return QSsl::UnknownProtocol; - - const int ver = q_SSL_version(ssl); - switch (ver) { - case 0x301: - return QSsl::TlsV1_0; - case 0x302: - return QSsl::TlsV1_1; - case 0x303: - return QSsl::TlsV1_2; - case 0x304: - return QSsl::TlsV1_3; - } - - return QSsl::UnknownProtocol; -} - -QList<QOcspResponse> TlsCryptographOpenSSL::ocsps() const -{ - return ocspResponses; -} - -bool TlsCryptographOpenSSL::checkSslErrors() -{ - Q_ASSERT(q); - Q_ASSERT(d); - - if (sslErrors.isEmpty()) - return true; - - emit q->sslErrors(sslErrors); - - const auto vfyMode = q->peerVerifyMode(); - const auto mode = d->tlsMode(); - - bool doVerifyPeer = vfyMode == QSslSocket::VerifyPeer || (vfyMode == QSslSocket::AutoVerifyPeer - && mode == QSslSocket::SslClientMode); - bool doEmitSslError = !d->verifyErrorsHaveBeenIgnored(); - // check whether we need to emit an SSL handshake error - if (doVerifyPeer && doEmitSslError) { - if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) { - QSslSocketPrivate::pauseSocketNotifiers(q); - d->setPaused(true); - } else { - d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, sslErrors.constFirst().errorString()); - auto *plainSocket = d->plainTcpSocket(); - Q_ASSERT(plainSocket); - plainSocket->disconnectFromHost(); - } - return false; - } - return true; -} - -int TlsCryptographOpenSSL::handleNewSessionTicket(SSL *connection) -{ - // If we return 1, this means we own the session, but we don't. - // 0 would tell OpenSSL to deref (but they still have it in the - // internal cache). - Q_ASSERT(connection); - - Q_ASSERT(q); - Q_ASSERT(d); - - if (q->sslConfiguration().testSslOption(QSsl::SslOptionDisableSessionPersistence)) { - // We silently ignore, do nothing, remove from cache. - return 0; - } - - SSL_SESSION *currentSession = q_SSL_get_session(connection); - if (!currentSession) { - qCWarning(lcTlsBackend, - "New session ticket callback, the session is invalid (nullptr)"); - return 0; - } - - if (q_SSL_version(connection) < 0x304) { - // We only rely on this mechanics with TLS >= 1.3 - return 0; - } - -#ifdef TLS1_3_VERSION - if (!q_SSL_SESSION_is_resumable(currentSession)) { - qCDebug(lcTlsBackend, "New session ticket, but the session is non-resumable"); - return 0; - } -#endif // TLS1_3_VERSION - - const int sessionSize = q_i2d_SSL_SESSION(currentSession, nullptr); - if (sessionSize <= 0) { - qCWarning(lcTlsBackend, "could not store persistent version of SSL session"); - return 0; - } - - // We have somewhat perverse naming, it's not a ticket, it's a session. - QByteArray sessionTicket(sessionSize, 0); - auto data = reinterpret_cast<unsigned char *>(sessionTicket.data()); - if (!q_i2d_SSL_SESSION(currentSession, &data)) { - qCWarning(lcTlsBackend, "could not store persistent version of SSL session"); - return 0; - } - - QTlsBackend::setSessionAsn1(d, sessionTicket); - QTlsBackend::setSessionLifetimeHint(d, q_SSL_SESSION_get_ticket_lifetime_hint(currentSession)); - - emit q->newSessionTicketReceived(); - return 0; -} - -void TlsCryptographOpenSSL::alertMessageSent(int value) -{ - Q_ASSERT(q); - Q_ASSERT(d); - - const auto level = tlsAlertLevel(value); - if (level == QSsl::AlertLevel::Fatal && !q->isEncrypted()) { - // Note, this logic is handshake-time only: - pendingFatalAlert = true; - } - - emit q->alertSent(level, tlsAlertType(value), tlsAlertDescription(value)); - -} - -void TlsCryptographOpenSSL::alertMessageReceived(int value) -{ - Q_ASSERT(q); - - emit q->alertReceived(tlsAlertLevel(value), tlsAlertType(value), tlsAlertDescription(value)); -} - -int TlsCryptographOpenSSL::emitErrorFromCallback(X509_STORE_CTX *ctx) -{ - // Returns 0 to abort verification, 1 to continue despite error (as - // OpenSSL expects from the verification callback). - Q_ASSERT(q); - Q_ASSERT(ctx); - - using ScopedBool = QScopedValueRollback<bool>; - // While we are not setting, we are emitting and in general - - // we want to prevent accidental recursive startHandshake() - // calls: - const ScopedBool bg(inSetAndEmitError, true); - - X509 *x509 = q_X509_STORE_CTX_get_current_cert(ctx); - if (!x509) { - qCWarning(lcTlsBackend, "Could not obtain the certificate (that failed to verify)"); - return 0; - } - - const QSslCertificate certificate = QTlsPrivate::X509CertificateOpenSSL::certificateFromX509(x509); - const auto errorAndDepth = QTlsPrivate::X509CertificateOpenSSL::errorEntryFromStoreContext(ctx); - const QSslError tlsError = QTlsPrivate::X509CertificateOpenSSL::openSSLErrorToQSslError(errorAndDepth.code, certificate); - - errorsReportedFromCallback = true; - handshakeInterrupted = true; - emit q->handshakeInterruptedOnError(tlsError); - - // Conveniently so, we also can access 'lastErrors' external data set - // in startHandshake, we store it for the case an application later - // wants to check errors (ignored or not): - const auto offset = QTlsBackendOpenSSL::s_indexForSSLExtraData - + TlsCryptographOpenSSL::errorOffsetInExData; - if (auto errorList = static_cast<QList<QSslErrorEntry> *>(q_SSL_get_ex_data(ssl, offset))) - errorList->append(errorAndDepth); - - // An application is expected to ignore this error (by calling ignoreSslErrors) - // in its directly connected slot: - return !handshakeInterrupted; -} - -void TlsCryptographOpenSSL::trySendFatalAlert() -{ - Q_ASSERT(pendingFatalAlert); - Q_ASSERT(d); - - auto *plainSocket = d->plainTcpSocket(); - - pendingFatalAlert = false; - QVarLengthArray<char, 4096> data; - int pendingBytes = 0; - while (plainSocket->isValid() && (pendingBytes = q_BIO_pending(writeBio)) > 0 - && plainSocket->openMode() != QIODevice::NotOpen) { - // Read encrypted data from the write BIO into a buffer. - data.resize(pendingBytes); - const int bioReadBytes = q_BIO_read(writeBio, data.data(), pendingBytes); - - // Write encrypted data from the buffer to the socket. - qint64 actualWritten = plainSocket->write(data.constData(), bioReadBytes); - if (actualWritten < 0) - return; - plainSocket->flush(); - } -} - -bool TlsCryptographOpenSSL::initSslContext() -{ - Q_ASSERT(q); - Q_ASSERT(d); - - // If no external context was set (e.g. by QHttpNetworkConnection) we will - // create a new one. - const auto mode = d->tlsMode(); - const auto configuration = q->sslConfiguration(); - if (!sslContextPointer) - sslContextPointer = QSslContext::sharedFromConfiguration(mode, configuration, d->isRootsOnDemandAllowed()); - - if (sslContextPointer->error() != QSslError::NoError) { - d->setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError, sslContextPointer->errorString()); - sslContextPointer.clear(); // deletes the QSslContext - return false; - } - - // Create and initialize SSL session - if (!(ssl = sslContextPointer->createSsl())) { - d->setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Error creating SSL session, %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl())); - return false; - } - - if (configuration.protocol() != QSsl::UnknownProtocol && mode == QSslSocket::SslClientMode) { - const auto verificationPeerName = d->verificationName(); - // Set server hostname on TLS extension. RFC4366 section 3.1 requires it in ACE format. - QString tlsHostName = verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName; - if (tlsHostName.isEmpty()) - tlsHostName = d->tlsHostName(); - QByteArray ace = QUrl::toAce(tlsHostName); - // only send the SNI header if the URL is valid and not an IP - if (!ace.isEmpty() - && !QHostAddress().setAddress(tlsHostName) - && !(configuration.testSslOption(QSsl::SslOptionDisableServerNameIndication))) { - // We don't send the trailing dot from the host header if present see - // https://tools.ietf.org/html/rfc6066#section-3 - if (ace.endsWith('.')) - ace.chop(1); - if (!q_SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, ace.data())) - qCWarning(lcTlsBackend, "could not set SSL_CTRL_SET_TLSEXT_HOSTNAME, Server Name Indication disabled"); - } - } - - // Clear the session. - errorList.clear(); - - // Initialize memory BIOs for encryption and decryption. - readBio = q_BIO_new(q_BIO_s_mem()); - writeBio = q_BIO_new(q_BIO_s_mem()); - if (!readBio || !writeBio) { - d->setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Error creating SSL session: %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl())); - if (readBio) - q_BIO_free(readBio); - if (writeBio) - q_BIO_free(writeBio); - return false; - } - - // Assign the bios. - q_SSL_set_bio(ssl, readBio, writeBio); - - if (mode == QSslSocket::SslClientMode) - q_SSL_set_connect_state(ssl); - else - q_SSL_set_accept_state(ssl); - - q_SSL_set_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData, this); - -#ifndef OPENSSL_NO_PSK - // Set the client callback for PSK - if (mode == QSslSocket::SslClientMode) - q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback); - else if (mode == QSslSocket::SslServerMode) - q_SSL_set_psk_server_callback(ssl, &q_ssl_psk_server_callback); - -#if OPENSSL_VERSION_NUMBER >= 0x10101006L - // Set the client callback for TLSv1.3 PSK - if (mode == QSslSocket::SslClientMode - && QSslSocket::sslLibraryBuildVersionNumber() >= 0x10101006L) { - q_SSL_set_psk_use_session_callback(ssl, &q_ssl_psk_use_session_callback); - } -#endif // openssl version >= 0x10101006L - -#endif // OPENSSL_NO_PSK - -#if QT_CONFIG(ocsp) - if (configuration.ocspStaplingEnabled()) { - if (mode == QSslSocket::SslServerMode) { - d->setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError, - QSslSocket::tr("Server-side QSslSocket does not support OCSP stapling")); - return false; - } - if (q_SSL_set_tlsext_status_type(ssl, TLSEXT_STATUSTYPE_ocsp) != 1) { - d->setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Failed to enable OCSP stapling")); - return false; - } - } - - ocspResponseDer.clear(); - const auto backendConfig = configuration.backendConfiguration(); - auto responsePos = backendConfig.find("Qt-OCSP-response"); - if (responsePos != backendConfig.end()) { - // This is our private, undocumented 'API' we use for the auto-testing of - // OCSP-stapling. It must be a der-encoded OCSP response, presumably set - // by tst_QOcsp. - const QVariant data(responsePos.value()); - if (data.canConvert<QByteArray>()) - ocspResponseDer = data.toByteArray(); - } - - if (ocspResponseDer.size()) { - if (mode != QSslSocket::SslServerMode) { - d->setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError, - QSslSocket::tr("Client-side sockets do not send OCSP responses")); - return false; - } - } -#endif // ocsp - - return true; -} - -void TlsCryptographOpenSSL::destroySslContext() -{ - if (ssl) { - if (!q_SSL_in_init(ssl) && !systemOrSslErrorDetected) { - // We do not send a shutdown alert here. Just mark the session as - // resumable for qhttpnetworkconnection's "optimization", otherwise - // OpenSSL won't start a session resumption. - if (q_SSL_shutdown(ssl) != 1) { - // Some error may be queued, clear it. - const auto errors = QTlsBackendOpenSSL::getErrorsFromOpenSsl(); - Q_UNUSED(errors); - } - } - q_SSL_free(ssl); - ssl = nullptr; - } - sslContextPointer.clear(); -} - -void TlsCryptographOpenSSL::storePeerCertificates() -{ - Q_ASSERT(d); - - // Store the peer certificate and chain. For clients, the peer certificate - // chain includes the peer certificate; for servers, it doesn't. Both the - // peer certificate and the chain may be empty if the peer didn't present - // any certificate. - X509 *x509 = q_SSL_get_peer_certificate(ssl); - - const auto peerCertificate = QTlsPrivate::X509CertificateOpenSSL::certificateFromX509(x509); - QTlsBackend::storePeerCertificate(d, peerCertificate); - q_X509_free(x509); - auto peerCertificateChain = q->peerCertificateChain(); - if (peerCertificateChain.isEmpty()) { - peerCertificateChain = QTlsPrivate::X509CertificateOpenSSL::stackOfX509ToQSslCertificates(q_SSL_get_peer_cert_chain(ssl)); - if (!peerCertificate.isNull() && d->tlsMode() == QSslSocket::SslServerMode) - peerCertificateChain.prepend(peerCertificate); - QTlsBackend::storePeerCertificateChain(d, peerCertificateChain); - } -} - -#if QT_CONFIG(ocsp) - -bool TlsCryptographOpenSSL::checkOcspStatus() -{ - Q_ASSERT(ssl); - Q_ASSERT(d); - - const auto &configuration = q->sslConfiguration(); - Q_ASSERT(d->tlsMode() == QSslSocket::SslClientMode); // See initSslContext() for SslServerMode - Q_ASSERT(configuration.peerVerifyMode() != QSslSocket::VerifyNone); - - const auto clearErrorQueue = qScopeGuard([] { - QTlsBackendOpenSSL::logAndClearErrorQueue(); - }); - - ocspResponses.clear(); - ocspErrorDescription.clear(); - ocspErrors.clear(); - - const unsigned char *responseData = nullptr; - const long responseLength = q_SSL_get_tlsext_status_ocsp_resp(ssl, &responseData); - if (responseLength <= 0 || !responseData) { - ocspErrors.push_back(QSslError(QSslError::OcspNoResponseFound)); - return false; - } - - OCSP_RESPONSE *response = q_d2i_OCSP_RESPONSE(nullptr, &responseData, responseLength); - if (!response) { - // Treat this as a fatal SslHandshakeError. - ocspErrorDescription = QSslSocket::tr("Failed to decode OCSP response"); - return false; - } - const QSharedPointer<OCSP_RESPONSE> responseGuard(response, q_OCSP_RESPONSE_free); - - const int ocspStatus = q_OCSP_response_status(response); - if (ocspStatus != OCSP_RESPONSE_STATUS_SUCCESSFUL) { - // It's not a definitive response, it's an error message (not signed by the responder). - ocspErrors.push_back(QSslError(qt_OCSP_response_status_to_SslError(ocspStatus))); - return false; - } - - OCSP_BASICRESP *basicResponse = q_OCSP_response_get1_basic(response); - if (!basicResponse) { - // SslHandshakeError. - ocspErrorDescription = QSslSocket::tr("Failed to extract basic OCSP response"); - return false; - } - const QSharedPointer<OCSP_BASICRESP> basicResponseGuard(basicResponse, q_OCSP_BASICRESP_free); - - SSL_CTX *ctx = q_SSL_get_SSL_CTX(ssl); // Does not increment refcount. - Q_ASSERT(ctx); - X509_STORE *store = q_SSL_CTX_get_cert_store(ctx); // Does not increment refcount. - if (!store) { - // SslHandshakeError. - ocspErrorDescription = QSslSocket::tr("No certificate verification store, cannot verify OCSP response"); - return false; - } - - STACK_OF(X509) *peerChain = q_SSL_get_peer_cert_chain(ssl); // Does not increment refcount. - X509 *peerX509 = q_SSL_get_peer_certificate(ssl); - Q_ASSERT(peerChain || peerX509); - const QSharedPointer<X509> peerX509Guard(peerX509, q_X509_free); - // OCSP_basic_verify with 0 as verificationFlags: - // - // 0) Tries to find the OCSP responder's certificate in either peerChain - // or basicResponse->certs. If not found, verification fails. - // 1) It checks the signature using the responder's public key. - // 2) Then it tries to validate the responder's cert (building a chain - // etc.) - // 3) It checks CertID in response. - // 4) Ensures the responder is authorized to sign the status respond. - // - // Note, OpenSSL prior to 1.0.2b would only use bs->certs to - // verify the responder's chain (see their commit 4ba9a4265bd). - // Working this around - is too much fuss for ancient versions we - // are dropping quite soon anyway. - const unsigned long verificationFlags = 0; - const int success = q_OCSP_basic_verify(basicResponse, peerChain, store, verificationFlags); - if (success <= 0) - ocspErrors.push_back(QSslError(QSslError::OcspResponseCannotBeTrusted)); - - if (q_OCSP_resp_count(basicResponse) != 1) { - ocspErrors.push_back(QSslError(QSslError::OcspMalformedResponse)); - return false; - } - - OCSP_SINGLERESP *singleResponse = q_OCSP_resp_get0(basicResponse, 0); - if (!singleResponse) { - ocspErrors.clear(); - // A fatal problem -> SslHandshakeError. - ocspErrorDescription = QSslSocket::tr("Failed to decode a SingleResponse from OCSP status response"); - return false; - } - - // Let's make sure the response is for the correct certificate - we - // can re-create this CertID using our peer's certificate and its - // issuer's public key. - ocspResponses.push_back(QOcspResponse()); - QOcspResponsePrivate *dResponse = ocspResponses.back().d.data(); - dResponse->subjectCert = configuration.peerCertificate(); - bool matchFound = false; - if (dResponse->subjectCert.isSelfSigned()) { - dResponse->signerCert = configuration.peerCertificate(); - matchFound = qt_OCSP_certificate_match(singleResponse, peerX509, peerX509); - } else { - const STACK_OF(X509) *certs = q_SSL_get_peer_cert_chain(ssl); - if (!certs) // Oh, what a cataclysm! Last try: - certs = q_OCSP_resp_get0_certs(basicResponse); - if (certs) { - // It could be the first certificate in 'certs' is our peer's - // certificate. Since it was not captured by the 'self-signed' branch - // above, the CertID will not match and we'll just iterate on to the - // next certificate. So we start from 0, not 1. - for (int i = 0, e = q_sk_X509_num(certs); i < e; ++i) { - X509 *issuer = q_sk_X509_value(certs, i); - matchFound = qt_OCSP_certificate_match(singleResponse, peerX509, issuer); - if (matchFound) { - if (q_X509_check_issued(issuer, peerX509) == X509_V_OK) { - dResponse->signerCert = QTlsPrivate::X509CertificateOpenSSL::certificateFromX509(issuer); - break; - } - matchFound = false; - } - } - } - } - - if (!matchFound) { - dResponse->signerCert.clear(); - ocspErrors.push_back({QSslError::OcspResponseCertIdUnknown, configuration.peerCertificate()}); - } - - // Check if the response is valid time-wise: - ASN1_GENERALIZEDTIME *revTime = nullptr; - ASN1_GENERALIZEDTIME *thisUpdate = nullptr; - ASN1_GENERALIZEDTIME *nextUpdate = nullptr; - int reason; - const int certStatus = q_OCSP_single_get0_status(singleResponse, &reason, &revTime, &thisUpdate, &nextUpdate); - if (!thisUpdate) { - // This is unexpected, treat as SslHandshakeError, OCSP_check_validity assumes this pointer - // to be != nullptr. - ocspErrors.clear(); - ocspResponses.clear(); - ocspErrorDescription = QSslSocket::tr("Failed to extract 'this update time' from the SingleResponse"); - return false; - } - - // OCSP_check_validity(this, next, nsec, maxsec) does this check: - // this <= now <= next. They allow some freedom to account - // for delays/time inaccuracy. - // this > now + nsec ? -> NOT_YET_VALID - // if maxsec >= 0: - // now - maxsec > this ? -> TOO_OLD - // now - nsec > next ? -> EXPIRED - // next < this ? -> NEXT_BEFORE_THIS - // OK. - if (!q_OCSP_check_validity(thisUpdate, nextUpdate, 60, -1)) - ocspErrors.push_back({QSslError::OcspResponseExpired, configuration.peerCertificate()}); - - // And finally, the status: - switch (certStatus) { - case V_OCSP_CERTSTATUS_GOOD: - // This certificate was not found among the revoked ones. - dResponse->certificateStatus = QOcspCertificateStatus::Good; - break; - case V_OCSP_CERTSTATUS_REVOKED: - dResponse->certificateStatus = QOcspCertificateStatus::Revoked; - dResponse->revocationReason = qt_OCSP_revocation_reason(reason); - ocspErrors.push_back({QSslError::CertificateRevoked, configuration.peerCertificate()}); - break; - case V_OCSP_CERTSTATUS_UNKNOWN: - dResponse->certificateStatus = QOcspCertificateStatus::Unknown; - ocspErrors.push_back({QSslError::OcspStatusUnknown, configuration.peerCertificate()}); - } - - return !ocspErrors.size(); -} - -#endif // QT_CONFIG(ocsp) - - -unsigned TlsCryptographOpenSSL::pskClientTlsCallback(const char *hint, char *identity, - unsigned max_identity_len, - unsigned char *psk, unsigned max_psk_len) -{ - Q_ASSERT(q); - - QSslPreSharedKeyAuthenticator authenticator; - // Fill in some read-only fields (for the user) - const int hintLength = hint ? int(std::strlen(hint)) : 0; - QTlsBackend::setupClientPskAuth(&authenticator, hint, hintLength, max_identity_len, max_psk_len); - // Let the client provide the remaining bits... - emit q->preSharedKeyAuthenticationRequired(&authenticator); - - // No PSK set? Return now to make the handshake fail - if (authenticator.preSharedKey().isEmpty()) - return 0; - - // Copy data back into OpenSSL - const int identityLength = qMin(authenticator.identity().length(), authenticator.maximumIdentityLength()); - std::memcpy(identity, authenticator.identity().constData(), identityLength); - identity[identityLength] = 0; - - const int pskLength = qMin(authenticator.preSharedKey().length(), authenticator.maximumPreSharedKeyLength()); - std::memcpy(psk, authenticator.preSharedKey().constData(), pskLength); - return pskLength; -} - -unsigned TlsCryptographOpenSSL::pskServerTlsCallback(const char *identity, unsigned char *psk, - unsigned max_psk_len) -{ - Q_ASSERT(q); - - QSslPreSharedKeyAuthenticator authenticator; - - // Fill in some read-only fields (for the user) - QTlsBackend::setupServerPskAuth(&authenticator, identity, q->sslConfiguration().preSharedKeyIdentityHint(), - max_psk_len); - emit q->preSharedKeyAuthenticationRequired(&authenticator); - - // No PSK set? Return now to make the handshake fail - if (authenticator.preSharedKey().isEmpty()) - return 0; - - // Copy data back into OpenSSL - const int pskLength = qMin(authenticator.preSharedKey().length(), authenticator.maximumPreSharedKeyLength()); - std::memcpy(psk, authenticator.preSharedKey().constData(), pskLength); - return pskLength; -} - -#ifdef Q_OS_WIN - -void TlsCryptographOpenSSL::fetchCaRootForCert(const QSslCertificate &cert) -{ - Q_ASSERT(d); - Q_ASSERT(q); - - //The root certificate is downloaded from windows update, which blocks for 15 seconds in the worst case - //so the request is done in a worker thread. - QList<QSslCertificate> customRoots; - if (fetchAuthorityInformation) - customRoots = q->sslConfiguration().caCertificates(); - - //Remember we are fetching and what we are fetching: - caToFetch = cert; - - QWindowsCaRootFetcher *fetcher = new QWindowsCaRootFetcher(cert, d->tlsMode(), customRoots, - q->peerVerifyName()); - connect(fetcher, &QWindowsCaRootFetcher::finished, this, &TlsCryptographOpenSSL::caRootLoaded, - Qt::QueuedConnection); - QMetaObject::invokeMethod(fetcher, "start", Qt::QueuedConnection); - QSslSocketPrivate::pauseSocketNotifiers(q); - d->setPaused(true); -} - -void TlsCryptographOpenSSL::caRootLoaded(QSslCertificate cert, QSslCertificate trustedRoot) -{ - if (caToFetch != cert) { - //Ooops, something from the previous connection attempt, ignore! - return; - } - - Q_ASSERT(d); - Q_ASSERT(q); - - //Done, fetched already: - caToFetch = QSslCertificate{}; - - if (fetchAuthorityInformation) { - if (!q->sslConfiguration().caCertificates().contains(trustedRoot)) - trustedRoot = QSslCertificate{}; - fetchAuthorityInformation = false; - } - - if (!trustedRoot.isNull() && !trustedRoot.isBlacklisted()) { - if (QSslSocketPrivate::rootCertOnDemandLoadingSupported()) { - //Add the new root cert to default cert list for use by future sockets - auto defaultConfig = QSslConfiguration::defaultConfiguration(); - defaultConfig.addCaCertificate(trustedRoot); - QSslConfiguration::setDefaultConfiguration(defaultConfig); - } - //Add the new root cert to this socket for future connections - QTlsBackend::addTustedRoot(d, trustedRoot); - //Remove the broken chain ssl errors (as chain is verified by windows) - for (int i=sslErrors.count() - 1; i >= 0; --i) { - if (sslErrors.at(i).certificate() == cert) { - switch (sslErrors.at(i).error()) { - case QSslError::UnableToGetLocalIssuerCertificate: - case QSslError::CertificateUntrusted: - case QSslError::UnableToVerifyFirstCertificate: - case QSslError::SelfSignedCertificateInChain: - // error can be ignored if OS says the chain is trusted - sslErrors.removeAt(i); - break; - default: - // error cannot be ignored - break; - } - } - } - } - - auto *plainSocket = d->plainTcpSocket(); - Q_ASSERT(plainSocket); - // Continue with remaining errors - if (plainSocket) - plainSocket->resume(); - d->setPaused(false); - if (checkSslErrors() && ssl) { - bool willClose = (d->isAutoStartingHandshake() && d->isPendingClose()); - continueHandshake(); - if (!willClose) - transmit(); - } -} - -#endif // Q_OS_WIN - -} // namespace QTlsPrivate - -QT_END_NAMESPACE diff --git a/src/network/ssl/qtls_openssl_p.h b/src/network/ssl/qtls_openssl_p.h deleted file mode 100644 index 1def93831d..0000000000 --- a/src/network/ssl/qtls_openssl_p.h +++ /dev/null @@ -1,169 +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$ -** -****************************************************************************/ - -#ifndef QTLS_OPENSSL_P_H -#define QTLS_OPENSSL_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 <private/qtnetworkglobal_p.h> - -#include "qtlsbackend_openssl_p.h" -#include "qsslcontext_openssl_p.h" -#include "qsslcertificate.h" -#include "qocspresponse.h" -#include "qopenssl_p.h" - -#include <QtCore/qsharedpointer.h> -#include <QtCore/qbytearray.h> -#include <QtCore/qglobal.h> -#include <QtCore/qlist.h> - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -class TlsCryptographOpenSSL : public TlsCryptograph -{ -public: - enum ExDataOffset { - errorOffsetInExData = 1, - socketOffsetInExData = 2 - }; - - ~TlsCryptographOpenSSL(); - - void init(QSslSocket *qObj, QSslSocketPrivate *dObj) override; - void checkSettingSslContext(QSharedPointer<QSslContext> tlsContext) override; - QSharedPointer<QSslContext> sslContext() const override; - - QList<QSslError> tlsErrors() const override; - - void startClientEncryption() override; - void startServerEncryption() override; - bool startHandshake(); - void enableHandshakeContinuation() override; - void cancelCAFetch() override; - void continueHandshake() override; - void transmit() override; - void disconnectFromHost() override; - void disconnected() override; - QSslCipher sessionCipher() const override; - QSsl::SslProtocol sessionProtocol() const override; - QList<QOcspResponse> ocsps() const override; - - bool checkSslErrors(); - int handleNewSessionTicket(SSL *connection); - - void alertMessageSent(int encoded); - void alertMessageReceived(int encoded); - - int emitErrorFromCallback(X509_STORE_CTX *ctx); - void trySendFatalAlert(); - -#if QT_CONFIG(ocsp) - bool checkOcspStatus(); -#endif - - QSslSocket *q = nullptr; - QSslSocketPrivate *d = nullptr; - - void storePeerCertificates(); - - unsigned pskClientTlsCallback(const char *hint, char *identity, unsigned max_identity_len, - unsigned char *psk, unsigned max_psk_len); - unsigned pskServerTlsCallback(const char *identity, unsigned char *psk, - unsigned max_psk_len); - -#ifdef Q_OS_WIN - void fetchCaRootForCert(const QSslCertificate &cert); - void caRootLoaded(QSslCertificate certificate, QSslCertificate trustedRoot); -#endif - - QByteArray ocspResponseDer; -private: - // TLSTODO: names were preserved, to make comparison - // easier (see qsslsocket_openssl.cpp, while it exists). - bool initSslContext(); - void destroySslContext(); - - QSharedPointer<QSslContext> sslContextPointer; - SSL *ssl = nullptr; // TLSTODO: RAII. - - QList<QSslErrorEntry> errorList; - QList<QSslError> sslErrors; - - BIO *readBio = nullptr; - BIO *writeBio = nullptr; - - QList<QOcspResponse> ocspResponses; - - // This decription will go to setErrorAndEmit(SslHandshakeError, ocspErrorDescription) - QString ocspErrorDescription; - // These will go to sslErrors() - QList<QSslError> ocspErrors; - - bool systemOrSslErrorDetected = false; - bool handshakeInterrupted = false; - - bool fetchAuthorityInformation = false; - QSslCertificate caToFetch; - - bool inSetAndEmitError = false; - bool pendingFatalAlert = false; - bool errorsReportedFromCallback = false; - - bool shutdown = false; -}; - -} // namespace QTlsPrivate - -QT_END_NAMESPACE - -#endif // QTLS_OPENSSL_P_H - diff --git a/src/network/ssl/qtls_schannel.cpp b/src/network/ssl/qtls_schannel.cpp deleted file mode 100644 index cb4b9ce79d..0000000000 --- a/src/network/ssl/qtls_schannel.cpp +++ /dev/null @@ -1,2322 +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$ -** -****************************************************************************/ - -// #define QSSLSOCKET_DEBUG - -#include "qssl_p.h" -#include "qsslsocket.h" -#include "qtls_schannel_p.h" -#include "qsslcertificate.h" -#include "qsslcertificateextension.h" -#include "qsslcertificate_p.h" -#include "qsslcipher_p.h" -#include "qtlsbackend_schannel_p.h" -#include "qtlskey_schannel_p.h" -#include "qx509_schannel_p.h" - -#include <QtCore/qscopeguard.h> -#include <QtCore/qoperatingsystemversion.h> -#include <QtCore/qregularexpression.h> -#include <QtCore/qdatastream.h> -#include <QtCore/qmutex.h> - -#define SECURITY_WIN32 -#include <security.h> -#include <schnlsp.h> - -#if NTDDI_VERSION >= NTDDI_WINBLUE && !defined(Q_CC_MINGW) -// ALPN = Application Layer Protocol Negotiation -#define SUPPORTS_ALPN 1 -#endif - -// Redstone 5/1809 has all the API available, but TLS 1.3 is not enabled until a later version of -// Win 10, checked at runtime in supportsTls13() -#if defined(NTDDI_WIN10_RS5) && NTDDI_VERSION >= NTDDI_WIN10_RS5 -#define SUPPORTS_TLS13 1 -#endif - -// Not defined in MinGW -#ifndef SECBUFFER_ALERT -#define SECBUFFER_ALERT 17 -#endif -#ifndef SECPKG_ATTR_APPLICATION_PROTOCOL -#define SECPKG_ATTR_APPLICATION_PROTOCOL 35 -#endif - -// Another missing MinGW define -#ifndef SEC_E_APPLICATION_PROTOCOL_MISMATCH -#define SEC_E_APPLICATION_PROTOCOL_MISMATCH _HRESULT_TYPEDEF_(0x80090367L) -#endif - -// Also not defined in MinGW....... -#ifndef SP_PROT_TLS1_SERVER -#define SP_PROT_TLS1_SERVER 0x00000040 -#endif -#ifndef SP_PROT_TLS1_CLIENT -#define SP_PROT_TLS1_CLIENT 0x00000080 -#endif -#ifndef SP_PROT_TLS1_0_SERVER -#define SP_PROT_TLS1_0_SERVER SP_PROT_TLS1_SERVER -#endif -#ifndef SP_PROT_TLS1_0_CLIENT -#define SP_PROT_TLS1_0_CLIENT SP_PROT_TLS1_CLIENT -#endif -#ifndef SP_PROT_TLS1_0 -#define SP_PROT_TLS1_0 (SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_0_SERVER) -#endif -#ifndef SP_PROT_TLS1_1_SERVER -#define SP_PROT_TLS1_1_SERVER 0x00000100 -#endif -#ifndef SP_PROT_TLS1_1_CLIENT -#define SP_PROT_TLS1_1_CLIENT 0x00000200 -#endif -#ifndef SP_PROT_TLS1_1 -#define SP_PROT_TLS1_1 (SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_1_SERVER) -#endif -#ifndef SP_PROT_TLS1_2_SERVER -#define SP_PROT_TLS1_2_SERVER 0x00000400 -#endif -#ifndef SP_PROT_TLS1_2_CLIENT -#define SP_PROT_TLS1_2_CLIENT 0x00000800 -#endif -#ifndef SP_PROT_TLS1_2 -#define SP_PROT_TLS1_2 (SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_2_SERVER) -#endif -#ifndef SP_PROT_TLS1_3_SERVER -#define SP_PROT_TLS1_3_SERVER 0x00001000 -#endif -#ifndef SP_PROT_TLS1_3_CLIENT -#define SP_PROT_TLS1_3_CLIENT 0x00002000 -#endif -#ifndef SP_PROT_TLS1_3 -#define SP_PROT_TLS1_3 (SP_PROT_TLS1_3_CLIENT | SP_PROT_TLS1_3_SERVER) -#endif - -/* - @future!: - - - Transmitting intermediate certificates - - Look for a way to avoid putting intermediate certificates in the certificate store - - No documentation on how to send the chain - - A stackoverflow question on this from 3 years ago implies schannel only sends intermediate - certificates if it's "in the system or user certificate store". - - https://stackoverflow.com/q/30156584/2493610 - - This can be done by users, but we shouldn't add any and all local intermediate - certs to the stores automatically. - - PSK support - - Was added in Windows 10 (it seems), documentation at time of writing is sparse/non-existent. - - Specifically about how to supply credentials when they're requested. - - Or how to recognize that they're requested in the first place. - - Skip certificate verification. - - Check if "PSK-only" is still required to do PSK _at all_ (all-around bad solution). - - Check if SEC_I_INCOMPLETE_CREDENTIALS is still returned for both "missing certificate" and - "missing PSK" when calling InitializeSecurityContext in "performHandshake". - - Medium priority: - - Setting cipher-suites (or ALG_ID) - - People have survived without it in WinRT - - Low priority: - - Possibly make RAII wrappers for SecBuffer (which I commonly create QScopeGuards for) - -*/ - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.schannel"); - -// Defined in qsslsocket_qt.cpp. -QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, - const QString &passPhrase); - -namespace QTlsPrivate { - -QList<QSslCipher> defaultCiphers() -{ - // Previously the code was in QSslSocketBackendPrivate. - QList<QSslCipher> ciphers; - // @temp (I hope), stolen from qsslsocket_winrt.cpp - const QString protocolStrings[] = { QStringLiteral("TLSv1"), QStringLiteral("TLSv1.1"), - QStringLiteral("TLSv1.2"), QStringLiteral("TLSv1.3") }; - const QSsl::SslProtocol protocols[] = { QSsl::TlsV1_0, QSsl::TlsV1_1, - QSsl::TlsV1_2, QSsl::TlsV1_3 }; - const int size = ARRAYSIZE(protocols); - static_assert(size == ARRAYSIZE(protocolStrings)); - ciphers.reserve(size); - for (int i = 0; i < size; ++i) { - const QSslCipher cipher = QTlsBackend::createCipher(QStringLiteral("Schannel"), - protocols[i], protocolStrings[i]); - - ciphers.append(cipher); - } - - return ciphers; - -} - -} // namespace QTlsPrivate - -namespace { -bool supportsTls13(); -} - -bool QSchannelBackend::s_loadedCiphersAndCerts = false; -Q_GLOBAL_STATIC(QRecursiveMutex, qt_schannel_mutex) - -long QSchannelBackend::tlsLibraryVersionNumber() const -{ - const auto os = QOperatingSystemVersion::current(); - return (os.majorVersion() << 24) | ((os.minorVersion() & 0xFF) << 16) | (os.microVersion() & 0xFFFF); -} - -QString QSchannelBackend::tlsLibraryVersionString() const -{ - const auto os = QOperatingSystemVersion::current(); - return QString::fromLatin1("Secure Channel, %1 %2.%3.%4") - .arg(os.name(), - QString::number(os.majorVersion()), - QString::number(os.minorVersion()), - QString::number(os.microVersion())); -} - -long QSchannelBackend::tlsLibraryBuildVersionNumber() const -{ - return tlsLibraryVersionNumber(); -} - -QString QSchannelBackend::tlsLibraryBuildVersionString() const -{ - const auto os = QOperatingSystemVersion::current(); - return QString::fromLatin1("%1.%2.%3") - .arg(QString::number(os.majorVersion()), - QString::number(os.minorVersion()), - QString::number(os.microVersion())); -} - -void QSchannelBackend::ensureInitialized() const -{ - ensureInitializedImplementation(); -} - -void QSchannelBackend::ensureInitializedImplementation() -{ - const QMutexLocker<QRecursiveMutex> locker(qt_schannel_mutex); - if (s_loadedCiphersAndCerts) - return; - s_loadedCiphersAndCerts = true; - - setDefaultCaCertificates(systemCaCertificatesImplementation()); - // setDefaultCaCertificates sets it to false, re-enable it: - QSslSocketPrivate::setRootCertOnDemandLoadingSupported(true); - - resetDefaultCiphers(); -} - -void QSchannelBackend::resetDefaultCiphers() -{ - setDefaultSupportedCiphers(QTlsPrivate::defaultCiphers()); - setDefaultCiphers(QTlsPrivate::defaultCiphers()); -} - -QString QSchannelBackend::backendName() const -{ - return builtinBackendNames[nameIndexSchannel]; -} - -QList<QSsl::SslProtocol> QSchannelBackend::supportedProtocols() const -{ - QList<QSsl::SslProtocol> protocols; - - protocols << QSsl::AnyProtocol; - protocols << QSsl::SecureProtocols; - protocols << QSsl::TlsV1_0; - protocols << QSsl::TlsV1_0OrLater; - protocols << QSsl::TlsV1_1; - protocols << QSsl::TlsV1_1OrLater; - protocols << QSsl::TlsV1_2; - protocols << QSsl::TlsV1_2OrLater; - - if (supportsTls13()) { - protocols << QSsl::TlsV1_3; - protocols << QSsl::TlsV1_3OrLater; - } - - return protocols; -} - -QList<QSsl::SupportedFeature> QSchannelBackend::supportedFeatures() const -{ - QList<QSsl::SupportedFeature> features; - - features << QSsl::SupportedFeature::ClientSideAlpn; - features << QSsl::SupportedFeature::ServerSideAlpn; - - return features; -} - -QList<QSsl::ImplementedClass> QSchannelBackend::implementedClasses() const -{ - QList<QSsl::ImplementedClass> classes; - - classes << QSsl::ImplementedClass::Socket; - classes << QSsl::ImplementedClass::Certificate; - classes << QSsl::ImplementedClass::Key; - - return classes; -} - -QTlsPrivate::TlsKey *QSchannelBackend::createKey() const -{ - return new QTlsPrivate::TlsKeySchannel; -} - -QTlsPrivate::X509Certificate *QSchannelBackend::createCertificate() const -{ - return new QTlsPrivate::X509CertificateSchannel; -} - -QList<QSslCertificate> QSchannelBackend::systemCaCertificates() const -{ - return systemCaCertificatesImplementation(); -} - -QTlsPrivate::TlsCryptograph *QSchannelBackend::createTlsCryptograph() const -{ - return new QTlsPrivate::TlsCryptographSchannel; -} - -QList<QSslCertificate> QSchannelBackend::systemCaCertificatesImplementation() -{ - // Similar to non-Darwin version found in qtlsbackend_openssl.cpp, - // QTlsPrivate::systemCaCertificates function. - QList<QSslCertificate> systemCerts; - auto hSystemStore = QHCertStorePointer(CertOpenSystemStore(0, L"ROOT")); - if (hSystemStore) { - PCCERT_CONTEXT pc = nullptr; - while ((pc = CertFindCertificateInStore(hSystemStore.get(), X509_ASN_ENCODING, 0, - CERT_FIND_ANY, nullptr, pc))) { - systemCerts.append(QTlsPrivate::X509CertificateSchannel::QSslCertificate_from_CERT_CONTEXT(pc)); - } - } - return systemCerts; -} - -QTlsPrivate::X509PemReaderPtr QSchannelBackend::X509PemReader() const -{ - return QTlsPrivate::X509CertificateGeneric::certificatesFromPem; -} - -QTlsPrivate::X509DerReaderPtr QSchannelBackend::X509DerReader() const -{ - return QTlsPrivate::X509CertificateGeneric::certificatesFromDer; -} - -Q_GLOBAL_STATIC(QSchannelBackend, backendSchannel) - -namespace { - -SecBuffer createSecBuffer(void *ptr, unsigned long length, unsigned long bufferType) -{ - return SecBuffer{ length, bufferType, ptr }; -} - -SecBuffer createSecBuffer(QByteArray &buffer, unsigned long bufferType) -{ - return createSecBuffer(buffer.data(), static_cast<unsigned long>(buffer.length()), bufferType); -} - -QString schannelErrorToString(qint32 status) -{ - switch (status) { - case SEC_E_INSUFFICIENT_MEMORY: - return QSslSocket::tr("Insufficient memory"); - case SEC_E_INTERNAL_ERROR: - return QSslSocket::tr("Internal error"); - case SEC_E_INVALID_HANDLE: - return QSslSocket::tr("An internal handle was invalid"); - case SEC_E_INVALID_TOKEN: - return QSslSocket::tr("An internal token was invalid"); - case SEC_E_LOGON_DENIED: - // According to the link below we get this error when Schannel receives TLS1_ALERT_ACCESS_DENIED - // https://docs.microsoft.com/en-us/windows/desktop/secauthn/schannel-error-codes-for-tls-and-ssl-alerts - return QSslSocket::tr("Access denied"); - case SEC_E_NO_AUTHENTICATING_AUTHORITY: - return QSslSocket::tr("No authority could be contacted for authorization"); - case SEC_E_NO_CREDENTIALS: - return QSslSocket::tr("No credentials"); - case SEC_E_TARGET_UNKNOWN: - return QSslSocket::tr("The target is unknown or unreachable"); - case SEC_E_UNSUPPORTED_FUNCTION: - return QSslSocket::tr("An unsupported function was requested"); - case SEC_E_WRONG_PRINCIPAL: - // SNI error - return QSslSocket::tr("The hostname provided does not match the one received from the peer"); - case SEC_E_APPLICATION_PROTOCOL_MISMATCH: - return QSslSocket::tr("No common protocol exists between the client and the server"); - case SEC_E_ILLEGAL_MESSAGE: - return QSslSocket::tr("Unexpected or badly-formatted message received"); - case SEC_E_ENCRYPT_FAILURE: - return QSslSocket::tr("The data could not be encrypted"); - case SEC_E_ALGORITHM_MISMATCH: - return QSslSocket::tr("No cipher suites in common"); - case SEC_E_UNKNOWN_CREDENTIALS: - // This can mean "invalid argument" in some cases... - return QSslSocket::tr("The credentials were not recognized / Invalid argument"); - case SEC_E_MESSAGE_ALTERED: - // According to the Internet it also triggers for messages that are out of order. - // https://microsoft.public.platformsdk.security.narkive.com/4JAvlMvD/help-please-schannel-security-contexts-and-decryptmessage - return QSslSocket::tr("The message was tampered with, damaged or out of sequence."); - case SEC_E_OUT_OF_SEQUENCE: - return QSslSocket::tr("A message was received out of sequence."); - case SEC_E_CONTEXT_EXPIRED: - return QSslSocket::tr("The TLS/SSL connection has been closed"); - default: - return QSslSocket::tr("Unknown error occurred: %1").arg(status); - } -} - -bool supportsTls13() -{ -#ifdef SUPPORTS_TLS13 - static bool supported = []() { - const auto current = QOperatingSystemVersion::current(); - // 20221 just happens to be the preview version I run on my laptop where I tested TLS 1.3. - const auto minimum = - QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 20221); - return current >= minimum; - }(); - return supported; -#else - return false; -#endif -} - -DWORD toSchannelProtocol(QSsl::SslProtocol protocol) -{ - DWORD protocols = SP_PROT_NONE; - switch (protocol) { - case QSsl::UnknownProtocol: - return DWORD(-1); - case QSsl::DtlsV1_0: - case QSsl::DtlsV1_2: - case QSsl::DtlsV1_0OrLater: - case QSsl::DtlsV1_2OrLater: - return DWORD(-1); // Not supported at the moment (@future) - case QSsl::AnyProtocol: - protocols = SP_PROT_TLS1_0 | SP_PROT_TLS1_1 | SP_PROT_TLS1_2; - if (supportsTls13()) - protocols |= SP_PROT_TLS1_3; - break; - case QSsl::TlsV1_0: - protocols = SP_PROT_TLS1_0; - break; - case QSsl::TlsV1_1: - protocols = SP_PROT_TLS1_1; - break; - case QSsl::TlsV1_2: - protocols = SP_PROT_TLS1_2; - break; - case QSsl::TlsV1_3: - if (supportsTls13()) - protocols = SP_PROT_TLS1_3; - else - protocols = DWORD(-1); - break; - case QSsl::SecureProtocols: // TLS v1.0 and later is currently considered secure - case QSsl::TlsV1_0OrLater: - // For the "OrLater" protocols we fall through from one to the next, adding all of them - // in ascending order - protocols = SP_PROT_TLS1_0; - Q_FALLTHROUGH(); - case QSsl::TlsV1_1OrLater: - protocols |= SP_PROT_TLS1_1; - Q_FALLTHROUGH(); - case QSsl::TlsV1_2OrLater: - protocols |= SP_PROT_TLS1_2; - Q_FALLTHROUGH(); - case QSsl::TlsV1_3OrLater: - if (supportsTls13()) - protocols |= SP_PROT_TLS1_3; - else if (protocol == QSsl::TlsV1_3OrLater) - protocols = DWORD(-1); // if TlsV1_3OrLater was specifically chosen we should fail - break; - } - return protocols; -} - -#ifdef SUPPORTS_TLS13 -// In the new API that descended down upon us we are not asked which protocols we want -// but rather which protocols we don't want. So now we have this function to disable -// anything that is not enabled. -DWORD toSchannelProtocolNegated(QSsl::SslProtocol protocol) -{ - DWORD protocols = SP_PROT_ALL; // all protocols - protocols &= ~toSchannelProtocol(protocol); // minus the one(s) we want - return protocols; -} -#endif - -/*! - \internal - Used when converting the established session's \a protocol back to - Qt's own SslProtocol type. - - Only one protocol should be passed in at a time. -*/ -QSsl::SslProtocol toQtSslProtocol(DWORD protocol) -{ -#define MAP_PROTOCOL(sp_protocol, q_protocol) \ - if (protocol & sp_protocol) { \ - Q_ASSERT(!(protocol & ~sp_protocol)); \ - return q_protocol; \ - } - - MAP_PROTOCOL(SP_PROT_TLS1_0, QSsl::TlsV1_0) - MAP_PROTOCOL(SP_PROT_TLS1_1, QSsl::TlsV1_1) - MAP_PROTOCOL(SP_PROT_TLS1_2, QSsl::TlsV1_2) - MAP_PROTOCOL(SP_PROT_TLS1_3, QSsl::TlsV1_3) -#undef MAP_PROTOCOL - Q_UNREACHABLE(); - return QSsl::UnknownProtocol; -} - -/*! - \internal - Used by verifyCertContext to check if a client cert is used by a server or vice versa. -*/ -bool netscapeWrongCertType(const QList<QSslCertificateExtension> &extensions, bool isClient) -{ - const auto netscapeIt = std::find_if( - extensions.cbegin(), extensions.cend(), - [](const QSslCertificateExtension &extension) { - const auto netscapeCertType = QStringLiteral("2.16.840.1.113730.1.1"); - return extension.oid() == netscapeCertType; - }); - if (netscapeIt != extensions.cend()) { - const QByteArray netscapeCertTypeByte = netscapeIt->value().toByteArray(); - int netscapeCertType = 0; - QDataStream dataStream(netscapeCertTypeByte); - dataStream >> netscapeCertType; - if (dataStream.status() != QDataStream::Status::Ok) - return true; - const int expectedPeerCertType = isClient ? NETSCAPE_SSL_SERVER_AUTH_CERT_TYPE - : NETSCAPE_SSL_CLIENT_AUTH_CERT_TYPE; - if ((netscapeCertType & expectedPeerCertType) == 0) - return true; - } - return false; -} - -/*! - \internal - Used by verifyCertContext to check the basicConstraints certificate - extension to see if the certificate is a certificate authority. - Returns false if the certificate does not have the basicConstraints - extension or if it is not a certificate authority. -*/ -bool isCertificateAuthority(const QList<QSslCertificateExtension> &extensions) -{ - auto it = std::find_if(extensions.cbegin(), extensions.cend(), - [](const QSslCertificateExtension &extension) { - return extension.name() == QLatin1String("basicConstraints"); - }); - if (it != extensions.cend()) { - QVariantMap basicConstraints = it->value().toMap(); - return basicConstraints.value(QLatin1String("ca"), false).toBool(); - } - return false; -} - -/*! - \internal - Returns true if the attributes we requested from the context/handshake have - been given. -*/ -bool matchesContextRequirements(DWORD attributes, DWORD requirements, - QSslSocket::PeerVerifyMode verifyMode, - bool isClient) -{ -#ifdef QSSLSOCKET_DEBUG -#define DEBUG_WARN(message) qCWarning(lcSsl, message) -#else -#define DEBUG_WARN(message) -#endif - -#define CHECK_ATTRIBUTE(attributeName) \ - do { \ - const DWORD req##attributeName = isClient ? ISC_REQ_##attributeName : ASC_REQ_##attributeName; \ - const DWORD ret##attributeName = isClient ? ISC_RET_##attributeName : ASC_RET_##attributeName; \ - if (!(requirements & req##attributeName) != !(attributes & ret##attributeName)) { \ - DEBUG_WARN("Missing attribute \"" #attributeName "\""); \ - return false; \ - } \ - } while (false) - - CHECK_ATTRIBUTE(CONFIDENTIALITY); - CHECK_ATTRIBUTE(REPLAY_DETECT); - CHECK_ATTRIBUTE(SEQUENCE_DETECT); - CHECK_ATTRIBUTE(STREAM); - if (verifyMode == QSslSocket::PeerVerifyMode::VerifyPeer) - CHECK_ATTRIBUTE(MUTUAL_AUTH); - - // This one is manual because there is no server / ASC_ version - if (isClient) { - const auto reqManualCredValidation = ISC_REQ_MANUAL_CRED_VALIDATION; - const auto retManualCredValidation = ISC_RET_MANUAL_CRED_VALIDATION; - if (!(requirements & reqManualCredValidation) != !(attributes & retManualCredValidation)) { - DEBUG_WARN("Missing attribute \"MANUAL_CRED_VALIDATION\""); - return false; - } - } - - return true; -#undef CHECK_ATTRIBUTE -#undef DEBUG_WARN -} - -template<typename Required, typename Actual> -Required const_reinterpret_cast(Actual *p) -{ - return Required(p); -} - -#ifdef SUPPORTS_ALPN -bool supportsAlpn() -{ - return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8_1; -} - -QByteArray createAlpnString(const QByteArrayList &nextAllowedProtocols) -{ - QByteArray alpnString; - if (!nextAllowedProtocols.isEmpty() && supportsAlpn()) { - const QByteArray names = [&nextAllowedProtocols]() { - QByteArray protocolString; - for (QByteArray proto : nextAllowedProtocols) { - if (proto.size() > 255) { - qCWarning(lcSsl) << "TLS ALPN extension" << proto - << "is too long and will be ignored."; - continue; - } else if (proto.isEmpty()) { - continue; - } - protocolString += char(proto.length()) + proto; - } - return protocolString; - }(); - if (names.isEmpty()) - return alpnString; - - const quint16 namesSize = names.size(); - const quint32 alpnId = SecApplicationProtocolNegotiationExt_ALPN; - const quint32 totalSize = sizeof(alpnId) + sizeof(namesSize) + namesSize; - alpnString = QByteArray::fromRawData(reinterpret_cast<const char *>(&totalSize), sizeof(totalSize)) - + QByteArray::fromRawData(reinterpret_cast<const char *>(&alpnId), sizeof(alpnId)) - + QByteArray::fromRawData(reinterpret_cast<const char *>(&namesSize), sizeof(namesSize)) - + names; - } - return alpnString; -} -#endif // SUPPORTS_ALPN - -qint64 readToBuffer(QByteArray &buffer, QTcpSocket *plainSocket) -{ - Q_ASSERT(plainSocket); - static const qint64 shrinkCutoff = 1024 * 12; - static const qint64 defaultRead = 1024 * 16; - qint64 bytesRead = 0; - - const auto toRead = std::min(defaultRead, plainSocket->bytesAvailable()); - if (toRead > 0) { - const auto bufferSize = buffer.size(); - buffer.reserve(bufferSize + toRead); // avoid growth strategy kicking in - buffer.resize(bufferSize + toRead); - bytesRead = plainSocket->read(buffer.data() + bufferSize, toRead); - buffer.resize(bufferSize + bytesRead); - // In case of excessive memory usage we shrink: - if (buffer.size() < shrinkCutoff && buffer.capacity() > defaultRead) - buffer.shrink_to_fit(); - } - - return bytesRead; -} - -void retainExtraData(QByteArray &buffer, const SecBuffer &secBuffer) -{ - Q_ASSERT(secBuffer.BufferType == SECBUFFER_EXTRA); - if (int(secBuffer.cbBuffer) >= buffer.size()) - return; - -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl, "We got SECBUFFER_EXTRA, will retain %lu bytes", secBuffer.cbBuffer); -#endif - std::move(buffer.end() - secBuffer.cbBuffer, buffer.end(), buffer.begin()); - buffer.resize(secBuffer.cbBuffer); -} - -qint64 checkIncompleteData(const SecBuffer &secBuffer) -{ - if (secBuffer.BufferType == SECBUFFER_MISSING) { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl, "Need %lu more bytes.", secBuffer.cbBuffer); -#endif - return secBuffer.cbBuffer; -} - return 0; -} - -} // anonymous namespace - - -namespace QTlsPrivate { - -TlsCryptographSchannel::TlsCryptographSchannel() -{ - SecInvalidateHandle(&credentialHandle); - SecInvalidateHandle(&contextHandle); - QSchannelBackend::ensureInitializedImplementation(); -} - -TlsCryptographSchannel::~TlsCryptographSchannel() -{ - closeCertificateStores(); - deallocateContext(); - freeCredentialsHandle(); - CertFreeCertificateContext(localCertContext); -} - -void TlsCryptographSchannel::init(QSslSocket *qObj, QSslSocketPrivate *dObj) -{ - Q_ASSERT(qObj); - Q_ASSERT(dObj); - - q = qObj; - d = dObj; - - reset(); -} - -bool TlsCryptographSchannel::sendToken(void *token, unsigned long tokenLength, bool emitError) -{ - if (tokenLength == 0) - return true; - - Q_ASSERT(d); - auto *plainSocket = d->plainTcpSocket(); - Q_ASSERT(plainSocket); - - const qint64 written = plainSocket->write(static_cast<const char *>(token), tokenLength); - if (written != qint64(tokenLength)) { - // Failed to write/buffer everything or an error occurred - if (emitError) - d->setErrorAndEmit(plainSocket->error(), plainSocket->errorString()); - return false; - } - return true; -} - -QString TlsCryptographSchannel::targetName() const -{ - // Used for SNI extension - Q_ASSERT(q); - Q_ASSERT(d); - - const auto verificationPeerName = d->verificationName(); - return verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName; -} - -ULONG TlsCryptographSchannel::getContextRequirements() -{ - Q_ASSERT(d); - Q_ASSERT(q); - - const bool isClient = d->tlsMode() == QSslSocket::SslClientMode; - ULONG req = 0; - - req |= ISC_REQ_ALLOCATE_MEMORY; // Allocate memory for buffers automatically - req |= ISC_REQ_CONFIDENTIALITY; // Encrypt messages - req |= ISC_REQ_REPLAY_DETECT; // Detect replayed messages - req |= ISC_REQ_SEQUENCE_DETECT; // Detect out of sequence messages - req |= ISC_REQ_STREAM; // Support a stream-oriented connection - - if (isClient) { - req |= ISC_REQ_MANUAL_CRED_VALIDATION; // Manually validate certificate - } else { - switch (q->peerVerifyMode()) { - case QSslSocket::PeerVerifyMode::VerifyNone: - // There doesn't seem to be a way to ask for an optional client cert :-( - case QSslSocket::PeerVerifyMode::AutoVerifyPeer: - case QSslSocket::PeerVerifyMode::QueryPeer: - break; - case QSslSocket::PeerVerifyMode::VerifyPeer: - req |= ISC_REQ_MUTUAL_AUTH; - break; - } - } - - return req; -} - -bool TlsCryptographSchannel::acquireCredentialsHandle() -{ - Q_ASSERT(d); - Q_ASSERT(q); - const auto &configuration = q->sslConfiguration(); - - Q_ASSERT(schannelState == SchannelState::InitializeHandshake); - - const bool isClient = d->tlsMode() == QSslSocket::SslClientMode; - const DWORD protocols = toSchannelProtocol(configuration.protocol()); - if (protocols == DWORD(-1)) { - d->setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError, - QSslSocket::tr("Invalid protocol chosen")); - return false; - } - - const CERT_CHAIN_CONTEXT *chainContext = nullptr; - auto freeCertChain = qScopeGuard([&chainContext]() { - if (chainContext) - CertFreeCertificateChain(chainContext); - }); - - DWORD certsCount = 0; - // Set up our certificate stores before trying to use one... - initializeCertificateStores(); - - // Check if user has specified a certificate chain but it could not be loaded. - // This happens if there was something wrong with the certificate chain or there was no private - // key. - if (!configuration.localCertificateChain().isEmpty() && !localCertificateStore) - return true; // 'true' because "tst_QSslSocket::setEmptyKey" expects us to not disconnect - - if (localCertificateStore != nullptr) { - CERT_CHAIN_FIND_BY_ISSUER_PARA findParam; - ZeroMemory(&findParam, sizeof(findParam)); - findParam.cbSize = sizeof(findParam); - findParam.pszUsageIdentifier = isClient ? szOID_PKIX_KP_CLIENT_AUTH : szOID_PKIX_KP_SERVER_AUTH; - - // There should only be one chain in our store, so.. we grab that one. - chainContext = CertFindChainInStore(localCertificateStore.get(), - X509_ASN_ENCODING, - 0, - CERT_CHAIN_FIND_BY_ISSUER, - &findParam, - nullptr); - if (!chainContext) { - const QString message = isClient - ? QSslSocket::tr("The certificate provided cannot be used for a client.") - : QSslSocket::tr("The certificate provided cannot be used for a server."); - d->setErrorAndEmit(QAbstractSocket::SocketError::SslInvalidUserDataError, message); - return false; - } - Q_ASSERT(chainContext->cChain == 1); - Q_ASSERT(chainContext->rgpChain[0]); - Q_ASSERT(chainContext->rgpChain[0]->cbSize >= 1); - Q_ASSERT(chainContext->rgpChain[0]->rgpElement[0]); - Q_ASSERT(!localCertContext); - localCertContext = CertDuplicateCertificateContext(chainContext->rgpChain[0] - ->rgpElement[0] - ->pCertContext); - certsCount = 1; - Q_ASSERT(localCertContext); - } - void *credentials = nullptr; -#ifdef SUPPORTS_TLS13 - TLS_PARAMETERS tlsParameters = { - 0, - nullptr, - toSchannelProtocolNegated(configuration.protocol()), // what protocols to disable - 0, - nullptr, - 0 - }; - if (supportsTls13()) { - SCH_CREDENTIALS *cred = new SCH_CREDENTIALS{ - SCH_CREDENTIALS_VERSION, - 0, - certsCount, - &localCertContext, - nullptr, - 0, - nullptr, - 0, - SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT - | SCH_CRED_NO_DEFAULT_CREDS, - 1, - &tlsParameters - }; - credentials = cred; - } else -#endif // SUPPORTS_TLS13 - { - SCHANNEL_CRED *cred = new SCHANNEL_CRED{ - SCHANNEL_CRED_VERSION, // dwVersion - certsCount, // cCreds - &localCertContext, // paCred (certificate(s) containing a private key for authentication) - nullptr, // hRootStore - - 0, // cMappers (reserved) - nullptr, // aphMappers (reserved) - - 0, // cSupportedAlgs - nullptr, // palgSupportedAlgs (nullptr = system default) - - protocols, // grbitEnabledProtocols - 0, // dwMinimumCipherStrength (0 = system default) - 0, // dwMaximumCipherStrength (0 = system default) - 0, // dwSessionLifespan (0 = schannel default, 10 hours) - SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT - | SCH_CRED_NO_DEFAULT_CREDS, // dwFlags - 0 // dwCredFormat (must be 0) - }; - credentials = cred; - } - Q_ASSERT(credentials != nullptr); - - TimeStamp expiration{}; - auto status = AcquireCredentialsHandle(nullptr, // pszPrincipal (unused) - const_cast<wchar_t *>(UNISP_NAME), // pszPackage - isClient ? SECPKG_CRED_OUTBOUND : SECPKG_CRED_INBOUND, // fCredentialUse - nullptr, // pvLogonID (unused) - credentials, // pAuthData - nullptr, // pGetKeyFn (unused) - nullptr, // pvGetKeyArgument (unused) - &credentialHandle, // phCredential - &expiration // ptsExpir - ); - -#ifdef SUPPORTS_TLS13 - if (supportsTls13()) { - delete static_cast<SCH_CREDENTIALS *>(credentials); - } else -#endif // SUPPORTS_TLS13 - { - delete static_cast<SCHANNEL_CRED *>(credentials); - } - - if (status != SEC_E_OK) { - d->setErrorAndEmit(QAbstractSocket::SslInternalError, schannelErrorToString(status)); - return false; - } - return true; -} - -void TlsCryptographSchannel::deallocateContext() -{ - if (SecIsValidHandle(&contextHandle)) { - DeleteSecurityContext(&contextHandle); - SecInvalidateHandle(&contextHandle); - } -} - -void TlsCryptographSchannel::freeCredentialsHandle() -{ - if (SecIsValidHandle(&credentialHandle)) { - FreeCredentialsHandle(&credentialHandle); - SecInvalidateHandle(&credentialHandle); - } -} - -void TlsCryptographSchannel::closeCertificateStores() -{ - localCertificateStore.reset(); - peerCertificateStore.reset(); - caCertificateStore.reset(); -} - -bool TlsCryptographSchannel::createContext() -{ - Q_ASSERT(q); - Q_ASSERT(d); - const auto &configuration = q->sslConfiguration(); - - Q_ASSERT(SecIsValidHandle(&credentialHandle)); - Q_ASSERT(schannelState == SchannelState::InitializeHandshake); - Q_ASSERT(d->tlsMode() == QSslSocket::SslClientMode); - ULONG contextReq = getContextRequirements(); - - SecBuffer outBuffers[3]; - outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN); - outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT); - outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY); - auto freeBuffers = qScopeGuard([&outBuffers]() { - for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) { - if (outBuffers[i].pvBuffer) - FreeContextBuffer(outBuffers[i].pvBuffer); - } - }); - SecBufferDesc outputBufferDesc{ - SECBUFFER_VERSION, - ARRAYSIZE(outBuffers), - outBuffers - }; - - TimeStamp expiry; - - SecBufferDesc alpnBufferDesc; - bool useAlpn = false; -#ifdef SUPPORTS_ALPN - QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNone); - QByteArray alpnString = createAlpnString(configuration.allowedNextProtocols()); - useAlpn = !alpnString.isEmpty(); - SecBuffer alpnBuffers[1]; - alpnBuffers[0] = createSecBuffer(alpnString, SECBUFFER_APPLICATION_PROTOCOLS); - alpnBufferDesc = { - SECBUFFER_VERSION, - ARRAYSIZE(alpnBuffers), - alpnBuffers - }; -#endif - - auto status = InitializeSecurityContext(&credentialHandle, // phCredential - nullptr, // phContext - const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName - contextReq, // fContextReq - 0, // Reserved1 - 0, // TargetDataRep (unused) - useAlpn ? &alpnBufferDesc : nullptr, // pInput - 0, // Reserved2 - &contextHandle, // phNewContext - &outputBufferDesc, // pOutput - &contextAttributes, // pfContextAttr - &expiry // ptsExpiry - ); - - // This is the first call to InitializeSecurityContext, so theoretically "CONTINUE_NEEDED" - // should be the only non-error return-code here. - if (status != SEC_I_CONTINUE_NEEDED) { - d->setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Error creating SSL context (%1)").arg(schannelErrorToString(status))); - return false; - } - - if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer)) - return false; - schannelState = SchannelState::PerformHandshake; - return true; -} - -bool TlsCryptographSchannel::acceptContext() -{ - Q_ASSERT(d); - Q_ASSERT(q); - const auto &configuration = q->sslConfiguration(); - auto *plainSocket = d->plainTcpSocket(); - - Q_ASSERT(SecIsValidHandle(&credentialHandle)); - Q_ASSERT(schannelState == SchannelState::InitializeHandshake); - Q_ASSERT(d->tlsMode() == QSslSocket::SslServerMode); - ULONG contextReq = getContextRequirements(); - - if (missingData > plainSocket->bytesAvailable()) - return true; - - missingData = 0; - readToBuffer(intermediateBuffer, plainSocket); - if (intermediateBuffer.isEmpty()) - return true; // definitely need more data.. - - SecBuffer inBuffers[2]; - inBuffers[0] = createSecBuffer(intermediateBuffer, SECBUFFER_TOKEN); - -#ifdef SUPPORTS_ALPN - QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNone); - // The string must be alive when we call AcceptSecurityContext - QByteArray alpnString = createAlpnString(configuration.allowedNextProtocols()); - if (!alpnString.isEmpty()) { - inBuffers[1] = createSecBuffer(alpnString, SECBUFFER_APPLICATION_PROTOCOLS); - } else -#endif - { - inBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY); - } - - SecBufferDesc inputBufferDesc{ - SECBUFFER_VERSION, - ARRAYSIZE(inBuffers), - inBuffers - }; - - SecBuffer outBuffers[3]; - outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN); - outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT); - outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY); - auto freeBuffers = qScopeGuard([&outBuffers]() { - for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) { - if (outBuffers[i].pvBuffer) - FreeContextBuffer(outBuffers[i].pvBuffer); - } - }); - SecBufferDesc outputBufferDesc{ - SECBUFFER_VERSION, - ARRAYSIZE(outBuffers), - outBuffers - }; - - TimeStamp expiry; - auto status = AcceptSecurityContext( - &credentialHandle, // phCredential - nullptr, // phContext - &inputBufferDesc, // pInput - contextReq, // fContextReq - 0, // TargetDataRep (unused) - &contextHandle, // phNewContext - &outputBufferDesc, // pOutput - &contextAttributes, // pfContextAttr - &expiry // ptsTimeStamp - ); - - if (status == SEC_E_INCOMPLETE_MESSAGE) { - // Need more data - missingData = checkIncompleteData(outBuffers[0]); - return true; - } - - if (inBuffers[1].BufferType == SECBUFFER_EXTRA) { - // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel - // inBuffers[1].cbBuffer indicates the amount of bytes _NOT_ processed, the rest need to - // be stored. - retainExtraData(intermediateBuffer, inBuffers[1]); - } else { /* No 'extra' data, message not incomplete */ - intermediateBuffer.resize(0); - } - - if (status != SEC_I_CONTINUE_NEEDED) { - d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - QSslSocket::tr("Error creating SSL context (%1)").arg(schannelErrorToString(status))); - return false; - } - if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer)) - return false; - schannelState = SchannelState::PerformHandshake; - return true; -} - -bool TlsCryptographSchannel::performHandshake() -{ - Q_ASSERT(d); - auto *plainSocket = d->plainTcpSocket(); - Q_ASSERT(plainSocket); - - if (plainSocket->state() == QAbstractSocket::UnconnectedState) { - d->setErrorAndEmit(QAbstractSocket::RemoteHostClosedError, - QSslSocket::tr("The TLS/SSL connection has been closed")); - return false; - } - Q_ASSERT(SecIsValidHandle(&credentialHandle)); - Q_ASSERT(SecIsValidHandle(&contextHandle)); - Q_ASSERT(schannelState == SchannelState::PerformHandshake); - -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl, "Bytes available from socket: %lld", plainSocket->bytesAvailable()); - qCDebug(lcSsl, "intermediateBuffer size: %d", intermediateBuffer.size()); -#endif - - if (missingData > plainSocket->bytesAvailable()) - return true; - - missingData = 0; - readToBuffer(intermediateBuffer, plainSocket); - if (intermediateBuffer.isEmpty()) - return true; // no data, will fail - - SecBuffer outBuffers[3] = {}; - const auto freeOutBuffers = [&outBuffers]() { - for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) { - if (outBuffers[i].pvBuffer) - FreeContextBuffer(outBuffers[i].pvBuffer); - } - }; - const auto outBuffersGuard = qScopeGuard(freeOutBuffers); - // For this call to InitializeSecurityContext we may need to call it twice. - // In some cases us not having a certificate isn't actually an error, but just a request. - // With Schannel, to ignore this warning, we need to call InitializeSecurityContext again - // when we get SEC_I_INCOMPLETE_CREDENTIALS! As far as I can tell it's not documented anywhere. - // https://stackoverflow.com/a/47479968/2493610 - SECURITY_STATUS status; - short attempts = 2; - do { - SecBuffer inputBuffers[2]; - inputBuffers[0] = createSecBuffer(intermediateBuffer, SECBUFFER_TOKEN); - inputBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY); - SecBufferDesc inputBufferDesc{ - SECBUFFER_VERSION, - ARRAYSIZE(inputBuffers), - inputBuffers - }; - - freeOutBuffers(); // free buffers from any previous attempt - outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN); - outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT); - outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY); - SecBufferDesc outputBufferDesc{ - SECBUFFER_VERSION, - ARRAYSIZE(outBuffers), - outBuffers - }; - - ULONG contextReq = getContextRequirements(); - TimeStamp expiry; - status = InitializeSecurityContext( - &credentialHandle, // phCredential - &contextHandle, // phContext - const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName - contextReq, // fContextReq - 0, // Reserved1 - 0, // TargetDataRep (unused) - &inputBufferDesc, // pInput - 0, // Reserved2 - nullptr, // phNewContext (we already have one) - &outputBufferDesc, // pOutput - &contextAttributes, // pfContextAttr - &expiry // ptsExpiry - ); - - if (inputBuffers[1].BufferType == SECBUFFER_EXTRA) { - // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel - // inputBuffers[1].cbBuffer indicates the amount of bytes _NOT_ processed, the rest need - // to be stored. - retainExtraData(intermediateBuffer, inputBuffers[1]); - } else if (status != SEC_E_INCOMPLETE_MESSAGE) { - // Clear the buffer if we weren't asked for more data - intermediateBuffer.resize(0); - } - - --attempts; - } while (status == SEC_I_INCOMPLETE_CREDENTIALS && attempts > 0); - - switch (status) { - case SEC_E_OK: - // Need to transmit a final token in the handshake if 'cbBuffer' is non-zero. - if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer)) - return false; - schannelState = SchannelState::VerifyHandshake; - return true; - case SEC_I_CONTINUE_NEEDED: - if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer)) - return false; - // Must call InitializeSecurityContext again later (done through continueHandshake) - return true; - case SEC_I_INCOMPLETE_CREDENTIALS: - // Schannel takes care of picking certificate to send (other than the one we can specify), - // so if we get here then that means we don't have a certificate the server accepts. - d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - QSslSocket::tr("Server did not accept any certificate we could present.")); - return false; - case SEC_I_CONTEXT_EXPIRED: - // "The message sender has finished using the connection and has initiated a shutdown." - if (outBuffers[0].BufferType == SECBUFFER_TOKEN) { - if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer)) - return false; - } - if (!shutdown) { // we did not initiate this - d->setErrorAndEmit(QAbstractSocket::RemoteHostClosedError, - QSslSocket::tr("The TLS/SSL connection has been closed")); - } - return true; - case SEC_E_INCOMPLETE_MESSAGE: - // Simply incomplete, wait for more data - missingData = checkIncompleteData(outBuffers[0]); - return true; - case SEC_E_ALGORITHM_MISMATCH: - d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - QSslSocket::tr("Algorithm mismatch")); - shutdown = true; // skip sending the "Shutdown" alert - return false; - } - - // Note: We can get here if the connection is using TLS 1.2 and the server certificate uses - // MD5, which is not allowed in Schannel. This causes an "invalid token" error during handshake. - // (If you came here investigating an error: md5 is insecure, update your certificate) - d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - QSslSocket::tr("Handshake failed: %1").arg(schannelErrorToString(status))); - return false; -} - -bool TlsCryptographSchannel::verifyHandshake() -{ - Q_ASSERT(d); - Q_ASSERT(q); - const auto &configuration = q->sslConfiguration(); - - sslErrors.clear(); - - const bool isClient = d->tlsMode() == QSslSocket::SslClientMode; -#define CHECK_STATUS(status) \ - if (status != SEC_E_OK) { \ - d->setErrorAndEmit(QAbstractSocket::SslInternalError, \ - QSslSocket::tr("Failed to query the TLS context: %1") \ - .arg(schannelErrorToString(status))); \ - return false; \ - } - - // Everything is set up, now make sure there's nothing wrong and query some attributes... - if (!matchesContextRequirements(contextAttributes, getContextRequirements(), - configuration.peerVerifyMode(), isClient)) { - d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - QSslSocket::tr("Did not get the required attributes for the connection.")); - return false; - } - - // Get stream sizes (to know the max size of a message and the size of the header and trailer) - auto status = QueryContextAttributes(&contextHandle, - SECPKG_ATTR_STREAM_SIZES, - &streamSizes); - CHECK_STATUS(status); - - // Get session cipher info - status = QueryContextAttributes(&contextHandle, - SECPKG_ATTR_CONNECTION_INFO, - &connectionInfo); - CHECK_STATUS(status); - -#ifdef SUPPORTS_ALPN - const auto allowedProtos = configuration.allowedNextProtocols(); - if (!allowedProtos.isEmpty() && supportsAlpn()) { - SecPkgContext_ApplicationProtocol alpn; - status = QueryContextAttributes(&contextHandle, - SECPKG_ATTR_APPLICATION_PROTOCOL, - &alpn); - CHECK_STATUS(status); - if (alpn.ProtoNegoStatus == SecApplicationProtocolNegotiationStatus_Success) { - QByteArray negotiatedProto = QByteArray((const char *)alpn.ProtocolId, - alpn.ProtocolIdSize); - if (!allowedProtos.contains(negotiatedProto)) { - d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - QSslSocket::tr("Unwanted protocol was negotiated")); - return false; - } - QTlsBackend::setNegotiatedProtocol(d, negotiatedProto); - QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNegotiated); - } else { - QTlsBackend::setNegotiatedProtocol(d, {}); - QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationUnsupported); - } - } -#endif // supports ALPN - -#undef CHECK_STATUS - - // Verify certificate - CERT_CONTEXT *certificateContext = nullptr; - auto freeCertificate = qScopeGuard([&certificateContext]() { - if (certificateContext) - CertFreeCertificateContext(certificateContext); - }); - status = QueryContextAttributes(&contextHandle, - SECPKG_ATTR_REMOTE_CERT_CONTEXT, - &certificateContext); - - // QueryPeer can (currently) not work in Schannel since Schannel itself doesn't have a way to - // ask for a certificate and then still be OK if it's not received. - // To work around this we don't request a certificate at all for QueryPeer. - // For servers AutoVerifyPeer is supposed to be treated the same as QueryPeer. - // This means that servers using Schannel will only request client certificate for "VerifyPeer". - if ((!isClient && configuration.peerVerifyMode() == QSslSocket::PeerVerifyMode::VerifyPeer) - || (isClient && configuration.peerVerifyMode() != QSslSocket::PeerVerifyMode::VerifyNone - && configuration.peerVerifyMode() != QSslSocket::PeerVerifyMode::QueryPeer)) { - if (status != SEC_E_OK) { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << "Couldn't retrieve peer certificate, status:" - << schannelErrorToString(status); -#endif - const QSslError error{ QSslError::NoPeerCertificate }; - sslErrors += error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - } - - // verifyCertContext returns false if the user disconnected while it was checking errors. - if (certificateContext && !verifyCertContext(certificateContext)) - return false; - - if (!checkSslErrors() || q->state() != QAbstractSocket::ConnectedState) { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << __func__ << "was unsuccessful. Paused:" << paused; -#endif - // If we're paused then checkSslErrors returned false, but it's not an error - return d->isPaused() && q->state() == QAbstractSocket::ConnectedState; - } - - schannelState = SchannelState::Done; - return true; -} - -bool TlsCryptographSchannel::renegotiate() -{ - Q_ASSERT(d); - - SecBuffer outBuffers[3]; - outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN); - outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT); - outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY); - auto freeBuffers = qScopeGuard([&outBuffers]() { - for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) { - if (outBuffers[i].pvBuffer) - FreeContextBuffer(outBuffers[i].pvBuffer); - } - }); - SecBufferDesc outputBufferDesc{ - SECBUFFER_VERSION, - ARRAYSIZE(outBuffers), - outBuffers - }; - - ULONG contextReq = getContextRequirements(); - TimeStamp expiry; - SECURITY_STATUS status; - if (d->tlsMode() == QSslSocket::SslClientMode) { - status = InitializeSecurityContext(&credentialHandle, // phCredential - &contextHandle, // phContext - const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName - contextReq, // fContextReq - 0, // Reserved1 - 0, // TargetDataRep (unused) - nullptr, // pInput (nullptr for renegotiate) - 0, // Reserved2 - nullptr, // phNewContext (we already have one) - &outputBufferDesc, // pOutput - &contextAttributes, // pfContextAttr - &expiry // ptsExpiry - ); - } else { - status = AcceptSecurityContext( - &credentialHandle, // phCredential - &contextHandle, // phContext - nullptr, // pInput - contextReq, // fContextReq - 0, // TargetDataRep (unused) - nullptr, // phNewContext - &outputBufferDesc, // pOutput - &contextAttributes, // pfContextAttr, - &expiry // ptsTimeStamp - ); - } - if (status == SEC_I_CONTINUE_NEEDED) { - schannelState = SchannelState::PerformHandshake; - return sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer); - } else if (status == SEC_E_OK) { - schannelState = SchannelState::PerformHandshake; - return true; - } - d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - QSslSocket::tr("Renegotiation was unsuccessful: %1").arg(schannelErrorToString(status))); - return false; -} - -/*! - \internal - reset the state in preparation for reuse of socket -*/ -void TlsCryptographSchannel::reset() -{ - Q_ASSERT(d); - - closeCertificateStores(); // certificate stores could've changed - deallocateContext(); - freeCredentialsHandle(); // in case we already had one (@future: session resumption requires re-use) - - connectionInfo = {}; - streamSizes = {}; - - CertFreeCertificateContext(localCertContext); - localCertContext = nullptr; - - contextAttributes = 0; - intermediateBuffer.clear(); - schannelState = SchannelState::InitializeHandshake; - - - d->setEncrypted(false); - shutdown = false; - renegotiating = false; - - missingData = 0; -} - -void TlsCryptographSchannel::startClientEncryption() -{ - Q_ASSERT(q); - - if (q->isEncrypted()) - return; // let's not mess up the connection... - reset(); - continueHandshake(); -} - -void TlsCryptographSchannel::startServerEncryption() -{ - Q_ASSERT(q); - - if (q->isEncrypted()) - return; // let's not mess up the connection... - reset(); - continueHandshake(); -} - -void TlsCryptographSchannel::transmit() -{ - Q_ASSERT(q); - Q_ASSERT(d); - auto *plainSocket = d->plainTcpSocket(); - Q_ASSERT(plainSocket); - - if (d->tlsMode() == QSslSocket::UnencryptedMode) - return; // This function should not have been called - - // Can happen if called through QSslSocket::abort->QSslSocket::close->QSslSocket::flush->here - if (plainSocket->state() == QAbstractSocket::SocketState::UnconnectedState) - return; - - if (schannelState != SchannelState::Done) { - continueHandshake(); - return; - } - - auto &writeBuffer = d->tlsWriteBuffer(); - auto &buffer = d->tlsBuffer(); - if (q->isEncrypted()) { // encrypt data in writeBuffer and write it to plainSocket - qint64 totalBytesWritten = 0; - qint64 writeBufferSize; - while ((writeBufferSize = writeBuffer.size()) > 0) { - const int headerSize = int(streamSizes.cbHeader); - const int trailerSize = int(streamSizes.cbTrailer); - // Try to read 'cbMaximumMessage' bytes from buffer before encrypting. - const int size = int(std::min(writeBufferSize, qint64(streamSizes.cbMaximumMessage))); - QByteArray fullMessage(headerSize + trailerSize + size, Qt::Uninitialized); - { - // Use peek() here instead of read() so we don't lose data if encryption fails. - qint64 copied = writeBuffer.peek(fullMessage.data() + headerSize, size); - Q_ASSERT(copied == size); - } - - SecBuffer inputBuffers[4]{ - createSecBuffer(fullMessage.data(), headerSize, SECBUFFER_STREAM_HEADER), - createSecBuffer(fullMessage.data() + headerSize, size, SECBUFFER_DATA), - createSecBuffer(fullMessage.data() + headerSize + size, trailerSize, SECBUFFER_STREAM_TRAILER), - createSecBuffer(nullptr, 0, SECBUFFER_EMPTY) - }; - SecBufferDesc message{ - SECBUFFER_VERSION, - ARRAYSIZE(inputBuffers), - inputBuffers - }; - auto status = EncryptMessage(&contextHandle, 0, &message, 0); - if (status != SEC_E_OK) { - d->setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Schannel failed to encrypt data: %1") - .arg(schannelErrorToString(status))); - return; - } - // Data was encrypted successfully, so we free() what we peek()ed earlier - writeBuffer.free(size); - - // The trailer's size is not final, so resize fullMessage to not send trailing junk - fullMessage.resize(inputBuffers[0].cbBuffer + inputBuffers[1].cbBuffer + inputBuffers[2].cbBuffer); - const qint64 bytesWritten = plainSocket->write(fullMessage); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl, "Wrote %lld of total %d bytes", bytesWritten, fullMessage.length()); -#endif - if (bytesWritten >= 0) { - totalBytesWritten += bytesWritten; - } else { - d->setErrorAndEmit(plainSocket->error(), plainSocket->errorString()); - return; - } - } - - if (totalBytesWritten > 0) { - // Don't emit bytesWritten() recursively. - bool &emittedBytesWritten = d->tlsEmittedBytesWritten(); - if (!emittedBytesWritten) { - emittedBytesWritten = true; - emit q->bytesWritten(totalBytesWritten); - emittedBytesWritten = false; - } - emit q->channelBytesWritten(0, totalBytesWritten); - } - } - - if (q->isEncrypted()) { // Decrypt data from remote - int totalRead = 0; - bool hadIncompleteData = false; - const auto readBufferMaxSize = d->maxReadBufferSize(); - while (!readBufferMaxSize || buffer.size() < readBufferMaxSize) { - if (missingData > plainSocket->bytesAvailable() - && (!readBufferMaxSize || readBufferMaxSize >= missingData)) { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl, "We're still missing %lld bytes, will check later.", missingData); -#endif - break; - } - - missingData = 0; - const qint64 bytesRead = readToBuffer(intermediateBuffer, plainSocket); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl, "Read %lld encrypted bytes from the socket", bytesRead); -#endif - if (intermediateBuffer.length() == 0 || (hadIncompleteData && bytesRead == 0)) { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl, (hadIncompleteData ? "No new data received, leaving loop!" - : "Nothing to decrypt, leaving loop!")); -#endif - break; - } - hadIncompleteData = false; -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl, "Total amount of bytes to decrypt: %d", intermediateBuffer.length()); -#endif - - SecBuffer dataBuffer[4]{ - createSecBuffer(intermediateBuffer, SECBUFFER_DATA), - createSecBuffer(nullptr, 0, SECBUFFER_EMPTY), - createSecBuffer(nullptr, 0, SECBUFFER_EMPTY), - createSecBuffer(nullptr, 0, SECBUFFER_EMPTY) - }; - SecBufferDesc message{ - SECBUFFER_VERSION, - ARRAYSIZE(dataBuffer), - dataBuffer - }; - auto status = DecryptMessage(&contextHandle, &message, 0, nullptr); - if (status == SEC_E_OK || status == SEC_I_RENEGOTIATE || status == SEC_I_CONTEXT_EXPIRED) { - // There can still be 0 output even if it succeeds, this is fine - if (dataBuffer[1].cbBuffer > 0) { - // It is always decrypted in-place. - // But [0] is the STREAM_HEADER, [1] is the DATA and [2] is the STREAM_TRAILER. - // The pointers in all of those still point into 'intermediateBuffer'. - buffer.append(static_cast<char *>(dataBuffer[1].pvBuffer), - dataBuffer[1].cbBuffer); - totalRead += dataBuffer[1].cbBuffer; -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl, "Decrypted %lu bytes. New read buffer size: %d", - dataBuffer[1].cbBuffer, buffer.size()); -#endif - } - if (dataBuffer[3].BufferType == SECBUFFER_EXTRA) { - // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel - // dataBuffer[3].cbBuffer indicates the amount of bytes _NOT_ processed, - // the rest need to be stored. - retainExtraData(intermediateBuffer, dataBuffer[3]); - } else { - intermediateBuffer.resize(0); - } - } - - if (status == SEC_E_INCOMPLETE_MESSAGE) { - missingData = checkIncompleteData(dataBuffer[0]); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl, "We didn't have enough data to decrypt anything, will try again!"); -#endif - // We try again, but if we don't get any more data then we leave - hadIncompleteData = true; - } else if (status == SEC_E_INVALID_HANDLE) { - // I don't think this should happen, if it does we're done... - qCWarning(lcSsl, "The internal SSPI handle is invalid!"); - Q_UNREACHABLE(); - } else if (status == SEC_E_INVALID_TOKEN) { - qCWarning(lcSsl, "Got SEC_E_INVALID_TOKEN!"); - Q_UNREACHABLE(); // Happened once due to a bug, but shouldn't generally happen(?) - } else if (status == SEC_E_MESSAGE_ALTERED) { - // The message has been altered, disconnect now. - shutdown = true; // skips sending the shutdown alert - disconnectFromHost(); - d->setErrorAndEmit(QAbstractSocket::SslInternalError, - schannelErrorToString(status)); - break; - } else if (status == SEC_E_OUT_OF_SEQUENCE) { - // @todo: I don't know if this one is actually "fatal".. - // This path might never be hit as it seems this is for connection-oriented connections, - // while SEC_E_MESSAGE_ALTERED is for stream-oriented ones (what we use). - shutdown = true; // skips sending the shutdown alert - disconnectFromHost(); - d->setErrorAndEmit(QAbstractSocket::SslInternalError, - schannelErrorToString(status)); - break; - } else if (status == SEC_I_CONTEXT_EXPIRED) { - // 'remote' has initiated a shutdown - disconnectFromHost(); - d->setErrorAndEmit(QAbstractSocket::RemoteHostClosedError, - schannelErrorToString(status)); - break; - } else if (status == SEC_I_RENEGOTIATE) { - // 'remote' wants to renegotiate -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl, "The peer wants to renegotiate."); -#endif - schannelState = SchannelState::Renegotiate; - renegotiating = true; - - // We need to call 'continueHandshake' or else there's no guarantee it ever gets called - continueHandshake(); - break; - } - } - - if (totalRead) { - if (bool *readyReadEmittedPointer = d->readyReadPointer()) - *readyReadEmittedPointer = true; - emit q->readyRead(); - emit q->channelReadyRead(0); - } - } -} - -void TlsCryptographSchannel::sendShutdown() -{ - Q_ASSERT(d); - - const bool isClient = d->tlsMode() == QSslSocket::SslClientMode; - DWORD shutdownToken = SCHANNEL_SHUTDOWN; - SecBuffer buffer = createSecBuffer(&shutdownToken, sizeof(DWORD), SECBUFFER_TOKEN); - SecBufferDesc token{ - SECBUFFER_VERSION, - 1, - &buffer - }; - auto status = ApplyControlToken(&contextHandle, &token); - - if (status != SEC_E_OK) { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << "Failed to apply shutdown control token:" << schannelErrorToString(status); -#endif - return; - } - - SecBuffer outBuffers[3]; - outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN); - outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT); - outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY); - auto freeBuffers = qScopeGuard([&outBuffers]() { - for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) { - if (outBuffers[i].pvBuffer) - FreeContextBuffer(outBuffers[i].pvBuffer); - } - }); - SecBufferDesc outputBufferDesc{ - SECBUFFER_VERSION, - ARRAYSIZE(outBuffers), - outBuffers - }; - - ULONG contextReq = getContextRequirements(); - TimeStamp expiry; - if (isClient) { - status = InitializeSecurityContext(&credentialHandle, // phCredential - &contextHandle, // phContext - const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName - contextReq, // fContextReq - 0, // Reserved1 - 0, // TargetDataRep (unused) - nullptr, // pInput - 0, // Reserved2 - nullptr, // phNewContext (we already have one) - &outputBufferDesc, // pOutput - &contextAttributes, // pfContextAttr - &expiry // ptsExpiry - ); - } else { - status = AcceptSecurityContext( - &credentialHandle, // phCredential - &contextHandle, // phContext - nullptr, // pInput - contextReq, // fContextReq - 0, // TargetDataRep (unused) - nullptr, // phNewContext - &outputBufferDesc, // pOutput - &contextAttributes, // pfContextAttr, - &expiry // ptsTimeStamp - ); - } - if (status == SEC_E_OK || status == SEC_I_CONTEXT_EXPIRED) { - if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, false)) { - // We failed to send the shutdown message, but it's not that important since we're - // shutting down anyway. - return; - } - } else { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << "Failed to initialize shutdown:" << schannelErrorToString(status); -#endif - } -} - -void TlsCryptographSchannel::disconnectFromHost() -{ - Q_ASSERT(q); - Q_ASSERT(d); - auto *plainSocket = d->plainTcpSocket(); - Q_ASSERT(plainSocket); - - if (SecIsValidHandle(&contextHandle)) { - if (!shutdown) { - shutdown = true; - if (plainSocket->state() != QAbstractSocket::UnconnectedState) { - if (q->isEncrypted()) { - // Read as much as possible because this is likely our last chance - qint64 tempMax = d->maxReadBufferSize(); - d->setMaxReadBufferSize(0); - transmit(); - d->setMaxReadBufferSize(tempMax); - sendShutdown(); - } - } - } - } - if (plainSocket->state() != QAbstractSocket::UnconnectedState) - plainSocket->disconnectFromHost(); -} - -void TlsCryptographSchannel::disconnected() -{ - Q_ASSERT(d); - - shutdown = true; - d->setEncrypted(false); - deallocateContext(); - freeCredentialsHandle(); -} - -QSslCipher TlsCryptographSchannel::sessionCipher() const -{ - Q_ASSERT(q); - - if (!q->isEncrypted()) - return QSslCipher(); - return QSslCipher(QStringLiteral("Schannel"), sessionProtocol()); -} - -QSsl::SslProtocol TlsCryptographSchannel::sessionProtocol() const -{ - if (!q->isEncrypted()) - return QSsl::SslProtocol::UnknownProtocol; - return toQtSslProtocol(connectionInfo.dwProtocol); -} - -void TlsCryptographSchannel::continueHandshake() -{ - Q_ASSERT(q); - Q_ASSERT(d); - auto *plainSocket = d->plainTcpSocket(); - Q_ASSERT(plainSocket); - - const bool isServer = d->tlsMode() == QSslSocket::SslServerMode; - switch (schannelState) { - case SchannelState::InitializeHandshake: - if (!SecIsValidHandle(&credentialHandle) && !acquireCredentialsHandle()) { - disconnectFromHost(); - return; - } - if (!SecIsValidHandle(&credentialHandle)) // Needed to support tst_QSslSocket::setEmptyKey - return; - if (!SecIsValidHandle(&contextHandle) && !(isServer ? acceptContext() : createContext())) { - disconnectFromHost(); - return; - } - if (schannelState != SchannelState::PerformHandshake) - break; - Q_FALLTHROUGH(); - case SchannelState::PerformHandshake: - if (!performHandshake()) { - disconnectFromHost(); - return; - } - if (schannelState != SchannelState::VerifyHandshake) - break; - Q_FALLTHROUGH(); - case SchannelState::VerifyHandshake: - // if we're in shutdown or renegotiating then we might not need to verify - // (since we already did) - if (!verifyHandshake()) { - shutdown = true; // Skip sending shutdown alert - q->abort(); // We don't want to send buffered data - disconnectFromHost(); - return; - } - if (schannelState != SchannelState::Done) - break; - Q_FALLTHROUGH(); - case SchannelState::Done: - // connectionEncrypted is already true if we come here from a renegotiation - if (!q->isEncrypted()) { - d->setEncrypted(true); // all is done - emit q->encrypted(); - } - renegotiating = false; - if (d->isPendingClose()) { - d->setPendingClose(false); - disconnectFromHost(); - } else { - transmit(); - } - break; - case SchannelState::Renegotiate: - if (!renegotiate()) { - disconnectFromHost(); - return; - } else if (intermediateBuffer.size() || plainSocket->bytesAvailable()) { - continueHandshake(); - } - break; - } -} - -QList<QSslError> TlsCryptographSchannel::tlsErrors() const -{ - return sslErrors; -} - -/* - Copied from qsslsocket_mac.cpp, which was copied from qsslsocket_openssl.cpp -*/ -bool TlsCryptographSchannel::checkSslErrors() -{ - if (sslErrors.isEmpty()) - return true; - - Q_ASSERT(q); - Q_ASSERT(d); - const auto &configuration = q->sslConfiguration(); - auto *plainSocket = d->plainTcpSocket(); - - emit q->sslErrors(sslErrors); - - const bool doVerifyPeer = configuration.peerVerifyMode() == QSslSocket::VerifyPeer - || (configuration.peerVerifyMode() == QSslSocket::AutoVerifyPeer - && d->tlsMode() == QSslSocket::SslClientMode); - const bool doEmitSslError = !d->verifyErrorsHaveBeenIgnored(); - // check whether we need to emit an SSL handshake error - if (doVerifyPeer && doEmitSslError) { - if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) { - QSslSocketPrivate::pauseSocketNotifiers(q); - d->setPaused(true); - } else { - d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - sslErrors.constFirst().errorString()); - plainSocket->disconnectFromHost(); - } - return false; - } - - return true; -} - -void TlsCryptographSchannel::initializeCertificateStores() -{ - //// helper function which turns a chain into a certificate store - Q_ASSERT(d); - Q_ASSERT(q); - const auto &configuration = q->sslConfiguration(); - - auto createStoreFromCertificateChain = [](const QList<QSslCertificate> certChain, const QSslKey &privateKey) { - const wchar_t *passphrase = L""; - // Need to embed the private key in the certificate - QByteArray pkcs12 = _q_makePkcs12(certChain, - privateKey, - QString::fromWCharArray(passphrase, 0)); - CRYPT_DATA_BLOB pfxBlob; - pfxBlob.cbData = DWORD(pkcs12.length()); - pfxBlob.pbData = reinterpret_cast<unsigned char *>(pkcs12.data()); - return QHCertStorePointer(PFXImportCertStore(&pfxBlob, passphrase, 0)); - }; - - if (!configuration.localCertificateChain().isEmpty()) { - if (configuration.privateKey().isNull()) { - d->setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError, - QSslSocket::tr("Cannot provide a certificate with no key")); - return; - } - if (localCertificateStore == nullptr) { - localCertificateStore = createStoreFromCertificateChain(configuration.localCertificateChain(), - configuration.privateKey()); - if (localCertificateStore == nullptr) - qCWarning(lcSsl, "Failed to load certificate chain!"); - } - } - - if (!configuration.caCertificates().isEmpty() && !caCertificateStore) { - caCertificateStore = createStoreFromCertificateChain(configuration.caCertificates(), - {}); // No private key for the CA certs - } -} - -bool TlsCryptographSchannel::verifyCertContext(CERT_CONTEXT *certContext) -{ - Q_ASSERT(certContext); - Q_ASSERT(q); - Q_ASSERT(d); - - const bool isClient = d->tlsMode() == QSslSocket::SslClientMode; - - // Create a collection of stores so we can pass in multiple stores as additional locations to - // search for the certificate chain - auto tempCertCollection = QHCertStorePointer(CertOpenStore(CERT_STORE_PROV_COLLECTION, - X509_ASN_ENCODING, - 0, - CERT_STORE_CREATE_NEW_FLAG, - nullptr)); - if (!tempCertCollection) { -#ifdef QSSLSOCKET_DEBUG - qCWarning(lcSsl, "Failed to create certificate store collection!"); -#endif - return false; - } - - if (rootCertOnDemandLoadingAllowed()) { - // @future(maybe): following the OpenSSL backend these certificates should be added into - // the Ca list, not just included during verification. - // That being said, it's not trivial to add the root certificates (if and only if they - // came from the system root store). And I don't see this mentioned in our documentation. - auto rootStore = QHCertStorePointer(CertOpenSystemStore(0, L"ROOT")); - if (!rootStore) { -#ifdef QSSLSOCKET_DEBUG - qCWarning(lcSsl, "Failed to open the system root CA certificate store!"); -#endif - return false; - } else if (!CertAddStoreToCollection(tempCertCollection.get(), rootStore.get(), 0, 1)) { -#ifdef QSSLSOCKET_DEBUG - qCWarning(lcSsl, "Failed to add the system root CA certificate store to the certificate store collection!"); -#endif - return false; - } - } - if (caCertificateStore) { - if (!CertAddStoreToCollection(tempCertCollection.get(), caCertificateStore.get(), 0, 1)) { -#ifdef QSSLSOCKET_DEBUG - qCWarning(lcSsl, "Failed to add the user's CA certificate store to the certificate store collection!"); -#endif - return false; - } - } - - if (!CertAddStoreToCollection(tempCertCollection.get(), certContext->hCertStore, 0, 0)) { -#ifdef QSSLSOCKET_DEBUG - qCWarning(lcSsl, "Failed to add certificate's origin store to the certificate store collection!"); -#endif - return false; - } - - CERT_CHAIN_PARA parameters; - ZeroMemory(¶meters, sizeof(parameters)); - parameters.cbSize = sizeof(CERT_CHAIN_PARA); - parameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND; - parameters.RequestedUsage.Usage.cUsageIdentifier = 1; - LPSTR oid = LPSTR(isClient ? szOID_PKIX_KP_SERVER_AUTH - : szOID_PKIX_KP_CLIENT_AUTH); - parameters.RequestedUsage.Usage.rgpszUsageIdentifier = &oid; - - QTlsBackend::clearPeerCertificates(d); - const CERT_CHAIN_CONTEXT *chainContext = nullptr; - auto freeCertChain = qScopeGuard([&chainContext]() { - if (chainContext) - CertFreeCertificateChain(chainContext); - }); - BOOL status = CertGetCertificateChain(nullptr, // hChainEngine, default - certContext, // pCertContext - nullptr, // pTime, 'now' - tempCertCollection.get(), // hAdditionalStore, additional cert store - ¶meters, // pChainPara - CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, // dwFlags - nullptr, // reserved - &chainContext // ppChainContext - ); - if (status == FALSE || !chainContext || chainContext->cChain == 0) { - QSslError error(QSslError::UnableToVerifyFirstCertificate); - sslErrors += error; - emit q->peerVerifyError(error); - return q->state() == QAbstractSocket::ConnectedState; - } - - // Helper-function to get a QSslCertificate given a CERT_CHAIN_ELEMENT - static auto getCertificateFromChainElement = [](CERT_CHAIN_ELEMENT *element) { - if (!element) - return QSslCertificate(); - - const CERT_CONTEXT *certContext = element->pCertContext; - return QTlsPrivate::X509CertificateSchannel::QSslCertificate_from_CERT_CONTEXT(certContext); - }; - - // Pick a chain to use as the certificate chain, if multiple are available: - // According to https://docs.microsoft.com/en-gb/windows/desktop/api/wincrypt/ns-wincrypt-_cert_chain_context - // this seems to be the best way to get a trusted chain. - CERT_SIMPLE_CHAIN *chain = chainContext->rgpChain[chainContext->cChain - 1]; - - if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN) { - auto error = QSslError(QSslError::SslError::UnableToGetIssuerCertificate, - getCertificateFromChainElement(chain->rgpElement[chain->cElement - 1])); - sslErrors += error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_INVALID_BASIC_CONSTRAINTS) { - // @Note: This is actually one of two errors: - // "either the certificate cannot be used to issue other certificates, or the chain path length has been exceeded." - // But here we are checking the chain's status, so we assume the "issuing" error cannot occur here. - auto error = QSslError(QSslError::PathLengthExceeded); - sslErrors += error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - static const DWORD leftoverCertChainErrorMask = CERT_TRUST_IS_CYCLIC | CERT_TRUST_INVALID_EXTENSION - | CERT_TRUST_INVALID_POLICY_CONSTRAINTS | CERT_TRUST_INVALID_NAME_CONSTRAINTS - | CERT_TRUST_CTL_IS_NOT_TIME_VALID | CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID - | CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE; - if (chain->TrustStatus.dwErrorStatus & leftoverCertChainErrorMask) { - auto error = QSslError(QSslError::SslError::UnspecifiedError); - sslErrors += error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - - DWORD verifyDepth = chain->cElement; - if (q->peerVerifyDepth() > 0 && DWORD(q->peerVerifyDepth()) < verifyDepth) - verifyDepth = DWORD(q->peerVerifyDepth()); - - const auto &caCertificates = q->sslConfiguration().caCertificates(); - QList<QSslCertificate> peerCertificateChain; - for (DWORD i = 0; i < verifyDepth; i++) { - CERT_CHAIN_ELEMENT *element = chain->rgpElement[i]; - QSslCertificate certificate = getCertificateFromChainElement(element); - const QList<QSslCertificateExtension> extensions = certificate.extensions(); - -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << "issuer:" << certificate.issuerDisplayName() - << "\nsubject:" << certificate.subjectDisplayName() - << "\nQSslCertificate info:" << certificate - << "\nextended error info:" << element->pwszExtendedErrorInfo - << "\nerror status:" << element->TrustStatus.dwErrorStatus; -#endif - - peerCertificateChain.append(certificate); - QTlsBackend::storePeerCertificateChain(d, peerCertificateChain); - - if (certificate.isBlacklisted()) { - const auto error = QSslError(QSslError::CertificateBlacklisted, certificate); - sslErrors += error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - - LONG result = CertVerifyTimeValidity(nullptr /*== now */, element->pCertContext->pCertInfo); - if (result != 0) { - auto error = QSslError(result == -1 ? QSslError::CertificateNotYetValid - : QSslError::CertificateExpired, - certificate); - sslErrors += error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - - //// Errors - if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_TIME_VALID) { - // handled right above - Q_ASSERT(!sslErrors.isEmpty()); - } - if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED) { - auto error = QSslError(QSslError::CertificateRevoked, certificate); - sslErrors += error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_SIGNATURE_VALID) { - auto error = QSslError(QSslError::CertificateSignatureFailed, certificate); - sslErrors += error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - - // While netscape shouldn't be relevant now it defined an extension which is - // still in use. Schannel does not check this automatically, so we do it here. - // It is used to differentiate between client and server certificates. - if (netscapeWrongCertType(extensions, isClient)) - element->TrustStatus.dwErrorStatus |= CERT_TRUST_IS_NOT_VALID_FOR_USAGE; - - if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_VALID_FOR_USAGE) { - auto error = QSslError(QSslError::InvalidPurpose, certificate); - sslErrors += error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_UNTRUSTED_ROOT) { - // Override this error if we have the certificate inside our trusted CAs list. - const bool isTrustedRoot = caCertificates.contains(certificate); - if (!isTrustedRoot) { - auto error = QSslError(QSslError::CertificateUntrusted, certificate); - sslErrors += error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - } - static const DWORD certRevocationCheckUnavailableError = CERT_TRUST_IS_OFFLINE_REVOCATION - | CERT_TRUST_REVOCATION_STATUS_UNKNOWN; - if (element->TrustStatus.dwErrorStatus & certRevocationCheckUnavailableError) { - // @future(maybe): Do something with this - } - - // Dumping ground of errors that don't fit our specific errors - static const DWORD leftoverCertErrorMask = CERT_TRUST_IS_CYCLIC - | CERT_TRUST_INVALID_EXTENSION | CERT_TRUST_INVALID_NAME_CONSTRAINTS - | CERT_TRUST_INVALID_POLICY_CONSTRAINTS - | CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT - | CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT - | CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT - | CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT - | CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT; - if (element->TrustStatus.dwErrorStatus & leftoverCertErrorMask) { - auto error = QSslError(QSslError::UnspecifiedError, certificate); - sslErrors += error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - if (element->TrustStatus.dwErrorStatus & CERT_TRUST_INVALID_BASIC_CONSTRAINTS) { - auto it = std::find_if(extensions.cbegin(), extensions.cend(), - [](const QSslCertificateExtension &extension) { - return extension.name() == QLatin1String("basicConstraints"); - }); - if (it != extensions.cend()) { - // @Note: This is actually one of two errors: - // "either the certificate cannot be used to issue other certificates, - // or the chain path length has been exceeded." - QVariantMap basicConstraints = it->value().toMap(); - QSslError error; - if (i > 0 && !basicConstraints.value(QLatin1String("ca"), false).toBool()) - error = QSslError(QSslError::InvalidPurpose, certificate); - else - error = QSslError(QSslError::PathLengthExceeded, certificate); - sslErrors += error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - } - if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_EXPLICIT_DISTRUST) { - auto error = QSslError(QSslError::CertificateBlacklisted, certificate); - sslErrors += error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - - if (element->TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED) { - // If it's self-signed *and* a CA then we can assume it's a root CA certificate - // and we can ignore the "self-signed" note: - // We check the basicConstraints certificate extension when possible, but this didn't - // exist for version 1, so we can only guess in that case - const bool isRootCertificateAuthority = isCertificateAuthority(extensions) - || certificate.version() == "1"; - - // Root certificate tends to be signed by themselves, so ignore self-signed status. - if (!isRootCertificateAuthority) { - auto error = QSslError(QSslError::SelfSignedCertificate, certificate); - sslErrors += error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - } - } - - if (!peerCertificateChain.isEmpty()) - QTlsBackend::storePeerCertificate(d, peerCertificateChain.first()); - - const auto &configuration = q->sslConfiguration(); // Probably, updated by QTlsBackend::storePeerCertificate etc. - // @Note: Somewhat copied from qsslsocket_mac.cpp - const bool doVerifyPeer = q->peerVerifyMode() == QSslSocket::VerifyPeer - || (q->peerVerifyMode() == QSslSocket::AutoVerifyPeer - && d->tlsMode() == QSslSocket::SslClientMode); - // Check the peer certificate itself. First try the subject's common name - // (CN) as a wildcard, then try all alternate subject name DNS entries the - // same way. - if (!configuration.peerCertificate().isNull()) { - // but only if we're a client connecting to a server - // if we're the server, don't check CN - if (d->tlsMode() == QSslSocket::SslClientMode) { - const auto verificationPeerName = d->verificationName(); - const QString peerName(verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName); - if (!isMatchingHostname(configuration.peerCertificate(), peerName)) { - // No matches in common names or alternate names. - const QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate()); - sslErrors += error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - } - } else if (doVerifyPeer) { - // No peer certificate presented. Report as error if the socket - // expected one. - const QSslError error(QSslError::NoPeerCertificate); - sslErrors += error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - - return true; -} - -bool TlsCryptographSchannel::rootCertOnDemandLoadingAllowed() -{ - Q_ASSERT(d); - return d->isRootsOnDemandAllowed() && QSslSocketPrivate::rootCertOnDemandLoadingSupported(); -} - -} // namespace QTlsPrivate - -void QSslSocketPrivate::registerAdHocFactory() -{ - // TLSTODO: this is a temporary solution, waiting for - // backends to move to ... plugins. - if (!backendSchannel()) - qCWarning(lcTlsBackend, "Failed to create backend factory"); -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qtls_schannel_p.h b/src/network/ssl/qtls_schannel_p.h deleted file mode 100644 index e4ac15206c..0000000000 --- a/src/network/ssl/qtls_schannel_p.h +++ /dev/null @@ -1,159 +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$ -** -****************************************************************************/ - -#ifndef QTLS_SCHANNEL_P_H -#define QTLS_SCHANNEL_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 <QtNetwork/private/qtnetworkglobal_p.h> - -QT_REQUIRE_CONFIG(schannel); - -#include <QtCore/qt_windows.h> - -#include "qtlsbackend_schannel_p.h" -#include "qsslsocket_p.h" - -#include "qwincrypt_p.h" - -#define SECURITY_WIN32 -#define SCHANNEL_USE_BLACKLISTS 1 -#include <Winternl.h> // needed for UNICODE defines -#include <security.h> -#include <schnlsp.h> -#undef SCHANNEL_USE_BLACKLISTS -#undef SECURITY_WIN32 - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -class TlsCryptographSchannel final : public TlsCryptograph -{ - Q_DISABLE_COPY_MOVE(TlsCryptographSchannel) -public: - TlsCryptographSchannel(); - ~TlsCryptographSchannel(); - - void init(QSslSocket *q, QSslSocketPrivate *d) override; - - void startClientEncryption() override; - void startServerEncryption() override; - void transmit() override; - void disconnectFromHost() override; - void disconnected() override; - QSslCipher sessionCipher() const override; - QSsl::SslProtocol sessionProtocol() const override; - void continueHandshake() override; - QList<QSslError> tlsErrors() const override; - -private: - enum class SchannelState { - InitializeHandshake, // create and transmit context (client)/accept context (server) - PerformHandshake, // get token back, process it - VerifyHandshake, // Verify that things are OK - Done, // Connection encrypted! - Renegotiate // Renegotiating! - } schannelState = SchannelState::InitializeHandshake; - - void reset(); - bool acquireCredentialsHandle(); - ULONG getContextRequirements(); - bool createContext(); // for clients - bool acceptContext(); // for server - bool performHandshake(); - bool verifyHandshake(); - bool renegotiate(); - - bool sendToken(void *token, unsigned long tokenLength, bool emitError = true); - QString targetName() const; - - bool checkSslErrors(); - void deallocateContext(); - void freeCredentialsHandle(); - void closeCertificateStores(); - void sendShutdown(); - - void initializeCertificateStores(); - bool verifyCertContext(CERT_CONTEXT *certContext); - - bool rootCertOnDemandLoadingAllowed(); - - bool hasUndecryptedData() const override { return intermediateBuffer.size() > 0; } - - QSslSocket *q = nullptr; - QSslSocketPrivate *d = nullptr; - - SecPkgContext_ConnectionInfo connectionInfo = {}; - SecPkgContext_StreamSizes streamSizes = {}; - - CredHandle credentialHandle; // Initialized in ctor - CtxtHandle contextHandle; // Initialized in ctor - - QByteArray intermediateBuffer; // data which is left-over or incomplete - - QHCertStorePointer localCertificateStore = nullptr; - QHCertStorePointer peerCertificateStore = nullptr; - QHCertStorePointer caCertificateStore = nullptr; - - const CERT_CONTEXT *localCertContext = nullptr; - - ULONG contextAttributes = 0; - qint64 missingData = 0; - - bool renegotiating = false; - bool shutdown = false; - QList<QSslError> sslErrors; -}; - -} // namespace QTlsPrivate - -QT_END_NAMESPACE - -#endif // QTLS_SCHANNEL_P_H diff --git a/src/network/ssl/qtls_st.cpp b/src/network/ssl/qtls_st.cpp deleted file mode 100644 index e4f5c71c02..0000000000 --- a/src/network/ssl/qtls_st.cpp +++ /dev/null @@ -1,1331 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org> -** 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 "qsslsocket.h" - -#include "qssl_p.h" -#include "qtls_st_p.h" -#include "qasn1element_p.h" -#include "qsslcertificate_p.h" -#include "qtlsbackend_st_p.h" -#include "qsslcipher_p.h" -#include "qtlskey_st_p.h" -#include "qsslkey_p.h" - -#include <QtCore/qmessageauthenticationcode.h> -#include <QtCore/qoperatingsystemversion.h> -#include <QtCore/qscopedvaluerollback.h> -#include <QtCore/qcryptographichash.h> -#include <QtCore/qsystemdetection.h> -#include <QtCore/qdatastream.h> -#include <QtCore/qsysinfo.h> -#include <QtCore/qlist.h> -#include <QtCore/qmutex.h> -#include <QtCore/qdebug.h> -#include <QtCore/quuid.h> -#include <QtCore/qdir.h> - -#include <algorithm> -#include <cstddef> -#include <limits> -#include <vector> - -#include <QtCore/private/qcore_mac_p.h> - -#ifdef Q_OS_MACOS -#include <CoreServices/CoreServices.h> -#endif - -QT_BEGIN_NAMESPACE - -Q_GLOBAL_STATIC(QSecureTransportBackend, backendSecureTransport) - -// Defined in qsslsocket_qt.cpp. -QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, - const QString &passPhrase); - -namespace QTlsPrivate { - -// Defined in qtlsbackend_st.cpp -QSslCipher QSslCipher_from_SSLCipherSuite(SSLCipherSuite cipher); - -namespace { - -#ifdef Q_OS_MACOS -/* - -Our own temporarykeychain is needed only on macOS where SecPKCS12Import changes -the default keychain and where we see annoying pop-ups asking about accessing a -private key. - -*/ - -struct EphemeralSecKeychain -{ - EphemeralSecKeychain(); - ~EphemeralSecKeychain(); - - SecKeychainRef keychain = nullptr; - Q_DISABLE_COPY_MOVE(EphemeralSecKeychain) -}; - -EphemeralSecKeychain::EphemeralSecKeychain() -{ - const auto uuid = QUuid::createUuid(); - if (uuid.isNull()) { - qCWarning(lcTlsBackend) << "Failed to create a unique keychain name"; - return; - } - - const QByteArray uuidAsByteArray = uuid.toByteArray(); - Q_ASSERT(uuidAsByteArray.size() > 2); - Q_ASSERT(uuidAsByteArray.startsWith('{')); - Q_ASSERT(uuidAsByteArray.endsWith('}')); - const auto uuidAsString = QLatin1String(uuidAsByteArray.data(), uuidAsByteArray.size()).mid(1, uuidAsByteArray.size() - 2); - - const QString keychainName - = QDir::tempPath() + QDir::separator() + uuidAsString + QLatin1String(".keychain"); - // SecKeychainCreate, pathName parameter: - // - // "A constant character string representing the POSIX path indicating where - // to store the keychain." - // - // Internally they seem to use std::string, but this does not really help. - // Fortunately, CFString has a convenient API. - QCFType<CFStringRef> cfName = keychainName.toCFString(); - std::vector<char> posixPath; - // "Extracts the contents of a string as a NULL-terminated 8-bit string - // appropriate for passing to POSIX APIs." - posixPath.resize(CFStringGetMaximumSizeOfFileSystemRepresentation(cfName)); - const auto ok = CFStringGetFileSystemRepresentation(cfName, &posixPath[0], - CFIndex(posixPath.size())); - if (!ok) { - qCWarning(lcTlsBackend) << "Failed to create a unique keychain name from" - << "QDir::tempPath()"; - return; - } - - std::vector<uint8_t> passUtf8(256); - if (SecRandomCopyBytes(kSecRandomDefault, passUtf8.size(), &passUtf8[0])) { - qCWarning(lcTlsBackend) << "SecRandomCopyBytes: failed to create a key"; - return; - } - - const OSStatus status = SecKeychainCreate(&posixPath[0], passUtf8.size(), - &passUtf8[0], FALSE, nullptr, - &keychain); - if (status != errSecSuccess || !keychain) { - qCWarning(lcTlsBackend) << "SecKeychainCreate: failed to create a custom keychain"; - if (keychain) { - SecKeychainDelete(keychain); - CFRelease(keychain); - keychain = nullptr; - } - } - - if (keychain) { - SecKeychainSettings settings = {}; - settings.version = SEC_KEYCHAIN_SETTINGS_VERS1; - // Strange, huh? But that's what their docs say to do! With lockOnSleep - // == false, set interval to INT_MAX to never lock ... - settings.lockInterval = INT_MAX; - if (SecKeychainSetSettings(keychain, &settings) != errSecSuccess) - qCWarning(lcTlsBackend) << "SecKeychainSettings: failed to disable lock on sleep"; - } - -#ifdef QSSLSOCKET_DEBUG - if (keychain) { - qCDebug(lcTlsBackend) << "Custom keychain with name" << keychainName << "was created" - << "successfully"; - } -#endif -} - -EphemeralSecKeychain::~EphemeralSecKeychain() -{ - if (keychain) { - // clear file off disk - SecKeychainDelete(keychain); - CFRelease(keychain); - } -} - -#endif // Q_OS_MACOS - -void qt_releaseSecureTransportContext(SSLContextRef context) -{ - if (context) - CFRelease(context); -} - -} // unnamed namespace - -// To be also used by qtlsbackend_st.cpp (thus not in unnamed namespace). -SSLContextRef qt_createSecureTransportContext(QSslSocket::SslMode mode) -{ - const bool isServer = mode == QSslSocket::SslServerMode; - const SSLProtocolSide side = isServer ? kSSLServerSide : kSSLClientSide; - // We never use kSSLDatagramType, so it's kSSLStreamType unconditionally. - SSLContextRef context = SSLCreateContext(nullptr, side, kSSLStreamType); - if (!context) - qCWarning(lcSsl) << "SSLCreateContext failed"; - return context; -} - -QSecureTransportContext::QSecureTransportContext(SSLContextRef c) - : context(c) -{ -} - -QSecureTransportContext::~QSecureTransportContext() -{ - qt_releaseSecureTransportContext(context); -} - -QSecureTransportContext::operator SSLContextRef()const -{ - return context; -} - -void QSecureTransportContext::reset(SSLContextRef newContext) -{ - qt_releaseSecureTransportContext(context); - context = newContext; -} - -#if !defined(QT_PLATFORM_UIKIT) // dhparam is only used on macOS. (see the SSLSetDiffieHellmanParams call below) -static const uint8_t dhparam[] = - "\x30\x82\x01\x08\x02\x82\x01\x01\x00\x97\xea\xd0\x46\xf7\xae\xa7\x76\x80" - "\x9c\x74\x56\x98\xd8\x56\x97\x2b\x20\x6c\x77\xe2\x82\xbb\xc8\x84\xbe\xe7" - "\x63\xaf\xcc\x30\xd0\x67\x97\x7d\x1b\xab\x59\x30\xa9\x13\x67\x21\xd7\xd4" - "\x0e\x46\xcf\xe5\x80\xdf\xc9\xb9\xba\x54\x9b\x46\x2f\x3b\x45\xfc\x2f\xaf" - "\xad\xc0\x17\x56\xdd\x52\x42\x57\x45\x70\x14\xe5\xbe\x67\xaa\xde\x69\x75" - "\x30\x0d\xf9\xa2\xc4\x63\x4d\x7a\x39\xef\x14\x62\x18\x33\x44\xa1\xf9\xc1" - "\x52\xd1\xb6\x72\x21\x98\xf8\xab\x16\x1b\x7b\x37\x65\xe3\xc5\x11\x00\xf6" - "\x36\x1f\xd8\x5f\xd8\x9f\x43\xa8\xce\x9d\xbf\x5e\xd6\x2d\xfa\x0a\xc2\x01" - "\x54\xc2\xd9\x81\x54\x55\xb5\x26\xf8\x88\x37\xf5\xfe\xe0\xef\x4a\x34\x81" - "\xdc\x5a\xb3\x71\x46\x27\xe3\xcd\x24\xf6\x1b\xf1\xe2\x0f\xc2\xa1\x39\x53" - "\x5b\xc5\x38\x46\x8e\x67\x4c\xd9\xdd\xe4\x37\x06\x03\x16\xf1\x1d\x7a\xba" - "\x2d\xc1\xe4\x03\x1a\x58\xe5\x29\x5a\x29\x06\x69\x61\x7a\xd8\xa9\x05\x9f" - "\xc1\xa2\x45\x9c\x17\xad\x52\x69\x33\xdc\x18\x8d\x15\xa6\x5e\xcd\x94\xf4" - "\x45\xbb\x9f\xc2\x7b\x85\x00\x61\xb0\x1a\xdc\x3c\x86\xaa\x9f\x5c\x04\xb3" - "\x90\x0b\x35\x64\xff\xd9\xe3\xac\xf2\xf2\xeb\x3a\x63\x02\x01\x02"; -#endif - -OSStatus TlsCryptographSecureTransport::ReadCallback(TlsCryptographSecureTransport *socket, - char *data, size_t *dataLength) -{ - Q_ASSERT(socket); - Q_ASSERT(data); - Q_ASSERT(dataLength); - - Q_ASSERT(socket->d); - QTcpSocket *plainSocket = socket->d->plainTcpSocket(); - Q_ASSERT(plainSocket); - - if (socket->isHandshakeComplete()) { - // Check if it's a renegotiation attempt, when the handshake is complete, the - // session state is 'kSSLConnected': - SSLSessionState currentState = kSSLConnected; - const OSStatus result = SSLGetSessionState(socket->context, ¤tState); - if (result != noErr) { - *dataLength = 0; - return result; - } - - if (currentState == kSSLHandshake) { - // Renegotiation detected, don't allow read more yet - 'transmit' - // will notice this and will call 'startHandshake': - *dataLength = 0; - socket->renegotiating = true; - return errSSLWouldBlock; - } - } - - const qint64 bytes = plainSocket->read(data, *dataLength); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << plainSocket << "read" << bytes; -#endif - if (bytes < 0) { - *dataLength = 0; - return errSecIO; - } - - const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : errSecSuccess; - *dataLength = bytes; - - return err; -} - -OSStatus TlsCryptographSecureTransport::WriteCallback(TlsCryptographSecureTransport *socket, - const char *data, size_t *dataLength) -{ - Q_ASSERT(socket); - Q_ASSERT(data); - Q_ASSERT(dataLength); - - Q_ASSERT(socket->d); - QTcpSocket *plainSocket = socket->d->plainTcpSocket(); - Q_ASSERT(plainSocket); - - const qint64 bytes = plainSocket->write(data, *dataLength); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << plainSocket << "write" << bytes; -#endif - if (bytes < 0) { - *dataLength = 0; - return errSecIO; - } - - const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : errSecSuccess; - *dataLength = bytes; - - return err; -} - -TlsCryptographSecureTransport::TlsCryptographSecureTransport() - : context(nullptr) -{ -} - -TlsCryptographSecureTransport::~TlsCryptographSecureTransport() -{ - destroySslContext(); -} - -void TlsCryptographSecureTransport::init(QSslSocket *qObj, QSslSocketPrivate *dObj) -{ - Q_ASSERT(qObj); - Q_ASSERT(dObj); - q = qObj; - d = dObj; - - renegotiating = false; - shutdown = false; -} - -void TlsCryptographSecureTransport::continueHandshake() -{ - Q_ASSERT(q); - Q_ASSERT(d); - d->setEncrypted(true); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << d->plainTcpSocket() << "connection encrypted"; -#endif - -#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13_4, __IPHONE_11_0, __TVOS_11_0, __WATCHOS_4_0) - // Unlike OpenSSL, Secure Transport does not allow to negotiate protocols via - // a callback during handshake. We can only set our list of preferred protocols - // (and send it during handshake) and then receive what our peer has sent to us. - // And here we can finally try to find a match (if any). - const auto &configuration = q->sslConfiguration(); - if (__builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) { - const auto &requestedProtocols = configuration.allowedNextProtocols(); - if (const int requestedCount = requestedProtocols.size()) { - QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNone); - QTlsBackend::setNegotiatedProtocol(d, {}); - - QCFType<CFArrayRef> cfArray; - const OSStatus result = SSLCopyALPNProtocols(context, &cfArray); - if (result == errSecSuccess && cfArray && CFArrayGetCount(cfArray)) { - const int size = CFArrayGetCount(cfArray); - QList<QString> peerProtocols(size); - for (int i = 0; i < size; ++i) - peerProtocols[i] = QString::fromCFString((CFStringRef)CFArrayGetValueAtIndex(cfArray, i)); - - for (int i = 0; i < requestedCount; ++i) { - const auto requestedName = QString::fromLatin1(requestedProtocols[i]); - for (int j = 0; j < size; ++j) { - if (requestedName == peerProtocols[j]) { - QTlsBackend::setNegotiatedProtocol(d, requestedName.toLatin1()); - QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNegotiated); - break; - } - } - if (configuration.nextProtocolNegotiationStatus() == QSslConfiguration::NextProtocolNegotiationNegotiated) - break; - } - } - } - } -#endif // QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE - - if (!renegotiating) - emit q->encrypted(); - - if (d->isAutoStartingHandshake() && d->isPendingClose()) { - d->setPendingClose(false); - q->disconnectFromHost(); - } -} - -void TlsCryptographSecureTransport::disconnected() -{ - Q_ASSERT(d && d->plainTcpSocket()); - if (d->plainTcpSocket()->bytesAvailable() <= 0) - destroySslContext(); - // If there is still buffered data in the plain socket, don't destroy the ssl context yet. - // It will be destroyed when the socket is deleted. -} - -void TlsCryptographSecureTransport::disconnectFromHost() -{ - Q_ASSERT(d && d->plainTcpSocket()); - if (context) { - if (!shutdown) { - SSLClose(context); - shutdown = true; - } - } - d->plainTcpSocket()->disconnectFromHost(); -} - -QSslCipher TlsCryptographSecureTransport::sessionCipher() const -{ - SSLCipherSuite cipher = 0; - if (context && SSLGetNegotiatedCipher(context, &cipher) == errSecSuccess) - return QSslCipher_from_SSLCipherSuite(cipher); - - return QSslCipher(); -} - -QSsl::SslProtocol TlsCryptographSecureTransport::sessionProtocol() const -{ - if (!context) - return QSsl::UnknownProtocol; - - SSLProtocol protocol = kSSLProtocolUnknown; - const OSStatus err = SSLGetNegotiatedProtocolVersion(context, &protocol); - if (err != errSecSuccess) { - qCWarning(lcTlsBackend) << "SSLGetNegotiatedProtocolVersion failed:" << err; - return QSsl::UnknownProtocol; - } - - switch (protocol) { - case kTLSProtocol1: - return QSsl::TlsV1_0; - case kTLSProtocol11: - return QSsl::TlsV1_1; - case kTLSProtocol12: - return QSsl::TlsV1_2; - case kTLSProtocol13: - return QSsl::TlsV1_3; - default: - return QSsl::UnknownProtocol; - } -} - -void TlsCryptographSecureTransport::startClientEncryption() -{ - if (!initSslContext()) { - Q_ASSERT(d); - // Error description/code were set, 'error' emitted - // by initSslContext, but OpenSSL socket also sets error - // emits a signal twice, so ... - d->setErrorAndEmit(QAbstractSocket::SslInternalError, QStringLiteral("Unable to init SSL Context")); - return; - } - - startHandshake(); -} - -void TlsCryptographSecureTransport::startServerEncryption() -{ - if (!initSslContext()) { - // Error description/code were set, 'error' emitted - // by initSslContext, but OpenSSL socket also sets error - // emits a signal twice, so ... - d->setErrorAndEmit(QAbstractSocket::SslInternalError, QStringLiteral("Unable to init SSL Context")); - return; - } - - startHandshake(); -} - -void TlsCryptographSecureTransport::transmit() -{ - Q_ASSERT(q); - Q_ASSERT(d); - - // If we don't have any SSL context, don't bother transmitting. - // Edit: if SSL session closed, don't bother either. - if (!context || shutdown) - return; - - if (!isHandshakeComplete()) - startHandshake(); - - auto &writeBuffer = d->tlsWriteBuffer(); - if (isHandshakeComplete() && !writeBuffer.isEmpty()) { - qint64 totalBytesWritten = 0; - while (writeBuffer.nextDataBlockSize() > 0 && context) { - const size_t nextDataBlockSize = writeBuffer.nextDataBlockSize(); - size_t writtenBytes = 0; - const OSStatus err = SSLWrite(context, writeBuffer.readPointer(), nextDataBlockSize, &writtenBytes); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << d->plainTcpSocket() << "SSLWrite returned" << err; -#endif - if (err != errSecSuccess && err != errSSLWouldBlock) { - d->setErrorAndEmit(QAbstractSocket::SslInternalError, - QStringLiteral("SSLWrite failed: %1").arg(err)); - break; - } - - if (writtenBytes) { - writeBuffer.free(writtenBytes); - totalBytesWritten += writtenBytes; - } - - if (writtenBytes < nextDataBlockSize) - break; - } - - if (totalBytesWritten > 0) { - // Don't emit bytesWritten() recursively. - auto &emittedBytesWritten = d->tlsEmittedBytesWritten(); - if (!emittedBytesWritten) { - emittedBytesWritten = true; - emit q->bytesWritten(totalBytesWritten); - emittedBytesWritten = false; - } - emit q->channelBytesWritten(0, totalBytesWritten); - } - } - - auto &buffer = d->tlsBuffer(); - const auto readBufferMaxSize = d->maxReadBufferSize(); - if (isHandshakeComplete()) { - QVarLengthArray<char, 4096> data; - while (context && (!readBufferMaxSize || buffer.size() < readBufferMaxSize)) { - size_t readBytes = 0; - data.resize(4096); - const OSStatus err = SSLRead(context, data.data(), data.size(), &readBytes); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << d->plainTcpSocket() << "SSLRead returned" << err; -#endif - if (err == errSSLClosedGraceful) { - shutdown = true; // the other side shut down, make sure we do not send shutdown ourselves - d->setErrorAndEmit(QAbstractSocket::RemoteHostClosedError, - QSslSocket::tr("The TLS/SSL connection has been closed")); - break; - } else if (err != errSecSuccess && err != errSSLWouldBlock) { - d->setErrorAndEmit(QAbstractSocket::SslInternalError, - QStringLiteral("SSLRead failed: %1").arg(err)); - break; - } - - if (err == errSSLWouldBlock && renegotiating) { - startHandshake(); - break; - } - - if (readBytes) { - buffer.append(data.constData(), readBytes); - if (bool *readyReadEmittedPointer = d->readyReadPointer()) - *readyReadEmittedPointer = true; - emit q->readyRead(); - emit q->channelReadyRead(0); - } - - if (err == errSSLWouldBlock) - break; - } - } -} - -SSLCipherSuite TlsCryptographSecureTransport::SSLCipherSuite_from_QSslCipher(const QSslCipher &ciph) -{ - if (ciph.name() == QLatin1String("AES128-SHA")) - return TLS_RSA_WITH_AES_128_CBC_SHA; - if (ciph.name() == QLatin1String("DHE-RSA-AES128-SHA")) - return TLS_DHE_RSA_WITH_AES_128_CBC_SHA; - if (ciph.name() == QLatin1String("AES256-SHA")) - return TLS_RSA_WITH_AES_256_CBC_SHA; - if (ciph.name() == QLatin1String("DHE-RSA-AES256-SHA")) - return TLS_DHE_RSA_WITH_AES_256_CBC_SHA; - if (ciph.name() == QLatin1String("ECDH-ECDSA-NULL-SHA")) - return TLS_ECDH_ECDSA_WITH_NULL_SHA; - if (ciph.name() == QLatin1String("ECDH-ECDSA-RC4-SHA")) - return TLS_ECDH_ECDSA_WITH_RC4_128_SHA; - if (ciph.name() == QLatin1String("ECDH-ECDSA-DES-CBC3-SHA")) - return TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA; - if (ciph.name() == QLatin1String("ECDH-ECDSA-AES128-SHA")) - return TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA; - if (ciph.name() == QLatin1String("ECDH-ECDSA-AES256-SHA")) - return TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA; - if (ciph.name() == QLatin1String("ECDH-ECDSA-RC4-SHA")) - return TLS_ECDHE_ECDSA_WITH_RC4_128_SHA; - if (ciph.name() == QLatin1String("ECDH-ECDSA-DES-CBC3-SHA")) - return TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA; - if (ciph.name() == QLatin1String("ECDH-ECDSA-AES128-SHA")) - return TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA; - if (ciph.name() == QLatin1String("ECDH-ECDSA-AES256-SHA")) - return TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA; - if (ciph.name() == QLatin1String("ECDH-RSA-NULL-SHA")) - return TLS_ECDH_RSA_WITH_NULL_SHA; - if (ciph.name() == QLatin1String("ECDH-RSA-RC4-SHA")) - return TLS_ECDH_RSA_WITH_RC4_128_SHA; - if (ciph.name() == QLatin1String("ECDH-RSA-DES-CBC3-SHA")) - return TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA; - if (ciph.name() == QLatin1String("ECDH-RSA-AES128-SHA")) - return TLS_ECDH_RSA_WITH_AES_128_CBC_SHA; - if (ciph.name() == QLatin1String("ECDH-RSA-AES256-SHA")) - return TLS_ECDH_RSA_WITH_AES_256_CBC_SHA; - if (ciph.name() == QLatin1String("ECDH-RSA-RC4-SHA")) - return TLS_ECDHE_RSA_WITH_RC4_128_SHA; - if (ciph.name() == QLatin1String("ECDH-RSA-DES-CBC3-SHA")) - return TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA; - if (ciph.name() == QLatin1String("ECDH-RSA-AES128-SHA")) - return TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA; - if (ciph.name() == QLatin1String("ECDH-RSA-AES256-SHA")) - return TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA; - if (ciph.name() == QLatin1String("DES-CBC3-SHA")) - return TLS_RSA_WITH_3DES_EDE_CBC_SHA; - if (ciph.name() == QLatin1String("AES128-SHA256")) - return TLS_RSA_WITH_AES_128_CBC_SHA256; - if (ciph.name() == QLatin1String("AES256-SHA256")) - return TLS_RSA_WITH_AES_256_CBC_SHA256; - if (ciph.name() == QLatin1String("DHE-RSA-DES-CBC3-SHA")) - return TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA; - if (ciph.name() == QLatin1String("DHE-RSA-AES128-SHA256")) - return TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; - if (ciph.name() == QLatin1String("DHE-RSA-AES256-SHA256")) - return TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; - if (ciph.name() == QLatin1String("AES256-GCM-SHA384")) - return TLS_RSA_WITH_AES_256_GCM_SHA384; - if (ciph.name() == QLatin1String("ECDHE-ECDSA-AES128-SHA256")) - return TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; - if (ciph.name() == QLatin1String("ECDHE-ECDSA-AES256-SHA384")) - return TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384; - if (ciph.name() == QLatin1String("ECDH-ECDSA-AES128-SHA256")) - return TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256; - if (ciph.name() == QLatin1String("ECDH-ECDSA-AES256-SHA384")) - return TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384; - if (ciph.name() == QLatin1String("ECDHE-RSA-AES128-SHA256")) - return TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256; - if (ciph.name() == QLatin1String("ECDHE-RSA-AES256-SHA384")) - return TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384; - if (ciph.name() == QLatin1String("ECDHE-RSA-AES256-SHA384")) - return TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256; - if (ciph.name() == QLatin1String("ECDHE-RSA-AES256-GCM-SHA384")) - return TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384; - return 0; -} - -bool TlsCryptographSecureTransport::initSslContext() -{ - Q_ASSERT(q); - Q_ASSERT(d); - - Q_ASSERT_X(!context, Q_FUNC_INFO, "invalid socket state, context is not null"); - auto *plainSocket = d->plainTcpSocket(); - Q_ASSERT(plainSocket); - - const auto mode = d->tlsMode(); - - context.reset(qt_createSecureTransportContext(mode)); - if (!context) { - d->setErrorAndEmit(QAbstractSocket::SslInternalError, QStringLiteral("SSLCreateContext failed")); - return false; - } - - const OSStatus err = SSLSetIOFuncs(context, - reinterpret_cast<SSLReadFunc>(&TlsCryptographSecureTransport::ReadCallback), - reinterpret_cast<SSLWriteFunc>(&TlsCryptographSecureTransport::WriteCallback)); - if (err != errSecSuccess) { - destroySslContext(); - d->setErrorAndEmit(QAbstractSocket::SslInternalError, - QStringLiteral("SSLSetIOFuncs failed: %1").arg(err)); - return false; - } - - SSLSetConnection(context, this); - - const auto &configuration = q->sslConfiguration(); - if (mode == QSslSocket::SslServerMode - && !configuration.localCertificateChain().isEmpty()) { - QString errorDescription; - QAbstractSocket::SocketError errorCode = QAbstractSocket::UnknownSocketError; - if (!setSessionCertificate(errorDescription, errorCode)) { - destroySslContext(); - d->setErrorAndEmit(errorCode, errorDescription); - return false; - } - } - - if (!setSessionProtocol()) { - destroySslContext(); - d->setErrorAndEmit(QAbstractSocket::SslInternalError, QStringLiteral("Failed to set protocol version")); - return false; - } - -#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13_4, __IPHONE_11_0, __TVOS_11_0, __WATCHOS_4_0) - if (__builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) { - const auto protocolNames = configuration.allowedNextProtocols(); - QCFType<CFMutableArrayRef> cfNames(CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks)); - if (cfNames) { - for (const QByteArray &name : protocolNames) { - if (name.size() > 255) { - qCWarning(lcTlsBackend) << "TLS ALPN extension" << name - << "is too long and will be ignored."; - continue; - } else if (name.isEmpty()) { - continue; - } - QCFString cfName(QString::fromLatin1(name).toCFString()); - CFArrayAppendValue(cfNames, cfName); - } - - if (CFArrayGetCount(cfNames)) { - // Up to the application layer to check that negotiation - // failed, and handle this non-TLS error, we do not handle - // the result of this call as an error: - if (SSLSetALPNProtocols(context, cfNames) != errSecSuccess) - qCWarning(lcTlsBackend) << "SSLSetALPNProtocols failed - too long protocol names?"; - } - } else { - qCWarning(lcTlsBackend) << "failed to allocate ALPN names array"; - } - } -#endif // QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE - - if (mode == QSslSocket::SslClientMode) { - // enable Server Name Indication (SNI) - const auto verificationPeerName = d->verificationName(); - QString tlsHostName(verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName); - if (tlsHostName.isEmpty()) - tlsHostName = d->tlsHostName(); - - const QByteArray ace(QUrl::toAce(tlsHostName)); - SSLSetPeerDomainName(context, ace.data(), ace.size()); - // tell SecureTransport we handle peer verification ourselves - OSStatus err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnServerAuth, true); - if (err == errSecSuccess) - err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnCertRequested, true); - - if (err != errSecSuccess) { - destroySslContext(); - d->setErrorAndEmit(QSslSocket::SslInternalError, - QStringLiteral("SSLSetSessionOption failed: %1").arg(err)); - return false; - } - // - } else { - if (configuration.peerVerifyMode() != QSslSocket::VerifyNone) { - // kAlwaysAuthenticate - always fails even if we set break on client auth. - OSStatus err = SSLSetClientSideAuthenticate(context, kTryAuthenticate); - if (err == errSecSuccess) { - // We'd like to verify peer ourselves, otherwise handshake will - // most probably fail before we can do anything. - err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnClientAuth, true); - } - - if (err != errSecSuccess) { - destroySslContext(); - d->setErrorAndEmit(QAbstractSocket::SslInternalError, - QStringLiteral("failed to set SSL context option in server mode: %1").arg(err)); - return false; - } - } -#if !defined(QT_PLATFORM_UIKIT) - // No SSLSetDiffieHellmanParams on iOS; calling it is optional according to docs. - SSLSetDiffieHellmanParams(context, dhparam, sizeof(dhparam)); -#endif - } - if (configuration.ciphers().size() > 0) { - QVector<SSLCipherSuite> cfCiphers; - for (const QSslCipher &cipher : configuration.ciphers()) { - if (auto sslCipher = TlsCryptographSecureTransport::SSLCipherSuite_from_QSslCipher(cipher)) - cfCiphers << sslCipher; - } - if (cfCiphers.size() == 0) { - qCWarning(lcTlsBackend) << "failed to add any of the requested ciphers from the configuration"; - return false; - } - OSStatus err = SSLSetEnabledCiphers(context, cfCiphers.data(), cfCiphers.size()); - if (err != errSecSuccess) { - qCWarning(lcTlsBackend) << "failed to set the ciphers from the configuration"; - return false; - } - } - return true; -} - -void TlsCryptographSecureTransport::destroySslContext() -{ - context.reset(nullptr); -} - -bool TlsCryptographSecureTransport::setSessionCertificate(QString &errorDescription, QAbstractSocket::SocketError &errorCode) -{ - Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)"); - - Q_ASSERT(d); - const auto &configuration = q->sslConfiguration(); - -#ifdef QSSLSOCKET_DEBUG - auto *plainSocket = d->plainTcpSocket(); -#endif - - QSslCertificate localCertificate; - - if (!configuration.localCertificateChain().isEmpty()) - localCertificate = configuration.localCertificateChain().at(0); - - if (!localCertificate.isNull()) { - // Require a private key as well. - if (configuration.privateKey().isNull()) { - errorCode = QAbstractSocket::SslInvalidUserDataError; - errorDescription = QStringLiteral("Cannot provide a certificate with no key"); - return false; - } - - // import certificates and key - const QString passPhrase(QString::fromLatin1("foobar")); - QCFType<CFDataRef> pkcs12 = _q_makePkcs12(configuration.localCertificateChain(), - configuration.privateKey(), passPhrase).toCFData(); - QCFType<CFStringRef> password = passPhrase.toCFString(); - const void *keys[2] = { kSecImportExportPassphrase }; - const void *values[2] = { password }; - CFIndex nKeys = 1; -#ifdef Q_OS_MACOS - bool envOk = false; - const int env = qEnvironmentVariableIntValue("QT_SSL_USE_TEMPORARY_KEYCHAIN", &envOk); - if (envOk && env) { - static const EphemeralSecKeychain temporaryKeychain; - if (temporaryKeychain.keychain) { - nKeys = 2; - keys[1] = kSecImportExportKeychain; - values[1] = temporaryKeychain.keychain; - } - } -#endif - QCFType<CFDictionaryRef> options = CFDictionaryCreate(nullptr, keys, values, nKeys, - nullptr, nullptr); - QCFType<CFArrayRef> items; - OSStatus err = SecPKCS12Import(pkcs12, options, &items); - if (err != errSecSuccess) { -#ifdef QSSLSOCKET_DEBUG - qCWarning(lcTlsBackend) << plainSocket - << QStringLiteral("SecPKCS12Import failed: %1").arg(err); -#endif - errorCode = QAbstractSocket::SslInvalidUserDataError; - errorDescription = QStringLiteral("SecPKCS12Import failed: %1").arg(err); - return false; - } - - if (!CFArrayGetCount(items)) { -#ifdef QSSLSOCKET_DEBUG - qCWarning(lcTlsBackend) << plainSocket << "SecPKCS12Import returned no items"; -#endif - errorCode = QAbstractSocket::SslInvalidUserDataError; - errorDescription = QStringLiteral("SecPKCS12Import returned no items"); - return false; - } - - CFDictionaryRef import = (CFDictionaryRef)CFArrayGetValueAtIndex(items, 0); - SecIdentityRef identity = (SecIdentityRef)CFDictionaryGetValue(import, kSecImportItemIdentity); - if (!identity) { -#ifdef QSSLSOCKET_DEBUG - qCWarning(lcTlsBackend) << plainSocket << "SecPKCS12Import returned no identity"; -#endif - errorCode = QAbstractSocket::SslInvalidUserDataError; - errorDescription = QStringLiteral("SecPKCS12Import returned no identity"); - return false; - } - - QCFType<CFMutableArrayRef> certs = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks); - if (!certs) { - errorCode = QAbstractSocket::SslInternalError; - errorDescription = QStringLiteral("Failed to allocate certificates array"); - return false; - } - - CFArrayAppendValue(certs, identity); - - CFArrayRef chain = (CFArrayRef)CFDictionaryGetValue(import, kSecImportItemCertChain); - if (chain) { - for (CFIndex i = 1, e = CFArrayGetCount(chain); i < e; ++i) - CFArrayAppendValue(certs, CFArrayGetValueAtIndex(chain, i)); - } - - err = SSLSetCertificate(context, certs); - if (err != errSecSuccess) { -#ifdef QSSLSOCKET_DEBUG - qCWarning(lcTlsBackend) - << plainSocket << QStringLiteral("Cannot set certificate and key: %1").arg(err); -#endif - errorCode = QAbstractSocket::SslInvalidUserDataError; - errorDescription = QStringLiteral("Cannot set certificate and key: %1").arg(err); - return false; - } - } - - return true; -} - -bool TlsCryptographSecureTransport::setSessionProtocol() -{ - Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)"); - Q_ASSERT(q); - Q_ASSERT(d); - // SecureTransport has kTLSProtocol13 constant and also, kTLSProtocolMaxSupported. - // Calling SSLSetProtocolVersionMax/Min with any of these two constants results - // in errInvalidParam and a failure to set the protocol version. This means - // no TLS 1.3 on macOS and iOS. - const auto &configuration = q->sslConfiguration(); - auto *plainSocket = d->plainTcpSocket(); - switch (configuration.protocol()) { - case QSsl::TlsV1_3: - case QSsl::TlsV1_3OrLater: - qCWarning(lcTlsBackend) << plainSocket << "SecureTransport does not support TLS 1.3"; - return false; - default:; - } - - OSStatus err = errSecSuccess; - - if (configuration.protocol() == QSsl::TlsV1_0) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << plainSocket << "requesting : TLSv1.0"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol1); - if (err == errSecSuccess) - err = SSLSetProtocolVersionMax(context, kTLSProtocol1); - } else if (configuration.protocol() == QSsl::TlsV1_1) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << plainSocket << "requesting : TLSv1.1"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol11); - if (err == errSecSuccess) - err = SSLSetProtocolVersionMax(context, kTLSProtocol11); - } else if (configuration.protocol() == QSsl::TlsV1_2) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << plainSocket << "requesting : TLSv1.2"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol12); - if (err == errSecSuccess) - err = SSLSetProtocolVersionMax(context, kTLSProtocol12); - } else if (configuration.protocol() == QSsl::AnyProtocol) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << plainSocket << "requesting : any"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol1); - } else if (configuration.protocol() == QSsl::SecureProtocols) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << plainSocket << "requesting : TLSv1 - TLSv1.2"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol1); - } else if (configuration.protocol() == QSsl::TlsV1_0OrLater) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << plainSocket << "requesting : TLSv1 - TLSv1.2"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol1); - } else if (configuration.protocol() == QSsl::TlsV1_1OrLater) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << plainSocket << "requesting : TLSv1.1 - TLSv1.2"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol11); - } else if (configuration.protocol() == QSsl::TlsV1_2OrLater) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << plainSocket << "requesting : TLSv1.2"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol12); - } else { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << plainSocket << "no protocol version found in the configuration"; - #endif - return false; - } - - return err == errSecSuccess; -} - -bool TlsCryptographSecureTransport::canIgnoreTrustVerificationFailure() const -{ - Q_ASSERT(q); - Q_ASSERT(d); - const auto &configuration = q->sslConfiguration(); - const QSslSocket::PeerVerifyMode verifyMode = configuration.peerVerifyMode(); - return d->tlsMode() == QSslSocket::SslServerMode - && (verifyMode == QSslSocket::QueryPeer - || verifyMode == QSslSocket::AutoVerifyPeer - || verifyMode == QSslSocket::VerifyNone); -} - -bool TlsCryptographSecureTransport::verifySessionProtocol() const -{ - Q_ASSERT(q); - - const auto &configuration = q->sslConfiguration(); - bool protocolOk = false; - if (configuration.protocol() == QSsl::AnyProtocol) - protocolOk = true; - else if (configuration.protocol() == QSsl::SecureProtocols) - protocolOk = (sessionProtocol() >= QSsl::TlsV1_0); - else if (configuration.protocol() == QSsl::TlsV1_0OrLater) - protocolOk = (sessionProtocol() >= QSsl::TlsV1_0); - else if (configuration.protocol() == QSsl::TlsV1_1OrLater) - protocolOk = (sessionProtocol() >= QSsl::TlsV1_1); - else if (configuration.protocol() == QSsl::TlsV1_2OrLater) - protocolOk = (sessionProtocol() >= QSsl::TlsV1_2); - else if (configuration.protocol() == QSsl::TlsV1_3OrLater) - protocolOk = (sessionProtocol() >= QSsl::TlsV1_3OrLater); - else - protocolOk = (sessionProtocol() == configuration.protocol()); - - return protocolOk; -} - -bool TlsCryptographSecureTransport::verifyPeerTrust() -{ - Q_ASSERT(q); - Q_ASSERT(d); - - const auto mode = d->tlsMode(); - const QSslSocket::PeerVerifyMode verifyMode = q->peerVerifyMode(); - const bool canIgnoreVerify = canIgnoreTrustVerificationFailure(); - - Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)"); - - auto *plainSocket = d->plainTcpSocket(); - Q_ASSERT(plainSocket); - - QCFType<SecTrustRef> trust; - OSStatus err = SSLCopyPeerTrust(context, &trust); - // !trust - SSLCopyPeerTrust can return errSecSuccess but null trust. - if (err != errSecSuccess || !trust) { - if (!canIgnoreVerify) { - d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - QStringLiteral("Failed to obtain peer trust: %1").arg(err)); - plainSocket->disconnectFromHost(); - return false; - } else { - return true; - } - } - - QList<QSslError> errors; - - // Store certificates. - // Apple's docs say SetTrustEvaluate must be called before - // SecTrustGetCertificateAtIndex, but this results - // in 'kSecTrustResultRecoverableTrustFailure', so - // here we just ignore 'res' (later we'll use SetAnchor etc. - // and evaluate again). - SecTrustResultType res = kSecTrustResultInvalid; - err = SecTrustEvaluate(trust, &res); - if (err != errSecSuccess) { - // We can not ignore this, it's not even about trust verification - // probably ... - d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - QStringLiteral("SecTrustEvaluate failed: %1").arg(err)); - plainSocket->disconnectFromHost(); - return false; - } - - QTlsBackend::clearPeerCertificates(d); - - QList<QSslCertificate> peerCertificateChain; - const CFIndex certCount = SecTrustGetCertificateCount(trust); - for (CFIndex i = 0; i < certCount; ++i) { - SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, i); - QCFType<CFDataRef> derData = SecCertificateCopyData(cert); - peerCertificateChain << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der); - } - QTlsBackend::storePeerCertificateChain(d, peerCertificateChain); - - if (peerCertificateChain.size()) - QTlsBackend::storePeerCertificate(d, peerCertificateChain.at(0)); - - // Check the whole chain for blacklisting (including root, as we check for subjectInfo and issuer): - for (const QSslCertificate &cert : qAsConst(peerCertificateChain)) { - if (QSslCertificatePrivate::isBlacklisted(cert) && !canIgnoreVerify) { - const QSslError error(QSslError::CertificateBlacklisted, cert); - errors << error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - } - - const bool doVerifyPeer = verifyMode == QSslSocket::VerifyPeer - || (verifyMode == QSslSocket::AutoVerifyPeer - && d->tlsMode() == QSslSocket::SslClientMode); - // Check the peer certificate itself. First try the subject's common name - // (CN) as a wildcard, then try all alternate subject name DNS entries the - // same way. - const auto &peerCertificate = q->peerCertificate(); - if (!peerCertificate.isNull()) { - // but only if we're a client connecting to a server - // if we're the server, don't check CN - const QString verificationPeerName = d->verificationName(); - if (mode == QSslSocket::SslClientMode) { - const QString peerName(verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName); - if (!isMatchingHostname(peerCertificate, peerName) && !canIgnoreVerify) { - // No matches in common names or alternate names. - const QSslError error(QSslError::HostNameMismatch, peerCertificate); - errors << error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - } - } else { - // No peer certificate presented. Report as error if the socket - // expected one. - if (doVerifyPeer && !canIgnoreVerify) { - const QSslError error(QSslError::NoPeerCertificate); - errors << error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - } - - // verify certificate chain - QCFType<CFMutableArrayRef> certArray = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks); - const auto &caCertificates = q->sslConfiguration().caCertificates(); - for (const QSslCertificate &cert : caCertificates) { - QCFType<CFDataRef> certData = cert.toDer().toCFData(); - if (QCFType<SecCertificateRef> secRef = SecCertificateCreateWithData(nullptr, certData)) - CFArrayAppendValue(certArray, secRef); - else - qCWarning(lcTlsBackend, "Failed to create SecCertificate from QSslCertificate"); - } - - SecTrustSetAnchorCertificates(trust, certArray); - - // By default SecTrustEvaluate uses both CA certificates provided in - // QSslConfiguration and the ones from the system database. This behavior can - // be unexpected if a user's code tries to limit the trusted CAs to those - // explicitly set in QSslConfiguration. - // Since on macOS we initialize the default QSslConfiguration copying the - // system CA certificates (using SecTrustSettingsCopyCertificates) we can - // call SecTrustSetAnchorCertificatesOnly(trust, true) to force SecTrustEvaluate - // to use anchors only from our QSslConfiguration. - // Unfortunately, SecTrustSettingsCopyCertificates is not available on iOS - // and the default QSslConfiguration always has an empty list of system CA - // certificates. This leaves no way to provide client code with access to the - // actual system CA certificate list (which most use-cases need) other than - // by letting SecTrustEvaluate fall through to the system list; so, in this case - // (even though the client code may have provided its own certs), we retain - // the default behavior. Note, with macOS SDK below 10.12 using 'trust my - // anchors only' may result in some valid chains rejected, apparently the - // ones containing intermediated certificates; so we use this functionality - // on more recent versions only. - - bool anchorsFromConfigurationOnly = false; - -#ifdef Q_OS_MACOS - if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSSierra) - anchorsFromConfigurationOnly = true; -#endif // Q_OS_MACOS - - SecTrustSetAnchorCertificatesOnly(trust, anchorsFromConfigurationOnly); - - SecTrustResultType trustResult = kSecTrustResultInvalid; - SecTrustEvaluate(trust, &trustResult); - switch (trustResult) { - case kSecTrustResultUnspecified: - case kSecTrustResultProceed: - break; - default: - if (!canIgnoreVerify) { - const QSslError error(QSslError::CertificateUntrusted, peerCertificate); - errors << error; - emit q->peerVerifyError(error); - } - } - - // report errors - if (!errors.isEmpty() && !canIgnoreVerify) { - sslErrors = errors; - // checkSslErrors unconditionally emits sslErrors: - // a user's slot can abort/close/disconnect on this - // signal, so we also test the socket's state: - if (!checkSslErrors() || q->state() != QAbstractSocket::ConnectedState) - return false; - } else { - sslErrors.clear(); - } - - return true; -} - -/* - Copied verbatim from qsslsocket_openssl.cpp -*/ -bool TlsCryptographSecureTransport::checkSslErrors() -{ - if (sslErrors.isEmpty()) - return true; - - Q_ASSERT(q); - Q_ASSERT(d); - - emit q->sslErrors(sslErrors); - const auto mode = d->tlsMode(); - const auto &configuration = q->sslConfiguration(); - const bool doVerifyPeer = configuration.peerVerifyMode() == QSslSocket::VerifyPeer - || (configuration.peerVerifyMode() == QSslSocket::AutoVerifyPeer - && mode == QSslSocket::SslClientMode); - const bool doEmitSslError = !d->verifyErrorsHaveBeenIgnored(); - // check whether we need to emit an SSL handshake error - if (doVerifyPeer && doEmitSslError) { - if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) { - QSslSocketPrivate::pauseSocketNotifiers(q); - d->setPaused(true); - } else { - d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - sslErrors.constFirst().errorString()); - Q_ASSERT(d->plainTcpSocket()); - d->plainTcpSocket()->disconnectFromHost(); - } - return false; - } - - return true; -} - -bool TlsCryptographSecureTransport::startHandshake() -{ - Q_ASSERT(context); - Q_ASSERT(q); - Q_ASSERT(d); - - auto *plainSocket = d->plainTcpSocket(); - Q_ASSERT(plainSocket); - const auto mode = d->tlsMode(); - - OSStatus err = SSLHandshake(context); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << plainSocket << "SSLHandhake returned" << err; -#endif - - if (err == errSSLWouldBlock) { - // startHandshake has to be called again ... later. - return false; - } else if (err == errSSLServerAuthCompleted) { - // errSSLServerAuthCompleted is a define for errSSLPeerAuthCompleted, - // it works for both server/client modes. - // In future we'll evaluate peer's trust at this point, - // for now we just continue. - // if (!verifyPeerTrust()) - // ... - return startHandshake(); - } else if (err == errSSLClientCertRequested) { - Q_ASSERT(mode == QSslSocket::SslClientMode); - QString errorDescription; - QAbstractSocket::SocketError errorCode = QAbstractSocket::UnknownSocketError; - // setSessionCertificate does not fail if we have no certificate. - // Failure means a real error (invalid certificate, no private key, etc). - if (!setSessionCertificate(errorDescription, errorCode)) { - d->setErrorAndEmit(errorCode, errorDescription); - renegotiating = false; - return false; - } else { - // We try to resume a handshake, even if have no - // local certificates ... (up to server to deal with our failure). - return startHandshake(); - } - } else if (err != errSecSuccess) { - if (err == errSSLBadCert && canIgnoreTrustVerificationFailure()) { - // We're on the server side and client did not provide any - // certificate. This is the new 'nice' error returned by - // Security Framework after it was recently updated. - return startHandshake(); - } - - renegotiating = false; - d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - QStringLiteral("SSLHandshake failed: %1").arg(err)); - plainSocket->disconnectFromHost(); - return false; - } - - // Connection aborted during handshake phase. - if (q->state() != QAbstractSocket::ConnectedState) { - qCDebug(lcTlsBackend) << "connection aborted"; - renegotiating = false; - return false; - } - - // check protocol version ourselves, as Secure Transport does not enforce - // the requested min / max versions. - if (!verifySessionProtocol()) { - d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, QStringLiteral("Protocol version mismatch")); - plainSocket->disconnectFromHost(); - renegotiating = false; - return false; - } - - if (verifyPeerTrust()) { - continueHandshake(); - renegotiating = false; - return true; - } else { - renegotiating = false; - return false; - } -} - -bool TlsCryptographSecureTransport::isHandshakeComplete() const -{ - Q_ASSERT(q); - return q->isEncrypted() && !renegotiating; -} - -QList<QSslError> TlsCryptographSecureTransport::tlsErrors() const -{ - return sslErrors; -} - -} // namespace QTlsPrivate - -void QSslSocketPrivate::registerAdHocFactory() -{ - // TLSTODO: this is a temporary solution, waiting for - // backends to move to ... plugins. - if (!backendSecureTransport()) - qCWarning(lcTlsBackend, "Failed to create backend factory"); -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qtls_st_p.h b/src/network/ssl/qtls_st_p.h deleted file mode 100644 index 42c0ad622f..0000000000 --- a/src/network/ssl/qtls_st_p.h +++ /dev/null @@ -1,141 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org> -** 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 QTLS_ST_P_H -#define QTLS_ST_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of the QtNetwork library. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include <QtNetwork/private/qtnetworkglobal_p.h> - -#include "qtlsbackend_st_p.h" - -#include <QtCore/qobject.h> -#include <QtCore/qstring.h> -#include <QtCore/qglobal.h> -#include <QtCore/qlist.h> - -#include "qabstractsocket.h" -#include "qsslsocket_p.h" - -#include <Security/Security.h> -#include <Security/SecureTransport.h> - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -class QSecureTransportContext -{ -public: - explicit QSecureTransportContext(SSLContextRef context); - ~QSecureTransportContext(); - - operator SSLContextRef () const; - void reset(SSLContextRef newContext); -private: - SSLContextRef context; - - Q_DISABLE_COPY_MOVE(QSecureTransportContext) -}; - -class TlsCryptographSecureTransport : public TlsCryptograph -{ -public: - TlsCryptographSecureTransport(); - ~TlsCryptographSecureTransport() override; - - void init(QSslSocket *qObj, QSslSocketPrivate *dObj) override; - void continueHandshake() override; - void disconnected() override; - void disconnectFromHost() override; - QSslCipher sessionCipher() const override; - QSsl::SslProtocol sessionProtocol() const override; - void startClientEncryption() override; - void startServerEncryption() override; - void transmit() override; - QList<QSslError> tlsErrors() const override; - - SSLCipherSuite SSLCipherSuite_from_QSslCipher(const QSslCipher &ciph); - -private: - // SSL context management/properties: - bool initSslContext(); - void destroySslContext(); - bool setSessionCertificate(QString &errorDescription, - QAbstractSocket::SocketError &errorCode); - bool setSessionProtocol(); - // Aux. functions to do a verification during handshake phase: - bool canIgnoreTrustVerificationFailure() const; - bool verifySessionProtocol() const; - bool verifyPeerTrust(); - - bool checkSslErrors(); - bool startHandshake(); - - bool isHandshakeComplete() const; - - // IO callbacks: - static OSStatus ReadCallback(TlsCryptographSecureTransport *socket, char *data, size_t *dataLength); - static OSStatus WriteCallback(TlsCryptographSecureTransport *plainSocket, const char *data, size_t *dataLength); - - QSecureTransportContext context; - bool renegotiating = false; - QSslSocket *q = nullptr; - QSslSocketPrivate *d = nullptr; - bool shutdown = false; - QList<QSslError> sslErrors; - - Q_DISABLE_COPY_MOVE(TlsCryptographSecureTransport) -}; - -} // namespace QTlsPrivate - -QT_END_NAMESPACE - -#endif // QTLS_ST_P_H diff --git a/src/network/ssl/qtlsbackend.cpp b/src/network/ssl/qtlsbackend.cpp index 079cbccbc0..4c412d436f 100644 --- a/src/network/ssl/qtlsbackend.cpp +++ b/src/network/ssl/qtlsbackend.cpp @@ -46,8 +46,6 @@ #include "qsslcipher_p.h" #include "qsslkey_p.h" #include "qsslkey.h" -#else -#include "qtlsbackend_cert_p.h" #endif #include "qssl_p.h" @@ -63,7 +61,7 @@ QT_BEGIN_NAMESPACE Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, - (QTlsBackend_iid, QStringLiteral("/tlsbackends"))) + (QTlsBackend_iid, QStringLiteral("/tls"))) namespace { @@ -104,14 +102,6 @@ public: while (loader->instance(index)) ++index; - // 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; } @@ -244,6 +234,13 @@ bool TlsCryptograph::isMatchingHostname(const QString &cn, const QString &hostna return QSslSocketPrivate::isMatchingHostname(cn, hostname); } +void TlsCryptograph::setErrorAndEmit(QSslSocketPrivate *d, QAbstractSocket::SocketError errorCode, + const QString &errorDescription) const +{ + Q_ASSERT(d); + d->setErrorAndEmit(errorCode, errorDescription); +} + #endif // QT_CONFIG(ssl) #if QT_CONFIG(dtls) @@ -255,7 +252,8 @@ DtlsBase::~DtlsBase() = default; const QString QTlsBackend::builtinBackendNames[] = { QStringLiteral("schannel"), QStringLiteral("securetransport"), - QStringLiteral("openssl") + QStringLiteral("openssl"), + QStringLiteral("cert-only") }; QTlsBackend::QTlsBackend() @@ -436,18 +434,25 @@ QList<QString> QTlsBackend::availableBackendNames() QString QTlsBackend::defaultBackendName() { - // We prefer native as default: + // We prefer OpenSSL as default: const auto names = availableBackendNames(); - auto name = builtinBackendNames[nameIndexSchannel]; + auto name = builtinBackendNames[nameIndexOpenSSL]; if (names.contains(name)) return name; - name = builtinBackendNames[nameIndexSecureTransport]; + name = builtinBackendNames[nameIndexSchannel]; if (names.contains(name)) return name; - name = builtinBackendNames[nameIndexOpenSSL]; + name = builtinBackendNames[nameIndexSecureTransport]; if (names.contains(name)) return name; + const auto pos = std::find_if(names.begin(), names.end(), [](const auto &name) { + return name != builtinBackendNames[nameIndexCertOnly]; + }); + + if (pos != names.end()) + return *pos; + if (names.size()) return names[0]; @@ -787,6 +792,16 @@ void QTlsBackend::setEphemeralKey(QSslSocketPrivate *d, const QSslKey &key) d->configuration.ephemeralServerKey = key; } +void QTlsBackend::forceAutotestSecurityLevel() +{ +} + +Q_NETWORK_EXPORT void qt_ForceTlsSecurityLevel() +{ + if (auto *backend = QSslSocketPrivate::tlsBackendInUse()) + backend->forceAutotestSecurityLevel(); +} + #endif // QT_CONFIG(ssl) QT_END_NAMESPACE diff --git a/src/network/ssl/qtlsbackend_cert.cpp b/src/network/ssl/qtlsbackend_cert.cpp deleted file mode 100644 index f541381ecb..0000000000 --- a/src/network/ssl/qtlsbackend_cert.cpp +++ /dev/null @@ -1,96 +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_cert_p.h" - -#ifdef QT_NO_SSL - -#include "qx509_generic_p.h" - -#include <qssl.h> - -#include <qlist.h> - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.cert-only"); - -QString QTlsBackendCertOnly::backendName() const -{ - return QStringLiteral("cert-only"); -} - - -QList<QSsl::SslProtocol> QTlsBackendCertOnly::supportedProtocols() const -{ - return {}; -} - -QList<QSsl::SupportedFeature> QTlsBackendCertOnly::supportedFeatures() const -{ - return {}; -} - -QList<QSsl::ImplementedClass> QTlsBackendCertOnly::implementedClasses() const -{ - QList<QSsl::ImplementedClass> classes; - classes << QSsl::ImplementedClass::Certificate; - - return classes; -} - -QTlsPrivate::X509Certificate *QTlsBackendCertOnly::createCertificate() const -{ - return new QTlsPrivate::X509CertificateGeneric; -} - -QTlsPrivate::X509PemReaderPtr QTlsBackendCertOnly::X509PemReader() const -{ - return QTlsPrivate::X509CertificateGeneric::certificatesFromPem; -} - -QTlsPrivate::X509DerReaderPtr QTlsBackendCertOnly::X509DerReader() const -{ - return QTlsPrivate::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 deleted file mode 100644 index 4a010dbf79..0000000000 --- a/src/network/ssl/qtlsbackend_cert_p.h +++ /dev/null @@ -1,83 +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$ -** -****************************************************************************/ - -#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 <private/qtnetworkglobal_p.h> - -#include "qtlsbackend_p.h" - -#include <QtCore/qglobal.h> - -#ifdef QT_NO_SSL - -QT_BEGIN_NAMESPACE - -class QTlsBackendCertOnly final : public QTlsBackend -{ -public: -private: - QString backendName() const override; - - QList<QSsl::SslProtocol> supportedProtocols() const override; - QList<QSsl::SupportedFeature> supportedFeatures() const override; - QList<QSsl::ImplementedClass> implementedClasses() const override; - - QTlsPrivate::X509Certificate *createCertificate() const override; - QTlsPrivate::X509PemReaderPtr X509PemReader() const override; - QTlsPrivate::X509DerReaderPtr X509DerReader() const override; -}; - -QT_END_NAMESPACE - -#endif // QT_NO_SSL - -#endif // QTLSBACKEND_CERT_P_H diff --git a/src/network/ssl/qtlsbackend_openssl.cpp b/src/network/ssl/qtlsbackend_openssl.cpp deleted file mode 100644 index ef4aab6283..0000000000 --- a/src/network/ssl/qtlsbackend_openssl.cpp +++ /dev/null @@ -1,630 +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 "qtls_openssl_p.h" -#include "qsslcipher_p.h" -//#include "qsslsocket_p.h" -#include "qsslcipher.h" - -#if QT_CONFIG(dtls) -#include "qdtls_openssl_p.h" -#endif // QT_CONFIG(dtls) - -#include "qsslsocket_openssl_symbols_p.h" -#include "qopenssl_p.h" - -#include <qssl.h> - -#include <qdir.h> -#include <qdiriterator.h> -#include <qlist.h> -#include <qmutex.h> -#include <qscopeguard.h> - -#include <algorithm> - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.ossl"); - -Q_GLOBAL_STATIC(QRecursiveMutex, qt_opensslInitMutex) - -static void q_loadCiphersForConnection(SSL *connection, QList<QSslCipher> &ciphers, - QList<QSslCipher> &defaultCiphers) -{ - Q_ASSERT(connection); - - STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(connection); - for (int i = 0; i < q_sk_SSL_CIPHER_num(supportedCiphers); ++i) { - if (SSL_CIPHER *cipher = q_sk_SSL_CIPHER_value(supportedCiphers, i)) { - const auto ciph = QTlsBackendOpenSSL::qt_OpenSSL_cipher_to_QSslCipher(cipher); - if (!ciph.isNull()) { - // Unconditionally exclude ADH and AECDH ciphers since they offer no MITM protection - if (!ciph.name().toLower().startsWith(QLatin1String("adh")) && - !ciph.name().toLower().startsWith(QLatin1String("exp-adh")) && - !ciph.name().toLower().startsWith(QLatin1String("aecdh"))) { - ciphers << ciph; - - if (ciph.usedBits() >= 128) - defaultCiphers << ciph; - } - } - } - } -} - -bool QTlsBackendOpenSSL::s_libraryLoaded = false; -bool QTlsBackendOpenSSL::s_loadedCiphersAndCerts = false; -int QTlsBackendOpenSSL::s_indexForSSLExtraData = -1; - -QString QTlsBackendOpenSSL::getErrorsFromOpenSsl() -{ - QString errorString; - char buf[256] = {}; // OpenSSL docs claim both 120 and 256; use the larger. - unsigned long errNum; - while ((errNum = q_ERR_get_error())) { - if (!errorString.isEmpty()) - errorString.append(QLatin1String(", ")); - q_ERR_error_string_n(errNum, buf, sizeof buf); - errorString.append(QString::fromLatin1(buf)); // error is ascii according to man ERR_error_string - } - return errorString; -} - -void QTlsBackendOpenSSL::logAndClearErrorQueue() -{ - const auto errors = getErrorsFromOpenSsl(); - if (errors.size()) - qCWarning(lcTlsBackend) << "Discarding errors:" << errors; -} - -void QTlsBackendOpenSSL::clearErrorQueue() -{ - const auto errs = getErrorsFromOpenSsl(); - Q_UNUSED(errs); -} - -bool QTlsBackendOpenSSL::ensureLibraryLoaded() -{ - if (!q_resolveOpenSslSymbols()) - return false; - - const QMutexLocker locker(qt_opensslInitMutex()); - - if (!s_libraryLoaded) { - // Initialize OpenSSL. - if (q_OPENSSL_init_ssl(0, nullptr) != 1) - return false; - - if (q_OpenSSL_version_num() < 0x10101000L) { - qCWarning(lcTlsBackend, "QSslSocket: OpenSSL >= 1.1.1 is required; %s was found instead", q_OpenSSL_version(OPENSSL_VERSION)); - return false; - } - - q_SSL_load_error_strings(); - q_OpenSSL_add_all_algorithms(); - - s_indexForSSLExtraData = q_CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL, 0L, nullptr, nullptr, - nullptr, nullptr); - - // Initialize OpenSSL's random seed. - if (!q_RAND_status()) { - qWarning("Random number generator not seeded, disabling SSL support"); - return false; - } - - s_libraryLoaded = true; - } - - return true; -} - -QString QTlsBackendOpenSSL::backendName() const -{ - return builtinBackendNames[nameIndexOpenSSL]; -} - -bool QTlsBackendOpenSSL::isValid() const -{ - return ensureLibraryLoaded(); -} - -long QTlsBackendOpenSSL::tlsLibraryVersionNumber() const -{ - return q_OpenSSL_version_num(); -} - -QString QTlsBackendOpenSSL::tlsLibraryVersionString() const -{ - const char *versionString = q_OpenSSL_version(OPENSSL_VERSION); - if (!versionString) - return QString(); - - return QString::fromLatin1(versionString); -} - -long QTlsBackendOpenSSL::tlsLibraryBuildVersionNumber() const -{ - return OPENSSL_VERSION_NUMBER; -} - -QString QTlsBackendOpenSSL::tlsLibraryBuildVersionString() const -{ - // Using QStringLiteral to store the version string as unicode and - // avoid false positives from Google searching the playstore for old - // SSL versions. See QTBUG-46265 - return QStringLiteral(OPENSSL_VERSION_TEXT); -} - -void QTlsBackendOpenSSL::ensureInitialized() const -{ - // Old qsslsocket_openssl calls supportsSsl() (which means - // library found and symbols resolved, this already assured - // by the fact we end up in this function (isValid() returned - // true for the backend, see its code). The qsslsocket_openssl - // proceedes with loading certificate, ciphers and elliptic - // curves. - ensureCiphersAndCertsLoaded(); -} - -void QTlsBackendOpenSSL::ensureCiphersAndCertsLoaded() const -{ - const QMutexLocker locker(qt_opensslInitMutex()); - - if (s_loadedCiphersAndCerts) - return; - s_loadedCiphersAndCerts = true; - - resetDefaultCiphers(); - resetDefaultEllipticCurves(); - -#if QT_CONFIG(library) - //load symbols needed to receive certificates from system store -#if defined(Q_OS_QNX) - QSslSocketPrivate::setRootCertOnDemandLoadingSupported(true); -#elif defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) - // check whether we can enable on-demand root-cert loading (i.e. check whether the sym links are there) - QList<QByteArray> dirs = QSslSocketPrivate::unixRootCertDirectories(); - QStringList symLinkFilter; - symLinkFilter << QLatin1String("[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].[0-9]"); - for (int a = 0; a < dirs.count(); ++a) { - QDirIterator iterator(QLatin1String(dirs.at(a)), symLinkFilter, QDir::Files); - if (iterator.hasNext()) { - QSslSocketPrivate::setRootCertOnDemandLoadingSupported(true); - break; - } - } -#endif -#endif // QT_CONFIG(library) - // if on-demand loading was not enabled, load the certs now - if (!QSslSocketPrivate::rootCertOnDemandLoadingSupported()) - setDefaultCaCertificates(systemCaCertificates()); -#ifdef Q_OS_WIN - //Enabled for fetching additional root certs from windows update on windows. - //This flag is set false by setDefaultCaCertificates() indicating the app uses - //its own cert bundle rather than the system one. - //Same logic that disables the unix on demand cert loading. - //Unlike unix, we do preload the certificates from the cert store. - QSslSocketPrivate::setRootCertOnDemandLoadingSupported(true); -#endif -} - -void QTlsBackendOpenSSL::resetDefaultCiphers() -{ - SSL_CTX *myCtx = q_SSL_CTX_new(q_TLS_client_method()); - // Note, we assert, not just silently return/bail out early: - // this should never happen and problems with OpenSSL's initialization - // must be caught before this (see supportsSsl()). - Q_ASSERT(myCtx); - SSL *mySsl = q_SSL_new(myCtx); - Q_ASSERT(mySsl); - - QList<QSslCipher> ciphers; - QList<QSslCipher> defaultCiphers; - - q_loadCiphersForConnection(mySsl, ciphers, defaultCiphers); - - q_SSL_CTX_free(myCtx); - q_SSL_free(mySsl); - - setDefaultSupportedCiphers(ciphers); - setDefaultCiphers(defaultCiphers); - -#if QT_CONFIG(dtls) - ciphers.clear(); - defaultCiphers.clear(); - myCtx = q_SSL_CTX_new(q_DTLS_client_method()); - if (myCtx) { - mySsl = q_SSL_new(myCtx); - if (mySsl) { - q_loadCiphersForConnection(mySsl, ciphers, defaultCiphers); - setDefaultDtlsCiphers(defaultCiphers); - q_SSL_free(mySsl); - } - q_SSL_CTX_free(myCtx); - } -#endif // dtls -} - -QList<QSsl::SslProtocol> QTlsBackendOpenSSL::supportedProtocols() const -{ - QList<QSsl::SslProtocol> protocols; - - protocols << QSsl::AnyProtocol; - protocols << QSsl::SecureProtocols; - protocols << QSsl::TlsV1_0; - protocols << QSsl::TlsV1_0OrLater; - protocols << QSsl::TlsV1_1; - protocols << QSsl::TlsV1_1OrLater; - protocols << QSsl::TlsV1_2; - protocols << QSsl::TlsV1_2OrLater; - -#ifdef TLS1_3_VERSION - protocols << QSsl::TlsV1_3; - protocols << QSsl::TlsV1_3OrLater; -#endif // TLS1_3_VERSION - -#if QT_CONFIG(dtls) - protocols << QSsl::DtlsV1_0; - protocols << QSsl::DtlsV1_0OrLater; - protocols << QSsl::DtlsV1_2; - protocols << QSsl::DtlsV1_2OrLater; -#endif // dtls - - return protocols; -} - -QList<QSsl::SupportedFeature> QTlsBackendOpenSSL::supportedFeatures() const -{ - QList<QSsl::SupportedFeature> features; - - features << QSsl::SupportedFeature::CertificateVerification; - features << QSsl::SupportedFeature::ClientSideAlpn; - features << QSsl::SupportedFeature::ServerSideAlpn; - features << QSsl::SupportedFeature::Ocsp; - features << QSsl::SupportedFeature::Psk; - features << QSsl::SupportedFeature::SessionTicket; - features << QSsl::SupportedFeature::Alerts; - - return features; -} - -QList<QSsl::ImplementedClass> QTlsBackendOpenSSL::implementedClasses() const -{ - QList<QSsl::ImplementedClass> classes; - - classes << QSsl::ImplementedClass::Key; - classes << QSsl::ImplementedClass::Certificate; - classes << QSsl::ImplementedClass::Socket; - classes << QSsl::ImplementedClass::Dtls; - classes << QSsl::ImplementedClass::EllipticCurve; - classes << QSsl::ImplementedClass::DiffieHellman; - - return classes; -} - -QTlsPrivate::TlsKey *QTlsBackendOpenSSL::createKey() const -{ - return new QTlsPrivate::TlsKeyOpenSSL; -} - -QTlsPrivate::X509Certificate *QTlsBackendOpenSSL::createCertificate() const -{ - return new QTlsPrivate::X509CertificateOpenSSL; -} - -namespace QTlsPrivate { - -// TLSTODO: remove. -#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) -QList<QByteArray> fetchSslCertificateData(); -#endif - -QList<QSslCertificate> systemCaCertificates(); - -#ifndef Q_OS_DARWIN -QList<QSslCertificate> systemCaCertificates() -{ -#ifdef QSSLSOCKET_DEBUG - QElapsedTimer timer; - timer.start(); -#endif - QList<QSslCertificate> systemCerts; -#if defined(Q_OS_WIN) - HCERTSTORE hSystemStore; - hSystemStore = CertOpenSystemStoreW(0, L"ROOT"); - if (hSystemStore) { - PCCERT_CONTEXT pc = nullptr; - while (1) { - pc = CertFindCertificateInStore(hSystemStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY, nullptr, pc); - if (!pc) - break; - QByteArray der(reinterpret_cast<const char *>(pc->pbCertEncoded), - static_cast<int>(pc->cbCertEncoded)); - QSslCertificate cert(der, QSsl::Der); - systemCerts.append(cert); - } - CertCloseStore(hSystemStore, 0); - } -#elif defined(Q_OS_UNIX) - QSet<QString> certFiles; - QDir currentDir; - QStringList nameFilters; - QList<QByteArray> directories; - QSsl::EncodingFormat platformEncodingFormat; -# ifndef Q_OS_ANDROID - directories = QSslSocketPrivate::unixRootCertDirectories(); - nameFilters << QLatin1String("*.pem") << QLatin1String("*.crt"); - platformEncodingFormat = QSsl::Pem; -# else - // Q_OS_ANDROID - QByteArray ministroPath = qgetenv("MINISTRO_SSL_CERTS_PATH"); // Set by Ministro - directories << ministroPath; - nameFilters << QLatin1String("*.der"); - platformEncodingFormat = QSsl::Der; -# ifndef Q_OS_ANDROID_EMBEDDED - if (ministroPath.isEmpty()) { - QList<QByteArray> certificateData = fetchSslCertificateData(); - for (int i = 0; i < certificateData.size(); ++i) { - systemCerts.append(QSslCertificate::fromData(certificateData.at(i), QSsl::Der)); - } - } else -# endif //Q_OS_ANDROID_EMBEDDED -# endif //Q_OS_ANDROID - { - currentDir.setNameFilters(nameFilters); - for (int a = 0; a < directories.count(); a++) { - currentDir.setPath(QLatin1String(directories.at(a))); - QDirIterator it(currentDir); - while (it.hasNext()) { - it.next(); - // use canonical path here to not load the same certificate twice if symlinked - certFiles.insert(it.fileInfo().canonicalFilePath()); - } - } - for (const QString& file : qAsConst(certFiles)) - systemCerts.append(QSslCertificate::fromPath(file, platformEncodingFormat)); -# ifndef Q_OS_ANDROID - systemCerts.append(QSslCertificate::fromPath(QLatin1String("/etc/pki/tls/certs/ca-bundle.crt"), QSsl::Pem)); // Fedora, Mandriva - systemCerts.append(QSslCertificate::fromPath(QLatin1String("/usr/local/share/certs/ca-root-nss.crt"), QSsl::Pem)); // FreeBSD's ca_root_nss -# endif - } -#endif -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackend) << "systemCaCertificates retrieval time " << timer.elapsed() << "ms"; - qCDebug(lcTlsBackend) << "imported " << systemCerts.count() << " certificates"; -#endif - - return systemCerts; -} -#endif // !Q_OS_DARWIN -} // namespace QTlsPrivate - -QList<QSslCertificate> QTlsBackendOpenSSL::systemCaCertificates() const -{ - return QTlsPrivate::systemCaCertificates(); -} - -QTlsPrivate::DtlsCookieVerifier *QTlsBackendOpenSSL::createDtlsCookieVerifier() const -{ -#if QT_CONFIG(dtls) - return new QDtlsClientVerifierOpenSSL; -#else - qCWarning(lcTlsBackend, "Feature 'dtls' is disabled, cannot verify DTLS cookies"); - return nullptr; -#endif // QT_CONFIG(dtls) -} - -QTlsPrivate::TlsCryptograph *QTlsBackendOpenSSL::createTlsCryptograph() const -{ - return new QTlsPrivate::TlsCryptographOpenSSL; -} - -QTlsPrivate::DtlsCryptograph *QTlsBackendOpenSSL::createDtlsCryptograph(QDtls *q, int mode) const -{ -#if QT_CONFIG(dtls) - return new QDtlsPrivateOpenSSL(q, QSslSocket::SslMode(mode)); -#else - Q_UNUSED(q); - Q_UNUSED(mode); - qCWarning(lcTlsBackend, "Feature 'dtls' is disabled, cannot encrypt UDP datagrams"); - return nullptr; -#endif // QT_CONFIG(dtls) -} - -QTlsPrivate::X509ChainVerifyPtr QTlsBackendOpenSSL::X509Verifier() const -{ - return QTlsPrivate::X509CertificateOpenSSL::verify; -} - -QTlsPrivate::X509PemReaderPtr QTlsBackendOpenSSL::X509PemReader() const -{ - return QTlsPrivate::X509CertificateOpenSSL::certificatesFromPem; -} - -QTlsPrivate::X509DerReaderPtr QTlsBackendOpenSSL::X509DerReader() const -{ - return QTlsPrivate::X509CertificateOpenSSL::certificatesFromDer; -} - -QTlsPrivate::X509Pkcs12ReaderPtr QTlsBackendOpenSSL::X509Pkcs12Reader() const -{ - return QTlsPrivate::X509CertificateOpenSSL::importPkcs12; -} - -QList<int> QTlsBackendOpenSSL::ellipticCurvesIds() const -{ - QList<int> ids; - -#ifndef OPENSSL_NO_EC - const size_t curveCount = q_EC_get_builtin_curves(nullptr, 0); - QVarLengthArray<EC_builtin_curve> builtinCurves(static_cast<int>(curveCount)); - - if (q_EC_get_builtin_curves(builtinCurves.data(), curveCount) == curveCount) { - ids.reserve(curveCount); - for (const auto &ec : builtinCurves) - ids.push_back(ec.nid); - } -#endif // OPENSSL_NO_EC - - return ids; -} - - int QTlsBackendOpenSSL::curveIdFromShortName(const QString &name) const - { - int nid = 0; - if (name.isEmpty()) - return nid; - - ensureInitialized(); // TLSTODO: check if it's needed! -#ifndef OPENSSL_NO_EC - const QByteArray curveNameLatin1 = name.toLatin1(); - nid = q_OBJ_sn2nid(curveNameLatin1.data()); - - if (nid == 0) - nid = q_EC_curve_nist2nid(curveNameLatin1.data()); -#endif // !OPENSSL_NO_EC - - return nid; - } - - int QTlsBackendOpenSSL::curveIdFromLongName(const QString &name) const - { - int nid = 0; - if (name.isEmpty()) - return nid; - - ensureInitialized(); - -#ifndef OPENSSL_NO_EC - const QByteArray curveNameLatin1 = name.toLatin1(); - nid = q_OBJ_ln2nid(curveNameLatin1.data()); -#endif - - return nid; - } - - QString QTlsBackendOpenSSL::shortNameForId(int id) const - { - QString result; - -#ifndef OPENSSL_NO_EC - if (id != 0) - result = QString::fromLatin1(q_OBJ_nid2sn(id)); -#endif - - return result; - } - -QString QTlsBackendOpenSSL::longNameForId(int id) const -{ - QString result; - -#ifndef OPENSSL_NO_EC - if (id != 0) - result = QString::fromLatin1(q_OBJ_nid2ln(id)); -#endif - - return result; -} - -// NIDs of named curves allowed in TLS as per RFCs 4492 and 7027, -// see also https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8 -static const int tlsNamedCurveNIDs[] = { - // RFC 4492 - NID_sect163k1, - NID_sect163r1, - NID_sect163r2, - NID_sect193r1, - NID_sect193r2, - NID_sect233k1, - NID_sect233r1, - NID_sect239k1, - NID_sect283k1, - NID_sect283r1, - NID_sect409k1, - NID_sect409r1, - NID_sect571k1, - NID_sect571r1, - - NID_secp160k1, - NID_secp160r1, - NID_secp160r2, - NID_secp192k1, - NID_X9_62_prime192v1, // secp192r1 - NID_secp224k1, - NID_secp224r1, - NID_secp256k1, - NID_X9_62_prime256v1, // secp256r1 - NID_secp384r1, - NID_secp521r1, - - // RFC 7027 - NID_brainpoolP256r1, - NID_brainpoolP384r1, - NID_brainpoolP512r1 -}; - -const size_t tlsNamedCurveNIDCount = sizeof(tlsNamedCurveNIDs) / sizeof(tlsNamedCurveNIDs[0]); - -bool QTlsBackendOpenSSL::isTlsNamedCurve(int id) const -{ - const int *const tlsNamedCurveNIDsEnd = tlsNamedCurveNIDs + tlsNamedCurveNIDCount; - return std::find(tlsNamedCurveNIDs, tlsNamedCurveNIDsEnd, id) != tlsNamedCurveNIDsEnd; -} - -QString QTlsBackendOpenSSL::msgErrorsDuringHandshake() -{ - return QSslSocket::tr("Error during SSL handshake: %1").arg(getErrorsFromOpenSsl()); -} - -QSslCipher QTlsBackendOpenSSL::qt_OpenSSL_cipher_to_QSslCipher(const SSL_CIPHER *cipher) -{ - Q_ASSERT(cipher); - char buf [256] = {}; - const QString desc = QString::fromLatin1(q_SSL_CIPHER_description(cipher, buf, sizeof(buf))); - int supportedBits = 0; - const int bits = q_SSL_CIPHER_get_bits(cipher, &supportedBits); - return createCiphersuite(desc, bits, supportedBits); -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qtlsbackend_openssl_p.h b/src/network/ssl/qtlsbackend_openssl_p.h deleted file mode 100644 index 1a02bf5bed..0000000000 --- a/src/network/ssl/qtlsbackend_openssl_p.h +++ /dev/null @@ -1,134 +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$ -** -****************************************************************************/ - -#ifndef QTLSBACKEND_OPENSSL_P_H -#define QTLSBACKEND_OPENSSL_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 <private/qtnetworkglobal_p.h> - -#include "qssldiffiehellmanparameters.h" -#include "qsslcertificate.h" -#include "qtlsbackend_p.h" - -#include <QtCore/qglobal.h> -#include <QtCore/qlist.h> - -#include <openssl/ssl.h> - -QT_BEGIN_NAMESPACE - -class QTlsBackendOpenSSL final : public QTlsBackend -{ -public: - - static QString getErrorsFromOpenSsl(); - static void logAndClearErrorQueue(); - static void clearErrorQueue(); - - static bool ensureLibraryLoaded(); - // Index used in SSL_get_ex_data to get the matching TlsCryptographerOpenSSL: - static bool s_libraryLoaded; - static bool s_loadedCiphersAndCerts; - static int s_indexForSSLExtraData; - - static QString msgErrorsDuringHandshake(); - static QSslCipher qt_OpenSSL_cipher_to_QSslCipher(const SSL_CIPHER *cipher); -private: - - QString backendName() const override; - bool isValid() const override; - long tlsLibraryVersionNumber() const override; - QString tlsLibraryVersionString() const override; - long tlsLibraryBuildVersionNumber() const override; - QString tlsLibraryBuildVersionString() const override; - - void ensureInitialized() const override; - void ensureCiphersAndCertsLoaded() const; - static void resetDefaultCiphers(); - - QList<QSsl::SslProtocol> supportedProtocols() const override; - QList<QSsl::SupportedFeature> supportedFeatures() const override; - QList<QSsl::ImplementedClass> implementedClasses() const override; - - // QSslKey: - QTlsPrivate::TlsKey *createKey() const override; - - // QSslCertificate: - QTlsPrivate::X509Certificate *createCertificate() const override; - QList<QSslCertificate> systemCaCertificates() const override; - - QTlsPrivate::TlsCryptograph *createTlsCryptograph() const override; - QTlsPrivate::DtlsCookieVerifier *createDtlsCookieVerifier() const override; - QTlsPrivate::DtlsCryptograph *createDtlsCryptograph(QDtls *q, int mode) const override; - - QTlsPrivate::X509ChainVerifyPtr X509Verifier() const override; - QTlsPrivate::X509PemReaderPtr X509PemReader() const override; - QTlsPrivate::X509DerReaderPtr X509DerReader() const override; - QTlsPrivate::X509Pkcs12ReaderPtr X509Pkcs12Reader() const override; - - // Elliptic curves: - QList<int> ellipticCurvesIds() const override; - int curveIdFromShortName(const QString &name) const override; - int curveIdFromLongName(const QString &name) const override; - QString shortNameForId(int cid) const override; - QString longNameForId(int cid) const override; - bool isTlsNamedCurve(int cid) const override; - - // DH parameters: - using DHParams = QSslDiffieHellmanParameters; - int dhParametersFromDer(const QByteArray &derData, QByteArray *data) const override; - int dhParametersFromPem(const QByteArray &pemData, QByteArray *data) const override; -}; - -QT_END_NAMESPACE - -#endif // QTLSBACKEND_OPENSSL_P_H - - diff --git a/src/network/ssl/qtlsbackend_p.h b/src/network/ssl/qtlsbackend_p.h index 4d2bc25300..1a6110b17b 100644 --- a/src/network/ssl/qtlsbackend_p.h +++ b/src/network/ssl/qtlsbackend_p.h @@ -226,6 +226,9 @@ public: static bool isMatchingHostname(const QSslCertificate &cert, const QString &peerName); static bool isMatchingHostname(const QString &cn, const QString &hostname); + + void setErrorAndEmit(QSslSocketPrivate *d, QAbstractSocket::SocketError errorCode, + const QString &errorDescription) const; }; #else class TlsCryptograph; @@ -371,6 +374,7 @@ public: static constexpr const int nameIndexSchannel = 0; static constexpr const int nameIndexSecureTransport = 1; static constexpr const int nameIndexOpenSSL = 2; + static constexpr const int nameIndexCertOnly = 3; static const QString builtinBackendNames[]; @@ -428,6 +432,8 @@ public: static void addTustedRoot(QSslSocketPrivate *d, const QSslCertificate &rootCert); // The next one - is a "very important" feature! Kidding ... static void setEphemeralKey(QSslSocketPrivate *d, const QSslKey &key); + + virtual void forceAutotestSecurityLevel(); #endif // QT_CONFIG(ssl) Q_DISABLE_COPY_MOVE(QTlsBackend) diff --git a/src/network/ssl/qtlsbackend_schannel_p.h b/src/network/ssl/qtlsbackend_schannel_p.h deleted file mode 100644 index ca1cb9e621..0000000000 --- a/src/network/ssl/qtlsbackend_schannel_p.h +++ /dev/null @@ -1,100 +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$ -** -****************************************************************************/ - -#ifndef QTLSBACKEND_ST_P_H -#define QTLSBACKEND_ST_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 <private/qtnetworkglobal_p.h> - -#include "qtlsbackend_p.h" - -#include <QtCore/qglobal.h> - - -QT_BEGIN_NAMESPACE - -class QSchannelBackend : public QTlsBackend -{ -public: - static void ensureInitializedImplementation(); - -private: - long tlsLibraryVersionNumber() const override; - QString tlsLibraryVersionString() const override; - long tlsLibraryBuildVersionNumber() const override; - QString tlsLibraryBuildVersionString() const override; - void ensureInitialized() const override; - - static void resetDefaultCiphers(); - - QString backendName() const override; - QList<QSsl::SslProtocol> supportedProtocols() const override; - QList<QSsl::SupportedFeature> supportedFeatures() const override; - QList<QSsl::ImplementedClass> implementedClasses() const override; - - QTlsPrivate::TlsKey *createKey() const override; - QTlsPrivate::X509Certificate *createCertificate() const override; - - QTlsPrivate::TlsCryptograph * createTlsCryptograph() const override; - - QList<QSslCertificate> systemCaCertificates() const override; - static QList<QSslCertificate> systemCaCertificatesImplementation(); - - QTlsPrivate::X509PemReaderPtr X509PemReader() const override; - QTlsPrivate::X509DerReaderPtr X509DerReader() const override; - - static bool s_loadedCiphersAndCerts; -}; - -QT_END_NAMESPACE - -#endif // QTLSBACKEND_ST_P_H - - diff --git a/src/network/ssl/qtlsbackend_st.cpp b/src/network/ssl/qtlsbackend_st.cpp deleted file mode 100644 index 7fc7692350..0000000000 --- a/src/network/ssl/qtlsbackend_st.cpp +++ /dev/null @@ -1,341 +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_st_p.h" -#include "qtlskey_st_p.h" -#include "qx509_st_p.h" -#include "qtls_st_p.h" - -#include <QtCore/qsysinfo.h> -#include <QtCore/qmutex.h> - -QT_BEGIN_NAMESPACE - -Q_GLOBAL_STATIC(QRecursiveMutex, qt_securetransport_mutex) - -Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.securetransport"); - -namespace QTlsPrivate { - -QList<QSslCertificate> systemCaCertificates(); // defined in qsslsocket_mac_shared.cpp - -SSLContextRef qt_createSecureTransportContext(QSslSocket::SslMode mode); - -QSslCipher QSslCipher_from_SSLCipherSuite(SSLCipherSuite cipher) -{ - QString name; - switch (cipher) { - // Sorted as in CipherSuite.h (and groupped by their RFC) - // TLS addenda using AES, per RFC 3268 - case TLS_RSA_WITH_AES_128_CBC_SHA: - name = QLatin1String("AES128-SHA"); - break; - case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: - name = QLatin1String("DHE-RSA-AES128-SHA"); - break; - case TLS_RSA_WITH_AES_256_CBC_SHA: - name = QLatin1String("AES256-SHA"); - break; - case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: - name = QLatin1String("DHE-RSA-AES256-SHA"); - break; - - // ECDSA addenda, RFC 4492 - case TLS_ECDH_ECDSA_WITH_NULL_SHA: - name = QLatin1String("ECDH-ECDSA-NULL-SHA"); - break; - case TLS_ECDH_ECDSA_WITH_RC4_128_SHA: - name = QLatin1String("ECDH-ECDSA-RC4-SHA"); - break; - case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: - name = QLatin1String("ECDH-ECDSA-DES-CBC3-SHA"); - break; - case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: - name = QLatin1String("ECDH-ECDSA-AES128-SHA"); - break; - case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: - name = QLatin1String("ECDH-ECDSA-AES256-SHA"); - break; - case TLS_ECDHE_ECDSA_WITH_NULL_SHA: - name = QLatin1String("ECDHE-ECDSA-NULL-SHA"); - break; - case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: - name = QLatin1String("ECDHE-ECDSA-RC4-SHA"); - break; - case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: - name = QLatin1String("ECDHE-ECDSA-DES-CBC3-SHA"); - break; - case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: - name = QLatin1String("ECDHE-ECDSA-AES128-SHA"); - break; - case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: - name = QLatin1String("ECDHE-ECDSA-AES256-SHA"); - break; - case TLS_ECDH_RSA_WITH_NULL_SHA: - name = QLatin1String("ECDH-RSA-NULL-SHA"); - break; - case TLS_ECDH_RSA_WITH_RC4_128_SHA: - name = QLatin1String("ECDH-RSA-RC4-SHA"); - break; - case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: - name = QLatin1String("ECDH-RSA-DES-CBC3-SHA"); - break; - case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: - name = QLatin1String("ECDH-RSA-AES128-SHA"); - break; - case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: - name = QLatin1String("ECDH-RSA-AES256-SHA"); - break; - case TLS_ECDHE_RSA_WITH_NULL_SHA: - name = QLatin1String("ECDHE-RSA-NULL-SHA"); - break; - case TLS_ECDHE_RSA_WITH_RC4_128_SHA: - name = QLatin1String("ECDHE-RSA-RC4-SHA"); - break; - case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: - name = QLatin1String("ECDHE-RSA-DES-CBC3-SHA"); - break; - case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: - name = QLatin1String("ECDHE-RSA-AES128-SHA"); - break; - case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: - name = QLatin1String("ECDHE-RSA-AES256-SHA"); - break; - - // TLS 1.2 addenda, RFC 5246 - case TLS_RSA_WITH_3DES_EDE_CBC_SHA: - name = QLatin1String("DES-CBC3-SHA"); - break; - case TLS_RSA_WITH_AES_128_CBC_SHA256: - name = QLatin1String("AES128-SHA256"); - break; - case TLS_RSA_WITH_AES_256_CBC_SHA256: - name = QLatin1String("AES256-SHA256"); - break; - case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: - name = QLatin1String("DHE-RSA-DES-CBC3-SHA"); - break; - case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: - name = QLatin1String("DHE-RSA-AES128-SHA256"); - break; - case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: - name = QLatin1String("DHE-RSA-AES256-SHA256"); - break; - - // Addendum from RFC 4279, TLS PSK - // all missing atm. - - // RFC 4785 - Pre-Shared Key (PSK) Ciphersuites with NULL Encryption - // all missing atm. - - // Addenda from rfc 5288 AES Galois Counter Mode (CGM) Cipher Suites for TLS - case TLS_RSA_WITH_AES_256_GCM_SHA384: - name = QLatin1String("AES256-GCM-SHA384"); - break; - - // RFC 5487 - PSK with SHA-256/384 and AES GCM - // all missing atm. - - // Addenda from rfc 5289 Elliptic Curve Cipher Suites with HMAC SHA-256/384 - case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: - name = QLatin1String("ECDHE-ECDSA-AES128-SHA256"); - break; - case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: - name = QLatin1String("ECDHE-ECDSA-AES256-SHA384"); - break; - case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: - name = QLatin1String("ECDH-ECDSA-AES128-SHA256"); - break; - case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: - name = QLatin1String("ECDH-ECDSA-AES256-SHA384"); - break; - case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: - name = QLatin1String("ECDHE-RSA-AES128-SHA256"); - break; - case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: - name = QLatin1String("ECDHE-RSA-AES256-SHA384"); - break; - case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: - name = QLatin1String("ECDH-RSA-AES128-SHA256"); - break; - case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: - name = QLatin1String("ECDH-RSA-AES256-SHA384"); - break; - - // Addenda from rfc 5289 Elliptic Curve Cipher Suites - // with SHA-256/384 and AES Galois Counter Mode (GCM) - case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: - name = QLatin1String("ECDHE-RSA-AES256-GCM-SHA384"); - break; - - default: - return {}; - } - - return QTlsBackend::createCiphersuite(name, QSsl::TlsV1_2, QLatin1String("TLSv1.2")); -} - -} // namespace QTlsPrivate - -bool QSecureTransportBackend::s_loadedCiphersAndCerts = false; - -QString QSecureTransportBackend::tlsLibraryVersionString() const -{ - return QLatin1String("Secure Transport, ") + QSysInfo::prettyProductName(); -} - -QString QSecureTransportBackend::tlsLibraryBuildVersionString() const -{ - return tlsLibraryVersionString(); -} - -void QSecureTransportBackend::ensureInitialized() const -{ - const QMutexLocker locker(qt_securetransport_mutex()); - if (s_loadedCiphersAndCerts) - return; - - // We have to set it before setDefaultSupportedCiphers, - // since this function can trigger static (global)'s initialization - // and as a result - recursive ensureInitialized call - // from QSslCertificatePrivate's ctor. - s_loadedCiphersAndCerts = true; - - const QTlsPrivate::QSecureTransportContext context(QTlsPrivate::qt_createSecureTransportContext(QSslSocket::SslClientMode)); - if (context) { - QList<QSslCipher> ciphers; - QList<QSslCipher> defaultCiphers; - - size_t numCiphers = 0; - // Fails only if any of parameters is null. - SSLGetNumberSupportedCiphers(context, &numCiphers); - QList<SSLCipherSuite> cfCiphers(numCiphers); - // Fails only if any of parameter is null or number of ciphers is wrong. - SSLGetSupportedCiphers(context, cfCiphers.data(), &numCiphers); - - for (size_t i = 0; i < size_t(cfCiphers.size()); ++i) { - const QSslCipher ciph(QTlsPrivate::QSslCipher_from_SSLCipherSuite(cfCiphers.at(i))); - if (!ciph.isNull()) { - ciphers << ciph; - if (ciph.usedBits() >= 128) - defaultCiphers << ciph; - } - } - - setDefaultSupportedCiphers(ciphers); - setDefaultCiphers(defaultCiphers); - - if (!QSslSocketPrivate::rootCertOnDemandLoadingSupported()) - setDefaultCaCertificates(systemCaCertificates()); - } else { - s_loadedCiphersAndCerts = false; - } -} - -QString QSecureTransportBackend::backendName() const -{ - return builtinBackendNames[nameIndexSecureTransport]; -} - -QTlsPrivate::TlsKey *QSecureTransportBackend::createKey() const -{ - return new QTlsPrivate::TlsKeySecureTransport; -} - -QTlsPrivate::X509Certificate *QSecureTransportBackend::createCertificate() const -{ - return new QTlsPrivate::X509CertificateSecureTransport; -} - -QList<QSslCertificate> QSecureTransportBackend::systemCaCertificates() const -{ - return QTlsPrivate::systemCaCertificates(); -} - -QList<QSsl::SslProtocol> QSecureTransportBackend::supportedProtocols() const -{ - QList<QSsl::SslProtocol> protocols; - - protocols << QSsl::AnyProtocol; - protocols << QSsl::SecureProtocols; - protocols << QSsl::TlsV1_0; - protocols << QSsl::TlsV1_0OrLater; - protocols << QSsl::TlsV1_1; - protocols << QSsl::TlsV1_1OrLater; - protocols << QSsl::TlsV1_2; - protocols << QSsl::TlsV1_2OrLater; - - return protocols; -} - -QList<QSsl::SupportedFeature> QSecureTransportBackend::supportedFeatures() const -{ - QList<QSsl::SupportedFeature> features; - features << QSsl::SupportedFeature::ClientSideAlpn; - - return features; -} - -QList<QSsl::ImplementedClass> QSecureTransportBackend::implementedClasses() const -{ - QList<QSsl::ImplementedClass> classes; - classes << QSsl::ImplementedClass::Socket; - classes << QSsl::ImplementedClass::Certificate; - classes << QSsl::ImplementedClass::Key; - - return classes; -} - -QTlsPrivate::X509PemReaderPtr QSecureTransportBackend::X509PemReader() const -{ - return QTlsPrivate::X509CertificateGeneric::certificatesFromPem; -} - -QTlsPrivate::X509DerReaderPtr QSecureTransportBackend::X509DerReader() const -{ - return QTlsPrivate::X509CertificateGeneric::certificatesFromDer; -} - -QTlsPrivate::TlsCryptograph *QSecureTransportBackend::createTlsCryptograph() const -{ - return new QTlsPrivate::TlsCryptographSecureTransport; -} - -QT_END_NAMESPACE - diff --git a/src/network/ssl/qtlsbackend_st_p.h b/src/network/ssl/qtlsbackend_st_p.h deleted file mode 100644 index b0f3050674..0000000000 --- a/src/network/ssl/qtlsbackend_st_p.h +++ /dev/null @@ -1,94 +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$ -** -****************************************************************************/ - -#ifndef QTLSBACKEND_ST_P_H -#define QTLSBACKEND_ST_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 <private/qtnetworkglobal_p.h> - -#include "qtlsbackend_p.h" - -#include <QtCore/qglobal.h> - - -QT_BEGIN_NAMESPACE - -class QSecureTransportBackend : public QTlsBackend -{ -private: - - QString tlsLibraryVersionString() const override; - virtual QString tlsLibraryBuildVersionString() const override; - virtual void ensureInitialized() const override; - - QString backendName() const override; - - QList<QSsl::SslProtocol> supportedProtocols() const override; - QList<QSsl::SupportedFeature> supportedFeatures() const override; - QList<QSsl::ImplementedClass> implementedClasses() const override; - - QTlsPrivate::TlsKey *createKey() const override; - QTlsPrivate::X509Certificate *createCertificate() const override; - - QList<QSslCertificate> systemCaCertificates() const override; - - QTlsPrivate::X509PemReaderPtr X509PemReader() const override; - QTlsPrivate::X509DerReaderPtr X509DerReader() const override; - - QTlsPrivate::TlsCryptograph *createTlsCryptograph() const override; - - static bool s_loadedCiphersAndCerts; -}; - -QT_END_NAMESPACE - -#endif // QTLSBACKEND_ST_P_H - - diff --git a/src/network/ssl/qtlskey_base.cpp b/src/network/ssl/qtlskey_base.cpp deleted file mode 100644 index 13ce063f30..0000000000 --- a/src/network/ssl/qtlskey_base.cpp +++ /dev/null @@ -1,133 +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 "qtlskey_base_p.h" -#include "qasn1element_p.h" - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -QByteArray TlsKeyBase::pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const -{ - QByteArray pem(der.toBase64()); - - const int lineWidth = 64; // RFC 1421 - const int newLines = pem.size() / lineWidth; - const bool rem = pem.size() % lineWidth; - - for (int i = 0; i < newLines; ++i) - pem.insert((i + 1) * lineWidth + i, '\n'); - if (rem) - pem.append('\n'); - - QByteArray extra; - if (!headers.isEmpty()) { - QMap<QByteArray, QByteArray>::const_iterator it = headers.constEnd(); - do { - --it; - extra += it.key() + ": " + it.value() + '\n'; - } while (it != headers.constBegin()); - extra += '\n'; - } - - if (isEncryptedPkcs8(der)) { - pem.prepend(pkcs8Header(true) + '\n' + extra); - pem.append(pkcs8Footer(true) + '\n'); - } else if (isPkcs8()) { - pem.prepend(pkcs8Header(false) + '\n' + extra); - pem.append(pkcs8Footer(false) + '\n'); - } else { - pem.prepend(pemHeader() + '\n' + extra); - pem.append(pemFooter() + '\n'); - } - - return pem; -} - -QByteArray TlsKeyBase::pkcs8Header(bool encrypted) -{ - return encrypted - ? QByteArrayLiteral("-----BEGIN ENCRYPTED PRIVATE KEY-----") - : QByteArrayLiteral("-----BEGIN PRIVATE KEY-----"); -} - -QByteArray TlsKeyBase::pkcs8Footer(bool encrypted) -{ - return encrypted - ? QByteArrayLiteral("-----END ENCRYPTED PRIVATE KEY-----") - : QByteArrayLiteral("-----END PRIVATE KEY-----"); -} - -bool TlsKeyBase::isEncryptedPkcs8(const QByteArray &der) -{ - static const QList<QByteArray> pbes1OIds { - // PKCS5 - { PKCS5_MD2_DES_CBC_OID }, { PKCS5_MD2_RC2_CBC_OID }, { PKCS5_MD5_DES_CBC_OID }, - { PKCS5_MD5_RC2_CBC_OID }, { PKCS5_SHA1_DES_CBC_OID }, { PKCS5_SHA1_RC2_CBC_OID }, - }; - QAsn1Element elem; - if (!elem.read(der) || elem.type() != QAsn1Element::SequenceType) - return false; - - const auto items = elem.toList(); - if (items.size() != 2 - || items[0].type() != QAsn1Element::SequenceType - || items[1].type() != QAsn1Element::OctetStringType) { - return false; - } - - const auto encryptionSchemeContainer = items[0].toList(); - if (encryptionSchemeContainer.size() != 2 - || encryptionSchemeContainer[0].type() != QAsn1Element::ObjectIdentifierType - || encryptionSchemeContainer[1].type() != QAsn1Element::SequenceType) { - return false; - } - - const QByteArray encryptionScheme = encryptionSchemeContainer[0].toObjectId(); - return encryptionScheme == PKCS5_PBES2_ENCRYPTION_OID - || pbes1OIds.contains(encryptionScheme) - || encryptionScheme.startsWith(PKCS12_OID); -} - -} // namespace QTlsPrivate - -QT_END_NAMESPACE - - diff --git a/src/network/ssl/qtlskey_base_p.h b/src/network/ssl/qtlskey_base_p.h deleted file mode 100644 index 6befed876c..0000000000 --- a/src/network/ssl/qtlskey_base_p.h +++ /dev/null @@ -1,112 +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$ -** -****************************************************************************/ - -#ifndef QTLSKEY_BASE_P_H -#define QTLSKEY_BASE_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 <private/qtnetworkglobal_p.h> - -#include <private/qtlsbackend_p.h> - -#include <qssl.h> - -#include <QtCore/qglobal.h> - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -// TLSTODO: Note, 'base' is supposed to move to plugins together with -// 'generic' and 'backendXXX'. -class TlsKeyBase : public TlsKey -{ -public: - TlsKeyBase(KeyType type = QSsl::PublicKey, KeyAlgorithm algorithm = QSsl::Opaque) - : keyType(type), - keyAlgorithm(algorithm) - { - } - - bool isNull() const override - { - return keyIsNull; - } - KeyType type() const override - { - return keyType; - } - KeyAlgorithm algorithm() const override - { - return keyAlgorithm; - } - bool isPkcs8 () const override - { - return false; - } - - QByteArray pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const override; - -protected: - static QByteArray pkcs8Header(bool encrypted); - static QByteArray pkcs8Footer(bool encrypted); - static bool isEncryptedPkcs8(const QByteArray &der); -public: - // TLSTODO: this public is quick fix needed by old _openssl classes - // will become non-public as soon as those classes fixed. - bool keyIsNull = true; - KeyType keyType = QSsl::PublicKey; - KeyAlgorithm keyAlgorithm = QSsl::Opaque; -}; - -} // namespace QTlsPrivate - -QT_END_NAMESPACE - -#endif // QTLSKEY_BASE_P_H diff --git a/src/network/ssl/qtlskey_generic.cpp b/src/network/ssl/qtlskey_generic.cpp deleted file mode 100644 index b0ab0bfa96..0000000000 --- a/src/network/ssl/qtlskey_generic.cpp +++ /dev/null @@ -1,884 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org> -** 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 "qtlskey_generic_p.h" -#include "qasn1element_p.h" -#include "qsslkey_p.h" - -#include <QtNetwork/qpassworddigestor.h> - -#include <QtCore/QMessageAuthenticationCode> -#include <QtCore/qcryptographichash.h> -#include <QtCore/qrandom.h> - -#include <QtCore/qdatastream.h> -#include <QtCore/qbytearray.h> -#include <QtCore/qdebug.h> -#include <QtCore/qmap.h> - -#include <cstring> - -QT_BEGIN_NAMESPACE - -// The code here is essentially what we had in qsslkey_qt.cpp before, with -// minimal changes/restructure. - -namespace QTlsPrivate { - -// OIDs of named curves allowed in TLS as per RFCs 4492 and 7027, -// see also https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8 -namespace { - -const quint8 bits_table[256] = { - 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, -}; - -using OidLengthMap = QMap<QByteArray, int>; - -OidLengthMap createOidMap() -{ - OidLengthMap oids; - oids.insert(oids.cend(), QByteArrayLiteral("1.2.840.10045.3.1.1"), 192); // secp192r1 a.k.a prime192v1 - oids.insert(oids.cend(), QByteArrayLiteral("1.2.840.10045.3.1.7"), 256); // secp256r1 a.k.a prime256v1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.1"), 193); // sect193r2 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.10"), 256); // secp256k1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.16"), 283); // sect283k1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.17"), 283); // sect283r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.26"), 233); // sect233k1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.27"), 233); // sect233r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.3"), 239); // sect239k1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.30"), 160); // secp160r2 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.31"), 192); // secp192k1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.32"), 224); // secp224k1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.33"), 224); // secp224r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.34"), 384); // secp384r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.35"), 521); // secp521r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.36"), 409); // sect409k1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.37"), 409); // sect409r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.38"), 571); // sect571k1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.39"), 571); // sect571r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.8"), 160); // secp160r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.9"), 160); // secp160k1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.36.3.3.2.8.1.1.11"), 384); // brainpoolP384r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.36.3.3.2.8.1.1.13"), 512); // brainpoolP512r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.36.3.3.2.8.1.1.7"), 256); // brainpoolP256r1 - return oids; -} - -} // Unnamed namespace. - -Q_GLOBAL_STATIC_WITH_ARGS(OidLengthMap, oidLengthMap, (createOidMap())) - -namespace { - -// Maps OIDs to the encryption cipher they specify -const QMap<QByteArray, QSslKeyPrivate::Cipher> oidCipherMap { - {DES_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::DesCbc}, - {DES_EDE3_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::DesEde3Cbc}, - // {PKCS5_MD2_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc}, // No MD2 - {PKCS5_MD5_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc}, - {PKCS5_SHA1_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc}, - // {PKCS5_MD2_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc}, // No MD2 - {PKCS5_MD5_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc}, - {PKCS5_SHA1_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc}, - {RC2_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Rc2Cbc} - // {RC5_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Rc5Cbc}, // No RC5 - // {AES128_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes128}, // no AES - // {AES192_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes192}, - // {AES256_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes256} -}; - -struct EncryptionData -{ - EncryptionData() = default; - - EncryptionData(QSslKeyPrivate::Cipher cipher, QByteArray key, QByteArray iv) - : initialized(true), cipher(cipher), key(key), iv(iv) - { - } - bool initialized = false; - QSslKeyPrivate::Cipher cipher; - QByteArray key; - QByteArray iv; -}; - -EncryptionData readPbes2(const QList<QAsn1Element> &element, const QByteArray &passPhrase) -{ - // RFC 8018: https://tools.ietf.org/html/rfc8018#section-6.2 - /*** Scheme: *** - * Sequence (scheme-specific info..) - * Sequence (key derivation info) - * Object Identifier (Key derivation algorithm (e.g. PBKDF2)) - * Sequence (salt) - * CHOICE (this entry can be either of the types it contains) - * Octet string (actual salt) - * Object identifier (Anything using this is deferred to a later version of PKCS #5) - * Integer (iteration count) - * Sequence (encryption algorithm info) - * Object identifier (identifier for the algorithm) - * Algorithm dependent, is covered in the switch further down - */ - - static const QMap<QByteArray, QCryptographicHash::Algorithm> pbes2OidHashFunctionMap { - // PBES2/PBKDF2 - {HMAC_WITH_SHA1, QCryptographicHash::Sha1}, - {HMAC_WITH_SHA224, QCryptographicHash::Sha224}, - {HMAC_WITH_SHA256, QCryptographicHash::Sha256}, - {HMAC_WITH_SHA512, QCryptographicHash::Sha512}, - {HMAC_WITH_SHA512_224, QCryptographicHash::Sha512}, - {HMAC_WITH_SHA512_256, QCryptographicHash::Sha512}, - {HMAC_WITH_SHA384, QCryptographicHash::Sha384} - }; - - // Values from their respective sections here: https://tools.ietf.org/html/rfc8018#appendix-B.2 - static const QMap<QSslKeyPrivate::Cipher, int> cipherKeyLengthMap { - {QSslKeyPrivate::Cipher::DesCbc, 8}, - {QSslKeyPrivate::Cipher::DesEde3Cbc, 24}, - // @note: variable key-length (https://tools.ietf.org/html/rfc8018#appendix-B.2.3) - {QSslKeyPrivate::Cipher::Rc2Cbc, 4} - // @todo: AES(, rc5?) - }; - - const QList<QAsn1Element> keyDerivationContainer = element[0].toList(); - if (keyDerivationContainer.size() != 2 - || keyDerivationContainer[0].type() != QAsn1Element::ObjectIdentifierType - || keyDerivationContainer[1].type() != QAsn1Element::SequenceType) { - return {}; - } - - const QByteArray keyDerivationAlgorithm = keyDerivationContainer[0].toObjectId(); - const auto keyDerivationParams = keyDerivationContainer[1].toList(); - - const auto encryptionAlgorithmContainer = element[1].toList(); - if (encryptionAlgorithmContainer.size() != 2 - || encryptionAlgorithmContainer[0].type() != QAsn1Element::ObjectIdentifierType) { - return {}; - } - - auto iterator = oidCipherMap.constFind(encryptionAlgorithmContainer[0].toObjectId()); - if (iterator == oidCipherMap.cend()) { - qWarning() - << "QSslKey: Unsupported encryption cipher OID:" << encryptionAlgorithmContainer[0].toObjectId() - << "\nFile a bug report to Qt (include the line above)."; - return {}; - } - - QSslKeyPrivate::Cipher cipher = *iterator; - QByteArray key; - QByteArray iv; - switch (cipher) { - case QSslKeyPrivate::Cipher::DesCbc: - case QSslKeyPrivate::Cipher::DesEde3Cbc: - // https://tools.ietf.org/html/rfc8018#appendix-B.2.1 (DES-CBC-PAD) - // https://tools.ietf.org/html/rfc8018#appendix-B.2.2 (DES-EDE3-CBC-PAD) - // @todo https://tools.ietf.org/html/rfc8018#appendix-B.2.5 (AES-CBC-PAD) - /*** Scheme: *** - * Octet string (IV) - */ - if (encryptionAlgorithmContainer[1].type() != QAsn1Element::OctetStringType) - return {}; - - // @note: All AES identifiers should be able to use this branch!! - iv = encryptionAlgorithmContainer[1].value(); - - if (iv.size() != 8) // @note: AES needs 16 bytes - return {}; - break; - case QSslKeyPrivate::Cipher::Rc2Cbc: { - // https://tools.ietf.org/html/rfc8018#appendix-B.2.3 - /*** Scheme: *** - * Sequence (rc2 parameters) - * Integer (rc2 parameter version) - * Octet string (IV) - */ - if (encryptionAlgorithmContainer[1].type() != QAsn1Element::SequenceType) - return {}; - const auto rc2ParametersContainer = encryptionAlgorithmContainer[1].toList(); - if ((rc2ParametersContainer.size() != 1 && rc2ParametersContainer.size() != 2) - || rc2ParametersContainer.back().type() != QAsn1Element::OctetStringType) { - return {}; - } - iv = rc2ParametersContainer.back().value(); - if (iv.size() != 8) - return {}; - break; - } // @todo(?): case (RC5 , AES) - case QSslKeyPrivate::Cipher::Aes128Cbc: - case QSslKeyPrivate::Cipher::Aes192Cbc: - case QSslKeyPrivate::Cipher::Aes256Cbc: - Q_UNREACHABLE(); - } - - if (Q_LIKELY(keyDerivationAlgorithm == PKCS5_PBKDF2_ENCRYPTION_OID)) { - // Definition: https://tools.ietf.org/html/rfc8018#appendix-A.2 - QByteArray salt; - if (keyDerivationParams[0].type() == QAsn1Element::OctetStringType) { - salt = keyDerivationParams[0].value(); - } else if (keyDerivationParams[0].type() == QAsn1Element::ObjectIdentifierType) { - Q_UNIMPLEMENTED(); - /* See paragraph from https://tools.ietf.org/html/rfc8018#appendix-A.2 - which ends with: "such facilities are deferred to a future version of PKCS #5" - */ - return {}; - } else { - return {}; - } - - // Iterations needed to derive the key - int iterationCount = keyDerivationParams[1].toInteger(); - // Optional integer - int keyLength = -1; - int vectorPos = 2; - if (keyDerivationParams.size() > vectorPos - && keyDerivationParams[vectorPos].type() == QAsn1Element::IntegerType) { - keyLength = keyDerivationParams[vectorPos].toInteger(nullptr); - ++vectorPos; - } else { - keyLength = cipherKeyLengthMap[cipher]; - } - - // Optional algorithm identifier (default: HMAC-SHA-1) - QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha1; - if (keyDerivationParams.size() > vectorPos - && keyDerivationParams[vectorPos].type() == QAsn1Element::SequenceType) { - const auto hashAlgorithmContainer = keyDerivationParams[vectorPos].toList(); - hashAlgorithm = pbes2OidHashFunctionMap[hashAlgorithmContainer.front().toObjectId()]; - Q_ASSERT(hashAlgorithmContainer[1].type() == QAsn1Element::NullType); - ++vectorPos; - } - Q_ASSERT(keyDerivationParams.size() == vectorPos); - - key = QPasswordDigestor::deriveKeyPbkdf2(hashAlgorithm, passPhrase, salt, iterationCount, keyLength); - } else { - qWarning() - << "QSslKey: Unsupported key derivation algorithm OID:" << keyDerivationAlgorithm - << "\nFile a bugreport to Qt (include the line above)."; - return {}; - } - return {cipher, key, iv}; -} - -// Maps OIDs to the hash function it specifies -const QMap<QByteArray, QCryptographicHash::Algorithm> pbes1OidHashFunctionMap { -#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 - // PKCS5 - //{PKCS5_MD2_DES_CBC_OID, QCryptographicHash::Md2}, No MD2 - //{PKCS5_MD2_RC2_CBC_OID, QCryptographicHash::Md2}, - {PKCS5_MD5_DES_CBC_OID, QCryptographicHash::Md5}, - {PKCS5_MD5_RC2_CBC_OID, QCryptographicHash::Md5}, -#endif - {PKCS5_SHA1_DES_CBC_OID, QCryptographicHash::Sha1}, - {PKCS5_SHA1_RC2_CBC_OID, QCryptographicHash::Sha1}, - // PKCS12 (unimplemented) - // {PKCS12_SHA1_RC4_128_OID, QCryptographicHash::Sha1}, // No RC4 - // {PKCS12_SHA1_RC4_40_OID, QCryptographicHash::Sha1}, - // @todo: lacking support. @note: there might be code to do this inside qsslsocket_mac... - // further note that more work may be required for the 3DES variations listed to be available. - // {PKCS12_SHA1_3KEY_3DES_CBC_OID, QCryptographicHash::Sha1}, - // {PKCS12_SHA1_2KEY_3DES_CBC_OID, QCryptographicHash::Sha1}, - // {PKCS12_SHA1_RC2_128_CBC_OID, QCryptographicHash::Sha1}, - // {PKCS12_SHA1_RC2_40_CBC_OID, QCryptographicHash::Sha1} -}; - -EncryptionData readPbes1(const QList<QAsn1Element> &element, const QByteArray &encryptionScheme, - const QByteArray &passPhrase) -{ - // RFC 8018: https://tools.ietf.org/html/rfc8018#section-6.1 - // Steps refer to this section: https://tools.ietf.org/html/rfc8018#section-6.1.2 - /*** Scheme: *** - * Sequence (PBE Parameter) - * Octet string (salt) - * Integer (iteration counter) - */ - // Step 1 - if (element.size() != 2 - || element[0].type() != QAsn1Element::ElementType::OctetStringType - || element[1].type() != QAsn1Element::ElementType::IntegerType) { - return {}; - } - QByteArray salt = element[0].value(); - if (salt.size() != 8) - return {}; - - int iterationCount = element[1].toInteger(); - if (iterationCount < 0) - return {}; - - // Step 2 - auto iterator = pbes1OidHashFunctionMap.constFind(encryptionScheme); - if (iterator == pbes1OidHashFunctionMap.cend()) { - // Qt was compiled with ONLY_SHA1 (or it's MD2) - return {}; - } - QCryptographicHash::Algorithm hashAlgorithm = *iterator; - QByteArray key = QPasswordDigestor::deriveKeyPbkdf1(hashAlgorithm, passPhrase, salt, iterationCount, 16); - if (key.size() != 16) - return {}; - - // Step 3 - QByteArray iv = key.right(8); // last 8 bytes are used as IV - key.truncate(8); // first 8 bytes are used for the key - - QSslKeyPrivate::Cipher cipher = oidCipherMap[encryptionScheme]; - // Steps 4-6 are done after returning - return {cipher, key, iv}; -} - -int curveBits(const QByteArray &oid) -{ - const int length = oidLengthMap->value(oid); - return length ? length : -1; -} - -int numberOfBits(const QByteArray &modulus) -{ - int bits = modulus.size() * 8; - for (int i = 0; i < modulus.size(); ++i) { - quint8 b = modulus[i]; - bits -= 8; - if (b != 0) { - bits += bits_table[b]; - break; - } - } - return bits; -} - -QByteArray deriveAesKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase, - const QByteArray &iv) -{ - // This is somewhat simplified and shortened version of what OpenSSL does. - // See, for example, EVP_BytesToKey for the "algorithm" itself and elsewhere - // in their code for what they pass as arguments to EVP_BytesToKey when - // deriving encryption keys (when reading/writing pems files with encrypted - // keys). - - Q_ASSERT(iv.size() >= 8); - - QCryptographicHash hash(QCryptographicHash::Md5); - - QByteArray data(passPhrase); - data.append(iv.data(), 8); // AKA PKCS5_SALT_LEN in OpenSSL. - - hash.addData(data); - - if (cipher == Cipher::Aes128Cbc) - return hash.result(); - - QByteArray key(hash.result()); - hash.reset(); - hash.addData(key); - hash.addData(data); - - if (cipher == Cipher::Aes192Cbc) - return key.append(hash.result().constData(), 8); - - return key.append(hash.result()); -} - -QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase, - const QByteArray &iv) -{ - QByteArray key; - QCryptographicHash hash(QCryptographicHash::Md5); - hash.addData(passPhrase); - hash.addData(iv); - switch (cipher) { - case Cipher::DesCbc: - key = hash.result().left(8); - break; - case Cipher::DesEde3Cbc: - key = hash.result(); - hash.reset(); - hash.addData(key); - hash.addData(passPhrase); - hash.addData(iv); - key += hash.result().left(8); - break; - case Cipher::Rc2Cbc: - key = hash.result(); - break; - case Cipher::Aes128Cbc: - case Cipher::Aes192Cbc: - case Cipher::Aes256Cbc: - return deriveAesKey(cipher, passPhrase, iv); - } - return key; -} - -int extractPkcs8KeyLength(const QList<QAsn1Element> &items, TlsKey *that) -{ - Q_ASSERT(items.size() == 3); - Q_ASSERT(that); - - int keyLength = -1; - - auto getName = [](QSsl::KeyAlgorithm algorithm) { - switch (algorithm){ - case QSsl::Rsa: return "RSA"; - case QSsl::Dsa: return "DSA"; - case QSsl::Dh: return "DH"; - case QSsl::Ec: return "EC"; - case QSsl::Opaque: return "Opaque"; - } - Q_UNREACHABLE(); - }; - - const auto pkcs8Info = items[1].toList(); - if (pkcs8Info.size() != 2 || pkcs8Info[0].type() != QAsn1Element::ObjectIdentifierType) - return -1; - const QByteArray value = pkcs8Info[0].toObjectId(); - if (value == RSA_ENCRYPTION_OID) { - if (Q_UNLIKELY(that->algorithm() != QSsl::Rsa)) { - // We could change the 'algorithm' of QSslKey here and continue loading, but - // this is not supported in the openssl back-end, so we'll fail here and give - // the user some feedback. - qWarning() << "QSslKey: Found RSA key when asked to use" << getName(that->algorithm()) - << "\nLoading will fail."; - return -1; - } - // Luckily it contains the 'normal' RSA-key format inside, so we can just recurse - // and read the key's info. - that->decodeDer(that->type(), that->algorithm(), items[2].value(), {}, true); - // The real info has been filled out in the call above, so return as if it was invalid - // to avoid overwriting the data. - return -1; - } else if (value == EC_ENCRYPTION_OID) { - if (Q_UNLIKELY(that->algorithm() != QSsl::Ec)) { - // As above for RSA. - qWarning() << "QSslKey: Found EC key when asked to use" << getName(that->algorithm()) - << "\nLoading will fail."; - return -1; - } - // I don't know where this is documented, but the elliptic-curve identifier has been - // moved into the "pkcs#8 wrapper", which is what we're interested in. - if (pkcs8Info[1].type() != QAsn1Element::ObjectIdentifierType) - return -1; - keyLength = curveBits(pkcs8Info[1].toObjectId()); - } else if (value == DSA_ENCRYPTION_OID) { - if (Q_UNLIKELY(that->algorithm() != QSsl::Dsa)) { - // As above for RSA. - qWarning() << "QSslKey: Found DSA when asked to use" << getName(that->algorithm()) - << "\nLoading will fail."; - return -1; - } - // DSA's structure is documented here: - // https://www.cryptsoft.com/pkcs11doc/STANDARD/v201-95.pdf in section 11.9. - if (pkcs8Info[1].type() != QAsn1Element::SequenceType) - return -1; - const auto dsaInfo = pkcs8Info[1].toList(); - if (dsaInfo.size() != 3 || dsaInfo[0].type() != QAsn1Element::IntegerType) - return -1; - keyLength = numberOfBits(dsaInfo[0].value()); - } else if (value == DH_ENCRYPTION_OID) { - if (Q_UNLIKELY(that->algorithm() != QSsl::Dh)) { - // As above for RSA. - qWarning() << "QSslKey: Found DH when asked to use" << getName(that->algorithm()) - << "\nLoading will fail."; - return -1; - } - // DH's structure is documented here: - // https://www.cryptsoft.com/pkcs11doc/STANDARD/v201-95.pdf in section 11.9. - if (pkcs8Info[1].type() != QAsn1Element::SequenceType) - return -1; - const auto dhInfo = pkcs8Info[1].toList(); - if (dhInfo.size() < 2 || dhInfo.size() > 3 || dhInfo[0].type() != QAsn1Element::IntegerType) - return -1; - keyLength = numberOfBits(dhInfo[0].value()); - } else { - // in case of unexpected formats: - qWarning() << "QSslKey: Unsupported PKCS#8 key algorithm:" << value - << "\nFile a bugreport to Qt (include the line above)."; - return -1; - } - - return keyLength; -} - -} // Unnamed namespace - -void TlsKeyGeneric::decodeDer(QSsl::KeyType type, QSsl::KeyAlgorithm algorithm, const QByteArray &der, - const QByteArray &passPhrase, bool deepClear) -{ - keyType = type; - keyAlgorithm = algorithm; - - clear(deepClear); - - if (der.isEmpty()) - return; - // decryptPkcs8 decrypts if necessary or returns 'der' unaltered - QByteArray decryptedDer = decryptPkcs8(der, passPhrase); - - QAsn1Element elem; - if (!elem.read(decryptedDer) || elem.type() != QAsn1Element::SequenceType) - return; - - if (type == QSsl::PublicKey) { - // key info - QDataStream keyStream(elem.value()); - if (!elem.read(keyStream) || elem.type() != QAsn1Element::SequenceType) - return; - const auto infoItems = elem.toList(); - if (infoItems.size() < 2 || infoItems[0].type() != QAsn1Element::ObjectIdentifierType) - return; - if (algorithm == QSsl::Rsa) { - if (infoItems[0].toObjectId() != RSA_ENCRYPTION_OID) - return; - // key data - if (!elem.read(keyStream) || elem.type() != QAsn1Element::BitStringType || elem.value().isEmpty()) - return; - if (!elem.read(elem.value().mid(1)) || elem.type() != QAsn1Element::SequenceType) - return; - if (!elem.read(elem.value()) || elem.type() != QAsn1Element::IntegerType) - return; - keyLength = numberOfBits(elem.value()); - } else if (algorithm == QSsl::Dsa) { - if (infoItems[0].toObjectId() != DSA_ENCRYPTION_OID) - return; - if (infoItems[1].type() != QAsn1Element::SequenceType) - return; - // key params - const auto params = infoItems[1].toList(); - if (params.isEmpty() || params[0].type() != QAsn1Element::IntegerType) - return; - keyLength = numberOfBits(params[0].value()); - } else if (algorithm == QSsl::Dh) { - if (infoItems[0].toObjectId() != DH_ENCRYPTION_OID) - return; - if (infoItems[1].type() != QAsn1Element::SequenceType) - return; - // key params - const auto params = infoItems[1].toList(); - if (params.isEmpty() || params[0].type() != QAsn1Element::IntegerType) - return; - keyLength = numberOfBits(params[0].value()); - } else if (algorithm == QSsl::Ec) { - if (infoItems[0].toObjectId() != EC_ENCRYPTION_OID) - return; - if (infoItems[1].type() != QAsn1Element::ObjectIdentifierType) - return; - keyLength = curveBits(infoItems[1].toObjectId()); - } - - } else { - const auto items = elem.toList(); - if (items.isEmpty()) - return; - - // version - if (items[0].type() != QAsn1Element::IntegerType) - return; - const QByteArray versionHex = items[0].value().toHex(); - - if (items.size() == 3 && items[1].type() == QAsn1Element::SequenceType - && items[2].type() == QAsn1Element::OctetStringType) { - if (versionHex != "00" && versionHex != "01") - return; - int pkcs8KeyLength = extractPkcs8KeyLength(items, this); - if (pkcs8KeyLength == -1) - return; - pkcs8 = true; - keyLength = pkcs8KeyLength; - } else if (algorithm == QSsl::Rsa) { - if (versionHex != "00") - return; - if (items.size() != 9 || items[1].type() != QAsn1Element::IntegerType) - return; - keyLength = numberOfBits(items[1].value()); - } else if (algorithm == QSsl::Dsa) { - if (versionHex != "00") - return; - if (items.size() != 6 || items[1].type() != QAsn1Element::IntegerType) - return; - keyLength = numberOfBits(items[1].value()); - } else if (algorithm == QSsl::Dh) { - if (versionHex != "00") - return; - if (items.size() < 5 || items.size() > 6 || items[1].type() != QAsn1Element::IntegerType) - return; - keyLength = numberOfBits(items[1].value()); - } else if (algorithm == QSsl::Ec) { - if (versionHex != "01") - return; - if (items.size() != 4 - || items[1].type() != QAsn1Element::OctetStringType - || items[2].type() != QAsn1Element::Context0Type - || items[3].type() != QAsn1Element::Context1Type) - return; - QAsn1Element oidElem; - if (!oidElem.read(items[2].value()) - || oidElem.type() != QAsn1Element::ObjectIdentifierType) - return; - keyLength = curveBits(oidElem.toObjectId()); - } - } - - derData = decryptedDer; - keyIsNull = false; -} - -void TlsKeyGeneric::decodePem(QSsl::KeyType type, QSsl::KeyAlgorithm algorithm, const QByteArray &pem, - const QByteArray &passPhrase, bool deepClear) -{ - keyType = type; - keyAlgorithm = algorithm; - - QMap<QByteArray, QByteArray> headers; - QByteArray data = derFromPem(pem, &headers); - - if (headers.value("Proc-Type") == "4,ENCRYPTED") { - const QList<QByteArray> dekInfo = headers.value("DEK-Info").split(','); - if (dekInfo.size() != 2) { - clear(deepClear); - return; - } - - QSslKeyPrivate::Cipher cipher; - if (dekInfo.first() == "DES-CBC") { - cipher = Cipher::DesCbc; - } else if (dekInfo.first() == "DES-EDE3-CBC") { - cipher = Cipher::DesEde3Cbc; - } else if (dekInfo.first() == "RC2-CBC") { - cipher = Cipher::Rc2Cbc; - } else if (dekInfo.first() == "AES-128-CBC") { - cipher = Cipher::Aes128Cbc; - } else if (dekInfo.first() == "AES-192-CBC") { - cipher = Cipher::Aes192Cbc; - } else if (dekInfo.first() == "AES-256-CBC") { - cipher = Cipher::Aes256Cbc; - } else { - clear(deepClear); - return; - } - - const QByteArray iv = QByteArray::fromHex(dekInfo.last()); - const QByteArray key = deriveKey(cipher, passPhrase, iv); - data = decrypt(cipher, data, key, iv); - } - - decodeDer(keyType, keyAlgorithm, data, passPhrase, deepClear); -} - -QByteArray TlsKeyGeneric::toPem(const QByteArray &passPhrase) const -{ - QByteArray data; - QMap<QByteArray, QByteArray> headers; - - if (type() == QSsl::PrivateKey && !passPhrase.isEmpty()) { - // ### use a cryptographically secure random number generator - quint64 random = QRandomGenerator::system()->generate64(); - QByteArray iv = QByteArray::fromRawData(reinterpret_cast<const char *>(&random), sizeof(random)); - - auto cipher = Cipher::DesEde3Cbc; - const QByteArray key = deriveKey(cipher, passPhrase, iv); - data = encrypt(cipher, derData, key, iv); - - headers.insert("Proc-Type", "4,ENCRYPTED"); - headers.insert("DEK-Info", "DES-EDE3-CBC," + iv.toHex()); - } else { - data = derData; - } - - return pemFromDer(data, headers); -} - -QByteArray TlsKeyGeneric::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const -{ - if (derData.size()) - return derData; - - QByteArray header = pemHeader(); - QByteArray footer = pemFooter(); - - QByteArray der(pem); - - int headerIndex = der.indexOf(header); - int footerIndex = der.indexOf(footer, headerIndex + header.length()); - if (type() != QSsl::PublicKey) { - if (headerIndex == -1 || footerIndex == -1) { - header = pkcs8Header(true); - footer = pkcs8Footer(true); - headerIndex = der.indexOf(header); - footerIndex = der.indexOf(footer, headerIndex + header.length()); - } - if (headerIndex == -1 || footerIndex == -1) { - header = pkcs8Header(false); - footer = pkcs8Footer(false); - headerIndex = der.indexOf(header); - footerIndex = der.indexOf(footer, headerIndex + header.length()); - } - } - if (headerIndex == -1 || footerIndex == -1) - return QByteArray(); - - der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size())); - - if (der.contains("Proc-Type:")) { - // taken from QHttpNetworkReplyPrivate::parseHeader - int i = 0; - while (i < der.count()) { - int j = der.indexOf(':', i); // field-name - if (j == -1) - break; - const QByteArray field = der.mid(i, j - i).trimmed(); - j++; - // any number of LWS is allowed before and after the value - QByteArray value; - do { - i = der.indexOf('\n', j); - if (i == -1) - break; - if (!value.isEmpty()) - value += ' '; - // check if we have CRLF or only LF - bool hasCR = (i && der[i-1] == '\r'); - int length = i -(hasCR ? 1: 0) - j; - value += der.mid(j, length).trimmed(); - j = ++i; - } while (i < der.count() && (der.at(i) == ' ' || der.at(i) == '\t')); - if (i == -1) - break; // something is wrong - - headers->insert(field, value); - } - der = der.mid(i); - } - - return QByteArray::fromBase64(der); // ignores newlines -} - -void TlsKeyGeneric::fromHandle(Qt::HANDLE handle, KeyType expectedType) -{ - opaque = handle; - keyType = expectedType; -} - -void TlsKeyGeneric::clear(bool deep) -{ - keyIsNull = true; - if (deep) - std::memset(derData.data(), 0, derData.size()); - derData.clear(); - keyLength = -1; -} - -QByteArray TlsKeyGeneric::decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase) -{ - // RFC 5958: https://tools.ietf.org/html/rfc5958 - /*** Scheme: *** - * Sequence - * Sequence - * Object Identifier (encryption scheme (currently PBES2, PBES1, @todo PKCS12)) - * Sequence (scheme parameters) - * Octet String (the encrypted data) - */ - QAsn1Element elem; - if (!elem.read(encrypted) || elem.type() != QAsn1Element::SequenceType) - return encrypted; - - const auto items = elem.toList(); - if (items.size() != 2 - || items[0].type() != QAsn1Element::SequenceType - || items[1].type() != QAsn1Element::OctetStringType) { - return encrypted; - } - - const auto encryptionSchemeContainer = items[0].toList(); - - if (encryptionSchemeContainer.size() != 2 - || encryptionSchemeContainer[0].type() != QAsn1Element::ObjectIdentifierType - || encryptionSchemeContainer[1].type() != QAsn1Element::SequenceType) { - return encrypted; - } - - const QByteArray encryptionScheme = encryptionSchemeContainer[0].toObjectId(); - const auto schemeParameterContainer = encryptionSchemeContainer[1].toList(); - - if (schemeParameterContainer.size() != 2 - && schemeParameterContainer[0].type() != QAsn1Element::SequenceType - && schemeParameterContainer[1].type() != QAsn1Element::SequenceType) { - return encrypted; - } - - EncryptionData data; - if (encryptionScheme == PKCS5_PBES2_ENCRYPTION_OID) { - data = readPbes2(schemeParameterContainer, passPhrase); - } else if (pbes1OidHashFunctionMap.contains(encryptionScheme)) { - data = readPbes1(schemeParameterContainer, encryptionScheme, passPhrase); - } else if (encryptionScheme.startsWith(PKCS12_OID)) { - Q_UNIMPLEMENTED(); // this isn't some 'unknown', I know these aren't implemented - return encrypted; - } else { - qWarning() - << "QSslKey: Unsupported encryption scheme OID:" << encryptionScheme - << "\nFile a bugreport to Qt (include the line above)."; - return encrypted; - } - - if (!data.initialized) { - // something went wrong, return - return encrypted; - } - - QByteArray decryptedKey = decrypt(data.cipher, items[1].value(), data.key, data.iv); - // The data is still wrapped in a octet string, so let's unwrap it - QAsn1Element decryptedKeyElement(QAsn1Element::ElementType::OctetStringType, decryptedKey); - return decryptedKeyElement.value(); -} - -} // namespace QTlsPrivate - -QT_END_NAMESPACE diff --git a/src/network/ssl/qtlskey_generic_p.h b/src/network/ssl/qtlskey_generic_p.h deleted file mode 100644 index 4c409f5f51..0000000000 --- a/src/network/ssl/qtlskey_generic_p.h +++ /dev/null @@ -1,117 +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$ -** -****************************************************************************/ - -#ifndef QTLSKEY_GENERIC_P_H -#define QTLSKEY_GENERIC_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 <private/qtnetworkglobal_p.h> - -#include <private/qtlskey_base_p.h> -#include <private/qtlsbackend_p.h> - -#include <QtCore/qnamespace.h> -#include <QtCore/qglobal.h> - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -// This class is what previously was known as qsslkey_qt: -// it implements most of functionality needed by QSslKey -// not relying on any TLS implementation. It's used by -// our SecureTransport and Schannel backends. -class TlsKeyGeneric : public TlsKeyBase -{ -public: - using TlsKeyBase::TlsKeyBase; - - void decodeDer(KeyType type, KeyAlgorithm algorithm, const QByteArray &der, - const QByteArray &passPhrase, bool deepClear) override; - void decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem, - const QByteArray &passPhrase, bool deepClear) override; - - QByteArray toPem(const QByteArray &passPhrase) const override; - - QByteArray derFromPem(const QByteArray &pem, QMap<QByteArray, - QByteArray> *headers) const override; - - void fromHandle(Qt::HANDLE opaque, KeyType expectedType) override; - - void clear(bool deep) override; - - Qt::HANDLE handle() const override - { - return Qt::HANDLE(opaque); - } - - int length() const override - { - return keyLength; - } - - bool isPkcs8() const override - { - return pkcs8; - } - -private: - QByteArray decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase); - - bool pkcs8 = false; - Qt::HANDLE opaque = nullptr; - QByteArray derData; - int keyLength = -1; -}; - -} // namespace QTlsPrivate - -QT_END_NAMESPACE - -#endif // QTLSKEY_GENERIC_P_H diff --git a/src/network/ssl/qtlskey_openssl.cpp b/src/network/ssl/qtlskey_openssl.cpp deleted file mode 100644 index 6d75dfd5b4..0000000000 --- a/src/network/ssl/qtlskey_openssl.cpp +++ /dev/null @@ -1,509 +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 "qsslsocket_openssl_symbols_p.h" -#include "qtlskey_openssl_p.h" -#include "qsslsocket.h" -#include "qsslkey_p.h" - -#include <qscopeguard.h> - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -void TlsKeyOpenSSL::decodeDer(QSsl::KeyType type, QSsl::KeyAlgorithm algorithm, const QByteArray &der, - const QByteArray &passPhrase, bool deepClear) -{ - if (der.isEmpty()) - return; - - keyType = type; - keyAlgorithm = algorithm; - - QMap<QByteArray, QByteArray> headers; - const auto pem = pemFromDer(der, headers); - - decodePem(type, algorithm, pem, passPhrase, deepClear); -} - -void TlsKeyOpenSSL::decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem, - const QByteArray &passPhrase, bool deepClear) -{ - if (pem.isEmpty()) - return; - - keyType = type; - keyAlgorithm = algorithm; - - clear(deepClear); - - BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size()); - if (!bio) - return; - - const auto bioRaii = qScopeGuard([bio]{q_BIO_free(bio);}); - - void *phrase = const_cast<char *>(passPhrase.data()); - - if (algorithm == QSsl::Rsa) { - RSA *result = (type == QSsl::PublicKey) - ? q_PEM_read_bio_RSA_PUBKEY(bio, &rsa, nullptr, phrase) - : q_PEM_read_bio_RSAPrivateKey(bio, &rsa, nullptr, phrase); - if (rsa && rsa == result) - keyIsNull = false; - } else if (algorithm == QSsl::Dsa) { - DSA *result = (type == QSsl::PublicKey) - ? q_PEM_read_bio_DSA_PUBKEY(bio, &dsa, nullptr, phrase) - : q_PEM_read_bio_DSAPrivateKey(bio, &dsa, nullptr, phrase); - if (dsa && dsa == result) - keyIsNull = false; - } else if (algorithm == QSsl::Dh) { - EVP_PKEY *result = (type == QSsl::PublicKey) - ? q_PEM_read_bio_PUBKEY(bio, nullptr, nullptr, phrase) - : q_PEM_read_bio_PrivateKey(bio, nullptr, nullptr, phrase); - if (result) - dh = q_EVP_PKEY_get1_DH(result); - if (dh) - keyIsNull = false; - q_EVP_PKEY_free(result); -#ifndef OPENSSL_NO_EC - } else if (algorithm == QSsl::Ec) { - EC_KEY *result = (type == QSsl::PublicKey) - ? q_PEM_read_bio_EC_PUBKEY(bio, &ec, nullptr, phrase) - : q_PEM_read_bio_ECPrivateKey(bio, &ec, nullptr, phrase); - if (ec && ec == result) - keyIsNull = false; -#endif - } -} - -QByteArray TlsKeyOpenSSL::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const -{ - QByteArray header = pemHeader(); - QByteArray footer = pemFooter(); - - QByteArray der(pem); - - int headerIndex = der.indexOf(header); - int footerIndex = der.indexOf(footer, headerIndex + header.length()); - if (type() != QSsl::PublicKey) { - if (headerIndex == -1 || footerIndex == -1) { - header = pkcs8Header(true); - footer = pkcs8Footer(true); - headerIndex = der.indexOf(header); - footerIndex = der.indexOf(footer, headerIndex + header.length()); - } - if (headerIndex == -1 || footerIndex == -1) { - header = pkcs8Header(false); - footer = pkcs8Footer(false); - headerIndex = der.indexOf(header); - footerIndex = der.indexOf(footer, headerIndex + header.length()); - } - } - if (headerIndex == -1 || footerIndex == -1) - return QByteArray(); - - der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size())); - - if (der.contains("Proc-Type:")) { - // taken from QHttpNetworkReplyPrivate::parseHeader - int i = 0; - while (i < der.count()) { - int j = der.indexOf(':', i); // field-name - if (j == -1) - break; - const QByteArray field = der.mid(i, j - i).trimmed(); - j++; - // any number of LWS is allowed before and after the value - QByteArray value; - do { - i = der.indexOf('\n', j); - if (i == -1) - break; - if (!value.isEmpty()) - value += ' '; - // check if we have CRLF or only LF - bool hasCR = (i && der[i-1] == '\r'); - int length = i -(hasCR ? 1: 0) - j; - value += der.mid(j, length).trimmed(); - j = ++i; - } while (i < der.count() && (der.at(i) == ' ' || der.at(i) == '\t')); - if (i == -1) - break; // something is wrong - - headers->insert(field, value); - } - der = der.mid(i); - } - - return QByteArray::fromBase64(der); // ignores newlines -} - -void TlsKeyOpenSSL::clear(bool deep) -{ - keyIsNull = true; - - if (algorithm() == QSsl::Rsa && rsa) { - if (deep) - q_RSA_free(rsa); - rsa = nullptr; - } - if (algorithm() == QSsl::Dsa && dsa) { - if (deep) - q_DSA_free(dsa); - dsa = nullptr; - } - if (algorithm() == QSsl::Dh && dh) { - if (deep) - q_DH_free(dh); - dh = nullptr; - } -#ifndef OPENSSL_NO_EC - if (algorithm() == QSsl::Ec && ec) { - if (deep) - q_EC_KEY_free(ec); - ec = nullptr; - } -#endif - if (algorithm() == QSsl::Opaque && opaque) { - if (deep) - q_EVP_PKEY_free(opaque); - opaque = nullptr; - } -} - -Qt::HANDLE TlsKeyOpenSSL::handle() const -{ - switch (keyAlgorithm) { - case QSsl::Opaque: - return Qt::HANDLE(opaque); - case QSsl::Rsa: - return Qt::HANDLE(rsa); - case QSsl::Dsa: - return Qt::HANDLE(dsa); - case QSsl::Dh: - return Qt::HANDLE(dh); -#ifndef OPENSSL_NO_EC - case QSsl::Ec: - return Qt::HANDLE(ec); -#endif - default: - return Qt::HANDLE(nullptr); - } -} - -int TlsKeyOpenSSL::length() const -{ - if (isNull() || algorithm() == QSsl::Opaque) - return -1; - - switch (algorithm()) { - case QSsl::Rsa: - return q_RSA_bits(rsa); - case QSsl::Dsa: - return q_DSA_bits(dsa); - case QSsl::Dh: - return q_DH_bits(dh); -#ifndef OPENSSL_NO_EC - case QSsl::Ec: - return q_EC_GROUP_get_degree(q_EC_KEY_get0_group(ec)); -#endif - default: - return -1; - } -} - -QByteArray TlsKeyOpenSSL::toPem(const QByteArray &passPhrase) const -{ - if (!QSslSocket::supportsSsl() || isNull() || algorithm() == QSsl::Opaque) - return {}; - - const EVP_CIPHER *cipher = nullptr; - if (type() == QSsl::PrivateKey && !passPhrase.isEmpty()) { -#ifndef OPENSSL_NO_DES - cipher = q_EVP_des_ede3_cbc(); -#else - return {}; -#endif - } - - BIO *bio = q_BIO_new(q_BIO_s_mem()); - if (!bio) - return {}; - - const auto bioRaii = qScopeGuard([bio]{q_BIO_free(bio);}); - - bool fail = false; - - if (algorithm() == QSsl::Rsa) { - if (type() == QSsl::PublicKey) { - if (!q_PEM_write_bio_RSA_PUBKEY(bio, rsa)) - fail = true; - } else { - if (!q_PEM_write_bio_RSAPrivateKey( - bio, rsa, cipher, (uchar *)passPhrase.data(), - passPhrase.size(), nullptr, nullptr)) { - fail = true; - } - } - } else if (algorithm() == QSsl::Dsa) { - if (type() == QSsl::PublicKey) { - if (!q_PEM_write_bio_DSA_PUBKEY(bio, dsa)) - fail = true; - } else { - if (!q_PEM_write_bio_DSAPrivateKey( - bio, dsa, cipher, (uchar *)passPhrase.data(), - passPhrase.size(), nullptr, nullptr)) { - fail = true; - } - } - } else if (algorithm() == QSsl::Dh) { - EVP_PKEY *result = q_EVP_PKEY_new(); - if (!result || !q_EVP_PKEY_set1_DH(result, dh)) { - fail = true; - } else if (type() == QSsl::PublicKey) { - if (!q_PEM_write_bio_PUBKEY(bio, result)) - fail = true; - } else if (!q_PEM_write_bio_PrivateKey( - bio, result, cipher, (uchar *)passPhrase.data(), - passPhrase.size(), nullptr, nullptr)) { - fail = true; - } - q_EVP_PKEY_free(result); -#ifndef OPENSSL_NO_EC - } else if (algorithm() == QSsl::Ec) { - if (type() == QSsl::PublicKey) { - if (!q_PEM_write_bio_EC_PUBKEY(bio, ec)) - fail = true; - } else { - if (!q_PEM_write_bio_ECPrivateKey( - bio, ec, cipher, (uchar *)passPhrase.data(), - passPhrase.size(), nullptr, nullptr)) { - fail = true; - } - } -#endif - } else { - fail = true; - } - - QByteArray pem; - if (!fail) { - char *data = nullptr; - const long size = q_BIO_get_mem_data(bio, &data); - if (size > 0 && data) - pem = QByteArray(data, size); - } - - return pem; -} - -void TlsKeyOpenSSL::fromHandle(Qt::HANDLE handle, QSsl::KeyType expectedType) -{ - EVP_PKEY *evpKey = reinterpret_cast<EVP_PKEY *>(handle); - if (!evpKey || !fromEVP_PKEY(evpKey)) { - opaque = evpKey; - keyAlgorithm = QSsl::Opaque; - } else { - q_EVP_PKEY_free(evpKey); - } - - keyType = expectedType; - keyIsNull = !opaque; -} - -bool TlsKeyOpenSSL::fromEVP_PKEY(EVP_PKEY *pkey) -{ - if (!pkey) - return false; - - switch (q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey))) { - case EVP_PKEY_RSA: - keyIsNull = false; - keyAlgorithm = QSsl::Rsa; - keyType = QSsl::PrivateKey; - rsa = q_EVP_PKEY_get1_RSA(pkey); - - return true; - case EVP_PKEY_DSA: - keyIsNull = false; - keyAlgorithm = QSsl::Dsa; - keyType = QSsl::PrivateKey; - dsa = q_EVP_PKEY_get1_DSA(pkey); - - return true; - case EVP_PKEY_DH: - keyIsNull = false; - keyAlgorithm = QSsl::Dh; - keyType = QSsl::PrivateKey; - dh = q_EVP_PKEY_get1_DH(pkey); - return true; -#ifndef OPENSSL_NO_EC - case EVP_PKEY_EC: - keyIsNull = false; - keyAlgorithm = QSsl::Ec; - keyType = QSsl::PrivateKey; - ec = q_EVP_PKEY_get1_EC_KEY(pkey); - - return true; -#endif - default:; - // Unknown key type. This could be handled as opaque, but then - // we'd eventually leak memory since we wouldn't be able to free - // the underlying EVP_PKEY structure. For now, we won't support - // this. - } - - return false; -} - -QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, - const QByteArray &key, const QByteArray &iv, bool enc) -{ - const EVP_CIPHER *type = nullptr; - int i = 0, len = 0; - - switch (cipher) { - case Cipher::DesCbc: -#ifndef OPENSSL_NO_DES - type = q_EVP_des_cbc(); -#endif - break; - case Cipher::DesEde3Cbc: -#ifndef OPENSSL_NO_DES - type = q_EVP_des_ede3_cbc(); -#endif - break; - case Cipher::Rc2Cbc: -#ifndef OPENSSL_NO_RC2 - type = q_EVP_rc2_cbc(); -#endif - break; - case Cipher::Aes128Cbc: - type = q_EVP_aes_128_cbc(); - break; - case Cipher::Aes192Cbc: - type = q_EVP_aes_192_cbc(); - break; - case Cipher::Aes256Cbc: - type = q_EVP_aes_256_cbc(); - break; - } - - if (type == nullptr) - return {}; - - QByteArray output; - output.resize(data.size() + EVP_MAX_BLOCK_LENGTH); - - EVP_CIPHER_CTX *ctx = q_EVP_CIPHER_CTX_new(); - q_EVP_CIPHER_CTX_reset(ctx); - q_EVP_CipherInit(ctx, type, nullptr, nullptr, enc); - q_EVP_CIPHER_CTX_set_key_length(ctx, key.size()); - if (cipher == Cipher::Rc2Cbc) - q_EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, 8 * key.size(), nullptr); - - q_EVP_CipherInit_ex(ctx, nullptr, nullptr, - reinterpret_cast<const unsigned char *>(key.constData()), - reinterpret_cast<const unsigned char *>(iv.constData()), - enc); - q_EVP_CipherUpdate(ctx, - reinterpret_cast<unsigned char *>(output.data()), &len, - reinterpret_cast<const unsigned char *>(data.constData()), data.size()); - q_EVP_CipherFinal(ctx, - reinterpret_cast<unsigned char *>(output.data()) + len, &i); - len += i; - - q_EVP_CIPHER_CTX_reset(ctx); - q_EVP_CIPHER_CTX_free(ctx); - - return output.left(len); -} - -QByteArray TlsKeyOpenSSL::decrypt(Cipher cipher, const QByteArray &data, - const QByteArray &key, const QByteArray &iv) const -{ - return doCrypt(cipher, data, key, iv, false); -} - -QByteArray TlsKeyOpenSSL::encrypt(Cipher cipher, const QByteArray &data, - const QByteArray &key, const QByteArray &iv) const -{ - return doCrypt(cipher, data, key, iv, true); -} - -TlsKeyOpenSSL *TlsKeyOpenSSL::publicKeyFromX509(X509 *x) -{ - TlsKeyOpenSSL *tlsKey = new TlsKeyOpenSSL; - std::unique_ptr<TlsKeyOpenSSL> keyRaii(tlsKey); - - tlsKey->keyType = QSsl::PublicKey; - - EVP_PKEY *pkey = q_X509_get_pubkey(x); - 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 (key is null) - } else { - // error? (key is null) - } - - q_EVP_PKEY_free(pkey); - return keyRaii.release(); -} - -} // namespace QTlsPrivate - -QT_END_NAMESPACE diff --git a/src/network/ssl/qtlskey_openssl_p.h b/src/network/ssl/qtlskey_openssl_p.h deleted file mode 100644 index 5ef51dfd56..0000000000 --- a/src/network/ssl/qtlskey_openssl_p.h +++ /dev/null @@ -1,126 +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$ -** -****************************************************************************/ - -#ifndef QTLSKEY_OPENSSL_H -#define QTLSKEY_OPENSSL_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 <private/qtnetworkglobal_p.h> - -#include "qtlskey_base_p.h" -#include "qtlsbackend_p.h" -#include "qsslkey_p.h" - -#include <QtNetwork/qssl.h> - -#include <QtCore/qbytearray.h> -#include <QtCore/qglobal.h> - -#include <openssl/rsa.h> -#include <openssl/dsa.h> -#include <openssl/dh.h> - -QT_BEGIN_NAMESPACE - -QT_REQUIRE_CONFIG(ssl); - -namespace QTlsPrivate { - -class TlsKeyOpenSSL final : public TlsKeyBase -{ -public: - TlsKeyOpenSSL() - : opaque(nullptr) - { - clear(false); - } - ~TlsKeyOpenSSL() - { - clear(true); - } - - void decodeDer(KeyType type, KeyAlgorithm algorithm, const QByteArray &der, - const QByteArray &passPhrase, bool deepClear) override; - void decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem, - const QByteArray &passPhrase, bool deepClear) override; - - QByteArray toPem(const QByteArray &passPhrase) const override; - QByteArray derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const override; - - void fromHandle(Qt::HANDLE opaque, KeyType expectedType) override; - - void clear(bool deep) override; - Qt::HANDLE handle() const override; - int length() const override; - - QByteArray decrypt(Cipher cipher, const QByteArray &data, - const QByteArray &key, const QByteArray &iv) const override; - QByteArray encrypt(Cipher cipher, const QByteArray &data, - const QByteArray &key, const QByteArray &iv) const override; - - static TlsKeyOpenSSL *publicKeyFromX509(X509 *x); - - union { - EVP_PKEY *opaque; - RSA *rsa; - DSA *dsa; - DH *dh; -#ifndef OPENSSL_NO_EC - EC_KEY *ec; -#endif - }; - - bool fromEVP_PKEY(EVP_PKEY *pkey); -}; - -} // namespace QTlsPrivate - -QT_END_NAMESPACE - -#endif // QTLSKEY_OPENSSL_H diff --git a/src/network/ssl/qtlskey_schannel.cpp b/src/network/ssl/qtlskey_schannel.cpp deleted file mode 100644 index e788eac03a..0000000000 --- a/src/network/ssl/qtlskey_schannel.cpp +++ /dev/null @@ -1,187 +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 "qssl_p.h" -#include "qtlskey_schannel_p.h" -#include "qtlsbackend_p.h" -#include "qsslkey_p.h" -#include "qsslkey.h" - -#include <QtCore/qscopeguard.h> -#include <QtCore/qbytearray.h> - -#include <QtCore/qt_windows.h> -#include <wincrypt.h> - -QT_BEGIN_NAMESPACE - -namespace { -const wchar_t *getName(QSslKeyPrivate::Cipher cipher) -{ - switch (cipher) { - case QTlsPrivate::Cipher::DesCbc: - return BCRYPT_DES_ALGORITHM; - case QTlsPrivate::Cipher::DesEde3Cbc: - return BCRYPT_3DES_ALGORITHM; - case QTlsPrivate::Cipher::Rc2Cbc: - return BCRYPT_RC2_ALGORITHM; - case QTlsPrivate::Cipher::Aes128Cbc: - case QTlsPrivate::Cipher::Aes192Cbc: - case QTlsPrivate::Cipher::Aes256Cbc: - return BCRYPT_AES_ALGORITHM; - } - Q_UNREACHABLE(); -} - -BCRYPT_ALG_HANDLE getHandle(QSslKeyPrivate::Cipher cipher) -{ - BCRYPT_ALG_HANDLE handle; - NTSTATUS status = BCryptOpenAlgorithmProvider( - &handle, // phAlgorithm - getName(cipher), // pszAlgId - nullptr, // pszImplementation - 0 // dwFlags - ); - if (status < 0) { - qCWarning(lcTlsBackend, "Failed to open algorithm handle (%ld)!", status); - return nullptr; - } - - return handle; -} - -BCRYPT_KEY_HANDLE generateSymmetricKey(BCRYPT_ALG_HANDLE handle, - const QByteArray &key) -{ - BCRYPT_KEY_HANDLE keyHandle; - NTSTATUS status = BCryptGenerateSymmetricKey( - handle, // hAlgorithm - &keyHandle, // phKey - nullptr, // pbKeyObject (can ignore) - 0, // cbKeyObject (also ignoring) - reinterpret_cast<unsigned char *>(const_cast<char *>(key.data())), // pbSecret - ULONG(key.length()), // cbSecret - 0 // dwFlags - ); - if (status < 0) { - qCWarning(lcTlsBackend, "Failed to generate symmetric key (%ld)!", status); - return nullptr; - } - - status = BCryptSetProperty( - keyHandle, // hObject - BCRYPT_CHAINING_MODE, // pszProperty - reinterpret_cast<UCHAR *>(const_cast<wchar_t *>(BCRYPT_CHAIN_MODE_CBC)), // pbInput - ARRAYSIZE(BCRYPT_CHAIN_MODE_CBC), // cbInput - 0 // dwFlags - ); - if (status < 0) { - BCryptDestroyKey(keyHandle); - qCWarning(lcTlsBackend, "Failed to change the symmetric key's chaining mode (%ld)!", status); - return nullptr; - } - return keyHandle; -} - -QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, const QByteArray &key, - const QByteArray &iv, bool encrypt) -{ - BCRYPT_ALG_HANDLE handle = getHandle(cipher); - if (!handle) - return {}; - auto handleDealloc = qScopeGuard([&handle]() { - BCryptCloseAlgorithmProvider(handle, 0); - }); - - BCRYPT_KEY_HANDLE keyHandle = generateSymmetricKey(handle, key); - if (!keyHandle) - return {}; - auto keyHandleDealloc = qScopeGuard([&keyHandle]() { - BCryptDestroyKey(keyHandle); - }); - - QByteArray ivCopy = iv; // This gets modified, so we take a copy - - ULONG sizeNeeded = 0; - QVarLengthArray<unsigned char> output; - auto cryptFunction = encrypt ? BCryptEncrypt : BCryptDecrypt; - for (int i = 0; i < 2; i++) { - output.resize(int(sizeNeeded)); - auto input = reinterpret_cast<unsigned char *>(const_cast<char *>(data.data())); - // Need to call it twice because the first iteration lets us know the size needed. - NTSTATUS status = cryptFunction( - keyHandle, // hKey - input, // pbInput - ULONG(data.length()), // cbInput - nullptr, // pPaddingInfo - reinterpret_cast<unsigned char *>(ivCopy.data()), // pbIV - ULONG(ivCopy.length()), // cbIV - sizeNeeded ? output.data() : nullptr, // pbOutput - ULONG(output.length()), // cbOutput - &sizeNeeded, // pcbResult - BCRYPT_BLOCK_PADDING // dwFlags - ); - if (status < 0) { - qCWarning(lcTlsBackend, "%s failed (%ld)!", encrypt ? "Encrypt" : "Decrypt", status); - return {}; - } - } - - return QByteArray(reinterpret_cast<const char *>(output.constData()), int(sizeNeeded)); -} -} // anonymous namespace - -namespace QTlsPrivate { - -QByteArray TlsKeySchannel::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, - const QByteArray &iv) const -{ - return doCrypt(cipher, data, key, iv, false); -} - -QByteArray TlsKeySchannel::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, - const QByteArray &iv) const -{ - return doCrypt(cipher, data, key, iv, true); -} - -} // namespace QTlsPrivate - -QT_END_NAMESPACE - diff --git a/src/network/ssl/qtlskey_schannel_p.h b/src/network/ssl/qtlskey_schannel_p.h deleted file mode 100644 index 72747e7fa6..0000000000 --- a/src/network/ssl/qtlskey_schannel_p.h +++ /dev/null @@ -1,82 +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$ -** -****************************************************************************/ - -#ifndef QTLSKEY_SCHANNEL_P_H -#define QTLSKEY_SCHANNEL_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 <private/qtnetworkglobal_p.h> - -#include <private/qtlskey_generic_p.h> - -#include <QtCore/qglobal.h> - -QT_REQUIRE_CONFIG(ssl); - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -class TlsKeySchannel final : public TlsKeyGeneric -{ -public: - using TlsKeyGeneric::TlsKeyGeneric; - - QByteArray decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, - const QByteArray &iv) const override; - QByteArray encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, - const QByteArray &iv) const override; -}; - -} // namespace QTlsPrivate - -QT_END_NAMESPACE - -#endif // QTLSKEY_SCHANNEL_P_H - diff --git a/src/network/ssl/qtlskey_st.cpp b/src/network/ssl/qtlskey_st.cpp deleted file mode 100644 index c551729c24..0000000000 --- a/src/network/ssl/qtlskey_st.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org> -** 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 "qtlskey_st_p.h" -#include "qsslkey_p.h" - -#include <qbytearray.h> - -#include <CommonCrypto/CommonCrypto.h> - -#include <cstddef> - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { -namespace { - -// Before this code was located in qsslkey_mac.cpp. -QByteArray wrapCCCrypt(CCOperation ccOp, QSslKeyPrivate::Cipher cipher, - const QByteArray &data, const QByteArray &key, - const QByteArray &iv) -{ - int blockSize = {}; - CCAlgorithm ccAlgorithm = {}; - switch (cipher) { - case Cipher::DesCbc: - blockSize = kCCBlockSizeDES; - ccAlgorithm = kCCAlgorithmDES; - break; - case Cipher::DesEde3Cbc: - blockSize = kCCBlockSize3DES; - ccAlgorithm = kCCAlgorithm3DES; - break; - case Cipher::Rc2Cbc: - blockSize = kCCBlockSizeRC2; - ccAlgorithm = kCCAlgorithmRC2; - break; - case Cipher::Aes128Cbc: - case Cipher::Aes192Cbc: - case Cipher::Aes256Cbc: - blockSize = kCCBlockSizeAES128; - ccAlgorithm = kCCAlgorithmAES; - break; - } - std::size_t plainLength = 0; - QByteArray plain(data.size() + blockSize, 0); - CCCryptorStatus status = CCCrypt(ccOp, ccAlgorithm, kCCOptionPKCS7Padding, - key.constData(), std::size_t(key.size()), - iv.constData(), data.constData(), std::size_t(data.size()), - plain.data(), std::size_t(plain.size()), &plainLength); - if (status == kCCSuccess) - return plain.left(int(plainLength)); - - return {}; -} - -} // Unnamed namespace. - -QByteArray TlsKeySecureTransport::decrypt(Cipher cipher, const QByteArray &data, - const QByteArray &key, const QByteArray &iv) const -{ - return wrapCCCrypt(kCCDecrypt, cipher, data, key, iv); -} - -QByteArray TlsKeySecureTransport::encrypt(Cipher cipher, const QByteArray &data, - const QByteArray &key, const QByteArray &iv) const -{ - return wrapCCCrypt(kCCEncrypt, cipher, data, key, iv); -} - -} // namespace QTlsPrivate - -QT_END_NAMESPACE diff --git a/src/network/ssl/qtlskey_st_p.h b/src/network/ssl/qtlskey_st_p.h deleted file mode 100644 index 7088daf39a..0000000000 --- a/src/network/ssl/qtlskey_st_p.h +++ /dev/null @@ -1,83 +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$ -** -****************************************************************************/ - -#ifndef QTLSKEY_ST_P_H -#define QTLSKEY_ST_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 <private/qtnetworkglobal_p.h> - -#include <private/qtlskey_generic_p.h> - -#include <QtCore/qglobal.h> - -QT_REQUIRE_CONFIG(ssl); - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -class TlsKeySecureTransport final : public TlsKeyGeneric -{ -public: - using TlsKeyGeneric::TlsKeyGeneric; - - QByteArray decrypt(Cipher cipher, const QByteArray &data, - const QByteArray &key, const QByteArray &iv) const override; - QByteArray encrypt(Cipher cipher, const QByteArray &data, - const QByteArray &key, const QByteArray &iv) const override; - - Q_DISABLE_COPY_MOVE(TlsKeySecureTransport) -}; - -} // namespace QTlsPrivate - -QT_END_NAMESPACE - -#endif // QTLSKEY_ST_P_H diff --git a/src/network/ssl/qwincrypt_p.h b/src/network/ssl/qwincrypt_p.h deleted file mode 100644 index 2a7bd1fae2..0000000000 --- a/src/network/ssl/qwincrypt_p.h +++ /dev/null @@ -1,81 +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$ -** -****************************************************************************/ - -#ifndef QWINCRYPT_P_H -#define QWINCRYPT_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 <QtNetwork/private/qtnetworkglobal_p.h> - -#include <QtCore/qt_windows.h> - -#include <QtCore/qglobal.h> - -#include <wincrypt.h> -#ifndef HCRYPTPROV_LEGACY -#define HCRYPTPROV_LEGACY HCRYPTPROV -#endif // !HCRYPTPROV_LEGACY - -#include <memory> - -QT_BEGIN_NAMESPACE - -struct QHCertStoreDeleter { - void operator()(HCERTSTORE store) - { - CertCloseStore(store, 0); - } -}; - -// A simple RAII type used by Schannel code and Window CA fetcher class: -using QHCertStorePointer = std::unique_ptr<void, QHCertStoreDeleter>; - -QT_END_NAMESPACE - -#endif // QWINCRYPT_P_H diff --git a/src/network/ssl/qwindowscarootfetcher.cpp b/src/network/ssl/qwindowscarootfetcher.cpp deleted file mode 100644 index b675ac8d4e..0000000000 --- a/src/network/ssl/qwindowscarootfetcher.cpp +++ /dev/null @@ -1,292 +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 "qwindowscarootfetcher_p.h" - -#include <QtCore/QThread> -#include <QtGlobal> - -#include <QtCore/qscopeguard.h> - -#ifdef QSSLSOCKET_DEBUG -#include "qssl_p.h" // for debug categories -#include <QtCore/QElapsedTimer> -#endif - -#include "qsslsocket_p.h" // Transitively includes Wincrypt.h - -#if QT_CONFIG(openssl) -#include "qopenssl_p.h" -#include "qx509_openssl_p.h" -#endif - -QT_BEGIN_NAMESPACE - -class QWindowsCaRootFetcherThread : public QThread -{ -public: - QWindowsCaRootFetcherThread() - { - qRegisterMetaType<QSslCertificate>(); - setObjectName(QStringLiteral("QWindowsCaRootFetcher")); - start(); - } - ~QWindowsCaRootFetcherThread() - { - quit(); - wait(15500); // worst case, a running request can block for 15 seconds - } -}; - -Q_GLOBAL_STATIC(QWindowsCaRootFetcherThread, windowsCaRootFetcherThread); - -#if QT_CONFIG(openssl) -namespace { -// TLSTODO: we have to ask the currently active TLS backend about verification -// support and get a function pointer. QT_CONFIG(openssl) check is becoming useless -// as soon as we have several plugins. -const QList<QSslCertificate> buildVerifiedChain(const QList<QSslCertificate> &caCertificates, - PCCERT_CHAIN_CONTEXT chainContext, - const QString &peerVerifyName) -{ - // We ended up here because OpenSSL verification failed to - // build a chain, with intermediate certificate missing - // but "Authority Information Access" extension present. - // Also, apparently the normal CA fetching path was disabled - // by setting custom CA certificates. We convert wincrypt's - // structures in QSslCertificate and give OpenSSL the second - // chance to verify the now (apparently) complete chain. - // In addition, wincrypt gives us a benifit of some checks - // we don't have in OpenSSL back-end. - Q_ASSERT(chainContext); - - if (!chainContext->cChain) - return {}; - - QList<QSslCertificate> verifiedChain; - - CERT_SIMPLE_CHAIN *chain = chainContext->rgpChain[chainContext->cChain - 1]; - if (!chain) - return {}; - - if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN) - return {}; // No need to mess with OpenSSL (the chain is still incomplete). - - for (DWORD i = 0; i < chain->cElement; ++i) { - CERT_CHAIN_ELEMENT *element = chain->rgpElement[i]; - QSslCertificate cert(QByteArray(reinterpret_cast<const char*>(element->pCertContext->pbCertEncoded), - int(element->pCertContext->cbCertEncoded)), QSsl::Der); - - if (cert.isBlacklisted()) - return {}; - - if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED) // Good to know! - return {}; - - if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_SIGNATURE_VALID) - return {}; - - verifiedChain.append(cert); - } - - // We rely on OpenSSL's ability to find other problems. - const auto tlsErrors = QTlsPrivate::X509CertificateOpenSSL::verify(caCertificates, verifiedChain, peerVerifyName); - if (tlsErrors.size()) - verifiedChain.clear(); - - return verifiedChain; -} - -} // unnamed namespace -#endif // QT_CONFIG(openssl) - -QWindowsCaRootFetcher::QWindowsCaRootFetcher(const QSslCertificate &certificate, QSslSocket::SslMode sslMode, - const QList<QSslCertificate> &caCertificates, const QString &hostName) - : cert(certificate), mode(sslMode), explicitlyTrustedCAs(caCertificates), peerVerifyName(hostName) -{ - moveToThread(windowsCaRootFetcherThread()); -} - -QWindowsCaRootFetcher::~QWindowsCaRootFetcher() -{ -} - -void QWindowsCaRootFetcher::start() -{ - QByteArray der = cert.toDer(); - PCCERT_CONTEXT wincert = CertCreateCertificateContext(X509_ASN_ENCODING, (const BYTE *)der.constData(), der.length()); - if (!wincert) { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl, "QWindowsCaRootFetcher failed to convert certificate to windows form"); -#endif - emit finished(cert, QSslCertificate()); - deleteLater(); - return; - } - - CERT_CHAIN_PARA parameters; - memset(¶meters, 0, sizeof(parameters)); - parameters.cbSize = sizeof(parameters); - // set key usage constraint - parameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND; - parameters.RequestedUsage.Usage.cUsageIdentifier = 1; - LPSTR oid = (LPSTR)(mode == QSslSocket::SslClientMode ? szOID_PKIX_KP_SERVER_AUTH : szOID_PKIX_KP_CLIENT_AUTH); - parameters.RequestedUsage.Usage.rgpszUsageIdentifier = &oid; - -#ifdef QSSLSOCKET_DEBUG - QElapsedTimer stopwatch; - stopwatch.start(); -#endif - PCCERT_CHAIN_CONTEXT chain; - auto additionalStore = createAdditionalStore(); - BOOL result = CertGetCertificateChain( - nullptr, //default engine - wincert, - nullptr, //current date/time - additionalStore.get(), //default store (nullptr) or CAs an application trusts - ¶meters, - 0, //default dwFlags - nullptr, //reserved - &chain); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << "QWindowsCaRootFetcher" << stopwatch.elapsed() << "ms to get chain"; -#endif - - QSslCertificate trustedRoot; - if (result) { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << "QWindowsCaRootFetcher - examining windows chains"; - if (chain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR) - qCDebug(lcSsl) << " - TRUSTED"; - else - qCDebug(lcSsl) << " - NOT TRUSTED" << chain->TrustStatus.dwErrorStatus; - if (chain->TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED) - qCDebug(lcSsl) << " - SELF SIGNED"; - qCDebug(lcSsl) << "QWindowsCaRootFetcher - dumping simple chains"; - for (unsigned int i = 0; i < chain->cChain; i++) { - if (chain->rgpChain[i]->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR) - qCDebug(lcSsl) << " - TRUSTED SIMPLE CHAIN" << i; - else - qCDebug(lcSsl) << " - UNTRUSTED SIMPLE CHAIN" << i << "reason:" << chain->rgpChain[i]->TrustStatus.dwErrorStatus; - for (unsigned int j = 0; j < chain->rgpChain[i]->cElement; j++) { - QSslCertificate foundCert(QByteArray((const char *)chain->rgpChain[i]->rgpElement[j]->pCertContext->pbCertEncoded - , chain->rgpChain[i]->rgpElement[j]->pCertContext->cbCertEncoded), QSsl::Der); - qCDebug(lcSsl) << " - " << foundCert; - } - } - qCDebug(lcSsl) << " - and" << chain->cLowerQualityChainContext << "low quality chains"; //expect 0, we haven't asked for them -#endif - - //based on http://msdn.microsoft.com/en-us/library/windows/desktop/aa377182%28v=vs.85%29.aspx - //about the final chain rgpChain[cChain-1] which must begin with a trusted root to be valid - if (chain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR - && chain->cChain > 0) { - const PCERT_SIMPLE_CHAIN finalChain = chain->rgpChain[chain->cChain - 1]; - // http://msdn.microsoft.com/en-us/library/windows/desktop/aa377544%28v=vs.85%29.aspx - // rgpElement[0] is the end certificate chain element. rgpElement[cElement-1] is the self-signed "root" certificate element. - if (finalChain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR - && finalChain->cElement > 0) { - trustedRoot = QSslCertificate(QByteArray((const char *)finalChain->rgpElement[finalChain->cElement - 1]->pCertContext->pbCertEncoded - , finalChain->rgpElement[finalChain->cElement - 1]->pCertContext->cbCertEncoded), QSsl::Der); - } - } else if (explicitlyTrustedCAs.size()) { - // Setting custom CA in configuration, and those CAs are not trusted by MS. -#if QT_CONFIG(openssl) - const auto verifiedChain = buildVerifiedChain(explicitlyTrustedCAs, chain, peerVerifyName); - if (verifiedChain.size()) - trustedRoot = verifiedChain.last(); -#else - //It's only OpenSSL code-path that can trigger such a fetch. - Q_UNREACHABLE(); -#endif - } - CertFreeCertificateChain(chain); - } - CertFreeCertificateContext(wincert); - - emit finished(cert, trustedRoot); - deleteLater(); -} - -QHCertStorePointer QWindowsCaRootFetcher::createAdditionalStore() const -{ - QHCertStorePointer customStore; - if (explicitlyTrustedCAs.isEmpty()) - return customStore; - - if (HCERTSTORE rawPtr = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 0, nullptr)) { - customStore.reset(rawPtr); - - unsigned rootsAdded = 0; - for (const QSslCertificate &caCert : explicitlyTrustedCAs) { - const auto der = caCert.toDer(); - PCCERT_CONTEXT winCert = CertCreateCertificateContext(X509_ASN_ENCODING, - reinterpret_cast<const BYTE *>(der.data()), - DWORD(der.length())); - if (!winCert) { -#if defined(QSSLSOCKET_DEBUG) - qCWarning(lcSsl) << "CA fetcher, failed to convert QSslCertificate" - << "to the native representation"; -#endif // QSSLSOCKET_DEBUG - continue; - } - const auto deleter = qScopeGuard([winCert](){ - CertFreeCertificateContext(winCert); - }); - if (CertAddCertificateContextToStore(customStore.get(), winCert, CERT_STORE_ADD_ALWAYS, nullptr)) - ++rootsAdded; -#if defined(QSSLSOCKET_DEBUG) - else //Why assert? With flags we're using and winCert check - should not happen! - Q_ASSERT("CertAddCertificateContextToStore() failed"); -#endif // QSSLSOCKET_DEBUG - } - if (!rootsAdded) //Useless store, no cert was added. - customStore.reset(); -#if defined(QSSLSOCKET_DEBUG) - } else { - - qCWarning(lcSsl) << "CA fetcher, failed to create a custom" - << "store for explicitly trusted CA certificate"; -#endif // QSSLSOCKET_DEBUG - } - - return customStore; -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qwindowscarootfetcher_p.h b/src/network/ssl/qwindowscarootfetcher_p.h deleted file mode 100644 index ee0d07c1e0..0000000000 --- a/src/network/ssl/qwindowscarootfetcher_p.h +++ /dev/null @@ -1,95 +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$ -** -****************************************************************************/ - -#ifndef QWINDOWSCAROOTFETCHER_P_H -#define QWINDOWSCAROOTFETCHER_P_H - -#include <QtNetwork/private/qtnetworkglobal_p.h> - -#include <QtCore/QtGlobal> -#include <QtCore/QObject> - -#include "qsslcertificate.h" -#include "qsslsocket.h" - -#include "qwincrypt_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. -// - -QT_BEGIN_NAMESPACE - -class QWindowsCaRootFetcher : public QObject -{ - Q_OBJECT -public: - QWindowsCaRootFetcher(const QSslCertificate &certificate, QSslSocket::SslMode sslMode, - const QList<QSslCertificate> &caCertificates = {}, - const QString &hostName = {}); - ~QWindowsCaRootFetcher(); -public slots: - void start(); -signals: - void finished(QSslCertificate brokenChain, QSslCertificate caroot); -private: - QHCertStorePointer createAdditionalStore() const; - - QSslCertificate cert; - QSslSocket::SslMode mode; - // In case the application set CA certificates in the configuration, - // in the past we did not load missing certs. But this disables - // recoverable case when a certificate has Authority Information Access - // extension. So we try to fetch in this scenario also, but in case - // explicitly trusted root was not in a system store, we'll do - // additional checks, thus we need 'peerVerifyName': - QList<QSslCertificate> explicitlyTrustedCAs; - QString peerVerifyName; -}; - -QT_END_NAMESPACE - -#endif // QWINDOWSCAROOTFETCHER_P_H diff --git a/src/network/ssl/qx509_base.cpp b/src/network/ssl/qx509_base.cpp deleted file mode 100644 index d7b7b81606..0000000000 --- a/src/network/ssl/qx509_base.cpp +++ /dev/null @@ -1,178 +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 "qx509_base_p.h" - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -QByteArray X509CertificateBase::subjectInfoToString(QSslCertificate::SubjectInfo info) -{ - QByteArray str; - switch (info) { - case QSslCertificate::Organization: str = QByteArray("O"); break; - case QSslCertificate::CommonName: str = QByteArray("CN"); break; - case QSslCertificate::LocalityName: str = QByteArray("L"); break; - case QSslCertificate::OrganizationalUnitName: str = QByteArray("OU"); break; - case QSslCertificate::CountryName: str = QByteArray("C"); break; - case QSslCertificate::StateOrProvinceName: str = QByteArray("ST"); break; - case QSslCertificate::DistinguishedNameQualifier: str = QByteArray("dnQualifier"); break; - case QSslCertificate::SerialNumber: str = QByteArray("serialNumber"); break; - case QSslCertificate::EmailAddress: str = QByteArray("emailAddress"); break; - } - - return str; -} - -bool X509CertificateBase::matchLineFeed(const QByteArray &pem, int *offset) -{ - Q_ASSERT(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; -} - -bool X509CertificateBase::isNull() const -{ - return null; -} - -QByteArray X509CertificateBase::version() const -{ - return versionString; -} - -QByteArray X509CertificateBase::serialNumber() const -{ - return serialNumberString; -} - -QStringList X509CertificateBase::issuerInfo(QSslCertificate::SubjectInfo info) const -{ - return issuerInfo(subjectInfoToString(info)); -} - -QStringList X509CertificateBase::issuerInfo(const QByteArray &attribute) const -{ - return issuerInfoEntries.values(attribute); -} - -QStringList X509CertificateBase::subjectInfo(QSslCertificate::SubjectInfo info) const -{ - return subjectInfo(subjectInfoToString(info)); -} - -QStringList X509CertificateBase::subjectInfo(const QByteArray &attribute) const -{ - return subjectInfoEntries.values(attribute); -} - -QList<QByteArray> X509CertificateBase::subjectInfoAttributes() const -{ - return subjectInfoEntries.uniqueKeys(); -} - -QList<QByteArray> X509CertificateBase::issuerInfoAttributes() const -{ - return issuerInfoEntries.uniqueKeys(); -} - -QDateTime X509CertificateBase::effectiveDate() const -{ - return notValidBefore; -} - -QDateTime X509CertificateBase::expiryDate() const -{ - return notValidAfter; -} - -qsizetype X509CertificateBase::numberOfExtensions() const -{ - return extensions.size(); -} - -QString X509CertificateBase::oidForExtension(qsizetype index) const -{ - Q_ASSERT(validIndex(index)); - return extensions[index].oid; -} - -QString X509CertificateBase::nameForExtension(qsizetype index) const -{ - Q_ASSERT(validIndex(index)); - return extensions[index].name; -} - -QVariant X509CertificateBase::valueForExtension(qsizetype index) const -{ - Q_ASSERT(validIndex(index)); - return extensions[index].value; -} - -bool X509CertificateBase::isExtensionCritical(qsizetype index) const -{ - Q_ASSERT(validIndex(index)); - return extensions[index].critical; -} - -bool X509CertificateBase::isExtensionSupported(qsizetype index) const -{ - Q_ASSERT(validIndex(index)); - return extensions[index].supported; -} - -} // namespace QTlsPrivate - -QT_END_NAMESPACE diff --git a/src/network/ssl/qx509_base_p.h b/src/network/ssl/qx509_base_p.h deleted file mode 100644 index 6905848e46..0000000000 --- a/src/network/ssl/qx509_base_p.h +++ /dev/null @@ -1,125 +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$ -** -****************************************************************************/ - -#ifndef QX509CERTIFICATE_BASE_P_H -#define QX509CERTIFICATE_BASE_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 <private/qtnetworkglobal_p.h> - -#include <private/qtlsbackend_p.h> - -#include <qssl.h> - -#include <QtCore/qbytearray.h> -#include <QtCore/qstring.h> -#include <QtCore/qglobal.h> -#include <QtCore/qlist.h> - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -class X509CertificateBase : public X509Certificate -{ -public: - bool isNull() const override; - QByteArray version() const override; - QByteArray serialNumber() const override; - QStringList issuerInfo(QSslCertificate::SubjectInfo info) const override; - QStringList issuerInfo(const QByteArray &attribute) const override; - QStringList subjectInfo(QSslCertificate::SubjectInfo info) const override; - QStringList subjectInfo(const QByteArray &attribute) const override; - QList<QByteArray> subjectInfoAttributes() const override; - QList<QByteArray> issuerInfoAttributes() const override; - QDateTime effectiveDate() const override; - QDateTime expiryDate() const override; - - qsizetype numberOfExtensions() const override; - QString oidForExtension(qsizetype index) const override; - QString nameForExtension(qsizetype index) const override; - QVariant valueForExtension(qsizetype index) const override; - bool isExtensionCritical(qsizetype index) const override; - bool isExtensionSupported(qsizetype index) const override; - - static QByteArray subjectInfoToString(QSslCertificate::SubjectInfo info); - static bool matchLineFeed(const QByteArray &pem, int *offset); - -protected: - bool validIndex(qsizetype index) const - { - return index >= 0 && index < extensions.size(); - } - - bool null = true; - QByteArray versionString; - QByteArray serialNumberString; - - QMultiMap<QByteArray, QString> issuerInfoEntries; - QMultiMap<QByteArray, QString> subjectInfoEntries; - QDateTime notValidAfter; - QDateTime notValidBefore; - - struct X509CertificateExtension - { - QString oid; - QString name; - QVariant value; - bool critical = false; - bool supported = false; - }; - - QList<X509CertificateExtension> extensions; -}; - -} // namespace QTlsPrivate - -QT_END_NAMESPACE - -#endif // QX509CERTIFICATE_BASE_P_H diff --git a/src/network/ssl/qx509_generic.cpp b/src/network/ssl/qx509_generic.cpp deleted file mode 100644 index 40178f5e7c..0000000000 --- a/src/network/ssl/qx509_generic.cpp +++ /dev/null @@ -1,467 +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 "qsslcertificate_p.h" -#include "qx509_generic_p.h" -#include "qasn1element_p.h" - -#include "qssl_p.h" - -#include <qhostaddress.h> -#include <qendian.h> -#include <qhash.h> - -#include <memory> - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -namespace { - -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(':'); -} - -} // Unnamed namespace. - -bool X509CertificateGeneric::isEqual(const X509Certificate &rhs) const -{ - const auto &other = static_cast<const X509CertificateGeneric &>(rhs); - return derData == other.derData; -} - -bool X509CertificateGeneric::isSelfSigned() const -{ - if (null) - return false; - - qCWarning(lcTlsBackend, "QSslCertificate::isSelfSigned: This function does not check, whether the certificate " - "is actually signed. It just checks whether issuer and subject are identical"); - return subjectMatchesIssuer; -} - -QMultiMap<QSsl::AlternativeNameEntryType, QString> X509CertificateGeneric::subjectAlternativeNames() const -{ - return saNames; -} - -#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----" -#define ENDCERTSTRING "-----END CERTIFICATE-----" - -QByteArray X509CertificateGeneric::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 X509CertificateGeneric::toDer() const -{ - return derData; -} - -QString X509CertificateGeneric::toText() const -{ - Q_UNIMPLEMENTED(); - return {}; -} - -Qt::HANDLE X509CertificateGeneric::handle() const -{ - Q_UNIMPLEMENTED(); - return nullptr; -} - -size_t X509CertificateGeneric::hash(size_t seed) const noexcept -{ - return qHash(toDer(), seed); -} - -QList<QSslCertificate> X509CertificateGeneric::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)); - certificates << certificatesFromDer(decoded, 1);; - } - - return certificates; -} - -QList<QSslCertificate> X509CertificateGeneric::certificatesFromDer(const QByteArray &der, int count) -{ - QList<QSslCertificate> certificates; - - QByteArray data = der; - while (count == -1 || certificates.size() < count) { - QSslCertificate cert; - auto *certBackend = QTlsBackend::backend<X509CertificateGeneric>(cert); - if (!certBackend->parse(data)) - break; - - certificates << cert; - data.remove(0, certBackend->derData.size()); - } - - return certificates; -} - -bool X509CertificateGeneric::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()); - issuerInfoEntries = 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()); - subjectInfoEntries = 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) { - X509CertificateExtension 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: - saNames.insert(QSsl::EmailEntry, nameElem.toString()); - extValue[QStringLiteral("email")] = nameElem.toString(); - break; - case QAsn1Element::DnsNameType: - saNames.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<quint32 *>(ipAddrValue.data()))); - break; - case 16: // IPv6 - ipAddress = QHostAddress(reinterpret_cast<quint8 *>(ipAddrValue.data())); - break; - default: // Unknown IP address format - break; - } - if (!ipAddress.isNull()) { - saNames.insert(QSsl::IpAddressEntry, ipAddress.toString()); - extValue[QStringLiteral("IP")] = ipAddress.toString(); - } - break; - } - default: - break; - } - } - extension.value = extValue; - extension.supported = true; - } - } - - extensions << extension; - } - } - } - } - - derData = data.left(dataStream.device()->pos()); - null = false; - return true; -} - -bool X509CertificateGeneric::parseExtension(const QByteArray &data, X509CertificateExtension &extension) -{ - bool ok = false; - 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.critical = critical; - extension.supported = supported; - extension.oid = QString::fromLatin1(oid); - extension.name = QString::fromLatin1(oidElem.toObjectName()); - extension.value = value; - - return true; -} - -} // namespace QTlsPrivate - -QT_END_NAMESPACE diff --git a/src/network/ssl/qx509_generic_p.h b/src/network/ssl/qx509_generic_p.h deleted file mode 100644 index b0dd3ec4b5..0000000000 --- a/src/network/ssl/qx509_generic_p.h +++ /dev/null @@ -1,101 +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$ -** -****************************************************************************/ -#ifndef QX509_GENERIC_P_H -#define QX509_GENERIC_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 <private/qtnetworkglobal_p.h> - -#include <private/qtlsbackend_p.h> -#include <private/qx509_base_p.h> - -#include <QtCore/qbytearray.h> -#include <QtCore/qglobal.h> - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -// TLSTODO: This class is what previously was known as qsslcertificate_qt. -// A part of SecureTransport and Schannel plugin. -class X509CertificateGeneric : public X509CertificateBase -{ -public: - bool isEqual(const X509Certificate &rhs) const override; - bool isSelfSigned() const override; - - QMultiMap<QSsl::AlternativeNameEntryType, QString> subjectAlternativeNames() const override; - QByteArray toPem() const override; - QByteArray toDer() const override; - QString toText() const override; - Qt::HANDLE handle() const override; - - size_t hash(size_t seed) const noexcept override; - - static QList<QSslCertificate> certificatesFromPem(const QByteArray &pem, int count); - static QList<QSslCertificate> certificatesFromDer(const QByteArray &der, int count); - -protected: - - bool subjectMatchesIssuer = false; - QSsl::KeyAlgorithm publicKeyAlgorithm = QSsl::Rsa; - QByteArray publicKeyDerData; - - QMultiMap<QSsl::AlternativeNameEntryType, QString> saNames; - QByteArray derData; - - bool parse(const QByteArray &data); - bool parseExtension(const QByteArray &data, X509CertificateExtension &extension); -}; - -} // namespace QTlsPrivate - -QT_END_NAMESPACE - -#endif // QX509_GENERIC_P_H diff --git a/src/network/ssl/qx509_openssl.cpp b/src/network/ssl/qx509_openssl.cpp deleted file mode 100644 index 6c4a83be0b..0000000000 --- a/src/network/ssl/qx509_openssl.cpp +++ /dev/null @@ -1,929 +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 "qsslcertificate_p.h" -#include "qtlskey_openssl_p.h" -#include "qx509_openssl_p.h" - -#include "qsslsocket_openssl_symbols_p.h" -#include "qtlsbackend_openssl_p.h" -#include "qtls_openssl_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 QTlsPrivate { - -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(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(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 = QTlsBackendOpenSSL::s_indexForSSLExtraData - + TlsCryptographOpenSSL::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::rootCertOnDemandLoadingSupported()) - 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 QTlsPrivate - -QT_END_NAMESPACE diff --git a/src/network/ssl/qx509_openssl_p.h b/src/network/ssl/qx509_openssl_p.h deleted file mode 100644 index 5bc5ad63f5..0000000000 --- a/src/network/ssl/qx509_openssl_p.h +++ /dev/null @@ -1,133 +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$ -** -****************************************************************************/ - -#ifndef QX509_OPENSSL_P_H -#define QX509_OPENSSL_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 <private/qtnetworkglobal_p.h> - -#include <private/qopenssl_p.h> - -#include <private/qtlsbackend_p.h> -#include <private/qx509_base_p.h> - -#include <QtCore/qvariant.h> -#include <QtCore/qglobal.h> -#include <QtCore/qstring.h> - -#include <openssl/x509.h> - -#include <algorithm> - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -// TLSTODO: This class is essentially what qsslcertificate_openssl.cpp -// contains - OpenSSL-based version of QSslCertificatePrivate. Remove -// this comment when plugins are ready. -class X509CertificateOpenSSL final : public X509CertificateBase -{ -public: - X509CertificateOpenSSL(); - ~X509CertificateOpenSSL(); - - // TLSTODO: in future may become movable/copyable (ref-counted based - // OpenSSL's X509 implementation). - - bool isEqual(const X509Certificate &rhs) const override; - bool isSelfSigned() const override; - QMultiMap<QSsl::AlternativeNameEntryType, QString> subjectAlternativeNames() const override; - TlsKey *publicKey() const override; - - QByteArray toPem() const override; - QByteArray toDer() const override; - QString toText() const override; - Qt::HANDLE handle() const override; - - size_t hash(size_t seed) const noexcept override; - - // TLSTODO: these are needed by qsslsocket_openssl and later, by - // TLS code inside OpenSSL plugin. Remove this comment when - // plugins are ready. - static QSslCertificate certificateFromX509(X509 *x); - static QList<QSslCertificate> stackOfX509ToQSslCertificates(STACK_OF(X509) *x509); - static QSslErrorEntry errorEntryFromStoreContext(X509_STORE_CTX *ctx); - - // TLSTODO: remove this comment when plugins are in place. This is what QSslSocketPrivate::verify() - // in qsslsocket_openssl.cpp is (was) doing (in the past). - static QList<QSslError> verify(const QList<QSslCertificate> &chain, const QString &hostName); - static QList<QSslError> verify(const QList<QSslCertificate> &caCertificates, - const QList<QSslCertificate> &certificateChain, - const QString &hostName); - - static QList<QSslCertificate> certificatesFromPem(const QByteArray &pem, int count); - static QList<QSslCertificate> certificatesFromDer(const QByteArray &der, int count); - static bool importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert, - QList<QSslCertificate> *caCertificates, - const QByteArray &passPhrase); - - static QSslError openSSLErrorToQSslError(int errorCode, const QSslCertificate &cert); -private: - void parseExtensions(); - static X509CertificateExtension convertExtension(X509_EXTENSION *ext); - - X509 *x509 = nullptr; - - Q_DISABLE_COPY_MOVE(X509CertificateOpenSSL) -}; - -extern "C" int qt_X509Callback(int ok, X509_STORE_CTX *ctx); - -} // namespace QTlsPrivate - -QT_END_NAMESPACE - -#endif // QX509_OPENSSL_P_H diff --git a/src/network/ssl/qx509_schannel.cpp b/src/network/ssl/qx509_schannel.cpp deleted file mode 100644 index a7371e0d97..0000000000 --- a/src/network/ssl/qx509_schannel.cpp +++ /dev/null @@ -1,87 +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 "qtlskey_schannel_p.h" -#include "qsslcertificate_p.h" -#include "qx509_schannel_p.h" - -#include <memory> - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -X509CertificateSchannel::X509CertificateSchannel() = default; - -X509CertificateSchannel::~X509CertificateSchannel() -{ - if (certificateContext) - CertFreeCertificateContext(certificateContext); -} - -TlsKey *X509CertificateSchannel::publicKey() const -{ - auto key = std::make_unique<TlsKeySchannel>(QSsl::PublicKey); - if (publicKeyAlgorithm != QSsl::Opaque) - key->decodeDer(QSsl::PublicKey, publicKeyAlgorithm, publicKeyDerData, {}, false); - - 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<X509CertificateSchannel>(certificate); - Q_ASSERT(certBackend); - certBackend->certificateContext = CertDuplicateCertificateContext(certificateContext); - return certificate; -} - -} // namespace QTlsPrivate - -QT_END_NAMESPACE - diff --git a/src/network/ssl/qx509_schannel_p.h b/src/network/ssl/qx509_schannel_p.h deleted file mode 100644 index 3b5d567c7b..0000000000 --- a/src/network/ssl/qx509_schannel_p.h +++ /dev/null @@ -1,87 +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$ -** -****************************************************************************/ - -#ifndef QX509_SCHANNEL_P_H -#define QX509_SCHANNEL_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 <private/qtnetworkglobal_p.h> - -#include <private/qx509_generic_p.h> - -#include <QtCore/qglobal.h> - -#include <QtCore/qt_windows.h> -#include <wincrypt.h> - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -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 QTlsPrivate - -QT_END_NAMESPACE - -#endif // QX509_SCHANNEL_P_H diff --git a/src/network/ssl/qx509_st.cpp b/src/network/ssl/qx509_st.cpp deleted file mode 100644 index 737b15cef8..0000000000 --- a/src/network/ssl/qx509_st.cpp +++ /dev/null @@ -1,61 +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 "qtlskey_st_p.h" -#include "qx509_st_p.h" - -#include <memory> - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -TlsKey *X509CertificateSecureTransport::publicKey() const -{ - auto key = std::make_unique<TlsKeySecureTransport>(QSsl::PublicKey); - if (publicKeyAlgorithm != QSsl::Opaque) - key->decodeDer(QSsl::PublicKey, publicKeyAlgorithm, publicKeyDerData, {}, false); - - return key.release(); -} - -} // namespace QTlsPrivate - -QT_END_NAMESPACE - diff --git a/src/network/ssl/qx509_st_p.h b/src/network/ssl/qx509_st_p.h deleted file mode 100644 index 8c3969442c..0000000000 --- a/src/network/ssl/qx509_st_p.h +++ /dev/null @@ -1,74 +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$ -** -****************************************************************************/ - -#ifndef QX509_ST_P_H -#define QX509_ST_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 <private/qtnetworkglobal_p.h> - -#include <private/qx509_generic_p.h> - -#include <QtCore/qglobal.h> - -QT_BEGIN_NAMESPACE - -namespace QTlsPrivate { - -class X509CertificateSecureTransport final : public X509CertificateGeneric -{ -public: - TlsKey *publicKey() const override; -}; - -} // namespace QTlsPrivate - -QT_END_NAMESPACE - -#endif // QX509_ST_P_H |