]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Introduce systemd-keyutil to do various key/certificate operations 35095/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Fri, 8 Nov 2024 10:34:21 +0000 (11:34 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Fri, 8 Nov 2024 14:00:21 +0000 (15:00 +0100)
Let's gather generic key/certificate operations in a new tool
systemd-keyutil instead of spreading them across various special
purpose tools.

Fixes #35087

12 files changed:
man/rules/meson.build
man/systemd-keyutil.xml [new file with mode: 0644]
man/systemd-measure.xml
man/systemd-sbsign.xml
meson.build
src/keyutil/keyutil.c [new file with mode: 0644]
src/keyutil/meson.build [new file with mode: 0644]
src/measure/measure.c
src/sbsign/sbsign.c
src/ukify/ukify.py
test/units/TEST-74-AUX-UTILS.keyutil.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.sbsign.sh

index 7d2c62f57424ec7d6cc5975aa2fc0a375b8c7d96..e76cb0223b56076d5632cbbcc6631f00147882bc 100644 (file)
@@ -992,6 +992,7 @@ manpages = [
    'systemd-journald@.service',
    'systemd-journald@.socket'],
   ''],
+ ['systemd-keyutil', '1', [], ''],
  ['systemd-localed.service', '8', ['systemd-localed'], 'ENABLE_LOCALED'],
  ['systemd-logind.service', '8', ['systemd-logind'], 'ENABLE_LOGIND'],
  ['systemd-machine-id-commit.service', '8', [], ''],
diff --git a/man/systemd-keyutil.xml b/man/systemd-keyutil.xml
new file mode 100644 (file)
index 0000000..99d4d90
--- /dev/null
@@ -0,0 +1,105 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="systemd-keyutil"
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+  <refentryinfo>
+    <title>systemd-keyutil</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-keyutil</refentrytitle>
+    <manvolnum>1</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-keyutil</refname>
+    <refpurpose>Perform various operations on private keys and X.509 certificates</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>systemd-keyutil</command>
+      <arg choice="opt" rep="repeat">OPTIONS</arg>
+      <arg choice="req">COMMAND</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>systemd-keyutil</command> can be used to perform various operations on private keys and
+    X.509 certificates.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Commands</title>
+
+    <variablelist>
+      <varlistentry>
+        <term><option>validate</option></term>
+
+        <listitem><para>Checks that we can load the private key and certificate specified with
+        <option>--private-key=</option> and <option>--certificate=</option> respectively.</para>
+
+        <para>As a side effect, if the private key is loaded from a PIN-protected hardware token, this
+        command can be used to cache the PIN in the kernel keyring. The
+        <varname>$SYSTEMD_ASK_PASSWORD_KEYRING_TIMEOUT_SEC</varname> and
+        <varname>$SYSTEMD_ASK_PASSWORD_KEYRING_TYPE</varname> environment variables can be used to control
+        how long and in which kernel keyring the PIN is cached.</para>
+
+        <xi:include href="version-info.xml" xpointer="v257"/>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>public</command></term>
+
+        <listitem><para>This commands prints the public key in PEM format extracted from either the
+        certificate given with <option>--certificate=</option> or the private key given with
+        <option>--private-key=</option>.</para>
+
+        <xi:include href="version-info.xml" xpointer="v257"/></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+    <para>The following options are understood:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><option>--private-key=<replaceable>PATH/URI</replaceable></option></term>
+        <term><option>--private-key-source=<replaceable>TYPE</replaceable>[:<replaceable>NAME</replaceable>]</option></term>
+        <term><option>--certificate=<replaceable>PATH</replaceable></option></term>
+        <term><option>--certificate-source=<replaceable>TYPE</replaceable>[:<replaceable>NAME</replaceable>]</option></term>
+
+        <listitem><para>Set the private key and certificate to use. The <option>--certificate=</option>
+        option takes a path to a PEM encoded X.509 certificate or a URI that's passed to the OpenSSL provider
+        configured with <option>--certificate-source</option>. The <option>--certificate-source</option>
+        takes one of <literal>file</literal> or <literal>provider</literal>, with the latter being followed
+        by a specific provider identifier, separated with a colon, e.g. <literal>provider:pkcs11</literal>.
+        The <option>--private-key=</option> option can take a path or a URI that will be passed to the
+        OpenSSL engine or provider, as specified by <option>--private-key-source=</option> as a
+        <literal>type:name</literal> tuple, such as <literal>engine:pkcs11</literal></para>.
+
+        <xi:include href="version-info.xml" xpointer="v257"/></listitem>
+      </varlistentry>
+
+      <xi:include href="standard-options.xml" xpointer="help"/>
+      <xi:include href="standard-options.xml" xpointer="version"/>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para><simplelist type="inline">
+      <member><citerefentry><refentrytitle>systemd-sbsign</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+    </simplelist></para>
+  </refsect1>
+</refentry>
index 5ca373f1814411705917181383645e92f60470c2..c7e5a5e9e2171822f55b5c11c3e8d5e877f9d923 100644 (file)
 
         <xi:include href="version-info.xml" xpointer="v252"/></listitem>
       </varlistentry>
-
-      <varlistentry>
-        <term><command>pcrpkey</command></term>
-
-        <listitem><para>This commands prints the public key either given with <option>--public-key=</option>,
-        or extracted from the certificate given with <option>--certificate=</option> or the private key given
-        with <option>--private-key=</option>.</para>
-
-        <xi:include href="version-info.xml" xpointer="v257"/></listitem>
-      </varlistentry>
     </variablelist>
   </refsect1>
 
index 1248377845f5d334e4040fa17652fbec92f35a6f..57b685f8c38204a19e80913e858c3e839b8d3d10 100644 (file)
         <xi:include href="version-info.xml" xpointer="v257"/>
         </listitem>
       </varlistentry>
-
-      <varlistentry>
-        <term><option>validate-key</option></term>
-
-        <listitem><para>Checks that we can load the private key specified with
-        <option>--private-key=</option>. </para>
-
-        <para>As a side effect, if the private key is loaded from a PIN-protected hardware token, this
-        command can be used to cache the PIN in the kernel keyring. The
-        <varname>$SYSTEMD_ASK_PASSWORD_KEYRING_TIMEOUT_SEC</varname> and
-        <varname>$SYSTEMD_ASK_PASSWORD_KEYRING_TYPE</varname> environment variables can be used to control
-        how long and in which kernel keyring the PIN is cached.</para>
-
-        <xi:include href="version-info.xml" xpointer="v257"/>
-        </listitem>
-      </varlistentry>
     </variablelist>
   </refsect1>
 
index 6b62dfa052dc9ae52d9aaee7a8be272f11b1a5d0..fbab9300c547780e0c688c5e37443e002d10ed13 100644 (file)
@@ -2377,6 +2377,7 @@ subdir('src/integritysetup')
 subdir('src/journal')
 subdir('src/journal-remote')
 subdir('src/kernel-install')
+subdir('src/keyutil')
 subdir('src/locale')
 subdir('src/login')
 subdir('src/machine')
@@ -2698,7 +2699,7 @@ endif
 
 mkosi_depends = public_programs
 
-foreach executable : ['systemd-journal-remote', 'systemd-measure']
+foreach executable : ['systemd-journal-remote', 'systemd-measure', 'systemd-sbsign', 'systemd-keyutil']
         if executable in executables_by_name
                 mkosi_depends += [executables_by_name[executable]]
         endif
diff --git a/src/keyutil/keyutil.c b/src/keyutil/keyutil.c
new file mode 100644 (file)
index 0000000..70176c7
--- /dev/null
@@ -0,0 +1,292 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <getopt.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "ask-password-api.h"
+#include "build.h"
+#include "fd-util.h"
+#include "main-func.h"
+#include "memstream-util.h"
+#include "openssl-util.h"
+#include "parse-argument.h"
+#include "pretty-print.h"
+#include "verbs.h"
+
+static char *arg_private_key = NULL;
+static KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE;
+static char *arg_private_key_source = NULL;
+static char *arg_certificate = NULL;
+static char *arg_certificate_source = NULL;
+static CertificateSourceType arg_certificate_source_type = OPENSSL_CERTIFICATE_SOURCE_FILE;
+
+STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_certificate_source, freep);
+
+static int help(int argc, char *argv[], void *userdata) {
+        _cleanup_free_ char *link = NULL;
+        int r;
+
+        r = terminal_urlify_man("systemd-keyutil", "1", &link);
+        if (r < 0)
+                return log_oom();
+
+        printf("%1$s  [OPTIONS...] COMMAND ...\n"
+               "\n%5$sPerform various operations on private keys and certificates.%6$s\n"
+               "\n%3$sCommands:%4$s\n"
+               "  validate               Load and validate the given certificate and private key\n"
+               "  public                 Extract a public key\n"
+               "\n%3$sOptions:%4$s\n"
+               "  -h --help              Show this help\n"
+               "     --version           Print version\n"
+               "     --private-key=KEY   Private key in PEM format\n"
+               "     --private-key-source=file|provider:PROVIDER|engine:ENGINE\n"
+               "                         Specify how to use KEY for --private-key=. Allows\n"
+               "                         an OpenSSL engine/provider to be used for signing\n"
+               "     --certificate=PATH|URI\n"
+               "                         PEM certificate to use for signing, or a provider\n"
+               "                         specific designation if --certificate-source= is used\n"
+               "     --certificate-source=file|provider:PROVIDER\n"
+               "                         Specify how to interpret the certificate from\n"
+               "                         --certificate=. Allows the certificate to be loaded\n"
+               "                         from an OpenSSL provider\n"
+               "\nSee the %2$s for details.\n",
+               program_invocation_short_name,
+               link,
+               ansi_underline(),
+               ansi_normal(),
+               ansi_highlight(),
+               ansi_normal());
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_PRIVATE_KEY,
+                ARG_PRIVATE_KEY_SOURCE,
+                ARG_CERTIFICATE,
+                ARG_CERTIFICATE_SOURCE,
+        };
+
+        static const struct option options[] = {
+                { "help",               no_argument,       NULL, 'h'                    },
+                { "version",            no_argument,       NULL, ARG_VERSION            },
+                { "private-key",        required_argument, NULL, ARG_PRIVATE_KEY        },
+                { "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE },
+                { "certificate",        required_argument, NULL, ARG_CERTIFICATE        },
+                { "certificate-source", required_argument, NULL, ARG_CERTIFICATE_SOURCE },
+                {}
+        };
+
+        int c, r;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+                switch (c) {
+
+                case 'h':
+                        help(0, NULL, NULL);
+                        return 0;
+
+                case ARG_VERSION:
+                        return version();
+
+                case ARG_PRIVATE_KEY:
+                        r = free_and_strdup_warn(&arg_private_key, optarg);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case ARG_PRIVATE_KEY_SOURCE:
+                        r = parse_openssl_key_source_argument(
+                                        optarg,
+                                        &arg_private_key_source,
+                                        &arg_private_key_source_type);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case ARG_CERTIFICATE:
+                        r = free_and_strdup_warn(&arg_certificate, optarg);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case ARG_CERTIFICATE_SOURCE:
+                        r = parse_openssl_certificate_source_argument(
+                                        optarg,
+                                        &arg_certificate_source,
+                                        &arg_certificate_source_type);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached();
+                }
+
+        if (arg_private_key_source && !arg_certificate)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "When using --private-key-source=, --certificate= must be specified.");
+
+        return 1;
+}
+
+static int verb_validate(int argc, char *argv[], void *userdata) {
+        _cleanup_(X509_freep) X509 *certificate = NULL;
+        _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL;
+        _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL;
+        int r;
+
+        if (!arg_certificate)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "No certificate specified, use --certificate=");
+
+        if (!arg_private_key)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "No private key specified, use --private-key=.");
+
+        if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) {
+                r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate);
+                if (r < 0)
+                        return r;
+        }
+
+        r = openssl_load_x509_certificate(
+                        arg_certificate_source_type,
+                        arg_certificate_source,
+                        arg_certificate,
+                        &certificate);
+        if (r < 0)
+                return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
+
+        if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) {
+                r = parse_path_argument(arg_private_key, /* suppress_root= */ false, &arg_private_key);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse private key path %s: %m", arg_private_key);
+        }
+
+        r = openssl_load_private_key(
+                        arg_private_key_source_type,
+                        arg_private_key_source,
+                        arg_private_key,
+                        &(AskPasswordRequest) {
+                                .id = "keyutil-private-key-pin",
+                                .keyring = arg_private_key,
+                                .credential = "keyutil.private-key-pin",
+                        },
+                        &private_key,
+                        &ui);
+        if (r < 0)
+                return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key);
+
+        puts("OK");
+        return 0;
+}
+
+static int verb_public(int argc, char *argv[], void *userdata) {
+        _cleanup_(EVP_PKEY_freep) EVP_PKEY *public_key = NULL;
+        int r;
+
+        if (arg_certificate) {
+                _cleanup_(X509_freep) X509 *certificate = NULL;
+
+                if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) {
+                        r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate);
+                        if (r < 0)
+                                return r;
+                }
+
+                r = openssl_load_x509_certificate(
+                                arg_certificate_source_type,
+                                arg_certificate_source,
+                                arg_certificate,
+                                &certificate);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
+
+                public_key = X509_get_pubkey(certificate);
+                if (!public_key)
+                        return log_error_errno(
+                                        SYNTHETIC_ERRNO(EIO),
+                                        "Failed to extract public key from certificate %s.",
+                                        arg_certificate);
+
+        } else if (arg_private_key) {
+                _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL;
+                _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL;
+
+                if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) {
+                        r = parse_path_argument(arg_private_key, /* suppress_root= */ false, &arg_private_key);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse private key path %s: %m", arg_private_key);
+                }
+
+                r = openssl_load_private_key(
+                                arg_private_key_source_type,
+                                arg_private_key_source,
+                                arg_private_key,
+                                &(AskPasswordRequest) {
+                                        .id = "keyutil-private-key-pin",
+                                        .keyring = arg_private_key,
+                                        .credential = "keyutil.private-key-pin",
+                                },
+                                &private_key,
+                                &ui);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key);
+
+                _cleanup_(memstream_done) MemStream m = {};
+                FILE *tf = memstream_init(&m);
+                if (!tf)
+                        return log_oom();
+
+                if (i2d_PUBKEY_fp(tf, private_key) != 1)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                               "Failed to extract public key from private key file '%s'.", arg_private_key);
+
+                fflush(tf);
+                rewind(tf);
+
+                if (!d2i_PUBKEY_fp(tf, &public_key))
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                               "Failed to parse extracted public key of private key file '%s'.", arg_private_key);
+        } else
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "One of --certificate=, or --private-key= must be specified");
+
+        if (PEM_write_PUBKEY(stdout, public_key) == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write public key to stdout");
+
+        return 0;
+}
+
+static int run(int argc, char *argv[]) {
+        static const Verb verbs[] = {
+                { "help",     VERB_ANY, VERB_ANY, 0, help          },
+                { "validate", VERB_ANY, 1,        0, verb_validate },
+                { "public",   VERB_ANY, 1,        0, verb_public   },
+                {}
+        };
+        int r;
+
+        log_setup();
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r;
+
+        return dispatch_verb(argc, argv, verbs, NULL);
+}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/keyutil/meson.build b/src/keyutil/meson.build
new file mode 100644 (file)
index 0000000..956f603
--- /dev/null
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+executables += [
+        libexec_template + {
+                'name' : 'systemd-keyutil',
+                'conditions' : [
+                        'HAVE_OPENSSL',
+                ],
+                'sources' : files('keyutil.c'),
+                'dependencies' : libopenssl,
+        },
+]
index ac294d28b36cc396118656ed88fa3bb1aaae5824..979426c18fd2d913cdfc92fbc404acb0b441563b 100644 (file)
@@ -77,7 +77,6 @@ static int help(int argc, char *argv[], void *userdata) {
                "  status                 Show current PCR values\n"
                "  calculate              Calculate expected PCR values\n"
                "  sign                   Calculate and sign expected PCR values\n"
-               "  pcrpkey                Extract the PCR public key\n"
                "\n%3$sOptions:%4$s\n"
                "  -h --help              Show this help\n"
                "     --version           Print version\n"
@@ -1174,100 +1173,12 @@ static int verb_status(int argc, char *argv[], void *userdata) {
         return 0;
 }
 
-static int verb_pcrpkey(int argc, char *argv[], void *userdata) {
-        _cleanup_(EVP_PKEY_freep) EVP_PKEY *public_key = NULL;
-        int r;
-
-        if (arg_public_key) {
-                _cleanup_fclose_ FILE *public_keyf = NULL;
-
-                public_keyf = fopen(arg_public_key, "re");
-                if (!public_keyf)
-                        return log_error_errno(errno, "Failed to open public key file '%s': %m", arg_public_key);
-
-                public_key = PEM_read_PUBKEY(public_keyf, NULL, NULL, NULL);
-                if (!public_key)
-                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse public key '%s'.", arg_public_key);
-
-        } else if (arg_certificate) {
-                _cleanup_(X509_freep) X509 *certificate = NULL;
-
-                if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) {
-                        r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate);
-                        if (r < 0)
-                                return r;
-                }
-
-                r = openssl_load_x509_certificate(
-                                arg_certificate_source_type,
-                                arg_certificate_source,
-                                arg_certificate,
-                                &certificate);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
-
-                public_key = X509_get_pubkey(certificate);
-                if (!public_key)
-                        return log_error_errno(
-                                        SYNTHETIC_ERRNO(EIO),
-                                        "Failed to extract public key from certificate %s.",
-                                        arg_certificate);
-
-        } else if (arg_private_key) {
-                _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL;
-                _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL;
-
-                if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) {
-                        r = parse_path_argument(arg_private_key, /* suppress_root= */ false, &arg_private_key);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to parse private key path %s: %m", arg_private_key);
-                }
-
-                r = openssl_load_private_key(
-                                arg_private_key_source_type,
-                                arg_private_key_source,
-                                arg_private_key,
-                                &(AskPasswordRequest) {
-                                        .id = "measure-private-key-pin",
-                                        .keyring = arg_private_key,
-                                        .credential = "measure.private-key-pin",
-                                },
-                                &private_key,
-                                &ui);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key);
-
-                _cleanup_(memstream_done) MemStream m = {};
-                FILE *tf = memstream_init(&m);
-                if (!tf)
-                        return log_oom();
-
-                if (i2d_PUBKEY_fp(tf, private_key) != 1)
-                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                               "Failed to extract public key from private key file '%s'.", arg_private_key);
-
-                fflush(tf);
-                rewind(tf);
-
-                if (!d2i_PUBKEY_fp(tf, &public_key))
-                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                               "Failed to parse extracted public key of private key file '%s'.", arg_private_key);
-        } else
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "One of --public-key=, --certificate=, or --private-key= must be specified");
-
-        if (PEM_write_PUBKEY(stdout, public_key) == 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write public key to stdout");
-
-        return 0;
-}
-
 static int measure_main(int argc, char *argv[]) {
         static const Verb verbs[] = {
                 { "help",      VERB_ANY, VERB_ANY, 0,            help           },
                 { "status",    VERB_ANY, 1,        VERB_DEFAULT, verb_status    },
                 { "calculate", VERB_ANY, 1,        0,            verb_calculate },
                 { "sign",      VERB_ANY, 1,        0,            verb_sign      },
-                { "pcrpkey",   VERB_ANY, 1,        0,            verb_pcrpkey   },
                 {}
         };
 
index d65f28b4c4e00c6f52c05fd9b99edbd64c40ce29..81970a7302b757442995e3bede6cb5f490095c74 100644 (file)
@@ -45,7 +45,6 @@ static int help(int argc, char *argv[], void *userdata) {
                "\n%5$sSign binaries for EFI Secure Boot%6$s\n"
                "\n%3$sCommands:%4$s\n"
                "  sign EXEFILE           Sign the given binary for EFI Secure Boot\n"
-               "  validate-key           Load and validate the given certificate and private key\n"
                "\n%3$sOptions:%4$s\n"
                "  -h --help              Show this help\n"
                "     --version           Print version\n"
@@ -498,63 +497,10 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
         return 0;
 }
 
-static int verb_validate_key(int argc, char *argv[], void *userdata) {
-        _cleanup_(X509_freep) X509 *certificate = NULL;
-        _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL;
-        _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL;
-        int r;
-
-        if (!arg_certificate)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "No certificate specified, use --certificate=");
-
-        if (!arg_private_key)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "No private key specified, use --private-key=.");
-
-        if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) {
-                r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate);
-                if (r < 0)
-                        return r;
-        }
-
-        r = openssl_load_x509_certificate(
-                        arg_certificate_source_type,
-                        arg_certificate_source,
-                        arg_certificate,
-                        &certificate);
-        if (r < 0)
-                return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
-
-        if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) {
-                r = parse_path_argument(arg_private_key, /* suppress_root= */ false, &arg_private_key);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to parse private key path %s: %m", arg_private_key);
-        }
-
-        r = openssl_load_private_key(
-                        arg_private_key_source_type,
-                        arg_private_key_source,
-                        arg_private_key,
-                        &(AskPasswordRequest) {
-                                .id = "sbsign-private-key-pin",
-                                .keyring = arg_private_key,
-                                .credential = "sbsign.private-key-pin",
-                        },
-                        &private_key,
-                        &ui);
-        if (r < 0)
-                return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key);
-
-        puts("OK");
-        return 0;
-}
-
 static int run(int argc, char *argv[]) {
         static const Verb verbs[] = {
                 { "help",         VERB_ANY, VERB_ANY, 0,    help              },
                 { "sign",         2,        2,        0,    verb_sign         },
-                { "validate-key", VERB_ANY, 1,        0,    verb_validate_key },
                 {}
         };
         int r;
index 355e3f99f4132b6e3a754446f439541c64db9275..ae3e1d03b4ae01045af13ea4587a8faaced79514 100755 (executable)
@@ -1017,8 +1017,8 @@ def make_uki(opts: UkifyConfig) -> None:
 
     pcrpkey: Union[bytes, Path, None] = opts.pcrpkey
     if pcrpkey is None:
-        measure_tool = find_tool('systemd-measure', '/usr/lib/systemd/systemd-measure')
-        cmd = [measure_tool, 'pcrpkey']
+        measure_tool = find_tool('systemd-keyutil', '/usr/lib/systemd/systemd-keyutil')
+        cmd = [measure_tool, 'public']
 
         if opts.pcr_public_keys and len(opts.pcr_public_keys) == 1:
             # If we're using an engine or provider, the public key will be an X.509 certificate.
@@ -1026,11 +1026,11 @@ def make_uki(opts: UkifyConfig) -> None:
                 cmd += ['--certificate', opts.pcr_public_keys[0]]
                 if opts.certificate_provider:
                     cmd += ['--certificate-source', f'provider:{opts.certificate_provider}']
-            else:
-                cmd += ['--public-key', opts.pcr_public_keys[0]]
 
-            print('+', shell_join(cmd))
-            pcrpkey = subprocess.check_output(cmd)
+                print('+', shell_join(cmd))
+                pcrpkey = subprocess.check_output(cmd)
+            else:
+                pcrpkey = Path(opts.pcr_public_keys[0])
         elif opts.pcr_private_keys and len(opts.pcr_private_keys) == 1:
             cmd += ['--private-key', Path(opts.pcr_private_keys[0])]
 
diff --git a/test/units/TEST-74-AUX-UTILS.keyutil.sh b/test/units/TEST-74-AUX-UTILS.keyutil.sh
new file mode 100755 (executable)
index 0000000..bbbbf9f
--- /dev/null
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+if ! command -v /usr/lib/systemd/systemd-keyutil >/dev/null; then
+    echo "systemd-keyutil not found, skipping."
+    exit 0
+fi
+
+cat >/tmp/openssl.conf <<EOF
+[ req ]
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = DE
+ST = Test State
+L = Test Locality
+O = Org Name
+OU = Org Unit Name
+CN = Common Name
+emailAddress = test@email.com
+EOF
+
+openssl req -config /tmp/openssl.conf -subj="/CN=waldo" \
+        -x509 -sha256 -nodes -days 365 -newkey rsa:4096 \
+        -keyout /tmp/test.key -out /tmp/test.crt
+
+testcase_validate() {
+    /usr/lib/systemd/systemd-keyutil validate --certificate /tmp/test.crt --private-key /tmp/test.key
+}
+
+testcase_public() {
+    PUBLIC="$(/usr/lib/systemd/systemd-keyutil public --certificate /tmp/test.crt)"
+    assert_eq "$PUBLIC" "$(openssl x509 -in /tmp/test.crt -pubkey -noout)"
+
+    PUBLIC="$(/usr/lib/systemd/systemd-keyutil public --private-key /tmp/test.key)"
+    assert_eq "$PUBLIC" "$(openssl x509 -in /tmp/test.crt -pubkey -noout)"
+
+    (! /usr/lib/systemd/systemd-keyutil public)
+}
+
+run_testcases
index 891a2ae8aff1bd6c278aa05772f65a4e6a3f385e..fc186517d129b89401d68961af6c83b75b73f574 100755 (executable)
@@ -53,8 +53,4 @@ testcase_sign_systemd_boot() {
     sbverify --cert /tmp/sb.crt /tmp/sdboot
 }
 
-testcase_validate_key() {
-    /usr/lib/systemd/systemd-sbsign validate-key --certificate /tmp/sb.crt --private-key /tmp/sb.key
-}
-
 run_testcases