summaryrefslogtreecommitdiffstats
path: root/src/keyutil
diff options
context:
space:
mode:
authorDaan De Meyer <daan.j.demeyer@gmail.com>2024-11-08 11:34:21 +0100
committerDaan De Meyer <daan.j.demeyer@gmail.com>2024-11-08 15:00:21 +0100
commit4b1ad0398e7b0524eac87e1b6c4fdcb8c2c40294 (patch)
tree041410fdacae5d09a5d232db836f256c8e5ec608 /src/keyutil
parentupdate TODO (diff)
downloadsystemd-4b1ad0398e7b0524eac87e1b6c4fdcb8c2c40294.tar.xz
systemd-4b1ad0398e7b0524eac87e1b6c4fdcb8c2c40294.zip
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
Diffstat (limited to 'src/keyutil')
-rw-r--r--src/keyutil/keyutil.c292
-rw-r--r--src/keyutil/meson.build12
2 files changed, 304 insertions, 0 deletions
diff --git a/src/keyutil/keyutil.c b/src/keyutil/keyutil.c
new file mode 100644
index 0000000000..70176c76c7
--- /dev/null
+++ b/src/keyutil/keyutil.c
@@ -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
index 0000000000..956f603989
--- /dev/null
+++ b/src/keyutil/meson.build
@@ -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,
+ },
+]