diff --git a/man/systemd-measure.xml b/man/systemd-measure.xml index e6b8d31a881..66d19a00725 100644 --- a/man/systemd-measure.xml +++ b/man/systemd-measure.xml @@ -101,6 +101,8 @@ sha512. + + diff --git a/src/boot/measure.c b/src/boot/measure.c index 29568c5b0d1..58c027f1b1d 100644 --- a/src/boot/measure.c +++ b/src/boot/measure.c @@ -8,6 +8,7 @@ #include "fd-util.h" #include "fileio.h" #include "hexdecoct.h" +#include "json.h" #include "main-func.h" #include "openssl-util.h" #include "parse-argument.h" @@ -23,6 +24,8 @@ static char *arg_sections[_UNIFIED_SECTION_MAX] = {}; static char **arg_banks = NULL; +static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; +static PagerFlags arg_pager_flags = 0; STATIC_DESTRUCTOR_REGISTER(arg_banks, strv_freep); @@ -47,15 +50,18 @@ static int help(int argc, char *argv[], void *userdata) { " status Show current PCR values\n" " calculate Calculate expected PCR values\n" "\n%3$sOptions:%4$s\n" - " -h --help Show this help\n" - " --version Print version\n" - " --linux=PATH Path Linux kernel ELF image\n" - " --osrel=PATH Path to os-release file\n" - " --cmdline=PATH Path to file with kernel command line\n" - " --initrd=PATH Path to initrd image\n" - " --splash=PATH Path to splash bitmap\n" - " --dtb=PATH Path to Devicetree file\n" - " --bank=DIGEST Select TPM bank (SHA1, SHA256)\n" + " -h --help Show this help\n" + " --version Print version\n" + " --no-pager Do not pipe output into a pager\n" + " --linux=PATH Path Linux kernel ELF image\n" + " --osrel=PATH Path to os-release file\n" + " --cmdline=PATH Path to file with kernel command line\n" + " --initrd=PATH Path to initrd image\n" + " --splash=PATH Path to splash bitmap\n" + " --dtb=PATH Path to Devicetree file\n" + " --bank=DIGEST Select TPM bank (SHA1, SHA256)\n" + " --json=MODE Output as JSON\n" + " -j Same as --json=pretty on tty, --json=short otherwise\n" "\nSee the %2$s for details.\n", program_invocation_short_name, link, @@ -70,6 +76,7 @@ static int help(int argc, char *argv[], void *userdata) { static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, + ARG_NO_PAGER, _ARG_SECTION_FIRST, ARG_LINUX = _ARG_SECTION_FIRST, ARG_OSREL, @@ -79,18 +86,21 @@ static int parse_argv(int argc, char *argv[]) { _ARG_SECTION_LAST, ARG_DTB = _ARG_SECTION_LAST, ARG_BANK, + ARG_JSON, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "linux", required_argument, NULL, ARG_LINUX }, - { "osrel", required_argument, NULL, ARG_OSREL }, - { "cmdline", required_argument, NULL, ARG_CMDLINE }, - { "initrd", required_argument, NULL, ARG_INITRD }, - { "splash", required_argument, NULL, ARG_SPLASH }, - { "dtb", required_argument, NULL, ARG_DTB }, - { "bank", required_argument, NULL, ARG_BANK }, + { "help", no_argument, NULL, 'h' }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "version", no_argument, NULL, ARG_VERSION }, + { "linux", required_argument, NULL, ARG_LINUX }, + { "osrel", required_argument, NULL, ARG_OSREL }, + { "cmdline", required_argument, NULL, ARG_CMDLINE }, + { "initrd", required_argument, NULL, ARG_INITRD }, + { "splash", required_argument, NULL, ARG_SPLASH }, + { "dtb", required_argument, NULL, ARG_DTB }, + { "bank", required_argument, NULL, ARG_BANK }, + { "json", required_argument, NULL, ARG_JSON }, {} }; @@ -102,7 +112,7 @@ static int parse_argv(int argc, char *argv[]) { /* Make sure the arguments list and the section list, stays in sync */ assert_cc(_ARG_SECTION_FIRST + _UNIFIED_SECTION_MAX == _ARG_SECTION_LAST + 1); - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "hj", options, NULL)) >= 0) switch (c) { case 'h': @@ -112,6 +122,10 @@ static int parse_argv(int argc, char *argv[]) { case ARG_VERSION: return version(); + case ARG_NO_PAGER: + arg_pager_flags |= PAGER_DISABLE; + break; + case _ARG_SECTION_FIRST..._ARG_SECTION_LAST: { UnifiedSection section = c - _ARG_SECTION_FIRST; @@ -134,6 +148,17 @@ static int parse_argv(int argc, char *argv[]) { break; } + case 'j': + arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO; + break; + + case ARG_JSON: + r = parse_json_argument(optarg, &arg_json_format_flags); + if (r <= 0) + return r; + + break; + case '?': return -EINVAL; @@ -313,6 +338,7 @@ static int measure_pcr(PcrState *pcr_states, size_t n) { static int verb_calculate(int argc, char *argv[], void *userdata) { _cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL; + _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; size_t n = 0; int r; @@ -351,17 +377,49 @@ static int verb_calculate(int argc, char *argv[], void *userdata) { return r; for (size_t i = 0; i < n; i++) { - _cleanup_free_ char *hd = NULL, *b = NULL; - - hd = hexmem(pcr_states[i].value, pcr_states[i].value_size); - if (!hd) - return log_oom(); + _cleanup_free_ char *b = NULL; b = strdup(EVP_MD_name(pcr_states[i].md)); if (!b) return log_oom(); - printf("%" PRIu32 ":%s=%s\n", TPM_PCR_INDEX_KERNEL_IMAGE, ascii_strlower(b), hd); + ascii_strlower(b); + + if (arg_json_format_flags & JSON_FORMAT_OFF) { + _cleanup_free_ char *hd = NULL; + + hd = hexmem(pcr_states[i].value, pcr_states[i].value_size); + if (!hd) + return log_oom(); + + printf("%" PRIu32 ":%s=%s\n", TPM_PCR_INDEX_KERNEL_IMAGE, b, hd); + } else { + _cleanup_(json_variant_unrefp) JsonVariant *bv = NULL; + + r = json_build(&bv, + JSON_BUILD_ARRAY( + JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("pcr", JSON_BUILD_INTEGER(TPM_PCR_INDEX_KERNEL_IMAGE)), + JSON_BUILD_PAIR("hash", JSON_BUILD_HEX(pcr_states[i].value, pcr_states[i].value_size)) + ) + ) + ); + if (r < 0) + return log_error_errno(r, "Failed to build JSON object: %m"); + + r = json_variant_set_field(&w, b, bv); + if (r < 0) + return log_error_errno(r, "Failed to add bank info to object: %m"); + + } + } + + if (!FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) { + + if (arg_json_format_flags & (JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO)) + pager_open(arg_pager_flags); + + json_variant_dump(w, arg_json_format_flags, stdout, NULL); } return 0; @@ -441,6 +499,7 @@ static int validate_stub(void) { } static int verb_status(int argc, char *argv[], void *userdata) { + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; static const struct { uint32_t nr; @@ -460,7 +519,7 @@ static int verb_status(int argc, char *argv[], void *userdata) { for (size_t i = 0; i < ELEMENTSOF(relevant_pcrs); i++) { STRV_FOREACH(bank, arg_banks) { - _cleanup_free_ char *b = NULL, *p = NULL, *s = NULL, *f = NULL; + _cleanup_free_ char *b = NULL, *p = NULL, *s = NULL; _cleanup_free_ void *h = NULL; size_t l; @@ -481,27 +540,60 @@ static int verb_status(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to decode PCR value '%s': %m", s); - f = hexmem(h, l); - if (!h) - return log_oom(); + if (arg_json_format_flags & JSON_FORMAT_OFF) { + _cleanup_free_ char *f = NULL; - if (bank == arg_banks) { - /* before the first line for each PCR, write a short descriptive text to - * stderr, and leave the primary content on stdout */ - fflush(stdout); - fprintf(stderr, "%s# PCR[%" PRIu32 "] %s%s%s\n", - ansi_grey(), - relevant_pcrs[i].nr, - relevant_pcrs[i].description, - memeqzero(h, l) ? " (NOT SET!)" : "", - ansi_normal()); - fflush(stderr); + f = hexmem(h, l); + if (!h) + return log_oom(); + + if (bank == arg_banks) { + /* before the first line for each PCR, write a short descriptive text to + * stderr, and leave the primary content on stdout */ + fflush(stdout); + fprintf(stderr, "%s# PCR[%" PRIu32 "] %s%s%s\n", + ansi_grey(), + relevant_pcrs[i].nr, + relevant_pcrs[i].description, + memeqzero(h, l) ? " (NOT SET!)" : "", + ansi_normal()); + fflush(stderr); + } + + printf("%" PRIu32 ":%s=%s\n", relevant_pcrs[i].nr, b, f); + + } else { + _cleanup_(json_variant_unrefp) JsonVariant *bv = NULL, *a = NULL; + + r = json_build(&bv, + JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("pcr", JSON_BUILD_INTEGER(relevant_pcrs[i].nr)), + JSON_BUILD_PAIR("hash", JSON_BUILD_HEX(h, l)) + ) + ); + if (r < 0) + return log_error_errno(r, "Failed to build JSON object: %m"); + + a = json_variant_ref(json_variant_by_key(v, b)); + + r = json_variant_append_array(&a, bv); + if (r < 0) + return log_error_errno(r, "Failed to append PCR entry to JSON array: %m"); + + r = json_variant_set_field(&v, b, a); + if (r < 0) + return log_error_errno(r, "Failed to add bank info to object: %m"); } - - printf("%" PRIu32 ":%s=%s\n", relevant_pcrs[i].nr, b, f); } } + if (!FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) { + if (arg_json_format_flags & (JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO)) + pager_open(arg_pager_flags); + + json_variant_dump(v, arg_json_format_flags, stdout, NULL); + } + return 0; }