report: add a simple software-backed signing backend
This commit is contained in:
@@ -1185,6 +1185,11 @@ manpages = [
|
||||
['systemd-remount-fs.service', '8', ['systemd-remount-fs'], ''],
|
||||
['systemd-repart', '8', ['systemd-repart.service'], 'ENABLE_REPART'],
|
||||
['systemd-report', '1', [], ''],
|
||||
['systemd-report-sign-plain@.service',
|
||||
'8',
|
||||
['systemd-report-sign-plain',
|
||||
'systemd-report-sign-plain.socket'],
|
||||
'HAVE_OPENSSL'],
|
||||
['systemd-resolved.service', '8', ['systemd-resolved'], 'ENABLE_RESOLVE'],
|
||||
['systemd-rfkill.service',
|
||||
'8',
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<?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-report-sign-plain_.service" conditional='HAVE_OPENSSL'
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-report-sign-plain@.service</title>
|
||||
<productname>systemd</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-report-sign-plain@.service</refentrytitle>
|
||||
<manvolnum>8</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-report-sign-plain@.service</refname>
|
||||
<refname>systemd-report-sign-plain.socket</refname>
|
||||
<refname>systemd-report-sign-plain</refname>
|
||||
<refpurpose>Sign system reports with a local software key</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<para><filename>systemd-report-sign-plain@.service</filename></para>
|
||||
<para><filename>systemd-report-sign-plain.socket</filename></para>
|
||||
<para><filename>/usr/lib/systemd/systemd-report-sign-plain</filename></para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><command>systemd-report-sign-plain@.service</command> is a system service that signs system reports
|
||||
generated by
|
||||
<citerefentry><refentrytitle>systemd-report</refentrytitle><manvolnum>1</manvolnum></citerefentry>. It is
|
||||
a signing backend for the <option>--sign=</option> logic of that tool: it implements the
|
||||
<function>io.systemd.Report.Signer.Sign()</function> Varlink method and is reached via a socket linked into
|
||||
the <filename>/run/systemd/report.sign/</filename> directory, named <filename>plain</filename>.</para>
|
||||
|
||||
<para>The service is socket-activated (one instance per connection) via
|
||||
<filename>systemd-report-sign-plain.socket</filename>, and signs the digest passed to it with a local
|
||||
software key. Signatures are made with the Ed25519 (EdDSA) algorithm.</para>
|
||||
|
||||
<para>On first use a key pair is generated automatically and stored below
|
||||
<filename>/var/lib/systemd/report.sign.plain/</filename>: the private key in
|
||||
<filename>local.private</filename> (PKCS#8 PEM, readable only by the root user) and the public key in
|
||||
<filename>local.public</filename> (PEM, world readable).</para>
|
||||
|
||||
<para>The public key is also exported as a system report metric.</para>
|
||||
</refsect1>
|
||||
|
||||
<!-- Note: we do not document the command line switches here. The systemd-report-sign-plain binary itself
|
||||
should not be invoked by users, but only as a service. Use systemd-report(1) to generate signed
|
||||
reports. -->
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para><simplelist type="inline">
|
||||
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-report</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
</simplelist></para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
@@ -33,6 +33,7 @@ enable systemd-nsresourced.socket
|
||||
enable systemd-pstore.service
|
||||
enable systemd-report-basic.socket
|
||||
enable systemd-report-cgroup.socket
|
||||
enable systemd-report-sign-plain.socket
|
||||
enable systemd-resolved.service
|
||||
enable systemd-sysext.service
|
||||
enable systemd-timesyncd.service
|
||||
|
||||
@@ -27,4 +27,14 @@ executables += [
|
||||
'report-cgroup-server.c',
|
||||
),
|
||||
},
|
||||
libexec_template + {
|
||||
'name' : 'systemd-report-sign-plain',
|
||||
'conditions' : [
|
||||
'HAVE_OPENSSL',
|
||||
],
|
||||
'sources' : files(
|
||||
'report-sign-plain.c',
|
||||
),
|
||||
'dependencies' : libopenssl_cflags,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -0,0 +1,403 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <sys/file.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-json.h"
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "build.h"
|
||||
#include "crypto-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "format-table.h"
|
||||
#include "fs-util.h"
|
||||
#include "help-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "json-util.h"
|
||||
#include "lock-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "metrics.h"
|
||||
#include "options.h"
|
||||
#include "stat-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "varlink-io.systemd.Metrics.h"
|
||||
#include "varlink-io.systemd.Report.Signer.h"
|
||||
#include "varlink-util.h"
|
||||
|
||||
#define REPORT_SIGN_PLAIN_DIR "/var/lib/systemd/report.sign.plain"
|
||||
#define REPORT_SIGN_PLAIN_PRIVATE_KEY "local.private"
|
||||
#define REPORT_SIGN_PLAIN_PUBLIC_KEY "local.public"
|
||||
|
||||
#define METRIC_IO_SYSTEMD_SIGN_PLAIN_PREFIX "io.systemd.Report.SignPlain."
|
||||
|
||||
static int load_key(int dir_fd, EVP_PKEY **ret) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
assert(dir_fd >= 0);
|
||||
assert(ret);
|
||||
|
||||
r = xfopenat(dir_fd, REPORT_SIGN_PLAIN_PRIVATE_KEY, "re", /* open_flags= */ 0, &f);
|
||||
if (r == -ENOENT) {
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open private key file '%s/%s': %m",
|
||||
REPORT_SIGN_PLAIN_DIR, REPORT_SIGN_PLAIN_PRIVATE_KEY);
|
||||
|
||||
if (fstat(fileno(f), &st) < 0)
|
||||
return log_error_errno(errno, "Failed to stat private key file: %m");
|
||||
|
||||
r = stat_verify_regular(&st);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Private key file is not a regular file: %m");
|
||||
|
||||
if (st.st_uid != 0 || (st.st_mode & 0077) != 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EPERM),
|
||||
"Private key file '%s/%s' is accessible by more than the root user, refusing.",
|
||||
REPORT_SIGN_PLAIN_DIR, REPORT_SIGN_PLAIN_PRIVATE_KEY);
|
||||
|
||||
_cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = sym_PEM_read_PrivateKey(f, /* x= */ NULL, /* cb= */ NULL, /* u= */ NULL);
|
||||
if (!pkey)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to load private key from '%s/%s'.",
|
||||
REPORT_SIGN_PLAIN_DIR, REPORT_SIGN_PLAIN_PRIVATE_KEY);
|
||||
|
||||
log_debug("Successfully loaded private key from '%s/%s'.", REPORT_SIGN_PLAIN_DIR, REPORT_SIGN_PLAIN_PRIVATE_KEY);
|
||||
|
||||
*ret = TAKE_PTR(pkey);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int write_key_file(int dir_fd, EVP_PKEY *pkey, bool private_key, const char *name, mode_t mode) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_free_ char *temp = NULL;
|
||||
int r;
|
||||
|
||||
assert(dir_fd >= 0);
|
||||
assert(pkey);
|
||||
assert(name);
|
||||
|
||||
r = fopen_temporary_at(dir_fd, name, &f, &temp);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open key file '%s/%s' for writing: %m", REPORT_SIGN_PLAIN_DIR, name);
|
||||
|
||||
CLEANUP_TMPFILE_AT(dir_fd, temp);
|
||||
|
||||
if (private_key)
|
||||
r = sym_PEM_write_PrivateKey(f, pkey, /* enc= */ NULL, /* kstr= */ NULL, /* klen= */ 0, /* cb= */ NULL, /* u= */ NULL);
|
||||
else
|
||||
r = sym_PEM_write_PUBKEY(f, pkey);
|
||||
if (r <= 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write %s key.", private_key ? "private" : "public");
|
||||
|
||||
if (fchmod(fileno(f), mode) < 0)
|
||||
return log_error_errno(errno, "Failed to adjust key file access mode: %m");
|
||||
|
||||
r = fflush_sync_and_check(f);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write key file: %m");
|
||||
|
||||
f = safe_fclose(f);
|
||||
|
||||
/* Never overwrite an existing private key: we must not destroy it. The public key is derived from the
|
||||
* private one and carries no secret, hence may be refreshed unconditionally. */
|
||||
if (private_key)
|
||||
r = rename_noreplace(dir_fd, temp, dir_fd, name);
|
||||
else
|
||||
r = RET_NERRNO(renameat(dir_fd, temp, dir_fd, name));
|
||||
if (r < 0) {
|
||||
if (private_key && r == -EEXIST)
|
||||
return log_error_errno(r, "Private key file '%s/%s' appeared while generating a new one, refusing to overwrite it.",
|
||||
REPORT_SIGN_PLAIN_DIR, name);
|
||||
|
||||
return log_error_errno(r, "Failed to move key file '%s/%s' into place: %m", REPORT_SIGN_PLAIN_DIR, name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generate_key(int dir_fd, EVP_PKEY **ret) {
|
||||
int r;
|
||||
|
||||
assert(dir_fd >= 0);
|
||||
assert(ret);
|
||||
|
||||
/* Generate a new Ed25519 key pair. */
|
||||
|
||||
_cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = sym_EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, /* e= */ NULL);
|
||||
if (!ctx)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to allocate Ed25519 key generation context.");
|
||||
|
||||
if (sym_EVP_PKEY_keygen_init(ctx) <= 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize Ed25519 key generation context.");
|
||||
|
||||
log_info("Generating Ed25519 key pair for signing reports.");
|
||||
|
||||
_cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL;
|
||||
if (sym_EVP_PKEY_keygen(ctx, &pkey) <= 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to generate Ed25519 key pair.");
|
||||
|
||||
log_info("Successfully generated Ed25519 key pair.");
|
||||
|
||||
/* Write out the private key (PKCS#8 PEM; this includes the public key too), readable by root only. */
|
||||
r = write_key_file(dir_fd, pkey, /* private_key= */ true, REPORT_SIGN_PLAIN_PRIVATE_KEY, 0400);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Write out the public key too, as a convenience for verification, world readable. */
|
||||
r = write_key_file(dir_fd, pkey, /* private_key= */ false, REPORT_SIGN_PLAIN_PUBLIC_KEY, 0444);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = RET_NERRNO(fsync(dir_fd));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to sync directory '%s': %m", REPORT_SIGN_PLAIN_DIR);
|
||||
|
||||
*ret = TAKE_PTR(pkey);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int acquire_key(EVP_PKEY **ret) {
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
/* Open (creating if necessary) and exclusively lock the key directory, so that loading/generating the
|
||||
* key is safe against parallel invocations (we are spawned once per connection). The directory is
|
||||
* created with mode 0700, so that nobody else can pre-create it with looser permissions. */
|
||||
_cleanup_close_ int dir_fd = xopenat_lock_full(
|
||||
AT_FDCWD,
|
||||
REPORT_SIGN_PLAIN_DIR,
|
||||
O_CLOEXEC|O_DIRECTORY|O_CREAT,
|
||||
/* xopen_flags= */ 0,
|
||||
/* mode= */ 0700,
|
||||
LOCK_BSD,
|
||||
LOCK_EX);
|
||||
if (dir_fd < 0)
|
||||
return log_error_errno(dir_fd, "Failed to open and lock directory '%s': %m", REPORT_SIGN_PLAIN_DIR);
|
||||
|
||||
/* First try to load the key off disk. If loading fails for any reason other than the key not
|
||||
* existing yet (e.g. it is malformed or has bad permissions) we propagate the error and refuse to
|
||||
* touch the existing file. Only if the key is genuinely missing do we generate a fresh one. */
|
||||
r = load_key(dir_fd, ret);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
return generate_key(dir_fd, ret);
|
||||
}
|
||||
|
||||
static int vl_method_sign(
|
||||
sd_varlink *link,
|
||||
sd_json_variant *parameters,
|
||||
sd_varlink_method_flags_t flags,
|
||||
void *userdata) {
|
||||
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "digest", SD_JSON_VARIANT_STRING, json_dispatch_unhex_iovec, 0, SD_JSON_MANDATORY },
|
||||
{ "algorithm", SD_JSON_VARIANT_STRING, NULL, 0, SD_JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
|
||||
_cleanup_(iovec_done) struct iovec digest = {};
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(parameters);
|
||||
|
||||
/* Signing is privileged: we share the Varlink server (and possibly the socket) with the unprivileged
|
||||
* metrics interface, so we cannot rely on a server-wide access check and must gate this method
|
||||
* ourselves. Only root (i.e. ourselves) may request signatures. */
|
||||
r = varlink_check_privileged_peer(link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_varlink_dispatch(link, parameters, dispatch_table, &digest);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
if (!iovec_is_set(&digest))
|
||||
return sd_varlink_error_invalid_parameter_name(link, "digest");
|
||||
|
||||
_cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL;
|
||||
r = acquire_key(&pkey);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Sign the provided digest directly. We pass a NULL message digest algorithm, which for a suitable
|
||||
* signing algorithm such as Ed25519 means the input bytes are signed as-is, without hashing them
|
||||
* first. The caller already passes us a digest, hence we must not hash it again. */
|
||||
_cleanup_(iovec_done) struct iovec signature = {};
|
||||
r = digest_and_sign(/* md= */ NULL, pkey, digest.iov_base, digest.iov_len, &signature.iov_base, &signature.iov_len);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to sign digest: %m");
|
||||
|
||||
_cleanup_free_ char *pubkey = NULL;
|
||||
r = openssl_pubkey_to_pem(pkey, &pubkey);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract public key: %m");
|
||||
|
||||
/* Return a single signature object, carrying the raw Ed25519 (EdDSA) signature and the public key it
|
||||
* was made with, in PEM form. */
|
||||
return sd_varlink_replybo(
|
||||
link,
|
||||
SD_JSON_BUILD_PAIR(
|
||||
"data",
|
||||
SD_JSON_BUILD_ARRAY(
|
||||
SD_JSON_BUILD_OBJECT(
|
||||
JSON_BUILD_PAIR_IOVEC_BASE64("signature", &signature),
|
||||
SD_JSON_BUILD_PAIR_STRING("key", pubkey)))));
|
||||
}
|
||||
|
||||
static int public_key_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) {
|
||||
int r;
|
||||
|
||||
assert(mf && mf->name);
|
||||
assert(link);
|
||||
|
||||
/* Expose the public key the signer uses also as metrics. This has the nice effect that the signature
|
||||
* will cover the public key, in the sense of a self-signed certificate. We acquire it through the
|
||||
* very same logic as the Sign() method, i.e. we load it off disk, or generate it on first use if it
|
||||
* is still missing. */
|
||||
_cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL;
|
||||
r = acquire_key(&pkey);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_free_ char *pubkey = NULL;
|
||||
r = openssl_pubkey_to_pem(pkey, &pubkey);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract public key: %m");
|
||||
|
||||
return metric_build_send_string(
|
||||
mf,
|
||||
link,
|
||||
/* object= */ NULL,
|
||||
pubkey,
|
||||
/* fields= */ NULL);
|
||||
}
|
||||
|
||||
static const MetricFamily metric_family_table[] = {
|
||||
{
|
||||
METRIC_IO_SYSTEMD_SIGN_PLAIN_PREFIX "PublicKey",
|
||||
"PEM-encoded public key used to sign system reports with the local software key",
|
||||
METRIC_FAMILY_TYPE_STRING,
|
||||
.generate = public_key_generate,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static int vl_method_list_metrics(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
return metrics_method_list(metric_family_table, link, parameters, flags, userdata);
|
||||
}
|
||||
|
||||
static int vl_method_describe_metrics(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
return metrics_method_describe(metric_family_table, link, parameters, flags, userdata);
|
||||
}
|
||||
|
||||
static int vl_server(void) {
|
||||
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *vs = NULL;
|
||||
int r;
|
||||
|
||||
r = DLOPEN_LIBCRYPTO(LOG_ERR, SD_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* No server-wide access restriction: the metrics interface is meant to be reachable unprivileged,
|
||||
* while the signing method enforces root access on its own (see vl_method_sign()). */
|
||||
r = varlink_server_new(&vs, /* flags= */ 0, /* userdata= */ NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate Varlink server: %m");
|
||||
|
||||
/* We serve two interfaces off the same server: the signing API and a metrics provider that exposes our
|
||||
* public key. Either is reachable regardless of which socket the connection arrived on. */
|
||||
r = sd_varlink_server_add_interface_many(
|
||||
vs,
|
||||
&vl_interface_io_systemd_Report_Signer,
|
||||
&vl_interface_io_systemd_Metrics);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add Varlink interfaces: %m");
|
||||
|
||||
r = sd_varlink_server_bind_method_many(
|
||||
vs,
|
||||
"io.systemd.Report.Signer.Sign", vl_method_sign,
|
||||
"io.systemd.Metrics.List", vl_method_list_metrics,
|
||||
"io.systemd.Metrics.Describe", vl_method_describe_metrics);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to bind Varlink methods: %m");
|
||||
|
||||
r = sd_varlink_server_loop_auto(vs);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to run Varlink event loop: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_(table_unrefp) Table *options = NULL;
|
||||
int r;
|
||||
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_cmdline("[OPTIONS...]");
|
||||
help_abstract("Sign system reports with a local software key.");
|
||||
help_section("Options");
|
||||
|
||||
r = table_print_or_warn(options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_man_page_reference("systemd-report-sign-plain@.service", "8");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
int r;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
OptionParser opts = { argc, argv };
|
||||
|
||||
FOREACH_OPTION_OR_RETURN(c, &opts)
|
||||
switch (c) {
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
}
|
||||
|
||||
if (option_parser_get_n_args(&opts) > 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"This program takes no arguments.");
|
||||
|
||||
r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
|
||||
if (r == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"This program can only run as a Varlink service.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
return vl_server();
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
@@ -1615,6 +1615,9 @@ int digest_and_sign(
|
||||
if (!mdctx)
|
||||
return log_openssl_errors("Failed to create new EVP_MD_CTX");
|
||||
|
||||
/* Note that a NULL 'md' (message digest algorithm) means to sign the provided data directly, without
|
||||
* hashing it first, as long as a suitable signing algorithm is used that supports this, such as
|
||||
* Ed25519 (PureEdDSA). For such algorithms callers may pass an already calculated digest as input. */
|
||||
if (sym_EVP_DigestSignInit(mdctx, NULL, md, NULL, privkey) != 1) {
|
||||
/* Distro security policies often disable support for SHA-1. Let's return a recognizable
|
||||
* error for that case. */
|
||||
|
||||
@@ -731,6 +731,14 @@ units = [
|
||||
},
|
||||
{ 'file' : 'systemd-report-cgroup.socket' },
|
||||
{ 'file' : 'systemd-report-cgroup@.service.in' },
|
||||
{
|
||||
'file' : 'systemd-report-sign-plain.socket',
|
||||
'conditions' : ['HAVE_OPENSSL'],
|
||||
},
|
||||
{
|
||||
'file' : 'systemd-report-sign-plain@.service.in',
|
||||
'conditions' : ['HAVE_OPENSSL'],
|
||||
},
|
||||
{
|
||||
'file' : 'systemd-resolved.service.in',
|
||||
'conditions' : ['ENABLE_RESOLVE'],
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd 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.
|
||||
|
||||
[Unit]
|
||||
Description=Report Signing Socket (Local Software Key)
|
||||
Documentation=man:systemd-report-sign-plain@.service(8)
|
||||
DefaultDependencies=no
|
||||
Before=sockets.target shutdown.target
|
||||
Conflicts=shutdown.target
|
||||
WantsMountsFor=/var/lib/systemd/report.sign.plain
|
||||
|
||||
# The same binary serves two interfaces off one socket: an unprivileged metrics provider exposing our public
|
||||
# key, and the privileged signing API. The actual socket node hence lives in the world-accessible metrics
|
||||
# directory /run/systemd/report/, and the signing entrypoint is a symlink into the root-only
|
||||
# /run/systemd/report.sign/ directory. Access to the signing method itself is restricted to root by the
|
||||
# service (see vl_method_sign()); the directory permissions are merely an additional barrier.
|
||||
[Socket]
|
||||
ListenStream=/run/systemd/report/io.systemd.Report.SignPlain
|
||||
Symlinks=/run/systemd/report.sign/plain
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0666
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
RemoveOnStop=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
@@ -0,0 +1,21 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd 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.
|
||||
|
||||
[Unit]
|
||||
Description=Report Signing Service (Local Software Key)
|
||||
Documentation=man:systemd-report-sign-plain@.service(8)
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target
|
||||
Before=shutdown.target
|
||||
WantsMountsFor=/var/lib/systemd/report.sign.plain
|
||||
|
||||
[Service]
|
||||
StateDirectory=systemd/report.sign.plain
|
||||
StateDirectoryMode=0700
|
||||
ExecStart={{LIBEXECDIR}}/systemd-report-sign-plain
|
||||
Reference in New Issue
Block a user