AUTHOR
======
-Daniel Veillard
+Daniel Veillard, Daniel P. Berrangé
BUGS
COPYRIGHT
=========
-Copyright (C) 2006-2012 by Red Hat, Inc.
+Copyright (C) 2006-2024 by Red Hat, Inc.
LICENSE
%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
%{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
%{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
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
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(
--- /dev/null
+/*
+ * 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-ca 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;
+}
+++ /dev/null
-#!/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-ca"
- 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-ca"
- 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