crypto: expand logic to cope with multiple certificate identities

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.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrangé 2025-10-29 20:46:28 +00:00
parent 8031b5fb1a
commit c497a51481

View file

@ -40,12 +40,8 @@ struct QCryptoTLSCredsX509 {
#include <gnutls/x509.h> #include <gnutls/x509.h>
typedef struct QCryptoTLSCredsX509Files QCryptoTLSCredsX509Files; typedef struct QCryptoTLSCredsX509IdentFiles QCryptoTLSCredsX509IdentFiles;
struct QCryptoTLSCredsX509Files { struct QCryptoTLSCredsX509IdentFiles {
char *cacertpath;
gnutls_x509_crt_t *cacerts;
unsigned int ncacerts;
char *certpath; char *certpath;
char *keypath; char *keypath;
gnutls_x509_crt_t *certs; gnutls_x509_crt_t *certs;
@ -53,6 +49,16 @@ struct QCryptoTLSCredsX509Files {
gnutls_x509_privkey_t key; gnutls_x509_privkey_t key;
}; };
typedef struct QCryptoTLSCredsX509Files QCryptoTLSCredsX509Files;
struct QCryptoTLSCredsX509Files {
char *cacertpath;
gnutls_x509_crt_t *cacerts;
unsigned int ncacerts;
QCryptoTLSCredsX509IdentFiles **identities;
size_t nidentities;
};
static QCryptoTLSCredsX509Files * static QCryptoTLSCredsX509Files *
qcrypto_tls_creds_x509_files_new(void) qcrypto_tls_creds_x509_files_new(void)
{ {
@ -60,6 +66,24 @@ qcrypto_tls_creds_x509_files_new(void)
} }
static void
qcrypto_tls_creds_x509_ident_files_free(QCryptoTLSCredsX509IdentFiles *files)
{
size_t i;
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);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSCredsX509IdentFiles,
qcrypto_tls_creds_x509_ident_files_free);
static void static void
qcrypto_tls_creds_x509_files_free(QCryptoTLSCredsX509Files *files) qcrypto_tls_creds_x509_files_free(QCryptoTLSCredsX509Files *files)
{ {
@ -69,13 +93,10 @@ qcrypto_tls_creds_x509_files_free(QCryptoTLSCredsX509Files *files)
} }
g_free(files->cacerts); g_free(files->cacerts);
g_free(files->cacertpath); g_free(files->cacertpath);
for (i = 0; i < files->ncerts; i++) { for (i = 0; i < files->nidentities; i++) {
gnutls_x509_crt_deinit(files->certs[i]); qcrypto_tls_creds_x509_ident_files_free(files->identities[i]);
} }
gnutls_x509_privkey_deinit(files->key); g_free(files->identities);
g_free(files->certs);
g_free(files->certpath);
g_free(files->keypath);
g_free(files); g_free(files);
} }
@ -572,6 +593,40 @@ qcrypto_tls_creds_load_privkey(QCryptoTLSCredsX509 *creds,
} }
static int
qcrypto_tls_creds_x509_sanity_check_identity(QCryptoTLSCredsX509 *creds,
QCryptoTLSCredsX509Files *files,
QCryptoTLSCredsX509IdentFiles *ifiles,
bool isServer,
Error **errp)
{
size_t i;
for (i = 0; i < ifiles->ncerts; i++) {
if (qcrypto_tls_creds_check_cert(creds,
ifiles->certs[i], ifiles->certpath,
isServer, i != 0, errp) < 0) {
return -1;
}
}
if (ifiles->ncerts &&
qcrypto_tls_creds_check_authority_chain(creds, files,
ifiles->certs, ifiles->ncerts,
isServer, errp) < 0) {
return -1;
}
if (ifiles->ncerts &&
qcrypto_tls_creds_check_cert_pair(files, ifiles->certs, ifiles->ncerts,
ifiles->certpath, isServer, errp) < 0) {
return -1;
}
return 0;
}
static int static int
qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds, qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
QCryptoTLSCredsX509Files *files, QCryptoTLSCredsX509Files *files,
@ -579,30 +634,15 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
Error **errp) Error **errp)
{ {
size_t i; size_t i;
for (i = 0; i < files->nidentities; i++) {
for (i = 0; i < files->ncerts; i++) { if (qcrypto_tls_creds_x509_sanity_check_identity(creds,
if (qcrypto_tls_creds_check_cert(creds, files,
files->certs[i], files->certpath, files->identities[i],
isServer, i != 0, errp) < 0) { isServer,
errp) < 0) {
return -1; return -1;
} }
} }
if (files->ncerts &&
qcrypto_tls_creds_check_authority_chain(creds, files,
files->certs, files->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) {
return -1;
}
return 0; return 0;
} }
@ -642,48 +682,38 @@ qcrypto_tls_creds_x509_load_ca(QCryptoTLSCredsX509 *creds,
} }
static int static QCryptoTLSCredsX509IdentFiles *
qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds, qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds,
QCryptoTLSCredsBox *box, QCryptoTLSCredsBox *box,
QCryptoTLSCredsX509Files *files, const char *certbase,
bool isServer, const char *keybase,
bool isOptional,
Error **errp) Error **errp)
{ {
g_autoptr(QCryptoTLSCredsX509IdentFiles) files =
g_new0(QCryptoTLSCredsX509IdentFiles, 1);
int ret; int ret;
if (isServer) { if (qcrypto_tls_creds_get_path(&creds->parent_obj, certbase,
if (qcrypto_tls_creds_get_path(&creds->parent_obj, !isOptional, &files->certpath, errp) < 0 ||
QCRYPTO_TLS_CREDS_X509_SERVER_CERT, qcrypto_tls_creds_get_path(&creds->parent_obj, keybase,
true, &files->certpath, errp) < 0 || !isOptional, &files->keypath, errp) < 0) {
qcrypto_tls_creds_get_path(&creds->parent_obj, return NULL;
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 && if (!files->certpath &&
!files->keypath) { !files->keypath) {
return 0; return NULL;
} }
if (files->certpath && !files->keypath) { if (files->certpath && !files->keypath) {
error_setg(errp, "Cert '%s' without corresponding key", error_setg(errp, "Cert '%s' without corresponding key",
files->certpath); files->certpath);
return -1; return NULL;
} }
if (!files->certpath && files->keypath) { if (!files->certpath && files->keypath) {
error_setg(errp, "Key '%s' without corresponding cert", error_setg(errp, "Key '%s' without corresponding cert",
files->keypath); files->keypath);
return -1; return NULL;
} }
if (qcrypto_tls_creds_load_cert_list(creds, if (qcrypto_tls_creds_load_cert_list(creds,
@ -691,14 +721,14 @@ qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds,
&files->certs, &files->certs,
&files->ncerts, &files->ncerts,
errp) < 0) { errp) < 0) {
return -1; return NULL;
} }
if (qcrypto_tls_creds_load_privkey(creds, if (qcrypto_tls_creds_load_privkey(creds,
files->keypath, files->keypath,
&files->key, &files->key,
errp) < 0) { errp) < 0) {
return -1; return NULL;
} }
ret = gnutls_certificate_set_x509_key(box->data.cert, ret = gnutls_certificate_set_x509_key(box->data.cert,
@ -708,8 +738,39 @@ qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds,
if (ret < 0) { if (ret < 0) {
error_setg(errp, "Cannot set certificate '%s' & key '%s': %s", error_setg(errp, "Cannot set certificate '%s' & key '%s': %s",
files->certpath, files->keypath, gnutls_strerror(ret)); 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; return -1;
} }
files->identities = g_renew(QCryptoTLSCredsX509IdentFiles *,
files->identities,
files->nidentities + 1);
files->identities[files->nidentities++] = ifiles;
return 0; return 0;
} }
@ -752,8 +813,8 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
return -1; return -1;
} }
if (qcrypto_tls_creds_x509_load_identity(creds, box, files, if (qcrypto_tls_creds_x509_load_identities(creds, box, files,
isServer, errp) < 0) { isServer, errp) < 0) {
return -1; return -1;
} }