1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "alloc-util.h"
7 #include "efi-loader.h"
10 #include "hexdecoct.h"
12 #include "main-func.h"
13 #include "openssl-util.h"
14 #include "parse-argument.h"
15 #include "parse-util.h"
16 #include "pretty-print.h"
18 #include "terminal-util.h"
20 #include "tpm2-util.h"
23 /* Tool for pre-calculating expected TPM PCR values based on measured resources. This is intended to be used
24 * to pre-calculate suitable values for PCR 11, the way sd-stub measures into it. */
26 static char *arg_sections
[_UNIFIED_SECTION_MAX
] = {};
27 static char **arg_banks
= NULL
;
28 static char *arg_tpm2_device
= NULL
;
29 static char *arg_private_key
= NULL
;
30 static char *arg_public_key
= NULL
;
31 static JsonFormatFlags arg_json_format_flags
= JSON_FORMAT_PRETTY_AUTO
|JSON_FORMAT_COLOR_AUTO
|JSON_FORMAT_OFF
;
32 static PagerFlags arg_pager_flags
= 0;
33 static bool arg_current
= false;
35 STATIC_DESTRUCTOR_REGISTER(arg_banks
, strv_freep
);
36 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device
, freep
);
37 STATIC_DESTRUCTOR_REGISTER(arg_private_key
, freep
);
38 STATIC_DESTRUCTOR_REGISTER(arg_public_key
, freep
);
40 static inline void free_sections(char*(*sections
)[_UNIFIED_SECTION_MAX
]) {
41 for (UnifiedSection c
= 0; c
< _UNIFIED_SECTION_MAX
; c
++)
45 STATIC_DESTRUCTOR_REGISTER(arg_sections
, free_sections
);
47 static int help(int argc
, char *argv
[], void *userdata
) {
48 _cleanup_free_
char *link
= NULL
;
51 r
= terminal_urlify_man("systemd-measure", "1", &link
);
55 printf("%1$s [OPTIONS...] COMMAND ...\n"
56 "\n%5$sPre-calculate and sign PCR hash for a unified kernel image.%6$s\n"
57 "\n%3$sCommands:%4$s\n"
58 " status Show current PCR values\n"
59 " calculate Calculate expected PCR values\n"
60 " sign Calculate and sign expected PCR values\n"
61 "\n%3$sOptions:%4$s\n"
62 " -h --help Show this help\n"
63 " --version Print version\n"
64 " --no-pager Do not pipe output into a pager\n"
65 " --linux=PATH Path Linux kernel ELF image\n"
66 " --osrel=PATH Path to os-release file\n"
67 " --cmdline=PATH Path to file with kernel command line\n"
68 " --initrd=PATH Path to initrd image\n"
69 " --splash=PATH Path to splash bitmap\n"
70 " --dtb=PATH Path to Devicetree file\n"
71 " --pcrpkey=PATH Path to public key for PCR signatures in DER format\n"
72 " -c --current Use current PCR values\n"
73 " --bank=DIGEST Select TPM bank (SHA1, SHA256)\n"
74 " --tpm2-device=PATH Use specified TPM2 device\n"
75 " --private-key=KEY Private key (PEM) to sign with\n"
76 " --public-key=KEY Public key (PEM) to validate against\n"
77 " --json=MODE Output as JSON\n"
78 " -j Same as --json=pretty on tty, --json=short otherwise\n"
79 "\nSee the %2$s for details.\n",
80 program_invocation_short_name
,
90 static int parse_argv(int argc
, char *argv
[]) {
95 ARG_LINUX
= _ARG_SECTION_FIRST
,
101 _ARG_PCRSIG
, /* the .pcrsig section is not input for signing, hence not actually an argument here */
103 ARG_PCRPKEY
= _ARG_SECTION_LAST
,
111 static const struct option options
[] = {
112 { "help", no_argument
, NULL
, 'h' },
113 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
114 { "version", no_argument
, NULL
, ARG_VERSION
},
115 { "linux", required_argument
, NULL
, ARG_LINUX
},
116 { "osrel", required_argument
, NULL
, ARG_OSREL
},
117 { "cmdline", required_argument
, NULL
, ARG_CMDLINE
},
118 { "initrd", required_argument
, NULL
, ARG_INITRD
},
119 { "splash", required_argument
, NULL
, ARG_SPLASH
},
120 { "dtb", required_argument
, NULL
, ARG_DTB
},
121 { "pcrpkey", required_argument
, NULL
, ARG_PCRPKEY
},
122 { "current", no_argument
, NULL
, 'c' },
123 { "bank", required_argument
, NULL
, ARG_BANK
},
124 { "tpm2-device", required_argument
, NULL
, ARG_TPM2_DEVICE
},
125 { "private-key", required_argument
, NULL
, ARG_PRIVATE_KEY
},
126 { "public-key", required_argument
, NULL
, ARG_PUBLIC_KEY
},
127 { "json", required_argument
, NULL
, ARG_JSON
},
136 /* Make sure the arguments list and the section list, stays in sync */
137 assert_cc(_ARG_SECTION_FIRST
+ _UNIFIED_SECTION_MAX
== _ARG_SECTION_LAST
+ 1);
139 while ((c
= getopt_long(argc
, argv
, "hjc", options
, NULL
)) >= 0)
150 arg_pager_flags
|= PAGER_DISABLE
;
153 case _ARG_SECTION_FIRST
..._ARG_SECTION_LAST
: {
154 UnifiedSection section
= c
- _ARG_SECTION_FIRST
;
156 r
= parse_path_argument(optarg
, /* suppress_root= */ false, arg_sections
+ section
);
167 const EVP_MD
*implementation
;
169 implementation
= EVP_get_digestbyname(optarg
);
171 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Unknown bank '%s', refusing.", optarg
);
173 if (strv_extend(&arg_banks
, EVP_MD_name(implementation
)) < 0)
179 case ARG_PRIVATE_KEY
:
180 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_private_key
);
187 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_public_key
);
193 case ARG_TPM2_DEVICE
: {
194 _cleanup_free_
char *device
= NULL
;
196 if (streq(optarg
, "list"))
197 return tpm2_list_devices();
199 if (!streq(optarg
, "auto")) {
200 device
= strdup(optarg
);
205 free_and_replace(arg_tpm2_device
, device
);
210 arg_json_format_flags
= JSON_FORMAT_PRETTY_AUTO
|JSON_FORMAT_COLOR_AUTO
;
214 r
= parse_json_argument(optarg
, &arg_json_format_flags
);
224 assert_not_reached();
227 if (strv_isempty(arg_banks
)) {
228 /* If no banks are specifically selected, pick all known banks */
229 arg_banks
= strv_new("SHA1", "SHA256", "SHA384", "SHA512");
234 strv_sort(arg_banks
);
235 strv_uniq(arg_banks
);
238 for (UnifiedSection us
= 0; us
< _UNIFIED_SECTION_MAX
; us
++)
239 if (arg_sections
[us
])
240 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "The --current switch cannot be used in combination with --linux= and related switches.");
245 typedef struct PcrState
{
252 static void pcr_state_free_all(PcrState
**pcr_state
) {
258 for (size_t i
= 0; (*pcr_state
)[i
].value
; i
++) {
259 free((*pcr_state
)[i
].bank
);
260 free((*pcr_state
)[i
].value
);
263 *pcr_state
= mfree(*pcr_state
);
266 static void evp_md_ctx_free_all(EVP_MD_CTX
**md
[]) {
272 for (size_t i
= 0; (*md
)[i
]; i
++)
273 EVP_MD_CTX_free((*md
)[i
]);
278 static int pcr_state_extend(PcrState
*pcr_state
, const void *data
, size_t sz
) {
279 _cleanup_(EVP_MD_CTX_freep
) EVP_MD_CTX
*mc
= NULL
;
283 assert(data
|| sz
== 0);
284 assert(pcr_state
->md
);
285 assert(pcr_state
->value
);
286 assert(pcr_state
->value_size
> 0);
288 /* Extends a (virtual) PCR by the given data */
290 mc
= EVP_MD_CTX_new();
294 if (EVP_DigestInit_ex(mc
, pcr_state
->md
, NULL
) != 1)
295 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to initialize %s context.", pcr_state
->bank
);
297 /* First thing we do, is hash the old PCR value */
298 if (EVP_DigestUpdate(mc
, pcr_state
->value
, pcr_state
->value_size
) != 1)
299 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to run digest.");
301 /* Then, we hash the new data */
302 if (EVP_DigestUpdate(mc
, data
, sz
) != 1)
303 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to run digest.");
305 if (EVP_DigestFinal_ex(mc
, pcr_state
->value
, &value_size
) != 1)
306 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to finalize hash context.");
308 assert(value_size
== pcr_state
->value_size
);
312 #define BUFFER_SIZE (16U * 1024U)
314 static int measure_pcr(PcrState
*pcr_states
, size_t n
) {
315 _cleanup_free_
void *buffer
= NULL
;
322 /* Shortcut things, if we should just use the current PCR value */
324 for (size_t i
= 0; i
< n
; i
++) {
325 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
326 _cleanup_free_
void *v
= NULL
;
329 if (asprintf(&p
, "/sys/class/tpm/tpm0/pcr-%s/%" PRIu32
, pcr_states
[i
].bank
, TPM_PCR_INDEX_KERNEL_IMAGE
) < 0)
332 r
= read_virtual_file(p
, 4096, &s
, NULL
);
333 if (r
== -ENOENT
&& access("/sys/class/tpm/tpm0/", F_OK
) >= 0)
334 return log_error_errno(r
, "TPM device exists, but cannot open '%s'; either the kernel is too old, or selected PCR bank is not supported: %m", p
);
336 return log_error_errno(r
, "Failed to read '%s': %m", p
);
338 r
= unhexmem(strstrip(s
), SIZE_MAX
, &v
, &sz
);
340 return log_error_errno(r
, "Failed to decode PCR value '%s': %m", s
);
342 assert(pcr_states
[i
].value_size
== sz
);
343 memcpy(pcr_states
[i
].value
, v
, sz
);
349 buffer
= malloc(BUFFER_SIZE
);
353 for (UnifiedSection c
= 0; c
< _UNIFIED_SECTION_MAX
; c
++) {
354 _cleanup_(evp_md_ctx_free_all
) EVP_MD_CTX
**mdctx
= NULL
;
355 _cleanup_close_
int fd
= -1;
358 if (!arg_sections
[c
])
361 fd
= open(arg_sections
[c
], O_RDONLY
|O_CLOEXEC
);
363 return log_error_errno(errno
, "Failed to open '%s': %m", arg_sections
[c
]);
365 /* Allocate one message digest context per bank (NULL terminated) */
366 mdctx
= new0(EVP_MD_CTX
*, n
+ 1);
370 for (size_t i
= 0; i
< n
; i
++) {
371 mdctx
[i
] = EVP_MD_CTX_new();
375 if (EVP_DigestInit_ex(mdctx
[i
], pcr_states
[i
].md
, NULL
) != 1)
376 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to initialize data %s context.", pcr_states
[i
].bank
);
382 sz
= read(fd
, buffer
, BUFFER_SIZE
);
384 return log_error_errno(errno
, "Failed to read '%s': %m", arg_sections
[c
]);
385 if (sz
== 0) /* EOF */
388 for (size_t i
= 0; i
< n
; i
++)
389 if (EVP_DigestUpdate(mdctx
[i
], buffer
, sz
) != 1)
390 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to run digest.");
397 if (m
== 0) /* We skip over empty files, the stub does so too */
400 for (size_t i
= 0; i
< n
; i
++) {
401 _cleanup_free_
void *data_hash
= NULL
;
402 unsigned data_hash_size
;
404 data_hash
= malloc(pcr_states
[i
].value_size
);
408 /* Measure name of section */
409 if (EVP_Digest(unified_sections
[c
], strlen(unified_sections
[c
]) + 1, data_hash
, &data_hash_size
, pcr_states
[i
].md
, NULL
) != 1)
410 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to hash section name with %s.", pcr_states
[i
].bank
);
412 assert(data_hash_size
== (unsigned) pcr_states
[i
].value_size
);
414 r
= pcr_state_extend(pcr_states
+ i
, data_hash
, data_hash_size
);
418 /* Retrieve hash of data an measure it*/
419 if (EVP_DigestFinal_ex(mdctx
[i
], data_hash
, &data_hash_size
) != 1)
420 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to finalize hash context.");
422 assert(data_hash_size
== (unsigned) pcr_states
[i
].value_size
);
424 r
= pcr_state_extend(pcr_states
+ i
, data_hash
, data_hash_size
);
433 static int pcr_states_allocate(PcrState
**ret
) {
434 _cleanup_(pcr_state_free_all
) PcrState
*pcr_states
= NULL
;
437 pcr_states
= new0(PcrState
, strv_length(arg_banks
) + 1);
441 /* Allocate a PCR state structure, one for each bank */
442 STRV_FOREACH(d
, arg_banks
) {
443 const EVP_MD
*implementation
;
444 _cleanup_free_
void *v
= NULL
;
445 _cleanup_free_
char *b
= NULL
;
448 assert_se(implementation
= EVP_get_digestbyname(*d
)); /* Must work, we already checked while parsing command line */
450 b
= strdup(EVP_MD_name(implementation
));
454 sz
= EVP_MD_size(implementation
);
455 if (sz
<= 0 || sz
>= INT_MAX
)
456 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Unexpected digest size: %i", sz
);
458 v
= malloc0(sz
); /* initial PCR state is all zeroes */
462 pcr_states
[n
++] = (struct PcrState
) {
463 .bank
= ascii_strlower(TAKE_PTR(b
)),
464 .md
= implementation
,
465 .value
= TAKE_PTR(v
),
470 *ret
= TAKE_PTR(pcr_states
);
474 static int verb_calculate(int argc
, char *argv
[], void *userdata
) {
475 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
476 _cleanup_(pcr_state_free_all
) PcrState
*pcr_states
= NULL
;
480 if (!arg_sections
[UNIFIED_SECTION_LINUX
] && !arg_current
)
481 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Either --linux= or --current must be specified, refusing.");
483 r
= pcr_states_allocate(&pcr_states
);
489 r
= measure_pcr(pcr_states
, n
);
493 for (size_t i
= 0; i
< n
; i
++) {
494 if (arg_json_format_flags
& JSON_FORMAT_OFF
) {
495 _cleanup_free_
char *hd
= NULL
;
497 hd
= hexmem(pcr_states
[i
].value
, pcr_states
[i
].value_size
);
501 printf("%" PRIu32
":%s=%s\n", TPM_PCR_INDEX_KERNEL_IMAGE
, pcr_states
[i
].bank
, hd
);
503 _cleanup_(json_variant_unrefp
) JsonVariant
*bv
= NULL
;
508 JSON_BUILD_PAIR("pcr", JSON_BUILD_INTEGER(TPM_PCR_INDEX_KERNEL_IMAGE
)),
509 JSON_BUILD_PAIR("hash", JSON_BUILD_HEX(pcr_states
[i
].value
, pcr_states
[i
].value_size
))
514 return log_error_errno(r
, "Failed to build JSON object: %m");
516 r
= json_variant_set_field(&w
, pcr_states
[i
].bank
, bv
);
518 return log_error_errno(r
, "Failed to add bank info to object: %m");
523 if (!FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
525 if (arg_json_format_flags
& (JSON_FORMAT_PRETTY
|JSON_FORMAT_PRETTY_AUTO
))
526 pager_open(arg_pager_flags
);
528 json_variant_dump(w
, arg_json_format_flags
, stdout
, NULL
);
534 static TPM2_ALG_ID
convert_evp_md_name_to_tpm2_alg(const EVP_MD
*md
) {
537 mdname
= EVP_MD_name(md
);
538 if (strcaseeq(mdname
, "sha1"))
539 return TPM2_ALG_SHA1
;
540 if (strcaseeq(mdname
, "sha256"))
541 return TPM2_ALG_SHA256
;
542 if (strcaseeq(mdname
, "sha384"))
543 return TPM2_ALG_SHA384
;
544 if (strcaseeq(mdname
, "sha512"))
545 return TPM2_ALG_SHA512
;
547 return TPM2_ALG_ERROR
;
550 static int verb_sign(int argc
, char *argv
[], void *userdata
) {
551 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
552 _cleanup_(pcr_state_free_all
) PcrState
*pcr_states
= NULL
;
553 _cleanup_(EVP_PKEY_freep
) EVP_PKEY
*privkey
= NULL
, *pubkey
= NULL
;
554 _cleanup_(tpm2_context_destroy
) struct tpm2_context c
= {};
555 _cleanup_fclose_
FILE *privkeyf
= NULL
, *pubkeyf
= NULL
;
556 ESYS_TR session_handle
= ESYS_TR_NONE
;
561 if (!arg_sections
[UNIFIED_SECTION_LINUX
] && !arg_current
)
562 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Either --linux= or --current must be specified, refusing.");
564 if (!arg_private_key
)
565 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No private key specified, use --private-key=.");
567 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No public key specified, use --public-key=.");
569 /* When signing we only support JSON output */
570 arg_json_format_flags
&= ~JSON_FORMAT_OFF
;
572 privkeyf
= fopen(arg_private_key
, "re");
574 return log_error_errno(errno
, "Failed to open private key file '%s': %m", arg_private_key
);
576 pubkeyf
= fopen(arg_public_key
, "re");
578 return log_error_errno(errno
, "Failed to open public key file '%s': %m", arg_public_key
);
580 privkey
= PEM_read_PrivateKey(privkeyf
, NULL
, NULL
, NULL
);
582 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to parse private key '%s'.", arg_private_key
);
584 pubkey
= PEM_read_PUBKEY(pubkeyf
, NULL
, NULL
, NULL
);
586 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to parse public key '%s'.", arg_public_key
);
588 r
= pcr_states_allocate(&pcr_states
);
594 r
= measure_pcr(pcr_states
, n
);
602 r
= tpm2_context_init(arg_tpm2_device
, &c
);
606 for (size_t i
= 0; i
< n
; i
++) {
607 static const TPMT_SYM_DEF symmetric
= {
608 .algorithm
= TPM2_ALG_AES
,
610 .mode
.aes
= TPM2_ALG_CFB
,
612 PcrState
*p
= pcr_states
+ i
;
614 rc
= sym_Esys_StartAuthSession(
626 if (rc
!= TSS2_RC_SUCCESS
) {
627 r
= log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
),
628 "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc
));
632 /* Generate a single hash value from the PCRs included in our policy. Given that that's
633 * exactly one, the calculation is trivial. */
634 TPM2B_DIGEST intermediate_digest
= {
635 .size
= SHA256_DIGEST_SIZE
,
637 assert(sizeof(intermediate_digest
.buffer
) >= SHA256_DIGEST_SIZE
);
638 sha256_direct(p
->value
, p
->value_size
, intermediate_digest
.buffer
);
640 TPM2_ALG_ID tpmalg
= convert_evp_md_name_to_tpm2_alg(p
->md
);
641 if (tpmalg
== TPM2_ALG_ERROR
) {
642 r
= log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
), "Unsupported PCR bank");
646 TPML_PCR_SELECTION pcr_selection
;
647 tpm2_pcr_mask_to_selection(1 << TPM_PCR_INDEX_KERNEL_IMAGE
, tpmalg
, &pcr_selection
);
649 rc
= sym_Esys_PolicyPCR(
655 &intermediate_digest
,
657 if (rc
!= TSS2_RC_SUCCESS
) {
658 r
= log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
),
659 "Failed to push PCR policy into TPM: %s", sym_Tss2_RC_Decode(rc
));
663 _cleanup_(Esys_Freep
) TPM2B_DIGEST
*pcr_policy_digest
= NULL
;
664 rc
= sym_Esys_PolicyGetDigest(
671 if (rc
!= TSS2_RC_SUCCESS
) {
672 r
= log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
),
673 "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc
));
677 session_handle
= tpm2_flush_context_verbose(c
.esys_context
, session_handle
);
679 _cleanup_(EVP_MD_CTX_freep
) EVP_MD_CTX
* mdctx
= NULL
;
680 mdctx
= EVP_MD_CTX_new();
686 if (EVP_DigestSignInit(mdctx
, NULL
, p
->md
, NULL
, privkey
) != 1) {
687 r
= log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
),
688 "Failed to initialize signature context.");
692 if (EVP_DigestSignUpdate(mdctx
, pcr_policy_digest
->buffer
, pcr_policy_digest
->size
) != 1) {
693 r
= log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
),
694 "Failed to sign data.");
699 if (EVP_DigestSignFinal(mdctx
, NULL
, &ss
) != 1) {
700 r
= log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
),
701 "Failed to finalize signature");
705 _cleanup_free_
void *sig
= malloc(ss
);
711 if (EVP_DigestSignFinal(mdctx
, sig
, &ss
) != 1) {
712 r
= log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
),
713 "Failed to acquire signature data");
717 _cleanup_free_
void *pubkey_fp
= NULL
;
718 size_t pubkey_fp_size
= 0;
719 r
= pubkey_fingerprint(pubkey
, EVP_sha256(), &pubkey_fp
, &pubkey_fp_size
);
723 _cleanup_(json_variant_unrefp
) JsonVariant
*bv
= NULL
, *a
= NULL
;
725 r
= tpm2_make_pcr_json_array(UINT64_C(1) << TPM_PCR_INDEX_KERNEL_IMAGE
, &a
);
727 log_error_errno(r
, "Failed to build JSON PCR mask array: %m");
731 r
= json_build(&bv
, JSON_BUILD_ARRAY(
733 JSON_BUILD_PAIR("pcrs", JSON_BUILD_VARIANT(a
)), /* PCR mask */
734 JSON_BUILD_PAIR("pkfp", JSON_BUILD_HEX(pubkey_fp
, pubkey_fp_size
)), /* SHA256 fingerprint of public key (DER) used for the signature */
735 JSON_BUILD_PAIR("pol", JSON_BUILD_HEX(pcr_policy_digest
->buffer
, pcr_policy_digest
->size
)), /* TPM2 policy hash that is signed */
736 JSON_BUILD_PAIR("sig", JSON_BUILD_BASE64(sig
, ss
))))); /* signature data */
738 log_error_errno(r
, "Failed to build JSON object: %m");
742 r
= json_variant_set_field(&v
, p
->bank
, bv
);
744 log_error_errno(r
, "Failed to add JSON field: %m");
750 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Unable to find a single working PCR bank.");
752 if (arg_json_format_flags
& (JSON_FORMAT_PRETTY
|JSON_FORMAT_PRETTY_AUTO
))
753 pager_open(arg_pager_flags
);
755 json_variant_dump(v
, arg_json_format_flags
, stdout
, NULL
);
759 session_handle
= tpm2_flush_context_verbose(c
.esys_context
, session_handle
);
763 static int compare_reported_pcr_nr(uint32_t pcr
, const char *varname
, const char *description
) {
764 _cleanup_free_
char *s
= NULL
;
768 r
= efi_get_variable_string(varname
, &s
);
772 return log_error_errno(r
, "Failed to read EFI variable '%s': %m", varname
);
774 r
= safe_atou32(s
, &v
);
776 return log_error_errno(r
, "Failed to parse EFI variable '%s': %s", varname
, s
);
779 log_warning("PCR number reported by stub for %s (%" PRIu32
") different from our expectation (%" PRIu32
").\n"
780 "The measurements are likely inconsistent.", description
, v
, pcr
);
785 static int validate_stub(void) {
790 if (tpm2_support() != TPM2_SUPPORT_FULL
)
791 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
), "Sorry, system lacks full TPM2 support.");
793 r
= efi_stub_get_features(&features
);
795 return log_error_errno(r
, "Unable to get stub features: %m");
797 if (!FLAGS_SET(features
, EFI_STUB_FEATURE_THREE_PCRS
))
798 log_warning("Warning: current kernel image does not support measuring itself, the command line or initrd system extension images.\n"
799 "The PCR measurements seen are unlikely to be valid.");
801 r
= compare_reported_pcr_nr(TPM_PCR_INDEX_KERNEL_IMAGE
, EFI_LOADER_VARIABLE("StubPcrKernelImage"), "kernel image");
805 r
= compare_reported_pcr_nr(TPM_PCR_INDEX_KERNEL_PARAMETERS
, EFI_LOADER_VARIABLE("StubPcrKernelParameters"), "kernel parameters");
809 r
= compare_reported_pcr_nr(TPM_PCR_INDEX_INITRD_SYSEXTS
, EFI_LOADER_VARIABLE("StubPcrInitRDSysExts"), "initrd system extension images");
813 STRV_FOREACH(bank
, arg_banks
) {
814 _cleanup_free_
char *b
= NULL
, *p
= NULL
;
820 if (asprintf(&p
, "/sys/class/tpm/tpm0/pcr-%s/", ascii_strlower(b
)) < 0)
823 if (access(p
, F_OK
) < 0) {
825 return log_error_errno(errno
, "Failed to detect if '%s' exists: %m", b
);
831 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
), "None of the select PCR banks appear to exist.");
836 static int verb_status(int argc
, char *argv
[], void *userdata
) {
837 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
839 static const struct {
841 const char *description
;
842 } relevant_pcrs
[] = {
843 { TPM_PCR_INDEX_KERNEL_IMAGE
, "Unified Kernel Image" },
844 { TPM_PCR_INDEX_KERNEL_PARAMETERS
, "Kernel Parameters" },
845 { TPM_PCR_INDEX_INITRD_SYSEXTS
, "initrd System Extensions" },
854 for (size_t i
= 0; i
< ELEMENTSOF(relevant_pcrs
); i
++) {
856 STRV_FOREACH(bank
, arg_banks
) {
857 _cleanup_free_
char *b
= NULL
, *p
= NULL
, *s
= NULL
;
858 _cleanup_free_
void *h
= NULL
;
865 if (asprintf(&p
, "/sys/class/tpm/tpm0/pcr-%s/%" PRIu32
, ascii_strlower(b
), relevant_pcrs
[i
].nr
) < 0)
868 r
= read_virtual_file(p
, 4096, &s
, NULL
);
872 return log_error_errno(r
, "Failed to read '%s': %m", p
);
874 r
= unhexmem(strstrip(s
), SIZE_MAX
, &h
, &l
);
876 return log_error_errno(r
, "Failed to decode PCR value '%s': %m", s
);
878 if (arg_json_format_flags
& JSON_FORMAT_OFF
) {
879 _cleanup_free_
char *f
= NULL
;
885 if (bank
== arg_banks
) {
886 /* before the first line for each PCR, write a short descriptive text to
887 * stderr, and leave the primary content on stdout */
889 fprintf(stderr
, "%s# PCR[%" PRIu32
"] %s%s%s\n",
892 relevant_pcrs
[i
].description
,
893 memeqzero(h
, l
) ? " (NOT SET!)" : "",
898 printf("%" PRIu32
":%s=%s\n", relevant_pcrs
[i
].nr
, b
, f
);
901 _cleanup_(json_variant_unrefp
) JsonVariant
*bv
= NULL
, *a
= NULL
;
905 JSON_BUILD_PAIR("pcr", JSON_BUILD_INTEGER(relevant_pcrs
[i
].nr
)),
906 JSON_BUILD_PAIR("hash", JSON_BUILD_HEX(h
, l
))
910 return log_error_errno(r
, "Failed to build JSON object: %m");
912 a
= json_variant_ref(json_variant_by_key(v
, b
));
914 r
= json_variant_append_array(&a
, bv
);
916 return log_error_errno(r
, "Failed to append PCR entry to JSON array: %m");
918 r
= json_variant_set_field(&v
, b
, a
);
920 return log_error_errno(r
, "Failed to add bank info to object: %m");
925 if (!FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
926 if (arg_json_format_flags
& (JSON_FORMAT_PRETTY
|JSON_FORMAT_PRETTY_AUTO
))
927 pager_open(arg_pager_flags
);
929 json_variant_dump(v
, arg_json_format_flags
, stdout
, NULL
);
935 static int measure_main(int argc
, char *argv
[]) {
936 static const Verb verbs
[] = {
937 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
938 { "status", VERB_ANY
, 1, VERB_DEFAULT
, verb_status
},
939 { "calculate", VERB_ANY
, 1, 0, verb_calculate
},
940 { "sign", VERB_ANY
, 1, 0, verb_sign
},
944 return dispatch_verb(argc
, argv
, verbs
, NULL
);
947 static int run(int argc
, char *argv
[]) {
950 log_show_color(true);
951 log_parse_environment();
954 r
= parse_argv(argc
, argv
);
958 return measure_main(argc
, argv
);
961 DEFINE_MAIN_FUNCTION(run
);