]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
pcrlock: add "is-supported" verb that checks if the local TPM supports the commands...
authorLennart Poettering <lennart@poettering.net>
Mon, 2 Jun 2025 09:20:52 +0000 (11:20 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 3 Jun 2025 14:12:45 +0000 (16:12 +0200)
systemd-pcrlock requires support for the PolicyAuthorizeNV command,
which is not implemented in the first TPM2 releases. We also strictly
require SHA-256 support. Hence add a tool for checking for both of
these.

This is a tighter version of "systemd-analyze has-tpm2", that checks for
the precise feature that systemd-pcrlock needs, on top of basic TPM2
functionality.

Fixes: #37607
man/systemd-pcrlock.xml
src/pcrlock/pcrlock.c
src/shared/tpm2-util.h
test/units/TEST-70-TPM2.pcrlock.sh

index 89b989ea2dac857426358ac4462e4c55992d9447..de77e7da1414b71ace8793f56cfc9689b2538fc2 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><command>is-supported</command></term>
+
+        <listitem><para>Checks if the local TPM2 supports all functionality for
+        <command>systemd-pcrlock</command> to work correctly. This does similar tests as
+        <command>systemd-analyze has-tpm2</command>, but also checks for supported of the TPM2 operations
+        requires by <command>systemd-pcrlock</command>. Outputs one of <literal>no</literal>,
+        <literal>partial</literal> (in case some parts of TPM2 support are avaialable in hardware, firmware,
+        OS, but not complete), <literal>obsolete</literal> (if TPM2 support is available in hardware,
+        firmware and OS, but the operations required for <command>systemd-pcrlock</command> are missing),
+        <literal>yes</literal>. Returns an exit status of zero if full support is available, otherwise
+        non-zero.</para>
+
+        <para>If combined with <option>--quiet</option>, suppresses the output of the string.</para>
+
+        <para>Currently, this checks for support for the PolicAuthorizeNV TPM2 command, as well as for
+        support of the SHA-256 hash algorithm.</para>
+
+        <xi:include href="version-info.xml" xpointer="v258"/>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><command>lock-firmware-code</command></term>
         <term><command>unlock-firmware-code</command></term>
         <xi:include href="version-info.xml" xpointer="v256"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--quiet</option></term>
+
+        <listitem><para>If specified suppresses output when invoked for
+        <command>is-supported</command>.</para>
+
+        <xi:include href="version-info.xml" xpointer="v258"/></listitem>
+      </varlistentry>
+
       <xi:include href="standard-options.xml" xpointer="json" />
       <xi:include href="standard-options.xml" xpointer="no-pager" />
       <xi:include href="standard-options.xml" xpointer="help" />
index 02860e6ca585fe39601ddc3a44b897667fc13c65..d3b72cc6649b023ff5458c66ea18cc8c7599fbcf 100644 (file)
@@ -85,6 +85,7 @@ static bool arg_force = false;
 static BootEntryTokenType arg_entry_token_type = BOOT_ENTRY_TOKEN_AUTO;
 static char *arg_entry_token = NULL;
 static bool arg_varlink = false;
+static bool arg_quiet = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_components, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_pcrlock_path, freep);
@@ -4938,6 +4939,60 @@ static int verb_remove_policy(int argc, char *argv[], void *userdata) {
         return remove_policy();
 }
 
+static int test_tpm2_support_pcrlock(Tpm2Support *ret) {
+        int r;
+
+        assert(ret);
+
+        /* First check basic support */
+        Tpm2Support s = tpm2_support();
+
+        /* If basic support is available, let's also check the things we need for systemd-pcrlock */
+        if (s == TPM2_SUPPORT_FULL) {
+                _cleanup_(tpm2_context_unrefp) Tpm2Context *tc = NULL;
+                r = tpm2_context_new_or_warn(/* device= */ NULL, &tc);
+                if (r < 0)
+                        return r;
+
+                /* We strictly need TPM2_CC_PolicyAuthorizeNV for systemd-pcrlock to work */
+                SET_FLAG(s, TPM2_SUPPORT_AUTHORIZE_NV, tpm2_supports_command(tc, TPM2_CC_PolicyAuthorizeNV));
+
+                log_debug("PolicyAuthorizeNV supported: %s", yes_no(FLAGS_SET(s, TPM2_SUPPORT_AUTHORIZE_NV)));
+
+                /* We also strictly need SHA-256 to work */
+                SET_FLAG(s, TPM2_SUPPORT_SHA256, tpm2_supports_alg(tc, TPM2_ALG_SHA256));
+
+                log_debug("SHA-256 supported: %s", yes_no(FLAGS_SET(s, TPM2_SUPPORT_SHA256)));
+        }
+
+        *ret = s;
+        return 0;
+}
+
+static int verb_is_supported(int argc, char *argv[], void *userdata) {
+        int r;
+
+        Tpm2Support s;
+        r = test_tpm2_support_pcrlock(&s);
+        if (r < 0)
+                return r;
+
+        if (!arg_quiet) {
+                if (s == (TPM2_SUPPORT_FULL|TPM2_SUPPORT_API_PCRLOCK))
+                        printf("%syes%s\n", ansi_green(), ansi_normal());
+                else if (FLAGS_SET(s, TPM2_SUPPORT_FULL))
+                        printf("%sobsolete%s\n", ansi_red(), ansi_normal());
+                else if (s == TPM2_SUPPORT_NONE)
+                        printf("%sno%s\n", ansi_red(), ansi_normal());
+                else
+                        printf("%spartial%s\n", ansi_yellow(), ansi_normal());
+        }
+
+        assert_cc(TPM2_SUPPORT_API_PCRLOCK <= 255); /* make sure this is safe to use as process exit status */
+
+        return ~s & TPM2_SUPPORT_API_PCRLOCK;
+}
+
 static int help(int argc, char *argv[], void *userdata) {
         _cleanup_free_ char *link = NULL;
         int r;
@@ -4955,6 +5010,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "  predict                     Predict PCR values\n"
                "  make-policy                 Predict PCR values and generate TPM2 policy from it\n"
                "  remove-policy               Remove TPM2 policy\n"
+               "  is-supported                Tests if TPM2 supports necessary features\n"
                "\n%3$sProtections:%4$s\n"
                "  lock-firmware-code          Generate a .pcrlock file from current firmware code\n"
                "  unlock-firmware-code        Remove .pcrlock file for firmware code\n"
@@ -4997,6 +5053,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --force                  Write policy even if it matches existing policy\n"
                "     --entry-token=machine-id|os-id|os-image-id|auto|literal:…\n"
                "                              Boot entry token to use for this installation\n"
+               "  -q --quiet                  Suppress unnecessary output\n"
                "\nSee the %2$s for details.\n",
                program_invocation_short_name,
                link,
@@ -5040,6 +5097,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "policy",          required_argument, NULL, ARG_POLICY          },
                 { "force",           no_argument,       NULL, ARG_FORCE           },
                 { "entry-token",     required_argument, NULL, ARG_ENTRY_TOKEN     },
+                { "quiet",           no_argument,       NULL, 'q'                 },
                 {}
         };
 
@@ -5049,7 +5107,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "hq", options, NULL)) >= 0)
                 switch (c) {
 
                 case 'h':
@@ -5193,6 +5251,10 @@ static int parse_argv(int argc, char *argv[]) {
                                 return r;
                         break;
 
+                case 'q':
+                        arg_quiet = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -5257,6 +5319,7 @@ static int pcrlock_main(int argc, char *argv[]) {
                 { "unlock-raw",                  VERB_ANY, 1,        0,            verb_unlock_simple               },
                 { "make-policy",                 VERB_ANY, 1,        0,            verb_make_policy                 },
                 { "remove-policy",               VERB_ANY, 1,        0,            verb_remove_policy               },
+                { "is-supported",                VERB_ANY, 1,        0,            verb_is_supported                },
                 {}
         };
 
index 4da7e098647345aa13e63a0613abd2e2a75d5b03..d81c362c4459bfe6ad97d6e92f434d8bac815978 100644 (file)
@@ -450,11 +450,18 @@ typedef enum Tpm2Support {
         TPM2_SUPPORT_LIBRARIES    = 1 << 4,  /* we can dlopen the tpm2 libraries */
         TPM2_SUPPORT_API          = TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER|TPM2_SUPPORT_SYSTEM|TPM2_SUPPORT_SUBSYSTEM|TPM2_SUPPORT_LIBRARIES,
 
-        /* Flags below are not returned by systemd-analyze has-tpm2 as exit status. */
-        TPM2_SUPPORT_LIBTSS2_ESYS = 1 << 5,  /* we can dlopen libtss2-esys.so.0 */
-        TPM2_SUPPORT_LIBTSS2_RC   = 1 << 6,  /* we can dlopen libtss2-rc.so.0 */
-        TPM2_SUPPORT_LIBTSS2_MU   = 1 << 7,  /* we can dlopen libtss2-mu.so.0 */
+        /* Flags below are used by pcrlock, to indicate hardware specific features. It's not used by systemd-analyze has-tpm2. */
+        TPM2_SUPPORT_AUTHORIZE_NV = 1 << 5,  /* chip supports PolicyAuthorizeNV */
+        TPM2_SUPPORT_SHA256       = 1 << 6,  /* chip supports SHA-256 */
+        TPM2_SUPPORT_API_PCRLOCK  = TPM2_SUPPORT_API | TPM2_SUPPORT_AUTHORIZE_NV | TPM2_SUPPORT_SHA256,
+
+        /* Flags below are not returned by systemd-analyze has-tpm2 nor by systemd-pcrlock as exit status. */
+        TPM2_SUPPORT_LIBTSS2_ESYS = 1 << 7,  /* we can dlopen libtss2-esys.so.0 */
+        TPM2_SUPPORT_LIBTSS2_RC   = 1 << 8,  /* we can dlopen libtss2-rc.so.0 */
+        TPM2_SUPPORT_LIBTSS2_MU   = 1 << 9,  /* we can dlopen libtss2-mu.so.0 */
         TPM2_SUPPORT_LIBTSS2_ALL  = TPM2_SUPPORT_LIBTSS2_ESYS|TPM2_SUPPORT_LIBTSS2_RC|TPM2_SUPPORT_LIBTSS2_MU,
+
+        /* Combined flags for generic (i.e. not tool-specific) support */
         TPM2_SUPPORT_FULL         = TPM2_SUPPORT_API|TPM2_SUPPORT_LIBTSS2_ALL,
 } Tpm2Support;
 
index 38274991db7aa067c4fefa0fb828f1c24cb0f066..38e082bb88617b1df29f0717c59f5fdc80f8ea53 100755 (executable)
@@ -52,6 +52,7 @@ set -e
 test $ret -eq 1
 
 SYSTEMD_COLORS=256 "$SD_PCRLOCK"
+"$SD_PCRLOCK" is-supported
 "$SD_PCRLOCK" cel --no-pager --json=pretty
 "$SD_PCRLOCK" log --pcr="$PCRS"
 "$SD_PCRLOCK" log --json=pretty --pcr="$PCRS"