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