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