]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
analyze: add new "pcrs" verb
authorLennart Poettering <lennart@poettering.net>
Tue, 27 Jun 2023 16:45:34 +0000 (18:45 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 28 Jun 2023 14:50:50 +0000 (16:50 +0200)
It shows the PCRs we know about along with their name strings and
current values.

man/systemd-analyze.xml
src/analyze/analyze-pcrs.c [new file with mode: 0644]
src/analyze/analyze-pcrs.h [new file with mode: 0644]
src/analyze/analyze.c
src/analyze/meson.build
src/shared/tpm2-util.c
src/shared/tpm2-util.h
test/units/testsuite-65.sh

index 4aa73d91f7464d4b9ac6d4520010b02e7cca0ff3..145d3f6188095c80815446e1e65bc16722465e43 100644 (file)
       <arg choice="plain">image-policy</arg>
       <arg choice="plain" rep="repeat"><replaceable>POLICY</replaceable></arg>
     </cmdsynopsis>
+    <cmdsynopsis>
+      <command>systemd-analyze</command>
+      <arg choice="opt" rep="repeat">OPTIONS</arg>
+      <arg choice="plain">pcrs</arg>
+      <arg choice="opt" rep="repeat"><replaceable>PCR</replaceable></arg>
+    </cmdsynopsis>
   </refsynopsisdiv>
 
   <refsect1>
@@ -883,6 +889,43 @@ var             ignore      -         -
 default         ignore      -         -</programlisting>
       </example>
     </refsect2>
+
+    <refsect2>
+      <title><command>systemd-analyze pcrs <optional><replaceable>PCR</replaceable>…</optional></command></title>
+
+      <para>This command shows the known TPM2 PCRs along with their identifying names and current values.</para>
+
+      <example>
+        <title>Example Output</title>
+
+        <programlisting>$ systemd-analyze pcrs
+NR NAME                SHA256
+ 0 platform-code       bcd2eb527108bbb1f5528409bcbe310aa9b74f687854cc5857605993f3d9eb11
+ 1 platform-config     b60622856eb7ce52637b80f30a520e6e87c347daa679f3335f4f1a600681bb01
+ 2 external-code       1471262403e9a62f9c392941300b4807fbdb6f0bfdd50abfab752732087017dd
+ 3 external-config     3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969
+ 4 boot-loader-code    939f7fa1458e1f7ce968874d908e524fc0debf890383d355e4ce347b7b78a95c
+ 5 boot-loader-config  864c61c5ea5ecbdb6951e6cb6d9c1f4b4eac79772f7fe13b8bece569d83d3768
+ 6 -                   3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969
+ 7 secure-boot-policy  9c905bd9b9891bfb889b90a54c4b537b889cfa817c4389cc25754823a9443255
+ 8 -                   0000000000000000000000000000000000000000000000000000000000000000
+ 9 kernel-initrd       9caa29b128113ef42aa53d421f03437be57211e5ebafc0fa8b5d4514ee37ff0c
+10 ima                 5ea9e3dab53eb6b483b6ec9e3b2c712bea66bca1b155637841216e0094387400
+11 kernel-boot         0000000000000000000000000000000000000000000000000000000000000000
+12 kernel-config       627ffa4b405e911902fe1f1a8b0164693b31acab04f805f15bccfe2209c7eace
+13 sysexts             0000000000000000000000000000000000000000000000000000000000000000
+14 shim-policy         0000000000000000000000000000000000000000000000000000000000000000
+15 system-identity     0000000000000000000000000000000000000000000000000000000000000000
+16 debug               0000000000000000000000000000000000000000000000000000000000000000
+17 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+18 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+19 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+20 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+21 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+22 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+23 application-support 0000000000000000000000000000000000000000000000000000000000000000</programlisting>
+      </example>
+    </refsect2>
   </refsect1>
 
   <refsect1>
diff --git a/src/analyze/analyze-pcrs.c b/src/analyze/analyze-pcrs.c
new file mode 100644 (file)
index 0000000..b9a6ac1
--- /dev/null
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "analyze.h"
+#include "analyze-pcrs.h"
+#include "fileio.h"
+#include "format-table.h"
+#include "hexdecoct.h"
+#include "terminal-util.h"
+#include "tpm2-util.h"
+
+static int get_pcr_alg(const char **ret) {
+        assert(ret);
+
+        FOREACH_STRING(alg, "sha256", "sha1") {
+                _cleanup_free_ char *p = NULL;
+
+                if (asprintf(&p, "/sys/class/tpm/tpm0/pcr-%s/0", alg) < 0)
+                        return log_oom();
+
+                if (access(p, F_OK) < 0) {
+                        if (errno != -ENOENT)
+                                return log_error_errno(errno, "Failed to determine whether %s exists: %m", p);
+                } else {
+                        *ret = alg;
+                        return 1;
+                }
+        }
+
+        log_notice("Kernel does not support reading PCR values.");
+        *ret = NULL;
+        return 0;
+}
+
+static int get_current_pcr(const char *alg, uint32_t pcr, void **ret, size_t *ret_size) {
+        _cleanup_free_ char *p = NULL, *s = NULL;
+        _cleanup_free_ void *buf = NULL;
+        size_t ss = 0, bufsize = 0;
+        int r;
+
+        assert(alg);
+        assert(ret);
+        assert(ret_size);
+
+        if (asprintf(&p, "/sys/class/tpm/tpm0/pcr-%s/%" PRIu32, alg, pcr) < 0)
+                return log_oom();
+
+        r = read_virtual_file(p, 4096, &s, &ss);
+        if (r < 0)
+                return log_error_errno(r, "Failed to read '%s': %m", p);
+
+        r = unhexmem(s, ss, &buf, &bufsize);
+        if (r < 0)
+                return log_error_errno(r, "Failed to decode hex PCR data '%s': %m", s);
+
+        *ret = TAKE_PTR(buf);
+        *ret_size = bufsize;
+        return 0;
+}
+
+static int add_pcr_to_table(Table *table, const char *alg, uint32_t pcr) {
+        _cleanup_free_ char *h = NULL;
+        const char *color = NULL;
+        int r;
+
+        if (alg) {
+                _cleanup_free_ void *buf = NULL;
+                size_t bufsize = 0;
+
+                r = get_current_pcr(alg, pcr, &buf, &bufsize);
+                if (r < 0)
+                        return r;
+
+                h = hexmem(buf, bufsize);
+                if (!h)
+                        return log_oom();
+
+                /* Grey out PCRs that are not sensibly initialized */
+                if (memeqbyte(0, buf, bufsize) ||
+                    memeqbyte(0xFFU, buf, bufsize))
+                        color = ANSI_GREY;
+        }
+
+        r = table_add_many(table,
+                           TABLE_UINT32, pcr,
+                           TABLE_STRING, pcr_index_to_string(pcr),
+                           TABLE_STRING, h,
+                           TABLE_SET_COLOR, color);
+        if (r < 0)
+                return table_log_add_error(r);
+
+        return 0;
+}
+
+int verb_pcrs(int argc, char *argv[], void *userdata) {
+        _cleanup_(table_unrefp) Table *table = NULL;
+        const char *alg = NULL;
+        int r;
+
+        if (tpm2_support() != TPM2_SUPPORT_FULL)
+                log_notice("System has not TPM2 support, not showing PCR state.");
+        else {
+                r = get_pcr_alg(&alg);
+                if (r < 0)
+                        return r;
+        }
+
+        table = table_new("nr", "name", alg);
+        if (!table)
+                return log_oom();
+
+        (void) table_set_align_percent(table, table_get_cell(table, 0, 0), 100);
+        (void) table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
+
+        if (!alg) /* hide hash column if we couldn't acquire it */
+                (void) table_set_display(table, 0, 1);
+
+        if (strv_isempty(strv_skip(argv, 1)))
+                for (uint32_t pi = 0; pi < _PCR_INDEX_MAX_DEFINED; pi++) {
+                        r = add_pcr_to_table(table, alg, pi);
+                        if (r < 0)
+                                return r;
+                }
+        else {
+                for (int i = 1; i < argc; i++) {
+                        int pi;
+
+                        pi = pcr_index_from_string(argv[i]);
+                        if (pi < 0)
+                                return log_error_errno(pi, "PCR index \"%s\" not known.", argv[i]);
+
+                        r = add_pcr_to_table(table, alg, pi);
+                        if (r < 0)
+                                return r;
+                }
+
+                (void) table_set_sort(table, (size_t) 0);
+        }
+
+        r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, /* show_header= */true);
+        if (r < 0)
+                return log_error_errno(r, "Failed to output table: %m");
+
+        return EXIT_SUCCESS;
+}
diff --git a/src/analyze/analyze-pcrs.h b/src/analyze/analyze-pcrs.h
new file mode 100644 (file)
index 0000000..2a59511
--- /dev/null
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int verb_pcrs(int argc, char *argv[], void *userdata);
index b555c713fc3a007e1361ce99dfba09dc03553acd..eb5fceba8deada246bd5b84068929f0347e0c627 100644 (file)
@@ -28,6 +28,7 @@
 #include "analyze-inspect-elf.h"
 #include "analyze-log-control.h"
 #include "analyze-malloc.h"
+#include "analyze-pcrs.h"
 #include "analyze-plot.h"
 #include "analyze-security.h"
 #include "analyze-service-watchdogs.h"
@@ -234,6 +235,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "  inspect-elf FILE...        Parse and print ELF package metadata\n"
                "  malloc [D-BUS SERVICE...]  Dump malloc stats of a D-Bus service\n"
                "  fdstore SERVICE...         Show file descriptor store contents of service\n"
+               "  pcrs [PCR...]              Show TPM2 PCRs and their names\n"
                "\nOptions:\n"
                "     --recursive-errors=MODE Control which units are verified\n"
                "     --offline=BOOL          Perform a security review on unit file(s)\n"
@@ -545,9 +547,9 @@ static int parse_argv(int argc, char *argv[]) {
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Option --offline= is only supported for security right now.");
 
-        if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf", "plot", "fdstore"))
+        if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf", "plot", "fdstore", "pcrs"))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Option --json= is only supported for security, inspect-elf, plot, and fdstore right now.");
+                                       "Option --json= is only supported for security, inspect-elf, plot, fdstore, pcrs right now.");
 
         if (arg_threshold != 100 && !streq_ptr(argv[optind], "security"))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -636,6 +638,7 @@ static int run(int argc, char *argv[]) {
                 { "malloc",            VERB_ANY, VERB_ANY, 0,            verb_malloc            },
                 { "fdstore",           2,        VERB_ANY, 0,            verb_fdstore           },
                 { "image-policy",      2,        2,        0,            verb_image_policy      },
+                { "pcrs",              VERB_ANY, VERB_ANY, 0,            verb_pcrs              },
                 {}
         };
 
index c50c35f09f6e0907df5e40f4e114753ad10baa0c..911c0bc3ed21cee8d2e41eff31dbb7e61a807aba 100644 (file)
@@ -17,6 +17,7 @@ systemd_analyze_sources = files(
         'analyze-inspect-elf.c',
         'analyze-log-control.c',
         'analyze-malloc.c',
+        'analyze-pcrs.c',
         'analyze-plot.c',
         'analyze-security.c',
         'analyze-service-watchdogs.c',
index 01a7a27ff39139d45df8edb8943e00f391fe9f07..bbff2a934b1155c2e42c83373f3c17802b39c582 100644 (file)
@@ -4046,3 +4046,4 @@ static const char* const pcr_index_table[_PCR_INDEX_MAX_DEFINED] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_FALLBACK(pcr_index, int, TPM2_PCRS_MAX - 1);
+DEFINE_STRING_TABLE_LOOKUP_TO_STRING(pcr_index, int);
index 953e936c75f38b166feca24c68e70b6782fb9ad4..ea17b26b309bb01f27b31e936e6d0daf52331556 100644 (file)
@@ -221,4 +221,5 @@ int tpm2_util_pbkdf2_hmac_sha256(const void *pass,
                     size_t saltlen,
                     uint8_t res[static SHA256_DIGEST_SIZE]);
 
-int pcr_index_from_string(const char *s);
+int pcr_index_from_string(const char *s) _pure_;
+const char *pcr_index_to_string(int pcr) _const_;
index c6a81b4b4eb2c54f514f95ed82033282956ebce3..4c8c4146edbad0e44c54bf4986e9756be1a09164 100755 (executable)
@@ -838,6 +838,11 @@ systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^usr
 
 (! systemd-analyze image-policy 'doedel')
 
+# Output is very hard to predict, but let's run it for coverage anyway
+systemd-analyze pcrs
+systemd-analyze pcrs --json=pretty
+systemd-analyze pcrs 14 7 0 ima
+
 systemd-analyze log-level info
 
 echo OK >/testok