From: Lennart Poettering Date: Mon, 2 Jun 2025 09:20:52 +0000 (+0200) Subject: pcrlock: add "is-supported" verb that checks if the local TPM supports the commands... X-Git-Tag: v258-rc1~408^2~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ccd2bf48b2d736e335f217d5621a817aa1b6beeb;p=thirdparty%2Fsystemd.git pcrlock: add "is-supported" verb that checks if the local TPM supports the commands we need for pcrlock 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 --- diff --git a/man/systemd-pcrlock.xml b/man/systemd-pcrlock.xml index 89b989ea2da..de77e7da141 100644 --- a/man/systemd-pcrlock.xml +++ b/man/systemd-pcrlock.xml @@ -183,6 +183,28 @@ + + is-supported + + Checks if the local TPM2 supports all functionality for + systemd-pcrlock to work correctly. This does similar tests as + systemd-analyze has-tpm2, but also checks for supported of the TPM2 operations + requires by systemd-pcrlock. Outputs one of no, + partial (in case some parts of TPM2 support are avaialable in hardware, firmware, + OS, but not complete), obsolete (if TPM2 support is available in hardware, + firmware and OS, but the operations required for systemd-pcrlock are missing), + yes. Returns an exit status of zero if full support is available, otherwise + non-zero. + + If combined with , suppresses the output of the string. + + Currently, this checks for support for the PolicAuthorizeNV TPM2 command, as well as for + support of the SHA-256 hash algorithm. + + + + + lock-firmware-code unlock-firmware-code @@ -562,6 +584,15 @@ + + + + If specified suppresses output when invoked for + is-supported. + + + + diff --git a/src/pcrlock/pcrlock.c b/src/pcrlock/pcrlock.c index 02860e6ca58..d3b72cc6649 100644 --- a/src/pcrlock/pcrlock.c +++ b/src/pcrlock/pcrlock.c @@ -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 }, {} }; diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 4da7e098647..d81c362c445 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -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; diff --git a/test/units/TEST-70-TPM2.pcrlock.sh b/test/units/TEST-70-TPM2.pcrlock.sh index 38274991db7..38e082bb886 100755 --- a/test/units/TEST-70-TPM2.pcrlock.sh +++ b/test/units/TEST-70-TPM2.pcrlock.sh @@ -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"