From: "Daniel P. Berrangé" <berrange@redhat.com>
To: qemu-devel@nongnu.org
Cc: "Daniel P. Berrangé" <berrange@redhat.com>,
"Fabiano Rosas" <farosas@suse.de>,
"Paolo Bonzini" <pbonzini@redhat.com>,
"Philippe Mathieu-Daudé" <philmd@linaro.org>,
"Marc-André Lureau" <marcandre.lureau@redhat.com>,
devel@lists.libvirt.org, "Laurent Vivier" <lvivier@redhat.com>
Subject: [PULL 31/32] crypto: support upto 5 parallel certificate identities
Date: Mon, 3 Nov 2025 13:37:25 +0000 [thread overview]
Message-ID: <20251103133727.423041-32-berrange@redhat.com> (raw)
In-Reply-To: <20251103133727.423041-1-berrange@redhat.com>
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.
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
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 3d25efe425..fb09e295a6 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 ecffde67c5..b8d0cd2f18 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -687,7 +687,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 =
@@ -695,9 +694,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;
}
@@ -706,13 +705,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;
}
@@ -751,7 +754,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,
@@ -761,15 +766,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
next prev parent reply other threads:[~2025-11-03 13:45 UTC|newest]
Thread overview: 38+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-03 13:36 [PULL 00/32] Next pr patches Daniel P. Berrangé
2025-11-03 13:36 ` [PULL 01/32] Implement -run-with exit-with-parent=on Daniel P. Berrangé
2025-11-03 13:36 ` [PULL 02/32] tests/qtest: Use exit-with-parent=on in qtest invocations Daniel P. Berrangé
2025-11-03 13:36 ` [PULL 03/32] crypto/hash: Have hashing functions take void * buffer argument Daniel P. Berrangé
2025-11-03 13:36 ` [PULL 04/32] io/channel: Have read/write " Daniel P. Berrangé
2025-11-03 13:36 ` [PULL 05/32] io: add a "blocking" field to QIOChannelSocket Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 06/32] io: flush zerocopy socket error queue on sendmsg failure due to ENOBUF Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 07/32] crypto: bump min gnutls to 3.7.5 Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 08/32] crypto: unconditionally enable gnutls XTS support Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 09/32] crypto: bump min libgcrypt to 1.9.4 Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 10/32] crypto: bump min nettle to 3.7.3 Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 11/32] crypto: drop in-tree XTS cipher mode impl Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 12/32] crypto: remove redundant parameter checking CA certs Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 13/32] crypto: add missing free of certs array Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 14/32] crypto: replace stat() with access() for credential checks Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 15/32] crypto: remove redundant access() checks before loading certs Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 16/32] crypto: move check for TLS creds 'dir' property Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 17/32] crypto: use g_autofree when loading x509 credentials Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 18/32] crypto: remove needless indirection via parent_obj field Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 19/32] crypto: move release of DH parameters into TLS creds parent Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 20/32] crypto: shorten the endpoint == server check in TLS creds Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 21/32] crypto: remove duplication loading x509 CA cert Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 22/32] crypto: reduce duplication in handling TLS priority strings Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 23/32] crypto: introduce method for reloading TLS creds Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 24/32] crypto: introduce a wrapper around gnutls credentials Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 25/32] crypto: fix lifecycle handling of gnutls credentials objects Daniel P. Berrangé
2026-04-03 18:25 ` Maciej S. Szmigiero
2026-04-17 13:31 ` Maciej S. Szmigiero
2026-04-23 13:45 ` Daniel P. Berrange
2026-04-23 19:07 ` Maciej S. Szmigiero
2025-11-03 13:37 ` [PULL 26/32] crypto: make TLS credentials structs private Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 27/32] crypto: deprecate use of external dh-params.pem file Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 28/32] crypto: avoid loading the CA certs twice Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 29/32] crypto: avoid loading the identity " Daniel P. Berrangé
2025-11-03 13:37 ` [PULL 30/32] crypto: expand logic to cope with multiple certificate identities Daniel P. Berrangé
2025-11-03 13:37 ` Daniel P. Berrangé [this message]
2025-11-03 13:37 ` [PULL 32/32] docs: creation of x509 certs compliant with post-quantum crypto Daniel P. Berrangé
2025-11-04 15:19 ` [PULL 00/32] Next pr patches Richard Henderson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251103133727.423041-32-berrange@redhat.com \
--to=berrange@redhat.com \
--cc=devel@lists.libvirt.org \
--cc=farosas@suse.de \
--cc=lvivier@redhat.com \
--cc=marcandre.lureau@redhat.com \
--cc=pbonzini@redhat.com \
--cc=philmd@linaro.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.