summaryrefslogtreecommitdiffstats
path: root/src/network/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/ssl')
-rw-r--r--src/network/ssl/qocspresponse.cpp6
-rw-r--r--src/network/ssl/qocspresponse.h4
-rw-r--r--src/network/ssl/qssl.cpp14
-rw-r--r--src/network/ssl/qssl.h14
-rw-r--r--src/network/ssl/qsslcertificate.cpp93
-rw-r--r--src/network/ssl/qsslcertificate.h16
-rw-r--r--src/network/ssl/qsslcertificate_openssl.cpp2
-rw-r--r--src/network/ssl/qsslcertificate_qt.cpp2
-rw-r--r--src/network/ssl/qsslconfiguration.cpp104
-rw-r--r--src/network/ssl/qsslconfiguration.h8
-rw-r--r--src/network/ssl/qsslconfiguration_p.h8
-rw-r--r--src/network/ssl/qsslcontext_openssl.cpp78
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters.cpp2
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters.h4
-rw-r--r--src/network/ssl/qsslellipticcurve.cpp2
-rw-r--r--src/network/ssl/qsslellipticcurve.h6
-rw-r--r--src/network/ssl/qsslerror.cpp2
-rw-r--r--src/network/ssl/qsslerror.h2
-rw-r--r--src/network/ssl/qsslsocket.cpp237
-rw-r--r--src/network/ssl/qsslsocket.h52
-rw-r--r--src/network/ssl/qsslsocket_mac.cpp54
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp290
-rw-r--r--src/network/ssl/qsslsocket_openssl_p.h13
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp8
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h4
-rw-r--r--src/network/ssl/qsslsocket_p.h3
-rw-r--r--src/network/ssl/qsslsocket_schannel.cpp6
-rw-r--r--src/network/ssl/qsslsocket_winrt.cpp12
28 files changed, 641 insertions, 405 deletions
diff --git a/src/network/ssl/qocspresponse.cpp b/src/network/ssl/qocspresponse.cpp
index bf27bb768b..fbbf8d9708 100644
--- a/src/network/ssl/qocspresponse.cpp
+++ b/src/network/ssl/qocspresponse.cpp
@@ -232,20 +232,20 @@ Q_NETWORK_EXPORT bool operator==(const QOcspResponse &lhs, const QOcspResponse &
*/
/*!
- \fn uint qHash(const QOcspResponse &response, uint seed)
+ \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
\relates QHash
*/
-uint qHash(const QOcspResponse &response, uint seed) noexcept
+size_t qHash(const QOcspResponse &response, size_t seed) noexcept
{
const QOcspResponsePrivate *d = response.d.data();
Q_ASSERT(d);
QtPrivate::QHashCombine hasher;
- uint hash = hasher(seed, int(d->certificateStatus));
+ size_t hash = hasher(seed, int(d->certificateStatus));
hash = hasher(hash, int(d->revocationReason));
if (!d->signerCert.isNull())
hash = hasher(hash, d->signerCert);
diff --git a/src/network/ssl/qocspresponse.h b/src/network/ssl/qocspresponse.h
index cf6be5a369..1fc3377d58 100644
--- a/src/network/ssl/qocspresponse.h
+++ b/src/network/ssl/qocspresponse.h
@@ -73,7 +73,7 @@ enum class QOcspRevocationReason
};
class QOcspResponse;
-Q_NETWORK_EXPORT uint qHash(const QOcspResponse &response, uint seed = 0) noexcept;
+Q_NETWORK_EXPORT size_t qHash(const QOcspResponse &response, size_t seed = 0) noexcept;
class QOcspResponsePrivate;
class Q_NETWORK_EXPORT QOcspResponse
@@ -100,7 +100,7 @@ private:
friend class QSslSocketBackendPrivate;
friend Q_NETWORK_EXPORT bool operator==(const QOcspResponse &lhs, const QOcspResponse &rhs);
- friend Q_NETWORK_EXPORT uint qHash(const QOcspResponse &response, uint seed) noexcept;
+ friend Q_NETWORK_EXPORT size_t qHash(const QOcspResponse &response, size_t seed) noexcept;
QSharedDataPointer<QOcspResponsePrivate> d;
};
diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp
index beb03b646a..87b9a1b038 100644
--- a/src/network/ssl/qssl.cpp
+++ b/src/network/ssl/qssl.cpp
@@ -102,29 +102,16 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl");
\value IpAddressEntry An IP address entry; the entry contains an IP address
entry that the certificate is valid for, introduced in Qt 5.13.
- \note In Qt 4, this enum was called \c {AlternateNameEntryType}. That name
- is deprecated in Qt 5.
-
\sa QSslCertificate::subjectAlternativeNames()
*/
/*!
- \typedef QSsl::AlternateNameEntryType
- \obsolete
-
- Use QSsl::AlternativeNameEntryType instead.
-*/
-
-/*!
\enum QSsl::SslProtocol
Describes the protocol of the cipher.
- \value SslV3 SSLv3; not supported by QSslSocket.
- \value SslV2 SSLv2; not supported by QSslSocket.
\value TlsV1_0 TLSv1.0
\value TlsV1_0OrLater TLSv1.0 and later versions. This option is not available when using the WinRT backend due to platform limitations.
- \value TlsV1 Obsolete, means the same as TlsV1_0
\value TlsV1_1 TLSv1.1. When using the WinRT backend this option will also enable TLSv1.0.
\value TlsV1_1OrLater TLSv1.1 and later versions. This option is not available when using the WinRT backend due to platform limitations.
\value TlsV1_2 TLSv1.2. When using the WinRT backend this option will also enable TLSv1.0 and TLSv1.1.
@@ -137,7 +124,6 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl");
\value TlsV1_3OrLater TLSv1.3 and later versions. (Since Qt 5.12)
\value UnknownProtocol The cipher's protocol cannot be determined.
\value AnyProtocol Any supported protocol. This value is used by QSslSocket only.
- \value TlsV1SslV3 Same as TlsV1_0. This enumerator is deprecated, use TlsV1_0 instead.
\value SecureProtocols The default option, using protocols known to be secure.
*/
diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h
index b28c2a87b9..d0b417f9ed 100644
--- a/src/network/ssl/qssl.h
+++ b/src/network/ssl/qssl.h
@@ -72,25 +72,11 @@ namespace QSsl {
IpAddressEntry
};
-#if QT_DEPRECATED_SINCE(5,0)
- typedef AlternativeNameEntryType AlternateNameEntryType;
-#endif
-
enum SslProtocol {
-#if QT_DEPRECATED_SINCE(5, 15)
- SslV3,
- SslV2,
-#endif
TlsV1_0 = 2,
-#if QT_DEPRECATED_SINCE(5,0)
- TlsV1 = TlsV1_0,
-#endif
TlsV1_1,
TlsV1_2,
AnyProtocol,
-#if QT_DEPRECATED_SINCE(5, 15)
- TlsV1SslV3,
-#endif
SecureProtocols = AnyProtocol + 2,
TlsV1_0OrLater,
diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp
index 791dc7852f..7539c26ecb 100644
--- a/src/network/ssl/qsslcertificate.cpp
+++ b/src/network/ssl/qsslcertificate.cpp
@@ -373,15 +373,6 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa subjectInfo()
*/
-#if QT_DEPRECATED_SINCE(5,0)
-/*!
- \fn QMultiMap<QSsl::AlternateNameEntryType, QString> QSslCertificate::alternateSubjectNames() const
- \obsolete
-
- Use QSslCertificate::subjectAlternativeNames();
-*/
-#endif
-
/*!
\fn QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const
@@ -464,86 +455,6 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\since 5.0
*/
-#if QT_DEPRECATED_SINCE(5,15)
-/*!
- \obsolete
-
- Searches all files in the \a path for certificates encoded in the
- specified \a format and returns them in a list. \a path must be a file
- or a pattern matching one or more files, as specified by \a syntax.
-
- Example:
-
- \snippet code/src_network_ssl_qsslcertificate.cpp 0
-
- \sa fromData()
-*/
-QList<QSslCertificate> QSslCertificate::fromPath(const QString &path,
- QSsl::EncodingFormat format,
- QRegExp::PatternSyntax syntax)
-{
- // $, (,), *, +, ., ?, [, ,], ^, {, | and }.
-
- // make sure to use the same path separators on Windows and Unix like systems.
- QString sourcePath = QDir::fromNativeSeparators(path);
-
- // Find the path without the filename
- QString pathPrefix = sourcePath.left(sourcePath.lastIndexOf(QLatin1Char('/')));
-
- // Check if the path contains any special chars
- int pos = -1;
- if (syntax == QRegExp::Wildcard)
- pos = pathPrefix.indexOf(QRegExp(QLatin1String("[*?[]")));
- else if (syntax != QRegExp::FixedString)
- pos = sourcePath.indexOf(QRegExp(QLatin1String("[\\$\\(\\)\\*\\+\\.\\?\\[\\]\\^\\{\\}\\|]")));
- 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('/'));
- if (lastIndexOfSlash != -1)
- pathPrefix = pathPrefix.left(lastIndexOfSlash);
- else
- pathPrefix.clear();
- } else {
- // Check if the path is a file.
- if (QFileInfo(sourcePath).isFile()) {
- QFile file(sourcePath);
- QIODevice::OpenMode openMode = QIODevice::ReadOnly;
- if (format == QSsl::Pem)
- openMode |= QIODevice::Text;
- if (file.open(openMode))
- return QSslCertificate::fromData(file.readAll(), format);
- return QList<QSslCertificate>();
- }
- }
-
- // Special case - if the prefix ends up being nothing, use "." instead.
- int startIndex = 0;
- if (pathPrefix.isEmpty()) {
- pathPrefix = QLatin1String(".");
- startIndex = 2;
- }
-
- // The path can be a file or directory.
- QList<QSslCertificate> certs;
- QRegExp pattern(sourcePath, Qt::CaseSensitive, syntax);
- QDirIterator it(pathPrefix, QDir::Files, QDirIterator::FollowSymlinks | QDirIterator::Subdirectories);
- while (it.hasNext()) {
- QString filePath = startIndex == 0 ? it.next() : it.next().mid(startIndex);
- if (!pattern.exactMatch(filePath))
- continue;
-
- QFile file(filePath);
- QIODevice::OpenMode openMode = QIODevice::ReadOnly;
- if (format == QSsl::Pem)
- openMode |= QIODevice::Text;
- if (file.open(openMode))
- certs += QSslCertificate::fromData(file.readAll(), format);
- }
- return certs;
-}
-#endif // QT_DEPRECATED_SINCE(5,15)
-
/*!
\since 5.15
@@ -616,7 +527,7 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path,
#if QT_CONFIG(regularexpression)
if (syntax == PatternSyntax::Wildcard)
- sourcePath = QRegularExpression::wildcardToRegularExpression(sourcePath);
+ sourcePath = QRegularExpression::wildcardToRegularExpression(sourcePath, QRegularExpression::UnanchoredWildcardConversion);
QRegularExpression pattern(QRegularExpression::anchoredPattern(sourcePath));
#endif
@@ -850,7 +761,7 @@ QString QSslCertificate::subjectDisplayName() const
}
/*!
- \fn uint qHash(const QSslCertificate &key, uint seed)
+ \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
diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h
index 147919fa3b..46119ae046 100644
--- a/src/network/ssl/qsslcertificate.h
+++ b/src/network/ssl/qsslcertificate.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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.
@@ -50,7 +50,6 @@
#include <QtCore/qbytearray.h>
#include <QtCore/qcryptographichash.h>
#include <QtCore/qdatetime.h>
-#include <QtCore/qregexp.h>
#include <QtCore/qsharedpointer.h>
#include <QtCore/qmap.h>
#include <QtNetwork/qssl.h>
@@ -66,7 +65,7 @@ class QStringList;
class QSslCertificate;
// qHash is a friend, but we can't use default arguments for friends (ยง8.3.6.4)
-Q_NETWORK_EXPORT uint qHash(const QSslCertificate &key, uint seed = 0) noexcept;
+Q_NETWORK_EXPORT size_t qHash(const QSslCertificate &key, size_t seed = 0) noexcept;
class QSslCertificatePrivate;
class Q_NETWORK_EXPORT QSslCertificate
@@ -130,10 +129,6 @@ public:
QList<QByteArray> subjectInfoAttributes() const;
QList<QByteArray> issuerInfoAttributes() const;
-#if QT_DEPRECATED_SINCE(5,0)
- QT_DEPRECATED inline QMultiMap<QSsl::AlternateNameEntryType, QString>
- alternateSubjectNames() const { return subjectAlternativeNames(); }
-#endif
QMultiMap<QSsl::AlternativeNameEntryType, QString> subjectAlternativeNames() const;
QDateTime effectiveDate() const;
QDateTime expiryDate() const;
@@ -146,11 +141,6 @@ public:
QByteArray toDer() const;
QString toText() const;
-#if QT_DEPRECATED_SINCE(5,15)
- QT_DEPRECATED_X("Use the overload not using QRegExp")
- static QList<QSslCertificate> fromPath(const QString &path, QSsl::EncodingFormat format,
- QRegExp::PatternSyntax syntax);
-#endif
static QList<QSslCertificate> fromPath(const QString &path,
QSsl::EncodingFormat format = QSsl::Pem,
PatternSyntax syntax = PatternSyntax::FixedString);
@@ -180,7 +170,7 @@ private:
friend class QSslCertificatePrivate;
friend class QSslSocketBackendPrivate;
- friend Q_NETWORK_EXPORT uint qHash(const QSslCertificate &key, uint seed) noexcept;
+ friend Q_NETWORK_EXPORT size_t qHash(const QSslCertificate &key, size_t seed) noexcept;
};
Q_DECLARE_SHARED(QSslCertificate)
diff --git a/src/network/ssl/qsslcertificate_openssl.cpp b/src/network/ssl/qsslcertificate_openssl.cpp
index 6f1fb26add..2bb7c930f2 100644
--- a/src/network/ssl/qsslcertificate_openssl.cpp
+++ b/src/network/ssl/qsslcertificate_openssl.cpp
@@ -72,7 +72,7 @@ bool QSslCertificate::operator==(const QSslCertificate &other) const
return false;
}
-uint qHash(const QSslCertificate &key, uint seed) noexcept
+size_t qHash(const QSslCertificate &key, size_t seed) noexcept
{
if (X509 * const x509 = key.d->x509) {
const EVP_MD *sha1 = q_EVP_sha1();
diff --git a/src/network/ssl/qsslcertificate_qt.cpp b/src/network/ssl/qsslcertificate_qt.cpp
index 8b5035ad96..33deaf558f 100644
--- a/src/network/ssl/qsslcertificate_qt.cpp
+++ b/src/network/ssl/qsslcertificate_qt.cpp
@@ -64,7 +64,7 @@ bool QSslCertificate::operator==(const QSslCertificate &other) const
return d->derData == other.d->derData;
}
-uint qHash(const QSslCertificate &key, uint seed) noexcept
+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);
diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp
index f5ce02807f..adff0a249f 100644
--- a/src/network/ssl/qsslconfiguration.cpp
+++ b/src/network/ssl/qsslconfiguration.cpp
@@ -54,7 +54,6 @@ const QSsl::SslOptions QSslConfigurationPrivate::defaultSslOptions = QSsl::SslOp
|QSsl::SslOptionDisableSessionPersistence;
const char QSslConfiguration::ALPNProtocolHTTP2[] = "h2";
-const char QSslConfiguration::NextProtocolSpdy3_0[] = "spdy/3";
const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1";
/*!
@@ -134,12 +133,6 @@ const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1";
*/
/*!
- \variable QSslConfiguration::NextProtocolSpdy3_0
- \brief The value used for negotiating SPDY 3.0 during the Next
- Protocol Negotiation.
-*/
-
-/*!
\variable QSslConfiguration::NextProtocolHttp1_1
\brief The value used for negotiating HTTP 1.1 during the Next
Protocol Negotiation.
@@ -229,7 +222,9 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const
d->nextNegotiatedProtocol == other.d->nextNegotiatedProtocol &&
d->nextProtocolNegotiationStatus == other.d->nextProtocolNegotiationStatus &&
d->dtlsCookieEnabled == other.d->dtlsCookieEnabled &&
- d->ocspStaplingEnabled == other.d->ocspStaplingEnabled;
+ d->ocspStaplingEnabled == other.d->ocspStaplingEnabled &&
+ d->reportFromCallback == other.d->reportFromCallback &&
+ d->missingCertIsFatal == other.d->missingCertIsFatal;
}
/*!
@@ -274,7 +269,9 @@ bool QSslConfiguration::isNull() const
d->nextAllowedProtocols.isEmpty() &&
d->nextNegotiatedProtocol.isNull() &&
d->nextProtocolNegotiationStatus == QSslConfiguration::NextProtocolNegotiationNone &&
- d->ocspStaplingEnabled == false);
+ d->ocspStaplingEnabled == false &&
+ d->reportFromCallback == false &&
+ d->missingCertIsFatal == false);
}
/*!
@@ -1035,7 +1032,7 @@ QByteArray QSslConfiguration::nextNegotiatedProtocol() const
Whether or not the negotiation succeeded can be queried through
nextProtocolNegotiationStatus().
- \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), allowedNextProtocols(), QSslConfiguration::NextProtocolSpdy3_0, QSslConfiguration::NextProtocolHttp1_1
+ \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), allowedNextProtocols(), QSslConfiguration::NextProtocolHttp1_1
*/
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
void QSslConfiguration::setAllowedNextProtocols(const QList<QByteArray> &protocols)
@@ -1053,7 +1050,7 @@ void QSslConfiguration::setAllowedNextProtocols(QList<QByteArray> protocols)
server through the Next Protocol Negotiation (NPN) or Application-Layer
Protocol Negotiation (ALPN) TLS extension, as set by setAllowedNextProtocols().
- \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), setAllowedNextProtocols(), QSslConfiguration::NextProtocolSpdy3_0, QSslConfiguration::NextProtocolHttp1_1
+ \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), setAllowedNextProtocols(), QSslConfiguration::NextProtocolHttp1_1
*/
QList<QByteArray> QSslConfiguration::allowedNextProtocols() const
{
@@ -1199,6 +1196,91 @@ bool QSslConfiguration::ocspStaplingEnabled() const
return d->ocspStaplingEnabled;
}
+/*!
+ \since 6.0
+
+ Returns true if a verification callback will emit QSslSocket::handshakeInterruptedOnError()
+ early, before concluding the handshake.
+
+ \note This function always returns false for all backends but OpenSSL.
+
+ \sa setHandshakeMustInterruptOnError(), QSslSocket::handshakeInterruptedOnError(), QSslSocket::continueInterruptedHandshake()
+*/
+bool QSslConfiguration::handshakeMustInterruptOnError() const
+{
+ return d->reportFromCallback;
+}
+
+/*!
+ \since 6.0
+
+ If \a interrupt is true and the underlying backend supports this option,
+ errors found during certificate verification are reported immediately
+ by emitting QSslSocket::handshakeInterruptedOnError(). This allows
+ to stop the unfinished handshake and send a proper alert message to
+ a peer. No special action is required from the application in this case.
+ QSslSocket will close the connection after sending the alert message.
+ If the application after inspecting the error wants to continue the
+ handshake, it must call QSslSocket::continueInterruptedHandshake()
+ from its slot function. The signal-slot connection must be direct.
+
+ \note When interrupting handshake is enabled, errors that would otherwise
+ be reported by QSslSocket::peerVerifyError() are instead only reported by
+ QSslSocket::handshakeInterruptedOnError().
+ \note Even if the handshake was continued, these errors will be
+ reported when emitting QSslSocket::sslErrors() signal (and thus must
+ be ignored in the corresponding function slot).
+
+ \sa handshakeMustInterruptOnError(), QSslSocket::handshakeInterruptedOnError(), QSslSocket::continueInterruptedHandshake()
+*/
+void QSslConfiguration::setHandshakeMustInterruptOnError(bool interrupt)
+{
+#if QT_CONFIG(openssl)
+ d->reportFromCallback = interrupt;
+#else
+ Q_UNUSED(interrupt);
+ qCWarning(lcSsl, "This operation requires OpenSSL as TLS backend");
+#endif
+}
+
+/*!
+ \since 6.0
+
+ Returns true if errors with code QSslError::NoPeerCertificate
+ cannot be ignored.
+
+ \note Always returns false for all TLS backends but OpenSSL.
+
+ \sa QSslSocket::ignoreSslErrors(), setMissingCertificateIsFatal()
+*/
+bool QSslConfiguration::missingCertificateIsFatal() const
+{
+ return d->missingCertIsFatal;
+}
+
+/*!
+ \since 6.0
+
+ If \a cannotRecover is true, and verification mode in use is
+ QSslSocket::VerifyPeer or QSslSocket::AutoVerifyPeer (for a
+ client-side socket), the missing peer's certificate would be
+ treated as an unrecoverable error that cannot be ignored. A proper
+ alert message will be sent to the peer before closing the connection.
+
+ \note Only available if Qt was configured and built with OpenSSL backend.
+
+ \sa QSslSocket::ignoreSslErrors(), QSslSocket::PeerVerifyMode, missingCertificateIsFatal()
+*/
+void QSslConfiguration::setMissingCertificateIsFatal(bool cannotRecover)
+{
+#if QT_CONFIG(openssl)
+ d->missingCertIsFatal = cannotRecover;
+#else
+ Q_UNUSED(cannotRecover);
+ qCWarning(lcSsl, "Handling a missing certificate as a fatal error requires an OpenSSL backend");
+#endif // openssl
+}
+
/*! \internal
*/
bool QSslConfigurationPrivate::peerSessionWasShared(const QSslConfiguration &configuration) {
diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h
index 1c181121f4..1ce01dbee9 100644
--- a/src/network/ssl/qsslconfiguration.h
+++ b/src/network/ssl/qsslconfiguration.h
@@ -66,7 +66,6 @@
QT_BEGIN_NAMESPACE
-template<typename T> class QList;
class QSslCertificate;
class QSslCipher;
class QSslKey;
@@ -174,6 +173,12 @@ public:
static void setDefaultDtlsConfiguration(const QSslConfiguration &configuration);
#endif // dtls
+ bool handshakeMustInterruptOnError() const;
+ void setHandshakeMustInterruptOnError(bool interrupt);
+
+ bool missingCertificateIsFatal() const;
+ void setMissingCertificateIsFatal(bool cannotRecover);
+
void setOcspStaplingEnabled(bool enable);
bool ocspStaplingEnabled() const;
@@ -194,7 +199,6 @@ public:
NextProtocolNegotiationStatus nextProtocolNegotiationStatus() const;
static const char ALPNProtocolHTTP2[];
- static const char NextProtocolSpdy3_0[];
static const char NextProtocolHttp1_1[];
private:
diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h
index 83126bb9a0..6ee3490df6 100644
--- a/src/network/ssl/qsslconfiguration_p.h
+++ b/src/network/ssl/qsslconfiguration_p.h
@@ -149,6 +149,14 @@ public:
const bool ocspStaplingEnabled = false;
#endif
+#if QT_CONFIG(openssl)
+ bool reportFromCallback = false;
+ bool missingCertIsFatal = false;
+#else
+ const bool reportFromCallback = false;
+ const bool missingCertIsFatal = false;
+#endif // openssl
+
// in qsslsocket.cpp:
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);
diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp
index 0aa8a4f438..78e37460c8 100644
--- a/src/network/ssl/qsslcontext_openssl.cpp
+++ b/src/network/ssl/qsslcontext_openssl.cpp
@@ -56,6 +56,7 @@ QT_BEGIN_NAMESPACE
// 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)
@@ -290,42 +291,31 @@ void QSslContext::initSslContext(QSslContext *sslContext, QSslSocket::SslMode mo
bool unsupportedProtocol = false;
bool isDtls = false;
init_context:
- if (sslContext->sslConfiguration.protocol() == QSsl::SslV2) {
- // SSL 2 is no longer supported, but chosen deliberately -> error
- sslContext->ctx = nullptr;
- unsupportedProtocol = true;
- } else if (sslContext->sslConfiguration.protocol() == QSsl::SslV3) {
- // SSL 3 is no longer supported, but chosen deliberately -> error
- sslContext->ctx = nullptr;
- unsupportedProtocol = true;
- } else {
- switch (sslContext->sslConfiguration.protocol()) {
- case QSsl::DtlsV1_0:
- case QSsl::DtlsV1_0OrLater:
- case QSsl::DtlsV1_2:
- case QSsl::DtlsV1_2OrLater:
+ 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());
+ 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");
-
+ 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:
+ 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;
+ 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());
- }
+ 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) {
@@ -377,7 +367,6 @@ init_context:
#endif // TLS1_3_VERSION
break;
// Ranges:
- case QSsl::TlsV1SslV3:
case QSsl::AnyProtocol:
case QSsl::SecureProtocols:
case QSsl::TlsV1_0OrLater:
@@ -419,12 +408,6 @@ init_context:
Q_UNREACHABLE();
break;
#endif // TLS1_3_VERSION
- case QSsl::SslV2:
- case QSsl::SslV3:
- // These protocols are not supported, and we handle
- // them as an error (see the code above).
- Q_UNREACHABLE();
- break;
case QSsl::UnknownProtocol:
break;
}
@@ -594,11 +577,20 @@ init_context:
if (sslContext->sslConfiguration.peerVerifyMode() == QSslSocket::VerifyNone) {
q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_NONE, nullptr);
} else {
- q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_PEER,
-#if QT_CONFIG(dtls)
- isDtls ? dtlscallbacks::q_X509DtlsCallback :
-#endif // dtls
- q_X509Callback);
+ 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
diff --git a/src/network/ssl/qssldiffiehellmanparameters.cpp b/src/network/ssl/qssldiffiehellmanparameters.cpp
index 7807afaa30..c8f3e564a5 100644
--- a/src/network/ssl/qssldiffiehellmanparameters.cpp
+++ b/src/network/ssl/qssldiffiehellmanparameters.cpp
@@ -316,7 +316,7 @@ QDebug operator<<(QDebug debug, const QSslDiffieHellmanParameters &dhparam)
Returns an hash value for \a dhparam, using \a seed to seed
the calculation.
*/
-uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed) noexcept
+size_t qHash(const QSslDiffieHellmanParameters &dhparam, size_t seed) noexcept
{
return qHash(dhparam.d->derData, seed);
}
diff --git a/src/network/ssl/qssldiffiehellmanparameters.h b/src/network/ssl/qssldiffiehellmanparameters.h
index f62a3b8f44..6a3cf01ddc 100644
--- a/src/network/ssl/qssldiffiehellmanparameters.h
+++ b/src/network/ssl/qssldiffiehellmanparameters.h
@@ -56,7 +56,7 @@ class QSslDiffieHellmanParametersPrivate;
class QSslDiffieHellmanParameters;
// qHash is a friend, but we can't use default arguments for friends (ยง8.3.6.4)
-Q_NETWORK_EXPORT uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed = 0) noexcept;
+Q_NETWORK_EXPORT size_t qHash(const QSslDiffieHellmanParameters &dhparam, size_t seed = 0) noexcept;
#ifndef QT_NO_DEBUG_STREAM
class QDebug;
@@ -106,7 +106,7 @@ private:
#ifndef QT_NO_DEBUG_STREAM
friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslDiffieHellmanParameters &dhparam);
#endif
- friend Q_NETWORK_EXPORT uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed) noexcept;
+ friend Q_NETWORK_EXPORT size_t qHash(const QSslDiffieHellmanParameters &dhparam, size_t seed) noexcept;
};
Q_DECLARE_SHARED(QSslDiffieHellmanParameters)
diff --git a/src/network/ssl/qsslellipticcurve.cpp b/src/network/ssl/qsslellipticcurve.cpp
index 5608d32fa7..f7faa607bd 100644
--- a/src/network/ssl/qsslellipticcurve.cpp
+++ b/src/network/ssl/qsslellipticcurve.cpp
@@ -156,7 +156,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn uint qHash(QSslEllipticCurve curve, uint seed)
+ \fn size_t qHash(QSslEllipticCurve curve, size_t seed)
\since 5.5
\relates QHash
diff --git a/src/network/ssl/qsslellipticcurve.h b/src/network/ssl/qsslellipticcurve.h
index 28de3a03b4..c63552c680 100644
--- a/src/network/ssl/qsslellipticcurve.h
+++ b/src/network/ssl/qsslellipticcurve.h
@@ -52,7 +52,7 @@ QT_BEGIN_NAMESPACE
class QSslEllipticCurve;
// qHash is a friend, but we can't use default arguments for friends (ยง8.3.6.4)
-Q_DECL_CONSTEXPR uint qHash(QSslEllipticCurve curve, uint seed = 0) noexcept;
+Q_DECL_CONSTEXPR size_t qHash(QSslEllipticCurve curve, size_t seed = 0) noexcept;
class QSslEllipticCurve {
public:
@@ -78,7 +78,7 @@ private:
int id;
friend Q_DECL_CONSTEXPR bool operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs) noexcept;
- friend Q_DECL_CONSTEXPR uint qHash(QSslEllipticCurve curve, uint seed) noexcept;
+ friend Q_DECL_CONSTEXPR size_t qHash(QSslEllipticCurve curve, size_t seed) noexcept;
friend class QSslContext;
friend class QSslSocketPrivate;
@@ -87,7 +87,7 @@ private:
Q_DECLARE_TYPEINFO(QSslEllipticCurve, Q_PRIMITIVE_TYPE);
-Q_DECL_CONSTEXPR inline uint qHash(QSslEllipticCurve curve, uint seed) noexcept
+Q_DECL_CONSTEXPR inline size_t qHash(QSslEllipticCurve curve, size_t seed) noexcept
{ return qHash(curve.id, seed); }
Q_DECL_CONSTEXPR inline bool operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs) noexcept
diff --git a/src/network/ssl/qsslerror.cpp b/src/network/ssl/qsslerror.cpp
index cdc018a508..5e935adf09 100644
--- a/src/network/ssl/qsslerror.cpp
+++ b/src/network/ssl/qsslerror.cpp
@@ -362,7 +362,7 @@ QSslCertificate QSslError::certificate() const
\since 5.4
\relates QHash
*/
-uint qHash(const QSslError &key, uint seed) noexcept
+size_t qHash(const QSslError &key, size_t seed) noexcept
{
QtPrivate::QHashCombine hash;
seed = hash(seed, key.error());
diff --git a/src/network/ssl/qsslerror.h b/src/network/ssl/qsslerror.h
index 28eb1a9ea8..834684cd9d 100644
--- a/src/network/ssl/qsslerror.h
+++ b/src/network/ssl/qsslerror.h
@@ -124,7 +124,7 @@ private:
};
Q_DECLARE_SHARED(QSslError)
-Q_NETWORK_EXPORT uint qHash(const QSslError &key, uint seed = 0) noexcept;
+Q_NETWORK_EXPORT size_t qHash(const QSslError &key, size_t seed = 0) noexcept;
#ifndef QT_NO_DEBUG_STREAM
class QDebug;
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index f411732036..5313e97430 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -228,6 +228,74 @@
*/
/*!
+ \enum QAlertLevel
+ \brief Describes the level of an alert message
+ \relates QSslSocket
+ \since 6.0
+
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+ This enum describes the level of an alert message that was sent
+ or received.
+
+ \value Warning Non-fatal alert message
+ \value Fatal Fatal alert message, the underlying backend will
+ handle such an alert properly and close the connection.
+ \value Unknown An alert of unknown level of severity.
+*/
+
+/*!
+ \enum QAlertType
+ \brief Enumerates possible codes that an alert message can have
+ \relates QSslSocket
+ \since 6.0
+
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+ See \l{https://tools.ietf.org/html/rfc8446#page-85}{RFC 8446, section 6}
+ for the possible values and their meaning.
+
+ \value CloseNotify,
+ \value UnexpectedMessage
+ \value BadRecordMac
+ \value RecordOverflow
+ \value DecompressionFailure
+ \value HandshakeFailure
+ \value NoCertificate
+ \value BadCertificate
+ \value UnsupportedCertificate
+ \value CertificateRevoked
+ \value CertificateExpired
+ \value CertificateUnknown
+ \value IllegalParameter
+ \value UnknownCa
+ \value AccessDenied
+ \value DecodeError
+ \value DecryptError
+ \value ExportRestriction
+ \value ProtocolVersion
+ \value InsufficientSecurity
+ \value InternalError
+ \value InappropriateFallback
+ \value UserCancelled
+ \value NoRenegotiation
+ \value MissingExtension
+ \value UnsupportedExtension
+ \value CertificateUnobtainable
+ \value UnrecognizedName
+ \value BadCertificateStatusResponse
+ \value BadCertificateHashValue
+ \value UnknownPskIdentity
+ \value CertificateRequired
+ \value NoApplicationProtocol
+ \value UnknownAlertMessage
+*/
+
+/*!
\fn void QSslSocket::encrypted()
This signal is emitted when QSslSocket enters encrypted mode. After this
@@ -323,6 +391,49 @@
*/
/*!
+ \fn void QSslSocket::alertSent(QAlertLevel level, QAlertType type, const QString &description)
+
+ QSslSocket emits this signal if an alert message was sent 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(), QAlertLevel, QAlertType
+*/
+
+/*!
+ \fn void QSslSocket::alertReceived(QAlertLevel level, QAlertType type, const QString &description)
+
+ QSslSocket emits this signal if an alert message was received 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(), QAlertLevel, QAlertType
+*/
+
+/*!
+ \fn void QSslSocket::handshakeInterruptedOnError(const QSslError &error)
+
+ QSslSocket emits this signal if a certificate verification error was
+ found 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 continueInterruptedHandshake(), sslErrors(), QSslConfiguration::setHandshakeMustInterruptOnError()
+*/
+
+/*!
\fn void QSslSocket::newSessionTicketReceived()
\since 5.15
@@ -993,7 +1104,10 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration)
#if QT_CONFIG(ocsp)
d->configuration.ocspStaplingEnabled = configuration.ocspStaplingEnabled();
#endif
-
+#if QT_CONFIG(openssl)
+ d->configuration.reportFromCallback = configuration.handshakeMustInterruptOnError();
+ d->configuration.missingCertIsFatal = configuration.missingCertificateIsFatal();
+#endif // openssl
// if the CA certificates were set explicitly (either via
// QSslConfiguration::setCaCertificates() or QSslSocket::setCaCertificates(),
// we cannot load the certificates on demand
@@ -1413,40 +1527,6 @@ QList<QSslCipher> QSslSocket::supportedCiphers()
/*!
\deprecated
- Use QSslConfiguration::addCaCertificates() instead.
-
- Searches all files in the \a path for certificates encoded in the
- specified \a format and adds them to this socket's CA certificate
- database. \a path must be a file or a pattern matching one or more
- files, as specified by \a syntax. Returns \c true if one or more
- certificates are added to the socket's CA certificate database;
- otherwise returns \c false.
-
- The CA certificate database is used by the socket during the
- handshake phase to validate the peer's certificate.
-
- For more precise control, use addCaCertificate().
-
- \sa addCaCertificate(), QSslCertificate::fromPath()
-*/
-bool QSslSocket::addCaCertificates(const QString &path, QSsl::EncodingFormat format,
- QRegExp::PatternSyntax syntax)
-{
- Q_D(QSslSocket);
-QT_WARNING_PUSH
-QT_WARNING_DISABLE_DEPRECATED
- QList<QSslCertificate> certs = QSslCertificate::fromPath(path, format, syntax);
-QT_WARNING_POP
- if (certs.isEmpty())
- return false;
-
- d->configuration.caCertificates += certs;
- return true;
-}
-
-/*!
- \deprecated
-
Use QSslConfiguration::addCaCertificate() instead.
Adds the \a certificate to this socket's CA certificate database.
@@ -1533,29 +1613,6 @@ QList<QSslCertificate> QSslSocket::caCertificates() const
/*!
\deprecated
- Use QSslConfiguration::addCaCertificates() on the default QSslConfiguration instead.
-
- Searches all files in the \a path for certificates with the
- specified \a encoding and adds them to the default CA certificate
- database. \a path can be an explicit file, or it can contain
- wildcards in the format specified by \a syntax. Returns \c true if
- any CA certificates are added to the default database.
-
- Each SSL socket's CA certificate database is initialized to the
- default CA certificate database.
-
- \sa QSslConfiguration::caCertificates(), QSslConfiguration::addCaCertificates(),
- QSslConfiguration::addCaCertificate()
-*/
-bool QSslSocket::addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat encoding,
- QRegExp::PatternSyntax syntax)
-{
- return QSslSocketPrivate::addDefaultCaCertificates(path, encoding, syntax);
-}
-
-/*!
- \deprecated
-
Use QSslConfiguration::addCaCertificate() on the default QSslConfiguration instead.
Adds \a certificate to the default CA certificate database. Each
@@ -2083,6 +2140,23 @@ void QSslSocket::ignoreSslErrors(const QList<QSslError> &errors)
d->ignoreErrorsList = errors;
}
+
+/*!
+ \since 6.0
+
+ If an application wants to conclude a handshake even after receiving
+ handshakeInterruptedOnError() signal, it must call this function.
+ This call must be done from a slot function attached to the signal.
+ The signal-slot connection must be direct.
+
+ \sa handshakeInterruptedOnError(), QSslConfiguration::setHandshakeMustInterruptOnError()
+*/
+void QSslSocket::continueInterruptedHandshake()
+{
+ Q_D(QSslSocket);
+ d->handshakeInterrupted = false;
+}
+
/*!
\internal
*/
@@ -2257,13 +2331,24 @@ void QSslSocketPrivate::init()
*/
bool QSslSocketPrivate::verifyProtocolSupported(const char *where)
{
- if (configuration.protocol == QSsl::SslV2 || configuration.protocol == QSsl::SslV3) {
- qCWarning(lcSsl) << where << "Attempted to use an unsupported protocol.";
+ QLatin1String protocolName("DTLS");
+ 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");
+ Q_FALLTHROUGH();
+ case QSsl::DtlsV1_0:
+ case QSsl::DtlsV1_2:
+ case QSsl::DtlsV1_0OrLater:
+ case QSsl::DtlsV1_2OrLater:
+ qCWarning(lcSsl) << where << "QSslConfiguration with unexpected protocol" << protocolName;
setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
QSslSocket::tr("Attempted to use an unsupported protocol."));
return false;
+ default:
+ return true;
}
- return true;
}
/*!
@@ -2376,28 +2461,6 @@ void QSslSocketPrivate::setDefaultCaCertificates(const QList<QSslCertificate> &c
/*!
\internal
*/
-bool QSslSocketPrivate::addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat format,
- QRegExp::PatternSyntax syntax)
-{
- QSslSocketPrivate::ensureInitialized();
-QT_WARNING_PUSH
-QT_WARNING_DISABLE_DEPRECATED
- QList<QSslCertificate> certs = QSslCertificate::fromPath(path, format, syntax);
-QT_WARNING_POP
- if (certs.isEmpty())
- return false;
-
- QMutexLocker locker(&globalData()->mutex);
- globalData()->config.detach();
- globalData()->config->caCertificates += certs;
- globalData()->dtlsConfig.detach();
- globalData()->dtlsConfig->caCertificates += certs;
- return true;
-}
-
-/*!
- \internal
-*/
void QSslSocketPrivate::addDefaultCaCertificate(const QSslCertificate &cert)
{
QSslSocketPrivate::ensureInitialized();
@@ -2477,6 +2540,10 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri
#if QT_CONFIG(ocsp)
ptr->ocspStaplingEnabled = global->ocspStaplingEnabled;
#endif
+#if QT_CONFIG(openssl)
+ ptr->reportFromCallback = global->reportFromCallback;
+ ptr->missingCertIsFatal = global->missingCertIsFatal;
+#endif
}
/*!
@@ -2519,10 +2586,6 @@ void QSslSocketPrivate::createPlainSocket(QIODevice::OpenMode openMode)
q->setPeerName(QString());
plainSocket = new QTcpSocket(q);
-#ifndef QT_NO_BEARERMANAGEMENT // ### Qt6: Remove section
- //copy network session down to the plain socket (if it has been set)
- plainSocket->setProperty("_q_networksession", q->property("_q_networksession"));
-#endif
q->connect(plainSocket, SIGNAL(connected()),
q, SLOT(_q_connectedSlot()),
Qt::DirectConnection);
diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h
index 298e7aa6c8..7fd2b1cb08 100644
--- a/src/network/ssl/qsslsocket.h
+++ b/src/network/ssl/qsslsocket.h
@@ -43,7 +43,6 @@
#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qlist.h>
-#include <QtCore/qregexp.h>
#include <QtCore/qvector.h>
#ifndef QT_NO_SSL
# include <QtNetwork/qtcpsocket.h>
@@ -63,6 +62,49 @@ class QSslEllipticCurve;
class QSslPreSharedKeyAuthenticator;
class QOcspResponse;
+enum class QAlertLevel {
+ Warning,
+ Fatal,
+ Unknown
+};
+
+enum class QAlertType {
+ CloseNotify,
+ UnexpectedMessage = 10,
+ BadRecordMac = 20,
+ RecordOverflow = 22,
+ DecompressionFailure = 30, // reserved
+ HandshakeFailure = 40,
+ NoCertificate = 41, // reserved
+ BadCertificate = 42,
+ UnsupportedCertificate = 43,
+ CertificateRevoked = 44,
+ CertificateExpired = 45,
+ CertificateUnknown = 46,
+ IllegalParameter = 47,
+ UnknownCa = 48,
+ AccessDenied = 49,
+ DecodeError = 50,
+ DecryptError = 51,
+ ExportRestriction = 60, // reserved
+ ProtocolVersion = 70,
+ InsufficientSecurity = 71,
+ InternalError = 80,
+ InappropriateFallback = 86,
+ UserCancelled = 90,
+ NoRenegotiation = 100,
+ MissingExtension = 109,
+ UnsupportedExtension = 110,
+ CertificateUnobtainable = 111, // reserved
+ UnrecognizedName = 112,
+ BadCertificateStatusResponse = 113,
+ BadCertificateHashValue = 114, // reserved
+ UnknownPskIdentity = 115,
+ CertificateRequired = 116,
+ NoApplicationProtocol = 120,
+ UnknownAlertMessage = 255
+};
+
class QSslSocketPrivate;
class Q_NETWORK_EXPORT QSslSocket : public QTcpSocket
{
@@ -165,8 +207,6 @@ public:
// CA settings.
#if QT_DEPRECATED_SINCE(5, 15)
- QT_DEPRECATED_X("Use QSslConfiguration::addCaCertificates()") bool addCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
- QRegExp::PatternSyntax syntax = QRegExp::FixedString);
QT_DEPRECATED_X("Use QSslConfiguration::addCaCertificate()") void addCaCertificate(const QSslCertificate &certificate);
QT_DEPRECATED_X("Use QSslConfiguration::addCaCertificates()") void addCaCertificates(const QList<QSslCertificate> &certificates);
#endif // QT_DEPRECATED_SINCE(5, 15)
@@ -175,8 +215,6 @@ public:
QT_DEPRECATED_X("Use QSslConfiguration::caCertificates()") QList<QSslCertificate> caCertificates() const;
#endif // QT_DEPRECATED_SINCE(5, 5)
#if QT_DEPRECATED_SINCE(5, 15)
- QT_DEPRECATED static bool addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
- QRegExp::PatternSyntax syntax = QRegExp::FixedString);
QT_DEPRECATED static void addDefaultCaCertificate(const QSslCertificate &certificate);
QT_DEPRECATED static void addDefaultCaCertificates(const QList<QSslCertificate> &certificates);
#endif // QT_DEPRECATED_SINCE(5, 15)
@@ -204,6 +242,7 @@ public:
static QString sslLibraryBuildVersionString();
void ignoreSslErrors(const QList<QSslError> &errors);
+ void continueInterruptedHandshake();
public Q_SLOTS:
void startClientEncryption();
@@ -218,6 +257,9 @@ Q_SIGNALS:
void encryptedBytesWritten(qint64 totalBytes);
void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator);
void newSessionTicketReceived();
+ void alertSent(QAlertLevel level, QAlertType type, const QString &description);
+ void alertReceived(QAlertLevel level, QAlertType type, const QString &description);
+ void handshakeInterruptedOnError(const QSslError &error);
protected:
qint64 readData(char *data, qint64 maxlen) override;
diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp
index 77e847e972..1ae32b0330 100644
--- a/src/network/ssl/qsslsocket_mac.cpp
+++ b/src/network/ssl/qsslsocket_mac.cpp
@@ -496,10 +496,6 @@ QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const
}
switch (protocol) {
- case kSSLProtocol2:
- return QSsl::SslV2;
- case kSSLProtocol3:
- return QSsl::SslV3;
case kTLSProtocol1:
return QSsl::TlsV1_0;
case kTLSProtocol11:
@@ -657,23 +653,6 @@ QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSLCipherSuite(SSLCipherSui
QSslCipher ciph;
switch (cipher) {
// Sorted as in CipherSuite.h (and groupped by their RFC)
- case SSL_RSA_WITH_NULL_MD5:
- ciph.d->name = QLatin1String("NULL-MD5");
- ciph.d->protocol = QSsl::SslV3;
- break;
- case SSL_RSA_WITH_NULL_SHA:
- ciph.d->name = QLatin1String("NULL-SHA");
- ciph.d->protocol = QSsl::SslV3;
- break;
- case SSL_RSA_WITH_RC4_128_MD5:
- ciph.d->name = QLatin1String("RC4-MD5");
- ciph.d->protocol = QSsl::SslV3;
- break;
- case SSL_RSA_WITH_RC4_128_SHA:
- ciph.d->name = QLatin1String("RC4-SHA");
- ciph.d->protocol = QSsl::SslV3;
- break;
-
// TLS addenda using AES, per RFC 3268
case TLS_RSA_WITH_AES_128_CBC_SHA:
ciph.d->name = QLatin1String("AES128-SHA");
@@ -822,12 +801,8 @@ QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSLCipherSuite(SSLCipherSui
ciph.d->isNull = false;
// protocol
- if (ciph.d->protocol == QSsl::SslV3) {
- ciph.d->protocolString = QLatin1String("SSLv3");
- } else {
- ciph.d->protocol = QSsl::TlsV1_2;
- ciph.d->protocolString = QLatin1String("TLSv1.2");
- }
+ ciph.d->protocol = QSsl::TlsV1_2;
+ ciph.d->protocolString = QLatin1String("TLSv1.2");
const auto bits = ciph.d->name.splitRef(QLatin1Char('-'));
if (bits.size() >= 2) {
@@ -1106,22 +1081,6 @@ bool QSslSocketBackendPrivate::setSessionProtocol()
{
Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)");
- // QSsl::SslV2 == kSSLProtocol2 is disabled in Secure Transport and
- // always fails with errSSLIllegalParam:
- // if (version < MINIMUM_STREAM_VERSION || version > MAXIMUM_STREAM_VERSION)
- // return errSSLIllegalParam;
- // where MINIMUM_STREAM_VERSION is SSL_Version_3_0, MAXIMUM_STREAM_VERSION is TLS_Version_1_2.
- if (configuration.protocol == QSsl::SslV2) {
- qCDebug(lcSsl) << "protocol QSsl::SslV2 is disabled";
- return false;
- }
-
- // SslV3 is unsupported.
- if (configuration.protocol == QSsl::SslV3) {
- qCDebug(lcSsl) << "protocol QSsl::SslV3 is disabled";
- return false;
- }
-
// 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
@@ -1162,13 +1121,6 @@ bool QSslSocketBackendPrivate::setSessionProtocol()
qCDebug(lcSsl) << plainSocket << "requesting : any";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
- } else if (configuration.protocol == QSsl::TlsV1SslV3) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : SSLv3 - TLSv1.2";
- #endif
- err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
- if (err == errSecSuccess)
- err = SSLSetProtocolVersionMax(context, kTLSProtocol1);
} else if (configuration.protocol == QSsl::SecureProtocols) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1 - TLSv1.2";
@@ -1213,8 +1165,6 @@ bool QSslSocketBackendPrivate::verifySessionProtocol() const
bool protocolOk = false;
if (configuration.protocol == QSsl::AnyProtocol)
protocolOk = true;
- else if (configuration.protocol == QSsl::TlsV1SslV3)
- protocolOk = (sessionProtocol() == QSsl::TlsV1_0);
else if (configuration.protocol == QSsl::SecureProtocols)
protocolOk = (sessionProtocol() >= QSsl::TlsV1_0);
else if (configuration.protocol == QSsl::TlsV1_0OrLater)
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 859216d097..b6570e2bdd 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -98,6 +98,123 @@
QT_BEGIN_NAMESPACE
+namespace {
+
+QAlertLevel tlsAlertLevel(int value)
+{
+ 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 QAlertLevel::Warning;
+ case 'F':
+ return QAlertLevel::Fatal;
+ default:;
+ }
+ }
+
+ return QAlertLevel::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;
+}
+
+QAlertType 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 QAlertType(value & 0xff);
+}
+
+} // 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;
@@ -266,11 +383,7 @@ QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(const SSL_CIPHER
QString protoString = descriptionList.at(1).toString();
ciph.d->protocolString = protoString;
ciph.d->protocol = QSsl::UnknownProtocol;
- if (protoString == QLatin1String("SSLv3"))
- ciph.d->protocol = QSsl::SslV3;
- else if (protoString == QLatin1String("SSLv2"))
- ciph.d->protocol = QSsl::SslV2;
- else if (protoString == QLatin1String("TLSv1"))
+ if (protoString == QLatin1String("TLSv1"))
ciph.d->protocol = QSsl::TlsV1_0;
else if (protoString == QLatin1String("TLSv1.1"))
ciph.d->protocol = QSsl::TlsV1_1;
@@ -425,12 +538,15 @@ int q_X509Callback(int ok, X509_STORE_CTX *ctx)
// 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 peers 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."
+ // "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, QSslSocketBackendPrivate::s_indexForSSLExtraData + 1));
+ errors = ErrorListPtr(q_SSL_get_ex_data(ssl, offset));
}
if (!errors) {
@@ -476,20 +592,23 @@ void q_setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers);
long QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions)
{
long options;
- if (protocol == QSsl::TlsV1SslV3)
- options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3;
- else if (protocol == QSsl::SecureProtocols)
- options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3;
- else if (protocol == QSsl::TlsV1_0OrLater)
- options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3;
- else if (protocol == QSsl::TlsV1_1OrLater)
- options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1;
- else if (protocol == QSsl::TlsV1_2OrLater)
- options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1;
- else if (protocol == 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;
- else
+ 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)
@@ -547,10 +666,7 @@ bool QSslSocketBackendPrivate::initSslContext()
return false;
}
- if (configuration.protocol != QSsl::SslV2 &&
- configuration.protocol != QSsl::SslV3 &&
- configuration.protocol != QSsl::UnknownProtocol &&
- mode == QSslSocket::SslClientMode) {
+ 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())
@@ -1223,18 +1339,32 @@ bool QSslSocketBackendPrivate::startHandshake()
if (inSetAndEmitError)
return false;
+ pendingFatalAlert = false;
+ errorsReportedFromCallback = false;
QVector<QSslErrorEntry> lastErrors;
- q_SSL_set_ex_data(ssl, s_indexForSSLExtraData + 1, &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 + 1, nullptr);
+ 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())
+ if (!lastErrors.isEmpty() || errorsReportedFromCallback)
storePeerCertificates();
- for (const auto &currentError : qAsConst(lastErrors)) {
- emit q->peerVerifyError(_q_OpenSSL_to_QSslError(currentError.code,
- configuration.peerCertificateChain.value(currentError.depth)));
- if (q->state() != QAbstractSocket::ConnectedState)
- break;
+
+ if (!errorsReportedFromCallback) {
+ for (const auto &currentError : qAsConst(lastErrors)) {
+ emit q->peerVerifyError(_q_OpenSSL_to_QSslError(currentError.code,
+ configuration.peerCertificateChain.value(currentError.depth)));
+ if (q->state() != QAbstractSocket::ConnectedState)
+ break;
+ }
}
errorList << lastErrors;
@@ -1258,6 +1388,10 @@ bool QSslSocketBackendPrivate::startHandshake()
{
const ScopedBool bg(inSetAndEmitError, true);
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, errorString);
+ if (pendingFatalAlert) {
+ trySendFatalAlert();
+ pendingFatalAlert = false;
+ }
}
q->abort();
}
@@ -1781,6 +1915,88 @@ bool QSslSocketBackendPrivate::checkOcspStatus()
#endif // ocsp
+void QSslSocketBackendPrivate::alertMessageSent(int value)
+{
+ Q_Q(QSslSocket);
+
+ const auto level = tlsAlertLevel(value);
+ if (level == QAlertLevel::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<QVector<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) {
@@ -1828,10 +2044,6 @@ QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const
int ver = q_SSL_version(ssl);
switch (ver) {
- case 0x2:
- return QSsl::SslV2;
- case 0x300:
- return QSsl::SslV3;
case 0x301:
return QSsl::TlsV1_0;
case 0x302:
diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h
index 47ccf06e71..6c1ffb7237 100644
--- a/src/network/ssl/qsslsocket_openssl_p.h
+++ b/src/network/ssl/qsslsocket_openssl_p.h
@@ -131,6 +131,10 @@ public:
SSL_SESSION *session;
QVector<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;
@@ -158,6 +162,15 @@ public:
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()
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index 627ae31651..777dc70565 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -156,6 +156,10 @@ 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)
#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)
@@ -842,7 +846,9 @@ bool q_resolveOpenSslSymbols()
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)
#ifdef TLS1_3_VERSION
RESOLVEFUNC(SSL_CTX_set_ciphersuites)
RESOLVEFUNC(SSL_set_psk_use_session_callback)
diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h
index e8f6b7e752..78b097fad6 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -736,6 +736,10 @@ int q_OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b);
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);
+
QT_END_NAMESPACE
#endif
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
index 87179c8083..25625a1313 100644
--- a/src/network/ssl/qsslsocket_p.h
+++ b/src/network/ssl/qsslsocket_p.h
@@ -140,8 +140,6 @@ public:
static QList<QSslCertificate> defaultCaCertificates();
static QList<QSslCertificate> systemCaCertificates();
static void setDefaultCaCertificates(const QList<QSslCertificate> &certs);
- static bool addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat format,
- QRegExp::PatternSyntax syntax);
static void addDefaultCaCertificate(const QSslCertificate &cert);
static void addDefaultCaCertificates(const QList<QSslCertificate> &certs);
Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QSslCertificate &cert,
@@ -209,6 +207,7 @@ protected:
bool flushTriggered;
bool systemOrSslErrorDetected = false;
QVector<QOcspResponse> ocspResponses;
+ bool handshakeInterrupted = false;
};
#if QT_CONFIG(securetransport) || QT_CONFIG(schannel)
diff --git a/src/network/ssl/qsslsocket_schannel.cpp b/src/network/ssl/qsslsocket_schannel.cpp
index e47fda17ab..c355abad73 100644
--- a/src/network/ssl/qsslsocket_schannel.cpp
+++ b/src/network/ssl/qsslsocket_schannel.cpp
@@ -226,12 +226,6 @@ DWORD toSchannelProtocol(QSsl::SslProtocol protocol)
protocols = SP_PROT_TLS1_0 | SP_PROT_TLS1_1 | SP_PROT_TLS1_2;
// @future Add TLS 1.3 when supported by Windows!
break;
- case QSsl::SslV2:
- case QSsl::SslV3:
- return DWORD(-1); // Not supported
- case QSsl::TlsV1SslV3:
- protocols = SP_PROT_TLS1_0;
- break;
case QSsl::TlsV1_0:
protocols = SP_PROT_TLS1_0;
break;
diff --git a/src/network/ssl/qsslsocket_winrt.cpp b/src/network/ssl/qsslsocket_winrt.cpp
index f3ca3dc257..5f5201fc82 100644
--- a/src/network/ssl/qsslsocket_winrt.cpp
+++ b/src/network/ssl/qsslsocket_winrt.cpp
@@ -230,13 +230,7 @@ void QSslSocketBackendPrivate::startClientEncryption()
QSsl::SslProtocol protocol = q->protocol();
switch (q->protocol()) {
- case QSsl::SslV2:
- case QSsl::SslV3:
- setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
- QStringLiteral("unsupported protocol"));
- return;
case QSsl::AnyProtocol:
- case QSsl::TlsV1SslV3:
protectionLevel = SocketProtectionLevel_Tls10;
break;
case QSsl::TlsV1_0:
@@ -270,7 +264,7 @@ void QSslSocketBackendPrivate::startClientEncryption()
}
// Sync custom certificates
- const QSet<QSslCertificate> caCertificates = configuration.caCertificates.toSet();
+ const QSet<QSslCertificate> caCertificates(configuration.caCertificates.constBegin(), configuration.caCertificates.constEnd());
const QSet<QSslCertificate> newCertificates = caCertificates - previousCaCertificates;
const QSet<QSslCertificate> oldCertificates = previousCaCertificates - caCertificates;
g->syncCaCertificates(newCertificates, oldCertificates);
@@ -393,7 +387,7 @@ void QSslSocketBackendPrivate::continueHandshake()
hr = control2->get_IgnorableServerCertificateErrors(&ignoreList);
Q_ASSERT_SUCCEEDED(hr);
- QSet<QSslError> ignoreErrors = ignoreErrorsList.toSet();
+ QSet<QSslError> ignoreErrors(ignoreErrorsList.constBegin(), ignoreErrorsList.constEnd());
for (int i = ChainValidationResult_Untrusted; i < ChainValidationResult_OtherErrors + 1; ++i) {
// Populate the native ignore list - break to add, continue to skip
switch (i) {
@@ -596,7 +590,7 @@ HRESULT QSslSocketBackendPrivate::onSslUpgrade(IAsyncAction *action, AsyncStatus
}
}
- sslErrors = errors.toList();
+ sslErrors = QList<QSslError>(errors.constBegin(), errors.constEnd());
// Peer validation
if (!configuration.peerCertificate.isNull()) {