]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
rpc: add support for loading multiple certs & keys
authorDaniel P. Berrangé <berrange@redhat.com>
Mon, 3 Nov 2025 17:15:59 +0000 (17:15 +0000)
committerDaniel P. Berrangé <berrange@redhat.com>
Mon, 24 Nov 2025 15:05:09 +0000 (15:05 +0000)
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>
src/libvirt_probes.d
src/remote/remote_daemon.c
src/rpc/virnettlscontext.c
src/rpc/virnettlscontext.h
tests/virnettlscontexttest.c
tests/virnettlssessiontest.c

index 6fac10a2bf1aa6adbef91f832744d5d84e838174..d9e75d979775c9fea097eed8e4b093a6ed2602bd 100644 (file)
@@ -54,7 +54,8 @@ provider libvirt {
        # file: src/rpc/virnettlscontext.c
        # prefix: rpc
        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_session_allow(void *ctxt, void *sess, const char *dname);
        probe rpc_tls_context_session_deny(void *ctxt, void *sess, const char *dname);
index 2973813548d9a65acd7c624eb82c6be8b66dad6e..e7c8f587c4a23ac29eb5ba9849b199ce62609fdc 100644 (file)
@@ -327,6 +327,9 @@ daemonSetupNetworking(virNetServer *srv,
         if (config->ca_file ||
             config->cert_file ||
             config->key_file) {
+            const char *certs[] = { config->cert_file, NULL };
+            const char *keys[] = { config->key_file, NULL };
+
             if (!config->ca_file) {
                 virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                                _("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);
             if (!(ctxt = virNetTLSContextNewServer(config->ca_file,
                                                    config->crl_file,
-                                                   config->cert_file,
-                                                   config->key_file,
+                                                   certs, keys,
                                                    (const char *const*)config->tls_allowed_dn_list,
                                                    config->tls_priority,
                                                    config->tls_no_sanity_certificate ? false : true,
index bb9db90dff315bedf482aa747470d014dbe88ca1..5e9c262b48b87dfd6f71d4f2994e75bd550dec15 100644 (file)
@@ -115,10 +115,11 @@ static int virNetTLSContextLoadCredentials(virNetTLSContext *ctxt,
                                            bool isServer,
                                            const char *cacert,
                                            const char *cacrl,
-                                           const char *cert,
-                                           const char *key)
+                                           const char *const *certs,
+                                           const char *const *keys)
 {
     int err;
+    size_t i;
 
     if (cacert && cacert[0] != '\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;
-        if ((rv = virNetTLSContextCheckCertFile("certificate", cert, !isServer)) < 0)
+        if ((rv = virNetTLSContextCheckCertFile("certificate", certs[i], !isServer)) < 0)
             return -1;
         if (rv == 0 &&
-            (rv = virNetTLSContextCheckCertFile("private key", key, !isServer)) < 0)
+            (rv = virNetTLSContextCheckCertFile("private key", keys[i], !isServer)) < 0)
             return -1;
 
         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 =
                 gnutls_certificate_set_x509_key_file(ctxt->x509cred,
-                                                     cert, key,
+                                                     certs[i], keys[i],
                                                      GNUTLS_X509_FMT_PEM);
             if (err < 0) {
                 virReportError(VIR_ERR_SYSTEM_ERROR,
                                _("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;
             }
         } else {
             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,
                                              const char *cacrl,
-                                             const char *cert,
-                                             const char *key,
+                                             const char *const *certs,
+                                             const char *const *keys,
                                              const char *const *x509dnACL,
                                              const char *priority,
                                              bool sanityCheckCert,
@@ -199,7 +200,6 @@ static virNetTLSContext *virNetTLSContextNew(const char *cacert,
 {
     virNetTLSContext *ctxt;
     int err;
-    const char *certs[] = { cert, NULL };
 
     if (virNetTLSContextInitialize() < 0)
         return NULL;
@@ -228,7 +228,8 @@ static virNetTLSContext *virNetTLSContextNew(const char *cacert,
         virNetTLSCertSanityCheck(isServer, cacert, certs) < 0)
         goto error;
 
-    if (virNetTLSContextLoadCredentials(ctxt, isServer, cacert, cacrl, cert, key) < 0)
+    if (virNetTLSContextLoadCredentials(ctxt, isServer, cacert, cacrl,
+                                        certs, keys) < 0)
         goto error;
 
     ctxt->requireValidCert = requireValidCert;
@@ -236,8 +237,8 @@ static virNetTLSContext *virNetTLSContextNew(const char *cacert,
     ctxt->isServer = isServer;
 
     PROBE(RPC_TLS_CONTEXT_NEW,
-          "ctxt=%p cacert=%s cacrl=%s cert=%s key=%s sanityCheckCert=%d requireValidCert=%d isServer=%d",
-          ctxt, cacert, NULLSTR(cacrl), cert, key, sanityCheckCert, requireValidCert, isServer);
+          "ctxt=%p cacert=%s cacrl=%s cert=%p key=%p sanityCheckCert=%d requireValidCert=%d isServer=%d",
+          ctxt, cacert, NULLSTR(cacrl), certs, keys, sanityCheckCert, requireValidCert, isServer);
 
     return ctxt;
 
@@ -313,12 +314,14 @@ static virNetTLSContext *virNetTLSContextNewPath(const char *pkipath,
     g_autofree char *cacrl = NULL;
     g_autofree char *key = NULL;
     g_autofree char *cert = NULL;
+    const char *certs[] = { cert, NULL };
+    const char *keys[] = { key, NULL };
 
     if (virNetTLSContextLocateCredentials(pkipath, tryUserPkiPath, isServer,
                                           &cacert, &cacrl, &cert, &key) < 0)
         return NULL;
 
-    return virNetTLSContextNew(cacert, cacrl, cert, key,
+    return virNetTLSContextNew(cacert, cacrl, certs, keys,
                                x509dnACL, priority, sanityCheckCert,
                                requireValidCert, isServer);
 }
@@ -347,14 +350,14 @@ virNetTLSContext *virNetTLSContextNewClientPath(const char *pkipath,
 
 virNetTLSContext *virNetTLSContextNewServer(const char *cacert,
                                             const char *cacrl,
-                                            const char *cert,
-                                            const char *key,
+                                            const char *const *certs,
+                                            const char *const *keys,
                                             const char *const *x509dnACL,
                                             const char *priority,
                                             bool sanityCheckCert,
                                             bool requireValidCert)
 {
-    return virNetTLSContextNew(cacert, cacrl, cert, key, x509dnACL, priority,
+    return virNetTLSContextNew(cacert, cacrl, certs, keys, x509dnACL, priority,
                                sanityCheckCert, requireValidCert, true);
 }
 
@@ -369,6 +372,7 @@ int virNetTLSContextReloadForServer(virNetTLSContext *ctxt,
     g_autofree char *cert = NULL;
     g_autofree char *key = NULL;
     const char *certs[] = { cert, NULL };
+    const char *keys[] = { key, NULL };
 
     x509credBak = g_steal_pointer(&ctxt->x509cred);
 
@@ -387,7 +391,8 @@ int virNetTLSContextReloadForServer(virNetTLSContext *ctxt,
     if (virNetTLSCertSanityCheck(true, cacert, certs))
         goto error;
 
-    if (virNetTLSContextLoadCredentials(ctxt, true, cacert, cacrl, cert, key))
+    if (virNetTLSContextLoadCredentials(ctxt, true, cacert, cacrl,
+                                        certs, keys))
         goto error;
 
     gnutls_certificate_free_credentials(x509credBak);
@@ -404,13 +409,13 @@ int virNetTLSContextReloadForServer(virNetTLSContext *ctxt,
 
 virNetTLSContext *virNetTLSContextNewClient(const char *cacert,
                                             const char *cacrl,
-                                            const char *cert,
-                                            const char *key,
+                                            const char *const *certs,
+                                            const char *const *keys,
                                             const char *priority,
                                             bool sanityCheckCert,
                                             bool requireValidCert)
 {
-    return virNetTLSContextNew(cacert, cacrl, cert, key, NULL, priority,
+    return virNetTLSContextNew(cacert, cacrl, certs, keys, NULL, priority,
                                sanityCheckCert, requireValidCert, false);
 }
 
index 11c954ce4bcfeb6fe80a091e5858e7aa65b8ad69..1e67171e3e86227a0bcb0fb6c8ed1b7ecb491860 100644 (file)
@@ -44,21 +44,21 @@ virNetTLSContext *virNetTLSContextNewClientPath(const char *pkipath,
                                                   bool requireValidCert);
 
 virNetTLSContext *virNetTLSContextNewServer(const char *cacert,
-                                              const char *cacrl,
-                                              const char *cert,
-                                              const char *key,
-                                              const char *const *x509dnACL,
-                                              const char *priority,
-                                              bool sanityCheckCert,
-                                              bool requireValidCert);
+                                            const char *cacrl,
+                                            const char *const *certs,
+                                            const char *const *keys,
+                                            const char *const *x509dnACL,
+                                            const char *priority,
+                                            bool sanityCheckCert,
+                                            bool requireValidCert);
 
 virNetTLSContext *virNetTLSContextNewClient(const char *cacert,
-                                              const char *cacrl,
-                                              const char *cert,
-                                              const char *key,
-                                              const char *priority,
-                                              bool sanityCheckCert,
-                                              bool requireValidCert);
+                                            const char *cacrl,
+                                            const char *const *certs,
+                                            const char *const *keys,
+                                            const char *priority,
+                                            bool sanityCheckCert,
+                                            bool requireValidCert);
 
 int virNetTLSContextReloadForServer(virNetTLSContext *ctxt,
                                     bool tryUserPkiPath);
index 48bdefdd76775acc88b26ca67e5adb96426a08c8..47675bffd02a0bf9d21543040e46f4e0a71beb61 100644 (file)
@@ -56,12 +56,14 @@ static int testTLSContextInit(const void *opaque)
     struct testTLSContextData *data = (struct testTLSContextData *)opaque;
     virNetTLSContext *ctxt = NULL;
     int ret = -1;
+    const char *certs[] = { data->crt, NULL };
+    const char *keys[] = { KEYFILE, NULL };
 
     if (data->isServer) {
         ctxt = virNetTLSContextNewServer(data->cacrt,
                                          NULL,
-                                         data->crt,
-                                         KEYFILE,
+                                         certs,
+                                         keys,
                                          NULL,
                                          "NORMAL",
                                          true,
@@ -69,8 +71,8 @@ static int testTLSContextInit(const void *opaque)
     } else {
         ctxt = virNetTLSContextNewClient(data->cacrt,
                                          NULL,
-                                         data->crt,
-                                         KEYFILE,
+                                         certs,
+                                         keys,
                                          "NORMAL",
                                          true,
                                          true);
index 459e17c52cfbd91a808b7f29cac4d5808d400bc1..e8d64c7da0378ceaea898703e18254c5c969d838 100644 (file)
@@ -81,6 +81,9 @@ static int testTLSSessionInit(const void *opaque)
     int channel[2];
     bool clientShake = 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 */
@@ -102,8 +105,7 @@ static int testTLSSessionInit(const void *opaque)
      */
     serverCtxt = virNetTLSContextNewServer(data->servercacrt,
                                            NULL,
-                                           data->servercrt,
-                                           KEYFILE,
+                                           servercerts, keys,
                                            data->wildcards,
                                            "NORMAL",
                                            false,
@@ -111,8 +113,7 @@ static int testTLSSessionInit(const void *opaque)
 
     clientCtxt = virNetTLSContextNewClient(data->clientcacrt,
                                            NULL,
-                                           data->clientcrt,
-                                           KEYFILE,
+                                           clientcerts, keys,
                                            "NORMAL",
                                            false,
                                            true);