rpc: add support for loading multiple certs & keys

In the transition to Post-Quantum Cryptography, it will often be
desirable to load multiple sets of certificates, some with RSA/ECC
and some with MLDSA. This extends the TLS context code to support
the loading of many certs, passed as a NULL terminated array.

Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrangé committed 2025-11-24 15:05:09 +00:00
1 parent 74a664edd7
commit d249170bf6
6 files changed
+58 -47

No files matched your search

+2 -1
View File
@@ -54,7 +54,8 @@ provider libvirt {
# file: src/rpc/virnettlscontext.c # file: src/rpc/virnettlscontext.c
# prefix: rpc # prefix: rpc
probe rpc_tls_context_new(void *ctxt, const char *cacert, const char *cacrl, probe rpc_tls_context_new(void *ctxt, const char *cacert, const char *cacrl,
const char *cert, const char *key, int sanityCheckCert, int requireValidCert, int isServer); const char **cert, const char **keys,
int sanityCheckCert, int requireValidCert, int isServer);
probe rpc_tls_context_dispose(void *ctxt); probe rpc_tls_context_dispose(void *ctxt);
probe rpc_tls_context_session_allow(void *ctxt, void *sess, const char *dname); probe rpc_tls_context_session_allow(void *ctxt, void *sess, const char *dname);
probe rpc_tls_context_session_deny(void *ctxt, void *sess, const char *dname); probe rpc_tls_context_session_deny(void *ctxt, void *sess, const char *dname);
+4 -2
View File
@@ -327,6 +327,9 @@ daemonSetupNetworking(virNetServer *srv,
if (config->ca_file || if (config->ca_file ||
config->cert_file || config->cert_file ||
config->key_file) { config->key_file) {
const char *certs[] = { config->cert_file, NULL };
const char *keys[] = { config->key_file, NULL };
if (!config->ca_file) { if (!config->ca_file) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("No CA certificate path set to match server key/cert")); _("No CA certificate path set to match server key/cert"));
@@ -346,8 +349,7 @@ daemonSetupNetworking(virNetServer *srv,
config->ca_file, config->cert_file, config->key_file); config->ca_file, config->cert_file, config->key_file);
if (!(ctxt = virNetTLSContextNewServer(config->ca_file, if (!(ctxt = virNetTLSContextNewServer(config->ca_file,
config->crl_file, config->crl_file,
config->cert_file, certs, keys,
config->key_file,
(const char *const*)config->tls_allowed_dn_list, (const char *const*)config->tls_allowed_dn_list,
config->tls_priority, config->tls_priority,
config->tls_no_sanity_certificate ? false : true, config->tls_no_sanity_certificate ? false : true,
+28 -23
View File
@@ -115,10 +115,11 @@ static int virNetTLSContextLoadCredentials(virNetTLSContext *ctxt,
bool isServer, bool isServer,
const char *cacert, const char *cacert,
const char *cacrl, const char *cacrl,
const char *cert, const char *const *certs,
const char *key) const char *const *keys)
{ {
int err; int err;
size_t i;
if (cacert && cacert[0] != '\0') { if (cacert && cacert[0] != '\0') {
if (virNetTLSContextCheckCertFile("CA certificate", cacert, false) < 0) if (virNetTLSContextCheckCertFile("CA certificate", cacert, false) < 0)
@@ -157,29 +158,29 @@ static int virNetTLSContextLoadCredentials(virNetTLSContext *ctxt,
} }
} }
if (cert && cert[0] != '\0' && key && key[0] != '\0') { for (i = 0; certs[i] != NULL && keys[i] != NULL; i++) {
int rv; int rv;
if ((rv = virNetTLSContextCheckCertFile("certificate", cert, !isServer)) < 0) if ((rv = virNetTLSContextCheckCertFile("certificate", certs[i], !isServer)) < 0)
return -1; return -1;
if (rv == 0 && if (rv == 0 &&
(rv = virNetTLSContextCheckCertFile("private key", key, !isServer)) < 0) (rv = virNetTLSContextCheckCertFile("private key", keys[i], !isServer)) < 0)
return -1; return -1;
if (rv == 0) { if (rv == 0) {
VIR_DEBUG("loading cert and key from %s and %s", cert, key); VIR_DEBUG("loading cert and key from %s and %s", certs[i], keys[i]);
err = err =
gnutls_certificate_set_x509_key_file(ctxt->x509cred, gnutls_certificate_set_x509_key_file(ctxt->x509cred,
cert, key, certs[i], keys[i],
GNUTLS_X509_FMT_PEM); GNUTLS_X509_FMT_PEM);
if (err < 0) { if (err < 0) {
virReportError(VIR_ERR_SYSTEM_ERROR, virReportError(VIR_ERR_SYSTEM_ERROR,
_("Unable to set x509 key and certificate: %1$s, %2$s: %3$s"), _("Unable to set x509 key and certificate: %1$s, %2$s: %3$s"),
key, cert, gnutls_strerror(err)); keys[i], certs[i], gnutls_strerror(err));
return -1; return -1;
} }
} else { } else {
VIR_DEBUG("Skipping non-existent cert %s key %s on client", VIR_DEBUG("Skipping non-existent cert %s key %s on client",
cert, key); certs[i], keys[i]);
} }
} }
@@ -189,8 +190,8 @@ static int virNetTLSContextLoadCredentials(virNetTLSContext *ctxt,
static virNetTLSContext *virNetTLSContextNew(const char *cacert, static virNetTLSContext *virNetTLSContextNew(const char *cacert,
const char *cacrl, const char *cacrl,
const char *cert, const char *const *certs,
const char *key, const char *const *keys,
const char *const *x509dnACL, const char *const *x509dnACL,
const char *priority, const char *priority,
bool sanityCheckCert, bool sanityCheckCert,
@@ -199,7 +200,6 @@ static virNetTLSContext *virNetTLSContextNew(const char *cacert,
{ {
virNetTLSContext *ctxt; virNetTLSContext *ctxt;
int err; int err;
const char *certs[] = { cert, NULL };
if (virNetTLSContextInitialize() < 0) if (virNetTLSContextInitialize() < 0)
return NULL; return NULL;
@@ -228,7 +228,8 @@ static virNetTLSContext *virNetTLSContextNew(const char *cacert,
virNetTLSCertSanityCheck(isServer, cacert, certs) < 0) virNetTLSCertSanityCheck(isServer, cacert, certs) < 0)
goto error; goto error;
if (virNetTLSContextLoadCredentials(ctxt, isServer, cacert, cacrl, cert, key) < 0) if (virNetTLSContextLoadCredentials(ctxt, isServer, cacert, cacrl,
certs, keys) < 0)
goto error; goto error;
ctxt->requireValidCert = requireValidCert; ctxt->requireValidCert = requireValidCert;
@@ -236,8 +237,8 @@ static virNetTLSContext *virNetTLSContextNew(const char *cacert,
ctxt->isServer = isServer; ctxt->isServer = isServer;
PROBE(RPC_TLS_CONTEXT_NEW, PROBE(RPC_TLS_CONTEXT_NEW,
"ctxt=%p cacert=%s cacrl=%s cert=%s key=%s sanityCheckCert=%d requireValidCert=%d isServer=%d", "ctxt=%p cacert=%s cacrl=%s cert=%p key=%p sanityCheckCert=%d requireValidCert=%d isServer=%d",
ctxt, cacert, NULLSTR(cacrl), cert, key, sanityCheckCert, requireValidCert, isServer); ctxt, cacert, NULLSTR(cacrl), certs, keys, sanityCheckCert, requireValidCert, isServer);
return ctxt; return ctxt;
@@ -313,12 +314,14 @@ static virNetTLSContext *virNetTLSContextNewPath(const char *pkipath,
g_autofree char *cacrl = NULL; g_autofree char *cacrl = NULL;
g_autofree char *key = NULL; g_autofree char *key = NULL;
g_autofree char *cert = NULL; g_autofree char *cert = NULL;
const char *certs[] = { cert, NULL };
const char *keys[] = { key, NULL };
if (virNetTLSContextLocateCredentials(pkipath, tryUserPkiPath, isServer, if (virNetTLSContextLocateCredentials(pkipath, tryUserPkiPath, isServer,
&cacert, &cacrl, &cert, &key) < 0) &cacert, &cacrl, &cert, &key) < 0)
return NULL; return NULL;
return virNetTLSContextNew(cacert, cacrl, cert, key, return virNetTLSContextNew(cacert, cacrl, certs, keys,
x509dnACL, priority, sanityCheckCert, x509dnACL, priority, sanityCheckCert,
requireValidCert, isServer); requireValidCert, isServer);
} }
@@ -347,14 +350,14 @@ virNetTLSContext *virNetTLSContextNewClientPath(const char *pkipath,
virNetTLSContext *virNetTLSContextNewServer(const char *cacert, virNetTLSContext *virNetTLSContextNewServer(const char *cacert,
const char *cacrl, const char *cacrl,
const char *cert, const char *const *certs,
const char *key, const char *const *keys,
const char *const *x509dnACL, const char *const *x509dnACL,
const char *priority, const char *priority,
bool sanityCheckCert, bool sanityCheckCert,
bool requireValidCert) bool requireValidCert)
{ {
return virNetTLSContextNew(cacert, cacrl, cert, key, x509dnACL, priority, return virNetTLSContextNew(cacert, cacrl, certs, keys, x509dnACL, priority,
sanityCheckCert, requireValidCert, true); sanityCheckCert, requireValidCert, true);
} }
@@ -369,6 +372,7 @@ int virNetTLSContextReloadForServer(virNetTLSContext *ctxt,
g_autofree char *cert = NULL; g_autofree char *cert = NULL;
g_autofree char *key = NULL; g_autofree char *key = NULL;
const char *certs[] = { cert, NULL }; const char *certs[] = { cert, NULL };
const char *keys[] = { key, NULL };
x509credBak = g_steal_pointer(&ctxt->x509cred); x509credBak = g_steal_pointer(&ctxt->x509cred);
@@ -387,7 +391,8 @@ int virNetTLSContextReloadForServer(virNetTLSContext *ctxt,
if (virNetTLSCertSanityCheck(true, cacert, certs)) if (virNetTLSCertSanityCheck(true, cacert, certs))
goto error; goto error;
if (virNetTLSContextLoadCredentials(ctxt, true, cacert, cacrl, cert, key)) if (virNetTLSContextLoadCredentials(ctxt, true, cacert, cacrl,
certs, keys))
goto error; goto error;
gnutls_certificate_free_credentials(x509credBak); gnutls_certificate_free_credentials(x509credBak);
@@ -404,13 +409,13 @@ int virNetTLSContextReloadForServer(virNetTLSContext *ctxt,
virNetTLSContext *virNetTLSContextNewClient(const char *cacert, virNetTLSContext *virNetTLSContextNewClient(const char *cacert,
const char *cacrl, const char *cacrl,
const char *cert, const char *const *certs,
const char *key, const char *const *keys,
const char *priority, const char *priority,
bool sanityCheckCert, bool sanityCheckCert,
bool requireValidCert) bool requireValidCert)
{ {
return virNetTLSContextNew(cacert, cacrl, cert, key, NULL, priority, return virNetTLSContextNew(cacert, cacrl, certs, keys, NULL, priority,
sanityCheckCert, requireValidCert, false); sanityCheckCert, requireValidCert, false);
} }
+13 -13
View File
@@ -44,21 +44,21 @@ virNetTLSContext *virNetTLSContextNewClientPath(const char *pkipath,
bool requireValidCert); bool requireValidCert);
virNetTLSContext *virNetTLSContextNewServer(const char *cacert, virNetTLSContext *virNetTLSContextNewServer(const char *cacert,
const char *cacrl, const char *cacrl,
const char *cert, const char *const *certs,
const char *key, const char *const *keys,
const char *const *x509dnACL, const char *const *x509dnACL,
const char *priority, const char *priority,
bool sanityCheckCert, bool sanityCheckCert,
bool requireValidCert); bool requireValidCert);
virNetTLSContext *virNetTLSContextNewClient(const char *cacert, virNetTLSContext *virNetTLSContextNewClient(const char *cacert,
const char *cacrl, const char *cacrl,
const char *cert, const char *const *certs,
const char *key, const char *const *keys,
const char *priority, const char *priority,
bool sanityCheckCert, bool sanityCheckCert,
bool requireValidCert); bool requireValidCert);
int virNetTLSContextReloadForServer(virNetTLSContext *ctxt, int virNetTLSContextReloadForServer(virNetTLSContext *ctxt,
bool tryUserPkiPath); bool tryUserPkiPath);
+6 -4
View File
@@ -56,12 +56,14 @@ static int testTLSContextInit(const void *opaque)
struct testTLSContextData *data = (struct testTLSContextData *)opaque; struct testTLSContextData *data = (struct testTLSContextData *)opaque;
virNetTLSContext *ctxt = NULL; virNetTLSContext *ctxt = NULL;
int ret = -1; int ret = -1;
const char *certs[] = { data->crt, NULL };
const char *keys[] = { KEYFILE, NULL };
if (data->isServer) { if (data->isServer) {
ctxt = virNetTLSContextNewServer(data->cacrt, ctxt = virNetTLSContextNewServer(data->cacrt,
NULL, NULL,
data->crt, certs,
KEYFILE, keys,
NULL, NULL,
"NORMAL", "NORMAL",
true, true,
@@ -69,8 +71,8 @@ static int testTLSContextInit(const void *opaque)
} else { } else {
ctxt = virNetTLSContextNewClient(data->cacrt, ctxt = virNetTLSContextNewClient(data->cacrt,
NULL, NULL,
data->crt, certs,
KEYFILE, keys,
"NORMAL", "NORMAL",
true, true,
true); true);
+5 -4
View File
@@ -81,6 +81,9 @@ static int testTLSSessionInit(const void *opaque)
int channel[2]; int channel[2];
bool clientShake = false; bool clientShake = false;
bool serverShake = false; bool serverShake = false;
const char *keys[] = { KEYFILE, NULL };
const char *clientcerts[] = { data->clientcrt, NULL };
const char *servercerts[] = { data->servercrt, NULL };
/* We'll use this for our fake client-server connection */ /* We'll use this for our fake client-server connection */
@@ -102,8 +105,7 @@ static int testTLSSessionInit(const void *opaque)
*/ */
serverCtxt = virNetTLSContextNewServer(data->servercacrt, serverCtxt = virNetTLSContextNewServer(data->servercacrt,
NULL, NULL,
data->servercrt, servercerts, keys,
KEYFILE,
data->wildcards, data->wildcards,
"NORMAL", "NORMAL",
false, false,
@@ -111,8 +113,7 @@ static int testTLSSessionInit(const void *opaque)
clientCtxt = virNetTLSContextNewClient(data->clientcacrt, clientCtxt = virNetTLSContextNewClient(data->clientcacrt,
NULL, NULL,
data->clientcrt, clientcerts, keys,
KEYFILE,
"NORMAL", "NORMAL",
false, false,
true); true);