]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/measure.c
Merge pull request #25389 from fbuihuu/update-test-for-opensuse
[thirdparty/systemd.git] / src / boot / measure.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <getopt.h>
4 #include <unistd.h>
5
6 #include "alloc-util.h"
7 #include "build.h"
8 #include "efi-loader.h"
9 #include "fd-util.h"
10 #include "fileio.h"
11 #include "hexdecoct.h"
12 #include "json.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"
18 #include "sha256.h"
19 #include "terminal-util.h"
20 #include "tpm-pcr.h"
21 #include "tpm2-util.h"
22 #include "verbs.h"
23
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. */
26
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
37 STATIC_DESTRUCTOR_REGISTER(arg_banks, strv_freep);
38 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
39 STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
40 STATIC_DESTRUCTOR_REGISTER(arg_public_key, freep);
41 STATIC_DESTRUCTOR_REGISTER(arg_phase, strv_freep);
42
43 static inline void free_sections(char*(*sections)[_UNIFIED_SECTION_MAX]) {
44 for (UnifiedSection c = 0; c < _UNIFIED_SECTION_MAX; c++)
45 free((*sections)[c]);
46 }
47
48 STATIC_DESTRUCTOR_REGISTER(arg_sections, free_sections);
49
50 static int help(int argc, char *argv[], void *userdata) {
51 _cleanup_free_ char *link = NULL;
52 int r;
53
54 r = terminal_urlify_man("systemd-measure", "1", &link);
55 if (r < 0)
56 return log_oom();
57
58 printf("%1$s [OPTIONS...] COMMAND ...\n"
59 "\n%5$sPre-calculate and sign PCR hash for a unified kernel image (UKI).%6$s\n"
60 "\n%3$sCommands:%4$s\n"
61 " status Show current PCR values\n"
62 " calculate Calculate expected PCR values\n"
63 " sign Calculate and sign expected PCR values\n"
64 "\n%3$sOptions:%4$s\n"
65 " -h --help Show this help\n"
66 " --version Print version\n"
67 " --no-pager Do not pipe output into a pager\n"
68 " -c --current Use current PCR values\n"
69 " --phase=PHASE Specify a boot phase to sign for\n"
70 " --bank=DIGEST Select TPM bank (SHA1, SHA256, SHA384, SHA512)\n"
71 " --tpm2-device=PATH Use specified TPM2 device\n"
72 " --private-key=KEY Private key (PEM) to sign with\n"
73 " --public-key=KEY Public key (PEM) to validate against\n"
74 " --json=MODE Output as JSON\n"
75 " -j Same as --json=pretty on tty, --json=short otherwise\n"
76 "\n%3$sUKI PE Section Options:%4$s %3$sUKI PE Section%4$s\n"
77 " --linux=PATH Path to Linux kernel image file %7$s .linux\n"
78 " --osrel=PATH Path to os-release file %7$s .osrel\n"
79 " --cmdline=PATH Path to file with kernel command line %7$s .cmdline\n"
80 " --initrd=PATH Path to initrd image file %7$s .initrd\n"
81 " --splash=PATH Path to splash bitmap file %7$s .splash\n"
82 " --dtb=PATH Path to Devicetree file %7$s .dtb\n"
83 " --pcrpkey=PATH Path to public key for PCR signatures %7$s .pcrpkey\n"
84 "\nSee the %2$s for details.\n",
85 program_invocation_short_name,
86 link,
87 ansi_underline(),
88 ansi_normal(),
89 ansi_highlight(),
90 ansi_normal(),
91 special_glyph(SPECIAL_GLYPH_ARROW_RIGHT));
92
93 return 0;
94 }
95
96 static char *normalize_phase(const char *s) {
97 _cleanup_strv_free_ char **l = NULL;
98
99 /* Let's normalize phase expressions. We split the series of colon-separated words up, then remove
100 * all empty ones, and glue them back together again. In other words we remove duplicate ":", as well
101 * as leading and trailing ones. */
102
103 l = strv_split(s, ":"); /* Split series of words */
104 if (!l)
105 return NULL;
106
107 /* Remove all empty words and glue things back together */
108 return strv_join(strv_remove(l, ""), ":");
109 }
110
111 static int parse_argv(int argc, char *argv[]) {
112 enum {
113 ARG_VERSION = 0x100,
114 ARG_NO_PAGER,
115 _ARG_SECTION_FIRST,
116 ARG_LINUX = _ARG_SECTION_FIRST,
117 ARG_OSREL,
118 ARG_CMDLINE,
119 ARG_INITRD,
120 ARG_SPLASH,
121 ARG_DTB,
122 _ARG_PCRSIG, /* the .pcrsig section is not input for signing, hence not actually an argument here */
123 _ARG_SECTION_LAST,
124 ARG_PCRPKEY = _ARG_SECTION_LAST,
125 ARG_BANK,
126 ARG_PRIVATE_KEY,
127 ARG_PUBLIC_KEY,
128 ARG_TPM2_DEVICE,
129 ARG_JSON,
130 ARG_PHASE,
131 };
132
133 static const struct option options[] = {
134 { "help", no_argument, NULL, 'h' },
135 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
136 { "version", no_argument, NULL, ARG_VERSION },
137 { "linux", required_argument, NULL, ARG_LINUX },
138 { "osrel", required_argument, NULL, ARG_OSREL },
139 { "cmdline", required_argument, NULL, ARG_CMDLINE },
140 { "initrd", required_argument, NULL, ARG_INITRD },
141 { "splash", required_argument, NULL, ARG_SPLASH },
142 { "dtb", required_argument, NULL, ARG_DTB },
143 { "pcrpkey", required_argument, NULL, ARG_PCRPKEY },
144 { "current", no_argument, NULL, 'c' },
145 { "bank", required_argument, NULL, ARG_BANK },
146 { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
147 { "private-key", required_argument, NULL, ARG_PRIVATE_KEY },
148 { "public-key", required_argument, NULL, ARG_PUBLIC_KEY },
149 { "json", required_argument, NULL, ARG_JSON },
150 { "phase", required_argument, NULL, ARG_PHASE },
151 {}
152 };
153
154 int c, r;
155
156 assert(argc >= 0);
157 assert(argv);
158
159 /* Make sure the arguments list and the section list, stays in sync */
160 assert_cc(_ARG_SECTION_FIRST + _UNIFIED_SECTION_MAX == _ARG_SECTION_LAST + 1);
161
162 while ((c = getopt_long(argc, argv, "hjc", options, NULL)) >= 0)
163 switch (c) {
164
165 case 'h':
166 help(0, NULL, NULL);
167 return 0;
168
169 case ARG_VERSION:
170 return version();
171
172 case ARG_NO_PAGER:
173 arg_pager_flags |= PAGER_DISABLE;
174 break;
175
176 case _ARG_SECTION_FIRST..._ARG_SECTION_LAST: {
177 UnifiedSection section = c - _ARG_SECTION_FIRST;
178
179 r = parse_path_argument(optarg, /* suppress_root= */ false, arg_sections + section);
180 if (r < 0)
181 return r;
182 break;
183 }
184
185 case 'c':
186 arg_current = true;
187 break;
188
189 case ARG_BANK: {
190 const EVP_MD *implementation;
191
192 implementation = EVP_get_digestbyname(optarg);
193 if (!implementation)
194 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown bank '%s', refusing.", optarg);
195
196 if (strv_extend(&arg_banks, EVP_MD_name(implementation)) < 0)
197 return log_oom();
198
199 break;
200 }
201
202 case ARG_PRIVATE_KEY:
203 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_private_key);
204 if (r < 0)
205 return r;
206
207 break;
208
209 case ARG_PUBLIC_KEY:
210 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_public_key);
211 if (r < 0)
212 return r;
213
214 break;
215
216 case ARG_TPM2_DEVICE: {
217 _cleanup_free_ char *device = NULL;
218
219 if (streq(optarg, "list"))
220 return tpm2_list_devices();
221
222 if (!streq(optarg, "auto")) {
223 device = strdup(optarg);
224 if (!device)
225 return log_oom();
226 }
227
228 free_and_replace(arg_tpm2_device, device);
229 break;
230 }
231
232 case 'j':
233 arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
234 break;
235
236 case ARG_JSON:
237 r = parse_json_argument(optarg, &arg_json_format_flags);
238 if (r <= 0)
239 return r;
240
241 break;
242
243 case ARG_PHASE: {
244 char *n;
245
246 n = normalize_phase(optarg);
247 if (!n)
248 return log_oom();
249
250 r = strv_consume(&arg_phase, TAKE_PTR(n));
251 if (r < 0)
252 return r;
253
254 break;
255 }
256
257 case '?':
258 return -EINVAL;
259
260 default:
261 assert_not_reached();
262 }
263
264 if (strv_isempty(arg_banks)) {
265 /* If no banks are specifically selected, pick all known banks */
266 arg_banks = strv_new("SHA1", "SHA256", "SHA384", "SHA512");
267 if (!arg_banks)
268 return log_oom();
269 }
270
271 strv_sort(arg_banks);
272 strv_uniq(arg_banks);
273
274 if (arg_current)
275 for (UnifiedSection us = 0; us < _UNIFIED_SECTION_MAX; us++)
276 if (arg_sections[us])
277 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --current switch cannot be used in combination with --linux= and related switches.");
278
279 if (strv_isempty(arg_phase)) {
280 /* If no phases are specifically selected, pick everything from the beginning of the initrd
281 * to the beginning of shutdown. */
282 if (strv_extend_strv(&arg_phase,
283 STRV_MAKE("enter-initrd",
284 "enter-initrd:leave-initrd",
285 "enter-initrd:leave-initrd:sysinit",
286 "enter-initrd:leave-initrd:sysinit:ready"),
287 /* filter_duplicates= */ false) < 0)
288 return log_oom();
289 } else {
290 strv_sort(arg_phase);
291 strv_uniq(arg_phase);
292 }
293
294 _cleanup_free_ char *j = NULL;
295 j = strv_join(arg_phase, ", ");
296 if (!j)
297 return log_oom();
298
299 log_debug("Measuring boot phases: %s", j);
300 return 1;
301 }
302
303 /* The PCR 11 state for one specific bank */
304 typedef struct PcrState {
305 char *bank;
306 const EVP_MD *md;
307 void *value;
308 size_t value_size;
309 void *saved_value; /* A copy of the original value we calculated, used by pcr_states_save()/pcr_states_restore() to come later back to */
310 } PcrState;
311
312 static void pcr_state_free_all(PcrState **pcr_state) {
313 assert(pcr_state);
314
315 if (!*pcr_state)
316 return;
317
318 for (size_t i = 0; (*pcr_state)[i].value; i++) {
319 free((*pcr_state)[i].bank);
320 free((*pcr_state)[i].value);
321 free((*pcr_state)[i].saved_value);
322 }
323
324 *pcr_state = mfree(*pcr_state);
325 }
326
327 static void evp_md_ctx_free_all(EVP_MD_CTX **md[]) {
328 assert(md);
329
330 if (!*md)
331 return;
332
333 for (size_t i = 0; (*md)[i]; i++)
334 EVP_MD_CTX_free((*md)[i]);
335
336 *md = mfree(*md);
337 }
338
339 static int pcr_state_extend(PcrState *pcr_state, const void *data, size_t sz) {
340 _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *mc = NULL;
341 unsigned value_size;
342
343 assert(pcr_state);
344 assert(data || sz == 0);
345 assert(pcr_state->md);
346 assert(pcr_state->value);
347 assert(pcr_state->value_size > 0);
348
349 /* Extends a (virtual) PCR by the given data */
350
351 mc = EVP_MD_CTX_new();
352 if (!mc)
353 return log_oom();
354
355 if (EVP_DigestInit_ex(mc, pcr_state->md, NULL) != 1)
356 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize %s context.", pcr_state->bank);
357
358 /* First thing we do, is hash the old PCR value */
359 if (EVP_DigestUpdate(mc, pcr_state->value, pcr_state->value_size) != 1)
360 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to run digest.");
361
362 /* Then, we hash the new data */
363 if (EVP_DigestUpdate(mc, data, sz) != 1)
364 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to run digest.");
365
366 if (EVP_DigestFinal_ex(mc, pcr_state->value, &value_size) != 1)
367 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finalize hash context.");
368
369 assert(value_size == pcr_state->value_size);
370 return 0;
371 }
372
373 #define BUFFER_SIZE (16U * 1024U)
374
375 static int measure_kernel(PcrState *pcr_states, size_t n) {
376 _cleanup_free_ void *buffer = NULL;
377 int r;
378
379 assert(n > 0);
380 assert(pcr_states);
381
382 /* Virtually measures the components of a unified kernel image into PCR 11 */
383
384 if (arg_current) {
385 /* Shortcut things, if we should just use the current PCR value */
386
387 for (size_t i = 0; i < n; i++) {
388 _cleanup_free_ char *p = NULL, *s = NULL;
389 _cleanup_free_ void *v = NULL;
390 size_t sz;
391
392 if (asprintf(&p, "/sys/class/tpm/tpm0/pcr-%s/%" PRIu32, pcr_states[i].bank, TPM_PCR_INDEX_KERNEL_IMAGE) < 0)
393 return log_oom();
394
395 r = read_virtual_file(p, 4096, &s, NULL);
396 if (r == -ENOENT && access("/sys/class/tpm/tpm0/", F_OK) >= 0)
397 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);
398 if (r < 0)
399 return log_error_errno(r, "Failed to read '%s': %m", p);
400
401 r = unhexmem(strstrip(s), SIZE_MAX, &v, &sz);
402 if (r < 0)
403 return log_error_errno(r, "Failed to decode PCR value '%s': %m", s);
404
405 assert(pcr_states[i].value_size == sz);
406 memcpy(pcr_states[i].value, v, sz);
407 }
408
409 return 0;
410 }
411
412 buffer = malloc(BUFFER_SIZE);
413 if (!buffer)
414 return log_oom();
415
416 for (UnifiedSection c = 0; c < _UNIFIED_SECTION_MAX; c++) {
417 _cleanup_(evp_md_ctx_free_all) EVP_MD_CTX **mdctx = NULL;
418 _cleanup_close_ int fd = -1;
419 uint64_t m = 0;
420
421 if (!arg_sections[c])
422 continue;
423
424 fd = open(arg_sections[c], O_RDONLY|O_CLOEXEC);
425 if (fd < 0)
426 return log_error_errno(errno, "Failed to open '%s': %m", arg_sections[c]);
427
428 /* Allocate one message digest context per bank (NULL terminated) */
429 mdctx = new0(EVP_MD_CTX*, n + 1);
430 if (!mdctx)
431 return log_oom();
432
433 for (size_t i = 0; i < n; i++) {
434 mdctx[i] = EVP_MD_CTX_new();
435 if (!mdctx[i])
436 return log_oom();
437
438 if (EVP_DigestInit_ex(mdctx[i], pcr_states[i].md, NULL) != 1)
439 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize data %s context.", pcr_states[i].bank);
440 }
441
442 for (;;) {
443 ssize_t sz;
444
445 sz = read(fd, buffer, BUFFER_SIZE);
446 if (sz < 0)
447 return log_error_errno(errno, "Failed to read '%s': %m", arg_sections[c]);
448 if (sz == 0) /* EOF */
449 break;
450
451 for (size_t i = 0; i < n; i++)
452 if (EVP_DigestUpdate(mdctx[i], buffer, sz) != 1)
453 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to run digest.");
454
455 m += sz;
456 }
457
458 fd = safe_close(fd);
459
460 if (m == 0) /* We skip over empty files, the stub does so too */
461 continue;
462
463 for (size_t i = 0; i < n; i++) {
464 _cleanup_free_ void *data_hash = NULL;
465 unsigned data_hash_size;
466
467 data_hash = malloc(pcr_states[i].value_size);
468 if (!data_hash)
469 return log_oom();
470
471 /* Measure name of section */
472 if (EVP_Digest(unified_sections[c], strlen(unified_sections[c]) + 1, data_hash, &data_hash_size, pcr_states[i].md, NULL) != 1)
473 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to hash section name with %s.", pcr_states[i].bank);
474
475 assert(data_hash_size == (unsigned) pcr_states[i].value_size);
476
477 r = pcr_state_extend(pcr_states + i, data_hash, data_hash_size);
478 if (r < 0)
479 return r;
480
481 /* Retrieve hash of data and measure it */
482 if (EVP_DigestFinal_ex(mdctx[i], data_hash, &data_hash_size) != 1)
483 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finalize hash context.");
484
485 assert(data_hash_size == (unsigned) pcr_states[i].value_size);
486
487 r = pcr_state_extend(pcr_states + i, data_hash, data_hash_size);
488 if (r < 0)
489 return r;
490 }
491 }
492
493 return 0;
494 }
495
496 static int measure_phase(PcrState *pcr_states, size_t n, const char *phase) {
497 _cleanup_strv_free_ char **l = NULL;
498 int r;
499
500 assert(pcr_states);
501 assert(n > 0);
502
503 /* Measure a phase string into PCR 11. This splits up the "phase" expression at colons, and then
504 * virtually extends each specified word into PCR 11, to model how during boot we measure a series of
505 * words into PCR 11, one for each phase. */
506
507 l = strv_split(phase, ":");
508 if (!l)
509 return log_oom();
510
511 STRV_FOREACH(word, l) {
512 size_t wl;
513
514 if (isempty(*word))
515 continue;
516
517 wl = strlen(*word);
518
519 for (size_t i = 0; i < n; i++) { /* For each bank */
520 _cleanup_free_ void *b = NULL;
521 int bsz;
522
523 bsz = EVP_MD_size(pcr_states[i].md);
524 assert(bsz > 0);
525
526 b = malloc(bsz);
527 if (!b)
528 return log_oom();
529
530 /* First hash the word itself */
531 if (EVP_Digest(*word, wl, b, NULL, pcr_states[i].md, NULL) != 1)
532 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to hash word '%s'.", *word);
533
534 /* And then extend the PCR with the resulting hash */
535 r = pcr_state_extend(pcr_states + i, b, bsz);
536 if (r < 0)
537 return r;
538 }
539 }
540
541 return 0;
542 }
543
544 static int pcr_states_allocate(PcrState **ret) {
545 _cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL;
546 size_t n = 0;
547
548 pcr_states = new0(PcrState, strv_length(arg_banks) + 1);
549 if (!pcr_states)
550 return log_oom();
551
552 /* Allocate a PCR state structure, one for each bank */
553 STRV_FOREACH(d, arg_banks) {
554 const EVP_MD *implementation;
555 _cleanup_free_ void *v = NULL;
556 _cleanup_free_ char *b = NULL;
557 int sz;
558
559 assert_se(implementation = EVP_get_digestbyname(*d)); /* Must work, we already checked while parsing command line */
560
561 b = strdup(EVP_MD_name(implementation));
562 if (!b)
563 return log_oom();
564
565 sz = EVP_MD_size(implementation);
566 if (sz <= 0 || sz >= INT_MAX)
567 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected digest size: %i", sz);
568
569 v = malloc0(sz); /* initial PCR state is all zeroes */
570 if (!v)
571 return log_oom();
572
573 pcr_states[n++] = (struct PcrState) {
574 .bank = ascii_strlower(TAKE_PTR(b)),
575 .md = implementation,
576 .value = TAKE_PTR(v),
577 .value_size = sz,
578 };
579 }
580
581 *ret = TAKE_PTR(pcr_states);
582 return (int) n;
583 }
584
585 static int pcr_states_save(PcrState *pcr_states, size_t n) {
586 assert(pcr_states);
587 assert(n > 0);
588
589 for (size_t i = 0; i < n; i++) {
590 _cleanup_free_ void *saved = NULL;
591
592 if (!pcr_states[i].value)
593 continue;
594
595 saved = memdup(pcr_states[i].value, pcr_states[i].value_size);
596 if (!saved)
597 return log_oom();
598
599 free_and_replace(pcr_states[i].saved_value, saved);
600 }
601
602 return 0;
603 }
604
605 static void pcr_states_restore(PcrState *pcr_states, size_t n) {
606 assert(pcr_states);
607 assert(n > 0);
608
609 for (size_t i = 0; i < n; i++) {
610
611 assert(pcr_states[i].value);
612 assert(pcr_states[i].saved_value);
613
614 memcpy(pcr_states[i].value, pcr_states[i].saved_value, pcr_states[i].value_size);
615 }
616 }
617
618 static int verb_calculate(int argc, char *argv[], void *userdata) {
619 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
620 _cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL;
621 size_t n;
622 int r;
623
624 if (!arg_sections[UNIFIED_SECTION_LINUX] && !arg_current)
625 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Either --linux= or --current must be specified, refusing.");
626
627 assert(!strv_isempty(arg_banks));
628 assert(!strv_isempty(arg_phase));
629
630 r = pcr_states_allocate(&pcr_states);
631 if (r < 0)
632 return r;
633
634 n = (size_t) r;
635
636 r = measure_kernel(pcr_states, n);
637 if (r < 0)
638 return r;
639
640 /* Save the current state, so that we later can restore to it. This way we can measure the PCR values
641 * for multiple different boot phases without heaving to start from zero each time */
642 r = pcr_states_save(pcr_states, n);
643 if (r < 0)
644 return r;
645
646 STRV_FOREACH(phase, arg_phase) {
647
648 r = measure_phase(pcr_states, n, *phase);
649 if (r < 0)
650 return r;
651
652 for (size_t i = 0; i < n; i++) {
653 if (arg_json_format_flags & JSON_FORMAT_OFF) {
654 _cleanup_free_ char *hd = NULL;
655
656 if (i == 0) {
657 fflush(stdout);
658 fprintf(stderr, "%s# PCR[%" PRIu32 "] Phase <%s>%s\n",
659 ansi_grey(),
660 TPM_PCR_INDEX_KERNEL_IMAGE,
661 isempty(*phase) ? ":" : *phase,
662 ansi_normal());
663 fflush(stderr);
664 }
665
666 hd = hexmem(pcr_states[i].value, pcr_states[i].value_size);
667 if (!hd)
668 return log_oom();
669
670 printf("%" PRIu32 ":%s=%s\n", TPM_PCR_INDEX_KERNEL_IMAGE, pcr_states[i].bank, hd);
671 } else {
672 _cleanup_(json_variant_unrefp) JsonVariant *bv = NULL, *array = NULL;
673
674 array = json_variant_ref(json_variant_by_key(w, pcr_states[i].bank));
675
676 r = json_build(&bv,
677 JSON_BUILD_OBJECT(
678 JSON_BUILD_PAIR_CONDITION(!isempty(*phase), "phase", JSON_BUILD_STRING(*phase)),
679 JSON_BUILD_PAIR("pcr", JSON_BUILD_INTEGER(TPM_PCR_INDEX_KERNEL_IMAGE)),
680 JSON_BUILD_PAIR("hash", JSON_BUILD_HEX(pcr_states[i].value, pcr_states[i].value_size))
681 )
682 );
683 if (r < 0)
684 return log_error_errno(r, "Failed to build JSON object: %m");
685
686 r = json_variant_append_array(&array, bv);
687 if (r < 0)
688 return log_error_errno(r, "Failed to append JSON object to array: %m");
689
690 r = json_variant_set_field(&w, pcr_states[i].bank, array);
691 if (r < 0)
692 return log_error_errno(r, "Failed to add bank info to object: %m");
693 }
694 }
695
696 /* Return to the original kernel measurement for the next phase calculation */
697 pcr_states_restore(pcr_states, n);
698 }
699
700 if (!FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
701
702 if (arg_json_format_flags & (JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO))
703 pager_open(arg_pager_flags);
704
705 json_variant_dump(w, arg_json_format_flags, stdout, NULL);
706 }
707
708 return 0;
709 }
710
711 static int verb_sign(int argc, char *argv[], void *userdata) {
712 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
713 _cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL;
714 _cleanup_(EVP_PKEY_freep) EVP_PKEY *privkey = NULL, *pubkey = NULL;
715 _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
716 _cleanup_fclose_ FILE *privkeyf = NULL;
717 ESYS_TR session_handle = ESYS_TR_NONE;
718 TSS2_RC rc;
719 size_t n;
720 int r;
721
722 if (!arg_sections[UNIFIED_SECTION_LINUX] && !arg_current)
723 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Either --linux= or --current must be specified, refusing.");
724
725 if (!arg_private_key)
726 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No private key specified, use --private-key=.");
727
728 assert(!strv_isempty(arg_banks));
729 assert(!strv_isempty(arg_phase));
730
731 /* When signing we only support JSON output */
732 arg_json_format_flags &= ~JSON_FORMAT_OFF;
733
734 privkeyf = fopen(arg_private_key, "re");
735 if (!privkeyf)
736 return log_error_errno(errno, "Failed to open private key file '%s': %m", arg_private_key);
737
738 privkey = PEM_read_PrivateKey(privkeyf, NULL, NULL, NULL);
739 if (!privkey)
740 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse private key '%s'.", arg_private_key);
741
742 if (arg_public_key) {
743 _cleanup_fclose_ FILE *pubkeyf = NULL;
744
745 pubkeyf = fopen(arg_public_key, "re");
746 if (!pubkeyf)
747 return log_error_errno(errno, "Failed to open public key file '%s': %m", arg_public_key);
748
749 pubkey = PEM_read_PUBKEY(pubkeyf, NULL, NULL, NULL);
750 if (!pubkey)
751 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse public key '%s'.", arg_public_key);
752 } else {
753 _cleanup_free_ char *data = NULL;
754 _cleanup_fclose_ FILE *tf = NULL;
755 size_t sz;
756
757 /* No public key was specified, let's derive it automatically, if we can */
758
759 tf = open_memstream_unlocked(&data, &sz);
760 if (!tf)
761 return log_oom();
762
763 if (i2d_PUBKEY_fp(tf, privkey) != 1)
764 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from private key file '%s'.", arg_private_key);
765
766 fflush(tf);
767 rewind(tf);
768
769 if (!d2i_PUBKEY_fp(tf, &pubkey))
770 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse extracted public key of private key file '%s'.", arg_private_key);
771 }
772
773 r = pcr_states_allocate(&pcr_states);
774 if (r < 0)
775 return r;
776
777 n = (size_t) r;
778
779 r = measure_kernel(pcr_states, n);
780 if (r < 0)
781 return r;
782
783 r = pcr_states_save(pcr_states, n);
784 if (r < 0)
785 return r;
786
787 r = dlopen_tpm2();
788 if (r < 0)
789 return r;
790
791 r = tpm2_context_init(arg_tpm2_device, &c);
792 if (r < 0)
793 return r;
794
795 STRV_FOREACH(phase, arg_phase) {
796
797 r = measure_phase(pcr_states, n, *phase);
798 if (r < 0)
799 return r;
800
801 for (size_t i = 0; i < n; i++) {
802 static const TPMT_SYM_DEF symmetric = {
803 .algorithm = TPM2_ALG_AES,
804 .keyBits.aes = 128,
805 .mode.aes = TPM2_ALG_CFB,
806 };
807 PcrState *p = pcr_states + i;
808
809 rc = sym_Esys_StartAuthSession(
810 c.esys_context,
811 ESYS_TR_NONE,
812 ESYS_TR_NONE,
813 ESYS_TR_NONE,
814 ESYS_TR_NONE,
815 ESYS_TR_NONE,
816 NULL,
817 TPM2_SE_TRIAL,
818 &symmetric,
819 TPM2_ALG_SHA256,
820 &session_handle);
821 if (rc != TSS2_RC_SUCCESS) {
822 r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
823 "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc));
824 goto finish;
825 }
826
827 /* Generate a single hash value from the PCRs included in our policy. Given that that's
828 * exactly one, the calculation is trivial. */
829 TPM2B_DIGEST intermediate_digest = {
830 .size = SHA256_DIGEST_SIZE,
831 };
832 assert(sizeof(intermediate_digest.buffer) >= SHA256_DIGEST_SIZE);
833 sha256_direct(p->value, p->value_size, intermediate_digest.buffer);
834
835 int tpmalg = tpm2_pcr_bank_from_string(EVP_MD_name(p->md));
836 if (tpmalg < 0) {
837 log_error_errno(tpmalg, "Unsupported PCR bank");
838 goto finish;
839 }
840
841 TPML_PCR_SELECTION pcr_selection;
842 tpm2_pcr_mask_to_selection(1 << TPM_PCR_INDEX_KERNEL_IMAGE, tpmalg, &pcr_selection);
843
844 rc = sym_Esys_PolicyPCR(
845 c.esys_context,
846 session_handle,
847 ESYS_TR_NONE,
848 ESYS_TR_NONE,
849 ESYS_TR_NONE,
850 &intermediate_digest,
851 &pcr_selection);
852 if (rc != TSS2_RC_SUCCESS) {
853 r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
854 "Failed to push PCR policy into TPM: %s", sym_Tss2_RC_Decode(rc));
855 goto finish;
856 }
857
858 _cleanup_(Esys_Freep) TPM2B_DIGEST *pcr_policy_digest = NULL;
859 rc = sym_Esys_PolicyGetDigest(
860 c.esys_context,
861 session_handle,
862 ESYS_TR_NONE,
863 ESYS_TR_NONE,
864 ESYS_TR_NONE,
865 &pcr_policy_digest);
866 if (rc != TSS2_RC_SUCCESS) {
867 r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
868 "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
869 goto finish;
870 }
871
872 session_handle = tpm2_flush_context_verbose(c.esys_context, session_handle);
873
874 _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* mdctx = NULL;
875 mdctx = EVP_MD_CTX_new();
876 if (!mdctx) {
877 r = log_oom();
878 goto finish;
879 }
880
881 if (EVP_DigestSignInit(mdctx, NULL, p->md, NULL, privkey) != 1) {
882 r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
883 "Failed to initialize signature context.");
884 goto finish;
885 }
886
887 if (EVP_DigestSignUpdate(mdctx, pcr_policy_digest->buffer, pcr_policy_digest->size) != 1) {
888 r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
889 "Failed to sign data.");
890 goto finish;
891 }
892
893 size_t ss;
894 if (EVP_DigestSignFinal(mdctx, NULL, &ss) != 1) {
895 r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
896 "Failed to finalize signature");
897 goto finish;
898 }
899
900 _cleanup_free_ void *sig = malloc(ss);
901 if (!sig) {
902 r = log_oom();
903 goto finish;
904 }
905
906 if (EVP_DigestSignFinal(mdctx, sig, &ss) != 1) {
907 r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
908 "Failed to acquire signature data");
909 goto finish;
910 }
911
912 _cleanup_free_ void *pubkey_fp = NULL;
913 size_t pubkey_fp_size = 0;
914 r = pubkey_fingerprint(pubkey, EVP_sha256(), &pubkey_fp, &pubkey_fp_size);
915 if (r < 0)
916 goto finish;
917
918 _cleanup_(json_variant_unrefp) JsonVariant *a = NULL;
919 r = tpm2_make_pcr_json_array(UINT64_C(1) << TPM_PCR_INDEX_KERNEL_IMAGE, &a);
920 if (r < 0) {
921 log_error_errno(r, "Failed to build JSON PCR mask array: %m");
922 goto finish;
923 }
924
925 _cleanup_(json_variant_unrefp) JsonVariant *bv = NULL;
926 r = json_build(&bv, JSON_BUILD_OBJECT(
927 JSON_BUILD_PAIR("pcrs", JSON_BUILD_VARIANT(a)), /* PCR mask */
928 JSON_BUILD_PAIR("pkfp", JSON_BUILD_HEX(pubkey_fp, pubkey_fp_size)), /* SHA256 fingerprint of public key (DER) used for the signature */
929 JSON_BUILD_PAIR("pol", JSON_BUILD_HEX(pcr_policy_digest->buffer, pcr_policy_digest->size)), /* TPM2 policy hash that is signed */
930 JSON_BUILD_PAIR("sig", JSON_BUILD_BASE64(sig, ss)))); /* signature data */
931 if (r < 0) {
932 log_error_errno(r, "Failed to build JSON object: %m");
933 goto finish;
934 }
935
936 _cleanup_(json_variant_unrefp) JsonVariant *av = NULL;
937 av = json_variant_ref(json_variant_by_key(v, p->bank));
938
939 r = json_variant_append_array(&av, bv);
940 if (r < 0) {
941 log_error_errno(r, "Failed to append JSON object: %m");
942 goto finish;
943 }
944
945 r = json_variant_set_field(&v, p->bank, av);
946 if (r < 0) {
947 log_error_errno(r, "Failed to add JSON field: %m");
948 goto finish;
949 }
950 }
951
952 /* Return to the original kernel measurement for the next phase calculation */
953 pcr_states_restore(pcr_states, n);
954 }
955
956 if (arg_json_format_flags & (JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO))
957 pager_open(arg_pager_flags);
958
959 json_variant_dump(v, arg_json_format_flags, stdout, NULL);
960 r = 0;
961
962 finish:
963 session_handle = tpm2_flush_context_verbose(c.esys_context, session_handle);
964 return r;
965 }
966
967 static int compare_reported_pcr_nr(uint32_t pcr, const char *varname, const char *description) {
968 _cleanup_free_ char *s = NULL;
969 uint32_t v;
970 int r;
971
972 r = efi_get_variable_string(varname, &s);
973 if (r == -ENOENT)
974 return 0;
975 if (r < 0)
976 return log_error_errno(r, "Failed to read EFI variable '%s': %m", varname);
977
978 r = safe_atou32(s, &v);
979 if (r < 0)
980 return log_error_errno(r, "Failed to parse EFI variable '%s': %s", varname, s);
981
982 if (pcr != v)
983 log_warning("PCR number reported by stub for %s (%" PRIu32 ") different from our expectation (%" PRIu32 ").\n"
984 "The measurements are likely inconsistent.", description, v, pcr);
985
986 return 0;
987 }
988
989 static int validate_stub(void) {
990 uint64_t features;
991 bool found = false;
992 int r;
993
994 if (tpm2_support() != TPM2_SUPPORT_FULL)
995 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Sorry, system lacks full TPM2 support.");
996
997 r = efi_stub_get_features(&features);
998 if (r < 0)
999 return log_error_errno(r, "Unable to get stub features: %m");
1000
1001 if (!FLAGS_SET(features, EFI_STUB_FEATURE_THREE_PCRS))
1002 log_warning("Warning: current kernel image does not support measuring itself, the command line or initrd system extension images.\n"
1003 "The PCR measurements seen are unlikely to be valid.");
1004
1005 r = compare_reported_pcr_nr(TPM_PCR_INDEX_KERNEL_IMAGE, EFI_LOADER_VARIABLE("StubPcrKernelImage"), "kernel image");
1006 if (r < 0)
1007 return r;
1008
1009 r = compare_reported_pcr_nr(TPM_PCR_INDEX_KERNEL_PARAMETERS, EFI_LOADER_VARIABLE("StubPcrKernelParameters"), "kernel parameters");
1010 if (r < 0)
1011 return r;
1012
1013 r = compare_reported_pcr_nr(TPM_PCR_INDEX_INITRD_SYSEXTS, EFI_LOADER_VARIABLE("StubPcrInitRDSysExts"), "initrd system extension images");
1014 if (r < 0)
1015 return r;
1016
1017 STRV_FOREACH(bank, arg_banks) {
1018 _cleanup_free_ char *b = NULL, *p = NULL;
1019
1020 b = strdup(*bank);
1021 if (!b)
1022 return log_oom();
1023
1024 if (asprintf(&p, "/sys/class/tpm/tpm0/pcr-%s/", ascii_strlower(b)) < 0)
1025 return log_oom();
1026
1027 if (access(p, F_OK) < 0) {
1028 if (errno != ENOENT)
1029 return log_error_errno(errno, "Failed to detect if '%s' exists: %m", b);
1030 } else
1031 found = true;
1032 }
1033
1034 if (!found)
1035 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "None of the select PCR banks appear to exist.");
1036
1037 return 0;
1038 }
1039
1040 static int verb_status(int argc, char *argv[], void *userdata) {
1041 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
1042
1043 static const struct {
1044 uint32_t nr;
1045 const char *description;
1046 } relevant_pcrs[] = {
1047 { TPM_PCR_INDEX_KERNEL_IMAGE, "Unified Kernel Image" },
1048 { TPM_PCR_INDEX_KERNEL_PARAMETERS, "Kernel Parameters" },
1049 { TPM_PCR_INDEX_INITRD_SYSEXTS, "initrd System Extensions" },
1050 };
1051
1052 int r;
1053
1054 r = validate_stub();
1055 if (r < 0)
1056 return r;
1057
1058 for (size_t i = 0; i < ELEMENTSOF(relevant_pcrs); i++) {
1059
1060 STRV_FOREACH(bank, arg_banks) {
1061 _cleanup_free_ char *b = NULL, *p = NULL, *s = NULL;
1062 _cleanup_free_ void *h = NULL;
1063 size_t l;
1064
1065 b = strdup(*bank);
1066 if (!b)
1067 return log_oom();
1068
1069 if (asprintf(&p, "/sys/class/tpm/tpm0/pcr-%s/%" PRIu32, ascii_strlower(b), relevant_pcrs[i].nr) < 0)
1070 return log_oom();
1071
1072 r = read_virtual_file(p, 4096, &s, NULL);
1073 if (r == -ENOENT)
1074 continue;
1075 if (r < 0)
1076 return log_error_errno(r, "Failed to read '%s': %m", p);
1077
1078 r = unhexmem(strstrip(s), SIZE_MAX, &h, &l);
1079 if (r < 0)
1080 return log_error_errno(r, "Failed to decode PCR value '%s': %m", s);
1081
1082 if (arg_json_format_flags & JSON_FORMAT_OFF) {
1083 _cleanup_free_ char *f = NULL;
1084
1085 f = hexmem(h, l);
1086 if (!h)
1087 return log_oom();
1088
1089 if (bank == arg_banks) {
1090 /* before the first line for each PCR, write a short descriptive text to
1091 * stderr, and leave the primary content on stdout */
1092 fflush(stdout);
1093 fprintf(stderr, "%s# PCR[%" PRIu32 "] %s%s%s\n",
1094 ansi_grey(),
1095 relevant_pcrs[i].nr,
1096 relevant_pcrs[i].description,
1097 memeqzero(h, l) ? " (NOT SET!)" : "",
1098 ansi_normal());
1099 fflush(stderr);
1100 }
1101
1102 printf("%" PRIu32 ":%s=%s\n", relevant_pcrs[i].nr, b, f);
1103
1104 } else {
1105 _cleanup_(json_variant_unrefp) JsonVariant *bv = NULL, *a = NULL;
1106
1107 r = json_build(&bv,
1108 JSON_BUILD_OBJECT(
1109 JSON_BUILD_PAIR("pcr", JSON_BUILD_INTEGER(relevant_pcrs[i].nr)),
1110 JSON_BUILD_PAIR("hash", JSON_BUILD_HEX(h, l))
1111 )
1112 );
1113 if (r < 0)
1114 return log_error_errno(r, "Failed to build JSON object: %m");
1115
1116 a = json_variant_ref(json_variant_by_key(v, b));
1117
1118 r = json_variant_append_array(&a, bv);
1119 if (r < 0)
1120 return log_error_errno(r, "Failed to append PCR entry to JSON array: %m");
1121
1122 r = json_variant_set_field(&v, b, a);
1123 if (r < 0)
1124 return log_error_errno(r, "Failed to add bank info to object: %m");
1125 }
1126 }
1127 }
1128
1129 if (!FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
1130 if (arg_json_format_flags & (JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO))
1131 pager_open(arg_pager_flags);
1132
1133 json_variant_dump(v, arg_json_format_flags, stdout, NULL);
1134 }
1135
1136 return 0;
1137 }
1138
1139 static int measure_main(int argc, char *argv[]) {
1140 static const Verb verbs[] = {
1141 { "help", VERB_ANY, VERB_ANY, 0, help },
1142 { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
1143 { "calculate", VERB_ANY, 1, 0, verb_calculate },
1144 { "sign", VERB_ANY, 1, 0, verb_sign },
1145 {}
1146 };
1147
1148 return dispatch_verb(argc, argv, verbs, NULL);
1149 }
1150
1151 static int run(int argc, char *argv[]) {
1152 int r;
1153
1154 log_show_color(true);
1155 log_parse_environment();
1156 log_open();
1157
1158 r = parse_argv(argc, argv);
1159 if (r <= 0)
1160 return r;
1161
1162 return measure_main(argc, argv);
1163 }
1164
1165 DEFINE_MAIN_FUNCTION(run);