summaryrefslogtreecommitdiffstats
path: root/src/network/ssl
diff options
context:
space:
mode:
authorLiang Qi <liang.qi@qt.io>2020-02-13 09:14:09 +0100
committerAlexandru Croitor <alexandru.croitor@qt.io>2020-02-13 18:31:40 +0100
commit6b2535ea15cdbdb2355416b604f072fc13ff36b2 (patch)
tree4bf1560bab77c8b315850c5337ba31a0ea87b5f0 /src/network/ssl
parent54c2cebabdda0280b8443c6947b6fee02445e138 (diff)
parent67491e2df5357706dbf88ddaf1f030ff095b4528 (diff)
Merge remote-tracking branch 'origin/5.15' into dev
Conflicts: examples/widgets/graphicsview/boxes/scene.h src/corelib/Qt5CoreMacros.cmake src/corelib/Qt6CoreMacros.cmake src/network/ssl/qsslsocket.cpp src/network/ssl/qsslsocket.h src/platformsupport/fontdatabases/windows/qwindowsfontenginedirectwrite.cpp src/testlib/CMakeLists.txt src/testlib/.prev_CMakeLists.txt tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp Disabled building manual tests with CMake for now, because qmake doesn't do it, and it confuses people. Done-With: Alexandru Croitor <alexandru.croitor@qt.io> Done-With: Volker Hilsheimer <volker.hilsheimer@qt.io> Change-Id: I865ae347bd01f4e59f16d007b66d175a52f1f152
Diffstat (limited to 'src/network/ssl')
-rw-r--r--src/network/ssl/qsslconfiguration.cpp10
-rw-r--r--src/network/ssl/qsslconfiguration.h2
-rw-r--r--src/network/ssl/qsslcontext_openssl.cpp21
-rw-r--r--src/network/ssl/qsslpresharedkeyauthenticator.cpp2
-rw-r--r--src/network/ssl/qsslsocket.cpp25
-rw-r--r--src/network/ssl/qsslsocket.h1
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp70
-rw-r--r--src/network/ssl/qsslsocket_openssl_p.h1
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp4
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h18
10 files changed, 143 insertions, 11 deletions
diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp
index b6199a2b16..8c7437a8de 100644
--- a/src/network/ssl/qsslconfiguration.cpp
+++ b/src/network/ssl/qsslconfiguration.cpp
@@ -657,6 +657,8 @@ void QSslConfiguration::setCaCertificates(const QList<QSslCertificate> &certific
}
/*!
+ \since 5.15
+
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
@@ -672,7 +674,7 @@ void QSslConfiguration::setCaCertificates(const QList<QSslCertificate> &certific
\sa addCaCertificate(), QSslCertificate::fromPath()
*/
bool QSslConfiguration::addCaCertificates(const QString &path, QSsl::EncodingFormat format,
- QRegExp::PatternSyntax syntax)
+ QSslCertificate::PatternSyntax syntax)
{
QList<QSslCertificate> certs = QSslCertificate::fromPath(path, format, syntax);
if (certs.isEmpty())
@@ -777,7 +779,7 @@ bool QSslConfiguration::testSslOption(QSsl::SslOption option) const
knowledge of the session allows for eavesdropping on data
encrypted with the session parameters.
- \sa setSessionTicket(), QSsl::SslOptionDisableSessionPersistence, setSslOption()
+ \sa setSessionTicket(), QSsl::SslOptionDisableSessionPersistence, setSslOption(), QSslSocket::newSessionTicketReceived()
*/
QByteArray QSslConfiguration::sessionTicket() const
{
@@ -792,7 +794,7 @@ QByteArray QSslConfiguration::sessionTicket() const
for this to work, and \a sessionTicket must be in ASN.1 format
as returned by sessionTicket().
- \sa sessionTicket(), QSsl::SslOptionDisableSessionPersistence, setSslOption()
+ \sa sessionTicket(), QSsl::SslOptionDisableSessionPersistence, setSslOption(), QSslSocket::newSessionTicketReceived()
*/
void QSslConfiguration::setSessionTicket(const QByteArray &sessionTicket)
{
@@ -810,7 +812,7 @@ void QSslConfiguration::setSessionTicket(const QByteArray &sessionTicket)
QSsl::SslOptionDisableSessionPersistence was not turned off,
this function returns -1.
- \sa sessionTicket(), QSsl::SslOptionDisableSessionPersistence, setSslOption()
+ \sa sessionTicket(), QSsl::SslOptionDisableSessionPersistence, setSslOption(), QSslSocket::newSessionTicketReceived()
*/
int QSslConfiguration::sessionTicketLifeTimeHint() const
{
diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h
index dc4587a835..5732deaaef 100644
--- a/src/network/ssl/qsslconfiguration.h
+++ b/src/network/ssl/qsslconfiguration.h
@@ -131,7 +131,7 @@ public:
QList<QSslCertificate> caCertificates() const;
void setCaCertificates(const QList<QSslCertificate> &certificates);
bool addCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
- QRegExp::PatternSyntax syntax = QRegExp::FixedString);
+ QSslCertificate::PatternSyntax syntax = QSslCertificate::FixedString);
void addCaCertificate(const QSslCertificate &certificate);
void addCaCertificates(const QList<QSslCertificate> &certificates);
diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp
index 574f48a2b5..78e37460c8 100644
--- a/src/network/ssl/qsslcontext_openssl.cpp
+++ b/src/network/ssl/qsslcontext_openssl.cpp
@@ -71,6 +71,10 @@ extern "C" int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie,
}
#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();
@@ -169,8 +173,8 @@ SSL* QSslContext::createSsl()
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()); // refcount is 1 already, set by function above
+ session = q_d2i_SSL_SESSION(nullptr, &data, m_sessionASN1.size());
+ // 'session' has refcount 1 already, set by the function above
}
if (session) {
@@ -568,7 +572,8 @@ init_context:
}
}
- // Initialize peer verification.
+ // 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 {
@@ -588,7 +593,17 @@ init_context:
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);
diff --git a/src/network/ssl/qsslpresharedkeyauthenticator.cpp b/src/network/ssl/qsslpresharedkeyauthenticator.cpp
index 01e1501763..ed6dbb87cf 100644
--- a/src/network/ssl/qsslpresharedkeyauthenticator.cpp
+++ b/src/network/ssl/qsslpresharedkeyauthenticator.cpp
@@ -141,7 +141,7 @@ QSslPreSharedKeyAuthenticator &QSslPreSharedKeyAuthenticator::operator=(const QS
/*!
\fn QSslPreSharedKeyAuthenticator &QSslPreSharedKeyAuthenticator::operator=(QSslPreSharedKeyAuthenticator &&authenticator)
- Move-assigns the the QSslPreSharedKeyAuthenticator object \a authenticator to this
+ Move-assigns the QSslPreSharedKeyAuthenticator object \a authenticator to this
object, and returns a reference to the moved instance.
*/
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index 504e27a463..64c6629645 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -432,6 +432,23 @@
\sa continueInterruptedHandshake(), sslErrors(), QSslConfiguration::setHandshakeMustInterruptOnError()
*/
+
+/*!
+ \fn void QSslSocket::newSessionTicketReceived()
+ \since 5.15
+
+ If TLS 1.3 protocol was negotiated during a handshake, QSslSocket
+ emits this signal after receiving NewSessionTicket message. Session
+ and session ticket's lifetime hint are updated in the socket's
+ configuration. The session can be used for session resumption (and
+ a shortened handshake) in future TLS connections.
+
+ \note This functionality enabled only with OpenSSL backend and requires
+ OpenSSL v 1.1.1 or above.
+
+ \sa QSslSocket::sslConfiguration(), QSslConfiguration::sessionTicket(), QSslConfiguration::sessionTicketLifeTimeHint()
+*/
+
#include "qssl_p.h"
#include "qsslsocket.h"
#include "qsslcipher.h"
@@ -1530,7 +1547,10 @@ bool QSslSocket::addCaCertificates(const QString &path, QSsl::EncodingFormat for
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;
@@ -2502,7 +2522,10 @@ bool QSslSocketPrivate::addDefaultCaCertificates(const QString &path, QSsl::Enco
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;
@@ -2642,7 +2665,7 @@ void QSslSocketPrivate::createPlainSocket(QIODevice::OpenMode openMode)
q->setPeerName(QString());
plainSocket = new QTcpSocket(q);
-#ifndef QT_NO_BEARERMANAGEMENT
+#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
diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h
index c12bb53334..e99c79a967 100644
--- a/src/network/ssl/qsslsocket.h
+++ b/src/network/ssl/qsslsocket.h
@@ -261,6 +261,7 @@ Q_SIGNALS:
void modeChanged(QSslSocket::SslMode newMode);
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);
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 11989cd2ef..f033d3b298 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -300,6 +300,22 @@ static int q_ssl_psk_use_session_callback(SSL *ssl, const EVP_MD *md, const unsi
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
@@ -1526,6 +1542,60 @@ void QSslSocketBackendPrivate::storePeerCertificates()
}
}
+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);
diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h
index 06af9f5974..6c1ffb7237 100644
--- a/src/network/ssl/qsslsocket_openssl_p.h
+++ b/src/network/ssl/qsslsocket_openssl_p.h
@@ -150,6 +150,7 @@ public:
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
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index 5b0a70d495..21f37789ff 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -163,6 +163,8 @@ DEFINEFUNC(const char *, SSL_alert_desc_string_long, int value, value, return nu
#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)
@@ -849,6 +851,8 @@ bool q_resolveOpenSslSymbols()
#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)
diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h
index ac6aa1760f..273a4f359a 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -224,7 +224,6 @@ QT_BEGIN_NAMESPACE
// 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.
-//#include "qsslsocket_openssl11_symbols_p.h"
const unsigned char * q_ASN1_STRING_get0_data(const ASN1_STRING *x);
@@ -287,6 +286,23 @@ 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)