aboutsummaryrefslogtreecommitdiff
path: root/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'crypto')
-rw-r--r--crypto/cipher-builtin.c.inc303
-rw-r--r--crypto/cipher-stub.c.inc30
-rw-r--r--crypto/cipher.c2
-rw-r--r--crypto/secret.c2
-rw-r--r--crypto/secret_common.c4
-rw-r--r--crypto/secret_keyring.c2
-rw-r--r--crypto/tls-cipher-suites.c5
-rw-r--r--crypto/tlscreds.c2
-rw-r--r--crypto/tlscredsanon.c4
-rw-r--r--crypto/tlscredspsk.c4
-rw-r--r--crypto/tlscredsx509.c27
-rw-r--r--crypto/tlssession.c103
-rw-r--r--crypto/trace-events2
-rw-r--r--crypto/x509-utils.c6
14 files changed, 163 insertions, 333 deletions
diff --git a/crypto/cipher-builtin.c.inc b/crypto/cipher-builtin.c.inc
deleted file mode 100644
index da5fcbd..0000000
--- a/crypto/cipher-builtin.c.inc
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * QEMU Crypto cipher built-in algorithms
- *
- * Copyright (c) 2015 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "crypto/aes.h"
-
-typedef struct QCryptoCipherBuiltinAESContext QCryptoCipherBuiltinAESContext;
-struct QCryptoCipherBuiltinAESContext {
- AES_KEY enc;
- AES_KEY dec;
-};
-
-typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES;
-struct QCryptoCipherBuiltinAES {
- QCryptoCipher base;
- QCryptoCipherBuiltinAESContext key;
- uint8_t iv[AES_BLOCK_SIZE];
-};
-
-
-static inline bool qcrypto_length_check(size_t len, size_t blocksize,
- Error **errp)
-{
- if (unlikely(len & (blocksize - 1))) {
- error_setg(errp, "Length %zu must be a multiple of block size %zu",
- len, blocksize);
- return false;
- }
- return true;
-}
-
-static void qcrypto_cipher_ctx_free(QCryptoCipher *cipher)
-{
- g_free(cipher);
-}
-
-static int qcrypto_cipher_no_setiv(QCryptoCipher *cipher,
- const uint8_t *iv, size_t niv,
- Error **errp)
-{
- error_setg(errp, "Setting IV is not supported");
- return -1;
-}
-
-static void do_aes_encrypt_ecb(const void *vctx,
- size_t len,
- uint8_t *out,
- const uint8_t *in)
-{
- const QCryptoCipherBuiltinAESContext *ctx = vctx;
-
- /* We have already verified that len % AES_BLOCK_SIZE == 0. */
- while (len) {
- AES_encrypt(in, out, &ctx->enc);
- in += AES_BLOCK_SIZE;
- out += AES_BLOCK_SIZE;
- len -= AES_BLOCK_SIZE;
- }
-}
-
-static void do_aes_decrypt_ecb(const void *vctx,
- size_t len,
- uint8_t *out,
- const uint8_t *in)
-{
- const QCryptoCipherBuiltinAESContext *ctx = vctx;
-
- /* We have already verified that len % AES_BLOCK_SIZE == 0. */
- while (len) {
- AES_decrypt(in, out, &ctx->dec);
- in += AES_BLOCK_SIZE;
- out += AES_BLOCK_SIZE;
- len -= AES_BLOCK_SIZE;
- }
-}
-
-static void do_aes_encrypt_cbc(const AES_KEY *key,
- size_t len,
- uint8_t *out,
- const uint8_t *in,
- uint8_t *ivec)
-{
- uint8_t tmp[AES_BLOCK_SIZE];
- size_t n;
-
- /* We have already verified that len % AES_BLOCK_SIZE == 0. */
- while (len) {
- for (n = 0; n < AES_BLOCK_SIZE; ++n) {
- tmp[n] = in[n] ^ ivec[n];
- }
- AES_encrypt(tmp, out, key);
- memcpy(ivec, out, AES_BLOCK_SIZE);
- len -= AES_BLOCK_SIZE;
- in += AES_BLOCK_SIZE;
- out += AES_BLOCK_SIZE;
- }
-}
-
-static void do_aes_decrypt_cbc(const AES_KEY *key,
- size_t len,
- uint8_t *out,
- const uint8_t *in,
- uint8_t *ivec)
-{
- uint8_t tmp[AES_BLOCK_SIZE];
- size_t n;
-
- /* We have already verified that len % AES_BLOCK_SIZE == 0. */
- while (len) {
- memcpy(tmp, in, AES_BLOCK_SIZE);
- AES_decrypt(in, out, key);
- for (n = 0; n < AES_BLOCK_SIZE; ++n) {
- out[n] ^= ivec[n];
- }
- memcpy(ivec, tmp, AES_BLOCK_SIZE);
- len -= AES_BLOCK_SIZE;
- in += AES_BLOCK_SIZE;
- out += AES_BLOCK_SIZE;
- }
-}
-
-static int qcrypto_cipher_aes_encrypt_ecb(QCryptoCipher *cipher,
- const void *in, void *out,
- size_t len, Error **errp)
-{
- QCryptoCipherBuiltinAES *ctx
- = container_of(cipher, QCryptoCipherBuiltinAES, base);
-
- if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) {
- return -1;
- }
- do_aes_encrypt_ecb(&ctx->key, len, out, in);
- return 0;
-}
-
-static int qcrypto_cipher_aes_decrypt_ecb(QCryptoCipher *cipher,
- const void *in, void *out,
- size_t len, Error **errp)
-{
- QCryptoCipherBuiltinAES *ctx
- = container_of(cipher, QCryptoCipherBuiltinAES, base);
-
- if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) {
- return -1;
- }
- do_aes_decrypt_ecb(&ctx->key, len, out, in);
- return 0;
-}
-
-static int qcrypto_cipher_aes_encrypt_cbc(QCryptoCipher *cipher,
- const void *in, void *out,
- size_t len, Error **errp)
-{
- QCryptoCipherBuiltinAES *ctx
- = container_of(cipher, QCryptoCipherBuiltinAES, base);
-
- if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) {
- return -1;
- }
- do_aes_encrypt_cbc(&ctx->key.enc, len, out, in, ctx->iv);
- return 0;
-}
-
-static int qcrypto_cipher_aes_decrypt_cbc(QCryptoCipher *cipher,
- const void *in, void *out,
- size_t len, Error **errp)
-{
- QCryptoCipherBuiltinAES *ctx
- = container_of(cipher, QCryptoCipherBuiltinAES, base);
-
- if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) {
- return -1;
- }
- do_aes_decrypt_cbc(&ctx->key.dec, len, out, in, ctx->iv);
- return 0;
-}
-
-static int qcrypto_cipher_aes_setiv(QCryptoCipher *cipher, const uint8_t *iv,
- size_t niv, Error **errp)
-{
- QCryptoCipherBuiltinAES *ctx
- = container_of(cipher, QCryptoCipherBuiltinAES, base);
-
- if (niv != AES_BLOCK_SIZE) {
- error_setg(errp, "IV must be %d bytes not %zu",
- AES_BLOCK_SIZE, niv);
- return -1;
- }
-
- memcpy(ctx->iv, iv, AES_BLOCK_SIZE);
- return 0;
-}
-
-static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_ecb = {
- .cipher_encrypt = qcrypto_cipher_aes_encrypt_ecb,
- .cipher_decrypt = qcrypto_cipher_aes_decrypt_ecb,
- .cipher_setiv = qcrypto_cipher_no_setiv,
- .cipher_free = qcrypto_cipher_ctx_free,
-};
-
-static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_cbc = {
- .cipher_encrypt = qcrypto_cipher_aes_encrypt_cbc,
- .cipher_decrypt = qcrypto_cipher_aes_decrypt_cbc,
- .cipher_setiv = qcrypto_cipher_aes_setiv,
- .cipher_free = qcrypto_cipher_ctx_free,
-};
-
-bool qcrypto_cipher_supports(QCryptoCipherAlgo alg,
- QCryptoCipherMode mode)
-{
- switch (alg) {
- case QCRYPTO_CIPHER_ALGO_AES_128:
- case QCRYPTO_CIPHER_ALGO_AES_192:
- case QCRYPTO_CIPHER_ALGO_AES_256:
- switch (mode) {
- case QCRYPTO_CIPHER_MODE_ECB:
- case QCRYPTO_CIPHER_MODE_CBC:
- return true;
- default:
- return false;
- }
- break;
- default:
- return false;
- }
-}
-
-static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
- QCryptoCipherMode mode,
- const uint8_t *key,
- size_t nkey,
- Error **errp)
-{
- if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
- return NULL;
- }
-
- switch (alg) {
- case QCRYPTO_CIPHER_ALGO_AES_128:
- case QCRYPTO_CIPHER_ALGO_AES_192:
- case QCRYPTO_CIPHER_ALGO_AES_256:
- {
- QCryptoCipherBuiltinAES *ctx;
- const QCryptoCipherDriver *drv;
-
- switch (mode) {
- case QCRYPTO_CIPHER_MODE_ECB:
- drv = &qcrypto_cipher_aes_driver_ecb;
- break;
- case QCRYPTO_CIPHER_MODE_CBC:
- drv = &qcrypto_cipher_aes_driver_cbc;
- break;
- default:
- goto bad_mode;
- }
-
- ctx = g_new0(QCryptoCipherBuiltinAES, 1);
- ctx->base.driver = drv;
-
- if (AES_set_encrypt_key(key, nkey * 8, &ctx->key.enc)) {
- error_setg(errp, "Failed to set encryption key");
- goto error;
- }
- if (AES_set_decrypt_key(key, nkey * 8, &ctx->key.dec)) {
- error_setg(errp, "Failed to set decryption key");
- goto error;
- }
-
- return &ctx->base;
-
- error:
- g_free(ctx);
- return NULL;
- }
-
- default:
- error_setg(errp,
- "Unsupported cipher algorithm %s",
- QCryptoCipherAlgo_str(alg));
- return NULL;
- }
-
- bad_mode:
- error_setg(errp, "Unsupported cipher mode %s",
- QCryptoCipherMode_str(mode));
- return NULL;
-}
diff --git a/crypto/cipher-stub.c.inc b/crypto/cipher-stub.c.inc
new file mode 100644
index 0000000..1b7ea81
--- /dev/null
+++ b/crypto/cipher-stub.c.inc
@@ -0,0 +1,30 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * QEMU Crypto cipher impl stub
+ *
+ * Copyright (c) 2025 Red Hat, Inc.
+ *
+ */
+
+bool qcrypto_cipher_supports(QCryptoCipherAlgo alg,
+ QCryptoCipherMode mode)
+{
+ return false;
+}
+
+static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
+ QCryptoCipherMode mode,
+ const uint8_t *key,
+ size_t nkey,
+ Error **errp)
+{
+ if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
+ return NULL;
+ }
+
+ error_setg(errp,
+ "Unsupported cipher algorithm %s, no crypto library enabled in build",
+ QCryptoCipherAlgo_str(alg));
+ return NULL;
+}
diff --git a/crypto/cipher.c b/crypto/cipher.c
index c14a8b8..229710f 100644
--- a/crypto/cipher.c
+++ b/crypto/cipher.c
@@ -145,7 +145,7 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgo alg,
#elif defined CONFIG_GNUTLS_CRYPTO
#include "cipher-gnutls.c.inc"
#else
-#include "cipher-builtin.c.inc"
+#include "cipher-stub.c.inc"
#endif
QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgo alg,
diff --git a/crypto/secret.c b/crypto/secret.c
index 44eaff1..61a4584 100644
--- a/crypto/secret.c
+++ b/crypto/secret.c
@@ -117,7 +117,7 @@ qcrypto_secret_finalize(Object *obj)
}
static void
-qcrypto_secret_class_init(ObjectClass *oc, void *data)
+qcrypto_secret_class_init(ObjectClass *oc, const void *data)
{
QCryptoSecretCommonClass *sic = QCRYPTO_SECRET_COMMON_CLASS(oc);
sic->load_data = qcrypto_secret_load_data;
diff --git a/crypto/secret_common.c b/crypto/secret_common.c
index dbda998..a5ecb87 100644
--- a/crypto/secret_common.c
+++ b/crypto/secret_common.c
@@ -263,7 +263,7 @@ qcrypto_secret_finalize(Object *obj)
}
static void
-qcrypto_secret_class_init(ObjectClass *oc, void *data)
+qcrypto_secret_class_init(ObjectClass *oc, const void *data)
{
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
@@ -375,7 +375,7 @@ static const TypeInfo qcrypto_secret_info = {
.class_size = sizeof(QCryptoSecretCommonClass),
.class_init = qcrypto_secret_class_init,
.abstract = true,
- .interfaces = (InterfaceInfo[]) {
+ .interfaces = (const InterfaceInfo[]) {
{ TYPE_USER_CREATABLE },
{ }
}
diff --git a/crypto/secret_keyring.c b/crypto/secret_keyring.c
index 1b7edec..78d7f09 100644
--- a/crypto/secret_keyring.c
+++ b/crypto/secret_keyring.c
@@ -103,7 +103,7 @@ qcrypto_secret_prop_get_key(Object *obj, Visitor *v,
static void
-qcrypto_secret_keyring_class_init(ObjectClass *oc, void *data)
+qcrypto_secret_keyring_class_init(ObjectClass *oc, const void *data)
{
QCryptoSecretCommonClass *sic = QCRYPTO_SECRET_COMMON_CLASS(oc);
sic->load_data = qcrypto_secret_keyring_load_data;
diff --git a/crypto/tls-cipher-suites.c b/crypto/tls-cipher-suites.c
index d0df4ba..d9b61d0 100644
--- a/crypto/tls-cipher-suites.c
+++ b/crypto/tls-cipher-suites.c
@@ -102,7 +102,8 @@ static GByteArray *qcrypto_tls_cipher_suites_fw_cfg_gen_data(Object *obj,
errp);
}
-static void qcrypto_tls_cipher_suites_class_init(ObjectClass *oc, void *data)
+static void qcrypto_tls_cipher_suites_class_init(ObjectClass *oc,
+ const void *data)
{
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
FWCfgDataGeneratorClass *fwgc = FW_CFG_DATA_GENERATOR_CLASS(oc);
@@ -117,7 +118,7 @@ static const TypeInfo qcrypto_tls_cipher_suites_info = {
.instance_size = sizeof(QCryptoTLSCipherSuites),
.class_size = sizeof(QCryptoTLSCredsClass),
.class_init = qcrypto_tls_cipher_suites_class_init,
- .interfaces = (InterfaceInfo[]) {
+ .interfaces = (const InterfaceInfo[]) {
{ TYPE_USER_CREATABLE },
{ TYPE_FW_CFG_DATA_GENERATOR_INTERFACE },
{ }
diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
index 084ce0d..9e59594 100644
--- a/crypto/tlscreds.c
+++ b/crypto/tlscreds.c
@@ -223,7 +223,7 @@ qcrypto_tls_creds_prop_get_endpoint(Object *obj,
static void
-qcrypto_tls_creds_class_init(ObjectClass *oc, void *data)
+qcrypto_tls_creds_class_init(ObjectClass *oc, const void *data)
{
object_class_property_add_bool(oc, "verify-peer",
qcrypto_tls_creds_prop_get_verify,
diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c
index 476cf89..44af9e6 100644
--- a/crypto/tlscredsanon.c
+++ b/crypto/tlscredsanon.c
@@ -137,7 +137,7 @@ qcrypto_tls_creds_anon_finalize(Object *obj)
static void
-qcrypto_tls_creds_anon_class_init(ObjectClass *oc, void *data)
+qcrypto_tls_creds_anon_class_init(ObjectClass *oc, const void *data)
{
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
@@ -152,7 +152,7 @@ static const TypeInfo qcrypto_tls_creds_anon_info = {
.instance_finalize = qcrypto_tls_creds_anon_finalize,
.class_size = sizeof(QCryptoTLSCredsAnonClass),
.class_init = qcrypto_tls_creds_anon_class_init,
- .interfaces = (InterfaceInfo[]) {
+ .interfaces = (const InterfaceInfo[]) {
{ TYPE_USER_CREATABLE },
{ }
}
diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c
index aa270d7..5b68a6b 100644
--- a/crypto/tlscredspsk.c
+++ b/crypto/tlscredspsk.c
@@ -236,7 +236,7 @@ qcrypto_tls_creds_psk_prop_get_username(Object *obj,
}
static void
-qcrypto_tls_creds_psk_class_init(ObjectClass *oc, void *data)
+qcrypto_tls_creds_psk_class_init(ObjectClass *oc, const void *data)
{
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
@@ -255,7 +255,7 @@ static const TypeInfo qcrypto_tls_creds_psk_info = {
.instance_finalize = qcrypto_tls_creds_psk_finalize,
.class_size = sizeof(QCryptoTLSCredsPSKClass),
.class_init = qcrypto_tls_creds_psk_class_init,
- .interfaces = (InterfaceInfo[]) {
+ .interfaces = (const InterfaceInfo[]) {
{ TYPE_USER_CREATABLE },
{ }
}
diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index 24ec584..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;
}
@@ -802,7 +801,7 @@ qcrypto_tls_creds_x509_finalize(Object *obj)
static void
-qcrypto_tls_creds_x509_class_init(ObjectClass *oc, void *data)
+qcrypto_tls_creds_x509_class_init(ObjectClass *oc, const void *data)
{
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
QCryptoTLSCredsClass *ctcc = QCRYPTO_TLS_CREDS_CLASS(oc);
@@ -828,7 +827,7 @@ static const TypeInfo qcrypto_tls_creds_x509_info = {
.instance_finalize = qcrypto_tls_creds_x509_finalize,
.class_size = sizeof(QCryptoTLSCredsX509Class),
.class_init = qcrypto_tls_creds_x509_class_init,
- .interfaces = (InterfaceInfo[]) {
+ .interfaces = (const InterfaceInfo[]) {
{ TYPE_USER_CREATABLE },
{ }
}
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");