qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/21] crypto: support multiple parallel certificate identities
@ 2025-10-30 14:49 Daniel P. Berrangé
  2025-10-30 14:49 ` [PATCH 01/21] crypto: remove redundant parameter checking CA certs Daniel P. Berrangé
                   ` (20 more replies)
  0 siblings, 21 replies; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

This series aims to improve the support for post-quantum cryptography
in TLS connections by allowing  multiple sets of certificates to be
loaded. The idea is that during a transition period servers will have
a traditional RSA based certificate in parallel with an MLDSA based
certificate for PQC, and the right one will be dynamically determined
during the TLS handshake.

The first 12 patches are trivial cleanups.

The next 3 patches fix a potential use-after-free problem

The last patches introduce support for multiple certificates.

NB, in terms of testing this will require either CentOS Stream 10,
or Fedora 43. Most other distros will not support PQC out of the
box at this time even if they have new enough gnutls, since they
don't make use of the crypto-policies package which is needed to
enable PQC by default.

Daniel P. Berrangé (21):
  crypto: remove redundant parameter checking CA certs
  crypto: add missing free of certs array
  crypto: replace stat() with access() for credential checks
  crypto: remove redundant access() checks before loading certs
  crypto: move check for TLS creds 'dir' property
  crypto: use g_autofree when loading x509 credentials
  crypto: remove needless indirection via parent_obj field
  crypto: move release of DH parameters into TLS creds parent
  crypto: shorten the endpoint == server check in TLS creds
  crypto: remove duplication loading x509 CA cert
  crypto: reduce duplication in handling TLS priority strings
  crypto: introduce method for reloading TLS creds
  crypto: introduce a wrapper around gnutls credentials
  crypto: fix lifecycle handling of gnutls credentials objects
  crypto: make TLS credentials structs private
  crypto: deprecate use of external dh-params.pem file
  crypto: avoid loading the CA certs twice
  crypto: avoid loading the identity certs twice
  crypto: expand logic to cope with multiple certificate identities
  crypto: support upto 5 parallel certificate identities
  docs: creation of x509 certs compliant with post-quantum crypto

 crypto/meson.build                    |   5 +-
 crypto/tlscreds.c                     |  77 ++--
 crypto/tlscredsanon.c                 |  62 +--
 crypto/tlscredsbox.c                  | 101 +++++
 crypto/tlscredsbox.h                  |  46 ++
 crypto/tlscredspriv.h                 |  36 +-
 crypto/tlscredspsk.c                  |  64 ++-
 crypto/tlscredsx509.c                 | 592 +++++++++++++++++---------
 crypto/tlssession.c                   | 139 ++----
 crypto/trace-events                   |   1 +
 docs/about/deprecated.rst             |   9 +
 docs/system/tls.rst                   | 134 +++++-
 include/crypto/tlscreds.h             |  26 ++
 include/crypto/tlscredsx509.h         |   6 +
 tests/unit/test-crypto-tlscredsx509.c |   8 +-
 ui/vnc.c                              |   9 +-
 16 files changed, 849 insertions(+), 466 deletions(-)
 create mode 100644 crypto/tlscredsbox.c
 create mode 100644 crypto/tlscredsbox.h

-- 
2.51.1



^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 01/21] crypto: remove redundant parameter checking CA certs
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-10-31 13:57   ` Philippe Mathieu-Daudé
  2025-10-30 14:49 ` [PATCH 02/21] crypto: add missing free of certs array Daniel P. Berrangé
                   ` (19 subsequent siblings)
  20 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

The only caller of qcrypto_tls_creds_check_authority_chain always
passes 'true' for the 'isCA' parameter. The point of this method
is to check the CA chani, so no other value would ever make sense.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscredsx509.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index db2b74bafa..847fd4d9fa 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -315,7 +315,6 @@ qcrypto_tls_creds_check_authority_chain(QCryptoTLSCredsX509 *creds,
                                         unsigned int ncacerts,
                                         const char *cacertFile,
                                         bool isServer,
-                                        bool isCA,
                                         Error **errp)
 {
     gnutls_x509_crt_t cert_to_check = certs[ncerts - 1];
@@ -356,7 +355,7 @@ qcrypto_tls_creds_check_authority_chain(QCryptoTLSCredsX509 *creds,
              */
             return qcrypto_tls_creds_check_cert(
                 creds, cert_to_check, cacertFile,
-                isServer, isCA, errp);
+                isServer, true, errp);
         }
         for (int i = 0; i < ncacerts; i++) {
             if (gnutls_x509_crt_check_issuer(cert_to_check,
@@ -370,7 +369,7 @@ qcrypto_tls_creds_check_authority_chain(QCryptoTLSCredsX509 *creds,
         }
 
         if (qcrypto_tls_creds_check_cert(creds, cert_issuer, cacertFile,
-                                         isServer, isCA, errp) < 0) {
+                                         isServer, true, errp) < 0) {
             return -1;
         }
 
@@ -534,7 +533,7 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
                                                 certs, ncerts,
                                                 cacerts, ncacerts,
                                                 cacertFile, isServer,
-                                                true, errp) < 0) {
+                                                errp) < 0) {
         goto cleanup;
     }
 
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 02/21] crypto: add missing free of certs array
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
  2025-10-30 14:49 ` [PATCH 01/21] crypto: remove redundant parameter checking CA certs Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-10-30 14:49 ` [PATCH 03/21] crypto: replace stat() with access() for credential checks Daniel P. Berrangé
                   ` (18 subsequent siblings)
  20 siblings, 0 replies; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscredsx509.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index 847fd4d9fa..75c70af522 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -550,6 +550,7 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
     for (i = 0; i < ncerts; i++) {
         gnutls_x509_crt_deinit(certs[i]);
     }
+    g_free(certs);
     for (i = 0; i < ncacerts; i++) {
         gnutls_x509_crt_deinit(cacerts[i]);
     }
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 03/21] crypto: replace stat() with access() for credential checks
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
  2025-10-30 14:49 ` [PATCH 01/21] crypto: remove redundant parameter checking CA certs Daniel P. Berrangé
  2025-10-30 14:49 ` [PATCH 02/21] crypto: add missing free of certs array Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-10-31 13:58   ` Philippe Mathieu-Daudé
  2025-10-30 14:49 ` [PATCH 04/21] crypto: remove redundant access() checks before loading certs Daniel P. Berrangé
                   ` (17 subsequent siblings)
  20 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

Readability of the credential files is what matters for our usage,
so access() is more appropriate than stat().

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscreds.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
index 9e59594d67..208a7e6d8f 100644
--- a/crypto/tlscreds.c
+++ b/crypto/tlscreds.c
@@ -100,7 +100,6 @@ qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
                            char **cred,
                            Error **errp)
 {
-    struct stat sb;
     int ret = -1;
 
     if (!creds->dir) {
@@ -114,7 +113,7 @@ qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
 
     *cred = g_strdup_printf("%s/%s", creds->dir, filename);
 
-    if (stat(*cred, &sb) < 0) {
+    if (access(*cred, R_OK) < 0) {
         if (errno == ENOENT && !required) {
             ret = 0;
         } else {
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 04/21] crypto: remove redundant access() checks before loading certs
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
                   ` (2 preceding siblings ...)
  2025-10-30 14:49 ` [PATCH 03/21] crypto: replace stat() with access() for credential checks Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-10-30 14:49 ` [PATCH 05/21] crypto: move check for TLS creds 'dir' property Daniel P. Berrangé
                   ` (16 subsequent siblings)
  20 siblings, 0 replies; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

The qcrypto_tls_creds_get_path method will perform an access()
check on the file and return a NULL path if it fails. By the
time we get to loading the cert files we know they must exist
on disk and thus the second access() check is redundant.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscredsx509.c | 22 ++++++++++------------
 1 file changed, 10 insertions(+), 12 deletions(-)

diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index 75c70af522..0acb17b6ec 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -496,8 +496,7 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
     size_t i;
     int ret = -1;
 
-    if (certFile &&
-        access(certFile, R_OK) == 0) {
+    if (certFile) {
         if (qcrypto_tls_creds_load_cert_list(creds,
                                              certFile,
                                              &certs,
@@ -508,16 +507,15 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
             goto cleanup;
         }
     }
-    if (access(cacertFile, R_OK) == 0) {
-        if (qcrypto_tls_creds_load_cert_list(creds,
-                                             cacertFile,
-                                             &cacerts,
-                                             &ncacerts,
-                                             isServer,
-                                             true,
-                                             errp) < 0) {
-            goto cleanup;
-        }
+
+    if (qcrypto_tls_creds_load_cert_list(creds,
+                                         cacertFile,
+                                         &cacerts,
+                                         &ncacerts,
+                                         isServer,
+                                         true,
+                                         errp) < 0) {
+        goto cleanup;
     }
 
     for (i = 0; i < ncerts; i++) {
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 05/21] crypto: move check for TLS creds 'dir' property
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
                   ` (3 preceding siblings ...)
  2025-10-30 14:49 ` [PATCH 04/21] crypto: remove redundant access() checks before loading certs Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-10-30 14:49 ` [PATCH 06/21] crypto: use g_autofree when loading x509 credentials Daniel P. Berrangé
                   ` (15 subsequent siblings)
  20 siblings, 0 replies; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

The check for the 'dir' property is being repeated for every
credential file to be loaded, but this results in incorrect
logic for optional credentials. The 'dir' property is mandatory
for PSK and x509 creds, even if some individual files are
optional. Address this by separating the check for the 'dir'
property.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscreds.c     | 9 ---------
 crypto/tlscredsanon.c | 3 ++-
 crypto/tlscredspsk.c  | 5 +++++
 crypto/tlscredsx509.c | 8 ++++++--
 4 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
index 208a7e6d8f..65e97ddd11 100644
--- a/crypto/tlscreds.c
+++ b/crypto/tlscreds.c
@@ -102,15 +102,6 @@ qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
 {
     int ret = -1;
 
-    if (!creds->dir) {
-        if (required) {
-            error_setg(errp, "Missing 'dir' property value");
-            return -1;
-        } else {
-            return 0;
-        }
-    }
-
     *cred = g_strdup_printf("%s/%s", creds->dir, filename);
 
     if (access(*cred, R_OK) < 0) {
diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c
index 44af9e6c9a..bc3351b5d6 100644
--- a/crypto/tlscredsanon.c
+++ b/crypto/tlscredsanon.c
@@ -43,7 +43,8 @@ qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
             creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
 
     if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
-        if (qcrypto_tls_creds_get_path(&creds->parent_obj,
+        if (creds->parent_obj.dir &&
+            qcrypto_tls_creds_get_path(&creds->parent_obj,
                                        QCRYPTO_TLS_CREDS_DH_PARAMS,
                                        false, &dhparams, errp) < 0) {
             return -1;
diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c
index 5b68a6b7ba..545d3e45db 100644
--- a/crypto/tlscredspsk.c
+++ b/crypto/tlscredspsk.c
@@ -81,6 +81,11 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
     trace_qcrypto_tls_creds_psk_load(creds,
             creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
 
+    if (!creds->parent_obj.dir) {
+        error_setg(errp, "Missing 'dir' property value");
+        goto cleanup;
+    }
+
     if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
         if (creds->username) {
             error_setg(errp, "username should not be set when endpoint=server");
diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index 0acb17b6ec..8fe6cc8e93 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -567,8 +567,12 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
     int ret;
     int rv = -1;
 
-    trace_qcrypto_tls_creds_x509_load(creds,
-            creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
+    if (!creds->parent_obj.dir) {
+        error_setg(errp, "Missing 'dir' property value");
+        return -1;
+    }
+
+    trace_qcrypto_tls_creds_x509_load(creds, creds->parent_obj.dir);
 
     if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
         if (qcrypto_tls_creds_get_path(&creds->parent_obj,
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 06/21] crypto: use g_autofree when loading x509 credentials
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
                   ` (4 preceding siblings ...)
  2025-10-30 14:49 ` [PATCH 05/21] crypto: move check for TLS creds 'dir' property Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-10-31 13:59   ` Philippe Mathieu-Daudé
  2025-10-30 14:49 ` [PATCH 07/21] crypto: remove needless indirection via parent_obj field Daniel P. Berrangé
                   ` (14 subsequent siblings)
  20 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

This allows removal of goto jumps during loading of the credentials
and will simplify the diff in following commits.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscredsx509.c | 35 +++++++++++++++--------------------
 1 file changed, 15 insertions(+), 20 deletions(-)

diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index 8fe6cc8e93..e5b869a35f 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -562,10 +562,12 @@ static int
 qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
                             Error **errp)
 {
-    char *cacert = NULL, *cacrl = NULL, *cert = NULL,
-        *key = NULL, *dhparams = NULL;
+    g_autofree char *cacert = NULL;
+    g_autofree char *cacrl = NULL;
+    g_autofree char *cert = NULL;
+    g_autofree char *key = NULL;
+    g_autofree char *dhparams = NULL;
     int ret;
-    int rv = -1;
 
     if (!creds->parent_obj.dir) {
         error_setg(errp, "Missing 'dir' property value");
@@ -590,7 +592,7 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
             qcrypto_tls_creds_get_path(&creds->parent_obj,
                                        QCRYPTO_TLS_CREDS_DH_PARAMS,
                                        false, &dhparams, errp) < 0) {
-            goto cleanup;
+            return -1;
         }
     } else {
         if (qcrypto_tls_creds_get_path(&creds->parent_obj,
@@ -602,7 +604,7 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
             qcrypto_tls_creds_get_path(&creds->parent_obj,
                                        QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
                                        false, &key, errp) < 0) {
-            goto cleanup;
+            return -1;
         }
     }
 
@@ -610,14 +612,14 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
         qcrypto_tls_creds_x509_sanity_check(creds,
             creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
             cacert, cert, errp) < 0) {
-        goto cleanup;
+        return -1;
     }
 
     ret = gnutls_certificate_allocate_credentials(&creds->data);
     if (ret < 0) {
         error_setg(errp, "Cannot allocate credentials: '%s'",
                    gnutls_strerror(ret));
-        goto cleanup;
+        return -1;
     }
 
     ret = gnutls_certificate_set_x509_trust_file(creds->data,
@@ -626,7 +628,7 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
     if (ret < 0) {
         error_setg(errp, "Cannot load CA certificate '%s': %s",
                    cacert, gnutls_strerror(ret));
-        goto cleanup;
+        return -1;
     }
 
     if (cert != NULL && key != NULL) {
@@ -635,7 +637,7 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
             password = qcrypto_secret_lookup_as_utf8(creds->passwordid,
                                                      errp);
             if (!password) {
-                goto cleanup;
+                return -1;
             }
         }
         ret = gnutls_certificate_set_x509_key_file2(creds->data,
@@ -647,7 +649,7 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
         if (ret < 0) {
             error_setg(errp, "Cannot load certificate '%s' & key '%s': %s",
                        cert, key, gnutls_strerror(ret));
-            goto cleanup;
+            return -1;
         }
     }
 
@@ -658,7 +660,7 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
         if (ret < 0) {
             error_setg(errp, "Cannot load CRL '%s': %s",
                        cacrl, gnutls_strerror(ret));
-            goto cleanup;
+            return -1;
         }
     }
 
@@ -666,20 +668,13 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
         if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams,
                                                  &creds->parent_obj.dh_params,
                                                  errp) < 0) {
-            goto cleanup;
+            return -1;
         }
         gnutls_certificate_set_dh_params(creds->data,
                                          creds->parent_obj.dh_params);
     }
 
-    rv = 0;
- cleanup:
-    g_free(cacert);
-    g_free(cacrl);
-    g_free(cert);
-    g_free(key);
-    g_free(dhparams);
-    return rv;
+    return 0;
 }
 
 
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 07/21] crypto: remove needless indirection via parent_obj field
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
                   ` (5 preceding siblings ...)
  2025-10-30 14:49 ` [PATCH 06/21] crypto: use g_autofree when loading x509 credentials Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-10-30 19:31   ` Marc-André Lureau
  2025-10-31 14:00   ` Philippe Mathieu-Daudé
  2025-10-30 14:49 ` [PATCH 08/21] crypto: move release of DH parameters into TLS creds parent Daniel P. Berrangé
                   ` (13 subsequent siblings)
  20 siblings, 2 replies; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

The reload method already has a pointer to the parent object in
the 'creds' parameter that is passed in, so indirect access via
the subclass 'parent_obj' field is redundant.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscredsx509.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index e5b869a35f..39f80b33ad 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -773,15 +773,15 @@ qcrypto_tls_creds_x509_reload(QCryptoTLSCreds *creds, Error **errp)
     QCryptoTLSCredsX509 *x509_creds = QCRYPTO_TLS_CREDS_X509(creds);
     Error *local_err = NULL;
     gnutls_certificate_credentials_t creds_data = x509_creds->data;
-    gnutls_dh_params_t creds_dh_params = x509_creds->parent_obj.dh_params;
+    gnutls_dh_params_t creds_dh_params = creds->dh_params;
 
     x509_creds->data = NULL;
-    x509_creds->parent_obj.dh_params = NULL;
+    creds->dh_params = NULL;
     qcrypto_tls_creds_x509_load(x509_creds, &local_err);
     if (local_err) {
         qcrypto_tls_creds_x509_unload(x509_creds);
         x509_creds->data = creds_data;
-        x509_creds->parent_obj.dh_params = creds_dh_params;
+        creds->dh_params = creds_dh_params;
         error_propagate(errp, local_err);
         return false;
     }
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 08/21] crypto: move release of DH parameters into TLS creds parent
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
                   ` (6 preceding siblings ...)
  2025-10-30 14:49 ` [PATCH 07/21] crypto: remove needless indirection via parent_obj field Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-10-30 19:31   ` Marc-André Lureau
  2025-10-30 14:49 ` [PATCH 09/21] crypto: shorten the endpoint == server check in TLS creds Daniel P. Berrangé
                   ` (12 subsequent siblings)
  20 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

The code for releasing DH parameters is common to all credential
subclasses, so can be moved into the parent.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscreds.c     | 4 ++++
 crypto/tlscredsanon.c | 4 ----
 crypto/tlscredspsk.c  | 4 ----
 crypto/tlscredsx509.c | 7 +++----
 4 files changed, 7 insertions(+), 12 deletions(-)

diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
index 65e97ddd11..1e39ee1141 100644
--- a/crypto/tlscreds.c
+++ b/crypto/tlscreds.c
@@ -246,6 +246,10 @@ qcrypto_tls_creds_finalize(Object *obj)
 {
     QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
 
+    if (creds->dh_params) {
+        gnutls_dh_params_deinit(creds->dh_params);
+    }
+
     g_free(creds->dir);
     g_free(creds->priority);
 }
diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c
index bc3351b5d6..1ddfe4eb31 100644
--- a/crypto/tlscredsanon.c
+++ b/crypto/tlscredsanon.c
@@ -92,10 +92,6 @@ qcrypto_tls_creds_anon_unload(QCryptoTLSCredsAnon *creds)
             creds->data.server = NULL;
         }
     }
-    if (creds->parent_obj.dh_params) {
-        gnutls_dh_params_deinit(creds->parent_obj.dh_params);
-        creds->parent_obj.dh_params = NULL;
-    }
 }
 
 #else /* ! CONFIG_GNUTLS */
diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c
index 545d3e45db..bf4efe2114 100644
--- a/crypto/tlscredspsk.c
+++ b/crypto/tlscredspsk.c
@@ -175,10 +175,6 @@ qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK *creds)
             creds->data.server = NULL;
         }
     }
-    if (creds->parent_obj.dh_params) {
-        gnutls_dh_params_deinit(creds->parent_obj.dh_params);
-        creds->parent_obj.dh_params = NULL;
-    }
 }
 
 #else /* ! CONFIG_GNUTLS */
diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index 39f80b33ad..1555285910 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -685,10 +685,6 @@ qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds)
         gnutls_certificate_free_credentials(creds->data);
         creds->data = NULL;
     }
-    if (creds->parent_obj.dh_params) {
-        gnutls_dh_params_deinit(creds->parent_obj.dh_params);
-        creds->parent_obj.dh_params = NULL;
-    }
 }
 
 
@@ -780,6 +776,9 @@ qcrypto_tls_creds_x509_reload(QCryptoTLSCreds *creds, Error **errp)
     qcrypto_tls_creds_x509_load(x509_creds, &local_err);
     if (local_err) {
         qcrypto_tls_creds_x509_unload(x509_creds);
+        if (creds->dh_params) {
+            gnutls_dh_params_deinit(creds->dh_params);
+        }
         x509_creds->data = creds_data;
         creds->dh_params = creds_dh_params;
         error_propagate(errp, local_err);
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 09/21] crypto: shorten the endpoint == server check in TLS creds
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
                   ` (7 preceding siblings ...)
  2025-10-30 14:49 ` [PATCH 08/21] crypto: move release of DH parameters into TLS creds parent Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-10-30 19:32   ` Marc-André Lureau
  2025-10-30 14:49 ` [PATCH 10/21] crypto: remove duplication loading x509 CA cert Daniel P. Berrangé
                   ` (11 subsequent siblings)
  20 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

This eliminates a number of long lines aiding readability.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscredsx509.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index 1555285910..08223781d7 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -567,6 +567,8 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
     g_autofree char *cert = NULL;
     g_autofree char *key = NULL;
     g_autofree char *dhparams = NULL;
+    bool isServer = (creds->parent_obj.endpoint ==
+                     QCRYPTO_TLS_CREDS_ENDPOINT_SERVER);
     int ret;
 
     if (!creds->parent_obj.dir) {
@@ -576,7 +578,7 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
 
     trace_qcrypto_tls_creds_x509_load(creds, creds->parent_obj.dir);
 
-    if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+    if (isServer) {
         if (qcrypto_tls_creds_get_path(&creds->parent_obj,
                                        QCRYPTO_TLS_CREDS_X509_CA_CERT,
                                        true, &cacert, errp) < 0 ||
@@ -609,9 +611,8 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
     }
 
     if (creds->sanityCheck &&
-        qcrypto_tls_creds_x509_sanity_check(creds,
-            creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
-            cacert, cert, errp) < 0) {
+        qcrypto_tls_creds_x509_sanity_check(creds, isServer,
+                                            cacert, cert, errp) < 0) {
         return -1;
     }
 
@@ -664,7 +665,7 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
         }
     }
 
-    if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+    if (isServer) {
         if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams,
                                                  &creds->parent_obj.dh_params,
                                                  errp) < 0) {
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 10/21] crypto: remove duplication loading x509 CA cert
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
                   ` (8 preceding siblings ...)
  2025-10-30 14:49 ` [PATCH 09/21] crypto: shorten the endpoint == server check in TLS creds Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-10-30 19:31   ` Marc-André Lureau
  2025-10-30 14:49 ` [PATCH 11/21] crypto: reduce duplication in handling TLS priority strings Daniel P. Berrangé
                   ` (10 subsequent siblings)
  20 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

The CA cert is mandatory in both client and server scenarios.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscredsx509.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index 08223781d7..f2f1aa2815 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -578,11 +578,14 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
 
     trace_qcrypto_tls_creds_x509_load(creds, creds->parent_obj.dir);
 
+    if (qcrypto_tls_creds_get_path(&creds->parent_obj,
+                                   QCRYPTO_TLS_CREDS_X509_CA_CERT,
+                                   true, &cacert, errp) < 0) {
+        return -1;
+    }
+
     if (isServer) {
         if (qcrypto_tls_creds_get_path(&creds->parent_obj,
-                                       QCRYPTO_TLS_CREDS_X509_CA_CERT,
-                                       true, &cacert, errp) < 0 ||
-            qcrypto_tls_creds_get_path(&creds->parent_obj,
                                        QCRYPTO_TLS_CREDS_X509_CA_CRL,
                                        false, &cacrl, errp) < 0 ||
             qcrypto_tls_creds_get_path(&creds->parent_obj,
@@ -598,9 +601,6 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
         }
     } else {
         if (qcrypto_tls_creds_get_path(&creds->parent_obj,
-                                       QCRYPTO_TLS_CREDS_X509_CA_CERT,
-                                       true, &cacert, errp) < 0 ||
-            qcrypto_tls_creds_get_path(&creds->parent_obj,
                                        QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
                                        false, &cert, errp) < 0 ||
             qcrypto_tls_creds_get_path(&creds->parent_obj,
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 11/21] crypto: reduce duplication in handling TLS priority strings
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
                   ` (9 preceding siblings ...)
  2025-10-30 14:49 ` [PATCH 10/21] crypto: remove duplication loading x509 CA cert Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-10-30 19:40   ` Marc-André Lureau
  2025-10-30 14:49 ` [PATCH 12/21] crypto: introduce method for reloading TLS creds Daniel P. Berrangé
                   ` (9 subsequent siblings)
  20 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

The logic for setting the TLS priority string on a session object has a
significant amount of logic duplication across the different credential
types. By recording the extra priority string suffix against the
credential class, we can introduce a common method for building the
priority string. The TLS session can now set the priority string without
caring about the credential type.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscreds.c         | 15 ++++++++++
 crypto/tlscredsanon.c     |  2 ++
 crypto/tlscredspsk.c      |  2 ++
 crypto/tlssession.c       | 60 ++++++---------------------------------
 include/crypto/tlscreds.h | 13 +++++++++
 5 files changed, 41 insertions(+), 51 deletions(-)

diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
index 1e39ee1141..49c7eb46a5 100644
--- a/crypto/tlscreds.c
+++ b/crypto/tlscreds.c
@@ -266,6 +266,21 @@ bool qcrypto_tls_creds_check_endpoint(QCryptoTLSCreds *creds,
     return true;
 }
 
+
+char *qcrypto_tls_creds_get_priority(QCryptoTLSCreds *creds)
+{
+    QCryptoTLSCredsClass *tcc = QCRYPTO_TLS_CREDS_GET_CLASS(creds);
+    const char *priorityBase =
+        creds->priority ? creds->priority : CONFIG_TLS_PRIORITY;
+
+    if (tcc->prioritySuffix) {
+        return g_strdup_printf("%s:%s", priorityBase, tcc->prioritySuffix);
+    } else {
+        return g_strdup(priorityBase);
+    }
+}
+
+
 static const TypeInfo qcrypto_tls_creds_info = {
     .parent = TYPE_OBJECT,
     .name = TYPE_QCRYPTO_TLS_CREDS,
diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c
index 1ddfe4eb31..5c55b07b2f 100644
--- a/crypto/tlscredsanon.c
+++ b/crypto/tlscredsanon.c
@@ -137,8 +137,10 @@ static void
 qcrypto_tls_creds_anon_class_init(ObjectClass *oc, const void *data)
 {
     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+    QCryptoTLSCredsClass *tcc = QCRYPTO_TLS_CREDS_CLASS(oc);
 
     ucc->complete = qcrypto_tls_creds_anon_complete;
+    tcc->prioritySuffix = "+ANON-DH";
 }
 
 
diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c
index bf4efe2114..6c2feae077 100644
--- a/crypto/tlscredspsk.c
+++ b/crypto/tlscredspsk.c
@@ -240,8 +240,10 @@ static void
 qcrypto_tls_creds_psk_class_init(ObjectClass *oc, const void *data)
 {
     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+    QCryptoTLSCredsClass *tcc = QCRYPTO_TLS_CREDS_CLASS(oc);
 
     ucc->complete = qcrypto_tls_creds_psk_complete;
+    tcc->prioritySuffix = "+ECDHE-PSK:+DHE-PSK:+PSK";
 
     object_class_property_add_str(oc, "username",
                                   qcrypto_tls_creds_psk_prop_get_username,
diff --git a/crypto/tlssession.c b/crypto/tlssession.c
index 92fe4f0380..77f334add3 100644
--- a/crypto/tlssession.c
+++ b/crypto/tlssession.c
@@ -155,9 +155,6 @@ qcrypto_tls_session_pull(void *opaque, void *buf, size_t len)
     }
 }
 
-#define TLS_PRIORITY_ADDITIONAL_ANON "+ANON-DH"
-#define TLS_PRIORITY_ADDITIONAL_PSK "+ECDHE-PSK:+DHE-PSK:+PSK"
-
 QCryptoTLSSession *
 qcrypto_tls_session_new(QCryptoTLSCreds *creds,
                         const char *hostname,
@@ -167,6 +164,7 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds,
 {
     QCryptoTLSSession *session;
     int ret;
+    g_autofree char *prio = NULL;
 
     session = g_new0(QCryptoTLSSession, 1);
     trace_qcrypto_tls_session_new(
@@ -200,28 +198,17 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds,
         goto error;
     }
 
+    prio = qcrypto_tls_creds_get_priority(creds);
+    ret = gnutls_priority_set_direct(session->handle, prio, NULL);
+    if (ret < 0) {
+        error_setg(errp, "Unable to set TLS session priority %s: %s",
+                   prio, gnutls_strerror(ret));
+        goto error;
+    }
+
     if (object_dynamic_cast(OBJECT(creds),
                             TYPE_QCRYPTO_TLS_CREDS_ANON)) {
         QCryptoTLSCredsAnon *acreds = QCRYPTO_TLS_CREDS_ANON(creds);
-        char *prio;
-
-        if (creds->priority != NULL) {
-            prio = g_strdup_printf("%s:%s",
-                                   creds->priority,
-                                   TLS_PRIORITY_ADDITIONAL_ANON);
-        } else {
-            prio = g_strdup(CONFIG_TLS_PRIORITY ":"
-                            TLS_PRIORITY_ADDITIONAL_ANON);
-        }
-
-        ret = gnutls_priority_set_direct(session->handle, prio, NULL);
-        if (ret < 0) {
-            error_setg(errp, "Unable to set TLS session priority %s: %s",
-                       prio, gnutls_strerror(ret));
-            g_free(prio);
-            goto error;
-        }
-        g_free(prio);
         if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
             ret = gnutls_credentials_set(session->handle,
                                          GNUTLS_CRD_ANON,
@@ -239,25 +226,6 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds,
     } else if (object_dynamic_cast(OBJECT(creds),
                                    TYPE_QCRYPTO_TLS_CREDS_PSK)) {
         QCryptoTLSCredsPSK *pcreds = QCRYPTO_TLS_CREDS_PSK(creds);
-        char *prio;
-
-        if (creds->priority != NULL) {
-            prio = g_strdup_printf("%s:%s",
-                                   creds->priority,
-                                   TLS_PRIORITY_ADDITIONAL_PSK);
-        } else {
-            prio = g_strdup(CONFIG_TLS_PRIORITY ":"
-                            TLS_PRIORITY_ADDITIONAL_PSK);
-        }
-
-        ret = gnutls_priority_set_direct(session->handle, prio, NULL);
-        if (ret < 0) {
-            error_setg(errp, "Unable to set TLS session priority %s: %s",
-                       prio, gnutls_strerror(ret));
-            g_free(prio);
-            goto error;
-        }
-        g_free(prio);
         if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
             ret = gnutls_credentials_set(session->handle,
                                          GNUTLS_CRD_PSK,
@@ -275,17 +243,7 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds,
     } else if (object_dynamic_cast(OBJECT(creds),
                                    TYPE_QCRYPTO_TLS_CREDS_X509)) {
         QCryptoTLSCredsX509 *tcreds = QCRYPTO_TLS_CREDS_X509(creds);
-        const char *prio = creds->priority;
-        if (!prio) {
-            prio = CONFIG_TLS_PRIORITY;
-        }
 
-        ret = gnutls_priority_set_direct(session->handle, prio, NULL);
-        if (ret < 0) {
-            error_setg(errp, "Cannot set default TLS session priority %s: %s",
-                       prio, gnutls_strerror(ret));
-            goto error;
-        }
         ret = gnutls_credentials_set(session->handle,
                                      GNUTLS_CRD_CERTIFICATE,
                                      tcreds->data);
diff --git a/include/crypto/tlscreds.h b/include/crypto/tlscreds.h
index 2a8a857010..afd1016088 100644
--- a/include/crypto/tlscreds.h
+++ b/include/crypto/tlscreds.h
@@ -47,6 +47,7 @@ typedef bool (*CryptoTLSCredsReload)(QCryptoTLSCreds *, Error **);
 struct QCryptoTLSCredsClass {
     ObjectClass parent_class;
     CryptoTLSCredsReload reload;
+    const char *prioritySuffix;
 };
 
 /**
@@ -64,4 +65,16 @@ bool qcrypto_tls_creds_check_endpoint(QCryptoTLSCreds *creds,
                                       QCryptoTLSCredsEndpoint endpoint,
                                       Error **errp);
 
+
+/**
+ * qcrypto_tls_creds_get_priority:
+ * @creds: pointer to a TLS credentials object
+ *
+ * Get the TLS credentials priority string. The caller
+ * must free the returned string when no longer required.
+ *
+ * Returns: a non-NULL priority string
+ */
+char *qcrypto_tls_creds_get_priority(QCryptoTLSCreds *creds);
+
 #endif /* QCRYPTO_TLSCREDS_H */
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 12/21] crypto: introduce method for reloading TLS creds
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
                   ` (10 preceding siblings ...)
  2025-10-30 14:49 ` [PATCH 11/21] crypto: reduce duplication in handling TLS priority strings Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-10-30 19:43   ` Marc-André Lureau
  2025-10-30 14:49 ` [PATCH 13/21] crypto: introduce a wrapper around gnutls credentials Daniel P. Berrangé
                   ` (8 subsequent siblings)
  20 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

This prevents direct access of the class members by the VNC
display code.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscreds.c         | 15 +++++++++++++++
 include/crypto/tlscreds.h | 13 +++++++++++++
 ui/vnc.c                  |  9 +--------
 3 files changed, 29 insertions(+), 8 deletions(-)

diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
index 49c7eb46a5..9433b4c363 100644
--- a/crypto/tlscreds.c
+++ b/crypto/tlscreds.c
@@ -281,6 +281,21 @@ char *qcrypto_tls_creds_get_priority(QCryptoTLSCreds *creds)
 }
 
 
+bool qcrypto_tls_creds_reload(QCryptoTLSCreds *creds,
+                              Error **errp)
+{
+    QCryptoTLSCredsClass *credscls = QCRYPTO_TLS_CREDS_GET_CLASS(OBJECT(creds));
+
+    if (credscls->reload) {
+        return credscls->reload(creds, errp);
+    }
+
+    error_setg(errp, "%s does not support reloading credentials",
+               object_get_typename(OBJECT(creds)));
+    return false;
+}
+
+
 static const TypeInfo qcrypto_tls_creds_info = {
     .parent = TYPE_OBJECT,
     .name = TYPE_QCRYPTO_TLS_CREDS,
diff --git a/include/crypto/tlscreds.h b/include/crypto/tlscreds.h
index afd1016088..bb9280ed1a 100644
--- a/include/crypto/tlscreds.h
+++ b/include/crypto/tlscreds.h
@@ -77,4 +77,17 @@ bool qcrypto_tls_creds_check_endpoint(QCryptoTLSCreds *creds,
  */
 char *qcrypto_tls_creds_get_priority(QCryptoTLSCreds *creds);
 
+
+/**
+ * qcrypto_tls_creds_reload:
+ * @creds: pointer to a TLS credentials object
+ * @errp: pointer to a NULL-initialized error object
+ *
+ * Request a reload of the TLS credentials, if supported
+ *
+ * Returns: true on success, false on error or if not supported
+ */
+bool qcrypto_tls_creds_reload(QCryptoTLSCreds *creds,
+                              Error **errp);
+
 #endif /* QCRYPTO_TLSCREDS_H */
diff --git a/ui/vnc.c b/ui/vnc.c
index 77c823bf2e..6b32dd0fe9 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -578,7 +578,6 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp)
 bool vnc_display_reload_certs(const char *id, Error **errp)
 {
     VncDisplay *vd = vnc_display_find(id);
-    QCryptoTLSCredsClass *creds = NULL;
 
     if (!vd) {
         error_setg(errp, "Can not find vnc display");
@@ -590,13 +589,7 @@ bool vnc_display_reload_certs(const char *id, Error **errp)
         return false;
     }
 
-    creds = QCRYPTO_TLS_CREDS_GET_CLASS(OBJECT(vd->tlscreds));
-    if (creds->reload == NULL) {
-        error_setg(errp, "%s doesn't support to reload TLS credential",
-                   object_get_typename(OBJECT(vd->tlscreds)));
-        return false;
-    }
-    if (!creds->reload(vd->tlscreds, errp)) {
+    if (!qcrypto_tls_creds_reload(vd->tlscreds, errp)) {
         return false;
     }
 
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 13/21] crypto: introduce a wrapper around gnutls credentials
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
                   ` (11 preceding siblings ...)
  2025-10-30 14:49 ` [PATCH 12/21] crypto: introduce method for reloading TLS creds Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-10-31 11:23   ` Marc-André Lureau
  2025-10-30 14:49 ` [PATCH 14/21] crypto: fix lifecycle handling of gnutls credentials objects Daniel P. Berrangé
                   ` (7 subsequent siblings)
  20 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

The gnutls_credentials_set() method has a very suprising API contract
that requires the caller to preserve the passed in credentials pointer
for as long as the gnutls_session_t object is alive. QEMU is failing
to ensure this happens.

In QEMU the GNUTLS credentials object is owned by the QCryptoTLSCreds
object instance while the GNUTLS session object is owned by the
QCryptoTLSSession object instance. Their lifetimes are not guaranteed
to be the same, though in most common usage the credentials will outlive
the session. This is notably not the case, however, after the VNC server
gained the ability to reload credentials on the fly with:

  commit 1f08e3415120637cad7f540d9ceb4dba3136dbdd
  Author: Zihao Chang <changzihao1@huawei.com>
  Date:   Tue Mar 16 15:58:44 2021 +0800

    vnc: support reload x509 certificates for vnc

If that is triggered while a VNC client is in the middle of performing
a TLS handshake, we might hit a use-after-free.

It is difficult to correct this problem because there's no way to deep-
clone a GNUTLS credentials object, nor is it reference counted. Thus we
introduce a QCryptoTLSCredsBox object whose only purpose is to add
reference counting around the GNUTLS credentials object.

The DH parameters set against a credentials object also have to be kept
alive for as long as the credentials exist. So the box must also hold
the DH parameters pointer.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/meson.build   |   5 ++-
 crypto/tlscredsbox.c | 101 +++++++++++++++++++++++++++++++++++++++++++
 crypto/tlscredsbox.h |  46 ++++++++++++++++++++
 3 files changed, 151 insertions(+), 1 deletion(-)
 create mode 100644 crypto/tlscredsbox.c
 create mode 100644 crypto/tlscredsbox.h

diff --git a/crypto/meson.build b/crypto/meson.build
index 735635de1f..1fc14b2a04 100644
--- a/crypto/meson.build
+++ b/crypto/meson.build
@@ -25,7 +25,10 @@ crypto_ss.add(files(
 ))
 
 if gnutls.found()
-  crypto_ss.add(files('x509-utils.c'))
+  crypto_ss.add(files(
+    'tlscredsbox.c',
+    'x509-utils.c',
+  ))
 endif
 
 if nettle.found()
diff --git a/crypto/tlscredsbox.c b/crypto/tlscredsbox.c
new file mode 100644
index 0000000000..b8d9846af8
--- /dev/null
+++ b/crypto/tlscredsbox.c
@@ -0,0 +1,101 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * QEMU crypto TLS credential support
+ *
+ * Copyright (c) 2025 Red Hat, Inc.
+ */
+
+#include "qemu/osdep.h"
+#include "crypto/tlscredsbox.h"
+#include "qemu/atomic.h"
+
+
+static QCryptoTLSCredsBox *
+qcrypto_tls_creds_box_new_impl(int type, bool server)
+{
+    QCryptoTLSCredsBox *credsbox = g_new0(QCryptoTLSCredsBox, 1);
+    credsbox->ref = 1;
+    credsbox->server = server;
+    credsbox->type = type;
+    return credsbox;
+}
+
+
+QCryptoTLSCredsBox *
+qcrypto_tls_creds_box_new_server(int type)
+{
+    return qcrypto_tls_creds_box_new_impl(type, true);
+}
+
+
+QCryptoTLSCredsBox *
+qcrypto_tls_creds_box_new_client(int type)
+{
+    return qcrypto_tls_creds_box_new_impl(type, false);
+}
+
+static void qcrypto_tls_creds_box_free(QCryptoTLSCredsBox *credsbox)
+{
+    switch (credsbox->type) {
+    case GNUTLS_CRD_CERTIFICATE:
+        if (credsbox->data.cert) {
+            gnutls_certificate_free_credentials(credsbox->data.cert);
+        }
+        break;
+    case GNUTLS_CRD_PSK:
+        if (credsbox->server) {
+            if (credsbox->data.pskserver) {
+                gnutls_psk_free_server_credentials(credsbox->data.pskserver);
+            }
+        } else {
+            if (credsbox->data.pskclient) {
+                gnutls_psk_free_client_credentials(credsbox->data.pskclient);
+            }
+        }
+        break;
+    case GNUTLS_CRD_ANON:
+        if (credsbox->server) {
+            if (credsbox->data.anonserver) {
+                gnutls_anon_free_server_credentials(credsbox->data.anonserver);
+            }
+        } else {
+            if (credsbox->data.anonclient) {
+                gnutls_anon_free_client_credentials(credsbox->data.anonclient);
+            }
+        }
+        break;
+    default:
+        g_assert_not_reached();
+    }
+
+    if (credsbox->dh_params) {
+        gnutls_dh_params_deinit(credsbox->dh_params);
+    }
+
+    g_free(credsbox);
+}
+
+
+void qcrypto_tls_creds_box_ref(QCryptoTLSCredsBox *credsbox)
+{
+    uint32_t ref = qatomic_fetch_inc(&credsbox->ref);
+    /* Assert waaay before the integer overflows */
+    g_assert(ref < INT_MAX);
+}
+
+
+void qcrypto_tls_creds_box_unref(QCryptoTLSCredsBox *credsbox)
+{
+    if (!credsbox) {
+        return;
+    }
+
+    g_assert(credsbox->ref > 0);
+
+    if (qatomic_fetch_dec(&credsbox->ref) == 1) {
+        qcrypto_tls_creds_box_free(credsbox);
+    }
+
+}
+
diff --git a/crypto/tlscredsbox.h b/crypto/tlscredsbox.h
new file mode 100644
index 0000000000..5d89335f46
--- /dev/null
+++ b/crypto/tlscredsbox.h
@@ -0,0 +1,46 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * QEMU crypto TLS credential support
+ *
+ * Copyright (c) 2025 Red Hat, Inc.
+ */
+
+#ifndef QCRYPTO_TLSCREDS_BOX_H
+#define QCRYPTO_TLSCREDS_BOX_H
+
+#include "qom/object.h"
+
+#ifdef CONFIG_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
+
+typedef struct QCryptoTLSCredsBox QCryptoTLSCredsBox;
+
+struct QCryptoTLSCredsBox {
+    uint32_t ref;
+    bool server;
+    int type;
+    union {
+        void *any;
+#ifdef CONFIG_GNUTLS
+        gnutls_anon_server_credentials_t anonserver;
+        gnutls_anon_client_credentials_t anonclient;
+        gnutls_psk_server_credentials_t pskserver;
+        gnutls_psk_client_credentials_t pskclient;
+        gnutls_certificate_credentials_t cert;
+#endif
+    } data;
+#ifdef CONFIG_GNUTLS
+    gnutls_dh_params_t dh_params;
+#endif
+};
+
+QCryptoTLSCredsBox *qcrypto_tls_creds_box_new_server(int type);
+QCryptoTLSCredsBox *qcrypto_tls_creds_box_new_client(int type);
+void qcrypto_tls_creds_box_ref(QCryptoTLSCredsBox *credsbox);
+void qcrypto_tls_creds_box_unref(QCryptoTLSCredsBox *credsbox);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSCredsBox, qcrypto_tls_creds_box_unref);
+
+#endif /* QCRYPTO_TLSCREDS_BOX_H */
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 14/21] crypto: fix lifecycle handling of gnutls credentials objects
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
                   ` (12 preceding siblings ...)
  2025-10-30 14:49 ` [PATCH 13/21] crypto: introduce a wrapper around gnutls credentials Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-10-31 11:24   ` Marc-André Lureau
  2025-10-30 14:49 ` [PATCH 15/21] crypto: make TLS credentials structs private Daniel P. Berrangé
                   ` (6 subsequent siblings)
  20 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

As described in the previous commit, the gnutls credentials need to
be kept alive for as long as the gnutls session object exists. Convert
the QCryptoTLSCreds objects to use QCryptoTLSCredsBox and holding the
gnutls credential objects. When loading the credentials into a gnutls
session, store a reference to the box into the QCryptoTLSSession object.

This has the useful side effect that the QCryptoTLSSession code no
longer needs to know about all the different credential types, it can
use the generic pointer stored in the box.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscreds.c     |  5 +--
 crypto/tlscredsanon.c | 48 +++++---------------------
 crypto/tlscredspriv.h | 20 ++---------
 crypto/tlscredspsk.c  | 46 ++++++++-----------------
 crypto/tlscredsx509.c | 71 +++++++++++++-------------------------
 crypto/tlssession.c   | 80 ++++++++++++++-----------------------------
 6 files changed, 75 insertions(+), 195 deletions(-)

diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
index 9433b4c363..798c9712fb 100644
--- a/crypto/tlscreds.c
+++ b/crypto/tlscreds.c
@@ -246,10 +246,7 @@ qcrypto_tls_creds_finalize(Object *obj)
 {
     QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
 
-    if (creds->dh_params) {
-        gnutls_dh_params_deinit(creds->dh_params);
-    }
-
+    qcrypto_tls_creds_box_unref(creds->box);
     g_free(creds->dir);
     g_free(creds->priority);
 }
diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c
index 5c55b07b2f..0a728ccbf6 100644
--- a/crypto/tlscredsanon.c
+++ b/crypto/tlscredsanon.c
@@ -36,6 +36,7 @@ static int
 qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
                             Error **errp)
 {
+    g_autoptr(QCryptoTLSCredsBox) box = NULL;
     g_autofree char *dhparams = NULL;
     int ret;
 
@@ -43,6 +44,8 @@ qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
             creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
 
     if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+        box = qcrypto_tls_creds_box_new_server(GNUTLS_CRD_ANON);
+
         if (creds->parent_obj.dir &&
             qcrypto_tls_creds_get_path(&creds->parent_obj,
                                        QCRYPTO_TLS_CREDS_DH_PARAMS,
@@ -50,7 +53,7 @@ qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
             return -1;
         }
 
-        ret = gnutls_anon_allocate_server_credentials(&creds->data.server);
+        ret = gnutls_anon_allocate_server_credentials(&box->data.anonserver);
         if (ret < 0) {
             error_setg(errp, "Cannot allocate credentials: %s",
                        gnutls_strerror(ret));
@@ -58,42 +61,26 @@ qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
         }
 
         if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams,
-                                                 &creds->parent_obj.dh_params,
-                                                 errp) < 0) {
+                                                 &box->dh_params, errp) < 0) {
             return -1;
         }
 
-        gnutls_anon_set_server_dh_params(creds->data.server,
-                                         creds->parent_obj.dh_params);
+        gnutls_anon_set_server_dh_params(box->data.anonserver,
+                                         box->dh_params);
     } else {
-        ret = gnutls_anon_allocate_client_credentials(&creds->data.client);
+        ret = gnutls_anon_allocate_client_credentials(&box->data.anonclient);
         if (ret < 0) {
             error_setg(errp, "Cannot allocate credentials: %s",
                        gnutls_strerror(ret));
             return -1;
         }
     }
+    creds->parent_obj.box = g_steal_pointer(&box);
 
     return 0;
 }
 
 
-static void
-qcrypto_tls_creds_anon_unload(QCryptoTLSCredsAnon *creds)
-{
-    if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
-        if (creds->data.client) {
-            gnutls_anon_free_client_credentials(creds->data.client);
-            creds->data.client = NULL;
-        }
-    } else {
-        if (creds->data.server) {
-            gnutls_anon_free_server_credentials(creds->data.server);
-            creds->data.server = NULL;
-        }
-    }
-}
-
 #else /* ! CONFIG_GNUTLS */
 
 
@@ -105,13 +92,6 @@ qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds G_GNUC_UNUSED,
 }
 
 
-static void
-qcrypto_tls_creds_anon_unload(QCryptoTLSCredsAnon *creds G_GNUC_UNUSED)
-{
-    /* nada */
-}
-
-
 #endif /* ! CONFIG_GNUTLS */
 
 
@@ -124,15 +104,6 @@ qcrypto_tls_creds_anon_complete(UserCreatable *uc, Error **errp)
 }
 
 
-static void
-qcrypto_tls_creds_anon_finalize(Object *obj)
-{
-    QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj);
-
-    qcrypto_tls_creds_anon_unload(creds);
-}
-
-
 static void
 qcrypto_tls_creds_anon_class_init(ObjectClass *oc, const void *data)
 {
@@ -148,7 +119,6 @@ static const TypeInfo qcrypto_tls_creds_anon_info = {
     .parent = TYPE_QCRYPTO_TLS_CREDS,
     .name = TYPE_QCRYPTO_TLS_CREDS_ANON,
     .instance_size = sizeof(QCryptoTLSCredsAnon),
-    .instance_finalize = qcrypto_tls_creds_anon_finalize,
     .class_size = sizeof(QCryptoTLSCredsAnonClass),
     .class_init = qcrypto_tls_creds_anon_class_init,
     .interfaces = (const InterfaceInfo[]) {
diff --git a/crypto/tlscredspriv.h b/crypto/tlscredspriv.h
index df9815a286..4e6dffa22f 100644
--- a/crypto/tlscredspriv.h
+++ b/crypto/tlscredspriv.h
@@ -22,6 +22,7 @@
 #define QCRYPTO_TLSCREDSPRIV_H
 
 #include "crypto/tlscreds.h"
+#include "crypto/tlscredsbox.h"
 
 #ifdef CONFIG_GNUTLS
 #include <gnutls/gnutls.h>
@@ -31,39 +32,22 @@ struct QCryptoTLSCreds {
     Object parent_obj;
     char *dir;
     QCryptoTLSCredsEndpoint endpoint;
-#ifdef CONFIG_GNUTLS
-    gnutls_dh_params_t dh_params;
-#endif
     bool verifyPeer;
     char *priority;
+    QCryptoTLSCredsBox *box;
 };
 
 struct QCryptoTLSCredsAnon {
     QCryptoTLSCreds parent_obj;
-#ifdef CONFIG_GNUTLS
-    union {
-        gnutls_anon_server_credentials_t server;
-        gnutls_anon_client_credentials_t client;
-    } data;
-#endif
 };
 
 struct QCryptoTLSCredsPSK {
     QCryptoTLSCreds parent_obj;
     char *username;
-#ifdef CONFIG_GNUTLS
-    union {
-        gnutls_psk_server_credentials_t server;
-        gnutls_psk_client_credentials_t client;
-    } data;
-#endif
 };
 
 struct QCryptoTLSCredsX509 {
     QCryptoTLSCreds parent_obj;
-#ifdef CONFIG_GNUTLS
-    gnutls_certificate_credentials_t data;
-#endif
     bool sanityCheck;
     char *passwordid;
 };
diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c
index 6c2feae077..5568f1ad0c 100644
--- a/crypto/tlscredspsk.c
+++ b/crypto/tlscredspsk.c
@@ -71,6 +71,7 @@ static int
 qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
                            Error **errp)
 {
+    g_autoptr(QCryptoTLSCredsBox) box = NULL;
     g_autofree char *pskfile = NULL;
     g_autofree char *dhparams = NULL;
     const char *username;
@@ -87,6 +88,8 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
     }
 
     if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+        box = qcrypto_tls_creds_box_new_server(GNUTLS_CRD_PSK);
+
         if (creds->username) {
             error_setg(errp, "username should not be set when endpoint=server");
             goto cleanup;
@@ -101,7 +104,7 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
             goto cleanup;
         }
 
-        ret = gnutls_psk_allocate_server_credentials(&creds->data.server);
+        ret = gnutls_psk_allocate_server_credentials(&box->data.pskserver);
         if (ret < 0) {
             error_setg(errp, "Cannot allocate credentials: %s",
                        gnutls_strerror(ret));
@@ -109,20 +112,23 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
         }
 
         if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams,
-                                                 &creds->parent_obj.dh_params,
+                                                 &box->dh_params,
                                                  errp) < 0) {
             goto cleanup;
         }
 
-        ret = gnutls_psk_set_server_credentials_file(creds->data.server, pskfile);
+        ret = gnutls_psk_set_server_credentials_file(box->data.pskserver,
+                                                     pskfile);
         if (ret < 0) {
             error_setg(errp, "Cannot set PSK server credentials: %s",
                        gnutls_strerror(ret));
             goto cleanup;
         }
-        gnutls_psk_set_server_dh_params(creds->data.server,
-                                        creds->parent_obj.dh_params);
+        gnutls_psk_set_server_dh_params(box->data.pskserver,
+                                        box->dh_params);
     } else {
+        box = qcrypto_tls_creds_box_new_client(GNUTLS_CRD_PSK);
+
         if (qcrypto_tls_creds_get_path(&creds->parent_obj,
                                        QCRYPTO_TLS_CREDS_PSKFILE,
                                        true, &pskfile, errp) < 0) {
@@ -138,14 +144,14 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
             goto cleanup;
         }
 
-        ret = gnutls_psk_allocate_client_credentials(&creds->data.client);
+        ret = gnutls_psk_allocate_client_credentials(&box->data.pskclient);
         if (ret < 0) {
             error_setg(errp, "Cannot allocate credentials: %s",
                        gnutls_strerror(ret));
             goto cleanup;
         }
 
-        ret = gnutls_psk_set_client_credentials(creds->data.client,
+        ret = gnutls_psk_set_client_credentials(box->data.pskclient,
                                                 username, &key, GNUTLS_PSK_KEY_HEX);
         if (ret < 0) {
             error_setg(errp, "Cannot set PSK client credentials: %s",
@@ -153,6 +159,7 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
             goto cleanup;
         }
     }
+    creds->parent_obj.box = g_steal_pointer(&box);
 
     rv = 0;
  cleanup:
@@ -160,23 +167,6 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
     return rv;
 }
 
-
-static void
-qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK *creds)
-{
-    if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
-        if (creds->data.client) {
-            gnutls_psk_free_client_credentials(creds->data.client);
-            creds->data.client = NULL;
-        }
-    } else {
-        if (creds->data.server) {
-            gnutls_psk_free_server_credentials(creds->data.server);
-            creds->data.server = NULL;
-        }
-    }
-}
-
 #else /* ! CONFIG_GNUTLS */
 
 
@@ -188,13 +178,6 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds G_GNUC_UNUSED,
 }
 
 
-static void
-qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK *creds G_GNUC_UNUSED)
-{
-    /* nada */
-}
-
-
 #endif /* ! CONFIG_GNUTLS */
 
 
@@ -212,7 +195,6 @@ qcrypto_tls_creds_psk_finalize(Object *obj)
 {
     QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj);
 
-    qcrypto_tls_creds_psk_unload(creds);
     g_free(creds->username);
 }
 
diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index f2f1aa2815..ef31ea664c 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -562,6 +562,7 @@ static int
 qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
                             Error **errp)
 {
+    g_autoptr(QCryptoTLSCredsBox) box = NULL;
     g_autofree char *cacert = NULL;
     g_autofree char *cacrl = NULL;
     g_autofree char *cert = NULL;
@@ -578,6 +579,19 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
 
     trace_qcrypto_tls_creds_x509_load(creds, creds->parent_obj.dir);
 
+    if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+        box = qcrypto_tls_creds_box_new_server(GNUTLS_CRD_CERTIFICATE);
+    } else {
+        box = qcrypto_tls_creds_box_new_client(GNUTLS_CRD_CERTIFICATE);
+    }
+
+    ret = gnutls_certificate_allocate_credentials(&box->data.cert);
+    if (ret < 0) {
+        error_setg(errp, "Cannot allocate credentials: '%s'",
+                   gnutls_strerror(ret));
+        return -1;
+    }
+
     if (qcrypto_tls_creds_get_path(&creds->parent_obj,
                                    QCRYPTO_TLS_CREDS_X509_CA_CERT,
                                    true, &cacert, errp) < 0) {
@@ -616,14 +630,7 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
         return -1;
     }
 
-    ret = gnutls_certificate_allocate_credentials(&creds->data);
-    if (ret < 0) {
-        error_setg(errp, "Cannot allocate credentials: '%s'",
-                   gnutls_strerror(ret));
-        return -1;
-    }
-
-    ret = gnutls_certificate_set_x509_trust_file(creds->data,
+    ret = gnutls_certificate_set_x509_trust_file(box->data.cert,
                                                  cacert,
                                                  GNUTLS_X509_FMT_PEM);
     if (ret < 0) {
@@ -641,7 +648,7 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
                 return -1;
             }
         }
-        ret = gnutls_certificate_set_x509_key_file2(creds->data,
+        ret = gnutls_certificate_set_x509_key_file2(box->data.cert,
                                                     cert, key,
                                                     GNUTLS_X509_FMT_PEM,
                                                     password,
@@ -655,7 +662,7 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
     }
 
     if (cacrl != NULL) {
-        ret = gnutls_certificate_set_x509_crl_file(creds->data,
+        ret = gnutls_certificate_set_x509_crl_file(box->data.cert,
                                                    cacrl,
                                                    GNUTLS_X509_FMT_PEM);
         if (ret < 0) {
@@ -667,28 +674,18 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
 
     if (isServer) {
         if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams,
-                                                 &creds->parent_obj.dh_params,
+                                                 &box->dh_params,
                                                  errp) < 0) {
             return -1;
         }
-        gnutls_certificate_set_dh_params(creds->data,
-                                         creds->parent_obj.dh_params);
+        gnutls_certificate_set_dh_params(box->data.cert, box->dh_params);
     }
+    creds->parent_obj.box = g_steal_pointer(&box);
 
     return 0;
 }
 
 
-static void
-qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds)
-{
-    if (creds->data) {
-        gnutls_certificate_free_credentials(creds->data);
-        creds->data = NULL;
-    }
-}
-
-
 #else /* ! CONFIG_GNUTLS */
 
 
@@ -700,13 +697,6 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED,
 }
 
 
-static void
-qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED)
-{
-    /* nada */
-}
-
-
 #endif /* ! CONFIG_GNUTLS */
 
 
@@ -769,29 +759,17 @@ qcrypto_tls_creds_x509_reload(QCryptoTLSCreds *creds, Error **errp)
 {
     QCryptoTLSCredsX509 *x509_creds = QCRYPTO_TLS_CREDS_X509(creds);
     Error *local_err = NULL;
-    gnutls_certificate_credentials_t creds_data = x509_creds->data;
-    gnutls_dh_params_t creds_dh_params = creds->dh_params;
+    QCryptoTLSCredsBox *creds_box = creds->box;
 
-    x509_creds->data = NULL;
-    creds->dh_params = NULL;
+    creds->box = NULL;
     qcrypto_tls_creds_x509_load(x509_creds, &local_err);
     if (local_err) {
-        qcrypto_tls_creds_x509_unload(x509_creds);
-        if (creds->dh_params) {
-            gnutls_dh_params_deinit(creds->dh_params);
-        }
-        x509_creds->data = creds_data;
-        creds->dh_params = creds_dh_params;
+        creds->box = creds_box;
         error_propagate(errp, local_err);
         return false;
     }
 
-    if (creds_data) {
-        gnutls_certificate_free_credentials(creds_data);
-    }
-    if (creds_dh_params) {
-        gnutls_dh_params_deinit(creds_dh_params);
-    }
+    qcrypto_tls_creds_box_unref(creds_box);
     return true;
 }
 
@@ -824,7 +802,6 @@ qcrypto_tls_creds_x509_finalize(Object *obj)
     QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
 
     g_free(creds->passwordid);
-    qcrypto_tls_creds_x509_unload(creds);
 }
 
 
diff --git a/crypto/tlssession.c b/crypto/tlssession.c
index 77f334add3..a1dc3b3ce0 100644
--- a/crypto/tlssession.c
+++ b/crypto/tlssession.c
@@ -38,6 +38,7 @@
 
 struct QCryptoTLSSession {
     QCryptoTLSCreds *creds;
+    QCryptoTLSCredsBox *credsbox;
     gnutls_session_t handle;
     char *hostname;
     char *authzid;
@@ -78,6 +79,7 @@ qcrypto_tls_session_free(QCryptoTLSSession *session)
     g_free(session->hostname);
     g_free(session->peername);
     g_free(session->authzid);
+    qcrypto_tls_creds_box_unref(session->credsbox);
     object_unref(OBJECT(session->creds));
     qemu_mutex_destroy(&session->lock);
     g_free(session);
@@ -206,63 +208,31 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds,
         goto error;
     }
 
-    if (object_dynamic_cast(OBJECT(creds),
-                            TYPE_QCRYPTO_TLS_CREDS_ANON)) {
-        QCryptoTLSCredsAnon *acreds = QCRYPTO_TLS_CREDS_ANON(creds);
-        if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
-            ret = gnutls_credentials_set(session->handle,
-                                         GNUTLS_CRD_ANON,
-                                         acreds->data.server);
-        } else {
-            ret = gnutls_credentials_set(session->handle,
-                                         GNUTLS_CRD_ANON,
-                                         acreds->data.client);
-        }
-        if (ret < 0) {
-            error_setg(errp, "Cannot set session credentials: %s",
-                       gnutls_strerror(ret));
-            goto error;
-        }
-    } else if (object_dynamic_cast(OBJECT(creds),
-                                   TYPE_QCRYPTO_TLS_CREDS_PSK)) {
-        QCryptoTLSCredsPSK *pcreds = QCRYPTO_TLS_CREDS_PSK(creds);
-        if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
-            ret = gnutls_credentials_set(session->handle,
-                                         GNUTLS_CRD_PSK,
-                                         pcreds->data.server);
-        } else {
-            ret = gnutls_credentials_set(session->handle,
-                                         GNUTLS_CRD_PSK,
-                                         pcreds->data.client);
-        }
-        if (ret < 0) {
-            error_setg(errp, "Cannot set session credentials: %s",
-                       gnutls_strerror(ret));
-            goto error;
-        }
-    } else if (object_dynamic_cast(OBJECT(creds),
-                                   TYPE_QCRYPTO_TLS_CREDS_X509)) {
-        QCryptoTLSCredsX509 *tcreds = QCRYPTO_TLS_CREDS_X509(creds);
+    ret = gnutls_credentials_set(session->handle,
+                                 creds->box->type,
+                                 creds->box->data.any);
+    if (ret < 0) {
+        error_setg(errp, "Cannot set session credentials: %s",
+                   gnutls_strerror(ret));
+        goto error;
+    }
 
-        ret = gnutls_credentials_set(session->handle,
-                                     GNUTLS_CRD_CERTIFICATE,
-                                     tcreds->data);
-        if (ret < 0) {
-            error_setg(errp, "Cannot set session credentials: %s",
-                       gnutls_strerror(ret));
-            goto error;
-        }
+    /*
+     * creds->box->data.any must be kept alive for as long
+     * as the gnutls_session_t is alive, so acquire a ref
+     */
+    qcrypto_tls_creds_box_ref(creds->box);
+    session->credsbox = creds->box;
 
-        if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
-            /* This requests, but does not enforce a client cert.
-             * The cert checking code later does enforcement */
-            gnutls_certificate_server_set_request(session->handle,
-                                                  GNUTLS_CERT_REQUEST);
-        }
-    } else {
-        error_setg(errp, "Unsupported TLS credentials type %s",
-                   object_get_typename(OBJECT(creds)));
-        goto error;
+    if (object_dynamic_cast(OBJECT(creds),
+                            TYPE_QCRYPTO_TLS_CREDS_X509) &&
+        creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+        /*
+         * This requests, but does not enforce a client cert.
+         * The cert checking code later does enforcement
+         */
+        gnutls_certificate_server_set_request(session->handle,
+                                              GNUTLS_CERT_REQUEST);
     }
 
     gnutls_transport_set_ptr(session->handle, session);
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 15/21] crypto: make TLS credentials structs private
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
                   ` (13 preceding siblings ...)
  2025-10-30 14:49 ` [PATCH 14/21] crypto: fix lifecycle handling of gnutls credentials objects Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-10-31 11:25   ` Marc-André Lureau
  2025-10-30 14:49 ` [PATCH 16/21] crypto: deprecate use of external dh-params.pem file Daniel P. Berrangé
                   ` (5 subsequent siblings)
  20 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

Now that the TLS session code no longer needs to look at the TLS
credential structs, they can be made private.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscredsanon.c |  3 +++
 crypto/tlscredspriv.h | 15 ---------------
 crypto/tlscredspsk.c  |  5 +++++
 crypto/tlscredsx509.c |  6 ++++++
 4 files changed, 14 insertions(+), 15 deletions(-)

diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c
index 0a728ccbf6..69ed1d792a 100644
--- a/crypto/tlscredsanon.c
+++ b/crypto/tlscredsanon.c
@@ -31,6 +31,9 @@
 
 #include <gnutls/gnutls.h>
 
+struct QCryptoTLSCredsAnon {
+    QCryptoTLSCreds parent_obj;
+};
 
 static int
 qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
diff --git a/crypto/tlscredspriv.h b/crypto/tlscredspriv.h
index 4e6dffa22f..69dac02437 100644
--- a/crypto/tlscredspriv.h
+++ b/crypto/tlscredspriv.h
@@ -37,21 +37,6 @@ struct QCryptoTLSCreds {
     QCryptoTLSCredsBox *box;
 };
 
-struct QCryptoTLSCredsAnon {
-    QCryptoTLSCreds parent_obj;
-};
-
-struct QCryptoTLSCredsPSK {
-    QCryptoTLSCreds parent_obj;
-    char *username;
-};
-
-struct QCryptoTLSCredsX509 {
-    QCryptoTLSCreds parent_obj;
-    bool sanityCheck;
-    char *passwordid;
-};
-
 #ifdef CONFIG_GNUTLS
 
 int qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c
index 5568f1ad0c..e437985260 100644
--- a/crypto/tlscredspsk.c
+++ b/crypto/tlscredspsk.c
@@ -31,6 +31,11 @@
 
 #include <gnutls/gnutls.h>
 
+struct QCryptoTLSCredsPSK {
+    QCryptoTLSCreds parent_obj;
+    char *username;
+};
+
 static int
 lookup_key(const char *pskfile, const char *username, gnutls_datum_t *key,
            Error **errp)
diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index ef31ea664c..2fc0872627 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -33,6 +33,12 @@
 #include <gnutls/gnutls.h>
 #include <gnutls/x509.h>
 
+struct QCryptoTLSCredsX509 {
+    QCryptoTLSCreds parent_obj;
+    bool sanityCheck;
+    char *passwordid;
+};
+
 
 static int
 qcrypto_tls_creds_check_cert_times(gnutls_x509_crt_t cert,
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 16/21] crypto: deprecate use of external dh-params.pem file
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
                   ` (14 preceding siblings ...)
  2025-10-30 14:49 ` [PATCH 15/21] crypto: make TLS credentials structs private Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-10-31 11:32   ` Marc-André Lureau
  2025-10-30 14:49 ` [PATCH 17/21] crypto: avoid loading the CA certs twice Daniel P. Berrangé
                   ` (4 subsequent siblings)
  20 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

GNUTLS has deprecated use of externally provided diffie-hellman
parameters, since it will automatically negotiate DH params in
accordance with RFC7919.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscreds.c         | 24 ++++++++----------------
 crypto/tlscredsanon.c     |  6 ++++--
 crypto/tlscredspsk.c      |  6 ++++--
 crypto/tlscredsx509.c     |  4 +++-
 docs/about/deprecated.rst |  9 +++++++++
 docs/system/tls.rst       | 12 +++++++-----
 6 files changed, 35 insertions(+), 26 deletions(-)

diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
index 798c9712fb..85268f3b57 100644
--- a/crypto/tlscreds.c
+++ b/crypto/tlscreds.c
@@ -22,6 +22,7 @@
 #include "qapi/error.h"
 #include "qapi-types-crypto.h"
 #include "qemu/module.h"
+#include "qemu/error-report.h"
 #include "tlscredspriv.h"
 #include "trace.h"
 
@@ -38,22 +39,7 @@ qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds,
 
     trace_qcrypto_tls_creds_load_dh(creds, filename ? filename : "<generated>");
 
-    if (filename == NULL) {
-        ret = gnutls_dh_params_init(dh_params);
-        if (ret < 0) {
-            error_setg(errp, "Unable to initialize DH parameters: %s",
-                       gnutls_strerror(ret));
-            return -1;
-        }
-        ret = gnutls_dh_params_generate2(*dh_params, DH_BITS);
-        if (ret < 0) {
-            gnutls_dh_params_deinit(*dh_params);
-            *dh_params = NULL;
-            error_setg(errp, "Unable to generate DH parameters: %s",
-                       gnutls_strerror(ret));
-            return -1;
-        }
-    } else {
+    if (filename != NULL) {
         GError *gerr = NULL;
         gchar *contents;
         gsize len;
@@ -67,6 +53,10 @@ qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds,
             g_error_free(gerr);
             return -1;
         }
+        warn_report_once("Use of an external DH parameters file '%s' is "
+                         "deprecated and will be removed in a future release",
+                         filename);
+
         data.data = (unsigned char *)contents;
         data.size = len;
         ret = gnutls_dh_params_init(dh_params);
@@ -87,6 +77,8 @@ qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds,
                        filename, gnutls_strerror(ret));
             return -1;
         }
+    } else {
+        *dh_params = NULL;
     }
 
     return 0;
diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c
index 69ed1d792a..777cc4f5bb 100644
--- a/crypto/tlscredsanon.c
+++ b/crypto/tlscredsanon.c
@@ -68,8 +68,10 @@ qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
             return -1;
         }
 
-        gnutls_anon_set_server_dh_params(box->data.anonserver,
-                                         box->dh_params);
+        if (box->dh_params) {
+            gnutls_anon_set_server_dh_params(box->data.anonserver,
+                                             box->dh_params);
+        }
     } else {
         ret = gnutls_anon_allocate_client_credentials(&box->data.anonclient);
         if (ret < 0) {
diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c
index e437985260..801da50625 100644
--- a/crypto/tlscredspsk.c
+++ b/crypto/tlscredspsk.c
@@ -129,8 +129,10 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
                        gnutls_strerror(ret));
             goto cleanup;
         }
-        gnutls_psk_set_server_dh_params(box->data.pskserver,
-                                        box->dh_params);
+        if (box->dh_params) {
+            gnutls_psk_set_server_dh_params(box->data.pskserver,
+                                            box->dh_params);
+        }
     } else {
         box = qcrypto_tls_creds_box_new_client(GNUTLS_CRD_PSK);
 
diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index 2fc0872627..7e79af4266 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -684,7 +684,9 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
                                                  errp) < 0) {
             return -1;
         }
-        gnutls_certificate_set_dh_params(box->data.cert, box->dh_params);
+        if (box->dh_params) {
+            gnutls_certificate_set_dh_params(box->data.cert, box->dh_params);
+        }
     }
     creds->parent_obj.box = g_steal_pointer(&box);
 
diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst
index ca6b3769b5..694a69da64 100644
--- a/docs/about/deprecated.rst
+++ b/docs/about/deprecated.rst
@@ -365,6 +365,15 @@ Options are:
     - move backing file to NVDIMM storage and keep ``pmem=on``
       (to have NVDIMM with persistence guaranties).
 
+Using an external DH (Diffie-Hellman) parameters file (since 10.2)
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Loading of external Diffie-Hellman parameters from a 'dh-params.pem'
+file is deprecated and will be removed with no replacement in a
+future release. Where no 'dh-params.pem' file is provided, the DH
+parameters will be automatically negotiated in accordance with
+RFC7919.
+
 Device options
 --------------
 
diff --git a/docs/system/tls.rst b/docs/system/tls.rst
index a4f6781d62..44c4bf04e9 100644
--- a/docs/system/tls.rst
+++ b/docs/system/tls.rst
@@ -251,11 +251,13 @@ When specifying the object, the ``dir`` parameters specifies which
 directory contains the credential files. This directory is expected to
 contain files with the names mentioned previously, ``ca-cert.pem``,
 ``server-key.pem``, ``server-cert.pem``, ``client-key.pem`` and
-``client-cert.pem`` as appropriate. It is also possible to include a set
-of pre-generated Diffie-Hellman (DH) parameters in a file
-``dh-params.pem``, which can be created using the
-``certtool --generate-dh-params`` command. If omitted, QEMU will
-dynamically generate DH parameters when loading the credentials.
+``client-cert.pem`` as appropriate.
+
+While it is possible to include a set of pre-generated Diffie-Hellman
+(DH) parameters in a file ``dh-params.pem``, this facility is now
+deprecated and will be removed in a future release. When omitted the
+DH parameters will be automatically negotiated in accordance with
+RFC7919.
 
 The ``endpoint`` parameter indicates whether the credentials will be
 used for a network client or server, and determines which PEM files are
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 17/21] crypto: avoid loading the CA certs twice
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
                   ` (15 preceding siblings ...)
  2025-10-30 14:49 ` [PATCH 16/21] crypto: deprecate use of external dh-params.pem file Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-10-31 15:08   ` Marc-André Lureau
  2025-10-30 14:49 ` [PATCH 18/21] crypto: avoid loading the identity " Daniel P. Berrangé
                   ` (3 subsequent siblings)
  20 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

The x509 TLS credentials code will load the CA certs once to perform
sanity chcking on the certs, then discard the certificate objects
and let gnutls load them a second time.

This introduces a new QCryptoTLSCredsX509Files struct which will
hold the CA certificates loaded for sanity checking and pass them on
to gnutls, avoiding the duplicated loading.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscredsx509.c | 141 ++++++++++++++++++++++++++----------------
 1 file changed, 87 insertions(+), 54 deletions(-)

diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index 7e79af4266..6a830af50d 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -40,6 +40,35 @@ struct QCryptoTLSCredsX509 {
 };
 
 
+typedef struct QCryptoTLSCredsX509Files QCryptoTLSCredsX509Files;
+struct QCryptoTLSCredsX509Files {
+    char *cacertpath;
+    gnutls_x509_crt_t *cacerts;
+    unsigned int ncacerts;
+};
+
+static QCryptoTLSCredsX509Files *
+qcrypto_tls_creds_x509_files_new(void)
+{
+    return g_new0(QCryptoTLSCredsX509Files, 1);
+}
+
+
+static void
+qcrypto_tls_creds_x509_files_free(QCryptoTLSCredsX509Files *files)
+{
+    size_t i;
+    for (i = 0; i < files->ncacerts; i++) {
+        gnutls_x509_crt_deinit(files->cacerts[i]);
+    }
+    g_free(files->cacerts);
+    g_free(files->cacertpath);
+    g_free(files);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSCredsX509Files,
+                              qcrypto_tls_creds_x509_files_free);
+
 static int
 qcrypto_tls_creds_check_cert_times(gnutls_x509_crt_t cert,
                                    const char *certFile,
@@ -315,11 +344,9 @@ qcrypto_tls_creds_check_cert(QCryptoTLSCredsX509 *creds,
 
 static int
 qcrypto_tls_creds_check_authority_chain(QCryptoTLSCredsX509 *creds,
+                                        QCryptoTLSCredsX509Files *files,
                                         gnutls_x509_crt_t *certs,
                                         unsigned int ncerts,
-                                        gnutls_x509_crt_t *cacerts,
-                                        unsigned int ncacerts,
-                                        const char *cacertFile,
                                         bool isServer,
                                         Error **errp)
 {
@@ -360,13 +387,13 @@ qcrypto_tls_creds_check_authority_chain(QCryptoTLSCredsX509 *creds,
              * reached the root of trust.
              */
             return qcrypto_tls_creds_check_cert(
-                creds, cert_to_check, cacertFile,
+                creds, cert_to_check, files->cacertpath,
                 isServer, true, errp);
         }
-        for (int i = 0; i < ncacerts; i++) {
+        for (int i = 0; i < files->ncacerts; i++) {
             if (gnutls_x509_crt_check_issuer(cert_to_check,
-                                             cacerts[i])) {
-                cert_issuer = cacerts[i];
+                                             files->cacerts[i])) {
+                cert_issuer = files->cacerts[i];
                 break;
             }
         }
@@ -374,7 +401,7 @@ qcrypto_tls_creds_check_authority_chain(QCryptoTLSCredsX509 *creds,
             break;
         }
 
-        if (qcrypto_tls_creds_check_cert(creds, cert_issuer, cacertFile,
+        if (qcrypto_tls_creds_check_cert(creds, cert_issuer, files->cacertpath,
                                          isServer, true, errp) < 0) {
             return -1;
         }
@@ -394,19 +421,17 @@ qcrypto_tls_creds_check_authority_chain(QCryptoTLSCredsX509 *creds,
 }
 
 static int
-qcrypto_tls_creds_check_cert_pair(gnutls_x509_crt_t *certs,
+qcrypto_tls_creds_check_cert_pair(QCryptoTLSCredsX509Files *files,
+                                  gnutls_x509_crt_t *certs,
                                   size_t ncerts,
                                   const char *certFile,
-                                  gnutls_x509_crt_t *cacerts,
-                                  size_t ncacerts,
-                                  const char *cacertFile,
                                   bool isServer,
                                   Error **errp)
 {
     unsigned int status;
 
     if (gnutls_x509_crt_list_verify(certs, ncerts,
-                                    cacerts, ncacerts,
+                                    files->cacerts, files->ncacerts,
                                     NULL, 0,
                                     0, &status) < 0) {
         error_setg(errp, isServer ?
@@ -414,7 +439,7 @@ qcrypto_tls_creds_check_cert_pair(gnutls_x509_crt_t *certs,
                    "CA certificate %s" :
                    "Unable to verify client certificate %s against "
                    "CA certificate %s",
-                   certFile, cacertFile);
+                   certFile, files->cacertpath);
         return -1;
     }
 
@@ -439,7 +464,7 @@ qcrypto_tls_creds_check_cert_pair(gnutls_x509_crt_t *certs,
 
         error_setg(errp,
                    "Our own certificate %s failed validation against %s: %s",
-                   certFile, cacertFile, reason);
+                   certFile, files->cacertpath, reason);
         return -1;
     }
 
@@ -490,15 +515,13 @@ qcrypto_tls_creds_load_cert_list(QCryptoTLSCredsX509 *creds,
 
 static int
 qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
+                                    QCryptoTLSCredsX509Files *files,
                                     bool isServer,
-                                    const char *cacertFile,
                                     const char *certFile,
                                     Error **errp)
 {
     gnutls_x509_crt_t *certs = NULL;
     unsigned int ncerts = 0;
-    gnutls_x509_crt_t *cacerts = NULL;
-    unsigned int ncacerts = 0;
     size_t i;
     int ret = -1;
 
@@ -514,16 +537,6 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
         }
     }
 
-    if (qcrypto_tls_creds_load_cert_list(creds,
-                                         cacertFile,
-                                         &cacerts,
-                                         &ncacerts,
-                                         isServer,
-                                         true,
-                                         errp) < 0) {
-        goto cleanup;
-    }
-
     for (i = 0; i < ncerts; i++) {
         if (qcrypto_tls_creds_check_cert(creds,
                                          certs[i], certFile,
@@ -533,17 +546,14 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
     }
 
     if (ncerts &&
-        qcrypto_tls_creds_check_authority_chain(creds,
+        qcrypto_tls_creds_check_authority_chain(creds, files,
                                                 certs, ncerts,
-                                                cacerts, ncacerts,
-                                                cacertFile, isServer,
-                                                errp) < 0) {
+                                                isServer, errp) < 0) {
         goto cleanup;
     }
 
-    if (ncerts && ncacerts &&
-        qcrypto_tls_creds_check_cert_pair(certs, ncerts, certFile,
-                                          cacerts, ncacerts, cacertFile,
+    if (ncerts &&
+        qcrypto_tls_creds_check_cert_pair(files, certs, ncerts, certFile,
                                           isServer, errp) < 0) {
         goto cleanup;
     }
@@ -555,21 +565,53 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
         gnutls_x509_crt_deinit(certs[i]);
     }
     g_free(certs);
-    for (i = 0; i < ncacerts; i++) {
-        gnutls_x509_crt_deinit(cacerts[i]);
-    }
-    g_free(cacerts);
 
     return ret;
 }
 
 
+static int
+qcrypto_tls_creds_x509_load_ca(QCryptoTLSCredsX509 *creds,
+                               QCryptoTLSCredsBox *box,
+                               QCryptoTLSCredsX509Files *files,
+                               bool isServer,
+                               Error **errp)
+{
+    int ret;
+
+    if (qcrypto_tls_creds_get_path(&creds->parent_obj,
+                                   QCRYPTO_TLS_CREDS_X509_CA_CERT,
+                                   true, &files->cacertpath, errp) < 0) {
+        return -1;
+    }
+
+    if (qcrypto_tls_creds_load_cert_list(creds,
+                                         files->cacertpath,
+                                         &files->cacerts,
+                                         &files->ncacerts,
+                                         isServer,
+                                         true,
+                                         errp) < 0) {
+        return -1;
+    }
+
+    ret = gnutls_certificate_set_x509_trust(box->data.cert,
+                                            files->cacerts, files->ncacerts);
+    if (ret < 0) {
+        error_setg(errp, "Cannot set CA certificate '%s': %s",
+                   files->cacertpath, gnutls_strerror(ret));
+        return -1;
+    }
+
+    return 0;
+}
+
 static int
 qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
                             Error **errp)
 {
     g_autoptr(QCryptoTLSCredsBox) box = NULL;
-    g_autofree char *cacert = NULL;
+    g_autoptr(QCryptoTLSCredsX509Files) files = NULL;
     g_autofree char *cacrl = NULL;
     g_autofree char *cert = NULL;
     g_autofree char *key = NULL;
@@ -598,9 +640,9 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
         return -1;
     }
 
-    if (qcrypto_tls_creds_get_path(&creds->parent_obj,
-                                   QCRYPTO_TLS_CREDS_X509_CA_CERT,
-                                   true, &cacert, errp) < 0) {
+    files = qcrypto_tls_creds_x509_files_new();
+
+    if (qcrypto_tls_creds_x509_load_ca(creds, box, files, isServer, errp) < 0) {
         return -1;
     }
 
@@ -631,17 +673,8 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
     }
 
     if (creds->sanityCheck &&
-        qcrypto_tls_creds_x509_sanity_check(creds, isServer,
-                                            cacert, cert, errp) < 0) {
-        return -1;
-    }
-
-    ret = gnutls_certificate_set_x509_trust_file(box->data.cert,
-                                                 cacert,
-                                                 GNUTLS_X509_FMT_PEM);
-    if (ret < 0) {
-        error_setg(errp, "Cannot load CA certificate '%s': %s",
-                   cacert, gnutls_strerror(ret));
+        qcrypto_tls_creds_x509_sanity_check(creds, files, isServer,
+                                            cert, errp) < 0) {
         return -1;
     }
 
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 18/21] crypto: avoid loading the identity certs twice
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
                   ` (16 preceding siblings ...)
  2025-10-30 14:49 ` [PATCH 17/21] crypto: avoid loading the CA certs twice Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-11-01  9:25   ` Marc-André Lureau
  2025-10-30 14:49 ` [PATCH 19/21] crypto: expand logic to cope with multiple certificate identities Daniel P. Berrangé
                   ` (2 subsequent siblings)
  20 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

The x509 TLS credentials code will load the identity certs once to
perform sanity chcking on the certs, then discard the certificate
objects and let gnutls load them a second time.

This extends the previous QCryptoTLSCredsX509Files struct to also
hold the identity certificates & key loaded for sanity checking
and pass them on to gnutls, avoiding the duplicated loading.

The unit tests need updating because we now correctly diagnose the
error scenario where the cert PEM file exists, without its matching
key PEM file. Previously that error was mistakenly ignored.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscredsx509.c                 | 247 +++++++++++++++++---------
 tests/unit/test-crypto-tlscredsx509.c |   8 +-
 2 files changed, 164 insertions(+), 91 deletions(-)

diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index 6a830af50d..3cb0a6c31f 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -45,6 +45,12 @@ struct QCryptoTLSCredsX509Files {
     char *cacertpath;
     gnutls_x509_crt_t *cacerts;
     unsigned int ncacerts;
+
+    char *certpath;
+    char *keypath;
+    gnutls_x509_crt_t *certs;
+    unsigned int ncerts;
+    gnutls_x509_privkey_t key;
 };
 
 static QCryptoTLSCredsX509Files *
@@ -63,6 +69,13 @@ qcrypto_tls_creds_x509_files_free(QCryptoTLSCredsX509Files *files)
     }
     g_free(files->cacerts);
     g_free(files->cacertpath);
+    for (i = 0; i < files->ncerts; i++) {
+        gnutls_x509_crt_deinit(files->certs[i]);
+    }
+    gnutls_x509_privkey_deinit(files->key);
+    g_free(files->certs);
+    g_free(files->certpath);
+    g_free(files->keypath);
     g_free(files);
 }
 
@@ -477,14 +490,13 @@ qcrypto_tls_creds_load_cert_list(QCryptoTLSCredsX509 *creds,
                                  const char *certFile,
                                  gnutls_x509_crt_t **certs,
                                  unsigned int *ncerts,
-                                 bool isServer,
-                                 bool isCA,
                                  Error **errp)
 {
     gnutls_datum_t data;
     g_autofree char *buf = NULL;
     gsize buflen;
     GError *gerr = NULL;
+    int ret;
 
     *ncerts = 0;
     trace_qcrypto_tls_creds_x509_load_cert_list(creds, certFile);
@@ -499,13 +511,60 @@ qcrypto_tls_creds_load_cert_list(QCryptoTLSCredsX509 *creds,
     data.data = (unsigned char *)buf;
     data.size = strlen(buf);
 
-    if (gnutls_x509_crt_list_import2(certs, ncerts, &data,
-                                     GNUTLS_X509_FMT_PEM, 0) < 0) {
-        error_setg(errp,
-                   isCA ? "Unable to import CA certificate list %s" :
-                   (isServer ? "Unable to import server certificate %s" :
-                    "Unable to import client certificate %s"),
-                   certFile);
+    ret = gnutls_x509_crt_list_import2(certs, ncerts, &data,
+                                       GNUTLS_X509_FMT_PEM, 0);
+    if (ret < 0) {
+        error_setg(errp, "Unable to import certificate %s: %s",
+                   certFile, gnutls_strerror(ret));
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int
+qcrypto_tls_creds_load_privkey(QCryptoTLSCredsX509 *creds,
+                               const char *keyFile,
+                               gnutls_x509_privkey_t *key,
+                               Error **errp)
+{
+    gnutls_datum_t data;
+    g_autofree char *buf = NULL;
+    g_autofree char *password = NULL;
+    gsize buflen;
+    GError *gerr = NULL;
+    int ret;
+
+    ret = gnutls_x509_privkey_init(key);
+    if (ret < 0) {
+        error_setg(errp, "Unable to initialize private key: %s",
+                   gnutls_strerror(ret));
+        return -1;
+    }
+
+    if (!g_file_get_contents(keyFile, &buf, &buflen, &gerr)) {
+        error_setg(errp, "Cannot load private key %s: %s",
+                   keyFile, gerr->message);
+        g_error_free(gerr);
+        return -1;
+    }
+
+    data.data = (unsigned char *)buf;
+    data.size = strlen(buf);
+
+    if (creds->passwordid) {
+        password = qcrypto_secret_lookup_as_utf8(creds->passwordid,
+                                                 errp);
+        if (!password) {
+            return -1;
+        }
+    }
+
+    if (gnutls_x509_privkey_import2(*key, &data,
+                                    GNUTLS_X509_FMT_PEM,
+                                    password, 0) < 0) {
+        error_setg(errp, "Unable to import private key %s", keyFile);
         return -1;
     }
 
@@ -517,56 +576,34 @@ static int
 qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
                                     QCryptoTLSCredsX509Files *files,
                                     bool isServer,
-                                    const char *certFile,
                                     Error **errp)
 {
-    gnutls_x509_crt_t *certs = NULL;
-    unsigned int ncerts = 0;
     size_t i;
-    int ret = -1;
-
-    if (certFile) {
-        if (qcrypto_tls_creds_load_cert_list(creds,
-                                             certFile,
-                                             &certs,
-                                             &ncerts,
-                                             isServer,
-                                             false,
-                                             errp) < 0) {
-            goto cleanup;
-        }
-    }
 
-    for (i = 0; i < ncerts; i++) {
+    for (i = 0; i < files->ncerts; i++) {
         if (qcrypto_tls_creds_check_cert(creds,
-                                         certs[i], certFile,
+                                         files->certs[i], files->certpath,
                                          isServer, i != 0, errp) < 0) {
-            goto cleanup;
+            return -1;
         }
     }
 
-    if (ncerts &&
+    if (files->ncerts &&
         qcrypto_tls_creds_check_authority_chain(creds, files,
-                                                certs, ncerts,
+                                                files->certs, files->ncerts,
                                                 isServer, errp) < 0) {
-        goto cleanup;
-    }
-
-    if (ncerts &&
-        qcrypto_tls_creds_check_cert_pair(files, certs, ncerts, certFile,
-                                          isServer, errp) < 0) {
-        goto cleanup;
+        return -1;
     }
 
-    ret = 0;
-
- cleanup:
-    for (i = 0; i < ncerts; i++) {
-        gnutls_x509_crt_deinit(certs[i]);
+    if (files->ncerts &&
+        qcrypto_tls_creds_check_cert_pair(files,
+                                          files->certs, files->ncerts,
+                                          files->certpath, isServer,
+                                          errp) < 0) {
+        return -1;
     }
-    g_free(certs);
 
-    return ret;
+    return 0;
 }
 
 
@@ -589,8 +626,6 @@ qcrypto_tls_creds_x509_load_ca(QCryptoTLSCredsX509 *creds,
                                          files->cacertpath,
                                          &files->cacerts,
                                          &files->ncacerts,
-                                         isServer,
-                                         true,
                                          errp) < 0) {
         return -1;
     }
@@ -606,6 +641,79 @@ qcrypto_tls_creds_x509_load_ca(QCryptoTLSCredsX509 *creds,
     return 0;
 }
 
+
+static int
+qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds,
+                                     QCryptoTLSCredsBox *box,
+                                     QCryptoTLSCredsX509Files *files,
+                                     bool isServer,
+                                     Error **errp)
+{
+    int ret;
+
+    if (isServer) {
+        if (qcrypto_tls_creds_get_path(&creds->parent_obj,
+                                       QCRYPTO_TLS_CREDS_X509_SERVER_CERT,
+                                       true, &files->certpath, errp) < 0 ||
+            qcrypto_tls_creds_get_path(&creds->parent_obj,
+                                       QCRYPTO_TLS_CREDS_X509_SERVER_KEY,
+                                       true, &files->keypath, errp) < 0) {
+            return -1;
+        }
+    } else {
+        if (qcrypto_tls_creds_get_path(&creds->parent_obj,
+                                       QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
+                                       false, &files->certpath, errp) < 0 ||
+            qcrypto_tls_creds_get_path(&creds->parent_obj,
+                                       QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
+                                       false, &files->keypath, errp) < 0) {
+            return -1;
+        }
+    }
+
+    if (!files->certpath &&
+        !files->keypath) {
+        return 0;
+    }
+    if (files->certpath && !files->keypath) {
+        error_setg(errp, "Cert '%s' without corresponding key",
+                   files->certpath);
+        return -1;
+    }
+    if (!files->certpath && files->keypath) {
+        error_setg(errp, "Key '%s' without corresponding cert",
+                   files->keypath);
+        return -1;
+    }
+
+    if (qcrypto_tls_creds_load_cert_list(creds,
+                                         files->certpath,
+                                         &files->certs,
+                                         &files->ncerts,
+                                         errp) < 0) {
+        return -1;
+    }
+
+    if (qcrypto_tls_creds_load_privkey(creds,
+                                       files->keypath,
+                                       &files->key,
+                                       errp) < 0) {
+        return -1;
+    }
+
+    ret = gnutls_certificate_set_x509_key(box->data.cert,
+                                          files->certs,
+                                          files->ncerts,
+                                          files->key);
+    if (ret < 0) {
+        error_setg(errp, "Cannot set certificate '%s' & key '%s': %s",
+                   files->certpath, files->keypath, gnutls_strerror(ret));
+        return -1;
+    }
+    return 0;
+}
+
+
 static int
 qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
                             Error **errp)
@@ -613,8 +721,6 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
     g_autoptr(QCryptoTLSCredsBox) box = NULL;
     g_autoptr(QCryptoTLSCredsX509Files) files = NULL;
     g_autofree char *cacrl = NULL;
-    g_autofree char *cert = NULL;
-    g_autofree char *key = NULL;
     g_autofree char *dhparams = NULL;
     bool isServer = (creds->parent_obj.endpoint ==
                      QCRYPTO_TLS_CREDS_ENDPOINT_SERVER);
@@ -646,60 +752,27 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
         return -1;
     }
 
+    if (qcrypto_tls_creds_x509_load_identity(creds, box, files,
+                                             isServer, errp) < 0) {
+        return -1;
+    }
+
     if (isServer) {
         if (qcrypto_tls_creds_get_path(&creds->parent_obj,
                                        QCRYPTO_TLS_CREDS_X509_CA_CRL,
                                        false, &cacrl, errp) < 0 ||
-            qcrypto_tls_creds_get_path(&creds->parent_obj,
-                                       QCRYPTO_TLS_CREDS_X509_SERVER_CERT,
-                                       true, &cert, errp) < 0 ||
-            qcrypto_tls_creds_get_path(&creds->parent_obj,
-                                       QCRYPTO_TLS_CREDS_X509_SERVER_KEY,
-                                       true, &key, errp) < 0 ||
             qcrypto_tls_creds_get_path(&creds->parent_obj,
                                        QCRYPTO_TLS_CREDS_DH_PARAMS,
                                        false, &dhparams, errp) < 0) {
             return -1;
         }
-    } else {
-        if (qcrypto_tls_creds_get_path(&creds->parent_obj,
-                                       QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
-                                       false, &cert, errp) < 0 ||
-            qcrypto_tls_creds_get_path(&creds->parent_obj,
-                                       QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
-                                       false, &key, errp) < 0) {
-            return -1;
-        }
     }
 
     if (creds->sanityCheck &&
-        qcrypto_tls_creds_x509_sanity_check(creds, files, isServer,
-                                            cert, errp) < 0) {
+        qcrypto_tls_creds_x509_sanity_check(creds, files, isServer, errp) < 0) {
         return -1;
     }
 
-    if (cert != NULL && key != NULL) {
-        char *password = NULL;
-        if (creds->passwordid) {
-            password = qcrypto_secret_lookup_as_utf8(creds->passwordid,
-                                                     errp);
-            if (!password) {
-                return -1;
-            }
-        }
-        ret = gnutls_certificate_set_x509_key_file2(box->data.cert,
-                                                    cert, key,
-                                                    GNUTLS_X509_FMT_PEM,
-                                                    password,
-                                                    0);
-        g_free(password);
-        if (ret < 0) {
-            error_setg(errp, "Cannot load certificate '%s' & key '%s': %s",
-                       cert, key, gnutls_strerror(ret));
-            return -1;
-        }
-    }
-
     if (cacrl != NULL) {
         ret = gnutls_certificate_set_x509_crl_file(box->data.cert,
                                                    cacrl,
diff --git a/tests/unit/test-crypto-tlscredsx509.c b/tests/unit/test-crypto-tlscredsx509.c
index a5f21728d4..b1ad7d5c0d 100644
--- a/tests/unit/test-crypto-tlscredsx509.c
+++ b/tests/unit/test-crypto-tlscredsx509.c
@@ -95,16 +95,16 @@ static void test_tls_creds(const void *opaque)
         if (access(data->crt, R_OK) == 0) {
             g_assert(link(data->crt,
                           CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0);
+            g_assert(link(KEYFILE,
+                          CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0);
         }
-        g_assert(link(KEYFILE,
-                      CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0);
     } else {
         if (access(data->crt, R_OK) == 0) {
             g_assert(link(data->crt,
                           CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0);
+            g_assert(link(KEYFILE,
+                          CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0);
         }
-        g_assert(link(KEYFILE,
-                      CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0);
     }
 
     creds = test_tls_creds_create(
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 19/21] crypto: expand logic to cope with multiple certificate identities
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
                   ` (17 preceding siblings ...)
  2025-10-30 14:49 ` [PATCH 18/21] crypto: avoid loading the identity " Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-11-02  8:11   ` Marc-André Lureau
  2025-10-30 14:49 ` [PATCH 20/21] crypto: support upto 5 parallel " Daniel P. Berrangé
  2025-10-30 14:49 ` [PATCH 21/21] docs: creation of x509 certs compliant with post-quantum crypto Daniel P. Berrangé
  20 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

Currently only a single set of certificates can be loaded for a
server / client. Certificates are created using a particular
key algorithm and in some scenarios it can be useful to support
multiple algorithms in parallel. This requires the ability to
load multiple sets of certificates.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscredsx509.c | 164 ++++++++++++++++++++++++++++--------------
 1 file changed, 112 insertions(+), 52 deletions(-)

diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index 3cb0a6c31f..d7d1f594c0 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -39,6 +39,14 @@ struct QCryptoTLSCredsX509 {
     char *passwordid;
 };
 
+typedef struct QCryptoTLSCredsX509IdentFiles QCryptoTLSCredsX509IdentFiles;
+struct QCryptoTLSCredsX509IdentFiles {
+    char *certpath;
+    char *keypath;
+    gnutls_x509_crt_t *certs;
+    unsigned int ncerts;
+    gnutls_x509_privkey_t key;
+};
 
 typedef struct QCryptoTLSCredsX509Files QCryptoTLSCredsX509Files;
 struct QCryptoTLSCredsX509Files {
@@ -46,11 +54,8 @@ struct QCryptoTLSCredsX509Files {
     gnutls_x509_crt_t *cacerts;
     unsigned int ncacerts;
 
-    char *certpath;
-    char *keypath;
-    gnutls_x509_crt_t *certs;
-    unsigned int ncerts;
-    gnutls_x509_privkey_t key;
+    QCryptoTLSCredsX509IdentFiles **identities;
+    size_t nidentities;
 };
 
 static QCryptoTLSCredsX509Files *
@@ -61,14 +66,9 @@ qcrypto_tls_creds_x509_files_new(void)
 
 
 static void
-qcrypto_tls_creds_x509_files_free(QCryptoTLSCredsX509Files *files)
+qcrypto_tls_creds_x509_ident_files_free(QCryptoTLSCredsX509IdentFiles *files)
 {
     size_t i;
-    for (i = 0; i < files->ncacerts; i++) {
-        gnutls_x509_crt_deinit(files->cacerts[i]);
-    }
-    g_free(files->cacerts);
-    g_free(files->cacertpath);
     for (i = 0; i < files->ncerts; i++) {
         gnutls_x509_crt_deinit(files->certs[i]);
     }
@@ -79,6 +79,26 @@ qcrypto_tls_creds_x509_files_free(QCryptoTLSCredsX509Files *files)
     g_free(files);
 }
 
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSCredsX509IdentFiles,
+                              qcrypto_tls_creds_x509_ident_files_free);
+
+
+static void
+qcrypto_tls_creds_x509_files_free(QCryptoTLSCredsX509Files *files)
+{
+    size_t i;
+    for (i = 0; i < files->ncacerts; i++) {
+        gnutls_x509_crt_deinit(files->cacerts[i]);
+    }
+    g_free(files->cacerts);
+    g_free(files->cacertpath);
+    for (i = 0; i < files->nidentities; i++) {
+        qcrypto_tls_creds_x509_ident_files_free(files->identities[i]);
+    }
+    g_free(files->identities);
+    g_free(files);
+}
+
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSCredsX509Files,
                               qcrypto_tls_creds_x509_files_free);
 
@@ -573,33 +593,32 @@ qcrypto_tls_creds_load_privkey(QCryptoTLSCredsX509 *creds,
 
 
 static int
-qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
-                                    QCryptoTLSCredsX509Files *files,
-                                    bool isServer,
-                                    Error **errp)
+qcrypto_tls_creds_x509_sanity_check_identity(QCryptoTLSCredsX509 *creds,
+                                             QCryptoTLSCredsX509Files *files,
+                                             QCryptoTLSCredsX509IdentFiles *ifiles,
+                                             bool isServer,
+                                             Error **errp)
 {
     size_t i;
 
-    for (i = 0; i < files->ncerts; i++) {
+    for (i = 0; i < ifiles->ncerts; i++) {
         if (qcrypto_tls_creds_check_cert(creds,
-                                         files->certs[i], files->certpath,
+                                         ifiles->certs[i], ifiles->certpath,
                                          isServer, i != 0, errp) < 0) {
             return -1;
         }
     }
 
-    if (files->ncerts &&
+    if (ifiles->ncerts &&
         qcrypto_tls_creds_check_authority_chain(creds, files,
-                                                files->certs, files->ncerts,
+                                                ifiles->certs, ifiles->ncerts,
                                                 isServer, errp) < 0) {
         return -1;
     }
 
-    if (files->ncerts &&
-        qcrypto_tls_creds_check_cert_pair(files,
-                                          files->certs, files->ncerts,
-                                          files->certpath, isServer,
-                                          errp) < 0) {
+    if (ifiles->ncerts &&
+        qcrypto_tls_creds_check_cert_pair(files, ifiles->certs, ifiles->ncerts,
+                                          ifiles->certpath, isServer, errp) < 0) {
         return -1;
     }
 
@@ -607,6 +626,26 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
 }
 
 
+static int
+qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
+                                    QCryptoTLSCredsX509Files *files,
+                                    bool isServer,
+                                    Error **errp)
+{
+    size_t i;
+    for (i = 0; i < files->nidentities; i++) {
+        if (qcrypto_tls_creds_x509_sanity_check_identity(creds,
+                                                         files,
+                                                         files->identities[i],
+                                                         isServer,
+                                                         errp) < 0) {
+            return -1;
+        }
+    }
+    return 0;
+}
+
+
 static int
 qcrypto_tls_creds_x509_load_ca(QCryptoTLSCredsX509 *creds,
                                QCryptoTLSCredsBox *box,
@@ -642,48 +681,38 @@ qcrypto_tls_creds_x509_load_ca(QCryptoTLSCredsX509 *creds,
 }
 
 
-static int
+static QCryptoTLSCredsX509IdentFiles *
 qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds,
                                      QCryptoTLSCredsBox *box,
-                                     QCryptoTLSCredsX509Files *files,
-                                     bool isServer,
+                                     const char *certbase,
+                                     const char *keybase,
+                                     bool isOptional,
                                      Error **errp)
 {
+    g_autoptr(QCryptoTLSCredsX509IdentFiles) files =
+        g_new0(QCryptoTLSCredsX509IdentFiles, 1);
     int ret;
 
-    if (isServer) {
-        if (qcrypto_tls_creds_get_path(&creds->parent_obj,
-                                       QCRYPTO_TLS_CREDS_X509_SERVER_CERT,
-                                       true, &files->certpath, errp) < 0 ||
-            qcrypto_tls_creds_get_path(&creds->parent_obj,
-                                       QCRYPTO_TLS_CREDS_X509_SERVER_KEY,
-                                       true, &files->keypath, errp) < 0) {
-            return -1;
-        }
-    } else {
-        if (qcrypto_tls_creds_get_path(&creds->parent_obj,
-                                       QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
-                                       false, &files->certpath, errp) < 0 ||
-            qcrypto_tls_creds_get_path(&creds->parent_obj,
-                                       QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
-                                       false, &files->keypath, errp) < 0) {
-            return -1;
-        }
+    if (qcrypto_tls_creds_get_path(&creds->parent_obj, certbase,
+                                   !isOptional, &files->certpath, errp) < 0 ||
+        qcrypto_tls_creds_get_path(&creds->parent_obj, keybase,
+                                   !isOptional, &files->keypath, errp) < 0) {
+        return NULL;
     }
 
     if (!files->certpath &&
         !files->keypath) {
-        return 0;
+        return NULL;
     }
     if (files->certpath && !files->keypath) {
         error_setg(errp, "Cert '%s' without corresponding key",
                    files->certpath);
-        return -1;
+        return NULL;
     }
     if (!files->certpath && files->keypath) {
         error_setg(errp, "Key '%s' without corresponding cert",
                    files->keypath);
-        return -1;
+        return NULL;
     }
 
     if (qcrypto_tls_creds_load_cert_list(creds,
@@ -691,14 +720,14 @@ qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds,
                                          &files->certs,
                                          &files->ncerts,
                                          errp) < 0) {
-        return -1;
+        return NULL;
     }
 
     if (qcrypto_tls_creds_load_privkey(creds,
                                        files->keypath,
                                        &files->key,
                                        errp) < 0) {
-        return -1;
+        return NULL;
     }
 
     ret = gnutls_certificate_set_x509_key(box->data.cert,
@@ -708,8 +737,39 @@ qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds,
     if (ret < 0) {
         error_setg(errp, "Cannot set certificate '%s' & key '%s': %s",
                    files->certpath, files->keypath, gnutls_strerror(ret));
+        return NULL;
+    }
+    return g_steal_pointer(&files);
+}
+
+
+static int
+qcrypto_tls_creds_x509_load_identities(QCryptoTLSCredsX509 *creds,
+                                       QCryptoTLSCredsBox *box,
+                                       QCryptoTLSCredsX509Files *files,
+                                       bool isServer,
+                                       Error **errp)
+{
+    QCryptoTLSCredsX509IdentFiles *ifiles;
+
+    ifiles = qcrypto_tls_creds_x509_load_identity(
+        creds, box,
+        isServer ?
+        QCRYPTO_TLS_CREDS_X509_SERVER_CERT :
+        QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
+        isServer ?
+        QCRYPTO_TLS_CREDS_X509_SERVER_KEY :
+        QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
+        !isServer, errp);
+    if (!ifiles) {
         return -1;
     }
+
+    files->identities = g_renew(QCryptoTLSCredsX509IdentFiles *,
+                                files->identities,
+                                files->nidentities + 1);
+    files->identities[files->nidentities++] = ifiles;
+
     return 0;
 }
 
@@ -752,8 +812,8 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
         return -1;
     }
 
-    if (qcrypto_tls_creds_x509_load_identity(creds, box, files,
-                                             isServer, errp) < 0) {
+    if (qcrypto_tls_creds_x509_load_identities(creds, box, files,
+                                               isServer, errp) < 0) {
         return -1;
     }
 
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 20/21] crypto: support upto 5 parallel certificate identities
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
                   ` (18 preceding siblings ...)
  2025-10-30 14:49 ` [PATCH 19/21] crypto: expand logic to cope with multiple certificate identities Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-11-02  8:25   ` Marc-André Lureau
  2025-10-30 14:49 ` [PATCH 21/21] docs: creation of x509 certs compliant with post-quantum crypto Daniel P. Berrangé
  20 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

The default (required) identity is stored in server-cert.pem /
client-cert.pem and server-key.pem / client-key.pem.

The 4 extra (optional) identities are stored in server-cert-$N.pem /
client-cert-$N.pem and server-key-$N.pem / client-key-$N.pem. The
numbering starts at 0 and the first missing cert/key pair will
terminate the loading process.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 crypto/tlscreds.c             | 10 +++++-
 crypto/tlscredspriv.h         |  3 ++
 crypto/tlscredsx509.c         | 68 ++++++++++++++++++++++++++++-------
 crypto/tlssession.c           |  1 +
 crypto/trace-events           |  1 +
 docs/system/tls.rst           | 54 ++++++++++++++++++++++++++--
 include/crypto/tlscredsx509.h |  6 ++++
 7 files changed, 127 insertions(+), 16 deletions(-)

diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
index 85268f3b57..b7e77f6285 100644
--- a/crypto/tlscreds.c
+++ b/crypto/tlscreds.c
@@ -85,6 +85,14 @@ qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds,
 }
 
 
+char *
+qcrypto_tls_creds_build_path(QCryptoTLSCreds *creds,
+                             const char *filename)
+{
+    return g_strdup_printf("%s/%s", creds->dir, filename);
+}
+
+
 int
 qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
                            const char *filename,
@@ -94,7 +102,7 @@ qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
 {
     int ret = -1;
 
-    *cred = g_strdup_printf("%s/%s", creds->dir, filename);
+    *cred = qcrypto_tls_creds_build_path(creds, filename);
 
     if (access(*cred, R_OK) < 0) {
         if (errno == ENOENT && !required) {
diff --git a/crypto/tlscredspriv.h b/crypto/tlscredspriv.h
index 69dac02437..8f2d096c7f 100644
--- a/crypto/tlscredspriv.h
+++ b/crypto/tlscredspriv.h
@@ -39,6 +39,9 @@ struct QCryptoTLSCreds {
 
 #ifdef CONFIG_GNUTLS
 
+char *qcrypto_tls_creds_build_path(QCryptoTLSCreds *creds,
+                                   const char *filename);
+
 int qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
                                const char *filename,
                                bool required,
diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index d7d1f594c0..fa92431906 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -686,7 +686,6 @@ qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds,
                                      QCryptoTLSCredsBox *box,
                                      const char *certbase,
                                      const char *keybase,
-                                     bool isOptional,
                                      Error **errp)
 {
     g_autoptr(QCryptoTLSCredsX509IdentFiles) files =
@@ -694,9 +693,9 @@ qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds,
     int ret;
 
     if (qcrypto_tls_creds_get_path(&creds->parent_obj, certbase,
-                                   !isOptional, &files->certpath, errp) < 0 ||
+                                   false, &files->certpath, errp) < 0 ||
         qcrypto_tls_creds_get_path(&creds->parent_obj, keybase,
-                                   !isOptional, &files->keypath, errp) < 0) {
+                                   false, &files->keypath, errp) < 0) {
         return NULL;
     }
 
@@ -705,13 +704,17 @@ qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds,
         return NULL;
     }
     if (files->certpath && !files->keypath) {
-        error_setg(errp, "Cert '%s' without corresponding key",
-                   files->certpath);
+        g_autofree char *keypath =
+            qcrypto_tls_creds_build_path(&creds->parent_obj, keybase);
+        error_setg(errp, "Cert '%s' without corresponding key '%s'",
+                   files->certpath, keypath);
         return NULL;
     }
     if (!files->certpath && files->keypath) {
-        error_setg(errp, "Key '%s' without corresponding cert",
-                   files->keypath);
+        g_autofree char *certpath =
+            qcrypto_tls_creds_build_path(&creds->parent_obj, certbase);
+        error_setg(errp, "Key '%s' without corresponding cert '%s'",
+                   files->keypath, certpath);
         return NULL;
     }
 
@@ -750,7 +753,9 @@ qcrypto_tls_creds_x509_load_identities(QCryptoTLSCredsX509 *creds,
                                        bool isServer,
                                        Error **errp)
 {
+    ERRP_GUARD();
     QCryptoTLSCredsX509IdentFiles *ifiles;
+    size_t i;
 
     ifiles = qcrypto_tls_creds_x509_load_identity(
         creds, box,
@@ -760,15 +765,52 @@ qcrypto_tls_creds_x509_load_identities(QCryptoTLSCredsX509 *creds,
         isServer ?
         QCRYPTO_TLS_CREDS_X509_SERVER_KEY :
         QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
-        !isServer, errp);
-    if (!ifiles) {
+        errp);
+    if (!ifiles && *errp) {
         return -1;
     }
 
-    files->identities = g_renew(QCryptoTLSCredsX509IdentFiles *,
-                                files->identities,
-                                files->nidentities + 1);
-    files->identities[files->nidentities++] = ifiles;
+    if (ifiles) {
+        files->identities = g_renew(QCryptoTLSCredsX509IdentFiles *,
+                                    files->identities,
+                                    files->nidentities + 1);
+        files->identities[files->nidentities++] = ifiles;
+    }
+
+    for (i = 0; i < QCRYPTO_TLS_CREDS_X509_IDENTITY_MAX; i++) {
+        g_autofree char *cert = g_strdup_printf(
+            isServer ?
+            QCRYPTO_TLS_CREDS_X509_SERVER_CERT_N :
+            QCRYPTO_TLS_CREDS_X509_CLIENT_CERT_N, i);
+        g_autofree char *key = g_strdup_printf(
+            isServer ?
+            QCRYPTO_TLS_CREDS_X509_SERVER_KEY_N :
+            QCRYPTO_TLS_CREDS_X509_CLIENT_KEY_N, i);
+
+        ifiles = qcrypto_tls_creds_x509_load_identity(creds, box,
+                                                      cert, key, errp);
+        if (!ifiles && *errp) {
+            return -1;
+        }
+        if (!ifiles) {
+            break;
+        }
+
+        files->identities = g_renew(QCryptoTLSCredsX509IdentFiles *,
+                                    files->identities,
+                                    files->nidentities + 1);
+        files->identities[files->nidentities++] = ifiles;
+    }
+
+    if (files->nidentities == 0 && isServer) {
+        g_autofree char *certpath = qcrypto_tls_creds_build_path(
+            &creds->parent_obj, QCRYPTO_TLS_CREDS_X509_SERVER_CERT);
+        g_autofree char *keypath = qcrypto_tls_creds_build_path(
+            &creds->parent_obj, QCRYPTO_TLS_CREDS_X509_SERVER_KEY);
+        error_setg(errp, "Missing server cert '%s' & key '%s'",
+                   certpath, keypath);
+        return -1;
+    }
 
     return 0;
 }
diff --git a/crypto/tlssession.c b/crypto/tlssession.c
index a1dc3b3ce0..314e3e96ba 100644
--- a/crypto/tlssession.c
+++ b/crypto/tlssession.c
@@ -345,6 +345,7 @@ qcrypto_tls_session_check_certificate(QCryptoTLSSession *session,
                 goto error;
             }
             session->peername = (char *)g_steal_pointer(&dname.data);
+            trace_qcrypto_tls_session_check_x509_dn(session, session->peername);
             if (session->authzid) {
                 bool allow;
 
diff --git a/crypto/trace-events b/crypto/trace-events
index d0e33427fa..771f9b8a6e 100644
--- a/crypto/trace-events
+++ b/crypto/trace-events
@@ -21,6 +21,7 @@ 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_check_x509_dn(void *session, const char *dname) "TLS session check x509 distinguished name session=%p dname=%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"
 
diff --git a/docs/system/tls.rst b/docs/system/tls.rst
index 44c4bf04e9..7cec4ac3df 100644
--- a/docs/system/tls.rst
+++ b/docs/system/tls.rst
@@ -36,8 +36,58 @@ server and exposing it directly to remote browser clients. In such a
 case it might be useful to use a commercial CA to avoid needing to
 install custom CA certs in the web browsers.
 
-The recommendation is for the server to keep its certificates in either
-``/etc/pki/qemu`` or for unprivileged users in ``$HOME/.pki/qemu``.
+.. _tls_cert_file_naming:
+
+Certificate file naming
+~~~~~~~~~~~~~~~~~~~~~~~
+
+In a simple setup, where all QEMU instances on a machine share the
+same TLS configuration, it is suggested that QEMU certificates be
+kept in either ``/etc/pki/qemu`` or, for unprivileged users, in
+``$HOME/.pki/qemu``. Where different QEMU subsystems require
+different certificate configurations, sub-dirs of these locations
+may be chosen.
+
+The default file names that QEMU will traditionally load are:
+
+* ``ca-cert.pem`` - mandatory; for both client and server configurations
+* ``ca-crl.pem`` - optional; for server configurations only
+* ``server-cert.pem`` - mandatory; for server configurations only
+* ``server-key.pem`` - mandatory; for server configurations only
+* ``client-cert.pem`` - optional; for client configurations only
+* ``client-key.pem`` - optional; for client configurations only
+* ``dh-params.pem`` - optional; for server configurations only
+
+Since QEMU 10.2.0, there is support for loading upto four additional
+identities:
+
+* ``server-cert-[IDX].pem`` - optional; for server configurations only
+* ``server-key-[IDX].pem`` - optional; for server configurations only
+* ``client-cert-[IDX].pem`` - optional; for client configurations only
+* ``client-key-[IDX].pem`` - optional; for client configurations only
+
+where ``-[IDX]`` is one of the digits 0-3. Loading will terminate at
+the first absent index. The index based certificate files may be used
+as a replacement for, or in addition to, the traditional non-index
+based certificate files. The traditional certificate files will be
+loaded first, if present, then the index based certificates. Where
+multiple certificates are compatible with a TLS session, the first
+loaded certificate will preferred. IOW file naming can influence
+which certificates are used for a session.
+
+The use of multiple sets of certificates is intended to allow an
+incremental transition to certificates using different crytographic
+algorithms. This allows a newly deployed QEMU to introduce use of
+stronger cryptographic algorithms that will be preferred when talking
+to other newly deployed QEMU instances, while retaining compatbility
+with certificates issued to a historically deployed QEMU. This is
+notably useful to support live migration from an old QEMU deployed
+on older operating system releases, which may support fewer crypto
+algorithm choices than the current OS.
+
+The certificate creation commands below will be illustrated using
+the traditional naming scheme, but their args can be substituted
+to use the indexed naming in the obvious manner.
 
 .. _tls_005fgenerate_005fca:
 
diff --git a/include/crypto/tlscredsx509.h b/include/crypto/tlscredsx509.h
index c4daba21a6..61b7f73573 100644
--- a/include/crypto/tlscredsx509.h
+++ b/include/crypto/tlscredsx509.h
@@ -37,7 +37,13 @@ typedef struct QCryptoTLSCredsX509Class QCryptoTLSCredsX509Class;
 #define QCRYPTO_TLS_CREDS_X509_SERVER_CERT "server-cert.pem"
 #define QCRYPTO_TLS_CREDS_X509_CLIENT_KEY "client-key.pem"
 #define QCRYPTO_TLS_CREDS_X509_CLIENT_CERT "client-cert.pem"
+#define QCRYPTO_TLS_CREDS_X509_SERVER_KEY_N "server-key-%zu.pem"
+#define QCRYPTO_TLS_CREDS_X509_SERVER_CERT_N "server-cert-%zu.pem"
+#define QCRYPTO_TLS_CREDS_X509_CLIENT_KEY_N "client-key-%zu.pem"
+#define QCRYPTO_TLS_CREDS_X509_CLIENT_CERT_N "client-cert-%zu.pem"
 
+/* Max number of additional cert/key pairs (ie _N constants) */
+#define QCRYPTO_TLS_CREDS_X509_IDENTITY_MAX 4
 
 /**
  * QCryptoTLSCredsX509:
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 21/21] docs: creation of x509 certs compliant with post-quantum crypto
  2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
                   ` (19 preceding siblings ...)
  2025-10-30 14:49 ` [PATCH 20/21] crypto: support upto 5 parallel " Daniel P. Berrangé
@ 2025-10-30 14:49 ` Daniel P. Berrangé
  2025-11-02 11:40   ` Marc-André Lureau
  20 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-30 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Daniel P. Berrangé, devel

Explain how to alter the certtool commands for creating certficates,
so that they can use algorithms that are compliant with post-quantum
crytography standards.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 docs/system/tls.rst | 68 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 68 insertions(+)

diff --git a/docs/system/tls.rst b/docs/system/tls.rst
index 7cec4ac3df..03fa1d8166 100644
--- a/docs/system/tls.rst
+++ b/docs/system/tls.rst
@@ -345,6 +345,74 @@ example with VNC:
 
 .. _tls_005fpsk:
 
+TLS certificates for Post-Quantum Cryptography
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Given a new enough gnutls release, suitably integrated & configured with the
+operating system crypto policies, QEMU is able to support post-quantum
+crytography on TLS enabled services, either exclusively or in a hybrid mode.
+
+In exclusive mode, only a single set of certificates need to be configured
+for QEMU, with PQC compliant algorithms. Such a QEMU configuration will only
+be able to interoperate with other services (including other QEMU's) that
+also have PQC enabled. This can result in compatibility concerns during the
+period of transition over to PQC compliant algorithms.
+
+In hybrid mode, multiple sets of certificates need to be configured for QEMU,
+at least one set with traditional (non-PQC compliant) algorithms, and at least
+one other set with modern (PQC compliant) algorithms. At time of the TLS
+handshake, the GNUTLS algorithm priorities should ensure that PQC compliant
+algorithms are negotiated if both sides of the connection support PQC. If one
+side lacks PQC, the TLS handshake should fallback to the non-PQC algorithms.
+This can assist with interoperability during the transition to PQC, but has a
+potential weakness wrt downgrade attacks forcing use of non-PQC algorithms.
+Exclusive PQC mode should be preferred where both peers in the TLS connections
+are known to support PQC.
+
+Key generation parameters
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To create certificates with PQC compliant algorithms, the ``--key-type``
+argument must be passed to ``certtool`` when creating private keys. No
+extra arguments are required for the other ``certtool`` commands, as
+their behaviour will be determined by the private key type.
+
+The typical PQC compliant algorithms to use are ``ML-DSA-44``, ``ML-DSA-65``
+and ``ML-DSA-87``, with ``ML-DSA-65`` being a suitable default choice in
+the absence of explicit requirements.
+
+Taking the example earlier, for creating a key for a client certificate,
+to use ``ML-DSA-65`` the command line would be modified to look like::
+
+   # certtool --generate-privkey --key-type=mldsa65 > client-hostNNN-key.pem
+
+The equivalent modification applies to the creation of the private keys
+used for server certs, or root/intermediate CA certs.
+
+For hybrid mode, the additional indexed certificate naming must be used.
+If multiple configured certificates are compatible with the mutually
+supported crypto algorithms between the client and server, then the
+first matching certificate will be used.
+
+IOW, to ensure that PQC certificates are preferred, they must use a
+non-index based filename, or use an index that is smaller than any
+non-PQC certificates. ie, ``server-cert.pem`` for PQC and ``server-cert-0.pem``
+for non-PQC, or ``server-cert-0.pem`` for PQC and ``server-cert-1.pem`` for
+non-PQC.
+
+Force disabling PQC via crypto priority
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In the OS configuration for system crypto algorithm priorities has
+enabled PQC, this can (optionally) be overriden in QEMU configuration
+disable use of PQC using the ``priority`` parameter to the ``tls-creds-x509``
+object::
+
+  NO_MLDSA="-SIGN-ML-DSA-65:-SIGN-ML-DSA-44:-SIGN-ML-DSA-87"
+  NO_MLKEM="-GROUP-X25519-MLKEM768:-GROUP-SECP256R1-MLKEM768:-GROUP-SECP384R1-MLKEM1024"
+  # qemu-nbd --object tls-creds-x509,id=tls0,endpoint=server,dir=....,priority=@SYSTEM:$NO_MLDSA:$NO_MLKEM
+
+
 TLS Pre-Shared Keys (PSK)
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* Re: [PATCH 07/21] crypto: remove needless indirection via parent_obj field
  2025-10-30 14:49 ` [PATCH 07/21] crypto: remove needless indirection via parent_obj field Daniel P. Berrangé
@ 2025-10-30 19:31   ` Marc-André Lureau
  2025-10-31 14:00   ` Philippe Mathieu-Daudé
  1 sibling, 0 replies; 44+ messages in thread
From: Marc-André Lureau @ 2025-10-30 19:31 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, devel

[-- Attachment #1: Type: text/plain, Size: 1607 bytes --]

Hi

On Thu, Oct 30, 2025 at 6:49 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:

> The reload method already has a pointer to the parent object in
> the 'creds' parameter that is passed in, so indirect access via
> the subclass 'parent_obj' field is redundant.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> ---
>  crypto/tlscredsx509.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
> index e5b869a35f..39f80b33ad 100644
> --- a/crypto/tlscredsx509.c
> +++ b/crypto/tlscredsx509.c
> @@ -773,15 +773,15 @@ qcrypto_tls_creds_x509_reload(QCryptoTLSCreds
> *creds, Error **errp)
>      QCryptoTLSCredsX509 *x509_creds = QCRYPTO_TLS_CREDS_X509(creds);
>      Error *local_err = NULL;
>      gnutls_certificate_credentials_t creds_data = x509_creds->data;
> -    gnutls_dh_params_t creds_dh_params = x509_creds->parent_obj.dh_params;
> +    gnutls_dh_params_t creds_dh_params = creds->dh_params;
>
>      x509_creds->data = NULL;
> -    x509_creds->parent_obj.dh_params = NULL;
> +    creds->dh_params = NULL;
>      qcrypto_tls_creds_x509_load(x509_creds, &local_err);
>      if (local_err) {
>          qcrypto_tls_creds_x509_unload(x509_creds);
>          x509_creds->data = creds_data;
> -        x509_creds->parent_obj.dh_params = creds_dh_params;
> +        creds->dh_params = creds_dh_params;
>          error_propagate(errp, local_err);
>          return false;
>      }
> --
> 2.51.1
>
>

[-- Attachment #2: Type: text/html, Size: 2402 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 08/21] crypto: move release of DH parameters into TLS creds parent
  2025-10-30 14:49 ` [PATCH 08/21] crypto: move release of DH parameters into TLS creds parent Daniel P. Berrangé
@ 2025-10-30 19:31   ` Marc-André Lureau
  0 siblings, 0 replies; 44+ messages in thread
From: Marc-André Lureau @ 2025-10-30 19:31 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, devel

[-- Attachment #1: Type: text/plain, Size: 3154 bytes --]

Hi

On Thu, Oct 30, 2025 at 6:49 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:

> The code for releasing DH parameters is common to all credential
> subclasses, so can be moved into the parent.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>

and unload() was only called from finalize
(and qcrypto_tls_creds_x509_reload())

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> ---
>  crypto/tlscreds.c     | 4 ++++
>  crypto/tlscredsanon.c | 4 ----
>  crypto/tlscredspsk.c  | 4 ----
>  crypto/tlscredsx509.c | 7 +++----
>  4 files changed, 7 insertions(+), 12 deletions(-)
>
> diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
> index 65e97ddd11..1e39ee1141 100644
> --- a/crypto/tlscreds.c
> +++ b/crypto/tlscreds.c
> @@ -246,6 +246,10 @@ qcrypto_tls_creds_finalize(Object *obj)
>  {
>      QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
>
> +    if (creds->dh_params) {
> +        gnutls_dh_params_deinit(creds->dh_params);
> +    }
> +
>      g_free(creds->dir);
>      g_free(creds->priority);
>  }
> diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c
> index bc3351b5d6..1ddfe4eb31 100644
> --- a/crypto/tlscredsanon.c
> +++ b/crypto/tlscredsanon.c
> @@ -92,10 +92,6 @@ qcrypto_tls_creds_anon_unload(QCryptoTLSCredsAnon
> *creds)
>              creds->data.server = NULL;
>          }
>      }
> -    if (creds->parent_obj.dh_params) {
> -        gnutls_dh_params_deinit(creds->parent_obj.dh_params);
> -        creds->parent_obj.dh_params = NULL;
> -    }
>  }
>
>  #else /* ! CONFIG_GNUTLS */
> diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c
> index 545d3e45db..bf4efe2114 100644
> --- a/crypto/tlscredspsk.c
> +++ b/crypto/tlscredspsk.c
> @@ -175,10 +175,6 @@ qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK
> *creds)
>              creds->data.server = NULL;
>          }
>      }
> -    if (creds->parent_obj.dh_params) {
> -        gnutls_dh_params_deinit(creds->parent_obj.dh_params);
> -        creds->parent_obj.dh_params = NULL;
> -    }
>  }
>
>  #else /* ! CONFIG_GNUTLS */
> diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
> index 39f80b33ad..1555285910 100644
> --- a/crypto/tlscredsx509.c
> +++ b/crypto/tlscredsx509.c
> @@ -685,10 +685,6 @@ qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509
> *creds)
>          gnutls_certificate_free_credentials(creds->data);
>          creds->data = NULL;
>      }
> -    if (creds->parent_obj.dh_params) {
> -        gnutls_dh_params_deinit(creds->parent_obj.dh_params);
> -        creds->parent_obj.dh_params = NULL;
> -    }
>  }
>
>
> @@ -780,6 +776,9 @@ qcrypto_tls_creds_x509_reload(QCryptoTLSCreds *creds,
> Error **errp)
>      qcrypto_tls_creds_x509_load(x509_creds, &local_err);
>      if (local_err) {
>          qcrypto_tls_creds_x509_unload(x509_creds);
> +        if (creds->dh_params) {
> +            gnutls_dh_params_deinit(creds->dh_params);
> +        }
>          x509_creds->data = creds_data;
>          creds->dh_params = creds_dh_params;
>          error_propagate(errp, local_err);
> --
> 2.51.1
>
>

[-- Attachment #2: Type: text/html, Size: 4187 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 10/21] crypto: remove duplication loading x509 CA cert
  2025-10-30 14:49 ` [PATCH 10/21] crypto: remove duplication loading x509 CA cert Daniel P. Berrangé
@ 2025-10-30 19:31   ` Marc-André Lureau
  0 siblings, 0 replies; 44+ messages in thread
From: Marc-André Lureau @ 2025-10-30 19:31 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, devel

[-- Attachment #1: Type: text/plain, Size: 2076 bytes --]

Hi

On Thu, Oct 30, 2025 at 6:49 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:

> The CA cert is mandatory in both client and server scenarios.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
>  crypto/tlscredsx509.c | 12 ++++++------
>  1 file changed, 6 insertions(+), 6 deletions(-)
>
> diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
> index 08223781d7..f2f1aa2815 100644
> --- a/crypto/tlscredsx509.c
> +++ b/crypto/tlscredsx509.c
> @@ -578,11 +578,14 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509
> *creds,
>
>      trace_qcrypto_tls_creds_x509_load(creds, creds->parent_obj.dir);
>
> +    if (qcrypto_tls_creds_get_path(&creds->parent_obj,
> +                                   QCRYPTO_TLS_CREDS_X509_CA_CERT,
> +                                   true, &cacert, errp) < 0) {
> +        return -1;
> +    }
> +
>      if (isServer) {
>          if (qcrypto_tls_creds_get_path(&creds->parent_obj,
> -                                       QCRYPTO_TLS_CREDS_X509_CA_CERT,
> -                                       true, &cacert, errp) < 0 ||
> -            qcrypto_tls_creds_get_path(&creds->parent_obj,
>                                         QCRYPTO_TLS_CREDS_X509_CA_CRL,
>                                         false, &cacrl, errp) < 0 ||
>              qcrypto_tls_creds_get_path(&creds->parent_obj,
> @@ -598,9 +601,6 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
>          }
>      } else {
>          if (qcrypto_tls_creds_get_path(&creds->parent_obj,
> -                                       QCRYPTO_TLS_CREDS_X509_CA_CERT,
> -                                       true, &cacert, errp) < 0 ||
> -            qcrypto_tls_creds_get_path(&creds->parent_obj,
>                                         QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
>                                         false, &cert, errp) < 0 ||
>              qcrypto_tls_creds_get_path(&creds->parent_obj,
> --
> 2.51.1
>
>

[-- Attachment #2: Type: text/html, Size: 3131 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 09/21] crypto: shorten the endpoint == server check in TLS creds
  2025-10-30 14:49 ` [PATCH 09/21] crypto: shorten the endpoint == server check in TLS creds Daniel P. Berrangé
@ 2025-10-30 19:32   ` Marc-André Lureau
  0 siblings, 0 replies; 44+ messages in thread
From: Marc-André Lureau @ 2025-10-30 19:32 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, devel

[-- Attachment #1: Type: text/plain, Size: 2297 bytes --]

Hi

On Thu, Oct 30, 2025 at 6:49 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:

> This eliminates a number of long lines aiding readability.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
>  crypto/tlscredsx509.c | 11 ++++++-----
>  1 file changed, 6 insertions(+), 5 deletions(-)
>
> diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
> index 1555285910..08223781d7 100644
> --- a/crypto/tlscredsx509.c
> +++ b/crypto/tlscredsx509.c
> @@ -567,6 +567,8 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
>      g_autofree char *cert = NULL;
>      g_autofree char *key = NULL;
>      g_autofree char *dhparams = NULL;
> +    bool isServer = (creds->parent_obj.endpoint ==
> +                     QCRYPTO_TLS_CREDS_ENDPOINT_SERVER);
>      int ret;
>
>      if (!creds->parent_obj.dir) {
> @@ -576,7 +578,7 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
>
>      trace_qcrypto_tls_creds_x509_load(creds, creds->parent_obj.dir);
>
> -    if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
> +    if (isServer) {
>          if (qcrypto_tls_creds_get_path(&creds->parent_obj,
>                                         QCRYPTO_TLS_CREDS_X509_CA_CERT,
>                                         true, &cacert, errp) < 0 ||
> @@ -609,9 +611,8 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
>      }
>
>      if (creds->sanityCheck &&
> -        qcrypto_tls_creds_x509_sanity_check(creds,
> -            creds->parent_obj.endpoint ==
> QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
> -            cacert, cert, errp) < 0) {
> +        qcrypto_tls_creds_x509_sanity_check(creds, isServer,
> +                                            cacert, cert, errp) < 0) {
>          return -1;
>      }
>
> @@ -664,7 +665,7 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
>          }
>      }
>
> -    if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
> +    if (isServer) {
>          if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj,
> dhparams,
>
> &creds->parent_obj.dh_params,
>                                                   errp) < 0) {
> --
> 2.51.1
>
>

[-- Attachment #2: Type: text/html, Size: 3350 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 11/21] crypto: reduce duplication in handling TLS priority strings
  2025-10-30 14:49 ` [PATCH 11/21] crypto: reduce duplication in handling TLS priority strings Daniel P. Berrangé
@ 2025-10-30 19:40   ` Marc-André Lureau
  0 siblings, 0 replies; 44+ messages in thread
From: Marc-André Lureau @ 2025-10-30 19:40 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, devel

[-- Attachment #1: Type: text/plain, Size: 8458 bytes --]

Hi

On Thu, Oct 30, 2025 at 6:49 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:

> The logic for setting the TLS priority string on a session object has a
> significant amount of logic duplication across the different credential
> types. By recording the extra priority string suffix against the
> credential class, we can introduce a common method for building the
> priority string. The TLS session can now set the priority string without
> caring about the credential type.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
>  crypto/tlscreds.c         | 15 ++++++++++
>  crypto/tlscredsanon.c     |  2 ++
>  crypto/tlscredspsk.c      |  2 ++
>  crypto/tlssession.c       | 60 ++++++---------------------------------
>  include/crypto/tlscreds.h | 13 +++++++++
>  5 files changed, 41 insertions(+), 51 deletions(-)
>
> diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
> index 1e39ee1141..49c7eb46a5 100644
> --- a/crypto/tlscreds.c
> +++ b/crypto/tlscreds.c
> @@ -266,6 +266,21 @@ bool qcrypto_tls_creds_check_endpoint(QCryptoTLSCreds
> *creds,
>      return true;
>  }
>
> +
> +char *qcrypto_tls_creds_get_priority(QCryptoTLSCreds *creds)
> +{
> +    QCryptoTLSCredsClass *tcc = QCRYPTO_TLS_CREDS_GET_CLASS(creds);
> +    const char *priorityBase =
> +        creds->priority ? creds->priority : CONFIG_TLS_PRIORITY;
> +
> +    if (tcc->prioritySuffix) {
> +        return g_strdup_printf("%s:%s", priorityBase,
> tcc->prioritySuffix);
> +    } else {
> +        return g_strdup(priorityBase);
> +    }
> +}
> +
> +
>  static const TypeInfo qcrypto_tls_creds_info = {
>      .parent = TYPE_OBJECT,
>      .name = TYPE_QCRYPTO_TLS_CREDS,
> diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c
> index 1ddfe4eb31..5c55b07b2f 100644
> --- a/crypto/tlscredsanon.c
> +++ b/crypto/tlscredsanon.c
> @@ -137,8 +137,10 @@ static void
>  qcrypto_tls_creds_anon_class_init(ObjectClass *oc, const void *data)
>  {
>      UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
> +    QCryptoTLSCredsClass *tcc = QCRYPTO_TLS_CREDS_CLASS(oc);
>
>      ucc->complete = qcrypto_tls_creds_anon_complete;
> +    tcc->prioritySuffix = "+ANON-DH";
>  }
>
>
> diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c
> index bf4efe2114..6c2feae077 100644
> --- a/crypto/tlscredspsk.c
> +++ b/crypto/tlscredspsk.c
> @@ -240,8 +240,10 @@ static void
>  qcrypto_tls_creds_psk_class_init(ObjectClass *oc, const void *data)
>  {
>      UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
> +    QCryptoTLSCredsClass *tcc = QCRYPTO_TLS_CREDS_CLASS(oc);
>
>      ucc->complete = qcrypto_tls_creds_psk_complete;
> +    tcc->prioritySuffix = "+ECDHE-PSK:+DHE-PSK:+PSK";
>
>      object_class_property_add_str(oc, "username",
>                                    qcrypto_tls_creds_psk_prop_get_username,
> diff --git a/crypto/tlssession.c b/crypto/tlssession.c
> index 92fe4f0380..77f334add3 100644
> --- a/crypto/tlssession.c
> +++ b/crypto/tlssession.c
> @@ -155,9 +155,6 @@ qcrypto_tls_session_pull(void *opaque, void *buf,
> size_t len)
>      }
>  }
>
> -#define TLS_PRIORITY_ADDITIONAL_ANON "+ANON-DH"
> -#define TLS_PRIORITY_ADDITIONAL_PSK "+ECDHE-PSK:+DHE-PSK:+PSK"
> -
>  QCryptoTLSSession *
>  qcrypto_tls_session_new(QCryptoTLSCreds *creds,
>                          const char *hostname,
> @@ -167,6 +164,7 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds,
>  {
>      QCryptoTLSSession *session;
>      int ret;
> +    g_autofree char *prio = NULL;
>
>      session = g_new0(QCryptoTLSSession, 1);
>      trace_qcrypto_tls_session_new(
> @@ -200,28 +198,17 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds,
>          goto error;
>      }
>
> +    prio = qcrypto_tls_creds_get_priority(creds);
> +    ret = gnutls_priority_set_direct(session->handle, prio, NULL);
> +    if (ret < 0) {
> +        error_setg(errp, "Unable to set TLS session priority %s: %s",
> +                   prio, gnutls_strerror(ret));
> +        goto error;
> +    }
> +
>      if (object_dynamic_cast(OBJECT(creds),
>                              TYPE_QCRYPTO_TLS_CREDS_ANON)) {
>          QCryptoTLSCredsAnon *acreds = QCRYPTO_TLS_CREDS_ANON(creds);
> -        char *prio;
> -
> -        if (creds->priority != NULL) {
> -            prio = g_strdup_printf("%s:%s",
> -                                   creds->priority,
> -                                   TLS_PRIORITY_ADDITIONAL_ANON);
> -        } else {
> -            prio = g_strdup(CONFIG_TLS_PRIORITY ":"
> -                            TLS_PRIORITY_ADDITIONAL_ANON);
> -        }
> -
> -        ret = gnutls_priority_set_direct(session->handle, prio, NULL);
> -        if (ret < 0) {
> -            error_setg(errp, "Unable to set TLS session priority %s: %s",
> -                       prio, gnutls_strerror(ret));
> -            g_free(prio);
> -            goto error;
> -        }
> -        g_free(prio);
>          if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
>              ret = gnutls_credentials_set(session->handle,
>                                           GNUTLS_CRD_ANON,
> @@ -239,25 +226,6 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds,
>      } else if (object_dynamic_cast(OBJECT(creds),
>                                     TYPE_QCRYPTO_TLS_CREDS_PSK)) {
>          QCryptoTLSCredsPSK *pcreds = QCRYPTO_TLS_CREDS_PSK(creds);
> -        char *prio;
> -
> -        if (creds->priority != NULL) {
> -            prio = g_strdup_printf("%s:%s",
> -                                   creds->priority,
> -                                   TLS_PRIORITY_ADDITIONAL_PSK);
> -        } else {
> -            prio = g_strdup(CONFIG_TLS_PRIORITY ":"
> -                            TLS_PRIORITY_ADDITIONAL_PSK);
> -        }
> -
> -        ret = gnutls_priority_set_direct(session->handle, prio, NULL);
> -        if (ret < 0) {
> -            error_setg(errp, "Unable to set TLS session priority %s: %s",
> -                       prio, gnutls_strerror(ret));
> -            g_free(prio);
> -            goto error;
> -        }
> -        g_free(prio);
>          if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
>              ret = gnutls_credentials_set(session->handle,
>                                           GNUTLS_CRD_PSK,
> @@ -275,17 +243,7 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds,
>      } else if (object_dynamic_cast(OBJECT(creds),
>                                     TYPE_QCRYPTO_TLS_CREDS_X509)) {
>          QCryptoTLSCredsX509 *tcreds = QCRYPTO_TLS_CREDS_X509(creds);
> -        const char *prio = creds->priority;
> -        if (!prio) {
> -            prio = CONFIG_TLS_PRIORITY;
> -        }
>
> -        ret = gnutls_priority_set_direct(session->handle, prio, NULL);
> -        if (ret < 0) {
> -            error_setg(errp, "Cannot set default TLS session priority %s:
> %s",
> -                       prio, gnutls_strerror(ret));
> -            goto error;
> -        }
>          ret = gnutls_credentials_set(session->handle,
>                                       GNUTLS_CRD_CERTIFICATE,
>                                       tcreds->data);
> diff --git a/include/crypto/tlscreds.h b/include/crypto/tlscreds.h
> index 2a8a857010..afd1016088 100644
> --- a/include/crypto/tlscreds.h
> +++ b/include/crypto/tlscreds.h
> @@ -47,6 +47,7 @@ typedef bool (*CryptoTLSCredsReload)(QCryptoTLSCreds *,
> Error **);
>  struct QCryptoTLSCredsClass {
>      ObjectClass parent_class;
>      CryptoTLSCredsReload reload;
> +    const char *prioritySuffix;
>

sneaking camelCase to scare the snakes?

anyhow:
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


>  };
>
>  /**
> @@ -64,4 +65,16 @@ bool qcrypto_tls_creds_check_endpoint(QCryptoTLSCreds
> *creds,
>                                        QCryptoTLSCredsEndpoint endpoint,
>                                        Error **errp);
>
> +
> +/**
> + * qcrypto_tls_creds_get_priority:
> + * @creds: pointer to a TLS credentials object
> + *
> + * Get the TLS credentials priority string. The caller
> + * must free the returned string when no longer required.
> + *
> + * Returns: a non-NULL priority string
> + */
> +char *qcrypto_tls_creds_get_priority(QCryptoTLSCreds *creds);
> +
>  #endif /* QCRYPTO_TLSCREDS_H */
> --
> 2.51.1
>
>

[-- Attachment #2: Type: text/html, Size: 10424 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 12/21] crypto: introduce method for reloading TLS creds
  2025-10-30 14:49 ` [PATCH 12/21] crypto: introduce method for reloading TLS creds Daniel P. Berrangé
@ 2025-10-30 19:43   ` Marc-André Lureau
  2025-10-31 11:31     ` Daniel P. Berrangé
  0 siblings, 1 reply; 44+ messages in thread
From: Marc-André Lureau @ 2025-10-30 19:43 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, devel

[-- Attachment #1: Type: text/plain, Size: 3186 bytes --]

On Thu, Oct 30, 2025 at 6:49 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:

> This prevents direct access of the class members by the VNC
> display code.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> ---
>  crypto/tlscreds.c         | 15 +++++++++++++++
>  include/crypto/tlscreds.h | 13 +++++++++++++
>  ui/vnc.c                  |  9 +--------
>  3 files changed, 29 insertions(+), 8 deletions(-)
>
> diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
> index 49c7eb46a5..9433b4c363 100644
> --- a/crypto/tlscreds.c
> +++ b/crypto/tlscreds.c
> @@ -281,6 +281,21 @@ char *qcrypto_tls_creds_get_priority(QCryptoTLSCreds
> *creds)
>  }
>
>
> +bool qcrypto_tls_creds_reload(QCryptoTLSCreds *creds,
> +                              Error **errp)
> +{
> +    QCryptoTLSCredsClass *credscls =
> QCRYPTO_TLS_CREDS_GET_CLASS(OBJECT(creds));
> +
>

OBJECT() unnecessary here


> +    if (credscls->reload) {
> +        return credscls->reload(creds, errp);
> +    }
> +
> +    error_setg(errp, "%s does not support reloading credentials",
> +               object_get_typename(OBJECT(creds)));
> +    return false;
> +}
> +
> +
>  static const TypeInfo qcrypto_tls_creds_info = {
>      .parent = TYPE_OBJECT,
>      .name = TYPE_QCRYPTO_TLS_CREDS,
> diff --git a/include/crypto/tlscreds.h b/include/crypto/tlscreds.h
> index afd1016088..bb9280ed1a 100644
> --- a/include/crypto/tlscreds.h
> +++ b/include/crypto/tlscreds.h
> @@ -77,4 +77,17 @@ bool qcrypto_tls_creds_check_endpoint(QCryptoTLSCreds
> *creds,
>   */
>  char *qcrypto_tls_creds_get_priority(QCryptoTLSCreds *creds);
>
> +
> +/**
> + * qcrypto_tls_creds_reload:
> + * @creds: pointer to a TLS credentials object
> + * @errp: pointer to a NULL-initialized error object
> + *
> + * Request a reload of the TLS credentials, if supported
> + *
> + * Returns: true on success, false on error or if not supported
> + */
> +bool qcrypto_tls_creds_reload(QCryptoTLSCreds *creds,
> +                              Error **errp);
> +
>  #endif /* QCRYPTO_TLSCREDS_H */
> diff --git a/ui/vnc.c b/ui/vnc.c
> index 77c823bf2e..6b32dd0fe9 100644
> --- a/ui/vnc.c
> +++ b/ui/vnc.c
> @@ -578,7 +578,6 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp)
>  bool vnc_display_reload_certs(const char *id, Error **errp)
>  {
>      VncDisplay *vd = vnc_display_find(id);
> -    QCryptoTLSCredsClass *creds = NULL;
>
>      if (!vd) {
>          error_setg(errp, "Can not find vnc display");
> @@ -590,13 +589,7 @@ bool vnc_display_reload_certs(const char *id, Error
> **errp)
>          return false;
>      }
>
> -    creds = QCRYPTO_TLS_CREDS_GET_CLASS(OBJECT(vd->tlscreds));
> -    if (creds->reload == NULL) {
> -        error_setg(errp, "%s doesn't support to reload TLS credential",
> -                   object_get_typename(OBJECT(vd->tlscreds)));
> -        return false;
> -    }
> -    if (!creds->reload(vd->tlscreds, errp)) {
> +    if (!qcrypto_tls_creds_reload(vd->tlscreds, errp)) {
>          return false;
>      }
>
> --
> 2.51.1
>
>

[-- Attachment #2: Type: text/html, Size: 4323 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 13/21] crypto: introduce a wrapper around gnutls credentials
  2025-10-30 14:49 ` [PATCH 13/21] crypto: introduce a wrapper around gnutls credentials Daniel P. Berrangé
@ 2025-10-31 11:23   ` Marc-André Lureau
  2025-10-31 11:27     ` Daniel P. Berrangé
  0 siblings, 1 reply; 44+ messages in thread
From: Marc-André Lureau @ 2025-10-31 11:23 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, devel

[-- Attachment #1: Type: text/plain, Size: 7013 bytes --]

Hi

On Thu, Oct 30, 2025 at 6:49 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:

> The gnutls_credentials_set() method has a very suprising API contract
> that requires the caller to preserve the passed in credentials pointer
> for as long as the gnutls_session_t object is alive. QEMU is failing
> to ensure this happens.
>
> In QEMU the GNUTLS credentials object is owned by the QCryptoTLSCreds
> object instance while the GNUTLS session object is owned by the
> QCryptoTLSSession object instance. Their lifetimes are not guaranteed
> to be the same, though in most common usage the credentials will outlive
> the session. This is notably not the case, however, after the VNC server
> gained the ability to reload credentials on the fly with:
>
>   commit 1f08e3415120637cad7f540d9ceb4dba3136dbdd
>   Author: Zihao Chang <changzihao1@huawei.com>
>   Date:   Tue Mar 16 15:58:44 2021 +0800
>
>     vnc: support reload x509 certificates for vnc
>
> If that is triggered while a VNC client is in the middle of performing
> a TLS handshake, we might hit a use-after-free.
>
> It is difficult to correct this problem because there's no way to deep-
> clone a GNUTLS credentials object, nor is it reference counted. Thus we
> introduce a QCryptoTLSCredsBox object whose only purpose is to add
> reference counting around the GNUTLS credentials object.
>
> The DH parameters set against a credentials object also have to be kept
> alive for as long as the credentials exist. So the box must also hold
> the DH parameters pointer.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> ---
>  crypto/meson.build   |   5 ++-
>  crypto/tlscredsbox.c | 101 +++++++++++++++++++++++++++++++++++++++++++
>  crypto/tlscredsbox.h |  46 ++++++++++++++++++++
>  3 files changed, 151 insertions(+), 1 deletion(-)
>  create mode 100644 crypto/tlscredsbox.c
>  create mode 100644 crypto/tlscredsbox.h
>
> diff --git a/crypto/meson.build b/crypto/meson.build
> index 735635de1f..1fc14b2a04 100644
> --- a/crypto/meson.build
> +++ b/crypto/meson.build
> @@ -25,7 +25,10 @@ crypto_ss.add(files(
>  ))
>
>  if gnutls.found()
> -  crypto_ss.add(files('x509-utils.c'))
> +  crypto_ss.add(files(
> +    'tlscredsbox.c',
> +    'x509-utils.c',
> +  ))
>  endif
>
>  if nettle.found()
> diff --git a/crypto/tlscredsbox.c b/crypto/tlscredsbox.c
> new file mode 100644
> index 0000000000..b8d9846af8
> --- /dev/null
> +++ b/crypto/tlscredsbox.c
> @@ -0,0 +1,101 @@
> +/*
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * QEMU crypto TLS credential support
> + *
> + * Copyright (c) 2025 Red Hat, Inc.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "crypto/tlscredsbox.h"
> +#include "qemu/atomic.h"
> +
> +
> +static QCryptoTLSCredsBox *
> +qcrypto_tls_creds_box_new_impl(int type, bool server)
> +{
> +    QCryptoTLSCredsBox *credsbox = g_new0(QCryptoTLSCredsBox, 1);
> +    credsbox->ref = 1;
> +    credsbox->server = server;
> +    credsbox->type = type;
> +    return credsbox;
> +}
> +
> +
> +QCryptoTLSCredsBox *
> +qcrypto_tls_creds_box_new_server(int type)
> +{
> +    return qcrypto_tls_creds_box_new_impl(type, true);
> +}
> +
> +
> +QCryptoTLSCredsBox *
> +qcrypto_tls_creds_box_new_client(int type)
> +{
> +    return qcrypto_tls_creds_box_new_impl(type, false);
> +}
> +
> +static void qcrypto_tls_creds_box_free(QCryptoTLSCredsBox *credsbox)
> +{
> +    switch (credsbox->type) {
> +    case GNUTLS_CRD_CERTIFICATE:
> +        if (credsbox->data.cert) {
> +            gnutls_certificate_free_credentials(credsbox->data.cert);
> +        }
> +        break;
> +    case GNUTLS_CRD_PSK:
> +        if (credsbox->server) {
> +            if (credsbox->data.pskserver) {
> +
> gnutls_psk_free_server_credentials(credsbox->data.pskserver);
> +            }
> +        } else {
> +            if (credsbox->data.pskclient) {
> +
> gnutls_psk_free_client_credentials(credsbox->data.pskclient);
> +            }
> +        }
> +        break;
> +    case GNUTLS_CRD_ANON:
> +        if (credsbox->server) {
> +            if (credsbox->data.anonserver) {
> +
> gnutls_anon_free_server_credentials(credsbox->data.anonserver);
> +            }
> +        } else {
> +            if (credsbox->data.anonclient) {
> +
> gnutls_anon_free_client_credentials(credsbox->data.anonclient);
> +            }
> +        }
> +        break;
> +    default:
> +        g_assert_not_reached();
> +    }
> +
> +    if (credsbox->dh_params) {
> +        gnutls_dh_params_deinit(credsbox->dh_params);
> +    }
> +
> +    g_free(credsbox);
> +}
> +
> +
> +void qcrypto_tls_creds_box_ref(QCryptoTLSCredsBox *credsbox)
> +{
> +    uint32_t ref = qatomic_fetch_inc(&credsbox->ref);
> +    /* Assert waaay before the integer overflows */
> +    g_assert(ref < INT_MAX);
> +}
> +
> +
> +void qcrypto_tls_creds_box_unref(QCryptoTLSCredsBox *credsbox)
> +{
> +    if (!credsbox) {
> +        return;
> +    }
> +
> +    g_assert(credsbox->ref > 0);
> +
> +    if (qatomic_fetch_dec(&credsbox->ref) == 1) {
> +        qcrypto_tls_creds_box_free(credsbox);
> +    }
> +
> +}
> +
> diff --git a/crypto/tlscredsbox.h b/crypto/tlscredsbox.h
> new file mode 100644
> index 0000000000..5d89335f46
> --- /dev/null
> +++ b/crypto/tlscredsbox.h
> @@ -0,0 +1,46 @@
> +/*
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * QEMU crypto TLS credential support
> + *
> + * Copyright (c) 2025 Red Hat, Inc.
> + */
> +
> +#ifndef QCRYPTO_TLSCREDS_BOX_H
> +#define QCRYPTO_TLSCREDS_BOX_H
> +
> +#include "qom/object.h"
> +
> +#ifdef CONFIG_GNUTLS
> +#include <gnutls/gnutls.h>
> +#endif
> +
> +typedef struct QCryptoTLSCredsBox QCryptoTLSCredsBox;
> +
> +struct QCryptoTLSCredsBox {
> +    uint32_t ref;
> +    bool server;
> +    int type;
> +    union {
> +        void *any;
>

since any is used in code to cast the variant to a void *, it may be worth
a comment to say that all fields are expected to be pointers.


> +#ifdef CONFIG_GNUTLS
> +        gnutls_anon_server_credentials_t anonserver;
> +        gnutls_anon_client_credentials_t anonclient;
> +        gnutls_psk_server_credentials_t pskserver;
> +        gnutls_psk_client_credentials_t pskclient;
> +        gnutls_certificate_credentials_t cert;
> +#endif
> +    } data;
> +#ifdef CONFIG_GNUTLS
> +    gnutls_dh_params_t dh_params;
> +#endif
> +};
> +
> +QCryptoTLSCredsBox *qcrypto_tls_creds_box_new_server(int type);
> +QCryptoTLSCredsBox *qcrypto_tls_creds_box_new_client(int type);
> +void qcrypto_tls_creds_box_ref(QCryptoTLSCredsBox *credsbox);
> +void qcrypto_tls_creds_box_unref(QCryptoTLSCredsBox *credsbox);
> +
> +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSCredsBox,
> qcrypto_tls_creds_box_unref);
> +
> +#endif /* QCRYPTO_TLSCREDS_BOX_H */
> --
> 2.51.1
>
>

[-- Attachment #2: Type: text/html, Size: 8735 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 14/21] crypto: fix lifecycle handling of gnutls credentials objects
  2025-10-30 14:49 ` [PATCH 14/21] crypto: fix lifecycle handling of gnutls credentials objects Daniel P. Berrangé
@ 2025-10-31 11:24   ` Marc-André Lureau
  0 siblings, 0 replies; 44+ messages in thread
From: Marc-André Lureau @ 2025-10-31 11:24 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, devel

[-- Attachment #1: Type: text/plain, Size: 22619 bytes --]

On Thu, Oct 30, 2025 at 6:50 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:

> As described in the previous commit, the gnutls credentials need to
> be kept alive for as long as the gnutls session object exists. Convert
> the QCryptoTLSCreds objects to use QCryptoTLSCredsBox and holding the
> gnutls credential objects. When loading the credentials into a gnutls
> session, store a reference to the box into the QCryptoTLSSession object.
>
> This has the useful side effect that the QCryptoTLSSession code no
> longer needs to know about all the different credential types, it can
> use the generic pointer stored in the box.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> ---
>  crypto/tlscreds.c     |  5 +--
>  crypto/tlscredsanon.c | 48 +++++---------------------
>  crypto/tlscredspriv.h | 20 ++---------
>  crypto/tlscredspsk.c  | 46 ++++++++-----------------
>  crypto/tlscredsx509.c | 71 +++++++++++++-------------------------
>  crypto/tlssession.c   | 80 ++++++++++++++-----------------------------
>  6 files changed, 75 insertions(+), 195 deletions(-)
>
> diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
> index 9433b4c363..798c9712fb 100644
> --- a/crypto/tlscreds.c
> +++ b/crypto/tlscreds.c
> @@ -246,10 +246,7 @@ qcrypto_tls_creds_finalize(Object *obj)
>  {
>      QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
>
> -    if (creds->dh_params) {
> -        gnutls_dh_params_deinit(creds->dh_params);
> -    }
> -
> +    qcrypto_tls_creds_box_unref(creds->box);
>      g_free(creds->dir);
>      g_free(creds->priority);
>  }
> diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c
> index 5c55b07b2f..0a728ccbf6 100644
> --- a/crypto/tlscredsanon.c
> +++ b/crypto/tlscredsanon.c
> @@ -36,6 +36,7 @@ static int
>  qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
>                              Error **errp)
>  {
> +    g_autoptr(QCryptoTLSCredsBox) box = NULL;
>      g_autofree char *dhparams = NULL;
>      int ret;
>
> @@ -43,6 +44,8 @@ qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
>              creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
>
>      if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
> +        box = qcrypto_tls_creds_box_new_server(GNUTLS_CRD_ANON);
> +
>          if (creds->parent_obj.dir &&
>              qcrypto_tls_creds_get_path(&creds->parent_obj,
>                                         QCRYPTO_TLS_CREDS_DH_PARAMS,
> @@ -50,7 +53,7 @@ qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
>              return -1;
>          }
>
> -        ret =
> gnutls_anon_allocate_server_credentials(&creds->data.server);
> +        ret =
> gnutls_anon_allocate_server_credentials(&box->data.anonserver);
>          if (ret < 0) {
>              error_setg(errp, "Cannot allocate credentials: %s",
>                         gnutls_strerror(ret));
> @@ -58,42 +61,26 @@ qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
>          }
>
>          if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj,
> dhparams,
> -
>  &creds->parent_obj.dh_params,
> -                                                 errp) < 0) {
> +                                                 &box->dh_params, errp) <
> 0) {
>              return -1;
>          }
>
> -        gnutls_anon_set_server_dh_params(creds->data.server,
> -                                         creds->parent_obj.dh_params);
> +        gnutls_anon_set_server_dh_params(box->data.anonserver,
> +                                         box->dh_params);
>      } else {
> -        ret =
> gnutls_anon_allocate_client_credentials(&creds->data.client);
> +        ret =
> gnutls_anon_allocate_client_credentials(&box->data.anonclient);
>          if (ret < 0) {
>              error_setg(errp, "Cannot allocate credentials: %s",
>                         gnutls_strerror(ret));
>              return -1;
>          }
>      }
> +    creds->parent_obj.box = g_steal_pointer(&box);
>
>      return 0;
>  }
>
>
> -static void
> -qcrypto_tls_creds_anon_unload(QCryptoTLSCredsAnon *creds)
> -{
> -    if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
> -        if (creds->data.client) {
> -            gnutls_anon_free_client_credentials(creds->data.client);
> -            creds->data.client = NULL;
> -        }
> -    } else {
> -        if (creds->data.server) {
> -            gnutls_anon_free_server_credentials(creds->data.server);
> -            creds->data.server = NULL;
> -        }
> -    }
> -}
> -
>  #else /* ! CONFIG_GNUTLS */
>
>
> @@ -105,13 +92,6 @@ qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds
> G_GNUC_UNUSED,
>  }
>
>
> -static void
> -qcrypto_tls_creds_anon_unload(QCryptoTLSCredsAnon *creds G_GNUC_UNUSED)
> -{
> -    /* nada */
> -}
> -
> -
>  #endif /* ! CONFIG_GNUTLS */
>
>
> @@ -124,15 +104,6 @@ qcrypto_tls_creds_anon_complete(UserCreatable *uc,
> Error **errp)
>  }
>
>
> -static void
> -qcrypto_tls_creds_anon_finalize(Object *obj)
> -{
> -    QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj);
> -
> -    qcrypto_tls_creds_anon_unload(creds);
> -}
> -
> -
>  static void
>  qcrypto_tls_creds_anon_class_init(ObjectClass *oc, const void *data)
>  {
> @@ -148,7 +119,6 @@ static const TypeInfo qcrypto_tls_creds_anon_info = {
>      .parent = TYPE_QCRYPTO_TLS_CREDS,
>      .name = TYPE_QCRYPTO_TLS_CREDS_ANON,
>      .instance_size = sizeof(QCryptoTLSCredsAnon),
> -    .instance_finalize = qcrypto_tls_creds_anon_finalize,
>      .class_size = sizeof(QCryptoTLSCredsAnonClass),
>      .class_init = qcrypto_tls_creds_anon_class_init,
>      .interfaces = (const InterfaceInfo[]) {
> diff --git a/crypto/tlscredspriv.h b/crypto/tlscredspriv.h
> index df9815a286..4e6dffa22f 100644
> --- a/crypto/tlscredspriv.h
> +++ b/crypto/tlscredspriv.h
> @@ -22,6 +22,7 @@
>  #define QCRYPTO_TLSCREDSPRIV_H
>
>  #include "crypto/tlscreds.h"
> +#include "crypto/tlscredsbox.h"
>
>  #ifdef CONFIG_GNUTLS
>  #include <gnutls/gnutls.h>
> @@ -31,39 +32,22 @@ struct QCryptoTLSCreds {
>      Object parent_obj;
>      char *dir;
>      QCryptoTLSCredsEndpoint endpoint;
> -#ifdef CONFIG_GNUTLS
> -    gnutls_dh_params_t dh_params;
> -#endif
>      bool verifyPeer;
>      char *priority;
> +    QCryptoTLSCredsBox *box;
>  };
>
>  struct QCryptoTLSCredsAnon {
>      QCryptoTLSCreds parent_obj;
> -#ifdef CONFIG_GNUTLS
> -    union {
> -        gnutls_anon_server_credentials_t server;
> -        gnutls_anon_client_credentials_t client;
> -    } data;
> -#endif
>  };
>
>  struct QCryptoTLSCredsPSK {
>      QCryptoTLSCreds parent_obj;
>      char *username;
> -#ifdef CONFIG_GNUTLS
> -    union {
> -        gnutls_psk_server_credentials_t server;
> -        gnutls_psk_client_credentials_t client;
> -    } data;
> -#endif
>  };
>
>  struct QCryptoTLSCredsX509 {
>      QCryptoTLSCreds parent_obj;
> -#ifdef CONFIG_GNUTLS
> -    gnutls_certificate_credentials_t data;
> -#endif
>      bool sanityCheck;
>      char *passwordid;
>  };
> diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c
> index 6c2feae077..5568f1ad0c 100644
> --- a/crypto/tlscredspsk.c
> +++ b/crypto/tlscredspsk.c
> @@ -71,6 +71,7 @@ static int
>  qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
>                             Error **errp)
>  {
> +    g_autoptr(QCryptoTLSCredsBox) box = NULL;
>      g_autofree char *pskfile = NULL;
>      g_autofree char *dhparams = NULL;
>      const char *username;
> @@ -87,6 +88,8 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
>      }
>
>      if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
> +        box = qcrypto_tls_creds_box_new_server(GNUTLS_CRD_PSK);
> +
>          if (creds->username) {
>              error_setg(errp, "username should not be set when
> endpoint=server");
>              goto cleanup;
> @@ -101,7 +104,7 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
>              goto cleanup;
>          }
>
> -        ret = gnutls_psk_allocate_server_credentials(&creds->data.server);
> +        ret =
> gnutls_psk_allocate_server_credentials(&box->data.pskserver);
>          if (ret < 0) {
>              error_setg(errp, "Cannot allocate credentials: %s",
>                         gnutls_strerror(ret));
> @@ -109,20 +112,23 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
>          }
>
>          if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj,
> dhparams,
> -
>  &creds->parent_obj.dh_params,
> +                                                 &box->dh_params,
>                                                   errp) < 0) {
>              goto cleanup;
>          }
>
> -        ret = gnutls_psk_set_server_credentials_file(creds->data.server,
> pskfile);
> +        ret = gnutls_psk_set_server_credentials_file(box->data.pskserver,
> +                                                     pskfile);
>          if (ret < 0) {
>              error_setg(errp, "Cannot set PSK server credentials: %s",
>                         gnutls_strerror(ret));
>              goto cleanup;
>          }
> -        gnutls_psk_set_server_dh_params(creds->data.server,
> -                                        creds->parent_obj.dh_params);
> +        gnutls_psk_set_server_dh_params(box->data.pskserver,
> +                                        box->dh_params);
>      } else {
> +        box = qcrypto_tls_creds_box_new_client(GNUTLS_CRD_PSK);
> +
>          if (qcrypto_tls_creds_get_path(&creds->parent_obj,
>                                         QCRYPTO_TLS_CREDS_PSKFILE,
>                                         true, &pskfile, errp) < 0) {
> @@ -138,14 +144,14 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
>              goto cleanup;
>          }
>
> -        ret = gnutls_psk_allocate_client_credentials(&creds->data.client);
> +        ret =
> gnutls_psk_allocate_client_credentials(&box->data.pskclient);
>          if (ret < 0) {
>              error_setg(errp, "Cannot allocate credentials: %s",
>                         gnutls_strerror(ret));
>              goto cleanup;
>          }
>
> -        ret = gnutls_psk_set_client_credentials(creds->data.client,
> +        ret = gnutls_psk_set_client_credentials(box->data.pskclient,
>                                                  username, &key,
> GNUTLS_PSK_KEY_HEX);
>          if (ret < 0) {
>              error_setg(errp, "Cannot set PSK client credentials: %s",
> @@ -153,6 +159,7 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
>              goto cleanup;
>          }
>      }
> +    creds->parent_obj.box = g_steal_pointer(&box);
>
>      rv = 0;
>   cleanup:
> @@ -160,23 +167,6 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
>      return rv;
>  }
>
> -
> -static void
> -qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK *creds)
> -{
> -    if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
> -        if (creds->data.client) {
> -            gnutls_psk_free_client_credentials(creds->data.client);
> -            creds->data.client = NULL;
> -        }
> -    } else {
> -        if (creds->data.server) {
> -            gnutls_psk_free_server_credentials(creds->data.server);
> -            creds->data.server = NULL;
> -        }
> -    }
> -}
> -
>  #else /* ! CONFIG_GNUTLS */
>
>
> @@ -188,13 +178,6 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds
> G_GNUC_UNUSED,
>  }
>
>
> -static void
> -qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK *creds G_GNUC_UNUSED)
> -{
> -    /* nada */
> -}
> -
> -
>  #endif /* ! CONFIG_GNUTLS */
>
>
> @@ -212,7 +195,6 @@ qcrypto_tls_creds_psk_finalize(Object *obj)
>  {
>      QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj);
>
> -    qcrypto_tls_creds_psk_unload(creds);
>      g_free(creds->username);
>  }
>
> diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
> index f2f1aa2815..ef31ea664c 100644
> --- a/crypto/tlscredsx509.c
> +++ b/crypto/tlscredsx509.c
> @@ -562,6 +562,7 @@ static int
>  qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
>                              Error **errp)
>  {
> +    g_autoptr(QCryptoTLSCredsBox) box = NULL;
>      g_autofree char *cacert = NULL;
>      g_autofree char *cacrl = NULL;
>      g_autofree char *cert = NULL;
> @@ -578,6 +579,19 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509
> *creds,
>
>      trace_qcrypto_tls_creds_x509_load(creds, creds->parent_obj.dir);
>
> +    if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
> +        box = qcrypto_tls_creds_box_new_server(GNUTLS_CRD_CERTIFICATE);
> +    } else {
> +        box = qcrypto_tls_creds_box_new_client(GNUTLS_CRD_CERTIFICATE);
> +    }
> +
> +    ret = gnutls_certificate_allocate_credentials(&box->data.cert);
> +    if (ret < 0) {
> +        error_setg(errp, "Cannot allocate credentials: '%s'",
> +                   gnutls_strerror(ret));
> +        return -1;
> +    }
> +
>      if (qcrypto_tls_creds_get_path(&creds->parent_obj,
>                                     QCRYPTO_TLS_CREDS_X509_CA_CERT,
>                                     true, &cacert, errp) < 0) {
> @@ -616,14 +630,7 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509
> *creds,
>          return -1;
>      }
>
> -    ret = gnutls_certificate_allocate_credentials(&creds->data);
> -    if (ret < 0) {
> -        error_setg(errp, "Cannot allocate credentials: '%s'",
> -                   gnutls_strerror(ret));
> -        return -1;
> -    }
> -
> -    ret = gnutls_certificate_set_x509_trust_file(creds->data,
> +    ret = gnutls_certificate_set_x509_trust_file(box->data.cert,
>                                                   cacert,
>                                                   GNUTLS_X509_FMT_PEM);
>      if (ret < 0) {
> @@ -641,7 +648,7 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
>                  return -1;
>              }
>          }
> -        ret = gnutls_certificate_set_x509_key_file2(creds->data,
> +        ret = gnutls_certificate_set_x509_key_file2(box->data.cert,
>                                                      cert, key,
>                                                      GNUTLS_X509_FMT_PEM,
>                                                      password,
> @@ -655,7 +662,7 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
>      }
>
>      if (cacrl != NULL) {
> -        ret = gnutls_certificate_set_x509_crl_file(creds->data,
> +        ret = gnutls_certificate_set_x509_crl_file(box->data.cert,
>                                                     cacrl,
>                                                     GNUTLS_X509_FMT_PEM);
>          if (ret < 0) {
> @@ -667,28 +674,18 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509
> *creds,
>
>      if (isServer) {
>          if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj,
> dhparams,
> -
>  &creds->parent_obj.dh_params,
> +                                                 &box->dh_params,
>                                                   errp) < 0) {
>              return -1;
>          }
> -        gnutls_certificate_set_dh_params(creds->data,
> -                                         creds->parent_obj.dh_params);
> +        gnutls_certificate_set_dh_params(box->data.cert, box->dh_params);
>      }
> +    creds->parent_obj.box = g_steal_pointer(&box);
>
>      return 0;
>  }
>
>
> -static void
> -qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds)
> -{
> -    if (creds->data) {
> -        gnutls_certificate_free_credentials(creds->data);
> -        creds->data = NULL;
> -    }
> -}
> -
> -
>  #else /* ! CONFIG_GNUTLS */
>
>
> @@ -700,13 +697,6 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509
> *creds G_GNUC_UNUSED,
>  }
>
>
> -static void
> -qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED)
> -{
> -    /* nada */
> -}
> -
> -
>  #endif /* ! CONFIG_GNUTLS */
>
>
> @@ -769,29 +759,17 @@ qcrypto_tls_creds_x509_reload(QCryptoTLSCreds
> *creds, Error **errp)
>  {
>      QCryptoTLSCredsX509 *x509_creds = QCRYPTO_TLS_CREDS_X509(creds);
>      Error *local_err = NULL;
> -    gnutls_certificate_credentials_t creds_data = x509_creds->data;
> -    gnutls_dh_params_t creds_dh_params = creds->dh_params;
> +    QCryptoTLSCredsBox *creds_box = creds->box;
>
> -    x509_creds->data = NULL;
> -    creds->dh_params = NULL;
> +    creds->box = NULL;
>      qcrypto_tls_creds_x509_load(x509_creds, &local_err);
>      if (local_err) {
> -        qcrypto_tls_creds_x509_unload(x509_creds);
> -        if (creds->dh_params) {
> -            gnutls_dh_params_deinit(creds->dh_params);
> -        }
> -        x509_creds->data = creds_data;
> -        creds->dh_params = creds_dh_params;
> +        creds->box = creds_box;
>          error_propagate(errp, local_err);
>          return false;
>      }
>
> -    if (creds_data) {
> -        gnutls_certificate_free_credentials(creds_data);
> -    }
> -    if (creds_dh_params) {
> -        gnutls_dh_params_deinit(creds_dh_params);
> -    }
> +    qcrypto_tls_creds_box_unref(creds_box);
>      return true;
>  }
>
> @@ -824,7 +802,6 @@ qcrypto_tls_creds_x509_finalize(Object *obj)
>      QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
>
>      g_free(creds->passwordid);
> -    qcrypto_tls_creds_x509_unload(creds);
>  }
>
>
> diff --git a/crypto/tlssession.c b/crypto/tlssession.c
> index 77f334add3..a1dc3b3ce0 100644
> --- a/crypto/tlssession.c
> +++ b/crypto/tlssession.c
> @@ -38,6 +38,7 @@
>
>  struct QCryptoTLSSession {
>      QCryptoTLSCreds *creds;
> +    QCryptoTLSCredsBox *credsbox;
>      gnutls_session_t handle;
>      char *hostname;
>      char *authzid;
> @@ -78,6 +79,7 @@ qcrypto_tls_session_free(QCryptoTLSSession *session)
>      g_free(session->hostname);
>      g_free(session->peername);
>      g_free(session->authzid);
> +    qcrypto_tls_creds_box_unref(session->credsbox);
>      object_unref(OBJECT(session->creds));
>      qemu_mutex_destroy(&session->lock);
>      g_free(session);
> @@ -206,63 +208,31 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds,
>          goto error;
>      }
>
> -    if (object_dynamic_cast(OBJECT(creds),
> -                            TYPE_QCRYPTO_TLS_CREDS_ANON)) {
> -        QCryptoTLSCredsAnon *acreds = QCRYPTO_TLS_CREDS_ANON(creds);
> -        if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
> -            ret = gnutls_credentials_set(session->handle,
> -                                         GNUTLS_CRD_ANON,
> -                                         acreds->data.server);
> -        } else {
> -            ret = gnutls_credentials_set(session->handle,
> -                                         GNUTLS_CRD_ANON,
> -                                         acreds->data.client);
> -        }
> -        if (ret < 0) {
> -            error_setg(errp, "Cannot set session credentials: %s",
> -                       gnutls_strerror(ret));
> -            goto error;
> -        }
> -    } else if (object_dynamic_cast(OBJECT(creds),
> -                                   TYPE_QCRYPTO_TLS_CREDS_PSK)) {
> -        QCryptoTLSCredsPSK *pcreds = QCRYPTO_TLS_CREDS_PSK(creds);
> -        if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
> -            ret = gnutls_credentials_set(session->handle,
> -                                         GNUTLS_CRD_PSK,
> -                                         pcreds->data.server);
> -        } else {
> -            ret = gnutls_credentials_set(session->handle,
> -                                         GNUTLS_CRD_PSK,
> -                                         pcreds->data.client);
> -        }
> -        if (ret < 0) {
> -            error_setg(errp, "Cannot set session credentials: %s",
> -                       gnutls_strerror(ret));
> -            goto error;
> -        }
> -    } else if (object_dynamic_cast(OBJECT(creds),
> -                                   TYPE_QCRYPTO_TLS_CREDS_X509)) {
> -        QCryptoTLSCredsX509 *tcreds = QCRYPTO_TLS_CREDS_X509(creds);
> +    ret = gnutls_credentials_set(session->handle,
> +                                 creds->box->type,
> +                                 creds->box->data.any);
> +    if (ret < 0) {
> +        error_setg(errp, "Cannot set session credentials: %s",
> +                   gnutls_strerror(ret));
> +        goto error;
> +    }
>
> -        ret = gnutls_credentials_set(session->handle,
> -                                     GNUTLS_CRD_CERTIFICATE,
> -                                     tcreds->data);
> -        if (ret < 0) {
> -            error_setg(errp, "Cannot set session credentials: %s",
> -                       gnutls_strerror(ret));
> -            goto error;
> -        }
> +    /*
> +     * creds->box->data.any must be kept alive for as long
> +     * as the gnutls_session_t is alive, so acquire a ref
> +     */
> +    qcrypto_tls_creds_box_ref(creds->box);
> +    session->credsbox = creds->box;
>
> -        if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
> -            /* This requests, but does not enforce a client cert.
> -             * The cert checking code later does enforcement */
> -            gnutls_certificate_server_set_request(session->handle,
> -                                                  GNUTLS_CERT_REQUEST);
> -        }
> -    } else {
> -        error_setg(errp, "Unsupported TLS credentials type %s",
> -                   object_get_typename(OBJECT(creds)));
> -        goto error;
> +    if (object_dynamic_cast(OBJECT(creds),
> +                            TYPE_QCRYPTO_TLS_CREDS_X509) &&
> +        creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
> +        /*
> +         * This requests, but does not enforce a client cert.
> +         * The cert checking code later does enforcement
> +         */
> +        gnutls_certificate_server_set_request(session->handle,
> +                                              GNUTLS_CERT_REQUEST);
>      }
>
>      gnutls_transport_set_ptr(session->handle, session);
> --
> 2.51.1
>
>

[-- Attachment #2: Type: text/html, Size: 27687 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 15/21] crypto: make TLS credentials structs private
  2025-10-30 14:49 ` [PATCH 15/21] crypto: make TLS credentials structs private Daniel P. Berrangé
@ 2025-10-31 11:25   ` Marc-André Lureau
  0 siblings, 0 replies; 44+ messages in thread
From: Marc-André Lureau @ 2025-10-31 11:25 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, devel

[-- Attachment #1: Type: text/plain, Size: 2523 bytes --]

On Thu, Oct 30, 2025 at 6:50 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:

> Now that the TLS session code no longer needs to look at the TLS
> credential structs, they can be made private.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> ---
>  crypto/tlscredsanon.c |  3 +++
>  crypto/tlscredspriv.h | 15 ---------------
>  crypto/tlscredspsk.c  |  5 +++++
>  crypto/tlscredsx509.c |  6 ++++++
>  4 files changed, 14 insertions(+), 15 deletions(-)
>
> diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c
> index 0a728ccbf6..69ed1d792a 100644
> --- a/crypto/tlscredsanon.c
> +++ b/crypto/tlscredsanon.c
> @@ -31,6 +31,9 @@
>
>  #include <gnutls/gnutls.h>
>
> +struct QCryptoTLSCredsAnon {
> +    QCryptoTLSCreds parent_obj;
> +};
>
>  static int
>  qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
> diff --git a/crypto/tlscredspriv.h b/crypto/tlscredspriv.h
> index 4e6dffa22f..69dac02437 100644
> --- a/crypto/tlscredspriv.h
> +++ b/crypto/tlscredspriv.h
> @@ -37,21 +37,6 @@ struct QCryptoTLSCreds {
>      QCryptoTLSCredsBox *box;
>  };
>
> -struct QCryptoTLSCredsAnon {
> -    QCryptoTLSCreds parent_obj;
> -};
> -
> -struct QCryptoTLSCredsPSK {
> -    QCryptoTLSCreds parent_obj;
> -    char *username;
> -};
> -
> -struct QCryptoTLSCredsX509 {
> -    QCryptoTLSCreds parent_obj;
> -    bool sanityCheck;
> -    char *passwordid;
> -};
> -
>  #ifdef CONFIG_GNUTLS
>
>  int qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
> diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c
> index 5568f1ad0c..e437985260 100644
> --- a/crypto/tlscredspsk.c
> +++ b/crypto/tlscredspsk.c
> @@ -31,6 +31,11 @@
>
>  #include <gnutls/gnutls.h>
>
> +struct QCryptoTLSCredsPSK {
> +    QCryptoTLSCreds parent_obj;
> +    char *username;
> +};
> +
>  static int
>  lookup_key(const char *pskfile, const char *username, gnutls_datum_t *key,
>             Error **errp)
> diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
> index ef31ea664c..2fc0872627 100644
> --- a/crypto/tlscredsx509.c
> +++ b/crypto/tlscredsx509.c
> @@ -33,6 +33,12 @@
>  #include <gnutls/gnutls.h>
>  #include <gnutls/x509.h>
>
> +struct QCryptoTLSCredsX509 {
> +    QCryptoTLSCreds parent_obj;
> +    bool sanityCheck;
> +    char *passwordid;
> +};
> +
>
>  static int
>  qcrypto_tls_creds_check_cert_times(gnutls_x509_crt_t cert,
> --
> 2.51.1
>
>

[-- Attachment #2: Type: text/html, Size: 3391 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 13/21] crypto: introduce a wrapper around gnutls credentials
  2025-10-31 11:23   ` Marc-André Lureau
@ 2025-10-31 11:27     ` Daniel P. Berrangé
  0 siblings, 0 replies; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-31 11:27 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, devel

On Fri, Oct 31, 2025 at 03:23:51PM +0400, Marc-André Lureau wrote:
> Hi
> 
> On Thu, Oct 30, 2025 at 6:49 PM Daniel P. Berrangé <berrange@redhat.com>
> wrote:
> 
> > The gnutls_credentials_set() method has a very suprising API contract
> > that requires the caller to preserve the passed in credentials pointer
> > for as long as the gnutls_session_t object is alive. QEMU is failing
> > to ensure this happens.
> >
> > In QEMU the GNUTLS credentials object is owned by the QCryptoTLSCreds
> > object instance while the GNUTLS session object is owned by the
> > QCryptoTLSSession object instance. Their lifetimes are not guaranteed
> > to be the same, though in most common usage the credentials will outlive
> > the session. This is notably not the case, however, after the VNC server
> > gained the ability to reload credentials on the fly with:
> >
> >   commit 1f08e3415120637cad7f540d9ceb4dba3136dbdd
> >   Author: Zihao Chang <changzihao1@huawei.com>
> >   Date:   Tue Mar 16 15:58:44 2021 +0800
> >
> >     vnc: support reload x509 certificates for vnc
> >
> > If that is triggered while a VNC client is in the middle of performing
> > a TLS handshake, we might hit a use-after-free.
> >
> > It is difficult to correct this problem because there's no way to deep-
> > clone a GNUTLS credentials object, nor is it reference counted. Thus we
> > introduce a QCryptoTLSCredsBox object whose only purpose is to add
> > reference counting around the GNUTLS credentials object.
> >
> > The DH parameters set against a credentials object also have to be kept
> > alive for as long as the credentials exist. So the box must also hold
> > the DH parameters pointer.
> >
> > Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> >
> 
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> > +#ifndef QCRYPTO_TLSCREDS_BOX_H
> > +#define QCRYPTO_TLSCREDS_BOX_H
> > +
> > +#include "qom/object.h"
> > +
> > +#ifdef CONFIG_GNUTLS
> > +#include <gnutls/gnutls.h>
> > +#endif
> > +
> > +typedef struct QCryptoTLSCredsBox QCryptoTLSCredsBox;
> > +
> > +struct QCryptoTLSCredsBox {
> > +    uint32_t ref;
> > +    bool server;
> > +    int type;
> > +    union {
> > +        void *any;
> >
> 
> since any is used in code to cast the variant to a void *, it may be worth
> a comment to say that all fields are expected to be pointers.

Yep, in practice almost all gnutls _t types are pointers, but
I'll explain that here since it is not obvious to casual
observers.

> 
> 
> > +#ifdef CONFIG_GNUTLS
> > +        gnutls_anon_server_credentials_t anonserver;
> > +        gnutls_anon_client_credentials_t anonclient;
> > +        gnutls_psk_server_credentials_t pskserver;
> > +        gnutls_psk_client_credentials_t pskclient;
> > +        gnutls_certificate_credentials_t cert;
> > +#endif
> > +    } data;
> > +#ifdef CONFIG_GNUTLS
> > +    gnutls_dh_params_t dh_params;
> > +#endif
> > +};
> > +

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 12/21] crypto: introduce method for reloading TLS creds
  2025-10-30 19:43   ` Marc-André Lureau
@ 2025-10-31 11:31     ` Daniel P. Berrangé
  0 siblings, 0 replies; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-31 11:31 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, devel

On Thu, Oct 30, 2025 at 11:43:29PM +0400, Marc-André Lureau wrote:
> On Thu, Oct 30, 2025 at 6:49 PM Daniel P. Berrangé <berrange@redhat.com>
> wrote:
> 
> > This prevents direct access of the class members by the VNC
> > display code.
> >
> > Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> >
> 
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> 
> > ---
> >  crypto/tlscreds.c         | 15 +++++++++++++++
> >  include/crypto/tlscreds.h | 13 +++++++++++++
> >  ui/vnc.c                  |  9 +--------
> >  3 files changed, 29 insertions(+), 8 deletions(-)
> >
> > diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
> > index 49c7eb46a5..9433b4c363 100644
> > --- a/crypto/tlscreds.c
> > +++ b/crypto/tlscreds.c
> > @@ -281,6 +281,21 @@ char *qcrypto_tls_creds_get_priority(QCryptoTLSCreds
> > *creds)
> >  }
> >
> >
> > +bool qcrypto_tls_creds_reload(QCryptoTLSCreds *creds,
> > +                              Error **errp)
> > +{
> > +    QCryptoTLSCredsClass *credscls =
> > QCRYPTO_TLS_CREDS_GET_CLASS(OBJECT(creds));
> > +
> >
> 
> OBJECT() unnecessary here

Ah yes, will remove it.


With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 16/21] crypto: deprecate use of external dh-params.pem file
  2025-10-30 14:49 ` [PATCH 16/21] crypto: deprecate use of external dh-params.pem file Daniel P. Berrangé
@ 2025-10-31 11:32   ` Marc-André Lureau
  2025-10-31 11:38     ` Daniel P. Berrangé
  0 siblings, 1 reply; 44+ messages in thread
From: Marc-André Lureau @ 2025-10-31 11:32 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, devel

[-- Attachment #1: Type: text/plain, Size: 7157 bytes --]

Hi

On Thu, Oct 30, 2025 at 6:50 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:

> GNUTLS has deprecated use of externally provided diffie-hellman
> parameters, since it will automatically negotiate DH params in
> accordance with RFC7919.
>

The doc says:
 Since 3.6.0, DH parameters are negotiated following RFC7919.

But QEMU doesn't require >= 3.6. Add a preliminary patch?


> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> ---
>  crypto/tlscreds.c         | 24 ++++++++----------------
>  crypto/tlscredsanon.c     |  6 ++++--
>  crypto/tlscredspsk.c      |  6 ++++--
>  crypto/tlscredsx509.c     |  4 +++-
>  docs/about/deprecated.rst |  9 +++++++++
>  docs/system/tls.rst       | 12 +++++++-----
>  6 files changed, 35 insertions(+), 26 deletions(-)
>
> diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
> index 798c9712fb..85268f3b57 100644
> --- a/crypto/tlscreds.c
> +++ b/crypto/tlscreds.c
> @@ -22,6 +22,7 @@
>  #include "qapi/error.h"
>  #include "qapi-types-crypto.h"
>  #include "qemu/module.h"
> +#include "qemu/error-report.h"
>  #include "tlscredspriv.h"
>  #include "trace.h"
>
> @@ -38,22 +39,7 @@ qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds
> *creds,
>
>      trace_qcrypto_tls_creds_load_dh(creds, filename ? filename :
> "<generated>");
>
> -    if (filename == NULL) {
> -        ret = gnutls_dh_params_init(dh_params);
> -        if (ret < 0) {
> -            error_setg(errp, "Unable to initialize DH parameters: %s",
> -                       gnutls_strerror(ret));
> -            return -1;
> -        }
> -        ret = gnutls_dh_params_generate2(*dh_params, DH_BITS);
> -        if (ret < 0) {
> -            gnutls_dh_params_deinit(*dh_params);
> -            *dh_params = NULL;
> -            error_setg(errp, "Unable to generate DH parameters: %s",
> -                       gnutls_strerror(ret));
> -            return -1;
> -        }
> -    } else {
> +    if (filename != NULL) {
>          GError *gerr = NULL;
>          gchar *contents;
>          gsize len;
> @@ -67,6 +53,10 @@ qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds
> *creds,
>              g_error_free(gerr);
>              return -1;
>          }
> +        warn_report_once("Use of an external DH parameters file '%s' is "
> +                         "deprecated and will be removed in a future
> release",
> +                         filename);
> +
>          data.data = (unsigned char *)contents;
>          data.size = len;
>          ret = gnutls_dh_params_init(dh_params);
> @@ -87,6 +77,8 @@ qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds
> *creds,
>                         filename, gnutls_strerror(ret));
>              return -1;
>          }
> +    } else {
> +        *dh_params = NULL;
>      }
>
>      return 0;
> diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c
> index 69ed1d792a..777cc4f5bb 100644
> --- a/crypto/tlscredsanon.c
> +++ b/crypto/tlscredsanon.c
> @@ -68,8 +68,10 @@ qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
>              return -1;
>          }
>
> -        gnutls_anon_set_server_dh_params(box->data.anonserver,
> -                                         box->dh_params);
> +        if (box->dh_params) {
> +            gnutls_anon_set_server_dh_params(box->data.anonserver,
> +                                             box->dh_params);
> +        }
>      } else {
>          ret =
> gnutls_anon_allocate_client_credentials(&box->data.anonclient);
>          if (ret < 0) {
> diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c
> index e437985260..801da50625 100644
> --- a/crypto/tlscredspsk.c
> +++ b/crypto/tlscredspsk.c
> @@ -129,8 +129,10 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
>                         gnutls_strerror(ret));
>              goto cleanup;
>          }
> -        gnutls_psk_set_server_dh_params(box->data.pskserver,
> -                                        box->dh_params);
> +        if (box->dh_params) {
> +            gnutls_psk_set_server_dh_params(box->data.pskserver,
> +                                            box->dh_params);
> +        }
>      } else {
>          box = qcrypto_tls_creds_box_new_client(GNUTLS_CRD_PSK);
>
> diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
> index 2fc0872627..7e79af4266 100644
> --- a/crypto/tlscredsx509.c
> +++ b/crypto/tlscredsx509.c
> @@ -684,7 +684,9 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
>                                                   errp) < 0) {
>              return -1;
>          }
> -        gnutls_certificate_set_dh_params(box->data.cert, box->dh_params);
> +        if (box->dh_params) {
> +            gnutls_certificate_set_dh_params(box->data.cert,
> box->dh_params);
> +        }
>      }
>      creds->parent_obj.box = g_steal_pointer(&box);
>
> diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst
> index ca6b3769b5..694a69da64 100644
> --- a/docs/about/deprecated.rst
> +++ b/docs/about/deprecated.rst
> @@ -365,6 +365,15 @@ Options are:
>      - move backing file to NVDIMM storage and keep ``pmem=on``
>        (to have NVDIMM with persistence guaranties).
>
> +Using an external DH (Diffie-Hellman) parameters file (since 10.2)
> +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
> +
> +Loading of external Diffie-Hellman parameters from a 'dh-params.pem'
> +file is deprecated and will be removed with no replacement in a
> +future release. Where no 'dh-params.pem' file is provided, the DH
> +parameters will be automatically negotiated in accordance with
> +RFC7919.
> +
>  Device options
>  --------------
>
> diff --git a/docs/system/tls.rst b/docs/system/tls.rst
> index a4f6781d62..44c4bf04e9 100644
> --- a/docs/system/tls.rst
> +++ b/docs/system/tls.rst
> @@ -251,11 +251,13 @@ When specifying the object, the ``dir`` parameters
> specifies which
>  directory contains the credential files. This directory is expected to
>  contain files with the names mentioned previously, ``ca-cert.pem``,
>  ``server-key.pem``, ``server-cert.pem``, ``client-key.pem`` and
> -``client-cert.pem`` as appropriate. It is also possible to include a set
> -of pre-generated Diffie-Hellman (DH) parameters in a file
> -``dh-params.pem``, which can be created using the
> -``certtool --generate-dh-params`` command. If omitted, QEMU will
> -dynamically generate DH parameters when loading the credentials.
> +``client-cert.pem`` as appropriate.
> +
> +While it is possible to include a set of pre-generated Diffie-Hellman
> +(DH) parameters in a file ``dh-params.pem``, this facility is now
> +deprecated and will be removed in a future release. When omitted the
> +DH parameters will be automatically negotiated in accordance with
> +RFC7919.
>
>  The ``endpoint`` parameter indicates whether the credentials will be
>  used for a network client or server, and determines which PEM files are
> --
> 2.51.1
>
>

[-- Attachment #2: Type: text/html, Size: 9239 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 16/21] crypto: deprecate use of external dh-params.pem file
  2025-10-31 11:32   ` Marc-André Lureau
@ 2025-10-31 11:38     ` Daniel P. Berrangé
  0 siblings, 0 replies; 44+ messages in thread
From: Daniel P. Berrangé @ 2025-10-31 11:38 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, devel

On Fri, Oct 31, 2025 at 03:32:51PM +0400, Marc-André Lureau wrote:
> Hi
> 
> On Thu, Oct 30, 2025 at 6:50 PM Daniel P. Berrangé <berrange@redhat.com>
> wrote:
> 
> > GNUTLS has deprecated use of externally provided diffie-hellman
> > parameters, since it will automatically negotiate DH params in
> > accordance with RFC7919.
> >
> 
> The doc says:
>  Since 3.6.0, DH parameters are negotiated following RFC7919.
> 
> But QEMU doesn't require >= 3.6. Add a preliminary patch?

Oh whoops. I mis-read the meson.build rules. Our gnutls bump
to 3.5.18 was done in:

  commit d4c7ee330cd0ca05cc0c026f845af6711e37b0f7
  Author: Daniel P. Berrangé <berrange@redhat.com>
  Date:   Fri May 14 13:04:09 2021 +0100

    crypto: bump min gnutls to 3.5.18, dropping RHEL-7 support
    
    It has been over two years since RHEL-8 was released, and thus per the
    platform build policy, we no longer need to support RHEL-7 as a build
    target. This lets us increment the minimum required gnutls version
    
    Per repology, current shipping versions are:
    
                 RHEL-8: 3.6.14
          Debian Buster: 3.6.7
     openSUSE Leap 15.2: 3.6.7
       Ubuntu LTS 18.04: 3.5.18
       Ubuntu LTS 20.04: 3.6.13
                FreeBSD: 3.6.15
              Fedora 33: 3.6.16
              Fedora 34: 3.7.1
                OpenBSD: 3.6.15
         macOS HomeBrew: 3.6.15
    

the only one not already on 3.6 was Ubuntu 18.04 and that
is long outside our support matrix. IOW we can easily assume
at least 3.6 these days and this patch is safe on that basis.

I'll prepare another standalone patch to explicit increase
the min version though. Can probably bump gcrypt & nettle min
versions too.


With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 01/21] crypto: remove redundant parameter checking CA certs
  2025-10-30 14:49 ` [PATCH 01/21] crypto: remove redundant parameter checking CA certs Daniel P. Berrangé
@ 2025-10-31 13:57   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 44+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-10-31 13:57 UTC (permalink / raw)
  To: Daniel P. Berrangé, qemu-devel; +Cc: Marc-André Lureau, devel

On 30/10/25 15:49, Daniel P. Berrangé wrote:
> The only caller of qcrypto_tls_creds_check_authority_chain always
> passes 'true' for the 'isCA' parameter. The point of this method
> is to check the CA chani, so no other value would ever make sense.
> 
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
>   crypto/tlscredsx509.c | 7 +++----
>   1 file changed, 3 insertions(+), 4 deletions(-)

Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>



^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 03/21] crypto: replace stat() with access() for credential checks
  2025-10-30 14:49 ` [PATCH 03/21] crypto: replace stat() with access() for credential checks Daniel P. Berrangé
@ 2025-10-31 13:58   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 44+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-10-31 13:58 UTC (permalink / raw)
  To: Daniel P. Berrangé, qemu-devel; +Cc: Marc-André Lureau, devel

On 30/10/25 15:49, Daniel P. Berrangé wrote:
> Readability of the credential files is what matters for our usage,
> so access() is more appropriate than stat().
> 
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
>   crypto/tlscreds.c | 3 +--
>   1 file changed, 1 insertion(+), 2 deletions(-)

Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>



^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 06/21] crypto: use g_autofree when loading x509 credentials
  2025-10-30 14:49 ` [PATCH 06/21] crypto: use g_autofree when loading x509 credentials Daniel P. Berrangé
@ 2025-10-31 13:59   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 44+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-10-31 13:59 UTC (permalink / raw)
  To: Daniel P. Berrangé, qemu-devel; +Cc: Marc-André Lureau, devel

On 30/10/25 15:49, Daniel P. Berrangé wrote:
> This allows removal of goto jumps during loading of the credentials
> and will simplify the diff in following commits.
> 
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
>   crypto/tlscredsx509.c | 35 +++++++++++++++--------------------
>   1 file changed, 15 insertions(+), 20 deletions(-)

Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>



^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 07/21] crypto: remove needless indirection via parent_obj field
  2025-10-30 14:49 ` [PATCH 07/21] crypto: remove needless indirection via parent_obj field Daniel P. Berrangé
  2025-10-30 19:31   ` Marc-André Lureau
@ 2025-10-31 14:00   ` Philippe Mathieu-Daudé
  1 sibling, 0 replies; 44+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-10-31 14:00 UTC (permalink / raw)
  To: Daniel P. Berrangé, qemu-devel; +Cc: Marc-André Lureau, devel

On 30/10/25 15:49, Daniel P. Berrangé wrote:
> The reload method already has a pointer to the parent object in
> the 'creds' parameter that is passed in, so indirect access via
> the subclass 'parent_obj' field is redundant.
> 
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
>   crypto/tlscredsx509.c | 6 +++---
>   1 file changed, 3 insertions(+), 3 deletions(-)

Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>



^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 17/21] crypto: avoid loading the CA certs twice
  2025-10-30 14:49 ` [PATCH 17/21] crypto: avoid loading the CA certs twice Daniel P. Berrangé
@ 2025-10-31 15:08   ` Marc-André Lureau
  0 siblings, 0 replies; 44+ messages in thread
From: Marc-André Lureau @ 2025-10-31 15:08 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, devel

[-- Attachment #1: Type: text/plain, Size: 11610 bytes --]

On Thu, Oct 30, 2025 at 6:50 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:

> The x509 TLS credentials code will load the CA certs once to perform
> sanity chcking on the certs, then discard the certificate objects
> and let gnutls load them a second time.
>
> This introduces a new QCryptoTLSCredsX509Files struct which will
> hold the CA certificates loaded for sanity checking and pass them on
> to gnutls, avoiding the duplicated loading.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> ---
>  crypto/tlscredsx509.c | 141 ++++++++++++++++++++++++++----------------
>  1 file changed, 87 insertions(+), 54 deletions(-)
>
> diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
> index 7e79af4266..6a830af50d 100644
> --- a/crypto/tlscredsx509.c
> +++ b/crypto/tlscredsx509.c
> @@ -40,6 +40,35 @@ struct QCryptoTLSCredsX509 {
>  };
>
>
> +typedef struct QCryptoTLSCredsX509Files QCryptoTLSCredsX509Files;
> +struct QCryptoTLSCredsX509Files {
> +    char *cacertpath;
> +    gnutls_x509_crt_t *cacerts;
> +    unsigned int ncacerts;
> +};
> +
> +static QCryptoTLSCredsX509Files *
> +qcrypto_tls_creds_x509_files_new(void)
> +{
> +    return g_new0(QCryptoTLSCredsX509Files, 1);
> +}
> +
> +
> +static void
> +qcrypto_tls_creds_x509_files_free(QCryptoTLSCredsX509Files *files)
> +{
> +    size_t i;
> +    for (i = 0; i < files->ncacerts; i++) {
> +        gnutls_x509_crt_deinit(files->cacerts[i]);
> +    }
> +    g_free(files->cacerts);
> +    g_free(files->cacertpath);
> +    g_free(files);
> +}
> +
> +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSCredsX509Files,
> +                              qcrypto_tls_creds_x509_files_free);
> +
>  static int
>  qcrypto_tls_creds_check_cert_times(gnutls_x509_crt_t cert,
>                                     const char *certFile,
> @@ -315,11 +344,9 @@ qcrypto_tls_creds_check_cert(QCryptoTLSCredsX509
> *creds,
>
>  static int
>  qcrypto_tls_creds_check_authority_chain(QCryptoTLSCredsX509 *creds,
> +                                        QCryptoTLSCredsX509Files *files,
>                                          gnutls_x509_crt_t *certs,
>                                          unsigned int ncerts,
> -                                        gnutls_x509_crt_t *cacerts,
> -                                        unsigned int ncacerts,
> -                                        const char *cacertFile,
>                                          bool isServer,
>                                          Error **errp)
>  {
> @@ -360,13 +387,13 @@
> qcrypto_tls_creds_check_authority_chain(QCryptoTLSCredsX509 *creds,
>               * reached the root of trust.
>               */
>              return qcrypto_tls_creds_check_cert(
> -                creds, cert_to_check, cacertFile,
> +                creds, cert_to_check, files->cacertpath,
>                  isServer, true, errp);
>          }
> -        for (int i = 0; i < ncacerts; i++) {
> +        for (int i = 0; i < files->ncacerts; i++) {
>              if (gnutls_x509_crt_check_issuer(cert_to_check,
> -                                             cacerts[i])) {
> -                cert_issuer = cacerts[i];
> +                                             files->cacerts[i])) {
> +                cert_issuer = files->cacerts[i];
>                  break;
>              }
>          }
> @@ -374,7 +401,7 @@
> qcrypto_tls_creds_check_authority_chain(QCryptoTLSCredsX509 *creds,
>              break;
>          }
>
> -        if (qcrypto_tls_creds_check_cert(creds, cert_issuer, cacertFile,
> +        if (qcrypto_tls_creds_check_cert(creds, cert_issuer,
> files->cacertpath,
>                                           isServer, true, errp) < 0) {
>              return -1;
>          }
> @@ -394,19 +421,17 @@
> qcrypto_tls_creds_check_authority_chain(QCryptoTLSCredsX509 *creds,
>  }
>
>  static int
> -qcrypto_tls_creds_check_cert_pair(gnutls_x509_crt_t *certs,
> +qcrypto_tls_creds_check_cert_pair(QCryptoTLSCredsX509Files *files,
> +                                  gnutls_x509_crt_t *certs,
>                                    size_t ncerts,
>                                    const char *certFile,
> -                                  gnutls_x509_crt_t *cacerts,
> -                                  size_t ncacerts,
> -                                  const char *cacertFile,
>                                    bool isServer,
>                                    Error **errp)
>  {
>      unsigned int status;
>
>      if (gnutls_x509_crt_list_verify(certs, ncerts,
> -                                    cacerts, ncacerts,
> +                                    files->cacerts, files->ncacerts,
>                                      NULL, 0,
>                                      0, &status) < 0) {
>          error_setg(errp, isServer ?
> @@ -414,7 +439,7 @@ qcrypto_tls_creds_check_cert_pair(gnutls_x509_crt_t
> *certs,
>                     "CA certificate %s" :
>                     "Unable to verify client certificate %s against "
>                     "CA certificate %s",
> -                   certFile, cacertFile);
> +                   certFile, files->cacertpath);
>          return -1;
>      }
>
> @@ -439,7 +464,7 @@ qcrypto_tls_creds_check_cert_pair(gnutls_x509_crt_t
> *certs,
>
>          error_setg(errp,
>                     "Our own certificate %s failed validation against %s:
> %s",
> -                   certFile, cacertFile, reason);
> +                   certFile, files->cacertpath, reason);
>          return -1;
>      }
>
> @@ -490,15 +515,13 @@ qcrypto_tls_creds_load_cert_list(QCryptoTLSCredsX509
> *creds,
>
>  static int
>  qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
> +                                    QCryptoTLSCredsX509Files *files,
>                                      bool isServer,
> -                                    const char *cacertFile,
>                                      const char *certFile,
>                                      Error **errp)
>  {
>      gnutls_x509_crt_t *certs = NULL;
>      unsigned int ncerts = 0;
> -    gnutls_x509_crt_t *cacerts = NULL;
> -    unsigned int ncacerts = 0;
>      size_t i;
>      int ret = -1;
>
> @@ -514,16 +537,6 @@
> qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
>          }
>      }
>
> -    if (qcrypto_tls_creds_load_cert_list(creds,
> -                                         cacertFile,
> -                                         &cacerts,
> -                                         &ncacerts,
> -                                         isServer,
> -                                         true,
> -                                         errp) < 0) {
> -        goto cleanup;
> -    }
> -
>      for (i = 0; i < ncerts; i++) {
>          if (qcrypto_tls_creds_check_cert(creds,
>                                           certs[i], certFile,
> @@ -533,17 +546,14 @@
> qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
>      }
>
>      if (ncerts &&
> -        qcrypto_tls_creds_check_authority_chain(creds,
> +        qcrypto_tls_creds_check_authority_chain(creds, files,
>                                                  certs, ncerts,
> -                                                cacerts, ncacerts,
> -                                                cacertFile, isServer,
> -                                                errp) < 0) {
> +                                                isServer, errp) < 0) {
>          goto cleanup;
>      }
>
> -    if (ncerts && ncacerts &&
> -        qcrypto_tls_creds_check_cert_pair(certs, ncerts, certFile,
> -                                          cacerts, ncacerts, cacertFile,
> +    if (ncerts &&
> +        qcrypto_tls_creds_check_cert_pair(files, certs, ncerts, certFile,
>                                            isServer, errp) < 0) {
>          goto cleanup;
>      }
> @@ -555,21 +565,53 @@
> qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
>          gnutls_x509_crt_deinit(certs[i]);
>      }
>      g_free(certs);
> -    for (i = 0; i < ncacerts; i++) {
> -        gnutls_x509_crt_deinit(cacerts[i]);
> -    }
> -    g_free(cacerts);
>
>      return ret;
>  }
>
>
> +static int
> +qcrypto_tls_creds_x509_load_ca(QCryptoTLSCredsX509 *creds,
> +                               QCryptoTLSCredsBox *box,
> +                               QCryptoTLSCredsX509Files *files,
> +                               bool isServer,
> +                               Error **errp)
> +{
> +    int ret;
> +
> +    if (qcrypto_tls_creds_get_path(&creds->parent_obj,
> +                                   QCRYPTO_TLS_CREDS_X509_CA_CERT,
> +                                   true, &files->cacertpath, errp) < 0) {
> +        return -1;
> +    }
> +
> +    if (qcrypto_tls_creds_load_cert_list(creds,
> +                                         files->cacertpath,
> +                                         &files->cacerts,
> +                                         &files->ncacerts,
> +                                         isServer,
> +                                         true,
> +                                         errp) < 0) {
> +        return -1;
> +    }
> +
> +    ret = gnutls_certificate_set_x509_trust(box->data.cert,
> +                                            files->cacerts,
> files->ncacerts);
> +    if (ret < 0) {
> +        error_setg(errp, "Cannot set CA certificate '%s': %s",
> +                   files->cacertpath, gnutls_strerror(ret));
> +        return -1;
> +    }
> +
> +    return 0;
> +}
> +
>  static int
>  qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
>                              Error **errp)
>  {
>      g_autoptr(QCryptoTLSCredsBox) box = NULL;
> -    g_autofree char *cacert = NULL;
> +    g_autoptr(QCryptoTLSCredsX509Files) files = NULL;
>      g_autofree char *cacrl = NULL;
>      g_autofree char *cert = NULL;
>      g_autofree char *key = NULL;
> @@ -598,9 +640,9 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
>          return -1;
>      }
>
> -    if (qcrypto_tls_creds_get_path(&creds->parent_obj,
> -                                   QCRYPTO_TLS_CREDS_X509_CA_CERT,
> -                                   true, &cacert, errp) < 0) {
> +    files = qcrypto_tls_creds_x509_files_new();
> +
> +    if (qcrypto_tls_creds_x509_load_ca(creds, box, files, isServer, errp)
> < 0) {
>          return -1;
>      }
>
> @@ -631,17 +673,8 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509
> *creds,
>      }
>
>      if (creds->sanityCheck &&
> -        qcrypto_tls_creds_x509_sanity_check(creds, isServer,
> -                                            cacert, cert, errp) < 0) {
> -        return -1;
> -    }
> -
> -    ret = gnutls_certificate_set_x509_trust_file(box->data.cert,
> -                                                 cacert,
> -                                                 GNUTLS_X509_FMT_PEM);
> -    if (ret < 0) {
> -        error_setg(errp, "Cannot load CA certificate '%s': %s",
> -                   cacert, gnutls_strerror(ret));
> +        qcrypto_tls_creds_x509_sanity_check(creds, files, isServer,
> +                                            cert, errp) < 0) {
>          return -1;
>      }
>
> --
> 2.51.1
>
>

[-- Attachment #2: Type: text/html, Size: 14845 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 18/21] crypto: avoid loading the identity certs twice
  2025-10-30 14:49 ` [PATCH 18/21] crypto: avoid loading the identity " Daniel P. Berrangé
@ 2025-11-01  9:25   ` Marc-André Lureau
  0 siblings, 0 replies; 44+ messages in thread
From: Marc-André Lureau @ 2025-11-01  9:25 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, devel

[-- Attachment #1: Type: text/plain, Size: 16090 bytes --]

On Thu, Oct 30, 2025 at 6:50 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:

> The x509 TLS credentials code will load the identity certs once to
> perform sanity chcking on the certs, then discard the certificate
> objects and let gnutls load them a second time.
>
> This extends the previous QCryptoTLSCredsX509Files struct to also
> hold the identity certificates & key loaded for sanity checking
> and pass them on to gnutls, avoiding the duplicated loading.
>
> The unit tests need updating because we now correctly diagnose the
> error scenario where the cert PEM file exists, without its matching
> key PEM file. Previously that error was mistakenly ignored.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>

lgtm
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> ---
>  crypto/tlscredsx509.c                 | 247 +++++++++++++++++---------
>  tests/unit/test-crypto-tlscredsx509.c |   8 +-
>  2 files changed, 164 insertions(+), 91 deletions(-)
>
> diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
> index 6a830af50d..3cb0a6c31f 100644
> --- a/crypto/tlscredsx509.c
> +++ b/crypto/tlscredsx509.c
> @@ -45,6 +45,12 @@ struct QCryptoTLSCredsX509Files {
>      char *cacertpath;
>      gnutls_x509_crt_t *cacerts;
>      unsigned int ncacerts;
> +
> +    char *certpath;
> +    char *keypath;
> +    gnutls_x509_crt_t *certs;
> +    unsigned int ncerts;
> +    gnutls_x509_privkey_t key;
>  };
>
>  static QCryptoTLSCredsX509Files *
> @@ -63,6 +69,13 @@
> qcrypto_tls_creds_x509_files_free(QCryptoTLSCredsX509Files *files)
>      }
>      g_free(files->cacerts);
>      g_free(files->cacertpath);
> +    for (i = 0; i < files->ncerts; i++) {
> +        gnutls_x509_crt_deinit(files->certs[i]);
> +    }
> +    gnutls_x509_privkey_deinit(files->key);
> +    g_free(files->certs);
> +    g_free(files->certpath);
> +    g_free(files->keypath);
>      g_free(files);
>  }
>
> @@ -477,14 +490,13 @@ qcrypto_tls_creds_load_cert_list(QCryptoTLSCredsX509
> *creds,
>                                   const char *certFile,
>                                   gnutls_x509_crt_t **certs,
>                                   unsigned int *ncerts,
> -                                 bool isServer,
> -                                 bool isCA,
>                                   Error **errp)
>  {
>      gnutls_datum_t data;
>      g_autofree char *buf = NULL;
>      gsize buflen;
>      GError *gerr = NULL;
> +    int ret;
>
>      *ncerts = 0;
>      trace_qcrypto_tls_creds_x509_load_cert_list(creds, certFile);
> @@ -499,13 +511,60 @@ qcrypto_tls_creds_load_cert_list(QCryptoTLSCredsX509
> *creds,
>      data.data = (unsigned char *)buf;
>      data.size = strlen(buf);
>
> -    if (gnutls_x509_crt_list_import2(certs, ncerts, &data,
> -                                     GNUTLS_X509_FMT_PEM, 0) < 0) {
> -        error_setg(errp,
> -                   isCA ? "Unable to import CA certificate list %s" :
> -                   (isServer ? "Unable to import server certificate %s" :
> -                    "Unable to import client certificate %s"),
> -                   certFile);
> +    ret = gnutls_x509_crt_list_import2(certs, ncerts, &data,
> +                                       GNUTLS_X509_FMT_PEM, 0);
> +    if (ret < 0) {
> +        error_setg(errp, "Unable to import certificate %s: %s",
> +                   certFile, gnutls_strerror(ret));
> +        return -1;
> +    }
> +
> +    return 0;
> +}
> +
> +
> +static int
> +qcrypto_tls_creds_load_privkey(QCryptoTLSCredsX509 *creds,
> +                               const char *keyFile,
> +                               gnutls_x509_privkey_t *key,
> +                               Error **errp)
> +{
> +    gnutls_datum_t data;
> +    g_autofree char *buf = NULL;
> +    g_autofree char *password = NULL;
> +    gsize buflen;
> +    GError *gerr = NULL;
> +    int ret;
> +
> +    ret = gnutls_x509_privkey_init(key);
> +    if (ret < 0) {
> +        error_setg(errp, "Unable to initialize private key: %s",
> +                   gnutls_strerror(ret));
> +        return -1;
> +    }
> +
> +    if (!g_file_get_contents(keyFile, &buf, &buflen, &gerr)) {
> +        error_setg(errp, "Cannot load private key %s: %s",
> +                   keyFile, gerr->message);
> +        g_error_free(gerr);
> +        return -1;
> +    }
> +
> +    data.data = (unsigned char *)buf;
> +    data.size = strlen(buf);
> +
> +    if (creds->passwordid) {
> +        password = qcrypto_secret_lookup_as_utf8(creds->passwordid,
> +                                                 errp);
> +        if (!password) {
> +            return -1;
> +        }
> +    }
> +
> +    if (gnutls_x509_privkey_import2(*key, &data,
> +                                    GNUTLS_X509_FMT_PEM,
> +                                    password, 0) < 0) {
> +        error_setg(errp, "Unable to import private key %s", keyFile);
>          return -1;
>      }
>
> @@ -517,56 +576,34 @@ static int
>  qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
>                                      QCryptoTLSCredsX509Files *files,
>                                      bool isServer,
> -                                    const char *certFile,
>                                      Error **errp)
>  {
> -    gnutls_x509_crt_t *certs = NULL;
> -    unsigned int ncerts = 0;
>      size_t i;
> -    int ret = -1;
> -
> -    if (certFile) {
> -        if (qcrypto_tls_creds_load_cert_list(creds,
> -                                             certFile,
> -                                             &certs,
> -                                             &ncerts,
> -                                             isServer,
> -                                             false,
> -                                             errp) < 0) {
> -            goto cleanup;
> -        }
> -    }
>
> -    for (i = 0; i < ncerts; i++) {
> +    for (i = 0; i < files->ncerts; i++) {
>          if (qcrypto_tls_creds_check_cert(creds,
> -                                         certs[i], certFile,
> +                                         files->certs[i], files->certpath,
>                                           isServer, i != 0, errp) < 0) {
> -            goto cleanup;
> +            return -1;
>          }
>      }
>
> -    if (ncerts &&
> +    if (files->ncerts &&
>          qcrypto_tls_creds_check_authority_chain(creds, files,
> -                                                certs, ncerts,
> +                                                files->certs,
> files->ncerts,
>                                                  isServer, errp) < 0) {
> -        goto cleanup;
> -    }
> -
> -    if (ncerts &&
> -        qcrypto_tls_creds_check_cert_pair(files, certs, ncerts, certFile,
> -                                          isServer, errp) < 0) {
> -        goto cleanup;
> +        return -1;
>      }
>
> -    ret = 0;
> -
> - cleanup:
> -    for (i = 0; i < ncerts; i++) {
> -        gnutls_x509_crt_deinit(certs[i]);
> +    if (files->ncerts &&
> +        qcrypto_tls_creds_check_cert_pair(files,
> +                                          files->certs, files->ncerts,
> +                                          files->certpath, isServer,
> +                                          errp) < 0) {
> +        return -1;
>      }
> -    g_free(certs);
>
> -    return ret;
> +    return 0;
>  }
>
>
> @@ -589,8 +626,6 @@ qcrypto_tls_creds_x509_load_ca(QCryptoTLSCredsX509
> *creds,
>                                           files->cacertpath,
>                                           &files->cacerts,
>                                           &files->ncacerts,
> -                                         isServer,
> -                                         true,
>                                           errp) < 0) {
>          return -1;
>      }
> @@ -606,6 +641,79 @@ qcrypto_tls_creds_x509_load_ca(QCryptoTLSCredsX509
> *creds,
>      return 0;
>  }
>
> +
> +static int
> +qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds,
> +                                     QCryptoTLSCredsBox *box,
> +                                     QCryptoTLSCredsX509Files *files,
> +                                     bool isServer,
> +                                     Error **errp)
> +{
> +    int ret;
> +
> +    if (isServer) {
> +        if (qcrypto_tls_creds_get_path(&creds->parent_obj,
> +                                       QCRYPTO_TLS_CREDS_X509_SERVER_CERT,
> +                                       true, &files->certpath, errp) < 0
> ||
> +            qcrypto_tls_creds_get_path(&creds->parent_obj,
> +                                       QCRYPTO_TLS_CREDS_X509_SERVER_KEY,
> +                                       true, &files->keypath, errp) < 0) {
> +            return -1;
> +        }
> +    } else {
> +        if (qcrypto_tls_creds_get_path(&creds->parent_obj,
> +                                       QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
> +                                       false, &files->certpath, errp) < 0
> ||
> +            qcrypto_tls_creds_get_path(&creds->parent_obj,
> +                                       QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
> +                                       false, &files->keypath, errp) < 0)
> {
> +            return -1;
> +        }
> +    }
> +
> +    if (!files->certpath &&
> +        !files->keypath) {
> +        return 0;
> +    }
> +    if (files->certpath && !files->keypath) {
> +        error_setg(errp, "Cert '%s' without corresponding key",
> +                   files->certpath);
> +        return -1;
> +    }
> +    if (!files->certpath && files->keypath) {
> +        error_setg(errp, "Key '%s' without corresponding cert",
> +                   files->keypath);
> +        return -1;
> +    }
> +
> +    if (qcrypto_tls_creds_load_cert_list(creds,
> +                                         files->certpath,
> +                                         &files->certs,
> +                                         &files->ncerts,
> +                                         errp) < 0) {
> +        return -1;
> +    }
> +
> +    if (qcrypto_tls_creds_load_privkey(creds,
> +                                       files->keypath,
> +                                       &files->key,
> +                                       errp) < 0) {
> +        return -1;
> +    }
> +
> +    ret = gnutls_certificate_set_x509_key(box->data.cert,
> +                                          files->certs,
> +                                          files->ncerts,
> +                                          files->key);
> +    if (ret < 0) {
> +        error_setg(errp, "Cannot set certificate '%s' & key '%s': %s",
> +                   files->certpath, files->keypath, gnutls_strerror(ret));
> +        return -1;
> +    }
> +    return 0;
> +}
> +
> +
>  static int
>  qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
>                              Error **errp)
> @@ -613,8 +721,6 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
>      g_autoptr(QCryptoTLSCredsBox) box = NULL;
>      g_autoptr(QCryptoTLSCredsX509Files) files = NULL;
>      g_autofree char *cacrl = NULL;
> -    g_autofree char *cert = NULL;
> -    g_autofree char *key = NULL;
>      g_autofree char *dhparams = NULL;
>      bool isServer = (creds->parent_obj.endpoint ==
>                       QCRYPTO_TLS_CREDS_ENDPOINT_SERVER);
> @@ -646,60 +752,27 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509
> *creds,
>          return -1;
>      }
>
> +    if (qcrypto_tls_creds_x509_load_identity(creds, box, files,
> +                                             isServer, errp) < 0) {
> +        return -1;
> +    }
> +
>      if (isServer) {
>          if (qcrypto_tls_creds_get_path(&creds->parent_obj,
>                                         QCRYPTO_TLS_CREDS_X509_CA_CRL,
>                                         false, &cacrl, errp) < 0 ||
> -            qcrypto_tls_creds_get_path(&creds->parent_obj,
> -                                       QCRYPTO_TLS_CREDS_X509_SERVER_CERT,
> -                                       true, &cert, errp) < 0 ||
> -            qcrypto_tls_creds_get_path(&creds->parent_obj,
> -                                       QCRYPTO_TLS_CREDS_X509_SERVER_KEY,
> -                                       true, &key, errp) < 0 ||
>              qcrypto_tls_creds_get_path(&creds->parent_obj,
>                                         QCRYPTO_TLS_CREDS_DH_PARAMS,
>                                         false, &dhparams, errp) < 0) {
>              return -1;
>          }
> -    } else {
> -        if (qcrypto_tls_creds_get_path(&creds->parent_obj,
> -                                       QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
> -                                       false, &cert, errp) < 0 ||
> -            qcrypto_tls_creds_get_path(&creds->parent_obj,
> -                                       QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
> -                                       false, &key, errp) < 0) {
> -            return -1;
> -        }
>      }
>
>      if (creds->sanityCheck &&
> -        qcrypto_tls_creds_x509_sanity_check(creds, files, isServer,
> -                                            cert, errp) < 0) {
> +        qcrypto_tls_creds_x509_sanity_check(creds, files, isServer, errp)
> < 0) {
>          return -1;
>      }
>
> -    if (cert != NULL && key != NULL) {
> -        char *password = NULL;
> -        if (creds->passwordid) {
> -            password = qcrypto_secret_lookup_as_utf8(creds->passwordid,
> -                                                     errp);
> -            if (!password) {
> -                return -1;
> -            }
> -        }
> -        ret = gnutls_certificate_set_x509_key_file2(box->data.cert,
> -                                                    cert, key,
> -                                                    GNUTLS_X509_FMT_PEM,
> -                                                    password,
> -                                                    0);
> -        g_free(password);
> -        if (ret < 0) {
> -            error_setg(errp, "Cannot load certificate '%s' & key '%s':
> %s",
> -                       cert, key, gnutls_strerror(ret));
> -            return -1;
> -        }
> -    }
> -
>      if (cacrl != NULL) {
>          ret = gnutls_certificate_set_x509_crl_file(box->data.cert,
>                                                     cacrl,
> diff --git a/tests/unit/test-crypto-tlscredsx509.c
> b/tests/unit/test-crypto-tlscredsx509.c
> index a5f21728d4..b1ad7d5c0d 100644
> --- a/tests/unit/test-crypto-tlscredsx509.c
> +++ b/tests/unit/test-crypto-tlscredsx509.c
> @@ -95,16 +95,16 @@ static void test_tls_creds(const void *opaque)
>          if (access(data->crt, R_OK) == 0) {
>              g_assert(link(data->crt,
>                            CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) ==
> 0);
> +            g_assert(link(KEYFILE,
> +                          CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) ==
> 0);
>          }
> -        g_assert(link(KEYFILE,
> -                      CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0);
>      } else {
>          if (access(data->crt, R_OK) == 0) {
>              g_assert(link(data->crt,
>                            CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) ==
> 0);
> +            g_assert(link(KEYFILE,
> +                          CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) ==
> 0);
>          }
> -        g_assert(link(KEYFILE,
> -                      CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0);
>      }
>
>      creds = test_tls_creds_create(
> --
> 2.51.1
>
>

[-- Attachment #2: Type: text/html, Size: 20742 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 19/21] crypto: expand logic to cope with multiple certificate identities
  2025-10-30 14:49 ` [PATCH 19/21] crypto: expand logic to cope with multiple certificate identities Daniel P. Berrangé
@ 2025-11-02  8:11   ` Marc-André Lureau
  0 siblings, 0 replies; 44+ messages in thread
From: Marc-André Lureau @ 2025-11-02  8:11 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, devel

[-- Attachment #1: Type: text/plain, Size: 11589 bytes --]

Hi

On Thu, Oct 30, 2025 at 6:50 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:

> Currently only a single set of certificates can be loaded for a
> server / client. Certificates are created using a particular
> key algorithm and in some scenarios it can be useful to support
> multiple algorithms in parallel. This requires the ability to
> load multiple sets of certificates.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> ---
>  crypto/tlscredsx509.c | 164 ++++++++++++++++++++++++++++--------------
>  1 file changed, 112 insertions(+), 52 deletions(-)
>
> diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
> index 3cb0a6c31f..d7d1f594c0 100644
> --- a/crypto/tlscredsx509.c
> +++ b/crypto/tlscredsx509.c
> @@ -39,6 +39,14 @@ struct QCryptoTLSCredsX509 {
>      char *passwordid;
>  };
>
> +typedef struct QCryptoTLSCredsX509IdentFiles
> QCryptoTLSCredsX509IdentFiles;
> +struct QCryptoTLSCredsX509IdentFiles {
> +    char *certpath;
> +    char *keypath;
> +    gnutls_x509_crt_t *certs;
> +    unsigned int ncerts;
> +    gnutls_x509_privkey_t key;
> +};
>
>  typedef struct QCryptoTLSCredsX509Files QCryptoTLSCredsX509Files;
>  struct QCryptoTLSCredsX509Files {
> @@ -46,11 +54,8 @@ struct QCryptoTLSCredsX509Files {
>      gnutls_x509_crt_t *cacerts;
>      unsigned int ncacerts;
>
> -    char *certpath;
> -    char *keypath;
> -    gnutls_x509_crt_t *certs;
> -    unsigned int ncerts;
> -    gnutls_x509_privkey_t key;
> +    QCryptoTLSCredsX509IdentFiles **identities;
> +    size_t nidentities;
>  };
>
>  static QCryptoTLSCredsX509Files *
> @@ -61,14 +66,9 @@ qcrypto_tls_creds_x509_files_new(void)
>
>
>  static void
> -qcrypto_tls_creds_x509_files_free(QCryptoTLSCredsX509Files *files)
> +qcrypto_tls_creds_x509_ident_files_free(QCryptoTLSCredsX509IdentFiles
> *files)
>  {
>      size_t i;
> -    for (i = 0; i < files->ncacerts; i++) {
> -        gnutls_x509_crt_deinit(files->cacerts[i]);
> -    }
> -    g_free(files->cacerts);
> -    g_free(files->cacertpath);
>      for (i = 0; i < files->ncerts; i++) {
>          gnutls_x509_crt_deinit(files->certs[i]);
>      }
> @@ -79,6 +79,26 @@
> qcrypto_tls_creds_x509_files_free(QCryptoTLSCredsX509Files *files)
>      g_free(files);
>  }
>
> +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSCredsX509IdentFiles,
> +                              qcrypto_tls_creds_x509_ident_files_free);
> +
> +
> +static void
> +qcrypto_tls_creds_x509_files_free(QCryptoTLSCredsX509Files *files)
> +{
> +    size_t i;
> +    for (i = 0; i < files->ncacerts; i++) {
> +        gnutls_x509_crt_deinit(files->cacerts[i]);
> +    }
> +    g_free(files->cacerts);
> +    g_free(files->cacertpath);
> +    for (i = 0; i < files->nidentities; i++) {
> +        qcrypto_tls_creds_x509_ident_files_free(files->identities[i]);
> +    }
> +    g_free(files->identities);
> +    g_free(files);
> +}
> +
>  G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSCredsX509Files,
>                                qcrypto_tls_creds_x509_files_free);
>
> @@ -573,33 +593,32 @@ qcrypto_tls_creds_load_privkey(QCryptoTLSCredsX509
> *creds,
>
>
>  static int
> -qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
> -                                    QCryptoTLSCredsX509Files *files,
> -                                    bool isServer,
> -                                    Error **errp)
> +qcrypto_tls_creds_x509_sanity_check_identity(QCryptoTLSCredsX509 *creds,
> +                                             QCryptoTLSCredsX509Files
> *files,
> +
>  QCryptoTLSCredsX509IdentFiles *ifiles,
> +                                             bool isServer,
> +                                             Error **errp)
>  {
>      size_t i;
>
> -    for (i = 0; i < files->ncerts; i++) {
> +    for (i = 0; i < ifiles->ncerts; i++) {
>          if (qcrypto_tls_creds_check_cert(creds,
> -                                         files->certs[i], files->certpath,
> +                                         ifiles->certs[i],
> ifiles->certpath,
>                                           isServer, i != 0, errp) < 0) {
>              return -1;
>          }
>      }
>
> -    if (files->ncerts &&
> +    if (ifiles->ncerts &&
>          qcrypto_tls_creds_check_authority_chain(creds, files,
> -                                                files->certs,
> files->ncerts,
> +                                                ifiles->certs,
> ifiles->ncerts,
>                                                  isServer, errp) < 0) {
>          return -1;
>      }
>
> -    if (files->ncerts &&
> -        qcrypto_tls_creds_check_cert_pair(files,
> -                                          files->certs, files->ncerts,
> -                                          files->certpath, isServer,
> -                                          errp) < 0) {
> +    if (ifiles->ncerts &&
> +        qcrypto_tls_creds_check_cert_pair(files, ifiles->certs,
> ifiles->ncerts,
> +                                          ifiles->certpath, isServer,
> errp) < 0) {
>          return -1;
>      }
>
> @@ -607,6 +626,26 @@
> qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
>  }
>
>
> +static int
> +qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
> +                                    QCryptoTLSCredsX509Files *files,
> +                                    bool isServer,
> +                                    Error **errp)
> +{
> +    size_t i;
> +    for (i = 0; i < files->nidentities; i++) {
> +        if (qcrypto_tls_creds_x509_sanity_check_identity(creds,
> +                                                         files,
> +
>  files->identities[i],
> +                                                         isServer,
> +                                                         errp) < 0) {
> +            return -1;
> +        }
> +    }
> +    return 0;
> +}
> +
> +
>  static int
>  qcrypto_tls_creds_x509_load_ca(QCryptoTLSCredsX509 *creds,
>                                 QCryptoTLSCredsBox *box,
> @@ -642,48 +681,38 @@ qcrypto_tls_creds_x509_load_ca(QCryptoTLSCredsX509
> *creds,
>  }
>
>
> -static int
> +static QCryptoTLSCredsX509IdentFiles *
>  qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds,
>                                       QCryptoTLSCredsBox *box,
> -                                     QCryptoTLSCredsX509Files *files,
> -                                     bool isServer,
> +                                     const char *certbase,
> +                                     const char *keybase,
> +                                     bool isOptional,
>                                       Error **errp)
>  {
> +    g_autoptr(QCryptoTLSCredsX509IdentFiles) files =
> +        g_new0(QCryptoTLSCredsX509IdentFiles, 1);
>      int ret;
>
> -    if (isServer) {
> -        if (qcrypto_tls_creds_get_path(&creds->parent_obj,
> -                                       QCRYPTO_TLS_CREDS_X509_SERVER_CERT,
> -                                       true, &files->certpath, errp) < 0
> ||
> -            qcrypto_tls_creds_get_path(&creds->parent_obj,
> -                                       QCRYPTO_TLS_CREDS_X509_SERVER_KEY,
> -                                       true, &files->keypath, errp) < 0) {
> -            return -1;
> -        }
> -    } else {
> -        if (qcrypto_tls_creds_get_path(&creds->parent_obj,
> -                                       QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
> -                                       false, &files->certpath, errp) < 0
> ||
> -            qcrypto_tls_creds_get_path(&creds->parent_obj,
> -                                       QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
> -                                       false, &files->keypath, errp) < 0)
> {
> -            return -1;
> -        }
> +    if (qcrypto_tls_creds_get_path(&creds->parent_obj, certbase,
> +                                   !isOptional, &files->certpath, errp) <
> 0 ||
> +        qcrypto_tls_creds_get_path(&creds->parent_obj, keybase,
> +                                   !isOptional, &files->keypath, errp) <
> 0) {
> +        return NULL;
>      }
>
>      if (!files->certpath &&
>          !files->keypath) {
> -        return 0;
> +        return NULL;
>      }
>      if (files->certpath && !files->keypath) {
>          error_setg(errp, "Cert '%s' without corresponding key",
>                     files->certpath);
> -        return -1;
> +        return NULL;
>      }
>      if (!files->certpath && files->keypath) {
>          error_setg(errp, "Key '%s' without corresponding cert",
>                     files->keypath);
> -        return -1;
> +        return NULL;
>      }
>
>      if (qcrypto_tls_creds_load_cert_list(creds,
> @@ -691,14 +720,14 @@
> qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds,
>                                           &files->certs,
>                                           &files->ncerts,
>                                           errp) < 0) {
> -        return -1;
> +        return NULL;
>      }
>
>      if (qcrypto_tls_creds_load_privkey(creds,
>                                         files->keypath,
>                                         &files->key,
>                                         errp) < 0) {
> -        return -1;
> +        return NULL;
>      }
>
>      ret = gnutls_certificate_set_x509_key(box->data.cert,
> @@ -708,8 +737,39 @@
> qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds,
>      if (ret < 0) {
>          error_setg(errp, "Cannot set certificate '%s' & key '%s': %s",
>                     files->certpath, files->keypath, gnutls_strerror(ret));
> +        return NULL;
> +    }
> +    return g_steal_pointer(&files);
> +}
> +
> +
> +static int
> +qcrypto_tls_creds_x509_load_identities(QCryptoTLSCredsX509 *creds,
> +                                       QCryptoTLSCredsBox *box,
> +                                       QCryptoTLSCredsX509Files *files,
> +                                       bool isServer,
> +                                       Error **errp)
> +{
> +    QCryptoTLSCredsX509IdentFiles *ifiles;
> +
> +    ifiles = qcrypto_tls_creds_x509_load_identity(
> +        creds, box,
> +        isServer ?
> +        QCRYPTO_TLS_CREDS_X509_SERVER_CERT :
> +        QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
> +        isServer ?
> +        QCRYPTO_TLS_CREDS_X509_SERVER_KEY :
> +        QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
> +        !isServer, errp);
> +    if (!ifiles) {
>          return -1;
>      }
> +
> +    files->identities = g_renew(QCryptoTLSCredsX509IdentFiles *,
> +                                files->identities,
> +                                files->nidentities + 1);
> +    files->identities[files->nidentities++] = ifiles;
> +
>      return 0;
>  }
>
> @@ -752,8 +812,8 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
>          return -1;
>      }
>
> -    if (qcrypto_tls_creds_x509_load_identity(creds, box, files,
> -                                             isServer, errp) < 0) {
> +    if (qcrypto_tls_creds_x509_load_identities(creds, box, files,
> +                                               isServer, errp) < 0) {
>          return -1;
>      }
>
> --
> 2.51.1
>
>

[-- Attachment #2: Type: text/html, Size: 14943 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 20/21] crypto: support upto 5 parallel certificate identities
  2025-10-30 14:49 ` [PATCH 20/21] crypto: support upto 5 parallel " Daniel P. Berrangé
@ 2025-11-02  8:25   ` Marc-André Lureau
  0 siblings, 0 replies; 44+ messages in thread
From: Marc-André Lureau @ 2025-11-02  8:25 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, devel

[-- Attachment #1: Type: text/plain, Size: 13300 bytes --]

Hi

On Thu, Oct 30, 2025 at 6:50 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:

> The default (required) identity is stored in server-cert.pem /
> client-cert.pem and server-key.pem / client-key.pem.
>
> The 4 extra (optional) identities are stored in server-cert-$N.pem /
> client-cert-$N.pem and server-key-$N.pem / client-key-$N.pem. The
> numbering starts at 0 and the first missing cert/key pair will
> terminate the loading process.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> ---
>  crypto/tlscreds.c             | 10 +++++-
>  crypto/tlscredspriv.h         |  3 ++
>  crypto/tlscredsx509.c         | 68 ++++++++++++++++++++++++++++-------
>  crypto/tlssession.c           |  1 +
>  crypto/trace-events           |  1 +
>  docs/system/tls.rst           | 54 ++++++++++++++++++++++++++--
>  include/crypto/tlscredsx509.h |  6 ++++
>  7 files changed, 127 insertions(+), 16 deletions(-)
>
> diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
> index 85268f3b57..b7e77f6285 100644
> --- a/crypto/tlscreds.c
> +++ b/crypto/tlscreds.c
> @@ -85,6 +85,14 @@ qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds
> *creds,
>  }
>
>
> +char *
> +qcrypto_tls_creds_build_path(QCryptoTLSCreds *creds,
> +                             const char *filename)
> +{
> +    return g_strdup_printf("%s/%s", creds->dir, filename);
> +}
> +
> +
>  int
>  qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
>                             const char *filename,
> @@ -94,7 +102,7 @@ qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
>  {
>      int ret = -1;
>
> -    *cred = g_strdup_printf("%s/%s", creds->dir, filename);
> +    *cred = qcrypto_tls_creds_build_path(creds, filename);
>
>      if (access(*cred, R_OK) < 0) {
>          if (errno == ENOENT && !required) {
> diff --git a/crypto/tlscredspriv.h b/crypto/tlscredspriv.h
> index 69dac02437..8f2d096c7f 100644
> --- a/crypto/tlscredspriv.h
> +++ b/crypto/tlscredspriv.h
> @@ -39,6 +39,9 @@ struct QCryptoTLSCreds {
>
>  #ifdef CONFIG_GNUTLS
>
> +char *qcrypto_tls_creds_build_path(QCryptoTLSCreds *creds,
> +                                   const char *filename);
> +
>  int qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
>                                 const char *filename,
>                                 bool required,
> diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
> index d7d1f594c0..fa92431906 100644
> --- a/crypto/tlscredsx509.c
> +++ b/crypto/tlscredsx509.c
> @@ -686,7 +686,6 @@
> qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds,
>                                       QCryptoTLSCredsBox *box,
>                                       const char *certbase,
>                                       const char *keybase,
> -                                     bool isOptional,
>                                       Error **errp)
>  {
>      g_autoptr(QCryptoTLSCredsX509IdentFiles) files =
> @@ -694,9 +693,9 @@
> qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds,
>      int ret;
>
>      if (qcrypto_tls_creds_get_path(&creds->parent_obj, certbase,
> -                                   !isOptional, &files->certpath, errp) <
> 0 ||
> +                                   false, &files->certpath, errp) < 0 ||
>          qcrypto_tls_creds_get_path(&creds->parent_obj, keybase,
> -                                   !isOptional, &files->keypath, errp) <
> 0) {
> +                                   false, &files->keypath, errp) < 0) {
>          return NULL;
>      }
>
> @@ -705,13 +704,17 @@
> qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds,
>          return NULL;
>      }
>      if (files->certpath && !files->keypath) {
> -        error_setg(errp, "Cert '%s' without corresponding key",
> -                   files->certpath);
> +        g_autofree char *keypath =
> +            qcrypto_tls_creds_build_path(&creds->parent_obj, keybase);
> +        error_setg(errp, "Cert '%s' without corresponding key '%s'",
> +                   files->certpath, keypath);
>          return NULL;
>      }
>      if (!files->certpath && files->keypath) {
> -        error_setg(errp, "Key '%s' without corresponding cert",
> -                   files->keypath);
> +        g_autofree char *certpath =
> +            qcrypto_tls_creds_build_path(&creds->parent_obj, certbase);
> +        error_setg(errp, "Key '%s' without corresponding cert '%s'",
> +                   files->keypath, certpath);
>          return NULL;
>      }
>
> @@ -750,7 +753,9 @@
> qcrypto_tls_creds_x509_load_identities(QCryptoTLSCredsX509 *creds,
>                                         bool isServer,
>                                         Error **errp)
>  {
> +    ERRP_GUARD();
>      QCryptoTLSCredsX509IdentFiles *ifiles;
> +    size_t i;
>
>      ifiles = qcrypto_tls_creds_x509_load_identity(
>          creds, box,
> @@ -760,15 +765,52 @@
> qcrypto_tls_creds_x509_load_identities(QCryptoTLSCredsX509 *creds,
>          isServer ?
>          QCRYPTO_TLS_CREDS_X509_SERVER_KEY :
>          QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
> -        !isServer, errp);
> -    if (!ifiles) {
> +        errp);
> +    if (!ifiles && *errp) {
>          return -1;
>      }
>
> -    files->identities = g_renew(QCryptoTLSCredsX509IdentFiles *,
> -                                files->identities,
> -                                files->nidentities + 1);
> -    files->identities[files->nidentities++] = ifiles;
> +    if (ifiles) {
> +        files->identities = g_renew(QCryptoTLSCredsX509IdentFiles *,
> +                                    files->identities,
> +                                    files->nidentities + 1);
> +        files->identities[files->nidentities++] = ifiles;
> +    }
> +
> +    for (i = 0; i < QCRYPTO_TLS_CREDS_X509_IDENTITY_MAX; i++) {
> +        g_autofree char *cert = g_strdup_printf(
> +            isServer ?
> +            QCRYPTO_TLS_CREDS_X509_SERVER_CERT_N :
> +            QCRYPTO_TLS_CREDS_X509_CLIENT_CERT_N, i);
> +        g_autofree char *key = g_strdup_printf(
> +            isServer ?
> +            QCRYPTO_TLS_CREDS_X509_SERVER_KEY_N :
> +            QCRYPTO_TLS_CREDS_X509_CLIENT_KEY_N, i);
> +
> +        ifiles = qcrypto_tls_creds_x509_load_identity(creds, box,
> +                                                      cert, key, errp);
> +        if (!ifiles && *errp) {
> +            return -1;
> +        }
> +        if (!ifiles) {
> +            break;
> +        }
> +
> +        files->identities = g_renew(QCryptoTLSCredsX509IdentFiles *,
> +                                    files->identities,
> +                                    files->nidentities + 1);
> +        files->identities[files->nidentities++] = ifiles;
> +    }
> +
> +    if (files->nidentities == 0 && isServer) {
> +        g_autofree char *certpath = qcrypto_tls_creds_build_path(
> +            &creds->parent_obj, QCRYPTO_TLS_CREDS_X509_SERVER_CERT);
> +        g_autofree char *keypath = qcrypto_tls_creds_build_path(
> +            &creds->parent_obj, QCRYPTO_TLS_CREDS_X509_SERVER_KEY);
> +        error_setg(errp, "Missing server cert '%s' & key '%s'",
> +                   certpath, keypath);
> +        return -1;
> +    }
>
>      return 0;
>  }
> diff --git a/crypto/tlssession.c b/crypto/tlssession.c
> index a1dc3b3ce0..314e3e96ba 100644
> --- a/crypto/tlssession.c
> +++ b/crypto/tlssession.c
> @@ -345,6 +345,7 @@
> qcrypto_tls_session_check_certificate(QCryptoTLSSession *session,
>                  goto error;
>              }
>              session->peername = (char *)g_steal_pointer(&dname.data);
> +            trace_qcrypto_tls_session_check_x509_dn(session,
> session->peername);
>              if (session->authzid) {
>                  bool allow;
>
> diff --git a/crypto/trace-events b/crypto/trace-events
> index d0e33427fa..771f9b8a6e 100644
> --- a/crypto/trace-events
> +++ b/crypto/trace-events
> @@ -21,6 +21,7 @@ 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_check_x509_dn(void *session, const char *dname) "TLS
> session check x509 distinguished name session=%p dname=%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"
>
> diff --git a/docs/system/tls.rst b/docs/system/tls.rst
> index 44c4bf04e9..7cec4ac3df 100644
> --- a/docs/system/tls.rst
> +++ b/docs/system/tls.rst
> @@ -36,8 +36,58 @@ server and exposing it directly to remote browser
> clients. In such a
>  case it might be useful to use a commercial CA to avoid needing to
>  install custom CA certs in the web browsers.
>
> -The recommendation is for the server to keep its certificates in either
> -``/etc/pki/qemu`` or for unprivileged users in ``$HOME/.pki/qemu``.
> +.. _tls_cert_file_naming:
> +
> +Certificate file naming
> +~~~~~~~~~~~~~~~~~~~~~~~
> +
> +In a simple setup, where all QEMU instances on a machine share the
> +same TLS configuration, it is suggested that QEMU certificates be
> +kept in either ``/etc/pki/qemu`` or, for unprivileged users, in
> +``$HOME/.pki/qemu``. Where different QEMU subsystems require
> +different certificate configurations, sub-dirs of these locations
> +may be chosen.
> +
> +The default file names that QEMU will traditionally load are:
> +
> +* ``ca-cert.pem`` - mandatory; for both client and server configurations
> +* ``ca-crl.pem`` - optional; for server configurations only
> +* ``server-cert.pem`` - mandatory; for server configurations only
> +* ``server-key.pem`` - mandatory; for server configurations only
> +* ``client-cert.pem`` - optional; for client configurations only
> +* ``client-key.pem`` - optional; for client configurations only
> +* ``dh-params.pem`` - optional; for server configurations only
> +
> +Since QEMU 10.2.0, there is support for loading upto four additional
> +identities:
> +
> +* ``server-cert-[IDX].pem`` - optional; for server configurations only
> +* ``server-key-[IDX].pem`` - optional; for server configurations only
> +* ``client-cert-[IDX].pem`` - optional; for client configurations only
> +* ``client-key-[IDX].pem`` - optional; for client configurations only
> +
> +where ``-[IDX]`` is one of the digits 0-3. Loading will terminate at
> +the first absent index. The index based certificate files may be used
> +as a replacement for, or in addition to, the traditional non-index
> +based certificate files. The traditional certificate files will be
> +loaded first, if present, then the index based certificates. Where
> +multiple certificates are compatible with a TLS session, the first
> +loaded certificate will preferred. IOW file naming can influence
> +which certificates are used for a session.
> +
> +The use of multiple sets of certificates is intended to allow an
> +incremental transition to certificates using different crytographic
> +algorithms. This allows a newly deployed QEMU to introduce use of
> +stronger cryptographic algorithms that will be preferred when talking
> +to other newly deployed QEMU instances, while retaining compatbility
> +with certificates issued to a historically deployed QEMU. This is
> +notably useful to support live migration from an old QEMU deployed
> +on older operating system releases, which may support fewer crypto
> +algorithm choices than the current OS.
> +
> +The certificate creation commands below will be illustrated using
> +the traditional naming scheme, but their args can be substituted
> +to use the indexed naming in the obvious manner.
>
>  .. _tls_005fgenerate_005fca:
>
> diff --git a/include/crypto/tlscredsx509.h b/include/crypto/tlscredsx509.h
> index c4daba21a6..61b7f73573 100644
> --- a/include/crypto/tlscredsx509.h
> +++ b/include/crypto/tlscredsx509.h
> @@ -37,7 +37,13 @@ typedef struct QCryptoTLSCredsX509Class
> QCryptoTLSCredsX509Class;
>  #define QCRYPTO_TLS_CREDS_X509_SERVER_CERT "server-cert.pem"
>  #define QCRYPTO_TLS_CREDS_X509_CLIENT_KEY "client-key.pem"
>  #define QCRYPTO_TLS_CREDS_X509_CLIENT_CERT "client-cert.pem"
> +#define QCRYPTO_TLS_CREDS_X509_SERVER_KEY_N "server-key-%zu.pem"
> +#define QCRYPTO_TLS_CREDS_X509_SERVER_CERT_N "server-cert-%zu.pem"
> +#define QCRYPTO_TLS_CREDS_X509_CLIENT_KEY_N "client-key-%zu.pem"
> +#define QCRYPTO_TLS_CREDS_X509_CLIENT_CERT_N "client-cert-%zu.pem"
>
> +/* Max number of additional cert/key pairs (ie _N constants) */
> +#define QCRYPTO_TLS_CREDS_X509_IDENTITY_MAX 4
>
>  /**
>   * QCryptoTLSCredsX509:
> --
> 2.51.1
>
>

[-- Attachment #2: Type: text/html, Size: 15785 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 21/21] docs: creation of x509 certs compliant with post-quantum crypto
  2025-10-30 14:49 ` [PATCH 21/21] docs: creation of x509 certs compliant with post-quantum crypto Daniel P. Berrangé
@ 2025-11-02 11:40   ` Marc-André Lureau
  0 siblings, 0 replies; 44+ messages in thread
From: Marc-André Lureau @ 2025-11-02 11:40 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, devel

[-- Attachment #1: Type: text/plain, Size: 4535 bytes --]

On Thu, Oct 30, 2025 at 6:50 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:

> Explain how to alter the certtool commands for creating certficates,
> so that they can use algorithms that are compliant with post-quantum
> crytography standards.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> ---
>  docs/system/tls.rst | 68 +++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 68 insertions(+)
>
> diff --git a/docs/system/tls.rst b/docs/system/tls.rst
> index 7cec4ac3df..03fa1d8166 100644
> --- a/docs/system/tls.rst
> +++ b/docs/system/tls.rst
> @@ -345,6 +345,74 @@ example with VNC:
>
>  .. _tls_005fpsk:
>
> +TLS certificates for Post-Quantum Cryptography
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Given a new enough gnutls release, suitably integrated & configured with
> the
> +operating system crypto policies, QEMU is able to support post-quantum
> +crytography on TLS enabled services, either exclusively or in a hybrid
> mode.
> +
> +In exclusive mode, only a single set of certificates need to be configured
> +for QEMU, with PQC compliant algorithms. Such a QEMU configuration will
> only
> +be able to interoperate with other services (including other QEMU's) that
> +also have PQC enabled. This can result in compatibility concerns during
> the
> +period of transition over to PQC compliant algorithms.
> +
> +In hybrid mode, multiple sets of certificates need to be configured for
> QEMU,
> +at least one set with traditional (non-PQC compliant) algorithms, and at
> least
> +one other set with modern (PQC compliant) algorithms. At time of the TLS
> +handshake, the GNUTLS algorithm priorities should ensure that PQC
> compliant
> +algorithms are negotiated if both sides of the connection support PQC. If
> one
> +side lacks PQC, the TLS handshake should fallback to the non-PQC
> algorithms.
> +This can assist with interoperability during the transition to PQC, but
> has a
> +potential weakness wrt downgrade attacks forcing use of non-PQC
> algorithms.
> +Exclusive PQC mode should be preferred where both peers in the TLS
> connections
> +are known to support PQC.
> +
> +Key generation parameters
> +^^^^^^^^^^^^^^^^^^^^^^^^^
> +
> +To create certificates with PQC compliant algorithms, the ``--key-type``
> +argument must be passed to ``certtool`` when creating private keys. No
> +extra arguments are required for the other ``certtool`` commands, as
> +their behaviour will be determined by the private key type.
> +
> +The typical PQC compliant algorithms to use are ``ML-DSA-44``,
> ``ML-DSA-65``
> +and ``ML-DSA-87``, with ``ML-DSA-65`` being a suitable default choice in
> +the absence of explicit requirements.
> +
> +Taking the example earlier, for creating a key for a client certificate,
> +to use ``ML-DSA-65`` the command line would be modified to look like::
> +
> +   # certtool --generate-privkey --key-type=mldsa65 >
> client-hostNNN-key.pem
> +
> +The equivalent modification applies to the creation of the private keys
> +used for server certs, or root/intermediate CA certs.
> +
> +For hybrid mode, the additional indexed certificate naming must be used.
> +If multiple configured certificates are compatible with the mutually
> +supported crypto algorithms between the client and server, then the
> +first matching certificate will be used.
> +
> +IOW, to ensure that PQC certificates are preferred, they must use a
> +non-index based filename, or use an index that is smaller than any
> +non-PQC certificates. ie, ``server-cert.pem`` for PQC and
> ``server-cert-0.pem``
> +for non-PQC, or ``server-cert-0.pem`` for PQC and ``server-cert-1.pem``
> for
> +non-PQC.
> +
> +Force disabling PQC via crypto priority
> +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> +
> +In the OS configuration for system crypto algorithm priorities has
> +enabled PQC, this can (optionally) be overriden in QEMU configuration
> +disable use of PQC using the ``priority`` parameter to the
> ``tls-creds-x509``
> +object::
> +
> +  NO_MLDSA="-SIGN-ML-DSA-65:-SIGN-ML-DSA-44:-SIGN-ML-DSA-87"
> +
> NO_MLKEM="-GROUP-X25519-MLKEM768:-GROUP-SECP256R1-MLKEM768:-GROUP-SECP384R1-MLKEM1024"
> +  # qemu-nbd --object
> tls-creds-x509,id=tls0,endpoint=server,dir=....,priority=@SYSTEM:$NO_MLDSA:$NO_MLKEM
> +
> +
>  TLS Pre-Shared Keys (PSK)
>  ~~~~~~~~~~~~~~~~~~~~~~~~~
>
> --
> 2.51.1
>
>

[-- Attachment #2: Type: text/html, Size: 5311 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

end of thread, other threads:[~2025-11-02 11:42 UTC | newest]

Thread overview: 44+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-30 14:49 [PATCH 00/21] crypto: support multiple parallel certificate identities Daniel P. Berrangé
2025-10-30 14:49 ` [PATCH 01/21] crypto: remove redundant parameter checking CA certs Daniel P. Berrangé
2025-10-31 13:57   ` Philippe Mathieu-Daudé
2025-10-30 14:49 ` [PATCH 02/21] crypto: add missing free of certs array Daniel P. Berrangé
2025-10-30 14:49 ` [PATCH 03/21] crypto: replace stat() with access() for credential checks Daniel P. Berrangé
2025-10-31 13:58   ` Philippe Mathieu-Daudé
2025-10-30 14:49 ` [PATCH 04/21] crypto: remove redundant access() checks before loading certs Daniel P. Berrangé
2025-10-30 14:49 ` [PATCH 05/21] crypto: move check for TLS creds 'dir' property Daniel P. Berrangé
2025-10-30 14:49 ` [PATCH 06/21] crypto: use g_autofree when loading x509 credentials Daniel P. Berrangé
2025-10-31 13:59   ` Philippe Mathieu-Daudé
2025-10-30 14:49 ` [PATCH 07/21] crypto: remove needless indirection via parent_obj field Daniel P. Berrangé
2025-10-30 19:31   ` Marc-André Lureau
2025-10-31 14:00   ` Philippe Mathieu-Daudé
2025-10-30 14:49 ` [PATCH 08/21] crypto: move release of DH parameters into TLS creds parent Daniel P. Berrangé
2025-10-30 19:31   ` Marc-André Lureau
2025-10-30 14:49 ` [PATCH 09/21] crypto: shorten the endpoint == server check in TLS creds Daniel P. Berrangé
2025-10-30 19:32   ` Marc-André Lureau
2025-10-30 14:49 ` [PATCH 10/21] crypto: remove duplication loading x509 CA cert Daniel P. Berrangé
2025-10-30 19:31   ` Marc-André Lureau
2025-10-30 14:49 ` [PATCH 11/21] crypto: reduce duplication in handling TLS priority strings Daniel P. Berrangé
2025-10-30 19:40   ` Marc-André Lureau
2025-10-30 14:49 ` [PATCH 12/21] crypto: introduce method for reloading TLS creds Daniel P. Berrangé
2025-10-30 19:43   ` Marc-André Lureau
2025-10-31 11:31     ` Daniel P. Berrangé
2025-10-30 14:49 ` [PATCH 13/21] crypto: introduce a wrapper around gnutls credentials Daniel P. Berrangé
2025-10-31 11:23   ` Marc-André Lureau
2025-10-31 11:27     ` Daniel P. Berrangé
2025-10-30 14:49 ` [PATCH 14/21] crypto: fix lifecycle handling of gnutls credentials objects Daniel P. Berrangé
2025-10-31 11:24   ` Marc-André Lureau
2025-10-30 14:49 ` [PATCH 15/21] crypto: make TLS credentials structs private Daniel P. Berrangé
2025-10-31 11:25   ` Marc-André Lureau
2025-10-30 14:49 ` [PATCH 16/21] crypto: deprecate use of external dh-params.pem file Daniel P. Berrangé
2025-10-31 11:32   ` Marc-André Lureau
2025-10-31 11:38     ` Daniel P. Berrangé
2025-10-30 14:49 ` [PATCH 17/21] crypto: avoid loading the CA certs twice Daniel P. Berrangé
2025-10-31 15:08   ` Marc-André Lureau
2025-10-30 14:49 ` [PATCH 18/21] crypto: avoid loading the identity " Daniel P. Berrangé
2025-11-01  9:25   ` Marc-André Lureau
2025-10-30 14:49 ` [PATCH 19/21] crypto: expand logic to cope with multiple certificate identities Daniel P. Berrangé
2025-11-02  8:11   ` Marc-André Lureau
2025-10-30 14:49 ` [PATCH 20/21] crypto: support upto 5 parallel " Daniel P. Berrangé
2025-11-02  8:25   ` Marc-André Lureau
2025-10-30 14:49 ` [PATCH 21/21] docs: creation of x509 certs compliant with post-quantum crypto Daniel P. Berrangé
2025-11-02 11:40   ` Marc-André Lureau

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).