From: Daniel P. Berrangé <berrange@redhat.com>
In addition to servercert.pem / serverkey.pem, we now also support
loading servercert{N}.pem / serverkey{N}.pem, for values of {N}
between 0 and 3 inclusive.
If servercert0.pem is provided, then using servercert.pem becomes
optional. The first missing index terminates the loading process.
eg if servercert1.pem is NOT present, then it will NOT attempt to
look for servercert2.pem / servercert3.pem.
This also applies to clientcert.pem / clientkey.pem.
This facilitates the transition to post-quantum cryptography by
allowing loading of certificates with different algorithms,
eg traditional RSA based cert, and optional ECC based cert or
MLDSA based cert for PQC.
The use of CA cert files is unchanged with only a single cacert.pem
loaded. WHen multiple CAs are needed they must be concatenated in
the single cacert.pem file.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
src/rpc/virnettlsconfig.c | 111 +++++++++++++++++++++++++++++++------
src/rpc/virnettlsconfig.h | 12 ++--
src/rpc/virnettlscontext.c | 51 ++++++++++-------
3 files changed, 130 insertions(+), 44 deletions(-)
diff --git a/src/rpc/virnettlsconfig.c b/src/rpc/virnettlsconfig.c
index 59cb8c2566..eec20cf6b7 100644
--- a/src/rpc/virnettlsconfig.c
+++ b/src/rpc/virnettlsconfig.c
@@ -31,6 +31,9 @@
VIR_LOG_INIT("rpc.nettlsconfig");
+
+#define VIR_NET_TLS_CONFIG_MAX_INDEXED 4
+
char *virNetTLSConfigUserPKIBaseDir(void)
{
g_autofree char *userdir = virGetUserDirectory();
@@ -69,6 +72,24 @@ static void virNetTLSConfigIdentity(bool isServer,
VIR_DEBUG("TLS cert %s", *cert);
}
+static void virNetTLSConfigIdentityIndexed(bool isServer,
+ const char *certdir,
+ const char *keydir,
+ size_t idx,
+ char **cert,
+ char **key)
+{
+ if (!*key)
+ *key = g_strdup_printf("%s/%skey%zu.pem", keydir,
+ (isServer ? "server" : "client"), idx);
+ if (!*cert)
+ *cert = g_strdup_printf("%s/%scert%zu.pem", certdir,
+ (isServer ? "server" : "client"), idx);
+
+ VIR_DEBUG("TLS key %s", *key);
+ VIR_DEBUG("TLS cert %s", *cert);
+}
+
void virNetTLSConfigCustomTrust(const char *pkipath,
char **cacert,
char **cacrl)
@@ -257,8 +278,8 @@ static int virNetTLSConfigCreds(const char *cacertdir,
bool allowMissingIdentity,
char **cacert,
char **cacrl,
- char **cert,
- char **key)
+ char ***certs,
+ char ***keys)
{
virNetTLSConfigTrust(cacertdir,
cacrldir,
@@ -268,14 +289,68 @@ static int virNetTLSConfigCreds(const char *cacertdir,
if (virNetTLSConfigEnsureTrust(cacert, cacrl, allowMissingCA) < 0)
return -1;
- virNetTLSConfigIdentity(isServer,
- certdir,
- keydir,
- cert,
- key);
+ if (!*certs && !*keys) {
+ g_auto(GStrv) certlist =
+ g_new0(char *, VIR_NET_TLS_CONFIG_MAX_INDEXED + 2);
+ g_auto(GStrv) keylist =
+ g_new0(char *, VIR_NET_TLS_CONFIG_MAX_INDEXED + 2);
+ size_t i;
+
+ /*
+ * When searching for indexed certs/keys, the first
+ * missing index terminates the search, so we don't
+ * get gaps in the returned array. All indexed files
+ * are optional, as if they're all missing, we'll
+ * still honour the traditional file names
+ */
+ for (i = 0; i < VIR_NET_TLS_CONFIG_MAX_INDEXED; i++) {
+ virNetTLSConfigIdentityIndexed(isServer,
+ certdir,
+ keydir,
+ i,
+ &(certlist[i + 1]),
+ &(keylist[i + 1]));
+
+ if (virNetTLSConfigEnsureIdentity(&(certlist[i + 1]),
+ &(keylist[i + 1]), true) < 0)
+ return -1;
- if (virNetTLSConfigEnsureIdentity(cert, key, allowMissingIdentity) < 0)
- return -1;
+ if (certlist[i + 1] == NULL) {
+ break;
+ }
+ }
+
+ /*
+ * If we find index 0, then allow the traditional
+ * default files to be optional
+ */
+ if (certlist[1] != NULL)
+ allowMissingIdentity = true;
+
+ virNetTLSConfigIdentity(isServer,
+ certdir,
+ keydir,
+ &(certlist[0]),
+ &(keylist[0]));
+
+ if (virNetTLSConfigEnsureIdentity(&(certlist[0]),
+ &(keylist[0]), allowMissingIdentity) < 0)
+ return -1;
+
+ if (certlist[0] == NULL) {
+ memmove(certlist, certlist + 1,
+ VIR_NET_TLS_CONFIG_MAX_INDEXED * sizeof(char *));
+ memmove(keylist, keylist + 1,
+ VIR_NET_TLS_CONFIG_MAX_INDEXED * sizeof(char *));
+ certlist[VIR_NET_TLS_CONFIG_MAX_INDEXED] = NULL;
+ keylist[VIR_NET_TLS_CONFIG_MAX_INDEXED] = NULL;
+ }
+
+ if (certlist[0] != NULL) {
+ *certs = g_steal_pointer(&certlist);
+ *keys = g_steal_pointer(&keylist);
+ }
+ }
return 0;
}
@@ -285,8 +360,8 @@ int virNetTLSConfigCustomCreds(const char *pkipath,
bool isServer,
char **cacert,
char **cacrl,
- char **cert,
- char **key)
+ char ***certs,
+ char ***keys)
{
VIR_DEBUG("Locating creds in custom dir %s", pkipath);
@@ -296,15 +371,15 @@ int virNetTLSConfigCustomCreds(const char *pkipath,
false,
!isServer,
cacert, cacrl,
- cert, key);
+ certs, keys);
}
int virNetTLSConfigUserCreds(bool isServer,
char **cacert,
char **cacrl,
- char **cert,
- char **key)
+ char ***certs,
+ char ***keys)
{
g_autofree char *pkipath = virNetTLSConfigUserPKIBaseDir();
@@ -316,14 +391,14 @@ int virNetTLSConfigUserCreds(bool isServer,
true,
true,
cacert, cacrl,
- cert, key);
+ certs, keys);
}
int virNetTLSConfigSystemCreds(bool isServer,
char **cacert,
char **cacrl,
- char **cert,
- char **key)
+ char ***certs,
+ char ***keys)
{
VIR_DEBUG("Locating creds in system dir %s", LIBVIRT_PKI_DIR);
@@ -335,5 +410,5 @@ int virNetTLSConfigSystemCreds(bool isServer,
false,
!isServer,
cacert, cacrl,
- cert, key);
+ certs, keys);
}
diff --git a/src/rpc/virnettlsconfig.h b/src/rpc/virnettlsconfig.h
index 9ad213fe06..c0bf604950 100644
--- a/src/rpc/virnettlsconfig.h
+++ b/src/rpc/virnettlsconfig.h
@@ -60,15 +60,15 @@ int virNetTLSConfigCustomCreds(const char *pkipath,
bool isServer,
char **cacert,
char **cacrl,
- char **cert,
- char **key);
+ char ***certs,
+ char ***keys);
int virNetTLSConfigUserCreds(bool isServer,
char **cacert,
char **cacrl,
- char **cert,
- char **key);
+ char ***certs,
+ char ***keys);
int virNetTLSConfigSystemCreds(bool isServer,
char **cacert,
char **cacrl,
- char **cert,
- char **key);
+ char ***certs,
+ char ***keys);
diff --git a/src/rpc/virnettlscontext.c b/src/rpc/virnettlscontext.c
index 7061eb5953..dfe793e5db 100644
--- a/src/rpc/virnettlscontext.c
+++ b/src/rpc/virnettlscontext.c
@@ -220,13 +220,13 @@ static int virNetTLSContextLocateCredentials(const char *pkipath,
bool isServer,
char **cacert,
char **cacrl,
- char **cert,
- char **key)
+ char ***certs,
+ char ***keys)
{
*cacert = NULL;
*cacrl = NULL;
- *key = NULL;
- *cert = NULL;
+ *keys = NULL;
+ *certs = NULL;
VIR_DEBUG("pkipath=%s isServer=%d tryUserPkiPath=%d",
pkipath, isServer, tryUserPkiPath);
@@ -237,20 +237,31 @@ static int virNetTLSContextLocateCredentials(const char *pkipath,
if (pkipath) {
if (virNetTLSConfigCustomCreds(pkipath, isServer,
cacert, cacrl,
- cert, key) < 0)
+ certs, keys) < 0)
return -1;
} else {
if (tryUserPkiPath &&
virNetTLSConfigUserCreds(isServer,
cacert, cacrl,
- cert, key) < 0)
+ certs, keys) < 0)
return -1;
if (virNetTLSConfigSystemCreds(isServer,
cacert, cacrl,
- cert, key) < 0)
+ certs, keys) < 0)
return -1;
}
+
+ /*
+ * Ensure the cert list is always non-NULL, even
+ * if it is an empty list, so that callers don't
+ * need to have repeated checks for a NULL array.
+ */
+ if (*certs == NULL)
+ *certs = g_new0(char *, 1);
+ if (*keys == NULL)
+ *keys = g_new0(char *, 1);
+
return 0;
}
@@ -265,16 +276,16 @@ static virNetTLSContext *virNetTLSContextNewPath(const char *pkipath,
{
g_autofree char *cacert = NULL;
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 };
+ g_auto(GStrv) keys = NULL;
+ g_auto(GStrv) certs = NULL;
if (virNetTLSContextLocateCredentials(pkipath, tryUserPkiPath, isServer,
- &cacert, &cacrl, &cert, &key) < 0)
+ &cacert, &cacrl, &certs, &keys) < 0)
return NULL;
- return virNetTLSContextNew(cacert, cacrl, certs, keys,
+ return virNetTLSContextNew(cacert, cacrl,
+ (const char *const *)certs,
+ (const char *const *)keys,
x509dnACL, priority, sanityCheckCert,
requireValidCert, isServer);
}
@@ -329,15 +340,13 @@ int virNetTLSContextReloadForServer(virNetTLSContext *ctxt,
int err;
g_autofree char *cacert = NULL;
g_autofree char *cacrl = NULL;
- g_autofree char *cert = NULL;
- g_autofree char *key = NULL;
- const char *certs[] = { cert, NULL };
- const char *keys[] = { key, NULL };
+ g_auto(GStrv) certs = NULL;
+ g_auto(GStrv) keys = NULL;
x509credBak = g_steal_pointer(&ctxt->x509cred);
if (virNetTLSContextLocateCredentials(NULL, tryUserPkiPath, true,
- &cacert, &cacrl, &cert, &key))
+ &cacert, &cacrl, &certs, &keys))
goto error;
err = gnutls_certificate_allocate_credentials(&ctxt->x509cred);
@@ -348,11 +357,13 @@ int virNetTLSContextReloadForServer(virNetTLSContext *ctxt,
goto error;
}
- if (virNetTLSCertSanityCheck(true, cacert, certs))
+ if (virNetTLSCertSanityCheck(true, cacert,
+ (const char *const *)certs))
goto error;
if (virNetTLSContextLoadCredentials(ctxt, cacert, cacrl,
- certs, keys))
+ (const char *const *)certs,
+ (const char *const *)keys))
goto error;
gnutls_certificate_free_credentials(x509credBak);
--
2.51.1
© 2016 - 2025 Red Hat, Inc.