1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "alloc-util.h"
8 #include "efi-loader.h"
11 #include "hexdecoct.h"
13 #include "main-func.h"
14 #include "openssl-util.h"
15 #include "parse-argument.h"
16 #include "parse-util.h"
17 #include "pretty-print.h"
19 #include "terminal-util.h"
21 #include "tpm2-util.h"
24 /* Tool for pre-calculating expected TPM PCR values based on measured resources. This is intended to be used
25 * to pre-calculate suitable values for PCR 11, the way sd-stub measures into it. */
27 static char *arg_sections
[_UNIFIED_SECTION_MAX
] = {};
28 static char **arg_banks
= NULL
;
29 static char *arg_tpm2_device
= NULL
;
30 static char *arg_private_key
= NULL
;
31 static char *arg_public_key
= NULL
;
32 static JsonFormatFlags arg_json_format_flags
= JSON_FORMAT_PRETTY_AUTO
|JSON_FORMAT_COLOR_AUTO
|JSON_FORMAT_OFF
;
33 static PagerFlags arg_pager_flags
= 0;
34 static bool arg_current
= false;
35 static char **arg_phase
= NULL
;
36 static char *arg_append
= NULL
;
38 STATIC_DESTRUCTOR_REGISTER(arg_banks
, strv_freep
);
39 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device
, freep
);
40 STATIC_DESTRUCTOR_REGISTER(arg_private_key
, freep
);
41 STATIC_DESTRUCTOR_REGISTER(arg_public_key
, freep
);
42 STATIC_DESTRUCTOR_REGISTER(arg_phase
, strv_freep
);
43 STATIC_DESTRUCTOR_REGISTER(arg_append
, freep
);
45 static inline void free_sections(char*(*sections
)[_UNIFIED_SECTION_MAX
]) {
46 for (UnifiedSection c
= 0; c
< _UNIFIED_SECTION_MAX
; c
++)
50 STATIC_DESTRUCTOR_REGISTER(arg_sections
, free_sections
);
52 static int help(int argc
, char *argv
[], void *userdata
) {
53 _cleanup_free_
char *link
= NULL
;
56 r
= terminal_urlify_man("systemd-measure", "1", &link
);
60 printf("%1$s [OPTIONS...] COMMAND ...\n"
61 "\n%5$sPre-calculate and sign PCR hash for a unified kernel image (UKI).%6$s\n"
62 "\n%3$sCommands:%4$s\n"
63 " status Show current PCR values\n"
64 " calculate Calculate expected PCR values\n"
65 " sign Calculate and sign expected PCR values\n"
66 "\n%3$sOptions:%4$s\n"
67 " -h --help Show this help\n"
68 " --version Print version\n"
69 " --no-pager Do not pipe output into a pager\n"
70 " -c --current Use current PCR values\n"
71 " --phase=PHASE Specify a boot phase to sign for\n"
72 " --bank=DIGEST Select TPM bank (SHA1, SHA256, SHA384, SHA512)\n"
73 " --tpm2-device=PATH Use specified TPM2 device\n"
74 " --private-key=KEY Private key (PEM) to sign with\n"
75 " --public-key=KEY Public key (PEM) to validate against\n"
76 " --json=MODE Output as JSON\n"
77 " -j Same as --json=pretty on tty, --json=short otherwise\n"
78 " --append=PATH Load specified JSON signature, and append new signature to it\n"
79 "\n%3$sUKI PE Section Options:%4$s %3$sUKI PE Section%4$s\n"
80 " --linux=PATH Path to Linux kernel image file %7$s .linux\n"
81 " --osrel=PATH Path to os-release file %7$s .osrel\n"
82 " --cmdline=PATH Path to file with kernel command line %7$s .cmdline\n"
83 " --initrd=PATH Path to initrd image file %7$s .initrd\n"
84 " --splash=PATH Path to splash bitmap file %7$s .splash\n"
85 " --dtb=PATH Path to Devicetree file %7$s .dtb\n"
86 " --uname=PATH Path to 'uname -r' file %7$s .uname\n"
87 " --pcrpkey=PATH Path to public key for PCR signatures %7$s .pcrpkey\n"
88 "\nSee the %2$s for details.\n",
89 program_invocation_short_name
,
95 special_glyph(SPECIAL_GLYPH_ARROW_RIGHT
));
100 static char *normalize_phase(const char *s
) {
101 _cleanup_strv_free_
char **l
= NULL
;
103 /* Let's normalize phase expressions. We split the series of colon-separated words up, then remove
104 * all empty ones, and glue them back together again. In other words we remove duplicate ":", as well
105 * as leading and trailing ones. */
107 l
= strv_split(s
, ":"); /* Split series of words */
111 /* Remove all empty words and glue things back together */
112 return strv_join(strv_remove(l
, ""), ":");
115 static int parse_argv(int argc
, char *argv
[]) {
120 ARG_LINUX
= _ARG_SECTION_FIRST
,
127 _ARG_PCRSIG
, /* the .pcrsig section is not input for signing, hence not actually an argument here */
129 ARG_PCRPKEY
= _ARG_SECTION_LAST
,
139 static const struct option options
[] = {
140 { "help", no_argument
, NULL
, 'h' },
141 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
142 { "version", no_argument
, NULL
, ARG_VERSION
},
143 { "linux", required_argument
, NULL
, ARG_LINUX
},
144 { "osrel", required_argument
, NULL
, ARG_OSREL
},
145 { "cmdline", required_argument
, NULL
, ARG_CMDLINE
},
146 { "initrd", required_argument
, NULL
, ARG_INITRD
},
147 { "splash", required_argument
, NULL
, ARG_SPLASH
},
148 { "dtb", required_argument
, NULL
, ARG_DTB
},
149 { "uname", required_argument
, NULL
, ARG_UNAME
},
150 { "pcrpkey", required_argument
, NULL
, ARG_PCRPKEY
},
151 { "current", no_argument
, NULL
, 'c' },
152 { "bank", required_argument
, NULL
, ARG_BANK
},
153 { "tpm2-device", required_argument
, NULL
, ARG_TPM2_DEVICE
},
154 { "private-key", required_argument
, NULL
, ARG_PRIVATE_KEY
},
155 { "public-key", required_argument
, NULL
, ARG_PUBLIC_KEY
},
156 { "json", required_argument
, NULL
, ARG_JSON
},
157 { "phase", required_argument
, NULL
, ARG_PHASE
},
158 { "append", required_argument
, NULL
, ARG_APPEND
},
167 /* Make sure the arguments list and the section list, stays in sync */
168 assert_cc(_ARG_SECTION_FIRST
+ _UNIFIED_SECTION_MAX
== _ARG_SECTION_LAST
+ 1);
170 while ((c
= getopt_long(argc
, argv
, "hjc", options
, NULL
)) >= 0)
181 arg_pager_flags
|= PAGER_DISABLE
;
184 case _ARG_SECTION_FIRST
..._ARG_SECTION_LAST
: {
185 UnifiedSection section
= c
- _ARG_SECTION_FIRST
;
187 r
= parse_path_argument(optarg
, /* suppress_root= */ false, arg_sections
+ section
);
198 const EVP_MD
*implementation
;
200 implementation
= EVP_get_digestbyname(optarg
);
202 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Unknown bank '%s', refusing.", optarg
);
204 if (strv_extend(&arg_banks
, EVP_MD_name(implementation
)) < 0)
210 case ARG_PRIVATE_KEY
:
211 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_private_key
);
218 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_public_key
);
224 case ARG_TPM2_DEVICE
: {
225 _cleanup_free_
char *device
= NULL
;
227 if (streq(optarg
, "list"))
228 return tpm2_list_devices();
230 if (!streq(optarg
, "auto")) {
231 device
= strdup(optarg
);
236 free_and_replace(arg_tpm2_device
, device
);
241 arg_json_format_flags
= JSON_FORMAT_PRETTY_AUTO
|JSON_FORMAT_COLOR_AUTO
;
245 r
= parse_json_argument(optarg
, &arg_json_format_flags
);
254 n
= normalize_phase(optarg
);
258 r
= strv_consume(&arg_phase
, TAKE_PTR(n
));
266 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_append
);
276 assert_not_reached();
279 if (strv_isempty(arg_banks
)) {
280 /* If no banks are specifically selected, pick all known banks */
281 arg_banks
= strv_new("SHA1", "SHA256", "SHA384", "SHA512");
286 strv_sort(arg_banks
);
287 strv_uniq(arg_banks
);
290 for (UnifiedSection us
= 0; us
< _UNIFIED_SECTION_MAX
; us
++)
291 if (arg_sections
[us
])
292 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
293 "The --current switch cannot be used in combination with --linux= and related switches.");
295 if (strv_isempty(arg_phase
)) {
296 /* If no phases are specifically selected, pick everything from the beginning of the initrd
297 * to the beginning of shutdown. */
298 if (strv_extend_strv(&arg_phase
,
299 STRV_MAKE("enter-initrd",
300 "enter-initrd:leave-initrd",
301 "enter-initrd:leave-initrd:sysinit",
302 "enter-initrd:leave-initrd:sysinit:ready"),
303 /* filter_duplicates= */ false) < 0)
306 strv_sort(arg_phase
);
307 strv_uniq(arg_phase
);
310 _cleanup_free_
char *j
= NULL
;
311 j
= strv_join(arg_phase
, ", ");
315 log_debug("Measuring boot phases: %s", j
);
319 /* The PCR 11 state for one specific bank */
320 typedef struct PcrState
{
325 void *saved_value
; /* A copy of the original value we calculated, used by pcr_states_save()/pcr_states_restore() to come later back to */
328 static void pcr_state_free_all(PcrState
**pcr_state
) {
334 for (size_t i
= 0; (*pcr_state
)[i
].value
; i
++) {
335 free((*pcr_state
)[i
].bank
);
336 free((*pcr_state
)[i
].value
);
337 free((*pcr_state
)[i
].saved_value
);
340 *pcr_state
= mfree(*pcr_state
);
343 static void evp_md_ctx_free_all(EVP_MD_CTX
**md
[]) {
349 for (size_t i
= 0; (*md
)[i
]; i
++)
350 EVP_MD_CTX_free((*md
)[i
]);
355 static int pcr_state_extend(PcrState
*pcr_state
, const void *data
, size_t sz
) {
356 _cleanup_(EVP_MD_CTX_freep
) EVP_MD_CTX
*mc
= NULL
;
360 assert(data
|| sz
== 0);
361 assert(pcr_state
->md
);
362 assert(pcr_state
->value
);
363 assert(pcr_state
->value_size
> 0);
365 /* Extends a (virtual) PCR by the given data */
367 mc
= EVP_MD_CTX_new();
371 if (EVP_DigestInit_ex(mc
, pcr_state
->md
, NULL
) != 1)
372 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to initialize %s context.", pcr_state
->bank
);
374 /* First thing we do, is hash the old PCR value */
375 if (EVP_DigestUpdate(mc
, pcr_state
->value
, pcr_state
->value_size
) != 1)
376 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to run digest.");
378 /* Then, we hash the new data */
379 if (EVP_DigestUpdate(mc
, data
, sz
) != 1)
380 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to run digest.");
382 if (EVP_DigestFinal_ex(mc
, pcr_state
->value
, &value_size
) != 1)
383 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to finalize hash context.");
385 assert(value_size
== pcr_state
->value_size
);
389 #define BUFFER_SIZE (16U * 1024U)
391 static int measure_kernel(PcrState
*pcr_states
, size_t n
) {
392 _cleanup_free_
void *buffer
= NULL
;
398 /* Virtually measures the components of a unified kernel image into PCR 11 */
401 /* Shortcut things, if we should just use the current PCR value */
403 for (size_t i
= 0; i
< n
; i
++) {
404 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
405 _cleanup_free_
void *v
= NULL
;
408 if (asprintf(&p
, "/sys/class/tpm/tpm0/pcr-%s/%" PRIu32
, pcr_states
[i
].bank
, TPM_PCR_INDEX_KERNEL_IMAGE
) < 0)
411 r
= read_virtual_file(p
, 4096, &s
, NULL
);
412 if (r
== -ENOENT
&& access("/sys/class/tpm/tpm0/", F_OK
) >= 0)
413 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
);
415 return log_error_errno(r
, "Failed to read '%s': %m", p
);
417 r
= unhexmem(strstrip(s
), SIZE_MAX
, &v
, &sz
);
419 return log_error_errno(r
, "Failed to decode PCR value '%s': %m", s
);
421 assert(pcr_states
[i
].value_size
== sz
);
422 memcpy(pcr_states
[i
].value
, v
, sz
);
428 buffer
= malloc(BUFFER_SIZE
);
432 for (UnifiedSection c
= 0; c
< _UNIFIED_SECTION_MAX
; c
++) {
433 _cleanup_(evp_md_ctx_free_all
) EVP_MD_CTX
**mdctx
= NULL
;
434 _cleanup_close_
int fd
= -EBADF
;
437 if (!arg_sections
[c
])
440 fd
= open(arg_sections
[c
], O_RDONLY
|O_CLOEXEC
);
442 return log_error_errno(errno
, "Failed to open '%s': %m", arg_sections
[c
]);
444 /* Allocate one message digest context per bank (NULL terminated) */
445 mdctx
= new0(EVP_MD_CTX
*, n
+ 1);
449 for (size_t i
= 0; i
< n
; i
++) {
450 mdctx
[i
] = EVP_MD_CTX_new();
454 if (EVP_DigestInit_ex(mdctx
[i
], pcr_states
[i
].md
, NULL
) != 1)
455 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
456 "Failed to initialize data %s context.", pcr_states
[i
].bank
);
462 sz
= read(fd
, buffer
, BUFFER_SIZE
);
464 return log_error_errno(errno
, "Failed to read '%s': %m", arg_sections
[c
]);
465 if (sz
== 0) /* EOF */
468 for (size_t i
= 0; i
< n
; i
++)
469 if (EVP_DigestUpdate(mdctx
[i
], buffer
, sz
) != 1)
470 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to run digest.");
477 if (m
== 0) /* We skip over empty files, the stub does so too */
480 for (size_t i
= 0; i
< n
; i
++) {
481 _cleanup_free_
void *data_hash
= NULL
;
482 unsigned data_hash_size
;
484 data_hash
= malloc(pcr_states
[i
].value_size
);
488 /* Measure name of section */
489 if (EVP_Digest(unified_sections
[c
], strlen(unified_sections
[c
]) + 1, data_hash
, &data_hash_size
, pcr_states
[i
].md
, NULL
) != 1)
490 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to hash section name with %s.", pcr_states
[i
].bank
);
492 assert(data_hash_size
== (unsigned) pcr_states
[i
].value_size
);
494 r
= pcr_state_extend(pcr_states
+ i
, data_hash
, data_hash_size
);
498 /* Retrieve hash of data and measure it */
499 if (EVP_DigestFinal_ex(mdctx
[i
], data_hash
, &data_hash_size
) != 1)
500 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to finalize hash context.");
502 assert(data_hash_size
== (unsigned) pcr_states
[i
].value_size
);
504 r
= pcr_state_extend(pcr_states
+ i
, data_hash
, data_hash_size
);
513 static int measure_phase(PcrState
*pcr_states
, size_t n
, const char *phase
) {
514 _cleanup_strv_free_
char **l
= NULL
;
520 /* Measure a phase string into PCR 11. This splits up the "phase" expression at colons, and then
521 * virtually extends each specified word into PCR 11, to model how during boot we measure a series of
522 * words into PCR 11, one for each phase. */
524 l
= strv_split(phase
, ":");
528 STRV_FOREACH(word
, l
) {
536 for (size_t i
= 0; i
< n
; i
++) { /* For each bank */
537 _cleanup_free_
void *b
= NULL
;
540 bsz
= EVP_MD_size(pcr_states
[i
].md
);
547 /* First hash the word itself */
548 if (EVP_Digest(*word
, wl
, b
, NULL
, pcr_states
[i
].md
, NULL
) != 1)
549 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Failed to hash word '%s'.", *word
);
551 /* And then extend the PCR with the resulting hash */
552 r
= pcr_state_extend(pcr_states
+ i
, b
, bsz
);
561 static int pcr_states_allocate(PcrState
**ret
) {
562 _cleanup_(pcr_state_free_all
) PcrState
*pcr_states
= NULL
;
565 pcr_states
= new0(PcrState
, strv_length(arg_banks
) + 1);
569 /* Allocate a PCR state structure, one for each bank */
570 STRV_FOREACH(d
, arg_banks
) {
571 const EVP_MD
*implementation
;
572 _cleanup_free_
void *v
= NULL
;
573 _cleanup_free_
char *b
= NULL
;
576 assert_se(implementation
= EVP_get_digestbyname(*d
)); /* Must work, we already checked while parsing command line */
578 b
= strdup(EVP_MD_name(implementation
));
582 sz
= EVP_MD_size(implementation
);
583 if (sz
<= 0 || sz
>= INT_MAX
)
584 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Unexpected digest size: %i", sz
);
586 v
= malloc0(sz
); /* initial PCR state is all zeroes */
590 pcr_states
[n
++] = (struct PcrState
) {
591 .bank
= ascii_strlower(TAKE_PTR(b
)),
592 .md
= implementation
,
593 .value
= TAKE_PTR(v
),
598 *ret
= TAKE_PTR(pcr_states
);
602 static int pcr_states_save(PcrState
*pcr_states
, size_t n
) {
606 for (size_t i
= 0; i
< n
; i
++) {
607 _cleanup_free_
void *saved
= NULL
;
609 if (!pcr_states
[i
].value
)
612 saved
= memdup(pcr_states
[i
].value
, pcr_states
[i
].value_size
);
616 free_and_replace(pcr_states
[i
].saved_value
, saved
);
622 static void pcr_states_restore(PcrState
*pcr_states
, size_t n
) {
626 for (size_t i
= 0; i
< n
; i
++) {
628 assert(pcr_states
[i
].value
);
629 assert(pcr_states
[i
].saved_value
);
631 memcpy(pcr_states
[i
].value
, pcr_states
[i
].saved_value
, pcr_states
[i
].value_size
);
635 static int verb_calculate(int argc
, char *argv
[], void *userdata
) {
636 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
637 _cleanup_(pcr_state_free_all
) PcrState
*pcr_states
= NULL
;
640 if (!arg_sections
[UNIFIED_SECTION_LINUX
] && !arg_current
)
641 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
642 "Either --linux= or --current must be specified, refusing.");
644 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
645 "The --append= switch is only supported for 'sign', not 'calculate'.");
647 assert(!strv_isempty(arg_banks
));
648 assert(!strv_isempty(arg_phase
));
650 r
= pcr_states_allocate(&pcr_states
);
656 r
= measure_kernel(pcr_states
, n
);
660 /* Save the current state, so that we later can restore to it. This way we can measure the PCR values
661 * for multiple different boot phases without heaving to start from zero each time */
662 r
= pcr_states_save(pcr_states
, n
);
666 STRV_FOREACH(phase
, arg_phase
) {
668 r
= measure_phase(pcr_states
, n
, *phase
);
672 for (size_t i
= 0; i
< n
; i
++) {
673 if (arg_json_format_flags
& JSON_FORMAT_OFF
) {
674 _cleanup_free_
char *hd
= NULL
;
678 fprintf(stderr
, "%s# PCR[%" PRIu32
"] Phase <%s>%s\n",
680 TPM_PCR_INDEX_KERNEL_IMAGE
,
681 isempty(*phase
) ? ":" : *phase
,
686 hd
= hexmem(pcr_states
[i
].value
, pcr_states
[i
].value_size
);
690 printf("%" PRIu32
":%s=%s\n", TPM_PCR_INDEX_KERNEL_IMAGE
, pcr_states
[i
].bank
, hd
);
692 _cleanup_(json_variant_unrefp
) JsonVariant
*bv
= NULL
, *array
= NULL
;
694 array
= json_variant_ref(json_variant_by_key(w
, pcr_states
[i
].bank
));
698 JSON_BUILD_PAIR_CONDITION(!isempty(*phase
), "phase", JSON_BUILD_STRING(*phase
)),
699 JSON_BUILD_PAIR("pcr", JSON_BUILD_INTEGER(TPM_PCR_INDEX_KERNEL_IMAGE
)),
700 JSON_BUILD_PAIR("hash", JSON_BUILD_HEX(pcr_states
[i
].value
, pcr_states
[i
].value_size
))
704 return log_error_errno(r
, "Failed to build JSON object: %m");
706 r
= json_variant_append_array(&array
, bv
);
708 return log_error_errno(r
, "Failed to append JSON object to array: %m");
710 r
= json_variant_set_field(&w
, pcr_states
[i
].bank
, array
);
712 return log_error_errno(r
, "Failed to add bank info to object: %m");
716 /* Return to the original kernel measurement for the next phase calculation */
717 pcr_states_restore(pcr_states
, n
);
720 if (!FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
722 if (arg_json_format_flags
& (JSON_FORMAT_PRETTY
|JSON_FORMAT_PRETTY_AUTO
))
723 pager_open(arg_pager_flags
);
725 json_variant_dump(w
, arg_json_format_flags
, stdout
, NULL
);
731 static int verb_sign(int argc
, char *argv
[], void *userdata
) {
732 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
733 _cleanup_(pcr_state_free_all
) PcrState
*pcr_states
= NULL
;
734 _cleanup_(EVP_PKEY_freep
) EVP_PKEY
*privkey
= NULL
, *pubkey
= NULL
;
735 _cleanup_fclose_
FILE *privkeyf
= NULL
;
740 if (!arg_sections
[UNIFIED_SECTION_LINUX
] && !arg_current
)
741 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
742 "Either --linux= or --current must be specified, refusing.");
744 if (!arg_private_key
)
745 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
746 "No private key specified, use --private-key=.");
748 assert(!strv_isempty(arg_banks
));
749 assert(!strv_isempty(arg_phase
));
752 r
= json_parse_file(NULL
, arg_append
, 0, &v
, NULL
, NULL
);
754 return log_error_errno(r
, "Failed to parse '%s': %m", arg_append
);
756 if (!json_variant_is_object(v
))
757 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
758 "File '%s' is not a valid JSON object, refusing.", arg_append
);
761 /* When signing we only support JSON output */
762 arg_json_format_flags
&= ~JSON_FORMAT_OFF
;
764 privkeyf
= fopen(arg_private_key
, "re");
766 return log_error_errno(errno
, "Failed to open private key file '%s': %m", arg_private_key
);
768 privkey
= PEM_read_PrivateKey(privkeyf
, NULL
, NULL
, NULL
);
770 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to parse private key '%s'.", arg_private_key
);
772 if (arg_public_key
) {
773 _cleanup_fclose_
FILE *pubkeyf
= NULL
;
775 pubkeyf
= fopen(arg_public_key
, "re");
777 return log_error_errno(errno
, "Failed to open public key file '%s': %m", arg_public_key
);
779 pubkey
= PEM_read_PUBKEY(pubkeyf
, NULL
, NULL
, NULL
);
781 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to parse public key '%s'.", arg_public_key
);
783 _cleanup_free_
char *data
= NULL
;
784 _cleanup_fclose_
FILE *tf
= NULL
;
787 /* No public key was specified, let's derive it automatically, if we can */
789 tf
= open_memstream_unlocked(&data
, &sz
);
793 if (i2d_PUBKEY_fp(tf
, privkey
) != 1)
794 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
795 "Failed to extract public key from private key file '%s'.", arg_private_key
);
800 if (!d2i_PUBKEY_fp(tf
, &pubkey
))
801 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
802 "Failed to parse extracted public key of private key file '%s'.", arg_private_key
);
805 r
= pcr_states_allocate(&pcr_states
);
811 r
= measure_kernel(pcr_states
, n
);
815 r
= pcr_states_save(pcr_states
, n
);
823 _cleanup_tpm2_context_ Tpm2Context
*c
= NULL
;
824 r
= tpm2_context_new(arg_tpm2_device
, &c
);
828 STRV_FOREACH(phase
, arg_phase
) {
830 r
= measure_phase(pcr_states
, n
, *phase
);
834 for (size_t i
= 0; i
< n
; i
++) {
835 static const TPMT_SYM_DEF symmetric
= {
836 .algorithm
= TPM2_ALG_AES
,
838 .mode
.aes
= TPM2_ALG_CFB
,
840 PcrState
*p
= pcr_states
+ i
;
842 _cleanup_tpm2_handle_ Tpm2Handle
*session
= NULL
;
843 r
= tpm2_handle_new(c
, &session
);
847 rc
= sym_Esys_StartAuthSession(
858 &session
->esys_handle
);
859 if (rc
!= TSS2_RC_SUCCESS
)
860 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
),
861 "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc
));
863 /* Generate a single hash value from the PCRs included in our policy. Given that that's
864 * exactly one, the calculation is trivial. */
865 TPM2B_DIGEST intermediate_digest
= {
866 .size
= SHA256_DIGEST_SIZE
,
868 assert(sizeof(intermediate_digest
.buffer
) >= SHA256_DIGEST_SIZE
);
869 sha256_direct(p
->value
, p
->value_size
, intermediate_digest
.buffer
);
871 int tpmalg
= tpm2_hash_alg_from_string(EVP_MD_name(p
->md
));
873 return log_error_errno(tpmalg
, "Unsupported PCR bank");
875 TPML_PCR_SELECTION pcr_selection
;
876 tpm2_tpml_pcr_selection_from_mask(1 << TPM_PCR_INDEX_KERNEL_IMAGE
,
880 rc
= sym_Esys_PolicyPCR(
882 session
->esys_handle
,
886 &intermediate_digest
,
888 if (rc
!= TSS2_RC_SUCCESS
)
889 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
),
890 "Failed to push PCR policy into TPM: %s", sym_Tss2_RC_Decode(rc
));
892 _cleanup_(Esys_Freep
) TPM2B_DIGEST
*pcr_policy_digest
= NULL
;
893 rc
= sym_Esys_PolicyGetDigest(
895 session
->esys_handle
,
900 if (rc
!= TSS2_RC_SUCCESS
)
901 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
),
902 "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc
));
904 _cleanup_(EVP_MD_CTX_freep
) EVP_MD_CTX
* mdctx
= NULL
;
905 mdctx
= EVP_MD_CTX_new();
909 if (EVP_DigestSignInit(mdctx
, NULL
, p
->md
, NULL
, privkey
) != 1)
910 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
),
911 "Failed to initialize signature context.");
913 if (EVP_DigestSignUpdate(mdctx
, pcr_policy_digest
->buffer
, pcr_policy_digest
->size
) != 1)
914 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
),
915 "Failed to sign data.");
918 if (EVP_DigestSignFinal(mdctx
, NULL
, &ss
) != 1)
919 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
),
920 "Failed to finalize signature");
922 _cleanup_free_
void *sig
= malloc(ss
);
926 if (EVP_DigestSignFinal(mdctx
, sig
, &ss
) != 1)
927 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
),
928 "Failed to acquire signature data");
930 _cleanup_free_
void *pubkey_fp
= NULL
;
931 size_t pubkey_fp_size
= 0;
932 r
= pubkey_fingerprint(pubkey
, EVP_sha256(), &pubkey_fp
, &pubkey_fp_size
);
936 _cleanup_(json_variant_unrefp
) JsonVariant
*a
= NULL
;
937 r
= tpm2_make_pcr_json_array(UINT64_C(1) << TPM_PCR_INDEX_KERNEL_IMAGE
, &a
);
939 return log_error_errno(r
, "Failed to build JSON PCR mask array: %m");
941 _cleanup_(json_variant_unrefp
) JsonVariant
*bv
= NULL
;
942 r
= json_build(&bv
, JSON_BUILD_OBJECT(
943 JSON_BUILD_PAIR("pcrs", JSON_BUILD_VARIANT(a
)), /* PCR mask */
944 JSON_BUILD_PAIR("pkfp", JSON_BUILD_HEX(pubkey_fp
, pubkey_fp_size
)), /* SHA256 fingerprint of public key (DER) used for the signature */
945 JSON_BUILD_PAIR("pol", JSON_BUILD_HEX(pcr_policy_digest
->buffer
, pcr_policy_digest
->size
)), /* TPM2 policy hash that is signed */
946 JSON_BUILD_PAIR("sig", JSON_BUILD_BASE64(sig
, ss
)))); /* signature data */
948 return log_error_errno(r
, "Failed to build JSON object: %m");
950 _cleanup_(json_variant_unrefp
) JsonVariant
*av
= NULL
;
951 av
= json_variant_ref(json_variant_by_key(v
, p
->bank
));
953 r
= json_variant_append_array_nodup(&av
, bv
);
955 return log_error_errno(r
, "Failed to append JSON object: %m");
957 r
= json_variant_set_field(&v
, p
->bank
, av
);
959 return log_error_errno(r
, "Failed to add JSON field: %m");
962 /* Return to the original kernel measurement for the next phase calculation */
963 pcr_states_restore(pcr_states
, n
);
966 if (arg_json_format_flags
& (JSON_FORMAT_PRETTY
|JSON_FORMAT_PRETTY_AUTO
))
967 pager_open(arg_pager_flags
);
969 json_variant_dump(v
, arg_json_format_flags
, stdout
, NULL
);
974 static int compare_reported_pcr_nr(uint32_t pcr
, const char *varname
, const char *description
) {
975 _cleanup_free_
char *s
= NULL
;
979 r
= efi_get_variable_string(varname
, &s
);
983 return log_error_errno(r
, "Failed to read EFI variable '%s': %m", varname
);
985 r
= safe_atou32(s
, &v
);
987 return log_error_errno(r
, "Failed to parse EFI variable '%s': %s", varname
, s
);
990 log_warning("PCR number reported by stub for %s (%" PRIu32
") different from our expectation (%" PRIu32
").\n"
991 "The measurements are likely inconsistent.", description
, v
, pcr
);
996 static int validate_stub(void) {
1001 if (tpm2_support() != TPM2_SUPPORT_FULL
)
1002 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
), "Sorry, system lacks full TPM2 support.");
1004 r
= efi_stub_get_features(&features
);
1006 return log_error_errno(r
, "Unable to get stub features: %m");
1008 if (!FLAGS_SET(features
, EFI_STUB_FEATURE_THREE_PCRS
))
1009 log_warning("Warning: current kernel image does not support measuring itself, the command line or initrd system extension images.\n"
1010 "The PCR measurements seen are unlikely to be valid.");
1012 r
= compare_reported_pcr_nr(TPM_PCR_INDEX_KERNEL_IMAGE
, EFI_LOADER_VARIABLE(StubPcrKernelImage
), "kernel image");
1016 r
= compare_reported_pcr_nr(TPM_PCR_INDEX_KERNEL_PARAMETERS
, EFI_LOADER_VARIABLE(StubPcrKernelParameters
), "kernel parameters");
1020 r
= compare_reported_pcr_nr(TPM_PCR_INDEX_INITRD_SYSEXTS
, EFI_LOADER_VARIABLE(StubPcrInitRDSysExts
), "initrd system extension images");
1024 STRV_FOREACH(bank
, arg_banks
) {
1025 _cleanup_free_
char *b
= NULL
, *p
= NULL
;
1031 if (asprintf(&p
, "/sys/class/tpm/tpm0/pcr-%s/", ascii_strlower(b
)) < 0)
1034 if (access(p
, F_OK
) < 0) {
1035 if (errno
!= ENOENT
)
1036 return log_error_errno(errno
, "Failed to detect if '%s' exists: %m", b
);
1042 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
), "None of the select PCR banks appear to exist.");
1047 static int verb_status(int argc
, char *argv
[], void *userdata
) {
1048 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
1050 static const struct {
1052 const char *description
;
1053 } relevant_pcrs
[] = {
1054 { TPM_PCR_INDEX_KERNEL_IMAGE
, "Unified Kernel Image" },
1055 { TPM_PCR_INDEX_KERNEL_PARAMETERS
, "Kernel Parameters" },
1056 { TPM_PCR_INDEX_INITRD_SYSEXTS
, "initrd System Extensions" },
1061 r
= validate_stub();
1065 for (size_t i
= 0; i
< ELEMENTSOF(relevant_pcrs
); i
++) {
1067 STRV_FOREACH(bank
, arg_banks
) {
1068 _cleanup_free_
char *b
= NULL
, *p
= NULL
, *s
= NULL
;
1069 _cleanup_free_
void *h
= NULL
;
1076 if (asprintf(&p
, "/sys/class/tpm/tpm0/pcr-%s/%" PRIu32
, ascii_strlower(b
), relevant_pcrs
[i
].nr
) < 0)
1079 r
= read_virtual_file(p
, 4096, &s
, NULL
);
1083 return log_error_errno(r
, "Failed to read '%s': %m", p
);
1085 r
= unhexmem(strstrip(s
), SIZE_MAX
, &h
, &l
);
1087 return log_error_errno(r
, "Failed to decode PCR value '%s': %m", s
);
1089 if (arg_json_format_flags
& JSON_FORMAT_OFF
) {
1090 _cleanup_free_
char *f
= NULL
;
1096 if (bank
== arg_banks
) {
1097 /* before the first line for each PCR, write a short descriptive text to
1098 * stderr, and leave the primary content on stdout */
1100 fprintf(stderr
, "%s# PCR[%" PRIu32
"] %s%s%s\n",
1102 relevant_pcrs
[i
].nr
,
1103 relevant_pcrs
[i
].description
,
1104 memeqzero(h
, l
) ? " (NOT SET!)" : "",
1109 printf("%" PRIu32
":%s=%s\n", relevant_pcrs
[i
].nr
, b
, f
);
1112 _cleanup_(json_variant_unrefp
) JsonVariant
*bv
= NULL
, *a
= NULL
;
1116 JSON_BUILD_PAIR("pcr", JSON_BUILD_INTEGER(relevant_pcrs
[i
].nr
)),
1117 JSON_BUILD_PAIR("hash", JSON_BUILD_HEX(h
, l
))
1121 return log_error_errno(r
, "Failed to build JSON object: %m");
1123 a
= json_variant_ref(json_variant_by_key(v
, b
));
1125 r
= json_variant_append_array(&a
, bv
);
1127 return log_error_errno(r
, "Failed to append PCR entry to JSON array: %m");
1129 r
= json_variant_set_field(&v
, b
, a
);
1131 return log_error_errno(r
, "Failed to add bank info to object: %m");
1136 if (!FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
1137 if (arg_json_format_flags
& (JSON_FORMAT_PRETTY
|JSON_FORMAT_PRETTY_AUTO
))
1138 pager_open(arg_pager_flags
);
1140 json_variant_dump(v
, arg_json_format_flags
, stdout
, NULL
);
1146 static int measure_main(int argc
, char *argv
[]) {
1147 static const Verb verbs
[] = {
1148 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
1149 { "status", VERB_ANY
, 1, VERB_DEFAULT
, verb_status
},
1150 { "calculate", VERB_ANY
, 1, 0, verb_calculate
},
1151 { "sign", VERB_ANY
, 1, 0, verb_sign
},
1155 return dispatch_verb(argc
, argv
, verbs
, NULL
);
1158 static int run(int argc
, char *argv
[]) {
1161 log_show_color(true);
1162 log_parse_environment();
1165 r
= parse_argv(argc
, argv
);
1169 return measure_main(argc
, argv
);
1172 DEFINE_MAIN_FUNCTION(run
);