aboutsummaryrefslogtreecommitdiff
path: root/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'crypto')
-rw-r--r--crypto/tlscredsx509.c23
-rw-r--r--crypto/tlssession.c103
-rw-r--r--crypto/trace-events2
-rw-r--r--crypto/x509-utils.c6
4 files changed, 118 insertions, 16 deletions
diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index 63a72fe..cd1f504 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -426,9 +426,8 @@ qcrypto_tls_creds_load_cert(QCryptoTLSCredsX509 *creds,
static int
qcrypto_tls_creds_load_ca_cert_list(QCryptoTLSCredsX509 *creds,
const char *certFile,
- gnutls_x509_crt_t *certs,
- unsigned int certMax,
- size_t *ncerts,
+ gnutls_x509_crt_t **certs,
+ unsigned int *ncerts,
Error **errp)
{
gnutls_datum_t data;
@@ -449,20 +448,18 @@ qcrypto_tls_creds_load_ca_cert_list(QCryptoTLSCredsX509 *creds,
data.data = (unsigned char *)buf;
data.size = strlen(buf);
- if (gnutls_x509_crt_list_import(certs, &certMax, &data,
- GNUTLS_X509_FMT_PEM, 0) < 0) {
+ if (gnutls_x509_crt_list_import2(certs, ncerts, &data,
+ GNUTLS_X509_FMT_PEM, 0) < 0) {
error_setg(errp,
"Unable to import CA certificate list %s",
certFile);
return -1;
}
- *ncerts = certMax;
return 0;
}
-#define MAX_CERTS 16
static int
qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
bool isServer,
@@ -471,12 +468,11 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
Error **errp)
{
gnutls_x509_crt_t cert = NULL;
- gnutls_x509_crt_t cacerts[MAX_CERTS];
- size_t ncacerts = 0;
+ gnutls_x509_crt_t *cacerts = NULL;
+ unsigned int ncacerts = 0;
size_t i;
int ret = -1;
- memset(cacerts, 0, sizeof(cacerts));
if (certFile &&
access(certFile, R_OK) == 0) {
cert = qcrypto_tls_creds_load_cert(creds,
@@ -488,8 +484,9 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
}
if (access(cacertFile, R_OK) == 0) {
if (qcrypto_tls_creds_load_ca_cert_list(creds,
- cacertFile, cacerts,
- MAX_CERTS, &ncacerts,
+ cacertFile,
+ &cacerts,
+ &ncacerts,
errp) < 0) {
goto cleanup;
}
@@ -526,6 +523,8 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
for (i = 0; i < ncacerts; i++) {
gnutls_x509_crt_deinit(cacerts[i]);
}
+ g_free(cacerts);
+
return ret;
}
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)
diff --git a/crypto/trace-events b/crypto/trace-events
index bccd0bbf..d0e3342 100644
--- a/crypto/trace-events
+++ b/crypto/trace-events
@@ -21,6 +21,8 @@ qcrypto_tls_creds_x509_load_cert_list(void *creds, const char *file) "TLS creds
# tlssession.c
qcrypto_tls_session_new(void *session, void *creds, const char *hostname, const char *authzid, int endpoint) "TLS session new session=%p creds=%p hostname=%s authzid=%s endpoint=%d"
qcrypto_tls_session_check_creds(void *session, const char *status) "TLS session check creds session=%p status=%s"
+qcrypto_tls_session_parameters(void *session, int threadSafety, int protocol, int cipher) "TLS session parameters session=%p threadSafety=%d protocol=%d cipher=%d"
+qcrypto_tls_session_bug1717_workaround(void *session) "TLS session bug1717 workaround session=%p"
# tls-cipher-suites.c
qcrypto_tls_cipher_suite_priority(const char *name) "priority: %s"
diff --git a/crypto/x509-utils.c b/crypto/x509-utils.c
index 8bad00a..39bb6d4 100644
--- a/crypto/x509-utils.c
+++ b/crypto/x509-utils.c
@@ -46,7 +46,11 @@ int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size,
return -1;
}
- gnutls_x509_crt_init(&crt);
+ if (gnutls_x509_crt_init(&crt) < 0) {
+ error_setg(errp, "Unable to initialize certificate: %s",
+ gnutls_strerror(ret));
+ return -1;
+ }
if (gnutls_x509_crt_import(crt, &datum, GNUTLS_X509_FMT_PEM) != 0) {
error_setg(errp, "Failed to import certificate");