summaryrefslogtreecommitdiffstats
path: root/src/network/ssl/qsslsocket_openssl.cpp
diff options
context:
space:
mode:
authorTimur Pocheptsov <timur.pocheptsov@qt.io>2020-01-27 14:11:08 +0100
committerTimur Pocheptsov <timur.pocheptsov@qt.io>2020-01-29 19:38:43 +0100
commitb36b7abb40f04f265c0453a2f4beb466ed462976 (patch)
tree2353834692f75f65c9dfdace5dbe83f205bfe783 /src/network/ssl/qsslsocket_openssl.cpp
parent33c9a1e0bcf9c7ced67d5ec62225d6295671d33b (diff)
Implement/fix session resumption with TLS 1.3
The session we cache at the end of a handshake is non-resumable in TLS 1.3, since NewSessionTicket message appears quite some time after the handshake was complete. OpenSSL has a callback where we can finally obtain a resumable session and inform an application about session ticket updated by emitting a signal. Truism: OpenSSL-only. [ChangeLog][QtNetwork] A new signal introduced to report when a valid session ticket received (TLS 1.3) Fixes: QTBUG-81591 Change-Id: I4d22fad5cc082e431577e20ddbda2835e864b511 Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io> Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
Diffstat (limited to 'src/network/ssl/qsslsocket_openssl.cpp')
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp70
1 files changed, 70 insertions, 0 deletions
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 41f933b299..4be27affca 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -183,6 +183,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
@@ -1392,6 +1408,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);