mirror of
https://github.com/systemd/systemd.git
synced 2024-11-26 19:53:45 +08:00
Introduce systemd-keyutil to do various key/certificate operations
Let's gather generic key/certificate operations in a new tool systemd-keyutil instead of spreading them across various special purpose tools. Fixes #35087
This commit is contained in:
parent
b480a4c15e
commit
4b1ad0398e
@ -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', [], ''],
|
||||
|
105
man/systemd-keyutil.xml
Normal file
105
man/systemd-keyutil.xml
Normal file
@ -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>
|
@ -104,16 +104,6 @@
|
||||
|
||||
<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>
|
||||
|
||||
|
@ -49,22 +49,6 @@
|
||||
<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>
|
||||
|
||||
|
@ -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
|
||||
|
292
src/keyutil/keyutil.c
Normal file
292
src/keyutil/keyutil.c
Normal file
@ -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);
|
12
src/keyutil/meson.build
Normal file
12
src/keyutil/meson.build
Normal file
@ -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,
|
||||
},
|
||||
]
|
@ -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 },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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])]
|
||||
|
||||
|
50
test/units/TEST-74-AUX-UTILS.keyutil.sh
Executable file
50
test/units/TEST-74-AUX-UTILS.keyutil.sh
Executable file
@ -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
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user