With this program we do not have to depend on the output of `certtool -i`, which changed the order of the fields at some point and the newest version is incompatible with what libvirt expects in tls_allowed_dn_list configuration option. Signed-off-by: Martin Kletzander <mkletzan@xxxxxxxxxx> --- libvirt.spec.in | 1 + po/POTFILES.in | 1 + tools/meson.build | 27 ++++++++ tools/virt-pki-query-dn.c | 140 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 169 insertions(+) create mode 100644 tools/virt-pki-query-dn.c diff --git a/libvirt.spec.in b/libvirt.spec.in index 4ecb28114ce8..5f1773ef93f2 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -1983,6 +1983,7 @@ exit 0 %{_mandir}/man1/virt-pki-validate.1* %{_bindir}/virsh %{_bindir}/virt-xml-validate +%{_bindir}/virt-pki-query-dn %{_bindir}/virt-pki-validate %{_datadir}/bash-completion/completions/virsh diff --git a/po/POTFILES.in b/po/POTFILES.in index 8a726f624e38..bf0a3b352979 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -376,6 +376,7 @@ @SRCDIR@tools/virt-host-validate-qemu.c @SRCDIR@tools/virt-host-validate.c @SRCDIR@tools/virt-login-shell-helper.c +@SRCDIR@tools/virt-pki-query-dn.c @SRCDIR@tools/vsh-table.c @SRCDIR@tools/vsh.c @SRCDIR@tools/vsh.h diff --git a/tools/meson.build b/tools/meson.build index bf0eab8b6bf2..3fba313e5f49 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -257,6 +257,33 @@ configure_file( install_mode: 'rwxrwxr-x', ) +executable( + 'virt-pki-query-dn', + [ + 'virt-pki-query-dn.c', + ], + dependencies: [ + glib_dep, + gnutls_dep, + ], + include_directories: [ + libvirt_inc, + src_inc_dir, + top_inc_dir, + util_inc_dir, + ], + link_args: ( + libvirt_relro + + libvirt_no_indirect + + libvirt_no_undefined + ), + link_with: [ + libvirt_lib + ], + install: true, + install_dir: bindir, +) + if conf.has('WITH_SANLOCK') configure_file( input: 'virt-sanlock-cleanup.in', diff --git a/tools/virt-pki-query-dn.c b/tools/virt-pki-query-dn.c new file mode 100644 index 000000000000..ee3783c1b279 --- /dev/null +++ b/tools/virt-pki-query-dn.c @@ -0,0 +1,140 @@ +/* + * 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" + + +static void +glib_auto_cleanup_gnutls_x509_crt_t(gnutls_x509_crt_t *pointer) +{ + gnutls_x509_crt_deinit(*pointer); +} + + +static void +print_usage(const char *progname, + FILE *out) +{ + fprintf(out, + _("Usage:\n" + " %s FILE\n" + " %s { -v | -h }\n" + "\n" + "Extract Distinguished Name from a PEM certificate.\n" + "The output is meant to be used in the tls_allowed_dn_list\n" + "configuration option in the libvirtd.conf file.\n" + "\n" + " FILE certificate file to extract the DN from\n" + "\n" + "options:\n" + " -h | --help display this help and exit\n" + " -v | --version output version information and exit\n"), + progname, progname); +} + + +int +main(int argc, + char **argv) +{ + const char *progname = NULL; + const char *filename = NULL; + size_t dnamesize = 256; + size_t bufsize = 0; + g_autofree char *dname = g_new0(char, dnamesize); + g_autofree char *buf = NULL; + g_auto(gnutls_x509_crt_t) crt = {0}; + gnutls_datum_t crt_data = {0}; + g_autoptr(GError) error = NULL; + int arg = 0; + int rv = 0; + + struct option opt[] = { + {"help", no_argument, NULL, 'h'}, + {"version", optional_argument, NULL, 'v'}, + {NULL, 0, NULL, 0} + }; + + if (virGettextInitialize() < 0) + return EXIT_FAILURE; + + if (!(progname = strrchr(argv[0], '/'))) + progname = argv[0]; + else + progname++; + + while ((arg = getopt_long(argc, argv, "hv", opt, NULL)) != -1) { + switch (arg) { + case 'v': + printf("%s\n", PACKAGE_VERSION); + return EXIT_SUCCESS; + case 'h': + print_usage(progname, stdout); + return EXIT_SUCCESS; + default: + print_usage(progname, stderr); + return EXIT_FAILURE; + } + } + + if (optind != argc - 1) { + print_usage(progname, stderr); + return EXIT_FAILURE; + } + + filename = argv[optind]; + + g_file_get_contents(filename, &buf, &bufsize, &error); + if (error) { + g_printerr("%s: %s\n", progname, error->message); + return EXIT_FAILURE; + } + + if (bufsize > UINT_MAX) { + g_printerr(_("%s: File '%s' is too large\n"), progname, filename); + return EXIT_FAILURE; + } + + crt_data.data = (unsigned char *)buf; + crt_data.size = bufsize; + + rv = gnutls_x509_crt_init(&crt); + if (rv < 0) { + g_printerr(_("Unable to initialize certificate: %s\n"), + gnutls_strerror(rv)); + return EXIT_FAILURE; + } + + rv = gnutls_x509_crt_import(crt, &crt_data, GNUTLS_X509_FMT_PEM); + if (rv < 0) { + g_printerr(_("Unable to load certificate, make sure it is in PEM format: %s\n"), + gnutls_strerror(rv)); + return EXIT_FAILURE; + } + + rv = gnutls_x509_crt_get_dn(crt, dname, &dnamesize); + if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER) { + dname = g_realloc(dname, dnamesize); + rv = gnutls_x509_crt_get_dn(crt, dname, &dnamesize); + } + if (rv != 0) { + g_printerr(_("Failed to get distinguished name: %s\n"), + gnutls_strerror(rv)); + return EXIT_FAILURE; + } + + printf("%s\n", dname); + + return EXIT_SUCCESS; +} -- 2.33.1