aboutsummaryrefslogtreecommitdiff
path: root/crypto/tlssession.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/tlssession.c')
-rw-r--r--crypto/tlssession.c103
1 files changed, 100 insertions, 3 deletions
diff --git a/crypto/tlssession.c b/crypto/tlssession.c
index 6d8f8df..86d407a 100644
--- a/crypto/tlssession.c
+++ b/crypto/tlssession.c
@@ -19,6 +19,8 @@
*/
#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/thread.h"
#include "crypto/tlssession.h"
#include "crypto/tlscredsanon.h"
#include "crypto/tlscredspsk.h"
@@ -51,6 +53,14 @@ struct QCryptoTLSSession {
*/
Error *rerr;
Error *werr;
+
+ /*
+ * Used to protect against broken GNUTLS thread safety
+ * https://gitlab.com/gnutls/gnutls/-/issues/1717
+ */
+ bool requireThreadSafety;
+ bool lockEnabled;
+ QemuMutex lock;
};
@@ -69,6 +79,7 @@ qcrypto_tls_session_free(QCryptoTLSSession *session)
g_free(session->peername);
g_free(session->authzid);
object_unref(OBJECT(session->creds));
+ qemu_mutex_destroy(&session->lock);
g_free(session);
}
@@ -84,10 +95,19 @@ qcrypto_tls_session_push(void *opaque, const void *buf, size_t len)
return -1;
};
+ if (session->lockEnabled) {
+ qemu_mutex_unlock(&session->lock);
+ }
+
error_free(session->werr);
session->werr = NULL;
ret = session->writeFunc(buf, len, session->opaque, &session->werr);
+
+ if (session->lockEnabled) {
+ qemu_mutex_lock(&session->lock);
+ }
+
if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) {
errno = EAGAIN;
return -1;
@@ -114,7 +134,16 @@ qcrypto_tls_session_pull(void *opaque, void *buf, size_t len)
error_free(session->rerr);
session->rerr = NULL;
+ if (session->lockEnabled) {
+ qemu_mutex_unlock(&session->lock);
+ }
+
ret = session->readFunc(buf, len, session->opaque, &session->rerr);
+
+ if (session->lockEnabled) {
+ qemu_mutex_lock(&session->lock);
+ }
+
if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) {
errno = EAGAIN;
return -1;
@@ -153,6 +182,8 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds,
session->creds = creds;
object_ref(OBJECT(creds));
+ qemu_mutex_init(&session->lock);
+
if (creds->endpoint != endpoint) {
error_setg(errp, "Credentials endpoint doesn't match session");
goto error;
@@ -289,6 +320,11 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds,
return NULL;
}
+void qcrypto_tls_session_require_thread_safety(QCryptoTLSSession *sess)
+{
+ sess->requireThreadSafety = true;
+}
+
static int
qcrypto_tls_session_check_certificate(QCryptoTLSSession *session,
Error **errp)
@@ -480,7 +516,17 @@ qcrypto_tls_session_write(QCryptoTLSSession *session,
size_t len,
Error **errp)
{
- ssize_t ret = gnutls_record_send(session->handle, buf, len);
+ ssize_t ret;
+
+ if (session->lockEnabled) {
+ qemu_mutex_lock(&session->lock);
+ }
+
+ ret = gnutls_record_send(session->handle, buf, len);
+
+ if (session->lockEnabled) {
+ qemu_mutex_unlock(&session->lock);
+ }
if (ret < 0) {
if (ret == GNUTLS_E_AGAIN) {
@@ -509,7 +555,17 @@ qcrypto_tls_session_read(QCryptoTLSSession *session,
bool gracefulTermination,
Error **errp)
{
- ssize_t ret = gnutls_record_recv(session->handle, buf, len);
+ ssize_t ret;
+
+ if (session->lockEnabled) {
+ qemu_mutex_lock(&session->lock);
+ }
+
+ ret = gnutls_record_recv(session->handle, buf, len);
+
+ if (session->lockEnabled) {
+ qemu_mutex_unlock(&session->lock);
+ }
if (ret < 0) {
if (ret == GNUTLS_E_AGAIN) {
@@ -545,8 +601,39 @@ int
qcrypto_tls_session_handshake(QCryptoTLSSession *session,
Error **errp)
{
- int ret = gnutls_handshake(session->handle);
+ int ret;
+ ret = gnutls_handshake(session->handle);
+
if (!ret) {
+#ifdef CONFIG_GNUTLS_BUG1717_WORKAROUND
+ gnutls_cipher_algorithm_t cipher =
+ gnutls_cipher_get(session->handle);
+
+ /*
+ * Any use of rekeying in TLS 1.3 is unsafe for
+ * a gnutls with bug 1717, however, we know that
+ * QEMU won't initiate manual rekeying. Thus we
+ * only have to protect against automatic rekeying
+ * which doesn't trigger with CHACHA20
+ */
+ trace_qcrypto_tls_session_parameters(
+ session,
+ session->requireThreadSafety,
+ gnutls_protocol_get_version(session->handle),
+ cipher);
+
+ if (session->requireThreadSafety &&
+ gnutls_protocol_get_version(session->handle) ==
+ GNUTLS_TLS1_3 &&
+ cipher != GNUTLS_CIPHER_CHACHA20_POLY1305) {
+ warn_report("WARNING: activating thread safety countermeasures "
+ "for potentially broken GNUTLS with TLS1.3 cipher=%d",
+ cipher);
+ trace_qcrypto_tls_session_bug1717_workaround(session);
+ session->lockEnabled = true;
+ }
+#endif
+
session->handshakeComplete = true;
return QCRYPTO_TLS_HANDSHAKE_COMPLETE;
}
@@ -584,8 +671,15 @@ qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp)
return 0;
}
+ if (session->lockEnabled) {
+ qemu_mutex_lock(&session->lock);
+ }
ret = gnutls_bye(session->handle, GNUTLS_SHUT_WR);
+ if (session->lockEnabled) {
+ qemu_mutex_unlock(&session->lock);
+ }
+
if (!ret) {
return QCRYPTO_TLS_BYE_COMPLETE;
}
@@ -651,6 +745,9 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED,
return NULL;
}
+void qcrypto_tls_session_require_thread_safety(QCryptoTLSSession *sess)
+{
+}
void
qcrypto_tls_session_free(QCryptoTLSSession *sess G_GNUC_UNUSED)