]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
analyze: add new verb for determining NvPCR values
authorLennart Poettering <lennart@poettering.net>
Tue, 4 Jun 2024 09:03:39 +0000 (11:03 +0200)
committerLennart Poettering <lennart@poettering.net>
Sun, 2 Nov 2025 20:14:35 +0000 (21:14 +0100)
man/systemd-analyze.xml
shell-completion/bash/systemd-analyze
src/analyze/analyze-nvpcrs.c [new file with mode: 0644]
src/analyze/analyze-nvpcrs.h [new file with mode: 0644]
src/analyze/analyze.c
src/analyze/meson.build

index 04b8d8d41f08209c2293c7e48f4478c2a90ae95f..fb95a3ec5346fd11c7dec7cee9ecd1e0203e51ff 100644 (file)
       <arg choice="plain">pcrs</arg>
       <arg choice="opt" rep="repeat"><replaceable>PCR</replaceable></arg>
     </cmdsynopsis>
+    <cmdsynopsis>
+      <command>systemd-analyze</command>
+      <arg choice="opt" rep="repeat">OPTIONS</arg>
+      <arg choice="plain">nvpcrs</arg>
+      <arg choice="opt" rep="repeat"><replaceable>NVPCR</replaceable></arg>
+    </cmdsynopsis>
     <cmdsynopsis>
       <command>systemd-analyze</command>
       <arg choice="opt" rep="repeat">OPTIONS</arg>
@@ -1059,6 +1065,24 @@ NR NAME                SHA256
       </example>
     </refsect2>
 
+    <refsect2>
+      <title><command>systemd-analyze nvpcrs <optional><replaceable>NVPCR</replaceable>…</optional></command></title>
+
+      <para>This command shows the known TPM2 NvPCRs (additional PCRs stored in TPM2 NV indexes) along with
+      their identifying names and current values.</para>
+
+      <example>
+        <title>Example Output</title>
+
+        <programlisting>$ systemd-analyze nvpcrs
+NAME         NVINDEX VALUE
+cryptsetup 0x1d10201 f400543943cc7215557ce672872ace5382e6d53177cc459078ba9277e41588d9
+hardware   0x1d10200 e155474936d7d74c893e6ece1099a2311d572cf23becea159dabf282db754284</programlisting>
+      </example>
+
+      <xi:include href="version-info.xml" xpointer="v259"/>
+    </refsect2>
+
     <refsect2>
       <title><command>systemd-analyze srk <optional>&gt;<replaceable>FILE</replaceable></optional></command></title>
 
index 832da33021bd53853a4a085baff2e33c54fa85fb..0ed8191c0a374ea35f944f940e22afe4a0638130 100644 (file)
@@ -68,7 +68,7 @@ _systemd_analyze() {
     )
 
     local -A VERBS=(
-        [STANDALONE]='time blame unit-files unit-paths exit-status compare-versions calendar timestamp timespan pcrs srk has-tpm2 smbios11 chid'
+        [STANDALONE]='time blame unit-files unit-paths exit-status compare-versions calendar timestamp timespan pcrs nvpcrs srk has-tpm2 smbios11 chid'
         [CRITICAL_CHAIN]='critical-chain'
         [DOT]='dot'
         [DUMP]='dump'
diff --git a/src/analyze/analyze-nvpcrs.c b/src/analyze/analyze-nvpcrs.c
new file mode 100644 (file)
index 0000000..0fc7b16
--- /dev/null
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "analyze-nvpcrs.h"
+#include "analyze.h"
+#include "conf-files.h"
+#include "constants.h"
+#include "format-table.h"
+#include "hexdecoct.h"
+#include "string-util.h"
+#include "strv.h"
+#include "tpm2-util.h"
+
+#if HAVE_TPM2
+static int add_nvpcr_to_table(Tpm2Context **c, Table *t, const char *name) {
+        int r;
+
+        _cleanup_free_ char *h = NULL;
+        uint32_t nv_index = 0;
+        if (c) {
+                if (!*c) {
+                        r = tpm2_context_new_or_warn(/* device= */ NULL, c);
+                        if (r < 0)
+                                return r;
+                }
+
+                _cleanup_(iovec_done) struct iovec digest = {};
+                r = tpm2_nvpcr_read(*c, /* session= */ NULL, name, &digest, &nv_index);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to read NvPCR '%s': %m", name);
+
+                h = hexmem(digest.iov_base, digest.iov_len);
+                if (!h)
+                        return log_oom();
+        } else {
+                r = tpm2_nvpcr_get_index(name, &nv_index);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get NV index of NvPCR '%s': %m", name);
+        }
+
+        r = table_add_many(
+                        t,
+                        TABLE_STRING, name,
+                        TABLE_UINT32_HEX_0x, nv_index,
+                        TABLE_STRING, h);
+        if (r < 0)
+                return table_log_add_error(r);
+
+        return 0;
+}
+#endif
+
+int verb_nvpcrs(int argc, char *argv[], void *userdata) {
+#if HAVE_TPM2
+        _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
+        _cleanup_(table_unrefp) Table *table = NULL;
+        int r;
+
+        bool have_tpm2 = tpm2_is_fully_supported();
+
+        if (!have_tpm2)
+                log_notice("System lacks full TPM2 support, not showing NvPCR state.");
+
+        table = table_new("name", "nvindex", "value");
+        if (!table)
+                return log_oom();
+
+        (void) table_set_align_percent(table, table_get_cell(table, 0, 1), 100);
+        (void) table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
+        (void) table_set_sort(table, (size_t) 0);
+
+        if (!have_tpm2)
+                (void) table_hide_column_from_display(table, (size_t) 2);
+
+        if (strv_isempty(strv_skip(argv, 1))) {
+                _cleanup_strv_free_ char **l = NULL;
+                r = conf_files_list_nulstr(
+                                &l,
+                                ".nvpcr",
+                                /* root= */ NULL,
+                                CONF_FILES_REGULAR|CONF_FILES_BASENAME|CONF_FILES_FILTER_MASKED|CONF_FILES_TRUNCATE_SUFFIX,
+                                CONF_PATHS_NULSTR("nvpcr"));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to find .nvpcr files: %m");
+
+                STRV_FOREACH(i, l) {
+                        r = add_nvpcr_to_table(have_tpm2 ? &c : NULL, table, *i);
+                        if (r < 0)
+                                return r;
+                }
+        } else
+                for (int i = 1; i < argc; i++) {
+                        r = add_nvpcr_to_table(have_tpm2 ? &c : NULL, table, argv[i]);
+                        if (r < 0)
+                                return r;
+                }
+
+        if (table_isempty(table) && FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+                log_notice("No NvPCRs defined.");
+        else {
+                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;
+#else
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support not enabled at build time.");
+#endif
+}
diff --git a/src/analyze/analyze-nvpcrs.h b/src/analyze/analyze-nvpcrs.h
new file mode 100644 (file)
index 0000000..2580056
--- /dev/null
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int verb_nvpcrs(int argc, char *argv[], void *userdata);
index 9b611daadefdc36916da014ccdcc7542982e0603..d3040d03e003697c4175e796dabed95728142068 100644 (file)
@@ -33,6 +33,7 @@
 #include "analyze-inspect-elf.h"
 #include "analyze-log-control.h"
 #include "analyze-malloc.h"
+#include "analyze-nvpcrs.h"
 #include "analyze-pcrs.h"
 #include "analyze-plot.h"
 #include "analyze-security.h"
@@ -258,6 +259,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "\n%3$sTPM Operations:%4$s\n"
                "  has-tpm2                   Report whether TPM2 support is available\n"
                "  pcrs [PCR...]              Show TPM2 PCRs and their names\n"
+               "  nvpcrs [NVPCR...]          Show additional TPM2 PCRs stored in NV indexes\n"
                "  srk [>FILE]                Write TPM2 SRK (to FILE)\n"
                "\n%3$sOptions:%4$s\n"
                "     --recursive-errors=MODE Control which units are verified\n"
@@ -713,6 +715,10 @@ done:
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Option --offline= requires one or more units to perform a security review.");
 
+        if (arg_json_format_flags != SD_JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf", "dlopen-metadata", "plot", "fdstore", "pcrs", "nvpcrs", "architectures", "capability", "exit-status"))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Option --json= is only supported for security, inspect-elf, dlopen-metadata, plot, fdstore, pcrs, nvpcrs, architectures, capability, exit-status right now.");
+
         if (arg_threshold != 100 && !streq_ptr(argv[optind], "security"))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Option --threshold= is only supported for security right now.");
@@ -806,6 +812,7 @@ static int run(int argc, char *argv[]) {
                 { "image-policy",       2,        2,        0,  verb_image_policy       },
                 { "has-tpm2",           VERB_ANY, 1,        0,  verb_has_tpm2           },
                 { "pcrs",               VERB_ANY, VERB_ANY, 0,  verb_pcrs               },
+                { "nvpcrs",             VERB_ANY, VERB_ANY, 0,  verb_nvpcrs             },
                 { "srk",                VERB_ANY, 1,        0,  verb_srk                },
                 { "architectures",      VERB_ANY, VERB_ANY, 0,  verb_architectures      },
                 { "smbios11",           VERB_ANY, 1,        0,  verb_smbios11           },
index e26426fc6c84ee491f436451f7f9c6e1a60076d7..03fade3f4bb65b858bfc3ae3a603d90409f69c19 100644 (file)
@@ -21,6 +21,7 @@ systemd_analyze_sources = files(
         'analyze-inspect-elf.c',
         'analyze-log-control.c',
         'analyze-malloc.c',
+        'analyze-nvpcrs.c',
         'analyze-pcrs.c',
         'analyze-plot.c',
         'analyze-security.c',