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