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