diff options
author | Liang Qi <liang.qi@qt.io> | 2020-02-13 09:14:09 +0100 |
---|---|---|
committer | Alexandru Croitor <alexandru.croitor@qt.io> | 2020-02-13 18:31:40 +0100 |
commit | 6b2535ea15cdbdb2355416b604f072fc13ff36b2 (patch) | |
tree | 4bf1560bab77c8b315850c5337ba31a0ea87b5f0 /src/network/ssl | |
parent | 54c2cebabdda0280b8443c6947b6fee02445e138 (diff) | |
parent | 67491e2df5357706dbf88ddaf1f030ff095b4528 (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.cpp | 10 | ||||
-rw-r--r-- | src/network/ssl/qsslconfiguration.h | 2 | ||||
-rw-r--r-- | src/network/ssl/qsslcontext_openssl.cpp | 21 | ||||
-rw-r--r-- | src/network/ssl/qsslpresharedkeyauthenticator.cpp | 2 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket.cpp | 25 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket.h | 1 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl.cpp | 70 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl_p.h | 1 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl_symbols.cpp | 4 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl_symbols_p.h | 18 |
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) |