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