[PATCH 0/9] tools: rewrite virt-pki-validate in C

This was driven by the complaint that libvirt pulls in gnutls-utils https://src.fedoraproject.org/rpms/virt-viewer/pull-request/4 but also it lets us remove more usage of Shell code from libvirt, as well as improving the consistency of certificate checks vs the runtime checks we do. Daniel P. Berrangé (9): rpc: split out helpers for TLS cert path location rpc: refactor method for checking session certificates rpc: split TLS cert validation into separate file docs: fix author credit for virt-pki-validate tool tools: split off common helpers for host validate tool tools: drop unused --version argument tools: stop checking init scripts & iptables config tools: reimplement virt-pki-validate in C tools: support validating user/custom PKI certs docs/manpages/virt-pki-validate.rst | 9 +- libvirt.spec.in | 2 - po/POTFILES | 3 + src/rpc/meson.build | 7 +- src/rpc/virnettlscert.c | 553 ++++++++++++++++++++++++++ src/rpc/virnettlscert.h | 42 ++ src/rpc/virnettlsconfig.c | 202 ++++++++++ src/rpc/virnettlsconfig.h | 68 ++++ src/rpc/virnettlscontext.c | 586 +--------------------------- tools/meson.build | 31 +- tools/virt-host-validate-ch.c | 12 +- tools/virt-host-validate-common.c | 308 ++++++--------- tools/virt-host-validate-common.h | 48 +-- tools/virt-host-validate-lxc.c | 18 +- tools/virt-host-validate-qemu.c | 30 +- tools/virt-host-validate.c | 2 +- tools/virt-login-shell-helper.c | 2 +- tools/virt-pki-query-dn.c | 2 +- tools/virt-pki-validate.c | 424 ++++++++++++++++++++ tools/virt-pki-validate.in | 323 --------------- tools/virt-validate-common.c | 110 ++++++ tools/virt-validate-common.h | 57 +++ 22 files changed, 1670 insertions(+), 1169 deletions(-) create mode 100644 src/rpc/virnettlscert.c create mode 100644 src/rpc/virnettlscert.h create mode 100644 src/rpc/virnettlsconfig.c create mode 100644 src/rpc/virnettlsconfig.h create mode 100644 tools/virt-pki-validate.c delete mode 100644 tools/virt-pki-validate.in create mode 100644 tools/virt-validate-common.c create mode 100644 tools/virt-validate-common.h -- 2.43.0

We'll want to access these paths from outside the TLS context code, so split them into a standalone file. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/rpc/meson.build | 6 +- src/rpc/virnettlsconfig.c | 202 +++++++++++++++++++++++++++++++++++++ src/rpc/virnettlsconfig.h | 68 +++++++++++++ src/rpc/virnettlscontext.c | 70 ++----------- 4 files changed, 285 insertions(+), 61 deletions(-) create mode 100644 src/rpc/virnettlsconfig.c create mode 100644 src/rpc/virnettlsconfig.h diff --git a/src/rpc/meson.build b/src/rpc/meson.build index 9d98bc6259..d11d532d0f 100644 --- a/src/rpc/meson.build +++ b/src/rpc/meson.build @@ -1,6 +1,10 @@ gendispatch_prog = find_program('gendispatch.pl') -socket_sources = [ +tlsconfig_sources = [ + files('virnettlsconfig.c'), +] + +socket_sources = tlsconfig_sources + [ 'virnettlscontext.c', 'virnetsocket.c', ] diff --git a/src/rpc/virnettlsconfig.c b/src/rpc/virnettlsconfig.c new file mode 100644 index 0000000000..d020083d6a --- /dev/null +++ b/src/rpc/virnettlsconfig.c @@ -0,0 +1,202 @@ +/* + * virnettlsconfig.c: TLS x509 configuration helpers + * + * Copyright (C) 2010-2024 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include "virnettlsconfig.h" +#include "virlog.h" +#include "virutil.h" + +#define VIR_FROM_THIS VIR_FROM_RPC + +VIR_LOG_INIT("rpc.nettlscontext"); + +char *virNetTLSConfigUserPKIBaseDir(void) +{ + g_autofree char *userdir = virGetUserDirectory(); + + return g_strdup_printf("%s/.pki/libvirt", userdir); +} + +static void virNetTLSConfigTrust(const char *cacertdir, + const char *cacrldir, + char **cacert, + char **cacrl) +{ + if (!*cacert) + *cacert = g_strdup_printf("%s/%s", cacertdir, "cacert.pem"); + if (!*cacrl) + *cacrl = g_strdup_printf("%s/%s", cacrldir, "cacrl.pem"); + + VIR_DEBUG("TLS CA cert %s", *cacert); + VIR_DEBUG("TLS CA CRL %s", *cacrl); +} + +static void virNetTLSConfigIdentity(int isServer, + const char *certdir, + const char *keydir, + char **cert, + char **key) +{ + if (!*key) + *key = g_strdup_printf("%s/%s", keydir, + isServer ? "serverkey.pem" : "clientkey.pem"); + if (!*cert) + *cert = g_strdup_printf("%s/%s", certdir, + isServer ? "servercert.pem" : "clientcert.pem"); + + VIR_DEBUG("TLS key %s", *key); + VIR_DEBUG("TLS cert %s", *cert); +} + +void virNetTLSConfigCustomTrust(const char *pkipath, + char **cacert, + char **cacrl) +{ + VIR_DEBUG("Locating trust chain in custom dir %s", pkipath); + virNetTLSConfigTrust(pkipath, + pkipath, + cacert, + cacrl); +} + +void virNetTLSConfigUserTrust(char **cacert, + char **cacrl) +{ + g_autofree char *pkipath = virNetTLSConfigUserPKIBaseDir(); + + VIR_DEBUG("Locating trust chain in user dir %s", pkipath); + + virNetTLSConfigTrust(pkipath, + pkipath, + cacert, + cacrl); +} + +void virNetTLSConfigSystemTrust(char **cacert, + char **cacrl) +{ + VIR_DEBUG("Locating trust chain in system dir %s", LIBVIRT_PKI_DIR); + + virNetTLSConfigTrust(LIBVIRT_CACERT_DIR, + LIBVIRT_CACRL_DIR, + cacert, + cacrl); +} + +void virNetTLSConfigCustomIdentity(const char *pkipath, + int isServer, + char **cert, + char **key) +{ + VIR_DEBUG("Locating creds in custom dir %s", pkipath); + virNetTLSConfigIdentity(isServer, + pkipath, + pkipath, + cert, + key); +} + +void virNetTLSConfigUserIdentity(int isServer, + char **cert, + char **key) +{ + g_autofree char *pkipath = virNetTLSConfigUserPKIBaseDir(); + + VIR_DEBUG("Locating creds in user dir %s", pkipath); + + virNetTLSConfigIdentity(isServer, + pkipath, + pkipath, + cert, + key); +} + +void virNetTLSConfigSystemIdentity(int isServer, + char **cert, + char **key) +{ + VIR_DEBUG("Locating creds in system dir %s", LIBVIRT_PKI_DIR); + + virNetTLSConfigIdentity(isServer, + LIBVIRT_CERT_DIR, + LIBVIRT_KEY_DIR, + cert, + key); +} + +void virNetTLSConfigCustomCreds(const char *pkipath, + int isServer, + char **cacert, + char **cacrl, + char **cert, + char **key) +{ + VIR_DEBUG("Locating creds in custom dir %s", pkipath); + virNetTLSConfigTrust(pkipath, + pkipath, + cacert, + cacrl); + virNetTLSConfigIdentity(isServer, + pkipath, + pkipath, + cert, + key); +} + +void virNetTLSConfigUserCreds(int isServer, + char **cacert, + char **cacrl, + char **cert, + char **key) +{ + g_autofree char *pkipath = virNetTLSConfigUserPKIBaseDir(); + + VIR_DEBUG("Locating creds in user dir %s", pkipath); + + virNetTLSConfigTrust(pkipath, + pkipath, + cacert, + cacrl); + virNetTLSConfigIdentity(isServer, + pkipath, + pkipath, + cert, + key); +} + +void virNetTLSConfigSystemCreds(int isServer, + char **cacert, + char **cacrl, + char **cert, + char **key) +{ + VIR_DEBUG("Locating creds in system dir %s", LIBVIRT_PKI_DIR); + + virNetTLSConfigTrust(LIBVIRT_CACERT_DIR, + LIBVIRT_CACRL_DIR, + cacert, + cacrl); + virNetTLSConfigIdentity(isServer, + LIBVIRT_CERT_DIR, + LIBVIRT_KEY_DIR, + cert, + key); +} diff --git a/src/rpc/virnettlsconfig.h b/src/rpc/virnettlsconfig.h new file mode 100644 index 0000000000..797b3e3ac5 --- /dev/null +++ b/src/rpc/virnettlsconfig.h @@ -0,0 +1,68 @@ +/* + * virnettlsconfig.h: TLS x509 configuration helpers + * + * Copyright (C) 2010-2024 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "configmake.h" + +#define LIBVIRT_PKI_DIR SYSCONFDIR "/pki" +#define LIBVIRT_CACERT_DIR LIBVIRT_PKI_DIR "/CA" +#define LIBVIRT_CACRL_DIR LIBVIRT_PKI_DIR "/CA" +#define LIBVIRT_KEY_DIR LIBVIRT_PKI_DIR "/libvirt/private" +#define LIBVIRT_CERT_DIR LIBVIRT_PKI_DIR "/libvirt" + +char *virNetTLSConfigUserPKIBaseDir(void); + +void virNetTLSConfigCustomTrust(const char *pkipath, + char **cacert, + char **cacrl); +void virNetTLSConfigUserTrust(char **cacert, + char **cacrl); +void virNetTLSConfigSystemTrust(char **cacert, + char **cacrl); + +void virNetTLSConfigCustomIdentity(const char *pkipath, + int isServer, + char **cert, + char **key); +void virNetTLSConfigUserIdentity(int isServer, + char **cert, + char **key); +void virNetTLSConfigSystemIdentity(int isServer, + char **cert, + char **key); + + +void virNetTLSConfigCustomCreds(const char *pkipath, + int isServer, + char **cacert, + char **cacrl, + char **cert, + char **key); +void virNetTLSConfigUserCreds(int isServer, + char **cacert, + char **cacrl, + char **cert, + char **key); +void virNetTLSConfigSystemCreds(int isServer, + char **cacert, + char **cacrl, + char **cert, + char **key); diff --git a/src/rpc/virnettlscontext.c b/src/rpc/virnettlscontext.c index dc60244927..4223043bc9 100644 --- a/src/rpc/virnettlscontext.c +++ b/src/rpc/virnettlscontext.c @@ -27,6 +27,7 @@ #include <gnutls/x509.h> #include "virnettlscontext.h" +#include "virnettlsconfig.h" #include "virstring.h" #include "viralloc.h" @@ -36,15 +37,6 @@ #include "virlog.h" #include "virprobe.h" #include "virthread.h" -#include "configmake.h" - -#define LIBVIRT_PKI_DIR SYSCONFDIR "/pki" -#define LIBVIRT_CACERT LIBVIRT_PKI_DIR "/CA/cacert.pem" -#define LIBVIRT_CACRL LIBVIRT_PKI_DIR "/CA/cacrl.pem" -#define LIBVIRT_CLIENTKEY LIBVIRT_PKI_DIR "/libvirt/private/clientkey.pem" -#define LIBVIRT_CLIENTCERT LIBVIRT_PKI_DIR "/libvirt/clientcert.pem" -#define LIBVIRT_SERVERKEY LIBVIRT_PKI_DIR "/libvirt/private/serverkey.pem" -#define LIBVIRT_SERVERCERT LIBVIRT_PKI_DIR "/libvirt/servercert.pem" #define VIR_FROM_THIS VIR_FROM_RPC @@ -721,9 +713,6 @@ static int virNetTLSContextLocateCredentials(const char *pkipath, char **cert, char **key) { - char *userdir = NULL; - char *user_pki_path = NULL; - *cacert = NULL; *cacrl = NULL; *key = NULL; @@ -736,33 +725,13 @@ static int virNetTLSContextLocateCredentials(const char *pkipath, * files actually exist there */ if (pkipath) { - VIR_DEBUG("Told to use TLS credentials in %s", pkipath); - *cacert = g_strdup_printf("%s/%s", pkipath, "cacert.pem"); - *cacrl = g_strdup_printf("%s/%s", pkipath, "cacrl.pem"); - *key = g_strdup_printf("%s/%s", pkipath, - isServer ? "serverkey.pem" : "clientkey.pem"); - - *cert = g_strdup_printf("%s/%s", pkipath, - isServer ? "servercert.pem" : "clientcert.pem"); + virNetTLSConfigCustomCreds(pkipath, isServer, + cacert, cacrl, + cert, key); } else if (tryUserPkiPath) { - /* Check to see if $HOME/.pki contains at least one of the - * files and if so, use that - */ - userdir = virGetUserDirectory(); - - user_pki_path = g_strdup_printf("%s/.pki/libvirt", userdir); - - VIR_DEBUG("Trying to find TLS user credentials in %s", user_pki_path); - - *cacert = g_strdup_printf("%s/%s", user_pki_path, "cacert.pem"); - - *cacrl = g_strdup_printf("%s/%s", user_pki_path, "cacrl.pem"); - - *key = g_strdup_printf("%s/%s", user_pki_path, - isServer ? "serverkey.pem" : "clientkey.pem"); - - *cert = g_strdup_printf("%s/%s", user_pki_path, - isServer ? "servercert.pem" : "clientcert.pem"); + virNetTLSConfigUserCreds(isServer, + cacert, cacrl, + cert, key); /* * If some of the files can't be found, fallback @@ -782,28 +751,9 @@ static int virNetTLSContextLocateCredentials(const char *pkipath, } } - /* No explicit path, or user path didn't exist, so - * fallback to global defaults - */ - if (!*cacert) { - VIR_DEBUG("Using default TLS CA certificate path"); - *cacert = g_strdup(LIBVIRT_CACERT); - } - - if (!*cacrl) { - VIR_DEBUG("Using default TLS CA revocation list path"); - *cacrl = g_strdup(LIBVIRT_CACRL); - } - - if (!*key && !*cert) { - VIR_DEBUG("Using default TLS key/certificate path"); - *key = g_strdup(isServer ? LIBVIRT_SERVERKEY : LIBVIRT_CLIENTKEY); - - *cert = g_strdup(isServer ? LIBVIRT_SERVERCERT : LIBVIRT_CLIENTCERT); - } - - VIR_FREE(user_pki_path); - VIR_FREE(userdir); + virNetTLSConfigSystemCreds(isServer, + cacert, cacrl, + cert, key); return 0; } -- 2.43.0

On 6/7/24 16:26, Daniel P. Berrangé wrote:
We'll want to access these paths from outside the TLS context code, so split them into a standalone file.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/rpc/meson.build | 6 +- src/rpc/virnettlsconfig.c | 202 +++++++++++++++++++++++++++++++++++++ src/rpc/virnettlsconfig.h | 68 +++++++++++++ src/rpc/virnettlscontext.c | 70 ++----------- 4 files changed, 285 insertions(+), 61 deletions(-) create mode 100644 src/rpc/virnettlsconfig.c create mode 100644 src/rpc/virnettlsconfig.h
diff --git a/src/rpc/meson.build b/src/rpc/meson.build index 9d98bc6259..d11d532d0f 100644 --- a/src/rpc/meson.build +++ b/src/rpc/meson.build @@ -1,6 +1,10 @@ gendispatch_prog = find_program('gendispatch.pl')
-socket_sources = [ +tlsconfig_sources = [ + files('virnettlsconfig.c'), +]
I believe this should be either: tlsconfig_sources = [ ... ] or tlsconfig_sources = files( ... ) and given that the latter remembers the subdir of passed files then I'd just stick to it. Michal

On Mon, Jun 10, 2024 at 01:57:21PM +0200, Michal Prívozník wrote:
On 6/7/24 16:26, Daniel P. Berrangé wrote:
We'll want to access these paths from outside the TLS context code, so split them into a standalone file.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/rpc/meson.build | 6 +- src/rpc/virnettlsconfig.c | 202 +++++++++++++++++++++++++++++++++++++ src/rpc/virnettlsconfig.h | 68 +++++++++++++ src/rpc/virnettlscontext.c | 70 ++----------- 4 files changed, 285 insertions(+), 61 deletions(-) create mode 100644 src/rpc/virnettlsconfig.c create mode 100644 src/rpc/virnettlsconfig.h
diff --git a/src/rpc/meson.build b/src/rpc/meson.build index 9d98bc6259..d11d532d0f 100644 --- a/src/rpc/meson.build +++ b/src/rpc/meson.build @@ -1,6 +1,10 @@ gendispatch_prog = find_program('gendispatch.pl')
-socket_sources = [ +tlsconfig_sources = [ + files('virnettlsconfig.c'), +]
I believe this should be either:
tlsconfig_sources = [ ... ]
This doesn't work as we need the sub-dir remembered, so that a later patch in the series can reference this from the tools/ subdir.
or
tlsconfig_sources = files( ... )
and given that the latter remembers the subdir of passed files then I'd just stick to it.
With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

This will facilitate moving much of the code into a new file in the subsequent commit. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/rpc/virnettlscontext.c | 109 ++++++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 43 deletions(-) diff --git a/src/rpc/virnettlscontext.c b/src/rpc/virnettlscontext.c index 4223043bc9..a56abee009 100644 --- a/src/rpc/virnettlscontext.c +++ b/src/rpc/virnettlscontext.c @@ -876,6 +876,65 @@ virNetTLSContext *virNetTLSContextNewClient(const char *cacert, sanityCheckCert, requireValidCert, false); } +static int virNetTLSContextCertValidateCA(gnutls_x509_crt_t cert, + bool isServer) +{ + if (virNetTLSContextCheckCertTimes(cert, "[session]", isServer, true) < 0) + return -1; + + return 0; +} + +static char *virNetTLSContextCertValidate(gnutls_x509_crt_t cert, + bool isServer, + const char *hostname, + const char *const *x509dnACL) +{ + size_t dnamesize = 256; + g_autofree char *dname = g_new0(char, dnamesize); + int ret; + + if (virNetTLSContextCheckCertTimes(cert, "[session]", + isServer, false) < 0) + return NULL; + + ret = gnutls_x509_crt_get_dn(cert, dname, &dnamesize); + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { + VIR_DEBUG("Reallocating dname to fit %zu bytes", dnamesize); + dname = g_realloc(dname, dnamesize); + ret = gnutls_x509_crt_get_dn(cert, dname, &dnamesize); + } + if (ret != 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Failed to get certificate %1$s distinguished name: %2$s"), + "[session]", gnutls_strerror(ret)); + return NULL; + } + + VIR_DEBUG("Peer DN is %s", dname); + + if (virNetTLSContextCheckCertDN(cert, "[session]", hostname, + dname, x509dnACL) < 0) + return NULL; + + /* !isServer, since on the client, we're validating the + * server's cert, and on the server, the client's cert + */ + if (virNetTLSContextCheckCertBasicConstraints(cert, "[session]", + !isServer, false) < 0) + return NULL; + + if (virNetTLSContextCheckCertKeyUsage(cert, "[session]", + false) < 0) + return NULL; + + /* !isServer - as above */ + if (virNetTLSContextCheckCertKeyPurpose(cert, "[session]", + !isServer) < 0) + return NULL; + + return g_steal_pointer(&dname); +} static int virNetTLSContextValidCertificate(virNetTLSContext *ctxt, virNetTLSSession *sess) @@ -945,57 +1004,21 @@ static int virNetTLSContextValidCertificate(virNetTLSContext *ctxt, goto authfail; } - if (virNetTLSContextCheckCertTimes(cert, "[session]", - sess->isServer, i > 0) < 0) { - gnutls_x509_crt_deinit(cert); - goto authdeny; - } - if (i == 0) { - ret = gnutls_x509_crt_get_dn(cert, dname, &dnamesize); - if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { - VIR_DEBUG("Reallocating dname to fit %zu bytes", dnamesize); - dname = g_realloc(dname, dnamesize); - dnameptr = dname; - ret = gnutls_x509_crt_get_dn(cert, dname, &dnamesize); - } - if (ret != 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("Failed to get certificate %1$s distinguished name: %2$s"), - "[session]", gnutls_strerror(ret)); - goto authfail; - } - sess->x509dname = g_steal_pointer(&dname); - VIR_DEBUG("Peer DN is %s", dnameptr); - - if (virNetTLSContextCheckCertDN(cert, "[session]", sess->hostname, - dnameptr, ctxt->x509dnACL) < 0) { - gnutls_x509_crt_deinit(cert); - goto authdeny; - } - - /* !sess->isServer, since on the client, we're validating the - * server's cert, and on the server, the client's cert - */ - if (virNetTLSContextCheckCertBasicConstraints(cert, "[session]", - !sess->isServer, false) < 0) { - gnutls_x509_crt_deinit(cert); - goto authdeny; - } - - if (virNetTLSContextCheckCertKeyUsage(cert, "[session]", - false) < 0) { + if (!(sess->x509dname = virNetTLSContextCertValidate(cert, + sess->isServer, + sess->hostname, + ctxt->x509dnACL))) { gnutls_x509_crt_deinit(cert); goto authdeny; } - - /* !sess->isServer - as above */ - if (virNetTLSContextCheckCertKeyPurpose(cert, "[session]", - !sess->isServer) < 0) { + } else { + if (virNetTLSContextCertValidateCA(cert, sess->isServer) < 0) { gnutls_x509_crt_deinit(cert); goto authdeny; } } + gnutls_x509_crt_deinit(cert); } -- 2.43.0

The TLS cert validation logic will be reused for the new impl of the virt-pki-validate tool. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- po/POTFILES | 1 + src/rpc/meson.build | 1 + src/rpc/virnettlscert.c | 553 +++++++++++++++++++++++++++++++++++++ src/rpc/virnettlscert.h | 42 +++ src/rpc/virnettlscontext.c | 535 +---------------------------------- 5 files changed, 606 insertions(+), 526 deletions(-) create mode 100644 src/rpc/virnettlscert.c create mode 100644 src/rpc/virnettlscert.h diff --git a/po/POTFILES b/po/POTFILES index 4ad7e19e08..0f68c652eb 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -219,6 +219,7 @@ src/rpc/virnetserverprogram.c src/rpc/virnetserverservice.c src/rpc/virnetsocket.c src/rpc/virnetsshsession.c +src/rpc/virnettlscert.c src/rpc/virnettlscontext.c src/secret/secret_driver.c src/security/security_apparmor.c diff --git a/src/rpc/meson.build b/src/rpc/meson.build index d11d532d0f..8bdbf5c88f 100644 --- a/src/rpc/meson.build +++ b/src/rpc/meson.build @@ -2,6 +2,7 @@ gendispatch_prog = find_program('gendispatch.pl') tlsconfig_sources = [ files('virnettlsconfig.c'), + files('virnettlscert.c'), ] socket_sources = tlsconfig_sources + [ diff --git a/src/rpc/virnettlscert.c b/src/rpc/virnettlscert.c new file mode 100644 index 0000000000..2e1e4c56d5 --- /dev/null +++ b/src/rpc/virnettlscert.c @@ -0,0 +1,553 @@ +/* + * virnettlscert.c: TLS x509 certificate helpers + * + * Copyright (C) 2010-2024 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include "virnettlscert.h" + +#include "viralloc.h" +#include "virfile.h" +#include "virlog.h" +#include "virerror.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +VIR_LOG_INIT("rpc.nettlscert"); + +static int virNetTLSCertCheckTimes(gnutls_x509_crt_t cert, + const char *certFile, + bool isServer, + bool isCA) +{ + time_t now; + + if ((now = time(NULL)) == ((time_t)-1)) { + virReportSystemError(errno, "%s", + _("cannot get current time")); + return -1; + } + + if (gnutls_x509_crt_get_expiration_time(cert) < now) { + virReportError(VIR_ERR_SYSTEM_ERROR, + (isCA ? + _("The CA certificate %1$s has expired") : + (isServer ? + _("The server certificate %1$s has expired") : + _("The client certificate %1$s has expired"))), + certFile); + return -1; + } + + if (gnutls_x509_crt_get_activation_time(cert) > now) { + virReportError(VIR_ERR_SYSTEM_ERROR, + (isCA ? + _("The CA certificate %1$s is not yet active") : + (isServer ? + _("The server certificate %1$s is not yet active") : + _("The client certificate %1$s is not yet active"))), + certFile); + return -1; + } + + return 0; +} + + +static int virNetTLSCertCheckBasicConstraints(gnutls_x509_crt_t cert, + const char *certFile, + bool isServer, + bool isCA) +{ + int status; + + status = gnutls_x509_crt_get_basic_constraints(cert, NULL, NULL, NULL); + VIR_DEBUG("Cert %s basic constraints %d", certFile, status); + + if (status > 0) { /* It is a CA cert */ + if (!isCA) { + virReportError(VIR_ERR_SYSTEM_ERROR, isServer ? + _("The certificate %1$s basic constraints show a CA, but we need one for a server") : + _("The certificate %1$s basic constraints show a CA, but we need one for a client"), + certFile); + return -1; + } + } else if (status == 0) { /* It is not a CA cert */ + if (isCA) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("The certificate %1$s basic constraints do not show a CA"), + certFile); + return -1; + } + } else if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { /* Missing basicConstraints */ + if (isCA) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("The certificate %1$s is missing basic constraints for a CA"), + certFile); + return -1; + } + } else { /* General error */ + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Unable to query certificate %1$s basic constraints %2$s"), + certFile, gnutls_strerror(status)); + return -1; + } + + return 0; +} + + +static int virNetTLSCertCheckKeyUsage(gnutls_x509_crt_t cert, + const char *certFile, + bool isCA) +{ + int status; + unsigned int usage = 0; + unsigned int critical = 0; + + status = gnutls_x509_crt_get_key_usage(cert, &usage, &critical); + + VIR_DEBUG("Cert %s key usage status %d usage %d critical %u", certFile, status, usage, critical); + if (status < 0) { + if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + usage = isCA ? GNUTLS_KEY_KEY_CERT_SIGN : + GNUTLS_KEY_DIGITAL_SIGNATURE|GNUTLS_KEY_KEY_ENCIPHERMENT; + } else { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Unable to query certificate %1$s key usage %2$s"), + certFile, gnutls_strerror(status)); + return -1; + } + } + + if (isCA) { + if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) { + if (critical) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Certificate %1$s usage does not permit certificate signing"), + certFile); + return -1; + } else { + VIR_WARN("Certificate %s usage does not permit certificate signing", + certFile); + } + } + } else { + if (!(usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) { + if (critical) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Certificate %1$s usage does not permit digital signature"), + certFile); + return -1; + } else { + VIR_WARN("Certificate %s usage does not permit digital signature", + certFile); + } + } + if (!(usage & GNUTLS_KEY_KEY_ENCIPHERMENT)) { + if (critical) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Certificate %1$s usage does not permit key encipherment"), + certFile); + return -1; + } else { + VIR_WARN("Certificate %s usage does not permit key encipherment", + certFile); + } + } + } + + return 0; +} + + +static int virNetTLSCertCheckKeyPurpose(gnutls_x509_crt_t cert, + const char *certFile, + bool isServer) +{ + int status; + size_t i; + unsigned int purposeCritical; + unsigned int critical; + char *buffer = NULL; + size_t size; + bool allowClient = false, allowServer = false; + + critical = 0; + for (i = 0; ; i++) { + size = 0; + status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, &size, NULL); + + if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + VIR_DEBUG("No key purpose data available at slot %zu", i); + + /* If there is no data at all, then we must allow client/server to pass */ + if (i == 0) + allowServer = allowClient = true; + break; + } + if (status != GNUTLS_E_SHORT_MEMORY_BUFFER) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Unable to query certificate %1$s key purpose %2$s"), + certFile, gnutls_strerror(status)); + return -1; + } + + buffer = g_new0(char, size); + status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, &size, &purposeCritical); + if (status < 0) { + VIR_FREE(buffer); + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Unable to query certificate %1$s key purpose %2$s"), + certFile, gnutls_strerror(status)); + return -1; + } + if (purposeCritical) + critical = true; + + VIR_DEBUG("Key purpose %d %s critical %u", status, buffer, purposeCritical); + if (STREQ(buffer, GNUTLS_KP_TLS_WWW_SERVER)) { + allowServer = true; + } else if (STREQ(buffer, GNUTLS_KP_TLS_WWW_CLIENT)) { + allowClient = true; + } else if (STRNEQ(buffer, GNUTLS_KP_ANY)) { + allowServer = allowClient = true; + } + + VIR_FREE(buffer); + } + + if (isServer) { + if (!allowServer) { + if (critical) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Certificate %1$s purpose does not allow use for with a TLS server"), + certFile); + return -1; + } else { + VIR_WARN("Certificate %s purpose does not allow use for with a TLS server", + certFile); + } + } + } else { + if (!allowClient) { + if (critical) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Certificate %1$s purpose does not allow use for with a TLS client"), + certFile); + return -1; + } else { + VIR_WARN("Certificate %s purpose does not allow use for with a TLS client", + certFile); + } + } + } + + return 0; +} + +/* Check DN is on tls_allowed_dn_list. */ +static int +virNetTLSCertCheckDNACL(const char *dname, + const char *const *wildcards) +{ + while (*wildcards) { + if (g_pattern_match_simple(*wildcards, dname)) + return 1; + + wildcards++; + } + + /* Log the client's DN for debugging */ + VIR_DEBUG("Failed ACL check for client DN '%s'", dname); + + /* This is the most common error: make it informative. */ + virReportError(VIR_ERR_SYSTEM_ERROR, "%s", + _("Client's Distinguished Name is not on the list of allowed clients (tls_allowed_dn_list). Use 'virt-pki-query-dn clientcert.pem' to view the Distinguished Name field in the client certificate, or run this daemon with --verbose option.")); + return 0; +} + + +static int +virNetTLSCertCheckDN(gnutls_x509_crt_t cert, + const char *certFile, + const char *hostname, + const char *dname, + const char *const *acl) +{ + if (acl && dname && + virNetTLSCertCheckDNACL(dname, acl) <= 0) + return -1; + + if (hostname && + !gnutls_x509_crt_check_hostname(cert, hostname)) { + virReportError(VIR_ERR_RPC, + _("Certificate %1$s owner does not match the hostname %2$s"), + certFile, hostname); + return -1; + } + + return 0; +} + + +static int virNetTLSCertCheck(gnutls_x509_crt_t cert, + const char *certFile, + bool isServer, + bool isCA) +{ + if (virNetTLSCertCheckTimes(cert, certFile, isServer, isCA) < 0) + return -1; + + if (virNetTLSCertCheckBasicConstraints(cert, certFile, isServer, isCA) < 0) + return -1; + + if (virNetTLSCertCheckKeyUsage(cert, certFile, isCA) < 0) + return -1; + + if (!isCA && + virNetTLSCertCheckKeyPurpose(cert, certFile, isServer) < 0) + return -1; + + return 0; +} + + +static int virNetTLSCertCheckPair(gnutls_x509_crt_t cert, + const char *certFile, + gnutls_x509_crt_t *cacerts, + size_t ncacerts, + const char *cacertFile, + bool isServer) +{ + unsigned int status; + + if (gnutls_x509_crt_list_verify(&cert, 1, + cacerts, ncacerts, + NULL, 0, + 0, &status) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, isServer ? + _("Unable to verify server certificate %1$s against CA certificate %2$s") : + _("Unable to verify client certificate %1$s against CA certificate %2$s"), + certFile, cacertFile); + return -1; + } + + if (status != 0) { + const char *reason = _("Invalid certificate"); + + if (status & GNUTLS_CERT_INVALID) + reason = _("The certificate is not trusted."); + + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) + reason = _("The certificate hasn't got a known issuer."); + + if (status & GNUTLS_CERT_REVOKED) + reason = _("The certificate has been revoked."); + + if (status & GNUTLS_CERT_INSECURE_ALGORITHM) + reason = _("The certificate uses an insecure algorithm"); + + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Our own certificate %1$s failed validation against %2$s: %3$s"), + certFile, cacertFile, reason); + return -1; + } + + return 0; +} + + +gnutls_x509_crt_t virNetTLSCertLoadFromFile(const char *certFile, + bool isServer) +{ + gnutls_datum_t data; + gnutls_x509_crt_t cert = NULL; + g_autofree char *buf = NULL; + int ret = -1; + + VIR_DEBUG("isServer %d certFile %s", + isServer, certFile); + + if (gnutls_x509_crt_init(&cert) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, "%s", + _("Unable to initialize certificate")); + goto cleanup; + } + + if (virFileReadAll(certFile, (1<<16), &buf) < 0) + goto cleanup; + + data.data = (unsigned char *)buf; + data.size = strlen(buf); + + if (gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, isServer ? + _("Unable to import server certificate %1$s") : + _("Unable to import client certificate %1$s"), + certFile); + goto cleanup; + } + + ret = 0; + + cleanup: + if (ret != 0) { + g_clear_pointer(&cert, gnutls_x509_crt_deinit); + } + return cert; +} + + +static int virNetTLSCertLoadCAListFromFile(const char *certFile, + gnutls_x509_crt_t *certs, + unsigned int certMax, + size_t *ncerts) +{ + gnutls_datum_t data; + g_autofree char *buf = NULL; + + *ncerts = 0; + VIR_DEBUG("certFile %s", certFile); + + if (virFileReadAll(certFile, (1<<16), &buf) < 0) + return -1; + + data.data = (unsigned char *)buf; + data.size = strlen(buf); + + if (gnutls_x509_crt_list_import(certs, &certMax, &data, GNUTLS_X509_FMT_PEM, 0) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Unable to import CA certificate list %1$s"), + certFile); + return -1; + } + *ncerts = certMax; + + return 0; +} + + +#define MAX_CERTS 16 +int virNetTLSCertSanityCheck(bool isServer, + const char *cacertFile, + const char *certFile) +{ + gnutls_x509_crt_t cert = NULL; + gnutls_x509_crt_t cacerts[MAX_CERTS] = { 0 }; + size_t ncacerts = 0; + size_t i; + int ret = -1; + + if ((access(certFile, R_OK) == 0) && + !(cert = virNetTLSCertLoadFromFile(certFile, isServer))) + goto cleanup; + if ((access(cacertFile, R_OK) == 0) && + virNetTLSCertLoadCAListFromFile(cacertFile, cacerts, + MAX_CERTS, &ncacerts) < 0) + goto cleanup; + + if (cert && + virNetTLSCertCheck(cert, certFile, isServer, false) < 0) + goto cleanup; + + for (i = 0; i < ncacerts; i++) { + if (virNetTLSCertCheck(cacerts[i], cacertFile, isServer, true) < 0) + goto cleanup; + } + + if (cert && ncacerts && + virNetTLSCertCheckPair(cert, certFile, cacerts, ncacerts, cacertFile, isServer) < 0) + goto cleanup; + + ret = 0; + + cleanup: + if (cert) + gnutls_x509_crt_deinit(cert); + for (i = 0; i < ncacerts; i++) + gnutls_x509_crt_deinit(cacerts[i]); + return ret; +} + +int virNetTLSCertValidateCA(gnutls_x509_crt_t cert, + bool isServer) +{ + if (virNetTLSCertCheckTimes(cert, "[session]", + isServer, true) < 0) { + return -1; + } + return 0; +} + +char *virNetTLSCertValidate(gnutls_x509_crt_t cert, + bool isServer, + const char *hostname, + const char *const *x509dnACL) +{ + size_t dnamesize = 256; + g_autofree char *dname = g_new0(char, dnamesize); + int ret; + + if (virNetTLSCertCheckTimes(cert, "[session]", + isServer, false) < 0) { + return NULL; + } + + ret = gnutls_x509_crt_get_dn(cert, dname, &dnamesize); + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { + VIR_DEBUG("Reallocating dname to fit %zu bytes", dnamesize); + dname = g_realloc(dname, dnamesize); + ret = gnutls_x509_crt_get_dn(cert, dname, &dnamesize); + } + if (ret != 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Failed to get certificate %1$s distinguished name: %2$s"), + "[session]", gnutls_strerror(ret)); + return NULL; + } + + VIR_DEBUG("Peer DN is %s", dname); + + if (virNetTLSCertCheckDN(cert, "[session]", hostname, + dname, x509dnACL) < 0) { + return NULL; + } + + /* !isServer, since on the client, we're validating the + * server's cert, and on the server, the client's cert + */ + if (virNetTLSCertCheckBasicConstraints(cert, "[session]", + !isServer, false) < 0) { + return NULL; + } + + if (virNetTLSCertCheckKeyUsage(cert, "[session]", + false) < 0) { + return NULL; + } + + /* !isServer - as above */ + if (virNetTLSCertCheckKeyPurpose(cert, "[session]", + !isServer) < 0) { + return NULL; + } + + return g_steal_pointer(&dname); +} diff --git a/src/rpc/virnettlscert.h b/src/rpc/virnettlscert.h new file mode 100644 index 0000000000..0ac511a141 --- /dev/null +++ b/src/rpc/virnettlscert.h @@ -0,0 +1,42 @@ +/* + * virnettlscert.h: TLS x509 certificate helpers + * + * Copyright (C) 2010-2024 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <gnutls/gnutls.h> +#include <gnutls/crypto.h> +#include <gnutls/x509.h> + +#include "internal.h" + +int virNetTLSCertSanityCheck(bool isServer, + const char *cacertFile, + const char *certFile); + +int virNetTLSCertValidateCA(gnutls_x509_crt_t cert, + bool isServer); + +char *virNetTLSCertValidate(gnutls_x509_crt_t cert, + bool isServer, + const char *hostname, + const char *const *x509dnACL); + +gnutls_x509_crt_t virNetTLSCertLoadFromFile(const char *certFile, + bool isServer); diff --git a/src/rpc/virnettlscontext.c b/src/rpc/virnettlscontext.c index a56abee009..e8023133b4 100644 --- a/src/rpc/virnettlscontext.c +++ b/src/rpc/virnettlscontext.c @@ -28,6 +28,7 @@ #include "virnettlscontext.h" #include "virnettlsconfig.h" +#include "virnettlscert.h" #include "virstring.h" #include "viralloc.h" @@ -110,466 +111,6 @@ static void virNetTLSLog(int level G_GNUC_UNUSED, } -static int virNetTLSContextCheckCertTimes(gnutls_x509_crt_t cert, - const char *certFile, - bool isServer, - bool isCA) -{ - time_t now; - - if ((now = time(NULL)) == ((time_t)-1)) { - virReportSystemError(errno, "%s", - _("cannot get current time")); - return -1; - } - - if (gnutls_x509_crt_get_expiration_time(cert) < now) { - virReportError(VIR_ERR_SYSTEM_ERROR, - (isCA ? - _("The CA certificate %1$s has expired") : - (isServer ? - _("The server certificate %1$s has expired") : - _("The client certificate %1$s has expired"))), - certFile); - return -1; - } - - if (gnutls_x509_crt_get_activation_time(cert) > now) { - virReportError(VIR_ERR_SYSTEM_ERROR, - (isCA ? - _("The CA certificate %1$s is not yet active") : - (isServer ? - _("The server certificate %1$s is not yet active") : - _("The client certificate %1$s is not yet active"))), - certFile); - return -1; - } - - return 0; -} - - -static int virNetTLSContextCheckCertBasicConstraints(gnutls_x509_crt_t cert, - const char *certFile, - bool isServer, - bool isCA) -{ - int status; - - status = gnutls_x509_crt_get_basic_constraints(cert, NULL, NULL, NULL); - VIR_DEBUG("Cert %s basic constraints %d", certFile, status); - - if (status > 0) { /* It is a CA cert */ - if (!isCA) { - virReportError(VIR_ERR_SYSTEM_ERROR, isServer ? - _("The certificate %1$s basic constraints show a CA, but we need one for a server") : - _("The certificate %1$s basic constraints show a CA, but we need one for a client"), - certFile); - return -1; - } - } else if (status == 0) { /* It is not a CA cert */ - if (isCA) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("The certificate %1$s basic constraints do not show a CA"), - certFile); - return -1; - } - } else if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { /* Missing basicConstraints */ - if (isCA) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("The certificate %1$s is missing basic constraints for a CA"), - certFile); - return -1; - } - } else { /* General error */ - virReportError(VIR_ERR_SYSTEM_ERROR, - _("Unable to query certificate %1$s basic constraints %2$s"), - certFile, gnutls_strerror(status)); - return -1; - } - - return 0; -} - - -static int virNetTLSContextCheckCertKeyUsage(gnutls_x509_crt_t cert, - const char *certFile, - bool isCA) -{ - int status; - unsigned int usage = 0; - unsigned int critical = 0; - - status = gnutls_x509_crt_get_key_usage(cert, &usage, &critical); - - VIR_DEBUG("Cert %s key usage status %d usage %d critical %u", certFile, status, usage, critical); - if (status < 0) { - if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { - usage = isCA ? GNUTLS_KEY_KEY_CERT_SIGN : - GNUTLS_KEY_DIGITAL_SIGNATURE|GNUTLS_KEY_KEY_ENCIPHERMENT; - } else { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("Unable to query certificate %1$s key usage %2$s"), - certFile, gnutls_strerror(status)); - return -1; - } - } - - if (isCA) { - if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) { - if (critical) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("Certificate %1$s usage does not permit certificate signing"), - certFile); - return -1; - } else { - VIR_WARN("Certificate %s usage does not permit certificate signing", - certFile); - } - } - } else { - if (!(usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) { - if (critical) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("Certificate %1$s usage does not permit digital signature"), - certFile); - return -1; - } else { - VIR_WARN("Certificate %s usage does not permit digital signature", - certFile); - } - } - if (!(usage & GNUTLS_KEY_KEY_ENCIPHERMENT)) { - if (critical) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("Certificate %1$s usage does not permit key encipherment"), - certFile); - return -1; - } else { - VIR_WARN("Certificate %s usage does not permit key encipherment", - certFile); - } - } - } - - return 0; -} - - -static int virNetTLSContextCheckCertKeyPurpose(gnutls_x509_crt_t cert, - const char *certFile, - bool isServer) -{ - int status; - size_t i; - unsigned int purposeCritical; - unsigned int critical; - char *buffer = NULL; - size_t size; - bool allowClient = false, allowServer = false; - - critical = 0; - for (i = 0; ; i++) { - size = 0; - status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, &size, NULL); - - if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { - VIR_DEBUG("No key purpose data available at slot %zu", i); - - /* If there is no data at all, then we must allow client/server to pass */ - if (i == 0) - allowServer = allowClient = true; - break; - } - if (status != GNUTLS_E_SHORT_MEMORY_BUFFER) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("Unable to query certificate %1$s key purpose %2$s"), - certFile, gnutls_strerror(status)); - return -1; - } - - buffer = g_new0(char, size); - status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, &size, &purposeCritical); - if (status < 0) { - VIR_FREE(buffer); - virReportError(VIR_ERR_SYSTEM_ERROR, - _("Unable to query certificate %1$s key purpose %2$s"), - certFile, gnutls_strerror(status)); - return -1; - } - if (purposeCritical) - critical = true; - - VIR_DEBUG("Key purpose %d %s critical %u", status, buffer, purposeCritical); - if (STREQ(buffer, GNUTLS_KP_TLS_WWW_SERVER)) { - allowServer = true; - } else if (STREQ(buffer, GNUTLS_KP_TLS_WWW_CLIENT)) { - allowClient = true; - } else if (STRNEQ(buffer, GNUTLS_KP_ANY)) { - allowServer = allowClient = true; - } - - VIR_FREE(buffer); - } - - if (isServer) { - if (!allowServer) { - if (critical) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("Certificate %1$s purpose does not allow use for with a TLS server"), - certFile); - return -1; - } else { - VIR_WARN("Certificate %s purpose does not allow use for with a TLS server", - certFile); - } - } - } else { - if (!allowClient) { - if (critical) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("Certificate %1$s purpose does not allow use for with a TLS client"), - certFile); - return -1; - } else { - VIR_WARN("Certificate %s purpose does not allow use for with a TLS client", - certFile); - } - } - } - - return 0; -} - -/* Check DN is on tls_allowed_dn_list. */ -static int -virNetTLSContextCheckCertDNACL(const char *dname, - const char *const *wildcards) -{ - while (*wildcards) { - if (g_pattern_match_simple(*wildcards, dname)) - return 1; - - wildcards++; - } - - /* Log the client's DN for debugging */ - VIR_DEBUG("Failed ACL check for client DN '%s'", dname); - - /* This is the most common error: make it informative. */ - virReportError(VIR_ERR_SYSTEM_ERROR, "%s", - _("Client's Distinguished Name is not on the list of allowed clients (tls_allowed_dn_list). Use 'virt-pki-query-dn clientcert.pem' to view the Distinguished Name field in the client certificate, or run this daemon with --verbose option.")); - return 0; -} - - -static int -virNetTLSContextCheckCertDN(gnutls_x509_crt_t cert, - const char *certFile, - const char *hostname, - const char *dname, - const char *const *acl) -{ - if (acl && dname && - virNetTLSContextCheckCertDNACL(dname, acl) <= 0) - return -1; - - if (hostname && - !gnutls_x509_crt_check_hostname(cert, hostname)) { - virReportError(VIR_ERR_RPC, - _("Certificate %1$s owner does not match the hostname %2$s"), - certFile, hostname); - return -1; - } - - return 0; -} - - -static int virNetTLSContextCheckCert(gnutls_x509_crt_t cert, - const char *certFile, - bool isServer, - bool isCA) -{ - if (virNetTLSContextCheckCertTimes(cert, certFile, - isServer, isCA) < 0) - return -1; - - if (virNetTLSContextCheckCertBasicConstraints(cert, certFile, - isServer, isCA) < 0) - return -1; - - if (virNetTLSContextCheckCertKeyUsage(cert, certFile, - isCA) < 0) - return -1; - - if (!isCA && - virNetTLSContextCheckCertKeyPurpose(cert, certFile, - isServer) < 0) - return -1; - - return 0; -} - - -static int virNetTLSContextCheckCertPair(gnutls_x509_crt_t cert, - const char *certFile, - gnutls_x509_crt_t *cacerts, - size_t ncacerts, - const char *cacertFile, - bool isServer) -{ - unsigned int status; - - if (gnutls_x509_crt_list_verify(&cert, 1, - cacerts, ncacerts, - NULL, 0, - 0, &status) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, isServer ? - _("Unable to verify server certificate %1$s against CA certificate %2$s") : - _("Unable to verify client certificate %1$s against CA certificate %2$s"), - certFile, cacertFile); - return -1; - } - - if (status != 0) { - const char *reason = _("Invalid certificate"); - - if (status & GNUTLS_CERT_INVALID) - reason = _("The certificate is not trusted."); - - if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) - reason = _("The certificate hasn't got a known issuer."); - - if (status & GNUTLS_CERT_REVOKED) - reason = _("The certificate has been revoked."); - - if (status & GNUTLS_CERT_INSECURE_ALGORITHM) - reason = _("The certificate uses an insecure algorithm"); - - virReportError(VIR_ERR_SYSTEM_ERROR, - _("Our own certificate %1$s failed validation against %2$s: %3$s"), - certFile, cacertFile, reason); - return -1; - } - - return 0; -} - - -static gnutls_x509_crt_t virNetTLSContextLoadCertFromFile(const char *certFile, - bool isServer) -{ - gnutls_datum_t data; - gnutls_x509_crt_t cert = NULL; - g_autofree char *buf = NULL; - int ret = -1; - - VIR_DEBUG("isServer %d certFile %s", - isServer, certFile); - - if (gnutls_x509_crt_init(&cert) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, "%s", - _("Unable to initialize certificate")); - goto cleanup; - } - - if (virFileReadAll(certFile, (1<<16), &buf) < 0) - goto cleanup; - - data.data = (unsigned char *)buf; - data.size = strlen(buf); - - if (gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, isServer ? - _("Unable to import server certificate %1$s") : - _("Unable to import client certificate %1$s"), - certFile); - goto cleanup; - } - - ret = 0; - - cleanup: - if (ret != 0) { - g_clear_pointer(&cert, gnutls_x509_crt_deinit); - } - return cert; -} - - -static int virNetTLSContextLoadCACertListFromFile(const char *certFile, - gnutls_x509_crt_t *certs, - unsigned int certMax, - size_t *ncerts) -{ - gnutls_datum_t data; - g_autofree char *buf = NULL; - - *ncerts = 0; - VIR_DEBUG("certFile %s", certFile); - - if (virFileReadAll(certFile, (1<<16), &buf) < 0) - return -1; - - data.data = (unsigned char *)buf; - data.size = strlen(buf); - - if (gnutls_x509_crt_list_import(certs, &certMax, &data, GNUTLS_X509_FMT_PEM, 0) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("Unable to import CA certificate list %1$s"), - certFile); - return -1; - } - *ncerts = certMax; - - return 0; -} - - -#define MAX_CERTS 16 -static int virNetTLSContextSanityCheckCredentials(bool isServer, - const char *cacertFile, - const char *certFile) -{ - gnutls_x509_crt_t cert = NULL; - gnutls_x509_crt_t cacerts[MAX_CERTS] = { 0 }; - size_t ncacerts = 0; - size_t i; - int ret = -1; - - if ((access(certFile, R_OK) == 0) && - !(cert = virNetTLSContextLoadCertFromFile(certFile, isServer))) - goto cleanup; - if ((access(cacertFile, R_OK) == 0) && - virNetTLSContextLoadCACertListFromFile(cacertFile, cacerts, - MAX_CERTS, &ncacerts) < 0) - goto cleanup; - - if (cert && - virNetTLSContextCheckCert(cert, certFile, isServer, false) < 0) - goto cleanup; - - for (i = 0; i < ncacerts; i++) { - if (virNetTLSContextCheckCert(cacerts[i], cacertFile, isServer, true) < 0) - goto cleanup; - } - - if (cert && ncacerts && - virNetTLSContextCheckCertPair(cert, certFile, cacerts, ncacerts, cacertFile, isServer) < 0) - goto cleanup; - - ret = 0; - - cleanup: - if (cert) - gnutls_x509_crt_deinit(cert); - for (i = 0; i < ncacerts; i++) - gnutls_x509_crt_deinit(cacerts[i]); - return ret; -} - - static int virNetTLSContextLoadCredentials(virNetTLSContext *ctxt, bool isServer, const char *cacert, @@ -683,7 +224,7 @@ static virNetTLSContext *virNetTLSContextNew(const char *cacert, } if (sanityCheckCert && - virNetTLSContextSanityCheckCredentials(isServer, cacert, cert) < 0) + virNetTLSCertSanityCheck(isServer, cacert, cert) < 0) goto error; if (virNetTLSContextLoadCredentials(ctxt, isServer, cacert, cacrl, cert, key) < 0) @@ -846,7 +387,7 @@ int virNetTLSContextReloadForServer(virNetTLSContext *ctxt, goto error; } - if (virNetTLSContextSanityCheckCredentials(true, cacert, cert)) + if (virNetTLSCertSanityCheck(true, cacert, cert)) goto error; if (virNetTLSContextLoadCredentials(ctxt, true, cacert, cacrl, cert, key)) @@ -876,65 +417,6 @@ virNetTLSContext *virNetTLSContextNewClient(const char *cacert, sanityCheckCert, requireValidCert, false); } -static int virNetTLSContextCertValidateCA(gnutls_x509_crt_t cert, - bool isServer) -{ - if (virNetTLSContextCheckCertTimes(cert, "[session]", isServer, true) < 0) - return -1; - - return 0; -} - -static char *virNetTLSContextCertValidate(gnutls_x509_crt_t cert, - bool isServer, - const char *hostname, - const char *const *x509dnACL) -{ - size_t dnamesize = 256; - g_autofree char *dname = g_new0(char, dnamesize); - int ret; - - if (virNetTLSContextCheckCertTimes(cert, "[session]", - isServer, false) < 0) - return NULL; - - ret = gnutls_x509_crt_get_dn(cert, dname, &dnamesize); - if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { - VIR_DEBUG("Reallocating dname to fit %zu bytes", dnamesize); - dname = g_realloc(dname, dnamesize); - ret = gnutls_x509_crt_get_dn(cert, dname, &dnamesize); - } - if (ret != 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("Failed to get certificate %1$s distinguished name: %2$s"), - "[session]", gnutls_strerror(ret)); - return NULL; - } - - VIR_DEBUG("Peer DN is %s", dname); - - if (virNetTLSContextCheckCertDN(cert, "[session]", hostname, - dname, x509dnACL) < 0) - return NULL; - - /* !isServer, since on the client, we're validating the - * server's cert, and on the server, the client's cert - */ - if (virNetTLSContextCheckCertBasicConstraints(cert, "[session]", - !isServer, false) < 0) - return NULL; - - if (virNetTLSContextCheckCertKeyUsage(cert, "[session]", - false) < 0) - return NULL; - - /* !isServer - as above */ - if (virNetTLSContextCheckCertKeyPurpose(cert, "[session]", - !isServer) < 0) - return NULL; - - return g_steal_pointer(&dname); -} static int virNetTLSContextValidCertificate(virNetTLSContext *ctxt, virNetTLSSession *sess) @@ -1005,15 +487,16 @@ static int virNetTLSContextValidCertificate(virNetTLSContext *ctxt, } if (i == 0) { - if (!(sess->x509dname = virNetTLSContextCertValidate(cert, - sess->isServer, - sess->hostname, - ctxt->x509dnACL))) { + if (!(sess->x509dname = virNetTLSCertValidate(cert, + sess->isServer, + sess->hostname, + ctxt->x509dnACL))) { gnutls_x509_crt_deinit(cert); goto authdeny; } } else { - if (virNetTLSContextCertValidateCA(cert, sess->isServer) < 0) { + if (virNetTLSCertValidateCA(cert, + sess->isServer) < 0) { gnutls_x509_crt_deinit(cert); goto authdeny; } -- 2.43.0

On 6/7/24 16:26, Daniel P. Berrangé wrote:
The TLS cert validation logic will be reused for the new impl of the virt-pki-validate tool.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- po/POTFILES | 1 + src/rpc/meson.build | 1 + src/rpc/virnettlscert.c | 553 +++++++++++++++++++++++++++++++++++++ src/rpc/virnettlscert.h | 42 +++ src/rpc/virnettlscontext.c | 535 +---------------------------------- 5 files changed, 606 insertions(+), 526 deletions(-) create mode 100644 src/rpc/virnettlscert.c create mode 100644 src/rpc/virnettlscert.h
diff --git a/po/POTFILES b/po/POTFILES index 4ad7e19e08..0f68c652eb 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -219,6 +219,7 @@ src/rpc/virnetserverprogram.c src/rpc/virnetserverservice.c src/rpc/virnetsocket.c src/rpc/virnetsshsession.c +src/rpc/virnettlscert.c src/rpc/virnettlscontext.c src/secret/secret_driver.c src/security/security_apparmor.c diff --git a/src/rpc/meson.build b/src/rpc/meson.build index d11d532d0f..8bdbf5c88f 100644 --- a/src/rpc/meson.build +++ b/src/rpc/meson.build @@ -2,6 +2,7 @@ gendispatch_prog = find_program('gendispatch.pl')
tlsconfig_sources = [ files('virnettlsconfig.c'), + files('virnettlscert.c'), ]
tlsconfig_source = files( 'virnettlsconfig.c', 'virnettlscert.c' ) Michal

When first writing the manpage in commit 3decd4f9f1b55000770f4203f98438b6f791256d Author: Daniel P. Berrangé <berrange@redhat.com> Date: Wed Sep 16 14:42:57 2009 +0100 Make pki_check.sh into an installed & supported tool I incorrectly credited Richard, instead of Daniel, who was the author per commit 62442d578d123de37ab27ae139f1270fc02e837c Author: Daniel Veillard <veillard@redhat.com> Date: Thu Jul 12 15:47:19 2007 +0000 * docs/libvir.html docs/remote.html: update the remote page, add an index * docs/pki_check.sh: shell script to check the PKI and client/server environment. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- docs/manpages/virt-pki-validate.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manpages/virt-pki-validate.rst b/docs/manpages/virt-pki-validate.rst index 063448f48b..53d5fbe269 100644 --- a/docs/manpages/virt-pki-validate.rst +++ b/docs/manpages/virt-pki-validate.rst @@ -48,7 +48,7 @@ failure a non-zero status will be set. AUTHOR ====== -Richard Jones +Daniel Veillard BUGS -- 2.43.0

The common messaging helpers will be reused in the new impl of the virt-pki-validate tool. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- po/POTFILES | 1 + tools/meson.build | 1 + tools/virt-host-validate-ch.c | 12 +- tools/virt-host-validate-common.c | 308 +++++++++++------------------- tools/virt-host-validate-common.h | 48 +---- tools/virt-host-validate-lxc.c | 18 +- tools/virt-host-validate-qemu.c | 30 +-- tools/virt-host-validate.c | 2 +- tools/virt-validate-common.c | 110 +++++++++++ tools/virt-validate-common.h | 57 ++++++ 10 files changed, 319 insertions(+), 268 deletions(-) create mode 100644 tools/virt-validate-common.c create mode 100644 tools/virt-validate-common.h diff --git a/po/POTFILES b/po/POTFILES index 0f68c652eb..cb73d07904 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -391,6 +391,7 @@ tools/virt-host-validate-qemu.c tools/virt-host-validate.c tools/virt-login-shell-helper.c tools/virt-pki-query-dn.c +tools/virt-validate-common.c tools/vsh-table.c tools/vsh.c tools/vsh.h diff --git a/tools/meson.build b/tools/meson.build index 1bb84be0be..feb6b7c3ad 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -40,6 +40,7 @@ libvirt_shell_lib = static_library( if conf.has('WITH_HOST_VALIDATE') virt_host_validate_sources = [ + 'virt-validate-common.c', 'virt-host-validate.c', 'virt-host-validate-common.c', ] diff --git a/tools/virt-host-validate-ch.c b/tools/virt-host-validate-ch.c index 3f96423836..e48c8c825f 100644 --- a/tools/virt-host-validate-ch.c +++ b/tools/virt-host-validate-ch.c @@ -57,21 +57,21 @@ int virHostValidateCh(void) } if (hasVirtFlag) { - virHostMsgCheck("CH", "%s", _("Checking for hardware virtualization")); + virValidateCheck("CH", "%s", _("Checking for hardware virtualization")); if (hasHwVirt) { - virHostMsgPass(); + virValidatePass(); } else { - virHostMsgFail(VIR_HOST_VALIDATE_FAIL, - _("Only emulated CPUs are available, performance will be significantly limited")); + virValidateFail(VIR_VALIDATE_FAIL, + _("Only emulated CPUs are available, performance will be significantly limited")); ret = -1; } } if (hasHwVirt || !hasVirtFlag) { - if (virHostValidateDeviceExists("CH", "/dev/kvm", VIR_HOST_VALIDATE_FAIL, + if (virHostValidateDeviceExists("CH", "/dev/kvm", VIR_VALIDATE_FAIL, kvmhint) < 0) ret = -1; - else if (virHostValidateDeviceAccessible("CH", "/dev/kvm", VIR_HOST_VALIDATE_FAIL, + else if (virHostValidateDeviceAccessible("CH", "/dev/kvm", VIR_VALIDATE_FAIL, _("Check /dev/kvm is world writable or you are in a group that is allowed to access it")) < 0) ret = -1; } diff --git a/tools/virt-host-validate-common.c b/tools/virt-host-validate-common.c index 66f08ac2b3..ad06dfb245 100644 --- a/tools/virt-host-validate-common.c +++ b/tools/virt-host-validate-common.c @@ -45,144 +45,58 @@ VIR_ENUM_IMPL(virHostValidateCPUFlag, "158", "sev"); -static bool quiet; - -void virHostMsgSetQuiet(bool quietFlag) -{ - quiet = quietFlag; -} - -void virHostMsgCheck(const char *prefix, - const char *format, - ...) -{ - va_list args; - g_autofree char *msg = NULL; - - if (quiet) - return; - - va_start(args, format); - msg = g_strdup_vprintf(format, args); - va_end(args); - - fprintf(stdout, "%1$6s: %2$-69s: ", prefix, msg); -} - -static bool virHostMsgWantEscape(void) -{ - static bool detectTty = true; - static bool wantEscape; - if (detectTty) { - if (isatty(STDOUT_FILENO)) - wantEscape = true; - detectTty = false; - } - return wantEscape; -} - -void virHostMsgPass(void) -{ - if (quiet) - return; - - if (virHostMsgWantEscape()) - fprintf(stdout, "\033[32m%s\033[0m\n", _("PASS")); - else - fprintf(stdout, "%s\n", _("PASS")); -} - - -static const char * failMessages[] = { - N_("FAIL"), - N_("WARN"), - N_("NOTE"), -}; - -G_STATIC_ASSERT(G_N_ELEMENTS(failMessages) == VIR_HOST_VALIDATE_LAST); - -static const char *failEscapeCodes[] = { - "\033[31m", - "\033[33m", - "\033[34m", -}; - -G_STATIC_ASSERT(G_N_ELEMENTS(failEscapeCodes) == VIR_HOST_VALIDATE_LAST); - -void virHostMsgFail(virHostValidateLevel level, - const char *format, - ...) -{ - va_list args; - g_autofree char *msg = NULL; - - if (quiet) - return; - - va_start(args, format); - msg = g_strdup_vprintf(format, args); - va_end(args); - - if (virHostMsgWantEscape()) - fprintf(stdout, "%s%s\033[0m (%s)\n", - failEscapeCodes[level], _(failMessages[level]), msg); - else - fprintf(stdout, "%s (%s)\n", - _(failMessages[level]), msg); -} - int virHostValidateDeviceExists(const char *hvname, const char *dev_name, - virHostValidateLevel level, + virValidateLevel level, const char *hint) { - virHostMsgCheck(hvname, _("Checking if device '%1$s' exists"), dev_name); + virValidateCheck(hvname, _("Checking if device '%1$s' exists"), dev_name); if (access(dev_name, F_OK) < 0) { - virHostMsgFail(level, "%s", hint); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, "%s", hint); + return VIR_VALIDATE_FAILURE(level); } - virHostMsgPass(); + virValidatePass(); return 0; } int virHostValidateDeviceAccessible(const char *hvname, const char *dev_name, - virHostValidateLevel level, + virValidateLevel level, const char *hint) { - virHostMsgCheck(hvname, _("Checking if device '%1$s' is accessible"), dev_name); + virValidateCheck(hvname, _("Checking if device '%1$s' is accessible"), dev_name); if (access(dev_name, R_OK|W_OK) < 0) { - virHostMsgFail(level, "%s", hint); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, "%s", hint); + return VIR_VALIDATE_FAILURE(level); } - virHostMsgPass(); + virValidatePass(); return 0; } int virHostValidateNamespace(const char *hvname, const char *ns_name, - virHostValidateLevel level, + virValidateLevel level, const char *hint) { char nspath[100]; - virHostMsgCheck(hvname, _("Checking for namespace '%1$s'"), ns_name); + virValidateCheck(hvname, _("Checking for namespace '%1$s'"), ns_name); g_snprintf(nspath, sizeof(nspath), "/proc/self/ns/%s", ns_name); if (access(nspath, F_OK) < 0) { - virHostMsgFail(level, "%s", hint); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, "%s", hint); + return VIR_VALIDATE_FAILURE(level); } - virHostMsgPass(); + virValidatePass(); return 0; } @@ -248,7 +162,7 @@ virBitmap *virHostValidateGetCPUFlags(void) int virHostValidateLinuxKernel(const char *hvname, int version, - virHostValidateLevel level, + virValidateLevel level, const char *hint) { struct utsname uts; @@ -256,26 +170,26 @@ int virHostValidateLinuxKernel(const char *hvname, uname(&uts); - virHostMsgCheck(hvname, _("Checking for Linux >= %1$d.%2$d.%3$d"), - ((version >> 16) & 0xff), - ((version >> 8) & 0xff), - (version & 0xff)); + virValidateCheck(hvname, _("Checking for Linux >= %1$d.%2$d.%3$d"), + ((version >> 16) & 0xff), + ((version >> 8) & 0xff), + (version & 0xff)); if (STRNEQ(uts.sysname, "Linux")) { - virHostMsgFail(level, "%s", hint); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, "%s", hint); + return VIR_VALIDATE_FAILURE(level); } if (virStringParseVersion(&thisversion, uts.release, true) < 0) { - virHostMsgFail(level, "%s", hint); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, "%s", hint); + return VIR_VALIDATE_FAILURE(level); } if (thisversion < version) { - virHostMsgFail(level, "%s", hint); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, "%s", hint); + return VIR_VALIDATE_FAILURE(level); } else { - virHostMsgPass(); + virValidatePass(); return 0; } } @@ -283,7 +197,7 @@ int virHostValidateLinuxKernel(const char *hvname, #ifdef __linux__ int virHostValidateCGroupControllers(const char *hvname, int controllers, - virHostValidateLevel level) + virValidateLevel level) { g_autoptr(virCgroup) group = NULL; int ret = 0; @@ -292,7 +206,7 @@ int virHostValidateCGroupControllers(const char *hvname, if (virCgroupNew("/", -1, &group) < 0) { fprintf(stderr, "Unable to initialize cgroups: %s\n", virGetLastErrorMessage()); - return VIR_HOST_VALIDATE_FAILURE(level); + return VIR_VALIDATE_FAILURE(level); } for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) { @@ -302,15 +216,15 @@ int virHostValidateCGroupControllers(const char *hvname, if (!(controllers & flag)) continue; - virHostMsgCheck(hvname, _("Checking for cgroup '%1$s' controller support"), cg_name); + virValidateCheck(hvname, _("Checking for cgroup '%1$s' controller support"), cg_name); if (!virCgroupHasController(group, i)) { - ret = VIR_HOST_VALIDATE_FAILURE(level); - virHostMsgFail(level, "Enable '%s' in kernel Kconfig file or " - "mount/enable cgroup controller in your system", - cg_name); + ret = VIR_VALIDATE_FAILURE(level); + virValidateFail(level, "Enable '%s' in kernel Kconfig file or " + "mount/enable cgroup controller in your system", + cg_name); } else { - virHostMsgPass(); + virValidatePass(); } } @@ -319,15 +233,15 @@ int virHostValidateCGroupControllers(const char *hvname, #else /* !__linux__ */ int virHostValidateCGroupControllers(const char *hvname G_GNUC_UNUSED, int controllers G_GNUC_UNUSED, - virHostValidateLevel level) + virValidateLevel level) { - virHostMsgFail(level, "%s", "This platform does not support cgroups"); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, "%s", "This platform does not support cgroups"); + return VIR_VALIDATE_FAILURE(level); } #endif /* !__linux__ */ int virHostValidateIOMMU(const char *hvname, - virHostValidateLevel level) + virValidateLevel level) { g_autoptr(virBitmap) flags = NULL; struct stat sb; @@ -337,7 +251,7 @@ int virHostValidateIOMMU(const char *hvname, struct dirent *dent; int rc; - virHostMsgCheck(hvname, "%s", _("Checking for device assignment IOMMU support")); + virValidateCheck(hvname, "%s", _("Checking for device assignment IOMMU support")); flags = virHostValidateGetCPUFlags(); @@ -348,28 +262,28 @@ int virHostValidateIOMMU(const char *hvname, if (isIntel) { if (access("/sys/firmware/acpi/tables/DMAR", F_OK) == 0) { - virHostMsgPass(); + virValidatePass(); bootarg = "intel_iommu=on"; } else { - virHostMsgFail(level, - "No ACPI DMAR table found, IOMMU either " - "disabled in BIOS or not supported by this " - "hardware platform"); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, + "No ACPI DMAR table found, IOMMU either " + "disabled in BIOS or not supported by this " + "hardware platform"); + return VIR_VALIDATE_FAILURE(level); } } else if (isAMD) { if (access("/sys/firmware/acpi/tables/IVRS", F_OK) == 0) { - virHostMsgPass(); + virValidatePass(); bootarg = "iommu=pt iommu=1"; } else { - virHostMsgFail(level, - "No ACPI IVRS table found, IOMMU either " - "disabled in BIOS or not supported by this " - "hardware platform"); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, + "No ACPI IVRS table found, IOMMU either " + "disabled in BIOS or not supported by this " + "hardware platform"); + return VIR_VALIDATE_FAILURE(level); } } else if (ARCH_IS_PPC64(arch)) { - virHostMsgPass(); + virValidatePass(); } else if (ARCH_IS_S390(arch)) { g_autoptr(DIR) dir = NULL; @@ -378,41 +292,41 @@ int virHostValidateIOMMU(const char *hvname, * no PCI devices the directory is still there but is * empty. */ if (!virDirOpen(&dir, "/sys/bus/pci/devices")) { - virHostMsgFail(VIR_HOST_VALIDATE_NOTE, - "Skipped - PCI support disabled"); - return VIR_HOST_VALIDATE_FAILURE(VIR_HOST_VALIDATE_NOTE); + virValidateFail(VIR_VALIDATE_NOTE, + "Skipped - PCI support disabled"); + return VIR_VALIDATE_FAILURE(VIR_VALIDATE_NOTE); } rc = virDirRead(dir, &dent, NULL); if (rc <= 0) { - virHostMsgFail(VIR_HOST_VALIDATE_NOTE, - "Skipped - No PCI devices are online"); - return VIR_HOST_VALIDATE_FAILURE(VIR_HOST_VALIDATE_NOTE); + virValidateFail(VIR_VALIDATE_NOTE, + "Skipped - No PCI devices are online"); + return VIR_VALIDATE_FAILURE(VIR_VALIDATE_NOTE); } - virHostMsgPass(); + virValidatePass(); } else if (ARCH_IS_ARM(arch)) { if (access("/sys/firmware/acpi/tables/IORT", F_OK) != 0) { - virHostMsgFail(level, - "No ACPI IORT table found, IOMMU not " - "supported by this hardware platform"); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, + "No ACPI IORT table found, IOMMU not " + "supported by this hardware platform"); + return VIR_VALIDATE_FAILURE(level); } else { rc = virAcpiHasSMMU(); if (rc < 0) { - virHostMsgFail(level, - "Failed to parse ACPI IORT table"); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, + "Failed to parse ACPI IORT table"); + return VIR_VALIDATE_FAILURE(level); } else if (rc == 0) { - virHostMsgFail(level, - "No SMMU found"); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, + "No SMMU found"); + return VIR_VALIDATE_FAILURE(level); } else { - virHostMsgPass(); + virValidatePass(); } } } else { - virHostMsgFail(level, - "Unknown if this platform has IOMMU support"); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, + "Unknown if this platform has IOMMU support"); + return VIR_VALIDATE_FAILURE(level); } @@ -423,17 +337,17 @@ int virHostValidateIOMMU(const char *hvname, if (!S_ISDIR(sb.st_mode)) return 0; - virHostMsgCheck(hvname, "%s", _("Checking if IOMMU is enabled by kernel")); + virValidateCheck(hvname, "%s", _("Checking if IOMMU is enabled by kernel")); if (sb.st_nlink <= 2) { if (bootarg) - virHostMsgFail(level, - "IOMMU appears to be disabled in kernel. " - "Add %s to kernel cmdline arguments", bootarg); + virValidateFail(level, + "IOMMU appears to be disabled in kernel. " + "Add %s to kernel cmdline arguments", bootarg); else - virHostMsgFail(level, "IOMMU capability not compiled into kernel."); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, "IOMMU capability not compiled into kernel."); + return VIR_VALIDATE_FAILURE(level); } - virHostMsgPass(); + virValidatePass(); return 0; } @@ -466,7 +380,7 @@ bool virHostKernelModuleIsLoaded(const char *module) int virHostValidateSecureGuests(const char *hvname, - virHostValidateLevel level) + virValidateLevel level) { g_autoptr(virBitmap) flags = NULL; bool hasFac158 = false; @@ -483,13 +397,13 @@ int virHostValidateSecureGuests(const char *hvname, else if (flags && virBitmapIsBitSet(flags, VIR_HOST_VALIDATE_CPU_FLAG_SEV)) hasAMDSev = true; - virHostMsgCheck(hvname, "%s", _("Checking for secure guest support")); + virValidateCheck(hvname, "%s", _("Checking for secure guest support")); if (ARCH_IS_S390(arch)) { if (hasFac158) { if (!virFileIsDir("/sys/firmware/uv")) { - virHostMsgFail(level, "IBM Secure Execution not supported by " - "the currently used kernel"); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, "IBM Secure Execution not supported by " + "the currently used kernel"); + return VIR_VALIDATE_FAILURE(level); } /* we're prefix matching rather than equality matching here, because @@ -501,47 +415,47 @@ int virHostValidateSecureGuests(const char *hvname, G_N_ELEMENTS(kIBMValues), VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST | VIR_KERNEL_CMDLINE_FLAGS_CMP_PREFIX)) { - virHostMsgPass(); + virValidatePass(); return 1; } else { - virHostMsgFail(level, - "IBM Secure Execution appears to be disabled " - "in kernel. Add prot_virt=1 to kernel cmdline " - "arguments"); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, + "IBM Secure Execution appears to be disabled " + "in kernel. Add prot_virt=1 to kernel cmdline " + "arguments"); + return VIR_VALIDATE_FAILURE(level); } } else { - virHostMsgFail(level, "Hardware or firmware does not provide " - "support for IBM Secure Execution"); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, "Hardware or firmware does not provide " + "support for IBM Secure Execution"); + return VIR_VALIDATE_FAILURE(level); } } else if (hasAMDSev) { if (virFileReadValueString(&mod_value, "/sys/module/kvm_amd/parameters/sev") < 0) { - virHostMsgFail(level, "AMD Secure Encrypted Virtualization not " - "supported by the currently used kernel"); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, "AMD Secure Encrypted Virtualization not " + "supported by the currently used kernel"); + return VIR_VALIDATE_FAILURE(level); } if (mod_value[0] != '1' && mod_value[0] != 'Y' && mod_value[0] != 'y') { - virHostMsgFail(level, - "AMD Secure Encrypted Virtualization appears to be " - "disabled in kernel. Add kvm_amd.sev=1 " - "to the kernel cmdline arguments"); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, + "AMD Secure Encrypted Virtualization appears to be " + "disabled in kernel. Add kvm_amd.sev=1 " + "to the kernel cmdline arguments"); + return VIR_VALIDATE_FAILURE(level); } if (virFileExists("/dev/sev")) { - virHostMsgPass(); + virValidatePass(); return 1; } else { - virHostMsgFail(level, - "AMD Secure Encrypted Virtualization appears to be " - "disabled in firmware."); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, + "AMD Secure Encrypted Virtualization appears to be " + "disabled in firmware."); + return VIR_VALIDATE_FAILURE(level); } } - virHostMsgFail(level, - "Unknown if this platform has Secure Guest support"); - return VIR_HOST_VALIDATE_FAILURE(level); + virValidateFail(level, + "Unknown if this platform has Secure Guest support"); + return VIR_VALIDATE_FAILURE(level); } diff --git a/tools/virt-host-validate-common.h b/tools/virt-host-validate-common.h index 9334fa8588..7fb3545fe3 100644 --- a/tools/virt-host-validate-common.h +++ b/tools/virt-host-validate-common.h @@ -24,14 +24,7 @@ #include "internal.h" #include "virbitmap.h" #include "virenum.h" - -typedef enum { - VIR_HOST_VALIDATE_FAIL, - VIR_HOST_VALIDATE_WARN, - VIR_HOST_VALIDATE_NOTE, - - VIR_HOST_VALIDATE_LAST, -} virHostValidateLevel; +#include "virt-validate-common.h" typedef enum { VIR_HOST_VALIDATE_CPU_FLAG_VMX = 0, @@ -45,61 +38,36 @@ typedef enum { VIR_ENUM_DECL(virHostValidateCPUFlag); -/** - * VIR_HOST_VALIDATE_FAILURE - * @level: the virHostValidateLevel to be checked - * - * This macro is to be used in to return a failures based on the - * virHostValidateLevel use in the function. - * - * If the virHostValidateLevel is VIR_HOST_VALIDATE_FAIL, -1 is returned. - * 0 is returned otherwise (as the virHosValidateLevel is then either a - * Warn or a Note). - */ - -#define VIR_HOST_VALIDATE_FAILURE(level) (level == VIR_HOST_VALIDATE_FAIL) ? -1 : 0 - -void virHostMsgSetQuiet(bool quietFlag); - -void virHostMsgCheck(const char *prefix, - const char *format, - ...) G_GNUC_PRINTF(2, 3); - -void virHostMsgPass(void); -void virHostMsgFail(virHostValidateLevel level, - const char *format, - ...) G_GNUC_PRINTF(2, 3); - int virHostValidateDeviceExists(const char *hvname, const char *dev_name, - virHostValidateLevel level, + virValidateLevel level, const char *hint); int virHostValidateDeviceAccessible(const char *hvname, const char *dev_name, - virHostValidateLevel level, + virValidateLevel level, const char *hint); virBitmap *virHostValidateGetCPUFlags(void); int virHostValidateLinuxKernel(const char *hvname, int version, - virHostValidateLevel level, + virValidateLevel level, const char *hint); int virHostValidateNamespace(const char *hvname, const char *ns_name, - virHostValidateLevel level, + virValidateLevel level, const char *hint); int virHostValidateCGroupControllers(const char *hvname, int controllers, - virHostValidateLevel level); + virValidateLevel level); int virHostValidateIOMMU(const char *hvname, - virHostValidateLevel level); + virValidateLevel level); int virHostValidateSecureGuests(const char *hvname, - virHostValidateLevel level); + virValidateLevel level); bool virHostKernelModuleIsLoaded(const char *module); diff --git a/tools/virt-host-validate-lxc.c b/tools/virt-host-validate-lxc.c index 8613f37cc7..5d6dbdf647 100644 --- a/tools/virt-host-validate-lxc.c +++ b/tools/virt-host-validate-lxc.c @@ -30,37 +30,37 @@ int virHostValidateLXC(void) int ret = 0; if (virHostValidateLinuxKernel("LXC", (2 << 16) | (6 << 8) | 26, - VIR_HOST_VALIDATE_FAIL, + VIR_VALIDATE_FAIL, _("Upgrade to a kernel supporting namespaces")) < 0) ret = -1; if (virHostValidateNamespace("LXC", "ipc", - VIR_HOST_VALIDATE_FAIL, + VIR_VALIDATE_FAIL, _("IPC namespace support is required")) < 0) ret = -1; if (virHostValidateNamespace("LXC", "mnt", - VIR_HOST_VALIDATE_FAIL, + VIR_VALIDATE_FAIL, _("Mount namespace support is required")) < 0) ret = -1; if (virHostValidateNamespace("LXC", "pid", - VIR_HOST_VALIDATE_FAIL, + VIR_VALIDATE_FAIL, _("PID namespace support is required")) < 0) ret = -1; if (virHostValidateNamespace("LXC", "uts", - VIR_HOST_VALIDATE_FAIL, + VIR_VALIDATE_FAIL, _("UTS namespace support is required")) < 0) ret = -1; if (virHostValidateNamespace("LXC", "net", - VIR_HOST_VALIDATE_WARN, + VIR_VALIDATE_WARN, _("Network namespace support is recommended")) < 0) ret = -1; if (virHostValidateNamespace("LXC", "user", - VIR_HOST_VALIDATE_WARN, + VIR_VALIDATE_WARN, _("User namespace support is recommended")) < 0) ret = -1; @@ -72,13 +72,13 @@ int virHostValidateLXC(void) (1 << VIR_CGROUP_CONTROLLER_DEVICES) | (1 << VIR_CGROUP_CONTROLLER_FREEZER) | (1 << VIR_CGROUP_CONTROLLER_BLKIO), - VIR_HOST_VALIDATE_FAIL) < 0) { + VIR_VALIDATE_FAIL) < 0) { ret = -1; } #if WITH_FUSE if (virHostValidateDeviceExists("LXC", "/sys/fs/fuse/connections", - VIR_HOST_VALIDATE_FAIL, + VIR_VALIDATE_FAIL, _("Load the 'fuse' module to enable /proc/ overrides")) < 0) ret = -1; #endif diff --git a/tools/virt-host-validate-qemu.c b/tools/virt-host-validate-qemu.c index b685fa01ba..833bb1b914 100644 --- a/tools/virt-host-validate-qemu.c +++ b/tools/virt-host-validate-qemu.c @@ -64,44 +64,44 @@ int virHostValidateQEMU(void) } if (hasVirtFlag) { - virHostMsgCheck("QEMU", "%s", _("Checking for hardware virtualization")); + virValidateCheck("QEMU", "%s", _("Checking for hardware virtualization")); if (hasHwVirt) { - virHostMsgPass(); + virValidatePass(); } else { - virHostMsgFail(VIR_HOST_VALIDATE_FAIL, - _("Host not compatible with KVM; HW virtualization CPU features not found. Only emulated CPUs are available; performance will be significantly limited")); + virValidateFail(VIR_VALIDATE_FAIL, + _("Host not compatible with KVM; HW virtualization CPU features not found. Only emulated CPUs are available; performance will be significantly limited")); ret = -1; } } if (hasHwVirt || !hasVirtFlag) { if (virHostValidateDeviceExists("QEMU", "/dev/kvm", - VIR_HOST_VALIDATE_FAIL, + VIR_VALIDATE_FAIL, kvmhint) <0) ret = -1; else if (virHostValidateDeviceAccessible("QEMU", "/dev/kvm", - VIR_HOST_VALIDATE_FAIL, + VIR_VALIDATE_FAIL, _("Check /dev/kvm is world writable or you are in a group that is allowed to access it")) < 0) ret = -1; } if (arch == VIR_ARCH_PPC64 || arch == VIR_ARCH_PPC64LE) { - virHostMsgCheck("QEMU", "%s", _("Checking for PowerPC KVM module loaded")); + virValidateCheck("QEMU", "%s", _("Checking for PowerPC KVM module loaded")); if (!virHostKernelModuleIsLoaded("kvm_hv")) - virHostMsgFail(VIR_HOST_VALIDATE_WARN, - _("Load kvm_hv for better performance")); + virValidateFail(VIR_VALIDATE_WARN, + _("Load kvm_hv for better performance")); else - virHostMsgPass(); + virValidatePass(); } if (virHostValidateDeviceExists("QEMU", "/dev/vhost-net", - VIR_HOST_VALIDATE_WARN, + VIR_VALIDATE_WARN, _("Load the 'vhost_net' module to improve performance of virtio networking")) < 0) ret = -1; if (virHostValidateDeviceExists("QEMU", "/dev/net/tun", - VIR_HOST_VALIDATE_FAIL, + VIR_VALIDATE_FAIL, _("Load the 'tun' module to enable networking for QEMU guests")) < 0) ret = -1; @@ -112,16 +112,16 @@ int virHostValidateQEMU(void) (1 << VIR_CGROUP_CONTROLLER_CPUSET) | (1 << VIR_CGROUP_CONTROLLER_DEVICES) | (1 << VIR_CGROUP_CONTROLLER_BLKIO), - VIR_HOST_VALIDATE_WARN) < 0) { + VIR_VALIDATE_WARN) < 0) { ret = -1; } if (virHostValidateIOMMU("QEMU", - VIR_HOST_VALIDATE_WARN) < 0) + VIR_VALIDATE_WARN) < 0) ret = -1; if (virHostValidateSecureGuests("QEMU", - VIR_HOST_VALIDATE_WARN) < 0) + VIR_VALIDATE_WARN) < 0) ret = -1; return ret; diff --git a/tools/virt-host-validate.c b/tools/virt-host-validate.c index d9fa52c310..426648a5d3 100644 --- a/tools/virt-host-validate.c +++ b/tools/virt-host-validate.c @@ -124,7 +124,7 @@ main(int argc, char **argv) if (argc > 1) hvname = argv[optind]; - virHostMsgSetQuiet(quiet); + virValidateSetQuiet(quiet); #if WITH_QEMU if (!hvname || STREQ(hvname, "qemu")) { diff --git a/tools/virt-validate-common.c b/tools/virt-validate-common.c new file mode 100644 index 0000000000..88c10e846f --- /dev/null +++ b/tools/virt-validate-common.c @@ -0,0 +1,110 @@ +/* + * virt-validate-common.c: Sanity check helper APIs + * + * Copyright (C) 2012-2024 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include <config.h> + +#include "virt-validate-common.h" + +static bool quiet; + +void virValidateSetQuiet(bool quietFlag) +{ + quiet = quietFlag; +} + +void virValidateCheck(const char *prefix, + const char *format, + ...) +{ + va_list args; + g_autofree char *msg = NULL; + + if (quiet) + return; + + va_start(args, format); + msg = g_strdup_vprintf(format, args); + va_end(args); + + fprintf(stdout, "%1$6s: %2$-69s: ", prefix, msg); +} + +static bool virValidateWantEscape(void) +{ + static bool detectTty = true; + static bool wantEscape; + if (detectTty) { + if (isatty(STDOUT_FILENO)) + wantEscape = true; + detectTty = false; + } + return wantEscape; +} + +void virValidatePass(void) +{ + if (quiet) + return; + + if (virValidateWantEscape()) + fprintf(stdout, "\033[32m%s\033[0m\n", _("PASS")); + else + fprintf(stdout, "%s\n", _("PASS")); +} + + +static const char * failMessages[] = { + N_("FAIL"), + N_("WARN"), + N_("NOTE"), +}; + +G_STATIC_ASSERT(G_N_ELEMENTS(failMessages) == VIR_VALIDATE_LAST); + +static const char *failEscapeCodes[] = { + "\033[31m", + "\033[33m", + "\033[34m", +}; + +G_STATIC_ASSERT(G_N_ELEMENTS(failEscapeCodes) == VIR_VALIDATE_LAST); + +void virValidateFail(virValidateLevel level, + const char *format, + ...) +{ + va_list args; + g_autofree char *msg = NULL; + + if (quiet) + return; + + va_start(args, format); + msg = g_strdup_vprintf(format, args); + va_end(args); + + if (virValidateWantEscape()) + fprintf(stdout, "%s%s\033[0m (%s)\n", + failEscapeCodes[level], _(failMessages[level]), msg); + else + fprintf(stdout, "%s (%s)\n", + _(failMessages[level]), msg); +} diff --git a/tools/virt-validate-common.h b/tools/virt-validate-common.h new file mode 100644 index 0000000000..7f7c373a66 --- /dev/null +++ b/tools/virt-validate-common.h @@ -0,0 +1,57 @@ +/* + * virt-validate-common.h: Sanity check helper APIs + * + * Copyright (C) 2012-2024 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#pragma once + +#include "internal.h" + +typedef enum { + VIR_VALIDATE_FAIL, + VIR_VALIDATE_WARN, + VIR_VALIDATE_NOTE, + + VIR_VALIDATE_LAST, +} virValidateLevel; + +/** + * VIR_VALIDATE_FAILURE + * @level: the virValidateLevel to be checked + * + * This macro is to be used in to return a failures based on the + * virValidateLevel use in the function. + * + * If the virValidateLevel is VIR_VALIDATE_FAIL, -1 is returned. + * 0 is returned otherwise (as the virValidateLevel is then either a + * Warn or a Note). + */ + +#define VIR_VALIDATE_FAILURE(level) (level == VIR_VALIDATE_FAIL) ? -1 : 0 + +void virValidateSetQuiet(bool quietFlag); + +void virValidateCheck(const char *prefix, + const char *format, + ...) G_GNUC_PRINTF(2, 3); + +void virValidatePass(void); +void virValidateFail(virValidateLevel level, + const char *format, + ...) G_GNUC_PRINTF(2, 3); -- 2.43.0

These tools never supported passing an argument to --version, this is a copy+paste mistake from virsh, which did support an argument. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- tools/virt-login-shell-helper.c | 2 +- tools/virt-pki-query-dn.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/virt-login-shell-helper.c b/tools/virt-login-shell-helper.c index 1627ba85e5..cb59b5dec0 100644 --- a/tools/virt-login-shell-helper.c +++ b/tools/virt-login-shell-helper.c @@ -180,7 +180,7 @@ main(int argc, char **argv) struct option opt[] = { { "help", no_argument, NULL, 'h' }, - { "version", optional_argument, NULL, 'V' }, + { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 }, }; if (virInitialize() < 0) { diff --git a/tools/virt-pki-query-dn.c b/tools/virt-pki-query-dn.c index c8196e32e3..5bf6717c4f 100644 --- a/tools/virt-pki-query-dn.c +++ b/tools/virt-pki-query-dn.c @@ -62,7 +62,7 @@ main(int argc, struct option opt[] = { { "help", no_argument, NULL, 'h' }, - { "version", optional_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0 }, }; -- 2.43.0

The /etc/sysconfig/libvirtd file is a Fedora/RHEL specific concept. Since those distros switched to systemd socket activation, the existance of --listen parameter in /etc/sysconfig/libvirtd is no longer a reliable check. This was further degraded with the switch to modular daemons where virtproxyd takes over the role. The /etc/sysconfig/iptables file is a Fedora/RHEL specific concept. Since those distros switched to firewalld, this file is no longer a reliable check. Rather than complicating these checks, just remove them, so that the virt-pki-validate tool focuses exclusively on TLS configuration validation. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- tools/virt-pki-validate.in | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/tools/virt-pki-validate.in b/tools/virt-pki-validate.in index c91f247ba5..c77daa9862 100644 --- a/tools/virt-pki-validate.in +++ b/tools/virt-pki-validate.in @@ -73,7 +73,6 @@ echo Found "$CERTOOL" # SYSCONFDIR="@sysconfdir@" PKI="$SYSCONFDIR/pki" -INITCONFDIR="@initconfdir@" if [ ! -d "$PKI" ] then echo the $PKI directory is missing, it is usually @@ -293,31 +292,4 @@ else SERVER=0 fi -if [ "$SERVER" = "1" ] -then - if [ -r "$INITCONFDIR"/libvirtd ] - then - if grep "^LIBVIRTD_ARGS.*--listen" "$INITCONFDIR"/libvirtd \ - >/dev/null 2>&1 - then - : - else - echo Make sure "$INITCONFDIR"/libvirtd is setup to listen to - echo TCP/IP connections and restart the libvirtd service - fi - fi - if [ -r "$INITCONFDIR"/iptables ] - then - if grep "$PORT" "$INITCONFDIR"/iptables >/dev/null 2>&1 - then - : - else - echo Make sure "$INITCONFDIR"/iptables is setup to allow - echo incoming TCP/IP connections on port $PORT and - echo restart the iptables service - fi - fi -fi - - exit 0 -- 2.43.0

The virt-pki-validate tool is currently a shell script. We have a general goal of eliminating use of shell in the project. By doing a new implementation in C, we can also make use of our more thorough sanity checking code to validate the certificate setup. This new implementation the same output format as the host validation tool for a more consistent user experiance. It also eliminates the requirement to have certtool installed on libvirt hosts, which has been an issue for Fedora flatpak packages since certtool isn't in the default platform runtime. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- docs/manpages/virt-pki-validate.rst | 4 +- libvirt.spec.in | 2 - po/POTFILES | 1 + tools/meson.build | 30 ++- tools/virt-pki-validate.c | 309 ++++++++++++++++++++++++++++ tools/virt-pki-validate.in | 295 -------------------------- 6 files changed, 337 insertions(+), 304 deletions(-) create mode 100644 tools/virt-pki-validate.c delete mode 100644 tools/virt-pki-validate.in diff --git a/docs/manpages/virt-pki-validate.rst b/docs/manpages/virt-pki-validate.rst index 53d5fbe269..cf17bad790 100644 --- a/docs/manpages/virt-pki-validate.rst +++ b/docs/manpages/virt-pki-validate.rst @@ -48,7 +48,7 @@ failure a non-zero status will be set. AUTHOR ====== -Daniel Veillard +Daniel Veillard, Daniel P. Berrangé BUGS @@ -70,7 +70,7 @@ Alternatively, you may report bugs to your software distributor / vendor. COPYRIGHT ========= -Copyright (C) 2006-2012 by Red Hat, Inc. +Copyright (C) 2006-2024 by Red Hat, Inc. LICENSE diff --git a/libvirt.spec.in b/libvirt.spec.in index 244e5e824c..2570c2458a 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -1032,8 +1032,6 @@ capabilities of VirtualBox %package client Summary: Client side utilities of the libvirt library Requires: libvirt-libs = %{version}-%{release} -# Needed by virt-pki-validate script. -Requires: gnutls-utils # Ensure smooth upgrades Obsoletes: libvirt-bash-completion < 7.3.0 diff --git a/po/POTFILES b/po/POTFILES index cb73d07904..4bfbb91164 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -391,6 +391,7 @@ tools/virt-host-validate-qemu.c tools/virt-host-validate.c tools/virt-login-shell-helper.c tools/virt-pki-query-dn.c +tools/virt-pki-validate.c tools/virt-validate-common.c tools/vsh-table.c tools/vsh.c diff --git a/tools/meson.build b/tools/meson.build index feb6b7c3ad..a58f5d4175 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -255,13 +255,33 @@ configure_file( install_mode: 'rwxr-xr-x', ) -configure_file( - input: 'virt-pki-validate.in', - output: '@BASENAME@', - configuration: tools_conf, +executable( + 'virt-pki-validate', + tlsconfig_sources + [ + 'virt-validate-common.c', + 'virt-pki-validate.c', + ], + dependencies: [ + glib_dep, + gnutls_dep, + ], + include_directories: [ + libvirt_inc, + src_inc_dir, + top_inc_dir, + util_inc_dir, + rpc_inc_dir, + ], + link_args: ( + libvirt_relro + + libvirt_no_indirect + + libvirt_no_undefined + ), + link_with: [ + libvirt_lib + ], install: true, install_dir: bindir, - install_mode: 'rwxr-xr-x', ) executable( diff --git a/tools/virt-pki-validate.c b/tools/virt-pki-validate.c new file mode 100644 index 0000000000..556664dd29 --- /dev/null +++ b/tools/virt-pki-validate.c @@ -0,0 +1,309 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <config.h> +#include "internal.h" + +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> + +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> + +#include "virgettext.h" +#include "virfile.h" +#include "virnettlsconfig.h" +#include "virnettlscert.h" +#include "virutil.h" +#include "virt-validate-common.h" + +static bool +virPKIValidateFile(const char *file, + uid_t owner, + gid_t group, + mode_t mode) +{ + struct stat sb; + if (stat(file, &sb) < 0) + return false; + + if (sb.st_uid != owner || + sb.st_gid != group) + return false; + + return (sb.st_mode & 0777) == mode; +} + +#define FILE_REQUIRE_EXISTS(scope, path, message, hint, ...) \ + do { \ + virValidateCheck(scope, "%s", message); \ + if (!virFileExists(path)) { \ + virValidateFail(VIR_VALIDATE_FAIL, hint, __VA_ARGS__); \ + ok = false; \ + goto done; \ + } else { \ + virValidatePass(); \ + } \ + } while (0) + +#define FILE_REQUIRE_ACCESS(scope, path, message, uid, gid, mode, hint, ...) \ + do { \ + virValidateCheck(scope, "%s", message); \ + if (!virPKIValidateFile(path, uid, gid, mode)) { \ + virValidateFail(VIR_VALIDATE_FAIL, hint, __VA_ARGS__); \ + ok = false; \ + } else { \ + virValidatePass(); \ + } \ + } while (0) + +static bool +virPKIValidateTrust(void) +{ + g_autofree char *cacert = NULL, *cacrl = NULL; + bool ok = true; + + virNetTLSConfigSystemTrust(&cacert, + &cacrl); + + FILE_REQUIRE_EXISTS("TRUST", + LIBVIRT_PKI_DIR, + _("Checking if system PKI dir exists"), + _("The system PKI dir %1$s is usually installed as part of the base filesystem or openssl packages"), + LIBVIRT_PKI_DIR); + + FILE_REQUIRE_ACCESS("TRUST", + LIBVIRT_PKI_DIR, + _("Checking system PKI dir access"), + 0, 0, 0755, + _("The system PKI dir %1$s must be accessible to all users. As root, run: chown root.root; chmod 0755 %2$s"), + LIBVIRT_PKI_DIR, LIBVIRT_PKI_DIR); + + + FILE_REQUIRE_EXISTS("TRUST", + LIBVIRT_CACERT_DIR, + _("Checking if system CA dir exists"), + _("The system CA dir %1$s is usually installed as part of the base filesystem or openssl packages"), + LIBVIRT_CACERT_DIR); + + FILE_REQUIRE_ACCESS("TRUST", + LIBVIRT_CACERT_DIR, + _("Checking system CA dir access"), + 0, 0, 0755, + _("The system CA dir %1$s must be accessible to all users. As root, run: chown root.root; chmod 0755 %2$s"), + LIBVIRT_CACERT_DIR, LIBVIRT_CACERT_DIR); + + FILE_REQUIRE_EXISTS("TRUST", + cacert, + _("Checking if CA cert exists"), + _("The machine cannot act as a client or server. See https://libvirt.org/kbase/tlscerts.html#setting-up-a-certificate-authority-c... on how to install %1$s"), + cacert); + + FILE_REQUIRE_ACCESS("TRUST", + cacert, + _("Checking CA cert access"), + 0, 0, 0644, + _("The CA certificate %1$s must be accessible to all users. As root run: chown root.root %2$s; chmod 0644 %3$s"), + cacert, cacert, cacert); + + done: + return ok; +} + +static bool +virPKIValidateIdentity(bool isServer) +{ + g_autofree char *cacert = NULL, *cacrl = NULL; + g_autofree char *cert = NULL, *key = NULL; + bool ok = true; + const char *scope = isServer ? "SERVER" : "CLIENT"; + + virNetTLSConfigSystemTrust(&cacert, + &cacrl); + virNetTLSConfigSystemIdentity(isServer, + &cert, + &key); + + FILE_REQUIRE_EXISTS(scope, + LIBVIRT_CERT_DIR, + _("Checking if system cert dir exists"), + _("The system cert dir %1$s is usually installed as part of the libvirt package"), + LIBVIRT_CERT_DIR); + + FILE_REQUIRE_ACCESS(scope, + LIBVIRT_CERT_DIR, + _("Checking system cert dir access"), + 0, 0, 0755, + _("The system cert dir %1$s must be accessible to all users. As root, run: chown root.root; chmod 0755 %2$s"), + LIBVIRT_PKI_DIR, LIBVIRT_PKI_DIR); + + FILE_REQUIRE_EXISTS(scope, + LIBVIRT_KEY_DIR, + _("Checking if system key dir exists"), + _("The system key dir %1$s is usually installed as part of the libvirt package"), + LIBVIRT_KEY_DIR); + + FILE_REQUIRE_ACCESS(scope, + LIBVIRT_KEY_DIR, + _("Checking system key dir access"), + 0, 0, 0755, + _("The system key dir %1$s must be accessible to all users. As root, run: chown root.root; chmod 0755 %2$s"), + LIBVIRT_KEY_DIR, LIBVIRT_PKI_DIR); + + FILE_REQUIRE_EXISTS(scope, + key, + _("Checking if key exists"), + isServer ? + _("The machine cannot act as a server. See https://libvirt.org/kbase/tlscerts.html#issuing-server-certificates on how to regenerate %1$s") : + _("The machine cannot act as a client. See https://libvirt.org/kbase/tlscerts.html#issuing-client-certificates on how to regenerate %1$s"), + key); + + FILE_REQUIRE_ACCESS(scope, + key, + _("Checking key access"), + 0, 0, isServer ? 0600 : 0644, + isServer ? + _("The server key %1$s must not be accessible to unprivileged users. As root run: chown root.root %2$s; chmod 0600 %3$s") : + _("The client key %1$s must be accessible to all users. As root run: chown root.root %2$s; chmod 0644 %3$s"), + key, key, key); + + FILE_REQUIRE_EXISTS(scope, + cert, + _("Checking if cert exists"), + isServer ? + _("The machine cannot act as a server. See https://libvirt.org/kbase/tlscerts.html#issuing-server-certificates on how to regenerate %1$s") : + _("The machine cannot act as a client. See https://libvirt.org/kbase/tlscerts.html#issuing-client-certificates on how to regenerate %1$s"), + cert); + + FILE_REQUIRE_ACCESS(scope, + cert, + _("Checking cert access"), + 0, 0, 0644, + isServer ? + _("The server cert %1$s must be accessible to all users. As root run: chown root.root %2$s; chmod 0644 %3$s") : + _("The client cert %1$s must be accessible to all users. As root run: chown root.root %2$s; chmod 0644 %3$s"), + cert, cert, cert); + + virValidateCheck(scope, "%s", _("Checking cert properties")); + + if (virNetTLSCertSanityCheck(isServer, + cacert, + cert) < 0) { + virValidateFail(VIR_VALIDATE_FAIL, "%s", + virGetLastErrorMessage()); + ok = false; + } else { + virValidatePass(); + } + + if (isServer) { + gnutls_x509_crt_t crt; + + virValidateCheck(scope, "%s", _("Checking cert hostname match")); + + if (!(crt = virNetTLSCertLoadFromFile(cert, true))) { + virValidateFail(VIR_VALIDATE_FAIL, + _("Unable to load %1$s: %2$s"), + cert, virGetLastErrorMessage()); + } else { + g_autofree char *hostname = virGetHostname(); + int ret = gnutls_x509_crt_check_hostname(crt, hostname); + gnutls_x509_crt_deinit(crt); + if (!ret) { + /* Only warning, since there can be valid reasons for mis-match */ + virValidateFail(VIR_VALIDATE_WARN, + _("Certificate %1$s owner does not match the hostname %2$s"), + cert, hostname); + ok = false; + } else { + virValidatePass(); + } + } + } + + done: + return ok; +} + + +static void +print_usage(const char *progname, + FILE *out) +{ + fprintf(out, + _("Usage:\n" + " %1$s { -v | -h } [TRUST|SERVER|CLIENT]\n" + "\n" + "Validate TLS certificate configuration\n" + "\n" + "options:\n" + " -h | --help display this help and exit\n" + " -v | --version output version information and exit\n"), + progname); +} + +int main(int argc, char **argv) +{ + const char *scope = NULL; + bool quiet = false; + int arg = 0; + bool ok = true; + const char *progname = argv[0]; + struct option opt[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 }, + }; + + if (virGettextInitialize() < 0) + return EXIT_FAILURE; + + while ((arg = getopt_long(argc, argv, "hvsup:", opt, NULL)) != -1) { + switch (arg) { + case 'v': + printf("%s\n", PACKAGE_VERSION); + return EXIT_SUCCESS; + + case 'h': + print_usage(progname, stdout); + return EXIT_SUCCESS; + + case 'q': + quiet = true; + break; + + case '?': + default: + print_usage(progname, stderr); + return EXIT_FAILURE; + } + } + + if ((argc - optind) > 2) { + fprintf(stderr, _("%1$s: too many command line arguments\n"), argv[0]); + print_usage(progname, stderr); + return EXIT_FAILURE; + } + + if (argc > 1) + scope = argv[optind]; + + virValidateSetQuiet(quiet); + + if ((!scope || g_str_equal(scope, "trust")) && + !virPKIValidateTrust()) + ok = false; + if ((!scope || g_str_equal(scope, "server")) && + !virPKIValidateIdentity(true)) + ok = false; + if ((!scope || g_str_equal(scope, "client")) && + !virPKIValidateIdentity(false)) + ok = false; + + if (!ok) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} diff --git a/tools/virt-pki-validate.in b/tools/virt-pki-validate.in deleted file mode 100644 index c77daa9862..0000000000 --- a/tools/virt-pki-validate.in +++ /dev/null @@ -1,295 +0,0 @@ -#!/bin/sh -# -# This shell script checks the TLS certificates and options needed -# for the secure client/server support of libvirt as documented at -# https://libvirt.org/kbase/tlscerts.html -# -# Copyright (C) 2009-2013 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library. If not, see -# <http://www.gnu.org/licenses/>. -# -# Daniel Veillard <veillard@redhat.com> -# - -case $1 in - -h | --h | --he | --hel | --help) - cat <<EOF -Usage: - $0 [OPTION] - -Options: - -h | --help Display program help - -V | --version Display program version -EOF - exit ;; - -V | --v | --ve | --ver | --vers | --versi | --versio | --version) - cat <<EOF -$0 (libvirt) @VERSION@ -EOF - exit ;; - --) shift ;; - -) # Not an option but an argument; it gets rejected later - ;; - -*) - echo "$0: unrecognized option '$1'" >&2 - exit 1 ;; -esac - -if test $# != 0; then - echo "$0: unrecognized argument '$1'" >&2 - exit 1 -fi - -USER=`who am i | awk '{ print $1 }'` -SERVER=1 -CLIENT=1 -PORT=16514 -# -# First get certtool -# -CERTOOL=`which certtool 2>/dev/null` -if [ ! -x "$CERTOOL" ] -then - echo "Could not locate the certtool program" - echo "make sure the gnutls-utils (or gnutls-bin) package is installed" - exit 1 -fi -echo Found "$CERTOOL" - -# -# Check the directory structure -# -SYSCONFDIR="@sysconfdir@" -PKI="$SYSCONFDIR/pki" -if [ ! -d "$PKI" ] -then - echo the $PKI directory is missing, it is usually - echo installed as part of the filesystem or openssl packages - exit 1 -fi - -if [ ! -r "$PKI" ] -then - echo the $PKI directory is not readable by $USER - echo "as root do: chmod a+rx $PKI" - exit 1 -fi -if [ ! -x "$PKI" ] -then - echo the $PKI directory is not listable by $USER - echo "as root do: chmod a+rx $PKI" - exit 1 -fi - -CA="$PKI/CA" -if [ ! -d "$CA" ] -then - echo the $CA directory is missing, it is usually - echo installed as part of the or openssl package - exit 1 -fi - -if [ ! -r "$CA" ] -then - echo the $CA directory is not readable by $USER - echo "as root do: chmod a+rx $CA" - exit 1 -fi -if [ ! -x "$CA" ] -then - echo the $CA directory is not listable by $USER - echo "as root do: chmod a+rx $CA" - exit 1 -fi - -LIBVIRT="$PKI/libvirt" -if [ ! -d "$LIBVIRT" ] -then - echo the $LIBVIRT directory is missing, it is usually - echo installed by the libvirt package - echo "as root do: mkdir -m 755 $LIBVIRT ; chown root:root $LIBVIRT" - exit 1 -fi - -if [ ! -r "$LIBVIRT" ] -then - echo the $LIBVIRT directory is not readable by $USER - echo "as root do: chown root:root $LIBVIRT ; chmod 755 $LIBVIRT" - exit 1 -fi -if [ ! -x "$LIBVIRT" ] -then - echo the $LIBVIRT directory is not listable by $USER - echo "as root do: chown root:root $LIBVIRT ; chmod 755 $LIBVIRT" - exit 1 -fi - -LIBVIRTP="$LIBVIRT/private" -if [ ! -d "$LIBVIRTP" ] -then - echo the $LIBVIRTP directory is missing, it is usually - echo installed by the libvirt package - echo "as root do: mkdir -m 755 $LIBVIRTP ; chown root:root $LIBVIRTP" - exit 1 -fi - -if [ ! -r "$LIBVIRTP" ] -then - echo the $LIBVIRTP directory is not readable by $USER - echo "as root do: chown root:root $LIBVIRTP ; chmod 755 $LIBVIRTP" - exit 1 -fi -if [ ! -x "$LIBVIRTP" ] -then - echo the $LIBVIRTP directory is not listable by $USER - echo "as root do: chown root:root $LIBVIRTP ; chmod 755 $LIBVIRTP" - exit 1 -fi - -# -# Now check the certificates -# First the CA certificate -# -if [ ! -f "$CA/cacert.pem" ] -then - echo the CA certificate $CA/cacert.pem is missing while it - echo should be installed on both client and servers - echo "see https://libvirt.org/kbase/tlscerts.html#setting-up-a-certificate-authority-c..." - echo on how to install it - exit 1 -fi -if [ ! -r "$CA/cacert.pem" ] -then - echo the CA certificate $CA/cacert.pem is not readable by $USER - echo "as root do: chmod 644 $CA/cacert.pem" - exit 1 -fi -sed_get_org='/Issuer:/ { - s/.*Issuer:.*CN=// - s/,.*// - p -}' -ORG=`"$CERTOOL" -i --infile "$CA/cacert.pem" | sed -n "$sed_get_org"` -if [ "$ORG" = "" ] -then - echo the CA certificate $CA/cacert.pem does not define the organization - echo it should probably regenerated - echo "see https://libvirt.org/kbase/tlscerts.html#setting-up-a-certificate-authority-c..." - echo on how to regenerate it - exit 1 -fi -echo Found CA certificate $CA/cacert.pem for $ORG - -# Second the client certificates - -if [ -f "$LIBVIRT/clientcert.pem" ] -then - if [ ! -r "$LIBVIRT/clientcert.pem" ] - then - echo Client certificate $LIBVIRT/clientcert.pem should be world readable - echo "as root do: chown root:root $LIBVIRT/clientcert.pem ; chmod 644 $LIBVIRT/clientcert.pem" - else - C_ORG=`"$CERTOOL" -i --infile "$LIBVIRT/clientcert.pem" | grep Subject: | sed 's+.*O=\([^,]*\).*+\1+'` - if [ "$ORG" != "$C_ORG" ] - then - echo The CA certificate and the client certificate do not match - echo CA organization: $ORG - echo Client organization: $C_ORG - fi - CLIENT=`"$CERTOOL" -i --infile "$LIBVIRT/clientcert.pem" | grep Subject: | sed 's+.*CN=\(.[^,]*\).*+\1+'` - echo Found client certificate $LIBVIRT/clientcert.pem for $CLIENT - if [ ! -e "$LIBVIRTP/clientkey.pem" ] - then - echo Missing client private key $LIBVIRTP/clientkey.pem - else - echo Found client private key $LIBVIRTP/clientkey.pem - OWN=`ls -l "$LIBVIRTP/clientkey.pem" | awk '{ print $3 }'` - # The substr($1, 1, 10) gets rid of acl and xattr markers - MOD=`ls -l "$LIBVIRTP/clientkey.pem" | awk '{ print substr($1, 1, 10) }'` - if [ "$OWN" != "root" ] - then - echo The client private key should be owned by root - echo "as root do: chown root $LIBVIRTP/clientkey.pem" - fi - if [ "$MOD" != "-rw-r--r--" ] - then - echo The client private key need to be read by client tools - echo "as root do: chmod 644 $LIBVIRTP/clientkey.pem" - fi - fi - - fi -else - echo Did not find "$LIBVIRT/clientcert.pem" client certificate - echo The machine cannot act as a client - echo "see https://libvirt.org/kbase/tlscerts.html#issuing-client-certificates" - echo on how to regenerate it - CLIENT=0 -fi - -# Third the server certificates - -if [ -f "$LIBVIRT/servercert.pem" ] -then - if [ ! -r "$LIBVIRT/servercert.pem" ] - then - echo Server certificate $LIBVIRT/servercert.pem should be world readable - echo "as root do: chown root:root $LIBVIRT/servercert.pem ; chmod 644 $LIBVIRT/servercert.pem" - else - S_ORG=`"$CERTOOL" -i --infile "$LIBVIRT/servercert.pem" | grep Subject: | sed 's+.*O=\([^,]*\).*+\1+'` - if [ "$ORG" != "$S_ORG" ] - then - echo The CA certificate and the server certificate do not match - echo CA organization: $ORG - echo Server organization: $S_ORG - fi - S_HOST=`"$CERTOOL" -i --infile "$LIBVIRT/servercert.pem" | grep Subject: | sed 's+.*CN=\([^,]*\).*+\1+'` - if test "$S_HOST" != "`hostname -s`" && test "$S_HOST" != "`hostname`" - then - echo The server certificate does not seem to match the host name - echo hostname: '"'`hostname`'"' - echo Server certificate CN: '"'$S_HOST'"' - fi - echo Found server certificate $LIBVIRT/servercert.pem for $S_HOST - if [ ! -e "$LIBVIRTP/serverkey.pem" ] - then - echo Missing server private key $LIBVIRTP/serverkey.pem - else - echo Found server private key $LIBVIRTP/serverkey.pem - OWN=`ls -l "$LIBVIRTP/serverkey.pem" | awk '{ print $3 }'` - # The substr($1, 1, 10) gets rid of acl and xattr markers - MOD=`ls -l "$LIBVIRTP/serverkey.pem" | awk '{ print substr($1, 1, 10) }'` - if [ "$OWN" != "root" ] - then - echo The server private key should be owned by root - echo "as root do: chown root $LIBVIRTP/serverkey.pem" - fi - if [ "$MOD" != "-rw-------" ] - then - echo The server private key need to be read only by root - echo "as root do: chmod 600 $LIBVIRTP/serverkey.pem" - fi - fi - - fi -else - echo Did not find $LIBVIRT/servercert.pem server certificate - echo The machine cannot act as a server - echo "see https://libvirt.org/kbase/tlscerts.html#issuing-server-certificates" - echo on how to regenerate it - SERVER=0 -fi - -exit 0 -- 2.43.0

The virt-pki-validate command can validate the system certificate directories. The remote driver, however, also supports a standard per-user certs location, as well as a runtime custom path. This extends the validation tool to be able to cope with these alternate locations too. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- docs/manpages/virt-pki-validate.rst | 5 +- tools/virt-pki-validate.c | 289 +++++++++++++++++++--------- 2 files changed, 206 insertions(+), 88 deletions(-) diff --git a/docs/manpages/virt-pki-validate.rst b/docs/manpages/virt-pki-validate.rst index cf17bad790..932c677cfc 100644 --- a/docs/manpages/virt-pki-validate.rst +++ b/docs/manpages/virt-pki-validate.rst @@ -15,7 +15,7 @@ SYNOPSIS ======== -``virt-pki-validate`` [*OPTION*] +``virt-pki-validate`` [*OPTION*] [trust|server|client] DESCRIPTION @@ -26,6 +26,9 @@ a secure libvirt server or client using the TLS encryption protocol. It will report any missing certificate or key files on the host. It should be run as root to ensure it can read all the necessary files +With no arguments it will check the trusted CA config, the server +config and the client config. The optional positional argument can +be used to restrict the checks to just one of these three sets. OPTIONS ======= diff --git a/tools/virt-pki-validate.c b/tools/virt-pki-validate.c index 556664dd29..656f29fdc5 100644 --- a/tools/virt-pki-validate.c +++ b/tools/virt-pki-validate.c @@ -60,40 +60,77 @@ virPKIValidateFile(const char *file, } while (0) static bool -virPKIValidateTrust(void) +virPKIValidateTrust(bool system, const char *path) { g_autofree char *cacert = NULL, *cacrl = NULL; bool ok = true; - virNetTLSConfigSystemTrust(&cacert, - &cacrl); - - FILE_REQUIRE_EXISTS("TRUST", - LIBVIRT_PKI_DIR, - _("Checking if system PKI dir exists"), - _("The system PKI dir %1$s is usually installed as part of the base filesystem or openssl packages"), - LIBVIRT_PKI_DIR); - - FILE_REQUIRE_ACCESS("TRUST", - LIBVIRT_PKI_DIR, - _("Checking system PKI dir access"), - 0, 0, 0755, - _("The system PKI dir %1$s must be accessible to all users. As root, run: chown root.root; chmod 0755 %2$s"), - LIBVIRT_PKI_DIR, LIBVIRT_PKI_DIR); - - - FILE_REQUIRE_EXISTS("TRUST", - LIBVIRT_CACERT_DIR, - _("Checking if system CA dir exists"), - _("The system CA dir %1$s is usually installed as part of the base filesystem or openssl packages"), - LIBVIRT_CACERT_DIR); - - FILE_REQUIRE_ACCESS("TRUST", - LIBVIRT_CACERT_DIR, - _("Checking system CA dir access"), - 0, 0, 0755, - _("The system CA dir %1$s must be accessible to all users. As root, run: chown root.root; chmod 0755 %2$s"), - LIBVIRT_CACERT_DIR, LIBVIRT_CACERT_DIR); + if (system) { + virNetTLSConfigSystemTrust(&cacert, + &cacrl); + + FILE_REQUIRE_EXISTS("TRUST", + LIBVIRT_PKI_DIR, + _("Checking if system PKI dir exists"), + _("The system PKI dir %1$s is usually installed as part of the base filesystem or openssl packages"), + LIBVIRT_PKI_DIR); + + FILE_REQUIRE_ACCESS("TRUST", + LIBVIRT_PKI_DIR, + _("Checking system PKI dir access"), + 0, 0, 0755, + _("The system PKI dir %1$s must be accessible to all users. As root, run: chown root.root; chmod 0755 %2$s"), + LIBVIRT_PKI_DIR, LIBVIRT_PKI_DIR); + + + FILE_REQUIRE_EXISTS("TRUST", + LIBVIRT_CACERT_DIR, + _("Checking if system CA dir exists"), + _("The system CA dir %1$s is usually installed as part of the base filesystem or openssl packages"), + LIBVIRT_CACERT_DIR); + + FILE_REQUIRE_ACCESS("TRUST", + LIBVIRT_CACERT_DIR, + _("Checking system CA dir access"), + 0, 0, 0755, + _("The system CA dir %1$s must be accessible to all users. As root, run: chown root.root; chmod 0755 %2$s"), + LIBVIRT_CACERT_DIR, LIBVIRT_CACERT_DIR); + } else if (path) { + virNetTLSConfigCustomTrust(path, + &cacert, + &cacrl); + + FILE_REQUIRE_EXISTS("TRUST", + path, + _("Checking if custom PKI base dir exists"), + _("Create the dir %1$s"), + path); + + FILE_REQUIRE_ACCESS("TRUST", + path, + _("Checking custom PKI base dir access"), + getuid(), getgid(), 0700, + _("The PKI base dir %1$s must not be accessible to other users. Run: chown %2$d.%3$d %4$s; chmod 0700 %5$s"), + path, getuid(), getgid(), path, path); + } else { + g_autofree char *pkipath = virNetTLSConfigUserPKIBaseDir(); + + virNetTLSConfigUserTrust(&cacert, + &cacrl); + + FILE_REQUIRE_EXISTS("TRUST", + pkipath, + _("Checking if user PKI base dir exists"), + _("Create the dir %1$s"), + pkipath); + + FILE_REQUIRE_ACCESS("TRUST", + pkipath, + _("Checking user PKI base dir access"), + getuid(), getgid(), 0700, + _("The PKI base dir %1$s must not be accessible to other users. Run: chown %2$d.%3$d %4$s; chmod 0700 %5$s"), + pkipath, getuid(), getgid(), pkipath, pkipath); + } FILE_REQUIRE_EXISTS("TRUST", cacert, @@ -101,56 +138,81 @@ virPKIValidateTrust(void) _("The machine cannot act as a client or server. See https://libvirt.org/kbase/tlscerts.html#setting-up-a-certificate-authority-c... on how to install %1$s"), cacert); - FILE_REQUIRE_ACCESS("TRUST", - cacert, - _("Checking CA cert access"), - 0, 0, 0644, - _("The CA certificate %1$s must be accessible to all users. As root run: chown root.root %2$s; chmod 0644 %3$s"), - cacert, cacert, cacert); + if (system) { + FILE_REQUIRE_ACCESS("TRUST", + cacert, + _("Checking CA cert access"), + 0, 0, 0644, + _("The CA certificate %1$s must be accessible to all users. As root run: chown root.root %2$s; chmod 0644 %3$s"), + cacert, cacert, cacert); + } else { + FILE_REQUIRE_ACCESS("TRUST", + cacert, + _("Checking CA cert access"), + getuid(), getgid(), 0600, + _("The CA certificate %1$s must not be accessible to other users. As this user, run: chown %2$d.%3$d %4$s; chmod 0600 %5$s"), + cacert, getuid(), getgid(), cacert, cacert); + } done: return ok; } static bool -virPKIValidateIdentity(bool isServer) +virPKIValidateIdentity(bool isServer, bool system, const char *path) { g_autofree char *cacert = NULL, *cacrl = NULL; g_autofree char *cert = NULL, *key = NULL; bool ok = true; const char *scope = isServer ? "SERVER" : "CLIENT"; - virNetTLSConfigSystemTrust(&cacert, - &cacrl); - virNetTLSConfigSystemIdentity(isServer, - &cert, - &key); - - FILE_REQUIRE_EXISTS(scope, - LIBVIRT_CERT_DIR, - _("Checking if system cert dir exists"), - _("The system cert dir %1$s is usually installed as part of the libvirt package"), - LIBVIRT_CERT_DIR); - - FILE_REQUIRE_ACCESS(scope, - LIBVIRT_CERT_DIR, - _("Checking system cert dir access"), - 0, 0, 0755, - _("The system cert dir %1$s must be accessible to all users. As root, run: chown root.root; chmod 0755 %2$s"), - LIBVIRT_PKI_DIR, LIBVIRT_PKI_DIR); - - FILE_REQUIRE_EXISTS(scope, - LIBVIRT_KEY_DIR, - _("Checking if system key dir exists"), - _("The system key dir %1$s is usually installed as part of the libvirt package"), - LIBVIRT_KEY_DIR); - - FILE_REQUIRE_ACCESS(scope, - LIBVIRT_KEY_DIR, - _("Checking system key dir access"), - 0, 0, 0755, - _("The system key dir %1$s must be accessible to all users. As root, run: chown root.root; chmod 0755 %2$s"), - LIBVIRT_KEY_DIR, LIBVIRT_PKI_DIR); + if (system) { + virNetTLSConfigSystemTrust(&cacert, + &cacrl); + virNetTLSConfigSystemIdentity(isServer, + &cert, + &key); + + FILE_REQUIRE_EXISTS(scope, + LIBVIRT_CERT_DIR, + _("Checking if system cert dir exists"), + _("The system cert dir %1$s is usually installed as part of the libvirt package"), + LIBVIRT_CERT_DIR); + + FILE_REQUIRE_ACCESS(scope, + LIBVIRT_CERT_DIR, + _("Checking system cert dir access"), + 0, 0, 0755, + _("The system cert dir %1$s must be accessible to all users. As root, run: chown root.root; chmod 0755 %2$s"), + LIBVIRT_PKI_DIR, LIBVIRT_PKI_DIR); + + FILE_REQUIRE_EXISTS(scope, + LIBVIRT_KEY_DIR, + _("Checking if system key dir exists"), + _("The system key dir %1$s is usually installed as part of the libvirt package"), + LIBVIRT_KEY_DIR); + + FILE_REQUIRE_ACCESS(scope, + LIBVIRT_KEY_DIR, + _("Checking system key dir access"), + 0, 0, 0755, + _("The system key dir %1$s must be accessible to all users. As root, run: chown root.root; chmod 0755 %2$s"), + LIBVIRT_KEY_DIR, LIBVIRT_PKI_DIR); + } else if (path) { + virNetTLSConfigCustomTrust(path, + &cacert, + &cacrl); + virNetTLSConfigCustomIdentity(path, + isServer, + &cert, + &key); + } else { + virNetTLSConfigUserTrust(&cacert, + &cacrl); + virNetTLSConfigUserIdentity(isServer, + &cert, + &key); + } FILE_REQUIRE_EXISTS(scope, key, @@ -160,14 +222,25 @@ virPKIValidateIdentity(bool isServer) _("The machine cannot act as a client. See https://libvirt.org/kbase/tlscerts.html#issuing-client-certificates on how to regenerate %1$s"), key); - FILE_REQUIRE_ACCESS(scope, - key, - _("Checking key access"), - 0, 0, isServer ? 0600 : 0644, - isServer ? - _("The server key %1$s must not be accessible to unprivileged users. As root run: chown root.root %2$s; chmod 0600 %3$s") : - _("The client key %1$s must be accessible to all users. As root run: chown root.root %2$s; chmod 0644 %3$s"), - key, key, key); + if (system) { + FILE_REQUIRE_ACCESS(scope, + key, + _("Checking key access"), + 0, 0, isServer ? 0600 : 0644, + isServer ? + _("The server key %1$s must not be accessible to unprivileged users. As root run: chown root.root %2$s; chmod 0600 %3$s") : + _("The client key %1$s must be accessible to all users. As root run: chown root.root %2$s; chmod 0644 %3$s"), + key, key, key); + } else { + FILE_REQUIRE_ACCESS(scope, + key, + _("Checking key access"), + getuid(), getgid(), 0600, + isServer ? + _("The server key %1$s must be not be accessible to other users. As this user, run: chown %2$d.%3$d %4$s; chmod 0600 %5$s") : + _("The client key %1$s must be not be accessible to other users. As this user, run: chown %2$d.%3$d %4$s; chmod 0600 %5$s"), + key, getuid(), getgid(), key, key); + } FILE_REQUIRE_EXISTS(scope, cert, @@ -177,14 +250,25 @@ virPKIValidateIdentity(bool isServer) _("The machine cannot act as a client. See https://libvirt.org/kbase/tlscerts.html#issuing-client-certificates on how to regenerate %1$s"), cert); - FILE_REQUIRE_ACCESS(scope, - cert, - _("Checking cert access"), - 0, 0, 0644, - isServer ? - _("The server cert %1$s must be accessible to all users. As root run: chown root.root %2$s; chmod 0644 %3$s") : - _("The client cert %1$s must be accessible to all users. As root run: chown root.root %2$s; chmod 0644 %3$s"), - cert, cert, cert); + if (system) { + FILE_REQUIRE_ACCESS(scope, + cert, + _("Checking cert access"), + 0, 0, 0644, + isServer ? + _("The server cert %1$s must be accessible to all users. As root run: chown root.root %2$s; chmod 0644 %3$s") : + _("The client cert %1$s must be accessible to all users. As root run: chown root.root %2$s; chmod 0644 %3$s"), + cert, cert, cert); + } else { + FILE_REQUIRE_ACCESS(scope, + cert, + _("Checking cert access"), + getuid(), getgid(), 0600, + isServer ? + _("The server cert %1$s must be restricted to this user. As this user, run: chown %2$d.%3$d %4$s; chmod 0600 %5$s") : + _("The client cert %1$s must be restricted to this user. As this user, run: chown %2$d.%3$d %4$s; chmod 0600 %5$s"), + cert, getuid(), getgid(), cert, cert); + } virValidateCheck(scope, "%s", _("Checking cert properties")); @@ -239,6 +323,9 @@ print_usage(const char *progname, "Validate TLS certificate configuration\n" "\n" "options:\n" + " -s | --system validate system certificates (default)\n" + " -u | --user validate user certificates\n" + " -p DIR | --path DIR validate custom certificate path\n" " -h | --help display this help and exit\n" " -v | --version output version information and exit\n"), progname); @@ -247,6 +334,9 @@ print_usage(const char *progname, int main(int argc, char **argv) { const char *scope = NULL; + bool system = false; + bool user = false; + const char *path = NULL; bool quiet = false; int arg = 0; bool ok = true; @@ -254,6 +344,9 @@ int main(int argc, char **argv) struct option opt[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, + { "system", no_argument, NULL, 's' }, + { "user", no_argument, NULL, 'u' }, + { "path", required_argument, NULL, 'p' }, { NULL, 0, NULL, 0 }, }; @@ -262,6 +355,18 @@ int main(int argc, char **argv) while ((arg = getopt_long(argc, argv, "hvsup:", opt, NULL)) != -1) { switch (arg) { + case 's': + system = true; + break; + + case 'u': + user = true; + break; + + case 'p': + path = optarg; + break; + case 'v': printf("%s\n", PACKAGE_VERSION); return EXIT_SUCCESS; @@ -292,14 +397,24 @@ int main(int argc, char **argv) virValidateSetQuiet(quiet); + if ((system && user) || + (system && path) || + (user && path)) { + g_printerr("--system, --user & --path are mutually exclusive\n"); + return EXIT_FAILURE; + } + + if (!system && !user && !path) + system = true; + if ((!scope || g_str_equal(scope, "trust")) && - !virPKIValidateTrust()) + !virPKIValidateTrust(system, path)) ok = false; if ((!scope || g_str_equal(scope, "server")) && - !virPKIValidateIdentity(true)) + !virPKIValidateIdentity(true, system, path)) ok = false; if ((!scope || g_str_equal(scope, "client")) && - !virPKIValidateIdentity(false)) + !virPKIValidateIdentity(false, system, path)) ok = false; if (!ok) -- 2.43.0

On 6/7/24 16:26, Daniel P. Berrangé wrote:
This was driven by the complaint that libvirt pulls in gnutls-utils
https://src.fedoraproject.org/rpms/virt-viewer/pull-request/4
but also it lets us remove more usage of Shell code from libvirt, as well as improving the consistency of certificate checks vs the runtime checks we do.
Daniel P. Berrangé (9): rpc: split out helpers for TLS cert path location rpc: refactor method for checking session certificates rpc: split TLS cert validation into separate file docs: fix author credit for virt-pki-validate tool tools: split off common helpers for host validate tool tools: drop unused --version argument tools: stop checking init scripts & iptables config tools: reimplement virt-pki-validate in C tools: support validating user/custom PKI certs
docs/manpages/virt-pki-validate.rst | 9 +- libvirt.spec.in | 2 - po/POTFILES | 3 + src/rpc/meson.build | 7 +- src/rpc/virnettlscert.c | 553 ++++++++++++++++++++++++++ src/rpc/virnettlscert.h | 42 ++ src/rpc/virnettlsconfig.c | 202 ++++++++++ src/rpc/virnettlsconfig.h | 68 ++++ src/rpc/virnettlscontext.c | 586 +--------------------------- tools/meson.build | 31 +- tools/virt-host-validate-ch.c | 12 +- tools/virt-host-validate-common.c | 308 ++++++--------- tools/virt-host-validate-common.h | 48 +-- tools/virt-host-validate-lxc.c | 18 +- tools/virt-host-validate-qemu.c | 30 +- tools/virt-host-validate.c | 2 +- tools/virt-login-shell-helper.c | 2 +- tools/virt-pki-query-dn.c | 2 +- tools/virt-pki-validate.c | 424 ++++++++++++++++++++ tools/virt-pki-validate.in | 323 --------------- tools/virt-validate-common.c | 110 ++++++ tools/virt-validate-common.h | 57 +++ 22 files changed, 1670 insertions(+), 1169 deletions(-) create mode 100644 src/rpc/virnettlscert.c create mode 100644 src/rpc/virnettlscert.h create mode 100644 src/rpc/virnettlsconfig.c create mode 100644 src/rpc/virnettlsconfig.h create mode 100644 tools/virt-pki-validate.c delete mode 100644 tools/virt-pki-validate.in create mode 100644 tools/virt-validate-common.c create mode 100644 tools/virt-validate-common.h
Reviewed-by: Michal Privoznik <mprivozn@redhat.com> Michal

On Mon, Jun 10, 2024 at 01:57:17PM +0200, Michal Prívozník wrote:
On 6/7/24 16:26, Daniel P. Berrangé wrote:
This was driven by the complaint that libvirt pulls in gnutls-utils
https://src.fedoraproject.org/rpms/virt-viewer/pull-request/4
but also it lets us remove more usage of Shell code from libvirt, as well as improving the consistency of certificate checks vs the runtime checks we do.
Daniel P. Berrangé (9): rpc: split out helpers for TLS cert path location rpc: refactor method for checking session certificates rpc: split TLS cert validation into separate file docs: fix author credit for virt-pki-validate tool tools: split off common helpers for host validate tool tools: drop unused --version argument tools: stop checking init scripts & iptables config tools: reimplement virt-pki-validate in C tools: support validating user/custom PKI certs
docs/manpages/virt-pki-validate.rst | 9 +- libvirt.spec.in | 2 - po/POTFILES | 3 + src/rpc/meson.build | 7 +- src/rpc/virnettlscert.c | 553 ++++++++++++++++++++++++++ src/rpc/virnettlscert.h | 42 ++ src/rpc/virnettlsconfig.c | 202 ++++++++++ src/rpc/virnettlsconfig.h | 68 ++++ src/rpc/virnettlscontext.c | 586 +--------------------------- tools/meson.build | 31 +- tools/virt-host-validate-ch.c | 12 +- tools/virt-host-validate-common.c | 308 ++++++--------- tools/virt-host-validate-common.h | 48 +-- tools/virt-host-validate-lxc.c | 18 +- tools/virt-host-validate-qemu.c | 30 +- tools/virt-host-validate.c | 2 +- tools/virt-login-shell-helper.c | 2 +- tools/virt-pki-query-dn.c | 2 +- tools/virt-pki-validate.c | 424 ++++++++++++++++++++ tools/virt-pki-validate.in | 323 --------------- tools/virt-validate-common.c | 110 ++++++ tools/virt-validate-common.h | 57 +++ 22 files changed, 1670 insertions(+), 1169 deletions(-) create mode 100644 src/rpc/virnettlscert.c create mode 100644 src/rpc/virnettlscert.h create mode 100644 src/rpc/virnettlsconfig.c create mode 100644 src/rpc/virnettlsconfig.h create mode 100644 tools/virt-pki-validate.c delete mode 100644 tools/virt-pki-validate.in create mode 100644 tools/virt-validate-common.c create mode 100644 tools/virt-validate-common.h
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Shoving this through CI highlighted that I forgot to test non-Linux portability. Here are the resulting fixes, including your feedback, that I'll be including before pushing, to get a clean build in CI: diff --git a/libvirt.spec.in b/libvirt.spec.in index 2570c2458a..9bff6ef6db 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -2511,7 +2511,7 @@ exit 0 %{mingw32_bindir}/virt-admin.exe %{mingw32_bindir}/virt-xml-validate %{mingw32_bindir}/virt-pki-query-dn.exe -%{mingw32_bindir}/virt-pki-validate +%{mingw32_bindir}/virt-pki-validate.exe %{mingw32_bindir}/libvirt-lxc-0.dll %{mingw32_bindir}/libvirt-qemu-0.dll %{mingw32_bindir}/libvirt-admin-0.dll @@ -2570,7 +2570,7 @@ exit 0 %{mingw64_bindir}/virt-admin.exe %{mingw64_bindir}/virt-xml-validate %{mingw64_bindir}/virt-pki-query-dn.exe -%{mingw64_bindir}/virt-pki-validate +%{mingw64_bindir}/virt-pki-validate.exe %{mingw64_bindir}/libvirt-lxc-0.dll %{mingw64_bindir}/libvirt-qemu-0.dll %{mingw64_bindir}/libvirt-admin-0.dll diff --git a/src/rpc/meson.build b/src/rpc/meson.build index 8bdbf5c88f..68aaf24b2a 100644 --- a/src/rpc/meson.build +++ b/src/rpc/meson.build @@ -1,9 +1,9 @@ gendispatch_prog = find_program('gendispatch.pl') -tlsconfig_sources = [ - files('virnettlsconfig.c'), - files('virnettlscert.c'), -] +tlsconfig_sources = files( + 'virnettlsconfig.c', + 'virnettlscert.c', +) socket_sources = tlsconfig_sources + [ 'virnettlscontext.c', diff --git a/src/rpc/virnettlscert.c b/src/rpc/virnettlscert.c index 2e1e4c56d5..1befbe06bc 100644 --- a/src/rpc/virnettlscert.c +++ b/src/rpc/virnettlscert.c @@ -20,6 +20,8 @@ #include <config.h> +#include <unistd.h> + #include "virnettlscert.h" #include "viralloc.h" diff --git a/tools/virt-host-validate-bhyve.c b/tools/virt-host-validate-bhyve.c index adb5ae1717..d7a409db9d 100644 --- a/tools/virt-host-validate-bhyve.c +++ b/tools/virt-host-validate-bhyve.c @@ -28,21 +28,21 @@ #include "virt-host-validate-common.h" #define MODULE_STATUS(mod, err_msg, err_code) \ - virHostMsgCheck("BHYVE", _("Checking for %1$s module"), #mod); \ + virValidateCheck("BHYVE", _("Checking for %1$s module"), #mod); \ if (mod ## _loaded) { \ - virHostMsgPass(); \ + virValidatePass(); \ } else { \ - virHostMsgFail(err_code, \ - _("%1$s module is not loaded, " err_msg), \ + virValidateFail(err_code, \ + _("%1$s module is not loaded, " err_msg), \ #mod); \ ret = -1; \ } #define MODULE_STATUS_FAIL(mod, err_msg) \ - MODULE_STATUS(mod, err_msg, VIR_HOST_VALIDATE_FAIL) + MODULE_STATUS(mod, err_msg, VIR_VALIDATE_FAIL) #define MODULE_STATUS_WARN(mod, err_msg) \ - MODULE_STATUS(mod, err_msg, VIR_HOST_VALIDATE_WARN) + MODULE_STATUS(mod, err_msg, VIR_VALIDATE_WARN) int virHostValidateBhyve(void) diff --git a/tools/virt-validate-common.c b/tools/virt-validate-common.c index 88c10e846f..9768fd9208 100644 --- a/tools/virt-validate-common.c +++ b/tools/virt-validate-common.c @@ -21,6 +21,8 @@ #include <config.h> +#include <unistd.h> + #include "virt-validate-common.h" static bool quiet; With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
participants (2)
-
Daniel P. Berrangé
-
Michal Prívozník