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