]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tpm2-setup: add new early boot tool for initializing the SRK
authorLennart Poettering <lennart@poettering.net>
Tue, 26 Sep 2023 19:25:53 +0000 (21:25 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 29 Sep 2023 17:36:04 +0000 (19:36 +0200)
This adds an explicit service for initializing the TPM2 SRK. This is
implicitly also done by systemd-cryptsetup, hence strictly speaking
redundant, but doing this early has the benefit that we can parallelize
this in a nicer way. This also write a copy of the SRK public key in PEM
format to /run/ + /var/lib/, thus pinning the disk image to the TPM.
Making the SRK public key is also useful for allowing easy offline
encryption for a specific TPM.

Sooner or later we should probably grow what this service does, the
above is just the first step. For example, the service should probably
offer the ability to reset the TPM (clear the owner hierarchy?) on a
factory reset, if such a policy is needed. And we might want to install
some default AK (?).

Fixes: #27986
Also see: #22637

12 files changed:
man/rules/meson.build
man/systemd-tpm2-setup.service.xml [new file with mode: 0644]
meson.build
src/shared/generator.c
src/shared/tpm2-util.c
src/shared/tpm2-util.h
src/tpm2-setup/meson.build [new file with mode: 0644]
src/tpm2-setup/tpm2-setup.c [new file with mode: 0644]
test/units/testsuite-70.sh
units/meson.build
units/systemd-tpm2-setup-early.service.in [new file with mode: 0644]
units/systemd-tpm2-setup.service.in [new file with mode: 0644]

index 9a383b985ef45ff61c365567ecf04199f356fbca..3454668cab9456fd737bbeb09f497be0c20f3040 100644 (file)
@@ -1088,6 +1088,10 @@ manpages = [
    'systemd-tmpfiles-setup-dev.service',
    'systemd-tmpfiles-setup.service'],
   ''],
+ ['systemd-tpm2-setup.service',
+  '8',
+  ['systemd-tpm2-setup', 'systemd-tpm2-setup-early.service'],
+  'ENABLE_BOOTLOADER'],
  ['systemd-tty-ask-password-agent', '1', [], ''],
  ['systemd-udev-settle.service', '8', [], ''],
  ['systemd-udevd.service',
diff --git a/man/systemd-tpm2-setup.service.xml b/man/systemd-tpm2-setup.service.xml
new file mode 100644 (file)
index 0000000..77502d5
--- /dev/null
@@ -0,0 +1,74 @@
+<?xml version="1.0"?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+<refentry id="systemd-tpm2-setup.service" conditional='ENABLE_BOOTLOADER'
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>systemd-tpm2-setup.service</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-tpm2-setup.service</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-tpm2-setup.service</refname>
+    <refname>systemd-tpm2-setup-early.service</refname>
+    <refname>systemd-tpm2-setup</refname>
+    <refpurpose>Set up the TPM2 Storage Root Key (SRK) at boot</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><filename>systemd-tpm2-setup.service</filename></para>
+    <para><filename>/usr/lib/systemd/systemd-tpm2-setup</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><filename>systemd-tpm2-setup.service</filename> and
+    <filename>systemd-tpm2-setup-early.service</filename> are services that generate the Storage Root Key
+    (SRK) if it hasn't been generated yet, and stores it in the TPM.</para>
+
+    <para>The services will store the public key of the SRK key pair in a PEM file in
+    <filename>/run/systemd/tpm2-srk-public-key.pem</filename> and
+    <filename>/var/lib/systemd/tpm2-srk-public-key.pem</filename>.</para>
+
+    <para><filename>systemd-tpm2-setup-early.service</filename> runs very early at boot (possibly in the
+    initrd), and writes the SRK public key to <filename>/run/systemd/tpm2-srk-public-key.pem</filename> (as
+    <filename>/var/</filename> is generally not accessible this early yet), while
+    <filename>systemd-tpm2-setup.service</filename> runs during a later boot phase and saves the public key
+    to <filename>/var/lib/systemd/tpm2-srk-public-key.pem</filename>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Files</title>
+
+    <variablelist>
+      <varlistentry>
+        <term><filename>/run/systemd/tpm2-srk-public-key.pem</filename></term>
+
+        <listitem><para>The SRK public key in PEM format, written during early boot.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><filename>/var/lib/systemd/tpm2-srk-public-key.pem</filename></term>
+
+        <listitem><para>The SRK public key in PEM format, written during later boot (once
+        <filename>/var/</filename> is available).</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+</refentry>
index 90705b31583a79ea3c73169be03f1d3fd73297be..44eac94fdf98cf04a924ee2bc5def72c43f5e16b 100644 (file)
@@ -2186,6 +2186,7 @@ subdir('src/sysusers')
 subdir('src/sysv-generator')
 subdir('src/timedate')
 subdir('src/timesync')
+subdir('src/tpm2-setup')
 subdir('src/tmpfiles')
 subdir('src/tty-ask-password-agent')
 subdir('src/update-done')
index aced4949a9a39049ba3c7377c0ed5439cafcae52..562658726912201d7148d53e4769bce9941c7626 100644 (file)
@@ -741,7 +741,7 @@ int generator_write_cryptsetup_unit_section(
         fprintf(f,
                 "\n"
                 "DefaultDependencies=no\n"
-                "After=cryptsetup-pre.target systemd-udevd-kernel.socket\n"
+                "After=cryptsetup-pre.target systemd-udevd-kernel.socket systemd-tpm2-setup-early.service\n"
                 "Before=blockdev@dev-mapper-%%i.target\n"
                 "Wants=blockdev@dev-mapper-%%i.target\n"
                 "IgnoreOnIsolate=true\n");
index 64dcc5503bab6caae9ae3ec7d6e2bd7e7ea92005..3374bd4f7833ce7e82226f93c95cf1c66d1647ac 100644 (file)
@@ -148,7 +148,7 @@ int dlopen_tpm2(void) {
                         DLSYM_ARG(Tss2_MU_TPMT_PUBLIC_Marshal));
 }
 
-static void Esys_Freep(void *p) {
+void Esys_Freep(void *p) {
         if (*(void**) p)
                 sym_Esys_Free(*(void**) p);
 }
@@ -1175,7 +1175,7 @@ static int tpm2_get_srk(
 }
 
 /* Get the SRK, creating one if needed. Returns 0 on success, or < 0 on error. */
-static int tpm2_get_or_create_srk(
+int tpm2_get_or_create_srk(
                 Tpm2Context *c,
                 const Tpm2Handle *session,
                 TPM2B_PUBLIC **ret_public,
@@ -1189,7 +1189,7 @@ static int tpm2_get_or_create_srk(
         if (r < 0)
                 return r;
         if (r == 1)
-                return 0;
+                return 0; /* 0 → SRK already set up */
 
         /* No SRK, create and persist one */
         TPM2B_PUBLIC template = { .size = sizeof(TPMT_PUBLIC), };
@@ -1223,7 +1223,7 @@ static int tpm2_get_or_create_srk(
                 /* This should never happen. */
                 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "SRK we just persisted couldn't be found.");
 
-        return 0;
+        return 1; /* > 0 → SRK newly set up */
 }
 
 /* Utility functions for TPMS_PCR_SELECTION. */
index 72533722931282cb37af4fc02a084569f7c4ced9..8df41f001d7ebb10a12e23989c847de1fdf33998 100644 (file)
@@ -67,6 +67,8 @@ typedef struct {
 #define _tpm2_handle(c, h) { .tpm2_context = (c), .esys_handle = (h), }
 static const Tpm2Handle TPM2_HANDLE_NONE = _tpm2_handle(NULL, ESYS_TR_NONE);
 
+void Esys_Freep(void *p);
+
 int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle);
 Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle);
 DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free);
@@ -188,6 +190,8 @@ int tpm2_calculate_sealing_policy(const Tpm2PCRValue *pcr_values, size_t n_pcr_v
 int tpm2_marshal_blob(const TPM2B_PUBLIC *public, const TPM2B_PRIVATE *private, void **ret_blob, size_t *ret_blob_size);
 int tpm2_unmarshal_blob(const void *blob, size_t blob_size, TPM2B_PUBLIC *ret_public, TPM2B_PRIVATE *ret_private);
 
+int tpm2_get_or_create_srk(Tpm2Context *c, const Tpm2Handle *session, TPM2B_PUBLIC **ret_public, TPM2B_NAME **ret_name, TPM2B_NAME **ret_qname, Tpm2Handle **ret_handle);
+
 int tpm2_seal(Tpm2Context *c, const TPM2B_DIGEST *policy, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size);
 int tpm2_unseal(const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size);
 
diff --git a/src/tpm2-setup/meson.build b/src/tpm2-setup/meson.build
new file mode 100644 (file)
index 0000000..c85721c
--- /dev/null
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+executables += [
+        libexec_template + {
+                'name' : 'systemd-tpm2-setup',
+                'sources' : files('tpm2-setup.c'),
+                'conditions' : [
+                        'ENABLE_BOOTLOADER',
+                        'HAVE_OPENSSL',
+                        'HAVE_TPM2',
+                ],
+                'dependencies' : [
+                        libopenssl,
+                ],
+        },
+]
diff --git a/src/tpm2-setup/tpm2-setup.c b/src/tpm2-setup/tpm2-setup.c
new file mode 100644 (file)
index 0000000..b88e7ec
--- /dev/null
@@ -0,0 +1,327 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <getopt.h>
+#include <unistd.h>
+
+#include "build.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "hexdecoct.h"
+#include "main-func.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "pretty-print.h"
+#include "terminal-util.h"
+#include "tmpfile-util.h"
+#include "tpm2-util.h"
+
+static char *arg_tpm2_device = NULL;
+static bool arg_early = false;
+
+STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
+
+#define TPM2_SRK_PEM_PERSISTENT_PATH "/var/lib/systemd/tpm2-srk-public-key.pem"
+#define TPM2_SRK_PEM_RUNTIME_PATH "/run/systemd/tpm2-srk-public-key.pem"
+
+static int help(int argc, char *argv[], void *userdata) {
+        _cleanup_free_ char *link = NULL;
+        int r;
+
+        r = terminal_urlify_man("systemd-tpm2-setup", "8", &link);
+        if (r < 0)
+                return log_oom();
+
+        printf("%1$s [OPTIONS...] COMMAND\n"
+               "\n%5$sSet up the TPM2 Storage Root Key (SRK).%6$s\n"
+               "\n%3$sOptions:%4$s\n"
+               "  -h --help               Show this help\n"
+               "     --version            Show package version\n"
+               "     --tpm2-device=PATH\n"
+               "                          Pick TPM2 device\n"
+               "     --early=BOOL         Store SRK public key in /run/ rather than /var/lib/\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_TPM2_DEVICE,
+                ARG_EARLY,
+        };
+
+        static const struct option options[] = {
+                { "help",        no_argument,       NULL, 'h'             },
+                { "version",     no_argument,       NULL, ARG_VERSION     },
+                { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
+                { "early",       required_argument, NULL, ARG_EARLY       },
+        };
+
+        int c, r;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+                switch (c) {
+
+                case 'h':
+                        return help(0, NULL, NULL);
+
+                case ARG_VERSION:
+                        return version();
+
+                case ARG_TPM2_DEVICE:
+                        if (streq(optarg, "list"))
+                                return tpm2_list_devices();
+
+                        if (free_and_strdup(&arg_tpm2_device, streq(optarg, "auto") ? NULL : optarg) < 0)
+                                return log_oom();
+
+                        break;
+
+                case ARG_EARLY:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --early= argument: %s", optarg);
+
+                        arg_early = r;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached();
+                }
+
+        if (optind != argc)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program expects no argument.");
+
+        return 1;
+}
+
+struct public_key_data {
+        EVP_PKEY *pkey;
+        void *fingerprint;
+        size_t fingerprint_size;
+        char *fingerprint_hex;
+        char *path;
+};
+
+static void public_key_data_done(struct public_key_data *d) {
+        assert(d);
+
+        if (d->pkey) {
+                EVP_PKEY_free(d->pkey);
+                d->pkey = NULL;
+        }
+        d->fingerprint = mfree(d->fingerprint);
+        d->fingerprint_size = 0;
+        d->fingerprint_hex = mfree(d->fingerprint_hex);
+        d->path = mfree(d->path);
+}
+
+static int public_key_make_fingerprint(struct public_key_data *d) {
+        int r;
+
+        assert(d);
+        assert(d->pkey);
+        assert(!d->fingerprint);
+        assert(!d->fingerprint_hex);
+
+        r = pubkey_fingerprint(d->pkey, EVP_sha256(), &d->fingerprint, &d->fingerprint_size);
+        if (r < 0)
+                return log_error_errno(r, "Failed to calculate fingerprint of public key: %m");
+
+        d->fingerprint_hex = hexmem(d->fingerprint, d->fingerprint_size);
+        if (!d->fingerprint_hex)
+                return log_oom();
+
+        return 0;
+}
+
+static int load_public_key_disk(const char *path, struct public_key_data *ret) {
+        _cleanup_(public_key_data_done) struct public_key_data data = {};
+        _cleanup_free_ char *blob = NULL;
+        size_t blob_size;
+        int r;
+
+        assert(path);
+        assert(ret);
+
+        r = read_full_file(path, &blob, &blob_size);
+        if (r < 0) {
+                if (r != -ENOENT)
+                        return log_error_errno(r, "Failed to read '%s': %m", path);
+
+                log_debug("SRK public key file '%s' does not exist.", path);
+        } else {
+                log_debug("Loaded SRK public key from '%s'.", path);
+
+                r = openssl_pkey_from_pem(blob, blob_size, &data.pkey);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse SRK public key file '%s': %m", path);
+
+                r = public_key_make_fingerprint(&data);
+                if (r < 0)
+                        return r;
+
+                log_debug("Loaded SRK public key fingerprint: %s", data.fingerprint_hex);
+        }
+
+        data.path = strdup(path);
+        if (!data.path)
+                return log_oom();
+
+        *ret = data;
+        data = (struct public_key_data) {};
+
+        return 0;
+}
+
+static int load_public_key_tpm2(struct public_key_data *ret) {
+        _cleanup_(public_key_data_done) struct public_key_data data = {};
+        _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
+        _cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL;
+        int r;
+
+        assert(ret);
+
+        r = tpm2_context_new(arg_tpm2_device, &c);
+        if (r < 0)
+                return r;
+
+        r = tpm2_get_or_create_srk(
+                        c,
+                        /* session= */ NULL,
+                        &public,
+                        /* ret_name= */ NULL,
+                        /* ret_qname= */ NULL,
+                        NULL);
+        if (r < 0)
+                return r;
+        if (r > 0)
+                log_info("New SRK generated and stored in the TPM.");
+        else
+                log_info("SRK already stored in the TPM.");
+
+        r = tpm2_tpm2b_public_to_openssl_pkey(public, &data.pkey);
+        if (r < 0)
+                return log_error_errno(r, "Failed to convert TPM2 SRK public key to OpenSSL public key: %m");
+
+        r = public_key_make_fingerprint(&data);
+        if (r < 0)
+                return r;
+
+        log_info("SRK fingerprint is %s.", data.fingerprint_hex);
+
+        *ret = data;
+        data = (struct public_key_data) {};
+
+        return 0;
+}
+
+static int run(int argc, char *argv[]) {
+        int r;
+
+        log_setup();
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r;
+
+        umask(0022);
+
+        _cleanup_(public_key_data_done) struct public_key_data runtime_key = {}, persistent_key = {}, tpm2_key = {};
+
+        r = load_public_key_disk(TPM2_SRK_PEM_RUNTIME_PATH, &runtime_key);
+        if (r < 0)
+                return r;
+
+        if (!arg_early) {
+                r = load_public_key_disk(TPM2_SRK_PEM_PERSISTENT_PATH, &persistent_key);
+                if (r < 0)
+                        return r;
+
+                if (runtime_key.pkey && persistent_key.pkey &&
+                    memcmp_nn(runtime_key.fingerprint, runtime_key.fingerprint_size,
+                              persistent_key.fingerprint, persistent_key.fingerprint_size) != 0) {
+
+                        /* One of those days we might want to add a stricter policy option here, that refuses
+                         * to boot when the SRK changes. For now, let's just warn and proceed, in order not
+                         * to break OS images that are moved around PCs. */
+
+                        log_notice("Saved persistent SRK (%s) and runtime SRK differ (fingerprint %s vs. %s), updating persistent SRK.",
+                                   persistent_key.path, persistent_key.fingerprint_hex, runtime_key.fingerprint_hex);
+
+                        public_key_data_done(&persistent_key);
+                }
+        }
+
+        r = load_public_key_tpm2(&tpm2_key);
+        if (r < 0)
+                return r;
+
+        assert(tpm2_key.pkey);
+
+        if (runtime_key.pkey) {
+                if (memcmp_nn(tpm2_key.fingerprint, tpm2_key.fingerprint_size,
+                             runtime_key.fingerprint, runtime_key.fingerprint_size) != 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Saved runtime SRK differs from TPM SRK, refusing.");
+
+                if (arg_early) {
+                        log_info("SRK saved in '%s' matches SRK in TPM2.", runtime_key.path);
+                        return 0;
+                }
+        }
+
+        if (persistent_key.pkey) {
+                if (memcmp_nn(tpm2_key.fingerprint, tpm2_key.fingerprint_size,
+                              persistent_key.fingerprint, persistent_key.fingerprint_size) == 0) {
+                        log_info("SRK saved in '%s' matches SRK in TPM2.", persistent_key.path);
+                        return 0;
+                }
+
+                /* As above, we probably want a stricter policy option here, one day. */
+
+                log_notice("Saved persistent SRK (%s) and TPM SRK differ (fingerprint %s vs. %s), updating persistent SRK.",
+                           persistent_key.path, persistent_key.fingerprint_hex, tpm2_key.fingerprint_hex);
+
+                public_key_data_done(&persistent_key);
+        }
+
+        const char *path = arg_early ? TPM2_SRK_PEM_RUNTIME_PATH : TPM2_SRK_PEM_PERSISTENT_PATH;
+
+        (void) mkdir_parents(path, 0755);
+
+        /* Write out public key (note that we only do that as a help to the user, we don't make use of this ever */
+        _cleanup_(unlink_and_freep) char *t = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        r = fopen_tmpfile_linkable(path, O_WRONLY, &t, &f);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open SRK public key file '%s' for writing: %m", path);
+
+        if (PEM_write_PUBKEY(f, tpm2_key.pkey) <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write SRK public key file '%s'.", path);
+
+        if (fchmod(fileno(f), 0444) < 0)
+                return log_error_errno(errno, "Failed to adjust access mode of SRK public key file '%s' to 0444: %m", path);
+
+        r = flink_tmpfile(f, t, path, LINK_TMPFILE_SYNC|LINK_TMPFILE_REPLACE);
+        if (r < 0)
+                return log_error_errno(r, "Failed to move SRK public key file to '%s': %m", path);
+
+        log_info("SRK public key saved to '%s'.", path);
+        return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
index 7f7183da08b50aa655dbc4dd371865c169b54a14..8358c48434acdd0823076352107fe4625476974b 100755 (executable)
@@ -5,6 +5,7 @@ set -o pipefail
 
 SD_MEASURE="/usr/lib/systemd/systemd-measure"
 SD_PCREXTEND="/usr/lib/systemd/systemd-pcrextend"
+SD_TPM2SETUP="/usr/lib/systemd/systemd-tpm2-setup"
 export SYSTEMD_LOG_LEVEL=debug
 
 cryptsetup_has_token_plugin_support() {
@@ -372,4 +373,12 @@ systemd-cryptenroll --tpm2-pcrs=boot-loader-code+boot-loader-config "$img"
 (! systemd-cryptenroll --wipe-slot=10240000 "$img")
 (! systemd-cryptenroll --fido2-device=auto --unlock-fido2-device=auto "$img")
 
+# Run this, just to get sanitizer coverage. The tools should be idempotent, hence run the multiple times.
+if [[ -x "$SD_TPM2SETUP" ]]; then
+    "$SD_TPM2SETUP" --early=yes
+    "$SD_TPM2SETUP" --early=yes
+    "$SD_TPM2SETUP" --early=no
+    "$SD_TPM2SETUP" --early=no
+fi
+
 touch /testok
index 2fb87934b811f3b4a431c6a5eaa1573cd8e8334c..e1de2b4a6da0f77b373dabd137e43d82a65d2345 100644 (file)
@@ -458,6 +458,16 @@ units = [
           'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
           'symlinks' : ['sysinit.target.wants/'],
         },
+        {
+          'file' : 'systemd-tpm2-setup.service.in',
+          'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
+          'symlinks' : ['sysinit.target.wants/'],
+        },
+        {
+          'file' : 'systemd-tpm2-setup-early.service.in',
+          'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
+          'symlinks' : ['sysinit.target.wants/'],
+        },
         {
           'file' : 'systemd-portabled.service.in',
           'conditions' : ['ENABLE_PORTABLED'],
diff --git a/units/systemd-tpm2-setup-early.service.in b/units/systemd-tpm2-setup-early.service.in
new file mode 100644 (file)
index 0000000..c1597ea
--- /dev/null
@@ -0,0 +1,22 @@
+#  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=TPM2 SRK Setup (Early)
+Documentation=man:systemd-tpm2-setup.service(8)
+DefaultDependencies=no
+Conflicts=shutdown.target
+Before=sysinit.target shutdown.target
+ConditionSecurity=measured-uki
+ConditionPathExists=!/run/systemd/tpm2-srk-public-key.pem
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart={{LIBEXECDIR}}/systemd-tpm2-setup --early=yes
diff --git a/units/systemd-tpm2-setup.service.in b/units/systemd-tpm2-setup.service.in
new file mode 100644 (file)
index 0000000..6c99f3a
--- /dev/null
@@ -0,0 +1,24 @@
+#  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=TPM2 SRK Setup
+Documentation=man:systemd-tpm2-setup.service(8)
+DefaultDependencies=no
+Conflicts=shutdown.target
+After=systemd-tpm2-setup-early.service systemd-remount-fs.service
+Before=sysinit.target shutdown.target
+RequiresMountsFor=/var/lib/systemd/tpm2-srk-public-key.pem
+ConditionSecurity=measured-uki
+ConditionPathExists=!/etc/initrd-release
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart={{LIBEXECDIR}}/systemd-tpm2-setup