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