diff options
Diffstat (limited to 'src/network/ssl')
76 files changed, 4701 insertions, 19256 deletions
diff --git a/src/network/ssl/qasn1element.cpp b/src/network/ssl/qasn1element.cpp deleted file mode 100644 index 13fc095e12..0000000000 --- a/src/network/ssl/qasn1element.cpp +++ /dev/null @@ -1,399 +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 - if (mValue.at(0) & 0x80) { - 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.cpp b/src/network/ssl/qdtls.cpp index af89740898..38ce144c8a 100644 --- a/src/network/ssl/qdtls.cpp +++ b/src/network/ssl/qdtls.cpp @@ -1,45 +1,10 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qsslconfiguration.h" -#include "qdtls_openssl_p.h" +#include "qsslsocket_p.h" #include "qudpsocket.h" +#include "qsslcipher.h" #include "qdtls_p.h" #include "qssl_p.h" #include "qdtls.h" @@ -57,7 +22,7 @@ The QDtlsClientVerifier class implements server-side DTLS cookie generation and verification. Datagram security protocols are highly susceptible to a - variety of Denial-of-Service attacks. According to \l {https://tools.ietf.org/html/rfc6347#section-4.2.1}{RFC 6347, section 4.2.1}, + variety of Denial-of-Service attacks. According to \l {RFC 6347, section 4.2.1}, these are two of the more common types of attack: \list @@ -70,7 +35,7 @@ which can be quite large, thus flooding the victim machine with datagrams. \endlist - As a countermeasure to these attacks, \l {https://tools.ietf.org/html/rfc6347#section-4.2.1}{RFC 6347, section 4.2.1} + As a countermeasure to these attacks, \l {RFC 6347, section 4.2.1} proposes a stateless cookie technique that a server may deploy: \list @@ -118,7 +83,7 @@ \note The default secret is shared by all objects of the classes QDtlsClientVerifier and QDtls. Since this can impose security risks, RFC 6347 recommends to change - the server's secret frequently. Please see \l {https://tools.ietf.org/html/rfc6347}{RFC 6347, section 4.2.1} + the server's secret frequently. Please see \l {RFC 6347, section 4.2.1} for hints about possible server implementations. Cookie generator parameters can be set using the class QDtlsClientVerifier::GeneratorParameters and setCookieGeneratorParameters(): @@ -249,7 +214,7 @@ \warning It's recommended to call shutdown() before destroying the client's QDtls object if you are planning to re-use the same port number to connect to the server later. Otherwise, the server may drop incoming ClientHello messages, - see \l{https://tools.ietf.org/html/rfc6347#page-25}{RFC 6347, section 4.2.8} + see \l {RFC 6347, section 4.2.8} for more details and implementation hints. If the server does not use QDtlsClientVerifier, it \e must configure its @@ -337,72 +302,6 @@ QT_BEGIN_NAMESPACE -QSslConfiguration QDtlsBasePrivate::configuration() const -{ - auto copyPrivate = new QSslConfigurationPrivate(dtlsConfiguration); - copyPrivate->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up - QSslConfiguration copy(copyPrivate); - copyPrivate->sessionCipher = sessionCipher; - copyPrivate->sessionProtocol = sessionProtocol; - - return copy; -} - -void QDtlsBasePrivate::setConfiguration(const QSslConfiguration &configuration) -{ - dtlsConfiguration.localCertificateChain = configuration.localCertificateChain(); - dtlsConfiguration.privateKey = configuration.privateKey(); - dtlsConfiguration.ciphers = configuration.ciphers(); - dtlsConfiguration.ellipticCurves = configuration.ellipticCurves(); - dtlsConfiguration.preSharedKeyIdentityHint = configuration.preSharedKeyIdentityHint(); - dtlsConfiguration.dhParams = configuration.diffieHellmanParameters(); - dtlsConfiguration.caCertificates = configuration.caCertificates(); - dtlsConfiguration.peerVerifyDepth = configuration.peerVerifyDepth(); - dtlsConfiguration.peerVerifyMode = configuration.peerVerifyMode(); - dtlsConfiguration.protocol = configuration.protocol(); - dtlsConfiguration.sslOptions = configuration.d->sslOptions; - dtlsConfiguration.sslSession = configuration.sessionTicket(); - dtlsConfiguration.sslSessionTicketLifeTimeHint = configuration.sessionTicketLifeTimeHint(); - dtlsConfiguration.nextAllowedProtocols = configuration.allowedNextProtocols(); - dtlsConfiguration.nextNegotiatedProtocol = configuration.nextNegotiatedProtocol(); - dtlsConfiguration.nextProtocolNegotiationStatus = configuration.nextProtocolNegotiationStatus(); - dtlsConfiguration.dtlsCookieEnabled = configuration.dtlsCookieVerificationEnabled(); - dtlsConfiguration.allowRootCertOnDemandLoading = configuration.d->allowRootCertOnDemandLoading; - dtlsConfiguration.backendConfig = configuration.backendConfiguration(); - - clearDtlsError(); -} - -bool QDtlsBasePrivate::setCookieGeneratorParameters(QCryptographicHash::Algorithm alg, - const QByteArray &key) -{ - if (!key.size()) { - setDtlsError(QDtlsError::InvalidInputParameters, - QDtls::tr("Invalid (empty) secret")); - return false; - } - - clearDtlsError(); - - hashAlgorithm = alg; - secret = key; - - return true; -} - -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; - } -} - static QString msgUnsupportedMulticastAddress() { return QDtls::tr("Multicast and broadcast addresses are not supported"); @@ -434,22 +333,37 @@ QDtlsClientVerifier::GeneratorParameters::GeneratorParameters(QCryptographicHash { } +QDtlsClientVerifierPrivate::QDtlsClientVerifierPrivate() +{ + const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse(); + if (!tlsBackend) { + qCWarning(lcSsl, "No TLS backend is available, cannot verify DTLS client"); + return; + } + backend.reset(tlsBackend->createDtlsCookieVerifier()); + if (!backend.get()) + qCWarning(lcSsl) << "The backend" << tlsBackend->backendName() << "does not support DTLS cookies"; +} + +QDtlsClientVerifierPrivate::~QDtlsClientVerifierPrivate() = default; + /*! Constructs a QDtlsClientVerifier object, \a parent is passed to QObject's constructor. */ QDtlsClientVerifier::QDtlsClientVerifier(QObject *parent) - : QObject(*new QDtlsClientVerifierOpenSSL, parent) + : QObject(*new QDtlsClientVerifierPrivate, parent) { Q_D(QDtlsClientVerifier); - d->mode = QSslSocket::SslServerMode; - // The default configuration suffices: verifier never does a full - // handshake and upon verifying a cookie in a client hello message, - // it reports success. - auto conf = QSslConfiguration::defaultDtlsConfiguration(); - conf.setPeerVerifyMode(QSslSocket::VerifyNone); - d->setConfiguration(conf); + if (auto *backend = d->backend.get()) { + // The default configuration suffices: verifier never does a full + // handshake and upon verifying a cookie in a client hello message, + // it reports success. + auto conf = QSslConfiguration::defaultDtlsConfiguration(); + conf.setPeerVerifyMode(QSslSocket::VerifyNone); + backend->setConfiguration(conf); + } } /*! @@ -473,8 +387,10 @@ QDtlsClientVerifier::~QDtlsClientVerifier() bool QDtlsClientVerifier::setCookieGeneratorParameters(const GeneratorParameters ¶ms) { Q_D(QDtlsClientVerifier); + if (auto *backend = d->backend.get()) + return backend->setCookieGeneratorParameters(params); - return d->setCookieGeneratorParameters(params.hash, params.secret); + return false; } /*! @@ -491,7 +407,10 @@ QDtlsClientVerifier::GeneratorParameters QDtlsClientVerifier::cookieGeneratorPar { Q_D(const QDtlsClientVerifier); - return {d->hashAlgorithm, d->secret}; + if (const auto *backend = d->backend.get()) + return backend->cookieGeneratorParameters(); + + return {}; } /*! @@ -514,19 +433,23 @@ bool QDtlsClientVerifier::verifyClient(QUdpSocket *socket, const QByteArray &dgr { Q_D(QDtlsClientVerifier); + auto *backend = d->backend.get(); + if (!backend) + return false; + if (!socket || address.isNull() || !dgram.size()) { - d->setDtlsError(QDtlsError::InvalidInputParameters, - tr("A valid UDP socket, non-empty datagram, valid address/port were expected")); + backend->setDtlsError(QDtlsError::InvalidInputParameters, + tr("A valid UDP socket, non-empty datagram, and valid address/port were expected")); return false; } if (address.isBroadcast() || address.isMulticast()) { - d->setDtlsError(QDtlsError::InvalidInputParameters, - msgUnsupportedMulticastAddress()); + backend->setDtlsError(QDtlsError::InvalidInputParameters, + msgUnsupportedMulticastAddress()); return false; } - return d->verifyClient(socket, dgram, address, port); + return backend->verifyClient(socket, dgram, address, port); } /*! @@ -539,7 +462,10 @@ QByteArray QDtlsClientVerifier::verifiedHello() const { Q_D(const QDtlsClientVerifier); - return d->verifiedClientHello; + if (const auto *backend = d->backend.get()) + return backend->verifiedHello(); + + return {}; } /*! @@ -551,7 +477,10 @@ QDtlsError QDtlsClientVerifier::dtlsError() const { Q_D(const QDtlsClientVerifier); - return d->errorCode; + if (const auto *backend = d->backend.get()) + return backend->error(); + + return QDtlsError::TlsInitializationError; } /*! @@ -561,11 +490,17 @@ QDtlsError QDtlsClientVerifier::dtlsError() const */ QString QDtlsClientVerifier::dtlsErrorString() const { - Q_D(const QDtlsBase); + Q_D(const QDtlsClientVerifier); + + if (const auto *backend = d->backend.get()) + return backend->errorString(); - return d->errorDescription; + return QStringLiteral("No TLS backend is available, no client verification"); } +QDtlsPrivate::QDtlsPrivate() = default; +QDtlsPrivate::~QDtlsPrivate() = default; + /*! Creates a QDtls object, \a parent is passed to the QObject constructor. \a mode is QSslSocket::SslServerMode for a server-side DTLS connection or @@ -574,11 +509,19 @@ QString QDtlsClientVerifier::dtlsErrorString() const \sa sslMode(), QSslSocket::SslMode */ QDtls::QDtls(QSslSocket::SslMode mode, QObject *parent) - : QObject(*new QDtlsPrivateOpenSSL, parent) + : QObject(*new QDtlsPrivate, parent) { Q_D(QDtls); - - d->mode = mode; + const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse(); + if (!tlsBackend) { + qCWarning(lcSsl, "No TLS backend found, QDtls is unsupported"); + return; + } + d->backend.reset(tlsBackend->createDtlsCryptograph(this, mode)); + if (!d->backend.get()) { + qCWarning(lcSsl) << "TLS backend" << tlsBackend->backendName() + << "does not support the protocol DTLS"; + } setDtlsConfiguration(QSslConfiguration::defaultDtlsConfiguration()); } @@ -601,29 +544,30 @@ bool QDtls::setPeer(const QHostAddress &address, quint16 port, { Q_D(QDtls); - if (d->handshakeState != HandshakeNotStarted) { - d->setDtlsError(QDtlsError::InvalidOperation, - tr("Cannot set peer after handshake started")); + auto *backend = d->backend.get(); + if (!backend) + return false; + + if (backend->state() != HandshakeNotStarted) { + backend->setDtlsError(QDtlsError::InvalidOperation, + tr("Cannot set peer after handshake started")); return false; } if (address.isNull()) { - d->setDtlsError(QDtlsError::InvalidInputParameters, - tr("Invalid address")); + backend->setDtlsError(QDtlsError::InvalidInputParameters, + tr("Invalid address")); return false; } if (address.isBroadcast() || address.isMulticast()) { - d->setDtlsError(QDtlsError::InvalidInputParameters, - msgUnsupportedMulticastAddress()); + backend->setDtlsError(QDtlsError::InvalidInputParameters, + msgUnsupportedMulticastAddress()); return false; } - d->clearDtlsError(); - - d->remoteAddress = address; - d->remotePort = port; - d->peerVerificationName = verificationName; + backend->clearDtlsError(); + backend->setPeer(address, port, verificationName); return true; } @@ -640,14 +584,18 @@ bool QDtls::setPeerVerificationName(const QString &name) { Q_D(QDtls); - if (d->handshakeState != HandshakeNotStarted) { - d->setDtlsError(QDtlsError::InvalidOperation, + auto *backend = d->backend.get(); + if (!backend) + return false; + + if (backend->state() != HandshakeNotStarted) { + backend->setDtlsError(QDtlsError::InvalidOperation, tr("Cannot set verification name after handshake started")); return false; } - d->clearDtlsError(); - d->peerVerificationName = name; + backend->clearDtlsError(); + backend->setPeerVerificationName(name); return true; } @@ -661,7 +609,10 @@ QHostAddress QDtls::peerAddress() const { Q_D(const QDtls); - return d->remoteAddress; + if (const auto *backend = d->backend.get()) + return backend->peerAddress(); + + return {}; } /*! @@ -671,9 +622,12 @@ QHostAddress QDtls::peerAddress() const */ quint16 QDtls::peerPort() const { - Q_D(const QDtlsBase); + Q_D(const QDtls); + + if (const auto *backend = d->backend.get()) + return backend->peerPort(); - return d->remotePort; + return 0; } /*! @@ -686,7 +640,10 @@ QString QDtls::peerVerificationName() const { Q_D(const QDtls); - return d->peerVerificationName; + if (const auto *backend = d->backend.get()) + return backend->peerVerificationName(); + + return {}; } /*! @@ -699,7 +656,10 @@ QSslSocket::SslMode QDtls::sslMode() const { Q_D(const QDtls); - return d->mode; + if (const auto *backend = d->backend.get()) + return backend->cryptographMode(); + + return QSslSocket::UnencryptedMode; } /*! @@ -712,7 +672,8 @@ void QDtls::setMtuHint(quint16 mtuHint) { Q_D(QDtls); - d->mtuHint = mtuHint; + if (auto *backend = d->backend.get()) + backend->setDtlsMtuHint(mtuHint); } /*! @@ -724,7 +685,10 @@ quint16 QDtls::mtuHint() const { Q_D(const QDtls); - return d->mtuHint; + if (const auto *backend = d->backend.get()) + return backend->dtlsMtuHint(); + + return 0; } /*! @@ -741,7 +705,10 @@ bool QDtls::setCookieGeneratorParameters(const GeneratorParameters ¶ms) { Q_D(QDtls); - return d->setCookieGeneratorParameters(params.hash, params.secret); + if (auto *backend = d->backend.get()) + backend->setCookieGeneratorParameters(params); + + return false; } /*! @@ -759,7 +726,10 @@ QDtls::GeneratorParameters QDtls::cookieGeneratorParameters() const { Q_D(const QDtls); - return {d->hashAlgorithm, d->secret}; + if (const auto *backend = d->backend.get()) + return backend->cookieGeneratorParameters(); + + return {}; } /*! @@ -774,13 +744,17 @@ bool QDtls::setDtlsConfiguration(const QSslConfiguration &configuration) { Q_D(QDtls); - if (d->handshakeState != HandshakeNotStarted) { - d->setDtlsError(QDtlsError::InvalidOperation, - tr("Cannot set configuration after handshake started")); + auto *backend = d->backend.get(); + if (!backend) + return false; + + if (backend->state() != HandshakeNotStarted) { + backend->setDtlsError(QDtlsError::InvalidOperation, + tr("Cannot set configuration after handshake started")); return false; } - d->setConfiguration(configuration); + backend->setConfiguration(configuration); return true; } @@ -793,8 +767,10 @@ bool QDtls::setDtlsConfiguration(const QSslConfiguration &configuration) QSslConfiguration QDtls::dtlsConfiguration() const { Q_D(const QDtls); + if (const auto *backend = d->backend.get()) + return backend->configuration(); - return d->configuration(); + return {}; } /*! @@ -806,7 +782,10 @@ QDtls::HandshakeState QDtls::handshakeState()const { Q_D(const QDtls); - return d->handshakeState; + if (const auto *backend = d->backend.get()) + return backend->state(); + + return QDtls::HandshakeNotStarted; } /*! @@ -832,13 +811,17 @@ bool QDtls::doHandshake(QUdpSocket *socket, const QByteArray &dgram) { Q_D(QDtls); - if (d->handshakeState == HandshakeNotStarted) + auto *backend = d->backend.get(); + if (!backend) + return false; + + if (backend->state() == HandshakeNotStarted) return startHandshake(socket, dgram); - else if (d->handshakeState == HandshakeInProgress) + else if (backend->state() == HandshakeInProgress) return continueHandshake(socket, dgram); - d->setDtlsError(QDtlsError::InvalidOperation, - tr("Cannot start/continue handshake, invalid handshake state")); + backend->setDtlsError(QDtlsError::InvalidOperation, + tr("Cannot start/continue handshake, invalid handshake state")); return false; } @@ -849,34 +832,38 @@ bool QDtls::startHandshake(QUdpSocket *socket, const QByteArray &datagram) { Q_D(QDtls); + auto *backend = d->backend.get(); + if (!backend) + return false; + if (!socket) { - d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket")); + backend->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket")); return false; } - if (d->remoteAddress.isNull()) { - d->setDtlsError(QDtlsError::InvalidOperation, - tr("To start a handshake you must set peer's address and port first")); + if (backend->peerAddress().isNull()) { + backend->setDtlsError(QDtlsError::InvalidOperation, + tr("To start a handshake you must set peer's address and port first")); return false; } if (sslMode() == QSslSocket::SslServerMode && !datagram.size()) { - d->setDtlsError(QDtlsError::InvalidInputParameters, - tr("To start a handshake, DTLS server requires non-empty datagram (client hello)")); + backend->setDtlsError(QDtlsError::InvalidInputParameters, + tr("To start a handshake, DTLS server requires non-empty datagram (client hello)")); return false; } - if (d->handshakeState != HandshakeNotStarted) { - d->setDtlsError(QDtlsError::InvalidOperation, - tr("Cannot start handshake, already done/in progress")); + if (backend->state() != HandshakeNotStarted) { + backend->setDtlsError(QDtlsError::InvalidOperation, + tr("Cannot start handshake, already done/in progress")); return false; } - return d->startHandshake(socket, datagram); + return backend->startHandshake(socket, datagram); } /*! - If a timeout occures during the handshake, the handshakeTimeout() signal + If a timeout occurs during the handshake, the handshakeTimeout() signal is emitted. The application must call handleTimeout() to retransmit handshake messages; handleTimeout() returns \c true if a timeout has occurred, false otherwise. \a socket must be a valid pointer. @@ -887,12 +874,16 @@ bool QDtls::handleTimeout(QUdpSocket *socket) { Q_D(QDtls); + auto *backend = d->backend.get(); + if (!backend) + return false; + if (!socket) { - d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket")); + backend->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket")); return false; } - return d->handleTimeout(socket); + return backend->handleTimeout(socket); } /*! @@ -902,19 +893,23 @@ bool QDtls::continueHandshake(QUdpSocket *socket, const QByteArray &datagram) { Q_D(QDtls); + auto *backend = d->backend.get(); + if (!backend) + return false; + if (!socket || !datagram.size()) { - d->setDtlsError(QDtlsError::InvalidInputParameters, - tr("A valid QUdpSocket and non-empty datagram are needed to continue the handshake")); + backend->setDtlsError(QDtlsError::InvalidInputParameters, + tr("A valid QUdpSocket and non-empty datagram are needed to continue the handshake")); return false; } - if (d->handshakeState != HandshakeInProgress) { - d->setDtlsError(QDtlsError::InvalidOperation, - tr("Cannot continue handshake, not in InProgress state")); + if (backend->state() != HandshakeInProgress) { + backend->setDtlsError(QDtlsError::InvalidOperation, + tr("Cannot continue handshake, not in InProgress state")); return false; } - return d->continueHandshake(socket, datagram); + return backend->continueHandshake(socket, datagram); } /*! @@ -929,18 +924,22 @@ bool QDtls::resumeHandshake(QUdpSocket *socket) { Q_D(QDtls); + auto *backend = d->backend.get(); + if (!backend) + return false; + if (!socket) { - d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket")); + backend->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket")); return false; } - if (d->handshakeState != PeerVerificationFailed) { - d->setDtlsError(QDtlsError::InvalidOperation, - tr("Cannot resume, not in VerificationError state")); + if (backend->state() != PeerVerificationFailed) { + backend->setDtlsError(QDtlsError::InvalidOperation, + tr("Cannot resume, not in VerificationError state")); return false; } - return d->resumeHandshake(socket); + return backend->resumeHandshake(socket); } /*! @@ -953,18 +952,22 @@ bool QDtls::abortHandshake(QUdpSocket *socket) { Q_D(QDtls); + auto *backend = d->backend.get(); + if (!backend) + return false; + if (!socket) { - d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket")); + backend->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket")); return false; } - if (d->handshakeState != PeerVerificationFailed && d->handshakeState != HandshakeInProgress) { - d->setDtlsError(QDtlsError::InvalidOperation, - tr("No handshake in progress, nothing to abort")); + if (backend->state() != PeerVerificationFailed && backend->state() != HandshakeInProgress) { + backend->setDtlsError(QDtlsError::InvalidOperation, + tr("No handshake in progress, nothing to abort")); return false; } - d->abortHandshake(socket); + backend->abortHandshake(socket); return true; } @@ -979,19 +982,23 @@ bool QDtls::shutdown(QUdpSocket *socket) { Q_D(QDtls); + auto *backend = d->backend.get(); + if (!backend) + return false; + if (!socket) { - d->setDtlsError(QDtlsError::InvalidInputParameters, - tr("Invalid (nullptr) socket")); + backend->setDtlsError(QDtlsError::InvalidInputParameters, + tr("Invalid (nullptr) socket")); return false; } - if (!d->connectionEncrypted) { - d->setDtlsError(QDtlsError::InvalidOperation, - tr("Cannot send shutdown alert, not encrypted")); + if (!backend->isConnectionEncrypted()) { + backend->setDtlsError(QDtlsError::InvalidOperation, + tr("Cannot send shutdown alert, not encrypted")); return false; } - d->sendShutdownAlert(socket); + backend->sendShutdownAlert(socket); return true; } @@ -1004,7 +1011,11 @@ bool QDtls::isConnectionEncrypted() const { Q_D(const QDtls); - return d->connectionEncrypted; + + if (const auto *backend = d->backend.get()) + return backend->isConnectionEncrypted(); + + return false; } /*! @@ -1023,7 +1034,10 @@ QSslCipher QDtls::sessionCipher() const { Q_D(const QDtls); - return d->sessionCipher; + if (const auto *backend = d->backend.get()) + return backend->dtlsSessionCipher(); + + return {}; } /*! @@ -1040,7 +1054,10 @@ QSsl::SslProtocol QDtls::sessionProtocol() const { Q_D(const QDtls); - return d->sessionProtocol; + if (const auto *backend = d->backend.get()) + return backend->dtlsSessionProtocol(); + + return QSsl::UnknownProtocol; } /*! @@ -1055,18 +1072,22 @@ qint64 QDtls::writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram { Q_D(QDtls); + auto *backend = d->backend.get(); + if (!backend) + return -1; + if (!socket) { - d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket")); + backend->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket")); return -1; } if (!isConnectionEncrypted()) { - d->setDtlsError(QDtlsError::InvalidOperation, - tr("Cannot write a datagram, not in encrypted state")); + backend->setDtlsError(QDtlsError::InvalidOperation, + tr("Cannot write a datagram, not in encrypted state")); return -1; } - return d->writeDatagramEncrypted(socket, dgram); + return backend->writeDatagramEncrypted(socket, dgram); } /*! @@ -1079,21 +1100,25 @@ QByteArray QDtls::decryptDatagram(QUdpSocket *socket, const QByteArray &dgram) { Q_D(QDtls); + auto *backend = d->backend.get(); + if (!backend) + return {}; + if (!socket) { - d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket")); + backend->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket")); return {}; } if (!isConnectionEncrypted()) { - d->setDtlsError(QDtlsError::InvalidOperation, - tr("Cannot read a datagram, not in encrypted state")); + backend->setDtlsError(QDtlsError::InvalidOperation, + tr("Cannot read a datagram, not in encrypted state")); return {}; } if (!dgram.size()) return {}; - return d->decryptDatagram(socket, dgram); + return backend->decryptDatagram(socket, dgram); } /*! @@ -1105,7 +1130,10 @@ QDtlsError QDtls::dtlsError() const { Q_D(const QDtls); - return d->errorCode; + if (const auto *backend = d->backend.get()) + return backend->error(); + + return QDtlsError::NoError; } /*! @@ -1118,7 +1146,10 @@ QString QDtls::dtlsErrorString() const { Q_D(const QDtls); - return d->errorDescription; + if (const auto *backend = d->backend.get()) + return backend->errorString(); + + return {}; } /*! @@ -1131,7 +1162,11 @@ QList<QSslError> QDtls::peerVerificationErrors() const { Q_D(const QDtls); - return d->tlsErrors; + if (const auto *backend = d->backend.get()) + return backend->peerVerificationErrors(); + + //return d->tlsErrors; + return {}; } /*! @@ -1156,7 +1191,10 @@ void QDtls::ignoreVerificationErrors(const QList<QSslError> &errorsToIgnore) { Q_D(QDtls); - d->tlsErrorsToIgnore = errorsToIgnore; + if (auto *backend = d->backend.get()) + backend->ignoreVerificationErrors(errorsToIgnore); } QT_END_NAMESPACE + +#include "moc_qdtls.cpp" diff --git a/src/network/ssl/qdtls.h b/src/network/ssl/qdtls.h index aee8dc0f87..dd24aa219a 100644 --- a/src/network/ssl/qdtls.h +++ b/src/network/ssl/qdtls.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QDTLS_H #define QDTLS_H @@ -51,7 +15,7 @@ Q_MOC_INCLUDE(<QtNetwork/QSslPreSharedKeyAuthenticator>) -#ifndef Q_CLANG_QDOC +#ifndef Q_QDOC QT_REQUIRE_CONFIG(dtls); #endif diff --git a/src/network/ssl/qdtls_openssl.cpp b/src/network/ssl/qdtls_openssl.cpp deleted file mode 100644 index 3f04ab97cd..0000000000 --- a/src/network/ssl/qdtls_openssl.cpp +++ /dev/null @@ -1,1389 +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 "qsslsocket_openssl_p.h" -#include "qsslcertificate_p.h" -#include "qdtls_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, QSslSocketBackendPrivate::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, QSslSocketBackendPrivate::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(QSslErrorEntry::fromStoreContext(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, - QSslSocketBackendPrivate::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, - QSslSocketBackendPrivate::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; - } - - // Create a deep copy of our configuration - auto configurationCopy = new QSslConfigurationPrivate(dtlsBase->dtlsConfiguration); - configurationCopy->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up - - // DTLSTODO: check we do not set something DTLS-incompatible there ... - TlsContext newContext(QSslContext::sharedFromConfiguration(dtlsBase->mode, - configurationCopy, - dtlsBase->dtlsConfiguration.allowRootCertOnDemandLoading)); - - 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(), - QSslSocketBackendPrivate::s_indexForSSLExtraData, - this); - - if (set != 1 && configurationCopy->peerVerifyMode != QSslSocket::VerifyNone) { - dtlsBase->setDtlsError(QDtlsError::TlsInitializationError, - msgFunctionFailed("SSL_set_ex_data")); - return false; - } - - if (dtlsBase->mode == QSslSocket::SslServerMode) { - if (dtlsBase->dtlsConfiguration.dtlsCookieEnabled) - 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() -{ - secret = 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, QSslSocketBackendPrivate::getErrorsFromOpenSsl()); - return false; - } - - if (ret > 0) { - verifiedClientHello = dgram; - return true; - } - - return false; -} - -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() -{ - secret = dtlsutil::fallbackSecret(); - dtls.dtlsPrivate = this; -} - -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.dtlsCookieEnabled) { - 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, - QSslSocketBackendPrivate::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(); -} - -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(QSslSocketBackendPrivate::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(QSslSocketBackendPrivate::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. - - Q_Q(QDtls); - - { - QSslPreSharedKeyAuthenticator authenticator; - // Fill in some read-only fields (for client code) - if (hint) { - identityHint.clear(); - identityHint.append(hint); - // From the original code in QSslSocket: - // "it's NULL terminated, but do not include the NULL" == this fromRawData(ptr/size). - authenticator.d->identityHint = QByteArray::fromRawData(identityHint.constData(), - int(std::strlen(hint))); - } - - authenticator.d->maximumIdentityLength = int(max_identity_len) - 1; // needs to be NULL terminated - authenticator.d->maximumPreSharedKeyLength = int(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) -{ - Q_Q(QDtls); - - { - QSslPreSharedKeyAuthenticator authenticator; - // Fill in some read-only fields (for the user) - authenticator.d->identityHint = dtlsConfiguration.preSharedKeyIdentityHint; - authenticator.d->identity = identity; - authenticator.d->maximumIdentityLength = 0; // user cannot set an identity - authenticator.d->maximumPreSharedKeyLength = int(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; -} - -// The definition is located in qsslsocket_openssl.cpp. -QSslError _q_OpenSSL_to_QSslError(int errorCode, const QSslCertificate &cert); - -bool QDtlsPrivateOpenSSL::verifyPeer() -{ - // DTLSTODO: Windows-specific code for CA fetcher is not here yet. - QList<QSslError> errors; - - // Check the whole chain for blacklisting (including root, as we check for - // subjectInfo and issuer) - for (const QSslCertificate &cert : qAsConst(dtlsConfiguration.peerCertificateChain)) { - if (QSslCertificatePrivate::isBlacklisted(cert)) - errors << QSslError(QSslError::CertificateBlacklisted, cert); - } - - if (dtlsConfiguration.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 = peerVerificationName; - if (name.isEmpty()) { - Q_ASSERT(dtls.udpSocket); - name = dtls.udpSocket->peerName(); - } - - if (!QSslSocketPrivate::isMatchingHostname(dtlsConfiguration.peerCertificate, name)) - errors << QSslError(QSslError::HostNameMismatch, dtlsConfiguration.peerCertificate); - } - - // Translate errors from the error list into QSslErrors - errors.reserve(errors.size() + opensslErrors.size()); - for (const auto &error : qAsConst(opensslErrors)) { - errors << _q_OpenSSL_to_QSslError(error.code, - dtlsConfiguration.peerCertificateChain.value(error.depth)); - } - - 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()); - dtlsConfiguration.peerCertificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509); - q_X509_free(x509); - if (dtlsConfiguration.peerCertificateChain.isEmpty()) { - auto stack = q_SSL_get_peer_cert_chain(dtls.tlsConnection.data()); - dtlsConfiguration.peerCertificateChain = QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(stack); - if (!dtlsConfiguration.peerCertificate.isNull() && mode == QSslSocket::SslServerMode) - dtlsConfiguration.peerCertificateChain.prepend(dtlsConfiguration.peerCertificate); - } -} - -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()); - - const SSL_CIPHER *cipher = q_SSL_get_current_cipher(dtls.tlsConnection.data()); - sessionCipher = cipher ? QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher) - : QSslCipher(); - - // 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() -{ - Q_Q(QDtls); - - emit q->handshakeTimeout(); -} - -void QDtlsPrivateOpenSSL::resetDtls() -{ - dtls.reset(); - connectionEncrypted = false; - tlsErrors.clear(); - tlsErrorsToIgnore.clear(); - dtlsConfiguration.peerCertificate.clear(); - dtlsConfiguration.peerCertificateChain.clear(); - 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 b1fcc99d5a..0000000000 --- a/src/network/ssl/qdtls_openssl_p.h +++ /dev/null @@ -1,212 +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 QDTLS_OPENSSL_P_H -#define QDTLS_OPENSSL_P_H - -#include <private/qtnetworkglobal_p.h> - -#include <QtCore/qglobal.h> - -#include <openssl/ossl_typ.h> - -#include "qdtls_p.h" - -#include <private/qsslcontext_openssl_p.h> -#include <private/qsslsocket_openssl_p.h> - -#include <QtNetwork/qsslpresharedkeyauthenticator.h> -#include <QtNetwork/qhostaddress.h> - -#include <QtCore/qbytearray.h> -#include <QtCore/qcryptographichash.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 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 - -class QDtlsClientVerifierOpenSSL : public QDtlsClientVerifierPrivate -{ -public: - - QDtlsClientVerifierOpenSSL(); - - bool verifyClient(QUdpSocket *socket, const QByteArray &dgram, - const QHostAddress &address, quint16 port) override; - -private: - dtlsopenssl::DtlsState dtls; -}; - -class QDtlsPrivateOpenSSL : public QDtlsPrivate -{ -public: - QDtlsPrivateOpenSSL(); - - 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; - - qint64 writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &datagram) override; - QByteArray decryptDatagram(QUdpSocket *socket, const QByteArray &tlsdgram) override; - - 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; - }; - - // 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; - - Q_DECLARE_PUBLIC(QDtls) -}; - - - -QT_END_NAMESPACE - -#endif // QDTLS_OPENSSL_P_H diff --git a/src/network/ssl/qdtls_p.h b/src/network/ssl/qdtls_p.h index be54c0e06e..5d519e2344 100644 --- a/src/network/ssl/qdtls_p.h +++ b/src/network/ssl/qdtls_p.h @@ -1,62 +1,14 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QDTLS_P_H #define QDTLS_P_H #include <private/qtnetworkglobal_p.h> -#include "qdtls.h" - -#include <private/qsslconfiguration_p.h> -#include <private/qobject_p.h> - -#include <QtNetwork/qabstractsocket.h> -#include <QtNetwork/qhostaddress.h> -#include <QtNetwork/qsslsocket.h> -#include <QtNetwork/qsslcipher.h> -#include <QtNetwork/qssl.h> - -#include <QtCore/qcryptographichash.h> -#include <QtCore/qbytearray.h> -#include <QtCore/qstring.h> +#include "qtlsbackend_p.h" +#include <QtCore/private/qobject_p.h> // // W A R N I N G // ------------- @@ -74,80 +26,20 @@ QT_BEGIN_NAMESPACE class QHostAddress; -class QDtlsBasePrivate : public QObjectPrivate -{ -public: - - void setDtlsError(QDtlsError code, const QString &description) - { - errorCode = code; - errorDescription = description; - } - - void clearDtlsError() - { - errorCode = QDtlsError::NoError; - errorDescription.clear(); - } - - void setConfiguration(const QSslConfiguration &configuration); - QSslConfiguration configuration() const; - - bool setCookieGeneratorParameters(QCryptographicHash::Algorithm alg, - const QByteArray &secret); - - static bool isDtlsProtocol(QSsl::SslProtocol protocol); - - QHostAddress remoteAddress; - quint16 remotePort = 0; - quint16 mtuHint = 0; - - QDtlsError errorCode = QDtlsError::NoError; - QString errorDescription; - QSslConfigurationPrivate dtlsConfiguration; - QSslSocket::SslMode mode = QSslSocket::SslClientMode; - QSslCipher sessionCipher; - QSsl::SslProtocol sessionProtocol = QSsl::UnknownProtocol; - QString peerVerificationName; - QByteArray secret; - -#ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 - QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha1; -#else - QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha256; -#endif -}; - -class QDtlsClientVerifierPrivate : public QDtlsBasePrivate +class QDtlsClientVerifierPrivate : public QObjectPrivate { public: - - QByteArray verifiedClientHello; - - virtual bool verifyClient(QUdpSocket *socket, const QByteArray &dgram, - const QHostAddress &address, quint16 port) = 0; + QDtlsClientVerifierPrivate(); + ~QDtlsClientVerifierPrivate(); + std::unique_ptr<QTlsPrivate::DtlsCookieVerifier> backend; }; -class QDtlsPrivate : public QDtlsBasePrivate +class QDtlsPrivate : public QObjectPrivate { public: - - virtual bool startHandshake(QUdpSocket *socket, const QByteArray &dgram) = 0; - virtual bool handleTimeout(QUdpSocket *socket) = 0; - virtual bool continueHandshake(QUdpSocket *socket, const QByteArray &dgram) = 0; - virtual bool resumeHandshake(QUdpSocket *socket) = 0; - virtual void abortHandshake(QUdpSocket *socket) = 0; - virtual void sendShutdownAlert(QUdpSocket *socket) = 0; - - virtual qint64 writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram) = 0; - virtual QByteArray decryptDatagram(QUdpSocket *socket, const QByteArray &dgram) = 0; - - QDtls::HandshakeState handshakeState = QDtls::HandshakeNotStarted; - - QList<QSslError> tlsErrors; - QList<QSslError> tlsErrorsToIgnore; - - bool connectionEncrypted = false; + QDtlsPrivate(); + ~QDtlsPrivate(); + std::unique_ptr<QTlsPrivate::DtlsCryptograph> backend; }; QT_END_NAMESPACE diff --git a/src/network/ssl/qocsp_p.h b/src/network/ssl/qocsp_p.h index 71f59da0b4..596cb1357f 100644 --- a/src/network/ssl/qocsp_p.h +++ b/src/network/ssl/qocsp_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 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$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QOCSP_P_H #define QOCSP_P_H diff --git a/src/network/ssl/qocspresponse.cpp b/src/network/ssl/qocspresponse.cpp index b63312506d..74e2c814fd 100644 --- a/src/network/ssl/qocspresponse.cpp +++ b/src/network/ssl/qocspresponse.cpp @@ -1,41 +1,6 @@ -/**************************************************************************** -** Copyright (C) 2011 Richard J. Moore <rich@kde.org> -** Copyright (C) 2019 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$ -** -****************************************************************************/ +// Copyright (C) 2011 Richard J. Moore <rich@kde.org> +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qocspresponse_p.h" #include "qocspresponse.h" @@ -44,6 +9,8 @@ QT_BEGIN_NAMESPACE +QT_IMPL_METATYPE_EXTERN(QOcspResponse) + /*! \class QOcspResponse \brief This class represents Online Certificate Status Protocol response. @@ -53,7 +20,7 @@ QT_BEGIN_NAMESPACE \ingroup ssl \inmodule QtNetwork - The QOcspResponse class represents the revocation status of a server's certficate, + The QOcspResponse class represents the revocation status of a server's certificate, received by the client-side socket during the TLS handshake. QSslSocket must be configured with OCSP stapling enabled. @@ -95,7 +62,7 @@ QT_BEGIN_NAMESPACE \inmodule QtNetwork - This enumeration describes revocation reasons, defined in \l{https://tools.ietf.org/html/rfc5280#section-5.3.1}{RFC 5280, section 5.3.1} + This enumeration describes revocation reasons, defined in \l{RFC 5280, section 5.3.1} \value None \value Unspecified @@ -234,8 +201,6 @@ bool QOcspResponse::isEqual(const QOcspResponse &other) const } /*! - \fn size_t qHash(const QOcspResponse &response, size_t seed) - Returns the hash value for the \a response, using \a seed to seed the calculation. \since 5.13 diff --git a/src/network/ssl/qocspresponse.h b/src/network/ssl/qocspresponse.h index 8f184924c7..68251a1547 100644 --- a/src/network/ssl/qocspresponse.h +++ b/src/network/ssl/qocspresponse.h @@ -1,41 +1,6 @@ -/**************************************************************************** -** Copyright (C) 2011 Richard J. Moore <rich@kde.org> -** Copyright (C) 2019 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$ -** -****************************************************************************/ +// Copyright (C) 2011 Richard J. Moore <rich@kde.org> +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QOCSPRESPONSE_H #define QOCSPRESPONSE_H @@ -46,7 +11,7 @@ #include <QtCore/qmetatype.h> #include <QtCore/qobject.h> -#ifndef Q_CLANG_QDOC +#ifndef Q_QDOC QT_REQUIRE_CONFIG(ssl); #endif @@ -72,6 +37,10 @@ enum class QOcspRevocationReason RemoveFromCRL }; +namespace QTlsPrivate { +class TlsCryptographOpenSSL; +} + class QOcspResponse; Q_NETWORK_EXPORT size_t qHash(const QOcspResponse &response, size_t seed = 0) noexcept; @@ -99,7 +68,7 @@ public: private: bool isEqual(const QOcspResponse &other) const; - friend class QSslSocketBackendPrivate; + friend class QTlsPrivate::TlsCryptographOpenSSL; friend bool operator==(const QOcspResponse &lhs, const QOcspResponse &rhs) { return lhs.isEqual(rhs); } friend bool operator!=(const QOcspResponse &lhs, const QOcspResponse &rhs) @@ -114,6 +83,6 @@ Q_DECLARE_SHARED(QOcspResponse) QT_END_NAMESPACE -Q_DECLARE_METATYPE(QOcspResponse) +QT_DECL_METATYPE_EXTERN(QOcspResponse, Q_NETWORK_EXPORT) #endif // QOCSPRESPONSE_H diff --git a/src/network/ssl/qocspresponse_p.h b/src/network/ssl/qocspresponse_p.h index e421b76899..5f08e306cd 100644 --- a/src/network/ssl/qocspresponse_p.h +++ b/src/network/ssl/qocspresponse_p.h @@ -1,41 +1,6 @@ -/**************************************************************************** -** Copyright (C) 2011 Richard J. Moore <rich@kde.org> -** Copyright (C) 2019 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$ -** -****************************************************************************/ +// Copyright (C) 2011 Richard J. Moore <rich@kde.org> +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QOCSPRESPONSE_P_H #define QOCSPRESPONSE_P_H diff --git a/src/network/ssl/qpassworddigestor.cpp b/src/network/ssl/qpassworddigestor.cpp index 706fa1de05..94de14abd4 100644 --- a/src/network/ssl/qpassworddigestor.cpp +++ b/src/network/ssl/qpassworddigestor.cpp @@ -1,50 +1,25 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore 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$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qpassworddigestor.h" #include <QtCore/QDebug> #include <QtCore/QMessageAuthenticationCode> #include <QtCore/QtEndian> +#include <QtCore/QList> + +#include "qtcore-config_p.h" #include <limits> +#if QT_CONFIG(opensslv30) && QT_CONFIG(openssl_linked) +#define USING_OPENSSL30 +#include <openssl/core_names.h> +#include <openssl/kdf.h> +#include <openssl/params.h> +#include <openssl/provider.h> +#endif + QT_BEGIN_NAMESPACE namespace QPasswordDigestor { @@ -60,7 +35,7 @@ namespace QPasswordDigestor { \since 5.12 Returns a hash computed using the PBKDF1-algorithm as defined in - \l {https://tools.ietf.org/html/rfc8018#section-5.1} {RFC 8018}. + \l {RFC 8018, section 5.1}. The function takes the \a data and \a salt, and then hashes it repeatedly for \a iterations iterations using the specified hash \a algorithm. If the @@ -122,11 +97,90 @@ Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf1(QCryptographicHash::Algorithm algori return key.left(dkLen); } +#ifdef USING_OPENSSL30 +// Copied from QCryptographicHashPrivate +static constexpr const char * methodToName(QCryptographicHash::Algorithm method) noexcept +{ + switch (method) { +#define CASE(Enum, Name) \ + case QCryptographicHash:: Enum : \ + return Name \ + /*end*/ + CASE(Sha1, "SHA1"); + CASE(Md4, "MD4"); + CASE(Md5, "MD5"); + CASE(Sha224, "SHA224"); + CASE(Sha256, "SHA256"); + CASE(Sha384, "SHA384"); + CASE(Sha512, "SHA512"); + CASE(RealSha3_224, "SHA3-224"); + CASE(RealSha3_256, "SHA3-256"); + CASE(RealSha3_384, "SHA3-384"); + CASE(RealSha3_512, "SHA3-512"); + CASE(Keccak_224, "SHA3-224"); + CASE(Keccak_256, "SHA3-256"); + CASE(Keccak_384, "SHA3-384"); + CASE(Keccak_512, "SHA3-512"); + CASE(Blake2b_512, "BLAKE2B512"); + CASE(Blake2s_256, "BLAKE2S256"); +#undef CASE + default: return nullptr; + } +} + +static QByteArray opensslDeriveKeyPbkdf2(QCryptographicHash::Algorithm algorithm, + const QByteArray &data, const QByteArray &salt, + uint64_t iterations, quint64 dkLen) +{ + EVP_KDF *kdf = EVP_KDF_fetch(nullptr, "PBKDF2", nullptr); + + if (!kdf) + return QByteArray(); + + auto cleanUpKdf = qScopeGuard([kdf] { + EVP_KDF_free(kdf); + }); + + EVP_KDF_CTX *ctx = EVP_KDF_CTX_new(kdf); + + if (!ctx) + return QByteArray(); + + auto cleanUpCtx = qScopeGuard([ctx] { + EVP_KDF_CTX_free(ctx); + }); + + // Do not enable SP800-132 compliance check, otherwise we will require: + // - the iteration count is at least 1000 + // - the salt length is at least 128 bits + // - the derived key length is at least 112 bits + // This would be a different behavior from the original implementation. + int checkDisabled = 1; + QList<OSSL_PARAM> params; + params.append(OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, const_cast<char*>(methodToName(algorithm)), 0)); + params.append(OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, const_cast<char*>(salt.data()), salt.size())); + params.append(OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_PASSWORD, const_cast<char*>(data.data()), data.size())); + params.append(OSSL_PARAM_construct_uint64(OSSL_KDF_PARAM_ITER, &iterations)); + params.append(OSSL_PARAM_construct_int(OSSL_KDF_PARAM_PKCS5, &checkDisabled)); + params.append(OSSL_PARAM_construct_end()); + + if (EVP_KDF_CTX_set_params(ctx, params.data()) <= 0) + return QByteArray(); + + QByteArray derived(dkLen, '\0'); + + if (!EVP_KDF_derive(ctx, reinterpret_cast<unsigned char*>(derived.data()), derived.size(), nullptr)) + return QByteArray(); + + return derived; +} +#endif + /*! \since 5.12 Derive a key using the PBKDF2-algorithm as defined in - \l {https://tools.ietf.org/html/rfc8018#section-5.2} {RFC 8018}. + \l {RFC 8018, section 5.2}. This function takes the \a data and \a salt, and then applies HMAC-X, where the X is \a algorithm, repeatedly. It internally concatenates intermediate @@ -143,8 +197,6 @@ Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf2(QCryptographicHash::Algorithm algori const QByteArray &data, const QByteArray &salt, int iterations, quint64 dkLen) { - // https://tools.ietf.org/html/rfc8018#section-5.2 - // The RFC recommends checking that 'dkLen' is not greater than '(2^32 - 1) * hLen' int hashLen = QCryptographicHash::hashLength(algorithm); const quint64 maxLen = quint64(std::numeric_limits<quint32>::max() - 1) * hashLen; @@ -158,11 +210,17 @@ Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf2(QCryptographicHash::Algorithm algori if (iterations < 1 || dkLen < 1) return QByteArray(); +#ifdef USING_OPENSSL30 + if (methodToName(algorithm)) + return opensslDeriveKeyPbkdf2(algorithm, data, salt, iterations, dkLen); +#endif + + // https://tools.ietf.org/html/rfc8018#section-5.2 QByteArray key; quint32 currentIteration = 1; QMessageAuthenticationCode hmac(algorithm, data); QByteArray index(4, Qt::Uninitialized); - while (quint64(key.length()) < dkLen) { + while (quint64(key.size()) < dkLen) { hmac.addData(salt); qToBigEndian(currentIteration, index.data()); diff --git a/src/network/ssl/qpassworddigestor.h b/src/network/ssl/qpassworddigestor.h index 0f88643298..279450178b 100644 --- a/src/network/ssl/qpassworddigestor.h +++ b/src/network/ssl/qpassworddigestor.h @@ -1,45 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore 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$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPASSWORDDIGESTOR_H #define QPASSWORDDIGESTOR_H +#if 0 +#pragma qt_class(QPasswordDigestor) +#endif + #include <QtNetwork/qtnetworkglobal.h> #include <QtCore/QByteArray> #include <QtCore/QCryptographicHash> diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp index fffb30fafb..dfd3745d3e 100644 --- a/src/network/ssl/qssl.cpp +++ b/src/network/ssl/qssl.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qsslkey.h" @@ -204,7 +168,7 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl"); \ingroup ssl \inmodule QtNetwork - See \l{https://tools.ietf.org/html/rfc8446#page-85}{RFC 8446, section 6} + See \l{RFC 8446, section 6} for the possible values and their meaning. \value CloseNotify, @@ -260,9 +224,10 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl"); \value Key Class QSslKey. \value Certificate Class QSslCertificate. \value Socket Class QSslSocket. - \value DiffieHellman Class QSslDiffieHellmanParameters - \value EllipticCurve Class QSslEllipticCurve - \value Dtls Classes QDtls and QDtlsClientVerifier + \value DiffieHellman Class QSslDiffieHellmanParameters. + \value EllipticCurve Class QSslEllipticCurve. + \value Dtls Class QDtls. + \value DtlsCookie Class QDtlsClientVerifier. */ /*! @@ -291,3 +256,5 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl"); */ QT_END_NAMESPACE + +#include "moc_qssl.cpp" diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h index 56727564b2..e52b8c6361 100644 --- a/src/network/ssl/qssl.h +++ b/src/network/ssl/qssl.h @@ -1,62 +1,35 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSSL_H #define QSSL_H +#if 0 +#pragma qt_class(QSsl) +#endif + #include <QtNetwork/qtnetworkglobal.h> +#include <QtCore/qobjectdefs.h> #include <QtCore/QFlags> QT_BEGIN_NAMESPACE namespace QSsl { + Q_NAMESPACE_EXPORT(Q_NETWORK_EXPORT) + enum KeyType { PrivateKey, PublicKey }; + Q_ENUM_NS(KeyType) enum EncodingFormat { Pem, Der }; + Q_ENUM_NS(EncodingFormat) enum KeyAlgorithm { Opaque, @@ -65,26 +38,28 @@ namespace QSsl { Ec, Dh, }; + Q_ENUM_NS(KeyAlgorithm) enum AlternativeNameEntryType { EmailEntry, DnsEntry, IpAddressEntry }; + Q_ENUM_NS(AlternativeNameEntryType) enum SslProtocol { - TlsV1_0, - TlsV1_1, + TlsV1_0 QT_DEPRECATED_VERSION_X_6_3("Use TlsV1_2OrLater instead."), + TlsV1_1 QT_DEPRECATED_VERSION_X_6_3("Use TlsV1_2OrLater instead."), TlsV1_2, AnyProtocol, SecureProtocols, - TlsV1_0OrLater, - TlsV1_1OrLater, + TlsV1_0OrLater QT_DEPRECATED_VERSION_X_6_3("Use TlsV1_2OrLater instead."), + TlsV1_1OrLater QT_DEPRECATED_VERSION_X_6_3("Use TlsV1_2OrLater instead."), TlsV1_2OrLater, - DtlsV1_0, - DtlsV1_0OrLater, + DtlsV1_0 QT_DEPRECATED_VERSION_X_6_3("Use DtlsV1_2OrLater instead."), + DtlsV1_0OrLater QT_DEPRECATED_VERSION_X_6_3("Use DtlsV1_2OrLater instead."), DtlsV1_2, DtlsV1_2OrLater, @@ -93,6 +68,7 @@ namespace QSsl { UnknownProtocol = -1 }; + Q_ENUM_NS(SslProtocol) enum SslOption { SslOptionDisableEmptyFragments = 0x01, @@ -104,6 +80,7 @@ namespace QSsl { SslOptionDisableSessionPersistence = 0x40, SslOptionDisableServerCipherPreference = 0x80 }; + Q_ENUM_NS(SslOption) Q_DECLARE_FLAGS(SslOptions, SslOption) enum class AlertLevel { @@ -111,6 +88,7 @@ namespace QSsl { Fatal, Unknown }; + Q_ENUM_NS(AlertLevel) enum class AlertType { CloseNotify, @@ -148,27 +126,31 @@ namespace QSsl { NoApplicationProtocol = 120, UnknownAlertMessage = 255 }; + Q_ENUM_NS(AlertType) enum class ImplementedClass { - Key = 0x1, - Certificate = 0x2, - Socket = 0x4, - DiffieHellman = 0x8, - EllipticCurve = 0x10, - Dtls = 0x20 + Key, + Certificate, + Socket, + DiffieHellman, + EllipticCurve, + Dtls, + DtlsCookie }; + Q_ENUM_NS(ImplementedClass) enum class SupportedFeature { - CertificateVerification = 0x1, - ClientSideAlpn = 0x2, - ServerSideAlpn = 0x4, - Ocsp = 0x8, - Psk = 0x10, - SessionTicket = 0x20, - Alerts = 040 + CertificateVerification, + ClientSideAlpn, + ServerSideAlpn, + Ocsp, + Psk, + SessionTicket, + Alerts }; + Q_ENUM_NS(SupportedFeature) } Q_DECLARE_OPERATORS_FOR_FLAGS(QSsl::SslOptions) diff --git a/src/network/ssl/qssl_p.h b/src/network/ssl/qssl_p.h index 83ccdc7fc3..ccbcf87029 100644 --- a/src/network/ssl/qssl_p.h +++ b/src/network/ssl/qssl_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSSL_P_H @@ -60,6 +24,19 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcSsl) +namespace QTlsPrivate { + +enum class Cipher { + DesCbc, + DesEde3Cbc, + Rc2Cbc, + Aes128Cbc, + Aes192Cbc, + Aes256Cbc +}; + +} // namespace QTlsPrivate + QT_END_NAMESPACE #endif // QSSL_P_H diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp index c63ef5d205..9878c603b6 100644 --- a/src/network/ssl/qsslcertificate.cpp +++ b/src/network/ssl/qsslcertificate.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /*! @@ -121,8 +85,7 @@ \value Wildcard This provides a simple pattern matching syntax similar to that used by shells (command interpreters) for "file - globbing". See \l {QRegularExpression#Wildcard matching} - {QRegularExpression Wildcard Matching}. + globbing". See \l {QRegularExpression::fromWildcard()}. \value FixedString The pattern is a fixed string. This is equivalent to using the RegularExpression pattern on a string in @@ -131,31 +94,46 @@ */ #include <QtNetwork/qtnetworkglobal.h> -#ifndef QT_NO_OPENSSL -#include "qsslsocket_openssl_symbols_p.h" -#endif -#ifdef QT_SECURETRANSPORT -#include "qsslsocket_mac_p.h" -#endif -#if QT_CONFIG(schannel) -#include "qsslsocket_schannel_p.h" -#endif + #if QT_CONFIG(regularexpression) #include "qregularexpression.h" #endif -#include "qssl_p.h" -#include "qsslcertificate.h" + +#include "qsslcertificateextension_p.h" #include "qsslcertificate_p.h" +#include "qsslcertificate.h" +#include "qssl_p.h" + #ifndef QT_NO_SSL +#include "qsslsocket_p.h" #include "qsslkey_p.h" #endif #include <QtCore/qdir.h> -#include <QtCore/qdiriterator.h> +#include <QtCore/qdirlisting.h> #include <QtCore/qfile.h> QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + +QT_IMPL_METATYPE_EXTERN(QSslCertificate) + +QSslCertificatePrivate::QSslCertificatePrivate() +{ +#ifndef QT_NO_SSL + QSslSocketPrivate::ensureInitialized(); +#endif + + const QTlsBackend *tlsBackend = QTlsBackend::activeOrAnyBackend(); + if (tlsBackend) + backend.reset(tlsBackend->createCertificate()); + else + qCWarning(lcSsl, "No TLS backend is available"); +} + +QSslCertificatePrivate::~QSslCertificatePrivate() = default; + /*! Constructs a QSslCertificate by reading \a format encoded data from \a device and using the first certificate found. You can @@ -165,13 +143,25 @@ QT_BEGIN_NAMESPACE QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format) : d(new QSslCertificatePrivate) { -#ifndef QT_NO_OPENSSL - QSslSocketPrivate::ensureInitialized(); - if (device && QSslSocket::supportsSsl()) -#else - if (device) -#endif - d->init(device->readAll(), format); + if (device) { + const auto data = device->readAll(); + if (data.isEmpty()) + return; + + const auto *tlsBackend = QTlsBackend::activeOrAnyBackend(); + if (!tlsBackend) + return; + + auto *X509Reader = format == QSsl::Pem ? tlsBackend->X509PemReader() : tlsBackend->X509DerReader(); + if (!X509Reader) { + qCWarning(lcSsl, "Current TLS plugin does not support reading from PEM/DER"); + return; + } + + QList<QSslCertificate> certs = X509Reader(data, 1); + if (!certs.isEmpty()) + d = certs.first().d; + } } /*! @@ -183,11 +173,22 @@ QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format) QSslCertificate::QSslCertificate(const QByteArray &data, QSsl::EncodingFormat format) : d(new QSslCertificatePrivate) { -#ifndef QT_NO_OPENSSL - QSslSocketPrivate::ensureInitialized(); - if (QSslSocket::supportsSsl()) -#endif - d->init(data, format); + if (data.isEmpty()) + return; + + const auto *tlsBackend = QTlsBackend::activeOrAnyBackend(); + if (!tlsBackend) + return; + + auto *X509Reader = format == QSsl::Pem ? tlsBackend->X509PemReader() : tlsBackend->X509DerReader(); + if (!X509Reader) { + qCWarning(lcSsl, "Current TLS plugin does not support reading from PEM/DER"); + return; + } + + const QList<QSslCertificate> certs = X509Reader(data, 1); + if (!certs.isEmpty()) + d = certs.first().d; } /*! @@ -229,6 +230,20 @@ QSslCertificate &QSslCertificate::operator=(const QSslCertificate &other) returns \c false. */ +bool QSslCertificate::operator==(const QSslCertificate &other) const +{ + if (d == other.d) + return true; + + if (isNull() && other.isNull()) + return true; + + if (d->backend.get() && other.d->backend.get()) + return d->backend->isEqual(*other.d->backend.get()); + + return false; +} + /*! \fn bool QSslCertificate::operator!=(const QSslCertificate &other) const @@ -246,6 +261,13 @@ QSslCertificate &QSslCertificate::operator=(const QSslCertificate &other) \sa clear() */ +bool QSslCertificate::isNull() const +{ + if (const auto *backend = d->backend.get()) + return backend->isNull(); + + return true; +} /*! Returns \c true if this certificate is blacklisted; otherwise @@ -268,6 +290,13 @@ bool QSslCertificate::isBlacklisted() const A certificate is considered self-signed its issuer and subject are identical. */ +bool QSslCertificate::isSelfSigned() const +{ + if (const auto *backend = d->backend.get()) + return backend->isSelfSigned(); + + return false; +} /*! Clears the contents of this certificate, making it a null @@ -286,12 +315,26 @@ void QSslCertificate::clear() \fn QByteArray QSslCertificate::version() const Returns the certificate's version string. */ +QByteArray QSslCertificate::version() const +{ + if (const auto *backend = d->backend.get()) + return backend->version(); + + return {}; +} /*! \fn QByteArray QSslCertificate::serialNumber() const Returns the certificate's serial number string in hexadecimal format. */ +QByteArray QSslCertificate::serialNumber() const +{ + if (const auto *backend = d->backend.get()) + return backend->serialNumber(); + + return {}; +} /*! Returns a cryptographic digest of this certificate. By default, @@ -313,6 +356,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa subjectInfo() */ +QStringList QSslCertificate::issuerInfo(SubjectInfo info) const +{ + if (const auto *backend = d->backend.get()) + return backend->issuerInfo(info); + + return {}; +} /*! \fn QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const @@ -323,6 +373,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa subjectInfo() */ +QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const +{ + if (const auto *backend = d->backend.get()) + return backend->issuerInfo(attribute); + + return {}; +} /*! \fn QString QSslCertificate::subjectInfo(SubjectInfo subject) const @@ -333,6 +390,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa issuerInfo() */ +QStringList QSslCertificate::subjectInfo(SubjectInfo info) const +{ + if (const auto *backend = d->backend.get()) + return backend->subjectInfo(info); + + return {}; +} /*! \fn QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const @@ -343,6 +407,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa issuerInfo() */ +QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const +{ + if (const auto *backend = d->backend.get()) + return backend->subjectInfo(attribute); + + return {}; +} /*! \fn QList<QByteArray> QSslCertificate::subjectInfoAttributes() const @@ -356,6 +427,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa subjectInfo() */ +QList<QByteArray> QSslCertificate::subjectInfoAttributes() const +{ + if (const auto *backend = d->backend.get()) + return backend->subjectInfoAttributes(); + + return {}; +} /*! \fn QList<QByteArray> QSslCertificate::issuerInfoAttributes() const @@ -369,6 +447,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa subjectInfo() */ +QList<QByteArray> QSslCertificate::issuerInfoAttributes() const +{ + if (const auto *backend = d->backend.get()) + return backend->issuerInfoAttributes(); + + return {}; +} /*! \fn QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const @@ -385,6 +470,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa subjectInfo() */ +QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const +{ + if (const auto *backend = d->backend.get()) + return backend->subjectAlternativeNames(); + + return {}; +} /*! \fn QDateTime QSslCertificate::effectiveDate() const @@ -394,6 +486,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa expiryDate() */ +QDateTime QSslCertificate::effectiveDate() const +{ + if (const auto *backend = d->backend.get()) + return backend->effectiveDate(); + + return {}; +} /*! \fn QDateTime QSslCertificate::expiryDate() const @@ -403,6 +502,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa effectiveDate() */ +QDateTime QSslCertificate::expiryDate() const +{ + if (const auto *backend = d->backend.get()) + return backend->expiryDate(); + + return {}; +} /*! \fn Qt::HANDLE QSslCertificate::handle() const @@ -416,11 +522,29 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons non-portable, and its return value may vary from platform to platform or change from minor release to minor release. */ +Qt::HANDLE QSslCertificate::handle() const +{ + if (const auto *backend = d->backend.get()) + return backend->handle(); + + return {}; +} +#ifndef QT_NO_SSL /*! \fn QSslKey QSslCertificate::publicKey() const Returns the certificate subject's public key. */ +QSslKey QSslCertificate::publicKey() const +{ + QSslKey key; + if (const auto *backend = d->backend.get()) + QTlsBackend::resetBackend(key, backend->publicKey()); + + return key; +} +#endif // QT_NO_SSL + /*! \fn QList<QSslCertificateExtension> QSslCertificate::extensions() const @@ -428,6 +552,10 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons Returns a list containing the X509 extensions of this certificate. \since 5.0 */ +QList<QSslCertificateExtension> QSslCertificate::extensions() const +{ + return d->extensions(); +} /*! \fn QByteArray QSslCertificate::toPem() const @@ -435,6 +563,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons Returns this certificate converted to a PEM (Base64) encoded representation. */ +QByteArray QSslCertificate::toPem() const +{ + if (const auto *backend = d->backend.get()) + return backend->toPem(); + + return {}; +} /*! \fn QByteArray QSslCertificate::toDer() const @@ -442,6 +577,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons Returns this certificate converted to a DER (binary) encoded representation. */ +QByteArray QSslCertificate::toDer() const +{ + if (const auto *backend = d->backend.get()) + return backend->toDer(); + + return {}; +} /*! \fn QString QSslCertificate::toText() const @@ -451,6 +593,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \since 5.0 */ +QString QSslCertificate::toText() const +{ + if (const auto *backend = d->backend.get()) + return backend->toText(); + + return {}; +} /*! \since 5.15 @@ -475,16 +624,16 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path, QString sourcePath = QDir::fromNativeSeparators(path); // Find the path without the filename - QString pathPrefix = sourcePath.left(sourcePath.lastIndexOf(QLatin1Char('/'))); + QStringView pathPrefix = QStringView(sourcePath).left(sourcePath.lastIndexOf(u'/')); // Check if the path contains any special chars int pos = -1; #if QT_CONFIG(regularexpression) if (syntax == PatternSyntax::Wildcard) - pos = pathPrefix.indexOf(QRegularExpression(QLatin1String("[*?[]"))); + pos = pathPrefix.indexOf(QRegularExpression("[*?[]"_L1)); else if (syntax == PatternSyntax::RegularExpression) - pos = sourcePath.indexOf(QRegularExpression(QLatin1String("[\\$\\(\\)\\*\\+\\.\\?\\[\\]\\^\\{\\}\\|]"))); + pos = sourcePath.indexOf(QRegularExpression("[\\$\\(\\)\\*\\+\\.\\?\\[\\]\\^\\{\\}\\|]"_L1)); #else if (syntax == PatternSyntax::Wildcard || syntax == PatternSyntax::RegExp) qWarning("Regular expression support is disabled in this build. Only fixed string can be searched"); @@ -494,11 +643,11 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path, if (pos != -1) { // there was a special char in the path so cut of the part containing that char. pathPrefix = pathPrefix.left(pos); - const int lastIndexOfSlash = pathPrefix.lastIndexOf(QLatin1Char('/')); + const qsizetype lastIndexOfSlash = pathPrefix.lastIndexOf(u'/'); if (lastIndexOfSlash != -1) pathPrefix = pathPrefix.left(lastIndexOfSlash); else - pathPrefix.clear(); + pathPrefix = {}; } else { // Check if the path is a file. if (QFileInfo(sourcePath).isFile()) { @@ -515,10 +664,12 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path, // Special case - if the prefix ends up being nothing, use "." instead. int startIndex = 0; if (pathPrefix.isEmpty()) { - pathPrefix = QLatin1String("."); + pathPrefix = u"."; startIndex = 2; } + const QString pathPrefixString = pathPrefix.toString(); + // The path can be a file or directory. QList<QSslCertificate> certs; @@ -529,9 +680,12 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path, QRegularExpression pattern(QRegularExpression::anchoredPattern(sourcePath)); #endif - QDirIterator it(pathPrefix, QDir::Files, QDirIterator::FollowSymlinks | QDirIterator::Subdirectories); - while (it.hasNext()) { - QString filePath = startIndex == 0 ? it.next() : it.next().mid(startIndex); + using F = QDirListing::IteratorFlag; + constexpr auto iterFlags = F::FollowSymlinks | F::Recursive; + for (const auto &dirEntry : QDirListing(pathPrefixString, QDir::Files, iterFlags)) { + QString filePath = dirEntry.filePath(); + if (startIndex > 0) + filePath.remove(0, startIndex); #if QT_CONFIG(regularexpression) if (!pattern.match(filePath).hasMatch()) @@ -576,13 +730,22 @@ QList<QSslCertificate> QSslCertificate::fromDevice(QIODevice *device, QSsl::Enco */ QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::EncodingFormat format) { - return (format == QSsl::Pem) - ? QSslCertificatePrivate::certificatesFromPem(data) - : QSslCertificatePrivate::certificatesFromDer(data); + const auto *tlsBackend = QTlsBackend::activeOrAnyBackend(); + if (!tlsBackend) { + qCWarning(lcSsl, "No TLS backend is available"); + return {}; + } + + auto reader = format == QSsl::Pem ? tlsBackend->X509PemReader() : tlsBackend->X509DerReader(); + if (!reader) { + qCWarning(lcSsl, "The available TLS backend does not support reading PEM/DER"); + return {}; + } + + return reader(data, -1); } #ifndef QT_NO_SSL - /*! Verifies a certificate chain. The chain to be verified is passed in the \a certificateChain parameter. The first certificate in the list should @@ -597,13 +760,19 @@ QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::E \since 5.0 */ -#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) QList<QSslError> QSslCertificate::verify(const QList<QSslCertificate> &certificateChain, const QString &hostName) -#else -QList<QSslError> QSslCertificate::verify(QList<QSslCertificate> certificateChain, const QString &hostName) -#endif { - return QSslSocketBackendPrivate::verify(certificateChain, hostName); + const auto *tlsBackend = QTlsBackend::activeOrAnyBackend(); + if (!tlsBackend) { + qCWarning(lcSsl, "No TLS backend is available"); + return {}; + } + auto verifyPtr = tlsBackend->X509Verifier(); + if (!verifyPtr) { + qCWarning(lcSsl, "Available TLS backend does not support manual certificate verification"); + return {}; + } + return verifyPtr(certificateChain, hostName); } /*! @@ -623,10 +792,43 @@ bool QSslCertificate::importPkcs12(QIODevice *device, QList<QSslCertificate> *caCertificates, const QByteArray &passPhrase) { - return QSslSocketBackendPrivate::importPkcs12(device, key, certificate, caCertificates, passPhrase); + if (!device || !key || !certificate) + return false; + + const auto *tlsBackend = QTlsBackend::activeOrAnyBackend(); + if (!tlsBackend) { + qCWarning(lcSsl, "No TLS backend is available"); + return false; + } + + if (auto reader = tlsBackend->X509Pkcs12Reader()) + return reader(device, key, certificate, caCertificates, passPhrase); + + qCWarning(lcSsl, "Available TLS backend does not support PKCS12"); + + return false; } +#endif // QT_NO_SSL -#endif +QList<QSslCertificateExtension> QSslCertificatePrivate::extensions() const +{ + QList<QSslCertificateExtension> result; + + if (backend.get()) { + auto nExt = backend->numberOfExtensions(); + for (decltype (nExt) i = 0; i < nExt; ++i) { + QSslCertificateExtension ext; + ext.d->oid = backend->oidForExtension(i); + ext.d->name = backend->nameForExtension(i); + ext.d->value = backend->valueForExtension(i); + ext.d->critical = backend->isExtensionCritical(i); + ext.d->supported = backend->isExtensionSupported(i); + result << ext; + } + } + + return result; +} // These certificates are known to be fraudulent and were created during the comodo // compromise. See http://www.comodo.com/Comodo-Fraud-Incident-2011-03-23.html @@ -681,7 +883,7 @@ static const char *const certificate_blacklist[] = { bool QSslCertificatePrivate::isBlacklisted(const QSslCertificate &certificate) { for (int a = 0; certificate_blacklist[a] != nullptr; a++) { - QString blacklistedCommonName = QString::fromUtf8(certificate_blacklist[(a+1)]); + auto blacklistedCommonName = QAnyStringView(QUtf8StringView(certificate_blacklist[(a+1)])); if (certificate.serialNumber() == certificate_blacklist[a++] && (certificate.subjectInfo(QSslCertificate::CommonName).contains(blacklistedCommonName) || certificate.issuerInfo(QSslCertificate::CommonName).contains(blacklistedCommonName))) @@ -692,19 +894,18 @@ bool QSslCertificatePrivate::isBlacklisted(const QSslCertificate &certificate) QByteArray QSslCertificatePrivate::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; + case QSslCertificate::Organization: return "O"_ba; + case QSslCertificate::CommonName: return "CN"_ba; + case QSslCertificate::LocalityName: return"L"_ba; + case QSslCertificate::OrganizationalUnitName: return "OU"_ba; + case QSslCertificate::CountryName: return "C"_ba; + case QSslCertificate::StateOrProvinceName: return "ST"_ba; + case QSslCertificate::DistinguishedNameQualifier: return "dnQualifier"_ba; + case QSslCertificate::SerialNumber: return "serialNumber"_ba; + case QSslCertificate::EmailAddress: return "emailAddress"_ba; } - return str; + return QByteArray(); } /*! @@ -721,13 +922,13 @@ QString QSslCertificate::issuerDisplayName() const QStringList names; names = issuerInfo(QSslCertificate::CommonName); if (!names.isEmpty()) - return names.first(); + return names.constFirst(); names = issuerInfo(QSslCertificate::Organization); if (!names.isEmpty()) - return names.first(); + return names.constFirst(); names = issuerInfo(QSslCertificate::OrganizationalUnitName); if (!names.isEmpty()) - return names.first(); + return names.constFirst(); return QString(); } @@ -746,24 +947,30 @@ QString QSslCertificate::subjectDisplayName() const QStringList names; names = subjectInfo(QSslCertificate::CommonName); if (!names.isEmpty()) - return names.first(); + return names.constFirst(); names = subjectInfo(QSslCertificate::Organization); if (!names.isEmpty()) - return names.first(); + return names.constFirst(); names = subjectInfo(QSslCertificate::OrganizationalUnitName); if (!names.isEmpty()) - return names.first(); + return names.constFirst(); return QString(); } /*! - \fn size_t qHash(const QSslCertificate &key, size_t seed) - Returns the hash value for the \a key, using \a seed to seed the calculation. \since 5.4 \relates QHash */ +size_t qHash(const QSslCertificate &key, size_t seed) noexcept +{ + if (const auto *backend = key.d->backend.get()) + return backend->hash(seed); + + return seed; + +} #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug debug, const QSslCertificate &certificate) @@ -771,15 +978,15 @@ QDebug operator<<(QDebug debug, const QSslCertificate &certificate) QDebugStateSaver saver(debug); debug.resetFormat().nospace(); debug << "QSslCertificate(" - << certificate.version() - << ", " << certificate.serialNumber() - << ", " << certificate.digest().toBase64() - << ", " << certificate.issuerDisplayName() - << ", " << certificate.subjectDisplayName() - << ", " << certificate.subjectAlternativeNames() + << "Version=" << certificate.version() + << ", SerialNumber=" << certificate.serialNumber() + << ", Digest=" << certificate.digest().toBase64() + << ", Issuer=" << certificate.issuerDisplayName() + << ", Subject=" << certificate.subjectDisplayName() + << ", AlternativeSubjectNames=" << certificate.subjectAlternativeNames() #if QT_CONFIG(datestring) - << ", " << certificate.effectiveDate() - << ", " << certificate.expiryDate() + << ", EffectiveDate=" << certificate.effectiveDate() + << ", ExpiryDate=" << certificate.expiryDate() #endif << ')'; return debug; diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h index cf97dcbee8..cdf11b28b0 100644 --- a/src/network/ssl/qsslcertificate.h +++ b/src/network/ssl/qsslcertificate.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSSLCERTIFICATE_H @@ -50,8 +14,8 @@ #include <QtCore/qbytearray.h> #include <QtCore/qcryptographichash.h> #include <QtCore/qdatetime.h> -#include <QtCore/qsharedpointer.h> #include <QtCore/qmap.h> +#include <QtCore/qshareddata.h> #include <QtNetwork/qssl.h> QT_BEGIN_NAMESPACE @@ -97,7 +61,7 @@ public: QSslCertificate &operator=(const QSslCertificate &other); void swap(QSslCertificate &other) noexcept - { qSwap(d, other.d); } + { d.swap(other.d); } bool operator==(const QSslCertificate &other) const; inline bool operator!=(const QSslCertificate &other) const { return !operator==(other); } @@ -142,12 +106,7 @@ public: const QByteArray &data, QSsl::EncodingFormat format = QSsl::Pem); #ifndef QT_NO_SSL -#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain, const QString &hostName = QString()); -#else - static QList<QSslError> verify(QList<QSslCertificate> certificateChain, const QString &hostName = QString()); -#endif - static bool importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert, QList<QSslCertificate> *caCertificates = nullptr, @@ -158,8 +117,7 @@ public: private: QExplicitlySharedDataPointer<QSslCertificatePrivate> d; - friend class QSslCertificatePrivate; - friend class QSslSocketBackendPrivate; + friend class QTlsBackend; friend Q_NETWORK_EXPORT size_t qHash(const QSslCertificate &key, size_t seed) noexcept; }; @@ -173,6 +131,6 @@ Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, QSslCertificate::SubjectInfo in QT_END_NAMESPACE -Q_DECLARE_METATYPE(QSslCertificate) +QT_DECL_METATYPE_EXTERN(QSslCertificate, Q_NETWORK_EXPORT) #endif diff --git a/src/network/ssl/qsslcertificate_openssl.cpp b/src/network/ssl/qsslcertificate_openssl.cpp deleted file mode 100644 index af65f3b6f7..0000000000 --- a/src/network/ssl/qsslcertificate_openssl.cpp +++ /dev/null @@ -1,796 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** 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 "qssl_p.h" -#include "qsslsocket_openssl_symbols_p.h" -#include "qsslcertificate_p.h" -#include "qsslkey_p.h" -#include "qsslcertificateextension_p.h" - -#include <QtCore/qendian.h> -#include <QtCore/qmutex.h> - -QT_BEGIN_NAMESPACE - -constexpr int MutexPoolSize = 17; -static QBasicMutex mutexPool[MutexPoolSize]; -namespace QMutexPool { - static QBasicMutex *globalInstanceGet(const void *addr) - { - return mutexPool + (quintptr(addr) % MutexPoolSize); - } -} - -// forward declaration -static QMultiMap<QByteArray, QString> _q_mapFromX509Name(X509_NAME *name); - -bool QSslCertificate::operator==(const QSslCertificate &other) const -{ - if (d == other.d) - return true; - - if (d->null && other.d->null) - return true; - - if (d->x509 && other.d->x509) { - const int ret = q_X509_cmp(d->x509, other.d->x509); - if (ret >= -1 && ret <= 1) - return ret == 0; - QSslSocketBackendPrivate::logAndClearErrorQueue(); - } - - return false; -} - -size_t qHash(const QSslCertificate &key, size_t seed) noexcept -{ - if (X509 * const x509 = key.d->x509) { - const EVP_MD *sha1 = q_EVP_sha1(); - unsigned int len = 0; - unsigned char md[EVP_MAX_MD_SIZE]; - q_X509_digest(x509, sha1, md, &len); - return qHashBits(md, len, seed); - } - - return seed; -} - -bool QSslCertificate::isNull() const -{ - return d->null; -} - -bool QSslCertificate::isSelfSigned() const -{ - if (!d->x509) - return false; - - return (q_X509_check_issued(d->x509, d->x509) == X509_V_OK); -} - -QByteArray QSslCertificate::version() const -{ -#if QT_CONFIG(thread) - QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); -#endif - if (d->versionString.isEmpty() && d->x509) - d->versionString = QByteArray::number(qlonglong(q_X509_get_version(d->x509)) + 1); - - return d->versionString; -} - -QByteArray QSslCertificate::serialNumber() const -{ -#if QT_CONFIG(thread) - QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); -#endif - if (d->serialNumberString.isEmpty() && d->x509) { - ASN1_INTEGER *serialNumber = q_X509_get_serialNumber(d->x509); - QByteArray hexString; - hexString.reserve(serialNumber->length * 3); - for (int a = 0; a < serialNumber->length; ++a) { - hexString += QByteArray::number(serialNumber->data[a], 16).rightJustified(2, '0'); - hexString += ':'; - } - hexString.chop(1); - d->serialNumberString = hexString; - } - return d->serialNumberString; -} - -QStringList QSslCertificate::issuerInfo(SubjectInfo info) const -{ -#if QT_CONFIG(thread) - QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); -#endif - // lazy init - if (d->issuerInfo.isEmpty() && d->x509) - d->issuerInfo = - _q_mapFromX509Name(q_X509_get_issuer_name(d->x509)); - - return d->issuerInfo.values(d->subjectInfoToString(info)); -} - -QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const -{ -#if QT_CONFIG(thread) - QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); -#endif - // lazy init - if (d->issuerInfo.isEmpty() && d->x509) - d->issuerInfo = - _q_mapFromX509Name(q_X509_get_issuer_name(d->x509)); - - return d->issuerInfo.values(attribute); -} - -QStringList QSslCertificate::subjectInfo(SubjectInfo info) const -{ -#if QT_CONFIG(thread) - QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); -#endif - // lazy init - if (d->subjectInfo.isEmpty() && d->x509) - d->subjectInfo = - _q_mapFromX509Name(q_X509_get_subject_name(d->x509)); - - return d->subjectInfo.values(d->subjectInfoToString(info)); -} - -QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const -{ -#if QT_CONFIG(thread) - QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); -#endif - // lazy init - if (d->subjectInfo.isEmpty() && d->x509) - d->subjectInfo = - _q_mapFromX509Name(q_X509_get_subject_name(d->x509)); - - return d->subjectInfo.values(attribute); -} - -QList<QByteArray> QSslCertificate::subjectInfoAttributes() const -{ -#if QT_CONFIG(thread) - QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); -#endif - // lazy init - if (d->subjectInfo.isEmpty() && d->x509) - d->subjectInfo = - _q_mapFromX509Name(q_X509_get_subject_name(d->x509)); - - return d->subjectInfo.uniqueKeys(); -} - -QList<QByteArray> QSslCertificate::issuerInfoAttributes() const -{ -#if QT_CONFIG(thread) - QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); -#endif - // lazy init - if (d->issuerInfo.isEmpty() && d->x509) - d->issuerInfo = - _q_mapFromX509Name(q_X509_get_issuer_name(d->x509)); - - return d->issuerInfo.uniqueKeys(); -} - -QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const -{ - QMultiMap<QSsl::AlternativeNameEntryType, QString> result; - - if (!d->x509) - return result; - - STACK_OF(GENERAL_NAME) *altNames = (STACK_OF(GENERAL_NAME) *)q_X509_get_ext_d2i( - d->x509, NID_subject_alt_name, nullptr, nullptr); - - auto altName = [](ASN1_IA5STRING *ia5, int len) { - const char *altNameStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(ia5)); - return QString::fromLatin1(altNameStr, len); - }; - if (altNames) { - for (int i = 0; i < q_sk_GENERAL_NAME_num(altNames); ++i) { - const GENERAL_NAME *genName = q_sk_GENERAL_NAME_value(altNames, i); - if (genName->type != GEN_DNS && genName->type != GEN_EMAIL && genName->type != GEN_IPADD) - continue; - - int len = q_ASN1_STRING_length(genName->d.ia5); - if (len < 0 || len >= 8192) { - // broken name - continue; - } - - switch (genName->type) { - case GEN_DNS: - result.insert(QSsl::DnsEntry, altName(genName->d.ia5, len)); - break; - case GEN_EMAIL: - result.insert(QSsl::EmailEntry, altName(genName->d.ia5, len)); - break; - case GEN_IPADD: { - QHostAddress ipAddress; - switch (len) { - case 4: // IPv4 - ipAddress = QHostAddress(qFromBigEndian(*reinterpret_cast<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; -} - -QDateTime QSslCertificate::effectiveDate() const -{ - return d->notValidBefore; -} - -QDateTime QSslCertificate::expiryDate() const -{ - return d->notValidAfter; -} - -Qt::HANDLE QSslCertificate::handle() const -{ - return Qt::HANDLE(d->x509); -} - -QSslKey QSslCertificate::publicKey() const -{ - if (!d->x509) - return QSslKey(); - - QSslKey key; - - key.d->type = QSsl::PublicKey; - - EVP_PKEY *pkey = q_X509_get_pubkey(d->x509); - Q_ASSERT(pkey); - const int keyType = q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey)); - - if (keyType == EVP_PKEY_RSA) { - key.d->rsa = q_EVP_PKEY_get1_RSA(pkey); - key.d->algorithm = QSsl::Rsa; - key.d->isNull = false; - } else if (keyType == EVP_PKEY_DSA) { - key.d->dsa = q_EVP_PKEY_get1_DSA(pkey); - key.d->algorithm = QSsl::Dsa; - key.d->isNull = false; -#ifndef OPENSSL_NO_EC - } else if (keyType == EVP_PKEY_EC) { - key.d->ec = q_EVP_PKEY_get1_EC_KEY(pkey); - key.d->algorithm = QSsl::Ec; - key.d->isNull = false; -#endif - } else if (keyType == EVP_PKEY_DH) { - // DH unsupported - } else { - // error? - } - - q_EVP_PKEY_free(pkey); - return key; -} - -/* - * Convert unknown extensions to a QVariant. - */ -static QVariant x509UnknownExtensionToValue(X509_EXTENSION *ext) -{ - // Get the extension specific method object if available - // we cast away the const-ness here because some versions of openssl - // don't use const for the parameters in the functions pointers stored - // in the object. - Q_ASSERT(ext); - - X509V3_EXT_METHOD *meth = const_cast<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; - } - - //const unsigned char *data = ext->value->data; - void *ext_internal = q_X509V3_EXT_d2i(ext); - - // If this extension can be converted - if (meth->i2v && ext_internal) { - STACK_OF(CONF_VALUE) *val = meth->i2v(meth, ext_internal, nullptr); - - QVariantMap map; - QVariantList list; - bool isMap = false; - - for (int j = 0; j < q_SKM_sk_num(CONF_VALUE, val); j++) { - CONF_VALUE *nval = q_SKM_sk_value(CONF_VALUE, val, j); - if (nval->name && nval->value) { - isMap = true; - map[QString::fromUtf8(nval->name)] = QString::fromUtf8(nval->value); - } else if (nval->name) { - list << QString::fromUtf8(nval->name); - } else if (nval->value) { - list << QString::fromUtf8(nval->value); - } - } - - if (isMap) - return map; - else - return list; - } else if (meth->i2s && ext_internal) { - QVariant result(QString::fromUtf8(meth->i2s(meth, ext_internal))); - return result; - } else if (meth->i2r && ext_internal) { - QByteArray result; - - BIO *bio = q_BIO_new(q_BIO_s_mem()); - if (!bio) - return result; - - meth->i2r(meth, ext_internal, bio, 0); - - char *bio_buffer; - long bio_size = q_BIO_get_mem_data(bio, &bio_buffer); - result = QByteArray(bio_buffer, bio_size); - - q_BIO_free(bio); - return result; - } - - return QVariant(); -} - -/* - * Convert extensions to a variant. The naming of the keys of the map are - * taken from RFC 5280, however we decided the capitalisation in the RFC - * was too silly for the real world. - */ -static QVariant x509ExtensionToValue(X509_EXTENSION *ext) -{ - ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(ext); - int nid = q_OBJ_obj2nid(obj); - - switch (nid) { - case NID_basic_constraints: - { - BASIC_CONSTRAINTS *basic = reinterpret_cast<BASIC_CONSTRAINTS *>(q_X509V3_EXT_d2i(ext)); - if (!basic) - return QVariant(); - - QVariantMap result; - result[QLatin1String("ca")] = basic->ca ? true : false; - if (basic->pathlen) - result[QLatin1String("pathLenConstraint")] = (qlonglong)q_ASN1_INTEGER_get(basic->pathlen); - - q_BASIC_CONSTRAINTS_free(basic); - return result; - } - break; - case NID_info_access: - { - AUTHORITY_INFO_ACCESS *info = reinterpret_cast<AUTHORITY_INFO_ACCESS *>(q_X509V3_EXT_d2i(ext)); - if (!info) - return QVariant(); - - QVariantMap result; - for (int i=0; i < q_SKM_sk_num(ACCESS_DESCRIPTION, info); i++) { - ACCESS_DESCRIPTION *ad = q_SKM_sk_value(ACCESS_DESCRIPTION, info, i); - - GENERAL_NAME *name = ad->location; - if (name->type == GEN_URI) { - int len = q_ASN1_STRING_length(name->d.uniformResourceIdentifier); - if (len < 0 || len >= 8192) { - // broken name - continue; - } - - const char *uriStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(name->d.uniformResourceIdentifier)); - const QString uri = QString::fromUtf8(uriStr, len); - - result[QString::fromUtf8(QSslCertificatePrivate::asn1ObjectName(ad->method))] = uri; - } else { - qCWarning(lcSsl) << "Strange location type" << name->type; - } - } - - q_OPENSSL_sk_pop_free((OPENSSL_STACK*)info, reinterpret_cast<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 QVariant(); - // we cast away the const-ness here because some versions of openssl - // don't use const for the parameters in the functions pointers stored - // in the object. - X509V3_EXT_METHOD *meth = const_cast<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 QVariant(); - - 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 QVariant(); -} - -QSslCertificateExtension QSslCertificatePrivate::convertExtension(X509_EXTENSION *ext) -{ - Q_ASSERT(ext); - - QSslCertificateExtension result; - - ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(ext); - if (!obj) { - qCWarning(lcSsl, "Invalid (nullptr) ASN1_OBJECT"); - return result; - } - - QByteArray oid = QSslCertificatePrivate::asn1ObjectId(obj); - QByteArray name = QSslCertificatePrivate::asn1ObjectName(obj); - - result.d->oid = QString::fromUtf8(oid); - result.d->name = QString::fromUtf8(name); - - bool critical = q_X509_EXTENSION_get_critical(ext); - result.d->critical = critical; - - // Lets see if we have custom support for this one - QVariant extensionValue = x509ExtensionToValue(ext); - if (extensionValue.isValid()) { - result.d->value = extensionValue; - result.d->supported = true; - - return result; - } - - extensionValue = x509UnknownExtensionToValue(ext); - if (extensionValue.isValid()) { - result.d->value = extensionValue; - result.d->supported = false; - return result; - } - - return result; -} - -QList<QSslCertificateExtension> QSslCertificate::extensions() const -{ - QList<QSslCertificateExtension> result; - - if (!d->x509) - return result; - - int count = q_X509_get_ext_count(d->x509); - if (count <= 0) - return result; - - result.reserve(count); - - for (int i = 0; i < count; i++) { - X509_EXTENSION *ext = q_X509_get_ext(d->x509, i); - if (!ext) { - qCWarning(lcSsl) << "Invalid (nullptr) extension at index" << i; - continue; - } - result << QSslCertificatePrivate::convertExtension(ext); - } - - // Converting an extension may result in an error(s), clean them up. - Q_UNUSED(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); - - return result; -} - -QByteArray QSslCertificate::toPem() const -{ - if (!d->x509) - return QByteArray(); - return d->QByteArray_from_X509(d->x509, QSsl::Pem); -} - -QByteArray QSslCertificate::toDer() const -{ - if (!d->x509) - return QByteArray(); - return d->QByteArray_from_X509(d->x509, QSsl::Der); -} - -QString QSslCertificate::toText() const -{ - if (!d->x509) - return QString(); - return d->text_from_X509(d->x509); -} - -#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----" -#define ENDCERTSTRING "-----END CERTIFICATE-----" - -void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format) -{ - if (!data.isEmpty()) { - const QList<QSslCertificate> certs = (format == QSsl::Pem) - ? certificatesFromPem(data, 1) - : certificatesFromDer(data, 1); - if (!certs.isEmpty()) { - *this = *certs.first().d; - if (x509) - x509 = q_X509_dup(x509); - } - } -} - -// ### refactor against QSsl::pemFromDer() etc. (to avoid redundant implementations) -QByteArray QSslCertificatePrivate::QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format) -{ - if (!x509) { - qCWarning(lcSsl, "QSslSocketBackendPrivate::X509_to_QByteArray: null X509"); - return QByteArray(); - } - - // Use i2d_X509 to convert the X509 to an array. - int length = q_i2d_X509(x509, nullptr); - QByteArray array; - array.resize(length); - char *data = array.data(); - char **dataP = &data; - unsigned char **dataPu = (unsigned char **)dataP; - if (q_i2d_X509(x509, dataPu) < 0) - return QByteArray(); - - if (format == QSsl::Der) - return array; - - // Convert to Base64 - wrap at 64 characters. - array = array.toBase64(); - QByteArray tmp; - for (int i = 0; i <= array.size() - 64; i += 64) { - tmp += QByteArray::fromRawData(array.data() + i, 64); - tmp += '\n'; - } - if (int remainder = array.size() % 64) { - tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder); - tmp += '\n'; - } - - return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n"; -} - -QString QSslCertificatePrivate::text_from_X509(X509 *x509) -{ - if (!x509) { - qCWarning(lcSsl, "QSslSocketBackendPrivate::text_from_X509: null X509"); - return QString(); - } - - QByteArray result; - BIO *bio = q_BIO_new(q_BIO_s_mem()); - if (!bio) - return QString(); - - q_X509_print(bio, x509); - - QVarLengthArray<char, 16384> data; - int count = q_BIO_read(bio, data.data(), 16384); - if ( count > 0 ) { - result = QByteArray( data.data(), count ); - } - - q_BIO_free(bio); - - return QString::fromLatin1(result); -} - -QByteArray QSslCertificatePrivate::asn1ObjectId(ASN1_OBJECT *object) -{ - char buf[80]; // The openssl docs a buffer length of 80 should be more than enough - q_OBJ_obj2txt(buf, sizeof(buf), object, 1); // the 1 says always use the oid not the long name - - return QByteArray(buf); -} - - -QByteArray QSslCertificatePrivate::asn1ObjectName(ASN1_OBJECT *object) -{ - int nid = q_OBJ_obj2nid(object); - if (nid != NID_undef) - return QByteArray(q_OBJ_nid2sn(nid)); - - return asn1ObjectId(object); -} - -static QMultiMap<QByteArray, QString> _q_mapFromX509Name(X509_NAME *name) -{ - 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 = QSslCertificatePrivate::asn1ObjectName(q_X509_NAME_ENTRY_get_object(e)); - unsigned char *data = nullptr; - int size = q_ASN1_STRING_to_UTF8(&data, q_X509_NAME_ENTRY_get_data(e)); - info.insert(name, QString::fromUtf8((char*)data, size)); -#if QT_CONFIG(opensslv11) - q_CRYPTO_free(data, nullptr, 0); -#else - q_CRYPTO_free(data); -#endif - } - - return info; -} - -QSslCertificate QSslCertificatePrivate::QSslCertificate_from_X509(X509 *x509) -{ - QSslCertificate certificate; - if (!x509 || !QSslSocket::supportsSsl()) - return certificate; - - ASN1_TIME *nbef = q_X509_getm_notBefore(x509); - ASN1_TIME *naft = q_X509_getm_notAfter(x509); - - certificate.d->notValidBefore = q_getTimeFromASN1(nbef); - certificate.d->notValidAfter = q_getTimeFromASN1(naft); - certificate.d->null = false; - certificate.d->x509 = q_X509_dup(x509); - - return certificate; -} - -static bool matchLineFeed(const QByteArray &pem, int *offset) -{ - char ch = 0; - - // ignore extra whitespace at the end of the line - while (*offset < pem.size() && (ch = pem.at(*offset)) == ' ') - ++*offset; - - if (ch == '\n') { - *offset += 1; - return true; - } - if (ch == '\r' && pem.size() > (*offset + 1) && pem.at(*offset + 1) == '\n') { - *offset += 2; - return true; - } - return false; -} - -QList<QSslCertificate> QSslCertificatePrivate::certificatesFromPem(const QByteArray &pem, int count) -{ - QList<QSslCertificate> certificates; - QSslSocketPrivate::ensureInitialized(); - - int offset = 0; - while (count == -1 || certificates.size() < count) { - int startPos = pem.indexOf(BEGINCERTSTRING, offset); - if (startPos == -1) - break; - startPos += sizeof(BEGINCERTSTRING) - 1; - if (!matchLineFeed(pem, &startPos)) - break; - - int endPos = pem.indexOf(ENDCERTSTRING, startPos); - if (endPos == -1) - break; - - offset = endPos + sizeof(ENDCERTSTRING) - 1; - if (offset < pem.size() && !matchLineFeed(pem, &offset)) - break; - - QByteArray decoded = QByteArray::fromBase64( - QByteArray::fromRawData(pem.data() + startPos, endPos - startPos)); - const unsigned char *data = (const unsigned char *)decoded.data(); - - if (X509 *x509 = q_d2i_X509(nullptr, &data, decoded.size())) { - certificates << QSslCertificate_from_X509(x509); - q_X509_free(x509); - } - } - - return certificates; -} - -QList<QSslCertificate> QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count) -{ - QList<QSslCertificate> certificates; - QSslSocketPrivate::ensureInitialized(); - - const unsigned char *data = (const unsigned char *)der.data(); - int size = der.size(); - - while (size > 0 && (count == -1 || certificates.size() < count)) { - if (X509 *x509 = q_d2i_X509(nullptr, &data, size)) { - certificates << QSslCertificate_from_X509(x509); - q_X509_free(x509); - } else { - break; - } - size -= ((const char *)data - der.data()); - } - - return certificates; -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslcertificate_p.h b/src/network/ssl/qsslcertificate_p.h index 4588aa7d6f..ca59abae82 100644 --- a/src/network/ssl/qsslcertificate_p.h +++ b/src/network/ssl/qsslcertificate_p.h @@ -1,48 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only -#ifndef QSSLCERTIFICATE_OPENSSL_P_H -#define QSSLCERTIFICATE_OPENSSL_P_H - -#include <QtNetwork/private/qtnetworkglobal_p.h> -#include "qsslcertificate.h" +#ifndef QSSLCERTIFICATE_P_H +#define QSSLCERTIFICATE_P_H // // W A R N I N G @@ -55,99 +16,32 @@ // We mean it. // -#ifndef QT_NO_SSL -#include "qsslsocket_p.h" -#endif +#include <QtNetwork/private/qtnetworkglobal_p.h> + #include "qsslcertificateextension.h" -#include <QtCore/qdatetime.h> -#include <QtCore/qmap.h> +#include "qsslcertificate.h" +#include "qtlsbackend_p.h" -#ifndef QT_NO_OPENSSL -#include <openssl/x509.h> -#else -struct X509; -struct X509_EXTENSION; -struct ASN1_OBJECT; -#endif +#include <qlist.h> -#if QT_CONFIG(schannel) -#include <wincrypt.h> -#endif +#include <memory> QT_BEGIN_NAMESPACE -// forward declaration - class QSslCertificatePrivate { public: - QSslCertificatePrivate() - : null(true), x509(nullptr) - { -#ifndef QT_NO_SSL - QSslSocketPrivate::ensureInitialized(); -#endif - } - - ~QSslCertificatePrivate() - { -#ifndef QT_NO_OPENSSL - if (x509) - q_X509_free(x509); -#endif -#if QT_CONFIG(schannel) - if (certificateContext) - CertFreeCertificateContext(certificateContext); -#endif - } - - bool null; - QByteArray versionString; - QByteArray serialNumberString; + QSslCertificatePrivate(); + ~QSslCertificatePrivate(); - QMultiMap<QByteArray, QString> issuerInfo; - QMultiMap<QByteArray, QString> subjectInfo; - QDateTime notValidAfter; - QDateTime notValidBefore; - -#ifdef QT_NO_OPENSSL - bool subjectMatchesIssuer; - QSsl::KeyAlgorithm publicKeyAlgorithm; - QByteArray publicKeyDerData; - QMultiMap<QSsl::AlternativeNameEntryType, QString> subjectAlternativeNames; - QList<QSslCertificateExtension> extensions; - - QByteArray derData; - - bool parse(const QByteArray &data); - bool parseExtension(const QByteArray &data, QSslCertificateExtension *extension); -#endif - X509 *x509; - - void init(const QByteArray &data, QSsl::EncodingFormat format); - - static QByteArray asn1ObjectId(ASN1_OBJECT *object); - static QByteArray asn1ObjectName(ASN1_OBJECT *object); - static QByteArray QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format); - static QString text_from_X509(X509 *x509); - static QSslCertificate QSslCertificate_from_X509(X509 *x509); - static QList<QSslCertificate> certificatesFromPem(const QByteArray &pem, int count = -1); - static QList<QSslCertificate> certificatesFromDer(const QByteArray &der, int count = -1); - static bool isBlacklisted(const QSslCertificate &certificate); - static QSslCertificateExtension convertExtension(X509_EXTENSION *ext); - static QByteArray subjectInfoToString(QSslCertificate::SubjectInfo info); - - friend class QSslSocketBackendPrivate; + QList<QSslCertificateExtension> extensions() const; + Q_NETWORK_EXPORT static bool isBlacklisted(const QSslCertificate &certificate); + Q_NETWORK_EXPORT static QByteArray subjectInfoToString(QSslCertificate::SubjectInfo info); QAtomicInt ref; - -#if QT_CONFIG(schannel) - const CERT_CONTEXT *certificateContext = nullptr; - - static QSslCertificate QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext); -#endif + std::unique_ptr<QTlsPrivate::X509Certificate> backend; }; QT_END_NAMESPACE -#endif // QSSLCERTIFICATE_OPENSSL_P_H +#endif // QSSLCERTIFICATE_P_H diff --git a/src/network/ssl/qsslcertificate_qt.cpp b/src/network/ssl/qsslcertificate_qt.cpp deleted file mode 100644 index 7c050e9c3b..0000000000 --- a/src/network/ssl/qsslcertificate_qt.cpp +++ /dev/null @@ -1,575 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsslcertificate.h" -#include "qsslcertificate_p.h" - -#include "qssl_p.h" -#ifndef QT_NO_SSL -#include "qsslkey.h" -#include "qsslkey_p.h" -#endif -#include "qsslcertificateextension.h" -#include "qsslcertificateextension_p.h" -#include "qasn1element_p.h" - -#include <QtCore/qdatastream.h> -#include <QtCore/qendian.h> -#include <QtNetwork/qhostaddress.h> - -QT_BEGIN_NAMESPACE - -bool QSslCertificate::operator==(const QSslCertificate &other) const -{ - if (d == other.d) - return true; - if (d->null && other.d->null) - return true; - return d->derData == other.d->derData; -} - -size_t qHash(const QSslCertificate &key, size_t seed) noexcept -{ - // DER is the native encoding here, so toDer() is just "return d->derData": - return qHash(key.toDer(), seed); -} - -bool QSslCertificate::isNull() const -{ - return d->null; -} - -bool QSslCertificate::isSelfSigned() const -{ - if (d->null) - return false; - - qCWarning(lcSsl, - "QSslCertificate::isSelfSigned: This function does not check, whether the certificate " - "is actually signed. It just checks whether issuer and subject are identical"); - return d->subjectMatchesIssuer; -} - -QByteArray QSslCertificate::version() const -{ - return d->versionString; -} - -QByteArray QSslCertificate::serialNumber() const -{ - return d->serialNumberString; -} - -QStringList QSslCertificate::issuerInfo(SubjectInfo info) const -{ - return issuerInfo(QSslCertificatePrivate::subjectInfoToString(info)); -} - -QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const -{ - return d->issuerInfo.values(attribute); -} - -QStringList QSslCertificate::subjectInfo(SubjectInfo info) const -{ - return subjectInfo(QSslCertificatePrivate::subjectInfoToString(info)); -} - -QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const -{ - return d->subjectInfo.values(attribute); -} - -QList<QByteArray> QSslCertificate::subjectInfoAttributes() const -{ - return d->subjectInfo.uniqueKeys(); -} - -QList<QByteArray> QSslCertificate::issuerInfoAttributes() const -{ - return d->issuerInfo.uniqueKeys(); -} - -QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const -{ - return d->subjectAlternativeNames; -} - -QDateTime QSslCertificate::effectiveDate() const -{ - return d->notValidBefore; -} - -QDateTime QSslCertificate::expiryDate() const -{ - return d->notValidAfter; -} - -#if !QT_CONFIG(schannel) // implemented in qsslcertificate_schannel.cpp -Qt::HANDLE QSslCertificate::handle() const -{ - Q_UNIMPLEMENTED(); - return nullptr; -} -#endif - -#ifndef QT_NO_SSL -QSslKey QSslCertificate::publicKey() const -{ - QSslKey key; - key.d->type = QSsl::PublicKey; - if (d->publicKeyAlgorithm != QSsl::Opaque) { - key.d->algorithm = d->publicKeyAlgorithm; - key.d->decodeDer(d->publicKeyDerData); - } - return key; -} -#endif - -QList<QSslCertificateExtension> QSslCertificate::extensions() const -{ - return d->extensions; -} - -#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----" -#define ENDCERTSTRING "-----END CERTIFICATE-----" - -QByteArray QSslCertificate::toPem() const -{ - QByteArray array = toDer(); - - // Convert to Base64 - wrap at 64 characters. - array = array.toBase64(); - QByteArray tmp; - for (int i = 0; i <= array.size() - 64; i += 64) { - tmp += QByteArray::fromRawData(array.data() + i, 64); - tmp += '\n'; - } - if (int remainder = array.size() % 64) { - tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder); - tmp += '\n'; - } - - return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n"; -} - -QByteArray QSslCertificate::toDer() const -{ - return d->derData; -} - -QString QSslCertificate::toText() const -{ - Q_UNIMPLEMENTED(); - return QString(); -} - -void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format) -{ - if (!data.isEmpty()) { - const QList<QSslCertificate> certs = (format == QSsl::Pem) - ? certificatesFromPem(data, 1) - : certificatesFromDer(data, 1); - if (!certs.isEmpty()) { - *this = *certs.first().d; -#if QT_CONFIG(schannel) - if (certificateContext) - certificateContext = CertDuplicateCertificateContext(certificateContext); -#endif - } - } -} - -static bool matchLineFeed(const QByteArray &pem, int *offset) -{ - char ch = 0; - - // ignore extra whitespace at the end of the line - while (*offset < pem.size() && (ch = pem.at(*offset)) == ' ') - ++*offset; - - if (ch == '\n') { - *offset += 1; - return true; - } - if (ch == '\r' && pem.size() > (*offset + 1) && pem.at(*offset + 1) == '\n') { - *offset += 2; - return true; - } - return false; -} - -QList<QSslCertificate> QSslCertificatePrivate::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> QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count) -{ - QList<QSslCertificate> certificates; - - QByteArray data = der; - while (count == -1 || certificates.size() < count) { - QSslCertificate cert; - if (!cert.d->parse(data)) - break; - - certificates << cert; - data.remove(0, cert.d->derData.size()); - } - - return certificates; -} - -static QByteArray colonSeparatedHex(const QByteArray &value) -{ - const int size = value.size(); - int i = 0; - while (i < size && !value.at(i)) // skip leading zeros - ++i; - - return value.mid(i).toHex(':'); -} - -bool QSslCertificatePrivate::parse(const QByteArray &data) -{ - QAsn1Element root; - - QDataStream dataStream(data); - if (!root.read(dataStream) || root.type() != QAsn1Element::SequenceType) - return false; - - QDataStream rootStream(root.value()); - QAsn1Element cert; - if (!cert.read(rootStream) || cert.type() != QAsn1Element::SequenceType) - return false; - - // version or serial number - QAsn1Element elem; - QDataStream certStream(cert.value()); - if (!elem.read(certStream)) - return false; - - if (elem.type() == QAsn1Element::Context0Type) { - QDataStream versionStream(elem.value()); - if (!elem.read(versionStream) - || elem.type() != QAsn1Element::IntegerType - || elem.value().isEmpty()) - return false; - - versionString = QByteArray::number(elem.value().at(0) + 1); - if (!elem.read(certStream)) - return false; - } else { - versionString = QByteArray::number(1); - } - - // serial number - if (elem.type() != QAsn1Element::IntegerType) - return false; - serialNumberString = colonSeparatedHex(elem.value()); - - // algorithm ID - if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) - return false; - - // issuer info - if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) - return false; - - QByteArray issuerDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length()); - issuerInfo = elem.toInfo(); - - // validity period - if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) - return false; - - QDataStream validityStream(elem.value()); - if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType)) - return false; - - notValidBefore = elem.toDateTime(); - if (!notValidBefore.isValid()) - return false; - - if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType)) - return false; - - notValidAfter = elem.toDateTime(); - if (!notValidAfter.isValid()) - return false; - - - // subject name - if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) - return false; - - QByteArray subjectDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length()); - subjectInfo = elem.toInfo(); - subjectMatchesIssuer = issuerDer == subjectDer; - - // public key - qint64 keyStart = certStream.device()->pos(); - if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) - return false; - - publicKeyDerData.resize(certStream.device()->pos() - keyStart); - QDataStream keyStream(elem.value()); - if (!elem.read(keyStream) || elem.type() != QAsn1Element::SequenceType) - return false; - - - // key algorithm - if (!elem.read(elem.value()) || elem.type() != QAsn1Element::ObjectIdentifierType) - return false; - - const QByteArray oid = elem.toObjectId(); - if (oid == RSA_ENCRYPTION_OID) - publicKeyAlgorithm = QSsl::Rsa; - else if (oid == DSA_ENCRYPTION_OID) - publicKeyAlgorithm = QSsl::Dsa; - else if (oid == EC_ENCRYPTION_OID) - publicKeyAlgorithm = QSsl::Ec; - else - publicKeyAlgorithm = QSsl::Opaque; - - certStream.device()->seek(keyStart); - certStream.readRawData(publicKeyDerData.data(), publicKeyDerData.size()); - - // extensions - while (elem.read(certStream)) { - if (elem.type() == QAsn1Element::Context3Type) { - if (elem.read(elem.value()) && elem.type() == QAsn1Element::SequenceType) { - QDataStream extStream(elem.value()); - while (elem.read(extStream) && elem.type() == QAsn1Element::SequenceType) { - QSslCertificateExtension extension; - if (!parseExtension(elem.value(), &extension)) - return false; - - if (extension.oid() == QLatin1String("2.5.29.17")) { - // subjectAltName - - // Note, parseExtension() returns true for this extensions, - // but considers it to be unsupported and assignes a useless - // value. OpenSSL also treats this extension as unsupported, - // but properly creates a map with 'name' and 'value' taken - // from the extension. We only support 'email', 'IP' and 'DNS', - // but this is what our subjectAlternativeNames map can contain - // anyway. - QVariantMap extValue; - QAsn1Element sanElem; - if (sanElem.read(extension.value().toByteArray()) && sanElem.type() == QAsn1Element::SequenceType) { - QDataStream nameStream(sanElem.value()); - QAsn1Element nameElem; - while (nameElem.read(nameStream)) { - switch (nameElem.type()) { - case QAsn1Element::Rfc822NameType: - subjectAlternativeNames.insert(QSsl::EmailEntry, nameElem.toString()); - extValue[QStringLiteral("email")] = nameElem.toString(); - break; - case QAsn1Element::DnsNameType: - subjectAlternativeNames.insert(QSsl::DnsEntry, nameElem.toString()); - extValue[QStringLiteral("DNS")] = nameElem.toString(); - break; - case QAsn1Element::IpAddressType: { - QHostAddress ipAddress; - QByteArray ipAddrValue = nameElem.value(); - switch (ipAddrValue.length()) { - case 4: // IPv4 - ipAddress = QHostAddress(qFromBigEndian(*reinterpret_cast<quint32 *>(ipAddrValue.data()))); - break; - case 16: // IPv6 - ipAddress = QHostAddress(reinterpret_cast<quint8 *>(ipAddrValue.data())); - break; - default: // Unknown IP address format - break; - } - if (!ipAddress.isNull()) { - subjectAlternativeNames.insert(QSsl::IpAddressEntry, ipAddress.toString()); - extValue[QStringLiteral("IP")] = ipAddress.toString(); - } - break; - } - default: - break; - } - } - extension.d->value = extValue; - extension.d->supported = true; - } - } - - extensions << extension; - } - } - } - } - - derData = data.left(dataStream.device()->pos()); - null = false; - return true; -} - -bool QSslCertificatePrivate::parseExtension(const QByteArray &data, QSslCertificateExtension *extension) -{ - bool ok; - bool critical = false; - QAsn1Element oidElem, valElem; - - QDataStream seqStream(data); - - // oid - if (!oidElem.read(seqStream) || oidElem.type() != QAsn1Element::ObjectIdentifierType) - return false; - const QByteArray oid = oidElem.toObjectId(); - - // critical and value - if (!valElem.read(seqStream)) - return false; - if (valElem.type() == QAsn1Element::BooleanType) { - critical = valElem.toBool(&ok); - if (!ok || !valElem.read(seqStream)) - return false; - } - if (valElem.type() != QAsn1Element::OctetStringType) - return false; - - // interpret value - QAsn1Element val; - bool supported = true; - QVariant value; - if (oid == "1.3.6.1.5.5.7.1.1") { - // authorityInfoAccess - if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType) - return false; - QVariantMap result; - const auto elems = val.toList(); - for (const QAsn1Element &el : elems) { - const auto items = el.toList(); - if (items.size() != 2) - return false; - const QString key = QString::fromLatin1(items.at(0).toObjectName()); - switch (items.at(1).type()) { - case QAsn1Element::Rfc822NameType: - case QAsn1Element::DnsNameType: - case QAsn1Element::UniformResourceIdentifierType: - result[key] = items.at(1).toString(); - break; - } - } - value = result; - } else if (oid == "2.5.29.14") { - // subjectKeyIdentifier - if (!val.read(valElem.value()) || val.type() != QAsn1Element::OctetStringType) - return false; - value = colonSeparatedHex(val.value()).toUpper(); - } else if (oid == "2.5.29.19") { - // basicConstraints - if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType) - return false; - - QVariantMap result; - const auto items = val.toList(); - if (items.size() > 0) { - result[QStringLiteral("ca")] = items.at(0).toBool(&ok); - if (!ok) - return false; - } else { - result[QStringLiteral("ca")] = false; - } - if (items.size() > 1) { - result[QStringLiteral("pathLenConstraint")] = items.at(1).toInteger(&ok); - if (!ok) - return false; - } - value = result; - } else if (oid == "2.5.29.35") { - // authorityKeyIdentifier - if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType) - return false; - QVariantMap result; - const auto elems = val.toList(); - for (const QAsn1Element &el : elems) { - if (el.type() == 0x80) { - const QString key = QStringLiteral("keyid"); - result[key] = el.value().toHex(); - } else if (el.type() == 0x82) { - const QString serial = QStringLiteral("serial"); - result[serial] = colonSeparatedHex(el.value()); - } - } - value = result; - } else { - supported = false; - value = valElem.value(); - } - - extension->d->critical = critical; - extension->d->supported = supported; - extension->d->oid = QString::fromLatin1(oid); - extension->d->name = QString::fromLatin1(oidElem.toObjectName()); - extension->d->value = value; - - return true; -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslcertificate_schannel.cpp b/src/network/ssl/qsslcertificate_schannel.cpp deleted file mode 100644 index 5ea713612a..0000000000 --- a/src/network/ssl/qsslcertificate_schannel.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsslcertificate.h" -#include "qsslcertificate_p.h" - -#include <wincrypt.h> - -QT_BEGIN_NAMESPACE - -QSslCertificate QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext) -{ - QByteArray derData = QByteArray((const char *)certificateContext->pbCertEncoded, - certificateContext->cbCertEncoded); - - QSslCertificate certificate(derData, QSsl::Der); - certificate.d->certificateContext = CertDuplicateCertificateContext(certificateContext); - return certificate; -} - -Qt::HANDLE QSslCertificate::handle() const -{ - return Qt::HANDLE(d->certificateContext); -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslcertificateextension.cpp b/src/network/ssl/qsslcertificateextension.cpp index 4896d3909a..3f583e2e2f 100644 --- a/src/network/ssl/qsslcertificateextension.cpp +++ b/src/network/ssl/qsslcertificateextension.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2011 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$ -** -****************************************************************************/ +// Copyright (C) 2011 Richard J. Moore <rich@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /*! \class QSslCertificateExtension diff --git a/src/network/ssl/qsslcertificateextension.h b/src/network/ssl/qsslcertificateextension.h index 7cc8a888be..c639d2fa45 100644 --- a/src/network/ssl/qsslcertificateextension.h +++ b/src/network/ssl/qsslcertificateextension.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2011 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$ -** -****************************************************************************/ +// Copyright (C) 2011 Richard J. Moore <rich@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSSLCERTIFICATEEXTENSION_H #define QSSLCERTIFICATEEXTENSION_H @@ -59,7 +23,7 @@ public: QSslCertificateExtension &operator=(const QSslCertificateExtension &other); ~QSslCertificateExtension(); - void swap(QSslCertificateExtension &other) noexcept { qSwap(d, other.d); } + void swap(QSslCertificateExtension &other) noexcept { d.swap(other.d); } QString oid() const; QString name() const; diff --git a/src/network/ssl/qsslcertificateextension_p.h b/src/network/ssl/qsslcertificateextension_p.h index 373f92a5cf..3f5d1e373e 100644 --- a/src/network/ssl/qsslcertificateextension_p.h +++ b/src/network/ssl/qsslcertificateextension_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2011 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$ -** -****************************************************************************/ +// Copyright (C) 2011 Richard J. Moore <rich@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSSLCERTIFICATEEXTENSION_P_H #define QSSLCERTIFICATEEXTENSION_P_H diff --git a/src/network/ssl/qsslcipher.cpp b/src/network/ssl/qsslcipher.cpp index 738d521a38..2a4da7991a 100644 --- a/src/network/ssl/qsslcipher.cpp +++ b/src/network/ssl/qsslcipher.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /*! @@ -68,6 +32,9 @@ QT_BEGIN_NAMESPACE +static_assert(QT_VERSION < QT_VERSION_CHECK(7, 0, 0) + && sizeof(QScopedPointer<QSslCipherPrivate>) == sizeof(std::unique_ptr<QSslCipherPrivate>)); + /*! Constructs an empty QSslCipher object. */ @@ -127,7 +94,7 @@ QSslCipher::QSslCipher(const QString &name, QSsl::SslProtocol protocol) QSslCipher::QSslCipher(const QSslCipher &other) : d(new QSslCipherPrivate) { - *d.data() = *other.d.data(); + *d.get() = *other.d.get(); } /*! @@ -143,7 +110,7 @@ QSslCipher::~QSslCipher() */ QSslCipher &QSslCipher::operator=(const QSslCipher &other) { - *d.data() = *other.d.data(); + *d.get() = *other.d.get(); return *this; } diff --git a/src/network/ssl/qsslcipher.h b/src/network/ssl/qsslcipher.h index 6994f590ae..ed727947f5 100644 --- a/src/network/ssl/qsslcipher.h +++ b/src/network/ssl/qsslcipher.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSSLCIPHER_H @@ -46,6 +10,8 @@ #include <QtCore/qscopedpointer.h> #include <QtNetwork/qssl.h> +#include <memory> + QT_BEGIN_NAMESPACE @@ -64,7 +30,7 @@ public: ~QSslCipher(); void swap(QSslCipher &other) noexcept - { qSwap(d, other.d); } + { d.swap(other.d); } bool operator==(const QSslCipher &other) const; inline bool operator!=(const QSslCipher &other) const { return !operator==(other); } @@ -81,8 +47,9 @@ public: QSsl::SslProtocol protocol() const; private: - QScopedPointer<QSslCipherPrivate> d; - friend class QSslSocketBackendPrivate; + // ### Qt 7: make implicitly shared + std::unique_ptr<QSslCipherPrivate> d; + friend class QTlsBackend; }; Q_DECLARE_SHARED(QSslCipher) diff --git a/src/network/ssl/qsslcipher_p.h b/src/network/ssl/qsslcipher_p.h index b8629f9f96..d7f5e7c471 100644 --- a/src/network/ssl/qsslcipher_p.h +++ b/src/network/ssl/qsslcipher_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSSLCIPHER_P_H #define QSSLCIPHER_P_H diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp index 916774db04..fd308d7037 100644 --- a/src/network/ssl/qsslconfiguration.cpp +++ b/src/network/ssl/qsslconfiguration.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2014 BlackBerry Limited. All rights reserved. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qssl_p.h" #include "qsslconfiguration.h" @@ -48,6 +12,8 @@ QT_BEGIN_NAMESPACE +QT_IMPL_METATYPE_EXTERN(QSslConfiguration) + const QSsl::SslOptions QSslConfigurationPrivate::defaultSslOptions = QSsl::SslOptionDisableEmptyFragments |QSsl::SslOptionDisableLegacyRenegotiation |QSsl::SslOptionDisableCompression @@ -107,7 +73,7 @@ const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1"; change the settings in the related SSL connection. You must call setSslConfiguration on a modified QSslConfiguration object to achieve that. The following example illustrates how to change the - protocol to TLSv1_0 in a QSslSocket object: + protocol to TLSv1_2 in a QSslSocket object: \snippet code/src_network_ssl_qsslconfiguration.cpp 0 @@ -139,6 +105,12 @@ const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1"; */ /*! + \variable QSslConfiguration::ALPNProtocolHTTP2 + \brief The value used for negotiating HTTP 2 during the Application-Layer + Protocol Negotiation. +*/ + +/*! Constructs an empty SSL configuration. This configuration contains no valid settings and the state will be empty. isNull() will return true after this constructor is called. @@ -252,15 +224,15 @@ bool QSslConfiguration::isNull() const d->peerVerifyMode == QSslSocket::AutoVerifyPeer && d->peerVerifyDepth == 0 && d->allowRootCertOnDemandLoading == true && - d->caCertificates.count() == 0 && - d->ciphers.count() == 0 && + d->caCertificates.size() == 0 && + d->ciphers.size() == 0 && d->ellipticCurves.isEmpty() && d->ephemeralServerKey.isNull() && d->dhParams == QSslDiffieHellmanParameters::defaultParameters() && d->localCertificateChain.isEmpty() && d->privateKey.isNull() && d->peerCertificate.isNull() && - d->peerCertificateChain.count() == 0 && + d->peerCertificateChain.size() == 0 && d->backendConfig.isEmpty() && d->sslOptions == QSslConfigurationPrivate::defaultSslOptions && d->sslSession.isNull() && @@ -584,8 +556,6 @@ void QSslConfiguration::setPrivateKey(const QSslKey &key) ciphers. You can revert to using the entire set by calling setCiphers() with the list returned by supportedCiphers(). - \note This is not currently supported in the Schannel backend. - \sa setCiphers(), supportedCiphers() */ QList<QSslCipher> QSslConfiguration::ciphers() const @@ -601,8 +571,6 @@ QList<QSslCipher> QSslConfiguration::ciphers() const Restricting the cipher suite must be done before the handshake phase, where the session cipher is chosen. - \note This is not currently supported in the Schannel backend. - \sa ciphers(), supportedCiphers() */ void QSslConfiguration::setCiphers(const QList<QSslCipher> &ciphers) @@ -615,16 +583,14 @@ void QSslConfiguration::setCiphers(const QList<QSslCipher> &ciphers) Sets the cryptographic cipher suite for this configuration to \a ciphers, which is a colon-separated list of cipher suite names. The ciphers are listed - in order of preference, starting with the most preferred cipher. For example: - - \snippet code/src_network_ssl_qsslconfiguration.cpp 1 - + in order of preference, starting with the most preferred cipher. Each cipher name in \a ciphers must be the name of a cipher in the list returned by supportedCiphers(). Restricting the cipher suite must be done before the handshake phase, where the session cipher is chosen. - \note This is not currently supported in the Schannel backend. + \note With the Schannel backend the order of the ciphers is ignored and Schannel + picks the most secure one during the handshake. \sa ciphers() */ @@ -632,7 +598,7 @@ void QSslConfiguration::setCiphers(const QString &ciphers) { auto *p = d.data(); p->ciphers.clear(); - const auto cipherNames = ciphers.split(QLatin1Char(':'), Qt::SkipEmptyParts); + const auto cipherNames = ciphers.split(u':', Qt::SkipEmptyParts); for (const QString &cipherName : cipherNames) { QSslCipher cipher(cipherName); if (!cipher.isNull()) @@ -956,7 +922,11 @@ void QSslConfiguration::setPreSharedKeyIdentityHint(const QByteArray &hint) Retrieves the current set of Diffie-Hellman parameters. If no Diffie-Hellman parameters have been set, the QSslConfiguration object - defaults to using the 1024-bit MODP group from RFC 2409. + defaults to using the 2048-bit MODP group from RFC 3526. + + \note The default parameters may change in future Qt versions. + Please check the documentation of the \e{exact Qt version} that you + are using in order to know what defaults that version uses. */ QSslDiffieHellmanParameters QSslConfiguration::diffieHellmanParameters() const { @@ -970,7 +940,14 @@ QSslDiffieHellmanParameters QSslConfiguration::diffieHellmanParameters() const a server to \a dhparams. If no Diffie-Hellman parameters have been set, the QSslConfiguration object - defaults to using the 1024-bit MODP group from RFC 2409. + defaults to using the 2048-bit MODP group from RFC 3526. + + Since 6.7 you can provide an empty Diffie-Hellman parameter to use auto selection + (see SSL_CTX_set_dh_auto of openssl) if the tls backend supports it. + + \note The default parameters may change in future Qt versions. + Please check the documentation of the \e{exact Qt version} that you + are using in order to know what defaults that version uses. */ void QSslConfiguration::setDiffieHellmanParameters(const QSslDiffieHellmanParameters &dhparams) { @@ -1132,7 +1109,7 @@ void QSslConfiguration::setDefaultConfiguration(const QSslConfiguration &configu QSslConfigurationPrivate::setDefaultConfiguration(configuration); } -#if QT_CONFIG(dtls) || defined(Q_CLANG_QDOC) +#if QT_CONFIG(dtls) || defined(Q_QDOC) /*! This function returns true if DTLS cookie verification was enabled on a diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h index 116920950e..dd2dd2a97c 100644 --- a/src/network/ssl/qsslconfiguration.h +++ b/src/network/ssl/qsslconfiguration.h @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2014 BlackBerry Limited. All rights reserved. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /**************************************************************************** ** @@ -72,11 +36,6 @@ class QSslKey; class QSslEllipticCurve; class QSslDiffieHellmanParameters; -namespace dtlsopenssl -{ -class DtlsState; -} - class QSslConfigurationPrivate; class Q_NETWORK_EXPORT QSslConfiguration { @@ -88,7 +47,7 @@ public: QSslConfiguration &operator=(const QSslConfiguration &other); void swap(QSslConfiguration &other) noexcept - { qSwap(d, other.d); } + { d.swap(other.d); } bool operator==(const QSslConfiguration &other) const; inline bool operator!=(const QSslConfiguration &other) const @@ -166,7 +125,7 @@ public: static QSslConfiguration defaultConfiguration(); static void setDefaultConfiguration(const QSslConfiguration &configuration); -#if QT_CONFIG(dtls) || defined(Q_CLANG_QDOC) +#if QT_CONFIG(dtls) || defined(Q_QDOC) bool dtlsCookieVerificationEnabled() const; void setDtlsCookieVerificationEnabled(bool enable); @@ -201,10 +160,8 @@ public: private: friend class QSslSocket; friend class QSslConfigurationPrivate; - friend class QSslSocketBackendPrivate; friend class QSslContext; - friend class QDtlsBasePrivate; - friend class dtlsopenssl::DtlsState; + friend class QTlsBackend; QSslConfiguration(QSslConfigurationPrivate *dd); QSharedDataPointer<QSslConfigurationPrivate> d; }; @@ -213,7 +170,7 @@ Q_DECLARE_SHARED(QSslConfiguration) QT_END_NAMESPACE -Q_DECLARE_METATYPE(QSslConfiguration) +QT_DECL_METATYPE_EXTERN(QSslConfiguration, Q_NETWORK_EXPORT) #endif // QT_NO_SSL diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h index 3d416b42d7..a31e7e1f04 100644 --- a/src/network/ssl/qsslconfiguration_p.h +++ b/src/network/ssl/qsslconfiguration_p.h @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2014 BlackBerry Limited. All rights reserved. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /**************************************************************************** ** @@ -118,7 +82,7 @@ public: QSsl::SslOptions sslOptions; - Q_AUTOTEST_EXPORT static const QSsl::SslOptions defaultSslOptions; + static const QSsl::SslOptions defaultSslOptions; QList<QSslEllipticCurve> ellipticCurves; diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp deleted file mode 100644 index a8d92bd80d..0000000000 --- a/src/network/ssl/qsslcontext_openssl.cpp +++ /dev/null @@ -1,767 +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/qssl_p.h" -#include "private/qsslsocket_p.h" -#include "private/qsslcontext_openssl_p.h" -#include "private/qsslsocket_openssl_p.h" -#include "private/qsslsocket_openssl_symbols_p.h" -#include "private/qssldiffiehellmanparameters_p.h" - -#include <vector> - -QT_BEGIN_NAMESPACE - -Q_GLOBAL_STATIC(bool, forceSecurityLevel) - -Q_NETWORK_EXPORT void qt_ForceTlsSecurityLevel() -{ - *forceSecurityLevel() = true; -} - -// defined in qsslsocket_openssl.cpp: -extern int q_X509Callback(int ok, X509_STORE_CTX *ctx); -extern "C" int q_X509CallbackDirect(int ok, X509_STORE_CTX *ctx); -extern QString getErrorsFromOpenSsl(); - -#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 - -// Defined in qsslsocket.cpp -QList<QSslCipher> q_getDefaultDtlsCiphers(); - -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); -} - -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; -} - -#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") : QSslSocketBackendPrivate::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. - long options = QSslSocketBackendPrivate::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 ? q_getDefaultDtlsCiphers() : QSslSocketPrivate::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(QSslSocketBackendPrivate::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(QSslSocketBackendPrivate::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::s_loadRootCertsOnDemand && 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 = QSslSocketBackendPrivate::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(QSslSocketBackendPrivate::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(QSslSocketBackendPrivate::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(QSslSocketBackendPrivate::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 - q_X509Callback; - - if (!isDtls && configuration.handshakeMustInterruptOnError()) - verificationCallback = 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(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); - sslContext->errorCode = QSslError::UnspecifiedError; - return; - } -#endif - } - - applyBackendConfig(sslContext); -} - -#if QT_CONFIG(ocsp) -extern "C" int qt_OCSP_status_server_callback(SSL *ssl, void *); // Defined in qsslsocket_openssl.cpp. -#endif // ocsp -// static -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, 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 5385c42240..0000000000 --- a/src/network/ssl/qsslcontext_openssl_p.h +++ /dev/null @@ -1,129 +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); - - 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.cpp b/src/network/ssl/qssldiffiehellmanparameters.cpp index e5574b2e09..7da14f3536 100644 --- a/src/network/ssl/qssldiffiehellmanparameters.cpp +++ b/src/network/ssl/qssldiffiehellmanparameters.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk> -** 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$ -** -****************************************************************************/ +// Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /*! @@ -56,6 +20,7 @@ #include "qssldiffiehellmanparameters.h" #include "qssldiffiehellmanparameters_p.h" +#include "qtlsbackend_p.h" #include "qsslsocket.h" #include "qsslsocket_p.h" @@ -68,17 +33,18 @@ QT_BEGIN_NAMESPACE -// The 1024-bit MODP group from RFC 2459 (Second Oakley Group) +// The 2048-bit MODP group from RFC 3526 Q_AUTOTEST_EXPORT const char *qssl_dhparams_default_base64 = - "MIGHAoGBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiKZ8x0Agu+pjsTmyJR" - "Sgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY37WsL" - "/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7OZTgf//////////AgEC"; + "MIIBCAKCAQEA///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxObIlFKCHmO" + "NATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjftawv/XLb0Brft7jhr" + "+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXTmmkWP6j9JM9fg2VdI9yjrZYc" + "YvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhghfDKQXkYuNs474553LBgOhgObJ4Oi7Aei" + "j7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq5RXSJhiY+gUQFXKOWoqsqmj//////////wIBAg=="; /*! Returns the default QSslDiffieHellmanParameters used by QSslSocket. - This is currently the 1024-bit MODP group from RFC 2459, also - known as the Second Oakley Group. + This is currently the 2048-bit MODP group from RFC 3526. */ QSslDiffieHellmanParameters QSslDiffieHellmanParameters::defaultParameters() { @@ -117,12 +83,15 @@ QSslDiffieHellmanParameters::QSslDiffieHellmanParameters() QSslDiffieHellmanParameters QSslDiffieHellmanParameters::fromEncoded(const QByteArray &encoded, QSsl::EncodingFormat encoding) { QSslDiffieHellmanParameters result; + const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse(); + if (!tlsBackend) + return result; switch (encoding) { case QSsl::Der: - result.d->decodeDer(encoded); + result.d->initFromDer(encoded); break; case QSsl::Pem: - result.d->decodePem(encoded); + result.d->initFromPem(encoded); break; } return result; @@ -273,8 +242,7 @@ QString QSslDiffieHellmanParameters::errorString() const noexcept return QCoreApplication::translate("QSslDiffieHellmanParameter", "The given Diffie-Hellman parameters are deemed unsafe"); } - Q_UNREACHABLE(); - return QString(); + Q_UNREACHABLE_RETURN(QString()); } /*! @@ -299,6 +267,24 @@ bool QSslDiffieHellmanParameters::isEqual(const QSslDiffieHellmanParameters &oth return d->derData == other.d->derData; } +/*! + \internal +*/ +void QSslDiffieHellmanParametersPrivate::initFromDer(const QByteArray &der) +{ + if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) + error = QSslDiffieHellmanParameters::Error(tlsBackend->dhParametersFromDer(der, &derData)); +} + +/*! + \internal +*/ +void QSslDiffieHellmanParametersPrivate::initFromPem(const QByteArray &pem) +{ + if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) + error = QSslDiffieHellmanParameters::Error(tlsBackend->dhParametersFromPem(pem, &derData)); +} + #ifndef QT_NO_DEBUG_STREAM /*! \since 5.8 diff --git a/src/network/ssl/qssldiffiehellmanparameters.h b/src/network/ssl/qssldiffiehellmanparameters.h index c65697796b..d1a525ba26 100644 --- a/src/network/ssl/qssldiffiehellmanparameters.h +++ b/src/network/ssl/qssldiffiehellmanparameters.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk> -** 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$ -** -****************************************************************************/ +// Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSSLDIFFIEHELLMANPARAMETERS_H @@ -82,7 +46,7 @@ public: QSslDiffieHellmanParameters &operator=(const QSslDiffieHellmanParameters &other); QSslDiffieHellmanParameters &operator=(QSslDiffieHellmanParameters &&other) noexcept { swap(other); return *this; } - void swap(QSslDiffieHellmanParameters &other) noexcept { qSwap(d, other.d); } + void swap(QSslDiffieHellmanParameters &other) noexcept { qt_ptr_swap(d, other.d); } static QSslDiffieHellmanParameters fromEncoded(const QByteArray &encoded, QSsl::EncodingFormat format = QSsl::Pem); static QSslDiffieHellmanParameters fromEncoded(QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem); diff --git a/src/network/ssl/qssldiffiehellmanparameters_dummy.cpp b/src/network/ssl/qssldiffiehellmanparameters_dummy.cpp deleted file mode 100644 index 8fcf141f73..0000000000 --- a/src/network/ssl/qssldiffiehellmanparameters_dummy.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk> -** 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 "qssldiffiehellmanparameters.h" -#include "qssldiffiehellmanparameters_p.h" - -#include <QtCore/qglobal.h> -#include <QtCore/qbytearray.h> - -QT_BEGIN_NAMESPACE - -void QSslDiffieHellmanParametersPrivate::decodeDer(const QByteArray &) -{ -} - -void QSslDiffieHellmanParametersPrivate::decodePem(const QByteArray &) -{ -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp b/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp deleted file mode 100644 index 05b3408b75..0000000000 --- a/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp +++ /dev/null @@ -1,224 +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 "qssldiffiehellmanparameters.h" -#include "qssldiffiehellmanparameters_p.h" -#include "qsslsocket_openssl_symbols_p.h" -#include "qsslsocket.h" -#include "qsslsocket_p.h" - -#include "private/qssl_p.h" - -#include <QtCore/qatomic.h> -#include <QtCore/qbytearray.h> -#include <QtCore/qiodevice.h> -#include <QtCore/qscopeguard.h> -#ifndef QT_NO_DEBUG_STREAM -#include <QtCore/qdebug.h> -#endif - -#include <openssl/bn.h> -#include <openssl/dh.h> - -QT_BEGIN_NAMESPACE - -#ifdef OPENSSL_NO_DEPRECATED_3_0 - -static 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"); - QSslSocketBackendPrivate::logAndClearErrorQueue(); - return 0; - } - const auto keyDeleter = qScopeGuard([key](){ - q_EVP_PKEY_free(key); - }); - if (!q_EVP_PKEY_set1_DH(key, dh)) { - qCWarning(lcSsl, "EVP_PKEY_set1_DH failed"); - QSslSocketBackendPrivate::logAndClearErrorQueue(); - return 0; - } - - EVP_PKEY_CTX *keyCtx = q_EVP_PKEY_CTX_new(key, nullptr); - if (!keyCtx) { - qCWarning(lcSsl, "EVP_PKEY_CTX_new failed"); - QSslSocketBackendPrivate::logAndClearErrorQueue(); - return 0; - } - const auto ctxDeleter = qScopeGuard([keyCtx]{ - q_EVP_PKEY_CTX_free(keyCtx); - }); - - const int result = q_EVP_PKEY_param_check(keyCtx); - QSslSocketBackendPrivate::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 - -static bool isSafeDH(DH *dh) -{ - int status = 0; - int bad = 0; - - 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); -} - -void QSslDiffieHellmanParametersPrivate::decodeDer(const QByteArray &der) -{ - if (der.isEmpty()) { - error = QSslDiffieHellmanParameters::InvalidInputDataError; - return; - } - - const unsigned char *data = reinterpret_cast<const unsigned char *>(der.data()); - int len = der.size(); - - QSslSocketPrivate::ensureInitialized(); - - DH *dh = q_d2i_DHparams(nullptr, &data, len); - if (dh) { - if (isSafeDH(dh)) - derData = der; - else - error = QSslDiffieHellmanParameters::UnsafeParametersError; - } else { - error = QSslDiffieHellmanParameters::InvalidInputDataError; - } - - q_DH_free(dh); -} - -void QSslDiffieHellmanParametersPrivate::decodePem(const QByteArray &pem) -{ - if (pem.isEmpty()) { - error = QSslDiffieHellmanParameters::InvalidInputDataError; - return; - } - - if (!QSslSocket::supportsSsl()) { - error = QSslDiffieHellmanParameters::InvalidInputDataError; - return; - } - - QSslSocketPrivate::ensureInitialized(); - - BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size()); - if (!bio) { - error = QSslDiffieHellmanParameters::InvalidInputDataError; - return; - } - - DH *dh = nullptr; - q_PEM_read_bio_DHparams(bio, &dh, nullptr, nullptr); - - if (dh) { - if (isSafeDH(dh)) { - char *buf = nullptr; - int len = q_i2d_DHparams(dh, reinterpret_cast<unsigned char **>(&buf)); - if (len > 0) - derData = QByteArray(buf, len); - else - error = QSslDiffieHellmanParameters::InvalidInputDataError; - } else { - error = QSslDiffieHellmanParameters::UnsafeParametersError; - } - } else { - error = QSslDiffieHellmanParameters::InvalidInputDataError; - } - - q_DH_free(dh); - q_BIO_free(bio); -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qssldiffiehellmanparameters_p.h b/src/network/ssl/qssldiffiehellmanparameters_p.h index 722c2e9cf0..705e0f007c 100644 --- a/src/network/ssl/qssldiffiehellmanparameters_p.h +++ b/src/network/ssl/qssldiffiehellmanparameters_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk> -** 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$ -** -****************************************************************************/ +// Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSSLDIFFIEHELLMANPARAMETERS_P_H @@ -53,23 +17,20 @@ // #include <QtNetwork/private/qtnetworkglobal_p.h> -#include <QSharedData> -#include "qsslkey.h" #include "qssldiffiehellmanparameters.h" -#include "qsslsocket_p.h" // includes wincrypt.h + +#include <QSharedData> QT_BEGIN_NAMESPACE class QSslDiffieHellmanParametersPrivate : public QSharedData { public: - QSslDiffieHellmanParametersPrivate() : error(QSslDiffieHellmanParameters::NoError) {} - - void decodeDer(const QByteArray &der); - void decodePem(const QByteArray &pem); + void initFromDer(const QByteArray &der); + void initFromPem(const QByteArray &pem); - QSslDiffieHellmanParameters::Error error; + QSslDiffieHellmanParameters::Error error = QSslDiffieHellmanParameters::NoError; QByteArray derData; }; diff --git a/src/network/ssl/qsslellipticcurve.cpp b/src/network/ssl/qsslellipticcurve.cpp index 6bc5ee6286..77aa66f3cc 100644 --- a/src/network/ssl/qsslellipticcurve.cpp +++ b/src/network/ssl/qsslellipticcurve.cpp @@ -1,43 +1,9 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2014 Governikus GmbH & Co. KG. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qsslellipticcurve.h" +#include "qtlsbackend_p.h" +#include "qsslsocket_p.h" #ifndef QT_NO_DEBUG_STREAM #include <QDebug> @@ -45,6 +11,8 @@ QT_BEGIN_NAMESPACE +QT_IMPL_METATYPE_EXTERN(QSslEllipticCurve) + /*! \class QSslEllipticCurve \since 5.5 @@ -77,8 +45,6 @@ QT_BEGIN_NAMESPACE */ /*! - \fn QSslEllipticCurve QSslEllipticCurve::fromShortName(const QString &name) - Returns an QSslEllipticCurve instance representing the named curve \a name. The \a name is the conventional short name for the curve, as represented by RFC 4492 (for instance \c{secp521r1}), @@ -91,10 +57,19 @@ QT_BEGIN_NAMESPACE \sa shortName() */ +QSslEllipticCurve QSslEllipticCurve::fromShortName(const QString &name) +{ + QSslEllipticCurve result; + if (name.isEmpty()) + return result; -/*! - \fn QSslEllipticCurve QSslEllipticCurve::fromLongName(const QString &name) + if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) + result.id = tlsBackend->curveIdFromShortName(name); + return result; +} + +/*! Returns an QSslEllipticCurve instance representing the named curve \a name. The \a name is a long name for the curve, whose exact spelling depends on the SSL implementation. @@ -105,24 +80,49 @@ QT_BEGIN_NAMESPACE \sa longName() */ +QSslEllipticCurve QSslEllipticCurve::fromLongName(const QString &name) +{ + QSslEllipticCurve result; + if (name.isEmpty()) + return result; -/*! - \fn QString QSslEllipticCurve::shortName() const + if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) + result.id = tlsBackend->curveIdFromLongName(name); + + return result; +} +/*! Returns the conventional short name for this curve. If this curve is invalid, returns an empty string. \sa longName() */ +QString QSslEllipticCurve::shortName() const +{ + QString name; -/*! - \fn QString QSslEllipticCurve::longName() const + if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) + name = tlsBackend->shortNameForId(id); + + return name; +} +/*! Returns the conventional long name for this curve. If this curve is invalid, returns an empty string. \sa shortName() */ +QString QSslEllipticCurve::longName() const +{ + QString name; + + if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) + name = tlsBackend->longNameForId(id); + + return name; +} /*! \fn bool QSslEllipticCurve::isValid() const @@ -131,12 +131,18 @@ QT_BEGIN_NAMESPACE */ /*! - \fn bool QSslEllipticCurve::isTlsNamedCurve() const - Returns true if this elliptic curve is one of the named curves that can be used in the key exchange when using an elliptic curve cipher with TLS; false otherwise. */ +bool QSslEllipticCurve::isTlsNamedCurve() const noexcept +{ + if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) + return tlsBackend->isTlsNamedCurve(id); + + return false; +} + /*! \fn bool QSslEllipticCurve::operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs) @@ -154,7 +160,7 @@ QT_BEGIN_NAMESPACE */ /*! - \fn size_t qHash(QSslEllipticCurve curve, size_t seed) + \fn size_t qHash(QSslEllipticCurve curve, size_t seed = 0) \since 5.5 \relates QHash diff --git a/src/network/ssl/qsslellipticcurve.h b/src/network/ssl/qsslellipticcurve.h index 2906891fdc..0585ffbd0e 100644 --- a/src/network/ssl/qsslellipticcurve.h +++ b/src/network/ssl/qsslellipticcurve.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2014 Governikus GmbH & Co. KG. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSSLELLIPTICCURVE_H #define QSSLELLIPTICCURVE_H @@ -82,7 +46,6 @@ private: friend class QSslContext; friend class QSslSocketPrivate; - friend class QSslSocketBackendPrivate; }; Q_DECLARE_TYPEINFO(QSslEllipticCurve, Q_PRIMITIVE_TYPE); @@ -97,6 +60,6 @@ Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, QSslEllipticCurve curve); QT_END_NAMESPACE -Q_DECLARE_METATYPE(QSslEllipticCurve) +QT_DECL_METATYPE_EXTERN(QSslEllipticCurve, Q_NETWORK_EXPORT) #endif // QSSLELLIPTICCURVE_H diff --git a/src/network/ssl/qsslellipticcurve_dummy.cpp b/src/network/ssl/qsslellipticcurve_dummy.cpp deleted file mode 100644 index 1313e06875..0000000000 --- a/src/network/ssl/qsslellipticcurve_dummy.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#include "qsslellipticcurve.h" - -QT_BEGIN_NAMESPACE - -QString QSslEllipticCurve::shortName() const -{ - return QString(); -} - -QString QSslEllipticCurve::longName() const -{ - return QString(); -} - -QSslEllipticCurve QSslEllipticCurve::fromShortName(const QString &name) -{ - Q_UNUSED(name); - return QSslEllipticCurve(); -} - -QSslEllipticCurve QSslEllipticCurve::fromLongName(const QString &name) -{ - Q_UNUSED(name); - return QSslEllipticCurve(); -} - -bool QSslEllipticCurve::isTlsNamedCurve() const noexcept -{ - return false; -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslellipticcurve_openssl.cpp b/src/network/ssl/qsslellipticcurve_openssl.cpp deleted file mode 100644 index bb7ad66bd2..0000000000 --- a/src/network/ssl/qsslellipticcurve_openssl.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/**************************************************************************** -** -** 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 "qsslellipticcurve.h" -#include "qsslsocket_p.h" -#include "qsslsocket_openssl_symbols_p.h" - -#include <openssl/ssl.h> -#include <openssl/obj_mac.h> - -#include <algorithm> - -QT_BEGIN_NAMESPACE - -QString QSslEllipticCurve::shortName() const -{ - QString result; -#ifndef OPENSSL_NO_EC - if (id != 0) - result = QString::fromLatin1(q_OBJ_nid2sn(id)); -#endif - return result; -} - -QString QSslEllipticCurve::longName() const -{ - QString result; -#ifndef OPENSSL_NO_EC - if (id != 0) - result = QString::fromLatin1(q_OBJ_nid2ln(id)); -#endif - return result; -} - -QSslEllipticCurve QSslEllipticCurve::fromShortName(const QString &name) -{ - if (name.isEmpty()) - return QSslEllipticCurve(); - - QSslSocketPrivate::ensureInitialized(); - - QSslEllipticCurve result; - -#ifndef OPENSSL_NO_EC - - const QByteArray curveNameLatin1 = name.toLatin1(); - int nid = q_OBJ_sn2nid(curveNameLatin1.data()); - - if (nid == 0) - nid = q_EC_curve_nist2nid(curveNameLatin1.data()); - - result.id = nid; - -#endif // !OPENSSL_NO_EC - - return result; -} - -QSslEllipticCurve QSslEllipticCurve::fromLongName(const QString &name) -{ - if (name.isEmpty()) - return QSslEllipticCurve(); - - QSslSocketPrivate::ensureInitialized(); - - QSslEllipticCurve result; - -#ifndef OPENSSL_NO_EC - const QByteArray curveNameLatin1 = name.toLatin1(); - - int nid = q_OBJ_ln2nid(curveNameLatin1.data()); - result.id = nid; -#endif - - return result; -} - - -// The brainpool curve NIDs (RFC 7027) have been introduced in OpenSSL 1.0.2, -// redefine them here to make Qt compile with previous versions of OpenSSL -// (yet correctly recognize them as TLS named curves). -// See crypto/objects/obj_mac.h -#ifndef NID_brainpoolP256r1 -#define NID_brainpoolP256r1 927 -#endif - -#ifndef NID_brainpoolP384r1 -#define NID_brainpoolP384r1 931 -#endif - -#ifndef NID_brainpoolP512r1 -#define NID_brainpoolP512r1 933 -#endif - -// 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 -}; - -static const size_t tlsNamedCurveNIDCount = sizeof(tlsNamedCurveNIDs) / sizeof(tlsNamedCurveNIDs[0]); - -bool QSslEllipticCurve::isTlsNamedCurve() const noexcept -{ - const int * const tlsNamedCurveNIDsEnd = tlsNamedCurveNIDs + tlsNamedCurveNIDCount; - return std::find(tlsNamedCurveNIDs, tlsNamedCurveNIDsEnd, id) != tlsNamedCurveNIDsEnd; -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslerror.cpp b/src/network/ssl/qsslerror.cpp index 5e935adf09..241e6291ac 100644 --- a/src/network/ssl/qsslerror.cpp +++ b/src/network/ssl/qsslerror.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /*! @@ -111,6 +75,16 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_SSL +QT_IMPL_METATYPE_EXTERN_TAGGED(QList<QSslError>, QList_QSslError) +#endif + + +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) +// Avoid an ABI break due to the QScopedPointer->std::unique_ptr change +static_assert(sizeof(QScopedPointer<QSslErrorPrivate>) == sizeof(std::unique_ptr<QSslErrorPrivate>)); +#endif + class QSslErrorPrivate { public: @@ -163,7 +137,7 @@ QSslError::QSslError(SslError error, const QSslCertificate &certificate) QSslError::QSslError(const QSslError &other) : d(new QSslErrorPrivate) { - *d.data() = *other.d.data(); + *d.get() = *other.d.get(); } /*! @@ -180,7 +154,7 @@ QSslError::~QSslError() */ QSslError &QSslError::operator=(const QSslError &other) { - *d.data() = *other.d.data(); + *d.get() = *other.d.get(); return *this; } @@ -385,3 +359,5 @@ QDebug operator<<(QDebug debug, const QSslError::SslError &error) #endif QT_END_NAMESPACE + +#include "moc_qsslerror.cpp" diff --git a/src/network/ssl/qsslerror.h b/src/network/ssl/qsslerror.h index f135dd10b7..d82b086d39 100644 --- a/src/network/ssl/qsslerror.h +++ b/src/network/ssl/qsslerror.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSSLERROR_H @@ -45,6 +9,8 @@ #include <QtCore/qvariant.h> #include <QtNetwork/qsslcertificate.h> +#include <memory> + QT_BEGIN_NAMESPACE @@ -106,7 +72,7 @@ public: QSslError(const QSslError &other); void swap(QSslError &other) noexcept - { qSwap(d, other.d); } + { d.swap(other.d); } ~QSslError(); QSslError &operator=(QSslError &&other) noexcept { swap(other); return *this; } @@ -120,7 +86,8 @@ public: QSslCertificate certificate() const; private: - QScopedPointer<QSslErrorPrivate> d; + // ### Qt 7: make QSslError implicitly shared + std::unique_ptr<QSslErrorPrivate> d; }; Q_DECLARE_SHARED(QSslError) @@ -138,7 +105,7 @@ class Q_NETWORK_EXPORT QSslError {}; // dummy class so that moc has a complete t QT_END_NAMESPACE #ifndef QT_NO_SSL -Q_DECLARE_METATYPE(QList<QSslError>) +QT_DECL_METATYPE_EXTERN_TAGGED(QList<QSslError>, QList_QSslError, Q_NETWORK_EXPORT) #endif #endif diff --git a/src/network/ssl/qsslkey.h b/src/network/ssl/qsslkey.h index 9e70554716..decfc4b5a1 100644 --- a/src/network/ssl/qsslkey.h +++ b/src/network/ssl/qsslkey.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSSLKEY_H @@ -44,7 +8,7 @@ #include <QtNetwork/qtnetworkglobal.h> #include <QtCore/qnamespace.h> #include <QtCore/qbytearray.h> -#include <QtCore/qsharedpointer.h> +#include <QtCore/qshareddata.h> #include <QtNetwork/qssl.h> QT_BEGIN_NAMESPACE @@ -74,7 +38,7 @@ public: QSslKey &operator=(const QSslKey &other); ~QSslKey(); - void swap(QSslKey &other) noexcept { qSwap(d, other.d); } + void swap(QSslKey &other) noexcept { d.swap(other.d); } bool isNull() const; void clear(); @@ -84,6 +48,7 @@ public: QSsl::KeyAlgorithm algorithm() const; QByteArray toPem(const QByteArray &passPhrase = QByteArray()) const; + // ### Qt 7: drop passPhrase QByteArray toDer(const QByteArray &passPhrase = QByteArray()) const; Qt::HANDLE handle() const; @@ -93,8 +58,7 @@ public: private: QExplicitlySharedDataPointer<QSslKeyPrivate> d; - friend class QSslCertificate; - friend class QSslSocketBackendPrivate; + friend class QTlsBackend; }; Q_DECLARE_SHARED(QSslKey) diff --git a/src/network/ssl/qsslkey_mac.cpp b/src/network/ssl/qsslkey_mac.cpp deleted file mode 100644 index 814fe1c4bc..0000000000 --- a/src/network/ssl/qsslkey_mac.cpp +++ /dev/null @@ -1,99 +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 "qsslkey.h" -#include "qsslkey_p.h" - -#include <CommonCrypto/CommonCrypto.h> - -#include <cstddef> - -QT_BEGIN_NAMESPACE - -static QByteArray wrapCCCrypt(CCOperation ccOp, - QSslKeyPrivate::Cipher cipher, - const QByteArray &data, - const QByteArray &key, const QByteArray &iv) -{ - int blockSize; - CCAlgorithm ccAlgorithm; - switch (cipher) { - case QSslKeyPrivate::DesCbc: - blockSize = kCCBlockSizeDES; - ccAlgorithm = kCCAlgorithmDES; - break; - case QSslKeyPrivate::DesEde3Cbc: - blockSize = kCCBlockSize3DES; - ccAlgorithm = kCCAlgorithm3DES; - break; - case QSslKeyPrivate::Rc2Cbc: - blockSize = kCCBlockSizeRC2; - ccAlgorithm = kCCAlgorithmRC2; - break; - case QSslKeyPrivate::Aes128Cbc: - case QSslKeyPrivate::Aes192Cbc: - case QSslKeyPrivate::Aes256Cbc: - blockSize = kCCBlockSizeAES128; - ccAlgorithm = kCCAlgorithmAES; - break; - } - 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 QByteArray(); -} - -QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv) -{ - return wrapCCCrypt(kCCDecrypt, cipher, data, key, iv); -} - -QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv) -{ - return wrapCCCrypt(kCCEncrypt, cipher, data, key, iv); -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslkey_openssl.cpp b/src/network/ssl/qsslkey_openssl.cpp deleted file mode 100644 index 43cb8c6de8..0000000000 --- a/src/network/ssl/qsslkey_openssl.cpp +++ /dev/null @@ -1,383 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** 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 "qsslkey.h" -#include "qsslkey_p.h" -#include "qsslsocket_openssl_symbols_p.h" -#include "qsslsocket.h" -#include "qsslsocket_p.h" - -#include <QtCore/qatomic.h> -#include <QtCore/qbytearray.h> -#include <QtCore/qiodevice.h> -#ifndef QT_NO_DEBUG_STREAM -#include <QtCore/qdebug.h> -#endif - -QT_BEGIN_NAMESPACE - -void QSslKeyPrivate::clear(bool deep) -{ - isNull = true; - if (!QSslSocket::supportsSsl()) - return; - 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; - } -} - -bool QSslKeyPrivate::fromEVP_PKEY(EVP_PKEY *pkey) -{ - if (pkey == nullptr) - return false; - - const int keyType = q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey)); - if (keyType == EVP_PKEY_RSA) { - isNull = false; - algorithm = QSsl::Rsa; - type = QSsl::PrivateKey; - rsa = q_EVP_PKEY_get1_RSA(pkey); - return true; - } else if (keyType == EVP_PKEY_DSA) { - isNull = false; - algorithm = QSsl::Dsa; - type = QSsl::PrivateKey; - dsa = q_EVP_PKEY_get1_DSA(pkey); - return true; - } else if (keyType == EVP_PKEY_DH) { - isNull = false; - algorithm = QSsl::Dh; - type = QSsl::PrivateKey; - dh = q_EVP_PKEY_get1_DH(pkey); - return true; - } -#ifndef OPENSSL_NO_EC - else if (keyType == EVP_PKEY_EC) { - isNull = false; - algorithm = QSsl::Ec; - type = QSsl::PrivateKey; - ec = q_EVP_PKEY_get1_EC_KEY(pkey); - return true; - } -#endif - else { - // 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; -} - -void QSslKeyPrivate::decodeDer(const QByteArray &der, const QByteArray &passPhrase, bool deepClear) -{ - QMap<QByteArray, QByteArray> headers; - decodePem(pemFromDer(der, headers), passPhrase, deepClear); -} - -void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase, - bool deepClear) -{ - if (pem.isEmpty()) - return; - - clear(deepClear); - - if (!QSslSocket::supportsSsl()) - return; - - BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size()); - if (!bio) - return; - - void *phrase = const_cast<char *>(passPhrase.constData()); - - 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) - isNull = 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) - isNull = 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) - isNull = 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) - isNull = false; -#endif - } - - q_BIO_free(bio); -} - -int QSslKeyPrivate::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 QSslKeyPrivate::toPem(const QByteArray &passPhrase) const -{ - if (!QSslSocket::supportsSsl() || isNull || algorithm == QSsl::Opaque) - return QByteArray(); - - // ### the cipher should be selectable in the API: - const EVP_CIPHER *cipher = nullptr; - if (type == QSsl::PrivateKey && !passPhrase.isEmpty()) { -#ifndef OPENSSL_NO_DES - cipher = q_EVP_des_ede3_cbc(); -#else - return QByteArray(); -#endif - } - - BIO *bio = q_BIO_new(q_BIO_s_mem()); - if (!bio) - return QByteArray(); - - 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; - long size = q_BIO_get_mem_data(bio, &data); - pem = QByteArray(data, size); - } - q_BIO_free(bio); - return pem; -} - -Qt::HANDLE QSslKeyPrivate::handle() const -{ - switch (algorithm) { - 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); - } -} - -static QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv, int enc) -{ - const EVP_CIPHER* type = nullptr; - int i = 0, len = 0; - - switch (cipher) { - case QSslKeyPrivate::DesCbc: -#ifndef OPENSSL_NO_DES - type = q_EVP_des_cbc(); -#endif - break; - case QSslKeyPrivate::DesEde3Cbc: -#ifndef OPENSSL_NO_DES - type = q_EVP_des_ede3_cbc(); -#endif - break; - case QSslKeyPrivate::Rc2Cbc: -#ifndef OPENSSL_NO_RC2 - type = q_EVP_rc2_cbc(); -#endif - break; - case QSslKeyPrivate::Aes128Cbc: - type = q_EVP_aes_128_cbc(); - break; - case QSslKeyPrivate::Aes192Cbc: - type = q_EVP_aes_192_cbc(); - break; - case QSslKeyPrivate::Aes256Cbc: - type = q_EVP_aes_256_cbc(); - break; - } - - if (type == nullptr) - return QByteArray(); - - 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 == QSslKeyPrivate::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 QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv) -{ - return doCrypt(cipher, data, key, iv, 0); -} - -QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv) -{ - return doCrypt(cipher, data, key, iv, 1); -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslkey_p.cpp b/src/network/ssl/qsslkey_p.cpp index ce12f49989..55cb2b0436 100644 --- a/src/network/ssl/qsslkey_p.cpp +++ b/src/network/ssl/qsslkey_p.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /*! @@ -54,14 +18,12 @@ \sa QSslSocket, QSslCertificate, QSslCipher */ +#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> #include <QtCore/qbytearray.h> @@ -73,11 +35,6 @@ QT_BEGIN_NAMESPACE /*! - \fn void QSslKeyPrivate::clear(bool deep) - \internal - */ - -/*! \fn void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase, bool deepClear) \internal @@ -94,210 +51,57 @@ QT_BEGIN_NAMESPACE */ /*! - Constructs a null key. - - \sa isNull() -*/ -QSslKey::QSslKey() - : d(new QSslKeyPrivate) -{ -} - -/*! \internal */ -QByteArray QSslKeyPrivate::pemHeader() const +QSslKeyPrivate::QSslKeyPrivate() { - if (type == QSsl::PublicKey) - return QByteArrayLiteral("-----BEGIN PUBLIC KEY-----"); - else if (algorithm == QSsl::Rsa) - return QByteArrayLiteral("-----BEGIN RSA PRIVATE KEY-----"); - else if (algorithm == QSsl::Dsa) - return QByteArrayLiteral("-----BEGIN DSA PRIVATE KEY-----"); - else if (algorithm == QSsl::Ec) - return QByteArrayLiteral("-----BEGIN EC PRIVATE KEY-----"); - else if (algorithm == QSsl::Dh) - return QByteArrayLiteral("-----BEGIN PRIVATE KEY-----"); - - Q_UNREACHABLE(); - return QByteArray(); -} - -static QByteArray pkcs8Header(bool encrypted) -{ - return encrypted - ? QByteArrayLiteral("-----BEGIN ENCRYPTED PRIVATE KEY-----") - : QByteArrayLiteral("-----BEGIN PRIVATE KEY-----"); + const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse(); + if (!tlsBackend) + return; + backend.reset(tlsBackend->createKey()); + if (backend.get()) + backend->clear(false /*not deep clear*/); + else + qCWarning(lcSsl, "Active TLS backend does not support key creation"); } /*! \internal */ -QByteArray QSslKeyPrivate::pemFooter() const +QSslKeyPrivate::~QSslKeyPrivate() { - if (type == QSsl::PublicKey) - return QByteArrayLiteral("-----END PUBLIC KEY-----"); - else if (algorithm == QSsl::Rsa) - return QByteArrayLiteral("-----END RSA PRIVATE KEY-----"); - else if (algorithm == QSsl::Dsa) - return QByteArrayLiteral("-----END DSA PRIVATE KEY-----"); - else if (algorithm == QSsl::Ec) - return QByteArrayLiteral("-----END EC PRIVATE KEY-----"); - else if (algorithm == QSsl::Dh) - return QByteArrayLiteral("-----END PRIVATE KEY-----"); - - Q_UNREACHABLE(); - return QByteArray(); + if (backend.get()) + backend->clear(true /*deep clear*/); } -static QByteArray pkcs8Footer(bool encrypted) +QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv) { - return encrypted - ? QByteArrayLiteral("-----END ENCRYPTED PRIVATE KEY-----") - : QByteArrayLiteral("-----END PRIVATE KEY-----"); -} + if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) { + const std::unique_ptr<QTlsPrivate::TlsKey> cryptor(tlsBackend->createKey()); + return cryptor->decrypt(cipher, data, key, iv); + } -/*! - \internal + return {}; +} - Returns a DER key formatted as PEM. -*/ -QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const +QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv) { - QByteArray pem(der.toBase64()); - - const int lineWidth = 64; // RFC 1421 - const int newLines = pem.size() / lineWidth; - const bool rem = pem.size() % lineWidth; - - // ### optimize - 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 (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) { + const std::unique_ptr<QTlsPrivate::TlsKey> cryptor(tlsBackend->createKey()); + return cryptor->encrypt(cipher, data, key, iv); } - if (isEncryptedPkcs8(der)) { - pem.prepend(pkcs8Header(true) + '\n' + extra); - pem.append(pkcs8Footer(true) + '\n'); -#if !QT_CONFIG(openssl) - } else if (isPkcs8) { - pem.prepend(pkcs8Header(false) + '\n' + extra); - pem.append(pkcs8Footer(false) + '\n'); -#endif - } else { - pem.prepend(pemHeader() + '\n' + extra); - pem.append(pemFooter() + '\n'); - } - - return pem; + return {}; } /*! - \internal + Constructs a null key. - Returns a PEM key formatted as DER. + \sa isNull() */ -QByteArray QSslKeyPrivate::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 -} - -bool QSslKeyPrivate::isEncryptedPkcs8(const QByteArray &der) const +QSslKey::QSslKey() + : d(new QSslKeyPrivate) { - 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); } /*! @@ -314,12 +118,12 @@ QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm, QSsl::EncodingFormat encoding, QSsl::KeyType type, const QByteArray &passPhrase) : d(new QSslKeyPrivate) { - d->type = type; - d->algorithm = algorithm; - if (encoding == QSsl::Der) - d->decodeDer(encoded, passPhrase); - else - d->decodePem(encoded, passPhrase); + if (auto *tlsKey = d->backend.get()) { + if (encoding == QSsl::Der) + tlsKey->decodeDer(type, algorithm, encoded, passPhrase, true /*deep clear*/); + else + tlsKey->decodePem(type, algorithm, encoded, passPhrase, true /*deep clear*/); + } } /*! @@ -339,12 +143,13 @@ QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::Encoding QByteArray encoded; if (device) encoded = device->readAll(); - d->type = type; - d->algorithm = algorithm; - if (encoding == QSsl::Der) - d->decodeDer(encoded, passPhrase); - else - d->decodePem(encoded, passPhrase); + + if (auto *tlsKey = d->backend.get()) { + if (encoding == QSsl::Der) + tlsKey->decodeDer(type, algorithm, encoded, passPhrase, true /*deep clear*/); + else + tlsKey->decodePem(type, algorithm, encoded, passPhrase, true /*deep clear*/); + } } /*! @@ -358,20 +163,8 @@ QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::Encoding QSslKey::QSslKey(Qt::HANDLE handle, QSsl::KeyType type) : d(new QSslKeyPrivate) { -#ifndef QT_NO_OPENSSL - EVP_PKEY *evpKey = reinterpret_cast<EVP_PKEY *>(handle); - if (!evpKey || !d->fromEVP_PKEY(evpKey)) { - d->opaque = evpKey; - d->algorithm = QSsl::Opaque; - } else { - q_EVP_PKEY_free(evpKey); - } -#else - d->opaque = handle; - d->algorithm = QSsl::Opaque; -#endif - d->type = type; - d->isNull = !d->opaque; + if (auto *tlsKey = d->backend.get()) + tlsKey->fromHandle(handle, type); } /*! @@ -433,7 +226,10 @@ QSslKey &QSslKey::operator=(const QSslKey &other) */ bool QSslKey::isNull() const { - return d->isNull; + if (const auto *tlsKey = d->backend.get()) + return tlsKey->isNull(); + + return true; } /*! @@ -451,7 +247,10 @@ void QSslKey::clear() */ int QSslKey::length() const { - return d->length(); + if (const auto *tlsKey = d->backend.get()) + return tlsKey->length(); + + return -1; } /*! @@ -459,7 +258,10 @@ int QSslKey::length() const */ QSsl::KeyType QSslKey::type() const { - return d->type; + if (const auto *tlsKey = d->backend.get()) + return tlsKey->type(); + + return QSsl::PublicKey; } /*! @@ -467,7 +269,10 @@ QSsl::KeyType QSslKey::type() const */ QSsl::KeyAlgorithm QSslKey::algorithm() const { - return d->algorithm; + if (const auto *tlsKey = d->backend.get()) + return tlsKey->algorithm(); + + return QSsl::Opaque; } /*! @@ -478,19 +283,18 @@ QSsl::KeyAlgorithm QSslKey::algorithm() const */ QByteArray QSslKey::toDer(const QByteArray &passPhrase) const { - if (d->isNull || d->algorithm == QSsl::Opaque) - return QByteArray(); + if (isNull() || algorithm() == QSsl::Opaque) + return {}; // Encrypted DER is nonsense, see QTBUG-41038. - if (d->type == QSsl::PrivateKey && !passPhrase.isEmpty()) - return QByteArray(); + if (type() == QSsl::PrivateKey && !passPhrase.isEmpty()) + return {}; -#ifndef QT_NO_OPENSSL QMap<QByteArray, QByteArray> headers; - return d->derFromPem(toPem(passPhrase), &headers); -#else - return d->derData; -#endif + if (const auto *tlsKey = d->backend.get()) + return tlsKey->derFromPem(toPem(passPhrase), &headers); + + return {}; } /*! @@ -500,7 +304,10 @@ QByteArray QSslKey::toDer(const QByteArray &passPhrase) const */ QByteArray QSslKey::toPem(const QByteArray &passPhrase) const { - return d->toPem(passPhrase); + if (const auto *tlsKey = d->backend.get()) + return tlsKey->toPem(passPhrase); + + return {}; } /*! @@ -516,7 +323,10 @@ QByteArray QSslKey::toPem(const QByteArray &passPhrase) const */ Qt::HANDLE QSslKey::handle() const { - return d->handle(); + if (d->backend.get()) + return d->backend->handle(); + + return nullptr; } /*! diff --git a/src/network/ssl/qsslkey_p.h b/src/network/ssl/qsslkey_p.h index dd1a31b0e5..d28ee5ad11 100644 --- a/src/network/ssl/qsslkey_p.h +++ b/src/network/ssl/qsslkey_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSSLKEY_OPENSSL_P_H @@ -53,83 +17,30 @@ // #include <QtNetwork/private/qtnetworkglobal_p.h> + #include "qsslkey.h" -#include "qsslsocket_p.h" // includes wincrypt.h +#include "qssl_p.h" -#ifndef QT_NO_OPENSSL -#include <openssl/rsa.h> -#include <openssl/dsa.h> -#endif +#include <memory> QT_BEGIN_NAMESPACE +namespace QTlsPrivate { +class TlsKey; +} + class QSslKeyPrivate { public: - inline QSslKeyPrivate() - : algorithm(QSsl::Opaque) - , opaque(nullptr) - { - clear(false); - } - - inline ~QSslKeyPrivate() - { clear(); } - - void clear(bool deep = true); - -#ifndef QT_NO_OPENSSL - bool fromEVP_PKEY(EVP_PKEY *pkey); -#endif - void decodeDer(const QByteArray &der, const QByteArray &passPhrase = {}, bool deepClear = true); - void decodePem(const QByteArray &pem, const QByteArray &passPhrase, bool deepClear = true); - QByteArray pemHeader() const; - QByteArray pemFooter() const; - QByteArray pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const; - QByteArray derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const; - - int length() const; - QByteArray toPem(const QByteArray &passPhrase) const; - Qt::HANDLE handle() const; - - bool isEncryptedPkcs8(const QByteArray &der) const; -#if !QT_CONFIG(openssl) - QByteArray decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase); - bool isPkcs8 = false; -#endif - - bool isNull; - QSsl::KeyType type; - QSsl::KeyAlgorithm algorithm; - - enum Cipher { - DesCbc, - DesEde3Cbc, - Rc2Cbc, - Aes128Cbc, - Aes192Cbc, - Aes256Cbc - }; + QSslKeyPrivate(); + ~QSslKeyPrivate(); - 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); + using Cipher = QTlsPrivate::Cipher; -#ifndef QT_NO_OPENSSL - union { - EVP_PKEY *opaque; - RSA *rsa; - DSA *dsa; - DH *dh; -#ifndef OPENSSL_NO_EC - EC_KEY *ec; -#endif - }; -#else - Qt::HANDLE opaque; - QByteArray derData; - int keyLength; -#endif + 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; private: diff --git a/src/network/ssl/qsslkey_qt.cpp b/src/network/ssl/qsslkey_qt.cpp deleted file mode 100644 index 3b8fada8fc..0000000000 --- a/src/network/ssl/qsslkey_qt.cpp +++ /dev/null @@ -1,785 +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 "qsslkey.h" -#include "qsslkey_p.h" -#include "qasn1element_p.h" - -#include <QtCore/qdatastream.h> -#include <QtCore/qcryptographichash.h> -#include <QtCore/QMessageAuthenticationCode> -#include <QtCore/qrandom.h> - -#include <QtNetwork/qpassworddigestor.h> - -#include <cstring> - -QT_USE_NAMESPACE - -static 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, -}; - -// 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 - -typedef QMap<QByteArray, int> OidLengthMap; -static 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; -} -Q_GLOBAL_STATIC_WITH_ARGS(OidLengthMap, oidLengthMap, (createOidMap())) - -static int curveBits(const QByteArray &oid) -{ - const int length = oidLengthMap->value(oid); - return length ? length : -1; -} - -static 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; -} - -static 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 == QSslKeyPrivate::Aes128Cbc) - return hash.result(); - - QByteArray key(hash.result()); - hash.reset(); - hash.addData(key); - hash.addData(data); - - if (cipher == QSslKeyPrivate::Aes192Cbc) - return key.append(hash.result().constData(), 8); - - return key.append(hash.result()); -} - -static 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 QSslKeyPrivate::DesCbc: - key = hash.result().left(8); - break; - case QSslKeyPrivate::DesEde3Cbc: - key = hash.result(); - hash.reset(); - hash.addData(key); - hash.addData(passPhrase); - hash.addData(iv); - key += hash.result().left(8); - break; - case QSslKeyPrivate::Rc2Cbc: - key = hash.result(); - break; - case QSslKeyPrivate::Aes128Cbc: - case QSslKeyPrivate::Aes192Cbc: - case QSslKeyPrivate::Aes256Cbc: - return deriveAesKey(cipher, passPhrase, iv); - } - return key; -} - -void QSslKeyPrivate::clear(bool deep) -{ - isNull = true; - if (deep) - std::memset(derData.data(), 0, derData.size()); - derData.clear(); - keyLength = -1; -} - -static int extractPkcs8KeyLength(const QList<QAsn1Element> &items, QSslKeyPrivate *that) -{ - Q_ASSERT(items.size() == 3); - int keyLength; - - 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(items[2].value()); - // 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; -} - -void QSslKeyPrivate::decodeDer(const QByteArray &der, const QByteArray &passPhrase, bool deepClear) -{ - 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; - isPkcs8 = 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; - isNull = false; -} - -void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase, - bool deepClear) -{ - 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; - } - - Cipher cipher; - if (dekInfo.first() == "DES-CBC") { - cipher = DesCbc; - } else if (dekInfo.first() == "DES-EDE3-CBC") { - cipher = DesEde3Cbc; - } else if (dekInfo.first() == "RC2-CBC") { - cipher = Rc2Cbc; - } else if (dekInfo.first() == "AES-128-CBC") { - cipher = Aes128Cbc; - } else if (dekInfo.first() == "AES-192-CBC") { - cipher = Aes192Cbc; - } else if (dekInfo.first() == "AES-256-CBC") { - 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(data, passPhrase, deepClear); -} - -int QSslKeyPrivate::length() const -{ - return keyLength; -} - -QByteArray QSslKeyPrivate::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)); - - 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); -} - -Qt::HANDLE QSslKeyPrivate::handle() const -{ - return opaque; -} - -// Maps OIDs to the encryption cipher they specify -static 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() : initialized(false) - {} - EncryptionData(QSslKeyPrivate::Cipher cipher, QByteArray key, QByteArray iv) - : initialized(true), cipher(cipher), key(key), iv(iv) - {} - bool initialized; - QSslKeyPrivate::Cipher cipher; - QByteArray key; - QByteArray iv; -}; - -static 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 bugreport 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 -static 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} -}; - -static 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}; -} - -QByteArray QSslKeyPrivate::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(); -} diff --git a/src/network/ssl/qsslkey_schannel.cpp b/src/network/ssl/qsslkey_schannel.cpp deleted file mode 100644 index 1e21d123f4..0000000000 --- a/src/network/ssl/qsslkey_schannel.cpp +++ /dev/null @@ -1,178 +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 "qssl_p.h" -#include "qsslkey.h" -#include "qsslkey_p.h" -#include "qsslcertificate_p.h" - -#include <QtCore/qbytearray.h> -#include <QtCore/qscopeguard.h> - -QT_BEGIN_NAMESPACE - -namespace { -const wchar_t *getName(QSslKeyPrivate::Cipher cipher) -{ - switch (cipher) { - case QSslKeyPrivate::Cipher::DesCbc: - return BCRYPT_DES_ALGORITHM; - case QSslKeyPrivate::Cipher::DesEde3Cbc: - return BCRYPT_3DES_ALGORITHM; - case QSslKeyPrivate::Cipher::Rc2Cbc: - return BCRYPT_RC2_ALGORITHM; - case QSslKeyPrivate::Cipher::Aes128Cbc: - case QSslKeyPrivate::Cipher::Aes192Cbc: - case QSslKeyPrivate::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(lcSsl, "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(lcSsl, "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(lcSsl, "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(lcSsl, "%s failed (%ld)!", encrypt ? "Encrypt" : "Decrypt", status); - return {}; - } - } - - return QByteArray(reinterpret_cast<const char *>(output.constData()), int(sizeNeeded)); -} -} // anonymous namespace - -QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, - const QByteArray &iv) -{ - return doCrypt(cipher, data, key, iv, false); -} - -QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, - const QByteArray &iv) -{ - return doCrypt(cipher, data, key, iv, true); -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslpresharedkeyauthenticator.cpp b/src/network/ssl/qsslpresharedkeyauthenticator.cpp index fe797ef883..0045a83bea 100644 --- a/src/network/ssl/qsslpresharedkeyauthenticator.cpp +++ b/src/network/ssl/qsslpresharedkeyauthenticator.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2014 Governikus GmbH & Co. KG. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qsslpresharedkeyauthenticator.h" #include "qsslpresharedkeyauthenticator_p.h" @@ -44,6 +8,9 @@ QT_BEGIN_NAMESPACE +QT_IMPL_METATYPE_EXTERN(QSslPreSharedKeyAuthenticator) +QT_IMPL_METATYPE_EXTERN_TAGGED(QSslPreSharedKeyAuthenticator*, QSslPreSharedKeyAuthenticator_ptr) + /*! \internal */ diff --git a/src/network/ssl/qsslpresharedkeyauthenticator.h b/src/network/ssl/qsslpresharedkeyauthenticator.h index 7b703d5020..a3912406d3 100644 --- a/src/network/ssl/qsslpresharedkeyauthenticator.h +++ b/src/network/ssl/qsslpresharedkeyauthenticator.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2014 Governikus GmbH & Co. KG. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSSLPRESHAREDKEYAUTHENTICATOR_H #define QSSLPRESHAREDKEYAUTHENTICATOR_H @@ -50,9 +14,9 @@ QT_REQUIRE_CONFIG(ssl); QT_BEGIN_NAMESPACE class QSslPreSharedKeyAuthenticatorPrivate; - class QSslPreSharedKeyAuthenticator { + Q_GADGET_EXPORT(Q_NETWORK_EXPORT) public: Q_NETWORK_EXPORT QSslPreSharedKeyAuthenticator(); Q_NETWORK_EXPORT ~QSslPreSharedKeyAuthenticator(); @@ -61,7 +25,7 @@ public: QSslPreSharedKeyAuthenticator &operator=(QSslPreSharedKeyAuthenticator &&other) noexcept { swap(other); return *this; } - void swap(QSslPreSharedKeyAuthenticator &other) noexcept { qSwap(d, other.d); } + void swap(QSslPreSharedKeyAuthenticator &other) noexcept { d.swap(other.d); } Q_NETWORK_EXPORT QByteArray identityHint() const; @@ -76,8 +40,7 @@ public: private: Q_NETWORK_EXPORT bool isEqual(const QSslPreSharedKeyAuthenticator &other) const; - friend class QSslSocketBackendPrivate; - friend class QDtlsPrivateOpenSSL; + friend class QTlsBackend; friend bool operator==(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs) { return lhs.isEqual(rhs); } @@ -92,7 +55,7 @@ Q_DECLARE_SHARED(QSslPreSharedKeyAuthenticator) QT_END_NAMESPACE -Q_DECLARE_METATYPE(QSslPreSharedKeyAuthenticator) -Q_DECLARE_METATYPE(QSslPreSharedKeyAuthenticator*) +QT_DECL_METATYPE_EXTERN(QSslPreSharedKeyAuthenticator, Q_NETWORK_EXPORT) +QT_DECL_METATYPE_EXTERN_TAGGED(QSslPreSharedKeyAuthenticator*, QSslPreSharedKeyAuthenticator_ptr, Q_NETWORK_EXPORT) #endif // QSSLPRESHAREDKEYAUTHENTICATOR_H diff --git a/src/network/ssl/qsslpresharedkeyauthenticator_p.h b/src/network/ssl/qsslpresharedkeyauthenticator_p.h index e5566c3b3c..0075579074 100644 --- a/src/network/ssl/qsslpresharedkeyauthenticator_p.h +++ b/src/network/ssl/qsslpresharedkeyauthenticator_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2014 Governikus GmbH & Co. KG. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSSLPRESHAREDKEYAUTHENTICATOR_P_H #define QSSLPRESHAREDKEYAUTHENTICATOR_P_H diff --git a/src/network/ssl/qsslserver.cpp b/src/network/ssl/qsslserver.cpp new file mode 100644 index 0000000000..40a6a6f526 --- /dev/null +++ b/src/network/ssl/qsslserver.cpp @@ -0,0 +1,412 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2016 Kurt Pattyn <pattyn.kurt@gmail.com>. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +/*! + \class QSslServer + + \ingroup network + \ingroup ssl + \inmodule QtNetwork + \since 6.4 + + \brief Implements an encrypted, secure TCP server over TLS. + + Class to use in place of QTcpServer to implement TCP server using + Transport Layer Security (TLS). + + To configure the secure handshake settings, use the applicable setter + functions on a QSslConfiguration object, and then use it as an argument + to the setSslConfiguration() function. All following incoming + connections handled will use these settings. + + To start listening to incoming connections use the listen() function + inherited from QTcpServer. Other settings can be configured by using the + setter functions inherited from the QTcpServer class. + + Connect to the signals of this class to respond to the incoming connection + attempts. They are the same as the signals on QSslSocket, but also + passes a pointer to the socket in question. + + When responding to the pendingConnectionAvailable() signal, use the + nextPendingConnection() function to fetch the next incoming connection and + take it out of the pending connection queue. The QSslSocket is a child of + the QSslServer and will be deleted when the QSslServer is deleted. It is + still a good idea to destroy the object explicitly when you are done + with it, to avoid wasting memory. + + \sa QTcpServer, QSslConfiguration, QSslSocket +*/ + +/*! + \fn void QSslServer::peerVerifyError(QSslSocket *socket, const QSslError &error) + + QSslServer can emit this signal several times during the SSL handshake, + before encryption has been established, to indicate that an error has + occurred while establishing the identity of the peer. The \a error is + usually an indication that \a socket is unable to securely identify the + peer. + + This signal provides you with an early indication when something's wrong. + By connecting to this signal, you can manually choose to tear down the + connection from inside the connected slot before the handshake has + completed. If no action is taken, QSslServer will proceed to emitting + sslErrors(). + + \sa sslErrors() +*/ + +/*! + \fn void QSslServer::sslErrors(QSslSocket *socket, const QList<QSslError> &errors); + + QSslServer emits this signal after the SSL handshake to indicate that one + or more errors have occurred while establishing the identity of the + peer. The errors are usually an indication that \a socket is unable to + securely identify the peer. Unless any action is taken, the connection + will be dropped after this signal has been emitted. + + If you want to continue connecting despite the errors that have occurred, + you must call QSslSocket::ignoreSslErrors() from inside a slot connected to + this signal. If you need to access the error list at a later point, you + can call sslHandshakeErrors(). + + \a errors contains one or more errors that prevent QSslSocket from + verifying the identity of the peer. + + \note You cannot use Qt::QueuedConnection when connecting to this signal, + or calling QSslSocket::ignoreSslErrors() will have no effect. + + \sa peerVerifyError() +*/ + +/*! + \fn void QSslServer::errorOccurred(QSslSocket *socket, QAbstractSocket::SocketError socketError) + + This signal is emitted after an error occurred during handshake. The + \a socketError parameter describes the type of error that occurred. + + The \a socket is automatically deleted after this signal is emitted if the + socket handshake has not reached encrypted state. But if the \a socket is + successfully encrypted, it is inserted into the QSslServer's pending + connections queue. When the user has called + QTcpServer::nextPendingConnection() it is the user's responsibility to + destroy the \a socket or the \a socket will not be destroyed until the + QSslServer object is destroyed. If an error occurs on a \a socket after + it has been inserted into the pending connections queue, this signal + will not be emitted, and the \a socket will not be removed or destroyed. + + \note You cannot use Qt::QueuedConnection when connecting to this signal, + or the \a socket will have been already destroyed when the signal is + handled. + + \sa QSslSocket::error(), errorString() +*/ + +/*! + \fn void QSslServer::preSharedKeyAuthenticationRequired(QSslSocket *socket, + QSslPreSharedKeyAuthenticator *authenticator) + + QSslServer emits this signal when \a socket negotiates a PSK ciphersuite, + and therefore PSK authentication is then required. + + When using PSK, the server must supply a valid identity and a valid pre + shared key, in order for the SSL handshake to continue. + Applications can provide this information in a slot connected to this + signal, by filling in the passed \a authenticator object according to their + needs. + + \note Ignoring this signal, or failing to provide the required credentials, + will cause the handshake to fail, and therefore the connection to be aborted. + + \note The \a authenticator object is owned by the \a socket and must not be + deleted by the application. + + \sa QSslPreSharedKeyAuthenticator +*/ + +/*! + \fn void QSslServer::alertSent(QSslSocket *socket, QSsl::AlertLevel level, QSsl::AlertType type, + const QString &description) + + QSslServer emits this signal if an alert message was sent from \a socket + to a peer. \a level describes if it was a warning or a fatal error. + \a type gives the code of the alert message. When a textual description + of the alert message is available, it is supplied in \a description. + + \note This signal is mostly informational and can be used for debugging + purposes, normally it does not require any actions from the application. + \note Not all backends support this functionality. + + \sa alertReceived(), QSsl::AlertLevel, QSsl::AlertType +*/ + +/*! + \fn void QSslServer::alertReceived(QSslSocket *socket, QSsl::AlertLevel level, QSsl::AlertType + type, const QString &description) + + QSslServer emits this signal if an alert message was received by the + \a socket from a peer. \a level tells if the alert was fatal or it was a + warning. \a type is the code explaining why the alert was sent. + When a textual description of the alert message is available, it is + supplied in \a description. + + \note The signal is mostly for informational and debugging purposes and does not + require any handling in the application. If the alert was fatal, underlying + backend will handle it and close the connection. + \note Not all backends support this functionality. + + \sa alertSent(), QSsl::AlertLevel, QSsl::AlertType +*/ + +/*! + \fn void QSslServer::handshakeInterruptedOnError(QSslSocket *socket, const QSslError &error) + + QSslServer emits this signal if a certificate verification error was found + by \a socket and if early error reporting was enabled in QSslConfiguration. + An application is expected to inspect the \a error and decide if it wants + to continue the handshake, or abort it and send an alert message to the + peer. The signal-slot connection must be direct. + + \sa QSslSocket::continueInterruptedHandshake(), sslErrors(), + QSslConfiguration::setHandshakeMustInterruptOnError() +*/ + +/*! + \fn void QSslServer::startedEncryptionHandshake(QSslSocket *socket) + + This signal is emitted when the client, connected to \a socket, + initiates the TLS handshake. +*/ + +#include "qsslserver.h" +#include "qsslserver_p.h" + +#include <QtNetwork/QSslSocket> +#include <QtNetwork/QSslCipher> + +QT_BEGIN_NAMESPACE + +/*! + \internal +*/ +QSslServerPrivate::QSslServerPrivate() : + sslConfiguration(QSslConfiguration::defaultConfiguration()) +{ +} + +/*! + Constructs a new QSslServer with the given \a parent. +*/ +QSslServer::QSslServer(QObject *parent) : + QTcpServer(QAbstractSocket::TcpSocket, *new QSslServerPrivate, parent) +{ +} + +/*! + Destroys the QSslServer. + + All open connections are closed. +*/ +QSslServer::~QSslServer() +{ +} + +/*! + Sets the \a sslConfiguration to use for all following incoming connections. + + This must be called before listen() to ensure that the desired + configuration was in use during all handshakes. + + \sa QSslSocket::setSslConfiguration() +*/ +void QSslServer::setSslConfiguration(const QSslConfiguration &sslConfiguration) +{ + Q_D(QSslServer); + d->sslConfiguration = sslConfiguration; +} + +/*! + Returns the current ssl configuration. +*/ +QSslConfiguration QSslServer::sslConfiguration() const +{ + const Q_D(QSslServer); + return d->sslConfiguration; +} + +/*! + Sets the \a timeout to use for all incoming handshakes, in milliseconds. + + This is relevant in the scenario where a client, whether malicious or + accidental, connects to the server but makes no attempt at communicating or + initiating a handshake. QSslServer will then automatically end the + connection after \a timeout milliseconds have elapsed. + + By default the timeout is 5000 milliseconds (5 seconds). + + \note The underlying TLS framework may have their own timeout logic now or + in the future, this function does not affect that. + + \note The \a timeout passed to this function will only apply to \e{new} + connections. If a client is already connected it will use the timeout which + was set when it connected. + + \sa handshakeTimeout() +*/ +void QSslServer::setHandshakeTimeout(int timeout) +{ + Q_D(QSslServer); + d->handshakeTimeout = timeout; +} + +/*! + Returns the currently configured handshake timeout. + + \sa setHandshakeTimeout() +*/ +int QSslServer::handshakeTimeout() const +{ + const Q_D(QSslServer); + return d->handshakeTimeout; +} + +/*! + Called when a new connection is established. + + Converts \a socket to a QSslSocket. + + \reimp +*/ +void QSslServer::incomingConnection(qintptr socket) +{ + QSslSocket *pSslSocket = new QSslSocket(this); + + pSslSocket->setSslConfiguration(sslConfiguration()); + + if (Q_LIKELY(pSslSocket->setSocketDescriptor(socket))) { + connect(pSslSocket, &QSslSocket::peerVerifyError, this, + [this, pSslSocket](const QSslError &error) { + Q_EMIT peerVerifyError(pSslSocket, error); + }); + connect(pSslSocket, &QSslSocket::sslErrors, this, + [this, pSslSocket](const QList<QSslError> &errors) { + Q_EMIT sslErrors(pSslSocket, errors); + }); + connect(pSslSocket, &QAbstractSocket::errorOccurred, this, + [this, pSslSocket](QAbstractSocket::SocketError error) { + Q_EMIT errorOccurred(pSslSocket, error); + if (!pSslSocket->isEncrypted()) + pSslSocket->deleteLater(); + }); + connect(pSslSocket, &QSslSocket::encrypted, this, [this, pSslSocket]() { + Q_D(QSslServer); + d->removeSocketData(quintptr(pSslSocket)); + pSslSocket->disconnect(this); + addPendingConnection(pSslSocket); + }); + connect(pSslSocket, &QSslSocket::preSharedKeyAuthenticationRequired, this, + [this, pSslSocket](QSslPreSharedKeyAuthenticator *authenticator) { + Q_EMIT preSharedKeyAuthenticationRequired(pSslSocket, authenticator); + }); + connect(pSslSocket, &QSslSocket::alertSent, this, + [this, pSslSocket](QSsl::AlertLevel level, QSsl::AlertType type, + const QString &description) { + Q_EMIT alertSent(pSslSocket, level, type, description); + }); + connect(pSslSocket, &QSslSocket::alertReceived, this, + [this, pSslSocket](QSsl::AlertLevel level, QSsl::AlertType type, + const QString &description) { + Q_EMIT alertReceived(pSslSocket, level, type, description); + }); + connect(pSslSocket, &QSslSocket::handshakeInterruptedOnError, this, + [this, pSslSocket](const QSslError &error) { + Q_EMIT handshakeInterruptedOnError(pSslSocket, error); + }); + + d_func()->initializeHandshakeProcess(pSslSocket); + } +} + +void QSslServerPrivate::initializeHandshakeProcess(QSslSocket *socket) +{ + Q_Q(QSslServer); + QMetaObject::Connection readyRead = QObject::connect( + socket, &QSslSocket::readyRead, q, [this]() { checkClientHelloAndContinue(); }); + + QMetaObject::Connection destroyed = + QObject::connect(socket, &QSslSocket::destroyed, q, [this](QObject *obj) { + // This cast is not safe to use since the socket is inside the + // QObject dtor, but we only use the pointer value! + removeSocketData(quintptr(obj)); + }); + auto it = socketData.emplace(quintptr(socket), readyRead, destroyed, std::make_shared<QTimer>()); + it->timeoutTimer->setSingleShot(true); + it->timeoutTimer->callOnTimeout(q, [this, socket]() { handleHandshakeTimedOut(socket); }); + it->timeoutTimer->setInterval(handshakeTimeout); + it->timeoutTimer->start(); +} + +// This function may be called while in the socket's QObject dtor, __never__ use +// the socket for anything other than a lookup! +void QSslServerPrivate::removeSocketData(quintptr socket) +{ + auto it = socketData.find(socket); + if (it != socketData.end()) { + it->disconnectSignals(); + socketData.erase(it); + } +} + +int QSslServerPrivate::totalPendingConnections() const +{ + // max pending connections is int, so this cannot exceed that + return QTcpServerPrivate::totalPendingConnections() + int(socketData.size()); +} + +void QSslServerPrivate::checkClientHelloAndContinue() +{ + Q_Q(QSslServer); + QSslSocket *socket = qobject_cast<QSslSocket *>(q->sender()); + if (Q_UNLIKELY(!socket) || socket->bytesAvailable() <= 0) + return; + + char byte = '\0'; + if (socket->peek(&byte, 1) != 1) { + socket->deleteLater(); + return; + } + + auto it = socketData.find(quintptr(socket)); + const bool foundData = it != socketData.end(); + if (foundData && it->readyReadConnection) + QObject::disconnect(std::exchange(it->readyReadConnection, {})); + + constexpr char CLIENT_HELLO = 0x16; + if (byte != CLIENT_HELLO) { + socket->disconnectFromHost(); + socket->deleteLater(); + return; + } + + // Be nice and restart the timeout timer since some progress was made + if (foundData) + it->timeoutTimer->start(); + + socket->startServerEncryption(); + Q_EMIT q->startedEncryptionHandshake(socket); +} + +void QSslServerPrivate::handleHandshakeTimedOut(QSslSocket *socket) +{ + Q_Q(QSslServer); + removeSocketData(quintptr(socket)); + socket->disconnectFromHost(); + Q_EMIT q->errorOccurred(socket, QAbstractSocket::SocketTimeoutError); + socket->deleteLater(); + if (!socketEngine->isReadNotificationEnabled() && totalPendingConnections() < maxConnections) + q->resumeAccepting(); +} + +QT_END_NAMESPACE + +#include "moc_qsslserver.cpp" diff --git a/src/network/ssl/qsslserver.h b/src/network/ssl/qsslserver.h new file mode 100644 index 0000000000..aaa0f43c35 --- /dev/null +++ b/src/network/ssl/qsslserver.h @@ -0,0 +1,61 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2016 Kurt Pattyn <pattyn.kurt@gmail.com>. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QSSLSERVER_H +#define QSSLSERVER_H + +#include <QtNetwork/QTcpServer> + +QT_REQUIRE_CONFIG(ssl); + +#include <QtNetwork/QSslError> +#include <QtNetwork/QSslConfiguration> +#include <QtNetwork/QSslPreSharedKeyAuthenticator> +#include <QtNetwork/QSslSocket> + +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE + +class QSslSocket; +class QSslServerPrivate; + +class Q_NETWORK_EXPORT QSslServer : public QTcpServer +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(QSslServer) + +public: + explicit QSslServer(QObject *parent = nullptr); + ~QSslServer() override; + + void setSslConfiguration(const QSslConfiguration &sslConfiguration); + QSslConfiguration sslConfiguration() const; + + void setHandshakeTimeout(int timeout); + int handshakeTimeout() const; + +Q_SIGNALS: + void sslErrors(QSslSocket *socket, const QList<QSslError> &errors); + void peerVerifyError(QSslSocket *socket, const QSslError &error); + void errorOccurred(QSslSocket *socket, QAbstractSocket::SocketError error); + void preSharedKeyAuthenticationRequired(QSslSocket *socket, + QSslPreSharedKeyAuthenticator *authenticator); + void alertSent(QSslSocket *socket, QSsl::AlertLevel level, + QSsl::AlertType type, const QString &description); + void alertReceived(QSslSocket *socket, QSsl::AlertLevel level, + QSsl::AlertType type, const QString &description); + void handshakeInterruptedOnError(QSslSocket *socket, const QSslError &error); + void startedEncryptionHandshake(QSslSocket *socket); + +protected: + void incomingConnection(qintptr socket) override; + +private: + Q_DECLARE_PRIVATE(QSslServer) +}; + +QT_END_NAMESPACE + +#endif // QSSLSERVER_H diff --git a/src/network/ssl/qsslserver_p.h b/src/network/ssl/qsslserver_p.h new file mode 100644 index 0000000000..1b90d35d48 --- /dev/null +++ b/src/network/ssl/qsslserver_p.h @@ -0,0 +1,71 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2016 Kurt Pattyn <pattyn.kurt@gmail.com>. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QSSLSERVER_P_H +#define QSSLSERVER_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/qhash.h> +#include <QtCore/qtimer.h> + +#include <QtNetwork/QSslConfiguration> +#include <QtNetwork/private/qtcpserver_p.h> +#include <utility> + +QT_BEGIN_NAMESPACE + +class Q_NETWORK_EXPORT QSslServerPrivate : public QTcpServerPrivate +{ + static constexpr int DefaultHandshakeTimeout = 5'000; // 5 seconds +public: + Q_DECLARE_PUBLIC(QSslServer) + + QSslServerPrivate(); + void checkClientHelloAndContinue(); + void initializeHandshakeProcess(QSslSocket *socket); + void removeSocketData(quintptr socket); + void handleHandshakeTimedOut(QSslSocket *socket); + int totalPendingConnections() const override; + + struct SocketData { + QMetaObject::Connection readyReadConnection; + QMetaObject::Connection destroyedConnection; + std::shared_ptr<QTimer> timeoutTimer; // shared_ptr because QHash demands copying + + SocketData(QMetaObject::Connection readyRead, QMetaObject::Connection destroyed, + std::shared_ptr<QTimer> &&timer) + : readyReadConnection(readyRead), + destroyedConnection(destroyed), + timeoutTimer(std::move(timer)) + { + } + + void disconnectSignals() + { + QObject::disconnect(std::exchange(readyReadConnection, {})); + QObject::disconnect(std::exchange(destroyedConnection, {})); + } + }; + QHash<quintptr, SocketData> socketData; + + QSslConfiguration sslConfiguration; + int handshakeTimeout = DefaultHandshakeTimeout; +}; + + +QT_END_NAMESPACE + +#endif // QSSLSERVER_P_H diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 0c231c1600..395394d432 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2014 BlackBerry Limited. All rights reserved. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only //#define QSSLSOCKET_DEBUG @@ -54,10 +18,10 @@ QSslSocket establishes a secure, encrypted TCP connection you can use for transmitting encrypted data. It can operate in both client - and server mode, and it supports modern SSL protocols, including - SSL 3 and TLS 1.2. By default, QSslSocket uses only SSL protocols + and server mode, and it supports modern TLS protocols, including + TLS 1.3. By default, QSslSocket uses only TLS protocols which are considered to be secure (QSsl::SecureProtocols), but you can - change the SSL protocol by calling setProtocol() as long as you do + change the TLS protocol by calling setProtocol() as long as you do it before the handshake has started. SSL encryption operates on top of the existing TCP stream after @@ -133,8 +97,7 @@ \list \li The socket's cryptographic cipher suite can be customized before - the handshake phase with QSslConfiguration::setCiphers() - and QSslConfiguration::setDefaultCiphers(). + the handshake phase with QSslConfiguration::setCiphers(). \li The socket's local certificate and private key can be customized before the handshake phase with setLocalCertificate() and setPrivateKey(). @@ -188,7 +151,7 @@ behavior is identical to QTcpSocket. \value SslClientMode The socket is a client-side SSL socket. - It is either alreayd encrypted, or it is in the SSL handshake + It is either already encrypted, or it is in the SSL handshake phase (see QSslSocket::isEncrypted()). \value SslServerMode The socket is a server-side SSL socket. @@ -386,16 +349,8 @@ #include "qsslcipher.h" #include "qocspresponse.h" #include "qtlsbackend_p.h" -#ifndef QT_NO_OPENSSL -#include "qsslsocket_openssl_p.h" -#endif -#ifdef QT_SECURETRANSPORT -#include "qsslsocket_mac_p.h" -#endif -#if QT_CONFIG(schannel) -#include "qsslsocket_schannel_p.h" -#endif #include "qsslconfiguration_p.h" +#include "qsslsocket_p.h" #include <QtCore/qdebug.h> #include <QtCore/qdir.h> @@ -407,6 +362,14 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + +#ifdef Q_OS_VXWORKS +constexpr auto isVxworks = true; +#else +constexpr auto isVxworks = false; +#endif + class QSslSocketGlobalData { public: @@ -433,7 +396,7 @@ Q_GLOBAL_STATIC(QSslSocketGlobalData, globalData) set to the one returned by the static method defaultCiphers(). */ QSslSocket::QSslSocket(QObject *parent) - : QTcpSocket(*new QSslSocketBackendPrivate, parent) + : QTcpSocket(*new QSslSocketPrivate, parent) { Q_D(QSslSocket); #ifdef QSSLSOCKET_DEBUG @@ -903,7 +866,8 @@ void QSslSocket::close() // On Windows, CertGetCertificateChain is probably still doing its // job, if the socket is re-used, we want to ignore its reported // root CA. - d->caToFetch = QSslCertificate{}; + if (auto *backend = d->backend.get()) + backend->cancelCAFetch(); if (!d->abortCalled && (encryptedBytesToWrite() || !d->writeBuffer.isEmpty())) flush(); @@ -1210,7 +1174,9 @@ QSsl::SslProtocol QSslSocket::sessionProtocol() const QList<QOcspResponse> QSslSocket::ocspResponses() const { Q_D(const QSslSocket); - return d->ocspResponses; + if (const auto *backend = d->backend.get()) + return backend->ocsps(); + return {}; } /*! @@ -1485,7 +1451,9 @@ bool QSslSocket::waitForDisconnected(int msecs) QList<QSslError> QSslSocket::sslHandshakeErrors() const { Q_D(const QSslSocket); - return d->sslErrors; + if (const auto *backend = d->backend.get()) + return backend->tlsErrors(); + return {}; } /*! @@ -1502,12 +1470,14 @@ bool QSslSocket::supportsSsl() \since 5.0 Returns the version number of the SSL library in use. Note that this is the version of the library in use at run-time not compile - time. If no SSL support is available then this will return an - undefined value. + time. If no SSL support is available then this will return -1. */ long QSslSocket::sslLibraryVersionNumber() { - return QSslSocketPrivate::sslLibraryVersionNumber(); + if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) + return tlsBackend->tlsLibraryVersionNumber(); + + return -1; } /*! @@ -1518,20 +1488,23 @@ long QSslSocket::sslLibraryVersionNumber() */ QString QSslSocket::sslLibraryVersionString() { - return QSslSocketPrivate::sslLibraryVersionString(); + if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) + return tlsBackend->tlsLibraryVersionString(); + return {}; } /*! \since 5.4 Returns the version number of the SSL library in use at compile - time. If no SSL support is available then this will return an - undefined value. + time. If no SSL support is available then this will return -1. \sa sslLibraryVersionNumber() */ long QSslSocket::sslLibraryBuildVersionNumber() { - return QSslSocketPrivate::sslLibraryBuildVersionNumber(); + if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) + return tlsBackend->tlsLibraryBuildVersionNumber(); + return -1; } /*! @@ -1544,7 +1517,10 @@ long QSslSocket::sslLibraryBuildVersionNumber() */ QString QSslSocket::sslLibraryBuildVersionString() { - return QSslSocketPrivate::sslLibraryBuildVersionString(); + if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) + return tlsBackend->tlsLibraryBuildVersionString(); + + return {}; } /*! @@ -1557,7 +1533,7 @@ QString QSslSocket::sslLibraryBuildVersionString() */ QList<QString> QSslSocket::availableBackends() { - return QTlsBackendFactory::availableBackendNames(); + return QTlsBackend::availableBackendNames(); } /*! @@ -1568,7 +1544,12 @@ 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. If it's not available, the Schannel backend + is implicitly selected on Windows, and Secure Transport on Darwin platforms. + Failing these, if a custom TLS backend is found, it is used. + If no other backend is found, the "certificate only" backend is selected. + For more information about TLS plugins, please see + \l {Enabling and Disabling SSL Support when Building Qt from Source}. \sa setActiveBackend(), availableBackends() */ @@ -1577,7 +1558,7 @@ QString QSslSocket::activeBackend() const QMutexLocker locker(&QSslSocketPrivate::backendMutex); if (!QSslSocketPrivate::activeBackendName.size()) - QSslSocketPrivate::activeBackendName = QTlsBackendFactory::defaultBackendName(); + QSslSocketPrivate::activeBackendName = QTlsBackend::defaultBackendName(); return QSslSocketPrivate::activeBackendName; } @@ -1603,14 +1584,14 @@ bool QSslSocket::setActiveBackend(const QString &backendName) } QMutexLocker locker(&QSslSocketPrivate::backendMutex); - if (QSslSocketPrivate::tlsBackend.get()) { + if (QSslSocketPrivate::tlsBackend) { qCWarning(lcSsl) << "Cannot set backend named" << backendName << "as active, another backend is already in use"; locker.unlock(); return activeBackend() == backendName; } - if (!QTlsBackendFactory::availableBackendNames().contains(backendName)) { + if (!QTlsBackend::availableBackendNames().contains(backendName)) { qCWarning(lcSsl) << "Cannot set unavailable backend named" << backendName << "as active"; return false; @@ -1632,7 +1613,7 @@ bool QSslSocket::setActiveBackend(const QString &backendName) */ QList<QSsl::SslProtocol> QSslSocket::supportedProtocols(const QString &backendName) { - return QTlsBackendFactory::supportedProtocols(backendName.size() ? backendName : activeBackend()); + return QTlsBackend::supportedProtocols(backendName.size() ? backendName : activeBackend()); } /*! @@ -1658,12 +1639,12 @@ bool QSslSocket::isProtocolSupported(QSsl::SslProtocol protocol, const QString & */ QList<QSsl::ImplementedClass> QSslSocket::implementedClasses(const QString &backendName) { - return QTlsBackendFactory::implementedClasses(backendName.size() ? backendName : activeBackend()); + return QTlsBackend::implementedClasses(backendName.size() ? backendName : activeBackend()); } /*! \since 6.1 - Returns true if a class \cl is implemented by the backend named \a backendName. An empty + Returns true if a class \a cl is implemented by the backend named \a backendName. An empty \a backendName is understood as a query about the currently active backend. \sa implementedClasses() @@ -1683,7 +1664,7 @@ bool QSslSocket::isClassImplemented(QSsl::ImplementedClass cl, const QString &ba */ QList<QSsl::SupportedFeature> QSslSocket::supportedFeatures(const QString &backendName) { - return QTlsBackendFactory::supportedFeatures(backendName.size() ? backendName : activeBackend()); + return QTlsBackend::supportedFeatures(backendName.size() ? backendName : activeBackend()); } /*! @@ -1857,7 +1838,8 @@ void QSslSocket::ignoreSslErrors(const QList<QSslError> &errors) void QSslSocket::continueInterruptedHandshake() { Q_D(QSslSocket); - d->handshakeInterrupted = false; + if (auto *backend = d->backend.get()) + backend->enableHandshakeContinuation(); } /*! @@ -1914,7 +1896,8 @@ void QSslSocket::disconnectFromHost() } // Make sure we don't process any signal from the CA fetcher // (Windows): - d->caToFetch = QSslCertificate{}; + if (auto *backend = d->backend.get()) + backend->cancelCAFetch(); // Perhaps emit closing() if (d->state != ClosingState) { @@ -1950,7 +1933,7 @@ qint64 QSslSocket::readData(char *data, qint64 maxlen) #endif } else { // possibly trigger another transmit() to decrypt more data from the socket - if (d->plainSocket->bytesAvailable()) + if (d->plainSocket->bytesAvailable() || d->hasUndecryptedData()) QMetaObject::invokeMethod(this, "_q_flushReadBuffer", Qt::QueuedConnection); else if (d->state != QAbstractSocket::ConnectedState) return maxlen ? qint64(-1) : qint64(0); @@ -1982,6 +1965,8 @@ qint64 QSslSocket::writeData(const char *data, qint64 len) return len; } +bool QSslSocketPrivate::s_loadRootCertsOnDemand = false; + /*! \internal */ @@ -1990,7 +1975,6 @@ QSslSocketPrivate::QSslSocketPrivate() , mode(QSslSocket::UnencryptedMode) , autoStartHandshake(false) , connectionEncrypted(false) - , shutdown(false) , ignoreAllSslErrors(false) , readyReadEmittedPointer(nullptr) , allowRootCertOnDemandLoading(true) @@ -1999,6 +1983,21 @@ QSslSocketPrivate::QSslSocketPrivate() , flushTriggered(false) { QSslConfigurationPrivate::deepCopyDefaultConfiguration(&configuration); + // If the global configuration doesn't allow root certificates to be loaded + // on demand then we have to disable it for this socket as well. + if (!configuration.allowRootCertOnDemandLoading) + allowRootCertOnDemandLoading = false; + + const auto *tlsBackend = tlsBackendInUse(); + if (!tlsBackend) { + qCWarning(lcSsl, "No TLS backend is available"); + return; + } + backend.reset(tlsBackend->createTlsCryptograph()); + if (!backend.get()) { + qCWarning(lcSsl) << "The backend named" << tlsBackend->backendName() + << "does not support TLS"; + } } /*! @@ -2011,28 +2010,54 @@ QSslSocketPrivate::~QSslSocketPrivate() /*! \internal */ +bool QSslSocketPrivate::supportsSsl() +{ + if (const auto *tlsBackend = tlsBackendInUse()) + return tlsBackend->implementedClasses().contains(QSsl::ImplementedClass::Socket); + return false; +} + +/*! + \internal + + Declared static in QSslSocketPrivate, makes sure the SSL libraries have + been initialized. +*/ +void QSslSocketPrivate::ensureInitialized() +{ + if (!supportsSsl()) + return; + + const auto *tlsBackend = tlsBackendInUse(); + Q_ASSERT(tlsBackend); + tlsBackend->ensureInitialized(); +} + +/*! + \internal +*/ void QSslSocketPrivate::init() { + // TLSTODO: delete those data members. mode = QSslSocket::UnencryptedMode; autoStartHandshake = false; connectionEncrypted = false; ignoreAllSslErrors = false; - shutdown = false; abortCalled = false; pendingClose = false; flushTriggered = false; - ocspResponses.clear(); - systemOrSslErrorDetected = false; - // we don't want to clear the ignoreErrorsList, so - // that it is possible setting it before connecting -// ignoreErrorsList.clear(); + // We don't want to clear the ignoreErrorsList, so + // that it is possible setting it before connecting. buffer.clear(); writeBuffer.clear(); configuration.peerCertificate.clear(); configuration.peerCertificateChain.clear(); - fetchAuthorityInformation = false; - caToFetch = QSslCertificate{}; + + if (backend.get()) { + Q_ASSERT(q_ptr); + backend->init(static_cast<QSslSocket *>(q_ptr), this); + } } /*! @@ -2040,13 +2065,15 @@ void QSslSocketPrivate::init() */ bool QSslSocketPrivate::verifyProtocolSupported(const char *where) { - QLatin1String protocolName("DTLS"); + auto protocolName = "DTLS"_L1; switch (configuration.protocol) { case QSsl::UnknownProtocol: // UnknownProtocol, according to our docs, is for cipher whose protocol is unknown. // Should not be used when configuring QSslSocket. - protocolName = QLatin1String("UnknownProtocol"); + protocolName = "UnknownProtocol"_L1; Q_FALLTHROUGH(); +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED case QSsl::DtlsV1_0: case QSsl::DtlsV1_2: case QSsl::DtlsV1_0OrLater: @@ -2055,6 +2082,7 @@ bool QSslSocketPrivate::verifyProtocolSupported(const char *where) setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError, QSslSocket::tr("Attempted to use an unsupported protocol.")); return false; +QT_WARNING_POP default: return true; } @@ -2103,7 +2131,35 @@ void QSslSocketPrivate::setDefaultSupportedCiphers(const QList<QSslCipher> &ciph /*! \internal */ -void q_setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers) +void QSslSocketPrivate::resetDefaultEllipticCurves() +{ + const auto *tlsBackend = tlsBackendInUse(); + if (!tlsBackend) + return; + + auto ids = tlsBackend->ellipticCurvesIds(); + if (!ids.size()) + return; + + QList<QSslEllipticCurve> curves; + curves.reserve(ids.size()); + for (int id : ids) { + QSslEllipticCurve curve; + curve.id = id; + curves.append(curve); + } + + // Set the list of supported ECs, but not the list + // of *default* ECs. OpenSSL doesn't like forcing an EC for the wrong + // ciphersuite, so don't try it -- leave the empty list to mean + // "the implementation will choose the most suitable one". + setDefaultSupportedEllipticCurves(curves); +} + +/*! + \internal +*/ +void QSslSocketPrivate::setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers) { QMutexLocker locker(&globalData()->mutex); globalData()->dtlsConfig.detach(); @@ -2113,7 +2169,7 @@ void q_setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers) /*! \internal */ -QList<QSslCipher> q_getDefaultDtlsCiphers() +QList<QSslCipher> QSslSocketPrivate::defaultDtlsCiphers() { QSslSocketPrivate::ensureInitialized(); QMutexLocker locker(&globalData()->mutex); @@ -2239,6 +2295,7 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri ptr->sessionProtocol = global->sessionProtocol; ptr->ciphers = global->ciphers; ptr->caCertificates = global->caCertificates; + ptr->allowRootCertOnDemandLoading = global->allowRootCertOnDemandLoading; ptr->protocol = global->protocol; ptr->peerVerifyMode = global->peerVerifyMode; ptr->peerVerifyDepth = global->peerVerifyDepth; @@ -2360,6 +2417,11 @@ bool QSslSocketPrivate::isPaused() const return paused; } +void QSslSocketPrivate::setPaused(bool p) +{ + paused = p; +} + bool QSslSocketPrivate::bind(const QHostAddress &address, quint16 port, QAbstractSocket::BindMode mode) { // this function is called from QAbstractSocket::bind @@ -2590,6 +2652,7 @@ void QSslSocketPrivate::_q_resumeImplementation() if (verifyErrorsHaveBeenIgnored()) { continueHandshake(); } else { + const auto sslErrors = backend->tlsErrors(); Q_ASSERT(!sslErrors.isEmpty()); setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, sslErrors.constFirst().errorString()); plainSocket->disconnectFromHost(); @@ -2604,13 +2667,16 @@ void QSslSocketPrivate::_q_resumeImplementation() */ bool QSslSocketPrivate::verifyErrorsHaveBeenIgnored() { + Q_ASSERT(backend.get()); + bool doEmitSslError; if (!ignoreErrorsList.empty()) { // check whether the errors we got are all in the list of expected errors // (applies only if the method QSslSocket::ignoreSslErrors(const QList<QSslError> &errors) // was called) + const auto &sslErrors = backend->tlsErrors(); doEmitSslError = false; - for (int a = 0; a < sslErrors.count(); a++) { + for (int a = 0; a < sslErrors.size(); a++) { if (!ignoreErrorsList.contains(sslErrors.at(a))) { doEmitSslError = true; break; @@ -2628,6 +2694,91 @@ bool QSslSocketPrivate::verifyErrorsHaveBeenIgnored() /*! \internal */ +bool QSslSocketPrivate::isAutoStartingHandshake() const +{ + return autoStartHandshake; +} + +/*! + \internal +*/ +bool QSslSocketPrivate::isPendingClose() const +{ + return pendingClose; +} + +/*! + \internal +*/ +void QSslSocketPrivate::setPendingClose(bool pc) +{ + pendingClose = pc; +} + +/*! + \internal +*/ +qint64 QSslSocketPrivate::maxReadBufferSize() const +{ + return readBufferMaxSize; +} + +/*! + \internal +*/ +void QSslSocketPrivate::setMaxReadBufferSize(qint64 maxSize) +{ + readBufferMaxSize = maxSize; +} + +/*! + \internal +*/ +void QSslSocketPrivate::setEncrypted(bool enc) +{ + connectionEncrypted = enc; +} + +/*! + \internal +*/ +QIODevicePrivate::QRingBufferRef &QSslSocketPrivate::tlsWriteBuffer() +{ + return writeBuffer; +} + +/*! + \internal +*/ +QIODevicePrivate::QRingBufferRef &QSslSocketPrivate::tlsBuffer() +{ + return buffer; +} + +/*! + \internal +*/ +bool &QSslSocketPrivate::tlsEmittedBytesWritten() +{ + return emittedBytesWritten; +} + +/*! + \internal +*/ +bool *QSslSocketPrivate::readyReadPointer() +{ + return readyReadEmittedPointer; +} + +bool QSslSocketPrivate::hasUndecryptedData() const +{ + return backend.get() && backend->hasUndecryptedData(); +} + +/*! + \internal +*/ qint64 QSslSocketPrivate::peek(char *data, qint64 maxSize) { if (mode == QSslSocket::UnencryptedMode && !autoStartHandshake) { @@ -2643,9 +2794,9 @@ qint64 QSslSocketPrivate::peek(char *data, qint64 maxSize) if (r2 < 0) return (r > 0 ? r : r2); return r + r2; - } else { - return -1; } + + return -1; } else { //encrypted mode - the socket engine will read and decrypt data into the QIODevice buffer return QTcpSocketPrivate::peek(data, maxSize); @@ -2663,13 +2814,13 @@ QByteArray QSslSocketPrivate::peek(qint64 maxSize) QByteArray ret; ret.reserve(maxSize); ret.resize(buffer.peek(ret.data(), maxSize, transactionPos)); - if (ret.length() == maxSize) + if (ret.size() == maxSize) return ret; //peek at data in the plain socket if (plainSocket) - return ret + plainSocket->peek(maxSize - ret.length()); - else - return QByteArray(); + return ret + plainSocket->peek(maxSize - ret.size()); + + return QByteArray(); } else { //encrypted mode - the socket engine will read and decrypt data into the QIODevice buffer return QTcpSocketPrivate::peek(maxSize); @@ -2711,6 +2862,82 @@ bool QSslSocketPrivate::flush() /*! \internal */ +void QSslSocketPrivate::startClientEncryption() +{ + if (backend.get()) + backend->startClientEncryption(); +} + +/*! + \internal +*/ +void QSslSocketPrivate::startServerEncryption() +{ + if (backend.get()) + backend->startServerEncryption(); +} + +/*! + \internal +*/ +void QSslSocketPrivate::transmit() +{ + if (backend.get()) + backend->transmit(); +} + +/*! + \internal +*/ +void QSslSocketPrivate::disconnectFromHost() +{ + if (backend.get()) + backend->disconnectFromHost(); +} + +/*! + \internal +*/ +void QSslSocketPrivate::disconnected() +{ + if (backend.get()) + backend->disconnected(); +} + +/*! + \internal +*/ +QSslCipher QSslSocketPrivate::sessionCipher() const +{ + if (backend.get()) + return backend->sessionCipher(); + + return {}; +} + +/*! + \internal +*/ +QSsl::SslProtocol QSslSocketPrivate::sessionProtocol() const +{ + if (backend.get()) + return backend->sessionProtocol(); + + return QSsl::UnknownProtocol; +} + +/*! + \internal +*/ +void QSslSocketPrivate::continueHandshake() +{ + if (backend.get()) + backend->continueHandshake(); +} + +/*! + \internal +*/ bool QSslSocketPrivate::rootCertOnDemandLoadingSupported() { return s_loadRootCertsOnDemand; @@ -2719,34 +2946,63 @@ bool QSslSocketPrivate::rootCertOnDemandLoadingSupported() /*! \internal */ +void QSslSocketPrivate::setRootCertOnDemandLoadingSupported(bool supported) +{ + s_loadRootCertsOnDemand = supported; +} + +/*! + \internal +*/ QList<QByteArray> QSslSocketPrivate::unixRootCertDirectories() { - return QList<QByteArray>() << "/etc/ssl/certs/" // (K)ubuntu, OpenSUSE, Mandriva ... - << "/usr/lib/ssl/certs/" // Gentoo, Mandrake - << "/usr/share/ssl/" // Centos, Redhat, SuSE - << "/usr/local/ssl/" // Normal OpenSSL Tarball - << "/var/ssl/certs/" // AIX - << "/usr/local/ssl/certs/" // Solaris - << "/etc/openssl/certs/" // BlackBerry - << "/opt/openssl/certs/" // HP-UX - << "/etc/ssl/"; // OpenBSD + const auto ba = [](const auto &cstr) constexpr { + return QByteArray::fromRawData(std::begin(cstr), std::size(cstr) - 1); + }; + static const QByteArray dirs[] = { + ba("/etc/ssl/certs/"), // (K)ubuntu, OpenSUSE, Mandriva ... + ba("/usr/lib/ssl/certs/"), // Gentoo, Mandrake + ba("/usr/share/ssl/"), // Centos, Redhat, SuSE + ba("/usr/local/ssl/"), // Normal OpenSSL Tarball + ba("/var/ssl/certs/"), // AIX + ba("/usr/local/ssl/certs/"), // Solaris + ba("/etc/openssl/certs/"), // BlackBerry + ba("/opt/openssl/certs/"), // HP-UX + ba("/etc/ssl/"), // OpenBSD + }; + QList<QByteArray> result = QList<QByteArray>::fromReadOnlyData(dirs); + if constexpr (isVxworks) { + static QByteArray vxworksCertsDir = qgetenv("VXWORKS_CERTS_DIR"); + if (!vxworksCertsDir.isEmpty()) + result.push_back(vxworksCertsDir); + } + return result; } /*! \internal */ -void QSslSocketPrivate::checkSettingSslContext(QSslSocket* socket, QSharedPointer<QSslContext> sslContext) +void QSslSocketPrivate::checkSettingSslContext(QSslSocket* socket, std::shared_ptr<QSslContext> tlsContext) { - if (socket->d_func()->sslContextPointer.isNull()) - socket->d_func()->sslContextPointer = sslContext; + if (!socket) + return; + + if (auto *backend = socket->d_func()->backend.get()) + backend->checkSettingSslContext(tlsContext); } /*! \internal */ -QSharedPointer<QSslContext> QSslSocketPrivate::sslContext(QSslSocket *socket) +std::shared_ptr<QSslContext> QSslSocketPrivate::sslContext(QSslSocket *socket) { - return (socket) ? socket->d_func()->sslContextPointer : QSharedPointer<QSslContext>(); + if (!socket) + return {}; + + if (const auto *backend = socket->d_func()->backend.get()) + return backend->sslContext(); + + return {}; } bool QSslSocketPrivate::isMatchingHostname(const QSslCertificate &cert, const QString &peerName) @@ -2786,17 +3042,17 @@ bool QSslSocketPrivate::isMatchingHostname(const QSslCertificate &cert, const QS */ bool QSslSocketPrivate::isMatchingHostname(const QString &cn, const QString &hostname) { - int wildcard = cn.indexOf(QLatin1Char('*')); + qsizetype wildcard = cn.indexOf(u'*'); // Check this is a wildcard cert, if not then just compare the strings if (wildcard < 0) - return QLatin1String(QUrl::toAce(cn)) == hostname; + return QLatin1StringView(QUrl::toAce(cn)) == hostname; - int firstCnDot = cn.indexOf(QLatin1Char('.')); - int secondCnDot = cn.indexOf(QLatin1Char('.'), firstCnDot+1); + qsizetype firstCnDot = cn.indexOf(u'.'); + qsizetype secondCnDot = cn.indexOf(u'.', firstCnDot+1); // Check at least 3 components - if ((-1 == secondCnDot) || (secondCnDot+1 >= cn.length())) + if ((-1 == secondCnDot) || (secondCnDot+1 >= cn.size())) return false; // Check * is last character of 1st component (ie. there's a following .) @@ -2804,12 +3060,12 @@ bool QSslSocketPrivate::isMatchingHostname(const QString &cn, const QString &hos return false; // Check only one star - if (cn.lastIndexOf(QLatin1Char('*')) != wildcard) + if (cn.lastIndexOf(u'*') != wildcard) return false; // Reject wildcard character embedded within the A-labels or U-labels of an internationalized // domain name (RFC6125 section 7.2) - if (cn.startsWith(QLatin1String("xn--"), Qt::CaseInsensitive)) + if (cn.startsWith("xn--"_L1, Qt::CaseInsensitive)) return false; // Check characters preceding * (if any) match @@ -2817,9 +3073,9 @@ bool QSslSocketPrivate::isMatchingHostname(const QString &cn, const QString &hos return false; // Check characters following first . match - int hnDot = hostname.indexOf(QLatin1Char('.')); + qsizetype hnDot = hostname.indexOf(u'.'); if (QStringView{hostname}.mid(hnDot + 1) != QStringView{cn}.mid(firstCnDot + 1) - && QStringView{hostname}.mid(hnDot + 1) != QLatin1String(QUrl::toAce(cn.mid(firstCnDot + 1)))) { + && QStringView{hostname}.mid(hnDot + 1) != QLatin1StringView(QUrl::toAce(cn.mid(firstCnDot + 1)))) { return false; } @@ -2832,6 +3088,81 @@ bool QSslSocketPrivate::isMatchingHostname(const QString &cn, const QString &hos return true; } +/*! + \internal +*/ +QTlsBackend *QSslSocketPrivate::tlsBackendInUse() +{ + const QMutexLocker locker(&backendMutex); + if (tlsBackend) + return tlsBackend; + + if (!activeBackendName.size()) + activeBackendName = QTlsBackend::defaultBackendName(); + + if (!activeBackendName.size()) { + qCWarning(lcSsl, "No functional TLS backend was found"); + return nullptr; + } + + tlsBackend = QTlsBackend::findBackend(activeBackendName); + if (tlsBackend) { + QObject::connect(tlsBackend, &QObject::destroyed, tlsBackend, [] { + const QMutexLocker locker(&backendMutex); + tlsBackend = nullptr; + }, + Qt::DirectConnection); + } + return tlsBackend; +} + +/*! + \internal +*/ +QSslSocket::SslMode QSslSocketPrivate::tlsMode() const +{ + return mode; +} + +/*! + \internal +*/ +bool QSslSocketPrivate::isRootsOnDemandAllowed() const +{ + return allowRootCertOnDemandLoading; +} + +/*! + \internal +*/ +QString QSslSocketPrivate::verificationName() const +{ + return verificationPeerName; +} + +/*! + \internal +*/ +QString QSslSocketPrivate::tlsHostName() const +{ + return hostName; +} + +QTcpSocket *QSslSocketPrivate::plainTcpSocket() const +{ + return plainSocket; +} + +/*! + \internal +*/ +QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates() +{ + if (const auto *tlsBackend = tlsBackendInUse()) + return tlsBackend->systemCaCertificates(); + return {}; +} + QT_END_NAMESPACE #include "moc_qsslsocket.cpp" diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h index 8841929eec..3ed1bc45cc 100644 --- a/src/network/ssl/qsslsocket.h +++ b/src/network/ssl/qsslsocket.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSSLSOCKET_H @@ -71,6 +35,7 @@ public: SslClientMode, SslServerMode }; + Q_ENUM(SslMode) enum PeerVerifyMode { VerifyNone, @@ -78,6 +43,7 @@ public: VerifyPeer, AutoVerifyPeer }; + Q_ENUM(PeerVerifyMode) explicit QSslSocket(QObject *parent = nullptr); ~QSslSocket(); @@ -201,6 +167,7 @@ protected: private: Q_DECLARE_PRIVATE(QSslSocket) Q_DISABLE_COPY_MOVE(QSslSocket) + Q_PRIVATE_SLOT(d_func(), void _q_connectedSlot()) Q_PRIVATE_SLOT(d_func(), void _q_hostFoundSlot()) Q_PRIVATE_SLOT(d_func(), void _q_disconnectedSlot()) @@ -214,10 +181,6 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_flushWriteBuffer()) Q_PRIVATE_SLOT(d_func(), void _q_flushReadBuffer()) Q_PRIVATE_SLOT(d_func(), void _q_resumeImplementation()) -#if defined(Q_OS_WIN) && !QT_CONFIG(schannel) - Q_PRIVATE_SLOT(d_func(), void _q_caRootLoaded(QSslCertificate,QSslCertificate)) -#endif - friend class QSslSocketBackendPrivate; }; #endif // QT_NO_SSL diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp deleted file mode 100644 index abbcf8a6ac..0000000000 --- a/src/network/ssl/qsslsocket_mac.cpp +++ /dev/null @@ -1,1625 +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 "qsslsocket_mac_p.h" -#include "qasn1element_p.h" -#include "qsslcertificate_p.h" -#include "qtlsbackend_p.h" -#include "qsslcipher_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 - -namespace -{ - -// These two classes are ad-hoc temporary solution, to be replaced -// by the real things soon. -class SecureTransportBackend : public QTlsBackend -{ -private: - QString backendName() const override - { - return QTlsBackendFactory::builtinBackendNames[QTlsBackendFactory::nameIndexSecureTransport]; - } -}; - -class SecureTransportBackendFactory : public QTlsBackendFactory -{ -private: - QString backendName() const override - { - return QTlsBackendFactory::builtinBackendNames[QTlsBackendFactory::nameIndexSecureTransport]; - } - QTlsBackend *create() const override - { - return new SecureTransportBackend; - } - - QList<QSsl::SslProtocol> supportedProtocols() const override - { - 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> supportedFeatures() const override - { - QList<QSsl::SupportedFeature> features; - features << QSsl::SupportedFeature::ClientSideAlpn; - - return features; - } - - QList<QSsl::ImplementedClass> implementedClasses() const override - { - QList<QSsl::ImplementedClass> classes; - classes << QSsl::ImplementedClass::Socket; - classes << QSsl::ImplementedClass::Certificate; - classes << QSsl::ImplementedClass::Key; - - return classes; - } -}; - -Q_GLOBAL_STATIC(SecureTransportBackendFactory, factory) - -#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(lcSsl) << "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(lcSsl) << "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(lcSsl) << "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(lcSsl) << "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(lcSsl) << "SecKeychainSettings: failed to disable lock on sleep"; - } - -#ifdef QSSLSOCKET_DEBUG - if (keychain) { - qCDebug(lcSsl) << "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 - -} // unnamed namespace - -static 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; -} - -static void qt_releaseSecureTransportContext(SSLContextRef context) -{ - if (context) - CFRelease(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; -} - -Q_GLOBAL_STATIC(QRecursiveMutex, qt_securetransport_mutex) - -//#define QSSLSOCKET_DEBUG - -bool QSslSocketPrivate::s_libraryLoaded = false; -bool QSslSocketPrivate::s_loadedCiphersAndCerts = false; -bool QSslSocketPrivate::s_loadRootCertsOnDemand = false; - - -#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 QSslSocketBackendPrivate::ReadCallback(QSslSocketBackendPrivate *socket, - char *data, size_t *dataLength) -{ - Q_ASSERT(socket); - Q_ASSERT(data); - Q_ASSERT(dataLength); - - QTcpSocket *plainSocket = socket->plainSocket; - 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(lcSsl) << 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 QSslSocketBackendPrivate::WriteCallback(QSslSocketBackendPrivate *socket, - const char *data, size_t *dataLength) -{ - Q_ASSERT(socket); - Q_ASSERT(data); - Q_ASSERT(dataLength); - - QTcpSocket *plainSocket = socket->plainSocket; - Q_ASSERT(plainSocket); - - const qint64 bytes = plainSocket->write(data, *dataLength); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "write" << bytes; -#endif - if (bytes < 0) { - *dataLength = 0; - return errSecIO; - } - - const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : errSecSuccess; - *dataLength = bytes; - - return err; -} - -void QSslSocketPrivate::ensureInitialized() -{ - 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 QSecureTransportContext context(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(QSslSocketBackendPrivate::QSslCipher_from_SSLCipherSuite(cfCiphers.at(i))); - if (!ciph.isNull()) { - ciphers << ciph; - if (ciph.usedBits() >= 128) - defaultCiphers << ciph; - } - } - - setDefaultSupportedCiphers(ciphers); - setDefaultCiphers(defaultCiphers); - - if (!s_loadRootCertsOnDemand) - setDefaultCaCertificates(systemCaCertificates()); - } else { - s_loadedCiphersAndCerts = false; - } - -} - -long QSslSocketPrivate::sslLibraryVersionNumber() -{ - return 0; -} - -QString QSslSocketPrivate::sslLibraryVersionString() -{ - return QLatin1String("Secure Transport, ") + QSysInfo::prettyProductName(); -} - -long QSslSocketPrivate::sslLibraryBuildVersionNumber() -{ - return 0; -} - -QString QSslSocketPrivate::sslLibraryBuildVersionString() -{ - return sslLibraryVersionString(); -} - -bool QSslSocketPrivate::supportsSsl() -{ - return true; -} - -void QSslSocketPrivate::resetDefaultCiphers() -{ - Q_UNIMPLEMENTED(); -} - -void QSslSocketPrivate::resetDefaultEllipticCurves() -{ - // No public API for this (?). - Q_UNIMPLEMENTED(); -} - -QSslSocketBackendPrivate::QSslSocketBackendPrivate() - : context(nullptr) -{ -} - -QSslSocketBackendPrivate::~QSslSocketBackendPrivate() -{ - destroySslContext(); -} - -void QSslSocketBackendPrivate::continueHandshake() -{ -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "connection encrypted"; -#endif - Q_Q(QSslSocket); - connectionEncrypted = true; - -#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). - if (__builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) { - const auto &requestedProtocols = configuration.nextAllowedProtocols; - if (const int requestedCount = requestedProtocols.size()) { - configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNone; - configuration.nextNegotiatedProtocol.clear(); - - 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]) { - configuration.nextNegotiatedProtocol = requestedName.toLatin1(); - configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNegotiated; - break; - } - } - if (configuration.nextProtocolNegotiationStatus == QSslConfiguration::NextProtocolNegotiationNegotiated) - break; - } - } - } - } -#endif // QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE - - if (!renegotiating) - emit q->encrypted(); - - if (autoStartHandshake && pendingClose) { - pendingClose = false; - q->disconnectFromHost(); - } -} - -void QSslSocketBackendPrivate::disconnected() -{ - if (plainSocket->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 QSslSocketBackendPrivate::disconnectFromHost() -{ - if (context) { - if (!shutdown) { - SSLClose(context); - shutdown = true; - } - } - plainSocket->disconnectFromHost(); -} - -QSslCipher QSslSocketBackendPrivate::sessionCipher() const -{ - SSLCipherSuite cipher = 0; - if (context && SSLGetNegotiatedCipher(context, &cipher) == errSecSuccess) - return QSslCipher_from_SSLCipherSuite(cipher); - - return QSslCipher(); -} - -QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const -{ - if (!context) - return QSsl::UnknownProtocol; - - SSLProtocol protocol = kSSLProtocolUnknown; - const OSStatus err = SSLGetNegotiatedProtocolVersion(context, &protocol); - if (err != errSecSuccess) { - qCWarning(lcSsl) << "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 QSslSocketBackendPrivate::startClientEncryption() -{ - if (!initSslContext()) { - // Error description/code were set, 'error' emitted - // by initSslContext, but OpenSSL socket also sets error - // emits a signal twice, so ... - setErrorAndEmit(QAbstractSocket::SslInternalError, QStringLiteral("Unable to init SSL Context")); - return; - } - - startHandshake(); -} - -void QSslSocketBackendPrivate::startServerEncryption() -{ - if (!initSslContext()) { - // Error description/code were set, 'error' emitted - // by initSslContext, but OpenSSL socket also sets error - // emits a signal twice, so ... - setErrorAndEmit(QAbstractSocket::SslInternalError, QStringLiteral("Unable to init SSL Context")); - return; - } - - startHandshake(); -} - -void QSslSocketBackendPrivate::transmit() -{ - Q_Q(QSslSocket); - - // 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(); - - 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(lcSsl) << plainSocket << "SSLWrite returned" << err; -#endif - if (err != errSecSuccess && err != errSSLWouldBlock) { - 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. - if (!emittedBytesWritten) { - emittedBytesWritten = true; - emit q->bytesWritten(totalBytesWritten); - emittedBytesWritten = false; - } - emit q->channelBytesWritten(0, totalBytesWritten); - } - } - - 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(lcSsl) << plainSocket << "SSLRead returned" << err; -#endif - if (err == errSSLClosedGraceful) { - shutdown = true; // the other side shut down, make sure we do not send shutdown ourselves - setErrorAndEmit(QAbstractSocket::RemoteHostClosedError, - QSslSocket::tr("The TLS/SSL connection has been closed")); - break; - } else if (err != errSecSuccess && err != errSSLWouldBlock) { - setErrorAndEmit(QAbstractSocket::SslInternalError, - QStringLiteral("SSLRead failed: %1").arg(err)); - break; - } - - if (err == errSSLWouldBlock && renegotiating) { - startHandshake(); - break; - } - - if (readBytes) { - buffer.append(data.constData(), readBytes); - if (readyReadEmittedPointer) - *readyReadEmittedPointer = true; - emit q->readyRead(); - emit q->channelReadyRead(0); - } - - if (err == errSSLWouldBlock) - break; - } - } -} - - -QList<QSslError> (QSslSocketBackendPrivate::verify)(QList<QSslCertificate> certificateChain, const QString &hostName) -{ - Q_UNIMPLEMENTED(); - Q_UNUSED(certificateChain); - Q_UNUSED(hostName); - - QList<QSslError> errors; - errors << QSslError(QSslError::UnspecifiedError); - - return errors; -} - -bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device, - QSslKey *key, QSslCertificate *cert, - QList<QSslCertificate> *caCertificates, - const QByteArray &passPhrase) -{ - Q_UNIMPLEMENTED(); - Q_UNUSED(device); - Q_UNUSED(key); - Q_UNUSED(cert); - Q_UNUSED(caCertificates); - Q_UNUSED(passPhrase); - return false; -} - -QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSLCipherSuite(SSLCipherSuite cipher) -{ - QSslCipher ciph; - 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: - ciph.d->name = QLatin1String("AES128-SHA"); - break; - case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: - ciph.d->name = QLatin1String("DHE-RSA-AES128-SHA"); - break; - case TLS_RSA_WITH_AES_256_CBC_SHA: - ciph.d->name = QLatin1String("AES256-SHA"); - break; - case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: - ciph.d->name = QLatin1String("DHE-RSA-AES256-SHA"); - break; - - // ECDSA addenda, RFC 4492 - case TLS_ECDH_ECDSA_WITH_NULL_SHA: - ciph.d->name = QLatin1String("ECDH-ECDSA-NULL-SHA"); - break; - case TLS_ECDH_ECDSA_WITH_RC4_128_SHA: - ciph.d->name = QLatin1String("ECDH-ECDSA-RC4-SHA"); - break; - case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: - ciph.d->name = QLatin1String("ECDH-ECDSA-DES-CBC3-SHA"); - break; - case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: - ciph.d->name = QLatin1String("ECDH-ECDSA-AES128-SHA"); - break; - case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: - ciph.d->name = QLatin1String("ECDH-ECDSA-AES256-SHA"); - break; - case TLS_ECDHE_ECDSA_WITH_NULL_SHA: - ciph.d->name = QLatin1String("ECDHE-ECDSA-NULL-SHA"); - break; - case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: - ciph.d->name = QLatin1String("ECDHE-ECDSA-RC4-SHA"); - break; - case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: - ciph.d->name = QLatin1String("ECDHE-ECDSA-DES-CBC3-SHA"); - break; - case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: - ciph.d->name = QLatin1String("ECDHE-ECDSA-AES128-SHA"); - break; - case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: - ciph.d->name = QLatin1String("ECDHE-ECDSA-AES256-SHA"); - break; - case TLS_ECDH_RSA_WITH_NULL_SHA: - ciph.d->name = QLatin1String("ECDH-RSA-NULL-SHA"); - break; - case TLS_ECDH_RSA_WITH_RC4_128_SHA: - ciph.d->name = QLatin1String("ECDH-RSA-RC4-SHA"); - break; - case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: - ciph.d->name = QLatin1String("ECDH-RSA-DES-CBC3-SHA"); - break; - case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: - ciph.d->name = QLatin1String("ECDH-RSA-AES128-SHA"); - break; - case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: - ciph.d->name = QLatin1String("ECDH-RSA-AES256-SHA"); - break; - case TLS_ECDHE_RSA_WITH_NULL_SHA: - ciph.d->name = QLatin1String("ECDHE-RSA-NULL-SHA"); - break; - case TLS_ECDHE_RSA_WITH_RC4_128_SHA: - ciph.d->name = QLatin1String("ECDHE-RSA-RC4-SHA"); - break; - case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: - ciph.d->name = QLatin1String("ECDHE-RSA-DES-CBC3-SHA"); - break; - case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: - ciph.d->name = QLatin1String("ECDHE-RSA-AES128-SHA"); - break; - case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: - ciph.d->name = QLatin1String("ECDHE-RSA-AES256-SHA"); - break; - - // TLS 1.2 addenda, RFC 5246 - case TLS_RSA_WITH_3DES_EDE_CBC_SHA: - ciph.d->name = QLatin1String("DES-CBC3-SHA"); - break; - case TLS_RSA_WITH_AES_128_CBC_SHA256: - ciph.d->name = QLatin1String("AES128-SHA256"); - break; - case TLS_RSA_WITH_AES_256_CBC_SHA256: - ciph.d->name = QLatin1String("AES256-SHA256"); - break; - case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: - ciph.d->name = QLatin1String("DHE-RSA-DES-CBC3-SHA"); - break; - case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: - ciph.d->name = QLatin1String("DHE-RSA-AES128-SHA256"); - break; - case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: - ciph.d->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: - ciph.d->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: - ciph.d->name = QLatin1String("ECDHE-ECDSA-AES128-SHA256"); - break; - case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: - ciph.d->name = QLatin1String("ECDHE-ECDSA-AES256-SHA384"); - break; - case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: - ciph.d->name = QLatin1String("ECDH-ECDSA-AES128-SHA256"); - break; - case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: - ciph.d->name = QLatin1String("ECDH-ECDSA-AES256-SHA384"); - break; - case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: - ciph.d->name = QLatin1String("ECDHE-RSA-AES128-SHA256"); - break; - case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: - ciph.d->name = QLatin1String("ECDHE-RSA-AES256-SHA384"); - break; - case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: - ciph.d->name = QLatin1String("ECDH-RSA-AES128-SHA256"); - break; - case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: - ciph.d->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: - ciph.d->name = QLatin1String("ECDHE-RSA-AES256-GCM-SHA384"); - break; - - default: - return ciph; - } - ciph.d->isNull = false; - - // protocol - ciph.d->protocol = QSsl::TlsV1_2; - ciph.d->protocolString = QLatin1String("TLSv1.2"); - - const auto bits = QStringView{ciph.d->name}.split(QLatin1Char('-')); - if (bits.size() >= 2) { - if (bits.size() == 2 || bits.size() == 3) { - ciph.d->keyExchangeMethod = QLatin1String("RSA"); - } else if (bits.front() == QLatin1String("DH") || bits.front() == QLatin1String("DHE")) { - ciph.d->keyExchangeMethod = QLatin1String("DH"); - } else if (bits.front() == QLatin1String("ECDH") || bits.front() == QLatin1String("ECDHE")) { - ciph.d->keyExchangeMethod = QLatin1String("ECDH"); - } else { - qCWarning(lcSsl) << "Unknown Kx" << ciph.d->name; - } - - if (bits.size() == 2 || bits.size() == 3) { - ciph.d->authenticationMethod = QLatin1String("RSA"); - } else if (ciph.d->name.contains(QLatin1String("-ECDSA-"))) { - ciph.d->authenticationMethod = QLatin1String("ECDSA"); - } else if (ciph.d->name.contains(QLatin1String("-RSA-"))) { - ciph.d->authenticationMethod = QLatin1String("RSA"); - } else { - qCWarning(lcSsl) << "Unknown Au" << ciph.d->name; - } - - if (ciph.d->name.contains(QLatin1String("RC4-"))) { - ciph.d->encryptionMethod = QLatin1String("RC4(128)"); - ciph.d->bits = 128; - ciph.d->supportedBits = 128; - } else if (ciph.d->name.contains(QLatin1String("DES-CBC3-"))) { - ciph.d->encryptionMethod = QLatin1String("3DES(168)"); - ciph.d->bits = 168; - ciph.d->supportedBits = 168; - } else if (ciph.d->name.contains(QLatin1String("AES128-"))) { - ciph.d->encryptionMethod = QLatin1String("AES(128)"); - ciph.d->bits = 128; - ciph.d->supportedBits = 128; - } else if (ciph.d->name.contains(QLatin1String("AES256-GCM"))) { - ciph.d->encryptionMethod = QLatin1String("AESGCM(256)"); - ciph.d->bits = 256; - ciph.d->supportedBits = 256; - } else if (ciph.d->name.contains(QLatin1String("AES256-"))) { - ciph.d->encryptionMethod = QLatin1String("AES(256)"); - ciph.d->bits = 256; - ciph.d->supportedBits = 256; - } else if (ciph.d->name.contains(QLatin1String("NULL-"))) { - ciph.d->encryptionMethod = QLatin1String("NULL"); - } else { - qCWarning(lcSsl) << "Unknown Enc" << ciph.d->name; - } - } - return ciph; -} -SSLCipherSuite QSslSocketBackendPrivate::SSLCipherSuite_from_QSslCipher(const QSslCipher &ciph) -{ - if (ciph.d->name == QLatin1String("AES128-SHA")) - return TLS_RSA_WITH_AES_128_CBC_SHA; - if (ciph.d->name == QLatin1String("DHE-RSA-AES128-SHA")) - return TLS_DHE_RSA_WITH_AES_128_CBC_SHA; - if (ciph.d->name == QLatin1String("AES256-SHA")) - return TLS_RSA_WITH_AES_256_CBC_SHA; - if (ciph.d->name == QLatin1String("DHE-RSA-AES256-SHA")) - return TLS_DHE_RSA_WITH_AES_256_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-NULL-SHA")) - return TLS_ECDH_ECDSA_WITH_NULL_SHA; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-RC4-SHA")) - return TLS_ECDH_ECDSA_WITH_RC4_128_SHA; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-DES-CBC3-SHA")) - return TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-AES128-SHA")) - return TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-AES256-SHA")) - return TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-RC4-SHA")) - return TLS_ECDHE_ECDSA_WITH_RC4_128_SHA; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-DES-CBC3-SHA")) - return TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-AES128-SHA")) - return TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-AES256-SHA")) - return TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-RSA-NULL-SHA")) - return TLS_ECDH_RSA_WITH_NULL_SHA; - if (ciph.d->name == QLatin1String("ECDH-RSA-RC4-SHA")) - return TLS_ECDH_RSA_WITH_RC4_128_SHA; - if (ciph.d->name == QLatin1String("ECDH-RSA-DES-CBC3-SHA")) - return TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-RSA-AES128-SHA")) - return TLS_ECDH_RSA_WITH_AES_128_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-RSA-AES256-SHA")) - return TLS_ECDH_RSA_WITH_AES_256_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-RSA-RC4-SHA")) - return TLS_ECDHE_RSA_WITH_RC4_128_SHA; - if (ciph.d->name == QLatin1String("ECDH-RSA-DES-CBC3-SHA")) - return TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-RSA-AES128-SHA")) - return TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-RSA-AES256-SHA")) - return TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA; - if (ciph.d->name == QLatin1String("DES-CBC3-SHA")) - return TLS_RSA_WITH_3DES_EDE_CBC_SHA; - if (ciph.d->name == QLatin1String("AES128-SHA256")) - return TLS_RSA_WITH_AES_128_CBC_SHA256; - if (ciph.d->name == QLatin1String("AES256-SHA256")) - return TLS_RSA_WITH_AES_256_CBC_SHA256; - if (ciph.d->name == QLatin1String("DHE-RSA-DES-CBC3-SHA")) - return TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA; - if (ciph.d->name == QLatin1String("DHE-RSA-AES128-SHA256")) - return TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; - if (ciph.d->name == QLatin1String("DHE-RSA-AES256-SHA256")) - return TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; - if (ciph.d->name == QLatin1String("AES256-GCM-SHA384")) - return TLS_RSA_WITH_AES_256_GCM_SHA384; - if (ciph.d->name == QLatin1String("ECDHE-ECDSA-AES128-SHA256")) - return TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; - if (ciph.d->name == QLatin1String("ECDHE-ECDSA-AES256-SHA384")) - return TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-AES128-SHA256")) - return TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-AES256-SHA384")) - return TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384; - if (ciph.d->name == QLatin1String("ECDHE-RSA-AES128-SHA256")) - return TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256; - if (ciph.d->name == QLatin1String("ECDHE-RSA-AES256-SHA384")) - return TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384; - if (ciph.d->name == QLatin1String("ECDHE-RSA-AES256-SHA384")) - return TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256; - if (ciph.d->name == QLatin1String("ECDHE-RSA-AES256-GCM-SHA384")) - return TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384; - return 0; -} - -bool QSslSocketBackendPrivate::initSslContext() -{ - Q_Q(QSslSocket); - - Q_ASSERT_X(!context, Q_FUNC_INFO, "invalid socket state, context is not null"); - Q_ASSERT(plainSocket); - - context.reset(qt_createSecureTransportContext(mode)); - if (!context) { - setErrorAndEmit(QAbstractSocket::SslInternalError, QStringLiteral("SSLCreateContext failed")); - return false; - } - - const OSStatus err = SSLSetIOFuncs(context, - reinterpret_cast<SSLReadFunc>(&QSslSocketBackendPrivate::ReadCallback), - reinterpret_cast<SSLWriteFunc>(&QSslSocketBackendPrivate::WriteCallback)); - if (err != errSecSuccess) { - destroySslContext(); - setErrorAndEmit(QAbstractSocket::SslInternalError, - QStringLiteral("SSLSetIOFuncs failed: %1").arg(err)); - return false; - } - - SSLSetConnection(context, this); - - if (mode == QSslSocket::SslServerMode - && !configuration.localCertificateChain.isEmpty()) { - QString errorDescription; - QAbstractSocket::SocketError errorCode = QAbstractSocket::UnknownSocketError; - if (!setSessionCertificate(errorDescription, errorCode)) { - destroySslContext(); - setErrorAndEmit(errorCode, errorDescription); - return false; - } - } - - if (!setSessionProtocol()) { - destroySslContext(); - 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.nextAllowedProtocols; - QCFType<CFMutableArrayRef> cfNames(CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks)); - if (cfNames) { - for (const QByteArray &name : protocolNames) { - if (name.size() > 255) { - qCWarning(lcSsl) << "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(lcSsl) << "SSLSetALPNProtocols failed - too long protocol names?"; - } - } else { - qCWarning(lcSsl) << "failed to allocate ALPN names array"; - } - } -#endif // QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE - - if (mode == QSslSocket::SslClientMode) { - // enable Server Name Indication (SNI) - QString tlsHostName(verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName); - if (tlsHostName.isEmpty()) - tlsHostName = hostName; - - 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(); - 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(); - 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 = QSslSocketBackendPrivate::SSLCipherSuite_from_QSslCipher(cipher)) - cfCiphers << sslCipher; - } - if (cfCiphers.size() == 0) { - qCWarning(lcSsl) << "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(lcSsl) << "failed to set the ciphers from the configuration"; - return false; - } - } - return true; -} - -void QSslSocketBackendPrivate::destroySslContext() -{ - context.reset(nullptr); -} - -bool QSslSocketBackendPrivate::setSessionCertificate(QString &errorDescription, QAbstractSocket::SocketError &errorCode) -{ - Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)"); - - 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(lcSsl) << 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(lcSsl) << 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(lcSsl) << 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(lcSsl) << 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 QSslSocketBackendPrivate::setSessionProtocol() -{ - Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)"); - - // 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. - switch (configuration.protocol) { - case QSsl::TlsV1_3: - case QSsl::TlsV1_3OrLater: - qCWarning(lcSsl) << plainSocket << "SecureTransport does not support TLS 1.3"; - return false; - default:; - } - - OSStatus err = errSecSuccess; - - if (configuration.protocol == QSsl::TlsV1_0) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << 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(lcSsl) << 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(lcSsl) << 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(lcSsl) << plainSocket << "requesting : any"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol1); - } else if (configuration.protocol == QSsl::SecureProtocols) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "requesting : TLSv1 - TLSv1.2"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol1); - } else if (configuration.protocol == QSsl::TlsV1_0OrLater) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "requesting : TLSv1 - TLSv1.2"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol1); - } else if (configuration.protocol == QSsl::TlsV1_1OrLater) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.1 - TLSv1.2"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol11); - } else if (configuration.protocol == QSsl::TlsV1_2OrLater) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.2"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol12); - } else { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "no protocol version found in the configuration"; - #endif - return false; - } - - return err == errSecSuccess; -} - -bool QSslSocketBackendPrivate::canIgnoreTrustVerificationFailure() const -{ - const QSslSocket::PeerVerifyMode verifyMode = configuration.peerVerifyMode; - return mode == QSslSocket::SslServerMode - && (verifyMode == QSslSocket::QueryPeer - || verifyMode == QSslSocket::AutoVerifyPeer - || verifyMode == QSslSocket::VerifyNone); -} - -bool QSslSocketBackendPrivate::verifySessionProtocol() const -{ - 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 QSslSocketBackendPrivate::verifyPeerTrust() -{ - Q_Q(QSslSocket); - - const QSslSocket::PeerVerifyMode verifyMode = configuration.peerVerifyMode; - const bool canIgnoreVerify = canIgnoreTrustVerificationFailure(); - - Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)"); - 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) { - 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 ... - setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - QStringLiteral("SecTrustEvaluate failed: %1").arg(err)); - plainSocket->disconnectFromHost(); - return false; - } - - configuration.peerCertificate.clear(); - configuration.peerCertificateChain.clear(); - - const CFIndex certCount = SecTrustGetCertificateCount(trust); - for (CFIndex i = 0; i < certCount; ++i) { - SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, i); - QCFType<CFDataRef> derData = SecCertificateCopyData(cert); - configuration.peerCertificateChain << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der); - } - - if (configuration.peerCertificateChain.size()) - configuration.peerCertificate = configuration.peerCertificateChain.at(0); - - // Check the whole chain for blacklisting (including root, as we check for subjectInfo and issuer): - for (const QSslCertificate &cert : qAsConst(configuration.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 - && 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. - 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 (mode == QSslSocket::SslClientMode) { - const QString peerName(verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName); - if (!isMatchingHostname(configuration.peerCertificate, peerName) && !canIgnoreVerify) { - // No matches in common names or alternate names. - const 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 && !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); - for (const QSslCertificate &cert : qAsConst(configuration.caCertificates)) { - QCFType<CFDataRef> certData = cert.d->derData.toCFData(); - if (QCFType<SecCertificateRef> secRef = SecCertificateCreateWithData(nullptr, certData)) - CFArrayAppendValue(certArray, secRef); - else - qCWarning(lcSsl, "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, configuration.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 QSslSocketBackendPrivate::checkSslErrors() -{ - Q_Q(QSslSocket); - if (sslErrors.isEmpty()) - return true; - - emit q->sslErrors(sslErrors); - - const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer - || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer - && mode == QSslSocket::SslClientMode); - const bool doEmitSslError = !verifyErrorsHaveBeenIgnored(); - // check whether we need to emit an SSL handshake error - if (doVerifyPeer && doEmitSslError) { - if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) { - pauseSocketNotifiers(q); - paused = true; - } else { - setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - sslErrors.constFirst().errorString()); - plainSocket->disconnectFromHost(); - } - return false; - } - - return true; -} - -bool QSslSocketBackendPrivate::startHandshake() -{ - Q_ASSERT(context); - Q_Q(QSslSocket); - - OSStatus err = SSLHandshake(context); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << 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)) { - 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; - setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - QStringLiteral("SSLHandshake failed: %1").arg(err)); - plainSocket->disconnectFromHost(); - return false; - } - - // Connection aborted during handshake phase. - if (q->state() != QAbstractSocket::ConnectedState) { - qCDebug(lcSsl) << "connection aborted"; - renegotiating = false; - return false; - } - - // check protocol version ourselves, as Secure Transport does not enforce - // the requested min / max versions. - if (!verifySessionProtocol()) { - 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; - } -} - -void QSslSocketPrivate::registerAdHocFactory() -{ - // TLSTODO: this is a temporary solution, waiting for - // backends to move to ... plugins. - if (!factory()) - qCWarning(lcSsl, "Failed to create backend factory"); -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_mac_p.h b/src/network/ssl/qsslsocket_mac_p.h deleted file mode 100644 index dcd4f4de72..0000000000 --- a/src/network/ssl/qsslsocket_mac_p.h +++ /dev/null @@ -1,138 +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 QSSLSOCKET_MAC_P_H -#define QSSLSOCKET_MAC_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 <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 - -class QSecureTransportContext -{ -public: - explicit QSecureTransportContext(SSLContextRef context); - ~QSecureTransportContext(); - - operator SSLContextRef () const; - void reset(SSLContextRef newContext); -private: - SSLContextRef context; - - Q_DISABLE_COPY_MOVE(QSecureTransportContext) -}; - -class QSslSocketBackendPrivate : public QSslSocketPrivate -{ - Q_DECLARE_PUBLIC(QSslSocket) -public: - QSslSocketBackendPrivate(); - virtual ~QSslSocketBackendPrivate(); - - // Final-overriders (QSslSocketPrivate): - 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; - - static QList<QSslError> verify(QList<QSslCertificate> certificateChain, - const QString &hostName); - - static bool importPkcs12(QIODevice *device, - QSslKey *key, QSslCertificate *cert, - QList<QSslCertificate> *caCertificates, - const QByteArray &passPhrase); - - static QSslCipher QSslCipher_from_SSLCipherSuite(SSLCipherSuite cipher); - static SSLCipherSuite SSLCipherSuite_from_QSslCipher(const QSslCipher &cipher); - -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 {return connectionEncrypted && !renegotiating;} - - // IO callbacks: - static OSStatus ReadCallback(QSslSocketBackendPrivate *socket, char *data, size_t *dataLength); - static OSStatus WriteCallback(QSslSocketBackendPrivate *plainSocket, const char *data, size_t *dataLength); - - QSecureTransportContext context; - bool renegotiating = false; - - Q_DISABLE_COPY_MOVE(QSslSocketBackendPrivate) -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/network/ssl/qsslsocket_mac_shared.cpp b/src/network/ssl/qsslsocket_mac_shared.cpp deleted file mode 100644 index 09f283775b..0000000000 --- a/src/network/ssl/qsslsocket_mac_shared.cpp +++ /dev/null @@ -1,155 +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$ -** -****************************************************************************/ - -//#define QSSLSOCKET_DEBUG -//#define QT_DECRYPT_SSL_TRAFFIC - -#include "qssl_p.h" -#include "qsslsocket.h" - -#ifndef QT_NO_OPENSSL -# include "qsslsocket_openssl_p.h" -# include "qsslsocket_openssl_symbols_p.h" -#endif - -#include "qsslcertificate_p.h" - -#ifdef Q_OS_DARWIN -# include <private/qcore_mac_p.h> -#endif - -#include <QtCore/qdebug.h> - -#ifdef Q_OS_MACOS -# include <Security/Security.h> -#endif - - -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(lcSsl, "Error receiving trust for a CA certificate"); - } - return false; -} - -} // anon namespace -#endif // Q_OS_MACOS - -QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates() -{ - ensureInitialized(); - - 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; -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp deleted file mode 100644 index 2f39b68002..0000000000 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ /dev/null @@ -1,2604 +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. -** -****************************************************************************/ - -//#define QSSLSOCKET_DEBUG - -#include "qssl_p.h" -#include "qsslsocket_openssl_p.h" -#include "qsslsocket_openssl_symbols_p.h" -#include "qsslsocket.h" -#include "qsslcertificate_p.h" -#include "qsslcipher_p.h" -#include "qsslkey_p.h" -#include "qsslellipticcurve.h" -#include "qsslpresharedkeyauthenticator.h" -#include "qsslpresharedkeyauthenticator_p.h" -#include "qocspresponse_p.h" -#include "qsslkey.h" - -#ifdef Q_OS_WIN -#include "qwindowscarootfetcher_p.h" -#endif - -#include <QtCore/qdatetime.h> -#include <QtCore/qdebug.h> -#include <QtCore/qdir.h> -#include <QtCore/qdiriterator.h> -#include <QtCore/qelapsedtimer.h> -#include <QtCore/qfile.h> -#include <QtCore/qfileinfo.h> -#include <QtCore/qmutex.h> -#include <QtCore/qthread.h> -#include <QtCore/qurl.h> -#include <QtCore/qvarlengtharray.h> -#include <QtCore/qscopedvaluerollback.h> -#include <QtCore/qscopeguard.h> -#include <QtCore/qlibrary.h> -#include <QtCore/qoperatingsystemversion.h> - -#if QT_CONFIG(ocsp) -#include "qocsp_p.h" -#endif - -#include <algorithm> -#include <memory> - -#include <string.h> - -QT_BEGIN_NAMESPACE - -namespace { - -// These two classes are ad-hoc temporary solution, to be replaced -// by the real things soon. -class OpenSSLBackend : public QTlsBackend -{ -private: - QString backendName() const override - { - return QTlsBackendFactory::builtinBackendNames[QTlsBackendFactory::nameIndexOpenSSL]; - } -}; - -class OpenSSLBackendFactory : public QTlsBackendFactory -{ -private: - QString backendName() const override - { - return QTlsBackendFactory::builtinBackendNames[QTlsBackendFactory::nameIndexOpenSSL]; - } - QTlsBackend *create() const override - { - return new OpenSSLBackend; - } - - QList<QSsl::SslProtocol> supportedProtocols() const override - { - 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> supportedFeatures() const override - { - 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> implementedClasses() const override - { - 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; - } -}; - -Q_GLOBAL_STATIC(OpenSSLBackendFactory, factory) - -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(lcSsl) << 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 - -extern "C" -{ - -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(lcSsl, "Invalid 'connection' parameter (nullptr)"); -#endif // QSSLSOCKET_DEBUG - return; - } - - const auto offset = QSslSocketBackendPrivate::s_indexForSSLExtraData - + QSslSocketBackendPrivate::socketOffsetInExData; - auto privateSocket = - static_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(connection, offset)); - if (!privateSocket) { - // SSL_set_ex_data can fail: -#ifdef QSSLSOCKET_DEBUG - qCWarning(lcSsl, "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) - privateSocket->alertMessageSent(value); - else - privateSocket->alertMessageReceived(value); -} - -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(lcSsl, "No external data (SSL) found in X509 store object"); - return 0; - } - - const auto offset = QSslSocketBackendPrivate::s_indexForSSLExtraData - + QSslSocketBackendPrivate::socketOffsetInExData; - auto privateSocket = static_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, offset)); - if (!privateSocket) { - qCWarning(lcSsl, "No external data (QSslSocketBackendPrivate) found in SSL object"); - return 0; - } - - return privateSocket->emitErrorFromCallback(ctx); - } - return 1; -} - -} // extern "C" - -Q_GLOBAL_STATIC(QRecursiveMutex, qt_opensslInitMutex) - -bool QSslSocketPrivate::s_libraryLoaded = false; -bool QSslSocketPrivate::s_loadedCiphersAndCerts = false; -bool QSslSocketPrivate::s_loadRootCertsOnDemand = false; -int QSslSocketBackendPrivate::s_indexForSSLExtraData = -1; - -QString QSslSocketBackendPrivate::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 QSslSocketBackendPrivate::logAndClearErrorQueue() -{ - const auto errors = getErrorsFromOpenSsl(); - if (errors.size()) - qCWarning(lcSsl) << "Discarding errors:" << errors; -} - -extern "C" { - -#ifndef OPENSSL_NO_PSK -static unsigned int q_ssl_psk_client_callback(SSL *ssl, - const char *hint, - char *identity, unsigned int max_identity_len, - unsigned char *psk, unsigned int max_psk_len) -{ - QSslSocketBackendPrivate *d = reinterpret_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData)); - Q_ASSERT(d); - return d->tlsPskClientCallback(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) -{ - QSslSocketBackendPrivate *d = reinterpret_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData)); - Q_ASSERT(d); - return d->tlsPskServerCallback(identity, psk, max_psk_len); -} - -#ifdef TLS1_3_VERSION -static unsigned int q_ssl_psk_restore_client(SSL *ssl, - const char *hint, - char *identity, unsigned int max_identity_len, - unsigned char *psk, unsigned int 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 - QSslSocketBackendPrivate *d = reinterpret_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData)); - Q_ASSERT(d); - Q_ASSERT(d->mode == 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 - QSslSocketBackendPrivate *d = reinterpret_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData)); - Q_ASSERT(d); - Q_ASSERT(d->mode == 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(lcSsl, "Invalid SSL (nullptr)"); - return 0; - } - if (!session) { - qCWarning(lcSsl, "Invalid SSL_SESSION (nullptr)"); - return 0; - } - - auto socketPrivate = static_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, - QSslSocketBackendPrivate::s_indexForSSLExtraData)); - return socketPrivate->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 d = static_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData)); - if (!d) - return SSL_TLSEXT_ERR_ALERT_FATAL; - - Q_ASSERT(d->mode == QSslSocket::SslServerMode); - const QByteArray &response = d->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 - -} // extern "C" - -QSslSocketBackendPrivate::QSslSocketBackendPrivate() - : ssl(nullptr), - readBio(nullptr), - writeBio(nullptr), - session(nullptr) -{ - // Calls SSL_library_init(). - ensureInitialized(); -} - -QSslSocketBackendPrivate::~QSslSocketBackendPrivate() -{ - destroySslContext(); -} - -QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(const SSL_CIPHER *cipher) -{ - QSslCipher ciph; - - char buf [256]; - QString descriptionOneLine = QString::fromLatin1(q_SSL_CIPHER_description(cipher, buf, sizeof(buf))); - - const auto descriptionList = QStringView{descriptionOneLine}.split(QLatin1Char(' '), Qt::SkipEmptyParts); - if (descriptionList.size() > 5) { - // ### crude code. - ciph.d->isNull = false; - ciph.d->name = descriptionList.at(0).toString(); - - QString protoString = descriptionList.at(1).toString(); - ciph.d->protocolString = protoString; - ciph.d->protocol = QSsl::UnknownProtocol; - if (protoString == QLatin1String("TLSv1")) - ciph.d->protocol = QSsl::TlsV1_0; - else if (protoString == QLatin1String("TLSv1.1")) - ciph.d->protocol = QSsl::TlsV1_1; - else if (protoString == QLatin1String("TLSv1.2")) - ciph.d->protocol = QSsl::TlsV1_2; - else if (protoString == QLatin1String("TLSv1.3")) - ciph.d->protocol = QSsl::TlsV1_3; - - if (descriptionList.at(2).startsWith(QLatin1String("Kx="))) - ciph.d->keyExchangeMethod = descriptionList.at(2).mid(3).toString(); - if (descriptionList.at(3).startsWith(QLatin1String("Au="))) - ciph.d->authenticationMethod = descriptionList.at(3).mid(3).toString(); - if (descriptionList.at(4).startsWith(QLatin1String("Enc="))) - ciph.d->encryptionMethod = descriptionList.at(4).mid(4).toString(); - ciph.d->exportable = (descriptionList.size() > 6 && descriptionList.at(6) == QLatin1String("export")); - - ciph.d->bits = q_SSL_CIPHER_get_bits(cipher, &ciph.d->supportedBits); - } - return ciph; -} - -QSslErrorEntry QSslErrorEntry::fromStoreContext(X509_STORE_CTX *ctx) -{ - return { - q_X509_STORE_CTX_get_error(ctx), - q_X509_STORE_CTX_get_error_depth(ctx) - }; -} - -#if QT_CONFIG(ocsp) - -QSslError::SslError qt_OCSP_response_status_to_SslError(long code) -{ - 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; -} - -#endif // ocsp - -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 = QSslSocketBackendPrivate::s_indexForSSLExtraData - + QSslSocketBackendPrivate::errorOffsetInExData; - if (SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx()))) - errors = ErrorListPtr(q_SSL_get_ex_data(ssl, offset)); - } - - if (!errors) { - qCWarning(lcSsl, "Neither X509_STORE, nor SSL contains error list, handshake failure"); - return 0; - } - - errors->append(QSslErrorEntry::fromStoreContext(ctx)); - } - // Always return OK to allow verification to continue. We handle the - // errors gracefully after collecting all errors, after verification has - // completed. - return 1; -} - -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)) { - QSslCipher ciph = QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(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; - } - } - } - } -} - -// Defined in qsslsocket.cpp -void q_setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers); - -long QSslSocketBackendPrivate::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; -} - -bool QSslSocketBackendPrivate::initSslContext() -{ - Q_Q(QSslSocket); - - // If no external context was set (e.g. by QHttpNetworkConnection) we will - // create a default context - if (!sslContextPointer) { - // create a deep copy of our configuration - QSslConfigurationPrivate *configurationCopy = new QSslConfigurationPrivate(configuration); - configurationCopy->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up - sslContextPointer = QSslContext::sharedFromConfiguration(mode, configurationCopy, allowRootCertOnDemandLoading); - } - - if (sslContextPointer->error() != QSslError::NoError) { - setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError, sslContextPointer->errorString()); - sslContextPointer.clear(); // deletes the QSslContext - return false; - } - - // Create and initialize SSL session - if (!(ssl = sslContextPointer->createSsl())) { - // ### Bad error code - setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Error creating SSL session, %1").arg(getErrorsFromOpenSsl())); - return false; - } - - if (configuration.protocol != QSsl::UnknownProtocol && mode == QSslSocket::SslClientMode) { - // 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 = hostName; - 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.sslOptions & 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(lcSsl, "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) { - setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Error creating SSL session: %1").arg(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, 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) { - 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) { - setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Failed to enable OCSP stapling")); - return false; - } - } - - ocspResponseDer.clear(); - auto responsePos = configuration.backendConfig.find("Qt-OCSP-response"); - if (responsePos != configuration.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) { - setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError, - QSslSocket::tr("Client-side sockets do not send OCSP responses")); - return false; - } - } -#endif // ocsp - - return true; -} - -void QSslSocketBackendPrivate::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 = getErrorsFromOpenSsl(); - Q_UNUSED(errors); - } - } - q_SSL_free(ssl); - ssl = nullptr; - } - sslContextPointer.clear(); -} - -/*! - \internal - - Does the minimum amount of initialization to determine whether SSL - is supported or not. -*/ - -bool QSslSocketPrivate::supportsSsl() -{ - return ensureLibraryLoaded(); -} - - -/*! - \internal - - Returns the version number of the SSL library in use. Note that - this is the version of the library in use at run-time, not compile - time. -*/ -long QSslSocketPrivate::sslLibraryVersionNumber() -{ - if (!supportsSsl()) - return 0; - - return q_OpenSSL_version_num(); -} - -/*! - \internal - - Returns the version string of the SSL library in use. Note that - this is the version of the library in use at run-time, not compile - time. If no SSL support is available then this will return an empty value. -*/ -QString QSslSocketPrivate::sslLibraryVersionString() -{ - if (!supportsSsl()) - return QString(); - - const char *versionString = q_OpenSSL_version(OPENSSL_VERSION); - if (!versionString) - return QString(); - - return QString::fromLatin1(versionString); -} - -/*! - \internal - - Declared static in QSslSocketPrivate, makes sure the SSL libraries have - been initialized. -*/ -void QSslSocketPrivate::ensureInitialized() -{ - if (!supportsSsl()) - return; - - ensureCiphersAndCertsLoaded(); -} - -/*! - \internal - - Returns the version number of the SSL library in use at compile - time. -*/ -long QSslSocketPrivate::sslLibraryBuildVersionNumber() -{ - return OPENSSL_VERSION_NUMBER; -} - -/*! - \internal - - Returns the version string of the SSL library in use at compile - time. -*/ -QString QSslSocketPrivate::sslLibraryBuildVersionString() -{ - // 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); -} - -/*! - \internal - - Declared static in QSslSocketPrivate, backend-dependent loading of - application-wide global ciphers. -*/ -void QSslSocketPrivate::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); - q_setDefaultDtlsCiphers(defaultCiphers); - q_SSL_free(mySsl); - } - q_SSL_CTX_free(myCtx); - } -#endif // dtls -} - -void QSslSocketPrivate::resetDefaultEllipticCurves() -{ - QList<QSslEllipticCurve> curves; - -#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) { - curves.reserve(int(curveCount)); - for (size_t i = 0; i < curveCount; ++i) { - QSslEllipticCurve curve; - curve.id = builtinCurves[int(i)].nid; - curves.append(curve); - } - } -#endif // OPENSSL_NO_EC - - // set the list of supported ECs, but not the list - // of *default* ECs. OpenSSL doesn't like forcing an EC for the wrong - // ciphersuite, so don't try it -- leave the empty list to mean - // "the implementation will choose the most suitable one". - setDefaultSupportedEllipticCurves(curves); -} - -#ifndef Q_OS_DARWIN // Apple implementation in qsslsocket_mac_shared.cpp -QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates() -{ - ensureInitialized(); -#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 = 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(lcSsl) << "systemCaCertificates retrieval time " << timer.elapsed() << "ms"; - qCDebug(lcSsl) << "imported " << systemCerts.count() << " certificates"; -#endif - - return systemCerts; -} -#endif // Q_OS_DARWIN - -void QSslSocketBackendPrivate::startClientEncryption() -{ - if (!initSslContext()) { - setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Unable to init SSL Context: %1").arg(getErrorsFromOpenSsl())); - return; - } - - // Start connecting. This will place outgoing data in the BIO, so we - // follow up with calling transmit(). - startHandshake(); - transmit(); -} - -void QSslSocketBackendPrivate::startServerEncryption() -{ - if (!initSslContext()) { - setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Unable to init SSL Context: %1").arg(getErrorsFromOpenSsl())); - return; - } - - // Start connecting. This will place outgoing data in the BIO, so we - // follow up with calling transmit(). - startHandshake(); - transmit(); -} - -/*! - \internal - - Transmits encrypted data between the BIOs and the socket. -*/ -void QSslSocketBackendPrivate::transmit() -{ - Q_Q(QSslSocket); - - using ScopedBool = QScopedValueRollback<bool>; - - if (inSetAndEmitError) - return; - - // If we don't have any SSL context, don't bother transmitting. - if (!ssl) - return; - - 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 (connectionEncrypted && !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); - setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Unable to write data: %1").arg( - getErrorsFromOpenSsl())); - return; - } - } -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << "QSslSocketBackendPrivate::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(lcSsl) << "QSslSocketBackendPrivate::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); - setErrorAndEmit(plainSocket->error(), plainSocket->errorString()); - return; - } - transmitting = true; - } - - // Check if we've got any data to be read from the socket. - if (!connectionEncrypted || !readBufferMaxSize || buffer.size() < readBufferMaxSize) - 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(lcSsl) << "QSslSocketBackendPrivate::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); - setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Unable to decrypt data: %1").arg( - getErrorsFromOpenSsl())); - return; - } - - transmitting = true; - } - - // If the connection isn't secured yet, this is the time to retry the - // connect / accept. - if (!connectionEncrypted) { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << "QSslSocketBackendPrivate::transmit: testing encryption"; -#endif - if (startHandshake()) { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << "QSslSocketBackendPrivate::transmit: encryption established"; -#endif - connectionEncrypted = true; - transmitting = true; - } else if (plainSocket->state() != QAbstractSocket::ConnectedState) { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << "QSslSocketBackendPrivate::transmit: connection lost"; -#endif - break; - } else if (paused) { - // just wait until the user continues - return; - } else { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << "QSslSocketBackendPrivate::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 (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(lcSsl) << "QSslSocketBackendPrivate::transmit: decrypted" << readBytes << "bytes"; -#endif - buffer.chop(bytesToRead - readBytes); - - if (readyReadEmittedPointer) - *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(lcSsl) << "QSslSocketBackendPrivate::transmit: remote disconnect"; -#endif - shutdown = true; // the other side shut down, make sure we do not send shutdown ourselves - { - const ScopedBool bg(inSetAndEmitError, true); - 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); - setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Error while reading: %1").arg(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); - setErrorAndEmit(QAbstractSocket::SslInternalError, - QSslSocket::tr("Error while reading: %1").arg(getErrorsFromOpenSsl())); - } - break; - } - } while (ssl && readBytes > 0); - } while (ssl && transmitting); -} - -QSslError _q_OpenSSL_to_QSslError(int errorCode, const QSslCertificate &cert) -{ - QSslError error; - switch (errorCode) { - case X509_V_OK: - // X509_V_OK is also reported if the peer had no certificate. - break; - case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: - error = QSslError(QSslError::UnableToGetIssuerCertificate, cert); break; - case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: - error = QSslError(QSslError::UnableToDecryptCertificateSignature, cert); break; - case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: - error = QSslError(QSslError::UnableToDecodeIssuerPublicKey, cert); break; - case X509_V_ERR_CERT_SIGNATURE_FAILURE: - error = QSslError(QSslError::CertificateSignatureFailed, cert); break; - case X509_V_ERR_CERT_NOT_YET_VALID: - error = QSslError(QSslError::CertificateNotYetValid, cert); break; - case X509_V_ERR_CERT_HAS_EXPIRED: - error = QSslError(QSslError::CertificateExpired, cert); break; - case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: - error = QSslError(QSslError::InvalidNotBeforeField, cert); break; - case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: - error = QSslError(QSslError::InvalidNotAfterField, cert); break; - case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: - error = QSslError(QSslError::SelfSignedCertificate, cert); break; - case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: - error = QSslError(QSslError::SelfSignedCertificateInChain, cert); break; - case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: - error = QSslError(QSslError::UnableToGetLocalIssuerCertificate, cert); break; - case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: - error = QSslError(QSslError::UnableToVerifyFirstCertificate, cert); break; - case X509_V_ERR_CERT_REVOKED: - error = QSslError(QSslError::CertificateRevoked, cert); break; - case X509_V_ERR_INVALID_CA: - error = QSslError(QSslError::InvalidCaCertificate, cert); break; - case X509_V_ERR_PATH_LENGTH_EXCEEDED: - error = QSslError(QSslError::PathLengthExceeded, cert); break; - case X509_V_ERR_INVALID_PURPOSE: - error = QSslError(QSslError::InvalidPurpose, cert); break; - case X509_V_ERR_CERT_UNTRUSTED: - error = QSslError(QSslError::CertificateUntrusted, cert); break; - case X509_V_ERR_CERT_REJECTED: - error = QSslError(QSslError::CertificateRejected, cert); break; - default: - error = QSslError(QSslError::UnspecifiedError, cert); break; - } - return error; -} - -QString QSslSocketBackendPrivate::msgErrorsDuringHandshake() -{ - return QSslSocket::tr("Error during SSL handshake: %1") - .arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); -} - -bool QSslSocketBackendPrivate::startHandshake() -{ - Q_Q(QSslSocket); - - // Check if the connection has been established. Get all errors from the - // verification stage. - - using ScopedBool = QScopedValueRollback<bool>; - - if (inSetAndEmitError) - return false; - - pendingFatalAlert = false; - errorsReportedFromCallback = false; - QList<QSslErrorEntry> lastErrors; - q_SSL_set_ex_data(ssl, 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, 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, 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(); - - if (!errorsReportedFromCallback) { - for (const auto ¤tError : qAsConst(lastErrors)) { - emit q->peerVerifyError(_q_OpenSSL_to_QSslError(currentError.code, - configuration.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 = QSslSocketBackendPrivate::msgErrorsDuringHandshake(); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << "QSslSocketBackendPrivate::startHandshake: error!" << errorString; -#endif - { - const ScopedBool bg(inSetAndEmitError, true); - setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, errorString); - if (pendingFatalAlert) { - trySendFatalAlert(); - pendingFatalAlert = false; - } - } - q->abort(); - } - return false; - } - - // store peer certificate chain - storePeerCertificates(); - - // Start translating errors. - QList<QSslError> errors; - - // check the whole chain for blacklisting (including root, as we check for subjectInfo and issuer) - for (const QSslCertificate &cert : qAsConst(configuration.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); - 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 - 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 << _q_OpenSSL_to_QSslError(error.code, configuration.peerCertificateChain.value(error.depth)); - - if (!errors.isEmpty()) { - sslErrors = errors; - -#ifdef Q_OS_WIN - const bool fetchEnabled = s_loadRootCertsOnDemand - && allowRootCertOnDemandLoading; - // !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 && !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 - 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 QSslSocketBackendPrivate::storePeerCertificates() -{ - // 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); - configuration.peerCertificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509); - q_X509_free(x509); - if (configuration.peerCertificateChain.isEmpty()) { - configuration.peerCertificateChain = STACKOFX509_to_QSslCertificates(q_SSL_get_peer_cert_chain(ssl)); - if (!configuration.peerCertificate.isNull() && mode == QSslSocket::SslServerMode) - configuration.peerCertificateChain.prepend(configuration.peerCertificate); - } -} - -int QSslSocketBackendPrivate::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_Q(QSslSocket); - - Q_ASSERT(connection); - - 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(lcSsl, - "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(lcSsl, "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(lcSsl, "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(lcSsl, "could not store persistent version of SSL session"); - return 0; - } - - configuration.sslSession = sessionTicket; - configuration.sslSessionTicketLifeTimeHint = int(q_SSL_SESSION_get_ticket_lifetime_hint(currentSession)); - - emit q->newSessionTicketReceived(); - return 0; -} - -bool QSslSocketBackendPrivate::checkSslErrors() -{ - Q_Q(QSslSocket); - if (sslErrors.isEmpty()) - return true; - - emit q->sslErrors(sslErrors); - - bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer - || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer - && mode == QSslSocket::SslClientMode); - bool doEmitSslError = !verifyErrorsHaveBeenIgnored(); - // check whether we need to emit an SSL handshake error - if (doVerifyPeer && doEmitSslError) { - if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) { - pauseSocketNotifiers(q); - paused = true; - } else { - setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, sslErrors.constFirst().errorString()); - plainSocket->disconnectFromHost(); - } - return false; - } - return true; -} - -unsigned int QSslSocketBackendPrivate::tlsPskClientCallback(const char *hint, - char *identity, unsigned int max_identity_len, - unsigned char *psk, unsigned int max_psk_len) -{ - QSslPreSharedKeyAuthenticator authenticator; - - // Fill in some read-only fields (for the user) - if (hint) - authenticator.d->identityHint = QByteArray::fromRawData(hint, int(::strlen(hint))); // it's NUL terminated, but do not include the NUL - - authenticator.d->maximumIdentityLength = int(max_identity_len) - 1; // needs to be NUL terminated - authenticator.d->maximumPreSharedKeyLength = int(max_psk_len); - - // Let the client provide the remaining bits... - Q_Q(QSslSocket); - 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()); - ::memcpy(identity, authenticator.identity().constData(), identityLength); - identity[identityLength] = 0; - - const int pskLength = qMin(authenticator.preSharedKey().length(), authenticator.maximumPreSharedKeyLength()); - ::memcpy(psk, authenticator.preSharedKey().constData(), pskLength); - return pskLength; -} - -unsigned int QSslSocketBackendPrivate::tlsPskServerCallback(const char *identity, - unsigned char *psk, unsigned int max_psk_len) -{ - QSslPreSharedKeyAuthenticator authenticator; - - // Fill in some read-only fields (for the user) - authenticator.d->identityHint = configuration.preSharedKeyIdentityHint; - authenticator.d->identity = identity; - authenticator.d->maximumIdentityLength = 0; // user cannot set an identity - authenticator.d->maximumPreSharedKeyLength = int(max_psk_len); - - // Let the client provide the remaining bits... - Q_Q(QSslSocket); - 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()); - ::memcpy(psk, authenticator.preSharedKey().constData(), pskLength); - return pskLength; -} - -#ifdef Q_OS_WIN - -void QSslSocketBackendPrivate::fetchCaRootForCert(const QSslCertificate &cert) -{ - Q_Q(QSslSocket); - //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 = configuration.caCertificates; - - //Remember we are fetching and what we are fetching: - caToFetch = cert; - - QWindowsCaRootFetcher *fetcher = new QWindowsCaRootFetcher(cert, mode, customRoots, q->peerVerifyName()); - QObjectPrivate::connect(fetcher, &QWindowsCaRootFetcher::finished, - this, &QSslSocketBackendPrivate::_q_caRootLoaded, - Qt::QueuedConnection); - QMetaObject::invokeMethod(fetcher, "start", Qt::QueuedConnection); - pauseSocketNotifiers(q); - paused = true; -} - -//This is the callback from QWindowsCaRootFetcher, trustedRoot will be invalid (default constructed) if it failed. -void QSslSocketBackendPrivate::_q_caRootLoaded(QSslCertificate cert, QSslCertificate trustedRoot) -{ - if (caToFetch != cert) { - //Ooops, something from the previous connection attempt, ignore! - return; - } - - //Done, fetched already: - caToFetch = QSslCertificate{}; - - if (fetchAuthorityInformation) { - if (!configuration.caCertificates.contains(trustedRoot)) - trustedRoot = QSslCertificate{}; - fetchAuthorityInformation = false; - } - - if (!trustedRoot.isNull() && !trustedRoot.isBlacklisted()) { - if (s_loadRootCertsOnDemand) { - //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 - if (!configuration.caCertificates.contains(trustedRoot)) - configuration.caCertificates += 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; - } - } - } - } - - // Continue with remaining errors - if (plainSocket) - plainSocket->resume(); - paused = false; - if (checkSslErrors() && ssl) { - bool willClose = (autoStartHandshake && pendingClose); - continueHandshake(); - if (!willClose) - transmit(); - } -} - -#endif - -#if QT_CONFIG(ocsp) - -bool QSslSocketBackendPrivate::checkOcspStatus() -{ - Q_ASSERT(ssl); - Q_ASSERT(mode == QSslSocket::SslClientMode); // See initSslContext() for SslServerMode - Q_ASSERT(configuration.peerVerifyMode != QSslSocket::VerifyNone); - - const auto clearErrorQueue = qScopeGuard([] { - 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 (configuration.peerCertificate.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 = QSslCertificatePrivate::QSslCertificate_from_X509(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 // ocsp - -void QSslSocketBackendPrivate::alertMessageSent(int value) -{ - Q_Q(QSslSocket); - - const auto level = tlsAlertLevel(value); - if (level == QSsl::AlertLevel::Fatal && !connectionEncrypted) { - // Note, this logic is handshake-time only: - pendingFatalAlert = true; - } - - emit q->alertSent(level, tlsAlertType(value), tlsAlertDescription(value)); -} - -void QSslSocketBackendPrivate::alertMessageReceived(int value) -{ - Q_Q(QSslSocket); - - emit q->alertReceived(tlsAlertLevel(value), tlsAlertType(value), tlsAlertDescription(value)); -} - -int QSslSocketBackendPrivate::emitErrorFromCallback(X509_STORE_CTX *ctx) -{ - // Returns 0 to abort verification, 1 to continue despite error (as - // OpenSSL expects from the verification callback). - Q_Q(QSslSocket); - - 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(lcSsl, "Could not obtain the certificate (that failed to verify)"); - return 0; - } - const QSslCertificate certificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509); - - const auto errorAndDepth = QSslErrorEntry::fromStoreContext(ctx); - const QSslError tlsError = _q_OpenSSL_to_QSslError(errorAndDepth.code, certificate); - - 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 = QSslSocketBackendPrivate::s_indexForSSLExtraData - + QSslSocketBackendPrivate::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 QSslSocketBackendPrivate::trySendFatalAlert() -{ - Q_ASSERT(pendingFatalAlert); - - 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(); - } -} - -void QSslSocketBackendPrivate::disconnectFromHost() -{ - if (ssl) { - if (!shutdown && !q_SSL_in_init(ssl) && !systemOrSslErrorDetected) { - if (q_SSL_shutdown(ssl) != 1) { - // Some error may be queued, clear it. - const auto errors = getErrorsFromOpenSsl(); - Q_UNUSED(errors); - } - shutdown = true; - transmit(); - } - } - plainSocket->disconnectFromHost(); -} - -void QSslSocketBackendPrivate::disconnected() -{ - if (plainSocket->bytesAvailable() <= 0) - destroySslContext(); - else { - // Move all bytes into the plain buffer - qint64 tmpReadBufferMaxSize = readBufferMaxSize; - readBufferMaxSize = 0; // reset temporarily so the plain socket buffer is completely drained - transmit(); - readBufferMaxSize = 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 QSslSocketBackendPrivate::sessionCipher() const -{ - if (!ssl) - return QSslCipher(); - - const SSL_CIPHER *sessionCipher = q_SSL_get_current_cipher(ssl); - return sessionCipher ? QSslCipher_from_SSL_CIPHER(sessionCipher) : QSslCipher(); -} - -QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const -{ - if (!ssl) - return QSsl::UnknownProtocol; - 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; -} - - -void QSslSocketBackendPrivate::continueHandshake() -{ - Q_Q(QSslSocket); - // if we have a max read buffer size, reset the plain socket's to match - if (readBufferMaxSize) - plainSocket->setReadBufferSize(readBufferMaxSize); - - if (q_SSL_session_reused(ssl)) - configuration.peerSessionShared = 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), 0, 0); - size_t client_random_len = q_SSL_get_client_random(ssl, 0, 0); - QByteArray masterKey(int(master_key_len), 0); // Will not overflow - QByteArray clientRandom(int(client_random_len), 0); // 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(lcSsl, "could not decrypt SSL traffic"); - } -#endif - - // Cache this SSL session inside the QSslContext - if (!(configuration.sslOptions & 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.sslOptions & QSsl::SslOptionDisableSessionPersistence)) { - if (!sslContextPointer->sessionASN1().isEmpty()) - configuration.sslSession = sslContextPointer->sessionASN1(); - configuration.sslSessionTicketLifeTimeHint = sslContextPointer->sessionTicketLifeTimeHint(); - } - } - } - -#if !defined(OPENSSL_NO_NEXTPROTONEG) - - configuration.nextProtocolNegotiationStatus = sslContextPointer->npnContext().status; - if (sslContextPointer->npnContext().status == QSslConfiguration::NextProtocolNegotiationUnsupported) { - // we could not agree -> be conservative and use HTTP/1.1 - configuration.nextNegotiatedProtocol = 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 ... - configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNegotiated; - } - - if (!proto_len) { // Test if NPN was more lucky ... - q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len); - } - - if (proto_len) - configuration.nextNegotiatedProtocol = QByteArray(reinterpret_cast<const char *>(proto), proto_len); - else - configuration.nextNegotiatedProtocol.clear(); - } -#endif // !defined(OPENSSL_NO_NEXTPROTONEG) - - if (mode == QSslSocket::SslClientMode) { - EVP_PKEY *key; - if (q_SSL_get_server_tmp_key(ssl, &key)) - configuration.ephemeralServerKey = QSslKey(key, QSsl::PublicKey); - } - - connectionEncrypted = true; - emit q->encrypted(); - if (autoStartHandshake && pendingClose) { - pendingClose = false; - q->disconnectFromHost(); - } -} - -bool QSslSocketPrivate::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(lcSsl, "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(); - - QSslSocketBackendPrivate::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; -} - -void QSslSocketPrivate::ensureCiphersAndCertsLoaded() -{ - 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) - s_loadRootCertsOnDemand = 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 = 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()) { - s_loadRootCertsOnDemand = true; - break; - } - } -#endif -#endif // QT_CONFIG(library) - // if on-demand loading was not enabled, load the certs now - if (!s_loadRootCertsOnDemand) - 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. - s_loadRootCertsOnDemand = true; -#endif -} - -QList<QSslCertificate> QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509) -{ - ensureInitialized(); - QList<QSslCertificate> certificates; - for (int i = 0; i < q_sk_X509_num(x509); ++i) { - if (X509 *entry = q_sk_X509_value(x509, i)) - certificates << QSslCertificatePrivate::QSslCertificate_from_X509(entry); - } - return certificates; -} - -QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &certificateChain, - const QString &hostName) -{ - 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 (s_loadRootCertsOnDemand) - roots.append(systemCaCertificates()); -#endif // Q_OS_WIN - return verify(roots, certificateChain, hostName); -} - -QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &caCertificates, - const QList<QSslCertificate> &certificateChain, - const QString &hostName) -{ - if (certificateChain.count() <= 0) - return {QSslError(QSslError::UnspecifiedError)}; - - QList<QSslError> errors; - // Setup the store with the default CA certificates - X509_STORE *certStore = q_X509_STORE_new(); - if (!certStore) { - qCWarning(lcSsl) << "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(lcSsl) << "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, q_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 (QSslCertificatePrivate::isBlacklisted(certificateChain[0])) { - QSslError error(QSslError::CertificateBlacklisted, certificateChain[0]); - errors << error; - } - - // Check the certificate name against the hostname if one was specified - if ((!hostName.isEmpty()) && (!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 << _q_OpenSSL_to_QSslError(error.code, certificateChain.value(error.depth)); - - return errors; -} - -bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device, - QSslKey *key, QSslCertificate *cert, - QList<QSslCertificate> *caCertificates, - const QByteArray &passPhrase) -{ - if (!supportsSsl()) - return false; - - // These are required - Q_ASSERT(device); - Q_ASSERT(key); - Q_ASSERT(cert); - - // Read the file into a BIO - QByteArray pkcs12data = device->readAll(); - if (pkcs12data.size() == 0) - return false; - - BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pkcs12data.constData()), pkcs12data.size()); - - // Create the PKCS#12 object - PKCS12 *p12 = q_d2i_PKCS12_bio(bio, nullptr); - if (!p12) { - qCWarning(lcSsl, "Unable to read PKCS#12 structure, %s", - q_ERR_error_string(q_ERR_get_error(), nullptr)); - q_BIO_free(bio); - return false; - } - - // Extract the data - EVP_PKEY *pkey = nullptr; - X509 *x509; - STACK_OF(X509) *ca = nullptr; - - if (!q_PKCS12_parse(p12, passPhrase.constData(), &pkey, &x509, &ca)) { - qCWarning(lcSsl, "Unable to parse PKCS#12 structure, %s", - q_ERR_error_string(q_ERR_get_error(), nullptr)); - q_PKCS12_free(p12); - q_BIO_free(bio); - return false; - } - - // Convert to Qt types - if (!key->d->fromEVP_PKEY(pkey)) { - qCWarning(lcSsl, "Unable to convert private key"); - q_OPENSSL_sk_pop_free(reinterpret_cast<OPENSSL_STACK *>(ca), - reinterpret_cast<void (*)(void *)>(q_X509_free)); - q_X509_free(x509); - q_EVP_PKEY_free(pkey); - q_PKCS12_free(p12); - q_BIO_free(bio); - - return false; - } - - *cert = QSslCertificatePrivate::QSslCertificate_from_X509(x509); - - if (caCertificates) - *caCertificates = QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(ca); - - // Clean up - q_OPENSSL_sk_pop_free(reinterpret_cast<OPENSSL_STACK *>(ca), - reinterpret_cast<void (*)(void *)>(q_X509_free)); - - q_X509_free(x509); - q_EVP_PKEY_free(pkey); - q_PKCS12_free(p12); - q_BIO_free(bio); - - return true; -} - -void QSslSocketPrivate::registerAdHocFactory() -{ - // TLSTODO: this is a temporary solution, waiting for - // backends to move to ... plugins. - if (!factory()) - qCWarning(lcSsl, "Failed to create backend factory"); -} - -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 deec1532a2..0000000000 --- a/src/network/ssl/qsslsocket_openssl_android.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/**************************************************************************** -** -** 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_openssl_p.h" -#include <QtCore/private/qjni_p.h> - -QT_BEGIN_NAMESPACE - -QList<QByteArray> QSslSocketPrivate::fetchSslCertificateData() -{ - QList<QByteArray> certificateData; - - QJNIObjectPrivate certificates = QJNIObjectPrivate::callStaticObjectMethod("org/qtproject/qt/android/QtNative", - "getSSLCertificates", - "()[[B"); - if (!certificates.isValid()) - return certificateData; - - QJNIEnvironmentPrivate 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; -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h deleted file mode 100644 index b45bbb0a30..0000000000 --- a/src/network/ssl/qsslsocket_openssl_p.h +++ /dev/null @@ -1,197 +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 <QtCore/qlist.h> -#include <QtCore/qstring.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 - -#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> - -#if QT_CONFIG(opensslv11) -#include <openssl/dh.h> -#endif - -QT_BEGIN_NAMESPACE - -struct QSslErrorEntry { - int code; - int depth; - - static QSslErrorEntry fromStoreContext(X509_STORE_CTX *ctx); -}; -Q_DECLARE_TYPEINFO(QSslErrorEntry, Q_PRIMITIVE_TYPE); - -class QSslSocketBackendPrivate : public QSslSocketPrivate -{ - Q_DECLARE_PUBLIC(QSslSocket) -public: - QSslSocketBackendPrivate(); - virtual ~QSslSocketBackendPrivate(); - - // SSL context - bool initSslContext(); - void destroySslContext(); - SSL *ssl; - BIO *readBio; - BIO *writeBio; - SSL_SESSION *session; - QList<QSslErrorEntry> errorList; - static int s_indexForSSLExtraData; // index used in SSL_get_ex_data to get the matching QSslSocketBackendPrivate - enum ExDataOffset { - errorOffsetInExData = 1, - socketOffsetInExData = 2 - }; - - bool inSetAndEmitError = false; - - // Platform specific functions - void startClientEncryption() override; - void startServerEncryption() override; - void transmit() override; - bool startHandshake(); - void disconnectFromHost() override; - void disconnected() override; - QSslCipher sessionCipher() const override; - QSsl::SslProtocol sessionProtocol() const override; - void continueHandshake() override; - bool checkSslErrors(); - void storePeerCertificates(); - int handleNewSessionTicket(SSL *context); - unsigned int tlsPskClientCallback(const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len); - unsigned int tlsPskServerCallback(const char *identity, unsigned char *psk, unsigned int max_psk_len); -#ifdef Q_OS_WIN - void fetchCaRootForCert(const QSslCertificate &cert); - void _q_caRootLoaded(QSslCertificate,QSslCertificate) override; -#endif - -#if QT_CONFIG(ocsp) - bool checkOcspStatus(); -#endif - - void alertMessageSent(int encoded); - void alertMessageReceived(int encoded); - - int emitErrorFromCallback(X509_STORE_CTX *ctx); - void trySendFatalAlert(); - - bool pendingFatalAlert = false; - bool errorsReportedFromCallback = false; - - // This decription will go to setErrorAndEmit(SslHandshakeError, ocspErrorDescription) - QString ocspErrorDescription; - // These will go to sslErrors() - QList<QSslError> ocspErrors; - QByteArray ocspResponseDer; - - Q_AUTOTEST_EXPORT static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions); - static QSslCipher QSslCipher_from_SSL_CIPHER(const SSL_CIPHER *cipher); - static QList<QSslCertificate> STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509); - static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain, const QString &hostName); - static QList<QSslError> verify(const QList<QSslCertificate> &cas, const QList<QSslCertificate> &certificateChain, - const QString &hostName); - static QString getErrorsFromOpenSsl(); - static void logAndClearErrorQueue(); - static bool importPkcs12(QIODevice *device, - QSslKey *key, QSslCertificate *cert, - QList<QSslCertificate> *caCertificates, - const QByteArray &passPhrase); - static QString msgErrorsDuringHandshake(); -}; - -QT_END_NAMESPACE - -#endif 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 9f54efddaa..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 "qsslsocket_openssl_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(type, st) ((int (*)(const STACK_OF(type) *))q_OPENSSL_sk_num)(st) -#define q_SKM_sk_value(type, st,i) ((type * (*)(const STACK_OF(type) *, int))q_OPENSSL_sk_value)(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(GENERAL_NAME, (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(X509, (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(SSL_CIPHER, (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 0a30f02e5f..9dafb36a08 100644 --- a/src/network/ssl/qsslsocket_p.h +++ b/src/network/ssl/qsslsocket_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSSLSOCKET_P_H @@ -55,60 +19,26 @@ // #include <QtNetwork/private/qtnetworkglobal_p.h> + #include <private/qtcpsocket_p.h> -#include "qsslkey.h" -#include "qsslconfiguration_p.h" + #include "qocspresponse.h" +#include "qsslconfiguration_p.h" +#include "qsslkey.h" #include "qtlsbackend_p.h" -#ifndef QT_NO_OPENSSL -#include <private/qsslcontext_openssl_p.h> -#else -class QSslContext; -#endif #include <QtCore/qlist.h> -#include <QtCore/qstringlist.h> #include <QtCore/qmutex.h> - -#include <private/qringbuffer_p.h> - -#if defined(Q_OS_MAC) -#include <Security/SecCertificate.h> -#include <CoreFoundation/CFArray.h> -#elif defined(Q_OS_WIN) -#include <QtCore/qt_windows.h> -#include <memory> -#include <wincrypt.h> -#ifndef HCRYPTPROV_LEGACY -#define HCRYPTPROV_LEGACY HCRYPTPROV -#endif // !HCRYPTPROV_LEGACY -#endif // Q_OS_WIN +#include <QtCore/qstringlist.h> #include <memory> QT_BEGIN_NAMESPACE -#if defined(Q_OS_MACOS) - typedef CFDataRef (*PtrSecCertificateCopyData)(SecCertificateRef); - typedef OSStatus (*PtrSecTrustSettingsCopyCertificates)(int, CFArrayRef*); - typedef OSStatus (*PtrSecTrustCopyAnchorCertificates)(CFArrayRef*); -#endif - -#if defined(Q_OS_WIN) - -// Those are needed by both OpenSSL and Schannel back-ends on Windows: -struct QHCertStoreDeleter { - void operator()(HCERTSTORE store) - { - CertCloseStore(store, 0); - } -}; - -using QHCertStorePointer = std::unique_ptr<void, QHCertStoreDeleter>; - -#endif // Q_OS_WIN +class QSslContext; +class QTlsBackend; -class QSslSocketPrivate : public QTcpSocketPrivate +class Q_NETWORK_EXPORT QSslSocketPrivate : public QTcpSocketPrivate { Q_DECLARE_PUBLIC(QSslSocket) public: @@ -122,14 +52,11 @@ public: QSslSocket::SslMode mode; bool autoStartHandshake; bool connectionEncrypted; - bool shutdown; bool ignoreAllSslErrors; QList<QSslError> ignoreErrorsList; bool* readyReadEmittedPointer; QSslConfigurationPrivate configuration; - QList<QSslError> sslErrors; - QSharedPointer<QSslContext> sslContextPointer; // if set, this hostname is used for certificate validation instead of the hostname // that was used for connecting to. @@ -140,16 +67,14 @@ public: static bool s_loadRootCertsOnDemand; static bool supportsSsl(); - static long sslLibraryVersionNumber(); - static QString sslLibraryVersionString(); - static long sslLibraryBuildVersionNumber(); - static QString sslLibraryBuildVersionString(); static void ensureInitialized(); + static QList<QSslCipher> defaultCiphers(); + static QList<QSslCipher> defaultDtlsCiphers(); static QList<QSslCipher> supportedCiphers(); static void setDefaultCiphers(const QList<QSslCipher> &ciphers); + static void setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers); static void setDefaultSupportedCiphers(const QList<QSslCipher> &ciphers); - static void resetDefaultCiphers(); static QList<QSslEllipticCurve> supportedEllipticCurves(); static void setDefaultSupportedEllipticCurves(const QList<QSslEllipticCurve> &curves); @@ -160,19 +85,19 @@ public: static void setDefaultCaCertificates(const QList<QSslCertificate> &certs); static void addDefaultCaCertificate(const QSslCertificate &cert); static void addDefaultCaCertificates(const QList<QSslCertificate> &certs); - Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QSslCertificate &cert, - const QString &peerName); - Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname); + static bool isMatchingHostname(const QSslCertificate &cert, const QString &peerName); + static bool isMatchingHostname(const QString &cn, const QString &hostname); // The socket itself, including private slots. - QTcpSocket *plainSocket; + QTcpSocket *plainSocket = nullptr; void createPlainSocket(QIODevice::OpenMode openMode); static void pauseSocketNotifiers(QSslSocket*); static void resumeSocketNotifiers(QSslSocket*); // ### The 2 methods below should be made member methods once the QSslContext class is made public - static void checkSettingSslContext(QSslSocket*, QSharedPointer<QSslContext>); - static QSharedPointer<QSslContext> sslContext(QSslSocket *socket); + static void checkSettingSslContext(QSslSocket*, std::shared_ptr<QSslContext>); + static std::shared_ptr<QSslContext> sslContext(QSslSocket *socket); bool isPaused() const; + void setPaused(bool p); bool bind(const QHostAddress &address, quint16, QAbstractSocket::BindMode) override; void _q_connectedSlot(); void _q_hostFoundSlot(); @@ -187,59 +112,57 @@ public: void _q_flushWriteBuffer(); void _q_flushReadBuffer(); void _q_resumeImplementation(); -#if defined(Q_OS_WIN) && !QT_CONFIG(schannel) - virtual void _q_caRootLoaded(QSslCertificate,QSslCertificate) = 0; -#endif static QList<QByteArray> unixRootCertDirectories(); // used also by QSslContext - virtual qint64 peek(char *data, qint64 maxSize) override; - virtual QByteArray peek(qint64 maxSize) override; + qint64 peek(char *data, qint64 maxSize) override; + QByteArray peek(qint64 maxSize) override; bool flush() override; - // Platform specific functions - virtual void startClientEncryption() = 0; - virtual void startServerEncryption() = 0; - virtual void transmit() = 0; - virtual void disconnectFromHost() = 0; - virtual void disconnected() = 0; - virtual QSslCipher sessionCipher() const = 0; - virtual QSsl::SslProtocol sessionProtocol() const = 0; - virtual void continueHandshake() = 0; - - Q_AUTOTEST_EXPORT static bool rootCertOnDemandLoadingSupported(); - - static bool loadBackend(const QString &backendName); - static void registerAdHocFactory(); - -private: - static bool ensureLibraryLoaded(); - static void ensureCiphersAndCertsLoaded(); -#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) - static QList<QByteArray> fetchSslCertificateData(); -#endif + void startClientEncryption(); + void startServerEncryption(); + void transmit(); + void disconnectFromHost(); + void disconnected(); + QSslCipher sessionCipher() const; + QSsl::SslProtocol sessionProtocol() const; + void continueHandshake(); + + static bool rootCertOnDemandLoadingSupported(); + static void setRootCertOnDemandLoadingSupported(bool supported); + + static QTlsBackend *tlsBackendInUse(); + + // Needed by TlsCryptograph: + QSslSocket::SslMode tlsMode() const; + bool isRootsOnDemandAllowed() const; + QString verificationName() const; + QString tlsHostName() const; + QTcpSocket *plainTcpSocket() const; + bool verifyErrorsHaveBeenIgnored(); + bool isAutoStartingHandshake() const; + bool isPendingClose() const; + void setPendingClose(bool pc); + qint64 maxReadBufferSize() const; + void setMaxReadBufferSize(qint64 maxSize); + void setEncrypted(bool enc); + QRingBufferRef &tlsWriteBuffer(); + QRingBufferRef &tlsBuffer(); + bool &tlsEmittedBytesWritten(); + bool *readyReadPointer(); - static bool s_libraryLoaded; - static bool s_loadedCiphersAndCerts; protected: - bool verifyErrorsHaveBeenIgnored(); + + bool hasUndecryptedData() const; bool paused; bool flushTriggered; - bool systemOrSslErrorDetected = false; - QList<QOcspResponse> ocspResponses; - bool handshakeInterrupted = false; - bool fetchAuthorityInformation = false; - QSslCertificate caToFetch; static inline QMutex backendMutex; static inline QString activeBackendName; - static inline std::unique_ptr<QTlsBackend> tlsBackend; -}; + static inline QTlsBackend *tlsBackend = nullptr; -#if QT_CONFIG(securetransport) || QT_CONFIG(schannel) -// Implemented in qsslsocket_qt.cpp -QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase); -#endif + std::unique_ptr<QTlsPrivate::TlsCryptograph> backend; +}; QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_qt.cpp b/src/network/ssl/qsslsocket_qt.cpp deleted file mode 100644 index a5d21bd77e..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(QSslKeyPrivate::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/qsslsocket_schannel.cpp b/src/network/ssl/qsslsocket_schannel.cpp deleted file mode 100644 index 7ac032bd52..0000000000 --- a/src/network/ssl/qsslsocket_schannel.cpp +++ /dev/null @@ -1,2224 +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 "qsslsocket_schannel_p.h" -#include "qsslcertificate.h" -#include "qsslcertificateextension.h" -#include "qsslcertificate_p.h" -#include "qsslcipher_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 - -namespace { - -class SchannelBackend : public QTlsBackend -{ -private: - QString backendName() const override - { - return QTlsBackendFactory::builtinBackendNames[QTlsBackendFactory::nameIndexSchannel]; - } -}; - -class SchannelBackendBackendFactory : public QTlsBackendFactory -{ -private: - QString backendName() const override - { - return QTlsBackendFactory::builtinBackendNames[QTlsBackendFactory::nameIndexSchannel]; - } - QTlsBackend *create() const override - { - return new SchannelBackend; - } - - QList<QSsl::SslProtocol> supportedProtocols() const override - { - 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; - - bool supportsTls13(); - if (supportsTls13()) { - protocols << QSsl::TlsV1_3; - protocols << QSsl::TlsV1_3OrLater; - } - - return protocols; - } - - QList<QSsl::SupportedFeature> supportedFeatures() const override - { - QList<QSsl::SupportedFeature> features; - - features << QSsl::SupportedFeature::ClientSideAlpn; - features << QSsl::SupportedFeature::ServerSideAlpn; - - return features; - } - - QList<QSsl::ImplementedClass> implementedClasses() const override - { - QList<QSsl::ImplementedClass> classes; - - classes << QSsl::ImplementedClass::Socket; - classes << QSsl::ImplementedClass::Certificate; - classes << QSsl::ImplementedClass::Key; - - return classes; - } -}; - -Q_GLOBAL_STATIC(SchannelBackendFactory, factory) - - -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 - -bool QSslSocketPrivate::s_loadRootCertsOnDemand = true; -bool QSslSocketPrivate::s_loadedCiphersAndCerts = false; -Q_GLOBAL_STATIC(QRecursiveMutex, qt_schannel_mutex) - -void QSslSocketPrivate::ensureInitialized() -{ - const QMutexLocker<QRecursiveMutex> locker(qt_schannel_mutex); - if (s_loadedCiphersAndCerts) - return; - s_loadedCiphersAndCerts = true; - - setDefaultCaCertificates(systemCaCertificates()); - s_loadRootCertsOnDemand = true; // setDefaultCaCertificates sets it to false, re-enable it. - - resetDefaultCiphers(); -} - -void QSslSocketPrivate::resetDefaultCiphers() -{ - setDefaultSupportedCiphers(QSslSocketBackendPrivate::defaultCiphers()); - setDefaultCiphers(QSslSocketBackendPrivate::defaultCiphers()); -} - -void QSslSocketPrivate::resetDefaultEllipticCurves() -{ - Q_UNIMPLEMENTED(); -} - -bool QSslSocketPrivate::supportsSsl() -{ - return true; -} - -QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates() -{ - // Copied from qsslsocket_openssl.cpp's 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(QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(pc)); - } - } - return systemCerts; -} - -long QSslSocketPrivate::sslLibraryVersionNumber() -{ - const auto os = QOperatingSystemVersion::current(); - return (os.majorVersion() << 24) | ((os.minorVersion() & 0xFF) << 16) | (os.microVersion() & 0xFFFF); -} - -QString QSslSocketPrivate::sslLibraryVersionString() -{ - 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 QSslSocketPrivate::sslLibraryBuildVersionNumber() -{ - // There is no separate build version - return sslLibraryVersionNumber(); -} - -QString QSslSocketPrivate::sslLibraryBuildVersionString() -{ - const auto os = QOperatingSystemVersion::current(); - return QString::fromLatin1("%1.%2.%3") - .arg(QString::number(os.majorVersion()), - QString::number(os.minorVersion()), - QString::number(os.microVersion())); -} - -QSslSocketBackendPrivate::QSslSocketBackendPrivate() -{ - SecInvalidateHandle(&credentialHandle); - SecInvalidateHandle(&contextHandle); - ensureInitialized(); -} - -QSslSocketBackendPrivate::~QSslSocketBackendPrivate() -{ - closeCertificateStores(); - deallocateContext(); - freeCredentialsHandle(); - CertFreeCertificateContext(localCertContext); -} - -bool QSslSocketBackendPrivate::sendToken(void *token, unsigned long tokenLength, bool emitError) -{ - if (tokenLength == 0) - return true; - 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) - setErrorAndEmit(plainSocket->error(), plainSocket->errorString()); - return false; - } - return true; -} - -QString QSslSocketBackendPrivate::targetName() const -{ - // Used for SNI extension - return verificationPeerName.isEmpty() ? q_func()->peerName() : verificationPeerName; -} - -ULONG QSslSocketBackendPrivate::getContextRequirements() -{ - const bool isClient = mode == 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 (configuration.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 QSslSocketBackendPrivate::acquireCredentialsHandle() -{ - Q_ASSERT(schannelState == SchannelState::InitializeHandshake); - - const bool isClient = mode == QSslSocket::SslClientMode; - const DWORD protocols = toSchannelProtocol(configuration.protocol); - if (protocols == DWORD(-1)) { - 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."); - 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) { - setErrorAndEmit(QAbstractSocket::SslInternalError, schannelErrorToString(status)); - return false; - } - return true; -} - -void QSslSocketBackendPrivate::deallocateContext() -{ - if (SecIsValidHandle(&contextHandle)) { - DeleteSecurityContext(&contextHandle); - SecInvalidateHandle(&contextHandle); - } -} - -void QSslSocketBackendPrivate::freeCredentialsHandle() -{ - if (SecIsValidHandle(&credentialHandle)) { - FreeCredentialsHandle(&credentialHandle); - SecInvalidateHandle(&credentialHandle); - } -} - -void QSslSocketBackendPrivate::closeCertificateStores() -{ - localCertificateStore.reset(); - peerCertificateStore.reset(); - caCertificateStore.reset(); -} - -bool QSslSocketBackendPrivate::createContext() -{ - Q_ASSERT(SecIsValidHandle(&credentialHandle)); - Q_ASSERT(schannelState == SchannelState::InitializeHandshake); - Q_ASSERT(mode == 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 - configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNone; - QByteArray alpnString = createAlpnString(configuration.nextAllowedProtocols); - 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) { - 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 QSslSocketBackendPrivate::acceptContext() -{ - Q_ASSERT(SecIsValidHandle(&credentialHandle)); - Q_ASSERT(schannelState == SchannelState::InitializeHandshake); - Q_ASSERT(mode == 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 - configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNone; - // The string must be alive when we call AcceptSecurityContext - QByteArray alpnString = createAlpnString(configuration.nextAllowedProtocols); - 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) { - 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 QSslSocketBackendPrivate::performHandshake() -{ - if (plainSocket->state() == QAbstractSocket::UnconnectedState) { - 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. - 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 - 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: - 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) - setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - QSslSocket::tr("Handshake failed: %1").arg(schannelErrorToString(status))); - return false; -} - -bool QSslSocketBackendPrivate::verifyHandshake() -{ - Q_Q(QSslSocket); - sslErrors.clear(); - - const bool isClient = mode == QSslSocket::SslClientMode; -#define CHECK_STATUS(status) \ - if (status != SEC_E_OK) { \ - 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)) { - 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 - if (!configuration.nextAllowedProtocols.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 (!configuration.nextAllowedProtocols.contains(negotiatedProto)) { - setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - QSslSocket::tr("Unwanted protocol was negotiated")); - return false; - } - configuration.nextNegotiatedProtocol = negotiatedProto; - configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNegotiated; - } else { - configuration.nextNegotiatedProtocol = ""; - configuration.nextProtocolNegotiationStatus = 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() || 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 paused && state == QAbstractSocket::ConnectedState; - } - - schannelState = SchannelState::Done; - return true; -} - -bool QSslSocketBackendPrivate::renegotiate() -{ - 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 (mode == 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; - } - 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 QSslSocketBackendPrivate::reset() -{ - 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; - - connectionEncrypted = false; - shutdown = false; - renegotiating = false; - - missingData = 0; -} - -void QSslSocketBackendPrivate::startClientEncryption() -{ - if (connectionEncrypted) - return; // let's not mess up the connection... - reset(); - continueHandshake(); -} - -void QSslSocketBackendPrivate::startServerEncryption() -{ - if (connectionEncrypted) - return; // let's not mess up the connection... - reset(); - continueHandshake(); -} - -void QSslSocketBackendPrivate::transmit() -{ - Q_Q(QSslSocket); - - if (mode == 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; - } - - if (connectionEncrypted) { // 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) { - 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 { - setErrorAndEmit(plainSocket->error(), plainSocket->errorString()); - return; - } - } - - if (totalBytesWritten > 0) { - // Don't emit bytesWritten() recursively. - if (!emittedBytesWritten) { - emittedBytesWritten = true; - emit q->bytesWritten(totalBytesWritten); - emittedBytesWritten = false; - } - emit q->channelBytesWritten(0, totalBytesWritten); - } - } - - if (connectionEncrypted) { // Decrypt data from remote - int totalRead = 0; - bool hadIncompleteData = false; - 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(); - 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(); - setErrorAndEmit(QAbstractSocket::SslInternalError, - schannelErrorToString(status)); - break; - } else if (status == SEC_I_CONTEXT_EXPIRED) { - // 'remote' has initiated a shutdown - disconnectFromHost(); - 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 (readyReadEmittedPointer) - *readyReadEmittedPointer = true; - emit q->readyRead(); - emit q->channelReadyRead(0); - } - } -} - -void QSslSocketBackendPrivate::sendShutdown() -{ - const bool isClient = mode == QSslSocket::SslClientMode; - DWORD shutdownToken = SCHANNEL_SHUTDOWN; - SecBuffer buffer = createSecBuffer(&shutdownToken, sizeof(SCHANNEL_SHUTDOWN), 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 QSslSocketBackendPrivate::disconnectFromHost() -{ - if (SecIsValidHandle(&contextHandle)) { - if (!shutdown) { - shutdown = true; - if (plainSocket->state() != QAbstractSocket::UnconnectedState) { - if (connectionEncrypted) { - // Read as much as possible because this is likely our last chance - qint64 tempMax = readBufferMaxSize; - readBufferMaxSize = 0; - transmit(); - readBufferMaxSize = tempMax; - sendShutdown(); - } - } - } - } - if (plainSocket->state() != QAbstractSocket::UnconnectedState) - plainSocket->disconnectFromHost(); -} - -void QSslSocketBackendPrivate::disconnected() -{ - shutdown = true; - connectionEncrypted = false; - deallocateContext(); - freeCredentialsHandle(); -} - -QSslCipher QSslSocketBackendPrivate::sessionCipher() const -{ - if (!connectionEncrypted) - return QSslCipher(); - return QSslCipher(QStringLiteral("Schannel"), sessionProtocol()); -} - -QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const -{ - if (!connectionEncrypted) - return QSsl::SslProtocol::UnknownProtocol; - return toQtSslProtocol(connectionInfo.dwProtocol); -} - -void QSslSocketBackendPrivate::continueHandshake() -{ - Q_Q(QSslSocket); - const bool isServer = mode == 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 (!connectionEncrypted) { - connectionEncrypted = true; // all is done - emit q->encrypted(); - } - renegotiating = false; - if (pendingClose) { - pendingClose = false; - disconnectFromHost(); - } else { - transmit(); - } - break; - case SchannelState::Renegotiate: - if (!renegotiate()) { - disconnectFromHost(); - return; - } else if (intermediateBuffer.size() || plainSocket->bytesAvailable()) { - continueHandshake(); - } - break; - } -} - -QList<QSslCipher> QSslSocketBackendPrivate::defaultCiphers() -{ - 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) { - QSslCipher cipher; - cipher.d->isNull = false; - cipher.d->name = QStringLiteral("Schannel"); - cipher.d->protocol = protocols[i]; - cipher.d->protocolString = protocolStrings[i]; - ciphers.append(cipher); - } - - return ciphers; -} - -QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &certificateChain, - const QString &hostName) -{ - Q_UNUSED(certificateChain); - Q_UNUSED(hostName); - - Q_UNIMPLEMENTED(); - return {}; // @future implement(?) -} - -bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert, - QList<QSslCertificate> *caCertificates, - const QByteArray &passPhrase) -{ - Q_UNUSED(device); - Q_UNUSED(key); - Q_UNUSED(cert); - Q_UNUSED(caCertificates); - Q_UNUSED(passPhrase); - // @future: can load into its own certificate store (encountered problems extracting key). - Q_UNIMPLEMENTED(); - return false; -} - -/* - Copied from qsslsocket_mac.cpp, which was copied from qsslsocket_openssl.cpp -*/ -bool QSslSocketBackendPrivate::checkSslErrors() -{ - if (sslErrors.isEmpty()) - return true; - Q_Q(QSslSocket); - - emit q->sslErrors(sslErrors); - - const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer - || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer - && mode == QSslSocket::SslClientMode); - const bool doEmitSslError = !verifyErrorsHaveBeenIgnored(); - // check whether we need to emit an SSL handshake error - if (doVerifyPeer && doEmitSslError) { - if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) { - pauseSocketNotifiers(q); - paused = true; - } else { - setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - sslErrors.constFirst().errorString()); - plainSocket->disconnectFromHost(); - } - return false; - } - - return true; -} - -void QSslSocketBackendPrivate::initializeCertificateStores() -{ - //// helper function which turns a chain into a certificate store - 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()) { - 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 QSslSocketBackendPrivate::verifyCertContext(CERT_CONTEXT *certContext) -{ - Q_ASSERT(certContext); - Q_Q(QSslSocket); - - const bool isClient = mode == 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; - - configuration.peerCertificate.clear(); - configuration.peerCertificateChain.clear(); - 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 QSslCertificatePrivate::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 (configuration.peerVerifyDepth > 0 && DWORD(configuration.peerVerifyDepth) < verifyDepth) - verifyDepth = DWORD(configuration.peerVerifyDepth); - - 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(lcSsl) << "issuer:" << certificate.issuerDisplayName() - << "\nsubject:" << certificate.subjectDisplayName() - << "\nQSslCertificate info:" << certificate - << "\nextended error info:" << element->pwszExtendedErrorInfo - << "\nerror status:" << element->TrustStatus.dwErrorStatus; -#endif - - configuration.peerCertificateChain.append(certificate); - - 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 = configuration.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 (!configuration.peerCertificateChain.isEmpty()) - configuration.peerCertificate = configuration.peerCertificateChain.first(); - - // @Note: Somewhat copied from qsslsocket_mac.cpp - const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer - || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer - && 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. - 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 (mode == QSslSocket::SslClientMode) { - 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 QSslSocketBackendPrivate::rootCertOnDemandLoadingAllowed() -{ - return allowRootCertOnDemandLoading && s_loadRootCertsOnDemand; -} - -void QSslSocketPrivate::registerAdHocFactory() -{ - // TLSTODO: this is a temporary solution, waiting for - // backends to move to ... plugins. - if (!factory()) - qCWarning(lcSsl, "Failed to create backend factory"); -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_schannel_p.h b/src/network/ssl/qsslsocket_schannel_p.h deleted file mode 100644 index 57c8c75629..0000000000 --- a/src/network/ssl/qsslsocket_schannel_p.h +++ /dev/null @@ -1,148 +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 QSSLSOCKET_SCHANNEL_P_H -#define QSSLSOCKET_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. -// - -QT_REQUIRE_CONFIG(schannel); - -#include <QtNetwork/private/qtnetworkglobal_p.h> - -#include "qsslsocket_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 - -class QSslSocketBackendPrivate final : public QSslSocketPrivate -{ - Q_DISABLE_COPY_MOVE(QSslSocketBackendPrivate) - Q_DECLARE_PUBLIC(QSslSocket) -public: - QSslSocketBackendPrivate(); - ~QSslSocketBackendPrivate(); - - // Platform specific functions - 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; - - static QList<QSslCipher> defaultCiphers(); - static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain, - const QString &hostName); - static bool importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert, - QList<QSslCertificate> *caCertificates, const QByteArray &passPhrase); - -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(); - - 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; -}; - -QT_END_NAMESPACE - -#endif // QSSLSOCKET_SCHANNEL_P_H diff --git a/src/network/ssl/qtls_utils_p.h b/src/network/ssl/qtls_utils_p.h deleted file mode 100644 index 514d1fefd0..0000000000 --- a/src/network/ssl/qtls_utils_p.h +++ /dev/null @@ -1,99 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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_UTILS_P_H -#define QTLS_UTILS_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> - -#if QT_CONFIG(openssl) -#include <QtNetwork/private/qsslsocket_openssl_p.h> -#endif - -#include <QtNetwork/private/qssl_p.h> - -#include <QtCore/qglobal.h> -#include <QtCore/qdebug.h> - -#include <memory> - -QT_BEGIN_NAMESPACE - -namespace QTlslUtils -{ - -template <class NativeTlsType, void (*Deleter)(NativeTlsType *)> -void safe_delete(NativeTlsType *object) -{ - if (object) - Deleter(object); -} - -template<class NativeTlsType, int ok, int (*Deleter)(NativeTlsType *)> -void safe_delete(NativeTlsType *object) -{ - if (object) { - if (Deleter(object) != ok) { - qCWarning(lcSsl, "Failed to free a resource."); -#if QT_CONFIG(openssl) // || wolfssl later - QSslSocketBackendPrivate::logAndClearErrorQueue(); -#endif // QT_CONFIG(openssl) - } - } -} - -template<class NativeTlsType> -using Deleter = std::unique_ptr<NativeTlsType, void (*)(NativeTlsType *)>; - -} // namespace QTlsUtils - -QT_END_NAMESPACE - -#endif // QTLS_UTILS_P_H diff --git a/src/network/ssl/qtlsbackend.cpp b/src/network/ssl/qtlsbackend.cpp index 4183c1e2f1..761ab33fbe 100644 --- a/src/network/ssl/qtlsbackend.cpp +++ b/src/network/ssl/qtlsbackend.cpp @@ -1,48 +1,23 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qtlsbackend_p.h" + +#if QT_CONFIG(ssl) +#include "qsslpresharedkeyauthenticator_p.h" +#include "qsslpresharedkeyauthenticator.h" #include "qsslsocket_p.h" +#include "qsslcipher_p.h" +#include "qsslkey_p.h" +#include "qsslkey.h" +#endif + #include "qssl_p.h" #include <QtCore/private/qfactoryloader_p.h> +#include "QtCore/qapplicationstatic.h" +#include <QtCore/qbytearray.h> #include <QtCore/qmutex.h> #include <algorithm> @@ -50,250 +25,2333 @@ QT_BEGIN_NAMESPACE -Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, - (QTlsBackendFactory_iid, QStringLiteral("/tlsbackends"))) +using namespace Qt::StringLiterals; + +Q_APPLICATION_STATIC(QFactoryLoader, qtlsbLoader, QTlsBackend_iid, + QStringLiteral("/tls")) + +namespace { + +class BackendCollection +{ +public: + void addBackend(QTlsBackend *backend) + { + Q_ASSERT(backend); + Q_ASSERT(std::find(backends.begin(), backends.end(), backend) == backends.end()); + const QMutexLocker locker(&collectionMutex); + backends.push_back(backend); + } + + void removeBackend(QTlsBackend *backend) + { + Q_ASSERT(backend); + const QMutexLocker locker(&collectionMutex); + const auto it = std::find(backends.begin(), backends.end(), backend); + Q_ASSERT(it != backends.end()); + backends.erase(it); + } + + bool tryPopulateCollection() + { + if (!qtlsbLoader()) + return false; + + Q_CONSTINIT static QBasicMutex mutex; + const QMutexLocker locker(&mutex); + if (backends.size()) + return true; + +#if QT_CONFIG(library) + qtlsbLoader->update(); +#endif + int index = 0; + while (qtlsbLoader->instance(index)) + ++index; + + return true; + } + + QList<QString> backendNames() + { + QList<QString> names; + if (!tryPopulateCollection()) + return names; + + const QMutexLocker locker(&collectionMutex); + if (!backends.size()) + return names; + + names.reserve(backends.size()); + for (const auto *backend : backends) { + if (backend->isValid()) + names.append(backend->backendName()); + } -const QString QTlsBackendFactory::builtinBackendNames[] = { + return names; + } + + QTlsBackend *backend(const QString &name) + { + if (!tryPopulateCollection()) + return nullptr; + + const QMutexLocker locker(&collectionMutex); + const auto it = std::find_if(backends.begin(), backends.end(), + [&name](const auto *fct) {return fct->backendName() == name;}); + + return it == backends.end() ? nullptr : *it; + } + +private: + std::vector<QTlsBackend *> backends; + QMutex collectionMutex; +}; + +} // Unnamed namespace + +Q_GLOBAL_STATIC(BackendCollection, backends); + +/*! + \class QTlsBackend + \internal (Network-private) + \brief QTlsBackend is a factory class, providing implementations + for the QSsl classes. + + The purpose of QTlsBackend is to enable and simplify the addition + of new TLS backends to be used by QSslSocket and related classes. + Starting from Qt 6.1, these backends have plugin-based design (and + thus can co-exist simultaneously, unlike pre 6.1 times), although + any given run of a program can only use one of them. + + Inheriting from QTlsBackend and creating an object of such + a class adds a new backend into the list of available TLS backends. + + A new backend must provide a list of classes, features and protocols + it supports, and override the corresponding virtual functions that + create backend-specific implementations for these QSsl-classes. + + The base abstract class - QTlsBackend - provides, where possible, + default implementations of its virtual member functions. These + default implementations can be overridden by a derived backend + class, if needed. + + QTlsBackend also provides some auxiliary functions that a derived + backend class can use to interact with the internals of network-private classes. + + \sa QSslSocket::availableBackends(), supportedFeatures(), supportedProtocols(), implementedClasses() +*/ + +/*! + \fn QString QTlsBackend::backendName() const + \internal + Returns the name of this backend. The name will be reported by QSslSocket::availableBackends(). + Example of backend names: "openssl", "schannel", "securetransport". + + \sa QSslSocket::availableBackends(), isValid() +*/ + +const QString QTlsBackend::builtinBackendNames[] = { QStringLiteral("schannel"), QStringLiteral("securetransport"), - QStringLiteral("openssl") + QStringLiteral("openssl"), + QStringLiteral("cert-only") }; +/*! + \internal + The default constructor, adds a new backend to the list of available backends. -QTlsBackend::QTlsBackend() = default; -QTlsBackend::~QTlsBackend() = default; + \sa ~QTlsBackend(), availableBackendNames(), QSslSocket::availableBackends() +*/ +QTlsBackend::QTlsBackend() +{ + if (backends()) + backends->addBackend(this); + + if (QCoreApplication::instance()) { + connect(QCoreApplication::instance(), &QCoreApplication::destroyed, this, [this] { + delete this; + }); + } +} -const QString dummyName = QStringLiteral("dummyTLS"); +/*! + \internal + Removes this backend from the list of available backends. -QString QTlsBackend::backendName() const + \sa QTlsBackend(), availableBackendNames(), QSslSocket::availableBackends() +*/ +QTlsBackend::~QTlsBackend() { - return dummyName; + if (backends()) + backends->removeBackend(this); } -QSsl::TlsKey *QTlsBackend::createKey() const +/*! + \internal + Returns \c true if this backend was initialised successfully. The default implementation + always returns \c true. + + \note This function must be overridden if a particular backend has a non-trivial initialization + that can fail. If reimplemented, returning \c false will exclude this backend from the list of + backends, reported as available by QSslSocket. + + \sa QSslSocket::availableBackends() +*/ + +bool QTlsBackend::isValid() const { - qCWarning(lcSsl, "Dummy TLS backend, cannot generate a key"); - return nullptr; + return true; } -QSsl::X509Certificate *QTlsBackend::createCertificate() const +/*! + \internal + Returns an implementations-specific integer value, representing the TLS library's + version, that is currently used by this backend (i.e. runtime library version). + The default implementation returns 0. + + \sa tlsLibraryBuildVersionNumber() +*/ +long QTlsBackend::tlsLibraryVersionNumber() const { - qCWarning(lcSsl, "Dummy TLS backend, cannot create a certificate"); + return 0; +} + +/*! + \internal + Returns an implementation-specific string, representing the TLS library's version, + that is currently used by this backend (i.e. runtime library version). The default + implementation returns an empty string. + + \sa tlsLibraryBuildVersionString() +*/ + +QString QTlsBackend::tlsLibraryVersionString() const +{ + return {}; +} + +/*! + \internal + Returns an implementation-specific integer value, representing the TLS library's + version that this backend was built against (i.e. compile-time library version). + The default implementation returns 0. + + \sa tlsLibraryVersionNumber() +*/ + +long QTlsBackend::tlsLibraryBuildVersionNumber() const +{ + return 0; +} + +/*! + \internal + Returns an implementation-specific string, representing the TLS library's version + that this backend was built against (i.e. compile-time version). The default + implementation returns an empty string. + + \sa tlsLibraryVersionString() +*/ +QString QTlsBackend::tlsLibraryBuildVersionString() const +{ + return {}; +} + +/*! + \internal + QSslSocket and related classes call this function to ensure that backend's internal + resources - e.g. CA certificates, or ciphersuites - were properly initialized. +*/ +void QTlsBackend::ensureInitialized() const +{ +} + +#define REPORT_MISSING_SUPPORT(message) \ + qCWarning(lcSsl) << "The backend" << backendName() << message + +/*! + \internal + If QSsl::ImplementedClass::Key is present in this backend's implementedClasses(), + the backend must reimplement this method to return a dynamically-allocated instance + of an implementation-specific type, inheriting from the class QTlsPrivate::TlsKey. + The default implementation of this function returns \nullptr. + + \sa QSslKey, implementedClasses(), QTlsPrivate::TlsKey +*/ +QTlsPrivate::TlsKey *QTlsBackend::createKey() const +{ + REPORT_MISSING_SUPPORT("does not support QSslKey"); return nullptr; } -QSsl::TlsCryptograph *QTlsBackend::createTlsCryptograph() const +/*! + \internal + If QSsl::ImplementedClass::Certificate is present in this backend's implementedClasses(), + the backend must reimplement this method to return a dynamically-allocated instance of an + implementation-specific type, inheriting from the class QTlsPrivate::X509Certificate. + The default implementation of this function returns \nullptr. + + \sa QSslCertificate, QTlsPrivate::X509Certificate, implementedClasses() +*/ +QTlsPrivate::X509Certificate *QTlsBackend::createCertificate() const { - qCWarning(lcSsl, "Dummy TLS backend, cannot create TLS session"); + REPORT_MISSING_SUPPORT("does not support QSslCertificate"); return nullptr; } -QSsl::DtlsCryptograph *QTlsBackend::createDtlsCryptograph() const +/*! + \internal + This function returns a list of system CA certificates - e.g. certificates, loaded + from a system store, if available. This function allows implementation of the class + QSslConfiguration. The default implementation of this function returns an empty list. + + \sa QSslCertificate, QSslConfiguration +*/ +QList<QSslCertificate> QTlsBackend::systemCaCertificates() const +{ + REPORT_MISSING_SUPPORT("does not provide system CA certificates"); + return {}; +} + +/*! + \internal + If QSsl::ImplementedClass::Socket is present in this backend's implementedClasses(), + the backend must reimplement this method to return a dynamically-allocated instance of an + implementation-specific type, inheriting from the class QTlsPrivate::TlsCryptograph. + The default implementation of this function returns \nullptr. + + \sa QSslSocket, QTlsPrivate::TlsCryptograph, implementedClasses() +*/ +QTlsPrivate::TlsCryptograph *QTlsBackend::createTlsCryptograph() const { - qCWarning(lcSsl, "Dummy TLS backend, cannot create DTLS session"); + REPORT_MISSING_SUPPORT("does not support QSslSocket"); return nullptr; } -QSsl::DtlsCookieVerifier *QTlsBackend::createDtlsCookieVerifier() const +/*! + \internal + If QSsl::ImplementedClass::Dtls is present in this backend's implementedClasses(), + the backend must reimplement this method to return a dynamically-allocated instance of an + implementation-specific type, inheriting from the class QTlsPrivate::DtlsCryptograph. + The default implementation of this function returns \nullptr. + + \sa QDtls, QTlsPrivate::DtlsCryptograph, implementedClasses() +*/ +QTlsPrivate::DtlsCryptograph *QTlsBackend::createDtlsCryptograph(QDtls *qObject, int mode) const { - qCWarning(lcSsl, "Dummy TLS backend, cannot create DTLS cookie generator/verifier"); + Q_UNUSED(qObject); + Q_UNUSED(mode); + REPORT_MISSING_SUPPORT("does not support QDtls"); return nullptr; } -QSsl::X509ChainVerifyPtr QTlsBackend::X509Verifier() const +/*! + \internal + If QSsl::ImplementedClass::DtlsCookie is present in this backend's implementedClasses(), + the backend must reimplement this method to return a dynamically-allocated instance of an + implementation-specific type, inheriting from the class QTlsPrivate::DtlsCookieVerifier. The + default implementation returns \nullptr. + + \sa QDtlsClientVerifier, QTlsPrivate::DtlsCookieVerifier, implementedClasses() +*/ +QTlsPrivate::DtlsCookieVerifier *QTlsBackend::createDtlsCookieVerifier() const { - qCWarning(lcSsl, "Dummy TLS backend, cannot verify X509 chain"); + REPORT_MISSING_SUPPORT("does not support DTLS cookies"); return nullptr; } -QSsl::X509PemReaderPtr QTlsBackend::X509PemReader() const +/*! + \internal + If QSsl::SupportedFeature::CertificateVerification is present in this backend's + supportedFeatures(), the backend must reimplement this method to return a pointer + to a function, that checks a certificate (or a chain of certificates) against available + CA certificates. The default implementation returns \nullptr. + + \sa supportedFeatures(), QSslCertificate +*/ + +QTlsPrivate::X509ChainVerifyPtr QTlsBackend::X509Verifier() const { - qCWarning(lcSsl, "Dummy TLS backend, cannot read PEM format"); + REPORT_MISSING_SUPPORT("does not support (manual) certificate verification"); return nullptr; } -QSsl::X509DerReaderPtr QTlsBackend::X509DerReader() const +/*! + \internal + Returns a pointer to function, that reads certificates in PEM format. The + default implementation returns \nullptr. + + \sa QSslCertificate +*/ +QTlsPrivate::X509PemReaderPtr QTlsBackend::X509PemReader() const { - qCWarning(lcSsl, "Dummy TLS backend, don't know how to read DER"); + REPORT_MISSING_SUPPORT("cannot read PEM format"); return nullptr; } -QSsl::X509Pkcs12ReaderPtr QTlsBackend::X509Pkcs12Reader() const +/*! + \internal + Returns a pointer to function, that can read certificates in DER format. + The default implementation returns \nullptr. + + \sa QSslCertificate +*/ +QTlsPrivate::X509DerReaderPtr QTlsBackend::X509DerReader() const { - qCWarning(lcSsl, "Dummy TLS backend, cannot read PKCS12"); + REPORT_MISSING_SUPPORT("cannot read DER format"); return nullptr; } -namespace { +/*! + \internal + Returns a pointer to function, that can read certificates in PKCS 12 format. + The default implementation returns \nullptr. -class BackEndFactoryCollection + \sa QSslCertificate +*/ +QTlsPrivate::X509Pkcs12ReaderPtr QTlsBackend::X509Pkcs12Reader() const { -public: - void addFactory(QTlsBackendFactory *newFactory) - { - Q_ASSERT(newFactory); - Q_ASSERT(std::find(backendFactories.begin(), backendFactories.end(), newFactory) == backendFactories.end()); - const QMutexLocker locker(&collectionMutex); - backendFactories.push_back(newFactory); - } + REPORT_MISSING_SUPPORT("cannot read PKCS12 format"); + return nullptr; +} - void removeFactory(QTlsBackendFactory *factory) - { - Q_ASSERT(factory); - const QMutexLocker locker(&collectionMutex); - const auto it = std::find(backendFactories.begin(), backendFactories.end(), factory); - Q_ASSERT(it != backendFactories.end()); - backendFactories.erase(it); - } +/*! + \internal + If QSsl::ImplementedClass::EllipticCurve is present in this backend's implementedClasses(), + and the backend provides information about supported curves, it must reimplement this + method to return a list of unique identifiers of the supported elliptic curves. The default + implementation returns an empty list. - bool tryPopulateCollection() - { - if (!loader()) - return false; + \note The meaning of a curve identifier is implementation-specific. - static QBasicMutex mutex; - const QMutexLocker locker(&mutex); - if (loaded) - return true; + \sa implemenedClasses(), QSslEllipticCurve +*/ +QList<int> QTlsBackend::ellipticCurvesIds() const +{ + REPORT_MISSING_SUPPORT("does not support QSslEllipticCurve"); + return {}; +} -#if QT_CONFIG(library) - loader->update(); -#endif - int index = 0; - while (loader->instance(index)) - ++index; +/*! + \internal + If this backend provides information about available elliptic curves, this + function should return a unique integer identifier for a curve named \a name, + which is a conventional short name for the curve. The default implementation + returns 0. - // TLSTODO: obviously, this one should go away: - QSslSocketPrivate::registerAdHocFactory(); + \note The meaning of a curve identifier is implementation-specific. - return loaded = true; - } + \sa QSslEllipticCurve::shortName() +*/ +int QTlsBackend::curveIdFromShortName(const QString &name) const +{ + Q_UNUSED(name); + REPORT_MISSING_SUPPORT("does not support QSslEllipticCurve"); + return 0; +} - QList<QString> backendNames() - { - QList<QString> names; - if (!tryPopulateCollection()) - return names; +/*! + \internal + If this backend provides information about available elliptic curves, this + function should return a unique integer identifier for a curve named \a name, + which is a conventional long name for the curve. The default implementation + returns 0. - const QMutexLocker locker(&collectionMutex); - if (!backendFactories.size()) - return names; + \note The meaning of a curve identifier is implementation-specific. - names.reserve(backendFactories.size()); - for (const auto *factory : backendFactories) - names.append(factory->backendName()); + \sa QSslElliptiCurve::longName() +*/ +int QTlsBackend::curveIdFromLongName(const QString &name) const +{ + Q_UNUSED(name); + REPORT_MISSING_SUPPORT("does not support QSslEllipticCurve"); + return 0; +} - return names; - } +/*! + \internal + If this backend provides information about available elliptic curves, + this function should return a conventional short name for a curve identified + by \a cid. The default implementation returns an empty string. - QTlsBackendFactory *factory(const QString &name) - { - if (!tryPopulateCollection()) - return nullptr; + \note The meaning of a curve identifier is implementation-specific. - const QMutexLocker locker(&collectionMutex); - const auto it = std::find_if(backendFactories.begin(), backendFactories.end(), - [&name](const auto *fct) {return fct->backendName() == name;}); + \sa ellipticCurvesIds(), QSslEllipticCurve::shortName() +*/ +QString QTlsBackend::shortNameForId(int cid) const +{ + Q_UNUSED(cid); + REPORT_MISSING_SUPPORT("does not support QSslEllipticCurve"); + return {}; +} - return it == backendFactories.end() ? nullptr : *it; - } +/*! + \internal + If this backend provides information about available elliptic curves, + this function should return a conventional long name for a curve identified + by \a cid. The default implementation returns an empty string. -private: - std::vector<QTlsBackendFactory *> backendFactories; - QMutex collectionMutex; - bool loaded = false; -}; + \note The meaning of a curve identifier is implementation-specific. -Q_GLOBAL_STATIC(BackEndFactoryCollection, factories); + \sa ellipticCurvesIds(), QSslEllipticCurve::shortName() +*/ +QString QTlsBackend::longNameForId(int cid) const +{ + Q_UNUSED(cid); + REPORT_MISSING_SUPPORT("does not support QSslEllipticCurve"); + return {}; +} -} // unnamed namespace +/*! + \internal + Returns true if the elliptic curve identified by \a cid is one of the named + curves, that can be used in the key exchange when using an elliptic curve + cipher with TLS; false otherwise. The default implementation returns false. -QTlsBackendFactory::QTlsBackendFactory() + \note The meaning of curve identifier is implementation-specific. +*/ +bool QTlsBackend::isTlsNamedCurve(int cid) const { - if (factories()) - factories->addFactory(this); + Q_UNUSED(cid); + REPORT_MISSING_SUPPORT("does not support QSslEllipticCurve"); + return false; } -QTlsBackendFactory::~QTlsBackendFactory() +/*! + \internal + If this backend supports the class QSslDiffieHellmanParameters, this function is + needed for construction of a QSslDiffieHellmanParameters from DER encoded data. + This function is expected to return a value that matches an enumerator in + QSslDiffieHellmanParameters::Error enumeration. The default implementation of this + function returns 0 (equals to QSslDiffieHellmanParameters::NoError). + + \sa QSslDiffieHellmanParameters, implementedClasses() +*/ +int QTlsBackend::dhParametersFromDer(const QByteArray &derData, QByteArray *data) const { - if (factories()) - factories->removeFactory(this); + Q_UNUSED(derData); + Q_UNUSED(data); + REPORT_MISSING_SUPPORT("does not support QSslDiffieHellmanParameters in DER format"); + return {}; } -QString QTlsBackendFactory::backendName() const +/*! + \internal + If this backend supports the class QSslDiffieHellmanParameters, this function is + is needed for construction of a QSslDiffieHellmanParameters from PEM encoded data. + This function is expected to return a value that matches an enumerator in + QSslDiffieHellmanParameters::Error enumeration. The default implementation of this + function returns 0 (equals to QSslDiffieHellmanParameters::NoError). + + \sa QSslDiffieHellmanParameters, implementedClasses() +*/ +int QTlsBackend::dhParametersFromPem(const QByteArray &pemData, QByteArray *data) const { - return dummyName; + Q_UNUSED(pemData); + Q_UNUSED(data); + REPORT_MISSING_SUPPORT("does not support QSslDiffieHellmanParameters in PEM format"); + return {}; } -QList<QString> QTlsBackendFactory::availableBackendNames() +/*! + \internal + Returns a list of names of available backends. + + \note This list contains only properly initialized backends. + + \sa QTlsBackend(), isValid() +*/ +QList<QString> QTlsBackend::availableBackendNames() { - if (!factories()) + if (!backends()) return {}; - return factories->backendNames(); + return backends->backendNames(); } -QString QTlsBackendFactory::defaultBackendName() +/*! + \internal + Returns the name of the backend that QSslSocket() would use by default. If no + backend was found, the function returns an empty string. +*/ +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]; + return {}; } -QTlsBackend *QTlsBackendFactory::create(const QString &backendName) +/*! + \internal + Returns a backend named \a backendName, if it exists. + Otherwise, it returns \nullptr. + + \sa backendName(), QSslSocket::availableBackends() +*/ +QTlsBackend *QTlsBackend::findBackend(const QString &backendName) { - if (!factories()) + if (!backends()) return {}; - if (const auto *fct = factories->factory(backendName)) - return fct->create(); + if (auto *fct = backends->backend(backendName)) + return fct; qCWarning(lcSsl) << "Cannot create unknown backend named" << backendName; return nullptr; } -QList<QSsl::SslProtocol> QTlsBackendFactory::supportedProtocols(const QString &backendName) +/*! + \internal + Returns the backend that QSslSocket is using. If Qt was built without TLS support, + this function returns a minimal backend that only supports QSslCertificate. + + \sa defaultBackend() +*/ +QTlsBackend *QTlsBackend::activeOrAnyBackend() +{ +#if QT_CONFIG(ssl) + return QSslSocketPrivate::tlsBackendInUse(); +#else + return findBackend(defaultBackendName()); +#endif // QT_CONFIG(ssl) +} + +/*! + \internal + Returns a list of TLS and DTLS protocol versions, that a backend named + \a backendName supports. + + \note This list is supposed to also include range-based versions, which + allows negotiation of protocols during the handshake, so that these versions + can be used when configuring QSslSocket (e.g. QSsl::TlsV1_2OrLater). + + \sa QSsl::SslProtocol +*/ +QList<QSsl::SslProtocol> QTlsBackend::supportedProtocols(const QString &backendName) { - if (!factories()) + if (!backends()) return {}; - if (const auto *fct = factories->factory(backendName)) + if (const auto *fct = backends->backend(backendName)) return fct->supportedProtocols(); return {}; } -QList<QSsl::SupportedFeature> QTlsBackendFactory::supportedFeatures(const QString &backendName) +/*! + \internal + Returns a list of features that a backend named \a backendName supports. E.g. + a backend may support PSK (pre-shared keys, defined as QSsl::SupportedFeature::Psk) + or ALPN (application layer protocol negotiation, identified by + QSsl::SupportedFeature::ClientSideAlpn or QSsl::SupportedFeature::ServerSideAlpn). + + \sa QSsl::SupportedFeature +*/ +QList<QSsl::SupportedFeature> QTlsBackend::supportedFeatures(const QString &backendName) { - if (!factories()) + if (!backends()) return {}; - if (const auto *fct = factories->factory(backendName)) + if (const auto *fct = backends->backend(backendName)) return fct->supportedFeatures(); return {}; } -QList<QSsl::ImplementedClass> QTlsBackendFactory::implementedClasses(const QString &backendName) +/*! + \internal + Returns a list of classes that a backend named \a backendName supports. E.g. a backend + may implement QSslSocket (QSsl::ImplementedClass::Socket), and QDtls + (QSsl::ImplementedClass::Dtls). + + \sa QSsl::ImplementedClass +*/ +QList<QSsl::ImplementedClass> QTlsBackend::implementedClasses(const QString &backendName) { - if (!factories()) + if (!backends()) return {}; - if (const auto *fct = factories->factory(backendName)) + if (const auto *fct = backends->backend(backendName)) return fct->implementedClasses(); return {}; +} + +/*! + \internal + Auxiliary function. Initializes \a key to use \a keyBackend. +*/ +void QTlsBackend::resetBackend(QSslKey &key, QTlsPrivate::TlsKey *keyBackend) +{ +#if QT_CONFIG(ssl) + key.d->backend.reset(keyBackend); +#else + Q_UNUSED(key); + Q_UNUSED(keyBackend); +#endif // QT_CONFIG(ssl) +} + +/*! + \internal + Auxiliary function. Initializes client-side \a auth using the \a hint, \a hintLength, + \a maxIdentityLength and \a maxPskLen. +*/ +void QTlsBackend::setupClientPskAuth(QSslPreSharedKeyAuthenticator *auth, const char *hint, + int hintLength, unsigned maxIdentityLen, unsigned maxPskLen) +{ + Q_ASSERT(auth); +#if QT_CONFIG(ssl) + if (hint) + auth->d->identityHint = QByteArray::fromRawData(hint, hintLength); // it's NUL terminated, but do not include the NUL + + auth->d->maximumIdentityLength = int(maxIdentityLen) - 1; // needs to be NUL terminated + auth->d->maximumPreSharedKeyLength = int(maxPskLen); +#else + Q_UNUSED(auth); + Q_UNUSED(hint); + Q_UNUSED(hintLength); + Q_UNUSED(maxIdentityLen); + Q_UNUSED(maxPskLen); +#endif +} + +/*! + \internal + Auxiliary function. Initializes server-side \a auth using the \a identity, \a identityHint and + \a maxPskLen. +*/ +void QTlsBackend::setupServerPskAuth(QSslPreSharedKeyAuthenticator *auth, const char *identity, + const QByteArray &identityHint, unsigned int maxPskLen) +{ +#if QT_CONFIG(ssl) + Q_ASSERT(auth); + auth->d->identityHint = identityHint; + auth->d->identity = identity; + auth->d->maximumIdentityLength = 0; // user cannot set an identity + auth->d->maximumPreSharedKeyLength = int(maxPskLen); +#else + Q_UNUSED(auth); + Q_UNUSED(identity); + Q_UNUSED(identityHint); + Q_UNUSED(maxPskLen); +#endif +} + +#if QT_CONFIG(ssl) +/*! + \internal + Auxiliary function. Creates a new QSslCipher from \a descriptionOneLine, \a bits + and \a supportedBits. \a descriptionOneLine consists of several fields, separated by + whitespace. These include: cipher name, protocol version, key exchange method, + authentication method, encryption method, message digest (Mac). Example: + "ECDHE-RSA-AES256-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD" +*/ +QSslCipher QTlsBackend::createCiphersuite(const QString &descriptionOneLine, int bits, int supportedBits) +{ + QSslCipher ciph; + const auto descriptionList = QStringView{descriptionOneLine}.split(u' ', Qt::SkipEmptyParts); + if (descriptionList.size() > 5) { + ciph.d->isNull = false; + ciph.d->name = descriptionList.at(0).toString(); + + QStringView protoString = descriptionList.at(1); + ciph.d->protocolString = protoString.toString(); + ciph.d->protocol = QSsl::UnknownProtocol; +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED + if (protoString.startsWith(u"TLSv1")) { + QStringView tail = protoString.sliced(5); + if (tail.startsWith(u'.')) { + tail = tail.sliced(1); + if (tail == u"3") + ciph.d->protocol = QSsl::TlsV1_3; + else if (tail == u"2") + ciph.d->protocol = QSsl::TlsV1_2; + else if (tail == u"1") + ciph.d->protocol = QSsl::TlsV1_1; + } else if (tail.isEmpty()) { + ciph.d->protocol = QSsl::TlsV1_0; + } + } +QT_WARNING_POP + + if (descriptionList.at(2).startsWith("Kx="_L1)) + ciph.d->keyExchangeMethod = descriptionList.at(2).mid(3).toString(); + if (descriptionList.at(3).startsWith("Au="_L1)) + ciph.d->authenticationMethod = descriptionList.at(3).mid(3).toString(); + if (descriptionList.at(4).startsWith("Enc="_L1)) + ciph.d->encryptionMethod = descriptionList.at(4).mid(4).toString(); + ciph.d->exportable = (descriptionList.size() > 6 && descriptionList.at(6) == "export"_L1); + + ciph.d->bits = bits; + ciph.d->supportedBits = supportedBits; + } + + return ciph; +} + +/*! + \internal + Auxiliary function. Creates a new QSslCipher from \a suiteName, \a protocol version and + \a protocolString. For example: + \code + createCiphersuite("ECDHE-RSA-AES256-GCM-SHA256"_L1, QSsl::TlsV1_2, "TLSv1.2"_L1); + \endcode +*/ +QSslCipher QTlsBackend::createCiphersuite(const QString &suiteName, QSsl::SslProtocol protocol, + const QString &protocolString) +{ + QSslCipher ciph; + + if (!suiteName.size()) + return ciph; + + ciph.d->isNull = false; + ciph.d->name = suiteName; + ciph.d->protocol = protocol; + ciph.d->protocolString = protocolString; + + const auto bits = QStringView{ciph.d->name}.split(u'-'); + if (bits.size() >= 2) { + if (bits.size() == 2 || bits.size() == 3) + ciph.d->keyExchangeMethod = "RSA"_L1; + else if (bits.front() == "DH"_L1 || bits.front() == "DHE"_L1) + ciph.d->keyExchangeMethod = "DH"_L1; + else if (bits.front() == "ECDH"_L1 || bits.front() == "ECDHE"_L1) + ciph.d->keyExchangeMethod = "ECDH"_L1; + else + qCWarning(lcSsl) << "Unknown Kx" << ciph.d->name; + + if (bits.size() == 2 || bits.size() == 3) + ciph.d->authenticationMethod = "RSA"_L1; + else if (ciph.d->name.contains("-ECDSA-"_L1)) + ciph.d->authenticationMethod = "ECDSA"_L1; + else if (ciph.d->name.contains("-RSA-"_L1)) + ciph.d->authenticationMethod = "RSA"_L1; + else + qCWarning(lcSsl) << "Unknown Au" << ciph.d->name; + + if (ciph.d->name.contains("RC4-"_L1)) { + ciph.d->encryptionMethod = "RC4(128)"_L1; + ciph.d->bits = 128; + ciph.d->supportedBits = 128; + } else if (ciph.d->name.contains("DES-CBC3-"_L1)) { + ciph.d->encryptionMethod = "3DES(168)"_L1; + ciph.d->bits = 168; + ciph.d->supportedBits = 168; + } else if (ciph.d->name.contains("AES128-"_L1)) { + ciph.d->encryptionMethod = "AES(128)"_L1; + ciph.d->bits = 128; + ciph.d->supportedBits = 128; + } else if (ciph.d->name.contains("AES256-GCM"_L1)) { + ciph.d->encryptionMethod = "AESGCM(256)"_L1; + ciph.d->bits = 256; + ciph.d->supportedBits = 256; + } else if (ciph.d->name.contains("AES256-"_L1)) { + ciph.d->encryptionMethod = "AES(256)"_L1; + ciph.d->bits = 256; + ciph.d->supportedBits = 256; + } else if (ciph.d->name.contains("CHACHA20-"_L1)) { + ciph.d->encryptionMethod = "CHACHA20"_L1; + ciph.d->bits = 256; + ciph.d->supportedBits = 256; + } else if (ciph.d->name.contains("NULL-"_L1)) { + ciph.d->encryptionMethod = "NULL"_L1; + } else { + qCWarning(lcSsl) << "Unknown Enc" << ciph.d->name; + } + } + return ciph; +} + +/*! + \internal + Auxiliary function. Creates a new QSslCipher from \a name, \a keyExchangeMethod, \a encryptionMethod, + \a authenticationMethod, \a bits, \a protocol version and \a protocolString. + For example: + \code + createCiphersuite("ECDHE-RSA-AES256-GCM-SHA256"_L1, "ECDH"_L1, "AES"_L1, "RSA"_L1, 256, + QSsl::TlsV1_2, "TLSv1.2"_L1); + \endcode +*/ +QSslCipher QTlsBackend::createCiphersuite(const QString &name, const QString &keyExchangeMethod, + const QString &encryptionMethod, + const QString &authenticationMethod, + int bits, QSsl::SslProtocol protocol, + const QString &protocolString) +{ + QSslCipher cipher; + cipher.d->isNull = false; + cipher.d->name = name; + cipher.d->bits = bits; + cipher.d->supportedBits = bits; + cipher.d->keyExchangeMethod = keyExchangeMethod; + cipher.d->encryptionMethod = encryptionMethod; + cipher.d->authenticationMethod = authenticationMethod; + cipher.d->protocol = protocol; + cipher.d->protocolString = protocolString; + return cipher; +} + +/*! + \internal + Returns an implementation-specific list of ciphersuites that can be used by QSslSocket. + + \sa QSslConfiguration::defaultCiphers() +*/ +QList<QSslCipher> QTlsBackend::defaultCiphers() +{ + return QSslSocketPrivate::defaultCiphers(); +} + +/*! + \internal + Returns an implementation-specific list of ciphersuites that can be used by QDtls. +*/ +QList<QSslCipher> QTlsBackend::defaultDtlsCiphers() +{ + return QSslSocketPrivate::defaultDtlsCiphers(); +} + +/*! + \internal + Sets \a ciphers as defaults ciphers that QSslSocket can use. + + \sa defaultCiphers() +*/ +void QTlsBackend::setDefaultCiphers(const QList<QSslCipher> &ciphers) +{ + QSslSocketPrivate::setDefaultCiphers(ciphers); +} + +/*! + \internal + Sets \a ciphers as defaults ciphers that QDtls can use. + + \sa defaultDtlsCiphers() +*/ +void QTlsBackend::setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers) +{ + QSslSocketPrivate::setDefaultDtlsCiphers(ciphers); +} + +/*! + \internal + Sets \a ciphers as a list of supported ciphers. + + \sa QSslConfiguration::supportedCiphers() +*/ +void QTlsBackend::setDefaultSupportedCiphers(const QList<QSslCipher> &ciphers) +{ + QSslSocketPrivate::setDefaultSupportedCiphers(ciphers); } +/*! + \internal + Sets the list of QSslEllipticCurve objects, that QSslConfiguration::supportedEllipticCurves() + returns, to ones that are supported by this backend. +*/ +void QTlsBackend::resetDefaultEllipticCurves() +{ + QSslSocketPrivate::resetDefaultEllipticCurves(); +} + +/*! + Sets \a certs as a list of certificates, that QSslConfiguration::caCertificates() + reports. + + \sa QSslConfiguration::defaultConfiguration(), QSslConfiguration::caCertificates() +*/ +void QTlsBackend::setDefaultCaCertificates(const QList<QSslCertificate> &certs) +{ + QSslSocketPrivate::setDefaultCaCertificates(certs); +} + +/*! + \internal + Returns true if \a configuration allows loading root certificates on demand. +*/ +bool QTlsBackend::rootLoadingOnDemandAllowed(const QSslConfiguration &configuration) +{ + return configuration.d->allowRootCertOnDemandLoading; +} + +/*! + \internal + Stores \a peerCert in the \a configuration. +*/ +void QTlsBackend::storePeerCertificate(QSslConfiguration &configuration, + const QSslCertificate &peerCert) +{ + configuration.d->peerCertificate = peerCert; +} + +/*! + \internal + Stores \a peerChain in the \a configuration. +*/ +void QTlsBackend::storePeerCertificateChain(QSslConfiguration &configuration, + const QList<QSslCertificate> &peerChain) +{ + configuration.d->peerCertificateChain = peerChain; +} + +/*! + \internal + Clears the peer certificate chain in \a configuration. +*/ +void QTlsBackend::clearPeerCertificates(QSslConfiguration &configuration) +{ + configuration.d->peerCertificate.clear(); + configuration.d->peerCertificateChain.clear(); +} + +/*! + \internal + Clears the peer certificate chain in \a d. +*/ +void QTlsBackend::clearPeerCertificates(QSslSocketPrivate *d) +{ + Q_ASSERT(d); + d->configuration.peerCertificate.clear(); + d->configuration.peerCertificateChain.clear(); +} + +/*! + \internal + Updates the configuration in \a d with \a shared value. +*/ +void QTlsBackend::setPeerSessionShared(QSslSocketPrivate *d, bool shared) +{ + Q_ASSERT(d); + d->configuration.peerSessionShared = shared; +} + +/*! + \internal + Sets TLS session in \a d to \a asn1. +*/ +void QTlsBackend::setSessionAsn1(QSslSocketPrivate *d, const QByteArray &asn1) +{ + Q_ASSERT(d); + d->configuration.sslSession = asn1; +} + +/*! + \internal + Sets TLS session lifetime hint in \a d to \a hint. +*/ +void QTlsBackend::setSessionLifetimeHint(QSslSocketPrivate *d, int hint) +{ + Q_ASSERT(d); + d->configuration.sslSessionTicketLifeTimeHint = hint; +} + +/*! + \internal + Sets application layer protocol negotiation status in \a d to \a st. +*/ +void QTlsBackend::setAlpnStatus(QSslSocketPrivate *d, AlpnNegotiationStatus st) +{ + Q_ASSERT(d); + d->configuration.nextProtocolNegotiationStatus = st; +} + +/*! + \internal + Sets \a protocol in \a d as a negotiated application layer protocol. +*/ +void QTlsBackend::setNegotiatedProtocol(QSslSocketPrivate *d, const QByteArray &protocol) +{ + Q_ASSERT(d); + d->configuration.nextNegotiatedProtocol = protocol; +} + +/*! + \internal + Stores \a peerCert in the TLS configuration of \a d. +*/ +void QTlsBackend::storePeerCertificate(QSslSocketPrivate *d, const QSslCertificate &peerCert) +{ + Q_ASSERT(d); + d->configuration.peerCertificate = peerCert; +} + +/*! + \internal + + Stores \a peerChain in the TLS configuration of \a d. + + \note This is a helper function that TlsCryptograph and DtlsCryptograph + call during a handshake. +*/ +void QTlsBackend::storePeerCertificateChain(QSslSocketPrivate *d, + const QList<QSslCertificate> &peerChain) +{ + Q_ASSERT(d); + d->configuration.peerCertificateChain = peerChain; +} + +/*! + \internal + + Adds \a rootCert to the list of trusted root certificates in \a d. + + \note In Qt 6.1 it's only used on Windows, during so called 'CA fetch'. +*/ +void QTlsBackend::addTustedRoot(QSslSocketPrivate *d, const QSslCertificate &rootCert) +{ + Q_ASSERT(d); + if (!d->configuration.caCertificates.contains(rootCert)) + d->configuration.caCertificates += rootCert; +} + +/*! + \internal + + Saves ephemeral \a key in \a d. + + \sa QSslConfiguration::ephemeralKey() +*/ +void QTlsBackend::setEphemeralKey(QSslSocketPrivate *d, const QSslKey &key) +{ + Q_ASSERT(d); + d->configuration.ephemeralServerKey = key; +} + +/*! + \internal + + Implementation-specific. Sets the security level suitable for Qt's + auto-tests. +*/ +void QTlsBackend::forceAutotestSecurityLevel() +{ +} + +#endif // QT_CONFIG(ssl) + +namespace QTlsPrivate { + +/*! + \internal (Network-private) + \namespace QTlsPrivate + \brief Namespace containing onternal types that TLS backends implement. + + This namespace is private to Qt and the backends that implement its TLS support. +*/ + +/*! + \class TlsKey + \internal (Network-private) + \brief TlsKey is an abstract class, that allows a TLS plugin to provide + an underlying implementation for the class QSslKey. + + Most functions in the class TlsKey are pure virtual and thus have to be + reimplemented by a TLS backend that supports QSslKey. In many cases an + empty implementation as an overrider is sufficient, albeit with some + of QSslKey's functionality missing. + + \sa QTlsBackend::createKey(), QTlsBackend::implementedClasses(), QSslKey +*/ + +/*! + \fn void TlsKey::decodeDer(KeyType type, KeyAlgorithm algorithm, const QByteArray &der, const QByteArray &passPhrase, bool deepClear) + \internal + + If a support of public and private keys in DER format is required, this function + must be overridden and should initialize this key using the \a type, \a algorithm, \a der + and \a passPhrase. If this key was initialized previously, \a deepClear + has an implementation-specific meaning (e.g., if an implementation is using + reference-counting and can share internally some data structures, a value \c true may + trigger decrementing a reference counter on some implementation-specific object). + + \note An empty overrider is sufficient, but then reading keys in QSsl::Der format + will not be supported. + + \sa isNull(), QSsl::KeyType, QSsl::EncodingFormat, QSsl::KeyAlgorithm +*/ + +/*! + \fn void TlsKey::decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem, const QByteArray &passPhrase, bool deepClear) + \internal + + If a support of public and private keys in PEM format is required, this function must + be overridden and should initialize this key using the \a type, \a algorithm, \a pem and + \a passPhrase. If this key was initialized previously, \a deepClear has an + implementation-specific meaning (e.g., in an implementation using reference-counting, + a value \c true may trigger decrementing a reference counter on some implementation-specific + object). + + \note An empty overrider is sufficient, but then reading keys in QSsl::Pem format + will not be supported. + + \sa isNull(), QSsl::KeyType, QSsl::EncodingFormat, QSsl::KeyAlgorithm +*/ + +/*! + \fn QByteArray TlsKey::toPem(const QByteArray &passPhrase) const + \internal + + This function must be overridden, if converting a key to PEM format, potentially with + encryption, is needed (e.g. to save a QSslKey into a file). If this key is + private and \a passPhrase is not empty, the key's data is expected to be encrypted using + some conventional encryption algorithm (e.g. DES or AES - the one that different tools + or even the class QSslKey can understand later). + + \note If this particular functionality is not needed, an overrider returning an + empty QByteArray is sufficient. + + \sa QSslKey::toPem() +*/ + +/*! + \fn QByteArray TlsKey::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const + \internal + + Converts \a pem to DER format, using this key's type and algorithm. The parameter \a headers + must be a valid, non-null pointer. When parsing \a pem, the headers found there will be saved + into \a headers. + + \note An overrider returning an empty QByteArray is sufficient, if QSslKey::toDer() is not + needed. + + \note This function is very implementation-specific. A backend, that already has this key's + non-empty DER data, may simply return this data. + + \sa QSslKey::toDer() +*/ + +/*! + \fn QByteArray TlsKey::pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const + \internal + + If overridden, this function is expected to convert \a der, using \a headers, to PEM format. + + \note This function is very implementation-specific. As of now (Qt 6.1), it is only required by + Qt's own non-OpenSSL backends, that internally use DER and implement QSslKey::toPem() + via pemFromDer(). +*/ + +/*! + \fn void TlsKey::fromHandle(Qt::HANDLE handle, KeyType type) + \internal + + Initializes this key using the \a handle and \a type, taking the ownership + of the \a handle. + + \note The meaning of the \a handle is implementation-specific. + + \note If a TLS backend does not support such keys, it must provide an + empty implementation. + + \sa handle(), QSslKey::QSslKey(), QSslKet::handle() +*/ + +/*! + \fn TlsKey::handle() const + \internal + + If a TLS backend supports opaque keys, returns a native handle that + this key was initialized with. + + \sa fromHandle(), QSslKey::handle() +*/ + +/*! + \fn bool TlsKey::isNull() const + \internal + + Returns \c true if this is a null key, \c false otherwise. + + \note A null key corresponds to the default-constructed + QSslKey or the one, that was cleared via QSslKey::clear(). + + \sa QSslKey::isNull() +*/ + +/*! + \fn QSsl::KeyType TlsKey::type() const + \internal + + Returns the type of this key (public or private). +*/ + +/*! + \fn QSsl::KeyAlgorithm TlsKey::algorithm() const + \internal + + Return this key's algorithm. +*/ + +/*! + \fn int TlsKey::length() const + \internal + + Returns the length of the key in bits, or -1 if the key is null. +*/ + +/*! + \fn void TlsKey::clear(bool deep) + \internal + + Clears the contents of this key, making it a null key. The meaning + of \a deep is implementation-specific (e.g. if some internal objects + representing a key can be shared using reference counting, \a deep equal + to \c true would imply decrementing a reference count). + + \sa isNull() +*/ + +/*! + \fn bool TlsKey::isPkcs8() const + \internal + + This function is internally used only by Qt's own TLS plugins and affects + the way PEM file is generated by TlsKey. It's sufficient to override it + and return \c false in case a new TLS backend is not using Qt's plugin + as a base. +*/ + +/*! + \fn QByteArray TlsKey::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &passPhrase, const QByteArray &iv) const + \internal + + This function allows to decrypt \a data (for example, a private key read from a file), using + \a passPhrase, initialization vector \a iv. \a cipher is describing a block cipher and its + mode (for example, AES256 + CBC). decrypt() is needed to implement QSslKey's constructor. + + \note A TLS backend may provide an empty implementation, but as a result QSslKey will not be able + to work with private encrypted keys. + + \sa QSslKey +*/ + +/*! + \fn QByteArray TlsKey::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &passPhrase, const QByteArray &iv) const + \internal + + This function is needed to implement QSslKey::toPem() with encryption (for a private + key). \a cipher names a block cipher to use to encrypt \a data, using + \a passPhrase and initialization vector \a iv. + + \note An empty implementation is sufficient, but QSslKey::toPem() will fail for + a private key and non-empty passphrase. + + \sa QSslKey +*/ + +/*! + \internal + + Destroys this key. +*/ +TlsKey::~TlsKey() = default; + +/*! + \internal + + A convenience function that returns a string, corresponding to the + key type or algorithm, which can be used as a header in a PEM file. +*/ +QByteArray TlsKey::pemHeader() const +{ + if (type() == QSsl::PublicKey) + return QByteArrayLiteral("-----BEGIN PUBLIC KEY-----"); + else if (algorithm() == QSsl::Rsa) + return QByteArrayLiteral("-----BEGIN RSA PRIVATE KEY-----"); + else if (algorithm() == QSsl::Dsa) + return QByteArrayLiteral("-----BEGIN DSA PRIVATE KEY-----"); + else if (algorithm() == QSsl::Ec) + return QByteArrayLiteral("-----BEGIN EC PRIVATE KEY-----"); + else if (algorithm() == QSsl::Dh) + return QByteArrayLiteral("-----BEGIN PRIVATE KEY-----"); + + Q_UNREACHABLE_RETURN({}); +} + +/*! + \internal + A convenience function that returns a string, corresponding to the + key type or algorithm, which can be used as a footer in a PEM file. +*/ +QByteArray TlsKey::pemFooter() const +{ + if (type() == QSsl::PublicKey) + return QByteArrayLiteral("-----END PUBLIC KEY-----"); + else if (algorithm() == QSsl::Rsa) + return QByteArrayLiteral("-----END RSA PRIVATE KEY-----"); + else if (algorithm() == QSsl::Dsa) + return QByteArrayLiteral("-----END DSA PRIVATE KEY-----"); + else if (algorithm() == QSsl::Ec) + return QByteArrayLiteral("-----END EC PRIVATE KEY-----"); + else if (algorithm() == QSsl::Dh) + return QByteArrayLiteral("-----END PRIVATE KEY-----"); + + Q_UNREACHABLE_RETURN({}); +} + +/*! + \class X509Certificate + \internal (Network-private) + \brief X509Certificate is an abstract class that allows a TLS backend to + provide an implementation of the QSslCertificate class. + + This class provides an interface that must be reimplemented by a TLS plugin, + that supports QSslCertificate. Most functions are pure virtual, and thus + have to be overridden. For some of them, an empty overrider is acceptable, + though a part of functionality in QSslCertificate will be missing. + + \sa QTlsBackend::createCertificate(), QTlsBackend::X509PemReader(), QTlsBackend::X509DerReader() +*/ + +/*! + \fn bool X509Certificate::isEqual(const X509Certificate &other) const + \internal + + This function is expected to return \c true if this certificate is the same as + the \a other, \c false otherwise. Used by QSslCertificate's comparison operators. +*/ + +/*! + \fn bool X509Certificate::isNull() const + \internal + + Returns true if this certificate was default-constructed and not initialized yet. + This function is called by QSslCertificate::isNull(). + + \sa QSslCertificate::isNull() +*/ + +/*! + \fn bool X509Certificate::isSelfSigned() const + \internal + + This function is needed to implement QSslCertificate::isSelfSigned() + + \sa QSslCertificate::isSelfSigned() +*/ + +/*! + \fn QByteArray X509Certificate::version() const + \internal + + Implements QSslCertificate::version(). + + \sa QSslCertificate::version() +*/ + +/*! + \fn QByteArray X509Certificate::serialNumber() const + \internal + + This function is expected to return the certificate's serial number string in + hexadecimal format. + + \sa QSslCertificate::serialNumber() +*/ + +/*! + \fn QStringList X509Certificate::issuerInfo(QSslCertificate::SubjectInfo subject) const + \internal + + This function is expected to return the issuer information for the \a subject + from the certificate, or an empty list if there is no information for subject + in the certificate. There can be more than one entry of each type. + + \sa QSslCertificate::issuerInfo(). +*/ + +/*! + \fn QStringList X509Certificate::issuerInfo(const QByteArray &attribute) const + \internal + + This function is expected to return the issuer information for attribute from + the certificate, or an empty list if there is no information for \a attribute + in the certificate. There can be more than one entry for an attribute. + + \sa QSslCertificate::issuerInfo(). +*/ + +/*! + \fn QStringList X509Certificate::subjectInfo(QSslCertificate::SubjectInfo subject) const + \internal + + This function is expected to return the information for the \a subject, or an empty list + if there is no information for subject in the certificate. There can be more than one + entry of each type. + + \sa QSslCertificate::subjectInfo(). +*/ + +/*! + \fn QStringList X509Certificate::subjectInfo(const QByteArray &attribute) const + \internal + + This function is expected to return the subject information for \a attribute, or + an empty list if there is no information for attribute in the certificate. + There can be more than one entry for an attribute. + + \sa QSslCertificate::subjectInfo(). +*/ + +/*! + \fn QList<QByteArray> X509Certificate::subjectInfoAttributes() const + \internal + + This function is expected to return a list of the attributes that have values + in the subject information of this certificate. The information associated + with a given attribute can be accessed using the subjectInfo() method. Note + that this list may include the OIDs for any elements that are not known by + the TLS backend. + + \note This function is needed for QSslCertificate:::subjectInfoAttributes(). + + \sa subjectInfo() +*/ + +/*! + \fn QList<QByteArray> X509Certificate::issuerInfoAttributes() const + \internal + + This function is expected to return a list of the attributes that have + values in the issuer information of this certificate. The information + associated with a given attribute can be accessed using the issuerInfo() + method. Note that this list may include the OIDs for any + elements that are not known by the TLS backend. + + \note This function implements QSslCertificate::issuerInfoAttributes(). + + \sa issuerInfo() +*/ + +/*! + \fn QMultiMap<QSsl::AlternativeNameEntryType, QString> X509Certificate::subjectAlternativeNames() const + \internal + + This function is expected to return the list of alternative subject names for + this certificate. The alternative names typically contain host names, optionally + with wildcards, that are valid for this certificate. + + \sa subjectInfo() +*/ + +/*! + \fn QDateTime X509Certificate::effectiveDate() const + \internal + + This function is expected to return the date-time that the certificate + becomes valid, or an empty QDateTime if this is a null certificate. + + \sa expiryDate() +*/ + +/*! + \fn QDateTime X509Certificate::expiryDate() const + \internal + + This function is expected to return the date-time that the certificate expires, + or an empty QDateTime if this is a null certificate. + + \sa effectiveDate() +*/ + +/*! + \fn qsizetype X509Certificate::numberOfExtensions() const + \internal + + This function is expected to return the number of X509 extensions of + this certificate. +*/ + +/*! + \fn QString X509Certificate::oidForExtension(qsizetype i) const + \internal + + This function is expected to return the ASN.1 OID for the extension + with index \a i. + + \sa numberOfExtensions() +*/ + +/*! + \fn QString X509Certificate::nameForExtension(qsizetype i) const + \internal + + This function is expected to return the name for the extension + with index \a i. If no name is known for the extension then the + OID will be returned. + + \sa numberOfExtensions(), oidForExtension() +*/ + +/*! + \fn QVariant X509Certificate::valueForExtension(qsizetype i) const + \internal + + This function is expected to return the value of the extension + with index \a i. The structure of the value returned depends on + the extension type + + \sa numberOfExtensions() +*/ + +/*! + \fn bool X509Certificate::isExtensionCritical(qsizetype i) const + \internal + + This function is expected to return the criticality of the extension + with index \a i. + + \sa numberOfExtensions() +*/ + +/*! + \fn bool X509Certificate::isExtensionSupported(qsizetype i) const + \internal + + This function is expected to return \c true if this extension is supported. + In this case, supported simply means that the structure of the QVariant returned + by the valueForExtension() accessor will remain unchanged between versions. + + \sa numberOfExtensions() +*/ + +/*! + \fn QByteArray X509Certificate::toPem() const + \internal + + This function is expected to return this certificate converted to a PEM (Base64) + encoded representation. +*/ + +/*! + \fn QByteArray X509Certificate::toDer() const + \internal + + This function is expected to return this certificate converted to a DER (binary) + encoded representation. +*/ + +/*! + \fn QString X509Certificate::toText() const + \internal + + This function is expected to return this certificate converted to a human-readable + text representation. +*/ + +/*! + \fn Qt::HANDLE X509Certificate::handle() const + \internal + + This function is expected to return a pointer to the native certificate handle, + if there is one, else nullptr. +*/ + +/*! + \fn size_t X509Certificate::hash(size_t seed) const + \internal + + This function is expected to return the hash value for this certificate, + using \a seed to seed the calculation. +*/ + +/*! + \internal + + Destroys this certificate. +*/ +X509Certificate::~X509Certificate() = default; + +/*! + \internal + + Returns the certificate subject's public key. +*/ +TlsKey *X509Certificate::publicKey() const +{ + return nullptr; +} + +#if QT_CONFIG(ssl) + +/*! + \class TlsCryptograph + \internal (Network-private) + \brief TlsCryptograph is an abstract class, that allows a TLS plugin to implement QSslSocket. + + This abstract base class provides an interface that must be reimplemented by a TLS plugin, + that supports QSslSocket. A class, implementing TlsCryptograph's interface, is responsible + for TLS handshake, reading and writing encryped application data; it is expected + to work with QSslSocket and it's private implementation - QSslSocketPrivate. + QSslSocketPrivate provides access to its read/write buffers, QTcpSocket it + internally uses for connecting, reading and writing. QSslSocketPrivate + can also be used for reporting errors and storing the certificates received + during the handshake phase. + + \note Most of the functions in this class are pure virtual and have no actual implementation + in the QtNetwork module. This documentation is mostly conceptual and only describes what those + functions are expected to do, but not how they must be implemented. + + \sa QTlsBackend::createTlsCryptograph() +*/ + +/*! + \fn void TlsCryptograph::init(QSslSocket *q, QSslSocketPrivate *d) + \internal + + When initializing this TlsCryptograph, QSslSocket will pass a pointer to self and + its d-object using this function. +*/ + +/*! + \fn QList<QSslError> TlsCryptograph::tlsErrors() const + \internal + + Returns a list of QSslError, describing errors encountered during + the TLS handshake. + + \sa QSslSocket::sslHandshakeErrors() +*/ + +/*! + \fn void TlsCryptograph::startClientEncryption() + \internal + + A client-side QSslSocket calls this function after its internal TCP socket + establishes a connection with a remote host, or from QSslSocket::startClientEncryption(). + This TlsCryptograph is expected to initialize some implementation-specific TLS context, + if needed, and then start the client side of the TLS handshake (for example, by calling + transmit()), using TCP socket from QSslSocketPrivate. + + \sa init(), transmit(), QSslSocket::startClientEncryption(), QSslSocket::connectToHostEncrypted() +*/ + +/*! + \fn void TlsCryptograph::startServerEncryption() + \internal + + This function is called by QSslSocket::startServerEncryption(). The TlsCryptograph + is expected to initialize some implementation-specific TLS context, if needed, + and then try to read the ClientHello message and continue the TLS handshake + (for example, by calling transmit()). + + \sa transmit(), QSslSocket::startServerEncryption() +*/ + +/*! + \fn void TlsCryptograph::continueHandshake() + \internal + + QSslSocket::resume() calls this function if its pause mode is QAbstractSocket::PauseOnSslErrors, + and errors, found during the handshake, were ignored. If implemented, this function is expected + to emit QSslSocket::encrypted(). + + \sa QAbstractSocket::pauseMode(), QSslSocket::sslHandshakeErrors(), QSslSocket::ignoreSslErrors(), QSslSocket::resume() +*/ + +/*! + \fn void TlsCryptograph::disconnectFromHost() + \internal + + This function is expected to call disconnectFromHost() on the TCP socket + that can be obtained from QSslSocketPrivate. Any additional actions + are implementation-specific (e.g., sending shutdown alert message). + +*/ + +/*! + \fn void TlsCryptograph::disconnected() + \internal + + This function is called when the remote has disconnected. If there + is data left to be read you may ignore the maxReadBufferSize restriction + and read it all now. +*/ + +/*! + \fn QSslCipher TlsCryptograph::sessionCipher() const + \internal + + This function returns a QSslCipher object describing the ciphersuite negotiated + during the handshake. +*/ + +/*! + \fn QSsl::SslProtocol TlsCryptograph::sessionProtocol() const + \internal + + This function returns the version of TLS (or DTLS) protocol negotiated during the handshake. +*/ + +/*! + \fn void TlsCryptograph::transmit() + \internal + + This function is responsible for reading and writing data. The meaning of these I/O + operations depends on an implementation-specific TLS state machine. These read and write + operations can be reading and writing parts of a TLS handshake (e.g. by calling handshake-specific + functions), or reading and writing application data (if encrypted connection was already + established). transmit() is expected to use the QSslSocket's TCP socket (accessible via + QSslSocketPrivate) to read the incoming data and write the outgoing data. When in encrypted + state, transmit() is also using QSslSocket's internal read and write buffers: the read buffer + to fill with decrypted incoming data; the write buffer - for the data to encrypt and send. + This TlsCryptograph can also use QSslSocketPrivate to check which TLS errors were ignored during + the handshake. + + \note This function is responsible for emitting QSslSocket's signals, that occur during the + handshake (e.g. QSslSocket::sslErrors() or QSslSocket::encrypted()), and also read/write signals, + e.g. QSslSocket::bytesWritten() and QSslSocket::readyRead(). + + \sa init() +*/ + +/*! + \internal + + Destroys this object. +*/ +TlsCryptograph::~TlsCryptograph() = default; + +/*! + \internal + + This function allows to share QSslContext between several QSslSocket objects. + The default implementation does nothing. + + \note The definition of the class QSslContext is implementation-specific. + + \sa sslContext() +*/ +void TlsCryptograph::checkSettingSslContext(std::shared_ptr<QSslContext> tlsContext) +{ + Q_UNUSED(tlsContext); +} + +/*! + \internal + + Returns the context previously set by checkSettingSslContext() or \nullptr, + if no context was set. The default implementation returns \nullptr. + + \sa checkSettingSslContext() +*/ +std::shared_ptr<QSslContext> TlsCryptograph::sslContext() const +{ + return {}; +} + +/*! + \internal + + If this TLS backend supports reporting errors before handshake is finished, + e.g. from a verification callback function, enableHandshakeContinuation() + allows this object to continue handshake. The default implementation does + nothing. + + \sa QSslSocket::handshakeInterruptedOnError(), QSslConfiguration::setHandshakeMustInterruptOnError() +*/ +void TlsCryptograph::enableHandshakeContinuation() +{ +} + +/*! + \internal + + Windows and OpenSSL-specific, only used internally by Qt's OpenSSL TLS backend. + + \note The default empty implementation is sufficient. +*/ +void TlsCryptograph::cancelCAFetch() +{ +} + +/*! + \internal + + Windows and Schannel-specific, only used by Qt's Schannel TLS backend, in + general, if a backend has its own buffer where it stores undecrypted data + then it must report true if it contains any data through this function. + + \note The default empty implementation, returning \c false is sufficient. +*/ +bool TlsCryptograph::hasUndecryptedData() const +{ + return false; +} + +/*! + \internal + + Returns the list of OCSP (Online Certificate Status Protocol) responses, + received during the handshake. The default implementation returns an empty + list. +*/ +QList<QOcspResponse> TlsCryptograph::ocsps() const +{ + return {}; +} + +/*! + \internal + + A helper function that can be used during a handshake. Returns \c true if the \a peerName + matches one of subject alternative names or common names found in the \a certificate. +*/ +bool TlsCryptograph::isMatchingHostname(const QSslCertificate &certificate, const QString &peerName) +{ + return QSslSocketPrivate::isMatchingHostname(certificate, peerName); +} + +/*! + \internal + Calls QAbstractSocketPrivate::setErrorAndEmit() for \a d, passing \a errorCode and + \a errorDescription as parameters. +*/ +void TlsCryptograph::setErrorAndEmit(QSslSocketPrivate *d, QAbstractSocket::SocketError errorCode, + const QString &errorDescription) const +{ + Q_ASSERT(d); + d->setErrorAndEmit(errorCode, errorDescription); +} + +#if QT_CONFIG(dtls) +/*! + \class DtlsBase + \internal (Network-private) + \brief DtlsBase is a base class for the classes DtlsCryptograph and DtlsCookieVerifier. + + DtlsBase is the base class for the classes DtlsCryptograph and DtlsCookieVerifier. It's + an abstract class, an interface that these before-mentioned classes share. It allows to + set, get and clear the last error that occurred, set and get cookie generation parameters, + set and get QSslConfiguration. + + \note This class is not supposed to be inherited directly, it's only needed by DtlsCryptograph + and DtlsCookieVerifier. + + \sa QDtls, QDtlsClientVerifier, DtlsCryptograph, DtlsCookieVerifier +*/ + +/*! + \fn void DtlsBase::setDtlsError(QDtlsError code, const QString &description) + \internal + + Sets the last error to \a code and its textual description to \a description. + + \sa QDtlsError, error(), errorString() +*/ + +/*! + \fn QDtlsError DtlsBase::error() const + \internal + + This function, when overridden, is expected to return the code for the last error that occurred. + If no error occurred it should return QDtlsError::NoError. + + \sa QDtlsError, errorString(), setDtlsError() +*/ + +/*! + \fn QDtlsError DtlsBase::errorString() const + \internal + + This function, when overridden, is expected to return the textual description for the last error + that occurred or an empty string if no error occurred. + + \sa QDtlsError, error(), setDtlsError() +*/ + +/*! + \fn void DtlsBase::clearDtlsError() + \internal + + This function is expected to set the error code for the last error to QDtlsError::NoError and + its textual description to an empty string. + + \sa QDtlsError, setDtlsError(), error(), errorString() +*/ + +/*! + \fn void DtlsBase::setConfiguration(const QSslConfiguration &configuration) + \internal + + Sets a TLS configuration that an object of a class inheriting from DtlsCookieVerifier or + DtlsCryptograph will use, to \a configuration. + + \sa configuration() +*/ + +/*! + \fn QSslConfiguration DtlsBase::configuration() const + \internal + + Returns TLS configuration this object is using (either set by setConfiguration() + previously, or the default DTLS configuration). + + \sa setConfiguration(), QSslConfiguration::defaultDtlsConfiguration() +*/ + +/*! + \fn bool DtlsBase::setCookieGeneratorParameters(const QDtlsClientVerifier::GeneratorParameters ¶ms) + \internal + + Sets the DTLS cookie generation parameters that DtlsCookieVerifier or DtlsCryptograph will use to + \a params. + + \note This function returns \c false if parameters were invalid - if the secret was empty. Otherwise, + this function must return true. + + \sa QDtlsClientVerifier::GeneratorParameters, cookieGeneratorParameters() +*/ + +/*! + \fn QDtlsClientVerifier::GeneratorParameters DtlsBase::cookieGeneratorParameters() const + \internal + + Returns DTLS cookie generation parameters that were either previously set by setCookieGeneratorParameters(), + or default parameters. + + \sa setCookieGeneratorParameters() +*/ + +/*! + \internal + + Destroys this object. +*/ +DtlsBase::~DtlsBase() = default; + +/*! + \class DtlsCookieVerifier + \internal (Network-private) + \brief DtlsCookieVerifier is an interface that allows a TLS plugin to support the class QDtlsClientVerifier. + + DtlsCookieVerifier is an interface, an abstract class, that has to be implemented by + a TLS plugin that supports DTLS cookie verification. + + \sa QDtlsClientVerifier +*/ + +/*! + \fn bool DtlsCookieVerifier::verifyClient(QUdpSocket *socket, const QByteArray &dgram, const QHostAddress &address, quint16 port) + \internal + + This function is expected to verify a ClientHello message, found in \a dgram, using \a address, + \a port, and cookie generator parameters. The function returns \c true if such cookie was found + and \c false otherwise. If no valid cookie was found in the \a dgram, this verifier should use + \a socket to send a HelloVerifyRequest message, using \a address and \a port as the destination + and a source material for cookie generation, see also + \l {RFC 6347, section 4.2.1} + + \sa QDtlsClientVerifier +*/ + +/*! + \fn QByteArray DtlsCookieVerifier::verifiedHello() const + \internal + + Returns the last ClientHello message containing the DTLS cookie that this verifier was + able to verify as correct, or an empty byte array. + + \sa verifyClient() +*/ + +/*! + \class DtlsCryptograph + \internal (Network-private) + \brief DtlsCryptograph is an interface that allows a TLS plugin to implement the class QDtls. + + DtlsCryptograph is an abstract class; a TLS plugin can provide a class, inheriting from + DtlsCryptograph and implementing its pure virtual functions, thus implementing the class + QDtls and enabling DTLS over UDP. + + To write DTLS datagrams, a class, inheriting DtlsCryptograph, is expected to use + QUdpSocket. In general, all reading is done externally, so DtlsCryptograph is + expected to only write into QUdpSocket, check possible socket errors, change socket + options if needed. + + \note All functions in this class are pure virtual and have no actual implementation + in the QtNetwork module. This documentation is mostly conceptual and only describes + what those functions are expected to do, but not how they must be implemented. + + \sa QDtls, QUdpSocket +*/ + +/*! + \fn QSslSocket::SslMode DtlsCryptograph::cryptographMode() const + \internal + + Returns the mode (client or server) this object operates in. + + \note This mode is set once when a new DtlsCryptograph is created + by QTlsBackend and cannot change. + + \sa QTlsBackend::createDtlsCryptograph() +*/ + +/*! + \fn void DtlsCryptograph::setPeer(const QHostAddress &addr, quint16 port, const QString &name) + \internal + + Sets the remote peer's address to \a addr and remote port to \a port. \a name, + if not empty, is to be used when validating the peer's certificate. + + \sa peerAddress(), peerPort(), peerVerificationName() +*/ + +/*! + \fn QHostAddress DtlsCryptograph::peerAddress() const + \internal + + Returns the remote peer's address previously set by setPeer() or, + if no address was set, an empty address. + + \sa setPeer() +*/ + +/*! + \fn quint16 DtlsCryptograph::peerPort() const + \internal + + Returns the remote peer's port previously set by setPeer() or + 0 if no port was set. + + \sa setPeer(), peerAddress() +*/ + +/*! + \fn void DtlsCryptograph::setPeerVerificationName(const QString &name) + \internal + + Sets the host name to use during certificate validation to \a name. + + \sa peerVerificationName(), setPeer() +*/ + +/*! + \fn QString DtlsCryptograph::peerVerificationName() const + \internal + + Returns the name that this object is using during the certificate validation, + previously set by setPeer() or setPeerVerificationName(). Returns an empty string + if no peer verification name was set. + + \sa setPeer(), setPeerVerificationName() +*/ + +/*! + \fn void DtlsCryptograph::setDtlsMtuHint(quint16 mtu) + \internal + + Sets the maximum transmission unit (MTU), if it is supported by a TLS implementation, to \a mtu. + + \sa dtlsMtuHint() +*/ + +/*! + \fn quint16 DtlsCryptograph::dtlsMtuHint() const + \internal + + Returns the value of the maximum transmission unit either previously set by setDtlsMtuHint(), + or some implementation-specific value (guessed or somehow known to this DtlsCryptograph). + + \sa setDtlsMtuHint() +*/ + +/*! + \fn QDtls::HandshakeState DtlsCryptograph::state() const + \internal + + Returns the current handshake state for this DtlsCryptograph (not started, in progress, + peer verification error found, complete). + + \sa isConnectionEncrypted(), startHandshake() +*/ + +/*! + \fn bool DtlsCryptograph::isConnectionEncrypted() const + \internal + + Returns \c true if this DtlsCryptograph has completed a handshake without validation + errors (or these errors were ignored). Returns \c false otherwise. +*/ + +/*! + \fn bool DtlsCryptograph::startHandshake(QUdpSocket *socket, const QByteArray &dgram) + \internal + + This function is expected to initialize some implementation-specific context and to start a DTLS + handshake, using \a socket to write datagrams (but not to read them). If this object is operating + as a server, \a dgram is non-empty and contains the ClientHello message. This function returns + \c true if no error occurred (and this DtlsCryptograph's state switching to + QDtls::HandshakeState::HandshakeInProgress), \c false otherwise. + + \sa continueHandshake(), handleTimeout(), resumeHandshake(), abortHandshake(), state() +*/ + +/*! + \fn bool DtlsCryptograph::handleTimeout(QUdpSocket *socket) + \internal + + In case a timeout occurred during the handshake, allows to re-transmit the last message, + using \a socket to write the datagram. Returns \c true if no error occurred, \c false otherwise. + + \sa QDtls::handshakeTimeout(), QDtls::handleTimeout() +*/ + +/*! + \fn bool DtlsCryptograph::continueHandshake(QUdpSocket *socket, const QByteArray &dgram) + \internal + + Continues the handshake, using \a socket to write datagrams (a handshake-specific message). + \a dgram contains the peer's handshake-specific message. Returns \c false in case some error + was encountered (this can include socket-related errors and errors found during the certificate + validation). Returns \c true if the handshake was complete successfully, or is still in progress. + + This function, depending on the implementation-specific state machine, may leave the handshake + state in QDtls::HandshakeState::HandshakeInProgress, or switch to QDtls::HandshakeState::HandshakeComplete + or QDtls::HandshakeState::PeerVerificationFailed. + + This function may store the peer's certificate (or chain of certificates), extract and store + the information about the negotiated session protocol and ciphersuite. + + \sa startHandshake() +*/ + +/*! + \fn bool DtlsCryptograph::resumeHandshake(QUdpSocket *socket) + \internal + + If peer validation errors were found duing the handshake, this function tries to + continue and complete the handshake. If errors were ignored, the function switches + this object's state to QDtls::HandshakeState::HandshakeComplete and returns \c true. + + \sa abortHandshake() +*/ + +/*! + \fn void DtlsCryptograph::abortHandshake(QUdpSocket *socket) + \internal + + Aborts the handshake if it's in progress or in the state QDtls::HandshakeState::PeerVerificationFailed. + The use of \a socket is implementation-specific (for example, this DtlsCryptograph may send + ShutdownAlert message). + + \sa resumeHandshake() +*/ + +/*! + \fn void DtlsCryptograph::sendShutdownAlert(QUdpSocket *socket) + \internal + + If the underlying TLS library provides the required functionality, this function + may sent ShutdownAlert message using \a socket. +*/ + +/*! + \fn QList<QSslError> DtlsCryptograph::peerVerificationErrors() const + \internal + + Returns the list of errors that this object encountered during DTLS handshake + and certificate validation. + + \sa ignoreVerificationErrors() +*/ + +/*! + \fn void DtlsCryptograph::ignoreVerificationErrors(const QList<QSslError> &errorsToIgnore) + \internal + + Tells this object to ignore errors from \a errorsToIgnore when they are found during + DTLS handshake. + + \sa peerVerificationErrors() +*/ + +/*! + \fn QSslCipher DtlsCryptograph::dtlsSessionCipher() const + \internal + + If such information is available, returns the ciphersuite, negotiated during + the handshake. + + \sa continueHandshake(), dtlsSessionProtocol() +*/ + +/*! + \fn QSsl::SslProtocol DtlsCryptograph::dtlsSessionProtocol() const + \internal + + Returns the version of the session protocol that was negotiated during the handshake or + QSsl::UnknownProtocol if the handshake is incomplete or no information about the session + protocol is available. + + \sa continueHandshake(), dtlsSessionCipher() +*/ + +/*! + \fn qint64 DtlsCryptograph::writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram) + \internal + + If this DtlsCryptograph is in the QDtls::HandshakeState::HandshakeComplete state, this function + encrypts \a dgram and writes this encrypted data into \a socket. + + Returns the number of bytes (of \a dgram) written, or -1 in case of error. This function should + set the error code and description if some error was encountered. + + \sa decryptDatagram() +*/ + +/*! + \fn QByteArray DtlsCryptograph::decryptDatagram(QUdpSocket *socket, const QByteArray &dgram) + \internal + + If this DtlsCryptograph is in the QDtls::HandshakeState::HandshakeComplete state, decrypts \a dgram. + The use of \a socket is implementation-specific. This function should return an empty byte array + and set the error code and description if some error was encountered. +*/ + +#endif // QT_CONFIG(dtls) +#endif // QT_CONFIG(ssl) + +} // namespace QTlsPrivate + +#if QT_CONFIG(ssl) +/*! + \internal +*/ +Q_NETWORK_EXPORT void qt_ForceTlsSecurityLevel() +{ + if (auto *backend = QSslSocketPrivate::tlsBackendInUse()) + backend->forceAutotestSecurityLevel(); +} + +#endif // QT_CONFIG(ssl) + QT_END_NAMESPACE + +#include "moc_qtlsbackend_p.cpp" diff --git a/src/network/ssl/qtlsbackend_p.h b/src/network/ssl/qtlsbackend_p.h index 9c4f2d3eb8..090531014b 100644 --- a/src/network/ssl/qtlsbackend_p.h +++ b/src/network/ssl/qtlsbackend_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QTLSBACKEND_P_H #define QTLSBACKEND_P_H @@ -51,41 +15,132 @@ // We mean it. // -#include <private/qtnetworkglobal_p.h> +#include <QtNetwork/private/qtnetworkglobal_p.h> + +#include "qsslconfiguration.h" +#include "qsslerror.h" +#include "qssl_p.h" + +#if QT_CONFIG(dtls) +#include "qdtls.h" +#endif #include <QtNetwork/qsslcertificate.h> -#include <QtNetwork/qsslerror.h> +#include <QtNetwork/qsslcipher.h> #include <QtNetwork/qsslkey.h> #include <QtNetwork/qssl.h> +#include <QtCore/qloggingcategory.h> +#include <QtCore/qnamespace.h> #include <QtCore/qobject.h> #include <QtCore/qglobal.h> #include <QtCore/qstring.h> #include <QtCore/qlist.h> #include <QtCore/qmap.h> -#include <vector> #include <memory> -QT_REQUIRE_CONFIG(ssl); - QT_BEGIN_NAMESPACE +class QSslPreSharedKeyAuthenticator; +class QSslSocketPrivate; +class QHostAddress; +class QSslContext; + +class QSslSocket; class QByteArray; +class QSslCipher; +class QUdpSocket; class QIODevice; +class QSslError; +class QSslKey; + +namespace QTlsPrivate { + +class Q_NETWORK_EXPORT TlsKey { +public: + virtual ~TlsKey(); + + using KeyType = QSsl::KeyType; + using KeyAlgorithm = QSsl::KeyAlgorithm; + + virtual void decodeDer(KeyType type, KeyAlgorithm algorithm, const QByteArray &der, + const QByteArray &passPhrase, bool deepClear) = 0; + virtual void decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem, + const QByteArray &passPhrase, bool deepClear) = 0; + + virtual QByteArray toPem(const QByteArray &passPhrase) const = 0; + virtual QByteArray derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const = 0; + virtual QByteArray pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const = 0; -namespace QSsl { + virtual void fromHandle(Qt::HANDLE handle, KeyType type) = 0; + virtual Qt::HANDLE handle() const = 0; -// Encapsulates key's data or backend-specific -// data-structure, like RSA/DSA/DH structs in OpenSSL. -class TlsKey; + virtual bool isNull() const = 0; + virtual KeyType type() const = 0; + virtual KeyAlgorithm algorithm() const = 0; + virtual int length() const = 0; -// Abstraction above OpenSSL's X509, or our generic -// 'derData'-based code. -class X509Certificate; + virtual void clear(bool deepClear) = 0; -// X509-related auxiliary functions, previously static -// member-functions in different classes. + virtual bool isPkcs8() const = 0; + + virtual QByteArray decrypt(Cipher cipher, const QByteArray &data, + const QByteArray &passPhrase, const QByteArray &iv) const = 0; + virtual QByteArray encrypt(Cipher cipher, const QByteArray &data, + const QByteArray &key, const QByteArray &iv) const = 0; + + QByteArray pemHeader() const; + QByteArray pemFooter() const; +}; + +class Q_NETWORK_EXPORT X509Certificate +{ +public: + virtual ~X509Certificate(); + + virtual bool isEqual(const X509Certificate &other) const = 0; + virtual bool isNull() const = 0; + virtual bool isSelfSigned() const = 0; + virtual QByteArray version() const = 0; + virtual QByteArray serialNumber() const = 0; + virtual QStringList issuerInfo(QSslCertificate::SubjectInfo subject) const = 0; + virtual QStringList issuerInfo(const QByteArray &attribute) const = 0; + virtual QStringList subjectInfo(QSslCertificate::SubjectInfo subject) const = 0; + virtual QStringList subjectInfo(const QByteArray &attribute) const = 0; + + virtual QList<QByteArray> subjectInfoAttributes() const = 0; + virtual QList<QByteArray> issuerInfoAttributes() const = 0; + virtual QMultiMap<QSsl::AlternativeNameEntryType, QString> subjectAlternativeNames() const = 0; + virtual QDateTime effectiveDate() const = 0; + virtual QDateTime expiryDate() const = 0; + + virtual TlsKey *publicKey() const; + + // Extensions. Plugins do not expose internal representation + // and cannot rely on QSslCertificate's internals. Thus, + // we provide this information 'in pieces': + virtual qsizetype numberOfExtensions() const = 0; + virtual QString oidForExtension(qsizetype i) const = 0; + virtual QString nameForExtension(qsizetype i) const = 0; + virtual QVariant valueForExtension(qsizetype i) const = 0; + virtual bool isExtensionCritical(qsizetype i) const = 0; + virtual bool isExtensionSupported(qsizetype i) const = 0; + + virtual QByteArray toPem() const = 0; + virtual QByteArray toDer() const = 0; + virtual QString toText() const = 0; + + virtual Qt::HANDLE handle() const = 0; + + virtual size_t hash(size_t seed) const noexcept = 0; +}; + +// TLSTODO: consider making those into virtuals in QTlsBackend. After all, we ask the backend +// to return those pointers if the functionality is supported, but it's a bit odd to have +// this level of indirection. They are not parts of the classes above because ... +// you'd then have to ask backend to create a certificate to ... call those +// functions on a certificate. using X509ChainVerifyPtr = QList<QSslError> (*)(const QList<QSslCertificate> &chain, const QString &hostName); using X509PemReaderPtr = QList<QSslCertificate> (*)(const QByteArray &pem, int count); @@ -94,65 +149,174 @@ using X509Pkcs12ReaderPtr = bool (*)(QIODevice *device, QSslKey *key, QSslCertif QList<QSslCertificate> *caCertificates, const QByteArray &passPhrase); +#if QT_CONFIG(ssl) // TLS over TCP. Handshake, encryption/decryption. +class Q_NETWORK_EXPORT TlsCryptograph : public QObject +{ +public: + virtual ~TlsCryptograph(); + + virtual void init(QSslSocket *q, QSslSocketPrivate *d) = 0; + virtual void checkSettingSslContext(std::shared_ptr<QSslContext> tlsContext); + virtual std::shared_ptr<QSslContext> sslContext() const; + + virtual QList<QSslError> tlsErrors() const = 0; + + virtual void startClientEncryption() = 0; + virtual void startServerEncryption() = 0; + virtual void continueHandshake() = 0; + virtual void enableHandshakeContinuation(); + virtual void disconnectFromHost() = 0; + virtual void disconnected() = 0; + virtual void cancelCAFetch(); + virtual QSslCipher sessionCipher() const = 0; + virtual QSsl::SslProtocol sessionProtocol() const = 0; + + virtual void transmit() = 0; + virtual bool hasUndecryptedData() const; + virtual QList<QOcspResponse> ocsps() const; + + static bool isMatchingHostname(const QSslCertificate &cert, const QString &peerName); + + void setErrorAndEmit(QSslSocketPrivate *d, QAbstractSocket::SocketError errorCode, + const QString &errorDescription) const; +}; +#else class TlsCryptograph; +#endif // QT_CONFIG(ssl) -// TLS over UDP. Handshake, encryption/decryption. -class DtlsCryptograph; +#if QT_CONFIG(dtls) -// DTLS cookie: generation and verification. -class DtlsCookieVerifier; +class Q_NETWORK_EXPORT DtlsBase +{ +public: + virtual ~DtlsBase(); -} // namespace QSsl + virtual void setDtlsError(QDtlsError code, const QString &description) = 0; -// Factory, creating back-end specific implementations of -// different entities QSslSocket is using. -// TLSTODO: consider merging with ... it's own factory -// below, no real benefit in having this split. -class Q_NETWORK_EXPORT QTlsBackend : public QObject + virtual QDtlsError error() const = 0; + virtual QString errorString() const = 0; + + virtual void clearDtlsError() = 0; + + virtual void setConfiguration(const QSslConfiguration &configuration) = 0; + virtual QSslConfiguration configuration() const = 0; + + using GenParams = QDtlsClientVerifier::GeneratorParameters; + virtual bool setCookieGeneratorParameters(const GenParams ¶ms) = 0; + virtual GenParams cookieGeneratorParameters() const = 0; +}; + +// DTLS cookie: generation and verification. +class Q_NETWORK_EXPORT DtlsCookieVerifier : virtual public DtlsBase { - Q_OBJECT public: - QTlsBackend(); - ~QTlsBackend() override; + virtual bool verifyClient(QUdpSocket *socket, const QByteArray &dgram, + const QHostAddress &address, quint16 port) = 0; + virtual QByteArray verifiedHello() const = 0; +}; - virtual QString backendName() const; +// TLS over UDP. Handshake, encryption/decryption. +class Q_NETWORK_EXPORT DtlsCryptograph : virtual public DtlsBase +{ +public: - // X509 and keys: - virtual QSsl::TlsKey *createKey() const; - virtual QSsl::X509Certificate *createCertificate() const; + virtual QSslSocket::SslMode cryptographMode() const = 0; + virtual void setPeer(const QHostAddress &addr, quint16 port, const QString &name) = 0; + virtual QHostAddress peerAddress() const = 0; + virtual quint16 peerPort() const = 0; + virtual void setPeerVerificationName(const QString &name) = 0; + virtual QString peerVerificationName() const = 0; - // TLS and DTLS: - virtual QSsl::TlsCryptograph *createTlsCryptograph() const; - virtual QSsl::DtlsCryptograph *createDtlsCryptograph() const; - virtual QSsl::DtlsCookieVerifier *createDtlsCookieVerifier() const; + virtual void setDtlsMtuHint(quint16 mtu) = 0; + virtual quint16 dtlsMtuHint() const = 0; - // X509 machinery: - virtual QSsl::X509ChainVerifyPtr X509Verifier() const; - virtual QSsl::X509PemReaderPtr X509PemReader() const; - virtual QSsl::X509DerReaderPtr X509DerReader() const; - virtual QSsl::X509Pkcs12ReaderPtr X509Pkcs12Reader() const; + virtual QDtls::HandshakeState state() const = 0; + virtual bool isConnectionEncrypted() const = 0; - Q_DISABLE_COPY_MOVE(QTlsBackend) + virtual bool startHandshake(QUdpSocket *socket, const QByteArray &dgram) = 0; + virtual bool handleTimeout(QUdpSocket *socket) = 0; + virtual bool continueHandshake(QUdpSocket *socket, const QByteArray &dgram) = 0; + virtual bool resumeHandshake(QUdpSocket *socket) = 0; + virtual void abortHandshake(QUdpSocket *socket) = 0; + virtual void sendShutdownAlert(QUdpSocket *socket) = 0; + + virtual QList<QSslError> peerVerificationErrors() const = 0; + virtual void ignoreVerificationErrors(const QList<QSslError> &errorsToIgnore) = 0; + + virtual QSslCipher dtlsSessionCipher() const = 0; + virtual QSsl::SslProtocol dtlsSessionProtocol() const = 0; + + virtual qint64 writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram) = 0; + virtual QByteArray decryptDatagram(QUdpSocket *socket, const QByteArray &dgram) = 0; }; -// Factory for a backend. -class Q_NETWORK_EXPORT QTlsBackendFactory : public QObject +#else + +class DtlsCookieVerifier; +class DtlsCryptograph; + +#endif // QT_CONFIG(dtls) + +} // namespace QTlsPrivate + +// Factory, creating back-end specific implementations of +// different entities QSslSocket is using. +class Q_NETWORK_EXPORT QTlsBackend : public QObject { Q_OBJECT public: - QTlsBackendFactory(); - ~QTlsBackendFactory() override; + QTlsBackend(); + ~QTlsBackend() override; + + virtual bool isValid() const; + virtual long tlsLibraryVersionNumber() const; + virtual QString tlsLibraryVersionString() const; + virtual long tlsLibraryBuildVersionNumber() const; + virtual QString tlsLibraryBuildVersionString() const; + virtual void ensureInitialized() const; virtual QString backendName() const = 0; - virtual QTlsBackend *create() const = 0; virtual QList<QSsl::SslProtocol> supportedProtocols() const = 0; virtual QList<QSsl::SupportedFeature> supportedFeatures() const = 0; virtual QList<QSsl::ImplementedClass> implementedClasses() const = 0; + // X509 and keys: + virtual QTlsPrivate::TlsKey *createKey() const; + virtual QTlsPrivate::X509Certificate *createCertificate() const; + + virtual QList<QSslCertificate> systemCaCertificates() const; + + // TLS and DTLS: + virtual QTlsPrivate::TlsCryptograph *createTlsCryptograph() const; + virtual QTlsPrivate::DtlsCryptograph *createDtlsCryptograph(class QDtls *qObject, int mode) const; + virtual QTlsPrivate::DtlsCookieVerifier *createDtlsCookieVerifier() const; + + // TLSTODO - get rid of these function pointers, make them virtuals in + // the backend itself. X509 machinery: + virtual QTlsPrivate::X509ChainVerifyPtr X509Verifier() const; + virtual QTlsPrivate::X509PemReaderPtr X509PemReader() const; + virtual QTlsPrivate::X509DerReaderPtr X509DerReader() const; + virtual QTlsPrivate::X509Pkcs12ReaderPtr X509Pkcs12Reader() const; + + // Elliptic curves: + virtual QList<int> ellipticCurvesIds() const; + virtual int curveIdFromShortName(const QString &name) const; + virtual int curveIdFromLongName(const QString &name) const; + virtual QString shortNameForId(int cid) const; + virtual QString longNameForId(int cid) const; + virtual bool isTlsNamedCurve(int cid) const; + + // Note: int and not QSslDiffieHellmanParameter::Error - because this class and + // its enum are QT_CONFIG(ssl)-conditioned. But not QTlsBackend and + // its virtual functions. DH decoding: + virtual int dhParametersFromDer(const QByteArray &derData, QByteArray *data) const; + virtual int dhParametersFromPem(const QByteArray &pemData, QByteArray *data) const; + static QList<QString> availableBackendNames(); static QString defaultBackendName(); - static QTlsBackend *create(const QString &backendName); + static QTlsBackend *findBackend(const QString &backendName); + static QTlsBackend *activeOrAnyBackend(); static QList<QSsl::SslProtocol> supportedProtocols(const QString &backendName); static QList<QSsl::SupportedFeature> supportedFeatures(const QString &backendName); @@ -162,15 +326,76 @@ 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[]; - Q_DISABLE_COPY_MOVE(QTlsBackendFactory) -}; + template<class DynamicType, class TLSObject> + static DynamicType *backend(const TLSObject &o) + { + return static_cast<DynamicType *>(o.d->backend.get()); + } + + static void resetBackend(QSslKey &key, QTlsPrivate::TlsKey *keyBackend); + + static void setupClientPskAuth(QSslPreSharedKeyAuthenticator *auth, const char *hint, + int hintLength, unsigned maxIdentityLen, unsigned maxPskLen); + static void setupServerPskAuth(QSslPreSharedKeyAuthenticator *auth, const char *identity, + const QByteArray &identityHint, unsigned maxPskLen); +#if QT_CONFIG(ssl) + static QSslCipher createCiphersuite(const QString &description, int bits, int supportedBits); + static QSslCipher createCiphersuite(const QString &suiteName, QSsl::SslProtocol protocol, + const QString &protocolString); + static QSslCipher createCiphersuite(const QString &name, const QString &keyExchangeMethod, + const QString &encryptionMethod, + const QString &authenticationMethod, + int bits, QSsl::SslProtocol protocol, + const QString &protocolString); + + // Those statics are implemented using QSslSocketPrivate (which is not exported, + // unlike QTlsBackend). + static QList<QSslCipher> defaultCiphers(); + static QList<QSslCipher> defaultDtlsCiphers(); + + static void setDefaultCiphers(const QList<QSslCipher> &ciphers); + static void setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers); + static void setDefaultSupportedCiphers(const QList<QSslCipher> &ciphers); + + static void resetDefaultEllipticCurves(); + + static void setDefaultCaCertificates(const QList<QSslCertificate> &certs); + + // Many thanks to people who designed QSslConfiguration with hidden + // data-members, that sneakily set by some 'friend' classes, having + // some twisted logic. + static bool rootLoadingOnDemandAllowed(const QSslConfiguration &configuration); + static void storePeerCertificate(QSslConfiguration &configuration, const QSslCertificate &peerCert); + static void storePeerCertificateChain(QSslConfiguration &configuration, + const QList<QSslCertificate> &peerCertificateChain); + static void clearPeerCertificates(QSslConfiguration &configuration); + // And those are even worse, this is where we don't have the original configuration, + // and can have only a copy. So instead we go to d->privateConfiguration.someMember: + static void clearPeerCertificates(QSslSocketPrivate *d); + static void setPeerSessionShared(QSslSocketPrivate *d, bool shared); + static void setSessionAsn1(QSslSocketPrivate *d, const QByteArray &asn1); + static void setSessionLifetimeHint(QSslSocketPrivate *d, int hint); + using AlpnNegotiationStatus = QSslConfiguration::NextProtocolNegotiationStatus; + static void setAlpnStatus(QSslSocketPrivate *d, AlpnNegotiationStatus st); + static void setNegotiatedProtocol(QSslSocketPrivate *d, const QByteArray &protocol); + static void storePeerCertificate(QSslSocketPrivate *d, const QSslCertificate &peerCert); + static void storePeerCertificateChain(QSslSocketPrivate *d, const QList<QSslCertificate> &peerChain); + static void addTustedRoot(QSslSocketPrivate *d, const QSslCertificate &rootCert);// TODO: "addTrusted..." + // The next one - is a "very important" feature! Kidding ... + static void setEphemeralKey(QSslSocketPrivate *d, const QSslKey &key); + + virtual void forceAutotestSecurityLevel(); +#endif // QT_CONFIG(ssl) -#define QTlsBackendFactory_iid "org.qt-project.Qt.QTlsBackendFactory" -Q_DECLARE_INTERFACE(QTlsBackendFactory, QTlsBackendFactory_iid); + Q_DISABLE_COPY_MOVE(QTlsBackend) +}; +#define QTlsBackend_iid "org.qt-project.Qt.QTlsBackend" +Q_DECLARE_INTERFACE(QTlsBackend, QTlsBackend_iid); QT_END_NAMESPACE diff --git a/src/network/ssl/qwindowscarootfetcher.cpp b/src/network/ssl/qwindowscarootfetcher.cpp deleted file mode 100644 index c414ca580b..0000000000 --- a/src/network/ssl/qwindowscarootfetcher.cpp +++ /dev/null @@ -1,289 +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 "qsslsocket_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 { - -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 = QSslSocketBackendPrivate::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) << "QSslSocketBackendPrivate::fetchCaRootForCert - 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 e98e59f0cf..0000000000 --- a/src/network/ssl/qwindowscarootfetcher_p.h +++ /dev/null @@ -1,95 +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 QWINDOWSCAROOTFETCHER_P_H -#define QWINDOWSCAROOTFETCHER_P_H - -#include <QtCore/QtGlobal> -#include <QtCore/QObject> - -#include "qsslsocket_p.h" - -#include "qsslsocket.h" -#include "qsslcertificate.h" - -#include <memory> - -// -// 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 |