]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
report: add systemd-report-sign-tsm backend 42683/head
authorPaul Meyer <katexochen0@gmail.com>
Fri, 19 Jun 2026 08:11:52 +0000 (10:11 +0200)
committerPaul Meyer <katexochen0@gmail.com>
Mon, 22 Jun 2026 18:42:35 +0000 (20:42 +0200)
A report signing backend that returns a confidential-computing
attestation report obtained via configfs-tsm. Implements
io.systemd.Report.Signer.Sign(): embeds the digest as the report's
inblob and returns the outblob (plus provider and any aux/manifest
blobs). Wired up as the "tsm" mechanism with a socket-activated service.

Signed-off-by: Paul Meyer <katexochen0@gmail.com>
man/rules/meson.build
man/systemd-report-sign-tsm@.service.xml [new file with mode: 0644]
presets/90-systemd.preset
src/report/meson.build
src/report/report-sign-tsm.c [new file with mode: 0644]
units/meson.build
units/systemd-report-sign-tsm.socket [new file with mode: 0644]
units/systemd-report-sign-tsm@.service.in [new file with mode: 0644]

index a3d8500cc6651dab85676fdf6d3a8f91720c3527..b37eb5f19f393c314bfc6be8a409b58890976594 100644 (file)
@@ -1196,6 +1196,11 @@ manpages = [
   ['systemd-report-sign-plain',
    'systemd-report-sign-plain.socket'],
   'HAVE_OPENSSL'],
+ ['systemd-report-sign-tsm@.service',
+  '8',
+  ['systemd-report-sign-tsm',
+   'systemd-report-sign-tsm.socket'],
+  ''],
  ['systemd-resolved.service', '8', ['systemd-resolved'], 'ENABLE_RESOLVE'],
  ['systemd-rfkill.service',
   '8',
diff --git a/man/systemd-report-sign-tsm@.service.xml b/man/systemd-report-sign-tsm@.service.xml
new file mode 100644 (file)
index 0000000..61d67b2
--- /dev/null
@@ -0,0 +1,73 @@
+<?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-tsm_.service"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>systemd-report-sign-tsm@.service</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-report-sign-tsm@.service</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-report-sign-tsm@.service</refname>
+    <refname>systemd-report-sign-tsm.socket</refname>
+    <refname>systemd-report-sign-tsm</refname>
+    <refpurpose>Sign system reports with a confidential-computing attestation report</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><filename>systemd-report-sign-tsm@.service</filename></para>
+    <para><filename>systemd-report-sign-tsm.socket</filename></para>
+    <para><filename>/usr/lib/systemd/systemd-report-sign-tsm</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>systemd-report-sign-tsm@.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>tsm</filename>.</para>
+
+    <para>The service is socket-activated (one instance per connection) via
+    <filename>systemd-report-sign-tsm.socket</filename>. Rather than signing with a local key, it obtains a
+    hardware-backed attestation report from the platform's Trusted Security Module (TSM) through the kernel's
+    configfs-TSM interface at <filename>/sys/kernel/config/tsm/report/</filename>. The digest passed to it is
+    embedded into the report as its run-time provided data (<literal>inblob</literal>), cryptographically
+    binding the attestation report to the system report being signed.</para>
+
+    <para>The returned signature carries the binary attestation report (the <literal>outblob</literal>), the
+    name of the TSM provider that generated it (for example <literal>sev_guest</literal> for AMD SEV-SNP or
+    <literal>tdx_guest</literal> for Intel TDX), and, where the provider supplies them, supplemental
+    certificate (<literal>auxblob</literal>) and manifest (<literal>manifestblob</literal>) data. A verifier
+    selects the appropriate validation logic based on the provider field.</para>
+
+    <para>This backend is only functional inside a confidential virtual machine whose kernel exposes the
+    configfs-TSM interface (such as AMD SEV-SNP or Intel TDX guests). On systems where the interface is
+    unavailable the signing operation is reported as unsupported and the mechanism is skipped. The service
+    keeps no persistent state.</para>
+  </refsect1>
+
+  <!-- Note: we do not document the command line switches here. The systemd-report-sign-tsm 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>
index a00643f6841280050ec89dc7bedada46c060c6ce..005280cd94db8d7f5b88dbb83444feb1d43bfa7e 100644 (file)
@@ -35,6 +35,7 @@ enable systemd-report-basic.socket
 enable systemd-report-cgroup.socket
 enable systemd-report-files.socket
 enable systemd-report-sign-plain.socket
+enable systemd-report-sign-tsm.socket
 enable systemd-report.socket
 enable systemd-resolved.service
 enable systemd-sysext.service
index c3e7cf959e456127a6b68e3d2579c933fe49439e..d0a316c6897d28bee7e229c418b0ad15c278680a 100644 (file)
@@ -44,4 +44,10 @@ executables += [
                 ),
                 'dependencies' : libopenssl_cflags,
         },
+        libexec_template + {
+                'name' : 'systemd-report-sign-tsm',
+                'sources' : files(
+                        'report-sign-tsm.c',
+                ),
+        },
 ]
diff --git a/src/report/report-sign-tsm.c b/src/report/report-sign-tsm.c
new file mode 100644 (file)
index 0000000..0929fbb
--- /dev/null
@@ -0,0 +1,167 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sd-json.h"
+#include "sd-varlink.h"
+
+#include "build.h"
+#include "format-table.h"
+#include "help-util.h"
+#include "iovec-util.h"
+#include "json-util.h"
+#include "log.h"
+#include "main-func.h"
+#include "options.h"
+#include "sha256.h"
+#include "string-util.h"
+#include "tsm-report.h"
+#include "varlink-io.systemd.Report.Signer.h"
+#include "varlink-util.h"
+
+typedef struct SignParameters {
+        struct iovec digest;
+        const char *algorithm;
+} SignParameters;
+
+static void sign_parameters_done(SignParameters *p) {
+        iovec_done(&p->digest);
+}
+
+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,     offsetof(SignParameters, digest),    SD_JSON_MANDATORY },
+                { "algorithm", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(SignParameters, algorithm), SD_JSON_MANDATORY },
+                {}
+        };
+
+        _cleanup_(sign_parameters_done) SignParameters sp = {};
+        _cleanup_(tsm_report_freep) TsmReport *report = NULL;
+        int r;
+
+        assert(link);
+        assert(parameters);
+
+        r = sd_varlink_dispatch(link, parameters, dispatch_table, &sp);
+        if (r != 0)
+                return r;
+
+        if (!streq(sp.algorithm, "SHA256"))
+                return sd_varlink_error_invalid_parameter_name(link, "algorithm");
+        if (sp.digest.iov_len != SHA256_DIGEST_SIZE)
+                return sd_varlink_error_invalid_parameter_name(link, "digest");
+
+        uint8_t report_data[TSM_REPORT_DATA_SIZE] = {};
+        memcpy(report_data, sp.digest.iov_base, sp.digest.iov_len);
+
+        r = tsm_report_acquire(&IOVEC_MAKE(report_data, sizeof report_data), /* options= */ NULL, &report);
+        if (IN_SET(r, -EOPNOTSUPP, -ENXIO))
+                /* Returning an error will fail the whole report signing, so we return
+                 * no signature instead which will be ignored by the aggregator. */
+                return sd_varlink_replybo(link, SD_JSON_BUILD_PAIR_EMPTY_ARRAY("data"));
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire TSM report: %m");
+
+        return sd_varlink_replybo(
+                        link,
+                        SD_JSON_BUILD_PAIR("data",
+                                        SD_JSON_BUILD_ARRAY(
+                                                        SD_JSON_BUILD_OBJECT(
+                                                                        SD_JSON_BUILD_PAIR_STRING("provider", report->provider),
+                                                                        JSON_BUILD_PAIR_IOVEC_BASE64("outblob", &report->outblob),
+                                                                        SD_JSON_BUILD_PAIR_CONDITION(iovec_is_set(&report->auxblob),
+                                                                                        "auxblob", JSON_BUILD_IOVEC_BASE64(&report->auxblob)),
+                                                                        SD_JSON_BUILD_PAIR_CONDITION(iovec_is_set(&report->manifestblob),
+                                                                                        "manifestblob", JSON_BUILD_IOVEC_BASE64(&report->manifestblob))))));
+}
+
+static int vl_server(void) {
+        _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *vs = NULL;
+        int r;
+
+        r = varlink_server_new(&vs, SD_VARLINK_SERVER_ROOT_ONLY|SD_VARLINK_SERVER_MYSELF_ONLY, /* userdata= */ NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate Varlink server: %m");
+
+        r = sd_varlink_server_add_interface(vs, &vl_interface_io_systemd_Report_Signer);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add Varlink interface: %m");
+
+        r = sd_varlink_server_bind_method(vs, "io.systemd.Report.Signer.Sign", vl_method_sign);
+        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("Get an attestation report via configfs Trusted Security Module (TSM) "
+                      "that includes the hash of the system report.");
+        help_section("Options");
+
+        r = table_print_or_warn(options);
+        if (r < 0)
+                return r;
+
+        help_man_page_reference("systemd-report-sign-tsm@.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);
index 47c48bcabcc101cd89ab87728d72bea7f887bd86..ea59dac424033469d43a3d39a92158a63777fccf 100644 (file)
@@ -747,6 +747,8 @@ units = [
           'file' : 'systemd-report-sign-plain@.service.in',
           'conditions' : ['HAVE_OPENSSL'],
         },
+        { 'file' : 'systemd-report-sign-tsm.socket' },
+        { 'file' : 'systemd-report-sign-tsm@.service.in' },
         {
           'file' : 'systemd-resolved.service.in',
           'conditions' : ['ENABLE_RESOLVE'],
diff --git a/units/systemd-report-sign-tsm.socket b/units/systemd-report-sign-tsm.socket
new file mode 100644 (file)
index 0000000..1756dde
--- /dev/null
@@ -0,0 +1,27 @@
+# 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 (configfs-tsm)
+Documentation=man:systemd-report-sign-tsm@.service(8)
+DefaultDependencies=no
+Before=sockets.target shutdown.target
+Conflicts=shutdown.target
+ConditionSecurity=cvm
+
+[Socket]
+ListenStream=/run/systemd/report.sign/tsm
+FileDescriptorName=varlink
+SocketMode=0600
+Accept=yes
+MaxConnectionsPerSource=16
+RemoveOnStop=yes
+
+[Install]
+WantedBy=sockets.target
diff --git a/units/systemd-report-sign-tsm@.service.in b/units/systemd-report-sign-tsm@.service.in
new file mode 100644 (file)
index 0000000..6e2f480
--- /dev/null
@@ -0,0 +1,46 @@
+# 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 (configfs-tsm)
+Documentation=man:systemd-report-sign-tsm@.service(8)
+DefaultDependencies=no
+Before=shutdown.target
+Conflicts=shutdown.target
+Wants=sys-kernel-config.mount
+After=sys-kernel-config.mount
+
+[Service]
+ExecStart={{LIBEXECDIR}}/systemd-report-sign-tsm
+CapabilityBoundingSet=
+DeviceAllow=
+IPAddressDeny=any
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
+PrivateDevices=yes
+PrivateIPC=yes
+PrivateNetwork=yes
+PrivateTmp=disconnected
+ProtectControlGroups=yes
+ProtectHome=yes
+ProtectHostname=yes
+ProtectKernelLogs=yes
+ProtectKernelModules=yes
+ProtectKernelTunables=yes
+ProtectSystem=strict
+ReadWritePaths=-/sys/kernel/config/tsm/report
+RestrictAddressFamilies=AF_UNIX
+RestrictNamespaces=yes
+RestrictRealtime=yes
+RestrictSUIDSGID=yes
+RuntimeMaxSec=5min
+SystemCallArchitectures=native
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service